1# Copyright 2014 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 5import contextlib 6import json 7import logging 8import re 9import urllib2 10 11from telemetry.internal.backends.chrome import chrome_browser_backend 12from telemetry.internal.backends.chrome import system_info_backend 13 14import py_utils 15 16 17class IosBrowserBackend(chrome_browser_backend.ChromeBrowserBackend): 18 _DEBUGGER_URL_BUILDER = 'ws://localhost:%i/devtools/page/%i' 19 _DEBUGGER_URL_REGEX = r'ws://localhost:(\d+)/devtools/page/(\d+)' 20 _DEVICE_LIST_URL = 'http://localhost:9221/json' 21 22 def __init__(self, ios_platform_backend, browser_options): 23 browser_options.output_profile_path = '.' 24 super(IosBrowserBackend, self).__init__( 25 ios_platform_backend, 26 supports_tab_control=False, 27 supports_extensions=False, 28 browser_options=browser_options) 29 self._webviews = [] 30 self._port = None 31 self._page = None 32 self._system_info_backend = None 33 self.UpdateRunningBrowsersInfo() 34 35 def UpdateRunningBrowsersInfo(self): 36 """ Refresh to match current state of the running browser. 37 """ 38 device_urls = self.GetDeviceUrls() 39 urls = self.GetWebSocketDebuggerUrls(device_urls) 40 for url in urls: 41 m = re.match(self._DEBUGGER_URL_REGEX, url) 42 if m: 43 self._webviews.append([int(m.group(1)), int(m.group(2))]) 44 else: 45 logging.error('Unexpected url format: %s' % url) 46 47 # TODO(baxley): For now, grab first item from |_webviews|. Ideally, we'd 48 # prefer to have the currently displayed tab, or something similar. 49 if self._webviews: 50 self._port = self._webviews[0][0] 51 self._page = self._webviews[0][1] 52 53 def GetDeviceUrls(self): 54 device_urls = [] 55 try: 56 with contextlib.closing( 57 urllib2.urlopen(self._DEVICE_LIST_URL)) as device_list: 58 json_urls = device_list.read() 59 device_urls = json.loads(json_urls) 60 if not device_urls: 61 logging.debug('No iOS devices found. Will not try searching for iOS ' 62 'browsers.') 63 return [] 64 except urllib2.URLError as e: 65 logging.debug('Error communicating with iOS device.') 66 logging.debug(str(e)) 67 return [] 68 return device_urls 69 70 def GetWebSocketDebuggerUrls(self, device_urls): 71 """ Get a list of the websocket debugger URLs to communicate with 72 all running UIWebViews. 73 """ 74 data = [] 75 # Loop through all devices. 76 for d in device_urls: 77 def GetData(): 78 try: 79 with contextlib.closing( 80 # pylint: disable=cell-var-from-loop 81 urllib2.urlopen('http://%s/json' % d['url'])) as f: 82 json_result = f.read() 83 data = json.loads(json_result) 84 return data 85 except urllib2.URLError as e: 86 logging.debug('Error communicating with iOS device.') 87 logging.debug(e) 88 return False 89 try: 90 # Retry a few times since it can take a few seconds for this API to be 91 # ready, if ios_webkit_debug_proxy is just launched. 92 data = py_utils.WaitFor(GetData, 5) 93 except py_utils.TimeoutException as e: 94 logging.debug('Timeout retrieving data from iOS device') 95 logging.debug(e) 96 return [] 97 98 # Find all running UIWebViews. 99 debug_urls = [] 100 for j in data: 101 debug_urls.append(j['webSocketDebuggerUrl']) 102 103 return debug_urls 104 105 def GetSystemInfo(self): 106 if self._system_info_backend is None: 107 self._system_info_backend = system_info_backend.SystemInfoBackend( 108 self._port, self._page) 109 return self._system_info_backend.GetSystemInfo() 110 111 def IsBrowserRunning(self): 112 return bool(self._webviews) 113 114 #TODO(baxley): The following were stubbed out to get the sunspider benchmark 115 # running. These should be implemented. 116 @property 117 def browser_directory(self): 118 logging.warn('Not implemented') 119 return None 120 121 @property 122 def profile_directory(self): 123 logging.warn('Not implemented') 124 return None 125 126 def Start(self): 127 logging.warn('Not implemented') 128 129 def extension_backend(self): 130 logging.warn('Not implemented') 131 return None 132 133 def GetBrowserStartupArgs(self): 134 logging.warn('Not implemented') 135 return None 136 137 def HasBrowserFinishedLaunching(self): 138 logging.warn('Not implemented') 139 return False 140 141 def GetStandardOutput(self): 142 raise NotImplementedError() 143 144 def GetStackTrace(self): 145 raise NotImplementedError() 146 147 def GetMostRecentMinidumpPath(self): 148 return None 149 150 def GetAllMinidumpPaths(self): 151 return None 152 153 def GetAllUnsymbolizedMinidumpPaths(self): 154 return None 155 156 def SymbolizeMinidump(self, minidump_path): 157 return None 158