diff options
author | mad@chromium.org <mad@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-11-03 00:37:31 +0000 |
---|---|---|
committer | mad@chromium.org <mad@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-11-03 00:37:31 +0000 |
commit | 1c1c77a5021be0b902240a4f78009a8d8f71d1ac (patch) | |
tree | c1afb46b67de923afaac68340ddb0113b7306193 /chrome/browser/extensions | |
parent | 23b3f6c67270a6aff5020c9a7279f4ed84192a02 (diff) | |
download | chromium_src-1c1c77a5021be0b902240a4f78009a8d8f71d1ac.zip chromium_src-1c1c77a5021be0b902240a4f78009a8d8f71d1ac.tar.gz chromium_src-1c1c77a5021be0b902240a4f78009a8d8f71d1ac.tar.bz2 |
Submitting change from http://codereview.chromium.org/276029/show
BUG=none
TEST=none
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@30778 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/extensions')
-rw-r--r-- | chrome/browser/extensions/extension_function_dispatcher.cc | 4 | ||||
-rw-r--r-- | chrome/browser/extensions/extension_host.cc | 63 | ||||
-rw-r--r-- | chrome/browser/extensions/extension_host.h | 45 | ||||
-rw-r--r-- | chrome/browser/extensions/extension_popup_api.cc | 164 | ||||
-rw-r--r-- | chrome/browser/extensions/extension_popup_api.h | 50 | ||||
-rw-r--r-- | chrome/browser/extensions/extension_popup_apitest.cc | 13 |
6 files changed, 336 insertions, 3 deletions
diff --git a/chrome/browser/extensions/extension_function_dispatcher.cc b/chrome/browser/extensions/extension_function_dispatcher.cc index d7105cb..a3f8a4c 100644 --- a/chrome/browser/extensions/extension_function_dispatcher.cc +++ b/chrome/browser/extensions/extension_function_dispatcher.cc @@ -17,6 +17,7 @@ #include "chrome/browser/extensions/extension_message_service.h" #include "chrome/browser/extensions/extension_page_actions_module.h" #include "chrome/browser/extensions/extension_page_actions_module_constants.h" +#include "chrome/browser/extensions/extension_popup_api.h" #include "chrome/browser/extensions/extension_process_manager.h" #include "chrome/browser/extensions/extension_tabs_module.h" #include "chrome/browser/extensions/extension_tabs_module_constants.h" @@ -139,6 +140,9 @@ void FactoryRegistry::ResetFunctions() { // I18N. RegisterFunction<GetAcceptLanguagesFunction>(); + // Popup API. + RegisterFunction<PopupShowFunction>(); + // Test. RegisterFunction<ExtensionTestPassFunction>(); RegisterFunction<ExtensionTestFailFunction>(); diff --git a/chrome/browser/extensions/extension_host.cc b/chrome/browser/extensions/extension_host.cc index 2f94612..9435913 100644 --- a/chrome/browser/extensions/extension_host.cc +++ b/chrome/browser/extensions/extension_host.cc @@ -18,12 +18,16 @@ #include "chrome/browser/dom_ui/dom_ui_factory.h" #include "chrome/browser/extensions/extension_message_service.h" #include "chrome/browser/extensions/extension_tabs_module.h" +#include "chrome/browser/extensions/extension_popup_api.h" #include "chrome/browser/profile.h" #include "chrome/browser/renderer_host/render_view_host.h" #include "chrome/browser/renderer_host/render_process_host.h" #include "chrome/browser/renderer_host/render_widget_host.h" #include "chrome/browser/renderer_host/render_widget_host_view.h" #include "chrome/browser/renderer_host/site_instance.h" +#if defined(TOOLKIT_VIEWS) +#include "chrome/browser/views/extensions/extension_popup.h" +#endif #include "chrome/common/bindings_policy.h" #include "chrome/common/extensions/extension.h" #include "chrome/common/notification_service.h" @@ -111,14 +115,25 @@ ExtensionHost::ExtensionHost(Extension* extension, SiteInstance* site_instance, did_stop_loading_(false), document_element_available_(false), url_(url), +#if defined(TOOLKIT_VIEWS) + child_popup_(NULL), +#endif extension_host_type_(host_type) { render_view_host_ = new RenderViewHost(site_instance, this, MSG_ROUTING_NONE); render_view_host_->AllowBindings(BindingsPolicy::EXTENSION); if (enable_dom_automation_) render_view_host_->AllowBindings(BindingsPolicy::DOM_AUTOMATION); + +#if defined(TOOLKIT_VIEWS) + // Listen for view close requests, so that we can dismiss a hosted pop-up + // view, if necessary. + registrar_.Add(this, NotificationType::EXTENSION_HOST_VIEW_SHOULD_CLOSE, + Source<Profile>(profile_)); +#endif } ExtensionHost::~ExtensionHost() { + DismissPopup(); NotificationService::current()->Notify( NotificationType::EXTENSION_HOST_DESTROYED, Source<Profile>(profile_), @@ -208,6 +223,15 @@ void ExtensionHost::Observe(NotificationType type, NavigateToURL(url_); } else if (type == NotificationType::BROWSER_THEME_CHANGED) { InsertThemeCSS(); +#if defined(TOOLKIT_VIEWS) + } else if (type == NotificationType::EXTENSION_HOST_VIEW_SHOULD_CLOSE) { + // 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(); } @@ -294,6 +318,38 @@ void ExtensionHost::InsertThemeCSS() { render_view_host()->InsertCSSInWebFrame(L"", css, "ToolstripThemeCSS"); } +void ExtensionHost::DismissPopup() { +#if defined(TOOLKIT_VIEWS) + if (child_popup_) { + child_popup_->Hide(); + child_popup_->DetachFromBrowser(); + delete child_popup_; + child_popup_ = NULL; + + PopupEventRouter::OnPopupClosed(GetBrowser()->profile(), + view()->render_view_host()->routing_id()); + } +#endif +} + +#if defined(TOOLKIT_VIEWS) +void ExtensionHost::BubbleBrowserWindowMoved(BrowserBubble* bubble) { + DismissPopup(); +} +void ExtensionHost::BubbleBrowserWindowClosing(BrowserBubble* bubble) { + DismissPopup(); +} + +void ExtensionHost::BubbleGotFocus(BrowserBubble* bubble) { +} + +void ExtensionHost::BubbleLostFocus(BrowserBubble* bubble) { + // TODO(twiz): Dismiss the pop-up upon loss of focus of the bubble, but not + // if the focus is transitioning to the host which owns the popup! + // DismissPopup(); +} +#endif // defined(TOOLKIT_VIEWS) + void ExtensionHost::DidStopLoading() { bool notify = !did_stop_loading_; did_stop_loading_ = true; @@ -325,6 +381,13 @@ void ExtensionHost::DocumentAvailableInMainFrame(RenderViewHost* rvh) { registrar_.Add(this, NotificationType::BROWSER_THEME_CHANGED, NotificationService::AllSources()); } + + if (ViewType::EXTENSION_POPUP == GetRenderViewType()) { + NotificationService::current()->Notify( + NotificationType::EXTENSION_POPUP_VIEW_READY, + Source<Profile>(profile_), + Details<ExtensionHost>(this)); + } } void ExtensionHost::RunJavaScriptMessage(const std::wstring& message, diff --git a/chrome/browser/extensions/extension_host.h b/chrome/browser/extensions/extension_host.h index ea9660d..bf66579 100644 --- a/chrome/browser/extensions/extension_host.h +++ b/chrome/browser/extensions/extension_host.h @@ -12,6 +12,7 @@ #include "chrome/browser/renderer_host/render_view_host_delegate.h" #include "chrome/browser/tab_contents/render_view_host_delegate_helper.h" #if defined(TOOLKIT_VIEWS) +#include "chrome/browser/views/browser_bubble.h" #include "chrome/browser/views/extensions/extension_view.h" #elif defined(OS_LINUX) #include "chrome/browser/gtk/extension_view_gtk.h" @@ -20,8 +21,12 @@ #endif #include "chrome/common/notification_registrar.h" + class Browser; class Extension; +#if defined(TOOLKIT_VIEWS) +class ExtensionPopup; +#endif class ExtensionProcessManager; class RenderProcessHost; class RenderWidgetHost; @@ -33,7 +38,11 @@ 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 RenderViewHostDelegate, +class ExtensionHost : // NOLINT +#if defined(TOOLKIT_VIEWS) + public BrowserBubble::Delegate, +#endif + public RenderViewHostDelegate, public RenderViewHostDelegate::View, public ExtensionFunctionDispatcher::Delegate, public NotificationObserver { @@ -72,6 +81,14 @@ class ExtensionHost : public RenderViewHostDelegate, } Profile* profile() const { return profile_; } +#if defined(TOOLKIT_VIEWS) + ExtensionPopup* child_popup() const { return child_popup_; } + void set_child_popup(ExtensionPopup* popup) { child_popup_ = popup; } +#endif + + // Dismiss the hosted pop-up, if one is present. + void DismissPopup(); + // Sets the the ViewType of this host (e.g. mole, toolstrip). void SetRenderViewType(ViewType::Type type); @@ -89,6 +106,22 @@ class ExtensionHost : public RenderViewHostDelegate, // Insert the theme CSS for a toolstrip/mole. void InsertThemeCSS(); +#if defined(TOOLKIT_VIEWS) + // 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); + + // Called when the bubble became active / got focus. + virtual void BubbleGotFocus(BrowserBubble* bubble); + + // Called when the bubble became inactive / lost focus. + virtual void BubbleLostFocus(BrowserBubble* bubble); +#endif // defined(TOOLKIT_VIEWS) + // RenderViewHostDelegate implementation. virtual RenderViewHostDelegate::View* GetViewDelegate(); virtual const GURL& GetURL() const { return url_; } @@ -192,12 +225,18 @@ class ExtensionHost : public RenderViewHostDelegate, // The URL being hosted. GURL url_; +#if defined(TOOLKIT_VIEWS) + // 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_; scoped_ptr<ExtensionFunctionDispatcher> extension_function_dispatcher_; - // Only EXTENSION_TOOLSTRIP and EXTENSION_BACKGROUND_PAGE are used here, - // others are not hostd by ExtensionHost. + // Only EXTENSION_TOOLSTRIP, EXTENSION_POPUP, and EXTENSION_BACKGROUND_PAGE + // are used here, others are not hosted by ExtensionHost. ViewType::Type extension_host_type_; DISALLOW_COPY_AND_ASSIGN(ExtensionHost); diff --git a/chrome/browser/extensions/extension_popup_api.cc b/chrome/browser/extensions/extension_popup_api.cc new file mode 100644 index 0000000..3059079 --- /dev/null +++ b/chrome/browser/extensions/extension_popup_api.cc @@ -0,0 +1,164 @@ +// 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_api.h" + +#include "base/gfx/point.h" +#include "base/json/json_writer.h" +#include "base/string_util.h" +#include "chrome/common/extensions/extension.h" +#include "chrome/common/notification_details.h" +#include "chrome/common/notification_service.h" +#include "chrome/common/notification_source.h" +#include "chrome/common/notification_type.h" +#include "chrome/browser/extensions/extension_host.h" +#include "chrome/browser/extensions/extension_message_service.h" +#include "chrome/browser/browser.h" +#include "chrome/browser/profile.h" +#if defined(TOOLKIT_VIEWS) +#include "chrome/browser/views/extensions/extension_popup.h" +#include "views/view.h" +#endif + +namespace extension_popup_module_events { + +const char kOnPopupClosed[] = "experimental.popup.onClosed.%d"; + +} // namespace extension_popup_module_events + +namespace { + +// Errors. +const char kBadAnchorArgument[] = "Invalid anchor argument."; +const char kInvalidURLError[] = "Invalid URL."; + +// Keys. +const wchar_t kUrlKey[] = L"url"; +const wchar_t kWidthKey[] = L"width"; +const wchar_t kHeightKey[] = L"height"; +const wchar_t kTopKey[] = L"top"; +const wchar_t kLeftKey[] = L"left"; + +}; // namespace + +PopupShowFunction::PopupShowFunction() +#if defined (TOOLKIT_VIEWS) + : popup_(NULL) +#endif +{} + +void PopupShowFunction::Run() { +#if defined(TOOLKIT_VIEWS) + if (!RunImpl()) { + SendResponse(false); + } else { + // If the contents of the popup are already available, then immediately + // send the response. Otherwise wait for the EXTENSION_POPUP_VIEW_READY + // notification. + if (popup_->host() && popup_->host()->document_element_available()) { + SendResponse(true); + } else { + AddRef(); + registrar_.Add(this, NotificationType::EXTENSION_POPUP_VIEW_READY, + NotificationService::AllSources()); + registrar_.Add(this, NotificationType::EXTENSION_HOST_DESTROYED, + NotificationService::AllSources()); + } + } +#else + SendResponse(false); +#endif +} + +bool PopupShowFunction::RunImpl() { + EXTENSION_FUNCTION_VALIDATE(args_->IsType(Value::TYPE_LIST)); + const ListValue* args = static_cast<const ListValue*>(args_); + + DictionaryValue* popup_info = NULL; + EXTENSION_FUNCTION_VALIDATE(args->GetDictionary(0, &popup_info)); + + std::string url_string; + EXTENSION_FUNCTION_VALIDATE(popup_info->GetString(kUrlKey, + &url_string)); + + DictionaryValue* dom_anchor = NULL; + EXTENSION_FUNCTION_VALIDATE(args->GetDictionary(1, &dom_anchor)); + + int dom_top, dom_left; + EXTENSION_FUNCTION_VALIDATE(dom_anchor->GetInteger(kTopKey, + &dom_top)); + EXTENSION_FUNCTION_VALIDATE(dom_anchor->GetInteger(kLeftKey, + &dom_left)); + + int dom_width, dom_height; + EXTENSION_FUNCTION_VALIDATE(dom_anchor->GetInteger(kWidthKey, + &dom_width)); + EXTENSION_FUNCTION_VALIDATE(dom_anchor->GetInteger(kHeightKey, + &dom_height)); + EXTENSION_FUNCTION_VALIDATE(dom_top >= 0 && dom_left >= 0 && + dom_width >= 0 && dom_height >= 0); + + GURL url = dispatcher()->url().Resolve(url_string); + if (!url.is_valid()) { + error_ = kInvalidURLError; + return false; + } + + // Disallow non-extension requests, or requests outside of the requesting + // extension view's extension. + const std::string& extension_id = url.host(); + if (extension_id != dispatcher()->GetExtension()->id() || + !url.SchemeIs("chrome-extension")) { + error_ = kInvalidURLError; + return false; + } + +#if defined(TOOLKIT_VIEWS) + views::View* extension_view = dispatcher()->GetExtensionHost()->view(); + gfx::Point origin(dom_left, dom_top); + views::View::ConvertPointToScreen(extension_view, &origin); + gfx::Rect rect(origin.x(), origin.y(), dom_width, dom_height); + + popup_ = ExtensionPopup::Show(url, dispatcher()->GetBrowser(), rect, + BubbleBorder::BOTTOM_LEFT); + + dispatcher()->GetExtensionHost()->set_child_popup(popup_); + popup_->set_delegate(dispatcher()->GetExtensionHost()); +#endif + return true; +} + +void PopupShowFunction::Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details) { +#if defined(TOOLKIT_VIEWS) + DCHECK(type == NotificationType::EXTENSION_POPUP_VIEW_READY || + type == NotificationType::EXTENSION_HOST_DESTROYED); + DCHECK(popup_ != NULL); + + if (popup_ && type == NotificationType::EXTENSION_POPUP_VIEW_READY && + Details<ExtensionHost>(popup_->host()) == details) { + SendResponse(true); + Release(); // Balanced in Run(). + } else if (popup_ && type == NotificationType::EXTENSION_HOST_DESTROYED && + Details<ExtensionHost>(popup_->host()) == details) { + // If the host was destroyed, then report failure, and release the remaining + // reference. + SendResponse(false); + Release(); // Balanced in Run(). + } +#endif +} + +// static +void PopupEventRouter::OnPopupClosed(Profile* profile, + int routing_id) { + std::string full_event_name = StringPrintf( + extension_popup_module_events::kOnPopupClosed, + routing_id); + + profile->GetExtensionMessageService()->DispatchEventToRenderers( + full_event_name, + base::JSONWriter::kEmptyArray); +} diff --git a/chrome/browser/extensions/extension_popup_api.h b/chrome/browser/extensions/extension_popup_api.h new file mode 100644 index 0000000..96d6d53 --- /dev/null +++ b/chrome/browser/extensions/extension_popup_api.h @@ -0,0 +1,50 @@ +// 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_API_H_ +#define CHROME_BROWSER_EXTENSIONS_EXTENSION_POPUP_API_H_ + +#include "chrome/browser/extensions/extension_function.h" +#include "chrome/common/notification_registrar.h" + +class Profile; +class ExtensionPopup; + +// This extension function shows a pop-up extension view. It is asynchronous +// because the callback must be invoked only after the associated render +// process/view has been created and fully initialized. +class PopupShowFunction : public AsyncExtensionFunction, + public NotificationObserver { + public: + PopupShowFunction(); + + virtual void Run(); + virtual bool RunImpl(); + DECLARE_EXTENSION_FUNCTION_NAME("experimental.popup.show") + + private: + // NotificationObserver methods. + virtual void Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details); + NotificationRegistrar registrar_; + +#if defined(TOOLKIT_VIEWS) + // The pop-up view created by this function, saved for access during + // event notification. The pop-up is not owned by the PopupShowFunction + // instance. + ExtensionPopup* popup_; +#endif +}; + +// Event router class for events related to the chrome.popup.* set of APIs. +class PopupEventRouter { + public: + static void OnPopupClosed(Profile* profile, + int routing_id); + private: + DISALLOW_COPY_AND_ASSIGN(PopupEventRouter); +}; + +#endif // CHROME_BROWSER_EXTENSIONS_EXTENSION_POPUP_API_H_ diff --git a/chrome/browser/extensions/extension_popup_apitest.cc b/chrome/browser/extensions/extension_popup_apitest.cc new file mode 100644 index 0000000..cfd8404 --- /dev/null +++ b/chrome/browser/extensions/extension_popup_apitest.cc @@ -0,0 +1,13 @@ +// 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 "base/command_line.h" +#include "chrome/browser/extensions/extension_apitest.h" +#include "chrome/common/chrome_switches.h" + +IN_PROC_BROWSER_TEST_F(ExtensionApiTest, Popup) { + CommandLine::ForCurrentProcess()->AppendSwitch( + switches::kEnableExperimentalExtensionApis); + ASSERT_TRUE(RunExtensionTest("popup_api")) << message_; +} |