| // 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. | 
 |  | 
 | #include "extensions/renderer/dispatcher.h" | 
 |  | 
 | #include <stddef.h> | 
 |  | 
 | #include <algorithm> | 
 | #include <memory> | 
 | #include <string_view> | 
 | #include <utility> | 
 | #include <vector> | 
 |  | 
 | #include "base/command_line.h" | 
 | #include "base/containers/contains.h" | 
 | #include "base/debug/alias.h" | 
 | #include "base/debug/crash_logging.h" | 
 | #include "base/debug/dump_without_crashing.h" | 
 | #include "base/feature_list.h" | 
 | #include "base/functional/bind.h" | 
 | #include "base/functional/callback.h" | 
 | #include "base/functional/callback_helpers.h" | 
 | #include "base/lazy_instance.h" | 
 | #include "base/memory/ptr_util.h" | 
 | #include "base/metrics/histogram_functions.h" | 
 | #include "base/metrics/histogram_macros.h" | 
 | #include "base/strings/span_printf.h" | 
 | #include "base/strings/string_number_conversions.h" | 
 | #include "base/strings/string_util.h" | 
 | #include "base/strings/utf_string_conversions.h" | 
 | #include "base/task/single_thread_task_runner.h" | 
 | #include "base/time/time.h" | 
 | #include "base/values.h" | 
 | #include "build/build_config.h" | 
 | #include "build/chromeos_buildflags.h" | 
 | #include "components/guest_view/buildflags/buildflags.h" | 
 | #include "content/public/common/content_features.h" | 
 | #include "content/public/common/content_switches.h" | 
 | #include "content/public/renderer/render_frame.h" | 
 | #include "content/public/renderer/render_thread.h" | 
 | #include "content/public/renderer/v8_value_converter.h" | 
 | #include "extensions/common/api/incognito.h" | 
 | #include "extensions/common/api/messaging/message.h" | 
 | #include "extensions/common/constants.h" | 
 | #include "extensions/common/cors_util.h" | 
 | #include "extensions/common/crash_keys.h" | 
 | #include "extensions/common/extension.h" | 
 | #include "extensions/common/extension_api.h" | 
 | #include "extensions/common/extension_features.h" | 
 | #include "extensions/common/extension_id.h" | 
 | #include "extensions/common/extension_urls.h" | 
 | #include "extensions/common/extensions_client.h" | 
 | #include "extensions/common/features/behavior_feature.h" | 
 | #include "extensions/common/features/feature.h" | 
 | #include "extensions/common/features/feature_channel.h" | 
 | #include "extensions/common/features/feature_developer_mode_only.h" | 
 | #include "extensions/common/features/feature_provider.h" | 
 | #include "extensions/common/features/feature_session_type.h" | 
 | #include "extensions/common/manifest_constants.h" | 
 | #include "extensions/common/manifest_handlers/background_info.h" | 
 | #include "extensions/common/manifest_handlers/incognito_info.h" | 
 | #include "extensions/common/manifest_handlers/options_page_info.h" | 
 | #include "extensions/common/mojom/context_type.mojom.h" | 
 | #include "extensions/common/mojom/host_id.mojom.h" | 
 | #include "extensions/common/permissions/permission_set.h" | 
 | #include "extensions/common/permissions/permissions_data.h" | 
 | #include "extensions/common/switches.h" | 
 | #include "extensions/common/user_scripts_allowed_state.h" | 
 | #include "extensions/common/utils/extension_utils.h" | 
 | #include "extensions/grit/extensions_renderer_resources.h" | 
 | #include "extensions/renderer/api/messaging/native_renderer_messaging_service.h" | 
 | #include "extensions/renderer/content_watcher.h" | 
 | #include "extensions/renderer/dom_activity_logger.h" | 
 | #include "extensions/renderer/extension_frame_helper.h" | 
 | #include "extensions/renderer/extension_interaction_provider.h" | 
 | #include "extensions/renderer/extensions_renderer_api_provider.h" | 
 | #include "extensions/renderer/extensions_renderer_client.h" | 
 | #include "extensions/renderer/ipc_message_sender.h" | 
 | #include "extensions/renderer/isolated_world_manager.h" | 
 | #include "extensions/renderer/module_system.h" | 
 | #include "extensions/renderer/native_extension_bindings_system.h" | 
 | #include "extensions/renderer/renderer_extension_registry.h" | 
 | #include "extensions/renderer/safe_builtins.h" | 
 | #include "extensions/renderer/script_context.h" | 
 | #include "extensions/renderer/script_context_set.h" | 
 | #include "extensions/renderer/script_injection_manager.h" | 
 | #include "extensions/renderer/service_worker_data.h" | 
 | #include "extensions/renderer/shared_l10n_map.h" | 
 | #include "extensions/renderer/static_v8_external_one_byte_string_resource.h" | 
 | #include "extensions/renderer/trace_util.h" | 
 | #include "extensions/renderer/v8_helpers.h" | 
 | #include "extensions/renderer/worker_script_context_set.h" | 
 | #include "extensions/renderer/worker_thread_dispatcher.h" | 
 | #include "extensions/renderer/worker_thread_util.h" | 
 | #include "gin/converter.h" | 
 | #include "services/network/public/mojom/cors.mojom.h" | 
 | #include "third_party/blink/public/common/associated_interfaces/associated_interface_registry.h" | 
 | #include "third_party/blink/public/common/tokens/tokens.h" | 
 | #include "third_party/blink/public/mojom/frame/user_activation_notification_type.mojom.h" | 
 | #include "third_party/blink/public/platform/web_string.h" | 
 | #include "third_party/blink/public/platform/web_url_request.h" | 
 | #include "third_party/blink/public/web/modules/service_worker/web_service_worker_context_proxy.h" | 
 | #include "third_party/blink/public/web/web_custom_element.h" | 
 | #include "third_party/blink/public/web/web_document.h" | 
 | #include "third_party/blink/public/web/web_frame.h" | 
 | #include "third_party/blink/public/web/web_local_frame.h" | 
 | #include "third_party/blink/public/web/web_script_controller.h" | 
 | #include "third_party/blink/public/web/web_security_policy.h" | 
 | #include "third_party/blink/public/web/web_settings.h" | 
 | #include "third_party/blink/public/web/web_v8_features.h" | 
 | #include "third_party/blink/public/web/web_view.h" | 
 | #include "ui/base/resource/resource_bundle.h" | 
 | #include "v8/include/v8-context.h" | 
 | #include "v8/include/v8-function.h" | 
 | #include "v8/include/v8-object.h" | 
 | #include "v8/include/v8-primitive.h" | 
 |  | 
 | using blink::WebDocument; | 
 | using blink::WebSecurityPolicy; | 
 | using blink::WebString; | 
 | using blink::WebView; | 
 | using content::RenderThread; | 
 |  | 
 | namespace extensions { | 
 |  | 
 | namespace { | 
 |  | 
 | // A feature flag for the crash issue in crbug.com/389971360. | 
 | BASE_FEATURE(kSpeculativeFixForServiceWorkerDataInDidStartServiceWorkerContext, | 
 |              base::FEATURE_ENABLED_BY_DEFAULT); | 
 | // A feature flag for the crash issue in crbug.com/424476776. | 
 | BASE_FEATURE(kSpeculativeFixForNoExtensionInDidStartServiceWorkerContext, | 
 |              base::FEATURE_ENABLED_BY_DEFAULT); | 
 |  | 
 | static const char kOnSuspendEvent[] = "runtime.onSuspend"; | 
 | static const char kOnSuspendCanceledEvent[] = "runtime.onSuspendCanceled"; | 
 |  | 
 | // TODO(crbug.com/389971360, crbug.com/424476776) Remove this enum class and | 
 | // `service_worker_context_state` once the issue is fixed. This is completely | 
 | // for debugging purpose in crbug.com/389971360. If crbug.com/424476776 relies | 
 | // on this enum, see if it can be simplified if the fix is proven. | 
 | enum class ServiceWorkerContextState { | 
 |   kDefault = 0, | 
 |   kInitializing = 1, | 
 |   kInitialized = 2, | 
 |   kScriptUrlIsNotExtensionScheme = 3, | 
 |   kNoExtension = 4, | 
 |   kExtensionAPIIsNotEnabledForServiceWorkerScript = 5, | 
 |   kDestroying = 6, | 
 |   kDestroyed = 7, | 
 |   kUnloadedExtension = 8, | 
 |   kDestroyedWithoutWorkerData = 9, | 
 |   kMaxValue = kDestroyedWithoutWorkerData, | 
 | }; | 
 |  | 
 | constinit thread_local ServiceWorkerContextState service_worker_context_state = | 
 |     extensions::ServiceWorkerContextState::kDefault; | 
 |  | 
 | enum class ExtensionRendererLoadStatus { | 
 |   // Extension is neither loaded in the registry nor unloaded. | 
 |   kUnknownExtension = 0, | 
 |   // Extension is loaded in the registry. | 
 |   kExtensionLoaded = 1, | 
 |   // Extension was loaded in the registry, but was unloaded. | 
 |   kExtensionUnloaded = 2, | 
 |   kMaxValue = kExtensionUnloaded, | 
 | }; | 
 |  | 
 | // Returns whether or not extension APIs are allowed for the specified | 
 | // `script_url` and `scope`. The script must be specified in the extension's | 
 | // manifest background section and the scope must be the root scope of the | 
 | // extension. | 
 | bool ExtensionAPIEnabledForServiceWorkerScript(const GURL& scope, | 
 |                                                const GURL& script_url) { | 
 |   if (!script_url.SchemeIs(kExtensionScheme)) { | 
 |     return false; | 
 |   } | 
 |  | 
 |   // This code runs on the service worker thread, so it needs to use the | 
 |   // thread-safe refcounted version of GetExtensionOrAppByURL. | 
 |   scoped_refptr<const Extension> extension = | 
 |       RendererExtensionRegistry::Get()->GetRefCountedExtensionOrAppByURL( | 
 |           script_url); | 
 |  | 
 |   if (!extension || !BackgroundInfo::IsServiceWorkerBased(extension.get())) { | 
 |     return false; | 
 |   } | 
 |  | 
 |   if (scope != extension->url()) { | 
 |     return false; | 
 |   } | 
 |  | 
 |   return BackgroundInfo::GetBackgroundServiceWorkerScriptURL(extension.get()) == | 
 |          script_url; | 
 | } | 
 |  | 
 | // Calls a method |method_name| in a module |module_name| belonging to the | 
 | // module system from |context|. Intended as a callback target from | 
 | // ScriptContextSet::ForEach. | 
 | void CallModuleMethod(const std::string& module_name, | 
 |                       const std::string& method_name, | 
 |                       const base::Value::List* args, | 
 |                       ScriptContext* context) { | 
 |   v8::HandleScope handle_scope(context->isolate()); | 
 |   v8::Context::Scope context_scope(context->v8_context()); | 
 |  | 
 |   std::unique_ptr<content::V8ValueConverter> converter = | 
 |       content::V8ValueConverter::Create(); | 
 |  | 
 |   v8::LocalVector<v8::Value> arguments(context->isolate()); | 
 |   for (const auto& arg : *args) { | 
 |     arguments.push_back(converter->ToV8Value(arg, context->v8_context())); | 
 |   } | 
 |  | 
 |   context->module_system()->CallModuleMethodSafe( | 
 |       module_name, method_name, &arguments); | 
 | } | 
 |  | 
 | class HandleScopeHelper { | 
 |  public: | 
 |   HandleScopeHelper(ScriptContext* script_context) | 
 |       : handle_scope_(script_context->isolate()), | 
 |         context_scope_(script_context->v8_context()) {} | 
 |  | 
 |   HandleScopeHelper(const HandleScopeHelper&) = delete; | 
 |   HandleScopeHelper& operator=(const HandleScopeHelper&) = delete; | 
 |  | 
 |  private: | 
 |   v8::HandleScope handle_scope_; | 
 |   v8::Context::Scope context_scope_; | 
 | }; | 
 |  | 
 | base::LazyInstance<WorkerScriptContextSet>::DestructorAtExit | 
 |     g_worker_script_context_set = LAZY_INSTANCE_INITIALIZER; | 
 |  | 
 | // Creates a new extension from the data in the mojom::ExtensionLoadedParams | 
 | // object. A context_id needs to be passed because each browser context can have | 
 | // different values for default_policy_blocked/allowed_hosts. | 
 | // (see extension_util.cc#GetBrowserContextId) | 
 | scoped_refptr<Extension> ConvertToExtension( | 
 |     mojom::ExtensionLoadedParamsPtr params, | 
 |     int context_id, | 
 |     std::u16string* error) { | 
 |   // We pass in the |id| to the create call because it will save work in the | 
 |   // normal case, and because in tests, extensions may not have paths or keys, | 
 |   // but it's important to retain the same id. | 
 |   scoped_refptr<Extension> extension = | 
 |       Extension::Create(params->path, params->location, params->manifest, | 
 |                         params->creation_flags, params->id, error); | 
 |  | 
 |   if (!extension.get()) | 
 |     return extension; | 
 |  | 
 |   const PermissionsData* permissions_data = extension->permissions_data(); | 
 |   permissions_data->SetPermissions( | 
 |       std::make_unique<const PermissionSet>( | 
 |           std::move(params->active_permissions)), | 
 |       std::make_unique<const PermissionSet>( | 
 |           std::move(params->withheld_permissions))); | 
 |   permissions_data->SetContextId(context_id); | 
 |  | 
 |   if (params->uses_default_policy_blocked_allowed_hosts) { | 
 |     permissions_data->SetUsesDefaultHostRestrictions(); | 
 |   } else { | 
 |     permissions_data->SetPolicyHostRestrictions(params->policy_blocked_hosts, | 
 |                                                 params->policy_allowed_hosts); | 
 |   } | 
 |  | 
 |   for (const auto& pair : params->tab_specific_permissions) { | 
 |     permissions_data->UpdateTabSpecificPermissions(pair.first, pair.second); | 
 |   } | 
 |  | 
 |   extension->SetGUID(params->guid); | 
 |  | 
 |   return extension; | 
 | } | 
 |  | 
 | using IncognitoManifestKeys = api::incognito::ManifestKeys; | 
 |  | 
 | base::debug::CrashKeyString* GetCrashKey(const char* key) { | 
 |   static auto* crash_key = base::debug::AllocateCrashKeyString( | 
 |       key, base::debug::CrashKeySize::Size32); | 
 |   return crash_key; | 
 | } | 
 |  | 
 | const ExtensionId& GetExtensionIdValue(const Extension& extension) { | 
 |   return extension.id(); | 
 | } | 
 |  | 
 | std::string GetManifestVersionValue(const Extension& extension) { | 
 |   return base::NumberToString(extension.manifest_version()); | 
 | } | 
 |  | 
 | const char* GetServiceWorkerBasedValue(const Extension& extension) { | 
 |   return BackgroundInfo::IsServiceWorkerBased(&extension) ? "yes" : "no"; | 
 | } | 
 |  | 
 | const char* GetIncognitoModeValue(const Extension& extension) { | 
 |   IncognitoInfo* info = static_cast<IncognitoInfo*>( | 
 |       extension.GetManifestData(IncognitoManifestKeys::kIncognito)); | 
 |   if (!info) { | 
 |     return "no_incognito_info"; | 
 |   } | 
 |   return api::incognito::ToString(info->mode); | 
 | } | 
 |  | 
 | const char* GetIncognitoProcessValue( | 
 |     const ExtensionsRendererClient* renderer_client) { | 
 |   if (!renderer_client) { | 
 |     return "no_renderer_client"; | 
 |   } | 
 |   return renderer_client->IsIncognitoProcess() ? "yes" : "no"; | 
 | } | 
 |  | 
 | }  // namespace | 
 |  | 
 | namespace debug { | 
 |  | 
 | // Helper for adding a set of missing activation token related crash keys. | 
 | // | 
 | // It is created when being notified that an extension worker will evaluate (is | 
 | // in the process of starting) and we might detect that there isn't an | 
 | // activation token recorded for the extension worker. | 
 | // | 
 | // All keys are logged every time this class is instantiated. | 
 | class ScopedActivationTokenMissingCrashKeys { | 
 |  public: | 
 |   explicit ScopedActivationTokenMissingCrashKeys( | 
 |       const Extension& extension, | 
 |       const ExtensionsRendererClient* renderer_client) | 
 |       : extension_id_crash_key_(GetCrashKey("ext_token_id"), | 
 |                                 GetExtensionIdValue(extension)), | 
 |         manifest_version_crash_key_(GetCrashKey("ext_token_manifest_version"), | 
 |                                     GetManifestVersionValue(extension)), | 
 |         sw_based_crash_key_(GetCrashKey("ext_token_sw_based"), | 
 |                             GetServiceWorkerBasedValue(extension)), | 
 |         incognito_mode_crash_key_(GetCrashKey("ext_token_incog_mode"), | 
 |                                   GetIncognitoModeValue(extension)), | 
 |         incognito_process_crash_key_( | 
 |             GetCrashKey("ext_token_incog_process"), | 
 |             GetIncognitoProcessValue(renderer_client)) {} | 
 |   ~ScopedActivationTokenMissingCrashKeys() = default; | 
 |  | 
 |  private: | 
 |   // ExtensionId of the extension. | 
 |   base::debug::ScopedCrashKeyString extension_id_crash_key_; | 
 |  | 
 |   // The manifest version of the extension. | 
 |   base::debug::ScopedCrashKeyString manifest_version_crash_key_; | 
 |  | 
 |   // Whether the extension has a service worker background script registered in | 
 |   // the manifest. | 
 |   base::debug::ScopedCrashKeyString sw_based_crash_key_; | 
 |  | 
 |   // What the api::incognito::IncognitoMode is for the extension. | 
 |   base::debug::ScopedCrashKeyString incognito_mode_crash_key_; | 
 |  | 
 |   // Whether the renderer process for the extension was launched incognito. | 
 |   base::debug::ScopedCrashKeyString incognito_process_crash_key_; | 
 | }; | 
 |  | 
 | }  // namespace debug | 
 |  | 
 | Dispatcher::PendingServiceWorker::PendingServiceWorker( | 
 |     blink::WebServiceWorkerContextProxy* context_proxy) | 
 |     : task_runner(base::SingleThreadTaskRunner::GetCurrentDefault()), | 
 |       context_proxy(context_proxy) { | 
 |   DCHECK(context_proxy); | 
 | } | 
 |  | 
 | Dispatcher::PendingServiceWorker::~PendingServiceWorker() = default; | 
 |  | 
 | // Note that we can't use Blink public APIs in the constructor because Blink | 
 | // is not initialized at the point we create Dispatcher. | 
 | Dispatcher::Dispatcher( | 
 |     std::vector<std::unique_ptr<const ExtensionsRendererAPIProvider>> | 
 |         api_providers) | 
 |     : api_providers_(std::move(api_providers)), | 
 |       content_watcher_(new ContentWatcher()), | 
 |       source_map_(&ui::ResourceBundle::GetSharedInstance()), | 
 |       v8_schema_registry_(new V8SchemaRegistry), | 
 |       activity_logging_enabled_(false), | 
 |       receiver_(this), | 
 |       dispatcher_(this) { | 
 |   bindings_system_ = CreateBindingsSystem( | 
 |       this, IPCMessageSender::CreateMainThreadIPCMessageSender()); | 
 |  | 
 |   script_context_set_ = | 
 |       std::make_unique<ScriptContextSet>(&active_extension_ids_); | 
 |   user_script_set_manager_ = std::make_unique<UserScriptSetManager>(); | 
 |   script_injection_manager_ = | 
 |       std::make_unique<ScriptInjectionManager>(user_script_set_manager_.get()); | 
 |   user_script_set_manager_observation_.Observe(user_script_set_manager_.get()); | 
 |   PopulateSourceMap(); | 
 |   // Ideally this should be done after checking | 
 |   // ExtensionAPIEnabledInExtensionServiceWorkers(), but the Dispatcher is | 
 |   // created so early that sending an IPC from browser/ process to synchronize | 
 |   // this enabled-ness is too late. | 
 |   WorkerThreadDispatcher::Get()->Init(RenderThread::Get()); | 
 |  | 
 |   // Register WebSecurityPolicy allowlists for the chrome-extension:// scheme. | 
 |   WebString extension_scheme(WebString::FromASCII(kExtensionScheme)); | 
 |  | 
 |   // Extension resources are HTTP-like and safe to expose to the fetch API. The | 
 |   // rules for the fetch API are consistent with XHR. | 
 |   WebSecurityPolicy::RegisterURLSchemeAsSupportingFetchAPI(extension_scheme); | 
 |  | 
 |   // Register WebSecurityPolicy allowlists for the file:// scheme. | 
 |   WebString file_scheme(WebString::FromASCII(url::kFileScheme)); | 
 |  | 
 |   // Extensions are allowed to make cross-origin requests to file scheme iff the | 
 |   // user explicitly grants them access post-installation in the | 
 |   // chrome://extensions page. | 
 |   WebSecurityPolicy::RegisterURLSchemeAsSupportingFetchAPI(file_scheme); | 
 |  | 
 |   // Extension resources, when loaded as the top-level document, should bypass | 
 |   // Blink's strict first-party origin checks. | 
 |   WebSecurityPolicy::RegisterURLSchemeAsFirstPartyWhenTopLevel( | 
 |       extension_scheme); | 
 |  | 
 |   // Disallow running javascript URLs on the chrome-extension scheme. | 
 |   WebSecurityPolicy::RegisterURLSchemeAsNotAllowingJavascriptURLs( | 
 |       extension_scheme); | 
 |  | 
 | #if !BUILDFLAG(IS_ANDROID) | 
 |   // Currently, extensions are only available on desktop, and are process- | 
 |   // separated from regular web contents. Therefore, it is safe to give them | 
 |   // access to SharedArrayBuffers. | 
 |   WebSecurityPolicy::RegisterURLSchemeAsAllowingSharedArrayBuffers( | 
 |       extension_scheme); | 
 | #endif | 
 |  | 
 |   // chrome-extension: resources should be allowed to register ServiceWorkers. | 
 |   WebSecurityPolicy::RegisterURLSchemeAsAllowingServiceWorkers( | 
 |       extension_scheme); | 
 |  | 
 |   WebSecurityPolicy::RegisterURLSchemeAsAllowingWasmEvalCSP(extension_scheme); | 
 |  | 
 |   // Initialize host permissions for any extensions that were activated before | 
 |   // WebKit was initialized. | 
 |   for (const ExtensionId& extension_id : active_extension_ids_) { | 
 |     const Extension* extension = | 
 |         RendererExtensionRegistry::Get()->GetByID(extension_id); | 
 |     CHECK(extension); | 
 |     InitOriginPermissions(extension); | 
 |   } | 
 |  | 
 |   EnableCustomElementAllowlist(); | 
 | } | 
 |  | 
 | Dispatcher::~Dispatcher() { | 
 | } | 
 |  | 
 | // static | 
 | WorkerScriptContextSet* Dispatcher::GetWorkerScriptContextSet() { | 
 |   return &(g_worker_script_context_set.Get()); | 
 | } | 
 |  | 
 | // static | 
 | bool Dispatcher::ShouldNotifyServiceWorkerOnWebSocketActivity( | 
 |     v8::Local<v8::Context> v8_context) { | 
 |   ScriptContext* script_context = | 
 |       GetWorkerScriptContextSet()->GetContextByV8Context(v8_context); | 
 |   // Only notify on web socket activity if the service worker is the background | 
 |   // service worker for an extension. | 
 |   return script_context && | 
 |          ExtensionAPIEnabledForServiceWorkerScript( | 
 |              script_context->service_worker_scope(), script_context->url()); | 
 | } | 
 |  | 
 | void Dispatcher::OnRenderThreadStarted(content::RenderThread* thread) { | 
 |   blink::WebScriptController::RegisterExtension( | 
 |       SafeBuiltins::CreateV8Extension()); | 
 | } | 
 |  | 
 | void Dispatcher::OnRenderFrameCreated(content::RenderFrame* render_frame) { | 
 |   script_injection_manager_->OnRenderFrameCreated(render_frame); | 
 |   content_watcher_->OnRenderFrameCreated(render_frame); | 
 |  | 
 |   // The RenderFrame comes with the initial empty document already created. | 
 |   DidCreateDocumentElement(render_frame->GetWebFrame()); | 
 |   // We run scripts on the empty document. | 
 |   RunScriptsAtDocumentStart(render_frame); | 
 | } | 
 |  | 
 | bool Dispatcher::IsExtensionActive(const ExtensionId& extension_id) const { | 
 |   const bool is_active = base::Contains(active_extension_ids_, extension_id); | 
 |   if (is_active) | 
 |     CHECK(RendererExtensionRegistry::Get()->Contains(extension_id)); | 
 |   return is_active; | 
 | } | 
 |  | 
 | void Dispatcher::DidCreateScriptContext( | 
 |     blink::WebLocalFrame* frame, | 
 |     const v8::Local<v8::Context>& v8_context, | 
 |     int32_t world_id) { | 
 |   const base::TimeTicks start_time = base::TimeTicks::Now(); | 
 |  | 
 |   ScriptContext* context = script_context_set_->Register( | 
 |       frame, v8_context, world_id, | 
 |       /*is_webview=*/webview_partition_id_.has_value()); | 
 |  | 
 |   // Initialize origin permissions for content scripts, which can't be | 
 |   // initialized in |ActivateExtension|. | 
 |   if (context->context_type() == mojom::ContextType::kContentScript) { | 
 |     InitOriginPermissions(context->extension()); | 
 |   } | 
 |  | 
 |   context->SetModuleSystem( | 
 |       std::make_unique<ModuleSystem>(context, &source_map_)); | 
 |  | 
 |   ModuleSystem* module_system = context->module_system(); | 
 |  | 
 |   // Enable natives in startup. | 
 |   ModuleSystem::NativesEnabledScope natives_enabled_scope(module_system); | 
 |  | 
 |   RegisterNativeHandlers(module_system, context, bindings_system_.get(), | 
 |                          v8_schema_registry_.get()); | 
 |  | 
 |   bindings_system_->DidCreateScriptContext(context); | 
 |  | 
 | #if BUILDFLAG(ENABLE_PLATFORM_APPS) | 
 |   // Inject custom JS into the platform app context. | 
 |   if (IsWithinPlatformApp()) { | 
 |     module_system->Require("platformApp"); | 
 |   } | 
 | #endif | 
 |  | 
 | #if BUILDFLAG(ENABLE_GUEST_VIEW) | 
 |   RequireGuestViewModules(context); | 
 | #endif | 
 |  | 
 |   const base::TimeDelta elapsed = base::TimeTicks::Now() - start_time; | 
 |   switch (context->context_type()) { | 
 |     case mojom::ContextType::kUnspecified: | 
 |       UMA_HISTOGRAM_TIMES("Extensions.DidCreateScriptContext_Unspecified", | 
 |                           elapsed); | 
 |       break; | 
 |     case mojom::ContextType::kPrivilegedExtension: | 
 |       // For service workers this is handled in | 
 |       // WillEvaluateServiceWorkerOnWorkerThread(). | 
 |       DCHECK(!context->IsForServiceWorker()); | 
 |       UMA_HISTOGRAM_TIMES("Extensions.DidCreateScriptContext_Blessed", elapsed); | 
 |       break; | 
 |     case mojom::ContextType::kUnprivilegedExtension: | 
 |       UMA_HISTOGRAM_TIMES("Extensions.DidCreateScriptContext_Unblessed", | 
 |                           elapsed); | 
 |       break; | 
 |     case mojom::ContextType::kContentScript: | 
 |       UMA_HISTOGRAM_TIMES("Extensions.DidCreateScriptContext_ContentScript", | 
 |                           elapsed); | 
 |       break; | 
 |     case mojom::ContextType::kWebPage: | 
 |       UMA_HISTOGRAM_TIMES("Extensions.DidCreateScriptContext_WebPage", elapsed); | 
 |       break; | 
 |     case mojom::ContextType::kPrivilegedWebPage: | 
 |       UMA_HISTOGRAM_TIMES("Extensions.DidCreateScriptContext_BlessedWebPage", | 
 |                           elapsed); | 
 |       break; | 
 |     case mojom::ContextType::kWebUi: | 
 |       UMA_HISTOGRAM_TIMES("Extensions.DidCreateScriptContext_WebUI", elapsed); | 
 |       break; | 
 |     case mojom::ContextType::kUntrustedWebUi: | 
 |       // Extension APIs in untrusted WebUIs are temporary so don't bother | 
 |       // recording metrics for them. | 
 |       break; | 
 |     case mojom::ContextType::kOffscreenExtension: | 
 |     case mojom::ContextType::kUserScript: | 
 |       // We don't really care about offscreen extension context or user script | 
 |       // context initialization time at the moment. Offscreen extension context | 
 |       // initialization is a strict subset (and very similar to) privileged | 
 |       // extension context time, while user script context initialization is | 
 |       // very similar to content script initialization. | 
 |       break; | 
 |   } | 
 |  | 
 |   VLOG(1) << "Num tracked contexts: " << script_context_set_->size(); | 
 |  | 
 |   ExtensionFrameHelper* frame_helper = | 
 |       ExtensionFrameHelper::Get(content::RenderFrame::FromWebFrame(frame)); | 
 |   if (!frame_helper) | 
 |     return;  // The frame is invisible to extensions. | 
 |  | 
 |   frame_helper->NotifyDidCreateScriptContext(world_id); | 
 | } | 
 |  | 
 | void Dispatcher::DidInitializeServiceWorkerContextOnWorkerThread( | 
 |     blink::WebServiceWorkerContextProxy* context_proxy, | 
 |     const GURL& service_worker_scope, | 
 |     const GURL& script_url) { | 
 |   if (!script_url.SchemeIs(kExtensionScheme)) | 
 |     return; | 
 |  | 
 |   // Defer `PrepareForEvaluation` until `ModuleSystem` is created. | 
 |   context_proxy->DeferPrepareForEvaluation(); | 
 |  | 
 |   { | 
 |     base::AutoLock lock(service_workers_paused_for_on_loaded_message_lock_); | 
 |     ExtensionId extension_id = | 
 |         RendererExtensionRegistry::Get()->GetExtensionOrAppIDByURL(script_url); | 
 |     // If the extension is already loaded we don't have to suspend the service | 
 |     // worker. The service worker will continue in | 
 |     // Dispatcher::WillEvaluateServiceWorkerOnWorkerThread(). | 
 |     if (RendererExtensionRegistry::Get()->GetByID(extension_id)) | 
 |       return; | 
 |  | 
 |     // Suspend the service worker until loaded message of the extension comes. | 
 |     // The service worker will be resumed in Dispatcher::OnLoaded(). | 
 |     context_proxy->PauseEvaluation(); | 
 |     service_workers_paused_for_on_loaded_message_.emplace( | 
 |         extension_id, std::make_unique<PendingServiceWorker>(context_proxy)); | 
 |   } | 
 | } | 
 |  | 
 | void Dispatcher::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) { | 
 |   const base::TimeTicks start_time = base::TimeTicks::Now(); | 
 |   service_worker_context_state = ServiceWorkerContextState::kInitializing; | 
 |  | 
 |   // TODO(crbug.com/40626913): We may want to give service workers not | 
 |   // registered by extensions minimal bindings, the same as other webpage-like | 
 |   // contexts. | 
 |   if (!script_url.SchemeIs(kExtensionScheme)) { | 
 |     // Early-out if this isn't a chrome-extension:// scheme, because looking up | 
 |     // the extension registry is unnecessary if it's not. Checking this will | 
 |     // also skip over hosted apps, which is the desired behavior - hosted app | 
 |     // service workers are not our concern. | 
 |     service_worker_context_state = | 
 |         ServiceWorkerContextState::kScriptUrlIsNotExtensionScheme; | 
 |     return; | 
 |   } | 
 |  | 
 |   // Runs the deferred `PrepareForEvaluation` before this returns. | 
 |   base::ScopedClosureRunner run_at_return(base::BindOnce( | 
 |       [](blink::WebServiceWorkerContextProxy* context_proxy) { | 
 |         context_proxy->RunDeferredPrepareForEvaluation(); | 
 |       }, | 
 |       context_proxy)); | 
 |  | 
 |   // This function is called from a service worker thread, so we use the | 
 |   // thread-safe refcounted version of GetExtensionOrAppByURL. | 
 |   scoped_refptr<const Extension> extension = | 
 |       RendererExtensionRegistry::Get()->GetRefCountedExtensionOrAppByURL( | 
 |           script_url); | 
 |  | 
 |   ExtensionRendererLoadStatus load_status = | 
 |       ExtensionRendererLoadStatus::kUnknownExtension; | 
 |   if (extension) { | 
 |     load_status = ExtensionRendererLoadStatus::kExtensionLoaded; | 
 |   } else if (base::Contains(unloaded_extensions_, script_url.GetHost())) { | 
 |     // script_url.host() is the extension's ID. | 
 |     load_status = ExtensionRendererLoadStatus::kExtensionUnloaded; | 
 |   } | 
 |   base::UmaHistogramEnumeration( | 
 |       "Extensions.ServiceWorkerRenderer." | 
 |       "ExtensionLoadStatusInWorkerScriptEvaluation", | 
 |       load_status); | 
 |  | 
 |   if (!extension) { | 
 |     // Set the context state based on the extension load status. | 
 |     if (load_status == ExtensionRendererLoadStatus::kExtensionUnloaded) { | 
 |       service_worker_context_state = | 
 |           ServiceWorkerContextState::kUnloadedExtension; | 
 |     } else { | 
 |       service_worker_context_state = ServiceWorkerContextState::kNoExtension; | 
 |     } | 
 |  | 
 |     // TODO(kalman): This is no good. Instead we need to either: | 
 |     // | 
 |     // - Hold onto the v8::Context and create the ScriptContext and install | 
 |     //   our bindings when this extension is loaded. | 
 |     // - Deal with there being an extension ID (script_url.host()) but no | 
 |     //   extension associated with it. | 
 |     // | 
 |     // The former is safer, but is unfriendly to caching (e.g. session restore). | 
 |     // It seems to contradict the service worker idiom. | 
 |     // | 
 |     // The latter is friendly to caching, but running extension code without an | 
 |     // installed extension makes me nervous, and means that we won't be able to | 
 |     // expose arbitrary (i.e. capability-checked) extension APIs to service | 
 |     // workers. We will probably need to relax some assertions - we just need | 
 |     // to find them. | 
 |     // | 
 |     // Perhaps this could be solved with our own event on the service worker | 
 |     // saying that an extension is ready, and documenting that extension APIs | 
 |     // won't work before that event has fired? | 
 |     return; | 
 |   } | 
 |  | 
 |   if (!ExtensionAPIEnabledForServiceWorkerScript(service_worker_scope, | 
 |                                                  script_url)) { | 
 |     service_worker_context_state = ServiceWorkerContextState:: | 
 |         kExtensionAPIIsNotEnabledForServiceWorkerScript; | 
 |     return; | 
 |   } | 
 |  | 
 |   const int thread_id = content::WorkerThread::GetCurrentId(); | 
 |   CHECK_NE(thread_id, kMainThreadId); | 
 |  | 
 |   // Only the script specific in the manifest's background data gets bindings. | 
 |   // | 
 |   // TODO(crbug.com/40626913): We may want to give other service workers | 
 |   // registered by extensions minimal bindings, just as we might want to give | 
 |   // them to service workers that aren't registered by extensions. | 
 |   ScriptContext* context = new ScriptContext( | 
 |       v8_context, nullptr, GenerateHostIdFromExtensionId(extension->id()), | 
 |       extension.get(), /*blink_isolated_world_id=*/std::nullopt, | 
 |       mojom::ContextType::kPrivilegedExtension, extension.get(), | 
 |       mojom::ContextType::kPrivilegedExtension); | 
 |   context->set_url(script_url); | 
 |   context->set_service_worker_scope(service_worker_scope); | 
 |   context->set_service_worker_version_id(service_worker_version_id); | 
 |  | 
 |   WorkerThreadDispatcher* worker_dispatcher = WorkerThreadDispatcher::Get(); | 
 |   std::optional<base::UnguessableToken> worker_activation_token = | 
 |       RendererExtensionRegistry::Get()->GetWorkerActivationToken( | 
 |           extension->id()); | 
 |  | 
 |   std::unique_ptr<IPCMessageSender> ipc_sender = | 
 |       IPCMessageSender::CreateWorkerThreadIPCMessageSender( | 
 |           worker_dispatcher, context_proxy, service_worker_version_id); | 
 |   { | 
 |     CHECK(extension); | 
 |     // TODO(crbug.com/357889496): Remove these crash keys once bug is resolved. | 
 |     debug::ScopedActivationTokenMissingCrashKeys activation_token_missing_keys( | 
 |         *extension, ExtensionsRendererClient::Get()); | 
 |     CHECK(worker_activation_token.has_value()); | 
 |   } | 
 |   worker_dispatcher->AddWorkerData( | 
 |       context_proxy, service_worker_version_id, worker_activation_token, | 
 |       service_worker_token, context, | 
 |       CreateBindingsSystem(worker_dispatcher, std::move(ipc_sender))); | 
 |  | 
 |   // TODO(lazyboy): Make sure accessing |source_map_| in worker thread is | 
 |   // safe. | 
 |   context->SetModuleSystem( | 
 |       std::make_unique<ModuleSystem>(context, &source_map_)); | 
 |  | 
 |   ModuleSystem* module_system = context->module_system(); | 
 |   // Enable natives in startup. | 
 |   ModuleSystem::NativesEnabledScope natives_enabled_scope(module_system); | 
 |   NativeExtensionBindingsSystem* worker_bindings_system = | 
 |       WorkerThreadDispatcher::GetBindingsSystem(); | 
 |   RegisterNativeHandlers(module_system, context, worker_bindings_system, | 
 |                          WorkerThreadDispatcher::GetV8SchemaRegistry()); | 
 |  | 
 |   worker_bindings_system->DidCreateScriptContext(context); | 
 |  | 
 | #if BUILDFLAG(ENABLE_GUEST_VIEW) | 
 |   // TODO(lazyboy): Get rid of RequireGuestViewModules() as this doesn't seem | 
 |   // necessary for Extension SW. | 
 |   RequireGuestViewModules(context); | 
 | #endif | 
 |  | 
 |   WorkerThreadDispatcher::GetServiceWorkerData()->Init(); | 
 |   g_worker_script_context_set.Get().Insert(base::WrapUnique(context)); | 
 |  | 
 |   const base::TimeDelta elapsed = base::TimeTicks::Now() - start_time; | 
 |   UMA_HISTOGRAM_TIMES( | 
 |       "Extensions.DidInitializeServiceWorkerContextOnWorkerThread2", elapsed); | 
 |   service_worker_context_state = ServiceWorkerContextState::kInitialized; | 
 | } | 
 |  | 
 | void Dispatcher::WillReleaseScriptContext( | 
 |     blink::WebLocalFrame* frame, | 
 |     const v8::Local<v8::Context>& v8_context, | 
 |     int32_t world_id) { | 
 |   ScriptContext* context = script_context_set_->GetByV8Context(v8_context); | 
 |   if (!context) | 
 |     return; | 
 |   bindings_system_->WillReleaseScriptContext(context); | 
 |  | 
 |   script_context_set_->Remove(context); | 
 |   VLOG(1) << "Num tracked contexts: " << script_context_set_->size(); | 
 | } | 
 |  | 
 | void Dispatcher::DidStartServiceWorkerContextOnWorkerThread( | 
 |     int64_t service_worker_version_id, | 
 |     const GURL& service_worker_scope, | 
 |     const GURL& script_url) { | 
 |   if (!ExtensionAPIEnabledForServiceWorkerScript(service_worker_scope, | 
 |                                                  script_url)) { | 
 |     return; | 
 |   } | 
 |  | 
 |   // Even if the extension appears in the registry there's a timing issue where | 
 |   // an extension that is reloaded could race with this (stale) async task being | 
 |   // posted for a previously evaluated worker script. | 
 |   // In that case we'd see this start notification for the previous worker | 
 |   // instance that was quickly terminated (for example: a JS syntax error on | 
 |   // script evaluation, but not limited to that) and we wouldn't have a | 
 |   // ServiceWorkerData/ScriptContext to use later in this function. | 
 |   // We'll get another DidStartServiceWorkerContextOnWorkerThread() when the | 
 |   // next worker instance starts so returning early here shouldn't cause any | 
 |   // issues. | 
 |   if (base::FeatureList::IsEnabled( | 
 |           kSpeculativeFixForNoExtensionInDidStartServiceWorkerContext) && | 
 |       service_worker_context_state == | 
 |           ServiceWorkerContextState::kUnloadedExtension) { | 
 |     return; | 
 |   } | 
 |  | 
 |   // TODO(crbug.com/389971360) Remove this once the bug is fixed. | 
 |   SCOPED_CRASH_KEY_NUMBER("extensions", "worker_context_state", | 
 |                           static_cast<int>(service_worker_context_state)); | 
 |  | 
 |   const int thread_id = content::WorkerThread::GetCurrentId(); | 
 |   CHECK_NE(thread_id, kMainThreadId); | 
 |   auto* service_worker_data = WorkerThreadDispatcher::GetServiceWorkerData(); | 
 |   if (base::FeatureList::IsEnabled( | 
 |           kSpeculativeFixForServiceWorkerDataInDidStartServiceWorkerContext)) { | 
 |     // `service_worker_data` can be nullptr if the extension is already unloaded | 
 |     // and the worker thread termination started. | 
 |     // | 
 |     // TODO(crbug.com/389971360) If this does seem to fix it, we should check | 
 |     // `thread_state_` or `requested_to_terminate_` to confirm we're in | 
 |     // termination when `service_worker_data` is false here. | 
 |     if (service_worker_data) { | 
 |       const ExtensionId& extension_id = | 
 |           service_worker_data->context()->GetExtensionID(); | 
 |       CHECK(!extension_id.empty()); | 
 |       service_worker_data->GetServiceWorkerHost()->DidStartServiceWorkerContext( | 
 |           extension_id, *service_worker_data->activation_sequence(), | 
 |           service_worker_scope, service_worker_version_id, thread_id); | 
 |     } | 
 |   } else { | 
 |     CHECK(service_worker_data); | 
 |     const ExtensionId& extension_id = | 
 |         service_worker_data->context()->GetExtensionID(); | 
 |     CHECK(!extension_id.empty()); | 
 |     service_worker_data->GetServiceWorkerHost()->DidStartServiceWorkerContext( | 
 |         extension_id, *service_worker_data->activation_sequence(), | 
 |         service_worker_scope, service_worker_version_id, thread_id); | 
 |   } | 
 | } | 
 |  | 
 | void Dispatcher::WillDestroyServiceWorkerContextOnWorkerThread( | 
 |     v8::Local<v8::Context> v8_context, | 
 |     int64_t service_worker_version_id, | 
 |     const GURL& service_worker_scope, | 
 |     const GURL& script_url) { | 
 |   // Note that using ExtensionAPIEnabledForServiceWorkerScript() won't work here | 
 |   // as RendererExtensionRegistry might have already unloaded this extension. | 
 |   // Use the existence of ServiceWorkerData as the source of truth instead. | 
 |   if (auto* service_worker_data = | 
 |           WorkerThreadDispatcher::GetServiceWorkerData()) { | 
 |     service_worker_context_state = ServiceWorkerContextState::kDestroying; | 
 |     const int thread_id = content::WorkerThread::GetCurrentId(); | 
 |     CHECK_NE(thread_id, kMainThreadId); | 
 |  | 
 |     // TODO(lazyboy/devlin): Should this cleanup happen in a worker class, like | 
 |     // WorkerThreadDispatcher? If so, we should move the initialization as well. | 
 |     ScriptContext* script_context = service_worker_data->context(); | 
 |     const ExtensionId& extension_id = | 
 |         service_worker_data->context()->GetExtensionID(); | 
 |     CHECK(!extension_id.empty()); | 
 |     NativeExtensionBindingsSystem* worker_bindings_system = | 
 |         service_worker_data->bindings_system(); | 
 |     if (worker_bindings_system) { | 
 |       worker_bindings_system->WillReleaseScriptContext(script_context); | 
 |       service_worker_data->GetServiceWorkerHost()->DidStopServiceWorkerContext( | 
 |           extension_id, *service_worker_data->activation_sequence(), | 
 |           service_worker_scope, service_worker_version_id, thread_id); | 
 |     } | 
 |     // Note: we have to remove the context (and thus perform invalidation on | 
 |     // the native handlers) prior to removing the worker data, which destroys | 
 |     // the associated bindings system. | 
 |     g_worker_script_context_set.Get().Remove(v8_context, script_url); | 
 |     WorkerThreadDispatcher::Get()->RemoveWorkerData(service_worker_version_id); | 
 |  | 
 |     // TODO(crbug.com/389971360) Remove this after the fix. If ServiceWorkerData | 
 |     // in `WorkerThreadDispatcher` is removed, update the eligibility status so | 
 |     // that we make sure if ServiceWorkerData is already removed or not in the | 
 |     // crash at `DidStartServiceWorkerContextOnWorkerThread()`. | 
 |     service_worker_context_state = ServiceWorkerContextState::kDestroyed; | 
 |   } else { | 
 |     // If extension APIs in service workers aren't enabled, we just need to | 
 |     // remove the context. | 
 |     g_worker_script_context_set.Get().Remove(v8_context, script_url); | 
 |     // TODO(crbug.com/424476776) Remove this after the fix. | 
 |     service_worker_context_state = | 
 |         ServiceWorkerContextState::kDestroyedWithoutWorkerData; | 
 |   } | 
 |  | 
 |   ExtensionId extension_id = | 
 |       RendererExtensionRegistry::Get()->GetExtensionOrAppIDByURL(script_url); | 
 |   { | 
 |     base::AutoLock lock(service_workers_paused_for_on_loaded_message_lock_); | 
 |     service_workers_paused_for_on_loaded_message_.erase(extension_id); | 
 |   } | 
 | } | 
 |  | 
 | void Dispatcher::DidCreateDocumentElement(blink::WebLocalFrame* frame) { | 
 |   // Note: use GetEffectiveDocumentURLForContext() and not just | 
 |   // frame->document()->url() so that this also injects the stylesheet on | 
 |   // about:blank frames that are hosted in the extension process. (Even though | 
 |   // this is used to determine whether to inject a stylesheet, we don't use | 
 |   // GetEffectiveDocumentURLForInjection() because we inject based on whether | 
 |   // it is an extension context, rather than based on the extension's injection | 
 |   // permissions.) | 
 |   GURL effective_document_url = | 
 |       ScriptContext::GetEffectiveDocumentURLForContext( | 
 |           frame, frame->GetDocument().Url(), true /* match_about_blank */); | 
 |  | 
 |   const Extension* extension = | 
 |       RendererExtensionRegistry::Get()->GetExtensionOrAppByURL( | 
 |           effective_document_url); | 
 |  | 
 |   if (extension && | 
 |       (extension->is_extension() || extension->is_platform_app())) { | 
 |     int resource_id = extension->is_platform_app() ? IDR_PLATFORM_APP_CSS | 
 |                                                    : IDR_EXTENSION_FONTS_CSS; | 
 |     std::string stylesheet = | 
 |         ui::ResourceBundle::GetSharedInstance().LoadDataResourceString( | 
 |             resource_id); | 
 |     base::ReplaceFirstSubstringAfterOffset( | 
 |         &stylesheet, 0, "$FONTFAMILY", system_font_family_); | 
 |     base::ReplaceFirstSubstringAfterOffset( | 
 |         &stylesheet, 0, "$FONTSIZE", system_font_size_); | 
 |  | 
 |     // Blink doesn't let us define an additional user agent stylesheet, so | 
 |     // we insert the default platform app or extension stylesheet into all | 
 |     // documents that are loaded in each app or extension. | 
 |     frame->GetDocument().InsertStyleSheet(WebString::FromUTF8(stylesheet)); | 
 |   } | 
 |  | 
 |   // If this is an extension options page, and the extension has opted into | 
 |   // using Chrome styles, then insert the Chrome extension stylesheet. | 
 |   if (extension && extension->is_extension() && | 
 |       OptionsPageInfo::ShouldUseChromeStyle(extension) && | 
 |       effective_document_url == OptionsPageInfo::GetOptionsPage(extension)) { | 
 |     std::string extension_css = | 
 |         ui::ResourceBundle::GetSharedInstance().LoadDataResourceString( | 
 |             IDR_EXTENSION_CSS); | 
 |     frame->GetDocument().InsertStyleSheet(WebString::FromUTF8(extension_css)); | 
 |   } | 
 | } | 
 |  | 
 | void Dispatcher::RunScriptsAtDocumentStart(content::RenderFrame* render_frame) { | 
 |   ExtensionFrameHelper* frame_helper = ExtensionFrameHelper::Get(render_frame); | 
 |   if (!frame_helper) | 
 |     return;  // The frame is invisible to extensions. | 
 |  | 
 |   frame_helper->RunScriptsAtDocumentStart(); | 
 |   // |frame_helper| and |render_frame| might be dead by now. | 
 | } | 
 |  | 
 | void Dispatcher::RunScriptsAtDocumentEnd(content::RenderFrame* render_frame) { | 
 |   ExtensionFrameHelper* frame_helper = ExtensionFrameHelper::Get(render_frame); | 
 |   if (!frame_helper) | 
 |     return;  // The frame is invisible to extensions. | 
 |  | 
 |   frame_helper->RunScriptsAtDocumentEnd(); | 
 |   // |frame_helper| and |render_frame| might be dead by now. | 
 | } | 
 |  | 
 | void Dispatcher::RunScriptsAtDocumentIdle(content::RenderFrame* render_frame) { | 
 |   ExtensionFrameHelper* frame_helper = ExtensionFrameHelper::Get(render_frame); | 
 |   if (!frame_helper) | 
 |     return;  // The frame is invisible to extensions. | 
 |  | 
 |   frame_helper->RunScriptsAtDocumentIdle(); | 
 |   // |frame_helper| and |render_frame| might be dead by now. | 
 | } | 
 |  | 
 | void Dispatcher::DispatchEventHelper( | 
 |     const mojom::HostID& host_id, | 
 |     const std::string& event_name, | 
 |     const base::Value::List& event_args, | 
 |     mojom::EventFilteringInfoPtr filtering_info) const { | 
 |   script_context_set_->ForEach( | 
 |       host_id, nullptr, | 
 |       base::BindRepeating( | 
 |           &NativeExtensionBindingsSystem::DispatchEventInContext, | 
 |           base::Unretained(bindings_system_.get()), event_name, | 
 |           std::cref(event_args), base::OwnedRef(std::move(filtering_info)))); | 
 | } | 
 |  | 
 | void Dispatcher::InvokeModuleSystemMethod(content::RenderFrame* render_frame, | 
 |                                           const ExtensionId& extension_id, | 
 |                                           const std::string& module_name, | 
 |                                           const std::string& function_name, | 
 |                                           const base::Value::List& args) { | 
 |   script_context_set_->ForEach( | 
 |       GenerateHostIdFromExtensionId(extension_id), render_frame, | 
 |       base::BindRepeating(&CallModuleMethod, module_name, function_name, | 
 |                           &args)); | 
 | } | 
 |  | 
 | void Dispatcher::ExecuteDeclarativeScript(content::RenderFrame* render_frame, | 
 |                                           int tab_id, | 
 |                                           const ExtensionId& extension_id, | 
 |                                           const std::string& script_id, | 
 |                                           const GURL& url) { | 
 |   TRACE_RENDERER_EXTENSION_EVENT("Dispatcher::ExecuteDeclarativeScript", | 
 |                                  extension_id); | 
 |   script_injection_manager_->ExecuteDeclarativeScript( | 
 |       render_frame, tab_id, extension_id, script_id, url); | 
 | } | 
 |  | 
 | void Dispatcher::ExecuteCode(mojom::ExecuteCodeParamsPtr param, | 
 |                              mojom::LocalFrame::ExecuteCodeCallback callback, | 
 |                              content::RenderFrame* render_frame) { | 
 |   TRACE_RENDERER_EXTENSION_EVENT("Dispatcher::ExecuteCode", param->host_id->id); | 
 |   script_injection_manager_->HandleExecuteCode( | 
 |       std::move(param), std::move(callback), render_frame); | 
 | } | 
 |  | 
 | void Dispatcher::RegisterMojoInterfaces( | 
 |     blink::AssociatedInterfaceRegistry* associated_interfaces) { | 
 |   // This base::Unretained() is safe, because: | 
 |   // 1) the Dispatcher is a RenderThreadObserver and outlives the RenderThread. | 
 |   // 2) |asscoiated_interfaces| is owned by the RenderThread. | 
 |   // As well the Dispatcher is owned by the | 
 |   // ExtensionsRendererClient, which in turn is a leaky LazyInstance (and thus | 
 |   // never deleted). | 
 |   associated_interfaces->AddInterface<mojom::Renderer>(base::BindRepeating( | 
 |       &Dispatcher::OnRendererAssociatedRequest, base::Unretained(this))); | 
 |   associated_interfaces->AddInterface<mojom::EventDispatcher>( | 
 |       base::BindRepeating(&Dispatcher::OnEventDispatcherRequest, | 
 |                           base::Unretained(this))); | 
 | } | 
 |  | 
 | void Dispatcher::UnregisterMojoInterfaces( | 
 |     blink::AssociatedInterfaceRegistry* associated_interfaces) { | 
 |   associated_interfaces->RemoveInterface(mojom::Renderer::Name_); | 
 |   associated_interfaces->RemoveInterface(mojom::EventDispatcher::Name_); | 
 | } | 
 |  | 
 | void Dispatcher::OnRendererAssociatedRequest( | 
 |     mojo::PendingAssociatedReceiver<mojom::Renderer> receiver) { | 
 |   receiver_.Bind(std::move(receiver)); | 
 | } | 
 |  | 
 | void Dispatcher::OnEventDispatcherRequest( | 
 |     mojo::PendingAssociatedReceiver<mojom::EventDispatcher> dispatcher) { | 
 |   CHECK(!dispatcher_.is_bound()); | 
 |   dispatcher_.Bind(std::move(dispatcher)); | 
 |   dispatcher_.reset_on_disconnect(); | 
 | } | 
 |  | 
 | void Dispatcher::ActivateExtension(const ExtensionId& extension_id) { | 
 |   TRACE_RENDERER_EXTENSION_EVENT("Dispatcher::ActivateExtension", extension_id); | 
 |  | 
 |   CHECK(!extension_id.empty()); | 
 |   const Extension* extension = | 
 |       RendererExtensionRegistry::Get()->GetByID(extension_id); | 
 |   if (!extension) { | 
 |     // Extension was activated but was never loaded. This probably means that | 
 |     // the renderer failed to load it (or the browser failed to tell us when it | 
 |     // did). Failures shouldn't happen, but instead of crashing there (which | 
 |     // executes on all renderers) just log an error and dump without crashing. | 
 |     std::string& error = extension_load_errors_[extension_id]; | 
 |     char minidump[256]; | 
 |     base::debug::Alias(&minidump); | 
 |     base::SpanPrintf(minidump, "e::dispatcher:%s:%s", extension_id.c_str(), | 
 |                      error.c_str()); | 
 |     LOG(ERROR) << extension_id << " was never loaded: " << error; | 
 |     base::debug::DumpWithoutCrashing(); | 
 |     return; | 
 |   } | 
 |  | 
 |   // It's possible that the same extension might generate multiple activation | 
 |   // messages, for example from an extension background page followed by an | 
 |   // extension subframe on a regular tab.  Ensure that any given extension is | 
 |   // only activated once. | 
 |   if (IsExtensionActive(extension_id)) | 
 |     return; | 
 |  | 
 |   active_extension_ids_.insert(extension_id); | 
 |  | 
 |   if (activity_logging_enabled_) { | 
 |     DOMActivityLogger::AttachToWorld(DOMActivityLogger::kMainWorldId, | 
 |                                      extension_id); | 
 |   } | 
 |  | 
 |   InitOriginPermissions(extension); | 
 |  | 
 |   UpdateActiveExtensions(); | 
 | } | 
 |  | 
 | void Dispatcher::LoadExtensions( | 
 |     std::vector<mojom::ExtensionLoadedParamsPtr> loaded_extensions) { | 
 |   for (auto& param : loaded_extensions) { | 
 |     std::u16string error; | 
 |     ExtensionId id = param->id; | 
 |     std::optional<base::UnguessableToken> worker_activation_token = | 
 |         param->worker_activation_token; | 
 |     SetCurrentUserScriptAllowedState(kRendererProfileId, id, | 
 |                                      param->user_scripts_allowed); | 
 |  | 
 |     scoped_refptr<const Extension> extension = | 
 |         ConvertToExtension(std::move(param), kRendererProfileId, &error); | 
 |     if (!extension.get()) { | 
 |       // Note: in tests |param.id| has been observed to be empty (see comment | 
 |       // just below) so this isn't all that reliable. | 
 |       NOTREACHED() << error; | 
 |     } | 
 |  | 
 |     RendererExtensionRegistry* extension_registry = | 
 |         RendererExtensionRegistry::Get(); | 
 |  | 
 |     // The order of setting the token before inserting the extension is | 
 |     // intentional so that DidInitializeServiceWorkerContextOnWorkerThread() | 
 |     // will pause the worker evaluation | 
 |     // (WillEvaluateServiceWorkerOnWorkerThread()) from running before we have | 
 |     // these two necessary pieces of information for setting up the extension | 
 |     // bindings. | 
 |     if (worker_activation_token.has_value()) { | 
 |       extension_registry->SetWorkerActivationToken( | 
 |           extension, std::move(*worker_activation_token)); | 
 |     } | 
 |     // TODO(jlulejian): This is deliberately a CHECK because other parts of the | 
 |     // renderer assume that an extension has been loaded (e.g. specifically | 
 |     // worker script evaluation process). If this fails, other CHECK()s and | 
 |     // logic will fail too. | 
 |     CHECK(extension_registry->Insert(extension)); | 
 |  | 
 |     unloaded_extensions_.erase(extension->id()); | 
 |  | 
 |     ExtensionsRendererClient::Get()->OnExtensionLoaded(*extension); | 
 |  | 
 |     // Resume service worker if it is suspended. | 
 |     { | 
 |       base::AutoLock lock(service_workers_paused_for_on_loaded_message_lock_); | 
 |       auto it = | 
 |           service_workers_paused_for_on_loaded_message_.find(extension->id()); | 
 |       if (it != service_workers_paused_for_on_loaded_message_.end()) { | 
 |         // It's possible that LoadExtensions is called multiple times for the | 
 |         // same extension before the worker thread has a chance to run the | 
 |         // ResumeEvaluationOnWorkerThread task. In that case, the task_runner | 
 |         // will have already been moved, so we need to check if it's valid. | 
 |         if (it->second->task_runner) { | 
 |           scoped_refptr<base::SingleThreadTaskRunner> task_runner = | 
 |               std::move(it->second->task_runner); | 
 |           // Using base::Unretained() should be fine as this won't get | 
 |           // destructed. | 
 |           task_runner->PostTask( | 
 |               FROM_HERE, | 
 |               base::BindOnce(&Dispatcher::ResumeEvaluationOnWorkerThread, | 
 |                              base::Unretained(this), extension->id())); | 
 |         } | 
 |       } | 
 |     } | 
 |   } | 
 |  | 
 |   // Update the available bindings for all contexts. These may have changed if | 
 |   // an externally_connectable extension was loaded that can connect to an | 
 |   // open webpage. | 
 |   UpdateAllBindings(/*api_permissions_changed=*/false); | 
 | } | 
 |  | 
 | void Dispatcher::UnloadExtension(const ExtensionId& extension_id) { | 
 |   TRACE_RENDERER_EXTENSION_EVENT("Dispatcher::UnloadExtension", extension_id); | 
 |  | 
 |   CHECK(!extension_id.empty()); | 
 |   // An extension should be in the registry if we are unloading it. Otherwise we | 
 |   // might be doing something out of the expected order. | 
 |   CHECK(RendererExtensionRegistry::Get()->Remove(extension_id)); | 
 |  | 
 |   unloaded_extensions_.insert(extension_id); | 
 |  | 
 |   ExtensionsRendererClient::Get()->OnExtensionUnloaded(extension_id); | 
 |  | 
 |   bindings_system_->OnExtensionRemoved(extension_id); | 
 |  | 
 |   active_extension_ids_.erase(extension_id); | 
 |  | 
 |   user_script_set_manager_->OnExtensionUnloaded(extension_id); | 
 |  | 
 |   script_injection_manager_->OnExtensionUnloaded(extension_id); | 
 |  | 
 |   // If the extension is later reloaded with a different set of permissions, | 
 |   // we'd like it to get a new isolated world ID, so that it can pick up the | 
 |   // changed origin allowlist. | 
 |   IsolatedWorldManager::GetInstance().RemoveIsolatedWorlds(extension_id); | 
 |  | 
 |   // Inform the bindings system that the contexts will be removed to allow time | 
 |   // to clear out context-specific data, and then remove the contexts | 
 |   // themselves. | 
 |   script_context_set_->ForEach( | 
 |       GenerateHostIdFromExtensionId(extension_id), nullptr, | 
 |       base::BindRepeating( | 
 |           &NativeExtensionBindingsSystem::WillReleaseScriptContext, | 
 |           base::Unretained(bindings_system_.get()))); | 
 |   script_context_set_->OnExtensionUnloaded(extension_id); | 
 |  | 
 |   // Update the available bindings for the remaining contexts. These may have | 
 |   // changed if an externally_connectable extension is unloaded and a webpage | 
 |   // is no longer accessible. | 
 |   UpdateAllBindings(/*api_permissions_changed=*/false); | 
 |  | 
 |   // Invalidates the messages map for the extension in case the extension is | 
 |   // reloaded with a new messages map. | 
 |   SharedL10nMap::GetInstance().EraseMessagesMap(extension_id); | 
 |  | 
 |   // Update the origin access map so that any content scripts injected no longer | 
 |   // have dedicated allow/block lists for extra origins. | 
 |   WebSecurityPolicy::ClearOriginAccessListForOrigin( | 
 |       Extension::GetBaseURLFromExtensionId(extension_id)); | 
 |  | 
 |   // We don't do anything with existing platform-app stylesheets. They will | 
 |   // stay resident, but the URL pattern corresponding to the unloaded | 
 |   // extension's URL just won't match anything anymore. | 
 | } | 
 |  | 
 | void Dispatcher::SuspendExtension( | 
 |     const ExtensionId& extension_id, | 
 |     mojom::Renderer::SuspendExtensionCallback callback) { | 
 |   TRACE_RENDERER_EXTENSION_EVENT("Dispatcher::SuspendExtension", extension_id); | 
 |  | 
 |   CHECK(!extension_id.empty()); | 
 |   // Dispatch the suspend event. This doesn't go through the standard event | 
 |   // dispatch machinery because it requires special handling. We need to let | 
 |   // the browser know when we are starting and stopping the event dispatch, so | 
 |   // that it still considers the extension idle despite any activity the suspend | 
 |   // event creates. | 
 |   DispatchEventHelper(GenerateHostIdFromExtensionId(extension_id), | 
 |                       kOnSuspendEvent, base::Value::List(), nullptr); | 
 |   std::move(callback).Run(); | 
 | } | 
 |  | 
 | void Dispatcher::CancelSuspendExtension(const ExtensionId& extension_id) { | 
 |   CHECK(!extension_id.empty()); | 
 |   DispatchEventHelper(GenerateHostIdFromExtensionId(extension_id), | 
 |                       kOnSuspendCanceledEvent, base::Value::List(), nullptr); | 
 | } | 
 |  | 
 | void Dispatcher::SetSystemFont(const std::string& font_family, | 
 |                                const std::string& font_size) { | 
 |   system_font_family_ = font_family; | 
 |   system_font_size_ = font_size; | 
 | } | 
 |  | 
 | void Dispatcher::SetWebViewPartitionID(const std::string& partition_id) { | 
 |   // |webview_partition_id_| cannot be changed once set. | 
 |   CHECK(!webview_partition_id_ || webview_partition_id_ == partition_id); | 
 |   webview_partition_id_ = partition_id; | 
 | } | 
 |  | 
 | void Dispatcher::SetScriptingAllowlist( | 
 |     const std::vector<ExtensionId>& extension_ids) { | 
 |   CHECK(std::all_of( | 
 |       extension_ids.begin(), extension_ids.end(), | 
 |       [](const ExtensionId& extension_id) { return !extension_id.empty(); })); | 
 |   ExtensionsClient::Get()->SetScriptingAllowlist(extension_ids); | 
 | } | 
 |  | 
 | void Dispatcher::UpdateDefaultPolicyHostRestrictions( | 
 |     URLPatternSet default_policy_blocked_hosts, | 
 |     URLPatternSet default_policy_allowed_hosts) { | 
 |   PermissionsData::SetDefaultPolicyHostRestrictions( | 
 |       kRendererProfileId, default_policy_blocked_hosts, | 
 |       default_policy_allowed_hosts); | 
 |   // Update blink host permission allowlist exceptions for all loaded | 
 |   // extensions. | 
 |   for (const ExtensionId& extension_id : | 
 |        RendererExtensionRegistry::Get()->GetIDs()) { | 
 |     const Extension* extension = | 
 |         RendererExtensionRegistry::Get()->GetByID(extension_id); | 
 |     if (extension->permissions_data()->UsesDefaultPolicyHostRestrictions()) { | 
 |       UpdateOriginPermissions(*extension); | 
 |     } | 
 |   } | 
 |   UpdateAllBindings(/*api_permissions_changed=*/false); | 
 | } | 
 |  | 
 | void Dispatcher::UpdateUserScriptWorlds( | 
 |     std::vector<mojom::UserScriptWorldInfoPtr> infos) { | 
 |   for (const auto& info : infos) { | 
 |     IsolatedWorldManager::GetInstance().SetUserScriptWorldProperties( | 
 |         info->extension_id, info->world_id, info->csp, info->enable_messaging); | 
 |   } | 
 | } | 
 |  | 
 | void Dispatcher::ClearUserScriptWorldConfig( | 
 |     const ExtensionId& extension_id, | 
 |     const std::optional<std::string>& world_id) { | 
 |   CHECK(!extension_id.empty()); | 
 |   IsolatedWorldManager::GetInstance().ClearUserScriptWorldProperties( | 
 |       extension_id, world_id); | 
 | } | 
 |  | 
 | void Dispatcher::UpdateUserHostRestrictions(URLPatternSet user_blocked_hosts, | 
 |                                             URLPatternSet user_allowed_hosts) { | 
 |   PermissionsData::SetUserHostRestrictions(kRendererProfileId, | 
 |                                            std::move(user_blocked_hosts), | 
 |                                            std::move(user_allowed_hosts)); | 
 |  | 
 |   // TODO(crbug.com/40803363): Update origin permissions and bindings as | 
 |   // we do with policy host restrictions above.  Currently, user host | 
 |   // restrictions aren't used in the origin access allowlist, so there's no | 
 |   // point in updating it. | 
 | } | 
 |  | 
 | void Dispatcher::UpdateTabSpecificPermissions(const ExtensionId& extension_id, | 
 |                                               URLPatternSet new_hosts, | 
 |                                               int tab_id, | 
 |                                               bool update_origin_allowlist) { | 
 |   CHECK(!extension_id.empty()); | 
 |   const Extension* extension = | 
 |       RendererExtensionRegistry::Get()->GetByID(extension_id); | 
 |   if (!extension) | 
 |     return; | 
 |  | 
 |   extension->permissions_data()->UpdateTabSpecificPermissions( | 
 |       tab_id, PermissionSet(APIPermissionSet(), ManifestPermissionSet(), | 
 |                             new_hosts.Clone(), new_hosts.Clone())); | 
 |  | 
 |   if (update_origin_allowlist) | 
 |     UpdateOriginPermissions(*extension); | 
 | } | 
 |  | 
 | void Dispatcher::UpdateUserScripts( | 
 |     base::ReadOnlySharedMemoryRegion shared_memory, | 
 |     mojom::HostIDPtr host_id) { | 
 |   TRACE_RENDERER_EXTENSION_EVENT("Dispatcher::UpdateUserScripts", host_id->id); | 
 |   user_script_set_manager_->OnUpdateUserScripts(std::move(shared_memory), | 
 |                                                 *host_id); | 
 | } | 
 |  | 
 | void Dispatcher::ClearTabSpecificPermissions( | 
 |     const std::vector<ExtensionId>& extension_ids, | 
 |     int tab_id, | 
 |     bool update_origin_allowlist) { | 
 |   for (const ExtensionId& id : extension_ids) { | 
 |     CHECK(!id.empty()); | 
 |     const Extension* extension = RendererExtensionRegistry::Get()->GetByID(id); | 
 |     if (extension) { | 
 |       extension->permissions_data()->ClearTabSpecificPermissions(tab_id); | 
 |       if (update_origin_allowlist) | 
 |         UpdateOriginPermissions(*extension); | 
 |     } | 
 |   } | 
 | } | 
 |  | 
 | void Dispatcher::WatchPages(const std::vector<std::string>& css_selectors) { | 
 |   DCHECK(content_watcher_); | 
 |   content_watcher_->OnWatchPages(css_selectors); | 
 | } | 
 |  | 
 | void Dispatcher::DispatchEvent(mojom::DispatchEventParamsPtr params, | 
 |                                base::Value::List event_args, | 
 |                                DispatchEventCallback callback) { | 
 |   CHECK_EQ(params->worker_thread_id, kMainThreadId); | 
 |   CHECK(params->host_id); | 
 |   content::RenderFrame* background_frame = nullptr; | 
 |   if (params->host_id->type == mojom::HostID::HostType::kExtensions) { | 
 |     background_frame = ExtensionFrameHelper::GetBackgroundPageFrame( | 
 |         GenerateExtensionIdFromHostId(*params->host_id)); | 
 |   } | 
 |   ScriptContext* background_context = nullptr; | 
 |   if (background_frame) { | 
 |     background_context = | 
 |         ScriptContextSet::GetMainWorldContextForFrame(background_frame); | 
 |   } | 
 |   bool event_has_listener_in_background_context = | 
 |       background_context && bindings_system_->HasEventListenerInContext( | 
 |                                 params->event_name, background_context); | 
 |  | 
 |   // Synthesize a user gesture if this was in response to user action; this is | 
 |   // necessary if the gesture was e.g. by clicking on the extension toolbar | 
 |   // icon, context menu entry, etc. | 
 |   // | 
 |   // This will only add an active user gesture for the background page, so any | 
 |   // listeners in different frames (like a popup or tab) won't be able to use | 
 |   // the user gesture. This is intentional, since frames other than the | 
 |   // background page should have their own user gestures, such as through button | 
 |   // clicks. | 
 |   if (params->is_user_gesture && background_context && | 
 |       event_has_listener_in_background_context) { | 
 |     background_frame->GetWebFrame()->NotifyUserActivation( | 
 |         blink::mojom::UserActivationNotificationType::kExtensionEvent); | 
 |   } | 
 |  | 
 |   DispatchEventHelper(*params->host_id, params->event_name, event_args, | 
 |                       std::move(params->filtering_info)); | 
 |   std::move(callback).Run(event_has_listener_in_background_context); | 
 | } | 
 |  | 
 | void Dispatcher::SetDeveloperMode(bool current_developer_mode) { | 
 |   SetCurrentDeveloperMode(kRendererProfileId, current_developer_mode); | 
 |   // Since this affects the availability of different APIs, we indicate that | 
 |   // api permissions may have changed. | 
 |   UpdateAllBindings(/*api_permissions_changed=*/true); | 
 | } | 
 |  | 
 | void Dispatcher::SetUserScriptsAllowed(const ExtensionId& extension_id, | 
 |                                        bool enabled) { | 
 |   CHECK(!extension_id.empty()); | 
 |   SetCurrentUserScriptAllowedState(kRendererProfileId, extension_id, enabled); | 
 |   const Extension* extension = | 
 |       RendererExtensionRegistry::Get()->GetByID(extension_id); | 
 |   if (!extension) { | 
 |     return; | 
 |   } | 
 |   UpdateBindingsForExtension(*extension); | 
 | } | 
 |  | 
 | void Dispatcher::SetSessionInfo(version_info::Channel channel, | 
 |                                 mojom::FeatureSessionType session_type) { | 
 |   SetCurrentChannel(channel); | 
 |   SetCurrentFeatureSessionType(session_type); | 
 | } | 
 |  | 
 | void Dispatcher::ShouldSuspend(ShouldSuspendCallback callback) { | 
 |   std::move(callback).Run(); | 
 | } | 
 |  | 
 | void Dispatcher::TransferBlobs(TransferBlobsCallback callback) { | 
 |   std::move(callback).Run(); | 
 | } | 
 |  | 
 | void Dispatcher::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) { | 
 |   CHECK(!extension_id.empty()); | 
 |   const Extension* extension = | 
 |       RendererExtensionRegistry::Get()->GetByID(extension_id); | 
 |   if (!extension) | 
 |     return; | 
 |  | 
 |   if (uses_default_policy_host_restrictions) { | 
 |     extension->permissions_data()->SetUsesDefaultHostRestrictions(); | 
 |   } else { | 
 |     extension->permissions_data()->SetPolicyHostRestrictions( | 
 |         policy_blocked_hosts, policy_allowed_hosts); | 
 |   } | 
 |  | 
 |   extension->permissions_data()->SetPermissions( | 
 |       std::make_unique<const PermissionSet>(std::move(active_permissions)), | 
 |       std::make_unique<const PermissionSet>(std::move(withheld_permissions))); | 
 |  | 
 |   UpdateOriginPermissions(*extension); | 
 |  | 
 |   UpdateBindingsForExtension(*extension); | 
 | } | 
 |  | 
 | void Dispatcher::SetActivityLoggingEnabled(bool enabled) { | 
 |   activity_logging_enabled_ = enabled; | 
 |   if (enabled) { | 
 |     for (const ExtensionId& id : active_extension_ids_) { | 
 |       DOMActivityLogger::AttachToWorld(DOMActivityLogger::kMainWorldId, id); | 
 |     } | 
 |   } | 
 |   script_injection_manager_->set_activity_logging_enabled(enabled); | 
 |   user_script_set_manager_->set_activity_logging_enabled(enabled); | 
 | } | 
 |  | 
 | void Dispatcher::OnUserScriptsUpdated(const mojom::HostID& changed_host) { | 
 |   // Update the set of active extensions if `changed_host` is an extension and | 
 |   // it has scripts. | 
 |   if (changed_host.type == mojom::HostID::HostType::kExtensions) | 
 |     UpdateActiveExtensions(); | 
 | } | 
 |  | 
 | ScriptContextSetIterable* Dispatcher::GetScriptContextSet() { | 
 |   return script_context_set_iterator(); | 
 | } | 
 |  | 
 | void Dispatcher::UpdateActiveExtensions() { | 
 |   std::set<ExtensionId> active_extensions = active_extension_ids_; | 
 |   user_script_set_manager_->GetAllActiveExtensionIds(&active_extensions); | 
 |  | 
 |   // In single-process mode, the browser process reports the active extensions. | 
 |   if (!base::CommandLine::ForCurrentProcess()->HasSwitch( | 
 |           ::switches::kSingleProcess)) { | 
 |     crash_keys::SetActiveExtensions(active_extensions); | 
 |   } | 
 | } | 
 |  | 
 | void Dispatcher::InitOriginPermissions(const Extension* extension) { | 
 |   UpdateOriginPermissions(*extension); | 
 | } | 
 |  | 
 | void Dispatcher::UpdateOriginPermissions(const Extension& extension) { | 
 |   // Remove all old patterns associated with this extension. | 
 |   WebSecurityPolicy::ClearOriginAccessListForOrigin(extension.url()); | 
 |  | 
 |   std::vector<network::mojom::CorsOriginPatternPtr> allow_list = | 
 |       CreateCorsOriginAccessAllowList(extension); | 
 |   ExtensionsClient::Get()->AddOriginAccessPermissions( | 
 |       extension, IsExtensionActive(extension.id()), &allow_list); | 
 |   for (const auto& entry : allow_list) { | 
 |     WebSecurityPolicy::AddOriginAccessAllowListEntry( | 
 |         extension.url(), WebString::FromUTF8(entry->protocol), | 
 |         WebString::FromUTF8(entry->domain), entry->port, | 
 |         entry->domain_match_mode, entry->port_match_mode, entry->priority); | 
 |   } | 
 |  | 
 |   for (const auto& entry : CreateCorsOriginAccessBlockList(extension)) { | 
 |     WebSecurityPolicy::AddOriginAccessBlockListEntry( | 
 |         extension.url(), WebString::FromUTF8(entry->protocol), | 
 |         WebString::FromUTF8(entry->domain), entry->port, | 
 |         entry->domain_match_mode, entry->port_match_mode, entry->priority); | 
 |   } | 
 | } | 
 |  | 
 | void Dispatcher::EnableCustomElementAllowlist() { | 
 |   blink::WebCustomElement::AddEmbedderCustomElementName("appview"); | 
 |   blink::WebCustomElement::AddEmbedderCustomElementName("extensionoptions"); | 
 |   blink::WebCustomElement::AddEmbedderCustomElementName("webview"); | 
 |   for (const auto& api_provider : api_providers_) { | 
 |     api_provider->EnableCustomElementAllowlist(); | 
 |   } | 
 | } | 
 |  | 
 | void Dispatcher::UpdateAllBindings(bool api_permissions_changed) { | 
 |   bindings_system_->UpdateBindings(ExtensionId() /* all contexts */, | 
 |                                    api_permissions_changed, | 
 |                                    script_context_set_iterator()); | 
 |  | 
 |   WorkerThreadDispatcher::Get()->UpdateAllServiceWorkerBindings(); | 
 | } | 
 |  | 
 | void Dispatcher::UpdateBindingsForExtension(const Extension& extension) { | 
 |   bindings_system_->UpdateBindings(extension.id(), | 
 |                                    true /* permissions_changed */, | 
 |                                    script_context_set_iterator()); | 
 |  | 
 |   // Update Service Worker bindings too, if applicable. | 
 |   if (!BackgroundInfo::IsServiceWorkerBased(&extension)) | 
 |     return; | 
 |  | 
 |   const bool updated = | 
 |       WorkerThreadDispatcher::Get()->UpdateBindingsForWorkers(extension.id()); | 
 |   // TODO(lazyboy): When can this fail? | 
 |   DCHECK(updated) << "Some or all workers failed to update bindings."; | 
 | } | 
 |  | 
 | // NOTE: please use the naming convention "foo_natives" for these. | 
 | void Dispatcher::RegisterNativeHandlers( | 
 |     ModuleSystem* module_system, | 
 |     ScriptContext* context, | 
 |     NativeExtensionBindingsSystem* bindings_system, | 
 |     V8SchemaRegistry* v8_schema_registry) { | 
 |   for (const auto& api_provider : api_providers_) { | 
 |     api_provider->RegisterNativeHandlers(module_system, bindings_system, | 
 |                                          v8_schema_registry, context); | 
 |   } | 
 | } | 
 |  | 
 | void Dispatcher::PopulateSourceMap() { | 
 |   for (const auto& api_provider : api_providers_) { | 
 |     api_provider->PopulateSourceMap(&source_map_); | 
 |   } | 
 | } | 
 |  | 
 | bool Dispatcher::IsWithinPlatformApp() { | 
 |   for (auto iter = active_extension_ids_.begin(); | 
 |        iter != active_extension_ids_.end(); ++iter) { | 
 |     const Extension* extension = | 
 |         RendererExtensionRegistry::Get()->GetByID(*iter); | 
 |     if (extension && extension->is_platform_app()) | 
 |       return true; | 
 |   } | 
 |   return false; | 
 | } | 
 |  | 
 | #if BUILDFLAG(ENABLE_GUEST_VIEW) | 
 | void Dispatcher::RequireGuestViewModules(ScriptContext* context) { | 
 |   ModuleSystem* module_system = context->module_system(); | 
 |   bool requires_guest_view_module = false; | 
 |  | 
 |   // This determines whether to register error-providing custom elements for the | 
 |   // GuestView types that are not available. We only do this in contexts where | 
 |   // it is possible to gain access to a given GuestView element by declaring the | 
 |   // necessary permission in a manifest file. We don't want to define | 
 |   // error-providing elements in other extension contexts as the names could | 
 |   // collide with names used in the extension. Also, WebUIs may be allowlisted | 
 |   // to use GuestViews, but we don't define the error-providing elements in this | 
 |   // case. | 
 |   const bool is_platform_app = | 
 |       context->context_type() == mojom::ContextType::kPrivilegedExtension && | 
 |       !context->IsForServiceWorker() && context->extension() && | 
 |       context->extension()->is_platform_app(); | 
 |   // The webview permission is also available to internal allowlisted | 
 |   // extensions, but not to extensions in general. | 
 |   const bool web_view_permission_exists = is_platform_app; | 
 |  | 
 |   // TODO(fsamuel): Eagerly calling Require on context startup is expensive. | 
 |   // It would be better if there were a light way of detecting when a webview | 
 |   // or appview is created and only then set up the infrastructure. | 
 |  | 
 | #if BUILDFLAG(ENABLE_PLATFORM_APPS) | 
 |   const bool app_view_permission_exists = is_platform_app; | 
 |   // Require AppView. | 
 |   if (context->GetAvailability("appViewEmbedderInternal").is_available()) { | 
 |     requires_guest_view_module = true; | 
 |     module_system->Require("appViewElement"); | 
 |   } else if (app_view_permission_exists) { | 
 |     module_system->Require("appViewDeny"); | 
 |   } | 
 | #endif | 
 |  | 
 |   // Require ExtensionOptions. | 
 |   if (context->GetAvailability("extensionOptionsInternal").is_available()) { | 
 |     requires_guest_view_module = true; | 
 |     module_system->Require("extensionOptionsElement"); | 
 |   } | 
 |  | 
 |   // Require WebView. | 
 |   if (context->GetAvailability("webViewInternal").is_available()) { | 
 |     requires_guest_view_module = true; | 
 |  | 
 |     for (const auto& api_provider : api_providers_) { | 
 |       api_provider->RequireWebViewModules(context); | 
 |     } | 
 |   } else if (web_view_permission_exists) { | 
 |     module_system->Require("webViewDeny"); | 
 |   } | 
 |  | 
 |   if (requires_guest_view_module) { | 
 |     // If a frame has guest view custom elements defined, we need to make sure | 
 |     // the custom elements are also defined in subframes. The subframes will | 
 |     // need a scripting context which we will need to forcefully create if | 
 |     // the subframe doesn't otherwise have any scripts. | 
 |     context->web_frame() | 
 |         ->View() | 
 |         ->GetSettings() | 
 |         ->SetForceMainWorldInitialization(true); | 
 |   } | 
 | } | 
 | #endif  // BUILDFLAG(ENABLE_GUEST_VIEW) | 
 |  | 
 | std::unique_ptr<NativeExtensionBindingsSystem> Dispatcher::CreateBindingsSystem( | 
 |     NativeExtensionBindingsSystem::Delegate* delegate, | 
 |     std::unique_ptr<IPCMessageSender> ipc_sender) { | 
 |   auto bindings_system = std::make_unique<NativeExtensionBindingsSystem>( | 
 |       delegate, std::move(ipc_sender)); | 
 |   for (const auto& api_provider : api_providers_) { | 
 |     api_provider->AddBindingsSystemHooks(this, bindings_system.get()); | 
 |   } | 
 |   return bindings_system; | 
 | } | 
 |  | 
 | void Dispatcher::ResumeEvaluationOnWorkerThread( | 
 |     const ExtensionId& extension_id) { | 
 |   base::AutoLock lock(service_workers_paused_for_on_loaded_message_lock_); | 
 |   auto it = service_workers_paused_for_on_loaded_message_.find(extension_id); | 
 |   if (it != service_workers_paused_for_on_loaded_message_.end()) { | 
 |     blink::WebServiceWorkerContextProxy* context_proxy = | 
 |         it->second->context_proxy; | 
 |     context_proxy->ResumeEvaluation(); | 
 |     service_workers_paused_for_on_loaded_message_.erase(it); | 
 |   } | 
 | } | 
 |  | 
 | }  // namespace extensions |