910e62b5创建于 1月15日历史提交
// Copyright 2019 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_ACTION_TRACKER_H_
#define EXTENSIONS_BROWSER_API_DECLARATIVE_NET_REQUEST_ACTION_TRACKER_H_

#include <list>
#include <map>
#include <optional>
#include <vector>

#include "base/memory/raw_ptr.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "extensions/browser/api/declarative_net_request/prefs_helper.h"
#include "extensions/common/api/declarative_net_request.h"
#include "extensions/common/api/declarative_net_request/constants.h"
#include "extensions/common/extension_id.h"

namespace base {
class Clock;
class RetainingOneShotTimer;
}

namespace content {
class BrowserContext;
}

namespace extensions {

class Extension;
struct WebRequestInfo;

namespace declarative_net_request {
struct RequestAction;

class ActionTracker {
 public:
  // The lifespan of matched rules in `rules_matched_` not associated with an
  // active document,
  static constexpr base::TimeDelta kNonActiveTabRuleLifespan = base::Minutes(5);

  explicit ActionTracker(content::BrowserContext* browser_context);
  ~ActionTracker();
  ActionTracker(const ActionTracker& other) = delete;
  ActionTracker& operator=(const ActionTracker& other) = delete;

  // TODO(crbug.com/40115239): Use a task environment to avoid having to set
  // clocks just for tests.

  // Sets a custom Clock to use in tests. `clock` should be owned by the caller
  // of this function.
  void SetClockForTests(const base::Clock* clock);

  // Sets a custom RetainingOneShotTimer to use in tests, which replaces
  // `trim_rules_timer_`.
  void SetTimerForTest(
      std::unique_ptr<base::RetainingOneShotTimer> injected_trim_rules_timer);

  // Disables checking whether a tab ID corresponds to an existing tab when a
  // rule is matched. Used for unit tests where WebContents/actual tabs do not
  // exist.
  void SetCheckTabIdOnRuleMatchForTest(bool check_tab_id);

  // Called whenever a request matches with a rule.
  void OnRuleMatched(const RequestAction& request_action,
                     const WebRequestInfo& request_info);

  // Updates the action count for all tabs for the specified `extension_id`'s
  // extension action. Called when the extension calls setExtensionActionOptions
  // to enable setting the action count as badge text.
  void OnActionCountAsBadgeTextPreferenceEnabled(
      const ExtensionId& extension_id) const;

  // Clears the TrackedInfo for the specified `extension_id` for all tabs.
  // Called when an extension's ruleset is removed.
  void ClearExtensionData(const ExtensionId& extension_id);

  // Clears the TrackedInfo for every extension for the specified `tab_id`.
  // Called when the tab has been closed.
  void ClearTabData(int tab_id);

  // Clears the pending action count for every extension in
  // `pending_navigation_actions_` for the specified `navigation_id`.
  void ClearPendingNavigation(int64_t navigation_id);

  // Called when a main-frame navigation to a different document commits.
  // Updates the TrackedInfo for all extensions for the given `tab_id`.
  void ResetTrackedInfoForTab(int tab_id, int64_t navigation_id);

  // Returns all matched rules for `extension`. If `tab_id` is provided, only
  // rules matched for `tab_id` will be returned.
  std::vector<api::declarative_net_request::MatchedRuleInfo> GetMatchedRules(
      const Extension& extension,
      const std::optional<int>& tab_id,
      const base::Time& min_time_stamp);

  // Returns the number of matched rules in `rules_tracked_` for the given
  // `extension_id` and `tab_id`. If `trim_non_active_rules` is true,
  // TrimRulesFromNonActiveTabs is invoked before returning the matched rule
  // count, similar to GetMatchedRules. Should only be used for tests.
  int GetMatchedRuleCountForTest(const ExtensionId& extension_id,
                                 int tab_id,
                                 bool trim_non_active_rules);

  // Returns the number of matched rules in `pending_navigation_actions_` for
  // the given `extension_id` and `navigation_id`. Should only be used for
  // tests.
  int GetPendingRuleCountForTest(const ExtensionId& extension_id,
                                 int64_t navigation_id);

  // Increments the action count for the given `extension_id` and `tab_id`.
  // A negative value for `increment` will decrement the action count, but the
  // action count will never be less than 0.
  void IncrementActionCountForTab(const ExtensionId& extension_id,
                                  int tab_id,
                                  int increment);

