| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "chrome/browser/extensions/extension_popup_api.h" | |
| 6 | |
| 7 #include <string> | |
| 8 | |
| 9 #include "base/json/json_writer.h" | |
| 10 #include "base/string_util.h" | |
| 11 #include "base/stringprintf.h" | |
| 12 #include "base/values.h" | |
| 13 #include "chrome/browser/extensions/extension_dom_ui.h" | |
| 14 #include "chrome/browser/extensions/extension_event_router.h" | |
| 15 #include "chrome/browser/extensions/extension_host.h" | |
| 16 #include "chrome/browser/profiles/profile.h" | |
| 17 #include "chrome/browser/renderer_host/render_view_host.h" | |
| 18 #include "chrome/browser/renderer_host/render_view_host_delegate.h" | |
| 19 #include "chrome/browser/renderer_host/render_widget_host_view.h" | |
| 20 #include "chrome/browser/ui/browser.h" | |
| 21 #include "chrome/browser/ui/browser_window.h" | |
| 22 #include "chrome/browser/ui/window_sizer.h" | |
| 23 #include "chrome/common/extensions/extension.h" | |
| 24 #include "chrome/common/notification_details.h" | |
| 25 #include "chrome/common/notification_service.h" | |
| 26 #include "chrome/common/notification_source.h" | |
| 27 #include "chrome/common/notification_type.h" | |
| 28 #include "chrome/common/url_constants.h" | |
| 29 #include "gfx/point.h" | |
| 30 | |
| 31 #if defined(TOOLKIT_VIEWS) | |
| 32 #include "chrome/browser/ui/views/bubble_border.h" | |
| 33 #include "chrome/browser/ui/views/extensions/extension_popup.h" | |
| 34 #include "views/view.h" | |
| 35 #include "views/focus/focus_manager.h" | |
| 36 #endif // TOOLKIT_VIEWS | |
| 37 | |
| 38 namespace extension_popup_module_events { | |
| 39 | |
| 40 const char kOnPopupClosed[] = "experimental.popup.onClosed.%d"; | |
| 41 | |
| 42 } // namespace extension_popup_module_events | |
| 43 | |
| 44 namespace { | |
| 45 | |
| 46 // Errors. | |
| 47 const char kBadAnchorArgument[] = "Invalid anchor argument."; | |
| 48 const char kInvalidURLError[] = "Invalid URL."; | |
| 49 const char kNotAnExtension[] = "Not an extension view."; | |
| 50 const char kPopupsDisallowed[] = | |
| 51 "Popups are only supported from tab-contents views."; | |
| 52 | |
| 53 // Keys. | |
| 54 const char kWidthKey[] = "width"; | |
| 55 const char kHeightKey[] = "height"; | |
| 56 const char kTopKey[] = "top"; | |
| 57 const char kLeftKey[] = "left"; | |
| 58 const char kGiveFocusKey[] = "giveFocus"; | |
| 59 const char kDomAnchorKey[] = "domAnchor"; | |
| 60 const char kBorderStyleKey[] = "borderStyle"; | |
| 61 const char kMaxSizeKey[] = "maxSize"; | |
| 62 | |
| 63 // chrome enumeration values | |
| 64 const char kRectangleChrome[] = "rectangle"; | |
| 65 | |
| 66 #if defined(TOOLKIT_VIEWS) | |
| 67 // Returns an updated arrow location, conditioned on the type of intersection | |
| 68 // between the popup window, and the screen. |location| is the current position | |
| 69 // of the arrow on the popup. |intersection| is the rect representing the | |
| 70 // intersection between the popup view and its working screen. |popup_rect| | |
| 71 // is the rect of the popup window in screen space coordinates. | |
| 72 // The returned location will be horizontally or vertically inverted based on | |
| 73 // if the popup has been clipped horizontally or vertically. | |
| 74 BubbleBorder::ArrowLocation ToggleArrowLocation( | |
| 75 BubbleBorder::ArrowLocation location, const gfx::Rect& intersection, | |
| 76 const gfx::Rect& popup_rect) { | |
| 77 // If the popup has been clipped horizontally, flip the right-left position | |
| 78 // of the arrow. | |
| 79 if (intersection.right() != popup_rect.right() || | |
| 80 intersection.x() != popup_rect.x()) { | |
| 81 location = BubbleBorder::horizontal_mirror(location); | |
| 82 } | |
| 83 | |
| 84 // If the popup has been clipped vertically, flip the bottom-top position | |
| 85 // of the arrow. | |
| 86 if (intersection.y() != popup_rect.y() || | |
| 87 intersection.bottom() != popup_rect.bottom()) { | |
| 88 location = BubbleBorder::vertical_mirror(location); | |
| 89 } | |
| 90 | |
| 91 return location; | |
| 92 } | |
| 93 #endif // TOOLKIT_VIEWS | |
| 94 | |
| 95 }; // namespace | |
| 96 | |
| 97 #if defined(TOOLKIT_VIEWS) | |
| 98 // ExtensionPopupHost objects implement the environment necessary to host | |
| 99 // an ExtensionPopup views for the popup api. Its main job is to handle | |
| 100 // its lifetime and to fire the popup-closed event when the popup is closed. | |
| 101 // Because the close-on-focus-lost behavior is different from page action | |
| 102 // and browser action, it also manages its own focus change listening. The | |
| 103 // difference in close-on-focus-lost is that in the page action and browser | |
| 104 // action cases, the popup closes when the focus leaves the popup or any of its | |
| 105 // children. In this case, the popup closes when the focus leaves the popups | |
| 106 // containing view or any of *its* children. | |
| 107 class ExtensionPopupHost : public ExtensionPopup::Observer, | |
| 108 public views::WidgetFocusChangeListener, | |
| 109 public base::RefCounted<ExtensionPopupHost>, | |
| 110 public NotificationObserver { | |
| 111 public: | |
| 112 // Pass |max_popup_size| to specify the maximal size to which the popup | |
| 113 // will expand. A width or height of 0 will result in the popup making use | |
| 114 // of the default max width or height, respectively: ExtensionPopup:kMaxWidth, | |
| 115 // and ExtensionPopup::kMaxHeight. | |
| 116 explicit ExtensionPopupHost(ExtensionFunctionDispatcher* dispatcher, | |
| 117 const gfx::Size& max_popup_size) | |
| 118 : dispatcher_(dispatcher), popup_(NULL), max_popup_size_(max_popup_size) { | |
| 119 AddRef(); // Balanced in DispatchPopupClosedEvent(). | |
| 120 views::FocusManager::GetWidgetFocusManager()->AddFocusChangeListener(this); | |
| 121 } | |
| 122 | |
| 123 ~ExtensionPopupHost() { | |
| 124 views::FocusManager::GetWidgetFocusManager()-> | |
| 125 RemoveFocusChangeListener(this); | |
| 126 } | |
| 127 | |
| 128 void set_popup(ExtensionPopup* popup) { | |
| 129 popup_ = popup; | |
| 130 | |
| 131 // Now that a popup has been assigned, listen for subsequent popups being | |
| 132 // created in the same extension - we want to disallow more than one | |
| 133 // concurrently displayed popup windows. | |
| 134 registrar_.Add( | |
| 135 this, | |
| 136 NotificationType::EXTENSION_HOST_CREATED, | |
| 137 Source<ExtensionProcessManager>( | |
| 138 dispatcher_->profile()->GetExtensionProcessManager())); | |
| 139 | |
| 140 registrar_.Add( | |
| 141 this, | |
| 142 NotificationType::RENDER_VIEW_HOST_WILL_CLOSE_RENDER_VIEW, | |
| 143 Source<RenderViewHost>(dispatcher_->render_view_host())); | |
| 144 | |
| 145 registrar_.Add( | |
| 146 this, | |
| 147 NotificationType::EXTENSION_FUNCTION_DISPATCHER_DESTROYED, | |
| 148 Source<Profile>(dispatcher_->profile())); | |
| 149 } | |
| 150 | |
| 151 // Overridden from ExtensionPopup::Observer | |
| 152 virtual void ExtensionPopupIsClosing(ExtensionPopup* popup) { | |
| 153 // Unregister the automation resource routing registered upon host | |
| 154 // creation. | |
| 155 AutomationResourceRoutingDelegate* router = | |
| 156 GetRoutingFromDispatcher(dispatcher_); | |
| 157 if (router) | |
| 158 router->UnregisterRenderViewHost(popup_->host()->render_view_host()); | |
| 159 } | |
| 160 | |
| 161 virtual void ExtensionPopupClosed(void* popup_token) { | |
| 162 if (popup_ == popup_token) { | |
| 163 popup_ = NULL; | |
| 164 DispatchPopupClosedEvent(); | |
| 165 } | |
| 166 } | |
| 167 | |
| 168 virtual void ExtensionHostCreated(ExtensionHost* host) { | |
| 169 // Pop-up views should share the same automation routing configuration as | |
| 170 // their hosting views, so register the RenderViewHost of the pop-up with | |
| 171 // the AutomationResourceRoutingDelegate interface of the dispatcher. | |
| 172 AutomationResourceRoutingDelegate* router = | |
| 173 GetRoutingFromDispatcher(dispatcher_); | |
| 174 if (router) | |
| 175 router->RegisterRenderViewHost(host->render_view_host()); | |
| 176 | |
| 177 // Extension hosts created for popup contents exist in the same tab | |
| 178 // contents as the ExtensionFunctionDispatcher that requested the popup. | |
| 179 // For example, '_blank' link navigation should be routed through the tab | |
| 180 // contents that requested the popup. | |
| 181 if (dispatcher_ && dispatcher_->delegate()) { | |
| 182 host->set_associated_tab_contents( | |
| 183 dispatcher_->delegate()->associated_tab_contents()); | |
| 184 } | |
| 185 } | |
| 186 | |
| 187 virtual void ExtensionPopupCreated(ExtensionPopup* popup) { | |
| 188 // The popup has been created, but not yet displayed, so install the max | |
| 189 // size overrides before the first positioning. | |
| 190 if (max_popup_size_.width()) | |
| 191 popup->set_max_width(max_popup_size_.width()); | |
| 192 | |
| 193 if (max_popup_size_.height()) | |
| 194 popup->set_max_height(max_popup_size_.height()); | |
| 195 } | |
| 196 | |
| 197 virtual void ExtensionPopupResized(ExtensionPopup* popup) { | |
| 198 // Reposition the location of the arrow on the popup so that the popup | |
| 199 // better fits on the working monitor. | |
| 200 gfx::Rect popup_rect = popup->GetOuterBounds(); | |
| 201 if (popup_rect.IsEmpty()) | |
| 202 return; | |
| 203 | |
| 204 scoped_ptr<WindowSizer::MonitorInfoProvider> monitor_provider( | |
| 205 WindowSizer::CreateDefaultMonitorInfoProvider()); | |
| 206 gfx::Rect monitor_bounds( | |
| 207 monitor_provider->GetMonitorWorkAreaMatching(popup_rect)); | |
| 208 gfx::Rect intersection = monitor_bounds.Intersect(popup_rect); | |
| 209 | |
| 210 // If the popup is totally out of the bounds of the monitor, then toggling | |
| 211 // the arrow location will not result in an un-clipped window. | |
| 212 if (intersection.IsEmpty()) | |
| 213 return; | |
| 214 | |
| 215 if (!intersection.Equals(popup_rect)) { | |
| 216 // The popup was clipped by the monitor. Toggle the arrow position | |
| 217 // to see if that improves visibility. Note: The assignment and | |
| 218 // re-assignment of the arrow-position will not trigger an intermittent | |
| 219 // display. | |
| 220 BubbleBorder::ArrowLocation previous_location = popup->arrow_position(); | |
| 221 BubbleBorder::ArrowLocation flipped_location = ToggleArrowLocation( | |
| 222 previous_location, intersection, popup_rect); | |
| 223 popup->SetArrowPosition(flipped_location); | |
| 224 | |
| 225 // Double check that toggling the position actually improved the | |
| 226 // situation - the popup will be contained entirely in its working monitor | |
| 227 // bounds. | |
| 228 gfx::Rect flipped_bounds = popup->GetOuterBounds(); | |
| 229 gfx::Rect updated_monitor_bounds = | |
| 230 monitor_provider->GetMonitorWorkAreaMatching(flipped_bounds); | |
| 231 if (!updated_monitor_bounds.Contains(flipped_bounds)) | |
| 232 popup->SetArrowPosition(previous_location); | |
| 233 } | |
| 234 } | |
| 235 | |
| 236 // Overridden from views::WidgetFocusChangeListener | |
| 237 virtual void NativeFocusWillChange(gfx::NativeView focused_before, | |
| 238 gfx::NativeView focused_now) { | |
| 239 // If the popup doesn't exist, then do nothing. | |
| 240 if (!popup_) | |
| 241 return; | |
| 242 | |
| 243 // If no view is to be focused, then Chrome was deactivated, so hide the | |
| 244 // popup. | |
| 245 if (focused_now) { | |
| 246 // On XP, the focus change handler may be invoked when the delegate has | |
| 247 // already been revoked. | |
| 248 // TODO(twiz@chromium.org): Resolve the trigger of this behaviour. | |
| 249 if (!dispatcher_ || !dispatcher_->delegate()) | |
| 250 return; | |
| 251 | |
| 252 gfx::NativeView host_view = | |
| 253 dispatcher_->delegate()->GetNativeViewOfHost(); | |
| 254 | |
| 255 // If the widget hosting the popup contains the newly focused view, then | |
| 256 // don't dismiss the pop-up. | |
| 257 ExtensionView* view = popup_->host()->view(); | |
| 258 if (view) { | |
| 259 views::Widget* popup_root_widget = view->GetWidget(); | |
| 260 if (popup_root_widget && | |
| 261 popup_root_widget->ContainsNativeView(focused_now)) | |
| 262 return; | |
| 263 } | |
| 264 | |
| 265 // If the widget or RenderWidgetHostView hosting the extension that | |
| 266 // launched the pop-up is receiving focus, then don't dismiss the popup. | |
| 267 views::Widget* host_widget = | |
| 268 views::Widget::GetWidgetFromNativeView(host_view); | |
| 269 if (host_widget && host_widget->ContainsNativeView(focused_now)) | |
| 270 return; | |
| 271 | |
| 272 RenderWidgetHostView* render_host_view = | |
| 273 RenderWidgetHostView::GetRenderWidgetHostViewFromNativeView( | |
| 274 host_view); | |
| 275 if (render_host_view && | |
| 276 render_host_view->ContainsNativeView(focused_now)) | |
| 277 return; | |
| 278 } | |
| 279 | |
| 280 // We are careful here to let the current event loop unwind before | |
| 281 // causing the popup to be closed. | |
| 282 MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(popup_, | |
| 283 &ExtensionPopup::Close)); | |
| 284 } | |
| 285 | |
| 286 // Overridden from NotificationObserver | |
| 287 virtual void Observe(NotificationType type, | |
| 288 const NotificationSource& source, | |
| 289 const NotificationDetails& details) { | |
| 290 if (NotificationType::EXTENSION_HOST_CREATED == type) { | |
| 291 Details<ExtensionHost> details_host(details); | |
| 292 // Disallow multiple pop-ups from the same extension, by closing | |
| 293 // the presently opened popup during construction of any new popups. | |
| 294 if (ViewType::EXTENSION_POPUP == details_host->GetRenderViewType() && | |
| 295 popup_->host()->extension() == details_host->extension() && | |
| 296 Details<ExtensionHost>(popup_->host()) != details) { | |
| 297 popup_->Close(); | |
| 298 } | |
| 299 } else if (NotificationType::RENDER_VIEW_HOST_WILL_CLOSE_RENDER_VIEW == | |
| 300 type) { | |
| 301 if (Source<RenderViewHost>(dispatcher_->render_view_host()) == source) { | |
| 302 // If the parent render view is about to be closed, signal closure | |
| 303 // of the popup. | |
| 304 popup_->Close(); | |
| 305 } | |
| 306 } else if (NotificationType::EXTENSION_FUNCTION_DISPATCHER_DESTROYED == | |
| 307 type) { | |
| 308 // Popups should not outlive the dispatchers that launched them. | |
| 309 // Normally, long-lived popups will be dismissed in response to the | |
| 310 // RENDER_VIEW_WILL_CLOSE_BY_RENDER_VIEW_HOST message. Unfortunately, | |
| 311 // if the hosting view invokes window.close(), there is no communication | |
| 312 // back to the browser until the entire view has been torn down, at which | |
| 313 // time the dispatcher will be invoked. | |
| 314 // Note: The onClosed event will not be fired, but because the hosting | |
| 315 // view has already been torn down, it is already too late to process it. | |
| 316 // TODO(twiz): Add a communication path between the renderer and browser | |
| 317 // for RenderView closure notifications initiatied within the renderer. | |
| 318 if (Details<ExtensionFunctionDispatcher>(dispatcher_) == details) { | |
| 319 dispatcher_ = NULL; | |
| 320 popup_->Close(); | |
| 321 } | |
| 322 } | |
| 323 } | |
| 324 | |
| 325 private: | |
| 326 // Returns the AutomationResourceRoutingDelegate interface for |dispatcher|. | |
| 327 static AutomationResourceRoutingDelegate* | |
| 328 GetRoutingFromDispatcher(ExtensionFunctionDispatcher* dispatcher) { | |
| 329 if (!dispatcher) | |
| 330 return NULL; | |
| 331 | |
| 332 RenderViewHost* render_view_host = dispatcher->render_view_host(); | |
| 333 RenderViewHostDelegate* delegate = | |
| 334 render_view_host ? render_view_host->delegate() : NULL; | |
| 335 | |
| 336 return delegate ? delegate->GetAutomationResourceRoutingDelegate() : NULL; | |
| 337 } | |
| 338 | |
| 339 void DispatchPopupClosedEvent() { | |
| 340 if (dispatcher_) { | |
| 341 PopupEventRouter::OnPopupClosed( | |
| 342 dispatcher_->profile(), | |
| 343 dispatcher_->render_view_host()->routing_id()); | |
| 344 dispatcher_ = NULL; | |
| 345 } | |
| 346 Release(); // Balanced in ctor. | |
| 347 } | |
| 348 | |
| 349 // A pointer to the dispatcher that handled the request that opened this | |
| 350 // popup view. | |
| 351 ExtensionFunctionDispatcher* dispatcher_; | |
| 352 | |
| 353 // A pointer to the popup. | |
| 354 ExtensionPopup* popup_; | |
| 355 | |
| 356 // The maximal size to which the popup is permitted to expand. | |
| 357 gfx::Size max_popup_size_; | |
| 358 | |
| 359 NotificationRegistrar registrar_; | |
| 360 | |
| 361 DISALLOW_COPY_AND_ASSIGN(ExtensionPopupHost); | |
| 362 }; | |
| 363 #endif // TOOLKIT_VIEWS | |
| 364 | |
| 365 PopupShowFunction::PopupShowFunction() | |
| 366 #if defined (TOOLKIT_VIEWS) | |
| 367 : popup_(NULL) | |
| 368 #endif | |
| 369 {} | |
| 370 | |
| 371 void PopupShowFunction::Run() { | |
| 372 #if defined(TOOLKIT_VIEWS) | |
| 373 if (!RunImpl()) { | |
| 374 SendResponse(false); | |
| 375 } else { | |
| 376 // If the contents of the popup are already available, then immediately | |
| 377 // send the response. Otherwise wait for the EXTENSION_POPUP_VIEW_READY | |
| 378 // notification. | |
| 379 if (popup_->host() && popup_->host()->document_element_available()) { | |
| 380 SendResponse(true); | |
| 381 } else { | |
| 382 AddRef(); | |
| 383 registrar_.Add(this, NotificationType::EXTENSION_POPUP_VIEW_READY, | |
| 384 NotificationService::AllSources()); | |
| 385 registrar_.Add(this, NotificationType::EXTENSION_HOST_DESTROYED, | |
| 386 NotificationService::AllSources()); | |
| 387 } | |
| 388 } | |
| 389 #else | |
| 390 SendResponse(false); | |
| 391 #endif | |
| 392 } | |
| 393 | |
| 394 bool PopupShowFunction::RunImpl() { | |
| 395 // Popups may only be displayed from TAB_CONTENTS and EXTENSION_INFOBAR. | |
| 396 ViewType::Type view_type = | |
| 397 dispatcher()->render_view_host()->delegate()->GetRenderViewType(); | |
| 398 if (ViewType::TAB_CONTENTS != view_type && | |
| 399 ViewType::EXTENSION_INFOBAR != view_type) { | |
| 400 error_ = kPopupsDisallowed; | |
| 401 return false; | |
| 402 } | |
| 403 | |
| 404 std::string url_string; | |
| 405 EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &url_string)); | |
| 406 | |
| 407 DictionaryValue* show_details = NULL; | |
| 408 EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(1, &show_details)); | |
| 409 | |
| 410 DictionaryValue* dom_anchor = NULL; | |
| 411 EXTENSION_FUNCTION_VALIDATE(show_details->GetDictionary(kDomAnchorKey, | |
| 412 &dom_anchor)); | |
| 413 | |
| 414 int dom_top, dom_left; | |
| 415 EXTENSION_FUNCTION_VALIDATE(dom_anchor->GetInteger(kTopKey, | |
| 416 &dom_top)); | |
| 417 EXTENSION_FUNCTION_VALIDATE(dom_anchor->GetInteger(kLeftKey, | |
| 418 &dom_left)); | |
| 419 | |
| 420 int dom_width, dom_height; | |
| 421 EXTENSION_FUNCTION_VALIDATE(dom_anchor->GetInteger(kWidthKey, | |
| 422 &dom_width)); | |
| 423 EXTENSION_FUNCTION_VALIDATE(dom_anchor->GetInteger(kHeightKey, | |
| 424 &dom_height)); | |
| 425 EXTENSION_FUNCTION_VALIDATE(dom_top >= 0 && dom_left >= 0 && | |
| 426 dom_width >= 0 && dom_height >= 0); | |
| 427 | |
| 428 // The default behaviour is to give the focus to the pop-up window. | |
| 429 bool give_focus = true; | |
| 430 if (show_details->HasKey(kGiveFocusKey)) { | |
| 431 EXTENSION_FUNCTION_VALIDATE(show_details->GetBoolean(kGiveFocusKey, | |
| 432 &give_focus)); | |
| 433 } | |
| 434 | |
| 435 int max_width = 0; | |
| 436 int max_height = 0; | |
| 437 if (show_details->HasKey(kMaxSizeKey)) { | |
| 438 DictionaryValue* max_size = NULL; | |
| 439 EXTENSION_FUNCTION_VALIDATE(show_details->GetDictionary(kMaxSizeKey, | |
| 440 &max_size)); | |
| 441 | |
| 442 if (max_size->HasKey(kWidthKey)) | |
| 443 EXTENSION_FUNCTION_VALIDATE(max_size->GetInteger(kWidthKey, &max_width)); | |
| 444 | |
| 445 if (max_size->HasKey(kHeightKey)) | |
| 446 EXTENSION_FUNCTION_VALIDATE(max_size->GetInteger(kHeightKey, | |
| 447 &max_height)); | |
| 448 } | |
| 449 | |
| 450 #if defined(TOOLKIT_VIEWS) | |
| 451 // The default behaviour is to provide the bubble-chrome to the popup. | |
| 452 ExtensionPopup::PopupChrome chrome = ExtensionPopup::BUBBLE_CHROME; | |
| 453 if (show_details->HasKey(kBorderStyleKey)) { | |
| 454 std::string chrome_string; | |
| 455 EXTENSION_FUNCTION_VALIDATE(show_details->GetString(kBorderStyleKey, | |
| 456 &chrome_string)); | |
| 457 if (chrome_string == kRectangleChrome) | |
| 458 chrome = ExtensionPopup::RECTANGLE_CHROME; | |
| 459 } | |
| 460 #endif | |
| 461 | |
| 462 GURL url = dispatcher()->url().Resolve(url_string); | |
| 463 if (!url.is_valid()) { | |
| 464 error_ = kInvalidURLError; | |
| 465 return false; | |
| 466 } | |
| 467 | |
| 468 // Disallow non-extension requests, or requests outside of the requesting | |
| 469 // extension view's extension. | |
| 470 const std::string& extension_id = url.host(); | |
| 471 if (extension_id != GetExtension()->id() || | |
| 472 !url.SchemeIs(chrome::kExtensionScheme)) { | |
| 473 error_ = kInvalidURLError; | |
| 474 return false; | |
| 475 } | |
| 476 | |
| 477 gfx::Point origin(dom_left, dom_top); | |
| 478 if (!dispatcher()->render_view_host()->view()) { | |
| 479 error_ = kNotAnExtension; | |
| 480 return false; | |
| 481 } | |
| 482 | |
| 483 gfx::Rect content_bounds = | |
| 484 dispatcher()->render_view_host()->view()->GetViewBounds(); | |
| 485 origin.Offset(content_bounds.x(), content_bounds.y()); | |
| 486 gfx::Rect rect(origin.x(), origin.y(), dom_width, dom_height); | |
| 487 | |
| 488 // Get the correct native window to pass to ExtensionPopup. | |
| 489 // ExtensionFunctionDispatcher::Delegate may provide a custom implementation | |
| 490 // of this. | |
| 491 gfx::NativeWindow window = | |
| 492 dispatcher()->delegate()->GetCustomFrameNativeWindow(); | |
| 493 if (!window) | |
| 494 window = GetCurrentBrowser()->window()->GetNativeHandle(); | |
| 495 | |
| 496 #if defined(TOOLKIT_VIEWS) | |
| 497 BubbleBorder::ArrowLocation arrow_location = BubbleBorder::TOP_LEFT; | |
| 498 | |
| 499 // ExtensionPopupHost manages it's own lifetime. | |
| 500 ExtensionPopupHost* popup_host = | |
| 501 new ExtensionPopupHost(dispatcher(), gfx::Size(max_width, max_height)); | |
| 502 popup_ = ExtensionPopup::Show(url, | |
| 503 GetCurrentBrowser(), | |
| 504 dispatcher()->profile(), | |
| 505 window, | |
| 506 rect, | |
| 507 arrow_location, | |
| 508 give_focus, | |
| 509 false, // inspect_with_devtools | |
| 510 chrome, | |
| 511 popup_host); // ExtensionPopup::Observer | |
| 512 | |
| 513 // popup_host will handle focus change listening and close the popup when | |
| 514 // focus leaves the containing views hierarchy. | |
| 515 popup_->set_close_on_lost_focus(false); | |
| 516 popup_host->set_popup(popup_); | |
| 517 #endif // defined(TOOLKIT_VIEWS) | |
| 518 | |
| 519 return true; | |
| 520 } | |
| 521 | |
| 522 void PopupShowFunction::Observe(NotificationType type, | |
| 523 const NotificationSource& source, | |
| 524 const NotificationDetails& details) { | |
| 525 #if defined(TOOLKIT_VIEWS) | |
| 526 DCHECK(type == NotificationType::EXTENSION_POPUP_VIEW_READY || | |
| 527 type == NotificationType::EXTENSION_HOST_DESTROYED); | |
| 528 DCHECK(popup_ != NULL); | |
| 529 | |
| 530 // Wait for notification that the popup view is ready (and onload has been | |
| 531 // called), before completing the API call. | |
| 532 if (popup_ && type == NotificationType::EXTENSION_POPUP_VIEW_READY && | |
| 533 Details<ExtensionHost>(popup_->host()) == details) { | |
| 534 SendResponse(true); | |
| 535 Release(); // Balanced in Run(). | |
| 536 } else if (popup_ && type == NotificationType::EXTENSION_HOST_DESTROYED && | |
| 537 Details<ExtensionHost>(popup_->host()) == details) { | |
| 538 // If the host was destroyed, then report failure, and release the remaining | |
| 539 // reference. | |
| 540 SendResponse(false); | |
| 541 Release(); // Balanced in Run(). | |
| 542 } | |
| 543 #endif // defined(TOOLKIT_VIEWS) | |
| 544 } | |
| 545 | |
| 546 // static | |
| 547 void PopupEventRouter::OnPopupClosed(Profile* profile, | |
| 548 int routing_id) { | |
| 549 std::string full_event_name = base::StringPrintf( | |
| 550 extension_popup_module_events::kOnPopupClosed, | |
| 551 routing_id); | |
| 552 | |
| 553 profile->GetExtensionEventRouter()->DispatchEventToRenderers( | |
| 554 full_event_name, base::JSONWriter::kEmptyArray, profile, GURL()); | |
| 555 } | |
| OLD | NEW |