|  | // 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_ |