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

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: Fix leak 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) it might be nice to do some stuff to capture a stack.
yzshen1 2012/04/11 18:30:46 Please do so soon. :) Besides, please record some
brettw 2012/04/11 19:56:08 I filed a bug for this and updated the comment.
44 // 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 : is_hung(true),
132 path(p),
133 name(n),
134 info_bar(NULL),
135 next_reshow_delay(base::TimeDelta::FromSeconds(kInitialReshowDelaySec)),
136 timer(false, false) {
137 }
138
139 HungPluginTabHelper::PluginState::~PluginState() {
140 }
141
142 // -----------------------------------------------------------------------------
143
144 HungPluginTabHelper::HungPluginTabHelper(content::WebContents* contents)
145 : contents_(contents) {
146 }
147
148 HungPluginTabHelper::~HungPluginTabHelper() {
149 }
150
151 void HungPluginTabHelper::PluginCrashed(const FilePath& plugin_path) {
152 // TODO(brettw) ideally this would take the child process ID. When we do this
153 // for NaCl plugins, we'll want to know exactly which process it was since
154 // the path won't be useful.
155 TabContentsWrapper* tcw =
yzshen1 2012/04/11 18:30:46 line 155-159 can be extracted as a separate method
156 TabContentsWrapper::GetCurrentWrapperForContents(contents_);
157 if (!tcw)
158 return;
159 InfoBarTabHelper* infobar_helper = tcw->infobar_tab_helper();
160
161 // For now, just do a brute-force search to see if we have this plugin. Since
162 // we'll normally have 0 or 1, this is fast.
163 for (PluginStateMap::iterator i = hung_plugins_.begin();
164 i != hung_plugins_.end(); ++i) {
165 if (i->second->path == plugin_path) {
166 if (i->second->info_bar)
167 infobar_helper->RemoveInfoBar(i->second->info_bar);
168 hung_plugins_.erase(i);
169 break;
170 }
171 }
172 }
173
174 void HungPluginTabHelper::PluginHungStatusChanged(int plugin_child_id,
175 const FilePath& plugin_path,
176 bool is_hung) {
177 TabContentsWrapper* tcw =
178 TabContentsWrapper::GetCurrentWrapperForContents(contents_);
179 if (!tcw)
180 return;
181 InfoBarTabHelper* infobar_helper = tcw->infobar_tab_helper();
182
183 PluginStateMap::iterator found = hung_plugins_.find(plugin_child_id);
184 if (found != hung_plugins_.end()) {
185 if (!is_hung) {
186 // Hung plugin became un-hung, close the infobar and delete our info.
187 if (found->second->info_bar)
188 infobar_helper->RemoveInfoBar(found->second->info_bar);
189 hung_plugins_.erase(found);
190 }
191 return;
192 }
193
194 string16 plugin_name =
195 content::PluginService::GetInstance()->GetPluginDisplayNameByPath(
196 plugin_path);
197
198 linked_ptr<PluginState> state(new PluginState(plugin_path, plugin_name));
199 hung_plugins_[plugin_child_id] = state;
200 ShowBar(plugin_child_id, state.get());
201 }
202
203 void HungPluginTabHelper::KillPlugin(int child_id) {
204 PluginStateMap::iterator found = hung_plugins_.find(child_id);
205 if (found == hung_plugins_.end()) {
206 NOTREACHED();
207 return;
208 }
209
210 content::BrowserThread::PostTask(content::BrowserThread::IO,
211 FROM_HERE,
212 base::Bind(&KillPluginOnIOThread, child_id));
213 CloseBar(found->second.get());
214 }
215
216 void HungPluginTabHelper::BarClosed(int child_id) {
217 PluginStateMap::iterator found = hung_plugins_.find(child_id);
218 if (found == hung_plugins_.end() || !found->second->info_bar) {
219 NOTREACHED();
220 return;
221 }
222 found->second->info_bar = NULL;
223 found->second->bar_closed_time = base::TimeTicks::Now();
224
225 // Schedule the timer to re-show the infobar if the plugin continues to be
226 // hung.
227 found->second->timer.Start(FROM_HERE, found->second->next_reshow_delay,
228 base::Bind(&HungPluginTabHelper::OnReshowTimer,
229 base::Unretained(this),
230 child_id));
231
232 // Next time we do this, delay it twice as long to avoid being annoying.
233 found->second->next_reshow_delay *= 2;
234 }
235
236 void HungPluginTabHelper::OnReshowTimer(int child_id) {
237 PluginStateMap::iterator found = hung_plugins_.find(child_id);
238 if (found == hung_plugins_.end() || found->second->info_bar) {
239 // The timer should be cancelled if the record isn't in our map anymore.
240 NOTREACHED();
241 return;
242 }
243 ShowBar(child_id, found->second.get());
244 }
245
246 void HungPluginTabHelper::ShowBar(int child_id, PluginState* state) {
247 TabContentsWrapper* tcw =
248 TabContentsWrapper::GetCurrentWrapperForContents(contents_);
249 if (!tcw)
250 return;
251 InfoBarTabHelper* infobar_helper = tcw->infobar_tab_helper();
252
253 DCHECK(!state->info_bar);
254 state->info_bar = new InfoBarDelegate(this, infobar_helper,
255 child_id, state->name);
256 infobar_helper->AddInfoBar(state->info_bar);
257 }
258
259 void HungPluginTabHelper::CloseBar(PluginState* state) {
260 TabContentsWrapper* tcw =
261 TabContentsWrapper::GetCurrentWrapperForContents(contents_);
262 if (!tcw)
263 return;
264 InfoBarTabHelper* infobar_helper = tcw->infobar_tab_helper();
265
266 if (state->info_bar) {
267 infobar_helper->RemoveInfoBar(state->info_bar);
268 state->info_bar = NULL;
269 }
270 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698