1ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// Copyright (c) 2011 The Chromium Authors. All rights reserved. 2c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Use of this source code is governed by a BSD-style license that can be 3c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// found in the LICENSE file. 4c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 5c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/shell_integration.h" 6c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 7c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include <windows.h> 8c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include <shobjidl.h> 9731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick#include <propkey.h> 10731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick#include <propvarutil.h> 11c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 12c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/command_line.h" 13c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/file_util.h" 14c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/message_loop.h" 15c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/path_service.h" 16c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/string_util.h" 17c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/task.h" 183345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick#include "base/utf_string_conversions.h" 19731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick#include "base/win/registry.h" 20ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "base/win/scoped_comptr.h" 21731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick#include "base/win/windows_version.h" 22c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/web_applications/web_app.h" 23c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/common/chrome_constants.h" 24c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/common/chrome_paths.h" 25c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/common/chrome_paths_internal.h" 26c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/common/chrome_switches.h" 27c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/installer/util/browser_distribution.h" 28c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/installer/util/create_reg_key_work_item.h" 29c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/installer/util/set_reg_value_work_item.h" 30c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/installer/util/shell_util.h" 31c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/installer/util/util_constants.h" 32c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/installer/util/work_item.h" 33c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/installer/util/work_item_list.h" 34dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#include "content/browser/browser_thread.h" 35c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 36c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochnamespace { 37c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 38c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Helper function for ShellIntegration::GetAppId to generates profile id 3972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen// from profile path. "profile_id" is composed of sanitized basenames of 40c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// user data dir and profile dir joined by a ".". 41c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochstd::wstring GetProfileIdFromPath(const FilePath& profile_path) { 42c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Return empty string if profile_path is empty 43c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (profile_path.empty()) 44c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return std::wstring(); 45c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 46c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch FilePath default_user_data_dir; 47c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Return empty string if profile_path is in default user data 48c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // dir and is the default profile. 49c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (chrome::GetDefaultUserDataDirectory(&default_user_data_dir) && 50c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch profile_path.DirName() == default_user_data_dir && 51ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen profile_path.BaseName().value() == 52ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen ASCIIToUTF16(chrome::kNotSignedInProfile)) { 53c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return std::wstring(); 54ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen } 55c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 56c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Get joined basenames of user data dir and profile. 57c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch std::wstring basenames = profile_path.DirName().BaseName().value() + 58c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch L"." + profile_path.BaseName().value(); 59c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 60c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch std::wstring profile_id; 61c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch profile_id.reserve(basenames.size()); 62c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 63c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Generate profile_id from sanitized basenames. 64c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch for (size_t i = 0; i < basenames.length(); ++i) { 65c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (IsAsciiAlpha(basenames[i]) || 66c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch IsAsciiDigit(basenames[i]) || 67c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch basenames[i] == L'.') 68c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch profile_id += basenames[i]; 69c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 70c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 71c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return profile_id; 72c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 73c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 74c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Migrates existing chromium shortucts for Win7. 75c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochclass MigrateChromiumShortcutsTask : public Task { 76c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch public: 77c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch MigrateChromiumShortcutsTask() { } 78c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 79c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch virtual void Run(); 80c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 81c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch private: 82c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch void MigrateWin7Shortcuts(); 83c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch void MigrateWin7ShortcutsInPath(const FilePath& path) const; 84c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 85c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Get expected app id for given chrome shortcut. 86c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Returns true if the shortcut point to chrome and expected app id is 87c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // successfully derived. 88c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch bool GetExpectedAppId(IShellLink* shell_link, 89c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch std::wstring* expected_app_id) const; 90c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 91c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Get app id associated with given shortcut. 92c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch bool GetShortcutAppId(IShellLink* shell_link, std::wstring* app_id) const; 93c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 94c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch FilePath chrome_exe_; 95c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 96c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch DISALLOW_COPY_AND_ASSIGN(MigrateChromiumShortcutsTask); 97c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}; 98c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 99c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid MigrateChromiumShortcutsTask::Run() { 100c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // This should run on the file thread. 101731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 102c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 103c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch MigrateWin7Shortcuts(); 104c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 105c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 106c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid MigrateChromiumShortcutsTask::MigrateWin7Shortcuts() { 107c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Get full path of chrome. 108c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!PathService::Get(base::FILE_EXE, &chrome_exe_)) 109c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return; 110c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 111c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Locations to check for shortcuts migration. 112c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch static const struct { 113c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch int location_id; 114c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const wchar_t* sub_dir; 115c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } kLocations[] = { 116c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch { 117c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch base::DIR_APP_DATA, 118c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch L"Microsoft\\Internet Explorer\\Quick Launch\\User Pinned\\TaskBar" 119c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch }, { 120c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch chrome::DIR_USER_DESKTOP, 121c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch NULL 122c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch }, { 123c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch base::DIR_START_MENU, 124c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch NULL 125c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch }, { 126c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch base::DIR_APP_DATA, 127c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch L"Microsoft\\Internet Explorer\\Quick Launch\\User Pinned\\StartMenu" 128c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 129c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch }; 130c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 131c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch for (int i = 0; i < arraysize(kLocations); ++i) { 132c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch FilePath path; 133c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!PathService::Get(kLocations[i].location_id, &path)) { 134c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch NOTREACHED(); 135c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch continue; 136c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 137c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 138c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (kLocations[i].sub_dir) 139c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch path = path.Append(kLocations[i].sub_dir); 140c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 141c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch MigrateWin7ShortcutsInPath(path); 142c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 143c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 144c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 145c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid MigrateChromiumShortcutsTask::MigrateWin7ShortcutsInPath( 146c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const FilePath& path) const { 147c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Enumerate all pinned shortcuts in the given path directly. 148c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch file_util::FileEnumerator shortcuts_enum( 149c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch path, 150c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch false, // not recursive 151c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch file_util::FileEnumerator::FILES, 152c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch FILE_PATH_LITERAL("*.lnk")); 153c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 154c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch for (FilePath shortcut = shortcuts_enum.Next(); !shortcut.empty(); 155c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch shortcut = shortcuts_enum.Next()) { 156c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Load the shortcut. 157ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen base::win::ScopedComPtr<IShellLink> shell_link; 158c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (FAILED(shell_link.CreateInstance(CLSID_ShellLink, 159c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch NULL, 160c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch CLSCTX_INPROC_SERVER))) { 161c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch NOTREACHED(); 162c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return; 163c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 164c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 165ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen base::win::ScopedComPtr<IPersistFile> persist_file; 166c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (FAILED(persist_file.QueryFrom(shell_link)) || 167c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch FAILED(persist_file->Load(shortcut.value().c_str(), STGM_READ))) { 168c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch NOTREACHED(); 169c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return; 170c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 171c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 172c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Get expected app id from shortcut. 173c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch std::wstring expected_app_id; 174c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!GetExpectedAppId(shell_link, &expected_app_id) || 175c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch expected_app_id.empty()) 176c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch continue; 177c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 178c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Get existing app id from shortcut if any. 179c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch std::wstring existing_app_id; 180c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch GetShortcutAppId(shell_link, &existing_app_id); 181c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 182c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (expected_app_id != existing_app_id) { 183c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch file_util::UpdateShortcutLink(NULL, 184c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch shortcut.value().c_str(), 185c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch NULL, 186c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch NULL, 187c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch NULL, 188c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch NULL, 189c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 0, 190c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch expected_app_id.c_str()); 191c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 192c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 193c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 194c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 195c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochbool MigrateChromiumShortcutsTask::GetExpectedAppId( 196c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch IShellLink* shell_link, 197c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch std::wstring* expected_app_id) const { 198c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch DCHECK(shell_link); 199c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch DCHECK(expected_app_id); 200c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 201c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch expected_app_id->clear(); 202c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 203c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Check if the shortcut points to chrome_exe. 204c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch std::wstring source; 205c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (FAILED(shell_link->GetPath(WriteInto(&source, MAX_PATH), 206c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch MAX_PATH, 207c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch NULL, 208c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch SLGP_RAWPATH)) || 209c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch lstrcmpi(chrome_exe_.value().c_str(), source.c_str())) 210c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return false; 211c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 212c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch std::wstring arguments; 213c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (FAILED(shell_link->GetArguments(WriteInto(&arguments, MAX_PATH), 214c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch MAX_PATH))) 215c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return false; 216c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 217c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Get expected app id from shortcut command line. 218c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch CommandLine command_line = CommandLine::FromString(StringPrintf( 219c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch L"\"%ls\" %ls", source.c_str(), arguments.c_str())); 220c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 221c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch FilePath profile_path; 222c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (command_line.HasSwitch(switches::kUserDataDir)) { 2233345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick profile_path = 224ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen command_line.GetSwitchValuePath(switches::kUserDataDir).AppendASCII( 2253345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick chrome::kNotSignedInProfile); 226c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 227c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 228c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch std::wstring app_name; 229c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (command_line.HasSwitch(switches::kApp)) { 2303345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick app_name = UTF8ToWide(web_app::GenerateApplicationNameFromURL( 2313345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick GURL(command_line.GetSwitchValueASCII(switches::kApp)))); 23272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen } else if (command_line.HasSwitch(switches::kAppId)) { 23372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen app_name = UTF8ToWide(web_app::GenerateApplicationNameFromExtensionId( 23472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen command_line.GetSwitchValueASCII(switches::kAppId))); 235c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } else { 236c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch app_name = BrowserDistribution::GetDistribution()->GetBrowserAppId(); 237c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 238c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 239c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch expected_app_id->assign(ShellIntegration::GetAppId(app_name, profile_path)); 240c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return true; 241c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 242c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 243c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochbool MigrateChromiumShortcutsTask::GetShortcutAppId( 244c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch IShellLink* shell_link, 245c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch std::wstring* app_id) const { 246c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch DCHECK(shell_link); 247c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch DCHECK(app_id); 248c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 249c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch app_id->clear(); 250c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 251ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen base::win::ScopedComPtr<IPropertyStore> property_store; 252c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (FAILED(property_store.QueryFrom(shell_link))) 253c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return false; 254c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 255c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch PROPVARIANT appid_value; 256c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch PropVariantInit(&appid_value); 257731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick if (FAILED(property_store->GetValue(PKEY_AppUserModel_ID, 258c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch &appid_value))) 259c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return false; 260c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 261c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (appid_value.vt == VT_LPWSTR || 262c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch appid_value.vt == VT_BSTR) 263c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch app_id->assign(appid_value.pwszVal); 264c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 265c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch PropVariantClear(&appid_value); 266c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return true; 267c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 268c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 269c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}; 270c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 271c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochbool ShellIntegration::SetAsDefaultBrowser() { 27221d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen FilePath chrome_exe; 273c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!PathService::Get(base::FILE_EXE, &chrome_exe)) { 274c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch LOG(ERROR) << "Error getting app exe path"; 275c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return false; 276c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 277c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 278c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // From UI currently we only allow setting default browser for current user. 27921d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen BrowserDistribution* dist = BrowserDistribution::GetDistribution(); 28021d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen if (!ShellUtil::MakeChromeDefault(dist, ShellUtil::CURRENT_USER, 28121d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen chrome_exe.value(), true)) { 282c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch LOG(ERROR) << "Chrome could not be set as default browser."; 283c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return false; 284c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 285c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 286731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick VLOG(1) << "Chrome registered as default browser."; 287c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return true; 288c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 289c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 290c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochShellIntegration::DefaultBrowserState ShellIntegration::IsDefaultBrowser() { 291c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // First determine the app path. If we can't determine what that is, we have 292c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // bigger fish to fry... 29321d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen FilePath app_path; 294c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!PathService::Get(base::FILE_EXE, &app_path)) { 295c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch LOG(ERROR) << "Error getting app exe path"; 296c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return UNKNOWN_DEFAULT_BROWSER; 297c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 298c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // When we check for default browser we don't necessarily want to count file 299c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // type handlers and icons as having changed the default browser status, 300c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // since the user may have changed their shell settings to cause HTML files 301c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // to open with a text editor for example. We also don't want to aggressively 302c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // claim FTP, since the user may have a separate FTP client. It is an open 303c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // question as to how to "heal" these settings. Perhaps the user should just 304c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // re-run the installer or run with the --set-default-browser command line 305c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // flag. There is doubtless some other key we can hook into to cause "Repair" 306c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // to show up in Add/Remove programs for us. 307c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const std::wstring kChromeProtocols[] = {L"http", L"https"}; 308c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 309731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick if (base::win::GetVersion() >= base::win::VERSION_VISTA) { 310c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch IApplicationAssociationRegistration* pAAR; 311c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch HRESULT hr = CoCreateInstance(CLSID_ApplicationAssociationRegistration, 312c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch NULL, CLSCTX_INPROC, __uuidof(IApplicationAssociationRegistration), 313c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch (void**)&pAAR); 314c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!SUCCEEDED(hr)) 315c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return NOT_DEFAULT_BROWSER; 316c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 317c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch BrowserDistribution* dist = BrowserDistribution::GetDistribution(); 318c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch std::wstring app_name = dist->GetApplicationName(); 319c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // If a user specific default browser entry exists, we check for that 320c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // app name being default. If not, then default browser is just called 321c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Google Chrome or Chromium so we do not append suffix to app name. 322c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch std::wstring suffix; 32321d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen if (ShellUtil::GetUserSpecificDefaultBrowserSuffix(dist, &suffix)) 324c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch app_name += suffix; 325c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 326c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch for (int i = 0; i < _countof(kChromeProtocols); i++) { 327c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch BOOL result = TRUE; 328c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch hr = pAAR->QueryAppIsDefault(kChromeProtocols[i].c_str(), AT_URLPROTOCOL, 329c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch AL_EFFECTIVE, app_name.c_str(), &result); 330c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!SUCCEEDED(hr) || result == FALSE) { 331c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch pAAR->Release(); 332c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return NOT_DEFAULT_BROWSER; 333c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 334c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 335c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch pAAR->Release(); 336c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } else { 337c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch std::wstring short_app_path; 33821d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen GetShortPathName(app_path.value().c_str(), 33921d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen WriteInto(&short_app_path, MAX_PATH), 340c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch MAX_PATH); 341c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 342c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // open command for protocol associations 343c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch for (int i = 0; i < _countof(kChromeProtocols); i++) { 344c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Check in HKEY_CLASSES_ROOT that is the result of merge between 345c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // HKLM and HKCU 346c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch HKEY root_key = HKEY_CLASSES_ROOT; 347c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Check <protocol>\shell\open\command 348c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch std::wstring key_path(kChromeProtocols[i] + ShellUtil::kRegShellOpen); 349731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick base::win::RegKey key(root_key, key_path.c_str(), KEY_READ); 350c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch std::wstring value; 35172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen if (!key.Valid() || (key.ReadValue(L"", &value) != ERROR_SUCCESS)) 352c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return NOT_DEFAULT_BROWSER; 353c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Need to normalize path in case it's been munged. 354c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch CommandLine command_line = CommandLine::FromString(value); 355c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch std::wstring short_path; 356731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick GetShortPathName(command_line.GetProgram().value().c_str(), 357c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch WriteInto(&short_path, MAX_PATH), MAX_PATH); 358c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!FilePath::CompareEqualIgnoreCase(short_path, short_app_path)) 359c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return NOT_DEFAULT_BROWSER; 360c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 361c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 362c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return IS_DEFAULT_BROWSER; 363c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 364c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 365c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// There is no reliable way to say which browser is default on a machine (each 366c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// browser can have some of the protocols/shortcuts). So we look for only HTTP 367c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// protocol handler. Even this handler is located at different places in 368c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// registry on XP and Vista: 369c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// - HKCR\http\shell\open\command (XP) 370c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// - HKCU\Software\Microsoft\Windows\Shell\Associations\UrlAssociations\ 371c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// http\UserChoice (Vista) 372c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// This method checks if Firefox is defualt browser by checking these 373c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// locations and returns true if Firefox traces are found there. In case of 374c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// error (or if Firefox is not found)it returns the default value which 375c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// is false. 376c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochbool ShellIntegration::IsFirefoxDefaultBrowser() { 377c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch bool ff_default = false; 378731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick if (base::win::GetVersion() >= base::win::VERSION_VISTA) { 379c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch std::wstring app_cmd; 380731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick base::win::RegKey key(HKEY_CURRENT_USER, 381731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick ShellUtil::kRegVistaUrlPrefs, KEY_READ); 38272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen if (key.Valid() && (key.ReadValue(L"Progid", &app_cmd) == ERROR_SUCCESS) && 383c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch app_cmd == L"FirefoxURL") 384c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch ff_default = true; 385c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } else { 386c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch std::wstring key_path(L"http"); 387c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch key_path.append(ShellUtil::kRegShellOpen); 388731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick base::win::RegKey key(HKEY_CLASSES_ROOT, key_path.c_str(), KEY_READ); 389c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch std::wstring app_cmd; 39072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen if (key.Valid() && (key.ReadValue(L"", &app_cmd) == ERROR_SUCCESS) && 391c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch std::wstring::npos != StringToLowerASCII(app_cmd).find(L"firefox")) 392c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch ff_default = true; 393c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 394c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return ff_default; 395c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 396c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 397c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochstd::wstring ShellIntegration::GetAppId(const std::wstring& app_name, 398c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const FilePath& profile_path) { 399c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch std::wstring app_id(app_name); 400c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 401c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch std::wstring profile_id(GetProfileIdFromPath(profile_path)); 402c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!profile_id.empty()) { 403c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch app_id += L"."; 404c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch app_id += profile_id; 405c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 406c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 407c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // App id should be less than 128 chars. 408c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch DCHECK(app_id.length() < 128); 409c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return app_id; 410c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 411c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 412c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochstd::wstring ShellIntegration::GetChromiumAppId(const FilePath& profile_path) { 413c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return GetAppId(BrowserDistribution::GetDistribution()->GetBrowserAppId(), 414c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch profile_path); 415c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 416c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 417c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid ShellIntegration::MigrateChromiumShortcuts() { 418731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick if (base::win::GetVersion() < base::win::VERSION_WIN7) 419c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return; 420c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 421731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick BrowserThread::PostTask( 422731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick BrowserThread::FILE, FROM_HERE, new MigrateChromiumShortcutsTask()); 423c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 424