Chromium Code Reviews| Index: chrome/browser/views/about_network_dialog.cc |
| =================================================================== |
| --- chrome/browser/views/about_network_dialog.cc (revision 0) |
| +++ chrome/browser/views/about_network_dialog.cc (revision 0) |
| @@ -0,0 +1,381 @@ |
| +// Copyright (c) 2009 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/views/about_network_dialog.h" |
| + |
| +#include "base/string_util.h" |
| +#include "base/thread.h" |
| +#include "chrome/browser/browser_process.h" |
| +#include "chrome/browser/views/standard_layout.h" |
| +#include "chrome/views/grid_layout.h" |
| +#include "chrome/views/text_button.h" |
| +#include "chrome/views/text_field.h" |
| +#include "chrome/views/window.h" |
| +#include "net/url_request/url_request.h" |
| +#include "net/url_request/url_request_job.h" |
| +#include "net/url_request/url_request_job_tracker.h" |
| + |
| +namespace { |
| + |
| +// We don't localize this UI since this is a developer-only feature. |
| +const wchar_t kStartTrackingLabel[] = L"Start tracking"; |
| +const wchar_t kStopTrackingLabel[] = L"Stop tracking"; |
| +const wchar_t KShowCurrentLabel[] = L"Show Current"; |
|
Ben Goodger (Google)
2009/01/23 23:24:24
K?
|
| +const wchar_t kClearLabel[] = L"Clear"; |
| + |
| +// The singleton dialog box. This is non-NULL when a dialog is active so we |
| +// know not to create a new one. |
| +AboutNetworkDialog* active_dialog = NULL; |
| + |
| +// 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)"); |
| +} |
| + |
| +// JobTracker ------------------------------------------------------------------ |
| + |
| +// A JobTracker is allocated to monitor network jobs running on the IO |
| +// thread. This allows the NetworkStatusView to remain single-threaded. |
| +class JobTracker : public URLRequestJobTracker::JobObserver, |
| + public base::RefCountedThreadSafe<JobTracker> { |
| + public: |
| + JobTracker(AboutNetworkDialog* view); |
| + ~JobTracker(); |
| + |
| + // Called by the NetworkStatusView on the main application thread. |
| + void StartTracking(); |
| + void StopTracking(); |
| + void ReportStatus(); |
| + |
| + // URLRequestJobTracker::JobObserver methods (called on the IO thread): |
| + virtual void OnJobAdded(URLRequestJob* job); |
| + virtual void OnJobRemoved(URLRequestJob* job); |
| + virtual void OnJobDone(URLRequestJob* job, const URLRequestStatus& status); |
| + virtual void OnJobRedirect(URLRequestJob* job, const GURL& location, |
| + int status_code); |
| + virtual void OnBytesRead(URLRequestJob* job, int byte_count); |
| + |
| + // The JobTracker may be deleted after NetworkStatusView is deleted. |
| + void DetachView() { view_ = NULL; } |
| + |
| + private: |
| + void InvokeOnIOThread(void (JobTracker::*method)()); |
| + |
| + // Called on the IO thread |
| + void OnStartTracking(); |
| + void OnStopTracking(); |
| + void OnReportStatus(); |
| + void AppendText(const std::wstring& text); |
| + |
| + // Called on the main thread |
| + void OnAppendText(const std::wstring& text); |
| + |
| + AboutNetworkDialog* view_; |
| + MessageLoop* view_message_loop_; |
| +}; |
| + |
| +// main thread: |
| +JobTracker::JobTracker(AboutNetworkDialog* view) |
| + : view_(view), |
| + view_message_loop_(MessageLoop::current()) { |
| +} |
| + |
| +JobTracker::~JobTracker() { |
| +} |
| + |
| +// main thread: |
| +void 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 JobTracker::StartTracking() { |
| + DCHECK(MessageLoop::current() == view_message_loop_); |
| + DCHECK(view_); |
| + InvokeOnIOThread(&JobTracker::OnStartTracking); |
| +} |
| + |
| +// main thread: |
| +void 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 JobTracker::ReportStatus() { |
| + DCHECK(MessageLoop::current() == view_message_loop_); |
| + InvokeOnIOThread(&JobTracker::OnReportStatus); |
| +} |
| + |
| +// main thread: |
| +void JobTracker::OnAppendText(const std::wstring& text) { |
| + DCHECK(MessageLoop::current() == view_message_loop_); |
| + if (view_ && view_->tracking()) |
| + view_->AppendText(text); |
| +} |
| + |
| +// IO thread: |
| +void 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 JobTracker::OnStartTracking() { |
| + DCHECK(MessageLoop::current() != view_message_loop_); |
| + g_url_request_job_tracker.AddObserver(this); |
| +} |
| + |
| +// IO thread: |
| +void 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 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 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 JobTracker::OnJobRemoved(URLRequestJob* job) { |
| + DCHECK(MessageLoop::current() != view_message_loop_); |
| +} |
| + |
| +// IO thread: |
| +void 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 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 JobTracker::OnBytesRead(URLRequestJob* job, int byte_count) { |
| +} |
| + |
| +// The singleton job tracker associated with the dialog. |
| +JobTracker* tracker = NULL; |
| + |
| +} // namespace |
| + |
| +// AboutNetworkDialog ---------------------------------------------------------- |
| + |
| +AboutNetworkDialog::AboutNetworkDialog() : tracking_(false) { |
| + SetupControls(); |
| + tracker = new JobTracker(this); |
| + tracker->AddRef(); |
| +} |
| + |
| +AboutNetworkDialog::~AboutNetworkDialog() { |
| + active_dialog = NULL; |
| + tracker->Release(); |
| + tracker = NULL; |
| +} |
| + |
| +// static |
| +void AboutNetworkDialog::RunDialog() { |
| + if (!active_dialog) { |
| + active_dialog = new AboutNetworkDialog; |
| + views::Window::CreateChromeWindow(NULL, gfx::Rect(), active_dialog)->Show(); |
| + } else { |
| + // TOOD(brettw) it would be nice to focus the existing window. |
| + } |
| +} |
| + |
| +void AboutNetworkDialog::AppendText(const std::wstring& text) { |
| + text_field_->AppendText(text); |
| +} |
| + |
| +void AboutNetworkDialog::SetupControls() { |
| + views::GridLayout* layout = CreatePanelGridLayout(this); |
| + SetLayoutManager(layout); |
| + |
| + track_toggle_ = new views::TextButton(kStartTrackingLabel); |
| + track_toggle_->SetListener(this, 1); |
| + show_button_ = new views::TextButton(KShowCurrentLabel); |
| + show_button_->SetListener(this, 2); |
| + clear_button_ = new views::TextButton(kClearLabel); |
| + clear_button_->SetListener(this, 3); |
| + |
| + text_field_ = new views::TextField(static_cast<views::TextField::StyleFlags>( |
| + views::TextField::STYLE_MULTILINE)); |
| + text_field_->SetReadOnly(true); |
| + |
| + // TODO(brettw): We may want to add this in the future. It can't be called |
| + // from here, though, since the hwnd for the field hasn't been created yet. |
| + // |
| + // This raises the maximum number of chars from 32K to some large maximum, |
| + // probably 2GB. 32K is not nearly enough for our use-case. |
| + //SendMessageW(text_field_->GetNativeComponent(), EM_SETLIMITTEXT, 0, 0); |
| + |
| + static const int first_column_set = 1; |
| + views::ColumnSet* column_set = layout->AddColumnSet(first_column_set); |
| + column_set->AddColumn(views::GridLayout::CENTER, views::GridLayout::CENTER, |
| + 33.33f, views::GridLayout::FIXED, 0, 0); |
| + column_set->AddColumn(views::GridLayout::CENTER, views::GridLayout::CENTER, |
| + 33.33f, views::GridLayout::FIXED, 0, 0); |
| + column_set->AddColumn(views::GridLayout::CENTER, views::GridLayout::CENTER, |
| + 33.33f, views::GridLayout::FIXED, 0, 0); |
| + |
| + static const int text_column_set = 2; |
| + column_set = layout->AddColumnSet(text_column_set); |
| + column_set->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 100.0f, |
| + views::GridLayout::FIXED, 0, 0); |
| + |
| + layout->StartRow(0, first_column_set); |
| + layout->AddView(track_toggle_); |
| + layout->AddView(show_button_); |
| + layout->AddView(clear_button_); |
| + layout->AddPaddingRow(0, kRelatedControlVerticalSpacing); |
| + layout->StartRow(1.0f, text_column_set); |
| + layout->AddView(text_field_); |
| +} |
| + |
| +gfx::Size AboutNetworkDialog::GetPreferredSize() { |
| + return gfx::Size(800, 400); |
| +} |
| + |
| +views::View* AboutNetworkDialog::GetContentsView() { |
| + return this; |
| +} |
| + |
| +int AboutNetworkDialog::GetDialogButtons() const { |
| + // Don't want OK or Cancel. |
| + return 0; |
| +} |
| + |
| +std::wstring AboutNetworkDialog::GetWindowTitle() const { |
| + return L"about:network"; |
| +} |
| + |
| +void AboutNetworkDialog::Layout() { |
| + GetLayoutManager()->Layout(this); |
| +} |
| + |
| +bool AboutNetworkDialog::CanResize() const { |
| + return true; |
| +} |
| + |
| +void AboutNetworkDialog::ButtonPressed(views::BaseButton* button) { |
| + if (button == track_toggle_) { |
| + if (tracking_) { |
| + track_toggle_->SetText(kStartTrackingLabel); |
| + tracking_ = false; |
| + tracker->StopTracking(); |
| + } else { |
| + track_toggle_->SetText(kStopTrackingLabel); |
| + tracking_ = true; |
| + tracker->StartTracking(); |
| + } |
| + track_toggle_->SchedulePaint(); |
| + } else if (button == show_button_) { |
| + tracker->ReportStatus(); |
| + } else if (button == clear_button_) { |
| + text_field_->SetText(std::wstring()); |
| + } |
| +} |