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
5"""Finds iOS browsers that can be controlled by telemetry."""
6
7import logging
8import re
9import subprocess
10
11from telemetry import decorators
12from telemetry.core import browser
13from telemetry.core import platform
14from telemetry.core import possible_browser
15from telemetry.core.backends.chrome import inspector_backend
16from telemetry.core.backends.chrome import ios_browser_backend
17from telemetry.core.platform import ios_platform_backend
18
19
20# Key matches output from ios-webkit-debug-proxy and the value is a readable
21# description of the browser.
22IOS_BROWSERS = {'CriOS': 'ios-chrome', 'Version': 'ios-safari'}
23DEVICE_LIST_URL = 'http://127.0.0.1:9221/json'
24IOS_WEBKIT_DEBUG_PROXY = 'ios_webkit_debug_proxy'
25
26
27class PossibleIOSBrowser(possible_browser.PossibleBrowser):
28
29  """A running iOS browser instance."""
30  def __init__(self, browser_type, finder_options):
31    super(PossibleIOSBrowser, self).__init__(browser_type, 'ios',
32        finder_options, True)
33
34  # TODO(baxley): Implement the following methods for iOS.
35  def Create(self):
36    backend = ios_browser_backend.IosBrowserBackend(
37        self.finder_options.browser_options)
38    return browser.Browser(backend,
39                           self._platform_backend,
40                           self._archive_path,
41                           self._append_to_existing_wpr,
42                           self._make_javascript_deterministic,
43                           self._credentials_path)
44
45  def SupportsOptions(self, finder_options):
46    #TODO(baxley): Implement me.
47    return True
48
49  def UpdateExecutableIfNeeded(self):
50    #TODO(baxley): Implement me.
51    pass
52
53  def _InitPlatformIfNeeded(self):
54    if self._platform:
55      return
56
57    self._platform_backend = ios_platform_backend.IosPlatformBackend()
58    self._platform = platform.Platform(self._platform_backend)
59
60def SelectDefaultBrowser(_):
61  return None  # TODO(baxley): Implement me.
62
63
64def CanFindAvailableBrowsers():
65  # TODO(baxley): Add support for all platforms possible. Probably Linux,
66  # probably not Windows.
67  return platform.GetHostPlatform().GetOSName() == 'mac'
68
69
70def FindAllBrowserTypes(_):
71  return IOS_BROWSERS.values()
72
73
74@decorators.Cache
75def _IsIosDeviceAttached():
76  devices = subprocess.check_output('system_profiler SPUSBDataType', shell=True)
77  for line in devices.split('\n'):
78    if line and re.match('\s*(iPod|iPhone|iPad):', line):
79      return True
80  return False
81
82
83def FindAllAvailableBrowsers(finder_options):
84  """Find all running iOS browsers on connected devices."""
85  if not CanFindAvailableBrowsers():
86    return []
87
88  if not _IsIosDeviceAttached():
89    return []
90
91  options = finder_options.browser_options
92
93  options.browser_type = 'ios-chrome'
94  backend = ios_browser_backend.IosBrowserBackend(options)
95  host = platform.GetHostPlatform()
96  # TODO(baxley): Use idevice to wake up device or log debug statement.
97  if not host.IsApplicationRunning(IOS_WEBKIT_DEBUG_PROXY):
98    host.LaunchApplication(IOS_WEBKIT_DEBUG_PROXY)
99    if not host.IsApplicationRunning(IOS_WEBKIT_DEBUG_PROXY):
100      return []
101
102  device_urls = backend.GetDeviceUrls()
103  if not device_urls:
104    logging.debug('Could not find any devices over %s.'
105                  % IOS_WEBKIT_DEBUG_PROXY)
106    return []
107
108  debug_urls = backend.GetWebSocketDebuggerUrls(device_urls)
109
110  # Get the userAgent for each UIWebView to find the browsers.
111  browser_pattern = ('\)\s(%s)\/(\d+[\.\d]*)\sMobile'
112                     % '|'.join(IOS_BROWSERS.keys()))
113  browser_types = set()
114  for url in debug_urls:
115    context = {'webSocketDebuggerUrl':url , 'id':1}
116    inspector = inspector_backend.InspectorBackend(backend, context)
117    res = inspector.EvaluateJavaScript("navigator.userAgent")
118    match_browsers = re.search(browser_pattern, res)
119    if match_browsers:
120      browser_types.add(match_browsers.group(1))
121
122  browsers = []
123  for browser_type in browser_types:
124    browsers.append(PossibleIOSBrowser(IOS_BROWSERS[browser_type],
125                                       finder_options))
126  return list(browsers)
127