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> 8a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include <shlwapi.h> 95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <shobjidl.h> 101320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include <propkey.h> // Needs to come after shobjidl.h. 115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/bind.h" 135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/command_line.h" 14868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/files/file_enumerator.h" 151320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "base/files/file_util.h" 169ab5563a3196760eb381d102cbb2bc0f7abc6a50Ben Murdoch#include "base/message_loop/message_loop.h" 175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/path_service.h" 182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/strings/string_number_conversions.h" 19868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/strings/string_util.h" 20868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/strings/stringprintf.h" 21868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/strings/utf_string_conversions.h" 225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/win/registry.h" 235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/win/scoped_comptr.h" 242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/win/scoped_propvariant.h" 255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/win/shortcut.h" 265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/win/windows_version.h" 27eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include "chrome/browser/policy/policy_path_parser.h" 285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/web_applications/web_app.h" 295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/common/chrome_constants.h" 305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/common/chrome_paths_internal.h" 315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/common/chrome_switches.h" 325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/installer/setup/setup_util.h" 335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/installer/util/browser_distribution.h" 345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/installer/util/create_reg_key_work_item.h" 355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/installer/util/install_util.h" 365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/installer/util/set_reg_value_work_item.h" 375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/installer/util/shell_util.h" 385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/installer/util/util_constants.h" 395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/installer/util/work_item.h" 405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/installer/util/work_item_list.h" 415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/browser_thread.h" 425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using content::BrowserThread; 445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace { 465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)const wchar_t kAppListAppNameSuffix[] = L"AppList"; 485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Helper function for ShellIntegration::GetAppId to generates profile id 505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// from profile path. "profile_id" is composed of sanitized basenames of 515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// user data dir and profile dir joined by a ".". 525d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)base::string16 GetProfileIdFromPath(const base::FilePath& profile_path) { 535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Return empty string if profile_path is empty 545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (profile_path.empty()) 55a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) return base::string16(); 565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) base::FilePath default_user_data_dir; 585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Return empty string if profile_path is in default user data 595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // dir and is the default profile. 605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (chrome::GetDefaultUserDataDirectory(&default_user_data_dir) && 615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) profile_path.DirName() == default_user_data_dir && 625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) profile_path.BaseName().value() == 635d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) base::ASCIIToUTF16(chrome::kInitialProfile)) { 64a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) return base::string16(); 655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Get joined basenames of user data dir and profile. 68a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) base::string16 basenames = profile_path.DirName().BaseName().value() + 695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) L"." + profile_path.BaseName().value(); 705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 71a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) base::string16 profile_id; 725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) profile_id.reserve(basenames.size()); 735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Generate profile_id from sanitized basenames. 755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for (size_t i = 0; i < basenames.length(); ++i) { 765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (IsAsciiAlpha(basenames[i]) || 775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) IsAsciiDigit(basenames[i]) || 785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) basenames[i] == L'.') 795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) profile_id += basenames[i]; 805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return profile_id; 835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 855d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)base::string16 GetAppListAppName() { 862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) BrowserDistribution* dist = BrowserDistribution::GetDistribution(); 87a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) base::string16 app_name(dist->GetBaseAppId()); 882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) app_name.append(kAppListAppNameSuffix); 892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return app_name; 905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Gets expected app id for given Chrome (based on |command_line| and 932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// |is_per_user_install|). 945d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)base::string16 GetExpectedAppId(const CommandLine& command_line, 955d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) bool is_per_user_install) { 96c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) base::FilePath user_data_dir; 97c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) if (command_line.HasSwitch(switches::kUserDataDir)) 98c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) user_data_dir = command_line.GetSwitchValuePath(switches::kUserDataDir); 99c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) else 100c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) chrome::GetDefaultUserDataDirectory(&user_data_dir); 101eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch // Adjust with any policy that overrides any other way to set the path. 102eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch policy::path_parser::CheckUserDataDirPolicy(&user_data_dir); 103c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) DCHECK(!user_data_dir.empty()); 104c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 105c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) base::FilePath profile_subdir; 106c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) if (command_line.HasSwitch(switches::kProfileDirectory)) { 107c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) profile_subdir = 108c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) command_line.GetSwitchValuePath(switches::kProfileDirectory); 109c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) } else { 1105d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) profile_subdir = 1115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) base::FilePath(base::ASCIIToUTF16(chrome::kInitialProfile)); 1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 113c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) DCHECK(!profile_subdir.empty()); 1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 115c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) base::FilePath profile_path = user_data_dir.Append(profile_subdir); 116a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) base::string16 app_name; 1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (command_line.HasSwitch(switches::kApp)) { 1185d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) app_name = base::UTF8ToUTF16(web_app::GenerateApplicationNameFromURL( 1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) GURL(command_line.GetSwitchValueASCII(switches::kApp)))); 1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } else if (command_line.HasSwitch(switches::kAppId)) { 1215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) app_name = base::UTF8ToUTF16( 1225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) web_app::GenerateApplicationNameFromExtensionId( 1235d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) command_line.GetSwitchValueASCII(switches::kAppId))); 1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } else if (command_line.HasSwitch(switches::kShowAppList)) { 1252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) app_name = GetAppListAppName(); 1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } else { 1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) BrowserDistribution* dist = BrowserDistribution::GetDistribution(); 1282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) app_name = ShellUtil::GetBrowserModelId(dist, is_per_user_install); 1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 130c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) DCHECK(!app_name.empty()); 1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return ShellIntegration::GetAppModelIdForProfile(app_name, profile_path); 1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void MigrateChromiumShortcutsCallback() { 1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // This should run on the file thread. 1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Get full path of chrome. 1402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) base::FilePath chrome_exe; 1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!PathService::Get(base::FILE_EXE, &chrome_exe)) 1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return; 1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Locations to check for shortcuts migration. 1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) static const struct { 1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) int location_id; 1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const wchar_t* sub_dir; 1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } kLocations[] = { 1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) { 1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) base::DIR_TASKBAR_PINS, 1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) NULL 1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) }, { 1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) base::DIR_USER_DESKTOP, 1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) NULL 1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) }, { 1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) base::DIR_START_MENU, 1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) NULL 1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) }, { 1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) base::DIR_APP_DATA, 1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) L"Microsoft\\Internet Explorer\\Quick Launch\\User Pinned\\StartMenu" 1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) }; 1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for (int i = 0; i < arraysize(kLocations); ++i) { 1652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) base::FilePath path; 1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!PathService::Get(kLocations[i].location_id, &path)) { 1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) NOTREACHED(); 1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) continue; 1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (kLocations[i].sub_dir) 1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) path = path.Append(kLocations[i].sub_dir); 1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) bool check_dual_mode = (kLocations[i].location_id == base::DIR_START_MENU); 1752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) ShellIntegration::MigrateShortcutsInPathInternal(chrome_exe, path, 1762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) check_dual_mode); 1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 180a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)// Windows 8 introduced a new protocol->executable binding system which cannot 181a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)// be retrieved in the HKCR registry subkey method implemented below. We call 182a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)// AssocQueryString with the new Win8-only flag ASSOCF_IS_PROTOCOL instead. 183a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)base::string16 GetAppForProtocolUsingAssocQuery(const GURL& url) { 184cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) base::string16 url_scheme = base::ASCIIToWide(url.scheme()); 185a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) // Don't attempt to query protocol association on an empty string. 186cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (url_scheme.empty()) 187a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) return base::string16(); 188a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 189a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) // Query AssocQueryString for a human-readable description of the program 190a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) // that will be invoked given the provided URL spec. This is used only to 191a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) // populate the external protocol dialog box the user sees when invoking 192a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) // an unknown external protocol. 193a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) wchar_t out_buffer[1024]; 194a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) DWORD buffer_size = arraysize(out_buffer); 195a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) HRESULT hr = AssocQueryString(ASSOCF_IS_PROTOCOL, 196a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) ASSOCSTR_FRIENDLYAPPNAME, 197cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) url_scheme.c_str(), 198a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) NULL, 199a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) out_buffer, 200a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) &buffer_size); 201a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) if (FAILED(hr)) { 202a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) DLOG(WARNING) << "AssocQueryString failed!"; 203a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) return base::string16(); 204a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) } 205a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) return base::string16(out_buffer); 206a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)} 207a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 208a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)base::string16 GetAppForProtocolUsingRegistry(const GURL& url) { 209a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) base::string16 url_spec = base::ASCIIToWide(url.possibly_invalid_spec()); 210a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) const base::string16 cmd_key_path = 211a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) base::ASCIIToWide(url.scheme() + "\\shell\\open\\command"); 212a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) base::win::RegKey cmd_key(HKEY_CLASSES_ROOT, 213a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) cmd_key_path.c_str(), 214a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) KEY_READ); 215a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) size_t split_offset = url_spec.find(L':'); 216a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) if (split_offset == base::string16::npos) 217a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) return base::string16(); 218a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) const base::string16 parameters = url_spec.substr(split_offset + 1, 219a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) url_spec.length() - 1); 220a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) base::string16 application_to_launch; 221a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) if (cmd_key.ReadValue(NULL, &application_to_launch) == ERROR_SUCCESS) { 222a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) ReplaceSubstringsAfterOffset(&application_to_launch, 223a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 0, 224a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) L"%1", 225a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) parameters); 226a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) return application_to_launch; 227a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) } 228a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) return base::string16(); 229a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)} 230a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 231a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ShellIntegration::DefaultWebClientState 2335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) GetDefaultWebClientStateFromShellUtilDefaultState( 2345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ShellUtil::DefaultState default_state) { 2355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) switch (default_state) { 2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) case ShellUtil::NOT_DEFAULT: 2372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return ShellIntegration::NOT_DEFAULT; 2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) case ShellUtil::IS_DEFAULT: 2392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return ShellIntegration::IS_DEFAULT; 2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) default: 2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK_EQ(ShellUtil::UNKNOWN_DEFAULT, default_state); 2422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return ShellIntegration::UNKNOWN_DEFAULT; 2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} // namespace 2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ShellIntegration::DefaultWebClientSetPermission 2495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ShellIntegration::CanSetAsDefaultBrowser() { 2501e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) BrowserDistribution* distribution = BrowserDistribution::GetDistribution(); 2511e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) if (distribution->GetDefaultBrowserControlPolicy() != 2521e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) BrowserDistribution::DEFAULT_BROWSER_FULL_CONTROL) 2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return SET_DEFAULT_NOT_ALLOWED; 2545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (ShellUtil::CanMakeChromeDefaultUnattended()) 2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return SET_DEFAULT_UNATTENDED; 2575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) else 2585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return SET_DEFAULT_INTERACTIVE; 2595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 2605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool ShellIntegration::SetAsDefaultBrowser() { 2622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) base::FilePath chrome_exe; 2635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!PathService::Get(base::FILE_EXE, &chrome_exe)) { 2645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) LOG(ERROR) << "Error getting app exe path"; 2655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 2665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 2675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // From UI currently we only allow setting default browser for current user. 2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) BrowserDistribution* dist = BrowserDistribution::GetDistribution(); 2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!ShellUtil::MakeChromeDefault(dist, ShellUtil::CURRENT_USER, 2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) chrome_exe.value(), true)) { 2725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) LOG(ERROR) << "Chrome could not be set as default browser."; 2735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 2745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) VLOG(1) << "Chrome registered as default browser."; 2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return true; 2785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 2795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool ShellIntegration::SetAsDefaultProtocolClient(const std::string& protocol) { 2815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (protocol.empty()) 2825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 2835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) base::FilePath chrome_exe; 2855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!PathService::Get(base::FILE_EXE, &chrome_exe)) { 2865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) LOG(ERROR) << "Error getting app exe path"; 2875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 2885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 2895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2905d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) base::string16 wprotocol(base::UTF8ToUTF16(protocol)); 2915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) BrowserDistribution* dist = BrowserDistribution::GetDistribution(); 2925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!ShellUtil::MakeChromeDefaultProtocolClient(dist, chrome_exe.value(), 2935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) wprotocol)) { 2945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) LOG(ERROR) << "Chrome could not be set as default handler for " 2955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) << protocol << "."; 2965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 2975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 2985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) VLOG(1) << "Chrome registered as default handler for " << protocol << "."; 3005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return true; 3015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 3025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool ShellIntegration::SetAsDefaultBrowserInteractive() { 3042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) base::FilePath chrome_exe; 3055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!PathService::Get(base::FILE_EXE, &chrome_exe)) { 3065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) NOTREACHED() << "Error getting app exe path"; 3075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 3085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 3095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) BrowserDistribution* dist = BrowserDistribution::GetDistribution(); 3115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!ShellUtil::ShowMakeChromeDefaultSystemUI(dist, chrome_exe.value())) { 3125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) LOG(ERROR) << "Failed to launch the set-default-browser Windows UI."; 3135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 3145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 3155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) VLOG(1) << "Set-default-browser Windows UI completed."; 3175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return true; 3185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 3195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool ShellIntegration::SetAsDefaultProtocolClientInteractive( 3215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const std::string& protocol) { 3222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) base::FilePath chrome_exe; 3235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!PathService::Get(base::FILE_EXE, &chrome_exe)) { 3245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) NOTREACHED() << "Error getting app exe path"; 3255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 3265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 3275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) BrowserDistribution* dist = BrowserDistribution::GetDistribution(); 3295d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) base::string16 wprotocol(base::UTF8ToUTF16(protocol)); 3305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!ShellUtil::ShowMakeChromeDefaultProtocolClientSystemUI( 3315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) dist, chrome_exe.value(), wprotocol)) { 3325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) LOG(ERROR) << "Failed to launch the set-default-client Windows UI."; 3335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 3345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 3355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) VLOG(1) << "Set-default-client Windows UI completed."; 3375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return true; 3385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 3395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)ShellIntegration::DefaultWebClientState ShellIntegration::GetDefaultBrowser() { 3415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return GetDefaultWebClientStateFromShellUtilDefaultState( 3425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ShellUtil::GetChromeDefaultState()); 3435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 3445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ShellIntegration::DefaultWebClientState 3465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ShellIntegration::IsDefaultProtocolClient(const std::string& protocol) { 3475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return GetDefaultWebClientStateFromShellUtilDefaultState( 3485d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) ShellUtil::GetChromeDefaultProtocolClientState( 3495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) base::UTF8ToUTF16(protocol))); 3505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 3515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 352a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)base::string16 ShellIntegration::GetApplicationNameForProtocol( 353a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) const GURL& url) { 354a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) // Windows 8 or above requires a new protocol association query. 355a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) if (base::win::GetVersion() >= base::win::VERSION_WIN8) 356a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) return GetAppForProtocolUsingAssocQuery(url); 357a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) else 358a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) return GetAppForProtocolUsingRegistry(url); 3592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)} 3602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 3615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// There is no reliable way to say which browser is default on a machine (each 3625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// browser can have some of the protocols/shortcuts). So we look for only HTTP 3635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// protocol handler. Even this handler is located at different places in 3645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// registry on XP and Vista: 3655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// - HKCR\http\shell\open\command (XP) 3665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// - HKCU\Software\Microsoft\Windows\Shell\Associations\UrlAssociations\ 3675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// http\UserChoice (Vista) 3685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// This method checks if Firefox is defualt browser by checking these 3695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// locations and returns true if Firefox traces are found there. In case of 3705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// error (or if Firefox is not found)it returns the default value which 3715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// is false. 3725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool ShellIntegration::IsFirefoxDefaultBrowser() { 3735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) bool ff_default = false; 3745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (base::win::GetVersion() >= base::win::VERSION_VISTA) { 375a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) base::string16 app_cmd; 3765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) base::win::RegKey key(HKEY_CURRENT_USER, 3775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ShellUtil::kRegVistaUrlPrefs, KEY_READ); 3785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (key.Valid() && (key.ReadValue(L"Progid", &app_cmd) == ERROR_SUCCESS) && 3795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) app_cmd == L"FirefoxURL") 3805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ff_default = true; 3815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } else { 382a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) base::string16 key_path(L"http"); 3835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) key_path.append(ShellUtil::kRegShellOpen); 3845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) base::win::RegKey key(HKEY_CLASSES_ROOT, key_path.c_str(), KEY_READ); 385a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) base::string16 app_cmd; 3865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (key.Valid() && (key.ReadValue(L"", &app_cmd) == ERROR_SUCCESS) && 3876e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) base::string16::npos != 3886e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) base::StringToLowerASCII(app_cmd).find(L"firefox")) 3895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ff_default = true; 3905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 3915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return ff_default; 3925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 3935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3945d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)base::string16 ShellIntegration::GetAppModelIdForProfile( 395a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) const base::string16& app_name, 3962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) const base::FilePath& profile_path) { 3975d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) std::vector<base::string16> components; 3985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) components.push_back(app_name); 399a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) const base::string16 profile_id(GetProfileIdFromPath(profile_path)); 4005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!profile_id.empty()) 4015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) components.push_back(profile_id); 4025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return ShellUtil::BuildAppModelId(components); 4035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 4045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 4055d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)base::string16 ShellIntegration::GetChromiumModelIdForProfile( 4062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) const base::FilePath& profile_path) { 4075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) BrowserDistribution* dist = BrowserDistribution::GetDistribution(); 4082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) base::FilePath chrome_exe; 4095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!PathService::Get(base::FILE_EXE, &chrome_exe)) { 4105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) NOTREACHED(); 4115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return dist->GetBaseAppId(); 4125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 4135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return GetAppModelIdForProfile( 4145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ShellUtil::GetBrowserModelId( 4155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) dist, InstallUtil::IsPerUserInstall(chrome_exe.value().c_str())), 4165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) profile_path); 4175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 4185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 4195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)base::string16 ShellIntegration::GetAppListAppModelIdForProfile( 4202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) const base::FilePath& profile_path) { 4212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return ShellIntegration::GetAppModelIdForProfile( 4222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) GetAppListAppName(), profile_path); 4235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 4245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 4255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void ShellIntegration::MigrateChromiumShortcuts() { 4265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (base::win::GetVersion() < base::win::VERSION_WIN7) 4275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return; 4285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 4292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // This needs to happen eventually (e.g. so that the appid is fixed and the 4302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // run-time Chrome icon is merged with the taskbar shortcut), but this is not 4312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // urgent and shouldn't delay Chrome startup. 4322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) static const int64 kMigrateChromiumShortcutsDelaySeconds = 15; 4332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) BrowserThread::PostDelayedTask( 4345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) BrowserThread::FILE, FROM_HERE, 4352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) base::Bind(&MigrateChromiumShortcutsCallback), 4362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) base::TimeDelta::FromSeconds(kMigrateChromiumShortcutsDelaySeconds)); 4372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)} 4382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 4392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)int ShellIntegration::MigrateShortcutsInPathInternal( 4402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) const base::FilePath& chrome_exe, 4412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) const base::FilePath& path, 4422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) bool check_dual_mode) { 4432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) DCHECK(base::win::GetVersion() >= base::win::VERSION_WIN7); 4442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 4452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // Enumerate all pinned shortcuts in the given path directly. 446868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) base::FileEnumerator shortcuts_enum( 4472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) path, false, // not recursive 448868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) base::FileEnumerator::FILES, FILE_PATH_LITERAL("*.lnk")); 4492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 4502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) bool is_per_user_install = 4512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) InstallUtil::IsPerUserInstall(chrome_exe.value().c_str()); 4522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 4532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) int shortcuts_migrated = 0; 4542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) base::FilePath target_path; 455a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) base::string16 arguments; 4562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) base::win::ScopedPropVariant propvariant; 4572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) for (base::FilePath shortcut = shortcuts_enum.Next(); !shortcut.empty(); 4582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) shortcut = shortcuts_enum.Next()) { 4592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // TODO(gab): Use ProgramCompare instead of comparing FilePaths below once 4602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // it is fixed to work with FilePaths with spaces. 4612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (!base::win::ResolveShortcut(shortcut, &target_path, &arguments) || 4622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) chrome_exe != target_path) { 4632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) continue; 4642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 4652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) CommandLine command_line(CommandLine::FromString(base::StringPrintf( 4662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) L"\"%ls\" %ls", target_path.value().c_str(), arguments.c_str()))); 4672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 4682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // Get the expected AppId for this Chrome shortcut. 469a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) base::string16 expected_app_id( 4702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) GetExpectedAppId(command_line, is_per_user_install)); 4712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (expected_app_id.empty()) 4722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) continue; 4732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 4742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // Load the shortcut. 4752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) base::win::ScopedComPtr<IShellLink> shell_link; 4762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) base::win::ScopedComPtr<IPersistFile> persist_file; 4772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (FAILED(shell_link.CreateInstance(CLSID_ShellLink, NULL, 4782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) CLSCTX_INPROC_SERVER)) || 4792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) FAILED(persist_file.QueryFrom(shell_link)) || 4802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) FAILED(persist_file->Load(shortcut.value().c_str(), STGM_READ))) { 4812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) DLOG(WARNING) << "Failed loading shortcut at " << shortcut.value(); 4822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) continue; 4832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 4842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 4852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // Any properties that need to be updated on the shortcut will be stored in 4862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // |updated_properties|. 4872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) base::win::ShortcutProperties updated_properties; 4882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 4892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // Validate the existing app id for the shortcut. 4902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) base::win::ScopedComPtr<IPropertyStore> property_store; 4912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) propvariant.Reset(); 4922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (FAILED(property_store.QueryFrom(shell_link)) || 4932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) property_store->GetValue(PKEY_AppUserModel_ID, 4942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) propvariant.Receive()) != S_OK) { 4952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // When in doubt, prefer not updating the shortcut. 4962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) NOTREACHED(); 4972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) continue; 4982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } else { 4992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) switch (propvariant.get().vt) { 5002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) case VT_EMPTY: 5012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // If there is no app_id set, set our app_id if one is expected. 5022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (!expected_app_id.empty()) 5032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) updated_properties.set_app_id(expected_app_id); 5042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) break; 5052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) case VT_LPWSTR: 506a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) if (expected_app_id != base::string16(propvariant.get().pwszVal)) 5072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) updated_properties.set_app_id(expected_app_id); 5082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) break; 5092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) default: 5102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) NOTREACHED(); 5112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) continue; 5122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 5132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 5142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 515c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) // Only set dual mode if the expected app id is the default app id. 516c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) BrowserDistribution* dist = BrowserDistribution::GetDistribution(); 517a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) base::string16 default_chromium_model_id( 518c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) ShellUtil::GetBrowserModelId(dist, is_per_user_install)); 519c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) if (check_dual_mode && expected_app_id == default_chromium_model_id) { 5202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) propvariant.Reset(); 5212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (property_store->GetValue(PKEY_AppUserModel_IsDualMode, 5222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) propvariant.Receive()) != S_OK) { 5232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // When in doubt, prefer to not update the shortcut. 5242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) NOTREACHED(); 5252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) continue; 5262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } else { 5272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) switch (propvariant.get().vt) { 5282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) case VT_EMPTY: 5292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // If dual_mode is not set at all, make sure it gets set to true. 5302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) updated_properties.set_dual_mode(true); 5312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) break; 5322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) case VT_BOOL: 5332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // If it is set to false, make sure it gets set to true as well. 5342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (!propvariant.get().boolVal) 5352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) updated_properties.set_dual_mode(true); 5362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) break; 5372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) default: 5382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) NOTREACHED(); 5392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) continue; 5402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 5412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 5422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 5432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 5442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) persist_file.Release(); 5452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) shell_link.Release(); 5462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 5472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // Update the shortcut if some of its properties need to be updated. 5482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (updated_properties.options && 5492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) base::win::CreateOrUpdateShortcutLink( 5502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) shortcut, updated_properties, 5512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) base::win::SHORTCUT_UPDATE_EXISTING)) { 5522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) ++shortcuts_migrated; 5532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 5542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 5552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return shortcuts_migrated; 5565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 5575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 5582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)base::FilePath ShellIntegration::GetStartMenuShortcut( 5592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) const base::FilePath& chrome_exe) { 5605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) static const int kFolderIds[] = { 5615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) base::DIR_COMMON_START_MENU, 5625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) base::DIR_START_MENU, 5635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) }; 5645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) BrowserDistribution* dist = BrowserDistribution::GetDistribution(); 565a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) base::string16 shortcut_name( 5663551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) dist->GetShortcutName(BrowserDistribution::SHORTCUT_CHROME)); 5672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) base::FilePath shortcut; 5685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 5695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Check both the common and the per-user Start Menu folders for system-level 5705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // installs. 5715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) size_t folder = 5725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) InstallUtil::IsPerUserInstall(chrome_exe.value().c_str()) ? 1 : 0; 5735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for (; folder < arraysize(kFolderIds); ++folder) { 5745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!PathService::Get(kFolderIds[folder], &shortcut)) { 5755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) NOTREACHED(); 5765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) continue; 5775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 5785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 5795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) shortcut = shortcut.Append(shortcut_name).Append(shortcut_name + 5805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) installer::kLnkExt); 5817dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch if (base::PathExists(shortcut)) 5825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return shortcut; 5835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 5845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 5852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return base::FilePath(); 5865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 587