blob: 12bad061e926fe2c210fd62bb3968c30bb01c490 [file] [log] [blame]
// 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;
bool operator<(const TrackedInfoContextKey& other) const;
};
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_