Updated script that can be controled by Nodejs web app
This commit is contained in:
19
lib/python3.13/site-packages/selenium/__init__.py
Executable file
19
lib/python3.13/site-packages/selenium/__init__.py
Executable file
@ -0,0 +1,19 @@
|
||||
# Licensed to the Software Freedom Conservancy (SFC) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The SFC licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
|
||||
__version__ = "4.26.1"
|
Binary file not shown.
Binary file not shown.
86
lib/python3.13/site-packages/selenium/common/__init__.py
Executable file
86
lib/python3.13/site-packages/selenium/common/__init__.py
Executable file
@ -0,0 +1,86 @@
|
||||
# Licensed to the Software Freedom Conservancy (SFC) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The SFC licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from .exceptions import ElementClickInterceptedException
|
||||
from .exceptions import ElementNotInteractableException
|
||||
from .exceptions import ElementNotSelectableException
|
||||
from .exceptions import ElementNotVisibleException
|
||||
from .exceptions import ImeActivationFailedException
|
||||
from .exceptions import ImeNotAvailableException
|
||||
from .exceptions import InsecureCertificateException
|
||||
from .exceptions import InvalidArgumentException
|
||||
from .exceptions import InvalidCookieDomainException
|
||||
from .exceptions import InvalidCoordinatesException
|
||||
from .exceptions import InvalidElementStateException
|
||||
from .exceptions import InvalidSelectorException
|
||||
from .exceptions import InvalidSessionIdException
|
||||
from .exceptions import InvalidSwitchToTargetException
|
||||
from .exceptions import JavascriptException
|
||||
from .exceptions import MoveTargetOutOfBoundsException
|
||||
from .exceptions import NoAlertPresentException
|
||||
from .exceptions import NoSuchAttributeException
|
||||
from .exceptions import NoSuchCookieException
|
||||
from .exceptions import NoSuchDriverException
|
||||
from .exceptions import NoSuchElementException
|
||||
from .exceptions import NoSuchFrameException
|
||||
from .exceptions import NoSuchShadowRootException
|
||||
from .exceptions import NoSuchWindowException
|
||||
from .exceptions import ScreenshotException
|
||||
from .exceptions import SessionNotCreatedException
|
||||
from .exceptions import StaleElementReferenceException
|
||||
from .exceptions import TimeoutException
|
||||
from .exceptions import UnableToSetCookieException
|
||||
from .exceptions import UnexpectedAlertPresentException
|
||||
from .exceptions import UnexpectedTagNameException
|
||||
from .exceptions import UnknownMethodException
|
||||
from .exceptions import WebDriverException
|
||||
|
||||
__all__ = [
|
||||
"WebDriverException",
|
||||
"InvalidSwitchToTargetException",
|
||||
"NoSuchFrameException",
|
||||
"NoSuchWindowException",
|
||||
"NoSuchElementException",
|
||||
"NoSuchAttributeException",
|
||||
"NoSuchDriverException",
|
||||
"NoSuchShadowRootException",
|
||||
"StaleElementReferenceException",
|
||||
"InvalidElementStateException",
|
||||
"UnexpectedAlertPresentException",
|
||||
"NoAlertPresentException",
|
||||
"ElementNotVisibleException",
|
||||
"ElementNotInteractableException",
|
||||
"ElementNotSelectableException",
|
||||
"InvalidCookieDomainException",
|
||||
"UnableToSetCookieException",
|
||||
"TimeoutException",
|
||||
"MoveTargetOutOfBoundsException",
|
||||
"UnexpectedTagNameException",
|
||||
"InvalidSelectorException",
|
||||
"ImeNotAvailableException",
|
||||
"ImeActivationFailedException",
|
||||
"InvalidArgumentException",
|
||||
"JavascriptException",
|
||||
"NoSuchCookieException",
|
||||
"ScreenshotException",
|
||||
"ElementClickInterceptedException",
|
||||
"InsecureCertificateException",
|
||||
"InvalidCoordinatesException",
|
||||
"InvalidSessionIdException",
|
||||
"SessionNotCreatedException",
|
||||
"UnknownMethodException",
|
||||
]
|
Binary file not shown.
Binary file not shown.
287
lib/python3.13/site-packages/selenium/common/exceptions.py
Executable file
287
lib/python3.13/site-packages/selenium/common/exceptions.py
Executable file
@ -0,0 +1,287 @@
|
||||
# Licensed to the Software Freedom Conservancy (SFC) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The SFC licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
"""Exceptions that may happen in all the webdriver code."""
|
||||
|
||||
from typing import Optional
|
||||
from typing import Sequence
|
||||
|
||||
SUPPORT_MSG = "For documentation on this error, please visit:"
|
||||
ERROR_URL = "https://www.selenium.dev/documentation/webdriver/troubleshooting/errors"
|
||||
|
||||
|
||||
class WebDriverException(Exception):
|
||||
"""Base webdriver exception."""
|
||||
|
||||
def __init__(
|
||||
self, msg: Optional[str] = None, screen: Optional[str] = None, stacktrace: Optional[Sequence[str]] = None
|
||||
) -> None:
|
||||
super().__init__()
|
||||
self.msg = msg
|
||||
self.screen = screen
|
||||
self.stacktrace = stacktrace
|
||||
|
||||
def __str__(self) -> str:
|
||||
exception_msg = f"Message: {self.msg}\n"
|
||||
if self.screen:
|
||||
exception_msg += "Screenshot: available via screen\n"
|
||||
if self.stacktrace:
|
||||
stacktrace = "\n".join(self.stacktrace)
|
||||
exception_msg += f"Stacktrace:\n{stacktrace}"
|
||||
return exception_msg
|
||||
|
||||
|
||||
class InvalidSwitchToTargetException(WebDriverException):
|
||||
"""Thrown when frame or window target to be switched doesn't exist."""
|
||||
|
||||
|
||||
class NoSuchFrameException(InvalidSwitchToTargetException):
|
||||
"""Thrown when frame target to be switched doesn't exist."""
|
||||
|
||||
|
||||
class NoSuchWindowException(InvalidSwitchToTargetException):
|
||||
"""Thrown when window target to be switched doesn't exist.
|
||||
|
||||
To find the current set of active window handles, you can get a list
|
||||
of the active window handles in the following way::
|
||||
|
||||
print driver.window_handles
|
||||
"""
|
||||
|
||||
|
||||
class NoSuchElementException(WebDriverException):
|
||||
"""Thrown when element could not be found.
|
||||
|
||||
If you encounter this exception, you may want to check the following:
|
||||
* Check your selector used in your find_by...
|
||||
* Element may not yet be on the screen at the time of the find operation,
|
||||
(webpage is still loading) see selenium.webdriver.support.wait.WebDriverWait()
|
||||
for how to write a wait wrapper to wait for an element to appear.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self, msg: Optional[str] = None, screen: Optional[str] = None, stacktrace: Optional[Sequence[str]] = None
|
||||
) -> None:
|
||||
with_support = f"{msg}; {SUPPORT_MSG} {ERROR_URL}#no-such-element-exception"
|
||||
|
||||
super().__init__(with_support, screen, stacktrace)
|
||||
|
||||
|
||||
class NoSuchAttributeException(WebDriverException):
|
||||
"""Thrown when the attribute of element could not be found.
|
||||
|
||||
You may want to check if the attribute exists in the particular
|
||||
browser you are testing against. Some browsers may have different
|
||||
property names for the same property. (IE8's .innerText vs. Firefox
|
||||
.textContent)
|
||||
"""
|
||||
|
||||
|
||||
class NoSuchShadowRootException(WebDriverException):
|
||||
"""Thrown when trying to access the shadow root of an element when it does
|
||||
not have a shadow root attached."""
|
||||
|
||||
|
||||
class StaleElementReferenceException(WebDriverException):
|
||||
"""Thrown when a reference to an element is now "stale".
|
||||
|
||||
Stale means the element no longer appears on the DOM of the page.
|
||||
|
||||
|
||||
Possible causes of StaleElementReferenceException include, but not limited to:
|
||||
* You are no longer on the same page, or the page may have refreshed since the element
|
||||
was located.
|
||||
* The element may have been removed and re-added to the screen, since it was located.
|
||||
Such as an element being relocated.
|
||||
This can happen typically with a javascript framework when values are updated and the
|
||||
node is rebuilt.
|
||||
* Element may have been inside an iframe or another context which was refreshed.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self, msg: Optional[str] = None, screen: Optional[str] = None, stacktrace: Optional[Sequence[str]] = None
|
||||
) -> None:
|
||||
with_support = f"{msg}; {SUPPORT_MSG} {ERROR_URL}#stale-element-reference-exception"
|
||||
|
||||
super().__init__(with_support, screen, stacktrace)
|
||||
|
||||
|
||||
class InvalidElementStateException(WebDriverException):
|
||||
"""Thrown when a command could not be completed because the element is in
|
||||
an invalid state.
|
||||
|
||||
This can be caused by attempting to clear an element that isn't both
|
||||
editable and resettable.
|
||||
"""
|
||||
|
||||
|
||||
class UnexpectedAlertPresentException(WebDriverException):
|
||||
"""Thrown when an unexpected alert has appeared.
|
||||
|
||||
Usually raised when an unexpected modal is blocking the webdriver
|
||||
from executing commands.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
msg: Optional[str] = None,
|
||||
screen: Optional[str] = None,
|
||||
stacktrace: Optional[Sequence[str]] = None,
|
||||
alert_text: Optional[str] = None,
|
||||
) -> None:
|
||||
super().__init__(msg, screen, stacktrace)
|
||||
self.alert_text = alert_text
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f"Alert Text: {self.alert_text}\n{super().__str__()}"
|
||||
|
||||
|
||||
class NoAlertPresentException(WebDriverException):
|
||||
"""Thrown when switching to no presented alert.
|
||||
|
||||
This can be caused by calling an operation on the Alert() class when
|
||||
an alert is not yet on the screen.
|
||||
"""
|
||||
|
||||
|
||||
class ElementNotVisibleException(InvalidElementStateException):
|
||||
"""Thrown when an element is present on the DOM, but it is not visible, and
|
||||
so is not able to be interacted with.
|
||||
|
||||
Most commonly encountered when trying to click or read text of an
|
||||
element that is hidden from view.
|
||||
"""
|
||||
|
||||
|
||||
class ElementNotInteractableException(InvalidElementStateException):
|
||||
"""Thrown when an element is present in the DOM but interactions with that
|
||||
element will hit another element due to paint order."""
|
||||
|
||||
|
||||
class ElementNotSelectableException(InvalidElementStateException):
|
||||
"""Thrown when trying to select an unselectable element.
|
||||
|
||||
For example, selecting a 'script' element.
|
||||
"""
|
||||
|
||||
|
||||
class InvalidCookieDomainException(WebDriverException):
|
||||
"""Thrown when attempting to add a cookie under a different domain than the
|
||||
current URL."""
|
||||
|
||||
|
||||
class UnableToSetCookieException(WebDriverException):
|
||||
"""Thrown when a driver fails to set a cookie."""
|
||||
|
||||
|
||||
class TimeoutException(WebDriverException):
|
||||
"""Thrown when a command does not complete in enough time."""
|
||||
|
||||
|
||||
class MoveTargetOutOfBoundsException(WebDriverException):
|
||||
"""Thrown when the target provided to the `ActionsChains` move() method is
|
||||
invalid, i.e. out of document."""
|
||||
|
||||
|
||||
class UnexpectedTagNameException(WebDriverException):
|
||||
"""Thrown when a support class did not get an expected web element."""
|
||||
|
||||
|
||||
class InvalidSelectorException(WebDriverException):
|
||||
"""Thrown when the selector which is used to find an element does not
|
||||
return a WebElement.
|
||||
|
||||
Currently this only happens when the selector is an xpath expression
|
||||
and it is either syntactically invalid (i.e. it is not a xpath
|
||||
expression) or the expression does not select WebElements (e.g.
|
||||
"count(//input)").
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self, msg: Optional[str] = None, screen: Optional[str] = None, stacktrace: Optional[Sequence[str]] = None
|
||||
) -> None:
|
||||
with_support = f"{msg}; {SUPPORT_MSG} {ERROR_URL}#invalid-selector-exception"
|
||||
|
||||
super().__init__(with_support, screen, stacktrace)
|
||||
|
||||
|
||||
class ImeNotAvailableException(WebDriverException):
|
||||
"""Thrown when IME support is not available.
|
||||
|
||||
This exception is thrown for every IME-related method call if IME
|
||||
support is not available on the machine.
|
||||
"""
|
||||
|
||||
|
||||
class ImeActivationFailedException(WebDriverException):
|
||||
"""Thrown when activating an IME engine has failed."""
|
||||
|
||||
|
||||
class InvalidArgumentException(WebDriverException):
|
||||
"""The arguments passed to a command are either invalid or malformed."""
|
||||
|
||||
|
||||
class JavascriptException(WebDriverException):
|
||||
"""An error occurred while executing JavaScript supplied by the user."""
|
||||
|
||||
|
||||
class NoSuchCookieException(WebDriverException):
|
||||
"""No cookie matching the given path name was found amongst the associated
|
||||
cookies of the current browsing context's active document."""
|
||||
|
||||
|
||||
class ScreenshotException(WebDriverException):
|
||||
"""A screen capture was made impossible."""
|
||||
|
||||
|
||||
class ElementClickInterceptedException(WebDriverException):
|
||||
"""The Element Click command could not be completed because the element
|
||||
receiving the events is obscuring the element that was requested to be
|
||||
clicked."""
|
||||
|
||||
|
||||
class InsecureCertificateException(WebDriverException):
|
||||
"""Navigation caused the user agent to hit a certificate warning, which is
|
||||
usually the result of an expired or invalid TLS certificate."""
|
||||
|
||||
|
||||
class InvalidCoordinatesException(WebDriverException):
|
||||
"""The coordinates provided to an interaction's operation are invalid."""
|
||||
|
||||
|
||||
class InvalidSessionIdException(WebDriverException):
|
||||
"""Occurs if the given session id is not in the list of active sessions,
|
||||
meaning the session either does not exist or that it's not active."""
|
||||
|
||||
|
||||
class SessionNotCreatedException(WebDriverException):
|
||||
"""A new session could not be created."""
|
||||
|
||||
|
||||
class UnknownMethodException(WebDriverException):
|
||||
"""The requested command matched a known URL but did not match any methods
|
||||
for that URL."""
|
||||
|
||||
|
||||
class NoSuchDriverException(WebDriverException):
|
||||
"""Raised when driver is not specified and cannot be located."""
|
||||
|
||||
def __init__(
|
||||
self, msg: Optional[str] = None, screen: Optional[str] = None, stacktrace: Optional[Sequence[str]] = None
|
||||
) -> None:
|
||||
with_support = f"{msg}; {SUPPORT_MSG} {ERROR_URL}/driver_location"
|
||||
|
||||
super().__init__(with_support, screen, stacktrace)
|
0
lib/python3.13/site-packages/selenium/py.typed
Executable file
0
lib/python3.13/site-packages/selenium/py.typed
Executable file
25
lib/python3.13/site-packages/selenium/types.py
Executable file
25
lib/python3.13/site-packages/selenium/types.py
Executable file
@ -0,0 +1,25 @@
|
||||
# Licensed to the Software Freedom Conservancy (SFC) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The SFC licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
"""Selenium type definitions."""
|
||||
|
||||
import typing
|
||||
|
||||
AnyKey = typing.Union[str, int, float]
|
||||
WaitExcTypes = typing.Iterable[typing.Type[Exception]]
|
||||
|
||||
# Service Types
|
||||
SubprocessStdAlias = typing.Union[int, str, typing.IO[typing.Any]]
|
79
lib/python3.13/site-packages/selenium/webdriver/__init__.py
Executable file
79
lib/python3.13/site-packages/selenium/webdriver/__init__.py
Executable file
@ -0,0 +1,79 @@
|
||||
# Licensed to the Software Freedom Conservancy (SFC) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The SFC licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from .chrome.options import Options as ChromeOptions # noqa
|
||||
from .chrome.service import Service as ChromeService # noqa
|
||||
from .chrome.webdriver import WebDriver as Chrome # noqa
|
||||
from .common.action_chains import ActionChains # noqa
|
||||
from .common.desired_capabilities import DesiredCapabilities # noqa
|
||||
from .common.keys import Keys # noqa
|
||||
from .common.proxy import Proxy # noqa
|
||||
from .edge.options import Options as EdgeOptions # noqa
|
||||
from .edge.service import Service as EdgeService # noqa
|
||||
from .edge.webdriver import WebDriver as ChromiumEdge # noqa
|
||||
from .edge.webdriver import WebDriver as Edge # noqa
|
||||
from .firefox.firefox_profile import FirefoxProfile # noqa
|
||||
from .firefox.options import Options as FirefoxOptions # noqa
|
||||
from .firefox.service import Service as FirefoxService # noqa
|
||||
from .firefox.webdriver import WebDriver as Firefox # noqa
|
||||
from .ie.options import Options as IeOptions # noqa
|
||||
from .ie.service import Service as IeService # noqa
|
||||
from .ie.webdriver import WebDriver as Ie # noqa
|
||||
from .remote.webdriver import WebDriver as Remote # noqa
|
||||
from .safari.options import Options as SafariOptions
|
||||
from .safari.service import Service as SafariService # noqa
|
||||
from .safari.webdriver import WebDriver as Safari # noqa
|
||||
from .webkitgtk.options import Options as WebKitGTKOptions # noqa
|
||||
from .webkitgtk.service import Service as WebKitGTKService # noqa
|
||||
from .webkitgtk.webdriver import WebDriver as WebKitGTK # noqa
|
||||
from .wpewebkit.options import Options as WPEWebKitOptions # noqa
|
||||
from .wpewebkit.service import Service as WPEWebKitService # noqa
|
||||
from .wpewebkit.webdriver import WebDriver as WPEWebKit # noqa
|
||||
|
||||
__version__ = "4.26.1"
|
||||
|
||||
# We need an explicit __all__ because the above won't otherwise be exported.
|
||||
__all__ = [
|
||||
"Firefox",
|
||||
"FirefoxProfile",
|
||||
"FirefoxOptions",
|
||||
"FirefoxService",
|
||||
"Chrome",
|
||||
"ChromeOptions",
|
||||
"ChromeService",
|
||||
"Ie",
|
||||
"IeOptions",
|
||||
"IeService",
|
||||
"Edge",
|
||||
"ChromiumEdge",
|
||||
"EdgeOptions",
|
||||
"EdgeService",
|
||||
"Safari",
|
||||
"SafariOptions",
|
||||
"SafariService",
|
||||
"WebKitGTK",
|
||||
"WebKitGTKOptions",
|
||||
"WebKitGTKService",
|
||||
"WPEWebKit",
|
||||
"WPEWebKitOptions",
|
||||
"WPEWebKitService",
|
||||
"Remote",
|
||||
"DesiredCapabilities",
|
||||
"ActionChains",
|
||||
"Proxy",
|
||||
"Keys",
|
||||
]
|
Binary file not shown.
16
lib/python3.13/site-packages/selenium/webdriver/chrome/__init__.py
Executable file
16
lib/python3.13/site-packages/selenium/webdriver/chrome/__init__.py
Executable file
@ -0,0 +1,16 @@
|
||||
# Licensed to the Software Freedom Conservancy (SFC) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The SFC licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
35
lib/python3.13/site-packages/selenium/webdriver/chrome/options.py
Executable file
35
lib/python3.13/site-packages/selenium/webdriver/chrome/options.py
Executable file
@ -0,0 +1,35 @@
|
||||
# Licensed to the Software Freedom Conservancy (SFC) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The SFC licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from typing import Optional
|
||||
|
||||
from selenium.webdriver.chromium.options import ChromiumOptions
|
||||
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
|
||||
|
||||
|
||||
class Options(ChromiumOptions):
|
||||
@property
|
||||
def default_capabilities(self) -> dict:
|
||||
return DesiredCapabilities.CHROME.copy()
|
||||
|
||||
def enable_mobile(
|
||||
self,
|
||||
android_package: Optional[str] = "com.android.chrome",
|
||||
android_activity: Optional[str] = None,
|
||||
device_serial: Optional[str] = None,
|
||||
) -> None:
|
||||
super().enable_mobile(android_package, android_activity, device_serial)
|
42
lib/python3.13/site-packages/selenium/webdriver/chrome/remote_connection.py
Executable file
42
lib/python3.13/site-packages/selenium/webdriver/chrome/remote_connection.py
Executable file
@ -0,0 +1,42 @@
|
||||
# Licensed to the Software Freedom Conservancy (SFC) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The SFC licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from typing import Optional
|
||||
|
||||
from selenium.webdriver import DesiredCapabilities
|
||||
from selenium.webdriver.chromium.remote_connection import ChromiumRemoteConnection
|
||||
from selenium.webdriver.remote.client_config import ClientConfig
|
||||
|
||||
|
||||
class ChromeRemoteConnection(ChromiumRemoteConnection):
|
||||
browser_name = DesiredCapabilities.CHROME["browserName"]
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
remote_server_addr: str,
|
||||
keep_alive: bool = True,
|
||||
ignore_proxy: Optional[bool] = False,
|
||||
client_config: Optional[ClientConfig] = None,
|
||||
) -> None:
|
||||
super().__init__(
|
||||
remote_server_addr=remote_server_addr,
|
||||
vendor_prefix="goog",
|
||||
browser_name=ChromeRemoteConnection.browser_name,
|
||||
keep_alive=keep_alive,
|
||||
ignore_proxy=ignore_proxy,
|
||||
client_config=client_config,
|
||||
)
|
50
lib/python3.13/site-packages/selenium/webdriver/chrome/service.py
Executable file
50
lib/python3.13/site-packages/selenium/webdriver/chrome/service.py
Executable file
@ -0,0 +1,50 @@
|
||||
# Licensed to the Software Freedom Conservancy (SFC) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The SFC licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
import typing
|
||||
|
||||
from selenium.types import SubprocessStdAlias
|
||||
from selenium.webdriver.chromium import service
|
||||
|
||||
|
||||
class Service(service.ChromiumService):
|
||||
"""A Service class that is responsible for the starting and stopping of
|
||||
`chromedriver`.
|
||||
|
||||
:param executable_path: install path of the chromedriver executable, defaults to `chromedriver`.
|
||||
:param port: Port for the service to run on, defaults to 0 where the operating system will decide.
|
||||
:param service_args: (Optional) List of args to be passed to the subprocess when launching the executable.
|
||||
:param log_output: (Optional) int representation of STDOUT/DEVNULL, any IO instance or String path to file.
|
||||
:param env: (Optional) Mapping of environment variables for the new process, defaults to `os.environ`.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
executable_path=None,
|
||||
port: int = 0,
|
||||
service_args: typing.Optional[typing.List[str]] = None,
|
||||
log_output: SubprocessStdAlias = None,
|
||||
env: typing.Optional[typing.Mapping[str, str]] = None,
|
||||
**kwargs,
|
||||
) -> None:
|
||||
super().__init__(
|
||||
executable_path=executable_path,
|
||||
port=port,
|
||||
service_args=service_args,
|
||||
log_output=log_output,
|
||||
env=env,
|
||||
**kwargs,
|
||||
)
|
51
lib/python3.13/site-packages/selenium/webdriver/chrome/webdriver.py
Executable file
51
lib/python3.13/site-packages/selenium/webdriver/chrome/webdriver.py
Executable file
@ -0,0 +1,51 @@
|
||||
# Licensed to the Software Freedom Conservancy (SFC) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The SFC licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from selenium.webdriver.chromium.webdriver import ChromiumDriver
|
||||
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
|
||||
|
||||
from .options import Options
|
||||
from .service import Service
|
||||
|
||||
|
||||
class WebDriver(ChromiumDriver):
|
||||
"""Controls the ChromeDriver and allows you to drive the browser."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
options: Options = None,
|
||||
service: Service = None,
|
||||
keep_alive: bool = True,
|
||||
) -> None:
|
||||
"""Creates a new instance of the chrome driver. Starts the service and
|
||||
then creates new instance of chrome driver.
|
||||
|
||||
:Args:
|
||||
- options - this takes an instance of ChromeOptions
|
||||
- service - Service object for handling the browser driver if you need to pass extra details
|
||||
- keep_alive - Whether to configure ChromeRemoteConnection to use HTTP keep-alive.
|
||||
"""
|
||||
service = service if service else Service()
|
||||
options = options if options else Options()
|
||||
|
||||
super().__init__(
|
||||
browser_name=DesiredCapabilities.CHROME["browserName"],
|
||||
vendor_prefix="goog",
|
||||
options=options,
|
||||
service=service,
|
||||
keep_alive=keep_alive,
|
||||
)
|
16
lib/python3.13/site-packages/selenium/webdriver/chromium/__init__.py
Executable file
16
lib/python3.13/site-packages/selenium/webdriver/chromium/__init__.py
Executable file
@ -0,0 +1,16 @@
|
||||
# Licensed to the Software Freedom Conservancy (SFC) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The SFC licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
153
lib/python3.13/site-packages/selenium/webdriver/chromium/options.py
Executable file
153
lib/python3.13/site-packages/selenium/webdriver/chromium/options.py
Executable file
@ -0,0 +1,153 @@
|
||||
# Licensed to the Software Freedom Conservancy (SFC) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The SFC licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import base64
|
||||
import os
|
||||
from typing import BinaryIO
|
||||
from typing import Dict
|
||||
from typing import List
|
||||
from typing import Optional
|
||||
from typing import Union
|
||||
|
||||
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
|
||||
from selenium.webdriver.common.options import ArgOptions
|
||||
|
||||
|
||||
class ChromiumOptions(ArgOptions):
|
||||
KEY = "goog:chromeOptions"
|
||||
|
||||
def __init__(self) -> None:
|
||||
super().__init__()
|
||||
self._binary_location: str = ""
|
||||
self._extension_files: List[str] = []
|
||||
self._extensions: List[str] = []
|
||||
self._experimental_options: Dict[str, Union[str, int, dict, List[str]]] = {}
|
||||
self._debugger_address: Optional[str] = None
|
||||
|
||||
@property
|
||||
def binary_location(self) -> str:
|
||||
""":Returns: The location of the binary, otherwise an empty string."""
|
||||
return self._binary_location
|
||||
|
||||
@binary_location.setter
|
||||
def binary_location(self, value: str) -> None:
|
||||
"""Allows you to set where the chromium binary lives.
|
||||
|
||||
:Args:
|
||||
- value: path to the Chromium binary
|
||||
"""
|
||||
if not isinstance(value, str):
|
||||
raise TypeError(self.BINARY_LOCATION_ERROR)
|
||||
self._binary_location = value
|
||||
|
||||
@property
|
||||
def debugger_address(self) -> Optional[str]:
|
||||
""":Returns: The address of the remote devtools instance."""
|
||||
return self._debugger_address
|
||||
|
||||
@debugger_address.setter
|
||||
def debugger_address(self, value: str) -> None:
|
||||
"""Allows you to set the address of the remote devtools instance that
|
||||
the ChromeDriver instance will try to connect to during an active wait.
|
||||
|
||||
:Args:
|
||||
- value: address of remote devtools instance if any (hostname[:port])
|
||||
"""
|
||||
if not isinstance(value, str):
|
||||
raise TypeError("Debugger Address must be a string")
|
||||
self._debugger_address = value
|
||||
|
||||
@property
|
||||
def extensions(self) -> List[str]:
|
||||
""":Returns: A list of encoded extensions that will be loaded."""
|
||||
|
||||
def _decode(file_data: BinaryIO) -> str:
|
||||
# Should not use base64.encodestring() which inserts newlines every
|
||||
# 76 characters (per RFC 1521). Chromedriver has to remove those
|
||||
# unnecessary newlines before decoding, causing performance hit.
|
||||
return base64.b64encode(file_data.read()).decode("utf-8")
|
||||
|
||||
encoded_extensions = []
|
||||
for extension in self._extension_files:
|
||||
with open(extension, "rb") as f:
|
||||
encoded_extensions.append(_decode(f))
|
||||
|
||||
return encoded_extensions + self._extensions
|
||||
|
||||
def add_extension(self, extension: str) -> None:
|
||||
"""Adds the path to the extension to a list that will be used to
|
||||
extract it to the ChromeDriver.
|
||||
|
||||
:Args:
|
||||
- extension: path to the \\*.crx file
|
||||
"""
|
||||
if extension:
|
||||
extension_to_add = os.path.abspath(os.path.expanduser(extension))
|
||||
if os.path.exists(extension_to_add):
|
||||
self._extension_files.append(extension_to_add)
|
||||
else:
|
||||
raise OSError("Path to the extension doesn't exist")
|
||||
else:
|
||||
raise ValueError("argument can not be null")
|
||||
|
||||
def add_encoded_extension(self, extension: str) -> None:
|
||||
"""Adds Base64 encoded string with extension data to a list that will
|
||||
be used to extract it to the ChromeDriver.
|
||||
|
||||
:Args:
|
||||
- extension: Base64 encoded string with extension data
|
||||
"""
|
||||
if extension:
|
||||
self._extensions.append(extension)
|
||||
else:
|
||||
raise ValueError("argument can not be null")
|
||||
|
||||
@property
|
||||
def experimental_options(self) -> dict:
|
||||
""":Returns: A dictionary of experimental options for chromium."""
|
||||
return self._experimental_options
|
||||
|
||||
def add_experimental_option(self, name: str, value: Union[str, int, dict, List[str]]) -> None:
|
||||
"""Adds an experimental option which is passed to chromium.
|
||||
|
||||
:Args:
|
||||
name: The experimental option name.
|
||||
value: The option value.
|
||||
"""
|
||||
self._experimental_options[name] = value
|
||||
|
||||
def to_capabilities(self) -> dict:
|
||||
"""Creates a capabilities with all the options that have been set
|
||||
:Returns: A dictionary with everything."""
|
||||
caps = self._caps
|
||||
chrome_options = self.experimental_options.copy()
|
||||
if self.mobile_options:
|
||||
chrome_options.update(self.mobile_options)
|
||||
chrome_options["extensions"] = self.extensions
|
||||
if self.binary_location:
|
||||
chrome_options["binary"] = self.binary_location
|
||||
chrome_options["args"] = self._arguments
|
||||
if self.debugger_address:
|
||||
chrome_options["debuggerAddress"] = self.debugger_address
|
||||
|
||||
caps[self.KEY] = chrome_options
|
||||
|
||||
return caps
|
||||
|
||||
@property
|
||||
def default_capabilities(self) -> dict:
|
||||
return DesiredCapabilities.CHROME.copy()
|
@ -0,0 +1,60 @@
|
||||
# Licensed to the Software Freedom Conservancy (SFC) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The SFC licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
from typing import Optional
|
||||
|
||||
from selenium.webdriver.remote.client_config import ClientConfig
|
||||
from selenium.webdriver.remote.remote_connection import RemoteConnection
|
||||
|
||||
|
||||
class ChromiumRemoteConnection(RemoteConnection):
|
||||
def __init__(
|
||||
self,
|
||||
remote_server_addr: str,
|
||||
vendor_prefix: str,
|
||||
browser_name: str,
|
||||
keep_alive: bool = True,
|
||||
ignore_proxy: Optional[bool] = False,
|
||||
client_config: Optional[ClientConfig] = None,
|
||||
) -> None:
|
||||
client_config = client_config or ClientConfig(
|
||||
remote_server_addr=remote_server_addr, keep_alive=keep_alive, timeout=120
|
||||
)
|
||||
super().__init__(
|
||||
ignore_proxy=ignore_proxy,
|
||||
client_config=client_config,
|
||||
)
|
||||
self.browser_name = browser_name
|
||||
commands = self._remote_commands(vendor_prefix)
|
||||
for key, value in commands.items():
|
||||
self._commands[key] = value
|
||||
|
||||
def _remote_commands(self, vendor_prefix):
|
||||
remote_commands = {
|
||||
"launchApp": ("POST", "/session/$sessionId/chromium/launch_app"),
|
||||
"setPermissions": ("POST", "/session/$sessionId/permissions"),
|
||||
"setNetworkConditions": ("POST", "/session/$sessionId/chromium/network_conditions"),
|
||||
"getNetworkConditions": ("GET", "/session/$sessionId/chromium/network_conditions"),
|
||||
"deleteNetworkConditions": ("DELETE", "/session/$sessionId/chromium/network_conditions"),
|
||||
"executeCdpCommand": ("POST", f"/session/$sessionId/{vendor_prefix}/cdp/execute"),
|
||||
"getSinks": ("GET", f"/session/$sessionId/{vendor_prefix}/cast/get_sinks"),
|
||||
"getIssueMessage": ("GET", f"/session/$sessionId/{vendor_prefix}/cast/get_issue_message"),
|
||||
"setSinkToUse": ("POST", f"/session/$sessionId/{vendor_prefix}/cast/set_sink_to_use"),
|
||||
"startDesktopMirroring": ("POST", f"/session/$sessionId/{vendor_prefix}/cast/start_desktop_mirroring"),
|
||||
"startTabMirroring": ("POST", f"/session/$sessionId/{vendor_prefix}/cast/start_tab_mirroring"),
|
||||
"stopCasting": ("POST", f"/session/$sessionId/{vendor_prefix}/cast/stop_casting"),
|
||||
}
|
||||
return remote_commands
|
66
lib/python3.13/site-packages/selenium/webdriver/chromium/service.py
Executable file
66
lib/python3.13/site-packages/selenium/webdriver/chromium/service.py
Executable file
@ -0,0 +1,66 @@
|
||||
# Licensed to the Software Freedom Conservancy (SFC) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The SFC licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
import typing
|
||||
from io import IOBase
|
||||
|
||||
from selenium.types import SubprocessStdAlias
|
||||
from selenium.webdriver.common import service
|
||||
|
||||
|
||||
class ChromiumService(service.Service):
|
||||
"""A Service class that is responsible for the starting and stopping the
|
||||
WebDriver instance of the ChromiumDriver.
|
||||
|
||||
:param executable_path: install path of the executable.
|
||||
:param port: Port for the service to run on, defaults to 0 where the operating system will decide.
|
||||
:param service_args: (Optional) List of args to be passed to the subprocess when launching the executable.
|
||||
:param log_output: (Optional) int representation of STDOUT/DEVNULL, any IO instance or String path to file.
|
||||
:param env: (Optional) Mapping of environment variables for the new process, defaults to `os.environ`.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
executable_path: str = None,
|
||||
port: int = 0,
|
||||
service_args: typing.Optional[typing.List[str]] = None,
|
||||
log_output: SubprocessStdAlias = None,
|
||||
env: typing.Optional[typing.Mapping[str, str]] = None,
|
||||
driver_path_env_key: str = None,
|
||||
**kwargs,
|
||||
) -> None:
|
||||
self.service_args = service_args or []
|
||||
driver_path_env_key = driver_path_env_key or "SE_CHROMEDRIVER"
|
||||
|
||||
if isinstance(log_output, str):
|
||||
self.service_args.append(f"--log-path={log_output}")
|
||||
self.log_output: typing.Optional[IOBase] = None
|
||||
elif isinstance(log_output, IOBase):
|
||||
self.log_output = log_output
|
||||
else:
|
||||
self.log_output = log_output
|
||||
|
||||
super().__init__(
|
||||
executable_path=executable_path,
|
||||
port=port,
|
||||
env=env,
|
||||
log_output=self.log_output,
|
||||
driver_path_env_key=driver_path_env_key,
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
def command_line_args(self) -> typing.List[str]:
|
||||
return [f"--port={self.port}"] + self.service_args
|
193
lib/python3.13/site-packages/selenium/webdriver/chromium/webdriver.py
Executable file
193
lib/python3.13/site-packages/selenium/webdriver/chromium/webdriver.py
Executable file
@ -0,0 +1,193 @@
|
||||
# Licensed to the Software Freedom Conservancy (SFC) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The SFC licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from selenium.webdriver.chromium.remote_connection import ChromiumRemoteConnection
|
||||
from selenium.webdriver.common.driver_finder import DriverFinder
|
||||
from selenium.webdriver.common.options import ArgOptions
|
||||
from selenium.webdriver.common.service import Service
|
||||
from selenium.webdriver.remote.webdriver import WebDriver as RemoteWebDriver
|
||||
|
||||
|
||||
class ChromiumDriver(RemoteWebDriver):
|
||||
"""Controls the WebDriver instance of ChromiumDriver and allows you to
|
||||
drive the browser."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
browser_name: str = None,
|
||||
vendor_prefix: str = None,
|
||||
options: ArgOptions = ArgOptions(),
|
||||
service: Service = None,
|
||||
keep_alive: bool = True,
|
||||
) -> None:
|
||||
"""Creates a new WebDriver instance of the ChromiumDriver. Starts the
|
||||
service and then creates new WebDriver instance of ChromiumDriver.
|
||||
|
||||
:Args:
|
||||
- browser_name - Browser name used when matching capabilities.
|
||||
- vendor_prefix - Company prefix to apply to vendor-specific WebDriver extension commands.
|
||||
- options - this takes an instance of ChromiumOptions
|
||||
- service - Service object for handling the browser driver if you need to pass extra details
|
||||
- keep_alive - Whether to configure ChromiumRemoteConnection to use HTTP keep-alive.
|
||||
"""
|
||||
self.service = service
|
||||
|
||||
finder = DriverFinder(self.service, options)
|
||||
if finder.get_browser_path():
|
||||
options.binary_location = finder.get_browser_path()
|
||||
options.browser_version = None
|
||||
|
||||
self.service.path = self.service.env_path() or finder.get_driver_path()
|
||||
self.service.start()
|
||||
|
||||
executor = ChromiumRemoteConnection(
|
||||
remote_server_addr=self.service.service_url,
|
||||
browser_name=browser_name,
|
||||
vendor_prefix=vendor_prefix,
|
||||
keep_alive=keep_alive,
|
||||
ignore_proxy=options._ignore_local_proxy,
|
||||
)
|
||||
|
||||
try:
|
||||
super().__init__(command_executor=executor, options=options)
|
||||
except Exception:
|
||||
self.quit()
|
||||
raise
|
||||
|
||||
self._is_remote = False
|
||||
|
||||
def launch_app(self, id):
|
||||
"""Launches Chromium app specified by id."""
|
||||
return self.execute("launchApp", {"id": id})
|
||||
|
||||
def get_network_conditions(self):
|
||||
"""Gets Chromium network emulation settings.
|
||||
|
||||
:Returns: A dict. For example: {'latency': 4,
|
||||
'download_throughput': 2, 'upload_throughput': 2, 'offline':
|
||||
False}
|
||||
"""
|
||||
return self.execute("getNetworkConditions")["value"]
|
||||
|
||||
def set_network_conditions(self, **network_conditions) -> None:
|
||||
"""Sets Chromium network emulation settings.
|
||||
|
||||
:Args:
|
||||
- network_conditions: A dict with conditions specification.
|
||||
|
||||
:Usage:
|
||||
::
|
||||
|
||||
driver.set_network_conditions(
|
||||
offline=False,
|
||||
latency=5, # additional latency (ms)
|
||||
download_throughput=500 * 1024, # maximal throughput
|
||||
upload_throughput=500 * 1024) # maximal throughput
|
||||
|
||||
Note: 'throughput' can be used to set both (for download and upload).
|
||||
"""
|
||||
self.execute("setNetworkConditions", {"network_conditions": network_conditions})
|
||||
|
||||
def delete_network_conditions(self) -> None:
|
||||
"""Resets Chromium network emulation settings."""
|
||||
self.execute("deleteNetworkConditions")
|
||||
|
||||
def set_permissions(self, name: str, value: str) -> None:
|
||||
"""Sets Applicable Permission.
|
||||
|
||||
:Args:
|
||||
- name: The item to set the permission on.
|
||||
- value: The value to set on the item
|
||||
|
||||
:Usage:
|
||||
::
|
||||
|
||||
driver.set_permissions('clipboard-read', 'denied')
|
||||
"""
|
||||
self.execute("setPermissions", {"descriptor": {"name": name}, "state": value})
|
||||
|
||||
def execute_cdp_cmd(self, cmd: str, cmd_args: dict):
|
||||
"""Execute Chrome Devtools Protocol command and get returned result The
|
||||
command and command args should follow chrome devtools protocol
|
||||
domains/commands, refer to link
|
||||
https://chromedevtools.github.io/devtools-protocol/
|
||||
|
||||
:Args:
|
||||
- cmd: A str, command name
|
||||
- cmd_args: A dict, command args. empty dict {} if there is no command args
|
||||
:Usage:
|
||||
::
|
||||
|
||||
driver.execute_cdp_cmd('Network.getResponseBody', {'requestId': requestId})
|
||||
:Returns:
|
||||
A dict, empty dict {} if there is no result to return.
|
||||
For example to getResponseBody:
|
||||
{'base64Encoded': False, 'body': 'response body string'}
|
||||
"""
|
||||
return self.execute("executeCdpCommand", {"cmd": cmd, "params": cmd_args})["value"]
|
||||
|
||||
def get_sinks(self) -> list:
|
||||
""":Returns: A list of sinks available for Cast."""
|
||||
return self.execute("getSinks")["value"]
|
||||
|
||||
def get_issue_message(self):
|
||||
""":Returns: An error message when there is any issue in a Cast
|
||||
session."""
|
||||
return self.execute("getIssueMessage")["value"]
|
||||
|
||||
def set_sink_to_use(self, sink_name: str) -> dict:
|
||||
"""Sets a specific sink, using its name, as a Cast session receiver
|
||||
target.
|
||||
|
||||
:Args:
|
||||
- sink_name: Name of the sink to use as the target.
|
||||
"""
|
||||
return self.execute("setSinkToUse", {"sinkName": sink_name})
|
||||
|
||||
def start_desktop_mirroring(self, sink_name: str) -> dict:
|
||||
"""Starts a desktop mirroring session on a specific receiver target.
|
||||
|
||||
:Args:
|
||||
- sink_name: Name of the sink to use as the target.
|
||||
"""
|
||||
return self.execute("startDesktopMirroring", {"sinkName": sink_name})
|
||||
|
||||
def start_tab_mirroring(self, sink_name: str) -> dict:
|
||||
"""Starts a tab mirroring session on a specific receiver target.
|
||||
|
||||
:Args:
|
||||
- sink_name: Name of the sink to use as the target.
|
||||
"""
|
||||
return self.execute("startTabMirroring", {"sinkName": sink_name})
|
||||
|
||||
def stop_casting(self, sink_name: str) -> dict:
|
||||
"""Stops the existing Cast session on a specific receiver target.
|
||||
|
||||
:Args:
|
||||
- sink_name: Name of the sink to stop the Cast session.
|
||||
"""
|
||||
return self.execute("stopCasting", {"sinkName": sink_name})
|
||||
|
||||
def quit(self) -> None:
|
||||
"""Closes the browser and shuts down the ChromiumDriver executable."""
|
||||
try:
|
||||
super().quit()
|
||||
except Exception:
|
||||
# We don't care about the message because something probably has gone wrong
|
||||
pass
|
||||
finally:
|
||||
self.service.stop()
|
16
lib/python3.13/site-packages/selenium/webdriver/common/__init__.py
Executable file
16
lib/python3.13/site-packages/selenium/webdriver/common/__init__.py
Executable file
@ -0,0 +1,16 @@
|
||||
# Licensed to the Software Freedom Conservancy (SFC) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The SFC licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
380
lib/python3.13/site-packages/selenium/webdriver/common/action_chains.py
Executable file
380
lib/python3.13/site-packages/selenium/webdriver/common/action_chains.py
Executable file
@ -0,0 +1,380 @@
|
||||
# Licensed to the Software Freedom Conservancy (SFC) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The SFC licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
"""The ActionChains implementation."""
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
from typing import Union
|
||||
|
||||
from selenium.webdriver.remote.webelement import WebElement
|
||||
|
||||
from .actions.action_builder import ActionBuilder
|
||||
from .actions.key_input import KeyInput
|
||||
from .actions.pointer_input import PointerInput
|
||||
from .actions.wheel_input import ScrollOrigin
|
||||
from .actions.wheel_input import WheelInput
|
||||
from .utils import keys_to_typing
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from selenium.webdriver.remote.webdriver import WebDriver
|
||||
|
||||
AnyDevice = Union[PointerInput, KeyInput, WheelInput]
|
||||
|
||||
|
||||
class ActionChains:
|
||||
"""ActionChains are a way to automate low level interactions such as mouse
|
||||
movements, mouse button actions, key press, and context menu interactions.
|
||||
This is useful for doing more complex actions like hover over and drag and
|
||||
drop.
|
||||
|
||||
Generate user actions.
|
||||
When you call methods for actions on the ActionChains object,
|
||||
the actions are stored in a queue in the ActionChains object.
|
||||
When you call perform(), the events are fired in the order they
|
||||
are queued up.
|
||||
|
||||
ActionChains can be used in a chain pattern::
|
||||
|
||||
menu = driver.find_element(By.CSS_SELECTOR, ".nav")
|
||||
hidden_submenu = driver.find_element(By.CSS_SELECTOR, ".nav #submenu1")
|
||||
|
||||
ActionChains(driver).move_to_element(menu).click(hidden_submenu).perform()
|
||||
|
||||
Or actions can be queued up one by one, then performed.::
|
||||
|
||||
menu = driver.find_element(By.CSS_SELECTOR, ".nav")
|
||||
hidden_submenu = driver.find_element(By.CSS_SELECTOR, ".nav #submenu1")
|
||||
|
||||
actions = ActionChains(driver)
|
||||
actions.move_to_element(menu)
|
||||
actions.click(hidden_submenu)
|
||||
actions.perform()
|
||||
|
||||
Either way, the actions are performed in the order they are called, one after
|
||||
another.
|
||||
"""
|
||||
|
||||
def __init__(self, driver: WebDriver, duration: int = 250, devices: list[AnyDevice] | None = None) -> None:
|
||||
"""Creates a new ActionChains.
|
||||
|
||||
:Args:
|
||||
- driver: The WebDriver instance which performs user actions.
|
||||
- duration: override the default 250 msecs of DEFAULT_MOVE_DURATION in PointerInput
|
||||
"""
|
||||
self._driver = driver
|
||||
mouse = None
|
||||
keyboard = None
|
||||
wheel = None
|
||||
if devices is not None and isinstance(devices, list):
|
||||
for device in devices:
|
||||
if isinstance(device, PointerInput):
|
||||
mouse = device
|
||||
if isinstance(device, KeyInput):
|
||||
keyboard = device
|
||||
if isinstance(device, WheelInput):
|
||||
wheel = device
|
||||
self.w3c_actions = ActionBuilder(driver, mouse=mouse, keyboard=keyboard, wheel=wheel, duration=duration)
|
||||
|
||||
def perform(self) -> None:
|
||||
"""Performs all stored actions."""
|
||||
self.w3c_actions.perform()
|
||||
|
||||
def reset_actions(self) -> None:
|
||||
"""Clears actions that are already stored locally and on the remote
|
||||
end."""
|
||||
self.w3c_actions.clear_actions()
|
||||
for device in self.w3c_actions.devices:
|
||||
device.clear_actions()
|
||||
|
||||
def click(self, on_element: WebElement | None = None) -> ActionChains:
|
||||
"""Clicks an element.
|
||||
|
||||
:Args:
|
||||
- on_element: The element to click.
|
||||
If None, clicks on current mouse position.
|
||||
"""
|
||||
if on_element:
|
||||
self.move_to_element(on_element)
|
||||
|
||||
self.w3c_actions.pointer_action.click()
|
||||
self.w3c_actions.key_action.pause()
|
||||
self.w3c_actions.key_action.pause()
|
||||
|
||||
return self
|
||||
|
||||
def click_and_hold(self, on_element: WebElement | None = None) -> ActionChains:
|
||||
"""Holds down the left mouse button on an element.
|
||||
|
||||
:Args:
|
||||
- on_element: The element to mouse down.
|
||||
If None, clicks on current mouse position.
|
||||
"""
|
||||
if on_element:
|
||||
self.move_to_element(on_element)
|
||||
|
||||
self.w3c_actions.pointer_action.click_and_hold()
|
||||
self.w3c_actions.key_action.pause()
|
||||
|
||||
return self
|
||||
|
||||
def context_click(self, on_element: WebElement | None = None) -> ActionChains:
|
||||
"""Performs a context-click (right click) on an element.
|
||||
|
||||
:Args:
|
||||
- on_element: The element to context-click.
|
||||
If None, clicks on current mouse position.
|
||||
"""
|
||||
if on_element:
|
||||
self.move_to_element(on_element)
|
||||
|
||||
self.w3c_actions.pointer_action.context_click()
|
||||
self.w3c_actions.key_action.pause()
|
||||
self.w3c_actions.key_action.pause()
|
||||
|
||||
return self
|
||||
|
||||
def double_click(self, on_element: WebElement | None = None) -> ActionChains:
|
||||
"""Double-clicks an element.
|
||||
|
||||
:Args:
|
||||
- on_element: The element to double-click.
|
||||
If None, clicks on current mouse position.
|
||||
"""
|
||||
if on_element:
|
||||
self.move_to_element(on_element)
|
||||
|
||||
self.w3c_actions.pointer_action.double_click()
|
||||
for _ in range(4):
|
||||
self.w3c_actions.key_action.pause()
|
||||
|
||||
return self
|
||||
|
||||
def drag_and_drop(self, source: WebElement, target: WebElement) -> ActionChains:
|
||||
"""Holds down the left mouse button on the source element, then moves
|
||||
to the target element and releases the mouse button.
|
||||
|
||||
:Args:
|
||||
- source: The element to mouse down.
|
||||
- target: The element to mouse up.
|
||||
"""
|
||||
self.click_and_hold(source)
|
||||
self.release(target)
|
||||
return self
|
||||
|
||||
def drag_and_drop_by_offset(self, source: WebElement, xoffset: int, yoffset: int) -> ActionChains:
|
||||
"""Holds down the left mouse button on the source element, then moves
|
||||
to the target offset and releases the mouse button.
|
||||
|
||||
:Args:
|
||||
- source: The element to mouse down.
|
||||
- xoffset: X offset to move to.
|
||||
- yoffset: Y offset to move to.
|
||||
"""
|
||||
self.click_and_hold(source)
|
||||
self.move_by_offset(xoffset, yoffset)
|
||||
self.release()
|
||||
return self
|
||||
|
||||
def key_down(self, value: str, element: WebElement | None = None) -> ActionChains:
|
||||
"""Sends a key press only, without releasing it. Should only be used
|
||||
with modifier keys (Control, Alt and Shift).
|
||||
|
||||
:Args:
|
||||
- value: The modifier key to send. Values are defined in `Keys` class.
|
||||
- element: The element to send keys.
|
||||
If None, sends a key to current focused element.
|
||||
|
||||
Example, pressing ctrl+c::
|
||||
|
||||
ActionChains(driver).key_down(Keys.CONTROL).send_keys('c').key_up(Keys.CONTROL).perform()
|
||||
"""
|
||||
if element:
|
||||
self.click(element)
|
||||
|
||||
self.w3c_actions.key_action.key_down(value)
|
||||
self.w3c_actions.pointer_action.pause()
|
||||
|
||||
return self
|
||||
|
||||
def key_up(self, value: str, element: WebElement | None = None) -> ActionChains:
|
||||
"""Releases a modifier key.
|
||||
|
||||
:Args:
|
||||
- value: The modifier key to send. Values are defined in Keys class.
|
||||
- element: The element to send keys.
|
||||
If None, sends a key to current focused element.
|
||||
|
||||
Example, pressing ctrl+c::
|
||||
|
||||
ActionChains(driver).key_down(Keys.CONTROL).send_keys('c').key_up(Keys.CONTROL).perform()
|
||||
"""
|
||||
if element:
|
||||
self.click(element)
|
||||
|
||||
self.w3c_actions.key_action.key_up(value)
|
||||
self.w3c_actions.pointer_action.pause()
|
||||
|
||||
return self
|
||||
|
||||
def move_by_offset(self, xoffset: int, yoffset: int) -> ActionChains:
|
||||
"""Moving the mouse to an offset from current mouse position.
|
||||
|
||||
:Args:
|
||||
- xoffset: X offset to move to, as a positive or negative integer.
|
||||
- yoffset: Y offset to move to, as a positive or negative integer.
|
||||
"""
|
||||
|
||||
self.w3c_actions.pointer_action.move_by(xoffset, yoffset)
|
||||
self.w3c_actions.key_action.pause()
|
||||
|
||||
return self
|
||||
|
||||
def move_to_element(self, to_element: WebElement) -> ActionChains:
|
||||
"""Moving the mouse to the middle of an element.
|
||||
|
||||
:Args:
|
||||
- to_element: The WebElement to move to.
|
||||
"""
|
||||
|
||||
self.w3c_actions.pointer_action.move_to(to_element)
|
||||
self.w3c_actions.key_action.pause()
|
||||
|
||||
return self
|
||||
|
||||
def move_to_element_with_offset(self, to_element: WebElement, xoffset: int, yoffset: int) -> ActionChains:
|
||||
"""Move the mouse by an offset of the specified element. Offsets are
|
||||
relative to the in-view center point of the element.
|
||||
|
||||
:Args:
|
||||
- to_element: The WebElement to move to.
|
||||
- xoffset: X offset to move to, as a positive or negative integer.
|
||||
- yoffset: Y offset to move to, as a positive or negative integer.
|
||||
"""
|
||||
|
||||
self.w3c_actions.pointer_action.move_to(to_element, int(xoffset), int(yoffset))
|
||||
self.w3c_actions.key_action.pause()
|
||||
|
||||
return self
|
||||
|
||||
def pause(self, seconds: float | int) -> ActionChains:
|
||||
"""Pause all inputs for the specified duration in seconds."""
|
||||
|
||||
self.w3c_actions.pointer_action.pause(seconds)
|
||||
self.w3c_actions.key_action.pause(seconds)
|
||||
|
||||
return self
|
||||
|
||||
def release(self, on_element: WebElement | None = None) -> ActionChains:
|
||||
"""Releasing a held mouse button on an element.
|
||||
|
||||
:Args:
|
||||
- on_element: The element to mouse up.
|
||||
If None, releases on current mouse position.
|
||||
"""
|
||||
if on_element:
|
||||
self.move_to_element(on_element)
|
||||
|
||||
self.w3c_actions.pointer_action.release()
|
||||
self.w3c_actions.key_action.pause()
|
||||
|
||||
return self
|
||||
|
||||
def send_keys(self, *keys_to_send: str) -> ActionChains:
|
||||
"""Sends keys to current focused element.
|
||||
|
||||
:Args:
|
||||
- keys_to_send: The keys to send. Modifier keys constants can be found in the
|
||||
'Keys' class.
|
||||
"""
|
||||
typing = keys_to_typing(keys_to_send)
|
||||
|
||||
for key in typing:
|
||||
self.key_down(key)
|
||||
self.key_up(key)
|
||||
|
||||
return self
|
||||
|
||||
def send_keys_to_element(self, element: WebElement, *keys_to_send: str) -> ActionChains:
|
||||
"""Sends keys to an element.
|
||||
|
||||
:Args:
|
||||
- element: The element to send keys.
|
||||
- keys_to_send: The keys to send. Modifier keys constants can be found in the
|
||||
'Keys' class.
|
||||
"""
|
||||
self.click(element)
|
||||
self.send_keys(*keys_to_send)
|
||||
return self
|
||||
|
||||
def scroll_to_element(self, element: WebElement) -> ActionChains:
|
||||
"""If the element is outside the viewport, scrolls the bottom of the
|
||||
element to the bottom of the viewport.
|
||||
|
||||
:Args:
|
||||
- element: Which element to scroll into the viewport.
|
||||
"""
|
||||
|
||||
self.w3c_actions.wheel_action.scroll(origin=element)
|
||||
return self
|
||||
|
||||
def scroll_by_amount(self, delta_x: int, delta_y: int) -> ActionChains:
|
||||
"""Scrolls by provided amounts with the origin in the top left corner
|
||||
of the viewport.
|
||||
|
||||
:Args:
|
||||
- delta_x: Distance along X axis to scroll using the wheel. A negative value scrolls left.
|
||||
- delta_y: Distance along Y axis to scroll using the wheel. A negative value scrolls up.
|
||||
"""
|
||||
|
||||
self.w3c_actions.wheel_action.scroll(delta_x=delta_x, delta_y=delta_y)
|
||||
return self
|
||||
|
||||
def scroll_from_origin(self, scroll_origin: ScrollOrigin, delta_x: int, delta_y: int) -> ActionChains:
|
||||
"""Scrolls by provided amount based on a provided origin. The scroll
|
||||
origin is either the center of an element or the upper left of the
|
||||
viewport plus any offsets. If the origin is an element, and the element
|
||||
is not in the viewport, the bottom of the element will first be
|
||||
scrolled to the bottom of the viewport.
|
||||
|
||||
:Args:
|
||||
- origin: Where scroll originates (viewport or element center) plus provided offsets.
|
||||
- delta_x: Distance along X axis to scroll using the wheel. A negative value scrolls left.
|
||||
- delta_y: Distance along Y axis to scroll using the wheel. A negative value scrolls up.
|
||||
|
||||
:Raises: If the origin with offset is outside the viewport.
|
||||
- MoveTargetOutOfBoundsException - If the origin with offset is outside the viewport.
|
||||
"""
|
||||
|
||||
if not isinstance(scroll_origin, ScrollOrigin):
|
||||
raise TypeError(f"Expected object of type ScrollOrigin, got: {type(scroll_origin)}")
|
||||
|
||||
self.w3c_actions.wheel_action.scroll(
|
||||
origin=scroll_origin.origin,
|
||||
x=scroll_origin.x_offset,
|
||||
y=scroll_origin.y_offset,
|
||||
delta_x=delta_x,
|
||||
delta_y=delta_y,
|
||||
)
|
||||
return self
|
||||
|
||||
# Context manager so ActionChains can be used in a 'with .. as' statements.
|
||||
|
||||
def __enter__(self) -> ActionChains:
|
||||
return self # Return created instance of self.
|
||||
|
||||
def __exit__(self, _type, _value, _traceback) -> None:
|
||||
pass # Do nothing, does not require additional cleanup.
|
16
lib/python3.13/site-packages/selenium/webdriver/common/actions/__init__.py
Executable file
16
lib/python3.13/site-packages/selenium/webdriver/common/actions/__init__.py
Executable file
@ -0,0 +1,16 @@
|
||||
# Licensed to the Software Freedom Conservancy (SFC) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The SFC licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
103
lib/python3.13/site-packages/selenium/webdriver/common/actions/action_builder.py
Executable file
103
lib/python3.13/site-packages/selenium/webdriver/common/actions/action_builder.py
Executable file
@ -0,0 +1,103 @@
|
||||
# Licensed to the Software Freedom Conservancy (SFC) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The SFC licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from typing import List
|
||||
from typing import Optional
|
||||
from typing import Union
|
||||
|
||||
from selenium.webdriver.remote.command import Command
|
||||
|
||||
from . import interaction
|
||||
from .key_actions import KeyActions
|
||||
from .key_input import KeyInput
|
||||
from .pointer_actions import PointerActions
|
||||
from .pointer_input import PointerInput
|
||||
from .wheel_actions import WheelActions
|
||||
from .wheel_input import WheelInput
|
||||
|
||||
|
||||
class ActionBuilder:
|
||||
def __init__(
|
||||
self,
|
||||
driver,
|
||||
mouse: Optional[PointerInput] = None,
|
||||
wheel: Optional[WheelInput] = None,
|
||||
keyboard: Optional[KeyInput] = None,
|
||||
duration: int = 250,
|
||||
) -> None:
|
||||
mouse = mouse or PointerInput(interaction.POINTER_MOUSE, "mouse")
|
||||
keyboard = keyboard or KeyInput(interaction.KEY)
|
||||
wheel = wheel or WheelInput(interaction.WHEEL)
|
||||
self.devices = [mouse, keyboard, wheel]
|
||||
self._key_action = KeyActions(keyboard)
|
||||
self._pointer_action = PointerActions(mouse, duration=duration)
|
||||
self._wheel_action = WheelActions(wheel)
|
||||
self.driver = driver
|
||||
|
||||
def get_device_with(self, name: str) -> Optional[Union["WheelInput", "PointerInput", "KeyInput"]]:
|
||||
return next(filter(lambda x: x == name, self.devices), None)
|
||||
|
||||
@property
|
||||
def pointer_inputs(self) -> List[PointerInput]:
|
||||
return [device for device in self.devices if device.type == interaction.POINTER]
|
||||
|
||||
@property
|
||||
def key_inputs(self) -> List[KeyInput]:
|
||||
return [device for device in self.devices if device.type == interaction.KEY]
|
||||
|
||||
@property
|
||||
def key_action(self) -> KeyActions:
|
||||
return self._key_action
|
||||
|
||||
@property
|
||||
def pointer_action(self) -> PointerActions:
|
||||
return self._pointer_action
|
||||
|
||||
@property
|
||||
def wheel_action(self) -> WheelActions:
|
||||
return self._wheel_action
|
||||
|
||||
def add_key_input(self, name: str) -> KeyInput:
|
||||
new_input = KeyInput(name)
|
||||
self._add_input(new_input)
|
||||
return new_input
|
||||
|
||||
def add_pointer_input(self, kind: str, name: str) -> PointerInput:
|
||||
new_input = PointerInput(kind, name)
|
||||
self._add_input(new_input)
|
||||
return new_input
|
||||
|
||||
def add_wheel_input(self, name: str) -> WheelInput:
|
||||
new_input = WheelInput(name)
|
||||
self._add_input(new_input)
|
||||
return new_input
|
||||
|
||||
def perform(self) -> None:
|
||||
enc = {"actions": []}
|
||||
for device in self.devices:
|
||||
encoded = device.encode()
|
||||
if encoded["actions"]:
|
||||
enc["actions"].append(encoded)
|
||||
device.actions = []
|
||||
self.driver.execute(Command.W3C_ACTIONS, enc)
|
||||
|
||||
def clear_actions(self) -> None:
|
||||
"""Clears actions that are already stored on the remote end."""
|
||||
self.driver.execute(Command.W3C_CLEAR_ACTIONS)
|
||||
|
||||
def _add_input(self, new_input: Union[KeyInput, PointerInput, WheelInput]) -> None:
|
||||
self.devices.append(new_input)
|
@ -0,0 +1,39 @@
|
||||
# Licensed to the Software Freedom Conservancy (SFC) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The SFC licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import uuid
|
||||
from typing import Any
|
||||
from typing import List
|
||||
from typing import Optional
|
||||
|
||||
|
||||
class InputDevice:
|
||||
"""Describes the input device being used for the action."""
|
||||
|
||||
def __init__(self, name: Optional[str] = None):
|
||||
self.name = name or uuid.uuid4()
|
||||
self.actions: List[Any] = []
|
||||
|
||||
def add_action(self, action: Any) -> None:
|
||||
""""""
|
||||
self.actions.append(action)
|
||||
|
||||
def clear_actions(self) -> None:
|
||||
self.actions = []
|
||||
|
||||
def create_pause(self, duration: float = 0) -> None:
|
||||
pass
|
@ -0,0 +1,46 @@
|
||||
# Licensed to the Software Freedom Conservancy (SFC) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The SFC licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
from typing import Dict
|
||||
from typing import Union
|
||||
|
||||
KEY = "key"
|
||||
POINTER = "pointer"
|
||||
NONE = "none"
|
||||
WHEEL = "wheel"
|
||||
SOURCE_TYPES = {KEY, POINTER, NONE}
|
||||
|
||||
POINTER_MOUSE = "mouse"
|
||||
POINTER_TOUCH = "touch"
|
||||
POINTER_PEN = "pen"
|
||||
|
||||
POINTER_KINDS = {POINTER_MOUSE, POINTER_TOUCH, POINTER_PEN}
|
||||
|
||||
|
||||
class Interaction:
|
||||
PAUSE = "pause"
|
||||
|
||||
def __init__(self, source: str) -> None:
|
||||
self.source = source
|
||||
|
||||
|
||||
class Pause(Interaction):
|
||||
def __init__(self, source, duration: float = 0) -> None:
|
||||
super().__init__(source)
|
||||
self.duration = duration
|
||||
|
||||
def encode(self) -> Dict[str, Union[str, int]]:
|
||||
return {"type": self.PAUSE, "duration": int(self.duration * 1000)}
|
@ -0,0 +1,54 @@
|
||||
# Licensed to the Software Freedom Conservancy (SFC) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The SFC licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
from __future__ import annotations
|
||||
|
||||
from ..utils import keys_to_typing
|
||||
from .interaction import KEY
|
||||
from .interaction import Interaction
|
||||
from .key_input import KeyInput
|
||||
from .pointer_input import PointerInput
|
||||
from .wheel_input import WheelInput
|
||||
|
||||
|
||||
class KeyActions(Interaction):
|
||||
def __init__(self, source: KeyInput | PointerInput | WheelInput | None = None) -> None:
|
||||
if not source:
|
||||
source = KeyInput(KEY)
|
||||
self.source = source
|
||||
super().__init__(source)
|
||||
|
||||
def key_down(self, letter: str) -> KeyActions:
|
||||
return self._key_action("create_key_down", letter)
|
||||
|
||||
def key_up(self, letter: str) -> KeyActions:
|
||||
return self._key_action("create_key_up", letter)
|
||||
|
||||
def pause(self, duration: int = 0) -> KeyActions:
|
||||
return self._key_action("create_pause", duration)
|
||||
|
||||
def send_keys(self, text: str | list) -> KeyActions:
|
||||
if not isinstance(text, list):
|
||||
text = keys_to_typing(text)
|
||||
for letter in text:
|
||||
self.key_down(letter)
|
||||
self.key_up(letter)
|
||||
return self
|
||||
|
||||
def _key_action(self, action: str, letter) -> KeyActions:
|
||||
meth = getattr(self.source, action)
|
||||
meth(letter)
|
||||
return self
|
49
lib/python3.13/site-packages/selenium/webdriver/common/actions/key_input.py
Executable file
49
lib/python3.13/site-packages/selenium/webdriver/common/actions/key_input.py
Executable file
@ -0,0 +1,49 @@
|
||||
# Licensed to the Software Freedom Conservancy (SFC) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The SFC licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
from . import interaction
|
||||
from .input_device import InputDevice
|
||||
from .interaction import Interaction
|
||||
from .interaction import Pause
|
||||
|
||||
|
||||
class KeyInput(InputDevice):
|
||||
def __init__(self, name: str) -> None:
|
||||
super().__init__()
|
||||
self.name = name
|
||||
self.type = interaction.KEY
|
||||
|
||||
def encode(self) -> dict:
|
||||
return {"type": self.type, "id": self.name, "actions": [acts.encode() for acts in self.actions]}
|
||||
|
||||
def create_key_down(self, key) -> None:
|
||||
self.add_action(TypingInteraction(self, "keyDown", key))
|
||||
|
||||
def create_key_up(self, key) -> None:
|
||||
self.add_action(TypingInteraction(self, "keyUp", key))
|
||||
|
||||
def create_pause(self, pause_duration: float = 0) -> None:
|
||||
self.add_action(Pause(self, pause_duration))
|
||||
|
||||
|
||||
class TypingInteraction(Interaction):
|
||||
def __init__(self, source, type_, key) -> None:
|
||||
super().__init__(source)
|
||||
self.type = type_
|
||||
self.key = key
|
||||
|
||||
def encode(self) -> dict:
|
||||
return {"type": self.type, "value": self.key}
|
@ -0,0 +1,24 @@
|
||||
# Licensed to the Software Freedom Conservancy (SFC) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The SFC licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
|
||||
class MouseButton:
|
||||
LEFT = 0
|
||||
MIDDLE = 1
|
||||
RIGHT = 2
|
||||
BACK = 3
|
||||
FORWARD = 4
|
@ -0,0 +1,205 @@
|
||||
# Licensed to the Software Freedom Conservancy (SFC) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The SFC licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
from typing import Optional
|
||||
|
||||
from selenium.webdriver.remote.webelement import WebElement
|
||||
|
||||
from . import interaction
|
||||
from .interaction import Interaction
|
||||
from .mouse_button import MouseButton
|
||||
from .pointer_input import PointerInput
|
||||
|
||||
|
||||
class PointerActions(Interaction):
|
||||
def __init__(self, source: Optional[PointerInput] = None, duration: int = 250):
|
||||
"""
|
||||
Args:
|
||||
- source: PointerInput instance
|
||||
- duration: override the default 250 msecs of DEFAULT_MOVE_DURATION in source
|
||||
"""
|
||||
if not source:
|
||||
source = PointerInput(interaction.POINTER_MOUSE, "mouse")
|
||||
self.source = source
|
||||
self._duration = duration
|
||||
super().__init__(source)
|
||||
|
||||
def pointer_down(
|
||||
self,
|
||||
button=MouseButton.LEFT,
|
||||
width=None,
|
||||
height=None,
|
||||
pressure=None,
|
||||
tangential_pressure=None,
|
||||
tilt_x=None,
|
||||
tilt_y=None,
|
||||
twist=None,
|
||||
altitude_angle=None,
|
||||
azimuth_angle=None,
|
||||
):
|
||||
self._button_action(
|
||||
"create_pointer_down",
|
||||
button=button,
|
||||
width=width,
|
||||
height=height,
|
||||
pressure=pressure,
|
||||
tangential_pressure=tangential_pressure,
|
||||
tilt_x=tilt_x,
|
||||
tilt_y=tilt_y,
|
||||
twist=twist,
|
||||
altitude_angle=altitude_angle,
|
||||
azimuth_angle=azimuth_angle,
|
||||
)
|
||||
return self
|
||||
|
||||
def pointer_up(self, button=MouseButton.LEFT):
|
||||
self._button_action("create_pointer_up", button=button)
|
||||
return self
|
||||
|
||||
def move_to(
|
||||
self,
|
||||
element,
|
||||
x=0,
|
||||
y=0,
|
||||
width=None,
|
||||
height=None,
|
||||
pressure=None,
|
||||
tangential_pressure=None,
|
||||
tilt_x=None,
|
||||
tilt_y=None,
|
||||
twist=None,
|
||||
altitude_angle=None,
|
||||
azimuth_angle=None,
|
||||
):
|
||||
if not isinstance(element, WebElement):
|
||||
raise AttributeError("move_to requires a WebElement")
|
||||
|
||||
self.source.create_pointer_move(
|
||||
origin=element,
|
||||
duration=self._duration,
|
||||
x=int(x),
|
||||
y=int(y),
|
||||
width=width,
|
||||
height=height,
|
||||
pressure=pressure,
|
||||
tangential_pressure=tangential_pressure,
|
||||
tilt_x=tilt_x,
|
||||
tilt_y=tilt_y,
|
||||
twist=twist,
|
||||
altitude_angle=altitude_angle,
|
||||
azimuth_angle=azimuth_angle,
|
||||
)
|
||||
return self
|
||||
|
||||
def move_by(
|
||||
self,
|
||||
x,
|
||||
y,
|
||||
width=None,
|
||||
height=None,
|
||||
pressure=None,
|
||||
tangential_pressure=None,
|
||||
tilt_x=None,
|
||||
tilt_y=None,
|
||||
twist=None,
|
||||
altitude_angle=None,
|
||||
azimuth_angle=None,
|
||||
):
|
||||
self.source.create_pointer_move(
|
||||
origin=interaction.POINTER,
|
||||
duration=self._duration,
|
||||
x=int(x),
|
||||
y=int(y),
|
||||
width=width,
|
||||
height=height,
|
||||
pressure=pressure,
|
||||
tangential_pressure=tangential_pressure,
|
||||
tilt_x=tilt_x,
|
||||
tilt_y=tilt_y,
|
||||
twist=twist,
|
||||
altitude_angle=altitude_angle,
|
||||
azimuth_angle=azimuth_angle,
|
||||
)
|
||||
return self
|
||||
|
||||
def move_to_location(
|
||||
self,
|
||||
x,
|
||||
y,
|
||||
width=None,
|
||||
height=None,
|
||||
pressure=None,
|
||||
tangential_pressure=None,
|
||||
tilt_x=None,
|
||||
tilt_y=None,
|
||||
twist=None,
|
||||
altitude_angle=None,
|
||||
azimuth_angle=None,
|
||||
):
|
||||
self.source.create_pointer_move(
|
||||
origin="viewport",
|
||||
duration=self._duration,
|
||||
x=int(x),
|
||||
y=int(y),
|
||||
width=width,
|
||||
height=height,
|
||||
pressure=pressure,
|
||||
tangential_pressure=tangential_pressure,
|
||||
tilt_x=tilt_x,
|
||||
tilt_y=tilt_y,
|
||||
twist=twist,
|
||||
altitude_angle=altitude_angle,
|
||||
azimuth_angle=azimuth_angle,
|
||||
)
|
||||
return self
|
||||
|
||||
def click(self, element: Optional[WebElement] = None, button=MouseButton.LEFT):
|
||||
if element:
|
||||
self.move_to(element)
|
||||
self.pointer_down(button)
|
||||
self.pointer_up(button)
|
||||
return self
|
||||
|
||||
def context_click(self, element: Optional[WebElement] = None):
|
||||
return self.click(element=element, button=MouseButton.RIGHT)
|
||||
|
||||
def click_and_hold(self, element: Optional[WebElement] = None, button=MouseButton.LEFT):
|
||||
if element:
|
||||
self.move_to(element)
|
||||
self.pointer_down(button=button)
|
||||
return self
|
||||
|
||||
def release(self, button=MouseButton.LEFT):
|
||||
self.pointer_up(button=button)
|
||||
return self
|
||||
|
||||
def double_click(self, element: Optional[WebElement] = None):
|
||||
if element:
|
||||
self.move_to(element)
|
||||
self.pointer_down(MouseButton.LEFT)
|
||||
self.pointer_up(MouseButton.LEFT)
|
||||
self.pointer_down(MouseButton.LEFT)
|
||||
self.pointer_up(MouseButton.LEFT)
|
||||
return self
|
||||
|
||||
def pause(self, duration: float = 0):
|
||||
self.source.create_pause(duration)
|
||||
return self
|
||||
|
||||
def _button_action(self, action, **kwargs):
|
||||
meth = getattr(self.source, action)
|
||||
meth(**kwargs)
|
||||
return self
|
@ -0,0 +1,81 @@
|
||||
# Licensed to the Software Freedom Conservancy (SFC) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The SFC licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
import typing
|
||||
from typing import Union
|
||||
|
||||
from selenium.common.exceptions import InvalidArgumentException
|
||||
from selenium.webdriver.remote.webelement import WebElement
|
||||
|
||||
from .input_device import InputDevice
|
||||
from .interaction import POINTER
|
||||
from .interaction import POINTER_KINDS
|
||||
|
||||
|
||||
class PointerInput(InputDevice):
|
||||
DEFAULT_MOVE_DURATION = 250
|
||||
|
||||
def __init__(self, kind, name):
|
||||
super().__init__()
|
||||
if kind not in POINTER_KINDS:
|
||||
raise InvalidArgumentException(f"Invalid PointerInput kind '{kind}'")
|
||||
self.type = POINTER
|
||||
self.kind = kind
|
||||
self.name = name
|
||||
|
||||
def create_pointer_move(
|
||||
self,
|
||||
duration=DEFAULT_MOVE_DURATION,
|
||||
x: float = 0,
|
||||
y: float = 0,
|
||||
origin: typing.Optional[WebElement] = None,
|
||||
**kwargs,
|
||||
):
|
||||
action = {"type": "pointerMove", "duration": duration, "x": x, "y": y, **kwargs}
|
||||
if isinstance(origin, WebElement):
|
||||
action["origin"] = {"element-6066-11e4-a52e-4f735466cecf": origin.id}
|
||||
elif origin is not None:
|
||||
action["origin"] = origin
|
||||
self.add_action(self._convert_keys(action))
|
||||
|
||||
def create_pointer_down(self, **kwargs):
|
||||
data = {"type": "pointerDown", "duration": 0, **kwargs}
|
||||
self.add_action(self._convert_keys(data))
|
||||
|
||||
def create_pointer_up(self, button):
|
||||
self.add_action({"type": "pointerUp", "duration": 0, "button": button})
|
||||
|
||||
def create_pointer_cancel(self):
|
||||
self.add_action({"type": "pointerCancel"})
|
||||
|
||||
def create_pause(self, pause_duration: Union[int, float] = 0) -> None:
|
||||
self.add_action({"type": "pause", "duration": int(pause_duration * 1000)})
|
||||
|
||||
def encode(self):
|
||||
return {"type": self.type, "parameters": {"pointerType": self.kind}, "id": self.name, "actions": self.actions}
|
||||
|
||||
def _convert_keys(self, actions: typing.Dict[str, typing.Any]):
|
||||
out = {}
|
||||
for k, v in actions.items():
|
||||
if v is None:
|
||||
continue
|
||||
if k in ("x", "y"):
|
||||
out[k] = int(v)
|
||||
continue
|
||||
splits = k.split("_")
|
||||
new_key = splits[0] + "".join(v.title() for v in splits[1:])
|
||||
out[new_key] = v
|
||||
return out
|
@ -0,0 +1,33 @@
|
||||
# Licensed to the Software Freedom Conservancy (SFC) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The SFC licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
from .interaction import Interaction
|
||||
from .wheel_input import WheelInput
|
||||
|
||||
|
||||
class WheelActions(Interaction):
|
||||
def __init__(self, source: WheelInput = None):
|
||||
if not source:
|
||||
source = WheelInput("wheel")
|
||||
super().__init__(source)
|
||||
|
||||
def pause(self, duration: float = 0):
|
||||
self.source.create_pause(duration)
|
||||
return self
|
||||
|
||||
def scroll(self, x=0, y=0, delta_x=0, delta_y=0, duration=0, origin="viewport"):
|
||||
self.source.create_scroll(x, y, delta_x, delta_y, duration, origin)
|
||||
return self
|
@ -0,0 +1,77 @@
|
||||
# Licensed to the Software Freedom Conservancy (SFC) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The SFC licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
from typing import Union
|
||||
|
||||
from selenium.webdriver.remote.webelement import WebElement
|
||||
|
||||
from . import interaction
|
||||
from .input_device import InputDevice
|
||||
|
||||
|
||||
class ScrollOrigin:
|
||||
def __init__(self, origin: Union[str, WebElement], x_offset: int, y_offset: int) -> None:
|
||||
self._origin = origin
|
||||
self._x_offset = x_offset
|
||||
self._y_offset = y_offset
|
||||
|
||||
@classmethod
|
||||
def from_element(cls, element: WebElement, x_offset: int = 0, y_offset: int = 0):
|
||||
return cls(element, x_offset, y_offset)
|
||||
|
||||
@classmethod
|
||||
def from_viewport(cls, x_offset: int = 0, y_offset: int = 0):
|
||||
return cls("viewport", x_offset, y_offset)
|
||||
|
||||
@property
|
||||
def origin(self) -> Union[str, WebElement]:
|
||||
return self._origin
|
||||
|
||||
@property
|
||||
def x_offset(self) -> int:
|
||||
return self._x_offset
|
||||
|
||||
@property
|
||||
def y_offset(self) -> int:
|
||||
return self._y_offset
|
||||
|
||||
|
||||
class WheelInput(InputDevice):
|
||||
def __init__(self, name) -> None:
|
||||
super().__init__(name=name)
|
||||
self.name = name
|
||||
self.type = interaction.WHEEL
|
||||
|
||||
def encode(self) -> dict:
|
||||
return {"type": self.type, "id": self.name, "actions": self.actions}
|
||||
|
||||
def create_scroll(self, x: int, y: int, delta_x: int, delta_y: int, duration: int, origin) -> None:
|
||||
if isinstance(origin, WebElement):
|
||||
origin = {"element-6066-11e4-a52e-4f735466cecf": origin.id}
|
||||
self.add_action(
|
||||
{
|
||||
"type": "scroll",
|
||||
"x": x,
|
||||
"y": y,
|
||||
"deltaX": delta_x,
|
||||
"deltaY": delta_y,
|
||||
"duration": duration,
|
||||
"origin": origin,
|
||||
}
|
||||
)
|
||||
|
||||
def create_pause(self, pause_duration: Union[int, float] = 0) -> None:
|
||||
self.add_action({"type": "pause", "duration": int(pause_duration * 1000)})
|
80
lib/python3.13/site-packages/selenium/webdriver/common/alert.py
Executable file
80
lib/python3.13/site-packages/selenium/webdriver/common/alert.py
Executable file
@ -0,0 +1,80 @@
|
||||
# Licensed to the Software Freedom Conservancy (SFC) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The SFC licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
"""The Alert implementation."""
|
||||
|
||||
from selenium.webdriver.common.utils import keys_to_typing
|
||||
from selenium.webdriver.remote.command import Command
|
||||
|
||||
|
||||
class Alert:
|
||||
"""Allows to work with alerts.
|
||||
|
||||
Use this class to interact with alert prompts. It contains methods for dismissing,
|
||||
accepting, inputting, and getting text from alert prompts.
|
||||
|
||||
Accepting / Dismissing alert prompts::
|
||||
|
||||
Alert(driver).accept()
|
||||
Alert(driver).dismiss()
|
||||
|
||||
Inputting a value into an alert prompt::
|
||||
|
||||
name_prompt = Alert(driver)
|
||||
name_prompt.send_keys("Willian Shakesphere")
|
||||
name_prompt.accept()
|
||||
|
||||
|
||||
Reading a the text of a prompt for verification::
|
||||
|
||||
alert_text = Alert(driver).text
|
||||
self.assertEqual("Do you wish to quit?", alert_text)
|
||||
"""
|
||||
|
||||
def __init__(self, driver) -> None:
|
||||
"""Creates a new Alert.
|
||||
|
||||
:Args:
|
||||
- driver: The WebDriver instance which performs user actions.
|
||||
"""
|
||||
self.driver = driver
|
||||
|
||||
@property
|
||||
def text(self) -> str:
|
||||
"""Gets the text of the Alert."""
|
||||
return self.driver.execute(Command.W3C_GET_ALERT_TEXT)["value"]
|
||||
|
||||
def dismiss(self) -> None:
|
||||
"""Dismisses the alert available."""
|
||||
self.driver.execute(Command.W3C_DISMISS_ALERT)
|
||||
|
||||
def accept(self) -> None:
|
||||
"""Accepts the alert available.
|
||||
|
||||
:Usage:
|
||||
::
|
||||
|
||||
Alert(driver).accept() # Confirm a alert dialog.
|
||||
"""
|
||||
self.driver.execute(Command.W3C_ACCEPT_ALERT)
|
||||
|
||||
def send_keys(self, keysToSend: str) -> None:
|
||||
"""Send Keys to the Alert.
|
||||
|
||||
:Args:
|
||||
- keysToSend: The text to be sent to Alert.
|
||||
"""
|
||||
self.driver.execute(Command.W3C_SET_ALERT_VALUE, {"value": keys_to_typing(keysToSend), "text": keysToSend})
|
16
lib/python3.13/site-packages/selenium/webdriver/common/bidi/__init__.py
Executable file
16
lib/python3.13/site-packages/selenium/webdriver/common/bidi/__init__.py
Executable file
@ -0,0 +1,16 @@
|
||||
# Licensed to the Software Freedom Conservancy (SFC) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The SFC licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
509
lib/python3.13/site-packages/selenium/webdriver/common/bidi/cdp.py
Executable file
509
lib/python3.13/site-packages/selenium/webdriver/common/bidi/cdp.py
Executable file
@ -0,0 +1,509 @@
|
||||
# The MIT License(MIT)
|
||||
#
|
||||
# Copyright(c) 2018 Hyperion Gray
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files(the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
#
|
||||
# This code comes from https://github.com/HyperionGray/trio-chrome-devtools-protocol/tree/master/trio_cdp
|
||||
|
||||
# flake8: noqa
|
||||
|
||||
import contextvars
|
||||
import importlib
|
||||
import itertools
|
||||
import json
|
||||
import logging
|
||||
import pathlib
|
||||
import typing
|
||||
from collections import defaultdict
|
||||
from contextlib import asynccontextmanager
|
||||
from contextlib import contextmanager
|
||||
from dataclasses import dataclass
|
||||
|
||||
import trio
|
||||
from trio_websocket import ConnectionClosed as WsConnectionClosed
|
||||
from trio_websocket import connect_websocket_url
|
||||
|
||||
logger = logging.getLogger("trio_cdp")
|
||||
T = typing.TypeVar("T")
|
||||
MAX_WS_MESSAGE_SIZE = 2**24
|
||||
|
||||
devtools = None
|
||||
version = None
|
||||
|
||||
|
||||
def import_devtools(ver):
|
||||
"""Attempt to load the current latest available devtools into the module
|
||||
cache for use later."""
|
||||
global devtools
|
||||
global version
|
||||
version = ver
|
||||
base = "selenium.webdriver.common.devtools.v"
|
||||
try:
|
||||
devtools = importlib.import_module(f"{base}{ver}")
|
||||
return devtools
|
||||
except ModuleNotFoundError:
|
||||
# Attempt to parse and load the 'most recent' devtools module. This is likely
|
||||
# because cdp has been updated but selenium python has not been released yet.
|
||||
devtools_path = pathlib.Path(__file__).parents[1].joinpath("devtools")
|
||||
versions = tuple(f.name for f in devtools_path.iterdir() if f.is_dir())
|
||||
latest = max(int(x[1:]) for x in versions)
|
||||
selenium_logger = logging.getLogger(__name__)
|
||||
selenium_logger.debug("Falling back to loading `devtools`: v%s", latest)
|
||||
devtools = importlib.import_module(f"{base}{latest}")
|
||||
return devtools
|
||||
|
||||
|
||||
_connection_context: contextvars.ContextVar = contextvars.ContextVar("connection_context")
|
||||
_session_context: contextvars.ContextVar = contextvars.ContextVar("session_context")
|
||||
|
||||
|
||||
def get_connection_context(fn_name):
|
||||
"""Look up the current connection.
|
||||
|
||||
If there is no current connection, raise a ``RuntimeError`` with a
|
||||
helpful message.
|
||||
"""
|
||||
try:
|
||||
return _connection_context.get()
|
||||
except LookupError:
|
||||
raise RuntimeError(f"{fn_name}() must be called in a connection context.")
|
||||
|
||||
|
||||
def get_session_context(fn_name):
|
||||
"""Look up the current session.
|
||||
|
||||
If there is no current session, raise a ``RuntimeError`` with a
|
||||
helpful message.
|
||||
"""
|
||||
try:
|
||||
return _session_context.get()
|
||||
except LookupError:
|
||||
raise RuntimeError(f"{fn_name}() must be called in a session context.")
|
||||
|
||||
|
||||
@contextmanager
|
||||
def connection_context(connection):
|
||||
"""This context manager installs ``connection`` as the session context for
|
||||
the current Trio task."""
|
||||
token = _connection_context.set(connection)
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
_connection_context.reset(token)
|
||||
|
||||
|
||||
@contextmanager
|
||||
def session_context(session):
|
||||
"""This context manager installs ``session`` as the session context for the
|
||||
current Trio task."""
|
||||
token = _session_context.set(session)
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
_session_context.reset(token)
|
||||
|
||||
|
||||
def set_global_connection(connection):
|
||||
"""Install ``connection`` in the root context so that it will become the
|
||||
default connection for all tasks.
|
||||
|
||||
This is generally not recommended, except it may be necessary in
|
||||
certain use cases such as running inside Jupyter notebook.
|
||||
"""
|
||||
global _connection_context
|
||||
_connection_context = contextvars.ContextVar("_connection_context", default=connection)
|
||||
|
||||
|
||||
def set_global_session(session):
|
||||
"""Install ``session`` in the root context so that it will become the
|
||||
default session for all tasks.
|
||||
|
||||
This is generally not recommended, except it may be necessary in
|
||||
certain use cases such as running inside Jupyter notebook.
|
||||
"""
|
||||
global _session_context
|
||||
_session_context = contextvars.ContextVar("_session_context", default=session)
|
||||
|
||||
|
||||
class BrowserError(Exception):
|
||||
"""This exception is raised when the browser's response to a command
|
||||
indicates that an error occurred."""
|
||||
|
||||
def __init__(self, obj):
|
||||
self.code = obj.get("code")
|
||||
self.message = obj.get("message")
|
||||
self.detail = obj.get("data")
|
||||
|
||||
def __str__(self):
|
||||
return f"BrowserError<code={self.code} message={self.message}> {self.detail}"
|
||||
|
||||
|
||||
class CdpConnectionClosed(WsConnectionClosed):
|
||||
"""Raised when a public method is called on a closed CDP connection."""
|
||||
|
||||
def __init__(self, reason):
|
||||
"""Constructor.
|
||||
|
||||
:param reason:
|
||||
:type reason: wsproto.frame_protocol.CloseReason
|
||||
"""
|
||||
self.reason = reason
|
||||
|
||||
def __repr__(self):
|
||||
"""Return representation."""
|
||||
return f"{self.__class__.__name__}<{self.reason}>"
|
||||
|
||||
|
||||
class InternalError(Exception):
|
||||
"""This exception is only raised when there is faulty logic in TrioCDP or
|
||||
the integration with PyCDP."""
|
||||
|
||||
|
||||
@dataclass
|
||||
class CmEventProxy:
|
||||
"""A proxy object returned by :meth:`CdpBase.wait_for()``.
|
||||
|
||||
After the context manager executes, this proxy object will have a
|
||||
value set that contains the returned event.
|
||||
"""
|
||||
|
||||
value: typing.Any = None
|
||||
|
||||
|
||||
class CdpBase:
|
||||
def __init__(self, ws, session_id, target_id):
|
||||
self.ws = ws
|
||||
self.session_id = session_id
|
||||
self.target_id = target_id
|
||||
self.channels = defaultdict(set)
|
||||
self.id_iter = itertools.count()
|
||||
self.inflight_cmd = {}
|
||||
self.inflight_result = {}
|
||||
|
||||
async def execute(self, cmd: typing.Generator[dict, T, typing.Any]) -> T:
|
||||
"""Execute a command on the server and wait for the result.
|
||||
|
||||
:param cmd: any CDP command
|
||||
:returns: a CDP result
|
||||
"""
|
||||
cmd_id = next(self.id_iter)
|
||||
cmd_event = trio.Event()
|
||||
self.inflight_cmd[cmd_id] = cmd, cmd_event
|
||||
request = next(cmd)
|
||||
request["id"] = cmd_id
|
||||
if self.session_id:
|
||||
request["sessionId"] = self.session_id
|
||||
request_str = json.dumps(request)
|
||||
if logger.isEnabledFor(logging.DEBUG):
|
||||
logger.debug(f"Sending CDP message: {cmd_id} {cmd_event}: {request_str}")
|
||||
try:
|
||||
await self.ws.send_message(request_str)
|
||||
except WsConnectionClosed as wcc:
|
||||
raise CdpConnectionClosed(wcc.reason) from None
|
||||
await cmd_event.wait()
|
||||
response = self.inflight_result.pop(cmd_id)
|
||||
if logger.isEnabledFor(logging.DEBUG):
|
||||
logger.debug(f"Received CDP message: {response}")
|
||||
if isinstance(response, Exception):
|
||||
if logger.isEnabledFor(logging.DEBUG):
|
||||
logger.debug(f"Exception raised by {cmd_event} message: {type(response).__name__}")
|
||||
raise response
|
||||
return response
|
||||
|
||||
def listen(self, *event_types, buffer_size=10):
|
||||
"""Return an async iterator that iterates over events matching the
|
||||
indicated types."""
|
||||
sender, receiver = trio.open_memory_channel(buffer_size)
|
||||
for event_type in event_types:
|
||||
self.channels[event_type].add(sender)
|
||||
return receiver
|
||||
|
||||
@asynccontextmanager
|
||||
async def wait_for(self, event_type: typing.Type[T], buffer_size=10) -> typing.AsyncGenerator[CmEventProxy, None]:
|
||||
"""Wait for an event of the given type and return it.
|
||||
|
||||
This is an async context manager, so you should open it inside
|
||||
an async with block. The block will not exit until the indicated
|
||||
event is received.
|
||||
"""
|
||||
sender: trio.MemorySendChannel
|
||||
receiver: trio.MemoryReceiveChannel
|
||||
sender, receiver = trio.open_memory_channel(buffer_size)
|
||||
self.channels[event_type].add(sender)
|
||||
proxy = CmEventProxy()
|
||||
yield proxy
|
||||
async with receiver:
|
||||
event = await receiver.receive()
|
||||
proxy.value = event
|
||||
|
||||
def _handle_data(self, data):
|
||||
"""Handle incoming WebSocket data.
|
||||
|
||||
:param dict data: a JSON dictionary
|
||||
"""
|
||||
if "id" in data:
|
||||
self._handle_cmd_response(data)
|
||||
else:
|
||||
self._handle_event(data)
|
||||
|
||||
def _handle_cmd_response(self, data):
|
||||
"""Handle a response to a command. This will set an event flag that
|
||||
will return control to the task that called the command.
|
||||
|
||||
:param dict data: response as a JSON dictionary
|
||||
"""
|
||||
cmd_id = data["id"]
|
||||
try:
|
||||
cmd, event = self.inflight_cmd.pop(cmd_id)
|
||||
except KeyError:
|
||||
logger.warning("Got a message with a command ID that does not exist: %s", data)
|
||||
return
|
||||
if "error" in data:
|
||||
# If the server reported an error, convert it to an exception and do
|
||||
# not process the response any further.
|
||||
self.inflight_result[cmd_id] = BrowserError(data["error"])
|
||||
else:
|
||||
# Otherwise, continue the generator to parse the JSON result
|
||||
# into a CDP object.
|
||||
try:
|
||||
_ = cmd.send(data["result"])
|
||||
raise InternalError("The command's generator function did not exit when expected!")
|
||||
except StopIteration as exit:
|
||||
return_ = exit.value
|
||||
self.inflight_result[cmd_id] = return_
|
||||
event.set()
|
||||
|
||||
def _handle_event(self, data):
|
||||
"""Handle an event.
|
||||
|
||||
:param dict data: event as a JSON dictionary
|
||||
"""
|
||||
global devtools
|
||||
event = devtools.util.parse_json_event(data)
|
||||
logger.debug("Received event: %s", event)
|
||||
to_remove = set()
|
||||
for sender in self.channels[type(event)]:
|
||||
try:
|
||||
sender.send_nowait(event)
|
||||
except trio.WouldBlock:
|
||||
logger.error('Unable to send event "%r" due to full channel %s', event, sender)
|
||||
except trio.BrokenResourceError:
|
||||
to_remove.add(sender)
|
||||
if to_remove:
|
||||
self.channels[type(event)] -= to_remove
|
||||
|
||||
|
||||
class CdpSession(CdpBase):
|
||||
"""Contains the state for a CDP session.
|
||||
|
||||
Generally you should not instantiate this object yourself; you should call
|
||||
:meth:`CdpConnection.open_session`.
|
||||
"""
|
||||
|
||||
def __init__(self, ws, session_id, target_id):
|
||||
"""Constructor.
|
||||
|
||||
:param trio_websocket.WebSocketConnection ws:
|
||||
:param devtools.target.SessionID session_id:
|
||||
:param devtools.target.TargetID target_id:
|
||||
"""
|
||||
super().__init__(ws, session_id, target_id)
|
||||
|
||||
self._dom_enable_count = 0
|
||||
self._dom_enable_lock = trio.Lock()
|
||||
self._page_enable_count = 0
|
||||
self._page_enable_lock = trio.Lock()
|
||||
|
||||
@asynccontextmanager
|
||||
async def dom_enable(self):
|
||||
"""A context manager that executes ``dom.enable()`` when it enters and
|
||||
then calls ``dom.disable()``.
|
||||
|
||||
This keeps track of concurrent callers and only disables DOM
|
||||
events when all callers have exited.
|
||||
"""
|
||||
global devtools
|
||||
async with self._dom_enable_lock:
|
||||
self._dom_enable_count += 1
|
||||
if self._dom_enable_count == 1:
|
||||
await self.execute(devtools.dom.enable())
|
||||
|
||||
yield
|
||||
|
||||
async with self._dom_enable_lock:
|
||||
self._dom_enable_count -= 1
|
||||
if self._dom_enable_count == 0:
|
||||
await self.execute(devtools.dom.disable())
|
||||
|
||||
@asynccontextmanager
|
||||
async def page_enable(self):
|
||||
"""A context manager that executes ``page.enable()`` when it enters and
|
||||
then calls ``page.disable()`` when it exits.
|
||||
|
||||
This keeps track of concurrent callers and only disables page
|
||||
events when all callers have exited.
|
||||
"""
|
||||
global devtools
|
||||
async with self._page_enable_lock:
|
||||
self._page_enable_count += 1
|
||||
if self._page_enable_count == 1:
|
||||
await self.execute(devtools.page.enable())
|
||||
|
||||
yield
|
||||
|
||||
async with self._page_enable_lock:
|
||||
self._page_enable_count -= 1
|
||||
if self._page_enable_count == 0:
|
||||
await self.execute(devtools.page.disable())
|
||||
|
||||
|
||||
class CdpConnection(CdpBase, trio.abc.AsyncResource):
|
||||
"""Contains the connection state for a Chrome DevTools Protocol server.
|
||||
|
||||
CDP can multiplex multiple "sessions" over a single connection. This
|
||||
class corresponds to the "root" session, i.e. the implicitly created
|
||||
session that has no session ID. This class is responsible for
|
||||
reading incoming WebSocket messages and forwarding them to the
|
||||
corresponding session, as well as handling messages targeted at the
|
||||
root session itself. You should generally call the
|
||||
:func:`open_cdp()` instead of instantiating this class directly.
|
||||
"""
|
||||
|
||||
def __init__(self, ws):
|
||||
"""Constructor.
|
||||
|
||||
:param trio_websocket.WebSocketConnection ws:
|
||||
"""
|
||||
super().__init__(ws, session_id=None, target_id=None)
|
||||
self.sessions = {}
|
||||
|
||||
async def aclose(self):
|
||||
"""Close the underlying WebSocket connection.
|
||||
|
||||
This will cause the reader task to gracefully exit when it tries
|
||||
to read the next message from the WebSocket. All of the public
|
||||
APIs (``execute()``, ``listen()``, etc.) will raise
|
||||
``CdpConnectionClosed`` after the CDP connection is closed. It
|
||||
is safe to call this multiple times.
|
||||
"""
|
||||
await self.ws.aclose()
|
||||
|
||||
@asynccontextmanager
|
||||
async def open_session(self, target_id) -> typing.AsyncIterator[CdpSession]:
|
||||
"""This context manager opens a session and enables the "simple" style
|
||||
of calling CDP APIs.
|
||||
|
||||
For example, inside a session context, you can call ``await
|
||||
dom.get_document()`` and it will execute on the current session
|
||||
automatically.
|
||||
"""
|
||||
session = await self.connect_session(target_id)
|
||||
with session_context(session):
|
||||
yield session
|
||||
|
||||
async def connect_session(self, target_id) -> "CdpSession":
|
||||
"""Returns a new :class:`CdpSession` connected to the specified
|
||||
target."""
|
||||
global devtools
|
||||
session_id = await self.execute(devtools.target.attach_to_target(target_id, True))
|
||||
session = CdpSession(self.ws, session_id, target_id)
|
||||
self.sessions[session_id] = session
|
||||
return session
|
||||
|
||||
async def _reader_task(self):
|
||||
"""Runs in the background and handles incoming messages: dispatching
|
||||
responses to commands and events to listeners."""
|
||||
global devtools
|
||||
while True:
|
||||
try:
|
||||
message = await self.ws.get_message()
|
||||
except WsConnectionClosed:
|
||||
# If the WebSocket is closed, we don't want to throw an
|
||||
# exception from the reader task. Instead we will throw
|
||||
# exceptions from the public API methods, and we can quietly
|
||||
# exit the reader task here.
|
||||
break
|
||||
try:
|
||||
data = json.loads(message)
|
||||
except json.JSONDecodeError:
|
||||
raise BrowserError({"code": -32700, "message": "Client received invalid JSON", "data": message})
|
||||
logger.debug("Received message %r", data)
|
||||
if "sessionId" in data:
|
||||
session_id = devtools.target.SessionID(data["sessionId"])
|
||||
try:
|
||||
session = self.sessions[session_id]
|
||||
except KeyError:
|
||||
raise BrowserError(
|
||||
{
|
||||
"code": -32700,
|
||||
"message": "Browser sent a message for an invalid session",
|
||||
"data": f"{session_id!r}",
|
||||
}
|
||||
)
|
||||
session._handle_data(data)
|
||||
else:
|
||||
self._handle_data(data)
|
||||
|
||||
for _, session in self.sessions.items():
|
||||
for _, senders in session.channels.items():
|
||||
for sender in senders:
|
||||
sender.close()
|
||||
|
||||
|
||||
@asynccontextmanager
|
||||
async def open_cdp(url) -> typing.AsyncIterator[CdpConnection]:
|
||||
"""This async context manager opens a connection to the browser specified
|
||||
by ``url`` before entering the block, then closes the connection when the
|
||||
block exits.
|
||||
|
||||
The context manager also sets the connection as the default
|
||||
connection for the current task, so that commands like ``await
|
||||
target.get_targets()`` will run on this connection automatically. If
|
||||
you want to use multiple connections concurrently, it is recommended
|
||||
to open each on in a separate task.
|
||||
"""
|
||||
|
||||
async with trio.open_nursery() as nursery:
|
||||
conn = await connect_cdp(nursery, url)
|
||||
try:
|
||||
with connection_context(conn):
|
||||
yield conn
|
||||
finally:
|
||||
await conn.aclose()
|
||||
|
||||
|
||||
async def connect_cdp(nursery, url) -> CdpConnection:
|
||||
"""Connect to the browser specified by ``url`` and spawn a background task
|
||||
in the specified nursery.
|
||||
|
||||
The ``open_cdp()`` context manager is preferred in most situations.
|
||||
You should only use this function if you need to specify a custom
|
||||
nursery. This connection is not automatically closed! You can either
|
||||
use the connection object as a context manager (``async with
|
||||
conn:``) or else call ``await conn.aclose()`` on it when you are
|
||||
done with it. If ``set_context`` is True, then the returned
|
||||
connection will be installed as the default connection for the
|
||||
current task. This argument is for unusual use cases, such as
|
||||
running inside of a notebook.
|
||||
"""
|
||||
ws = await connect_websocket_url(nursery, url, max_message_size=MAX_WS_MESSAGE_SIZE)
|
||||
cdp_conn = CdpConnection(ws)
|
||||
nursery.start_soon(cdp_conn._reader_task)
|
||||
return cdp_conn
|
24
lib/python3.13/site-packages/selenium/webdriver/common/bidi/console.py
Executable file
24
lib/python3.13/site-packages/selenium/webdriver/common/bidi/console.py
Executable file
@ -0,0 +1,24 @@
|
||||
# Licensed to the Software Freedom Conservancy (SFC) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The SFC licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from enum import Enum
|
||||
|
||||
|
||||
class Console(Enum):
|
||||
ALL = "all"
|
||||
LOG = "log"
|
||||
ERROR = "error"
|
110
lib/python3.13/site-packages/selenium/webdriver/common/bidi/script.py
Executable file
110
lib/python3.13/site-packages/selenium/webdriver/common/bidi/script.py
Executable file
@ -0,0 +1,110 @@
|
||||
# Licensed to the Software Freedom Conservancy (SFC) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The SFC licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import typing
|
||||
from dataclasses import dataclass
|
||||
|
||||
from .session import session_subscribe
|
||||
from .session import session_unsubscribe
|
||||
|
||||
|
||||
class Script:
|
||||
def __init__(self, conn):
|
||||
self.conn = conn
|
||||
self.log_entry_subscribed = False
|
||||
|
||||
def add_console_message_handler(self, handler):
|
||||
self._subscribe_to_log_entries()
|
||||
return self.conn.add_callback(LogEntryAdded, self._handle_log_entry("console", handler))
|
||||
|
||||
def add_javascript_error_handler(self, handler):
|
||||
self._subscribe_to_log_entries()
|
||||
return self.conn.add_callback(LogEntryAdded, self._handle_log_entry("javascript", handler))
|
||||
|
||||
def remove_console_message_handler(self, id):
|
||||
self.conn.remove_callback(LogEntryAdded, id)
|
||||
self._unsubscribe_from_log_entries()
|
||||
|
||||
remove_javascript_error_handler = remove_console_message_handler
|
||||
|
||||
def _subscribe_to_log_entries(self):
|
||||
if not self.log_entry_subscribed:
|
||||
self.conn.execute(session_subscribe(LogEntryAdded.event_class))
|
||||
self.log_entry_subscribed = True
|
||||
|
||||
def _unsubscribe_from_log_entries(self):
|
||||
if self.log_entry_subscribed and LogEntryAdded.event_class not in self.conn.callbacks:
|
||||
self.conn.execute(session_unsubscribe(LogEntryAdded.event_class))
|
||||
self.log_entry_subscribed = False
|
||||
|
||||
def _handle_log_entry(self, type, handler):
|
||||
def _handle_log_entry(log_entry):
|
||||
if log_entry.type_ == type:
|
||||
handler(log_entry)
|
||||
|
||||
return _handle_log_entry
|
||||
|
||||
|
||||
class LogEntryAdded:
|
||||
event_class = "log.entryAdded"
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, json):
|
||||
if json["type"] == "console":
|
||||
return ConsoleLogEntry.from_json(json)
|
||||
elif json["type"] == "javascript":
|
||||
return JavaScriptLogEntry.from_json(json)
|
||||
|
||||
|
||||
@dataclass
|
||||
class ConsoleLogEntry:
|
||||
level: str
|
||||
text: str
|
||||
timestamp: str
|
||||
method: str
|
||||
args: typing.List[dict]
|
||||
type_: str
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, json):
|
||||
return cls(
|
||||
level=json["level"],
|
||||
text=json["text"],
|
||||
timestamp=json["timestamp"],
|
||||
method=json["method"],
|
||||
args=json["args"],
|
||||
type_=json["type"],
|
||||
)
|
||||
|
||||
|
||||
@dataclass
|
||||
class JavaScriptLogEntry:
|
||||
level: str
|
||||
text: str
|
||||
timestamp: str
|
||||
stacktrace: dict
|
||||
type_: str
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, json):
|
||||
return cls(
|
||||
level=json["level"],
|
||||
text=json["text"],
|
||||
timestamp=json["timestamp"],
|
||||
stacktrace=json["stackTrace"],
|
||||
type_=json["type"],
|
||||
)
|
46
lib/python3.13/site-packages/selenium/webdriver/common/bidi/session.py
Executable file
46
lib/python3.13/site-packages/selenium/webdriver/common/bidi/session.py
Executable file
@ -0,0 +1,46 @@
|
||||
# Licensed to the Software Freedom Conservancy (SFC) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The SFC licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
|
||||
def session_subscribe(*events, browsing_contexts=None):
|
||||
cmd_dict = {
|
||||
"method": "session.subscribe",
|
||||
"params": {
|
||||
"events": events,
|
||||
},
|
||||
}
|
||||
if browsing_contexts is None:
|
||||
browsing_contexts = []
|
||||
if browsing_contexts:
|
||||
cmd_dict["params"]["browsingContexts"] = browsing_contexts
|
||||
_ = yield cmd_dict
|
||||
return None
|
||||
|
||||
|
||||
def session_unsubscribe(*events, browsing_contexts=None):
|
||||
cmd_dict = {
|
||||
"method": "session.unsubscribe",
|
||||
"params": {
|
||||
"events": events,
|
||||
},
|
||||
}
|
||||
if browsing_contexts is None:
|
||||
browsing_contexts = []
|
||||
if browsing_contexts:
|
||||
cmd_dict["params"]["browsingContexts"] = browsing_contexts
|
||||
_ = yield cmd_dict
|
||||
return None
|
51
lib/python3.13/site-packages/selenium/webdriver/common/by.py
Executable file
51
lib/python3.13/site-packages/selenium/webdriver/common/by.py
Executable file
@ -0,0 +1,51 @@
|
||||
# Licensed to the Software Freedom Conservancy (SFC) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The SFC licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
"""The By implementation."""
|
||||
|
||||
from typing import Dict
|
||||
from typing import Literal
|
||||
from typing import Optional
|
||||
|
||||
|
||||
class By:
|
||||
"""Set of supported locator strategies."""
|
||||
|
||||
ID = "id"
|
||||
XPATH = "xpath"
|
||||
LINK_TEXT = "link text"
|
||||
PARTIAL_LINK_TEXT = "partial link text"
|
||||
NAME = "name"
|
||||
TAG_NAME = "tag name"
|
||||
CLASS_NAME = "class name"
|
||||
CSS_SELECTOR = "css selector"
|
||||
|
||||
_custom_finders: Dict[str, str] = {}
|
||||
|
||||
@classmethod
|
||||
def register_custom_finder(cls, name: str, strategy: str) -> None:
|
||||
cls._custom_finders[name] = strategy
|
||||
|
||||
@classmethod
|
||||
def get_finder(cls, name: str) -> Optional[str]:
|
||||
return cls._custom_finders.get(name) or getattr(cls, name.upper(), None)
|
||||
|
||||
@classmethod
|
||||
def clear_custom_finders(cls) -> None:
|
||||
cls._custom_finders.clear()
|
||||
|
||||
|
||||
ByType = Literal["id", "xpath", "link text", "partial link text", "name", "tag name", "class name", "css selector"]
|
100
lib/python3.13/site-packages/selenium/webdriver/common/desired_capabilities.py
Executable file
100
lib/python3.13/site-packages/selenium/webdriver/common/desired_capabilities.py
Executable file
@ -0,0 +1,100 @@
|
||||
# Licensed to the Software Freedom Conservancy (SFC) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The SFC licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
"""The Desired Capabilities implementation."""
|
||||
|
||||
|
||||
class DesiredCapabilities:
|
||||
"""Set of default supported desired capabilities.
|
||||
|
||||
Use this as a starting point for creating a desired capabilities object for
|
||||
requesting remote webdrivers for connecting to selenium server or selenium grid.
|
||||
|
||||
Usage Example::
|
||||
|
||||
from selenium import webdriver
|
||||
|
||||
selenium_grid_url = "http://198.0.0.1:4444/wd/hub"
|
||||
|
||||
# Create a desired capabilities object as a starting point.
|
||||
capabilities = DesiredCapabilities.FIREFOX.copy()
|
||||
capabilities['platform'] = "WINDOWS"
|
||||
capabilities['version'] = "10"
|
||||
|
||||
# Instantiate an instance of Remote WebDriver with the desired capabilities.
|
||||
driver = webdriver.Remote(desired_capabilities=capabilities,
|
||||
command_executor=selenium_grid_url)
|
||||
|
||||
Note: Always use '.copy()' on the DesiredCapabilities object to avoid the side
|
||||
effects of altering the Global class instance.
|
||||
"""
|
||||
|
||||
FIREFOX = {
|
||||
"browserName": "firefox",
|
||||
"acceptInsecureCerts": True,
|
||||
"moz:debuggerAddress": True,
|
||||
}
|
||||
|
||||
INTERNETEXPLORER = {
|
||||
"browserName": "internet explorer",
|
||||
"platformName": "windows",
|
||||
}
|
||||
|
||||
EDGE = {
|
||||
"browserName": "MicrosoftEdge",
|
||||
}
|
||||
|
||||
CHROME = {
|
||||
"browserName": "chrome",
|
||||
}
|
||||
|
||||
SAFARI = {
|
||||
"browserName": "safari",
|
||||
"platformName": "mac",
|
||||
}
|
||||
|
||||
HTMLUNIT = {
|
||||
"browserName": "htmlunit",
|
||||
"version": "",
|
||||
"platform": "ANY",
|
||||
}
|
||||
|
||||
HTMLUNITWITHJS = {
|
||||
"browserName": "htmlunit",
|
||||
"version": "firefox",
|
||||
"platform": "ANY",
|
||||
"javascriptEnabled": True,
|
||||
}
|
||||
|
||||
IPHONE = {
|
||||
"browserName": "iPhone",
|
||||
"version": "",
|
||||
"platform": "mac",
|
||||
}
|
||||
|
||||
IPAD = {
|
||||
"browserName": "iPad",
|
||||
"version": "",
|
||||
"platform": "mac",
|
||||
}
|
||||
|
||||
WEBKITGTK = {
|
||||
"browserName": "MiniBrowser",
|
||||
}
|
||||
|
||||
WPEWEBKIT = {
|
||||
"browserName": "MiniBrowser",
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
# DO NOT EDIT THIS FILE!
|
||||
#
|
||||
# This file is generated from the CDP specification. If you need to make
|
||||
# changes, edit the generator and regenerate all of the modules.
|
||||
from . import accessibility
|
||||
from . import animation
|
||||
from . import audits
|
||||
from . import autofill
|
||||
from . import background_service
|
||||
from . import browser
|
||||
from . import css
|
||||
from . import cache_storage
|
||||
from . import cast
|
||||
from . import console
|
||||
from . import dom
|
||||
from . import dom_debugger
|
||||
from . import dom_snapshot
|
||||
from . import dom_storage
|
||||
from . import database
|
||||
from . import debugger
|
||||
from . import device_access
|
||||
from . import device_orientation
|
||||
from . import emulation
|
||||
from . import event_breakpoints
|
||||
from . import extensions
|
||||
from . import fed_cm
|
||||
from . import fetch
|
||||
from . import file_system
|
||||
from . import headless_experimental
|
||||
from . import heap_profiler
|
||||
from . import io
|
||||
from . import indexed_db
|
||||
from . import input_
|
||||
from . import inspector
|
||||
from . import layer_tree
|
||||
from . import log
|
||||
from . import media
|
||||
from . import memory
|
||||
from . import network
|
||||
from . import overlay
|
||||
from . import pwa
|
||||
from . import page
|
||||
from . import performance
|
||||
from . import performance_timeline
|
||||
from . import preload
|
||||
from . import profiler
|
||||
from . import runtime
|
||||
from . import schema
|
||||
from . import security
|
||||
from . import service_worker
|
||||
from . import storage
|
||||
from . import system_info
|
||||
from . import target
|
||||
from . import tethering
|
||||
from . import tracing
|
||||
from . import web_audio
|
||||
from . import web_authn
|
||||
from . import util
|
||||
|
@ -0,0 +1,647 @@
|
||||
# DO NOT EDIT THIS FILE!
|
||||
#
|
||||
# This file is generated from the CDP specification. If you need to make
|
||||
# changes, edit the generator and regenerate all of the modules.
|
||||
#
|
||||
# CDP domain: Accessibility (experimental)
|
||||
from __future__ import annotations
|
||||
from .util import event_class, T_JSON_DICT
|
||||
from dataclasses import dataclass
|
||||
import enum
|
||||
import typing
|
||||
from . import dom
|
||||
from . import page
|
||||
from . import runtime
|
||||
|
||||
|
||||
class AXNodeId(str):
|
||||
'''
|
||||
Unique accessibility node identifier.
|
||||
'''
|
||||
def to_json(self) -> str:
|
||||
return self
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, json: str) -> AXNodeId:
|
||||
return cls(json)
|
||||
|
||||
def __repr__(self):
|
||||
return 'AXNodeId({})'.format(super().__repr__())
|
||||
|
||||
|
||||
class AXValueType(enum.Enum):
|
||||
'''
|
||||
Enum of possible property types.
|
||||
'''
|
||||
BOOLEAN = "boolean"
|
||||
TRISTATE = "tristate"
|
||||
BOOLEAN_OR_UNDEFINED = "booleanOrUndefined"
|
||||
IDREF = "idref"
|
||||
IDREF_LIST = "idrefList"
|
||||
INTEGER = "integer"
|
||||
NODE = "node"
|
||||
NODE_LIST = "nodeList"
|
||||
NUMBER = "number"
|
||||
STRING = "string"
|
||||
COMPUTED_STRING = "computedString"
|
||||
TOKEN = "token"
|
||||
TOKEN_LIST = "tokenList"
|
||||
DOM_RELATION = "domRelation"
|
||||
ROLE = "role"
|
||||
INTERNAL_ROLE = "internalRole"
|
||||
VALUE_UNDEFINED = "valueUndefined"
|
||||
|
||||
def to_json(self):
|
||||
return self.value
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, json):
|
||||
return cls(json)
|
||||
|
||||
|
||||
class AXValueSourceType(enum.Enum):
|
||||
'''
|
||||
Enum of possible property sources.
|
||||
'''
|
||||
ATTRIBUTE = "attribute"
|
||||
IMPLICIT = "implicit"
|
||||
STYLE = "style"
|
||||
CONTENTS = "contents"
|
||||
PLACEHOLDER = "placeholder"
|
||||
RELATED_ELEMENT = "relatedElement"
|
||||
|
||||
def to_json(self):
|
||||
return self.value
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, json):
|
||||
return cls(json)
|
||||
|
||||
|
||||
class AXValueNativeSourceType(enum.Enum):
|
||||
'''
|
||||
Enum of possible native property sources (as a subtype of a particular AXValueSourceType).
|
||||
'''
|
||||
DESCRIPTION = "description"
|
||||
FIGCAPTION = "figcaption"
|
||||
LABEL = "label"
|
||||
LABELFOR = "labelfor"
|
||||
LABELWRAPPED = "labelwrapped"
|
||||
LEGEND = "legend"
|
||||
RUBYANNOTATION = "rubyannotation"
|
||||
TABLECAPTION = "tablecaption"
|
||||
TITLE = "title"
|
||||
OTHER = "other"
|
||||
|
||||
def to_json(self):
|
||||
return self.value
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, json):
|
||||
return cls(json)
|
||||
|
||||
|
||||
@dataclass
|
||||
class AXValueSource:
|
||||
'''
|
||||
A single source for a computed AX property.
|
||||
'''
|
||||
#: What type of source this is.
|
||||
type_: AXValueSourceType
|
||||
|
||||
#: The value of this property source.
|
||||
value: typing.Optional[AXValue] = None
|
||||
|
||||
#: The name of the relevant attribute, if any.
|
||||
attribute: typing.Optional[str] = None
|
||||
|
||||
#: The value of the relevant attribute, if any.
|
||||
attribute_value: typing.Optional[AXValue] = None
|
||||
|
||||
#: Whether this source is superseded by a higher priority source.
|
||||
superseded: typing.Optional[bool] = None
|
||||
|
||||
#: The native markup source for this value, e.g. a ``<label>`` element.
|
||||
native_source: typing.Optional[AXValueNativeSourceType] = None
|
||||
|
||||
#: The value, such as a node or node list, of the native source.
|
||||
native_source_value: typing.Optional[AXValue] = None
|
||||
|
||||
#: Whether the value for this property is invalid.
|
||||
invalid: typing.Optional[bool] = None
|
||||
|
||||
#: Reason for the value being invalid, if it is.
|
||||
invalid_reason: typing.Optional[str] = None
|
||||
|
||||
def to_json(self):
|
||||
json = dict()
|
||||
json['type'] = self.type_.to_json()
|
||||
if self.value is not None:
|
||||
json['value'] = self.value.to_json()
|
||||
if self.attribute is not None:
|
||||
json['attribute'] = self.attribute
|
||||
if self.attribute_value is not None:
|
||||
json['attributeValue'] = self.attribute_value.to_json()
|
||||
if self.superseded is not None:
|
||||
json['superseded'] = self.superseded
|
||||
if self.native_source is not None:
|
||||
json['nativeSource'] = self.native_source.to_json()
|
||||
if self.native_source_value is not None:
|
||||
json['nativeSourceValue'] = self.native_source_value.to_json()
|
||||
if self.invalid is not None:
|
||||
json['invalid'] = self.invalid
|
||||
if self.invalid_reason is not None:
|
||||
json['invalidReason'] = self.invalid_reason
|
||||
return json
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, json):
|
||||
return cls(
|
||||
type_=AXValueSourceType.from_json(json['type']),
|
||||
value=AXValue.from_json(json['value']) if 'value' in json else None,
|
||||
attribute=str(json['attribute']) if 'attribute' in json else None,
|
||||
attribute_value=AXValue.from_json(json['attributeValue']) if 'attributeValue' in json else None,
|
||||
superseded=bool(json['superseded']) if 'superseded' in json else None,
|
||||
native_source=AXValueNativeSourceType.from_json(json['nativeSource']) if 'nativeSource' in json else None,
|
||||
native_source_value=AXValue.from_json(json['nativeSourceValue']) if 'nativeSourceValue' in json else None,
|
||||
invalid=bool(json['invalid']) if 'invalid' in json else None,
|
||||
invalid_reason=str(json['invalidReason']) if 'invalidReason' in json else None,
|
||||
)
|
||||
|
||||
|
||||
@dataclass
|
||||
class AXRelatedNode:
|
||||
#: The BackendNodeId of the related DOM node.
|
||||
backend_dom_node_id: dom.BackendNodeId
|
||||
|
||||
#: The IDRef value provided, if any.
|
||||
idref: typing.Optional[str] = None
|
||||
|
||||
#: The text alternative of this node in the current context.
|
||||
text: typing.Optional[str] = None
|
||||
|
||||
def to_json(self):
|
||||
json = dict()
|
||||
json['backendDOMNodeId'] = self.backend_dom_node_id.to_json()
|
||||
if self.idref is not None:
|
||||
json['idref'] = self.idref
|
||||
if self.text is not None:
|
||||
json['text'] = self.text
|
||||
return json
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, json):
|
||||
return cls(
|
||||
backend_dom_node_id=dom.BackendNodeId.from_json(json['backendDOMNodeId']),
|
||||
idref=str(json['idref']) if 'idref' in json else None,
|
||||
text=str(json['text']) if 'text' in json else None,
|
||||
)
|
||||
|
||||
|
||||
@dataclass
|
||||
class AXProperty:
|
||||
#: The name of this property.
|
||||
name: AXPropertyName
|
||||
|
||||
#: The value of this property.
|
||||
value: AXValue
|
||||
|
||||
def to_json(self):
|
||||
json = dict()
|
||||
json['name'] = self.name.to_json()
|
||||
json['value'] = self.value.to_json()
|
||||
return json
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, json):
|
||||
return cls(
|
||||
name=AXPropertyName.from_json(json['name']),
|
||||
value=AXValue.from_json(json['value']),
|
||||
)
|
||||
|
||||
|
||||
@dataclass
|
||||
class AXValue:
|
||||
'''
|
||||
A single computed AX property.
|
||||
'''
|
||||
#: The type of this value.
|
||||
type_: AXValueType
|
||||
|
||||
#: The computed value of this property.
|
||||
value: typing.Optional[typing.Any] = None
|
||||
|
||||
#: One or more related nodes, if applicable.
|
||||
related_nodes: typing.Optional[typing.List[AXRelatedNode]] = None
|
||||
|
||||
#: The sources which contributed to the computation of this property.
|
||||
sources: typing.Optional[typing.List[AXValueSource]] = None
|
||||
|
||||
def to_json(self):
|
||||
json = dict()
|
||||
json['type'] = self.type_.to_json()
|
||||
if self.value is not None:
|
||||
json['value'] = self.value
|
||||
if self.related_nodes is not None:
|
||||
json['relatedNodes'] = [i.to_json() for i in self.related_nodes]
|
||||
if self.sources is not None:
|
||||
json['sources'] = [i.to_json() for i in self.sources]
|
||||
return json
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, json):
|
||||
return cls(
|
||||
type_=AXValueType.from_json(json['type']),
|
||||
value=json['value'] if 'value' in json else None,
|
||||
related_nodes=[AXRelatedNode.from_json(i) for i in json['relatedNodes']] if 'relatedNodes' in json else None,
|
||||
sources=[AXValueSource.from_json(i) for i in json['sources']] if 'sources' in json else None,
|
||||
)
|
||||
|
||||
|
||||
class AXPropertyName(enum.Enum):
|
||||
'''
|
||||
Values of AXProperty name:
|
||||
- from 'busy' to 'roledescription': states which apply to every AX node
|
||||
- from 'live' to 'root': attributes which apply to nodes in live regions
|
||||
- from 'autocomplete' to 'valuetext': attributes which apply to widgets
|
||||
- from 'checked' to 'selected': states which apply to widgets
|
||||
- from 'activedescendant' to 'owns' - relationships between elements other than parent/child/sibling.
|
||||
'''
|
||||
BUSY = "busy"
|
||||
DISABLED = "disabled"
|
||||
EDITABLE = "editable"
|
||||
FOCUSABLE = "focusable"
|
||||
FOCUSED = "focused"
|
||||
HIDDEN = "hidden"
|
||||
HIDDEN_ROOT = "hiddenRoot"
|
||||
INVALID = "invalid"
|
||||
KEYSHORTCUTS = "keyshortcuts"
|
||||
SETTABLE = "settable"
|
||||
ROLEDESCRIPTION = "roledescription"
|
||||
LIVE = "live"
|
||||
ATOMIC = "atomic"
|
||||
RELEVANT = "relevant"
|
||||
ROOT = "root"
|
||||
AUTOCOMPLETE = "autocomplete"
|
||||
HAS_POPUP = "hasPopup"
|
||||
LEVEL = "level"
|
||||
MULTISELECTABLE = "multiselectable"
|
||||
ORIENTATION = "orientation"
|
||||
MULTILINE = "multiline"
|
||||
READONLY = "readonly"
|
||||
REQUIRED = "required"
|
||||
VALUEMIN = "valuemin"
|
||||
VALUEMAX = "valuemax"
|
||||
VALUETEXT = "valuetext"
|
||||
CHECKED = "checked"
|
||||
EXPANDED = "expanded"
|
||||
MODAL = "modal"
|
||||
PRESSED = "pressed"
|
||||
SELECTED = "selected"
|
||||
ACTIVEDESCENDANT = "activedescendant"
|
||||
CONTROLS = "controls"
|
||||
DESCRIBEDBY = "describedby"
|
||||
DETAILS = "details"
|
||||
ERRORMESSAGE = "errormessage"
|
||||
FLOWTO = "flowto"
|
||||
LABELLEDBY = "labelledby"
|
||||
OWNS = "owns"
|
||||
URL = "url"
|
||||
|
||||
def to_json(self):
|
||||
return self.value
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, json):
|
||||
return cls(json)
|
||||
|
||||
|
||||
@dataclass
|
||||
class AXNode:
|
||||
'''
|
||||
A node in the accessibility tree.
|
||||
'''
|
||||
#: Unique identifier for this node.
|
||||
node_id: AXNodeId
|
||||
|
||||
#: Whether this node is ignored for accessibility
|
||||
ignored: bool
|
||||
|
||||
#: Collection of reasons why this node is hidden.
|
||||
ignored_reasons: typing.Optional[typing.List[AXProperty]] = None
|
||||
|
||||
#: This ``Node``'s role, whether explicit or implicit.
|
||||
role: typing.Optional[AXValue] = None
|
||||
|
||||
#: This ``Node``'s Chrome raw role.
|
||||
chrome_role: typing.Optional[AXValue] = None
|
||||
|
||||
#: The accessible name for this ``Node``.
|
||||
name: typing.Optional[AXValue] = None
|
||||
|
||||
#: The accessible description for this ``Node``.
|
||||
description: typing.Optional[AXValue] = None
|
||||
|
||||
#: The value for this ``Node``.
|
||||
value: typing.Optional[AXValue] = None
|
||||
|
||||
#: All other properties
|
||||
properties: typing.Optional[typing.List[AXProperty]] = None
|
||||
|
||||
#: ID for this node's parent.
|
||||
parent_id: typing.Optional[AXNodeId] = None
|
||||
|
||||
#: IDs for each of this node's child nodes.
|
||||
child_ids: typing.Optional[typing.List[AXNodeId]] = None
|
||||
|
||||
#: The backend ID for the associated DOM node, if any.
|
||||
backend_dom_node_id: typing.Optional[dom.BackendNodeId] = None
|
||||
|
||||
#: The frame ID for the frame associated with this nodes document.
|
||||
frame_id: typing.Optional[page.FrameId] = None
|
||||
|
||||
def to_json(self):
|
||||
json = dict()
|
||||
json['nodeId'] = self.node_id.to_json()
|
||||
json['ignored'] = self.ignored
|
||||
if self.ignored_reasons is not None:
|
||||
json['ignoredReasons'] = [i.to_json() for i in self.ignored_reasons]
|
||||
if self.role is not None:
|
||||
json['role'] = self.role.to_json()
|
||||
if self.chrome_role is not None:
|
||||
json['chromeRole'] = self.chrome_role.to_json()
|
||||
if self.name is not None:
|
||||
json['name'] = self.name.to_json()
|
||||
if self.description is not None:
|
||||
json['description'] = self.description.to_json()
|
||||
if self.value is not None:
|
||||
json['value'] = self.value.to_json()
|
||||
if self.properties is not None:
|
||||
json['properties'] = [i.to_json() for i in self.properties]
|
||||
if self.parent_id is not None:
|
||||
json['parentId'] = self.parent_id.to_json()
|
||||
if self.child_ids is not None:
|
||||
json['childIds'] = [i.to_json() for i in self.child_ids]
|
||||
if self.backend_dom_node_id is not None:
|
||||
json['backendDOMNodeId'] = self.backend_dom_node_id.to_json()
|
||||
if self.frame_id is not None:
|
||||
json['frameId'] = self.frame_id.to_json()
|
||||
return json
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, json):
|
||||
return cls(
|
||||
node_id=AXNodeId.from_json(json['nodeId']),
|
||||
ignored=bool(json['ignored']),
|
||||
ignored_reasons=[AXProperty.from_json(i) for i in json['ignoredReasons']] if 'ignoredReasons' in json else None,
|
||||
role=AXValue.from_json(json['role']) if 'role' in json else None,
|
||||
chrome_role=AXValue.from_json(json['chromeRole']) if 'chromeRole' in json else None,
|
||||
name=AXValue.from_json(json['name']) if 'name' in json else None,
|
||||
description=AXValue.from_json(json['description']) if 'description' in json else None,
|
||||
value=AXValue.from_json(json['value']) if 'value' in json else None,
|
||||
properties=[AXProperty.from_json(i) for i in json['properties']] if 'properties' in json else None,
|
||||
parent_id=AXNodeId.from_json(json['parentId']) if 'parentId' in json else None,
|
||||
child_ids=[AXNodeId.from_json(i) for i in json['childIds']] if 'childIds' in json else None,
|
||||
backend_dom_node_id=dom.BackendNodeId.from_json(json['backendDOMNodeId']) if 'backendDOMNodeId' in json else None,
|
||||
frame_id=page.FrameId.from_json(json['frameId']) if 'frameId' in json else None,
|
||||
)
|
||||
|
||||
|
||||
def disable() -> typing.Generator[T_JSON_DICT,T_JSON_DICT,None]:
|
||||
'''
|
||||
Disables the accessibility domain.
|
||||
'''
|
||||
cmd_dict: T_JSON_DICT = {
|
||||
'method': 'Accessibility.disable',
|
||||
}
|
||||
json = yield cmd_dict
|
||||
|
||||
|
||||
def enable() -> typing.Generator[T_JSON_DICT,T_JSON_DICT,None]:
|
||||
'''
|
||||
Enables the accessibility domain which causes ``AXNodeId``'s to remain consistent between method calls.
|
||||
This turns on accessibility for the page, which can impact performance until accessibility is disabled.
|
||||
'''
|
||||
cmd_dict: T_JSON_DICT = {
|
||||
'method': 'Accessibility.enable',
|
||||
}
|
||||
json = yield cmd_dict
|
||||
|
||||
|
||||
def get_partial_ax_tree(
|
||||
node_id: typing.Optional[dom.NodeId] = None,
|
||||
backend_node_id: typing.Optional[dom.BackendNodeId] = None,
|
||||
object_id: typing.Optional[runtime.RemoteObjectId] = None,
|
||||
fetch_relatives: typing.Optional[bool] = None
|
||||
) -> typing.Generator[T_JSON_DICT,T_JSON_DICT,typing.List[AXNode]]:
|
||||
'''
|
||||
Fetches the accessibility node and partial accessibility tree for this DOM node, if it exists.
|
||||
|
||||
**EXPERIMENTAL**
|
||||
|
||||
:param node_id: *(Optional)* Identifier of the node to get the partial accessibility tree for.
|
||||
:param backend_node_id: *(Optional)* Identifier of the backend node to get the partial accessibility tree for.
|
||||
:param object_id: *(Optional)* JavaScript object id of the node wrapper to get the partial accessibility tree for.
|
||||
:param fetch_relatives: *(Optional)* Whether to fetch this node's ancestors, siblings and children. Defaults to true.
|
||||
:returns: The ``Accessibility.AXNode`` for this DOM node, if it exists, plus its ancestors, siblings and children, if requested.
|
||||
'''
|
||||
params: T_JSON_DICT = dict()
|
||||
if node_id is not None:
|
||||
params['nodeId'] = node_id.to_json()
|
||||
if backend_node_id is not None:
|
||||
params['backendNodeId'] = backend_node_id.to_json()
|
||||
if object_id is not None:
|
||||
params['objectId'] = object_id.to_json()
|
||||
if fetch_relatives is not None:
|
||||
params['fetchRelatives'] = fetch_relatives
|
||||
cmd_dict: T_JSON_DICT = {
|
||||
'method': 'Accessibility.getPartialAXTree',
|
||||
'params': params,
|
||||
}
|
||||
json = yield cmd_dict
|
||||
return [AXNode.from_json(i) for i in json['nodes']]
|
||||
|
||||
|
||||
def get_full_ax_tree(
|
||||
depth: typing.Optional[int] = None,
|
||||
frame_id: typing.Optional[page.FrameId] = None
|
||||
) -> typing.Generator[T_JSON_DICT,T_JSON_DICT,typing.List[AXNode]]:
|
||||
'''
|
||||
Fetches the entire accessibility tree for the root Document
|
||||
|
||||
**EXPERIMENTAL**
|
||||
|
||||
:param depth: *(Optional)* The maximum depth at which descendants of the root node should be retrieved. If omitted, the full tree is returned.
|
||||
:param frame_id: *(Optional)* The frame for whose document the AX tree should be retrieved. If omitted, the root frame is used.
|
||||
:returns:
|
||||
'''
|
||||
params: T_JSON_DICT = dict()
|
||||
if depth is not None:
|
||||
params['depth'] = depth
|
||||
if frame_id is not None:
|
||||
params['frameId'] = frame_id.to_json()
|
||||
cmd_dict: T_JSON_DICT = {
|
||||
'method': 'Accessibility.getFullAXTree',
|
||||
'params': params,
|
||||
}
|
||||
json = yield cmd_dict
|
||||
return [AXNode.from_json(i) for i in json['nodes']]
|
||||
|
||||
|
||||
def get_root_ax_node(
|
||||
frame_id: typing.Optional[page.FrameId] = None
|
||||
) -> typing.Generator[T_JSON_DICT,T_JSON_DICT,AXNode]:
|
||||
'''
|
||||
Fetches the root node.
|
||||
Requires ``enable()`` to have been called previously.
|
||||
|
||||
**EXPERIMENTAL**
|
||||
|
||||
:param frame_id: *(Optional)* The frame in whose document the node resides. If omitted, the root frame is used.
|
||||
:returns:
|
||||
'''
|
||||
params: T_JSON_DICT = dict()
|
||||
if frame_id is not None:
|
||||
params['frameId'] = frame_id.to_json()
|
||||
cmd_dict: T_JSON_DICT = {
|
||||
'method': 'Accessibility.getRootAXNode',
|
||||
'params': params,
|
||||
}
|
||||
json = yield cmd_dict
|
||||
return AXNode.from_json(json['node'])
|
||||
|
||||
|
||||
def get_ax_node_and_ancestors(
|
||||
node_id: typing.Optional[dom.NodeId] = None,
|
||||
backend_node_id: typing.Optional[dom.BackendNodeId] = None,
|
||||
object_id: typing.Optional[runtime.RemoteObjectId] = None
|
||||
) -> typing.Generator[T_JSON_DICT,T_JSON_DICT,typing.List[AXNode]]:
|
||||
'''
|
||||
Fetches a node and all ancestors up to and including the root.
|
||||
Requires ``enable()`` to have been called previously.
|
||||
|
||||
**EXPERIMENTAL**
|
||||
|
||||
:param node_id: *(Optional)* Identifier of the node to get.
|
||||
:param backend_node_id: *(Optional)* Identifier of the backend node to get.
|
||||
:param object_id: *(Optional)* JavaScript object id of the node wrapper to get.
|
||||
:returns:
|
||||
'''
|
||||
params: T_JSON_DICT = dict()
|
||||
if node_id is not None:
|
||||
params['nodeId'] = node_id.to_json()
|
||||
if backend_node_id is not None:
|
||||
params['backendNodeId'] = backend_node_id.to_json()
|
||||
if object_id is not None:
|
||||
params['objectId'] = object_id.to_json()
|
||||
cmd_dict: T_JSON_DICT = {
|
||||
'method': 'Accessibility.getAXNodeAndAncestors',
|
||||
'params': params,
|
||||
}
|
||||
json = yield cmd_dict
|
||||
return [AXNode.from_json(i) for i in json['nodes']]
|
||||
|
||||
|
||||
def get_child_ax_nodes(
|
||||
id_: AXNodeId,
|
||||
frame_id: typing.Optional[page.FrameId] = None
|
||||
) -> typing.Generator[T_JSON_DICT,T_JSON_DICT,typing.List[AXNode]]:
|
||||
'''
|
||||
Fetches a particular accessibility node by AXNodeId.
|
||||
Requires ``enable()`` to have been called previously.
|
||||
|
||||
**EXPERIMENTAL**
|
||||
|
||||
:param id_:
|
||||
:param frame_id: *(Optional)* The frame in whose document the node resides. If omitted, the root frame is used.
|
||||
:returns:
|
||||
'''
|
||||
params: T_JSON_DICT = dict()
|
||||
params['id'] = id_.to_json()
|
||||
if frame_id is not None:
|
||||
params['frameId'] = frame_id.to_json()
|
||||
cmd_dict: T_JSON_DICT = {
|
||||
'method': 'Accessibility.getChildAXNodes',
|
||||
'params': params,
|
||||
}
|
||||
json = yield cmd_dict
|
||||
return [AXNode.from_json(i) for i in json['nodes']]
|
||||
|
||||
|
||||
def query_ax_tree(
|
||||
node_id: typing.Optional[dom.NodeId] = None,
|
||||
backend_node_id: typing.Optional[dom.BackendNodeId] = None,
|
||||
object_id: typing.Optional[runtime.RemoteObjectId] = None,
|
||||
accessible_name: typing.Optional[str] = None,
|
||||
role: typing.Optional[str] = None
|
||||
) -> typing.Generator[T_JSON_DICT,T_JSON_DICT,typing.List[AXNode]]:
|
||||
'''
|
||||
Query a DOM node's accessibility subtree for accessible name and role.
|
||||
This command computes the name and role for all nodes in the subtree, including those that are
|
||||
ignored for accessibility, and returns those that match the specified name and role. If no DOM
|
||||
node is specified, or the DOM node does not exist, the command returns an error. If neither
|
||||
``accessibleName`` or ``role`` is specified, it returns all the accessibility nodes in the subtree.
|
||||
|
||||
**EXPERIMENTAL**
|
||||
|
||||
:param node_id: *(Optional)* Identifier of the node for the root to query.
|
||||
:param backend_node_id: *(Optional)* Identifier of the backend node for the root to query.
|
||||
:param object_id: *(Optional)* JavaScript object id of the node wrapper for the root to query.
|
||||
:param accessible_name: *(Optional)* Find nodes with this computed name.
|
||||
:param role: *(Optional)* Find nodes with this computed role.
|
||||
:returns: A list of ``Accessibility.AXNode`` matching the specified attributes, including nodes that are ignored for accessibility.
|
||||
'''
|
||||
params: T_JSON_DICT = dict()
|
||||
if node_id is not None:
|
||||
params['nodeId'] = node_id.to_json()
|
||||
if backend_node_id is not None:
|
||||
params['backendNodeId'] = backend_node_id.to_json()
|
||||
if object_id is not None:
|
||||
params['objectId'] = object_id.to_json()
|
||||
if accessible_name is not None:
|
||||
params['accessibleName'] = accessible_name
|
||||
if role is not None:
|
||||
params['role'] = role
|
||||
cmd_dict: T_JSON_DICT = {
|
||||
'method': 'Accessibility.queryAXTree',
|
||||
'params': params,
|
||||
}
|
||||
json = yield cmd_dict
|
||||
return [AXNode.from_json(i) for i in json['nodes']]
|
||||
|
||||
|
||||
@event_class('Accessibility.loadComplete')
|
||||
@dataclass
|
||||
class LoadComplete:
|
||||
'''
|
||||
**EXPERIMENTAL**
|
||||
|
||||
The loadComplete event mirrors the load complete event sent by the browser to assistive
|
||||
technology when the web page has finished loading.
|
||||
'''
|
||||
#: New document root node.
|
||||
root: AXNode
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, json: T_JSON_DICT) -> LoadComplete:
|
||||
return cls(
|
||||
root=AXNode.from_json(json['root'])
|
||||
)
|
||||
|
||||
|
||||
@event_class('Accessibility.nodesUpdated')
|
||||
@dataclass
|
||||
class NodesUpdated:
|
||||
'''
|
||||
**EXPERIMENTAL**
|
||||
|
||||
The nodesUpdated event is sent every time a previously requested node has changed the in tree.
|
||||
'''
|
||||
#: Updated node data.
|
||||
nodes: typing.List[AXNode]
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, json: T_JSON_DICT) -> NodesUpdated:
|
||||
return cls(
|
||||
nodes=[AXNode.from_json(i) for i in json['nodes']]
|
||||
)
|
@ -0,0 +1,491 @@
|
||||
# DO NOT EDIT THIS FILE!
|
||||
#
|
||||
# This file is generated from the CDP specification. If you need to make
|
||||
# changes, edit the generator and regenerate all of the modules.
|
||||
#
|
||||
# CDP domain: Animation (experimental)
|
||||
from __future__ import annotations
|
||||
from .util import event_class, T_JSON_DICT
|
||||
from dataclasses import dataclass
|
||||
import enum
|
||||
import typing
|
||||
from . import dom
|
||||
from . import runtime
|
||||
|
||||
|
||||
@dataclass
|
||||
class Animation:
|
||||
'''
|
||||
Animation instance.
|
||||
'''
|
||||
#: ``Animation``'s id.
|
||||
id_: str
|
||||
|
||||
#: ``Animation``'s name.
|
||||
name: str
|
||||
|
||||
#: ``Animation``'s internal paused state.
|
||||
paused_state: bool
|
||||
|
||||
#: ``Animation``'s play state.
|
||||
play_state: str
|
||||
|
||||
#: ``Animation``'s playback rate.
|
||||
playback_rate: float
|
||||
|
||||
#: ``Animation``'s start time.
|
||||
#: Milliseconds for time based animations and
|
||||
#: percentage [0 - 100] for scroll driven animations
|
||||
#: (i.e. when viewOrScrollTimeline exists).
|
||||
start_time: float
|
||||
|
||||
#: ``Animation``'s current time.
|
||||
current_time: float
|
||||
|
||||
#: Animation type of ``Animation``.
|
||||
type_: str
|
||||
|
||||
#: ``Animation``'s source animation node.
|
||||
source: typing.Optional[AnimationEffect] = None
|
||||
|
||||
#: A unique ID for ``Animation`` representing the sources that triggered this CSS
|
||||
#: animation/transition.
|
||||
css_id: typing.Optional[str] = None
|
||||
|
||||
#: View or scroll timeline
|
||||
view_or_scroll_timeline: typing.Optional[ViewOrScrollTimeline] = None
|
||||
|
||||
def to_json(self):
|
||||
json = dict()
|
||||
json['id'] = self.id_
|
||||
json['name'] = self.name
|
||||
json['pausedState'] = self.paused_state
|
||||
json['playState'] = self.play_state
|
||||
json['playbackRate'] = self.playback_rate
|
||||
json['startTime'] = self.start_time
|
||||
json['currentTime'] = self.current_time
|
||||
json['type'] = self.type_
|
||||
if self.source is not None:
|
||||
json['source'] = self.source.to_json()
|
||||
if self.css_id is not None:
|
||||
json['cssId'] = self.css_id
|
||||
if self.view_or_scroll_timeline is not None:
|
||||
json['viewOrScrollTimeline'] = self.view_or_scroll_timeline.to_json()
|
||||
return json
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, json):
|
||||
return cls(
|
||||
id_=str(json['id']),
|
||||
name=str(json['name']),
|
||||
paused_state=bool(json['pausedState']),
|
||||
play_state=str(json['playState']),
|
||||
playback_rate=float(json['playbackRate']),
|
||||
start_time=float(json['startTime']),
|
||||
current_time=float(json['currentTime']),
|
||||
type_=str(json['type']),
|
||||
source=AnimationEffect.from_json(json['source']) if 'source' in json else None,
|
||||
css_id=str(json['cssId']) if 'cssId' in json else None,
|
||||
view_or_scroll_timeline=ViewOrScrollTimeline.from_json(json['viewOrScrollTimeline']) if 'viewOrScrollTimeline' in json else None,
|
||||
)
|
||||
|
||||
|
||||
@dataclass
|
||||
class ViewOrScrollTimeline:
|
||||
'''
|
||||
Timeline instance
|
||||
'''
|
||||
#: Orientation of the scroll
|
||||
axis: dom.ScrollOrientation
|
||||
|
||||
#: Scroll container node
|
||||
source_node_id: typing.Optional[dom.BackendNodeId] = None
|
||||
|
||||
#: Represents the starting scroll position of the timeline
|
||||
#: as a length offset in pixels from scroll origin.
|
||||
start_offset: typing.Optional[float] = None
|
||||
|
||||
#: Represents the ending scroll position of the timeline
|
||||
#: as a length offset in pixels from scroll origin.
|
||||
end_offset: typing.Optional[float] = None
|
||||
|
||||
#: The element whose principal box's visibility in the
|
||||
#: scrollport defined the progress of the timeline.
|
||||
#: Does not exist for animations with ScrollTimeline
|
||||
subject_node_id: typing.Optional[dom.BackendNodeId] = None
|
||||
|
||||
def to_json(self):
|
||||
json = dict()
|
||||
json['axis'] = self.axis.to_json()
|
||||
if self.source_node_id is not None:
|
||||
json['sourceNodeId'] = self.source_node_id.to_json()
|
||||
if self.start_offset is not None:
|
||||
json['startOffset'] = self.start_offset
|
||||
if self.end_offset is not None:
|
||||
json['endOffset'] = self.end_offset
|
||||
if self.subject_node_id is not None:
|
||||
json['subjectNodeId'] = self.subject_node_id.to_json()
|
||||
return json
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, json):
|
||||
return cls(
|
||||
axis=dom.ScrollOrientation.from_json(json['axis']),
|
||||
source_node_id=dom.BackendNodeId.from_json(json['sourceNodeId']) if 'sourceNodeId' in json else None,
|
||||
start_offset=float(json['startOffset']) if 'startOffset' in json else None,
|
||||
end_offset=float(json['endOffset']) if 'endOffset' in json else None,
|
||||
subject_node_id=dom.BackendNodeId.from_json(json['subjectNodeId']) if 'subjectNodeId' in json else None,
|
||||
)
|
||||
|
||||
|
||||
@dataclass
|
||||
class AnimationEffect:
|
||||
'''
|
||||
AnimationEffect instance
|
||||
'''
|
||||
#: ``AnimationEffect``'s delay.
|
||||
delay: float
|
||||
|
||||
#: ``AnimationEffect``'s end delay.
|
||||
end_delay: float
|
||||
|
||||
#: ``AnimationEffect``'s iteration start.
|
||||
iteration_start: float
|
||||
|
||||
#: ``AnimationEffect``'s iterations.
|
||||
iterations: float
|
||||
|
||||
#: ``AnimationEffect``'s iteration duration.
|
||||
#: Milliseconds for time based animations and
|
||||
#: percentage [0 - 100] for scroll driven animations
|
||||
#: (i.e. when viewOrScrollTimeline exists).
|
||||
duration: float
|
||||
|
||||
#: ``AnimationEffect``'s playback direction.
|
||||
direction: str
|
||||
|
||||
#: ``AnimationEffect``'s fill mode.
|
||||
fill: str
|
||||
|
||||
#: ``AnimationEffect``'s timing function.
|
||||
easing: str
|
||||
|
||||
#: ``AnimationEffect``'s target node.
|
||||
backend_node_id: typing.Optional[dom.BackendNodeId] = None
|
||||
|
||||
#: ``AnimationEffect``'s keyframes.
|
||||
keyframes_rule: typing.Optional[KeyframesRule] = None
|
||||
|
||||
def to_json(self):
|
||||
json = dict()
|
||||
json['delay'] = self.delay
|
||||
json['endDelay'] = self.end_delay
|
||||
json['iterationStart'] = self.iteration_start
|
||||
json['iterations'] = self.iterations
|
||||
json['duration'] = self.duration
|
||||
json['direction'] = self.direction
|
||||
json['fill'] = self.fill
|
||||
json['easing'] = self.easing
|
||||
if self.backend_node_id is not None:
|
||||
json['backendNodeId'] = self.backend_node_id.to_json()
|
||||
if self.keyframes_rule is not None:
|
||||
json['keyframesRule'] = self.keyframes_rule.to_json()
|
||||
return json
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, json):
|
||||
return cls(
|
||||
delay=float(json['delay']),
|
||||
end_delay=float(json['endDelay']),
|
||||
iteration_start=float(json['iterationStart']),
|
||||
iterations=float(json['iterations']),
|
||||
duration=float(json['duration']),
|
||||
direction=str(json['direction']),
|
||||
fill=str(json['fill']),
|
||||
easing=str(json['easing']),
|
||||
backend_node_id=dom.BackendNodeId.from_json(json['backendNodeId']) if 'backendNodeId' in json else None,
|
||||
keyframes_rule=KeyframesRule.from_json(json['keyframesRule']) if 'keyframesRule' in json else None,
|
||||
)
|
||||
|
||||
|
||||
@dataclass
|
||||
class KeyframesRule:
|
||||
'''
|
||||
Keyframes Rule
|
||||
'''
|
||||
#: List of animation keyframes.
|
||||
keyframes: typing.List[KeyframeStyle]
|
||||
|
||||
#: CSS keyframed animation's name.
|
||||
name: typing.Optional[str] = None
|
||||
|
||||
def to_json(self):
|
||||
json = dict()
|
||||
json['keyframes'] = [i.to_json() for i in self.keyframes]
|
||||
if self.name is not None:
|
||||
json['name'] = self.name
|
||||
return json
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, json):
|
||||
return cls(
|
||||
keyframes=[KeyframeStyle.from_json(i) for i in json['keyframes']],
|
||||
name=str(json['name']) if 'name' in json else None,
|
||||
)
|
||||
|
||||
|
||||
@dataclass
|
||||
class KeyframeStyle:
|
||||
'''
|
||||
Keyframe Style
|
||||
'''
|
||||
#: Keyframe's time offset.
|
||||
offset: str
|
||||
|
||||
#: ``AnimationEffect``'s timing function.
|
||||
easing: str
|
||||
|
||||
def to_json(self):
|
||||
json = dict()
|
||||
json['offset'] = self.offset
|
||||
json['easing'] = self.easing
|
||||
return json
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, json):
|
||||
return cls(
|
||||
offset=str(json['offset']),
|
||||
easing=str(json['easing']),
|
||||
)
|
||||
|
||||
|
||||
def disable() -> typing.Generator[T_JSON_DICT,T_JSON_DICT,None]:
|
||||
'''
|
||||
Disables animation domain notifications.
|
||||
'''
|
||||
cmd_dict: T_JSON_DICT = {
|
||||
'method': 'Animation.disable',
|
||||
}
|
||||
json = yield cmd_dict
|
||||
|
||||
|
||||
def enable() -> typing.Generator[T_JSON_DICT,T_JSON_DICT,None]:
|
||||
'''
|
||||
Enables animation domain notifications.
|
||||
'''
|
||||
cmd_dict: T_JSON_DICT = {
|
||||
'method': 'Animation.enable',
|
||||
}
|
||||
json = yield cmd_dict
|
||||
|
||||
|
||||
def get_current_time(
|
||||
id_: str
|
||||
) -> typing.Generator[T_JSON_DICT,T_JSON_DICT,float]:
|
||||
'''
|
||||
Returns the current time of the an animation.
|
||||
|
||||
:param id_: Id of animation.
|
||||
:returns: Current time of the page.
|
||||
'''
|
||||
params: T_JSON_DICT = dict()
|
||||
params['id'] = id_
|
||||
cmd_dict: T_JSON_DICT = {
|
||||
'method': 'Animation.getCurrentTime',
|
||||
'params': params,
|
||||
}
|
||||
json = yield cmd_dict
|
||||
return float(json['currentTime'])
|
||||
|
||||
|
||||
def get_playback_rate() -> typing.Generator[T_JSON_DICT,T_JSON_DICT,float]:
|
||||
'''
|
||||
Gets the playback rate of the document timeline.
|
||||
|
||||
:returns: Playback rate for animations on page.
|
||||
'''
|
||||
cmd_dict: T_JSON_DICT = {
|
||||
'method': 'Animation.getPlaybackRate',
|
||||
}
|
||||
json = yield cmd_dict
|
||||
return float(json['playbackRate'])
|
||||
|
||||
|
||||
def release_animations(
|
||||
animations: typing.List[str]
|
||||
) -> typing.Generator[T_JSON_DICT,T_JSON_DICT,None]:
|
||||
'''
|
||||
Releases a set of animations to no longer be manipulated.
|
||||
|
||||
:param animations: List of animation ids to seek.
|
||||
'''
|
||||
params: T_JSON_DICT = dict()
|
||||
params['animations'] = [i for i in animations]
|
||||
cmd_dict: T_JSON_DICT = {
|
||||
'method': 'Animation.releaseAnimations',
|
||||
'params': params,
|
||||
}
|
||||
json = yield cmd_dict
|
||||
|
||||
|
||||
def resolve_animation(
|
||||
animation_id: str
|
||||
) -> typing.Generator[T_JSON_DICT,T_JSON_DICT,runtime.RemoteObject]:
|
||||
'''
|
||||
Gets the remote object of the Animation.
|
||||
|
||||
:param animation_id: Animation id.
|
||||
:returns: Corresponding remote object.
|
||||
'''
|
||||
params: T_JSON_DICT = dict()
|
||||
params['animationId'] = animation_id
|
||||
cmd_dict: T_JSON_DICT = {
|
||||
'method': 'Animation.resolveAnimation',
|
||||
'params': params,
|
||||
}
|
||||
json = yield cmd_dict
|
||||
return runtime.RemoteObject.from_json(json['remoteObject'])
|
||||
|
||||
|
||||
def seek_animations(
|
||||
animations: typing.List[str],
|
||||
current_time: float
|
||||
) -> typing.Generator[T_JSON_DICT,T_JSON_DICT,None]:
|
||||
'''
|
||||
Seek a set of animations to a particular time within each animation.
|
||||
|
||||
:param animations: List of animation ids to seek.
|
||||
:param current_time: Set the current time of each animation.
|
||||
'''
|
||||
params: T_JSON_DICT = dict()
|
||||
params['animations'] = [i for i in animations]
|
||||
params['currentTime'] = current_time
|
||||
cmd_dict: T_JSON_DICT = {
|
||||
'method': 'Animation.seekAnimations',
|
||||
'params': params,
|
||||
}
|
||||
json = yield cmd_dict
|
||||
|
||||
|
||||
def set_paused(
|
||||
animations: typing.List[str],
|
||||
paused: bool
|
||||
) -> typing.Generator[T_JSON_DICT,T_JSON_DICT,None]:
|
||||
'''
|
||||
Sets the paused state of a set of animations.
|
||||
|
||||
:param animations: Animations to set the pause state of.
|
||||
:param paused: Paused state to set to.
|
||||
'''
|
||||
params: T_JSON_DICT = dict()
|
||||
params['animations'] = [i for i in animations]
|
||||
params['paused'] = paused
|
||||
cmd_dict: T_JSON_DICT = {
|
||||
'method': 'Animation.setPaused',
|
||||
'params': params,
|
||||
}
|
||||
json = yield cmd_dict
|
||||
|
||||
|
||||
def set_playback_rate(
|
||||
playback_rate: float
|
||||
) -> typing.Generator[T_JSON_DICT,T_JSON_DICT,None]:
|
||||
'''
|
||||
Sets the playback rate of the document timeline.
|
||||
|
||||
:param playback_rate: Playback rate for animations on page
|
||||
'''
|
||||
params: T_JSON_DICT = dict()
|
||||
params['playbackRate'] = playback_rate
|
||||
cmd_dict: T_JSON_DICT = {
|
||||
'method': 'Animation.setPlaybackRate',
|
||||
'params': params,
|
||||
}
|
||||
json = yield cmd_dict
|
||||
|
||||
|
||||
def set_timing(
|
||||
animation_id: str,
|
||||
duration: float,
|
||||
delay: float
|
||||
) -> typing.Generator[T_JSON_DICT,T_JSON_DICT,None]:
|
||||
'''
|
||||
Sets the timing of an animation node.
|
||||
|
||||
:param animation_id: Animation id.
|
||||
:param duration: Duration of the animation.
|
||||
:param delay: Delay of the animation.
|
||||
'''
|
||||
params: T_JSON_DICT = dict()
|
||||
params['animationId'] = animation_id
|
||||
params['duration'] = duration
|
||||
params['delay'] = delay
|
||||
cmd_dict: T_JSON_DICT = {
|
||||
'method': 'Animation.setTiming',
|
||||
'params': params,
|
||||
}
|
||||
json = yield cmd_dict
|
||||
|
||||
|
||||
@event_class('Animation.animationCanceled')
|
||||
@dataclass
|
||||
class AnimationCanceled:
|
||||
'''
|
||||
Event for when an animation has been cancelled.
|
||||
'''
|
||||
#: Id of the animation that was cancelled.
|
||||
id_: str
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, json: T_JSON_DICT) -> AnimationCanceled:
|
||||
return cls(
|
||||
id_=str(json['id'])
|
||||
)
|
||||
|
||||
|
||||
@event_class('Animation.animationCreated')
|
||||
@dataclass
|
||||
class AnimationCreated:
|
||||
'''
|
||||
Event for each animation that has been created.
|
||||
'''
|
||||
#: Id of the animation that was created.
|
||||
id_: str
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, json: T_JSON_DICT) -> AnimationCreated:
|
||||
return cls(
|
||||
id_=str(json['id'])
|
||||
)
|
||||
|
||||
|
||||
@event_class('Animation.animationStarted')
|
||||
@dataclass
|
||||
class AnimationStarted:
|
||||
'''
|
||||
Event for animation that has been started.
|
||||
'''
|
||||
#: Animation that was started.
|
||||
animation: Animation
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, json: T_JSON_DICT) -> AnimationStarted:
|
||||
return cls(
|
||||
animation=Animation.from_json(json['animation'])
|
||||
)
|
||||
|
||||
|
||||
@event_class('Animation.animationUpdated')
|
||||
@dataclass
|
||||
class AnimationUpdated:
|
||||
'''
|
||||
Event for animation that has been updated.
|
||||
'''
|
||||
#: Animation that was updated.
|
||||
animation: Animation
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, json: T_JSON_DICT) -> AnimationUpdated:
|
||||
return cls(
|
||||
animation=Animation.from_json(json['animation'])
|
||||
)
|
1532
lib/python3.13/site-packages/selenium/webdriver/common/devtools/v128/audits.py
Executable file
1532
lib/python3.13/site-packages/selenium/webdriver/common/devtools/v128/audits.py
Executable file
File diff suppressed because it is too large
Load Diff
283
lib/python3.13/site-packages/selenium/webdriver/common/devtools/v128/autofill.py
Executable file
283
lib/python3.13/site-packages/selenium/webdriver/common/devtools/v128/autofill.py
Executable file
@ -0,0 +1,283 @@
|
||||
# DO NOT EDIT THIS FILE!
|
||||
#
|
||||
# This file is generated from the CDP specification. If you need to make
|
||||
# changes, edit the generator and regenerate all of the modules.
|
||||
#
|
||||
# CDP domain: Autofill (experimental)
|
||||
from __future__ import annotations
|
||||
from .util import event_class, T_JSON_DICT
|
||||
from dataclasses import dataclass
|
||||
import enum
|
||||
import typing
|
||||
from . import dom
|
||||
from . import page
|
||||
|
||||
|
||||
@dataclass
|
||||
class CreditCard:
|
||||
#: 16-digit credit card number.
|
||||
number: str
|
||||
|
||||
#: Name of the credit card owner.
|
||||
name: str
|
||||
|
||||
#: 2-digit expiry month.
|
||||
expiry_month: str
|
||||
|
||||
#: 4-digit expiry year.
|
||||
expiry_year: str
|
||||
|
||||
#: 3-digit card verification code.
|
||||
cvc: str
|
||||
|
||||
def to_json(self):
|
||||
json = dict()
|
||||
json['number'] = self.number
|
||||
json['name'] = self.name
|
||||
json['expiryMonth'] = self.expiry_month
|
||||
json['expiryYear'] = self.expiry_year
|
||||
json['cvc'] = self.cvc
|
||||
return json
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, json):
|
||||
return cls(
|
||||
number=str(json['number']),
|
||||
name=str(json['name']),
|
||||
expiry_month=str(json['expiryMonth']),
|
||||
expiry_year=str(json['expiryYear']),
|
||||
cvc=str(json['cvc']),
|
||||
)
|
||||
|
||||
|
||||
@dataclass
|
||||
class AddressField:
|
||||
#: address field name, for example GIVEN_NAME.
|
||||
name: str
|
||||
|
||||
#: address field value, for example Jon Doe.
|
||||
value: str
|
||||
|
||||
def to_json(self):
|
||||
json = dict()
|
||||
json['name'] = self.name
|
||||
json['value'] = self.value
|
||||
return json
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, json):
|
||||
return cls(
|
||||
name=str(json['name']),
|
||||
value=str(json['value']),
|
||||
)
|
||||
|
||||
|
||||
@dataclass
|
||||
class AddressFields:
|
||||
'''
|
||||
A list of address fields.
|
||||
'''
|
||||
fields: typing.List[AddressField]
|
||||
|
||||
def to_json(self):
|
||||
json = dict()
|
||||
json['fields'] = [i.to_json() for i in self.fields]
|
||||
return json
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, json):
|
||||
return cls(
|
||||
fields=[AddressField.from_json(i) for i in json['fields']],
|
||||
)
|
||||
|
||||
|
||||
@dataclass
|
||||
class Address:
|
||||
#: fields and values defining an address.
|
||||
fields: typing.List[AddressField]
|
||||
|
||||
def to_json(self):
|
||||
json = dict()
|
||||
json['fields'] = [i.to_json() for i in self.fields]
|
||||
return json
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, json):
|
||||
return cls(
|
||||
fields=[AddressField.from_json(i) for i in json['fields']],
|
||||
)
|
||||
|
||||
|
||||
@dataclass
|
||||
class AddressUI:
|
||||
'''
|
||||
Defines how an address can be displayed like in chrome://settings/addresses.
|
||||
Address UI is a two dimensional array, each inner array is an "address information line", and when rendered in a UI surface should be displayed as such.
|
||||
The following address UI for instance:
|
||||
[[{name: "GIVE_NAME", value: "Jon"}, {name: "FAMILY_NAME", value: "Doe"}], [{name: "CITY", value: "Munich"}, {name: "ZIP", value: "81456"}]]
|
||||
should allow the receiver to render:
|
||||
Jon Doe
|
||||
Munich 81456
|
||||
'''
|
||||
#: A two dimension array containing the representation of values from an address profile.
|
||||
address_fields: typing.List[AddressFields]
|
||||
|
||||
def to_json(self):
|
||||
json = dict()
|
||||
json['addressFields'] = [i.to_json() for i in self.address_fields]
|
||||
return json
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, json):
|
||||
return cls(
|
||||
address_fields=[AddressFields.from_json(i) for i in json['addressFields']],
|
||||
)
|
||||
|
||||
|
||||
class FillingStrategy(enum.Enum):
|
||||
'''
|
||||
Specified whether a filled field was done so by using the html autocomplete attribute or autofill heuristics.
|
||||
'''
|
||||
AUTOCOMPLETE_ATTRIBUTE = "autocompleteAttribute"
|
||||
AUTOFILL_INFERRED = "autofillInferred"
|
||||
|
||||
def to_json(self):
|
||||
return self.value
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, json):
|
||||
return cls(json)
|
||||
|
||||
|
||||
@dataclass
|
||||
class FilledField:
|
||||
#: The type of the field, e.g text, password etc.
|
||||
html_type: str
|
||||
|
||||
#: the html id
|
||||
id_: str
|
||||
|
||||
#: the html name
|
||||
name: str
|
||||
|
||||
#: the field value
|
||||
value: str
|
||||
|
||||
#: The actual field type, e.g FAMILY_NAME
|
||||
autofill_type: str
|
||||
|
||||
#: The filling strategy
|
||||
filling_strategy: FillingStrategy
|
||||
|
||||
#: The frame the field belongs to
|
||||
frame_id: page.FrameId
|
||||
|
||||
#: The form field's DOM node
|
||||
field_id: dom.BackendNodeId
|
||||
|
||||
def to_json(self):
|
||||
json = dict()
|
||||
json['htmlType'] = self.html_type
|
||||
json['id'] = self.id_
|
||||
json['name'] = self.name
|
||||
json['value'] = self.value
|
||||
json['autofillType'] = self.autofill_type
|
||||
json['fillingStrategy'] = self.filling_strategy.to_json()
|
||||
json['frameId'] = self.frame_id.to_json()
|
||||
json['fieldId'] = self.field_id.to_json()
|
||||
return json
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, json):
|
||||
return cls(
|
||||
html_type=str(json['htmlType']),
|
||||
id_=str(json['id']),
|
||||
name=str(json['name']),
|
||||
value=str(json['value']),
|
||||
autofill_type=str(json['autofillType']),
|
||||
filling_strategy=FillingStrategy.from_json(json['fillingStrategy']),
|
||||
frame_id=page.FrameId.from_json(json['frameId']),
|
||||
field_id=dom.BackendNodeId.from_json(json['fieldId']),
|
||||
)
|
||||
|
||||
|
||||
def trigger(
|
||||
field_id: dom.BackendNodeId,
|
||||
frame_id: typing.Optional[page.FrameId] = None,
|
||||
card: CreditCard = None
|
||||
) -> typing.Generator[T_JSON_DICT,T_JSON_DICT,None]:
|
||||
'''
|
||||
Trigger autofill on a form identified by the fieldId.
|
||||
If the field and related form cannot be autofilled, returns an error.
|
||||
|
||||
:param field_id: Identifies a field that serves as an anchor for autofill.
|
||||
:param frame_id: *(Optional)* Identifies the frame that field belongs to.
|
||||
:param card: Credit card information to fill out the form. Credit card data is not saved.
|
||||
'''
|
||||
params: T_JSON_DICT = dict()
|
||||
params['fieldId'] = field_id.to_json()
|
||||
if frame_id is not None:
|
||||
params['frameId'] = frame_id.to_json()
|
||||
params['card'] = card.to_json()
|
||||
cmd_dict: T_JSON_DICT = {
|
||||
'method': 'Autofill.trigger',
|
||||
'params': params,
|
||||
}
|
||||
json = yield cmd_dict
|
||||
|
||||
|
||||
def set_addresses(
|
||||
addresses: typing.List[Address]
|
||||
) -> typing.Generator[T_JSON_DICT,T_JSON_DICT,None]:
|
||||
'''
|
||||
Set addresses so that developers can verify their forms implementation.
|
||||
|
||||
:param addresses:
|
||||
'''
|
||||
params: T_JSON_DICT = dict()
|
||||
params['addresses'] = [i.to_json() for i in addresses]
|
||||
cmd_dict: T_JSON_DICT = {
|
||||
'method': 'Autofill.setAddresses',
|
||||
'params': params,
|
||||
}
|
||||
json = yield cmd_dict
|
||||
|
||||
|
||||
def disable() -> typing.Generator[T_JSON_DICT,T_JSON_DICT,None]:
|
||||
'''
|
||||
Disables autofill domain notifications.
|
||||
'''
|
||||
cmd_dict: T_JSON_DICT = {
|
||||
'method': 'Autofill.disable',
|
||||
}
|
||||
json = yield cmd_dict
|
||||
|
||||
|
||||
def enable() -> typing.Generator[T_JSON_DICT,T_JSON_DICT,None]:
|
||||
'''
|
||||
Enables autofill domain notifications.
|
||||
'''
|
||||
cmd_dict: T_JSON_DICT = {
|
||||
'method': 'Autofill.enable',
|
||||
}
|
||||
json = yield cmd_dict
|
||||
|
||||
|
||||
@event_class('Autofill.addressFormFilled')
|
||||
@dataclass
|
||||
class AddressFormFilled:
|
||||
'''
|
||||
Emitted when an address form is filled.
|
||||
'''
|
||||
#: Information about the fields that were filled
|
||||
filled_fields: typing.List[FilledField]
|
||||
#: An UI representation of the address used to fill the form.
|
||||
#: Consists of a 2D array where each child represents an address/profile line.
|
||||
address_ui: AddressUI
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, json: T_JSON_DICT) -> AddressFormFilled:
|
||||
return cls(
|
||||
filled_fields=[FilledField.from_json(i) for i in json['filledFields']],
|
||||
address_ui=AddressUI.from_json(json['addressUi'])
|
||||
)
|
@ -0,0 +1,213 @@
|
||||
# DO NOT EDIT THIS FILE!
|
||||
#
|
||||
# This file is generated from the CDP specification. If you need to make
|
||||
# changes, edit the generator and regenerate all of the modules.
|
||||
#
|
||||
# CDP domain: BackgroundService (experimental)
|
||||
from __future__ import annotations
|
||||
from .util import event_class, T_JSON_DICT
|
||||
from dataclasses import dataclass
|
||||
import enum
|
||||
import typing
|
||||
from . import network
|
||||
from . import service_worker
|
||||
|
||||
|
||||
class ServiceName(enum.Enum):
|
||||
'''
|
||||
The Background Service that will be associated with the commands/events.
|
||||
Every Background Service operates independently, but they share the same
|
||||
API.
|
||||
'''
|
||||
BACKGROUND_FETCH = "backgroundFetch"
|
||||
BACKGROUND_SYNC = "backgroundSync"
|
||||
PUSH_MESSAGING = "pushMessaging"
|
||||
NOTIFICATIONS = "notifications"
|
||||
PAYMENT_HANDLER = "paymentHandler"
|
||||
PERIODIC_BACKGROUND_SYNC = "periodicBackgroundSync"
|
||||
|
||||
def to_json(self):
|
||||
return self.value
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, json):
|
||||
return cls(json)
|
||||
|
||||
|
||||
@dataclass
|
||||
class EventMetadata:
|
||||
'''
|
||||
A key-value pair for additional event information to pass along.
|
||||
'''
|
||||
key: str
|
||||
|
||||
value: str
|
||||
|
||||
def to_json(self):
|
||||
json = dict()
|
||||
json['key'] = self.key
|
||||
json['value'] = self.value
|
||||
return json
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, json):
|
||||
return cls(
|
||||
key=str(json['key']),
|
||||
value=str(json['value']),
|
||||
)
|
||||
|
||||
|
||||
@dataclass
|
||||
class BackgroundServiceEvent:
|
||||
#: Timestamp of the event (in seconds).
|
||||
timestamp: network.TimeSinceEpoch
|
||||
|
||||
#: The origin this event belongs to.
|
||||
origin: str
|
||||
|
||||
#: The Service Worker ID that initiated the event.
|
||||
service_worker_registration_id: service_worker.RegistrationID
|
||||
|
||||
#: The Background Service this event belongs to.
|
||||
service: ServiceName
|
||||
|
||||
#: A description of the event.
|
||||
event_name: str
|
||||
|
||||
#: An identifier that groups related events together.
|
||||
instance_id: str
|
||||
|
||||
#: A list of event-specific information.
|
||||
event_metadata: typing.List[EventMetadata]
|
||||
|
||||
#: Storage key this event belongs to.
|
||||
storage_key: str
|
||||
|
||||
def to_json(self):
|
||||
json = dict()
|
||||
json['timestamp'] = self.timestamp.to_json()
|
||||
json['origin'] = self.origin
|
||||
json['serviceWorkerRegistrationId'] = self.service_worker_registration_id.to_json()
|
||||
json['service'] = self.service.to_json()
|
||||
json['eventName'] = self.event_name
|
||||
json['instanceId'] = self.instance_id
|
||||
json['eventMetadata'] = [i.to_json() for i in self.event_metadata]
|
||||
json['storageKey'] = self.storage_key
|
||||
return json
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, json):
|
||||
return cls(
|
||||
timestamp=network.TimeSinceEpoch.from_json(json['timestamp']),
|
||||
origin=str(json['origin']),
|
||||
service_worker_registration_id=service_worker.RegistrationID.from_json(json['serviceWorkerRegistrationId']),
|
||||
service=ServiceName.from_json(json['service']),
|
||||
event_name=str(json['eventName']),
|
||||
instance_id=str(json['instanceId']),
|
||||
event_metadata=[EventMetadata.from_json(i) for i in json['eventMetadata']],
|
||||
storage_key=str(json['storageKey']),
|
||||
)
|
||||
|
||||
|
||||
def start_observing(
|
||||
service: ServiceName
|
||||
) -> typing.Generator[T_JSON_DICT,T_JSON_DICT,None]:
|
||||
'''
|
||||
Enables event updates for the service.
|
||||
|
||||
:param service:
|
||||
'''
|
||||
params: T_JSON_DICT = dict()
|
||||
params['service'] = service.to_json()
|
||||
cmd_dict: T_JSON_DICT = {
|
||||
'method': 'BackgroundService.startObserving',
|
||||
'params': params,
|
||||
}
|
||||
json = yield cmd_dict
|
||||
|
||||
|
||||
def stop_observing(
|
||||
service: ServiceName
|
||||
) -> typing.Generator[T_JSON_DICT,T_JSON_DICT,None]:
|
||||
'''
|
||||
Disables event updates for the service.
|
||||
|
||||
:param service:
|
||||
'''
|
||||
params: T_JSON_DICT = dict()
|
||||
params['service'] = service.to_json()
|
||||
cmd_dict: T_JSON_DICT = {
|
||||
'method': 'BackgroundService.stopObserving',
|
||||
'params': params,
|
||||
}
|
||||
json = yield cmd_dict
|
||||
|
||||
|
||||
def set_recording(
|
||||
should_record: bool,
|
||||
service: ServiceName
|
||||
) -> typing.Generator[T_JSON_DICT,T_JSON_DICT,None]:
|
||||
'''
|
||||
Set the recording state for the service.
|
||||
|
||||
:param should_record:
|
||||
:param service:
|
||||
'''
|
||||
params: T_JSON_DICT = dict()
|
||||
params['shouldRecord'] = should_record
|
||||
params['service'] = service.to_json()
|
||||
cmd_dict: T_JSON_DICT = {
|
||||
'method': 'BackgroundService.setRecording',
|
||||
'params': params,
|
||||
}
|
||||
json = yield cmd_dict
|
||||
|
||||
|
||||
def clear_events(
|
||||
service: ServiceName
|
||||
) -> typing.Generator[T_JSON_DICT,T_JSON_DICT,None]:
|
||||
'''
|
||||
Clears all stored data for the service.
|
||||
|
||||
:param service:
|
||||
'''
|
||||
params: T_JSON_DICT = dict()
|
||||
params['service'] = service.to_json()
|
||||
cmd_dict: T_JSON_DICT = {
|
||||
'method': 'BackgroundService.clearEvents',
|
||||
'params': params,
|
||||
}
|
||||
json = yield cmd_dict
|
||||
|
||||
|
||||
@event_class('BackgroundService.recordingStateChanged')
|
||||
@dataclass
|
||||
class RecordingStateChanged:
|
||||
'''
|
||||
Called when the recording state for the service has been updated.
|
||||
'''
|
||||
is_recording: bool
|
||||
service: ServiceName
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, json: T_JSON_DICT) -> RecordingStateChanged:
|
||||
return cls(
|
||||
is_recording=bool(json['isRecording']),
|
||||
service=ServiceName.from_json(json['service'])
|
||||
)
|
||||
|
||||
|
||||
@event_class('BackgroundService.backgroundServiceEventReceived')
|
||||
@dataclass
|
||||
class BackgroundServiceEventReceived:
|
||||
'''
|
||||
Called with all existing backgroundServiceEvents when enabled, and all new
|
||||
events afterwards if enabled and recording.
|
||||
'''
|
||||
background_service_event: BackgroundServiceEvent
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, json: T_JSON_DICT) -> BackgroundServiceEventReceived:
|
||||
return cls(
|
||||
background_service_event=BackgroundServiceEvent.from_json(json['backgroundServiceEvent'])
|
||||
)
|
725
lib/python3.13/site-packages/selenium/webdriver/common/devtools/v128/browser.py
Executable file
725
lib/python3.13/site-packages/selenium/webdriver/common/devtools/v128/browser.py
Executable file
@ -0,0 +1,725 @@
|
||||
# DO NOT EDIT THIS FILE!
|
||||
#
|
||||
# This file is generated from the CDP specification. If you need to make
|
||||
# changes, edit the generator and regenerate all of the modules.
|
||||
#
|
||||
# CDP domain: Browser
|
||||
from __future__ import annotations
|
||||
from .util import event_class, T_JSON_DICT
|
||||
from dataclasses import dataclass
|
||||
import enum
|
||||
import typing
|
||||
from . import page
|
||||
from . import target
|
||||
|
||||
|
||||
class BrowserContextID(str):
|
||||
def to_json(self) -> str:
|
||||
return self
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, json: str) -> BrowserContextID:
|
||||
return cls(json)
|
||||
|
||||
def __repr__(self):
|
||||
return 'BrowserContextID({})'.format(super().__repr__())
|
||||
|
||||
|
||||
class WindowID(int):
|
||||
def to_json(self) -> int:
|
||||
return self
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, json: int) -> WindowID:
|
||||
return cls(json)
|
||||
|
||||
def __repr__(self):
|
||||
return 'WindowID({})'.format(super().__repr__())
|
||||
|
||||
|
||||
class WindowState(enum.Enum):
|
||||
'''
|
||||
The state of the browser window.
|
||||
'''
|
||||
NORMAL = "normal"
|
||||
MINIMIZED = "minimized"
|
||||
MAXIMIZED = "maximized"
|
||||
FULLSCREEN = "fullscreen"
|
||||
|
||||
def to_json(self):
|
||||
return self.value
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, json):
|
||||
return cls(json)
|
||||
|
||||
|
||||
@dataclass
|
||||
class Bounds:
|
||||
'''
|
||||
Browser window bounds information
|
||||
'''
|
||||
#: The offset from the left edge of the screen to the window in pixels.
|
||||
left: typing.Optional[int] = None
|
||||
|
||||
#: The offset from the top edge of the screen to the window in pixels.
|
||||
top: typing.Optional[int] = None
|
||||
|
||||
#: The window width in pixels.
|
||||
width: typing.Optional[int] = None
|
||||
|
||||
#: The window height in pixels.
|
||||
height: typing.Optional[int] = None
|
||||
|
||||
#: The window state. Default to normal.
|
||||
window_state: typing.Optional[WindowState] = None
|
||||
|
||||
def to_json(self):
|
||||
json = dict()
|
||||
if self.left is not None:
|
||||
json['left'] = self.left
|
||||
if self.top is not None:
|
||||
json['top'] = self.top
|
||||
if self.width is not None:
|
||||
json['width'] = self.width
|
||||
if self.height is not None:
|
||||
json['height'] = self.height
|
||||
if self.window_state is not None:
|
||||
json['windowState'] = self.window_state.to_json()
|
||||
return json
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, json):
|
||||
return cls(
|
||||
left=int(json['left']) if 'left' in json else None,
|
||||
top=int(json['top']) if 'top' in json else None,
|
||||
width=int(json['width']) if 'width' in json else None,
|
||||
height=int(json['height']) if 'height' in json else None,
|
||||
window_state=WindowState.from_json(json['windowState']) if 'windowState' in json else None,
|
||||
)
|
||||
|
||||
|
||||
class PermissionType(enum.Enum):
|
||||
ACCESSIBILITY_EVENTS = "accessibilityEvents"
|
||||
AUDIO_CAPTURE = "audioCapture"
|
||||
BACKGROUND_SYNC = "backgroundSync"
|
||||
BACKGROUND_FETCH = "backgroundFetch"
|
||||
CAPTURED_SURFACE_CONTROL = "capturedSurfaceControl"
|
||||
CLIPBOARD_READ_WRITE = "clipboardReadWrite"
|
||||
CLIPBOARD_SANITIZED_WRITE = "clipboardSanitizedWrite"
|
||||
DISPLAY_CAPTURE = "displayCapture"
|
||||
DURABLE_STORAGE = "durableStorage"
|
||||
FLASH = "flash"
|
||||
GEOLOCATION = "geolocation"
|
||||
IDLE_DETECTION = "idleDetection"
|
||||
LOCAL_FONTS = "localFonts"
|
||||
MIDI = "midi"
|
||||
MIDI_SYSEX = "midiSysex"
|
||||
NFC = "nfc"
|
||||
NOTIFICATIONS = "notifications"
|
||||
PAYMENT_HANDLER = "paymentHandler"
|
||||
PERIODIC_BACKGROUND_SYNC = "periodicBackgroundSync"
|
||||
PROTECTED_MEDIA_IDENTIFIER = "protectedMediaIdentifier"
|
||||
SENSORS = "sensors"
|
||||
STORAGE_ACCESS = "storageAccess"
|
||||
SPEAKER_SELECTION = "speakerSelection"
|
||||
TOP_LEVEL_STORAGE_ACCESS = "topLevelStorageAccess"
|
||||
VIDEO_CAPTURE = "videoCapture"
|
||||
VIDEO_CAPTURE_PAN_TILT_ZOOM = "videoCapturePanTiltZoom"
|
||||
WAKE_LOCK_SCREEN = "wakeLockScreen"
|
||||
WAKE_LOCK_SYSTEM = "wakeLockSystem"
|
||||
WINDOW_MANAGEMENT = "windowManagement"
|
||||
|
||||
def to_json(self):
|
||||
return self.value
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, json):
|
||||
return cls(json)
|
||||
|
||||
|
||||
class PermissionSetting(enum.Enum):
|
||||
GRANTED = "granted"
|
||||
DENIED = "denied"
|
||||
PROMPT = "prompt"
|
||||
|
||||
def to_json(self):
|
||||
return self.value
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, json):
|
||||
return cls(json)
|
||||
|
||||
|
||||
@dataclass
|
||||
class PermissionDescriptor:
|
||||
'''
|
||||
Definition of PermissionDescriptor defined in the Permissions API:
|
||||
https://w3c.github.io/permissions/#dom-permissiondescriptor.
|
||||
'''
|
||||
#: Name of permission.
|
||||
#: See https://cs.chromium.org/chromium/src/third_party/blink/renderer/modules/permissions/permission_descriptor.idl for valid permission names.
|
||||
name: str
|
||||
|
||||
#: For "midi" permission, may also specify sysex control.
|
||||
sysex: typing.Optional[bool] = None
|
||||
|
||||
#: For "push" permission, may specify userVisibleOnly.
|
||||
#: Note that userVisibleOnly = true is the only currently supported type.
|
||||
user_visible_only: typing.Optional[bool] = None
|
||||
|
||||
#: For "clipboard" permission, may specify allowWithoutSanitization.
|
||||
allow_without_sanitization: typing.Optional[bool] = None
|
||||
|
||||
#: For "fullscreen" permission, must specify allowWithoutGesture:true.
|
||||
allow_without_gesture: typing.Optional[bool] = None
|
||||
|
||||
#: For "camera" permission, may specify panTiltZoom.
|
||||
pan_tilt_zoom: typing.Optional[bool] = None
|
||||
|
||||
def to_json(self):
|
||||
json = dict()
|
||||
json['name'] = self.name
|
||||
if self.sysex is not None:
|
||||
json['sysex'] = self.sysex
|
||||
if self.user_visible_only is not None:
|
||||
json['userVisibleOnly'] = self.user_visible_only
|
||||
if self.allow_without_sanitization is not None:
|
||||
json['allowWithoutSanitization'] = self.allow_without_sanitization
|
||||
if self.allow_without_gesture is not None:
|
||||
json['allowWithoutGesture'] = self.allow_without_gesture
|
||||
if self.pan_tilt_zoom is not None:
|
||||
json['panTiltZoom'] = self.pan_tilt_zoom
|
||||
return json
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, json):
|
||||
return cls(
|
||||
name=str(json['name']),
|
||||
sysex=bool(json['sysex']) if 'sysex' in json else None,
|
||||
user_visible_only=bool(json['userVisibleOnly']) if 'userVisibleOnly' in json else None,
|
||||
allow_without_sanitization=bool(json['allowWithoutSanitization']) if 'allowWithoutSanitization' in json else None,
|
||||
allow_without_gesture=bool(json['allowWithoutGesture']) if 'allowWithoutGesture' in json else None,
|
||||
pan_tilt_zoom=bool(json['panTiltZoom']) if 'panTiltZoom' in json else None,
|
||||
)
|
||||
|
||||
|
||||
class BrowserCommandId(enum.Enum):
|
||||
'''
|
||||
Browser command ids used by executeBrowserCommand.
|
||||
'''
|
||||
OPEN_TAB_SEARCH = "openTabSearch"
|
||||
CLOSE_TAB_SEARCH = "closeTabSearch"
|
||||
|
||||
def to_json(self):
|
||||
return self.value
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, json):
|
||||
return cls(json)
|
||||
|
||||
|
||||
@dataclass
|
||||
class Bucket:
|
||||
'''
|
||||
Chrome histogram bucket.
|
||||
'''
|
||||
#: Minimum value (inclusive).
|
||||
low: int
|
||||
|
||||
#: Maximum value (exclusive).
|
||||
high: int
|
||||
|
||||
#: Number of samples.
|
||||
count: int
|
||||
|
||||
def to_json(self):
|
||||
json = dict()
|
||||
json['low'] = self.low
|
||||
json['high'] = self.high
|
||||
json['count'] = self.count
|
||||
return json
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, json):
|
||||
return cls(
|
||||
low=int(json['low']),
|
||||
high=int(json['high']),
|
||||
count=int(json['count']),
|
||||
)
|
||||
|
||||
|
||||
@dataclass
|
||||
class Histogram:
|
||||
'''
|
||||
Chrome histogram.
|
||||
'''
|
||||
#: Name.
|
||||
name: str
|
||||
|
||||
#: Sum of sample values.
|
||||
sum_: int
|
||||
|
||||
#: Total number of samples.
|
||||
count: int
|
||||
|
||||
#: Buckets.
|
||||
buckets: typing.List[Bucket]
|
||||
|
||||
def to_json(self):
|
||||
json = dict()
|
||||
json['name'] = self.name
|
||||
json['sum'] = self.sum_
|
||||
json['count'] = self.count
|
||||
json['buckets'] = [i.to_json() for i in self.buckets]
|
||||
return json
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, json):
|
||||
return cls(
|
||||
name=str(json['name']),
|
||||
sum_=int(json['sum']),
|
||||
count=int(json['count']),
|
||||
buckets=[Bucket.from_json(i) for i in json['buckets']],
|
||||
)
|
||||
|
||||
|
||||
def set_permission(
|
||||
permission: PermissionDescriptor,
|
||||
setting: PermissionSetting,
|
||||
origin: typing.Optional[str] = None,
|
||||
browser_context_id: typing.Optional[BrowserContextID] = None
|
||||
) -> typing.Generator[T_JSON_DICT,T_JSON_DICT,None]:
|
||||
'''
|
||||
Set permission settings for given origin.
|
||||
|
||||
**EXPERIMENTAL**
|
||||
|
||||
:param permission: Descriptor of permission to override.
|
||||
:param setting: Setting of the permission.
|
||||
:param origin: *(Optional)* Origin the permission applies to, all origins if not specified.
|
||||
:param browser_context_id: *(Optional)* Context to override. When omitted, default browser context is used.
|
||||
'''
|
||||
params: T_JSON_DICT = dict()
|
||||
params['permission'] = permission.to_json()
|
||||
params['setting'] = setting.to_json()
|
||||
if origin is not None:
|
||||
params['origin'] = origin
|
||||
if browser_context_id is not None:
|
||||
params['browserContextId'] = browser_context_id.to_json()
|
||||
cmd_dict: T_JSON_DICT = {
|
||||
'method': 'Browser.setPermission',
|
||||
'params': params,
|
||||
}
|
||||
json = yield cmd_dict
|
||||
|
||||
|
||||
def grant_permissions(
|
||||
permissions: typing.List[PermissionType],
|
||||
origin: typing.Optional[str] = None,
|
||||
browser_context_id: typing.Optional[BrowserContextID] = None
|
||||
) -> typing.Generator[T_JSON_DICT,T_JSON_DICT,None]:
|
||||
'''
|
||||
Grant specific permissions to the given origin and reject all others.
|
||||
|
||||
**EXPERIMENTAL**
|
||||
|
||||
:param permissions:
|
||||
:param origin: *(Optional)* Origin the permission applies to, all origins if not specified.
|
||||
:param browser_context_id: *(Optional)* BrowserContext to override permissions. When omitted, default browser context is used.
|
||||
'''
|
||||
params: T_JSON_DICT = dict()
|
||||
params['permissions'] = [i.to_json() for i in permissions]
|
||||
if origin is not None:
|
||||
params['origin'] = origin
|
||||
if browser_context_id is not None:
|
||||
params['browserContextId'] = browser_context_id.to_json()
|
||||
cmd_dict: T_JSON_DICT = {
|
||||
'method': 'Browser.grantPermissions',
|
||||
'params': params,
|
||||
}
|
||||
json = yield cmd_dict
|
||||
|
||||
|
||||
def reset_permissions(
|
||||
browser_context_id: typing.Optional[BrowserContextID] = None
|
||||
) -> typing.Generator[T_JSON_DICT,T_JSON_DICT,None]:
|
||||
'''
|
||||
Reset all permission management for all origins.
|
||||
|
||||
:param browser_context_id: *(Optional)* BrowserContext to reset permissions. When omitted, default browser context is used.
|
||||
'''
|
||||
params: T_JSON_DICT = dict()
|
||||
if browser_context_id is not None:
|
||||
params['browserContextId'] = browser_context_id.to_json()
|
||||
cmd_dict: T_JSON_DICT = {
|
||||
'method': 'Browser.resetPermissions',
|
||||
'params': params,
|
||||
}
|
||||
json = yield cmd_dict
|
||||
|
||||
|
||||
def set_download_behavior(
|
||||
behavior: str,
|
||||
browser_context_id: typing.Optional[BrowserContextID] = None,
|
||||
download_path: typing.Optional[str] = None,
|
||||
events_enabled: typing.Optional[bool] = None
|
||||
) -> typing.Generator[T_JSON_DICT,T_JSON_DICT,None]:
|
||||
'''
|
||||
Set the behavior when downloading a file.
|
||||
|
||||
**EXPERIMENTAL**
|
||||
|
||||
:param behavior: Whether to allow all or deny all download requests, or use default Chrome behavior if available (otherwise deny). ``allowAndName`` allows download and names files according to their download guids.
|
||||
:param browser_context_id: *(Optional)* BrowserContext to set download behavior. When omitted, default browser context is used.
|
||||
:param download_path: *(Optional)* The default path to save downloaded files to. This is required if behavior is set to 'allow' or 'allowAndName'.
|
||||
:param events_enabled: *(Optional)* Whether to emit download events (defaults to false).
|
||||
'''
|
||||
params: T_JSON_DICT = dict()
|
||||
params['behavior'] = behavior
|
||||
if browser_context_id is not None:
|
||||
params['browserContextId'] = browser_context_id.to_json()
|
||||
if download_path is not None:
|
||||
params['downloadPath'] = download_path
|
||||
if events_enabled is not None:
|
||||
params['eventsEnabled'] = events_enabled
|
||||
cmd_dict: T_JSON_DICT = {
|
||||
'method': 'Browser.setDownloadBehavior',
|
||||
'params': params,
|
||||
}
|
||||
json = yield cmd_dict
|
||||
|
||||
|
||||
def cancel_download(
|
||||
guid: str,
|
||||
browser_context_id: typing.Optional[BrowserContextID] = None
|
||||
) -> typing.Generator[T_JSON_DICT,T_JSON_DICT,None]:
|
||||
'''
|
||||
Cancel a download if in progress
|
||||
|
||||
**EXPERIMENTAL**
|
||||
|
||||
:param guid: Global unique identifier of the download.
|
||||
:param browser_context_id: *(Optional)* BrowserContext to perform the action in. When omitted, default browser context is used.
|
||||
'''
|
||||
params: T_JSON_DICT = dict()
|
||||
params['guid'] = guid
|
||||
if browser_context_id is not None:
|
||||
params['browserContextId'] = browser_context_id.to_json()
|
||||
cmd_dict: T_JSON_DICT = {
|
||||
'method': 'Browser.cancelDownload',
|
||||
'params': params,
|
||||
}
|
||||
json = yield cmd_dict
|
||||
|
||||
|
||||
def close() -> typing.Generator[T_JSON_DICT,T_JSON_DICT,None]:
|
||||
'''
|
||||
Close browser gracefully.
|
||||
'''
|
||||
cmd_dict: T_JSON_DICT = {
|
||||
'method': 'Browser.close',
|
||||
}
|
||||
json = yield cmd_dict
|
||||
|
||||
|
||||
def crash() -> typing.Generator[T_JSON_DICT,T_JSON_DICT,None]:
|
||||
'''
|
||||
Crashes browser on the main thread.
|
||||
|
||||
**EXPERIMENTAL**
|
||||
'''
|
||||
cmd_dict: T_JSON_DICT = {
|
||||
'method': 'Browser.crash',
|
||||
}
|
||||
json = yield cmd_dict
|
||||
|
||||
|
||||
def crash_gpu_process() -> typing.Generator[T_JSON_DICT,T_JSON_DICT,None]:
|
||||
'''
|
||||
Crashes GPU process.
|
||||
|
||||
**EXPERIMENTAL**
|
||||
'''
|
||||
cmd_dict: T_JSON_DICT = {
|
||||
'method': 'Browser.crashGpuProcess',
|
||||
}
|
||||
json = yield cmd_dict
|
||||
|
||||
|
||||
def get_version() -> typing.Generator[T_JSON_DICT,T_JSON_DICT,typing.Tuple[str, str, str, str, str]]:
|
||||
'''
|
||||
Returns version information.
|
||||
|
||||
:returns: A tuple with the following items:
|
||||
|
||||
0. **protocolVersion** - Protocol version.
|
||||
1. **product** - Product name.
|
||||
2. **revision** - Product revision.
|
||||
3. **userAgent** - User-Agent.
|
||||
4. **jsVersion** - V8 version.
|
||||
'''
|
||||
cmd_dict: T_JSON_DICT = {
|
||||
'method': 'Browser.getVersion',
|
||||
}
|
||||
json = yield cmd_dict
|
||||
return (
|
||||
str(json['protocolVersion']),
|
||||
str(json['product']),
|
||||
str(json['revision']),
|
||||
str(json['userAgent']),
|
||||
str(json['jsVersion'])
|
||||
)
|
||||
|
||||
|
||||
def get_browser_command_line() -> typing.Generator[T_JSON_DICT,T_JSON_DICT,typing.List[str]]:
|
||||
'''
|
||||
Returns the command line switches for the browser process if, and only if
|
||||
--enable-automation is on the commandline.
|
||||
|
||||
**EXPERIMENTAL**
|
||||
|
||||
:returns: Commandline parameters
|
||||
'''
|
||||
cmd_dict: T_JSON_DICT = {
|
||||
'method': 'Browser.getBrowserCommandLine',
|
||||
}
|
||||
json = yield cmd_dict
|
||||
return [str(i) for i in json['arguments']]
|
||||
|
||||
|
||||
def get_histograms(
|
||||
query: typing.Optional[str] = None,
|
||||
delta: typing.Optional[bool] = None
|
||||
) -> typing.Generator[T_JSON_DICT,T_JSON_DICT,typing.List[Histogram]]:
|
||||
'''
|
||||
Get Chrome histograms.
|
||||
|
||||
**EXPERIMENTAL**
|
||||
|
||||
:param query: *(Optional)* Requested substring in name. Only histograms which have query as a substring in their name are extracted. An empty or absent query returns all histograms.
|
||||
:param delta: *(Optional)* If true, retrieve delta since last delta call.
|
||||
:returns: Histograms.
|
||||
'''
|
||||
params: T_JSON_DICT = dict()
|
||||
if query is not None:
|
||||
params['query'] = query
|
||||
if delta is not None:
|
||||
params['delta'] = delta
|
||||
cmd_dict: T_JSON_DICT = {
|
||||
'method': 'Browser.getHistograms',
|
||||
'params': params,
|
||||
}
|
||||
json = yield cmd_dict
|
||||
return [Histogram.from_json(i) for i in json['histograms']]
|
||||
|
||||
|
||||
def get_histogram(
|
||||
name: str,
|
||||
delta: typing.Optional[bool] = None
|
||||
) -> typing.Generator[T_JSON_DICT,T_JSON_DICT,Histogram]:
|
||||
'''
|
||||
Get a Chrome histogram by name.
|
||||
|
||||
**EXPERIMENTAL**
|
||||
|
||||
:param name: Requested histogram name.
|
||||
:param delta: *(Optional)* If true, retrieve delta since last delta call.
|
||||
:returns: Histogram.
|
||||
'''
|
||||
params: T_JSON_DICT = dict()
|
||||
params['name'] = name
|
||||
if delta is not None:
|
||||
params['delta'] = delta
|
||||
cmd_dict: T_JSON_DICT = {
|
||||
'method': 'Browser.getHistogram',
|
||||
'params': params,
|
||||
}
|
||||
json = yield cmd_dict
|
||||
return Histogram.from_json(json['histogram'])
|
||||
|
||||
|
||||
def get_window_bounds(
|
||||
window_id: WindowID
|
||||
) -> typing.Generator[T_JSON_DICT,T_JSON_DICT,Bounds]:
|
||||
'''
|
||||
Get position and size of the browser window.
|
||||
|
||||
**EXPERIMENTAL**
|
||||
|
||||
:param window_id: Browser window id.
|
||||
:returns: Bounds information of the window. When window state is 'minimized', the restored window position and size are returned.
|
||||
'''
|
||||
params: T_JSON_DICT = dict()
|
||||
params['windowId'] = window_id.to_json()
|
||||
cmd_dict: T_JSON_DICT = {
|
||||
'method': 'Browser.getWindowBounds',
|
||||
'params': params,
|
||||
}
|
||||
json = yield cmd_dict
|
||||
return Bounds.from_json(json['bounds'])
|
||||
|
||||
|
||||
def get_window_for_target(
|
||||
target_id: typing.Optional[target.TargetID] = None
|
||||
) -> typing.Generator[T_JSON_DICT,T_JSON_DICT,typing.Tuple[WindowID, Bounds]]:
|
||||
'''
|
||||
Get the browser window that contains the devtools target.
|
||||
|
||||
**EXPERIMENTAL**
|
||||
|
||||
:param target_id: *(Optional)* Devtools agent host id. If called as a part of the session, associated targetId is used.
|
||||
:returns: A tuple with the following items:
|
||||
|
||||
0. **windowId** - Browser window id.
|
||||
1. **bounds** - Bounds information of the window. When window state is 'minimized', the restored window position and size are returned.
|
||||
'''
|
||||
params: T_JSON_DICT = dict()
|
||||
if target_id is not None:
|
||||
params['targetId'] = target_id.to_json()
|
||||
cmd_dict: T_JSON_DICT = {
|
||||
'method': 'Browser.getWindowForTarget',
|
||||
'params': params,
|
||||
}
|
||||
json = yield cmd_dict
|
||||
return (
|
||||
WindowID.from_json(json['windowId']),
|
||||
Bounds.from_json(json['bounds'])
|
||||
)
|
||||
|
||||
|
||||
def set_window_bounds(
|
||||
window_id: WindowID,
|
||||
bounds: Bounds
|
||||
) -> typing.Generator[T_JSON_DICT,T_JSON_DICT,None]:
|
||||
'''
|
||||
Set position and/or size of the browser window.
|
||||
|
||||
**EXPERIMENTAL**
|
||||
|
||||
:param window_id: Browser window id.
|
||||
:param bounds: New window bounds. The 'minimized', 'maximized' and 'fullscreen' states cannot be combined with 'left', 'top', 'width' or 'height'. Leaves unspecified fields unchanged.
|
||||
'''
|
||||
params: T_JSON_DICT = dict()
|
||||
params['windowId'] = window_id.to_json()
|
||||
params['bounds'] = bounds.to_json()
|
||||
cmd_dict: T_JSON_DICT = {
|
||||
'method': 'Browser.setWindowBounds',
|
||||
'params': params,
|
||||
}
|
||||
json = yield cmd_dict
|
||||
|
||||
|
||||
def set_dock_tile(
|
||||
badge_label: typing.Optional[str] = None,
|
||||
image: typing.Optional[str] = None
|
||||
) -> typing.Generator[T_JSON_DICT,T_JSON_DICT,None]:
|
||||
'''
|
||||
Set dock tile details, platform-specific.
|
||||
|
||||
**EXPERIMENTAL**
|
||||
|
||||
:param badge_label: *(Optional)*
|
||||
:param image: *(Optional)* Png encoded image.
|
||||
'''
|
||||
params: T_JSON_DICT = dict()
|
||||
if badge_label is not None:
|
||||
params['badgeLabel'] = badge_label
|
||||
if image is not None:
|
||||
params['image'] = image
|
||||
cmd_dict: T_JSON_DICT = {
|
||||
'method': 'Browser.setDockTile',
|
||||
'params': params,
|
||||
}
|
||||
json = yield cmd_dict
|
||||
|
||||
|
||||
def execute_browser_command(
|
||||
command_id: BrowserCommandId
|
||||
) -> typing.Generator[T_JSON_DICT,T_JSON_DICT,None]:
|
||||
'''
|
||||
Invoke custom browser commands used by telemetry.
|
||||
|
||||
**EXPERIMENTAL**
|
||||
|
||||
:param command_id:
|
||||
'''
|
||||
params: T_JSON_DICT = dict()
|
||||
params['commandId'] = command_id.to_json()
|
||||
cmd_dict: T_JSON_DICT = {
|
||||
'method': 'Browser.executeBrowserCommand',
|
||||
'params': params,
|
||||
}
|
||||
json = yield cmd_dict
|
||||
|
||||
|
||||
def add_privacy_sandbox_enrollment_override(
|
||||
url: str
|
||||
) -> typing.Generator[T_JSON_DICT,T_JSON_DICT,None]:
|
||||
'''
|
||||
Allows a site to use privacy sandbox features that require enrollment
|
||||
without the site actually being enrolled. Only supported on page targets.
|
||||
|
||||
:param url:
|
||||
'''
|
||||
params: T_JSON_DICT = dict()
|
||||
params['url'] = url
|
||||
cmd_dict: T_JSON_DICT = {
|
||||
'method': 'Browser.addPrivacySandboxEnrollmentOverride',
|
||||
'params': params,
|
||||
}
|
||||
json = yield cmd_dict
|
||||
|
||||
|
||||
@event_class('Browser.downloadWillBegin')
|
||||
@dataclass
|
||||
class DownloadWillBegin:
|
||||
'''
|
||||
**EXPERIMENTAL**
|
||||
|
||||
Fired when page is about to start a download.
|
||||
'''
|
||||
#: Id of the frame that caused the download to begin.
|
||||
frame_id: page.FrameId
|
||||
#: Global unique identifier of the download.
|
||||
guid: str
|
||||
#: URL of the resource being downloaded.
|
||||
url: str
|
||||
#: Suggested file name of the resource (the actual name of the file saved on disk may differ).
|
||||
suggested_filename: str
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, json: T_JSON_DICT) -> DownloadWillBegin:
|
||||
return cls(
|
||||
frame_id=page.FrameId.from_json(json['frameId']),
|
||||
guid=str(json['guid']),
|
||||
url=str(json['url']),
|
||||
suggested_filename=str(json['suggestedFilename'])
|
||||
)
|
||||
|
||||
|
||||
@event_class('Browser.downloadProgress')
|
||||
@dataclass
|
||||
class DownloadProgress:
|
||||
'''
|
||||
**EXPERIMENTAL**
|
||||
|
||||
Fired when download makes progress. Last call has ``done`` == true.
|
||||
'''
|
||||
#: Global unique identifier of the download.
|
||||
guid: str
|
||||
#: Total expected bytes to download.
|
||||
total_bytes: float
|
||||
#: Total bytes received.
|
||||
received_bytes: float
|
||||
#: Download status.
|
||||
state: str
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, json: T_JSON_DICT) -> DownloadProgress:
|
||||
return cls(
|
||||
guid=str(json['guid']),
|
||||
total_bytes=float(json['totalBytes']),
|
||||
received_bytes=float(json['receivedBytes']),
|
||||
state=str(json['state'])
|
||||
)
|
@ -0,0 +1,309 @@
|
||||
# DO NOT EDIT THIS FILE!
|
||||
#
|
||||
# This file is generated from the CDP specification. If you need to make
|
||||
# changes, edit the generator and regenerate all of the modules.
|
||||
#
|
||||
# CDP domain: CacheStorage (experimental)
|
||||
from __future__ import annotations
|
||||
from .util import event_class, T_JSON_DICT
|
||||
from dataclasses import dataclass
|
||||
import enum
|
||||
import typing
|
||||
from . import storage
|
||||
|
||||
|
||||
class CacheId(str):
|
||||
'''
|
||||
Unique identifier of the Cache object.
|
||||
'''
|
||||
def to_json(self) -> str:
|
||||
return self
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, json: str) -> CacheId:
|
||||
return cls(json)
|
||||
|
||||
def __repr__(self):
|
||||
return 'CacheId({})'.format(super().__repr__())
|
||||
|
||||
|
||||
class CachedResponseType(enum.Enum):
|
||||
'''
|
||||
type of HTTP response cached
|
||||
'''
|
||||
BASIC = "basic"
|
||||
CORS = "cors"
|
||||
DEFAULT = "default"
|
||||
ERROR = "error"
|
||||
OPAQUE_RESPONSE = "opaqueResponse"
|
||||
OPAQUE_REDIRECT = "opaqueRedirect"
|
||||
|
||||
def to_json(self):
|
||||
return self.value
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, json):
|
||||
return cls(json)
|
||||
|
||||
|
||||
@dataclass
|
||||
class DataEntry:
|
||||
'''
|
||||
Data entry.
|
||||
'''
|
||||
#: Request URL.
|
||||
request_url: str
|
||||
|
||||
#: Request method.
|
||||
request_method: str
|
||||
|
||||
#: Request headers
|
||||
request_headers: typing.List[Header]
|
||||
|
||||
#: Number of seconds since epoch.
|
||||
response_time: float
|
||||
|
||||
#: HTTP response status code.
|
||||
response_status: int
|
||||
|
||||
#: HTTP response status text.
|
||||
response_status_text: str
|
||||
|
||||
#: HTTP response type
|
||||
response_type: CachedResponseType
|
||||
|
||||
#: Response headers
|
||||
response_headers: typing.List[Header]
|
||||
|
||||
def to_json(self):
|
||||
json = dict()
|
||||
json['requestURL'] = self.request_url
|
||||
json['requestMethod'] = self.request_method
|
||||
json['requestHeaders'] = [i.to_json() for i in self.request_headers]
|
||||
json['responseTime'] = self.response_time
|
||||
json['responseStatus'] = self.response_status
|
||||
json['responseStatusText'] = self.response_status_text
|
||||
json['responseType'] = self.response_type.to_json()
|
||||
json['responseHeaders'] = [i.to_json() for i in self.response_headers]
|
||||
return json
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, json):
|
||||
return cls(
|
||||
request_url=str(json['requestURL']),
|
||||
request_method=str(json['requestMethod']),
|
||||
request_headers=[Header.from_json(i) for i in json['requestHeaders']],
|
||||
response_time=float(json['responseTime']),
|
||||
response_status=int(json['responseStatus']),
|
||||
response_status_text=str(json['responseStatusText']),
|
||||
response_type=CachedResponseType.from_json(json['responseType']),
|
||||
response_headers=[Header.from_json(i) for i in json['responseHeaders']],
|
||||
)
|
||||
|
||||
|
||||
@dataclass
|
||||
class Cache:
|
||||
'''
|
||||
Cache identifier.
|
||||
'''
|
||||
#: An opaque unique id of the cache.
|
||||
cache_id: CacheId
|
||||
|
||||
#: Security origin of the cache.
|
||||
security_origin: str
|
||||
|
||||
#: Storage key of the cache.
|
||||
storage_key: str
|
||||
|
||||
#: The name of the cache.
|
||||
cache_name: str
|
||||
|
||||
#: Storage bucket of the cache.
|
||||
storage_bucket: typing.Optional[storage.StorageBucket] = None
|
||||
|
||||
def to_json(self):
|
||||
json = dict()
|
||||
json['cacheId'] = self.cache_id.to_json()
|
||||
json['securityOrigin'] = self.security_origin
|
||||
json['storageKey'] = self.storage_key
|
||||
json['cacheName'] = self.cache_name
|
||||
if self.storage_bucket is not None:
|
||||
json['storageBucket'] = self.storage_bucket.to_json()
|
||||
return json
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, json):
|
||||
return cls(
|
||||
cache_id=CacheId.from_json(json['cacheId']),
|
||||
security_origin=str(json['securityOrigin']),
|
||||
storage_key=str(json['storageKey']),
|
||||
cache_name=str(json['cacheName']),
|
||||
storage_bucket=storage.StorageBucket.from_json(json['storageBucket']) if 'storageBucket' in json else None,
|
||||
)
|
||||
|
||||
|
||||
@dataclass
|
||||
class Header:
|
||||
name: str
|
||||
|
||||
value: str
|
||||
|
||||
def to_json(self):
|
||||
json = dict()
|
||||
json['name'] = self.name
|
||||
json['value'] = self.value
|
||||
return json
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, json):
|
||||
return cls(
|
||||
name=str(json['name']),
|
||||
value=str(json['value']),
|
||||
)
|
||||
|
||||
|
||||
@dataclass
|
||||
class CachedResponse:
|
||||
'''
|
||||
Cached response
|
||||
'''
|
||||
#: Entry content, base64-encoded.
|
||||
body: str
|
||||
|
||||
def to_json(self):
|
||||
json = dict()
|
||||
json['body'] = self.body
|
||||
return json
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, json):
|
||||
return cls(
|
||||
body=str(json['body']),
|
||||
)
|
||||
|
||||
|
||||
def delete_cache(
|
||||
cache_id: CacheId
|
||||
) -> typing.Generator[T_JSON_DICT,T_JSON_DICT,None]:
|
||||
'''
|
||||
Deletes a cache.
|
||||
|
||||
:param cache_id: Id of cache for deletion.
|
||||
'''
|
||||
params: T_JSON_DICT = dict()
|
||||
params['cacheId'] = cache_id.to_json()
|
||||
cmd_dict: T_JSON_DICT = {
|
||||
'method': 'CacheStorage.deleteCache',
|
||||
'params': params,
|
||||
}
|
||||
json = yield cmd_dict
|
||||
|
||||
|
||||
def delete_entry(
|
||||
cache_id: CacheId,
|
||||
request: str
|
||||
) -> typing.Generator[T_JSON_DICT,T_JSON_DICT,None]:
|
||||
'''
|
||||
Deletes a cache entry.
|
||||
|
||||
:param cache_id: Id of cache where the entry will be deleted.
|
||||
:param request: URL spec of the request.
|
||||
'''
|
||||
params: T_JSON_DICT = dict()
|
||||
params['cacheId'] = cache_id.to_json()
|
||||
params['request'] = request
|
||||
cmd_dict: T_JSON_DICT = {
|
||||
'method': 'CacheStorage.deleteEntry',
|
||||
'params': params,
|
||||
}
|
||||
json = yield cmd_dict
|
||||
|
||||
|
||||
def request_cache_names(
|
||||
security_origin: typing.Optional[str] = None,
|
||||
storage_key: typing.Optional[str] = None,
|
||||
storage_bucket: typing.Optional[storage.StorageBucket] = None
|
||||
) -> typing.Generator[T_JSON_DICT,T_JSON_DICT,typing.List[Cache]]:
|
||||
'''
|
||||
Requests cache names.
|
||||
|
||||
:param security_origin: *(Optional)* At least and at most one of securityOrigin, storageKey, storageBucket must be specified. Security origin.
|
||||
:param storage_key: *(Optional)* Storage key.
|
||||
:param storage_bucket: *(Optional)* Storage bucket. If not specified, it uses the default bucket.
|
||||
:returns: Caches for the security origin.
|
||||
'''
|
||||
params: T_JSON_DICT = dict()
|
||||
if security_origin is not None:
|
||||
params['securityOrigin'] = security_origin
|
||||
if storage_key is not None:
|
||||
params['storageKey'] = storage_key
|
||||
if storage_bucket is not None:
|
||||
params['storageBucket'] = storage_bucket.to_json()
|
||||
cmd_dict: T_JSON_DICT = {
|
||||
'method': 'CacheStorage.requestCacheNames',
|
||||
'params': params,
|
||||
}
|
||||
json = yield cmd_dict
|
||||
return [Cache.from_json(i) for i in json['caches']]
|
||||
|
||||
|
||||
def request_cached_response(
|
||||
cache_id: CacheId,
|
||||
request_url: str,
|
||||
request_headers: typing.List[Header]
|
||||
) -> typing.Generator[T_JSON_DICT,T_JSON_DICT,CachedResponse]:
|
||||
'''
|
||||
Fetches cache entry.
|
||||
|
||||
:param cache_id: Id of cache that contains the entry.
|
||||
:param request_url: URL spec of the request.
|
||||
:param request_headers: headers of the request.
|
||||
:returns: Response read from the cache.
|
||||
'''
|
||||
params: T_JSON_DICT = dict()
|
||||
params['cacheId'] = cache_id.to_json()
|
||||
params['requestURL'] = request_url
|
||||
params['requestHeaders'] = [i.to_json() for i in request_headers]
|
||||
cmd_dict: T_JSON_DICT = {
|
||||
'method': 'CacheStorage.requestCachedResponse',
|
||||
'params': params,
|
||||
}
|
||||
json = yield cmd_dict
|
||||
return CachedResponse.from_json(json['response'])
|
||||
|
||||
|
||||
def request_entries(
|
||||
cache_id: CacheId,
|
||||
skip_count: typing.Optional[int] = None,
|
||||
page_size: typing.Optional[int] = None,
|
||||
path_filter: typing.Optional[str] = None
|
||||
) -> typing.Generator[T_JSON_DICT,T_JSON_DICT,typing.Tuple[typing.List[DataEntry], float]]:
|
||||
'''
|
||||
Requests data from cache.
|
||||
|
||||
:param cache_id: ID of cache to get entries from.
|
||||
:param skip_count: *(Optional)* Number of records to skip.
|
||||
:param page_size: *(Optional)* Number of records to fetch.
|
||||
:param path_filter: *(Optional)* If present, only return the entries containing this substring in the path
|
||||
:returns: A tuple with the following items:
|
||||
|
||||
0. **cacheDataEntries** - Array of object store data entries.
|
||||
1. **returnCount** - Count of returned entries from this storage. If pathFilter is empty, it is the count of all entries from this storage.
|
||||
'''
|
||||
params: T_JSON_DICT = dict()
|
||||
params['cacheId'] = cache_id.to_json()
|
||||
if skip_count is not None:
|
||||
params['skipCount'] = skip_count
|
||||
if page_size is not None:
|
||||
params['pageSize'] = page_size
|
||||
if path_filter is not None:
|
||||
params['pathFilter'] = path_filter
|
||||
cmd_dict: T_JSON_DICT = {
|
||||
'method': 'CacheStorage.requestEntries',
|
||||
'params': params,
|
||||
}
|
||||
json = yield cmd_dict
|
||||
return (
|
||||
[DataEntry.from_json(i) for i in json['cacheDataEntries']],
|
||||
float(json['returnCount'])
|
||||
)
|
170
lib/python3.13/site-packages/selenium/webdriver/common/devtools/v128/cast.py
Executable file
170
lib/python3.13/site-packages/selenium/webdriver/common/devtools/v128/cast.py
Executable file
@ -0,0 +1,170 @@
|
||||
# DO NOT EDIT THIS FILE!
|
||||
#
|
||||
# This file is generated from the CDP specification. If you need to make
|
||||
# changes, edit the generator and regenerate all of the modules.
|
||||
#
|
||||
# CDP domain: Cast (experimental)
|
||||
from __future__ import annotations
|
||||
from .util import event_class, T_JSON_DICT
|
||||
from dataclasses import dataclass
|
||||
import enum
|
||||
import typing
|
||||
|
||||
@dataclass
|
||||
class Sink:
|
||||
name: str
|
||||
|
||||
id_: str
|
||||
|
||||
#: Text describing the current session. Present only if there is an active
|
||||
#: session on the sink.
|
||||
session: typing.Optional[str] = None
|
||||
|
||||
def to_json(self):
|
||||
json = dict()
|
||||
json['name'] = self.name
|
||||
json['id'] = self.id_
|
||||
if self.session is not None:
|
||||
json['session'] = self.session
|
||||
return json
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, json):
|
||||
return cls(
|
||||
name=str(json['name']),
|
||||
id_=str(json['id']),
|
||||
session=str(json['session']) if 'session' in json else None,
|
||||
)
|
||||
|
||||
|
||||
def enable(
|
||||
presentation_url: typing.Optional[str] = None
|
||||
) -> typing.Generator[T_JSON_DICT,T_JSON_DICT,None]:
|
||||
'''
|
||||
Starts observing for sinks that can be used for tab mirroring, and if set,
|
||||
sinks compatible with ``presentationUrl`` as well. When sinks are found, a
|
||||
``sinksUpdated`` event is fired.
|
||||
Also starts observing for issue messages. When an issue is added or removed,
|
||||
an ``issueUpdated`` event is fired.
|
||||
|
||||
:param presentation_url: *(Optional)*
|
||||
'''
|
||||
params: T_JSON_DICT = dict()
|
||||
if presentation_url is not None:
|
||||
params['presentationUrl'] = presentation_url
|
||||
cmd_dict: T_JSON_DICT = {
|
||||
'method': 'Cast.enable',
|
||||
'params': params,
|
||||
}
|
||||
json = yield cmd_dict
|
||||
|
||||
|
||||
def disable() -> typing.Generator[T_JSON_DICT,T_JSON_DICT,None]:
|
||||
'''
|
||||
Stops observing for sinks and issues.
|
||||
'''
|
||||
cmd_dict: T_JSON_DICT = {
|
||||
'method': 'Cast.disable',
|
||||
}
|
||||
json = yield cmd_dict
|
||||
|
||||
|
||||
def set_sink_to_use(
|
||||
sink_name: str
|
||||
) -> typing.Generator[T_JSON_DICT,T_JSON_DICT,None]:
|
||||
'''
|
||||
Sets a sink to be used when the web page requests the browser to choose a
|
||||
sink via Presentation API, Remote Playback API, or Cast SDK.
|
||||
|
||||
:param sink_name:
|
||||
'''
|
||||
params: T_JSON_DICT = dict()
|
||||
params['sinkName'] = sink_name
|
||||
cmd_dict: T_JSON_DICT = {
|
||||
'method': 'Cast.setSinkToUse',
|
||||
'params': params,
|
||||
}
|
||||
json = yield cmd_dict
|
||||
|
||||
|
||||
def start_desktop_mirroring(
|
||||
sink_name: str
|
||||
) -> typing.Generator[T_JSON_DICT,T_JSON_DICT,None]:
|
||||
'''
|
||||
Starts mirroring the desktop to the sink.
|
||||
|
||||
:param sink_name:
|
||||
'''
|
||||
params: T_JSON_DICT = dict()
|
||||
params['sinkName'] = sink_name
|
||||
cmd_dict: T_JSON_DICT = {
|
||||
'method': 'Cast.startDesktopMirroring',
|
||||
'params': params,
|
||||
}
|
||||
json = yield cmd_dict
|
||||
|
||||
|
||||
def start_tab_mirroring(
|
||||
sink_name: str
|
||||
) -> typing.Generator[T_JSON_DICT,T_JSON_DICT,None]:
|
||||
'''
|
||||
Starts mirroring the tab to the sink.
|
||||
|
||||
:param sink_name:
|
||||
'''
|
||||
params: T_JSON_DICT = dict()
|
||||
params['sinkName'] = sink_name
|
||||
cmd_dict: T_JSON_DICT = {
|
||||
'method': 'Cast.startTabMirroring',
|
||||
'params': params,
|
||||
}
|
||||
json = yield cmd_dict
|
||||
|
||||
|
||||
def stop_casting(
|
||||
sink_name: str
|
||||
) -> typing.Generator[T_JSON_DICT,T_JSON_DICT,None]:
|
||||
'''
|
||||
Stops the active Cast session on the sink.
|
||||
|
||||
:param sink_name:
|
||||
'''
|
||||
params: T_JSON_DICT = dict()
|
||||
params['sinkName'] = sink_name
|
||||
cmd_dict: T_JSON_DICT = {
|
||||
'method': 'Cast.stopCasting',
|
||||
'params': params,
|
||||
}
|
||||
json = yield cmd_dict
|
||||
|
||||
|
||||
@event_class('Cast.sinksUpdated')
|
||||
@dataclass
|
||||
class SinksUpdated:
|
||||
'''
|
||||
This is fired whenever the list of available sinks changes. A sink is a
|
||||
device or a software surface that you can cast to.
|
||||
'''
|
||||
sinks: typing.List[Sink]
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, json: T_JSON_DICT) -> SinksUpdated:
|
||||
return cls(
|
||||
sinks=[Sink.from_json(i) for i in json['sinks']]
|
||||
)
|
||||
|
||||
|
||||
@event_class('Cast.issueUpdated')
|
||||
@dataclass
|
||||
class IssueUpdated:
|
||||
'''
|
||||
This is fired whenever the outstanding issue/error message changes.
|
||||
``issueMessage`` is empty if there is no issue.
|
||||
'''
|
||||
issue_message: str
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, json: T_JSON_DICT) -> IssueUpdated:
|
||||
return cls(
|
||||
issue_message=str(json['issueMessage'])
|
||||
)
|
105
lib/python3.13/site-packages/selenium/webdriver/common/devtools/v128/console.py
Executable file
105
lib/python3.13/site-packages/selenium/webdriver/common/devtools/v128/console.py
Executable file
@ -0,0 +1,105 @@
|
||||
# DO NOT EDIT THIS FILE!
|
||||
#
|
||||
# This file is generated from the CDP specification. If you need to make
|
||||
# changes, edit the generator and regenerate all of the modules.
|
||||
#
|
||||
# CDP domain: Console
|
||||
from __future__ import annotations
|
||||
from .util import event_class, T_JSON_DICT
|
||||
from dataclasses import dataclass
|
||||
import enum
|
||||
import typing
|
||||
|
||||
@dataclass
|
||||
class ConsoleMessage:
|
||||
'''
|
||||
Console message.
|
||||
'''
|
||||
#: Message source.
|
||||
source: str
|
||||
|
||||
#: Message severity.
|
||||
level: str
|
||||
|
||||
#: Message text.
|
||||
text: str
|
||||
|
||||
#: URL of the message origin.
|
||||
url: typing.Optional[str] = None
|
||||
|
||||
#: Line number in the resource that generated this message (1-based).
|
||||
line: typing.Optional[int] = None
|
||||
|
||||
#: Column number in the resource that generated this message (1-based).
|
||||
column: typing.Optional[int] = None
|
||||
|
||||
def to_json(self):
|
||||
json = dict()
|
||||
json['source'] = self.source
|
||||
json['level'] = self.level
|
||||
json['text'] = self.text
|
||||
if self.url is not None:
|
||||
json['url'] = self.url
|
||||
if self.line is not None:
|
||||
json['line'] = self.line
|
||||
if self.column is not None:
|
||||
json['column'] = self.column
|
||||
return json
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, json):
|
||||
return cls(
|
||||
source=str(json['source']),
|
||||
level=str(json['level']),
|
||||
text=str(json['text']),
|
||||
url=str(json['url']) if 'url' in json else None,
|
||||
line=int(json['line']) if 'line' in json else None,
|
||||
column=int(json['column']) if 'column' in json else None,
|
||||
)
|
||||
|
||||
|
||||
def clear_messages() -> typing.Generator[T_JSON_DICT,T_JSON_DICT,None]:
|
||||
'''
|
||||
Does nothing.
|
||||
'''
|
||||
cmd_dict: T_JSON_DICT = {
|
||||
'method': 'Console.clearMessages',
|
||||
}
|
||||
json = yield cmd_dict
|
||||
|
||||
|
||||
def disable() -> typing.Generator[T_JSON_DICT,T_JSON_DICT,None]:
|
||||
'''
|
||||
Disables console domain, prevents further console messages from being reported to the client.
|
||||
'''
|
||||
cmd_dict: T_JSON_DICT = {
|
||||
'method': 'Console.disable',
|
||||
}
|
||||
json = yield cmd_dict
|
||||
|
||||
|
||||
def enable() -> typing.Generator[T_JSON_DICT,T_JSON_DICT,None]:
|
||||
'''
|
||||
Enables console domain, sends the messages collected so far to the client by means of the
|
||||
``messageAdded`` notification.
|
||||
'''
|
||||
cmd_dict: T_JSON_DICT = {
|
||||
'method': 'Console.enable',
|
||||
}
|
||||
json = yield cmd_dict
|
||||
|
||||
|
||||
@event_class('Console.messageAdded')
|
||||
@dataclass
|
||||
class MessageAdded:
|
||||
'''
|
||||
Issued when new console message is added.
|
||||
'''
|
||||
#: Console message that has been added.
|
||||
message: ConsoleMessage
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, json: T_JSON_DICT) -> MessageAdded:
|
||||
return cls(
|
||||
message=ConsoleMessage.from_json(json['message'])
|
||||
)
|
2194
lib/python3.13/site-packages/selenium/webdriver/common/devtools/v128/css.py
Executable file
2194
lib/python3.13/site-packages/selenium/webdriver/common/devtools/v128/css.py
Executable file
File diff suppressed because it is too large
Load Diff
162
lib/python3.13/site-packages/selenium/webdriver/common/devtools/v128/database.py
Executable file
162
lib/python3.13/site-packages/selenium/webdriver/common/devtools/v128/database.py
Executable file
@ -0,0 +1,162 @@
|
||||
# DO NOT EDIT THIS FILE!
|
||||
#
|
||||
# This file is generated from the CDP specification. If you need to make
|
||||
# changes, edit the generator and regenerate all of the modules.
|
||||
#
|
||||
# CDP domain: Database (experimental)
|
||||
from __future__ import annotations
|
||||
from .util import event_class, T_JSON_DICT
|
||||
from dataclasses import dataclass
|
||||
import enum
|
||||
import typing
|
||||
|
||||
class DatabaseId(str):
|
||||
'''
|
||||
Unique identifier of Database object.
|
||||
'''
|
||||
def to_json(self) -> str:
|
||||
return self
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, json: str) -> DatabaseId:
|
||||
return cls(json)
|
||||
|
||||
def __repr__(self):
|
||||
return 'DatabaseId({})'.format(super().__repr__())
|
||||
|
||||
|
||||
@dataclass
|
||||
class Database:
|
||||
'''
|
||||
Database object.
|
||||
'''
|
||||
#: Database ID.
|
||||
id_: DatabaseId
|
||||
|
||||
#: Database domain.
|
||||
domain: str
|
||||
|
||||
#: Database name.
|
||||
name: str
|
||||
|
||||
#: Database version.
|
||||
version: str
|
||||
|
||||
def to_json(self):
|
||||
json = dict()
|
||||
json['id'] = self.id_.to_json()
|
||||
json['domain'] = self.domain
|
||||
json['name'] = self.name
|
||||
json['version'] = self.version
|
||||
return json
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, json):
|
||||
return cls(
|
||||
id_=DatabaseId.from_json(json['id']),
|
||||
domain=str(json['domain']),
|
||||
name=str(json['name']),
|
||||
version=str(json['version']),
|
||||
)
|
||||
|
||||
|
||||
@dataclass
|
||||
class Error:
|
||||
'''
|
||||
Database error.
|
||||
'''
|
||||
#: Error message.
|
||||
message: str
|
||||
|
||||
#: Error code.
|
||||
code: int
|
||||
|
||||
def to_json(self):
|
||||
json = dict()
|
||||
json['message'] = self.message
|
||||
json['code'] = self.code
|
||||
return json
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, json):
|
||||
return cls(
|
||||
message=str(json['message']),
|
||||
code=int(json['code']),
|
||||
)
|
||||
|
||||
|
||||
def disable() -> typing.Generator[T_JSON_DICT,T_JSON_DICT,None]:
|
||||
'''
|
||||
Disables database tracking, prevents database events from being sent to the client.
|
||||
'''
|
||||
cmd_dict: T_JSON_DICT = {
|
||||
'method': 'Database.disable',
|
||||
}
|
||||
json = yield cmd_dict
|
||||
|
||||
|
||||
def enable() -> typing.Generator[T_JSON_DICT,T_JSON_DICT,None]:
|
||||
'''
|
||||
Enables database tracking, database events will now be delivered to the client.
|
||||
'''
|
||||
cmd_dict: T_JSON_DICT = {
|
||||
'method': 'Database.enable',
|
||||
}
|
||||
json = yield cmd_dict
|
||||
|
||||
|
||||
def execute_sql(
|
||||
database_id: DatabaseId,
|
||||
query: str
|
||||
) -> typing.Generator[T_JSON_DICT,T_JSON_DICT,typing.Tuple[typing.Optional[typing.List[str]], typing.Optional[typing.List[typing.Any]], typing.Optional[Error]]]:
|
||||
'''
|
||||
:param database_id:
|
||||
:param query:
|
||||
:returns: A tuple with the following items:
|
||||
|
||||
0. **columnNames** -
|
||||
1. **values** -
|
||||
2. **sqlError** -
|
||||
'''
|
||||
params: T_JSON_DICT = dict()
|
||||
params['databaseId'] = database_id.to_json()
|
||||
params['query'] = query
|
||||
cmd_dict: T_JSON_DICT = {
|
||||
'method': 'Database.executeSQL',
|
||||
'params': params,
|
||||
}
|
||||
json = yield cmd_dict
|
||||
return (
|
||||
[str(i) for i in json['columnNames']] if 'columnNames' in json else None,
|
||||
[i for i in json['values']] if 'values' in json else None,
|
||||
Error.from_json(json['sqlError']) if 'sqlError' in json else None
|
||||
)
|
||||
|
||||
|
||||
def get_database_table_names(
|
||||
database_id: DatabaseId
|
||||
) -> typing.Generator[T_JSON_DICT,T_JSON_DICT,typing.List[str]]:
|
||||
'''
|
||||
:param database_id:
|
||||
:returns:
|
||||
'''
|
||||
params: T_JSON_DICT = dict()
|
||||
params['databaseId'] = database_id.to_json()
|
||||
cmd_dict: T_JSON_DICT = {
|
||||
'method': 'Database.getDatabaseTableNames',
|
||||
'params': params,
|
||||
}
|
||||
json = yield cmd_dict
|
||||
return [str(i) for i in json['tableNames']]
|
||||
|
||||
|
||||
@event_class('Database.addDatabase')
|
||||
@dataclass
|
||||
class AddDatabase:
|
||||
database: Database
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, json: T_JSON_DICT) -> AddDatabase:
|
||||
return cls(
|
||||
database=Database.from_json(json['database'])
|
||||
)
|
1336
lib/python3.13/site-packages/selenium/webdriver/common/devtools/v128/debugger.py
Executable file
1336
lib/python3.13/site-packages/selenium/webdriver/common/devtools/v128/debugger.py
Executable file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,139 @@
|
||||
# DO NOT EDIT THIS FILE!
|
||||
#
|
||||
# This file is generated from the CDP specification. If you need to make
|
||||
# changes, edit the generator and regenerate all of the modules.
|
||||
#
|
||||
# CDP domain: DeviceAccess (experimental)
|
||||
from __future__ import annotations
|
||||
from .util import event_class, T_JSON_DICT
|
||||
from dataclasses import dataclass
|
||||
import enum
|
||||
import typing
|
||||
|
||||
class RequestId(str):
|
||||
'''
|
||||
Device request id.
|
||||
'''
|
||||
def to_json(self) -> str:
|
||||
return self
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, json: str) -> RequestId:
|
||||
return cls(json)
|
||||
|
||||
def __repr__(self):
|
||||
return 'RequestId({})'.format(super().__repr__())
|
||||
|
||||
|
||||
class DeviceId(str):
|
||||
'''
|
||||
A device id.
|
||||
'''
|
||||
def to_json(self) -> str:
|
||||
return self
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, json: str) -> DeviceId:
|
||||
return cls(json)
|
||||
|
||||
def __repr__(self):
|
||||
return 'DeviceId({})'.format(super().__repr__())
|
||||
|
||||
|
||||
@dataclass
|
||||
class PromptDevice:
|
||||
'''
|
||||
Device information displayed in a user prompt to select a device.
|
||||
'''
|
||||
id_: DeviceId
|
||||
|
||||
#: Display name as it appears in a device request user prompt.
|
||||
name: str
|
||||
|
||||
def to_json(self):
|
||||
json = dict()
|
||||
json['id'] = self.id_.to_json()
|
||||
json['name'] = self.name
|
||||
return json
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, json):
|
||||
return cls(
|
||||
id_=DeviceId.from_json(json['id']),
|
||||
name=str(json['name']),
|
||||
)
|
||||
|
||||
|
||||
def enable() -> typing.Generator[T_JSON_DICT,T_JSON_DICT,None]:
|
||||
'''
|
||||
Enable events in this domain.
|
||||
'''
|
||||
cmd_dict: T_JSON_DICT = {
|
||||
'method': 'DeviceAccess.enable',
|
||||
}
|
||||
json = yield cmd_dict
|
||||
|
||||
|
||||
def disable() -> typing.Generator[T_JSON_DICT,T_JSON_DICT,None]:
|
||||
'''
|
||||
Disable events in this domain.
|
||||
'''
|
||||
cmd_dict: T_JSON_DICT = {
|
||||
'method': 'DeviceAccess.disable',
|
||||
}
|
||||
json = yield cmd_dict
|
||||
|
||||
|
||||
def select_prompt(
|
||||
id_: RequestId,
|
||||
device_id: DeviceId
|
||||
) -> typing.Generator[T_JSON_DICT,T_JSON_DICT,None]:
|
||||
'''
|
||||
Select a device in response to a DeviceAccess.deviceRequestPrompted event.
|
||||
|
||||
:param id_:
|
||||
:param device_id:
|
||||
'''
|
||||
params: T_JSON_DICT = dict()
|
||||
params['id'] = id_.to_json()
|
||||
params['deviceId'] = device_id.to_json()
|
||||
cmd_dict: T_JSON_DICT = {
|
||||
'method': 'DeviceAccess.selectPrompt',
|
||||
'params': params,
|
||||
}
|
||||
json = yield cmd_dict
|
||||
|
||||
|
||||
def cancel_prompt(
|
||||
id_: RequestId
|
||||
) -> typing.Generator[T_JSON_DICT,T_JSON_DICT,None]:
|
||||
'''
|
||||
Cancel a prompt in response to a DeviceAccess.deviceRequestPrompted event.
|
||||
|
||||
:param id_:
|
||||
'''
|
||||
params: T_JSON_DICT = dict()
|
||||
params['id'] = id_.to_json()
|
||||
cmd_dict: T_JSON_DICT = {
|
||||
'method': 'DeviceAccess.cancelPrompt',
|
||||
'params': params,
|
||||
}
|
||||
json = yield cmd_dict
|
||||
|
||||
|
||||
@event_class('DeviceAccess.deviceRequestPrompted')
|
||||
@dataclass
|
||||
class DeviceRequestPrompted:
|
||||
'''
|
||||
A device request opened a user prompt to select a device. Respond with the
|
||||
selectPrompt or cancelPrompt command.
|
||||
'''
|
||||
id_: RequestId
|
||||
devices: typing.List[PromptDevice]
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, json: T_JSON_DICT) -> DeviceRequestPrompted:
|
||||
return cls(
|
||||
id_=RequestId.from_json(json['id']),
|
||||
devices=[PromptDevice.from_json(i) for i in json['devices']]
|
||||
)
|
@ -0,0 +1,43 @@
|
||||
# DO NOT EDIT THIS FILE!
|
||||
#
|
||||
# This file is generated from the CDP specification. If you need to make
|
||||
# changes, edit the generator and regenerate all of the modules.
|
||||
#
|
||||
# CDP domain: DeviceOrientation (experimental)
|
||||
from __future__ import annotations
|
||||
from .util import event_class, T_JSON_DICT
|
||||
from dataclasses import dataclass
|
||||
import enum
|
||||
import typing
|
||||
|
||||
def clear_device_orientation_override() -> typing.Generator[T_JSON_DICT,T_JSON_DICT,None]:
|
||||
'''
|
||||
Clears the overridden Device Orientation.
|
||||
'''
|
||||
cmd_dict: T_JSON_DICT = {
|
||||
'method': 'DeviceOrientation.clearDeviceOrientationOverride',
|
||||
}
|
||||
json = yield cmd_dict
|
||||
|
||||
|
||||
def set_device_orientation_override(
|
||||
alpha: float,
|
||||
beta: float,
|
||||
gamma: float
|
||||
) -> typing.Generator[T_JSON_DICT,T_JSON_DICT,None]:
|
||||
'''
|
||||
Overrides the Device Orientation.
|
||||
|
||||
:param alpha: Mock alpha
|
||||
:param beta: Mock beta
|
||||
:param gamma: Mock gamma
|
||||
'''
|
||||
params: T_JSON_DICT = dict()
|
||||
params['alpha'] = alpha
|
||||
params['beta'] = beta
|
||||
params['gamma'] = gamma
|
||||
cmd_dict: T_JSON_DICT = {
|
||||
'method': 'DeviceOrientation.setDeviceOrientationOverride',
|
||||
'params': params,
|
||||
}
|
||||
json = yield cmd_dict
|
2042
lib/python3.13/site-packages/selenium/webdriver/common/devtools/v128/dom.py
Executable file
2042
lib/python3.13/site-packages/selenium/webdriver/common/devtools/v128/dom.py
Executable file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,312 @@
|
||||
# DO NOT EDIT THIS FILE!
|
||||
#
|
||||
# This file is generated from the CDP specification. If you need to make
|
||||
# changes, edit the generator and regenerate all of the modules.
|
||||
#
|
||||
# CDP domain: DOMDebugger
|
||||
from __future__ import annotations
|
||||
from .util import event_class, T_JSON_DICT
|
||||
from dataclasses import dataclass
|
||||
import enum
|
||||
import typing
|
||||
from . import dom
|
||||
from . import runtime
|
||||
|
||||
|
||||
class DOMBreakpointType(enum.Enum):
|
||||
'''
|
||||
DOM breakpoint type.
|
||||
'''
|
||||
SUBTREE_MODIFIED = "subtree-modified"
|
||||
ATTRIBUTE_MODIFIED = "attribute-modified"
|
||||
NODE_REMOVED = "node-removed"
|
||||
|
||||
def to_json(self):
|
||||
return self.value
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, json):
|
||||
return cls(json)
|
||||
|
||||
|
||||
class CSPViolationType(enum.Enum):
|
||||
'''
|
||||
CSP Violation type.
|
||||
'''
|
||||
TRUSTEDTYPE_SINK_VIOLATION = "trustedtype-sink-violation"
|
||||
TRUSTEDTYPE_POLICY_VIOLATION = "trustedtype-policy-violation"
|
||||
|
||||
def to_json(self):
|
||||
return self.value
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, json):
|
||||
return cls(json)
|
||||
|
||||
|
||||
@dataclass
|
||||
class EventListener:
|
||||
'''
|
||||
Object event listener.
|
||||
'''
|
||||
#: ``EventListener``'s type.
|
||||
type_: str
|
||||
|
||||
#: ``EventListener``'s useCapture.
|
||||
use_capture: bool
|
||||
|
||||
#: ``EventListener``'s passive flag.
|
||||
passive: bool
|
||||
|
||||
#: ``EventListener``'s once flag.
|
||||
once: bool
|
||||
|
||||
#: Script id of the handler code.
|
||||
script_id: runtime.ScriptId
|
||||
|
||||
#: Line number in the script (0-based).
|
||||
line_number: int
|
||||
|
||||
#: Column number in the script (0-based).
|
||||
column_number: int
|
||||
|
||||
#: Event handler function value.
|
||||
handler: typing.Optional[runtime.RemoteObject] = None
|
||||
|
||||
#: Event original handler function value.
|
||||
original_handler: typing.Optional[runtime.RemoteObject] = None
|
||||
|
||||
#: Node the listener is added to (if any).
|
||||
backend_node_id: typing.Optional[dom.BackendNodeId] = None
|
||||
|
||||
def to_json(self):
|
||||
json = dict()
|
||||
json['type'] = self.type_
|
||||
json['useCapture'] = self.use_capture
|
||||
json['passive'] = self.passive
|
||||
json['once'] = self.once
|
||||
json['scriptId'] = self.script_id.to_json()
|
||||
json['lineNumber'] = self.line_number
|
||||
json['columnNumber'] = self.column_number
|
||||
if self.handler is not None:
|
||||
json['handler'] = self.handler.to_json()
|
||||
if self.original_handler is not None:
|
||||
json['originalHandler'] = self.original_handler.to_json()
|
||||
if self.backend_node_id is not None:
|
||||
json['backendNodeId'] = self.backend_node_id.to_json()
|
||||
return json
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, json):
|
||||
return cls(
|
||||
type_=str(json['type']),
|
||||
use_capture=bool(json['useCapture']),
|
||||
passive=bool(json['passive']),
|
||||
once=bool(json['once']),
|
||||
script_id=runtime.ScriptId.from_json(json['scriptId']),
|
||||
line_number=int(json['lineNumber']),
|
||||
column_number=int(json['columnNumber']),
|
||||
handler=runtime.RemoteObject.from_json(json['handler']) if 'handler' in json else None,
|
||||
original_handler=runtime.RemoteObject.from_json(json['originalHandler']) if 'originalHandler' in json else None,
|
||||
backend_node_id=dom.BackendNodeId.from_json(json['backendNodeId']) if 'backendNodeId' in json else None,
|
||||
)
|
||||
|
||||
|
||||
def get_event_listeners(
|
||||
object_id: runtime.RemoteObjectId,
|
||||
depth: typing.Optional[int] = None,
|
||||
pierce: typing.Optional[bool] = None
|
||||
) -> typing.Generator[T_JSON_DICT,T_JSON_DICT,typing.List[EventListener]]:
|
||||
'''
|
||||
Returns event listeners of the given object.
|
||||
|
||||
:param object_id: Identifier of the object to return listeners for.
|
||||
:param depth: *(Optional)* The maximum depth at which Node children should be retrieved, defaults to 1. Use -1 for the entire subtree or provide an integer larger than 0.
|
||||
:param pierce: *(Optional)* Whether or not iframes and shadow roots should be traversed when returning the subtree (default is false). Reports listeners for all contexts if pierce is enabled.
|
||||
:returns: Array of relevant listeners.
|
||||
'''
|
||||
params: T_JSON_DICT = dict()
|
||||
params['objectId'] = object_id.to_json()
|
||||
if depth is not None:
|
||||
params['depth'] = depth
|
||||
if pierce is not None:
|
||||
params['pierce'] = pierce
|
||||
cmd_dict: T_JSON_DICT = {
|
||||
'method': 'DOMDebugger.getEventListeners',
|
||||
'params': params,
|
||||
}
|
||||
json = yield cmd_dict
|
||||
return [EventListener.from_json(i) for i in json['listeners']]
|
||||
|
||||
|
||||
def remove_dom_breakpoint(
|
||||
node_id: dom.NodeId,
|
||||
type_: DOMBreakpointType
|
||||
) -> typing.Generator[T_JSON_DICT,T_JSON_DICT,None]:
|
||||
'''
|
||||
Removes DOM breakpoint that was set using ``setDOMBreakpoint``.
|
||||
|
||||
:param node_id: Identifier of the node to remove breakpoint from.
|
||||
:param type_: Type of the breakpoint to remove.
|
||||
'''
|
||||
params: T_JSON_DICT = dict()
|
||||
params['nodeId'] = node_id.to_json()
|
||||
params['type'] = type_.to_json()
|
||||
cmd_dict: T_JSON_DICT = {
|
||||
'method': 'DOMDebugger.removeDOMBreakpoint',
|
||||
'params': params,
|
||||
}
|
||||
json = yield cmd_dict
|
||||
|
||||
|
||||
def remove_event_listener_breakpoint(
|
||||
event_name: str,
|
||||
target_name: typing.Optional[str] = None
|
||||
) -> typing.Generator[T_JSON_DICT,T_JSON_DICT,None]:
|
||||
'''
|
||||
Removes breakpoint on particular DOM event.
|
||||
|
||||
:param event_name: Event name.
|
||||
:param target_name: **(EXPERIMENTAL)** *(Optional)* EventTarget interface name.
|
||||
'''
|
||||
params: T_JSON_DICT = dict()
|
||||
params['eventName'] = event_name
|
||||
if target_name is not None:
|
||||
params['targetName'] = target_name
|
||||
cmd_dict: T_JSON_DICT = {
|
||||
'method': 'DOMDebugger.removeEventListenerBreakpoint',
|
||||
'params': params,
|
||||
}
|
||||
json = yield cmd_dict
|
||||
|
||||
|
||||
def remove_instrumentation_breakpoint(
|
||||
event_name: str
|
||||
) -> typing.Generator[T_JSON_DICT,T_JSON_DICT,None]:
|
||||
'''
|
||||
Removes breakpoint on particular native event.
|
||||
|
||||
**EXPERIMENTAL**
|
||||
|
||||
:param event_name: Instrumentation name to stop on.
|
||||
'''
|
||||
params: T_JSON_DICT = dict()
|
||||
params['eventName'] = event_name
|
||||
cmd_dict: T_JSON_DICT = {
|
||||
'method': 'DOMDebugger.removeInstrumentationBreakpoint',
|
||||
'params': params,
|
||||
}
|
||||
json = yield cmd_dict
|
||||
|
||||
|
||||
def remove_xhr_breakpoint(
|
||||
url: str
|
||||
) -> typing.Generator[T_JSON_DICT,T_JSON_DICT,None]:
|
||||
'''
|
||||
Removes breakpoint from XMLHttpRequest.
|
||||
|
||||
:param url: Resource URL substring.
|
||||
'''
|
||||
params: T_JSON_DICT = dict()
|
||||
params['url'] = url
|
||||
cmd_dict: T_JSON_DICT = {
|
||||
'method': 'DOMDebugger.removeXHRBreakpoint',
|
||||
'params': params,
|
||||
}
|
||||
json = yield cmd_dict
|
||||
|
||||
|
||||
def set_break_on_csp_violation(
|
||||
violation_types: typing.List[CSPViolationType]
|
||||
) -> typing.Generator[T_JSON_DICT,T_JSON_DICT,None]:
|
||||
'''
|
||||
Sets breakpoint on particular CSP violations.
|
||||
|
||||
**EXPERIMENTAL**
|
||||
|
||||
:param violation_types: CSP Violations to stop upon.
|
||||
'''
|
||||
params: T_JSON_DICT = dict()
|
||||
params['violationTypes'] = [i.to_json() for i in violation_types]
|
||||
cmd_dict: T_JSON_DICT = {
|
||||
'method': 'DOMDebugger.setBreakOnCSPViolation',
|
||||
'params': params,
|
||||
}
|
||||
json = yield cmd_dict
|
||||
|
||||
|
||||
def set_dom_breakpoint(
|
||||
node_id: dom.NodeId,
|
||||
type_: DOMBreakpointType
|
||||
) -> typing.Generator[T_JSON_DICT,T_JSON_DICT,None]:
|
||||
'''
|
||||
Sets breakpoint on particular operation with DOM.
|
||||
|
||||
:param node_id: Identifier of the node to set breakpoint on.
|
||||
:param type_: Type of the operation to stop upon.
|
||||
'''
|
||||
params: T_JSON_DICT = dict()
|
||||
params['nodeId'] = node_id.to_json()
|
||||
params['type'] = type_.to_json()
|
||||
cmd_dict: T_JSON_DICT = {
|
||||
'method': 'DOMDebugger.setDOMBreakpoint',
|
||||
'params': params,
|
||||
}
|
||||
json = yield cmd_dict
|
||||
|
||||
|
||||
def set_event_listener_breakpoint(
|
||||
event_name: str,
|
||||
target_name: typing.Optional[str] = None
|
||||
) -> typing.Generator[T_JSON_DICT,T_JSON_DICT,None]:
|
||||
'''
|
||||
Sets breakpoint on particular DOM event.
|
||||
|
||||
:param event_name: DOM Event name to stop on (any DOM event will do).
|
||||
:param target_name: **(EXPERIMENTAL)** *(Optional)* EventTarget interface name to stop on. If equal to ```"*"``` or not provided, will stop on any EventTarget.
|
||||
'''
|
||||
params: T_JSON_DICT = dict()
|
||||
params['eventName'] = event_name
|
||||
if target_name is not None:
|
||||
params['targetName'] = target_name
|
||||
cmd_dict: T_JSON_DICT = {
|
||||
'method': 'DOMDebugger.setEventListenerBreakpoint',
|
||||
'params': params,
|
||||
}
|
||||
json = yield cmd_dict
|
||||
|
||||
|
||||
def set_instrumentation_breakpoint(
|
||||
event_name: str
|
||||
) -> typing.Generator[T_JSON_DICT,T_JSON_DICT,None]:
|
||||
'''
|
||||
Sets breakpoint on particular native event.
|
||||
|
||||
**EXPERIMENTAL**
|
||||
|
||||
:param event_name: Instrumentation name to stop on.
|
||||
'''
|
||||
params: T_JSON_DICT = dict()
|
||||
params['eventName'] = event_name
|
||||
cmd_dict: T_JSON_DICT = {
|
||||
'method': 'DOMDebugger.setInstrumentationBreakpoint',
|
||||
'params': params,
|
||||
}
|
||||
json = yield cmd_dict
|
||||
|
||||
|
||||
def set_xhr_breakpoint(
|
||||
url: str
|
||||
) -> typing.Generator[T_JSON_DICT,T_JSON_DICT,None]:
|
||||
'''
|
||||
Sets breakpoint on XMLHttpRequest.
|
||||
|
||||
:param url: Resource URL substring. All XHRs having this substring in the URL will get stopped upon.
|
||||
'''
|
||||
params: T_JSON_DICT = dict()
|
||||
params['url'] = url
|
||||
cmd_dict: T_JSON_DICT = {
|
||||
'method': 'DOMDebugger.setXHRBreakpoint',
|
||||
'params': params,
|
||||
}
|
||||
json = yield cmd_dict
|
@ -0,0 +1,870 @@
|
||||
# DO NOT EDIT THIS FILE!
|
||||
#
|
||||
# This file is generated from the CDP specification. If you need to make
|
||||
# changes, edit the generator and regenerate all of the modules.
|
||||
#
|
||||
# CDP domain: DOMSnapshot (experimental)
|
||||
from __future__ import annotations
|
||||
from .util import event_class, T_JSON_DICT
|
||||
from dataclasses import dataclass
|
||||
import enum
|
||||
import typing
|
||||
from . import dom
|
||||
from . import dom_debugger
|
||||
from . import page
|
||||
|
||||
|
||||
@dataclass
|
||||
class DOMNode:
|
||||
'''
|
||||
A Node in the DOM tree.
|
||||
'''
|
||||
#: ``Node``'s nodeType.
|
||||
node_type: int
|
||||
|
||||
#: ``Node``'s nodeName.
|
||||
node_name: str
|
||||
|
||||
#: ``Node``'s nodeValue.
|
||||
node_value: str
|
||||
|
||||
#: ``Node``'s id, corresponds to DOM.Node.backendNodeId.
|
||||
backend_node_id: dom.BackendNodeId
|
||||
|
||||
#: Only set for textarea elements, contains the text value.
|
||||
text_value: typing.Optional[str] = None
|
||||
|
||||
#: Only set for input elements, contains the input's associated text value.
|
||||
input_value: typing.Optional[str] = None
|
||||
|
||||
#: Only set for radio and checkbox input elements, indicates if the element has been checked
|
||||
input_checked: typing.Optional[bool] = None
|
||||
|
||||
#: Only set for option elements, indicates if the element has been selected
|
||||
option_selected: typing.Optional[bool] = None
|
||||
|
||||
#: The indexes of the node's child nodes in the ``domNodes`` array returned by ``getSnapshot``, if
|
||||
#: any.
|
||||
child_node_indexes: typing.Optional[typing.List[int]] = None
|
||||
|
||||
#: Attributes of an ``Element`` node.
|
||||
attributes: typing.Optional[typing.List[NameValue]] = None
|
||||
|
||||
#: Indexes of pseudo elements associated with this node in the ``domNodes`` array returned by
|
||||
#: ``getSnapshot``, if any.
|
||||
pseudo_element_indexes: typing.Optional[typing.List[int]] = None
|
||||
|
||||
#: The index of the node's related layout tree node in the ``layoutTreeNodes`` array returned by
|
||||
#: ``getSnapshot``, if any.
|
||||
layout_node_index: typing.Optional[int] = None
|
||||
|
||||
#: Document URL that ``Document`` or ``FrameOwner`` node points to.
|
||||
document_url: typing.Optional[str] = None
|
||||
|
||||
#: Base URL that ``Document`` or ``FrameOwner`` node uses for URL completion.
|
||||
base_url: typing.Optional[str] = None
|
||||
|
||||
#: Only set for documents, contains the document's content language.
|
||||
content_language: typing.Optional[str] = None
|
||||
|
||||
#: Only set for documents, contains the document's character set encoding.
|
||||
document_encoding: typing.Optional[str] = None
|
||||
|
||||
#: ``DocumentType`` node's publicId.
|
||||
public_id: typing.Optional[str] = None
|
||||
|
||||
#: ``DocumentType`` node's systemId.
|
||||
system_id: typing.Optional[str] = None
|
||||
|
||||
#: Frame ID for frame owner elements and also for the document node.
|
||||
frame_id: typing.Optional[page.FrameId] = None
|
||||
|
||||
#: The index of a frame owner element's content document in the ``domNodes`` array returned by
|
||||
#: ``getSnapshot``, if any.
|
||||
content_document_index: typing.Optional[int] = None
|
||||
|
||||
#: Type of a pseudo element node.
|
||||
pseudo_type: typing.Optional[dom.PseudoType] = None
|
||||
|
||||
#: Shadow root type.
|
||||
shadow_root_type: typing.Optional[dom.ShadowRootType] = None
|
||||
|
||||
#: Whether this DOM node responds to mouse clicks. This includes nodes that have had click
|
||||
#: event listeners attached via JavaScript as well as anchor tags that naturally navigate when
|
||||
#: clicked.
|
||||
is_clickable: typing.Optional[bool] = None
|
||||
|
||||
#: Details of the node's event listeners, if any.
|
||||
event_listeners: typing.Optional[typing.List[dom_debugger.EventListener]] = None
|
||||
|
||||
#: The selected url for nodes with a srcset attribute.
|
||||
current_source_url: typing.Optional[str] = None
|
||||
|
||||
#: The url of the script (if any) that generates this node.
|
||||
origin_url: typing.Optional[str] = None
|
||||
|
||||
#: Scroll offsets, set when this node is a Document.
|
||||
scroll_offset_x: typing.Optional[float] = None
|
||||
|
||||
scroll_offset_y: typing.Optional[float] = None
|
||||
|
||||
def to_json(self):
|
||||
json = dict()
|
||||
json['nodeType'] = self.node_type
|
||||
json['nodeName'] = self.node_name
|
||||
json['nodeValue'] = self.node_value
|
||||
json['backendNodeId'] = self.backend_node_id.to_json()
|
||||
if self.text_value is not None:
|
||||
json['textValue'] = self.text_value
|
||||
if self.input_value is not None:
|
||||
json['inputValue'] = self.input_value
|
||||
if self.input_checked is not None:
|
||||
json['inputChecked'] = self.input_checked
|
||||
if self.option_selected is not None:
|
||||
json['optionSelected'] = self.option_selected
|
||||
if self.child_node_indexes is not None:
|
||||
json['childNodeIndexes'] = [i for i in self.child_node_indexes]
|
||||
if self.attributes is not None:
|
||||
json['attributes'] = [i.to_json() for i in self.attributes]
|
||||
if self.pseudo_element_indexes is not None:
|
||||
json['pseudoElementIndexes'] = [i for i in self.pseudo_element_indexes]
|
||||
if self.layout_node_index is not None:
|
||||
json['layoutNodeIndex'] = self.layout_node_index
|
||||
if self.document_url is not None:
|
||||
json['documentURL'] = self.document_url
|
||||
if self.base_url is not None:
|
||||
json['baseURL'] = self.base_url
|
||||
if self.content_language is not None:
|
||||
json['contentLanguage'] = self.content_language
|
||||
if self.document_encoding is not None:
|
||||
json['documentEncoding'] = self.document_encoding
|
||||
if self.public_id is not None:
|
||||
json['publicId'] = self.public_id
|
||||
if self.system_id is not None:
|
||||
json['systemId'] = self.system_id
|
||||
if self.frame_id is not None:
|
||||
json['frameId'] = self.frame_id.to_json()
|
||||
if self.content_document_index is not None:
|
||||
json['contentDocumentIndex'] = self.content_document_index
|
||||
if self.pseudo_type is not None:
|
||||
json['pseudoType'] = self.pseudo_type.to_json()
|
||||
if self.shadow_root_type is not None:
|
||||
json['shadowRootType'] = self.shadow_root_type.to_json()
|
||||
if self.is_clickable is not None:
|
||||
json['isClickable'] = self.is_clickable
|
||||
if self.event_listeners is not None:
|
||||
json['eventListeners'] = [i.to_json() for i in self.event_listeners]
|
||||
if self.current_source_url is not None:
|
||||
json['currentSourceURL'] = self.current_source_url
|
||||
if self.origin_url is not None:
|
||||
json['originURL'] = self.origin_url
|
||||
if self.scroll_offset_x is not None:
|
||||
json['scrollOffsetX'] = self.scroll_offset_x
|
||||
if self.scroll_offset_y is not None:
|
||||
json['scrollOffsetY'] = self.scroll_offset_y
|
||||
return json
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, json):
|
||||
return cls(
|
||||
node_type=int(json['nodeType']),
|
||||
node_name=str(json['nodeName']),
|
||||
node_value=str(json['nodeValue']),
|
||||
backend_node_id=dom.BackendNodeId.from_json(json['backendNodeId']),
|
||||
text_value=str(json['textValue']) if 'textValue' in json else None,
|
||||
input_value=str(json['inputValue']) if 'inputValue' in json else None,
|
||||
input_checked=bool(json['inputChecked']) if 'inputChecked' in json else None,
|
||||
option_selected=bool(json['optionSelected']) if 'optionSelected' in json else None,
|
||||
child_node_indexes=[int(i) for i in json['childNodeIndexes']] if 'childNodeIndexes' in json else None,
|
||||
attributes=[NameValue.from_json(i) for i in json['attributes']] if 'attributes' in json else None,
|
||||
pseudo_element_indexes=[int(i) for i in json['pseudoElementIndexes']] if 'pseudoElementIndexes' in json else None,
|
||||
layout_node_index=int(json['layoutNodeIndex']) if 'layoutNodeIndex' in json else None,
|
||||
document_url=str(json['documentURL']) if 'documentURL' in json else None,
|
||||
base_url=str(json['baseURL']) if 'baseURL' in json else None,
|
||||
content_language=str(json['contentLanguage']) if 'contentLanguage' in json else None,
|
||||
document_encoding=str(json['documentEncoding']) if 'documentEncoding' in json else None,
|
||||
public_id=str(json['publicId']) if 'publicId' in json else None,
|
||||
system_id=str(json['systemId']) if 'systemId' in json else None,
|
||||
frame_id=page.FrameId.from_json(json['frameId']) if 'frameId' in json else None,
|
||||
content_document_index=int(json['contentDocumentIndex']) if 'contentDocumentIndex' in json else None,
|
||||
pseudo_type=dom.PseudoType.from_json(json['pseudoType']) if 'pseudoType' in json else None,
|
||||
shadow_root_type=dom.ShadowRootType.from_json(json['shadowRootType']) if 'shadowRootType' in json else None,
|
||||
is_clickable=bool(json['isClickable']) if 'isClickable' in json else None,
|
||||
event_listeners=[dom_debugger.EventListener.from_json(i) for i in json['eventListeners']] if 'eventListeners' in json else None,
|
||||
current_source_url=str(json['currentSourceURL']) if 'currentSourceURL' in json else None,
|
||||
origin_url=str(json['originURL']) if 'originURL' in json else None,
|
||||
scroll_offset_x=float(json['scrollOffsetX']) if 'scrollOffsetX' in json else None,
|
||||
scroll_offset_y=float(json['scrollOffsetY']) if 'scrollOffsetY' in json else None,
|
||||
)
|
||||
|
||||
|
||||
@dataclass
|
||||
class InlineTextBox:
|
||||
'''
|
||||
Details of post layout rendered text positions. The exact layout should not be regarded as
|
||||
stable and may change between versions.
|
||||
'''
|
||||
#: The bounding box in document coordinates. Note that scroll offset of the document is ignored.
|
||||
bounding_box: dom.Rect
|
||||
|
||||
#: The starting index in characters, for this post layout textbox substring. Characters that
|
||||
#: would be represented as a surrogate pair in UTF-16 have length 2.
|
||||
start_character_index: int
|
||||
|
||||
#: The number of characters in this post layout textbox substring. Characters that would be
|
||||
#: represented as a surrogate pair in UTF-16 have length 2.
|
||||
num_characters: int
|
||||
|
||||
def to_json(self):
|
||||
json = dict()
|
||||
json['boundingBox'] = self.bounding_box.to_json()
|
||||
json['startCharacterIndex'] = self.start_character_index
|
||||
json['numCharacters'] = self.num_characters
|
||||
return json
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, json):
|
||||
return cls(
|
||||
bounding_box=dom.Rect.from_json(json['boundingBox']),
|
||||
start_character_index=int(json['startCharacterIndex']),
|
||||
num_characters=int(json['numCharacters']),
|
||||
)
|
||||
|
||||
|
||||
@dataclass
|
||||
class LayoutTreeNode:
|
||||
'''
|
||||
Details of an element in the DOM tree with a LayoutObject.
|
||||
'''
|
||||
#: The index of the related DOM node in the ``domNodes`` array returned by ``getSnapshot``.
|
||||
dom_node_index: int
|
||||
|
||||
#: The bounding box in document coordinates. Note that scroll offset of the document is ignored.
|
||||
bounding_box: dom.Rect
|
||||
|
||||
#: Contents of the LayoutText, if any.
|
||||
layout_text: typing.Optional[str] = None
|
||||
|
||||
#: The post-layout inline text nodes, if any.
|
||||
inline_text_nodes: typing.Optional[typing.List[InlineTextBox]] = None
|
||||
|
||||
#: Index into the ``computedStyles`` array returned by ``getSnapshot``.
|
||||
style_index: typing.Optional[int] = None
|
||||
|
||||
#: Global paint order index, which is determined by the stacking order of the nodes. Nodes
|
||||
#: that are painted together will have the same index. Only provided if includePaintOrder in
|
||||
#: getSnapshot was true.
|
||||
paint_order: typing.Optional[int] = None
|
||||
|
||||
#: Set to true to indicate the element begins a new stacking context.
|
||||
is_stacking_context: typing.Optional[bool] = None
|
||||
|
||||
def to_json(self):
|
||||
json = dict()
|
||||
json['domNodeIndex'] = self.dom_node_index
|
||||
json['boundingBox'] = self.bounding_box.to_json()
|
||||
if self.layout_text is not None:
|
||||
json['layoutText'] = self.layout_text
|
||||
if self.inline_text_nodes is not None:
|
||||
json['inlineTextNodes'] = [i.to_json() for i in self.inline_text_nodes]
|
||||
if self.style_index is not None:
|
||||
json['styleIndex'] = self.style_index
|
||||
if self.paint_order is not None:
|
||||
json['paintOrder'] = self.paint_order
|
||||
if self.is_stacking_context is not None:
|
||||
json['isStackingContext'] = self.is_stacking_context
|
||||
return json
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, json):
|
||||
return cls(
|
||||
dom_node_index=int(json['domNodeIndex']),
|
||||
bounding_box=dom.Rect.from_json(json['boundingBox']),
|
||||
layout_text=str(json['layoutText']) if 'layoutText' in json else None,
|
||||
inline_text_nodes=[InlineTextBox.from_json(i) for i in json['inlineTextNodes']] if 'inlineTextNodes' in json else None,
|
||||
style_index=int(json['styleIndex']) if 'styleIndex' in json else None,
|
||||
paint_order=int(json['paintOrder']) if 'paintOrder' in json else None,
|
||||
is_stacking_context=bool(json['isStackingContext']) if 'isStackingContext' in json else None,
|
||||
)
|
||||
|
||||
|
||||
@dataclass
|
||||
class ComputedStyle:
|
||||
'''
|
||||
A subset of the full ComputedStyle as defined by the request whitelist.
|
||||
'''
|
||||
#: Name/value pairs of computed style properties.
|
||||
properties: typing.List[NameValue]
|
||||
|
||||
def to_json(self):
|
||||
json = dict()
|
||||
json['properties'] = [i.to_json() for i in self.properties]
|
||||
return json
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, json):
|
||||
return cls(
|
||||
properties=[NameValue.from_json(i) for i in json['properties']],
|
||||
)
|
||||
|
||||
|
||||
@dataclass
|
||||
class NameValue:
|
||||
'''
|
||||
A name/value pair.
|
||||
'''
|
||||
#: Attribute/property name.
|
||||
name: str
|
||||
|
||||
#: Attribute/property value.
|
||||
value: str
|
||||
|
||||
def to_json(self):
|
||||
json = dict()
|
||||
json['name'] = self.name
|
||||
json['value'] = self.value
|
||||
return json
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, json):
|
||||
return cls(
|
||||
name=str(json['name']),
|
||||
value=str(json['value']),
|
||||
)
|
||||
|
||||
|
||||
class StringIndex(int):
|
||||
'''
|
||||
Index of the string in the strings table.
|
||||
'''
|
||||
def to_json(self) -> int:
|
||||
return self
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, json: int) -> StringIndex:
|
||||
return cls(json)
|
||||
|
||||
def __repr__(self):
|
||||
return 'StringIndex({})'.format(super().__repr__())
|
||||
|
||||
|
||||
class ArrayOfStrings(list):
|
||||
'''
|
||||
Index of the string in the strings table.
|
||||
'''
|
||||
def to_json(self) -> typing.List[StringIndex]:
|
||||
return self
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, json: typing.List[StringIndex]) -> ArrayOfStrings:
|
||||
return cls(json)
|
||||
|
||||
def __repr__(self):
|
||||
return 'ArrayOfStrings({})'.format(super().__repr__())
|
||||
|
||||
|
||||
@dataclass
|
||||
class RareStringData:
|
||||
'''
|
||||
Data that is only present on rare nodes.
|
||||
'''
|
||||
index: typing.List[int]
|
||||
|
||||
value: typing.List[StringIndex]
|
||||
|
||||
def to_json(self):
|
||||
json = dict()
|
||||
json['index'] = [i for i in self.index]
|
||||
json['value'] = [i.to_json() for i in self.value]
|
||||
return json
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, json):
|
||||
return cls(
|
||||
index=[int(i) for i in json['index']],
|
||||
value=[StringIndex.from_json(i) for i in json['value']],
|
||||
)
|
||||
|
||||
|
||||
@dataclass
|
||||
class RareBooleanData:
|
||||
index: typing.List[int]
|
||||
|
||||
def to_json(self):
|
||||
json = dict()
|
||||
json['index'] = [i for i in self.index]
|
||||
return json
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, json):
|
||||
return cls(
|
||||
index=[int(i) for i in json['index']],
|
||||
)
|
||||
|
||||
|
||||
@dataclass
|
||||
class RareIntegerData:
|
||||
index: typing.List[int]
|
||||
|
||||
value: typing.List[int]
|
||||
|
||||
def to_json(self):
|
||||
json = dict()
|
||||
json['index'] = [i for i in self.index]
|
||||
json['value'] = [i for i in self.value]
|
||||
return json
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, json):
|
||||
return cls(
|
||||
index=[int(i) for i in json['index']],
|
||||
value=[int(i) for i in json['value']],
|
||||
)
|
||||
|
||||
|
||||
class Rectangle(list):
|
||||
def to_json(self) -> typing.List[float]:
|
||||
return self
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, json: typing.List[float]) -> Rectangle:
|
||||
return cls(json)
|
||||
|
||||
def __repr__(self):
|
||||
return 'Rectangle({})'.format(super().__repr__())
|
||||
|
||||
|
||||
@dataclass
|
||||
class DocumentSnapshot:
|
||||
'''
|
||||
Document snapshot.
|
||||
'''
|
||||
#: Document URL that ``Document`` or ``FrameOwner`` node points to.
|
||||
document_url: StringIndex
|
||||
|
||||
#: Document title.
|
||||
title: StringIndex
|
||||
|
||||
#: Base URL that ``Document`` or ``FrameOwner`` node uses for URL completion.
|
||||
base_url: StringIndex
|
||||
|
||||
#: Contains the document's content language.
|
||||
content_language: StringIndex
|
||||
|
||||
#: Contains the document's character set encoding.
|
||||
encoding_name: StringIndex
|
||||
|
||||
#: ``DocumentType`` node's publicId.
|
||||
public_id: StringIndex
|
||||
|
||||
#: ``DocumentType`` node's systemId.
|
||||
system_id: StringIndex
|
||||
|
||||
#: Frame ID for frame owner elements and also for the document node.
|
||||
frame_id: StringIndex
|
||||
|
||||
#: A table with dom nodes.
|
||||
nodes: NodeTreeSnapshot
|
||||
|
||||
#: The nodes in the layout tree.
|
||||
layout: LayoutTreeSnapshot
|
||||
|
||||
#: The post-layout inline text nodes.
|
||||
text_boxes: TextBoxSnapshot
|
||||
|
||||
#: Horizontal scroll offset.
|
||||
scroll_offset_x: typing.Optional[float] = None
|
||||
|
||||
#: Vertical scroll offset.
|
||||
scroll_offset_y: typing.Optional[float] = None
|
||||
|
||||
#: Document content width.
|
||||
content_width: typing.Optional[float] = None
|
||||
|
||||
#: Document content height.
|
||||
content_height: typing.Optional[float] = None
|
||||
|
||||
def to_json(self):
|
||||
json = dict()
|
||||
json['documentURL'] = self.document_url.to_json()
|
||||
json['title'] = self.title.to_json()
|
||||
json['baseURL'] = self.base_url.to_json()
|
||||
json['contentLanguage'] = self.content_language.to_json()
|
||||
json['encodingName'] = self.encoding_name.to_json()
|
||||
json['publicId'] = self.public_id.to_json()
|
||||
json['systemId'] = self.system_id.to_json()
|
||||
json['frameId'] = self.frame_id.to_json()
|
||||
json['nodes'] = self.nodes.to_json()
|
||||
json['layout'] = self.layout.to_json()
|
||||
json['textBoxes'] = self.text_boxes.to_json()
|
||||
if self.scroll_offset_x is not None:
|
||||
json['scrollOffsetX'] = self.scroll_offset_x
|
||||
if self.scroll_offset_y is not None:
|
||||
json['scrollOffsetY'] = self.scroll_offset_y
|
||||
if self.content_width is not None:
|
||||
json['contentWidth'] = self.content_width
|
||||
if self.content_height is not None:
|
||||
json['contentHeight'] = self.content_height
|
||||
return json
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, json):
|
||||
return cls(
|
||||
document_url=StringIndex.from_json(json['documentURL']),
|
||||
title=StringIndex.from_json(json['title']),
|
||||
base_url=StringIndex.from_json(json['baseURL']),
|
||||
content_language=StringIndex.from_json(json['contentLanguage']),
|
||||
encoding_name=StringIndex.from_json(json['encodingName']),
|
||||
public_id=StringIndex.from_json(json['publicId']),
|
||||
system_id=StringIndex.from_json(json['systemId']),
|
||||
frame_id=StringIndex.from_json(json['frameId']),
|
||||
nodes=NodeTreeSnapshot.from_json(json['nodes']),
|
||||
layout=LayoutTreeSnapshot.from_json(json['layout']),
|
||||
text_boxes=TextBoxSnapshot.from_json(json['textBoxes']),
|
||||
scroll_offset_x=float(json['scrollOffsetX']) if 'scrollOffsetX' in json else None,
|
||||
scroll_offset_y=float(json['scrollOffsetY']) if 'scrollOffsetY' in json else None,
|
||||
content_width=float(json['contentWidth']) if 'contentWidth' in json else None,
|
||||
content_height=float(json['contentHeight']) if 'contentHeight' in json else None,
|
||||
)
|
||||
|
||||
|
||||
@dataclass
|
||||
class NodeTreeSnapshot:
|
||||
'''
|
||||
Table containing nodes.
|
||||
'''
|
||||
#: Parent node index.
|
||||
parent_index: typing.Optional[typing.List[int]] = None
|
||||
|
||||
#: ``Node``'s nodeType.
|
||||
node_type: typing.Optional[typing.List[int]] = None
|
||||
|
||||
#: Type of the shadow root the ``Node`` is in. String values are equal to the ``ShadowRootType`` enum.
|
||||
shadow_root_type: typing.Optional[RareStringData] = None
|
||||
|
||||
#: ``Node``'s nodeName.
|
||||
node_name: typing.Optional[typing.List[StringIndex]] = None
|
||||
|
||||
#: ``Node``'s nodeValue.
|
||||
node_value: typing.Optional[typing.List[StringIndex]] = None
|
||||
|
||||
#: ``Node``'s id, corresponds to DOM.Node.backendNodeId.
|
||||
backend_node_id: typing.Optional[typing.List[dom.BackendNodeId]] = None
|
||||
|
||||
#: Attributes of an ``Element`` node. Flatten name, value pairs.
|
||||
attributes: typing.Optional[typing.List[ArrayOfStrings]] = None
|
||||
|
||||
#: Only set for textarea elements, contains the text value.
|
||||
text_value: typing.Optional[RareStringData] = None
|
||||
|
||||
#: Only set for input elements, contains the input's associated text value.
|
||||
input_value: typing.Optional[RareStringData] = None
|
||||
|
||||
#: Only set for radio and checkbox input elements, indicates if the element has been checked
|
||||
input_checked: typing.Optional[RareBooleanData] = None
|
||||
|
||||
#: Only set for option elements, indicates if the element has been selected
|
||||
option_selected: typing.Optional[RareBooleanData] = None
|
||||
|
||||
#: The index of the document in the list of the snapshot documents.
|
||||
content_document_index: typing.Optional[RareIntegerData] = None
|
||||
|
||||
#: Type of a pseudo element node.
|
||||
pseudo_type: typing.Optional[RareStringData] = None
|
||||
|
||||
#: Pseudo element identifier for this node. Only present if there is a
|
||||
#: valid pseudoType.
|
||||
pseudo_identifier: typing.Optional[RareStringData] = None
|
||||
|
||||
#: Whether this DOM node responds to mouse clicks. This includes nodes that have had click
|
||||
#: event listeners attached via JavaScript as well as anchor tags that naturally navigate when
|
||||
#: clicked.
|
||||
is_clickable: typing.Optional[RareBooleanData] = None
|
||||
|
||||
#: The selected url for nodes with a srcset attribute.
|
||||
current_source_url: typing.Optional[RareStringData] = None
|
||||
|
||||
#: The url of the script (if any) that generates this node.
|
||||
origin_url: typing.Optional[RareStringData] = None
|
||||
|
||||
def to_json(self):
|
||||
json = dict()
|
||||
if self.parent_index is not None:
|
||||
json['parentIndex'] = [i for i in self.parent_index]
|
||||
if self.node_type is not None:
|
||||
json['nodeType'] = [i for i in self.node_type]
|
||||
if self.shadow_root_type is not None:
|
||||
json['shadowRootType'] = self.shadow_root_type.to_json()
|
||||
if self.node_name is not None:
|
||||
json['nodeName'] = [i.to_json() for i in self.node_name]
|
||||
if self.node_value is not None:
|
||||
json['nodeValue'] = [i.to_json() for i in self.node_value]
|
||||
if self.backend_node_id is not None:
|
||||
json['backendNodeId'] = [i.to_json() for i in self.backend_node_id]
|
||||
if self.attributes is not None:
|
||||
json['attributes'] = [i.to_json() for i in self.attributes]
|
||||
if self.text_value is not None:
|
||||
json['textValue'] = self.text_value.to_json()
|
||||
if self.input_value is not None:
|
||||
json['inputValue'] = self.input_value.to_json()
|
||||
if self.input_checked is not None:
|
||||
json['inputChecked'] = self.input_checked.to_json()
|
||||
if self.option_selected is not None:
|
||||
json['optionSelected'] = self.option_selected.to_json()
|
||||
if self.content_document_index is not None:
|
||||
json['contentDocumentIndex'] = self.content_document_index.to_json()
|
||||
if self.pseudo_type is not None:
|
||||
json['pseudoType'] = self.pseudo_type.to_json()
|
||||
if self.pseudo_identifier is not None:
|
||||
json['pseudoIdentifier'] = self.pseudo_identifier.to_json()
|
||||
if self.is_clickable is not None:
|
||||
json['isClickable'] = self.is_clickable.to_json()
|
||||
if self.current_source_url is not None:
|
||||
json['currentSourceURL'] = self.current_source_url.to_json()
|
||||
if self.origin_url is not None:
|
||||
json['originURL'] = self.origin_url.to_json()
|
||||
return json
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, json):
|
||||
return cls(
|
||||
parent_index=[int(i) for i in json['parentIndex']] if 'parentIndex' in json else None,
|
||||
node_type=[int(i) for i in json['nodeType']] if 'nodeType' in json else None,
|
||||
shadow_root_type=RareStringData.from_json(json['shadowRootType']) if 'shadowRootType' in json else None,
|
||||
node_name=[StringIndex.from_json(i) for i in json['nodeName']] if 'nodeName' in json else None,
|
||||
node_value=[StringIndex.from_json(i) for i in json['nodeValue']] if 'nodeValue' in json else None,
|
||||
backend_node_id=[dom.BackendNodeId.from_json(i) for i in json['backendNodeId']] if 'backendNodeId' in json else None,
|
||||
attributes=[ArrayOfStrings.from_json(i) for i in json['attributes']] if 'attributes' in json else None,
|
||||
text_value=RareStringData.from_json(json['textValue']) if 'textValue' in json else None,
|
||||
input_value=RareStringData.from_json(json['inputValue']) if 'inputValue' in json else None,
|
||||
input_checked=RareBooleanData.from_json(json['inputChecked']) if 'inputChecked' in json else None,
|
||||
option_selected=RareBooleanData.from_json(json['optionSelected']) if 'optionSelected' in json else None,
|
||||
content_document_index=RareIntegerData.from_json(json['contentDocumentIndex']) if 'contentDocumentIndex' in json else None,
|
||||
pseudo_type=RareStringData.from_json(json['pseudoType']) if 'pseudoType' in json else None,
|
||||
pseudo_identifier=RareStringData.from_json(json['pseudoIdentifier']) if 'pseudoIdentifier' in json else None,
|
||||
is_clickable=RareBooleanData.from_json(json['isClickable']) if 'isClickable' in json else None,
|
||||
current_source_url=RareStringData.from_json(json['currentSourceURL']) if 'currentSourceURL' in json else None,
|
||||
origin_url=RareStringData.from_json(json['originURL']) if 'originURL' in json else None,
|
||||
)
|
||||
|
||||
|
||||
@dataclass
|
||||
class LayoutTreeSnapshot:
|
||||
'''
|
||||
Table of details of an element in the DOM tree with a LayoutObject.
|
||||
'''
|
||||
#: Index of the corresponding node in the ``NodeTreeSnapshot`` array returned by ``captureSnapshot``.
|
||||
node_index: typing.List[int]
|
||||
|
||||
#: Array of indexes specifying computed style strings, filtered according to the ``computedStyles`` parameter passed to ``captureSnapshot``.
|
||||
styles: typing.List[ArrayOfStrings]
|
||||
|
||||
#: The absolute position bounding box.
|
||||
bounds: typing.List[Rectangle]
|
||||
|
||||
#: Contents of the LayoutText, if any.
|
||||
text: typing.List[StringIndex]
|
||||
|
||||
#: Stacking context information.
|
||||
stacking_contexts: RareBooleanData
|
||||
|
||||
#: Global paint order index, which is determined by the stacking order of the nodes. Nodes
|
||||
#: that are painted together will have the same index. Only provided if includePaintOrder in
|
||||
#: captureSnapshot was true.
|
||||
paint_orders: typing.Optional[typing.List[int]] = None
|
||||
|
||||
#: The offset rect of nodes. Only available when includeDOMRects is set to true
|
||||
offset_rects: typing.Optional[typing.List[Rectangle]] = None
|
||||
|
||||
#: The scroll rect of nodes. Only available when includeDOMRects is set to true
|
||||
scroll_rects: typing.Optional[typing.List[Rectangle]] = None
|
||||
|
||||
#: The client rect of nodes. Only available when includeDOMRects is set to true
|
||||
client_rects: typing.Optional[typing.List[Rectangle]] = None
|
||||
|
||||
#: The list of background colors that are blended with colors of overlapping elements.
|
||||
blended_background_colors: typing.Optional[typing.List[StringIndex]] = None
|
||||
|
||||
#: The list of computed text opacities.
|
||||
text_color_opacities: typing.Optional[typing.List[float]] = None
|
||||
|
||||
def to_json(self):
|
||||
json = dict()
|
||||
json['nodeIndex'] = [i for i in self.node_index]
|
||||
json['styles'] = [i.to_json() for i in self.styles]
|
||||
json['bounds'] = [i.to_json() for i in self.bounds]
|
||||
json['text'] = [i.to_json() for i in self.text]
|
||||
json['stackingContexts'] = self.stacking_contexts.to_json()
|
||||
if self.paint_orders is not None:
|
||||
json['paintOrders'] = [i for i in self.paint_orders]
|
||||
if self.offset_rects is not None:
|
||||
json['offsetRects'] = [i.to_json() for i in self.offset_rects]
|
||||
if self.scroll_rects is not None:
|
||||
json['scrollRects'] = [i.to_json() for i in self.scroll_rects]
|
||||
if self.client_rects is not None:
|
||||
json['clientRects'] = [i.to_json() for i in self.client_rects]
|
||||
if self.blended_background_colors is not None:
|
||||
json['blendedBackgroundColors'] = [i.to_json() for i in self.blended_background_colors]
|
||||
if self.text_color_opacities is not None:
|
||||
json['textColorOpacities'] = [i for i in self.text_color_opacities]
|
||||
return json
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, json):
|
||||
return cls(
|
||||
node_index=[int(i) for i in json['nodeIndex']],
|
||||
styles=[ArrayOfStrings.from_json(i) for i in json['styles']],
|
||||
bounds=[Rectangle.from_json(i) for i in json['bounds']],
|
||||
text=[StringIndex.from_json(i) for i in json['text']],
|
||||
stacking_contexts=RareBooleanData.from_json(json['stackingContexts']),
|
||||
paint_orders=[int(i) for i in json['paintOrders']] if 'paintOrders' in json else None,
|
||||
offset_rects=[Rectangle.from_json(i) for i in json['offsetRects']] if 'offsetRects' in json else None,
|
||||
scroll_rects=[Rectangle.from_json(i) for i in json['scrollRects']] if 'scrollRects' in json else None,
|
||||
client_rects=[Rectangle.from_json(i) for i in json['clientRects']] if 'clientRects' in json else None,
|
||||
blended_background_colors=[StringIndex.from_json(i) for i in json['blendedBackgroundColors']] if 'blendedBackgroundColors' in json else None,
|
||||
text_color_opacities=[float(i) for i in json['textColorOpacities']] if 'textColorOpacities' in json else None,
|
||||
)
|
||||
|
||||
|
||||
@dataclass
|
||||
class TextBoxSnapshot:
|
||||
'''
|
||||
Table of details of the post layout rendered text positions. The exact layout should not be regarded as
|
||||
stable and may change between versions.
|
||||
'''
|
||||
#: Index of the layout tree node that owns this box collection.
|
||||
layout_index: typing.List[int]
|
||||
|
||||
#: The absolute position bounding box.
|
||||
bounds: typing.List[Rectangle]
|
||||
|
||||
#: The starting index in characters, for this post layout textbox substring. Characters that
|
||||
#: would be represented as a surrogate pair in UTF-16 have length 2.
|
||||
start: typing.List[int]
|
||||
|
||||
#: The number of characters in this post layout textbox substring. Characters that would be
|
||||
#: represented as a surrogate pair in UTF-16 have length 2.
|
||||
length: typing.List[int]
|
||||
|
||||
def to_json(self):
|
||||
json = dict()
|
||||
json['layoutIndex'] = [i for i in self.layout_index]
|
||||
json['bounds'] = [i.to_json() for i in self.bounds]
|
||||
json['start'] = [i for i in self.start]
|
||||
json['length'] = [i for i in self.length]
|
||||
return json
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, json):
|
||||
return cls(
|
||||
layout_index=[int(i) for i in json['layoutIndex']],
|
||||
bounds=[Rectangle.from_json(i) for i in json['bounds']],
|
||||
start=[int(i) for i in json['start']],
|
||||
length=[int(i) for i in json['length']],
|
||||
)
|
||||
|
||||
|
||||
def disable() -> typing.Generator[T_JSON_DICT,T_JSON_DICT,None]:
|
||||
'''
|
||||
Disables DOM snapshot agent for the given page.
|
||||
'''
|
||||
cmd_dict: T_JSON_DICT = {
|
||||
'method': 'DOMSnapshot.disable',
|
||||
}
|
||||
json = yield cmd_dict
|
||||
|
||||
|
||||
def enable() -> typing.Generator[T_JSON_DICT,T_JSON_DICT,None]:
|
||||
'''
|
||||
Enables DOM snapshot agent for the given page.
|
||||
'''
|
||||
cmd_dict: T_JSON_DICT = {
|
||||
'method': 'DOMSnapshot.enable',
|
||||
}
|
||||
json = yield cmd_dict
|
||||
|
||||
|
||||
def get_snapshot(
|
||||
computed_style_whitelist: typing.List[str],
|
||||
include_event_listeners: typing.Optional[bool] = None,
|
||||
include_paint_order: typing.Optional[bool] = None,
|
||||
include_user_agent_shadow_tree: typing.Optional[bool] = None
|
||||
) -> typing.Generator[T_JSON_DICT,T_JSON_DICT,typing.Tuple[typing.List[DOMNode], typing.List[LayoutTreeNode], typing.List[ComputedStyle]]]:
|
||||
'''
|
||||
Returns a document snapshot, including the full DOM tree of the root node (including iframes,
|
||||
template contents, and imported documents) in a flattened array, as well as layout and
|
||||
white-listed computed style information for the nodes. Shadow DOM in the returned DOM tree is
|
||||
flattened.
|
||||
|
||||
:param computed_style_whitelist: Whitelist of computed styles to return.
|
||||
:param include_event_listeners: *(Optional)* Whether or not to retrieve details of DOM listeners (default false).
|
||||
:param include_paint_order: *(Optional)* Whether to determine and include the paint order index of LayoutTreeNodes (default false).
|
||||
:param include_user_agent_shadow_tree: *(Optional)* Whether to include UA shadow tree in the snapshot (default false).
|
||||
:returns: A tuple with the following items:
|
||||
|
||||
0. **domNodes** - The nodes in the DOM tree. The DOMNode at index 0 corresponds to the root document.
|
||||
1. **layoutTreeNodes** - The nodes in the layout tree.
|
||||
2. **computedStyles** - Whitelisted ComputedStyle properties for each node in the layout tree.
|
||||
'''
|
||||
params: T_JSON_DICT = dict()
|
||||
params['computedStyleWhitelist'] = [i for i in computed_style_whitelist]
|
||||
if include_event_listeners is not None:
|
||||
params['includeEventListeners'] = include_event_listeners
|
||||
if include_paint_order is not None:
|
||||
params['includePaintOrder'] = include_paint_order
|
||||
if include_user_agent_shadow_tree is not None:
|
||||
params['includeUserAgentShadowTree'] = include_user_agent_shadow_tree
|
||||
cmd_dict: T_JSON_DICT = {
|
||||
'method': 'DOMSnapshot.getSnapshot',
|
||||
'params': params,
|
||||
}
|
||||
json = yield cmd_dict
|
||||
return (
|
||||
[DOMNode.from_json(i) for i in json['domNodes']],
|
||||
[LayoutTreeNode.from_json(i) for i in json['layoutTreeNodes']],
|
||||
[ComputedStyle.from_json(i) for i in json['computedStyles']]
|
||||
)
|
||||
|
||||
|
||||
def capture_snapshot(
|
||||
computed_styles: typing.List[str],
|
||||
include_paint_order: typing.Optional[bool] = None,
|
||||
include_dom_rects: typing.Optional[bool] = None,
|
||||
include_blended_background_colors: typing.Optional[bool] = None,
|
||||
include_text_color_opacities: typing.Optional[bool] = None
|
||||
) -> typing.Generator[T_JSON_DICT,T_JSON_DICT,typing.Tuple[typing.List[DocumentSnapshot], typing.List[str]]]:
|
||||
'''
|
||||
Returns a document snapshot, including the full DOM tree of the root node (including iframes,
|
||||
template contents, and imported documents) in a flattened array, as well as layout and
|
||||
white-listed computed style information for the nodes. Shadow DOM in the returned DOM tree is
|
||||
flattened.
|
||||
|
||||
:param computed_styles: Whitelist of computed styles to return.
|
||||
:param include_paint_order: *(Optional)* Whether to include layout object paint orders into the snapshot.
|
||||
:param include_dom_rects: *(Optional)* Whether to include DOM rectangles (offsetRects, clientRects, scrollRects) into the snapshot
|
||||
:param include_blended_background_colors: **(EXPERIMENTAL)** *(Optional)* Whether to include blended background colors in the snapshot (default: false). Blended background color is achieved by blending background colors of all elements that overlap with the current element.
|
||||
:param include_text_color_opacities: **(EXPERIMENTAL)** *(Optional)* Whether to include text color opacity in the snapshot (default: false). An element might have the opacity property set that affects the text color of the element. The final text color opacity is computed based on the opacity of all overlapping elements.
|
||||
:returns: A tuple with the following items:
|
||||
|
||||
0. **documents** - The nodes in the DOM tree. The DOMNode at index 0 corresponds to the root document.
|
||||
1. **strings** - Shared string table that all string properties refer to with indexes.
|
||||
'''
|
||||
params: T_JSON_DICT = dict()
|
||||
params['computedStyles'] = [i for i in computed_styles]
|
||||
if include_paint_order is not None:
|
||||
params['includePaintOrder'] = include_paint_order
|
||||
if include_dom_rects is not None:
|
||||
params['includeDOMRects'] = include_dom_rects
|
||||
if include_blended_background_colors is not None:
|
||||
params['includeBlendedBackgroundColors'] = include_blended_background_colors
|
||||
if include_text_color_opacities is not None:
|
||||
params['includeTextColorOpacities'] = include_text_color_opacities
|
||||
cmd_dict: T_JSON_DICT = {
|
||||
'method': 'DOMSnapshot.captureSnapshot',
|
||||
'params': params,
|
||||
}
|
||||
json = yield cmd_dict
|
||||
return (
|
||||
[DocumentSnapshot.from_json(i) for i in json['documents']],
|
||||
[str(i) for i in json['strings']]
|
||||
)
|
@ -0,0 +1,220 @@
|
||||
# DO NOT EDIT THIS FILE!
|
||||
#
|
||||
# This file is generated from the CDP specification. If you need to make
|
||||
# changes, edit the generator and regenerate all of the modules.
|
||||
#
|
||||
# CDP domain: DOMStorage (experimental)
|
||||
from __future__ import annotations
|
||||
from .util import event_class, T_JSON_DICT
|
||||
from dataclasses import dataclass
|
||||
import enum
|
||||
import typing
|
||||
|
||||
class SerializedStorageKey(str):
|
||||
def to_json(self) -> str:
|
||||
return self
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, json: str) -> SerializedStorageKey:
|
||||
return cls(json)
|
||||
|
||||
def __repr__(self):
|
||||
return 'SerializedStorageKey({})'.format(super().__repr__())
|
||||
|
||||
|
||||
@dataclass
|
||||
class StorageId:
|
||||
'''
|
||||
DOM Storage identifier.
|
||||
'''
|
||||
#: Whether the storage is local storage (not session storage).
|
||||
is_local_storage: bool
|
||||
|
||||
#: Security origin for the storage.
|
||||
security_origin: typing.Optional[str] = None
|
||||
|
||||
#: Represents a key by which DOM Storage keys its CachedStorageAreas
|
||||
storage_key: typing.Optional[SerializedStorageKey] = None
|
||||
|
||||
def to_json(self):
|
||||
json = dict()
|
||||
json['isLocalStorage'] = self.is_local_storage
|
||||
if self.security_origin is not None:
|
||||
json['securityOrigin'] = self.security_origin
|
||||
if self.storage_key is not None:
|
||||
json['storageKey'] = self.storage_key.to_json()
|
||||
return json
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, json):
|
||||
return cls(
|
||||
is_local_storage=bool(json['isLocalStorage']),
|
||||
security_origin=str(json['securityOrigin']) if 'securityOrigin' in json else None,
|
||||
storage_key=SerializedStorageKey.from_json(json['storageKey']) if 'storageKey' in json else None,
|
||||
)
|
||||
|
||||
|
||||
class Item(list):
|
||||
'''
|
||||
DOM Storage item.
|
||||
'''
|
||||
def to_json(self) -> typing.List[str]:
|
||||
return self
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, json: typing.List[str]) -> Item:
|
||||
return cls(json)
|
||||
|
||||
def __repr__(self):
|
||||
return 'Item({})'.format(super().__repr__())
|
||||
|
||||
|
||||
def clear(
|
||||
storage_id: StorageId
|
||||
) -> typing.Generator[T_JSON_DICT,T_JSON_DICT,None]:
|
||||
'''
|
||||
:param storage_id:
|
||||
'''
|
||||
params: T_JSON_DICT = dict()
|
||||
params['storageId'] = storage_id.to_json()
|
||||
cmd_dict: T_JSON_DICT = {
|
||||
'method': 'DOMStorage.clear',
|
||||
'params': params,
|
||||
}
|
||||
json = yield cmd_dict
|
||||
|
||||
|
||||
def disable() -> typing.Generator[T_JSON_DICT,T_JSON_DICT,None]:
|
||||
'''
|
||||
Disables storage tracking, prevents storage events from being sent to the client.
|
||||
'''
|
||||
cmd_dict: T_JSON_DICT = {
|
||||
'method': 'DOMStorage.disable',
|
||||
}
|
||||
json = yield cmd_dict
|
||||
|
||||
|
||||
def enable() -> typing.Generator[T_JSON_DICT,T_JSON_DICT,None]:
|
||||
'''
|
||||
Enables storage tracking, storage events will now be delivered to the client.
|
||||
'''
|
||||
cmd_dict: T_JSON_DICT = {
|
||||
'method': 'DOMStorage.enable',
|
||||
}
|
||||
json = yield cmd_dict
|
||||
|
||||
|
||||
def get_dom_storage_items(
|
||||
storage_id: StorageId
|
||||
) -> typing.Generator[T_JSON_DICT,T_JSON_DICT,typing.List[Item]]:
|
||||
'''
|
||||
:param storage_id:
|
||||
:returns:
|
||||
'''
|
||||
params: T_JSON_DICT = dict()
|
||||
params['storageId'] = storage_id.to_json()
|
||||
cmd_dict: T_JSON_DICT = {
|
||||
'method': 'DOMStorage.getDOMStorageItems',
|
||||
'params': params,
|
||||
}
|
||||
json = yield cmd_dict
|
||||
return [Item.from_json(i) for i in json['entries']]
|
||||
|
||||
|
||||
def remove_dom_storage_item(
|
||||
storage_id: StorageId,
|
||||
key: str
|
||||
) -> typing.Generator[T_JSON_DICT,T_JSON_DICT,None]:
|
||||
'''
|
||||
:param storage_id:
|
||||
:param key:
|
||||
'''
|
||||
params: T_JSON_DICT = dict()
|
||||
params['storageId'] = storage_id.to_json()
|
||||
params['key'] = key
|
||||
cmd_dict: T_JSON_DICT = {
|
||||
'method': 'DOMStorage.removeDOMStorageItem',
|
||||
'params': params,
|
||||
}
|
||||
json = yield cmd_dict
|
||||
|
||||
|
||||
def set_dom_storage_item(
|
||||
storage_id: StorageId,
|
||||
key: str,
|
||||
value: str
|
||||
) -> typing.Generator[T_JSON_DICT,T_JSON_DICT,None]:
|
||||
'''
|
||||
:param storage_id:
|
||||
:param key:
|
||||
:param value:
|
||||
'''
|
||||
params: T_JSON_DICT = dict()
|
||||
params['storageId'] = storage_id.to_json()
|
||||
params['key'] = key
|
||||
params['value'] = value
|
||||
cmd_dict: T_JSON_DICT = {
|
||||
'method': 'DOMStorage.setDOMStorageItem',
|
||||
'params': params,
|
||||
}
|
||||
json = yield cmd_dict
|
||||
|
||||
|
||||
@event_class('DOMStorage.domStorageItemAdded')
|
||||
@dataclass
|
||||
class DomStorageItemAdded:
|
||||
storage_id: StorageId
|
||||
key: str
|
||||
new_value: str
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, json: T_JSON_DICT) -> DomStorageItemAdded:
|
||||
return cls(
|
||||
storage_id=StorageId.from_json(json['storageId']),
|
||||
key=str(json['key']),
|
||||
new_value=str(json['newValue'])
|
||||
)
|
||||
|
||||
|
||||
@event_class('DOMStorage.domStorageItemRemoved')
|
||||
@dataclass
|
||||
class DomStorageItemRemoved:
|
||||
storage_id: StorageId
|
||||
key: str
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, json: T_JSON_DICT) -> DomStorageItemRemoved:
|
||||
return cls(
|
||||
storage_id=StorageId.from_json(json['storageId']),
|
||||
key=str(json['key'])
|
||||
)
|
||||
|
||||
|
||||
@event_class('DOMStorage.domStorageItemUpdated')
|
||||
@dataclass
|
||||
class DomStorageItemUpdated:
|
||||
storage_id: StorageId
|
||||
key: str
|
||||
old_value: str
|
||||
new_value: str
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, json: T_JSON_DICT) -> DomStorageItemUpdated:
|
||||
return cls(
|
||||
storage_id=StorageId.from_json(json['storageId']),
|
||||
key=str(json['key']),
|
||||
old_value=str(json['oldValue']),
|
||||
new_value=str(json['newValue'])
|
||||
)
|
||||
|
||||
|
||||
@event_class('DOMStorage.domStorageItemsCleared')
|
||||
@dataclass
|
||||
class DomStorageItemsCleared:
|
||||
storage_id: StorageId
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, json: T_JSON_DICT) -> DomStorageItemsCleared:
|
||||
return cls(
|
||||
storage_id=StorageId.from_json(json['storageId'])
|
||||
)
|
1195
lib/python3.13/site-packages/selenium/webdriver/common/devtools/v128/emulation.py
Executable file
1195
lib/python3.13/site-packages/selenium/webdriver/common/devtools/v128/emulation.py
Executable file
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user