1 // Copyright 2012 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/icon_loader.h"
10 #include "base/files/file_path.h"
11 #include "base/functional/bind.h"
12 #include "base/functional/callback.h"
13 #include "base/strings/string_util.h"
14 #include "base/task/single_thread_task_runner.h"
15 #include "base/task/thread_pool.h"
16 #include "base/threading/scoped_blocking_call.h"
17 #include "base/threading/thread.h"
18 #include "chrome/browser/win/icon_reader_service.h"
19 #include "chrome/services/util_win/public/mojom/util_read_icon.mojom.h"
20 #include "content/public/browser/browser_task_traits.h"
21 #include "content/public/browser/browser_thread.h"
22 #include "ui/display/win/dpi.h"
23 #include "ui/gfx/geometry/size.h"
24 #include "ui/gfx/icon_util.h"
25 #include "ui/gfx/image/image_skia.h"
28 // Helper class to manage lifetime of icon reader service.
29 class IconLoaderHelper {
31 static void ExecuteLoadIcon(
32 base::FilePath filename,
33 chrome::mojom::IconSize size,
35 gfx::Image default_icon,
36 scoped_refptr<base::SingleThreadTaskRunner> target_task_runner,
37 IconLoader::IconLoadedCallback icon_loaded_callback);
39 IconLoaderHelper(base::FilePath filename,
40 chrome::mojom::IconSize size,
42 gfx::Image default_icon);
44 IconLoaderHelper(const IconLoaderHelper&) = delete;
45 IconLoaderHelper& operator=(const IconLoaderHelper&) = delete;
48 void StartReadIconRequest();
49 void OnConnectionError();
50 void OnReadIconExecuted(const gfx::ImageSkia& icon,
51 const std::u16string& group);
53 using IconLoaderHelperCallback =
54 base::OnceCallback<void(gfx::Image image,
55 const IconLoader::IconGroup& icon_group)>;
57 void set_finally(IconLoaderHelperCallback finally) {
58 finally_ = std::move(finally);
61 mojo::Remote<chrome::mojom::UtilReadIcon> remote_read_icon_;
62 base::FilePath filename_;
63 chrome::mojom::IconSize size_;
65 // This callback owns the object until work is done.
66 IconLoaderHelperCallback finally_;
67 gfx::Image default_icon_;
69 SEQUENCE_CHECKER(sequence_checker_);
72 void IconLoaderHelper::ExecuteLoadIcon(
73 base::FilePath filename,
74 chrome::mojom::IconSize size,
76 gfx::Image default_icon,
77 scoped_refptr<base::SingleThreadTaskRunner> target_task_runner,
78 IconLoader::IconLoadedCallback icon_loaded_callback) {
79 // Self-deleting helper manages service lifetime.
80 auto helper = std::make_unique<IconLoaderHelper>(filename, size, scale,
81 std::move(default_icon));
82 auto* helper_raw = helper.get();
83 // This callback owns the helper and extinguishes itself once work is done.
84 auto finally_callback = base::BindOnce(
85 [](std::unique_ptr<IconLoaderHelper> helper,
86 IconLoader::IconLoadedCallback icon_loaded_callback,
87 scoped_refptr<base::SingleThreadTaskRunner> target_task_runner,
88 gfx::Image image, const IconLoader::IconGroup& icon_group) {
89 target_task_runner->PostTask(
90 FROM_HERE, base::BindOnce(std::move(icon_loaded_callback),
91 std::move(image), icon_group));
93 std::move(helper), std::move(icon_loaded_callback), target_task_runner);
95 helper_raw->set_finally(std::move(finally_callback));
96 helper_raw->StartReadIconRequest();
99 IconLoaderHelper::IconLoaderHelper(base::FilePath filename,
100 chrome::mojom::IconSize size,
102 gfx::Image default_icon)
103 : filename_(filename),
106 default_icon_(std::move(default_icon)) {
107 remote_read_icon_ = LaunchIconReaderInstance();
108 remote_read_icon_.set_disconnect_handler(base::BindOnce(
109 &IconLoaderHelper::OnConnectionError, base::Unretained(this)));
112 void IconLoaderHelper::StartReadIconRequest() {
113 remote_read_icon_->ReadIcon(
114 filename_, size_, scale_,
115 base::BindOnce(&IconLoaderHelper::OnReadIconExecuted,
116 base::Unretained(this)));
119 void IconLoaderHelper::OnConnectionError() {
120 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
121 if (finally_.is_null())
124 std::move(finally_).Run(std::move(default_icon_), filename_.value());
127 void IconLoaderHelper::OnReadIconExecuted(const gfx::ImageSkia& icon,
128 const std::u16string& group) {
129 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
131 std::wstring icon_group = base::AsWString(group);
133 std::move(finally_).Run(std::move(default_icon_), icon_group);
135 gfx::Image image(icon);
136 std::move(finally_).Run(std::move(image), icon_group);
140 // Must be called in a COM context. |group| should be a file extension.
141 gfx::Image GetIconForFileExtension(const std::wstring& group,
142 IconLoader::IconSize icon_size) {
145 case IconLoader::SMALL:
146 size = SHGFI_SMALLICON;
148 case IconLoader::NORMAL:
151 case IconLoader::LARGE:
152 size = SHGFI_LARGEICON;
160 // Not only is GetFileInfo a blocking call, it's also known to hang
161 // (crbug.com/1249943), add a ScopedBlockingCall to let the scheduler know
162 // when this hangs and to explicitly label this call in tracing.
163 base::ScopedBlockingCall blocking_call(FROM_HERE,
164 base::BlockingType::MAY_BLOCK);
166 SHFILEINFO file_info = {0};
167 if (SHGetFileInfo(group.c_str(), FILE_ATTRIBUTE_NORMAL, &file_info,
169 SHGFI_ICON | size | SHGFI_USEFILEATTRIBUTES)) {
170 const SkBitmap bitmap = IconUtil::CreateSkBitmapFromHICON(file_info.hIcon);
171 if (!bitmap.isNull()) {
172 gfx::ImageSkia image_skia(
173 gfx::ImageSkiaRep(bitmap, display::win::GetDPIScale()));
174 image_skia.MakeThreadSafe();
175 image = gfx::Image(image_skia);
177 DestroyIcon(file_info.hIcon);
185 IconLoader::IconGroup IconLoader::GroupForFilepath(
186 const base::FilePath& file_path) {
187 if (file_path.MatchesExtension(L".exe") ||
188 file_path.MatchesExtension(L".dll") ||
189 file_path.MatchesExtension(L".ico")) {
190 return file_path.value();
193 return file_path.Extension();
197 scoped_refptr<base::TaskRunner> IconLoader::GetReadIconTaskRunner() {
198 // Technically speaking, only a thread with COM is needed, not one that has
199 // a COM STA. However, this is what is available for now.
200 return base::ThreadPool::CreateCOMSTATaskRunner(traits());
203 void IconLoader::ReadGroup() {
204 group_ = GroupForFilepath(file_path_);
206 if (group_ == file_path_.value()) {
207 // Calls a Windows API that parses the file so must be sandboxed.
208 GetReadIconTaskRunner()->PostTask(
210 base::BindOnce(&IconLoader::ReadIconInSandbox, base::Unretained(this)));
212 // Looks up generic icons for groups based only on the file's extension.
213 GetReadIconTaskRunner()->PostTask(
215 base::BindOnce(&IconLoader::ReadIcon, base::Unretained(this)));
219 void IconLoader::ReadIcon() {
220 auto image = GetIconForFileExtension(group_, icon_size_);
222 target_task_runner_->PostTask(
224 base::BindOnce(std::move(callback_), std::move(image), group_));
229 void IconLoader::ReadIconInSandbox() {
230 // Get default first as loader is deleted before ExecuteLoadIcon
232 auto path = base::FilePath(group_);
233 auto default_icon = GetIconForFileExtension(path.Extension(), icon_size_);
235 chrome::mojom::IconSize size = chrome::mojom::IconSize::kNormal;
236 switch (icon_size_) {
237 case IconLoader::SMALL:
238 size = chrome::mojom::IconSize::kSmall;
240 case IconLoader::NORMAL:
241 size = chrome::mojom::IconSize::kNormal;
243 case IconLoader::LARGE:
244 size = chrome::mojom::IconSize::kLarge;
250 target_task_runner_->PostTask(
252 base::BindOnce(&IconLoaderHelper::ExecuteLoadIcon, std::move(path), size,
253 scale_, std::move(default_icon), target_task_runner_,
254 std::move(callback_)));