chromium/chrome/browser/ash/customization/customization_document.h

// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef CHROME_BROWSER_ASH_CUSTOMIZATION_CUSTOMIZATION_DOCUMENT_H_
#define CHROME_BROWSER_ASH_CUSTOMIZATION_CUSTOMIZATION_DOCUMENT_H_

#include <stddef.h>

#include <memory>
#include <optional>
#include <string>
#include <vector>

#include "base/functional/callback_forward.h"
#include "base/gtest_prod_util.h"
#include "base/memory/singleton.h"
#include "base/memory/weak_ptr.h"
#include "base/time/time.h"
#include "base/values.h"
#include "url/gurl.h"

class PrefRegistrySimple;
class Profile;

namespace base {
class FilePath;
}  // namespace base

namespace extensions {
class ExternalLoader;
}  // namespace extensions

namespace network {
class SharedURLLoaderFactory;
class SimpleURLLoader;
}  // namespace network

namespace user_prefs {
class PrefRegistrySyncable;
}  // namespace user_prefs

namespace ash {

namespace system {
class StatisticsProvider;
}

class CustomizationWallpaperDownloader;
class ServicesCustomizationExternalLoader;

// Friend function to initialize StartupCustomizationDocument for testing.
void InitStartupCustomizationDocumentForTesting(const std::string& manifest);

// Base class for OEM customization document classes.
class CustomizationDocument {
 public:
  CustomizationDocument(const CustomizationDocument&) = delete;
  CustomizationDocument& operator=(const CustomizationDocument&) = delete;

  virtual ~CustomizationDocument();

  // Return true if the document was successfully fetched and parsed.
  bool IsReady() const { return root_.get(); }

 protected:
  explicit CustomizationDocument(const std::string& accepted_version);

  virtual bool LoadManifestFromFile(const base::FilePath& manifest_path);
  virtual bool LoadManifestFromString(const std::string& manifest);

  std::string GetLocaleSpecificString(const std::string& locale,
                                      const std::string& dictionary_name,
                                      const std::string& entry_name) const;

  std::unique_ptr<base::Value::Dict> root_;

  // Value of the "version" attribute that is supported.
  // Otherwise config is not loaded.
  std::string accepted_version_;
};

// OEM startup customization document class.
// Now StartupCustomizationDocument is loaded in c-tor so just after create it
// may be ready or not (if manifest is missing or corrupted) and this state
// won't be changed later (i.e. IsReady() always return the same value).
class StartupCustomizationDocument : public CustomizationDocument {
 public:
  static StartupCustomizationDocument* GetInstance();

  StartupCustomizationDocument(const StartupCustomizationDocument&) = delete;
  StartupCustomizationDocument& operator=(const StartupCustomizationDocument&) =
      delete;

  std::string GetEULAPage(const std::string& locale) const;

  // These methods can be called even if !IsReady(), in this case VPD values
  // will be returned.
  //
  // Raw value of "initial_locale" like initial_locale="en-US,sv,da,fi,no" .
  const std::string& initial_locale() const { return initial_locale_; }

  // Vector of individual locale values.
  const std::vector<std::string>& configured_locales() const;

  // Default locale value (first value in initial_locale list).
  const std::string& initial_locale_default() const;
  const std::string& initial_timezone() const { return initial_timezone_; }
  const std::string& keyboard_layout() const { return keyboard_layout_; }

 private:
  FRIEND_TEST_ALL_PREFIXES(StartupCustomizationDocumentTest, Basic);
  FRIEND_TEST_ALL_PREFIXES(StartupCustomizationDocumentTest, VPD);
  FRIEND_TEST_ALL_PREFIXES(StartupCustomizationDocumentTest, BadManifest);
  FRIEND_TEST_ALL_PREFIXES(ServicesCustomizationDocumentTest, MultiLanguage);
  friend class OobeLocalizationTest;
  friend void InitStartupCustomizationDocumentForTesting(
      const std::string& manifest);
  friend struct base::DefaultSingletonTraits<StartupCustomizationDocument>;

