Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(306)

Side by Side Diff: chrome/browser/ui/hung_plugin_tab_helper.cc

Issue 10014013: Add a hang monitor for Pepper plugins (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 8 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
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
3 // found in the LICENSE file.
4
5 #include "chrome/browser/ui/hung_plugin_tab_helper.h"
6
7 #include "base/bind.h"
8 #include "base/process_util.h"
9 #include "chrome/browser/infobars/infobar_tab_helper.h"
10 #include "chrome/browser/tab_contents/confirm_infobar_delegate.h"
11 #include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h"
12 #include "content/public/browser/browser_thread.h"
13 #include "content/public/browser/browser_child_process_host_iterator.h"
14 #include "content/public/browser/child_process_data.h"
15 #include "content/public/browser/plugin_service.h"
16 #include "content/public/common/result_codes.h"
17 #include "grit/chromium_strings.h"
18 #include "grit/generated_resources.h"
19 #include "grit/locale_settings.h"
20 #include "grit/theme_resources_standard.h"
21 #include "ui/base/l10n/l10n_util.h"
22 #include "ui/base/resource/resource_bundle.h"
23
24 namespace {
25
26 // Delay in seconds before re-showing the hung plugin message. This will be
27 // increased each time.
28 const int kInitialReshowDelaySec = 10;
29
30 // Called on the I/O thread to actually kill the plugin with the given child
31 // ID. We specifically don't want this to be a member function since if the
32 // user chooses to kill the plugin, we want to kill it even if they close the
33 // tab first.
34 //
35 // Be careful with the child_id. It's supplied by the renderer which might be
36 // hacked.
37 void KillPluginOnIOThread(int child_id) {
38 content::BrowserChildProcessHostIterator iter(
39 content::PROCESS_TYPE_PPAPI_PLUGIN);
40 while (!iter.Done()) {
41 const content::ChildProcessData& data = iter.GetData();
42 if (data.id == child_id) {
43 // TODO(brettw) bug 123021: it might be nice to do some stuff to capture
44 // a stack. The NPAPI Windows hang monitor does some cool stuff in
45 // hung_window_detector.cc.
46 base::KillProcess(data.handle, content::RESULT_CODE_HUNG, false);
47 break;
48 }
49 ++iter;
50 }
51 // Ignore the case where we didn't find the plugin, it may have terminated
52 // before this function could run.
53 }
54
55 } // namespace
56
57 class HungPluginTabHelper::InfoBarDelegate : public ConfirmInfoBarDelegate {
58 public:
59 InfoBarDelegate(HungPluginTabHelper* helper,
60 InfoBarTabHelper* infobar_helper,
61 int plugin_child_id,
62 const string16& plugin_name);
63 virtual ~InfoBarDelegate();
64
65 // ConfirmInfoBarDelegate:
66 virtual gfx::Image* GetIcon() const OVERRIDE;
67 virtual string16 GetMessageText() const OVERRIDE;
68 virtual int GetButtons() const OVERRIDE;
69 virtual string16 GetButtonLabel(InfoBarButton button) const OVERRIDE;
70 virtual bool Accept() OVERRIDE;
71 virtual void InfoBarDismissed() OVERRIDE;
72
73 private:
74 HungPluginTabHelper* helper_;
75 int plugin_child_id_;
76
77 string16 message_;
78 string16 button_text_;
79 gfx::Image* icon_;
80 };
81
82 HungPluginTabHelper::InfoBarDelegate::InfoBarDelegate(
83 HungPluginTabHelper* helper,
84 InfoBarTabHelper* infobar_helper,
85 int plugin_child_id,
86 const string16& plugin_name)
87 : ConfirmInfoBarDelegate(infobar_helper),
88 helper_(helper),
89 plugin_child_id_(plugin_child_id) {
90 message_ = l10n_util::GetStringFUTF16(IDS_BROWSER_HANGMONITOR_PLUGIN_INFOBAR,
91 plugin_name);
92 button_text_ = l10n_util::GetStringUTF16(
93 IDS_BROWSER_HANGMONITOR_PLUGIN_INFOBAR_KILLBUTTON);
94 icon_ = &ResourceBundle::GetSharedInstance().GetNativeImageNamed(
95 IDR_INFOBAR_PLUGIN_CRASHED);
96 }
97
98 HungPluginTabHelper::InfoBarDelegate::~InfoBarDelegate() {
99 }
100
101 gfx::Image* HungPluginTabHelper::InfoBarDelegate::GetIcon() const {
102 return icon_;
103 }
104
105 string16 HungPluginTabHelper::InfoBarDelegate::GetMessageText() const {
106 return message_;
107 }
108
109 int HungPluginTabHelper::InfoBarDelegate::GetButtons() const {
110 return BUTTON_OK;
111 }
112
113 string16 HungPluginTabHelper::InfoBarDelegate::GetButtonLabel(
114 InfoBarButton button) const {
115 return button_text_;
116 }
117
118 bool HungPluginTabHelper::InfoBarDelegate::Accept() {
119 helper_->KillPlugin(plugin_child_id_);
120 return true;
121 }
122
123 void HungPluginTabHelper::InfoBarDelegate::InfoBarDismissed() {
124 helper_->BarClosed(plugin_child_id_);
125 }
126
127 // -----------------------------------------------------------------------------
128
129 HungPluginTabHelper::PluginState::PluginState(const FilePath& p,
130 const string16& n)
131 : path(p),
132 name(n),
133 info_bar(NULL),
134 next_reshow_delay(base::TimeDelta::FromSeconds(kInitialReshowDelaySec)),
135 timer(false, false) {
136 }
137
138 HungPluginTabHelper::PluginState::~PluginState() {
139 }
140
141 // -----------------------------------------------------------------------------
142
143 HungPluginTabHelper::HungPluginTabHelper(content::WebContents* contents)
144 : contents_(contents) {
145 }
146
147 HungPluginTabHelper::~HungPluginTabHelper() {
148 }
149
150 void HungPluginTabHelper::PluginCrashed(const FilePath& plugin_path) {
151 // TODO(brettw) ideally this would take the child process ID. When we do this
152 // for NaCl plugins, we'll want to know exactly which process it was since
153 // the path won't be useful.
154 InfoBarTabHelper* infobar_helper = GetInfoBarHelper();
155 if (!infobar_helper)
156 return;
157
158 // For now, just do a brute-force search to see if we have this plugin. Since
159 // we'll normally have 0 or 1, this is fast.
160 for (PluginStateMap::iterator i = hung_plugins_.begin();
161 i != hung_plugins_.end(); ++i) {
162 if (i->second->path == plugin_path) {
163 if (i->second->info_bar)
164 infobar_helper->RemoveInfoBar(i->second->info_bar);
165 hung_plugins_.erase(i);
166 break;
167 }
168 }
169 }
170
171 void HungPluginTabHelper::PluginHungStatusChanged(int plugin_child_id,
172 const FilePath& plugin_path,
173 bool is_hung) {
174 InfoBarTabHelper* infobar_helper = GetInfoBarHelper();
175 if (!infobar_helper)
176 return;
177
178 PluginStateMap::iterator found = hung_plugins_.find(plugin_child_id);
179 if (found != hung_plugins_.end()) {
180 if (!is_hung) {
181 // Hung plugin became un-hung, close the infobar and delete our info.
182 if (found->second->info_bar)
183 infobar_helper->RemoveInfoBar(found->second->info_bar);
184 hung_plugins_.erase(found);
185 }
186 return;
187 }
188
189 string16 plugin_name =
190 content::PluginService::GetInstance()->GetPluginDisplayNameByPath(
191 plugin_path);
192
193 linked_ptr<PluginState> state(new PluginState(plugin_path, plugin_name));
194 hung_plugins_[plugin_child_id] = state;
195 ShowBar(plugin_child_id, state.get());
196 }
197
198 void HungPluginTabHelper::KillPlugin(int child_id) {
199 PluginStateMap::iterator found = hung_plugins_.find(child_id);
200 if (found == hung_plugins_.end()) {
201 NOTREACHED();
202 return;
203 }
204
205 content::BrowserThread::PostTask(content::BrowserThread::IO,
206 FROM_HERE,
207 base::Bind(&KillPluginOnIOThread, child_id));
208 CloseBar(found->second.get());
209 }
210
211 void HungPluginTabHelper::BarClosed(int child_id) {
212 PluginStateMap::iterator found = hung_plugins_.find(child_id);
213 if (found == hung_plugins_.end() || !found->second->info_bar) {
214 NOTREACHED();
215 return;
216 }
217 found->second->info_bar = NULL;
218
219 // Schedule the timer to re-show the infobar if the plugin continues to be
220 // hung.
221 found->second->timer.Start(FROM_HERE, found->second->next_reshow_delay,
222 base::Bind(&HungPluginTabHelper::OnReshowTimer,
223 base::Unretained(this),
224 child_id));
225
226 // Next time we do this, delay it twice as long to avoid being annoying.
227 found->second->next_reshow_delay *= 2;
228 }
229
230 void HungPluginTabHelper::OnReshowTimer(int child_id) {
231 PluginStateMap::iterator found = hung_plugins_.find(child_id);
232 if (found == hung_plugins_.end() || found->second->info_bar) {
233 // The timer should be cancelled if the record isn't in our map anymore.
234 NOTREACHED();
235 return;
236 }
237 ShowBar(child_id, found->second.get());
238 }
239
240 void HungPluginTabHelper::ShowBar(int child_id, PluginState* state) {
241 InfoBarTabHelper* infobar_helper = GetInfoBarHelper();
242 if (!infobar_helper)
243 return;
244
245 DCHECK(!state->info_bar);
246 state->info_bar = new InfoBarDelegate(this, infobar_helper,
247 child_id, state->name);
248 infobar_helper->AddInfoBar(state->info_bar);
249 }
250
251 void HungPluginTabHelper::CloseBar(PluginState* state) {
252 InfoBarTabHelper* infobar_helper = GetInfoBarHelper();
253 if (!infobar_helper)
254 return;
255
256 if (state->info_bar) {
257 infobar_helper->RemoveInfoBar(state->info_bar);
258 state->info_bar = NULL;
259 }
260 }
261
262 InfoBarTabHelper* HungPluginTabHelper::GetInfoBarHelper() {
263 TabContentsWrapper* tcw =
264 TabContentsWrapper::GetCurrentWrapperForContents(contents_);
265 if (!tcw)
266 return NULL;
267 return tcw->infobar_tab_helper();
268 }
OLDNEW
« no previous file with comments | « chrome/browser/ui/hung_plugin_tab_helper.h ('k') | chrome/browser/ui/tab_contents/tab_contents_wrapper.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698