chromium/chrome/browser/ash/wallpaper_handlers/wallpaper_handlers.h

// Copyright 2018 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_WALLPAPER_HANDLERS_WALLPAPER_HANDLERS_H_
#define CHROME_BROWSER_ASH_WALLPAPER_HANDLERS_WALLPAPER_HANDLERS_H_

#include <map>
#include <memory>
#include <vector>

#include "ash/webui/personalization_app/mojom/personalization_app.mojom-forward.h"
#include "base/functional/callback_forward.h"
#include "base/memory/raw_ptr.h"
#include "base/scoped_observation.h"
#include "base/values.h"
#include "chrome/browser/ash/wallpaper_handlers/wallpaper_fetcher_delegate.h"
#include "components/signin/public/identity_manager/identity_manager.h"
#include "url/gurl.h"

class GoogleServiceAuthError;
class Profile;

namespace backdrop {
class Collection;
class Image;
}  // namespace backdrop

namespace net {
struct NetworkTrafficAnnotationTag;
}  // namespace net

namespace network {
class SimpleURLLoader;
}  // namespace network

namespace signin {
class PrimaryAccountAccessTokenFetcher;
struct AccessTokenInfo;
}  // namespace signin

namespace wallpaper_handlers {

class BackdropFetcher;

// Downloads the wallpaper collections info from the Backdrop service.
class BackdropCollectionInfoFetcher {
 public:
  using OnCollectionsInfoFetched = base::OnceCallback<
      void(bool success, const std::vector<backdrop::Collection>& collections)>;

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

  virtual ~BackdropCollectionInfoFetcher();

  // Starts the fetcher.
  virtual void Start(OnCollectionsInfoFetched callback);

 protected:
  // Protected constructor forces creation via `WallpaperFetcherDelegate` to
  // allow mocking in test code.
  BackdropCollectionInfoFetcher();

 private:
  // Allow delegate to view the constructor.
  friend class WallpaperFetcherDelegateImpl;

  // Called when the collections info download completes.
  void OnResponseFetched(const std::string& response);

  // Used to download the proto from the Backdrop service.
  std::unique_ptr<BackdropFetcher> backdrop_fetcher_;

  // The callback upon completion of downloading and deserializing the
  // collections info.
  OnCollectionsInfoFetched callback_;
};

// Downloads the wallpaper images info from the Backdrop service.
class BackdropImageInfoFetcher {
 public:
  using OnImagesInfoFetched =
      base::OnceCallback<void(bool success,
                              const std::string& collection_id,
                              const std::vector<backdrop::Image>& images)>;

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

  virtual ~BackdropImageInfoFetcher();

  // Starts the fetcher.
  virtual void Start(OnImagesInfoFetched callback);

 protected:
  // Protected constructor forces creation via `WallpaperFetcherDelegate` to
  // allow mocking in test code.
  explicit BackdropImageInfoFetcher(const std::string& collection_id);

 private:
  // Allow delegate to view the constructor.
  friend class WallpaperFetcherDelegateImpl;

  // Called when the images info download completes.
  void OnResponseFetched(const std::string& response);

  // Used to download the proto from the Backdrop service.
  std::unique_ptr<BackdropFetcher> backdrop_fetcher_;

  // The id of the collection, used as the token to fetch the images info.
  const std::string collection_id_;

  // The callback upon completion of downloading and deserializing the images
  // info.
  OnImagesInfoFetched callback_;
};

// Downloads the surprise me image info from the Backdrop service.
class BackdropSurpriseMeImageFetcher {
 public:
  using OnSurpriseMeImageFetched =
      base::OnceCallback<void(bool success,
                              const backdrop::Image& image,
                              const std::string& new_resume_token)>;

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

  virtual ~BackdropSurpriseMeImageFetcher();

  // Starts the fetcher.
  virtual void Start(OnSurpriseMeImageFetched callback);

 protected:
  // Protected constructor forces creation via `WallpaperFetcherDelegate` to
  // allow mocking in test code.
  BackdropSurpriseMeImageFetcher(const std::string& collection_id,
                                 const std::string& resume_token);

 private:
  // Allow delegate to view the constructor.
  friend class WallpaperFetcherDelegateImpl;

  // Called when the surprise me image info download completes.
  void OnResponseFetched(const std::string& response);

  // Used to download the proto from the Backdrop service.
  std::unique_ptr<BackdropFetcher> backdrop_fetcher_;

  // The id of the collection, used as the token to fetch the image info.
  const std::string collection_id_;

