• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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