Commit d06fdffc authored by Karan Bhatia's avatar Karan Bhatia Committed by Commit Bot

DNR: Move shareable logic from ExtensionUrlPatternIndexMatcher to RulesetMatcherInterface.

This CL moves some logic from ExtensionUrlPatternIndexMatcher to its base class,
the RulesetMatcherInterface. In a subsequent CL, this common logic will also be
used by RegexRulesMatcher, which will evaluate regex rules.

This CL is split off of
https://chromium-review.googlesource.com/c/chromium/src/+/1892132 for ease of
review. It has no behavior change.

BUG=974391

Change-Id: I2073ed480e5f15611ce2ba4eb1ea29cf7de7f0a0
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1910875
Auto-Submit: Karan Bhatia <karandeepb@chromium.org>
Reviewed-by: default avatarIstiaque Ahmed <lazyboy@chromium.org>
Commit-Queue: Karan Bhatia <karandeepb@chromium.org>
Cr-Commit-Position: refs/heads/master@{#715131}
parent 1d768300
......@@ -32,6 +32,7 @@ source_set("declarative_net_request") {
"ruleset_manager.h",
"ruleset_matcher.cc",
"ruleset_matcher.h",
"ruleset_matcher_interface.cc",
"ruleset_matcher_interface.h",
"ruleset_source.cc",
"ruleset_source.h",
......
......@@ -11,16 +11,9 @@
#include <utility>
#include "base/logging.h"
#include "base/strings/strcat.h"
#include "extensions/browser/api/declarative_net_request/constants.h"
#include "extensions/browser/api/declarative_net_request/request_action.h"
#include "extensions/browser/api/declarative_net_request/request_params.h"
#include "extensions/browser/api/declarative_net_request/ruleset_source.h"
#include "extensions/common/api/declarative_net_request.h"
#include "net/base/url_util.h"
#include "net/http/http_request_headers.h"
#include "url/gurl.h"
#include "url/url_constants.h"
namespace extensions {
namespace declarative_net_request {
......@@ -29,8 +22,6 @@ namespace dnr_api = api::declarative_net_request;
namespace {
constexpr const char kSetCookieResponseHeader[] = "set-cookie";
using FindRuleStrategy =
url_pattern_index::UrlPatternIndexMatcher::FindRuleStrategy;
......@@ -85,205 +76,6 @@ bool IsExtraHeadersMatcherInternal(
return false;
}
base::StringPiece CreateStringPiece(const ::flatbuffers::String& str) {
return base::StringPiece(str.c_str(), str.size());
}
// Returns true if the given |vec| is nullptr or empty.
template <typename T>
bool IsEmpty(const flatbuffers::Vector<T>* vec) {
return !vec || vec->size() == 0;
}
// Performs any required query transformations on the |url|. Returns true if the
// query should be modified and populates |modified_query|.
bool GetModifiedQuery(const GURL& url,
const flat::UrlTransform& transform,
std::string* modified_query) {
DCHECK(modified_query);
// |remove_query_params| should always be sorted.
DCHECK(
IsEmpty(transform.remove_query_params()) ||
std::is_sorted(transform.remove_query_params()->begin(),
transform.remove_query_params()->end(),
[](const flatbuffers::String* x1,
const flatbuffers::String* x2) { return *x1 < *x2; }));
// Return early if there's nothing to modify.
if (IsEmpty(transform.remove_query_params()) &&
IsEmpty(transform.add_or_replace_query_params())) {
return false;
}
std::vector<base::StringPiece> remove_query_params;
if (!IsEmpty(transform.remove_query_params())) {
remove_query_params.reserve(transform.remove_query_params()->size());
for (const ::flatbuffers::String* str : *transform.remove_query_params())
remove_query_params.push_back(CreateStringPiece(*str));
}
// We don't use a map from keys to vector of values to ensure the relative
// order of different params specified by the extension is respected. We use a
// std::list to support fast removal from middle of the list. Note that the
// key value pairs should already be escaped.
std::list<std::pair<base::StringPiece, base::StringPiece>>
add_or_replace_query_params;
if (!IsEmpty(transform.add_or_replace_query_params())) {
for (const flat::QueryKeyValue* query_pair :
*transform.add_or_replace_query_params()) {
DCHECK(query_pair->key());
DCHECK(query_pair->value());
add_or_replace_query_params.emplace_back(
CreateStringPiece(*query_pair->key()),
CreateStringPiece(*query_pair->value()));
}
}
std::vector<std::string> query_parts;
auto create_query_part = [](base::StringPiece key, base::StringPiece value) {
return base::StrCat({key, "=", value});
};
bool query_changed = false;
for (net::QueryIterator it(url); !it.IsAtEnd(); it.Advance()) {
std::string key = it.GetKey();
// Remove query param.
if (std::binary_search(remove_query_params.begin(),
remove_query_params.end(), key)) {
query_changed = true;
continue;
}
auto replace_iterator = std::find_if(
add_or_replace_query_params.begin(), add_or_replace_query_params.end(),
[&key](const std::pair<base::StringPiece, base::StringPiece>& param) {
return param.first == key;
});
// Nothing to do.
if (replace_iterator == add_or_replace_query_params.end()) {
query_parts.push_back(create_query_part(key, it.GetValue()));
continue;
}
// Replace query param.
query_changed = true;
query_parts.push_back(create_query_part(key, replace_iterator->second));
add_or_replace_query_params.erase(replace_iterator);
}
// Append any remaining query params.
for (const auto& params : add_or_replace_query_params) {
query_changed = true;
query_parts.push_back(create_query_part(params.first, params.second));
}
if (!query_changed)
return false;
*modified_query = base::JoinString(query_parts, "&");
return true;
}
GURL GetTransformedURL(const RequestParams& params,
const flat::UrlTransform& transform) {
GURL::Replacements replacements;
if (transform.scheme())
replacements.SetSchemeStr(CreateStringPiece(*transform.scheme()));
if (transform.host())
replacements.SetHostStr(CreateStringPiece(*transform.host()));
DCHECK(!(transform.clear_port() && transform.port()));
if (transform.clear_port())
replacements.ClearPort();
else if (transform.port())
replacements.SetPortStr(CreateStringPiece(*transform.port()));
DCHECK(!(transform.clear_path() && transform.path()));
if (transform.clear_path())
replacements.ClearPath();
else if (transform.path())
replacements.SetPathStr(CreateStringPiece(*transform.path()));
// |query| is defined outside the if conditions since url::Replacements does
// not own the strings it uses.
std::string query;
if (transform.clear_query()) {
replacements.ClearQuery();
} else if (transform.query()) {
replacements.SetQueryStr(CreateStringPiece(*transform.query()));
} else if (GetModifiedQuery(*params.url, transform, &query)) {
replacements.SetQueryStr(query);
}
DCHECK(!(transform.clear_fragment() && transform.fragment()));
if (transform.clear_fragment())
replacements.ClearRef();
else if (transform.fragment())
replacements.SetRefStr(CreateStringPiece(*transform.fragment()));
if (transform.password())
replacements.SetPasswordStr(CreateStringPiece(*transform.password()));
if (transform.username())
replacements.SetUsernameStr(CreateStringPiece(*transform.username()));
return params.url->ReplaceComponents(replacements);
}
bool ShouldCollapseResourceType(flat_rule::ElementType type) {
// TODO(crbug.com/848842): Add support for other element types like
// OBJECT.
return type == flat_rule::ElementType_IMAGE ||
type == flat_rule::ElementType_SUBDOCUMENT;
}
// Upgrades the url's scheme to HTTPS.
GURL GetUpgradedUrl(const GURL& url) {
DCHECK(url.SchemeIs(url::kHttpScheme) || url.SchemeIs(url::kFtpScheme));
GURL::Replacements replacements;
replacements.SetSchemeStr(url::kHttpsScheme);
return url.ReplaceComponents(replacements);
}
// Populates the list of headers corresponding to |mask|.
RequestAction GetRemoveHeadersActionForMask(const ExtensionId& extension_id,
uint8_t mask,
uint32_t rule_id,
uint32_t rule_priority,
dnr_api::SourceType source_type) {
RequestAction action(RequestAction::Type::REMOVE_HEADERS, rule_id,
rule_priority, source_type, extension_id);
for (int i = 0; mask && i <= dnr_api::REMOVE_HEADER_TYPE_LAST; ++i) {
switch (i) {
case dnr_api::REMOVE_HEADER_TYPE_NONE:
break;
case dnr_api::REMOVE_HEADER_TYPE_COOKIE:
if (mask & flat::RemoveHeaderType_cookie)
action.request_headers_to_remove.push_back(
net::HttpRequestHeaders::kCookie);
break;
case dnr_api::REMOVE_HEADER_TYPE_REFERER:
if (mask & flat::RemoveHeaderType_referer)
action.request_headers_to_remove.push_back(
net::HttpRequestHeaders::kReferer);
break;
case dnr_api::REMOVE_HEADER_TYPE_SETCOOKIE:
if (mask & flat::RemoveHeaderType_set_cookie)
action.response_headers_to_remove.push_back(kSetCookieResponseHeader);
break;
}
}
return action;
}
} // namespace
ExtensionUrlPatternIndexMatcher::ExtensionUrlPatternIndexMatcher(
......@@ -291,8 +83,7 @@ ExtensionUrlPatternIndexMatcher::ExtensionUrlPatternIndexMatcher(
api::declarative_net_request::SourceType source_type,
const ExtensionUrlPatternIndexMatcher::UrlPatternIndexList* index_list,
const ExtensionMetadataList* metadata_list)
: extension_id_(extension_id),
source_type_(source_type),
: RulesetMatcherInterface(extension_id, source_type),
metadata_list_(metadata_list),
matchers_(GetMatchers(index_list)),
is_extra_headers_matcher_(IsExtraHeadersMatcherInternal(index_list)) {}
......@@ -307,11 +98,7 @@ ExtensionUrlPatternIndexMatcher::GetBlockOrCollapseAction(
if (!rule)
return base::nullopt;
return ShouldCollapseResourceType(params.element_type)
? RequestAction(RequestAction::Type::COLLAPSE, rule->id(),
rule->priority(), source_type(), extension_id())
: RequestAction(RequestAction::Type::BLOCK, rule->id(),
rule->priority(), source_type(), extension_id());
return CreateBlockOrCollapseRequestAction(params, *rule);
}
bool ExtensionUrlPatternIndexMatcher::HasMatchingAllowRule(
......@@ -322,56 +109,38 @@ bool ExtensionUrlPatternIndexMatcher::HasMatchingAllowRule(
base::Optional<RequestAction>
ExtensionUrlPatternIndexMatcher::GetRedirectAction(
const RequestParams& params) const {
GURL redirect_rule_url;
const flat_rule::UrlRule* redirect_rule =
GetRedirectRule(params, &redirect_rule_url);
const flat_rule::UrlRule* redirect_rule = GetMatchingRule(
params, flat::ActionIndex_redirect, FindRuleStrategy::kHighestPriority);
if (!redirect_rule)
return base::nullopt;
RequestAction redirect_action(RequestAction::Type::REDIRECT,
redirect_rule->id(), redirect_rule->priority(),
source_type(), extension_id());
redirect_action.redirect_url = std::move(redirect_rule_url);
return redirect_action;
return CreateRedirectAction(params, *redirect_rule, *metadata_list_);
}
base::Optional<RequestAction> ExtensionUrlPatternIndexMatcher::GetUpgradeAction(
const RequestParams& params) const {
const flat_rule::UrlRule* upgrade_rule = GetUpgradeRule(params);
DCHECK(IsUpgradeableRequest(params));
const flat_rule::UrlRule* upgrade_rule =
GetMatchingRule(params, flat::ActionIndex_upgrade_scheme,
FindRuleStrategy::kHighestPriority);
if (!upgrade_rule)
return base::nullopt;
RequestAction upgrade_action(RequestAction::Type::REDIRECT,
upgrade_rule->id(), upgrade_rule->priority(),
source_type(), extension_id());
upgrade_action.redirect_url = GetUpgradedUrl(*params.url);
return upgrade_action;
return CreateUpgradeAction(params, *upgrade_rule);
}
uint8_t ExtensionUrlPatternIndexMatcher::GetRemoveHeadersMask(
const RequestParams& params,
uint8_t ignored_mask,
std::vector<RequestAction>* remove_headers_actions) const {
uint8_t mask = 0;
static_assert(
flat::RemoveHeaderType_ANY <= std::numeric_limits<uint8_t>::max(),
"flat::RemoveHeaderType can't fit in a uint8_t");
// Iterate over each RemoveHeaderType value.
uint8_t bit = 0;
// Rules with the same IDs may be split across different action indices. To
// ensure we return one RequestAction for one ID, maintain a map from the rule
// ID to the mask of rules removed for that rule ID.
base::flat_map<uint32_t, uint8_t> rule_id_to_mask;
// The same flat_rule::UrlRule may be split across different action indices.
// To ensure we return one RequestAction for one ID/rule, maintain a map from
// the rule to the mask of rules removed for that rule.
base::flat_map<const flat_rule::UrlRule*, uint8_t> rule_to_mask_map;
auto handle_remove_header_bit = [this, &params, ignored_mask,
&rule_id_to_mask, &mask](
uint8_t bit, flat::ActionIndex index) {
&rule_to_mask_map](uint8_t bit,
flat::ActionIndex index) {
if (ignored_mask & bit)
return;
......@@ -379,10 +148,11 @@ uint8_t ExtensionUrlPatternIndexMatcher::GetRemoveHeadersMask(
if (!rule)
return;
rule_id_to_mask[rule->id()] |= bit;
mask |= bit;
rule_to_mask_map[rule] |= bit;
};
// Iterate over each RemoveHeaderType value.
uint8_t bit = 0;
for (int i = 0; i <= dnr_api::REMOVE_HEADER_TYPE_LAST; ++i) {
switch (i) {
case dnr_api::REMOVE_HEADER_TYPE_NONE:
......@@ -403,67 +173,20 @@ uint8_t ExtensionUrlPatternIndexMatcher::GetRemoveHeadersMask(
}
}
// Rule priority doesn't matter for remove header rules.
uint32_t rule_priority = kDefaultPriority;
for (auto it = rule_id_to_mask.begin(); it != rule_id_to_mask.end(); ++it) {
uint8_t mask_for_rule = it->second;
uint8_t mask = 0;
for (const auto& it : rule_to_mask_map) {
uint8_t mask_for_rule = it.second;
DCHECK(mask_for_rule);
remove_headers_actions->push_back(GetRemoveHeadersActionForMask(
extension_id(), mask_for_rule, it->first /* rule_id */, rule_priority,
source_type()));
mask |= mask_for_rule;
remove_headers_actions->push_back(
GetRemoveHeadersActionForMask(*it.first, mask_for_rule));
}
DCHECK(!(mask & ignored_mask));
return mask;
}
const flat_rule::UrlRule* ExtensionUrlPatternIndexMatcher::GetRedirectRule(
const RequestParams& params,
GURL* redirect_url) const {
DCHECK(redirect_url);
DCHECK_NE(flat_rule::ElementType_WEBSOCKET, params.element_type);
const flat_rule::UrlRule* rule = GetMatchingRule(
params, flat::ActionIndex_redirect, FindRuleStrategy::kHighestPriority);
if (!rule)
return nullptr;
// Find the UrlRuleMetadata corresponding to |rule|. Since |metadata_list_| is
// sorted by rule id, use LookupByKey which binary searches for fast lookup.
const flat::UrlRuleMetadata* metadata =
metadata_list_->LookupByKey(rule->id());
// There must be a UrlRuleMetadata object corresponding to each redirect rule.
DCHECK(metadata);
DCHECK_EQ(metadata->id(), rule->id());
DCHECK(metadata->redirect_url() || metadata->transform());
if (metadata->redirect_url())
*redirect_url = GURL(CreateStringPiece(*metadata->redirect_url()));
else
*redirect_url = GetTransformedURL(params, *metadata->transform());
// Sanity check that we don't redirect to a javascript url. Specifying
// redirect to a javascript url and specifying javascript as a transform
// scheme is prohibited. In addition extensions can't intercept requests to
// javascript urls. Hence we should never end up with a javascript url here.
DCHECK(!redirect_url->SchemeIs(url::kJavaScriptScheme));
// Prevent a redirect loop where a URL continuously redirects to itself.
return (redirect_url->is_valid() && *params.url != *redirect_url) ? rule
: nullptr;
}
const flat_rule::UrlRule* ExtensionUrlPatternIndexMatcher::GetUpgradeRule(
const RequestParams& params) const {
const bool is_upgradeable = params.url->SchemeIs(url::kHttpScheme) ||
params.url->SchemeIs(url::kFtpScheme);
return is_upgradeable
? GetMatchingRule(params, flat::ActionIndex_upgrade_scheme,
FindRuleStrategy::kHighestPriority)
: nullptr;
}
const flat_rule::UrlRule* ExtensionUrlPatternIndexMatcher::GetMatchingRule(
const RequestParams& params,
flat::ActionIndex index,
......
......@@ -8,7 +8,6 @@
#include <vector>
#include "components/url_pattern_index/url_pattern_index.h"
#include "extensions/browser/api/declarative_net_request/flat/extension_ruleset_generated.h"
#include "extensions/browser/api/declarative_net_request/ruleset_matcher_interface.h"
namespace extensions {
......@@ -22,8 +21,6 @@ class ExtensionUrlPatternIndexMatcher final : public RulesetMatcherInterface {
public:
using UrlPatternIndexList = flatbuffers::Vector<
flatbuffers::Offset<url_pattern_index::flat::UrlPatternIndex>>;
using ExtensionMetadataList =
flatbuffers::Vector<flatbuffers::Offset<flat::UrlRuleMetadata>>;
ExtensionUrlPatternIndexMatcher(
const ExtensionId& extension_id,
api::declarative_net_request::SourceType source_type,
......@@ -46,35 +43,16 @@ class ExtensionUrlPatternIndexMatcher final : public RulesetMatcherInterface {
bool IsExtraHeadersMatcher() const override {
return is_extra_headers_matcher_;
}
const ExtensionId& extension_id() const override { return extension_id_; }
api::declarative_net_request::SourceType source_type() const override {
return source_type_;
}
private:
using UrlPatternIndexMatcher = url_pattern_index::UrlPatternIndexMatcher;
// Returns the ruleset's highest priority matching redirect rule and populates
// |redirect_url|. Returns nullptr if there is no matching redirect rule.
const url_pattern_index::flat::UrlRule* GetRedirectRule(
const RequestParams& params,
GURL* redirect_url) const;
// Returns the ruleset's highest priority matching upgrade scheme rule or
// nullptr if no matching rule is found or if the request's scheme is not
// upgradeable.
const url_pattern_index::flat::UrlRule* GetUpgradeRule(
const RequestParams& params) const;
const url_pattern_index::flat::UrlRule* GetMatchingRule(
const RequestParams& params,
flat::ActionIndex index,
UrlPatternIndexMatcher::FindRuleStrategy strategy =
UrlPatternIndexMatcher::FindRuleStrategy::kAny) const;
const ExtensionId extension_id_;
const api::declarative_net_request::SourceType source_type_;
const ExtensionMetadataList* const metadata_list_;
// UrlPatternIndexMatchers corresponding to entries in flat::ActionIndex.
......
......@@ -79,6 +79,9 @@ base::Optional<RequestAction> RulesetMatcher::GetRedirectAction(
base::Optional<RequestAction> RulesetMatcher::GetUpgradeAction(
const RequestParams& params) const {
if (!IsUpgradeableRequest(params))
return base::nullopt;
return url_pattern_index_matcher_.GetUpgradeAction(params);
}
......@@ -115,12 +118,11 @@ RulesetMatcher::RulesetMatcher(
size_t priority,
api::declarative_net_request::SourceType source_type,
const ExtensionId& extension_id)
: ruleset_data_(std::move(ruleset_data)),
: RulesetMatcherInterface(extension_id, source_type),
ruleset_data_(std::move(ruleset_data)),
root_(flat::GetExtensionIndexedRuleset(ruleset_data_.data())),
id_(id),
priority_(priority),
source_type_(source_type),
extension_id_(extension_id),
url_pattern_index_matcher_(extension_id,
source_type,
root_->index_list(),
......
......@@ -78,10 +78,6 @@ class RulesetMatcher : public RulesetMatcherInterface {
uint8_t ignored_mask,
std::vector<RequestAction>* remove_headers_actions) const override;
bool IsExtraHeadersMatcher() const override;
const ExtensionId& extension_id() const override { return extension_id_; }
api::declarative_net_request::SourceType source_type() const override {
return source_type_;
}
// Returns a RequestAction constructed from the matching redirect or upgrade
// rule with the highest priority, or base::nullopt if no matching redirect or
......@@ -111,11 +107,6 @@ class RulesetMatcher : public RulesetMatcherInterface {
const size_t id_;
const size_t priority_;
const api::declarative_net_request::SourceType source_type_;
// The ID of the extension from which this matcher's ruleset originates from.
const ExtensionId extension_id_;
// Underlying matcher for filter-list style rules supported using the
// |url_pattern_index| component.
const ExtensionUrlPatternIndexMatcher url_pattern_index_matcher_;
......
// Copyright 2019 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 "extensions/browser/api/declarative_net_request/ruleset_matcher_interface.h"
#include "base/strings/strcat.h"
#include "components/url_pattern_index/flat/url_pattern_index_generated.h"
#include "extensions/browser/api/declarative_net_request/request_action.h"
#include "extensions/browser/api/declarative_net_request/request_params.h"
#include "net/base/url_util.h"
#include "net/http/http_request_headers.h"
#include "url/gurl.h"
#include "url/url_constants.h"
namespace extensions {
namespace declarative_net_request {
namespace flat_rule = url_pattern_index::flat;
namespace dnr_api = api::declarative_net_request;
namespace {
constexpr const char kSetCookieResponseHeader[] = "set-cookie";
bool ShouldCollapseResourceType(flat_rule::ElementType type) {
// TODO(crbug.com/848842): Add support for other element types like
// OBJECT.
return type == flat_rule::ElementType_IMAGE ||
type == flat_rule::ElementType_SUBDOCUMENT;
}
// Upgrades the url's scheme to HTTPS.
GURL GetUpgradedUrl(const GURL& url) {
GURL::Replacements replacements;
replacements.SetSchemeStr(url::kHttpsScheme);
return url.ReplaceComponents(replacements);
}
base::StringPiece CreateStringPiece(const ::flatbuffers::String& str) {
return base::StringPiece(str.c_str(), str.size());
}
// Returns true if the given |vec| is nullptr or empty.
template <typename T>
bool IsEmpty(const flatbuffers::Vector<T>* vec) {
return !vec || vec->size() == 0;
}
// Performs any required query transformations on the |url|. Returns true if the
// query should be modified and populates |modified_query|.
bool GetModifiedQuery(const GURL& url,
const flat::UrlTransform& transform,
std::string* modified_query) {
DCHECK(modified_query);
// |remove_query_params| should always be sorted.
DCHECK(
IsEmpty(transform.remove_query_params()) ||
std::is_sorted(transform.remove_query_params()->begin(),
transform.remove_query_params()->end(),
[](const flatbuffers::String* x1,
const flatbuffers::String* x2) { return *x1 < *x2; }));
// Return early if there's nothing to modify.
if (IsEmpty(transform.remove_query_params()) &&
IsEmpty(transform.add_or_replace_query_params())) {
return false;
}
std::vector<base::StringPiece> remove_query_params;
if (!IsEmpty(transform.remove_query_params())) {
remove_query_params.reserve(transform.remove_query_params()->size());
for (const ::flatbuffers::String* str : *transform.remove_query_params())
remove_query_params.push_back(CreateStringPiece(*str));
}
// We don't use a map from keys to vector of values to ensure the relative
// order of different params specified by the extension is respected. We use a
// std::list to support fast removal from middle of the list. Note that the
// key value pairs should already be escaped.
std::list<std::pair<base::StringPiece, base::StringPiece>>
add_or_replace_query_params;
if (!IsEmpty(transform.add_or_replace_query_params())) {
for (const flat::QueryKeyValue* query_pair :
*transform.add_or_replace_query_params()) {
DCHECK(query_pair->key());
DCHECK(query_pair->value());
add_or_replace_query_params.emplace_back(
CreateStringPiece(*query_pair->key()),
CreateStringPiece(*query_pair->value()));
}
}
std::vector<std::string> query_parts;
auto create_query_part = [](base::StringPiece key,
base::StringPiece value) -> std::string {
return base::StrCat({key, "=", value});
};
bool query_changed = false;
for (net::QueryIterator it(url); !it.IsAtEnd(); it.Advance()) {
std::string key = it.GetKey();
// Remove query param.
if (std::binary_search(remove_query_params.begin(),
remove_query_params.end(), key)) {
query_changed = true;
continue;
}
auto replace_iterator = std::find_if(
add_or_replace_query_params.begin(), add_or_replace_query_params.end(),
[&key](const std::pair<base::StringPiece, base::StringPiece>& param) {
return param.first == key;
});
// Nothing to do.
if (replace_iterator == add_or_replace_query_params.end()) {
query_parts.push_back(create_query_part(key, it.GetValue()));
continue;
}
// Replace query param.
query_changed = true;
query_parts.push_back(create_query_part(key, replace_iterator->second));
add_or_replace_query_params.erase(replace_iterator);
}
// Append any remaining query params.
for (const auto& params : add_or_replace_query_params)
query_parts.push_back(create_query_part(params.first, params.second));
query_changed |= !add_or_replace_query_params.empty();
if (!query_changed)
return false;
*modified_query = base::JoinString(query_parts, "&");
return true;
}
GURL GetTransformedURL(const RequestParams& params,
const flat::UrlTransform& transform) {
GURL::Replacements replacements;
if (transform.scheme())
replacements.SetSchemeStr(CreateStringPiece(*transform.scheme()));
if (transform.host())
replacements.SetHostStr(CreateStringPiece(*transform.host()));
DCHECK(!(transform.clear_port() && transform.port()));
if (transform.clear_port())
replacements.ClearPort();
else if (transform.port())
replacements.SetPortStr(CreateStringPiece(*transform.port()));
DCHECK(!(transform.clear_path() && transform.path()));
if (transform.clear_path())
replacements.ClearPath();
else if (transform.path())
replacements.SetPathStr(CreateStringPiece(*transform.path()));
// |query| is defined outside the if conditions since url::Replacements does
// not own the strings it uses.
std::string query;
if (transform.clear_query()) {
replacements.ClearQuery();
} else if (transform.query()) {
replacements.SetQueryStr(CreateStringPiece(*transform.query()));
} else if (GetModifiedQuery(*params.url, transform, &query)) {
replacements.SetQueryStr(query);
}
DCHECK(!(transform.clear_fragment() && transform.fragment()));
if (transform.clear_fragment())
replacements.ClearRef();
else if (transform.fragment())
replacements.SetRefStr(CreateStringPiece(*transform.fragment()));
if (transform.password())
replacements.SetPasswordStr(CreateStringPiece(*transform.password()));
if (transform.username())
replacements.SetUsernameStr(CreateStringPiece(*transform.username()));
return params.url->ReplaceComponents(replacements);
}
} // namespace
RulesetMatcherInterface::RulesetMatcherInterface(
const ExtensionId& extension_id,
api::declarative_net_request::SourceType source_type)
: extension_id_(extension_id), source_type_(source_type) {}
RulesetMatcherInterface::~RulesetMatcherInterface() = default;
// static
bool RulesetMatcherInterface::IsUpgradeableRequest(
const RequestParams& params) {
return params.url->SchemeIs(url::kHttpScheme) ||
params.url->SchemeIs(url::kFtpScheme);
}
RequestAction RulesetMatcherInterface::CreateBlockOrCollapseRequestAction(
const RequestParams& params,
const flat_rule::UrlRule& rule) const {
return ShouldCollapseResourceType(params.element_type)
? RequestAction(RequestAction::Type::COLLAPSE, rule.id(),
rule.priority(), source_type(), extension_id())
: RequestAction(RequestAction::Type::BLOCK, rule.id(),
rule.priority(), source_type(), extension_id());
}
RequestAction RulesetMatcherInterface::CreateUpgradeAction(
const RequestParams& params,
const url_pattern_index::flat::UrlRule& rule) const {
DCHECK(IsUpgradeableRequest(params));
RequestAction upgrade_action(RequestAction::Type::REDIRECT, rule.id(),
rule.priority(), source_type(), extension_id());
upgrade_action.redirect_url = GetUpgradedUrl(*params.url);
return upgrade_action;
}
base::Optional<RequestAction> RulesetMatcherInterface::CreateRedirectAction(
const RequestParams& params,
const url_pattern_index::flat::UrlRule& rule,
const ExtensionMetadataList& metadata_list) const {
DCHECK_NE(flat_rule::ElementType_WEBSOCKET, params.element_type);
// Find the UrlRuleMetadata corresponding to |rule|. Since |metadata_list| is
// sorted by rule id, use LookupByKey which binary searches for fast lookup.
const flat::UrlRuleMetadata* metadata = metadata_list.LookupByKey(rule.id());
// There must be a UrlRuleMetadata object corresponding to each redirect rule.
DCHECK(metadata);
DCHECK_EQ(metadata->id(), rule.id());
DCHECK(metadata->redirect_url() || metadata->transform());
GURL redirect_url;
if (metadata->redirect_url())
redirect_url = GURL(CreateStringPiece(*metadata->redirect_url()));
else
redirect_url = GetTransformedURL(params, *metadata->transform());
// Sanity check that we don't redirect to a javascript url. Specifying
// redirect to a javascript url and specifying javascript as a transform
// scheme is prohibited. In addition extensions can't intercept requests to
// javascript urls. Hence we should never end up with a javascript url here.
DCHECK(!redirect_url.SchemeIs(url::kJavaScriptScheme));
// Prevent a redirect loop where a URL continuously redirects to itself.
if (!redirect_url.is_valid() || *params.url == redirect_url)
return base::nullopt;
RequestAction redirect_action(RequestAction::Type::REDIRECT, rule.id(),
rule.priority(), source_type(), extension_id());
redirect_action.redirect_url = std::move(redirect_url);
return redirect_action;
}
RequestAction RulesetMatcherInterface::GetRemoveHeadersActionForMask(
const url_pattern_index::flat::UrlRule& rule,
uint8_t mask) const {
DCHECK(mask);
RequestAction action(RequestAction::Type::REMOVE_HEADERS, rule.id(),
rule.priority(), source_type(), extension_id());
for (int header = 0; header <= dnr_api::REMOVE_HEADER_TYPE_LAST; ++header) {
switch (header) {
case dnr_api::REMOVE_HEADER_TYPE_NONE:
break;
case dnr_api::REMOVE_HEADER_TYPE_COOKIE:
if (mask & flat::RemoveHeaderType_cookie) {
action.request_headers_to_remove.push_back(
net::HttpRequestHeaders::kCookie);
}
break;
case dnr_api::REMOVE_HEADER_TYPE_REFERER:
if (mask & flat::RemoveHeaderType_referer) {
action.request_headers_to_remove.push_back(
net::HttpRequestHeaders::kReferer);
}
break;
case dnr_api::REMOVE_HEADER_TYPE_SETCOOKIE:
if (mask & flat::RemoveHeaderType_set_cookie)
action.response_headers_to_remove.push_back(kSetCookieResponseHeader);
break;
}
}
return action;
}
} // namespace declarative_net_request
} // namespace extensions
......@@ -8,6 +8,7 @@
#include <vector>
#include "base/optional.h"
#include "extensions/browser/api/declarative_net_request/flat/extension_ruleset_generated.h"
#include "extensions/common/api/declarative_net_request.h"
#include "extensions/common/extension_id.h"
......@@ -17,11 +18,15 @@ namespace declarative_net_request {
struct RequestAction;
struct RequestParams;
// An abstract interface for rule matchers. Overridden by different kinds of
// An abstract class for rule matchers. Overridden by different kinds of
// matchers, e.g. filter lists and regex.
// TODO(karandeepb): This is no longer an interface. Rename this class.
class RulesetMatcherInterface {
public:
virtual ~RulesetMatcherInterface() = default;
RulesetMatcherInterface(const ExtensionId& extension_id,
api::declarative_net_request::SourceType source_type);
virtual ~RulesetMatcherInterface();
// Returns the ruleset's matching RequestAction with type |BLOCK| or
// |COLLAPSE|, or base::nullopt if the ruleset has no matching blocking rule.
......@@ -55,10 +60,50 @@ class RulesetMatcherInterface {
virtual bool IsExtraHeadersMatcher() const = 0;
// Returns the extension ID with which this matcher is associated.
virtual const ExtensionId& extension_id() const = 0;
const ExtensionId& extension_id() const { return extension_id_; }
// The source type of the matcher.
virtual api::declarative_net_request::SourceType source_type() const = 0;
api::declarative_net_request::SourceType source_type() const {
return source_type_;
}
protected:
using ExtensionMetadataList =
::flatbuffers::Vector<flatbuffers::Offset<flat::UrlRuleMetadata>>;
// Returns true if the given request can be upgraded.
static bool IsUpgradeableRequest(const RequestParams& params);
// Helper to create a RequestAction of type |BLOCK| or |COLLAPSE|.
RequestAction CreateBlockOrCollapseRequestAction(
const RequestParams& params,
const url_pattern_index::flat::UrlRule& rule) const;
// Helper to create a RequestAction of type |REDIRECT| with the request
// upgraded.
RequestAction CreateUpgradeAction(
const RequestParams& params,
const url_pattern_index::flat::UrlRule& rule) const;
// Helper to create a RequestAction of type |REDIRECT| with the appropriate
// redirect url. Can return base::nullopt if the redirect url is ill-formed or
// same as the current request url.
base::Optional<RequestAction> CreateRedirectAction(
const RequestParams& params,
const url_pattern_index::flat::UrlRule& rule,
const ExtensionMetadataList& metadata_list) const;
// Helper to create a RequestAction of type |REMOVE_HEADERS|. |mask|
// corresponds to bitmask of flat::RemoveHeaderType, and must be non-empty.
RequestAction GetRemoveHeadersActionForMask(
const url_pattern_index::flat::UrlRule& rule,
uint8_t mask) const;
private:
const ExtensionId extension_id_;
const api::declarative_net_request::SourceType source_type_;
DISALLOW_COPY_AND_ASSIGN(RulesetMatcherInterface);
};
} // namespace declarative_net_request
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment