| // Copyright 2012 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef EXTENSIONS_BROWSER_API_WEB_REQUEST_WEB_REQUEST_API_H_ |
| #define EXTENSIONS_BROWSER_API_WEB_REQUEST_WEB_REQUEST_API_H_ |
| |
| #include <stdint.h> |
| |
| #include <list> |
| #include <map> |
| #include <optional> |
| #include <set> |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| #include "base/containers/unique_ptr_adapters.h" |
| #include "base/feature_list.h" |
| #include "base/functional/callback.h" |
| #include "base/gtest_prod_util.h" |
| #include "base/memory/raw_ptr.h" |
| #include "base/memory/scoped_refptr.h" |
| #include "base/memory/weak_ptr.h" |
| #include "base/scoped_multi_source_observation.h" |
| #include "base/scoped_observation.h" |
| #include "base/strings/string_util.h" |
| #include "base/task/sequenced_task_runner.h" |
| #include "base/time/time.h" |
| #include "base/values.h" |
| #include "content/public/browser/content_browser_client.h" |
| #include "content/public/browser/global_request_id.h" |
| #include "content/public/browser/service_worker_context.h" |
| #include "content/public/browser/service_worker_context_observer.h" |
| #include "extensions/browser/api/declarative_webrequest/request_stage.h" |
| #include "extensions/browser/api/web_request/extension_web_request_event_router.h" |
| #include "extensions/browser/api/web_request/web_request_permissions.h" |
| #include "extensions/browser/browser_context_keyed_api_factory.h" |
| #include "extensions/browser/event_router.h" |
| #include "extensions/browser/extension_api_frame_id_map.h" |
| #include "extensions/browser/extension_function.h" |
| #include "extensions/browser/extension_registry_observer.h" |
| #include "extensions/browser/service_worker/service_worker_task_queue.h" |
| #include "extensions/common/extension_id.h" |
| #include "ipc/ipc_sender.h" |
| #include "net/base/auth.h" |
| #include "net/base/completion_once_callback.h" |
| #include "net/http/http_request_headers.h" |
| #include "services/metrics/public/cpp/ukm_source_id.h" |
| #include "services/network/public/mojom/url_loader_factory.mojom.h" |
| #include "services/network/public/mojom/websocket.mojom.h" |
| |
| class GURL; |
| |
| namespace content { |
| class BrowserContext; |
| class RenderFrameHost; |
| class ServiceWorkerContext; |
| } // namespace content |
| |
| namespace net { |
| class AuthCredentials; |
| class HttpResponseHeaders; |
| class SiteForCookies; |
| } // namespace net |
| |
| namespace network { |
| class URLLoaderFactoryBuilder; |
| } // namespace network |
| |
| namespace extensions { |
| class WebViewGuest; |
| |
| // Support class for the WebRequest API. Lives on the UI thread. Most of the |
| // work is done by ExtensionWebRequestEventRouter below. This class observes |
| // extensions::EventRouter to deal with event listeners. There is one instance |
| // per BrowserContext which is shared with incognito. |
| class WebRequestAPI : public BrowserContextKeyedAPI, |
| public EventRouter::Observer, |
| public ExtensionRegistryObserver, |
| public ServiceWorkerTaskQueue::RegistrationObserver, |
| public content::ServiceWorkerContextObserverSynchronous { |
| public: |
| // A callback used to asynchronously respond to an intercepted authentication |
| // request. If `should_cancel` is true the request will be cancelled. |
| // Otherwise any supplied `credentials` will be used. If no credentials are |
| // supplied, default browser behavior will follow (e.g. UI prompt for login). |
| using AuthRequestCallback = base::OnceCallback<void( |
| const std::optional<net::AuthCredentials>& credentials, |
| bool should_cancel)>; |
| |
| // An interface which is held by ProxySet defined below. |
| class Proxy { |
| public: |
| virtual ~Proxy() = default; |
| |
| // Asks the Proxy to handle an auth request on behalf of one of its known |
| // in-progress network requests. If the request will *not* be handled by |
| // the proxy, `callback` should be invoked with |std::nullopt|. |
| virtual void HandleAuthRequest( |
| const net::AuthChallengeInfo& auth_info, |
| scoped_refptr<net::HttpResponseHeaders> response_headers, |
| int32_t request_id, |
| AuthRequestCallback callback); |
| |
| // Called when an extension that can execute declarativeNetRequest actions |
| // is unloaded, so orphaned DNR actions on current requests can be cleaned |
| // up. |
| virtual void OnDNRExtensionUnloaded(const Extension* extension) = 0; |
| }; |
| |
| // A ProxySet is a set of proxies used by WebRequestAPI: It holds Proxy |
| // instances, and removes all proxies when it is destroyed. |
| class ProxySet { |
| public: |
| ProxySet(); |
| |
| ProxySet(const ProxySet&) = delete; |
| ProxySet& operator=(const ProxySet&) = delete; |
| |
| ~ProxySet(); |
| |
| // Add a Proxy. |
| void AddProxy(std::unique_ptr<Proxy> proxy); |
| // Remove a Proxy. The removed proxy is deleted upon this call. |
| void RemoveProxy(Proxy* proxy); |
| |
| // Associates `proxy` with `id`. `proxy` must already be registered within |
| // this ProxySet. |
| // |
| // Each Proxy may be responsible for multiple requests, but any given |
| // request identified by `id` must be associated with only a single proxy. |
| void AssociateProxyWithRequestId(Proxy* proxy, |
| const content::GlobalRequestID& id); |
| |
| // Disassociates `proxy` with `id`. `proxy` must already be registered |
| // within this ProxySet. |
| void DisassociateProxyWithRequestId(Proxy* proxy, |
| const content::GlobalRequestID& id); |
| |
| Proxy* GetProxyFromRequestId(const content::GlobalRequestID& id); |
| |
| void MaybeProxyAuthRequest( |
| const net::AuthChallengeInfo& auth_info, |
| scoped_refptr<net::HttpResponseHeaders> response_headers, |
| const content::GlobalRequestID& request_id, |
| AuthRequestCallback callback); |
| |
| void OnDNRExtensionUnloaded(const Extension* extension); |
| |
| private: |
| // Although these members are initialized on the UI thread, we expect at |
| // least one memory barrier before actually calling Generate in the IO |
| // thread, so we don't protect them with a lock. |
| std::set<std::unique_ptr<Proxy>, base::UniquePtrComparator> proxies_; |
| |
| // Bi-directional mapping between request ID and Proxy for faster lookup. |
| std::map<content::GlobalRequestID, raw_ptr<Proxy, CtnExperimental>> |
| request_id_to_proxy_map_; |
| std::map<Proxy*, std::set<content::GlobalRequestID>> |
| proxy_to_request_id_map_; |
| }; |
| |
| class RequestIDGenerator { |
| public: |
| RequestIDGenerator(); |
| |
| RequestIDGenerator(const RequestIDGenerator&) = delete; |
| RequestIDGenerator& operator=(const RequestIDGenerator&) = delete; |
| |
| ~RequestIDGenerator(); |
| |
| // Generates a WebRequest ID. If the same (routing_id, |
| // network_service_request_id) pair is passed to this as was previously |
| // passed to SaveID(), the `request_id` passed to SaveID() will be returned. |
| int64_t Generate(int32_t routing_id, int32_t network_service_request_id); |
| |
| // This saves a WebRequest ID mapped to the (routing_id, |
| // network_service_request_id) pair. Clients must call Generate() with the |
| // same ID pair to retrieve the `request_id`, or else there may be a memory |
| // leak. |
| void SaveID(int32_t routing_id, |
| int32_t network_service_request_id, |
| uint64_t request_id); |
| |
| private: |
| int64_t id_ = 0; |
| std::map<std::pair<int32_t, int32_t>, uint64_t> saved_id_map_; |
| }; |
| |
| explicit WebRequestAPI(content::BrowserContext* context); |
| |
| WebRequestAPI(const WebRequestAPI&) = delete; |
| WebRequestAPI& operator=(const WebRequestAPI&) = delete; |
| |
| ~WebRequestAPI() override; |
| |
| // BrowserContextKeyedAPI support: |
| static BrowserContextKeyedAPIFactory<WebRequestAPI>* GetFactoryInstance(); |
| void Shutdown() override; |
| |
| class TestObserver { |
| public: |
| TestObserver(); |
| TestObserver(const TestObserver&) = delete; |
| TestObserver& operator=(const TestObserver&) = delete; |
| virtual ~TestObserver(); |
| |
| // Called when `ResetURLLoaderFactories()` has been performed. |
| virtual void OnDidResetURLLoaderFactories() {} |
| }; |
| |
| static void SetObserverForTest(TestObserver* observer); |
| |
| // EventRouter::Observer overrides: |
| void OnListenerRemoved(const EventListenerInfo& details) override; |
| |
| // If any WebRequest event listeners are currently active for this |
| // BrowserContext, |*factory_request| is swapped out for a new request which |
| // proxies through an internal URLLoaderFactory. This supports lifetime |
| // observation and control on behalf of the WebRequest API. |
| // `frame` and `render_process_id` are the frame and render process id in |
| // which the URLLoaderFactory will be used. `frame` can be nullptr for |
| // factories proxied for service worker. |
| // |
| // `navigation_response_task_runner` is a task runner that may be non-null for |
| // navigation requests and can be used to run navigation request blocking |
| // tasks. |
| // |
| // Returns `true` if the URLLoaderFactory will be proxied; `false` otherwise. |
| bool MaybeProxyURLLoaderFactory( |
| content::BrowserContext* browser_context, |
| content::RenderFrameHost* frame, |
| int render_process_id, |
| content::ContentBrowserClient::URLLoaderFactoryType type, |
| std::optional<int64_t> navigation_id, |
| ukm::SourceIdObj ukm_source_id, |
| network::URLLoaderFactoryBuilder& factory_builder, |
| mojo::PendingRemote<network::mojom::TrustedURLLoaderHeaderClient>* |
| header_client, |
| scoped_refptr<base::SequencedTaskRunner> navigation_response_task_runner, |
| const url::Origin& request_initiator = url::Origin()); |
| |
| // Any request which requires authentication to complete will be bounced |
| // through this method. |
| // |
| // If this returns `true`, `callback` will eventually be invoked on the UI |
| // thread. |
| bool MaybeProxyAuthRequest( |
| content::BrowserContext* browser_context, |
| const net::AuthChallengeInfo& auth_info, |
| scoped_refptr<net::HttpResponseHeaders> response_headers, |
| const content::GlobalRequestID& request_id, |
| bool is_request_for_navigation, |
| AuthRequestCallback callback, |
| WebViewGuest* web_view_guest); |
| |
| // Starts proxying the connection with `factory`. This function can be called |
| // only when MayHaveProxies() returns true. |
| void ProxyWebSocket( |
| content::RenderFrameHost* frame, |
| content::ContentBrowserClient::WebSocketFactory factory, |
| const GURL& url, |
| const net::SiteForCookies& site_for_cookies, |
| const std::optional<std::string>& user_agent, |
| mojo::PendingRemote<network::mojom::WebSocketHandshakeClient> |
| handshake_client); |
| |
| // Starts proxying WebTransport handshake. |
| void ProxyWebTransport( |
| content::RenderProcessHost& render_process_host, |
| int frame_routing_id, |
| const GURL& url, |
| const url::Origin& initiator_origin, |
| mojo::PendingRemote<network::mojom::WebTransportHandshakeClient> |
| handshake_client, |
| content::ContentBrowserClient::WillCreateWebTransportCallback callback); |
| |
| void ForceProxyForTesting(); |
| |
| // Indicates whether or not the WebRequestAPI may have one or more proxies |
| // installed to support the API. |
| bool MayHaveProxies() const; |
| |
| // Indicates whether the WebRequestAPI is available to a RenderFrameHost |
| // that embeds a WebView instance. |
| bool IsAvailableToWebViewEmbedderFrame( |
| content::RenderFrameHost* render_frame_host) const; |
| |
| bool HasExtraHeadersListenerForTesting(); |
| |
| private: |
| // These values are persisted to logs. Entries should not be renumbered and |
| // numeric values should never be reused. |
| // |
| // LINT.IfChange(ProxyDecision) |
| enum class ProxyDecision { |
| // Proxy will not be used. |
| kWillNotProxy = 0, |
| // Proxy will be used for Extensions installed with webRequest API uses. |
| kWillProxyForExtension = 1, |
| // Proxy will be used for WebUI to use the webRequestInternal API. |
| kWillProxyForWebUI = 2, |
| // Proxy will be used for the <webview> permitted to use webRequest API. |
| kWillProxyForEmbedderWebView = 3, |
| // Proxy will be used for the telemetry service. |
| kWillProxyForTelemetry = 4, |
| |
| kMaxValue = kWillProxyForTelemetry, |
| }; |
| // LINT.ThenChange(/tools/metrics/histograms/metadata/extensions/enums.xml:WebRequestProxyDecision) |
| |
| friend class BrowserContextKeyedAPIFactory<WebRequestAPI>; |
| |
| // BrowserContextKeyedAPI support: |
| static const char* service_name() { return "WebRequestAPI"; } |
| static const bool kServiceRedirectedInIncognito = true; |
| static const bool kServiceIsNULLWhileTesting = true; |
| |
| // Checks if |MayHaveProxies()| has changed from false to true, and resets |
| // URLLoaderFactories if so. |
| void UpdateMayHaveProxies(); |
| |
| void ResetURLLoaderFactories(); |
| |
| // ExtensionRegistryObserver implementation. |
| void OnExtensionLoaded(content::BrowserContext* browser_context, |
| const Extension* extension) override; |
| void OnExtensionUnloaded(content::BrowserContext* browser_context, |
| const Extension* extension, |
| UnloadedExtensionReason reason) override; |
| |
| // content::ServiceWorkerContextObserverSynchronous: |
| // Listens for the moment right before the URLLoaderFactory that will |
| // be used to fetch the worker's script is constructed. |
| void OnWillCreateURLLoaderFactory(const GURL& scope) override; |
| |
| // ServiceWorkerTaskQueue::RegistrationObserver: |
| void OnWillRegisterServiceWorker( |
| content::ServiceWorkerContext* context) override; |
| void OnAllRegistrationsStored() override; |
| |
| // This a proxy API for the tasks that are posted. It is either called |
| // when the task is run and forwards to the corresponding member function |
| // in ExtensionWebRequestEventRouter, or not, if the owning BrowserContext |
| // goes away or the WeakPtr instance bound in the callback is invalidated. |
| void UpdateActiveListener( |
| void* browser_context_id, |
| WebRequestEventRouter::ListenerUpdateType update_type, |
| const ExtensionId& extension_id, |
| const std::string& sub_event_name, |
| int worker_thread_id, |
| int64_t service_worker_version_id); |
| |
| // This a proxy API for the tasks that are posted. It is either called |
| // when the task is run and forwards to the corresponding member function |
| // in ExtensionWebRequestEventRouter, or not, if the owning BrowserContext |
| // goes away or the WeakPtr instance bound in the callback is invalidated. |
| void RemoveLazyListener(content::BrowserContext* browser_context, |
| const ExtensionId& extension_id, |
| const std::string& sub_event_name); |
| |
| // Internal implemntation of MaybeProxyURLLoaderFactory that returns a |
| // detailed reason, ProxyDecision, to tell why the proxy is used. |
| ProxyDecision MaybeProxyURLLoaderFactoryInternal( |
| content::BrowserContext* browser_context, |
| content::RenderFrameHost* frame, |
| int render_process_id, |
| content::ContentBrowserClient::URLLoaderFactoryType type, |
| std::optional<int64_t> navigation_id, |
| ukm::SourceIdObj ukm_source_id, |
| network::URLLoaderFactoryBuilder& factory_builder, |
| mojo::PendingRemote<network::mojom::TrustedURLLoaderHeaderClient>* |
| header_client, |
| scoped_refptr<base::SequencedTaskRunner> navigation_response_task_runner, |
| const url::Origin& request_initiator = url::Origin()); |
| |
| // Counts of active extensions for this BrowserContext that use kWebRequest*, |
| // kDeclarative{Web|Net}Request*, or kWebView permissions. |
| int web_request_extension_count_ = 0; |
| int declarative_request_extension_count_ = 0; |
| int web_view_extension_count_ = 0; |
| |
| const raw_ptr<content::BrowserContext, DanglingUntriaged> browser_context_; |
| |
| RequestIDGenerator request_id_generator_; |
| std::unique_ptr<ProxySet> proxies_; |
| |
| // Stores the last result of |MayHaveProxies()|, so it can be used in |
| // |UpdateMayHaveProxies()|. |
| bool may_have_proxies_; |
| |
| // Stores whether there's any active worker registration for extensions, |
| // which means their URLLoaderFactory have already been constructed. |
| // Used to decide whether |ResetURLLoaderFactories()| can be called |
| // immediately, or if it needs to be deferred to when all registrations |
| // are stored. |
| bool has_pending_worker_registrations_ = false; |
| |
| // Stores whether the execution of |ResetURLLoaderFactories()| has been |
| // deferred to when service workers registrations are stored. |
| bool deferred_reset_url_loader_factories_ = false; |
| |
| base::ScopedMultiSourceObservation< |
| content::ServiceWorkerContext, |
| content::ServiceWorkerContextObserverSynchronous> |
| service_worker_context_observation_{this}; |
| |
| base::ScopedObservation<ServiceWorkerTaskQueue, |
| ServiceWorkerTaskQueue::RegistrationObserver> |
| service_worker_task_queue_observation_{this}; |
| |
| base::WeakPtrFactory<WebRequestAPI> weak_factory_{this}; |
| }; |
| |
| template <> |
| void BrowserContextKeyedAPIFactory<WebRequestAPI>::DeclareFactoryDependencies(); |
| |
| class WebRequestInternalFunction : public ExtensionFunction { |
| public: |
| WebRequestInternalFunction() = default; |
| |
| protected: |
| ~WebRequestInternalFunction() override = default; |
| |
| const ExtensionId& extension_id_safe() const { |
| return extension() ? extension_id() : base::EmptyString(); |
| } |
| }; |
| |
| class WebRequestInternalAddEventListenerFunction |
| : public WebRequestInternalFunction { |
| public: |
| DECLARE_EXTENSION_FUNCTION("webRequestInternal.addEventListener", |
| WEBREQUESTINTERNAL_ADDEVENTLISTENER) |
| |
| protected: |
| ~WebRequestInternalAddEventListenerFunction() override = default; |
| |
| // ExtensionFunction: |
| ResponseAction Run() override; |
| }; |
| |
| class WebRequestInternalEventHandledFunction |
| : public WebRequestInternalFunction { |
| public: |
| DECLARE_EXTENSION_FUNCTION("webRequestInternal.eventHandled", |
| WEBREQUESTINTERNAL_EVENTHANDLED) |
| |
| protected: |
| ~WebRequestInternalEventHandledFunction() override = default; |
| |
| private: |
| // Unblocks the network request. Use this function when handling incorrect |
| // requests from the extension that cannot be detected by the schema |
| // validator. |
| void OnError(const std::string& event_name, |
| const std::string& sub_event_name, |
| uint64_t request_id, |
| int render_process_id, |
| int web_view_instance_id, |
| std::unique_ptr<WebRequestEventRouter::EventResponse> response); |
| |
| // ExtensionFunction: |
| ResponseAction Run() override; |
| }; |
| |
| class WebRequestHandlerBehaviorChangedFunction |
| : public WebRequestInternalFunction { |
| public: |
| DECLARE_EXTENSION_FUNCTION("webRequest.handlerBehaviorChanged", |
| WEBREQUEST_HANDLERBEHAVIORCHANGED) |
| |
| protected: |
| ~WebRequestHandlerBehaviorChangedFunction() override = default; |
| |
| // ExtensionFunction: |
| void GetQuotaLimitHeuristics( |
| extensions::QuotaLimitHeuristics* heuristics) const override; |
| // Handle quota exceeded gracefully: Only warn the user but still execute the |
| // function. |
| void OnQuotaExceeded(std::string error) override; |
| ResponseAction Run() override; |
| }; |
| |
| } // namespace extensions |
| |
| #endif // EXTENSIONS_BROWSER_API_WEB_REQUEST_WEB_REQUEST_API_H_ |