Path: blob/trunk/py/selenium/webdriver/chromium/options.py
1864 views
# Licensed to the Software Freedom Conservancy (SFC) under one1# or more contributor license agreements. See the NOTICE file2# distributed with this work for additional information3# regarding copyright ownership. The SFC licenses this file4# to you under the Apache License, Version 2.0 (the5# "License"); you may not use this file except in compliance6# with the License. You may obtain a copy of the License at7#8# http://www.apache.org/licenses/LICENSE-2.09#10# Unless required by applicable law or agreed to in writing,11# software distributed under the License is distributed on an12# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY13# KIND, either express or implied. See the License for the14# specific language governing permissions and limitations15# under the License.1617import base6418import os19from typing import BinaryIO, Optional, Union2021from selenium.webdriver.common.desired_capabilities import DesiredCapabilities22from selenium.webdriver.common.options import ArgOptions232425class ChromiumOptions(ArgOptions):26KEY = "goog:chromeOptions"2728def __init__(self) -> None:29super().__init__()30self._binary_location: str = ""31self._extension_files: list[str] = []32self._extensions: list[str] = []33self._experimental_options: dict[str, Union[str, int, dict, list[str]]] = {}34self._debugger_address: Optional[str] = None35self._enable_webextensions: bool = False3637@property38def binary_location(self) -> str:39""":Returns: The location of the binary, otherwise an empty string."""40return self._binary_location4142@binary_location.setter43def binary_location(self, value: str) -> None:44"""Allows you to set where the chromium binary lives.4546Parameters:47----------48value: path to the Chromium binary49"""50if not isinstance(value, str):51raise TypeError(self.BINARY_LOCATION_ERROR)52self._binary_location = value5354@property55def debugger_address(self) -> Optional[str]:56""":Returns: The address of the remote devtools instance."""57return self._debugger_address5859@debugger_address.setter60def debugger_address(self, value: str) -> None:61"""Allows you to set the address of the remote devtools instance that62the ChromeDriver instance will try to connect to during an active wait.6364Parameters:65----------66value: address of remote devtools instance if any (hostname[:port])67"""68if not isinstance(value, str):69raise TypeError("Debugger Address must be a string")70self._debugger_address = value7172@property73def extensions(self) -> list[str]:74""":Returns: A list of encoded extensions that will be loaded."""7576def _decode(file_data: BinaryIO) -> str:77# Should not use base64.encodestring() which inserts newlines every78# 76 characters (per RFC 1521). Chromedriver has to remove those79# unnecessary newlines before decoding, causing performance hit.80return base64.b64encode(file_data.read()).decode("utf-8")8182encoded_extensions = []83for extension in self._extension_files:84with open(extension, "rb") as f:85encoded_extensions.append(_decode(f))8687return encoded_extensions + self._extensions8889def add_extension(self, extension: str) -> None:90"""Adds the path to the extension to a list that will be used to91extract it to the ChromeDriver.9293Parameters:94----------95extension: path to the \\*.crx file96"""97if extension:98extension_to_add = os.path.abspath(os.path.expanduser(extension))99if os.path.exists(extension_to_add):100self._extension_files.append(extension_to_add)101else:102raise OSError("Path to the extension doesn't exist")103else:104raise ValueError("argument can not be null")105106def add_encoded_extension(self, extension: str) -> None:107"""Adds Base64 encoded string with extension data to a list that will108be used to extract it to the ChromeDriver.109110Parameters:111----------112extension: Base64 encoded string with extension data113"""114if extension:115self._extensions.append(extension)116else:117raise ValueError("argument can not be null")118119@property120def experimental_options(self) -> dict:121""":Returns: A dictionary of experimental options for chromium."""122return self._experimental_options123124def add_experimental_option(self, name: str, value: Union[str, int, dict, list[str]]) -> None:125"""Adds an experimental option which is passed to chromium.126127Parameters:128----------129name: The experimental option name.130value: The option value.131"""132self._experimental_options[name] = value133134@property135def enable_webextensions(self) -> bool:136""":Returns: Whether webextension support is enabled for Chromium-based browsers.137True if webextension support is enabled, False otherwise.138"""139return self._enable_webextensions140141@enable_webextensions.setter142def enable_webextensions(self, value: bool) -> None:143"""Enables or disables webextension support for Chromium-based browsers.144145Parameters:146----------147value : bool148True to enable webextension support, False to disable.149150Notes:151-----152- When enabled, this automatically adds the required Chromium flags:153- --enable-unsafe-extension-debugging154- --remote-debugging-pipe155- When disabled, this removes BOTH flags listed above, even if they were manually added via add_argument()156before enabling webextensions.157- Enabling --remote-debugging-pipe makes the connection b/w chromedriver158and the browser use a pipe instead of a port, disabling many CDP functionalities159like devtools160"""161self._enable_webextensions = value162if value:163# Add required flags for Chromium webextension support164required_flags = ["--enable-unsafe-extension-debugging", "--remote-debugging-pipe"]165for flag in required_flags:166if flag not in self._arguments:167self.add_argument(flag)168else:169# Remove webextension flags if disabling170flags_to_remove = ["--enable-unsafe-extension-debugging", "--remote-debugging-pipe"]171for flag in flags_to_remove:172if flag in self._arguments:173self._arguments.remove(flag)174175def to_capabilities(self) -> dict:176"""Creates a capabilities with all the options that have been set177178Returns:179-------180dict : a dictionary with all set options181"""182caps = self._caps183chrome_options = self.experimental_options.copy()184if self.mobile_options:185chrome_options.update(self.mobile_options)186chrome_options["extensions"] = self.extensions187if self.binary_location:188chrome_options["binary"] = self.binary_location189chrome_options["args"] = self._arguments190if self.debugger_address:191chrome_options["debuggerAddress"] = self.debugger_address192193caps[self.KEY] = chrome_options194195return caps196197@property198def default_capabilities(self) -> dict:199return DesiredCapabilities.CHROME.copy()200201202