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/browser_dialogs.h" | 5 #include "chrome/browser/ui/views/hung_renderer_view.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" |
17 #include "chrome/browser/ui/tab_contents/core_tab_helper.h" | 18 #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" | |
25 #include "content/public/common/result_codes.h" | 24 #include "content/public/common/result_codes.h" |
26 #include "grit/chromium_strings.h" | 25 #include "grit/chromium_strings.h" |
27 #include "grit/generated_resources.h" | 26 #include "grit/generated_resources.h" |
28 #include "grit/theme_resources.h" | 27 #include "grit/theme_resources.h" |
29 #include "ui/base/l10n/l10n_util.h" | 28 #include "ui/base/l10n/l10n_util.h" |
30 #include "ui/base/resource/resource_bundle.h" | 29 #include "ui/base/resource/resource_bundle.h" |
31 #include "ui/gfx/canvas.h" | 30 #include "ui/gfx/canvas.h" |
32 #include "ui/views/controls/button/text_button.h" | |
33 #include "ui/views/controls/image_view.h" | 31 #include "ui/views/controls/image_view.h" |
34 #include "ui/views/controls/label.h" | 32 #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" | |
37 #include "ui/views/layout/grid_layout.h" | 33 #include "ui/views/layout/grid_layout.h" |
38 #include "ui/views/layout/layout_constants.h" | 34 #include "ui/views/layout/layout_constants.h" |
39 #include "ui/views/widget/widget.h" | 35 #include "ui/views/widget/widget.h" |
40 #include "ui/views/window/client_view.h" | 36 #include "ui/views/window/client_view.h" |
41 #include "ui/views/window/dialog_delegate.h" | |
42 | 37 |
43 #if defined(USE_AURA) | 38 #if defined(USE_AURA) |
44 #include "ui/aura/window.h" | 39 #include "ui/aura/window.h" |
45 #endif | 40 #endif |
46 | 41 |
47 #if defined(OS_WIN) | 42 #if defined(OS_WIN) |
48 #include "chrome/browser/hang_monitor/hang_crash_dump_win.h" | 43 #include "chrome/browser/hang_monitor/hang_crash_dump_win.h" |
49 #endif | 44 #endif |
50 | 45 |
51 class HungRendererDialogView; | 46 HungRendererDialogView* HungRendererDialogView::g_instance_ = NULL; |
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 }; | |
134 | 47 |
135 /////////////////////////////////////////////////////////////////////////////// | 48 /////////////////////////////////////////////////////////////////////////////// |
136 // HungPagesTableModel, public: | 49 // HungPagesTableModel, public: |
137 | 50 |
138 HungPagesTableModel::HungPagesTableModel(Delegate* delegate) | 51 HungPagesTableModel::HungPagesTableModel(Delegate* delegate) |
139 : observer_(NULL), | 52 : observer_(NULL), |
140 delegate_(delegate) { | 53 delegate_(delegate) { |
141 } | 54 } |
142 | 55 |
143 HungPagesTableModel::~HungPagesTableModel() { | 56 HungPagesTableModel::~HungPagesTableModel() { |
(...skipping 95 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
239 } | 152 } |
240 | 153 |
241 void HungPagesTableModel::WebContentsObserverImpl::WebContentsDestroyed( | 154 void HungPagesTableModel::WebContentsObserverImpl::WebContentsDestroyed( |
242 WebContents* tab) { | 155 WebContents* tab) { |
243 model_->TabDestroyed(this); | 156 model_->TabDestroyed(this); |
244 } | 157 } |
245 | 158 |
246 /////////////////////////////////////////////////////////////////////////////// | 159 /////////////////////////////////////////////////////////////////////////////// |
247 // HungRendererDialogView | 160 // HungRendererDialogView |
248 | 161 |
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 | |
314 // static | 162 // static |
315 gfx::ImageSkia* HungRendererDialogView::frozen_icon_ = NULL; | 163 gfx::ImageSkia* HungRendererDialogView::frozen_icon_ = NULL; |
316 | 164 |
317 // The distance in pixels from the top of the relevant contents to place the | 165 // The distance in pixels from the top of the relevant contents to place the |
318 // warning window. | 166 // warning window. |
319 static const int kOverlayContentsOffsetY = 50; | 167 static const int kOverlayContentsOffsetY = 50; |
320 | 168 |
321 // The dimensions of the hung pages list table view, in pixels. | 169 // The dimensions of the hung pages list table view, in pixels. |
322 static const int kTableViewWidth = 300; | 170 static const int kTableViewWidth = 300; |
323 static const int kTableViewHeight = 100; | 171 static const int kTableViewHeight = 100; |
324 | 172 |
325 /////////////////////////////////////////////////////////////////////////////// | 173 /////////////////////////////////////////////////////////////////////////////// |
326 // HungRendererDialogView, public: | 174 // HungRendererDialogView, public: |
327 | 175 |
| 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 |
328 HungRendererDialogView::HungRendererDialogView() | 193 HungRendererDialogView::HungRendererDialogView() |
329 : hung_pages_table_(NULL), | 194 : hung_pages_table_(NULL), |
330 kill_button_(NULL), | 195 kill_button_(NULL), |
331 kill_button_container_(NULL), | 196 kill_button_container_(NULL), |
332 initialized_(false) { | 197 initialized_(false) { |
333 InitClass(); | 198 InitClass(); |
334 } | 199 } |
335 | 200 |
336 HungRendererDialogView::~HungRendererDialogView() { | 201 HungRendererDialogView::~HungRendererDialogView() { |
337 hung_pages_table_->SetModel(NULL); | 202 hung_pages_table_->SetModel(NULL); |
338 } | 203 } |
339 | 204 |
340 void HungRendererDialogView::ShowForWebContents(WebContents* contents) { | 205 void HungRendererDialogView::ShowForWebContents(WebContents* contents) { |
341 DCHECK(contents && GetWidget()); | 206 DCHECK(contents && GetWidget()); |
342 | 207 |
343 // Don't show the warning unless the foreground window is the frame, or this | 208 // Don't show the warning unless the foreground window is the frame, or this |
344 // window (but still invisible). If the user has another window or | 209 // window (but still invisible). If the user has another window or |
345 // application selected, activating ourselves is rude. | 210 // application selected, activating ourselves is rude. |
346 gfx::NativeView frame_view = | 211 if (!IsFrameActive(contents)) |
347 platform_util::GetTopLevel(contents->GetNativeView()); | |
348 if (!platform_util::IsWindowActive(frame_view) && | |
349 !platform_util::IsWindowActive(GetWidget()->GetNativeWindow())) { | |
350 return; | 212 return; |
351 } | |
352 | 213 |
353 if (!GetWidget()->IsActive()) { | 214 if (!GetWidget()->IsActive()) { |
354 gfx::Rect bounds = GetDisplayBounds(contents); | 215 gfx::Rect bounds = GetDisplayBounds(contents); |
| 216 |
| 217 gfx::NativeView frame_view = |
| 218 platform_util::GetTopLevel(contents->GetNativeView()); |
| 219 |
355 views::Widget* insert_after = | 220 views::Widget* insert_after = |
356 views::Widget::GetWidgetForNativeView(frame_view); | 221 views::Widget::GetWidgetForNativeView(frame_view); |
357 GetWidget()->SetBoundsConstrained(bounds); | 222 GetWidget()->SetBoundsConstrained(bounds); |
358 if (insert_after) | 223 if (insert_after) |
359 GetWidget()->StackAboveWidget(insert_after); | 224 GetWidget()->StackAboveWidget(insert_after); |
360 | 225 |
361 // We only do this if the window isn't active (i.e. hasn't been shown yet, | 226 // We only do this if the window isn't active (i.e. hasn't been shown yet, |
362 // or is currently shown but deactivated for another WebContents). This is | 227 // or is currently shown but deactivated for another WebContents). This is |
363 // because this window is a singleton, and it's possible another active | 228 // because this window is a singleton, and it's possible another active |
364 // renderer may hang while this one is showing, and we don't want to reset | 229 // renderer may hang while this one is showing, and we don't want to reset |
(...skipping 18 matching lines...) Expand all Loading... |
383 | 248 |
384 /////////////////////////////////////////////////////////////////////////////// | 249 /////////////////////////////////////////////////////////////////////////////// |
385 // HungRendererDialogView, views::DialogDelegate implementation: | 250 // HungRendererDialogView, views::DialogDelegate implementation: |
386 | 251 |
387 string16 HungRendererDialogView::GetWindowTitle() const { | 252 string16 HungRendererDialogView::GetWindowTitle() const { |
388 return l10n_util::GetStringUTF16(IDS_BROWSER_HANGMONITOR_RENDERER_TITLE); | 253 return l10n_util::GetStringUTF16(IDS_BROWSER_HANGMONITOR_RENDERER_TITLE); |
389 } | 254 } |
390 | 255 |
391 void HungRendererDialogView::WindowClosing() { | 256 void HungRendererDialogView::WindowClosing() { |
392 // We are going to be deleted soon, so make sure our instance is destroyed. | 257 // We are going to be deleted soon, so make sure our instance is destroyed. |
393 g_instance = NULL; | 258 g_instance_ = NULL; |
394 } | 259 } |
395 | 260 |
396 int HungRendererDialogView::GetDialogButtons() const { | 261 int HungRendererDialogView::GetDialogButtons() const { |
397 // We specifically don't want a CANCEL button here because that code path is | 262 // We specifically don't want a CANCEL button here because that code path is |
398 // also called when the window is closed by the user clicking the X button in | 263 // also called when the window is closed by the user clicking the X button in |
399 // the window's titlebar, and also if we call Window::Close. Rather, we want | 264 // the window's titlebar, and also if we call Window::Close. Rather, we want |
400 // the OK button to wait for responsiveness (and close the dialog) and our | 265 // the OK button to wait for responsiveness (and close the dialog) and our |
401 // additional button (which we create) to kill the process (which will result | 266 // additional button (which we create) to kill the process (which will result |
402 // in the dialog being destroyed). | 267 // in the dialog being destroyed). |
403 return ui::DIALOG_BUTTON_OK; | 268 return ui::DIALOG_BUTTON_OK; |
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
461 /////////////////////////////////////////////////////////////////////////////// | 326 /////////////////////////////////////////////////////////////////////////////// |
462 // HungRendererDialogView, views::View overrides: | 327 // HungRendererDialogView, views::View overrides: |
463 | 328 |
464 void HungRendererDialogView::ViewHierarchyChanged(bool is_add, | 329 void HungRendererDialogView::ViewHierarchyChanged(bool is_add, |
465 views::View* parent, | 330 views::View* parent, |
466 views::View* child) { | 331 views::View* child) { |
467 if (!initialized_ && is_add && child == this && GetWidget()) | 332 if (!initialized_ && is_add && child == this && GetWidget()) |
468 Init(); | 333 Init(); |
469 } | 334 } |
470 | 335 |
| 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 |
471 /////////////////////////////////////////////////////////////////////////////// | 346 /////////////////////////////////////////////////////////////////////////////// |
472 // HungRendererDialogView, private: | 347 // HungRendererDialogView, private: |
473 | 348 |
474 void HungRendererDialogView::Init() { | 349 void HungRendererDialogView::Init() { |
475 views::ImageView* frozen_icon_view = new views::ImageView; | 350 views::ImageView* frozen_icon_view = new views::ImageView; |
476 frozen_icon_view->SetImage(frozen_icon_); | 351 frozen_icon_view->SetImage(frozen_icon_); |
477 | 352 |
478 views::Label* info_label = new views::Label( | 353 views::Label* info_label = new views::Label( |
479 l10n_util::GetStringUTF16(IDS_BROWSER_HANGMONITOR_RENDERER)); | 354 l10n_util::GetStringUTF16(IDS_BROWSER_HANGMONITOR_RENDERER)); |
480 info_label->SetMultiLine(true); | 355 info_label->SetMultiLine(true); |
(...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
567 // static | 442 // static |
568 void HungRendererDialogView::InitClass() { | 443 void HungRendererDialogView::InitClass() { |
569 static bool initialized = false; | 444 static bool initialized = false; |
570 if (!initialized) { | 445 if (!initialized) { |
571 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); | 446 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); |
572 frozen_icon_ = rb.GetImageSkiaNamed(IDR_FROZEN_TAB_ICON); | 447 frozen_icon_ = rb.GetImageSkiaNamed(IDR_FROZEN_TAB_ICON); |
573 initialized = true; | 448 initialized = true; |
574 } | 449 } |
575 } | 450 } |
576 | 451 |
577 static HungRendererDialogView* CreateHungRendererDialogView() { | |
578 HungRendererDialogView* cv = new HungRendererDialogView; | |
579 views::Widget::CreateWindow(cv); | |
580 return cv; | |
581 } | |
582 | |
583 namespace chrome { | 452 namespace chrome { |
584 | 453 |
585 void ShowHungRendererDialog(WebContents* contents) { | 454 void ShowHungRendererDialog(WebContents* contents) { |
586 if (!logging::DialogsAreSuppressed()) { | 455 if (!logging::DialogsAreSuppressed()) { |
587 if (!g_instance) | 456 HungRendererDialogView* view = HungRendererDialogView::Create(); |
588 g_instance = CreateHungRendererDialogView(); | 457 view->ShowForWebContents(contents); |
589 g_instance->ShowForWebContents(contents); | |
590 } | 458 } |
591 } | 459 } |
592 | 460 |
593 void HideHungRendererDialog(WebContents* contents) { | 461 void HideHungRendererDialog(WebContents* contents) { |
594 if (!logging::DialogsAreSuppressed() && g_instance) | 462 if (!logging::DialogsAreSuppressed() && |
595 g_instance->EndForWebContents(contents); | 463 HungRendererDialogView::GetInstance()) |
| 464 HungRendererDialogView::GetInstance()->EndForWebContents(contents); |
596 } | 465 } |
597 | 466 |
598 } // namespace chrome | 467 } // namespace chrome |
OLD | NEW |