// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chrome/browser/network_status_view.h" #include #include "base/string_util.h" #include "base/thread.h" #include "chrome/browser/browser.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/tab_contents_delegate.h" #include "chrome/views/hwnd_view_container.h" #include "chrome/views/root_view.h" #include "net/url_request/url_request.h" #include "net/url_request/url_request_job.h" namespace { const wchar_t kTitleMsg[] = L"Network Status"; const wchar_t kStartTrackingMsg[] = L"Start I/O Tracking"; const wchar_t kStopTrackingMsg[] = L"Stop I/O Tracking"; const wchar_t kShowIOStatusMsg[] = L"Show Current I/O Status"; const wchar_t kClearOutputMsg[] = L"Clear Output"; // Returns a string representing the URL, handling the case where the spec // is invalid. std::wstring StringForURL(const GURL& url) { if (url.is_valid()) return UTF8ToWide(url.spec()); return UTF8ToWide(url.possibly_invalid_spec()) + L" (invalid)"; } std::wstring URLForJob(URLRequestJob* job) { URLRequest* request = job->request(); if (request) return StringForURL(request->url()); return std::wstring(L"(orphaned)"); } } // namespace NetworkStatusView::NetworkStatusView() : StatusView(TAB_CONTENTS_NETWORK_STATUS_VIEW) { tracker_ = new JobTracker(this); } NetworkStatusView::~NetworkStatusView() { if (monospaced_font_) DeleteObject(monospaced_font_); if (is_tracking_) { tracker_->StopTracking(); is_tracking_ = false; } tracker_->DetachView(); } const std::wstring NetworkStatusView::GetDefaultTitle() { return kTitleMsg; } void NetworkStatusView::OnCreate(const CRect& rect) { CreateButton(IDC_CONFIG_TRACKING_BUTTON, kStartTrackingMsg); CreateButton(IDC_CURRENT_STATUS_BUTTON, kShowIOStatusMsg); CreateButton(IDC_CLEAR, kClearOutputMsg); is_tracking_ = false; // Initialize the text box for network tracking // Don't worry about the size, we'll resize when we get WM_SIZE text_area_.Create(m_hWnd, const_cast(rect), NULL, WS_CHILD | WS_HSCROLL | WS_VSCROLL | ES_MULTILINE | ES_AUTOHSCROLL | ES_AUTOVSCROLL, 0); // This raises the maximum number of chars from 32K to some large maximum, // probably 2GB. 32K is not nearly enough for our use-case. text_area_.SendMessageW(EM_SETLIMITTEXT, 0, 0); // make a monospaced font for the edit control LOGFONT lf = {0}; lf.lfHeight = 16; wcscpy_s(lf.lfFaceName, LF_FACESIZE, L"Courier New"); monospaced_font_ = CreateFontIndirect(&lf); text_area_.SetFont(monospaced_font_); } void NetworkStatusView::OnSize(const CRect& rect) { // re-layout the edit control text_area_.MoveWindow(rect); // re-layout the performance view CRect new_rect(rect); int list_width = rect.Width(); int list_height = static_cast(rect.Height() / 5); int page_width = rect.Width() / 2; int page_height = static_cast(rect.Height() * 4 / 5); } void NetworkStatusView::OnConfigTrackingClicked(UINT code, int button_id, HWND hwnd) { if (is_tracking_) { tracker_->StopTracking(); is_tracking_ = false; SetButtonText(IDC_CONFIG_TRACKING_BUTTON, kStartTrackingMsg); } else { tracker_->StartTracking(); is_tracking_ = true; ClearTrackingResults(); ShowTrackingResults(); SetButtonText(IDC_CONFIG_TRACKING_BUTTON, kStopTrackingMsg); } } void NetworkStatusView::OnCurrentStatusClicked(UINT code, int button_id, HWND hwnd) { ShowTrackingResults(); if (is_tracking_) { tracker_->ReportStatus(); } } void NetworkStatusView::OnClearClicked(UINT code, int button_id, HWND hwnd) { ClearTrackingResults(); } void NetworkStatusView::AppendText(const std::wstring& text) { text_area_.AppendText(text.c_str()); } void NetworkStatusView::HideTrackingResults() { text_area_.ShowWindow(SW_HIDE); } void NetworkStatusView::ShowTrackingResults() { text_area_.ShowWindow(SW_SHOW); } void NetworkStatusView::ClearTrackingResults() { text_area_.SetSelAll(); text_area_.Clear(); } //----------------------------------------------------------------------------- // main thread: NetworkStatusView::JobTracker::JobTracker(NetworkStatusView* view) : view_(view), view_message_loop_(MessageLoop::current()) { } // main thread: void NetworkStatusView::JobTracker::InvokeOnIOThread(void (JobTracker::*m)()) { base::Thread* thread = g_browser_process->io_thread(); if (!thread) return; thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(this, m)); } // main thread: void NetworkStatusView::JobTracker::StartTracking() { DCHECK(MessageLoop::current() == view_message_loop_); DCHECK(view_); InvokeOnIOThread(&JobTracker::OnStartTracking); } // main thread: void NetworkStatusView::JobTracker::StopTracking() { DCHECK(MessageLoop::current() == view_message_loop_); // The tracker should not be deleted before it is removed from observer // list. AddRef(); InvokeOnIOThread(&JobTracker::OnStopTracking); } // main thread: void NetworkStatusView::JobTracker::ReportStatus() { DCHECK(MessageLoop::current() == view_message_loop_); InvokeOnIOThread(&JobTracker::OnReportStatus); } // main thread: void NetworkStatusView::JobTracker::OnAppendText(const std::wstring& text) { DCHECK(MessageLoop::current() == view_message_loop_); if (view_ && view_->is_tracking_) view_->AppendText(text); } // IO thread: void NetworkStatusView::JobTracker::AppendText(const std::wstring& text) { DCHECK(MessageLoop::current() != view_message_loop_); view_message_loop_->PostTask(FROM_HERE, NewRunnableMethod( this, &JobTracker::OnAppendText, text)); } // IO thread: void NetworkStatusView::JobTracker::OnStartTracking() { DCHECK(MessageLoop::current() != view_message_loop_); g_url_request_job_tracker.AddObserver(this); } // IO thread: void NetworkStatusView::JobTracker::OnStopTracking() { DCHECK(MessageLoop::current() != view_message_loop_); g_url_request_job_tracker.RemoveObserver(this); // Balance the AddRef() in StopTracking() called in main thread. Release(); } // IO thread: void NetworkStatusView::JobTracker::OnReportStatus() { DCHECK(MessageLoop::current() != view_message_loop_); std::wstring text(L"\r\n===== Active Job Summary =====\r\n"); URLRequestJobTracker::JobIterator begin_job = g_url_request_job_tracker.begin(); URLRequestJobTracker::JobIterator end_job = g_url_request_job_tracker.end(); int orphaned_count = 0; int regular_count = 0; for (URLRequestJobTracker::JobIterator cur = begin_job; cur != end_job; ++cur) { URLRequestJob* job = (*cur); URLRequest* request = job->request(); if (!request) { orphaned_count++; continue; } regular_count++; // active state if (job->is_done()) text.append(L" Done: "); else text.append(L" Active: "); // URL text.append(StringForURL(request->url())); text.append(L"\r\n"); } if (regular_count == 0) text.append(L" (No active jobs)\r\n"); if (orphaned_count) { wchar_t buf[64]; swprintf(buf, arraysize(buf), L" %d orphaned jobs\r\n", orphaned_count); text.append(buf); } text.append(L"=====\r\n\r\n"); AppendText(text); } // IO thread: void NetworkStatusView::JobTracker::OnJobAdded(URLRequestJob* job) { DCHECK(MessageLoop::current() != view_message_loop_); std::wstring text(L"+ New job : "); text.append(URLForJob(job)); text.append(L"\r\n"); AppendText(text); } // IO thread: void NetworkStatusView::JobTracker::OnJobRemoved(URLRequestJob* job) { DCHECK(MessageLoop::current() != view_message_loop_); } // IO thread: void NetworkStatusView::JobTracker::OnJobDone(URLRequestJob* job, const URLRequestStatus& status) { DCHECK(MessageLoop::current() != view_message_loop_); std::wstring text; if (status.is_success()) { text.assign(L"- Complete: "); } else if (status.status() == URLRequestStatus::CANCELED) { text.assign(L"- Canceled: "); } else if (status.status() == URLRequestStatus::HANDLED_EXTERNALLY) { text.assign(L"- Handled externally: "); } else { wchar_t buf[32]; swprintf(buf, arraysize(buf), L"Failed with %d: ", status.os_error()); text.assign(buf); } text.append(URLForJob(job)); text.append(L"\r\n"); AppendText(text); } // IO thread: void NetworkStatusView::JobTracker::OnJobRedirect(URLRequestJob* job, const GURL& location, int status_code) { DCHECK(MessageLoop::current() != view_message_loop_); std::wstring text(L"- Redirect: "); text.append(URLForJob(job)); text.append(L"\r\n "); wchar_t buf[16]; swprintf(buf, arraysize(buf), L"(%d) to: ", status_code); text.append(buf); text.append(StringForURL(location)); text.append(L"\r\n"); AppendText(text); } void NetworkStatusView::JobTracker::OnBytesRead(URLRequestJob* job, int byte_count) { }