blob: 7e0a3adddbb20f479f35675e64579921b81b5456 [file] [log] [blame]
// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef EXTENSIONS_BROWSER_API_DECLARATIVE_NET_REQUEST_RULESET_MANAGER_H_
#define EXTENSIONS_BROWSER_API_DECLARATIVE_NET_REQUEST_RULESET_MANAGER_H_
#include <stddef.h>
#include <memory>
#include <optional>
#include <set>
#include <utility>
#include <vector>
#include "base/containers/flat_set.h"
#include "base/memory/raw_ptr.h"
#include "base/sequence_checker.h"
#include "base/time/time.h"
#include "extensions/browser/api/declarative_net_request/constants.h"
#include "extensions/browser/api/declarative_net_request/utils.h"
#include "extensions/common/extension_id.h"
#include "extensions/common/permissions/permissions_data.h"
namespace content {
class BrowserContext;
class NavigationHandle;
class RenderFrameHost;
}
namespace net {
class HttpResponseHeaders;
}
namespace extensions {
class ExtensionPrefs;
class PermissionHelper;
struct WebRequestInfo;
namespace declarative_net_request {
class CompositeMatcher;
struct RequestAction;
struct RequestParams;
// Manages the set of active rulesets for the Declarative Net Request API. Can
// be constructed on any sequence but must be accessed and destroyed from the
// same sequence.
class RulesetManager {
public:
explicit RulesetManager(content::BrowserContext* browser_context);
RulesetManager(const RulesetManager&) = delete;
RulesetManager& operator=(const RulesetManager&) = delete;
~RulesetManager();
// An observer used for testing purposes.
class TestObserver {
public:
virtual void OnEvaluateRequest(const WebRequestInfo& request,
bool is_incognito_context) {}
// Called whenever a ruleset is added or removed.
virtual void OnRulesetCountChanged(size_t new_count) {}
protected:
virtual ~TestObserver() {}
};
// Adds the ruleset for the given `extension_id`. Should not be called twice
// in succession for an extension.
void AddRuleset(const ExtensionId& extension_id,
std::unique_ptr<CompositeMatcher> matcher);
// Removes the ruleset for `extension_id`. Should be called only after a
// corresponding AddRuleset.
void RemoveRuleset(const ExtensionId& extension_id);
// Returns the set of extensions which have active rulesets.
std::set<ExtensionId> GetExtensionsWithRulesets() const;
// Returns the CompositeMatcher corresponding to the `extension_id` or null
// if no matcher is present for the extension.
CompositeMatcher* GetMatcherForExtension(const ExtensionId& extension_id);
const CompositeMatcher* GetMatcherForExtension(
const ExtensionId& extension_id) const;
// Returns the action to take for the given request before it is sent.
// Note: this can return an `ALLOW` or `ALLOW_ALL_REQUESTS` rule which is
// effectively a no-op. We do this to ensure that matched allow rules are
// correctly tracked by the `getMatchedRules` and `OnRuleMatchedDebug` APIs.
// Note: the returned action is owned by `request`.
const std::vector<RequestAction>& EvaluateBeforeRequest(
const WebRequestInfo& request,
bool is_incognito_context) const;
// Returns the action to take for the given request after response headers
// have been received.
// Note: See comments for `EvaluateBeforeRequest` above for notes on returning
// an ALLOW or ALLOW_ALL_REQUESTS action.
std::vector<RequestAction> EvaluateRequestWithHeaders(
const WebRequestInfo& request,
const net::HttpResponseHeaders* response_headers,
bool is_incognito_context) const;
// Returns true if there is an active matcher which modifies "extraHeaders".
bool HasAnyExtraHeadersMatcher() const;
// Returns true if there is a matcher which modifies "extraHeaders" for the
// given `request`.
bool HasExtraHeadersMatcherForRequest(const WebRequestInfo& request,
bool is_incognito_context) const;
void OnRenderFrameCreated(content::RenderFrameHost* host);
void OnRenderFrameDeleted(content::RenderFrameHost* host);
void OnDidFinishNavigation(content::NavigationHandle* navigation_handle);
// Returns if there are any matchers containing rules for the corresponding
// request matching `stage`.
bool HasRulesets(RulesetMatchingStage stage) const;
// Merges two lists of modifyHeaders actions and returns a list containing
// actions from both lists sorted in descending order of priority.
std::vector<RequestAction> MergeModifyHeaderActions(
std::vector<RequestAction> lhs_actions,
std::vector<RequestAction> rhs_actions) const;
// Returns the number of CompositeMatchers currently being managed.
size_t GetMatcherCountForTest() const { return rulesets_.size(); }
// Sets the TestObserver. Client maintains ownership of `observer`.
void SetObserverForTest(TestObserver* observer);
private:
struct ExtensionRulesetData {
ExtensionRulesetData(const ExtensionId& extension_id,
const base::Time& extension_install_time,
std::unique_ptr<CompositeMatcher> matcher);
ExtensionRulesetData(const ExtensionRulesetData&) = delete;
ExtensionRulesetData(ExtensionRulesetData&& other);
ExtensionRulesetData& operator=(const ExtensionRulesetData&) = delete;
ExtensionRulesetData& operator=(ExtensionRulesetData&& other);
~ExtensionRulesetData();
ExtensionId extension_id;
base::Time extension_install_time;
std::unique_ptr<CompositeMatcher> matcher;
bool operator<(const ExtensionRulesetData& other) const;
};
using RulesetAndPageAccess =
std::pair<const ExtensionRulesetData*, PermissionsData::PageAccess>;
std::optional<RequestAction> GetAction(
const std::vector<RulesetAndPageAccess>& rulesets,
const WebRequestInfo& request,
const RequestParams& params,
RulesetMatchingStage stage) const;
// Returns the list of matching modifyHeaders actions sorted in descending
// order of priority (`rulesets` is sorted in descending order of extension
// priority.)
std::vector<RequestAction> GetModifyHeadersActions(
const std::vector<RulesetAndPageAccess>& rulesets,
const WebRequestInfo& request,
const RequestParams& params,
RulesetMatchingStage stage) const;
// Helper for EvaluateRequest.
std::vector<RequestAction> EvaluateRequestInternal(
const WebRequestInfo& request,
const net::HttpResponseHeaders* response_headers,
bool is_incognito_context) const;
// Returns true if the given `request` should be evaluated for
// blocking/redirection.
bool ShouldEvaluateRequest(const WebRequestInfo& request) const;
// Returns whether `ruleset` should be evaluated for the given `request`.
// Returns true if it should and populates `host_permission_access`.
bool ShouldEvaluateRulesetForRequest(
const ExtensionRulesetData& ruleset,
const WebRequestInfo& request,
bool is_incognito_context,
PermissionsData::PageAccess& host_permission_access) const;
// Sorted in decreasing order of `extension_install_time`.
// Use a flat_set instead of std::set/map. This makes [Add/Remove]Ruleset
// O(n), but it's fine since the no. of rulesets are expected to be quite
// small.
base::flat_set<ExtensionRulesetData> rulesets_;
// Maps an extension ID to its install time. Used to determine an extension's
// ruleset matching priority order relative to other extensions, with matched
// rules/actions from more recently installed extensions having higher
// precedence.
std::map<ExtensionId, base::Time> extension_install_times_;
// Non-owning pointer to BrowserContext.
const raw_ptr<content::BrowserContext> browser_context_;
// Guaranteed to be valid through-out the lifetime of this instance.
const raw_ptr<ExtensionPrefs> prefs_;
const raw_ptr<PermissionHelper> permission_helper_;
// Non-owning pointer to TestObserver.
raw_ptr<TestObserver> test_observer_ = nullptr;
SEQUENCE_CHECKER(sequence_checker_);
};
} // namespace declarative_net_request
} // namespace extensions
#endif // EXTENSIONS_BROWSER_API_DECLARATIVE_NET_REQUEST_RULESET_MANAGER_H_