blob: a51e7ce9b2d46f21b065b705e731fd04bc40d969 [file] [log] [blame]
// Copyright 2017 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/flat_ruleset_indexer.h"
#include <string>
#include "base/logging.h"
#include "base/numerics/safe_conversions.h"
#include "extensions/browser/api/declarative_net_request/indexed_rule.h"
namespace extensions {
namespace declarative_net_request {
namespace {
namespace dnr_api = extensions::api::declarative_net_request;
namespace flat_rule = url_pattern_index::flat;
template <typename T>
using FlatOffset = flatbuffers::Offset<T>;
template <typename T>
using FlatVectorOffset = FlatOffset<flatbuffers::Vector<FlatOffset<T>>>;
using FlatStringOffset = FlatOffset<flatbuffers::String>;
using FlatStringListOffset = FlatVectorOffset<flatbuffers::String>;
// 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
// offset.
FlatStringListOffset BuildVectorOfSharedStrings(
flatbuffers::FlatBufferBuilder* builder,
const std::vector<std::string>& vec) {
if (vec.empty())
return FlatStringListOffset();
std::vector<FlatStringOffset> offsets;
offsets.reserve(vec.size());
for (const auto& str : vec)
offsets.push_back(builder->CreateSharedString(str));
return builder->CreateVector(offsets);
}
std::vector<std::unique_ptr<url_pattern_index::UrlPatternIndexBuilder>>
CreateIndexBuilders(flatbuffers::FlatBufferBuilder* builder) {
std::vector<std::unique_ptr<url_pattern_index::UrlPatternIndexBuilder>>
result(flat::ActionIndex_count);
for (size_t i = 0; i < flat::ActionIndex_count; ++i) {
result[i] =
std::make_unique<url_pattern_index::UrlPatternIndexBuilder>(builder);
}
return result;
}
FlatOffset<flat::UrlTransform> BuildTransformOffset(
flatbuffers::FlatBufferBuilder* builder,
const dnr_api::URLTransform& transform) {
auto create_string_offset =
[builder](const std::unique_ptr<std::string>& str) {
if (!str)
return FlatStringOffset();
return builder->CreateSharedString(*str);
};
auto skip_separator_and_create_string_offset =
[builder](const std::unique_ptr<std::string>& str, char separator) {
if (!str)
return FlatStringOffset();
DCHECK(!str->empty());
DCHECK_EQ(separator, str->at(0));
return builder->CreateSharedString(str->c_str() + 1, str->length() - 1);
};
auto should_clear_component = [](const std::unique_ptr<std::string>& str) {
return str && str->empty();
};
const FlatStringOffset kNullOffset;
FlatStringOffset scheme = create_string_offset(transform.scheme);
FlatStringOffset host = create_string_offset(transform.host);
bool clear_port = should_clear_component(transform.port);
FlatStringOffset port =
clear_port ? kNullOffset : create_string_offset(transform.port);
// Don't skip separator for path. Not all paths begin with '/'.
bool clear_path = should_clear_component(transform.path);
FlatStringOffset path =
clear_path ? kNullOffset : create_string_offset(transform.path);
bool clear_query = should_clear_component(transform.query);
FlatStringOffset query =
clear_query
? kNullOffset
: skip_separator_and_create_string_offset(transform.query, '?');
bool clear_fragment = should_clear_component(transform.fragment);
FlatStringOffset fragment =
clear_fragment
? kNullOffset
: skip_separator_and_create_string_offset(transform.fragment, '#');
FlatStringOffset username = create_string_offset(transform.username);
FlatStringOffset password = create_string_offset(transform.password);
FlatStringListOffset remove_query_params;
if (transform.query_transform && transform.query_transform->remove_params) {
remove_query_params = BuildVectorOfSharedStrings(
builder, *transform.query_transform->remove_params);
}
FlatVectorOffset<flat::QueryKeyValue> add_or_replace_params;
if (transform.query_transform &&
transform.query_transform->add_or_replace_params &&
!transform.query_transform->add_or_replace_params->empty()) {
std::vector<FlatOffset<flat::QueryKeyValue>> add_or_replace_queries;
add_or_replace_queries.reserve(
transform.query_transform->add_or_replace_params->size());
for (const dnr_api::QueryKeyValue& query_pair :
*transform.query_transform->add_or_replace_params) {
add_or_replace_queries.push_back(flat::CreateQueryKeyValue(
*builder, builder->CreateSharedString(query_pair.key),
builder->CreateSharedString(query_pair.value)));
}
add_or_replace_params = builder->CreateVector(add_or_replace_queries);
}
return flat::CreateUrlTransform(*builder, scheme, host, clear_port, port,
clear_path, path, clear_query, query,
remove_query_params, add_or_replace_params,
clear_fragment, fragment, username, password);
}
} // namespace
FlatRulesetIndexer::FlatRulesetIndexer()
: index_builders_(CreateIndexBuilders(&builder_)) {}
FlatRulesetIndexer::~FlatRulesetIndexer() = default;
void FlatRulesetIndexer::AddUrlRule(const IndexedRule& indexed_rule) {
DCHECK(!finished_);
++indexed_rules_count_;
FlatStringListOffset domains_included_offset =
BuildVectorOfSharedStrings(&builder_, indexed_rule.domains);
FlatStringListOffset domains_excluded_offset =
BuildVectorOfSharedStrings(&builder_, indexed_rule.excluded_domains);
FlatStringOffset url_pattern_offset =
builder_.CreateSharedString(indexed_rule.url_pattern);
FlatOffset<flat_rule::UrlRule> offset = flat_rule::CreateUrlRule(
builder_, indexed_rule.options, indexed_rule.element_types,
indexed_rule.activation_types, indexed_rule.url_pattern_type,
indexed_rule.anchor_left, indexed_rule.anchor_right,
domains_included_offset, domains_excluded_offset, url_pattern_offset,
indexed_rule.id, indexed_rule.priority);
std::vector<UrlPatternIndexBuilder*> builders = GetBuilders(indexed_rule);
DCHECK(!builders.empty());
for (UrlPatternIndexBuilder* builder : builders)
builder->IndexUrlRule(offset);
// Store additional metadata required for a redirect rule.
if (indexed_rule.action_type == dnr_api::RULE_ACTION_TYPE_REDIRECT) {
DCHECK(indexed_rule.redirect_url || indexed_rule.url_transform);
if (indexed_rule.redirect_url) {
DCHECK(!indexed_rule.redirect_url->empty());
FlatStringOffset redirect_url_offset =
builder_.CreateSharedString(*indexed_rule.redirect_url);
metadata_.push_back(flat::CreateUrlRuleMetadata(
builder_, indexed_rule.id, redirect_url_offset,
FlatOffset<flat::UrlTransform>()));
} else {
FlatOffset<flat::UrlTransform> transform_offset =
BuildTransformOffset(&builder_, *indexed_rule.url_transform);
metadata_.push_back(flat::CreateUrlRuleMetadata(
builder_, indexed_rule.id, FlatStringOffset() /* redirect_url */,
transform_offset));
}
}
}
void FlatRulesetIndexer::Finish() {
DCHECK(!finished_);
finished_ = true;
std::vector<url_pattern_index::UrlPatternIndexOffset> index_offsets;
index_offsets.reserve(index_builders_.size());
for (const auto& builder : index_builders_)
index_offsets.push_back(builder->Finish());
FlatVectorOffset<url_pattern_index::flat::UrlPatternIndex>
index_vector_offset = builder_.CreateVector(index_offsets);
// Store the extension metadata sorted by ID to support fast lookup through
// binary search.
FlatVectorOffset<flat::UrlRuleMetadata> extension_metadata_offset =
builder_.CreateVectorOfSortedTables(&metadata_);
FlatOffset<flat::ExtensionIndexedRuleset> root_offset =
flat::CreateExtensionIndexedRuleset(builder_, index_vector_offset,
extension_metadata_offset);
flat::FinishExtensionIndexedRulesetBuffer(builder_, root_offset);
}
base::span<const uint8_t> FlatRulesetIndexer::GetData() {
DCHECK(finished_);
return base::make_span(builder_.GetBufferPointer(), builder_.GetSize());
}
std::vector<FlatRulesetIndexer::UrlPatternIndexBuilder*>
FlatRulesetIndexer::GetBuilders(const IndexedRule& indexed_rule) {
switch (indexed_rule.action_type) {
case dnr_api::RULE_ACTION_TYPE_BLOCK:
return {index_builders_[flat::ActionIndex_block].get()};
case dnr_api::RULE_ACTION_TYPE_ALLOW:
return {index_builders_[flat::ActionIndex_allow].get()};
case dnr_api::RULE_ACTION_TYPE_REDIRECT:
return {index_builders_[flat::ActionIndex_redirect].get()};
case dnr_api::RULE_ACTION_TYPE_REMOVEHEADERS:
return GetRemoveHeaderBuilders(indexed_rule.remove_headers_set);
case dnr_api::RULE_ACTION_TYPE_UPGRADESCHEME:
return {index_builders_[flat::ActionIndex_upgrade_scheme].get()};
case dnr_api::RULE_ACTION_TYPE_NONE:
break;
}
NOTREACHED();
return {};
}
std::vector<FlatRulesetIndexer::UrlPatternIndexBuilder*>
FlatRulesetIndexer::GetRemoveHeaderBuilders(
const std::set<dnr_api::RemoveHeaderType>& types) {
// A single "removeHeaders" JSON/indexed rule does still correspond to a
// single flatbuffer rule but can be stored in multiple indices.
DCHECK(!types.empty());
std::vector<UrlPatternIndexBuilder*> result;
for (dnr_api::RemoveHeaderType type : types) {
switch (type) {
case dnr_api::REMOVE_HEADER_TYPE_NONE:
NOTREACHED();
break;
case dnr_api::REMOVE_HEADER_TYPE_COOKIE:
result.push_back(
index_builders_[flat::ActionIndex_remove_cookie_header].get());
break;
case dnr_api::REMOVE_HEADER_TYPE_REFERER:
result.push_back(
index_builders_[flat::ActionIndex_remove_referer_header].get());
break;
case dnr_api::REMOVE_HEADER_TYPE_SETCOOKIE:
result.push_back(
index_builders_[flat::ActionIndex_remove_set_cookie_header].get());
break;
}
}
return result;
}
} // namespace declarative_net_request
} // namespace extensions