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