1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250
|
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_UI_VIEWS_HUNG_RENDERER_VIEW_H_
#define CHROME_BROWSER_UI_VIEWS_HUNG_RENDERER_VIEW_H_
#include <memory>
#include <vector>
#include "base/functional/callback.h"
#include "base/memory/raw_ptr.h"
#include "base/scoped_observation.h"
#include "components/favicon/content/content_favicon_driver.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/render_process_host_observer.h"
#include "content/public/browser/render_widget_host.h"
#include "content/public/browser/render_widget_host_observer.h"
#include "content/public/browser/web_contents_observer.h"
#include "ui/base/metadata/metadata_header_macros.h"
#include "ui/base/models/table_model.h"
#include "ui/views/controls/button/button.h"
#include "ui/views/controls/table/table_view.h"
#include "ui/views/window/dialog_delegate.h"
namespace content {
class WebContents;
}
namespace views {
class Label;
}
// Provides functionality to display information about a hung renderer.
class HungPagesTableModel : public ui::TableModel,
public content::RenderProcessHostObserver,
public content::RenderWidgetHostObserver {
public:
class Delegate {
public:
// Notification when the model is updated (e.g. new location) yet
// still hung.
virtual void TabUpdated() = 0;
// Notification when the model is destroyed.
virtual void TabDestroyed() = 0;
protected:
virtual ~Delegate() = default;
};
explicit HungPagesTableModel(Delegate* delegate);
HungPagesTableModel(const HungPagesTableModel&) = delete;
HungPagesTableModel& operator=(const HungPagesTableModel&) = delete;
~HungPagesTableModel() override;
void InitForWebContents(content::WebContents* hung_contents,
content::RenderWidgetHost* render_widget_host,
base::RepeatingClosure hang_monitor_restarter);
// Resets the model to the uninitialized state (e.g. unregisters observers
// added by InitForWebContents and disassociates this model from any
// particular WebContents and/or RenderWidgetHost).
void Reset();
void RestartHangMonitorTimeout();
// Returns the hung RenderWidgetHost, or null if there aren't any WebContents.
content::RenderWidgetHost* GetRenderWidgetHost();
// Overridden from ui::TableModel:
size_t RowCount() override;
std::u16string GetText(size_t row, int column_id) override;
ui::ImageModel GetIcon(size_t row) override;
void SetObserver(ui::TableModelObserver* observer) override;
// Overridden from RenderProcessHostObserver:
void RenderProcessExited(
content::RenderProcessHost* host,
const content::ChildProcessTerminationInfo& info) override;
// Overridden from RenderWidgetHostObserver:
void RenderWidgetHostDestroyed(
content::RenderWidgetHost* widget_host) override;
private:
friend class HungRendererDialogViewBrowserTest;
// Used to track a single WebContents. If the WebContents is destroyed
// TabDestroyed() is invoked on the model.
class WebContentsObserverImpl : public content::WebContentsObserver {
public:
WebContentsObserverImpl(HungPagesTableModel* model,
content::WebContents* tab);
WebContentsObserverImpl(const WebContentsObserverImpl&) = delete;
WebContentsObserverImpl& operator=(const WebContentsObserverImpl&) = delete;
favicon::FaviconDriver* favicon_driver() {
return favicon::ContentFaviconDriver::FromWebContents(web_contents());
}
// WebContentsObserver overrides:
void RenderFrameHostChanged(content::RenderFrameHost* old_host,
content::RenderFrameHost* new_host) override;
void WebContentsDestroyed() override;
private:
raw_ptr<HungPagesTableModel> model_;
};
// Invoked when a WebContents is destroyed. Cleans up |tab_observers_| and
// notifies the observer and delegate.
void TabDestroyed(WebContentsObserverImpl* tab);
// Invoked when a WebContents have been updated. The title or location of
// the WebContents may have changed.
void TabUpdated(WebContentsObserverImpl* tab);
std::vector<std::unique_ptr<WebContentsObserverImpl>> tab_observers_;
raw_ptr<ui::TableModelObserver, DanglingUntriaged> observer_ = nullptr;
raw_ptr<Delegate> delegate_ = nullptr;
raw_ptr<content::RenderWidgetHost> render_widget_host_ = nullptr;
// Callback that restarts the hang timeout (e.g. if the user wants to wait
// some more until the renderer process responds).
base::RepeatingClosure hang_monitor_restarter_;
base::ScopedObservation<content::RenderProcessHost,
content::RenderProcessHostObserver>
process_observation_{this};
base::ScopedObservation<content::RenderWidgetHost,
content::RenderWidgetHostObserver>
widget_observation_{this};
};
// This class displays a dialog which contains information about a hung
// renderer process.
class HungRendererDialogView : public views::DialogDelegateView,
public HungPagesTableModel::Delegate {
METADATA_HEADER(HungRendererDialogView, views::DialogDelegateView)
public:
HungRendererDialogView(const HungRendererDialogView&) = delete;
HungRendererDialogView& operator=(const HungRendererDialogView&) = delete;
// Shows or hides the hung renderer dialog for the given WebContents.
static void Show(content::WebContents* contents,
content::RenderWidgetHost* render_widget_host,
base::RepeatingClosure hang_monitor_restarter);
static void Hide(content::WebContents* contents,
content::RenderWidgetHost* render_widget_host);
// Returns true if there is an instance showing for the given WebContents.
static bool IsShowingForWebContents(content::WebContents* contents);
views::TableView* table_for_testing() { return hung_pages_table_; }
HungPagesTableModel* table_model_for_testing() {
return hung_pages_table_model_.get();
}
// views::DialogDelegateView overrides:
std::u16string GetWindowTitle() const override;
bool ShouldShowCloseButton() const override;
// HungPagesTableModel::Delegate overrides:
void TabUpdated() override;
void TabDestroyed() override;
private:
friend class HungRendererDialogViewBrowserTest;
explicit HungRendererDialogView(content::WebContents* web_contents);
~HungRendererDialogView() override;
// Creates an instance for the given WebContents and window.
static HungRendererDialogView* CreateInstance(content::WebContents* contents,
gfx::NativeWindow window);
// Gets the instance, if any, for the given WebContents, or null if there is
// none.
static HungRendererDialogView* GetInstanceForWebContentsForTests(
content::WebContents* contents);
// Shows or hides the dialog. Dispatched to by the `Show()` and `Hide()`
// static methods.
void ShowDialog(content::RenderWidgetHost* render_widget_host,
base::RepeatingClosure hang_monitor_restarter);
void EndDialog(content::RenderWidgetHost* render_widget_host);
// Called when the dialog is accepted (i.e. the user clicked the "Wait"
// button).
void OnDialogAccepted();
// Called when the dialog is cancelled (i.e. the user clicked the "Exit Page"
// button).
void OnDialogCancelled();
// Called when the dialog is closed (i.e. the user closed the dialog without
// clicking any of the buttons, e.g. by pressing the ESC key).
void OnDialogClosed();
// Restart the hang timer, giving the page more time.
void RestartHangTimer();
// Crashes the hung renderer.
void ForceCrashHungRenderer();
// Resets the association with the WebContents.
//
// TODO(avi): Calls to this are rather unfortunately scattered throughout the
// class, but there doesn't seem to be a place that would work for the three
// ways that the dialog can go away (the two buttons plus the external
// closing). Both the destructor and `WindowClosing()` functions are too late.
// Can it be wired in better?
void ResetWebContentsAssociation();
// Updates the labels and the button text of the dialog. Normally called only
// once when the render process first hangs, right before the dialog is shown.
// It is separated into its own function so that the browsertest's "show UI"
// functionality is able to fake a multi-page hang and force the UI to refresh
// as if multiple pages were legitimately hung.
void UpdateLabels();
// Causes the dialog to close with no action taken. Called when the page
// stops hanging by itself, or when the page or render process goes away.
void CloseDialogWithNoAction();
// Bypasses the requirement for the browser window to be active. Only used in
// tests.
static void BypassActiveBrowserRequirementForTests();
// The WebContents that this dialog was created for and is associated with.
const raw_ptr<content::WebContents, AcrossTasksDanglingUntriaged>
web_contents_;
// The label describing the list.
raw_ptr<views::Label> info_label_ = nullptr;
// Controls within the dialog box.
raw_ptr<views::TableView> hung_pages_table_ = nullptr;
// The model that provides the contents of the table that shows a list of
// pages affected by the hang.
std::unique_ptr<HungPagesTableModel> hung_pages_table_model_;
};
#endif // CHROME_BROWSER_UI_VIEWS_HUNG_RENDERER_VIEW_H_
|