• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2011 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 "chrome/browser/extensions/extension_process_manager.h"
6 
7 #include "chrome/browser/ui/browser_window.h"
8 #include "content/browser/browsing_instance.h"
9 #if defined(OS_MACOSX)
10 #include "chrome/browser/extensions/extension_host_mac.h"
11 #endif
12 #include "chrome/browser/extensions/extension_host.h"
13 #include "chrome/browser/extensions/extension_service.h"
14 #include "chrome/browser/profiles/profile.h"
15 #include "chrome/browser/ui/browser.h"
16 #include "chrome/common/extensions/extension.h"
17 #include "chrome/common/extensions/extension_action.h"
18 #include "chrome/common/extensions/extension_messages.h"
19 #include "chrome/common/url_constants.h"
20 #include "content/browser/renderer_host/render_view_host.h"
21 #include "content/browser/site_instance.h"
22 #include "content/browser/tab_contents/tab_contents.h"
23 #include "content/common/notification_service.h"
24 #include "content/common/notification_type.h"
25 
26 namespace {
27 
28 // Incognito profiles use this process manager. It is mostly a shim that decides
29 // whether to fall back on the original profile's ExtensionProcessManager based
30 // on whether a given extension uses "split" or "spanning" incognito behavior.
31 class IncognitoExtensionProcessManager : public ExtensionProcessManager {
32  public:
33   explicit IncognitoExtensionProcessManager(Profile* profile);
~IncognitoExtensionProcessManager()34   virtual ~IncognitoExtensionProcessManager() {}
35   virtual ExtensionHost* CreateView(const Extension* extension,
36                                     const GURL& url,
37                                     Browser* browser,
38                                     ViewType::Type view_type);
39   virtual void CreateBackgroundHost(const Extension* extension,
40                                     const GURL& url);
41   virtual SiteInstance* GetSiteInstanceForURL(const GURL& url);
42   virtual RenderProcessHost* GetExtensionProcess(const GURL& url);
43 
44  private:
45   // NotificationObserver:
46   virtual void Observe(NotificationType type,
47                        const NotificationSource& source,
48                        const NotificationDetails& details);
49 
50   // Returns the extension for an URL, which can either be a chrome-extension
51   // URL or a web app URL.
52   const Extension* GetExtensionOrAppByURL(const GURL& url);
53 
54   // Returns true if the extension is allowed to run in incognito mode.
55   bool IsIncognitoEnabled(const Extension* extension);
56 
57   ExtensionProcessManager* original_manager_;
58 };
59 
CreateBackgroundHost(ExtensionProcessManager * manager,const Extension * extension)60 static void CreateBackgroundHost(
61     ExtensionProcessManager* manager, const Extension* extension) {
62   // Start the process for the master page, if it exists.
63   if (extension->background_url().is_valid())
64     manager->CreateBackgroundHost(extension, extension->background_url());
65 }
66 
CreateBackgroundHosts(ExtensionProcessManager * manager,const ExtensionList * extensions)67 static void CreateBackgroundHosts(
68     ExtensionProcessManager* manager, const ExtensionList* extensions) {
69   for (ExtensionList::const_iterator extension = extensions->begin();
70        extension != extensions->end(); ++extension) {
71     CreateBackgroundHost(manager, *extension);
72   }
73 }
74 
75 }  // namespace
76 
77 extern bool g_log_bug53991;
78 
79 //
80 // ExtensionProcessManager
81 //
82 
83 // static
Create(Profile * profile)84 ExtensionProcessManager* ExtensionProcessManager::Create(Profile* profile) {
85   return (profile->IsOffTheRecord()) ?
86       new IncognitoExtensionProcessManager(profile) :
87       new ExtensionProcessManager(profile);
88 }
89 
ExtensionProcessManager(Profile * profile)90 ExtensionProcessManager::ExtensionProcessManager(Profile* profile)
91     : browsing_instance_(new BrowsingInstance(profile)) {
92   Profile* original_profile = profile->GetOriginalProfile();
93   registrar_.Add(this, NotificationType::EXTENSIONS_READY,
94                  Source<Profile>(original_profile));
95   registrar_.Add(this, NotificationType::EXTENSION_LOADED,
96                  Source<Profile>(original_profile));
97   registrar_.Add(this, NotificationType::EXTENSION_UNLOADED,
98                  Source<Profile>(original_profile));
99   registrar_.Add(this, NotificationType::EXTENSION_HOST_DESTROYED,
100                  Source<Profile>(profile));
101   registrar_.Add(this, NotificationType::RENDERER_PROCESS_TERMINATED,
102                  NotificationService::AllSources());
103   registrar_.Add(this, NotificationType::RENDERER_PROCESS_CLOSED,
104                  NotificationService::AllSources());
105   registrar_.Add(this, NotificationType::APP_TERMINATING,
106                  NotificationService::AllSources());
107 }
108 
~ExtensionProcessManager()109 ExtensionProcessManager::~ExtensionProcessManager() {
110   VLOG_IF(1, g_log_bug53991) << "~ExtensionProcessManager: " << this;
111   CloseBackgroundHosts();
112   DCHECK(background_hosts_.empty());
113 }
114 
CreateView(const Extension * extension,const GURL & url,Browser * browser,ViewType::Type view_type)115 ExtensionHost* ExtensionProcessManager::CreateView(const Extension* extension,
116                                                    const GURL& url,
117                                                    Browser* browser,
118                                                    ViewType::Type view_type) {
119   DCHECK(extension);
120   // A NULL browser may only be given for pop-up views.
121   DCHECK(browser || (!browser && view_type == ViewType::EXTENSION_POPUP));
122   ExtensionHost* host =
123 #if defined(OS_MACOSX)
124       new ExtensionHostMac(extension, GetSiteInstanceForURL(url), url,
125                            view_type);
126 #else
127       new ExtensionHost(extension, GetSiteInstanceForURL(url), url, view_type);
128 #endif
129   host->CreateView(browser);
130   OnExtensionHostCreated(host, false);
131   return host;
132 }
133 
CreateView(const GURL & url,Browser * browser,ViewType::Type view_type)134 ExtensionHost* ExtensionProcessManager::CreateView(const GURL& url,
135                                                    Browser* browser,
136                                                    ViewType::Type view_type) {
137   // A NULL browser may only be given for pop-up views.
138   DCHECK(browser || (!browser && view_type == ViewType::EXTENSION_POPUP));
139   ExtensionService* service =
140       browsing_instance_->profile()->GetExtensionService();
141   if (service) {
142     const Extension* extension = service->GetExtensionByURL(url);
143     if (extension)
144       return CreateView(extension, url, browser, view_type);
145   }
146   return NULL;
147 }
148 
CreatePopup(const Extension * extension,const GURL & url,Browser * browser)149 ExtensionHost* ExtensionProcessManager::CreatePopup(const Extension* extension,
150                                                     const GURL& url,
151                                                     Browser* browser) {
152   return CreateView(extension, url, browser, ViewType::EXTENSION_POPUP);
153 }
154 
CreatePopup(const GURL & url,Browser * browser)155 ExtensionHost* ExtensionProcessManager::CreatePopup(const GURL& url,
156                                                     Browser* browser) {
157   return CreateView(url, browser, ViewType::EXTENSION_POPUP);
158 }
159 
CreateInfobar(const Extension * extension,const GURL & url,Browser * browser)160 ExtensionHost* ExtensionProcessManager::CreateInfobar(
161     const Extension* extension, const GURL& url, Browser* browser) {
162   return CreateView(extension, url, browser, ViewType::EXTENSION_INFOBAR);
163 }
164 
CreateInfobar(const GURL & url,Browser * browser)165 ExtensionHost* ExtensionProcessManager::CreateInfobar(const GURL& url,
166                                                       Browser* browser) {
167   return CreateView(url, browser, ViewType::EXTENSION_INFOBAR);
168 }
169 
CreateBackgroundHost(const Extension * extension,const GURL & url)170 void ExtensionProcessManager::CreateBackgroundHost(
171     const Extension* extension, const GURL& url) {
172   // Hosted apps are taken care of from BackgroundContentsService. Ignore them
173   // here.
174   if (extension->is_hosted_app())
175     return;
176 
177   // Don't create multiple background hosts for an extension.
178   if (GetBackgroundHostForExtension(extension))
179     return;
180 
181   ExtensionHost* host =
182 #if defined(OS_MACOSX)
183       new ExtensionHostMac(extension, GetSiteInstanceForURL(url), url,
184                            ViewType::EXTENSION_BACKGROUND_PAGE);
185 #else
186       new ExtensionHost(extension, GetSiteInstanceForURL(url), url,
187                         ViewType::EXTENSION_BACKGROUND_PAGE);
188 #endif
189 
190   host->CreateRenderViewSoon(NULL);  // create a RenderViewHost with no view
191   OnExtensionHostCreated(host, true);
192 }
193 
OpenOptionsPage(const Extension * extension,Browser * browser)194 void ExtensionProcessManager::OpenOptionsPage(const Extension* extension,
195                                               Browser* browser) {
196   DCHECK(!extension->options_url().is_empty());
197 
198   // Force the options page to open in non-OTR window, because it won't be
199   // able to save settings from OTR.
200   if (!browser || browser->profile()->IsOffTheRecord()) {
201     browser = Browser::GetOrCreateTabbedBrowser(
202         browsing_instance_->profile()->GetOriginalProfile());
203   }
204 
205   browser->OpenURL(extension->options_url(), GURL(), SINGLETON_TAB,
206                    PageTransition::LINK);
207   browser->window()->Show();
208   browser->GetSelectedTabContents()->Activate();
209 }
210 
GetBackgroundHostForExtension(const Extension * extension)211 ExtensionHost* ExtensionProcessManager::GetBackgroundHostForExtension(
212     const Extension* extension) {
213   for (ExtensionHostSet::iterator iter = background_hosts_.begin();
214        iter != background_hosts_.end(); ++iter) {
215     ExtensionHost* host = *iter;
216     if (host->extension() == extension)
217       return host;
218   }
219   return NULL;
220 }
221 
RegisterExtensionProcess(const std::string & extension_id,int process_id)222 void ExtensionProcessManager::RegisterExtensionProcess(
223     const std::string& extension_id, int process_id) {
224   // TODO(mpcomplete): This is the only place we actually read process_ids_.
225   // Is it necessary?
226   ProcessIDMap::const_iterator it = process_ids_.find(extension_id);
227   if (it != process_ids_.end() && (*it).second == process_id)
228     return;
229 
230   // Extension ids should get removed from the map before the process ids get
231   // reused from a dead renderer.
232   DCHECK(it == process_ids_.end());
233   process_ids_[extension_id] = process_id;
234 
235   ExtensionService* extension_service =
236       browsing_instance_->profile()->GetExtensionService();
237 
238   std::vector<std::string> page_action_ids;
239   const Extension* extension =
240       extension_service->GetExtensionById(extension_id, false);
241   if (extension->page_action())
242     page_action_ids.push_back(extension->page_action()->id());
243 
244   RenderProcessHost* rph = RenderProcessHost::FromID(process_id);
245   rph->Send(new ExtensionMsg_UpdatePageActions(extension_id, page_action_ids));
246 }
247 
UnregisterExtensionProcess(int process_id)248 void ExtensionProcessManager::UnregisterExtensionProcess(int process_id) {
249   ProcessIDMap::iterator it = process_ids_.begin();
250   while (it != process_ids_.end()) {
251     if (it->second == process_id)
252       process_ids_.erase(it++);
253     else
254       ++it;
255   }
256 }
257 
GetExtensionProcess(const GURL & url)258 RenderProcessHost* ExtensionProcessManager::GetExtensionProcess(
259     const GURL& url) {
260   if (!browsing_instance_->HasSiteInstance(url))
261     return NULL;
262   scoped_refptr<SiteInstance> site(
263       browsing_instance_->GetSiteInstanceForURL(url));
264   if (site->HasProcess())
265     return site->GetProcess();
266   return NULL;
267 }
268 
GetExtensionProcess(const std::string & extension_id)269 RenderProcessHost* ExtensionProcessManager::GetExtensionProcess(
270     const std::string& extension_id) {
271   return GetExtensionProcess(
272       Extension::GetBaseURLFromExtensionId(extension_id));
273 }
274 
GetSiteInstanceForURL(const GURL & url)275 SiteInstance* ExtensionProcessManager::GetSiteInstanceForURL(const GURL& url) {
276   return browsing_instance_->GetSiteInstanceForURL(url);
277 }
278 
HasExtensionHost(ExtensionHost * host) const279 bool ExtensionProcessManager::HasExtensionHost(ExtensionHost* host) const {
280   return all_hosts_.find(host) != all_hosts_.end();
281 }
282 
Observe(NotificationType type,const NotificationSource & source,const NotificationDetails & details)283 void ExtensionProcessManager::Observe(NotificationType type,
284                                       const NotificationSource& source,
285                                       const NotificationDetails& details) {
286   switch (type.value) {
287     case NotificationType::EXTENSIONS_READY: {
288       CreateBackgroundHosts(this,
289           Source<Profile>(source).ptr()->GetExtensionService()->extensions());
290       break;
291     }
292 
293     case NotificationType::EXTENSION_LOADED: {
294       ExtensionService* service =
295           Source<Profile>(source).ptr()->GetExtensionService();
296       if (service->is_ready()) {
297         const Extension* extension = Details<const Extension>(details).ptr();
298         ::CreateBackgroundHost(this, extension);
299       }
300       break;
301     }
302 
303     case NotificationType::EXTENSION_UNLOADED: {
304       const Extension* extension =
305           Details<UnloadedExtensionInfo>(details)->extension;
306       for (ExtensionHostSet::iterator iter = background_hosts_.begin();
307            iter != background_hosts_.end(); ++iter) {
308         ExtensionHost* host = *iter;
309         if (host->extension_id() == extension->id()) {
310           delete host;
311           // |host| should deregister itself from our structures.
312           DCHECK(background_hosts_.find(host) == background_hosts_.end());
313           break;
314         }
315       }
316       break;
317     }
318 
319     case NotificationType::EXTENSION_HOST_DESTROYED: {
320       ExtensionHost* host = Details<ExtensionHost>(details).ptr();
321       all_hosts_.erase(host);
322       background_hosts_.erase(host);
323       break;
324     }
325 
326     case NotificationType::RENDERER_PROCESS_TERMINATED:
327     case NotificationType::RENDERER_PROCESS_CLOSED: {
328       RenderProcessHost* host = Source<RenderProcessHost>(source).ptr();
329       UnregisterExtensionProcess(host->id());
330       break;
331     }
332 
333     case NotificationType::APP_TERMINATING: {
334       // Close background hosts when the last browser is closed so that they
335       // have time to shutdown various objects on different threads. Our
336       // destructor is called too late in the shutdown sequence.
337       CloseBackgroundHosts();
338       break;
339     }
340 
341     default:
342       NOTREACHED();
343   }
344 }
345 
OnExtensionHostCreated(ExtensionHost * host,bool is_background)346 void ExtensionProcessManager::OnExtensionHostCreated(ExtensionHost* host,
347                                                      bool is_background) {
348   DCHECK_EQ(browsing_instance_->profile(), host->profile());
349 
350   all_hosts_.insert(host);
351   if (is_background)
352     background_hosts_.insert(host);
353   NotificationService::current()->Notify(
354       NotificationType::EXTENSION_HOST_CREATED,
355       Source<ExtensionProcessManager>(this),
356       Details<ExtensionHost>(host));
357 }
358 
CloseBackgroundHosts()359 void ExtensionProcessManager::CloseBackgroundHosts() {
360   VLOG_IF(1, g_log_bug53991) << "CloseBackgroundHosts: " << this;
361   for (ExtensionHostSet::iterator iter = background_hosts_.begin();
362        iter != background_hosts_.end(); ) {
363     ExtensionHostSet::iterator current = iter++;
364     delete *current;
365   }
366 }
367 
368 //
369 // IncognitoExtensionProcessManager
370 //
371 
IncognitoExtensionProcessManager(Profile * profile)372 IncognitoExtensionProcessManager::IncognitoExtensionProcessManager(
373     Profile* profile)
374     : ExtensionProcessManager(profile),
375       original_manager_(profile->GetOriginalProfile()->
376                             GetExtensionProcessManager()) {
377   DCHECK(profile->IsOffTheRecord());
378 
379   registrar_.Add(this, NotificationType::BROWSER_WINDOW_READY,
380                  NotificationService::AllSources());
381 }
382 
CreateView(const Extension * extension,const GURL & url,Browser * browser,ViewType::Type view_type)383 ExtensionHost* IncognitoExtensionProcessManager::CreateView(
384     const Extension* extension,
385     const GURL& url,
386     Browser* browser,
387     ViewType::Type view_type) {
388   if (extension->incognito_split_mode()) {
389     if (IsIncognitoEnabled(extension)) {
390       return ExtensionProcessManager::CreateView(extension, url,
391                                                  browser, view_type);
392     } else {
393       NOTREACHED() <<
394           "We shouldn't be trying to create an incognito extension view unless "
395           "it has been enabled for incognito.";
396       return NULL;
397     }
398   } else {
399     return original_manager_->CreateView(extension, url, browser, view_type);
400   }
401 }
402 
CreateBackgroundHost(const Extension * extension,const GURL & url)403 void IncognitoExtensionProcessManager::CreateBackgroundHost(
404     const Extension* extension, const GURL& url) {
405   if (extension->incognito_split_mode()) {
406     if (IsIncognitoEnabled(extension))
407       ExtensionProcessManager::CreateBackgroundHost(extension, url);
408   } else {
409     // Do nothing. If an extension is spanning, then its original-profile
410     // background page is shared with incognito, so we don't create another.
411   }
412 }
413 
GetSiteInstanceForURL(const GURL & url)414 SiteInstance* IncognitoExtensionProcessManager::GetSiteInstanceForURL(
415     const GURL& url) {
416   const Extension* extension = GetExtensionOrAppByURL(url);
417   if (!extension || extension->incognito_split_mode()) {
418     return ExtensionProcessManager::GetSiteInstanceForURL(url);
419   } else {
420     return original_manager_->GetSiteInstanceForURL(url);
421   }
422 }
423 
GetExtensionProcess(const GURL & url)424 RenderProcessHost* IncognitoExtensionProcessManager::GetExtensionProcess(
425     const GURL& url) {
426   const Extension* extension = GetExtensionOrAppByURL(url);
427   if (!extension || extension->incognito_split_mode()) {
428     return ExtensionProcessManager::GetExtensionProcess(url);
429   } else {
430     return original_manager_->GetExtensionProcess(url);
431   }
432 }
433 
GetExtensionOrAppByURL(const GURL & url)434 const Extension* IncognitoExtensionProcessManager::GetExtensionOrAppByURL(
435     const GURL& url) {
436   ExtensionService* service =
437       browsing_instance_->profile()->GetExtensionService();
438   if (!service)
439     return NULL;
440   return (url.SchemeIs(chrome::kExtensionScheme)) ?
441       service->GetExtensionByURL(url) : service->GetExtensionByWebExtent(url);
442 }
443 
IsIncognitoEnabled(const Extension * extension)444 bool IncognitoExtensionProcessManager::IsIncognitoEnabled(
445     const Extension* extension) {
446   ExtensionService* service =
447       browsing_instance_->profile()->GetExtensionService();
448   return service && service->IsIncognitoEnabled(extension->id());
449 }
450 
Observe(NotificationType type,const NotificationSource & source,const NotificationDetails & details)451 void IncognitoExtensionProcessManager::Observe(
452     NotificationType type,
453     const NotificationSource& source,
454     const NotificationDetails& details) {
455   switch (type.value) {
456     case NotificationType::BROWSER_WINDOW_READY: {
457       // We want to spawn our background hosts as soon as the user opens an
458       // incognito window. Watch for new browsers and create the hosts if
459       // it matches our profile.
460       Browser* browser = Source<Browser>(source).ptr();
461       if (browser->profile() == browsing_instance_->profile()) {
462         // On Chrome OS, a login screen is implemented as a browser.
463         // This browser has no extension service.  In this case,
464         // service will be NULL.
465         ExtensionService* service =
466             browsing_instance_->profile()->GetExtensionService();
467         if (service && service->is_ready())
468           CreateBackgroundHosts(this, service->extensions());
469       }
470       break;
471     }
472     default:
473       ExtensionProcessManager::Observe(type, source, details);
474       break;
475   }
476 }
477