Index: extensions/browser/renderer_startup_helper.cc |
diff --git a/extensions/browser/renderer_startup_helper.cc b/extensions/browser/renderer_startup_helper.cc |
index c200c27a2dd843ec4e6fa83d24ef74f875f9c21a..4a309faa66b52703b07cf139612d261f59dc6701 100644 |
--- a/extensions/browser/renderer_startup_helper.cc |
+++ b/extensions/browser/renderer_startup_helper.cc |
@@ -4,6 +4,9 @@ |
#include "extensions/browser/renderer_startup_helper.h" |
+#include "base/debug/dump_without_crashing.h" |
+#include "base/stl_util.h" |
+#include "base/strings/string_util.h" |
#include "base/values.h" |
#include "components/keyed_service/content/browser_context_dependency_manager.h" |
#include "content/public/browser/notification_service.h" |
@@ -31,6 +34,8 @@ RendererStartupHelper::RendererStartupHelper(BrowserContext* browser_context) |
content::NotificationService::AllBrowserContextsAndSources()); |
registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_TERMINATED, |
content::NotificationService::AllBrowserContextsAndSources()); |
+ registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_CLOSED, |
+ content::NotificationService::AllBrowserContextsAndSources()); |
} |
RendererStartupHelper::~RendererStartupHelper() {} |
@@ -45,6 +50,10 @@ void RendererStartupHelper::Observe( |
content::Source<content::RenderProcessHost>(source).ptr()); |
break; |
case content::NOTIFICATION_RENDERER_PROCESS_TERMINATED: |
+ // Fall through. |
+ case content::NOTIFICATION_RENDERER_PROCESS_CLOSED: |
+ // This is needed to take care of the case when a RenderProcessHost is |
+ // reused for a different renderer process. |
UntrackProcess(content::Source<content::RenderProcessHost>(source).ptr()); |
break; |
default: |
@@ -96,6 +105,10 @@ void RendererStartupHelper::InitializeProcess( |
const ExtensionSet& extensions = |
ExtensionRegistry::Get(browser_context_)->enabled_extensions(); |
for (const auto& ext : extensions) { |
+ // OnLoadedExtension should have already been called for the extension. |
+ DCHECK(base::ContainsKey(extension_process_map_, ext->id())); |
+ DCHECK(!base::ContainsKey(extension_process_map_[ext->id()], process)); |
+ |
// Renderers don't need to know about themes. |
if (!ext->is_theme()) { |
// TODO(kalman): Only include tab specific permissions for extension |
@@ -105,18 +118,23 @@ void RendererStartupHelper::InitializeProcess( |
bool include_tab_permissions = true; |
loaded_extensions.push_back( |
ExtensionMsg_Loaded_Params(ext.get(), include_tab_permissions)); |
+ extension_process_map_[ext->id()].insert(process); |
} |
} |
process->Send(new ExtensionMsg_Loaded(loaded_extensions)); |
auto iter = pending_active_extensions_.find(process); |
if (iter != pending_active_extensions_.end()) { |
for (const ExtensionId& id : iter->second) { |
+ // The extension should be loaded in the process. |
DCHECK(extensions.Contains(id)); |
+ DCHECK(base::ContainsKey(extension_process_map_, id)); |
+ DCHECK(base::ContainsKey(extension_process_map_[id], process)); |
process->Send(new ExtensionMsg_ActivateExtension(id)); |
} |
} |
initialized_processes_.insert(process); |
+ pending_active_extensions_.erase(process); |
} |
void RendererStartupHelper::UntrackProcess( |
@@ -128,24 +146,50 @@ void RendererStartupHelper::UntrackProcess( |
initialized_processes_.erase(process); |
pending_active_extensions_.erase(process); |
+ for (auto& extension_process_pair : extension_process_map_) |
+ extension_process_pair.second.erase(process); |
} |
void RendererStartupHelper::ActivateExtensionInProcess( |
const Extension& extension, |
content::RenderProcessHost* process) { |
+ // The extension should have been loaded already. Dump without crashing to |
+ // debug crbug.com/528026. |
+ if (!base::ContainsKey(extension_process_map_, extension.id())) { |
+#if DCHECK_IS_ON() |
+ NOTREACHED() << "Extension " << extension.id() |
+ << "activated before loading"; |
+#else |
+ base::debug::DumpWithoutCrashing(); |
+ return; |
+#endif |
+ } |
+ |
// Renderers don't need to know about themes. We also don't normally |
// "activate" themes, but this could happen if someone tries to open a tab |
// to the e.g. theme's manifest. |
if (extension.is_theme()) |
return; |
- if (initialized_processes_.count(process)) |
+ if (base::ContainsKey(initialized_processes_, process)) { |
+ DCHECK(base::ContainsKey(extension_process_map_[extension.id()], process)); |
process->Send(new ExtensionMsg_ActivateExtension(extension.id())); |
- else |
+ } else { |
pending_active_extensions_[process].insert(extension.id()); |
+ } |
} |
void RendererStartupHelper::OnExtensionLoaded(const Extension& extension) { |
+ // Extension was already loaded. |
+ // TODO(crbug.com/708230): Ensure that clients don't call this for an |
+ // already loaded extension and change this to a DCHECK. |
+ if (base::ContainsKey(extension_process_map_, extension.id())) |
+ return; |
+ |
+ // Mark the extension as loaded. |
+ std::set<content::RenderProcessHost*>& loaded_process_set = |
+ extension_process_map_[extension.id()]; |
+ |
// Renderers don't need to know about themes. |
if (extension.is_theme()) |
return; |
@@ -157,19 +201,31 @@ void RendererStartupHelper::OnExtensionLoaded(const Extension& extension) { |
std::vector<ExtensionMsg_Loaded_Params> params( |
1, |
ExtensionMsg_Loaded_Params(&extension, false /* no tab permissions */)); |
- for (content::RenderProcessHost* process : initialized_processes_) |
+ for (content::RenderProcessHost* process : initialized_processes_) { |
process->Send(new ExtensionMsg_Loaded(params)); |
+ loaded_process_set.insert(process); |
+ } |
} |
void RendererStartupHelper::OnExtensionUnloaded(const Extension& extension) { |
- // Renderers don't need to know about themes. |
- if (extension.is_theme()) |
+ // Extension is not loaded. |
+ // TODO(crbug.com/708230): Ensure that clients call this for a loaded |
+ // extension only and change this to a DCHECK. |
+ if (!base::ContainsKey(extension_process_map_, extension.id())) |
return; |
- for (content::RenderProcessHost* process : initialized_processes_) |
+ const std::set<content::RenderProcessHost*>& loaded_process_set = |
+ extension_process_map_[extension.id()]; |
+ for (content::RenderProcessHost* process : loaded_process_set) { |
+ DCHECK(base::ContainsKey(initialized_processes_, process)); |
process->Send(new ExtensionMsg_Unloaded(extension.id())); |
+ } |
+ |
for (auto& process_extensions_pair : pending_active_extensions_) |
process_extensions_pair.second.erase(extension.id()); |
+ |
+ // Mark the extension as unloaded. |
+ extension_process_map_.erase(extension.id()); |
} |
////////////////////////////////////////////////////////////////////////////// |