blob: 4cb975a445d5c45c517692216b46a345cc610bb8 [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"
Alex Gougha4160b852020-06-23 00:21:2920#include "chrome/browser/win/icon_reader_service.h"
21#include "chrome/services/util_win/public/mojom/util_read_icon.mojom.h"
22#include "content/public/browser/browser_task_traits.h"
23#include "content/public/browser/browser_thread.h"
robliao18e220e82016-04-19 16:47:1224#include "ui/display/win/dpi.h"
tfarinaebe974f02015-01-03 04:25:3225#include "ui/gfx/geometry/size.h"
[email protected]08397d52011-02-05 01:53:3826#include "ui/gfx/icon_util.h"
[email protected]2b8ac342012-08-29 03:46:2727#include "ui/gfx/image/image_skia.h"
[email protected]46728b1f2009-05-07 20:42:2428
Alex Gougha4160b852020-06-23 00:21:2929namespace {
30// Helper class to manage lifetime of icon reader service.
31class IconLoaderHelper {
32 public:
33 static void ExecuteLoadIcon(
34 base::FilePath filename,
James Forshaw7148bc32024-06-04 17:41:1835 base::File file,
Alex Gougha4160b852020-06-23 00:21:2936 chrome::mojom::IconSize size,
Alexander Zhirovf51bebd2021-04-13 09:52:0437 float scale,
Alex Gough57ff3c52020-06-23 01:42:4338 gfx::Image default_icon,
Alex Gougha4160b852020-06-23 00:21:2939 scoped_refptr<base::SingleThreadTaskRunner> target_task_runner,
40 IconLoader::IconLoadedCallback icon_loaded_callback);
41
Alex Gough57ff3c52020-06-23 01:42:4342 IconLoaderHelper(base::FilePath filename,
43 chrome::mojom::IconSize size,
Alexander Zhirovf51bebd2021-04-13 09:52:0444 float scale,
Alex Gough57ff3c52020-06-23 01:42:4345 gfx::Image default_icon);
Alex Gougha4160b852020-06-23 00:21:2946
Peter Boströmfadb1752021-09-30 19:17:0147 IconLoaderHelper(const IconLoaderHelper&) = delete;
48 IconLoaderHelper& operator=(const IconLoaderHelper&) = delete;
49
Alex Gougha4160b852020-06-23 00:21:2950 private:
James Forshaw7148bc32024-06-04 17:41:1851 void StartReadIconRequest(base::File file);
Alex Gougha4160b852020-06-23 00:21:2952 void OnConnectionError();
James Forshaw7148bc32024-06-04 17:41:1853 void OnReadIconExecuted(const gfx::ImageSkia& icon);
Alex Gougha4160b852020-06-23 00:21:2954
55 using IconLoaderHelperCallback =
56 base::OnceCallback<void(gfx::Image image,
57 const IconLoader::IconGroup& icon_group)>;
58
59 void set_finally(IconLoaderHelperCallback finally) {
60 finally_ = std::move(finally);
61 }
62
63 mojo::Remote<chrome::mojom::UtilReadIcon> remote_read_icon_;
64 base::FilePath filename_;
65 chrome::mojom::IconSize size_;
Alexander Zhirovf51bebd2021-04-13 09:52:0466 const float scale_;
Alex Gougha4160b852020-06-23 00:21:2967 // This callback owns the object until work is done.
68 IconLoaderHelperCallback finally_;
Alex Gough57ff3c52020-06-23 01:42:4369 gfx::Image default_icon_;
Alex Gougha4160b852020-06-23 00:21:2970
71 SEQUENCE_CHECKER(sequence_checker_);
Alex Gougha4160b852020-06-23 00:21:2972};
73
74void IconLoaderHelper::ExecuteLoadIcon(
75 base::FilePath filename,
James Forshaw7148bc32024-06-04 17:41:1876 base::File file,
Alex Gougha4160b852020-06-23 00:21:2977 chrome::mojom::IconSize size,
Alexander Zhirovf51bebd2021-04-13 09:52:0478 float scale,
Alex Gough57ff3c52020-06-23 01:42:4379 gfx::Image default_icon,
Alex Gougha4160b852020-06-23 00:21:2980 scoped_refptr<base::SingleThreadTaskRunner> target_task_runner,
81 IconLoader::IconLoadedCallback icon_loaded_callback) {
82 // Self-deleting helper manages service lifetime.
Alexander Zhirovf51bebd2021-04-13 09:52:0483 auto helper = std::make_unique<IconLoaderHelper>(filename, size, scale,
Alex Gough57ff3c52020-06-23 01:42:4384 std::move(default_icon));
Alex Gougha4160b852020-06-23 00:21:2985 auto* helper_raw = helper.get();
86 // This callback owns the helper and extinguishes itself once work is done.
87 auto finally_callback = base::BindOnce(
88 [](std::unique_ptr<IconLoaderHelper> helper,
89 IconLoader::IconLoadedCallback icon_loaded_callback,
90 scoped_refptr<base::SingleThreadTaskRunner> target_task_runner,
91 gfx::Image image, const IconLoader::IconGroup& icon_group) {
92 target_task_runner->PostTask(
93 FROM_HERE, base::BindOnce(std::move(icon_loaded_callback),
94 std::move(image), icon_group));
95 },
96 std::move(helper), std::move(icon_loaded_callback), target_task_runner);
97
98 helper_raw->set_finally(std::move(finally_callback));
James Forshaw7148bc32024-06-04 17:41:1899 helper_raw->StartReadIconRequest(std::move(file));
Alex Gougha4160b852020-06-23 00:21:29100}
101
102IconLoaderHelper::IconLoaderHelper(base::FilePath filename,
Alex Gough57ff3c52020-06-23 01:42:43103 chrome::mojom::IconSize size,
Alexander Zhirovf51bebd2021-04-13 09:52:04104 float scale,
Alex Gough57ff3c52020-06-23 01:42:43105 gfx::Image default_icon)
Alexander Zhirovf51bebd2021-04-13 09:52:04106 : filename_(filename),
107 size_(size),
108 scale_(scale),
109 default_icon_(std::move(default_icon)) {
Alex Gougha4160b852020-06-23 00:21:29110 remote_read_icon_ = LaunchIconReaderInstance();
111 remote_read_icon_.set_disconnect_handler(base::BindOnce(
112 &IconLoaderHelper::OnConnectionError, base::Unretained(this)));
113}
114
James Forshaw7148bc32024-06-04 17:41:18115void IconLoaderHelper::StartReadIconRequest(base::File file) {
Alex Gougha4160b852020-06-23 00:21:29116 remote_read_icon_->ReadIcon(
James Forshaw7148bc32024-06-04 17:41:18117 std::move(file), size_, scale_,
Alex Gougha4160b852020-06-23 00:21:29118 base::BindOnce(&IconLoaderHelper::OnReadIconExecuted,
119 base::Unretained(this)));
120}
121
122void IconLoaderHelper::OnConnectionError() {
123 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
124 if (finally_.is_null())
125 return;
126
Alex Gough57ff3c52020-06-23 01:42:43127 std::move(finally_).Run(std::move(default_icon_), filename_.value());
Alex Gougha4160b852020-06-23 00:21:29128}
129
James Forshaw7148bc32024-06-04 17:41:18130void IconLoaderHelper::OnReadIconExecuted(const gfx::ImageSkia& icon) {
Alex Gougha4160b852020-06-23 00:21:29131 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
132
James Forshaw7148bc32024-06-04 17:41:18133 std::wstring icon_group = filename_.value();
Alex Gough57ff3c52020-06-23 01:42:43134 if (icon.isNull()) {
Peter Kasting5105d612021-02-09 22:41:42135 std::move(finally_).Run(std::move(default_icon_), icon_group);
Alex Gough57ff3c52020-06-23 01:42:43136 } else {
137 gfx::Image image(icon);
Peter Kasting5105d612021-02-09 22:41:42138 std::move(finally_).Run(std::move(image), icon_group);
Alex Gough57ff3c52020-06-23 01:42:43139 }
140}
141
142// Must be called in a COM context. |group| should be a file extension.
Peter Kasting5105d612021-02-09 22:41:42143gfx::Image GetIconForFileExtension(const std::wstring& group,
Alex Gough57ff3c52020-06-23 01:42:43144 IconLoader::IconSize icon_size) {
145 int size = 0;
146 switch (icon_size) {
147 case IconLoader::SMALL:
148 size = SHGFI_SMALLICON;
149 break;
150 case IconLoader::NORMAL:
151 size = 0;
152 break;
153 case IconLoader::LARGE:
154 size = SHGFI_LARGEICON;
155 break;
156 default:
Peter Boström3e79afa2024-05-15 00:49:26157 NOTREACHED_IN_MIGRATION();
Alex Gough57ff3c52020-06-23 01:42:43158 }
159
160 gfx::Image image;
161
Gabriel Charettebf3832022021-09-17 22:37:08162 // Not only is GetFileInfo a blocking call, it's also known to hang
163 // (crbug.com/1249943), add a ScopedBlockingCall to let the scheduler know
164 // when this hangs and to explicitly label this call in tracing.
165 base::ScopedBlockingCall blocking_call(FROM_HERE,
166 base::BlockingType::MAY_BLOCK);
167
Alex Gough57ff3c52020-06-23 01:42:43168 SHFILEINFO file_info = {0};
169 if (SHGetFileInfo(group.c_str(), FILE_ATTRIBUTE_NORMAL, &file_info,
170 sizeof(file_info),
171 SHGFI_ICON | size | SHGFI_USEFILEATTRIBUTES)) {
172 const SkBitmap bitmap = IconUtil::CreateSkBitmapFromHICON(file_info.hIcon);
173 if (!bitmap.isNull()) {
174 gfx::ImageSkia image_skia(
175 gfx::ImageSkiaRep(bitmap, display::win::GetDPIScale()));
176 image_skia.MakeThreadSafe();
177 image = gfx::Image(image_skia);
178 }
179 DestroyIcon(file_info.hIcon);
180 }
181 return image;
Alex Gougha4160b852020-06-23 00:21:29182}
183
184} // namespace
185
[email protected]bc0147b2013-04-03 20:50:59186// static
avi381f719f2016-12-16 00:05:02187IconLoader::IconGroup IconLoader::GroupForFilepath(
188 const base::FilePath& file_path) {
189 if (file_path.MatchesExtension(L".exe") ||
190 file_path.MatchesExtension(L".dll") ||
191 file_path.MatchesExtension(L".ico")) {
192 return file_path.value();
193 }
[email protected]681b4b82013-04-09 23:34:21194
avi381f719f2016-12-16 00:05:02195 return file_path.Extension();
[email protected]bc0147b2013-04-03 20:50:59196}
197
[email protected]b3b6a372014-03-12 01:48:04198// static
avif4d431c2017-06-22 23:30:53199scoped_refptr<base::TaskRunner> IconLoader::GetReadIconTaskRunner() {
200 // Technically speaking, only a thread with COM is needed, not one that has
201 // a COM STA. However, this is what is available for now.
Gabriel Charette53381852020-03-02 15:27:20202 return base::ThreadPool::CreateCOMSTATaskRunner(traits());
[email protected]b3b6a372014-03-12 01:48:04203}
204
Alex Gougha4160b852020-06-23 00:21:29205void IconLoader::ReadGroup() {
206 group_ = GroupForFilepath(file_path_);
207
208 if (group_ == file_path_.value()) {
Alex Gough57ff3c52020-06-23 01:42:43209 // Calls a Windows API that parses the file so must be sandboxed.
210 GetReadIconTaskRunner()->PostTask(
Alex Gougha4160b852020-06-23 00:21:29211 FROM_HERE,
212 base::BindOnce(&IconLoader::ReadIconInSandbox, base::Unretained(this)));
213 } else {
214 // Looks up generic icons for groups based only on the file's extension.
215 GetReadIconTaskRunner()->PostTask(
216 FROM_HERE,
217 base::BindOnce(&IconLoader::ReadIcon, base::Unretained(this)));
218 }
219}
220
[email protected]0f38ceae2009-05-08 19:01:02221void IconLoader::ReadIcon() {
Alex Gough57ff3c52020-06-23 01:42:43222 auto image = GetIconForFileExtension(group_, icon_size_);
[email protected]ab6d23f2012-09-07 18:43:12223
avif0a7b5b812016-12-17 19:01:31224 target_task_runner_->PostTask(
Avi Drissmanefe4dc82018-02-23 17:55:39225 FROM_HERE,
226 base::BindOnce(std::move(callback_), std::move(image), group_));
Alex Gough57ff3c52020-06-23 01:42:43227
avif0a7b5b812016-12-17 19:01:31228 delete this;
[email protected]46728b1f2009-05-07 20:42:24229}
Alex Gougha4160b852020-06-23 00:21:29230
231void IconLoader::ReadIconInSandbox() {
Alex Gough57ff3c52020-06-23 01:42:43232 // Get default first as loader is deleted before ExecuteLoadIcon
233 // completes.
234 auto path = base::FilePath(group_);
235 auto default_icon = GetIconForFileExtension(path.Extension(), icon_size_);
236
Alex Gougha4160b852020-06-23 00:21:29237 chrome::mojom::IconSize size = chrome::mojom::IconSize::kNormal;
238 switch (icon_size_) {
239 case IconLoader::SMALL:
240 size = chrome::mojom::IconSize::kSmall;
241 break;
242 case IconLoader::NORMAL:
243 size = chrome::mojom::IconSize::kNormal;
244 break;
245 case IconLoader::LARGE:
246 size = chrome::mojom::IconSize::kLarge;
247 break;
248 default:
Peter Boström3e79afa2024-05-15 00:49:26249 NOTREACHED_IN_MIGRATION();
Alex Gougha4160b852020-06-23 00:21:29250 }
251
James Forshaw7148bc32024-06-04 17:41:18252 base::File file;
253 file.Initialize(path, base::File::FLAG_READ |
254 base::File::FLAG_WIN_SHARE_DELETE |
255 base::File::FLAG_OPEN);
256 if (file.IsValid()) {
257 target_task_runner_->PostTask(
258 FROM_HERE,
259 base::BindOnce(&IconLoaderHelper::ExecuteLoadIcon, std::move(path),
260 std::move(file), size, scale_, std::move(default_icon),
261 target_task_runner_, std::move(callback_)));
262 } else {
263 target_task_runner_->PostTask(
264 FROM_HERE, base::BindOnce(std::move(callback_), std::move(default_icon),
265 path.value()));
266 }
Alex Gougha4160b852020-06-23 00:21:29267 delete this;
268}