ExtensionSyncService: listen for relevant changes instead of being explicitly called...
[chromium-blink-merge.git] / chrome / browser / web_applications / update_shortcut_worker_win.cc
blob09b0051642dc9aeeb064e809efe7eae35602f242
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"
7 #include <algorithm>
9 #include "base/bind.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/profiles/profile.h"
19 #include "chrome/browser/web_applications/web_app.h"
20 #include "chrome/browser/web_applications/web_app_win.h"
21 #include "components/favicon_base/select_favicon_frames.h"
22 #include "content/public/browser/browser_thread.h"
23 #include "content/public/browser/notification_details.h"
24 #include "content/public/browser/notification_source.h"
25 #include "content/public/browser/web_contents.h"
26 #include "ui/gfx/icon_util.h"
27 #include "url/gurl.h"
29 using content::BrowserThread;
30 using content::NavigationController;
31 using content::WebContents;
33 namespace web_app {
35 UpdateShortcutWorker::UpdateShortcutWorker(WebContents* web_contents)
36 : web_contents_(web_contents),
37 profile_path_(Profile::FromBrowserContext(
38 web_contents->GetBrowserContext())->GetPath()) {
39 extensions::TabHelper* extensions_tab_helper =
40 extensions::TabHelper::FromWebContents(web_contents);
41 shortcut_info_ = web_app::GetShortcutInfoForTab(web_contents_);
42 web_app::GetIconsInfo(extensions_tab_helper->web_app_info(),
43 &unprocessed_icons_);
44 file_name_ = web_app::internals::GetSanitizedFileName(shortcut_info_->title);
46 registrar_.Add(
47 this,
48 chrome::NOTIFICATION_TAB_CLOSING,
49 content::Source<NavigationController>(&web_contents->GetController()));
52 UpdateShortcutWorker::~UpdateShortcutWorker() {
55 void UpdateShortcutWorker::Run() {
56 // Starting by downloading app icon.
57 DownloadIcon();
60 void UpdateShortcutWorker::Observe(
61 int type,
62 const content::NotificationSource& source,
63 const content::NotificationDetails& details) {
64 if (type == chrome::NOTIFICATION_TAB_CLOSING &&
65 content::Source<NavigationController>(source).ptr() ==
66 &web_contents_->GetController()) {
67 // Underlying tab is closing.
68 web_contents_ = NULL;
72 void UpdateShortcutWorker::DownloadIcon() {
73 // FetchIcon must run on UI thread because it relies on WebContents
74 // to download the icon.
75 DCHECK_CURRENTLY_ON(BrowserThread::UI);
77 if (web_contents_ == NULL) {
78 DeleteMe(); // We are done if underlying WebContents is gone.
79 return;
82 if (unprocessed_icons_.empty()) {
83 // No app icon. Just use the favicon from WebContents.
84 UpdateShortcuts();
85 return;
88 int preferred_size = std::max(unprocessed_icons_.back().width,
89 unprocessed_icons_.back().height);
90 web_contents_->DownloadImage(
91 unprocessed_icons_.back().url,
92 true, // favicon
93 0, // no maximum size
94 false, // normal cache policy
95 base::Bind(&UpdateShortcutWorker::DidDownloadFavicon,
96 base::Unretained(this),
97 preferred_size));
98 unprocessed_icons_.pop_back();
101 void UpdateShortcutWorker::DidDownloadFavicon(
102 int requested_size,
103 int id,
104 int http_status_code,
105 const GURL& image_url,
106 const std::vector<SkBitmap>& bitmaps,
107 const std::vector<gfx::Size>& original_sizes) {
108 std::vector<int> requested_sizes_in_pixel;
109 requested_sizes_in_pixel.push_back(requested_size);
111 std::vector<size_t> closest_indices;
112 SelectFaviconFrameIndices(
113 original_sizes, requested_sizes_in_pixel, &closest_indices, NULL);
115 SkBitmap bitmap;
116 if (!bitmaps.empty()) {
117 size_t closest_index = closest_indices[0];
118 bitmap = bitmaps[closest_index];
121 if (!bitmap.isNull()) {
122 // Update icon with download image and update shortcut.
123 shortcut_info_->favicon.Add(gfx::Image::CreateFrom1xBitmap(bitmap));
124 extensions::TabHelper* extensions_tab_helper =
125 extensions::TabHelper::FromWebContents(web_contents_);
126 extensions_tab_helper->SetAppIcon(bitmap);
127 UpdateShortcuts();
128 } else {
129 // Try the next icon otherwise.
130 DownloadIcon();
134 void UpdateShortcutWorker::CheckExistingShortcuts() {
135 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
137 // Locations to check to shortcut_paths.
138 struct {
139 int location_id;
140 const wchar_t* sub_dir;
141 } locations[] = {
143 base::DIR_USER_DESKTOP,
144 NULL
145 }, {
146 base::DIR_START_MENU,
147 NULL
148 }, {
149 // For Win7, create_in_quick_launch_bar means pinning to taskbar.
150 base::DIR_APP_DATA,
151 (base::win::GetVersion() >= base::win::VERSION_WIN7) ?
152 L"Microsoft\\Internet Explorer\\Quick Launch\\User Pinned\\TaskBar" :
153 L"Microsoft\\Internet Explorer\\Quick Launch"
157 for (int i = 0; i < arraysize(locations); ++i) {
158 base::FilePath path;
159 if (!PathService::Get(locations[i].location_id, &path)) {
160 NOTREACHED();
161 continue;
164 if (locations[i].sub_dir != NULL)
165 path = path.Append(locations[i].sub_dir);
167 base::FilePath shortcut_file = path.Append(file_name_).
168 ReplaceExtension(FILE_PATH_LITERAL(".lnk"));
169 if (base::PathExists(shortcut_file)) {
170 shortcut_files_.push_back(shortcut_file);
175 void UpdateShortcutWorker::UpdateShortcuts() {
176 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
177 base::Bind(&UpdateShortcutWorker::UpdateShortcutsOnFileThread,
178 base::Unretained(this)));
181 void UpdateShortcutWorker::UpdateShortcutsOnFileThread() {
182 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
184 base::FilePath web_app_path = web_app::GetWebAppDataDirectory(
185 profile_path_, shortcut_info_->extension_id, shortcut_info_->url);
187 // Ensure web_app_path exists. web_app_path could be missing for a legacy
188 // shortcut created by Gears.
189 if (!base::PathExists(web_app_path) &&
190 !base::CreateDirectory(web_app_path)) {
191 NOTREACHED();
192 return;
195 base::FilePath icon_file =
196 web_app::internals::GetIconFilePath(web_app_path, shortcut_info_->title);
197 web_app::internals::CheckAndSaveIcon(icon_file, shortcut_info_->favicon,
198 true);
200 // Update existing shortcuts' description, icon and app id.
201 CheckExistingShortcuts();
202 if (!shortcut_files_.empty()) {
203 // Generates app id from web app url and profile path.
204 base::string16 app_id = ShellIntegration::GetAppModelIdForProfile(
205 base::UTF8ToWide(
206 web_app::GenerateApplicationNameFromURL(shortcut_info_->url)),
207 profile_path_);
209 // Sanitize description
210 if (shortcut_info_->description.length() >= MAX_PATH)
211 shortcut_info_->description.resize(MAX_PATH - 1);
213 for (size_t i = 0; i < shortcut_files_.size(); ++i) {
214 base::win::ShortcutProperties shortcut_properties;
215 shortcut_properties.set_target(shortcut_files_[i]);
216 shortcut_properties.set_description(shortcut_info_->description);
217 shortcut_properties.set_icon(icon_file, 0);
218 shortcut_properties.set_app_id(app_id);
219 base::win::CreateOrUpdateShortcutLink(
220 shortcut_files_[i], shortcut_properties,
221 base::win::SHORTCUT_UPDATE_EXISTING);
225 OnShortcutsUpdated(true);
228 void UpdateShortcutWorker::OnShortcutsUpdated(bool) {
229 DeleteMe(); // We are done.
232 void UpdateShortcutWorker::DeleteMe() {
233 if (BrowserThread::CurrentlyOn(BrowserThread::UI)) {
234 DeleteMeOnUIThread();
235 } else {
236 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
237 base::Bind(&UpdateShortcutWorker::DeleteMeOnUIThread,
238 base::Unretained(this)));
242 void UpdateShortcutWorker::DeleteMeOnUIThread() {
243 DCHECK_CURRENTLY_ON(BrowserThread::UI);
244 delete this;
247 } // namespace web_app