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