1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69
   70
   71
   72
   73
   74
   75
   76
   77
   78
   79
   80
   81
   82
   83
   84
   85
   86
   87
   88
   89
   90
   91
   92
   93
   94
   95
   96
   97
   98
   99
  100
  101
  102
  103
  104
  105
  106
  107
  108
  109
  110
  111
  112
  113
  114
  115
  116
  117
  118
  119
  120
  121
  122
  123
  124
  125
  126
  127
  128
  129
  130
  131
  132
  133
  134
  135
  136
  137
  138
  139
  140
  141
  142
  143
  144
  145
  146
  147
  148
  149
  150
  151
  152
  153
  154

ash / wallpaper / online_wallpaper_manager.cc [blame]

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

#include "ash/wallpaper/online_wallpaper_manager.h"

#include "ash/public/cpp/image_util.h"
#include "ash/public/cpp/wallpaper/online_wallpaper_variant.h"
#include "ash/shell.h"
#include "ash/wallpaper/wallpaper_constants.h"
#include "ash/wallpaper/wallpaper_image_downloader.h"
#include "ash/wallpaper/wallpaper_utils/wallpaper_file_utils.h"
#include "ash/wallpaper/wallpaper_utils/wallpaper_resizer.h"
#include "ash/wallpaper/wallpaper_utils/wallpaper_resolution.h"
#include "base/barrier_closure.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/functional/bind.h"
#include "components/account_id/account_id.h"
#include "ui/gfx/image/image.h"
#include "ui/gfx/image/image_util.h"

namespace ash {

OnlineWallpaperManager::OnlineWallpaperManager(
    WallpaperImageDownloader* wallpaper_image_downloader,
    WallpaperFileManager* wallpaper_file_manager)
    : wallpaper_image_downloader_(wallpaper_image_downloader),
      wallpaper_file_manager_(wallpaper_file_manager) {}

OnlineWallpaperManager::~OnlineWallpaperManager() = default;

void OnlineWallpaperManager::GetOnlineWallpaper(
    const base::FilePath& wallpaper_dir,
    const AccountId& account_id,
    const WallpaperInfo& wallpaper_info,
    LoadOnlineWallpaperCallback callback) {
  auto on_load = base::BindOnce(
      &OnlineWallpaperManager::OnLoadExistingOnlineWallpaperComplete,
      weak_factory_.GetWeakPtr(), wallpaper_dir, account_id, wallpaper_info,
      std::move(callback));
  wallpaper_file_manager_->LoadWallpaper(wallpaper_info.type, wallpaper_dir,
                                         wallpaper_info.location,
                                         std::move(on_load));
}

void OnlineWallpaperManager::OnLoadExistingOnlineWallpaperComplete(
    const base::FilePath& wallpaper_dir,
    const AccountId& account_id,
    const WallpaperInfo& wallpaper_info,
    LoadOnlineWallpaperCallback callback,
    const gfx::ImageSkia& image) {
  DCHECK(callback);
  if (image.isNull()) {
    DownloadAndSaveAllVariants(wallpaper_dir, account_id, wallpaper_info,
                               std::move(callback));
  } else {
    std::move(callback).Run(image);
  }
}

void OnlineWallpaperManager::DownloadAndSaveAllVariants(
    const base::FilePath& wallpaper_dir,
    const AccountId& account_id,
    const WallpaperInfo& wallpaper_info,
    LoadOnlineWallpaperCallback callback) {
  std::vector<OnlineWallpaperVariant> variants = wallpaper_info.variants;
  if (variants.empty()) {
    // `variants` can be empty for users who have just migrated from the old
    // wallpaper picker to the new one.
    //
    // OnlineWallpaperVariant's `asset_id` and `type` are not actually used in
    // this function, so they can have dummy values here.
    variants.emplace_back(/*asset_id=*/0, GURL(wallpaper_info.location),
                          backdrop::Image::IMAGE_TYPE_UNKNOWN);
  }

  // There's only one variant that is actually needed to fulfill the immediate
  // request. However, it's important that all of the other variants are
  // available as well (ex: the user picks a wallpaper and toggles between D/L
  // modes to see what it looks like). As such, the whole operation is
  // considered a failure unless all variants are downloaded (otherwise the
  // feature is confusing as it would advertise multiple variants but only
  // have one).
  auto downloads_result = std::make_unique<VariantsDownloadResult>();
  auto* downloads_result_ptr = downloads_result.get();
  auto on_all_variants_downloaded = base::BarrierClosure(
      variants.size(),
      base::BindOnce(&OnlineWallpaperManager::OnAllVariantsDownloaded,
                     weak_factory_.GetWeakPtr(), std::move(downloads_result),
                     std::move(callback)));
  for (const OnlineWallpaperVariant& variant : variants) {
    wallpaper_image_downloader_->DownloadBackdropImage(
        variant.raw_url, account_id,
        base::BindOnce(
            &OnlineWallpaperManager::OnVariantDownloaded,
            weak_factory_.GetWeakPtr(), wallpaper_info.type, wallpaper_dir,
            variant.raw_url, wallpaper_info.layout,
            /*is_target_variant=*/wallpaper_info.location ==
                variant.raw_url.spec(),
            // Since `downloads_result`'s lifetime matches the
            // OnAllVariantsDownloaded() callback, and OnAllVariantsDownloaded()
            // is guaranteed to be run after OnVariantDownloaded(), there's no
            // possibility of use-after-free.
            base::Unretained(downloads_result_ptr),
            on_all_variants_downloaded));
  }
}

void OnlineWallpaperManager::OnAllVariantsDownloaded(
    std::unique_ptr<VariantsDownloadResult> downloads_result,
    LoadOnlineWallpaperCallback callback) {
  DCHECK(downloads_result);
  // Due to the order of variants being downloaded, `target_variant` may have
  // been set when others fail to download. To ensure it's all or nothing, we
  // need to reset it so the operation is considered a failure.
  if (downloads_result->any_downloads_failed) {
    downloads_result->target_variant = gfx::ImageSkia();
  }
  std::move(callback).Run(std::move(downloads_result->target_variant));
}

void OnlineWallpaperManager::OnVariantDownloaded(
    WallpaperType type,
    const base::FilePath& wallpaper_dir,
    const GURL& variant_url,
    WallpaperLayout layout,
    bool is_target_variant,
    VariantsDownloadResult* downloads_result,
    base::RepeatingClosure on_done,
    const gfx::ImageSkia& image) {
  DCHECK(downloads_result);
  if (image.isNull()) {
    LOG(WARNING) << "Image download failed";
    downloads_result->any_downloads_failed = true;
    std::move(on_done).Run();
    return;
  }

  if (is_target_variant) {
    downloads_result->target_variant = image;
  }

  // Using wallpaper_file_manager_->SaveWallpaperToDisk() to post a task of
  // saving the image to disk via `blocking_task_runner_` to ensure the
  // operation order is maintained. It is important this task is executed before
  // loading the preview image to make sure the files have been saved on disk.
  wallpaper_file_manager_->SaveWallpaperToDisk(
      type, wallpaper_dir, variant_url.ExtractFileName(), layout, image);

  std::move(on_done).Run();
}

}  // namespace ash