1// Copyright 2014 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 "extensions/browser/extension_host.h" 6 7#include <list> 8 9#include "base/bind.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 "content/public/browser/browser_context.h" 18#include "content/public/browser/content_browser_client.h" 19#include "content/public/browser/native_web_keyboard_event.h" 20#include "content/public/browser/notification_service.h" 21#include "content/public/browser/notification_source.h" 22#include "content/public/browser/notification_types.h" 23#include "content/public/browser/render_process_host.h" 24#include "content/public/browser/render_view_host.h" 25#include "content/public/browser/render_widget_host_view.h" 26#include "content/public/browser/site_instance.h" 27#include "content/public/browser/web_contents.h" 28#include "extensions/browser/event_router.h" 29#include "extensions/browser/extension_error.h" 30#include "extensions/browser/extension_host_delegate.h" 31#include "extensions/browser/extension_system.h" 32#include "extensions/browser/extensions_browser_client.h" 33#include "extensions/browser/notification_types.h" 34#include "extensions/browser/process_manager.h" 35#include "extensions/browser/runtime_data.h" 36#include "extensions/browser/view_type_utils.h" 37#include "extensions/common/extension.h" 38#include "extensions/common/extension_messages.h" 39#include "extensions/common/extension_urls.h" 40#include "extensions/common/feature_switch.h" 41#include "extensions/common/manifest_handlers/background_info.h" 42#include "ui/base/l10n/l10n_util.h" 43#include "ui/base/window_open_disposition.h" 44 45using content::BrowserContext; 46using content::OpenURLParams; 47using content::RenderProcessHost; 48using content::RenderViewHost; 49using content::SiteInstance; 50using content::WebContents; 51 52namespace extensions { 53 54// Helper class that rate-limits the creation of renderer processes for 55// ExtensionHosts, to avoid blocking the UI. 56class ExtensionHost::ProcessCreationQueue { 57 public: 58 static ProcessCreationQueue* GetInstance() { 59 return Singleton<ProcessCreationQueue>::get(); 60 } 61 62 // Add a host to the queue for RenderView creation. 63 void CreateSoon(ExtensionHost* host) { 64 queue_.push_back(host); 65 PostTask(); 66 } 67 68 // Remove a host from the queue (in case it's being deleted). 69 void Remove(ExtensionHost* host) { 70 Queue::iterator it = std::find(queue_.begin(), queue_.end(), host); 71 if (it != queue_.end()) 72 queue_.erase(it); 73 } 74 75 private: 76 friend class Singleton<ProcessCreationQueue>; 77 friend struct DefaultSingletonTraits<ProcessCreationQueue>; 78 ProcessCreationQueue() 79 : pending_create_(false), 80 ptr_factory_(this) {} 81 82 // Queue up a delayed task to process the next ExtensionHost in the queue. 83 void PostTask() { 84 if (!pending_create_) { 85 base::MessageLoop::current()->PostTask(FROM_HERE, 86 base::Bind(&ProcessCreationQueue::ProcessOneHost, 87 ptr_factory_.GetWeakPtr())); 88 pending_create_ = true; 89 } 90 } 91 92 // Create the RenderView for the next host in the queue. 93 void ProcessOneHost() { 94 pending_create_ = false; 95 if (queue_.empty()) 96 return; // can happen on shutdown 97 98 queue_.front()->CreateRenderViewNow(); 99 queue_.pop_front(); 100 101 if (!queue_.empty()) 102 PostTask(); 103 } 104 105 typedef std::list<ExtensionHost*> Queue; 106 Queue queue_; 107 bool pending_create_; 108 base::WeakPtrFactory<ProcessCreationQueue> ptr_factory_; 109}; 110 111//////////////// 112// ExtensionHost 113 114ExtensionHost::ExtensionHost(const Extension* extension, 115 SiteInstance* site_instance, 116 const GURL& url, 117 ViewType host_type) 118 : delegate_(ExtensionsBrowserClient::Get()->CreateExtensionHostDelegate()), 119 extension_(extension), 120 extension_id_(extension->id()), 121 browser_context_(site_instance->GetBrowserContext()), 122 render_view_host_(NULL), 123 did_stop_loading_(false), 124 document_element_available_(false), 125 initial_url_(url), 126 extension_function_dispatcher_(browser_context_, this), 127 extension_host_type_(host_type) { 128 // Not used for panels, see PanelHost. 129 DCHECK(host_type == VIEW_TYPE_EXTENSION_BACKGROUND_PAGE || 130 host_type == VIEW_TYPE_EXTENSION_DIALOG || 131 host_type == VIEW_TYPE_EXTENSION_INFOBAR || 132 host_type == VIEW_TYPE_EXTENSION_POPUP); 133 host_contents_.reset(WebContents::Create( 134 WebContents::CreateParams(browser_context_, site_instance))), 135 content::WebContentsObserver::Observe(host_contents_.get()); 136 host_contents_->SetDelegate(this); 137 SetViewType(host_contents_.get(), host_type); 138 139 render_view_host_ = host_contents_->GetRenderViewHost(); 140 141 // Listen for when an extension is unloaded from the same profile, as it may 142 // be the same extension that this points to. 143 registrar_.Add(this, 144 extensions::NOTIFICATION_EXTENSION_UNLOADED_DEPRECATED, 145 content::Source<BrowserContext>(browser_context_)); 146 147 // Set up web contents observers and pref observers. 148 delegate_->OnExtensionHostCreated(host_contents()); 149} 150 151ExtensionHost::~ExtensionHost() { 152 if (extension_host_type_ == VIEW_TYPE_EXTENSION_BACKGROUND_PAGE && 153 extension_ && BackgroundInfo::HasLazyBackgroundPage(extension_)) { 154 UMA_HISTOGRAM_LONG_TIMES("Extensions.EventPageActiveTime", 155 since_created_.Elapsed()); 156 } 157 content::NotificationService::current()->Notify( 158 extensions::NOTIFICATION_EXTENSION_HOST_DESTROYED, 159 content::Source<BrowserContext>(browser_context_), 160 content::Details<ExtensionHost>(this)); 161 ProcessCreationQueue::GetInstance()->Remove(this); 162} 163 164content::RenderProcessHost* ExtensionHost::render_process_host() const { 165 return render_view_host()->GetProcess(); 166} 167 168RenderViewHost* ExtensionHost::render_view_host() const { 169 // TODO(mpcomplete): This can be NULL. How do we handle that? 170 return render_view_host_; 171} 172 173bool ExtensionHost::IsRenderViewLive() const { 174 return render_view_host()->IsRenderViewLive(); 175} 176 177void ExtensionHost::CreateRenderViewSoon() { 178 if ((render_process_host() && render_process_host()->HasConnection())) { 179 // If the process is already started, go ahead and initialize the RenderView 180 // synchronously. The process creation is the real meaty part that we want 181 // to defer. 182 CreateRenderViewNow(); 183 } else { 184 ProcessCreationQueue::GetInstance()->CreateSoon(this); 185 } 186} 187 188void ExtensionHost::CreateRenderViewNow() { 189 LoadInitialURL(); 190 if (IsBackgroundPage()) { 191 DCHECK(IsRenderViewLive()); 192 // Connect orphaned dev-tools instances. 193 delegate_->OnRenderViewCreatedForBackgroundPage(this); 194 } 195} 196 197const GURL& ExtensionHost::GetURL() const { 198 return host_contents()->GetURL(); 199} 200 201void ExtensionHost::LoadInitialURL() { 202 host_contents_->GetController().LoadURL( 203 initial_url_, content::Referrer(), ui::PAGE_TRANSITION_LINK, 204 std::string()); 205} 206 207bool ExtensionHost::IsBackgroundPage() const { 208 DCHECK(extension_host_type_ == VIEW_TYPE_EXTENSION_BACKGROUND_PAGE); 209 return true; 210} 211 212void ExtensionHost::Close() { 213 content::NotificationService::current()->Notify( 214 extensions::NOTIFICATION_EXTENSION_HOST_VIEW_SHOULD_CLOSE, 215 content::Source<BrowserContext>(browser_context_), 216 content::Details<ExtensionHost>(this)); 217} 218 219void ExtensionHost::Observe(int type, 220 const content::NotificationSource& source, 221 const content::NotificationDetails& details) { 222 switch (type) { 223 case extensions::NOTIFICATION_EXTENSION_UNLOADED_DEPRECATED: 224 // The extension object will be deleted after this notification has been 225 // sent. NULL it out so that dirty pointer issues don't arise in cases 226 // when multiple ExtensionHost objects pointing to the same Extension are 227 // present. 228 if (extension_ == content::Details<UnloadedExtensionInfo>(details)-> 229 extension) { 230 extension_ = NULL; 231 } 232 break; 233 default: 234 NOTREACHED() << "Unexpected notification sent."; 235 break; 236 } 237} 238 239void ExtensionHost::RenderProcessGone(base::TerminationStatus status) { 240 // During browser shutdown, we may use sudden termination on an extension 241 // process, so it is expected to lose our connection to the render view. 242 // Do nothing. 243 RenderProcessHost* process_host = host_contents_->GetRenderProcessHost(); 244 if (process_host && process_host->FastShutdownStarted()) 245 return; 246 247 // In certain cases, multiple ExtensionHost objects may have pointed to 248 // the same Extension at some point (one with a background page and a 249 // popup, for example). When the first ExtensionHost goes away, the extension 250 // is unloaded, and any other host that pointed to that extension will have 251 // its pointer to it NULLed out so that any attempt to unload a dirty pointer 252 // will be averted. 253 if (!extension_) 254 return; 255 256 // TODO(aa): This is suspicious. There can be multiple views in an extension, 257 // and they aren't all going to use ExtensionHost. This should be in someplace 258 // more central, like EPM maybe. 259 content::NotificationService::current()->Notify( 260 extensions::NOTIFICATION_EXTENSION_PROCESS_TERMINATED, 261 content::Source<BrowserContext>(browser_context_), 262 content::Details<ExtensionHost>(this)); 263} 264 265void ExtensionHost::DidStopLoading(content::RenderViewHost* render_view_host) { 266 bool notify = !did_stop_loading_; 267 did_stop_loading_ = true; 268 OnDidStopLoading(); 269 if (notify) { 270 if (extension_host_type_ == VIEW_TYPE_EXTENSION_BACKGROUND_PAGE) { 271 if (extension_ && BackgroundInfo::HasLazyBackgroundPage(extension_)) { 272 UMA_HISTOGRAM_TIMES("Extensions.EventPageLoadTime", 273 since_created_.Elapsed()); 274 } else { 275 UMA_HISTOGRAM_TIMES("Extensions.BackgroundPageLoadTime", 276 since_created_.Elapsed()); 277 } 278 } else if (extension_host_type_ == VIEW_TYPE_EXTENSION_DIALOG) { 279 UMA_HISTOGRAM_TIMES("Extensions.DialogLoadTime", 280 since_created_.Elapsed()); 281 } else if (extension_host_type_ == VIEW_TYPE_EXTENSION_POPUP) { 282 UMA_HISTOGRAM_TIMES("Extensions.PopupLoadTime", 283 since_created_.Elapsed()); 284 } else if (extension_host_type_ == VIEW_TYPE_EXTENSION_INFOBAR) { 285 UMA_HISTOGRAM_TIMES("Extensions.InfobarLoadTime", 286 since_created_.Elapsed()); 287 } 288 289 // Send the notification last, because it might result in this being 290 // deleted. 291 content::NotificationService::current()->Notify( 292 extensions::NOTIFICATION_EXTENSION_HOST_DID_STOP_LOADING, 293 content::Source<BrowserContext>(browser_context_), 294 content::Details<ExtensionHost>(this)); 295 } 296} 297 298void ExtensionHost::OnDidStopLoading() { 299 DCHECK(extension_host_type_ == VIEW_TYPE_EXTENSION_BACKGROUND_PAGE); 300 // Nothing to do for background pages. 301} 302 303void ExtensionHost::DocumentAvailableInMainFrame() { 304 // If the document has already been marked as available for this host, then 305 // bail. No need for the redundant setup. http://crbug.com/31170 306 if (document_element_available_) 307 return; 308 document_element_available_ = true; 309 OnDocumentAvailable(); 310} 311 312void ExtensionHost::OnDocumentAvailable() { 313 DCHECK(extension_host_type_ == VIEW_TYPE_EXTENSION_BACKGROUND_PAGE); 314 ExtensionSystem::Get(browser_context_) 315 ->runtime_data() 316 ->SetBackgroundPageReady(extension_, true); 317 content::NotificationService::current()->Notify( 318 extensions::NOTIFICATION_EXTENSION_BACKGROUND_PAGE_READY, 319 content::Source<const Extension>(extension_), 320 content::NotificationService::NoDetails()); 321} 322 323void ExtensionHost::CloseContents(WebContents* contents) { 324 Close(); 325} 326 327bool ExtensionHost::OnMessageReceived(const IPC::Message& message) { 328 bool handled = true; 329 IPC_BEGIN_MESSAGE_MAP(ExtensionHost, message) 330 IPC_MESSAGE_HANDLER(ExtensionHostMsg_Request, OnRequest) 331 IPC_MESSAGE_HANDLER(ExtensionHostMsg_EventAck, OnEventAck) 332 IPC_MESSAGE_HANDLER(ExtensionHostMsg_IncrementLazyKeepaliveCount, 333 OnIncrementLazyKeepaliveCount) 334 IPC_MESSAGE_HANDLER(ExtensionHostMsg_DecrementLazyKeepaliveCount, 335 OnDecrementLazyKeepaliveCount) 336 IPC_MESSAGE_UNHANDLED(handled = false) 337 IPC_END_MESSAGE_MAP() 338 return handled; 339} 340 341void ExtensionHost::OnRequest(const ExtensionHostMsg_Request_Params& params) { 342 extension_function_dispatcher_.Dispatch(params, render_view_host()); 343} 344 345void ExtensionHost::OnEventAck() { 346 EventRouter* router = EventRouter::Get(browser_context_); 347 if (router) 348 router->OnEventAck(browser_context_, extension_id()); 349} 350 351void ExtensionHost::OnIncrementLazyKeepaliveCount() { 352 ProcessManager* pm = ExtensionSystem::Get( 353 browser_context_)->process_manager(); 354 if (pm) 355 pm->IncrementLazyKeepaliveCount(extension()); 356} 357 358void ExtensionHost::OnDecrementLazyKeepaliveCount() { 359 ProcessManager* pm = ExtensionSystem::Get( 360 browser_context_)->process_manager(); 361 if (pm) 362 pm->DecrementLazyKeepaliveCount(extension()); 363} 364 365// content::WebContentsObserver 366 367void ExtensionHost::RenderViewCreated(RenderViewHost* render_view_host) { 368 render_view_host_ = render_view_host; 369} 370 371void ExtensionHost::RenderViewDeleted(RenderViewHost* render_view_host) { 372 // If our RenderViewHost is deleted, fall back to the host_contents' current 373 // RVH. There is sometimes a small gap between the pending RVH being deleted 374 // and RenderViewCreated being called, so we update it here. 375 if (render_view_host == render_view_host_) 376 render_view_host_ = host_contents_->GetRenderViewHost(); 377} 378 379content::JavaScriptDialogManager* ExtensionHost::GetJavaScriptDialogManager() { 380 return delegate_->GetJavaScriptDialogManager(); 381} 382 383void ExtensionHost::AddNewContents(WebContents* source, 384 WebContents* new_contents, 385 WindowOpenDisposition disposition, 386 const gfx::Rect& initial_pos, 387 bool user_gesture, 388 bool* was_blocked) { 389 // First, if the creating extension view was associated with a tab contents, 390 // use that tab content's delegate. We must be careful here that the 391 // associated tab contents has the same profile as the new tab contents. In 392 // the case of extensions in 'spanning' incognito mode, they can mismatch. 393 // We don't want to end up putting a normal tab into an incognito window, or 394 // vice versa. 395 // Note that we don't do this for popup windows, because we need to associate 396 // those with their extension_app_id. 397 if (disposition != NEW_POPUP) { 398 WebContents* associated_contents = GetAssociatedWebContents(); 399 if (associated_contents && 400 associated_contents->GetBrowserContext() == 401 new_contents->GetBrowserContext()) { 402 WebContentsDelegate* delegate = associated_contents->GetDelegate(); 403 if (delegate) { 404 delegate->AddNewContents( 405 associated_contents, new_contents, disposition, initial_pos, 406 user_gesture, was_blocked); 407 return; 408 } 409 } 410 } 411 412 delegate_->CreateTab( 413 new_contents, extension_id_, disposition, initial_pos, user_gesture); 414} 415 416void ExtensionHost::RenderViewReady() { 417 content::NotificationService::current()->Notify( 418 extensions::NOTIFICATION_EXTENSION_HOST_CREATED, 419 content::Source<BrowserContext>(browser_context_), 420 content::Details<ExtensionHost>(this)); 421} 422 423void ExtensionHost::RequestMediaAccessPermission( 424 content::WebContents* web_contents, 425 const content::MediaStreamRequest& request, 426 const content::MediaResponseCallback& callback) { 427 delegate_->ProcessMediaAccessRequest( 428 web_contents, request, callback, extension()); 429} 430 431bool ExtensionHost::CheckMediaAccessPermission( 432 content::WebContents* web_contents, 433 const GURL& security_origin, 434 content::MediaStreamType type) { 435 return delegate_->CheckMediaAccessPermission( 436 web_contents, security_origin, type, extension()); 437} 438 439bool ExtensionHost::IsNeverVisible(content::WebContents* web_contents) { 440 ViewType view_type = extensions::GetViewType(web_contents); 441 return view_type == extensions::VIEW_TYPE_EXTENSION_BACKGROUND_PAGE; 442} 443 444} // namespace extensions 445