Esempio n. 1
0
 def _WaitForBrowserToComeUp(self):
   """ Wait for browser to come up. """
   try:
     timeout = self.browser_options.browser_startup_timeout
     util.WaitFor(self.HasBrowserFinishedLaunching, timeout=timeout)
   except (exceptions.TimeoutException, exceptions.ProcessGoneException) as e:
     if not self.IsBrowserRunning():
       raise exceptions.BrowserGoneException(self.browser, e)
     raise exceptions.BrowserConnectionGoneException(self.browser, e)
 def pid(self):
     pids = self.device.GetPids(self._backend_settings.package)
     if not pids or self._backend_settings.package not in pids:
         raise exceptions.BrowserGoneException(self.browser)
     if len(pids[self._backend_settings.package]) > 1:
         raise Exception(
             'At most one instance of process %s expected but found pids: '
             '%s' % (self._backend_settings.package, pids))
     return int(pids[self._backend_settings.package][0])
 def ValidateAndMeasurePage(self, page, tab, results):
     # This value should be discarded on the first run when the
     # browser crashed.
     results.AddValue(
         string.StringValue(page, 'test', 't',
                            str(self.has_crashed)))
     if not self.has_crashed:
         self.has_crashed = True
         raise exceptions.BrowserGoneException(tab.browser)
Esempio n. 4
0
 def testRaiseBrowserGoneExceptionFromRunPage(self):
   self.RunStories([
       test_stories.DummyStory(
           'foo', run_side_effect=exceptions.BrowserGoneException(
               None, 'i am a browser crash message')),
       test_stories.DummyStory('bar')])
   test_results = self.ReadTestResults()
   self.assertEqual(['FAIL', 'PASS'],
                    [test['status'] for test in test_results])
   self.assertIn('i am a browser crash message', sys.stderr.getvalue())
Esempio n. 5
0
 def BindDevToolsClient(self):
   super(AndroidBrowserBackend, self).BindDevToolsClient()
   package = self.devtools_client.GetVersion().get('Android-Package')
   if package is None:
     logging.warning('Could not determine package name from DevTools client.')
   elif package == self._backend_settings.package:
     logging.info('Successfully connected to %s DevTools client', package)
   else:
     raise exceptions.BrowserGoneException(
         self.browser, 'Expected connection to %s but got %s.' % (
             self._backend_settings.package, package))
Esempio n. 6
0
    def _WaitForBrowserToComeUp(self, wait_for_extensions=True):
        try:
            util.WaitFor(self.HasBrowserFinishedLaunching, timeout=30)
        except (util.TimeoutException, exceptions.ProcessGoneException) as e:
            if not self.IsBrowserRunning():
                raise exceptions.BrowserGoneException(self.browser, e)
            raise exceptions.BrowserConnectionGoneException(self.browser, e)

        def AllExtensionsLoaded():
            # Extension pages are loaded from an about:blank page,
            # so we need to check that the document URL is the extension
            # page in addition to the ready state.
            extension_ready_js = """
          document.URL.lastIndexOf('chrome-extension://%s/', 0) == 0 &&
          (document.readyState == 'complete' ||
           document.readyState == 'interactive')
      """
            for e in self._extensions_to_load:
                try:
                    extension_objects = self.extension_backend[e.extension_id]
                except KeyError:
                    return False
                for extension_object in extension_objects:
                    try:
                        res = extension_object.EvaluateJavaScript(
                            extension_ready_js % e.extension_id)
                    except exceptions.EvaluateException:
                        # If the inspected page is not ready, we will get an error
                        # when we evaluate a JS expression, but we can just keep polling
                        # until the page is ready (crbug.com/251913).
                        res = None

                    # TODO(tengs): We don't have full support for getting the Chrome
                    # version before launch, so for now we use a generic workaround to
                    # check for an extension binding bug in old versions of Chrome.
                    # See crbug.com/263162 for details.
                    if res and extension_object.EvaluateJavaScript(
                            'chrome.runtime == null'):
                        extension_object.Reload()
                    if not res:
                        return False
            return True

        if wait_for_extensions and self._supports_extensions:
            try:
                util.WaitFor(AllExtensionsLoaded, timeout=60)
            except util.TimeoutException:
                logging.error(
                    'ExtensionsToLoad: ' +
                    repr([e.extension_id for e in self._extensions_to_load]))
                logging.error('Extension list: ' +
                              pprint.pformat(self.extension_backend, indent=4))
                raise
