#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/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/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 "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/buildflags/buildflags.h"
#include "extensions/common/extension_id.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"
static_assert(BUILDFLAG(ENABLE_EXTENSIONS_CORE));
class GURL;
namespace content {
class BrowserContext;
class RenderFrameHost;
}
namespace net {
class AuthCredentials;
class HttpResponseHeaders;
class SiteForCookies;
}
namespace network {
class URLLoaderFactoryBuilder;
}
namespace extensions {
class WebViewGuest;
class WebRequestAPI : public BrowserContextKeyedAPI,
public EventRouter::Observer,
public ExtensionRegistryObserver {
public:
using AuthRequestCallback = base::OnceCallback<void(
const std::optional<net::AuthCredentials>& credentials,
bool should_cancel)>;
class Proxy {
public:
virtual ~Proxy() = default;
virtual void HandleAuthRequest(
const net::AuthChallengeInfo& auth_info,
scoped_refptr<net::HttpResponseHeaders> response_headers,
int32_t request_id,
AuthRequestCallback callback);
virtual void OnDNRExtensionUnloaded(const Extension* extension) = 0;
};
class ProxySet {
public:
ProxySet();
ProxySet(const ProxySet&) = delete;
ProxySet& operator=(const ProxySet&) = delete;
~ProxySet();
void AddProxy(std::unique_ptr<Proxy> proxy);
void RemoveProxy(Proxy* proxy);
void AssociateProxyWithRequestId(Proxy* proxy,
const content::GlobalRequestID& id);
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:
std::set<std::unique_ptr<Proxy>, base::UniquePtrComparator> proxies_;
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();
int64_t Generate(int32_t routing_id, int32_t network_service_request_id);
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;
static BrowserContextKeyedAPIFactory<WebRequestAPI>* GetFactoryInstance();
void Shutdown() override;
class TestObserver {
public:
TestObserver();
TestObserver(const TestObserver&) = delete;
TestObserver& operator=(const TestObserver&) = delete;
virtual ~TestObserver();
virtual void OnDidResetURLLoaderFactories() {}
};
static void SetObserverForTest(TestObserver* observer);
void OnListenerRemoved(const EventListenerInfo& details) override;
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());
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);
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);
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();
bool MayHaveProxies() const;
bool IsAvailableToWebViewEmbedderFrame(
content::RenderFrameHost* render_frame_host) const;
bool HasExtraHeadersListenerForTesting();
private:
enum class ProxyDecision {
kWillNotProxy = 0,
kWillProxyForExtension = 1,
kWillProxyForWebUI = 2,
kWillProxyForEmbedderWebView = 3,
kWillProxyForTelemetry = 4,
kMaxValue = kWillProxyForTelemetry,
};
friend class BrowserContextKeyedAPIFactory<WebRequestAPI>;
static const char* service_name() { return "WebRequestAPI"; }
static const bool kServiceRedirectedInIncognito = true;
static const bool kServiceIsNULLWhileTesting = true;
void UpdateMayHaveProxies();
void ResetURLLoaderFactories();
void OnExtensionLoaded(content::BrowserContext* browser_context,
const Extension* extension) override;
void OnExtensionUnloaded(content::BrowserContext* browser_context,
const Extension* extension,
UnloadedExtensionReason reason) override;
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);
void RemoveLazyListener(content::BrowserContext* browser_context,
const ExtensionId& extension_id,
const std::string& sub_event_name);
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());
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_;
bool may_have_proxies_;
base::WeakPtrFactory<WebRequestAPI> weak_factory_{this};
};
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;
ResponseAction Run() override;
};
class WebRequestInternalEventHandledFunction
: public WebRequestInternalFunction {
public:
DECLARE_EXTENSION_FUNCTION("webRequestInternal.eventHandled",
WEBREQUESTINTERNAL_EVENTHANDLED)
protected:
~WebRequestInternalEventHandledFunction() override = default;
private:
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);
ResponseAction Run() override;
};
class WebRequestHandlerBehaviorChangedFunction
: public WebRequestInternalFunction {
public:
DECLARE_EXTENSION_FUNCTION("webRequest.handlerBehaviorChanged",
WEBREQUEST_HANDLERBEHAVIORCHANGED)
protected:
~WebRequestHandlerBehaviorChangedFunction() override = default;
void GetQuotaLimitHeuristics(
extensions::QuotaLimitHeuristics* heuristics) const override;
void OnQuotaExceeded(std::string error) override;
ResponseAction Run() override;
};
}
#endif