1 // Copyright 2013 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/process_manager.h"
6
7 #include "base/bind.h"
8 #include "base/command_line.h"
9 #include "base/lazy_instance.h"
10 #include "base/logging.h"
11 #include "base/message_loop/message_loop.h"
12 #include "base/metrics/histogram.h"
13 #include "base/stl_util.h"
14 #include "base/strings/string_number_conversions.h"
15 #include "base/time/time.h"
16 #include "chrome/browser/chrome_notification_types.h"
17 #include "content/public/browser/browser_context.h"
18 #include "content/public/browser/browser_thread.h"
19 #include "content/public/browser/devtools_agent_host.h"
20 #include "content/public/browser/devtools_manager.h"
21 #include "content/public/browser/notification_service.h"
22 #include "content/public/browser/render_frame_host.h"
23 #include "content/public/browser/render_process_host.h"
24 #include "content/public/browser/render_view_host.h"
25 #include "content/public/browser/site_instance.h"
26 #include "content/public/browser/web_contents.h"
27 #include "content/public/browser/web_contents_delegate.h"
28 #include "content/public/browser/web_contents_observer.h"
29 #include "content/public/browser/web_contents_user_data.h"
30 #include "content/public/common/renderer_preferences.h"
31 #include "content/public/common/url_constants.h"
32 #include "extensions/browser/extension_host.h"
33 #include "extensions/browser/extension_registry.h"
34 #include "extensions/browser/extension_system.h"
35 #include "extensions/browser/extensions_browser_client.h"
36 #include "extensions/browser/process_manager_observer.h"
37 #include "extensions/browser/view_type_utils.h"
38 #include "extensions/common/constants.h"
39 #include "extensions/common/extension.h"
40 #include "extensions/common/extension_messages.h"
41 #include "extensions/common/manifest_handlers/background_info.h"
42 #include "extensions/common/manifest_handlers/incognito_info.h"
43 #include "extensions/common/one_shot_event.h"
44 #include "extensions/common/switches.h"
45
46 using content::BrowserContext;
47 using content::RenderViewHost;
48 using content::SiteInstance;
49 using content::WebContents;
50
51 namespace extensions {
52 class RenderViewHostDestructionObserver;
53 }
54 DEFINE_WEB_CONTENTS_USER_DATA_KEY(
55 extensions::RenderViewHostDestructionObserver);
56
57 namespace extensions {
58
59 namespace {
60
GetExtensionID(RenderViewHost * render_view_host)61 std::string GetExtensionID(RenderViewHost* render_view_host) {
62 // This works for both apps and extensions because the site has been
63 // normalized to the extension URL for hosted apps.
64 content::SiteInstance* site_instance = render_view_host->GetSiteInstance();
65 if (!site_instance)
66 return std::string();
67
68 const GURL& site_url = site_instance->GetSiteURL();
69
70 if (!site_url.SchemeIs(kExtensionScheme) &&
71 !site_url.SchemeIs(content::kGuestScheme))
72 return std::string();
73
74 return site_url.host();
75 }
76
GetExtensionIDFromFrame(content::RenderFrameHost * render_frame_host)77 std::string GetExtensionIDFromFrame(
78 content::RenderFrameHost* render_frame_host) {
79 // This works for both apps and extensions because the site has been
80 // normalized to the extension URL for apps.
81 if (!render_frame_host->GetSiteInstance())
82 return std::string();
83
84 return render_frame_host->GetSiteInstance()->GetSiteURL().host();
85 }
86
IsFrameInExtensionHost(ExtensionHost * extension_host,content::RenderFrameHost * render_frame_host)87 bool IsFrameInExtensionHost(ExtensionHost* extension_host,
88 content::RenderFrameHost* render_frame_host) {
89 return WebContents::FromRenderFrameHost(render_frame_host) ==
90 extension_host->host_contents();
91 }
92
OnRenderViewHostUnregistered(BrowserContext * context,RenderViewHost * render_view_host)93 void OnRenderViewHostUnregistered(BrowserContext* context,
94 RenderViewHost* render_view_host) {
95 content::NotificationService::current()->Notify(
96 chrome::NOTIFICATION_EXTENSION_VIEW_UNREGISTERED,
97 content::Source<BrowserContext>(context),
98 content::Details<RenderViewHost>(render_view_host));
99 }
100
101 // Incognito profiles use this process manager. It is mostly a shim that decides
102 // whether to fall back on the original profile's ProcessManager based
103 // on whether a given extension uses "split" or "spanning" incognito behavior.
104 class IncognitoProcessManager : public ProcessManager {
105 public:
106 IncognitoProcessManager(BrowserContext* incognito_context,
107 BrowserContext* original_context,
108 ProcessManager* original_manager);
~IncognitoProcessManager()109 virtual ~IncognitoProcessManager() {}
110 virtual bool CreateBackgroundHost(const Extension* extension,
111 const GURL& url) OVERRIDE;
112 virtual SiteInstance* GetSiteInstanceForURL(const GURL& url) OVERRIDE;
113
114 private:
115 ProcessManager* original_manager_;
116
117 DISALLOW_COPY_AND_ASSIGN(IncognitoProcessManager);
118 };
119
CreateBackgroundHostForExtensionLoad(ProcessManager * manager,const Extension * extension)120 static void CreateBackgroundHostForExtensionLoad(
121 ProcessManager* manager, const Extension* extension) {
122 DVLOG(1) << "CreateBackgroundHostForExtensionLoad";
123 if (BackgroundInfo::HasPersistentBackgroundPage(extension))
124 manager->CreateBackgroundHost(extension,
125 BackgroundInfo::GetBackgroundURL(extension));
126 }
127
128 } // namespace
129
130 class RenderViewHostDestructionObserver
131 : public content::WebContentsObserver,
132 public content::WebContentsUserData<RenderViewHostDestructionObserver> {
133 public:
~RenderViewHostDestructionObserver()134 virtual ~RenderViewHostDestructionObserver() {}
135
136 private:
RenderViewHostDestructionObserver(WebContents * web_contents)137 explicit RenderViewHostDestructionObserver(WebContents* web_contents)
138 : WebContentsObserver(web_contents) {
139 BrowserContext* context = web_contents->GetBrowserContext();
140 process_manager_ = ExtensionSystem::Get(context)->process_manager();
141 }
142
143 friend class content::WebContentsUserData<RenderViewHostDestructionObserver>;
144
145 // content::WebContentsObserver overrides.
RenderViewDeleted(RenderViewHost * render_view_host)146 virtual void RenderViewDeleted(RenderViewHost* render_view_host) OVERRIDE {
147 process_manager_->UnregisterRenderViewHost(render_view_host);
148 }
149
150 ProcessManager* process_manager_;
151
152 DISALLOW_COPY_AND_ASSIGN(RenderViewHostDestructionObserver);
153 };
154
155 struct ProcessManager::BackgroundPageData {
156 // The count of things keeping the lazy background page alive.
157 int lazy_keepalive_count;
158
159 // Tracks if an impulse event has occured since the last polling check.
160 bool keepalive_impulse;
161 bool previous_keepalive_impulse;
162
163 // This is used with the ShouldSuspend message, to ensure that the extension
164 // remained idle between sending the message and receiving the ack.
165 int close_sequence_id;
166
167 // True if the page responded to the ShouldSuspend message and is currently
168 // dispatching the suspend event. During this time any events that arrive will
169 // cancel the suspend process and an onSuspendCanceled event will be
170 // dispatched to the page.
171 bool is_closing;
172
173 // Keeps track of when this page was last suspended. Used for perf metrics.
174 linked_ptr<base::ElapsedTimer> since_suspended;
175
BackgroundPageDataextensions::ProcessManager::BackgroundPageData176 BackgroundPageData()
177 : lazy_keepalive_count(0),
178 keepalive_impulse(false),
179 previous_keepalive_impulse(false),
180 close_sequence_id(0),
181 is_closing(false) {}
182 };
183
184 //
185 // ProcessManager
186 //
187
188 // static
Create(BrowserContext * context)189 ProcessManager* ProcessManager::Create(BrowserContext* context) {
190 ExtensionsBrowserClient* client = ExtensionsBrowserClient::Get();
191 if (client->IsGuestSession(context)) {
192 // In the guest session, there is a single off-the-record context. Unlike
193 // a regular incognito mode, background pages of extensions must be
194 // created regardless of whether extensions use "spanning" or "split"
195 // incognito behavior.
196 BrowserContext* original_context = client->GetOriginalContext(context);
197 return new ProcessManager(context, original_context);
198 }
199
200 if (context->IsOffTheRecord()) {
201 BrowserContext* original_context = client->GetOriginalContext(context);
202 ProcessManager* original_manager =
203 ExtensionSystem::Get(original_context)->process_manager();
204 return new IncognitoProcessManager(
205 context, original_context, original_manager);
206 }
207
208 return new ProcessManager(context, context);
209 }
210
211 // static
CreateIncognitoForTesting(BrowserContext * incognito_context,BrowserContext * original_context,ProcessManager * original_manager)212 ProcessManager* ProcessManager::CreateIncognitoForTesting(
213 BrowserContext* incognito_context,
214 BrowserContext* original_context,
215 ProcessManager* original_manager) {
216 DCHECK(incognito_context->IsOffTheRecord());
217 DCHECK(!original_context->IsOffTheRecord());
218 return new IncognitoProcessManager(
219 incognito_context, original_context, original_manager);
220 }
221
ProcessManager(BrowserContext * context,BrowserContext * original_context)222 ProcessManager::ProcessManager(BrowserContext* context,
223 BrowserContext* original_context)
224 : site_instance_(SiteInstance::Create(context)),
225 startup_background_hosts_created_(false),
226 devtools_callback_(base::Bind(
227 &ProcessManager::OnDevToolsStateChanged,
228 base::Unretained(this))),
229 weak_ptr_factory_(this) {
230 registrar_.Add(this, chrome::NOTIFICATION_EXTENSIONS_READY,
231 content::Source<BrowserContext>(original_context));
232 registrar_.Add(this,
233 chrome::NOTIFICATION_EXTENSION_LOADED_DEPRECATED,
234 content::Source<BrowserContext>(original_context));
235 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNLOADED_DEPRECATED,
236 content::Source<BrowserContext>(original_context));
237 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_HOST_DESTROYED,
238 content::Source<BrowserContext>(context));
239 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_HOST_VIEW_SHOULD_CLOSE,
240 content::Source<BrowserContext>(context));
241 registrar_.Add(this, content::NOTIFICATION_RENDER_VIEW_HOST_CHANGED,
242 content::NotificationService::AllSources());
243 registrar_.Add(this, content::NOTIFICATION_WEB_CONTENTS_CONNECTED,
244 content::NotificationService::AllSources());
245 registrar_.Add(this, chrome::NOTIFICATION_PROFILE_CREATED,
246 content::Source<BrowserContext>(original_context));
247 registrar_.Add(this, chrome::NOTIFICATION_PROFILE_DESTROYED,
248 content::Source<BrowserContext>(context));
249 if (context->IsOffTheRecord()) {
250 registrar_.Add(this, chrome::NOTIFICATION_PROFILE_DESTROYED,
251 content::Source<BrowserContext>(original_context));
252 }
253
254 // Note: event_page_idle_time_ must be sufficiently larger (e.g. 2x) than
255 // kKeepaliveThrottleIntervalInSeconds in ppapi/proxy/plugin_globals.
256 event_page_idle_time_ = base::TimeDelta::FromSeconds(10);
257 unsigned idle_time_msec = 0;
258 if (base::StringToUint(CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
259 extensions::switches::kEventPageIdleTime), &idle_time_msec)) {
260 CHECK_GT(idle_time_msec, 0u); // OnKeepaliveImpulseCheck requires non zero.
261 event_page_idle_time_ = base::TimeDelta::FromMilliseconds(idle_time_msec);
262 }
263 event_page_suspending_time_ = base::TimeDelta::FromSeconds(5);
264 unsigned suspending_time_msec = 0;
265 if (base::StringToUint(CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
266 extensions::switches::kEventPageSuspendingTime),
267 &suspending_time_msec)) {
268 event_page_suspending_time_ =
269 base::TimeDelta::FromMilliseconds(suspending_time_msec);
270 }
271
272 content::DevToolsManager::GetInstance()->AddAgentStateCallback(
273 devtools_callback_);
274
275 OnKeepaliveImpulseCheck();
276 }
277
~ProcessManager()278 ProcessManager::~ProcessManager() {
279 CloseBackgroundHosts();
280 DCHECK(background_hosts_.empty());
281 content::DevToolsManager::GetInstance()->RemoveAgentStateCallback(
282 devtools_callback_);
283 }
284
GetAllViews() const285 const ProcessManager::ViewSet ProcessManager::GetAllViews() const {
286 ViewSet result;
287 for (ExtensionRenderViews::const_iterator iter =
288 all_extension_views_.begin();
289 iter != all_extension_views_.end(); ++iter) {
290 result.insert(iter->first);
291 }
292 return result;
293 }
294
AddObserver(ProcessManagerObserver * observer)295 void ProcessManager::AddObserver(ProcessManagerObserver* observer) {
296 observer_list_.AddObserver(observer);
297 }
298
RemoveObserver(ProcessManagerObserver * observer)299 void ProcessManager::RemoveObserver(ProcessManagerObserver* observer) {
300 observer_list_.RemoveObserver(observer);
301 }
302
CreateBackgroundHost(const Extension * extension,const GURL & url)303 bool ProcessManager::CreateBackgroundHost(const Extension* extension,
304 const GURL& url) {
305 // Hosted apps are taken care of from BackgroundContentsService. Ignore them
306 // here.
307 if (extension->is_hosted_app() ||
308 !ExtensionsBrowserClient::Get()->
309 IsBackgroundPageAllowed(GetBrowserContext())) {
310 return false;
311 }
312
313 // Don't create multiple background hosts for an extension.
314 if (GetBackgroundHostForExtension(extension->id()))
315 return true; // TODO(kalman): return false here? It might break things...
316
317 ExtensionHost* host =
318 new ExtensionHost(extension, GetSiteInstanceForURL(url), url,
319 VIEW_TYPE_EXTENSION_BACKGROUND_PAGE);
320 host->CreateRenderViewSoon();
321 OnBackgroundHostCreated(host);
322 return true;
323 }
324
GetBackgroundHostForExtension(const std::string & extension_id)325 ExtensionHost* ProcessManager::GetBackgroundHostForExtension(
326 const std::string& extension_id) {
327 for (ExtensionHostSet::iterator iter = background_hosts_.begin();
328 iter != background_hosts_.end(); ++iter) {
329 ExtensionHost* host = *iter;
330 if (host->extension_id() == extension_id)
331 return host;
332 }
333 return NULL;
334 }
335
GetRenderViewHostsForExtension(const std::string & extension_id)336 std::set<RenderViewHost*> ProcessManager::GetRenderViewHostsForExtension(
337 const std::string& extension_id) {
338 std::set<RenderViewHost*> result;
339
340 SiteInstance* site_instance = GetSiteInstanceForURL(
341 Extension::GetBaseURLFromExtensionId(extension_id));
342 if (!site_instance)
343 return result;
344
345 // Gather up all the views for that site.
346 for (ExtensionRenderViews::iterator view = all_extension_views_.begin();
347 view != all_extension_views_.end(); ++view) {
348 if (view->first->GetSiteInstance() == site_instance)
349 result.insert(view->first);
350 }
351
352 return result;
353 }
354
GetExtensionForRenderViewHost(RenderViewHost * render_view_host)355 const Extension* ProcessManager::GetExtensionForRenderViewHost(
356 RenderViewHost* render_view_host) {
357 if (!render_view_host->GetSiteInstance())
358 return NULL;
359
360 ExtensionRegistry* registry = ExtensionRegistry::Get(GetBrowserContext());
361 if (!registry)
362 return NULL;
363
364 return registry->enabled_extensions().GetByID(
365 GetExtensionID(render_view_host));
366 }
367
UnregisterRenderViewHost(RenderViewHost * render_view_host)368 void ProcessManager::UnregisterRenderViewHost(
369 RenderViewHost* render_view_host) {
370 ExtensionRenderViews::iterator view =
371 all_extension_views_.find(render_view_host);
372 if (view == all_extension_views_.end())
373 return;
374
375 OnRenderViewHostUnregistered(GetBrowserContext(), render_view_host);
376 ViewType view_type = view->second;
377 all_extension_views_.erase(view);
378
379 // Keepalive count, balanced in RegisterRenderViewHost.
380 if (view_type != VIEW_TYPE_INVALID &&
381 view_type != VIEW_TYPE_EXTENSION_BACKGROUND_PAGE) {
382 const Extension* extension = GetExtensionForRenderViewHost(
383 render_view_host);
384 if (extension)
385 DecrementLazyKeepaliveCount(extension);
386 }
387 }
388
RegisterRenderViewHost(RenderViewHost * render_view_host)389 bool ProcessManager::RegisterRenderViewHost(RenderViewHost* render_view_host) {
390 const Extension* extension = GetExtensionForRenderViewHost(
391 render_view_host);
392 if (!extension)
393 return false;
394
395 WebContents* web_contents = WebContents::FromRenderViewHost(render_view_host);
396 all_extension_views_[render_view_host] = GetViewType(web_contents);
397
398 // Keep the lazy background page alive as long as any non-background-page
399 // extension views are visible. Keepalive count balanced in
400 // UnregisterRenderViewHost.
401 IncrementLazyKeepaliveCountForView(render_view_host);
402 return true;
403 }
404
GetSiteInstanceForURL(const GURL & url)405 SiteInstance* ProcessManager::GetSiteInstanceForURL(const GURL& url) {
406 return site_instance_->GetRelatedSiteInstance(url);
407 }
408
IsBackgroundHostClosing(const std::string & extension_id)409 bool ProcessManager::IsBackgroundHostClosing(const std::string& extension_id) {
410 ExtensionHost* host = GetBackgroundHostForExtension(extension_id);
411 return (host && background_page_data_[extension_id].is_closing);
412 }
413
GetLazyKeepaliveCount(const Extension * extension)414 int ProcessManager::GetLazyKeepaliveCount(const Extension* extension) {
415 if (!BackgroundInfo::HasLazyBackgroundPage(extension))
416 return 0;
417
418 return background_page_data_[extension->id()].lazy_keepalive_count;
419 }
420
IncrementLazyKeepaliveCount(const Extension * extension)421 void ProcessManager::IncrementLazyKeepaliveCount(const Extension* extension) {
422 if (!BackgroundInfo::HasLazyBackgroundPage(extension))
423 return;
424
425 int& count = background_page_data_[extension->id()].lazy_keepalive_count;
426 if (++count == 1)
427 OnLazyBackgroundPageActive(extension->id());
428 }
429
DecrementLazyKeepaliveCount(const Extension * extension)430 void ProcessManager::DecrementLazyKeepaliveCount(const Extension* extension) {
431 if (!BackgroundInfo::HasLazyBackgroundPage(extension))
432 return;
433 DecrementLazyKeepaliveCount(extension->id());
434 }
435
DecrementLazyKeepaliveCount(const std::string & extension_id)436 void ProcessManager::DecrementLazyKeepaliveCount(
437 const std::string& extension_id) {
438 int& count = background_page_data_[extension_id].lazy_keepalive_count;
439 DCHECK(count > 0 ||
440 !ExtensionRegistry::Get(GetBrowserContext())
441 ->enabled_extensions()
442 .Contains(extension_id));
443
444 // If we reach a zero keepalive count when the lazy background page is about
445 // to be closed, incrementing close_sequence_id will cancel the close
446 // sequence and cause the background page to linger. So check is_closing
447 // before initiating another close sequence.
448 if (--count == 0 && !background_page_data_[extension_id].is_closing) {
449 base::MessageLoop::current()->PostDelayedTask(
450 FROM_HERE,
451 base::Bind(&ProcessManager::OnLazyBackgroundPageIdle,
452 weak_ptr_factory_.GetWeakPtr(), extension_id,
453 ++background_page_data_[extension_id].close_sequence_id),
454 event_page_idle_time_);
455 }
456 }
457
IncrementLazyKeepaliveCountForView(RenderViewHost * render_view_host)458 void ProcessManager::IncrementLazyKeepaliveCountForView(
459 RenderViewHost* render_view_host) {
460 WebContents* web_contents =
461 WebContents::FromRenderViewHost(render_view_host);
462 ViewType view_type = GetViewType(web_contents);
463 if (view_type != VIEW_TYPE_INVALID &&
464 view_type != VIEW_TYPE_EXTENSION_BACKGROUND_PAGE) {
465 const Extension* extension = GetExtensionForRenderViewHost(
466 render_view_host);
467 if (extension)
468 IncrementLazyKeepaliveCount(extension);
469 }
470 }
471
472 // This implementation layers on top of the keepalive count. An impulse sets
473 // a per extension flag. On a regular interval that flag is checked. Changes
474 // from the flag not being set to set cause an IncrementLazyKeepaliveCount.
KeepaliveImpulse(const Extension * extension)475 void ProcessManager::KeepaliveImpulse(const Extension* extension) {
476 if (!BackgroundInfo::HasLazyBackgroundPage(extension))
477 return;
478
479 BackgroundPageData& bd = background_page_data_[extension->id()];
480
481 if (!bd.keepalive_impulse) {
482 bd.keepalive_impulse = true;
483 if (!bd.previous_keepalive_impulse) {
484 IncrementLazyKeepaliveCount(extension);
485 }
486 }
487
488 if (!keepalive_impulse_callback_for_testing_.is_null()) {
489 ImpulseCallbackForTesting callback_may_clear_callbacks_reentrantly =
490 keepalive_impulse_callback_for_testing_;
491 callback_may_clear_callbacks_reentrantly.Run(extension->id());
492 }
493 }
494
495 // DecrementLazyKeepaliveCount is called when no calls to KeepaliveImpulse
496 // have been made for at least event_page_idle_time_. In the best case an
497 // impulse was made just before being cleared, and the decrement will occur
498 // event_page_idle_time_ later, causing a 2 * event_page_idle_time_ total time
499 // for extension to be shut down based on impulses. Worst case is an impulse
500 // just after a clear, adding one check cycle and resulting in 3x total time.
OnKeepaliveImpulseCheck()501 void ProcessManager::OnKeepaliveImpulseCheck() {
502 for (BackgroundPageDataMap::iterator i = background_page_data_.begin();
503 i != background_page_data_.end();
504 ++i) {
505 if (i->second.previous_keepalive_impulse && !i->second.keepalive_impulse) {
506 DecrementLazyKeepaliveCount(i->first);
507 if (!keepalive_impulse_decrement_callback_for_testing_.is_null()) {
508 ImpulseCallbackForTesting callback_may_clear_callbacks_reentrantly =
509 keepalive_impulse_decrement_callback_for_testing_;
510 callback_may_clear_callbacks_reentrantly.Run(i->first);
511 }
512 }
513
514 i->second.previous_keepalive_impulse = i->second.keepalive_impulse;
515 i->second.keepalive_impulse = false;
516 }
517
518 // OnKeepaliveImpulseCheck() is always called in constructor, but in unit
519 // tests there will be no message loop. In that event don't schedule tasks.
520 if (base::MessageLoop::current()) {
521 base::MessageLoop::current()->PostDelayedTask(
522 FROM_HERE,
523 base::Bind(&ProcessManager::OnKeepaliveImpulseCheck,
524 weak_ptr_factory_.GetWeakPtr()),
525 event_page_idle_time_);
526 }
527 }
528
OnLazyBackgroundPageIdle(const std::string & extension_id,int sequence_id)529 void ProcessManager::OnLazyBackgroundPageIdle(const std::string& extension_id,
530 int sequence_id) {
531 ExtensionHost* host = GetBackgroundHostForExtension(extension_id);
532 if (host && !background_page_data_[extension_id].is_closing &&
533 sequence_id == background_page_data_[extension_id].close_sequence_id) {
534 // Tell the renderer we are about to close. This is a simple ping that the
535 // renderer will respond to. The purpose is to control sequencing: if the
536 // extension remains idle until the renderer responds with an ACK, then we
537 // know that the extension process is ready to shut down. If our
538 // close_sequence_id has already changed, then we would ignore the
539 // ShouldSuspendAck, so we don't send the ping.
540 host->render_view_host()->Send(new ExtensionMsg_ShouldSuspend(
541 extension_id, sequence_id));
542 }
543 }
544
OnLazyBackgroundPageActive(const std::string & extension_id)545 void ProcessManager::OnLazyBackgroundPageActive(
546 const std::string& extension_id) {
547 ExtensionHost* host = GetBackgroundHostForExtension(extension_id);
548 if (host && !background_page_data_[extension_id].is_closing) {
549 // Cancel the current close sequence by changing the close_sequence_id,
550 // which causes us to ignore the next ShouldSuspendAck.
551 ++background_page_data_[extension_id].close_sequence_id;
552 }
553 }
554
OnShouldSuspendAck(const std::string & extension_id,int sequence_id)555 void ProcessManager::OnShouldSuspendAck(const std::string& extension_id,
556 int sequence_id) {
557 ExtensionHost* host = GetBackgroundHostForExtension(extension_id);
558 if (host &&
559 sequence_id == background_page_data_[extension_id].close_sequence_id) {
560 host->render_view_host()->Send(new ExtensionMsg_Suspend(extension_id));
561 }
562 }
563
OnSuspendAck(const std::string & extension_id)564 void ProcessManager::OnSuspendAck(const std::string& extension_id) {
565 background_page_data_[extension_id].is_closing = true;
566 int sequence_id = background_page_data_[extension_id].close_sequence_id;
567 base::MessageLoop::current()->PostDelayedTask(
568 FROM_HERE,
569 base::Bind(&ProcessManager::CloseLazyBackgroundPageNow,
570 weak_ptr_factory_.GetWeakPtr(), extension_id, sequence_id),
571 event_page_suspending_time_);
572 }
573
CloseLazyBackgroundPageNow(const std::string & extension_id,int sequence_id)574 void ProcessManager::CloseLazyBackgroundPageNow(const std::string& extension_id,
575 int sequence_id) {
576 ExtensionHost* host = GetBackgroundHostForExtension(extension_id);
577 if (host &&
578 sequence_id == background_page_data_[extension_id].close_sequence_id) {
579 ExtensionHost* host = GetBackgroundHostForExtension(extension_id);
580 if (host)
581 CloseBackgroundHost(host);
582 }
583 }
584
OnNetworkRequestStarted(content::RenderFrameHost * render_frame_host)585 void ProcessManager::OnNetworkRequestStarted(
586 content::RenderFrameHost* render_frame_host) {
587 ExtensionHost* host = GetBackgroundHostForExtension(
588 GetExtensionIDFromFrame(render_frame_host));
589 if (host && IsFrameInExtensionHost(host, render_frame_host))
590 IncrementLazyKeepaliveCount(host->extension());
591 }
592
OnNetworkRequestDone(content::RenderFrameHost * render_frame_host)593 void ProcessManager::OnNetworkRequestDone(
594 content::RenderFrameHost* render_frame_host) {
595 ExtensionHost* host = GetBackgroundHostForExtension(
596 GetExtensionIDFromFrame(render_frame_host));
597 if (host && IsFrameInExtensionHost(host, render_frame_host))
598 DecrementLazyKeepaliveCount(host->extension());
599 }
600
CancelSuspend(const Extension * extension)601 void ProcessManager::CancelSuspend(const Extension* extension) {
602 bool& is_closing = background_page_data_[extension->id()].is_closing;
603 ExtensionHost* host = GetBackgroundHostForExtension(extension->id());
604 if (host && is_closing) {
605 is_closing = false;
606 host->render_view_host()->Send(
607 new ExtensionMsg_CancelSuspend(extension->id()));
608 // This increment / decrement is to simulate an instantaneous event. This
609 // has the effect of invalidating close_sequence_id, preventing any in
610 // progress closes from completing and starting a new close process if
611 // necessary.
612 IncrementLazyKeepaliveCount(extension);
613 DecrementLazyKeepaliveCount(extension);
614 }
615 }
616
OnBrowserWindowReady()617 void ProcessManager::OnBrowserWindowReady() {
618 // If the extension system isn't ready yet the background hosts will be
619 // created via NOTIFICATION_EXTENSIONS_READY below.
620 ExtensionSystem* system = ExtensionSystem::Get(GetBrowserContext());
621 if (!system->ready().is_signaled())
622 return;
623
624 CreateBackgroundHostsForProfileStartup();
625 }
626
GetBrowserContext() const627 content::BrowserContext* ProcessManager::GetBrowserContext() const {
628 return site_instance_->GetBrowserContext();
629 }
630
SetKeepaliveImpulseCallbackForTesting(const ImpulseCallbackForTesting & callback)631 void ProcessManager::SetKeepaliveImpulseCallbackForTesting(
632 const ImpulseCallbackForTesting& callback) {
633 keepalive_impulse_callback_for_testing_ = callback;
634 }
635
SetKeepaliveImpulseDecrementCallbackForTesting(const ImpulseCallbackForTesting & callback)636 void ProcessManager::SetKeepaliveImpulseDecrementCallbackForTesting(
637 const ImpulseCallbackForTesting& callback) {
638 keepalive_impulse_decrement_callback_for_testing_ = callback;
639 }
640
Observe(int type,const content::NotificationSource & source,const content::NotificationDetails & details)641 void ProcessManager::Observe(int type,
642 const content::NotificationSource& source,
643 const content::NotificationDetails& details) {
644 switch (type) {
645 case chrome::NOTIFICATION_EXTENSIONS_READY:
646 case chrome::NOTIFICATION_PROFILE_CREATED: {
647 // Don't load background hosts now if the loading should be deferred.
648 // Instead they will be loaded when a browser window for this profile
649 // (or an incognito profile from this profile) is ready.
650 if (DeferLoadingBackgroundHosts())
651 break;
652
653 CreateBackgroundHostsForProfileStartup();
654 break;
655 }
656
657 case chrome::NOTIFICATION_EXTENSION_LOADED_DEPRECATED: {
658 BrowserContext* context = content::Source<BrowserContext>(source).ptr();
659 ExtensionSystem* system = ExtensionSystem::Get(context);
660 if (system->ready().is_signaled()) {
661 // The extension system is ready, so create the background host.
662 const Extension* extension =
663 content::Details<const Extension>(details).ptr();
664 CreateBackgroundHostForExtensionLoad(this, extension);
665 }
666 break;
667 }
668
669 case chrome::NOTIFICATION_EXTENSION_UNLOADED_DEPRECATED: {
670 const Extension* extension =
671 content::Details<UnloadedExtensionInfo>(details)->extension;
672 for (ExtensionHostSet::iterator iter = background_hosts_.begin();
673 iter != background_hosts_.end(); ++iter) {
674 ExtensionHost* host = *iter;
675 if (host->extension_id() == extension->id()) {
676 CloseBackgroundHost(host);
677 break;
678 }
679 }
680 UnregisterExtension(extension->id());
681 break;
682 }
683
684 case chrome::NOTIFICATION_EXTENSION_HOST_DESTROYED: {
685 ExtensionHost* host = content::Details<ExtensionHost>(details).ptr();
686 if (background_hosts_.erase(host)) {
687 ClearBackgroundPageData(host->extension()->id());
688 background_page_data_[host->extension()->id()].since_suspended.reset(
689 new base::ElapsedTimer());
690 }
691 break;
692 }
693
694 case chrome::NOTIFICATION_EXTENSION_HOST_VIEW_SHOULD_CLOSE: {
695 ExtensionHost* host = content::Details<ExtensionHost>(details).ptr();
696 if (host->extension_host_type() == VIEW_TYPE_EXTENSION_BACKGROUND_PAGE) {
697 CloseBackgroundHost(host);
698 }
699 break;
700 }
701
702 case content::NOTIFICATION_RENDER_VIEW_HOST_CHANGED: {
703 // We get this notification both for new WebContents and when one
704 // has its RenderViewHost replaced (e.g. when a user does a cross-site
705 // navigation away from an extension URL). For the replaced case, we must
706 // unregister the old RVH so it doesn't count as an active view that would
707 // keep the event page alive.
708 WebContents* contents = content::Source<WebContents>(source).ptr();
709 if (contents->GetBrowserContext() != GetBrowserContext())
710 break;
711
712 typedef std::pair<RenderViewHost*, RenderViewHost*> RVHPair;
713 RVHPair* switched_details = content::Details<RVHPair>(details).ptr();
714 if (switched_details->first)
715 UnregisterRenderViewHost(switched_details->first);
716
717 // The above will unregister a RVH when it gets swapped out with a new
718 // one. However we need to watch the WebContents to know when a RVH is
719 // deleted because the WebContents has gone away.
720 if (RegisterRenderViewHost(switched_details->second)) {
721 RenderViewHostDestructionObserver::CreateForWebContents(contents);
722 }
723 break;
724 }
725
726 case content::NOTIFICATION_WEB_CONTENTS_CONNECTED: {
727 WebContents* contents = content::Source<WebContents>(source).ptr();
728 if (contents->GetBrowserContext() != GetBrowserContext())
729 break;
730 const Extension* extension = GetExtensionForRenderViewHost(
731 contents->GetRenderViewHost());
732 if (!extension)
733 return;
734
735 // RegisterRenderViewHost is called too early (before the process is
736 // available), so we need to wait until now to notify.
737 content::NotificationService::current()->Notify(
738 chrome::NOTIFICATION_EXTENSION_VIEW_REGISTERED,
739 content::Source<BrowserContext>(GetBrowserContext()),
740 content::Details<RenderViewHost>(contents->GetRenderViewHost()));
741 break;
742 }
743
744 case chrome::NOTIFICATION_PROFILE_DESTROYED: {
745 // Close background hosts when the last browser is closed so that they
746 // have time to shutdown various objects on different threads. Our
747 // destructor is called too late in the shutdown sequence.
748 CloseBackgroundHosts();
749 break;
750 }
751
752 default:
753 NOTREACHED();
754 }
755 }
756
OnDevToolsStateChanged(content::DevToolsAgentHost * agent_host,bool attached)757 void ProcessManager::OnDevToolsStateChanged(
758 content::DevToolsAgentHost* agent_host,
759 bool attached) {
760 RenderViewHost* rvh = agent_host->GetRenderViewHost();
761 // Ignore unrelated notifications.
762 if (!rvh ||
763 rvh->GetSiteInstance()->GetProcess()->GetBrowserContext() !=
764 GetBrowserContext())
765 return;
766 if (GetViewType(WebContents::FromRenderViewHost(rvh)) !=
767 VIEW_TYPE_EXTENSION_BACKGROUND_PAGE)
768 return;
769 const Extension* extension = GetExtensionForRenderViewHost(rvh);
770 if (!extension)
771 return;
772 if (attached) {
773 // Keep the lazy background page alive while it's being inspected.
774 CancelSuspend(extension);
775 IncrementLazyKeepaliveCount(extension);
776 } else {
777 DecrementLazyKeepaliveCount(extension);
778 }
779 }
780
CreateBackgroundHostsForProfileStartup()781 void ProcessManager::CreateBackgroundHostsForProfileStartup() {
782 if (startup_background_hosts_created_ ||
783 !ExtensionsBrowserClient::Get()->
784 IsBackgroundPageAllowed(GetBrowserContext())) {
785 return;
786 }
787
788 const ExtensionSet& enabled_extensions =
789 ExtensionRegistry::Get(GetBrowserContext())->enabled_extensions();
790 for (ExtensionSet::const_iterator extension = enabled_extensions.begin();
791 extension != enabled_extensions.end();
792 ++extension) {
793 CreateBackgroundHostForExtensionLoad(this, extension->get());
794
795 FOR_EACH_OBSERVER(ProcessManagerObserver,
796 observer_list_,
797 OnBackgroundHostStartup(*extension));
798 }
799 startup_background_hosts_created_ = true;
800
801 // Background pages should only be loaded once. To prevent any further loads
802 // occurring, we remove the notification listeners.
803 BrowserContext* original_context =
804 ExtensionsBrowserClient::Get()->GetOriginalContext(GetBrowserContext());
805 if (registrar_.IsRegistered(
806 this,
807 chrome::NOTIFICATION_PROFILE_CREATED,
808 content::Source<BrowserContext>(original_context))) {
809 registrar_.Remove(this,
810 chrome::NOTIFICATION_PROFILE_CREATED,
811 content::Source<BrowserContext>(original_context));
812 }
813 if (registrar_.IsRegistered(
814 this,
815 chrome::NOTIFICATION_EXTENSIONS_READY,
816 content::Source<BrowserContext>(original_context))) {
817 registrar_.Remove(this,
818 chrome::NOTIFICATION_EXTENSIONS_READY,
819 content::Source<BrowserContext>(original_context));
820 }
821 }
822
OnBackgroundHostCreated(ExtensionHost * host)823 void ProcessManager::OnBackgroundHostCreated(ExtensionHost* host) {
824 DCHECK_EQ(GetBrowserContext(), host->browser_context());
825 background_hosts_.insert(host);
826
827 if (BackgroundInfo::HasLazyBackgroundPage(host->extension())) {
828 linked_ptr<base::ElapsedTimer> since_suspended(
829 background_page_data_[host->extension()->id()].
830 since_suspended.release());
831 if (since_suspended.get()) {
832 UMA_HISTOGRAM_LONG_TIMES("Extensions.EventPageIdleTime",
833 since_suspended->Elapsed());
834 }
835 }
836 }
837
CloseBackgroundHost(ExtensionHost * host)838 void ProcessManager::CloseBackgroundHost(ExtensionHost* host) {
839 CHECK(host->extension_host_type() ==
840 VIEW_TYPE_EXTENSION_BACKGROUND_PAGE);
841 delete host;
842 // |host| should deregister itself from our structures.
843 CHECK(background_hosts_.find(host) == background_hosts_.end());
844 }
845
CloseBackgroundHosts()846 void ProcessManager::CloseBackgroundHosts() {
847 for (ExtensionHostSet::iterator iter = background_hosts_.begin();
848 iter != background_hosts_.end(); ) {
849 ExtensionHostSet::iterator current = iter++;
850 delete *current;
851 }
852 }
853
UnregisterExtension(const std::string & extension_id)854 void ProcessManager::UnregisterExtension(const std::string& extension_id) {
855 // The lazy_keepalive_count may be greater than zero at this point because
856 // RenderViewHosts are still alive. During extension reloading, they will
857 // decrement the lazy_keepalive_count to negative for the new extension
858 // instance when they are destroyed. Since we are erasing the background page
859 // data for the unloaded extension, unregister the RenderViewHosts too.
860 BrowserContext* context = GetBrowserContext();
861 for (ExtensionRenderViews::iterator it = all_extension_views_.begin();
862 it != all_extension_views_.end(); ) {
863 if (GetExtensionID(it->first) == extension_id) {
864 OnRenderViewHostUnregistered(context, it->first);
865 all_extension_views_.erase(it++);
866 } else {
867 ++it;
868 }
869 }
870
871 background_page_data_.erase(extension_id);
872 }
873
ClearBackgroundPageData(const std::string & extension_id)874 void ProcessManager::ClearBackgroundPageData(const std::string& extension_id) {
875 background_page_data_.erase(extension_id);
876
877 // Re-register all RenderViews for this extension. We do this to restore
878 // the lazy_keepalive_count (if any) to properly reflect the number of open
879 // views.
880 for (ExtensionRenderViews::const_iterator it = all_extension_views_.begin();
881 it != all_extension_views_.end(); ++it) {
882 if (GetExtensionID(it->first) == extension_id)
883 IncrementLazyKeepaliveCountForView(it->first);
884 }
885 }
886
DeferLoadingBackgroundHosts() const887 bool ProcessManager::DeferLoadingBackgroundHosts() const {
888 // The extensions embedder may have special rules about background hosts.
889 return ExtensionsBrowserClient::Get()->DeferLoadingBackgroundHosts(
890 GetBrowserContext());
891 }
892
893 //
894 // IncognitoProcessManager
895 //
896
IncognitoProcessManager(BrowserContext * incognito_context,BrowserContext * original_context,ProcessManager * original_manager)897 IncognitoProcessManager::IncognitoProcessManager(
898 BrowserContext* incognito_context,
899 BrowserContext* original_context,
900 ProcessManager* original_manager)
901 : ProcessManager(incognito_context, original_context),
902 original_manager_(original_manager) {
903 DCHECK(incognito_context->IsOffTheRecord());
904
905 // The original profile will have its own ProcessManager to
906 // load the background pages of the spanning extensions. This process
907 // manager need only worry about the split mode extensions, which is handled
908 // in the NOTIFICATION_BROWSER_WINDOW_READY notification handler.
909 registrar_.Remove(this, chrome::NOTIFICATION_EXTENSIONS_READY,
910 content::Source<BrowserContext>(original_context));
911 registrar_.Remove(this, chrome::NOTIFICATION_PROFILE_CREATED,
912 content::Source<BrowserContext>(original_context));
913 }
914
CreateBackgroundHost(const Extension * extension,const GURL & url)915 bool IncognitoProcessManager::CreateBackgroundHost(const Extension* extension,
916 const GURL& url) {
917 if (IncognitoInfo::IsSplitMode(extension)) {
918 if (ExtensionsBrowserClient::Get()->IsExtensionIncognitoEnabled(
919 extension->id(), GetBrowserContext()))
920 return ProcessManager::CreateBackgroundHost(extension, url);
921 } else {
922 // Do nothing. If an extension is spanning, then its original-profile
923 // background page is shared with incognito, so we don't create another.
924 }
925 return false;
926 }
927
GetSiteInstanceForURL(const GURL & url)928 SiteInstance* IncognitoProcessManager::GetSiteInstanceForURL(const GURL& url) {
929 ExtensionRegistry* registry = ExtensionRegistry::Get(GetBrowserContext());
930 if (registry) {
931 const Extension* extension =
932 registry->enabled_extensions().GetExtensionOrAppByURL(url);
933 if (extension && !IncognitoInfo::IsSplitMode(extension)) {
934 return original_manager_->GetSiteInstanceForURL(url);
935 }
936 }
937 return ProcessManager::GetSiteInstanceForURL(url);
938 }
939
940 } // namespace extensions
941