Esempio n. 7
0
    def _SetPreferencesIfNeeded(self, is_content_shell):
        # TODO(bulach): Once --enable-remote-debugging flag makes its way to the
        # oldest version under test (m27 goes to stable), remove this function.
        if (is_content_shell
                or not self._adb.Adb().CanAccessProtectedFileContents()):
            return

        prefs_file = self._profile_dir + 'Default/Preferences'
        # Reuse the previous preferences if available, otherwise take the slow path
        # (launch chrome and wait for it to be created).
        if AndroidBrowserBackend._default_preferences_file:
            self._adb.Adb().SetProtectedFileContents(
                prefs_file, AndroidBrowserBackend._default_preferences_file)
            return

        # Make sure we can find the apps' prefs file
        if not self._adb.FileExistsOnDevice(prefs_file):
            # Start it up the first time so we can tweak the prefs.
            self._adb.StartActivity(self._package, self._activity, True, None,
                                    None)
            retries = 0
            timeout = 3
            time.sleep(timeout)
            while not self._adb.Adb().GetProtectedFileContents(prefs_file):
                time.sleep(timeout)
                retries += 1
                timeout *= 2
                if retries == 3:
                    logging.critical(
                        'android_browser_backend: Could not find '
                        'preferences file %s for %s', prefs_file,
                        self._package)
                    raise exceptions.BrowserGoneException(
                        'Missing preferences file.')
            self._adb.CloseApplication(self._package)

        preferences = json.loads(''.join(
            self._adb.Adb().GetProtectedFileContents(prefs_file)))
        changed = False
        if 'devtools' not in preferences:
            preferences['devtools'] = {}
            changed = True
        if not preferences['devtools'].get('remote_enabled'):
            preferences['devtools']['remote_enabled'] = True
            changed = True
        AndroidBrowserBackend._default_preferences_file = json.dumps(
            preferences, indent=2)
        if changed:
            logging.warning('Manually enabled devtools protocol on %s' %
                            self._package)
            self._adb.Adb().SetProtectedFileContents(
                prefs_file, AndroidBrowserBackend._default_preferences_file)
Esempio n. 8
0
    def _Connect(self):
        if self._socket:
            return
        try:
            self._socket = websocket.create_connection(self._debugger_url)
        except (websocket.WebSocketException):
            if self._browser_backend.IsBrowserRunning():
                raise exceptions.TabCrashException(sys.exc_info()[1])
            else:
                raise exceptions.BrowserGoneException()

        self._cur_socket_timeout = 0
        self._next_request_id = 0
 def Request(self, path, timeout=None, throw_network_exception=False):
     url = 'http://localhost:%i/json' % self._port
     if path:
         url += '/' + path
     try:
         req = urllib2.urlopen(url, timeout=timeout)
         return req.read()
     except (socket.error, httplib.BadStatusLine, urllib2.URLError) as e:
         if throw_network_exception:
             raise e
         if not self.IsBrowserRunning():
             raise exceptions.BrowserGoneException()
         raise exceptions.BrowserConnectionGoneException()
 def pid(self):
     try:
         pid = self.device.GetApplicationPids(
             self._backend_settings.package, at_most_one=True)
     except device_errors.CommandFailedError as exc:
         logging.warning('Dumping output of "ps" command for diagnosis:')
         for line in self.device.RunShellCommand(['ps']):
             logging.warning('- %s', line)
         # This may happen if we end up with two PIDs for the browser process.
         # Re-raise as an AppCrashException to get further debug information.
         raise exceptions.AppCrashException(
             self.browser, 'Error getting browser PID: %s' % exc)
     if not pid:
         raise exceptions.BrowserGoneException(self.browser)
     return int(pid)
 def Request(self, path, timeout=30, throw_network_exception=False):
   url = 'http://127.0.0.1:%i/json' % self._port
   if path:
     url += '/' + path
   try:
     proxy_handler = urllib2.ProxyHandler({})  # Bypass any system proxy.
     opener = urllib2.build_opener(proxy_handler)
     with contextlib.closing(opener.open(url, timeout=timeout)) as req:
       return req.read()
   except (socket.error, httplib.BadStatusLine, urllib2.URLError) as e:
     if throw_network_exception:
       raise e
     if not self.IsBrowserRunning():
       raise exceptions.BrowserGoneException(self.browser, e)
     raise exceptions.BrowserConnectionGoneException(self.browser, e)