  // C-tor for singleton construction.
  StartupCustomizationDocument();

  // C-tor for test construction.
  StartupCustomizationDocument(system::StatisticsProvider* provider,
                               const std::string& manifest);

  ~StartupCustomizationDocument() override;

  void Init(system::StatisticsProvider* provider);

  std::string initial_locale_;
  std::vector<std::string> configured_locales_;
  std::string initial_timezone_;
  std::string keyboard_layout_;
};

// OEM services customization document class.
// ServicesCustomizationDocument is fetched from network therefore it is not
// ready just after creation. Fetching of the manifest should be initiated
// outside this class by calling StartFetching() or EnsureCustomizationApplied()
// methods.
// User of the file should check IsReady before use it.
class ServicesCustomizationDocument : public CustomizationDocument {
 public:
  static ServicesCustomizationDocument* GetInstance();

  ServicesCustomizationDocument(const ServicesCustomizationDocument&) = delete;
  ServicesCustomizationDocument& operator=(
      const ServicesCustomizationDocument&) = delete;

  // Registers preferences.
  static void RegisterPrefs(PrefRegistrySimple* registry);
  static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry);

  // Template URL where to fetch OEM services customization manifest from.
  static constexpr char kManifestUrl[] =
      "https://ssl.gstatic.com/chrome/chromeos-customization/%s.json";

  // Return true if the customization was applied. Customization is applied only
  // once per machine.
  static bool WasOOBECustomizationApplied();

  // If customization has not been applied, start fetching and applying.
  void EnsureCustomizationApplied();

  // Returns OnceClosure with the EnsureCustomizationApplied() method.
  base::OnceClosure EnsureCustomizationAppliedClosure();

  // Start fetching customization document.
  void StartFetching();

  // Apply customization and save in machine options that customization was
  // applied successfully. Return true if customization was applied.
  bool ApplyOOBECustomization();

  // Returns true if default wallpaper URL attribute found in manifest.
  // |out_url| is set to attribute value.
  bool GetDefaultWallpaperUrl(GURL* out_url) const;

  // Returns list of default apps.
  std::optional<base::Value::Dict> GetDefaultApps() const;

  // Creates an extensions::ExternalLoader that will provide OEM default apps.
  // Cache of OEM default apps stored in profile preferences.
  ::extensions::ExternalLoader* CreateExternalLoader(Profile* profile);

  // Returns the name of the folder for OEM apps for given |locale|.
  std::string GetOemAppsFolderName(const std::string& locale) const;

  // Initialize instance of ServicesCustomizationDocument for tests that will
  // override singleton until ShutdownForTesting is called.
  static void InitializeForTesting(
      scoped_refptr<network::SharedURLLoaderFactory> factory);

  // Remove instance of ServicesCustomizationDocument for tests.
  static void ShutdownForTesting();

  // These methods are also called by WallpaperManager to get "global default"
  // customized wallpaper path (and to init default wallpaper path from it)
  // before first wallpaper is shown.
  static base::FilePath GetCustomizedWallpaperCacheDir();
  static base::FilePath GetCustomizedWallpaperDownloadedFileName();

  CustomizationWallpaperDownloader* wallpaper_downloader_for_testing() {
    return wallpaper_downloader_.get();
  }

 private:
  friend struct base::DefaultSingletonTraits<ServicesCustomizationDocument>;
  FRIEND_TEST_ALL_PREFIXES(CustomizationWallpaperDownloaderBrowserTest,
                           OEMWallpaperIsPresent);
  FRIEND_TEST_ALL_PREFIXES(CustomizationWallpaperDownloaderBrowserTest,
                           OEMWallpaperRetryFetch);

  typedef std::vector<base::WeakPtr<ServicesCustomizationExternalLoader> >
      ExternalLoaders;

  // Guard for a single application task (wallpaper downloading, for example).
  class ApplyingTask;

  // C-tor for singleton construction.
  ServicesCustomizationDocument();

