OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/web_applications/web_app.h" | 5 #include "chrome/browser/web_applications/web_app.h" |
6 | 6 |
7 #include <shlobj.h> | 7 #include <shlobj.h> |
8 | 8 |
9 #include "base/command_line.h" | 9 #include "base/command_line.h" |
10 #include "base/file_util.h" | 10 #include "base/file_util.h" |
11 #include "base/logging.h" | 11 #include "base/logging.h" |
12 #include "base/md5.h" | 12 #include "base/md5.h" |
13 #include "base/path_service.h" | 13 #include "base/path_service.h" |
14 #include "base/stringprintf.h" | 14 #include "base/stringprintf.h" |
15 #include "base/utf_string_conversions.h" | 15 #include "base/utf_string_conversions.h" |
16 #include "base/win/windows_version.h" | 16 #include "base/win/windows_version.h" |
17 #include "chrome/common/chrome_paths.h" | 17 #include "chrome/common/chrome_paths.h" |
| 18 #include "chrome/common/chrome_switches.h" |
18 #include "content/public/browser/browser_thread.h" | 19 #include "content/public/browser/browser_thread.h" |
19 #include "ui/gfx/icon_util.h" | 20 #include "ui/gfx/icon_util.h" |
20 | 21 |
21 namespace { | 22 namespace { |
22 | 23 |
23 const FilePath::CharType kIconChecksumFileExt[] = FILE_PATH_LITERAL(".ico.md5"); | 24 const FilePath::CharType kIconChecksumFileExt[] = FILE_PATH_LITERAL(".ico.md5"); |
24 | 25 |
25 // Calculates image checksum using MD5. | 26 // Calculates image checksum using MD5. |
26 void GetImageCheckSum(const SkBitmap& image, base::MD5Digest* digest) { | 27 void GetImageCheckSum(const SkBitmap& image, base::MD5Digest* digest) { |
27 DCHECK(digest); | 28 DCHECK(digest); |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
60 return true; | 61 return true; |
61 | 62 |
62 base::MD5Digest downloaded_image_checksum; | 63 base::MD5Digest downloaded_image_checksum; |
63 GetImageCheckSum(image, &downloaded_image_checksum); | 64 GetImageCheckSum(image, &downloaded_image_checksum); |
64 | 65 |
65 // Update icon if checksums are not equal. | 66 // Update icon if checksums are not equal. |
66 return memcmp(&persisted_image_checksum, &downloaded_image_checksum, | 67 return memcmp(&persisted_image_checksum, &downloaded_image_checksum, |
67 sizeof(base::MD5Digest)) != 0; | 68 sizeof(base::MD5Digest)) != 0; |
68 } | 69 } |
69 | 70 |
70 } // namespace | 71 std::vector<FilePath> GetShortcutPaths( |
71 | 72 ShellIntegration::ShortcutInfo shortcut_info) { |
72 namespace web_app { | |
73 | |
74 namespace internals { | |
75 | |
76 // Saves |image| to |icon_file| if the file is outdated and refresh shell's | |
77 // icon cache to ensure correct icon is displayed. Returns true if icon_file | |
78 // is up to date or successfully updated. | |
79 bool CheckAndSaveIcon(const FilePath& icon_file, const SkBitmap& image) { | |
80 if (ShouldUpdateIcon(icon_file, image)) { | |
81 if (SaveIconWithCheckSum(icon_file, image)) { | |
82 // Refresh shell's icon cache. This call is quite disruptive as user would | |
83 // see explorer rebuilding the icon cache. It would be great that we find | |
84 // a better way to achieve this. | |
85 SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST | SHCNF_FLUSHNOWAIT, | |
86 NULL, NULL); | |
87 } else { | |
88 return false; | |
89 } | |
90 } | |
91 | |
92 return true; | |
93 } | |
94 | |
95 bool CreatePlatformShortcut( | |
96 const FilePath& web_app_path, | |
97 const FilePath& profile_path, | |
98 const ShellIntegration::ShortcutInfo& shortcut_info) { | |
99 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE)); | |
100 | |
101 // Shortcut paths under which to create shortcuts. | 73 // Shortcut paths under which to create shortcuts. |
102 std::vector<FilePath> shortcut_paths; | 74 std::vector<FilePath> shortcut_paths; |
103 | 75 |
104 // Locations to add to shortcut_paths. | 76 // Locations to add to shortcut_paths. |
105 struct { | 77 struct { |
106 const bool& use_this_location; | 78 const bool& use_this_location; |
107 int location_id; | 79 int location_id; |
108 const wchar_t* sub_dir; | 80 const wchar_t* sub_dir; |
109 } locations[] = { | 81 } locations[] = { |
110 { | 82 { |
(...skipping 18 matching lines...) Expand all Loading... |
129 // Populate shortcut_paths. | 101 // Populate shortcut_paths. |
130 for (int i = 0; i < arraysize(locations); ++i) { | 102 for (int i = 0; i < arraysize(locations); ++i) { |
131 if (locations[i].use_this_location) { | 103 if (locations[i].use_this_location) { |
132 FilePath path; | 104 FilePath path; |
133 | 105 |
134 // Skip the Win7 case. | 106 // Skip the Win7 case. |
135 if (locations[i].location_id == base::PATH_START) | 107 if (locations[i].location_id == base::PATH_START) |
136 continue; | 108 continue; |
137 | 109 |
138 if (!PathService::Get(locations[i].location_id, &path)) { | 110 if (!PathService::Get(locations[i].location_id, &path)) { |
139 return false; | 111 continue; |
140 } | 112 } |
141 | 113 |
142 if (locations[i].sub_dir != NULL) | 114 if (locations[i].sub_dir != NULL) |
143 path = path.Append(locations[i].sub_dir); | 115 path = path.Append(locations[i].sub_dir); |
144 | 116 |
145 shortcut_paths.push_back(path); | 117 shortcut_paths.push_back(path); |
146 } | 118 } |
147 } | 119 } |
148 | 120 |
149 bool pin_to_taskbar = | 121 return shortcut_paths; |
150 shortcut_info.create_in_quick_launch_bar && | 122 } |
151 (base::win::GetVersion() >= base::win::VERSION_WIN7); | 123 |
| 124 bool ShortcutIsForProfile(const FilePath& shortcut_file_name, |
| 125 const FilePath& profile_path) { |
| 126 string16 cmd_line_string; |
| 127 if (file_util::ResolveShortcut(shortcut_file_name, NULL, &cmd_line_string)) { |
| 128 cmd_line_string = L"program " + cmd_line_string; |
| 129 CommandLine shortcut_cmd_line = CommandLine::FromString(cmd_line_string); |
| 130 return shortcut_cmd_line.HasSwitch(switches::kProfileDirectory) && |
| 131 shortcut_cmd_line.GetSwitchValuePath(switches::kProfileDirectory) == |
| 132 profile_path.BaseName(); |
| 133 } |
| 134 |
| 135 return false; |
| 136 } |
| 137 |
| 138 std::vector<FilePath> MatchingShortcutsForProfileAndExtension( |
| 139 const FilePath& shortcut_path, |
| 140 const FilePath& profile_path, |
| 141 const string16& shortcut_name) { |
| 142 std::vector<FilePath> shortcut_paths; |
| 143 FilePath base_path = shortcut_path. |
| 144 Append(web_app::internals::GetSanitizedFileName(shortcut_name)). |
| 145 ReplaceExtension(FILE_PATH_LITERAL(".lnk")); |
| 146 |
| 147 const int fileNamesToCheck = 10; |
| 148 for (int i = 0; i < fileNamesToCheck; ++i) { |
| 149 FilePath shortcut_file = base_path; |
| 150 if (i) { |
| 151 shortcut_file = shortcut_file.InsertBeforeExtensionASCII( |
| 152 StringPrintf(" (%d)", i)); |
| 153 } |
| 154 if (file_util::PathExists(shortcut_file) && |
| 155 ShortcutIsForProfile(shortcut_file, profile_path)) { |
| 156 shortcut_paths.push_back(shortcut_file); |
| 157 } |
| 158 } |
| 159 return shortcut_paths; |
| 160 } |
| 161 |
| 162 } // namespace |
| 163 |
| 164 namespace web_app { |
| 165 |
| 166 namespace internals { |
| 167 |
| 168 // Saves |image| to |icon_file| if the file is outdated and refresh shell's |
| 169 // icon cache to ensure correct icon is displayed. Returns true if icon_file |
| 170 // is up to date or successfully updated. |
| 171 bool CheckAndSaveIcon(const FilePath& icon_file, const SkBitmap& image) { |
| 172 if (ShouldUpdateIcon(icon_file, image)) { |
| 173 if (SaveIconWithCheckSum(icon_file, image)) { |
| 174 // Refresh shell's icon cache. This call is quite disruptive as user would |
| 175 // see explorer rebuilding the icon cache. It would be great that we find |
| 176 // a better way to achieve this. |
| 177 SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST | SHCNF_FLUSHNOWAIT, |
| 178 NULL, NULL); |
| 179 } else { |
| 180 return false; |
| 181 } |
| 182 } |
| 183 |
| 184 return true; |
| 185 } |
| 186 |
| 187 bool CreatePlatformShortcuts( |
| 188 const FilePath& web_app_path, |
| 189 const ShellIntegration::ShortcutInfo& shortcut_info) { |
| 190 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE)); |
| 191 |
| 192 // Shortcut paths under which to create shortcuts. |
| 193 std::vector<FilePath> shortcut_paths = GetShortcutPaths(shortcut_info); |
| 194 |
| 195 bool pin_to_taskbar = shortcut_info.create_in_quick_launch_bar && |
| 196 (base::win::GetVersion() >= base::win::VERSION_WIN7); |
152 | 197 |
153 // For Win7's pinning support, any shortcut could be used. So we only create | 198 // For Win7's pinning support, any shortcut could be used. So we only create |
154 // the shortcut file when there is no shortcut file will be created. That is, | 199 // the shortcut file when there is no shortcut file will be created. That is, |
155 // user only selects "Pin to taskbar". | 200 // user only selects "Pin to taskbar". |
156 if (pin_to_taskbar && shortcut_paths.empty()) { | 201 if (pin_to_taskbar && shortcut_paths.empty()) { |
157 // Creates the shortcut in web_app_path in this case. | 202 // Creates the shortcut in web_app_path in this case. |
158 shortcut_paths.push_back(web_app_path); | 203 shortcut_paths.push_back(web_app_path); |
159 } | 204 } |
160 | 205 |
161 if (shortcut_paths.empty()) { | 206 if (shortcut_paths.empty()) |
162 return false; | 207 return false; |
163 } | |
164 | 208 |
165 // Ensure web_app_path exists | 209 // Ensure web_app_path exists |
166 if (!file_util::PathExists(web_app_path) && | 210 if (!file_util::PathExists(web_app_path) && |
167 !file_util::CreateDirectory(web_app_path)) { | 211 !file_util::CreateDirectory(web_app_path)) { |
168 return false; | 212 return false; |
169 } | 213 } |
170 | 214 |
171 // Generates file name to use with persisted ico and shortcut file. | 215 // Generates file name to use with persisted ico and shortcut file. |
172 FilePath file_name = | 216 FilePath file_name = |
173 web_app::internals::GetSanitizedFileName(shortcut_info.title); | 217 web_app::internals::GetSanitizedFileName(shortcut_info.title); |
174 | 218 |
175 // Creates an ico file to use with shortcut. | 219 // Creates an ico file to use with shortcut. |
176 FilePath icon_file = web_app_path.Append(file_name).ReplaceExtension( | 220 FilePath icon_file = web_app_path.Append(file_name).ReplaceExtension( |
177 FILE_PATH_LITERAL(".ico")); | 221 FILE_PATH_LITERAL(".ico")); |
178 if (!web_app::internals::CheckAndSaveIcon(icon_file, | 222 if (!web_app::internals::CheckAndSaveIcon(icon_file, |
179 *shortcut_info.favicon.ToSkBitmap())) { | 223 *shortcut_info.favicon.ToSkBitmap())) { |
180 return false; | 224 return false; |
181 } | 225 } |
182 | 226 |
183 FilePath chrome_exe; | 227 FilePath chrome_exe; |
184 if (!PathService::Get(base::FILE_EXE, &chrome_exe)) { | 228 if (!PathService::Get(base::FILE_EXE, &chrome_exe)) |
185 return false; | 229 return false; |
186 } | |
187 | 230 |
188 // Working directory. | 231 // Working directory. |
189 FilePath chrome_folder = chrome_exe.DirName(); | 232 FilePath chrome_folder = chrome_exe.DirName(); |
190 | 233 |
191 CommandLine cmd_line(CommandLine::NO_PROGRAM); | 234 CommandLine cmd_line(CommandLine::NO_PROGRAM); |
192 cmd_line = ShellIntegration::CommandLineArgsForLauncher(shortcut_info.url, | 235 cmd_line = ShellIntegration::CommandLineArgsForLauncher(shortcut_info.url, |
193 shortcut_info.extension_id, shortcut_info.is_platform_app, | 236 shortcut_info.extension_id, shortcut_info.is_platform_app, |
194 shortcut_info.profile_path); | 237 shortcut_info.profile_path); |
195 | 238 |
196 // TODO(evan): we rely on the fact that command_line_string() is | 239 // TODO(evan): we rely on the fact that command_line_string() is |
197 // properly quoted for a Windows command line. The method on | 240 // properly quoted for a Windows command line. The method on |
198 // CommandLine should probably be renamed to better reflect that | 241 // CommandLine should probably be renamed to better reflect that |
199 // fact. | 242 // fact. |
200 string16 wide_switches(cmd_line.GetCommandLineString()); | 243 string16 wide_switches(cmd_line.GetCommandLineString()); |
201 | 244 |
202 // Sanitize description | 245 // Sanitize description |
203 string16 description = shortcut_info.description; | 246 string16 description = shortcut_info.description; |
204 if (description.length() >= MAX_PATH) | 247 if (description.length() >= MAX_PATH) |
205 description.resize(MAX_PATH - 1); | 248 description.resize(MAX_PATH - 1); |
206 | 249 |
207 // Generates app id from web app url and profile path. | 250 // Generates app id from web app url and profile path. |
208 std::string app_name = | 251 std::string app_name = |
209 web_app::GenerateApplicationNameFromInfo(shortcut_info); | 252 web_app::GenerateApplicationNameFromInfo(shortcut_info); |
210 string16 app_id = ShellIntegration::GetAppModelIdForProfile( | 253 string16 app_id = ShellIntegration::GetAppModelIdForProfile( |
211 UTF8ToUTF16(app_name), profile_path); | 254 UTF8ToUTF16(app_name), shortcut_info.profile_path); |
212 | 255 |
213 FilePath shortcut_to_pin; | 256 FilePath shortcut_to_pin; |
214 | |
215 bool success = true; | 257 bool success = true; |
216 for (size_t i = 0; i < shortcut_paths.size(); ++i) { | 258 for (size_t i = 0; i < shortcut_paths.size(); ++i) { |
217 FilePath shortcut_file = shortcut_paths[i].Append(file_name). | 259 FilePath shortcut_file = shortcut_paths[i].Append(file_name). |
218 ReplaceExtension(FILE_PATH_LITERAL(".lnk")); | 260 ReplaceExtension(FILE_PATH_LITERAL(".lnk")); |
219 | 261 |
220 int unique_number = | 262 int unique_number = |
221 file_util::GetUniquePathNumber(shortcut_file, FILE_PATH_LITERAL("")); | 263 file_util::GetUniquePathNumber(shortcut_file, FILE_PATH_LITERAL("")); |
222 if (unique_number == -1) { | 264 if (unique_number == -1) { |
223 success = false; | 265 success = false; |
224 continue; | 266 continue; |
(...skipping 23 matching lines...) Expand all Loading... |
248 success &= file_util::TaskbarPinShortcutLink( | 290 success &= file_util::TaskbarPinShortcutLink( |
249 shortcut_to_pin.value().c_str()); | 291 shortcut_to_pin.value().c_str()); |
250 } else { | 292 } else { |
251 success = false; | 293 success = false; |
252 } | 294 } |
253 } | 295 } |
254 | 296 |
255 return success; | 297 return success; |
256 } | 298 } |
257 | 299 |
258 void DeletePlatformShortcuts(const FilePath& profile_path, | 300 void DeletePlatformShortcuts( |
259 const std::string& extension_id) { | 301 const FilePath& web_app_path, |
260 // TODO(benwells): Implement this. | 302 const ShellIntegration::ShortcutInfo& shortcut_info) { |
| 303 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE)); |
| 304 |
| 305 // Get all possible locations for shortcuts. |
| 306 ShellIntegration::ShortcutInfo all_shortcuts_info = shortcut_info; |
| 307 all_shortcuts_info.create_in_applications_menu = true; |
| 308 all_shortcuts_info.create_in_quick_launch_bar = true; |
| 309 all_shortcuts_info.create_on_desktop = true; |
| 310 std::vector<FilePath> shortcut_locations = GetShortcutPaths( |
| 311 all_shortcuts_info); |
| 312 if (base::win::GetVersion() >= base::win::VERSION_WIN7) |
| 313 shortcut_locations.push_back(web_app_path); |
| 314 |
| 315 for (std::vector<FilePath>::const_iterator i = shortcut_locations.begin(); |
| 316 i != shortcut_locations.end(); ++i) { |
| 317 std::vector<FilePath> shortcut_files = |
| 318 MatchingShortcutsForProfileAndExtension(*i, shortcut_info.profile_path, |
| 319 shortcut_info.title); |
| 320 for (std::vector<FilePath>::const_iterator j = shortcut_files.begin(); |
| 321 j != shortcut_files.end(); ++j) { |
| 322 // Any shortcut could have been pinned, either by chrome or the user, so |
| 323 // they are all unpinned. |
| 324 file_util::TaskbarUnpinShortcutLink(j->value().c_str()); |
| 325 file_util::Delete(*j, false); |
| 326 } |
| 327 } |
261 } | 328 } |
262 | 329 |
263 } // namespace internals | 330 } // namespace internals |
264 | 331 |
265 } // namespace web_app | 332 } // namespace web_app |
OLD | NEW |