tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

chrome_driver_wrapper.py (4200B)


      1 # Copyright 2024 The Chromium Authors
      2 # Use of this source code is governed by a BSD-style license that can be
      3 # found in the LICENSE file.
      4 """ Starts a web engine shell on an existing fuchsia device, and returns a
      5    ChromeDriver instance to control it."""
      6 
      7 import logging
      8 import os
      9 import subprocess
     10 
     11 from contextlib import AbstractContextManager
     12 from typing import List
     13 
     14 # From vpython wheel.
     15 # pylint: disable=import-error
     16 from selenium import webdriver
     17 from selenium.webdriver import ChromeOptions
     18 from selenium.webdriver.chrome.service import Service
     19 from selenium.webdriver.common.by import By
     20 # pylint: enable=import-error
     21 
     22 from common import get_ffx_isolate_dir, get_free_local_port
     23 from isolate_daemon import IsolateDaemon
     24 from run_webpage_test import capture_devtools_addr
     25 
     26 LOG_DIR = os.environ.get('ISOLATED_OUTDIR', '/tmp')
     27 
     28 
     29 class ChromeDriverWrapper(AbstractContextManager):
     30    """Manages the web engine shell on the device and the chromedriver
     31    communicating with it. This class expects the chromedriver exists at
     32    clang_x64/stripped/chromedriver in output dir."""
     33 
     34    def __init__(self, extra_args: List[str] = None):
     35        # The reference of the webdriver.Chrome instance.
     36        self._driver = None
     37 
     38        # Creates the isolate dir for daemon to ensure it can be shared across
     39        # the processes. Note, it has no effect if isolate_dir has already been
     40        # set.
     41        self._isolate_dir = IsolateDaemon.IsolateDir()
     42 
     43        # The process of the run_test.py webpage.
     44        self._proc: subprocess.Popen = None
     45 
     46        # Extra arguments sent to run_test.py webpage process.
     47        self._extra_args = extra_args or []
     48 
     49    def __enter__(self):
     50        """Starts the run_test.py and the chromedriver connecting to it, must be
     51        executed before other commands."""
     52        self._isolate_dir.__enter__()
     53        logging.warning('ffx daemon is running in %s', get_ffx_isolate_dir())
     54 
     55        self._proc = subprocess.Popen([
     56            os.path.join(os.path.dirname(os.path.abspath(__file__)),
     57                         'run_test.py'), 'webpage', '--out-dir=.',
     58            '--browser=web-engine-shell', '--device', f'--logs-dir={LOG_DIR}'
     59        ] + self._extra_args,
     60                                      env={
     61                                          **os.environ, 'CHROME_HEADLESS': '1'
     62                                      })
     63        address, port = capture_devtools_addr(self._proc, LOG_DIR)
     64        logging.warning('DevTools is now running on %s:%s', address, port)
     65 
     66        options = ChromeOptions()
     67        options.debugger_address = f'{address}:{str(port)}'
     68        # The port webdriver running on is not very interesting, the _driver
     69        # instance will be used directly. So a random free local port is used.
     70        self._driver = webdriver.Chrome(options=options,
     71                                        service=Service(
     72                                            os.path.join(
     73                                                'clang_x64', 'stripped',
     74                                                'chromedriver'),
     75                                            get_free_local_port()))
     76        self._driver.__enter__()
     77        return self
     78 
     79    def __exit__(self, exc_type, exc_val, exc_tb) -> bool:
     80        """Stops the run_test.py and the chromedriver, cannot perform other
     81        commands afterward."""
     82        try:
     83            return self._driver.__exit__(exc_type, exc_val, exc_tb)
     84        finally:
     85            self._proc.terminate()
     86            self._proc.wait()
     87            self._isolate_dir.__exit__(exc_type, exc_val, exc_tb)
     88 
     89    def __getattr__(self, name):
     90        """Forwards function calls to the underlying |_driver| instance."""
     91        return getattr(self._driver, name)
     92 
     93    # Explicitly override find_element_by_id to avoid importing selenium
     94    # packages in the caller files.
     95    # The find_element_by_id in webdriver.Chrome is deprecated.
     96    #   DeprecationWarning: find_element_by_* commands are deprecated. Please
     97    #   use find_element() instead
     98    def find_element_by_id(self, id_str):
     99        """Returns the element in the page with id |id_str|."""
    100        return self._driver.find_element(By.ID, id_str)