Esempio n. 12
0
    def _ListTabs(self, timeout=None):
        def _IsTab(context):
            if 'type' in context:
                return context['type'] == 'page'
            # TODO: For compatibility with Chrome before r177683.
            # This check is not completely correct, see crbug.com/190592.
            return not context['url'].startswith('chrome-extension://')

        try:
            data = self._browser_backend.Request('', timeout=timeout)
            all_contexts = json.loads(data)
            tabs = filter(_IsTab, all_contexts)
            return tabs
        except (socket.error, httplib.BadStatusLine, urllib2.URLError):
            if not self._browser_backend.IsBrowserRunning():
                raise exceptions.BrowserGoneException()
            raise exceptions.BrowserConnectionGoneException()
Esempio n. 13
0
    def _Connect(self, timeout=10):
        assert not self._socket
        logging.debug('InspectorBackend._Connect() to %s' % self.debugger_url)
        try:
            self._socket = websocket.create_connection(self.debugger_url,
                                                       timeout=timeout)
        except (websocket.WebSocketException, util.TimeoutException):
            err_msg = sys.exc_info()[1]
            if not self._browser_backend.IsBrowserRunning():
                raise exceptions.BrowserGoneException(err_msg)
            elif not self._browser_backend.HasBrowserFinishedLaunching():
                raise exceptions.BrowserConnectionGoneException(err_msg)
            else:
                raise exceptions.TabCrashException(err_msg)

        self._cur_socket_timeout = 0
        self._next_request_id = 0
Esempio n. 14
0
 def Foreground(self):
   package = self._backend_settings.package
   activity = self._backend_settings.activity
   self.device.StartActivity(
       intent.Intent(package=package,
                     activity=activity,
                     action=None,
                     flags=[intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED]),
       blocking=False)
   # TODO(crbug.com/601052): The following waits for any UI node for the
   # package launched to appear on the screen. When the referenced bug is
   # fixed, remove this workaround and just switch blocking above to True.
   try:
     app_ui.AppUi(self.device).WaitForUiNode(package=package)
   except Exception:
     raise exceptions.BrowserGoneException(self.browser,
         'Timed out waiting for browser to come back foreground.')
Esempio n. 15
0
    def _WaitForBrowserToComeUp(self, remote_devtools_port=None):
        """ Wait for browser to come up.

    Args:
      remote_devtools_port: The remote devtools port, if
          any. Otherwise assumed to be the same as self._port.
    """
        try:
            timeout = self.browser_options.browser_startup_timeout
            py_utils.WaitFor(self.HasBrowserFinishedLaunching, timeout=timeout)
        except (py_utils.TimeoutException,
                exceptions.ProcessGoneException) as e:
            if not self.IsBrowserRunning():
                raise exceptions.BrowserGoneException(self.browser, e)
            raise exceptions.BrowserConnectionGoneException(self.browser, e)
        self._devtools_client = devtools_client_backend.DevToolsClientBackend(
            self._port, self._browser_target, remote_devtools_port
            or self._port, self)
