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.

#ifndef COMPONENTS_USER_EDUCATION_WEBUI_WHATS_NEW_REGISTRY_H_
#define COMPONENTS_USER_EDUCATION_WEBUI_WHATS_NEW_REGISTRY_H_

#include <vector>

#include "base/feature_list.h"
#include "components/user_education/webui/whats_new_storage_service.h"
#include "ui/webui/resources/js/browser_command/browser_command.mojom.h"

namespace whats_new {

using BrowserCommand = browser_command::mojom::Command;

// Features in the What's New system may provide additional
// customization to the rendered page by assigning a string to a Feature
// using this parameter.
//
// This should be used sparingly. Typically, this is only used when the
// server-side team is performing an experiment.
inline constexpr char kCustomizationParam[] = "whats_new_customization";

// Features for Editions in the What's New system may provide a survey
// ID to override the default survey.
//
// This should be used sparingly. Typically, this is only used when the
// server-side team is performing an experiment. Be aware that if
// multiple survey parameters are defined in this manner, the first
// survey found for an active edition will be used. This situation should
// be avoided.
inline constexpr char kSurveyParam[] = "whats_new_survey_id";

// What's New modules represent sections of content on the What's New
// page. These are meant to contain the Feature they describe, the ownership
// of the module, and the browser command that it triggers, if any.
//
// To connect the rollout of your Feature to your WhatsNewModule,
// supple a base::Feature when creating a module. This will tell the
// embedded page that the content may be shown when the user has enabled
// this Feature.
//
// Content on the What's New page that is released to 100% Stable before
// a milestone launches does not need to register a WhatsNewModule. The
// content will always be shown. Regardless, do remember to create metrics
// variants for these modules using the module name you agreed upon
// with frizzle-team@google.com. However, if this module triggers
// a browser command, it still needs to be created and registered,
// albeit without a base::Feature.
//
// Metrics:
// When registering a module, make sure to add UserAction and Histogram
// variants. Creating these are enforced by the registrar unit tests.
//
// * For a WhatsNewModule with a Feature, the metric name should be the
//      same as the name of the base::Feature.
// * For a WhatsNewModule without a Feature, the metric name should follow
//      the same pattern, but can be any string that uniquely identifies
//      this content.
class WhatsNewModule {
 public:
  ~WhatsNewModule() = default;

  // Creates a WhatsNewModule tied to the rollout of a base::Feature.
  //
  // This module must include a base::Feature and an owner string, but
  // can optionally pass a browser command.
  WhatsNewModule(const base::Feature& feature,
                 std::string owner,
                 std::optional<BrowserCommand> browser_command = std::nullopt)
      : feature_(&feature),
        unique_name_(feature.name),
        browser_command_(browser_command) {}

  // Creates a default-enabled WhatsNewModule in order to enable
  // a browser command on the embedded page.
  //
  // Default-enabled modules do not reference a base::Feature, but must
  // include a metric string, an owner string and a browser command.
  WhatsNewModule(std::string metric_name,
                 std::string owner,
                 std::optional<BrowserCommand> browser_command = std::nullopt)
      : feature_(nullptr),
        unique_name_(metric_name),
        browser_command_(browser_command) {}

  std::optional<BrowserCommand> browser_command() const {
    return browser_command_;
  }

  // Return true if the module has a feature, i.e. is not default-enabled.
  bool HasFeature() const;

  // Return true if the feature is enabled, but not by default.
  // This indicates a feature is in the process of rolling out.
  bool HasActiveFeature() const;

  // Return true if the feature has been enabled by default.
  // This indicates the feature has recently rolled out to all users.
  bool HasRolledFeature() const;

  // Returns unique name for the module. This is used for the key within
  // the registry as well as for metrics.
  const std::string& unique_name() const { return unique_name_; }

  // Return true if the feature is enabled.
  bool IsFeatureEnabled() const;

  // Get the name of the feature for this module.
  const char* GetFeatureName() const;

  // Get the customization of the feature for this module, if any.
  const std::string GetCustomization() const;

 private:
  raw_ptr<const base::Feature> feature_ = nullptr;
  std::string unique_name_;
  std::optional<BrowserCommand> browser_command_;
};

// What's New editions represent an entire What's New page with content
// relevant to a single feature or set of features. Editions are always
// tied to a base::Feature, but this Feature may be enabled by default.
//
// As with modules, remember to add user action and histogram variants
// with the same name as the base::Feature for your edition.
class WhatsNewEdition {
 public:
  WhatsNewEdition(const base::Feature& feature,
                  std::string owner,
                  std::vector<BrowserCommand> browser_commands = {});
  WhatsNewEdition(WhatsNewEdition&&);
  WhatsNewEdition& operator=(WhatsNewEdition&&);
  ~WhatsNewEdition();

  std::vector<BrowserCommand> browser_commands() const {
    return browser_commands_;
  }

  // Return true if the feature is enabled.
  bool IsFeatureEnabled() const;

  // Get the name of the feature for this module.
  const char* GetFeatureName() const;

  // Returns unique name for the module. This is used for the key within
  // the registry as well as for metrics.
  const std::string& unique_name() const { return unique_name_; }

  // Get the customization of the feature for this edition, if any.
  const std::string GetCustomization() const;

  // Get the survey override of the feature for this edition, if any.
  const std::optional<std::string> GetSurvey() const;

 private:
  raw_ref<const base::Feature> feature_;
  std::string unique_name_;
  std::vector<BrowserCommand> browser_commands_;
};

// Stores module and edition data used to display the What's New page,
// customized to the feature's a user has enabled.
class WhatsNewRegistry {
 public:
  explicit WhatsNewRegistry(
      std::unique_ptr<WhatsNewStorageService> storage_service);
  ~WhatsNewRegistry();

  // Register a module to be shown on the What's New Page.
  void RegisterModule(WhatsNewModule module);

  // Register an edition of the What's New Page.
  void RegisterEdition(WhatsNewEdition edition);

  // Used to pass active browser commands to WhatsNewUI.
  const std::vector<BrowserCommand> GetActiveCommands() const;

  // Used to send enabled flags to server-side router.
  const std::vector<std::string_view> GetActiveFeatureNames() const;

  // Used to send enabled-by-default flags to server-side router.
  const std::vector<std::string_view> GetRolledFeatureNames() const;

  // Used to send customization parameters to server-side router.
  const std::vector<std::string> GetCustomizations() const;

  // Used to override the default survey.
  const std::optional<std::string> GetActiveEditionSurvey() const;

  // Set a "used version" for an edition.
  void SetEditionUsed(std::string edition) const;

  // Record that the version page was used for this milestone.
  void SetVersionUsed() const;

  // Cleanup data from storage for housekeeping.
  void ClearUnregisteredModules() const;
  void ClearUnregisteredEditions() const;

  // Resets all stored data for manual testing.
  void ResetData() const;

  const WhatsNewStorageService* storage_service() const {
    return storage_service_.get();
  }

  const std::map<std::string, WhatsNewModule>& modules() const {
    return modules_;
  }
  const std::map<std::string, WhatsNewEdition>& editions() const {
    return editions_;
  }

  WhatsNewStorageService* GetMutableStorageServiceForTesting() {
    return storage_service_.get();
  }

 private:
  std::unique_ptr<WhatsNewStorageService> storage_service_;
  std::map<std::string, WhatsNewModule> modules_;
  std::map<std::string, WhatsNewEdition> editions_;
};

}  // namespace whats_new

#endif  // COMPONENTS_USER_EDUCATION_WEBUI_WHATS_NEW_REGISTRY_H_