910e62b5创建于 1月15日历史提交
// Copyright 2024 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/user_education/webui/whats_new_registry.h"

#include "base/containers/contains.h"
#include "base/metrics/field_trial_params.h"
#include "base/values.h"
#include "ui/webui/resources/js/browser_command/browser_command.mojom.h"

namespace whats_new {
namespace {
constexpr int kRequestEntropyLimit = 15;
}  // namespace

using BrowserCommand = browser_command::mojom::Command;

bool WhatsNewModule::HasFeature() const {
  return feature_ != nullptr;
}

bool WhatsNewModule::HasActiveFeature() const {
  if (!HasFeature()) {
    return false;
  }
  return base::FeatureList::IsEnabled(*feature_) &&
         feature_->default_state == base::FEATURE_DISABLED_BY_DEFAULT;
}

bool WhatsNewModule::HasRolledFeature() const {
  if (!HasFeature()) {
    return false;
  }
  return feature_->default_state == base::FEATURE_ENABLED_BY_DEFAULT;
}

bool WhatsNewModule::IsFeatureEnabled() const {
  CHECK(HasFeature());
  return base::FeatureList::IsEnabled(*feature_);
}

const char* WhatsNewModule::GetFeatureName() const {
  CHECK(feature_);
  return feature_->name;
}

const std::string WhatsNewModule::GetCustomization() const {
  if (!feature_) {
    return "";
  }
  std::string customization = base::GetFieldTrialParamValueByFeature(
      *feature_, whats_new::kCustomizationParam);
  return customization;
}

WhatsNewEdition::WhatsNewEdition(const base::Feature& feature,
                                 std::string owner,
                                 std::vector<BrowserCommand> browser_commands)
    : feature_(feature),
      unique_name_(feature.name),
      browser_commands_(browser_commands) {}

WhatsNewEdition::WhatsNewEdition(WhatsNewEdition&& other) = default;
WhatsNewEdition::~WhatsNewEdition() = default;

bool WhatsNewEdition::IsFeatureEnabled() const {
  return base::FeatureList::IsEnabled(*feature_);
}

const char* WhatsNewEdition::GetFeatureName() const {
  return feature_->name;
}

const std::string WhatsNewEdition::GetCustomization() const {
  std::string customization = base::GetFieldTrialParamValueByFeature(
      *feature_, whats_new::kCustomizationParam);
  return customization;
}

const std::optional<std::string> WhatsNewEdition::GetSurvey() const {
  std::string survey = base::GetFieldTrialParamValueByFeature(
      *feature_, whats_new::kSurveyParam);
  if (survey.empty()) {
    return std::nullopt;
  }
  return survey;
}

void WhatsNewRegistry::RegisterModule(WhatsNewModule module) {
  CHECK(!modules_.contains(module.unique_name()));
  if (module.HasFeature() && module.IsFeatureEnabled()) {
    storage_service_->SetModuleEnabled(module.GetFeatureName());
  }
  modules_.emplace(module.unique_name(), std::move(module));
}

void WhatsNewRegistry::RegisterEdition(WhatsNewEdition edition) {
  CHECK(!editions_.contains(edition.unique_name()));
  editions_.emplace(edition.unique_name(), std::move(edition));
}

const std::vector<BrowserCommand> WhatsNewRegistry::GetActiveCommands() const {
  std::vector<BrowserCommand> commands;
  for (const auto& [key, module] : modules_) {
    if (module.browser_command().has_value()) {
      // Modules without a feature are default-enabled.
      const bool module_is_default_enabled = !module.HasFeature();
      // If the module is tied to a feature, ensure the feature is available.
      const bool module_is_available =
          module.HasActiveFeature() || module.HasRolledFeature();
      if (module_is_default_enabled || module_is_available) {
        commands.emplace_back(module.browser_command().value());
      }
    }
  }
  for (const auto& [key, edition] : editions_) {
    // The only requirement for an edition to show is that it is enabled.
    if (edition.IsFeatureEnabled()) {
      for (const auto browser_command : edition.browser_commands()) {
        commands.emplace_back(browser_command);
      }
    }
  }
  return commands;
}

const std::vector<std::string_view> WhatsNewRegistry::GetActiveFeatureNames()
    const {
  std::vector<std::string_view> feature_names;

  // Check if the current version is used for an edition.
  const auto current_edition_name =
      storage_service_->FindEditionForCurrentVersion();
  if (current_edition_name.has_value()) {
    // An edition is tied to this milestone. Request this edition again.
    feature_names.emplace_back(*current_edition_name);
  } else if (!storage_service_->WasVersionPageUsedForCurrentMilestone()) {
    // Only request other unused editions if no other page was shown
    // during this milestone (version page or other edition page).
    for (const auto& [key, edition] : editions_) {
      if (edition.IsFeatureEnabled() &&
          feature_names.size() < kRequestEntropyLimit &&
          !storage_service_->IsUsedEdition(edition.GetFeatureName())) {
        feature_names.emplace_back(edition.GetFeatureName());
      }
    }
  }

  // Add modules based on ordered list.
  const base::Value::List& module_names_in_order =
      storage_service_->ReadModuleData();
  for (const base::Value& module_value : module_names_in_order) {
    if (feature_names.size() >= kRequestEntropyLimit) {
      break;
    }
    auto found_module = modules_.find(module_value.GetString());
    if (found_module != modules_.end() &&
        found_module->second.HasActiveFeature()) {
      feature_names.emplace_back(found_module->second.GetFeatureName());
    }
  }

  return feature_names;
}

const std::vector<std::string_view> WhatsNewRegistry::GetRolledFeatureNames()
    const {
  std::vector<std::string_view> feature_names;

  // Add modules based on ordered list.
  const base::Value::List& module_names_in_order =
      storage_service_->ReadModuleData();
  for (auto& module_value : module_names_in_order) {
    if (feature_names.size() >= kRequestEntropyLimit) {
      break;
    }
    auto found_module = modules_.find(module_value.GetString());
    if (found_module != modules_.end() &&
        found_module->second.HasRolledFeature()) {
      feature_names.emplace_back(found_module->second.GetFeatureName());
    }
  }

  return feature_names;
}

const std::vector<std::string> WhatsNewRegistry::GetCustomizations() const {
  std::vector<std::string> customizations;
  const base::Value::List& module_names_in_order =
      storage_service_->ReadModuleData();
  for (const base::Value& module_value : module_names_in_order) {
    auto found_module = modules_.find(module_value.GetString());
    if (found_module != modules_.end()) {
      auto module = found_module->second;

      // Modules without a feature are default-enabled.
      const bool module_is_default_enabled = !module.HasFeature();
      // If the module is tied to a feature, ensure the feature is available.
      const bool module_is_available =
          module.HasActiveFeature() || module.HasRolledFeature();
      if (module_is_default_enabled || module_is_available) {
        std::string customization = module.GetCustomization();
        if (!customization.empty()) {
          customizations.emplace_back(customization);
        }
      }
    }
  }
  for (const auto& [key, edition] : editions_) {
    // The only requirement for an edition to show is that it is enabled.
    if (edition.IsFeatureEnabled()) {
      const auto customization = edition.GetCustomization();
      if (!customization.empty()) {
        customizations.emplace_back(customization);
      }
    }
  }
  return customizations;
}

const std::optional<std::string> WhatsNewRegistry::GetActiveEditionSurvey()
    const {
  // Check if the current version is used for an edition.
  const auto current_edition_name =
      storage_service_->FindEditionForCurrentVersion();
  if (current_edition_name.has_value()) {
    auto found_edition = editions_.find(std::string(*current_edition_name));
    if (found_edition != editions_.end()) {
      return found_edition->second.GetSurvey();
    }
  } else {
    // Only request other unused editions if there was not one shown during
    // this version.
    for (const auto& [key, edition] : editions_) {
      if (edition.IsFeatureEnabled() &&
          !storage_service_->IsUsedEdition(edition.GetFeatureName())) {
        const auto survey = edition.GetSurvey();
        if (survey.has_value()) {
          return survey;
        }
      }
    }
  }

  return std::nullopt;
}

void WhatsNewRegistry::SetEditionUsed(std::string edition_name) const {
  // Verify edition exists.
  auto found_edition = editions_.find(edition_name);
  if (found_edition != editions_.end()) {
    storage_service_->SetEditionUsed(edition_name);
  }
}

void WhatsNewRegistry::SetVersionUsed() const {
  storage_service_->SetVersionUsed();
}

void WhatsNewRegistry::ClearUnregisteredModules() const {
  std::set<std::string_view> modules_to_clear;
  for (auto& module_value : storage_service_->ReadModuleData()) {
    auto found_module = modules_.find(module_value.GetString());
    // If the stored module cannot be found in the current registered
    // modules, clear its data.
    if (found_module == modules_.end()) {
      modules_to_clear.emplace(module_value.GetString());
    }
  }
  storage_service_->ClearModules(std::move(modules_to_clear));
}

void WhatsNewRegistry::ClearUnregisteredEditions() const {
  std::set<std::string_view> editions_to_clear;
  for (auto edition_value : storage_service_->ReadEditionData()) {
    auto found_edition = editions_.find(edition_value.first);
    // If the stored edition cannot be found in the current registered
    // editions, clear its data.
    if (found_edition == editions_.end()) {
      editions_to_clear.emplace(edition_value.first);
    }
  }
  storage_service_->ClearEditions(std::move(editions_to_clear));
}

void WhatsNewRegistry::ResetData() const {
  storage_service_->Reset();
}

WhatsNewRegistry::WhatsNewRegistry(
    std::unique_ptr<WhatsNewStorageService> storage_service)
    : storage_service_(std::move(storage_service)) {}
WhatsNewRegistry::~WhatsNewRegistry() = default;

}  // namespace whats_new