Esempio n. 16
0
  def BindDevToolsClient(self):
    """Find an existing DevTools agent and bind this browser backend to it."""
    if self._devtools_client:
      # In case we are launching a second browser instance (as is done by
      # the CrOS backend), ensure that the old devtools_client is closed,
      # otherwise re-creating it will fail.
      self._devtools_client.Close()
      self._devtools_client = None

    try:
      self._devtools_client = py_utils.WaitFor(
          self._GetDevToolsClient,
          timeout=self.browser_options.browser_startup_timeout)
    except (py_utils.TimeoutException, exceptions.ProcessGoneException) as e:
      if not self.IsBrowserRunning():
        logging.exception(e)  # crbug.com/940075
        raise exceptions.BrowserGoneException(self.browser, e)
      raise exceptions.BrowserConnectionGoneException(self.browser, e)
 def GetDevtoolsRemotePort(self):
   # The DevTools socket name for WebView depends on the activity PID's.
   retries = 0
   timeout = 1
   pid = None
   while True:
     pids = self.adb.ExtractPid(self.package)
     if (len(pids) > 0):
       pid = pids[-1]
       break
     time.sleep(timeout)
     retries += 1
     timeout *= 2
     if retries == 4:
       logging.critical('android_browser_backend: Timeout while waiting for '
                        'activity %s:%s to come up',
                        self.package,
                        self.activity)
       raise exceptions.BrowserGoneException('Timeout waiting for PID.')
   return 'localabstract:webview_devtools_remote_%s' % str(pid)
Esempio n. 18
0
 def GetDevtoolsRemotePort(self, device):
   # The DevTools socket name for WebView depends on the activity PID's.
   retries = 0
   timeout = 1
   pid = None
   while True:
     pids = device.GetPids(self.package)
     if not pids or self.package not in pids:
       time.sleep(timeout)
       retries += 1
       timeout *= 2
       if retries == 4:
         logging.critical('android_browser_backend: Timeout while waiting for '
                          'activity %s:%s to come up',
                          self.package,
                          self.activity)
         raise exceptions.BrowserGoneException(self.browser,
                                               'Timeout waiting for PID.')
     pid = pids[self.package]
     break
   return 'localabstract:webview_devtools_remote_%s' % str(pid)
Esempio n. 19
0
    def BindDevToolsClient(self):
        """Find an existing DevTools agent and bind this browser backend to it."""
        if self._devtools_client:
            # In case we are launching a second browser instance (as is done by
            # the CrOS backend), ensure that the old devtools_client is closed,
            # otherwise re-creating it will fail.
            self._devtools_client.Close()
            self._devtools_client = None

        try:
            timeout = self.browser_options.browser_startup_timeout
            # TODO(crbug.com/787834): Subclasses should WaitFor the config if needed.
            devtools_config = py_utils.WaitFor(self._GetDevToolsClientConfig,
                                               timeout=timeout)
            self._devtools_client = devtools_config.WaitForAndCreate(
                timeout=timeout)
        except (py_utils.TimeoutException,
                exceptions.ProcessGoneException) as e:
            if not self.IsBrowserRunning():
                raise exceptions.BrowserGoneException(self.browser, e)
            raise exceptions.BrowserConnectionGoneException(self.browser, e)
Esempio n. 20
0
  def _WaitForBrowserToComeUp(self, remote_devtools_port=None):
    """ Wait for browser to come up.

    Args:
      remote_devtools_port: The remote devtools port, if
          any. Otherwise assumed to be the same as self._port.
    """
    if self._devtools_client:
      # In case we are launching a second browser instance (as is done by
      # the CrOS backend), ensure that the old devtools_client is closed,
      # otherwise re-creating it will fail.
      self._devtools_client.Close()
      self._devtools_client = None
    try:
      timeout = self.browser_options.browser_startup_timeout
      py_utils.WaitFor(self.HasBrowserFinishedLaunching, timeout=timeout)
    except (py_utils.TimeoutException, exceptions.ProcessGoneException) as e:
      if not self.IsBrowserRunning():
        raise exceptions.BrowserGoneException(self.browser, e)
      raise exceptions.BrowserConnectionGoneException(self.browser, e)
    self._devtools_client = devtools_client_backend.DevToolsClientBackend(
        self._port, self._browser_target,
        remote_devtools_port or self._port, self)
