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

Unified 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 side-by-side diff with in-line comments
Download patch
« 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 »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: chrome/browser/ui/hung_plugin_tab_helper.cc
diff --git a/chrome/browser/ui/hung_plugin_tab_helper.cc b/chrome/browser/ui/hung_plugin_tab_helper.cc
new file mode 100644
index 0000000000000000000000000000000000000000..df2cf8c0714fe765ed264744d2885d4badb5e3b2
--- /dev/null
+++ b/chrome/browser/ui/hung_plugin_tab_helper.cc
@@ -0,0 +1,268 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/hung_plugin_tab_helper.h"
+
+#include "base/bind.h"
+#include "base/process_util.h"
+#include "chrome/browser/infobars/infobar_tab_helper.h"
+#include "chrome/browser/tab_contents/confirm_infobar_delegate.h"
+#include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/browser_child_process_host_iterator.h"
+#include "content/public/browser/child_process_data.h"
+#include "content/public/browser/plugin_service.h"
+#include "content/public/common/result_codes.h"
+#include "grit/chromium_strings.h"
+#include "grit/generated_resources.h"
+#include "grit/locale_settings.h"
+#include "grit/theme_resources_standard.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/resource/resource_bundle.h"
+
+namespace {
+
+// Delay in seconds before re-showing the hung plugin message. This will be
+// increased each time.
+const int kInitialReshowDelaySec = 10;
+
+// Called on the I/O thread to actually kill the plugin with the given child
+// ID. We specifically don't want this to be a member function since if the
+// user chooses to kill the plugin, we want to kill it even if they close the
+// tab first.
+//
+// Be careful with the child_id. It's supplied by the renderer which might be
+// hacked.
+void KillPluginOnIOThread(int child_id) {
+ content::BrowserChildProcessHostIterator iter(
+ content::PROCESS_TYPE_PPAPI_PLUGIN);
+ while (!iter.Done()) {
+ const content::ChildProcessData& data = iter.GetData();
+ if (data.id == child_id) {
+ // TODO(brettw) bug 123021: it might be nice to do some stuff to capture
+ // a stack. The NPAPI Windows hang monitor does some cool stuff in
+ // hung_window_detector.cc.
+ base::KillProcess(data.handle, content::RESULT_CODE_HUNG, false);
+ break;
+ }
+ ++iter;
+ }
+ // Ignore the case where we didn't find the plugin, it may have terminated
+ // before this function could run.
+}
+
+} // namespace
+
+class HungPluginTabHelper::InfoBarDelegate : public ConfirmInfoBarDelegate {
+ public:
+ InfoBarDelegate(HungPluginTabHelper* helper,
+ InfoBarTabHelper* infobar_helper,
+ int plugin_child_id,
+ const string16& plugin_name);
+ virtual ~InfoBarDelegate();
+
+ // ConfirmInfoBarDelegate:
+ virtual gfx::Image* GetIcon() const OVERRIDE;
+ virtual string16 GetMessageText() const OVERRIDE;
+ virtual int GetButtons() const OVERRIDE;
+ virtual string16 GetButtonLabel(InfoBarButton button) const OVERRIDE;
+ virtual bool Accept() OVERRIDE;
+ virtual void InfoBarDismissed() OVERRIDE;
+
+ private:
+ HungPluginTabHelper* helper_;
+ int plugin_child_id_;
+
+ string16 message_;
+ string16 button_text_;
+ gfx::Image* icon_;
+};
+
+HungPluginTabHelper::InfoBarDelegate::InfoBarDelegate(
+ HungPluginTabHelper* helper,
+ InfoBarTabHelper* infobar_helper,
+ int plugin_child_id,
+ const string16& plugin_name)
+ : ConfirmInfoBarDelegate(infobar_helper),
+ helper_(helper),
+ plugin_child_id_(plugin_child_id) {
+ message_ = l10n_util::GetStringFUTF16(IDS_BROWSER_HANGMONITOR_PLUGIN_INFOBAR,
+ plugin_name);
+ button_text_ = l10n_util::GetStringUTF16(
+ IDS_BROWSER_HANGMONITOR_PLUGIN_INFOBAR_KILLBUTTON);
+ icon_ = &ResourceBundle::GetSharedInstance().GetNativeImageNamed(
+ IDR_INFOBAR_PLUGIN_CRASHED);
+}
+
+HungPluginTabHelper::InfoBarDelegate::~InfoBarDelegate() {
+}
+
+gfx::Image* HungPluginTabHelper::InfoBarDelegate::GetIcon() const {
+ return icon_;
+}
+
+string16 HungPluginTabHelper::InfoBarDelegate::GetMessageText() const {
+ return message_;
+}
+
+int HungPluginTabHelper::InfoBarDelegate::GetButtons() const {
+ return BUTTON_OK;
+}
+
+string16 HungPluginTabHelper::InfoBarDelegate::GetButtonLabel(
+ InfoBarButton button) const {
+ return button_text_;
+}
+
+bool HungPluginTabHelper::InfoBarDelegate::Accept() {
+ helper_->KillPlugin(plugin_child_id_);
+ return true;
+}
+
+void HungPluginTabHelper::InfoBarDelegate::InfoBarDismissed() {
+ helper_->BarClosed(plugin_child_id_);
+}
+
+// -----------------------------------------------------------------------------
+
+HungPluginTabHelper::PluginState::PluginState(const FilePath& p,
+ const string16& n)
+ : path(p),
+ name(n),
+ info_bar(NULL),
+ next_reshow_delay(base::TimeDelta::FromSeconds(kInitialReshowDelaySec)),
+ timer(false, false) {
+}
+
+HungPluginTabHelper::PluginState::~PluginState() {
+}
+
+// -----------------------------------------------------------------------------
+
+HungPluginTabHelper::HungPluginTabHelper(content::WebContents* contents)
+ : contents_(contents) {
+}
+
+HungPluginTabHelper::~HungPluginTabHelper() {
+}
+
+void HungPluginTabHelper::PluginCrashed(const FilePath& plugin_path) {
+ // TODO(brettw) ideally this would take the child process ID. When we do this
+ // for NaCl plugins, we'll want to know exactly which process it was since
+ // the path won't be useful.
+ InfoBarTabHelper* infobar_helper = GetInfoBarHelper();
+ if (!infobar_helper)
+ return;
+
+ // For now, just do a brute-force search to see if we have this plugin. Since
+ // we'll normally have 0 or 1, this is fast.
+ for (PluginStateMap::iterator i = hung_plugins_.begin();
+ i != hung_plugins_.end(); ++i) {
+ if (i->second->path == plugin_path) {
+ if (i->second->info_bar)
+ infobar_helper->RemoveInfoBar(i->second->info_bar);
+ hung_plugins_.erase(i);
+ break;
+ }
+ }
+}
+
+void HungPluginTabHelper::PluginHungStatusChanged(int plugin_child_id,
+ const FilePath& plugin_path,
+ bool is_hung) {
+ InfoBarTabHelper* infobar_helper = GetInfoBarHelper();
+ if (!infobar_helper)
+ return;
+
+ PluginStateMap::iterator found = hung_plugins_.find(plugin_child_id);
+ if (found != hung_plugins_.end()) {
+ if (!is_hung) {
+ // Hung plugin became un-hung, close the infobar and delete our info.
+ if (found->second->info_bar)
+ infobar_helper->RemoveInfoBar(found->second->info_bar);
+ hung_plugins_.erase(found);
+ }
+ return;
+ }
+
+ string16 plugin_name =
+ content::PluginService::GetInstance()->GetPluginDisplayNameByPath(
+ plugin_path);
+
+ linked_ptr<PluginState> state(new PluginState(plugin_path, plugin_name));
+ hung_plugins_[plugin_child_id] = state;
+ ShowBar(plugin_child_id, state.get());
+}
+
+void HungPluginTabHelper::KillPlugin(int child_id) {
+ PluginStateMap::iterator found = hung_plugins_.find(child_id);
+ if (found == hung_plugins_.end()) {
+ NOTREACHED();
+ return;
+ }
+
+ content::BrowserThread::PostTask(content::BrowserThread::IO,
+ FROM_HERE,
+ base::Bind(&KillPluginOnIOThread, child_id));
+ CloseBar(found->second.get());
+}
+
+void HungPluginTabHelper::BarClosed(int child_id) {
+ PluginStateMap::iterator found = hung_plugins_.find(child_id);
+ if (found == hung_plugins_.end() || !found->second->info_bar) {
+ NOTREACHED();
+ return;
+ }
+ found->second->info_bar = NULL;
+
+ // Schedule the timer to re-show the infobar if the plugin continues to be
+ // hung.
+ found->second->timer.Start(FROM_HERE, found->second->next_reshow_delay,
+ base::Bind(&HungPluginTabHelper::OnReshowTimer,
+ base::Unretained(this),
+ child_id));
+
+ // Next time we do this, delay it twice as long to avoid being annoying.
+ found->second->next_reshow_delay *= 2;
+}
+
+void HungPluginTabHelper::OnReshowTimer(int child_id) {
+ PluginStateMap::iterator found = hung_plugins_.find(child_id);
+ if (found == hung_plugins_.end() || found->second->info_bar) {
+ // The timer should be cancelled if the record isn't in our map anymore.
+ NOTREACHED();
+ return;
+ }
+ ShowBar(child_id, found->second.get());
+}
+
+void HungPluginTabHelper::ShowBar(int child_id, PluginState* state) {
+ InfoBarTabHelper* infobar_helper = GetInfoBarHelper();
+ if (!infobar_helper)
+ return;
+
+ DCHECK(!state->info_bar);
+ state->info_bar = new InfoBarDelegate(this, infobar_helper,
+ child_id, state->name);
+ infobar_helper->AddInfoBar(state->info_bar);
+}
+
+void HungPluginTabHelper::CloseBar(PluginState* state) {
+ InfoBarTabHelper* infobar_helper = GetInfoBarHelper();
+ if (!infobar_helper)
+ return;
+
+ if (state->info_bar) {
+ infobar_helper->RemoveInfoBar(state->info_bar);
+ state->info_bar = NULL;
+ }
+}
+
+InfoBarTabHelper* HungPluginTabHelper::GetInfoBarHelper() {
+ TabContentsWrapper* tcw =
+ TabContentsWrapper::GetCurrentWrapperForContents(contents_);
+ if (!tcw)
+ return NULL;
+ return tcw->infobar_tab_helper();
+}
« 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