extension_host.cc revision c407dc5cd9bdc5668497f21b26b09d988ab439de
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/l10n_util.h"
10#include "app/resource_bundle.h"
11#include "base/keyboard_codes.h"
12#include "base/message_loop.h"
13#include "base/singleton.h"
14#include "base/string_util.h"
15#include "chrome/browser/browser.h"
16#include "chrome/browser/browser_list.h"
17#include "chrome/browser/browser_shutdown.h"
18#include "chrome/browser/browser_theme_provider.h"
19#include "chrome/browser/browsing_instance.h"
20#include "chrome/browser/debugger/devtools_manager.h"
21#include "chrome/browser/dom_ui/dom_ui_factory.h"
22#include "chrome/browser/extensions/extension_message_service.h"
23#include "chrome/browser/extensions/extension_tabs_module.h"
24#include "chrome/browser/extensions/extension_tabs_module_constants.h"
25#include "chrome/browser/extensions/extensions_service.h"
26#include "chrome/browser/in_process_webkit/dom_storage_context.h"
27#include "chrome/browser/in_process_webkit/webkit_context.h"
28#include "chrome/browser/message_box_handler.h"
29#include "chrome/browser/platform_util.h"
30#include "chrome/browser/pref_service.h"
31#include "chrome/browser/profile.h"
32#include "chrome/browser/renderer_host/render_view_host.h"
33#include "chrome/browser/renderer_host/render_process_host.h"
34#include "chrome/browser/renderer_host/render_widget_host.h"
35#include "chrome/browser/renderer_host/render_widget_host_view.h"
36#include "chrome/browser/renderer_host/site_instance.h"
37#include "chrome/browser/renderer_preferences_util.h"
38#include "chrome/browser/tab_contents/tab_contents.h"
39#include "chrome/browser/tab_contents/tab_contents_view.h"
40#include "chrome/common/bindings_policy.h"
41#include "chrome/common/extensions/extension.h"
42#include "chrome/common/extensions/extension_constants.h"
43#include "chrome/common/notification_service.h"
44#include "chrome/common/pref_names.h"
45#include "chrome/common/view_types.h"
46#include "chrome/common/render_messages.h"
47#include "chrome/common/url_constants.h"
48#include "grit/browser_resources.h"
49#include "grit/generated_resources.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
62static const char* kToolstripTextColorSubstitution = "$TEXT_COLOR$";
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* get() {
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(Extension* extension, SiteInstance* site_instance,
125                             const GURL& url, ViewType::Type host_type)
126    : extension_(extension),
127      profile_(site_instance->browsing_instance()->profile()),
128      did_stop_loading_(false),
129      document_element_available_(false),
130      url_(url),
131      extension_host_type_(host_type),
132      associated_tab_contents_(NULL) {
133  int64 session_storage_namespace_id = profile_->GetWebKitContext()->
134      dom_storage_context()->AllocateSessionStorageNamespaceId();
135  render_view_host_ = new RenderViewHost(site_instance, this, MSG_ROUTING_NONE,
136                                         session_storage_namespace_id);
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
152ExtensionHost::~ExtensionHost() {
153  NotificationService::current()->Notify(
154      NotificationType::EXTENSION_HOST_DESTROYED,
155      Source<Profile>(profile_),
156      Details<ExtensionHost>(this));
157  ProcessCreationQueue::get()->Remove(this);
158  render_view_host_->Shutdown();  // deletes render_view_host
159}
160
161void ExtensionHost::CreateView(Browser* browser) {
162#if defined(TOOLKIT_VIEWS)
163  view_.reset(new ExtensionView(this, browser));
164  // We own |view_|, so don't auto delete when it's removed from the view
165  // hierarchy.
166  view_->set_parent_owned(false);
167#elif defined(OS_MACOSX)
168  view_.reset(new ExtensionViewMac(this, browser));
169  view_->Init();
170#elif defined(TOOLKIT_USES_GTK)
171  view_.reset(new ExtensionViewGtk(this, browser));
172  view_->Init();
173#else
174  // TODO(port)
175  NOTREACHED();
176#endif
177}
178
179RenderProcessHost* ExtensionHost::render_process_host() const {
180  return render_view_host_->process();
181}
182
183SiteInstance* ExtensionHost::site_instance() const {
184  return render_view_host_->site_instance();
185}
186
187bool ExtensionHost::IsRenderViewLive() const {
188  return render_view_host_->IsRenderViewLive();
189}
190
191void ExtensionHost::CreateRenderViewSoon(RenderWidgetHostView* host_view) {
192  LOG(INFO) << "Creating RenderView for " + extension_->name();
193  render_view_host_->set_view(host_view);
194  if (render_view_host_->process()->HasConnection()) {
195    // If the process is already started, go ahead and initialize the RenderView
196    // synchronously. The process creation is the real meaty part that we want
197    // to defer.
198    CreateRenderViewNow();
199  } else {
200    ProcessCreationQueue::get()->CreateSoon(this);
201  }
202}
203
204void ExtensionHost::CreateRenderViewNow() {
205  render_view_host_->CreateRenderView(profile_->GetRequestContext(),
206                                      string16());
207  NavigateToURL(url_);
208  DCHECK(IsRenderViewLive());
209}
210
211void ExtensionHost::NavigateToURL(const GURL& url) {
212  LOG(INFO) << "Request to NavigateToURL " << url.spec() << " for "
213      << extension_->name();
214  // Prevent explicit navigation to another extension id's pages.
215  // This method is only called by some APIs, so we still need to protect
216  // DidNavigate below (location = "").
217  if (url.SchemeIs(chrome::kExtensionScheme) &&
218      url.host() != extension_->id()) {
219    // TODO(erikkay) communicate this back to the caller?
220    return;
221  }
222
223  url_ = url;
224
225  if (!is_background_page() && !extension_->GetBackgroundPageReady()) {
226    LOG(INFO) << "...Waiting on EXTENSION_BACKGROUND_PAGE_READY";
227    // Make sure the background page loads before any others.
228    registrar_.Add(this, NotificationType::EXTENSION_BACKGROUND_PAGE_READY,
229                   Source<Extension>(extension_));
230    return;
231  }
232
233  LOG(INFO) << "Navigating to " << url_.spec();
234  render_view_host_->NavigateToURL(url_);
235}
236
237void ExtensionHost::Observe(NotificationType type,
238                            const NotificationSource& source,
239                            const NotificationDetails& details) {
240  switch (type.value) {
241    case NotificationType::EXTENSION_BACKGROUND_PAGE_READY:
242      DCHECK(extension_->GetBackgroundPageReady());
243      NavigateToURL(url_);
244      break;
245    case NotificationType::BROWSER_THEME_CHANGED:
246      if (extension_host_type_ == ViewType::EXTENSION_TOOLSTRIP ||
247          extension_host_type_ == ViewType::EXTENSION_MOLE) {
248        InsertThemedToolstripCSS();
249      }
250      break;
251    case NotificationType::RENDERER_PROCESS_CREATED:
252      LOG(INFO) << "Sending EXTENSION_PROCESS_CREATED";
253      NotificationService::current()->Notify(
254          NotificationType::EXTENSION_PROCESS_CREATED,
255          Source<Profile>(profile_),
256          Details<ExtensionHost>(this));
257      break;
258    case NotificationType::EXTENSION_UNLOADED:
259      // The extension object will be deleted after this notification has been
260      // sent. NULL it out so that dirty pointer issues don't arise in cases
261      // when multiple ExtensionHost objects pointing to the same Extension are
262      // present.
263      if (extension_ == Details<Extension>(details).ptr())
264        extension_ = NULL;
265      break;
266    default:
267      NOTREACHED() << "Unexpected notification sent.";
268      break;
269  }
270}
271
272void ExtensionHost::UpdatePreferredSize(const gfx::Size& new_size) {
273  if (view_.get())
274    view_->UpdatePreferredSize(new_size);
275}
276
277void ExtensionHost::RenderViewGone(RenderViewHost* render_view_host) {
278  // During browser shutdown, we may use sudden termination on an extension
279  // process, so it is expected to lose our connection to the render view.
280  // Do nothing.
281  if (browser_shutdown::GetShutdownType() != browser_shutdown::NOT_VALID)
282    return;
283
284  // In certain cases, multiple ExtensionHost objects may have pointed to
285  // the same Extension at some point (one with a background page and a
286  // popup, for example). When the first ExtensionHost goes away, the extension
287  // is unloaded, and any other host that pointed to that extension will have
288  // its pointer to it NULLed out so that any attempt to unload a dirty pointer
289  // will be averted.
290  if (!extension_)
291    return;
292
293  LOG(INFO) << "Sending EXTENSION_PROCESS_TERMINATED for " + extension_->name();
294  DCHECK_EQ(render_view_host_, render_view_host);
295  NotificationService::current()->Notify(
296      NotificationType::EXTENSION_PROCESS_TERMINATED,
297      Source<Profile>(profile_),
298      Details<ExtensionHost>(this));
299}
300
301void ExtensionHost::DidNavigate(RenderViewHost* render_view_host,
302    const ViewHostMsg_FrameNavigate_Params& params) {
303  // We only care when the outer frame changes.
304  if (!PageTransition::IsMainFrame(params.transition))
305    return;
306
307  if (!params.url.SchemeIs(chrome::kExtensionScheme)) {
308    extension_function_dispatcher_.reset(NULL);
309    url_ = params.url;
310    return;
311  }
312
313  // This catches two bogus use cases:
314  // (1) URLs that look like chrome-extension://somethingbogus or
315  //     chrome-extension://nosuchid/, in other words, no Extension would
316  //     be found.
317  // (2) URLs that refer to a different extension than this one.
318  // In both cases, we preserve the old URL and reset the EFD to NULL.  This
319  // will leave the host in kind of a bad state with poor UI and errors, but
320  // it's better than the alternative.
321  // TODO(erikkay) Perhaps we should display log errors or display a big 404
322  // in the toolstrip or something like that.
323  if (params.url.host() != extension_->id()) {
324    extension_function_dispatcher_.reset(NULL);
325    return;
326  }
327
328  LOG(INFO) << "(DidNavigate) Resetting EFD to " << url_.spec() << " for "
329      << extension_->name();
330  url_ = params.url;
331  extension_function_dispatcher_.reset(
332      ExtensionFunctionDispatcher::Create(render_view_host_, this, url_));
333}
334
335void ExtensionHost::InsertInfobarCSS() {
336  DCHECK(!is_background_page());
337
338  static const base::StringPiece css(
339      ResourceBundle::GetSharedInstance().GetRawDataResource(
340      IDR_EXTENSIONS_INFOBAR_CSS));
341
342  render_view_host()->InsertCSSInWebFrame(
343      L"", css.as_string(), "InfobarThemeCSS");
344}
345
346void ExtensionHost::InsertThemedToolstripCSS() {
347  DCHECK(!is_background_page());
348
349  static const base::StringPiece toolstrip_theme_css(
350      ResourceBundle::GetSharedInstance().GetRawDataResource(
351      IDR_EXTENSIONS_TOOLSTRIP_THEME_CSS));
352
353  std::string css = toolstrip_theme_css.as_string();
354  ThemeProvider* theme_provider =
355      render_view_host()->process()->profile()->GetThemeProvider();
356
357  SkColor text_color = theme_provider ?
358      theme_provider->GetColor(BrowserThemeProvider::COLOR_BOOKMARK_TEXT) :
359      SK_ColorBLACK;
360
361  std::string hex_color_string = StringPrintf(
362      "#%02x%02x%02x", SkColorGetR(text_color),
363                       SkColorGetG(text_color),
364                       SkColorGetB(text_color));
365  size_t pos = css.find(kToolstripTextColorSubstitution);
366  while (pos != std::string::npos) {
367    css.replace(pos, 12, hex_color_string);
368    pos = css.find(kToolstripTextColorSubstitution);
369  }
370
371  // As a toolstrip, inject our toolstrip CSS to make it easier for toolstrips
372  // to blend in with the chrome UI.
373  render_view_host()->InsertCSSInWebFrame(L"", css, "ToolstripThemeCSS");
374}
375
376void ExtensionHost::DisableScrollbarsForSmallWindows(
377    const gfx::Size& size_limit) {
378  render_view_host()->Send(new ViewMsg_DisableScrollbarsForSmallWindows(
379      render_view_host()->routing_id(), size_limit));
380}
381
382void ExtensionHost::DidStopLoading() {
383  bool notify = !did_stop_loading_;
384  did_stop_loading_ = true;
385  if (extension_host_type_ == ViewType::EXTENSION_TOOLSTRIP ||
386      extension_host_type_ == ViewType::EXTENSION_MOLE ||
387      extension_host_type_ == ViewType::EXTENSION_POPUP ||
388      extension_host_type_ == ViewType::EXTENSION_INFOBAR) {
389#if defined(TOOLKIT_VIEWS)
390    if (view_.get())
391      view_->DidStopLoading();
392#endif
393  }
394  if (notify) {
395    LOG(INFO) << "Sending EXTENSION_HOST_DID_STOP_LOADING";
396    NotificationService::current()->Notify(
397        NotificationType::EXTENSION_HOST_DID_STOP_LOADING,
398        Source<Profile>(profile_),
399        Details<ExtensionHost>(this));
400    if (extension_host_type_ == ViewType::EXTENSION_BACKGROUND_PAGE) {
401      UMA_HISTOGRAM_TIMES("Extensions.BackgroundPageLoadTime",
402                          since_created_.Elapsed());
403    } else if (extension_host_type_ == ViewType::EXTENSION_POPUP) {
404      UMA_HISTOGRAM_TIMES("Extensions.PopupLoadTime",
405                          since_created_.Elapsed());
406    } else if (extension_host_type_ == ViewType::EXTENSION_TOOLSTRIP) {
407      UMA_HISTOGRAM_TIMES("Extensions.ToolstripLoadTime",
408                          since_created_.Elapsed());
409    } else if (extension_host_type_ == ViewType::EXTENSION_INFOBAR) {
410      UMA_HISTOGRAM_TIMES("Extensions.InfobarLoadTime",
411        since_created_.Elapsed());
412    }
413  }
414}
415
416void ExtensionHost::DocumentAvailableInMainFrame(RenderViewHost* rvh) {
417  // If the document has already been marked as available for this host, then
418  // bail. No need for the redundant setup. http://crbug.com/31170
419  if (document_element_available_)
420    return;
421
422  document_element_available_ = true;
423  if (is_background_page()) {
424    extension_->SetBackgroundPageReady();
425  } else {
426    switch (extension_host_type_) {
427      case ViewType::EXTENSION_INFOBAR:
428        InsertInfobarCSS();
429        break;
430      case ViewType::EXTENSION_TOOLSTRIP:
431      case ViewType::EXTENSION_MOLE:
432        // See also BROWSER_THEME_CHANGED in the Observe function.
433        InsertThemedToolstripCSS();
434
435        // Listen for browser changes so we can resend the CSS.
436        registrar_.Add(this, NotificationType::BROWSER_THEME_CHANGED,
437                       NotificationService::AllSources());
438        break;
439      default:
440        break;  // No style sheet for other types, at the moment.
441    }
442  }
443}
444
445void ExtensionHost::DocumentOnLoadCompletedInMainFrame(RenderViewHost* rvh) {
446  if (ViewType::EXTENSION_POPUP == GetRenderViewType()) {
447    NotificationService::current()->Notify(
448        NotificationType::EXTENSION_POPUP_VIEW_READY,
449        Source<Profile>(profile_),
450        Details<ExtensionHost>(this));
451  }
452}
453
454void ExtensionHost::RunJavaScriptMessage(const std::wstring& message,
455                                         const std::wstring& default_prompt,
456                                         const GURL& frame_url,
457                                         const int flags,
458                                         IPC::Message* reply_msg,
459                                         bool* did_suppress_message) {
460  *did_suppress_message = false;
461  // Unlike for page alerts, navigations aren't a good signal for when to
462  // resume showing alerts, so we can't reasonably stop showing them even if
463  // the extension is spammy.
464  RunJavascriptMessageBox(this, frame_url, flags, message, default_prompt,
465                          false, reply_msg);
466}
467
468std::wstring ExtensionHost::GetMessageBoxTitle(const GURL& frame_url,
469                                               bool is_alert) {
470  if (extension_->name().empty())
471    return l10n_util::GetString(
472        is_alert ? IDS_EXTENSION_ALERT_DEFAULT_TITLE
473                 : IDS_EXTENSION_MESSAGEBOX_DEFAULT_TITLE);
474
475  return l10n_util::GetStringF(
476      is_alert ? IDS_EXTENSION_ALERT_TITLE : IDS_EXTENSION_MESSAGEBOX_TITLE,
477      UTF8ToWide(extension_->name()));
478}
479
480gfx::NativeWindow ExtensionHost::GetMessageBoxRootWindow() {
481  // If we have a view, use that.
482  gfx::NativeView native_view = GetNativeViewOfHost();
483  if (native_view)
484    return platform_util::GetTopLevel(native_view);
485
486  // Otherwise, try the active tab's view.
487  Browser* browser = extension_function_dispatcher_->GetCurrentBrowser(true);
488  if (browser) {
489    TabContents* active_tab = browser->GetSelectedTabContents();
490    if (active_tab)
491      return active_tab->view()->GetTopLevelNativeWindow();
492  }
493
494  return NULL;
495}
496
497void ExtensionHost::OnMessageBoxClosed(IPC::Message* reply_msg,
498                                       bool success,
499                                       const std::wstring& prompt) {
500  render_view_host()->JavaScriptMessageBoxClosed(reply_msg, success, prompt);
501}
502
503void ExtensionHost::Close(RenderViewHost* render_view_host) {
504  if (extension_host_type_ == ViewType::EXTENSION_POPUP ||
505      extension_host_type_ == ViewType::EXTENSION_INFOBAR) {
506    NotificationService::current()->Notify(
507        NotificationType::EXTENSION_HOST_VIEW_SHOULD_CLOSE,
508        Source<Profile>(profile_),
509        Details<ExtensionHost>(this));
510  }
511}
512
513RendererPreferences ExtensionHost::GetRendererPrefs(Profile* profile) const {
514  RendererPreferences preferences;
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_dom_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  // TODO(dcheng): incorporate this setting into kClipboardPermission check.
534  webkit_prefs.javascript_can_access_clipboard = true;
535
536  // TODO(dcheng): check kClipboardPermission instead once it's implemented.
537  if (extension_->HasApiPermission(Extension::kExperimentalPermission))
538    webkit_prefs.dom_paste_enabled = true;
539  return webkit_prefs;
540}
541
542void ExtensionHost::ProcessDOMUIMessage(const std::string& message,
543                                        const ListValue* content,
544                                        const GURL& source_url,
545                                        int request_id,
546                                        bool has_callback) {
547  if (extension_function_dispatcher_.get()) {
548    extension_function_dispatcher_->HandleRequest(
549        message, content, source_url, request_id, has_callback);
550  }
551}
552
553RenderViewHostDelegate::View* ExtensionHost::GetViewDelegate() {
554  return this;
555}
556
557void ExtensionHost::CreateNewWindow(
558    int route_id,
559    WindowContainerType window_container_type,
560    const string16& frame_name) {
561  delegate_view_helper_.CreateNewWindow(
562      route_id,
563      render_view_host()->process()->profile(),
564      site_instance(),
565      DOMUIFactory::GetDOMUIType(url_),
566      this,
567      window_container_type,
568      frame_name);
569}
570
571void ExtensionHost::CreateNewWidget(int route_id,
572                                    WebKit::WebPopupType popup_type) {
573  CreateNewWidgetInternal(route_id, popup_type);
574}
575
576RenderWidgetHostView* ExtensionHost::CreateNewWidgetInternal(
577    int route_id, WebKit::WebPopupType popup_type) {
578  return delegate_view_helper_.CreateNewWidget(route_id, popup_type,
579                                               site_instance()->GetProcess());
580}
581
582void ExtensionHost::ShowCreatedWindow(int route_id,
583                                      WindowOpenDisposition disposition,
584                                      const gfx::Rect& initial_pos,
585                                      bool user_gesture) {
586  TabContents* contents = delegate_view_helper_.GetCreatedWindow(route_id);
587  if (!contents)
588    return;
589
590  Browser* browser = extension_function_dispatcher_->GetCurrentBrowser(
591      profile_->GetExtensionsService()->IsIncognitoEnabled(extension_));
592  if (!browser)
593    return;
594
595  browser->AddTabContents(contents, disposition, initial_pos, user_gesture);
596}
597
598void ExtensionHost::ShowCreatedWidget(int route_id,
599                                      const gfx::Rect& initial_pos) {
600  ShowCreatedWidgetInternal(delegate_view_helper_.GetCreatedWidget(route_id),
601                            initial_pos);
602}
603
604void ExtensionHost::ShowCreatedWidgetInternal(
605    RenderWidgetHostView* widget_host_view,
606    const gfx::Rect& initial_pos) {
607  Browser *browser = GetBrowser();
608  DCHECK(browser);
609  if (!browser)
610    return;
611  browser->BrowserRenderWidgetShowing();
612  // TODO(erikkay): These two lines could be refactored with TabContentsView.
613  widget_host_view->InitAsPopup(render_view_host()->view(), initial_pos);
614  widget_host_view->GetRenderWidgetHost()->Init();
615}
616
617void ExtensionHost::ShowContextMenu(const ContextMenuParams& params) {
618  // TODO(erikkay) Show a default context menu.
619}
620
621void ExtensionHost::StartDragging(const WebDropData& drop_data,
622    WebDragOperationsMask operation_mask,
623    const SkBitmap& image,
624    const gfx::Point& image_offset) {
625  // We're not going to do any drag & drop, but we have to tell the renderer the
626  // drag & drop ended, othewise the renderer thinks the drag operation is
627  // underway and mouse events won't work.  See bug 34061.
628  // TODO(twiz) Implement drag & drop support for ExtensionHost instances.
629  // See feature issue 36288.
630  render_view_host()->DragSourceSystemDragEnded();
631}
632
633void ExtensionHost::UpdateDragCursor(WebDragOperation operation) {
634}
635
636void ExtensionHost::GotFocus() {
637#if defined(TOOLKIT_VIEWS)
638  // Request focus so that the FocusManager has a focused view and can perform
639  // normally its key event processing (so that it lets tab key events go to the
640  // renderer).
641  view()->RequestFocus();
642#else
643  // TODO(port)
644#endif
645}
646
647void ExtensionHost::TakeFocus(bool reverse) {
648}
649
650bool ExtensionHost::PreHandleKeyboardEvent(const NativeWebKeyboardEvent& event,
651                                           bool* is_keyboard_shortcut) {
652  if (extension_host_type_ == ViewType::EXTENSION_POPUP &&
653      event.type == NativeWebKeyboardEvent::RawKeyDown &&
654      event.windowsKeyCode == base::VKEY_ESCAPE) {
655    DCHECK(is_keyboard_shortcut != NULL);
656    *is_keyboard_shortcut = true;
657  }
658  return false;
659}
660
661void ExtensionHost::HandleKeyboardEvent(const NativeWebKeyboardEvent& event) {
662  if (extension_host_type_ == ViewType::EXTENSION_POPUP) {
663    if (event.type == NativeWebKeyboardEvent::RawKeyDown &&
664        event.windowsKeyCode == base::VKEY_ESCAPE) {
665      NotificationService::current()->Notify(
666          NotificationType::EXTENSION_HOST_VIEW_SHOULD_CLOSE,
667          Source<Profile>(profile_),
668          Details<ExtensionHost>(this));
669      return;
670    }
671  }
672  UnhandledKeyboardEvent(event);
673}
674
675void ExtensionHost::HandleMouseEvent() {
676#if defined(OS_WIN)
677  if (view_.get())
678    view_->HandleMouseEvent();
679#endif
680}
681
682void ExtensionHost::HandleMouseLeave() {
683#if defined(OS_WIN)
684  if (view_.get())
685    view_->HandleMouseLeave();
686#endif
687}
688
689void ExtensionHost::SetRenderViewType(ViewType::Type type) {
690  DCHECK(type == ViewType::EXTENSION_MOLE ||
691         type == ViewType::EXTENSION_TOOLSTRIP ||
692         type == ViewType::EXTENSION_POPUP);
693  extension_host_type_ = type;
694  render_view_host()->ViewTypeChanged(extension_host_type_);
695}
696
697ViewType::Type ExtensionHost::GetRenderViewType() const {
698  return extension_host_type_;
699}
700
701void ExtensionHost::RenderViewCreated(RenderViewHost* render_view_host) {
702  if (view_.get())
703    view_->RenderViewCreated();
704
705  // TODO(mpcomplete): This is duplicated in DidNavigate, which means that
706  // we'll create 2 EFDs for the first navigation. We should try to find a
707  // better way to unify them.
708  // See http://code.google.com/p/chromium/issues/detail?id=18240
709  LOG(INFO) << "(RenderViewCreated) Resetting EFD to " << url_.spec() << " for "
710      << extension_->name();
711  extension_function_dispatcher_.reset(
712      ExtensionFunctionDispatcher::Create(render_view_host, this, url_));
713
714  if (extension_host_type_ == ViewType::EXTENSION_TOOLSTRIP ||
715      extension_host_type_ == ViewType::EXTENSION_MOLE ||
716      extension_host_type_ == ViewType::EXTENSION_POPUP ||
717      extension_host_type_ == ViewType::EXTENSION_INFOBAR) {
718    render_view_host->EnablePreferredSizeChangedMode(
719        kPreferredSizeWidth | kPreferredSizeHeightThisIsSlow);
720  }
721}
722
723int ExtensionHost::GetBrowserWindowID() const {
724  // Hosts not attached to any browser window have an id of -1.  This includes
725  // those mentioned below, and background pages.
726  int window_id = extension_misc::kUnknownWindowId;
727  if (extension_host_type_ == ViewType::EXTENSION_TOOLSTRIP ||
728      extension_host_type_ == ViewType::EXTENSION_MOLE ||
729      extension_host_type_ == ViewType::EXTENSION_POPUP ||
730      extension_host_type_ == ViewType::EXTENSION_INFOBAR) {
731    // If the host is bound to a browser, then extract its window id.
732    // Extensions hosted in ExternalTabContainer objects may not have
733    // an associated browser.
734    Browser* browser = GetBrowser();
735    if (browser)
736      window_id = ExtensionTabUtil::GetWindowId(browser);
737  } else if (extension_host_type_ != ViewType::EXTENSION_BACKGROUND_PAGE) {
738    NOTREACHED();
739  }
740  return window_id;
741}
742