1 // Copyright (c) 2012 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.
5 #include "chrome/browser/extensions/extension_host.h"
10 #include "base/logging.h"
11 #include "base/memory/singleton.h"
12 #include "base/memory/weak_ptr.h"
13 #include "base/message_loop/message_loop.h"
14 #include "base/metrics/histogram.h"
15 #include "base/strings/string_util.h"
16 #include "base/strings/utf_string_conversions.h"
17 #include "chrome/browser/browser_shutdown.h"
18 #include "chrome/browser/chrome_notification_types.h"
19 #include "chrome/browser/extensions/error_console/error_console.h"
20 #include "chrome/browser/extensions/event_router.h"
21 #include "chrome/browser/extensions/extension_process_manager.h"
22 #include "chrome/browser/extensions/extension_service.h"
23 #include "chrome/browser/extensions/extension_system.h"
24 #include "chrome/browser/extensions/extension_tab_util.h"
25 #include "chrome/browser/extensions/extension_web_contents_observer.h"
26 #include "chrome/browser/extensions/window_controller.h"
27 #include "chrome/browser/file_select_helper.h"
28 #include "chrome/browser/media/media_capture_devices_dispatcher.h"
29 #include "chrome/browser/profiles/profile.h"
30 #include "chrome/browser/ui/app_modal_dialogs/javascript_dialog_manager.h"
31 #include "chrome/browser/ui/browser.h"
32 #include "chrome/browser/ui/browser_dialogs.h"
33 #include "chrome/browser/ui/browser_finder.h"
34 #include "chrome/browser/ui/browser_list.h"
35 #include "chrome/browser/ui/browser_window.h"
36 #include "chrome/browser/ui/prefs/prefs_tab_helper.h"
37 #include "chrome/common/chrome_constants.h"
38 #include "chrome/common/extensions/background_info.h"
39 #include "chrome/common/extensions/extension.h"
40 #include "chrome/common/extensions/extension_constants.h"
41 #include "chrome/common/extensions/extension_messages.h"
42 #include "chrome/common/extensions/feature_switch.h"
43 #include "chrome/common/render_messages.h"
44 #include "chrome/common/url_constants.h"
45 #include "content/public/browser/content_browser_client.h"
46 #include "content/public/browser/native_web_keyboard_event.h"
47 #include "content/public/browser/notification_service.h"
48 #include "content/public/browser/render_process_host.h"
49 #include "content/public/browser/render_view_host.h"
50 #include "content/public/browser/render_widget_host_view.h"
51 #include "content/public/browser/site_instance.h"
52 #include "content/public/browser/web_contents.h"
53 #include "content/public/browser/web_contents_view.h"
54 #include "extensions/browser/extension_error.h"
55 #include "extensions/browser/view_type_utils.h"
56 #include "extensions/common/extension_urls.h"
57 #include "grit/browser_resources.h"
58 #include "grit/chromium_strings.h"
59 #include "grit/generated_resources.h"
60 #include "ui/base/l10n/l10n_util.h"
61 #include "ui/base/resource/resource_bundle.h"
62 #include "ui/events/keycodes/keyboard_codes.h"
64 #if !defined(OS_ANDROID)
65 #include "components/web_modal/web_contents_modal_dialog_manager.h"
68 using WebKit::WebDragOperation;
69 using WebKit::WebDragOperationsMask;
70 using content::NativeWebKeyboardEvent;
71 using content::OpenURLParams;
72 using content::RenderViewHost;
73 using content::SiteInstance;
74 using content::WebContents;
76 namespace extensions {
78 // Helper class that rate-limits the creation of renderer processes for
79 // ExtensionHosts, to avoid blocking the UI.
80 class ExtensionHost::ProcessCreationQueue {
82 static ProcessCreationQueue* GetInstance() {
83 return Singleton<ProcessCreationQueue>::get();
86 // Add a host to the queue for RenderView creation.
87 void CreateSoon(ExtensionHost* host) {
88 queue_.push_back(host);
92 // Remove a host from the queue (in case it's being deleted).
93 void Remove(ExtensionHost* host) {
94 Queue::iterator it = std::find(queue_.begin(), queue_.end(), host);
95 if (it != queue_.end())
100 friend class Singleton<ProcessCreationQueue>;
101 friend struct DefaultSingletonTraits<ProcessCreationQueue>;
102 ProcessCreationQueue()
103 : pending_create_(false),
104 ptr_factory_(this) {}
106 // Queue up a delayed task to process the next ExtensionHost in the queue.
108 if (!pending_create_) {
109 base::MessageLoop::current()->PostTask(FROM_HERE,
110 base::Bind(&ProcessCreationQueue::ProcessOneHost,
111 ptr_factory_.GetWeakPtr()));
112 pending_create_ = true;
116 // Create the RenderView for the next host in the queue.
117 void ProcessOneHost() {
118 pending_create_ = false;
120 return; // can happen on shutdown
122 queue_.front()->CreateRenderViewNow();
129 typedef std::list<ExtensionHost*> Queue;
131 bool pending_create_;
132 base::WeakPtrFactory<ProcessCreationQueue> ptr_factory_;
138 ExtensionHost::ExtensionHost(const Extension* extension,
139 SiteInstance* site_instance,
142 : extension_(extension),
143 extension_id_(extension->id()),
144 profile_(Profile::FromBrowserContext(
145 site_instance->GetBrowserContext())),
146 render_view_host_(NULL),
147 did_stop_loading_(false),
148 document_element_available_(false),
150 extension_function_dispatcher_(profile_, this),
151 extension_host_type_(host_type),
152 associated_web_contents_(NULL) {
153 host_contents_.reset(WebContents::Create(
154 WebContents::CreateParams(profile_, site_instance))),
155 content::WebContentsObserver::Observe(host_contents_.get());
156 host_contents_->SetDelegate(this);
157 SetViewType(host_contents_.get(), host_type);
159 ExtensionWebContentsObserver::CreateForWebContents(host_contents());
160 PrefsTabHelper::CreateForWebContents(host_contents());
162 render_view_host_ = host_contents_->GetRenderViewHost();
164 // Listen for when an extension is unloaded from the same profile, as it may
165 // be the same extension that this points to.
166 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNLOADED,
167 content::Source<Profile>(profile_));
170 ExtensionHost::~ExtensionHost() {
171 if (extension_host_type_ == VIEW_TYPE_EXTENSION_BACKGROUND_PAGE &&
172 extension_ && BackgroundInfo::HasLazyBackgroundPage(extension_)) {
173 UMA_HISTOGRAM_LONG_TIMES("Extensions.EventPageActiveTime",
174 since_created_.Elapsed());
176 content::NotificationService::current()->Notify(
177 chrome::NOTIFICATION_EXTENSION_HOST_DESTROYED,
178 content::Source<Profile>(profile_),
179 content::Details<ExtensionHost>(this));
180 ProcessCreationQueue::GetInstance()->Remove(this);
183 void ExtensionHost::CreateView(Browser* browser) {
184 #if defined(TOOLKIT_VIEWS)
185 view_.reset(new ExtensionViewViews(this, browser));
186 // We own |view_|, so don't auto delete when it's removed from the view
188 view_->set_owned_by_client();
189 #elif defined(OS_MACOSX)
190 view_.reset(new ExtensionViewMac(this, browser));
192 #elif defined(TOOLKIT_GTK)
193 view_.reset(new ExtensionViewGtk(this, browser));
201 WebContents* ExtensionHost::GetAssociatedWebContents() const {
202 return associated_web_contents_;
205 WebContents* ExtensionHost::GetVisibleWebContents() const {
206 if (associated_web_contents_)
207 return associated_web_contents_;
208 if ((extension_host_type_ == VIEW_TYPE_EXTENSION_POPUP) ||
209 (extension_host_type_ == VIEW_TYPE_PANEL))
210 return host_contents_.get();
214 void ExtensionHost::SetAssociatedWebContents(
215 content::WebContents* web_contents) {
216 associated_web_contents_ = web_contents;
218 registrar_.Add(this, content::NOTIFICATION_WEB_CONTENTS_DESTROYED,
219 content::Source<WebContents>(associated_web_contents_));
223 content::RenderProcessHost* ExtensionHost::render_process_host() const {
224 return render_view_host()->GetProcess();
227 RenderViewHost* ExtensionHost::render_view_host() const {
228 // TODO(mpcomplete): This can be NULL. How do we handle that?
229 return render_view_host_;
232 bool ExtensionHost::IsRenderViewLive() const {
233 return render_view_host()->IsRenderViewLive();
236 void ExtensionHost::CreateRenderViewSoon() {
237 if ((render_process_host() && render_process_host()->HasConnection())) {
238 // If the process is already started, go ahead and initialize the RenderView
239 // synchronously. The process creation is the real meaty part that we want
241 CreateRenderViewNow();
243 ProcessCreationQueue::GetInstance()->CreateSoon(this);
247 void ExtensionHost::CreateRenderViewNow() {
249 if (is_background_page()) {
250 DCHECK(IsRenderViewLive());
251 ExtensionSystem::Get(profile_)->extension_service()->
252 DidCreateRenderViewForBackgroundPage(this);
256 WindowController* ExtensionHost::GetExtensionWindowController() const {
257 return view() && view()->browser() ?
258 view()->browser()->extension_window_controller() : NULL;
261 content::BrowserContext* ExtensionHost::browser_context() {
265 const GURL& ExtensionHost::GetURL() const {
266 return host_contents()->GetURL();
269 void ExtensionHost::LoadInitialURL() {
270 if (!is_background_page() &&
271 !ExtensionSystem::Get(profile_)->extension_service()->
272 IsBackgroundPageReady(extension_)) {
273 // Make sure the background page loads before any others.
274 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_BACKGROUND_PAGE_READY,
275 content::Source<Extension>(extension_));
279 #if !defined(OS_ANDROID)
280 if ((extension_host_type_ == VIEW_TYPE_EXTENSION_POPUP) ||
281 (extension_host_type_ == VIEW_TYPE_PANEL)) {
282 web_modal::WebContentsModalDialogManager::CreateForWebContents(
283 host_contents_.get());
284 web_modal::WebContentsModalDialogManager::FromWebContents(
285 host_contents_.get())->SetDelegate(this);
289 host_contents_->GetController().LoadURL(
290 initial_url_, content::Referrer(), content::PAGE_TRANSITION_LINK,
294 void ExtensionHost::Close() {
295 content::NotificationService::current()->Notify(
296 chrome::NOTIFICATION_EXTENSION_HOST_VIEW_SHOULD_CLOSE,
297 content::Source<Profile>(profile_),
298 content::Details<ExtensionHost>(this));
301 #if !defined(OS_ANDROID)
302 web_modal::WebContentsModalDialogHost*
303 ExtensionHost::GetWebContentsModalDialogHost() {
307 gfx::NativeView ExtensionHost::GetHostView() const {
308 return view_ ? view_->native_view() : NULL;
311 gfx::Point ExtensionHost::GetDialogPosition(const gfx::Size& size) {
312 if (!GetVisibleWebContents())
314 gfx::Rect bounds = GetVisibleWebContents()->GetView()->GetViewBounds();
316 std::max(0, (bounds.width() - size.width()) / 2),
317 std::max(0, (bounds.height() - size.height()) / 2));
320 gfx::Size ExtensionHost::GetMaximumDialogSize() {
321 if (!GetVisibleWebContents())
323 return GetVisibleWebContents()->GetView()->GetViewBounds().size();
326 void ExtensionHost::AddObserver(
327 web_modal::ModalDialogHostObserver* observer) {
330 void ExtensionHost::RemoveObserver(
331 web_modal::ModalDialogHostObserver* observer) {
335 void ExtensionHost::Observe(int type,
336 const content::NotificationSource& source,
337 const content::NotificationDetails& details) {
339 case chrome::NOTIFICATION_EXTENSION_BACKGROUND_PAGE_READY:
340 DCHECK(ExtensionSystem::Get(profile_)->extension_service()->
341 IsBackgroundPageReady(extension_));
344 case chrome::NOTIFICATION_EXTENSION_UNLOADED:
345 // The extension object will be deleted after this notification has been
346 // sent. NULL it out so that dirty pointer issues don't arise in cases
347 // when multiple ExtensionHost objects pointing to the same Extension are
349 if (extension_ == content::Details<UnloadedExtensionInfo>(details)->
354 case content::NOTIFICATION_WEB_CONTENTS_DESTROYED:
355 if (content::Source<WebContents>(source).ptr() ==
356 associated_web_contents_) {
357 associated_web_contents_ = NULL;
361 NOTREACHED() << "Unexpected notification sent.";
366 void ExtensionHost::ResizeDueToAutoResize(WebContents* source,
367 const gfx::Size& new_size) {
369 view()->ResizeDueToAutoResize(new_size);
372 void ExtensionHost::RenderProcessGone(base::TerminationStatus status) {
373 // During browser shutdown, we may use sudden termination on an extension
374 // process, so it is expected to lose our connection to the render view.
376 if (browser_shutdown::GetShutdownType() != browser_shutdown::NOT_VALID)
379 // In certain cases, multiple ExtensionHost objects may have pointed to
380 // the same Extension at some point (one with a background page and a
381 // popup, for example). When the first ExtensionHost goes away, the extension
382 // is unloaded, and any other host that pointed to that extension will have
383 // its pointer to it NULLed out so that any attempt to unload a dirty pointer
388 // TODO(aa): This is suspicious. There can be multiple views in an extension,
389 // and they aren't all going to use ExtensionHost. This should be in someplace
390 // more central, like EPM maybe.
391 content::NotificationService::current()->Notify(
392 chrome::NOTIFICATION_EXTENSION_PROCESS_TERMINATED,
393 content::Source<Profile>(profile_),
394 content::Details<ExtensionHost>(this));
397 void ExtensionHost::InsertInfobarCSS() {
398 DCHECK(!is_background_page());
400 static const base::StringPiece css(
401 ResourceBundle::GetSharedInstance().GetRawDataResource(
402 IDR_EXTENSIONS_INFOBAR_CSS));
404 render_view_host()->InsertCSS(string16(), css.as_string());
407 void ExtensionHost::DidStopLoading(content::RenderViewHost* render_view_host) {
408 bool notify = !did_stop_loading_;
409 did_stop_loading_ = true;
410 if (extension_host_type_ == VIEW_TYPE_EXTENSION_POPUP ||
411 extension_host_type_ == VIEW_TYPE_EXTENSION_DIALOG ||
412 extension_host_type_ == VIEW_TYPE_EXTENSION_INFOBAR ||
413 extension_host_type_ == VIEW_TYPE_PANEL) {
414 #if defined(TOOLKIT_VIEWS) || defined(OS_MACOSX)
416 view()->DidStopLoading();
420 if (extension_host_type_ == VIEW_TYPE_EXTENSION_BACKGROUND_PAGE) {
421 if (extension_ && BackgroundInfo::HasLazyBackgroundPage(extension_)) {
422 UMA_HISTOGRAM_TIMES("Extensions.EventPageLoadTime",
423 since_created_.Elapsed());
425 UMA_HISTOGRAM_TIMES("Extensions.BackgroundPageLoadTime",
426 since_created_.Elapsed());
428 } else if (extension_host_type_ == VIEW_TYPE_EXTENSION_DIALOG) {
429 UMA_HISTOGRAM_TIMES("Extensions.DialogLoadTime",
430 since_created_.Elapsed());
431 } else if (extension_host_type_ == VIEW_TYPE_EXTENSION_POPUP) {
432 UMA_HISTOGRAM_TIMES("Extensions.PopupLoadTime",
433 since_created_.Elapsed());
434 } else if (extension_host_type_ == VIEW_TYPE_EXTENSION_INFOBAR) {
435 UMA_HISTOGRAM_TIMES("Extensions.InfobarLoadTime",
436 since_created_.Elapsed());
437 } else if (extension_host_type_ == VIEW_TYPE_PANEL) {
438 UMA_HISTOGRAM_TIMES("Extensions.PanelLoadTime", since_created_.Elapsed());
441 // Send the notification last, because it might result in this being
443 content::NotificationService::current()->Notify(
444 chrome::NOTIFICATION_EXTENSION_HOST_DID_STOP_LOADING,
445 content::Source<Profile>(profile_),
446 content::Details<ExtensionHost>(this));
450 void ExtensionHost::DocumentAvailableInMainFrame() {
451 // If the document has already been marked as available for this host, then
452 // bail. No need for the redundant setup. http://crbug.com/31170
453 if (document_element_available_)
456 document_element_available_ = true;
457 if (is_background_page()) {
458 ExtensionSystem::Get(profile_)->extension_service()->
459 SetBackgroundPageReady(extension_);
461 switch (extension_host_type_) {
462 case VIEW_TYPE_EXTENSION_INFOBAR:
466 break; // No style sheet for other types, at the moment.
471 void ExtensionHost::CloseContents(WebContents* contents) {
472 // TODO(mpcomplete): is this check really necessary?
473 if (extension_host_type_ == VIEW_TYPE_EXTENSION_POPUP ||
474 extension_host_type_ == VIEW_TYPE_EXTENSION_DIALOG ||
475 extension_host_type_ == VIEW_TYPE_EXTENSION_BACKGROUND_PAGE ||
476 extension_host_type_ == VIEW_TYPE_EXTENSION_INFOBAR ||
477 extension_host_type_ == VIEW_TYPE_PANEL) {
482 void ExtensionHost::WillRunJavaScriptDialog() {
483 ExtensionProcessManager* pm =
484 ExtensionSystem::Get(profile_)->process_manager();
486 pm->IncrementLazyKeepaliveCount(extension());
489 void ExtensionHost::DidCloseJavaScriptDialog() {
490 ExtensionProcessManager* pm =
491 ExtensionSystem::Get(profile_)->process_manager();
493 pm->DecrementLazyKeepaliveCount(extension());
496 WebContents* ExtensionHost::OpenURLFromTab(WebContents* source,
497 const OpenURLParams& params) {
498 // Whitelist the dispositions we will allow to be opened.
499 switch (params.disposition) {
501 case NEW_FOREGROUND_TAB:
502 case NEW_BACKGROUND_TAB:
506 case OFF_THE_RECORD: {
507 // Only allow these from hosts that are bound to a browser (e.g. popups).
508 // Otherwise they are not driven by a user gesture.
509 Browser* browser = view() ? view()->browser() : NULL;
510 return browser ? browser->OpenURL(params) : NULL;
517 bool ExtensionHost::PreHandleKeyboardEvent(WebContents* source,
518 const NativeWebKeyboardEvent& event,
519 bool* is_keyboard_shortcut) {
520 if (extension_host_type_ == VIEW_TYPE_EXTENSION_POPUP &&
521 event.type == NativeWebKeyboardEvent::RawKeyDown &&
522 event.windowsKeyCode == ui::VKEY_ESCAPE) {
523 DCHECK(is_keyboard_shortcut != NULL);
524 *is_keyboard_shortcut = true;
528 // Handle higher priority browser shortcuts such as Ctrl-w.
529 Browser* browser = view() ? view()->browser() : NULL;
531 return browser->PreHandleKeyboardEvent(source, event, is_keyboard_shortcut);
533 *is_keyboard_shortcut = false;
537 void ExtensionHost::HandleKeyboardEvent(WebContents* source,
538 const NativeWebKeyboardEvent& event) {
539 if (extension_host_type_ == VIEW_TYPE_EXTENSION_POPUP) {
540 if (event.type == NativeWebKeyboardEvent::RawKeyDown &&
541 event.windowsKeyCode == ui::VKEY_ESCAPE) {
546 UnhandledKeyboardEvent(source, event);
549 bool ExtensionHost::OnMessageReceived(const IPC::Message& message) {
551 IPC_BEGIN_MESSAGE_MAP(ExtensionHost, message)
552 IPC_MESSAGE_HANDLER(ExtensionHostMsg_Request, OnRequest)
553 IPC_MESSAGE_HANDLER(ExtensionHostMsg_EventAck, OnEventAck)
554 IPC_MESSAGE_HANDLER(ExtensionHostMsg_IncrementLazyKeepaliveCount,
555 OnIncrementLazyKeepaliveCount)
556 IPC_MESSAGE_HANDLER(ExtensionHostMsg_DecrementLazyKeepaliveCount,
557 OnDecrementLazyKeepaliveCount)
558 IPC_MESSAGE_HANDLER(ChromeViewHostMsg_DetailedConsoleMessageAdded,
559 OnDetailedConsoleMessageAdded)
560 IPC_MESSAGE_UNHANDLED(handled = false)
561 IPC_END_MESSAGE_MAP()
565 void ExtensionHost::OnRequest(const ExtensionHostMsg_Request_Params& params) {
566 extension_function_dispatcher_.Dispatch(params, render_view_host());
569 void ExtensionHost::OnEventAck() {
570 EventRouter* router = ExtensionSystem::Get(profile_)->event_router();
572 router->OnEventAck(profile_, extension_id());
575 void ExtensionHost::OnIncrementLazyKeepaliveCount() {
576 ExtensionProcessManager* pm =
577 ExtensionSystem::Get(profile_)->process_manager();
579 pm->IncrementLazyKeepaliveCount(extension());
582 void ExtensionHost::OnDecrementLazyKeepaliveCount() {
583 ExtensionProcessManager* pm =
584 ExtensionSystem::Get(profile_)->process_manager();
586 pm->DecrementLazyKeepaliveCount(extension());
589 void ExtensionHost::OnDetailedConsoleMessageAdded(
590 const base::string16& message,
591 const base::string16& source,
592 const StackTrace& stack_trace,
593 int32 severity_level) {
594 if (IsSourceFromAnExtension(source)) {
596 if (associated_web_contents_)
597 context_url = associated_web_contents_->GetLastCommittedURL();
598 else if (host_contents_.get())
599 context_url = host_contents_->GetLastCommittedURL();
601 ErrorConsole::Get(profile_)->ReportError(
602 scoped_ptr<ExtensionError>(new RuntimeError(
604 profile_->IsOffTheRecord(),
609 static_cast<logging::LogSeverity>(severity_level),
610 render_view_host_->GetRoutingID(),
611 render_view_host_->GetProcess()->GetID())));
615 void ExtensionHost::UnhandledKeyboardEvent(
617 const content::NativeWebKeyboardEvent& event) {
618 Browser* browser = view() ? view()->browser() : NULL;
620 // Handle lower priority browser shortcuts such as Ctrl-f.
621 return browser->HandleKeyboardEvent(source, event);
623 #if defined(TOOLKIT_VIEWS)
624 // In case there's no Browser (e.g. for dialogs), pass it to
625 // ExtensionViewViews to handle acceleratos. The view's FocusManager does
626 // not know anything about Browser accelerators, but might know others such
629 view()->HandleKeyboardEvent(event);
634 void ExtensionHost::RenderViewCreated(RenderViewHost* render_view_host) {
635 render_view_host_ = render_view_host;
638 view()->RenderViewCreated();
640 // If the host is bound to a window, then extract its id. Extensions hosted
641 // in ExternalTabContainer objects may not have an associated window.
642 WindowController* window = GetExtensionWindowController();
644 render_view_host->Send(new ExtensionMsg_UpdateBrowserWindowId(
645 render_view_host->GetRoutingID(), window->GetWindowId()));
649 void ExtensionHost::RenderViewDeleted(RenderViewHost* render_view_host) {
650 // If our RenderViewHost is deleted, fall back to the host_contents' current
651 // RVH. There is sometimes a small gap between the pending RVH being deleted
652 // and RenderViewCreated being called, so we update it here.
653 if (render_view_host == render_view_host_)
654 render_view_host_ = host_contents_->GetRenderViewHost();
657 content::JavaScriptDialogManager* ExtensionHost::GetJavaScriptDialogManager() {
658 if (!dialog_manager_) {
659 dialog_manager_.reset(CreateJavaScriptDialogManagerInstance(this));
661 return dialog_manager_.get();
664 content::ColorChooser* ExtensionHost::OpenColorChooser(
665 WebContents* web_contents, SkColor initial_color) {
666 return chrome::ShowColorChooser(web_contents, initial_color);
669 void ExtensionHost::RunFileChooser(WebContents* tab,
670 const content::FileChooserParams& params) {
671 FileSelectHelper::RunFileChooser(tab, params);
674 void ExtensionHost::AddNewContents(WebContents* source,
675 WebContents* new_contents,
676 WindowOpenDisposition disposition,
677 const gfx::Rect& initial_pos,
680 // First, if the creating extension view was associated with a tab contents,
681 // use that tab content's delegate. We must be careful here that the
682 // associated tab contents has the same profile as the new tab contents. In
683 // the case of extensions in 'spanning' incognito mode, they can mismatch.
684 // We don't want to end up putting a normal tab into an incognito window, or
686 // Note that we don't do this for popup windows, because we need to associate
687 // those with their extension_app_id.
688 if (disposition != NEW_POPUP) {
689 WebContents* associated_contents = GetAssociatedWebContents();
690 if (associated_contents &&
691 associated_contents->GetBrowserContext() ==
692 new_contents->GetBrowserContext()) {
693 WebContentsDelegate* delegate = associated_contents->GetDelegate();
695 delegate->AddNewContents(
696 associated_contents, new_contents, disposition, initial_pos,
697 user_gesture, was_blocked);
703 ExtensionTabUtil::CreateTab(new_contents, extension_id_, disposition,
704 initial_pos, user_gesture);
707 void ExtensionHost::RenderViewReady() {
708 content::NotificationService::current()->Notify(
709 chrome::NOTIFICATION_EXTENSION_HOST_CREATED,
710 content::Source<Profile>(profile_),
711 content::Details<ExtensionHost>(this));
714 void ExtensionHost::RequestMediaAccessPermission(
715 content::WebContents* web_contents,
716 const content::MediaStreamRequest& request,
717 const content::MediaResponseCallback& callback) {
718 MediaCaptureDevicesDispatcher::GetInstance()->ProcessMediaAccessRequest(
719 web_contents, request, callback, extension());
722 } // namespace extensions