Fix build break
[chromium-blink-merge.git] / chrome / browser / profiles / profile_shortcut_manager_win.cc
blob4620240825ae897b36b72fe078729a08d424487c
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/profiles/profile_shortcut_manager_win.h"
7 #include <shlobj.h> // For SHChangeNotify().
9 #include <string>
10 #include <vector>
12 #include "base/bind.h"
13 #include "base/command_line.h"
14 #include "base/file_util.h"
15 #include "base/path_service.h"
16 #include "base/string16.h"
17 #include "base/string_util.h"
18 #include "base/stringprintf.h"
19 #include "base/utf_string_conversions.h"
20 #include "base/win/shortcut.h"
21 #include "chrome/browser/app_icon_win.h"
22 #include "chrome/browser/browser_process.h"
23 #include "chrome/browser/profiles/profile_info_cache_observer.h"
24 #include "chrome/browser/profiles/profile_info_util.h"
25 #include "chrome/browser/profiles/profile_manager.h"
26 #include "chrome/common/chrome_switches.h"
27 #include "chrome/installer/util/browser_distribution.h"
28 #include "chrome/installer/util/product.h"
29 #include "chrome/installer/util/shell_util.h"
30 #include "content/public/browser/browser_thread.h"
31 #include "grit/chrome_unscaled_resources.h"
32 #include "grit/chromium_strings.h"
33 #include "skia/ext/image_operations.h"
34 #include "skia/ext/platform_canvas.h"
35 #include "ui/base/l10n/l10n_util.h"
36 #include "ui/base/resource/resource_bundle.h"
37 #include "ui/gfx/icon_util.h"
38 #include "ui/gfx/image/image.h"
39 #include "ui/gfx/rect.h"
40 #include "ui/gfx/skia_util.h"
42 using content::BrowserThread;
44 namespace {
46 // Characters that are not allowed in Windows filenames. Taken from
47 // http://msdn.microsoft.com/en-us/library/aa365247.aspx
48 const char16 kReservedCharacters[] = L"<>:\"/\\|?*\x01\x02\x03\x04\x05\x06\x07"
49 L"\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19"
50 L"\x1A\x1B\x1C\x1D\x1E\x1F";
52 // The maximum number of characters allowed in profile shortcuts' file names.
53 // Warning: migration code will be needed if this is changed later, since
54 // existing shortcuts might no longer be found if the name is generated
55 // differently than it was when a shortcut was originally created.
56 const int kMaxProfileShortcutFileNameLength = 64;
58 const int kProfileAvatarBadgeSize = 28;
59 const int kShortcutIconSize = 48;
61 // 2x sized profile avatar icons. Mirrors |kDefaultAvatarIconResources| in
62 // profile_info_cache.cc.
63 const int kProfileAvatarIconResources2x[] = {
64 IDR_PROFILE_AVATAR_2X_0,
65 IDR_PROFILE_AVATAR_2X_1,
66 IDR_PROFILE_AVATAR_2X_2,
67 IDR_PROFILE_AVATAR_2X_3,
68 IDR_PROFILE_AVATAR_2X_4,
69 IDR_PROFILE_AVATAR_2X_5,
70 IDR_PROFILE_AVATAR_2X_6,
71 IDR_PROFILE_AVATAR_2X_7,
72 IDR_PROFILE_AVATAR_2X_8,
73 IDR_PROFILE_AVATAR_2X_9,
74 IDR_PROFILE_AVATAR_2X_10,
75 IDR_PROFILE_AVATAR_2X_11,
76 IDR_PROFILE_AVATAR_2X_12,
77 IDR_PROFILE_AVATAR_2X_13,
78 IDR_PROFILE_AVATAR_2X_14,
79 IDR_PROFILE_AVATAR_2X_15,
80 IDR_PROFILE_AVATAR_2X_16,
81 IDR_PROFILE_AVATAR_2X_17,
82 IDR_PROFILE_AVATAR_2X_18,
83 IDR_PROFILE_AVATAR_2X_19,
84 IDR_PROFILE_AVATAR_2X_20,
85 IDR_PROFILE_AVATAR_2X_21,
86 IDR_PROFILE_AVATAR_2X_22,
87 IDR_PROFILE_AVATAR_2X_23,
88 IDR_PROFILE_AVATAR_2X_24,
89 IDR_PROFILE_AVATAR_2X_25,
92 // Badges |app_icon_bitmap| with |avatar_bitmap| at the bottom right corner and
93 // returns the resulting SkBitmap.
94 SkBitmap BadgeIcon(const SkBitmap& app_icon_bitmap,
95 const SkBitmap& avatar_bitmap,
96 int scale_factor) {
97 // TODO(rlp): Share this chunk of code with
98 // avatar_menu_button::DrawTaskBarDecoration.
99 SkBitmap source_bitmap = avatar_bitmap;
100 if ((avatar_bitmap.width() == scale_factor * profiles::kAvatarIconWidth) &&
101 (avatar_bitmap.height() == scale_factor * profiles::kAvatarIconHeight)) {
102 // Shave a couple of columns so the bitmap is more square. So when
103 // resized to a square aspect ratio it looks pretty.
104 gfx::Rect frame(scale_factor * profiles::kAvatarIconWidth,
105 scale_factor * profiles::kAvatarIconHeight);
106 frame.Inset(scale_factor * 2, 0, scale_factor * 2, 0);
107 avatar_bitmap.extractSubset(&source_bitmap, gfx::RectToSkIRect(frame));
108 } else {
109 NOTREACHED();
111 int avatar_badge_size = kProfileAvatarBadgeSize;
112 if (app_icon_bitmap.width() != kShortcutIconSize) {
113 avatar_badge_size =
114 app_icon_bitmap.width() * kProfileAvatarBadgeSize / kShortcutIconSize;
116 SkBitmap sk_icon = skia::ImageOperations::Resize(
117 source_bitmap, skia::ImageOperations::RESIZE_LANCZOS3, avatar_badge_size,
118 source_bitmap.height() * avatar_badge_size / source_bitmap.width());
120 // Overlay the avatar on the icon, anchoring it to the bottom-right of the
121 // icon.
122 scoped_ptr<SkCanvas> offscreen_canvas(
123 skia::CreateBitmapCanvas(app_icon_bitmap.width(),
124 app_icon_bitmap.height(),
125 false));
126 DCHECK(offscreen_canvas.get());
127 offscreen_canvas->drawBitmap(app_icon_bitmap, 0, 0);
128 offscreen_canvas->drawBitmap(sk_icon,
129 app_icon_bitmap.width() - sk_icon.width(),
130 app_icon_bitmap.height() - sk_icon.height());
131 const SkBitmap& badged_bitmap =
132 offscreen_canvas->getDevice()->accessBitmap(false);
133 SkBitmap badged_bitmap_copy;
134 badged_bitmap.deepCopyTo(&badged_bitmap_copy, badged_bitmap.getConfig());
135 return badged_bitmap_copy;
138 // Creates a desktop shortcut icon file (.ico) on the disk for a given profile,
139 // badging the browser distribution icon with the profile avatar.
140 // Returns a path to the shortcut icon file on disk, which is empty if this
141 // fails. Use index 0 when assigning the resulting file as the icon.
142 base::FilePath CreateChromeDesktopShortcutIconForProfile(
143 const base::FilePath& profile_path,
144 const SkBitmap& avatar_bitmap_1x,
145 const SkBitmap& avatar_bitmap_2x) {
146 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
147 scoped_ptr<SkBitmap> app_icon_bitmap(GetAppIconForSize(kShortcutIconSize));
148 if (!app_icon_bitmap.get())
149 return base::FilePath();
151 const SkBitmap badged_bitmap = BadgeIcon(*app_icon_bitmap,
152 avatar_bitmap_1x, 1);
154 SkBitmap large_badged_bitmap;
155 app_icon_bitmap = GetAppIconForSize(IconUtil::kLargeIconSize);
156 if (app_icon_bitmap.get())
157 large_badged_bitmap = BadgeIcon(*app_icon_bitmap, avatar_bitmap_2x, 2);
159 // Finally, write the .ico file containing this new bitmap.
160 const base::FilePath icon_path =
161 profile_path.AppendASCII(profiles::internal::kProfileIconFileName);
162 if (!IconUtil::CreateIconFileFromSkBitmap(badged_bitmap, large_badged_bitmap,
163 icon_path))
164 return base::FilePath();
166 return icon_path;
169 // Gets the user and system directories for desktop shortcuts. Parameters may
170 // be NULL if a directory type is not needed. Returns true on success.
171 bool GetDesktopShortcutsDirectories(
172 base::FilePath* user_shortcuts_directory,
173 base::FilePath* system_shortcuts_directory) {
174 BrowserDistribution* distribution = BrowserDistribution::GetDistribution();
175 if (user_shortcuts_directory &&
176 !ShellUtil::GetShortcutPath(ShellUtil::SHORTCUT_LOCATION_DESKTOP,
177 distribution, ShellUtil::CURRENT_USER,
178 user_shortcuts_directory)) {
179 NOTREACHED();
180 return false;
182 if (system_shortcuts_directory &&
183 !ShellUtil::GetShortcutPath(ShellUtil::SHORTCUT_LOCATION_DESKTOP,
184 distribution, ShellUtil::SYSTEM_LEVEL,
185 system_shortcuts_directory)) {
186 NOTREACHED();
187 return false;
189 return true;
192 // Returns the long form of |path|, which will expand any shortened components
193 // like "foo~2" to their full names.
194 base::FilePath ConvertToLongPath(const base::FilePath& path) {
195 const size_t length = GetLongPathName(path.value().c_str(), NULL, 0);
196 if (length != 0 && length != path.value().length()) {
197 std::vector<wchar_t> long_path(length);
198 if (GetLongPathName(path.value().c_str(), &long_path[0], length) != 0)
199 return base::FilePath(&long_path[0]);
201 return path;
204 // Returns true if the file at |path| is a Chrome shortcut and returns its
205 // command line in output parameter |command_line|.
206 bool IsChromeShortcut(const base::FilePath& path,
207 const base::FilePath& chrome_exe,
208 string16* command_line) {
209 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
211 if (path.Extension() != installer::kLnkExt)
212 return false;
214 base::FilePath target_path;
215 if (!base::win::ResolveShortcut(path, &target_path, command_line))
216 return false;
217 // One of the paths may be in short (elided) form. Compare long paths to
218 // ensure these are still properly matched.
219 return ConvertToLongPath(target_path) == ConvertToLongPath(chrome_exe);
222 // Populates |paths| with the file paths of Chrome desktop shortcuts that have
223 // the specified |command_line|. If |include_empty_command_lines| is true,
224 // Chrome desktop shortcuts with empty command lines will also be included.
225 void ListDesktopShortcutsWithCommandLine(const base::FilePath& chrome_exe,
226 const string16& command_line,
227 bool include_empty_command_lines,
228 std::vector<base::FilePath>* paths) {
229 base::FilePath user_shortcuts_directory;
230 if (!GetDesktopShortcutsDirectories(&user_shortcuts_directory, NULL))
231 return;
233 file_util::FileEnumerator enumerator(user_shortcuts_directory, false,
234 file_util::FileEnumerator::FILES);
235 for (base::FilePath path = enumerator.Next(); !path.empty();
236 path = enumerator.Next()) {
237 string16 shortcut_command_line;
238 if (!IsChromeShortcut(path, chrome_exe, &shortcut_command_line))
239 continue;
241 // TODO(asvitkine): Change this to build a CommandLine object and ensure all
242 // args from |command_line| are present in the shortcut's CommandLine. This
243 // will be more robust when |command_line| contains multiple args.
244 if ((shortcut_command_line.empty() && include_empty_command_lines) ||
245 (shortcut_command_line.find(command_line) != string16::npos)) {
246 paths->push_back(path);
251 // Renames the given desktop shortcut and informs the shell of this change.
252 bool RenameDesktopShortcut(const base::FilePath& old_shortcut_path,
253 const base::FilePath& new_shortcut_path) {
254 if (!file_util::Move(old_shortcut_path, new_shortcut_path))
255 return false;
257 // Notify the shell of the rename, which allows the icon to keep its position
258 // on the desktop when renamed. Note: This only works if either SHCNF_FLUSH or
259 // SHCNF_FLUSHNOWAIT is specified as a flag.
260 SHChangeNotify(SHCNE_RENAMEITEM, SHCNF_PATH | SHCNF_FLUSHNOWAIT,
261 old_shortcut_path.value().c_str(),
262 new_shortcut_path.value().c_str());
263 return true;
266 // Renames an existing Chrome desktop profile shortcut. Must be called on the
267 // FILE thread.
268 void RenameChromeDesktopShortcutForProfile(
269 const string16& old_shortcut_filename,
270 const string16& new_shortcut_filename) {
271 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
273 base::FilePath user_shortcuts_directory;
274 base::FilePath system_shortcuts_directory;
275 if (!GetDesktopShortcutsDirectories(&user_shortcuts_directory,
276 &system_shortcuts_directory)) {
277 return;
280 const base::FilePath old_shortcut_path =
281 user_shortcuts_directory.Append(old_shortcut_filename);
282 const base::FilePath new_shortcut_path =
283 user_shortcuts_directory.Append(new_shortcut_filename);
285 if (file_util::PathExists(old_shortcut_path)) {
286 // Rename the old shortcut unless a system-level shortcut exists at the
287 // destination, in which case the old shortcut is simply deleted.
288 const base::FilePath possible_new_system_shortcut =
289 system_shortcuts_directory.Append(new_shortcut_filename);
290 if (file_util::PathExists(possible_new_system_shortcut))
291 file_util::Delete(old_shortcut_path, false);
292 else if (!RenameDesktopShortcut(old_shortcut_path, new_shortcut_path))
293 DLOG(ERROR) << "Could not rename Windows profile desktop shortcut.";
294 } else {
295 // If the shortcut does not exist, it may have been renamed by the user. In
296 // that case, its name should not be changed.
297 // It's also possible that a system-level shortcut exists instead - this
298 // should only be the case for the original Chrome shortcut from an
299 // installation. If that's the case, copy that one over - it will get its
300 // properties updated by |CreateOrUpdateDesktopShortcutsForProfile()|.
301 const base::FilePath possible_old_system_shortcut =
302 system_shortcuts_directory.Append(old_shortcut_filename);
303 if (file_util::PathExists(possible_old_system_shortcut))
304 file_util::CopyFile(possible_old_system_shortcut, new_shortcut_path);
308 // Updates all desktop shortcuts for the given profile to have the specified
309 // parameters. If |create_mode| is CREATE_WHEN_NONE_FOUND, a new shortcut is
310 // created if no existing ones were found. Whether non-profile shortcuts should
311 // be updated is specified by |action|. Must be called on the FILE thread.
312 void CreateOrUpdateDesktopShortcutsForProfile(
313 const base::FilePath& profile_path,
314 const string16& old_profile_name,
315 const string16& profile_name,
316 const SkBitmap& avatar_image_1x,
317 const SkBitmap& avatar_image_2x,
318 ProfileShortcutManagerWin::CreateOrUpdateMode create_mode,
319 ProfileShortcutManagerWin::NonProfileShortcutAction action) {
320 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
322 base::FilePath chrome_exe;
323 if (!PathService::Get(base::FILE_EXE, &chrome_exe)) {
324 NOTREACHED();
325 return;
328 BrowserDistribution* distribution = BrowserDistribution::GetDistribution();
329 // Ensure that the distribution supports creating shortcuts. If it doesn't,
330 // the following code may result in NOTREACHED() being hit.
331 DCHECK(distribution->CanCreateDesktopShortcuts());
333 if (old_profile_name != profile_name) {
334 const string16 old_shortcut_filename =
335 profiles::internal::GetShortcutFilenameForProfile(old_profile_name,
336 distribution);
337 const string16 new_shortcut_filename =
338 profiles::internal::GetShortcutFilenameForProfile(profile_name,
339 distribution);
340 RenameChromeDesktopShortcutForProfile(old_shortcut_filename,
341 new_shortcut_filename);
344 ShellUtil::ShortcutProperties properties(ShellUtil::CURRENT_USER);
345 installer::Product product(distribution);
346 product.AddDefaultShortcutProperties(chrome_exe, &properties);
348 const string16 command_line =
349 profiles::internal::CreateProfileShortcutFlags(profile_path);
351 // Only set the profile-specific properties when |profile_name| is non empty.
352 // If it is empty, it means the shortcut being created should be a regular,
353 // non-profile Chrome shortcut.
354 if (!profile_name.empty()) {
355 const base::FilePath shortcut_icon =
356 CreateChromeDesktopShortcutIconForProfile(profile_path,
357 avatar_image_1x,
358 avatar_image_2x);
359 if (!shortcut_icon.empty())
360 properties.set_icon(shortcut_icon, 0);
361 properties.set_arguments(command_line);
362 } else {
363 // Set the arguments explicitly to the empty string to ensure that
364 // |ShellUtil::CreateOrUpdateShortcut| updates that part of the shortcut.
365 properties.set_arguments(string16());
368 ShellUtil::ShortcutOperation operation =
369 ShellUtil::SHELL_SHORTCUT_REPLACE_EXISTING;
371 std::vector<base::FilePath> shortcuts;
372 ListDesktopShortcutsWithCommandLine(chrome_exe, command_line,
373 action == ProfileShortcutManagerWin::UPDATE_NON_PROFILE_SHORTCUTS,
374 &shortcuts);
375 if (create_mode == ProfileShortcutManagerWin::CREATE_WHEN_NONE_FOUND &&
376 shortcuts.empty()) {
377 const string16 shortcut_name =
378 profiles::internal::GetShortcutFilenameForProfile(profile_name,
379 distribution);
380 shortcuts.push_back(base::FilePath(shortcut_name));
381 operation = ShellUtil::SHELL_SHORTCUT_CREATE_IF_NO_SYSTEM_LEVEL;
384 for (size_t i = 0; i < shortcuts.size(); ++i) {
385 const base::FilePath shortcut_name =
386 shortcuts[i].BaseName().RemoveExtension();
387 properties.set_shortcut_name(shortcut_name.value());
388 ShellUtil::CreateOrUpdateShortcut(ShellUtil::SHORTCUT_LOCATION_DESKTOP,
389 distribution, properties, operation);
393 // Returns true if any desktop shortcuts exist with target |chrome_exe|,
394 // regardless of their command line arguments.
395 bool ChromeDesktopShortcutsExist(const base::FilePath& chrome_exe) {
396 base::FilePath user_shortcuts_directory;
397 if (!GetDesktopShortcutsDirectories(&user_shortcuts_directory, NULL))
398 return false;
400 file_util::FileEnumerator enumerator(user_shortcuts_directory, false,
401 file_util::FileEnumerator::FILES);
402 for (base::FilePath path = enumerator.Next(); !path.empty();
403 path = enumerator.Next()) {
404 if (IsChromeShortcut(path, chrome_exe, NULL))
405 return true;
408 return false;
411 // Deletes all desktop shortcuts for the specified profile and also removes the
412 // corresponding icon file. If |ensure_shortcuts_remain| is true, then a regular
413 // non-profile shortcut will be created if this function would otherwise delete
414 // the last Chrome desktop shortcut(s). Must be called on the FILE thread.
415 void DeleteDesktopShortcutsAndIconFile(const base::FilePath& profile_path,
416 bool ensure_shortcuts_remain) {
417 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
419 base::FilePath chrome_exe;
420 if (!PathService::Get(base::FILE_EXE, &chrome_exe)) {
421 NOTREACHED();
422 return;
425 const string16 command_line =
426 profiles::internal::CreateProfileShortcutFlags(profile_path);
427 std::vector<base::FilePath> shortcuts;
428 ListDesktopShortcutsWithCommandLine(chrome_exe, command_line, false,
429 &shortcuts);
431 BrowserDistribution* distribution = BrowserDistribution::GetDistribution();
432 for (size_t i = 0; i < shortcuts.size(); ++i) {
433 // Use file_util::Delete() instead of ShellUtil::RemoveShortcut(), as the
434 // latter causes non-profile taskbar shortcuts to be unpinned.
435 file_util::Delete(shortcuts[i], false);
436 // Notify the shell that the shortcut was deleted to ensure desktop refresh.
437 SHChangeNotify(SHCNE_DELETE, SHCNF_PATH, shortcuts[i].value().c_str(),
438 NULL);
441 const base::FilePath icon_path =
442 profile_path.AppendASCII(profiles::internal::kProfileIconFileName);
443 file_util::Delete(icon_path, false);
445 // If |ensure_shortcuts_remain| is true and deleting this profile caused the
446 // last shortcuts to be removed, re-create a regular non-profile shortcut.
447 const bool had_shortcuts = !shortcuts.empty();
448 if (ensure_shortcuts_remain && had_shortcuts &&
449 !ChromeDesktopShortcutsExist(chrome_exe)) {
450 BrowserDistribution* distribution = BrowserDistribution::GetDistribution();
451 // Ensure that the distribution supports creating shortcuts. If it doesn't,
452 // the following code may result in NOTREACHED() being hit.
453 DCHECK(distribution->CanCreateDesktopShortcuts());
454 installer::Product product(distribution);
456 ShellUtil::ShortcutProperties properties(ShellUtil::CURRENT_USER);
457 product.AddDefaultShortcutProperties(chrome_exe, &properties);
458 properties.set_shortcut_name(
459 profiles::internal::GetShortcutFilenameForProfile(string16(),
460 distribution));
461 ShellUtil::CreateOrUpdateShortcut(
462 ShellUtil::SHORTCUT_LOCATION_DESKTOP, distribution, properties,
463 ShellUtil::SHELL_SHORTCUT_CREATE_IF_NO_SYSTEM_LEVEL);
467 // Returns true if profile at |profile_path| has any shortcuts. Does not
468 // consider non-profile shortcuts. Must be called on the FILE thread.
469 bool HasAnyProfileShortcuts(const base::FilePath& profile_path) {
470 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
472 base::FilePath chrome_exe;
473 if (!PathService::Get(base::FILE_EXE, &chrome_exe)) {
474 NOTREACHED();
475 return false;
478 const string16 command_line =
479 profiles::internal::CreateProfileShortcutFlags(profile_path);
480 std::vector<base::FilePath> shortcuts;
481 ListDesktopShortcutsWithCommandLine(chrome_exe, command_line, false,
482 &shortcuts);
483 return !shortcuts.empty();
486 // Replaces any reserved characters with spaces, and trims the resulting string
487 // to prevent any leading and trailing spaces. Also makes sure that the
488 // resulting filename doesn't exceed |kMaxProfileShortcutFileNameLength|.
489 // TODO(macourteau): find a way to limit the total path's length to MAX_PATH
490 // instead of limiting the profile's name to |kMaxProfileShortcutFileNameLength|
491 // characters.
492 string16 SanitizeShortcutProfileNameString(const string16& profile_name) {
493 string16 sanitized = profile_name;
494 size_t pos = sanitized.find_first_of(kReservedCharacters);
495 while (pos != string16::npos) {
496 sanitized[pos] = L' ';
497 pos = sanitized.find_first_of(kReservedCharacters, pos + 1);
500 TrimWhitespace(sanitized, TRIM_LEADING, &sanitized);
501 if (sanitized.size() > kMaxProfileShortcutFileNameLength)
502 sanitized.erase(kMaxProfileShortcutFileNameLength);
503 TrimWhitespace(sanitized, TRIM_TRAILING, &sanitized);
505 return sanitized;
508 // Returns a copied SkBitmap for the given resource id that can be safely passed
509 // to another thread.
510 SkBitmap GetImageResourceSkBitmapCopy(int resource_id) {
511 const gfx::Image image =
512 ResourceBundle::GetSharedInstance().GetNativeImageNamed(resource_id);
513 DCHECK(!image.IsEmpty());
515 const SkBitmap* image_bitmap = image.ToSkBitmap();
516 SkBitmap bitmap_copy;
517 image_bitmap->deepCopyTo(&bitmap_copy, image_bitmap->getConfig());
518 return bitmap_copy;
521 } // namespace
523 namespace profiles {
524 namespace internal {
526 const char kProfileIconFileName[] = "Google Profile.ico";
528 string16 GetShortcutFilenameForProfile(const string16& profile_name,
529 BrowserDistribution* distribution) {
530 string16 shortcut_name;
531 if (!profile_name.empty()) {
532 shortcut_name.append(SanitizeShortcutProfileNameString(profile_name));
533 shortcut_name.append(L" - ");
534 shortcut_name.append(l10n_util::GetStringUTF16(IDS_SHORT_PRODUCT_NAME));
535 } else {
536 shortcut_name.append(distribution->GetAppShortCutName());
538 return shortcut_name + installer::kLnkExt;
541 string16 CreateProfileShortcutFlags(const base::FilePath& profile_path) {
542 return base::StringPrintf(L"--%ls=\"%ls\"",
543 ASCIIToUTF16(switches::kProfileDirectory).c_str(),
544 profile_path.BaseName().value().c_str());
547 } // namespace internal
548 } // namespace profiles
550 // static
551 bool ProfileShortcutManager::IsFeatureEnabled() {
552 return BrowserDistribution::GetDistribution()->CanCreateDesktopShortcuts() &&
553 !CommandLine::ForCurrentProcess()->HasSwitch(switches::kUserDataDir) &&
554 !CommandLine::ForCurrentProcess()->HasSwitch(switches::kShowAppList);
557 // static
558 ProfileShortcutManager* ProfileShortcutManager::Create(
559 ProfileManager* manager) {
560 return new ProfileShortcutManagerWin(manager);
563 ProfileShortcutManagerWin::ProfileShortcutManagerWin(ProfileManager* manager)
564 : profile_manager_(manager) {
565 DCHECK_EQ(
566 arraysize(kProfileAvatarIconResources2x),
567 profile_manager_->GetProfileInfoCache().GetDefaultAvatarIconCount());
569 profile_manager_->GetProfileInfoCache().AddObserver(this);
572 ProfileShortcutManagerWin::~ProfileShortcutManagerWin() {
573 profile_manager_->GetProfileInfoCache().RemoveObserver(this);
576 void ProfileShortcutManagerWin::CreateProfileShortcut(
577 const base::FilePath& profile_path) {
578 CreateOrUpdateShortcutsForProfileAtPath(profile_path, CREATE_WHEN_NONE_FOUND,
579 IGNORE_NON_PROFILE_SHORTCUTS);
582 void ProfileShortcutManagerWin::RemoveProfileShortcuts(
583 const base::FilePath& profile_path) {
584 BrowserThread::PostTask(
585 BrowserThread::FILE, FROM_HERE,
586 base::Bind(&DeleteDesktopShortcutsAndIconFile, profile_path, false));
589 void ProfileShortcutManagerWin::HasProfileShortcuts(
590 const base::FilePath& profile_path,
591 const base::Callback<void(bool)>& callback) {
592 BrowserThread::PostTaskAndReplyWithResult(
593 BrowserThread::FILE, FROM_HERE,
594 base::Bind(&HasAnyProfileShortcuts, profile_path), callback);
597 void ProfileShortcutManagerWin::OnProfileAdded(
598 const base::FilePath& profile_path) {
599 const size_t profile_count =
600 profile_manager_->GetProfileInfoCache().GetNumberOfProfiles();
601 if (profile_count == 1) {
602 CreateOrUpdateShortcutsForProfileAtPath(profile_path,
603 CREATE_WHEN_NONE_FOUND,
604 UPDATE_NON_PROFILE_SHORTCUTS);
605 } else if (profile_count == 2) {
606 CreateOrUpdateShortcutsForProfileAtPath(GetOtherProfilePath(profile_path),
607 UPDATE_EXISTING_ONLY,
608 UPDATE_NON_PROFILE_SHORTCUTS);
612 void ProfileShortcutManagerWin::OnProfileWillBeRemoved(
613 const base::FilePath& profile_path) {
616 void ProfileShortcutManagerWin::OnProfileWasRemoved(
617 const base::FilePath& profile_path,
618 const string16& profile_name) {
619 const ProfileInfoCache& cache = profile_manager_->GetProfileInfoCache();
620 // If there is only one profile remaining, remove the badging information
621 // from an existing shortcut.
622 const bool deleting_down_to_last_profile = (cache.GetNumberOfProfiles() == 1);
623 if (deleting_down_to_last_profile) {
624 CreateOrUpdateShortcutsForProfileAtPath(cache.GetPathOfProfileAtIndex(0),
625 UPDATE_EXISTING_ONLY,
626 IGNORE_NON_PROFILE_SHORTCUTS);
629 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
630 base::Bind(&DeleteDesktopShortcutsAndIconFile,
631 profile_path,
632 deleting_down_to_last_profile));
635 void ProfileShortcutManagerWin::OnProfileNameChanged(
636 const base::FilePath& profile_path,
637 const string16& old_profile_name) {
638 CreateOrUpdateShortcutsForProfileAtPath(profile_path, UPDATE_EXISTING_ONLY,
639 IGNORE_NON_PROFILE_SHORTCUTS);
642 void ProfileShortcutManagerWin::OnProfileAvatarChanged(
643 const base::FilePath& profile_path) {
644 CreateOrUpdateShortcutsForProfileAtPath(profile_path, UPDATE_EXISTING_ONLY,
645 IGNORE_NON_PROFILE_SHORTCUTS);
648 base::FilePath ProfileShortcutManagerWin::GetOtherProfilePath(
649 const base::FilePath& profile_path) {
650 const ProfileInfoCache& cache = profile_manager_->GetProfileInfoCache();
651 DCHECK_EQ(2U, cache.GetNumberOfProfiles());
652 // Get the index of the current profile, in order to find the index of the
653 // other profile.
654 size_t current_profile_index = cache.GetIndexOfProfileWithPath(profile_path);
655 size_t other_profile_index = (current_profile_index == 0) ? 1 : 0;
656 return cache.GetPathOfProfileAtIndex(other_profile_index);
659 void ProfileShortcutManagerWin::CreateOrUpdateShortcutsForProfileAtPath(
660 const base::FilePath& profile_path,
661 CreateOrUpdateMode create_mode,
662 NonProfileShortcutAction action) {
663 ProfileInfoCache* cache = &profile_manager_->GetProfileInfoCache();
664 size_t profile_index = cache->GetIndexOfProfileWithPath(profile_path);
665 if (profile_index == std::string::npos)
666 return;
667 bool remove_badging = cache->GetNumberOfProfiles() == 1;
669 string16 old_shortcut_appended_name =
670 cache->GetShortcutNameOfProfileAtIndex(profile_index);
672 // Exit early if the mode is to update existing profile shortcuts only and
673 // none were ever created for this profile, per the shortcut name not being
674 // set in the profile info cache.
675 if (old_shortcut_appended_name.empty() &&
676 create_mode == UPDATE_EXISTING_ONLY &&
677 action == IGNORE_NON_PROFILE_SHORTCUTS) {
678 return;
681 string16 new_shortcut_appended_name;
682 if (!remove_badging)
683 new_shortcut_appended_name = cache->GetNameOfProfileAtIndex(profile_index);
685 SkBitmap avatar_bitmap_copy_1x;
686 SkBitmap avatar_bitmap_copy_2x;
687 if (!remove_badging) {
688 const size_t icon_index =
689 cache->GetAvatarIconIndexOfProfileAtIndex(profile_index);
690 const int resource_id_1x =
691 cache->GetDefaultAvatarIconResourceIDAtIndex(icon_index);
692 const int resource_id_2x = kProfileAvatarIconResources2x[icon_index];
693 // Make a copy of the SkBitmaps to ensure that we can safely use the image
694 // data on the FILE thread.
695 avatar_bitmap_copy_1x = GetImageResourceSkBitmapCopy(resource_id_1x);
696 avatar_bitmap_copy_2x = GetImageResourceSkBitmapCopy(resource_id_2x);
698 BrowserThread::PostTask(
699 BrowserThread::FILE, FROM_HERE,
700 base::Bind(&CreateOrUpdateDesktopShortcutsForProfile, profile_path,
701 old_shortcut_appended_name, new_shortcut_appended_name,
702 avatar_bitmap_copy_1x, avatar_bitmap_copy_2x, create_mode,
703 action));
705 cache->SetShortcutNameOfProfileAtIndex(profile_index,
706 new_shortcut_appended_name);