  // C-tor for test construction.
  explicit ServicesCustomizationDocument(const std::string& manifest);

  ~ServicesCustomizationDocument() override;

  // Save applied state in machine settings.
  static void SetApplied(bool val);

  // Overriden from CustomizationDocument:
  bool LoadManifestFromString(const std::string& manifest) override;

  void OnSimpleLoaderComplete(std::unique_ptr<std::string> response_body);

  // Initiate file fetching. Wait for online status.
  void StartFileFetch();

  // Initiate file fetching. Don't wait for online status.
  void DoStartFileFetch();

  // Called on UI thread with results of ReadFileInBackground.
  void OnManifestRead(const std::string& manifest);

  // Method called when manifest was successfully loaded.
  void OnManifestLoaded();

  // Returns list of default apps in ExternalProvider format.
  static base::Value::Dict GetDefaultAppsInProviderFormat(
      const base::Value::Dict& root);

  // Update cached manifest for |profile|.
  void UpdateCachedManifest(Profile* profile);

  // Customization document not found for give ID.
  void OnCustomizationNotFound();

  // Set OEM apps folder name for AppListSyncableService for |profile|.
  void SetOemFolderName(Profile* profile, const base::Value::Dict& root);

  // Returns the name of the folder for OEM apps for given |locale|.
  std::string GetOemAppsFolderNameImpl(const std::string& locale,
                                       const base::Value::Dict& root) const;

  // Start download of wallpaper image if needed.
  void StartOEMWallpaperDownload(const GURL& wallpaper_url,
                                 std::unique_ptr<ApplyingTask> applying);

  // Check that current customized wallpaper cache exists. Once wallpaper is
  // downloaded, it's never updated (even if manifest is re-fetched).
  // Start wallpaper download if needed.
  void CheckAndApplyWallpaper();

  // Intermediate function to pass the result of PathExists to ApplyWallpaper.
  void OnCheckedWallpaperCacheExists(std::unique_ptr<bool> exists,
                                     std::unique_ptr<ApplyingTask> applying);

  // Called after downloaded wallpaper has been checked.
  void ApplyWallpaper(bool default_wallpaper_file_exists,
                      std::unique_ptr<ApplyingTask> applying);

  // Set Shell default wallpaper to customized.
  // It's wrapped as a callback and passed as a parameter to
  // CustomizationWallpaperDownloader.
  void OnOEMWallpaperDownloaded(std::unique_ptr<ApplyingTask> applying,
                                bool success,
                                const GURL& wallpaper_url);

  // Register one of Customization applying tasks.
  void ApplyingTaskStarted();

  // Mark task finished and check for "all customization applied".
  void ApplyingTaskFinished(bool success);

  // Services customization manifest URL.
  GURL url_;

  // SimpleURLLoader instance.
  std::unique_ptr<network::SimpleURLLoader> url_loader_;

  // How many times we already tried to fetch customization manifest file.
  int num_retries_;

  // Manifest fetch is already in progress.
  bool load_started_;

  // Delay between checks for network online state. If the optional is empty,
  // the default value for delay is used.
  std::optional<base::TimeDelta> custom_network_delay_ = std::nullopt;

  // Known external loaders.
  ExternalLoaders external_loaders_;

  std::unique_ptr<CustomizationWallpaperDownloader> wallpaper_downloader_;

  // This is barrier until customization is applied.
  // When number of finished tasks match number of started - customization is
  // applied.
  size_t apply_tasks_started_;
  size_t apply_tasks_finished_;

  // This is the number of successfully finished customization tasks.
  // If it matches number of tasks finished - customization is applied
  // successfully.
  size_t apply_tasks_success_;

  // Weak factory for callbacks.
  base::WeakPtrFactory<ServicesCustomizationDocument> weak_ptr_factory_{this};
};

}  // namespace ash

#endif  // CHROME_BROWSER_ASH_CUSTOMIZATION_CUSTOMIZATION_DOCUMENT_H_