910e62b5创建于 1月15日历史提交
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "components/commerce/core/commerce_heuristics_data.h"

#include "base/json/json_reader.h"
#include "base/json/json_writer.h"
#include "base/no_destructor.h"
#include "base/time/time_delta_from_string.h"

namespace commerce_heuristics {

namespace {
// CommerceHintHeuristics types.
constexpr char kMerchantNameType[] = "merchant_name";
constexpr char kMerchantCartURLType[] = "cart_url";
constexpr char kMerchantCartURLRegexType[] = "cart_url_regex";
constexpr char kMerchantCheckoutURLRegexType[] = "checkout_url_regex";
constexpr char kMerchantPurchaseURLRegexType[] = "purchase_url_regex";
constexpr char kSkipAddToCartRegexType[] = "skip_add_to_cart_regex";

// CommerceGlobalHeuristics types.
constexpr char kSkipProductPatternType[] = "sensitive_product_regex";
constexpr char kRuleDiscountPartnerMerchantPatternType[] =
    "rule_discount_partner_merchant_regex";
constexpr char kCouponDiscountPartnerMerchantPatternType[] =
    "coupon_discount_partner_merchant_regex";
constexpr char kNoDiscountMerchantPatternType[] = "no_discount_merchant_regex";
constexpr char kCartPagetURLPatternType[] = "cart_page_url_regex";
constexpr char kCheckoutPageURLPatternType[] = "checkout_page_url_regex";
constexpr char kPurchaseButtonTextPatternType[] = "purchase_button_text_regex";
constexpr char kAddToCartRequestPatternType[] = "add_to_cart_request_regex";
constexpr char kDiscountFetchDelayType[] = "discount_fetch_delay";

}  // namespace

// static
CommerceHeuristicsData& CommerceHeuristicsData::GetInstance() {
  static base::NoDestructor<CommerceHeuristicsData> instance;
  return *instance;
}

CommerceHeuristicsData::CommerceHeuristicsData() = default;
CommerceHeuristicsData::~CommerceHeuristicsData() = default;

void CommerceHeuristicsData::UpdateVersion(base::Version version) {
  version_ = std::move(version);
}

const std::string CommerceHeuristicsData::GetVersion() {
  if (!version_.IsValid())
    return std::string();
  return version_.GetString();
}

bool CommerceHeuristicsData::PopulateDataFromComponent(
    const std::string& hint_json_data,
    const std::string& global_json_data,
    const std::string& product_id_json_data,
    const std::string& cart_extraction_script) {
  auto hint_json_value = base::JSONReader::ReadDict(
      hint_json_data, base::JSON_PARSE_CHROMIUM_EXTENSIONS);
  auto global_json_value = base::JSONReader::ReadDict(
      global_json_data, base::JSON_PARSE_CHROMIUM_EXTENSIONS);
  if (!hint_json_value) {
    return false;
  }
  if (!global_json_value) {
    return false;
  }
  hint_heuristics_ = std::move(*hint_json_value);
  global_heuristics_string_ = global_json_data;
  global_heuristics_ = std::move(*global_json_value);
  // Global regex patterns.
  product_skip_pattern_ = ConstructGlobalRegex(kSkipProductPatternType);
  rule_discount_partner_merchant_pattern_ =
      ConstructGlobalRegex(kRuleDiscountPartnerMerchantPatternType);
  coupon_discount_partner_merchant_pattern_ =
      ConstructGlobalRegex(kCouponDiscountPartnerMerchantPatternType);
  no_discount_merchant_pattern_ =
      ConstructGlobalRegex(kNoDiscountMerchantPatternType);
  cart_url_pattern_ = ConstructGlobalRegex(kCartPagetURLPatternType);
  checkout_url_pattern_ = ConstructGlobalRegex(kCheckoutPageURLPatternType);
  purchase_button_pattern_ =
      ConstructGlobalRegex(kPurchaseButtonTextPatternType);
  add_to_cart_request_pattern_ =
      ConstructGlobalRegex(kAddToCartRequestPatternType);
  product_id_json_ = product_id_json_data;
  cart_extraction_script_ = cart_extraction_script;
  domain_cart_url_pattern_mapping_.clear();
  domain_checkout_url_pattern_mapping_.clear();
  domain_purchase_url_pattern_mapping_.clear();
  domain_skip_add_to_cart_pattern_mapping_.clear();
  return true;
}

std::optional<std::string> CommerceHeuristicsData::GetMerchantName(
    const std::string& domain) {
  return GetCommerceHintHeuristics(kMerchantNameType, domain);
}

std::optional<std::string> CommerceHeuristicsData::GetMerchantCartURL(
    const std::string& domain) {
  return GetCommerceHintHeuristics(kMerchantCartURLType, domain);
}

std::optional<std::string>
CommerceHeuristicsData::GetHintHeuristicsJSONForDomain(
    const std::string& domain) {
  if (!hint_heuristics_.contains(domain)) {
    return std::nullopt;
  }
  base::Value::Dict domain_heuristics =
      hint_heuristics_.FindDict(domain)->Clone();
  if (domain_heuristics.empty()) {
    return std::nullopt;
  }
  base::Value::Dict res_dic;
  res_dic.Set(domain, std::move(domain_heuristics));
  return base::WriteJson(res_dic).value_or("");
}

std::optional<std::string> CommerceHeuristicsData::GetGlobalHeuristicsJSON() {
  return global_heuristics_string_;
}

const re2::RE2* CommerceHeuristicsData::GetProductSkipPattern() {
  return product_skip_pattern_.get();
}

const re2::RE2*
CommerceHeuristicsData::GetRuleDiscountPartnerMerchantPattern() {
  return rule_discount_partner_merchant_pattern_.get();
}

const re2::RE2*
CommerceHeuristicsData::GetCouponDiscountPartnerMerchantPattern() {
  return coupon_discount_partner_merchant_pattern_.get();
}

const re2::RE2* CommerceHeuristicsData::GetNoDiscountMerchantPattern() {
  return no_discount_merchant_pattern_.get();
}

const re2::RE2* CommerceHeuristicsData::GetCartPageURLPattern() {
  return cart_url_pattern_.get();
}

const re2::RE2* CommerceHeuristicsData::GetCheckoutPageURLPattern() {
  return checkout_url_pattern_.get();
}

const re2::RE2* CommerceHeuristicsData::GetPurchaseButtonTextPattern() {
  return purchase_button_pattern_.get();
}

const re2::RE2* CommerceHeuristicsData::GetAddToCartRequestPattern() {
  return add_to_cart_request_pattern_.get();
}

const re2::RE2* CommerceHeuristicsData::GetCartPageURLPatternForDomain(
    const std::string& domain) {
  return GetCommerceHintHeuristicsRegex(domain_cart_url_pattern_mapping_,
                                        kMerchantCartURLRegexType, domain);
}

const re2::RE2* CommerceHeuristicsData::GetCheckoutPageURLPatternForDomain(
    const std::string& domain) {
  return GetCommerceHintHeuristicsRegex(domain_checkout_url_pattern_mapping_,
                                        kMerchantCheckoutURLRegexType, domain);
}

const re2::RE2* CommerceHeuristicsData::GetPurchasePageURLPatternForDomain(
    const std::string& domain) {
  return GetCommerceHintHeuristicsRegex(domain_purchase_url_pattern_mapping_,
                                        kMerchantPurchaseURLRegexType, domain);
}

const re2::RE2* CommerceHeuristicsData::GetSkipAddToCartPatternForDomain(
    const std::string& domain) {
  return GetCommerceHintHeuristicsRegex(
      domain_skip_add_to_cart_pattern_mapping_, kSkipAddToCartRegexType,
      domain);
}

std::string CommerceHeuristicsData::GetProductIDExtractionJSON() {
  return product_id_json_;
}

std::string CommerceHeuristicsData::GetCartProductExtractionScript() {
  return cart_extraction_script_;
}

std::optional<base::TimeDelta> CommerceHeuristicsData::GetDiscountFetchDelay() {
  auto delay_value_optional =
      GetCommerceGlobalHeuristics(kDiscountFetchDelayType);
  if (!delay_value_optional.has_value()) {
    return std::nullopt;
  }
  return base::TimeDeltaFromString(*delay_value_optional);
}

std::optional<std::string> CommerceHeuristicsData::GetCommerceHintHeuristics(
    const std::string& type,
    const std::string& domain) {
  if (!hint_heuristics_.contains(domain)) {
    return std::nullopt;
  }
  const base::Value::Dict* domain_heuristics =
      hint_heuristics_.FindDict(domain);
  if (!domain_heuristics || domain_heuristics->empty() ||
      !domain_heuristics->contains(type)) {
    return std::nullopt;
  }
  return std::optional<std::string>(*domain_heuristics->FindString(type));
}

std::optional<std::string> CommerceHeuristicsData::GetCommerceGlobalHeuristics(
    const std::string& type) {
  if (!global_heuristics_.contains(type)) {
    return std::nullopt;
  }
  return std::optional<std::string>(*global_heuristics_.FindString(type));
}

const re2::RE2* CommerceHeuristicsData::GetCommerceHintHeuristicsRegex(
    std::map<std::string, std::unique_ptr<re2::RE2>>& map,
    const std::string type,
    const std::string domain) {
  if (map.find(domain) != map.end())
    return map.at(domain).get();
  std::optional<std::string> pattern = GetCommerceHintHeuristics(type, domain);
  if (!pattern.has_value())
    return nullptr;
  re2::RE2::Options options;
  options.set_case_sensitive(false);
  map.emplace(domain, std::make_unique<re2::RE2>(*pattern, options));
  return map.at(domain).get();
}

std::unique_ptr<re2::RE2> CommerceHeuristicsData::ConstructGlobalRegex(
    const std::string& type) {
  if (!GetCommerceGlobalHeuristics(type).has_value()) {
    return nullptr;
  }
  std::string pattern = *GetCommerceGlobalHeuristics(type);
  re2::RE2::Options options;
  options.set_case_sensitive(false);
  return std::make_unique<re2::RE2>(std::move(pattern), options);
}

}  // namespace commerce_heuristics