blob: 4a1230fc68e7495917aabaa1b66ffbae985d06a9 [file] [log] [blame]
// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "extensions/browser/extension_host_registry.h"
#include "base/check.h"
#include "base/containers/contains.h"
#include "base/no_destructor.h"
#include "base/observer_list.h"
#include "components/keyed_service/content/browser_context_dependency_manager.h"
#include "components/keyed_service/content/browser_context_keyed_service_factory.h"
#include "content/public/browser/render_frame_host.h"
#include "extensions/browser/extension_host.h"
#include "extensions/browser/extensions_browser_client.h"
namespace extensions {
namespace {
class ExtensionHostRegistryFactory : public BrowserContextKeyedServiceFactory {
public:
ExtensionHostRegistryFactory();
ExtensionHostRegistryFactory(const ExtensionHostRegistryFactory&) = delete;
ExtensionHostRegistryFactory& operator=(const ExtensionHostRegistryFactory&) =
delete;
~ExtensionHostRegistryFactory() override = default;
ExtensionHostRegistry* GetForBrowserContext(content::BrowserContext* context);
private:
// BrowserContextKeyedServiceFactory:
content::BrowserContext* GetBrowserContextToUse(
content::BrowserContext* context) const override;
KeyedService* BuildServiceInstanceFor(
content::BrowserContext* context) const override;
};
ExtensionHostRegistryFactory::ExtensionHostRegistryFactory()
: BrowserContextKeyedServiceFactory(
"ExtensionHostRegistry",
BrowserContextDependencyManager::GetInstance()) {}
ExtensionHostRegistry* ExtensionHostRegistryFactory::GetForBrowserContext(
content::BrowserContext* browser_context) {
return static_cast<ExtensionHostRegistry*>(
GetServiceForBrowserContext(browser_context, /*create=*/true));
}
content::BrowserContext* ExtensionHostRegistryFactory::GetBrowserContextToUse(
content::BrowserContext* context) const {
// This seems like a service that should have its own instance in incognito
// in order to better ensure there isn't any bleed-over from off-the-record
// contexts. Unfortunately, other systems (I'm looking at you,
// LazyBackgroundTaskQueue!) rely on this, and are set up to be redirect to
// the original context. This makes it quite challenging to let this have its
// own incognito context.
return ExtensionsBrowserClient::Get()->GetOriginalContext(context);
}
KeyedService* ExtensionHostRegistryFactory::BuildServiceInstanceFor(
content::BrowserContext* context) const {
return new ExtensionHostRegistry();
}
} // namespace
ExtensionHostRegistry::ExtensionHostRegistry() = default;
ExtensionHostRegistry::~ExtensionHostRegistry() = default;
// static
ExtensionHostRegistry* ExtensionHostRegistry::Get(
content::BrowserContext* browser_context) {
return static_cast<ExtensionHostRegistryFactory*>(GetFactory())
->GetForBrowserContext(browser_context);
}
// static
BrowserContextKeyedServiceFactory* ExtensionHostRegistry::GetFactory() {
static base::NoDestructor<ExtensionHostRegistryFactory> g_factory;
return g_factory.get();
}
void ExtensionHostRegistry::ExtensionHostCreated(
ExtensionHost* extension_host) {
DCHECK(!base::Contains(extension_hosts_, extension_host));
extension_hosts_.insert(extension_host);
// Note: There's not currently any observer method corresponding to host
// creation, because most systems and listeners care about the host being
// at a certain state of readiness. This is just to start properly
// tracking the host.
}
void ExtensionHostRegistry::ExtensionHostRenderProcessReady(
ExtensionHost* extension_host) {
DCHECK(base::Contains(extension_hosts_, extension_host));
for (Observer& observer : observers_) {
observer.OnExtensionHostRenderProcessReady(
extension_host->browser_context(), extension_host);
}
}
void ExtensionHostRegistry::ExtensionHostCompletedFirstLoad(
ExtensionHost* extension_host) {
DCHECK(base::Contains(extension_hosts_, extension_host));
// TODO(devlin): This can unexpectedly fire when a renderer process is
// terminating. When a renderer process is terminated, it causes the
// RenderFrameHostImpl to reset its loading state, which calls
// DidStopLoading() if it was loading. Then, if the first load never
// happened, ExtensionHost will fire the DidCompleteFirstLoad() notification.
//
// This is probably a behavioral bug. We should have ExtensionHost check
// whether the renderer is still around or whether the load succeeded before
// notifying observers, or at least indicate the success in the notification.
for (Observer& observer : observers_) {
observer.OnExtensionHostCompletedFirstLoad(
extension_host->browser_context(), extension_host);
}
}
void ExtensionHostRegistry::ExtensionHostDocumentElementAvailable(
ExtensionHost* extension_host) {
DCHECK(base::Contains(extension_hosts_, extension_host));
for (Observer& observer : observers_) {
observer.OnExtensionHostDocumentElementAvailable(
extension_host->browser_context(), extension_host);
}
}
void ExtensionHostRegistry::ExtensionHostRenderProcessGone(
ExtensionHost* extension_host) {
DCHECK(base::Contains(extension_hosts_, extension_host));
for (Observer& observer : observers_) {
observer.OnExtensionHostRenderProcessGone(extension_host->browser_context(),
extension_host);
}
}
void ExtensionHostRegistry::ExtensionHostDestroyed(
ExtensionHost* extension_host) {
DCHECK(base::Contains(extension_hosts_, extension_host));
extension_hosts_.erase(extension_host);
for (Observer& observer : observers_) {
observer.OnExtensionHostDestroyed(extension_host->browser_context(),
extension_host);
}
}
std::vector<ExtensionHost*> ExtensionHostRegistry::GetHostsForExtension(
const ExtensionId& extension_id) {
std::vector<ExtensionHost*> hosts;
for (ExtensionHost* host : extension_hosts_) {
if (host->extension_id() == extension_id)
hosts.push_back(host);
}
return hosts;
}
ExtensionHost* ExtensionHostRegistry::GetExtensionHostForPrimaryMainFrame(
content::RenderFrameHost* render_frame_host) {
DCHECK(render_frame_host->IsInPrimaryMainFrame())
<< "GetExtensionHostForPrimaryMainFrame() should only be called with "
<< "the primary main frame.";
for (ExtensionHost* host : extension_hosts_) {
if (host->main_frame_host() == render_frame_host)
return host;
}
return nullptr;
}
void ExtensionHostRegistry::AddObserver(Observer* observer) {
observers_.AddObserver(observer);
}
void ExtensionHostRegistry::RemoveObserver(Observer* observer) {
observers_.RemoveObserver(observer);
}
} // namespace extensions