  // An opaque token returned by a previous image info fetch request. It is used
  // to prevent duplicate images from being returned. It's intentional
  // that this field is always empty. See (https://crbug.com/843537#c13).
  const std::string resume_token_;

  // The callback upon completion of downloading and deserializing the surprise
  // me image info.
  OnSurpriseMeImageFetched callback_;
};

// Base class for common logic among fetchers that query the Google Photos API.
// Parametrized by the client callback's argument type.
template <typename T>
class GooglePhotosFetcher : public signin::IdentityManager::Observer {
 public:
  GooglePhotosFetcher(
      Profile* profile,
      const net::NetworkTrafficAnnotationTag& traffic_annotation);

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

  ~GooglePhotosFetcher() override;

 protected:
  // Issues an API request to `service_url` if and only if one is not in
  // progress. Each subclass is expected to write a public overload of this
  // function that prepares `service_url`--with appended query params from the
  // client if applicable--and delegates the rest of the work to this function.
  using ClientCallback = base::OnceCallback<void(T)>;
  void AddRequestAndStartIfNecessary(const GURL& service_url,
                                     ClientCallback callback);

  // Called when the API request finishes. `response` will be absent if there
  // was an error in sending the request, receiving the response, or parsing the
  // response; otherwise, it will hold a response in the API's specified
  // structure.
  virtual T ParseResponse(const base::Value::Dict* response) = 0;

  // Returns the count of results contained within the specified `result`.
  virtual std::optional<size_t> GetResultCount(const T& result) = 0;

  // Contains logic for different HTTP error codes that we receive, as they can
  // carry information on the state of the user's Google Photos library.
  virtual std::optional<base::Value> CreateErrorResponse(int error_code);

  // Returns the result of the managed policy
  // WallpaperGooglePhotosIntegrationEnabled, or true if this pref is
  // not managed.
  virtual bool IsGooglePhotosIntegrationPolicyEnabled() const;

 private:
  void OnTokenReceived(
      std::unique_ptr<signin::PrimaryAccountAccessTokenFetcher> fetcher,
      const GURL& service_url,
      base::TimeTicks start_time,
      GoogleServiceAuthError error,
      signin::AccessTokenInfo token_info);
  void OnJsonReceived(std::unique_ptr<network::SimpleURLLoader> loader,
                      const GURL& service_url,
                      base::TimeTicks start_time,
                      std::unique_ptr<std::string> response_body);
  void OnResponseReady(const GURL& service_url,
                       base::TimeTicks start_time,
                       std::optional<base::Value> response);

  // Profile associated with the Google Photos account that will be queried.
  const raw_ptr<Profile> profile_;

  // Supplies `token_fetcher_` with `profile_`'s GAIA account information.
  const raw_ptr<signin::IdentityManager> identity_manager_;
  base::ScopedObservation<signin::IdentityManager,
                          signin::IdentityManager::Observer>
      identity_manager_observation_{this};

  // States metadata about the network request that this fetcher sends.
  const net::NetworkTrafficAnnotationTag traffic_annotation_;

  // Callbacks for each distinct query this fetcher has been asked to make. A
  // URL's callbacks are called and then removed when the download finishes,
  // successfully or in error.
  std::map<GURL, std::vector<ClientCallback>> pending_client_callbacks_;

