1 // Copyright 2014 The Chromium Authors. All rights reserved.
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/web_applications/update_shortcut_worker_win.h"
10 #include "base/bind_helpers.h"
11 #include "base/files/file_util.h"
12 #include "base/path_service.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "base/win/shortcut.h"
15 #include "base/win/windows_version.h"
16 #include "chrome/browser/chrome_notification_types.h"
17 #include "chrome/browser/extensions/tab_helper.h"
18 #include "chrome/browser/favicon/favicon_tab_helper.h"
19 #include "chrome/browser/profiles/profile.h"
20 #include "chrome/browser/web_applications/web_app.h"
21 #include "chrome/browser/web_applications/web_app_win.h"
22 #include "components/favicon_base/select_favicon_frames.h"
23 #include "content/public/browser/browser_thread.h"
24 #include "content/public/browser/notification_details.h"
25 #include "content/public/browser/notification_source.h"
26 #include "content/public/browser/web_contents.h"
27 #include "ui/gfx/icon_util.h"
30 using content::BrowserThread
;
31 using content::NavigationController
;
32 using content::WebContents
;
36 UpdateShortcutWorker::UpdateShortcutWorker(WebContents
* web_contents
)
37 : web_contents_(web_contents
),
38 profile_path_(Profile::FromBrowserContext(
39 web_contents
->GetBrowserContext())->GetPath()) {
40 extensions::TabHelper
* extensions_tab_helper
=
41 extensions::TabHelper::FromWebContents(web_contents
);
42 web_app::GetShortcutInfoForTab(web_contents_
, &shortcut_info_
);
43 web_app::GetIconsInfo(extensions_tab_helper
->web_app_info(),
45 file_name_
= web_app::internals::GetSanitizedFileName(shortcut_info_
.title
);
49 chrome::NOTIFICATION_TAB_CLOSING
,
50 content::Source
<NavigationController
>(&web_contents
->GetController()));
53 void UpdateShortcutWorker::Run() {
54 // Starting by downloading app icon.
58 void UpdateShortcutWorker::Observe(
60 const content::NotificationSource
& source
,
61 const content::NotificationDetails
& details
) {
62 if (type
== chrome::NOTIFICATION_TAB_CLOSING
&&
63 content::Source
<NavigationController
>(source
).ptr() ==
64 &web_contents_
->GetController()) {
65 // Underlying tab is closing.
70 void UpdateShortcutWorker::DownloadIcon() {
71 // FetchIcon must run on UI thread because it relies on WebContents
72 // to download the icon.
73 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
75 if (web_contents_
== NULL
) {
76 DeleteMe(); // We are done if underlying WebContents is gone.
80 if (unprocessed_icons_
.empty()) {
81 // No app icon. Just use the favicon from WebContents.
86 int preferred_size
= std::max(unprocessed_icons_
.back().width
,
87 unprocessed_icons_
.back().height
);
88 web_contents_
->DownloadImage(
89 unprocessed_icons_
.back().url
,
92 false, // normal cache policy
93 base::Bind(&UpdateShortcutWorker::DidDownloadFavicon
,
94 base::Unretained(this),
96 unprocessed_icons_
.pop_back();
99 void UpdateShortcutWorker::DidDownloadFavicon(
102 int http_status_code
,
103 const GURL
& image_url
,
104 const std::vector
<SkBitmap
>& bitmaps
,
105 const std::vector
<gfx::Size
>& original_sizes
) {
106 std::vector
<int> requested_sizes_in_pixel
;
107 requested_sizes_in_pixel
.push_back(requested_size
);
109 std::vector
<size_t> closest_indices
;
110 SelectFaviconFrameIndices(
111 original_sizes
, requested_sizes_in_pixel
, &closest_indices
, NULL
);
114 if (!bitmaps
.empty()) {
115 size_t closest_index
= closest_indices
[0];
116 bitmap
= bitmaps
[closest_index
];
119 if (!bitmap
.isNull()) {
120 // Update icon with download image and update shortcut.
121 shortcut_info_
.favicon
.Add(gfx::Image::CreateFrom1xBitmap(bitmap
));
122 extensions::TabHelper
* extensions_tab_helper
=
123 extensions::TabHelper::FromWebContents(web_contents_
);
124 extensions_tab_helper
->SetAppIcon(bitmap
);
127 // Try the next icon otherwise.
132 void UpdateShortcutWorker::CheckExistingShortcuts() {
133 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
135 // Locations to check to shortcut_paths.
138 const wchar_t* sub_dir
;
141 base::DIR_USER_DESKTOP
,
144 base::DIR_START_MENU
,
147 // For Win7, create_in_quick_launch_bar means pinning to taskbar.
149 (base::win::GetVersion() >= base::win::VERSION_WIN7
) ?
150 L
"Microsoft\\Internet Explorer\\Quick Launch\\User Pinned\\TaskBar" :
151 L
"Microsoft\\Internet Explorer\\Quick Launch"
155 for (int i
= 0; i
< arraysize(locations
); ++i
) {
157 if (!PathService::Get(locations
[i
].location_id
, &path
)) {
162 if (locations
[i
].sub_dir
!= NULL
)
163 path
= path
.Append(locations
[i
].sub_dir
);
165 base::FilePath shortcut_file
= path
.Append(file_name_
).
166 ReplaceExtension(FILE_PATH_LITERAL(".lnk"));
167 if (base::PathExists(shortcut_file
)) {
168 shortcut_files_
.push_back(shortcut_file
);
173 void UpdateShortcutWorker::UpdateShortcuts() {
174 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE
,
175 base::Bind(&UpdateShortcutWorker::UpdateShortcutsOnFileThread
,
176 base::Unretained(this)));
179 void UpdateShortcutWorker::UpdateShortcutsOnFileThread() {
180 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
182 base::FilePath web_app_path
= web_app::GetWebAppDataDirectory(
183 profile_path_
, shortcut_info_
.extension_id
, shortcut_info_
.url
);
185 // Ensure web_app_path exists. web_app_path could be missing for a legacy
186 // shortcut created by Gears.
187 if (!base::PathExists(web_app_path
) &&
188 !base::CreateDirectory(web_app_path
)) {
193 base::FilePath icon_file
=
194 web_app::internals::GetIconFilePath(web_app_path
, shortcut_info_
.title
);
195 web_app::internals::CheckAndSaveIcon(icon_file
, shortcut_info_
.favicon
, true);
197 // Update existing shortcuts' description, icon and app id.
198 CheckExistingShortcuts();
199 if (!shortcut_files_
.empty()) {
200 // Generates app id from web app url and profile path.
201 base::string16 app_id
= ShellIntegration::GetAppModelIdForProfile(
203 web_app::GenerateApplicationNameFromURL(shortcut_info_
.url
)),
206 // Sanitize description
207 if (shortcut_info_
.description
.length() >= MAX_PATH
)
208 shortcut_info_
.description
.resize(MAX_PATH
- 1);
210 for (size_t i
= 0; i
< shortcut_files_
.size(); ++i
) {
211 base::win::ShortcutProperties shortcut_properties
;
212 shortcut_properties
.set_target(shortcut_files_
[i
]);
213 shortcut_properties
.set_description(shortcut_info_
.description
);
214 shortcut_properties
.set_icon(icon_file
, 0);
215 shortcut_properties
.set_app_id(app_id
);
216 base::win::CreateOrUpdateShortcutLink(
217 shortcut_files_
[i
], shortcut_properties
,
218 base::win::SHORTCUT_UPDATE_EXISTING
);
222 OnShortcutsUpdated(true);
225 void UpdateShortcutWorker::OnShortcutsUpdated(bool) {
226 DeleteMe(); // We are done.
229 void UpdateShortcutWorker::DeleteMe() {
230 if (BrowserThread::CurrentlyOn(BrowserThread::UI
)) {
231 DeleteMeOnUIThread();
233 BrowserThread::PostTask(BrowserThread::UI
, FROM_HERE
,
234 base::Bind(&UpdateShortcutWorker::DeleteMeOnUIThread
,
235 base::Unretained(this)));
239 void UpdateShortcutWorker::DeleteMeOnUIThread() {
240 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
244 } // namespace web_app