 private:
  // Template key type used for TrackedInfo, specified by an extension_id and
  // another ID.
  template <typename T>
  struct TrackedInfoContextKey {
    TrackedInfoContextKey(ExtensionId extension_id, T secondary_id);
    TrackedInfoContextKey(const TrackedInfoContextKey& other) = delete;
    TrackedInfoContextKey& operator=(const TrackedInfoContextKey& other) =
        delete;
    TrackedInfoContextKey(TrackedInfoContextKey&&);
    TrackedInfoContextKey& operator=(TrackedInfoContextKey&&);

    ExtensionId extension_id;
    T secondary_id;

    auto operator<=>(const TrackedInfoContextKey& rhs) const = default;
  };

  using ExtensionTabIdKey = TrackedInfoContextKey<int>;
  using ExtensionNavigationIdKey = TrackedInfoContextKey<int64_t>;

  // Represents a matched rule. This is used as a lightweight version of
  // api::declarative_net_request::MatchedRuleInfo.
  struct TrackedRule {
    TrackedRule(int rule_id, RulesetID ruleset_id);
    TrackedRule(const TrackedRule& other) = delete;
    TrackedRule& operator=(const TrackedRule& other) = delete;

    const int rule_id;
    const RulesetID ruleset_id;

    // The timestamp for when the rule was matched.
    const base::Time time_stamp;
  };

  // Info tracked for each ExtensionTabIdKey or ExtensionNavigationIdKey.
  struct TrackedInfo {
    TrackedInfo();
    ~TrackedInfo();
    TrackedInfo(const TrackedInfo& other) = delete;
    TrackedInfo& operator=(const TrackedInfo& other) = delete;
    TrackedInfo(TrackedInfo&&);
    TrackedInfo& operator=(TrackedInfo&&);

    // The number of actions matched. Invalid when the corresponding
    // TrackedInfoContextKey has a tab_id of -1. Does not include allow rules.
    size_t action_count = 0;

    // The list of rules matched. Includes allow rules.
    std::list<TrackedRule> matched_rules;
  };

  // Called from OnRuleMatched. Dispatches a OnRuleMatchedDebug event to the
  // observer for the extension specified by |request_action.extension_id|.
  void DispatchOnRuleMatchedDebugIfNeeded(
      const RequestAction& request_action,
      api::declarative_net_request::RequestDetails request_details);

  // For all matched rules attributed to `tab_id`, set their tab ID to the
  // unknown tab ID (-1). Called by ClearTabData and ResetActionCountForTab.
  void TransferRulesOnTabInvalid(int tab_id);

  // Removes all rules in `rules_tracked_` older than
  // `kNonActiveTabRuleLifespan` from non active tabs (i.e. `tab_id` = -1).
  // Called periodically to ensure no rules attributed to the unknown tab ID in
  // `rules_tracked_` are older than `kNonActiveTabRuleLifespan`.
  void TrimRulesFromNonActiveTabs();

  // Schedules TrimRulesFromNonActiveTabs to be run after
  // `kNonActiveTabRuleLifespan`. Called in the constructor and whenever
  // `trim_rules_timer_` gets set.
  void StartTrimRulesTask();

  // Converts an internally represented `tracked_rule` to a MatchedRuleInfo.
  api::declarative_net_request::MatchedRuleInfo CreateMatchedRuleInfo(
      const Extension& extension,
      const TrackedRule& tracked_rule,
      int tab_id) const;

  // A timer to call TrimRulesFromNonActiveTabs with an interval of
  // `kNonActiveTabRuleLifespan`.
  std::unique_ptr<base::RetainingOneShotTimer> trim_rules_timer_ =
      std::make_unique<base::RetainingOneShotTimer>();

  // Maps a pair of (extension ID, tab ID) to the TrackedInfo for that pair.
  std::map<ExtensionTabIdKey, TrackedInfo> rules_tracked_;

  // Maps a pair of (extension ID, navigation ID) to the TrackedInfo matched for
  // the main-frame request associated with the navigation ID in the key. The
  // TrackedInfo is added to `rules_tracked_` once the navigation commits.
  std::map<ExtensionNavigationIdKey, TrackedInfo> pending_navigation_actions_;

  raw_ptr<content::BrowserContext> browser_context_;

  PrefsHelper prefs_helper_;
};

}  // namespace declarative_net_request
}  // namespace extensions

#endif  // EXTENSIONS_BROWSER_API_DECLARATIVE_NET_REQUEST_ACTION_TRACKER_H_