Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / web_applications / update_shortcut_worker_win.cc
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.
4
5 #include "chrome/browser/web_applications/update_shortcut_worker_win.h"
6
7 #include <algorithm>
8
9 #include "base/bind.h"
10 #include "base/bind_helpers.h"
11 #include "base/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"
28 #include "url/gurl.h"
29
30 using content::BrowserThread;
31 using content::NavigationController;
32 using content::WebContents;
33
34 namespace web_app {
35
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(),
44                         &unprocessed_icons_);
45   file_name_ = web_app::internals::GetSanitizedFileName(shortcut_info_.title);
46
47   registrar_.Add(
48       this,
49       chrome::NOTIFICATION_TAB_CLOSING,
50       content::Source<NavigationController>(&web_contents->GetController()));
51 }
52
53 void UpdateShortcutWorker::Run() {
54   // Starting by downloading app icon.
55   DownloadIcon();
56 }
57
58 void UpdateShortcutWorker::Observe(
59     int type,
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.
66     web_contents_ = NULL;
67   }
68 }
69
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);
74
75   if (web_contents_ == NULL) {
76     DeleteMe();  // We are done if underlying WebContents is gone.
77     return;
78   }
79
80   if (unprocessed_icons_.empty()) {
81     // No app icon. Just use the favicon from WebContents.
82     UpdateShortcuts();
83     return;
84   }
85
86   int preferred_size = std::max(unprocessed_icons_.back().width,
87                                 unprocessed_icons_.back().height);
88   web_contents_->DownloadImage(
89       unprocessed_icons_.back().url,
90       true,  // favicon
91       0,  // no maximum size
92       base::Bind(&UpdateShortcutWorker::DidDownloadFavicon,
93                  base::Unretained(this),
94                  preferred_size));
95   unprocessed_icons_.pop_back();
96 }
97
98 void UpdateShortcutWorker::DidDownloadFavicon(
99     int requested_size,
100     int id,
101     int http_status_code,
102     const GURL& image_url,
103     const std::vector<SkBitmap>& bitmaps,
104     const std::vector<gfx::Size>& original_sizes) {
105   std::vector<ui::ScaleFactor> scale_factors;
106   scale_factors.push_back(ui::SCALE_FACTOR_100P);
107
108   std::vector<size_t> closest_indices;
109   SelectFaviconFrameIndices(original_sizes,
110                             scale_factors,
111                             requested_size,
112                             &closest_indices,
113                             NULL);
114
115   SkBitmap bitmap;
116   if (!bitmaps.empty()) {
117      size_t closest_index = closest_indices[0];
118      bitmap = bitmaps[closest_index];
119   }
120
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();
131   }
132 }
133
134 void UpdateShortcutWorker::CheckExistingShortcuts() {
135   DCHECK_CURRENTLY_ON(BrowserThread::FILE);
136
137   // Locations to check to shortcut_paths.
138   struct {
139     int location_id;
140     const wchar_t* sub_dir;
141   } locations[] = {
142     {
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"
154     }
155   };
156
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;
162     }
163
164     if (locations[i].sub_dir != NULL)
165       path = path.Append(locations[i].sub_dir);
166
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);
171     }
172   }
173 }
174
175 void UpdateShortcutWorker::UpdateShortcuts() {
176   BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
177       base::Bind(&UpdateShortcutWorker::UpdateShortcutsOnFileThread,
178                  base::Unretained(this)));
179 }
180
181 void UpdateShortcutWorker::UpdateShortcutsOnFileThread() {
182   DCHECK_CURRENTLY_ON(BrowserThread::FILE);
183
184   base::FilePath web_app_path = web_app::GetWebAppDataDirectory(
185       profile_path_, shortcut_info_.extension_id, shortcut_info_.url);
186
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;
193   }
194
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
199   // Update existing shortcuts' description, icon and app id.
200   CheckExistingShortcuts();
201   if (!shortcut_files_.empty()) {
202     // Generates app id from web app url and profile path.
203     base::string16 app_id = ShellIntegration::GetAppModelIdForProfile(
204         base::UTF8ToWide(
205             web_app::GenerateApplicationNameFromURL(shortcut_info_.url)),
206         profile_path_);
207
208     // Sanitize description
209     if (shortcut_info_.description.length() >= MAX_PATH)
210       shortcut_info_.description.resize(MAX_PATH - 1);
211
212     for (size_t i = 0; i < shortcut_files_.size(); ++i) {
213       base::win::ShortcutProperties shortcut_properties;
214       shortcut_properties.set_target(shortcut_files_[i]);
215       shortcut_properties.set_description(shortcut_info_.description);
216       shortcut_properties.set_icon(icon_file, 0);
217       shortcut_properties.set_app_id(app_id);
218       base::win::CreateOrUpdateShortcutLink(
219           shortcut_files_[i], shortcut_properties,
220           base::win::SHORTCUT_UPDATE_EXISTING);
221     }
222   }
223
224   OnShortcutsUpdated(true);
225 }
226
227 void UpdateShortcutWorker::OnShortcutsUpdated(bool) {
228   DeleteMe();  // We are done.
229 }
230
231 void UpdateShortcutWorker::DeleteMe() {
232   if (BrowserThread::CurrentlyOn(BrowserThread::UI)) {
233     DeleteMeOnUIThread();
234   } else {
235     BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
236       base::Bind(&UpdateShortcutWorker::DeleteMeOnUIThread,
237                  base::Unretained(this)));
238   }
239 }
240
241 void UpdateShortcutWorker::DeleteMeOnUIThread() {
242   DCHECK_CURRENTLY_ON(BrowserThread::UI);
243   delete this;
244 }
245
246 }  // namespace web_app