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