Upstream version 7.35.139.0
[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 "content/public/common/url_constants.h"
33 #include "extensions/browser/extension_host.h"
34 #include "extensions/browser/extension_registry.h"
35 #include "extensions/browser/extension_system.h"
36 #include "extensions/browser/extensions_browser_client.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
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
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
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
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);
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
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:
134   virtual ~RenderViewHostDestructionObserver() {}
135
136  private:
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.
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
176   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
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
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
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, chrome::NOTIFICATION_EXTENSION_LOADED,
233                  content::Source<BrowserContext>(original_context));
234   registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNLOADED_DEPRECATED,
235                  content::Source<BrowserContext>(original_context));
236   registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_HOST_DESTROYED,
237                  content::Source<BrowserContext>(context));
238   registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_HOST_VIEW_SHOULD_CLOSE,
239                  content::Source<BrowserContext>(context));
240   registrar_.Add(this, content::NOTIFICATION_RENDER_VIEW_HOST_CHANGED,
241                  content::NotificationService::AllSources());
242   registrar_.Add(this, content::NOTIFICATION_WEB_CONTENTS_CONNECTED,
243                  content::NotificationService::AllSources());
244   registrar_.Add(this, chrome::NOTIFICATION_PROFILE_CREATED,
245                  content::Source<BrowserContext>(original_context));
246   registrar_.Add(this, chrome::NOTIFICATION_PROFILE_DESTROYED,
247                  content::Source<BrowserContext>(context));
248   if (context->IsOffTheRecord()) {
249     registrar_.Add(this, chrome::NOTIFICATION_PROFILE_DESTROYED,
250                    content::Source<BrowserContext>(original_context));
251   }
252
253   // Note: event_page_idle_time_ must be sufficiently larger (e.g. 2x) than
254   // kKeepaliveThrottleIntervalInSeconds in ppapi/proxy/plugin_globals.
255   event_page_idle_time_ = base::TimeDelta::FromSeconds(10);
256   unsigned idle_time_msec = 0;
257   if (base::StringToUint(CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
258           extensions::switches::kEventPageIdleTime), &idle_time_msec)) {
259     CHECK(idle_time_msec > 0);  // OnKeepaliveImpulseCheck requires non zero.
260     event_page_idle_time_ = base::TimeDelta::FromMilliseconds(idle_time_msec);
261   }
262   event_page_suspending_time_ = base::TimeDelta::FromSeconds(5);
263   unsigned suspending_time_msec = 0;
264   if (base::StringToUint(CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
265                              extensions::switches::kEventPageSuspendingTime),
266                          &suspending_time_msec)) {
267     event_page_suspending_time_ =
268         base::TimeDelta::FromMilliseconds(suspending_time_msec);
269   }
270
271   content::DevToolsManager::GetInstance()->AddAgentStateCallback(
272       devtools_callback_);
273
274   OnKeepaliveImpulseCheck();
275 }
276
277 ProcessManager::~ProcessManager() {
278   CloseBackgroundHosts();
279   DCHECK(background_hosts_.empty());
280   content::DevToolsManager::GetInstance()->RemoveAgentStateCallback(
281       devtools_callback_);
282 }
283
284 const ProcessManager::ViewSet ProcessManager::GetAllViews() const {
285   ViewSet result;
286   for (ExtensionRenderViews::const_iterator iter =
287            all_extension_views_.begin();
288        iter != all_extension_views_.end(); ++iter) {
289     result.insert(iter->first);
290   }
291   return result;
292 }
293
294 bool ProcessManager::CreateBackgroundHost(const Extension* extension,
295                                           const GURL& url) {
296   // Hosted apps are taken care of from BackgroundContentsService. Ignore them
297   // here.
298   if (extension->is_hosted_app() ||
299       !ExtensionsBrowserClient::Get()->
300           IsBackgroundPageAllowed(GetBrowserContext())) {
301     return false;
302   }
303
304   // Don't create multiple background hosts for an extension.
305   if (GetBackgroundHostForExtension(extension->id()))
306     return true;  // TODO(kalman): return false here? It might break things...
307
308   ExtensionHost* host =
309       new ExtensionHost(extension, GetSiteInstanceForURL(url), url,
310                         VIEW_TYPE_EXTENSION_BACKGROUND_PAGE);
311   host->CreateRenderViewSoon();
312   OnBackgroundHostCreated(host);
313   return true;
314 }
315
316 ExtensionHost* ProcessManager::GetBackgroundHostForExtension(
317     const std::string& extension_id) {
318   for (ExtensionHostSet::iterator iter = background_hosts_.begin();
319        iter != background_hosts_.end(); ++iter) {
320     ExtensionHost* host = *iter;
321     if (host->extension_id() == extension_id)
322       return host;
323   }
324   return NULL;
325 }
326
327 std::set<RenderViewHost*> ProcessManager::GetRenderViewHostsForExtension(
328     const std::string& extension_id) {
329   std::set<RenderViewHost*> result;
330
331   SiteInstance* site_instance = GetSiteInstanceForURL(
332       Extension::GetBaseURLFromExtensionId(extension_id));
333   if (!site_instance)
334     return result;
335
336   // Gather up all the views for that site.
337   for (ExtensionRenderViews::iterator view = all_extension_views_.begin();
338        view != all_extension_views_.end(); ++view) {
339     if (view->first->GetSiteInstance() == site_instance)
340       result.insert(view->first);
341   }
342
343   return result;
344 }
345
346 const Extension* ProcessManager::GetExtensionForRenderViewHost(
347     RenderViewHost* render_view_host) {
348   if (!render_view_host->GetSiteInstance())
349     return NULL;
350
351   ExtensionRegistry* registry = ExtensionRegistry::Get(GetBrowserContext());
352   if (!registry)
353     return NULL;
354
355   return registry->enabled_extensions().GetByID(
356       GetExtensionID(render_view_host));
357 }
358
359 void ProcessManager::UnregisterRenderViewHost(
360     RenderViewHost* render_view_host) {
361   ExtensionRenderViews::iterator view =
362       all_extension_views_.find(render_view_host);
363   if (view == all_extension_views_.end())
364     return;
365
366   OnRenderViewHostUnregistered(GetBrowserContext(), render_view_host);
367   ViewType view_type = view->second;
368   all_extension_views_.erase(view);
369
370   // Keepalive count, balanced in RegisterRenderViewHost.
371   if (view_type != VIEW_TYPE_INVALID &&
372       view_type != VIEW_TYPE_EXTENSION_BACKGROUND_PAGE) {
373     const Extension* extension = GetExtensionForRenderViewHost(
374         render_view_host);
375     if (extension)
376       DecrementLazyKeepaliveCount(extension);
377   }
378 }
379
380 bool ProcessManager::RegisterRenderViewHost(RenderViewHost* render_view_host) {
381   const Extension* extension = GetExtensionForRenderViewHost(
382       render_view_host);
383   if (!extension)
384     return false;
385
386   WebContents* web_contents = WebContents::FromRenderViewHost(render_view_host);
387   all_extension_views_[render_view_host] = GetViewType(web_contents);
388
389   // Keep the lazy background page alive as long as any non-background-page
390   // extension views are visible. Keepalive count balanced in
391   // UnregisterRenderViewHost.
392   IncrementLazyKeepaliveCountForView(render_view_host);
393   return true;
394 }
395
396 SiteInstance* ProcessManager::GetSiteInstanceForURL(const GURL& url) {
397   return site_instance_->GetRelatedSiteInstance(url);
398 }
399
400 bool ProcessManager::IsBackgroundHostClosing(const std::string& extension_id) {
401   ExtensionHost* host = GetBackgroundHostForExtension(extension_id);
402   return (host && background_page_data_[extension_id].is_closing);
403 }
404
405 int ProcessManager::GetLazyKeepaliveCount(const Extension* extension) {
406   if (!BackgroundInfo::HasLazyBackgroundPage(extension))
407     return 0;
408
409   return background_page_data_[extension->id()].lazy_keepalive_count;
410 }
411
412 void ProcessManager::IncrementLazyKeepaliveCount(const Extension* extension) {
413   if (!BackgroundInfo::HasLazyBackgroundPage(extension))
414     return;
415
416   int& count = background_page_data_[extension->id()].lazy_keepalive_count;
417   if (++count == 1)
418     OnLazyBackgroundPageActive(extension->id());
419 }
420
421 void ProcessManager::DecrementLazyKeepaliveCount(const Extension* extension) {
422   if (!BackgroundInfo::HasLazyBackgroundPage(extension))
423     return;
424   DecrementLazyKeepaliveCount(extension->id());
425 }
426
427 void ProcessManager::DecrementLazyKeepaliveCount(
428     const std::string& extension_id) {
429   int& count = background_page_data_[extension_id].lazy_keepalive_count;
430   DCHECK_GT(count, 0);
431
432   // If we reach a zero keepalive count when the lazy background page is about
433   // to be closed, incrementing close_sequence_id will cancel the close
434   // sequence and cause the background page to linger. So check is_closing
435   // before initiating another close sequence.
436   if (--count == 0 && !background_page_data_[extension_id].is_closing) {
437     base::MessageLoop::current()->PostDelayedTask(
438         FROM_HERE,
439         base::Bind(&ProcessManager::OnLazyBackgroundPageIdle,
440                    weak_ptr_factory_.GetWeakPtr(), extension_id,
441                    ++background_page_data_[extension_id].close_sequence_id),
442         event_page_idle_time_);
443   }
444 }
445
446 void ProcessManager::IncrementLazyKeepaliveCountForView(
447     RenderViewHost* render_view_host) {
448   WebContents* web_contents =
449       WebContents::FromRenderViewHost(render_view_host);
450   ViewType view_type = GetViewType(web_contents);
451   if (view_type != VIEW_TYPE_INVALID &&
452       view_type != VIEW_TYPE_EXTENSION_BACKGROUND_PAGE) {
453     const Extension* extension = GetExtensionForRenderViewHost(
454         render_view_host);
455     if (extension)
456       IncrementLazyKeepaliveCount(extension);
457   }
458 }
459
460 // This implementation layers on top of the keepalive count. An impulse sets
461 // a per extension flag. On a regular interval that flag is checked. Changes
462 // from the flag not being set to set cause an IncrementLazyKeepaliveCount.
463 void ProcessManager::KeepaliveImpulse(const Extension* extension) {
464   if (!BackgroundInfo::HasLazyBackgroundPage(extension))
465     return;
466
467   BackgroundPageData& bd = background_page_data_[extension->id()];
468
469   if (!bd.keepalive_impulse) {
470     bd.keepalive_impulse = true;
471     if (!bd.previous_keepalive_impulse) {
472       IncrementLazyKeepaliveCount(extension);
473     }
474   }
475
476   if (!keepalive_impulse_callback_for_testing_.is_null()) {
477     ImpulseCallbackForTesting callback_may_clear_callbacks_reentrantly =
478       keepalive_impulse_callback_for_testing_;
479     callback_may_clear_callbacks_reentrantly.Run(extension->id());
480   }
481 }
482
483 // DecrementLazyKeepaliveCount is called when no calls to KeepaliveImpulse
484 // have been made for at least event_page_idle_time_. In the best case an
485 // impulse was made just before being cleared, and the decrement will occur
486 // event_page_idle_time_ later, causing a 2 * event_page_idle_time_ total time
487 // for extension to be shut down based on impulses. Worst case is an impulse
488 // just after a clear, adding one check cycle and resulting in 3x total time.
489 void ProcessManager::OnKeepaliveImpulseCheck() {
490   for (BackgroundPageDataMap::iterator i = background_page_data_.begin();
491        i != background_page_data_.end();
492        ++i) {
493     if (i->second.previous_keepalive_impulse && !i->second.keepalive_impulse) {
494       DecrementLazyKeepaliveCount(i->first);
495       if (!keepalive_impulse_decrement_callback_for_testing_.is_null()) {
496         ImpulseCallbackForTesting callback_may_clear_callbacks_reentrantly =
497           keepalive_impulse_decrement_callback_for_testing_;
498         callback_may_clear_callbacks_reentrantly.Run(i->first);
499       }
500     }
501
502     i->second.previous_keepalive_impulse = i->second.keepalive_impulse;
503     i->second.keepalive_impulse = false;
504   }
505
506   // OnKeepaliveImpulseCheck() is always called in constructor, but in unit
507   // tests there will be no message loop. In that event don't schedule tasks.
508   if (base::MessageLoop::current()) {
509     base::MessageLoop::current()->PostDelayedTask(
510         FROM_HERE,
511         base::Bind(&ProcessManager::OnKeepaliveImpulseCheck,
512                    weak_ptr_factory_.GetWeakPtr()),
513         event_page_idle_time_);
514   }
515 }
516
517 void ProcessManager::OnLazyBackgroundPageIdle(const std::string& extension_id,
518                                               int sequence_id) {
519   ExtensionHost* host = GetBackgroundHostForExtension(extension_id);
520   if (host && !background_page_data_[extension_id].is_closing &&
521       sequence_id == background_page_data_[extension_id].close_sequence_id) {
522     // Tell the renderer we are about to close. This is a simple ping that the
523     // renderer will respond to. The purpose is to control sequencing: if the
524     // extension remains idle until the renderer responds with an ACK, then we
525     // know that the extension process is ready to shut down. If our
526     // close_sequence_id has already changed, then we would ignore the
527     // ShouldSuspendAck, so we don't send the ping.
528     host->render_view_host()->Send(new ExtensionMsg_ShouldSuspend(
529         extension_id, sequence_id));
530   }
531 }
532
533 void ProcessManager::OnLazyBackgroundPageActive(
534     const std::string& extension_id) {
535   ExtensionHost* host = GetBackgroundHostForExtension(extension_id);
536   if (host && !background_page_data_[extension_id].is_closing) {
537     // Cancel the current close sequence by changing the close_sequence_id,
538     // which causes us to ignore the next ShouldSuspendAck.
539     ++background_page_data_[extension_id].close_sequence_id;
540   }
541 }
542
543 void ProcessManager::OnShouldSuspendAck(const std::string& extension_id,
544                                         int sequence_id) {
545   ExtensionHost* host = GetBackgroundHostForExtension(extension_id);
546   if (host &&
547       sequence_id == background_page_data_[extension_id].close_sequence_id) {
548     host->render_view_host()->Send(new ExtensionMsg_Suspend(extension_id));
549   }
550 }
551
552 void ProcessManager::OnSuspendAck(const std::string& extension_id) {
553   background_page_data_[extension_id].is_closing = true;
554   int sequence_id = background_page_data_[extension_id].close_sequence_id;
555   base::MessageLoop::current()->PostDelayedTask(
556       FROM_HERE,
557       base::Bind(&ProcessManager::CloseLazyBackgroundPageNow,
558                  weak_ptr_factory_.GetWeakPtr(), extension_id, sequence_id),
559       event_page_suspending_time_);
560 }
561
562 void ProcessManager::CloseLazyBackgroundPageNow(const std::string& extension_id,
563                                                 int sequence_id) {
564   ExtensionHost* host = GetBackgroundHostForExtension(extension_id);
565   if (host &&
566       sequence_id == background_page_data_[extension_id].close_sequence_id) {
567     ExtensionHost* host = GetBackgroundHostForExtension(extension_id);
568     if (host)
569       CloseBackgroundHost(host);
570   }
571 }
572
573 void ProcessManager::OnNetworkRequestStarted(
574     content::RenderFrameHost* render_frame_host) {
575   ExtensionHost* host = GetBackgroundHostForExtension(
576       GetExtensionIDFromFrame(render_frame_host));
577   if (host && IsFrameInExtensionHost(host, render_frame_host))
578     IncrementLazyKeepaliveCount(host->extension());
579 }
580
581 void ProcessManager::OnNetworkRequestDone(
582     content::RenderFrameHost* render_frame_host) {
583   ExtensionHost* host = GetBackgroundHostForExtension(
584       GetExtensionIDFromFrame(render_frame_host));
585   if (host && IsFrameInExtensionHost(host, render_frame_host))
586     DecrementLazyKeepaliveCount(host->extension());
587 }
588
589 void ProcessManager::CancelSuspend(const Extension* extension) {
590   bool& is_closing = background_page_data_[extension->id()].is_closing;
591   ExtensionHost* host = GetBackgroundHostForExtension(extension->id());
592   if (host && is_closing) {
593     is_closing = false;
594     host->render_view_host()->Send(
595         new ExtensionMsg_CancelSuspend(extension->id()));
596     // This increment / decrement is to simulate an instantaneous event. This
597     // has the effect of invalidating close_sequence_id, preventing any in
598     // progress closes from completing and starting a new close process if
599     // necessary.
600     IncrementLazyKeepaliveCount(extension);
601     DecrementLazyKeepaliveCount(extension);
602   }
603 }
604
605 void ProcessManager::OnBrowserWindowReady() {
606   // If the extension system isn't ready yet the background hosts will be
607   // created via NOTIFICATION_EXTENSIONS_READY below.
608   ExtensionSystem* system = ExtensionSystem::Get(GetBrowserContext());
609   if (!system->ready().is_signaled())
610     return;
611
612   CreateBackgroundHostsForProfileStartup();
613 }
614
615 content::BrowserContext* ProcessManager::GetBrowserContext() const {
616   return site_instance_->GetBrowserContext();
617 }
618
619 void ProcessManager::SetKeepaliveImpulseCallbackForTesting(
620     const ImpulseCallbackForTesting& callback) {
621   keepalive_impulse_callback_for_testing_ = callback;
622 }
623
624 void ProcessManager::SetKeepaliveImpulseDecrementCallbackForTesting(
625     const ImpulseCallbackForTesting& callback) {
626   keepalive_impulse_decrement_callback_for_testing_ = callback;
627 }
628
629 void ProcessManager::Observe(int type,
630                              const content::NotificationSource& source,
631                              const content::NotificationDetails& details) {
632   switch (type) {
633     case chrome::NOTIFICATION_EXTENSIONS_READY:
634     case chrome::NOTIFICATION_PROFILE_CREATED: {
635        // Don't load background hosts now if the loading should be deferred.
636        // Instead they will be loaded when a browser window for this profile
637        // (or an incognito profile from this profile) is ready.
638        if (DeferLoadingBackgroundHosts())
639          break;
640
641       CreateBackgroundHostsForProfileStartup();
642       break;
643     }
644
645     case chrome::NOTIFICATION_EXTENSION_LOADED: {
646       BrowserContext* context = content::Source<BrowserContext>(source).ptr();
647       ExtensionSystem* system = ExtensionSystem::Get(context);
648       if (system->ready().is_signaled()) {
649         // The extension system is ready, so create the background host.
650         const Extension* extension =
651             content::Details<const Extension>(details).ptr();
652         CreateBackgroundHostForExtensionLoad(this, extension);
653       }
654       break;
655     }
656
657     case chrome::NOTIFICATION_EXTENSION_UNLOADED_DEPRECATED: {
658       const Extension* extension =
659           content::Details<UnloadedExtensionInfo>(details)->extension;
660       for (ExtensionHostSet::iterator iter = background_hosts_.begin();
661            iter != background_hosts_.end(); ++iter) {
662         ExtensionHost* host = *iter;
663         if (host->extension_id() == extension->id()) {
664           CloseBackgroundHost(host);
665           break;
666         }
667       }
668       UnregisterExtension(extension->id());
669       break;
670     }
671
672     case chrome::NOTIFICATION_EXTENSION_HOST_DESTROYED: {
673       ExtensionHost* host = content::Details<ExtensionHost>(details).ptr();
674       if (background_hosts_.erase(host)) {
675         ClearBackgroundPageData(host->extension()->id());
676         background_page_data_[host->extension()->id()].since_suspended.reset(
677             new base::ElapsedTimer());
678       }
679       break;
680     }
681
682     case chrome::NOTIFICATION_EXTENSION_HOST_VIEW_SHOULD_CLOSE: {
683       ExtensionHost* host = content::Details<ExtensionHost>(details).ptr();
684       if (host->extension_host_type() == VIEW_TYPE_EXTENSION_BACKGROUND_PAGE) {
685         CloseBackgroundHost(host);
686       }
687       break;
688     }
689
690     case content::NOTIFICATION_RENDER_VIEW_HOST_CHANGED: {
691       // We get this notification both for new WebContents and when one
692       // has its RenderViewHost replaced (e.g. when a user does a cross-site
693       // navigation away from an extension URL). For the replaced case, we must
694       // unregister the old RVH so it doesn't count as an active view that would
695       // keep the event page alive.
696       WebContents* contents = content::Source<WebContents>(source).ptr();
697       if (contents->GetBrowserContext() != GetBrowserContext())
698         break;
699
700       typedef std::pair<RenderViewHost*, RenderViewHost*> RVHPair;
701       RVHPair* switched_details = content::Details<RVHPair>(details).ptr();
702       if (switched_details->first)
703         UnregisterRenderViewHost(switched_details->first);
704
705       // The above will unregister a RVH when it gets swapped out with a new
706       // one. However we need to watch the WebContents to know when a RVH is
707       // deleted because the WebContents has gone away.
708       if (RegisterRenderViewHost(switched_details->second)) {
709         RenderViewHostDestructionObserver::CreateForWebContents(contents);
710       }
711       break;
712     }
713
714     case content::NOTIFICATION_WEB_CONTENTS_CONNECTED: {
715       WebContents* contents = content::Source<WebContents>(source).ptr();
716       if (contents->GetBrowserContext() != GetBrowserContext())
717         break;
718       const Extension* extension = GetExtensionForRenderViewHost(
719           contents->GetRenderViewHost());
720       if (!extension)
721         return;
722
723       // RegisterRenderViewHost is called too early (before the process is
724       // available), so we need to wait until now to notify.
725       content::NotificationService::current()->Notify(
726           chrome::NOTIFICATION_EXTENSION_VIEW_REGISTERED,
727           content::Source<BrowserContext>(GetBrowserContext()),
728           content::Details<RenderViewHost>(contents->GetRenderViewHost()));
729       break;
730     }
731
732     case chrome::NOTIFICATION_PROFILE_DESTROYED: {
733       // Close background hosts when the last browser is closed so that they
734       // have time to shutdown various objects on different threads. Our
735       // destructor is called too late in the shutdown sequence.
736       CloseBackgroundHosts();
737       break;
738     }
739
740     default:
741       NOTREACHED();
742   }
743 }
744
745 void ProcessManager::OnDevToolsStateChanged(
746     content::DevToolsAgentHost* agent_host,
747     bool attached) {
748   RenderViewHost* rvh = agent_host->GetRenderViewHost();
749   // Ignore unrelated notifications.
750   if (!rvh ||
751       rvh->GetSiteInstance()->GetProcess()->GetBrowserContext() !=
752           GetBrowserContext())
753     return;
754   if (GetViewType(WebContents::FromRenderViewHost(rvh)) !=
755       VIEW_TYPE_EXTENSION_BACKGROUND_PAGE)
756     return;
757   const Extension* extension = GetExtensionForRenderViewHost(rvh);
758   if (!extension)
759     return;
760   if (attached) {
761     // Keep the lazy background page alive while it's being inspected.
762     CancelSuspend(extension);
763     IncrementLazyKeepaliveCount(extension);
764   } else {
765     DecrementLazyKeepaliveCount(extension);
766   }
767 }
768
769 void ProcessManager::CreateBackgroundHostsForProfileStartup() {
770   if (startup_background_hosts_created_ ||
771       !ExtensionsBrowserClient::Get()->
772           IsBackgroundPageAllowed(GetBrowserContext())) {
773     return;
774   }
775
776   const ExtensionSet& enabled_extensions =
777       ExtensionRegistry::Get(GetBrowserContext())->enabled_extensions();
778   for (ExtensionSet::const_iterator extension = enabled_extensions.begin();
779        extension != enabled_extensions.end();
780        ++extension) {
781     CreateBackgroundHostForExtensionLoad(this, extension->get());
782
783     RuntimeEventRouter::DispatchOnStartupEvent(GetBrowserContext(),
784                                                (*extension)->id());
785   }
786   startup_background_hosts_created_ = true;
787
788   // Background pages should only be loaded once. To prevent any further loads
789   // occurring, we remove the notification listeners.
790   BrowserContext* original_context =
791       ExtensionsBrowserClient::Get()->GetOriginalContext(GetBrowserContext());
792   if (registrar_.IsRegistered(
793           this,
794           chrome::NOTIFICATION_PROFILE_CREATED,
795           content::Source<BrowserContext>(original_context))) {
796     registrar_.Remove(this,
797                       chrome::NOTIFICATION_PROFILE_CREATED,
798                       content::Source<BrowserContext>(original_context));
799   }
800   if (registrar_.IsRegistered(
801           this,
802           chrome::NOTIFICATION_EXTENSIONS_READY,
803           content::Source<BrowserContext>(original_context))) {
804     registrar_.Remove(this,
805                       chrome::NOTIFICATION_EXTENSIONS_READY,
806                       content::Source<BrowserContext>(original_context));
807   }
808 }
809
810 void ProcessManager::OnBackgroundHostCreated(ExtensionHost* host) {
811   DCHECK_EQ(GetBrowserContext(), host->browser_context());
812   background_hosts_.insert(host);
813
814   if (BackgroundInfo::HasLazyBackgroundPage(host->extension())) {
815     linked_ptr<base::ElapsedTimer> since_suspended(
816         background_page_data_[host->extension()->id()].
817             since_suspended.release());
818     if (since_suspended.get()) {
819       UMA_HISTOGRAM_LONG_TIMES("Extensions.EventPageIdleTime",
820                                since_suspended->Elapsed());
821     }
822   }
823 }
824
825 void ProcessManager::CloseBackgroundHost(ExtensionHost* host) {
826   CHECK(host->extension_host_type() ==
827         VIEW_TYPE_EXTENSION_BACKGROUND_PAGE);
828   delete host;
829   // |host| should deregister itself from our structures.
830   CHECK(background_hosts_.find(host) == background_hosts_.end());
831 }
832
833 void ProcessManager::CloseBackgroundHosts() {
834   for (ExtensionHostSet::iterator iter = background_hosts_.begin();
835        iter != background_hosts_.end(); ) {
836     ExtensionHostSet::iterator current = iter++;
837     delete *current;
838   }
839 }
840
841 void ProcessManager::UnregisterExtension(const std::string& extension_id) {
842   // The lazy_keepalive_count may be greater than zero at this point because
843   // RenderViewHosts are still alive. During extension reloading, they will
844   // decrement the lazy_keepalive_count to negative for the new extension
845   // instance when they are destroyed. Since we are erasing the background page
846   // data for the unloaded extension, unregister the RenderViewHosts too.
847   BrowserContext* context = GetBrowserContext();
848   for (ExtensionRenderViews::iterator it = all_extension_views_.begin();
849        it != all_extension_views_.end(); ) {
850     if (GetExtensionID(it->first) == extension_id) {
851       OnRenderViewHostUnregistered(context, it->first);
852       all_extension_views_.erase(it++);
853     } else {
854       ++it;
855     }
856   }
857
858   background_page_data_.erase(extension_id);
859 }
860
861 void ProcessManager::ClearBackgroundPageData(const std::string& extension_id) {
862   background_page_data_.erase(extension_id);
863
864   // Re-register all RenderViews for this extension. We do this to restore
865   // the lazy_keepalive_count (if any) to properly reflect the number of open
866   // views.
867   for (ExtensionRenderViews::const_iterator it = all_extension_views_.begin();
868        it != all_extension_views_.end(); ++it) {
869     if (GetExtensionID(it->first) == extension_id)
870       IncrementLazyKeepaliveCountForView(it->first);
871   }
872 }
873
874 bool ProcessManager::DeferLoadingBackgroundHosts() const {
875   // The extensions embedder may have special rules about background hosts.
876   return ExtensionsBrowserClient::Get()->DeferLoadingBackgroundHosts(
877       GetBrowserContext());
878 }
879
880 //
881 // IncognitoProcessManager
882 //
883
884 IncognitoProcessManager::IncognitoProcessManager(
885     BrowserContext* incognito_context,
886     BrowserContext* original_context,
887     ProcessManager* original_manager)
888     : ProcessManager(incognito_context, original_context),
889       original_manager_(original_manager) {
890   DCHECK(incognito_context->IsOffTheRecord());
891
892   // The original profile will have its own ProcessManager to
893   // load the background pages of the spanning extensions. This process
894   // manager need only worry about the split mode extensions, which is handled
895   // in the NOTIFICATION_BROWSER_WINDOW_READY notification handler.
896   registrar_.Remove(this, chrome::NOTIFICATION_EXTENSIONS_READY,
897                     content::Source<BrowserContext>(original_context));
898   registrar_.Remove(this, chrome::NOTIFICATION_PROFILE_CREATED,
899                     content::Source<BrowserContext>(original_context));
900 }
901
902 bool IncognitoProcessManager::CreateBackgroundHost(const Extension* extension,
903                                                    const GURL& url) {
904   if (IncognitoInfo::IsSplitMode(extension)) {
905     if (ExtensionsBrowserClient::Get()->IsExtensionIncognitoEnabled(
906             extension->id(), GetBrowserContext()))
907       return ProcessManager::CreateBackgroundHost(extension, url);
908   } else {
909     // Do nothing. If an extension is spanning, then its original-profile
910     // background page is shared with incognito, so we don't create another.
911   }
912   return false;
913 }
914
915 SiteInstance* IncognitoProcessManager::GetSiteInstanceForURL(const GURL& url) {
916   ExtensionRegistry* registry = ExtensionRegistry::Get(GetBrowserContext());
917   if (registry) {
918     const Extension* extension =
919         registry->enabled_extensions().GetExtensionOrAppByURL(url);
920     if (extension && !IncognitoInfo::IsSplitMode(extension)) {
921       return original_manager_->GetSiteInstanceForURL(url);
922     }
923   }
924   return ProcessManager::GetSiteInstanceForURL(url);
925 }
926
927 }  // namespace extensions