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.

#include "extensions/browser/api/declarative_net_request/ruleset_matcher.h"

#include <iterator>
#include <optional>
#include <utility>

#include "base/check.h"
#include "base/containers/flat_set.h"
#include "base/containers/span.h"
#include "base/memory/ptr_util.h"
#include "base/metrics/histogram_macros.h"
#include "base/time/time.h"
#include "extensions/browser/api/declarative_net_request/constants.h"
#include "extensions/browser/api/declarative_net_request/flat/extension_ruleset_generated.h"
#include "extensions/browser/api/declarative_net_request/request_action.h"
#include "extensions/browser/api/declarative_net_request/rule_counts.h"
#include "extensions/browser/api/declarative_net_request/ruleset_matcher_base.h"
#include "extensions/browser/api/declarative_net_request/utils.h"
#include "extensions/common/api/declarative_net_request/constants.h"

namespace extensions::declarative_net_request {

namespace {

using ExtensionMetadataList =
    flatbuffers::Vector<flatbuffers::Offset<flat::UrlRuleMetadata>>;

// These constants specify the number of, the minimum, and the maximum buckets
// for histograms which record the evaluation time for a request against a
// single DNR ruleset.
constexpr int kRequestActionTimeUmaBucketCount = 50;
constexpr base::TimeDelta kRequestActionUmaMinTime = base::Microseconds(1);
constexpr base::TimeDelta kRequestActionUmaMaxTime = base::Seconds(3);

size_t ComputeUnsafeRuleCount(const ExtensionMetadataList* metadata_list) {
  size_t unsafe_rule_count = 0;
  for (const auto* url_rule_metadata : *metadata_list) {
    if (!IsRuleSafe(*url_rule_metadata)) {
      unsafe_rule_count++;
    }
  }
  return unsafe_rule_count;
}

bool IsRulesetStatic(const RulesetID& id) {
  return id != kDynamicRulesetID && id != kSessionRulesetID;
}

void RecordOnBeforeRequestActionTime(const base::TimeTicks& start_time,
                                     const base::TimeDelta& regex_time,
                                     const base::TimeDelta& total_time,
                                     int rules_count,
                                     int regex_rules_count) {
  int percent_taken_by_regex = 0;
  // It's possible that the rule evaluation took no measurable time; be sure we
  // don't divide by zero.
  if (regex_time.is_positive()) {
    percent_taken_by_regex =
        static_cast<int>((regex_time / total_time) * 100.0);
  }

  if (regex_rules_count > 0) {
    UMA_HISTOGRAM_PERCENTAGE(
        "Extensions.DeclarativeNetRequest."
        "RegexRulesBeforeRequestEvaluationPercentage",
        percent_taken_by_regex);

    if (regex_rules_count < 15) {
      UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES(
          "Extensions.DeclarativeNetRequest.RegexRulesBeforeRequestActionTime."
          "LessThan15Rules",
          regex_time, kRequestActionUmaMinTime, kRequestActionUmaMaxTime,
          kRequestActionTimeUmaBucketCount);
    } else if (regex_rules_count < 100) {
      UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES(
          "Extensions.DeclarativeNetRequest.RegexRulesBeforeRequestActionTime."
          "15To100Rules",
          regex_time, kRequestActionUmaMinTime, kRequestActionUmaMaxTime,
          kRequestActionTimeUmaBucketCount);
    } else if (regex_rules_count < 500) {
      UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES(
          "Extensions.DeclarativeNetRequest.RegexRulesBeforeRequestActionTime."
          "100To500Rules",
          regex_time, kRequestActionUmaMinTime, kRequestActionUmaMaxTime,
          kRequestActionTimeUmaBucketCount);
    } else {
      UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES(
          "Extensions.DeclarativeNetRequest.RegexRulesBeforeRequestActionTime."
          "Over500Rules",
          regex_time, kRequestActionUmaMinTime, kRequestActionUmaMaxTime,
          kRequestActionTimeUmaBucketCount);
    }
  }

  if (rules_count < 1000) {
    UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES(
        "Extensions.DeclarativeNetRequest."
        "RulesetMatchingBeforeRequestActionTime."
        "LessThan1000Rules",
        total_time, kRequestActionUmaMinTime, kRequestActionUmaMaxTime,
        kRequestActionTimeUmaBucketCount);
  } else if (rules_count < 10000) {
    UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES(
        "Extensions.DeclarativeNetRequest."
        "RulesetMatchingBeforeRequestActionTime."
        "1000To10000Rules",
        total_time, kRequestActionUmaMinTime, kRequestActionUmaMaxTime,
        kRequestActionTimeUmaBucketCount);
  } else if (rules_count < 30000) {
    UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES(
        "Extensions.DeclarativeNetRequest."
        "RulesetMatchingBeforeRequestActionTime."
        "10000To30000Rules",
        total_time, kRequestActionUmaMinTime, kRequestActionUmaMaxTime,
        kRequestActionTimeUmaBucketCount);
  } else if (rules_count < 100000) {
    UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES(
        "Extensions.DeclarativeNetRequest."
        "RulesetMatchingBeforeRequestActionTime."
        "30000To100000Rules",
        total_time, kRequestActionUmaMinTime, kRequestActionUmaMaxTime,
        kRequestActionTimeUmaBucketCount);
  } else if (rules_count < 300000) {
    UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES(
        "Extensions.DeclarativeNetRequest."
        "RulesetMatchingBeforeRequestActionTime."
        "100000To300000Rules",
        total_time, kRequestActionUmaMinTime, kRequestActionUmaMaxTime,
        kRequestActionTimeUmaBucketCount);
  } else {
    UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES(
        "Extensions.DeclarativeNetRequest."
        "RulesetMatchingBeforeRequestActionTime."
        "Over300000Rules",
        total_time, kRequestActionUmaMinTime, kRequestActionUmaMaxTime,
        kRequestActionTimeUmaBucketCount);
  }
}

void RecordOnHeadersReceivedActionTime(const base::TimeTicks& start_time,
                                       const base::TimeDelta& regex_time,
                                       const base::TimeDelta& total_time,
                                       int rules_count,
                                       int regex_rules_count) {
  int percent_taken_by_regex = 0;
  // It's possible that the rule evaluation took no measurable time; be sure we
  // don't divide by zero.
  if (regex_time.is_positive()) {
    percent_taken_by_regex =
        static_cast<int>((regex_time / total_time) * 100.0);
  }

  if (regex_rules_count > 0) {
    UMA_HISTOGRAM_PERCENTAGE(
        "Extensions.DeclarativeNetRequest."
        "RegexRulesHeadersReceivedEvaluationPercentage",
        percent_taken_by_regex);

    if (regex_rules_count < 15) {
      UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES(
          "Extensions.DeclarativeNetRequest."
          "RegexRulesHeadersReceivedActionTime."
          "LessThan15Rules",
          regex_time, kRequestActionUmaMinTime, kRequestActionUmaMaxTime,
          kRequestActionTimeUmaBucketCount);
    } else if (regex_rules_count < 100) {
      UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES(
          "Extensions.DeclarativeNetRequest."
          "RegexRulesHeadersReceivedActionTime."
          "15To100Rules",
          regex_time, kRequestActionUmaMinTime, kRequestActionUmaMaxTime,
          kRequestActionTimeUmaBucketCount);
    } else if (regex_rules_count < 500) {
      UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES(
          "Extensions.DeclarativeNetRequest."
          "RegexRulesHeadersReceivedActionTime."
          "100To500Rules",
          regex_time, kRequestActionUmaMinTime, kRequestActionUmaMaxTime,
          kRequestActionTimeUmaBucketCount);
    } else {
      UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES(
          "Extensions.DeclarativeNetRequest."
          "RegexRulesHeadersReceivedActionTime."
          "Over500Rules",
          regex_time, kRequestActionUmaMinTime, kRequestActionUmaMaxTime,
          kRequestActionTimeUmaBucketCount);
    }
  }

  if (rules_count < 1000) {
    UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES(
        "Extensions.DeclarativeNetRequest."
        "RulesetMatchingHeadersReceivedActionTime."
        "LessThan1000Rules",
        total_time, kRequestActionUmaMinTime, kRequestActionUmaMaxTime,
        kRequestActionTimeUmaBucketCount);
  } else if (rules_count < 10000) {
    UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES(
        "Extensions.DeclarativeNetRequest."
        "RulesetMatchingHeadersReceivedActionTime."
        "1000To10000Rules",
        total_time, kRequestActionUmaMinTime, kRequestActionUmaMaxTime,
        kRequestActionTimeUmaBucketCount);
  } else if (rules_count < 30000) {
    UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES(
        "Extensions.DeclarativeNetRequest."
        "RulesetMatchingHeadersReceivedActionTime."
        "10000To30000Rules",
        total_time, kRequestActionUmaMinTime, kRequestActionUmaMaxTime,
        kRequestActionTimeUmaBucketCount);
  } else if (rules_count < 100000) {
    UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES(
        "Extensions.DeclarativeNetRequest."
        "RulesetMatchingHeadersReceivedActionTime."
        "30000To100000Rules",
        total_time, kRequestActionUmaMinTime, kRequestActionUmaMaxTime,
        kRequestActionTimeUmaBucketCount);
  } else if (rules_count < 300000) {
    UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES(
        "Extensions.DeclarativeNetRequest."
        "RulesetMatchingHeadersReceivedActionTime."
        "100000To300000Rules",
        total_time, kRequestActionUmaMinTime, kRequestActionUmaMaxTime,
        kRequestActionTimeUmaBucketCount);
  } else {
    UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES(
        "Extensions.DeclarativeNetRequest."
        "RulesetMatchingHeadersReceivedActionTime."
        "Over300000Rules",
        total_time, kRequestActionUmaMinTime, kRequestActionUmaMaxTime,
        kRequestActionTimeUmaBucketCount);
  }
}

}  // namespace

RulesetMatcher::RulesetMatcher(std::string ruleset_data,
                               RulesetID id,
                               const ExtensionId& extension_id)
    : ruleset_data_(std::move(ruleset_data)),
      root_(flat::GetExtensionIndexedRuleset(ruleset_data_.data())),
      id_(id),
      url_matcher_(extension_id,
                   id,
                   root_->before_request_index_list(),
                   root_->headers_received_index_list(),
                   root_->extension_metadata()),
      regex_matcher_(extension_id,
                     id,
                     root_->before_request_regex_rules(),
                     root_->headers_received_regex_rules(),
                     root_->extension_metadata()) {
  if (!IsRulesetStatic(id)) {
    unsafe_rule_count_ = ComputeUnsafeRuleCount(root_->extension_metadata());
  }
}

RulesetMatcher::~RulesetMatcher() = default;

std::optional<RequestAction> RulesetMatcher::GetAction(
    const RequestParams& params,
    RulesetMatchingStage stage) const {
  base::TimeTicks start_time = base::TimeTicks::Now();
  std::optional<RequestAction> regex_result =
      regex_matcher_.GetAction(params, stage);
  base::TimeDelta regex_time = base::TimeTicks::Now() - start_time;
  std::optional<RequestAction> url_pattern_result =
      url_matcher_.GetAction(params, stage);
  std::optional<RequestAction> final_result = GetMaxPriorityAction(
      std::move(url_pattern_result), std::move(regex_result));
  base::TimeDelta total_time = base::TimeTicks::Now() - start_time;
  int regex_rules_count = GetRegexRulesCount(stage);
  int rules_count = GetRulesCount(stage);

  switch (stage) {
    case RulesetMatchingStage::kOnBeforeRequest:
      RecordOnBeforeRequestActionTime(start_time, regex_time, total_time,
                                      rules_count, regex_rules_count);
      break;
    case RulesetMatchingStage::kOnHeadersReceived:
      RecordOnHeadersReceivedActionTime(start_time, regex_time, total_time,
                                        rules_count, regex_rules_count);
      break;
  }

  return final_result;
}

std::vector<RequestAction> RulesetMatcher::GetModifyHeadersActions(
    const RequestParams& params,
    RulesetMatchingStage stage,
    std::optional<uint64_t> min_priority) const {
  std::vector<RequestAction> modify_header_actions =
      url_matcher_.GetModifyHeadersActions(params, stage, min_priority);

  std::vector<RequestAction> regex_modify_header_actions =
      regex_matcher_.GetModifyHeadersActions(params, stage, min_priority);

  modify_header_actions.insert(
      modify_header_actions.end(),
      std::make_move_iterator(regex_modify_header_actions.begin()),
      std::make_move_iterator(regex_modify_header_actions.end()));

  return modify_header_actions;
}

bool RulesetMatcher::IsExtraHeadersMatcher() const {
  return url_matcher_.IsExtraHeadersMatcher() ||
         regex_matcher_.IsExtraHeadersMatcher();
}

size_t RulesetMatcher::GetRulesCount() const {
  return url_matcher_.GetRulesCount() + regex_matcher_.GetRulesCount();
}

std::optional<size_t> RulesetMatcher::GetUnsafeRulesCount() const {
  return unsafe_rule_count_;
}

size_t RulesetMatcher::GetRegexRulesCount() const {
  return regex_matcher_.GetRulesCount();
}

RuleCounts RulesetMatcher::GetRuleCounts() const {
  return RuleCounts(GetRulesCount(), unsafe_rule_count_, GetRegexRulesCount());
}

void RulesetMatcher::OnRenderFrameCreated(content::RenderFrameHost* host) {
  url_matcher_.OnRenderFrameCreated(host);
  regex_matcher_.OnRenderFrameCreated(host);
}

void RulesetMatcher::OnRenderFrameDeleted(content::RenderFrameHost* host) {
  url_matcher_.OnRenderFrameDeleted(host);
  regex_matcher_.OnRenderFrameDeleted(host);
}

void RulesetMatcher::OnDidFinishNavigation(
    content::NavigationHandle* navigation_handle) {
  url_matcher_.OnDidFinishNavigation(navigation_handle);
  regex_matcher_.OnDidFinishNavigation(navigation_handle);
}

std::optional<RequestAction>
RulesetMatcher::GetAllowlistedFrameActionForTesting(
    content::RenderFrameHost* host) const {
  return GetMaxPriorityAction(
      url_matcher_.GetAllowlistedFrameActionForTesting(host),     // IN-TEST
      regex_matcher_.GetAllowlistedFrameActionForTesting(host));  // IN-TEST
}

void RulesetMatcher::SetDisabledRuleIds(base::flat_set<int> disabled_rule_ids) {
  url_matcher_.SetDisabledRuleIds(std::move(disabled_rule_ids));
}

const base::flat_set<int>& RulesetMatcher::GetDisabledRuleIdsForTesting()
    const {
  return url_matcher_.GetDisabledRuleIdsForTesting();  // IN-TEST
}

size_t RulesetMatcher::GetRulesCount(RulesetMatchingStage stage) const {
  switch (stage) {
    case RulesetMatchingStage::kOnBeforeRequest:
      return url_matcher_.GetBeforeRequestRulesCount() +
             regex_matcher_.GetBeforeRequestRulesCount();
    case RulesetMatchingStage::kOnHeadersReceived:
      return url_matcher_.GetHeadersReceivedRulesCount() +
             regex_matcher_.GetHeadersReceivedRulesCount();
  }

  NOTREACHED();
}

size_t RulesetMatcher::GetRegexRulesCount(RulesetMatchingStage stage) const {
  switch (stage) {
    case RulesetMatchingStage::kOnBeforeRequest:
      return regex_matcher_.GetBeforeRequestRulesCount();
    case RulesetMatchingStage::kOnHeadersReceived:
      return regex_matcher_.GetHeadersReceivedRulesCount();
  }

  NOTREACHED();
}

}  // namespace extensions::declarative_net_request