chromium/chrome/browser/shortcuts/shortcut_creator_win.cc

// 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 "chrome/browser/shortcuts/shortcut_creator.h"

#include <windows.h>

#include "base/base_paths.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/hash/hash.h"
#include "base/i18n/file_util_icu.h"
#include "base/memory/scoped_refptr.h"
#include "base/path_service.h"
#include "base/strings/strcat.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "base/task/sequenced_task_runner.h"
#include "base/task/task_traits.h"
#include "base/task/thread_pool.h"
#include "base/win/shortcut.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/shortcuts/platform_util_win.h"
#include "chrome/common/chrome_switches.h"
#include "ui/gfx/icon_util.h"
#include "ui/gfx/image/image_family.h"
#include "url/gurl.h"

namespace shortcuts {
namespace {

base::FilePath CreateIconFileFromBitmap(const base::FilePath& icon_path,
                                        const gfx::ImageFamily& icon_images) {
  if (!base::CreateDirectory(icon_path)) {
    return base::FilePath();
  }
  EmitIconStorageCountMetric(icon_path);

  const base::FilePath icon_file = icon_path.Append(L"shortcut.ico");

  // Write the .ico file containing this new bitmap.
  if (!IconUtil::CreateIconFileFromImageFamily(icon_images, icon_file)) {
    // This can happen if the profile directory is deleted between the
    // beginning of this function and here.
    return base::FilePath();
  }
  return icon_file;
}

}  // namespace

void CreateShortcutOnUserDesktop(ShortcutMetadata shortcut_metadata,
                                 ShortcutCreatorCallback complete) {
  CHECK(shortcut_metadata.IsValid());
  const GURL& shortcut_url = shortcut_metadata.shortcut_url;

  base::FilePath chrome_proxy_path = GetChromeProxyPath();
  // Create a .ico file from the icon images, and put it in
  // <profile dir>/shortcuts/resources/<Url Hash>/icons.
  std::string url_hash =
      base::NumberToString(base::PersistentHash(shortcut_url.spec()));
  base::FilePath target_path =
      shortcut_metadata.profile_path.Append(kWebShortcutsIconDirName)
          .AppendASCII(url_hash);
  base::FilePath icon_path =
      CreateIconFileFromBitmap(target_path, shortcut_metadata.shortcut_images);

  // Create a desktop shortcut
  base::FilePath desktop;
  if (!base::PathService::Get(base::DIR_USER_DESKTOP, &desktop) ||
      desktop.empty()) {
    std::move(complete).Run(/*created_shortcut_path=*/base::FilePath(),
                            ShortcutCreatorResult::kError);
    return;
  }
  std::wstring shortcut_name =
      base::UTF16ToWide(shortcut_metadata.shortcut_title);
  base::i18n::ReplaceIllegalCharactersInPath(&shortcut_name, ' ');
  base::FilePath shortcut_path =
      GetUniquePath(desktop.Append(base::StrCat({shortcut_name, L".lnk"})));

  base::win::ShortcutProperties target_and_args_properties;
  target_and_args_properties.set_target(chrome_proxy_path);
  target_and_args_properties.set_arguments(base::StrCat(
      {L"--", base::ASCIIToWide(switches::kProfileDirectory), L"=\"",
       shortcut_metadata.profile_path.BaseName().value(), L"\" --",
       base::ASCIIToWide(switches::kIgnoreProfileDirectoryIfNotExists), L" ",
       base::ASCIIToWide(shortcut_url.spec())}));
  target_and_args_properties.set_icon(icon_path, /*icon_index_in=*/0);

  bool res =
      CreateOrUpdateShortcutLink(shortcut_path, target_and_args_properties,
                                 base::win::ShortcutOperation::kCreateAlways);

  auto final_result_callback =
      res ? base::BindOnce(std::move(complete), shortcut_path,
                           ShortcutCreatorResult::kSuccess)
          : base::BindOnce(std::move(complete),
                           /*created_shortcut_path=*/base::FilePath(),
                           ShortcutCreatorResult::kError);
  base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
      FROM_HERE, std::move(final_result_callback));
}

scoped_refptr<base::SequencedTaskRunner> GetShortcutsTaskRunner() {
  return base::ThreadPool::CreateCOMSTATaskRunner(
      {base::MayBlock(), base::TaskPriority::USER_VISIBLE,
       base::TaskShutdownBehavior::BLOCK_SHUTDOWN});
}

}  // namespace shortcuts