OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 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/ui/views/hung_renderer_view.h" | 5 #include "chrome/browser/ui/browser_dialogs.h" |
6 | 6 |
7 #if defined(OS_WIN) && !defined(USE_AURA) | 7 #if defined(OS_WIN) && !defined(USE_AURA) |
8 #include <windows.h> | 8 #include <windows.h> |
9 #endif | 9 #endif |
10 | 10 |
11 #include "base/i18n/rtl.h" | 11 #include "base/i18n/rtl.h" |
12 #include "base/memory/scoped_vector.h" | 12 #include "base/memory/scoped_vector.h" |
13 #include "base/process_util.h" | 13 #include "base/process_util.h" |
14 #include "base/utf_string_conversions.h" | 14 #include "base/utf_string_conversions.h" |
15 #include "chrome/browser/favicon/favicon_tab_helper.h" | 15 #include "chrome/browser/favicon/favicon_tab_helper.h" |
16 #include "chrome/browser/platform_util.h" | 16 #include "chrome/browser/platform_util.h" |
17 #include "chrome/browser/ui/browser_dialogs.h" | |
18 #include "chrome/browser/ui/tab_contents/core_tab_helper.h" | 17 #include "chrome/browser/ui/tab_contents/core_tab_helper.h" |
| 18 #include "chrome/browser/ui/tab_contents/tab_contents.h" |
19 #include "chrome/browser/ui/tab_contents/tab_contents_iterator.h" | 19 #include "chrome/browser/ui/tab_contents/tab_contents_iterator.h" |
20 #include "chrome/common/chrome_constants.h" | 20 #include "chrome/common/chrome_constants.h" |
21 #include "chrome/common/logging_chrome.h" | 21 #include "chrome/common/logging_chrome.h" |
22 #include "content/public/browser/render_process_host.h" | 22 #include "content/public/browser/render_process_host.h" |
23 #include "content/public/browser/render_view_host.h" | 23 #include "content/public/browser/render_view_host.h" |
| 24 #include "content/public/browser/web_contents.h" |
24 #include "content/public/common/result_codes.h" | 25 #include "content/public/common/result_codes.h" |
25 #include "grit/chromium_strings.h" | 26 #include "grit/chromium_strings.h" |
26 #include "grit/generated_resources.h" | 27 #include "grit/generated_resources.h" |
27 #include "grit/theme_resources.h" | 28 #include "grit/theme_resources.h" |
28 #include "ui/base/l10n/l10n_util.h" | 29 #include "ui/base/l10n/l10n_util.h" |
29 #include "ui/base/resource/resource_bundle.h" | 30 #include "ui/base/resource/resource_bundle.h" |
30 #include "ui/gfx/canvas.h" | 31 #include "ui/gfx/canvas.h" |
| 32 #include "ui/views/controls/button/text_button.h" |
31 #include "ui/views/controls/image_view.h" | 33 #include "ui/views/controls/image_view.h" |
32 #include "ui/views/controls/label.h" | 34 #include "ui/views/controls/label.h" |
| 35 #include "ui/views/controls/table/group_table_model.h" |
| 36 #include "ui/views/controls/table/group_table_view.h" |
33 #include "ui/views/layout/grid_layout.h" | 37 #include "ui/views/layout/grid_layout.h" |
34 #include "ui/views/layout/layout_constants.h" | 38 #include "ui/views/layout/layout_constants.h" |
35 #include "ui/views/widget/widget.h" | 39 #include "ui/views/widget/widget.h" |
36 #include "ui/views/window/client_view.h" | 40 #include "ui/views/window/client_view.h" |
| 41 #include "ui/views/window/dialog_delegate.h" |
37 | 42 |
38 #if defined(USE_AURA) | 43 #if defined(USE_AURA) |
39 #include "ui/aura/window.h" | 44 #include "ui/aura/window.h" |
40 #endif | 45 #endif |
41 | 46 |
42 #if defined(OS_WIN) | 47 #if defined(OS_WIN) |
43 #include "chrome/browser/hang_monitor/hang_crash_dump_win.h" | 48 #include "chrome/browser/hang_monitor/hang_crash_dump_win.h" |
44 #endif | 49 #endif |
45 | 50 |
46 HungRendererDialogView* HungRendererDialogView::g_instance_ = NULL; | 51 class HungRendererDialogView; |
| 52 |
| 53 using content::RenderViewHost; |
| 54 using content::WebContents; |
| 55 |
| 56 namespace { |
| 57 // We only support showing one of these at a time per app. |
| 58 HungRendererDialogView* g_instance = NULL; |
| 59 } |
| 60 |
| 61 /////////////////////////////////////////////////////////////////////////////// |
| 62 // HungPagesTableModel |
| 63 |
| 64 class HungPagesTableModel : public views::GroupTableModel { |
| 65 public: |
| 66 // The Delegate is notified any time a WebContents the model is listening to |
| 67 // is destroyed. |
| 68 class Delegate { |
| 69 public: |
| 70 virtual void TabDestroyed() = 0; |
| 71 |
| 72 protected: |
| 73 virtual ~Delegate() {} |
| 74 }; |
| 75 |
| 76 explicit HungPagesTableModel(Delegate* delegate); |
| 77 virtual ~HungPagesTableModel(); |
| 78 |
| 79 void InitForWebContents(WebContents* hung_contents); |
| 80 |
| 81 // Returns the first RenderProcessHost, or NULL if there aren't any |
| 82 // WebContents. |
| 83 content::RenderProcessHost* GetRenderProcessHost(); |
| 84 |
| 85 // Returns the first RenderViewHost, or NULL if there aren't any WebContents. |
| 86 RenderViewHost* GetRenderViewHost(); |
| 87 |
| 88 // Overridden from views::GroupTableModel: |
| 89 virtual int RowCount(); |
| 90 virtual string16 GetText(int row, int column_id); |
| 91 virtual gfx::ImageSkia GetIcon(int row); |
| 92 virtual void SetObserver(ui::TableModelObserver* observer); |
| 93 virtual void GetGroupRangeForItem(int item, views::GroupRange* range); |
| 94 |
| 95 private: |
| 96 // Used to track a single WebContents. If the WebContents is destroyed |
| 97 // TabDestroyed() is invoked on the model. |
| 98 class WebContentsObserverImpl : public content::WebContentsObserver { |
| 99 public: |
| 100 WebContentsObserverImpl(HungPagesTableModel* model, |
| 101 TabContents* tab); |
| 102 |
| 103 WebContents* web_contents() const { |
| 104 return content::WebContentsObserver::web_contents(); |
| 105 } |
| 106 |
| 107 FaviconTabHelper* favicon_tab_helper() { |
| 108 return tab_->favicon_tab_helper(); |
| 109 } |
| 110 |
| 111 // WebContentsObserver overrides: |
| 112 virtual void RenderViewGone(base::TerminationStatus status) OVERRIDE; |
| 113 virtual void WebContentsDestroyed(WebContents* tab) OVERRIDE; |
| 114 |
| 115 private: |
| 116 HungPagesTableModel* model_; |
| 117 TabContents* tab_; |
| 118 |
| 119 DISALLOW_COPY_AND_ASSIGN(WebContentsObserverImpl); |
| 120 }; |
| 121 |
| 122 // Invoked when a WebContents is destroyed. Cleans up |tab_observers_| and |
| 123 // notifies the observer and delegate. |
| 124 void TabDestroyed(WebContentsObserverImpl* tab); |
| 125 |
| 126 typedef ScopedVector<WebContentsObserverImpl> TabObservers; |
| 127 TabObservers tab_observers_; |
| 128 |
| 129 ui::TableModelObserver* observer_; |
| 130 Delegate* delegate_; |
| 131 |
| 132 DISALLOW_COPY_AND_ASSIGN(HungPagesTableModel); |
| 133 }; |
47 | 134 |
48 /////////////////////////////////////////////////////////////////////////////// | 135 /////////////////////////////////////////////////////////////////////////////// |
49 // HungPagesTableModel, public: | 136 // HungPagesTableModel, public: |
50 | 137 |
51 HungPagesTableModel::HungPagesTableModel(Delegate* delegate) | 138 HungPagesTableModel::HungPagesTableModel(Delegate* delegate) |
52 : observer_(NULL), | 139 : observer_(NULL), |
53 delegate_(delegate) { | 140 delegate_(delegate) { |
54 } | 141 } |
55 | 142 |
56 HungPagesTableModel::~HungPagesTableModel() { | 143 HungPagesTableModel::~HungPagesTableModel() { |
(...skipping 95 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
152 } | 239 } |
153 | 240 |
154 void HungPagesTableModel::WebContentsObserverImpl::WebContentsDestroyed( | 241 void HungPagesTableModel::WebContentsObserverImpl::WebContentsDestroyed( |
155 WebContents* tab) { | 242 WebContents* tab) { |
156 model_->TabDestroyed(this); | 243 model_->TabDestroyed(this); |
157 } | 244 } |
158 | 245 |
159 /////////////////////////////////////////////////////////////////////////////// | 246 /////////////////////////////////////////////////////////////////////////////// |
160 // HungRendererDialogView | 247 // HungRendererDialogView |
161 | 248 |
| 249 class HungRendererDialogView : public views::DialogDelegateView, |
| 250 public views::ButtonListener, |
| 251 public HungPagesTableModel::Delegate { |
| 252 public: |
| 253 HungRendererDialogView(); |
| 254 ~HungRendererDialogView(); |
| 255 |
| 256 void ShowForWebContents(WebContents* contents); |
| 257 void EndForWebContents(WebContents* contents); |
| 258 |
| 259 // views::DialogDelegateView overrides: |
| 260 virtual string16 GetWindowTitle() const OVERRIDE; |
| 261 virtual void WindowClosing() OVERRIDE; |
| 262 virtual int GetDialogButtons() const OVERRIDE; |
| 263 virtual string16 GetDialogButtonLabel(ui::DialogButton button) const OVERRIDE; |
| 264 virtual views::View* GetExtraView() OVERRIDE; |
| 265 virtual bool Accept(bool window_closing) OVERRIDE; |
| 266 virtual views::View* GetContentsView() OVERRIDE; |
| 267 |
| 268 // views::ButtonListener overrides: |
| 269 virtual void ButtonPressed(views::Button* sender, |
| 270 const views::Event& event) OVERRIDE; |
| 271 |
| 272 // HungPagesTableModel::Delegate overrides: |
| 273 virtual void TabDestroyed() OVERRIDE; |
| 274 |
| 275 protected: |
| 276 // views::View overrides: |
| 277 virtual void ViewHierarchyChanged(bool is_add, |
| 278 views::View* parent, |
| 279 views::View* child) OVERRIDE; |
| 280 |
| 281 private: |
| 282 // Initialize the controls in this dialog. |
| 283 void Init(); |
| 284 void CreateKillButtonView(); |
| 285 |
| 286 // Returns the bounds the dialog should be displayed at to be meaningfully |
| 287 // associated with the specified WebContents. |
| 288 gfx::Rect GetDisplayBounds(WebContents* contents); |
| 289 |
| 290 static void InitClass(); |
| 291 |
| 292 // Controls within the dialog box. |
| 293 views::GroupTableView* hung_pages_table_; |
| 294 |
| 295 // The button we insert into the ClientView to kill the errant process. This |
| 296 // is parented to a container view that uses a grid layout to align it |
| 297 // properly. |
| 298 views::TextButton* kill_button_; |
| 299 views::View* kill_button_container_; |
| 300 |
| 301 // The model that provides the contents of the table that shows a list of |
| 302 // pages affected by the hang. |
| 303 scoped_ptr<HungPagesTableModel> hung_pages_table_model_; |
| 304 |
| 305 // Whether or not we've created controls for ourself. |
| 306 bool initialized_; |
| 307 |
| 308 // An amusing icon image. |
| 309 static gfx::ImageSkia* frozen_icon_; |
| 310 |
| 311 DISALLOW_COPY_AND_ASSIGN(HungRendererDialogView); |
| 312 }; |
| 313 |
162 // static | 314 // static |
163 gfx::ImageSkia* HungRendererDialogView::frozen_icon_ = NULL; | 315 gfx::ImageSkia* HungRendererDialogView::frozen_icon_ = NULL; |
164 | 316 |
165 // The distance in pixels from the top of the relevant contents to place the | 317 // The distance in pixels from the top of the relevant contents to place the |
166 // warning window. | 318 // warning window. |
167 static const int kOverlayContentsOffsetY = 50; | 319 static const int kOverlayContentsOffsetY = 50; |
168 | 320 |
169 // The dimensions of the hung pages list table view, in pixels. | 321 // The dimensions of the hung pages list table view, in pixels. |
170 static const int kTableViewWidth = 300; | 322 static const int kTableViewWidth = 300; |
171 static const int kTableViewHeight = 100; | 323 static const int kTableViewHeight = 100; |
172 | 324 |
173 /////////////////////////////////////////////////////////////////////////////// | 325 /////////////////////////////////////////////////////////////////////////////// |
174 // HungRendererDialogView, public: | 326 // HungRendererDialogView, public: |
175 | 327 |
176 #if !defined(OS_WIN) | |
177 | |
178 // static | |
179 HungRendererDialogView* HungRendererDialogView::Create() { | |
180 if (!g_instance_) { | |
181 g_instance_ = new HungRendererDialogView; | |
182 views::Widget::CreateWindow(g_instance_); | |
183 } | |
184 return g_instance_; | |
185 } | |
186 #endif // defined(OS_WIN) | |
187 | |
188 // static | |
189 HungRendererDialogView* HungRendererDialogView::GetInstance() { | |
190 return g_instance_; | |
191 } | |
192 | |
193 HungRendererDialogView::HungRendererDialogView() | 328 HungRendererDialogView::HungRendererDialogView() |
194 : hung_pages_table_(NULL), | 329 : hung_pages_table_(NULL), |
195 kill_button_(NULL), | 330 kill_button_(NULL), |
196 kill_button_container_(NULL), | 331 kill_button_container_(NULL), |
197 initialized_(false) { | 332 initialized_(false) { |
198 InitClass(); | 333 InitClass(); |
199 } | 334 } |
200 | 335 |
201 HungRendererDialogView::~HungRendererDialogView() { | 336 HungRendererDialogView::~HungRendererDialogView() { |
202 hung_pages_table_->SetModel(NULL); | 337 hung_pages_table_->SetModel(NULL); |
203 } | 338 } |
204 | 339 |
205 void HungRendererDialogView::ShowForWebContents(WebContents* contents) { | 340 void HungRendererDialogView::ShowForWebContents(WebContents* contents) { |
206 DCHECK(contents && GetWidget()); | 341 DCHECK(contents && GetWidget()); |
207 | 342 |
208 // Don't show the warning unless the foreground window is the frame, or this | 343 // Don't show the warning unless the foreground window is the frame, or this |
209 // window (but still invisible). If the user has another window or | 344 // window (but still invisible). If the user has another window or |
210 // application selected, activating ourselves is rude. | 345 // application selected, activating ourselves is rude. |
211 if (!IsFrameActive(contents)) | 346 gfx::NativeView frame_view = |
| 347 platform_util::GetTopLevel(contents->GetNativeView()); |
| 348 if (!platform_util::IsWindowActive(frame_view) && |
| 349 !platform_util::IsWindowActive(GetWidget()->GetNativeWindow())) { |
212 return; | 350 return; |
| 351 } |
213 | 352 |
214 if (!GetWidget()->IsActive()) { | 353 if (!GetWidget()->IsActive()) { |
215 gfx::Rect bounds = GetDisplayBounds(contents); | 354 gfx::Rect bounds = GetDisplayBounds(contents); |
216 | |
217 gfx::NativeView frame_view = | |
218 platform_util::GetTopLevel(contents->GetNativeView()); | |
219 | |
220 views::Widget* insert_after = | 355 views::Widget* insert_after = |
221 views::Widget::GetWidgetForNativeView(frame_view); | 356 views::Widget::GetWidgetForNativeView(frame_view); |
222 GetWidget()->SetBoundsConstrained(bounds); | 357 GetWidget()->SetBoundsConstrained(bounds); |
223 if (insert_after) | 358 if (insert_after) |
224 GetWidget()->StackAboveWidget(insert_after); | 359 GetWidget()->StackAboveWidget(insert_after); |
225 | 360 |
226 // We only do this if the window isn't active (i.e. hasn't been shown yet, | 361 // We only do this if the window isn't active (i.e. hasn't been shown yet, |
227 // or is currently shown but deactivated for another WebContents). This is | 362 // or is currently shown but deactivated for another WebContents). This is |
228 // because this window is a singleton, and it's possible another active | 363 // because this window is a singleton, and it's possible another active |
229 // renderer may hang while this one is showing, and we don't want to reset | 364 // renderer may hang while this one is showing, and we don't want to reset |
(...skipping 18 matching lines...) Expand all Loading... |
248 | 383 |
249 /////////////////////////////////////////////////////////////////////////////// | 384 /////////////////////////////////////////////////////////////////////////////// |
250 // HungRendererDialogView, views::DialogDelegate implementation: | 385 // HungRendererDialogView, views::DialogDelegate implementation: |
251 | 386 |
252 string16 HungRendererDialogView::GetWindowTitle() const { | 387 string16 HungRendererDialogView::GetWindowTitle() const { |
253 return l10n_util::GetStringUTF16(IDS_BROWSER_HANGMONITOR_RENDERER_TITLE); | 388 return l10n_util::GetStringUTF16(IDS_BROWSER_HANGMONITOR_RENDERER_TITLE); |
254 } | 389 } |
255 | 390 |
256 void HungRendererDialogView::WindowClosing() { | 391 void HungRendererDialogView::WindowClosing() { |
257 // We are going to be deleted soon, so make sure our instance is destroyed. | 392 // We are going to be deleted soon, so make sure our instance is destroyed. |
258 g_instance_ = NULL; | 393 g_instance = NULL; |
259 } | 394 } |
260 | 395 |
261 int HungRendererDialogView::GetDialogButtons() const { | 396 int HungRendererDialogView::GetDialogButtons() const { |
262 // We specifically don't want a CANCEL button here because that code path is | 397 // We specifically don't want a CANCEL button here because that code path is |
263 // also called when the window is closed by the user clicking the X button in | 398 // also called when the window is closed by the user clicking the X button in |
264 // the window's titlebar, and also if we call Window::Close. Rather, we want | 399 // the window's titlebar, and also if we call Window::Close. Rather, we want |
265 // the OK button to wait for responsiveness (and close the dialog) and our | 400 // the OK button to wait for responsiveness (and close the dialog) and our |
266 // additional button (which we create) to kill the process (which will result | 401 // additional button (which we create) to kill the process (which will result |
267 // in the dialog being destroyed). | 402 // in the dialog being destroyed). |
268 return ui::DIALOG_BUTTON_OK; | 403 return ui::DIALOG_BUTTON_OK; |
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
326 /////////////////////////////////////////////////////////////////////////////// | 461 /////////////////////////////////////////////////////////////////////////////// |
327 // HungRendererDialogView, views::View overrides: | 462 // HungRendererDialogView, views::View overrides: |
328 | 463 |
329 void HungRendererDialogView::ViewHierarchyChanged(bool is_add, | 464 void HungRendererDialogView::ViewHierarchyChanged(bool is_add, |
330 views::View* parent, | 465 views::View* parent, |
331 views::View* child) { | 466 views::View* child) { |
332 if (!initialized_ && is_add && child == this && GetWidget()) | 467 if (!initialized_ && is_add && child == this && GetWidget()) |
333 Init(); | 468 Init(); |
334 } | 469 } |
335 | 470 |
336 bool HungRendererDialogView::IsFrameActive(WebContents* contents) { | |
337 gfx::NativeView frame_view = | |
338 platform_util::GetTopLevel(contents->GetNativeView()); | |
339 if (!platform_util::IsWindowActive(frame_view) && | |
340 !platform_util::IsWindowActive(GetWidget()->GetNativeWindow())) { | |
341 return false; | |
342 } | |
343 return true; | |
344 } | |
345 | |
346 /////////////////////////////////////////////////////////////////////////////// | 471 /////////////////////////////////////////////////////////////////////////////// |
347 // HungRendererDialogView, private: | 472 // HungRendererDialogView, private: |
348 | 473 |
349 void HungRendererDialogView::Init() { | 474 void HungRendererDialogView::Init() { |
350 views::ImageView* frozen_icon_view = new views::ImageView; | 475 views::ImageView* frozen_icon_view = new views::ImageView; |
351 frozen_icon_view->SetImage(frozen_icon_); | 476 frozen_icon_view->SetImage(frozen_icon_); |
352 | 477 |
353 views::Label* info_label = new views::Label( | 478 views::Label* info_label = new views::Label( |
354 l10n_util::GetStringUTF16(IDS_BROWSER_HANGMONITOR_RENDERER)); | 479 l10n_util::GetStringUTF16(IDS_BROWSER_HANGMONITOR_RENDERER)); |
355 info_label->SetMultiLine(true); | 480 info_label->SetMultiLine(true); |
(...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
442 // static | 567 // static |
443 void HungRendererDialogView::InitClass() { | 568 void HungRendererDialogView::InitClass() { |
444 static bool initialized = false; | 569 static bool initialized = false; |
445 if (!initialized) { | 570 if (!initialized) { |
446 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); | 571 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); |
447 frozen_icon_ = rb.GetImageSkiaNamed(IDR_FROZEN_TAB_ICON); | 572 frozen_icon_ = rb.GetImageSkiaNamed(IDR_FROZEN_TAB_ICON); |
448 initialized = true; | 573 initialized = true; |
449 } | 574 } |
450 } | 575 } |
451 | 576 |
| 577 static HungRendererDialogView* CreateHungRendererDialogView() { |
| 578 HungRendererDialogView* cv = new HungRendererDialogView; |
| 579 views::Widget::CreateWindow(cv); |
| 580 return cv; |
| 581 } |
| 582 |
452 namespace chrome { | 583 namespace chrome { |
453 | 584 |
454 void ShowHungRendererDialog(WebContents* contents) { | 585 void ShowHungRendererDialog(WebContents* contents) { |
455 if (!logging::DialogsAreSuppressed()) { | 586 if (!logging::DialogsAreSuppressed()) { |
456 HungRendererDialogView* view = HungRendererDialogView::Create(); | 587 if (!g_instance) |
457 view->ShowForWebContents(contents); | 588 g_instance = CreateHungRendererDialogView(); |
| 589 g_instance->ShowForWebContents(contents); |
458 } | 590 } |
459 } | 591 } |
460 | 592 |
461 void HideHungRendererDialog(WebContents* contents) { | 593 void HideHungRendererDialog(WebContents* contents) { |
462 if (!logging::DialogsAreSuppressed() && | 594 if (!logging::DialogsAreSuppressed() && g_instance) |
463 HungRendererDialogView::GetInstance()) | 595 g_instance->EndForWebContents(contents); |
464 HungRendererDialogView::GetInstance()->EndForWebContents(contents); | |
465 } | 596 } |
466 | 597 |
467 } // namespace chrome | 598 } // namespace chrome |
OLD | NEW |