// 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