shell_integration_win.cc revision 1e9bf3e0803691d0a228da41fc608347b6db4340
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/shell_integration.h"
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <windows.h>
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <shobjidl.h>
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <propkey.h>
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/bind.h"
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/command_line.h"
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/file_util.h"
14868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/files/file_enumerator.h"
159ab5563a3196760eb381d102cbb2bc0f7abc6a50Ben Murdoch#include "base/message_loop/message_loop.h"
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/path_service.h"
172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/strings/string_number_conversions.h"
18868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/strings/string_util.h"
19868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/strings/stringprintf.h"
20868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/strings/utf_string_conversions.h"
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/win/registry.h"
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/win/scoped_comptr.h"
232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/win/scoped_propvariant.h"
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/win/shortcut.h"
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/win/windows_version.h"
26eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include "chrome/browser/policy/policy_path_parser.h"
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/web_applications/web_app.h"
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/common/chrome_constants.h"
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/common/chrome_paths_internal.h"
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/common/chrome_switches.h"
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/installer/setup/setup_util.h"
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/installer/util/browser_distribution.h"
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/installer/util/create_reg_key_work_item.h"
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/installer/util/install_util.h"
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/installer/util/set_reg_value_work_item.h"
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/installer/util/shell_util.h"
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/installer/util/util_constants.h"
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/installer/util/work_item.h"
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/installer/util/work_item_list.h"
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/browser_thread.h"
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using content::BrowserThread;
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace {
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)const wchar_t kAppListAppNameSuffix[] = L"AppList";
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Helper function for ShellIntegration::GetAppId to generates profile id
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// from profile path. "profile_id" is composed of sanitized basenames of
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// user data dir and profile dir joined by a ".".
512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)string16 GetProfileIdFromPath(const base::FilePath& profile_path) {
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Return empty string if profile_path is empty
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (profile_path.empty())
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return string16();
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  base::FilePath default_user_data_dir;
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Return empty string if profile_path is in default user data
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // dir and is the default profile.
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (chrome::GetDefaultUserDataDirectory(&default_user_data_dir) &&
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      profile_path.DirName() == default_user_data_dir &&
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      profile_path.BaseName().value() ==
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          ASCIIToUTF16(chrome::kInitialProfile)) {
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return string16();
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Get joined basenames of user data dir and profile.
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  string16 basenames = profile_path.DirName().BaseName().value() +
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      L"." + profile_path.BaseName().value();
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  string16 profile_id;
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  profile_id.reserve(basenames.size());
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Generate profile_id from sanitized basenames.
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (size_t i = 0; i < basenames.length(); ++i) {
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (IsAsciiAlpha(basenames[i]) ||
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        IsAsciiDigit(basenames[i]) ||
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        basenames[i] == L'.')
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      profile_id += basenames[i];
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return profile_id;
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)string16 GetAppListAppName() {
852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  BrowserDistribution* dist = BrowserDistribution::GetDistribution();
862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  string16 app_name(dist->GetBaseAppId());
872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  app_name.append(kAppListAppNameSuffix);
882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return app_name;
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Gets expected app id for given Chrome (based on |command_line| and
922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// |is_per_user_install|).
932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)string16 GetExpectedAppId(const CommandLine& command_line,
942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                          bool is_per_user_install) {
95c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  base::FilePath user_data_dir;
96c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (command_line.HasSwitch(switches::kUserDataDir))
97c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    user_data_dir = command_line.GetSwitchValuePath(switches::kUserDataDir);
98c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  else
99c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    chrome::GetDefaultUserDataDirectory(&user_data_dir);
100eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  // Adjust with any policy that overrides any other way to set the path.
101eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  policy::path_parser::CheckUserDataDirPolicy(&user_data_dir);
102c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  DCHECK(!user_data_dir.empty());
103c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
104c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  base::FilePath profile_subdir;
105c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (command_line.HasSwitch(switches::kProfileDirectory)) {
106c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    profile_subdir =
107c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        command_line.GetSwitchValuePath(switches::kProfileDirectory);
108c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  } else {
109c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    profile_subdir = base::FilePath(ASCIIToUTF16(chrome::kInitialProfile));
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
111c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  DCHECK(!profile_subdir.empty());
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
113c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  base::FilePath profile_path = user_data_dir.Append(profile_subdir);
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  string16 app_name;
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (command_line.HasSwitch(switches::kApp)) {
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    app_name = UTF8ToUTF16(web_app::GenerateApplicationNameFromURL(
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        GURL(command_line.GetSwitchValueASCII(switches::kApp))));
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else if (command_line.HasSwitch(switches::kAppId)) {
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    app_name = UTF8ToUTF16(web_app::GenerateApplicationNameFromExtensionId(
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        command_line.GetSwitchValueASCII(switches::kAppId)));
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else if (command_line.HasSwitch(switches::kShowAppList)) {
1222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    app_name = GetAppListAppName();
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    BrowserDistribution* dist = BrowserDistribution::GetDistribution();
1252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    app_name = ShellUtil::GetBrowserModelId(dist, is_per_user_install);
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
127c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  DCHECK(!app_name.empty());
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return ShellIntegration::GetAppModelIdForProfile(app_name, profile_path);
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void MigrateChromiumShortcutsCallback() {
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // This should run on the file thread.
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Get full path of chrome.
1372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  base::FilePath chrome_exe;
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!PathService::Get(base::FILE_EXE, &chrome_exe))
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Locations to check for shortcuts migration.
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  static const struct {
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int location_id;
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const wchar_t* sub_dir;
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } kLocations[] = {
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    {
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      base::DIR_TASKBAR_PINS,
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      NULL
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }, {
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      base::DIR_USER_DESKTOP,
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      NULL
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }, {
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      base::DIR_START_MENU,
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      NULL
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }, {
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      base::DIR_APP_DATA,
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      L"Microsoft\\Internet Explorer\\Quick Launch\\User Pinned\\StartMenu"
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  };
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (int i = 0; i < arraysize(kLocations); ++i) {
1622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    base::FilePath path;
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!PathService::Get(kLocations[i].location_id, &path)) {
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      NOTREACHED();
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      continue;
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (kLocations[i].sub_dir)
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      path = path.Append(kLocations[i].sub_dir);
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    bool check_dual_mode = (kLocations[i].location_id == base::DIR_START_MENU);
1722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    ShellIntegration::MigrateShortcutsInPathInternal(chrome_exe, path,
1732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                                     check_dual_mode);
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ShellIntegration::DefaultWebClientState
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    GetDefaultWebClientStateFromShellUtilDefaultState(
1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        ShellUtil::DefaultState default_state) {
1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  switch (default_state) {
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ShellUtil::NOT_DEFAULT:
1822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return ShellIntegration::NOT_DEFAULT;
1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ShellUtil::IS_DEFAULT:
1842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return ShellIntegration::IS_DEFAULT;
1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    default:
1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      DCHECK_EQ(ShellUtil::UNKNOWN_DEFAULT, default_state);
1872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return ShellIntegration::UNKNOWN_DEFAULT;
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace
1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ShellIntegration::DefaultWebClientSetPermission
1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ShellIntegration::CanSetAsDefaultBrowser() {
1951e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  BrowserDistribution* distribution = BrowserDistribution::GetDistribution();
1961e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  if (distribution->GetDefaultBrowserControlPolicy() !=
1971e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)          BrowserDistribution::DEFAULT_BROWSER_FULL_CONTROL)
1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return SET_DEFAULT_NOT_ALLOWED;
1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (ShellUtil::CanMakeChromeDefaultUnattended())
2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return SET_DEFAULT_UNATTENDED;
2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  else
2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return SET_DEFAULT_INTERACTIVE;
2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool ShellIntegration::SetAsDefaultBrowser() {
2072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  base::FilePath chrome_exe;
2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!PathService::Get(base::FILE_EXE, &chrome_exe)) {
2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    LOG(ERROR) << "Error getting app exe path";
2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // From UI currently we only allow setting default browser for current user.
2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  BrowserDistribution* dist = BrowserDistribution::GetDistribution();
2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!ShellUtil::MakeChromeDefault(dist, ShellUtil::CURRENT_USER,
2165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                    chrome_exe.value(), true)) {
2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    LOG(ERROR) << "Chrome could not be set as default browser.";
2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  VLOG(1) << "Chrome registered as default browser.";
2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return true;
2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool ShellIntegration::SetAsDefaultProtocolClient(const std::string& protocol) {
2265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (protocol.empty())
2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  base::FilePath chrome_exe;
2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!PathService::Get(base::FILE_EXE, &chrome_exe)) {
2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    LOG(ERROR) << "Error getting app exe path";
2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
2335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  string16 wprotocol(UTF8ToUTF16(protocol));
2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  BrowserDistribution* dist = BrowserDistribution::GetDistribution();
2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!ShellUtil::MakeChromeDefaultProtocolClient(dist, chrome_exe.value(),
2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        wprotocol)) {
2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    LOG(ERROR) << "Chrome could not be set as default handler for "
2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)               << protocol << ".";
2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  VLOG(1) << "Chrome registered as default handler for " << protocol << ".";
2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return true;
2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool ShellIntegration::SetAsDefaultBrowserInteractive() {
2492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  base::FilePath chrome_exe;
2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!PathService::Get(base::FILE_EXE, &chrome_exe)) {
2515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    NOTREACHED() << "Error getting app exe path";
2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  BrowserDistribution* dist = BrowserDistribution::GetDistribution();
2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!ShellUtil::ShowMakeChromeDefaultSystemUI(dist, chrome_exe.value())) {
2575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    LOG(ERROR) << "Failed to launch the set-default-browser Windows UI.";
2585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
2595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  VLOG(1) << "Set-default-browser Windows UI completed.";
2625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return true;
2635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool ShellIntegration::SetAsDefaultProtocolClientInteractive(
2665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const std::string& protocol) {
2672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  base::FilePath chrome_exe;
2685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!PathService::Get(base::FILE_EXE, &chrome_exe)) {
2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    NOTREACHED() << "Error getting app exe path";
2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  BrowserDistribution* dist = BrowserDistribution::GetDistribution();
2745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  string16 wprotocol(UTF8ToUTF16(protocol));
2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!ShellUtil::ShowMakeChromeDefaultProtocolClientSystemUI(
2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          dist, chrome_exe.value(), wprotocol)) {
2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    LOG(ERROR) << "Failed to launch the set-default-client Windows UI.";
2785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
2795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  VLOG(1) << "Set-default-client Windows UI completed.";
2825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return true;
2835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)ShellIntegration::DefaultWebClientState ShellIntegration::GetDefaultBrowser() {
2865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return GetDefaultWebClientStateFromShellUtilDefaultState(
2875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ShellUtil::GetChromeDefaultState());
2885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ShellIntegration::DefaultWebClientState
2915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ShellIntegration::IsDefaultProtocolClient(const std::string& protocol) {
2925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return GetDefaultWebClientStateFromShellUtilDefaultState(
2935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ShellUtil::GetChromeDefaultProtocolClientState(UTF8ToUTF16(protocol)));
2945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)std::string ShellIntegration::GetApplicationForProtocol(const GURL& url) {
2972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // TODO(calamity): this will be implemented when external_protocol_dialog is
2982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // refactored on windows.
2992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  NOTREACHED();
3002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return std::string();
3012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
3022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// There is no reliable way to say which browser is default on a machine (each
3045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// browser can have some of the protocols/shortcuts). So we look for only HTTP
3055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// protocol handler. Even this handler is located at different places in
3065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// registry on XP and Vista:
3075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// - HKCR\http\shell\open\command (XP)
3085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// - HKCU\Software\Microsoft\Windows\Shell\Associations\UrlAssociations\
3095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//   http\UserChoice (Vista)
3105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// This method checks if Firefox is defualt browser by checking these
3115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// locations and returns true if Firefox traces are found there. In case of
3125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// error (or if Firefox is not found)it returns the default value which
3135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// is false.
3145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool ShellIntegration::IsFirefoxDefaultBrowser() {
3155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool ff_default = false;
3165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (base::win::GetVersion() >= base::win::VERSION_VISTA) {
3175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    string16 app_cmd;
3185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    base::win::RegKey key(HKEY_CURRENT_USER,
3195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                          ShellUtil::kRegVistaUrlPrefs, KEY_READ);
3205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (key.Valid() && (key.ReadValue(L"Progid", &app_cmd) == ERROR_SUCCESS) &&
3215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        app_cmd == L"FirefoxURL")
3225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ff_default = true;
3235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
3245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    string16 key_path(L"http");
3255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    key_path.append(ShellUtil::kRegShellOpen);
3265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    base::win::RegKey key(HKEY_CLASSES_ROOT, key_path.c_str(), KEY_READ);
3275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    string16 app_cmd;
3285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (key.Valid() && (key.ReadValue(L"", &app_cmd) == ERROR_SUCCESS) &&
3295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        string16::npos != StringToLowerASCII(app_cmd).find(L"firefox"))
3305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ff_default = true;
3315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return ff_default;
3335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)string16 ShellIntegration::GetAppModelIdForProfile(
3365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const string16& app_name,
3372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const base::FilePath& profile_path) {
3385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::vector<string16> components;
3395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  components.push_back(app_name);
3405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const string16 profile_id(GetProfileIdFromPath(profile_path));
3415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!profile_id.empty())
3425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    components.push_back(profile_id);
3435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return ShellUtil::BuildAppModelId(components);
3445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)string16 ShellIntegration::GetChromiumModelIdForProfile(
3472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const base::FilePath& profile_path) {
3485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  BrowserDistribution* dist = BrowserDistribution::GetDistribution();
3492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  base::FilePath chrome_exe;
3505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!PathService::Get(base::FILE_EXE, &chrome_exe)) {
3515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    NOTREACHED();
3525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return dist->GetBaseAppId();
3535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return GetAppModelIdForProfile(
3555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ShellUtil::GetBrowserModelId(
3565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)           dist, InstallUtil::IsPerUserInstall(chrome_exe.value().c_str())),
3575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      profile_path);
3585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)string16 ShellIntegration::GetAppListAppModelIdForProfile(
3612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const base::FilePath& profile_path) {
3622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return ShellIntegration::GetAppModelIdForProfile(
3632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      GetAppListAppName(), profile_path);
3645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void ShellIntegration::MigrateChromiumShortcuts() {
3675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (base::win::GetVersion() < base::win::VERSION_WIN7)
3685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
3695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // This needs to happen eventually (e.g. so that the appid is fixed and the
3712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // run-time Chrome icon is merged with the taskbar shortcut), but this is not
3722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // urgent and shouldn't delay Chrome startup.
3732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  static const int64 kMigrateChromiumShortcutsDelaySeconds = 15;
3742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  BrowserThread::PostDelayedTask(
3755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      BrowserThread::FILE, FROM_HERE,
3762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      base::Bind(&MigrateChromiumShortcutsCallback),
3772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      base::TimeDelta::FromSeconds(kMigrateChromiumShortcutsDelaySeconds));
3782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
3792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)int ShellIntegration::MigrateShortcutsInPathInternal(
3812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const base::FilePath& chrome_exe,
3822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const base::FilePath& path,
3832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    bool check_dual_mode) {
3842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK(base::win::GetVersion() >= base::win::VERSION_WIN7);
3852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Enumerate all pinned shortcuts in the given path directly.
387868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  base::FileEnumerator shortcuts_enum(
3882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      path, false,  // not recursive
389868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      base::FileEnumerator::FILES, FILE_PATH_LITERAL("*.lnk"));
3902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  bool is_per_user_install =
3922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      InstallUtil::IsPerUserInstall(chrome_exe.value().c_str());
3932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  int shortcuts_migrated = 0;
3952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  base::FilePath target_path;
3962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  string16 arguments;
3972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  base::win::ScopedPropVariant propvariant;
3982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  for (base::FilePath shortcut = shortcuts_enum.Next(); !shortcut.empty();
3992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)       shortcut = shortcuts_enum.Next()) {
4002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // TODO(gab): Use ProgramCompare instead of comparing FilePaths below once
4012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // it is fixed to work with FilePaths with spaces.
4022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (!base::win::ResolveShortcut(shortcut, &target_path, &arguments) ||
4032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        chrome_exe != target_path) {
4042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      continue;
4052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
4062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    CommandLine command_line(CommandLine::FromString(base::StringPrintf(
4072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        L"\"%ls\" %ls", target_path.value().c_str(), arguments.c_str())));
4082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // Get the expected AppId for this Chrome shortcut.
4102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    string16 expected_app_id(
4112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        GetExpectedAppId(command_line, is_per_user_install));
4122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (expected_app_id.empty())
4132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      continue;
4142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // Load the shortcut.
4162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    base::win::ScopedComPtr<IShellLink> shell_link;
4172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    base::win::ScopedComPtr<IPersistFile> persist_file;
4182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (FAILED(shell_link.CreateInstance(CLSID_ShellLink, NULL,
4192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                         CLSCTX_INPROC_SERVER)) ||
4202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        FAILED(persist_file.QueryFrom(shell_link)) ||
4212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        FAILED(persist_file->Load(shortcut.value().c_str(), STGM_READ))) {
4222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      DLOG(WARNING) << "Failed loading shortcut at " << shortcut.value();
4232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      continue;
4242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
4252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // Any properties that need to be updated on the shortcut will be stored in
4272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // |updated_properties|.
4282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    base::win::ShortcutProperties updated_properties;
4292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // Validate the existing app id for the shortcut.
4312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    base::win::ScopedComPtr<IPropertyStore> property_store;
4322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    propvariant.Reset();
4332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (FAILED(property_store.QueryFrom(shell_link)) ||
4342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        property_store->GetValue(PKEY_AppUserModel_ID,
4352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                 propvariant.Receive()) != S_OK) {
4362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      // When in doubt, prefer not updating the shortcut.
4372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      NOTREACHED();
4382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      continue;
4392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    } else {
4402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      switch (propvariant.get().vt) {
4412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        case VT_EMPTY:
4422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          // If there is no app_id set, set our app_id if one is expected.
4432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          if (!expected_app_id.empty())
4442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            updated_properties.set_app_id(expected_app_id);
4452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          break;
4462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        case VT_LPWSTR:
4472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          if (expected_app_id != string16(propvariant.get().pwszVal))
4482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            updated_properties.set_app_id(expected_app_id);
4492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          break;
4502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        default:
4512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          NOTREACHED();
4522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          continue;
4532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
4542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
4552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
456c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    // Only set dual mode if the expected app id is the default app id.
457c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    BrowserDistribution* dist = BrowserDistribution::GetDistribution();
458c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    string16 default_chromium_model_id(
459c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        ShellUtil::GetBrowserModelId(dist, is_per_user_install));
460c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    if (check_dual_mode && expected_app_id == default_chromium_model_id) {
4612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      propvariant.Reset();
4622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if (property_store->GetValue(PKEY_AppUserModel_IsDualMode,
4632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                   propvariant.Receive()) != S_OK) {
4642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        // When in doubt, prefer to not update the shortcut.
4652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        NOTREACHED();
4662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        continue;
4672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      } else {
4682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        switch (propvariant.get().vt) {
4692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          case VT_EMPTY:
4702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            // If dual_mode is not set at all, make sure it gets set to true.
4712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            updated_properties.set_dual_mode(true);
4722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            break;
4732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          case VT_BOOL:
4742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            // If it is set to false, make sure it gets set to true as well.
4752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            if (!propvariant.get().boolVal)
4762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)              updated_properties.set_dual_mode(true);
4772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            break;
4782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          default:
4792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            NOTREACHED();
4802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            continue;
4812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        }
4822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
4832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
4842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    persist_file.Release();
4862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    shell_link.Release();
4872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // Update the shortcut if some of its properties need to be updated.
4892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (updated_properties.options &&
4902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        base::win::CreateOrUpdateShortcutLink(
4912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            shortcut, updated_properties,
4922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            base::win::SHORTCUT_UPDATE_EXISTING)) {
4932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      ++shortcuts_migrated;
4942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
4952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
4962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return shortcuts_migrated;
4975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)base::FilePath ShellIntegration::GetStartMenuShortcut(
5002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const base::FilePath& chrome_exe) {
5015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  static const int kFolderIds[] = {
5025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    base::DIR_COMMON_START_MENU,
5035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    base::DIR_START_MENU,
5045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  };
5055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  BrowserDistribution* dist = BrowserDistribution::GetDistribution();
5063551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  string16 shortcut_name(
5073551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      dist->GetShortcutName(BrowserDistribution::SHORTCUT_CHROME));
5082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  base::FilePath shortcut;
5095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Check both the common and the per-user Start Menu folders for system-level
5115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // installs.
5125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  size_t folder =
5135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      InstallUtil::IsPerUserInstall(chrome_exe.value().c_str()) ? 1 : 0;
5145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (; folder < arraysize(kFolderIds); ++folder) {
5155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!PathService::Get(kFolderIds[folder], &shortcut)) {
5165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      NOTREACHED();
5175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      continue;
5185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
5195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    shortcut = shortcut.Append(shortcut_name).Append(shortcut_name +
5215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                                     installer::kLnkExt);
5227dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    if (base::PathExists(shortcut))
5235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return shortcut;
5245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
5255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return base::FilePath();
5275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
528