├── .coveragerc ├── .flake8 ├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── cripy ├── __init__.py ├── cdp.py ├── cdp_result_future.py ├── cdp_session.py ├── client.py ├── connection.py ├── errors.py ├── events.py ├── protocol │ ├── __init__.py │ ├── accessibility.py │ ├── animation.py │ ├── applicationcache.py │ ├── audits.py │ ├── backgroundservice.py │ ├── browser.py │ ├── cachestorage.py │ ├── cast.py │ ├── console.py │ ├── css.py │ ├── database.py │ ├── debugger.py │ ├── deviceorientation.py │ ├── dom.py │ ├── domdebugger.py │ ├── domsnapshot.py │ ├── domstorage.py │ ├── emulation.py │ ├── fetch.py │ ├── headlessexperimental.py │ ├── heapprofiler.py │ ├── indexeddb.py │ ├── input.py │ ├── inspector.py │ ├── io.py │ ├── layertree.py │ ├── log.py │ ├── memory.py │ ├── network.py │ ├── overlay.py │ ├── page.py │ ├── performance.py │ ├── profiler.py │ ├── runtime.py │ ├── schema.py │ ├── security.py │ ├── serviceworker.py │ ├── storage.py │ ├── systeminfo.py │ ├── target.py │ ├── testing.py │ ├── tethering.py │ ├── tracing.py │ └── webaudio.py ├── protogen │ ├── __init__.py │ ├── cdp.py │ └── generate.py ├── target_session.py └── templates │ ├── __init__.py │ ├── full │ ├── commands.async.py.j2 │ ├── commands.py.j2 │ ├── domain_init.py.j2 │ ├── domain_type.py.j2 │ ├── domain_typent.py.j2 │ ├── events.py.j2 │ ├── eventsnt.py.j2 │ └── protocol_init.py.j2 │ └── simple │ ├── commands.async.py.j2 │ ├── commands.py.j2 │ └── protocol_init.py.j2 ├── data ├── 70.0.3528.4-protocol.json ├── browser_protocol.json ├── js_protocol.json └── protocol.json ├── dev-requirements.txt ├── generate_protocol.py ├── launch_chrome_dev.sh ├── mypy.ini ├── requirements.txt ├── setup.py ├── tests ├── __init__.py ├── conftest.py ├── helpers │ ├── __init__.py │ ├── chrome.py │ └── utils.py ├── test_cdp.py ├── test_connect.py └── test_events.py └── update_protocol.py /.coveragerc: -------------------------------------------------------------------------------- 1 | [run] 2 | omit = 3 | cripy/protocol/*.py 4 | 5 | [report] 6 | exclude_lines = 7 | # Have to re-enable the standard pragma 8 | pragma: no cover 9 | 10 | # Don't complain if tests don't hit defensive code: 11 | raise ClientError 12 | raise NetworkError 13 | if loop is None 14 | loop = asyncio\.get_event_loop\(\) 15 | if not self\._connection: 16 | if not (cb|callback)\.done\(\): 17 | (cb|callback)\.set_exception\( 18 | 19 | 20 | # Don't complain about missing debug-only code: 21 | def __repr__ 22 | def __str__ 23 | 24 | # Don't complain about utility code 25 | def loop -------------------------------------------------------------------------------- /.flake8: -------------------------------------------------------------------------------- 1 | # This is an example .flake8 config, used when developing *Black* itself. 2 | # Keep in sync with setup.cfg which is used for source packages. 3 | 4 | [flake8] 5 | ignore = E203, E266, E501, W503 6 | max-line-length = 88 7 | max-complexity = 18 8 | select = B,C,E,F,W,T4,B950 9 | exclude = .git, 10 | __pycache__, 11 | .mypy_cache, 12 | .pytest_cache, 13 | venv, 14 | tests, 15 | cripy/protocol, 16 | build 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### JetBrains template 3 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm 4 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 5 | 6 | # User-specific stuff 7 | .idea/**/workspace.xml 8 | .idea/**/tasks.xml 9 | .idea/**/dictionaries 10 | .idea/**/shelf 11 | 12 | # Sensitive or high-churn files 13 | .idea/**/dataSources/ 14 | .idea/**/dataSources.ids 15 | .idea/**/dataSources.local.xml 16 | .idea/**/sqlDataSources.xml 17 | .idea/**/dynamic.xml 18 | .idea/**/uiDesigner.xml 19 | .idea/**/dbnavigator.xml 20 | 21 | # Gradle 22 | .idea/**/gradle.xml 23 | .idea/**/libraries 24 | 25 | # CMake 26 | cmake-build-debug/ 27 | cmake-build-release/ 28 | 29 | # Mongo Explorer plugin 30 | .idea/**/mongoSettings.xml 31 | 32 | # File-based project format 33 | *.iws 34 | 35 | # IntelliJ 36 | out/ 37 | 38 | # mpeltonen/sbt-idea plugin 39 | .idea_modules/ 40 | 41 | # JIRA plugin 42 | atlassian-ide-plugin.xml 43 | 44 | # Cursive Clojure plugin 45 | .idea/replstate.xml 46 | 47 | # Crashlytics plugin (for Android Studio and IntelliJ) 48 | com_crashlytics_export_strings.xml 49 | crashlytics.properties 50 | crashlytics-build.properties 51 | fabric.properties 52 | 53 | # Editor-based Rest Client 54 | .idea/httpRequests 55 | ### Python template 56 | # Byte-compiled / optimized / DLL files 57 | __pycache__/ 58 | *.py[cod] 59 | *$py.class 60 | 61 | # C extensions 62 | *.so 63 | 64 | # Distribution / packaging 65 | .Python 66 | build/ 67 | develop-eggs/ 68 | dist/ 69 | downloads/ 70 | eggs/ 71 | .eggs/ 72 | lib/ 73 | lib64/ 74 | parts/ 75 | sdist/ 76 | var/ 77 | wheels/ 78 | *.egg-info/ 79 | .installed.cfg 80 | *.egg 81 | MANIFEST 82 | 83 | # PyInstaller 84 | # Usually these files are written by a python script from a template 85 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 86 | *.manifest 87 | *.spec 88 | 89 | # Installer logs 90 | pip-log.txt 91 | pip-delete-this-directory.txt 92 | 93 | # Unit test / coverage reports 94 | htmlcov/ 95 | .tox/ 96 | .coverage 97 | .coverage.* 98 | .cache 99 | nosetests.xml 100 | coverage.xml 101 | *.cover 102 | .hypothesis/ 103 | .pytest_cache/ 104 | 105 | # Translations 106 | *.mo 107 | *.pot 108 | 109 | # Django stuff: 110 | *.log 111 | local_settings.py 112 | db.sqlite3 113 | 114 | # Flask stuff: 115 | instance/ 116 | .webassets-cache 117 | 118 | # Scrapy stuff: 119 | .scrapy 120 | 121 | # Sphinx documentation 122 | docs/_build/ 123 | 124 | # PyBuilder 125 | target/ 126 | !cripy/asyncio/protocol/target/ 127 | !cripy/gevent/protocol/target/ 128 | # Jupyter Notebook 129 | .ipynb_checkpoints 130 | 131 | # pyenv 132 | .python-version 133 | 134 | # celery beat schedule file 135 | celerybeat-schedule 136 | 137 | # SageMath parsed files 138 | *.sage.py 139 | 140 | # Environments 141 | .env 142 | .venv 143 | env/ 144 | venv/ 145 | ENV/ 146 | env.bak/ 147 | venv.bak/ 148 | 149 | # Spyder project settings 150 | .spyderproject 151 | .spyproject 152 | 153 | # Rope project settings 154 | .ropeproject 155 | 156 | # mkdocs documentation 157 | /site 158 | 159 | # mypy 160 | .mypy_cache/ 161 | ### Example user template template 162 | ### Example user template 163 | 164 | # IntelliJ project files 165 | .idea 166 | *.iml 167 | out 168 | gen 169 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | dist: xenial 2 | sudo: required 3 | language: python 4 | python: 5 | - "3.6" 6 | - "3.7" 7 | addons: 8 | chrome: stable 9 | before_install: 10 | - "sysctl kernel.unprivileged_userns_clone=1" 11 | install: 12 | - pip install --upgrade -r requirements.txt -r dev-requirements.txt 13 | script: pytest -v 14 | -------------------------------------------------------------------------------- /cripy/__init__.py: -------------------------------------------------------------------------------- 1 | from typing import Union 2 | 3 | from .cdp import CDP, DEFAULT_HOST, DEFAULT_PORT, DEFAULT_URL, connect 4 | from .cdp_session import CDPSession 5 | from .client import Client, ClientDynamic 6 | from .connection import Connection 7 | from .errors import ClientError, NetworkError, ProtocolError 8 | from .events import ConnectionEvents, SessionEvents 9 | from .target_session import TargetSession, TargetSessionDynamic 10 | 11 | ConnectionType = Union[Client, Connection, ClientDynamic] 12 | SessionType = Union[TargetSession, CDPSession, TargetSessionDynamic] 13 | 14 | __all__ = [ 15 | "CDP", 16 | "CDPSession", 17 | "Client", 18 | "ClientDynamic", 19 | "ClientError", 20 | "connect", 21 | "Connection", 22 | "ConnectionEvents", 23 | "ConnectionType", 24 | "DEFAULT_HOST", 25 | "DEFAULT_PORT", 26 | "DEFAULT_URL", 27 | "NetworkError", 28 | "ProtocolError", 29 | "SessionEvents", 30 | "SessionType", 31 | "TargetSession", 32 | "TargetSessionDynamic", 33 | ] 34 | -------------------------------------------------------------------------------- /cripy/cdp_result_future.py: -------------------------------------------------------------------------------- 1 | from asyncio import AbstractEventLoop, Future 2 | from typing import Optional 3 | 4 | __all__ = ["CDPResultFuture"] 5 | 6 | 7 | class CDPResultFuture(Future): 8 | """A subclass of asyncio.Future to make linting happy about adding method to it""" 9 | 10 | def __init__(self, method: str, loop: Optional[AbstractEventLoop] = None) -> None: 11 | super().__init__(loop=loop) 12 | self.method: str = method 13 | -------------------------------------------------------------------------------- /cripy/cdp_session.py: -------------------------------------------------------------------------------- 1 | from asyncio import AbstractEventLoop, get_event_loop 2 | from typing import ClassVar, Dict, Optional, TYPE_CHECKING, Type, Union 3 | 4 | from ujson import dumps, loads 5 | from pyee2 import EventEmitterS 6 | 7 | from .cdp_result_future import CDPResultFuture 8 | from .errors import NetworkError, create_protocol_error 9 | from .events import SessionEvents 10 | 11 | if TYPE_CHECKING: # pragma: no cover 12 | from cripy import ConnectionType, SessionType # noqa: F401 13 | 14 | 15 | class CDPSession(EventEmitterS): 16 | __slots__ = [ 17 | "_lastId", 18 | "_connection", 19 | "_target_type", 20 | "_session_id", 21 | "_flat_session", 22 | "_callbacks", 23 | "_sessions", 24 | ] 25 | 26 | Events: ClassVar[Type[SessionEvents]] = SessionEvents 27 | 28 | def __init__( 29 | self, 30 | connection: Union["ConnectionType", "SessionType"], 31 | target_type: str, 32 | session_id: str, 33 | flat_session: bool = False, 34 | ) -> None: 35 | """Make new session 36 | 37 | :param connection: The connection to use 38 | :param target_type: The type of the target connected to 39 | :param session_id: The id of the session being connected to 40 | :param flat_session: Should any sessions created from this session 41 | using flat session mode 42 | """ 43 | _loop: AbstractEventLoop = ( 44 | connection.loop if connection.loop is not None else get_event_loop() 45 | ) # pragma: no cover 46 | super().__init__(loop=_loop) 47 | self._lastId: int = 0 48 | self._connection: Union["ConnectionType", "SessionType"] = connection 49 | self._target_type: str = target_type 50 | self._session_id: str = session_id 51 | self._flat_session: bool = flat_session 52 | self._callbacks: Dict[int, CDPResultFuture] = {} 53 | self._sessions: Dict[str, SessionType] = {} 54 | 55 | @property 56 | def loop(self) -> AbstractEventLoop: 57 | """Returns the instance of event loop""" 58 | return self._loop 59 | 60 | @property 61 | def flat_session(self) -> bool: 62 | """Returns T/F indicating if flat session mode is enabled""" 63 | return self._flat_session 64 | 65 | @property 66 | def session_id(self) -> str: 67 | """Returns the id of the session""" 68 | return self._session_id 69 | 70 | @property 71 | def target_type(self) -> str: 72 | """Returns the type of the target""" 73 | return self._target_type 74 | 75 | def send(self, method: str, params: Optional[Dict] = None) -> CDPResultFuture: 76 | """Send message to the connected session. 77 | 78 | :param method: Protocol method name 79 | :param params: Optional method parameters 80 | :return: A future that resolves once a response has been received 81 | """ 82 | if not self._connection: # pragma: no cover 83 | raise NetworkError( 84 | f"Protocol Error ({method}): Session closed. Most likely the " 85 | f"target {self._target_type} has been closed." 86 | ) 87 | if params is None: 88 | params = {} 89 | if self._flat_session: 90 | _id = self._connection._raw_send( 91 | {"method": method, "params": params, "sessionId": self.session_id} 92 | ) 93 | callback = CDPResultFuture(method, self._loop) 94 | self._callbacks[_id] = callback 95 | return callback 96 | self._lastId += 1 97 | _id = self._lastId 98 | msg = dumps({"id": _id, "method": method, "params": params}) 99 | callback = CDPResultFuture(method, self._loop) 100 | self._callbacks[_id] = callback 101 | self._connection.send( 102 | "Target.sendMessageToTarget", 103 | {"sessionId": self._session_id, "message": msg}, 104 | ) 105 | return callback 106 | 107 | async def detach(self) -> None: 108 | """Detach session from target. Once detached, session won't emit any events and 109 | can't be used to send messages. 110 | """ 111 | if not self._connection: 112 | raise NetworkError(f"CDPSession for {self._target_type} already closed.") 113 | await self._connection.send( 114 | "Target.detachFromTarget", {"sessionId": self._session_id} 115 | ) 116 | 117 | def create_session(self, target_type: str, session_id: str) -> "CDPSession": 118 | """Creates a new session for the target being connected to specified 119 | by the session_id 120 | 121 | :param target_type: The type of the target being connected to 122 | :param session_id: The session id used to communicate to the target 123 | :return: A new session connected to the target 124 | """ 125 | connection = self._connection if self._flat_session else self 126 | session = CDPSession( 127 | connection, target_type, session_id, flat_session=self._flat_session 128 | ) 129 | if self._flat_session: 130 | self._connection.add_session(session) 131 | else: 132 | self._sessions[session_id] = session 133 | return session 134 | 135 | def on_message(self, maybe_str_or_dict: Union[str, Dict]) -> None: 136 | """Handles the recite of a message. Depending on if flat session 137 | mode is enabled the message supplied to this method will be either 138 | a string (non-flat more) or a dict (flat mode). 139 | 140 | :param maybe_str_or_dict: The message received 141 | """ 142 | if isinstance(maybe_str_or_dict, str): 143 | obj = loads(maybe_str_or_dict) 144 | else: 145 | obj = maybe_str_or_dict 146 | _id = obj.get("id") 147 | if _id and _id in self._callbacks: 148 | callback = self._callbacks.pop(_id) 149 | if "error" in obj: 150 | # Checking state of the future object. 151 | # It can be canceled. 152 | if callback and not callback.done(): 153 | callback.set_exception(create_protocol_error(callback.method, obj)) 154 | else: 155 | result = obj.get("result") 156 | if callback and not callback.done(): 157 | callback.set_result(result) 158 | return 159 | method = obj.get("method") 160 | params = obj.get("params") 161 | if not self._flat_session: 162 | if method == "Target.receivedMessageFromTarget": 163 | session = self._sessions.get(params.get("sessionId")) 164 | if session is not None: 165 | session.on_message(params.get("message")) 166 | return 167 | if method == "Target.detachedFromTarget": 168 | session_id = params.get("sessionId") 169 | session = self._sessions.get(session_id) 170 | if session is not None: 171 | session.on_closed() 172 | del self._sessions[session_id] 173 | return 174 | self.emit(method, params) 175 | 176 | def on_closed(self) -> None: 177 | """Close this session""" 178 | for cb in self._callbacks.values(): 179 | if not cb.done(): 180 | cb.set_exception( 181 | NetworkError( 182 | f"Network error {cb.method}: {self._target_type} closed." 183 | ) 184 | ) 185 | self._callbacks.clear() 186 | for session in self._sessions.values(): 187 | session.on_closed() 188 | self._sessions.clear() 189 | self._connection = None 190 | self.emit(SessionEvents.Disconnected) 191 | 192 | def __str__(self) -> str: 193 | return f"{self.__class__.__name__}(target={self._target_type}, sessionId={self._session_id})" 194 | 195 | def __repr__(self) -> str: 196 | return self.__str__() 197 | -------------------------------------------------------------------------------- /cripy/errors.py: -------------------------------------------------------------------------------- 1 | from typing import Dict 2 | 3 | __all__ = ["ClientError", "NetworkError", "ProtocolError"] 4 | 5 | 6 | class NetworkError(Exception): 7 | """Network related exception.""" 8 | 9 | 10 | class ClientError(Exception): 11 | """Client specific exception.""" 12 | 13 | 14 | class ProtocolError(Exception): 15 | """Exception used to indicate that a CDP command has received an error""" 16 | 17 | 18 | def create_protocol_error(method: str, msg: Dict) -> ProtocolError: 19 | error = msg["error"] 20 | data = error.get("data") 21 | data_m = f" {data}" if data is not None else "" 22 | return ProtocolError(f"Protocol Error ({method}): {error.get('message')}{data_m}") 23 | -------------------------------------------------------------------------------- /cripy/events.py: -------------------------------------------------------------------------------- 1 | from typing import ClassVar 2 | 3 | __all__ = ["ConnectionEvents", "SessionEvents"] 4 | 5 | 6 | class ConnectionEvents: 7 | Disconnected: ClassVar[str] = "Connection.Disconnected" 8 | Ready: ClassVar[str] = "Connection.Ready" 9 | AllMessages: ClassVar[str] = "Connection.AllMessages" 10 | 11 | 12 | class SessionEvents: 13 | Disconnected: ClassVar[str] = "Session.Disconnected" 14 | -------------------------------------------------------------------------------- /cripy/protocol/__init__.py: -------------------------------------------------------------------------------- 1 | from .accessibility import Accessibility 2 | from .animation import Animation 3 | from .applicationcache import ApplicationCache 4 | from .audits import Audits 5 | from .backgroundservice import BackgroundService 6 | from .browser import Browser 7 | from .css import CSS 8 | from .cachestorage import CacheStorage 9 | from .cast import Cast 10 | from .console import Console 11 | from .dom import DOM 12 | from .domdebugger import DOMDebugger 13 | from .domsnapshot import DOMSnapshot 14 | from .domstorage import DOMStorage 15 | from .database import Database 16 | from .debugger import Debugger 17 | from .deviceorientation import DeviceOrientation 18 | from .emulation import Emulation 19 | from .fetch import Fetch 20 | from .headlessexperimental import HeadlessExperimental 21 | from .heapprofiler import HeapProfiler 22 | from .io import IO 23 | from .indexeddb import IndexedDB 24 | from .input import Input 25 | from .inspector import Inspector 26 | from .layertree import LayerTree 27 | from .log import Log 28 | from .memory import Memory 29 | from .network import Network 30 | from .overlay import Overlay 31 | from .page import Page 32 | from .performance import Performance 33 | from .profiler import Profiler 34 | from .runtime import Runtime 35 | from .schema import Schema 36 | from .security import Security 37 | from .serviceworker import ServiceWorker 38 | from .storage import Storage 39 | from .systeminfo import SystemInfo 40 | from .target import Target 41 | from .tethering import Tethering 42 | from .tracing import Tracing 43 | from .webaudio import WebAudio 44 | 45 | __all__ = [ 46 | "Accessibility", 47 | "Animation", 48 | "ApplicationCache", 49 | "Audits", 50 | "BackgroundService", 51 | "Browser", 52 | "CSS", 53 | "CacheStorage", 54 | "Cast", 55 | "Console", 56 | "DOM", 57 | "DOMDebugger", 58 | "DOMSnapshot", 59 | "DOMStorage", 60 | "Database", 61 | "Debugger", 62 | "DeviceOrientation", 63 | "Emulation", 64 | "Fetch", 65 | "HeadlessExperimental", 66 | "HeapProfiler", 67 | "IO", 68 | "IndexedDB", 69 | "Input", 70 | "Inspector", 71 | "LayerTree", 72 | "Log", 73 | "Memory", 74 | "Network", 75 | "Overlay", 76 | "Page", 77 | "Performance", 78 | "Profiler", 79 | "Runtime", 80 | "Schema", 81 | "Security", 82 | "ServiceWorker", 83 | "Storage", 84 | "SystemInfo", 85 | "Target", 86 | "Tethering", 87 | "Tracing", 88 | "WebAudio", 89 | ] 90 | -------------------------------------------------------------------------------- /cripy/protocol/accessibility.py: -------------------------------------------------------------------------------- 1 | """This is an auto-generated file. Modify at your own risk""" 2 | from typing import Awaitable, Any, Dict, List, Optional, Union, TYPE_CHECKING 3 | 4 | if TYPE_CHECKING: 5 | from cripy import ConnectionType, SessionType 6 | 7 | __all__ = ["Accessibility"] 8 | 9 | 10 | class Accessibility: 11 | """ 12 | Domain Dependencies: 13 | * DOM 14 | Status: Experimental 15 | 16 | See `https://chromedevtools.github.io/devtools-protocol/tot/Accessibility` 17 | """ 18 | 19 | __slots__ = ["client"] 20 | 21 | def __init__(self, client: Union["ConnectionType", "SessionType"]) -> None: 22 | """Initialize a new instance of Accessibility 23 | 24 | :param client: The client instance to be used to communicate with the remote browser instance 25 | """ 26 | self.client: Union["ConnectionType", "SessionType"] = client 27 | 28 | def disable(self) -> Awaitable[Dict]: 29 | """ 30 | Disables the accessibility domain. 31 | 32 | See `https://chromedevtools.github.io/devtools-protocol/tot/Accessibility#method-disable` 33 | 34 | :return: The results of the command 35 | """ 36 | return self.client.send("Accessibility.disable", {}) 37 | 38 | def enable(self) -> Awaitable[Dict]: 39 | """ 40 | Enables the accessibility domain which causes `AXNodeId`s to remain consistent between method calls. 41 | This turns on accessibility for the page, which can impact performance until accessibility is disabled. 42 | 43 | See `https://chromedevtools.github.io/devtools-protocol/tot/Accessibility#method-enable` 44 | 45 | :return: The results of the command 46 | """ 47 | return self.client.send("Accessibility.enable", {}) 48 | 49 | def getPartialAXTree( 50 | self, 51 | nodeId: Optional[int] = None, 52 | backendNodeId: Optional[int] = None, 53 | objectId: Optional[str] = None, 54 | fetchRelatives: Optional[bool] = None, 55 | ) -> Awaitable[Dict]: 56 | """ 57 | Fetches the accessibility node and partial accessibility tree for this DOM node, if it exists. 58 | 59 | Status: Experimental 60 | 61 | See `https://chromedevtools.github.io/devtools-protocol/tot/Accessibility#method-getPartialAXTree` 62 | 63 | :param nodeId: Identifier of the node to get the partial accessibility tree for. 64 | :param backendNodeId: Identifier of the backend node to get the partial accessibility tree for. 65 | :param objectId: JavaScript object id of the node wrapper to get the partial accessibility tree for. 66 | :param fetchRelatives: Whether to fetch this nodes ancestors, siblings and children. Defaults to true. 67 | :return: The results of the command 68 | """ 69 | msg = {} 70 | if nodeId is not None: 71 | msg["nodeId"] = nodeId 72 | if backendNodeId is not None: 73 | msg["backendNodeId"] = backendNodeId 74 | if objectId is not None: 75 | msg["objectId"] = objectId 76 | if fetchRelatives is not None: 77 | msg["fetchRelatives"] = fetchRelatives 78 | return self.client.send("Accessibility.getPartialAXTree", msg) 79 | 80 | def getFullAXTree(self) -> Awaitable[Dict]: 81 | """ 82 | Fetches the entire accessibility tree 83 | 84 | Status: Experimental 85 | 86 | See `https://chromedevtools.github.io/devtools-protocol/tot/Accessibility#method-getFullAXTree` 87 | 88 | :return: The results of the command 89 | """ 90 | return self.client.send("Accessibility.getFullAXTree", {}) 91 | -------------------------------------------------------------------------------- /cripy/protocol/applicationcache.py: -------------------------------------------------------------------------------- 1 | """This is an auto-generated file. Modify at your own risk""" 2 | from typing import Awaitable, Any, Callable, Dict, List, Optional, Union, TYPE_CHECKING 3 | 4 | if TYPE_CHECKING: 5 | from cripy import ConnectionType, SessionType 6 | 7 | __all__ = ["ApplicationCache"] 8 | 9 | 10 | class ApplicationCache: 11 | """ 12 | Status: Experimental 13 | 14 | See `https://chromedevtools.github.io/devtools-protocol/tot/ApplicationCache` 15 | """ 16 | 17 | __slots__ = ["client"] 18 | 19 | def __init__(self, client: Union["ConnectionType", "SessionType"]) -> None: 20 | """Initialize a new instance of ApplicationCache 21 | 22 | :param client: The client instance to be used to communicate with the remote browser instance 23 | """ 24 | self.client: Union["ConnectionType", "SessionType"] = client 25 | 26 | def enable(self) -> Awaitable[Dict]: 27 | """ 28 | Enables application cache domain notifications. 29 | 30 | See `https://chromedevtools.github.io/devtools-protocol/tot/ApplicationCache#method-enable` 31 | 32 | :return: The results of the command 33 | """ 34 | return self.client.send("ApplicationCache.enable", {}) 35 | 36 | def getApplicationCacheForFrame(self, frameId: str) -> Awaitable[Dict]: 37 | """ 38 | Returns relevant application cache data for the document in given frame. 39 | 40 | See `https://chromedevtools.github.io/devtools-protocol/tot/ApplicationCache#method-getApplicationCacheForFrame` 41 | 42 | :param frameId: Identifier of the frame containing document whose application cache is retrieved. 43 | :return: The results of the command 44 | """ 45 | return self.client.send( 46 | "ApplicationCache.getApplicationCacheForFrame", {"frameId": frameId} 47 | ) 48 | 49 | def getFramesWithManifests(self) -> Awaitable[Dict]: 50 | """ 51 | Returns array of frame identifiers with manifest urls for each frame containing a document 52 | associated with some application cache. 53 | 54 | See `https://chromedevtools.github.io/devtools-protocol/tot/ApplicationCache#method-getFramesWithManifests` 55 | 56 | :return: The results of the command 57 | """ 58 | return self.client.send("ApplicationCache.getFramesWithManifests", {}) 59 | 60 | def getManifestForFrame(self, frameId: str) -> Awaitable[Dict]: 61 | """ 62 | Returns manifest URL for document in the given frame. 63 | 64 | See `https://chromedevtools.github.io/devtools-protocol/tot/ApplicationCache#method-getManifestForFrame` 65 | 66 | :param frameId: Identifier of the frame containing document whose manifest is retrieved. 67 | :return: The results of the command 68 | """ 69 | return self.client.send( 70 | "ApplicationCache.getManifestForFrame", {"frameId": frameId} 71 | ) 72 | 73 | def applicationCacheStatusUpdated( 74 | self, listener: Optional[Callable[[Dict[str, Any]], Any]] = None 75 | ) -> Any: 76 | """ 77 | See `https://chromedevtools.github.io/devtools-protocol/tot/ApplicationCache#event-applicationCacheStatusUpdated` 78 | 79 | :param listener: Optional listener function 80 | :return: If a listener was supplied the return value is a callable that 81 | will remove the supplied listener otherwise a future that resolves 82 | with the value of the event 83 | """ 84 | event_name = "ApplicationCache.applicationCacheStatusUpdated" 85 | if listener is None: 86 | future = self.client.loop.create_future() 87 | 88 | def _listener(event: Optional[Dict] = None) -> None: 89 | future.set_result(event) 90 | 91 | self.client.once(event_name, _listener) 92 | 93 | return future 94 | 95 | self.client.on(event_name, listener) 96 | return lambda: self.client.remove_listener(event_name, listener) 97 | 98 | def networkStateUpdated( 99 | self, listener: Optional[Callable[[Dict[str, Any]], Any]] = None 100 | ) -> Any: 101 | """ 102 | See `https://chromedevtools.github.io/devtools-protocol/tot/ApplicationCache#event-networkStateUpdated` 103 | 104 | :param listener: Optional listener function 105 | :return: If a listener was supplied the return value is a callable that 106 | will remove the supplied listener otherwise a future that resolves 107 | with the value of the event 108 | """ 109 | event_name = "ApplicationCache.networkStateUpdated" 110 | if listener is None: 111 | future = self.client.loop.create_future() 112 | 113 | def _listener(event: Optional[Dict] = None) -> None: 114 | future.set_result(event) 115 | 116 | self.client.once(event_name, _listener) 117 | 118 | return future 119 | 120 | self.client.on(event_name, listener) 121 | return lambda: self.client.remove_listener(event_name, listener) 122 | -------------------------------------------------------------------------------- /cripy/protocol/audits.py: -------------------------------------------------------------------------------- 1 | """This is an auto-generated file. Modify at your own risk""" 2 | from typing import Awaitable, Any, Dict, List, Optional, Union, TYPE_CHECKING 3 | 4 | if TYPE_CHECKING: 5 | from cripy import ConnectionType, SessionType 6 | 7 | __all__ = ["Audits"] 8 | 9 | 10 | class Audits: 11 | """ 12 | Audits domain allows investigation of page violations and possible improvements. 13 | 14 | Domain Dependencies: 15 | * Network 16 | Status: Experimental 17 | 18 | See `https://chromedevtools.github.io/devtools-protocol/tot/Audits` 19 | """ 20 | 21 | __slots__ = ["client"] 22 | 23 | def __init__(self, client: Union["ConnectionType", "SessionType"]) -> None: 24 | """Initialize a new instance of Audits 25 | 26 | :param client: The client instance to be used to communicate with the remote browser instance 27 | """ 28 | self.client: Union["ConnectionType", "SessionType"] = client 29 | 30 | def getEncodedResponse( 31 | self, 32 | requestId: str, 33 | encoding: str, 34 | quality: Optional[Union[int, float]] = None, 35 | sizeOnly: Optional[bool] = None, 36 | ) -> Awaitable[Dict]: 37 | """ 38 | Returns the response body and size if it were re-encoded with the specified settings. Only 39 | applies to images. 40 | 41 | See `https://chromedevtools.github.io/devtools-protocol/tot/Audits#method-getEncodedResponse` 42 | 43 | :param requestId: Identifier of the network request to get content for. 44 | :param encoding: The encoding to use. 45 | :param quality: The quality of the encoding (0-1). (defaults to 1) 46 | :param sizeOnly: Whether to only return the size information (defaults to false). 47 | :return: The results of the command 48 | """ 49 | msg = {"requestId": requestId, "encoding": encoding} 50 | if quality is not None: 51 | msg["quality"] = quality 52 | if sizeOnly is not None: 53 | msg["sizeOnly"] = sizeOnly 54 | return self.client.send("Audits.getEncodedResponse", msg) 55 | -------------------------------------------------------------------------------- /cripy/protocol/backgroundservice.py: -------------------------------------------------------------------------------- 1 | """This is an auto-generated file. Modify at your own risk""" 2 | from typing import Awaitable, Any, Callable, Dict, List, Optional, Union, TYPE_CHECKING 3 | 4 | if TYPE_CHECKING: 5 | from cripy import ConnectionType, SessionType 6 | 7 | __all__ = ["BackgroundService"] 8 | 9 | 10 | class BackgroundService: 11 | """ 12 | Defines events for background web platform features. 13 | Status: Experimental 14 | 15 | See `https://chromedevtools.github.io/devtools-protocol/tot/BackgroundService` 16 | """ 17 | 18 | __slots__ = ["client"] 19 | 20 | def __init__(self, client: Union["ConnectionType", "SessionType"]) -> None: 21 | """Initialize a new instance of BackgroundService 22 | 23 | :param client: The client instance to be used to communicate with the remote browser instance 24 | """ 25 | self.client: Union["ConnectionType", "SessionType"] = client 26 | 27 | def startObserving(self, service: str) -> Awaitable[Dict]: 28 | """ 29 | Enables event updates for the service. 30 | 31 | See `https://chromedevtools.github.io/devtools-protocol/tot/BackgroundService#method-startObserving` 32 | 33 | :param service: The service 34 | :return: The results of the command 35 | """ 36 | return self.client.send( 37 | "BackgroundService.startObserving", {"service": service} 38 | ) 39 | 40 | def stopObserving(self, service: str) -> Awaitable[Dict]: 41 | """ 42 | Disables event updates for the service. 43 | 44 | See `https://chromedevtools.github.io/devtools-protocol/tot/BackgroundService#method-stopObserving` 45 | 46 | :param service: The service 47 | :return: The results of the command 48 | """ 49 | return self.client.send("BackgroundService.stopObserving", {"service": service}) 50 | 51 | def setRecording(self, shouldRecord: bool, service: str) -> Awaitable[Dict]: 52 | """ 53 | Set the recording state for the service. 54 | 55 | See `https://chromedevtools.github.io/devtools-protocol/tot/BackgroundService#method-setRecording` 56 | 57 | :param shouldRecord: The shouldRecord 58 | :param service: The service 59 | :return: The results of the command 60 | """ 61 | return self.client.send( 62 | "BackgroundService.setRecording", 63 | {"shouldRecord": shouldRecord, "service": service}, 64 | ) 65 | 66 | def clearEvents(self, service: str) -> Awaitable[Dict]: 67 | """ 68 | Clears all stored data for the service. 69 | 70 | See `https://chromedevtools.github.io/devtools-protocol/tot/BackgroundService#method-clearEvents` 71 | 72 | :param service: The service 73 | :return: The results of the command 74 | """ 75 | return self.client.send("BackgroundService.clearEvents", {"service": service}) 76 | 77 | def recordingStateChanged( 78 | self, listener: Optional[Callable[[Dict[str, Any]], Any]] = None 79 | ) -> Any: 80 | """ 81 | Called when the recording state for the service has been updated. 82 | 83 | See `https://chromedevtools.github.io/devtools-protocol/tot/BackgroundService#event-recordingStateChanged` 84 | 85 | :param listener: Optional listener function 86 | :return: If a listener was supplied the return value is a callable that 87 | will remove the supplied listener otherwise a future that resolves 88 | with the value of the event 89 | """ 90 | event_name = "BackgroundService.recordingStateChanged" 91 | if listener is None: 92 | future = self.client.loop.create_future() 93 | 94 | def _listener(event: Optional[Dict] = None) -> None: 95 | future.set_result(event) 96 | 97 | self.client.once(event_name, _listener) 98 | 99 | return future 100 | 101 | self.client.on(event_name, listener) 102 | return lambda: self.client.remove_listener(event_name, listener) 103 | 104 | def backgroundServiceEventReceived( 105 | self, listener: Optional[Callable[[Dict[str, Any]], Any]] = None 106 | ) -> Any: 107 | """ 108 | Called with all existing backgroundServiceEvents when enabled, and all new 109 | events afterwards if enabled and recording. 110 | 111 | See `https://chromedevtools.github.io/devtools-protocol/tot/BackgroundService#event-backgroundServiceEventReceived` 112 | 113 | :param listener: Optional listener function 114 | :return: If a listener was supplied the return value is a callable that 115 | will remove the supplied listener otherwise a future that resolves 116 | with the value of the event 117 | """ 118 | event_name = "BackgroundService.backgroundServiceEventReceived" 119 | if listener is None: 120 | future = self.client.loop.create_future() 121 | 122 | def _listener(event: Optional[Dict] = None) -> None: 123 | future.set_result(event) 124 | 125 | self.client.once(event_name, _listener) 126 | 127 | return future 128 | 129 | self.client.on(event_name, listener) 130 | return lambda: self.client.remove_listener(event_name, listener) 131 | -------------------------------------------------------------------------------- /cripy/protocol/cachestorage.py: -------------------------------------------------------------------------------- 1 | """This is an auto-generated file. Modify at your own risk""" 2 | from typing import Awaitable, Any, Dict, List, Optional, Union, TYPE_CHECKING 3 | 4 | if TYPE_CHECKING: 5 | from cripy import ConnectionType, SessionType 6 | 7 | __all__ = ["CacheStorage"] 8 | 9 | 10 | class CacheStorage: 11 | """ 12 | Status: Experimental 13 | 14 | See `https://chromedevtools.github.io/devtools-protocol/tot/CacheStorage` 15 | """ 16 | 17 | __slots__ = ["client"] 18 | 19 | def __init__(self, client: Union["ConnectionType", "SessionType"]) -> None: 20 | """Initialize a new instance of CacheStorage 21 | 22 | :param client: The client instance to be used to communicate with the remote browser instance 23 | """ 24 | self.client: Union["ConnectionType", "SessionType"] = client 25 | 26 | def deleteCache(self, cacheId: str) -> Awaitable[Dict]: 27 | """ 28 | Deletes a cache. 29 | 30 | See `https://chromedevtools.github.io/devtools-protocol/tot/CacheStorage#method-deleteCache` 31 | 32 | :param cacheId: Id of cache for deletion. 33 | :return: The results of the command 34 | """ 35 | return self.client.send("CacheStorage.deleteCache", {"cacheId": cacheId}) 36 | 37 | def deleteEntry(self, cacheId: str, request: str) -> Awaitable[Dict]: 38 | """ 39 | Deletes a cache entry. 40 | 41 | See `https://chromedevtools.github.io/devtools-protocol/tot/CacheStorage#method-deleteEntry` 42 | 43 | :param cacheId: Id of cache where the entry will be deleted. 44 | :param request: URL spec of the request. 45 | :return: The results of the command 46 | """ 47 | return self.client.send( 48 | "CacheStorage.deleteEntry", {"cacheId": cacheId, "request": request} 49 | ) 50 | 51 | def requestCacheNames(self, securityOrigin: str) -> Awaitable[Dict]: 52 | """ 53 | Requests cache names. 54 | 55 | See `https://chromedevtools.github.io/devtools-protocol/tot/CacheStorage#method-requestCacheNames` 56 | 57 | :param securityOrigin: Security origin. 58 | :return: The results of the command 59 | """ 60 | return self.client.send( 61 | "CacheStorage.requestCacheNames", {"securityOrigin": securityOrigin} 62 | ) 63 | 64 | def requestCachedResponse( 65 | self, cacheId: str, requestURL: str, requestHeaders: List[Dict[str, Any]] 66 | ) -> Awaitable[Dict]: 67 | """ 68 | Fetches cache entry. 69 | 70 | See `https://chromedevtools.github.io/devtools-protocol/tot/CacheStorage#method-requestCachedResponse` 71 | 72 | :param cacheId: Id of cache that contains the entry. 73 | :param requestURL: URL spec of the request. 74 | :param requestHeaders: headers of the request. 75 | :return: The results of the command 76 | """ 77 | return self.client.send( 78 | "CacheStorage.requestCachedResponse", 79 | { 80 | "cacheId": cacheId, 81 | "requestURL": requestURL, 82 | "requestHeaders": requestHeaders, 83 | }, 84 | ) 85 | 86 | def requestEntries( 87 | self, 88 | cacheId: str, 89 | skipCount: int, 90 | pageSize: int, 91 | pathFilter: Optional[str] = None, 92 | ) -> Awaitable[Dict]: 93 | """ 94 | Requests data from cache. 95 | 96 | See `https://chromedevtools.github.io/devtools-protocol/tot/CacheStorage#method-requestEntries` 97 | 98 | :param cacheId: ID of cache to get entries from. 99 | :param skipCount: Number of records to skip. 100 | :param pageSize: Number of records to fetch. 101 | :param pathFilter: If present, only return the entries containing this substring in the path 102 | :return: The results of the command 103 | """ 104 | msg = {"cacheId": cacheId, "skipCount": skipCount, "pageSize": pageSize} 105 | if pathFilter is not None: 106 | msg["pathFilter"] = pathFilter 107 | return self.client.send("CacheStorage.requestEntries", msg) 108 | -------------------------------------------------------------------------------- /cripy/protocol/cast.py: -------------------------------------------------------------------------------- 1 | """This is an auto-generated file. Modify at your own risk""" 2 | from typing import Awaitable, Any, Callable, Dict, List, Optional, Union, TYPE_CHECKING 3 | 4 | if TYPE_CHECKING: 5 | from cripy import ConnectionType, SessionType 6 | 7 | __all__ = ["Cast"] 8 | 9 | 10 | class Cast: 11 | """ 12 | A domain for interacting with Cast, Presentation API, and Remote Playback API 13 | functionalities. 14 | Status: Experimental 15 | 16 | See `https://chromedevtools.github.io/devtools-protocol/tot/Cast` 17 | """ 18 | 19 | __slots__ = ["client"] 20 | 21 | def __init__(self, client: Union["ConnectionType", "SessionType"]) -> None: 22 | """Initialize a new instance of Cast 23 | 24 | :param client: The client instance to be used to communicate with the remote browser instance 25 | """ 26 | self.client: Union["ConnectionType", "SessionType"] = client 27 | 28 | def enable(self, presentationUrl: Optional[str] = None) -> Awaitable[Dict]: 29 | """ 30 | Starts observing for sinks that can be used for tab mirroring, and if set, 31 | sinks compatible with |presentationUrl| as well. When sinks are found, a 32 | |sinksUpdated| event is fired. 33 | Also starts observing for issue messages. When an issue is added or removed, 34 | an |issueUpdated| event is fired. 35 | 36 | See `https://chromedevtools.github.io/devtools-protocol/tot/Cast#method-enable` 37 | 38 | :param presentationUrl: The presentationUrl 39 | :return: The results of the command 40 | """ 41 | msg = {} 42 | if presentationUrl is not None: 43 | msg["presentationUrl"] = presentationUrl 44 | return self.client.send("Cast.enable", msg) 45 | 46 | def disable(self) -> Awaitable[Dict]: 47 | """ 48 | Stops observing for sinks and issues. 49 | 50 | See `https://chromedevtools.github.io/devtools-protocol/tot/Cast#method-disable` 51 | 52 | :return: The results of the command 53 | """ 54 | return self.client.send("Cast.disable", {}) 55 | 56 | def setSinkToUse(self, sinkName: str) -> Awaitable[Dict]: 57 | """ 58 | Sets a sink to be used when the web page requests the browser to choose a 59 | sink via Presentation API, Remote Playback API, or Cast SDK. 60 | 61 | See `https://chromedevtools.github.io/devtools-protocol/tot/Cast#method-setSinkToUse` 62 | 63 | :param sinkName: The sinkName 64 | :return: The results of the command 65 | """ 66 | return self.client.send("Cast.setSinkToUse", {"sinkName": sinkName}) 67 | 68 | def startTabMirroring(self, sinkName: str) -> Awaitable[Dict]: 69 | """ 70 | Starts mirroring the tab to the sink. 71 | 72 | See `https://chromedevtools.github.io/devtools-protocol/tot/Cast#method-startTabMirroring` 73 | 74 | :param sinkName: The sinkName 75 | :return: The results of the command 76 | """ 77 | return self.client.send("Cast.startTabMirroring", {"sinkName": sinkName}) 78 | 79 | def stopCasting(self, sinkName: str) -> Awaitable[Dict]: 80 | """ 81 | Stops the active Cast session on the sink. 82 | 83 | See `https://chromedevtools.github.io/devtools-protocol/tot/Cast#method-stopCasting` 84 | 85 | :param sinkName: The sinkName 86 | :return: The results of the command 87 | """ 88 | return self.client.send("Cast.stopCasting", {"sinkName": sinkName}) 89 | 90 | def sinksUpdated( 91 | self, listener: Optional[Callable[[Dict[str, Any]], Any]] = None 92 | ) -> Any: 93 | """ 94 | This is fired whenever the list of available sinks changes. A sink is a 95 | device or a software surface that you can cast to. 96 | 97 | See `https://chromedevtools.github.io/devtools-protocol/tot/Cast#event-sinksUpdated` 98 | 99 | :param listener: Optional listener function 100 | :return: If a listener was supplied the return value is a callable that 101 | will remove the supplied listener otherwise a future that resolves 102 | with the value of the event 103 | """ 104 | event_name = "Cast.sinksUpdated" 105 | if listener is None: 106 | future = self.client.loop.create_future() 107 | 108 | def _listener(event: Optional[Dict] = None) -> None: 109 | future.set_result(event) 110 | 111 | self.client.once(event_name, _listener) 112 | 113 | return future 114 | 115 | self.client.on(event_name, listener) 116 | return lambda: self.client.remove_listener(event_name, listener) 117 | 118 | def issueUpdated( 119 | self, listener: Optional[Callable[[Dict[str, Any]], Any]] = None 120 | ) -> Any: 121 | """ 122 | This is fired whenever the outstanding issue/error message changes. 123 | |issueMessage| is empty if there is no issue. 124 | 125 | See `https://chromedevtools.github.io/devtools-protocol/tot/Cast#event-issueUpdated` 126 | 127 | :param listener: Optional listener function 128 | :return: If a listener was supplied the return value is a callable that 129 | will remove the supplied listener otherwise a future that resolves 130 | with the value of the event 131 | """ 132 | event_name = "Cast.issueUpdated" 133 | if listener is None: 134 | future = self.client.loop.create_future() 135 | 136 | def _listener(event: Optional[Dict] = None) -> None: 137 | future.set_result(event) 138 | 139 | self.client.once(event_name, _listener) 140 | 141 | return future 142 | 143 | self.client.on(event_name, listener) 144 | return lambda: self.client.remove_listener(event_name, listener) 145 | -------------------------------------------------------------------------------- /cripy/protocol/console.py: -------------------------------------------------------------------------------- 1 | """This is an auto-generated file. Modify at your own risk""" 2 | from typing import Awaitable, Any, Callable, Dict, List, Optional, Union, TYPE_CHECKING 3 | 4 | if TYPE_CHECKING: 5 | from cripy import ConnectionType, SessionType 6 | 7 | __all__ = ["Console"] 8 | 9 | 10 | class Console: 11 | """ 12 | This domain is deprecated - use Runtime or Log instead. 13 | 14 | Domain Dependencies: 15 | * Runtime 16 | Status: Deprecated 17 | 18 | See `https://chromedevtools.github.io/devtools-protocol/tot/Console` 19 | """ 20 | 21 | __slots__ = ["client"] 22 | 23 | def __init__(self, client: Union["ConnectionType", "SessionType"]) -> None: 24 | """Initialize a new instance of Console 25 | 26 | :param client: The client instance to be used to communicate with the remote browser instance 27 | """ 28 | self.client: Union["ConnectionType", "SessionType"] = client 29 | 30 | def clearMessages(self) -> Awaitable[Dict]: 31 | """ 32 | Does nothing. 33 | 34 | See `https://chromedevtools.github.io/devtools-protocol/tot/Console#method-clearMessages` 35 | 36 | :return: The results of the command 37 | """ 38 | return self.client.send("Console.clearMessages", {}) 39 | 40 | def disable(self) -> Awaitable[Dict]: 41 | """ 42 | Disables console domain, prevents further console messages from being reported to the client. 43 | 44 | See `https://chromedevtools.github.io/devtools-protocol/tot/Console#method-disable` 45 | 46 | :return: The results of the command 47 | """ 48 | return self.client.send("Console.disable", {}) 49 | 50 | def enable(self) -> Awaitable[Dict]: 51 | """ 52 | Enables console domain, sends the messages collected so far to the client by means of the 53 | `messageAdded` notification. 54 | 55 | See `https://chromedevtools.github.io/devtools-protocol/tot/Console#method-enable` 56 | 57 | :return: The results of the command 58 | """ 59 | return self.client.send("Console.enable", {}) 60 | 61 | def messageAdded( 62 | self, listener: Optional[Callable[[Dict[str, Any]], Any]] = None 63 | ) -> Any: 64 | """ 65 | Issued when new console message is added. 66 | 67 | See `https://chromedevtools.github.io/devtools-protocol/tot/Console#event-messageAdded` 68 | 69 | :param listener: Optional listener function 70 | :return: If a listener was supplied the return value is a callable that 71 | will remove the supplied listener otherwise a future that resolves 72 | with the value of the event 73 | """ 74 | event_name = "Console.messageAdded" 75 | if listener is None: 76 | future = self.client.loop.create_future() 77 | 78 | def _listener(event: Optional[Dict] = None) -> None: 79 | future.set_result(event) 80 | 81 | self.client.once(event_name, _listener) 82 | 83 | return future 84 | 85 | self.client.on(event_name, listener) 86 | return lambda: self.client.remove_listener(event_name, listener) 87 | -------------------------------------------------------------------------------- /cripy/protocol/database.py: -------------------------------------------------------------------------------- 1 | """This is an auto-generated file. Modify at your own risk""" 2 | from typing import Awaitable, Any, Callable, Dict, List, Optional, Union, TYPE_CHECKING 3 | 4 | if TYPE_CHECKING: 5 | from cripy import ConnectionType, SessionType 6 | 7 | __all__ = ["Database"] 8 | 9 | 10 | class Database: 11 | """ 12 | Status: Experimental 13 | 14 | See `https://chromedevtools.github.io/devtools-protocol/tot/Database` 15 | """ 16 | 17 | __slots__ = ["client"] 18 | 19 | def __init__(self, client: Union["ConnectionType", "SessionType"]) -> None: 20 | """Initialize a new instance of Database 21 | 22 | :param client: The client instance to be used to communicate with the remote browser instance 23 | """ 24 | self.client: Union["ConnectionType", "SessionType"] = client 25 | 26 | def disable(self) -> Awaitable[Dict]: 27 | """ 28 | Disables database tracking, prevents database events from being sent to the client. 29 | 30 | See `https://chromedevtools.github.io/devtools-protocol/tot/Database#method-disable` 31 | 32 | :return: The results of the command 33 | """ 34 | return self.client.send("Database.disable", {}) 35 | 36 | def enable(self) -> Awaitable[Dict]: 37 | """ 38 | Enables database tracking, database events will now be delivered to the client. 39 | 40 | See `https://chromedevtools.github.io/devtools-protocol/tot/Database#method-enable` 41 | 42 | :return: The results of the command 43 | """ 44 | return self.client.send("Database.enable", {}) 45 | 46 | def executeSQL(self, databaseId: str, query: str) -> Awaitable[Dict]: 47 | """ 48 | See `https://chromedevtools.github.io/devtools-protocol/tot/Database#method-executeSQL` 49 | 50 | :param databaseId: The databaseId 51 | :param query: The query 52 | :return: The results of the command 53 | """ 54 | return self.client.send( 55 | "Database.executeSQL", {"databaseId": databaseId, "query": query} 56 | ) 57 | 58 | def getDatabaseTableNames(self, databaseId: str) -> Awaitable[Dict]: 59 | """ 60 | See `https://chromedevtools.github.io/devtools-protocol/tot/Database#method-getDatabaseTableNames` 61 | 62 | :param databaseId: The databaseId 63 | :return: The results of the command 64 | """ 65 | return self.client.send( 66 | "Database.getDatabaseTableNames", {"databaseId": databaseId} 67 | ) 68 | 69 | def addDatabase( 70 | self, listener: Optional[Callable[[Dict[str, Any]], Any]] = None 71 | ) -> Any: 72 | """ 73 | See `https://chromedevtools.github.io/devtools-protocol/tot/Database#event-addDatabase` 74 | 75 | :param listener: Optional listener function 76 | :return: If a listener was supplied the return value is a callable that 77 | will remove the supplied listener otherwise a future that resolves 78 | with the value of the event 79 | """ 80 | event_name = "Database.addDatabase" 81 | if listener is None: 82 | future = self.client.loop.create_future() 83 | 84 | def _listener(event: Optional[Dict] = None) -> None: 85 | future.set_result(event) 86 | 87 | self.client.once(event_name, _listener) 88 | 89 | return future 90 | 91 | self.client.on(event_name, listener) 92 | return lambda: self.client.remove_listener(event_name, listener) 93 | -------------------------------------------------------------------------------- /cripy/protocol/deviceorientation.py: -------------------------------------------------------------------------------- 1 | """This is an auto-generated file. Modify at your own risk""" 2 | from typing import Awaitable, Any, Dict, List, Optional, Union, TYPE_CHECKING 3 | 4 | if TYPE_CHECKING: 5 | from cripy import ConnectionType, SessionType 6 | 7 | __all__ = ["DeviceOrientation"] 8 | 9 | 10 | class DeviceOrientation: 11 | """ 12 | Status: Experimental 13 | 14 | See `https://chromedevtools.github.io/devtools-protocol/tot/DeviceOrientation` 15 | """ 16 | 17 | __slots__ = ["client"] 18 | 19 | def __init__(self, client: Union["ConnectionType", "SessionType"]) -> None: 20 | """Initialize a new instance of DeviceOrientation 21 | 22 | :param client: The client instance to be used to communicate with the remote browser instance 23 | """ 24 | self.client: Union["ConnectionType", "SessionType"] = client 25 | 26 | def clearDeviceOrientationOverride(self) -> Awaitable[Dict]: 27 | """ 28 | Clears the overridden Device Orientation. 29 | 30 | See `https://chromedevtools.github.io/devtools-protocol/tot/DeviceOrientation#method-clearDeviceOrientationOverride` 31 | 32 | :return: The results of the command 33 | """ 34 | return self.client.send("DeviceOrientation.clearDeviceOrientationOverride", {}) 35 | 36 | def setDeviceOrientationOverride( 37 | self, 38 | alpha: Union[int, float], 39 | beta: Union[int, float], 40 | gamma: Union[int, float], 41 | ) -> Awaitable[Dict]: 42 | """ 43 | Overrides the Device Orientation. 44 | 45 | See `https://chromedevtools.github.io/devtools-protocol/tot/DeviceOrientation#method-setDeviceOrientationOverride` 46 | 47 | :param alpha: Mock alpha 48 | :param beta: Mock beta 49 | :param gamma: Mock gamma 50 | :return: The results of the command 51 | """ 52 | return self.client.send( 53 | "DeviceOrientation.setDeviceOrientationOverride", 54 | {"alpha": alpha, "beta": beta, "gamma": gamma}, 55 | ) 56 | -------------------------------------------------------------------------------- /cripy/protocol/domdebugger.py: -------------------------------------------------------------------------------- 1 | """This is an auto-generated file. Modify at your own risk""" 2 | from typing import Awaitable, Any, Dict, List, Optional, Union, TYPE_CHECKING 3 | 4 | if TYPE_CHECKING: 5 | from cripy import ConnectionType, SessionType 6 | 7 | __all__ = ["DOMDebugger"] 8 | 9 | 10 | class DOMDebugger: 11 | """ 12 | DOM debugging allows setting breakpoints on particular DOM operations and events. JavaScript 13 | execution will stop on these operations as if there was a regular breakpoint set. 14 | 15 | Domain Dependencies: 16 | * DOM 17 | * Debugger 18 | * Runtime 19 | 20 | See `https://chromedevtools.github.io/devtools-protocol/tot/DOMDebugger` 21 | """ 22 | 23 | __slots__ = ["client"] 24 | 25 | def __init__(self, client: Union["ConnectionType", "SessionType"]) -> None: 26 | """Initialize a new instance of DOMDebugger 27 | 28 | :param client: The client instance to be used to communicate with the remote browser instance 29 | """ 30 | self.client: Union["ConnectionType", "SessionType"] = client 31 | 32 | def getEventListeners( 33 | self, objectId: str, depth: Optional[int] = None, pierce: Optional[bool] = None 34 | ) -> Awaitable[Dict]: 35 | """ 36 | Returns event listeners of the given object. 37 | 38 | See `https://chromedevtools.github.io/devtools-protocol/tot/DOMDebugger#method-getEventListeners` 39 | 40 | :param objectId: Identifier of the object to return listeners for. 41 | :param depth: The maximum depth at which Node children should be retrieved, defaults to 1. Use -1 for the 42 | entire subtree or provide an integer larger than 0. 43 | :param pierce: Whether or not iframes and shadow roots should be traversed when returning the subtree 44 | (default is false). Reports listeners for all contexts if pierce is enabled. 45 | :return: The results of the command 46 | """ 47 | msg = {"objectId": objectId} 48 | if depth is not None: 49 | msg["depth"] = depth 50 | if pierce is not None: 51 | msg["pierce"] = pierce 52 | return self.client.send("DOMDebugger.getEventListeners", msg) 53 | 54 | def removeDOMBreakpoint(self, nodeId: int, type: str) -> Awaitable[Dict]: 55 | """ 56 | Removes DOM breakpoint that was set using `setDOMBreakpoint`. 57 | 58 | See `https://chromedevtools.github.io/devtools-protocol/tot/DOMDebugger#method-removeDOMBreakpoint` 59 | 60 | :param nodeId: Identifier of the node to remove breakpoint from. 61 | :param type: Type of the breakpoint to remove. 62 | :return: The results of the command 63 | """ 64 | return self.client.send( 65 | "DOMDebugger.removeDOMBreakpoint", {"nodeId": nodeId, "type": type} 66 | ) 67 | 68 | def removeEventListenerBreakpoint( 69 | self, eventName: str, targetName: Optional[str] = None 70 | ) -> Awaitable[Dict]: 71 | """ 72 | Removes breakpoint on particular DOM event. 73 | 74 | See `https://chromedevtools.github.io/devtools-protocol/tot/DOMDebugger#method-removeEventListenerBreakpoint` 75 | 76 | :param eventName: Event name. 77 | :param targetName: EventTarget interface name. 78 | :return: The results of the command 79 | """ 80 | msg = {"eventName": eventName} 81 | if targetName is not None: 82 | msg["targetName"] = targetName 83 | return self.client.send("DOMDebugger.removeEventListenerBreakpoint", msg) 84 | 85 | def removeInstrumentationBreakpoint(self, eventName: str) -> Awaitable[Dict]: 86 | """ 87 | Removes breakpoint on particular native event. 88 | 89 | Status: Experimental 90 | 91 | See `https://chromedevtools.github.io/devtools-protocol/tot/DOMDebugger#method-removeInstrumentationBreakpoint` 92 | 93 | :param eventName: Instrumentation name to stop on. 94 | :return: The results of the command 95 | """ 96 | return self.client.send( 97 | "DOMDebugger.removeInstrumentationBreakpoint", {"eventName": eventName} 98 | ) 99 | 100 | def removeXHRBreakpoint(self, url: str) -> Awaitable[Dict]: 101 | """ 102 | Removes breakpoint from XMLHttpRequest. 103 | 104 | See `https://chromedevtools.github.io/devtools-protocol/tot/DOMDebugger#method-removeXHRBreakpoint` 105 | 106 | :param url: Resource URL substring. 107 | :return: The results of the command 108 | """ 109 | return self.client.send("DOMDebugger.removeXHRBreakpoint", {"url": url}) 110 | 111 | def setDOMBreakpoint(self, nodeId: int, type: str) -> Awaitable[Dict]: 112 | """ 113 | Sets breakpoint on particular operation with DOM. 114 | 115 | See `https://chromedevtools.github.io/devtools-protocol/tot/DOMDebugger#method-setDOMBreakpoint` 116 | 117 | :param nodeId: Identifier of the node to set breakpoint on. 118 | :param type: Type of the operation to stop upon. 119 | :return: The results of the command 120 | """ 121 | return self.client.send( 122 | "DOMDebugger.setDOMBreakpoint", {"nodeId": nodeId, "type": type} 123 | ) 124 | 125 | def setEventListenerBreakpoint( 126 | self, eventName: str, targetName: Optional[str] = None 127 | ) -> Awaitable[Dict]: 128 | """ 129 | Sets breakpoint on particular DOM event. 130 | 131 | See `https://chromedevtools.github.io/devtools-protocol/tot/DOMDebugger#method-setEventListenerBreakpoint` 132 | 133 | :param eventName: DOM Event name to stop on (any DOM event will do). 134 | :param targetName: EventTarget interface name to stop on. If equal to `"*"` or not provided, will stop on any 135 | EventTarget. 136 | :return: The results of the command 137 | """ 138 | msg = {"eventName": eventName} 139 | if targetName is not None: 140 | msg["targetName"] = targetName 141 | return self.client.send("DOMDebugger.setEventListenerBreakpoint", msg) 142 | 143 | def setInstrumentationBreakpoint(self, eventName: str) -> Awaitable[Dict]: 144 | """ 145 | Sets breakpoint on particular native event. 146 | 147 | Status: Experimental 148 | 149 | See `https://chromedevtools.github.io/devtools-protocol/tot/DOMDebugger#method-setInstrumentationBreakpoint` 150 | 151 | :param eventName: Instrumentation name to stop on. 152 | :return: The results of the command 153 | """ 154 | return self.client.send( 155 | "DOMDebugger.setInstrumentationBreakpoint", {"eventName": eventName} 156 | ) 157 | 158 | def setXHRBreakpoint(self, url: str) -> Awaitable[Dict]: 159 | """ 160 | Sets breakpoint on XMLHttpRequest. 161 | 162 | See `https://chromedevtools.github.io/devtools-protocol/tot/DOMDebugger#method-setXHRBreakpoint` 163 | 164 | :param url: Resource URL substring. All XHRs having this substring in the URL will get stopped upon. 165 | :return: The results of the command 166 | """ 167 | return self.client.send("DOMDebugger.setXHRBreakpoint", {"url": url}) 168 | -------------------------------------------------------------------------------- /cripy/protocol/domsnapshot.py: -------------------------------------------------------------------------------- 1 | """This is an auto-generated file. Modify at your own risk""" 2 | from typing import Awaitable, Any, Dict, List, Optional, Union, TYPE_CHECKING 3 | 4 | if TYPE_CHECKING: 5 | from cripy import ConnectionType, SessionType 6 | 7 | __all__ = ["DOMSnapshot"] 8 | 9 | 10 | class DOMSnapshot: 11 | """ 12 | This domain facilitates obtaining document snapshots with DOM, layout, and style information. 13 | 14 | Domain Dependencies: 15 | * CSS 16 | * DOM 17 | * DOMDebugger 18 | * Page 19 | Status: Experimental 20 | 21 | See `https://chromedevtools.github.io/devtools-protocol/tot/DOMSnapshot` 22 | """ 23 | 24 | __slots__ = ["client"] 25 | 26 | def __init__(self, client: Union["ConnectionType", "SessionType"]) -> None: 27 | """Initialize a new instance of DOMSnapshot 28 | 29 | :param client: The client instance to be used to communicate with the remote browser instance 30 | """ 31 | self.client: Union["ConnectionType", "SessionType"] = client 32 | 33 | def disable(self) -> Awaitable[Dict]: 34 | """ 35 | Disables DOM snapshot agent for the given page. 36 | 37 | See `https://chromedevtools.github.io/devtools-protocol/tot/DOMSnapshot#method-disable` 38 | 39 | :return: The results of the command 40 | """ 41 | return self.client.send("DOMSnapshot.disable", {}) 42 | 43 | def enable(self) -> Awaitable[Dict]: 44 | """ 45 | Enables DOM snapshot agent for the given page. 46 | 47 | See `https://chromedevtools.github.io/devtools-protocol/tot/DOMSnapshot#method-enable` 48 | 49 | :return: The results of the command 50 | """ 51 | return self.client.send("DOMSnapshot.enable", {}) 52 | 53 | def getSnapshot( 54 | self, 55 | computedStyleWhitelist: List[str], 56 | includeEventListeners: Optional[bool] = None, 57 | includePaintOrder: Optional[bool] = None, 58 | includeUserAgentShadowTree: Optional[bool] = None, 59 | ) -> Awaitable[Dict]: 60 | """ 61 | Returns a document snapshot, including the full DOM tree of the root node (including iframes, 62 | template contents, and imported documents) in a flattened array, as well as layout and 63 | white-listed computed style information for the nodes. Shadow DOM in the returned DOM tree is 64 | flattened. 65 | 66 | Status: Deprecated 67 | 68 | See `https://chromedevtools.github.io/devtools-protocol/tot/DOMSnapshot#method-getSnapshot` 69 | 70 | :param computedStyleWhitelist: Whitelist of computed styles to return. 71 | :param includeEventListeners: Whether or not to retrieve details of DOM listeners (default false). 72 | :param includePaintOrder: Whether to determine and include the paint order index of LayoutTreeNodes (default false). 73 | :param includeUserAgentShadowTree: Whether to include UA shadow tree in the snapshot (default false). 74 | :return: The results of the command 75 | """ 76 | msg = {"computedStyleWhitelist": computedStyleWhitelist} 77 | if includeEventListeners is not None: 78 | msg["includeEventListeners"] = includeEventListeners 79 | if includePaintOrder is not None: 80 | msg["includePaintOrder"] = includePaintOrder 81 | if includeUserAgentShadowTree is not None: 82 | msg["includeUserAgentShadowTree"] = includeUserAgentShadowTree 83 | return self.client.send("DOMSnapshot.getSnapshot", msg) 84 | 85 | def captureSnapshot(self, computedStyles: List[str]) -> Awaitable[Dict]: 86 | """ 87 | Returns a document snapshot, including the full DOM tree of the root node (including iframes, 88 | template contents, and imported documents) in a flattened array, as well as layout and 89 | white-listed computed style information for the nodes. Shadow DOM in the returned DOM tree is 90 | flattened. 91 | 92 | See `https://chromedevtools.github.io/devtools-protocol/tot/DOMSnapshot#method-captureSnapshot` 93 | 94 | :param computedStyles: Whitelist of computed styles to return. 95 | :return: The results of the command 96 | """ 97 | return self.client.send( 98 | "DOMSnapshot.captureSnapshot", {"computedStyles": computedStyles} 99 | ) 100 | -------------------------------------------------------------------------------- /cripy/protocol/domstorage.py: -------------------------------------------------------------------------------- 1 | """This is an auto-generated file. Modify at your own risk""" 2 | from typing import Awaitable, Any, Callable, Dict, List, Optional, Union, TYPE_CHECKING 3 | 4 | if TYPE_CHECKING: 5 | from cripy import ConnectionType, SessionType 6 | 7 | __all__ = ["DOMStorage"] 8 | 9 | 10 | class DOMStorage: 11 | """ 12 | Query and modify DOM storage. 13 | Status: Experimental 14 | 15 | See `https://chromedevtools.github.io/devtools-protocol/tot/DOMStorage` 16 | """ 17 | 18 | __slots__ = ["client"] 19 | 20 | def __init__(self, client: Union["ConnectionType", "SessionType"]) -> None: 21 | """Initialize a new instance of DOMStorage 22 | 23 | :param client: The client instance to be used to communicate with the remote browser instance 24 | """ 25 | self.client: Union["ConnectionType", "SessionType"] = client 26 | 27 | def clear(self, storageId: Dict[str, Any]) -> Awaitable[Dict]: 28 | """ 29 | See `https://chromedevtools.github.io/devtools-protocol/tot/DOMStorage#method-clear` 30 | 31 | :param storageId: The storageId 32 | :return: The results of the command 33 | """ 34 | return self.client.send("DOMStorage.clear", {"storageId": storageId}) 35 | 36 | def disable(self) -> Awaitable[Dict]: 37 | """ 38 | Disables storage tracking, prevents storage events from being sent to the client. 39 | 40 | See `https://chromedevtools.github.io/devtools-protocol/tot/DOMStorage#method-disable` 41 | 42 | :return: The results of the command 43 | """ 44 | return self.client.send("DOMStorage.disable", {}) 45 | 46 | def enable(self) -> Awaitable[Dict]: 47 | """ 48 | Enables storage tracking, storage events will now be delivered to the client. 49 | 50 | See `https://chromedevtools.github.io/devtools-protocol/tot/DOMStorage#method-enable` 51 | 52 | :return: The results of the command 53 | """ 54 | return self.client.send("DOMStorage.enable", {}) 55 | 56 | def getDOMStorageItems(self, storageId: Dict[str, Any]) -> Awaitable[Dict]: 57 | """ 58 | See `https://chromedevtools.github.io/devtools-protocol/tot/DOMStorage#method-getDOMStorageItems` 59 | 60 | :param storageId: The storageId 61 | :return: The results of the command 62 | """ 63 | return self.client.send( 64 | "DOMStorage.getDOMStorageItems", {"storageId": storageId} 65 | ) 66 | 67 | def removeDOMStorageItem( 68 | self, storageId: Dict[str, Any], key: str 69 | ) -> Awaitable[Dict]: 70 | """ 71 | See `https://chromedevtools.github.io/devtools-protocol/tot/DOMStorage#method-removeDOMStorageItem` 72 | 73 | :param storageId: The storageId 74 | :param key: The key 75 | :return: The results of the command 76 | """ 77 | return self.client.send( 78 | "DOMStorage.removeDOMStorageItem", {"storageId": storageId, "key": key} 79 | ) 80 | 81 | def setDOMStorageItem( 82 | self, storageId: Dict[str, Any], key: str, value: str 83 | ) -> Awaitable[Dict]: 84 | """ 85 | See `https://chromedevtools.github.io/devtools-protocol/tot/DOMStorage#method-setDOMStorageItem` 86 | 87 | :param storageId: The storageId 88 | :param key: The key 89 | :param value: The value 90 | :return: The results of the command 91 | """ 92 | return self.client.send( 93 | "DOMStorage.setDOMStorageItem", 94 | {"storageId": storageId, "key": key, "value": value}, 95 | ) 96 | 97 | def domStorageItemAdded( 98 | self, listener: Optional[Callable[[Dict[str, Any]], Any]] = None 99 | ) -> Any: 100 | """ 101 | See `https://chromedevtools.github.io/devtools-protocol/tot/DOMStorage#event-domStorageItemAdded` 102 | 103 | :param listener: Optional listener function 104 | :return: If a listener was supplied the return value is a callable that 105 | will remove the supplied listener otherwise a future that resolves 106 | with the value of the event 107 | """ 108 | event_name = "DOMStorage.domStorageItemAdded" 109 | if listener is None: 110 | future = self.client.loop.create_future() 111 | 112 | def _listener(event: Optional[Dict] = None) -> None: 113 | future.set_result(event) 114 | 115 | self.client.once(event_name, _listener) 116 | 117 | return future 118 | 119 | self.client.on(event_name, listener) 120 | return lambda: self.client.remove_listener(event_name, listener) 121 | 122 | def domStorageItemRemoved( 123 | self, listener: Optional[Callable[[Dict[str, Any]], Any]] = None 124 | ) -> Any: 125 | """ 126 | See `https://chromedevtools.github.io/devtools-protocol/tot/DOMStorage#event-domStorageItemRemoved` 127 | 128 | :param listener: Optional listener function 129 | :return: If a listener was supplied the return value is a callable that 130 | will remove the supplied listener otherwise a future that resolves 131 | with the value of the event 132 | """ 133 | event_name = "DOMStorage.domStorageItemRemoved" 134 | if listener is None: 135 | future = self.client.loop.create_future() 136 | 137 | def _listener(event: Optional[Dict] = None) -> None: 138 | future.set_result(event) 139 | 140 | self.client.once(event_name, _listener) 141 | 142 | return future 143 | 144 | self.client.on(event_name, listener) 145 | return lambda: self.client.remove_listener(event_name, listener) 146 | 147 | def domStorageItemUpdated( 148 | self, listener: Optional[Callable[[Dict[str, Any]], Any]] = None 149 | ) -> Any: 150 | """ 151 | See `https://chromedevtools.github.io/devtools-protocol/tot/DOMStorage#event-domStorageItemUpdated` 152 | 153 | :param listener: Optional listener function 154 | :return: If a listener was supplied the return value is a callable that 155 | will remove the supplied listener otherwise a future that resolves 156 | with the value of the event 157 | """ 158 | event_name = "DOMStorage.domStorageItemUpdated" 159 | if listener is None: 160 | future = self.client.loop.create_future() 161 | 162 | def _listener(event: Optional[Dict] = None) -> None: 163 | future.set_result(event) 164 | 165 | self.client.once(event_name, _listener) 166 | 167 | return future 168 | 169 | self.client.on(event_name, listener) 170 | return lambda: self.client.remove_listener(event_name, listener) 171 | 172 | def domStorageItemsCleared( 173 | self, listener: Optional[Callable[[Dict[str, Any]], Any]] = None 174 | ) -> Any: 175 | """ 176 | See `https://chromedevtools.github.io/devtools-protocol/tot/DOMStorage#event-domStorageItemsCleared` 177 | 178 | :param listener: Optional listener function 179 | :return: If a listener was supplied the return value is a callable that 180 | will remove the supplied listener otherwise a future that resolves 181 | with the value of the event 182 | """ 183 | event_name = "DOMStorage.domStorageItemsCleared" 184 | if listener is None: 185 | future = self.client.loop.create_future() 186 | 187 | def _listener(event: Optional[Dict] = None) -> None: 188 | future.set_result(event) 189 | 190 | self.client.once(event_name, _listener) 191 | 192 | return future 193 | 194 | self.client.on(event_name, listener) 195 | return lambda: self.client.remove_listener(event_name, listener) 196 | -------------------------------------------------------------------------------- /cripy/protocol/headlessexperimental.py: -------------------------------------------------------------------------------- 1 | """This is an auto-generated file. Modify at your own risk""" 2 | from typing import Awaitable, Any, Callable, Dict, List, Optional, Union, TYPE_CHECKING 3 | 4 | if TYPE_CHECKING: 5 | from cripy import ConnectionType, SessionType 6 | 7 | __all__ = ["HeadlessExperimental"] 8 | 9 | 10 | class HeadlessExperimental: 11 | """ 12 | This domain provides experimental commands only supported in headless mode. 13 | 14 | Domain Dependencies: 15 | * Page 16 | * Runtime 17 | Status: Experimental 18 | 19 | See `https://chromedevtools.github.io/devtools-protocol/tot/HeadlessExperimental` 20 | """ 21 | 22 | __slots__ = ["client"] 23 | 24 | def __init__(self, client: Union["ConnectionType", "SessionType"]) -> None: 25 | """Initialize a new instance of HeadlessExperimental 26 | 27 | :param client: The client instance to be used to communicate with the remote browser instance 28 | """ 29 | self.client: Union["ConnectionType", "SessionType"] = client 30 | 31 | def beginFrame( 32 | self, 33 | frameTimeTicks: Optional[Union[int, float]] = None, 34 | interval: Optional[Union[int, float]] = None, 35 | noDisplayUpdates: Optional[bool] = None, 36 | screenshot: Optional[Dict[str, Any]] = None, 37 | ) -> Awaitable[Dict]: 38 | """ 39 | Sends a BeginFrame to the target and returns when the frame was completed. Optionally captures a 40 | screenshot from the resulting frame. Requires that the target was created with enabled 41 | BeginFrameControl. Designed for use with --run-all-compositor-stages-before-draw, see also 42 | https://goo.gl/3zHXhB for more background. 43 | 44 | See `https://chromedevtools.github.io/devtools-protocol/tot/HeadlessExperimental#method-beginFrame` 45 | 46 | :param frameTimeTicks: Timestamp of this BeginFrame in Renderer TimeTicks (milliseconds of uptime). If not set, 47 | the current time will be used. 48 | :param interval: The interval between BeginFrames that is reported to the compositor, in milliseconds. 49 | Defaults to a 60 frames/second interval, i.e. about 16.666 milliseconds. 50 | :param noDisplayUpdates: Whether updates should not be committed and drawn onto the display. False by default. If 51 | true, only side effects of the BeginFrame will be run, such as layout and animations, but 52 | any visual updates may not be visible on the display or in screenshots. 53 | :param screenshot: If set, a screenshot of the frame will be captured and returned in the response. Otherwise, 54 | no screenshot will be captured. Note that capturing a screenshot can fail, for example, 55 | during renderer initialization. In such a case, no screenshot data will be returned. 56 | :return: The results of the command 57 | """ 58 | msg = {} 59 | if frameTimeTicks is not None: 60 | msg["frameTimeTicks"] = frameTimeTicks 61 | if interval is not None: 62 | msg["interval"] = interval 63 | if noDisplayUpdates is not None: 64 | msg["noDisplayUpdates"] = noDisplayUpdates 65 | if screenshot is not None: 66 | msg["screenshot"] = screenshot 67 | return self.client.send("HeadlessExperimental.beginFrame", msg) 68 | 69 | def disable(self) -> Awaitable[Dict]: 70 | """ 71 | Disables headless events for the target. 72 | 73 | See `https://chromedevtools.github.io/devtools-protocol/tot/HeadlessExperimental#method-disable` 74 | 75 | :return: The results of the command 76 | """ 77 | return self.client.send("HeadlessExperimental.disable", {}) 78 | 79 | def enable(self) -> Awaitable[Dict]: 80 | """ 81 | Enables headless events for the target. 82 | 83 | See `https://chromedevtools.github.io/devtools-protocol/tot/HeadlessExperimental#method-enable` 84 | 85 | :return: The results of the command 86 | """ 87 | return self.client.send("HeadlessExperimental.enable", {}) 88 | 89 | def needsBeginFramesChanged( 90 | self, listener: Optional[Callable[[Dict[str, Any]], Any]] = None 91 | ) -> Any: 92 | """ 93 | Issued when the target starts or stops needing BeginFrames. 94 | 95 | See `https://chromedevtools.github.io/devtools-protocol/tot/HeadlessExperimental#event-needsBeginFramesChanged` 96 | 97 | :param listener: Optional listener function 98 | :return: If a listener was supplied the return value is a callable that 99 | will remove the supplied listener otherwise a future that resolves 100 | with the value of the event 101 | """ 102 | event_name = "HeadlessExperimental.needsBeginFramesChanged" 103 | if listener is None: 104 | future = self.client.loop.create_future() 105 | 106 | def _listener(event: Optional[Dict] = None) -> None: 107 | future.set_result(event) 108 | 109 | self.client.once(event_name, _listener) 110 | 111 | return future 112 | 113 | self.client.on(event_name, listener) 114 | return lambda: self.client.remove_listener(event_name, listener) 115 | -------------------------------------------------------------------------------- /cripy/protocol/indexeddb.py: -------------------------------------------------------------------------------- 1 | """This is an auto-generated file. Modify at your own risk""" 2 | from typing import Awaitable, Any, Dict, List, Optional, Union, TYPE_CHECKING 3 | 4 | if TYPE_CHECKING: 5 | from cripy import ConnectionType, SessionType 6 | 7 | __all__ = ["IndexedDB"] 8 | 9 | 10 | class IndexedDB: 11 | """ 12 | Domain Dependencies: 13 | * Runtime 14 | Status: Experimental 15 | 16 | See `https://chromedevtools.github.io/devtools-protocol/tot/IndexedDB` 17 | """ 18 | 19 | __slots__ = ["client"] 20 | 21 | def __init__(self, client: Union["ConnectionType", "SessionType"]) -> None: 22 | """Initialize a new instance of IndexedDB 23 | 24 | :param client: The client instance to be used to communicate with the remote browser instance 25 | """ 26 | self.client: Union["ConnectionType", "SessionType"] = client 27 | 28 | def clearObjectStore( 29 | self, securityOrigin: str, databaseName: str, objectStoreName: str 30 | ) -> Awaitable[Dict]: 31 | """ 32 | Clears all entries from an object store. 33 | 34 | See `https://chromedevtools.github.io/devtools-protocol/tot/IndexedDB#method-clearObjectStore` 35 | 36 | :param securityOrigin: Security origin. 37 | :param databaseName: Database name. 38 | :param objectStoreName: Object store name. 39 | :return: The results of the command 40 | """ 41 | return self.client.send( 42 | "IndexedDB.clearObjectStore", 43 | { 44 | "securityOrigin": securityOrigin, 45 | "databaseName": databaseName, 46 | "objectStoreName": objectStoreName, 47 | }, 48 | ) 49 | 50 | def deleteDatabase(self, securityOrigin: str, databaseName: str) -> Awaitable[Dict]: 51 | """ 52 | Deletes a database. 53 | 54 | See `https://chromedevtools.github.io/devtools-protocol/tot/IndexedDB#method-deleteDatabase` 55 | 56 | :param securityOrigin: Security origin. 57 | :param databaseName: Database name. 58 | :return: The results of the command 59 | """ 60 | return self.client.send( 61 | "IndexedDB.deleteDatabase", 62 | {"securityOrigin": securityOrigin, "databaseName": databaseName}, 63 | ) 64 | 65 | def deleteObjectStoreEntries( 66 | self, 67 | securityOrigin: str, 68 | databaseName: str, 69 | objectStoreName: str, 70 | keyRange: Dict[str, Any], 71 | ) -> Awaitable[Dict]: 72 | """ 73 | Delete a range of entries from an object store 74 | 75 | See `https://chromedevtools.github.io/devtools-protocol/tot/IndexedDB#method-deleteObjectStoreEntries` 76 | 77 | :param securityOrigin: The securityOrigin 78 | :param databaseName: The databaseName 79 | :param objectStoreName: The objectStoreName 80 | :param keyRange: Range of entry keys to delete 81 | :return: The results of the command 82 | """ 83 | return self.client.send( 84 | "IndexedDB.deleteObjectStoreEntries", 85 | { 86 | "securityOrigin": securityOrigin, 87 | "databaseName": databaseName, 88 | "objectStoreName": objectStoreName, 89 | "keyRange": keyRange, 90 | }, 91 | ) 92 | 93 | def disable(self) -> Awaitable[Dict]: 94 | """ 95 | Disables events from backend. 96 | 97 | See `https://chromedevtools.github.io/devtools-protocol/tot/IndexedDB#method-disable` 98 | 99 | :return: The results of the command 100 | """ 101 | return self.client.send("IndexedDB.disable", {}) 102 | 103 | def enable(self) -> Awaitable[Dict]: 104 | """ 105 | Enables events from backend. 106 | 107 | See `https://chromedevtools.github.io/devtools-protocol/tot/IndexedDB#method-enable` 108 | 109 | :return: The results of the command 110 | """ 111 | return self.client.send("IndexedDB.enable", {}) 112 | 113 | def requestData( 114 | self, 115 | securityOrigin: str, 116 | databaseName: str, 117 | objectStoreName: str, 118 | indexName: str, 119 | skipCount: int, 120 | pageSize: int, 121 | keyRange: Optional[Dict[str, Any]] = None, 122 | ) -> Awaitable[Dict]: 123 | """ 124 | Requests data from object store or index. 125 | 126 | See `https://chromedevtools.github.io/devtools-protocol/tot/IndexedDB#method-requestData` 127 | 128 | :param securityOrigin: Security origin. 129 | :param databaseName: Database name. 130 | :param objectStoreName: Object store name. 131 | :param indexName: Index name, empty string for object store data requests. 132 | :param skipCount: Number of records to skip. 133 | :param pageSize: Number of records to fetch. 134 | :param keyRange: Key range. 135 | :return: The results of the command 136 | """ 137 | msg = { 138 | "securityOrigin": securityOrigin, 139 | "databaseName": databaseName, 140 | "objectStoreName": objectStoreName, 141 | "indexName": indexName, 142 | "skipCount": skipCount, 143 | "pageSize": pageSize, 144 | } 145 | if keyRange is not None: 146 | msg["keyRange"] = keyRange 147 | return self.client.send("IndexedDB.requestData", msg) 148 | 149 | def getMetadata( 150 | self, securityOrigin: str, databaseName: str, objectStoreName: str 151 | ) -> Awaitable[Dict]: 152 | """ 153 | Gets metadata of an object store 154 | 155 | See `https://chromedevtools.github.io/devtools-protocol/tot/IndexedDB#method-getMetadata` 156 | 157 | :param securityOrigin: Security origin. 158 | :param databaseName: Database name. 159 | :param objectStoreName: Object store name. 160 | :return: The results of the command 161 | """ 162 | return self.client.send( 163 | "IndexedDB.getMetadata", 164 | { 165 | "securityOrigin": securityOrigin, 166 | "databaseName": databaseName, 167 | "objectStoreName": objectStoreName, 168 | }, 169 | ) 170 | 171 | def requestDatabase( 172 | self, securityOrigin: str, databaseName: str 173 | ) -> Awaitable[Dict]: 174 | """ 175 | Requests database with given name in given frame. 176 | 177 | See `https://chromedevtools.github.io/devtools-protocol/tot/IndexedDB#method-requestDatabase` 178 | 179 | :param securityOrigin: Security origin. 180 | :param databaseName: Database name. 181 | :return: The results of the command 182 | """ 183 | return self.client.send( 184 | "IndexedDB.requestDatabase", 185 | {"securityOrigin": securityOrigin, "databaseName": databaseName}, 186 | ) 187 | 188 | def requestDatabaseNames(self, securityOrigin: str) -> Awaitable[Dict]: 189 | """ 190 | Requests database names for given security origin. 191 | 192 | See `https://chromedevtools.github.io/devtools-protocol/tot/IndexedDB#method-requestDatabaseNames` 193 | 194 | :param securityOrigin: Security origin. 195 | :return: The results of the command 196 | """ 197 | return self.client.send( 198 | "IndexedDB.requestDatabaseNames", {"securityOrigin": securityOrigin} 199 | ) 200 | -------------------------------------------------------------------------------- /cripy/protocol/inspector.py: -------------------------------------------------------------------------------- 1 | """This is an auto-generated file. Modify at your own risk""" 2 | from typing import Awaitable, Any, Callable, Dict, List, Optional, Union, TYPE_CHECKING 3 | 4 | if TYPE_CHECKING: 5 | from cripy import ConnectionType, SessionType 6 | 7 | __all__ = ["Inspector"] 8 | 9 | 10 | class Inspector: 11 | """ 12 | Status: Experimental 13 | 14 | See `https://chromedevtools.github.io/devtools-protocol/tot/Inspector` 15 | """ 16 | 17 | __slots__ = ["client"] 18 | 19 | def __init__(self, client: Union["ConnectionType", "SessionType"]) -> None: 20 | """Initialize a new instance of Inspector 21 | 22 | :param client: The client instance to be used to communicate with the remote browser instance 23 | """ 24 | self.client: Union["ConnectionType", "SessionType"] = client 25 | 26 | def disable(self) -> Awaitable[Dict]: 27 | """ 28 | Disables inspector domain notifications. 29 | 30 | See `https://chromedevtools.github.io/devtools-protocol/tot/Inspector#method-disable` 31 | 32 | :return: The results of the command 33 | """ 34 | return self.client.send("Inspector.disable", {}) 35 | 36 | def enable(self) -> Awaitable[Dict]: 37 | """ 38 | Enables inspector domain notifications. 39 | 40 | See `https://chromedevtools.github.io/devtools-protocol/tot/Inspector#method-enable` 41 | 42 | :return: The results of the command 43 | """ 44 | return self.client.send("Inspector.enable", {}) 45 | 46 | def detached( 47 | self, listener: Optional[Callable[[Dict[str, Any]], Any]] = None 48 | ) -> Any: 49 | """ 50 | Fired when remote debugging connection is about to be terminated. Contains detach reason. 51 | 52 | See `https://chromedevtools.github.io/devtools-protocol/tot/Inspector#event-detached` 53 | 54 | :param listener: Optional listener function 55 | :return: If a listener was supplied the return value is a callable that 56 | will remove the supplied listener otherwise a future that resolves 57 | with the value of the event 58 | """ 59 | event_name = "Inspector.detached" 60 | if listener is None: 61 | future = self.client.loop.create_future() 62 | 63 | def _listener(event: Optional[Dict] = None) -> None: 64 | future.set_result(event) 65 | 66 | self.client.once(event_name, _listener) 67 | 68 | return future 69 | 70 | self.client.on(event_name, listener) 71 | return lambda: self.client.remove_listener(event_name, listener) 72 | 73 | def targetCrashed(self, listener: Optional[Callable[[Any], Any]] = None) -> Any: 74 | """ 75 | Fired when debugging target has crashed 76 | 77 | See `https://chromedevtools.github.io/devtools-protocol/tot/Inspector#event-targetCrashed` 78 | 79 | :param listener: Optional listener function 80 | :return: If a listener was supplied the return value is a callable that 81 | will remove the supplied listener otherwise a future that resolves 82 | with the value of the event 83 | """ 84 | event_name = "Inspector.targetCrashed" 85 | if listener is None: 86 | future = self.client.loop.create_future() 87 | 88 | def _listener(event: Optional[Dict] = None) -> None: 89 | future.set_result(event) 90 | 91 | self.client.once(event_name, _listener) 92 | 93 | return future 94 | 95 | self.client.on(event_name, listener) 96 | return lambda: self.client.remove_listener(event_name, listener) 97 | 98 | def targetReloadedAfterCrash( 99 | self, listener: Optional[Callable[[Any], Any]] = None 100 | ) -> Any: 101 | """ 102 | Fired when debugging target has reloaded after crash 103 | 104 | See `https://chromedevtools.github.io/devtools-protocol/tot/Inspector#event-targetReloadedAfterCrash` 105 | 106 | :param listener: Optional listener function 107 | :return: If a listener was supplied the return value is a callable that 108 | will remove the supplied listener otherwise a future that resolves 109 | with the value of the event 110 | """ 111 | event_name = "Inspector.targetReloadedAfterCrash" 112 | if listener is None: 113 | future = self.client.loop.create_future() 114 | 115 | def _listener(event: Optional[Dict] = None) -> None: 116 | future.set_result(event) 117 | 118 | self.client.once(event_name, _listener) 119 | 120 | return future 121 | 122 | self.client.on(event_name, listener) 123 | return lambda: self.client.remove_listener(event_name, listener) 124 | -------------------------------------------------------------------------------- /cripy/protocol/io.py: -------------------------------------------------------------------------------- 1 | """This is an auto-generated file. Modify at your own risk""" 2 | from typing import Awaitable, Any, Dict, List, Optional, Union, TYPE_CHECKING 3 | 4 | if TYPE_CHECKING: 5 | from cripy import ConnectionType, SessionType 6 | 7 | __all__ = ["IO"] 8 | 9 | 10 | class IO: 11 | """ 12 | Input/Output operations for streams produced by DevTools. 13 | 14 | See `https://chromedevtools.github.io/devtools-protocol/tot/IO` 15 | """ 16 | 17 | __slots__ = ["client"] 18 | 19 | def __init__(self, client: Union["ConnectionType", "SessionType"]) -> None: 20 | """Initialize a new instance of IO 21 | 22 | :param client: The client instance to be used to communicate with the remote browser instance 23 | """ 24 | self.client: Union["ConnectionType", "SessionType"] = client 25 | 26 | def close(self, handle: str) -> Awaitable[Dict]: 27 | """ 28 | Close the stream, discard any temporary backing storage. 29 | 30 | See `https://chromedevtools.github.io/devtools-protocol/tot/IO#method-close` 31 | 32 | :param handle: Handle of the stream to close. 33 | :return: The results of the command 34 | """ 35 | return self.client.send("IO.close", {"handle": handle}) 36 | 37 | def read( 38 | self, handle: str, offset: Optional[int] = None, size: Optional[int] = None 39 | ) -> Awaitable[Dict]: 40 | """ 41 | Read a chunk of the stream 42 | 43 | See `https://chromedevtools.github.io/devtools-protocol/tot/IO#method-read` 44 | 45 | :param handle: Handle of the stream to read. 46 | :param offset: Seek to the specified offset before reading (if not specificed, proceed with offset 47 | following the last read). Some types of streams may only support sequential reads. 48 | :param size: Maximum number of bytes to read (left upon the agent discretion if not specified). 49 | :return: The results of the command 50 | """ 51 | msg = {"handle": handle} 52 | if offset is not None: 53 | msg["offset"] = offset 54 | if size is not None: 55 | msg["size"] = size 56 | return self.client.send("IO.read", msg) 57 | 58 | def resolveBlob(self, objectId: str) -> Awaitable[Dict]: 59 | """ 60 | Return UUID of Blob object specified by a remote object id. 61 | 62 | See `https://chromedevtools.github.io/devtools-protocol/tot/IO#method-resolveBlob` 63 | 64 | :param objectId: Object id of a Blob object wrapper. 65 | :return: The results of the command 66 | """ 67 | return self.client.send("IO.resolveBlob", {"objectId": objectId}) 68 | -------------------------------------------------------------------------------- /cripy/protocol/layertree.py: -------------------------------------------------------------------------------- 1 | """This is an auto-generated file. Modify at your own risk""" 2 | from typing import Awaitable, Any, Callable, Dict, List, Optional, Union, TYPE_CHECKING 3 | 4 | if TYPE_CHECKING: 5 | from cripy import ConnectionType, SessionType 6 | 7 | __all__ = ["LayerTree"] 8 | 9 | 10 | class LayerTree: 11 | """ 12 | Domain Dependencies: 13 | * DOM 14 | Status: Experimental 15 | 16 | See `https://chromedevtools.github.io/devtools-protocol/tot/LayerTree` 17 | """ 18 | 19 | __slots__ = ["client"] 20 | 21 | def __init__(self, client: Union["ConnectionType", "SessionType"]) -> None: 22 | """Initialize a new instance of LayerTree 23 | 24 | :param client: The client instance to be used to communicate with the remote browser instance 25 | """ 26 | self.client: Union["ConnectionType", "SessionType"] = client 27 | 28 | def compositingReasons(self, layerId: str) -> Awaitable[Dict]: 29 | """ 30 | Provides the reasons why the given layer was composited. 31 | 32 | See `https://chromedevtools.github.io/devtools-protocol/tot/LayerTree#method-compositingReasons` 33 | 34 | :param layerId: The id of the layer for which we want to get the reasons it was composited. 35 | :return: The results of the command 36 | """ 37 | return self.client.send("LayerTree.compositingReasons", {"layerId": layerId}) 38 | 39 | def disable(self) -> Awaitable[Dict]: 40 | """ 41 | Disables compositing tree inspection. 42 | 43 | See `https://chromedevtools.github.io/devtools-protocol/tot/LayerTree#method-disable` 44 | 45 | :return: The results of the command 46 | """ 47 | return self.client.send("LayerTree.disable", {}) 48 | 49 | def enable(self) -> Awaitable[Dict]: 50 | """ 51 | Enables compositing tree inspection. 52 | 53 | See `https://chromedevtools.github.io/devtools-protocol/tot/LayerTree#method-enable` 54 | 55 | :return: The results of the command 56 | """ 57 | return self.client.send("LayerTree.enable", {}) 58 | 59 | def loadSnapshot(self, tiles: List[Dict[str, Any]]) -> Awaitable[Dict]: 60 | """ 61 | Returns the snapshot identifier. 62 | 63 | See `https://chromedevtools.github.io/devtools-protocol/tot/LayerTree#method-loadSnapshot` 64 | 65 | :param tiles: An array of tiles composing the snapshot. 66 | :return: The results of the command 67 | """ 68 | return self.client.send("LayerTree.loadSnapshot", {"tiles": tiles}) 69 | 70 | def makeSnapshot(self, layerId: str) -> Awaitable[Dict]: 71 | """ 72 | Returns the layer snapshot identifier. 73 | 74 | See `https://chromedevtools.github.io/devtools-protocol/tot/LayerTree#method-makeSnapshot` 75 | 76 | :param layerId: The id of the layer. 77 | :return: The results of the command 78 | """ 79 | return self.client.send("LayerTree.makeSnapshot", {"layerId": layerId}) 80 | 81 | def profileSnapshot( 82 | self, 83 | snapshotId: str, 84 | minRepeatCount: Optional[int] = None, 85 | minDuration: Optional[Union[int, float]] = None, 86 | clipRect: Optional[Dict[str, Any]] = None, 87 | ) -> Awaitable[Dict]: 88 | """ 89 | See `https://chromedevtools.github.io/devtools-protocol/tot/LayerTree#method-profileSnapshot` 90 | 91 | :param snapshotId: The id of the layer snapshot. 92 | :param minRepeatCount: The maximum number of times to replay the snapshot (1, if not specified). 93 | :param minDuration: The minimum duration (in seconds) to replay the snapshot. 94 | :param clipRect: The clip rectangle to apply when replaying the snapshot. 95 | :return: The results of the command 96 | """ 97 | msg = {"snapshotId": snapshotId} 98 | if minRepeatCount is not None: 99 | msg["minRepeatCount"] = minRepeatCount 100 | if minDuration is not None: 101 | msg["minDuration"] = minDuration 102 | if clipRect is not None: 103 | msg["clipRect"] = clipRect 104 | return self.client.send("LayerTree.profileSnapshot", msg) 105 | 106 | def releaseSnapshot(self, snapshotId: str) -> Awaitable[Dict]: 107 | """ 108 | Releases layer snapshot captured by the back-end. 109 | 110 | See `https://chromedevtools.github.io/devtools-protocol/tot/LayerTree#method-releaseSnapshot` 111 | 112 | :param snapshotId: The id of the layer snapshot. 113 | :return: The results of the command 114 | """ 115 | return self.client.send("LayerTree.releaseSnapshot", {"snapshotId": snapshotId}) 116 | 117 | def replaySnapshot( 118 | self, 119 | snapshotId: str, 120 | fromStep: Optional[int] = None, 121 | toStep: Optional[int] = None, 122 | scale: Optional[Union[int, float]] = None, 123 | ) -> Awaitable[Dict]: 124 | """ 125 | Replays the layer snapshot and returns the resulting bitmap. 126 | 127 | See `https://chromedevtools.github.io/devtools-protocol/tot/LayerTree#method-replaySnapshot` 128 | 129 | :param snapshotId: The id of the layer snapshot. 130 | :param fromStep: The first step to replay from (replay from the very start if not specified). 131 | :param toStep: The last step to replay to (replay till the end if not specified). 132 | :param scale: The scale to apply while replaying (defaults to 1). 133 | :return: The results of the command 134 | """ 135 | msg = {"snapshotId": snapshotId} 136 | if fromStep is not None: 137 | msg["fromStep"] = fromStep 138 | if toStep is not None: 139 | msg["toStep"] = toStep 140 | if scale is not None: 141 | msg["scale"] = scale 142 | return self.client.send("LayerTree.replaySnapshot", msg) 143 | 144 | def snapshotCommandLog(self, snapshotId: str) -> Awaitable[Dict]: 145 | """ 146 | Replays the layer snapshot and returns canvas log. 147 | 148 | See `https://chromedevtools.github.io/devtools-protocol/tot/LayerTree#method-snapshotCommandLog` 149 | 150 | :param snapshotId: The id of the layer snapshot. 151 | :return: The results of the command 152 | """ 153 | return self.client.send( 154 | "LayerTree.snapshotCommandLog", {"snapshotId": snapshotId} 155 | ) 156 | 157 | def layerPainted( 158 | self, listener: Optional[Callable[[Dict[str, Any]], Any]] = None 159 | ) -> Any: 160 | """ 161 | See `https://chromedevtools.github.io/devtools-protocol/tot/LayerTree#event-layerPainted` 162 | 163 | :param listener: Optional listener function 164 | :return: If a listener was supplied the return value is a callable that 165 | will remove the supplied listener otherwise a future that resolves 166 | with the value of the event 167 | """ 168 | event_name = "LayerTree.layerPainted" 169 | if listener is None: 170 | future = self.client.loop.create_future() 171 | 172 | def _listener(event: Optional[Dict] = None) -> None: 173 | future.set_result(event) 174 | 175 | self.client.once(event_name, _listener) 176 | 177 | return future 178 | 179 | self.client.on(event_name, listener) 180 | return lambda: self.client.remove_listener(event_name, listener) 181 | 182 | def layerTreeDidChange( 183 | self, listener: Optional[Callable[[Dict[str, Any]], Any]] = None 184 | ) -> Any: 185 | """ 186 | See `https://chromedevtools.github.io/devtools-protocol/tot/LayerTree#event-layerTreeDidChange` 187 | 188 | :param listener: Optional listener function 189 | :return: If a listener was supplied the return value is a callable that 190 | will remove the supplied listener otherwise a future that resolves 191 | with the value of the event 192 | """ 193 | event_name = "LayerTree.layerTreeDidChange" 194 | if listener is None: 195 | future = self.client.loop.create_future() 196 | 197 | def _listener(event: Optional[Dict] = None) -> None: 198 | future.set_result(event) 199 | 200 | self.client.once(event_name, _listener) 201 | 202 | return future 203 | 204 | self.client.on(event_name, listener) 205 | return lambda: self.client.remove_listener(event_name, listener) 206 | -------------------------------------------------------------------------------- /cripy/protocol/log.py: -------------------------------------------------------------------------------- 1 | """This is an auto-generated file. Modify at your own risk""" 2 | from typing import Awaitable, Any, Callable, Dict, List, Optional, Union, TYPE_CHECKING 3 | 4 | if TYPE_CHECKING: 5 | from cripy import ConnectionType, SessionType 6 | 7 | __all__ = ["Log"] 8 | 9 | 10 | class Log: 11 | """ 12 | Provides access to log entries. 13 | 14 | Domain Dependencies: 15 | * Runtime 16 | * Network 17 | 18 | See `https://chromedevtools.github.io/devtools-protocol/tot/Log` 19 | """ 20 | 21 | __slots__ = ["client"] 22 | 23 | def __init__(self, client: Union["ConnectionType", "SessionType"]) -> None: 24 | """Initialize a new instance of Log 25 | 26 | :param client: The client instance to be used to communicate with the remote browser instance 27 | """ 28 | self.client: Union["ConnectionType", "SessionType"] = client 29 | 30 | def clear(self) -> Awaitable[Dict]: 31 | """ 32 | Clears the log. 33 | 34 | See `https://chromedevtools.github.io/devtools-protocol/tot/Log#method-clear` 35 | 36 | :return: The results of the command 37 | """ 38 | return self.client.send("Log.clear", {}) 39 | 40 | def disable(self) -> Awaitable[Dict]: 41 | """ 42 | Disables log domain, prevents further log entries from being reported to the client. 43 | 44 | See `https://chromedevtools.github.io/devtools-protocol/tot/Log#method-disable` 45 | 46 | :return: The results of the command 47 | """ 48 | return self.client.send("Log.disable", {}) 49 | 50 | def enable(self) -> Awaitable[Dict]: 51 | """ 52 | Enables log domain, sends the entries collected so far to the client by means of the 53 | `entryAdded` notification. 54 | 55 | See `https://chromedevtools.github.io/devtools-protocol/tot/Log#method-enable` 56 | 57 | :return: The results of the command 58 | """ 59 | return self.client.send("Log.enable", {}) 60 | 61 | def startViolationsReport(self, config: List[Dict[str, Any]]) -> Awaitable[Dict]: 62 | """ 63 | start violation reporting. 64 | 65 | See `https://chromedevtools.github.io/devtools-protocol/tot/Log#method-startViolationsReport` 66 | 67 | :param config: Configuration for violations. 68 | :return: The results of the command 69 | """ 70 | return self.client.send("Log.startViolationsReport", {"config": config}) 71 | 72 | def stopViolationsReport(self) -> Awaitable[Dict]: 73 | """ 74 | Stop violation reporting. 75 | 76 | See `https://chromedevtools.github.io/devtools-protocol/tot/Log#method-stopViolationsReport` 77 | 78 | :return: The results of the command 79 | """ 80 | return self.client.send("Log.stopViolationsReport", {}) 81 | 82 | def entryAdded( 83 | self, listener: Optional[Callable[[Dict[str, Any]], Any]] = None 84 | ) -> Any: 85 | """ 86 | Issued when new message was logged. 87 | 88 | See `https://chromedevtools.github.io/devtools-protocol/tot/Log#event-entryAdded` 89 | 90 | :param listener: Optional listener function 91 | :return: If a listener was supplied the return value is a callable that 92 | will remove the supplied listener otherwise a future that resolves 93 | with the value of the event 94 | """ 95 | event_name = "Log.entryAdded" 96 | if listener is None: 97 | future = self.client.loop.create_future() 98 | 99 | def _listener(event: Optional[Dict] = None) -> None: 100 | future.set_result(event) 101 | 102 | self.client.once(event_name, _listener) 103 | 104 | return future 105 | 106 | self.client.on(event_name, listener) 107 | return lambda: self.client.remove_listener(event_name, listener) 108 | -------------------------------------------------------------------------------- /cripy/protocol/memory.py: -------------------------------------------------------------------------------- 1 | """This is an auto-generated file. Modify at your own risk""" 2 | from typing import Awaitable, Any, Dict, List, Optional, Union, TYPE_CHECKING 3 | 4 | if TYPE_CHECKING: 5 | from cripy import ConnectionType, SessionType 6 | 7 | __all__ = ["Memory"] 8 | 9 | 10 | class Memory: 11 | """ 12 | Status: Experimental 13 | 14 | See `https://chromedevtools.github.io/devtools-protocol/tot/Memory` 15 | """ 16 | 17 | __slots__ = ["client"] 18 | 19 | def __init__(self, client: Union["ConnectionType", "SessionType"]) -> None: 20 | """Initialize a new instance of Memory 21 | 22 | :param client: The client instance to be used to communicate with the remote browser instance 23 | """ 24 | self.client: Union["ConnectionType", "SessionType"] = client 25 | 26 | def getDOMCounters(self) -> Awaitable[Dict]: 27 | """ 28 | See `https://chromedevtools.github.io/devtools-protocol/tot/Memory#method-getDOMCounters` 29 | 30 | :return: The results of the command 31 | """ 32 | return self.client.send("Memory.getDOMCounters", {}) 33 | 34 | def prepareForLeakDetection(self) -> Awaitable[Dict]: 35 | """ 36 | See `https://chromedevtools.github.io/devtools-protocol/tot/Memory#method-prepareForLeakDetection` 37 | 38 | :return: The results of the command 39 | """ 40 | return self.client.send("Memory.prepareForLeakDetection", {}) 41 | 42 | def forciblyPurgeJavaScriptMemory(self) -> Awaitable[Dict]: 43 | """ 44 | Simulate OomIntervention by purging V8 memory. 45 | 46 | See `https://chromedevtools.github.io/devtools-protocol/tot/Memory#method-forciblyPurgeJavaScriptMemory` 47 | 48 | :return: The results of the command 49 | """ 50 | return self.client.send("Memory.forciblyPurgeJavaScriptMemory", {}) 51 | 52 | def setPressureNotificationsSuppressed(self, suppressed: bool) -> Awaitable[Dict]: 53 | """ 54 | Enable/disable suppressing memory pressure notifications in all processes. 55 | 56 | See `https://chromedevtools.github.io/devtools-protocol/tot/Memory#method-setPressureNotificationsSuppressed` 57 | 58 | :param suppressed: If true, memory pressure notifications will be suppressed. 59 | :return: The results of the command 60 | """ 61 | return self.client.send( 62 | "Memory.setPressureNotificationsSuppressed", {"suppressed": suppressed} 63 | ) 64 | 65 | def simulatePressureNotification(self, level: str) -> Awaitable[Dict]: 66 | """ 67 | Simulate a memory pressure notification in all processes. 68 | 69 | See `https://chromedevtools.github.io/devtools-protocol/tot/Memory#method-simulatePressureNotification` 70 | 71 | :param level: Memory pressure level of the notification. 72 | :return: The results of the command 73 | """ 74 | return self.client.send("Memory.simulatePressureNotification", {"level": level}) 75 | 76 | def startSampling( 77 | self, 78 | samplingInterval: Optional[int] = None, 79 | suppressRandomness: Optional[bool] = None, 80 | ) -> Awaitable[Dict]: 81 | """ 82 | Start collecting native memory profile. 83 | 84 | See `https://chromedevtools.github.io/devtools-protocol/tot/Memory#method-startSampling` 85 | 86 | :param samplingInterval: Average number of bytes between samples. 87 | :param suppressRandomness: Do not randomize intervals between samples. 88 | :return: The results of the command 89 | """ 90 | msg = {} 91 | if samplingInterval is not None: 92 | msg["samplingInterval"] = samplingInterval 93 | if suppressRandomness is not None: 94 | msg["suppressRandomness"] = suppressRandomness 95 | return self.client.send("Memory.startSampling", msg) 96 | 97 | def stopSampling(self) -> Awaitable[Dict]: 98 | """ 99 | Stop collecting native memory profile. 100 | 101 | See `https://chromedevtools.github.io/devtools-protocol/tot/Memory#method-stopSampling` 102 | 103 | :return: The results of the command 104 | """ 105 | return self.client.send("Memory.stopSampling", {}) 106 | 107 | def getAllTimeSamplingProfile(self) -> Awaitable[Dict]: 108 | """ 109 | Retrieve native memory allocations profile 110 | collected since renderer process startup. 111 | 112 | See `https://chromedevtools.github.io/devtools-protocol/tot/Memory#method-getAllTimeSamplingProfile` 113 | 114 | :return: The results of the command 115 | """ 116 | return self.client.send("Memory.getAllTimeSamplingProfile", {}) 117 | 118 | def getBrowserSamplingProfile(self) -> Awaitable[Dict]: 119 | """ 120 | Retrieve native memory allocations profile 121 | collected since browser process startup. 122 | 123 | See `https://chromedevtools.github.io/devtools-protocol/tot/Memory#method-getBrowserSamplingProfile` 124 | 125 | :return: The results of the command 126 | """ 127 | return self.client.send("Memory.getBrowserSamplingProfile", {}) 128 | 129 | def getSamplingProfile(self) -> Awaitable[Dict]: 130 | """ 131 | Retrieve native memory allocations profile collected since last 132 | `startSampling` call. 133 | 134 | See `https://chromedevtools.github.io/devtools-protocol/tot/Memory#method-getSamplingProfile` 135 | 136 | :return: The results of the command 137 | """ 138 | return self.client.send("Memory.getSamplingProfile", {}) 139 | -------------------------------------------------------------------------------- /cripy/protocol/performance.py: -------------------------------------------------------------------------------- 1 | """This is an auto-generated file. Modify at your own risk""" 2 | from typing import Awaitable, Any, Callable, Dict, List, Optional, Union, TYPE_CHECKING 3 | 4 | if TYPE_CHECKING: 5 | from cripy import ConnectionType, SessionType 6 | 7 | __all__ = ["Performance"] 8 | 9 | 10 | class Performance: 11 | """ 12 | See `https://chromedevtools.github.io/devtools-protocol/tot/Performance` 13 | """ 14 | 15 | __slots__ = ["client"] 16 | 17 | def __init__(self, client: Union["ConnectionType", "SessionType"]) -> None: 18 | """Initialize a new instance of Performance 19 | 20 | :param client: The client instance to be used to communicate with the remote browser instance 21 | """ 22 | self.client: Union["ConnectionType", "SessionType"] = client 23 | 24 | def disable(self) -> Awaitable[Dict]: 25 | """ 26 | Disable collecting and reporting metrics. 27 | 28 | See `https://chromedevtools.github.io/devtools-protocol/tot/Performance#method-disable` 29 | 30 | :return: The results of the command 31 | """ 32 | return self.client.send("Performance.disable", {}) 33 | 34 | def enable(self) -> Awaitable[Dict]: 35 | """ 36 | Enable collecting and reporting metrics. 37 | 38 | See `https://chromedevtools.github.io/devtools-protocol/tot/Performance#method-enable` 39 | 40 | :return: The results of the command 41 | """ 42 | return self.client.send("Performance.enable", {}) 43 | 44 | def setTimeDomain(self, timeDomain: str) -> Awaitable[Dict]: 45 | """ 46 | Sets time domain to use for collecting and reporting duration metrics. 47 | Note that this must be called before enabling metrics collection. Calling 48 | this method while metrics collection is enabled returns an error. 49 | 50 | Status: Experimental 51 | 52 | See `https://chromedevtools.github.io/devtools-protocol/tot/Performance#method-setTimeDomain` 53 | 54 | :param timeDomain: Time domain 55 | :return: The results of the command 56 | """ 57 | return self.client.send("Performance.setTimeDomain", {"timeDomain": timeDomain}) 58 | 59 | def getMetrics(self) -> Awaitable[Dict]: 60 | """ 61 | Retrieve current values of run-time metrics. 62 | 63 | See `https://chromedevtools.github.io/devtools-protocol/tot/Performance#method-getMetrics` 64 | 65 | :return: The results of the command 66 | """ 67 | return self.client.send("Performance.getMetrics", {}) 68 | 69 | def metrics( 70 | self, listener: Optional[Callable[[Dict[str, Any]], Any]] = None 71 | ) -> Any: 72 | """ 73 | Current values of the metrics. 74 | 75 | See `https://chromedevtools.github.io/devtools-protocol/tot/Performance#event-metrics` 76 | 77 | :param listener: Optional listener function 78 | :return: If a listener was supplied the return value is a callable that 79 | will remove the supplied listener otherwise a future that resolves 80 | with the value of the event 81 | """ 82 | event_name = "Performance.metrics" 83 | if listener is None: 84 | future = self.client.loop.create_future() 85 | 86 | def _listener(event: Optional[Dict] = None) -> None: 87 | future.set_result(event) 88 | 89 | self.client.once(event_name, _listener) 90 | 91 | return future 92 | 93 | self.client.on(event_name, listener) 94 | return lambda: self.client.remove_listener(event_name, listener) 95 | -------------------------------------------------------------------------------- /cripy/protocol/profiler.py: -------------------------------------------------------------------------------- 1 | """This is an auto-generated file. Modify at your own risk""" 2 | from typing import Awaitable, Any, Callable, Dict, List, Optional, Union, TYPE_CHECKING 3 | 4 | if TYPE_CHECKING: 5 | from cripy import ConnectionType, SessionType 6 | 7 | __all__ = ["Profiler"] 8 | 9 | 10 | class Profiler: 11 | """ 12 | Domain Dependencies: 13 | * Runtime 14 | * Debugger 15 | 16 | See `https://chromedevtools.github.io/devtools-protocol/tot/Profiler` 17 | """ 18 | 19 | __slots__ = ["client"] 20 | 21 | def __init__(self, client: Union["ConnectionType", "SessionType"]) -> None: 22 | """Initialize a new instance of Profiler 23 | 24 | :param client: The client instance to be used to communicate with the remote browser instance 25 | """ 26 | self.client: Union["ConnectionType", "SessionType"] = client 27 | 28 | def disable(self) -> Awaitable[Dict]: 29 | """ 30 | See `https://chromedevtools.github.io/devtools-protocol/tot/Profiler#method-disable` 31 | 32 | :return: The results of the command 33 | """ 34 | return self.client.send("Profiler.disable", {}) 35 | 36 | def enable(self) -> Awaitable[Dict]: 37 | """ 38 | See `https://chromedevtools.github.io/devtools-protocol/tot/Profiler#method-enable` 39 | 40 | :return: The results of the command 41 | """ 42 | return self.client.send("Profiler.enable", {}) 43 | 44 | def getBestEffortCoverage(self) -> Awaitable[Dict]: 45 | """ 46 | Collect coverage data for the current isolate. The coverage data may be incomplete due to 47 | garbage collection. 48 | 49 | See `https://chromedevtools.github.io/devtools-protocol/tot/Profiler#method-getBestEffortCoverage` 50 | 51 | :return: The results of the command 52 | """ 53 | return self.client.send("Profiler.getBestEffortCoverage", {}) 54 | 55 | def setSamplingInterval(self, interval: int) -> Awaitable[Dict]: 56 | """ 57 | Changes CPU profiler sampling interval. Must be called before CPU profiles recording started. 58 | 59 | See `https://chromedevtools.github.io/devtools-protocol/tot/Profiler#method-setSamplingInterval` 60 | 61 | :param interval: New sampling interval in microseconds. 62 | :return: The results of the command 63 | """ 64 | return self.client.send("Profiler.setSamplingInterval", {"interval": interval}) 65 | 66 | def start(self) -> Awaitable[Dict]: 67 | """ 68 | See `https://chromedevtools.github.io/devtools-protocol/tot/Profiler#method-start` 69 | 70 | :return: The results of the command 71 | """ 72 | return self.client.send("Profiler.start", {}) 73 | 74 | def startPreciseCoverage( 75 | self, callCount: Optional[bool] = None, detailed: Optional[bool] = None 76 | ) -> Awaitable[Dict]: 77 | """ 78 | Enable precise code coverage. Coverage data for JavaScript executed before enabling precise code 79 | coverage may be incomplete. Enabling prevents running optimized code and resets execution 80 | counters. 81 | 82 | See `https://chromedevtools.github.io/devtools-protocol/tot/Profiler#method-startPreciseCoverage` 83 | 84 | :param callCount: Collect accurate call counts beyond simple 'covered' or 'not covered'. 85 | :param detailed: Collect block-based coverage. 86 | :return: The results of the command 87 | """ 88 | msg = {} 89 | if callCount is not None: 90 | msg["callCount"] = callCount 91 | if detailed is not None: 92 | msg["detailed"] = detailed 93 | return self.client.send("Profiler.startPreciseCoverage", msg) 94 | 95 | def startTypeProfile(self) -> Awaitable[Dict]: 96 | """ 97 | Enable type profile. 98 | 99 | Status: Experimental 100 | 101 | See `https://chromedevtools.github.io/devtools-protocol/tot/Profiler#method-startTypeProfile` 102 | 103 | :return: The results of the command 104 | """ 105 | return self.client.send("Profiler.startTypeProfile", {}) 106 | 107 | def stop(self) -> Awaitable[Dict]: 108 | """ 109 | See `https://chromedevtools.github.io/devtools-protocol/tot/Profiler#method-stop` 110 | 111 | :return: The results of the command 112 | """ 113 | return self.client.send("Profiler.stop", {}) 114 | 115 | def stopPreciseCoverage(self) -> Awaitable[Dict]: 116 | """ 117 | Disable precise code coverage. Disabling releases unnecessary execution count records and allows 118 | executing optimized code. 119 | 120 | See `https://chromedevtools.github.io/devtools-protocol/tot/Profiler#method-stopPreciseCoverage` 121 | 122 | :return: The results of the command 123 | """ 124 | return self.client.send("Profiler.stopPreciseCoverage", {}) 125 | 126 | def stopTypeProfile(self) -> Awaitable[Dict]: 127 | """ 128 | Disable type profile. Disabling releases type profile data collected so far. 129 | 130 | Status: Experimental 131 | 132 | See `https://chromedevtools.github.io/devtools-protocol/tot/Profiler#method-stopTypeProfile` 133 | 134 | :return: The results of the command 135 | """ 136 | return self.client.send("Profiler.stopTypeProfile", {}) 137 | 138 | def takePreciseCoverage(self) -> Awaitable[Dict]: 139 | """ 140 | Collect coverage data for the current isolate, and resets execution counters. Precise code 141 | coverage needs to have started. 142 | 143 | See `https://chromedevtools.github.io/devtools-protocol/tot/Profiler#method-takePreciseCoverage` 144 | 145 | :return: The results of the command 146 | """ 147 | return self.client.send("Profiler.takePreciseCoverage", {}) 148 | 149 | def takeTypeProfile(self) -> Awaitable[Dict]: 150 | """ 151 | Collect type profile. 152 | 153 | Status: Experimental 154 | 155 | See `https://chromedevtools.github.io/devtools-protocol/tot/Profiler#method-takeTypeProfile` 156 | 157 | :return: The results of the command 158 | """ 159 | return self.client.send("Profiler.takeTypeProfile", {}) 160 | 161 | def consoleProfileFinished( 162 | self, listener: Optional[Callable[[Dict[str, Any]], Any]] = None 163 | ) -> Any: 164 | """ 165 | See `https://chromedevtools.github.io/devtools-protocol/tot/Profiler#event-consoleProfileFinished` 166 | 167 | :param listener: Optional listener function 168 | :return: If a listener was supplied the return value is a callable that 169 | will remove the supplied listener otherwise a future that resolves 170 | with the value of the event 171 | """ 172 | event_name = "Profiler.consoleProfileFinished" 173 | if listener is None: 174 | future = self.client.loop.create_future() 175 | 176 | def _listener(event: Optional[Dict] = None) -> None: 177 | future.set_result(event) 178 | 179 | self.client.once(event_name, _listener) 180 | 181 | return future 182 | 183 | self.client.on(event_name, listener) 184 | return lambda: self.client.remove_listener(event_name, listener) 185 | 186 | def consoleProfileStarted( 187 | self, listener: Optional[Callable[[Dict[str, Any]], Any]] = None 188 | ) -> Any: 189 | """ 190 | Sent when new profile recording is started using console.profile() call. 191 | 192 | See `https://chromedevtools.github.io/devtools-protocol/tot/Profiler#event-consoleProfileStarted` 193 | 194 | :param listener: Optional listener function 195 | :return: If a listener was supplied the return value is a callable that 196 | will remove the supplied listener otherwise a future that resolves 197 | with the value of the event 198 | """ 199 | event_name = "Profiler.consoleProfileStarted" 200 | if listener is None: 201 | future = self.client.loop.create_future() 202 | 203 | def _listener(event: Optional[Dict] = None) -> None: 204 | future.set_result(event) 205 | 206 | self.client.once(event_name, _listener) 207 | 208 | return future 209 | 210 | self.client.on(event_name, listener) 211 | return lambda: self.client.remove_listener(event_name, listener) 212 | -------------------------------------------------------------------------------- /cripy/protocol/schema.py: -------------------------------------------------------------------------------- 1 | """This is an auto-generated file. Modify at your own risk""" 2 | from typing import Awaitable, Any, Dict, List, Optional, Union, TYPE_CHECKING 3 | 4 | if TYPE_CHECKING: 5 | from cripy import ConnectionType, SessionType 6 | 7 | __all__ = ["Schema"] 8 | 9 | 10 | class Schema: 11 | """ 12 | This domain is deprecated. 13 | Status: Deprecated 14 | 15 | See `https://chromedevtools.github.io/devtools-protocol/tot/Schema` 16 | """ 17 | 18 | __slots__ = ["client"] 19 | 20 | def __init__(self, client: Union["ConnectionType", "SessionType"]) -> None: 21 | """Initialize a new instance of Schema 22 | 23 | :param client: The client instance to be used to communicate with the remote browser instance 24 | """ 25 | self.client: Union["ConnectionType", "SessionType"] = client 26 | 27 | def getDomains(self) -> Awaitable[Dict]: 28 | """ 29 | Returns supported domains. 30 | 31 | See `https://chromedevtools.github.io/devtools-protocol/tot/Schema#method-getDomains` 32 | 33 | :return: The results of the command 34 | """ 35 | return self.client.send("Schema.getDomains", {}) 36 | -------------------------------------------------------------------------------- /cripy/protocol/security.py: -------------------------------------------------------------------------------- 1 | """This is an auto-generated file. Modify at your own risk""" 2 | from typing import Awaitable, Any, Callable, Dict, List, Optional, Union, TYPE_CHECKING 3 | 4 | if TYPE_CHECKING: 5 | from cripy import ConnectionType, SessionType 6 | 7 | __all__ = ["Security"] 8 | 9 | 10 | class Security: 11 | """ 12 | Security 13 | 14 | See `https://chromedevtools.github.io/devtools-protocol/tot/Security` 15 | """ 16 | 17 | __slots__ = ["client"] 18 | 19 | def __init__(self, client: Union["ConnectionType", "SessionType"]) -> None: 20 | """Initialize a new instance of Security 21 | 22 | :param client: The client instance to be used to communicate with the remote browser instance 23 | """ 24 | self.client: Union["ConnectionType", "SessionType"] = client 25 | 26 | def disable(self) -> Awaitable[Dict]: 27 | """ 28 | Disables tracking security state changes. 29 | 30 | See `https://chromedevtools.github.io/devtools-protocol/tot/Security#method-disable` 31 | 32 | :return: The results of the command 33 | """ 34 | return self.client.send("Security.disable", {}) 35 | 36 | def enable(self) -> Awaitable[Dict]: 37 | """ 38 | Enables tracking security state changes. 39 | 40 | See `https://chromedevtools.github.io/devtools-protocol/tot/Security#method-enable` 41 | 42 | :return: The results of the command 43 | """ 44 | return self.client.send("Security.enable", {}) 45 | 46 | def setIgnoreCertificateErrors(self, ignore: bool) -> Awaitable[Dict]: 47 | """ 48 | Enable/disable whether all certificate errors should be ignored. 49 | 50 | Status: Experimental 51 | 52 | See `https://chromedevtools.github.io/devtools-protocol/tot/Security#method-setIgnoreCertificateErrors` 53 | 54 | :param ignore: If true, all certificate errors will be ignored. 55 | :return: The results of the command 56 | """ 57 | return self.client.send( 58 | "Security.setIgnoreCertificateErrors", {"ignore": ignore} 59 | ) 60 | 61 | def handleCertificateError(self, eventId: int, action: str) -> Awaitable[Dict]: 62 | """ 63 | Handles a certificate error that fired a certificateError event. 64 | 65 | Status: Deprecated 66 | 67 | See `https://chromedevtools.github.io/devtools-protocol/tot/Security#method-handleCertificateError` 68 | 69 | :param eventId: The ID of the event. 70 | :param action: The action to take on the certificate error. 71 | :return: The results of the command 72 | """ 73 | return self.client.send( 74 | "Security.handleCertificateError", {"eventId": eventId, "action": action} 75 | ) 76 | 77 | def setOverrideCertificateErrors(self, override: bool) -> Awaitable[Dict]: 78 | """ 79 | Enable/disable overriding certificate errors. If enabled, all certificate error events need to 80 | be handled by the DevTools client and should be answered with `handleCertificateError` commands. 81 | 82 | Status: Deprecated 83 | 84 | See `https://chromedevtools.github.io/devtools-protocol/tot/Security#method-setOverrideCertificateErrors` 85 | 86 | :param override: If true, certificate errors will be overridden. 87 | :return: The results of the command 88 | """ 89 | return self.client.send( 90 | "Security.setOverrideCertificateErrors", {"override": override} 91 | ) 92 | 93 | def certificateError( 94 | self, listener: Optional[Callable[[Dict[str, Any]], Any]] = None 95 | ) -> Any: 96 | """ 97 | There is a certificate error. If overriding certificate errors is enabled, then it should be 98 | handled with the `handleCertificateError` command. Note: this event does not fire if the 99 | certificate error has been allowed internally. Only one client per target should override 100 | certificate errors at the same time. 101 | 102 | See `https://chromedevtools.github.io/devtools-protocol/tot/Security#event-certificateError` 103 | 104 | :param listener: Optional listener function 105 | :return: If a listener was supplied the return value is a callable that 106 | will remove the supplied listener otherwise a future that resolves 107 | with the value of the event 108 | """ 109 | event_name = "Security.certificateError" 110 | if listener is None: 111 | future = self.client.loop.create_future() 112 | 113 | def _listener(event: Optional[Dict] = None) -> None: 114 | future.set_result(event) 115 | 116 | self.client.once(event_name, _listener) 117 | 118 | return future 119 | 120 | self.client.on(event_name, listener) 121 | return lambda: self.client.remove_listener(event_name, listener) 122 | 123 | def securityStateChanged( 124 | self, listener: Optional[Callable[[Dict[str, Any]], Any]] = None 125 | ) -> Any: 126 | """ 127 | The security state of the page changed. 128 | 129 | See `https://chromedevtools.github.io/devtools-protocol/tot/Security#event-securityStateChanged` 130 | 131 | :param listener: Optional listener function 132 | :return: If a listener was supplied the return value is a callable that 133 | will remove the supplied listener otherwise a future that resolves 134 | with the value of the event 135 | """ 136 | event_name = "Security.securityStateChanged" 137 | if listener is None: 138 | future = self.client.loop.create_future() 139 | 140 | def _listener(event: Optional[Dict] = None) -> None: 141 | future.set_result(event) 142 | 143 | self.client.once(event_name, _listener) 144 | 145 | return future 146 | 147 | self.client.on(event_name, listener) 148 | return lambda: self.client.remove_listener(event_name, listener) 149 | -------------------------------------------------------------------------------- /cripy/protocol/storage.py: -------------------------------------------------------------------------------- 1 | """This is an auto-generated file. Modify at your own risk""" 2 | from typing import Awaitable, Any, Callable, Dict, List, Optional, Union, TYPE_CHECKING 3 | 4 | if TYPE_CHECKING: 5 | from cripy import ConnectionType, SessionType 6 | 7 | __all__ = ["Storage"] 8 | 9 | 10 | class Storage: 11 | """ 12 | Status: Experimental 13 | 14 | See `https://chromedevtools.github.io/devtools-protocol/tot/Storage` 15 | """ 16 | 17 | __slots__ = ["client"] 18 | 19 | def __init__(self, client: Union["ConnectionType", "SessionType"]) -> None: 20 | """Initialize a new instance of Storage 21 | 22 | :param client: The client instance to be used to communicate with the remote browser instance 23 | """ 24 | self.client: Union["ConnectionType", "SessionType"] = client 25 | 26 | def clearDataForOrigin(self, origin: str, storageTypes: str) -> Awaitable[Dict]: 27 | """ 28 | Clears storage for origin. 29 | 30 | See `https://chromedevtools.github.io/devtools-protocol/tot/Storage#method-clearDataForOrigin` 31 | 32 | :param origin: Security origin. 33 | :param storageTypes: Comma separated list of StorageType to clear. 34 | :return: The results of the command 35 | """ 36 | return self.client.send( 37 | "Storage.clearDataForOrigin", 38 | {"origin": origin, "storageTypes": storageTypes}, 39 | ) 40 | 41 | def getUsageAndQuota(self, origin: str) -> Awaitable[Dict]: 42 | """ 43 | Returns usage and quota in bytes. 44 | 45 | See `https://chromedevtools.github.io/devtools-protocol/tot/Storage#method-getUsageAndQuota` 46 | 47 | :param origin: Security origin. 48 | :return: The results of the command 49 | """ 50 | return self.client.send("Storage.getUsageAndQuota", {"origin": origin}) 51 | 52 | def trackCacheStorageForOrigin(self, origin: str) -> Awaitable[Dict]: 53 | """ 54 | Registers origin to be notified when an update occurs to its cache storage list. 55 | 56 | See `https://chromedevtools.github.io/devtools-protocol/tot/Storage#method-trackCacheStorageForOrigin` 57 | 58 | :param origin: Security origin. 59 | :return: The results of the command 60 | """ 61 | return self.client.send( 62 | "Storage.trackCacheStorageForOrigin", {"origin": origin} 63 | ) 64 | 65 | def trackIndexedDBForOrigin(self, origin: str) -> Awaitable[Dict]: 66 | """ 67 | Registers origin to be notified when an update occurs to its IndexedDB. 68 | 69 | See `https://chromedevtools.github.io/devtools-protocol/tot/Storage#method-trackIndexedDBForOrigin` 70 | 71 | :param origin: Security origin. 72 | :return: The results of the command 73 | """ 74 | return self.client.send("Storage.trackIndexedDBForOrigin", {"origin": origin}) 75 | 76 | def untrackCacheStorageForOrigin(self, origin: str) -> Awaitable[Dict]: 77 | """ 78 | Unregisters origin from receiving notifications for cache storage. 79 | 80 | See `https://chromedevtools.github.io/devtools-protocol/tot/Storage#method-untrackCacheStorageForOrigin` 81 | 82 | :param origin: Security origin. 83 | :return: The results of the command 84 | """ 85 | return self.client.send( 86 | "Storage.untrackCacheStorageForOrigin", {"origin": origin} 87 | ) 88 | 89 | def untrackIndexedDBForOrigin(self, origin: str) -> Awaitable[Dict]: 90 | """ 91 | Unregisters origin from receiving notifications for IndexedDB. 92 | 93 | See `https://chromedevtools.github.io/devtools-protocol/tot/Storage#method-untrackIndexedDBForOrigin` 94 | 95 | :param origin: Security origin. 96 | :return: The results of the command 97 | """ 98 | return self.client.send("Storage.untrackIndexedDBForOrigin", {"origin": origin}) 99 | 100 | def cacheStorageContentUpdated( 101 | self, listener: Optional[Callable[[Dict[str, Any]], Any]] = None 102 | ) -> Any: 103 | """ 104 | A cache's contents have been modified. 105 | 106 | See `https://chromedevtools.github.io/devtools-protocol/tot/Storage#event-cacheStorageContentUpdated` 107 | 108 | :param listener: Optional listener function 109 | :return: If a listener was supplied the return value is a callable that 110 | will remove the supplied listener otherwise a future that resolves 111 | with the value of the event 112 | """ 113 | event_name = "Storage.cacheStorageContentUpdated" 114 | if listener is None: 115 | future = self.client.loop.create_future() 116 | 117 | def _listener(event: Optional[Dict] = None) -> None: 118 | future.set_result(event) 119 | 120 | self.client.once(event_name, _listener) 121 | 122 | return future 123 | 124 | self.client.on(event_name, listener) 125 | return lambda: self.client.remove_listener(event_name, listener) 126 | 127 | def cacheStorageListUpdated( 128 | self, listener: Optional[Callable[[Dict[str, Any]], Any]] = None 129 | ) -> Any: 130 | """ 131 | A cache has been added/deleted. 132 | 133 | See `https://chromedevtools.github.io/devtools-protocol/tot/Storage#event-cacheStorageListUpdated` 134 | 135 | :param listener: Optional listener function 136 | :return: If a listener was supplied the return value is a callable that 137 | will remove the supplied listener otherwise a future that resolves 138 | with the value of the event 139 | """ 140 | event_name = "Storage.cacheStorageListUpdated" 141 | if listener is None: 142 | future = self.client.loop.create_future() 143 | 144 | def _listener(event: Optional[Dict] = None) -> None: 145 | future.set_result(event) 146 | 147 | self.client.once(event_name, _listener) 148 | 149 | return future 150 | 151 | self.client.on(event_name, listener) 152 | return lambda: self.client.remove_listener(event_name, listener) 153 | 154 | def indexedDBContentUpdated( 155 | self, listener: Optional[Callable[[Dict[str, Any]], Any]] = None 156 | ) -> Any: 157 | """ 158 | The origin's IndexedDB object store has been modified. 159 | 160 | See `https://chromedevtools.github.io/devtools-protocol/tot/Storage#event-indexedDBContentUpdated` 161 | 162 | :param listener: Optional listener function 163 | :return: If a listener was supplied the return value is a callable that 164 | will remove the supplied listener otherwise a future that resolves 165 | with the value of the event 166 | """ 167 | event_name = "Storage.indexedDBContentUpdated" 168 | if listener is None: 169 | future = self.client.loop.create_future() 170 | 171 | def _listener(event: Optional[Dict] = None) -> None: 172 | future.set_result(event) 173 | 174 | self.client.once(event_name, _listener) 175 | 176 | return future 177 | 178 | self.client.on(event_name, listener) 179 | return lambda: self.client.remove_listener(event_name, listener) 180 | 181 | def indexedDBListUpdated( 182 | self, listener: Optional[Callable[[Dict[str, Any]], Any]] = None 183 | ) -> Any: 184 | """ 185 | The origin's IndexedDB database list has been modified. 186 | 187 | See `https://chromedevtools.github.io/devtools-protocol/tot/Storage#event-indexedDBListUpdated` 188 | 189 | :param listener: Optional listener function 190 | :return: If a listener was supplied the return value is a callable that 191 | will remove the supplied listener otherwise a future that resolves 192 | with the value of the event 193 | """ 194 | event_name = "Storage.indexedDBListUpdated" 195 | if listener is None: 196 | future = self.client.loop.create_future() 197 | 198 | def _listener(event: Optional[Dict] = None) -> None: 199 | future.set_result(event) 200 | 201 | self.client.once(event_name, _listener) 202 | 203 | return future 204 | 205 | self.client.on(event_name, listener) 206 | return lambda: self.client.remove_listener(event_name, listener) 207 | -------------------------------------------------------------------------------- /cripy/protocol/systeminfo.py: -------------------------------------------------------------------------------- 1 | """This is an auto-generated file. Modify at your own risk""" 2 | from typing import Awaitable, Any, Dict, List, Optional, Union, TYPE_CHECKING 3 | 4 | if TYPE_CHECKING: 5 | from cripy import ConnectionType, SessionType 6 | 7 | __all__ = ["SystemInfo"] 8 | 9 | 10 | class SystemInfo: 11 | """ 12 | The SystemInfo domain defines methods and events for querying low-level system information. 13 | Status: Experimental 14 | 15 | See `https://chromedevtools.github.io/devtools-protocol/tot/SystemInfo` 16 | """ 17 | 18 | __slots__ = ["client"] 19 | 20 | def __init__(self, client: Union["ConnectionType", "SessionType"]) -> None: 21 | """Initialize a new instance of SystemInfo 22 | 23 | :param client: The client instance to be used to communicate with the remote browser instance 24 | """ 25 | self.client: Union["ConnectionType", "SessionType"] = client 26 | 27 | def getInfo(self) -> Awaitable[Dict]: 28 | """ 29 | Returns information about the system. 30 | 31 | See `https://chromedevtools.github.io/devtools-protocol/tot/SystemInfo#method-getInfo` 32 | 33 | :return: The results of the command 34 | """ 35 | return self.client.send("SystemInfo.getInfo", {}) 36 | 37 | def getProcessInfo(self) -> Awaitable[Dict]: 38 | """ 39 | Returns information about all running processes. 40 | 41 | See `https://chromedevtools.github.io/devtools-protocol/tot/SystemInfo#method-getProcessInfo` 42 | 43 | :return: The results of the command 44 | """ 45 | return self.client.send("SystemInfo.getProcessInfo", {}) 46 | -------------------------------------------------------------------------------- /cripy/protocol/testing.py: -------------------------------------------------------------------------------- 1 | """This is an auto-generated file. Modify at your own risk""" 2 | from typing import Awaitable, Dict, List, Optional, Union, TYPE_CHECKING 3 | 4 | if TYPE_CHECKING: 5 | from cripy import ConnectionType, SessionType 6 | 7 | __all__ = ["Testing"] 8 | 9 | 10 | class Testing: 11 | """ 12 | Testing domain is a dumping ground for the capabilities requires for browser or app testing that do not fit other 13 | domains. 14 | """ 15 | 16 | __slots__ = ["client"] 17 | 18 | def __init__(self, client: Union["ConnectionType", "SessionType"]) -> None: 19 | self.client: Union["ConnectionType", "SessionType"] = client 20 | 21 | def generateTestReport( 22 | self, message: str, group: Optional[str] = None 23 | ) -> Awaitable[Dict]: 24 | """ 25 | Generates a report for testing. 26 | 27 | :param message: Message to be displayed in the report. 28 | :type message: str 29 | :param group: Specifies the endpoint group to deliver the report to. 30 | :type group: Optional[str] 31 | """ 32 | msg_dict = {} 33 | if message is not None: 34 | msg_dict["message"] = message 35 | if group is not None: 36 | msg_dict["group"] = group 37 | return self.client.send("Testing.generateTestReport", msg_dict) 38 | -------------------------------------------------------------------------------- /cripy/protocol/tethering.py: -------------------------------------------------------------------------------- 1 | """This is an auto-generated file. Modify at your own risk""" 2 | from typing import Awaitable, Any, Callable, Dict, List, Optional, Union, TYPE_CHECKING 3 | 4 | if TYPE_CHECKING: 5 | from cripy import ConnectionType, SessionType 6 | 7 | __all__ = ["Tethering"] 8 | 9 | 10 | class Tethering: 11 | """ 12 | The Tethering domain defines methods and events for browser port binding. 13 | Status: Experimental 14 | 15 | See `https://chromedevtools.github.io/devtools-protocol/tot/Tethering` 16 | """ 17 | 18 | __slots__ = ["client"] 19 | 20 | def __init__(self, client: Union["ConnectionType", "SessionType"]) -> None: 21 | """Initialize a new instance of Tethering 22 | 23 | :param client: The client instance to be used to communicate with the remote browser instance 24 | """ 25 | self.client: Union["ConnectionType", "SessionType"] = client 26 | 27 | def bind(self, port: int) -> Awaitable[Dict]: 28 | """ 29 | Request browser port binding. 30 | 31 | See `https://chromedevtools.github.io/devtools-protocol/tot/Tethering#method-bind` 32 | 33 | :param port: Port number to bind. 34 | :return: The results of the command 35 | """ 36 | return self.client.send("Tethering.bind", {"port": port}) 37 | 38 | def unbind(self, port: int) -> Awaitable[Dict]: 39 | """ 40 | Request browser port unbinding. 41 | 42 | See `https://chromedevtools.github.io/devtools-protocol/tot/Tethering#method-unbind` 43 | 44 | :param port: Port number to unbind. 45 | :return: The results of the command 46 | """ 47 | return self.client.send("Tethering.unbind", {"port": port}) 48 | 49 | def accepted( 50 | self, listener: Optional[Callable[[Dict[str, Any]], Any]] = None 51 | ) -> Any: 52 | """ 53 | Informs that port was successfully bound and got a specified connection id. 54 | 55 | See `https://chromedevtools.github.io/devtools-protocol/tot/Tethering#event-accepted` 56 | 57 | :param listener: Optional listener function 58 | :return: If a listener was supplied the return value is a callable that 59 | will remove the supplied listener otherwise a future that resolves 60 | with the value of the event 61 | """ 62 | event_name = "Tethering.accepted" 63 | if listener is None: 64 | future = self.client.loop.create_future() 65 | 66 | def _listener(event: Optional[Dict] = None) -> None: 67 | future.set_result(event) 68 | 69 | self.client.once(event_name, _listener) 70 | 71 | return future 72 | 73 | self.client.on(event_name, listener) 74 | return lambda: self.client.remove_listener(event_name, listener) 75 | -------------------------------------------------------------------------------- /cripy/protocol/tracing.py: -------------------------------------------------------------------------------- 1 | """This is an auto-generated file. Modify at your own risk""" 2 | from typing import Awaitable, Any, Callable, Dict, List, Optional, Union, TYPE_CHECKING 3 | 4 | if TYPE_CHECKING: 5 | from cripy import ConnectionType, SessionType 6 | 7 | __all__ = ["Tracing"] 8 | 9 | 10 | class Tracing: 11 | """ 12 | Domain Dependencies: 13 | * IO 14 | Status: Experimental 15 | 16 | See `https://chromedevtools.github.io/devtools-protocol/tot/Tracing` 17 | """ 18 | 19 | __slots__ = ["client"] 20 | 21 | def __init__(self, client: Union["ConnectionType", "SessionType"]) -> None: 22 | """Initialize a new instance of Tracing 23 | 24 | :param client: The client instance to be used to communicate with the remote browser instance 25 | """ 26 | self.client: Union["ConnectionType", "SessionType"] = client 27 | 28 | def end(self) -> Awaitable[Dict]: 29 | """ 30 | Stop trace events collection. 31 | 32 | See `https://chromedevtools.github.io/devtools-protocol/tot/Tracing#method-end` 33 | 34 | :return: The results of the command 35 | """ 36 | return self.client.send("Tracing.end", {}) 37 | 38 | def getCategories(self) -> Awaitable[Dict]: 39 | """ 40 | Gets supported tracing categories. 41 | 42 | See `https://chromedevtools.github.io/devtools-protocol/tot/Tracing#method-getCategories` 43 | 44 | :return: The results of the command 45 | """ 46 | return self.client.send("Tracing.getCategories", {}) 47 | 48 | def recordClockSyncMarker(self, syncId: str) -> Awaitable[Dict]: 49 | """ 50 | Record a clock sync marker in the trace. 51 | 52 | See `https://chromedevtools.github.io/devtools-protocol/tot/Tracing#method-recordClockSyncMarker` 53 | 54 | :param syncId: The ID of this clock sync marker 55 | :return: The results of the command 56 | """ 57 | return self.client.send("Tracing.recordClockSyncMarker", {"syncId": syncId}) 58 | 59 | def requestMemoryDump(self) -> Awaitable[Dict]: 60 | """ 61 | Request a global memory dump. 62 | 63 | See `https://chromedevtools.github.io/devtools-protocol/tot/Tracing#method-requestMemoryDump` 64 | 65 | :return: The results of the command 66 | """ 67 | return self.client.send("Tracing.requestMemoryDump", {}) 68 | 69 | def start( 70 | self, 71 | categories: Optional[str] = None, 72 | options: Optional[str] = None, 73 | bufferUsageReportingInterval: Optional[Union[int, float]] = None, 74 | transferMode: Optional[str] = None, 75 | streamFormat: Optional[str] = None, 76 | streamCompression: Optional[str] = None, 77 | traceConfig: Optional[Dict[str, Any]] = None, 78 | ) -> Awaitable[Dict]: 79 | """ 80 | Start trace events collection. 81 | 82 | See `https://chromedevtools.github.io/devtools-protocol/tot/Tracing#method-start` 83 | 84 | :param categories: Category/tag filter 85 | :param options: Tracing options 86 | :param bufferUsageReportingInterval: If set, the agent will issue bufferUsage events at this interval, specified in milliseconds 87 | :param transferMode: Whether to report trace events as series of dataCollected events or to save trace to a 88 | stream (defaults to `ReportEvents`). 89 | :param streamFormat: Trace data format to use. This only applies when using `ReturnAsStream` 90 | transfer mode (defaults to `json`). 91 | :param streamCompression: Compression format to use. This only applies when using `ReturnAsStream` 92 | transfer mode (defaults to `none`) 93 | :param traceConfig: The traceConfig 94 | :return: The results of the command 95 | """ 96 | msg = {} 97 | if categories is not None: 98 | msg["categories"] = categories 99 | if options is not None: 100 | msg["options"] = options 101 | if bufferUsageReportingInterval is not None: 102 | msg["bufferUsageReportingInterval"] = bufferUsageReportingInterval 103 | if transferMode is not None: 104 | msg["transferMode"] = transferMode 105 | if streamFormat is not None: 106 | msg["streamFormat"] = streamFormat 107 | if streamCompression is not None: 108 | msg["streamCompression"] = streamCompression 109 | if traceConfig is not None: 110 | msg["traceConfig"] = traceConfig 111 | return self.client.send("Tracing.start", msg) 112 | 113 | def bufferUsage( 114 | self, listener: Optional[Callable[[Dict[str, Any]], Any]] = None 115 | ) -> Any: 116 | """ 117 | See `https://chromedevtools.github.io/devtools-protocol/tot/Tracing#event-bufferUsage` 118 | 119 | :param listener: Optional listener function 120 | :return: If a listener was supplied the return value is a callable that 121 | will remove the supplied listener otherwise a future that resolves 122 | with the value of the event 123 | """ 124 | event_name = "Tracing.bufferUsage" 125 | if listener is None: 126 | future = self.client.loop.create_future() 127 | 128 | def _listener(event: Optional[Dict] = None) -> None: 129 | future.set_result(event) 130 | 131 | self.client.once(event_name, _listener) 132 | 133 | return future 134 | 135 | self.client.on(event_name, listener) 136 | return lambda: self.client.remove_listener(event_name, listener) 137 | 138 | def dataCollected( 139 | self, listener: Optional[Callable[[Dict[str, Any]], Any]] = None 140 | ) -> Any: 141 | """ 142 | Contains an bucket of collected trace events. When tracing is stopped collected events will be 143 | send as a sequence of dataCollected events followed by tracingComplete event. 144 | 145 | See `https://chromedevtools.github.io/devtools-protocol/tot/Tracing#event-dataCollected` 146 | 147 | :param listener: Optional listener function 148 | :return: If a listener was supplied the return value is a callable that 149 | will remove the supplied listener otherwise a future that resolves 150 | with the value of the event 151 | """ 152 | event_name = "Tracing.dataCollected" 153 | if listener is None: 154 | future = self.client.loop.create_future() 155 | 156 | def _listener(event: Optional[Dict] = None) -> None: 157 | future.set_result(event) 158 | 159 | self.client.once(event_name, _listener) 160 | 161 | return future 162 | 163 | self.client.on(event_name, listener) 164 | return lambda: self.client.remove_listener(event_name, listener) 165 | 166 | def tracingComplete( 167 | self, listener: Optional[Callable[[Dict[str, Any]], Any]] = None 168 | ) -> Any: 169 | """ 170 | Signals that tracing is stopped and there is no trace buffers pending flush, all data were 171 | delivered via dataCollected events. 172 | 173 | See `https://chromedevtools.github.io/devtools-protocol/tot/Tracing#event-tracingComplete` 174 | 175 | :param listener: Optional listener function 176 | :return: If a listener was supplied the return value is a callable that 177 | will remove the supplied listener otherwise a future that resolves 178 | with the value of the event 179 | """ 180 | event_name = "Tracing.tracingComplete" 181 | if listener is None: 182 | future = self.client.loop.create_future() 183 | 184 | def _listener(event: Optional[Dict] = None) -> None: 185 | future.set_result(event) 186 | 187 | self.client.once(event_name, _listener) 188 | 189 | return future 190 | 191 | self.client.on(event_name, listener) 192 | return lambda: self.client.remove_listener(event_name, listener) 193 | -------------------------------------------------------------------------------- /cripy/protocol/webaudio.py: -------------------------------------------------------------------------------- 1 | """This is an auto-generated file. Modify at your own risk""" 2 | from typing import Awaitable, Any, Callable, Dict, List, Optional, Union, TYPE_CHECKING 3 | 4 | if TYPE_CHECKING: 5 | from cripy import ConnectionType, SessionType 6 | 7 | __all__ = ["WebAudio"] 8 | 9 | 10 | class WebAudio: 11 | """ 12 | This domain allows inspection of Web Audio API. 13 | https://webaudio.github.io/web-audio-api/ 14 | Status: Experimental 15 | 16 | See `https://chromedevtools.github.io/devtools-protocol/tot/WebAudio` 17 | """ 18 | 19 | __slots__ = ["client"] 20 | 21 | def __init__(self, client: Union["ConnectionType", "SessionType"]) -> None: 22 | """Initialize a new instance of WebAudio 23 | 24 | :param client: The client instance to be used to communicate with the remote browser instance 25 | """ 26 | self.client: Union["ConnectionType", "SessionType"] = client 27 | 28 | def enable(self) -> Awaitable[Dict]: 29 | """ 30 | Enables the WebAudio domain and starts sending context lifetime events. 31 | 32 | See `https://chromedevtools.github.io/devtools-protocol/tot/WebAudio#method-enable` 33 | 34 | :return: The results of the command 35 | """ 36 | return self.client.send("WebAudio.enable", {}) 37 | 38 | def disable(self) -> Awaitable[Dict]: 39 | """ 40 | Disables the WebAudio domain. 41 | 42 | See `https://chromedevtools.github.io/devtools-protocol/tot/WebAudio#method-disable` 43 | 44 | :return: The results of the command 45 | """ 46 | return self.client.send("WebAudio.disable", {}) 47 | 48 | def getRealtimeData(self, contextId: str) -> Awaitable[Dict]: 49 | """ 50 | Fetch the realtime data from the registered contexts. 51 | 52 | See `https://chromedevtools.github.io/devtools-protocol/tot/WebAudio#method-getRealtimeData` 53 | 54 | :param contextId: The contextId 55 | :return: The results of the command 56 | """ 57 | return self.client.send("WebAudio.getRealtimeData", {"contextId": contextId}) 58 | 59 | def contextCreated( 60 | self, listener: Optional[Callable[[Dict[str, Any]], Any]] = None 61 | ) -> Any: 62 | """ 63 | Notifies that a new BaseAudioContext has been created. 64 | 65 | See `https://chromedevtools.github.io/devtools-protocol/tot/WebAudio#event-contextCreated` 66 | 67 | :param listener: Optional listener function 68 | :return: If a listener was supplied the return value is a callable that 69 | will remove the supplied listener otherwise a future that resolves 70 | with the value of the event 71 | """ 72 | event_name = "WebAudio.contextCreated" 73 | if listener is None: 74 | future = self.client.loop.create_future() 75 | 76 | def _listener(event: Optional[Dict] = None) -> None: 77 | future.set_result(event) 78 | 79 | self.client.once(event_name, _listener) 80 | 81 | return future 82 | 83 | self.client.on(event_name, listener) 84 | return lambda: self.client.remove_listener(event_name, listener) 85 | 86 | def contextDestroyed( 87 | self, listener: Optional[Callable[[Dict[str, Any]], Any]] = None 88 | ) -> Any: 89 | """ 90 | Notifies that existing BaseAudioContext has been destroyed. 91 | 92 | See `https://chromedevtools.github.io/devtools-protocol/tot/WebAudio#event-contextDestroyed` 93 | 94 | :param listener: Optional listener function 95 | :return: If a listener was supplied the return value is a callable that 96 | will remove the supplied listener otherwise a future that resolves 97 | with the value of the event 98 | """ 99 | event_name = "WebAudio.contextDestroyed" 100 | if listener is None: 101 | future = self.client.loop.create_future() 102 | 103 | def _listener(event: Optional[Dict] = None) -> None: 104 | future.set_result(event) 105 | 106 | self.client.once(event_name, _listener) 107 | 108 | return future 109 | 110 | self.client.on(event_name, listener) 111 | return lambda: self.client.remove_listener(event_name, listener) 112 | 113 | def contextChanged( 114 | self, listener: Optional[Callable[[Dict[str, Any]], Any]] = None 115 | ) -> Any: 116 | """ 117 | Notifies that existing BaseAudioContext has changed some properties (id stays the same).. 118 | 119 | See `https://chromedevtools.github.io/devtools-protocol/tot/WebAudio#event-contextChanged` 120 | 121 | :param listener: Optional listener function 122 | :return: If a listener was supplied the return value is a callable that 123 | will remove the supplied listener otherwise a future that resolves 124 | with the value of the event 125 | """ 126 | event_name = "WebAudio.contextChanged" 127 | if listener is None: 128 | future = self.client.loop.create_future() 129 | 130 | def _listener(event: Optional[Dict] = None) -> None: 131 | future.set_result(event) 132 | 133 | self.client.once(event_name, _listener) 134 | 135 | return future 136 | 137 | self.client.on(event_name, listener) 138 | return lambda: self.client.remove_listener(event_name, listener) 139 | -------------------------------------------------------------------------------- /cripy/protogen/__init__.py: -------------------------------------------------------------------------------- 1 | from .cdp import CDPType, Command, Domain, Event, Param, Property, ReturnValue 2 | from .generate import ( 3 | dynamically_generate_domains, 4 | generate_domains, 5 | generate_protocol_clazzs, 6 | get_default_templates, 7 | ) 8 | 9 | __all__ = [ 10 | "CDPType", 11 | "Command", 12 | "Domain", 13 | "dynamically_generate_domains", 14 | "Event", 15 | "generate_domains", 16 | "generate_protocol_clazzs", 17 | "get_default_templates", 18 | "Param", 19 | "Property", 20 | "ReturnValue", 21 | ] 22 | -------------------------------------------------------------------------------- /cripy/protogen/generate.py: -------------------------------------------------------------------------------- 1 | from asyncio import AbstractEventLoop, get_event_loop 2 | from collections import defaultdict 3 | from copy import deepcopy 4 | from pathlib import Path 5 | from types import ModuleType 6 | from typing import Any, DefaultDict, Dict, Generator, List, Optional, Tuple 7 | 8 | import aiofiles 9 | from jinja2 import Template 10 | 11 | from .cdp import CDPType, Command, Domain, Event 12 | from ..templates import SIMPLE_COMMANDS_PATH, SIMPLE_PROTO_INIT_PATH 13 | 14 | __all__ = [ 15 | "dynamically_generate_domains", 16 | "generate_domains", 17 | "generate_protocol_clazzs", 18 | "get_default_templates", 19 | ] 20 | 21 | 22 | def generate_domains(cdp_domains: List[Dict]) -> Generator[Domain, None, None]: 23 | domain_types: DefaultDict[str, Dict[str, CDPType]] = defaultdict(dict) 24 | types_: List[CDPType] = [] 25 | add_type = types_.append 26 | create_type = CDPType.create 27 | create_command = Command.create 28 | create_event = Event.create 29 | 30 | for cdp_domain in cdp_domains: 31 | types = cdp_domain.get("types") 32 | if types is not None: 33 | dname = cdp_domain.get("domain") 34 | for domain_type in cdp_domain.get("types"): 35 | t = create_type(dname, domain_type) 36 | domain_types[dname][t.name] = t 37 | add_type(t) 38 | 39 | for dt in types_: 40 | dt.qualify_types(domain_types) 41 | 42 | for cdp_domain in cdp_domains: 43 | domain_name = cdp_domain.get("domain") 44 | cdp_domain["types"] = domain_types.get(domain_name) 45 | 46 | cdp_commands: Optional[List[Dict]] = cdp_domain.get("commands") 47 | if cdp_commands: 48 | commands: List[Command] = [] 49 | add_command = commands.append 50 | for command in cdp_commands: 51 | add_command(create_command(domain_name, command, domain_types)) 52 | cdp_domain["commands"] = commands 53 | 54 | cdp_events: Optional[List[Dict]] = cdp_domain.get("events") 55 | if cdp_events: 56 | events: List[Event] = [] 57 | add_event = events.append 58 | for event in cdp_events: 59 | add_event(create_event(domain_name, event, domain_types)) 60 | cdp_domain["events"] = events 61 | 62 | yield Domain(**cdp_domain) 63 | 64 | 65 | def generate_protocol_clazzs(cdp_domains: List[Dict], protocol_dir: Path) -> None: 66 | with open(SIMPLE_COMMANDS_PATH, "r") as iin: 67 | command_template = Template(iin.read(), trim_blocks=True, lstrip_blocks=True) 68 | with open(SIMPLE_PROTO_INIT_PATH, "r") as iin: 69 | init_template = Template(iin.read(), trim_blocks=True, lstrip_blocks=True) 70 | render_clazz = command_template.render 71 | inits = [] 72 | add_init = inits.append 73 | for domain in generate_domains(cdp_domains): 74 | name = domain.name 75 | name_lower = name.lower() 76 | with (protocol_dir / f"{name_lower}.py").open("w") as out: 77 | out.write(render_clazz(d=domain)) 78 | add_init((name_lower, name)) 79 | with (protocol_dir / "__init__.py").open("w") as out: 80 | out.write(init_template.render(domains=sorted(inits, key=lambda x: x[1]))) 81 | 82 | 83 | async def dynamically_generate_domains( 84 | protocol_info: Dict, loop: Optional[AbstractEventLoop] = None 85 | ) -> Dict[str, Any]: 86 | if loop is None: 87 | loop = get_event_loop() 88 | async with aiofiles.open(SIMPLE_COMMANDS_PATH, mode="r", loop=loop) as iin: 89 | command_template = Template( 90 | await iin.read(), trim_blocks=True, lstrip_blocks=True 91 | ) 92 | make_class = command_template.render 93 | domain_classes = {} 94 | domains = deepcopy(protocol_info["domains"]) 95 | for domain in generate_domains(domains): 96 | domain_name = domain.domain.lower() 97 | code = compile( 98 | make_class(d=domain), f"cripy/protocoldyn/{domain_name}.py", "exec" 99 | ) 100 | module = ModuleType(f"cripy.protocoldyn.{domain_name}") 101 | exec(code, module.__dict__) 102 | name = code.co_names[-1] 103 | domain_classes[name] = getattr(module, domain.domain) 104 | return domain_classes 105 | 106 | 107 | def get_default_templates() -> Tuple[Template, Template]: 108 | with open(SIMPLE_COMMANDS_PATH, "r") as iin: 109 | command_template = Template(iin.read(), trim_blocks=True, lstrip_blocks=True) 110 | with open(SIMPLE_PROTO_INIT_PATH, "r") as iin: 111 | pinit = Template(iin.read(), trim_blocks=True, lstrip_blocks=True) 112 | return command_template, pinit 113 | -------------------------------------------------------------------------------- /cripy/target_session.py: -------------------------------------------------------------------------------- 1 | from typing import Dict, TYPE_CHECKING, Union 2 | 3 | from .connection import CDPSession 4 | from .protocol import ( 5 | Accessibility, 6 | Animation, 7 | ApplicationCache, 8 | Audits, 9 | BackgroundService, 10 | Browser, 11 | CSS, 12 | CacheStorage, 13 | Cast, 14 | Console, 15 | DOM, 16 | DOMDebugger, 17 | DOMSnapshot, 18 | DOMStorage, 19 | Database, 20 | Debugger, 21 | DeviceOrientation, 22 | Emulation, 23 | Fetch, 24 | HeadlessExperimental, 25 | HeapProfiler, 26 | IO, 27 | IndexedDB, 28 | Input, 29 | Inspector, 30 | LayerTree, 31 | Log, 32 | Memory, 33 | Network, 34 | Overlay, 35 | Page, 36 | Performance, 37 | Profiler, 38 | Runtime, 39 | Schema, 40 | Security, 41 | ServiceWorker, 42 | Storage, 43 | SystemInfo, 44 | Target, 45 | Tethering, 46 | Tracing, 47 | WebAudio, 48 | ) 49 | 50 | if TYPE_CHECKING: 51 | from .client import Client, ClientDynamic 52 | 53 | __all__ = ["TargetSession", "TargetSessionDynamic"] 54 | 55 | 56 | class TargetSession(CDPSession): 57 | __slots__ = [ 58 | "Accessibility", 59 | "Animation", 60 | "ApplicationCache", 61 | "Audits", 62 | "BackgroundService", 63 | "Browser", 64 | "CSS", 65 | "CacheStorage", 66 | "Cast", 67 | "Console", 68 | "DOM", 69 | "DOMDebugger", 70 | "DOMSnapshot", 71 | "DOMStorage", 72 | "Database", 73 | "Debugger", 74 | "DeviceOrientation", 75 | "Emulation", 76 | "Fetch", 77 | "HeadlessExperimental", 78 | "HeapProfiler", 79 | "IO", 80 | "IndexedDB", 81 | "Input", 82 | "Inspector", 83 | "LayerTree", 84 | "Log", 85 | "Memory", 86 | "Network", 87 | "Overlay", 88 | "Page", 89 | "Performance", 90 | "Profiler", 91 | "Runtime", 92 | "Schema", 93 | "Security", 94 | "ServiceWorker", 95 | "Storage", 96 | "SystemInfo", 97 | "Target", 98 | "Tethering", 99 | "Tracing", 100 | "WebAudio", 101 | ] 102 | 103 | def __init__( 104 | self, 105 | client: Union["Client", "TargetSession"], 106 | target_type: str, 107 | session_id: str, 108 | flat_session: bool = False, 109 | ) -> None: 110 | """Creat a new TargetSession 111 | 112 | :param client: The client or connection to be used 113 | :param target_type: The type of the target 114 | :param session_id: The session id for communication with the target 115 | :param flat_session: Is flat session mode enabled 116 | """ 117 | super().__init__(client, target_type, session_id, flat_session) 118 | self.Accessibility: Accessibility = Accessibility(self) 119 | self.Animation: Animation = Animation(self) 120 | self.ApplicationCache: ApplicationCache = ApplicationCache(self) 121 | self.Audits: Audits = Audits(self) 122 | self.Browser: Browser = Browser(self) 123 | self.BackgroundService: BackgroundService = BackgroundService(self) 124 | self.CacheStorage: CacheStorage = CacheStorage(self) 125 | self.Cast: Cast = Cast(self) 126 | self.Console: Console = Console(self) 127 | self.CSS: CSS = CSS(self) 128 | self.Database: Database = Database(self) 129 | self.Debugger: Debugger = Debugger(self) 130 | self.DeviceOrientation: DeviceOrientation = DeviceOrientation(self) 131 | self.DOM: DOM = DOM(self) 132 | self.DOMDebugger: DOMDebugger = DOMDebugger(self) 133 | self.DOMSnapshot: DOMSnapshot = DOMSnapshot(self) 134 | self.DOMStorage: DOMStorage = DOMStorage(self) 135 | self.Emulation: Emulation = Emulation(self) 136 | self.Fetch: Fetch = Fetch(self) 137 | self.HeadlessExperimental: HeadlessExperimental = HeadlessExperimental(self) 138 | self.HeapProfiler: HeapProfiler = HeapProfiler(self) 139 | self.IO: IO = IO(self) 140 | self.IndexedDB: IndexedDB = IndexedDB(self) 141 | self.Input: Input = Input(self) 142 | self.Inspector: Inspector = Inspector(self) 143 | self.LayerTree: LayerTree = LayerTree(self) 144 | self.Log: Log = Log(self) 145 | self.Memory: Memory = Memory(self) 146 | self.Network: Network = Network(self) 147 | self.Overlay: Overlay = Overlay(self) 148 | self.Page: Page = Page(self) 149 | self.Performance: Performance = Performance(self) 150 | self.Profiler: Profiler = Profiler(self) 151 | self.Runtime: Runtime = Runtime(self) 152 | self.Schema: Schema = Schema(self) 153 | self.Security: Security = Security(self) 154 | self.ServiceWorker: ServiceWorker = ServiceWorker(self) 155 | self.Storage: Storage = Storage(self) 156 | self.SystemInfo: SystemInfo = SystemInfo(self) 157 | self.Target: Target = Target(self) 158 | self.Tethering: Tethering = Tethering(self) 159 | self.Tracing: Tracing = Tracing(self) 160 | self.WebAudio: WebAudio = WebAudio(self) 161 | 162 | def create_session(self, target_type: str, session_id: str) -> "TargetSession": 163 | """Creates a new session for the target being connected to specified 164 | by the session_id 165 | 166 | :param target_type: The type of the target being connected to 167 | :param session_id: The session id used to communicate to the target 168 | :return: A new session connected to the target 169 | """ 170 | connection = self._connection if self._flat_session else self 171 | session = TargetSession( 172 | connection, target_type, session_id, flat_session=self._flat_session 173 | ) 174 | if self._flat_session: 175 | self._connection.add_session(session) 176 | else: 177 | self._sessions[session_id] = session 178 | return session 179 | 180 | 181 | class TargetSessionDynamic(CDPSession): 182 | def __init__( 183 | self, 184 | client: Union["ClientDynamic", "TargetSessionDynamic"], 185 | target_type: str, 186 | session_id: str, 187 | flat_session: bool = False, 188 | proto_def: Dict = None, 189 | ) -> None: 190 | """Creat a new TargetSession 191 | 192 | :param client: The client or connection to be used 193 | :param target_type: The type of the target 194 | :param session_id: The session id for communication with the target 195 | :param flat_session: Is flat session mode enabled 196 | :param proto_def: The CDP protocol definition 197 | """ 198 | super().__init__(client, target_type, session_id, flat_session) 199 | self._proto_def: Dict = proto_def 200 | for domain, clazz in proto_def.items(): 201 | setattr(self, domain, clazz(self)) 202 | 203 | def create_session( 204 | self, target_type: str, session_id: str 205 | ) -> "TargetSessionDynamic": 206 | """Creates a new session for the target being connected to specified 207 | by the session_id 208 | 209 | :param target_type: The type of the target being connected to 210 | :param session_id: The session id used to communicate to the target 211 | :return: A new session connected to the target 212 | """ 213 | connection = self._connection if self._flat_session else self 214 | session = TargetSessionDynamic( 215 | connection, 216 | target_type, 217 | session_id, 218 | flat_session=self._flat_session, 219 | proto_def=self._proto_def, 220 | ) 221 | if self._flat_session: 222 | self._connection.add_session(session) 223 | else: 224 | self._sessions[session_id] = session 225 | return session 226 | -------------------------------------------------------------------------------- /cripy/templates/__init__.py: -------------------------------------------------------------------------------- 1 | import os.path 2 | 3 | __all__ = ["SIMPLE_COMMANDS_PATH", "SIMPLE_PROTO_INIT_PATH"] 4 | 5 | THIS_PATH = os.path.dirname(__file__) 6 | SIMPLE_COMMANDS_PATH = os.path.join(THIS_PATH, "simple", "commands.async.py.j2") 7 | SIMPLE_PROTO_INIT_PATH = os.path.join(THIS_PATH, "simple", "protocol_init.py.j2") 8 | -------------------------------------------------------------------------------- /cripy/templates/full/commands.async.py.j2: -------------------------------------------------------------------------------- 1 | {% set dl = d.domain | lower %} 2 | from typing import Any, List, Optional, Union 3 | {% for import in timports %} 4 | from cripy.asyncio.protocol.{{ import | lower }} import types as {{ import }} 5 | {% endfor %} 6 | {% if d.has_events %} 7 | from cripy.asyncio.protocol.{{ dl }} import events as Events 8 | {% endif %} 9 | {% if d.has_types %} 10 | from cripy.asyncio.protocol.{{ dl }} import types as Types 11 | {% endif %} 12 | 13 | __all__ = ["{{ d.domain }}"] 14 | 15 | 16 | class {{ d.domain }}(object): 17 | {% if d.description %} 18 | """ 19 | {{ d.description }} 20 | """ 21 | 22 | {% endif %} 23 | {% if d.has_deps %} 24 | {{ d.dep_list_str }} 25 | 26 | {% endif %} 27 | {% if d.has_events %} 28 | events = Events.{{ d.domain | upper }}_EVENTS_NS 29 | 30 | {% endif %} 31 | def __init__(self, chrome): 32 | """ 33 | Construct a new {{ d.domain }} object 34 | 35 | :param chrome: An instance of the devtools protocol client 36 | """ 37 | self.chrome = chrome 38 | 39 | {% for command in d.commands %} 40 | {% if command.has_parameters %} 41 | async def {{ command.name }}(self, {{ command.command_arg_string }}) -> Optional[dict]: 42 | {% else %} 43 | async def {{ command.name }}(self) -> Optional[dict]: 44 | {% endif %} 45 | {% if command.has_parameters %} 46 | """ 47 | {% if command.description %} 48 | {{ command.description }} 49 | 50 | {% endif %} 51 | {% for prop in command.parameters %} 52 | {% if prop.description %} 53 | :param {{ prop.name }}: {{ prop.nice_description }} 54 | {% else %} 55 | :param {{ prop.name }}: The {{ prop.name }} 56 | {% endif %} 57 | :type {{ prop.name }}: {{ prop.constructor_docstr }} 58 | {% endfor %} 59 | """ 60 | {% elif command.description %} 61 | """ 62 | {{ command.description }} 63 | """ 64 | {% endif %} 65 | {% if command.has_parameters %} 66 | msg_dict = dict() 67 | {% for param in command.parameters %} 68 | if {{ param.name }} is not None: 69 | msg_dict['{{ param.name }}'] = {{ param.name }} 70 | {% endfor %} 71 | res = await self.chrome.send('{{ command.scoped_name }}', msg_dict) 72 | {% else %} 73 | res = await self.chrome.send('{{ command.scoped_name }}') 74 | {% endif %} 75 | {% if command.returns %} 76 | {% for trans in command.returns.yield_result_trans() %} 77 | {{ trans }} 78 | {% endfor %} 79 | return res 80 | {% else %} 81 | return res 82 | {% endif %} 83 | 84 | {% endfor %} 85 | {% if events %} 86 | {% for e, _ in events %} 87 | def {{ onEvent(e) }}(self, fn, once=False): 88 | if once: 89 | self.chrome.once("{{ e }}", fn) 90 | else: 91 | self.chrome.on("{{ e }}", fn) 92 | 93 | {% endfor %} 94 | {% endif %} 95 | @staticmethod 96 | def get_event_classes() -> Optional[dict]: 97 | """ 98 | Retrieve a dictionary of events emitted by the {{ dup }} domain to their python class 99 | 100 | If {{ dup }} has events this method returns a dictionary of 101 | fully qualified event name (str) to it python class 102 | 103 | :return: Dictionary of the {{ dup }} domain event classes 104 | :retype: Optional[dict] 105 | """ 106 | {% if d.has_events %} 107 | return Events.{{ d.domain | upper }}_EVENTS_TO_CLASS 108 | {% else %} 109 | return None 110 | {% endif %} 111 | 112 | 113 | -------------------------------------------------------------------------------- /cripy/templates/full/commands.py.j2: -------------------------------------------------------------------------------- 1 | {% set dl = d.domain | lower %} 2 | {% for import in timports %} 3 | from cripy.gevent.protocol.{{ import | lower }} import types as {{ import }} 4 | {% endfor %} 5 | {% if d.has_events %} 6 | from cripy.gevent.protocol.{{ dl }} import events as Events 7 | {% endif %} 8 | {% if d.has_types %} 9 | from cripy.gevent.protocol.{{ dl }} import types as Types 10 | {% endif %} 11 | 12 | __all__ = ["{{ d.domain }}"] 13 | 14 | 15 | class {{ d.domain }}(object): 16 | {% if d.description %} 17 | """ 18 | {{ d.description }} 19 | """ 20 | 21 | {% endif %} 22 | {% if d.has_deps %} 23 | {{ d.dep_list_str }} 24 | 25 | {% endif %} 26 | {% if d.has_events %} 27 | events = Events.{{ d.domain | upper }}_EVENTS_NS 28 | 29 | {% endif %} 30 | def __init__(self, chrome): 31 | """ 32 | Construct a new {{ d.domain }} object 33 | 34 | :param chrome: An instance of the devtools protocol client 35 | """ 36 | self.chrome = chrome 37 | 38 | {% for command in d.commands %} 39 | {% if command.has_parameters %} 40 | def {{ command.name }}(self, {{ command.command_arg_string_no_types }}): 41 | {% else %} 42 | def {{ command.name }}(self): 43 | {% endif %} 44 | {% if command.has_parameters %} 45 | """ 46 | {% if command.description %} 47 | {{ command.description }} 48 | 49 | {% endif %} 50 | {% for prop in command.parameters %} 51 | {% if prop.description %} 52 | :param {{ prop.name }}: {{ prop.nice_description }} 53 | {% else %} 54 | :param {{ prop.name }}: The {{ prop.name }} 55 | {% endif %} 56 | :type {{ prop.name }}: {{ prop.constructor_docstr }} 57 | {% endfor %} 58 | """ 59 | {% elif command.description %} 60 | """ 61 | {{ command.description }} 62 | """ 63 | {% endif %} 64 | {% if command.has_parameters %} 65 | msg_dict = dict() 66 | {% for param in command.parameters %} 67 | if {{ param.name }} is not None: 68 | msg_dict['{{ param.name }}'] = {{ param.name }} 69 | {% endfor %} 70 | wres = self.chrome.send('{{ command.scoped_name }}', msg_dict) 71 | {% else %} 72 | wres = self.chrome.send('{{ command.scoped_name }}') 73 | {% endif %} 74 | {% if command.returns %} 75 | res = wres.get() 76 | {% for trans in command.returns.yield_result_trans() %} 77 | {{ trans }} 78 | {% endfor %} 79 | return res 80 | {% else %} 81 | return wres.get() 82 | {% endif %} 83 | 84 | {% endfor %} 85 | {% if events %} 86 | {% for e, _ in events %} 87 | def {{ onEvent(e) }}(self, fn, once=False): 88 | self.chrome.on("{{ e }}", fn, once=once) 89 | 90 | {% endfor %} 91 | {% endif %} 92 | @staticmethod 93 | def get_event_classes(): 94 | """ 95 | Retrieve a dictionary of events emitted by the {{ dup }} domain to their python class 96 | 97 | If {{ dup }} has events this method returns a dictionary of 98 | fully qualified event name (str) to it python class 99 | 100 | :return: Dictionary of the {{ dup }} domain event classes 101 | :retype: Optional[dict] 102 | """ 103 | {% if d.has_events %} 104 | return Events.{{ d.domain | upper }}_EVENTS_TO_CLASS 105 | {% else %} 106 | return None 107 | {% endif %} 108 | 109 | 110 | -------------------------------------------------------------------------------- /cripy/templates/full/domain_init.py.j2: -------------------------------------------------------------------------------- 1 | {% if d.has_events %} 2 | from .events import * 3 | {% endif %} 4 | {% if d.has_types %} 5 | from .types import * 6 | {% endif %} 7 | from .domain import * 8 | 9 | __all__ = domain.__all__{% if d.has_events %} + events.__all__{% endif %}{% if d.has_types %} + types.__all__{% endif %} 10 | {%+ if True %} 11 | 12 | {% endif %} -------------------------------------------------------------------------------- /cripy/templates/full/domain_type.py.j2: -------------------------------------------------------------------------------- 1 | from typing import Any, List, Optional, Union 2 | {% for import in timports %} 3 | from cripy.asyncio.protocol.{{ import | lower }} import types as {{ import }} 4 | {% endfor %} 5 | 6 | __all__ = [ 7 | {%+ for dt in obj_types %} 8 | "{{ dt.type_name }}", 9 | {% endfor %} 10 | "{{ domain | upper }}_TYPES_TO_OBJECT" 11 | ] 12 | 13 | 14 | {%+ for dt in obj_types %} 15 | {% if dt.has_properties %} 16 | class {{ dt.type_name }}(object): 17 | {% else %} 18 | class {{ dt.type_name }}(dict): 19 | {% endif %} 20 | {% if dt.description %} 21 | """ 22 | {{ dt.description }} 23 | """ 24 | 25 | {% endif %} 26 | {% if dt.has_properties %} 27 | __slots__ = [{{ slotgen(dt.properties) }}] 28 | 29 | def __init__(self, {{ dt.constructor_string }}) -> None: 30 | """ 31 | {% for prop in dt.properties %} 32 | {% if prop.description %} 33 | :param {{ prop.name }}: {{ prop.nice_description }} 34 | {% else %} 35 | :param {{ prop.name }}: The {{ prop.name }} 36 | {% endif %} 37 | :type {{ prop.name }}: {{ prop.constructor_docstr }} 38 | {% endfor %} 39 | """ 40 | super().__init__() 41 | {% for prop in dt.properties %} 42 | {{ prop.construct_thyself }} 43 | {# self.{{ prop.tinfo_str }} = {{ prop.name }}#} 44 | {% endfor %} 45 | {% endif %} 46 | 47 | def __repr__(self) -> str: 48 | {% if dt.has_properties %} 49 | repr_args = [] 50 | {% for prop in dt.properties %} 51 | if self.{{ prop.name }} is not None: 52 | repr_args.append("{{ prop.name }}={!r}".format(self.{{ prop.name }})) 53 | {% endfor %} 54 | return "{{ dt.type_name }}(" + ', '.join(repr_args)+")" 55 | {% else %} 56 | return "{{ dt.type_name }}(dict)" 57 | {% endif %} 58 | 59 | @staticmethod 60 | def safe_create(init: Optional[dict]) -> Optional[Union['{{ dt.type_name }}', dict]]: 61 | """ 62 | Safely create {{ dt.type_name }} from the supplied init dictionary. 63 | 64 | This method will not throw an Exception and will return a new instance of {{ dt.type_name }} 65 | if init is not None otherwise returns init or None if init was None. 66 | 67 | :param init: The init dictionary 68 | :type init: dict 69 | :return: A new instance of {{ dt.type_name }} if creation did not fail 70 | :rtype: Optional[Union[dict, {{ dt.type_name}}]] 71 | """ 72 | if init is not None: 73 | try: 74 | ourselves = {{ dt.type_name }}(**init) 75 | return ourselves 76 | except Exception: 77 | return init 78 | else: 79 | return init 80 | 81 | @staticmethod 82 | def safe_create_from_list(init: Optional[List[dict]]) -> Optional[List[Union['{{ dt.type_name }}', dict]]]: 83 | """ 84 | Safely create a new list {{ dt.type_name }}s from the supplied list of dictionaries. 85 | 86 | This method will not throw an Exception and will return a new list {{ dt.type_name }} instances 87 | if init is not None otherwise returns init or None if init was None. 88 | 89 | :param init: The init dictionary 90 | :type init: dict 91 | :return: A new list of {{ dt.type_name }} instances if creation did not fail 92 | :rtype: Optional[List[Union[dict, {{ dt.type_name}}]]] 93 | """ 94 | if init is not None: 95 | list_of_self = [] 96 | for it in init: 97 | list_of_self.append({{ dt.type_name }}.safe_create(it)) 98 | return list_of_self 99 | else: 100 | return init 101 | 102 | 103 | {% endfor %} 104 | {% if obj_types | length > 0 %} 105 | {{ domain | upper }}_TYPES_TO_OBJECT = { 106 | {% for dt in obj_types %} 107 | "{{ dt.type_name }}": {{ dt.type_name }}, 108 | {% endfor %} 109 | } 110 | {% endif %} -------------------------------------------------------------------------------- /cripy/templates/full/domain_typent.py.j2: -------------------------------------------------------------------------------- 1 | {% for import in timports %} 2 | from cripy.gevent.protocol.{{ import | lower }} import types as {{ import }} 3 | {% endfor %} 4 | 5 | __all__ = [ 6 | {%+ for dt in obj_types %} 7 | "{{ dt.type_name }}", 8 | {% endfor %} 9 | "{{ domain | upper }}_TYPE_TO_OBJECT" 10 | ] 11 | 12 | 13 | {%+ for dt in obj_types %} 14 | {% if dt.has_properties %} 15 | class {{ dt.type_name }}(object): 16 | {% else %} 17 | class {{ dt.type_name }}(dict): 18 | {% endif %} 19 | {% if dt.description %} 20 | """ 21 | {{ dt.description }} 22 | """ 23 | 24 | {% endif %} 25 | {% if dt.has_properties %} 26 | __slots__ = [{{ slotgen(dt.properties) }}] 27 | 28 | def __init__(self, {{ dt.constructor_string_no_type }}): 29 | """ 30 | {% for prop in dt.properties %} 31 | {% if prop.description %} 32 | :param {{ prop.name }}: {{ prop.nice_description }} 33 | {% else %} 34 | :param {{ prop.name }}: The {{ prop.name }} 35 | {% endif %} 36 | :type {{ prop.name }}: {{ prop.constructor_docstr }} 37 | {% endfor %} 38 | """ 39 | super({{ dt.type_name }}, self).__init__() 40 | {% for prop in dt.properties %} 41 | {{ prop.construct_thyself }} 42 | {# self.{{ prop.name }} = {{ prop.name }}#} 43 | {% endfor %} 44 | {% endif %} 45 | 46 | def __repr__(self): 47 | {% if dt.has_properties %} 48 | repr_args = [] 49 | {% for prop in dt.properties %} 50 | if self.{{ prop.name }} is not None: 51 | repr_args.append("{{ prop.name }}={!r}".format(self.{{ prop.name }})) 52 | {% endfor %} 53 | return "{{ dt.type_name }}(" + ', '.join(repr_args)+")" 54 | {% else %} 55 | return "{{ dt.type_name }}(dict)" 56 | {% endif %} 57 | 58 | @staticmethod 59 | def safe_create(init): 60 | """ 61 | Safely create {{ dt.type_name }} from the supplied init dictionary. 62 | 63 | This method will not throw an Exception and will return a new instance of {{ dt.type_name }} 64 | if init is not None otherwise returns init or None if init was None. 65 | 66 | :param init: The init dictionary 67 | :type init: dict 68 | :return: A new instance of {{ dt.type_name }} if creation did not fail 69 | :rtype: Optional[Union[dict, {{ dt.type_name}}]] 70 | """ 71 | if init is not None: 72 | try: 73 | ourselves = {{ dt.type_name }}(**init) 74 | return ourselves 75 | except Exception: 76 | return init 77 | else: 78 | return init 79 | 80 | @staticmethod 81 | def safe_create_from_list(init): 82 | """ 83 | Safely create a new list {{ dt.type_name }}s from the supplied list of dictionaries. 84 | 85 | This method will not throw an Exception and will return a new list {{ dt.type_name }} instances 86 | if init is not None otherwise returns init or None if init was None. 87 | 88 | :param init: The init dictionary 89 | :type init: dict 90 | :return: A new list of {{ dt.type_name }} instances if creation did not fail 91 | :rtype: Optional[List[Union[dict, {{ dt.type_name}}]]] 92 | """ 93 | if init is not None: 94 | list_of_self = [] 95 | for it in init: 96 | list_of_self.append({{ dt.type_name }}.safe_create(it)) 97 | return list_of_self 98 | else: 99 | return init 100 | 101 | 102 | {% endfor %} 103 | {% if obj_types | length > 0 %} 104 | {{ domain | upper }}_TYPE_TO_OBJECT = { 105 | {% for dt in obj_types %} 106 | "{{ dt.type_name }}": {{ dt.type_name }}, 107 | {% endfor %} 108 | } 109 | {% endif %} -------------------------------------------------------------------------------- /cripy/templates/full/events.py.j2: -------------------------------------------------------------------------------- 1 | from typing import Any, List, Optional, Union 2 | from collections import namedtuple 3 | {% for import in timports %} 4 | from cripy.asyncio.protocol.{{ import | lower }} import types as {{ import }} 5 | {% endfor %} 6 | {% if dhastype %} 7 | from cripy.asyncio.protocol.{{ domain | lower }}.types import * 8 | {% endif %} 9 | {% set dup = domain | upper %} 10 | 11 | __all__ = [ 12 | {%+ for event in events %} 13 | "{{event.class_name}}", 14 | {% endfor %} 15 | "{{ dup }}_EVENTS_TO_CLASS", 16 | "{{ domain | upper }}_EVENTS_NS" 17 | ] 18 | 19 | 20 | {%+ for event in events %} 21 | {% if event.has_parameters %} 22 | class {{ event.class_name }}(object): 23 | {% else %} 24 | class {{ event.class_name }}(dict): 25 | {% endif %} 26 | {% if event.description %} 27 | """ 28 | {{ event.description }} 29 | """ 30 | {% endif %} 31 | 32 | 33 | {% if event.has_parameters %} 34 | __slots__ = [{{ slotgen(event.parameters) }}] 35 | 36 | def __init__(self, {{ event.constructor_string }}) -> None: 37 | {% if event.has_parameters %} 38 | """ 39 | Create a new instance of {{ event.class_name }} 40 | 41 | {% for prop in event.parameters %} 42 | {% if prop.description %} 43 | :param {{ prop.name }}: {{ prop.nice_description }} 44 | {% else %} 45 | :param {{ prop.name }}: The {{ prop.name }} 46 | {% endif %} 47 | :type {{ prop.name }}: {{ prop.constructor_docstr }} 48 | {% endfor %} 49 | """ 50 | {% else %} 51 | """ 52 | Create a new instance of {{ event.class_name }} 53 | """ 54 | {% endif %} 55 | super().__init__() 56 | {% if event.has_parameters %} 57 | {% for prop in event.parameters %} 58 | {{ prop.construct_thyself }} 59 | {% endfor %} 60 | {% endif %} 61 | {% else %} 62 | def __init__(self, *args, **kwargs) -> None: 63 | super().__init__(*args, **kwargs) 64 | {% endif %} 65 | 66 | def __repr__(self) -> str: 67 | {% if event.has_parameters %} 68 | repr_args = [] 69 | {% for prop in event.parameters %} 70 | if self.{{ prop.name }} is not None: 71 | repr_args.append("{{ prop.name }}={!r}".format(self.{{ prop.name }})) 72 | {% endfor %} 73 | return "{{ event.class_name }}(" + ', '.join(repr_args)+")" 74 | {% else %} 75 | return "{{ event.class_name }}(dict)" 76 | {% endif %} 77 | 78 | @staticmethod 79 | def safe_create(init: Optional[dict]) -> Optional[Union['{{ event.class_name }}', dict]]: 80 | """ 81 | Safely create {{ event.class_name }} from the supplied init dictionary. 82 | 83 | This method will not throw an Exception and will return a new instance of {{ event.class_name }} 84 | if init is not None otherwise returns init or None if init was None. 85 | 86 | :param init: The init dictionary 87 | :type init: dict 88 | :return: A new instance of {{ event.class_name }} if creation did not fail 89 | :rtype: Optional[Union[dict, {{ event.class_name }}]] 90 | """ 91 | if init is not None: 92 | try: 93 | ourselves = {{ event.class_name }}(**init) 94 | return ourselves 95 | except Exception: 96 | return init 97 | else: 98 | return init 99 | 100 | @staticmethod 101 | def safe_create_from_list(init: Optional[List[dict]]) -> Optional[List[Union['{{ event.class_name }}', dict]]]: 102 | """ 103 | Safely create a new list {{ event.class_name }}s from the supplied list of dictionaries. 104 | 105 | This method will not throw an Exception and will return a new list {{ event.class_name }} instances 106 | if init is not None otherwise returns init or None if init was None. 107 | 108 | :param init: The init dictionary 109 | :type init: dict 110 | :return: A new list of {{ event.class_name }} instances if creation did not fail 111 | :rtype: Optional[List[Union[dict, {{ event.class_name }}]]] 112 | """ 113 | if init is not None: 114 | list_of_self = [] 115 | for it in init: 116 | list_of_self.append({{ event.class_name }}.safe_create(it)) 117 | return list_of_self 118 | else: 119 | return init 120 | 121 | 122 | {% endfor %} 123 | {{ dup }}_EVENTS_TO_CLASS = { 124 | {% for ename, clazz in event_to_clazz %} 125 | "{{ ename }}": {{ clazz }}, 126 | {% endfor %} 127 | } 128 | 129 | {{ domain }}NS = namedtuple("{{ domain }}NS", [{{ event_ns_init(event_to_clazz) }}]) 130 | 131 | {{ dup }}_EVENTS_NS = {{ domain }}NS( 132 | {% for ename, clazz in event_to_clazz %} 133 | {{ pascalcase(ename.split('.')[1]) }}="{{ ename }}", 134 | {% endfor %} 135 | ) 136 | 137 | -------------------------------------------------------------------------------- /cripy/templates/full/eventsnt.py.j2: -------------------------------------------------------------------------------- 1 | from collections import namedtuple 2 | {% for import in timports %} 3 | from cripy.gevent.protocol.{{ import | lower }} import types as {{ import }} 4 | {% endfor %} 5 | {% if dhastype %} 6 | from cripy.gevent.protocol.{{ domain | lower }}.types import * 7 | {% endif %} 8 | {% set dup = domain | upper %} 9 | 10 | __all__ = [ 11 | {%+ for event in events %} 12 | "{{event.class_name}}", 13 | {% endfor %} 14 | "{{ dup }}_EVENTS_TO_CLASS", 15 | "{{ domain | upper }}_EVENTS_NS" 16 | ] 17 | 18 | 19 | {%+ for event in events %} 20 | {% if event.has_parameters %} 21 | class {{ event.class_name }}(object): 22 | {% else %} 23 | class {{ event.class_name }}(dict): 24 | {% endif %} 25 | {% if event.description %} 26 | """ 27 | {{ event.description }} 28 | """ 29 | 30 | {% endif %} 31 | {% if event.has_parameters %} 32 | __slots__ = [{{ slotgen(event.parameters) }}] 33 | 34 | def __init__(self, {{ event.constructor_string_no_type }}): 35 | {% if event.has_parameters %} 36 | """ 37 | Create a new instance of {{ event.class_name }} 38 | 39 | {% for prop in event.parameters %} 40 | {% if prop.description %} 41 | :param {{ prop.name }}: {{ prop.nice_description }} 42 | {% else %} 43 | :param {{ prop.name }}: The {{ prop.name }} 44 | {% endif %} 45 | :type {{ prop.name }}: {{ prop.constructor_docstr }} 46 | {% endfor %} 47 | """ 48 | {% else %} 49 | """ 50 | Create a new instance of {{ event.class_name }} 51 | """ 52 | {% endif %} 53 | super({{ event.class_name }}, self).__init__() 54 | {% for prop in event.parameters %} 55 | {{ prop.construct_thyself }} 56 | {% endfor %} 57 | 58 | {% endif %} 59 | def __repr__(self): 60 | {% if event.has_parameters %} 61 | repr_args = [] 62 | {% for prop in event.parameters %} 63 | if self.{{ prop.name }} is not None: 64 | repr_args.append("{{ prop.name }}={!r}".format(self.{{ prop.name }})) 65 | {% endfor %} 66 | return "{{ event.class_name }}(" + ', '.join(repr_args)+")" 67 | {% else %} 68 | return "{{ event.class_name }}(dict)" 69 | {% endif %} 70 | 71 | @staticmethod 72 | def safe_create(init): 73 | """ 74 | Safely create {{ event.class_name }} from the supplied init dictionary. 75 | 76 | This method will not throw an Exception and will return a new instance of {{ event.class_name }} 77 | if init is not None otherwise returns init or None if init was None. 78 | 79 | :param init: The init dictionary 80 | :type init: dict 81 | :return: A new instance of {{ event.class_name }} if creation did not fail 82 | :rtype: Optional[Union[dict, {{ event.class_name }}]] 83 | """ 84 | if init is not None: 85 | try: 86 | ourselves = {{ event.class_name }}(**init) 87 | return ourselves 88 | except Exception: 89 | return init 90 | else: 91 | return init 92 | 93 | @staticmethod 94 | def safe_create_from_list(init): 95 | """ 96 | Safely create a new list {{ event.class_name }}s from the supplied list of dictionaries. 97 | 98 | This method will not throw an Exception and will return a new list {{ event.class_name }} instances 99 | if init is not None otherwise returns init or None if init was None. 100 | 101 | :param init: The init dictionary 102 | :type init: dict 103 | :return: A new list of {{ event.class_name }} instances if creation did not fail 104 | :rtype: Optional[List[Union[dict, {{ event.class_name }}]]] 105 | """ 106 | if init is not None: 107 | list_of_self = [] 108 | for it in init: 109 | list_of_self.append({{ event.class_name }}.safe_create(it)) 110 | return list_of_self 111 | else: 112 | return init 113 | 114 | 115 | {% endfor %} 116 | {{ dup }}_EVENTS_TO_CLASS = { 117 | {% for ename, clazz in event_to_clazz %} 118 | "{{ ename }}": {{ clazz }}, 119 | {% endfor %} 120 | } 121 | 122 | {{ domain }}NS = namedtuple("{{ domain }}NS", [{{ event_ns_init(event_to_clazz) }}]) 123 | 124 | {{ dup }}_EVENTS_NS = {{ domain }}NS( 125 | {% for ename, clazz in event_to_clazz %} 126 | {{ pascalcase(ename.split('.')[1]) }}="{{ ename }}", 127 | {% endfor %} 128 | ) 129 | 130 | -------------------------------------------------------------------------------- /cripy/templates/full/protocol_init.py.j2: -------------------------------------------------------------------------------- 1 | {% for import_from, imported in domains %} 2 | from cripy.{{ which }}.protocol.{{ import_from }} import {{ imported }} 3 | {% endfor %} 4 | 5 | __all__ = [ "ProtocolMixin" ] 6 | 7 | 8 | class ProtocolMixin(object): 9 | def __init__(self, *args, **kwargs): 10 | {% if which == 'gevent' %} 11 | super(ProtocolMixin, self).__init__(*args, **kwargs) 12 | {% else %} 13 | super().__init__(*args, **kwargs) 14 | {% endif %} 15 | self.protocol_events = dict() 16 | {% for _, imported in domains %} 17 | self.{{ imported }}: {{ imported }} = {{ imported }}(self) 18 | self._update_protocol_events(self.{{ imported }}.get_event_classes()) 19 | {% endfor %} 20 | 21 | def _update_protocol_events(self, events): 22 | if events is not None: 23 | self.protocol_events.update(events) 24 | 25 | -------------------------------------------------------------------------------- /cripy/templates/simple/commands.async.py.j2: -------------------------------------------------------------------------------- 1 | """This is an auto-generated file. Modify at your own risk""" 2 | from typing import Awaitable, Any,{% if d.events %} Callable,{% endif %} Dict, List, Optional, Union, TYPE_CHECKING 3 | 4 | if TYPE_CHECKING: 5 | from cripy import ConnectionType, SessionType 6 | 7 | __all__ = ["{{ d.domain }}"] 8 | 9 | 10 | class {{ d.domain }}: 11 | """ 12 | {% for section in d.description_parts() %} 13 | {% for part in section %} 14 | {{ part }} 15 | {% endfor %} 16 | {% endfor %} 17 | """ 18 | 19 | __slots__ = ["client"] 20 | 21 | def __init__(self, client: Union["ConnectionType", "SessionType"]) -> None: 22 | """Initialize a new instance of {{ d.domain }} 23 | 24 | :param client: The client instance to be used to communicate with the remote browser instance 25 | """ 26 | self.client: Union["ConnectionType", "SessionType"] = client 27 | 28 | {% for command in d.commands %} 29 | {{ command.command_sig() }} 30 | {% if command.has_description %} 31 | """ 32 | {% for descript in command.description_parts() %} 33 | {{ descript }} 34 | {% endfor %} 35 | {% if command.is_deprecation_or_experimental %} 36 | 37 | {{ command.deprecation_experimental_status() }} 38 | 39 | {% else %} 40 | 41 | {% endif %} 42 | {{ command.command_link() }} 43 | {% else %} 44 | """ 45 | {% if command.is_deprecation_or_experimental %} 46 | {{ command.deprecation_experimental_status() }} 47 | 48 | {% endif %} 49 | {{ command.command_link() }} 50 | {% endif %} 51 | {% if command.has_parameters %} 52 | 53 | {% for param in command.parameters %} 54 | {{ param.doc_string() }} 55 | {% endfor %} 56 | {% else %} 57 | 58 | {% endif %} 59 | :return: The results of the command 60 | """ 61 | {% if command.has_parameters %} 62 | {% set required, optional = command.required_optional_params() %} 63 | {% if required and not optional %} 64 | return self.client.send("{{d.domain}}.{{ command.name }}", {{ required }}) 65 | {% else %} 66 | {% if required %} 67 | msg = {{ required }} 68 | {% else %} 69 | msg = {} 70 | {% endif %} 71 | {% for param_name in optional %} 72 | if {{ param_name }} is not None: 73 | msg["{{ param_name }}"] = {{ param_name }} 74 | {% endfor %} 75 | return self.client.send("{{d.domain}}.{{ command.name }}", msg) 76 | {% endif %} 77 | {% else %} 78 | return self.client.send("{{d.domain}}.{{ command.name }}", {}) 79 | {% endif %} 80 | 81 | {% endfor %} 82 | {% if d.events %} 83 | {% for event in d.events %} 84 | def {{ event.name }}(self, listener: Optional[Callable[[{{ event.event_sig() }}], Any]] = None) -> Any: 85 | """ 86 | {% if event.has_description %} 87 | {% for descript in event.description_parts() %} 88 | {{ descript }} 89 | {% endfor %} 90 | 91 | {{ event.event_link() }} 92 | {% else %} 93 | {{ event.event_link() }} 94 | {% endif %} 95 | 96 | :param listener: Optional listener function 97 | :return: If a listener was supplied the return value is a callable that 98 | will remove the supplied listener otherwise a future that resolves 99 | with the value of the event 100 | """ 101 | event_name = "{{ d.domain }}.{{ event.name }}" 102 | if listener is None: 103 | future = self.client.loop.create_future() 104 | 105 | def _listener(event: Optional[Dict] = None) -> None: 106 | future.set_result(event) 107 | 108 | self.client.once(event_name, _listener) 109 | 110 | return future 111 | 112 | self.client.on(event_name, listener) 113 | return lambda: self.client.remove_listener(event_name, listener) 114 | 115 | {% endfor %} 116 | {% endif %} -------------------------------------------------------------------------------- /cripy/templates/simple/commands.py.j2: -------------------------------------------------------------------------------- 1 | __all__ = ["{{ d.domain }}"] 2 | 3 | 4 | class {{ d.domain }}(object): 5 | {% if d.description %} 6 | """ 7 | {{ d.description }} 8 | """ 9 | 10 | {% endif %} 11 | {% if d.has_deps %} 12 | {{ d.dep_list_str }} 13 | 14 | {% endif %} 15 | def __init__(self, chrome): 16 | """ 17 | Construct a new {{ d.domain }} object 18 | 19 | :param chrome: An instance of the devtools protocol client 20 | """ 21 | self.chrome = chrome 22 | 23 | {% for command in d.commands %} 24 | {% if command.has_parameters %} 25 | def {{ command.name }}(self, {{ command.command_arg_string_no_types }}): 26 | {% else %} 27 | def {{ command.name }}(self): 28 | {% endif %} 29 | {% if command.has_parameters %} 30 | """ 31 | {% if command.description %} 32 | {{ command.description }} 33 | 34 | {% endif %} 35 | {% for prop in command.parameters %} 36 | {% if prop.description %} 37 | :param {{ prop.name }}: {{ prop.nice_description }} 38 | {% else %} 39 | :param {{ prop.name }}: The {{ prop.name }} 40 | {% endif %} 41 | :type {{ prop.name }}: {{ prop.constructor_docstr }} 42 | {% endfor %} 43 | """ 44 | {% elif command.description %} 45 | """ 46 | {{ command.description }} 47 | """ 48 | {% endif %} 49 | {% if command.has_parameters %} 50 | msg_dict = dict() 51 | {% for param in command.parameters %} 52 | if {{ param.name }} is not None: 53 | msg_dict['{{ param.name }}'] = {{ param.name }} 54 | {% endfor %} 55 | wres = self.chrome.send('{{ command.scoped_name }}', msg_dict) 56 | {% else %} 57 | wres = self.chrome.send('{{ command.scoped_name }}') 58 | {% endif %} 59 | return wres.get() 60 | 61 | {% endfor %} 62 | {% if events %} 63 | {% for e, d in events %} 64 | def {{ onEvent(e) }}(self, fn, once=False): 65 | {% if d %} 66 | """ 67 | {% for l in d %} 68 | {{ l }} 69 | {% endfor %} 70 | """ 71 | {% endif %} 72 | self.chrome.on("{{ e }}", fn, once=once) 73 | 74 | {% endfor %} 75 | {% endif %} 76 | 77 | 78 | -------------------------------------------------------------------------------- /cripy/templates/simple/protocol_init.py.j2: -------------------------------------------------------------------------------- 1 | {% for import_from, imported in domains %} 2 | from .{{ import_from }} import {{ imported }} 3 | {% endfor %} 4 | 5 | __all__ = [ 6 | {% for import_from, imported in domains %} 7 | "{{ imported }}", 8 | {% endfor %} 9 | ] 10 | 11 | 12 | -------------------------------------------------------------------------------- /dev-requirements.txt: -------------------------------------------------------------------------------- 1 | black 2 | m2r 3 | pytest 4 | pytest-asyncio 5 | pytest-cov 6 | git+https://github.com/python/mypy.git 7 | flake8 8 | flake8-bugbear 9 | psutil 10 | -------------------------------------------------------------------------------- /generate_protocol.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | import traceback 3 | from pathlib import Path 4 | 5 | import ujson 6 | 7 | from cripy.protogen import generate_protocol_clazzs 8 | 9 | 10 | def gen() -> None: 11 | cwd = Path.cwd() 12 | with (cwd / "data" / "protocol.json").open("r") as pin: 13 | proto_data = ujson.load(pin) 14 | generate_protocol_clazzs(proto_data["domains"], cwd / "cripy" / "protocol") 15 | try: 16 | from cripy.client import Client 17 | 18 | c = Client("") 19 | except Exception: 20 | print("Client failed") 21 | traceback.print_exc() 22 | else: 23 | print("Client appears good") 24 | subprocess.run(["black", "cripy/"]) 25 | 26 | 27 | if __name__ == "__main__": 28 | gen() 29 | -------------------------------------------------------------------------------- /launch_chrome_dev.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | args="--remote-debugging-port=9222 --disable-hang-monitor --disable-features=site-per-process about:blank" 4 | 5 | google-chrome-unstable $args -------------------------------------------------------------------------------- /mypy.ini: -------------------------------------------------------------------------------- 1 | [mypy] 2 | # Specify the target platform details in config, so your developers are 3 | # free to run mypy on Windows, Linux, or macOS and get consistent 4 | # results. 5 | python_version=3.6 6 | 7 | incremental=True 8 | 9 | # flake8-mypy expects the two following for sensible formatting 10 | show_column_numbers=True 11 | 12 | # show error messages from unrelated files 13 | follow_imports=normal 14 | 15 | # suppress errors about unsatisfied imports 16 | ignore_missing_imports=True 17 | 18 | # no strict optionals 19 | strict_optional=False 20 | 21 | # be strict 22 | disallow_untyped_calls=True 23 | warn_return_any=False 24 | warn_no_return=True 25 | warn_redundant_casts=True 26 | warn_unused_ignores=True 27 | 28 | # The following are off by default. Flip them on if you feel 29 | # adventurous. 30 | disallow_untyped_defs=True 31 | check_untyped_defs=True 32 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | aiodns 2 | aiofiles 3 | aiohttp 4 | async-timeout 5 | attrs 6 | cchardet 7 | jinja2 8 | pyee2 9 | stringcase 10 | ujson 11 | websockets 12 | uvloop 13 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from os import path 2 | from pathlib import Path 3 | 4 | from setuptools import setup, find_packages 5 | 6 | basedir = Path(path.dirname(path.abspath(__file__))) 7 | 8 | with basedir.joinpath("README.md").open("r", encoding="utf-8") as f: 9 | README = f.read() 10 | 11 | 12 | def get_requirements(): 13 | reqs = [] 14 | reqp = basedir.joinpath("requirements.txt") 15 | with reqp.open("r") as rin: 16 | for line in rin: 17 | reqs.append(line.rstrip()) 18 | return reqs 19 | 20 | 21 | setup( 22 | name="cripy", 23 | version="1.5.0", 24 | description="Unofficial port of chrome-remote-interface", 25 | long_description=README, 26 | long_description_content_type="text/markdown", 27 | author="John Berlin", 28 | author_email="john.berlin@rhizome.com", 29 | url="https://github.com/webrecorder/chrome-remote-interface-py", 30 | packages=find_packages(exclude=["tests", "tests.*"]), 31 | install_requires=get_requirements(), 32 | package_data={"": ["templates/simple/*.j2", "templates/full/*.j2"]}, 33 | include_package_data=True, 34 | zip_safe=False, 35 | license="Apache", 36 | keywords="cripy", 37 | classifiers=[ 38 | "Development Status :: 1 - Alpha", 39 | "Intended Audience :: Developers", 40 | "Intended Audience :: Archivists", 41 | "License :: OSI Approved :: MIT License", 42 | "Programming Language :: Python :: 3.6", 43 | "Topic :: Software Development :: DevTools Protocol", 44 | ], 45 | python_requires=">=3.6", 46 | ) 47 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/N0taN3rd/chrome-remote-interface-py/49a77349fc5712508e5071d69dfcc9e94f36bfe8/tests/__init__.py -------------------------------------------------------------------------------- /tests/conftest.py: -------------------------------------------------------------------------------- 1 | from typing import Dict, List, Union 2 | 3 | import pytest 4 | import uvloop 5 | from _pytest.fixtures import SubRequest 6 | 7 | from cripy import CDP, Client, connect 8 | from .helpers import Cleaner, launch_chrome 9 | 10 | uvloop.install() 11 | 12 | 13 | @pytest.fixture(scope="class") 14 | def event_loop() -> uvloop.Loop: 15 | loop = uvloop.new_event_loop() 16 | yield loop 17 | loop.close() 18 | 19 | 20 | @pytest.fixture(scope="class") 21 | async def chrome(request: SubRequest): 22 | cp, tempdir, wsurl = await launch_chrome() 23 | if request.cls: 24 | request.cls.wsurl = wsurl 25 | yield wsurl 26 | cp.kill() 27 | await cp.wait() 28 | try: 29 | tempdir.cleanup() 30 | except Exception: 31 | pass 32 | 33 | 34 | @pytest.fixture 35 | async def client(request: SubRequest) -> Client: 36 | _client = await connect(url=request.cls.wsurl, remote=True) 37 | request.cls.client = _client 38 | yield _client 39 | await _client.dispose() 40 | 41 | 42 | @pytest.fixture 43 | async def mr_clean(request: SubRequest) -> Cleaner: 44 | cleaner = Cleaner() 45 | yield cleaner 46 | await cleaner.clean_up() 47 | 48 | 49 | @pytest.fixture(scope="class") 50 | async def protocol_def(request: SubRequest) -> Dict[str, Union[List[Dict], Dict]]: 51 | protocol = await CDP.Protocol() 52 | request.cls.protocol = protocol 53 | yield protocol 54 | -------------------------------------------------------------------------------- /tests/helpers/__init__.py: -------------------------------------------------------------------------------- 1 | from .chrome import launch_chrome 2 | from .utils import ( 3 | Cleaner, 4 | evaluation_result, 5 | make_target_selector, 6 | get_target_from_list, 7 | ) 8 | 9 | __all__ = [ 10 | "launch_chrome", 11 | "Cleaner", 12 | "evaluation_result", 13 | "make_target_selector", 14 | "get_target_from_list", 15 | ] 16 | -------------------------------------------------------------------------------- /tests/helpers/chrome.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | import os 3 | import platform 4 | from asyncio import AbstractEventLoop 5 | from asyncio.subprocess import Process 6 | from itertools import chain 7 | from tempfile import TemporaryDirectory 8 | from typing import Dict, Tuple, Optional 9 | 10 | import uvloop 11 | 12 | from cripy.cdp import CDP 13 | 14 | __all__ = ["launch_chrome", "LaunchError"] 15 | 16 | chromez = ["unstable", "beta", "stable", "chromium"] 17 | 18 | dt_command = ( 19 | "ls {0} /usr/share/applications/*.desktop | grep -E '(google-chrome*|chromium*|chrome*)' |" 20 | " xargs grep -E '^Exec=.*(google|chrome|chromium).*' | " 21 | "awk 'match($0, /[^:]+[^/]+([^ ]+)/, arr) {{ print arr[1] }}' | uniq | sort -r" 22 | ) 23 | 24 | # https://peter.sh/experiments/chromium-command-line-switches/ 25 | # https://cs.chromium.org/chromium/src/chrome/common/chrome_switches.cc 26 | DEFAULT_ARGS = [ 27 | "--remote-debugging-port=9222", 28 | "--disable-background-networking", 29 | "--disable-background-timer-throttling", 30 | "--disable-client-side-phishing-detection", 31 | "--disable-default-apps", 32 | "--disable-extensions", 33 | "--disable-backgrounding-occluded-windows", 34 | "--disable-ipc-flooding-protection", 35 | "--disable-popup-blocking", 36 | "--disable-hang-monitor", 37 | "--disable-prompt-on-repost", 38 | "--disable-sync", 39 | "--disable-translate", 40 | "--disable-domain-reliability", 41 | "--disable-renderer-backgrounding", 42 | "--disable-infobars", 43 | "--disable-translate", 44 | "--disable-features=site-per-process", 45 | "--disable-breakpad", 46 | "--metrics-recording-only", 47 | "--no-first-run", 48 | "--safebrowsing-disable-auto-update", 49 | "--password-store=basic", 50 | "--use-mock-keychain", 51 | "--mute-audio", 52 | "--autoplay-policy=no-user-gesture-required", 53 | "--enable-automation", 54 | ] 55 | 56 | 57 | class LaunchError(Exception): 58 | pass 59 | 60 | 61 | chrome_execs = [ 62 | "google-chrome-unstable", 63 | "google-chrome-beta", 64 | "google-chrome-stable", 65 | "chromium", 66 | "chromium-browser", 67 | ] 68 | 69 | 70 | async def run_command(cmd: str, loop: AbstractEventLoop, env: Dict) -> Tuple[str, str]: 71 | proc = await asyncio.create_subprocess_shell( 72 | cmd, 73 | stdout=asyncio.subprocess.PIPE, 74 | stderr=asyncio.subprocess.PIPE, 75 | stdin=asyncio.subprocess.DEVNULL, 76 | loop=loop, 77 | env=env, 78 | ) 79 | stdout, stderr = await proc.communicate() 80 | await proc.wait() 81 | return stdout.decode("utf-8").strip(), stderr.decode("utf-8").strip() 82 | 83 | 84 | async def which_chrome(loop: AbstractEventLoop, env: Dict) -> Optional[str]: 85 | for chrome in chrome_execs: 86 | exe, _ = await run_command(f"which {chrome}", loop, env) 87 | if exe: 88 | return exe 89 | return None 90 | 91 | 92 | async def check_chrome_desktops(loop: AbstractEventLoop, env: Dict) -> Optional[str]: 93 | results = await asyncio.gather( 94 | run_command(dt_command.format("/usr/share/applications/*.desktop"), loop, env), 95 | run_command( 96 | dt_command.format("~/.local/share/applications/*.desktop"), loop, env 97 | ), 98 | loop=loop, 99 | ) 100 | 101 | found: Dict[str, str] = dict( 102 | it 103 | for it in map( 104 | lambda cexe: (cexe[cexe.rindex("/") + 1 :], cexe), 105 | chain.from_iterable(map(lambda t: t[0].splitlines(), results)), 106 | ) 107 | ) 108 | 109 | for desired in chrome_execs: 110 | if desired in found: 111 | return found[desired] 112 | 113 | return None 114 | 115 | 116 | async def find_chrome(loop: AbstractEventLoop, env: Dict) -> Optional[str]: 117 | plat = platform.system() 118 | if plat == "Linux": 119 | chrome_exe = await which_chrome(loop, env) 120 | if chrome_exe is None: 121 | chrome_exe = await check_chrome_desktops(loop, env) 122 | return chrome_exe 123 | 124 | 125 | async def launch_chrome( 126 | headless: bool = True 127 | ) -> Tuple[Process, TemporaryDirectory, str]: 128 | loop = asyncio.get_event_loop() 129 | env = os.environ.copy() 130 | chrome_exe = await find_chrome(loop, env) 131 | if chrome_exe is None: 132 | raise LaunchError("Could not find chrome") 133 | tmpdir = TemporaryDirectory() 134 | args = [chrome_exe, f"--user-data-dir={tmpdir.name}"] + DEFAULT_ARGS 135 | if headless: 136 | args.extend(["--headless", "--hide-scrollbars", "about:blank"]) 137 | else: 138 | args.append("about:blank") 139 | chrome_proc = await asyncio.create_subprocess_exec( 140 | *args, 141 | stderr=asyncio.subprocess.DEVNULL, 142 | stdin=asyncio.subprocess.DEVNULL, 143 | loop=loop, 144 | env=env, 145 | ) 146 | for _ in range(100): 147 | try: 148 | targets = await CDP.List() 149 | for tab in targets: 150 | if tab["type"] == "page": 151 | return chrome_proc, tmpdir, tab["webSocketDebuggerUrl"] 152 | except Exception: 153 | await asyncio.sleep(1, loop=loop) 154 | chrome_proc.kill() 155 | await chrome_proc.wait() 156 | tmpdir.cleanup() 157 | raise LaunchError("Could not find tab for connecting to") 158 | 159 | 160 | if __name__ == "__main__": 161 | asyncio.set_event_loop_policy(uvloop.EventLoopPolicy()) 162 | asyncio.get_event_loop().run_until_complete(launch_chrome()) 163 | # cp, tmpdir, wsurl = ChromeLauncher.launch(headless=False) 164 | # tmpdir.cleanup() 165 | # cp.kill() 166 | # cp.wait() 167 | -------------------------------------------------------------------------------- /tests/helpers/utils.py: -------------------------------------------------------------------------------- 1 | from typing import Any, Callable, Dict, List, Optional, Tuple, Union 2 | 3 | import attr 4 | from pyee2 import EventEmitter, EventEmitterS 5 | 6 | from cripy import CDP, Client, Connection 7 | 8 | __all__ = [ 9 | "Cleaner", 10 | "evaluation_result", 11 | "make_target_selector", 12 | "get_target_from_list", 13 | ] 14 | 15 | EE = Union[EventEmitter, EventEmitterS] 16 | 17 | EEListener = Dict[str, Union[str, EE, Callable]] 18 | 19 | 20 | @attr.dataclass(slots=True) 21 | class Cleaner: 22 | listeners: List[EEListener] = attr.ib(init=False, factory=list) 23 | disposables: List[Any] = attr.ib(init=False, factory=list) 24 | 25 | def addEventListener( 26 | self, emitter: EE, eventName: str, handler: Callable 27 | ) -> None: 28 | emitter.on(eventName, handler) 29 | self.listeners.append( 30 | dict(emitter=emitter, eventName=eventName, handler=handler) 31 | ) 32 | 33 | def addEventListeners( 34 | self, emitter: EE, eventsHandlers: List[Tuple[str, Callable]] 35 | ) -> None: 36 | for eventName, handler in eventsHandlers: 37 | self.addEventListener(emitter, eventName, handler) 38 | 39 | def add_disposable(self, disposable: Any) -> None: 40 | self.disposables.append(disposable) 41 | 42 | async def clean_up(self) -> None: 43 | for listener in self.listeners: 44 | emitter = listener["emitter"] 45 | eventName = listener["eventName"] 46 | handler = listener["handler"] 47 | emitter.remove_listener(eventName, handler) 48 | self.listeners.clear() 49 | 50 | for disposable in self.disposables: 51 | if isinstance(disposable, EventEmitter): 52 | disposable.remove_all_listeners() 53 | await disposable.dispose() 54 | self.disposables.clear() 55 | 56 | 57 | DefaultEvalArgs = {'includeCommandLineAPI': True, 'awaitPromise': True, 'userGesture': True} 58 | 59 | 60 | def merge_dicts(*dicts: Dict) -> Dict: 61 | merged: Dict = {} 62 | for d in dicts: 63 | merged.update(d) 64 | return merged 65 | 66 | 67 | async def evaluation_result( 68 | cdp_client: Union[Connection, Client], expression: str, **kwargs: Any 69 | ) -> Tuple[Any, Any]: 70 | args = merge_dicts(DefaultEvalArgs, kwargs) 71 | args["expression"] = expression 72 | if isinstance(cdp_client, Connection): 73 | result = await cdp_client.send("Runtime.evaluate", args) 74 | else: 75 | result = await cdp_client.Runtime.evaluate(**args) 76 | res = result.get("result") 77 | return res.get("type"), res.get("value") 78 | 79 | 80 | def make_target_selector(which: str) -> Callable[[List[Dict[str, str]]], Any]: 81 | def target_selector(targets: List[Dict[str, str]]) -> Any: 82 | for idx, target in enumerate(targets): 83 | if target["type"] == "page": 84 | if which == "dict": 85 | return target 86 | elif which == "idx": 87 | return idx 88 | elif which == "str": 89 | return target["webSocketDebuggerUrl"] 90 | 91 | return target_selector 92 | 93 | 94 | async def get_target_from_list( 95 | return_what: str 96 | ) -> Optional[Union[Dict[str, str], str]]: 97 | targets = await CDP.List() 98 | for target in targets: 99 | if target["type"] == "page": 100 | if return_what == "target": 101 | return target 102 | elif return_what == "wsurl": 103 | return target["webSocketDebuggerUrl"] 104 | elif return_what == "id": 105 | return target["id"] 106 | return None 107 | -------------------------------------------------------------------------------- /tests/test_connect.py: -------------------------------------------------------------------------------- 1 | from asyncio import AbstractEventLoop 2 | from typing import Any 3 | 4 | import pytest 5 | from aiohttp import ClientConnectorError 6 | from async_timeout import timeout 7 | from websockets import InvalidURI 8 | 9 | from cripy.cdp import CDP, connect 10 | from cripy.connection import Connection 11 | from cripy.events import ConnectionEvents 12 | from .helpers import Cleaner 13 | 14 | 15 | class TestConnectFailsNoChrome: 16 | @pytest.mark.asyncio 17 | async def test_connect_fails_with_nothing_to_connect_to(self): 18 | with pytest.raises(ClientConnectorError): 19 | await connect() 20 | 21 | @pytest.mark.asyncio 22 | async def test_connect_fails_supplied_ws_url_bad(self): 23 | with pytest.raises(Exception): 24 | await connect("ws://nope") 25 | 26 | @pytest.mark.asyncio 27 | async def test_connection_connect_fails_with_no_supplied_ws_url(self): 28 | with pytest.raises(InvalidURI): 29 | conn = Connection() 30 | await conn.connect() 31 | 32 | @pytest.mark.asyncio 33 | async def test_client_connect_fails_supplied_ws_url_bad_constructor(self): 34 | with pytest.raises(Exception): 35 | conn = Connection("ws://nope") 36 | await conn.connect() 37 | 38 | @pytest.mark.asyncio 39 | async def test_client_connect_fails_supplied_ws_url_bad_connect(self): 40 | with pytest.raises(Exception): 41 | conn = Connection() 42 | await conn.connect("ws://nope") 43 | 44 | 45 | @pytest.mark.usefixtures("chrome") 46 | class TestConnecting: 47 | @pytest.mark.parametrize( 48 | "url,additional_args", 49 | [ 50 | (None, {}), 51 | (None, {'remote': True}), 52 | ("http://localhost:9222", {}), 53 | ("http://localhost:9222", {'remote': True}), 54 | ], 55 | ids=[ 56 | "default url", 57 | "default url, remote protocol", 58 | "supplied HTTP url", 59 | "supplied HTTP url, remote protocol", 60 | ] 61 | ) 62 | @pytest.mark.asyncio 63 | async def test_connects_using(self, url: Any, additional_args: Any): 64 | if url is not None: 65 | client = await connect(url=url, **additional_args) 66 | else: 67 | client = await connect(**additional_args) 68 | version = await client.Browser.getVersion() 69 | assert version["product"] in version["userAgent"] 70 | assert client.ws_url is not None 71 | await client.dispose() 72 | 73 | @pytest.mark.asyncio 74 | async def test_connects_using_default_url_supplied_proto(self, mr_clean: Cleaner): 75 | proto = await CDP.Protocol() 76 | client = await connect(protocol=proto) 77 | mr_clean.add_disposable(client) 78 | version = await client.Browser.getVersion() 79 | assert version["product"] in version["userAgent"] 80 | 81 | @pytest.mark.asyncio 82 | async def test_connects_using_supplied_url_proto(self, mr_clean: Cleaner): 83 | proto = await CDP.Protocol() 84 | client = await connect(url="http://localhost:9222", protocol=proto) 85 | mr_clean.add_disposable(client) 86 | version = await client.Browser.getVersion() 87 | assert version["product"] in version["userAgent"] 88 | 89 | @pytest.mark.asyncio 90 | async def test_connects_and_emits_closed_after_dispose_default_url( 91 | self, mr_clean: Cleaner, event_loop: AbstractEventLoop 92 | ): 93 | future = event_loop.create_future() 94 | client = await connect() 95 | 96 | def listener(): 97 | if not future.done(): 98 | future.set_result(True) 99 | 100 | mr_clean.addEventListener(client, ConnectionEvents.Disconnected, listener) 101 | 102 | async with timeout(10, loop=event_loop): 103 | await client.dispose() 104 | 105 | async with timeout(10, loop=event_loop): 106 | assert await future 107 | 108 | @pytest.mark.asyncio 109 | async def test_connects_and_calls_closecb_after_dispose_default_url( 110 | self, event_loop: AbstractEventLoop 111 | ): 112 | future = event_loop.create_future() 113 | client = await connect() 114 | 115 | def cb(): 116 | if not future.done(): 117 | future.set_result(True) 118 | 119 | client.set_close_callback(cb) 120 | 121 | async with timeout(10, loop=event_loop): 122 | await client.dispose() 123 | 124 | async with timeout(10, loop=event_loop): 125 | assert await future 126 | 127 | @pytest.mark.asyncio 128 | async def test_connects_and_emits_closed_after_dispose_supplied_url( 129 | self, mr_clean: Cleaner, event_loop: AbstractEventLoop 130 | ): 131 | future = event_loop.create_future() 132 | client = await connect(url="http://localhost:9222") 133 | 134 | def listener(): 135 | if not future.done(): 136 | future.set_result(True) 137 | 138 | mr_clean.addEventListener(client, ConnectionEvents.Disconnected, listener) 139 | 140 | async with timeout(10, loop=event_loop): 141 | await client.dispose() 142 | 143 | async with timeout(10, loop=event_loop): 144 | assert await future 145 | 146 | @pytest.mark.asyncio 147 | async def test_connects_and_emits_closed_after_dispose_supplied_url( 148 | self, event_loop: AbstractEventLoop 149 | ): 150 | future = event_loop.create_future() 151 | client = await connect(url="http://localhost:9222") 152 | 153 | def cb(): 154 | if not future.done(): 155 | future.set_result(True) 156 | 157 | client.set_close_callback(cb) 158 | 159 | async with timeout(10, loop=event_loop): 160 | await client.dispose() 161 | 162 | async with timeout(10, loop=event_loop): 163 | assert await future 164 | -------------------------------------------------------------------------------- /update_protocol.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | from aiohttp import ClientSession 3 | from typing import List, Tuple 4 | import pathlib 5 | from cripy import CDP 6 | from ujson import dumps 7 | 8 | try: 9 | import uvloop 10 | asyncio.set_event_loop_policy(uvloop.EventLoopPolicy()) 11 | except ImportError: 12 | pass 13 | 14 | 15 | async def fetch(url: str, session: ClientSession) -> Tuple[str, bytes]: 16 | """Fetch a url, using specified ClientSession.""" 17 | async with session.get(url) as response: 18 | resp = await response.read() 19 | return url, resp 20 | 21 | 22 | async def fetch_all(urls: List[str]) -> None: 23 | """Launch requests for all web pages.""" 24 | tasks = [] 25 | async with ClientSession() as session: 26 | for url in urls: 27 | task = asyncio.ensure_future(fetch(url, session)) 28 | tasks.append(task) # create list of tasks 29 | done = await asyncio.gather(*tasks) 30 | dp = pathlib.Path("data") 31 | for url, res in done: 32 | fp = dp.joinpath(url[url.find("json") + 5 :]) 33 | with fp.open("w") as out: 34 | out.write(res.decode("utf-8")) 35 | 36 | 37 | async def get_proto_from_browser() -> None: 38 | proto = await CDP.Protocol(loop=asyncio.get_event_loop()) 39 | with open('./data/protocol.json', 'w') as out: 40 | out.write(dumps(proto)) 41 | print(proto) 42 | 43 | if __name__ == "__main__": 44 | # urls = [ 45 | # "https://raw.githubusercontent.com/ChromeDevTools/devtools-protocol/master/json/browser_protocol.json", 46 | # "https://raw.githubusercontent.com/ChromeDevTools/devtools-protocol/master/json/js_protocol.json", 47 | # ] 48 | loop = asyncio.get_event_loop() # event loop 49 | loop.run_until_complete(get_proto_from_browser()) 50 | --------------------------------------------------------------------------------