// Copyright 2021 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "extensions/browser/content_script_tracker.h" #include #include "base/containers/contains.h" #include "base/logging.h" #include "base/memory/ptr_util.h" #include "base/memory/raw_ptr.h" #include "base/memory/raw_ref.h" #include "base/ranges/algorithm.h" #include "base/trace_event/typed_macros.h" #include "components/guest_view/browser/guest_view_base.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/global_routing_id.h" #include "content/public/browser/navigation_handle.h" #include "content/public/browser/render_frame_host.h" #include "content/public/browser/render_process_host.h" #include "content/public/browser/web_contents.h" #include "extensions/browser/browser_frame_context_data.h" #include "extensions/browser/extension_registry.h" #include "extensions/browser/extension_system.h" #include "extensions/browser/guest_view/web_view/web_view_content_script_manager.h" #include "extensions/browser/url_loader_factory_manager.h" #include "extensions/browser/user_script_manager.h" #include "extensions/common/constants.h" #include "extensions/common/content_script_injection_url_getter.h" #include "extensions/common/context_data.h" #include "extensions/common/extension.h" #include "extensions/common/manifest_handlers/content_scripts_handler.h" #include "extensions/common/permissions/permissions_data.h" #include "extensions/common/trace_util.h" #include "extensions/common/user_script.h" #include "services/metrics/public/cpp/metrics_utils.h" #include "services/metrics/public/cpp/ukm_builders.h" using perfetto::protos::pbzero::ChromeTrackEvent; namespace extensions { namespace { // Helper for lazily attaching ExtensionIdSet to a RenderProcessHost. Used to // track the set of extensions which have injected a JS script into a // RenderProcessHost. // // We track script injection per-RenderProcessHost: // 1. This matches the real security boundary that Site Isolation uses (the // boundary of OS processes) and follows the precedent of // content::ChildProcessSecurityPolicy. // 2. This robustly handles initial empty documents (see the *InitialEmptyDoc* // tests in //content_script_tracker_browsertest.cc) and isn't impacted // by ReadyToCommit races associated with DocumentUserData. // For more information see: // https://docs.google.com/document/d/1MFprp2ss2r9RNamJ7Jxva1bvRZvec3rzGceDGoJ6vW0/edit# class RenderProcessHostUserData : public base::SupportsUserData::Data { public: static const RenderProcessHostUserData* Get( const content::RenderProcessHost& process) { DCHECK_CURRENTLY_ON(content::BrowserThread::UI); return static_cast( process.GetUserData(kUserDataKey)); } static RenderProcessHostUserData& GetOrCreate( content::RenderProcessHost& process) { DCHECK_CURRENTLY_ON(content::BrowserThread::UI); auto* self = static_cast( process.GetUserData(kUserDataKey)); if (!self) { // Create a new RenderProcessHostUserData if needed. The ownership is // passed to the `process` (i.e. the new RenderProcessHostUserData will be // destroyed at the same time as the `process` - this is why we don't need // to purge or destroy the set from within ContentScriptTracker). auto owned_self = base::WrapUnique(new RenderProcessHostUserData(process)); self = owned_self.get(); process.SetUserData(kUserDataKey, std::move(owned_self)); } DCHECK(self); return *self; } // base::SupportsUserData::Data override: ~RenderProcessHostUserData() override { TRACE_EVENT_END("extensions", perfetto::Track::FromPointer(this), ChromeTrackEvent::kRenderProcessHost, *process_); } bool HasScript(ContentScriptTracker::ScriptType script_type, const ExtensionId& extension_id) const { return base::Contains(GetScripts(script_type), extension_id); } void AddScript(ContentScriptTracker::ScriptType script_type, const ExtensionId& extension_id) { TRACE_EVENT_INSTANT( "extensions", "ContentScriptTracker::RenderProcessHostUserData::AddScript", ChromeTrackEvent::kRenderProcessHost, *process_, ChromeTrackEvent::kChromeExtensionId, ExtensionIdForTracing(extension_id)); GetScripts(script_type).insert(extension_id); } void AddFrame(content::RenderFrameHost* frame) { frames_.insert(frame); } void RemoveFrame(content::RenderFrameHost* frame) { frames_.erase(frame); } const std::set& frames() const { return frames_; } const ExtensionIdSet& content_scripts() const { return content_scripts_; } const ExtensionIdSet& user_scripts() const { return user_scripts_; } private: explicit RenderProcessHostUserData(content::RenderProcessHost& process) : process_(process) { TRACE_EVENT_BEGIN("extensions", "ContentScriptTracker::RenderProcessHostUserData", perfetto::Track::FromPointer(this), ChromeTrackEvent::kRenderProcessHost, *process_); } const ExtensionIdSet& GetScripts( ContentScriptTracker::ScriptType script_type) const { switch (script_type) { case ContentScriptTracker::ScriptType::kContentScript: return content_scripts_; case ContentScriptTracker::ScriptType::kUserScript: return user_scripts_; } } ExtensionIdSet& GetScripts(ContentScriptTracker::ScriptType script_type) { return const_cast( const_cast(this)->GetScripts( script_type)); } static const char* kUserDataKey; // The sets of extension ids that have *ever* injected a content script or // user script into this particular renderer process. This is the core data // maintained by the ContentScriptTracker. ExtensionIdSet content_scripts_; ExtensionIdSet user_scripts_; // Set of frames that are *currently* hosted in this particular renderer // process. This is mostly used just to get GetLastCommittedURL of these // frames so that when a new extension is loaded, then ContentScriptTracker // can know where content scripts may be injected. std::set frames_; // Only used for tracing. const raw_ref process_; }; const char* RenderProcessHostUserData::kUserDataKey = "ContentScriptTracker's data"; // This function approximates ScriptContext::GetEffectiveDocumentURLForInjection // from the renderer side. GURL GetEffectiveDocumentURL( content::RenderFrameHost* frame, const GURL& document_url, MatchOriginAsFallbackBehavior match_origin_as_fallback) { // This is a simplification to avoid calling // `BrowserFrameContextData::CanAccess` which is unable to replicate all of // WebSecurityOrigin::CanAccess checks (e.g. universal access or file // exceptions tracked on the renderer side). This is okay, because our only // caller (DoesContentScriptMatch()) expects false positives. constexpr bool kAllowInaccessibleParents = true; return ContentScriptInjectionUrlGetter::Get( BrowserFrameContextData(frame), document_url, match_origin_as_fallback, kAllowInaccessibleParents); } // If `user_script` will inject JavaScript content script into the target of // `navigation`, then DoesContentScriptMatch returns true. Otherwise it may // return either true or false. Note that this function ignores CSS content // scripts. // // This function approximates a subset of checks from // UserScriptSet::GetInjectionForScript (which runs in the renderer process). // Unlike the renderer version, the code below doesn't consider ability to // create an injection host, nor the results of // ScriptInjector::CanExecuteOnFrame, nor the path of `url_patterns`. // Additionally the `effective_url` calculations are also only an approximation. // This is okay, because the top-level doc comment for ContentScriptTracker // documents that false positives are expected and why they are okay. bool DoesContentScriptMatch(const UserScript& user_script, content::RenderFrameHost* frame, const GURL& url) { content::RenderProcessHost& process = *frame->GetProcess(); const ExtensionId& extension_id = user_script.extension_id(); // ContentScriptTracker only needs to track Javascript content scripts (e.g. // doesn't track CSS-only injections). if (user_script.js_scripts().empty()) { TRACE_EVENT_INSTANT( "extensions", "ContentScriptTracker/DoesContentScriptMatch=false(non-js)", ChromeTrackEvent::kRenderProcessHost, process, ChromeTrackEvent::kChromeExtensionId, ExtensionIdForTracing(extension_id)); return false; } GURL effective_url = GetEffectiveDocumentURL( frame, url, user_script.match_origin_as_fallback()); if (user_script.url_patterns().MatchesSecurityOrigin(effective_url)) { TRACE_EVENT_INSTANT("extensions", "ContentScriptTracker/DoesContentScriptMatch=true", ChromeTrackEvent::kRenderProcessHost, process, ChromeTrackEvent::kChromeExtensionId, ExtensionIdForTracing(extension_id)); return true; } else { TRACE_EVENT_INSTANT( "extensions", "ContentScriptTracker/DoesContentScriptMatch=false(mismatch)", ChromeTrackEvent::kRenderProcessHost, process, ChromeTrackEvent::kChromeExtensionId, ExtensionIdForTracing(extension_id)); return false; } } void HandleProgrammaticContentScriptInjection( base::PassKey pass_key, ContentScriptTracker::ScriptType script_type, content::RenderFrameHost* frame, const Extension& extension) { DCHECK_CURRENTLY_ON(content::BrowserThread::UI); // Store `extension.id()` in `process_data`. ContentScriptTracker never // removes entries from this set - once a renderer process gains an ability to // talk on behalf of a content script, it retains this ability forever. Note // that the `process_data` will be destroyed together with the // RenderProcessHost (see also a comment inside // RenderProcessHostUserData::GetOrCreate). auto& process_data = RenderProcessHostUserData::GetOrCreate(*frame->GetProcess()); process_data.AddScript(script_type, extension.id()); URLLoaderFactoryManager::WillProgrammaticallyInjectContentScript( pass_key, frame, extension); } bool DoContentScriptsMatch(const UserScriptList& content_script_list, content::RenderFrameHost* frame, const GURL& url) { return base::ranges::any_of( content_script_list.begin(), content_script_list.end(), [frame, &url](const std::unique_ptr& script) { return DoesContentScriptMatch(*script, frame, url); }); } // If `extension`'s manifest declares that it may inject JavaScript content // script into the `frame` / `url`, then DoContentScriptsMatch returns true. // Otherwise it may return either true or false. // // Note that the `url` might be either 1) the last committed URL of `frame` or // 2) the target of a ReadyToCommit navigation in `frame`. // // Note that this method ignores CSS content scripts. bool DoContentScriptsMatch(const Extension& extension, content::RenderFrameHost* frame, const GURL& url) { TRACE_EVENT("extensions", "ContentScriptTracker/DoContentScriptsMatch", ChromeTrackEvent::kRenderProcessHost, *frame->GetProcess(), ChromeTrackEvent::kChromeExtensionId, ExtensionIdForTracing(extension.id())); content::RenderProcessHost& process = *frame->GetProcess(); auto* guest = guest_view::GuestViewBase::FromRenderFrameHost(frame); if (guest) { // Return true if `extension` is an owner of `guest` and it registered // content scripts using the `webview.addContentScripts` API. GURL owner_site_url = guest->GetOwnerSiteURL(); if (owner_site_url.SchemeIs(kExtensionScheme) && owner_site_url.host_piece() == extension.id()) { WebViewContentScriptManager* script_manager = WebViewContentScriptManager::Get(frame->GetBrowserContext()); int embedder_process_id = guest->owner_rfh()->GetProcess()->GetID(); std::set script_ids = script_manager->GetContentScriptIDSet( embedder_process_id, guest->view_instance_id()); // Note - more granular checks (e.g. against URL patterns) are desirable // for performance (to avoid creating unnecessary URLLoaderFactory via // URLLoaderFactoryManager), but not necessarily for security (because // there are anyway no OOPIFs inside the webView process - // https://crbug.com/614463). At the same time, more granular checks are // difficult to achieve, because the UserScript objects are not retained // (i.e. only UserScriptIDs are available) by WebViewContentScriptManager. if (!script_ids.empty()) { TRACE_EVENT_INSTANT( "extensions", "ContentScriptTracker/DoContentScriptsMatch=true(guest)", ChromeTrackEvent::kRenderProcessHost, process, ChromeTrackEvent::kChromeExtensionId, ExtensionIdForTracing(extension.id())); return true; } } } if (!guest || PermissionsData::CanExecuteScriptEverywhere( extension.id(), extension.location())) { // Return true if manifest-declared content scripts match. const UserScriptList& manifest_scripts = ContentScriptsInfo::GetContentScripts(&extension); if (DoContentScriptsMatch(manifest_scripts, frame, url)) { TRACE_EVENT_INSTANT( "extensions", "ContentScriptTracker/DoContentScriptsMatch=true(manifest)", ChromeTrackEvent::kRenderProcessHost, process, ChromeTrackEvent::kChromeExtensionId, ExtensionIdForTracing(extension.id())); return true; } // Return true if dynamic content scripts match. Note that `manager` can be // null for some unit tests which do not initialize the ExtensionSystem. UserScriptManager* manager = ExtensionSystem::Get(frame->GetProcess()->GetBrowserContext()) ->user_script_manager(); if (manager) { const UserScriptList& dynamic_scripts = manager->GetUserScriptLoaderForExtension(extension.id()) ->GetLoadedDynamicScripts(); if (DoContentScriptsMatch(dynamic_scripts, frame, url)) { TRACE_EVENT_INSTANT( "extensions", "ContentScriptTracker/DoContentScriptsMatch=true(dynamic)", ChromeTrackEvent::kRenderProcessHost, process, ChromeTrackEvent::kChromeExtensionId, ExtensionIdForTracing(extension.id())); return true; } } } // Otherwise, no content script from `extension` can run in `frame` at `url`. TRACE_EVENT_INSTANT("extensions", "ContentScriptTracker/DoContentScriptsMatch=false", ChromeTrackEvent::kRenderProcessHost, process, ChromeTrackEvent::kChromeExtensionId, ExtensionIdForTracing(extension.id())); return false; } std::vector GetExtensionsInjectingContentScripts( content::NavigationHandle* navigation) { content::RenderFrameHost* frame = navigation->GetRenderFrameHost(); const GURL& url = navigation->GetURL(); std::vector extensions_injecting_content_scripts; const ExtensionRegistry* registry = ExtensionRegistry::Get(frame->GetProcess()->GetBrowserContext()); DCHECK(registry); // This method shouldn't be called during shutdown. for (const auto& it : registry->enabled_extensions()) { const Extension& extension = *it; if (!DoContentScriptsMatch(extension, frame, url)) { continue; } extensions_injecting_content_scripts.push_back(&extension); } return extensions_injecting_content_scripts; } void RecordUkm(content::NavigationHandle* navigation, int extensions_injecting_content_script_count) { using PermissionID = extensions::mojom::APIPermissionID; const ExtensionSet& enabled_extensions = ExtensionRegistry::Get( navigation->GetRenderFrameHost()->GetProcess()->GetBrowserContext()) ->enabled_extensions(); int enabled_extension_count = 0; int enabled_extension_count_has_host_permissions = 0; int web_request_permission_count = 0; int web_request_auth_provider_permission_count = 0; int web_request_blocking_permission_count = 0; int declarative_net_request_permission_count = 0; int declarative_net_request_feedback_permission_count = 0; int declarative_net_request_with_host_access_permission_count = 0; int declarative_web_request_permission_count = 0; for (const scoped_refptr& extension : enabled_extensions) { if (!extension->is_extension()) { continue; } // Ignore component extensions. if (Manifest::IsComponentLocation(extension->location())) { continue; } enabled_extension_count++; const PermissionsData* permissions = extension->permissions_data(); if (!permissions) { continue; } if (!permissions->HasHostPermission(navigation->GetURL())) { continue; } enabled_extension_count_has_host_permissions++; if (permissions->HasAPIPermission(PermissionID::kWebRequest)) { web_request_permission_count++; } if (permissions->HasAPIPermission(PermissionID::kWebRequestAuthProvider)) { web_request_auth_provider_permission_count++; } if (permissions->HasAPIPermission(PermissionID::kWebRequestBlocking)) { web_request_blocking_permission_count++; } if (permissions->HasAPIPermission(PermissionID::kDeclarativeNetRequest)) { declarative_net_request_permission_count++; } if (permissions->HasAPIPermission( PermissionID::kDeclarativeNetRequestFeedback)) { declarative_net_request_feedback_permission_count++; } if (permissions->HasAPIPermission( PermissionID::kDeclarativeNetRequestWithHostAccess)) { declarative_net_request_with_host_access_permission_count++; } if (permissions->HasAPIPermission(PermissionID::kDeclarativeWebRequest)) { declarative_web_request_permission_count++; } } const double kBucketSpacing = 2; ukm::builders::Extensions_OnNavigation(navigation->GetNextPageUkmSourceId()) .SetEnabledExtensionCount( ukm::GetExponentialBucketMin(enabled_extension_count, kBucketSpacing)) .SetEnabledExtensionCount_InjectContentScript( ukm::GetExponentialBucketMin( extensions_injecting_content_script_count, kBucketSpacing)) .SetEnabledExtensionCount_HaveHostPermissions( ukm::GetExponentialBucketMin( enabled_extension_count_has_host_permissions, kBucketSpacing)) .SetWebRequestPermissionCount(ukm::GetExponentialBucketMin( web_request_permission_count, kBucketSpacing)) .SetWebRequestAuthProviderPermissionCount(ukm::GetExponentialBucketMin( web_request_auth_provider_permission_count, kBucketSpacing)) .SetWebRequestBlockingPermissionCount(ukm::GetExponentialBucketMin( web_request_blocking_permission_count, kBucketSpacing)) .SetDeclarativeNetRequestPermissionCount(ukm::GetExponentialBucketMin( declarative_net_request_permission_count, kBucketSpacing)) .SetDeclarativeNetRequestFeedbackPermissionCount( ukm::GetExponentialBucketMin( declarative_net_request_feedback_permission_count, kBucketSpacing)) .SetDeclarativeNetRequestWithHostAccessPermissionCount( ukm::GetExponentialBucketMin( declarative_net_request_with_host_access_permission_count, kBucketSpacing)) .SetDeclarativeWebRequestPermissionCount(ukm::GetExponentialBucketMin( declarative_web_request_permission_count, kBucketSpacing)) .Record(ukm::UkmRecorder::Get()); } const Extension* FindExtensionByHostId(content::BrowserContext* browser_context, const mojom::HostID& host_id) { DCHECK_CURRENTLY_ON(content::BrowserThread::UI); switch (host_id.type) { case mojom::HostID::HostType::kWebUi: // ContentScriptTracker only tracks extensions. return nullptr; case mojom::HostID::HostType::kExtensions: break; } const ExtensionRegistry* registry = ExtensionRegistry::Get(browser_context); DCHECK(registry); // WillExecuteCode and WillUpdateContentScriptsInRenderer // shouldn't happen during shutdown. const Extension* extension = registry->enabled_extensions().GetByID(host_id.id); return extension; } void StoreExtensionsInjectingContentScripts( const std::vector& extensions_injecting_content_scripts, content::RenderProcessHost& process) { // Store `extensions_injecting_content_scripts` in `process_data`. // ContentScriptTracker never removes entries from this set - once a renderer // process gains an ability to talk on behalf of a content script, it retains // this ability forever. Note that the `process_data` will be destroyed // together with the RenderProcessHost (see also a comment inside // RenderProcessHostUserData::GetOrCreate). auto& process_data = RenderProcessHostUserData::GetOrCreate(process); for (const Extension* extension : extensions_injecting_content_scripts) { process_data.AddScript(ContentScriptTracker::ScriptType::kContentScript, extension->id()); } } bool DidProcessRunScriptFromExtension( ContentScriptTracker::ScriptType script_type, const content::RenderProcessHost& process, const ExtensionId& extension_id) { DCHECK_CURRENTLY_ON(content::BrowserThread::UI); DCHECK(!extension_id.empty()); // Check if we've been notified about the content script injection via // ReadyToCommitNavigation or WillExecuteCode methods. const auto* process_data = RenderProcessHostUserData::Get(process); if (!process_data) { return false; } return process_data->HasScript(script_type, extension_id); } } // namespace // static ExtensionIdSet ContentScriptTracker::GetExtensionsThatRanContentScriptsInProcess( const content::RenderProcessHost& process) { DCHECK_CURRENTLY_ON(content::BrowserThread::UI); const auto* process_data = RenderProcessHostUserData::Get(process); if (!process_data) { return {}; } return process_data->content_scripts(); } // static bool ContentScriptTracker::DidProcessRunContentScriptFromExtension( const content::RenderProcessHost& process, const ExtensionId& extension_id) { return DidProcessRunScriptFromExtension(ScriptType::kContentScript, process, extension_id); } // static bool ContentScriptTracker::DidProcessRunUserScriptFromExtension( const content::RenderProcessHost& process, const ExtensionId& extension_id) { return DidProcessRunScriptFromExtension(ScriptType::kUserScript, process, extension_id); } // static void ContentScriptTracker::ReadyToCommitNavigation( base::PassKey pass_key, content::NavigationHandle* navigation) { DCHECK_CURRENTLY_ON(content::BrowserThread::UI); content::RenderProcessHost& process = *navigation->GetRenderFrameHost()->GetProcess(); TRACE_EVENT("extensions", "ContentScriptTracker::ReadyToCommitNavigation", ChromeTrackEvent::kRenderProcessHost, process); // Need to call StoreExtensionsInjectingContentScripts at // ReadyToCommitNavigation time to deal with a (hypothetical, not confirmed by // tests) race condition where Browser process sends Commit IPC and then // immediately disables the extension. In this scenario, the renderer may run // some content scripts, even though at DidCommit time the Browser will see // that the extension has been disabled. std::vector extensions_injecting_content_scripts = GetExtensionsInjectingContentScripts(navigation); StoreExtensionsInjectingContentScripts(extensions_injecting_content_scripts, process); // Notify URLLoaderFactoryManager - this needs to happen at // ReadyToCommitNavigation time (i.e. before constructing a URLLoaderFactory // that will be sent to the Renderer in a Commit IPC). URLLoaderFactoryManager::WillInjectContentScriptsWhenNavigationCommits( base::PassKey(), navigation, extensions_injecting_content_scripts); } // static void ContentScriptTracker::DidFinishNavigation( base::PassKey pass_key, content::NavigationHandle* navigation) { DCHECK_CURRENTLY_ON(content::BrowserThread::UI); // Only consider cross-document navigations that actually commit. (Documents // associated with same-document navigations should have already been // processed by an earlier DidFinishNavigation. Navigations that don't // commit/load won't inject content scripts. Content script injections are // primarily driven by URL matching and therefore failed navigations may still // end up injecting content scripts into the error page. Pre-rendered pages // already ran content scripts at the initial navigation and don't need to // run them again on activation.) if (!navigation->HasCommitted() || navigation->IsSameDocument() || navigation->IsPrerenderedPageActivation()) { return; } content::RenderProcessHost& process = *navigation->GetRenderFrameHost()->GetProcess(); TRACE_EVENT("extensions", "ContentScriptTracker::DidFinishNavigation", ChromeTrackEvent::kRenderProcessHost, process); // Calling StoreExtensionsInjectingContentScripts in response to DidCommit IPC // is required for correct handling of the race condition from // https://crbug.com/1312125. std::vector extensions_injecting_content_scripts = GetExtensionsInjectingContentScripts(navigation); StoreExtensionsInjectingContentScripts(extensions_injecting_content_scripts, process); RecordUkm(navigation, extensions_injecting_content_scripts.size()); } // static void ContentScriptTracker::RenderFrameCreated( base::PassKey pass_key, content::RenderFrameHost* frame) { TRACE_EVENT("extensions", "ContentScriptTracker::RenderFrameCreated", ChromeTrackEvent::kRenderProcessHost, *frame->GetProcess()); auto& process_data = RenderProcessHostUserData::GetOrCreate(*frame->GetProcess()); process_data.AddFrame(frame); } // static void ContentScriptTracker::RenderFrameDeleted( base::PassKey pass_key, content::RenderFrameHost* frame) { TRACE_EVENT("extensions", "ContentScriptTracker::RenderFrameDeleted", ChromeTrackEvent::kRenderProcessHost, *frame->GetProcess()); auto& process_data = RenderProcessHostUserData::GetOrCreate(*frame->GetProcess()); process_data.RemoveFrame(frame); } // static void ContentScriptTracker::WillExecuteCode( base::PassKey pass_key, ScriptType script_type, content::RenderFrameHost* frame, const mojom::HostID& host_id) { DCHECK_CURRENTLY_ON(content::BrowserThread::UI); content::RenderProcessHost& process = *frame->GetProcess(); TRACE_EVENT("extensions", "ContentScriptTracker::WillExecuteCode/1", ChromeTrackEvent::kRenderProcessHost, process, ChromeTrackEvent::kChromeExtensionId, ExtensionIdForTracing(host_id.id)); const Extension* extension = FindExtensionByHostId(process.GetBrowserContext(), host_id); if (!extension) { return; } HandleProgrammaticContentScriptInjection(PassKey(), script_type, frame, *extension); } // static void ContentScriptTracker::WillExecuteCode( base::PassKey pass_key, content::RenderFrameHost* frame, const Extension& extension) { DCHECK_CURRENTLY_ON(content::BrowserThread::UI); TRACE_EVENT("extensions", "ContentScriptTracker::WillExecuteCode/2", ChromeTrackEvent::kRenderProcessHost, *frame->GetProcess(), ChromeTrackEvent::kChromeExtensionId, ExtensionIdForTracing(extension.id())); // Declarative content scripts are only ever of a kContentScript type and // never handle user scripts. HandleProgrammaticContentScriptInjection( PassKey(), ScriptType::kContentScript, frame, extension); } // static void ContentScriptTracker::WillUpdateContentScriptsInRenderer( base::PassKey pass_key, const mojom::HostID& host_id, content::RenderProcessHost& process) { DCHECK_CURRENTLY_ON(content::BrowserThread::UI); TRACE_EVENT( "extensions", "ContentScriptTracker::WillUpdateContentScriptsInRenderer", ChromeTrackEvent::kRenderProcessHost, process, ChromeTrackEvent::kChromeExtensionId, ExtensionIdForTracing(host_id.id)); const Extension* extension = FindExtensionByHostId(process.GetBrowserContext(), host_id); if (!extension) { return; } auto& process_data = RenderProcessHostUserData::GetOrCreate(process); const std::set& frames_in_process = process_data.frames(); bool any_frame_matches_content_scripts = base::ranges::any_of( frames_in_process, [extension](content::RenderFrameHost* frame) { return DoContentScriptsMatch(*extension, frame, frame->GetLastCommittedURL()); }); if (any_frame_matches_content_scripts) { process_data.AddScript(ScriptType::kContentScript, extension->id()); } else { TRACE_EVENT_INSTANT( "extensions", "ContentScriptTracker::WillUpdateContentScriptsInRenderer - no matches", ChromeTrackEvent::kRenderProcessHost, process, ChromeTrackEvent::kChromeExtensionId, ExtensionIdForTracing(host_id.id)); } } // static bool ContentScriptTracker::DoContentScriptsMatchForTesting( const Extension& extension, content::RenderFrameHost* frame, const GURL& url) { return DoContentScriptsMatch(extension, frame, url); } } // namespace extensions