blob: 9d4c74b7996e2e573079413cd24a99f86dd5b90f [file] [log] [blame] [edit]
// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef EXTENSIONS_RENDERER_DISPATCHER_H_
#define EXTENSIONS_RENDERER_DISPATCHER_H_
#include <stdint.h>
#include <map>
#include <memory>
#include <set>
#include <string>
#include <utility>
#include <vector>
#include "base/gtest_prod_util.h"
#include "base/memory/raw_ptr.h"
#include "base/scoped_observation.h"
#include "base/timer/timer.h"
#include "components/guest_view/buildflags/buildflags.h"
#include "components/version_info/channel.h"
#include "content/public/renderer/render_thread_observer.h"
#include "extensions/common/event_filter.h"
#include "extensions/common/extension_id.h"
#include "extensions/common/extensions_client.h"
#include "extensions/common/features/feature.h"
#include "extensions/common/mojom/event_dispatcher.mojom.h"
#include "extensions/common/mojom/feature_session_type.mojom.h"
#include "extensions/common/mojom/frame.mojom.h"
#include "extensions/common/mojom/host_id.mojom-forward.h"
#include "extensions/common/mojom/renderer.mojom.h"
#include "extensions/renderer/native_extension_bindings_system.h"
#include "extensions/renderer/resource_bundle_source_map.h"
#include "extensions/renderer/script_context.h"
#include "extensions/renderer/script_context_set.h"
#include "extensions/renderer/user_script_set_manager.h"
#include "extensions/renderer/v8_schema_registry.h"
#include "mojo/public/cpp/bindings/associated_receiver.h"
#include "third_party/blink/public/common/tokens/tokens.h"
#include "third_party/blink/public/platform/web_string.h"
#include "v8/include/v8-forward.h"
class ChromeRenderViewTest;
class GURL;
namespace blink {
class WebLocalFrame;
class WebServiceWorkerContextProxy;
}
namespace base {
class SingleThreadTaskRunner;
}
namespace content {
class RenderThread;
} // namespace content
namespace extensions {
// Constant to define the default profile id for the renderer to 0.
// Since each renderer is associated with a single context, we don't need
// separate ids for the profile.
const int kRendererProfileId = 0;
class ContentWatcher;
class Extension;
class ExtensionsRendererAPIProvider;
class ModuleSystem;
class IPCMessageSender;
class ScriptContext;
class ScriptContextSetIterable;
class ScriptInjectionManager;
class WorkerScriptContextSet;
// Dispatches extension control messages sent to the renderer and stores
// renderer extension related state.
class Dispatcher : public content::RenderThreadObserver,
public UserScriptSetManager::Observer,
public mojom::Renderer,
public mojom::EventDispatcher,
public NativeExtensionBindingsSystem::Delegate {
public:
explicit Dispatcher(
std::vector<std::unique_ptr<const ExtensionsRendererAPIProvider>>
api_providers);
Dispatcher(const Dispatcher&) = delete;
Dispatcher& operator=(const Dispatcher&) = delete;
~Dispatcher() override;
// Returns Service Worker ScriptContexts belonging to current worker thread.
static WorkerScriptContextSet* GetWorkerScriptContextSet();
// Returns true if web socket activity for the service worker associated with
// the given `v8_context` should count as service worker activity, prolonging
// the service worker's lifetime.
// Called on the service worker thread.
static bool ShouldNotifyServiceWorkerOnWebSocketActivity(
v8::Local<v8::Context> v8_context);
const ScriptContextSet& script_context_set() const {
return *script_context_set_;
}
// Returns iterator to iterate over all main thread ScriptContexts.
ScriptContextSetIterable* script_context_set_iterator() {
return script_context_set_.get();
}
V8SchemaRegistry* v8_schema_registry() { return v8_schema_registry_.get(); }
const std::optional<std::string>& webview_partition_id() {
return webview_partition_id_;
}
bool activity_logging_enabled() const { return activity_logging_enabled_; }
void OnRenderThreadStarted(content::RenderThread* render_thread);
void OnRenderFrameCreated(content::RenderFrame* render_frame);
bool IsExtensionActive(const ExtensionId& extension_id) const;
void DidCreateScriptContext(blink::WebLocalFrame* frame,
const v8::Local<v8::Context>& context,
int32_t world_id);
// This is called when a service worker is ready to evaluate the toplevel
// script. This method suspends the service worker if:
// * the service worker is background of a service worker based extension,
// and
// * the extension isn't loaded yet.
// Suspending background service worker is required because we need to
// install extension API bindings before executing the service worker.
// TODO(crbug.com/40645846): Figure out better way to coalesce them.
//
// Runs on the service worker thread and should only use thread-safe member
// variables.
void DidInitializeServiceWorkerContextOnWorkerThread(
blink::WebServiceWorkerContextProxy* context_proxy,
const GURL& service_worker_scope,
const GURL& script_url);
// This is called immediately before a service worker evaluates the
// toplevel script. This method installs extension API bindings.
//
// Runs on a different thread and should only use thread-safe member
// variables.
void WillEvaluateServiceWorkerOnWorkerThread(
blink::WebServiceWorkerContextProxy* context_proxy,
v8::Local<v8::Context> v8_context,
int64_t service_worker_version_id,
const GURL& service_worker_scope,
const GURL& script_url,
const blink::ServiceWorkerToken& service_worker_token);
void WillReleaseScriptContext(blink::WebLocalFrame* frame,
const v8::Local<v8::Context>& context,
int32_t world_id);
// Runs on worker thread and should not use any member variables.
void DidStartServiceWorkerContextOnWorkerThread(
int64_t service_worker_version_id,
const GURL& service_worker_scope,
const GURL& script_url);
// Runs on a different thread and should not use any member variables.
void WillDestroyServiceWorkerContextOnWorkerThread(
v8::Local<v8::Context> v8_context,
int64_t service_worker_version_id,
const GURL& service_worker_scope,
const GURL& script_url);
// This method is not allowed to run JavaScript code in the frame.
void DidCreateDocumentElement(blink::WebLocalFrame* frame);
// These methods may run (untrusted) JavaScript code in the frame, and
// cause `render_frame` to become invalid.
void RunScriptsAtDocumentStart(content::RenderFrame* render_frame);
void RunScriptsAtDocumentEnd(content::RenderFrame* render_frame);
void RunScriptsAtDocumentIdle(content::RenderFrame* render_frame);
// Dispatches the event named `event_name` to all render views.
void DispatchEventHelper(const mojom::HostID& extension_id,
const std::string& event_name,
const base::Value::List& event_args,
mojom::EventFilteringInfoPtr filtering_info) const;
// Shared implementation of the various MessageInvoke IPCs.
void InvokeModuleSystemMethod(content::RenderFrame* render_frame,
const ExtensionId& extension_id,
const std::string& module_name,
const std::string& function_name,
const base::Value::List& args);
void ExecuteDeclarativeScript(content::RenderFrame* render_frame,
int tab_id,
const ExtensionId& extension_id,
const std::string& script_id,
const GURL& url);
// Executes the code described in `param` and calls `callback` if it's done.
void ExecuteCode(mojom::ExecuteCodeParamsPtr param,
mojom::LocalFrame::ExecuteCodeCallback callback,
content::RenderFrame* render_frame);
NativeExtensionBindingsSystem* bindings_system() {
return bindings_system_.get();
}
private:
// The RendererPermissionsPolicyDelegateTest.CannotScriptWebstore test needs
// to call the ActivateExtension IPCs.
friend class ::ChromeRenderViewTest;
FRIEND_TEST_ALL_PREFIXES(RendererPermissionsPolicyDelegateTest,
CannotScriptWebstore);
// RenderThreadObserver implementation:
void RegisterMojoInterfaces(
blink::AssociatedInterfaceRegistry* associated_interfaces) override;
void UnregisterMojoInterfaces(
blink::AssociatedInterfaceRegistry* associated_interfaces) override;
// mojom::Renderer implementation:
void ActivateExtension(const ExtensionId& extension_id) override;
void SetActivityLoggingEnabled(bool enabled) override;
void LoadExtensions(
std::vector<mojom::ExtensionLoadedParamsPtr> loaded_extensions) override;
void UnloadExtension(const ExtensionId& extension_id) override;
void SuspendExtension(
const ExtensionId& extension_id,
mojom::Renderer::SuspendExtensionCallback callback) override;
void CancelSuspendExtension(const ExtensionId& extension_id) override;
void SetDeveloperMode(bool current_developer_mode) override;
void SetUserScriptsAllowed(const ExtensionId& extension_id,
bool allowed) override;
void SetSessionInfo(version_info::Channel channel,
mojom::FeatureSessionType session_type) override;
void SetSystemFont(const std::string& font_family,
const std::string& font_size) override;
void SetWebViewPartitionID(const std::string& partition_id) override;
void SetScriptingAllowlist(
const std::vector<ExtensionId>& extension_ids) override;
void UpdateUserScriptWorlds(
std::vector<mojom::UserScriptWorldInfoPtr> infos) override;
void ClearUserScriptWorldConfig(
const ExtensionId& extension_id,
const std::optional<std::string>& world_id) override;
void ShouldSuspend(ShouldSuspendCallback callback) override;
void TransferBlobs(TransferBlobsCallback callback) override;
void UpdatePermissions(const ExtensionId& extension_id,
PermissionSet active_permissions,
PermissionSet withheld_permissions,
URLPatternSet policy_blocked_hosts,
URLPatternSet policy_allowed_hosts,
bool uses_default_policy_host_restrictions) override;
void UpdateDefaultPolicyHostRestrictions(
URLPatternSet default_policy_blocked_hosts,
URLPatternSet default_policy_allowed_hosts) override;
void UpdateUserHostRestrictions(URLPatternSet user_blocked_hosts,
URLPatternSet user_allowed_hosts) override;
void UpdateTabSpecificPermissions(const ExtensionId& extension_id,
URLPatternSet new_hosts,
int tab_id,
bool update_origin_allowlist) override;
void UpdateUserScripts(base::ReadOnlySharedMemoryRegion shared_memory,
mojom::HostIDPtr host_id) override;
void ClearTabSpecificPermissions(
const std::vector<ExtensionId>& extension_ids,
int tab_id,
bool update_origin_allowlist) override;
void WatchPages(const std::vector<std::string>& css_selectors) override;
void OnRendererAssociatedRequest(
mojo::PendingAssociatedReceiver<mojom::Renderer> receiver);
void OnEventDispatcherRequest(
mojo::PendingAssociatedReceiver<mojom::EventDispatcher> receiver);
// mojom::EventDispatcher implementation.
void DispatchEvent(mojom::DispatchEventParamsPtr params,
base::Value::List event_args,
DispatchEventCallback callback) override;
// UserScriptSetManager::Observer implementation.
void OnUserScriptsUpdated(const mojom::HostID& changed_host) override;
// NativeExtensionBindingsSystem::Delegate implementation.
ScriptContextSetIterable* GetScriptContextSet() override;
void UpdateActiveExtensions();
// Sets up the host permissions for `extension`.
void InitOriginPermissions(const Extension* extension);
// Updates the host permissions for the extension url to include only those
// the extension currently has, removing any old entries.
void UpdateOriginPermissions(const Extension& extension);
// Enable custom element allowlist in Apps.
void EnableCustomElementAllowlist();
// Adds or removes bindings for all contexts. `api_permissions_changed`
// indicates whether the effective permission state for extensions has
// changed and cached features should be re-calculated.
void UpdateAllBindings(bool api_permissions_changed);
// Adds or removes bindings for every context belonging to `extension`, due to
// permissions change in the extension.
void UpdateBindingsForExtension(const Extension& extension);
void RegisterNativeHandlers(ModuleSystem* module_system,
ScriptContext* context,
NativeExtensionBindingsSystem* bindings_system,
V8SchemaRegistry* v8_schema_registry);
// Inserts static source code into `source_map_`.
void PopulateSourceMap();
// Returns whether the current renderer hosts a platform app.
bool IsWithinPlatformApp();
#if BUILDFLAG(ENABLE_GUEST_VIEW)
// Requires the GuestView modules in the module system of the ScriptContext
// `context`.
void RequireGuestViewModules(ScriptContext* context);
#endif
// Creates the NativeExtensionBindingsSystem. Note: this may be called on any
// thread, and thus cannot mutate any state or rely on state which can be
// mutated in Dispatcher.
std::unique_ptr<NativeExtensionBindingsSystem> CreateBindingsSystem(
NativeExtensionBindingsSystem::Delegate* delegate,
std::unique_ptr<IPCMessageSender> ipc_sender);
void ResumeEvaluationOnWorkerThread(const ExtensionId& extension_id);
// The list of embedder API providers.
// This list is accessed on multiple threads, since these API providers are
// used in the initialization of script contexts (which can be both main-
// thread contexts and worker-thread contexts).
// This is safe, since this list is established on Dispatcher construction
// (which happens before any access on worker threads), the Dispatcher should
// not be destroyed, and this list is immutable. This is enforced by the
// `const`s below.
const std::vector<std::unique_ptr<const ExtensionsRendererAPIProvider>>
api_providers_;
// The IDs of extensions that failed to load, mapped to the error message
// generated on failure.
std::map<ExtensionId, std::string> extension_load_errors_;
// ExtensionIds for extensions that were loaded, but then unloaded later.
// Used for metrics purposes.
std::set<ExtensionId> unloaded_extensions_;
// All the bindings contexts that are currently loaded for this renderer.
// There is zero or one for each v8 context.
std::unique_ptr<ScriptContextSet> script_context_set_;
std::unique_ptr<ContentWatcher> content_watcher_;
std::unique_ptr<UserScriptSetManager> user_script_set_manager_;
std::unique_ptr<ScriptInjectionManager> script_injection_manager_;
// The extensions and apps that are active in this process.
ExtensionIdSet active_extension_ids_;
ResourceBundleSourceMap source_map_;
// Cache for the v8 representation of extension API schemas.
std::unique_ptr<V8SchemaRegistry> v8_schema_registry_;
// The bindings system associated with the main thread.
std::unique_ptr<NativeExtensionBindingsSystem> bindings_system_;
// The platforms system font family and size;
std::string system_font_family_;
std::string system_font_size_;
// It is important for this to come after the ScriptInjectionManager, so that
// the observer is destroyed before the UserScriptSet.
base::ScopedObservation<UserScriptSetManager, UserScriptSetManager::Observer>
user_script_set_manager_observation_{this};
// Whether or not extension activity is enabled.
bool activity_logging_enabled_;
// The WebView partition ID associated with this process's storage partition,
// if this renderer is a WebView guest render process, otherwise unset.
// Note that this may be an empty string, even if it's set (if the webview
// doesn't have a set partition ID).
std::optional<std::string> webview_partition_id_;
// Extensions renderer receiver. This is an associated receiver because
// it is dependent on other messages sent on other associated channels.
mojo::AssociatedReceiver<mojom::Renderer> receiver_;
// Extensions mojom::EventDispatcher receiver. This is an associated receiver
// because it is dependent on other messages sent on other associated
// channels.
mojo::AssociatedReceiver<mojom::EventDispatcher> dispatcher_;
// Used to hold a service worker information which is ready to execute but the
// onloaded message haven't been received yet. We need to defer service worker
// execution until the ExtensionMsg_Loaded message is received because we can
// install extension bindings only after the onload message is received.
// TODO(bashi): Consider to have a separate class to put this logic?
struct PendingServiceWorker {
scoped_refptr<base::SingleThreadTaskRunner> task_runner;
raw_ptr<blink::WebServiceWorkerContextProxy> context_proxy;
PendingServiceWorker(blink::WebServiceWorkerContextProxy* context_proxy);
~PendingServiceWorker();
};
// This will be accessed both from the main thread and worker threads.
std::map<ExtensionId, std::unique_ptr<PendingServiceWorker>>
service_workers_paused_for_on_loaded_message_;
base::Lock service_workers_paused_for_on_loaded_message_lock_;
};
} // namespace extensions
#endif // EXTENSIONS_RENDERER_DISPATCHER_H_