Esempio n. 21
0
  def pid(self):
    try:
      # Although there might be multiple processes sharing the same name as
      # the browser app, the browser process is the only one being a direct
      # descendant of an Android zygote. (See crbug.com/785446)
      zygotes = self.device.ListProcesses('zygote')
      zygote_pids = set(p.pid for p in zygotes)
      assert zygote_pids, 'No Android zygote found'

      processes = self.device.ListProcesses(self._backend_settings.package)
      pids = []
      for process in processes:
        if (process.name == self._backend_settings.package and
            process.ppid in zygote_pids):
          pids.append(process.pid)
      assert len(pids) <= 1, 'Found too many browsers: %r' % pids
    except Exception as exc:
      # Re-raise as an AppCrashException to get further diagnostic information.
      # In particular we also get the values of all local variables above.
      raise exceptions.AppCrashException(
          self.browser, 'Error getting browser PID: %s' % exc)
    if not pids:
      raise exceptions.BrowserGoneException(self.browser)
    return pids[0]
    def _WaitForBrowserToComeUp(self, timeout=None):
        def IsBrowserUp():
            try:
                self.Request('', timeout=timeout)
            except (exceptions.BrowserGoneException,
                    exceptions.BrowserConnectionGoneException):
                return False
            else:
                return True

        try:
            util.WaitFor(IsBrowserUp, timeout=30)
        except util.TimeoutException:
            raise exceptions.BrowserGoneException(self.GetStackTrace())

        def AllExtensionsLoaded():
            # Extension pages are loaded from an about:blank page,
            # so we need to check that the document URL is the extension
            # page in addition to the ready state.
            extension_ready_js = """
          document.URL.lastIndexOf('chrome-extension://%s/', 0) == 0 &&
          (document.readyState == 'complete' ||
           document.readyState == 'interactive')
      """
            for e in self.options.extensions_to_load:
                if not e.extension_id in self._extension_dict_backend:
                    return False
                extension_object = self._extension_dict_backend[e.extension_id]
                res = extension_object.EvaluateJavaScript(extension_ready_js %
                                                          e.extension_id)
                if not res:
                    return False
            return True

        if self._supports_extensions:
            util.WaitFor(AllExtensionsLoaded, timeout=30)
 def MeasurePage(self, *_):
     if not self.has_crashed:
         self.has_crashed = True
         raise exceptions.BrowserGoneException()
 def RunPage(self, *_):
   old_run_count = self.run_count
   self.run_count += 1
   if old_run_count == 0:
     raise exceptions.BrowserGoneException('i am a browser instance')
    def __init__(self, options, adb, package, is_content_shell, cmdline_file,
                 activity, devtools_remote_port):
        super(AndroidBrowserBackend,
              self).__init__(is_content_shell=is_content_shell,
                             supports_extensions=False,
                             options=options)
        if len(options.extensions_to_load) > 0:
            raise browser_backend.ExtensionsNotSupportedException(
                'Android browser does not support extensions.')
        # Initialize fields so that an explosion during init doesn't break in Close.
        self._options = options
        self._adb = adb
        self._package = package
        self._cmdline_file = cmdline_file
        self._saved_cmdline = None
        self._activity = activity
        if not options.keep_test_server_ports:
            adb_commands.ResetTestServerPortAllocation()
        self._port = adb_commands.AllocateTestServerPort()
        self._devtools_remote_port = devtools_remote_port
        self._profile_dir = '/data/data/%s/' % self._package
        if is_content_shell:
            self._profile_dir += 'app_content_shell/'
        else:
            self._profile_dir += 'app_chrome/'

        # Kill old browser.
        self._adb.CloseApplication(self._package)
        self._adb.KillAll('device_forwarder')
        self._adb.Forward('tcp:%d' % self._port, self._devtools_remote_port)

        if self._adb.Adb().CanAccessProtectedFileContents():
            if not options.dont_override_profile:
                self._adb.RunShellCommand('su -c rm -r "%s"' %
                                          self._profile_dir)
            if options.profile_dir:
                if is_content_shell:
                    logging.critical(
                        'Profiles cannot be used with content shell')
                    sys.exit(1)
                self._adb.Push(options.profile_dir, self._profile_dir)

        # Set up the command line.
        self._saved_cmdline = ''.join(
            self._adb.Adb().GetProtectedFileContents(cmdline_file) or [])
        if is_content_shell:
            pseudo_exec_name = 'content_shell'
        else:
            pseudo_exec_name = 'chrome'

        args = [pseudo_exec_name]
        args.extend(self.GetBrowserStartupArgs())

        def QuoteIfNeeded(arg):
            # Escape 'key=valueA valueB' to 'key="valueA valueB"'
            # Already quoted values, or values without space are left untouched.
            # This is required so CommandLine.java can parse valueB correctly rather
            # than as a separate switch.
            params = arg.split('=')
            if len(params) != 2:
                return arg
            key, values = params
            if ' ' not in values:
                return arg
            if values[0] in '"\'' and values[-1] == values[0]:
                return arg
            return '%s="%s"' % (key, values)

        args = map(QuoteIfNeeded, args)
        self._adb.Adb().SetProtectedFileContents(cmdline_file, ' '.join(args))

        # TODO: Once --enable-remote-debugging flag makes its way to the oldest
        # version under test (m27 goes to stable), remove this hack.
        # Force devtools protocol on, if not already done and we can access
        # protected files.
        if (not is_content_shell
                and self._adb.Adb().CanAccessProtectedFileContents()):
            # Make sure we can find the apps' prefs file
            prefs_file = self._profile_dir + 'Default/Preferences'
            if not self._adb.FileExistsOnDevice(prefs_file):
                # Start it up the first time so we can tweak the prefs.
                self._adb.StartActivity(self._package, self._activity, True,
                                        None, None)
                retries = 0
                timeout = 3
                time.sleep(timeout)
                while not self._adb.Adb().GetProtectedFileContents(prefs_file):
                    time.sleep(timeout)
                    retries += 1
                    timeout *= 2
                    if retries == 3:
                        logging.critical(
                            'android_browser_backend: Could not find '
                            'preferences file %s for %s', prefs_file,
                            self._package)
                        raise exceptions.BrowserGoneException(
                            'Missing preferences file.')
                self._adb.CloseApplication(self._package)

            preferences = json.loads(''.join(
                self._adb.Adb().GetProtectedFileContents(prefs_file)))
            changed = False
            if 'devtools' not in preferences:
                preferences['devtools'] = {}
                changed = True
            if not preferences['devtools'].get('remote_enabled'):
                preferences['devtools']['remote_enabled'] = True
                changed = True
            if changed:
                logging.warning('Manually enabled devtools protocol on %s' %
                                self._package)
                txt = json.dumps(preferences, indent=2)
                self._adb.Adb().SetProtectedFileContents(prefs_file, txt)

        # Start it up with a fresh log.
        self._adb.RunShellCommand('logcat -c')
        self._adb.StartActivity(self._package, self._activity, True, None,
                                'chrome://newtab/')
        try:
            self._WaitForBrowserToComeUp()
            self._PostBrowserStartupInitialization()
        except exceptions.BrowserGoneException:
            logging.critical('Failed to connect to browser.')
            if not self._adb.IsRootEnabled():
                logging.critical(
                    'Ensure web debugging is enabled in Chrome at '
                    '"Settings > Developer tools > Enable USB Web debugging".')
            sys.exit(1)
        except:
            import traceback
            traceback.print_exc()
            self.Close()
            raise
 def pid(self):
   pids = self._adb.ExtractPid(self._backend_settings.package)
   if not pids:
     raise exceptions.BrowserGoneException(self.browser)
   return int(pids[0])
Esempio n. 27
0
 def MeasurePage(self, _, tab, __):
     if not self.has_crashed:
         self.has_crashed = True
         raise exceptions.BrowserGoneException(tab.browser)
Esempio n. 28
0
 def RunPage(self, *_):
     old_run_count = self.run_count
     self.run_count += 1
     if old_run_count == 0:
         raise exceptions.BrowserGoneException(
             None, 'i am a browser crash message')
 def pid(self):
     pids = self.device.GetPids(self._package)
     if not pids or self._package not in pids:
         raise exceptions.BrowserGoneException(self.browser)
     return int(pids[self._package])
Esempio n. 30
0
 def RestartBrowserBeforeEachPage(self):
     old_run_count = self.run_count
     self.run_count += 1
     if old_run_count == 0:
         raise exceptions.BrowserGoneException(None)
     return self._needs_browser_restart_after_each_page