extension_host.cc revision 513209b27ff55e2841eac0e4120199c23acce758
1// Copyright (c) 2010 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_host.h" 6 7#include <list> 8 9#include "app/keyboard_codes.h" 10#include "app/l10n_util.h" 11#include "app/resource_bundle.h" 12#include "base/message_loop.h" 13#include "base/singleton.h" 14#include "base/metrics/histogram.h" 15#include "base/string_util.h" 16#include "chrome/browser/browser.h" 17#include "chrome/browser/browser_list.h" 18#include "chrome/browser/browser_shutdown.h" 19#include "chrome/browser/browser_window.h" 20#include "chrome/browser/browsing_instance.h" 21#include "chrome/browser/debugger/devtools_manager.h" 22#include "chrome/browser/dom_ui/dom_ui_factory.h" 23#include "chrome/browser/extensions/extension_message_service.h" 24#include "chrome/browser/extensions/extension_tabs_module.h" 25#include "chrome/browser/extensions/extensions_service.h" 26#include "chrome/browser/file_select_helper.h" 27#include "chrome/browser/message_box_handler.h" 28#include "chrome/browser/platform_util.h" 29#include "chrome/browser/prefs/pref_service.h" 30#include "chrome/browser/profile.h" 31#include "chrome/browser/renderer_host/render_process_host.h" 32#include "chrome/browser/renderer_host/render_view_host.h" 33#include "chrome/browser/renderer_host/render_widget_host.h" 34#include "chrome/browser/renderer_host/render_widget_host_view.h" 35#include "chrome/browser/renderer_host/site_instance.h" 36#include "chrome/browser/renderer_preferences_util.h" 37#include "chrome/browser/tab_contents/popup_menu_helper_mac.h" 38#include "chrome/browser/tab_contents/tab_contents.h" 39#include "chrome/browser/tab_contents/tab_contents_view.h" 40#include "chrome/browser/themes/browser_theme_provider.h" 41#include "chrome/common/bindings_policy.h" 42#include "chrome/common/extensions/extension.h" 43#include "chrome/common/extensions/extension_constants.h" 44#include "chrome/common/native_web_keyboard_event.h" 45#include "chrome/common/notification_service.h" 46#include "chrome/common/pref_names.h" 47#include "chrome/common/render_messages.h" 48#include "chrome/common/render_messages_params.h" 49#include "chrome/common/url_constants.h" 50#include "chrome/common/view_types.h" 51#include "grit/browser_resources.h" 52#include "grit/generated_resources.h" 53#include "webkit/glue/context_menu.h" 54 55#if defined(TOOLKIT_VIEWS) 56#include "views/widget/widget.h" 57#endif 58 59using WebKit::WebDragOperation; 60using WebKit::WebDragOperationsMask; 61 62// static 63bool ExtensionHost::enable_dom_automation_ = false; 64 65// Helper class that rate-limits the creation of renderer processes for 66// ExtensionHosts, to avoid blocking the UI. 67class ExtensionHost::ProcessCreationQueue { 68 public: 69 static ProcessCreationQueue* get() { 70 return Singleton<ProcessCreationQueue>::get(); 71 } 72 73 // Add a host to the queue for RenderView creation. 74 void CreateSoon(ExtensionHost* host) { 75 queue_.push_back(host); 76 PostTask(); 77 } 78 79 // Remove a host from the queue (in case it's being deleted). 80 void Remove(ExtensionHost* host) { 81 Queue::iterator it = std::find(queue_.begin(), queue_.end(), host); 82 if (it != queue_.end()) 83 queue_.erase(it); 84 } 85 86 private: 87 friend class Singleton<ProcessCreationQueue>; 88 friend struct DefaultSingletonTraits<ProcessCreationQueue>; 89 ProcessCreationQueue() 90 : pending_create_(false), 91 ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)) { } 92 93 // Queue up a delayed task to process the next ExtensionHost in the queue. 94 void PostTask() { 95 if (!pending_create_) { 96 MessageLoop::current()->PostTask(FROM_HERE, 97 method_factory_.NewRunnableMethod( 98 &ProcessCreationQueue::ProcessOneHost)); 99 pending_create_ = true; 100 } 101 } 102 103 // Create the RenderView for the next host in the queue. 104 void ProcessOneHost() { 105 pending_create_ = false; 106 if (queue_.empty()) 107 return; // can happen on shutdown 108 109 queue_.front()->CreateRenderViewNow(); 110 queue_.pop_front(); 111 112 if (!queue_.empty()) 113 PostTask(); 114 } 115 116 typedef std::list<ExtensionHost*> Queue; 117 Queue queue_; 118 bool pending_create_; 119 ScopedRunnableMethodFactory<ProcessCreationQueue> method_factory_; 120}; 121 122//////////////// 123// ExtensionHost 124 125ExtensionHost::ExtensionHost(const Extension* extension, 126 SiteInstance* site_instance, 127 const GURL& url, 128 ViewType::Type host_type) 129 : extension_(extension), 130 profile_(site_instance->browsing_instance()->profile()), 131 did_stop_loading_(false), 132 document_element_available_(false), 133 url_(url), 134 extension_host_type_(host_type), 135 associated_tab_contents_(NULL) { 136 render_view_host_ = new RenderViewHost(site_instance, this, MSG_ROUTING_NONE, 137 NULL); 138 render_view_host_->set_is_extension_process(true); 139 render_view_host_->AllowBindings(BindingsPolicy::EXTENSION); 140 if (enable_dom_automation_) 141 render_view_host_->AllowBindings(BindingsPolicy::DOM_AUTOMATION); 142 143 // Listen for when the render process' handle is available so we can add it 144 // to the task manager then. 145 registrar_.Add(this, NotificationType::RENDERER_PROCESS_CREATED, 146 Source<RenderProcessHost>(render_process_host())); 147 // Listen for when an extension is unloaded from the same profile, as it may 148 // be the same extension that this points to. 149 registrar_.Add(this, NotificationType::EXTENSION_UNLOADED, 150 Source<Profile>(profile_)); 151} 152 153ExtensionHost::~ExtensionHost() { 154 NotificationService::current()->Notify( 155 NotificationType::EXTENSION_HOST_DESTROYED, 156 Source<Profile>(profile_), 157 Details<ExtensionHost>(this)); 158 ProcessCreationQueue::get()->Remove(this); 159 render_view_host_->Shutdown(); // deletes render_view_host 160 161 if (recently_deleted()->size() >= 20) 162 recently_deleted()->pop_front(); 163 recently_deleted()->push_back(this); 164} 165 166ExtensionHost::HostPointerList* ExtensionHost::recently_deleted() { 167 return Singleton<HostPointerList>::get(); 168} 169 170void ExtensionHost::CreateView(Browser* browser) { 171#if defined(TOOLKIT_VIEWS) 172 view_.reset(new ExtensionView(this, browser)); 173 // We own |view_|, so don't auto delete when it's removed from the view 174 // hierarchy. 175 view_->set_parent_owned(false); 176#elif defined(OS_MACOSX) 177 view_.reset(new ExtensionViewMac(this, browser)); 178 view_->Init(); 179#elif defined(TOOLKIT_USES_GTK) 180 view_.reset(new ExtensionViewGtk(this, browser)); 181 view_->Init(); 182#else 183 // TODO(port) 184 NOTREACHED(); 185#endif 186} 187 188RenderProcessHost* ExtensionHost::render_process_host() const { 189 return render_view_host_->process(); 190} 191 192SiteInstance* ExtensionHost::site_instance() const { 193 return render_view_host_->site_instance(); 194} 195 196bool ExtensionHost::IsRenderViewLive() const { 197 return render_view_host_->IsRenderViewLive(); 198} 199 200void ExtensionHost::CreateRenderViewSoon(RenderWidgetHostView* host_view) { 201 render_view_host_->set_view(host_view); 202 if (render_view_host_->process()->HasConnection()) { 203 // If the process is already started, go ahead and initialize the RenderView 204 // synchronously. The process creation is the real meaty part that we want 205 // to defer. 206 CreateRenderViewNow(); 207 } else { 208 ProcessCreationQueue::get()->CreateSoon(this); 209 } 210} 211 212void ExtensionHost::CreateRenderViewNow() { 213 render_view_host_->CreateRenderView(string16()); 214 NavigateToURL(url_); 215 DCHECK(IsRenderViewLive()); 216 if (is_background_page()) 217 profile_->GetExtensionsService()->DidCreateRenderViewForBackgroundPage( 218 this); 219} 220 221Browser* ExtensionHost::GetBrowser() const { 222 return view() ? view()->browser() : NULL; 223} 224 225gfx::NativeView ExtensionHost::GetNativeViewOfHost() { 226 return view() ? view()->native_view() : NULL; 227} 228 229void ExtensionHost::NavigateToURL(const GURL& url) { 230 // Prevent explicit navigation to another extension id's pages. 231 // This method is only called by some APIs, so we still need to protect 232 // DidNavigate below (location = ""). 233 if (url.SchemeIs(chrome::kExtensionScheme) && 234 url.host() != extension_->id()) { 235 // TODO(erikkay) communicate this back to the caller? 236 return; 237 } 238 239 url_ = url; 240 241 if (!is_background_page() && 242 !profile_->GetExtensionsService()->IsBackgroundPageReady(extension_)) { 243 // Make sure the background page loads before any others. 244 registrar_.Add(this, NotificationType::EXTENSION_BACKGROUND_PAGE_READY, 245 Source<Extension>(extension_)); 246 return; 247 } 248 249 render_view_host_->NavigateToURL(url_); 250} 251 252void ExtensionHost::Observe(NotificationType type, 253 const NotificationSource& source, 254 const NotificationDetails& details) { 255 switch (type.value) { 256 case NotificationType::EXTENSION_BACKGROUND_PAGE_READY: 257 DCHECK(profile_->GetExtensionsService()-> 258 IsBackgroundPageReady(extension_)); 259 NavigateToURL(url_); 260 break; 261 case NotificationType::RENDERER_PROCESS_CREATED: 262 NotificationService::current()->Notify( 263 NotificationType::EXTENSION_PROCESS_CREATED, 264 Source<Profile>(profile_), 265 Details<ExtensionHost>(this)); 266 break; 267 case NotificationType::EXTENSION_UNLOADED: 268 // The extension object will be deleted after this notification has been 269 // sent. NULL it out so that dirty pointer issues don't arise in cases 270 // when multiple ExtensionHost objects pointing to the same Extension are 271 // present. 272 if (extension_ == Details<const Extension>(details).ptr()) 273 extension_ = NULL; 274 break; 275 default: 276 NOTREACHED() << "Unexpected notification sent."; 277 break; 278 } 279} 280 281void ExtensionHost::UpdatePreferredSize(const gfx::Size& new_size) { 282 if (view_.get()) 283 view_->UpdatePreferredSize(new_size); 284} 285 286void ExtensionHost::UpdateInspectorSetting(const std::string& key, 287 const std::string& value) { 288 RenderViewHostDelegateHelper::UpdateInspectorSetting(profile(), key, value); 289} 290 291void ExtensionHost::ClearInspectorSettings() { 292 RenderViewHostDelegateHelper::ClearInspectorSettings(profile()); 293} 294 295void ExtensionHost::RenderViewGone(RenderViewHost* render_view_host) { 296 // During browser shutdown, we may use sudden termination on an extension 297 // process, so it is expected to lose our connection to the render view. 298 // Do nothing. 299 if (browser_shutdown::GetShutdownType() != browser_shutdown::NOT_VALID) 300 return; 301 302 // In certain cases, multiple ExtensionHost objects may have pointed to 303 // the same Extension at some point (one with a background page and a 304 // popup, for example). When the first ExtensionHost goes away, the extension 305 // is unloaded, and any other host that pointed to that extension will have 306 // its pointer to it NULLed out so that any attempt to unload a dirty pointer 307 // will be averted. 308 if (!extension_) 309 return; 310 311 DCHECK_EQ(render_view_host_, render_view_host); 312 NotificationService::current()->Notify( 313 NotificationType::EXTENSION_PROCESS_TERMINATED, 314 Source<Profile>(profile_), 315 Details<ExtensionHost>(this)); 316} 317 318void ExtensionHost::DidNavigate(RenderViewHost* render_view_host, 319 const ViewHostMsg_FrameNavigate_Params& params) { 320 // We only care when the outer frame changes. 321 if (!PageTransition::IsMainFrame(params.transition)) 322 return; 323 324 if (!params.url.SchemeIs(chrome::kExtensionScheme)) { 325 extension_function_dispatcher_.reset(NULL); 326 url_ = params.url; 327 return; 328 } 329 330 // This catches two bogus use cases: 331 // (1) URLs that look like chrome-extension://somethingbogus or 332 // chrome-extension://nosuchid/, in other words, no Extension would 333 // be found. 334 // (2) URLs that refer to a different extension than this one. 335 // In both cases, we preserve the old URL and reset the EFD to NULL. This 336 // will leave the host in kind of a bad state with poor UI and errors, but 337 // it's better than the alternative. 338 // TODO(erikkay) Perhaps we should display errors in developer mode. 339 if (params.url.host() != extension_->id()) { 340 extension_function_dispatcher_.reset(NULL); 341 return; 342 } 343 344 url_ = params.url; 345 extension_function_dispatcher_.reset( 346 ExtensionFunctionDispatcher::Create(render_view_host_, this, url_)); 347} 348 349void ExtensionHost::InsertInfobarCSS() { 350 DCHECK(!is_background_page()); 351 352 static const base::StringPiece css( 353 ResourceBundle::GetSharedInstance().GetRawDataResource( 354 IDR_EXTENSIONS_INFOBAR_CSS)); 355 356 render_view_host()->InsertCSSInWebFrame( 357 L"", css.as_string(), "InfobarThemeCSS"); 358} 359 360void ExtensionHost::DisableScrollbarsForSmallWindows( 361 const gfx::Size& size_limit) { 362 render_view_host()->Send(new ViewMsg_DisableScrollbarsForSmallWindows( 363 render_view_host()->routing_id(), size_limit)); 364} 365 366void ExtensionHost::DidStopLoading() { 367 bool notify = !did_stop_loading_; 368 did_stop_loading_ = true; 369 if (extension_host_type_ == ViewType::EXTENSION_POPUP || 370 extension_host_type_ == ViewType::EXTENSION_INFOBAR) { 371#if defined(TOOLKIT_VIEWS) 372 if (view_.get()) 373 view_->DidStopLoading(); 374#endif 375 } 376 if (notify) { 377 NotificationService::current()->Notify( 378 NotificationType::EXTENSION_HOST_DID_STOP_LOADING, 379 Source<Profile>(profile_), 380 Details<ExtensionHost>(this)); 381 if (extension_host_type_ == ViewType::EXTENSION_BACKGROUND_PAGE) { 382 UMA_HISTOGRAM_TIMES("Extensions.BackgroundPageLoadTime", 383 since_created_.Elapsed()); 384 } else if (extension_host_type_ == ViewType::EXTENSION_POPUP) { 385 UMA_HISTOGRAM_TIMES("Extensions.PopupLoadTime", 386 since_created_.Elapsed()); 387 } else if (extension_host_type_ == ViewType::EXTENSION_INFOBAR) { 388 UMA_HISTOGRAM_TIMES("Extensions.InfobarLoadTime", 389 since_created_.Elapsed()); 390 } 391 } 392} 393 394void ExtensionHost::DocumentAvailableInMainFrame(RenderViewHost* rvh) { 395 // If the document has already been marked as available for this host, then 396 // bail. No need for the redundant setup. http://crbug.com/31170 397 if (document_element_available_) 398 return; 399 400 document_element_available_ = true; 401 if (is_background_page()) { 402 profile_->GetExtensionsService()->SetBackgroundPageReady(extension_); 403 } else { 404 switch (extension_host_type_) { 405 case ViewType::EXTENSION_INFOBAR: 406 InsertInfobarCSS(); 407 break; 408 default: 409 break; // No style sheet for other types, at the moment. 410 } 411 } 412} 413 414void ExtensionHost::DocumentOnLoadCompletedInMainFrame(RenderViewHost* rvh, 415 int32 page_id) { 416 if (ViewType::EXTENSION_POPUP == GetRenderViewType()) { 417 NotificationService::current()->Notify( 418 NotificationType::EXTENSION_POPUP_VIEW_READY, 419 Source<Profile>(profile_), 420 Details<ExtensionHost>(this)); 421 } 422} 423 424void ExtensionHost::RunJavaScriptMessage(const std::wstring& message, 425 const std::wstring& default_prompt, 426 const GURL& frame_url, 427 const int flags, 428 IPC::Message* reply_msg, 429 bool* did_suppress_message) { 430 *did_suppress_message = false; 431 // Unlike for page alerts, navigations aren't a good signal for when to 432 // resume showing alerts, so we can't reasonably stop showing them even if 433 // the extension is spammy. 434 RunJavascriptMessageBox(profile_, this, frame_url, flags, message, 435 default_prompt, false, reply_msg); 436} 437 438gfx::NativeWindow ExtensionHost::GetMessageBoxRootWindow() { 439 // If we have a view, use that. 440 gfx::NativeView native_view = GetNativeViewOfHost(); 441 if (native_view) 442 return platform_util::GetTopLevel(native_view); 443 444 // Otherwise, try the active tab's view. 445 Browser* browser = extension_function_dispatcher_->GetCurrentBrowser(true); 446 if (browser) { 447 TabContents* active_tab = browser->GetSelectedTabContents(); 448 if (active_tab) 449 return active_tab->view()->GetTopLevelNativeWindow(); 450 } 451 452 return NULL; 453} 454 455void ExtensionHost::OnMessageBoxClosed(IPC::Message* reply_msg, 456 bool success, 457 const std::wstring& prompt) { 458 render_view_host()->JavaScriptMessageBoxClosed(reply_msg, success, prompt); 459} 460 461void ExtensionHost::Close(RenderViewHost* render_view_host) { 462 if (extension_host_type_ == ViewType::EXTENSION_POPUP || 463 extension_host_type_ == ViewType::EXTENSION_INFOBAR) { 464 NotificationService::current()->Notify( 465 NotificationType::EXTENSION_HOST_VIEW_SHOULD_CLOSE, 466 Source<Profile>(profile_), 467 Details<ExtensionHost>(this)); 468 } 469} 470 471RendererPreferences ExtensionHost::GetRendererPrefs(Profile* profile) const { 472 RendererPreferences preferences; 473 474 TabContents* associated_contents = associated_tab_contents(); 475 if (associated_contents) 476 preferences = 477 static_cast<RenderViewHostDelegate*>(associated_contents)-> 478 GetRendererPrefs(profile); 479 480 renderer_preferences_util::UpdateFromSystemSettings(&preferences, profile); 481 return preferences; 482} 483 484WebPreferences ExtensionHost::GetWebkitPrefs() { 485 Profile* profile = render_view_host()->process()->profile(); 486 WebPreferences webkit_prefs = 487 RenderViewHostDelegateHelper::GetWebkitPrefs(profile, 488 false); // is_dom_ui 489 // Extensions are trusted so we override any user preferences for disabling 490 // javascript or images. 491 webkit_prefs.loads_images_automatically = true; 492 webkit_prefs.javascript_enabled = true; 493 494 if (extension_host_type_ == ViewType::EXTENSION_POPUP || 495 extension_host_type_ == ViewType::EXTENSION_INFOBAR) 496 webkit_prefs.allow_scripts_to_close_windows = true; 497 498 // TODO(dcheng): incorporate this setting into kClipboardPermission check. 499 webkit_prefs.javascript_can_access_clipboard = true; 500 501 // TODO(dcheng): check kClipboardPermission instead once it's implemented. 502 if (extension_->HasApiPermission(Extension::kExperimentalPermission)) 503 webkit_prefs.dom_paste_enabled = true; 504 return webkit_prefs; 505} 506 507void ExtensionHost::ProcessDOMUIMessage( 508 const ViewHostMsg_DomMessage_Params& params) { 509 if (extension_function_dispatcher_.get()) { 510 extension_function_dispatcher_->HandleRequest(params); 511 } 512} 513 514RenderViewHostDelegate::View* ExtensionHost::GetViewDelegate() { 515 return this; 516} 517 518void ExtensionHost::CreateNewWindow( 519 int route_id, 520 WindowContainerType window_container_type, 521 const string16& frame_name) { 522 // TODO(aa): Use the browser's profile if the extension is split mode 523 // incognito. 524 TabContents* new_contents = delegate_view_helper_.CreateNewWindow( 525 route_id, 526 render_view_host()->process()->profile(), 527 site_instance(), 528 DOMUIFactory::GetDOMUIType(render_view_host()->process()->profile(), 529 url_), 530 this, 531 window_container_type, 532 frame_name); 533 534 TabContents* associated_contents = associated_tab_contents(); 535 if (associated_contents && associated_contents->delegate()) 536 associated_contents->delegate()->TabContentsCreated(new_contents); 537} 538 539void ExtensionHost::CreateNewWidget(int route_id, 540 WebKit::WebPopupType popup_type) { 541 CreateNewWidgetInternal(route_id, popup_type); 542} 543 544void ExtensionHost::CreateNewFullscreenWidget(int route_id, 545 WebKit::WebPopupType popup_type) { 546 NOTREACHED() 547 << "ExtensionHost does not support showing full screen popups yet."; 548} 549 550RenderWidgetHostView* ExtensionHost::CreateNewWidgetInternal( 551 int route_id, WebKit::WebPopupType popup_type) { 552 return delegate_view_helper_.CreateNewWidget(route_id, popup_type, 553 site_instance()->GetProcess()); 554} 555 556void ExtensionHost::ShowCreatedWindow(int route_id, 557 WindowOpenDisposition disposition, 558 const gfx::Rect& initial_pos, 559 bool user_gesture) { 560 TabContents* contents = delegate_view_helper_.GetCreatedWindow(route_id); 561 if (!contents) 562 return; 563 564 if (disposition == NEW_POPUP) { 565 // Create a new Browser window of type TYPE_APP_POPUP. 566 // (AddTabContents would otherwise create a window of type TYPE_POPUP). 567 Browser* browser = Browser::CreateForPopup(Browser::TYPE_APP_POPUP, 568 contents->profile(), 569 contents, 570 initial_pos); 571 browser->window()->Show(); 572 return; 573 } 574 575 // If the tab contents isn't a popup, it's a normal tab. We need to find a 576 // home for it. This is typically a Browser, but it can also be some other 577 // TabContentsDelegate in the case of ChromeFrame. 578 579 // First, if the creating extension view was associated with a tab contents, 580 // use that tab content's delegate. We must be careful here that the 581 // associated tab contents has the same profile as the new tab contents. In 582 // the case of extensions in 'spanning' incognito mode, they can mismatch. 583 // We don't want to end up putting a normal tab into an incognito window, or 584 // vice versa. 585 TabContents* associated_contents = associated_tab_contents(); 586 if (associated_contents && 587 associated_contents->profile() == contents->profile()) { 588 associated_contents->AddNewContents(contents, disposition, initial_pos, 589 user_gesture); 590 return; 591 } 592 593 // If there's no associated tab contents, or it doesn't have a matching 594 // profile, try finding an open window. Again, we must make sure to find a 595 // window with the correct profile. 596 Browser* browser = BrowserList::FindBrowserWithType( 597 contents->profile(), 598 Browser::TYPE_NORMAL, 599 false); // Match incognito exactly. 600 601 // If there's no Browser open with the right profile, create a new one. 602 if (!browser) { 603 browser = Browser::Create(contents->profile()); 604 browser->window()->Show(); 605 } 606 607 if (browser) 608 browser->AddTabContents(contents, disposition, initial_pos, user_gesture); 609} 610 611void ExtensionHost::ShowCreatedWidget(int route_id, 612 const gfx::Rect& initial_pos) { 613 ShowCreatedWidgetInternal(delegate_view_helper_.GetCreatedWidget(route_id), 614 initial_pos); 615} 616 617void ExtensionHost::ShowCreatedFullscreenWidget(int route_id) { 618 NOTREACHED() 619 << "ExtensionHost does not support showing full screen popups yet."; 620} 621 622void ExtensionHost::ShowCreatedWidgetInternal( 623 RenderWidgetHostView* widget_host_view, 624 const gfx::Rect& initial_pos) { 625 Browser *browser = GetBrowser(); 626 DCHECK(browser); 627 if (!browser) 628 return; 629 browser->BrowserRenderWidgetShowing(); 630 // TODO(erikkay): These two lines could be refactored with TabContentsView. 631 widget_host_view->InitAsPopup(render_view_host()->view(), initial_pos); 632 widget_host_view->GetRenderWidgetHost()->Init(); 633} 634 635void ExtensionHost::ShowContextMenu(const ContextMenuParams& params) { 636 // TODO(erikkay) Show a default context menu. 637} 638 639void ExtensionHost::ShowPopupMenu(const gfx::Rect& bounds, 640 int item_height, 641 double item_font_size, 642 int selected_item, 643 const std::vector<WebMenuItem>& items, 644 bool right_aligned) { 645#if defined(OS_MACOSX) 646 PopupMenuHelper popup_menu_helper(render_view_host()); 647 popup_menu_helper.ShowPopupMenu(bounds, item_height, item_font_size, 648 selected_item, items, right_aligned); 649#else 650 // Only on Mac are select popup menus external. 651 NOTREACHED(); 652#endif 653} 654 655void ExtensionHost::StartDragging(const WebDropData& drop_data, 656 WebDragOperationsMask operation_mask, 657 const SkBitmap& image, 658 const gfx::Point& image_offset) { 659 // We're not going to do any drag & drop, but we have to tell the renderer the 660 // drag & drop ended, othewise the renderer thinks the drag operation is 661 // underway and mouse events won't work. See bug 34061. 662 // TODO(twiz) Implement drag & drop support for ExtensionHost instances. 663 // See feature issue 36288. 664 render_view_host()->DragSourceSystemDragEnded(); 665} 666 667void ExtensionHost::UpdateDragCursor(WebDragOperation operation) { 668} 669 670void ExtensionHost::GotFocus() { 671#if defined(TOOLKIT_VIEWS) 672 // Request focus so that the FocusManager has a focused view and can perform 673 // normally its key event processing (so that it lets tab key events go to the 674 // renderer). 675 view()->RequestFocus(); 676#else 677 // TODO(port) 678#endif 679} 680 681void ExtensionHost::TakeFocus(bool reverse) { 682} 683 684void ExtensionHost::LostCapture() { 685} 686 687void ExtensionHost::Activate() { 688} 689 690void ExtensionHost::Deactivate() { 691} 692 693bool ExtensionHost::PreHandleKeyboardEvent(const NativeWebKeyboardEvent& event, 694 bool* is_keyboard_shortcut) { 695 if (extension_host_type_ == ViewType::EXTENSION_POPUP && 696 event.type == NativeWebKeyboardEvent::RawKeyDown && 697 event.windowsKeyCode == app::VKEY_ESCAPE) { 698 DCHECK(is_keyboard_shortcut != NULL); 699 *is_keyboard_shortcut = true; 700 } 701 return false; 702} 703 704void ExtensionHost::HandleKeyboardEvent(const NativeWebKeyboardEvent& event) { 705 if (extension_host_type_ == ViewType::EXTENSION_POPUP) { 706 if (event.type == NativeWebKeyboardEvent::RawKeyDown && 707 event.windowsKeyCode == app::VKEY_ESCAPE) { 708 NotificationService::current()->Notify( 709 NotificationType::EXTENSION_HOST_VIEW_SHOULD_CLOSE, 710 Source<Profile>(profile_), 711 Details<ExtensionHost>(this)); 712 return; 713 } 714 } 715 UnhandledKeyboardEvent(event); 716} 717 718void ExtensionHost::HandleMouseMove() { 719#if defined(OS_WIN) 720 if (view_.get()) 721 view_->HandleMouseMove(); 722#endif 723} 724 725void ExtensionHost::HandleMouseDown() { 726} 727 728void ExtensionHost::HandleMouseLeave() { 729#if defined(OS_WIN) 730 if (view_.get()) 731 view_->HandleMouseLeave(); 732#endif 733} 734 735void ExtensionHost::HandleMouseUp() { 736} 737 738void ExtensionHost::HandleMouseActivate() { 739} 740 741ViewType::Type ExtensionHost::GetRenderViewType() const { 742 return extension_host_type_; 743} 744 745void ExtensionHost::RenderViewCreated(RenderViewHost* render_view_host) { 746 if (view_.get()) 747 view_->RenderViewCreated(); 748 749 // TODO(mpcomplete): This is duplicated in DidNavigate, which means that 750 // we'll create 2 EFDs for the first navigation. We should try to find a 751 // better way to unify them. 752 // See http://code.google.com/p/chromium/issues/detail?id=18240 753 extension_function_dispatcher_.reset( 754 ExtensionFunctionDispatcher::Create(render_view_host, this, url_)); 755 756 if (extension_host_type_ == ViewType::EXTENSION_POPUP || 757 extension_host_type_ == ViewType::EXTENSION_INFOBAR) { 758 render_view_host->EnablePreferredSizeChangedMode( 759 kPreferredSizeWidth | kPreferredSizeHeightThisIsSlow); 760 } 761} 762 763RenderViewHostDelegate::FileSelect* ExtensionHost::GetFileSelectDelegate() { 764 if (file_select_helper_.get() == NULL) 765 file_select_helper_.reset(new FileSelectHelper(profile())); 766 return file_select_helper_.get(); 767} 768 769int ExtensionHost::GetBrowserWindowID() const { 770 // Hosts not attached to any browser window have an id of -1. This includes 771 // those mentioned below, and background pages. 772 int window_id = extension_misc::kUnknownWindowId; 773 if (extension_host_type_ == ViewType::EXTENSION_POPUP || 774 extension_host_type_ == ViewType::EXTENSION_INFOBAR) { 775 // If the host is bound to a browser, then extract its window id. 776 // Extensions hosted in ExternalTabContainer objects may not have 777 // an associated browser. 778 Browser* browser = GetBrowser(); 779 if (browser) 780 window_id = ExtensionTabUtil::GetWindowId(browser); 781 } else if (extension_host_type_ != ViewType::EXTENSION_BACKGROUND_PAGE) { 782 NOTREACHED(); 783 } 784 return window_id; 785} 786