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