diff options
author | estade@chromium.org <estade@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-03-23 22:20:47 +0000 |
---|---|---|
committer | estade@chromium.org <estade@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-03-23 22:20:47 +0000 |
commit | 5ff5ee97910c9fb6909f4a3029427edd21366d9b (patch) | |
tree | ae91db49647992032403bea599588408e74282c8 /chrome | |
parent | b22189fe300c9cdcd5cf929c2f9e76c3f935fcfa (diff) | |
download | chromium_src-5ff5ee97910c9fb6909f4a3029427edd21366d9b.zip chromium_src-5ff5ee97910c9fb6909f4a3029427edd21366d9b.tar.gz chromium_src-5ff5ee97910c9fb6909f4a3029427edd21366d9b.tar.bz2 |
GTK: allow inspecting of extension popups.
BUG=24477
TEST=manual
Review URL: http://codereview.chromium.org/1170001
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@42389 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome')
-rw-r--r-- | chrome/browser/gtk/bookmark_bubble_gtk.cc | 3 | ||||
-rw-r--r-- | chrome/browser/gtk/browser_actions_toolbar_gtk.cc | 52 | ||||
-rw-r--r-- | chrome/browser/gtk/content_blocked_bubble_gtk.cc | 3 | ||||
-rw-r--r-- | chrome/browser/gtk/extension_installed_bubble_gtk.cc | 3 | ||||
-rw-r--r-- | chrome/browser/gtk/extension_popup_gtk.cc | 55 | ||||
-rw-r--r-- | chrome/browser/gtk/extension_popup_gtk.h | 16 | ||||
-rw-r--r-- | chrome/browser/gtk/first_run_bubble.cc | 3 | ||||
-rw-r--r-- | chrome/browser/gtk/info_bubble_gtk.cc | 102 | ||||
-rw-r--r-- | chrome/browser/gtk/info_bubble_gtk.h | 17 | ||||
-rw-r--r-- | chrome/browser/gtk/location_bar_view_gtk.cc | 3 | ||||
-rw-r--r-- | chrome/browser/views/extensions/extension_popup.cc | 3 |
11 files changed, 171 insertions, 89 deletions
diff --git a/chrome/browser/gtk/bookmark_bubble_gtk.cc b/chrome/browser/gtk/bookmark_bubble_gtk.cc index 19c7374..663d338 100644 --- a/chrome/browser/gtk/bookmark_bubble_gtk.cc +++ b/chrome/browser/gtk/bookmark_bubble_gtk.cc @@ -229,7 +229,8 @@ BookmarkBubbleGtk::BookmarkBubbleGtk(GtkWindow* toplevel_window, rect, content, arrow_location, - true, + true, // match_system_theme + true, // grab_input theme_provider_, this); // delegate if (!bubble_) { diff --git a/chrome/browser/gtk/browser_actions_toolbar_gtk.cc b/chrome/browser/gtk/browser_actions_toolbar_gtk.cc index 31d3c24..b6bb11b 100644 --- a/chrome/browser/gtk/browser_actions_toolbar_gtk.cc +++ b/chrome/browser/gtk/browser_actions_toolbar_gtk.cc @@ -178,10 +178,32 @@ class BrowserActionButton : public NotificationObserver, } private: + // Returns true to prevent further processing of the event that caused us to + // show the popup, or false to continue processing. + bool ShowPopup(bool devtools) { + ExtensionAction* browser_action = extension_->browser_action(); + + int tab_id = toolbar_->GetCurrentTabId(); + if (tab_id < 0) { + NOTREACHED() << "No current tab."; + return true; + } + + if (browser_action->HasPopup(tab_id)) { + ExtensionPopupGtk::Show( + browser_action->GetPopupUrl(tab_id), + toolbar_->browser(), + gtk_util::GetWidgetRectRelativeToToplevel(widget()), + devtools); + return true; + } + + return false; + } + // ExtensionContextMenuModel::PopupDelegate implementation. virtual void InspectPopup(ExtensionAction* action) { - // TODO(estade): http://crbug.com/24477 - NOTIMPLEMENTED(); + ShowPopup(true); } void SetImage(GdkPixbuf* image) { @@ -208,24 +230,12 @@ class BrowserActionButton : public NotificationObserver, } static void OnClicked(GtkWidget* widget, BrowserActionButton* action) { - ExtensionAction* browser_action = action->extension_->browser_action(); - - int tab_id = action->toolbar_->GetCurrentTabId(); - if (tab_id < 0) { - NOTREACHED() << "No current tab."; + if (action->ShowPopup(false)) return; - } - if (browser_action->HasPopup(tab_id)) { - ExtensionPopupGtk::Show( - browser_action->GetPopupUrl(tab_id), - action->toolbar_->browser(), - gtk_util::GetWidgetRectRelativeToToplevel(widget)); - } else { - ExtensionBrowserEventRouter::GetInstance()->BrowserActionExecuted( - action->toolbar_->browser()->profile(), action->extension_->id(), - action->toolbar_->browser()); - } + ExtensionBrowserEventRouter::GetInstance()->BrowserActionExecuted( + action->toolbar_->browser()->profile(), action->extension_->id(), + action->toolbar_->browser()); } static gboolean OnExposeEvent(GtkWidget* widget, @@ -541,7 +551,8 @@ void BrowserActionsToolbarGtk::ExecuteCommandById(int command_id) { if (browser_action->HasPopup(tab_id)) { ExtensionPopupGtk::Show( browser_action->GetPopupUrl(tab_id), browser(), - gtk_util::GetWidgetRectRelativeToToplevel(overflow_button_.widget())); + gtk_util::GetWidgetRectRelativeToToplevel(overflow_button_.widget()), + false); } else { ExtensionBrowserEventRouter::GetInstance()->BrowserActionExecuted( browser()->profile(), extension->id(), browser()); @@ -644,10 +655,11 @@ void BrowserActionsToolbarGtk::OnHierarchyChanged(GtkWidget* widget, void BrowserActionsToolbarGtk::OnSetFocus(GtkWidget* widget, GtkWidget* focus_widget) { + ExtensionPopupGtk* popup = ExtensionPopupGtk::get_current_extension_popup(); // The focus of the parent window has changed. Close the popup. Delay the hide // because it will destroy the RenderViewHost, which may still be on the // call stack. - if (!ExtensionPopupGtk::get_current_extension_popup()) + if (!popup || popup->being_inspected()) return; MessageLoop::current()->PostTask(FROM_HERE, method_factory_.NewRunnableMethod(&BrowserActionsToolbarGtk::HidePopup)); diff --git a/chrome/browser/gtk/content_blocked_bubble_gtk.cc b/chrome/browser/gtk/content_blocked_bubble_gtk.cc index 5f083c21..79f3bad 100644 --- a/chrome/browser/gtk/content_blocked_bubble_gtk.cc +++ b/chrome/browser/gtk/content_blocked_bubble_gtk.cc @@ -174,7 +174,8 @@ void ContentSettingBubbleGtk::BuildBubble() { bounds_, bubble_content, arrow_location, - true, + true, // match_system_theme + true, // grab_input theme_provider, this); } diff --git a/chrome/browser/gtk/extension_installed_bubble_gtk.cc b/chrome/browser/gtk/extension_installed_bubble_gtk.cc index 053fe15..24b579a 100644 --- a/chrome/browser/gtk/extension_installed_bubble_gtk.cc +++ b/chrome/browser/gtk/extension_installed_bubble_gtk.cc @@ -211,7 +211,8 @@ void ExtensionInstalledBubbleGtk::ShowInternal() { bounds, bubble_content, arrow_location, - true, + true, // match_system_theme + true, // grab_input theme_provider, this); } diff --git a/chrome/browser/gtk/extension_popup_gtk.cc b/chrome/browser/gtk/extension_popup_gtk.cc index 878e6db..fad0c0b 100644 --- a/chrome/browser/gtk/extension_popup_gtk.cc +++ b/chrome/browser/gtk/extension_popup_gtk.cc @@ -7,8 +7,10 @@ #include <gtk/gtk.h> #include "base/i18n/rtl.h" +#include "base/message_loop.h" #include "chrome/browser/browser.h" #include "chrome/browser/browser_window.h" +#include "chrome/browser/debugger/devtools_manager.h" #include "chrome/browser/profile.h" #include "chrome/browser/extensions/extension_host.h" #include "chrome/browser/extensions/extension_process_manager.h" @@ -20,11 +22,14 @@ ExtensionPopupGtk* ExtensionPopupGtk::current_extension_popup_ = NULL; ExtensionPopupGtk::ExtensionPopupGtk(Browser* browser, ExtensionHost* host, - const gfx::Rect& relative_to) + const gfx::Rect& relative_to, + bool inspect) : browser_(browser), bubble_(NULL), host_(host), - relative_to_(relative_to) { + relative_to_(relative_to), + being_inspected_(inspect), + method_factory_(this) { // If the host had somehow finished loading, then we'd miss the notification // and not show. This seems to happen in single-process mode. if (host->did_stop_loading()) { @@ -44,15 +49,28 @@ ExtensionPopupGtk::~ExtensionPopupGtk() { void ExtensionPopupGtk::Observe(NotificationType type, const NotificationSource& source, const NotificationDetails& details) { - if (Details<ExtensionHost>(host_.get()) != details) - return; - - if (type == NotificationType::EXTENSION_HOST_DID_STOP_LOADING) { - ShowPopup(); - } else if (type == NotificationType::EXTENSION_HOST_VIEW_SHOULD_CLOSE) { - DestroyPopup(); - } else { - NOTREACHED() << "Received unexpected notification"; + switch (type.value) { + case NotificationType::EXTENSION_HOST_DID_STOP_LOADING: + if (Details<ExtensionHost>(host_.get()) == details) + ShowPopup(); + break; + case NotificationType::EXTENSION_HOST_VIEW_SHOULD_CLOSE: + if (Details<ExtensionHost>(host_.get()) == details) + DestroyPopup(); + break; + case NotificationType::DEVTOOLS_WINDOW_CLOSING: + // Make sure its the devtools window that inspecting our popup. + if (Details<RenderViewHost>(host_->render_view_host()) != details) + break; + + // If the devtools window is closing, we post a task to ourselves to + // close the popup. This gives the devtools window a chance to finish + // detaching from the inspected RenderViewHost. + MessageLoop::current()->PostTask(FROM_HERE, + method_factory_.NewRunnableMethod(&ExtensionPopupGtk::DestroyPopup)); + break; + default: + NOTREACHED() << "Received unexpected notification"; } } @@ -62,6 +80,14 @@ void ExtensionPopupGtk::ShowPopup() { return; } + if (being_inspected_) { + DevToolsManager::GetInstance()->OpenDevToolsWindow( + host_->render_view_host()); + // Listen for the the devtools window closing. + registrar_.Add(this, NotificationType::DEVTOOLS_WINDOW_CLOSING, + Source<Profile>(host_->profile())); + } + // Only one instance should be showing at a time. Get rid of the old one, if // any. Typically, |current_extension_popup_| will be NULL, but it can be // non-NULL if a browser action button is clicked while another extension @@ -81,7 +107,8 @@ void ExtensionPopupGtk::ShowPopup() { relative_to_, host_->view()->native_view(), arrow_location, - false, + false, // match_system_theme + !being_inspected_, // grab_input GtkThemeProvider::GetFrom(browser_->profile()), this); } @@ -104,7 +131,7 @@ void ExtensionPopupGtk::InfoBubbleClosing(InfoBubbleGtk* bubble, // static void ExtensionPopupGtk::Show(const GURL& url, Browser* browser, - const gfx::Rect& relative_to) { + const gfx::Rect& relative_to, bool inspect) { ExtensionProcessManager* manager = browser->profile()->GetExtensionProcessManager(); DCHECK(manager); @@ -113,7 +140,7 @@ void ExtensionPopupGtk::Show(const GURL& url, Browser* browser, ExtensionHost* host = manager->CreatePopup(url, browser); // This object will delete itself when the info bubble is closed. - new ExtensionPopupGtk(browser, host, relative_to); + new ExtensionPopupGtk(browser, host, relative_to, inspect); } gfx::Rect ExtensionPopupGtk::GetViewBounds() { diff --git a/chrome/browser/gtk/extension_popup_gtk.h b/chrome/browser/gtk/extension_popup_gtk.h index 62750fe..9cced80 100644 --- a/chrome/browser/gtk/extension_popup_gtk.h +++ b/chrome/browser/gtk/extension_popup_gtk.h @@ -6,6 +6,7 @@ #define CHROME_BROWSER_GTK_EXTENSION_POPUP_GTK_H_ #include "base/scoped_ptr.h" +#include "base/task.h" #include "chrome/browser/gtk/info_bubble_gtk.h" #include "chrome/common/notification_observer.h" #include "chrome/common/notification_registrar.h" @@ -20,12 +21,14 @@ class ExtensionPopupGtk : public NotificationObserver, public: ExtensionPopupGtk(Browser* browser, ExtensionHost* host, - const gfx::Rect& relative_to); + const gfx::Rect& relative_to, + bool inspect); virtual ~ExtensionPopupGtk(); static void Show(const GURL& url, Browser* browser, - const gfx::Rect& relative_to); + const gfx::Rect& relative_to, + bool inspect); // NotificationObserver implementation. virtual void Observe(NotificationType type, @@ -46,6 +49,10 @@ class ExtensionPopupGtk : public NotificationObserver, return current_extension_popup_; } + bool being_inspected() const { + return being_inspected_; + } + private: // Shows the popup widget. Called after loading completes. void ShowPopup(); @@ -65,6 +72,11 @@ class ExtensionPopupGtk : public NotificationObserver, static ExtensionPopupGtk* current_extension_popup_; + // Whether a devtools window is attached to this bubble. + bool being_inspected_; + + ScopedRunnableMethodFactory<ExtensionPopupGtk> method_factory_; + // Used for testing. --------------------------------------------------------- gfx::Rect GetViewBounds(); diff --git a/chrome/browser/gtk/first_run_bubble.cc b/chrome/browser/gtk/first_run_bubble.cc index d70c94c..f2a8433 100644 --- a/chrome/browser/gtk/first_run_bubble.cc +++ b/chrome/browser/gtk/first_run_bubble.cc @@ -156,7 +156,8 @@ FirstRunBubble::FirstRunBubble(Profile* profile, rect, content_, arrow_location, - true, + true, // match_system_theme + true, // grab_input theme_provider_, this); // delegate if (!bubble_) { diff --git a/chrome/browser/gtk/info_bubble_gtk.cc b/chrome/browser/gtk/info_bubble_gtk.cc index 9da396a..5e1c2f2 100644 --- a/chrome/browser/gtk/info_bubble_gtk.cc +++ b/chrome/browser/gtk/info_bubble_gtk.cc @@ -49,10 +49,11 @@ InfoBubbleGtk* InfoBubbleGtk::Show(GtkWindow* toplevel_window, GtkWidget* content, ArrowLocationGtk arrow_location, bool match_system_theme, + bool grab_input, GtkThemeProvider* provider, InfoBubbleGtkDelegate* delegate) { InfoBubbleGtk* bubble = new InfoBubbleGtk(provider, match_system_theme); - bubble->Init(toplevel_window, rect, content, arrow_location); + bubble->Init(toplevel_window, rect, content, arrow_location, grab_input); bubble->set_delegate(delegate); return bubble; } @@ -67,37 +68,51 @@ InfoBubbleGtk::InfoBubbleGtk(GtkThemeProvider* provider, mask_region_(NULL), preferred_arrow_location_(ARROW_LOCATION_TOP_LEFT), current_arrow_location_(ARROW_LOCATION_TOP_LEFT), - match_system_theme_(match_system_theme) { + match_system_theme_(match_system_theme), + grab_input_(true), + closed_by_escape_(false) { } InfoBubbleGtk::~InfoBubbleGtk() { + // Notify the delegate that we're about to close. This gives the chance + // to save state / etc from the hosted widget before it's destroyed. + if (delegate_) + delegate_->InfoBubbleClosing(this, closed_by_escape_); + g_object_unref(accel_group_); if (mask_region_) { gdk_region_destroy(mask_region_); mask_region_ = NULL; } - g_signal_handlers_disconnect_by_func( - toplevel_window_, - reinterpret_cast<gpointer>(HandleToplevelConfigureThunk), - this); - g_signal_handlers_disconnect_by_func( - toplevel_window_, - reinterpret_cast<gpointer>(HandleToplevelUnmapThunk), - this); + if (toplevel_window_) { + g_signal_handlers_disconnect_by_func( + toplevel_window_, + reinterpret_cast<gpointer>(HandleToplevelConfigureThunk), + this); + g_signal_handlers_disconnect_by_func( + toplevel_window_, + reinterpret_cast<gpointer>(HandleToplevelUnmapThunk), + this); + } toplevel_window_ = NULL; } void InfoBubbleGtk::Init(GtkWindow* toplevel_window, const gfx::Rect& rect, GtkWidget* content, - ArrowLocationGtk arrow_location) { + ArrowLocationGtk arrow_location, + bool grab_input) { DCHECK(!window_); toplevel_window_ = toplevel_window; rect_ = rect; preferred_arrow_location_ = arrow_location; - window_ = gtk_window_new(GTK_WINDOW_POPUP); + grab_input_ = grab_input; + // Using a TOPLEVEL window may cause placement issues with certain WMs but it + // is necessary to be able to focus the window. + window_ = gtk_window_new(grab_input ? GTK_WINDOW_POPUP : GTK_WINDOW_TOPLEVEL); + gtk_widget_set_app_paintable(window_, TRUE); // Resizing is handled by the program, not user. gtk_window_set_resizable(GTK_WINDOW(window_), FALSE); @@ -140,26 +155,31 @@ void InfoBubbleGtk::Init(GtkWindow* toplevel_window, G_CALLBACK(&HandleToplevelConfigureThunk), this); g_signal_connect(toplevel_window, "unmap-event", G_CALLBACK(&HandleToplevelUnmapThunk), this); + // Set |toplevel_window_| to NULL if it gets destroyed. + g_signal_connect(toplevel_window, "destroy", + G_CALLBACK(gtk_widget_destroyed), &toplevel_window_); gtk_widget_show_all(window_); - // We add a GTK (application-level) grab. This means we will get all - // mouse events for our application, even if they were delivered on another - // window. We don't need this to get button presses outside of the bubble's - // window so we'll know to close it (the pointer grab takes care of that), but - // it prevents other widgets from getting highlighted when the pointer moves - // over them. - // - // (Ideally we wouldn't add the window to a group and it would just get all - // the mouse events, but gtk_grab_add() doesn't appear to do anything in that - // case. Adding it to the toplevel window's group first appears to block - // enter/leave events for that window and its subwindows, although other - // browser windows still receive them). - gtk_window_group_add_window(gtk_window_get_group(toplevel_window), - GTK_WINDOW(window_)); - gtk_grab_add(window_); - - GrabPointerAndKeyboard(); + if (grab_input_) { + // We add a GTK (application-level) grab. This means we will get all + // mouse events for our application, even if they were delivered on another + // window. We don't need this to get button presses outside of the bubble's + // window so we'll know to close it (the pointer grab takes care of that), + // but it prevents other widgets from getting highlighted when the pointer + // moves over them. + // + // (Ideally we wouldn't add the window to a group and it would just get all + // the mouse events, but gtk_grab_add() doesn't appear to do anything in + // that case. Adding it to the toplevel window's group first appears to + // block enter/leave events for that window and its subwindows, although + // other browser windows still receive them). + gtk_window_group_add_window(gtk_window_get_group(toplevel_window), + GTK_WINDOW(window_)); + gtk_grab_add(window_); + + GrabPointerAndKeyboard(); + } registrar_.Add(this, NotificationType::BROWSER_THEME_CHANGED, NotificationService::AllSources()); @@ -321,18 +341,13 @@ void InfoBubbleGtk::Observe(NotificationType type, } void InfoBubbleGtk::HandlePointerAndKeyboardUngrabbedByContent() { - GrabPointerAndKeyboard(); + if (grab_input_) + GrabPointerAndKeyboard(); } -void InfoBubbleGtk::CloseInternal(bool closed_by_escape) { - // Notify the delegate that we're about to close. This gives the chance - // to save state / etc from the hosted widget before it's destroyed. - if (delegate_) - delegate_->InfoBubbleClosing(this, closed_by_escape); - +void InfoBubbleGtk::Close() { // We don't need to ungrab the pointer or keyboard here; the X server will // automatically do that when we destroy our window. - DCHECK(window_); gtk_widget_destroy(window_); // |this| has been deleted, see HandleDestroy. @@ -365,7 +380,8 @@ void InfoBubbleGtk::GrabPointerAndKeyboard() { } gboolean InfoBubbleGtk::HandleEscape() { - CloseInternal(true); // Close by escape. + closed_by_escape_ = true; + Close(); return TRUE; } @@ -411,9 +427,13 @@ gboolean InfoBubbleGtk::HandleButtonPress(GdkEventButton* event) { return FALSE; } - // Otherwise we had a click outside of our window, close ourself. - Close(); - return TRUE; + if (grab_input_) { + // Otherwise we had a click outside of our window, close ourself. + Close(); + return TRUE; + } + + return FALSE; } gboolean InfoBubbleGtk::HandleDestroy() { diff --git a/chrome/browser/gtk/info_bubble_gtk.h b/chrome/browser/gtk/info_bubble_gtk.h index ae5048b..4d3d763 100644 --- a/chrome/browser/gtk/info_bubble_gtk.h +++ b/chrome/browser/gtk/info_bubble_gtk.h @@ -61,12 +61,13 @@ class InfoBubbleGtk : public NotificationObserver { GtkWidget* content, ArrowLocationGtk arrow_location, bool match_system_theme, + bool grab_input, GtkThemeProvider* provider, InfoBubbleGtkDelegate* delegate); // Close the bubble if it's open. This will delete the widgets and object, // so you shouldn't hold a InfoBubbleGtk pointer after calling Close(). - void Close() { CloseInternal(false); } + void Close(); // NotificationObserver implementation. virtual void Observe(NotificationType type, @@ -96,7 +97,8 @@ class InfoBubbleGtk : public NotificationObserver { void Init(GtkWindow* toplevel_window, const gfx::Rect& rect, GtkWidget* content, - ArrowLocationGtk arrow_location); + ArrowLocationGtk arrow_location, + bool grab_input); // Make the points for our polygon frame, either for fill (the mask), or for // when we stroke the border. @@ -133,10 +135,6 @@ class InfoBubbleGtk : public NotificationObserver { // Sets the delegate. void set_delegate(InfoBubbleGtkDelegate* delegate) { delegate_ = delegate; } - // Closes the window and notifies the delegate. |closed_by_escape| is true if - // the close is the result of pressing escape. - void CloseInternal(bool closed_by_escape); - // Grab (in the X sense) the pointer and keyboard. This is needed to make // sure that we have the input focus. void GrabPointerAndKeyboard(); @@ -235,6 +233,13 @@ class InfoBubbleGtk : public NotificationObserver { // do not. bool match_system_theme_; + // If true, the popup owns all X input for the duration of its existence. + // This will usually be true, the exception being when inspecting extension + // popups with dev tools. + bool grab_input_; + + bool closed_by_escape_; + NotificationRegistrar registrar_; DISALLOW_COPY_AND_ASSIGN(InfoBubbleGtk); diff --git a/chrome/browser/gtk/location_bar_view_gtk.cc b/chrome/browser/gtk/location_bar_view_gtk.cc index 34346bb..541d01e 100644 --- a/chrome/browser/gtk/location_bar_view_gtk.cc +++ b/chrome/browser/gtk/location_bar_view_gtk.cc @@ -1224,7 +1224,8 @@ gboolean LocationBarViewGtk::PageActionViewGtk::OnButtonPressed( ExtensionPopupGtk::Show( page_action_->GetPopupUrl(current_tab_id_), owner_->browser_, - gtk_util::GetWidgetRectRelativeToToplevel(event_box_.get())); + gtk_util::GetWidgetRectRelativeToToplevel(event_box_.get()), + false); } else { ExtensionBrowserEventRouter::GetInstance()->PageActionExecuted( profile_, diff --git a/chrome/browser/views/extensions/extension_popup.cc b/chrome/browser/views/extensions/extension_popup.cc index 0330b9a..5ff8126 100644 --- a/chrome/browser/views/extensions/extension_popup.cc +++ b/chrome/browser/views/extensions/extension_popup.cc @@ -258,7 +258,8 @@ void ExtensionPopup::Observe(NotificationType type, break; case NotificationType::DEVTOOLS_WINDOW_CLOSING: // Make sure its the devtools window that inspecting our popup. - if (Details<RenderViewHost>(extension_host_->render_view_host()) != details) + if (Details<RenderViewHost>(extension_host_->render_view_host()) != + details) return; // If the devtools window is closing, we post a task to ourselves to |