profile_shortcut_manager_win.cc revision 5821806d5e7f356e8fa4b058a389a808ea183019
15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/profiles/profile_shortcut_manager.h"
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <map>
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/bind.h"
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/command_line.h"
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/file_util.h"
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/path_service.h"
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/string16.h"
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/stringprintf.h"
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/utf_string_conversions.h"
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/app_icon_win.h"
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/browser_process.h"
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/prefs/pref_service.h"
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/profiles/profile_info_cache_observer.h"
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/profiles/profile_info_util.h"
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/profiles/profile_manager.h"
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/common/chrome_constants.h"
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/common/chrome_switches.h"
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/common/pref_names.h"
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/installer/util/auto_launch_util.h"
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/installer/util/browser_distribution.h"
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/installer/util/product.h"
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/installer/util/shell_util.h"
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/browser_thread.h"
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "grit/generated_resources.h"
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "skia/ext/image_operations.h"
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "skia/ext/platform_canvas.h"
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/base/l10n/l10n_util.h"
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/base/resource/resource_bundle.h"
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/gfx/icon_util.h"
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/gfx/image/image.h"
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using content::BrowserThread;
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace {
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const char kProfileIconFileName[] = "Google Profile.ico";
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const int kProfileAvatarShortcutBadgeWidth = 28;
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const int kProfileAvatarShortcutBadgeHeight = 28;
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const int kShortcutIconSize = 48;
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Creates a desktop shortcut icon file (.ico) on the disk for a given profile,
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// badging the browser distribution icon with the profile avatar.
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Returns a path to the shortcut icon file on disk, which is empty if this
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// fails. Use index 0 when assigning the resulting file as the icon.
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)FilePath CreateChromeDesktopShortcutIconForProfile(
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const FilePath& profile_path,
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const SkBitmap& avatar_bitmap) {
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  HICON app_icon_handle = GetAppIconForSize(kShortcutIconSize);
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  scoped_ptr<SkBitmap> app_icon_bitmap(
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      IconUtil::CreateSkBitmapFromHICON(app_icon_handle));
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DestroyIcon(app_icon_handle);
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!app_icon_bitmap.get())
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return FilePath();
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // TODO(rlp): Share this chunk of code with
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // avatar_menu_button::DrawTaskBarDecoration.
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const SkBitmap* source_bitmap = NULL;
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SkBitmap squarer_bitmap;
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ((avatar_bitmap.width() == profiles::kAvatarIconWidth) &&
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      (avatar_bitmap.height() == profiles::kAvatarIconHeight)) {
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Shave a couple of columns so the bitmap is more square. So when
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // resized to a square aspect ratio it looks pretty.
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int x = 2;
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    avatar_bitmap.extractSubset(&squarer_bitmap, SkIRect::MakeXYWH(x, 0,
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        profiles::kAvatarIconWidth - x * 2, profiles::kAvatarIconHeight));
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    source_bitmap = &squarer_bitmap;
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    source_bitmap = &avatar_bitmap;
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SkBitmap sk_icon = skia::ImageOperations::Resize(
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      *source_bitmap,
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      skia::ImageOperations::RESIZE_LANCZOS3,
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      kProfileAvatarShortcutBadgeWidth,
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      kProfileAvatarShortcutBadgeHeight);
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Overlay the avatar on the icon, anchoring it to the bottom-right of the
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // icon.
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  scoped_ptr<SkCanvas> offscreen_canvas(
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      skia::CreateBitmapCanvas(app_icon_bitmap->width(),
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                               app_icon_bitmap->height(),
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                               false));
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(offscreen_canvas.get());
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  offscreen_canvas->drawBitmap(*app_icon_bitmap, 0, 0);
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  offscreen_canvas->drawBitmap(
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      sk_icon,
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      app_icon_bitmap->width() - kProfileAvatarShortcutBadgeWidth,
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      app_icon_bitmap->height() - kProfileAvatarShortcutBadgeHeight);
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const SkBitmap& final_bitmap =
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      offscreen_canvas->getDevice()->accessBitmap(false);
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Finally, write the .ico file containing this new bitmap.
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  FilePath icon_path = profile_path.AppendASCII(kProfileIconFileName);
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!IconUtil::CreateIconFileFromSkBitmap(final_bitmap, icon_path))
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return FilePath();
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return icon_path;
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)string16 CreateProfileShortcutFlags(const FilePath& profile_path) {
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return base::StringPrintf(L"--%ls=\"%ls\"",
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ASCIIToUTF16(switches::kProfileDirectory).c_str(),
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      profile_path.BaseName().value().c_str());
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Wrap a ShellUtil/FileUtil function that returns a bool so it can be posted
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// in a task to the FILE thread.
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void CallBoolFunction(
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const base::Callback<bool(void)>& bool_function) {
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool_function.Run();
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Renames an existing Chrome desktop profile shortcut. Must be called on the
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// FILE thread.
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void RenameChromeDesktopShortcutForProfile(
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const string16& old_shortcut_file,
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const string16& new_shortcut_file) {
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  BrowserDistribution* dist = BrowserDistribution::GetDistribution();
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  FilePath shortcut_path;
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (ShellUtil::GetShortcutPath(ShellUtil::SHORTCUT_LOCATION_DESKTOP, dist,
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                 ShellUtil::CURRENT_USER, &shortcut_path)) {
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    FilePath old_shortcut_path = shortcut_path.Append(old_shortcut_file);
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    FilePath new_shortcut_path = shortcut_path.Append(new_shortcut_file);
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!file_util::Move(old_shortcut_path, new_shortcut_path))
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       LOG(ERROR) << "Could not rename Windows profile desktop shortcut.";
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Create or update a profile desktop shortcut. Must be called on the FILE
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// thread.
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void CreateOrUpdateProfileDesktopShortcut(
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const FilePath& profile_path,
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const string16& profile_name,
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const SkBitmap& avatar_image,
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    bool create) {
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  FilePath shortcut_icon =
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      CreateChromeDesktopShortcutIconForProfile(profile_path, avatar_image);
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  FilePath chrome_exe;
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!PathService::Get(base::FILE_EXE, &chrome_exe)) {
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    NOTREACHED();
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  BrowserDistribution* dist = BrowserDistribution::GetDistribution();
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  installer::Product product(dist);
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ShellUtil::ShortcutProperties properties(ShellUtil::CURRENT_USER);
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  product.AddDefaultShortcutProperties(chrome_exe, &properties);
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  properties.set_arguments(CreateProfileShortcutFlags(profile_path));
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!shortcut_icon.empty())
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    properties.set_icon(shortcut_icon, 0);
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  properties.set_shortcut_name(
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ProfileShortcutManager::GetShortcutNameForProfile(profile_name));
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ShellUtil::ShortcutOperation operation =
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      create ? ShellUtil::SHELL_SHORTCUT_CREATE_ALWAYS :
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)               ShellUtil::SHELL_SHORTCUT_REPLACE_EXISTING;
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ShellUtil::CreateOrUpdateShortcut(
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ShellUtil::SHORTCUT_LOCATION_DESKTOP, dist, properties, operation);
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class ProfileShortcutManagerWin : public ProfileShortcutManager,
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  public ProfileInfoCacheObserver {
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public:
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  explicit ProfileShortcutManagerWin(ProfileManager* manager);
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  virtual ~ProfileShortcutManagerWin();
1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  virtual void CreateProfileShortcut(const FilePath& profile_path) OVERRIDE;
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // ProfileInfoCacheObserver:
1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  virtual void OnProfileAdded(const FilePath& profile_path) OVERRIDE;
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  virtual void OnProfileWillBeRemoved(const FilePath& profile_path) OVERRIDE;
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  virtual void OnProfileWasRemoved(const FilePath& profile_path,
1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                   const string16& profile_name) OVERRIDE;
1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  virtual void OnProfileNameChanged(const FilePath& profile_path,
1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                    const string16& old_profile_name) OVERRIDE;
1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  virtual void OnProfileAvatarChanged(const FilePath& profile_path) OVERRIDE;
1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) private:
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void StartProfileShortcutNameChange(const FilePath& profile_path,
1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                      const string16& old_profile_name);
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Gives the profile path of an alternate profile than |profile_path|.
1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Must only be called when the number profiles is 2.
1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  FilePath GetOtherProfilePath(const FilePath& profile_path);
1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void UpdateShortcutForProfileAtPath(const FilePath& profile_path,
1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                      bool create_always);
1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ProfileManager* profile_manager_;
1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DISALLOW_COPY_AND_ASSIGN(ProfileShortcutManagerWin);
2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static
2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool ProfileShortcutManager::IsFeatureEnabled() {
2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return false;
2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static
2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ProfileShortcutManager* ProfileShortcutManager::Create(
2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ProfileManager* manager) {
2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return new ProfileShortcutManagerWin(manager);
2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static
2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)string16 ProfileShortcutManager::GetShortcutNameForProfile(
2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const string16& profile_name) {
2165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  BrowserDistribution* dist = BrowserDistribution::GetDistribution();
2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  string16 shortcut_name(dist->GetAppShortCutName());
2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  shortcut_name.append(L" (");
2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  shortcut_name.append(profile_name);
2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  shortcut_name.append(L")");
2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return shortcut_name;
2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ProfileShortcutManagerWin::ProfileShortcutManagerWin(ProfileManager* manager)
2255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    : profile_manager_(manager) {
2265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    profile_manager_->GetProfileInfoCache().AddObserver(this);
2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ProfileShortcutManagerWin::~ProfileShortcutManagerWin() {
2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    profile_manager_->GetProfileInfoCache().RemoveObserver(this);
2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void ProfileShortcutManagerWin::CreateProfileShortcut(
2345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const FilePath& profile_path) {
2355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  UpdateShortcutForProfileAtPath(profile_path, true);
2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void ProfileShortcutManagerWin::OnProfileAdded(const FilePath& profile_path) {
2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (profile_manager_->GetProfileInfoCache().GetNumberOfProfiles() == 1) {
2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    UpdateShortcutForProfileAtPath(profile_path, true);
2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else if (
2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      profile_manager_->GetProfileInfoCache().GetNumberOfProfiles() == 2) {
2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    UpdateShortcutForProfileAtPath(
2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        GetOtherProfilePath(profile_path), false);
2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void ProfileShortcutManagerWin::OnProfileWillBeRemoved(
2495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const FilePath& profile_path) {
2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void ProfileShortcutManagerWin::OnProfileWasRemoved(
2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const FilePath& profile_path,
2545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const string16& profile_name) {
2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // If there is only one profile remaining, remove the badging information
2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // from an existing shortcut.
2575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (profile_manager_->GetProfileInfoCache().GetNumberOfProfiles() == 1) {
2585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    UpdateShortcutForProfileAtPath(
2595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        profile_manager_->GetProfileInfoCache().GetPathOfProfileAtIndex(0),
2605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        false);
2615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  string16 profile_name_updated;
2645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (profile_manager_->GetProfileInfoCache().GetNumberOfProfiles() != 0)
2655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    profile_name_updated = profile_name;
2665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  BrowserDistribution* dist = BrowserDistribution::GetDistribution();
2685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  FilePath chrome_exe;
2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (PathService::Get(base::FILE_EXE, &chrome_exe)) {
2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    BrowserThread::PostTask(
2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        BrowserThread::FILE, FROM_HERE,
2725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            base::Bind(&CallBoolFunction, base::Bind(
2735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                &ShellUtil::RemoveShortcut,
2745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                ShellUtil::SHORTCUT_LOCATION_DESKTOP,
2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                dist, chrome_exe.value(), ShellUtil::CURRENT_USER,
2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                &GetShortcutNameForProfile(profile_name_updated))));
2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    FilePath icon_path = profile_path.AppendASCII(kProfileIconFileName);
2795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    BrowserThread::PostTask(
2805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        BrowserThread::FILE, FROM_HERE,
2815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            base::Bind(&CallBoolFunction, base::Bind(
2825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                &file_util::Delete, icon_path, false)));
2835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
2845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    NOTREACHED();
2855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void ProfileShortcutManagerWin::OnProfileNameChanged(
2895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const FilePath& profile_path,
2905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const string16& old_profile_name) {
2915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  UpdateShortcutForProfileAtPath(profile_path, false);
2925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void ProfileShortcutManagerWin::OnProfileAvatarChanged(
2955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const FilePath& profile_path) {
2965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  UpdateShortcutForProfileAtPath(profile_path, false);
2975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void ProfileShortcutManagerWin::StartProfileShortcutNameChange(
3005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const FilePath& profile_path,
3015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const string16& old_profile_name) {
3025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  size_t profile_index = profile_manager_->GetProfileInfoCache().
3035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      GetIndexOfProfileWithPath(profile_path);
3045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (profile_index == std::string::npos)
3055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return;
3065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // If the shortcut will have an appended name, get the profile name.
3075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  string16 new_profile_name =
3085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      (profile_manager_->GetProfileInfoCache().GetNumberOfProfiles() == 1) ?
3095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          string16() :
3105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          profile_manager_->GetProfileInfoCache().
3115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              GetNameOfProfileAtIndex(profile_index);
3125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  string16 old_shortcut_file(GetShortcutNameForProfile(old_profile_name));
3145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  string16 new_shortcut_file(GetShortcutNameForProfile(new_profile_name));
3155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  BrowserThread::PostTask(
3165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      BrowserThread::FILE, FROM_HERE,
3175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      base::Bind(&RenameChromeDesktopShortcutForProfile,
3185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 old_shortcut_file,
3195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 new_shortcut_file));
3205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)FilePath ProfileShortcutManagerWin::GetOtherProfilePath(
3235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const FilePath& profile_path) {
3245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK_EQ(2U, profile_manager_->GetProfileInfoCache().GetNumberOfProfiles());
3255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Get the index of the current profile, in order to find the index of the
3265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // other profile.
3275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  size_t current_profile_index = profile_manager_->GetProfileInfoCache().
3285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    GetIndexOfProfileWithPath(profile_path);
3295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  size_t other_profile_index = (current_profile_index == 0) ? 1 : 0;
3305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return profile_manager_->GetProfileInfoCache().
3315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      GetPathOfProfileAtIndex(other_profile_index);
3325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void ProfileShortcutManagerWin::UpdateShortcutForProfileAtPath(
3355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const FilePath& profile_path,
3365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    bool create_always) {
3375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  size_t profile_index = profile_manager_->GetProfileInfoCache().
3385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      GetIndexOfProfileWithPath(profile_path);
3395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (profile_index == std::string::npos)
3405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return;
3415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool remove_badging = profile_manager_->GetProfileInfoCache().
3425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      GetNumberOfProfiles() == 1;
3435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  string16 old_shortcut_appended_name =
3455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      profile_manager_->GetProfileInfoCache().
3465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          GetShortcutNameOfProfileAtIndex(profile_index);
3475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  string16 new_shortcut_appended_name;
3495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!remove_badging) {
3505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    new_shortcut_appended_name =
3515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        profile_manager_->GetProfileInfoCache().
3525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            GetNameOfProfileAtIndex(profile_index);
3535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!create_always &&
3565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      new_shortcut_appended_name != old_shortcut_appended_name)
3575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    StartProfileShortcutNameChange(profile_path, old_shortcut_appended_name);
3585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SkBitmap profile_avatar_bitmap_copy;
3605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!remove_badging) {
3615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    size_t profile_icon_index = profile_manager_->GetProfileInfoCache().
3625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      GetAvatarIconIndexOfProfileAtIndex(profile_index);
3635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    gfx::Image profile_avatar_image = ResourceBundle::GetSharedInstance().
3645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        GetNativeImageNamed(
3655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            profile_manager_->GetProfileInfoCache().
3665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                GetDefaultAvatarIconResourceIDAtIndex(profile_icon_index));
3675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DCHECK(!profile_avatar_image.IsEmpty());
3695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const SkBitmap* profile_avatar_bitmap =
3705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        profile_avatar_image.ToSkBitmap();
3715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Make a copy of the SkBitmap to ensure that we can safely use the image
3725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // data on the FILE thread.
3735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    profile_avatar_bitmap->deepCopyTo(
3745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        &profile_avatar_bitmap_copy, profile_avatar_bitmap->getConfig());
3755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  BrowserThread::PostTask(
3775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      BrowserThread::FILE, FROM_HERE,
3785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      base::Bind(&CreateOrUpdateProfileDesktopShortcut,
3795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 profile_path, new_shortcut_appended_name,
3805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 profile_avatar_bitmap_copy, create_always));
3815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  profile_manager_->GetProfileInfoCache().SetShortcutNameOfProfileAtIndex(
3835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      profile_index, new_shortcut_appended_name);
3845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
385