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