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