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