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