  base::WeakPtrFactory<GooglePhotosFetcher> weak_factory_{this};
};

using GooglePhotosAlbumsCbkArgs =
    ash::personalization_app::mojom::FetchGooglePhotosAlbumsResponsePtr;
// Downloads the Google Photos albums a user has created.
class GooglePhotosAlbumsFetcher
    : public GooglePhotosFetcher<GooglePhotosAlbumsCbkArgs> {
 public:
  GooglePhotosAlbumsFetcher(const GooglePhotosAlbumsFetcher&) = delete;
  GooglePhotosAlbumsFetcher& operator=(const GooglePhotosAlbumsFetcher&) =
      delete;

  ~GooglePhotosAlbumsFetcher() override;

  virtual void AddRequestAndStartIfNecessary(
      const std::optional<std::string>& resume_token,
      base::OnceCallback<void(GooglePhotosAlbumsCbkArgs)> callback);

 protected:
  // Protected constructor forces creation via `WallpaperFetcherDelegate` to
  // allow mocking in test code.
  explicit GooglePhotosAlbumsFetcher(Profile* profile);

  // GooglePhotosFetcher:
  GooglePhotosAlbumsCbkArgs ParseResponse(
      const base::Value::Dict* response) override;
  std::optional<size_t> GetResultCount(
      const GooglePhotosAlbumsCbkArgs& result) override;

 private:
  // Allow delegate to see the constructor.
  friend class WallpaperFetcherDelegateImpl;
  friend class GooglePhotosAlbumsFetcherTest;

  int albums_api_refresh_counter_ = 0;
};

using GooglePhotosAlbumsCbkArgs =
    ash::personalization_app::mojom::FetchGooglePhotosAlbumsResponsePtr;
// Downloads the Google Photos albums a user has created.
class GooglePhotosSharedAlbumsFetcher
    : public GooglePhotosFetcher<GooglePhotosAlbumsCbkArgs> {
 public:
  GooglePhotosSharedAlbumsFetcher(const GooglePhotosSharedAlbumsFetcher&) =
      delete;
  GooglePhotosSharedAlbumsFetcher& operator=(
      const GooglePhotosSharedAlbumsFetcher&) = delete;

  ~GooglePhotosSharedAlbumsFetcher() override;

  virtual void AddRequestAndStartIfNecessary(
      const std::optional<std::string>& resume_token,
      base::OnceCallback<void(GooglePhotosAlbumsCbkArgs)> callback);

 protected:
  // Protected constructor forces creation via `WallpaperFetcherDelegate` to
  // allow mocking in test code.
  explicit GooglePhotosSharedAlbumsFetcher(Profile* profile);

  // GooglePhotosFetcher:
  GooglePhotosAlbumsCbkArgs ParseResponse(
      const base::Value::Dict* response) override;
  std::optional<size_t> GetResultCount(
      const GooglePhotosAlbumsCbkArgs& result) override;

 private:
  friend class WallpaperFetcherDelegateImpl;

  int shared_albums_api_refresh_counter_ = 0;
};

using ash::personalization_app::mojom::GooglePhotosEnablementState;
// Downloads whether the user is allowed to access Google Photos data.
class GooglePhotosEnabledFetcher
    : public GooglePhotosFetcher<GooglePhotosEnablementState> {
 public:
  GooglePhotosEnabledFetcher(const GooglePhotosEnabledFetcher&) = delete;
  GooglePhotosEnabledFetcher& operator=(const GooglePhotosEnabledFetcher&) =
      delete;

  ~GooglePhotosEnabledFetcher() override;

  virtual void AddRequestAndStartIfNecessary(
      base::OnceCallback<void(GooglePhotosEnablementState)> callback);

 protected:
  // Protected constructor forces creation via `WallpaperFetcherDelegate` to
  // allow mocking in test code.
  explicit GooglePhotosEnabledFetcher(Profile* profile);

  // GooglePhotosFetcher:
  GooglePhotosEnablementState ParseResponse(
      const base::Value::Dict* response) override;
  std::optional<size_t> GetResultCount(
      const GooglePhotosEnablementState& result) override;

 private:
  friend class WallpaperFetcherDelegateImpl;
  friend class GooglePhotosEnabledFetcherTest;
};

using GooglePhotosPhotosCbkArgs =
    ash::personalization_app::mojom::FetchGooglePhotosPhotosResponsePtr;
// Downloads visible photos from a user's Google Photos library.
class GooglePhotosPhotosFetcher
    : public GooglePhotosFetcher<GooglePhotosPhotosCbkArgs> {
 public:
  GooglePhotosPhotosFetcher(const GooglePhotosPhotosFetcher&) = delete;
  GooglePhotosPhotosFetcher& operator=(const GooglePhotosPhotosFetcher&) =
      delete;

  ~GooglePhotosPhotosFetcher() override;

  virtual void AddRequestAndStartIfNecessary(
      const std::optional<std::string>& item_id,
      const std::optional<std::string>& album_id,
      const std::optional<std::string>& resume_token,
      bool shuffle,
      base::OnceCallback<void(GooglePhotosPhotosCbkArgs)> callback);

 protected:
  // Protected constructor forces creation via `WallpaperFetcherDelegate` to
  // allow mocking in test code.
  explicit GooglePhotosPhotosFetcher(Profile* profile);

  // GooglePhotosFetcher:
  std::optional<base::Value> CreateErrorResponse(int error_code) override;
  GooglePhotosPhotosCbkArgs ParseResponse(
      const base::Value::Dict* response) override;
  std::optional<size_t> GetResultCount(
      const GooglePhotosPhotosCbkArgs& result) override;

 private:
  friend class WallpaperFetcherDelegateImpl;
  friend class GooglePhotosPhotosFetcherTest;

  int photos_api_refresh_counter_ = 0;
};

}  // namespace wallpaper_handlers

#endif  // CHROME_BROWSER_ASH_WALLPAPER_HANDLERS_WALLPAPER_HANDLERS_H_