Chromium Code Reviews| 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/profiles/profile_shortcut_manager_win.h" | 5 #include "chrome/browser/profiles/profile_shortcut_manager_win.h" | 
| 6 | 6 | 
| 7 #include <shlobj.h> // For SHChangeNotify(). | 7 #include <shlobj.h> // For SHChangeNotify(). | 
| 8 | 8 | 
| 9 #include <string> | 9 #include <string> | 
| 10 #include <vector> | 10 #include <vector> | 
| (...skipping 27 matching lines...) Expand all Loading... | |
| 38 #include "ui/gfx/icon_util.h" | 38 #include "ui/gfx/icon_util.h" | 
| 39 #include "ui/gfx/image/image.h" | 39 #include "ui/gfx/image/image.h" | 
| 40 #include "ui/gfx/image/image_family.h" | 40 #include "ui/gfx/image/image_family.h" | 
| 41 #include "ui/gfx/rect.h" | 41 #include "ui/gfx/rect.h" | 
| 42 #include "ui/gfx/skia_util.h" | 42 #include "ui/gfx/skia_util.h" | 
| 43 | 43 | 
| 44 using content::BrowserThread; | 44 using content::BrowserThread; | 
| 45 | 45 | 
| 46 namespace { | 46 namespace { | 
| 47 | 47 | 
| 48 // Name of the badged icon file generated for a given profile. | |
| 49 const char kProfileIconFileName[] = "Google Profile.ico"; | |
| 50 | |
| 48 // Characters that are not allowed in Windows filenames. Taken from | 51 // Characters that are not allowed in Windows filenames. Taken from | 
| 49 // http://msdn.microsoft.com/en-us/library/aa365247.aspx | 52 // http://msdn.microsoft.com/en-us/library/aa365247.aspx | 
| 50 const char16 kReservedCharacters[] = L"<>:\"/\\|?*\x01\x02\x03\x04\x05\x06\x07" | 53 const char16 kReservedCharacters[] = L"<>:\"/\\|?*\x01\x02\x03\x04\x05\x06\x07" | 
| 51 L"\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19" | 54 L"\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19" | 
| 52 L"\x1A\x1B\x1C\x1D\x1E\x1F"; | 55 L"\x1A\x1B\x1C\x1D\x1E\x1F"; | 
| 53 | 56 | 
| 54 // The maximum number of characters allowed in profile shortcuts' file names. | 57 // The maximum number of characters allowed in profile shortcuts' file names. | 
| 55 // Warning: migration code will be needed if this is changed later, since | 58 // Warning: migration code will be needed if this is changed later, since | 
| 56 // existing shortcuts might no longer be found if the name is generated | 59 // existing shortcuts might no longer be found if the name is generated | 
| 57 // differently than it was when a shortcut was originally created. | 60 // differently than it was when a shortcut was originally created. | 
| (...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 133 const SkBitmap& badged_bitmap = | 136 const SkBitmap& badged_bitmap = | 
| 134 offscreen_canvas->getDevice()->accessBitmap(false); | 137 offscreen_canvas->getDevice()->accessBitmap(false); | 
| 135 SkBitmap badged_bitmap_copy; | 138 SkBitmap badged_bitmap_copy; | 
| 136 badged_bitmap.deepCopyTo(&badged_bitmap_copy, badged_bitmap.getConfig()); | 139 badged_bitmap.deepCopyTo(&badged_bitmap_copy, badged_bitmap.getConfig()); | 
| 137 return badged_bitmap_copy; | 140 return badged_bitmap_copy; | 
| 138 } | 141 } | 
| 139 | 142 | 
| 140 // Creates a desktop shortcut icon file (.ico) on the disk for a given profile, | 143 // Creates a desktop shortcut icon file (.ico) on the disk for a given profile, | 
| 141 // badging the browser distribution icon with the profile avatar. | 144 // badging the browser distribution icon with the profile avatar. | 
| 142 // Returns a path to the shortcut icon file on disk, which is empty if this | 145 // Returns a path to the shortcut icon file on disk, which is empty if this | 
| 143 // fails. Use index 0 when assigning the resulting file as the icon. | 146 // fails. Use index 0 when assigning the resulting file as the icon. If both | 
| 144 base::FilePath CreateChromeDesktopShortcutIconForProfile( | 147 // given bitmaps are empty, an unbadged icon is created. | 
| 148 // |is_create| should be true if we are creating the icon at a time when it | |
| 149 // is not possibly already in use. If it is false, we will refresh the Windows | |
| 150 // icon cache. | |
| 151 // TODO(calamity): Ideally we'd just copy the app icon verbatim from the exe's | |
| 152 // resources in the case of an unbadged icon. | |
| 153 base::FilePath CreateOrUpdateShortcutIconForProfile( | |
| 145 const base::FilePath& profile_path, | 154 const base::FilePath& profile_path, | 
| 146 const SkBitmap& avatar_bitmap_1x, | 155 const SkBitmap& avatar_bitmap_1x, | 
| 147 const SkBitmap& avatar_bitmap_2x) { | 156 const SkBitmap& avatar_bitmap_2x) { | 
| 148 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | 157 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | 
| 149 scoped_ptr<SkBitmap> app_icon_bitmap(GetAppIconForSize(kShortcutIconSize)); | 158 scoped_ptr<SkBitmap> app_icon_bitmap(GetAppIconForSize(kShortcutIconSize)); | 
| 150 if (!app_icon_bitmap) | 159 if (!app_icon_bitmap) | 
| 151 return base::FilePath(); | 160 return base::FilePath(); | 
| 152 | 161 | 
| 153 gfx::ImageFamily badged_bitmaps; | 162 gfx::ImageFamily badged_bitmaps; | 
| 154 badged_bitmaps.Add(gfx::Image::CreateFrom1xBitmap( | 163 if (!avatar_bitmap_1x.empty()) { | 
| 155 BadgeIcon(*app_icon_bitmap, avatar_bitmap_1x, 1))); | |
| 156 | |
| 157 app_icon_bitmap = GetAppIconForSize(IconUtil::kLargeIconSize); | |
| 158 if (app_icon_bitmap) { | |
| 159 badged_bitmaps.Add(gfx::Image::CreateFrom1xBitmap( | 164 badged_bitmaps.Add(gfx::Image::CreateFrom1xBitmap( | 
| 160 BadgeIcon(*app_icon_bitmap, avatar_bitmap_2x, 2))); | 165 BadgeIcon(*app_icon_bitmap, avatar_bitmap_1x, 1))); | 
| 161 } | 166 } | 
| 162 | 167 | 
| 168 scoped_ptr<SkBitmap> large_app_icon_bitmap( | |
| 169 GetAppIconForSize(IconUtil::kLargeIconSize)); | |
| 170 if (large_app_icon_bitmap && !avatar_bitmap_2x.empty()) { | |
| 171 badged_bitmaps.Add(gfx::Image::CreateFrom1xBitmap( | |
| 172 BadgeIcon(*large_app_icon_bitmap, avatar_bitmap_2x, 2))); | |
| 173 } | |
| 174 | |
| 175 // If we have no badged bitmaps, we should just use the default chrome icon. | |
| 176 if (badged_bitmaps.empty()) { | |
| 177 badged_bitmaps.Add(gfx::Image::CreateFrom1xBitmap(*app_icon_bitmap)); | |
| 178 if (large_app_icon_bitmap) { | |
| 179 badged_bitmaps.Add( | |
| 180 gfx::Image::CreateFrom1xBitmap(*large_app_icon_bitmap)); | |
| 181 } | |
| 182 } | |
| 163 // Finally, write the .ico file containing this new bitmap. | 183 // Finally, write the .ico file containing this new bitmap. | 
| 164 const base::FilePath icon_path = | 184 const base::FilePath icon_path = | 
| 165 profile_path.AppendASCII(profiles::internal::kProfileIconFileName); | 185 profiles::internal::GetProfileIconPath(profile_path); | 
| 166 if (!IconUtil::CreateIconFileFromImageFamily(badged_bitmaps, icon_path)) | 186 bool icon_existed = file_util::PathExists(icon_path); | 
| 
 
Alexei Svitkine (slow)
2013/05/31 13:48:38
Nit: had_icon, make it const
 
calamity
2013/06/04 01:24:57
Done.
 
 | |
| 187 | |
| 188 if (!IconUtil::CreateIconFileFromImageFamily(badged_bitmaps, icon_path)) { | |
| 189 NOTREACHED(); | |
| 167 return base::FilePath(); | 190 return base::FilePath(); | 
| 191 } | |
| 168 | 192 | 
| 193 if (icon_existed) { | |
| 194 // This invalidates the Windows icon cache and causes the icon changes to | |
| 195 // register with the taskbar and desktop. SHCNE_ASSOCCHANGED will cause a | |
| 196 // desktop flash and we would like to avoid that if possible. | |
| 197 SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST | SHCNF_FLUSHNOWAIT, | |
| 198 NULL, NULL); | |
| 199 } else { | |
| 200 SHChangeNotify(SHCNE_CREATE, SHCNF_PATH, icon_path.value().c_str(), NULL); | |
| 201 } | |
| 169 return icon_path; | 202 return icon_path; | 
| 170 } | 203 } | 
| 171 | 204 | 
| 172 // Gets the user and system directories for desktop shortcuts. Parameters may | 205 // Gets the user and system directories for desktop shortcuts. Parameters may | 
| 173 // be NULL if a directory type is not needed. Returns true on success. | 206 // be NULL if a directory type is not needed. Returns true on success. | 
| 174 bool GetDesktopShortcutsDirectories( | 207 bool GetDesktopShortcutsDirectories( | 
| 175 base::FilePath* user_shortcuts_directory, | 208 base::FilePath* user_shortcuts_directory, | 
| 176 base::FilePath* system_shortcuts_directory) { | 209 base::FilePath* system_shortcuts_directory) { | 
| 177 BrowserDistribution* distribution = BrowserDistribution::GetDistribution(); | 210 BrowserDistribution* distribution = BrowserDistribution::GetDistribution(); | 
| 178 if (user_shortcuts_directory && | 211 if (user_shortcuts_directory && | 
| (...skipping 114 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 293 if (file_util::PathExists(possible_new_system_shortcut)) | 326 if (file_util::PathExists(possible_new_system_shortcut)) | 
| 294 file_util::Delete(old_shortcut_path, false); | 327 file_util::Delete(old_shortcut_path, false); | 
| 295 else if (!RenameDesktopShortcut(old_shortcut_path, new_shortcut_path)) | 328 else if (!RenameDesktopShortcut(old_shortcut_path, new_shortcut_path)) | 
| 296 DLOG(ERROR) << "Could not rename Windows profile desktop shortcut."; | 329 DLOG(ERROR) << "Could not rename Windows profile desktop shortcut."; | 
| 297 } else { | 330 } else { | 
| 298 // If the shortcut does not exist, it may have been renamed by the user. In | 331 // If the shortcut does not exist, it may have been renamed by the user. In | 
| 299 // that case, its name should not be changed. | 332 // that case, its name should not be changed. | 
| 300 // It's also possible that a system-level shortcut exists instead - this | 333 // It's also possible that a system-level shortcut exists instead - this | 
| 301 // should only be the case for the original Chrome shortcut from an | 334 // should only be the case for the original Chrome shortcut from an | 
| 302 // installation. If that's the case, copy that one over - it will get its | 335 // installation. If that's the case, copy that one over - it will get its | 
| 303 // properties updated by |CreateOrUpdateDesktopShortcutsForProfile()|. | 336 // properties updated by | 
| 337 // |CreateOrUpdateDesktopShortcutsAndIconForProfile()|. | |
| 304 const base::FilePath possible_old_system_shortcut = | 338 const base::FilePath possible_old_system_shortcut = | 
| 305 system_shortcuts_directory.Append(old_shortcut_filename); | 339 system_shortcuts_directory.Append(old_shortcut_filename); | 
| 306 if (file_util::PathExists(possible_old_system_shortcut)) | 340 if (file_util::PathExists(possible_old_system_shortcut)) | 
| 307 file_util::CopyFile(possible_old_system_shortcut, new_shortcut_path); | 341 file_util::CopyFile(possible_old_system_shortcut, new_shortcut_path); | 
| 308 } | 342 } | 
| 309 } | 343 } | 
| 310 | 344 | 
| 345 struct CreateOrUpdateShortcutsParams { | |
| 346 CreateOrUpdateShortcutsParams() {} | |
| 347 ~CreateOrUpdateShortcutsParams() {} | |
| 348 | |
| 349 base::FilePath profile_path; | |
| 350 string16 old_profile_name; | |
| 351 string16 profile_name; | |
| 352 SkBitmap avatar_image_1x; | |
| 353 SkBitmap avatar_image_2x; | |
| 354 ProfileShortcutManagerWin::CreateOrUpdateMode create_mode; | |
| 355 ProfileShortcutManagerWin::NonProfileShortcutAction action; | |
| 356 }; | |
| 357 | |
| 311 // Updates all desktop shortcuts for the given profile to have the specified | 358 // Updates all desktop shortcuts for the given profile to have the specified | 
| 312 // parameters. If |create_mode| is CREATE_WHEN_NONE_FOUND, a new shortcut is | 359 // parameters. If |create_mode| is CREATE_WHEN_NONE_FOUND, a new shortcut is | 
| 313 // created if no existing ones were found. Whether non-profile shortcuts should | 360 // created if no existing ones were found. Whether non-profile shortcuts should | 
| 314 // be updated is specified by |action|. Must be called on the FILE thread. | 361 // be updated is specified by |action|. Must be called on the FILE thread. | 
| 315 void CreateOrUpdateDesktopShortcutsForProfile( | 362 void CreateOrUpdateDesktopShortcutsAndIconForProfile( | 
| 316 const base::FilePath& profile_path, | 363 const CreateOrUpdateShortcutsParams& params) { | 
| 317 const string16& old_profile_name, | |
| 318 const string16& profile_name, | |
| 319 const SkBitmap& avatar_image_1x, | |
| 320 const SkBitmap& avatar_image_2x, | |
| 321 ProfileShortcutManagerWin::CreateOrUpdateMode create_mode, | |
| 322 ProfileShortcutManagerWin::NonProfileShortcutAction action) { | |
| 323 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | 364 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | 
| 324 | 365 | 
| 366 base::FilePath shortcut_icon = | |
| 367 CreateOrUpdateShortcutIconForProfile(params.profile_path, | |
| 368 params.avatar_image_1x, | |
| 369 params.avatar_image_2x); | |
| 370 if (shortcut_icon.empty()) { | |
| 371 NOTREACHED(); | |
| 372 return; | |
| 373 } | |
| 374 if (params.create_mode == ProfileShortcutManagerWin::CREATE_ICON_ONLY) | |
| 375 return; | |
| 376 | |
| 325 base::FilePath chrome_exe; | 377 base::FilePath chrome_exe; | 
| 326 if (!PathService::Get(base::FILE_EXE, &chrome_exe)) { | 378 if (!PathService::Get(base::FILE_EXE, &chrome_exe)) { | 
| 327 NOTREACHED(); | 379 NOTREACHED(); | 
| 328 return; | 380 return; | 
| 329 } | 381 } | 
| 330 | 382 | 
| 331 BrowserDistribution* distribution = BrowserDistribution::GetDistribution(); | 383 BrowserDistribution* distribution = BrowserDistribution::GetDistribution(); | 
| 332 // Ensure that the distribution supports creating shortcuts. If it doesn't, | 384 // Ensure that the distribution supports creating shortcuts. If it doesn't, | 
| 333 // the following code may result in NOTREACHED() being hit. | 385 // the following code may result in NOTREACHED() being hit. | 
| 334 DCHECK(distribution->CanCreateDesktopShortcuts()); | 386 DCHECK(distribution->CanCreateDesktopShortcuts()); | 
| 335 | 387 | 
| 336 if (old_profile_name != profile_name) { | 388 if (params.old_profile_name != params.profile_name) { | 
| 337 const string16 old_shortcut_filename = | 389 const string16 old_shortcut_filename = | 
| 338 profiles::internal::GetShortcutFilenameForProfile(old_profile_name, | 390 profiles::internal::GetShortcutFilenameForProfile( | 
| 339 distribution); | 391 params.old_profile_name, | 
| 392 distribution); | |
| 340 const string16 new_shortcut_filename = | 393 const string16 new_shortcut_filename = | 
| 341 profiles::internal::GetShortcutFilenameForProfile(profile_name, | 394 profiles::internal::GetShortcutFilenameForProfile(params.profile_name, | 
| 342 distribution); | 395 distribution); | 
| 343 RenameChromeDesktopShortcutForProfile(old_shortcut_filename, | 396 RenameChromeDesktopShortcutForProfile(old_shortcut_filename, | 
| 344 new_shortcut_filename); | 397 new_shortcut_filename); | 
| 345 } | 398 } | 
| 346 | 399 | 
| 347 ShellUtil::ShortcutProperties properties(ShellUtil::CURRENT_USER); | 400 ShellUtil::ShortcutProperties properties(ShellUtil::CURRENT_USER); | 
| 348 installer::Product product(distribution); | 401 installer::Product product(distribution); | 
| 349 product.AddDefaultShortcutProperties(chrome_exe, &properties); | 402 product.AddDefaultShortcutProperties(chrome_exe, &properties); | 
| 350 | 403 | 
| 351 const string16 command_line = | 404 const string16 command_line = | 
| 352 profiles::internal::CreateProfileShortcutFlags(profile_path); | 405 profiles::internal::CreateProfileShortcutFlags(params.profile_path); | 
| 353 | 406 | 
| 354 // Only set the profile-specific properties when |profile_name| is non empty. | 407 // Only set the profile-specific properties when |profile_name| is non empty. | 
| 355 // If it is empty, it means the shortcut being created should be a regular, | 408 // If it is empty, it means the shortcut being created should be a regular, | 
| 356 // non-profile Chrome shortcut. | 409 // non-profile Chrome shortcut. | 
| 357 if (!profile_name.empty()) { | 410 if (!params.profile_name.empty()) { | 
| 358 const base::FilePath shortcut_icon = | |
| 359 CreateChromeDesktopShortcutIconForProfile(profile_path, | |
| 360 avatar_image_1x, | |
| 361 avatar_image_2x); | |
| 362 if (!shortcut_icon.empty()) | 411 if (!shortcut_icon.empty()) | 
| 363 properties.set_icon(shortcut_icon, 0); | 412 properties.set_icon(shortcut_icon, 0); | 
| 364 properties.set_arguments(command_line); | 413 properties.set_arguments(command_line); | 
| 365 } else { | 414 } else { | 
| 366 // Set the arguments explicitly to the empty string to ensure that | 415 // Set the arguments explicitly to the empty string to ensure that | 
| 367 // |ShellUtil::CreateOrUpdateShortcut| updates that part of the shortcut. | 416 // |ShellUtil::CreateOrUpdateShortcut| updates that part of the shortcut. | 
| 368 properties.set_arguments(string16()); | 417 properties.set_arguments(string16()); | 
| 369 } | 418 } | 
| 370 | 419 | 
| 371 properties.set_app_id( | 420 properties.set_app_id( | 
| 372 ShellIntegration::GetChromiumModelIdForProfile(profile_path)); | 421 ShellIntegration::GetChromiumModelIdForProfile(params.profile_path)); | 
| 373 | 422 | 
| 374 ShellUtil::ShortcutOperation operation = | 423 ShellUtil::ShortcutOperation operation = | 
| 375 ShellUtil::SHELL_SHORTCUT_REPLACE_EXISTING; | 424 ShellUtil::SHELL_SHORTCUT_REPLACE_EXISTING; | 
| 376 | 425 | 
| 377 std::vector<base::FilePath> shortcuts; | 426 std::vector<base::FilePath> shortcuts; | 
| 378 ListDesktopShortcutsWithCommandLine(chrome_exe, command_line, | 427 ListDesktopShortcutsWithCommandLine(chrome_exe, command_line, | 
| 379 action == ProfileShortcutManagerWin::UPDATE_NON_PROFILE_SHORTCUTS, | 428 params.action == ProfileShortcutManagerWin::UPDATE_NON_PROFILE_SHORTCUTS, | 
| 380 &shortcuts); | 429 &shortcuts); | 
| 381 if (create_mode == ProfileShortcutManagerWin::CREATE_WHEN_NONE_FOUND && | 430 if (params.create_mode == ProfileShortcutManagerWin::CREATE_WHEN_NONE_FOUND && | 
| 382 shortcuts.empty()) { | 431 shortcuts.empty()) { | 
| 383 const string16 shortcut_name = | 432 const string16 shortcut_name = | 
| 384 profiles::internal::GetShortcutFilenameForProfile(profile_name, | 433 profiles::internal::GetShortcutFilenameForProfile(params.profile_name, | 
| 385 distribution); | 434 distribution); | 
| 386 shortcuts.push_back(base::FilePath(shortcut_name)); | 435 shortcuts.push_back(base::FilePath(shortcut_name)); | 
| 387 operation = ShellUtil::SHELL_SHORTCUT_CREATE_IF_NO_SYSTEM_LEVEL; | 436 operation = ShellUtil::SHELL_SHORTCUT_CREATE_IF_NO_SYSTEM_LEVEL; | 
| 388 } | 437 } | 
| 389 | 438 | 
| 390 for (size_t i = 0; i < shortcuts.size(); ++i) { | 439 for (size_t i = 0; i < shortcuts.size(); ++i) { | 
| 391 const base::FilePath shortcut_name = | 440 const base::FilePath shortcut_name = | 
| 392 shortcuts[i].BaseName().RemoveExtension(); | 441 shortcuts[i].BaseName().RemoveExtension(); | 
| 393 properties.set_shortcut_name(shortcut_name.value()); | 442 properties.set_shortcut_name(shortcut_name.value()); | 
| 394 ShellUtil::CreateOrUpdateShortcut(ShellUtil::SHORTCUT_LOCATION_DESKTOP, | 443 ShellUtil::CreateOrUpdateShortcut(ShellUtil::SHORTCUT_LOCATION_DESKTOP, | 
| (...skipping 12 matching lines...) Expand all Loading... | |
| 407 file_util::FileEnumerator::FILES); | 456 file_util::FileEnumerator::FILES); | 
| 408 for (base::FilePath path = enumerator.Next(); !path.empty(); | 457 for (base::FilePath path = enumerator.Next(); !path.empty(); | 
| 409 path = enumerator.Next()) { | 458 path = enumerator.Next()) { | 
| 410 if (IsChromeShortcut(path, chrome_exe, NULL)) | 459 if (IsChromeShortcut(path, chrome_exe, NULL)) | 
| 411 return true; | 460 return true; | 
| 412 } | 461 } | 
| 413 | 462 | 
| 414 return false; | 463 return false; | 
| 415 } | 464 } | 
| 416 | 465 | 
| 417 // Deletes all desktop shortcuts for the specified profile and also removes the | 466 // Deletes all desktop shortcuts for the specified profile. If | 
| 418 // corresponding icon file. If |ensure_shortcuts_remain| is true, then a regular | 467 // |ensure_shortcuts_remain| is true, then a regular non-profile shortcut will | 
| 419 // non-profile shortcut will be created if this function would otherwise delete | 468 // be created if this function would otherwise delete the last Chrome desktop | 
| 420 // the last Chrome desktop shortcut(s). Must be called on the FILE thread. | 469 // shortcut(s). Must be called on the FILE thread. | 
| 421 void DeleteDesktopShortcutsAndIconFile(const base::FilePath& profile_path, | 470 void DeleteDesktopShortcuts(const base::FilePath& profile_path, | 
| 422 bool ensure_shortcuts_remain) { | 471 bool ensure_shortcuts_remain) { | 
| 423 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | 472 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | 
| 424 | 473 | 
| 425 base::FilePath chrome_exe; | 474 base::FilePath chrome_exe; | 
| 426 if (!PathService::Get(base::FILE_EXE, &chrome_exe)) { | 475 if (!PathService::Get(base::FILE_EXE, &chrome_exe)) { | 
| 427 NOTREACHED(); | 476 NOTREACHED(); | 
| 428 return; | 477 return; | 
| 429 } | 478 } | 
| 430 | 479 | 
| 431 const string16 command_line = | 480 const string16 command_line = | 
| 432 profiles::internal::CreateProfileShortcutFlags(profile_path); | 481 profiles::internal::CreateProfileShortcutFlags(profile_path); | 
| 433 std::vector<base::FilePath> shortcuts; | 482 std::vector<base::FilePath> shortcuts; | 
| 434 ListDesktopShortcutsWithCommandLine(chrome_exe, command_line, false, | 483 ListDesktopShortcutsWithCommandLine(chrome_exe, command_line, false, | 
| 435 &shortcuts); | 484 &shortcuts); | 
| 436 | 485 | 
| 437 BrowserDistribution* distribution = BrowserDistribution::GetDistribution(); | 486 BrowserDistribution* distribution = BrowserDistribution::GetDistribution(); | 
| 438 for (size_t i = 0; i < shortcuts.size(); ++i) { | 487 for (size_t i = 0; i < shortcuts.size(); ++i) { | 
| 439 // Use file_util::Delete() instead of ShellUtil::RemoveShortcut(), as the | 488 // Use file_util::Delete() instead of ShellUtil::RemoveShortcut(), as the | 
| 440 // latter causes non-profile taskbar shortcuts to be unpinned. | 489 // latter causes non-profile taskbar shortcuts to be unpinned. | 
| 441 file_util::Delete(shortcuts[i], false); | 490 file_util::Delete(shortcuts[i], false); | 
| 442 // Notify the shell that the shortcut was deleted to ensure desktop refresh. | 491 // Notify the shell that the shortcut was deleted to ensure desktop refresh. | 
| 443 SHChangeNotify(SHCNE_DELETE, SHCNF_PATH, shortcuts[i].value().c_str(), | 492 SHChangeNotify(SHCNE_DELETE, SHCNF_PATH, shortcuts[i].value().c_str(), | 
| 444 NULL); | 493 NULL); | 
| 445 } | 494 } | 
| 446 | 495 | 
| 447 const base::FilePath icon_path = | |
| 448 profile_path.AppendASCII(profiles::internal::kProfileIconFileName); | |
| 449 file_util::Delete(icon_path, false); | |
| 450 | |
| 451 // If |ensure_shortcuts_remain| is true and deleting this profile caused the | 496 // If |ensure_shortcuts_remain| is true and deleting this profile caused the | 
| 452 // last shortcuts to be removed, re-create a regular non-profile shortcut. | 497 // last shortcuts to be removed, re-create a regular non-profile shortcut. | 
| 453 const bool had_shortcuts = !shortcuts.empty(); | 498 const bool had_shortcuts = !shortcuts.empty(); | 
| 454 if (ensure_shortcuts_remain && had_shortcuts && | 499 if (ensure_shortcuts_remain && had_shortcuts && | 
| 455 !ChromeDesktopShortcutsExist(chrome_exe)) { | 500 !ChromeDesktopShortcutsExist(chrome_exe)) { | 
| 456 BrowserDistribution* distribution = BrowserDistribution::GetDistribution(); | 501 BrowserDistribution* distribution = BrowserDistribution::GetDistribution(); | 
| 457 // Ensure that the distribution supports creating shortcuts. If it doesn't, | 502 // Ensure that the distribution supports creating shortcuts. If it doesn't, | 
| 458 // the following code may result in NOTREACHED() being hit. | 503 // the following code may result in NOTREACHED() being hit. | 
| 459 DCHECK(distribution->CanCreateDesktopShortcuts()); | 504 DCHECK(distribution->CanCreateDesktopShortcuts()); | 
| 460 installer::Product product(distribution); | 505 installer::Product product(distribution); | 
| (...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 522 SkBitmap bitmap_copy; | 567 SkBitmap bitmap_copy; | 
| 523 image_bitmap->deepCopyTo(&bitmap_copy, image_bitmap->getConfig()); | 568 image_bitmap->deepCopyTo(&bitmap_copy, image_bitmap->getConfig()); | 
| 524 return bitmap_copy; | 569 return bitmap_copy; | 
| 525 } | 570 } | 
| 526 | 571 | 
| 527 } // namespace | 572 } // namespace | 
| 528 | 573 | 
| 529 namespace profiles { | 574 namespace profiles { | 
| 530 namespace internal { | 575 namespace internal { | 
| 531 | 576 | 
| 532 const char kProfileIconFileName[] = "Google Profile.ico"; | 577 base::FilePath GetProfileIconPath(const base::FilePath& profile_path) { | 
| 578 return profile_path.AppendASCII(kProfileIconFileName); | |
| 579 } | |
| 533 | 580 | 
| 534 string16 GetShortcutFilenameForProfile(const string16& profile_name, | 581 string16 GetShortcutFilenameForProfile(const string16& profile_name, | 
| 535 BrowserDistribution* distribution) { | 582 BrowserDistribution* distribution) { | 
| 536 string16 shortcut_name; | 583 string16 shortcut_name; | 
| 537 if (!profile_name.empty()) { | 584 if (!profile_name.empty()) { | 
| 538 shortcut_name.append(SanitizeShortcutProfileNameString(profile_name)); | 585 shortcut_name.append(SanitizeShortcutProfileNameString(profile_name)); | 
| 539 shortcut_name.append(L" - "); | 586 shortcut_name.append(L" - "); | 
| 540 shortcut_name.append(l10n_util::GetStringUTF16(IDS_SHORT_PRODUCT_NAME)); | 587 shortcut_name.append(l10n_util::GetStringUTF16(IDS_SHORT_PRODUCT_NAME)); | 
| 541 } else { | 588 } else { | 
| 542 shortcut_name.append(distribution->GetAppShortCutName()); | 589 shortcut_name.append(distribution->GetAppShortCutName()); | 
| 543 } | 590 } | 
| 544 return shortcut_name + installer::kLnkExt; | 591 return shortcut_name + installer::kLnkExt; | 
| 545 } | 592 } | 
| 546 | 593 | 
| 547 string16 CreateProfileShortcutFlags(const base::FilePath& profile_path) { | 594 string16 CreateProfileShortcutFlags(const base::FilePath& profile_path) { | 
| 548 return base::StringPrintf(L"--%ls=\"%ls\"", | 595 return base::StringPrintf(L"--%ls=\"%ls\"", | 
| 549 ASCIIToUTF16(switches::kProfileDirectory).c_str(), | 596 ASCIIToUTF16(switches::kProfileDirectory).c_str(), | 
| 550 profile_path.BaseName().value().c_str()); | 597 profile_path.BaseName().value().c_str()); | 
| 551 } | 598 } | 
| 552 | 599 | 
| 553 } // namespace internal | 600 } // namespace internal | 
| 554 } // namespace profiles | 601 } // namespace profiles | 
| 555 | 602 | 
| 556 // static | 603 // static | 
| 557 bool ProfileShortcutManager::IsFeatureEnabled() { | 604 bool ProfileShortcutManager::IsFeatureEnabled() { | 
| 558 return BrowserDistribution::GetDistribution()->CanCreateDesktopShortcuts() && | 605 return BrowserDistribution::GetDistribution()->CanCreateDesktopShortcuts() && | 
| 559 !CommandLine::ForCurrentProcess()->HasSwitch(switches::kUserDataDir) && | 606 !CommandLine::ForCurrentProcess()->HasSwitch(switches::kUserDataDir); | 
| 560 !CommandLine::ForCurrentProcess()->HasSwitch(switches::kShowAppList); | |
| 561 } | 607 } | 
| 562 | 608 | 
| 563 // static | 609 // static | 
| 564 ProfileShortcutManager* ProfileShortcutManager::Create( | 610 ProfileShortcutManager* ProfileShortcutManager::Create( | 
| 565 ProfileManager* manager) { | 611 ProfileManager* manager) { | 
| 566 return new ProfileShortcutManagerWin(manager); | 612 return new ProfileShortcutManagerWin(manager); | 
| 567 } | 613 } | 
| 568 | 614 | 
| 569 ProfileShortcutManagerWin::ProfileShortcutManagerWin(ProfileManager* manager) | 615 ProfileShortcutManagerWin::ProfileShortcutManagerWin(ProfileManager* manager) | 
| 570 : profile_manager_(manager) { | 616 : profile_manager_(manager) { | 
| 571 DCHECK_EQ( | 617 DCHECK_EQ( | 
| 572 arraysize(kProfileAvatarIconResources2x), | 618 arraysize(kProfileAvatarIconResources2x), | 
| 573 profile_manager_->GetProfileInfoCache().GetDefaultAvatarIconCount()); | 619 profile_manager_->GetProfileInfoCache().GetDefaultAvatarIconCount()); | 
| 574 | 620 | 
| 575 profile_manager_->GetProfileInfoCache().AddObserver(this); | 621 profile_manager_->GetProfileInfoCache().AddObserver(this); | 
| 576 } | 622 } | 
| 577 | 623 | 
| 578 ProfileShortcutManagerWin::~ProfileShortcutManagerWin() { | 624 ProfileShortcutManagerWin::~ProfileShortcutManagerWin() { | 
| 579 profile_manager_->GetProfileInfoCache().RemoveObserver(this); | 625 profile_manager_->GetProfileInfoCache().RemoveObserver(this); | 
| 580 } | 626 } | 
| 581 | 627 | 
| 628 void ProfileShortcutManagerWin::CreateProfileIcon( | |
| 629 const base::FilePath& profile_path) { | |
| 630 CreateOrUpdateShortcutsForProfileAtPath(profile_path, CREATE_ICON_ONLY, | |
| 631 IGNORE_NON_PROFILE_SHORTCUTS); | |
| 632 } | |
| 633 | |
| 634 void ProfileShortcutManagerWin::UpdateProfileIcon( | |
| 635 const base::FilePath& profile_path) { | |
| 636 CreateOrUpdateShortcutsForProfileAtPath(profile_path, CREATE_ICON_ONLY, | |
| 637 IGNORE_NON_PROFILE_SHORTCUTS); | |
| 638 } | |
| 639 | |
| 582 void ProfileShortcutManagerWin::CreateProfileShortcut( | 640 void ProfileShortcutManagerWin::CreateProfileShortcut( | 
| 583 const base::FilePath& profile_path) { | 641 const base::FilePath& profile_path) { | 
| 584 CreateOrUpdateShortcutsForProfileAtPath(profile_path, CREATE_WHEN_NONE_FOUND, | 642 CreateOrUpdateShortcutsForProfileAtPath(profile_path, CREATE_WHEN_NONE_FOUND, | 
| 585 IGNORE_NON_PROFILE_SHORTCUTS); | 643 IGNORE_NON_PROFILE_SHORTCUTS); | 
| 586 } | 644 } | 
| 587 | 645 | 
| 588 void ProfileShortcutManagerWin::RemoveProfileShortcuts( | 646 void ProfileShortcutManagerWin::RemoveProfileShortcuts( | 
| 589 const base::FilePath& profile_path) { | 647 const base::FilePath& profile_path) { | 
| 590 BrowserThread::PostTask( | 648 BrowserThread::PostTask( | 
| 591 BrowserThread::FILE, FROM_HERE, | 649 BrowserThread::FILE, FROM_HERE, | 
| 592 base::Bind(&DeleteDesktopShortcutsAndIconFile, profile_path, false)); | 650 base::Bind(&DeleteDesktopShortcuts, profile_path, false)); | 
| 593 } | 651 } | 
| 594 | 652 | 
| 595 void ProfileShortcutManagerWin::HasProfileShortcuts( | 653 void ProfileShortcutManagerWin::HasProfileShortcuts( | 
| 596 const base::FilePath& profile_path, | 654 const base::FilePath& profile_path, | 
| 597 const base::Callback<void(bool)>& callback) { | 655 const base::Callback<void(bool)>& callback) { | 
| 598 BrowserThread::PostTaskAndReplyWithResult( | 656 BrowserThread::PostTaskAndReplyWithResult( | 
| 599 BrowserThread::FILE, FROM_HERE, | 657 BrowserThread::FILE, FROM_HERE, | 
| 600 base::Bind(&HasAnyProfileShortcuts, profile_path), callback); | 658 base::Bind(&HasAnyProfileShortcuts, profile_path), callback); | 
| 601 } | 659 } | 
| 602 | 660 | 
| 603 void ProfileShortcutManagerWin::OnProfileAdded( | 661 void ProfileShortcutManagerWin::OnProfileAdded( | 
| 604 const base::FilePath& profile_path) { | 662 const base::FilePath& profile_path) { | 
| 663 CreateProfileIcon(profile_path); | |
| 605 if (profile_manager_->GetProfileInfoCache().GetNumberOfProfiles() == 2) { | 664 if (profile_manager_->GetProfileInfoCache().GetNumberOfProfiles() == 2) { | 
| 606 // When the second profile is added, make existing non-profile shortcuts | 665 // When the second profile is added, make existing non-profile shortcuts | 
| 607 // point to the first profile and be badged/named appropriately. | 666 // point to the first profile and be badged/named appropriately. | 
| 608 CreateOrUpdateShortcutsForProfileAtPath(GetOtherProfilePath(profile_path), | 667 CreateOrUpdateShortcutsForProfileAtPath(GetOtherProfilePath(profile_path), | 
| 609 UPDATE_EXISTING_ONLY, | 668 UPDATE_EXISTING_ONLY, | 
| 610 UPDATE_NON_PROFILE_SHORTCUTS); | 669 UPDATE_NON_PROFILE_SHORTCUTS); | 
| 611 } | 670 } | 
| 612 } | 671 } | 
| 613 | 672 | 
| 614 void ProfileShortcutManagerWin::OnProfileWillBeRemoved( | 673 void ProfileShortcutManagerWin::OnProfileWillBeRemoved( | 
| 615 const base::FilePath& profile_path) { | 674 const base::FilePath& profile_path) { | 
| 616 } | 675 } | 
| 617 | 676 | 
| 618 void ProfileShortcutManagerWin::OnProfileWasRemoved( | 677 void ProfileShortcutManagerWin::OnProfileWasRemoved( | 
| 619 const base::FilePath& profile_path, | 678 const base::FilePath& profile_path, | 
| 620 const string16& profile_name) { | 679 const string16& profile_name) { | 
| 621 const ProfileInfoCache& cache = profile_manager_->GetProfileInfoCache(); | 680 const ProfileInfoCache& cache = profile_manager_->GetProfileInfoCache(); | 
| 622 // If there is only one profile remaining, remove the badging information | 681 // If there is only one profile remaining, remove the badging information | 
| 623 // from an existing shortcut. | 682 // from an existing shortcut. | 
| 624 const bool deleting_down_to_last_profile = (cache.GetNumberOfProfiles() == 1); | 683 const bool deleting_down_to_last_profile = (cache.GetNumberOfProfiles() == 1); | 
| 625 if (deleting_down_to_last_profile) { | 684 if (deleting_down_to_last_profile) { | 
| 685 // This is needed to unbadge the icon. | |
| 626 CreateOrUpdateShortcutsForProfileAtPath(cache.GetPathOfProfileAtIndex(0), | 686 CreateOrUpdateShortcutsForProfileAtPath(cache.GetPathOfProfileAtIndex(0), | 
| 627 UPDATE_EXISTING_ONLY, | 687 UPDATE_EXISTING_ONLY, | 
| 628 IGNORE_NON_PROFILE_SHORTCUTS); | 688 IGNORE_NON_PROFILE_SHORTCUTS); | 
| 629 } | 689 } | 
| 630 | 690 | 
| 631 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, | 691 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, | 
| 632 base::Bind(&DeleteDesktopShortcutsAndIconFile, | 692 base::Bind(&DeleteDesktopShortcuts, | 
| 633 profile_path, | 693 profile_path, | 
| 634 deleting_down_to_last_profile)); | 694 deleting_down_to_last_profile)); | 
| 635 } | 695 } | 
| 636 | 696 | 
| 637 void ProfileShortcutManagerWin::OnProfileNameChanged( | 697 void ProfileShortcutManagerWin::OnProfileNameChanged( | 
| 638 const base::FilePath& profile_path, | 698 const base::FilePath& profile_path, | 
| 639 const string16& old_profile_name) { | 699 const string16& old_profile_name) { | 
| 640 CreateOrUpdateShortcutsForProfileAtPath(profile_path, UPDATE_EXISTING_ONLY, | 700 CreateOrUpdateShortcutsForProfileAtPath(profile_path, UPDATE_EXISTING_ONLY, | 
| 641 IGNORE_NON_PROFILE_SHORTCUTS); | 701 IGNORE_NON_PROFILE_SHORTCUTS); | 
| 642 } | 702 } | 
| 643 | 703 | 
| 644 void ProfileShortcutManagerWin::OnProfileAvatarChanged( | 704 void ProfileShortcutManagerWin::OnProfileAvatarChanged( | 
| 645 const base::FilePath& profile_path) { | 705 const base::FilePath& profile_path) { | 
| 646 CreateOrUpdateShortcutsForProfileAtPath(profile_path, UPDATE_EXISTING_ONLY, | 706 UpdateProfileIcon(profile_path); | 
| 647 IGNORE_NON_PROFILE_SHORTCUTS); | |
| 648 } | 707 } | 
| 649 | 708 | 
| 650 base::FilePath ProfileShortcutManagerWin::GetOtherProfilePath( | 709 base::FilePath ProfileShortcutManagerWin::GetOtherProfilePath( | 
| 651 const base::FilePath& profile_path) { | 710 const base::FilePath& profile_path) { | 
| 652 const ProfileInfoCache& cache = profile_manager_->GetProfileInfoCache(); | 711 const ProfileInfoCache& cache = profile_manager_->GetProfileInfoCache(); | 
| 653 DCHECK_EQ(2U, cache.GetNumberOfProfiles()); | 712 DCHECK_EQ(2U, cache.GetNumberOfProfiles()); | 
| 654 // Get the index of the current profile, in order to find the index of the | 713 // Get the index of the current profile, in order to find the index of the | 
| 655 // other profile. | 714 // other profile. | 
| 656 size_t current_profile_index = cache.GetIndexOfProfileWithPath(profile_path); | 715 size_t current_profile_index = cache.GetIndexOfProfileWithPath(profile_path); | 
| 657 size_t other_profile_index = (current_profile_index == 0) ? 1 : 0; | 716 size_t other_profile_index = (current_profile_index == 0) ? 1 : 0; | 
| 658 return cache.GetPathOfProfileAtIndex(other_profile_index); | 717 return cache.GetPathOfProfileAtIndex(other_profile_index); | 
| 659 } | 718 } | 
| 660 | 719 | 
| 661 void ProfileShortcutManagerWin::CreateOrUpdateShortcutsForProfileAtPath( | 720 void ProfileShortcutManagerWin::CreateOrUpdateShortcutsForProfileAtPath( | 
| 662 const base::FilePath& profile_path, | 721 const base::FilePath& profile_path, | 
| 663 CreateOrUpdateMode create_mode, | 722 CreateOrUpdateMode create_mode, | 
| 664 NonProfileShortcutAction action) { | 723 NonProfileShortcutAction action) { | 
| 724 CreateOrUpdateShortcutsParams params; | |
| 725 params.profile_path = profile_path; | |
| 726 params.create_mode = create_mode; | |
| 727 params.action = action; | |
| 728 | |
| 665 ProfileInfoCache* cache = &profile_manager_->GetProfileInfoCache(); | 729 ProfileInfoCache* cache = &profile_manager_->GetProfileInfoCache(); | 
| 666 size_t profile_index = cache->GetIndexOfProfileWithPath(profile_path); | 730 size_t profile_index = cache->GetIndexOfProfileWithPath(profile_path); | 
| 667 if (profile_index == std::string::npos) | 731 if (profile_index == std::string::npos) | 
| 668 return; | 732 return; | 
| 669 bool remove_badging = cache->GetNumberOfProfiles() == 1; | 733 bool remove_badging = cache->GetNumberOfProfiles() == 1; | 
| 670 | 734 | 
| 671 string16 old_shortcut_appended_name = | 735 params.old_profile_name = | 
| 672 cache->GetShortcutNameOfProfileAtIndex(profile_index); | 736 cache->GetShortcutNameOfProfileAtIndex(profile_index); | 
| 673 | 737 | 
| 674 // Exit early if the mode is to update existing profile shortcuts only and | 738 // Exit early if the mode is to update existing profile shortcuts only and | 
| 675 // none were ever created for this profile, per the shortcut name not being | 739 // none were ever created for this profile, per the shortcut name not being | 
| 676 // set in the profile info cache. | 740 // set in the profile info cache. | 
| 677 if (old_shortcut_appended_name.empty() && | 741 if (params.old_profile_name.empty() && | 
| 678 create_mode == UPDATE_EXISTING_ONLY && | 742 create_mode == UPDATE_EXISTING_ONLY && | 
| 679 action == IGNORE_NON_PROFILE_SHORTCUTS) { | 743 action == IGNORE_NON_PROFILE_SHORTCUTS) { | 
| 680 return; | 744 return; | 
| 681 } | 745 } | 
| 682 | 746 | 
| 683 string16 new_shortcut_appended_name; | |
| 684 if (!remove_badging) | 747 if (!remove_badging) | 
| 685 new_shortcut_appended_name = cache->GetNameOfProfileAtIndex(profile_index); | 748 params.profile_name = cache->GetNameOfProfileAtIndex(profile_index); | 
| 686 | 749 | 
| 687 SkBitmap avatar_bitmap_copy_1x; | |
| 688 SkBitmap avatar_bitmap_copy_2x; | |
| 689 if (!remove_badging) { | 750 if (!remove_badging) { | 
| 690 const size_t icon_index = | 751 const size_t icon_index = | 
| 691 cache->GetAvatarIconIndexOfProfileAtIndex(profile_index); | 752 cache->GetAvatarIconIndexOfProfileAtIndex(profile_index); | 
| 692 const int resource_id_1x = | 753 const int resource_id_1x = | 
| 693 cache->GetDefaultAvatarIconResourceIDAtIndex(icon_index); | 754 cache->GetDefaultAvatarIconResourceIDAtIndex(icon_index); | 
| 694 const int resource_id_2x = kProfileAvatarIconResources2x[icon_index]; | 755 const int resource_id_2x = kProfileAvatarIconResources2x[icon_index]; | 
| 695 // Make a copy of the SkBitmaps to ensure that we can safely use the image | 756 // Make a copy of the SkBitmaps to ensure that we can safely use the image | 
| 696 // data on the FILE thread. | 757 // data on the FILE thread. | 
| 697 avatar_bitmap_copy_1x = GetImageResourceSkBitmapCopy(resource_id_1x); | 758 params.avatar_image_1x = GetImageResourceSkBitmapCopy(resource_id_1x); | 
| 698 avatar_bitmap_copy_2x = GetImageResourceSkBitmapCopy(resource_id_2x); | 759 params.avatar_image_2x = GetImageResourceSkBitmapCopy(resource_id_2x); | 
| 699 } | 760 } | 
| 700 BrowserThread::PostTask( | 761 BrowserThread::PostTask( | 
| 701 BrowserThread::FILE, FROM_HERE, | 762 BrowserThread::FILE, FROM_HERE, | 
| 702 base::Bind(&CreateOrUpdateDesktopShortcutsForProfile, profile_path, | 763 base::Bind(&CreateOrUpdateDesktopShortcutsAndIconForProfile, params)); | 
| 703 old_shortcut_appended_name, new_shortcut_appended_name, | |
| 704 avatar_bitmap_copy_1x, avatar_bitmap_copy_2x, create_mode, | |
| 705 action)); | |
| 706 | 764 | 
| 707 cache->SetShortcutNameOfProfileAtIndex(profile_index, | 765 cache->SetShortcutNameOfProfileAtIndex(profile_index, | 
| 708 new_shortcut_appended_name); | 766 params.profile_name); | 
| 709 } | 767 } | 
| OLD | NEW |