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