Chromium Code Reviews
|
| OLD | NEW |
|---|---|
| (Empty) | |
| 1 #!/usr/bin/python | |
| 2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
| 3 # Use of this source code is governed by a BSD-style license that can be | |
| 4 # found in the LICENSE file. | |
| 5 | |
| 6 """Provides an interface for installing Chrome.""" | |
| 7 | |
| 8 import _winreg | |
| 9 import ctypes | |
| 10 from ctypes import wintypes, windll | |
| 11 import httplib | |
| 12 import logging | |
| 13 import os | |
| 14 import shutil | |
| 15 import socket | |
| 16 import subprocess | |
| 17 import tempfile | |
| 18 import urllib | |
| 19 | |
| 20 | |
| 21 class InstallationType: | |
| 22 """Defines the Chrome installation types.""" | |
| 23 SYSTEM = 0 | |
| 24 USER = 1 | |
| 25 | |
| 26 | |
| 27 class ChromeRegistryValues: | |
| 28 """Defines the Chrome registry key values.""" | |
| 29 PRODUCT_VERSION = 0 | |
|
kkania
2012/08/03 18:41:08
you can just change these values to the string equ
nkang
2012/08/06 23:57:13
Changed the three values from numerical to string.
| |
| 30 UNINSTALL_ARGUMENTS = 1 | |
| 31 UNINSTALL_STRING = 2 | |
| 32 | |
| 33 | |
| 34 def Install(installer_path, chrome_type, build, options='', clean=True): | |
| 35 """Installs the specified Chrome build. | |
| 36 | |
| 37 Args: | |
| 38 installer_path: Path to the Chrome installer. | |
| 39 chrome_type: Type of installation (i.e., system or user). | |
| 40 build: Chrome build number. | |
| 41 options: Any additional installation options. | |
| 42 clean: Determines whether to delete registry settings before installation. | |
| 43 | |
| 44 Returns: | |
| 45 A ChromeInstallation object if successful, otherwise None. | |
|
kkania
2012/08/03 18:41:08
this comment needs to be updated
nkang
2012/08/06 23:57:13
Changed the comment. It now says, 'Returns: A Chro
| |
| 46 """ | |
| 47 def DoPreliminaryChecks(regedit): | |
| 48 """Validates the test parameters and Chrome version. | |
| 49 | |
| 50 Args: | |
| 51 regedit: ChromeRegistryKeys object. | |
| 52 """ | |
| 53 assert(os.path.isfile(installer_path)) | |
| 54 install_type = ('system-level' in options and InstallationType.SYSTEM | |
|
kkania
2012/08/03 18:41:08
this statement no longer works. that is why I typi
nkang
2012/08/06 23:57:13
Good find! Changed this statement to the following
| |
| 55 or InstallationType.USER) | |
| 56 cur_build = regedit.GetKeyValue(chrome_type, | |
| 57 ChromeRegistryValues.PRODUCT_VERSION) | |
| 58 # Chrome already installed, make sure new build can be installed over it. | |
| 59 if cur_build: | |
| 60 current_type = ChromeInstallation.GetType() | |
| 61 if(current_type == InstallationType.SYSTEM and install_type == | |
| 62 InstallationType.USER): | |
| 63 raise RuntimeError('System level Chrome exists, aborting user level ' | |
| 64 'installation.') | |
| 65 # Installing a build that's older than the currently installed build. | |
| 66 elif current_type == install_type: | |
| 67 if cur_build >= build: | |
| 68 raise RuntimeError('Please specify a newer version of Chrome.') | |
| 69 | |
| 70 regedit = ChromeRegistryKeys() | |
| 71 DoPreliminaryChecks(regedit) | |
| 72 options += ' --install --do-not-launch-chrome' | |
| 73 logging.log(logging.INFO, 'Launching Chrome installer...') | |
| 74 cmd = '%s %s' % (installer_path, options) | |
| 75 ret = subprocess.Popen(cmd, shell=True).wait() | |
| 76 if ret == 0: | |
| 77 logging.log(logging.INFO, 'Installation complete.') | |
| 78 return ChromeInstallation(ChromeInstallation.GetType()) | |
| 79 raise RuntimeError('Chrome installation for build %s failed.' % build) | |
| 80 | |
| 81 | |
| 82 class ChromeRegistryKeys(object): | |
| 83 """An interface for accessing and manipulating Chrome registry keys.""" | |
| 84 | |
| 85 _HKEY_LOCAL = r'SOFTWARE\Wow6432Node\Google\Update' | |
| 86 _HKEY_USER = _HKEY_LOCAL.replace('\\Wow6432Node', '') | |
| 87 _chrome_version = r'Clients\{8A69D345-D564-463C-AFF1-A69D9E530F96}' | |
| 88 _chrome_uargs = r'ClientState\{8A69D345-D564-463C-AFF1-A69D9E530F96}' | |
| 89 _chrome_ustring = r'ClientState\{8A69D345-D564-463C-AFF1-A69D9E530F96}' | |
|
kkania
2012/08/03 18:41:08
what's diff between this and above var? merge them
nkang
2012/08/06 23:57:13
There's no difference; they are the same. I just c
| |
| 90 | |
| 91 def _GetKeyName(self, install_type, value): | |
| 92 """Generates the registry key name for the specified value. | |
| 93 | |
| 94 Args: | |
| 95 install_type: Type of installation, must be InstallationType type. | |
| 96 value: ChromeRegistryValues type for which the key name is required. | |
| 97 | |
| 98 Returns: | |
| 99 A string representing the full key name of the specified key value. | |
| 100 """ | |
| 101 key_name = None | |
| 102 if install_type == InstallationType.USER: | |
| 103 key_name = self._HKEY_USER | |
| 104 elif install_type == InstallationType.SYSTEM: | |
| 105 key_name = self._HKEY_LOCAL | |
| 106 if value == ChromeRegistryValues.PRODUCT_VERSION: | |
| 107 return '%s\%s' % (key_name, self._chrome_version) | |
| 108 elif value == ChromeRegistryValues.UNINSTALL_ARGUMENTS: | |
| 109 return '%s\%s' % (key_name, self._chrome_uargs) | |
| 110 elif value == ChromeRegistryValues.UNINSTALL_STRING: | |
| 111 return '%s\%s' % (key_name, self._chrome_ustring) | |
| 112 return '' | |
|
kkania
2012/08/03 18:41:08
throw instead
nkang
2012/08/06 23:57:13
Instead of returning an empty string, the method n
| |
| 113 | |
| 114 def _GetRegistryType(self, install_type): | |
| 115 """Determines the registry key to use based on installation type. | |
| 116 | |
| 117 Args: | |
| 118 install_type: Type of installation, must be InstallationType type. | |
| 119 | |
| 120 Returns: | |
| 121 A long representing HKLM or HKCU, depending on installation type. | |
| 122 """ | |
| 123 if install_type == InstallationType.SYSTEM: | |
| 124 return _winreg.HKEY_LOCAL_MACHINE | |
| 125 elif install_type == InstallationType.USER: | |
| 126 return _winreg.HKEY_CURRENT_USER | |
| 127 return None | |
|
kkania
2012/08/03 18:41:08
throw instead
nkang
2012/08/06 23:57:13
Instead of returning None, the method raises a Run
| |
| 128 | |
| 129 def DoesKeyExist(self, install_type, subkey): | |
| 130 """Determines if a particular key exists in the registry. | |
| 131 | |
| 132 Args: | |
| 133 install_type: Type of installation, must be InstallationType type. | |
| 134 subkey: Subkey to look up. It must be a ChromeRegistryValues type. | |
| 135 | |
| 136 Returns: | |
| 137 True if the key exists, otherwise False. | |
| 138 """ | |
| 139 b_exists = False | |
| 140 key = self._GetRegistryType(install_type) | |
| 141 key_name = self._GetKeyName(install_type, subkey) | |
| 142 if not key_name or not key: | |
| 143 return b_exists | |
|
kkania
2012/08/03 18:41:08
throw instead
nkang
2012/08/06 23:57:13
Got rid of this statement altogether. We can't thr
| |
| 144 try: | |
| 145 hkey = _winreg.OpenKey(key, key_name) | |
| 146 if hkey.handle: | |
| 147 b_exists = True | |
| 148 hkey.Close() | |
| 149 return b_exists | |
| 150 except _winreg.error: | |
| 151 return False | |
| 152 | |
| 153 def GetKeyValue(self, install_type, subkey): | |
| 154 """Gets value of the specified subkey from the registry. | |
| 155 | |
| 156 Args: | |
| 157 install_type: Type of installation, must be InstallationType type. | |
| 158 subkey: ChromeRegistryValue type representing the value to be returned. | |
| 159 | |
| 160 Returns: | |
| 161 A string representing the subkey value if successful, otherwise None. | |
|
kkania
2012/08/03 18:41:08
don't return none in this func in any circumstance
nkang
2012/08/06 23:57:13
Got rid of the None return value if a _winreg.erro
| |
| 162 """ | |
| 163 reg_value = '' | |
| 164 key = self._GetRegistryType(install_type) | |
| 165 key_name = self._GetKeyName(install_type, subkey) | |
| 166 value = ({ChromeRegistryValues.PRODUCT_VERSION : 'pv', | |
| 167 ChromeRegistryValues.UNINSTALL_STRING : 'UninstallString', | |
| 168 ChromeRegistryValues.UNINSTALL_ARGUMENTS : 'UninstallArguments' | |
| 169 }).get(subkey) | |
| 170 if not key or not key_name: | |
|
kkania
2012/08/03 18:41:08
don't check here, let the other funcs throw
nkang
2012/08/06 23:57:13
Got rid of the 'if' statement, altogether. If key
| |
| 171 return None | |
| 172 try: | |
| 173 hkey = _winreg.OpenKey(key, key_name) | |
|
kkania
2012/08/03 18:41:08
don't catch exceptions here, let them be raised
nkang
2012/08/06 23:57:13
Got rid of the try/except block. We will no longer
| |
| 174 if hkey.handle: | |
| 175 reg_value = str(_winreg.QueryValueEx(hkey, value)[0]) | |
| 176 hkey.Close() | |
| 177 return reg_value | |
| 178 except _winreg.error: | |
| 179 return None | |
| 180 | |
| 181 def DeleteRegistryEntries(self, install_type): | |
| 182 """Deletes chrome registry settings. | |
| 183 | |
| 184 Args: | |
| 185 install_type: Type of installation, must be InstallationType type. | |
| 186 """ | |
| 187 key = self._GetRegistryType(install_type) | |
| 188 key_name = self._GetKeyName(install_type, | |
| 189 ChromeRegistryValues.UNINSTALL_ARGUMENTS) | |
| 190 root = key_name[: key_name.rfind('\\')] | |
|
kkania
2012/08/03 18:41:08
no space between :
nkang
2012/08/06 23:57:13
Fixed.
| |
| 191 child = key_name[key_name.rfind('\\') + 1 :] | |
|
kkania
2012/08/03 18:41:08
same
nkang
2012/08/06 23:57:13
Fixed.
| |
| 192 try: | |
| 193 key = _winreg.OpenKey(key, root, 0, _winreg.KEY_ALL_ACCESS) | |
| 194 _winreg.DeleteKey(key, child) | |
| 195 key.Close() | |
| 196 except _winreg.error, err: | |
|
kkania
2012/08/03 18:41:08
why bother catching if you're just going to raise?
nkang
2012/08/06 23:57:13
Previously 'DeleteRegistryEntries' did not raise a
| |
| 197 raise(err) | |
| 198 | |
| 199 | |
| 200 class ChromeInstallation(object): | |
| 201 """Provides pertinent information about the installed Chrome version. | |
| 202 | |
| 203 The type of Chrome version must be passed as an argument to the constructor, | |
| 204 (i.e. - user or system level). | |
| 205 """ | |
| 206 | |
| 207 _CSIDL_COMMON_APPDATA = 0x1C | |
| 208 _CSIDL_PROGRAM_FILESX86 = 0x2A | |
| 209 | |
| 210 def __init__(self, install_type): | |
| 211 assert(install_type == InstallationType.SYSTEM or | |
| 212 install_type == InstallationType.USER) | |
| 213 self._type = install_type | |
| 214 self._regedit = ChromeRegistryKeys() | |
| 215 | |
| 216 def _GetWinLocalFolder(self, ftype=_CSIDL_COMMON_APPDATA): | |
| 217 """Returns full path of the 'Local' folder on Windows. | |
| 218 | |
| 219 Args: | |
| 220 ftype: Location to look up, which could vary based on installation type. | |
| 221 | |
| 222 Returns: | |
| 223 A String representing the folder path if successful, otherwise an empty | |
| 224 string. | |
| 225 """ | |
| 226 SHGetFolderPathW = windll.shell32.SHGetFolderPathW | |
| 227 SHGetFolderPathW.argtypes = [wintypes.HWND, | |
| 228 ctypes.c_int, | |
| 229 wintypes.HANDLE, | |
| 230 wintypes.DWORD, | |
| 231 wintypes.LPCWSTR] | |
| 232 path_buf = wintypes.create_unicode_buffer(wintypes.MAX_PATH) | |
| 233 result = SHGetFolderPathW(0, ftype, 0, 0, path_buf) | |
| 234 return str(path_buf.value) | |
| 235 | |
| 236 def _GetUninstallString(self): | |
| 237 """Returns the Chrome uninstall string from the registry.""" | |
| 238 return self._regedit.GetKeyValue(self._type, | |
| 239 ChromeRegistryValues.UNINSTALL_STRING) | |
| 240 | |
| 241 def _GetUninstallArguments(self): | |
| 242 """Returns the Chrome uninstall arguments from the registry.""" | |
| 243 return self._regedit.GetKeyValue(self._type, | |
| 244 ChromeRegistryValues.UNINSTALL_ARGUMENTS) | |
| 245 | |
| 246 def _LaunchInstaller(self, installer_path, options): | |
|
kkania
2012/08/03 18:41:08
remove this separate func and put subproces.popen
nkang
2012/08/06 23:57:13
Got rid of the '_LaunchInstaller' method. The whol
| |
| 247 """Launches the Chrome installer. | |
| 248 | |
| 249 Args: | |
| 250 installer_path: Path where the installer is located. | |
| 251 options: Any additional options to be used for installation. | |
| 252 """ | |
| 253 cmd = '%s %s' % (installer_path, options) | |
| 254 try: | |
| 255 subprocess.Popen(cmd, shell=True).wait() | |
| 256 except OSError, err: | |
| 257 raise err | |
| 258 | |
| 259 def GetPath(self): | |
|
kkania
2012/08/03 18:41:08
GetExePath
nkang
2012/08/06 23:57:13
Changed method name to GetExePath.
| |
| 260 """Returns Chrome binary location based on installation type.""" | |
| 261 if self._type == InstallationType.USER: | |
| 262 folder_id = self._CSIDL_COMMON_APPDATA | |
| 263 elif self._type == InstallationType.SYSTEM: | |
| 264 folder_id = self._CSIDL_PROGRAM_FILESX86 | |
| 265 chrome_path = os.path.join(self._GetWinLocalFolder(folder_id), 'Google', | |
| 266 'Chrome', 'Application', 'chrome.exe') | |
| 267 return (os.path.exists(chrome_path) and chrome_path or '') | |
| 268 | |
| 269 @staticmethod | |
| 270 def GetType(): | |
|
kkania
2012/08/03 18:41:08
change this to GetCurrent, which returns None or a
nkang
2012/08/06 23:57:13
Changed method name to GetCurrent. Also changed th
| |
| 271 """Determines Chrome installation type. | |
| 272 | |
| 273 Returns: | |
| 274 InstallationType type if Chrome is present, otherwise None. | |
| 275 """ | |
| 276 registry = ChromeRegistryKeys() | |
| 277 if registry.DoesKeyExist(InstallationType.SYSTEM, | |
| 278 ChromeRegistryValues.PRODUCT_VERSION): | |
| 279 return InstallationType.SYSTEM | |
| 280 elif registry.DoesKeyExist(InstallationType.USER, | |
| 281 ChromeRegistryValues.PRODUCT_VERSION): | |
| 282 return InstallationType.USER | |
| 283 return None | |
| 284 | |
| 285 def Uninstall(self): | |
| 286 """Uninstalls Chrome.""" | |
| 287 install_type = ChromeInstallation.GetType() | |
|
kkania
2012/08/03 18:41:08
don't do this
nkang
2012/08/06 23:57:13
Fine, I won't :)
| |
| 288 if not install_type: | |
| 289 raise RuntimeError('No Chrome version found on this system.') | |
| 290 chrome_path = self.GetPath() | |
| 291 reg_opts = self._GetUninstallArguments() | |
| 292 uninstall_str = self._GetUninstallString() | |
| 293 options = '%s --force-uninstall' % (reg_opts) | |
| 294 if self._type == InstallationType.SYSTEM: | |
| 295 options += ' --system-level' | |
| 296 if not os.path.exists(chrome_path): | |
| 297 raise RuntimeError('Could not find chrome, aborting uninstall.') | |
| 298 logging.log(logging.INFO, 'Launching Chrome installer...') | |
| 299 self._LaunchInstaller(uninstall_str, options) | |
| 300 if not os.path.exists(chrome_path): | |
| 301 logging.log(logging.INFO, 'Chrome was uninstalled successfully...') | |
| 302 logging.log(logging.INFO, 'Deleting registry entries...') | |
| 303 self._regedit.DeleteRegistryEntries(self._type) | |
| 304 logging.log(logging.INFO, 'Uninstall complete.') | |
| 305 else: | |
| 306 raise RuntimeError('Uninstall failed.') | |
| OLD | NEW |