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