[M120 Migration]Fix for crash during chrome exit
[platform/framework/web/chromium-efl.git] / chrome / browser / icon_loader_win.cc
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.
4
5 #include "chrome/browser/icon_loader.h"
6
7 #include <windows.h>
8 #include <shellapi.h>
9
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"
26
27 namespace {
28 // Helper class to manage lifetime of icon reader service.
29 class IconLoaderHelper {
30  public:
31   static void ExecuteLoadIcon(
32       base::FilePath filename,
33       chrome::mojom::IconSize size,
34       float scale,
35       gfx::Image default_icon,
36       scoped_refptr<base::SingleThreadTaskRunner> target_task_runner,
37       IconLoader::IconLoadedCallback icon_loaded_callback);
38
39   IconLoaderHelper(base::FilePath filename,
40                    chrome::mojom::IconSize size,
41                    float scale,
42                    gfx::Image default_icon);
43
44   IconLoaderHelper(const IconLoaderHelper&) = delete;
45   IconLoaderHelper& operator=(const IconLoaderHelper&) = delete;
46
47  private:
48   void StartReadIconRequest();
49   void OnConnectionError();
50   void OnReadIconExecuted(const gfx::ImageSkia& icon,
51                           const std::u16string& group);
52
53   using IconLoaderHelperCallback =
54       base::OnceCallback<void(gfx::Image image,
55                               const IconLoader::IconGroup& icon_group)>;
56
57   void set_finally(IconLoaderHelperCallback finally) {
58     finally_ = std::move(finally);
59   }
60
61   mojo::Remote<chrome::mojom::UtilReadIcon> remote_read_icon_;
62   base::FilePath filename_;
63   chrome::mojom::IconSize size_;
64   const float scale_;
65   // This callback owns the object until work is done.
66   IconLoaderHelperCallback finally_;
67   gfx::Image default_icon_;
68
69   SEQUENCE_CHECKER(sequence_checker_);
70 };
71
72 void IconLoaderHelper::ExecuteLoadIcon(
73     base::FilePath filename,
74     chrome::mojom::IconSize size,
75     float scale,
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));
92       },
93       std::move(helper), std::move(icon_loaded_callback), target_task_runner);
94
95   helper_raw->set_finally(std::move(finally_callback));
96   helper_raw->StartReadIconRequest();
97 }
98
99 IconLoaderHelper::IconLoaderHelper(base::FilePath filename,
100                                    chrome::mojom::IconSize size,
101                                    float scale,
102                                    gfx::Image default_icon)
103     : filename_(filename),
104       size_(size),
105       scale_(scale),
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)));
110 }
111
112 void IconLoaderHelper::StartReadIconRequest() {
113   remote_read_icon_->ReadIcon(
114       filename_, size_, scale_,
115       base::BindOnce(&IconLoaderHelper::OnReadIconExecuted,
116                      base::Unretained(this)));
117 }
118
119 void IconLoaderHelper::OnConnectionError() {
120   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
121   if (finally_.is_null())
122     return;
123
124   std::move(finally_).Run(std::move(default_icon_), filename_.value());
125 }
126
127 void IconLoaderHelper::OnReadIconExecuted(const gfx::ImageSkia& icon,
128                                           const std::u16string& group) {
129   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
130
131   std::wstring icon_group = base::AsWString(group);
132   if (icon.isNull()) {
133     std::move(finally_).Run(std::move(default_icon_), icon_group);
134   } else {
135     gfx::Image image(icon);
136     std::move(finally_).Run(std::move(image), icon_group);
137   }
138 }
139
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) {
143   int size = 0;
144   switch (icon_size) {
145     case IconLoader::SMALL:
146       size = SHGFI_SMALLICON;
147       break;
148     case IconLoader::NORMAL:
149       size = 0;
150       break;
151     case IconLoader::LARGE:
152       size = SHGFI_LARGEICON;
153       break;
154     default:
155       NOTREACHED();
156   }
157
158   gfx::Image image;
159
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);
165
166   SHFILEINFO file_info = {0};
167   if (SHGetFileInfo(group.c_str(), FILE_ATTRIBUTE_NORMAL, &file_info,
168                     sizeof(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);
176     }
177     DestroyIcon(file_info.hIcon);
178   }
179   return image;
180 }
181
182 }  // namespace
183
184 // static
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();
191   }
192
193   return file_path.Extension();
194 }
195
196 // static
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());
201 }
202
203 void IconLoader::ReadGroup() {
204   group_ = GroupForFilepath(file_path_);
205
206   if (group_ == file_path_.value()) {
207     // Calls a Windows API that parses the file so must be sandboxed.
208     GetReadIconTaskRunner()->PostTask(
209         FROM_HERE,
210         base::BindOnce(&IconLoader::ReadIconInSandbox, base::Unretained(this)));
211   } else {
212     // Looks up generic icons for groups based only on the file's extension.
213     GetReadIconTaskRunner()->PostTask(
214         FROM_HERE,
215         base::BindOnce(&IconLoader::ReadIcon, base::Unretained(this)));
216   }
217 }
218
219 void IconLoader::ReadIcon() {
220   auto image = GetIconForFileExtension(group_, icon_size_);
221
222   target_task_runner_->PostTask(
223       FROM_HERE,
224       base::BindOnce(std::move(callback_), std::move(image), group_));
225
226   delete this;
227 }
228
229 void IconLoader::ReadIconInSandbox() {
230   // Get default first as loader is deleted before ExecuteLoadIcon
231   // completes.
232   auto path = base::FilePath(group_);
233   auto default_icon = GetIconForFileExtension(path.Extension(), icon_size_);
234
235   chrome::mojom::IconSize size = chrome::mojom::IconSize::kNormal;
236   switch (icon_size_) {
237     case IconLoader::SMALL:
238       size = chrome::mojom::IconSize::kSmall;
239       break;
240     case IconLoader::NORMAL:
241       size = chrome::mojom::IconSize::kNormal;
242       break;
243     case IconLoader::LARGE:
244       size = chrome::mojom::IconSize::kLarge;
245       break;
246     default:
247       NOTREACHED();
248   }
249
250   target_task_runner_->PostTask(
251       FROM_HERE,
252       base::BindOnce(&IconLoaderHelper::ExecuteLoadIcon, std::move(path), size,
253                      scale_, std::move(default_icon), target_task_runner_,
254                      std::move(callback_)));
255
256   delete this;
257 }