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
56using WebKit::WebDragOperation;
57using WebKit::WebDragOperationsMask;
58
59// static
60bool 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.
64class 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
122ExtensionHost::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
157ExtensionHost::~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
166void 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
184TabContents* ExtensionHost::associated_tab_contents() const {
185  return associated_tab_contents_;
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::GetInstance()->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_->GetExtensionService()->DidCreateRenderViewForBackgroundPage(
218        this);
219}
220
221const Browser* ExtensionHost::GetBrowser() const {
222  return view() ? view()->browser() : NULL;
223}
224
225Browser* ExtensionHost::GetBrowser() {
226  return view() ? view()->browser() : NULL;
227}
228
229gfx::NativeView ExtensionHost::GetNativeViewOfHost() {
230  return view() ? view()->native_view() : NULL;
231}
232
233void 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
255void 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
284void ExtensionHost::UpdatePreferredSize(const gfx::Size& new_size) {
285  if (view_.get())
286    view_->UpdatePreferredSize(new_size);
287}
288
289void ExtensionHost::UpdateInspectorSetting(const std::string& key,
290                                         const std::string& value) {
291  RenderViewHostDelegateHelper::UpdateInspectorSetting(profile(), key, value);
292}
293
294void ExtensionHost::ClearInspectorSettings() {
295  RenderViewHostDelegateHelper::ClearInspectorSettings(profile());
296}
297
298void 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
323void 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
354void 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
365void 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
371void 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
399void 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
419void 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
429void 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
460gfx::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
477TabContents* ExtensionHost::AsTabContents() {
478  return NULL;
479}
480
481ExtensionHost* ExtensionHost::AsExtensionHost() {
482  return this;
483}
484
485void 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
492void ExtensionHost::SetSuppressMessageBoxes(bool suppress_message_boxes) {
493  suppress_javascript_messages_ = suppress_message_boxes;
494}
495
496void 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
506RendererPreferences 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
519WebPreferences 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
550void ExtensionHost::ProcessWebUIMessage(
551    const ExtensionHostMsg_DomMessage_Params& params) {
552  if (extension_function_dispatcher_.get()) {
553    extension_function_dispatcher_->HandleRequest(params);
554  }
555}
556
557RenderViewHostDelegate::View* ExtensionHost::GetViewDelegate() {
558  return this;
559}
560
561void 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
581void ExtensionHost::CreateNewWidget(int route_id,
582                                    WebKit::WebPopupType popup_type) {
583  CreateNewWidgetInternal(route_id, popup_type);
584}
585
586void ExtensionHost::CreateNewFullscreenWidget(int route_id) {
587  NOTREACHED()
588      << "ExtensionHost does not support showing full screen popups yet.";
589}
590
591RenderWidgetHostView* 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
597void 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
653void ExtensionHost::ShowCreatedWidget(int route_id,
654                                      const gfx::Rect& initial_pos) {
655  ShowCreatedWidgetInternal(delegate_view_helper_.GetCreatedWidget(route_id),
656                            initial_pos);
657}
658
659void ExtensionHost::ShowCreatedFullscreenWidget(int route_id) {
660  NOTREACHED()
661      << "ExtensionHost does not support showing full screen popups yet.";
662}
663
664void 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
677void ExtensionHost::ShowContextMenu(const ContextMenuParams& params) {
678  // TODO(erikkay) Show a default context menu.
679}
680
681void 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
697void 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
709void ExtensionHost::UpdateDragCursor(WebDragOperation operation) {
710}
711
712void 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
723void ExtensionHost::TakeFocus(bool reverse) {
724}
725
726void ExtensionHost::LostCapture() {
727}
728
729void ExtensionHost::Activate() {
730}
731
732void ExtensionHost::Deactivate() {
733}
734
735bool 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
746void 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
760void ExtensionHost::HandleMouseMove() {
761#if defined(OS_WIN)
762  if (view_.get())
763    view_->HandleMouseMove();
764#endif
765}
766
767void ExtensionHost::HandleMouseDown() {
768}
769
770void ExtensionHost::HandleMouseLeave() {
771#if defined(OS_WIN)
772  if (view_.get())
773    view_->HandleMouseLeave();
774#endif
775}
776
777void ExtensionHost::HandleMouseUp() {
778}
779
780void ExtensionHost::HandleMouseActivate() {
781}
782
783ViewType::Type ExtensionHost::GetRenderViewType() const {
784  return extension_host_type_;
785}
786
787bool 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
796const GURL& ExtensionHost::GetURL() const {
797  return url_;
798}
799
800void 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
818int 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
836void 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