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