Roll src/third_party/WebKit 8b42d1d:744641d (svn 186770:186771)
[chromium-blink-merge.git] / chrome / browser / web_applications / web_app_win.cc
blob00f7ac23d02b68f153d22ba20a244ca68d1365e9
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
3 // found in the LICENSE file.
5 #include "chrome/browser/web_applications/web_app_win.h"
7 #include <shlobj.h>
9 #include "base/command_line.h"
10 #include "base/files/file_enumerator.h"
11 #include "base/files/file_util.h"
12 #include "base/logging.h"
13 #include "base/md5.h"
14 #include "base/path_service.h"
15 #include "base/strings/string16.h"
16 #include "base/strings/string_piece.h"
17 #include "base/strings/stringprintf.h"
18 #include "base/strings/utf_string_conversions.h"
19 #include "base/win/shortcut.h"
20 #include "base/win/windows_version.h"
21 #include "chrome/browser/profiles/profile.h"
22 #include "chrome/browser/web_applications/update_shortcut_worker_win.h"
23 #include "chrome/common/chrome_switches.h"
24 #include "chrome/installer/util/browser_distribution.h"
25 #include "chrome/installer/util/install_util.h"
26 #include "chrome/installer/util/shell_util.h"
27 #include "chrome/installer/util/util_constants.h"
28 #include "content/public/browser/browser_thread.h"
29 #include "extensions/common/extension.h"
30 #include "net/base/mime_util.h"
31 #include "ui/base/win/shell.h"
32 #include "ui/gfx/icon_util.h"
33 #include "ui/gfx/image/image.h"
34 #include "ui/gfx/image/image_family.h"
36 namespace {
38 const base::FilePath::CharType kIconChecksumFileExt[] =
39 FILE_PATH_LITERAL(".ico.md5");
41 const base::FilePath::CharType kAppShimExe[] =
42 FILE_PATH_LITERAL("app_shim.exe");
44 // Calculates checksum of an icon family using MD5.
45 // The checksum is derived from all of the icons in the family.
46 void GetImageCheckSum(const gfx::ImageFamily& image, base::MD5Digest* digest) {
47 DCHECK(digest);
48 base::MD5Context md5_context;
49 base::MD5Init(&md5_context);
51 for (gfx::ImageFamily::const_iterator it = image.begin(); it != image.end();
52 ++it) {
53 SkBitmap bitmap = it->AsBitmap();
55 SkAutoLockPixels image_lock(bitmap);
56 base::StringPiece image_data(
57 reinterpret_cast<const char*>(bitmap.getPixels()), bitmap.getSize());
58 base::MD5Update(&md5_context, image_data);
61 base::MD5Final(digest, &md5_context);
64 // Saves |image| as an |icon_file| with the checksum.
65 bool SaveIconWithCheckSum(const base::FilePath& icon_file,
66 const gfx::ImageFamily& image) {
67 if (!IconUtil::CreateIconFileFromImageFamily(image, icon_file))
68 return false;
70 base::MD5Digest digest;
71 GetImageCheckSum(image, &digest);
73 base::FilePath cheksum_file(icon_file.ReplaceExtension(kIconChecksumFileExt));
74 return base::WriteFile(cheksum_file,
75 reinterpret_cast<const char*>(&digest),
76 sizeof(digest)) == sizeof(digest);
79 // Returns true if |icon_file| is missing or different from |image|.
80 bool ShouldUpdateIcon(const base::FilePath& icon_file,
81 const gfx::ImageFamily& image) {
82 base::FilePath checksum_file(
83 icon_file.ReplaceExtension(kIconChecksumFileExt));
85 // Returns true if icon_file or checksum file is missing.
86 if (!base::PathExists(icon_file) ||
87 !base::PathExists(checksum_file))
88 return true;
90 base::MD5Digest persisted_image_checksum;
91 if (sizeof(persisted_image_checksum) != base::ReadFile(checksum_file,
92 reinterpret_cast<char*>(&persisted_image_checksum),
93 sizeof(persisted_image_checksum)))
94 return true;
96 base::MD5Digest downloaded_image_checksum;
97 GetImageCheckSum(image, &downloaded_image_checksum);
99 // Update icon if checksums are not equal.
100 return memcmp(&persisted_image_checksum, &downloaded_image_checksum,
101 sizeof(base::MD5Digest)) != 0;
104 // Returns true if |shortcut_file_name| matches profile |profile_path|, and has
105 // an --app-id flag.
106 bool IsAppShortcutForProfile(const base::FilePath& shortcut_file_name,
107 const base::FilePath& profile_path) {
108 base::string16 cmd_line_string;
109 if (base::win::ResolveShortcut(shortcut_file_name, NULL, &cmd_line_string)) {
110 cmd_line_string = L"program " + cmd_line_string;
111 CommandLine shortcut_cmd_line = CommandLine::FromString(cmd_line_string);
112 return shortcut_cmd_line.HasSwitch(switches::kProfileDirectory) &&
113 shortcut_cmd_line.GetSwitchValuePath(switches::kProfileDirectory) ==
114 profile_path.BaseName() &&
115 shortcut_cmd_line.HasSwitch(switches::kAppId);
118 return false;
121 // Finds shortcuts in |shortcut_path| that match profile for |profile_path| and
122 // extension with title |shortcut_name|.
123 // If |shortcut_name| is empty, finds all shortcuts matching |profile_path|.
124 std::vector<base::FilePath> FindAppShortcutsByProfileAndTitle(
125 const base::FilePath& shortcut_path,
126 const base::FilePath& profile_path,
127 const base::string16& shortcut_name) {
128 std::vector<base::FilePath> shortcut_paths;
130 if (shortcut_name.empty()) {
131 // Find all shortcuts for this profile.
132 base::FileEnumerator files(shortcut_path, false,
133 base::FileEnumerator::FILES,
134 FILE_PATH_LITERAL("*.lnk"));
135 base::FilePath shortcut_file = files.Next();
136 while (!shortcut_file.empty()) {
137 if (IsAppShortcutForProfile(shortcut_file, profile_path))
138 shortcut_paths.push_back(shortcut_file);
139 shortcut_file = files.Next();
141 } else {
142 // Find all shortcuts matching |shortcut_name|.
143 base::FilePath base_path = shortcut_path.
144 Append(web_app::internals::GetSanitizedFileName(shortcut_name)).
145 AddExtension(FILE_PATH_LITERAL(".lnk"));
147 const int fileNamesToCheck = 10;
148 for (int i = 0; i < fileNamesToCheck; ++i) {
149 base::FilePath shortcut_file = base_path;
150 if (i > 0) {
151 shortcut_file = shortcut_file.InsertBeforeExtensionASCII(
152 base::StringPrintf(" (%d)", i));
154 if (base::PathExists(shortcut_file) &&
155 IsAppShortcutForProfile(shortcut_file, profile_path)) {
156 shortcut_paths.push_back(shortcut_file);
161 return shortcut_paths;
164 // Creates application shortcuts in a given set of paths.
165 // |shortcut_paths| is a list of directories in which shortcuts should be
166 // created. If |creation_reason| is SHORTCUT_CREATION_AUTOMATED and there is an
167 // existing shortcut to this app for this profile, does nothing (succeeding).
168 // Returns true on success, false on failure.
169 // Must be called on the FILE thread.
170 bool CreateShortcutsInPaths(
171 const base::FilePath& web_app_path,
172 const web_app::ShortcutInfo& shortcut_info,
173 const std::vector<base::FilePath>& shortcut_paths,
174 web_app::ShortcutCreationReason creation_reason,
175 std::vector<base::FilePath>* out_filenames) {
176 // Ensure web_app_path exists.
177 if (!base::PathExists(web_app_path) &&
178 !base::CreateDirectory(web_app_path)) {
179 return false;
182 // Generates file name to use with persisted ico and shortcut file.
183 base::FilePath icon_file =
184 web_app::internals::GetIconFilePath(web_app_path, shortcut_info.title);
185 if (!web_app::internals::CheckAndSaveIcon(icon_file, shortcut_info.favicon)) {
186 return false;
189 base::FilePath chrome_exe;
190 if (!PathService::Get(base::FILE_EXE, &chrome_exe)) {
191 NOTREACHED();
192 return false;
195 // Working directory.
196 base::FilePath working_dir(chrome_exe.DirName());
198 CommandLine cmd_line(CommandLine::NO_PROGRAM);
199 cmd_line = ShellIntegration::CommandLineArgsForLauncher(shortcut_info.url,
200 shortcut_info.extension_id, shortcut_info.profile_path);
202 // TODO(evan): we rely on the fact that command_line_string() is
203 // properly quoted for a Windows command line. The method on
204 // CommandLine should probably be renamed to better reflect that
205 // fact.
206 base::string16 wide_switches(cmd_line.GetCommandLineString());
208 // Sanitize description
209 base::string16 description = shortcut_info.description;
210 if (description.length() >= MAX_PATH)
211 description.resize(MAX_PATH - 1);
213 // Generates app id from web app url and profile path.
214 std::string app_name(web_app::GenerateApplicationNameFromInfo(shortcut_info));
215 base::string16 app_id(ShellIntegration::GetAppModelIdForProfile(
216 base::UTF8ToUTF16(app_name), shortcut_info.profile_path));
218 bool success = true;
219 for (size_t i = 0; i < shortcut_paths.size(); ++i) {
220 base::FilePath shortcut_file =
221 shortcut_paths[i]
222 .Append(
223 web_app::internals::GetSanitizedFileName(shortcut_info.title))
224 .AddExtension(installer::kLnkExt);
225 if (creation_reason == web_app::SHORTCUT_CREATION_AUTOMATED) {
226 // Check whether there is an existing shortcut to this app.
227 std::vector<base::FilePath> shortcut_files =
228 FindAppShortcutsByProfileAndTitle(shortcut_paths[i],
229 shortcut_info.profile_path,
230 shortcut_info.title);
231 if (!shortcut_files.empty())
232 continue;
234 if (shortcut_paths[i] != web_app_path) {
235 int unique_number =
236 base::GetUniquePathNumber(shortcut_file,
237 base::FilePath::StringType());
238 if (unique_number == -1) {
239 success = false;
240 continue;
241 } else if (unique_number > 0) {
242 shortcut_file = shortcut_file.InsertBeforeExtensionASCII(
243 base::StringPrintf(" (%d)", unique_number));
246 base::win::ShortcutProperties shortcut_properties;
247 shortcut_properties.set_target(chrome_exe);
248 shortcut_properties.set_working_dir(working_dir);
249 shortcut_properties.set_arguments(wide_switches);
250 shortcut_properties.set_description(description);
251 shortcut_properties.set_icon(icon_file, 0);
252 shortcut_properties.set_app_id(app_id);
253 shortcut_properties.set_dual_mode(false);
254 if (!base::PathExists(shortcut_file.DirName()) &&
255 !base::CreateDirectory(shortcut_file.DirName())) {
256 NOTREACHED();
257 return false;
259 success = base::win::CreateOrUpdateShortcutLink(
260 shortcut_file, shortcut_properties,
261 base::win::SHORTCUT_CREATE_ALWAYS) && success;
262 if (out_filenames)
263 out_filenames->push_back(shortcut_file);
266 return success;
269 // Gets the directories with shortcuts for an app, and deletes the shortcuts.
270 // This will search the standard locations for shortcuts named |title| that open
271 // in the profile with |profile_path|.
272 // |was_pinned_to_taskbar| will be set to true if there was previously a
273 // shortcut pinned to the taskbar for this app; false otherwise.
274 // If |web_app_path| is empty, this will not delete shortcuts from the web app
275 // directory. If |title| is empty, all shortcuts for this profile will be
276 // deleted.
277 // |shortcut_paths| will be populated with a list of directories where shortcuts
278 // for this app were found (and deleted). This will delete duplicate shortcuts,
279 // but only return each path once, even if it contained multiple deleted
280 // shortcuts. Both of these may be NULL.
281 void GetShortcutLocationsAndDeleteShortcuts(
282 const base::FilePath& web_app_path,
283 const base::FilePath& profile_path,
284 const base::string16& title,
285 bool* was_pinned_to_taskbar,
286 std::vector<base::FilePath>* shortcut_paths) {
287 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE));
289 // Get all possible locations for shortcuts.
290 web_app::ShortcutLocations all_shortcut_locations;
291 all_shortcut_locations.in_quick_launch_bar = true;
292 all_shortcut_locations.on_desktop = true;
293 // Delete shortcuts from the Chrome Apps subdirectory.
294 // This matches the subdir name set by CreateApplicationShortcutView::Accept
295 // for Chrome apps (not URL apps, but this function does not apply for them).
296 all_shortcut_locations.applications_menu_location =
297 web_app::APP_MENU_LOCATION_SUBDIR_CHROMEAPPS;
298 std::vector<base::FilePath> all_paths = web_app::internals::GetShortcutPaths(
299 all_shortcut_locations);
300 if (base::win::GetVersion() >= base::win::VERSION_WIN7 &&
301 !web_app_path.empty()) {
302 all_paths.push_back(web_app_path);
305 if (was_pinned_to_taskbar) {
306 // Determine if there is a link to this app in the TaskBar pin directory.
307 base::FilePath taskbar_pin_path;
308 if (PathService::Get(base::DIR_TASKBAR_PINS, &taskbar_pin_path)) {
309 std::vector<base::FilePath> taskbar_pin_files =
310 FindAppShortcutsByProfileAndTitle(taskbar_pin_path, profile_path,
311 title);
312 *was_pinned_to_taskbar = !taskbar_pin_files.empty();
313 } else {
314 *was_pinned_to_taskbar = false;
318 for (std::vector<base::FilePath>::const_iterator i = all_paths.begin();
319 i != all_paths.end(); ++i) {
320 std::vector<base::FilePath> shortcut_files =
321 FindAppShortcutsByProfileAndTitle(*i, profile_path, title);
322 if (shortcut_paths && !shortcut_files.empty()) {
323 shortcut_paths->push_back(*i);
325 for (std::vector<base::FilePath>::const_iterator j = shortcut_files.begin();
326 j != shortcut_files.end(); ++j) {
327 // Any shortcut could have been pinned, either by chrome or the user, so
328 // they are all unpinned.
329 base::win::TaskbarUnpinShortcutLink(j->value().c_str());
330 base::DeleteFile(*j, false);
335 void CreateIconAndSetRelaunchDetails(const base::FilePath& web_app_path,
336 const base::FilePath& icon_file,
337 const web_app::ShortcutInfo& shortcut_info,
338 HWND hwnd) {
339 DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
341 CommandLine command_line =
342 ShellIntegration::CommandLineArgsForLauncher(shortcut_info.url,
343 shortcut_info.extension_id,
344 shortcut_info.profile_path);
346 base::FilePath chrome_exe;
347 if (!PathService::Get(base::FILE_EXE, &chrome_exe)) {
348 NOTREACHED();
349 return;
351 command_line.SetProgram(chrome_exe);
352 ui::win::SetRelaunchDetailsForWindow(
353 command_line.GetCommandLineString(), shortcut_info.title, hwnd);
355 if (!base::PathExists(web_app_path) && !base::CreateDirectory(web_app_path))
356 return;
358 ui::win::SetAppIconForWindow(icon_file.value(), hwnd);
359 web_app::internals::CheckAndSaveIcon(icon_file, shortcut_info.favicon);
362 void OnShortcutInfoLoadedForSetRelaunchDetails(
363 HWND hwnd,
364 const web_app::ShortcutInfo& shortcut_info) {
365 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
367 // Set window's icon to the one we're about to create/update in the web app
368 // path. The icon cache will refresh on icon creation.
369 base::FilePath web_app_path =
370 web_app::GetWebAppDataDirectory(shortcut_info.profile_path,
371 shortcut_info.extension_id,
372 shortcut_info.url);
373 base::FilePath icon_file =
374 web_app::internals::GetIconFilePath(web_app_path, shortcut_info.title);
375 content::BrowserThread::PostBlockingPoolTask(
376 FROM_HERE,
377 base::Bind(&CreateIconAndSetRelaunchDetails,
378 web_app_path,
379 icon_file,
380 shortcut_info,
381 hwnd));
384 // Creates an "app shim exe" by linking or copying the generic app shim exe.
385 // This is the binary that will be run when the user opens a file with this
386 // application. The name and icon of the binary will be used on the Open With
387 // menu. For this reason, we cannot simply launch chrome.exe. We give the app
388 // shim exe the same name as the application (with no ".exe" extension), so that
389 // the correct title will appear on the Open With menu. (Note: we also need a
390 // separate binary per app because Windows only allows a single association with
391 // each executable.)
392 // |path| is the full path of the shim binary to be created.
393 bool CreateAppShimBinary(const base::FilePath& path) {
394 // TODO(mgiuca): Hard-link instead of copying, if on the same file system.
395 base::FilePath chrome_binary_directory;
396 if (!PathService::Get(base::DIR_EXE, &chrome_binary_directory)) {
397 NOTREACHED();
398 return false;
401 base::FilePath generic_shim_path =
402 chrome_binary_directory.Append(kAppShimExe);
403 if (!base::CopyFile(generic_shim_path, path)) {
404 LOG(ERROR) << "Could not copy app shim exe to " << path.value();
405 return false;
408 return true;
411 // Gets the full command line for calling the shim binary. This will include a
412 // placeholder "%1" argument, which Windows will substitute with the filename
413 // chosen by the user.
414 base::CommandLine GetAppShimCommandLine(const base::FilePath& app_shim_path,
415 const std::string& extension_id,
416 const base::FilePath& profile_path) {
417 // Get the command-line to pass to the shim (e.g., "chrome.exe --app-id=...").
418 CommandLine chrome_cmd_line = ShellIntegration::CommandLineArgsForLauncher(
419 GURL(), extension_id, profile_path);
420 chrome_cmd_line.AppendArg("%1");
422 // Get the command-line for calling the shim (e.g.,
423 // "app_shim [--chrome-sxs] -- --app-id=...").
424 CommandLine shim_cmd_line(app_shim_path);
425 // If this is a canary build, launch the shim in canary mode.
426 if (InstallUtil::IsChromeSxSProcess())
427 shim_cmd_line.AppendSwitch(installer::switches::kChromeSxS);
428 // Ensure all subsequent switches are treated as args to the shim.
429 shim_cmd_line.AppendArg("--");
430 for (size_t i = 1; i < chrome_cmd_line.argv().size(); ++i)
431 shim_cmd_line.AppendArgNative(chrome_cmd_line.argv()[i]);
433 return shim_cmd_line;
436 // Gets the set of file extensions associated with a particular file handler.
437 // Uses both the MIME types and extensions.
438 void GetHandlerFileExtensions(const extensions::FileHandlerInfo& handler,
439 std::set<base::string16>* exts) {
440 for (const auto& mime : handler.types) {
441 std::vector<base::string16> mime_type_extensions;
442 net::GetExtensionsForMimeType(mime, &mime_type_extensions);
443 exts->insert(mime_type_extensions.begin(), mime_type_extensions.end());
445 for (const auto& ext : handler.extensions)
446 exts->insert(base::UTF8ToUTF16(ext));
449 // Creates operating system file type associations for a given app.
450 // This is the platform specific implementation of the CreateFileAssociations
451 // function, and is executed on the FILE thread.
452 // Returns true on success, false on failure.
453 bool CreateFileAssociationsForApp(
454 const std::string& extension_id,
455 const base::string16& title,
456 const base::FilePath& profile_path,
457 const extensions::FileHandlersInfo& file_handlers_info) {
458 base::FilePath web_app_path =
459 web_app::GetWebAppDataDirectory(profile_path, extension_id, GURL());
460 base::FilePath file_name = web_app::internals::GetSanitizedFileName(title);
462 // The progid is "chrome-APPID-HANDLERID". This is the internal name Windows
463 // will use for file associations with this application.
464 base::string16 progid_base = L"chrome-";
465 progid_base += base::UTF8ToUTF16(extension_id);
467 // Create the app shim binary (see CreateAppShimBinary for rationale). Get the
468 // command line for the shim.
469 base::FilePath app_shim_path = web_app_path.Append(file_name);
470 if (!CreateAppShimBinary(app_shim_path))
471 return false;
473 CommandLine shim_cmd_line(
474 GetAppShimCommandLine(app_shim_path, extension_id, profile_path));
476 // TODO(mgiuca): Get the file type name from the manifest, or generate a
477 // default one. (If this is blank, Windows will generate one of the form
478 // '<EXT> file'.)
479 base::string16 file_type_name = L"";
481 // TODO(mgiuca): Generate a new icon for this application's file associations
482 // that looks like a page with the application icon inside.
483 base::FilePath icon_file =
484 web_app::internals::GetIconFilePath(web_app_path, title);
486 // Create a separate file association (ProgId) for each handler. This allows
487 // each handler to have its own filetype name and icon, and also a different
488 // command line (so the app can see which handler was invoked).
489 size_t num_successes = 0;
490 for (const auto& handler : file_handlers_info) {
491 base::string16 progid = progid_base + L"-" + base::UTF8ToUTF16(handler.id);
493 std::set<base::string16> exts;
494 GetHandlerFileExtensions(handler, &exts);
496 if (ShellUtil::AddFileAssociations(progid, shim_cmd_line, file_type_name,
497 icon_file, exts)) {
498 ++num_successes;
502 if (num_successes == 0) {
503 // There were no successes; delete the shim.
504 base::DeleteFile(app_shim_path, false);
505 } else {
506 // There were some successes; tell Windows Explorer to update its cache.
507 SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST | SHCNF_FLUSHNOWAIT,
508 nullptr, nullptr);
511 return num_successes == file_handlers_info.size();
514 } // namespace
516 namespace web_app {
518 base::FilePath CreateShortcutInWebAppDir(const base::FilePath& web_app_dir,
519 const ShortcutInfo& shortcut_info) {
520 std::vector<base::FilePath> paths;
521 paths.push_back(web_app_dir);
522 std::vector<base::FilePath> out_filenames;
523 base::FilePath web_app_dir_shortcut =
524 web_app_dir.Append(internals::GetSanitizedFileName(shortcut_info.title))
525 .AddExtension(installer::kLnkExt);
526 if (!PathExists(web_app_dir_shortcut)) {
527 CreateShortcutsInPaths(web_app_dir,
528 shortcut_info,
529 paths,
530 SHORTCUT_CREATION_BY_USER,
531 &out_filenames);
532 DCHECK_EQ(out_filenames.size(), 1u);
533 DCHECK_EQ(out_filenames[0].value(), web_app_dir_shortcut.value());
534 } else {
535 internals::CheckAndSaveIcon(
536 internals::GetIconFilePath(web_app_dir, shortcut_info.title),
537 shortcut_info.favicon);
539 return web_app_dir_shortcut;
542 void UpdateRelaunchDetailsForApp(Profile* profile,
543 const extensions::Extension* extension,
544 HWND hwnd) {
545 web_app::GetShortcutInfoForApp(
546 extension,
547 profile,
548 base::Bind(&OnShortcutInfoLoadedForSetRelaunchDetails, hwnd));
551 void UpdateShortcutsForAllApps(Profile* profile,
552 const base::Closure& callback) {
553 callback.Run();
556 namespace internals {
558 // Saves |image| to |icon_file| if the file is outdated and refresh shell's
559 // icon cache to ensure correct icon is displayed. Returns true if icon_file
560 // is up to date or successfully updated.
561 bool CheckAndSaveIcon(const base::FilePath& icon_file,
562 const gfx::ImageFamily& image) {
563 if (ShouldUpdateIcon(icon_file, image)) {
564 if (SaveIconWithCheckSum(icon_file, image)) {
565 // Refresh shell's icon cache. This call is quite disruptive as user would
566 // see explorer rebuilding the icon cache. It would be great that we find
567 // a better way to achieve this.
568 SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST | SHCNF_FLUSHNOWAIT,
569 NULL, NULL);
570 } else {
571 return false;
575 return true;
578 bool CreatePlatformShortcuts(
579 const base::FilePath& web_app_path,
580 const ShortcutInfo& shortcut_info,
581 const extensions::FileHandlersInfo& file_handlers_info,
582 const ShortcutLocations& creation_locations,
583 ShortcutCreationReason creation_reason) {
584 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE));
586 // Nothing to do on Windows for hidden apps.
587 if (creation_locations.applications_menu_location == APP_MENU_LOCATION_HIDDEN)
588 return true;
590 // Shortcut paths under which to create shortcuts.
591 std::vector<base::FilePath> shortcut_paths =
592 GetShortcutPaths(creation_locations);
594 bool pin_to_taskbar = creation_locations.in_quick_launch_bar &&
595 (base::win::GetVersion() >= base::win::VERSION_WIN7);
597 // Create/update the shortcut in the web app path for the "Pin To Taskbar"
598 // option in Win7. We use the web app path shortcut because we will overwrite
599 // it rather than appending unique numbers if the shortcut already exists.
600 // This prevents pinned apps from having unique numbers in their names.
601 if (pin_to_taskbar)
602 shortcut_paths.push_back(web_app_path);
604 if (shortcut_paths.empty())
605 return false;
607 if (!CreateShortcutsInPaths(web_app_path, shortcut_info, shortcut_paths,
608 creation_reason, NULL))
609 return false;
611 if (pin_to_taskbar) {
612 base::FilePath file_name = GetSanitizedFileName(shortcut_info.title);
613 // Use the web app path shortcut for pinning to avoid having unique numbers
614 // in the application name.
615 base::FilePath shortcut_to_pin = web_app_path.Append(file_name).
616 AddExtension(installer::kLnkExt);
617 if (!base::win::TaskbarPinShortcutLink(shortcut_to_pin.value().c_str()))
618 return false;
621 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
622 switches::kEnableAppsFileAssociations)) {
623 CreateFileAssociationsForApp(
624 shortcut_info.extension_id, shortcut_info.title,
625 shortcut_info.profile_path, file_handlers_info);
628 return true;
631 void UpdatePlatformShortcuts(
632 const base::FilePath& web_app_path,
633 const base::string16& old_app_title,
634 const ShortcutInfo& shortcut_info,
635 const extensions::FileHandlersInfo& file_handlers_info) {
636 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE));
638 // Generates file name to use with persisted ico and shortcut file.
639 base::FilePath file_name =
640 web_app::internals::GetSanitizedFileName(shortcut_info.title);
642 if (old_app_title != shortcut_info.title) {
643 // The app's title has changed. Delete all existing app shortcuts and
644 // recreate them in any locations they already existed (but do not add them
645 // to locations where they do not currently exist).
646 bool was_pinned_to_taskbar;
647 std::vector<base::FilePath> shortcut_paths;
648 GetShortcutLocationsAndDeleteShortcuts(
649 web_app_path, shortcut_info.profile_path, old_app_title,
650 &was_pinned_to_taskbar, &shortcut_paths);
651 CreateShortcutsInPaths(web_app_path, shortcut_info, shortcut_paths,
652 SHORTCUT_CREATION_BY_USER, NULL);
653 // If the shortcut was pinned to the taskbar,
654 // GetShortcutLocationsAndDeleteShortcuts will have deleted it. In that
655 // case, re-pin it.
656 if (was_pinned_to_taskbar) {
657 base::FilePath file_name = GetSanitizedFileName(shortcut_info.title);
658 // Use the web app path shortcut for pinning to avoid having unique
659 // numbers in the application name.
660 base::FilePath shortcut_to_pin = web_app_path.Append(file_name).
661 AddExtension(installer::kLnkExt);
662 base::win::TaskbarPinShortcutLink(shortcut_to_pin.value().c_str());
666 // Update the icon if necessary.
667 base::FilePath icon_file = GetIconFilePath(web_app_path, shortcut_info.title);
668 CheckAndSaveIcon(icon_file, shortcut_info.favicon);
671 void DeletePlatformShortcuts(const base::FilePath& web_app_path,
672 const ShortcutInfo& shortcut_info) {
673 GetShortcutLocationsAndDeleteShortcuts(
674 web_app_path, shortcut_info.profile_path, shortcut_info.title, NULL,
675 NULL);
677 // If there are no more shortcuts in the Chrome Apps subdirectory, remove it.
678 base::FilePath chrome_apps_dir;
679 if (ShellUtil::GetShortcutPath(
680 ShellUtil::SHORTCUT_LOCATION_START_MENU_CHROME_APPS_DIR,
681 BrowserDistribution::GetDistribution(),
682 ShellUtil::CURRENT_USER,
683 &chrome_apps_dir)) {
684 if (base::IsDirectoryEmpty(chrome_apps_dir))
685 base::DeleteFile(chrome_apps_dir, false);
689 void DeleteAllShortcutsForProfile(const base::FilePath& profile_path) {
690 GetShortcutLocationsAndDeleteShortcuts(base::FilePath(), profile_path, L"",
691 NULL, NULL);
693 // If there are no more shortcuts in the Chrome Apps subdirectory, remove it.
694 base::FilePath chrome_apps_dir;
695 if (ShellUtil::GetShortcutPath(
696 ShellUtil::SHORTCUT_LOCATION_START_MENU_CHROME_APPS_DIR,
697 BrowserDistribution::GetDistribution(),
698 ShellUtil::CURRENT_USER,
699 &chrome_apps_dir)) {
700 if (base::IsDirectoryEmpty(chrome_apps_dir))
701 base::DeleteFile(chrome_apps_dir, false);
705 std::vector<base::FilePath> GetShortcutPaths(
706 const ShortcutLocations& creation_locations) {
707 // Shortcut paths under which to create shortcuts.
708 std::vector<base::FilePath> shortcut_paths;
709 // Locations to add to shortcut_paths.
710 struct {
711 bool use_this_location;
712 ShellUtil::ShortcutLocation location_id;
713 } locations[] = {
715 creation_locations.on_desktop,
716 ShellUtil::SHORTCUT_LOCATION_DESKTOP
717 }, {
718 creation_locations.applications_menu_location ==
719 APP_MENU_LOCATION_ROOT,
720 ShellUtil::SHORTCUT_LOCATION_START_MENU_ROOT
721 }, {
722 creation_locations.applications_menu_location ==
723 APP_MENU_LOCATION_SUBDIR_CHROME,
724 ShellUtil::SHORTCUT_LOCATION_START_MENU_CHROME_DIR
725 }, {
726 creation_locations.applications_menu_location ==
727 APP_MENU_LOCATION_SUBDIR_CHROMEAPPS,
728 ShellUtil::SHORTCUT_LOCATION_START_MENU_CHROME_APPS_DIR
729 }, {
730 // For Win7+, |in_quick_launch_bar| indicates that we are pinning to
731 // taskbar. This needs to be handled by callers.
732 creation_locations.in_quick_launch_bar &&
733 base::win::GetVersion() < base::win::VERSION_WIN7,
734 ShellUtil::SHORTCUT_LOCATION_QUICK_LAUNCH
738 BrowserDistribution* dist = BrowserDistribution::GetDistribution();
739 // Populate shortcut_paths.
740 for (int i = 0; i < arraysize(locations); ++i) {
741 if (locations[i].use_this_location) {
742 base::FilePath path;
743 if (!ShellUtil::GetShortcutPath(locations[i].location_id,
744 dist,
745 ShellUtil::CURRENT_USER,
746 &path)) {
747 NOTREACHED();
748 continue;
750 shortcut_paths.push_back(path);
753 return shortcut_paths;
756 base::FilePath GetIconFilePath(const base::FilePath& web_app_path,
757 const base::string16& title) {
758 return web_app_path.Append(GetSanitizedFileName(title))
759 .AddExtension(FILE_PATH_LITERAL(".ico"));
762 } // namespace internals
764 void UpdateShortcutForTabContents(content::WebContents* web_contents) {
765 // UpdateShortcutWorker will delete itself when it's done.
766 UpdateShortcutWorker* worker = new UpdateShortcutWorker(web_contents);
767 worker->Run();
770 } // namespace web_app