OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 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 #ifndef CHROME_BROWSER_UI_VIEWS_HUNG_RENDERER_VIEW_H_ |
| 6 #define CHROME_BROWSER_UI_VIEWS_HUNG_RENDERER_VIEW_H_ |
6 | 7 |
7 #if defined(OS_WIN) && !defined(USE_AURA) | |
8 #include <windows.h> | |
9 #endif | |
10 | |
11 #include "base/i18n/rtl.h" | |
12 #include "base/memory/scoped_vector.h" | 8 #include "base/memory/scoped_vector.h" |
13 #include "base/process_util.h" | |
14 #include "base/utf_string_conversions.h" | |
15 #include "chrome/browser/favicon/favicon_tab_helper.h" | |
16 #include "chrome/browser/platform_util.h" | |
17 #include "chrome/browser/ui/tab_contents/core_tab_helper.h" | |
18 #include "chrome/browser/ui/tab_contents/tab_contents.h" | 9 #include "chrome/browser/ui/tab_contents/tab_contents.h" |
19 #include "chrome/browser/ui/tab_contents/tab_contents_iterator.h" | |
20 #include "chrome/common/chrome_constants.h" | |
21 #include "chrome/common/logging_chrome.h" | |
22 #include "content/public/browser/render_process_host.h" | |
23 #include "content/public/browser/render_view_host.h" | |
24 #include "content/public/browser/web_contents.h" | 10 #include "content/public/browser/web_contents.h" |
25 #include "content/public/common/result_codes.h" | 11 #include "content/public/browser/web_contents_observer.h" |
26 #include "grit/chromium_strings.h" | |
27 #include "grit/generated_resources.h" | |
28 #include "grit/theme_resources.h" | |
29 #include "ui/base/l10n/l10n_util.h" | |
30 #include "ui/base/resource/resource_bundle.h" | |
31 #include "ui/gfx/canvas.h" | |
32 #include "ui/views/controls/button/text_button.h" | 12 #include "ui/views/controls/button/text_button.h" |
33 #include "ui/views/controls/image_view.h" | |
34 #include "ui/views/controls/label.h" | |
35 #include "ui/views/controls/table/group_table_model.h" | 13 #include "ui/views/controls/table/group_table_model.h" |
36 #include "ui/views/controls/table/group_table_view.h" | 14 #include "ui/views/controls/table/group_table_view.h" |
37 #include "ui/views/layout/grid_layout.h" | |
38 #include "ui/views/layout/layout_constants.h" | |
39 #include "ui/views/widget/widget.h" | |
40 #include "ui/views/window/client_view.h" | |
41 #include "ui/views/window/dialog_delegate.h" | 15 #include "ui/views/window/dialog_delegate.h" |
42 | 16 |
43 #if defined(USE_AURA) | |
44 #include "ui/aura/window.h" | |
45 #endif | |
46 | |
47 #if defined(OS_WIN) | |
48 #include "chrome/browser/hang_monitor/hang_crash_dump_win.h" | |
49 #endif | |
50 | |
51 class HungRendererDialogView; | |
52 | |
53 using content::RenderViewHost; | 17 using content::RenderViewHost; |
54 using content::WebContents; | 18 using content::WebContents; |
55 | 19 |
56 namespace { | 20 // Provides functionality to display information about a hung renderer. |
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 { | 21 class HungPagesTableModel : public views::GroupTableModel { |
65 public: | 22 public: |
66 // The Delegate is notified any time a WebContents the model is listening to | 23 // The Delegate is notified any time a WebContents the model is listening to |
67 // is destroyed. | 24 // is destroyed. |
68 class Delegate { | 25 class Delegate { |
69 public: | 26 public: |
70 virtual void TabDestroyed() = 0; | 27 virtual void TabDestroyed() = 0; |
71 | 28 |
72 protected: | 29 protected: |
73 virtual ~Delegate() {} | 30 virtual ~Delegate() {} |
74 }; | 31 }; |
75 | 32 |
76 explicit HungPagesTableModel(Delegate* delegate); | 33 explicit HungPagesTableModel(Delegate* delegate); |
77 virtual ~HungPagesTableModel(); | 34 virtual ~HungPagesTableModel(); |
78 | 35 |
79 void InitForWebContents(WebContents* hung_contents); | 36 void InitForWebContents(WebContents* hung_contents); |
80 | 37 |
81 // Returns the first RenderProcessHost, or NULL if there aren't any | 38 // Returns the first RenderProcessHost, or NULL if there aren't any |
82 // WebContents. | 39 // WebContents. |
83 content::RenderProcessHost* GetRenderProcessHost(); | 40 content::RenderProcessHost* GetRenderProcessHost(); |
84 | 41 |
85 // Returns the first RenderViewHost, or NULL if there aren't any WebContents. | 42 // Returns the first RenderViewHost, or NULL if there aren't any WebContents. |
86 RenderViewHost* GetRenderViewHost(); | 43 RenderViewHost* GetRenderViewHost(); |
87 | 44 |
88 // Overridden from views::GroupTableModel: | 45 // Overridden from views::GroupTableModel: |
89 virtual int RowCount(); | 46 virtual int RowCount() OVERRIDE; |
90 virtual string16 GetText(int row, int column_id); | 47 virtual string16 GetText(int row, int column_id) OVERRIDE; |
91 virtual gfx::ImageSkia GetIcon(int row); | 48 virtual gfx::ImageSkia GetIcon(int row) OVERRIDE; |
92 virtual void SetObserver(ui::TableModelObserver* observer); | 49 virtual void SetObserver(ui::TableModelObserver* observer) OVERRIDE; |
93 virtual void GetGroupRangeForItem(int item, views::GroupRange* range); | 50 virtual void GetGroupRangeForItem(int item, views::GroupRange* range) |
| 51 OVERRIDE; |
94 | 52 |
95 private: | 53 private: |
96 // Used to track a single WebContents. If the WebContents is destroyed | 54 // Used to track a single WebContents. If the WebContents is destroyed |
97 // TabDestroyed() is invoked on the model. | 55 // TabDestroyed() is invoked on the model. |
98 class WebContentsObserverImpl : public content::WebContentsObserver { | 56 class WebContentsObserverImpl : public content::WebContentsObserver { |
99 public: | 57 public: |
100 WebContentsObserverImpl(HungPagesTableModel* model, | 58 WebContentsObserverImpl(HungPagesTableModel* model, |
101 TabContents* tab); | 59 TabContents* tab); |
102 | 60 |
103 WebContents* web_contents() const { | 61 WebContents* web_contents() const { |
(...skipping 21 matching lines...) Expand all Loading... |
125 | 83 |
126 typedef ScopedVector<WebContentsObserverImpl> TabObservers; | 84 typedef ScopedVector<WebContentsObserverImpl> TabObservers; |
127 TabObservers tab_observers_; | 85 TabObservers tab_observers_; |
128 | 86 |
129 ui::TableModelObserver* observer_; | 87 ui::TableModelObserver* observer_; |
130 Delegate* delegate_; | 88 Delegate* delegate_; |
131 | 89 |
132 DISALLOW_COPY_AND_ASSIGN(HungPagesTableModel); | 90 DISALLOW_COPY_AND_ASSIGN(HungPagesTableModel); |
133 }; | 91 }; |
134 | 92 |
135 /////////////////////////////////////////////////////////////////////////////// | 93 // This class displays a dialog which contains information about a hung |
136 // HungPagesTableModel, public: | 94 // renderer process. |
137 | |
138 HungPagesTableModel::HungPagesTableModel(Delegate* delegate) | |
139 : observer_(NULL), | |
140 delegate_(delegate) { | |
141 } | |
142 | |
143 HungPagesTableModel::~HungPagesTableModel() { | |
144 } | |
145 | |
146 content::RenderProcessHost* HungPagesTableModel::GetRenderProcessHost() { | |
147 return tab_observers_.empty() ? NULL : | |
148 tab_observers_[0]->web_contents()->GetRenderProcessHost(); | |
149 } | |
150 | |
151 RenderViewHost* HungPagesTableModel::GetRenderViewHost() { | |
152 return tab_observers_.empty() ? NULL : | |
153 tab_observers_[0]->web_contents()->GetRenderViewHost(); | |
154 } | |
155 | |
156 void HungPagesTableModel::InitForWebContents(WebContents* hung_contents) { | |
157 tab_observers_.clear(); | |
158 if (hung_contents) { | |
159 // Force hung_contents to be first. | |
160 TabContents* hung_tab_contents = | |
161 TabContents::FromWebContents(hung_contents); | |
162 if (hung_tab_contents) { | |
163 tab_observers_.push_back(new WebContentsObserverImpl(this, | |
164 hung_tab_contents)); | |
165 } | |
166 for (TabContentsIterator it; !it.done(); ++it) { | |
167 if (*it != hung_tab_contents && | |
168 it->web_contents()->GetRenderProcessHost() == | |
169 hung_contents->GetRenderProcessHost()) | |
170 tab_observers_.push_back(new WebContentsObserverImpl(this, *it)); | |
171 } | |
172 } | |
173 // The world is different. | |
174 if (observer_) | |
175 observer_->OnModelChanged(); | |
176 } | |
177 | |
178 /////////////////////////////////////////////////////////////////////////////// | |
179 // HungPagesTableModel, views::GroupTableModel implementation: | |
180 | |
181 int HungPagesTableModel::RowCount() { | |
182 return static_cast<int>(tab_observers_.size()); | |
183 } | |
184 | |
185 string16 HungPagesTableModel::GetText(int row, int column_id) { | |
186 DCHECK(row >= 0 && row < RowCount()); | |
187 string16 title = tab_observers_[row]->web_contents()->GetTitle(); | |
188 if (title.empty()) | |
189 title = CoreTabHelper::GetDefaultTitle(); | |
190 // TODO(xji): Consider adding a special case if the title text is a URL, | |
191 // since those should always have LTR directionality. Please refer to | |
192 // http://crbug.com/6726 for more information. | |
193 base::i18n::AdjustStringForLocaleDirection(&title); | |
194 return title; | |
195 } | |
196 | |
197 gfx::ImageSkia HungPagesTableModel::GetIcon(int row) { | |
198 DCHECK(row >= 0 && row < RowCount()); | |
199 return tab_observers_[row]->favicon_tab_helper()->GetFavicon(); | |
200 } | |
201 | |
202 void HungPagesTableModel::SetObserver(ui::TableModelObserver* observer) { | |
203 observer_ = observer; | |
204 } | |
205 | |
206 void HungPagesTableModel::GetGroupRangeForItem(int item, | |
207 views::GroupRange* range) { | |
208 DCHECK(range); | |
209 range->start = 0; | |
210 range->length = RowCount(); | |
211 } | |
212 | |
213 void HungPagesTableModel::TabDestroyed(WebContentsObserverImpl* tab) { | |
214 // Clean up tab_observers_ and notify our observer. | |
215 TabObservers::iterator i = std::find( | |
216 tab_observers_.begin(), tab_observers_.end(), tab); | |
217 DCHECK(i != tab_observers_.end()); | |
218 int index = static_cast<int>(i - tab_observers_.begin()); | |
219 tab_observers_.erase(i); | |
220 if (observer_) | |
221 observer_->OnItemsRemoved(index, 1); | |
222 | |
223 // Notify the delegate. | |
224 delegate_->TabDestroyed(); | |
225 // WARNING: we've likely been deleted. | |
226 } | |
227 | |
228 HungPagesTableModel::WebContentsObserverImpl::WebContentsObserverImpl( | |
229 HungPagesTableModel* model, | |
230 TabContents* tab) | |
231 : content::WebContentsObserver(tab->web_contents()), | |
232 model_(model), | |
233 tab_(tab) { | |
234 } | |
235 | |
236 void HungPagesTableModel::WebContentsObserverImpl::RenderViewGone( | |
237 base::TerminationStatus status) { | |
238 model_->TabDestroyed(this); | |
239 } | |
240 | |
241 void HungPagesTableModel::WebContentsObserverImpl::WebContentsDestroyed( | |
242 WebContents* tab) { | |
243 model_->TabDestroyed(this); | |
244 } | |
245 | |
246 /////////////////////////////////////////////////////////////////////////////// | |
247 // HungRendererDialogView | |
248 | |
249 class HungRendererDialogView : public views::DialogDelegateView, | 95 class HungRendererDialogView : public views::DialogDelegateView, |
250 public views::ButtonListener, | 96 public views::ButtonListener, |
251 public HungPagesTableModel::Delegate { | 97 public HungPagesTableModel::Delegate { |
252 public: | 98 public: |
253 HungRendererDialogView(); | 99 // Factory function for creating an instance of the HungRendererDialogView |
254 ~HungRendererDialogView(); | 100 // class. At any given point only one instance can be active. |
| 101 static HungRendererDialogView* Create(); |
| 102 // Returns a pointer to the singleton instance if any. |
| 103 static HungRendererDialogView* GetInstance(); |
255 | 104 |
256 void ShowForWebContents(WebContents* contents); | 105 virtual void ShowForWebContents(WebContents* contents); |
257 void EndForWebContents(WebContents* contents); | 106 virtual void EndForWebContents(WebContents* contents); |
258 | 107 |
259 // views::DialogDelegateView overrides: | 108 // views::DialogDelegateView overrides: |
260 virtual string16 GetWindowTitle() const OVERRIDE; | 109 virtual string16 GetWindowTitle() const OVERRIDE; |
261 virtual void WindowClosing() OVERRIDE; | 110 virtual void WindowClosing() OVERRIDE; |
262 virtual int GetDialogButtons() const OVERRIDE; | 111 virtual int GetDialogButtons() const OVERRIDE; |
263 virtual string16 GetDialogButtonLabel(ui::DialogButton button) const OVERRIDE; | 112 virtual string16 GetDialogButtonLabel(ui::DialogButton button) const OVERRIDE; |
264 virtual views::View* GetExtraView() OVERRIDE; | 113 virtual views::View* GetExtraView() OVERRIDE; |
265 virtual bool Accept(bool window_closing) OVERRIDE; | 114 virtual bool Accept(bool window_closing) OVERRIDE; |
266 virtual views::View* GetContentsView() OVERRIDE; | 115 virtual views::View* GetContentsView() OVERRIDE; |
267 | 116 |
268 // views::ButtonListener overrides: | 117 // views::ButtonListener overrides: |
269 virtual void ButtonPressed(views::Button* sender, | 118 virtual void ButtonPressed(views::Button* sender, |
270 const views::Event& event) OVERRIDE; | 119 const views::Event& event) OVERRIDE; |
271 | 120 |
272 // HungPagesTableModel::Delegate overrides: | 121 // HungPagesTableModel::Delegate overrides: |
273 virtual void TabDestroyed() OVERRIDE; | 122 virtual void TabDestroyed() OVERRIDE; |
274 | 123 |
275 protected: | 124 protected: |
| 125 HungRendererDialogView(); |
| 126 virtual ~HungRendererDialogView(); |
| 127 |
276 // views::View overrides: | 128 // views::View overrides: |
277 virtual void ViewHierarchyChanged(bool is_add, | 129 virtual void ViewHierarchyChanged(bool is_add, |
278 views::View* parent, | 130 views::View* parent, |
279 views::View* child) OVERRIDE; | 131 views::View* child) OVERRIDE; |
280 | 132 |
| 133 // Returns true if the frame is in the foreground. |
| 134 bool IsFrameActive(WebContents* contents); |
| 135 |
| 136 static HungRendererDialogView* g_instance_; |
| 137 |
281 private: | 138 private: |
282 // Initialize the controls in this dialog. | 139 // Initialize the controls in this dialog. |
283 void Init(); | 140 void Init(); |
284 void CreateKillButtonView(); | 141 void CreateKillButtonView(); |
285 | 142 |
286 // Returns the bounds the dialog should be displayed at to be meaningfully | 143 // Returns the bounds the dialog should be displayed at to be meaningfully |
287 // associated with the specified WebContents. | 144 // associated with the specified WebContents. |
288 gfx::Rect GetDisplayBounds(WebContents* contents); | 145 gfx::Rect GetDisplayBounds(WebContents* contents); |
289 | 146 |
290 static void InitClass(); | 147 static void InitClass(); |
(...skipping 13 matching lines...) Expand all Loading... |
304 | 161 |
305 // Whether or not we've created controls for ourself. | 162 // Whether or not we've created controls for ourself. |
306 bool initialized_; | 163 bool initialized_; |
307 | 164 |
308 // An amusing icon image. | 165 // An amusing icon image. |
309 static gfx::ImageSkia* frozen_icon_; | 166 static gfx::ImageSkia* frozen_icon_; |
310 | 167 |
311 DISALLOW_COPY_AND_ASSIGN(HungRendererDialogView); | 168 DISALLOW_COPY_AND_ASSIGN(HungRendererDialogView); |
312 }; | 169 }; |
313 | 170 |
314 // static | 171 #endif // CHROME_BROWSER_UI_VIEWS_HUNG_RENDERER_VIEW_H_ |
315 gfx::ImageSkia* HungRendererDialogView::frozen_icon_ = NULL; | |
316 | |
317 // The distance in pixels from the top of the relevant contents to place the | |
318 // warning window. | |
319 static const int kOverlayContentsOffsetY = 50; | |
320 | |
321 // The dimensions of the hung pages list table view, in pixels. | |
322 static const int kTableViewWidth = 300; | |
323 static const int kTableViewHeight = 100; | |
324 | |
325 /////////////////////////////////////////////////////////////////////////////// | |
326 // HungRendererDialogView, public: | |
327 | |
328 HungRendererDialogView::HungRendererDialogView() | |
329 : hung_pages_table_(NULL), | |
330 kill_button_(NULL), | |
331 kill_button_container_(NULL), | |
332 initialized_(false) { | |
333 InitClass(); | |
334 } | |
335 | |
336 HungRendererDialogView::~HungRendererDialogView() { | |
337 hung_pages_table_->SetModel(NULL); | |
338 } | |
339 | |
340 void HungRendererDialogView::ShowForWebContents(WebContents* contents) { | |
341 DCHECK(contents && GetWidget()); | |
342 | |
343 // 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 | |
345 // application selected, activating ourselves is rude. | |
346 gfx::NativeView frame_view = | |
347 platform_util::GetTopLevel(contents->GetNativeView()); | |
348 if (!platform_util::IsWindowActive(frame_view) && | |
349 !platform_util::IsWindowActive(GetWidget()->GetNativeWindow())) { | |
350 return; | |
351 } | |
352 | |
353 if (!GetWidget()->IsActive()) { | |
354 gfx::Rect bounds = GetDisplayBounds(contents); | |
355 views::Widget* insert_after = | |
356 views::Widget::GetWidgetForNativeView(frame_view); | |
357 GetWidget()->SetBoundsConstrained(bounds); | |
358 if (insert_after) | |
359 GetWidget()->StackAboveWidget(insert_after); | |
360 | |
361 // 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 | |
363 // 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 | |
365 // the list of hung pages for a potentially unrelated renderer while this | |
366 // one is showing. | |
367 hung_pages_table_model_->InitForWebContents(contents); | |
368 GetWidget()->Show(); | |
369 } | |
370 } | |
371 | |
372 void HungRendererDialogView::EndForWebContents(WebContents* contents) { | |
373 DCHECK(contents); | |
374 if (hung_pages_table_model_->RowCount() == 0 || | |
375 hung_pages_table_model_->GetRenderProcessHost() == | |
376 contents->GetRenderProcessHost()) { | |
377 GetWidget()->Close(); | |
378 // Close is async, make sure we drop our references to the tab immediately | |
379 // (it may be going away). | |
380 hung_pages_table_model_->InitForWebContents(NULL); | |
381 } | |
382 } | |
383 | |
384 /////////////////////////////////////////////////////////////////////////////// | |
385 // HungRendererDialogView, views::DialogDelegate implementation: | |
386 | |
387 string16 HungRendererDialogView::GetWindowTitle() const { | |
388 return l10n_util::GetStringUTF16(IDS_BROWSER_HANGMONITOR_RENDERER_TITLE); | |
389 } | |
390 | |
391 void HungRendererDialogView::WindowClosing() { | |
392 // We are going to be deleted soon, so make sure our instance is destroyed. | |
393 g_instance = NULL; | |
394 } | |
395 | |
396 int HungRendererDialogView::GetDialogButtons() const { | |
397 // 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 | |
399 // 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 | |
401 // additional button (which we create) to kill the process (which will result | |
402 // in the dialog being destroyed). | |
403 return ui::DIALOG_BUTTON_OK; | |
404 } | |
405 | |
406 string16 HungRendererDialogView::GetDialogButtonLabel( | |
407 ui::DialogButton button) const { | |
408 if (button == ui::DIALOG_BUTTON_OK) | |
409 return l10n_util::GetStringUTF16(IDS_BROWSER_HANGMONITOR_RENDERER_WAIT); | |
410 return string16(); | |
411 } | |
412 | |
413 views::View* HungRendererDialogView::GetExtraView() { | |
414 return kill_button_container_; | |
415 } | |
416 | |
417 bool HungRendererDialogView::Accept(bool window_closing) { | |
418 // Don't do anything if we're being called only because the dialog is being | |
419 // destroyed and we don't supply a Cancel function... | |
420 if (window_closing) | |
421 return true; | |
422 | |
423 // Start waiting again for responsiveness. | |
424 if (hung_pages_table_model_->GetRenderViewHost()) | |
425 hung_pages_table_model_->GetRenderViewHost()->RestartHangMonitorTimeout(); | |
426 return true; | |
427 } | |
428 | |
429 views::View* HungRendererDialogView::GetContentsView() { | |
430 return this; | |
431 } | |
432 | |
433 /////////////////////////////////////////////////////////////////////////////// | |
434 // HungRendererDialogView, views::ButtonListener implementation: | |
435 | |
436 void HungRendererDialogView::ButtonPressed( | |
437 views::Button* sender, const views::Event& event) { | |
438 if (sender == kill_button_ && | |
439 hung_pages_table_model_->GetRenderProcessHost()) { | |
440 | |
441 base::ProcessHandle process_handle = | |
442 hung_pages_table_model_->GetRenderProcessHost()->GetHandle(); | |
443 | |
444 #if defined(OS_WIN) | |
445 // Try to generate a crash report for the hung process. | |
446 CrashDumpAndTerminateHungChildProcess(process_handle); | |
447 #else | |
448 // Kill the process. | |
449 base::KillProcess(process_handle, content::RESULT_CODE_HUNG, false); | |
450 #endif | |
451 } | |
452 } | |
453 | |
454 /////////////////////////////////////////////////////////////////////////////// | |
455 // HungRendererDialogView, HungPagesTableModel::Delegate overrides: | |
456 | |
457 void HungRendererDialogView::TabDestroyed() { | |
458 GetWidget()->Close(); | |
459 } | |
460 | |
461 /////////////////////////////////////////////////////////////////////////////// | |
462 // HungRendererDialogView, views::View overrides: | |
463 | |
464 void HungRendererDialogView::ViewHierarchyChanged(bool is_add, | |
465 views::View* parent, | |
466 views::View* child) { | |
467 if (!initialized_ && is_add && child == this && GetWidget()) | |
468 Init(); | |
469 } | |
470 | |
471 /////////////////////////////////////////////////////////////////////////////// | |
472 // HungRendererDialogView, private: | |
473 | |
474 void HungRendererDialogView::Init() { | |
475 views::ImageView* frozen_icon_view = new views::ImageView; | |
476 frozen_icon_view->SetImage(frozen_icon_); | |
477 | |
478 views::Label* info_label = new views::Label( | |
479 l10n_util::GetStringUTF16(IDS_BROWSER_HANGMONITOR_RENDERER)); | |
480 info_label->SetMultiLine(true); | |
481 info_label->SetHorizontalAlignment(views::Label::ALIGN_LEFT); | |
482 | |
483 hung_pages_table_model_.reset(new HungPagesTableModel(this)); | |
484 std::vector<ui::TableColumn> columns; | |
485 columns.push_back(ui::TableColumn()); | |
486 hung_pages_table_ = new views::GroupTableView( | |
487 hung_pages_table_model_.get(), columns, views::ICON_AND_TEXT, true, | |
488 false, true, false); | |
489 | |
490 CreateKillButtonView(); | |
491 | |
492 using views::GridLayout; | |
493 using views::ColumnSet; | |
494 | |
495 GridLayout* layout = GridLayout::CreatePanel(this); | |
496 SetLayoutManager(layout); | |
497 | |
498 const int double_column_set_id = 0; | |
499 ColumnSet* column_set = layout->AddColumnSet(double_column_set_id); | |
500 column_set->AddColumn(GridLayout::LEADING, GridLayout::LEADING, 0, | |
501 GridLayout::FIXED, frozen_icon_->width(), 0); | |
502 column_set->AddPaddingColumn( | |
503 0, views::kUnrelatedControlLargeHorizontalSpacing); | |
504 column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 1, | |
505 GridLayout::USE_PREF, 0, 0); | |
506 | |
507 layout->StartRow(0, double_column_set_id); | |
508 layout->AddView(frozen_icon_view, 1, 3); | |
509 // Add the label with a preferred width of 1, this way it doesn't effect the | |
510 // overall preferred size of the dialog. | |
511 layout->AddView( | |
512 info_label, 1, 1, GridLayout::FILL, GridLayout::LEADING, 1, 0); | |
513 | |
514 layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing); | |
515 | |
516 layout->StartRow(0, double_column_set_id); | |
517 layout->SkipColumns(1); | |
518 layout->AddView(hung_pages_table_->CreateParentIfNecessary(), 1, 1, | |
519 views::GridLayout::FILL, | |
520 views::GridLayout::FILL, kTableViewWidth, kTableViewHeight); | |
521 | |
522 initialized_ = true; | |
523 } | |
524 | |
525 void HungRendererDialogView::CreateKillButtonView() { | |
526 kill_button_ = new views::NativeTextButton(this, | |
527 l10n_util::GetStringUTF16(IDS_BROWSER_HANGMONITOR_RENDERER_END)); | |
528 | |
529 kill_button_container_ = new View; | |
530 | |
531 using views::GridLayout; | |
532 using views::ColumnSet; | |
533 | |
534 GridLayout* layout = new GridLayout(kill_button_container_); | |
535 kill_button_container_->SetLayoutManager(layout); | |
536 | |
537 const int single_column_set_id = 0; | |
538 ColumnSet* column_set = layout->AddColumnSet(single_column_set_id); | |
539 column_set->AddPaddingColumn(0, frozen_icon_->width() + | |
540 views::kPanelHorizMargin + views::kUnrelatedControlHorizontalSpacing); | |
541 column_set->AddColumn(GridLayout::LEADING, GridLayout::LEADING, 0, | |
542 GridLayout::USE_PREF, 0, 0); | |
543 | |
544 layout->StartRow(0, single_column_set_id); | |
545 layout->AddView(kill_button_); | |
546 } | |
547 | |
548 gfx::Rect HungRendererDialogView::GetDisplayBounds( | |
549 WebContents* contents) { | |
550 #if defined(USE_AURA) | |
551 gfx::Rect contents_bounds(contents->GetNativeView()->GetBoundsInRootWindow()); | |
552 #elif defined(OS_WIN) | |
553 HWND contents_hwnd = contents->GetNativeView(); | |
554 RECT contents_bounds_rect; | |
555 GetWindowRect(contents_hwnd, &contents_bounds_rect); | |
556 gfx::Rect contents_bounds(contents_bounds_rect); | |
557 #endif | |
558 gfx::Rect window_bounds = GetWidget()->GetWindowBoundsInScreen(); | |
559 | |
560 int window_x = contents_bounds.x() + | |
561 (contents_bounds.width() - window_bounds.width()) / 2; | |
562 int window_y = contents_bounds.y() + kOverlayContentsOffsetY; | |
563 return gfx::Rect(window_x, window_y, window_bounds.width(), | |
564 window_bounds.height()); | |
565 } | |
566 | |
567 // static | |
568 void HungRendererDialogView::InitClass() { | |
569 static bool initialized = false; | |
570 if (!initialized) { | |
571 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); | |
572 frozen_icon_ = rb.GetImageSkiaNamed(IDR_FROZEN_TAB_ICON); | |
573 initialized = true; | |
574 } | |
575 } | |
576 | |
577 static HungRendererDialogView* CreateHungRendererDialogView() { | |
578 HungRendererDialogView* cv = new HungRendererDialogView; | |
579 views::Widget::CreateWindow(cv); | |
580 return cv; | |
581 } | |
582 | |
583 namespace chrome { | |
584 | |
585 void ShowHungRendererDialog(WebContents* contents) { | |
586 if (!logging::DialogsAreSuppressed()) { | |
587 if (!g_instance) | |
588 g_instance = CreateHungRendererDialogView(); | |
589 g_instance->ShowForWebContents(contents); | |
590 } | |
591 } | |
592 | |
593 void HideHungRendererDialog(WebContents* contents) { | |
594 if (!logging::DialogsAreSuppressed() && g_instance) | |
595 g_instance->EndForWebContents(contents); | |
596 } | |
597 | |
598 } // namespace chrome | |
OLD | NEW |