910e62b5创建于 1月15日历史提交
// 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_