Commit 6f97f809 authored by Karan Bhatia's avatar Karan Bhatia Committed by Commit Bot

DNR: Reduce work while evaluating query transform.

Reduce the work that's done while evaluating the query transform of a rule. This
is done by indexing escaped values for the query key value pairs. Also, sort and
remove duplicates before indexing the query keys to be removed.

BUG=983685

Change-Id: Ia6e72832253025c4a43b5852e00a7a064d3fcd6b
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1742887
Commit-Queue: Karan Bhatia <karandeepb@chromium.org>
Reviewed-by: default avatarDevlin <rdevlin.cronin@chromium.org>
Cr-Commit-Position: refs/heads/master@{#685397}
parent 2ab03faa
...@@ -30,7 +30,12 @@ table UrlTransform { ...@@ -30,7 +30,12 @@ table UrlTransform {
/// If valid, doesn't include the '?' separator. /// If valid, doesn't include the '?' separator.
query : string; query : string;
/// Query params to be removed. These are already sorted and escaped using
/// net::EscapeQueryParamValue.
remove_query_params : [string]; remove_query_params : [string];
/// Query params to be added/replaced. These are already escaped using
/// net::EscapeQueryParamValue.
add_or_replace_query_params : [QueryKeyValue]; add_or_replace_query_params : [QueryKeyValue];
clear_fragment : bool = false; clear_fragment : bool = false;
......
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include "base/logging.h" #include "base/logging.h"
#include "base/numerics/safe_conversions.h" #include "base/numerics/safe_conversions.h"
#include "extensions/browser/api/declarative_net_request/indexed_rule.h" #include "extensions/browser/api/declarative_net_request/indexed_rule.h"
#include "net/base/escape.h"
namespace extensions { namespace extensions {
namespace declarative_net_request { namespace declarative_net_request {
...@@ -28,17 +29,18 @@ using FlatStringOffset = FlatOffset<flatbuffers::String>; ...@@ -28,17 +29,18 @@ using FlatStringOffset = FlatOffset<flatbuffers::String>;
using FlatStringListOffset = FlatVectorOffset<flatbuffers::String>; using FlatStringListOffset = FlatVectorOffset<flatbuffers::String>;
// Writes to |builder| a flatbuffer vector of shared strings corresponding to // Writes to |builder| a flatbuffer vector of shared strings corresponding to
// |vec| and returns the offset to it. If |vec| is empty, returns an empty // |container| and returns the offset to it. If |container| is empty, returns an
// offset. // empty offset.
template <typename T>
FlatStringListOffset BuildVectorOfSharedStrings( FlatStringListOffset BuildVectorOfSharedStrings(
flatbuffers::FlatBufferBuilder* builder, flatbuffers::FlatBufferBuilder* builder,
const std::vector<std::string>& vec) { const T& container) {
if (vec.empty()) if (container.empty())
return FlatStringListOffset(); return FlatStringListOffset();
std::vector<FlatStringOffset> offsets; std::vector<FlatStringOffset> offsets;
offsets.reserve(vec.size()); offsets.reserve(container.size());
for (const auto& str : vec) for (const std::string& str : container)
offsets.push_back(builder->CreateSharedString(str)); offsets.push_back(builder->CreateSharedString(str));
return builder->CreateVector(offsets); return builder->CreateVector(offsets);
} }
...@@ -110,9 +112,18 @@ FlatOffset<flat::UrlTransform> BuildTransformOffset( ...@@ -110,9 +112,18 @@ FlatOffset<flat::UrlTransform> BuildTransformOffset(
FlatStringOffset password = create_string_offset(transform.password); FlatStringOffset password = create_string_offset(transform.password);
FlatStringListOffset remove_query_params; FlatStringListOffset remove_query_params;
const bool use_plus = true;
if (transform.query_transform && transform.query_transform->remove_params) { if (transform.query_transform && transform.query_transform->remove_params) {
remove_query_params = BuildVectorOfSharedStrings( // Escape, sort and remove duplicates.
builder, *transform.query_transform->remove_params); std::set<std::string> remove_params_escaped;
for (const std::string& remove_param :
*transform.query_transform->remove_params) {
remove_params_escaped.insert(
net::EscapeQueryParamValue(remove_param, use_plus));
}
remove_query_params =
BuildVectorOfSharedStrings(builder, remove_params_escaped);
} }
FlatVectorOffset<flat::QueryKeyValue> add_or_replace_params; FlatVectorOffset<flat::QueryKeyValue> add_or_replace_params;
...@@ -124,9 +135,12 @@ FlatOffset<flat::UrlTransform> BuildTransformOffset( ...@@ -124,9 +135,12 @@ FlatOffset<flat::UrlTransform> BuildTransformOffset(
transform.query_transform->add_or_replace_params->size()); transform.query_transform->add_or_replace_params->size());
for (const dnr_api::QueryKeyValue& query_pair : for (const dnr_api::QueryKeyValue& query_pair :
*transform.query_transform->add_or_replace_params) { *transform.query_transform->add_or_replace_params) {
add_or_replace_queries.push_back(flat::CreateQueryKeyValue( FlatStringOffset key = builder->CreateSharedString(
*builder, builder->CreateSharedString(query_pair.key), net::EscapeQueryParamValue(query_pair.key, use_plus));
builder->CreateSharedString(query_pair.value))); FlatStringOffset value = builder->CreateSharedString(
net::EscapeQueryParamValue(query_pair.value, use_plus));
add_or_replace_queries.push_back(
flat::CreateQueryKeyValue(*builder, key, value));
} }
add_or_replace_params = builder->CreateVector(add_or_replace_queries); add_or_replace_params = builder->CreateVector(add_or_replace_queries);
} }
......
...@@ -125,7 +125,7 @@ TEST_F(IndexedRulesetFormatVersionTest, CheckVersionUpdated) { ...@@ -125,7 +125,7 @@ TEST_F(IndexedRulesetFormatVersionTest, CheckVersionUpdated) {
EXPECT_EQ(StripCommentsAndWhitespace(kFlatbufferSchemaExpected), EXPECT_EQ(StripCommentsAndWhitespace(kFlatbufferSchemaExpected),
StripCommentsAndWhitespace(flatbuffer_schema)) StripCommentsAndWhitespace(flatbuffer_schema))
<< "Schema change detected; update this test and the schema version."; << "Schema change detected; update this test and the schema version.";
EXPECT_EQ(10, GetIndexedRulesetFormatVersionForTesting()) EXPECT_EQ(11, GetIndexedRulesetFormatVersionForTesting())
<< "Update this test if you update the schema version."; << "Update this test if you update the schema version.";
} }
......
...@@ -22,7 +22,6 @@ ...@@ -22,7 +22,6 @@
#include "extensions/browser/api/web_request/web_request_info.h" #include "extensions/browser/api/web_request/web_request_info.h"
#include "extensions/common/api/declarative_net_request.h" #include "extensions/common/api/declarative_net_request.h"
#include "extensions/common/api/declarative_net_request/utils.h" #include "extensions/common/api/declarative_net_request/utils.h"
#include "net/base/escape.h"
#include "net/base/registry_controlled_domains/registry_controlled_domain.h" #include "net/base/registry_controlled_domains/registry_controlled_domain.h"
#include "net/base/url_util.h" #include "net/base/url_util.h"
#include "url/url_constants.h" #include "url/url_constants.h"
...@@ -152,6 +151,12 @@ base::StringPiece CreateStringPiece(const ::flatbuffers::String& str) { ...@@ -152,6 +151,12 @@ base::StringPiece CreateStringPiece(const ::flatbuffers::String& str) {
return base::StringPiece(str.c_str(), str.size()); 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 // Performs any required query transformations on the |url|. Returns true if the
// query should be modified and populates |modified_query|. // query should be modified and populates |modified_query|.
bool GetModifiedQuery(const GURL& url, bool GetModifiedQuery(const GURL& url,
...@@ -159,41 +164,44 @@ bool GetModifiedQuery(const GURL& url, ...@@ -159,41 +164,44 @@ bool GetModifiedQuery(const GURL& url,
std::string* modified_query) { std::string* modified_query) {
DCHECK(modified_query); DCHECK(modified_query);
// TODO(crbug.com/983685): We should be able to reduce the work here by // |remove_query_params| should always be sorted.
// storing the escaped query params in the indexed format. DCHECK(
const bool use_plus = true; IsEmpty(transform.remove_query_params()) ||
std::set<std::string> escaped_remove_query_params; std::is_sorted(transform.remove_query_params()->begin(),
if (transform.remove_query_params()) { transform.remove_query_params()->end(),
for (const ::flatbuffers::String* str : *transform.remove_query_params()) { [](const flatbuffers::String* x1,
escaped_remove_query_params.insert( const flatbuffers::String* x2) { return *x1 < *x2; }));
net::EscapeQueryParamValue(CreateStringPiece(*str), use_plus));
// 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 // 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 // order of different params specified by the extension is respected. We use a
// a std::list to support fast removal from middle of the list. // std::list to support fast removal from middle of the list. Note that the
std::list<std::pair<std::string, std::string>> // key value pairs should already be escaped.
escaped_add_or_replace_query_params; std::list<std::pair<base::StringPiece, base::StringPiece>>
if (transform.add_or_replace_query_params()) { add_or_replace_query_params;
if (!IsEmpty(transform.add_or_replace_query_params())) {
for (const flat::QueryKeyValue* query_pair : for (const flat::QueryKeyValue* query_pair :
*transform.add_or_replace_query_params()) { *transform.add_or_replace_query_params()) {
DCHECK(query_pair->key()); DCHECK(query_pair->key());
DCHECK(query_pair->value()); DCHECK(query_pair->value());
std::string key = net::EscapeQueryParamValue( add_or_replace_query_params.emplace_back(
CreateStringPiece(*query_pair->key()), use_plus); CreateStringPiece(*query_pair->key()),
std::string value = net::EscapeQueryParamValue( CreateStringPiece(*query_pair->value()));
CreateStringPiece(*query_pair->value()), use_plus);
escaped_add_or_replace_query_params.emplace_back(std::move(key),
std::move(value));
} }
} }
if (escaped_add_or_replace_query_params.empty() &&
escaped_remove_query_params.empty()) {
return false;
}
std::vector<std::string> query_parts; std::vector<std::string> query_parts;
auto create_query_part = [](base::StringPiece key, base::StringPiece value) { auto create_query_part = [](base::StringPiece key, base::StringPiece value) {
...@@ -204,20 +212,20 @@ bool GetModifiedQuery(const GURL& url, ...@@ -204,20 +212,20 @@ bool GetModifiedQuery(const GURL& url,
for (net::QueryIterator it(url); !it.IsAtEnd(); it.Advance()) { for (net::QueryIterator it(url); !it.IsAtEnd(); it.Advance()) {
std::string key = it.GetKey(); std::string key = it.GetKey();
// Remove query param. // Remove query param.
if (base::Contains(escaped_remove_query_params, key)) { if (std::binary_search(remove_query_params.begin(),
remove_query_params.end(), key)) {
query_changed = true; query_changed = true;
continue; continue;
} }
auto replace_iterator = auto replace_iterator = std::find_if(
std::find_if(escaped_add_or_replace_query_params.begin(), add_or_replace_query_params.begin(), add_or_replace_query_params.end(),
escaped_add_or_replace_query_params.end(), [&key](const std::pair<base::StringPiece, base::StringPiece>& param) {
[&key](const std::pair<std::string, std::string>& param) {
return param.first == key; return param.first == key;
}); });
// Nothing to do. // Nothing to do.
if (replace_iterator == escaped_add_or_replace_query_params.end()) { if (replace_iterator == add_or_replace_query_params.end()) {
query_parts.push_back(create_query_part(key, it.GetValue())); query_parts.push_back(create_query_part(key, it.GetValue()));
continue; continue;
} }
...@@ -225,11 +233,11 @@ bool GetModifiedQuery(const GURL& url, ...@@ -225,11 +233,11 @@ bool GetModifiedQuery(const GURL& url,
// Replace query param. // Replace query param.
query_changed = true; query_changed = true;
query_parts.push_back(create_query_part(key, replace_iterator->second)); query_parts.push_back(create_query_part(key, replace_iterator->second));
escaped_add_or_replace_query_params.erase(replace_iterator); add_or_replace_query_params.erase(replace_iterator);
} }
// Append any remaining query params. // Append any remaining query params.
for (const auto& params : escaped_add_or_replace_query_params) { for (const auto& params : add_or_replace_query_params) {
query_changed = true; query_changed = true;
query_parts.push_back(create_query_part(params.first, params.second)); query_parts.push_back(create_query_part(params.first, params.second));
} }
......
...@@ -33,7 +33,7 @@ namespace { ...@@ -33,7 +33,7 @@ namespace {
// url_pattern_index.fbs. Whenever an extension with an indexed ruleset format // url_pattern_index.fbs. Whenever an extension with an indexed ruleset format
// version different from the one currently used by Chrome is loaded, the // version different from the one currently used by Chrome is loaded, the
// extension ruleset will be reindexed. // extension ruleset will be reindexed.
constexpr int kIndexedRulesetFormatVersion = 10; constexpr int kIndexedRulesetFormatVersion = 11;
// This static assert is meant to catch cases where // This static assert is meant to catch cases where
// url_pattern_index::kUrlPatternIndexFormatVersion is incremented without // url_pattern_index::kUrlPatternIndexFormatVersion is incremented without
......
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