diff options
Diffstat (limited to 'chrome/browser/extensions/extension_popup_api.cc')
-rw-r--r-- | chrome/browser/extensions/extension_popup_api.cc | 555 |
1 files changed, 0 insertions, 555 deletions
diff --git a/chrome/browser/extensions/extension_popup_api.cc b/chrome/browser/extensions/extension_popup_api.cc deleted file mode 100644 index 0c3acb2..0000000 --- a/chrome/browser/extensions/extension_popup_api.cc +++ /dev/null @@ -1,555 +0,0 @@ -// Copyright (c) 2011 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 <string> - -#include "base/json/json_writer.h" -#include "base/string_util.h" -#include "base/stringprintf.h" -#include "base/values.h" -#include "chrome/browser/extensions/extension_event_router.h" -#include "chrome/browser/extensions/extension_host.h" -#include "chrome/browser/extensions/extension_web_ui.h" -#include "chrome/browser/profiles/profile.h" -#include "chrome/browser/renderer_host/render_view_host.h" -#include "chrome/browser/renderer_host/render_view_host_delegate.h" -#include "chrome/browser/renderer_host/render_widget_host_view.h" -#include "chrome/browser/ui/browser.h" -#include "chrome/browser/ui/browser_window.h" -#include "chrome/browser/ui/window_sizer.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/common/url_constants.h" -#include "ui/gfx/point.h" - -#if defined(TOOLKIT_VIEWS) -#include "chrome/browser/ui/views/bubble_border.h" -#include "chrome/browser/ui/views/extensions/extension_popup.h" -#include "views/view.h" -#include "views/focus/focus_manager.h" -#endif // TOOLKIT_VIEWS - -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."; -const char kNotAnExtension[] = "Not an extension view."; -const char kPopupsDisallowed[] = - "Popups are only supported from tab-contents views."; - -// Keys. -const char kWidthKey[] = "width"; -const char kHeightKey[] = "height"; -const char kTopKey[] = "top"; -const char kLeftKey[] = "left"; -const char kGiveFocusKey[] = "giveFocus"; -const char kDomAnchorKey[] = "domAnchor"; -const char kBorderStyleKey[] = "borderStyle"; -const char kMaxSizeKey[] = "maxSize"; - -// chrome enumeration values -const char kRectangleChrome[] = "rectangle"; - -#if defined(TOOLKIT_VIEWS) -// Returns an updated arrow location, conditioned on the type of intersection -// between the popup window, and the screen. |location| is the current position -// of the arrow on the popup. |intersection| is the rect representing the -// intersection between the popup view and its working screen. |popup_rect| -// is the rect of the popup window in screen space coordinates. -// The returned location will be horizontally or vertically inverted based on -// if the popup has been clipped horizontally or vertically. -BubbleBorder::ArrowLocation ToggleArrowLocation( - BubbleBorder::ArrowLocation location, const gfx::Rect& intersection, - const gfx::Rect& popup_rect) { - // If the popup has been clipped horizontally, flip the right-left position - // of the arrow. - if (intersection.right() != popup_rect.right() || - intersection.x() != popup_rect.x()) { - location = BubbleBorder::horizontal_mirror(location); - } - - // If the popup has been clipped vertically, flip the bottom-top position - // of the arrow. - if (intersection.y() != popup_rect.y() || - intersection.bottom() != popup_rect.bottom()) { - location = BubbleBorder::vertical_mirror(location); - } - - return location; -} -#endif // TOOLKIT_VIEWS - -}; // 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 NotificationObserver { - public: - // Pass |max_popup_size| to specify the maximal size to which the popup - // will expand. A width or height of 0 will result in the popup making use - // of the default max width or height, respectively: ExtensionPopup:kMaxWidth, - // and ExtensionPopup::kMaxHeight. - explicit ExtensionPopupHost(ExtensionFunctionDispatcher* dispatcher, - const gfx::Size& max_popup_size) - : dispatcher_(dispatcher), popup_(NULL), max_popup_size_(max_popup_size) { - AddRef(); // Balanced in DispatchPopupClosedEvent(). - views::FocusManager::GetWidgetFocusManager()->AddFocusChangeListener(this); - } - - ~ExtensionPopupHost() { - views::FocusManager::GetWidgetFocusManager()-> - RemoveFocusChangeListener(this); - } - - void set_popup(ExtensionPopup* popup) { - popup_ = popup; - - // Now that a popup has been assigned, listen for subsequent popups being - // created in the same extension - we want to disallow more than one - // concurrently displayed popup windows. - registrar_.Add( - this, - NotificationType::EXTENSION_HOST_CREATED, - Source<ExtensionProcessManager>( - dispatcher_->profile()->GetExtensionProcessManager())); - - registrar_.Add( - this, - NotificationType::RENDER_VIEW_HOST_WILL_CLOSE_RENDER_VIEW, - Source<RenderViewHost>(dispatcher_->render_view_host())); - - registrar_.Add( - this, - NotificationType::EXTENSION_FUNCTION_DISPATCHER_DESTROYED, - Source<Profile>(dispatcher_->profile())); - } - - // Overridden from ExtensionPopup::Observer - virtual void ExtensionPopupIsClosing(ExtensionPopup* popup) { - // Unregister the automation resource routing registered upon host - // creation. - AutomationResourceRoutingDelegate* router = - GetRoutingFromDispatcher(dispatcher_); - if (router) - router->UnregisterRenderViewHost(popup_->host()->render_view_host()); - } - - virtual void ExtensionPopupClosed(void* popup_token) { - if (popup_ == popup_token) { - popup_ = NULL; - DispatchPopupClosedEvent(); - } - } - - virtual void ExtensionHostCreated(ExtensionHost* host) { - // Pop-up views should share the same automation routing configuration as - // their hosting views, so register the RenderViewHost of the pop-up with - // the AutomationResourceRoutingDelegate interface of the dispatcher. - AutomationResourceRoutingDelegate* router = - GetRoutingFromDispatcher(dispatcher_); - if (router) - router->RegisterRenderViewHost(host->render_view_host()); - - // Extension hosts created for popup contents exist in the same tab - // contents as the ExtensionFunctionDispatcher that requested the popup. - // For example, '_blank' link navigation should be routed through the tab - // contents that requested the popup. - if (dispatcher_ && dispatcher_->delegate()) { - host->set_associated_tab_contents( - dispatcher_->delegate()->associated_tab_contents()); - } - } - - virtual void ExtensionPopupCreated(ExtensionPopup* popup) { - // The popup has been created, but not yet displayed, so install the max - // size overrides before the first positioning. - if (max_popup_size_.width()) - popup->set_max_width(max_popup_size_.width()); - - if (max_popup_size_.height()) - popup->set_max_height(max_popup_size_.height()); - } - - virtual void ExtensionPopupResized(ExtensionPopup* popup) { - // Reposition the location of the arrow on the popup so that the popup - // better fits on the working monitor. - gfx::Rect popup_rect = popup->GetOuterBounds(); - if (popup_rect.IsEmpty()) - return; - - scoped_ptr<WindowSizer::MonitorInfoProvider> monitor_provider( - WindowSizer::CreateDefaultMonitorInfoProvider()); - gfx::Rect monitor_bounds( - monitor_provider->GetMonitorWorkAreaMatching(popup_rect)); - gfx::Rect intersection = monitor_bounds.Intersect(popup_rect); - - // If the popup is totally out of the bounds of the monitor, then toggling - // the arrow location will not result in an un-clipped window. - if (intersection.IsEmpty()) - return; - - if (!intersection.Equals(popup_rect)) { - // The popup was clipped by the monitor. Toggle the arrow position - // to see if that improves visibility. Note: The assignment and - // re-assignment of the arrow-position will not trigger an intermittent - // display. - BubbleBorder::ArrowLocation previous_location = popup->arrow_position(); - BubbleBorder::ArrowLocation flipped_location = ToggleArrowLocation( - previous_location, intersection, popup_rect); - popup->SetArrowPosition(flipped_location); - - // Double check that toggling the position actually improved the - // situation - the popup will be contained entirely in its working monitor - // bounds. - gfx::Rect flipped_bounds = popup->GetOuterBounds(); - gfx::Rect updated_monitor_bounds = - monitor_provider->GetMonitorWorkAreaMatching(flipped_bounds); - if (!updated_monitor_bounds.Contains(flipped_bounds)) - popup->SetArrowPosition(previous_location); - } - } - - // Overridden from views::WidgetFocusChangeListener - virtual void NativeFocusWillChange(gfx::NativeView focused_before, - gfx::NativeView focused_now) { - // If the popup doesn't exist, then do nothing. - if (!popup_) - return; - - // If no view is to be focused, then Chrome was deactivated, so hide the - // popup. - if (focused_now) { - // On XP, the focus change handler may be invoked when the delegate has - // already been revoked. - // TODO(twiz@chromium.org): Resolve the trigger of this behaviour. - if (!dispatcher_ || !dispatcher_->delegate()) - return; - - gfx::NativeView host_view = - dispatcher_->delegate()->GetNativeViewOfHost(); - - // If the widget hosting the popup contains the newly focused view, then - // don't dismiss the pop-up. - ExtensionView* view = popup_->host()->view(); - if (view) { - views::Widget* popup_root_widget = 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)); - } - - // Overridden from NotificationObserver - virtual void Observe(NotificationType type, - const NotificationSource& source, - const NotificationDetails& details) { - if (NotificationType::EXTENSION_HOST_CREATED == type) { - Details<ExtensionHost> details_host(details); - // Disallow multiple pop-ups from the same extension, by closing - // the presently opened popup during construction of any new popups. - if (ViewType::EXTENSION_POPUP == details_host->GetRenderViewType() && - popup_->host()->extension() == details_host->extension() && - Details<ExtensionHost>(popup_->host()) != details) { - popup_->Close(); - } - } else if (NotificationType::RENDER_VIEW_HOST_WILL_CLOSE_RENDER_VIEW == - type) { - if (Source<RenderViewHost>(dispatcher_->render_view_host()) == source) { - // If the parent render view is about to be closed, signal closure - // of the popup. - popup_->Close(); - } - } else if (NotificationType::EXTENSION_FUNCTION_DISPATCHER_DESTROYED == - type) { - // Popups should not outlive the dispatchers that launched them. - // Normally, long-lived popups will be dismissed in response to the - // RENDER_VIEW_WILL_CLOSE_BY_RENDER_VIEW_HOST message. Unfortunately, - // if the hosting view invokes window.close(), there is no communication - // back to the browser until the entire view has been torn down, at which - // time the dispatcher will be invoked. - // Note: The onClosed event will not be fired, but because the hosting - // view has already been torn down, it is already too late to process it. - // TODO(twiz): Add a communication path between the renderer and browser - // for RenderView closure notifications initiatied within the renderer. - if (Details<ExtensionFunctionDispatcher>(dispatcher_) == details) { - dispatcher_ = NULL; - popup_->Close(); - } - } - } - - private: - // Returns the AutomationResourceRoutingDelegate interface for |dispatcher|. - static AutomationResourceRoutingDelegate* - GetRoutingFromDispatcher(ExtensionFunctionDispatcher* dispatcher) { - if (!dispatcher) - return NULL; - - RenderViewHost* render_view_host = dispatcher->render_view_host(); - RenderViewHostDelegate* delegate = - render_view_host ? render_view_host->delegate() : NULL; - - return delegate ? delegate->GetAutomationResourceRoutingDelegate() : NULL; - } - - void DispatchPopupClosedEvent() { - if (dispatcher_) { - PopupEventRouter::OnPopupClosed( - dispatcher_->profile(), - dispatcher_->render_view_host()->routing_id()); - dispatcher_ = NULL; - } - Release(); // Balanced in ctor. - } - - // A pointer to the dispatcher that handled the request that opened this - // popup view. - ExtensionFunctionDispatcher* dispatcher_; - - // A pointer to the popup. - ExtensionPopup* popup_; - - // The maximal size to which the popup is permitted to expand. - gfx::Size max_popup_size_; - - NotificationRegistrar registrar_; - - DISALLOW_COPY_AND_ASSIGN(ExtensionPopupHost); -}; -#endif // TOOLKIT_VIEWS - -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() { - // Popups may only be displayed from TAB_CONTENTS and EXTENSION_INFOBAR. - ViewType::Type view_type = - dispatcher()->render_view_host()->delegate()->GetRenderViewType(); - if (ViewType::TAB_CONTENTS != view_type && - ViewType::EXTENSION_INFOBAR != view_type) { - error_ = kPopupsDisallowed; - return false; - } - - std::string url_string; - EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &url_string)); - - DictionaryValue* show_details = NULL; - EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(1, &show_details)); - - DictionaryValue* dom_anchor = NULL; - EXTENSION_FUNCTION_VALIDATE(show_details->GetDictionary(kDomAnchorKey, - &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); - - // The default behaviour is to give the focus to the pop-up window. - bool give_focus = true; - if (show_details->HasKey(kGiveFocusKey)) { - EXTENSION_FUNCTION_VALIDATE(show_details->GetBoolean(kGiveFocusKey, - &give_focus)); - } - - int max_width = 0; - int max_height = 0; - if (show_details->HasKey(kMaxSizeKey)) { - DictionaryValue* max_size = NULL; - EXTENSION_FUNCTION_VALIDATE(show_details->GetDictionary(kMaxSizeKey, - &max_size)); - - if (max_size->HasKey(kWidthKey)) - EXTENSION_FUNCTION_VALIDATE(max_size->GetInteger(kWidthKey, &max_width)); - - if (max_size->HasKey(kHeightKey)) - EXTENSION_FUNCTION_VALIDATE(max_size->GetInteger(kHeightKey, - &max_height)); - } - -#if defined(TOOLKIT_VIEWS) - // The default behaviour is to provide the bubble-chrome to the popup. - ExtensionPopup::PopupChrome chrome = ExtensionPopup::BUBBLE_CHROME; - if (show_details->HasKey(kBorderStyleKey)) { - std::string chrome_string; - EXTENSION_FUNCTION_VALIDATE(show_details->GetString(kBorderStyleKey, - &chrome_string)); - if (chrome_string == kRectangleChrome) - chrome = ExtensionPopup::RECTANGLE_CHROME; - } -#endif - - 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 != GetExtension()->id() || - !url.SchemeIs(chrome::kExtensionScheme)) { - error_ = kInvalidURLError; - return false; - } - - gfx::Point origin(dom_left, dom_top); - if (!dispatcher()->render_view_host()->view()) { - error_ = kNotAnExtension; - return false; - } - - gfx::Rect content_bounds = - dispatcher()->render_view_host()->view()->GetViewBounds(); - origin.Offset(content_bounds.x(), content_bounds.y()); - gfx::Rect rect(origin.x(), origin.y(), dom_width, dom_height); - - // Get the correct native window to pass to ExtensionPopup. - // ExtensionFunctionDispatcher::Delegate may provide a custom implementation - // of this. - gfx::NativeWindow window = - dispatcher()->delegate()->GetCustomFrameNativeWindow(); - if (!window) - window = GetCurrentBrowser()->window()->GetNativeHandle(); - -#if defined(TOOLKIT_VIEWS) - BubbleBorder::ArrowLocation arrow_location = BubbleBorder::TOP_LEFT; - - // ExtensionPopupHost manages it's own lifetime. - ExtensionPopupHost* popup_host = - new ExtensionPopupHost(dispatcher(), gfx::Size(max_width, max_height)); - popup_ = ExtensionPopup::Show(url, - GetCurrentBrowser(), - dispatcher()->profile(), - window, - rect, - arrow_location, - give_focus, - 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; -} - -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); - - // Wait for notification that the popup view is ready (and onload has been - // called), before completing the API call. - 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 // defined(TOOLKIT_VIEWS) -} - -// static -void PopupEventRouter::OnPopupClosed(Profile* profile, - int routing_id) { - std::string full_event_name = base::StringPrintf( - extension_popup_module_events::kOnPopupClosed, - routing_id); - - profile->GetExtensionEventRouter()->DispatchEventToRenderers( - full_event_name, base::JSONWriter::kEmptyArray, profile, GURL()); -} |