blob: 32fdffd9e8dd502eff2fc2d5f1956435170e0dcd [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// 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 <set>
#include <string>
#include <utility>
#include <vector>
#include "base/gtest_prod_util.h"
#include "base/macros.h"
#include "base/memory/singleton.h"
#include "base/memory/weak_ptr.h"
#include "base/strings/string_util.h"
#include "base/time/time.h"
#include "extensions/browser/api/declarative/rules_registry.h"
#include "extensions/browser/api/declarative_webrequest/request_stage.h"
#include "extensions/browser/api/web_request/web_request_api_helpers.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_function.h"
#include "extensions/common/url_pattern_set.h"
#include "ipc/ipc_sender.h"
#include "net/base/completion_callback.h"
#include "net/base/network_delegate.h"
#include "net/http/http_request_headers.h"
class ExtensionWebRequestTimeTracker;
class GURL;
namespace base {
class DictionaryValue;
}
namespace content {
class BrowserContext;
}
namespace net {
class AuthChallengeInfo;
class AuthCredentials;
class HttpRequestHeaders;
class HttpResponseHeaders;
class URLRequest;
}
namespace extensions {
enum class WebRequestResourceType : uint8_t;
class ExtensionNavigationUIData;
class InfoMap;
class WebRequestEventDetails;
class WebRequestRulesRegistry;
class WebRequestEventRouterDelegate;
// 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:
explicit WebRequestAPI(content::BrowserContext* context);
~WebRequestAPI() override;
// BrowserContextKeyedAPI support:
static BrowserContextKeyedAPIFactory<WebRequestAPI>* GetFactoryInstance();
// EventRouter::Observer overrides:
void OnListenerRemoved(const EventListenerInfo& details) override;
private:
friend class BrowserContextKeyedAPIFactory<WebRequestAPI>;
// BrowserContextKeyedAPI support:
static const char* service_name() { return "WebRequestAPI"; }
static const bool kServiceRedirectedInIncognito = true;
static const bool kServiceIsNULLWhileTesting = true;
content::BrowserContext* browser_context_;
DISALLOW_COPY_AND_ASSIGN(WebRequestAPI);
};
// This class observes network events and routes them to the appropriate
// extensions listening to those events. All methods must be called on the IO
// thread unless otherwise specified.
class ExtensionWebRequestEventRouter
: public base::SupportsWeakPtr<ExtensionWebRequestEventRouter> {
public:
struct BlockedRequest;
enum EventTypes {
kInvalidEvent = 0,
kOnBeforeRequest = 1 << 0,
kOnBeforeSendHeaders = 1 << 1,
kOnSendHeaders = 1 << 2,
kOnHeadersReceived = 1 << 3,
kOnBeforeRedirect = 1 << 4,
kOnAuthRequired = 1 << 5,
kOnResponseStarted = 1 << 6,
kOnErrorOccurred = 1 << 7,
kOnCompleted = 1 << 8,
};
// Internal representation of the webRequest.RequestFilter type, used to
// filter what network events an extension cares about.
struct RequestFilter {
RequestFilter();
RequestFilter(const RequestFilter& other);
~RequestFilter();
// Returns false if there was an error initializing. If it is a user error,
// an error message is provided, otherwise the error is internal (and
// unexpected).
bool InitFromValue(const base::DictionaryValue& value, std::string* error);
extensions::URLPatternSet urls;
std::vector<WebRequestResourceType> types;
int tab_id;
int window_id;
};
// Contains an extension's response to a blocking event.
struct EventResponse {
EventResponse(const std::string& extension_id,
const base::Time& extension_install_time);
~EventResponse();
// ID of the extension that sent this response.
std::string extension_id;
// The time that the extension was installed. Used for deciding order of
// precedence in case multiple extensions respond with conflicting
// decisions.
base::Time extension_install_time;
// Response values. These are mutually exclusive.
bool cancel;
GURL new_url;
std::unique_ptr<net::HttpRequestHeaders> request_headers;
std::unique_ptr<extension_web_request_api_helpers::ResponseHeaders>
response_headers;
std::unique_ptr<net::AuthCredentials> auth_credentials;
private:
DISALLOW_COPY_AND_ASSIGN(EventResponse);
};
static ExtensionWebRequestEventRouter* GetInstance();
// Registers a rule registry. Pass null for |rules_registry| to unregister
// the rule registry for |browser_context|.
void RegisterRulesRegistry(
void* browser_context,
int rules_registry_id,
scoped_refptr<extensions::WebRequestRulesRegistry> rules_registry);
// Dispatches the OnBeforeRequest event to any extensions whose filters match
// the given request. Returns net::ERR_IO_PENDING if an extension is
// intercepting the request, OK otherwise.
int OnBeforeRequest(void* browser_context,
const extensions::InfoMap* extension_info_map,
net::URLRequest* request,
const net::CompletionCallback& callback,
GURL* new_url);
// Dispatches the onBeforeSendHeaders event. This is fired for HTTP(s)
// requests only, and allows modification of the outgoing request headers.
// Returns net::ERR_IO_PENDING if an extension is intercepting the request, OK
// otherwise.
int OnBeforeSendHeaders(void* browser_context,
const extensions::InfoMap* extension_info_map,
net::URLRequest* request,
const net::CompletionCallback& callback,
net::HttpRequestHeaders* headers);
// Dispatches the onSendHeaders event. This is fired for HTTP(s) requests
// only.
void OnSendHeaders(void* browser_context,
const extensions::InfoMap* extension_info_map,
net::URLRequest* request,
const net::HttpRequestHeaders& headers);
// Dispatches the onHeadersReceived event. This is fired for HTTP(s)
// requests only, and allows modification of incoming response headers.
// Returns net::ERR_IO_PENDING if an extension is intercepting the request,
// OK otherwise. |original_response_headers| is reference counted. |callback|
// |override_response_headers| and |allowed_unsafe_redirect_url| are owned by
// a URLRequestJob. They are guaranteed to be valid until |callback| is called
// or OnURLRequestDestroyed is called (whatever comes first).
// Do not modify |original_response_headers| directly but write new ones
// into |override_response_headers|.
int OnHeadersReceived(
void* browser_context,
const extensions::InfoMap* extension_info_map,
net::URLRequest* request,
const net::CompletionCallback& callback,
const net::HttpResponseHeaders* original_response_headers,
scoped_refptr<net::HttpResponseHeaders>* override_response_headers,
GURL* allowed_unsafe_redirect_url);
// Dispatches the OnAuthRequired event to any extensions whose filters match
// the given request. If the listener is not registered as "blocking", then
// AUTH_REQUIRED_RESPONSE_OK is returned. Otherwise,
// AUTH_REQUIRED_RESPONSE_IO_PENDING is returned and |callback| will be
// invoked later.
net::NetworkDelegate::AuthRequiredResponse OnAuthRequired(
void* browser_context,
const extensions::InfoMap* extension_info_map,
net::URLRequest* request,
const net::AuthChallengeInfo& auth_info,
const net::NetworkDelegate::AuthCallback& callback,
net::AuthCredentials* credentials);
// Dispatches the onBeforeRedirect event. This is fired for HTTP(s) requests
// only.
void OnBeforeRedirect(void* browser_context,
const extensions::InfoMap* extension_info_map,
net::URLRequest* request,
const GURL& new_location);
// Dispatches the onResponseStarted event indicating that the first bytes of
// the response have arrived.
void OnResponseStarted(void* browser_context,
const extensions::InfoMap* extension_info_map,
net::URLRequest* request,
int net_error);
// Deprecated.
// TODO(maksims): Remove this.
void OnResponseStarted(void* browser_context,
const extensions::InfoMap* extension_info_map,
net::URLRequest* request);
// Dispatches the onComplete event.
void OnCompleted(void* browser_context,
const extensions::InfoMap* extension_info_map,
net::URLRequest* request,
int net_error);
// Deprecated.
// TODO(maksims): Remove this.
void OnCompleted(void* browser_context,
const extensions::InfoMap* extension_info_map,
net::URLRequest* request);
// Dispatches an onErrorOccurred event.
void OnErrorOccurred(void* browser_context,
const extensions::InfoMap* extension_info_map,
net::URLRequest* request,
bool started,
int net_error);
// Deprecated.
// TODO(maksims): Remove this.
void OnErrorOccurred(void* browser_context,
const extensions::InfoMap* extension_info_map,
net::URLRequest* request,
bool started);
// Notifications when objects are going away.
void OnURLRequestDestroyed(void* browser_context,
const net::URLRequest* request);
// Called when an event listener handles a blocking event and responds.
void OnEventHandled(void* browser_context,
const std::string& extension_id,
const std::string& event_name,
const std::string& sub_event_name,
uint64_t request_id,
EventResponse* response);
// Adds a listener to the given event. |event_name| specifies the event being
// listened to. |sub_event_name| is an internal event uniquely generated in
// the extension process to correspond to the given filter and
// extra_info_spec. It returns true on success, false on failure.
bool AddEventListener(void* browser_context,
const std::string& extension_id,
const std::string& extension_name,
events::HistogramValue histogram_value,
const std::string& event_name,
const std::string& sub_event_name,
const RequestFilter& filter,
int extra_info_spec,
int embedder_process_id,
int web_view_instance_id,
base::WeakPtr<IPC::Sender> ipc_sender);
// Removes the listeners for a given <webview>.
void RemoveWebViewEventListeners(
void* browser_context,
int embedder_process_id,
int web_view_instance_id);
// Called when an incognito browser_context is created or destroyed.
void OnOTRBrowserContextCreated(void* original_browser_context,
void* otr_browser_context);
void OnOTRBrowserContextDestroyed(void* original_browser_context,
void* otr_browser_context);
// Registers a |callback| that is executed when the next page load happens.
// The callback is then deleted.
void AddCallbackForPageLoad(const base::Closure& callback);
private:
friend class WebRequestAPI;
FRIEND_TEST_ALL_PREFIXES(ExtensionWebRequestTest,
BlockingEventPrecedenceRedirect);
FRIEND_TEST_ALL_PREFIXES(ExtensionWebRequestTest,
BlockingEventPrecedenceCancel);
FRIEND_TEST_ALL_PREFIXES(ExtensionWebRequestTest,
SimulateChancelWhileBlocked);
FRIEND_TEST_ALL_PREFIXES(ExtensionWebRequestTest, AccessRequestBodyData);
FRIEND_TEST_ALL_PREFIXES(ExtensionWebRequestTest,
MinimalAccessRequestBodyData);
FRIEND_TEST_ALL_PREFIXES(ExtensionWebRequestTest, NoAccessRequestBodyData);
FRIEND_TEST_ALL_PREFIXES(ExtensionWebRequestTest, AddAndRemoveListeners);
FRIEND_TEST_ALL_PREFIXES(ExtensionWebRequestTest, BlockedRequestsAreRemoved);
FRIEND_TEST_ALL_PREFIXES(ExtensionWebRequestHeaderModificationTest,
TestModifications);
struct EventListener {
// An EventListener is uniquely defined by five properties.
// TODO(rdevlin.cronin): There are two types of EventListeners - those
// associated with WebViews and those that are not. The ones associated with
// WebViews are always identified by all five properties. The other ones
// will always have web_view_instance_id = 0. Unfortunately, the
// callbacks/interfaces for these ones don't specify embedder_process_id.
// This is why we need the LooselyMatches method, and the need for a
// |strict| argument on RemoveEventListener.
struct ID {
ID(void* browser_context,
const std::string& extension_id,
const std::string& sub_event_name,
int embedder_process_id,
int web_view_instance_id);
// If web_view_instance_id is 0, then ignore embedder_process_id.
// TODO(rdevlin.cronin): In a more sane world, LooselyMatches wouldn't be
// necessary.
bool LooselyMatches(const ID& that) const;
bool operator==(const ID& that) const;
void* browser_context;
std::string extension_id;
std::string sub_event_name;
int embedder_process_id;
int web_view_instance_id;
};
EventListener(ID id);
~EventListener();
const ID id;
std::string extension_name;
events::HistogramValue histogram_value = events::UNKNOWN;
RequestFilter filter;
int extra_info_spec = 0;
base::WeakPtr<IPC::Sender> ipc_sender;
std::unordered_set<uint64_t> blocked_requests;
private:
DISALLOW_COPY_AND_ASSIGN(EventListener);
};
friend struct base::DefaultSingletonTraits<ExtensionWebRequestEventRouter>;
using RawListeners = std::vector<EventListener*>;
using ListenerIDs = std::vector<EventListener::ID>;
using Listeners = std::vector<std::unique_ptr<EventListener>>;
using ListenerMapForBrowserContext = std::map<std::string, Listeners>;
using ListenerMap = std::map<void*, ListenerMapForBrowserContext>;
using BlockedRequestMap = std::map<uint64_t, BlockedRequest>;
// Map of request_id -> bit vector of EventTypes already signaled
using SignaledRequestMap = std::map<uint64_t, int>;
// For each browser_context: a bool indicating whether it is an incognito
// browser_context, and a pointer to the corresponding (non-)incognito
// browser_context.
using CrossBrowserContextMap = std::map<void*, std::pair<bool, void*>>;
using CallbacksForPageLoad = std::list<base::Closure>;
ExtensionWebRequestEventRouter();
~ExtensionWebRequestEventRouter();
// Returns the EventListener with the given |id|, or nullptr. Must be called
// from the IO thread.
EventListener* FindEventListener(const EventListener::ID& id);
// Returns the EventListener with the given |id| from |listeners|.
EventListener* FindEventListenerInContainer(const EventListener::ID& id,
Listeners& listeners);
// Removes the listener for the given sub-event. Must be called from the IO
// thread.
void RemoveEventListener(const EventListener::ID& id, bool strict);
// Ensures that future callbacks for |request| are ignored so that it can be
// destroyed safely.
void ClearPendingCallbacks(const net::URLRequest* request);
bool DispatchEvent(void* browser_context,
net::URLRequest* request,
const RawListeners& listener_ids,
ExtensionNavigationUIData* navigation_ui_data,
std::unique_ptr<WebRequestEventDetails> event_details);
void DispatchEventToListeners(
void* browser_context,
std::unique_ptr<ListenerIDs> listener_ids,
std::unique_ptr<WebRequestEventDetails> event_details);
// Returns a list of event listeners that care about the given event, based
// on their filter parameters. |extra_info_spec| will contain the combined
// set of extra_info_spec flags that every matching listener asked for.
RawListeners GetMatchingListeners(
void* browser_context,
const extensions::InfoMap* extension_info_map,
ExtensionNavigationUIData* navigation_ui_data,
const std::string& event_name,
const net::URLRequest* request,
int* extra_info_spec);
// Helper for the above functions. This is called twice: once for the
// browser_context of the event, the next time for the "cross" browser_context
// (i.e. the incognito browser_context if the event is originally for the
// normal browser_context, or vice versa).
void GetMatchingListenersImpl(void* browser_context,
const net::URLRequest* request,
const extensions::InfoMap* extension_info_map,
ExtensionNavigationUIData* navigation_ui_data,
bool crosses_incognito,
const std::string& event_name,
const GURL& url,
int render_process_host_id,
int routing_id,
WebRequestResourceType resource_type,
bool is_async_request,
bool is_request_from_extension,
int* extra_info_spec,
RawListeners* matching_listeners);
// Decrements the count of event handlers blocking the given request. When the
// count reaches 0, we stop blocking the request and proceed it using the
// method requested by the extension with the highest precedence. Precedence
// is decided by extension install time. If |response| is non-NULL, this
// method assumes ownership.
void DecrementBlockCount(void* browser_context,
const std::string& extension_id,
const std::string& event_name,
uint64_t request_id,
EventResponse* response);
// Processes the generated deltas from blocked_requests_ on the specified
// request. If |call_back| is true, the callback registered in
// |blocked_requests_| is called.
// The function returns the error code for the network request. This is
// mostly relevant in case the caller passes |call_callback| = false
// and wants to return the correct network error code himself.
int ExecuteDeltas(void* browser_context,
uint64_t request_id,
ExtensionNavigationUIData* navigation_ui_data,
bool call_callback);
// Evaluates the rules of the declarative webrequest API and stores
// modifications to the request that result from WebRequestActions as
// deltas in |blocked_requests_|. |original_response_headers| should only be
// set for the OnHeadersReceived stage and NULL otherwise. Returns whether any
// deltas were generated.
bool ProcessDeclarativeRules(
void* browser_context,
const extensions::InfoMap* extension_info_map,
const std::string& event_name,
net::URLRequest* request,
ExtensionNavigationUIData* navigation_ui_data,
extensions::RequestStage request_stage,
const net::HttpResponseHeaders* original_response_headers);
// If the BlockedRequest contains messages_to_extension entries in the event
// deltas, we send them to subscribers of
// chrome.declarativeWebRequest.onMessage.
void SendMessages(void* browser_context,
const BlockedRequest& blocked_request,
ExtensionNavigationUIData* navigation_ui_data);
// Called when the RulesRegistry is ready to unblock a request that was
// waiting for said event.
void OnRulesRegistryReady(void* browser_context,
const std::string& event_name,
uint64_t request_id,
extensions::RequestStage request_stage);
// Returns event details for a given request.
std::unique_ptr<WebRequestEventDetails> CreateEventDetails(
const net::URLRequest* request,
int extra_info_spec);
// Sets the flag that |event_type| has been signaled for |request_id|.
// Returns the value of the flag before setting it.
bool GetAndSetSignaled(uint64_t request_id, EventTypes event_type);
// Clears the flag that |event_type| has been signaled for |request_id|.
void ClearSignaled(uint64_t request_id, EventTypes event_type);
// Returns whether |request| represents a top level window navigation.
bool IsPageLoad(const net::URLRequest* request) const;
// Called on a page load to process all registered callbacks.
void NotifyPageLoad();
// Returns the matching cross browser_context (the regular browser_context if
// |browser_context| is OTR and vice versa).
void* GetCrossBrowserContext(void* browser_context) const;
// Determines whether the specified browser_context is an incognito
// browser_context (based on the contents of the cross-browser_context table
// and without dereferencing the browser_context pointer).
bool IsIncognitoBrowserContext(void* browser_context) const;
// Returns true if |request| was already signaled to some event handlers.
bool WasSignaled(const net::URLRequest& request) const;
// Get the number of listeners - for testing only.
size_t GetListenerCountForTesting(void* browser_context,
const std::string& event_name);
// A map for each browser_context that maps an event name to a set of
// extensions that are listening to that event.
ListenerMap listeners_;
// A map of network requests that are waiting for at least one event handler
// to respond.
BlockedRequestMap blocked_requests_;
// A map of request ids to a bitvector indicating which events have been
// signaled and should not be sent again.
SignaledRequestMap signaled_requests_;
// A map of original browser_context -> corresponding incognito
// browser_context (and vice versa).
CrossBrowserContextMap cross_browser_context_map_;
// Keeps track of time spent waiting on extensions using the blocking
// webRequest API.
std::unique_ptr<ExtensionWebRequestTimeTracker> request_time_tracker_;
CallbacksForPageLoad callbacks_for_page_load_;
typedef std::pair<void*, int> RulesRegistryKey;
// Maps each browser_context (and OTRBrowserContext) and a webview key to its
// respective rules registry.
std::map<RulesRegistryKey,
scoped_refptr<extensions::WebRequestRulesRegistry> > rules_registries_;
std::unique_ptr<extensions::WebRequestEventRouterDelegate>
web_request_event_router_delegate_;
DISALLOW_COPY_AND_ASSIGN(ExtensionWebRequestEventRouter);
};
class WebRequestInternalFunction : public IOThreadExtensionFunction {
public:
WebRequestInternalFunction() {}
protected:
~WebRequestInternalFunction() override {}
const std::string& 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 {}
// ExtensionFunction:
ResponseAction Run() override;
};
class WebRequestInternalEventHandledFunction
: public WebRequestInternalFunction {
public:
DECLARE_EXTENSION_FUNCTION("webRequestInternal.eventHandled",
WEBREQUESTINTERNAL_EVENTHANDLED)
protected:
~WebRequestInternalEventHandledFunction() override {}
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,
std::unique_ptr<ExtensionWebRequestEventRouter::EventResponse> response);
// ExtensionFunction:
ResponseAction Run() override;
};
class WebRequestHandlerBehaviorChangedFunction
: public WebRequestInternalFunction {
public:
DECLARE_EXTENSION_FUNCTION("webRequest.handlerBehaviorChanged",
WEBREQUEST_HANDLERBEHAVIORCHANGED)
protected:
~WebRequestHandlerBehaviorChangedFunction() override {}
// ExtensionFunction:
void GetQuotaLimitHeuristics(
extensions::QuotaLimitHeuristics* heuristics) const override;
// Handle quota exceeded gracefully: Only warn the user but still execute the
// function.
void OnQuotaExceeded(const std::string& error) override;
ResponseAction Run() override;
};
} // namespace extensions
#endif // EXTENSIONS_BROWSER_API_WEB_REQUEST_WEB_REQUEST_API_H_