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 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182
|
// 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_linux.h"
#include <stdlib.h>
#include <optional>
#include "base/base_paths.h"
#include "base/check_op.h"
#include "base/containers/span.h"
#include "base/files/file.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/files/safe_base_name.h"
#include "base/hash/md5.h"
#include "base/location.h"
#include "base/memory/ref_counted_memory.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/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/task/sequenced_task_runner.h"
#include "base/task/thread_pool.h"
#include "chrome/browser/shell_integration_linux.h"
#include "chrome/browser/shortcuts/linux_xdg_wrapper.h"
#include "chrome/browser/shortcuts/linux_xdg_wrapper_impl.h"
#include "chrome/browser/shortcuts/shortcut_creator.h"
#include "ui/gfx/geometry/size.h"
#include "ui/gfx/image/image.h"
#include "ui/gfx/image/image_family.h"
#include "url/gurl.h"
namespace shortcuts {
namespace {
base::SafeBaseName GenerateIconFilename(const GURL& url) {
std::string url_hash = base::MD5String(url.spec());
std::optional<base::SafeBaseName> base_name =
base::SafeBaseName::Create(base::StrCat({"shortcut-", url_hash, ".png"}));
CHECK(base_name);
return base_name.value();
}
ShortcutCreatorResult CreateExecutableFile(const base::FilePath& file_path,
const std::string& contents) {
base::File file(file_path, base::File::FLAG_CREATE | base::File::FLAG_WRITE);
if (!file.IsValid()) {
return ShortcutCreatorResult::kError;
}
if (!file.Write(0, base::as_byte_span(contents)).has_value()) {
// Attempt to delete the file to clean up
file.Close();
base::DeleteFile(file_path);
return ShortcutCreatorResult::kError;
}
file.Close();
// User: RWX
// Group: R_X
// Other: R_X
bool success = base::SetPosixFilePermissions(
file_path, base::FILE_PERMISSION_USER_MASK |
base::FILE_PERMISSION_READ_BY_GROUP |
base::FILE_PERMISSION_EXECUTE_BY_GROUP |
base::FILE_PERMISSION_READ_BY_OTHERS |
base::FILE_PERMISSION_EXECUTE_BY_OTHERS);
// Failing to set permissions is acceptable.
return success ? ShortcutCreatorResult::kSuccess
: ShortcutCreatorResult::kSuccessWithErrors;
}
} // namespace
ShortcutCreatorOutput CreateShortcutOnLinuxDesktop(
const std::string& shortcut_name,
const GURL& shortcut_url,
const gfx::Image& icon,
const base::FilePath& profile_path,
LinuxXdgWrapper& xdg_wrapper) {
// First, create necessary directories and do .desktop file location
// resolution before writing any data, removing some cleanup.
base::FilePath icon_directory = profile_path.Append(kWebShortcutsIconDirName);
if (!base::CreateDirectory(icon_directory)) {
return {.result = ShortcutCreatorResult::kError};
}
EmitIconStorageCountMetric(icon_directory);
std::optional<base::SafeBaseName> desktop_file_basename =
shell_integration_linux::GetUniqueWebShortcutFilename(shortcut_name);
if (!desktop_file_basename) {
return {.result = ShortcutCreatorResult::kError};
}
base::FilePath desktop_path;
if (!base::PathService::Get(base::DIR_USER_DESKTOP, &desktop_path)) {
return {.result = ShortcutCreatorResult::kError};
}
base::FilePath shortcut_desktop_location =
desktop_path.Append(desktop_file_basename->path());
// Second, write the icon to disk.
base::SafeBaseName icon_base_name = GenerateIconFilename(shortcut_url);
base::FilePath icon_path = icon_directory.Append(icon_base_name);
scoped_refptr<base::RefCountedMemory> png_data = icon.As1xPNGBytes();
bool non_fatal_failure = false;
if (!base::WriteFile(icon_path, *png_data)) {
non_fatal_failure = true;
}
// Third, create the .desktop file.
std::string desktop_file_contents =
shell_integration_linux::GetDesktopFileContentsForUrlShortcut(
shortcut_name, shortcut_url, icon_path, profile_path);
ShortcutCreatorResult shortcut_file_creation_result =
CreateExecutableFile(shortcut_desktop_location, desktop_file_contents);
switch (shortcut_file_creation_result) {
case ShortcutCreatorResult::kError:
// Attempt to clean up the icon.
base::DeleteFile(icon_path);
return {.result = ShortcutCreatorResult::kError};
case ShortcutCreatorResult::kSuccessWithErrors:
non_fatal_failure = true;
break;
case ShortcutCreatorResult::kSuccess:
break;
}
// Fourth, install the .desktop file into the desktop menu. If this fails that
// is OK, as linux distros often have the capability of double-clicking the
// .desktop file to launch them.
int error_code = xdg_wrapper.XdgDesktopMenuInstall(shortcut_desktop_location);
if (error_code != EXIT_SUCCESS) {
non_fatal_failure = true;
}
auto success_result = non_fatal_failure
? ShortcutCreatorResult::kSuccessWithErrors
: ShortcutCreatorResult::kSuccess;
return {.shortcut_path = shortcut_desktop_location, .result = success_result};
}
namespace {
LinuxXdgWrapper* g_xdg_wrapper_override = nullptr;
}
void SetDefaultXdgWrapperForTesting(LinuxXdgWrapper* xdg_wrapper) {
CHECK_NE(xdg_wrapper != nullptr, g_xdg_wrapper_override != nullptr);
g_xdg_wrapper_override = xdg_wrapper;
}
void CreateShortcutOnUserDesktop(ShortcutMetadata shortcut_metadata,
ShortcutCreatorCallback complete) {
CHECK(shortcut_metadata.IsValid());
const gfx::Image* image =
shortcut_metadata.shortcut_images.GetBest(gfx::Size(128, 128));
CHECK(image);
CHECK_EQ(image->Size().width(), 128);
LinuxXdgWrapperImpl wrapper_impl;
ShortcutCreatorOutput result = CreateShortcutOnLinuxDesktop(
base::UTF16ToUTF8(shortcut_metadata.shortcut_title),
shortcut_metadata.shortcut_url, *image, shortcut_metadata.profile_path,
g_xdg_wrapper_override ? *g_xdg_wrapper_override : wrapper_impl);
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE,
base::BindOnce(std::move(complete), result.shortcut_path, result.result));
}
scoped_refptr<base::SequencedTaskRunner> GetShortcutsTaskRunner() {
return base::ThreadPool::CreateSequencedTaskRunner(
{base::MayBlock(), base::TaskPriority::USER_VISIBLE,
base::TaskShutdownBehavior::BLOCK_SHUTDOWN});
}
} // namespace shortcuts
|