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