diff options
author | rafaelw@chromium.org <rafaelw@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-03-17 18:20:31 +0000 |
---|---|---|
committer | rafaelw@chromium.org <rafaelw@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-03-17 18:20:31 +0000 |
commit | 01f829ab3cc802b79942f5a988f6a72e7a2b594d (patch) | |
tree | 8d5566ebfa133239e7947cac02b685df95d5b66f /chrome/browser | |
parent | 5715c63e814adcf7d0515ea08c16955d49615a33 (diff) | |
download | chromium_src-01f829ab3cc802b79942f5a988f6a72e7a2b594d.zip chromium_src-01f829ab3cc802b79942f5a988f6a72e7a2b594d.tar.gz chromium_src-01f829ab3cc802b79942f5a988f6a72e7a2b594d.tar.bz2 |
Initial support for inspecting extension popups.
The primary change in this CL is a refactor which makes ExtensionPopup a bit more self-contained WRT its clients. It adds the ability to specify an "inspect_with_devtools" flag to its Show() method which will cause the popup to remain open regardless of losing focus and to focus a devtools window on the popup's render view host.
This CL also pulls apart some aspects of the extension_popup_api from ExtensionFunctionDispatcher and ExtensionHost.
Still remaining to be done are:
1) Also the popup to stay open when the host window drags (it current closes)
2) Support for GTK
3) Support for Mac
BUG=24477
Review URL: http://codereview.chromium.org/1001002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@41854 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser')
22 files changed, 455 insertions, 555 deletions
diff --git a/chrome/browser/debugger/devtools_manager.cc b/chrome/browser/debugger/devtools_manager.cc index f55dbba..fe3462e 100644 --- a/chrome/browser/debugger/devtools_manager.cc +++ b/chrome/browser/debugger/devtools_manager.cc @@ -171,6 +171,12 @@ void DevToolsManager::ClientHostClosing(DevToolsClientHost* host) { } return; } + + NotificationService::current()->Notify( + NotificationType::DEVTOOLS_WINDOW_CLOSING, + Source<Profile>(inspected_rvh->site_instance()->GetProcess()->profile()), + Details<RenderViewHost>(inspected_rvh)); + SendDetachToAgent(inspected_rvh); inspected_rvh_to_client_host_.erase(inspected_rvh); diff --git a/chrome/browser/extensions/extension_action_context_menu_model.cc b/chrome/browser/extensions/extension_action_context_menu_model.cc index 40b49b7..fa49c4e 100644 --- a/chrome/browser/extensions/extension_action_context_menu_model.cc +++ b/chrome/browser/extensions/extension_action_context_menu_model.cc @@ -7,10 +7,14 @@ #include "app/l10n_util.h" #include "chrome/browser/browser_list.h" #include "chrome/browser/browser_process.h" +#include "chrome/browser/extensions/extension_tabs_module.h" #include "chrome/browser/extensions/extensions_service.h" +#include "chrome/browser/pref_service.h" #include "chrome/browser/profile.h" #include "chrome/common/extensions/extension.h" +#include "chrome/common/extensions/extension_action.h" #include "chrome/common/extensions/extension_constants.h" +#include "chrome/common/pref_names.h" #include "chrome/common/url_constants.h" #include "grit/generated_resources.h" @@ -20,20 +24,29 @@ enum MenuEntries { DISABLE, UNINSTALL, MANAGE, + INSPECT_POPUP }; ExtensionActionContextMenuModel::ExtensionActionContextMenuModel( - Extension* extension) + Extension* extension, ExtensionAction* extension_action, PrefService* prefs, + MenuDelegate* delegate) : ALLOW_THIS_IN_INITIALIZER_LIST(SimpleMenuModel(this)), - extension_(extension) { + extension_(extension), + extension_action_(extension_action), + delegate_(delegate) { AddItem(NAME, UTF8ToUTF16(extension->name())); AddSeparator(); AddItemWithStringId(CONFIGURE, IDS_EXTENSIONS_OPTIONS); AddItemWithStringId(DISABLE, IDS_EXTENSIONS_DISABLE); AddItemWithStringId(UNINSTALL, IDS_EXTENSIONS_UNINSTALL); AddSeparator(); - AddItemWithStringId(MANAGE, IDS_MANAGE_EXTENSIONS); + + if (extension_ && delegate_ && prefs && + prefs->GetBoolean(prefs::kExtensionsUIDeveloperMode)) { + AddSeparator(); + AddItemWithStringId(INSPECT_POPUP, IDS_EXTENSION_ACTION_INSPECT_POPUP); + } } ExtensionActionContextMenuModel::~ExtensionActionContextMenuModel() { @@ -52,6 +65,19 @@ bool ExtensionActionContextMenuModel::IsCommandIdEnabled(int command_id) const { // homepage url, so we just disable this menu item on those cases, at least // for now. return extension_->update_url().DomainIs("google.com"); + } else if (command_id == INSPECT_POPUP) { + if (!delegate_ || !extension_) + return false; + Browser* browser = BrowserList::GetLastActive(); + if (!browser) + return false; + TabContents* contents = browser->GetSelectedTabContents(); + if (!contents) + return false; + + // Different tabs can have different popups set. We need to make sure we + // only enable the menu item if the current tab has a popup. + return (extension_action_->HasPopup(ExtensionTabUtil::GetTabId(contents))); } return true; } @@ -97,6 +123,11 @@ void ExtensionActionContextMenuModel::ExecuteCommand(int command_id) { SINGLETON_TAB, PageTransition::LINK); break; } + case INSPECT_POPUP: { + if (delegate_) + delegate_->ShowPopupForDevToolsWindow(extension_, extension_action_); + break; + } default: NOTREACHED() << "Unknown option"; break; diff --git a/chrome/browser/extensions/extension_action_context_menu_model.h b/chrome/browser/extensions/extension_action_context_menu_model.h index 1a8e664..a35d269 100644 --- a/chrome/browser/extensions/extension_action_context_menu_model.h +++ b/chrome/browser/extensions/extension_action_context_menu_model.h @@ -9,6 +9,8 @@ #include "chrome/browser/extensions/extension_install_ui.h" class Extension; +class ExtensionAction; +class PrefService; // The menu model for the context menu for extension action icons (browser and // page actions). @@ -17,7 +19,25 @@ class ExtensionActionContextMenuModel public menus::SimpleMenuModel::Delegate, public ExtensionInstallUI::Delegate { public: - explicit ExtensionActionContextMenuModel(Extension* extension); + // Delegate to handle menu commands. + class MenuDelegate { + public: + // Called when the user selects the menu item which requests that the + // popup be shown and inspected. + virtual void ShowPopupForDevToolsWindow(Extension* extension, + ExtensionAction* extension_action) { + } + }; + + // |extension_action|, |prefs|, & |delegate| call all be NULL. If valid + // values are provided for all three, and prefs::kExtensionsUIDeveloperMode + // is enabled in the PrefService, a menu item will be shown for "Inspect + // Popup" which, when selected, will cause ShowPopupForDevToolsWindow() to be + // called on |delegate|. + ExtensionActionContextMenuModel(Extension* extension, + ExtensionAction* extension_action, + PrefService* prefs, + MenuDelegate* delegate); ~ExtensionActionContextMenuModel(); // SimpleMenuModel behavior overrides. @@ -35,6 +55,12 @@ class ExtensionActionContextMenuModel // The extension we are displaying the context menu for. Extension* extension_; + // The extension action we are displaying the context menu for. + ExtensionAction* extension_action_; + + // The delegate which handles the 'inspect popup' menu command. + MenuDelegate* delegate_; + DISALLOW_COPY_AND_ASSIGN(ExtensionActionContextMenuModel); }; diff --git a/chrome/browser/extensions/extension_dom_ui.h b/chrome/browser/extensions/extension_dom_ui.h index fd98355..54de39c 100644 --- a/chrome/browser/extensions/extension_dom_ui.h +++ b/chrome/browser/extensions/extension_dom_ui.h @@ -11,7 +11,6 @@ #include "chrome/browser/dom_ui/dom_ui.h" #include "chrome/browser/extensions/extension_bookmark_manager_api.h" #include "chrome/browser/extensions/extension_function_dispatcher.h" -#include "chrome/browser/extensions/extension_popup_host.h" #include "chrome/common/extensions/extension.h" class ListValue; @@ -25,7 +24,6 @@ class TabContents; // the main tab contents area. class ExtensionDOMUI : public DOMUI, - public ExtensionPopupHost::PopupDelegate, public ExtensionFunctionDispatcher::Delegate { public: explicit ExtensionDOMUI(TabContents* tab_contents); @@ -47,8 +45,6 @@ class ExtensionDOMUI virtual ExtensionDOMUI* GetExtensionDOMUI() { return this; } virtual gfx::NativeWindow GetFrameNativeWindow(); - // ExtensionPopupHost::Delegate - virtual Browser* GetBrowser() const { return GetBrowser(true); } virtual RenderViewHost* GetRenderViewHost(); virtual Profile* GetProfile(); virtual gfx::NativeView GetNativeViewOfHost(); diff --git a/chrome/browser/extensions/extension_function_dispatcher.cc b/chrome/browser/extensions/extension_function_dispatcher.cc index 1e6bee1..640725d 100644 --- a/chrome/browser/extensions/extension_function_dispatcher.cc +++ b/chrome/browser/extensions/extension_function_dispatcher.cc @@ -321,18 +321,6 @@ Browser* ExtensionFunctionDispatcher::GetBrowser(bool include_incognito) { return delegate_->GetBrowser(include_incognito); } -ExtensionPopupHost* ExtensionFunctionDispatcher::GetPopupHost() { - ExtensionHost* extension_host = GetExtensionHost(); - if (extension_host) { - DCHECK(!GetExtensionDOMUI()) << - "Function dispatcher registered in too many environments."; - return extension_host->popup_host(); - } else { - ExtensionDOMUI* dom_ui = GetExtensionDOMUI(); - return dom_ui->popup_host(); - } -} - ExtensionHost* ExtensionFunctionDispatcher::GetExtensionHost() { return delegate_->GetExtensionHost(); } diff --git a/chrome/browser/extensions/extension_function_dispatcher.h b/chrome/browser/extensions/extension_function_dispatcher.h index d87d22c..6fe4ba0 100644 --- a/chrome/browser/extensions/extension_function_dispatcher.h +++ b/chrome/browser/extensions/extension_function_dispatcher.h @@ -18,7 +18,6 @@ class Extension; class ExtensionDOMUI; class ExtensionFunction; class ExtensionHost; -class ExtensionPopupHost; class Profile; class RenderViewHost; class RenderViewHostDelegate; @@ -96,10 +95,6 @@ class ExtensionFunctionDispatcher { // we will fall back to a regular browser window or NULL if unavailable. Browser* GetBrowser(bool include_incognito); - // Get the extension popup hosting environment for the ExtensionHost - // or ExtensionDOMUI associted with this dispatcher. - ExtensionPopupHost* GetPopupHost(); - // Gets the ExtensionHost associated with this object. In the case of // tab hosted extension pages, this will return NULL. ExtensionHost* GetExtensionHost(); diff --git a/chrome/browser/extensions/extension_host.h b/chrome/browser/extensions/extension_host.h index 6d442fa..ad26e9f 100644 --- a/chrome/browser/extensions/extension_host.h +++ b/chrome/browser/extensions/extension_host.h @@ -10,7 +10,6 @@ #include "base/perftimer.h" #include "base/scoped_ptr.h" #include "chrome/browser/extensions/extension_function_dispatcher.h" -#include "chrome/browser/extensions/extension_popup_host.h" #include "chrome/browser/jsmessage_box_client.h" #include "chrome/browser/renderer_host/render_view_host_delegate.h" #include "chrome/browser/tab_contents/render_view_host_delegate_helper.h" @@ -37,8 +36,7 @@ struct WebPreferences; // It handles setting up the renderer process, if needed, with special // privileges available to extensions. It may have a view to be shown in the // in the browser UI, or it may be hidden. -class ExtensionHost : public ExtensionPopupHost::PopupDelegate, - public RenderViewHostDelegate, +class ExtensionHost : public RenderViewHostDelegate, public RenderViewHostDelegate::View, public ExtensionFunctionDispatcher::Delegate, public NotificationObserver, @@ -79,6 +77,10 @@ class ExtensionHost : public ExtensionPopupHost::PopupDelegate, bool document_element_available() const { return document_element_available_; } + gfx::NativeView GetNativeViewOfHost() { + return view() ? view()->native_view() : NULL; + } + Profile* profile() const { return profile_; } ViewType::Type extension_host_type() const { return extension_host_type_; } @@ -204,12 +206,8 @@ class ExtensionHost : public ExtensionPopupHost::PopupDelegate, virtual Browser* GetBrowser(bool include_incognito) const; virtual ExtensionHost* GetExtensionHost() { return this; } - // ExtensionPopupHost::Delegate virtual Browser* GetBrowser() const { return GetBrowser(true); } virtual RenderViewHost* GetRenderViewHost() { return render_view_host(); } - virtual gfx::NativeView GetNativeViewOfHost() { - return view() ? view()->native_view() : NULL; - } // Handles keyboard events that were not handled by HandleKeyboardEvent(). // Platform specific implementation may override this method to handle the diff --git a/chrome/browser/extensions/extension_popup_api.cc b/chrome/browser/extensions/extension_popup_api.cc index c70747d..504a7a07 100644 --- a/chrome/browser/extensions/extension_popup_api.cc +++ b/chrome/browser/extensions/extension_popup_api.cc @@ -21,9 +21,12 @@ #include "gfx/point.h" #if defined(TOOLKIT_VIEWS) +#include "chrome/browser/renderer_host/render_view_host.h" +#include "chrome/browser/renderer_host/render_widget_host_view.h" #include "chrome/browser/views/extensions/extension_popup.h" #include "views/view.h" -#endif +#include "views/focus/focus_manager.h" +#endif // TOOLKIT_VIEWS namespace extension_popup_module_events { @@ -53,6 +56,97 @@ const char kRectangleChrome[] = "rectangle"; }; // namespace +#if defined(TOOLKIT_VIEWS) +// ExtensionPopupHost objects implement the environment necessary to host +// an ExtensionPopup views for the popup api. Its main job is to handle +// its lifetime and to fire the popup-closed event when the popup is closed. +// Because the close-on-focus-lost behavior is different from page action +// and browser action, it also manages its own focus change listening. The +// difference in close-on-focus-lost is that in the page action and browser +// action cases, the popup closes when the focus leaves the popup or any of its +// children. In this case, the popup closes when the focus leaves the popups +// containing view or any of *its* children. +class ExtensionPopupHost : public ExtensionPopup::Observer, + public views::WidgetFocusChangeListener, + public base::RefCounted<ExtensionPopupHost> { + public: + explicit ExtensionPopupHost(ExtensionFunctionDispatcher* dispatcher) + : dispatcher_(dispatcher), popup_(NULL) { + AddRef(); // Balanced in ExtensionPopupClosed(). + views::FocusManager::GetWidgetFocusManager()->AddFocusChangeListener(this); + } + + ~ExtensionPopupHost() { + views::FocusManager::GetWidgetFocusManager()-> + RemoveFocusChangeListener(this); + } + + void set_popup(ExtensionPopup* popup) { + popup_ = popup; + } + + // Overriden from ExtensionPopup::Observer + virtual void ExtensionPopupClosed(ExtensionPopup* popup) { + RenderViewHost* render_view_host = dispatcher_->GetExtensionHost() ? + dispatcher_->GetExtensionHost()->render_view_host() : + dispatcher_->GetExtensionDOMUI()->GetRenderViewHost(); + + PopupEventRouter::OnPopupClosed(dispatcher_->profile(), + render_view_host->routing_id()); + dispatcher_ = NULL; + Release(); // Balanced in ctor. + } + + // Overriden from views::WidgetFocusChangeListener + virtual void NativeFocusWillChange(gfx::NativeView focused_before, + gfx::NativeView focused_now) { + // If no view is to be focused, then Chrome was deactivated, so hide the + // popup. + if (focused_now) { + gfx::NativeView host_view = dispatcher_->GetExtensionHost() ? + dispatcher_->GetExtensionHost()->GetNativeViewOfHost() : + dispatcher_->GetExtensionDOMUI()->GetNativeViewOfHost(); + + // If the widget hosting the popup contains the newly focused view, then + // don't dismiss the pop-up. + views::Widget* popup_root_widget = popup_->host()->view()->GetWidget(); + if (popup_root_widget && + popup_root_widget->ContainsNativeView(focused_now)) + return; + + // If the widget or RenderWidgetHostView hosting the extension that + // launched the pop-up is receiving focus, then don't dismiss the popup. + views::Widget* host_widget = + views::Widget::GetWidgetFromNativeView(host_view); + if (host_widget && host_widget->ContainsNativeView(focused_now)) + return; + + RenderWidgetHostView* render_host_view = + RenderWidgetHostView::GetRenderWidgetHostViewFromNativeView( + host_view); + if (render_host_view && + render_host_view->ContainsNativeView(focused_now)) + return; + } + + // We are careful here to let the current event loop unwind before + // causing the popup to be closed. + MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(popup_, + &ExtensionPopup::Close)); + } + + private: + // A pointer to the dispatcher that handled the request that opened this + // popup view. + ExtensionFunctionDispatcher* dispatcher_; + + // A pointer to the popup. + ExtensionPopup* popup_; + + DISALLOW_COPY_AND_ASSIGN(ExtensionPopupHost); +}; +#endif // TOOLKIT_VIEWS + PopupShowFunction::PopupShowFunction() #if defined (TOOLKIT_VIEWS) : popup_(NULL) @@ -157,19 +251,24 @@ bool PopupShowFunction::RunImpl() { BubbleBorder::ArrowLocation arrow_location = (NULL != dispatcher()->GetExtensionHost()) ? BubbleBorder::BOTTOM_LEFT : BubbleBorder::TOP_LEFT; - popup_ = ExtensionPopup::Show(url, GetBrowser(), + + // ExtensionPopupHost manages it's own lifetime. + ExtensionPopupHost* popup_host = new ExtensionPopupHost(dispatcher()); + popup_ = ExtensionPopup::Show(url, + GetBrowser(), dispatcher()->profile(), dispatcher()->GetFrameNativeWindow(), rect, arrow_location, give_focus, - chrome); - - ExtensionPopupHost* popup_host = dispatcher()->GetPopupHost(); - DCHECK(popup_host); - - popup_host->set_child_popup(popup_); - popup_->set_delegate(popup_host); + false, // inspect_with_devtools + chrome, + popup_host); // ExtensionPopup::Observer + + // popup_host will handle focus change listening and close the popup when + // focus leaves the containing views hierarchy. + popup_->set_close_on_lost_focus(false); + popup_host->set_popup(popup_); #endif // defined(TOOLKIT_VIEWS) return true; } diff --git a/chrome/browser/extensions/extension_popup_host.cc b/chrome/browser/extensions/extension_popup_host.cc deleted file mode 100644 index ec679e3..0000000 --- a/chrome/browser/extensions/extension_popup_host.cc +++ /dev/null @@ -1,202 +0,0 @@ -// Copyright (c) 2009 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/extensions/extension_popup_host.h" - -#include "base/message_loop.h" -#if defined(TOOLKIT_VIEWS) -#include "chrome/browser/extensions/extension_popup_api.h" -#endif -#include "chrome/browser/profile.h" -#include "chrome/browser/browser.h" -#include "chrome/browser/renderer_host/render_view_host.h" -#include "chrome/browser/renderer_host/render_widget_host_view.h" -#if defined(TOOLKIT_VIEWS) -#include "chrome/browser/views/extensions/extension_popup.h" -#endif -#include "chrome/common/notification_details.h" -#include "chrome/common/notification_source.h" -#include "chrome/common/notification_type.h" -#if defined(TOOLKIT_VIEWS) -#include "views/focus/focus_manager.h" -#include "views/widget/root_view.h" -#endif - -#if defined(TOOLKIT_VIEWS) -// A helper class that monitors native focus change events. Because the views -// FocusManager does not listen for view-change notification across -// native-windows, we need to use native-listener utilities. -class ExtensionPopupHost::PopupFocusListener - : public views::WidgetFocusChangeListener { - public: - // Constructs and registers a new PopupFocusListener for the given - // |popup_host|. - explicit PopupFocusListener(ExtensionPopupHost* popup_host) - : popup_host_(popup_host) { - views::FocusManager::GetWidgetFocusManager()->AddFocusChangeListener(this); - } - - virtual ~PopupFocusListener() { - views::FocusManager::GetWidgetFocusManager()-> - RemoveFocusChangeListener(this); - } - - virtual void NativeFocusWillChange(gfx::NativeView focused_before, - gfx::NativeView focused_now) { - // If no view is to be focused, then Chrome was deactivated, so hide the - // popup. - if (!focused_now) { - popup_host_->DismissPopupAsync(); - return; - } - - gfx::NativeView host_view = popup_host_->delegate()->GetNativeViewOfHost(); - - // If the widget hosting the popup contains the newly focused view, then - // don't dismiss the pop-up. - views::Widget* popup_root_widget = - popup_host_->child_popup()->host()->view()->GetWidget(); - if (popup_root_widget && - popup_root_widget->ContainsNativeView(focused_now)) { - return; - } - - // If the widget or RenderWidgetHostView hosting the extension that - // launched the pop-up is receiving focus, then don't dismiss the popup. - views::Widget* host_widget = - views::Widget::GetWidgetFromNativeView(host_view); - if (host_widget && host_widget->ContainsNativeView(focused_now)) { - return; - } - - RenderWidgetHostView* render_host_view = - RenderWidgetHostView::GetRenderWidgetHostViewFromNativeView( - host_view); - if (render_host_view && - render_host_view->ContainsNativeView(focused_now)) { - return; - } - - popup_host_->DismissPopupAsync(); - } - - private: - ExtensionPopupHost* popup_host_; - DISALLOW_COPY_AND_ASSIGN(PopupFocusListener); -}; -#endif // if defined(TOOLKIT_VIEWS) - - -ExtensionPopupHost::PopupDelegate::~PopupDelegate() { - // If the PopupDelegate is being torn down, then make sure to reset the - // cached pointer in the host to prevent access to a stale pointer. - if (popup_host_.get()) - popup_host_->RevokeDelegate(); -} - -ExtensionPopupHost* ExtensionPopupHost::PopupDelegate::popup_host() { - if (!popup_host_.get()) - popup_host_.reset(new ExtensionPopupHost(this)); - - return popup_host_.get(); -} - -Profile* ExtensionPopupHost::PopupDelegate::GetProfile() { - // If there is a browser present, return the profile associated with it. - // When hosting a view in an ExternalTabContainer, it is possible to have - // no Browser instance. - Browser* browser = GetBrowser(); - if (browser) { - return browser->profile(); - } - - return NULL; -} - -ExtensionPopupHost::ExtensionPopupHost(PopupDelegate* delegate) - : // NO LINT -#if defined(TOOLKIT_VIEWS) - listener_(NULL), - child_popup_(NULL), -#endif - delegate_(delegate), - ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)) { - DCHECK(delegate_); - - // Listen for view close requests, so that we can dismiss a hosted pop-up - // view, if necessary. - Profile* profile = delegate_->GetProfile(); - DCHECK(profile); - registrar_.Add(this, NotificationType::EXTENSION_HOST_VIEW_SHOULD_CLOSE, - Source<Profile>(profile)); -} - -ExtensionPopupHost::~ExtensionPopupHost() { - DismissPopup(); -} - -#if defined(TOOLKIT_VIEWS) -void ExtensionPopupHost::set_child_popup(ExtensionPopup* popup) { - // An extension may only have one popup active at a given time. - DismissPopup(); - if (popup) - listener_.reset(new PopupFocusListener(this)); - - child_popup_ = popup; -} - -void ExtensionPopupHost::BubbleBrowserWindowMoved(BrowserBubble* bubble) { - DismissPopupAsync(); -} - -void ExtensionPopupHost::BubbleBrowserWindowClosing(BrowserBubble* bubble) { - DismissPopupAsync(); -} -#endif // defined(TOOLKIT_VIEWS) - -void ExtensionPopupHost::Observe(NotificationType type, - const NotificationSource& source, - const NotificationDetails& details) { - if (type == NotificationType::EXTENSION_HOST_VIEW_SHOULD_CLOSE) { -#if defined(TOOLKIT_VIEWS) - // If we aren't the host of the popup, then disregard the notification. - if (!child_popup_ || - Details<ExtensionHost>(child_popup_->host()) != details) { - return; - } - DismissPopup(); -#endif - } else { - NOTREACHED(); - } -} - -void ExtensionPopupHost::DismissPopup() { -#if defined(TOOLKIT_VIEWS) - listener_.reset(NULL); - if (child_popup_) { - child_popup_->Hide(); - child_popup_->DetachFromBrowser(); - delete child_popup_; - child_popup_ = NULL; - - if (delegate_) { - PopupEventRouter::OnPopupClosed( - delegate_->GetProfile(), - delegate_->GetRenderViewHost()->routing_id()); - } - } -#endif // defined(TOOLKIT_VIEWS) -} - -void ExtensionPopupHost::DismissPopupAsync() { - // Dismiss the popup asynchronously, as we could be deep in a message loop - // processing activations, and the focus manager may get confused if the - // currently focused view is destroyed. - method_factory_.RevokeAll(); - MessageLoop::current()->PostNonNestableTask( - FROM_HERE, - method_factory_.NewRunnableMethod( - &ExtensionPopupHost::DismissPopup)); -} diff --git a/chrome/browser/extensions/extension_popup_host.h b/chrome/browser/extensions/extension_popup_host.h deleted file mode 100644 index 6f77b4d..0000000 --- a/chrome/browser/extensions/extension_popup_host.h +++ /dev/null @@ -1,113 +0,0 @@ -// Copyright (c) 2009 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. - -#ifndef CHROME_BROWSER_EXTENSIONS_EXTENSION_POPUP_HOST_H_ -#define CHROME_BROWSER_EXTENSIONS_EXTENSION_POPUP_HOST_H_ - -#include "base/scoped_ptr.h" -#include "base/task.h" -#include "build/build_config.h" -#if defined(TOOLKIT_VIEWS) -#include "chrome/browser/views/browser_bubble.h" -#endif -#include "chrome/common/notification_observer.h" -#include "chrome/common/notification_registrar.h" -#include "gfx/native_widget_types.h" - -#if defined(TOOLKIT_VIEWS) -class ExtensionPopup; -#endif - -class Browser; -class Profile; -class RenderViewHost; - -// ExtensionPopupHost objects implement the environment necessary to host -// ExtensionPopup views. This class manages the creation and life-time -// of extension pop-up views. -class ExtensionPopupHost : // NOLINT -#if defined(TOOLKIT_VIEWS) - public BrowserBubble::Delegate, -#endif - public NotificationObserver { - public: - // Classes wishing to host pop-ups should inherit from this class, and - // implement the virtual methods below. This class manages the lifetime - // of an ExtensionPopupHost instance. - class PopupDelegate { - public: - PopupDelegate() {} - virtual ~PopupDelegate(); - virtual Browser* GetBrowser() const = 0; - virtual RenderViewHost* GetRenderViewHost() = 0; - virtual gfx::NativeView GetNativeViewOfHost() = 0; - virtual Profile* GetProfile(); - - // Constructs, or returns the existing ExtensionPopupHost instance. - ExtensionPopupHost* popup_host(); - - private: - scoped_ptr<ExtensionPopupHost> popup_host_; - - DISALLOW_COPY_AND_ASSIGN(PopupDelegate); - }; - - explicit ExtensionPopupHost(PopupDelegate* delegate); - virtual ~ExtensionPopupHost(); - - PopupDelegate* delegate() { return delegate_; } - void RevokeDelegate() { delegate_ = NULL; } - - // Dismiss the hosted pop-up, if one is present. - void DismissPopup(); - -#if defined(TOOLKIT_VIEWS) - ExtensionPopup* child_popup() const { return child_popup_; } - void set_child_popup(ExtensionPopup* popup); - - // BrowserBubble::Delegate implementation. - // Called when the Browser Window that this bubble is attached to moves. - virtual void BubbleBrowserWindowMoved(BrowserBubble* bubble); - - // Called with the Browser Window that this bubble is attached to is - // about to close. - virtual void BubbleBrowserWindowClosing(BrowserBubble* bubble); -#endif // defined(TOOLKIT_VIEWS) - - // NotificationObserver implementation. - virtual void Observe(NotificationType type, - const NotificationSource& source, - const NotificationDetails& details); - - private: - // Posts a task to the current thread's message-loop that will dismiss the - // popup. - void DismissPopupAsync(); - -#if defined(TOOLKIT_VIEWS) - // A native-view focus listener that monitors when the pop-up should be - // dismissed due to focus change events. - class PopupFocusListener; - scoped_ptr<PopupFocusListener> listener_; - - // A popup view that is anchored to and owned by this ExtensionHost. However, - // the popup contains its own separate ExtensionHost - ExtensionPopup* child_popup_; -#endif - - NotificationRegistrar registrar_; - - // Non-owning pointer to the delegate for this host. - PopupDelegate* delegate_; - - // Boolean value used to ensure that the host only registers for event - // notifications once. - bool listeners_registered_; - - ScopedRunnableMethodFactory<ExtensionPopupHost> method_factory_; - - DISALLOW_COPY_AND_ASSIGN(ExtensionPopupHost); -}; - -#endif // CHROME_BROWSER_EXTENSIONS_EXTENSION_POPUP_HOST_H_ diff --git a/chrome/browser/gtk/browser_actions_toolbar_gtk.cc b/chrome/browser/gtk/browser_actions_toolbar_gtk.cc index 621a42f..5ab13c6 100644 --- a/chrome/browser/gtk/browser_actions_toolbar_gtk.cc +++ b/chrome/browser/gtk/browser_actions_toolbar_gtk.cc @@ -188,9 +188,12 @@ class BrowserActionButton : public NotificationObserver, if (event->button.button != 3) return FALSE; + // TODO(rafaelw): support inspecting popups. if (!action->context_menu_model_.get()) { action->context_menu_model_.reset( - new ExtensionActionContextMenuModel(action->extension_)); + new ExtensionActionContextMenuModel(action->extension_, + action->extension_->browser_action(), + action->toolbar_->browser()->profile()->GetPrefs(), NULL)); } action->context_menu_.reset( diff --git a/chrome/browser/gtk/location_bar_view_gtk.cc b/chrome/browser/gtk/location_bar_view_gtk.cc index 1d8c786..815bb2f 100644 --- a/chrome/browser/gtk/location_bar_view_gtk.cc +++ b/chrome/browser/gtk/location_bar_view_gtk.cc @@ -1217,8 +1217,10 @@ gboolean LocationBarViewGtk::PageActionViewGtk::OnButtonPressed( Extension* extension = profile_->GetExtensionsService()->GetExtensionById( page_action()->extension_id(), false); + // TODO(rafaelw): support inspecting popups. if (!context_menu_model_.get()) - context_menu_model_.reset(new ExtensionActionContextMenuModel(extension)); + context_menu_model_.reset(new ExtensionActionContextMenuModel(extension, + page_action_, profile_->GetPrefs(), NULL)); context_menu_.reset( new MenuGtk(NULL, context_menu_model_.get())); diff --git a/chrome/browser/views/browser_actions_container.cc b/chrome/browser/views/browser_actions_container.cc index a8fa362..32f0f02 100644 --- a/chrome/browser/views/browser_actions_container.cc +++ b/chrome/browser/views/browser_actions_container.cc @@ -13,6 +13,7 @@ #include "chrome/browser/browser_theme_provider.h" #include "chrome/browser/browser_window.h" #include "chrome/browser/extensions/extension_browser_event_router.h" +#include "chrome/browser/extensions/extension_host.h" #include "chrome/browser/extensions/extensions_service.h" #include "chrome/browser/extensions/extension_tabs_module.h" #include "chrome/browser/renderer_host/render_widget_host_view.h" @@ -133,9 +134,9 @@ gfx::Insets BrowserActionButton::GetInsets() const { return zero_inset; } -void BrowserActionButton::ButtonPressed( - views::Button* sender, const views::Event& event) { - panel_->OnBrowserActionExecuted(this); +void BrowserActionButton::ButtonPressed(views::Button* sender, + const views::Event& event) { + panel_->OnBrowserActionExecuted(this, false); // inspect_with_devtools } void BrowserActionButton::OnImageLoaded(SkBitmap* image, size_t index) { @@ -198,7 +199,7 @@ GURL BrowserActionButton::GetPopupUrl() { bool BrowserActionButton::Activate() { if (IsPopup()) { - panel_->OnBrowserActionExecuted(this); + panel_->OnBrowserActionExecuted(this, false); // inspect_with_devtools // TODO(erikkay): Run a nested modal loop while the mouse is down to // enable menu-like drag-select behavior. @@ -224,7 +225,8 @@ bool BrowserActionButton::OnMousePressed(const views::MouseEvent& e) { // Make the menu appear below the button. point.Offset(0, height()); - panel_->GetContextMenu()->Run(extension(), point); + panel_->GetContextMenu()->Run(extension(), extension()->browser_action(), + panel_, panel_->profile()->GetPrefs(), point); SetButtonNotPushed(); return false; @@ -340,9 +342,6 @@ BrowserActionsContainer::BrowserActionsContainer( if (!extension_service) // The |extension_service| can be NULL in Incognito. return; - registrar_.Add(this, NotificationType::EXTENSION_HOST_VIEW_SHOULD_CLOSE, - Source<Profile>(profile_)); - model_ = extension_service->toolbar_model(); model_->AddObserver(this); @@ -484,29 +483,13 @@ void BrowserActionsContainer::OnBrowserActionVisibilityChanged() { } void BrowserActionsContainer::HidePopup() { - if (popup_) { - // This sometimes gets called via a timer (See BubbleLostFocus), so clear - // the task factory in case one is pending. - task_factory_.RevokeAll(); - - // Save these variables in local temporaries since destroying the popup - // calls BubbleLostFocus to be called, which will try to call HidePopup() - // again if popup_ is non-null. - ExtensionPopup* closing_popup = popup_; - BrowserActionButton* closing_button = popup_button_; - popup_ = NULL; - popup_button_ = NULL; - - closing_popup->DetachFromBrowser(); - delete closing_popup; - closing_button->SetButtonNotPushed(); - return; - } + if (popup_) + popup_->Close(); } void BrowserActionsContainer::TestExecuteBrowserAction(int index) { BrowserActionButton* button = browser_action_views_[index]->button(); - OnBrowserActionExecuted(button); + OnBrowserActionExecuted(button, false); // inspect_with_devtools } void BrowserActionsContainer::TestSetIconVisibilityCount(size_t icons) { @@ -517,7 +500,7 @@ void BrowserActionsContainer::TestSetIconVisibilityCount(size_t icons) { } void BrowserActionsContainer::OnBrowserActionExecuted( - BrowserActionButton* button) { + BrowserActionButton* button, bool inspect_with_devtools) { ExtensionAction* browser_action = button->browser_action(); // Popups just display. No notification to the extension. @@ -548,18 +531,15 @@ void BrowserActionsContainer::OnBrowserActionExecuted( BubbleBorder::ArrowLocation arrow_location = UILayoutIsRightToLeft() ? BubbleBorder::TOP_LEFT : BubbleBorder::TOP_RIGHT; - popup_ = ExtensionPopup::Show( - button->GetPopupUrl(), - browser_, - browser_->profile(), - frame_window, - rect, - arrow_location, + popup_ = ExtensionPopup::Show(button->GetPopupUrl(), browser_, + browser_->profile(), frame_window, rect, arrow_location, true, // Activate the popup window. - ExtensionPopup::BUBBLE_CHROME); - popup_->set_delegate(this); + inspect_with_devtools, + ExtensionPopup::BUBBLE_CHROME, + this); // ExtensionPopupDelegate popup_button_ = button; popup_button_->SetButtonPushed(); + return; } @@ -809,54 +789,6 @@ void BrowserActionsContainer::MoveBrowserAction( SchedulePaint(); } -void BrowserActionsContainer::Observe(NotificationType type, - const NotificationSource& source, - const NotificationDetails& details) { - switch (type.value) { - case NotificationType::EXTENSION_HOST_VIEW_SHOULD_CLOSE: - // If we aren't the host of the popup, then disregard the notification. - if (!popup_ || Details<ExtensionHost>(popup_->host()) != details) - return; - - HidePopup(); - break; - - default: - NOTREACHED() << "Unexpected notification"; - } -} - -void BrowserActionsContainer::BubbleBrowserWindowMoved(BrowserBubble* bubble) { -} - -void BrowserActionsContainer::BubbleBrowserWindowClosing( - BrowserBubble* bubble) { - HidePopup(); -} - -void BrowserActionsContainer::BubbleGotFocus(BrowserBubble* bubble) { - if (!popup_) - return; - - // Forward the focus to the renderer. - popup_->host()->render_view_host()->view()->Focus(); -} - -void BrowserActionsContainer::BubbleLostFocus(BrowserBubble* bubble, - bool lost_focus_to_child) { - // Don't close when we are losing focus to a child window, this is the case - // for select popups and alert for example. - if (!popup_ || lost_focus_to_child) - return; - - // This is a bit annoying. If you click on the button that generated the - // current popup, then we first get this lost focus message, and then - // we get the click action. This results in the popup being immediately - // shown again. To workaround this, we put in a delay. - MessageLoop::current()->PostTask(FROM_HERE, - task_factory_.NewRunnableMethod(&BrowserActionsContainer::HidePopup)); -} - void BrowserActionsContainer::RunMenu(View* source, const gfx::Point& pt) { if (source == chevron_) { overflow_menu_ = new BrowserActionOverflowMenuController( @@ -1142,6 +1074,20 @@ void BrowserActionsContainer::NotifyMenuDeleted( overflow_menu_ = NULL; } +void BrowserActionsContainer::ShowPopupForDevToolsWindow(Extension* extension, + ExtensionAction* extension_action) { + OnBrowserActionExecuted(GetBrowserActionView(extension)->button(), + true); // inspect_with_devtools +} + +void BrowserActionsContainer::ExtensionPopupClosed(ExtensionPopup* popup) { + // ExtensionPopup is ref-counted, so we don't need to delete it. + DCHECK_EQ(popup_, popup); + popup_ = NULL; + popup_button_->SetButtonNotPushed(); + popup_button_ = NULL; +} + bool BrowserActionsContainer::ShouldDisplayBrowserAction(Extension* extension) { // Only display incognito-enabled extensions while in incognito mode. return (!profile_->IsOffTheRecord() || diff --git a/chrome/browser/views/browser_actions_container.h b/chrome/browser/views/browser_actions_container.h index 1452358..bd25f897 100644 --- a/chrome/browser/views/browser_actions_container.h +++ b/chrome/browser/views/browser_actions_container.h @@ -11,11 +11,13 @@ #include "app/slide_animation.h" #include "base/task.h" +#include "chrome/browser/extensions/extension_action_context_menu_model.h" #include "chrome/browser/extensions/extension_toolbar_model.h" #include "chrome/browser/extensions/image_loading_tracker.h" #include "chrome/browser/views/browser_bubble.h" #include "chrome/browser/views/extensions/browser_action_overflow_menu_controller.h" #include "chrome/browser/views/extensions/extension_action_context_menu.h" +#include "chrome/browser/views/extensions/extension_popup.h" #include "chrome/common/notification_observer.h" #include "chrome/common/notification_registrar.h" #include "views/controls/button/menu_button.h" @@ -225,14 +227,15 @@ class BrowserActionView : public views::View { //////////////////////////////////////////////////////////////////////////////// class BrowserActionsContainer : public views::View, - public NotificationObserver, - public BrowserBubble::Delegate, public views::ViewMenuDelegate, public views::DragController, public views::ResizeGripper::ResizeGripperDelegate, public AnimationDelegate, public ExtensionToolbarModel::Observer, - public BrowserActionOverflowMenuController::Observer { + public BrowserActionOverflowMenuController::Observer, + public ExtensionActionContextMenuModel::MenuDelegate, + public ExtensionPopup::Observer { + friend class ShowFolderMenuTask; public: BrowserActionsContainer(Browser* browser, views::View* owner_view); @@ -284,7 +287,8 @@ class BrowserActionsContainer size_t VisibleBrowserActions() const; // Called when the user clicks on the browser action icon. - void OnBrowserActionExecuted(BrowserActionButton* button); + void OnBrowserActionExecuted(BrowserActionButton* button, + bool inspect_with_devtools); // Overridden from views::View: virtual gfx::Size GetPreferredSize(); @@ -302,17 +306,6 @@ class BrowserActionsContainer virtual void OnDragExited(); virtual int OnPerformDrop(const views::DropTargetEvent& event); - // Overridden from NotificationObserver: - virtual void Observe(NotificationType type, - const NotificationSource& source, - const NotificationDetails& details); - - // BrowserBubble::Delegate methods. - virtual void BubbleBrowserWindowMoved(BrowserBubble* bubble); - virtual void BubbleBrowserWindowClosing(BrowserBubble* bubble); - virtual void BubbleGotFocus(BrowserBubble* bubble); - virtual void BubbleLostFocus(BrowserBubble* bubble, bool lost_focus_to_child); - // Overridden from views::ViewMenuDelegate: virtual void RunMenu(View* source, const gfx::Point& pt); @@ -336,6 +329,13 @@ class BrowserActionsContainer virtual void NotifyMenuDeleted( BrowserActionOverflowMenuController* controller); + // Overridden from ExtensionActionContextMenuModel::MenuDelegate + virtual void ShowPopupForDevToolsWindow(Extension* extension, + ExtensionAction* extension_action); + + // Overriden from ExtensionPopup::Delegate + virtual void ExtensionPopupClosed(ExtensionPopup* popup); + // Moves a browser action with |id| to |new_index|. void MoveBrowserAction(const std::string& extension_id, size_t new_index); @@ -418,8 +418,6 @@ class BrowserActionsContainer // this collection. Some extensions may be disabled in incognito windows. BrowserActionViews browser_action_views_; - NotificationRegistrar registrar_; - Profile* profile_; // The Browser object the container is associated with. diff --git a/chrome/browser/views/extensions/browser_action_overflow_menu_controller.cc b/chrome/browser/views/extensions/browser_action_overflow_menu_controller.cc index f0bb80d..dbee697 100644 --- a/chrome/browser/views/extensions/browser_action_overflow_menu_controller.cc +++ b/chrome/browser/views/extensions/browser_action_overflow_menu_controller.cc @@ -7,6 +7,7 @@ #include "app/gfx/canvas.h" #include "base/utf_string_conversions.h" #include "chrome/browser/browser_list.h" +#include "chrome/browser/profile.h" #include "chrome/browser/tab_contents/tab_contents.h" #include "chrome/browser/views/browser_actions_container.h" #include "chrome/browser/views/extensions/browser_action_drag_data.h" @@ -80,7 +81,8 @@ void BrowserActionOverflowMenuController::CancelMenu() { void BrowserActionOverflowMenuController::ExecuteCommand(int id) { BrowserActionView* view = (*views_)[start_index_ + id - 1]; - owner_->OnBrowserActionExecuted(view->button()); + owner_->OnBrowserActionExecuted(view->button(), + false); // inspect_with_devtools } bool BrowserActionOverflowMenuController::ShowContextMenu( @@ -90,7 +92,11 @@ bool BrowserActionOverflowMenuController::ShowContextMenu( bool is_mouse_gesture) { // This blocks until the user choses something or dismisses the menu. owner_->GetContextMenu()->Run( - (*views_)[start_index_ + id - 1]->button()->extension(), p); + (*views_)[start_index_ + id - 1]->button()->extension(), + (*views_)[start_index_ + id - 1]->button()->extension()->browser_action(), + owner_, + owner_->profile()->GetPrefs(), + p); // The user is done with the context menu, so we can close the underlying // menu. diff --git a/chrome/browser/views/extensions/extension_action_context_menu.cc b/chrome/browser/views/extensions/extension_action_context_menu.cc index 2e9ee7c..da7017d 100644 --- a/chrome/browser/views/extensions/extension_action_context_menu.cc +++ b/chrome/browser/views/extensions/extension_action_context_menu.cc @@ -17,11 +17,15 @@ ExtensionActionContextMenu::~ExtensionActionContextMenu() { } void ExtensionActionContextMenu::Run(Extension* extension, - const gfx::Point& point) { + ExtensionAction* extension_action, + ExtensionActionContextMenuModel::MenuDelegate* delegate, + PrefService* prefs, + const gfx::Point& point) { extension_ = extension; context_menu_contents_.reset( - new ExtensionActionContextMenuModel(extension)); + new ExtensionActionContextMenuModel(extension, extension_action, prefs, + delegate)); context_menu_menu_.reset(new views::Menu2(context_menu_contents_.get())); // This call blocks until the menu is dismissed or something is selected. context_menu_menu_->RunContextMenuAt(point); diff --git a/chrome/browser/views/extensions/extension_action_context_menu.h b/chrome/browser/views/extensions/extension_action_context_menu.h index 5340a389..8aec10a 100644 --- a/chrome/browser/views/extensions/extension_action_context_menu.h +++ b/chrome/browser/views/extensions/extension_action_context_menu.h @@ -10,6 +10,8 @@ #include "chrome/browser/extensions/extension_action_context_menu_model.h" class Extension; +class ExtensionAction; +class PrefService; // Displays the context menu for extension action icons (browser/page actions). class ExtensionActionContextMenu { @@ -18,7 +20,11 @@ class ExtensionActionContextMenu { ~ExtensionActionContextMenu(); // Display the context menu at a given point. - void Run(Extension* extension, const gfx::Point& point); + void Run(Extension* extension, + ExtensionAction* extension_action, + ExtensionActionContextMenuModel::MenuDelegate* delegate, + PrefService* prefs, + const gfx::Point& point); // Closes the context menu if open. void Cancel(); diff --git a/chrome/browser/views/extensions/extension_popup.cc b/chrome/browser/views/extensions/extension_popup.cc index c417e8e2..6755c0d 100644 --- a/chrome/browser/views/extensions/extension_popup.cc +++ b/chrome/browser/views/extensions/extension_popup.cc @@ -7,8 +7,12 @@ #include "chrome/browser/browser.h" #include "chrome/browser/browser_list.h" #include "chrome/browser/browser_window.h" -#include "chrome/browser/profile.h" +#include "chrome/browser/debugger/devtools_manager.h" +#include "chrome/browser/extensions/extension_host.h" #include "chrome/browser/extensions/extension_process_manager.h" +#include "chrome/browser/profile.h" +#include "chrome/browser/renderer_host/render_widget_host_view.h" +#include "chrome/browser/renderer_host/render_view_host.h" #include "chrome/browser/views/frame/browser_view.h" #include "chrome/common/extensions/extension.h" #include "chrome/common/notification_details.h" @@ -46,7 +50,9 @@ ExtensionPopup::ExtensionPopup(ExtensionHost* host, const gfx::Rect& relative_to, BubbleBorder::ArrowLocation arrow_location, bool activate_on_show, - PopupChrome chrome) + bool inspect_with_devtools, + PopupChrome chrome, + Observer* observer) : BrowserBubble(host->view(), frame, gfx::Point(), @@ -57,16 +63,28 @@ ExtensionPopup::ExtensionPopup(ExtensionHost* host, relative_to_(relative_to), extension_host_(host), activate_on_show_(activate_on_show), + inspect_with_devtools_(inspect_with_devtools), + close_on_lost_focus_(true), + closing_(false), border_widget_(NULL), border_(NULL), border_view_(NULL), popup_chrome_(chrome), + observer_(observer), anchor_position_(arrow_location) { + AddRef(); // Balanced in Close(); + set_delegate(this); host->view()->SetContainer(this); + + // We wait to show the popup until the contained host finishes loading. registrar_.Add(this, NotificationType::EXTENSION_HOST_DID_STOP_LOADING, Source<Profile>(host->profile())); + // Listen for the containing view calling window.close(); + registrar_.Add(this, NotificationType::EXTENSION_HOST_VIEW_SHOULD_CLOSE, + Source<Profile>(host->profile())); + // TODO(erikkay) Some of this border code is derived from InfoBubble. // We should see if we can unify these classes. @@ -167,16 +185,81 @@ void ExtensionPopup::ResizeToView() { } } +void ExtensionPopup::BubbleBrowserWindowMoved(BrowserBubble* bubble) { + if (!closing_) + Close(); + // TODO(rafaelw) -- the border must move as well. +} + +void ExtensionPopup::BubbleBrowserWindowClosing(BrowserBubble* bubble) { + if (!closing_) + Close(); +} + +void ExtensionPopup::BubbleGotFocus(BrowserBubble* bubble) { + // Forward the focus to the renderer. + host()->render_view_host()->view()->Focus(); +} + +void ExtensionPopup::BubbleLostFocus(BrowserBubble* bubble, + bool lost_focus_to_child) { + if (closing_ || // We are already closing. + inspect_with_devtools_ || // The popup is being inspected. + !close_on_lost_focus_ || // Our client is handling focus listening. + lost_focus_to_child) // A child of this view got focus. + return; + + // When we do close on BubbleLostFocus, we do it in the next event loop + // because a subsequent event in this loop may also want to close this popup + // and if so, we want to allow that. Example: Clicking the same browser + // action button that opened the popup. If we closed immediately, the + // browser action container would fail to discover that the same button + // was pressed. + MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(this, + &ExtensionPopup::Close)); +} + + void ExtensionPopup::Observe(NotificationType type, const NotificationSource& source, const NotificationDetails& details) { - if (type == NotificationType::EXTENSION_HOST_DID_STOP_LOADING) { - // Once we receive did stop loading, the content will be complete and - // the width will have been computed. Now it's safe to show. - if (extension_host_.get() == Details<ExtensionHost>(details).ptr()) - Show(activate_on_show_); - } else { - NOTREACHED() << L"Received unexpected notification"; + switch (type.value) { + case NotificationType::EXTENSION_HOST_DID_STOP_LOADING: + // Once we receive did stop loading, the content will be complete and + // the width will have been computed. Now it's safe to show. + if (extension_host_.get() == Details<ExtensionHost>(details).ptr()) { + Show(activate_on_show_); + + if (inspect_with_devtools_) { + // Listen for the the devtools window closing. + registrar_.Add(this, NotificationType::DEVTOOLS_WINDOW_CLOSING, + Source<Profile>(extension_host_->profile())); + DevToolsManager::GetInstance()->ToggleDevToolsWindow( + extension_host_->render_view_host(), true); + } + } + break; + case NotificationType::EXTENSION_HOST_VIEW_SHOULD_CLOSE: + // If we aren't the host of the popup, then disregard the notification. + if (Details<ExtensionHost>(host()) != details) + return; + Close(); + + 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) + return; + + // 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, NewRunnableMethod(this, + &ExtensionPopup::Close)); + + break; + default: + NOTREACHED() << L"Received unexpected notification"; } } @@ -232,7 +315,9 @@ ExtensionPopup* ExtensionPopup::Show( const gfx::Rect& relative_to, BubbleBorder::ArrowLocation arrow_location, bool activate_on_show, - PopupChrome chrome) { + bool inspect_with_devtools, + PopupChrome chrome, + Observer* observer) { DCHECK(profile); DCHECK(frame_window); ExtensionProcessManager* manager = profile->GetExtensionProcessManager(); @@ -253,7 +338,8 @@ ExtensionPopup* ExtensionPopup::Show( ExtensionHost* host = manager->CreatePopup(url, browser); ExtensionPopup* popup = new ExtensionPopup(host, frame_widget, relative_to, arrow_location, activate_on_show, - chrome); + inspect_with_devtools, chrome, + observer); // If the host had somehow finished loading, then we'd miss the notification // and not show. This seems to happen in single-process mode. @@ -262,3 +348,13 @@ ExtensionPopup* ExtensionPopup::Show( return popup; } + +void ExtensionPopup::Close() { + if (closing_) + return; + closing_ = true; + DetachFromBrowser(); + if (observer_) + observer_->ExtensionPopupClosed(this); + Release(); // Balanced in ctor. +} diff --git a/chrome/browser/views/extensions/extension_popup.h b/chrome/browser/views/extensions/extension_popup.h index 6969fc2..f44ace3 100644 --- a/chrome/browser/views/extensions/extension_popup.h +++ b/chrome/browser/views/extensions/extension_popup.h @@ -23,9 +23,20 @@ class Widget; } class ExtensionPopup : public BrowserBubble, + public BrowserBubble::Delegate, public NotificationObserver, - public ExtensionView::Container { + public ExtensionView::Container, + public base::RefCounted<ExtensionPopup> { public: + // Observer to ExtensionPopup events. + class Observer { + public: + // Called when the ExtensionPopup has closed. Note that it + // is ref-counted, and thus will be released shortly after + // making this delegate call. + virtual void ExtensionPopupClosed(ExtensionPopup* popup) {} + }; + enum PopupChrome { BUBBLE_CHROME, RECTANGLE_CHROME @@ -47,6 +58,8 @@ class ExtensionPopup : public BrowserBubble, // If |arrow_location| is BOTTOM_*, then the popup 'pops up', otherwise // the popup 'drops down'. // Pass |activate_on_show| as true to activate the popup window. + // Pass |inspect_with_devtools| as true to pin the popup open and show the + // devtools window for it. // The |chrome| argument controls the chrome that surrounds the pop-up. // Passing BUBBLE_CHROME will give the pop-up a bubble-like appearance, // including the arrow mentioned above. Passing RECTANGLE_CHROME will give @@ -62,7 +75,20 @@ class ExtensionPopup : public BrowserBubble, const gfx::Rect& relative_to, BubbleBorder::ArrowLocation arrow_location, bool activate_on_show, - PopupChrome chrome); + bool inspect_with_devtools, + PopupChrome chrome, + Observer* observer); + + // Closes the ExtensionPopup (this will cause the delegate + // ExtensionPopupClosed to fire. + void Close(); + + // Some clients wish to do their own custom focus change management. If this + // is set to false, then the ExtensionPopup will not do anything in response + // to the BubbleLostFocus() calls it gets from the BrowserBubble. + void set_close_on_lost_focus(bool close_on_lost_focus) { + close_on_lost_focus_ = close_on_lost_focus; + } ExtensionHost* host() const { return extension_host_.get(); } @@ -71,6 +97,13 @@ class ExtensionPopup : public BrowserBubble, virtual void Show(bool activate); virtual void ResizeToView(); + // BrowserBubble::Delegate methods. + virtual void BubbleBrowserWindowMoved(BrowserBubble* bubble); + virtual void BubbleBrowserWindowClosing(BrowserBubble* bubble); + virtual void BubbleGotFocus(BrowserBubble* bubble); + virtual void BubbleLostFocus(BrowserBubble* bubble, + bool lost_focus_to_child); + // NotificationObserver overrides. virtual void Observe(NotificationType type, const NotificationSource& source, @@ -93,7 +126,9 @@ class ExtensionPopup : public BrowserBubble, const gfx::Rect& relative_to, BubbleBorder::ArrowLocation arrow_location, bool activate_on_show, - PopupChrome chrome); + bool inspect_with_devtools, + PopupChrome chrome, + Observer* observer); // Gives the desired bounds (in screen coordinates) given the rect to point // to and the size of the contained contents. Includes all of the @@ -110,6 +145,16 @@ class ExtensionPopup : public BrowserBubble, // Flag used to indicate if the pop-up should be activated upon first display. bool activate_on_show_; + // Flag used to indicate if the pop-up should open a devtools window once + // it is shown inspecting it. + bool inspect_with_devtools_; + + // If false, ignore BrowserBubble::Delegate::BubbleLostFocus() calls. + bool close_on_lost_focus_; + + // Whether the ExtensionPopup is current going about closing itself. + bool closing_; + NotificationRegistrar registrar_; // A separate widget and associated pieces to draw a border behind the @@ -123,6 +168,9 @@ class ExtensionPopup : public BrowserBubble, // The type of chrome associated with the popup window. PopupChrome popup_chrome_; + // The observer of this popup. + Observer* observer_; + // A cached copy of the arrow-position for the bubble chrome. // If a black-border was requested, we still need this value to determine // the position of the pop-up in relation to |relative_to_|. diff --git a/chrome/browser/views/infobars/extension_infobar.cc b/chrome/browser/views/infobars/extension_infobar.cc index 70f3f7f..9303aff 100644 --- a/chrome/browser/views/infobars/extension_infobar.cc +++ b/chrome/browser/views/infobars/extension_infobar.cc @@ -89,10 +89,14 @@ void ExtensionInfoBar::Layout() { } void ExtensionInfoBar::RunMenu(View* source, const gfx::Point& pt) { - if (!options_menu_contents_.get()) { + if (!options_menu_contents_.get()) options_menu_contents_.reset(new ExtensionActionContextMenuModel( - delegate_->extension_host()->extension())); - } + delegate_->extension_host()->extension(), + // Do not include "Inspect Popup" in menu: + NULL, // ExtensionAction + NULL, // PrefService + NULL)); // ExtensionActionContextMenuModel::MenuDelegate + options_menu_menu_.reset(new views::Menu2(options_menu_contents_.get())); options_menu_menu_->RunMenuAt(pt, views::Menu2::ALIGN_TOPLEFT); } diff --git a/chrome/browser/views/location_bar_view.cc b/chrome/browser/views/location_bar_view.cc index 37c1759..98823bc 100644 --- a/chrome/browser/views/location_bar_view.cc +++ b/chrome/browser/views/location_bar_view.cc @@ -1427,8 +1427,7 @@ LocationBarView::PageActionImageView::PageActionImageView( tracker_(NULL), current_tab_id_(-1), preview_enabled_(false), - popup_(NULL), - ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)) { + popup_(NULL) { Extension* extension = profile->GetExtensionsService()->GetExtensionById( page_action->extension_id(), false); DCHECK(extension); @@ -1447,9 +1446,6 @@ LocationBarView::PageActionImageView::PageActionImageView( gfx::Size(Extension::kPageActionIconMaxSize, Extension::kPageActionIconMaxSize)); } - - registrar_.Add(this, NotificationType::EXTENSION_HOST_VIEW_SHOULD_CLOSE, - Source<Profile>(profile_)); } LocationBarView::PageActionImageView::~PageActionImageView() { @@ -1460,7 +1456,8 @@ LocationBarView::PageActionImageView::~PageActionImageView() { HidePopup(); } -void LocationBarView::PageActionImageView::ExecuteAction(int button) { +void LocationBarView::PageActionImageView::ExecuteAction(int button, + bool inspect_with_devtools) { if (current_tab_id_ < 0) { NOTREACHED() << "No current tab."; return; @@ -1500,8 +1497,9 @@ void LocationBarView::PageActionImageView::ExecuteAction(int button) { rect, BubbleBorder::TOP_RIGHT, true, // Activate the popup window. - ExtensionPopup::BUBBLE_CHROME); - popup_->set_delegate(this); + inspect_with_devtools, + ExtensionPopup::BUBBLE_CHROME, + this); // ExtensionPopup::Observer } else { ExtensionBrowserEventRouter::GetInstance()->PageActionExecuted( profile_, page_action_->extension_id(), page_action_->id(), @@ -1547,11 +1545,15 @@ void LocationBarView::PageActionImageView::OnMouseReleased( if (!context_menu_.get()) context_menu_.reset(new ExtensionActionContextMenu()); - context_menu_->Run(extension, point); + context_menu_->Run(extension, + extension->page_action(), + this, // ExtensionActionContextMenuModel::Delegate + profile_->GetPrefs(), + point); return; } - ExecuteAction(button); + ExecuteAction(button, false); // inspect_with_devtools } void LocationBarView::PageActionImageView::ShowInfoBubble() { @@ -1629,56 +1631,22 @@ void LocationBarView::PageActionImageView::UpdateVisibility( SetVisible(visible); } -void LocationBarView::PageActionImageView::BubbleBrowserWindowClosing( - BrowserBubble* bubble) { - HidePopup(); -} - -void LocationBarView::PageActionImageView::BubbleLostFocus( - BrowserBubble* bubble, bool lost_focus_to_child) { - // Don't close when we are losing focus to a child window, this is the case - // for select popups and alert for example. - if (!popup_ || lost_focus_to_child) - return; - - MessageLoop::current()->PostTask(FROM_HERE, - method_factory_.NewRunnableMethod( - &LocationBarView::PageActionImageView::HidePopup)); +void LocationBarView::PageActionImageView::ShowPopupForDevToolsWindow( + Extension* extension, ExtensionAction* extension_action) { + ExecuteAction(1, // left-click + true); // inspect_with_devtools } -void LocationBarView::PageActionImageView::HidePopup() { - if (!popup_) - return; - - // This sometimes gets called via a timer (See BubbleLostFocus), so clear - // the method factory. in case one is pending. - method_factory_.RevokeAll(); - - // Save the popup in a local since destroying it calls BubbleLostFocus, - // which will try to call HidePopup() again. - ExtensionPopup* closing_popup = popup_; +void LocationBarView::PageActionImageView::ExtensionPopupClosed( + ExtensionPopup* popup) { + DCHECK_EQ(popup_, popup); + // ExtensionPopup is ref-counted, so we don't need to delete it. popup_ = NULL; - - closing_popup->DetachFromBrowser(); - delete closing_popup; } -void LocationBarView::PageActionImageView::Observe( - NotificationType type, - const NotificationSource& source, - const NotificationDetails& details) { - switch (type.value) { - case NotificationType::EXTENSION_HOST_VIEW_SHOULD_CLOSE: - // If we aren't the host of the popup, then disregard the notification. - if (!popup_ || Details<ExtensionHost>(popup_->host()) != details) - return; - - HidePopup(); - break; - default: - NOTREACHED() << "Unexpected notification"; - break; - } +void LocationBarView::PageActionImageView::HidePopup() { + if (popup_) + popup_->Close(); } //////////////////////////////////////////////////////////////////////////////// @@ -1767,7 +1735,8 @@ void LocationBarView::TestPageActionPressed(size_t index) { if (page_action_views_[i]->IsVisible()) { if (current == index) { const int kLeftMouseButton = 1; - page_action_views_[i]->image_view()->ExecuteAction(kLeftMouseButton); + page_action_views_[i]->image_view()->ExecuteAction(kLeftMouseButton, + false); // inspect_with_devtools return; } ++current; diff --git a/chrome/browser/views/location_bar_view.h b/chrome/browser/views/location_bar_view.h index e3b4597..d591e97 100644 --- a/chrome/browser/views/location_bar_view.h +++ b/chrome/browser/views/location_bar_view.h @@ -18,6 +18,7 @@ #include "chrome/browser/toolbar_model.h" #include "chrome/browser/views/browser_bubble.h" #include "chrome/browser/views/extensions/extension_action_context_menu.h" +#include "chrome/browser/views/extensions/extension_popup.h" #include "chrome/browser/views/info_bubble.h" #include "chrome/common/content_settings_types.h" #include "chrome/common/notification_observer.h" @@ -409,9 +410,9 @@ class LocationBarView : public LocationBar, // PageActionImageView is used to display the icon for a given PageAction // and notify the extension when the icon is clicked. class PageActionImageView : public LocationBarImageView, - public ImageLoadingTracker::Observer, - public NotificationObserver, - public BrowserBubble::Delegate { + public ImageLoadingTracker::Observer, + public ExtensionActionContextMenuModel::MenuDelegate, + public ExtensionPopup::Observer { public: PageActionImageView(LocationBarView* owner, Profile* profile, @@ -438,10 +439,12 @@ class LocationBarView : public LocationBar, // Overridden from ImageLoadingTracker. virtual void OnImageLoaded(SkBitmap* image, size_t index); - // Overridden from BrowserBubble::Delegate - virtual void BubbleBrowserWindowClosing(BrowserBubble* bubble); - virtual void BubbleLostFocus(BrowserBubble* bubble, - bool lost_focus_to_child); + // Overridden from ExtensionActionContextMenuModel::MenuDelegate + virtual void ShowPopupForDevToolsWindow(Extension* extension, + ExtensionAction* extension_action); + + // Overriden from ExtensionPopup::Observer + virtual void ExtensionPopupClosed(ExtensionPopup* popup); // Called to notify the PageAction that it should determine whether to be // visible or hidden. |contents| is the TabContents that is active, |url| @@ -449,17 +452,12 @@ class LocationBarView : public LocationBar, void UpdateVisibility(TabContents* contents, const GURL& url); // Either notify listeners or show a popup depending on the page action. - void ExecuteAction(int button); + void ExecuteAction(int button, bool inspect_with_devtools); private: // Hides the active popup, if there is one. void HidePopup(); - // Overridden from NotificationObserver: - virtual void Observe(NotificationType type, - const NotificationSource& source, - const NotificationDetails& details); - // The location bar view that owns us. LocationBarView* owner_; @@ -497,10 +495,6 @@ class LocationBarView : public LocationBar, // The current popup and the button it came from. NULL if no popup. ExtensionPopup* popup_; - ScopedRunnableMethodFactory<PageActionImageView> method_factory_; - - NotificationRegistrar registrar_; - DISALLOW_COPY_AND_ASSIGN(PageActionImageView); }; friend class PageActionImageView; |