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