blob: 649c5cf51927abd31d4e99db6185167f7a5dd87f [file] [log] [blame]
Avi Drissman4a8573c2022-09-09 19:35:541// Copyright 2012 The Chromium Authors
[email protected]46728b1f2009-05-07 20:42:242// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
[email protected]0f38ceae2009-05-08 19:01:025#include "chrome/browser/icon_loader.h"
[email protected]46728b1f2009-05-07 20:42:246
7#include <windows.h>
Takuto Ikutac8d6b16f2024-04-15 16:59:198
[email protected]46728b1f2009-05-07 20:42:249#include <shellapi.h>
10
James Forshaw7148bc32024-06-04 17:41:1811#include "base/files/file.h"
Alex Gougha4160b852020-06-23 00:21:2912#include "base/files/file_path.h"
Avi Drissman02e49e582023-01-07 01:23:1813#include "base/functional/bind.h"
14#include "base/functional/callback.h"
Peter Kasting5105d612021-02-09 22:41:4215#include "base/strings/string_util.h"
Sean Mahere672a662023-01-09 21:42:2816#include "base/task/single_thread_task_runner.h"
Gabriel Charette53381852020-03-02 15:27:2017#include "base/task/thread_pool.h"
Gabriel Charettebf3832022021-09-17 22:37:0818#include "base/threading/scoped_blocking_call.h"
[email protected]34b99632011-01-01 01:01:0619#include "base/threading/thread.h"
Peter Kasting9d13b9702025-01-31 15:15:5120#include "base/win/scoped_gdi_object.h"
Alex Gougha4160b852020-06-23 00:21:2921#include "chrome/browser/win/icon_reader_service.h"
22#include "chrome/services/util_win/public/mojom/util_read_icon.mojom.h"
23#include "content/public/browser/browser_task_traits.h"
24#include "content/public/browser/browser_thread.h"
robliao18e220e82016-04-19 16:47:1225#include "ui/display/win/dpi.h"
tfarinaebe974f02015-01-03 04:25:3226#include "ui/gfx/geometry/size.h"
[email protected]2b8ac342012-08-29 03:46:2727#include "ui/gfx/image/image_skia.h"
Gastón Rodríguez78055b462025-10-13 17:05:2728#include "ui/gfx/win/icon_util.h"
[email protected]46728b1f2009-05-07 20:42:2429
Alex Gougha4160b852020-06-23 00:21:2930namespace {
31// Helper class to manage lifetime of icon reader service.
32class IconLoaderHelper {
33 public:
34 static void ExecuteLoadIcon(
35 base::FilePath filename,
James Forshaw7148bc32024-06-04 17:41:1836 base::File file,
Alex Gougha4160b852020-06-23 00:21:2937 chrome::mojom::IconSize size,
Alexander Zhirovf51bebd2021-04-13 09:52:0438 float scale,
Alex Gough57ff3c52020-06-23 01:42:4339 gfx::Image default_icon,
Alex Gougha4160b852020-06-23 00:21:2940 scoped_refptr<base::SingleThreadTaskRunner> target_task_runner,
41 IconLoader::IconLoadedCallback icon_loaded_callback);
42
Alex Gough57ff3c52020-06-23 01:42:4343 IconLoaderHelper(base::FilePath filename,
44 chrome::mojom::IconSize size,
Alexander Zhirovf51bebd2021-04-13 09:52:0445 float scale,
Alex Gough57ff3c52020-06-23 01:42:4346 gfx::Image default_icon);
Alex Gougha4160b852020-06-23 00:21:2947
Peter Boströmfadb1752021-09-30 19:17:0148 IconLoaderHelper(const IconLoaderHelper&) = delete;
49 IconLoaderHelper& operator=(const IconLoaderHelper&) = delete;
50
Alex Gougha4160b852020-06-23 00:21:2951 private:
James Forshaw7148bc32024-06-04 17:41:1852 void StartReadIconRequest(base::File file);
Alex Gougha4160b852020-06-23 00:21:2953 void OnConnectionError();
James Forshaw7148bc32024-06-04 17:41:1854 void OnReadIconExecuted(const gfx::ImageSkia& icon);
Alex Gougha4160b852020-06-23 00:21:2955
56 using IconLoaderHelperCallback =
57 base::OnceCallback<void(gfx::Image image,
58 const IconLoader::IconGroup& icon_group)>;
59
60 void set_finally(IconLoaderHelperCallback finally) {
61 finally_ = std::move(finally);
62 }
63
64 mojo::Remote<chrome::mojom::UtilReadIcon> remote_read_icon_;
65 base::FilePath filename_;
66 chrome::mojom::IconSize size_;
Alexander Zhirovf51bebd2021-04-13 09:52:0467 const float scale_;
Alex Gougha4160b852020-06-23 00:21:2968 // This callback owns the object until work is done.
69 IconLoaderHelperCallback finally_;
Alex Gough57ff3c52020-06-23 01:42:4370 gfx::Image default_icon_;
Alex Gougha4160b852020-06-23 00:21:2971
72 SEQUENCE_CHECKER(sequence_checker_);
Alex Gougha4160b852020-06-23 00:21:2973};
74
75void IconLoaderHelper::ExecuteLoadIcon(
76 base::FilePath filename,
James Forshaw7148bc32024-06-04 17:41:1877 base::File file,
Alex Gougha4160b852020-06-23 00:21:2978 chrome::mojom::IconSize size,
Alexander Zhirovf51bebd2021-04-13 09:52:0479 float scale,
Alex Gough57ff3c52020-06-23 01:42:4380 gfx::Image default_icon,
Alex Gougha4160b852020-06-23 00:21:2981 scoped_refptr<base::SingleThreadTaskRunner> target_task_runner,
82 IconLoader::IconLoadedCallback icon_loaded_callback) {
83 // Self-deleting helper manages service lifetime.
Alexander Zhirovf51bebd2021-04-13 09:52:0484 auto helper = std::make_unique<IconLoaderHelper>(filename, size, scale,
Alex Gough57ff3c52020-06-23 01:42:4385 std::move(default_icon));
Alex Gougha4160b852020-06-23 00:21:2986 auto* helper_raw = helper.get();
87 // This callback owns the helper and extinguishes itself once work is done.
88 auto finally_callback = base::BindOnce(
89 [](std::unique_ptr<IconLoaderHelper> helper,
90 IconLoader::IconLoadedCallback icon_loaded_callback,
91 scoped_refptr<base::SingleThreadTaskRunner> target_task_runner,
92 gfx::Image image, const IconLoader::IconGroup& icon_group) {
93 target_task_runner->PostTask(
94 FROM_HERE, base::BindOnce(std::move(icon_loaded_callback),
95 std::move(image), icon_group));
96 },
97 std::move(helper), std::move(icon_loaded_callback), target_task_runner);
98
99 helper_raw->set_finally(std::move(finally_callback));
James Forshaw7148bc32024-06-04 17:41:18100 helper_raw->StartReadIconRequest(std::move(file));
Alex Gougha4160b852020-06-23 00:21:29101}
102
103IconLoaderHelper::IconLoaderHelper(base::FilePath filename,
Alex Gough57ff3c52020-06-23 01:42:43104 chrome::mojom::IconSize size,
Alexander Zhirovf51bebd2021-04-13 09:52:04105 float scale,
Alex Gough57ff3c52020-06-23 01:42:43106 gfx::Image default_icon)
Alexander Zhirovf51bebd2021-04-13 09:52:04107 : filename_(filename),
108 size_(size),
109 scale_(scale),
110 default_icon_(std::move(default_icon)) {
Alex Gougha4160b852020-06-23 00:21:29111 remote_read_icon_ = LaunchIconReaderInstance();
112 remote_read_icon_.set_disconnect_handler(base::BindOnce(
113 &IconLoaderHelper::OnConnectionError, base::Unretained(this)));
114}
115
James Forshaw7148bc32024-06-04 17:41:18116void IconLoaderHelper::StartReadIconRequest(base::File file) {
Alex Gougha4160b852020-06-23 00:21:29117 remote_read_icon_->ReadIcon(
James Forshaw7148bc32024-06-04 17:41:18118 std::move(file), size_, scale_,
Alex Gougha4160b852020-06-23 00:21:29119 base::BindOnce(&IconLoaderHelper::OnReadIconExecuted,
120 base::Unretained(this)));
121}
122
123void IconLoaderHelper::OnConnectionError() {
124 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
125 if (finally_.is_null())
126 return;
127
Alex Gough57ff3c52020-06-23 01:42:43128 std::move(finally_).Run(std::move(default_icon_), filename_.value());
Alex Gougha4160b852020-06-23 00:21:29129}
130
James Forshaw7148bc32024-06-04 17:41:18131void IconLoaderHelper::OnReadIconExecuted(const gfx::ImageSkia& icon) {
Alex Gougha4160b852020-06-23 00:21:29132 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
133
James Forshaw7148bc32024-06-04 17:41:18134 std::wstring icon_group = filename_.value();
Alex Gough57ff3c52020-06-23 01:42:43135 if (icon.isNull()) {
Peter Kasting5105d612021-02-09 22:41:42136 std::move(finally_).Run(std::move(default_icon_), icon_group);
Alex Gough57ff3c52020-06-23 01:42:43137 } else {
138 gfx::Image image(icon);
Peter Kasting5105d612021-02-09 22:41:42139 std::move(finally_).Run(std::move(image), icon_group);
Alex Gough57ff3c52020-06-23 01:42:43140 }
141}
142
143// Must be called in a COM context. |group| should be a file extension.
Peter Kasting5105d612021-02-09 22:41:42144gfx::Image GetIconForFileExtension(const std::wstring& group,
Alex Gough57ff3c52020-06-23 01:42:43145 IconLoader::IconSize icon_size) {
146 int size = 0;
147 switch (icon_size) {
148 case IconLoader::SMALL:
149 size = SHGFI_SMALLICON;
150 break;
151 case IconLoader::NORMAL:
152 size = 0;
153 break;
154 case IconLoader::LARGE:
155 size = SHGFI_LARGEICON;
156 break;
157 default:
Peter Boström9be37efa2024-11-06 23:34:18158 NOTREACHED();
Alex Gough57ff3c52020-06-23 01:42:43159 }
160
161 gfx::Image image;
162
Gabriel Charettebf3832022021-09-17 22:37:08163 // Not only is GetFileInfo a blocking call, it's also known to hang
164 // (crbug.com/1249943), add a ScopedBlockingCall to let the scheduler know
165 // when this hangs and to explicitly label this call in tracing.
166 base::ScopedBlockingCall blocking_call(FROM_HERE,
167 base::BlockingType::MAY_BLOCK);
168
Alex Gough57ff3c52020-06-23 01:42:43169 SHFILEINFO file_info = {0};
170 if (SHGetFileInfo(group.c_str(), FILE_ATTRIBUTE_NORMAL, &file_info,
171 sizeof(file_info),
172 SHGFI_ICON | size | SHGFI_USEFILEATTRIBUTES)) {
Peter Kasting9d13b9702025-01-31 15:15:51173 base::win::ScopedGDIObject<HICON> file_icon(file_info.hIcon);
174 const SkBitmap bitmap = IconUtil::CreateSkBitmapFromHICON(file_icon.get());
Alex Gough57ff3c52020-06-23 01:42:43175 if (!bitmap.isNull()) {
176 gfx::ImageSkia image_skia(
177 gfx::ImageSkiaRep(bitmap, display::win::GetDPIScale()));
178 image_skia.MakeThreadSafe();
179 image = gfx::Image(image_skia);
180 }
Alex Gough57ff3c52020-06-23 01:42:43181 }
182 return image;
Alex Gougha4160b852020-06-23 00:21:29183}
184
185} // namespace
186
[email protected]bc0147b2013-04-03 20:50:59187// static
avi381f719f2016-12-16 00:05:02188IconLoader::IconGroup IconLoader::GroupForFilepath(
189 const base::FilePath& file_path) {
190 if (file_path.MatchesExtension(L".exe") ||
191 file_path.MatchesExtension(L".dll") ||
192 file_path.MatchesExtension(L".ico")) {
193 return file_path.value();
194 }
[email protected]681b4b82013-04-09 23:34:21195
avi381f719f2016-12-16 00:05:02196 return file_path.Extension();
[email protected]bc0147b2013-04-03 20:50:59197}
198
[email protected]b3b6a372014-03-12 01:48:04199// static
avif4d431c2017-06-22 23:30:53200scoped_refptr<base::TaskRunner> IconLoader::GetReadIconTaskRunner() {
201 // Technically speaking, only a thread with COM is needed, not one that has
202 // a COM STA. However, this is what is available for now.
Gabriel Charette53381852020-03-02 15:27:20203 return base::ThreadPool::CreateCOMSTATaskRunner(traits());
[email protected]b3b6a372014-03-12 01:48:04204}
205
Alex Gougha4160b852020-06-23 00:21:29206void IconLoader::ReadGroup() {
207 group_ = GroupForFilepath(file_path_);
208
209 if (group_ == file_path_.value()) {
Alex Gough57ff3c52020-06-23 01:42:43210 // Calls a Windows API that parses the file so must be sandboxed.
211 GetReadIconTaskRunner()->PostTask(
Alex Gougha4160b852020-06-23 00:21:29212 FROM_HERE,
213 base::BindOnce(&IconLoader::ReadIconInSandbox, base::Unretained(this)));
214 } else {
215 // Looks up generic icons for groups based only on the file's extension.
216 GetReadIconTaskRunner()->PostTask(
217 FROM_HERE,
218 base::BindOnce(&IconLoader::ReadIcon, base::Unretained(this)));
219 }
220}
221
[email protected]0f38ceae2009-05-08 19:01:02222void IconLoader::ReadIcon() {
Alex Gough57ff3c52020-06-23 01:42:43223 auto image = GetIconForFileExtension(group_, icon_size_);
[email protected]ab6d23f2012-09-07 18:43:12224
avif0a7b5b812016-12-17 19:01:31225 target_task_runner_->PostTask(
Avi Drissmanefe4dc82018-02-23 17:55:39226 FROM_HERE,
227 base::BindOnce(std::move(callback_), std::move(image), group_));
Alex Gough57ff3c52020-06-23 01:42:43228
avif0a7b5b812016-12-17 19:01:31229 delete this;
[email protected]46728b1f2009-05-07 20:42:24230}
Alex Gougha4160b852020-06-23 00:21:29231
232void IconLoader::ReadIconInSandbox() {
Alex Gough57ff3c52020-06-23 01:42:43233 // Get default first as loader is deleted before ExecuteLoadIcon
234 // completes.
235 auto path = base::FilePath(group_);
236 auto default_icon = GetIconForFileExtension(path.Extension(), icon_size_);
237
Alex Gougha4160b852020-06-23 00:21:29238 chrome::mojom::IconSize size = chrome::mojom::IconSize::kNormal;
239 switch (icon_size_) {
240 case IconLoader::SMALL:
241 size = chrome::mojom::IconSize::kSmall;
242 break;
243 case IconLoader::NORMAL:
244 size = chrome::mojom::IconSize::kNormal;
245 break;
246 case IconLoader::LARGE:
247 size = chrome::mojom::IconSize::kLarge;
248 break;
249 default:
Peter Boström9be37efa2024-11-06 23:34:18250 NOTREACHED();
Alex Gougha4160b852020-06-23 00:21:29251 }
252
James Forshaw7148bc32024-06-04 17:41:18253 base::File file;
254 file.Initialize(path, base::File::FLAG_READ |
255 base::File::FLAG_WIN_SHARE_DELETE |
256 base::File::FLAG_OPEN);
257 if (file.IsValid()) {
258 target_task_runner_->PostTask(
259 FROM_HERE,
260 base::BindOnce(&IconLoaderHelper::ExecuteLoadIcon, std::move(path),
261 std::move(file), size, scale_, std::move(default_icon),
262 target_task_runner_, std::move(callback_)));
263 } else {
264 target_task_runner_->PostTask(
265 FROM_HERE, base::BindOnce(std::move(callback_), std::move(default_icon),
266 path.value()));
267 }
Alex Gougha4160b852020-06-23 00:21:29268 delete this;
269}