| OLD | NEW |
| 1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/views/about_network_dialog.h" | 5 #include "chrome/browser/views/logging_about_dialog.h" |
| 6 | 6 |
| 7 #include "base/string_util.h" | |
| 8 #include "base/thread.h" | |
| 9 #include "chrome/browser/browser_process.h" | |
| 10 #include "chrome/browser/views/standard_layout.h" | 7 #include "chrome/browser/views/standard_layout.h" |
| 11 #include "chrome/views/grid_layout.h" | 8 #include "chrome/views/grid_layout.h" |
| 12 #include "chrome/views/text_button.h" | |
| 13 #include "chrome/views/text_field.h" | 9 #include "chrome/views/text_field.h" |
| 14 #include "chrome/views/window.h" | 10 #include "chrome/views/window.h" |
| 15 #include "net/url_request/url_request.h" | |
| 16 #include "net/url_request/url_request_job.h" | |
| 17 #include "net/url_request/url_request_job_tracker.h" | |
| 18 | 11 |
| 19 namespace { | 12 LoggingAboutDialog::LoggingAboutDialog() { |
| 20 | |
| 21 // We don't localize this UI since this is a developer-only feature. | |
| 22 const wchar_t kStartTrackingLabel[] = L"Start tracking"; | |
| 23 const wchar_t kStopTrackingLabel[] = L"Stop tracking"; | |
| 24 const wchar_t kShowCurrentLabel[] = L"Show Current"; | |
| 25 const wchar_t kClearLabel[] = L"Clear"; | |
| 26 | |
| 27 // The singleton dialog box. This is non-NULL when a dialog is active so we | |
| 28 // know not to create a new one. | |
| 29 AboutNetworkDialog* active_dialog = NULL; | |
| 30 | |
| 31 // Returns a string representing the URL, handling the case where the spec | |
| 32 // is invalid. | |
| 33 std::wstring StringForURL(const GURL& url) { | |
| 34 if (url.is_valid()) | |
| 35 return UTF8ToWide(url.spec()); | |
| 36 return UTF8ToWide(url.possibly_invalid_spec()) + L" (invalid)"; | |
| 37 } | 13 } |
| 38 | 14 |
| 39 std::wstring URLForJob(URLRequestJob* job) { | 15 LoggingAboutDialog::~LoggingAboutDialog() { |
| 40 URLRequest* request = job->request(); | |
| 41 if (request) | |
| 42 return StringForURL(request->url()); | |
| 43 return std::wstring(L"(orphaned)"); | |
| 44 } | 16 } |
| 45 | 17 |
| 46 // JobTracker ------------------------------------------------------------------ | 18 void LoggingAboutDialog::SetupControls() { |
| 47 | |
| 48 // A JobTracker is allocated to monitor network jobs running on the IO | |
| 49 // thread. This allows the NetworkStatusView to remain single-threaded. | |
| 50 class JobTracker : public URLRequestJobTracker::JobObserver, | |
| 51 public base::RefCountedThreadSafe<JobTracker> { | |
| 52 public: | |
| 53 JobTracker(AboutNetworkDialog* view); | |
| 54 ~JobTracker(); | |
| 55 | |
| 56 // Called by the NetworkStatusView on the main application thread. | |
| 57 void StartTracking(); | |
| 58 void StopTracking(); | |
| 59 void ReportStatus(); | |
| 60 | |
| 61 // URLRequestJobTracker::JobObserver methods (called on the IO thread): | |
| 62 virtual void OnJobAdded(URLRequestJob* job); | |
| 63 virtual void OnJobRemoved(URLRequestJob* job); | |
| 64 virtual void OnJobDone(URLRequestJob* job, const URLRequestStatus& status); | |
| 65 virtual void OnJobRedirect(URLRequestJob* job, const GURL& location, | |
| 66 int status_code); | |
| 67 virtual void OnBytesRead(URLRequestJob* job, int byte_count); | |
| 68 | |
| 69 // The JobTracker may be deleted after NetworkStatusView is deleted. | |
| 70 void DetachView() { view_ = NULL; } | |
| 71 | |
| 72 private: | |
| 73 void InvokeOnIOThread(void (JobTracker::*method)()); | |
| 74 | |
| 75 // Called on the IO thread | |
| 76 void OnStartTracking(); | |
| 77 void OnStopTracking(); | |
| 78 void OnReportStatus(); | |
| 79 void AppendText(const std::wstring& text); | |
| 80 | |
| 81 // Called on the main thread | |
| 82 void OnAppendText(const std::wstring& text); | |
| 83 | |
| 84 AboutNetworkDialog* view_; | |
| 85 MessageLoop* view_message_loop_; | |
| 86 }; | |
| 87 | |
| 88 // main thread: | |
| 89 JobTracker::JobTracker(AboutNetworkDialog* view) | |
| 90 : view_(view), | |
| 91 view_message_loop_(MessageLoop::current()) { | |
| 92 } | |
| 93 | |
| 94 JobTracker::~JobTracker() { | |
| 95 } | |
| 96 | |
| 97 // main thread: | |
| 98 void JobTracker::InvokeOnIOThread(void (JobTracker::*m)()) { | |
| 99 base::Thread* thread = g_browser_process->io_thread(); | |
| 100 if (!thread) | |
| 101 return; | |
| 102 thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(this, m)); | |
| 103 } | |
| 104 | |
| 105 // main thread: | |
| 106 void JobTracker::StartTracking() { | |
| 107 DCHECK(MessageLoop::current() == view_message_loop_); | |
| 108 DCHECK(view_); | |
| 109 InvokeOnIOThread(&JobTracker::OnStartTracking); | |
| 110 } | |
| 111 | |
| 112 // main thread: | |
| 113 void JobTracker::StopTracking() { | |
| 114 DCHECK(MessageLoop::current() == view_message_loop_); | |
| 115 // The tracker should not be deleted before it is removed from observer | |
| 116 // list. | |
| 117 AddRef(); | |
| 118 InvokeOnIOThread(&JobTracker::OnStopTracking); | |
| 119 } | |
| 120 | |
| 121 // main thread: | |
| 122 void JobTracker::ReportStatus() { | |
| 123 DCHECK(MessageLoop::current() == view_message_loop_); | |
| 124 InvokeOnIOThread(&JobTracker::OnReportStatus); | |
| 125 } | |
| 126 | |
| 127 // main thread: | |
| 128 void JobTracker::OnAppendText(const std::wstring& text) { | |
| 129 DCHECK(MessageLoop::current() == view_message_loop_); | |
| 130 if (view_ && view_->tracking()) | |
| 131 view_->AppendText(text); | |
| 132 } | |
| 133 | |
| 134 // IO thread: | |
| 135 void JobTracker::AppendText(const std::wstring& text) { | |
| 136 DCHECK(MessageLoop::current() != view_message_loop_); | |
| 137 view_message_loop_->PostTask(FROM_HERE, NewRunnableMethod( | |
| 138 this, &JobTracker::OnAppendText, text)); | |
| 139 } | |
| 140 | |
| 141 // IO thread: | |
| 142 void JobTracker::OnStartTracking() { | |
| 143 DCHECK(MessageLoop::current() != view_message_loop_); | |
| 144 g_url_request_job_tracker.AddObserver(this); | |
| 145 } | |
| 146 | |
| 147 // IO thread: | |
| 148 void JobTracker::OnStopTracking() { | |
| 149 DCHECK(MessageLoop::current() != view_message_loop_); | |
| 150 g_url_request_job_tracker.RemoveObserver(this); | |
| 151 // Balance the AddRef() in StopTracking() called in main thread. | |
| 152 Release(); | |
| 153 } | |
| 154 | |
| 155 // IO thread: | |
| 156 void JobTracker::OnReportStatus() { | |
| 157 DCHECK(MessageLoop::current() != view_message_loop_); | |
| 158 | |
| 159 std::wstring text(L"\r\n===== Active Job Summary =====\r\n"); | |
| 160 | |
| 161 URLRequestJobTracker::JobIterator begin_job = | |
| 162 g_url_request_job_tracker.begin(); | |
| 163 URLRequestJobTracker::JobIterator end_job = g_url_request_job_tracker.end(); | |
| 164 int orphaned_count = 0; | |
| 165 int regular_count = 0; | |
| 166 for (URLRequestJobTracker::JobIterator cur = begin_job; | |
| 167 cur != end_job; ++cur) { | |
| 168 URLRequestJob* job = (*cur); | |
| 169 URLRequest* request = job->request(); | |
| 170 if (!request) { | |
| 171 orphaned_count++; | |
| 172 continue; | |
| 173 } | |
| 174 | |
| 175 regular_count++; | |
| 176 | |
| 177 // active state | |
| 178 if (job->is_done()) | |
| 179 text.append(L" Done: "); | |
| 180 else | |
| 181 text.append(L" Active: "); | |
| 182 | |
| 183 // URL | |
| 184 text.append(StringForURL(request->url())); | |
| 185 text.append(L"\r\n"); | |
| 186 } | |
| 187 | |
| 188 if (regular_count == 0) | |
| 189 text.append(L" (No active jobs)\r\n"); | |
| 190 | |
| 191 if (orphaned_count) { | |
| 192 wchar_t buf[64]; | |
| 193 swprintf(buf, arraysize(buf), L" %d orphaned jobs\r\n", orphaned_count); | |
| 194 text.append(buf); | |
| 195 } | |
| 196 | |
| 197 text.append(L"=====\r\n\r\n"); | |
| 198 AppendText(text); | |
| 199 } | |
| 200 | |
| 201 // IO thread: | |
| 202 void JobTracker::OnJobAdded(URLRequestJob* job) { | |
| 203 DCHECK(MessageLoop::current() != view_message_loop_); | |
| 204 | |
| 205 std::wstring text(L"+ New job : "); | |
| 206 text.append(URLForJob(job)); | |
| 207 text.append(L"\r\n"); | |
| 208 AppendText(text); | |
| 209 } | |
| 210 | |
| 211 // IO thread: | |
| 212 void JobTracker::OnJobRemoved(URLRequestJob* job) { | |
| 213 DCHECK(MessageLoop::current() != view_message_loop_); | |
| 214 } | |
| 215 | |
| 216 // IO thread: | |
| 217 void JobTracker::OnJobDone(URLRequestJob* job, | |
| 218 const URLRequestStatus& status) { | |
| 219 DCHECK(MessageLoop::current() != view_message_loop_); | |
| 220 | |
| 221 std::wstring text; | |
| 222 if (status.is_success()) { | |
| 223 text.assign(L"- Complete: "); | |
| 224 } else if (status.status() == URLRequestStatus::CANCELED) { | |
| 225 text.assign(L"- Canceled: "); | |
| 226 } else if (status.status() == URLRequestStatus::HANDLED_EXTERNALLY) { | |
| 227 text.assign(L"- Handled externally: "); | |
| 228 } else { | |
| 229 wchar_t buf[32]; | |
| 230 swprintf(buf, arraysize(buf), L"Failed with %d: ", status.os_error()); | |
| 231 text.assign(buf); | |
| 232 } | |
| 233 | |
| 234 text.append(URLForJob(job)); | |
| 235 text.append(L"\r\n"); | |
| 236 AppendText(text); | |
| 237 } | |
| 238 | |
| 239 // IO thread: | |
| 240 void JobTracker::OnJobRedirect(URLRequestJob* job, | |
| 241 const GURL& location, | |
| 242 int status_code) { | |
| 243 DCHECK(MessageLoop::current() != view_message_loop_); | |
| 244 | |
| 245 std::wstring text(L"- Redirect: "); | |
| 246 text.append(URLForJob(job)); | |
| 247 text.append(L"\r\n "); | |
| 248 | |
| 249 wchar_t buf[16]; | |
| 250 swprintf(buf, arraysize(buf), L"(%d) to: ", status_code); | |
| 251 text.append(buf); | |
| 252 | |
| 253 text.append(StringForURL(location)); | |
| 254 text.append(L"\r\n"); | |
| 255 AppendText(text); | |
| 256 } | |
| 257 | |
| 258 void JobTracker::OnBytesRead(URLRequestJob* job, int byte_count) { | |
| 259 } | |
| 260 | |
| 261 // The singleton job tracker associated with the dialog. | |
| 262 JobTracker* tracker = NULL; | |
| 263 | |
| 264 } // namespace | |
| 265 | |
| 266 // AboutNetworkDialog ---------------------------------------------------------- | |
| 267 | |
| 268 AboutNetworkDialog::AboutNetworkDialog() : tracking_(false) { | |
| 269 SetupControls(); | |
| 270 tracker = new JobTracker(this); | |
| 271 tracker->AddRef(); | |
| 272 } | |
| 273 | |
| 274 AboutNetworkDialog::~AboutNetworkDialog() { | |
| 275 active_dialog = NULL; | |
| 276 tracker->Release(); | |
| 277 tracker = NULL; | |
| 278 } | |
| 279 | |
| 280 // static | |
| 281 void AboutNetworkDialog::RunDialog() { | |
| 282 if (!active_dialog) { | |
| 283 active_dialog = new AboutNetworkDialog; | |
| 284 views::Window::CreateChromeWindow(NULL, gfx::Rect(), active_dialog)->Show(); | |
| 285 } else { | |
| 286 // TOOD(brettw) it would be nice to focus the existing window. | |
| 287 } | |
| 288 } | |
| 289 | |
| 290 void AboutNetworkDialog::AppendText(const std::wstring& text) { | |
| 291 text_field_->AppendText(text); | |
| 292 } | |
| 293 | |
| 294 void AboutNetworkDialog::SetupControls() { | |
| 295 views::GridLayout* layout = CreatePanelGridLayout(this); | 19 views::GridLayout* layout = CreatePanelGridLayout(this); |
| 296 SetLayoutManager(layout); | 20 SetLayoutManager(layout); |
| 297 | 21 |
| 298 track_toggle_ = new views::TextButton(kStartTrackingLabel); | 22 static const int first_column_set = 1; |
| 299 track_toggle_->SetListener(this, 1); | 23 views::ColumnSet* button_set = layout->AddColumnSet(first_column_set); |
| 300 show_button_ = new views::TextButton(kShowCurrentLabel); | 24 SetupButtonColumnSet(button_set); |
| 301 show_button_->SetListener(this, 2); | |
| 302 clear_button_ = new views::TextButton(kClearLabel); | |
| 303 clear_button_->SetListener(this, 3); | |
| 304 | 25 |
| 305 text_field_ = new views::TextField(static_cast<views::TextField::StyleFlags>( | 26 text_field_ = new views::TextField(static_cast<views::TextField::StyleFlags>( |
| 306 views::TextField::STYLE_MULTILINE)); | 27 views::TextField::STYLE_MULTILINE)); |
| 307 text_field_->SetReadOnly(true); | 28 text_field_->SetReadOnly(true); |
| 308 | 29 |
| 309 // TODO(brettw): We may want to add this in the future. It can't be called | 30 // TODO(brettw): We may want to add this in the future. It can't be called |
| 310 // from here, though, since the hwnd for the field hasn't been created yet. | 31 // from here, though, since the hwnd for the field hasn't been created yet. |
| 311 // | 32 // |
| 312 // This raises the maximum number of chars from 32K to some large maximum, | 33 // This raises the maximum number of chars from 32K to some large maximum, |
| 313 // probably 2GB. 32K is not nearly enough for our use-case. | 34 // probably 2GB. 32K is not nearly enough for our use-case. |
| 314 //SendMessageW(text_field_->GetNativeComponent(), EM_SETLIMITTEXT, 0, 0); | 35 //SendMessageW(text_field_->GetNativeComponent(), EM_SETLIMITTEXT, 0, 0); |
| 315 | 36 |
| 316 static const int first_column_set = 1; | |
| 317 views::ColumnSet* column_set = layout->AddColumnSet(first_column_set); | |
| 318 column_set->AddColumn(views::GridLayout::CENTER, views::GridLayout::CENTER, | |
| 319 33.33f, views::GridLayout::FIXED, 0, 0); | |
| 320 column_set->AddColumn(views::GridLayout::CENTER, views::GridLayout::CENTER, | |
| 321 33.33f, views::GridLayout::FIXED, 0, 0); | |
| 322 column_set->AddColumn(views::GridLayout::CENTER, views::GridLayout::CENTER, | |
| 323 33.33f, views::GridLayout::FIXED, 0, 0); | |
| 324 | |
| 325 static const int text_column_set = 2; | 37 static const int text_column_set = 2; |
| 326 column_set = layout->AddColumnSet(text_column_set); | 38 views::ColumnSet* column_set = layout->AddColumnSet(text_column_set); |
| 327 column_set->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 100.0f
, | 39 column_set->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 100.0f
, |
| 328 views::GridLayout::FIXED, 0, 0); | 40 views::GridLayout::FIXED, 0, 0); |
| 329 | 41 |
| 330 layout->StartRow(0, first_column_set); | 42 layout->StartRow(0, first_column_set); |
| 331 layout->AddView(track_toggle_); | 43 AddButtonControlsToLayout(layout); |
| 332 layout->AddView(show_button_); | |
| 333 layout->AddView(clear_button_); | |
| 334 layout->AddPaddingRow(0, kRelatedControlVerticalSpacing); | 44 layout->AddPaddingRow(0, kRelatedControlVerticalSpacing); |
| 335 layout->StartRow(1.0f, text_column_set); | 45 layout->StartRow(1.0f, text_column_set); |
| 336 layout->AddView(text_field_); | 46 layout->AddView(text_field_); |
| 337 } | 47 } |
| 338 | 48 |
| 339 gfx::Size AboutNetworkDialog::GetPreferredSize() { | 49 void LoggingAboutDialog::AppendText(const std::wstring& text) { |
| 50 text_field_->AppendText(text); |
| 51 } |
| 52 |
| 53 gfx::Size LoggingAboutDialog::GetPreferredSize() { |
| 340 return gfx::Size(800, 400); | 54 return gfx::Size(800, 400); |
| 341 } | 55 } |
| 342 | 56 |
| 343 views::View* AboutNetworkDialog::GetContentsView() { | 57 views::View* LoggingAboutDialog::GetContentsView() { |
| 344 return this; | 58 return this; |
| 345 } | 59 } |
| 346 | 60 |
| 347 int AboutNetworkDialog::GetDialogButtons() const { | 61 int LoggingAboutDialog::GetDialogButtons() const { |
| 348 // Don't want OK or Cancel. | 62 // Don't want OK or Cancel. |
| 349 return 0; | 63 return 0; |
| 350 } | 64 } |
| 351 | 65 |
| 352 std::wstring AboutNetworkDialog::GetWindowTitle() const { | 66 std::wstring LoggingAboutDialog::GetWindowTitle() const { |
| 353 return L"about:network"; | 67 return L"about:network"; |
| 354 } | 68 } |
| 355 | 69 |
| 356 void AboutNetworkDialog::Layout() { | 70 bool LoggingAboutDialog::CanResize() const { |
| 357 GetLayoutManager()->Layout(this); | |
| 358 } | |
| 359 | |
| 360 bool AboutNetworkDialog::CanResize() const { | |
| 361 return true; | 71 return true; |
| 362 } | 72 } |
| 363 | |
| 364 void AboutNetworkDialog::ButtonPressed(views::BaseButton* button) { | |
| 365 if (button == track_toggle_) { | |
| 366 if (tracking_) { | |
| 367 track_toggle_->SetText(kStartTrackingLabel); | |
| 368 tracking_ = false; | |
| 369 tracker->StopTracking(); | |
| 370 } else { | |
| 371 track_toggle_->SetText(kStopTrackingLabel); | |
| 372 tracking_ = true; | |
| 373 tracker->StartTracking(); | |
| 374 } | |
| 375 track_toggle_->SchedulePaint(); | |
| 376 } else if (button == show_button_) { | |
| 377 tracker->ReportStatus(); | |
| 378 } else if (button == clear_button_) { | |
| 379 text_field_->SetText(std::wstring()); | |
| 380 } | |
| 381 } | |
| OLD | NEW |