Home | History | Annotate | Download | only in extensions
      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