- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / test / install_test / chrome_installer_win.py
1 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file.
4
5 """Provides an interface for installing Chrome.
6
7 At present the only platform it supports is Windows.
8 """
9
10 import _winreg
11 import ctypes
12 from ctypes import wintypes, windll
13 import httplib
14 import logging
15 import os
16 import shutil
17 import socket
18 import subprocess
19 import tempfile
20 import urllib
21
22
23 class InstallationType(object):
24   """Defines the Chrome installation types."""
25   SYSTEM = 1
26   USER = 2
27
28
29 def IsVersionNewer(cur_version, new_version):
30   """Determines if new Chrome version is higher than the installed one.
31
32   Args:
33     cur_version: Current version of Chrome.
34     new_version: New version that will be installed.
35
36   Returns:
37     True, if new version is higher, otherwise False.
38   """
39   if cur_version == new_version:
40     return False
41   cur = cur_version.split('.')
42   new = new_version.split('.')
43   if len(cur) != 4 or len(new) != 4:
44     raise RuntimeError('One or both of the versions are invalid.')
45   for x in range(len(cur)):
46     if int(cur[x]) > int(new[x]):
47       return False
48   return True
49
50
51 def Install(installer_path, install_type, build, options):
52   """Installs the specified Chrome version.
53
54   Args:
55     installer_path: Path to the Chrome installer.
56     install_type: Type of installation (i.e., system or user).
57     build: Chrome build number.
58     options: Additional installation options.
59
60   Returns:
61     An instance of ChromeInstallation.
62   """
63   current = ChromeInstallation.GetCurrent()
64   if current:
65     # Make sure new build can be installed over existing Chrome build.
66     if (current.GetType() == InstallationType.SYSTEM and
67         install_type == InstallationType.USER):
68       raise RuntimeError('System level Chrome exists, aborting user level '
69                          'installation.')
70     # Confirm the new Chrome build is higher than the installed build.
71     if not IsVersionNewer(current.GetVersion(), build):
72       raise RuntimeError('Installation failed because a newer version is '
73                          'already installed.')
74   options.append('--install')
75   options.append('--do-not-launch-chrome')
76   logging.info('Installing Chrome build %s...' % build)
77   args = [installer_path]
78   args.extend(options)
79   if subprocess.Popen(args).wait() != 0:
80     raise RuntimeError('Chrome installation for build %s failed.' % build)
81   logging.info('Installation complete.')
82   return ChromeInstallation.GetCurrent()
83
84
85 class ChromeRegistryValues(object):
86   """Defines the Chrome registry key values."""
87   PRODUCT_VERSION = 'pv'
88   UNINSTALL_STRING = 'UninstallString'
89   UNINSTALL_ARGUMENTS = 'UninstallArguments'
90
91
92 class ChromeRegistryKeys(object):
93   """An interface for accessing and manipulating Chrome registry keys."""
94
95   _HKEY_LOCAL = r'SOFTWARE\Wow6432Node\Google\Update'
96   _HKEY_USER = _HKEY_LOCAL.replace(r'\Wow6432Node', '')
97   _chrome_version = r'Clients\{8A69D345-D564-463C-AFF1-A69D9E530F96}'
98   _chrome_args = r'ClientState\{8A69D345-D564-463C-AFF1-A69D9E530F96}'
99
100   def _GetKeyName(self, install_type, value):
101     """Gets the registry key name for the specified value.
102
103     Args:
104       install_type: Type of installation, must be InstallationType type.
105       value: ChromeRegistryValues type for which the key name is required.
106
107     Returns:
108       A string representing the full key name of the specified key value.
109     """
110     key_name = None
111     if install_type == InstallationType.USER:
112       key_name = self._HKEY_USER
113     elif install_type == InstallationType.SYSTEM:
114       key_name = self._HKEY_LOCAL
115     if value == ChromeRegistryValues.PRODUCT_VERSION:
116       return r'%s\%s' % (key_name, self._chrome_version)
117     elif value == ChromeRegistryValues.UNINSTALL_ARGUMENTS:
118       return r'%s\%s' % (key_name, self._chrome_args)
119     elif value == ChromeRegistryValues.UNINSTALL_STRING:
120       return r'%s\%s' % (key_name, self._chrome_args)
121     raise RuntimeError('Invalid registry value.')
122
123   def _GetRegistryType(self, install_type):
124     """Determines the registry key to use based on installation type.
125
126     Args:
127       install_type: Type of installation, must be InstallationType type.
128
129     Returns:
130       A long representing HKLM or HKCU, depending on installation type.
131     """
132     if install_type == InstallationType.SYSTEM:
133       return _winreg.HKEY_LOCAL_MACHINE
134     elif install_type == InstallationType.USER:
135       return _winreg.HKEY_CURRENT_USER
136     raise RuntimeError('Invalid installation type.')
137
138   def DoesKeyExist(self, install_type, subkey):
139     """Determines if a particular key exists in the registry.
140
141     Args:
142       install_type: Type of installation, must be InstallationType type.
143       subkey: Subkey to look up. It must be a ChromeRegistryValues type.
144
145     Returns:
146       True if the key exists, otherwise False.
147     """
148     key = self._GetRegistryType(install_type)
149     key_name = self._GetKeyName(install_type, subkey)
150     try:
151       hkey = _winreg.OpenKey(key, key_name)
152     except _winreg.error:
153       return False
154     if not hkey.handle:
155       return False
156     hkey.Close()
157     return True
158
159   def GetKeyValue(self, install_type, subkey):
160     """Gets value of the specified subkey from the registry.
161
162     Args:
163       install_type: Type of installation, must be InstallationType type.
164       subkey: ChromeRegistryValue type representing the value to be returned.
165
166     Returns:
167       A string representing the subkey value.
168     """
169     key = self._GetRegistryType(install_type)
170     key_name = self._GetKeyName(install_type, subkey)
171     hkey = _winreg.OpenKey(key, key_name)
172     reg_value = str(_winreg.QueryValueEx(hkey, subkey)[0])
173     hkey.Close()
174     return reg_value
175
176   def DeleteRegistryEntries(self, install_type):
177     """Deletes chrome registry settings.
178
179     Args:
180       install_type: Type of installation, must be InstallationType type.
181     """
182     reg_type = self._GetRegistryType(install_type)
183     key_name = self._GetKeyName(install_type,
184                                 ChromeRegistryValues.UNINSTALL_ARGUMENTS)
185     root = key_name[:key_name.rfind('\\')]
186     child = key_name[key_name.rfind('\\') + 1:]
187     key = _winreg.OpenKey(reg_type, root, 0, _winreg.KEY_ALL_ACCESS)
188     _winreg.DeleteKey(key, child)
189     key.Close()
190
191
192 class ChromeInstallation(object):
193   """Provides pertinent information about the installed Chrome version.
194
195   The type of Chrome version must be passed as an argument to the constructor,
196   (i.e. - user or system level).
197   """
198
199   _CSIDL_COMMON_APPDATA = 0x1C
200   _CSIDL_PROGRAM_FILESX86 = 0x2A
201
202   def __init__(self, install_type):
203     assert(install_type == InstallationType.SYSTEM or
204            install_type == InstallationType.USER)
205     self._type = install_type
206     self._regedit = ChromeRegistryKeys()
207
208   def GetType(self):
209     """Returns the current installation type."""
210     return self._type
211
212   def GetVersion(self):
213     """Returns the installed version of Chrome."""
214     return self._regedit.GetKeyValue(self._type,
215                                      ChromeRegistryValues.PRODUCT_VERSION)
216
217   def _GetWinLocalFolder(self, ftype=_CSIDL_COMMON_APPDATA):
218     """Returns full path of the 'Local' folder on Windows.
219
220     Args:
221       ftype: Location to look up, which could vary based on installation type.
222
223     Returns:
224       A String representing the folder path if successful, otherwise an empty
225       string.
226     """
227     SHGetFolderPathW = windll.shell32.SHGetFolderPathW
228     SHGetFolderPathW.argtypes = [wintypes.HWND,
229                                  ctypes.c_int,
230                                  wintypes.HANDLE,
231                                  wintypes.DWORD,
232                                  wintypes.LPCWSTR]
233     path_buf = wintypes.create_unicode_buffer(wintypes.MAX_PATH)
234     result = SHGetFolderPathW(0, ftype, 0, 0, path_buf)
235     return str(path_buf.value)
236
237   def _GetUninstallString(self):
238     """Returns the Chrome uninstall string from the registry."""
239     return self._regedit.GetKeyValue(self._type,
240                                      ChromeRegistryValues.UNINSTALL_STRING)
241
242   def _GetUninstallArguments(self):
243     """Returns the Chrome uninstall arguments from the registry."""
244     return self._regedit.GetKeyValue(self._type,
245                                      ChromeRegistryValues.UNINSTALL_ARGUMENTS)
246
247   def GetExePath(self):
248     """Returns Chrome binary location based on installation type.
249
250     Currently this method only returns the location of the Chrome binary.
251     It does not support Chromium.
252     """
253     if self._type == InstallationType.USER:
254       folder_id = self._CSIDL_COMMON_APPDATA
255     elif self._type == InstallationType.SYSTEM:
256       folder_id = self._CSIDL_PROGRAM_FILESX86
257     chrome_path = os.path.join(self._GetWinLocalFolder(folder_id), 'Google',
258                                'Chrome', 'Application', 'chrome.exe')
259     return (chrome_path if os.path.exists(chrome_path) else '')
260
261   @staticmethod
262   def GetCurrent():
263     """Determines Chrome installation type.
264
265     Returns:
266       ChromeInstallation object if Chrome is present, otherwise None.
267     """
268     registry = ChromeRegistryKeys()
269     if registry.DoesKeyExist(InstallationType.SYSTEM,
270                              ChromeRegistryValues.PRODUCT_VERSION):
271       return ChromeInstallation(InstallationType.SYSTEM)
272     elif registry.DoesKeyExist(InstallationType.USER,
273                                ChromeRegistryValues.PRODUCT_VERSION):
274       return ChromeInstallation(InstallationType.USER)
275     return None
276
277   def Uninstall(self):
278     """Uninstalls Chrome."""
279     chrome_path = self.GetExePath()
280     reg_opts = self._GetUninstallArguments()
281     uninstall_str = self._GetUninstallString()
282     options = '%s --force-uninstall' % (reg_opts)
283     if self._type == InstallationType.SYSTEM:
284       options += ' --system-level'
285     if not os.path.exists(chrome_path):
286       raise RuntimeError('Could not find chrome, aborting uninstall.')
287     logging.info('Launching Chrome installer...')
288     cmd = '"%s" %s' % (uninstall_str, options)
289     subprocess.call(cmd)
290     if not os.path.exists(chrome_path):
291       logging.info('Chrome was uninstalled successfully...')
292       logging.info('Deleting registry entries...')
293       self._regedit.DeleteRegistryEntries(self._type)
294       logging.info('Uninstall complete.')
295     else:
296       raise RuntimeError('Uninstall failed.')