| Index: chrome/browser/extensions/api/web_request/web_request_api.cc
|
| diff --git a/chrome/browser/extensions/api/web_request/web_request_api.cc b/chrome/browser/extensions/api/web_request/web_request_api.cc
|
| deleted file mode 100644
|
| index 259d6b05946aaae20d3074bae3cfe93479ac39d9..0000000000000000000000000000000000000000
|
| --- a/chrome/browser/extensions/api/web_request/web_request_api.cc
|
| +++ /dev/null
|
| @@ -1,2463 +0,0 @@
|
| -// 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.
|
| -
|
| -#include "chrome/browser/extensions/api/web_request/web_request_api.h"
|
| -
|
| -#include <algorithm>
|
| -
|
| -#include "base/bind.h"
|
| -#include "base/bind_helpers.h"
|
| -#include "base/json/json_writer.h"
|
| -#include "base/lazy_instance.h"
|
| -#include "base/metrics/histogram.h"
|
| -#include "base/strings/string_number_conversions.h"
|
| -#include "base/strings/string_util.h"
|
| -#include "base/strings/utf_string_conversions.h"
|
| -#include "base/time/time.h"
|
| -#include "base/values.h"
|
| -#include "chrome/browser/extensions/api/web_request/upload_data_presenter.h"
|
| -#include "chrome/browser/extensions/api/web_request/web_request_time_tracker.h"
|
| -#include "chrome/common/extensions/api/web_request.h"
|
| -#include "content/public/browser/browser_message_filter.h"
|
| -#include "content/public/browser/browser_thread.h"
|
| -#include "content/public/browser/render_frame_host.h"
|
| -#include "content/public/browser/render_process_host.h"
|
| -#include "content/public/browser/resource_request_info.h"
|
| -#include "content/public/browser/user_metrics.h"
|
| -#include "extensions/browser/api/activity_log/web_request_constants.h"
|
| -#include "extensions/browser/api/declarative_webrequest/request_stage.h"
|
| -#include "extensions/browser/api/declarative_webrequest/webrequest_constants.h"
|
| -#include "extensions/browser/api/declarative_webrequest/webrequest_rules_registry.h"
|
| -#include "extensions/browser/api/extensions_api_client.h"
|
| -#include "extensions/browser/api/web_request/web_request_api_constants.h"
|
| -#include "extensions/browser/api/web_request/web_request_api_helpers.h"
|
| -#include "extensions/browser/api/web_request/web_request_api_utils.h"
|
| -#include "extensions/browser/api/web_request/web_request_api_utils.h"
|
| -#include "extensions/browser/api/web_request/web_request_event_router_delegate.h"
|
| -#include "extensions/browser/event_router.h"
|
| -#include "extensions/browser/extension_message_filter.h"
|
| -#include "extensions/browser/extension_prefs.h"
|
| -#include "extensions/browser/extension_registry.h"
|
| -#include "extensions/browser/extension_system.h"
|
| -#include "extensions/browser/extensions_browser_client.h"
|
| -#include "extensions/browser/guest_view/web_view/web_view_constants.h"
|
| -#include "extensions/browser/guest_view/web_view/web_view_renderer_state.h"
|
| -#include "extensions/browser/info_map.h"
|
| -#include "extensions/browser/runtime_data.h"
|
| -#include "extensions/browser/warning_service.h"
|
| -#include "extensions/browser/warning_set.h"
|
| -#include "extensions/common/error_utils.h"
|
| -#include "extensions/common/event_filtering_info.h"
|
| -#include "extensions/common/extension.h"
|
| -#include "extensions/common/features/feature.h"
|
| -#include "extensions/common/permissions/permissions_data.h"
|
| -#include "extensions/common/url_pattern.h"
|
| -#include "extensions/strings/grit/extensions_strings.h"
|
| -#include "net/base/auth.h"
|
| -#include "net/base/net_errors.h"
|
| -#include "net/base/upload_data_stream.h"
|
| -#include "net/http/http_response_headers.h"
|
| -#include "net/http/http_util.h"
|
| -#include "net/url_request/url_request.h"
|
| -#include "ui/base/l10n/l10n_util.h"
|
| -#include "url/gurl.h"
|
| -
|
| -using base::DictionaryValue;
|
| -using base::ListValue;
|
| -using base::StringValue;
|
| -using content::BrowserMessageFilter;
|
| -using content::BrowserThread;
|
| -using content::ResourceRequestInfo;
|
| -using content::ResourceType;
|
| -using extensions::ErrorUtils;
|
| -using extensions::Extension;
|
| -using extensions::InfoMap;
|
| -using extensions::Feature;
|
| -using extensions::RulesRegistryService;
|
| -using extensions::Warning;
|
| -using extensions::WarningService;
|
| -using extensions::WarningSet;
|
| -
|
| -namespace activitylog = activity_log_web_request_constants;
|
| -namespace helpers = extension_web_request_api_helpers;
|
| -namespace utils = extension_web_request_api_utils;
|
| -namespace keys = extension_web_request_api_constants;
|
| -namespace web_request = extensions::api::web_request;
|
| -namespace declarative_keys = extensions::declarative_webrequest_constants;
|
| -
|
| -namespace {
|
| -
|
| -const char kWebRequestEventPrefix[] = "webRequest.";
|
| -
|
| -// List of all the webRequest events.
|
| -const char* const kWebRequestEvents[] = {
|
| - keys::kOnBeforeRedirectEvent,
|
| - web_request::OnBeforeRequest::kEventName,
|
| - keys::kOnBeforeSendHeadersEvent,
|
| - keys::kOnCompletedEvent,
|
| - web_request::OnErrorOccurred::kEventName,
|
| - keys::kOnSendHeadersEvent,
|
| - keys::kOnAuthRequiredEvent,
|
| - keys::kOnResponseStartedEvent,
|
| - keys::kOnHeadersReceivedEvent,
|
| -};
|
| -
|
| -const size_t kWebRequestEventsLength = arraysize(kWebRequestEvents);
|
| -
|
| -const char* GetRequestStageAsString(
|
| - ExtensionWebRequestEventRouter::EventTypes type) {
|
| - switch (type) {
|
| - case ExtensionWebRequestEventRouter::kInvalidEvent:
|
| - return "Invalid";
|
| - case ExtensionWebRequestEventRouter::kOnBeforeRequest:
|
| - return keys::kOnBeforeRequest;
|
| - case ExtensionWebRequestEventRouter::kOnBeforeSendHeaders:
|
| - return keys::kOnBeforeSendHeaders;
|
| - case ExtensionWebRequestEventRouter::kOnSendHeaders:
|
| - return keys::kOnSendHeaders;
|
| - case ExtensionWebRequestEventRouter::kOnHeadersReceived:
|
| - return keys::kOnHeadersReceived;
|
| - case ExtensionWebRequestEventRouter::kOnBeforeRedirect:
|
| - return keys::kOnBeforeRedirect;
|
| - case ExtensionWebRequestEventRouter::kOnAuthRequired:
|
| - return keys::kOnAuthRequired;
|
| - case ExtensionWebRequestEventRouter::kOnResponseStarted:
|
| - return keys::kOnResponseStarted;
|
| - case ExtensionWebRequestEventRouter::kOnErrorOccurred:
|
| - return keys::kOnErrorOccurred;
|
| - case ExtensionWebRequestEventRouter::kOnCompleted:
|
| - return keys::kOnCompleted;
|
| - }
|
| - NOTREACHED();
|
| - return "Not reached";
|
| -}
|
| -
|
| -int GetFrameId(bool is_main_frame, int frame_id) {
|
| - return is_main_frame ? 0 : frame_id;
|
| -}
|
| -
|
| -bool IsWebRequestEvent(const std::string& event_name) {
|
| - std::string web_request_event_name(event_name);
|
| - if (StartsWithASCII(
|
| - web_request_event_name, webview::kWebViewEventPrefix, true)) {
|
| - web_request_event_name.replace(
|
| - 0, strlen(webview::kWebViewEventPrefix), kWebRequestEventPrefix);
|
| - }
|
| - return std::find(
|
| - kWebRequestEvents,
|
| - kWebRequestEvents + kWebRequestEventsLength,
|
| - web_request_event_name) != (kWebRequestEvents + kWebRequestEventsLength);
|
| -}
|
| -
|
| -// Returns whether |request| has been triggered by an extension in
|
| -// |extension_info_map|.
|
| -bool IsRequestFromExtension(const net::URLRequest* request,
|
| - const InfoMap* extension_info_map) {
|
| - // |extension_info_map| is NULL for system-level requests.
|
| - if (!extension_info_map)
|
| - return false;
|
| -
|
| - const ResourceRequestInfo* info = ResourceRequestInfo::ForRequest(request);
|
| -
|
| - // If this request was not created by the ResourceDispatcher, |info| is NULL.
|
| - // All requests from extensions are created by the ResourceDispatcher.
|
| - if (!info)
|
| - return false;
|
| -
|
| - return extension_info_map->process_map().Contains(info->GetChildID());
|
| -}
|
| -
|
| -void ExtractRequestRoutingInfo(net::URLRequest* request,
|
| - int* render_process_host_id,
|
| - int* routing_id) {
|
| - if (!request->GetUserData(NULL))
|
| - return;
|
| - const ResourceRequestInfo* info = ResourceRequestInfo::ForRequest(request);
|
| - *render_process_host_id = info->GetChildID();
|
| - *routing_id = info->GetRouteID();
|
| -}
|
| -
|
| -// Given a |request|, this function determines whether it originated from
|
| -// a <webview> guest process or not. If it is from a <webview> guest process,
|
| -// then |web_view_info| is returned with information about the instance ID
|
| -// that uniquely identifies the <webview> and its embedder.
|
| -bool GetWebViewInfo(
|
| - net::URLRequest* request,
|
| - extensions::WebViewRendererState::WebViewInfo* web_view_info) {
|
| - int render_process_host_id = -1;
|
| - int routing_id = -1;
|
| - ExtractRequestRoutingInfo(request, &render_process_host_id, &routing_id);
|
| - return extensions::WebViewRendererState::GetInstance()->
|
| - GetInfo(render_process_host_id, routing_id, web_view_info);
|
| -}
|
| -
|
| -void ExtractRequestInfoDetails(net::URLRequest* request,
|
| - bool* is_main_frame,
|
| - int* frame_id,
|
| - bool* parent_is_main_frame,
|
| - int* parent_frame_id,
|
| - int* render_process_host_id,
|
| - int* routing_id,
|
| - ResourceType* resource_type) {
|
| - if (!request->GetUserData(NULL))
|
| - return;
|
| -
|
| - const ResourceRequestInfo* info = ResourceRequestInfo::ForRequest(request);
|
| - *frame_id = info->GetRenderFrameID();
|
| - *is_main_frame = info->IsMainFrame();
|
| - *parent_frame_id = info->GetParentRenderFrameID();
|
| - *parent_is_main_frame = info->ParentIsMainFrame();
|
| - *render_process_host_id = info->GetChildID();
|
| - *routing_id = info->GetRouteID();
|
| -
|
| - // Restrict the resource type to the values we care about.
|
| - if (utils::IsRelevantResourceType(info->GetResourceType()))
|
| - *resource_type = info->GetResourceType();
|
| - else
|
| - *resource_type = content::RESOURCE_TYPE_LAST_TYPE;
|
| -}
|
| -
|
| -// Extracts the body from |request| and writes the data into |out|.
|
| -void ExtractRequestInfoBody(const net::URLRequest* request,
|
| - base::DictionaryValue* out) {
|
| - const net::UploadDataStream* upload_data = request->get_upload();
|
| - if (!upload_data ||
|
| - (request->method() != "POST" && request->method() != "PUT"))
|
| - return; // Need to exit without "out->Set(keys::kRequestBodyKey, ...);" .
|
| -
|
| - base::DictionaryValue* requestBody = new base::DictionaryValue();
|
| - out->Set(keys::kRequestBodyKey, requestBody);
|
| -
|
| - // Get the data presenters, ordered by how specific they are.
|
| - extensions::ParsedDataPresenter parsed_data_presenter(*request);
|
| - extensions::RawDataPresenter raw_data_presenter;
|
| - extensions::UploadDataPresenter* const presenters[] = {
|
| - &parsed_data_presenter, // 1: any parseable forms? (Specific to forms.)
|
| - &raw_data_presenter // 2: any data at all? (Non-specific.)
|
| - };
|
| - // Keys for the results of the corresponding presenters.
|
| - static const char* const kKeys[] = {
|
| - keys::kRequestBodyFormDataKey,
|
| - keys::kRequestBodyRawKey
|
| - };
|
| -
|
| - const ScopedVector<net::UploadElementReader>& readers =
|
| - upload_data->element_readers();
|
| - bool some_succeeded = false;
|
| - for (size_t i = 0; !some_succeeded && i < arraysize(presenters); ++i) {
|
| - ScopedVector<net::UploadElementReader>::const_iterator reader;
|
| - for (reader = readers.begin(); reader != readers.end(); ++reader)
|
| - presenters[i]->FeedNext(**reader);
|
| - if (presenters[i]->Succeeded()) {
|
| - requestBody->Set(kKeys[i], presenters[i]->Result().release());
|
| - some_succeeded = true;
|
| - }
|
| - }
|
| - if (!some_succeeded)
|
| - requestBody->SetString(keys::kRequestBodyErrorKey, "Unknown error.");
|
| -}
|
| -
|
| -// Converts a HttpHeaders dictionary to a |name|, |value| pair. Returns
|
| -// true if successful.
|
| -bool FromHeaderDictionary(const base::DictionaryValue* header_value,
|
| - std::string* name,
|
| - std::string* value) {
|
| - if (!header_value->GetString(keys::kHeaderNameKey, name))
|
| - return false;
|
| -
|
| - // We require either a "value" or a "binaryValue" entry.
|
| - if (!(header_value->HasKey(keys::kHeaderValueKey) ^
|
| - header_value->HasKey(keys::kHeaderBinaryValueKey)))
|
| - return false;
|
| -
|
| - if (header_value->HasKey(keys::kHeaderValueKey)) {
|
| - if (!header_value->GetString(keys::kHeaderValueKey, value)) {
|
| - return false;
|
| - }
|
| - } else if (header_value->HasKey(keys::kHeaderBinaryValueKey)) {
|
| - const base::ListValue* list = NULL;
|
| - if (!header_value->HasKey(keys::kHeaderBinaryValueKey)) {
|
| - *value = "";
|
| - } else if (!header_value->GetList(keys::kHeaderBinaryValueKey, &list) ||
|
| - !helpers::CharListToString(list, value)) {
|
| - return false;
|
| - }
|
| - }
|
| - return true;
|
| -}
|
| -
|
| -// Creates a list of HttpHeaders (see the extension API JSON). If |headers| is
|
| -// NULL, the list is empty. Ownership is passed to the caller.
|
| -base::ListValue* GetResponseHeadersList(
|
| - const net::HttpResponseHeaders* headers) {
|
| - base::ListValue* headers_value = new base::ListValue();
|
| - if (headers) {
|
| - void* iter = NULL;
|
| - std::string name;
|
| - std::string value;
|
| - while (headers->EnumerateHeaderLines(&iter, &name, &value))
|
| - headers_value->Append(helpers::CreateHeaderDictionary(name, value));
|
| - }
|
| - return headers_value;
|
| -}
|
| -
|
| -base::ListValue* GetRequestHeadersList(const net::HttpRequestHeaders& headers) {
|
| - base::ListValue* headers_value = new base::ListValue();
|
| - for (net::HttpRequestHeaders::Iterator it(headers); it.GetNext(); )
|
| - headers_value->Append(
|
| - helpers::CreateHeaderDictionary(it.name(), it.value()));
|
| - return headers_value;
|
| -}
|
| -
|
| -// Creates a base::StringValue with the status line of |headers|. If |headers|
|
| -// is NULL, an empty string is returned. Ownership is passed to the caller.
|
| -base::StringValue* GetStatusLine(net::HttpResponseHeaders* headers) {
|
| - return new base::StringValue(
|
| - headers ? headers->GetStatusLine() : std::string());
|
| -}
|
| -
|
| -void RemoveEventListenerOnUI(
|
| - void* browser_context_id,
|
| - const std::string& event_name,
|
| - int process_id,
|
| - const std::string& extension_id) {
|
| - DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
| -
|
| - content::BrowserContext* browser_context =
|
| - reinterpret_cast<content::BrowserContext*>(browser_context_id);
|
| - if (!extensions::ExtensionsBrowserClient::Get()->IsValidContext(
|
| - browser_context))
|
| - return;
|
| -
|
| - extensions::EventRouter* event_router =
|
| - extensions::EventRouter::Get(browser_context);
|
| - if (!event_router)
|
| - return;
|
| -
|
| - content::RenderProcessHost* process =
|
| - content::RenderProcessHost::FromID(process_id);
|
| - if (!process)
|
| - return;
|
| -
|
| - event_router->RemoveEventListener(event_name, process, extension_id);
|
| -}
|
| -
|
| -// Sends an event to subscribers of chrome.declarativeWebRequest.onMessage or
|
| -// to subscribers of webview.onMessage if the action is being operated upon
|
| -// a <webview> guest renderer.
|
| -// |extension_id| identifies the extension that sends and receives the event.
|
| -// |is_web_view_guest| indicates whether the action is for a <webview>.
|
| -// |web_view_info| is a struct containing information about the <webview>
|
| -// embedder.
|
| -// |event_argument| is passed to the event listener.
|
| -void SendOnMessageEventOnUI(
|
| - void* browser_context_id,
|
| - const std::string& extension_id,
|
| - bool is_web_view_guest,
|
| - const extensions::WebViewRendererState::WebViewInfo& web_view_info,
|
| - scoped_ptr<base::DictionaryValue> event_argument) {
|
| - DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
| -
|
| - content::BrowserContext* browser_context =
|
| - reinterpret_cast<content::BrowserContext*>(browser_context_id);
|
| - if (!extensions::ExtensionsBrowserClient::Get()->IsValidContext(
|
| - browser_context))
|
| - return;
|
| -
|
| - scoped_ptr<base::ListValue> event_args(new base::ListValue);
|
| - event_args->Append(event_argument.release());
|
| -
|
| - extensions::EventRouter* event_router =
|
| - extensions::EventRouter::Get(browser_context);
|
| -
|
| - extensions::EventFilteringInfo event_filtering_info;
|
| -
|
| - std::string event_name;
|
| - // The instance ID uniquely identifies a <webview> instance within an embedder
|
| - // process. We use a filter here so that only event listeners for a particular
|
| - // <webview> will fire.
|
| - if (is_web_view_guest) {
|
| - event_filtering_info.SetInstanceID(web_view_info.instance_id);
|
| - event_name = webview::kEventMessage;
|
| - } else {
|
| - event_name = declarative_keys::kOnMessage;
|
| - }
|
| -
|
| - scoped_ptr<extensions::Event> event(new extensions::Event(
|
| - event_name,
|
| - event_args.Pass(), browser_context, GURL(),
|
| - extensions::EventRouter::USER_GESTURE_UNKNOWN,
|
| - event_filtering_info));
|
| - event_router->DispatchEventToExtension(extension_id, event.Pass());
|
| -}
|
| -
|
| -void RemoveEventListenerOnIOThread(
|
| - content::BrowserContext* browser_context,
|
| - const std::string& extension_id,
|
| - const std::string& sub_event_name) {
|
| - ExtensionWebRequestEventRouter::GetInstance()->RemoveEventListener(
|
| - browser_context, extension_id, sub_event_name);
|
| -}
|
| -
|
| -} // namespace
|
| -
|
| -namespace extensions {
|
| -
|
| -WebRequestAPI::WebRequestAPI(content::BrowserContext* context)
|
| - : browser_context_(context) {
|
| - EventRouter* event_router = EventRouter::Get(browser_context_);
|
| - for (size_t i = 0; i < arraysize(kWebRequestEvents); ++i) {
|
| - // Observe the webRequest event.
|
| - std::string event_name = kWebRequestEvents[i];
|
| - event_router->RegisterObserver(this, event_name);
|
| -
|
| - // Also observe the corresponding webview event.
|
| - event_name.replace(
|
| - 0, sizeof(kWebRequestEventPrefix) - 1, webview::kWebViewEventPrefix);
|
| - event_router->RegisterObserver(this, event_name);
|
| - }
|
| -}
|
| -
|
| -WebRequestAPI::~WebRequestAPI() {
|
| - EventRouter::Get(browser_context_)->UnregisterObserver(this);
|
| -}
|
| -
|
| -static base::LazyInstance<BrowserContextKeyedAPIFactory<WebRequestAPI> >
|
| - g_factory = LAZY_INSTANCE_INITIALIZER;
|
| -
|
| -// static
|
| -BrowserContextKeyedAPIFactory<WebRequestAPI>*
|
| -WebRequestAPI::GetFactoryInstance() {
|
| - return g_factory.Pointer();
|
| -}
|
| -
|
| -void WebRequestAPI::OnListenerRemoved(const EventListenerInfo& details) {
|
| - DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
| - // Note that details.event_name includes the sub-event details (e.g. "/123").
|
| - BrowserThread::PostTask(BrowserThread::IO,
|
| - FROM_HERE,
|
| - base::Bind(&RemoveEventListenerOnIOThread,
|
| - details.browser_context,
|
| - details.extension_id,
|
| - details.event_name));
|
| -}
|
| -
|
| -} // namespace extensions
|
| -
|
| -// Represents a single unique listener to an event, along with whatever filter
|
| -// parameters and extra_info_spec were specified at the time the listener was
|
| -// added.
|
| -// NOTE(benjhayden) New APIs should not use this sub_event_name trick! It does
|
| -// not play well with event pages. See downloads.onDeterminingFilename and
|
| -// ExtensionDownloadsEventRouter for an alternative approach.
|
| -struct ExtensionWebRequestEventRouter::EventListener {
|
| - std::string extension_id;
|
| - std::string extension_name;
|
| - std::string sub_event_name;
|
| - RequestFilter filter;
|
| - int extra_info_spec;
|
| - int embedder_process_id;
|
| - int webview_instance_id;
|
| - base::WeakPtr<IPC::Sender> ipc_sender;
|
| - mutable std::set<uint64> blocked_requests;
|
| -
|
| - // Comparator to work with std::set.
|
| - bool operator<(const EventListener& that) const {
|
| - if (extension_id < that.extension_id)
|
| - return true;
|
| - if (extension_id == that.extension_id &&
|
| - sub_event_name < that.sub_event_name)
|
| - return true;
|
| - return false;
|
| - }
|
| -
|
| - EventListener() : extra_info_spec(0) {}
|
| -};
|
| -
|
| -// Contains info about requests that are blocked waiting for a response from
|
| -// an extension.
|
| -struct ExtensionWebRequestEventRouter::BlockedRequest {
|
| - // The request that is being blocked.
|
| - net::URLRequest* request;
|
| -
|
| - // Whether the request originates from an incognito tab.
|
| - bool is_incognito;
|
| -
|
| - // The event that we're currently blocked on.
|
| - EventTypes event;
|
| -
|
| - // The number of event handlers that we are awaiting a response from.
|
| - int num_handlers_blocking;
|
| -
|
| - // Pointer to NetLog to report significant changes to the request for
|
| - // debugging.
|
| - const net::BoundNetLog* net_log;
|
| -
|
| - // The callback to call when we get a response from all event handlers.
|
| - net::CompletionCallback callback;
|
| -
|
| - // If non-empty, this contains the new URL that the request will redirect to.
|
| - // Only valid for OnBeforeRequest and OnHeadersReceived.
|
| - GURL* new_url;
|
| -
|
| - // The request headers that will be issued along with this request. Only valid
|
| - // for OnBeforeSendHeaders.
|
| - net::HttpRequestHeaders* request_headers;
|
| -
|
| - // The response headers that were received from the server. Only valid for
|
| - // OnHeadersReceived.
|
| - scoped_refptr<const net::HttpResponseHeaders> original_response_headers;
|
| -
|
| - // Location where to override response headers. Only valid for
|
| - // OnHeadersReceived.
|
| - scoped_refptr<net::HttpResponseHeaders>* override_response_headers;
|
| -
|
| - // If non-empty, this contains the auth credentials that may be filled in.
|
| - // Only valid for OnAuthRequired.
|
| - net::AuthCredentials* auth_credentials;
|
| -
|
| - // The callback to invoke for auth. If |auth_callback.is_null()| is false,
|
| - // |callback| must be NULL.
|
| - // Only valid for OnAuthRequired.
|
| - net::NetworkDelegate::AuthCallback auth_callback;
|
| -
|
| - // Time the request was paused. Used for logging purposes.
|
| - base::Time blocking_time;
|
| -
|
| - // Changes requested by extensions.
|
| - helpers::EventResponseDeltas response_deltas;
|
| -
|
| - // Provider of meta data about extensions, only used and non-NULL for events
|
| - // that are delayed until the rules registry is ready.
|
| - InfoMap* extension_info_map;
|
| -
|
| - BlockedRequest()
|
| - : request(NULL),
|
| - is_incognito(false),
|
| - event(kInvalidEvent),
|
| - num_handlers_blocking(0),
|
| - net_log(NULL),
|
| - new_url(NULL),
|
| - request_headers(NULL),
|
| - override_response_headers(NULL),
|
| - auth_credentials(NULL),
|
| - extension_info_map(NULL) {}
|
| -};
|
| -
|
| -bool ExtensionWebRequestEventRouter::RequestFilter::InitFromValue(
|
| - const base::DictionaryValue& value, std::string* error) {
|
| - if (!value.HasKey("urls"))
|
| - return false;
|
| -
|
| - for (base::DictionaryValue::Iterator it(value); !it.IsAtEnd(); it.Advance()) {
|
| - if (it.key() == "urls") {
|
| - const base::ListValue* urls_value = NULL;
|
| - if (!it.value().GetAsList(&urls_value))
|
| - return false;
|
| - for (size_t i = 0; i < urls_value->GetSize(); ++i) {
|
| - std::string url;
|
| - URLPattern pattern(
|
| - URLPattern::SCHEME_HTTP | URLPattern::SCHEME_HTTPS |
|
| - URLPattern::SCHEME_FTP | URLPattern::SCHEME_FILE |
|
| - URLPattern::SCHEME_EXTENSION);
|
| - if (!urls_value->GetString(i, &url) ||
|
| - pattern.Parse(url) != URLPattern::PARSE_SUCCESS) {
|
| - *error = ErrorUtils::FormatErrorMessage(
|
| - keys::kInvalidRequestFilterUrl, url);
|
| - return false;
|
| - }
|
| - urls.AddPattern(pattern);
|
| - }
|
| - } else if (it.key() == "types") {
|
| - const base::ListValue* types_value = NULL;
|
| - if (!it.value().GetAsList(&types_value))
|
| - return false;
|
| - for (size_t i = 0; i < types_value->GetSize(); ++i) {
|
| - std::string type_str;
|
| - ResourceType type;
|
| - if (!types_value->GetString(i, &type_str) ||
|
| - !utils::ParseResourceType(type_str, &type))
|
| - return false;
|
| - types.push_back(type);
|
| - }
|
| - } else if (it.key() == "tabId") {
|
| - if (!it.value().GetAsInteger(&tab_id))
|
| - return false;
|
| - } else if (it.key() == "windowId") {
|
| - if (!it.value().GetAsInteger(&window_id))
|
| - return false;
|
| - } else {
|
| - return false;
|
| - }
|
| - }
|
| - return true;
|
| -}
|
| -
|
| -// static
|
| -bool ExtensionWebRequestEventRouter::ExtraInfoSpec::InitFromValue(
|
| - const base::ListValue& value, int* extra_info_spec) {
|
| - *extra_info_spec = 0;
|
| - for (size_t i = 0; i < value.GetSize(); ++i) {
|
| - std::string str;
|
| - if (!value.GetString(i, &str))
|
| - return false;
|
| -
|
| - if (str == "requestHeaders")
|
| - *extra_info_spec |= REQUEST_HEADERS;
|
| - else if (str == "responseHeaders")
|
| - *extra_info_spec |= RESPONSE_HEADERS;
|
| - else if (str == "blocking")
|
| - *extra_info_spec |= BLOCKING;
|
| - else if (str == "asyncBlocking")
|
| - *extra_info_spec |= ASYNC_BLOCKING;
|
| - else if (str == "requestBody")
|
| - *extra_info_spec |= REQUEST_BODY;
|
| - else
|
| - return false;
|
| -
|
| - // BLOCKING and ASYNC_BLOCKING are mutually exclusive.
|
| - if ((*extra_info_spec & BLOCKING) && (*extra_info_spec & ASYNC_BLOCKING))
|
| - return false;
|
| - }
|
| - return true;
|
| -}
|
| -
|
| -
|
| -ExtensionWebRequestEventRouter::EventResponse::EventResponse(
|
| - const std::string& extension_id, const base::Time& extension_install_time)
|
| - : extension_id(extension_id),
|
| - extension_install_time(extension_install_time),
|
| - cancel(false) {
|
| -}
|
| -
|
| -ExtensionWebRequestEventRouter::EventResponse::~EventResponse() {
|
| -}
|
| -
|
| -ExtensionWebRequestEventRouter::RequestFilter::RequestFilter()
|
| - : tab_id(-1), window_id(-1) {
|
| -}
|
| -
|
| -ExtensionWebRequestEventRouter::RequestFilter::~RequestFilter() {
|
| -}
|
| -
|
| -//
|
| -// ExtensionWebRequestEventRouter
|
| -//
|
| -
|
| -// static
|
| -ExtensionWebRequestEventRouter* ExtensionWebRequestEventRouter::GetInstance() {
|
| - return Singleton<ExtensionWebRequestEventRouter>::get();
|
| -}
|
| -
|
| -ExtensionWebRequestEventRouter::ExtensionWebRequestEventRouter()
|
| - : request_time_tracker_(new ExtensionWebRequestTimeTracker) {
|
| - web_request_event_router_delegate_.reset(
|
| - extensions::ExtensionsAPIClient::Get()->
|
| - CreateWebRequestEventRouterDelegate());
|
| -}
|
| -
|
| -ExtensionWebRequestEventRouter::~ExtensionWebRequestEventRouter() {
|
| -}
|
| -
|
| -void ExtensionWebRequestEventRouter::RegisterRulesRegistry(
|
| - void* browser_context,
|
| - const extensions::RulesRegistry::WebViewKey& webview_key,
|
| - scoped_refptr<extensions::WebRequestRulesRegistry> rules_registry) {
|
| - RulesRegistryKey key(browser_context, webview_key);
|
| - if (rules_registry.get())
|
| - rules_registries_[key] = rules_registry;
|
| - else
|
| - rules_registries_.erase(key);
|
| -}
|
| -
|
| -void ExtensionWebRequestEventRouter::ExtractRequestInfo(
|
| - net::URLRequest* request, base::DictionaryValue* out) {
|
| - bool is_main_frame = false;
|
| - int frame_id = -1;
|
| - bool parent_is_main_frame = false;
|
| - int parent_frame_id = -1;
|
| - int frame_id_for_extension = -1;
|
| - int parent_frame_id_for_extension = -1;
|
| - int render_process_host_id = -1;
|
| - int routing_id = -1;
|
| - ResourceType resource_type = content::RESOURCE_TYPE_LAST_TYPE;
|
| - ExtractRequestInfoDetails(request, &is_main_frame, &frame_id,
|
| - &parent_is_main_frame, &parent_frame_id,
|
| - &render_process_host_id, &routing_id,
|
| - &resource_type);
|
| - frame_id_for_extension = GetFrameId(is_main_frame, frame_id);
|
| - parent_frame_id_for_extension = GetFrameId(parent_is_main_frame,
|
| - parent_frame_id);
|
| -
|
| - out->SetString(keys::kRequestIdKey,
|
| - base::Uint64ToString(request->identifier()));
|
| - out->SetString(keys::kUrlKey, request->url().spec());
|
| - out->SetString(keys::kMethodKey, request->method());
|
| - out->SetInteger(keys::kFrameIdKey, frame_id_for_extension);
|
| - out->SetInteger(keys::kParentFrameIdKey, parent_frame_id_for_extension);
|
| - out->SetString(keys::kTypeKey, utils::ResourceTypeToString(resource_type));
|
| - out->SetDouble(keys::kTimeStampKey, base::Time::Now().ToDoubleT() * 1000);
|
| - if (web_request_event_router_delegate_) {
|
| - web_request_event_router_delegate_->ExtractExtraRequestDetails(
|
| - request, out);
|
| - }
|
| -}
|
| -
|
| -int ExtensionWebRequestEventRouter::OnBeforeRequest(
|
| - void* browser_context,
|
| - InfoMap* extension_info_map,
|
| - net::URLRequest* request,
|
| - const net::CompletionCallback& callback,
|
| - GURL* new_url) {
|
| - // We hide events from the system context as well as sensitive requests.
|
| - if (!browser_context ||
|
| - WebRequestPermissions::HideRequest(extension_info_map, request))
|
| - return net::OK;
|
| -
|
| - if (IsPageLoad(request))
|
| - NotifyPageLoad();
|
| -
|
| - request_time_tracker_->LogRequestStartTime(request->identifier(),
|
| - base::Time::Now(),
|
| - request->url(),
|
| - browser_context);
|
| -
|
| - // Whether to initialized blocked_requests_.
|
| - bool initialize_blocked_requests = false;
|
| -
|
| - initialize_blocked_requests |=
|
| - ProcessDeclarativeRules(browser_context, extension_info_map,
|
| - web_request::OnBeforeRequest::kEventName, request,
|
| - extensions::ON_BEFORE_REQUEST, NULL);
|
| -
|
| - int extra_info_spec = 0;
|
| - std::vector<const EventListener*> listeners =
|
| - GetMatchingListeners(browser_context, extension_info_map,
|
| - web_request::OnBeforeRequest::kEventName, request,
|
| - &extra_info_spec);
|
| - if (!listeners.empty() &&
|
| - !GetAndSetSignaled(request->identifier(), kOnBeforeRequest)) {
|
| - base::ListValue args;
|
| - base::DictionaryValue* dict = new base::DictionaryValue();
|
| - ExtractRequestInfo(request, dict);
|
| - if (extra_info_spec & ExtraInfoSpec::REQUEST_BODY)
|
| - ExtractRequestInfoBody(request, dict);
|
| - args.Append(dict);
|
| -
|
| - initialize_blocked_requests |=
|
| - DispatchEvent(browser_context, request, listeners, args);
|
| - }
|
| -
|
| - if (!initialize_blocked_requests)
|
| - return net::OK; // Nobody saw a reason for modifying the request.
|
| -
|
| - blocked_requests_[request->identifier()].event = kOnBeforeRequest;
|
| - blocked_requests_[request->identifier()].is_incognito |=
|
| - IsIncognitoBrowserContext(browser_context);
|
| - blocked_requests_[request->identifier()].request = request;
|
| - blocked_requests_[request->identifier()].callback = callback;
|
| - blocked_requests_[request->identifier()].new_url = new_url;
|
| - blocked_requests_[request->identifier()].net_log = &request->net_log();
|
| -
|
| - if (blocked_requests_[request->identifier()].num_handlers_blocking == 0) {
|
| - // If there are no blocking handlers, only the declarative rules tried
|
| - // to modify the request and we can respond synchronously.
|
| - return ExecuteDeltas(browser_context, request->identifier(),
|
| - false /* call_callback*/);
|
| - } else {
|
| - return net::ERR_IO_PENDING;
|
| - }
|
| -}
|
| -
|
| -int ExtensionWebRequestEventRouter::OnBeforeSendHeaders(
|
| - void* browser_context,
|
| - InfoMap* extension_info_map,
|
| - net::URLRequest* request,
|
| - const net::CompletionCallback& callback,
|
| - net::HttpRequestHeaders* headers) {
|
| - // We hide events from the system context as well as sensitive requests.
|
| - if (!browser_context ||
|
| - WebRequestPermissions::HideRequest(extension_info_map, request))
|
| - return net::OK;
|
| -
|
| - bool initialize_blocked_requests = false;
|
| -
|
| - initialize_blocked_requests |=
|
| - ProcessDeclarativeRules(browser_context, extension_info_map,
|
| - keys::kOnBeforeSendHeadersEvent, request,
|
| - extensions::ON_BEFORE_SEND_HEADERS, NULL);
|
| -
|
| - int extra_info_spec = 0;
|
| - std::vector<const EventListener*> listeners =
|
| - GetMatchingListeners(browser_context, extension_info_map,
|
| - keys::kOnBeforeSendHeadersEvent, request,
|
| - &extra_info_spec);
|
| - if (!listeners.empty() &&
|
| - !GetAndSetSignaled(request->identifier(), kOnBeforeSendHeaders)) {
|
| - base::ListValue args;
|
| - base::DictionaryValue* dict = new base::DictionaryValue();
|
| - ExtractRequestInfo(request, dict);
|
| - if (extra_info_spec & ExtraInfoSpec::REQUEST_HEADERS)
|
| - dict->Set(keys::kRequestHeadersKey, GetRequestHeadersList(*headers));
|
| - args.Append(dict);
|
| -
|
| - initialize_blocked_requests |=
|
| - DispatchEvent(browser_context, request, listeners, args);
|
| - }
|
| -
|
| - if (!initialize_blocked_requests)
|
| - return net::OK; // Nobody saw a reason for modifying the request.
|
| -
|
| - blocked_requests_[request->identifier()].event = kOnBeforeSendHeaders;
|
| - blocked_requests_[request->identifier()].is_incognito |=
|
| - IsIncognitoBrowserContext(browser_context);
|
| - blocked_requests_[request->identifier()].request = request;
|
| - blocked_requests_[request->identifier()].callback = callback;
|
| - blocked_requests_[request->identifier()].request_headers = headers;
|
| - blocked_requests_[request->identifier()].net_log = &request->net_log();
|
| -
|
| - if (blocked_requests_[request->identifier()].num_handlers_blocking == 0) {
|
| - // If there are no blocking handlers, only the declarative rules tried
|
| - // to modify the request and we can respond synchronously.
|
| - return ExecuteDeltas(browser_context, request->identifier(),
|
| - false /* call_callback*/);
|
| - } else {
|
| - return net::ERR_IO_PENDING;
|
| - }
|
| -}
|
| -
|
| -void ExtensionWebRequestEventRouter::OnSendHeaders(
|
| - void* browser_context,
|
| - InfoMap* extension_info_map,
|
| - net::URLRequest* request,
|
| - const net::HttpRequestHeaders& headers) {
|
| - // We hide events from the system context as well as sensitive requests.
|
| - if (!browser_context ||
|
| - WebRequestPermissions::HideRequest(extension_info_map, request))
|
| - return;
|
| -
|
| - if (GetAndSetSignaled(request->identifier(), kOnSendHeaders))
|
| - return;
|
| -
|
| - ClearSignaled(request->identifier(), kOnBeforeRedirect);
|
| -
|
| - int extra_info_spec = 0;
|
| - std::vector<const EventListener*> listeners =
|
| - GetMatchingListeners(browser_context, extension_info_map,
|
| - keys::kOnSendHeadersEvent, request,
|
| - &extra_info_spec);
|
| - if (listeners.empty())
|
| - return;
|
| -
|
| - base::ListValue args;
|
| - base::DictionaryValue* dict = new base::DictionaryValue();
|
| - ExtractRequestInfo(request, dict);
|
| - if (extra_info_spec & ExtraInfoSpec::REQUEST_HEADERS)
|
| - dict->Set(keys::kRequestHeadersKey, GetRequestHeadersList(headers));
|
| - args.Append(dict);
|
| -
|
| - DispatchEvent(browser_context, request, listeners, args);
|
| -}
|
| -
|
| -int ExtensionWebRequestEventRouter::OnHeadersReceived(
|
| - void* browser_context,
|
| - 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) {
|
| - // We hide events from the system context as well as sensitive requests.
|
| - if (!browser_context ||
|
| - WebRequestPermissions::HideRequest(extension_info_map, request))
|
| - return net::OK;
|
| -
|
| - bool initialize_blocked_requests = false;
|
| -
|
| - initialize_blocked_requests |=
|
| - ProcessDeclarativeRules(browser_context, extension_info_map,
|
| - keys::kOnHeadersReceivedEvent, request,
|
| - extensions::ON_HEADERS_RECEIVED,
|
| - original_response_headers);
|
| -
|
| - int extra_info_spec = 0;
|
| - std::vector<const EventListener*> listeners =
|
| - GetMatchingListeners(browser_context, extension_info_map,
|
| - keys::kOnHeadersReceivedEvent, request,
|
| - &extra_info_spec);
|
| -
|
| - if (!listeners.empty() &&
|
| - !GetAndSetSignaled(request->identifier(), kOnHeadersReceived)) {
|
| - base::ListValue args;
|
| - base::DictionaryValue* dict = new base::DictionaryValue();
|
| - ExtractRequestInfo(request, dict);
|
| - dict->SetString(keys::kStatusLineKey,
|
| - original_response_headers->GetStatusLine());
|
| - if (extra_info_spec & ExtraInfoSpec::RESPONSE_HEADERS) {
|
| - dict->Set(keys::kResponseHeadersKey,
|
| - GetResponseHeadersList(original_response_headers));
|
| - }
|
| - args.Append(dict);
|
| -
|
| - initialize_blocked_requests |=
|
| - DispatchEvent(browser_context, request, listeners, args);
|
| - }
|
| -
|
| - if (!initialize_blocked_requests)
|
| - return net::OK; // Nobody saw a reason for modifying the request.
|
| -
|
| - blocked_requests_[request->identifier()].event = kOnHeadersReceived;
|
| - blocked_requests_[request->identifier()].is_incognito |=
|
| - IsIncognitoBrowserContext(browser_context);
|
| - blocked_requests_[request->identifier()].request = request;
|
| - blocked_requests_[request->identifier()].callback = callback;
|
| - blocked_requests_[request->identifier()].net_log = &request->net_log();
|
| - blocked_requests_[request->identifier()].override_response_headers =
|
| - override_response_headers;
|
| - blocked_requests_[request->identifier()].original_response_headers =
|
| - original_response_headers;
|
| - blocked_requests_[request->identifier()].new_url =
|
| - allowed_unsafe_redirect_url;
|
| -
|
| - if (blocked_requests_[request->identifier()].num_handlers_blocking == 0) {
|
| - // If there are no blocking handlers, only the declarative rules tried
|
| - // to modify the request and we can respond synchronously.
|
| - return ExecuteDeltas(browser_context, request->identifier(),
|
| - false /* call_callback*/);
|
| - } else {
|
| - return net::ERR_IO_PENDING;
|
| - }
|
| -}
|
| -
|
| -net::NetworkDelegate::AuthRequiredResponse
|
| -ExtensionWebRequestEventRouter::OnAuthRequired(
|
| - void* browser_context,
|
| - InfoMap* extension_info_map,
|
| - net::URLRequest* request,
|
| - const net::AuthChallengeInfo& auth_info,
|
| - const net::NetworkDelegate::AuthCallback& callback,
|
| - net::AuthCredentials* credentials) {
|
| - // No browser_context means that this is for authentication challenges in the
|
| - // system context. Skip in that case. Also skip sensitive requests.
|
| - if (!browser_context ||
|
| - WebRequestPermissions::HideRequest(extension_info_map, request))
|
| - return net::NetworkDelegate::AUTH_REQUIRED_RESPONSE_NO_ACTION;
|
| -
|
| - int extra_info_spec = 0;
|
| - std::vector<const EventListener*> listeners =
|
| - GetMatchingListeners(browser_context, extension_info_map,
|
| - keys::kOnAuthRequiredEvent, request,
|
| - &extra_info_spec);
|
| - if (listeners.empty())
|
| - return net::NetworkDelegate::AUTH_REQUIRED_RESPONSE_NO_ACTION;
|
| -
|
| - base::ListValue args;
|
| - base::DictionaryValue* dict = new base::DictionaryValue();
|
| - ExtractRequestInfo(request, dict);
|
| - dict->SetBoolean(keys::kIsProxyKey, auth_info.is_proxy);
|
| - if (!auth_info.scheme.empty())
|
| - dict->SetString(keys::kSchemeKey, auth_info.scheme);
|
| - if (!auth_info.realm.empty())
|
| - dict->SetString(keys::kRealmKey, auth_info.realm);
|
| - base::DictionaryValue* challenger = new base::DictionaryValue();
|
| - challenger->SetString(keys::kHostKey, auth_info.challenger.host());
|
| - challenger->SetInteger(keys::kPortKey, auth_info.challenger.port());
|
| - dict->Set(keys::kChallengerKey, challenger);
|
| - dict->Set(keys::kStatusLineKey, GetStatusLine(request->response_headers()));
|
| - if (extra_info_spec & ExtraInfoSpec::RESPONSE_HEADERS) {
|
| - dict->Set(keys::kResponseHeadersKey,
|
| - GetResponseHeadersList(request->response_headers()));
|
| - }
|
| - args.Append(dict);
|
| -
|
| - if (DispatchEvent(browser_context, request, listeners, args)) {
|
| - blocked_requests_[request->identifier()].event = kOnAuthRequired;
|
| - blocked_requests_[request->identifier()].is_incognito |=
|
| - IsIncognitoBrowserContext(browser_context);
|
| - blocked_requests_[request->identifier()].request = request;
|
| - blocked_requests_[request->identifier()].auth_callback = callback;
|
| - blocked_requests_[request->identifier()].auth_credentials = credentials;
|
| - blocked_requests_[request->identifier()].net_log = &request->net_log();
|
| - return net::NetworkDelegate::AUTH_REQUIRED_RESPONSE_IO_PENDING;
|
| - }
|
| - return net::NetworkDelegate::AUTH_REQUIRED_RESPONSE_NO_ACTION;
|
| -}
|
| -
|
| -void ExtensionWebRequestEventRouter::OnBeforeRedirect(
|
| - void* browser_context,
|
| - InfoMap* extension_info_map,
|
| - net::URLRequest* request,
|
| - const GURL& new_location) {
|
| - // We hide events from the system context as well as sensitive requests.
|
| - if (!browser_context ||
|
| - WebRequestPermissions::HideRequest(extension_info_map, request))
|
| - return;
|
| -
|
| - if (GetAndSetSignaled(request->identifier(), kOnBeforeRedirect))
|
| - return;
|
| -
|
| - ClearSignaled(request->identifier(), kOnBeforeRequest);
|
| - ClearSignaled(request->identifier(), kOnBeforeSendHeaders);
|
| - ClearSignaled(request->identifier(), kOnSendHeaders);
|
| - ClearSignaled(request->identifier(), kOnHeadersReceived);
|
| -
|
| - int extra_info_spec = 0;
|
| - std::vector<const EventListener*> listeners =
|
| - GetMatchingListeners(browser_context, extension_info_map,
|
| - keys::kOnBeforeRedirectEvent, request,
|
| - &extra_info_spec);
|
| - if (listeners.empty())
|
| - return;
|
| -
|
| - int http_status_code = request->GetResponseCode();
|
| -
|
| - std::string response_ip = request->GetSocketAddress().host();
|
| -
|
| - base::ListValue args;
|
| - base::DictionaryValue* dict = new base::DictionaryValue();
|
| - ExtractRequestInfo(request, dict);
|
| - dict->SetString(keys::kRedirectUrlKey, new_location.spec());
|
| - dict->SetInteger(keys::kStatusCodeKey, http_status_code);
|
| - if (!response_ip.empty())
|
| - dict->SetString(keys::kIpKey, response_ip);
|
| - dict->SetBoolean(keys::kFromCache, request->was_cached());
|
| - dict->Set(keys::kStatusLineKey, GetStatusLine(request->response_headers()));
|
| - if (extra_info_spec & ExtraInfoSpec::RESPONSE_HEADERS) {
|
| - dict->Set(keys::kResponseHeadersKey,
|
| - GetResponseHeadersList(request->response_headers()));
|
| - }
|
| - args.Append(dict);
|
| -
|
| - DispatchEvent(browser_context, request, listeners, args);
|
| -}
|
| -
|
| -void ExtensionWebRequestEventRouter::OnResponseStarted(
|
| - void* browser_context,
|
| - InfoMap* extension_info_map,
|
| - net::URLRequest* request) {
|
| - // We hide events from the system context as well as sensitive requests.
|
| - if (!browser_context ||
|
| - WebRequestPermissions::HideRequest(extension_info_map, request))
|
| - return;
|
| -
|
| - // OnResponseStarted is even triggered, when the request was cancelled.
|
| - if (request->status().status() != net::URLRequestStatus::SUCCESS)
|
| - return;
|
| -
|
| - int extra_info_spec = 0;
|
| - std::vector<const EventListener*> listeners =
|
| - GetMatchingListeners(browser_context, extension_info_map,
|
| - keys::kOnResponseStartedEvent, request,
|
| - &extra_info_spec);
|
| - if (listeners.empty())
|
| - return;
|
| -
|
| - // UrlRequestFileJobs do not send headers, so we simulate their behavior.
|
| - int response_code = 200;
|
| - if (request->response_headers())
|
| - response_code = request->response_headers()->response_code();
|
| -
|
| - std::string response_ip = request->GetSocketAddress().host();
|
| -
|
| - base::ListValue args;
|
| - base::DictionaryValue* dict = new base::DictionaryValue();
|
| - ExtractRequestInfo(request, dict);
|
| - if (!response_ip.empty())
|
| - dict->SetString(keys::kIpKey, response_ip);
|
| - dict->SetBoolean(keys::kFromCache, request->was_cached());
|
| - dict->SetInteger(keys::kStatusCodeKey, response_code);
|
| - dict->Set(keys::kStatusLineKey, GetStatusLine(request->response_headers()));
|
| - if (extra_info_spec & ExtraInfoSpec::RESPONSE_HEADERS) {
|
| - dict->Set(keys::kResponseHeadersKey,
|
| - GetResponseHeadersList(request->response_headers()));
|
| - }
|
| - args.Append(dict);
|
| -
|
| - DispatchEvent(browser_context, request, listeners, args);
|
| -}
|
| -
|
| -void ExtensionWebRequestEventRouter::OnCompleted(void* browser_context,
|
| - InfoMap* extension_info_map,
|
| - net::URLRequest* request) {
|
| - // We hide events from the system context as well as sensitive requests.
|
| - // However, if the request first became sensitive after redirecting we have
|
| - // already signaled it and thus we have to signal the end of it. This is
|
| - // risk-free because the handler cannot modify the request now.
|
| - if (!browser_context ||
|
| - (WebRequestPermissions::HideRequest(extension_info_map, request) &&
|
| - !WasSignaled(*request)))
|
| - return;
|
| -
|
| - request_time_tracker_->LogRequestEndTime(request->identifier(),
|
| - base::Time::Now());
|
| -
|
| - DCHECK(request->status().status() == net::URLRequestStatus::SUCCESS);
|
| -
|
| - DCHECK(!GetAndSetSignaled(request->identifier(), kOnCompleted));
|
| -
|
| - ClearPendingCallbacks(request);
|
| -
|
| - int extra_info_spec = 0;
|
| - std::vector<const EventListener*> listeners =
|
| - GetMatchingListeners(browser_context, extension_info_map,
|
| - keys::kOnCompletedEvent, request, &extra_info_spec);
|
| - if (listeners.empty())
|
| - return;
|
| -
|
| - // UrlRequestFileJobs do not send headers, so we simulate their behavior.
|
| - int response_code = 200;
|
| - if (request->response_headers())
|
| - response_code = request->response_headers()->response_code();
|
| -
|
| - std::string response_ip = request->GetSocketAddress().host();
|
| -
|
| - base::ListValue args;
|
| - base::DictionaryValue* dict = new base::DictionaryValue();
|
| - ExtractRequestInfo(request, dict);
|
| - dict->SetInteger(keys::kStatusCodeKey, response_code);
|
| - if (!response_ip.empty())
|
| - dict->SetString(keys::kIpKey, response_ip);
|
| - dict->SetBoolean(keys::kFromCache, request->was_cached());
|
| - dict->Set(keys::kStatusLineKey, GetStatusLine(request->response_headers()));
|
| - if (extra_info_spec & ExtraInfoSpec::RESPONSE_HEADERS) {
|
| - dict->Set(keys::kResponseHeadersKey,
|
| - GetResponseHeadersList(request->response_headers()));
|
| - }
|
| - args.Append(dict);
|
| -
|
| - DispatchEvent(browser_context, request, listeners, args);
|
| -}
|
| -
|
| -void ExtensionWebRequestEventRouter::OnErrorOccurred(
|
| - void* browser_context,
|
| - InfoMap* extension_info_map,
|
| - net::URLRequest* request,
|
| - bool started) {
|
| - // We hide events from the system context as well as sensitive requests.
|
| - // However, if the request first became sensitive after redirecting we have
|
| - // already signaled it and thus we have to signal the end of it. This is
|
| - // risk-free because the handler cannot modify the request now.
|
| - if (!browser_context ||
|
| - (WebRequestPermissions::HideRequest(extension_info_map, request) &&
|
| - !WasSignaled(*request)))
|
| - return;
|
| -
|
| - request_time_tracker_->LogRequestEndTime(request->identifier(),
|
| - base::Time::Now());
|
| -
|
| - DCHECK(request->status().status() == net::URLRequestStatus::FAILED ||
|
| - request->status().status() == net::URLRequestStatus::CANCELED);
|
| -
|
| - DCHECK(!GetAndSetSignaled(request->identifier(), kOnErrorOccurred));
|
| -
|
| - ClearPendingCallbacks(request);
|
| -
|
| - int extra_info_spec = 0;
|
| - std::vector<const EventListener*> listeners =
|
| - GetMatchingListeners(browser_context, extension_info_map,
|
| - web_request::OnErrorOccurred::kEventName, request,
|
| - &extra_info_spec);
|
| - if (listeners.empty())
|
| - return;
|
| -
|
| - base::ListValue args;
|
| - base::DictionaryValue* dict = new base::DictionaryValue();
|
| - ExtractRequestInfo(request, dict);
|
| - if (started) {
|
| - std::string response_ip = request->GetSocketAddress().host();
|
| - if (!response_ip.empty())
|
| - dict->SetString(keys::kIpKey, response_ip);
|
| - }
|
| - dict->SetBoolean(keys::kFromCache, request->was_cached());
|
| - dict->SetString(keys::kErrorKey,
|
| - net::ErrorToString(request->status().error()));
|
| - args.Append(dict);
|
| -
|
| - DispatchEvent(browser_context, request, listeners, args);
|
| -}
|
| -
|
| -void ExtensionWebRequestEventRouter::OnURLRequestDestroyed(
|
| - void* browser_context, net::URLRequest* request) {
|
| - ClearPendingCallbacks(request);
|
| -
|
| - signaled_requests_.erase(request->identifier());
|
| -
|
| - request_time_tracker_->LogRequestEndTime(request->identifier(),
|
| - base::Time::Now());
|
| -}
|
| -
|
| -void ExtensionWebRequestEventRouter::ClearPendingCallbacks(
|
| - net::URLRequest* request) {
|
| - blocked_requests_.erase(request->identifier());
|
| -}
|
| -
|
| -bool ExtensionWebRequestEventRouter::DispatchEvent(
|
| - void* browser_context,
|
| - net::URLRequest* request,
|
| - const std::vector<const EventListener*>& listeners,
|
| - const base::ListValue& args) {
|
| - // TODO(mpcomplete): Consider consolidating common (extension_id,json_args)
|
| - // pairs into a single message sent to a list of sub_event_names.
|
| - int num_handlers_blocking = 0;
|
| - for (std::vector<const EventListener*>::const_iterator it = listeners.begin();
|
| - it != listeners.end(); ++it) {
|
| - // Filter out the optional keys that this listener didn't request.
|
| - scoped_ptr<base::ListValue> args_filtered(args.DeepCopy());
|
| - base::DictionaryValue* dict = NULL;
|
| - CHECK(args_filtered->GetDictionary(0, &dict) && dict);
|
| - if (!((*it)->extra_info_spec & ExtraInfoSpec::REQUEST_HEADERS))
|
| - dict->Remove(keys::kRequestHeadersKey, NULL);
|
| - if (!((*it)->extra_info_spec & ExtraInfoSpec::RESPONSE_HEADERS))
|
| - dict->Remove(keys::kResponseHeadersKey, NULL);
|
| -
|
| - extensions::EventRouter::DispatchEvent(
|
| - (*it)->ipc_sender.get(), browser_context,
|
| - (*it)->extension_id, (*it)->sub_event_name,
|
| - args_filtered.Pass(),
|
| - extensions::EventRouter::USER_GESTURE_UNKNOWN,
|
| - extensions::EventFilteringInfo());
|
| - if ((*it)->extra_info_spec &
|
| - (ExtraInfoSpec::BLOCKING | ExtraInfoSpec::ASYNC_BLOCKING)) {
|
| - (*it)->blocked_requests.insert(request->identifier());
|
| - // If this is the first delegate blocking the request, go ahead and log
|
| - // it.
|
| - if (num_handlers_blocking == 0) {
|
| - std::string delegate_info =
|
| - l10n_util::GetStringFUTF8(IDS_LOAD_STATE_PARAMETER_EXTENSION,
|
| - base::UTF8ToUTF16((*it)->extension_name));
|
| - // LobAndReport allows extensions that block requests to be displayed in
|
| - // the load status bar.
|
| - request->LogAndReportBlockedBy(delegate_info.c_str());
|
| - }
|
| - ++num_handlers_blocking;
|
| - }
|
| - }
|
| -
|
| - if (num_handlers_blocking > 0) {
|
| - blocked_requests_[request->identifier()].request = request;
|
| - blocked_requests_[request->identifier()].is_incognito |=
|
| - IsIncognitoBrowserContext(browser_context);
|
| - blocked_requests_[request->identifier()].num_handlers_blocking +=
|
| - num_handlers_blocking;
|
| - blocked_requests_[request->identifier()].blocking_time = base::Time::Now();
|
| -
|
| - return true;
|
| - }
|
| -
|
| - return false;
|
| -}
|
| -
|
| -void ExtensionWebRequestEventRouter::OnEventHandled(
|
| - void* browser_context,
|
| - const std::string& extension_id,
|
| - const std::string& event_name,
|
| - const std::string& sub_event_name,
|
| - uint64 request_id,
|
| - EventResponse* response) {
|
| - EventListener listener;
|
| - listener.extension_id = extension_id;
|
| - listener.sub_event_name = sub_event_name;
|
| -
|
| - // The listener may have been removed (e.g. due to the process going away)
|
| - // before we got here.
|
| - std::set<EventListener>::iterator found =
|
| - listeners_[browser_context][event_name].find(listener);
|
| - if (found != listeners_[browser_context][event_name].end())
|
| - found->blocked_requests.erase(request_id);
|
| -
|
| - DecrementBlockCount(
|
| - browser_context, extension_id, event_name, request_id, response);
|
| -}
|
| -
|
| -bool ExtensionWebRequestEventRouter::AddEventListener(
|
| - void* browser_context,
|
| - const std::string& extension_id,
|
| - const std::string& extension_name,
|
| - const std::string& event_name,
|
| - const std::string& sub_event_name,
|
| - const RequestFilter& filter,
|
| - int extra_info_spec,
|
| - int embedder_process_id,
|
| - int webview_instance_id,
|
| - base::WeakPtr<IPC::Sender> ipc_sender) {
|
| - if (!IsWebRequestEvent(event_name))
|
| - return false;
|
| -
|
| - EventListener listener;
|
| - listener.extension_id = extension_id;
|
| - listener.extension_name = extension_name;
|
| - listener.sub_event_name = sub_event_name;
|
| - listener.filter = filter;
|
| - listener.extra_info_spec = extra_info_spec;
|
| - listener.ipc_sender = ipc_sender;
|
| - listener.embedder_process_id = embedder_process_id;
|
| - listener.webview_instance_id = webview_instance_id;
|
| - if (listener.webview_instance_id) {
|
| - content::RecordAction(
|
| - base::UserMetricsAction("WebView.WebRequest.AddListener"));
|
| - }
|
| -
|
| - if (listeners_[browser_context][event_name].count(listener) != 0u) {
|
| - // This is likely an abuse of the API by a malicious extension.
|
| - return false;
|
| - }
|
| - listeners_[browser_context][event_name].insert(listener);
|
| - return true;
|
| -}
|
| -
|
| -void ExtensionWebRequestEventRouter::RemoveEventListener(
|
| - void* browser_context,
|
| - const std::string& extension_id,
|
| - const std::string& sub_event_name) {
|
| - std::string event_name =
|
| - extensions::EventRouter::GetBaseEventName(sub_event_name);
|
| - DCHECK(IsWebRequestEvent(event_name));
|
| -
|
| - EventListener listener;
|
| - listener.extension_id = extension_id;
|
| - listener.sub_event_name = sub_event_name;
|
| -
|
| - // It's possible for AddEventListener to fail asynchronously. In that case,
|
| - // the renderer believes the listener exists, while the browser does not.
|
| - // Ignore a RemoveEventListener in that case.
|
| - std::set<EventListener>::iterator found =
|
| - listeners_[browser_context][event_name].find(listener);
|
| - if (found == listeners_[browser_context][event_name].end())
|
| - return;
|
| -
|
| - CHECK_EQ(listeners_[browser_context][event_name].count(listener), 1u) <<
|
| - "extension=" << extension_id << " event=" << event_name;
|
| -
|
| - // Unblock any request that this event listener may have been blocking.
|
| - for (std::set<uint64>::iterator it = found->blocked_requests.begin();
|
| - it != found->blocked_requests.end(); ++it) {
|
| - DecrementBlockCount(browser_context, extension_id, event_name, *it, NULL);
|
| - }
|
| -
|
| - listeners_[browser_context][event_name].erase(listener);
|
| -
|
| - helpers::ClearCacheOnNavigation();
|
| -}
|
| -
|
| -void ExtensionWebRequestEventRouter::RemoveWebViewEventListeners(
|
| - void* browser_context,
|
| - const std::string& extension_id,
|
| - int embedder_process_id,
|
| - int webview_instance_id) {
|
| - // Iterate over all listeners of all WebRequest events to delete
|
| - // any listeners that belong to the provided <webview>.
|
| - ListenerMapForBrowserContext& map_for_browser_context =
|
| - listeners_[browser_context];
|
| - for (ListenerMapForBrowserContext::iterator event_iter =
|
| - map_for_browser_context.begin();
|
| - event_iter != map_for_browser_context.end(); ++event_iter) {
|
| - std::vector<EventListener> listeners_to_delete;
|
| - std::set<EventListener>& listeners = event_iter->second;
|
| - for (std::set<EventListener>::iterator listener_iter = listeners.begin();
|
| - listener_iter != listeners.end(); ++listener_iter) {
|
| - const EventListener& listener = *listener_iter;
|
| - if (listener.embedder_process_id == embedder_process_id &&
|
| - listener.webview_instance_id == webview_instance_id)
|
| - listeners_to_delete.push_back(listener);
|
| - }
|
| - for (size_t i = 0; i < listeners_to_delete.size(); ++i) {
|
| - EventListener& listener = listeners_to_delete[i];
|
| - content::BrowserThread::PostTask(
|
| - content::BrowserThread::UI,
|
| - FROM_HERE,
|
| - base::Bind(&RemoveEventListenerOnUI,
|
| - browser_context,
|
| - listener.sub_event_name,
|
| - embedder_process_id,
|
| - extension_id));
|
| - }
|
| - }
|
| -}
|
| -
|
| -void ExtensionWebRequestEventRouter::OnOTRBrowserContextCreated(
|
| - void* original_browser_context, void* otr_browser_context) {
|
| - cross_browser_context_map_[original_browser_context] =
|
| - std::make_pair(false, otr_browser_context);
|
| - cross_browser_context_map_[otr_browser_context] =
|
| - std::make_pair(true, original_browser_context);
|
| -}
|
| -
|
| -void ExtensionWebRequestEventRouter::OnOTRBrowserContextDestroyed(
|
| - void* original_browser_context, void* otr_browser_context) {
|
| - cross_browser_context_map_.erase(otr_browser_context);
|
| - cross_browser_context_map_.erase(original_browser_context);
|
| -}
|
| -
|
| -void ExtensionWebRequestEventRouter::AddCallbackForPageLoad(
|
| - const base::Closure& callback) {
|
| - callbacks_for_page_load_.push_back(callback);
|
| -}
|
| -
|
| -bool ExtensionWebRequestEventRouter::IsPageLoad(
|
| - net::URLRequest* request) const {
|
| - bool is_main_frame = false;
|
| - int frame_id = -1;
|
| - bool parent_is_main_frame = false;
|
| - int parent_frame_id = -1;
|
| - int render_process_host_id = -1;
|
| - int routing_id = -1;
|
| - ResourceType resource_type = content::RESOURCE_TYPE_LAST_TYPE;
|
| -
|
| - ExtractRequestInfoDetails(request, &is_main_frame, &frame_id,
|
| - &parent_is_main_frame, &parent_frame_id,
|
| - &render_process_host_id,
|
| - &routing_id, &resource_type);
|
| -
|
| - return resource_type == content::RESOURCE_TYPE_MAIN_FRAME;
|
| -}
|
| -
|
| -void ExtensionWebRequestEventRouter::NotifyPageLoad() {
|
| - for (CallbacksForPageLoad::const_iterator i =
|
| - callbacks_for_page_load_.begin();
|
| - i != callbacks_for_page_load_.end(); ++i) {
|
| - i->Run();
|
| - }
|
| - callbacks_for_page_load_.clear();
|
| -}
|
| -
|
| -void* ExtensionWebRequestEventRouter::GetCrossBrowserContext(
|
| - void* browser_context) const {
|
| - CrossBrowserContextMap::const_iterator cross_browser_context =
|
| - cross_browser_context_map_.find(browser_context);
|
| - if (cross_browser_context == cross_browser_context_map_.end())
|
| - return NULL;
|
| - return cross_browser_context->second.second;
|
| -}
|
| -
|
| -bool ExtensionWebRequestEventRouter::IsIncognitoBrowserContext(
|
| - void* browser_context) const {
|
| - CrossBrowserContextMap::const_iterator cross_browser_context =
|
| - cross_browser_context_map_.find(browser_context);
|
| - if (cross_browser_context == cross_browser_context_map_.end())
|
| - return false;
|
| - return cross_browser_context->second.first;
|
| -}
|
| -
|
| -bool ExtensionWebRequestEventRouter::WasSignaled(
|
| - const net::URLRequest& request) const {
|
| - SignaledRequestMap::const_iterator flag =
|
| - signaled_requests_.find(request.identifier());
|
| - return (flag != signaled_requests_.end()) && (flag->second != 0);
|
| -}
|
| -
|
| -void ExtensionWebRequestEventRouter::GetMatchingListenersImpl(
|
| - void* browser_context,
|
| - net::URLRequest* request,
|
| - InfoMap* extension_info_map,
|
| - bool crosses_incognito,
|
| - const std::string& event_name,
|
| - const GURL& url,
|
| - int render_process_host_id,
|
| - int routing_id,
|
| - ResourceType resource_type,
|
| - bool is_async_request,
|
| - bool is_request_from_extension,
|
| - int* extra_info_spec,
|
| - std::vector<const ExtensionWebRequestEventRouter::EventListener*>*
|
| - matching_listeners) {
|
| - std::string web_request_event_name(event_name);
|
| - extensions::WebViewRendererState::WebViewInfo web_view_info;
|
| - bool is_web_view_guest = extensions::WebViewRendererState::GetInstance()->
|
| - GetInfo(render_process_host_id, routing_id, &web_view_info);
|
| - if (is_web_view_guest) {
|
| - web_request_event_name.replace(
|
| - 0, sizeof(kWebRequestEventPrefix) - 1, webview::kWebViewEventPrefix);
|
| - }
|
| -
|
| - std::set<EventListener>& listeners =
|
| - listeners_[browser_context][web_request_event_name];
|
| - for (std::set<EventListener>::iterator it = listeners.begin();
|
| - it != listeners.end(); ++it) {
|
| - if (!it->ipc_sender.get()) {
|
| - // The IPC sender has been deleted. This listener will be removed soon
|
| - // via a call to RemoveEventListener. For now, just skip it.
|
| - continue;
|
| - }
|
| -
|
| - if (is_web_view_guest &&
|
| - (it->embedder_process_id != web_view_info.embedder_process_id ||
|
| - it->webview_instance_id != web_view_info.instance_id))
|
| - continue;
|
| -
|
| - if (!it->filter.urls.is_empty() && !it->filter.urls.MatchesURL(url))
|
| - continue;
|
| - if (web_request_event_router_delegate_ &&
|
| - web_request_event_router_delegate_->OnGetMatchingListenersImplCheck(
|
| - it->filter.tab_id, it->filter.window_id, request))
|
| - continue;
|
| - if (!it->filter.types.empty() &&
|
| - std::find(it->filter.types.begin(), it->filter.types.end(),
|
| - resource_type) == it->filter.types.end())
|
| - continue;
|
| -
|
| - if (!is_web_view_guest && !WebRequestPermissions::CanExtensionAccessURL(
|
| - extension_info_map, it->extension_id, url, crosses_incognito,
|
| - WebRequestPermissions::REQUIRE_HOST_PERMISSION))
|
| - continue;
|
| -
|
| - bool blocking_listener =
|
| - (it->extra_info_spec &
|
| - (ExtraInfoSpec::BLOCKING | ExtraInfoSpec::ASYNC_BLOCKING)) != 0;
|
| -
|
| - // We do not want to notify extensions about XHR requests that are
|
| - // triggered by themselves. This is a workaround to prevent deadlocks
|
| - // in case of synchronous XHR requests that block the extension renderer
|
| - // and therefore prevent the extension from processing the request
|
| - // handler. This is only a problem for blocking listeners.
|
| - // http://crbug.com/105656
|
| - bool synchronous_xhr_from_extension =
|
| - !is_async_request && is_request_from_extension &&
|
| - resource_type == content::RESOURCE_TYPE_XHR;
|
| -
|
| - // Only send webRequest events for URLs the extension has access to.
|
| - if (blocking_listener && synchronous_xhr_from_extension)
|
| - continue;
|
| -
|
| - matching_listeners->push_back(&(*it));
|
| - *extra_info_spec |= it->extra_info_spec;
|
| - }
|
| -}
|
| -
|
| -std::vector<const ExtensionWebRequestEventRouter::EventListener*>
|
| -ExtensionWebRequestEventRouter::GetMatchingListeners(
|
| - void* browser_context,
|
| - InfoMap* extension_info_map,
|
| - const std::string& event_name,
|
| - net::URLRequest* request,
|
| - int* extra_info_spec) {
|
| - // TODO(mpcomplete): handle browser_context == NULL (should collect all
|
| - // listeners).
|
| - *extra_info_spec = 0;
|
| -
|
| - bool is_main_frame = false;
|
| - int frame_id = -1;
|
| - bool parent_is_main_frame = false;
|
| - int parent_frame_id = -1;
|
| - int render_process_host_id = -1;
|
| - int routing_id = -1;
|
| - ResourceType resource_type = content::RESOURCE_TYPE_LAST_TYPE;
|
| - const GURL& url = request->url();
|
| -
|
| - ExtractRequestInfoDetails(request, &is_main_frame, &frame_id,
|
| - &parent_is_main_frame, &parent_frame_id,
|
| - &render_process_host_id,
|
| - &routing_id, &resource_type);
|
| -
|
| - std::vector<const ExtensionWebRequestEventRouter::EventListener*>
|
| - matching_listeners;
|
| -
|
| - bool is_request_from_extension =
|
| - IsRequestFromExtension(request, extension_info_map);
|
| -
|
| - const ResourceRequestInfo* info = ResourceRequestInfo::ForRequest(request);
|
| - // We are conservative here and assume requests are asynchronous in case
|
| - // we don't have an info object. We don't want to risk a deadlock.
|
| - bool is_async_request = !info || info->IsAsync();
|
| -
|
| - GetMatchingListenersImpl(
|
| - browser_context, request, extension_info_map, false, event_name,
|
| - url, render_process_host_id, routing_id, resource_type,
|
| - is_async_request, is_request_from_extension, extra_info_spec,
|
| - &matching_listeners);
|
| - void* cross_browser_context = GetCrossBrowserContext(browser_context);
|
| - if (cross_browser_context) {
|
| - GetMatchingListenersImpl(
|
| - cross_browser_context, request, extension_info_map, true, event_name,
|
| - url, render_process_host_id, routing_id, resource_type,
|
| - is_async_request, is_request_from_extension, extra_info_spec,
|
| - &matching_listeners);
|
| - }
|
| -
|
| - return matching_listeners;
|
| -}
|
| -
|
| -namespace {
|
| -
|
| -helpers::EventResponseDelta* CalculateDelta(
|
| - ExtensionWebRequestEventRouter::BlockedRequest* blocked_request,
|
| - ExtensionWebRequestEventRouter::EventResponse* response) {
|
| - switch (blocked_request->event) {
|
| - case ExtensionWebRequestEventRouter::kOnBeforeRequest:
|
| - return helpers::CalculateOnBeforeRequestDelta(
|
| - response->extension_id, response->extension_install_time,
|
| - response->cancel, response->new_url);
|
| - case ExtensionWebRequestEventRouter::kOnBeforeSendHeaders: {
|
| - net::HttpRequestHeaders* old_headers = blocked_request->request_headers;
|
| - net::HttpRequestHeaders* new_headers = response->request_headers.get();
|
| - return helpers::CalculateOnBeforeSendHeadersDelta(
|
| - response->extension_id, response->extension_install_time,
|
| - response->cancel, old_headers, new_headers);
|
| - }
|
| - case ExtensionWebRequestEventRouter::kOnHeadersReceived: {
|
| - const net::HttpResponseHeaders* old_headers =
|
| - blocked_request->original_response_headers.get();
|
| - helpers::ResponseHeaders* new_headers =
|
| - response->response_headers.get();
|
| - return helpers::CalculateOnHeadersReceivedDelta(
|
| - response->extension_id,
|
| - response->extension_install_time,
|
| - response->cancel,
|
| - response->new_url,
|
| - old_headers,
|
| - new_headers);
|
| - }
|
| - case ExtensionWebRequestEventRouter::kOnAuthRequired:
|
| - return helpers::CalculateOnAuthRequiredDelta(
|
| - response->extension_id, response->extension_install_time,
|
| - response->cancel, &response->auth_credentials);
|
| - default:
|
| - NOTREACHED();
|
| - break;
|
| - }
|
| - return NULL;
|
| -}
|
| -
|
| -base::Value* SerializeResponseHeaders(const helpers::ResponseHeaders& headers) {
|
| - scoped_ptr<base::ListValue> serialized_headers(new base::ListValue());
|
| - for (helpers::ResponseHeaders::const_iterator i = headers.begin();
|
| - i != headers.end(); ++i) {
|
| - serialized_headers->Append(
|
| - helpers::CreateHeaderDictionary(i->first, i->second));
|
| - }
|
| - return serialized_headers.release();
|
| -}
|
| -
|
| -// Convert a RequestCookieModifications/ResponseCookieModifications object to a
|
| -// base::ListValue which summarizes the changes made. This is templated since
|
| -// the two types (request/response) are different but contain essentially the
|
| -// same fields.
|
| -template<typename CookieType>
|
| -base::ListValue* SummarizeCookieModifications(
|
| - const std::vector<linked_ptr<CookieType> >& modifications) {
|
| - scoped_ptr<base::ListValue> cookie_modifications(new base::ListValue());
|
| - for (typename std::vector<linked_ptr<CookieType> >::const_iterator i =
|
| - modifications.begin();
|
| - i != modifications.end(); ++i) {
|
| - scoped_ptr<base::DictionaryValue> summary(new base::DictionaryValue());
|
| - const CookieType& mod = *i->get();
|
| - switch (mod.type) {
|
| - case helpers::ADD:
|
| - summary->SetString(activitylog::kCookieModificationTypeKey,
|
| - activitylog::kCookieModificationAdd);
|
| - break;
|
| - case helpers::EDIT:
|
| - summary->SetString(activitylog::kCookieModificationTypeKey,
|
| - activitylog::kCookieModificationEdit);
|
| - break;
|
| - case helpers::REMOVE:
|
| - summary->SetString(activitylog::kCookieModificationTypeKey,
|
| - activitylog::kCookieModificationRemove);
|
| - break;
|
| - }
|
| - if (mod.filter) {
|
| - if (mod.filter->name)
|
| - summary->SetString(activitylog::kCookieFilterNameKey,
|
| - *mod.modification->name);
|
| - if (mod.filter->domain)
|
| - summary->SetString(activitylog::kCookieFilterDomainKey,
|
| - *mod.modification->name);
|
| - }
|
| - if (mod.modification) {
|
| - if (mod.modification->name)
|
| - summary->SetString(activitylog::kCookieModDomainKey,
|
| - *mod.modification->name);
|
| - if (mod.modification->domain)
|
| - summary->SetString(activitylog::kCookieModDomainKey,
|
| - *mod.modification->name);
|
| - }
|
| - cookie_modifications->Append(summary.release());
|
| - }
|
| - return cookie_modifications.release();
|
| -}
|
| -
|
| -// Converts an EventResponseDelta object to a dictionary value suitable for the
|
| -// activity log.
|
| -scoped_ptr<base::DictionaryValue> SummarizeResponseDelta(
|
| - const std::string& event_name,
|
| - const helpers::EventResponseDelta& delta) {
|
| - scoped_ptr<base::DictionaryValue> details(new base::DictionaryValue());
|
| - if (delta.cancel) {
|
| - details->SetBoolean(activitylog::kCancelKey, true);
|
| - }
|
| - if (!delta.new_url.is_empty()) {
|
| - details->SetString(activitylog::kNewUrlKey, delta.new_url.spec());
|
| - }
|
| -
|
| - scoped_ptr<base::ListValue> modified_headers(new base::ListValue());
|
| - net::HttpRequestHeaders::Iterator iter(delta.modified_request_headers);
|
| - while (iter.GetNext()) {
|
| - modified_headers->Append(
|
| - helpers::CreateHeaderDictionary(iter.name(), iter.value()));
|
| - }
|
| - if (!modified_headers->empty()) {
|
| - details->Set(activitylog::kModifiedRequestHeadersKey,
|
| - modified_headers.release());
|
| - }
|
| -
|
| - scoped_ptr<base::ListValue> deleted_headers(new base::ListValue());
|
| - deleted_headers->AppendStrings(delta.deleted_request_headers);
|
| - if (!deleted_headers->empty()) {
|
| - details->Set(activitylog::kDeletedRequestHeadersKey,
|
| - deleted_headers.release());
|
| - }
|
| -
|
| - if (!delta.added_response_headers.empty()) {
|
| - details->Set(activitylog::kAddedRequestHeadersKey,
|
| - SerializeResponseHeaders(delta.added_response_headers));
|
| - }
|
| - if (!delta.deleted_response_headers.empty()) {
|
| - details->Set(activitylog::kDeletedResponseHeadersKey,
|
| - SerializeResponseHeaders(delta.deleted_response_headers));
|
| - }
|
| - if (delta.auth_credentials) {
|
| - details->SetString(activitylog::kAuthCredentialsKey,
|
| - base::UTF16ToUTF8(
|
| - delta.auth_credentials->username()) + ":*");
|
| - }
|
| -
|
| - if (!delta.response_cookie_modifications.empty()) {
|
| - details->Set(
|
| - activitylog::kResponseCookieModificationsKey,
|
| - SummarizeCookieModifications(delta.response_cookie_modifications));
|
| - }
|
| -
|
| - return details.Pass();
|
| -}
|
| -
|
| -} // namespace
|
| -
|
| -void ExtensionWebRequestEventRouter::LogExtensionActivity(
|
| - void* browser_context_id,
|
| - bool is_incognito,
|
| - const std::string& extension_id,
|
| - const GURL& url,
|
| - const std::string& api_call,
|
| - scoped_ptr<base::DictionaryValue> details) {
|
| - if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
|
| - BrowserThread::PostTask(
|
| - BrowserThread::UI,
|
| - FROM_HERE,
|
| - base::Bind(&ExtensionWebRequestEventRouter::LogExtensionActivity,
|
| - base::Unretained(this),
|
| - browser_context_id,
|
| - is_incognito,
|
| - extension_id,
|
| - url,
|
| - api_call,
|
| - base::Passed(&details)));
|
| - } else {
|
| - if (web_request_event_router_delegate_) {
|
| - web_request_event_router_delegate_->LogExtensionActivity(
|
| - reinterpret_cast<content::BrowserContext*>(browser_context_id),
|
| - is_incognito, extension_id, url, api_call, details.Pass());
|
| - }
|
| - }
|
| -}
|
| -
|
| -void ExtensionWebRequestEventRouter::DecrementBlockCount(
|
| - void* browser_context,
|
| - const std::string& extension_id,
|
| - const std::string& event_name,
|
| - uint64 request_id,
|
| - EventResponse* response) {
|
| - scoped_ptr<EventResponse> response_scoped(response);
|
| -
|
| - // It's possible that this request was deleted, or cancelled by a previous
|
| - // event handler. If so, ignore this response.
|
| - if (blocked_requests_.find(request_id) == blocked_requests_.end())
|
| - return;
|
| -
|
| - BlockedRequest& blocked_request = blocked_requests_[request_id];
|
| - int num_handlers_blocking = --blocked_request.num_handlers_blocking;
|
| - CHECK_GE(num_handlers_blocking, 0);
|
| -
|
| - if (response) {
|
| - helpers::EventResponseDelta* delta =
|
| - CalculateDelta(&blocked_request, response);
|
| -
|
| - LogExtensionActivity(browser_context,
|
| - blocked_request.is_incognito,
|
| - extension_id,
|
| - blocked_request.request->url(),
|
| - event_name,
|
| - SummarizeResponseDelta(event_name, *delta));
|
| -
|
| - blocked_request.response_deltas.push_back(
|
| - linked_ptr<helpers::EventResponseDelta>(delta));
|
| - }
|
| -
|
| - base::TimeDelta block_time =
|
| - base::Time::Now() - blocked_request.blocking_time;
|
| - if (!extension_id.empty()) {
|
| - request_time_tracker_->IncrementExtensionBlockTime(
|
| - extension_id, request_id, block_time);
|
| - } else {
|
| - // |extension_id| is empty for requests blocked on startup waiting for the
|
| - // declarative rules to be read from disk.
|
| - UMA_HISTOGRAM_TIMES("Extensions.NetworkDelayStartup", block_time);
|
| - }
|
| -
|
| - if (num_handlers_blocking == 0) {
|
| - blocked_request.request->LogUnblocked();
|
| - ExecuteDeltas(browser_context, request_id, true);
|
| - } else {
|
| - // Update the URLRequest to make sure it's tagged with an extension that's
|
| - // still blocking it. This may end up being the same extension as before.
|
| - std::set<EventListener>& listeners =
|
| - listeners_[browser_context][event_name];
|
| -
|
| - for (std::set<EventListener>::iterator it = listeners.begin();
|
| - it != listeners.end(); ++it) {
|
| - if (it->blocked_requests.count(request_id) == 0)
|
| - continue;
|
| - std::string delegate_info =
|
| - l10n_util::GetStringFUTF8(IDS_LOAD_STATE_PARAMETER_EXTENSION,
|
| - base::UTF8ToUTF16(it->extension_name));
|
| - blocked_request.request->LogAndReportBlockedBy(delegate_info.c_str());
|
| - break;
|
| - }
|
| - }
|
| -}
|
| -
|
| -void ExtensionWebRequestEventRouter::SendMessages(
|
| - void* browser_context,
|
| - const BlockedRequest& blocked_request) {
|
| - const helpers::EventResponseDeltas& deltas = blocked_request.response_deltas;
|
| - for (helpers::EventResponseDeltas::const_iterator delta = deltas.begin();
|
| - delta != deltas.end(); ++delta) {
|
| - const std::set<std::string>& messages = (*delta)->messages_to_extension;
|
| - for (std::set<std::string>::const_iterator message = messages.begin();
|
| - message != messages.end(); ++message) {
|
| - scoped_ptr<base::DictionaryValue> argument(new base::DictionaryValue);
|
| - ExtractRequestInfo(blocked_request.request, argument.get());
|
| - extensions::WebViewRendererState::WebViewInfo web_view_info;
|
| - bool is_web_view_guest = GetWebViewInfo(blocked_request.request,
|
| - &web_view_info);
|
| - argument->SetString(keys::kMessageKey, *message);
|
| - argument->SetString(keys::kStageKey,
|
| - GetRequestStageAsString(blocked_request.event));
|
| -
|
| - BrowserThread::PostTask(
|
| - BrowserThread::UI,
|
| - FROM_HERE,
|
| - base::Bind(&SendOnMessageEventOnUI,
|
| - browser_context,
|
| - (*delta)->extension_id,
|
| - is_web_view_guest,
|
| - web_view_info,
|
| - base::Passed(&argument)));
|
| - }
|
| - }
|
| -}
|
| -
|
| -int ExtensionWebRequestEventRouter::ExecuteDeltas(
|
| - void* browser_context,
|
| - uint64 request_id,
|
| - bool call_callback) {
|
| - BlockedRequest& blocked_request = blocked_requests_[request_id];
|
| - CHECK(blocked_request.num_handlers_blocking == 0);
|
| - helpers::EventResponseDeltas& deltas = blocked_request.response_deltas;
|
| - base::TimeDelta block_time =
|
| - base::Time::Now() - blocked_request.blocking_time;
|
| - request_time_tracker_->IncrementTotalBlockTime(request_id, block_time);
|
| -
|
| - bool credentials_set = false;
|
| -
|
| - deltas.sort(&helpers::InDecreasingExtensionInstallationTimeOrder);
|
| - WarningSet warnings;
|
| -
|
| - bool canceled = false;
|
| - helpers::MergeCancelOfResponses(
|
| - blocked_request.response_deltas,
|
| - &canceled,
|
| - blocked_request.net_log);
|
| -
|
| - if (blocked_request.event == kOnBeforeRequest) {
|
| - CHECK(!blocked_request.callback.is_null());
|
| - helpers::MergeOnBeforeRequestResponses(
|
| - blocked_request.response_deltas,
|
| - blocked_request.new_url,
|
| - &warnings,
|
| - blocked_request.net_log);
|
| - } else if (blocked_request.event == kOnBeforeSendHeaders) {
|
| - CHECK(!blocked_request.callback.is_null());
|
| - helpers::MergeOnBeforeSendHeadersResponses(
|
| - blocked_request.response_deltas,
|
| - blocked_request.request_headers,
|
| - &warnings,
|
| - blocked_request.net_log);
|
| - } else if (blocked_request.event == kOnHeadersReceived) {
|
| - CHECK(!blocked_request.callback.is_null());
|
| - helpers::MergeOnHeadersReceivedResponses(
|
| - blocked_request.response_deltas,
|
| - blocked_request.original_response_headers.get(),
|
| - blocked_request.override_response_headers,
|
| - blocked_request.new_url,
|
| - &warnings,
|
| - blocked_request.net_log);
|
| - } else if (blocked_request.event == kOnAuthRequired) {
|
| - CHECK(blocked_request.callback.is_null());
|
| - CHECK(!blocked_request.auth_callback.is_null());
|
| - credentials_set = helpers::MergeOnAuthRequiredResponses(
|
| - blocked_request.response_deltas,
|
| - blocked_request.auth_credentials,
|
| - &warnings,
|
| - blocked_request.net_log);
|
| - } else {
|
| - NOTREACHED();
|
| - }
|
| -
|
| - SendMessages(browser_context, blocked_request);
|
| -
|
| - if (!warnings.empty()) {
|
| - BrowserThread::PostTask(
|
| - BrowserThread::UI,
|
| - FROM_HERE,
|
| - base::Bind(&WarningService::NotifyWarningsOnUI,
|
| - browser_context, warnings));
|
| - }
|
| -
|
| - if (canceled) {
|
| - request_time_tracker_->SetRequestCanceled(request_id);
|
| - } else if (blocked_request.new_url &&
|
| - !blocked_request.new_url->is_empty()) {
|
| - request_time_tracker_->SetRequestRedirected(request_id);
|
| - }
|
| -
|
| - // This triggers onErrorOccurred if canceled is true.
|
| - int rv = canceled ? net::ERR_BLOCKED_BY_CLIENT : net::OK;
|
| -
|
| - if (!blocked_request.callback.is_null()) {
|
| - net::CompletionCallback callback = blocked_request.callback;
|
| - // Ensure that request is removed before callback because the callback
|
| - // might trigger the next event.
|
| - blocked_requests_.erase(request_id);
|
| - if (call_callback)
|
| - callback.Run(rv);
|
| - } else if (!blocked_request.auth_callback.is_null()) {
|
| - net::NetworkDelegate::AuthRequiredResponse response =
|
| - net::NetworkDelegate::AUTH_REQUIRED_RESPONSE_NO_ACTION;
|
| - if (canceled) {
|
| - response = net::NetworkDelegate::AUTH_REQUIRED_RESPONSE_CANCEL_AUTH;
|
| - } else if (credentials_set) {
|
| - response = net::NetworkDelegate::AUTH_REQUIRED_RESPONSE_SET_AUTH;
|
| - }
|
| - net::NetworkDelegate::AuthCallback callback = blocked_request.auth_callback;
|
| - blocked_requests_.erase(request_id);
|
| - if (call_callback)
|
| - callback.Run(response);
|
| - } else {
|
| - blocked_requests_.erase(request_id);
|
| - }
|
| - return rv;
|
| -}
|
| -
|
| -bool ExtensionWebRequestEventRouter::ProcessDeclarativeRules(
|
| - void* browser_context,
|
| - InfoMap* extension_info_map,
|
| - const std::string& event_name,
|
| - net::URLRequest* request,
|
| - extensions::RequestStage request_stage,
|
| - const net::HttpResponseHeaders* original_response_headers) {
|
| - extensions::WebViewRendererState::WebViewInfo web_view_info;
|
| - bool is_web_view_guest = GetWebViewInfo(request, &web_view_info);
|
| -
|
| - extensions::RulesRegistry::WebViewKey webview_key(
|
| - is_web_view_guest ? web_view_info.embedder_process_id : 0,
|
| - is_web_view_guest ? web_view_info.instance_id : 0);
|
| - RulesRegistryKey rules_key(browser_context, webview_key);
|
| - // If this check fails, check that the active stages are up-to-date in
|
| - // extensions/browser/api/declarative_webrequest/request_stage.h .
|
| - DCHECK(request_stage & extensions::kActiveStages);
|
| -
|
| - // Rules of the current |browser_context| may apply but we need to check also
|
| - // whether there are applicable rules from extensions whose background page
|
| - // spans from regular to incognito mode.
|
| -
|
| - // First parameter identifies the registry, the second indicates whether the
|
| - // registry belongs to the cross browser_context.
|
| - typedef std::pair<extensions::WebRequestRulesRegistry*, bool>
|
| - RelevantRegistry;
|
| - typedef std::vector<RelevantRegistry> RelevantRegistries;
|
| - RelevantRegistries relevant_registries;
|
| -
|
| - if (rules_registries_.find(rules_key) != rules_registries_.end()) {
|
| - relevant_registries.push_back(
|
| - std::make_pair(rules_registries_[rules_key].get(), false));
|
| - }
|
| -
|
| - void* cross_browser_context = GetCrossBrowserContext(browser_context);
|
| - RulesRegistryKey cross_browser_context_rules_key(
|
| - cross_browser_context, webview_key);
|
| - if (cross_browser_context &&
|
| - rules_registries_.find(cross_browser_context_rules_key) !=
|
| - rules_registries_.end()) {
|
| - relevant_registries.push_back(
|
| - std::make_pair(
|
| - rules_registries_[cross_browser_context_rules_key].get(), true));
|
| - }
|
| -
|
| - // The following block is experimentally enabled and its impact on load time
|
| - // logged with UMA Extensions.NetworkDelayRegistryLoad. crbug.com/175961
|
| - for (RelevantRegistries::iterator i = relevant_registries.begin();
|
| - i != relevant_registries.end(); ++i) {
|
| - extensions::WebRequestRulesRegistry* rules_registry = i->first;
|
| - if (!rules_registry->ready().is_signaled()) {
|
| - // The rules registry is still loading. Block this request until it
|
| - // finishes.
|
| - rules_registry->ready().Post(
|
| - FROM_HERE,
|
| - base::Bind(&ExtensionWebRequestEventRouter::OnRulesRegistryReady,
|
| - AsWeakPtr(),
|
| - browser_context,
|
| - event_name,
|
| - request->identifier(),
|
| - request_stage));
|
| - blocked_requests_[request->identifier()].num_handlers_blocking++;
|
| - blocked_requests_[request->identifier()].request = request;
|
| - blocked_requests_[request->identifier()].is_incognito |=
|
| - IsIncognitoBrowserContext(browser_context);
|
| - blocked_requests_[request->identifier()].blocking_time =
|
| - base::Time::Now();
|
| - blocked_requests_[request->identifier()].original_response_headers =
|
| - original_response_headers;
|
| - blocked_requests_[request->identifier()].extension_info_map =
|
| - extension_info_map;
|
| - return true;
|
| - }
|
| - }
|
| -
|
| - base::Time start = base::Time::Now();
|
| -
|
| - bool deltas_created = false;
|
| - for (RelevantRegistries::iterator i = relevant_registries.begin();
|
| - i != relevant_registries.end(); ++i) {
|
| - extensions::WebRequestRulesRegistry* rules_registry =
|
| - i->first;
|
| - helpers::EventResponseDeltas result =
|
| - rules_registry->CreateDeltas(
|
| - extension_info_map,
|
| - extensions::WebRequestData(
|
| - request, request_stage, original_response_headers),
|
| - i->second);
|
| -
|
| - if (!result.empty()) {
|
| - helpers::EventResponseDeltas& deltas =
|
| - blocked_requests_[request->identifier()].response_deltas;
|
| - deltas.insert(deltas.end(), result.begin(), result.end());
|
| - deltas_created = true;
|
| - }
|
| - }
|
| -
|
| - base::TimeDelta elapsed_time = start - base::Time::Now();
|
| - UMA_HISTOGRAM_TIMES("Extensions.DeclarativeWebRequestNetworkDelay",
|
| - elapsed_time);
|
| -
|
| - return deltas_created;
|
| -}
|
| -
|
| -void ExtensionWebRequestEventRouter::OnRulesRegistryReady(
|
| - void* browser_context,
|
| - const std::string& event_name,
|
| - uint64 request_id,
|
| - extensions::RequestStage request_stage) {
|
| - // It's possible that this request was deleted, or cancelled by a previous
|
| - // event handler. If so, ignore this response.
|
| - if (blocked_requests_.find(request_id) == blocked_requests_.end())
|
| - return;
|
| -
|
| - BlockedRequest& blocked_request = blocked_requests_[request_id];
|
| - base::TimeDelta block_time =
|
| - base::Time::Now() - blocked_request.blocking_time;
|
| - UMA_HISTOGRAM_TIMES("Extensions.NetworkDelayRegistryLoad", block_time);
|
| -
|
| - ProcessDeclarativeRules(browser_context,
|
| - blocked_request.extension_info_map,
|
| - event_name,
|
| - blocked_request.request,
|
| - request_stage,
|
| - blocked_request.original_response_headers.get());
|
| - // Reset to NULL so that nobody relies on this being set.
|
| - blocked_request.extension_info_map = NULL;
|
| - DecrementBlockCount(
|
| - browser_context, std::string(), event_name, request_id, NULL);
|
| -}
|
| -
|
| -bool ExtensionWebRequestEventRouter::GetAndSetSignaled(uint64 request_id,
|
| - EventTypes event_type) {
|
| - SignaledRequestMap::iterator iter = signaled_requests_.find(request_id);
|
| - if (iter == signaled_requests_.end()) {
|
| - signaled_requests_[request_id] = event_type;
|
| - return false;
|
| - }
|
| - bool was_signaled_before = (iter->second & event_type) != 0;
|
| - iter->second |= event_type;
|
| - return was_signaled_before;
|
| -}
|
| -
|
| -void ExtensionWebRequestEventRouter::ClearSignaled(uint64 request_id,
|
| - EventTypes event_type) {
|
| - SignaledRequestMap::iterator iter = signaled_requests_.find(request_id);
|
| - if (iter == signaled_requests_.end())
|
| - return;
|
| - iter->second &= ~event_type;
|
| -}
|
| -
|
| -// Special QuotaLimitHeuristic for WebRequestHandlerBehaviorChangedFunction.
|
| -//
|
| -// Each call of webRequest.handlerBehaviorChanged() clears the in-memory cache
|
| -// of WebKit at the time of the next page load (top level navigation event).
|
| -// This quota heuristic is intended to limit the number of times the cache is
|
| -// cleared by an extension.
|
| -//
|
| -// As we want to account for the number of times the cache is really cleared
|
| -// (opposed to the number of times webRequest.handlerBehaviorChanged() is
|
| -// called), we cannot decide whether a call of
|
| -// webRequest.handlerBehaviorChanged() should trigger a quota violation at the
|
| -// time it is called. Instead we only decrement the bucket counter at the time
|
| -// when the cache is cleared (when page loads happen).
|
| -class ClearCacheQuotaHeuristic : public extensions::QuotaLimitHeuristic {
|
| - public:
|
| - ClearCacheQuotaHeuristic(const Config& config, BucketMapper* map)
|
| - : QuotaLimitHeuristic(
|
| - config,
|
| - map,
|
| - "MAX_HANDLER_BEHAVIOR_CHANGED_CALLS_PER_10_MINUTES"),
|
| - callback_registered_(false),
|
| - weak_ptr_factory_(this) {}
|
| - virtual ~ClearCacheQuotaHeuristic() {}
|
| - virtual bool Apply(Bucket* bucket,
|
| - const base::TimeTicks& event_time) OVERRIDE;
|
| -
|
| - private:
|
| - // Callback that is triggered by the ExtensionWebRequestEventRouter on a page
|
| - // load.
|
| - //
|
| - // We don't need to take care of the life time of |bucket|: It is owned by the
|
| - // BucketMapper of our base class in |QuotaLimitHeuristic::bucket_mapper_|. As
|
| - // long as |this| exists, the respective BucketMapper and its bucket will
|
| - // exist as well.
|
| - void OnPageLoad(Bucket* bucket);
|
| -
|
| - // Flag to prevent that we register more than one call back in-between
|
| - // clearing the cache.
|
| - bool callback_registered_;
|
| -
|
| - base::WeakPtrFactory<ClearCacheQuotaHeuristic> weak_ptr_factory_;
|
| -
|
| - DISALLOW_COPY_AND_ASSIGN(ClearCacheQuotaHeuristic);
|
| -};
|
| -
|
| -bool ClearCacheQuotaHeuristic::Apply(Bucket* bucket,
|
| - const base::TimeTicks& event_time) {
|
| - if (event_time > bucket->expiration())
|
| - bucket->Reset(config(), event_time);
|
| -
|
| - // Call bucket->DeductToken() on a new page load, this is when
|
| - // webRequest.handlerBehaviorChanged() clears the cache.
|
| - if (!callback_registered_) {
|
| - ExtensionWebRequestEventRouter::GetInstance()->AddCallbackForPageLoad(
|
| - base::Bind(&ClearCacheQuotaHeuristic::OnPageLoad,
|
| - weak_ptr_factory_.GetWeakPtr(),
|
| - bucket));
|
| - callback_registered_ = true;
|
| - }
|
| -
|
| - // We only check whether tokens are left here. Deducting a token happens in
|
| - // OnPageLoad().
|
| - return bucket->has_tokens();
|
| -}
|
| -
|
| -void ClearCacheQuotaHeuristic::OnPageLoad(Bucket* bucket) {
|
| - callback_registered_ = false;
|
| - bucket->DeductToken();
|
| -}
|
| -
|
| -bool WebRequestInternalAddEventListenerFunction::RunSync() {
|
| - // Argument 0 is the callback, which we don't use here.
|
| - ExtensionWebRequestEventRouter::RequestFilter filter;
|
| - base::DictionaryValue* value = NULL;
|
| - error_.clear();
|
| - EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(1, &value));
|
| - // Failure + an empty error string means a fatal error.
|
| - EXTENSION_FUNCTION_VALIDATE(filter.InitFromValue(*value, &error_) ||
|
| - !error_.empty());
|
| - if (!error_.empty())
|
| - return false;
|
| -
|
| - int extra_info_spec = 0;
|
| - if (HasOptionalArgument(2)) {
|
| - base::ListValue* value = NULL;
|
| - EXTENSION_FUNCTION_VALIDATE(args_->GetList(2, &value));
|
| - EXTENSION_FUNCTION_VALIDATE(
|
| - ExtensionWebRequestEventRouter::ExtraInfoSpec::InitFromValue(
|
| - *value, &extra_info_spec));
|
| - }
|
| -
|
| - std::string event_name;
|
| - EXTENSION_FUNCTION_VALIDATE(args_->GetString(3, &event_name));
|
| -
|
| - std::string sub_event_name;
|
| - EXTENSION_FUNCTION_VALIDATE(args_->GetString(4, &sub_event_name));
|
| -
|
| - int webview_instance_id = 0;
|
| - EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(5, &webview_instance_id));
|
| -
|
| - base::WeakPtr<extensions::ExtensionMessageFilter> ipc_sender =
|
| - ipc_sender_weak();
|
| - int embedder_process_id =
|
| - ipc_sender.get() ? ipc_sender->render_process_id() : -1;
|
| -
|
| - const Extension* extension =
|
| - extension_info_map()->extensions().GetByID(extension_id());
|
| - std::string extension_name = extension ? extension->name() : extension_id();
|
| -
|
| - bool is_web_view_guest = webview_instance_id != 0;
|
| - // We check automatically whether the extension has the 'webRequest'
|
| - // permission. For blocking calls we require the additional permission
|
| - // 'webRequestBlocking'.
|
| - if ((!is_web_view_guest &&
|
| - extra_info_spec &
|
| - (ExtensionWebRequestEventRouter::ExtraInfoSpec::BLOCKING |
|
| - ExtensionWebRequestEventRouter::ExtraInfoSpec::ASYNC_BLOCKING)) &&
|
| - !extension->permissions_data()->HasAPIPermission(
|
| - extensions::APIPermission::kWebRequestBlocking)) {
|
| - error_ = keys::kBlockingPermissionRequired;
|
| - return false;
|
| - }
|
| -
|
| - // We allow to subscribe to patterns that are broader than the host
|
| - // permissions. E.g., we could subscribe to http://www.example.com/*
|
| - // while having host permissions for http://www.example.com/foo/* and
|
| - // http://www.example.com/bar/*.
|
| - // For this reason we do only a coarse check here to warn the extension
|
| - // developer if he does something obviously wrong.
|
| - if (!is_web_view_guest &&
|
| - extension->permissions_data()->GetEffectiveHostPermissions().is_empty()) {
|
| - error_ = keys::kHostPermissionsRequired;
|
| - return false;
|
| - }
|
| -
|
| - bool success =
|
| - ExtensionWebRequestEventRouter::GetInstance()->AddEventListener(
|
| - profile_id(), extension_id(), extension_name,
|
| - event_name, sub_event_name, filter, extra_info_spec,
|
| - embedder_process_id, webview_instance_id, ipc_sender_weak());
|
| - EXTENSION_FUNCTION_VALIDATE(success);
|
| -
|
| - helpers::ClearCacheOnNavigation();
|
| -
|
| - BrowserThread::PostTask(BrowserThread::UI,
|
| - FROM_HERE,
|
| - base::Bind(&helpers::NotifyWebRequestAPIUsed,
|
| - profile_id(),
|
| - make_scoped_refptr(extension)));
|
| -
|
| - return true;
|
| -}
|
| -
|
| -void WebRequestInternalEventHandledFunction::RespondWithError(
|
| - const std::string& event_name,
|
| - const std::string& sub_event_name,
|
| - uint64 request_id,
|
| - scoped_ptr<ExtensionWebRequestEventRouter::EventResponse> response,
|
| - const std::string& error) {
|
| - error_ = error;
|
| - ExtensionWebRequestEventRouter::GetInstance()->OnEventHandled(
|
| - profile_id(),
|
| - extension_id(),
|
| - event_name,
|
| - sub_event_name,
|
| - request_id,
|
| - response.release());
|
| -}
|
| -
|
| -bool WebRequestInternalEventHandledFunction::RunSync() {
|
| - std::string event_name;
|
| - EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &event_name));
|
| -
|
| - std::string sub_event_name;
|
| - EXTENSION_FUNCTION_VALIDATE(args_->GetString(1, &sub_event_name));
|
| -
|
| - std::string request_id_str;
|
| - EXTENSION_FUNCTION_VALIDATE(args_->GetString(2, &request_id_str));
|
| - uint64 request_id;
|
| - EXTENSION_FUNCTION_VALIDATE(base::StringToUint64(request_id_str,
|
| - &request_id));
|
| -
|
| - scoped_ptr<ExtensionWebRequestEventRouter::EventResponse> response;
|
| - if (HasOptionalArgument(3)) {
|
| - base::DictionaryValue* value = NULL;
|
| - EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(3, &value));
|
| -
|
| - if (!value->empty()) {
|
| - base::Time install_time =
|
| - extension_info_map()->GetInstallTime(extension_id());
|
| - response.reset(new ExtensionWebRequestEventRouter::EventResponse(
|
| - extension_id(), install_time));
|
| - }
|
| -
|
| - if (value->HasKey("cancel")) {
|
| - // Don't allow cancel mixed with other keys.
|
| - if (value->size() != 1) {
|
| - RespondWithError(event_name,
|
| - sub_event_name,
|
| - request_id,
|
| - response.Pass(),
|
| - keys::kInvalidBlockingResponse);
|
| - return false;
|
| - }
|
| -
|
| - bool cancel = false;
|
| - EXTENSION_FUNCTION_VALIDATE(value->GetBoolean("cancel", &cancel));
|
| - response->cancel = cancel;
|
| - }
|
| -
|
| - if (value->HasKey("redirectUrl")) {
|
| - std::string new_url_str;
|
| - EXTENSION_FUNCTION_VALIDATE(value->GetString("redirectUrl",
|
| - &new_url_str));
|
| - response->new_url = GURL(new_url_str);
|
| - if (!response->new_url.is_valid()) {
|
| - RespondWithError(event_name,
|
| - sub_event_name,
|
| - request_id,
|
| - response.Pass(),
|
| - ErrorUtils::FormatErrorMessage(
|
| - keys::kInvalidRedirectUrl, new_url_str));
|
| - return false;
|
| - }
|
| - }
|
| -
|
| - const bool hasRequestHeaders = value->HasKey("requestHeaders");
|
| - const bool hasResponseHeaders = value->HasKey("responseHeaders");
|
| - if (hasRequestHeaders || hasResponseHeaders) {
|
| - if (hasRequestHeaders && hasResponseHeaders) {
|
| - // Allow only one of the keys, not both.
|
| - RespondWithError(event_name,
|
| - sub_event_name,
|
| - request_id,
|
| - response.Pass(),
|
| - keys::kInvalidHeaderKeyCombination);
|
| - return false;
|
| - }
|
| -
|
| - base::ListValue* headers_value = NULL;
|
| - scoped_ptr<net::HttpRequestHeaders> request_headers;
|
| - scoped_ptr<helpers::ResponseHeaders> response_headers;
|
| - if (hasRequestHeaders) {
|
| - request_headers.reset(new net::HttpRequestHeaders());
|
| - EXTENSION_FUNCTION_VALIDATE(value->GetList(keys::kRequestHeadersKey,
|
| - &headers_value));
|
| - } else {
|
| - response_headers.reset(new helpers::ResponseHeaders());
|
| - EXTENSION_FUNCTION_VALIDATE(value->GetList(keys::kResponseHeadersKey,
|
| - &headers_value));
|
| - }
|
| -
|
| - for (size_t i = 0; i < headers_value->GetSize(); ++i) {
|
| - base::DictionaryValue* header_value = NULL;
|
| - std::string name;
|
| - std::string value;
|
| - EXTENSION_FUNCTION_VALIDATE(
|
| - headers_value->GetDictionary(i, &header_value));
|
| - if (!FromHeaderDictionary(header_value, &name, &value)) {
|
| - std::string serialized_header;
|
| - base::JSONWriter::Write(header_value, &serialized_header);
|
| - RespondWithError(event_name,
|
| - sub_event_name,
|
| - request_id,
|
| - response.Pass(),
|
| - ErrorUtils::FormatErrorMessage(keys::kInvalidHeader,
|
| - serialized_header));
|
| - return false;
|
| - }
|
| - if (!net::HttpUtil::IsValidHeaderName(name)) {
|
| - RespondWithError(event_name,
|
| - sub_event_name,
|
| - request_id,
|
| - response.Pass(),
|
| - keys::kInvalidHeaderName);
|
| - return false;
|
| - }
|
| - if (!net::HttpUtil::IsValidHeaderValue(value)) {
|
| - RespondWithError(event_name,
|
| - sub_event_name,
|
| - request_id,
|
| - response.Pass(),
|
| - ErrorUtils::FormatErrorMessage(
|
| - keys::kInvalidHeaderValue, name));
|
| - return false;
|
| - }
|
| - if (hasRequestHeaders)
|
| - request_headers->SetHeader(name, value);
|
| - else
|
| - response_headers->push_back(helpers::ResponseHeader(name, value));
|
| - }
|
| - if (hasRequestHeaders)
|
| - response->request_headers.reset(request_headers.release());
|
| - else
|
| - response->response_headers.reset(response_headers.release());
|
| - }
|
| -
|
| - if (value->HasKey(keys::kAuthCredentialsKey)) {
|
| - base::DictionaryValue* credentials_value = NULL;
|
| - EXTENSION_FUNCTION_VALIDATE(value->GetDictionary(
|
| - keys::kAuthCredentialsKey,
|
| - &credentials_value));
|
| - base::string16 username;
|
| - base::string16 password;
|
| - EXTENSION_FUNCTION_VALIDATE(
|
| - credentials_value->GetString(keys::kUsernameKey, &username));
|
| - EXTENSION_FUNCTION_VALIDATE(
|
| - credentials_value->GetString(keys::kPasswordKey, &password));
|
| - response->auth_credentials.reset(
|
| - new net::AuthCredentials(username, password));
|
| - }
|
| - }
|
| -
|
| - ExtensionWebRequestEventRouter::GetInstance()->OnEventHandled(
|
| - profile_id(), extension_id(), event_name, sub_event_name, request_id,
|
| - response.release());
|
| -
|
| - return true;
|
| -}
|
| -
|
| -void WebRequestHandlerBehaviorChangedFunction::GetQuotaLimitHeuristics(
|
| - extensions::QuotaLimitHeuristics* heuristics) const {
|
| - extensions::QuotaLimitHeuristic::Config config = {
|
| - // See web_request.json for current value.
|
| - web_request::MAX_HANDLER_BEHAVIOR_CHANGED_CALLS_PER_10_MINUTES,
|
| - base::TimeDelta::FromMinutes(10)};
|
| - extensions::QuotaLimitHeuristic::BucketMapper* bucket_mapper =
|
| - new extensions::QuotaLimitHeuristic::SingletonBucketMapper();
|
| - ClearCacheQuotaHeuristic* heuristic =
|
| - new ClearCacheQuotaHeuristic(config, bucket_mapper);
|
| - heuristics->push_back(heuristic);
|
| -}
|
| -
|
| -void WebRequestHandlerBehaviorChangedFunction::OnQuotaExceeded(
|
| - const std::string& violation_error) {
|
| - // Post warning message.
|
| - WarningSet warnings;
|
| - warnings.insert(
|
| - Warning::CreateRepeatedCacheFlushesWarning(extension_id()));
|
| - BrowserThread::PostTask(
|
| - BrowserThread::UI,
|
| - FROM_HERE,
|
| - base::Bind(&WarningService::NotifyWarningsOnUI, profile_id(), warnings));
|
| -
|
| - // Continue gracefully.
|
| - RunSync();
|
| -}
|
| -
|
| -bool WebRequestHandlerBehaviorChangedFunction::RunSync() {
|
| - helpers::ClearCacheOnNavigation();
|
| - return true;
|
| -}
|
|
|