summaryrefslogtreecommitdiff
path: root/chromium/extensions/browser/api/web_request
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@qt.io>2020-10-12 14:27:29 +0200
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2020-10-13 09:35:20 +0000
commitc30a6232df03e1efbd9f3b226777b07e087a1122 (patch)
treee992f45784689f373bcc38d1b79a239ebe17ee23 /chromium/extensions/browser/api/web_request
parent7b5b123ac58f58ffde0f4f6e488bcd09aa4decd3 (diff)
downloadqtwebengine-chromium-85-based.tar.gz
BASELINE: Update Chromium to 85.0.4183.14085-based
Change-Id: Iaa42f4680837c57725b1344f108c0196741f6057 Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
Diffstat (limited to 'chromium/extensions/browser/api/web_request')
-rw-r--r--chromium/extensions/browser/api/web_request/BUILD.gn1
-rw-r--r--chromium/extensions/browser/api/web_request/web_request_api.cc75
-rw-r--r--chromium/extensions/browser/api/web_request/web_request_api_helpers.cc309
-rw-r--r--chromium/extensions/browser/api/web_request/web_request_api_helpers.h17
-rw-r--r--chromium/extensions/browser/api/web_request/web_request_proxying_url_loader_factory.cc230
-rw-r--r--chromium/extensions/browser/api/web_request/web_request_proxying_url_loader_factory.h51
6 files changed, 544 insertions, 139 deletions
diff --git a/chromium/extensions/browser/api/web_request/BUILD.gn b/chromium/extensions/browser/api/web_request/BUILD.gn
index 117c87d9f72..ac64cd54722 100644
--- a/chromium/extensions/browser/api/web_request/BUILD.gn
+++ b/chromium/extensions/browser/api/web_request/BUILD.gn
@@ -43,6 +43,7 @@ source_set("web_request") {
]
deps = [
+ "//components/ukm/content",
"//components/web_cache/browser",
"//content/public/browser",
"//content/public/common",
diff --git a/chromium/extensions/browser/api/web_request/web_request_api.cc b/chromium/extensions/browser/api/web_request/web_request_api.cc
index 50db1d2502b..7c5498c16b1 100644
--- a/chromium/extensions/browser/api/web_request/web_request_api.cc
+++ b/chromium/extensions/browser/api/web_request/web_request_api.cc
@@ -26,6 +26,7 @@
#include "base/task/post_task.h"
#include "base/time/time.h"
#include "base/values.h"
+#include "components/ukm/content/source_url_recorder.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
@@ -689,11 +690,11 @@ bool WebRequestAPI::MaybeProxyURLLoaderFactory(
mojo::PendingRemote<network::mojom::TrustedURLLoaderHeaderClient>*
header_client) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ auto* web_contents = content::WebContents::FromRenderFrameHost(frame);
if (!MayHaveProxies()) {
bool skip_proxy = true;
// There are a few internal WebUIs that use WebView tag that are whitelisted
// for webRequest.
- auto* web_contents = content::WebContents::FromRenderFrameHost(frame);
if (web_contents && WebViewGuest::IsGuest(web_contents)) {
auto* guest_web_contents =
WebViewGuest::GetTopLevelWebContents(web_contents);
@@ -742,12 +743,15 @@ bool WebRequestAPI::MaybeProxyURLLoaderFactory(
(browser_context->IsOffTheRecord() &&
ExtensionsBrowserClient::Get()->GetOriginalContext(browser_context) ==
browser_context_));
+ const ukm::SourceId ukm_source_id =
+ web_contents ? ukm::GetSourceIdForWebContentsDocument(web_contents)
+ : ukm::kInvalidSourceId;
WebRequestProxyingURLLoaderFactory::StartProxying(
browser_context, is_navigation ? -1 : render_process_id,
&request_id_generator_, std::move(navigation_ui_data),
std::move(navigation_id), std::move(proxied_receiver),
std::move(target_factory_remote), std::move(header_client_receiver),
- proxies_.get(), type);
+ proxies_.get(), type, ukm_source_id);
return true;
}
@@ -893,10 +897,9 @@ struct ExtensionWebRequestEventRouter::BlockedRequest {
// for OnBeforeSendHeaders.
net::HttpRequestHeaders* request_headers = nullptr;
- // The response headers that were received from the server and subsequently
- // filtered by the Declarative Net Request API. Only valid for
+ // The response headers that were received from the server. Only valid for
// OnHeadersReceived.
- scoped_refptr<const net::HttpResponseHeaders> filtered_response_headers;
+ scoped_refptr<const net::HttpResponseHeaders> original_response_headers;
// Location where to override response headers. Only valid for
// OnHeadersReceived.
@@ -1104,8 +1107,14 @@ int ExtensionWebRequestEventRouter::OnBeforeRequest(
*new_url = action.redirect_url.value();
return net::OK;
case DNRRequestAction::Type::MODIFY_HEADERS:
- // TODO(crbug.com/947591): Evaluate modify headers DNR actions.
- NOTREACHED();
+ // Unlike other actions, allow web request extensions to intercept the
+ // request here. The headers will be modified during subsequent request
+ // stages.
+ DCHECK(std::all_of(request->dnr_actions->begin(),
+ request->dnr_actions->end(), [](const auto& action) {
+ return action.type ==
+ DNRRequestAction::Type::MODIFY_HEADERS;
+ }));
break;
}
}
@@ -1136,15 +1145,20 @@ int ExtensionWebRequestEventRouter::OnBeforeSendHeaders(
if (ShouldHideEvent(browser_context, *request))
return net::OK;
- // TODO(crbug.com/947591): Handle request header modification by the
- // Declarative Net Request API.
-
bool initialize_blocked_requests = false;
initialize_blocked_requests |=
ProcessDeclarativeRules(browser_context, keys::kOnBeforeSendHeadersEvent,
request, ON_BEFORE_SEND_HEADERS, nullptr);
+ DCHECK(request->dnr_actions);
+ initialize_blocked_requests |= std::any_of(
+ request->dnr_actions->begin(), request->dnr_actions->end(),
+ [](const DNRRequestAction& action) {
+ return action.type == DNRRequestAction::Type::MODIFY_HEADERS &&
+ !action.request_headers_to_modify.empty();
+ });
+
int extra_info_spec = 0;
RawListeners listeners =
GetMatchingListeners(browser_context, keys::kOnBeforeSendHeadersEvent,
@@ -1217,16 +1231,19 @@ int ExtensionWebRequestEventRouter::OnHeadersReceived(
if (ShouldHideEvent(browser_context, *request))
return net::OK;
- // TODO(crbug.com/947591): Handle header modification by the Declarative Net
- // Request API.
- scoped_refptr<const net::HttpResponseHeaders> filtered_response_headers =
- original_response_headers;
-
bool initialize_blocked_requests = false;
+ DCHECK(request->dnr_actions);
+ initialize_blocked_requests |= std::any_of(
+ request->dnr_actions->begin(), request->dnr_actions->end(),
+ [](const DNRRequestAction& action) {
+ return action.type == DNRRequestAction::Type::MODIFY_HEADERS &&
+ !action.response_headers_to_modify.empty();
+ });
+
initialize_blocked_requests |= ProcessDeclarativeRules(
browser_context, keys::kOnHeadersReceivedEvent, request,
- ON_HEADERS_RECEIVED, filtered_response_headers.get());
+ ON_HEADERS_RECEIVED, original_response_headers);
int extra_info_spec = 0;
RawListeners listeners =
@@ -1237,8 +1254,7 @@ int ExtensionWebRequestEventRouter::OnHeadersReceived(
!GetAndSetSignaled(request->id, kOnHeadersReceived)) {
std::unique_ptr<WebRequestEventDetails> event_details(
CreateEventDetails(*request, extra_info_spec));
- event_details->SetResponseHeaders(*request,
- filtered_response_headers.get());
+ event_details->SetResponseHeaders(*request, original_response_headers);
initialize_blocked_requests |= DispatchEvent(
browser_context, request, listeners, std::move(event_details));
@@ -1258,7 +1274,7 @@ int ExtensionWebRequestEventRouter::OnHeadersReceived(
blocked_request.request = request;
blocked_request.callback = std::move(callback);
blocked_request.override_response_headers = override_response_headers;
- blocked_request.filtered_response_headers = filtered_response_headers;
+ blocked_request.original_response_headers = original_response_headers;
blocked_request.new_url = preserve_fragment_on_redirect_url;
if (blocked_request.num_handlers_blocking == 0) {
@@ -2014,7 +2030,7 @@ helpers::EventResponseDelta CalculateDelta(
}
case ExtensionWebRequestEventRouter::kOnHeadersReceived: {
const net::HttpResponseHeaders* old_headers =
- blocked_request->filtered_response_headers.get();
+ blocked_request->original_response_headers.get();
helpers::ResponseHeaders* new_headers =
response->response_headers.get();
return helpers::CalculateOnHeadersReceivedDelta(
@@ -2229,6 +2245,7 @@ int ExtensionWebRequestEventRouter::ExecuteDeltas(
helpers::MergeCancelOfResponses(blocked_request.response_deltas, &canceled);
extension_web_request_api_helpers::IgnoredActions ignored_actions;
+ std::vector<const DNRRequestAction*> matched_dnr_actions;
if (blocked_request.event == kOnBeforeRequest) {
CHECK(!blocked_request.callback.is_null());
helpers::MergeOnBeforeRequestResponses(
@@ -2240,15 +2257,14 @@ int ExtensionWebRequestEventRouter::ExecuteDeltas(
*request, blocked_request.response_deltas,
blocked_request.request_headers, &ignored_actions,
&request_headers_removed, &request_headers_set,
- &request_headers_modified);
-
+ &request_headers_modified, &matched_dnr_actions);
} else if (blocked_request.event == kOnHeadersReceived) {
CHECK(!blocked_request.callback.is_null());
helpers::MergeOnHeadersReceivedResponses(
*request, blocked_request.response_deltas,
- blocked_request.filtered_response_headers.get(),
+ blocked_request.original_response_headers.get(),
blocked_request.override_response_headers, blocked_request.new_url,
- &ignored_actions, &response_headers_modified);
+ &ignored_actions, &response_headers_modified, &matched_dnr_actions);
} else if (blocked_request.event == kOnAuthRequired) {
CHECK(blocked_request.callback.is_null());
CHECK(!blocked_request.auth_callback.is_null());
@@ -2266,6 +2282,9 @@ int ExtensionWebRequestEventRouter::ExecuteDeltas(
std::move(ignored_actions));
}
+ for (const DNRRequestAction* action : matched_dnr_actions)
+ OnDNRActionMatched(browser_context, *request, *action);
+
const bool redirected =
blocked_request.new_url && !blocked_request.new_url->is_empty();
@@ -2330,7 +2349,7 @@ bool ExtensionWebRequestEventRouter::ProcessDeclarativeRules(
const std::string& event_name,
const WebRequestInfo* request,
RequestStage request_stage,
- const net::HttpResponseHeaders* filtered_response_headers) {
+ const net::HttpResponseHeaders* original_response_headers) {
int rules_registry_id = request->is_web_view
? request->web_view_rules_registry_id
: RulesRegistryService::kDefaultRulesRegistryID;
@@ -2384,7 +2403,7 @@ bool ExtensionWebRequestEventRouter::ProcessDeclarativeRules(
blocked_request.request = request;
blocked_request.is_incognito |= IsIncognitoBrowserContext(browser_context);
blocked_request.blocking_time = base::Time::Now();
- blocked_request.filtered_response_headers = filtered_response_headers;
+ blocked_request.original_response_headers = original_response_headers;
return true;
}
@@ -2393,7 +2412,7 @@ bool ExtensionWebRequestEventRouter::ProcessDeclarativeRules(
WebRequestRulesRegistry* rules_registry = it.first;
helpers::EventResponseDeltas result = rules_registry->CreateDeltas(
PermissionHelper::Get(browser_context),
- WebRequestData(request, request_stage, filtered_response_headers),
+ WebRequestData(request, request_stage, original_response_headers),
it.second);
if (!result.empty()) {
@@ -2422,7 +2441,7 @@ void ExtensionWebRequestEventRouter::OnRulesRegistryReady(
BlockedRequest& blocked_request = it->second;
ProcessDeclarativeRules(browser_context, event_name, blocked_request.request,
request_stage,
- blocked_request.filtered_response_headers.get());
+ blocked_request.original_response_headers.get());
DecrementBlockCount(browser_context, std::string(), event_name, request_id,
nullptr, 0 /* extra_info_spec */);
}
diff --git a/chromium/extensions/browser/api/web_request/web_request_api_helpers.cc b/chromium/extensions/browser/api/web_request/web_request_api_helpers.cc
index 2e5874d5ac8..58012fe6fe5 100644
--- a/chromium/extensions/browser/api/web_request/web_request_api_helpers.cc
+++ b/chromium/extensions/browser/api/web_request/web_request_api_helpers.cc
@@ -23,7 +23,6 @@
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
-#include "base/task/post_task.h"
#include "base/time/time.h"
#include "base/values.h"
#include "components/web_cache/browser/web_cache_manager.h"
@@ -31,6 +30,7 @@
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/render_process_host.h"
+#include "extensions/browser/api/declarative_net_request/request_action.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_info.h"
@@ -38,6 +38,7 @@
#include "extensions/browser/extension_system.h"
#include "extensions/browser/extensions_browser_client.h"
#include "extensions/browser/runtime_data.h"
+#include "extensions/common/api/declarative_net_request.h"
#include "extensions/common/extension_messages.h"
#include "net/cookies/cookie_util.h"
#include "net/cookies/parsed_cookie.h"
@@ -56,11 +57,13 @@ using net::cookie_util::ParsedRequestCookies;
namespace keys = extension_web_request_api_constants;
namespace web_request = extensions::api::web_request;
+using DNRRequestAction = extensions::declarative_net_request::RequestAction;
namespace extension_web_request_api_helpers {
namespace {
+namespace dnr_api = extensions::api::declarative_net_request;
using ParsedResponseCookies = std::vector<std::unique_ptr<net::ParsedCookie>>;
void ClearCacheOnNavigationOnUI() {
@@ -329,20 +332,178 @@ static_assert(static_cast<size_t>(ResponseHeaderType::kMaxValue) - 1 ==
static_assert(ValidateHeaderEntries(kResponseHeaderEntries),
"Invalid response header entries");
-bool HasMatchingRemovedDNRRequestHeader(
- const extensions::WebRequestInfo& request,
- const std::string& header) {
- // TODO(crbug.com/947591): Reimplement this method with
- // |action.request_headers_to_modify|.
- return false;
+// Represents an action to be taken on a given header.
+struct DNRHeaderAction {
+ DNRHeaderAction(const DNRRequestAction::HeaderInfo* header_info,
+ const extensions::ExtensionId* extension_id)
+ : header_info(header_info), extension_id(extension_id) {}
+
+ // Returns whether for the same header, the operation specified by
+ // |next_action| conflicts with the operation specified by this action.
+ bool ConflictsWithSubsequentAction(const DNRHeaderAction& next_action) const {
+ DCHECK_EQ(header_info->header, next_action.header_info->header);
+
+ switch (header_info->operation) {
+ case dnr_api::HEADER_OPERATION_APPEND:
+ return next_action.header_info->operation !=
+ dnr_api::HEADER_OPERATION_APPEND;
+ case dnr_api::HEADER_OPERATION_SET:
+ return *extension_id != *next_action.extension_id ||
+ next_action.header_info->operation !=
+ dnr_api::HEADER_OPERATION_APPEND;
+ case dnr_api::HEADER_OPERATION_REMOVE:
+ return true;
+ case dnr_api::HEADER_OPERATION_NONE:
+ NOTREACHED();
+ return true;
+ }
+ }
+
+ // Non-owning pointers to HeaderInfo and ExtensionId.
+ const DNRRequestAction::HeaderInfo* header_info;
+ const extensions::ExtensionId* extension_id;
+};
+
+// Helper to modify request headers from
+// |request_action.request_headers_to_modify|. Returns whether or not request
+// headers were actually modified and modifies |removed_headers|, |set_headers|
+// and |header_actions|. |header_actions| maps a header name to the operation
+// to be performed on the header.
+bool ModifyRequestHeadersForAction(
+ net::HttpRequestHeaders* headers,
+ const DNRRequestAction& request_action,
+ std::set<std::string>* removed_headers,
+ std::set<std::string>* set_headers,
+ std::map<base::StringPiece, DNRHeaderAction>* header_actions) {
+ bool request_headers_modified = false;
+ for (const DNRRequestAction::HeaderInfo& header_info :
+ request_action.request_headers_to_modify) {
+ bool header_modified = false;
+ const std::string& header = header_info.header;
+
+ DNRHeaderAction header_action(&header_info, &request_action.extension_id);
+ auto iter = header_actions->find(header);
+ if (iter != header_actions->end() &&
+ iter->second.ConflictsWithSubsequentAction(header_action)) {
+ continue;
+ }
+ header_actions->emplace(header, header_action);
+
+ // TODO(crbug.com/1088103): Record request headers modified by the
+ // Declarative Net Request API.
+ switch (header_info.operation) {
+ case extensions::api::declarative_net_request::HEADER_OPERATION_SET:
+ headers->SetHeader(header, *header_info.value);
+ header_modified = true;
+ set_headers->insert(header);
+ break;
+ case extensions::api::declarative_net_request::HEADER_OPERATION_REMOVE: {
+ while (headers->HasHeader(header)) {
+ header_modified = true;
+ headers->RemoveHeader(header);
+ }
+
+ if (header_modified)
+ removed_headers->insert(header);
+ break;
+ }
+ case extensions::api::declarative_net_request::HEADER_OPERATION_APPEND:
+ case extensions::api::declarative_net_request::HEADER_OPERATION_NONE:
+ NOTREACHED();
+ }
+
+ request_headers_modified |= header_modified;
+ }
+
+ return request_headers_modified;
}
-bool HasMatchingRemovedDNRResponseHeader(
- const extensions::WebRequestInfo& request,
- const std::string& header) {
- // TODO(crbug.com/947591): Reimplement this method with
- // |action.response_headers_to_modify|.
- return false;
+// Helper to modify response headers from |request_action|. Returns whether or
+// not response headers were actually modified and modifies |header_actions|.
+// |header_actions| maps a header name to a list of operations to be performed
+// on the header.
+bool ModifyResponseHeadersForAction(
+ const net::HttpResponseHeaders* original_response_headers,
+ scoped_refptr<net::HttpResponseHeaders>* override_response_headers,
+ const DNRRequestAction& request_action,
+ std::map<base::StringPiece, std::vector<DNRHeaderAction>>* header_actions) {
+ bool response_headers_modified = false;
+
+ // Check for |header| in |override_response_headers| if headers have been
+ // modified, otherwise, check in |original_response_headers|.
+ auto has_header = [&original_response_headers,
+ &override_response_headers](std::string header) {
+ return override_response_headers->get()
+ ? override_response_headers->get()->HasHeader(header)
+ : original_response_headers->HasHeader(header);
+ };
+
+ // Create a copy of |original_response_headers| iff we really want to modify
+ // the response headers.
+ auto create_override_headers_if_needed =
+ [&original_response_headers](
+ scoped_refptr<net::HttpResponseHeaders>* override_response_headers) {
+ if (override_response_headers->get() == nullptr) {
+ *override_response_headers =
+ base::MakeRefCounted<net::HttpResponseHeaders>(
+ original_response_headers->raw_headers());
+ }
+ };
+
+ for (const DNRRequestAction::HeaderInfo& header_info :
+ request_action.response_headers_to_modify) {
+ bool header_modified = false;
+ const std::string& header = header_info.header;
+
+ DNRHeaderAction header_action(&header_info, &request_action.extension_id);
+ auto iter = header_actions->find(header);
+
+ // Checking the first DNRHeaderAction should suffice for determining if a
+ // conflict exists, since the contents of |header_actions| for a given
+ // header will always be one of:
+ // [remove]
+ // [append+] one or more appends
+ // [set, append*] set, any number of appends from the same extension
+ if (iter != header_actions->end() &&
+ (*header_actions)[header][0].ConflictsWithSubsequentAction(
+ header_action)) {
+ continue;
+ }
+ (*header_actions)[header].push_back(header_action);
+
+ // TODO(crbug.com/1088103): Record response headers modified by the
+ // Declarative Net Request API.
+ switch (header_info.operation) {
+ case extensions::api::declarative_net_request::HEADER_OPERATION_REMOVE: {
+ if (has_header(header)) {
+ header_modified = true;
+ create_override_headers_if_needed(override_response_headers);
+ override_response_headers->get()->RemoveHeader(header);
+ }
+
+ break;
+ }
+ case extensions::api::declarative_net_request::HEADER_OPERATION_APPEND: {
+ header_modified = true;
+ create_override_headers_if_needed(override_response_headers);
+ override_response_headers->get()->AddHeader(header, *header_info.value);
+ break;
+ }
+ case extensions::api::declarative_net_request::HEADER_OPERATION_SET: {
+ header_modified = true;
+ create_override_headers_if_needed(override_response_headers);
+ override_response_headers->get()->RemoveHeader(header);
+ override_response_headers->get()->AddHeader(header, *header_info.value);
+ break;
+ }
+ case extensions::api::declarative_net_request::HEADER_OPERATION_NONE:
+ NOTREACHED();
+ }
+
+ response_headers_modified |= header_modified;
+ }
+
+ return response_headers_modified;
}
} // namespace
@@ -920,16 +1081,36 @@ void MergeOnBeforeSendHeadersResponses(
IgnoredActions* ignored_actions,
std::set<std::string>* removed_headers,
std::set<std::string>* set_headers,
- bool* request_headers_modified) {
+ bool* request_headers_modified,
+ std::vector<const DNRRequestAction*>* matched_dnr_actions) {
DCHECK(request_headers_modified);
DCHECK(removed_headers->empty());
DCHECK(set_headers->empty());
+ DCHECK(request.dnr_actions);
+ DCHECK(matched_dnr_actions);
*request_headers_modified = false;
- // Exhaustive subsets of |set_headers|. Split into a set for added headers and
- // a set for overridden headers.
- std::set<std::string> overridden_headers;
- std::set<std::string> added_headers;
+ std::map<base::StringPiece, DNRHeaderAction> dnr_header_actions;
+ for (const auto& action : *request.dnr_actions) {
+ bool headers_modified_for_action =
+ ModifyRequestHeadersForAction(request_headers, action, removed_headers,
+ set_headers, &dnr_header_actions);
+
+ *request_headers_modified |= headers_modified_for_action;
+ if (headers_modified_for_action)
+ matched_dnr_actions->push_back(&action);
+ }
+
+ // A strict subset of |removed_headers| consisting of headers removed by the
+ // web request API. Used for metrics.
+ // TODO(crbug.com/1098945): Use base::StringPiece to avoid copying header
+ // names.
+ std::set<std::string> web_request_removed_headers;
+
+ // Subsets of |set_headers| consisting of headers modified by the web request
+ // API. Split into a set for added headers and a set for overridden headers.
+ std::set<std::string> web_request_overridden_headers;
+ std::set<std::string> web_request_added_headers;
// We assume here that the deltas are sorted in decreasing extension
// precedence (i.e. decreasing extension installation time).
@@ -951,16 +1132,20 @@ void MergeOnBeforeSendHeadersResponses(
const std::string key = base::ToLowerASCII(modification.name());
const std::string& value = modification.value();
- // We must not modify anything that has been deleted before.
- if (base::Contains(*removed_headers, key)) {
+ // We must not modify anything that was specified to be removed by the
+ // Declarative Net Request API. Note that the actual header
+ // modifications made by Declarative Net Request should be represented
+ // in |removed_headers| and |set_headers|.
+ auto iter = dnr_header_actions.find(key);
+ if (iter != dnr_header_actions.end() &&
+ iter->second.header_info->operation ==
+ dnr_api::HEADER_OPERATION_REMOVE) {
extension_conflicts = true;
break;
}
- // Prevent extensions from adding any header removed by the Declarative
- // Net Request API.
- DCHECK(request.dnr_actions);
- if (HasMatchingRemovedDNRRequestHeader(request, key)) {
+ // We must not modify anything that has been deleted before.
+ if (base::Contains(*removed_headers, key)) {
extension_conflicts = true;
break;
}
@@ -998,11 +1183,11 @@ void MergeOnBeforeSendHeadersResponses(
while (modification.GetNext()) {
std::string key = base::ToLowerASCII(modification.name());
if (!request_headers->HasHeader(key)) {
- added_headers.insert(key);
- } else if (!base::Contains(added_headers, key)) {
+ web_request_added_headers.insert(key);
+ } else if (!base::Contains(web_request_added_headers, key)) {
// Note: |key| will only be present in |added_headers| if this is an
// identical edit.
- overridden_headers.insert(key);
+ web_request_overridden_headers.insert(key);
}
set_headers->insert(key);
@@ -1013,8 +1198,11 @@ void MergeOnBeforeSendHeadersResponses(
// Perform all deletions and record which keys were deleted.
{
for (const auto& header : delta.deleted_request_headers) {
+ std::string lowercase_header = base::ToLowerASCII(header);
+
request_headers->RemoveHeader(header);
- removed_headers->insert(base::ToLowerASCII(header));
+ removed_headers->insert(lowercase_header);
+ web_request_removed_headers.insert(lowercase_header);
}
}
*request_headers_modified = true;
@@ -1039,23 +1227,24 @@ void MergeOnBeforeSendHeadersResponses(
IsStringLowerCaseASCII));
DCHECK(std::all_of(set_headers->begin(), set_headers->end(),
IsStringLowerCaseASCII));
- DCHECK(std::all_of(overridden_headers.begin(), overridden_headers.end(),
- IsStringLowerCaseASCII));
- DCHECK(std::all_of(added_headers.begin(), added_headers.end(),
- IsStringLowerCaseASCII));
- DCHECK(*set_headers == base::STLSetUnion<std::set<std::string>>(
- added_headers, overridden_headers));
- DCHECK(base::STLSetIntersection<std::set<std::string>>(added_headers,
- overridden_headers)
+ DCHECK(base::STLIncludes(
+ *set_headers,
+ base::STLSetUnion<std::set<std::string>>(
+ web_request_added_headers, web_request_overridden_headers)));
+ DCHECK(base::STLSetIntersection<std::set<std::string>>(
+ web_request_added_headers, web_request_overridden_headers)
.empty());
DCHECK(base::STLSetIntersection<std::set<std::string>>(*removed_headers,
*set_headers)
.empty());
+ DCHECK(base::STLIncludes(*removed_headers, web_request_removed_headers));
// Record request header removals, additions and modifications.
- record_request_headers(*removed_headers, &RecordRequestHeaderRemoved);
- record_request_headers(added_headers, &RecordRequestHeaderAdded);
- record_request_headers(overridden_headers, &RecordRequestHeaderChanged);
+ record_request_headers(web_request_removed_headers,
+ &RecordRequestHeaderRemoved);
+ record_request_headers(web_request_added_headers, &RecordRequestHeaderAdded);
+ record_request_headers(web_request_overridden_headers,
+ &RecordRequestHeaderChanged);
// Currently, conflicts are ignored while merging cookies.
MergeCookiesInOnBeforeSendHeadersResponses(request.url, deltas,
@@ -1290,10 +1479,25 @@ void MergeOnHeadersReceivedResponses(
scoped_refptr<net::HttpResponseHeaders>* override_response_headers,
GURL* preserve_fragment_on_redirect_url,
IgnoredActions* ignored_actions,
- bool* response_headers_modified) {
+ bool* response_headers_modified,
+ std::vector<const DNRRequestAction*>* matched_dnr_actions) {
DCHECK(response_headers_modified);
*response_headers_modified = false;
+ DCHECK(request.dnr_actions);
+ DCHECK(matched_dnr_actions);
+
+ std::map<base::StringPiece, std::vector<DNRHeaderAction>> dnr_header_actions;
+ for (const auto& action : *request.dnr_actions) {
+ bool headers_modified_for_action = ModifyResponseHeadersForAction(
+ original_response_headers, override_response_headers, action,
+ &dnr_header_actions);
+
+ *response_headers_modified |= headers_modified_for_action;
+ if (headers_modified_for_action)
+ matched_dnr_actions->push_back(&action);
+ }
+
// Here we collect which headers we have removed or added so far due to
// extensions of higher precedence. Header keys are always stored as
// lower case.
@@ -1322,18 +1526,28 @@ void MergeOnHeadersReceivedResponses(
// this takes care of precedence.
bool extension_conflicts = false;
for (const ResponseHeader& header : delta.deleted_response_headers) {
- if (removed_headers.find(ToLowerCase(header)) != removed_headers.end()) {
+ ResponseHeader lowercase_header(ToLowerCase(header));
+ if (base::Contains(removed_headers, lowercase_header) ||
+ base::Contains(dnr_header_actions, lowercase_header.first)) {
extension_conflicts = true;
break;
}
}
- // Prevent extensions from adding any response header which was removed by
- // the Declarative Net Request API.
- DCHECK(request.dnr_actions);
+ // Prevent extensions from adding any response header which was specified to
+ // be removed or set by the Declarative Net Request API. However, multiple
+ // appends are allowed.
if (!extension_conflicts) {
for (const ResponseHeader& header : delta.added_response_headers) {
- if (HasMatchingRemovedDNRResponseHeader(request, header.first)) {
+ ResponseHeader lowercase_header(ToLowerCase(header));
+
+ auto it = dnr_header_actions.find(lowercase_header.first);
+ if (it == dnr_header_actions.end())
+ continue;
+
+ // Multiple appends are allowed.
+ if (it->second[0].header_info->operation !=
+ dnr_api::HEADER_OPERATION_APPEND) {
extension_conflicts = true;
break;
}
@@ -1406,6 +1620,7 @@ void MergeOnHeadersReceivedResponses(
std::set<base::StringPiece> modified_header_names;
std::set<base::StringPiece> added_header_names;
std::set<base::StringPiece> removed_header_names;
+
for (const ResponseHeader& header : added_headers) {
// Skip logging this header if this was subsequently removed by an
// extension.
@@ -1467,8 +1682,8 @@ void ClearCacheOnNavigation() {
if (content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)) {
ClearCacheOnNavigationOnUI();
} else {
- base::PostTask(FROM_HERE, {content::BrowserThread::UI},
- base::BindOnce(&ClearCacheOnNavigationOnUI));
+ content::GetUIThreadTaskRunner({})->PostTask(
+ FROM_HERE, base::BindOnce(&ClearCacheOnNavigationOnUI));
}
}
diff --git a/chromium/extensions/browser/api/web_request/web_request_api_helpers.h b/chromium/extensions/browser/api/web_request/web_request_api_helpers.h
index a90a34b999b..cd7d01a62cc 100644
--- a/chromium/extensions/browser/api/web_request/web_request_api_helpers.h
+++ b/chromium/extensions/browser/api/web_request/web_request_api_helpers.h
@@ -38,6 +38,11 @@ class BrowserContext;
namespace extensions {
class Extension;
struct WebRequestInfo;
+
+namespace declarative_net_request {
+struct RequestAction;
+} // namespace declarative_net_request
+
} // namespace extensions
namespace extension_web_request_api_helpers {
@@ -435,6 +440,8 @@ void MergeCookiesInOnBeforeSendHeadersResponses(
// are tried to be resolved.
// Stores in |request_headers_modified| whether the request headers were
// modified.
+// Any actions within |request.dnr_actions| which result in headers being
+// modified are added to |matched_dnr_actions|.
void MergeOnBeforeSendHeadersResponses(
const extensions::WebRequestInfo& request,
const EventResponseDeltas& deltas,
@@ -442,7 +449,9 @@ void MergeOnBeforeSendHeadersResponses(
IgnoredActions* ignored_actions,
std::set<std::string>* removed_headers,
std::set<std::string>* set_headers,
- bool* request_headers_modified);
+ bool* request_headers_modified,
+ std::vector<const extensions::declarative_net_request::RequestAction*>*
+ matched_dnr_actions);
// Modifies the "Set-Cookie" headers in |override_response_headers| according to
// |deltas.response_cookie_modifications|. If |override_response_headers| is
// NULL, a copy of |original_response_headers| is created. Conflicts are
@@ -460,6 +469,8 @@ void MergeCookiesInOnHeadersReceivedResponses(
// sure that the URL provided by the extension isn't modified by having its
// fragment overwritten by that of the original URL). Stores in
// |response_headers_modified| whether the response headers were modified.
+// Any actions within |request.dnr_actions| which result in headers being
+// modified are added to |matched_dnr_actions|.
void MergeOnHeadersReceivedResponses(
const extensions::WebRequestInfo& request,
const EventResponseDeltas& deltas,
@@ -467,7 +478,9 @@ void MergeOnHeadersReceivedResponses(
scoped_refptr<net::HttpResponseHeaders>* override_response_headers,
GURL* preserve_fragment_on_redirect_url,
IgnoredActions* ignored_actions,
- bool* response_headers_modified);
+ bool* response_headers_modified,
+ std::vector<const extensions::declarative_net_request::RequestAction*>*
+ matched_dnr_actions);
// Merge the responses of blocked onAuthRequired handlers. The first
// registered listener that supplies authentication credentials in a response,
// if any, will have its authentication credentials used. |request| must be
diff --git a/chromium/extensions/browser/api/web_request/web_request_proxying_url_loader_factory.cc b/chromium/extensions/browser/api/web_request/web_request_proxying_url_loader_factory.cc
index dbb25b166a8..e26a21f26b6 100644
--- a/chromium/extensions/browser/api/web_request/web_request_proxying_url_loader_factory.cc
+++ b/chromium/extensions/browser/api/web_request/web_request_proxying_url_loader_factory.cc
@@ -10,6 +10,7 @@
#include "base/bind.h"
#include "base/callback_helpers.h"
#include "base/feature_list.h"
+#include "base/metrics/histogram_macros.h"
#include "base/strings/stringprintf.h"
#include "base/task/post_task.h"
#include "components/keyed_service/content/browser_context_keyed_service_shutdown_notifier_factory.h"
@@ -21,12 +22,18 @@
#include "extensions/browser/api/web_request/permission_helper.h"
#include "extensions/browser/extension_navigation_ui_data.h"
#include "extensions/browser/extension_registry.h"
+#include "extensions/common/extension_features.h"
#include "extensions/common/manifest_handlers/web_accessible_resources_info.h"
#include "net/base/completion_repeating_callback.h"
+#include "net/http/http_response_headers.h"
+#include "net/http/http_status_code.h"
#include "net/http/http_util.h"
#include "net/url_request/redirect_info.h"
#include "net/url_request/redirect_util.h"
#include "net/url_request/url_request.h"
+#include "services/metrics/public/cpp/ukm_builders.h"
+#include "services/metrics/public/cpp/ukm_recorder.h"
+#include "services/metrics/public/cpp/ukm_source_id.h"
#include "services/network/public/cpp/features.h"
#include "third_party/blink/public/common/loader/throttling_url_loader.h"
#include "third_party/blink/public/platform/resource_request_blocked_reason.h"
@@ -91,6 +98,7 @@ WebRequestProxyingURLLoaderFactory::InProgressRequest::InProgressRequest(
int32_t network_service_request_id,
int32_t routing_id,
uint32_t options,
+ ukm::SourceId ukm_source_id,
const network::ResourceRequest& request,
const net::MutableNetworkTrafficAnnotationTag& traffic_annotation,
mojo::PendingReceiver<network::mojom::URLLoader> loader_receiver,
@@ -102,6 +110,7 @@ WebRequestProxyingURLLoaderFactory::InProgressRequest::InProgressRequest(
network_service_request_id_(network_service_request_id),
routing_id_(routing_id),
options_(options),
+ ukm_source_id_(ukm_source_id),
traffic_annotation_(traffic_annotation),
proxied_loader_receiver_(this, std::move(loader_receiver)),
target_client_(std::move(client)),
@@ -111,10 +120,10 @@ WebRequestProxyingURLLoaderFactory::InProgressRequest::InProgressRequest(
ExtensionWebRequestEventRouter::GetInstance()
->HasAnyExtraHeadersListener(factory_->browser_context_)) {
// If there is a client error, clean up the request.
- target_client_.set_disconnect_handler(base::BindOnce(
- &WebRequestProxyingURLLoaderFactory::InProgressRequest::OnRequestError,
- weak_factory_.GetWeakPtr(),
- network::URLLoaderCompletionStatus(net::ERR_ABORTED)));
+ target_client_.set_disconnect_handler(
+ base::BindOnce(&WebRequestProxyingURLLoaderFactory::InProgressRequest::
+ OnClientDisconnected,
+ weak_factory_.GetWeakPtr()));
proxied_loader_receiver_.set_disconnect_with_reason_handler(
base::BindOnce(&WebRequestProxyingURLLoaderFactory::InProgressRequest::
OnLoaderDisconnected,
@@ -129,6 +138,7 @@ WebRequestProxyingURLLoaderFactory::InProgressRequest::InProgressRequest(
request_(request),
original_initiator_(request.request_initiator),
request_id_(request_id),
+ ukm_source_id_(ukm::kInvalidSourceId),
proxied_loader_receiver_(this),
for_cors_preflight_(true),
has_any_extra_headers_listeners_(
@@ -136,6 +146,18 @@ WebRequestProxyingURLLoaderFactory::InProgressRequest::InProgressRequest(
->HasAnyExtraHeadersListener(factory_->browser_context_)) {}
WebRequestProxyingURLLoaderFactory::InProgressRequest::~InProgressRequest() {
+ DCHECK_NE(state_, State::kInvalid);
+ if (request_.keepalive && !for_cors_preflight_) {
+ UMA_HISTOGRAM_ENUMERATION("Extensions.WebRequest.KeepaliveRequestState",
+ state_);
+ if (base::FeatureList::IsEnabled(
+ extensions_features::kReportKeepaliveUkm)) {
+ ukm::builders::Extensions_WebRequest_KeepaliveRequestFinished(
+ ukm_source_id_)
+ .SetState(state_)
+ .Record(ukm::UkmRecorder::Get());
+ }
+ }
// This is important to ensure that no outstanding blocking requests continue
// to reference state owned by this object.
if (info_) {
@@ -186,20 +208,23 @@ void WebRequestProxyingURLLoaderFactory::InProgressRequest::
void WebRequestProxyingURLLoaderFactory::InProgressRequest::RestartInternal() {
DCHECK_EQ(info_->url, request_.url)
<< "UpdateRequestInfo must have been called first";
+ is_header_client_receiver_paused_ = false;
// If the header client will be used, we start the request immediately, and
// OnBeforeSendHeaders and OnSendHeaders will be handled there. Otherwise,
// send these events before the request starts.
base::RepeatingCallback<void(int)> continuation;
+ const auto state_on_error = State::kRejectedByOnBeforeRequest;
if (current_request_uses_header_client_) {
- continuation = base::BindRepeating(
- &InProgressRequest::ContinueToStartRequest, weak_factory_.GetWeakPtr());
+ continuation =
+ base::BindRepeating(&InProgressRequest::ContinueToStartRequest,
+ weak_factory_.GetWeakPtr(), state_on_error);
} else if (for_cors_preflight_) {
// In this case we do nothing because extensions should see nothing.
return;
} else {
continuation =
base::BindRepeating(&InProgressRequest::ContinueToBeforeSendHeaders,
- weak_factory_.GetWeakPtr());
+ weak_factory_.GetWeakPtr(), state_on_error);
}
redirect_url_ = GURL();
bool should_collapse_initiator = false;
@@ -214,7 +239,7 @@ void WebRequestProxyingURLLoaderFactory::InProgressRequest::RestartInternal() {
status.extended_error_code = static_cast<int>(
blink::ResourceRequestBlockedReason::kCollapsedByClient);
}
- OnRequestError(status);
+ OnRequestError(status, state_on_error);
return;
}
@@ -229,8 +254,10 @@ void WebRequestProxyingURLLoaderFactory::InProgressRequest::RestartInternal() {
// Pause the header client, since we want to wait until OnBeforeRequest has
// finished before processing any future events.
- if (header_client_receiver_.is_bound())
+ if (header_client_receiver_.is_bound()) {
header_client_receiver_.Pause();
+ is_header_client_receiver_paused_ = true;
+ }
return;
}
DCHECK_EQ(net::OK, result);
@@ -318,7 +345,7 @@ void WebRequestProxyingURLLoaderFactory::InProgressRequest::OnReceiveRedirect(
if (redirect_url_ != redirect_info.new_url &&
!IsRedirectSafe(request_.url, redirect_info.new_url,
info_->is_navigation_request)) {
- OnRequestError(
+ OnNetworkError(
network::URLLoaderCompletionStatus(net::ERR_UNSAFE_REDIRECT));
return;
}
@@ -368,10 +395,11 @@ void WebRequestProxyingURLLoaderFactory::InProgressRequest::
void WebRequestProxyingURLLoaderFactory::InProgressRequest::OnComplete(
const network::URLLoaderCompletionStatus& status) {
if (status.error_code != net::OK) {
- OnRequestError(status);
+ OnNetworkError(status);
return;
}
+ state_ = kCompleted;
target_client_->OnComplete(status);
ExtensionWebRequestEventRouter::GetInstance()->OnCompleted(
factory_->browser_context_, &info_.value(), status.error_code);
@@ -422,12 +450,14 @@ void WebRequestProxyingURLLoaderFactory::InProgressRequest::OnLoaderCreated(
header_client_receiver_.reset();
header_client_receiver_.Bind(std::move(receiver));
+ if (is_header_client_receiver_paused_)
+ header_client_receiver_.Pause();
if (for_cors_preflight_) {
// In this case we don't have |target_loader_| and
// |proxied_client_receiver_|, and |receiver| is the only connection to the
// network service, so we observe mojo connection errors.
header_client_receiver_.set_disconnect_handler(base::BindOnce(
- &WebRequestProxyingURLLoaderFactory::InProgressRequest::OnRequestError,
+ &WebRequestProxyingURLLoaderFactory::InProgressRequest::OnNetworkError,
weak_factory_.GetWeakPtr(),
network::URLLoaderCompletionStatus(net::ERR_FAILED)));
}
@@ -443,7 +473,7 @@ void WebRequestProxyingURLLoaderFactory::InProgressRequest::OnBeforeSendHeaders(
request_.headers = headers;
on_before_send_headers_callback_ = std::move(callback);
- ContinueToBeforeSendHeaders(net::OK);
+ ContinueToBeforeSendHeadersWithOk();
}
void WebRequestProxyingURLLoaderFactory::InProgressRequest::OnHeadersReceived(
@@ -539,9 +569,10 @@ void WebRequestProxyingURLLoaderFactory::InProgressRequest::
}
void WebRequestProxyingURLLoaderFactory::InProgressRequest::
- ContinueToBeforeSendHeaders(int error_code) {
+ ContinueToBeforeSendHeaders(State state_on_error, int error_code) {
if (error_code != net::OK) {
- OnRequestError(network::URLLoaderCompletionStatus(error_code));
+ OnRequestError(network::URLLoaderCompletionStatus(error_code),
+ state_on_error);
return;
}
@@ -558,8 +589,10 @@ void WebRequestProxyingURLLoaderFactory::InProgressRequest::
// intuitive), |onBeforeSendHeaders| is only dispatched for HTTP and HTTPS
// requests.
- auto continuation = base::BindRepeating(
- &InProgressRequest::ContinueToSendHeaders, weak_factory_.GetWeakPtr());
+ const auto state_on_error = State::kRejectedByOnBeforeSendHeaders;
+ auto continuation =
+ base::BindRepeating(&InProgressRequest::ContinueToSendHeaders,
+ weak_factory_.GetWeakPtr(), state_on_error);
int result =
ExtensionWebRequestEventRouter::GetInstance()->OnBeforeSendHeaders(
factory_->browser_context_, &info_.value(), continuation,
@@ -568,7 +601,8 @@ void WebRequestProxyingURLLoaderFactory::InProgressRequest::
if (result == net::ERR_BLOCKED_BY_CLIENT) {
// The request was cancelled synchronously. Dispatch an error notification
// and terminate the request.
- OnRequestError(network::URLLoaderCompletionStatus(result));
+ OnRequestError(network::URLLoaderCompletionStatus(result),
+ state_on_error);
return;
}
@@ -585,21 +619,26 @@ void WebRequestProxyingURLLoaderFactory::InProgressRequest::
DCHECK_EQ(net::OK, result);
}
- ContinueToSendHeaders(std::set<std::string>(), std::set<std::string>(),
- net::OK);
+ ContinueToSendHeadersWithOk(std::set<std::string>(), std::set<std::string>());
}
void WebRequestProxyingURLLoaderFactory::InProgressRequest::
- ContinueToStartRequest(int error_code) {
+ ContinueToBeforeSendHeadersWithOk() {
+ ContinueToBeforeSendHeaders(State::kInvalid, net::OK);
+}
+void WebRequestProxyingURLLoaderFactory::InProgressRequest::
+ ContinueToStartRequest(State state_on_error, int error_code) {
if (error_code != net::OK) {
- OnRequestError(network::URLLoaderCompletionStatus(error_code));
+ OnRequestError(network::URLLoaderCompletionStatus(error_code),
+ state_on_error);
return;
}
if (current_request_uses_header_client_ && !redirect_url_.is_empty()) {
if (for_cors_preflight_) {
// CORS preflight doesn't support redirect.
- OnRequestError(network::URLLoaderCompletionStatus(net::ERR_FAILED));
+ OnRequestError(network::URLLoaderCompletionStatus(net::ERR_FAILED),
+ state_on_error);
return;
}
HandleBeforeRequestRedirect();
@@ -609,8 +648,10 @@ void WebRequestProxyingURLLoaderFactory::InProgressRequest::
if (proxied_client_receiver_.is_bound())
proxied_client_receiver_.Resume();
- if (header_client_receiver_.is_bound())
+ if (header_client_receiver_.is_bound()) {
header_client_receiver_.Resume();
+ is_header_client_receiver_paused_ = false;
+ }
if (for_cors_preflight_) {
// For CORS preflight requests, we have already started the request in
@@ -640,11 +681,18 @@ void WebRequestProxyingURLLoaderFactory::InProgressRequest::
}
void WebRequestProxyingURLLoaderFactory::InProgressRequest::
- ContinueToSendHeaders(const std::set<std::string>& removed_headers,
+ ContinueToStartRequestWithOk() {
+ ContinueToStartRequest(State::kInvalid, net::OK);
+}
+
+void WebRequestProxyingURLLoaderFactory::InProgressRequest::
+ ContinueToSendHeaders(State state_on_error,
+ const std::set<std::string>& removed_headers,
const std::set<std::string>& set_headers,
int error_code) {
if (error_code != net::OK) {
- OnRequestError(network::URLLoaderCompletionStatus(error_code));
+ OnRequestError(network::URLLoaderCompletionStatus(error_code),
+ state_on_error);
return;
}
@@ -690,7 +738,13 @@ void WebRequestProxyingURLLoaderFactory::InProgressRequest::
}
if (!current_request_uses_header_client_)
- ContinueToStartRequest(net::OK);
+ ContinueToStartRequestWithOk();
+}
+
+void WebRequestProxyingURLLoaderFactory::InProgressRequest::
+ ContinueToSendHeadersWithOk(const std::set<std::string>& removed_headers,
+ const std::set<std::string>& set_headers) {
+ ContinueToSendHeaders(State::kInvalid, removed_headers, set_headers, net::OK);
}
void WebRequestProxyingURLLoaderFactory::InProgressRequest::ContinueAuthRequest(
@@ -698,6 +752,8 @@ void WebRequestProxyingURLLoaderFactory::InProgressRequest::ContinueAuthRequest(
WebRequestAPI::AuthRequestCallback callback,
int error_code) {
if (error_code != net::OK) {
+ // Here we come from an onHeaderReceived failure.
+ state_ = State::kRejectedByOnHeadersReceivedForAuth;
base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(std::move(callback), base::nullopt,
true /* should_cancel */));
@@ -756,6 +812,7 @@ void WebRequestProxyingURLLoaderFactory::InProgressRequest::
AUTH_REQUIRED_RESPONSE_CANCEL_AUTH:
completion = base::BindOnce(std::move(callback), base::nullopt,
true /* should_cancel */);
+ state_ = State::kRejectedByOnAuthRequired;
break;
default:
NOTREACHED();
@@ -770,7 +827,18 @@ void WebRequestProxyingURLLoaderFactory::InProgressRequest::
void WebRequestProxyingURLLoaderFactory::InProgressRequest::
ContinueToHandleOverrideHeaders(int error_code) {
if (error_code != net::OK) {
- OnRequestError(network::URLLoaderCompletionStatus(error_code));
+ const int status_code = current_response_->headers
+ ? current_response_->headers->response_code()
+ : 0;
+ State state;
+ if (status_code == net::HTTP_UNAUTHORIZED) {
+ state = State::kRejectedByOnHeadersReceivedForAuth;
+ } else if (net::HttpResponseHeaders::IsRedirectResponseCode(status_code)) {
+ state = State::kRejectedByOnHeadersReceivedForRedirect;
+ } else {
+ state = State::kRejectedByOnHeadersReceivedForFinalResponse;
+ }
+ OnRequestError(network::URLLoaderCompletionStatus(error_code), state);
return;
}
@@ -787,7 +855,8 @@ void WebRequestProxyingURLLoaderFactory::InProgressRequest::
}
if (for_cors_preflight_ && !redirect_url_.is_empty()) {
- OnRequestError(network::URLLoaderCompletionStatus(net::ERR_FAILED));
+ OnRequestError(network::URLLoaderCompletionStatus(net::ERR_FAILED),
+ State::kRejectedByOnHeadersReceivedForRedirect);
return;
}
@@ -816,10 +885,15 @@ void WebRequestProxyingURLLoaderFactory::InProgressRequest::
ContinueToResponseStarted(int error_code) {
DCHECK(!for_cors_preflight_);
if (error_code != net::OK) {
- OnRequestError(network::URLLoaderCompletionStatus(error_code));
+ OnRequestError(network::URLLoaderCompletionStatus(error_code),
+ State::kRejectedByOnHeadersReceivedForFinalResponse);
return;
}
+ if (state_ == State::kInProgress) {
+ state_ = State::kInProgressWithFinalResponseReceived;
+ }
+
DCHECK(!current_request_uses_header_client_ || !override_headers_);
if (override_headers_)
current_response_->headers = override_headers_;
@@ -863,7 +937,8 @@ void WebRequestProxyingURLLoaderFactory::InProgressRequest::
ContinueToBeforeRedirect(const net::RedirectInfo& redirect_info,
int error_code) {
if (error_code != net::OK) {
- OnRequestError(network::URLLoaderCompletionStatus(error_code));
+ OnRequestError(network::URLLoaderCompletionStatus(error_code),
+ kRejectedByOnHeadersReceivedForRedirect);
return;
}
@@ -907,7 +982,19 @@ void WebRequestProxyingURLLoaderFactory::InProgressRequest::
current_response_->headers.get(), &override_headers_,
&redirect_url_);
if (result == net::ERR_BLOCKED_BY_CLIENT) {
- OnRequestError(network::URLLoaderCompletionStatus(result));
+ const int status_code = current_response_->headers
+ ? current_response_->headers->response_code()
+ : 0;
+ State state;
+ if (status_code == net::HTTP_UNAUTHORIZED) {
+ state = State::kRejectedByOnHeadersReceivedForAuth;
+ } else if (net::HttpResponseHeaders::IsRedirectResponseCode(
+ status_code)) {
+ state = State::kRejectedByOnHeadersReceivedForRedirect;
+ } else {
+ state = State::kRejectedByOnHeadersReceivedForFinalResponse;
+ }
+ OnRequestError(network::URLLoaderCompletionStatus(result), state);
return;
}
@@ -930,17 +1017,41 @@ void WebRequestProxyingURLLoaderFactory::InProgressRequest::
copyable_callback.Run(net::OK);
}
void WebRequestProxyingURLLoaderFactory::InProgressRequest::OnRequestError(
- const network::URLLoaderCompletionStatus& status) {
+ const network::URLLoaderCompletionStatus& status,
+ State state) {
if (target_client_)
target_client_->OnComplete(status);
ExtensionWebRequestEventRouter::GetInstance()->OnErrorOccurred(
factory_->browser_context_, &info_.value(), true /* started */,
status.error_code);
+ state_ = state;
// Deletes |this|.
factory_->RemoveRequest(network_service_request_id_, request_id_);
}
+void WebRequestProxyingURLLoaderFactory::InProgressRequest::OnNetworkError(
+ const network::URLLoaderCompletionStatus& status) {
+ State state = state_;
+ if (state_ == State::kInProgress) {
+ state = State::kRejectedByNetworkError;
+ } else if (state_ == State::kInProgressWithFinalResponseReceived) {
+ state = State::kRejectedByNetworkErrorAfterReceivingFinalResponse;
+ }
+ OnRequestError(status, state);
+}
+
+void WebRequestProxyingURLLoaderFactory::InProgressRequest::
+ OnClientDisconnected() {
+ State state = state_;
+ if (state_ == State::kInProgress) {
+ state = State::kDetachedFromClient;
+ } else if (state_ == State::kInProgressWithFinalResponseReceived) {
+ state = State::kDetachedFromClientAfterReceivingResponse;
+ }
+ OnRequestError(network::URLLoaderCompletionStatus(net::ERR_ABORTED), state);
+}
+
void WebRequestProxyingURLLoaderFactory::InProgressRequest::
OnLoaderDisconnected(uint32_t custom_reason,
const std::string& description) {
@@ -953,10 +1064,11 @@ void WebRequestProxyingURLLoaderFactory::InProgressRequest::
factory_->request_id_generator_->SaveID(
routing_id_, network_service_request_id_, request_id_);
+ state_ = State::kRedirectFollowedByAnotherInProgressRequest;
// Deletes |this|.
factory_->RemoveRequest(network_service_request_id_, request_id_);
} else {
- OnRequestError(network::URLLoaderCompletionStatus(net::ERR_ABORTED));
+ OnNetworkError(network::URLLoaderCompletionStatus(net::ERR_ABORTED));
}
}
@@ -991,17 +1103,20 @@ WebRequestProxyingURLLoaderFactory::WebRequestProxyingURLLoaderFactory(
mojo::PendingReceiver<network::mojom::TrustedURLLoaderHeaderClient>
header_client_receiver,
WebRequestAPI::ProxySet* proxies,
- content::ContentBrowserClient::URLLoaderFactoryType loader_factory_type)
+ content::ContentBrowserClient::URLLoaderFactoryType loader_factory_type,
+ ukm::SourceId ukm_source_id)
: browser_context_(browser_context),
render_process_id_(render_process_id),
request_id_generator_(request_id_generator),
navigation_ui_data_(std::move(navigation_ui_data)),
navigation_id_(std::move(navigation_id)),
proxies_(proxies),
- loader_factory_type_(loader_factory_type) {
+ loader_factory_type_(loader_factory_type),
+ ukm_source_id_(ukm_source_id) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
- // base::Unretained is safe here because the callback will be canceled when
- // |shutdown_notifier_| is destroyed, and |proxies_| owns this.
+ // base::Unretained is safe here because the callback will be
+ // canceled when |shutdown_notifier_| is destroyed, and |proxies_|
+ // owns this.
shutdown_notifier_ =
ShutdownNotifierFactory::GetInstance()
->Get(browser_context)
@@ -1032,14 +1147,16 @@ void WebRequestProxyingURLLoaderFactory::StartProxying(
mojo::PendingReceiver<network::mojom::TrustedURLLoaderHeaderClient>
header_client_receiver,
WebRequestAPI::ProxySet* proxies,
- content::ContentBrowserClient::URLLoaderFactoryType loader_factory_type) {
+ content::ContentBrowserClient::URLLoaderFactoryType loader_factory_type,
+ ukm::SourceId ukm_source_id) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
auto proxy = std::make_unique<WebRequestProxyingURLLoaderFactory>(
browser_context, render_process_id, request_id_generator,
std::move(navigation_ui_data), std::move(navigation_id),
std::move(loader_receiver), std::move(target_factory_remote),
- std::move(header_client_receiver), proxies, loader_factory_type);
+ std::move(header_client_receiver), proxies, loader_factory_type,
+ ukm_source_id);
proxies->AddProxy(std::move(proxy));
}
@@ -1054,35 +1171,36 @@ void WebRequestProxyingURLLoaderFactory::CreateLoaderAndStart(
const net::MutableNetworkTrafficAnnotationTag& traffic_annotation) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
- // Make sure we are not proxying a browser initiated non-navigation request
- // except for loading service worker scripts.
+ // Make sure we are not proxying a browser initiated non-navigation
+ // request except for loading service worker scripts.
DCHECK(render_process_id_ != -1 || navigation_ui_data_ ||
IsForServiceWorkerScript());
- // The |web_request_id| doesn't really matter. It just needs to be unique
- // per-BrowserContext so extensions can make sense of it. Note that
- // |network_service_request_id_| by contrast is not necessarily unique, so we
- // don't use it for identity here. This request ID may be the same as a
- // previous request if the previous request was redirected to a URL that
- // required a different loader.
+ // The |web_request_id| doesn't really matter. It just needs to be
+ // unique per-BrowserContext so extensions can make sense of it.
+ // Note that |network_service_request_id_| by contrast is not
+ // necessarily unique, so we don't use it for identity here. This
+ // request ID may be the same as a previous request if the previous
+ // request was redirected to a URL that required a different loader.
const uint64_t web_request_id =
request_id_generator_->Generate(routing_id, request_id);
if (request_id) {
- // Only requests with a non-zero request ID can have their proxy associated
- // with said ID. This is necessary to support correlation against any auth
- // events received by the browser. Requests with a request ID of 0 therefore
- // do not support dispatching |WebRequest.onAuthRequired| events.
+ // Only requests with a non-zero request ID can have their proxy
+ // associated with said ID. This is necessary to support
+ // correlation against any auth events received by the browser.
+ // Requests with a request ID of 0 therefore do not support
+ // dispatching |WebRequest.onAuthRequired| events.
proxies_->AssociateProxyWithRequestId(
this, content::GlobalRequestID(render_process_id_, request_id));
network_request_id_to_web_request_id_.emplace(request_id, web_request_id);
}
auto result = requests_.emplace(
- web_request_id,
- std::make_unique<InProgressRequest>(
- this, web_request_id, request_id, routing_id, options, request,
- traffic_annotation, std::move(loader_receiver), std::move(client)));
+ web_request_id, std::make_unique<InProgressRequest>(
+ this, web_request_id, request_id, routing_id, options,
+ ukm_source_id_, request, traffic_annotation,
+ std::move(loader_receiver), std::move(client)));
result.first->second->Restart();
}
diff --git a/chromium/extensions/browser/api/web_request/web_request_proxying_url_loader_factory.h b/chromium/extensions/browser/api/web_request/web_request_proxying_url_loader_factory.h
index 35c4bda6134..67e79302271 100644
--- a/chromium/extensions/browser/api/web_request/web_request_proxying_url_loader_factory.h
+++ b/chromium/extensions/browser/api/web_request/web_request_proxying_url_loader_factory.h
@@ -25,6 +25,7 @@
#include "mojo/public/cpp/bindings/remote.h"
#include "net/base/completion_once_callback.h"
#include "net/traffic_annotation/network_traffic_annotation.h"
+#include "services/metrics/public/cpp/ukm_source_id.h"
#include "services/network/public/cpp/resource_request.h"
#include "services/network/public/mojom/network_context.mojom.h"
#include "services/network/public/mojom/url_loader.mojom.h"
@@ -54,6 +55,7 @@ class WebRequestProxyingURLLoaderFactory
int32_t routing_id,
int32_t network_service_request_id,
uint32_t options,
+ ukm::SourceId ukm_source_id,
const network::ResourceRequest& request,
const net::MutableNetworkTrafficAnnotationTag& traffic_annotation,
mojo::PendingReceiver<network::mojom::URLLoader> loader_receiver,
@@ -106,15 +108,41 @@ class WebRequestProxyingURLLoaderFactory
OnHeadersReceivedCallback callback) override;
private:
+ // The state of an InprogressRequest. This is reported via UMA and UKM
+ // at the end of the request, so do not change enum values.
+ enum State {
+ kInProgress = 0,
+ kInProgressWithFinalResponseReceived,
+ kInvalid, // This is an invalid state and must not be recorded.
+ kRedirectFollowedByAnotherInProgressRequest,
+ kRejectedByNetworkError,
+ kRejectedByNetworkErrorAfterReceivingFinalResponse,
+ kDetachedFromClient,
+ kDetachedFromClientAfterReceivingResponse,
+ kRejectedByOnBeforeRequest,
+ kRejectedByOnBeforeSendHeaders,
+ kRejectedByOnHeadersReceivedForFinalResponse,
+ kRejectedByOnHeadersReceivedForRedirect,
+ kRejectedByOnHeadersReceivedForAuth,
+ kRejectedByOnAuthRequired,
+ kCompleted,
+ kMaxValue = kCompleted,
+ };
// These two methods combined form the implementation of Restart().
void UpdateRequestInfo();
void RestartInternal();
- void ContinueToBeforeSendHeaders(int error_code);
- void ContinueToSendHeaders(const std::set<std::string>& removed_headers,
+ void ContinueToBeforeSendHeaders(State state_on_error, int error_code);
+ void ContinueToBeforeSendHeadersWithOk();
+ void ContinueToSendHeaders(State state_on_error,
+ const std::set<std::string>& removed_headers,
const std::set<std::string>& set_headers,
int error_code);
- void ContinueToStartRequest(int error_code);
+ void ContinueToSendHeadersWithOk(
+ const std::set<std::string>& removed_headers,
+ const std::set<std::string>& set_headers);
+ void ContinueToStartRequest(State state_on_error, int error_code);
+ void ContinueToStartRequestWithOk();
void ContinueToHandleOverrideHeaders(int error_code);
void ContinueToResponseStarted(int error_code);
void ContinueAuthRequest(const net::AuthChallengeInfo& auth_info,
@@ -127,7 +155,10 @@ class WebRequestProxyingURLLoaderFactory
int error_code);
void HandleResponseOrRedirectHeaders(
net::CompletionOnceCallback continuation);
- void OnRequestError(const network::URLLoaderCompletionStatus& status);
+ void OnRequestError(const network::URLLoaderCompletionStatus& status,
+ State state);
+ void OnNetworkError(const network::URLLoaderCompletionStatus& status);
+ void OnClientDisconnected();
void OnLoaderDisconnected(uint32_t custom_reason,
const std::string& description);
bool IsRedirectSafe(const GURL& from_url,
@@ -142,6 +173,7 @@ class WebRequestProxyingURLLoaderFactory
const int32_t network_service_request_id_ = 0;
const int32_t routing_id_ = 0;
const uint32_t options_ = 0;
+ const ukm::SourceId ukm_source_id_;
const net::MutableNetworkTrafficAnnotationTag traffic_annotation_;
mojo::Receiver<network::mojom::URLLoader> proxied_loader_receiver_;
mojo::Remote<network::mojom::URLLoaderClient> target_client_;
@@ -179,6 +211,7 @@ class WebRequestProxyingURLLoaderFactory
OnHeadersReceivedCallback on_headers_received_callback_;
mojo::Receiver<network::mojom::TrustedHeaderClient> header_client_receiver_{
this};
+ bool is_header_client_receiver_paused_ = false;
// If |has_any_extra_headers_listeners_| is set to false and a redirect is
// in progress, this stores the parameters to FollowRedirect that came from
@@ -195,6 +228,7 @@ class WebRequestProxyingURLLoaderFactory
DISALLOW_COPY_AND_ASSIGN(FollowRedirectParams);
};
std::unique_ptr<FollowRedirectParams> pending_follow_redirect_params_;
+ State state_ = State::kInProgress;
base::WeakPtrFactory<InProgressRequest> weak_factory_{this};
@@ -213,7 +247,8 @@ class WebRequestProxyingURLLoaderFactory
mojo::PendingReceiver<network::mojom::TrustedURLLoaderHeaderClient>
header_client_receiver,
WebRequestAPI::ProxySet* proxies,
- content::ContentBrowserClient::URLLoaderFactoryType loader_factory_type);
+ content::ContentBrowserClient::URLLoaderFactoryType loader_factory_type,
+ ukm::SourceId ukm_source_id);
~WebRequestProxyingURLLoaderFactory() override;
@@ -229,7 +264,8 @@ class WebRequestProxyingURLLoaderFactory
mojo::PendingReceiver<network::mojom::TrustedURLLoaderHeaderClient>
header_client_receiver,
WebRequestAPI::ProxySet* proxies,
- content::ContentBrowserClient::URLLoaderFactoryType loader_factory_type);
+ content::ContentBrowserClient::URLLoaderFactoryType loader_factory_type,
+ ukm::SourceId ukm_source_id);
// network::mojom::URLLoaderFactory:
void CreateLoaderAndStart(
@@ -289,6 +325,9 @@ class WebRequestProxyingURLLoaderFactory
const content::ContentBrowserClient::URLLoaderFactoryType
loader_factory_type_;
+ // A UKM source ID associated with the content::WebContents of the initiator
+ // frame.
+ const ukm::SourceId ukm_source_id_;
// Mapping from our own internally generated request ID to an
// InProgressRequest instance.