Full Code of pyppeteer/pyppeteer2 for AI

dev 7dc91ee5173d cached
143 files
588.3 KB
144.2k tokens
1110 symbols
1 requests
Download .txt
Showing preview only (627K chars total). Download the full file or copy to clipboard to get everything.
Repository: pyppeteer/pyppeteer2
Branch: dev
Commit: 7dc91ee5173d
Files: 143
Total size: 588.3 KB

Directory structure:
gitextract_a3lak9ot/

├── .circleci/
│   └── config.yml
├── .coveragerc
├── .gitignore
├── .noserc
├── .pre-commit-config.yaml
├── CHANGELOG.md
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── docs/
│   ├── Makefile
│   ├── _static/
│   │   └── custom.css
│   ├── _templates/
│   │   └── layout.html
│   ├── changes.md
│   ├── conf.py
│   ├── index.md
│   ├── make.bat
│   ├── reference.md
│   └── server.py
├── pyppeteer/
│   ├── __init__.py
│   ├── browser.py
│   ├── chromium_downloader.py
│   ├── command.py
│   ├── connection.py
│   ├── coverage.py
│   ├── dialog.py
│   ├── element_handle.py
│   ├── emulation_manager.py
│   ├── errors.py
│   ├── execution_context.py
│   ├── frame_manager.py
│   ├── helper.py
│   ├── input.py
│   ├── launcher.py
│   ├── multimap.py
│   ├── navigator_watcher.py
│   ├── network_manager.py
│   ├── options.py
│   ├── page.py
│   ├── target.py
│   ├── tracing.py
│   ├── us_keyboard_layout.py
│   ├── util.py
│   └── worker.py
├── pyproject.toml
├── spell.txt
├── tests/
│   ├── __init__.py
│   ├── base.py
│   ├── closeme.py
│   ├── dumpio.py
│   ├── file-to-upload.txt
│   ├── frame_utils.py
│   ├── server.py
│   ├── static/
│   │   ├── beforeunload.html
│   │   ├── button.html
│   │   ├── cached/
│   │   │   ├── one-style.css
│   │   │   └── one-style.html
│   │   ├── checkbox.html
│   │   ├── csp.html
│   │   ├── csscoverage/
│   │   │   ├── involved.html
│   │   │   ├── media.html
│   │   │   ├── multiple.html
│   │   │   ├── simple.html
│   │   │   ├── sourceurl.html
│   │   │   ├── stylesheet1.css
│   │   │   ├── stylesheet2.css
│   │   │   └── unused.html
│   │   ├── detect-touch.html
│   │   ├── error.html
│   │   ├── es6/
│   │   │   ├── es6import.js
│   │   │   ├── es6module.js
│   │   │   └── es6pathimport.js
│   │   ├── fileupload.html
│   │   ├── frame-204.html
│   │   ├── frame.html
│   │   ├── grid.html
│   │   ├── historyapi.html
│   │   ├── huge-page.html
│   │   ├── injectedfile.js
│   │   ├── injectedstyle.css
│   │   ├── jscoverage/
│   │   │   ├── eval.html
│   │   │   ├── involved.html
│   │   │   ├── multiple.html
│   │   │   ├── ranges.html
│   │   │   ├── script1.js
│   │   │   ├── script2.js
│   │   │   ├── simple.html
│   │   │   ├── sourceurl.html
│   │   │   └── unused.html
│   │   ├── keyboard.html
│   │   ├── mobile.html
│   │   ├── modernizr.js
│   │   ├── mouse-helper.js
│   │   ├── nested-frames.html
│   │   ├── offscreenbuttons.html
│   │   ├── one-frame.html
│   │   ├── one-style.css
│   │   ├── one-style.html
│   │   ├── popup/
│   │   │   ├── popup.html
│   │   │   └── window-open.html
│   │   ├── resetcss.html
│   │   ├── script.js
│   │   ├── scrollable.html
│   │   ├── select.html
│   │   ├── self-request.html
│   │   ├── serviceworkers/
│   │   │   ├── empty/
│   │   │   │   ├── sw.html
│   │   │   │   └── sw.js
│   │   │   └── fetch/
│   │   │       ├── style.css
│   │   │       ├── sw.html
│   │   │       └── sw.js
│   │   ├── shadow.html
│   │   ├── simple-extension/
│   │   │   ├── index.js
│   │   │   └── manifest.json
│   │   ├── simple.json
│   │   ├── style.css
│   │   ├── sw.js
│   │   ├── temperable.html
│   │   ├── textarea.html
│   │   ├── touches.html
│   │   ├── two-frames.html
│   │   ├── worker/
│   │   │   ├── worker.html
│   │   │   └── worker.js
│   │   └── wrappedlink.html
│   ├── test_abnormal_crash.py
│   ├── test_browser.py
│   ├── test_browser_context.py
│   ├── test_connection.py
│   ├── test_coverage.py
│   ├── test_dialog.py
│   ├── test_element_handle.py
│   ├── test_execution_context.py
│   ├── test_frame.py
│   ├── test_input.py
│   ├── test_launcher.py
│   ├── test_misc.py
│   ├── test_network.py
│   ├── test_page.py
│   ├── test_pyppeteer.py
│   ├── test_screenshot.py
│   ├── test_target.py
│   ├── test_tracing.py
│   ├── test_worker.py
│   └── utils.py
└── tox.ini

================================================
FILE CONTENTS
================================================

================================================
FILE: .circleci/config.yml
================================================
version: 2.1

orbs:
  codecov: codecov/codecov@1.0.5

workflows:
  main:
    jobs:
      - lint
      - mypy
      - test_36
      - test_37
      - test_38
      - test_39
      - test_310


jobs:
  test_36:
    docker:
      - image: circleci/python:3.6
    environment:
      TOXENV: py36
      PYTEST_ADDOPTS: -n 8 --junitxml=/tmp/tests/pytest/results.xml --cov=./
    steps: &step_template
      - checkout
      - restore_cache:
          keys:
            - poetry_deps_{{checksum "poetry.lock"}}
      - run:
          name: Install headless Chrome dependencies
          # chrome headless libs, see
          # https://github.com/puppeteer/puppeteer/blob/master/docs/troubleshooting.md#chrome-headless-doesnt-launch-on-unix
          command: |
            sudo apt install -yq \
              ca-certificates fonts-liberation libasound2 libatk1.0-0 \
              libcairo2 libcups2 libdbus-1-3 libgdk-pixbuf2.0-0 \
              libglib2.0-0 libgtk-3-0 libnspr4 libnss3 libpango-1.0-0 \
              libpangocairo-1.0-0 libx11-xcb1 libxcomposite1 libxcursor1 \
              libxfixes3 libxi6 libxrandr2 libxrender1 libxss1 libxtst6 \
              lsb-release xdg-utils wget 
      - run:
          name: Install tox
          command: pip install tox
      - run:
          name: Run tests
          command: tox
      - save_cache:
          key: poetry_deps_{{checksum "poetry.lock"}}
          paths: ~/.cache/pypoetry/
      - store_test_results:
          path: /tmp/tests/
      # this step will simply fail for other jobs
      - codecov/upload:
          file: ./pytest-cov.pth

  test_37:
    docker:
      - image: circleci/python:3.7
    environment:
      TOXENV: py37
      PYTEST_ADDOPTS: &pytest_default -n 8 --junitxml=/tmp/tests/pytest/results.xml
    steps: *step_template

  test_38:
    docker:
      - image: circleci/python:3.8
    environment:
      TOXENV: py38
      PYTEST_ADDOPTS: *pytest_default
    steps: *step_template

  test_39:
    docker:
      - image: circleci/python:3.9
    environment:
      TOXENV: py39
      PYTEST_ADDOPTS: *pytest_default
    steps: *step_template

  test_310:
    docker:
      - image: circleci/python:3.10-rc
    environment:
      TOXENV: py310
      PYTEST_ADDOPTS: *pytest_default
    steps: *step_template

  mypy:
    docker:
      - image: circleci/python:3.6
    environment:
      TOXENV: mypy
      MYPY_JUNIT_XML_PATH: /tmp/tests/mypy/results.xml
    steps:
      - checkout
      - run:
          name: Install tox
          command: pip install tox
      - run:
          name: Check typing
          command: tox
      - store_test_results:
          path: /tmp/tests


  lint:
    docker:
      - image: circleci/python:3.6
    environment:
      TOXENV: flake8
    steps:
      - checkout
      - run:
          name: Install tox
          command: pip install tox
      - run:
          name: Check code style
          command: tox



================================================
FILE: .coveragerc
================================================
[run]
omit=setup.py
source=pyppeteer,tests


================================================
FILE: .gitignore
================================================
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class

# C extensions
*.so

# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
*.egg-info/
.installed.cfg
*.egg

# Virtualenv
env/
venv/
bin/
include/
lib/
lib64
lib64/
man/
pyvenv.cfg

# PyInstaller
#  Usually these files are written by a python script from a template
#  before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec

# Installer logs
pip-log.txt
pip-delete-this-directory.txt

# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.coverage.*
.cache
.doit.db.*
.mypy_cache
nosetests.xml
coverage.xml
*,cover
.hypothesis/
.pytest_cache/

# Translations
*.mo
*.pot

# Django stuff:
*.log

# Sphinx documentation
docs/_build/

# PyBuilder
target/

# pyenv python configuration file
.python-version

# pycharm file
.idea/

###### direnv ######
.direnv
.envrc

###### zsh-autoenv ######
.autoenv.zsh
.autoenv_leave.zsh

# test files
trace.json


================================================
FILE: .noserc
================================================
[nosetests]
logging-level=INFO
# no-path-adjustment=true
# with-coverage=true
# cover-package=pyppeteer


================================================
FILE: .pre-commit-config.yaml
================================================
repos:
  - repo: https://github.com/pre-commit/pre-commit-hooks
    rev: v2.4.0
    hooks:
      - id: trailing-whitespace
      - id: end-of-file-fixer
      - id: check-yaml
      - id: check-toml
      - id: check-builtin-literals
      - id: debug-statements
      - id: check-added-large-files
  - repo: https://github.com/asottile/seed-isort-config
    rev: v2.1.1
    hooks:
      - id: seed-isort-config
        # if we don't specify these the seeder will intermittently include
        # these as 'known third parties' which messes with our diffs
        args: ['--application-directories', './pyppeteer:./tests']
  - repo: https://github.com/timothycrosley/isort
    rev: 4.3.21
    hooks:
      - id: isort
        additional_dependencies: [toml]
  - repo: https://github.com/psf/black
    rev: stable
    hooks:
      - id: black
        language_version: python3


================================================
FILE: CHANGELOG.md
================================================
History
=======

## Version 2.0.0

* Bump pyee version, which removes support for Python 3.7
* Bumped included browser version to revision 1181205. It may not match the base p*u*ppeteer version, but at least it runs
* Fix invalid escape sequence (#453)
* Fix deprecated asyncio wait in page.py (#451)
* Remove version_info. `version` is still present and can be parsed if necessary

## Version 1.0.2

* Fix circular import as result of 1.0.1 (#344)

## Version 1.0.1

* don't configure logging ourselves (#343)
* make logging better for chromium downloader

## Version 1.0.0

* hotfix: websockets 10 for python 3.10 (#321, #327)
  * removes support for python 3.6./
* remove `Page.craete`

## Version 0.2.6

* Change build backend to poetry-core (allows for faster PEP517 package building) #262 @fabaff
* Chromium download fixes (file not found) #245 @mborsetti
* Do not try to set an exception on finished futures #216 @polyfloyd
* Add HTTPException to caught exceptions in launch #293 @raymondguo-db
* Fix encoding error #226 @aleksei140888
* support websockets 9.0 #252 @mborsetti
* Fix tqdm exception when NO_PROGRESS_BAR is True #224 @mborsetti
* fix(browser): Clean up coroutine Browser._targetCreated() #271 @H--o-I

## Version 0.2.5

* Match package version and \_\_version__ (🤦‍♂️)
* Use `importlib_metadata` so this isn't a problem in the future

## Version 0.2.4

* Update `pyee` dependency breaking build failures on NixOS + Fedora packaging systems (#207)

## Version 0.2.3

* Hotfix: random freezes from sending stdout to PIPE instead of DEVNULL
* Fix `tests` package being installed for no reason

## Version 0.0.26

* Add `$PYPPETEER_NO_PROGRESS_BAR` environment variable
* `pyppeteer.defaultArgs` now accepts that help infer chromium command-line flags.
* `pyppeteer.launch()` argument `ignoreDefaultArgs` now accepts a list of flags to ignore.
* `Page.type()` now supports typing emoji
* `Page.pdf()` accepts a new argument `preferCSSPageSize`
* Add new option `defaultViewport` to `launch()` and `connect()`
* Add `BrowserContext.pages()` method

## Version 0.0.25 (2018-09-27)

* Fix miss-spelled methods and functions
  * Change `Browser.isIncognite` to `Browser.isIncognito`
  * Change `Browser.createIncogniteBrowserContext` to `Browser.createIncognitoBrowserContext`
  * Change `chromium_excutable` to `chromium_executable`
  * Remove `craete` function in `page.py`

## Version 0.0.24 (2018-09-12)

Catch up puppeteer v1.6.0

* Add `ElementHandle.isIntersectingViewport()`
* Add `reportAnonymousScript` option to `Coverage.startJSCoverage()`
* Add `Page.waitForRequest` and `Page.waitForResponse` methods
* Now possible to attach to extension background pages with `Target.page()`
* Improved reliability of clicking with `Page.click()` and `ElementHandle.click()`

## Version 0.0.23 (2018-09-10)

Catch up puppeteer v1.5.0

* Add `BrowserContext` class
* Add `Worker` class
* Change `CDPSession.send` to a normal function which returns awaitable value
* Add `Page.isClosed` method
* Add `ElementHandle.querySelectorAllEval` and `ElementHandle.JJeval`
* Add `Target.opener`
* Add `Request.isNavigationRequest`

## Version 0.0.22 (2018-09-06)

Catch up puppeteer v1.4.0

* Add `pyppeteer.DEBUG` variable
* Add `Page.browser`
* Add `Target.browser`
* Add `ElementHandle.querySelectorEval` and `ElementHandle.Jeval`
* Add `runBeforeUnload` option to `Page.close` method
* Change `Page.querySelectorEval` to raise `ElementHandleError` when element which matches `selector` is not found
* Report 'Log' domain entries as 'console' events
* Fix `Page.goto` to return response when page pushes new state
* (OS X) Suppress long log when extracting chromium


## Version 0.0.21 (2018-08-21)

Catch up puppeteer v1.3.0

* Add `pyppeteer-install` command
* Add `autoClose` option to `launch` function
* Add `loop` option to `launch` function (experimental)
* Add `Page.setBypassCSP` method
* `Page.tracing.stop` returns result data
* Rename `documentloaded` to `domcontentloaded` on `waitUntil` option
* Fix `slowMo` option
* Fix anchor navigation
* Fix to return response via redirects
* Continue to find WS URL while process is alive


## Version 0.0.20 (2018-08-11)

* Run on msys/cygwin, anyway
* Raise error correctly when connection failed (PR#91)
* Change browser download location and temporary user data directory to:
    * If `$PYPPETEER_HOME` environment variable is defined, use this location
    * Otherwise, use platform dependent locations, based on [appdirs](https://pypi.org/project/appdirs/):
        * `'C:\Users\<username>\AppData\Local\pyppeteer'` (Windows)
        * `'/Users/<username>/Library/Application Support/pyppeteer'` (OS X)
        * `'/home/<username>/.local/share/pyppeteer'` (Linux)
            * or in `'$XDG_DATA_HOME/pyppeteer'` if `$XDG_DATA_HOME` is defined

* Introduce `$PYPPETEER_CHROMIUM_REVISION`
* Introduce `$PYPPETEER_HOME`
* Add `logLevel` option to `launch` and `connect` functions
* Add page `close` event
* Add `ElementHandle.boxModel` method
* Add an option to disable timeout for `waitFor` functions


## Version 0.0.19 (2018-07-05)

Catch up puppeteer v1.2.0

* Add `ElementHandle.contentFrame` method
* Add `Request.redirectChain` method
* `Page.addScriptTag` accepts a new option `type`


## Version 0.0.18 (2018-07-04)

Catch up puppeteer v1.1.1

* Add `Page.waitForXPath` and `Frame.waitForXPath`
* `Page.waitFor` accepts xpath string which starts with `//`
* Add `Response.fromCache` and `Response.fromServiceWorker`
* Add `SecurityDetails` class and `response.securityDetails`
* Add `Page.setCacheEnabled` method
* Add `ExecutionContext.frame`
* Add `dumpio` option to `launch` function
* Add `slowMo` option to `connect` function
* `launcher.connect` can be access from package top
  * `from pyppeteer import connect` is now valid
* Add `Frame.evaluateHandle`
* Add `Page.Events.DOMContentLoaded`


## Version 0.0.17 (2018-04-02)

* Mark as alpha

* Gracefully terminate browser process
* `Request.method` and `Request.postData` return `None` if no data
* Change `Target.url` and `Target.type` to properties
* Change `Dialog.message` and `Dialog.defaultValue` to properties
* Fix: properly emit `Browser.targetChanged` events
* Fix: properly emit `Browser.targetDestroyed` events


## Version 0.0.16 (2018-03-23)

* BugFix: Skip SIGHUP option on windows (windows does not support this signal)


## Version 0.0.15 (2018-03-22)

Catch up puppeteer v1.0.0

* Support `raf` and `mutation` polling for `waitFor*` methods
* Add `Page.coverage` to support JS and CSS coverage
* Add XPath support with `Page.xpath`, `Frame.xpath`, and `ElementHandle.xpath`
* Add `Target.createCDPSession` to work with raw Devtools Protocol
* Change `Frame.executionContext` from property to coroutine
* Add `ignoreDefaultArgs` option to `pyppeteer.launch`
* Add `handleSIGINT`/`handleSIGTERM`/`handleSIGHUP` options to `pyppeteer.launch`
* Add `Page.setDefaultNavigationTimeout` method
* `Page.waitFor*` methods accept `JSHandle` as argument
* Implement `Frame.content` and `Frame.setContent` methods
* `page.tracing.start` accepts custom tracing categories option
* Add `Browser.process` property
* Add `Request.frame` property


## Version 0.0.14 (2018-03-14)

* Read WS endpoint from web interface instead of stdout
* Pass environment variables of python process to chrome by default
* Do not limit size of websocket frames

* BugFix:
    * `Keyboard.type`
    * `Page.Events.Metrics`

## Version 0.0.13 (2018-03-10)

Catch up puppeteer v0.13.0

* `pyppeteer.launch()` is now **coroutine**
* Implement `connect` function
* `PYPPETEER_DOWNLOAD_HOST` env variable specifies host part of URL to download chromium
* Rename `setRequestInterceptionEnable` to `setRequestInterception`
* Rename `Page.getMetrics` to `Page.metrics`
* Implement `Browser.pages` to access all pages
    * Add `Target` class and some new method on Browser
* Add `ElementHandle.querySelector` and `ElementHandle.querySelectorAll`
* Refactor NavigatorWatcher
    * add `documentloaded`, `networkidle0`, and `networkidle2` options
* `Request.abort` accepts error code
* `addScriptTag` and `addStyleTag` return `ElementHandle`
* Add `force_expr` option to `evaluate` method
* `Page.select` returns selected values
* Add `pyppeteer.version` and `pyppeteer.version_info`

* BugFix:
    * Do not change original options dictionary
    * `Page.frames`
    * `Page.queryObjects`
    * `Page.exposeFunction`
    * Request interception
    * Console API
    * websocket error on closing browser (#24)

## Version 0.0.12 (2018-03-01)

* BugFix (#33)

## Version 0.0.11 (2018-03-01)

Catch up puppeteer v0.12.0

* Remove `ElementHandle.evaluate`
* Remove `ElementHandle.attribute`
* Deprecate `Page.plainText`
* Deprecate `Page.injectFile`
* Add `Page.querySelectorAllEval`
* Add `Page.select` and `Page.type`
* Add `ElementHandle.boundingBox` and `ElementHandle.screenshot`
* Add `ElementHandle.focus`, `ElementHandle.type`, and `ElementHandle.press`
* Add `getMetrics` method
* Add `offlineMode`

## Version 0.0.10 (2018-02-27)

* Enable to import `launch` from package root
* Change `browser.close` to coroutine function
* Catch up puppeteer v0.11.0

### Version 0.0.9 (2017-09-09)

* Delete temporary user data directory when browser closed
* Fix bug to fail extracting zip on mac

### Version 0.0.8 (2017-09-03)

* Change chromium revision
* Support steps option of `Mouse.move()`
* Experimentally supports python 3.5 by py-backwards

### Version 0.0.7 (2017-09-03)

* Catch up puppeteer v0.10.2
    * Add `Page.querySelectorEval` (`Page.$eval` in puppeteer)
    * Deprecate `ElementHandle.attribute`
    * Add `Touchscreen` class and implement `Page.tap` and `ElementHandle.tap`

### Version 0.0.6 (2017-09-02)

* Accept keyword arguments for options
* Faster polling on `waitFor*` functions
* Fix bugs

### Version 0.0.5 (2017-08-30)

* Implement pdf printing
* Implement `waitFor*` functions

### Version 0.0.4 (2017-08-30)

* Register PyPI


================================================
FILE: CONTRIBUTING.md
================================================
# Contribution guidelines

Contributions are welcome as long as they follow core rule of the project:

The API of pyppeteer should [__match the API of puppeteer__](https://github.com/puppeteer/puppeteer) as closely as possible without sacrificing python too much.
ie keep public API keywords such as method names, arguments, class names etc. as they are in puppeteer version.

Other than that the contributions should remain as pythonic as possible and pass linting and code tests.

Changes worthy of a changelog entry should get one - simply follow the existing format in CHANGELOG.md

## Maintainers - creating a release

 - Make sure all relevant changes have been recorded in the changelog
 - Ensure that code is properly tested
 - Bump the version in `pyproject.toml`, then tag the release in git
   - ex: `git tag -a 2.0.0rc1 -m "pypi release"`
 - Run `poetry build`
 - Run `poetry publish`


================================================
FILE: LICENSE
================================================

MIT License

Copyright (c) 2017, Hiroyuki Takagi

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

This software includes the work that is distributed in the Apache License 2.0.


================================================
FILE: README.md
================================================
### Attention: This repo is unmaintained and has been outside of minor changes for a long time. Please consider [playwright-python](https://github.com/microsoft/playwright-python) as an alternative. 
If you are interested in maintaining this, please contact [me](https://github.com/Mattwmaster58)

pyppeteer
==========

[![PyPI](https://img.shields.io/pypi/v/pyppeteer.svg)](https://pypi.python.org/pypi/pyppeteer)
[![PyPI version](https://img.shields.io/pypi/pyversions/pyppeteer.svg)](https://pypi.python.org/pypi/pyppeteer)
[![Documentation](https://img.shields.io/badge/docs-latest-brightgreen.svg)](https://pyppeteer.github.io/pyppeteer/)
[![CircleCI](https://circleci.com/gh/pyppeteer/pyppeteer.svg?style=shield)](https://circleci.com/gh/pyppeteer/pyppeteer)
[![codecov](https://codecov.io/gh/pyppeteer/pyppeteer/branch/dev/graph/badge.svg)](https://codecov.io/gh/pyppeteer/pyppeteer)

_Note: this is a continuation of the [pyppeteer project](https://github.com/miyakogi/pyppeteer)_

Unofficial Python port of [puppeteer](https://github.com/GoogleChrome/puppeteer) JavaScript (headless) chrome/chromium browser automation library.

* Free software: MIT license (including the work distributed under the Apache 2.0 license)
* Documentation: https://pyppeteer.github.io/pyppeteer/

## Installation

pyppeteer requires Python >= 3.8

Install with `pip` from PyPI:

```
pip install pyppeteer
```

Or install the latest version from [this github repo](https://github.com/pyppeteer/pyppeteer/):

```
pip install -U git+https://github.com/pyppeteer/pyppeteer@dev
```

## Usage

> **Note**: When you run pyppeteer for the first time, it downloads the latest version of Chromium (~150MB) if it is not found on your system. If you don't prefer this behavior, ensure that a suitable Chrome binary is installed. One way to do this is to run `pyppeteer-install` command before prior to using this library.

Full documentation can be found [here](https://pyppeteer.github.io/pyppeteer/reference.html). [Puppeteer's documentation](https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#) and [its troubleshooting guide](https://github.com/GoogleChrome/puppeteer/blob/master/docs/troubleshooting.md) are also great resources for pyppeteer users.

### Examples

Open web page and take a screenshot:
```py
import asyncio
from pyppeteer import launch

async def main():
    browser = await launch()
    page = await browser.newPage()
    await page.goto('https://example.com')
    await page.screenshot({'path': 'example.png'})
    await browser.close()

asyncio.get_event_loop().run_until_complete(main())
```

Evaluate javascript on a page:
```py
import asyncio
from pyppeteer import launch

async def main():
    browser = await launch()
    page = await browser.newPage()
    await page.goto('https://example.com')
    await page.screenshot({'path': 'example.png'})

    dimensions = await page.evaluate('''() => {
        return {
            width: document.documentElement.clientWidth,
            height: document.documentElement.clientHeight,
            deviceScaleFactor: window.devicePixelRatio,
        }
    }''')

    print(dimensions)
    # >>> {'width': 800, 'height': 600, 'deviceScaleFactor': 1}
    await browser.close()

asyncio.get_event_loop().run_until_complete(main())
```

## Differences between puppeteer and pyppeteer

pyppeteer strives to replicate the puppeteer API as close as possible, however, fundamental differences between Javascript and Python make this difficult to do precisely. More information on specifics can be found in the [documentation](https://pyppeteer.github.io/pyppeteer/reference.html).

### Keyword arguments for options

puppeteer uses an object for passing options to functions/methods. pyppeteer methods/functions accept both dictionary (python equivalent to JavaScript's objects) and keyword arguments for options.

Dictionary style options (similar to puppeteer):

```python
browser = await launch({'headless': True})
```

Keyword argument style options (more pythonic, isn't it?):

```python
browser = await launch(headless=True)
```

### Element selector method names

In python, `$` is not a valid identifier. The equivalent methods to Puppeteer's `$`, `$$`, and `$x` methods are listed below, along with some shorthand methods for your convenience:

| puppeteer | pyppeteer              | pyppeteer shorthand |
|-----------|-------------------------|----------------------|
| Page.$()  | Page.querySelector()    | Page.J()             |
| Page.$$() | Page.querySelectorAll() | Page.JJ()            |
| Page.$x() | Page.xpath()            | Page.Jx()            |

### Arguments of `Page.evaluate()` and `Page.querySelectorEval()`

puppeteer's version of `evaluate()` takes a JavaScript function or a string representation of a JavaScript expression. pyppeteer takes string representation of JavaScript expression or function. pyppeteer will try to automatically detect if the string is function or expression, but it will fail sometimes. If an expression is erroneously treated as function and an error is raised, try setting `force_expr` to `True`, to force pyppeteer to treat the string as expression.

### Examples:

Get a page's `textContent`:

```python
content = await page.evaluate('document.body.textContent', force_expr=True)
```

Get an element's `textContent`:

```python
element = await page.querySelector('h1')
title = await page.evaluate('(element) => element.textContent', element)
```

## Roadmap

See [projects](https://github.com/pyppeteer/pyppeteer/projects)

## Credits

###### This package was created with [Cookiecutter](https://github.com/audreyr/cookiecutter) and the [audreyr/cookiecutter-pypackage](https://github.com/audreyr/cookiecutter-pypackage) project template.


================================================
FILE: docs/Makefile
================================================
# Makefile for Sphinx documentation
#

# You can set these variables from the command line.
SPHINXOPTS    =
SPHINXBUILD   = sphinx-build
PAPER         =
BUILDDIR      = _build

# User-friendly check for sphinx-build
ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1)
$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/)
endif

# Internal variables.
PAPEROPT_a4     = -D latex_paper_size=a4
PAPEROPT_letter = -D latex_paper_size=letter
ALLSPHINXOPTS   = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
# the i18n builder cannot share the environment and doctrees with the others
I18NSPHINXOPTS  = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .

.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext

help:
	@echo "Please use \`make <target>' where <target> is one of"
	@echo "  html       to make standalone HTML files"
	@echo "  dirhtml    to make HTML files named index.html in directories"
	@echo "  singlehtml to make a single large HTML file"
	@echo "  pickle     to make pickle files"
	@echo "  json       to make JSON files"
	@echo "  htmlhelp   to make HTML files and a HTML help project"
	@echo "  qthelp     to make HTML files and a qthelp project"
	@echo "  devhelp    to make HTML files and a Devhelp project"
	@echo "  epub       to make an epub"
	@echo "  latex      to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
	@echo "  latexpdf   to make LaTeX files and run them through pdflatex"
	@echo "  latexpdfja to make LaTeX files and run them through platex/dvipdfmx"
	@echo "  text       to make text files"
	@echo "  man        to make manual pages"
	@echo "  texinfo    to make Texinfo files"
	@echo "  info       to make Texinfo files and run them through makeinfo"
	@echo "  gettext    to make PO message catalogs"
	@echo "  changes    to make an overview of all changed/added/deprecated items"
	@echo "  xml        to make Docutils-native XML files"
	@echo "  pseudoxml  to make pseudoxml-XML files for display purposes"
	@echo "  linkcheck  to check all external links for integrity"
	@echo "  doctest    to run all doctests embedded in the documentation (if enabled)"

clean:
	rm -rf $(BUILDDIR)/*

html:
	$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
	@echo
	@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."

dirhtml:
	$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
	@echo
	@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."

singlehtml:
	$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
	@echo
	@echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."

pickle:
	$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
	@echo
	@echo "Build finished; now you can process the pickle files."

json:
	$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
	@echo
	@echo "Build finished; now you can process the JSON files."

htmlhelp:
	$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
	@echo
	@echo "Build finished; now you can run HTML Help Workshop with the" \
	      ".hhp project file in $(BUILDDIR)/htmlhelp."

qthelp:
	$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
	@echo
	@echo "Build finished; now you can run "qcollectiongenerator" with the" \
	      ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
	@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/pyppeteer.qhcp"
	@echo "To view the help file:"
	@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/pyppeteer.qhc"

devhelp:
	$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
	@echo
	@echo "Build finished."
	@echo "To view the help file:"
	@echo "# mkdir -p $$HOME/.local/share/devhelp/pyppeteer"
	@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/pyppeteer"
	@echo "# devhelp"

epub:
	$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
	@echo
	@echo "Build finished. The epub file is in $(BUILDDIR)/epub."

latex:
	$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
	@echo
	@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
	@echo "Run \`make' in that directory to run these through (pdf)latex" \
	      "(use \`make latexpdf' here to do that automatically)."

latexpdf:
	$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
	@echo "Running LaTeX files through pdflatex..."
	$(MAKE) -C $(BUILDDIR)/latex all-pdf
	@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."

latexpdfja:
	$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
	@echo "Running LaTeX files through platex and dvipdfmx..."
	$(MAKE) -C $(BUILDDIR)/latex all-pdf-ja
	@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."

text:
	$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
	@echo
	@echo "Build finished. The text files are in $(BUILDDIR)/text."

man:
	$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
	@echo
	@echo "Build finished. The manual pages are in $(BUILDDIR)/man."

texinfo:
	$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
	@echo
	@echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
	@echo "Run \`make' in that directory to run these through makeinfo" \
	      "(use \`make info' here to do that automatically)."

info:
	$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
	@echo "Running Texinfo files through makeinfo..."
	make -C $(BUILDDIR)/texinfo info
	@echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."

gettext:
	$(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
	@echo
	@echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."

changes:
	$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
	@echo
	@echo "The overview file is in $(BUILDDIR)/changes."

linkcheck:
	$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
	@echo
	@echo "Link check complete; look for any errors in the above output " \
	      "or in $(BUILDDIR)/linkcheck/output.txt."

doctest:
	$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
	@echo "Testing of doctests in the sources finished, look at the " \
	      "results in $(BUILDDIR)/doctest/output.txt."

xml:
	$(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml
	@echo
	@echo "Build finished. The XML files are in $(BUILDDIR)/xml."

pseudoxml:
	$(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml
	@echo
	@echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml."


================================================
FILE: docs/_static/custom.css
================================================
h1.logo {
  font-family: "Raleway";
  font-weight: 500;
}

a.headerlink {
  color: rgba(0, 0, 0, 0.1);
}

div.sphinxsidebarwrapper p.blurb {
  font-family: Lato, sans-serif;
}

div.sphinxsidebar li.toctree-l1 {
  font-family: Lato, sans-serif;
}

body {
  background-color: #fafafa
}

.search-btn {
  padding: 0 1em;
  font-family: Lato, sans-serif;
  font-weight: normal;
  line-height: normal;
  align-self: stretch;
}


================================================
FILE: docs/_templates/layout.html
================================================
{% extends 'alabaster/layout.html' %}
{% block extrahead %}
  <!-- font -->
  <link href='https://fonts.googleapis.com/css?family=Raleway:500' rel='stylesheet' type='text/css'>
  <link href='https://fonts.googleapis.com/css?family=Lato:400,400italic' rel='stylesheet' type='text/css'>
  <link href='https://fonts.googleapis.com/css?family=Noto+Serif:400,400italic,700,700italic' rel='stylesheet' type='text/css'>
  <link href='https://fonts.googleapis.com/css?family=Ubuntu+Mono:400,400italic,700,700italic' rel='stylesheet' type='text/css'>
  <!-- Style -->
  {{ super() }}
{% endblock %}


================================================
FILE: docs/changes.md
================================================
.. mdinclude:: ../CHANGES.md


================================================
FILE: docs/conf.py
================================================
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# pyppeteer documentation build configuration file, created by
# sphinx-quickstart on Tue Jul  9 22:26:36 2013.
#
# This file is execfile()d with the current directory set to its
# containing dir.
#
# Note that not all possible configuration values are present in this
# autogenerated file.
#
# All configuration values have a default; values that are commented out
# serve to show the default.

import sys
import os

# If extensions (or modules to document with autodoc) are in another
# directory, add these directories to sys.path here. If the directory is
# relative to the documentation root, use os.path.abspath to make it
# absolute, like shown here.
# sys.path.insert(0, os.path.abspath('.'))

# Get the project root dir, which is the parent dir of this
cwd = os.getcwd()
project_root = os.path.dirname(cwd)

# Insert the project root dir as the first element in the PYTHONPATH.
# This lets us ensure that the source package is imported, and that its
# version is used.
sys.path.insert(0, project_root)

import pyppeteer

# -- General configuration ---------------------------------------------

# If your documentation needs a minimal Sphinx version, state it here.
# needs_sphinx = '1.0'

# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
extensions = [
    'sphinx.ext.autodoc',
    'sphinx.ext.githubpages',
    'sphinx.ext.viewcode',
    # 'sphinx_autodoc_typehints',
    'sphinxcontrib.asyncio',
    'm2r',
]

primary_domain = 'py'
default_role = 'py:obj'
# autodoc_member_order = 'bysource'
# include class' and __init__'s docstring
# autoclass_content = 'both'
# autodoc_docstring_signature = False
autodoc_default_flags = ['show-inheritance']

suppress_warnings = ['image.nonlocal_uri']

# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']

# The suffix(es) of source filenames.
# You can specify multiple suffix as a list of string:
source_suffix = ['.rst', '.md']

# The encoding of source files.
# source_encoding = 'utf-8-sig'

# The master toctree document.
master_doc = 'index'

# General information about the project.
project = 'Pyppeteer'
copyright = "2017, Hiroyuki Takagi"

# The version info for the project you're documenting, acts as replacement
# for |version| and |release|, also used in various other places throughout
# the built documents.
#
# The short X.Y version.
version = pyppeteer.__version__
# The full version, including alpha/beta/rc tags.
release = pyppeteer.__version__

# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
# language = None

# There are two options for replacing |today|: either, you set today to
# some non-false value, then it is used:
# today = ''
# Else, today_fmt is used as the format for a strftime call.
# today_fmt = '%B %d, %Y'

# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
exclude_patterns = ['_build']

# The reST default role (used for this markup: `text`) to use for all
# documents.
# default_role = None

# If true, '()' will be appended to :func: etc. cross-reference text.
# add_function_parentheses = True

# If true, the current module name will be prepended to all description
# unit titles (such as .. function::).
# add_module_names = True

# If true, sectionauthor and moduleauthor directives will be shown in the
# output. They are ignored by default.
# show_authors = False

# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'

# A list of ignored prefixes for module index sorting.
# modindex_common_prefix = []

# If true, keep warnings as "system message" paragraphs in the built
# documents.
# keep_warnings = False


# -- Options for HTML output -------------------------------------------

# The theme to use for HTML and HTML Help pages.  See the documentation for
# a list of builtin themes.
html_theme = 'alabaster'

# Theme options are theme-specific and customize the look and feel of a
# theme further.  For a list of options available for each theme, see the
# documentation.
html_theme_options = {
    'description': ('Headless chrome/chromium automation library '
                    '(unofficial port of puppeteer)'),
    'github_user': 'miyakogi',
    'github_repo': 'pyppeteer',
    'github_banner': True,
    'github_type': 'mark',
    'github_count': False,
    'font_family': '"Charis SIL", "Noto Serif", serif',
    'head_font_family': 'Lato, sans-serif',
    'code_font_family': '"Code new roman", "Ubuntu Mono", monospace',
    'code_font_size': '1rem',
}

# Add any paths that contain custom themes here, relative to this directory.
# html_theme_path = []

# The name for this set of Sphinx documents.  If None, it defaults to
# "<project> v<release> documentation".
# html_title = None

# A shorter title for the navigation bar.  Default is the same as
# html_title.
# html_short_title = None

# The name of an image file (relative to this directory) to place at the
# top of the sidebar.
# html_logo = None

# The name of an image file (within the static path) to use as favicon
# of the docs.  This file should be a Windows icon file (.ico) being
# 16x16 or 32x32 pixels large.
# html_favicon = None

# Add any paths that contain custom static files (such as style sheets)
# here, relative to this directory. They are copied after the builtin
# static files, so a file named "default.css" will overwrite the builtin
# "default.css".
html_static_path = ['_static']

# If not '', a 'Last updated on:' timestamp is inserted at every page
# bottom, using the given strftime format.
# html_last_updated_fmt = '%b %d, %Y'

# If true, SmartyPants will be used to convert quotes and dashes to
# typographically correct entities.
# html_use_smartypants = True

# Custom sidebar templates, maps document names to template names.
html_sidebars = {
    '**': [
        'about.html',
        'navigation.html',
        'relations.html',
        'searchbox.html',
    ]
}

# Additional templates that should be rendered to pages, maps page names
# to template names.
# html_additional_pages = {}

# If false, no module index is generated.
# html_domain_indices = True

# If false, no index is generated.
# html_use_index = True

# If true, the index is split into individual pages for each letter.
# html_split_index = False

# If true, links to the reST sources are added to the pages.
# html_show_sourcelink = True

# If true, "Created using Sphinx" is shown in the HTML footer.
# Default is True.
# html_show_sphinx = True

# If true, "(C) Copyright ..." is shown in the HTML footer.
# Default is True.
# html_show_copyright = True

# If true, an OpenSearch description file will be output, and all pages
# will contain a <link> tag referring to it.  The value of this option
# must be the base URL from which the finished HTML is served.
# html_use_opensearch = ''

# This is the file name suffix for HTML files (e.g. ".xhtml").
# html_file_suffix = None

# Output file base name for HTML help builder.
htmlhelp_basename = 'pyppeteerdoc'

# -- Options for LaTeX output ------------------------------------------

latex_elements = {
    # The paper size ('letterpaper' or 'a4paper').
    # 'papersize': 'letterpaper',

    # The font size ('10pt', '11pt' or '12pt').
    # 'pointsize': '10pt',

    # Additional stuff for the LaTeX preamble.
    # 'preamble': '',
}

# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title, author, documentclass
# [howto/manual]).
latex_documents = [
    ('index', 'pyppeteer.tex',
     'pyppeteer Documentation',
     'Hiroyuki Takagi', 'manual'),
]

# The name of an image file (relative to this directory) to place at
# the top of the title page.
# latex_logo = None

# For "manual" documents, if this is true, then toplevel headings
# are parts, not chapters.
# latex_use_parts = False

# If true, show page references after internal links.
# latex_show_pagerefs = False

# If true, show URL addresses after external links.
# latex_show_urls = False

# Documents to append as an appendix to all manuals.
# latex_appendices = []

# If false, no module index is generated.
# latex_domain_indices = True


# -- Options for manual page output ------------------------------------

# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
    ('index', 'pyppeteer',
     'pyppeteer Documentation',
     ['Hiroyuki Takagi'], 1)
]

# If true, show URL addresses after external links.
# man_show_urls = False


# -- Options for Texinfo output ----------------------------------------

# Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author,
#  dir menu entry, description, category)
texinfo_documents = [
    ('index', 'pyppeteer',
     'pyppeteer Documentation',
     'Hiroyuki Takagi',
     'pyppeteer',
     'One line description of project.',
     'Miscellaneous'),
]

# Documents to append as an appendix to all manuals.
# texinfo_appendices = []

# If false, no module index is generated.
# texinfo_domain_indices = True

# How to display URL addresses: 'footnote', 'no', or 'inline'.
# texinfo_show_urls = 'footnote'

# If true, do not generate a @detailmenu in the "Top" node's menu.
# texinfo_no_detailmenu = False


================================================
FILE: docs/index.md
================================================
Pyppeteer's documentation
=========================

.. mdinclude:: ../README.md


Contents
--------

.. toctree::
   :maxdepth: 2

   reference
   changes

Indices and tables
==================

* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`


================================================
FILE: docs/make.bat
================================================
@ECHO OFF

REM Command file for Sphinx documentation

if "%SPHINXBUILD%" == "" (
	set SPHINXBUILD=sphinx-build
)
set BUILDDIR=_build
set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% .
set I18NSPHINXOPTS=%SPHINXOPTS% .
if NOT "%PAPER%" == "" (
	set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
	set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS%
)

if "%1" == "" goto help

if "%1" == "help" (
	:help
	echo.Please use `make ^<target^>` where ^<target^> is one of
	echo.  html       to make standalone HTML files
	echo.  dirhtml    to make HTML files named index.html in directories
	echo.  singlehtml to make a single large HTML file
	echo.  pickle     to make pickle files
	echo.  json       to make JSON files
	echo.  htmlhelp   to make HTML files and a HTML help project
	echo.  qthelp     to make HTML files and a qthelp project
	echo.  devhelp    to make HTML files and a Devhelp project
	echo.  epub       to make an epub
	echo.  latex      to make LaTeX files, you can set PAPER=a4 or PAPER=letter
	echo.  text       to make text files
	echo.  man        to make manual pages
	echo.  texinfo    to make Texinfo files
	echo.  gettext    to make PO message catalogs
	echo.  changes    to make an overview over all changed/added/deprecated items
	echo.  xml        to make Docutils-native XML files
	echo.  pseudoxml  to make pseudoxml-XML files for display purposes
	echo.  linkcheck  to check all external links for integrity
	echo.  doctest    to run all doctests embedded in the documentation if enabled
	goto end
)

if "%1" == "clean" (
	for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i
	del /q /s %BUILDDIR%\*
	goto end
)


%SPHINXBUILD% 2> nul
if errorlevel 9009 (
	echo.
	echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
	echo.installed, then set the SPHINXBUILD environment variable to point
	echo.to the full path of the 'sphinx-build' executable. Alternatively you
	echo.may add the Sphinx directory to PATH.
	echo.
	echo.If you don't have Sphinx installed, grab it from
	echo.http://sphinx-doc.org/
	exit /b 1
)

if "%1" == "html" (
	%SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html
	if errorlevel 1 exit /b 1
	echo.
	echo.Build finished. The HTML pages are in %BUILDDIR%/html.
	goto end
)

if "%1" == "dirhtml" (
	%SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
	if errorlevel 1 exit /b 1
	echo.
	echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.
	goto end
)

if "%1" == "singlehtml" (
	%SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml
	if errorlevel 1 exit /b 1
	echo.
	echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml.
	goto end
)

if "%1" == "pickle" (
	%SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle
	if errorlevel 1 exit /b 1
	echo.
	echo.Build finished; now you can process the pickle files.
	goto end
)

if "%1" == "json" (
	%SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json
	if errorlevel 1 exit /b 1
	echo.
	echo.Build finished; now you can process the JSON files.
	goto end
)

if "%1" == "htmlhelp" (
	%SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp
	if errorlevel 1 exit /b 1
	echo.
	echo.Build finished; now you can run HTML Help Workshop with the ^
.hhp project file in %BUILDDIR%/htmlhelp.
	goto end
)

if "%1" == "qthelp" (
	%SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp
	if errorlevel 1 exit /b 1
	echo.
	echo.Build finished; now you can run "qcollectiongenerator" with the ^
.qhcp project file in %BUILDDIR%/qthelp, like this:
	echo.^> qcollectiongenerator %BUILDDIR%\qthelp\pyppeteer.qhcp
	echo.To view the help file:
	echo.^> assistant -collectionFile %BUILDDIR%\qthelp\pyppeteer.ghc
	goto end
)

if "%1" == "devhelp" (
	%SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp
	if errorlevel 1 exit /b 1
	echo.
	echo.Build finished.
	goto end
)

if "%1" == "epub" (
	%SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub
	if errorlevel 1 exit /b 1
	echo.
	echo.Build finished. The epub file is in %BUILDDIR%/epub.
	goto end
)

if "%1" == "latex" (
	%SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
	if errorlevel 1 exit /b 1
	echo.
	echo.Build finished; the LaTeX files are in %BUILDDIR%/latex.
	goto end
)

if "%1" == "latexpdf" (
	%SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
	cd %BUILDDIR%/latex
	make all-pdf
	cd %BUILDDIR%/..
	echo.
	echo.Build finished; the PDF files are in %BUILDDIR%/latex.
	goto end
)

if "%1" == "latexpdfja" (
	%SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
	cd %BUILDDIR%/latex
	make all-pdf-ja
	cd %BUILDDIR%/..
	echo.
	echo.Build finished; the PDF files are in %BUILDDIR%/latex.
	goto end
)

if "%1" == "text" (
	%SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text
	if errorlevel 1 exit /b 1
	echo.
	echo.Build finished. The text files are in %BUILDDIR%/text.
	goto end
)

if "%1" == "man" (
	%SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man
	if errorlevel 1 exit /b 1
	echo.
	echo.Build finished. The manual pages are in %BUILDDIR%/man.
	goto end
)

if "%1" == "texinfo" (
	%SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo
	if errorlevel 1 exit /b 1
	echo.
	echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo.
	goto end
)

if "%1" == "gettext" (
	%SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale
	if errorlevel 1 exit /b 1
	echo.
	echo.Build finished. The message catalogs are in %BUILDDIR%/locale.
	goto end
)

if "%1" == "changes" (
	%SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes
	if errorlevel 1 exit /b 1
	echo.
	echo.The overview file is in %BUILDDIR%/changes.
	goto end
)

if "%1" == "linkcheck" (
	%SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck
	if errorlevel 1 exit /b 1
	echo.
	echo.Link check complete; look for any errors in the above output ^
or in %BUILDDIR%/linkcheck/output.txt.
	goto end
)

if "%1" == "doctest" (
	%SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest
	if errorlevel 1 exit /b 1
	echo.
	echo.Testing of doctests in the sources finished, look at the ^
results in %BUILDDIR%/doctest/output.txt.
	goto end
)

if "%1" == "xml" (
	%SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml
	if errorlevel 1 exit /b 1
	echo.
	echo.Build finished. The XML files are in %BUILDDIR%/xml.
	goto end
)

if "%1" == "pseudoxml" (
	%SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml
	if errorlevel 1 exit /b 1
	echo.
	echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml.
	goto end
)

:end


================================================
FILE: docs/reference.md
================================================
API Reference
=============

Commands
--------

* ``pyppeteer-install``: Download and install chromium for pyppeteer.

Environment Variables
---------------------

* ``$PYPPETEER_HOME``: Specify the directory to be used by pyppeteer.
  Pyppeteer uses this directory for extracting downloaded Chromium, and for
  making temporary user data directory.
  Default location depends on platform:
  * Windows: `C:\Users\<username>\AppData\Local\pyppeteer`
  * OS X: `/Users/<username>/Library/Application Support/pyppeteer`
  * Linux: `/home/<username>/.local/share/pyppeteer`
    * or in `$XDG_DATA_HOME/pyppeteer` if `$XDG_DATA_HOME` is defined.

  Details see [appdirs](https://pypi.org/project/appdirs/)'s `user_data_dir`.

* ``$PYPPETEER_DOWNLOAD_HOST``: Overwrite host part of URL that is used to
  download Chromium. Defaults to ``https://storage.googleapis.com``.

* ``$PYPPETEER_CHROMIUM_REVISION``: Specify a certain version of chromium you'd
  like pyppeteer to use. Default value can be checked by
  ``pyppeteer.__chromium_revision__``.

* ``$PYPPETEER_NO_PROGRESS_BAR``: Suppress showing progress bar in chromium
  download process. Acceptable values are ``1`` or ``true`` (case-insensitive).


Pyppeteer Main Module
---------------------

.. currentmodule:: pyppeteer

.. autofunction:: launch
.. autofunction:: connect
.. autofunction:: defaultArgs
.. autofunction:: executablePath

Browser Class
-------------

.. currentmodule:: pyppeteer.browser

.. autoclass:: pyppeteer.browser.Browser
   :members:
   :exclude-members: create

BrowserContext Class
--------------------

.. currentmodule:: pyppeteer.browser

.. autoclass:: pyppeteer.browser.BrowserContext
   :members:

Page Class
----------

.. currentmodule:: pyppeteer.page

.. autoclass:: pyppeteer.page.Page
   :members:
   :exclude-members: create

Worker Class
------------

.. currentmodule:: pyppeteer.worker

.. autoclass:: pyppeteer.worker.Worker
   :members:

Keyboard Class
--------------

.. currentmodule:: pyppeteer.input

.. autoclass:: pyppeteer.input.Keyboard
   :members:

Mouse Class
-----------

.. currentmodule:: pyppeteer.input

.. autoclass:: pyppeteer.input.Mouse
   :members:

Tracing Class
-------------

.. currentmodule:: pyppeteer.tracing

.. autoclass:: pyppeteer.tracing.Tracing
   :members:

Dialog Class
------------

.. currentmodule:: pyppeteer.dialog

.. autoclass:: pyppeteer.dialog.Dialog
   :members:

ConsoleMessage Class
--------------------

.. currentmodule:: pyppeteer.page

.. autoclass:: pyppeteer.page.ConsoleMessage
   :members:

Frame Class
-----------

.. currentmodule:: pyppeteer.frame

.. autoclass:: pyppeteer.frame_manager.Frame
   :members:

ExecutionContext Class
----------------------

.. currentmodule:: pyppeteer.execution_context

.. autoclass:: pyppeteer.execution_context.ExecutionContext
   :members:

JSHandle Class
--------------

.. autoclass:: pyppeteer.execution_context.JSHandle
   :members:

ElementHandle Class
-------------------

.. currentmodule:: pyppeteer.element_handle

.. autoclass:: pyppeteer.element_handle.ElementHandle
   :members:

Request Class
-------------

.. currentmodule:: pyppeteer.network_manager

.. autoclass:: pyppeteer.network_manager.Request
   :members:

Response Class
--------------

.. currentmodule:: pyppeteer.network_manager

.. autoclass:: pyppeteer.network_manager.Response
   :members:

Target Class
------------

.. currentmodule:: pyppeteer.target

.. autoclass:: pyppeteer.target.Target
   :members:

CDPSession Class
----------------

.. currentmodule:: pyppeteer.connection

.. autoclass:: pyppeteer.connection.CDPSession
   :members:

Coverage Class
--------------

.. currentmodule:: pyppeteer.coverage

.. autoclass:: pyppeteer.coverage.Coverage
   :members:

Debugging
---------

For debugging, you can set `logLevel` option to `logging.DEBUG` for
:func:`pyppeteer.launcher.launch` and :func:`pyppeteer.launcher.connect`
functions. However, this option prints too many logs including SEND/RECV
messages of pyppeteer. In order to only show suppressed error messages, you
should set ``pyppeteer.DEBUG`` to ``True``.

Example:

```python
import asyncio
import pyppeteer
from pyppeteer import launch

pyppeteer.DEBUG = True  # print suppressed errors as error log

async def main():
    browser = await launch()
    ...  # do something

asyncio.get_event_loop().run_until_complete(main())
```


================================================
FILE: docs/server.py
================================================
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

from os import path
import subprocess

from livereload import Server
from livereload import watcher

watcher.pyinotify = None  # disable pyinotify

docsdir = path.dirname(path.abspath(__file__))
builddir = path.join(docsdir, '_build')
build_cmd = [
    'sphinx-build', '-q', '-j', 'auto', '-b', 'html',
    '-d', path.join(builddir, 'doctrees'),
    docsdir, path.join(builddir, 'html'),
]


def cmd() -> None:
    print('=== Sphinx Build Start ===')
    subprocess.run(build_cmd, cwd=docsdir)
    print('=== Sphinx Build done ===')


# subprocess.run(['make', 'clean'], cwd=docsdir)
cmd()
server = Server()


def docs(p: str) -> str:
    return path.join(docsdir, p)


# Watch documents
server.watch(docs('*.py'), cmd, delay=1)
server.watch(docs('*.md'), cmd, delay=1)
server.watch(docs('../*.md'), cmd, delay=1)
server.watch(docs('*.md'), cmd, delay=1)
server.watch(docs('*/*.md'), cmd, delay=1)
server.watch(docs('*/*/*.md'), cmd, delay=1)

# Watch template/style
server.watch(docs('_templates/*.html'), cmd, delay=1)
server.watch(docs('_static/*.css'), cmd, delay=1)
server.watch(docs('_static/*.js'), cmd, delay=1)

# Watch package
server.watch(docs('../pyppeteer/*.py'), cmd, delay=1)
server.watch(docs('../pyppeteer/*/*.py'), cmd, delay=1)
server.watch(docs('../pyppeteer/*/*/*.py'), cmd, delay=1)

server.serve(port=8889, root=docs('_build/html'), debug=True, restart_delay=1)


================================================
FILE: pyppeteer/__init__.py
================================================
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""Meta data for pyppeteer."""

import logging
import os

from appdirs import AppDirs

from importlib.metadata import version

try:
    __version__ = version(__name__)
except Exception:
    __version__ = None


# old chrome version panic upon launching - this one may not match the base puppeteer version, but at least it launches
__chromium_revision__ = '1181205'
__base_puppeteer_version__ = 'v1.6.0'
__pyppeteer_home__ = os.environ.get('PYPPETEER_HOME', AppDirs('pyppeteer').user_data_dir)  # type: str
DEBUG = False

from pyppeteer.launcher import connect, executablePath, launch, defaultArgs  # noqa: E402; noqa: E402

version = __version__

__all__ = [
    'connect',
    'launch',
    'executablePath',
    'defaultArgs',
    'version',
]


================================================
FILE: pyppeteer/browser.py
================================================
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""Browser module."""

import logging
from subprocess import Popen
from types import SimpleNamespace
from typing import Any, Awaitable, Callable, Dict, List, Optional

from pyee import EventEmitter

from pyppeteer.connection import Connection
from pyppeteer.errors import BrowserError
from pyppeteer.page import Page
from pyppeteer.target import Target

logger = logging.getLogger(__name__)


class Browser(EventEmitter):
    """Browser class.

    A Browser object is created when pyppeteer connects to chrome, either
    through :func:`~pyppeteer.launcher.launch` or
    :func:`~pyppeteer.launcher.connect`.
    """

    Events = SimpleNamespace(
        TargetCreated='targetcreated',
        TargetDestroyed='targetdestroyed',
        TargetChanged='targetchanged',
        Disconnected='disconnected',
    )

    def __init__(self, connection: Connection, contextIds: List[str],
                 ignoreHTTPSErrors: bool, defaultViewport: Optional[Dict],
                 process: Optional[Popen] = None,
                 closeCallback: Callable[[], Awaitable[None]] = None,
                 **kwargs: Any) -> None:
        super().__init__()
        self._ignoreHTTPSErrors = ignoreHTTPSErrors
        self._defaultViewport = defaultViewport
        self._process = process
        self._screenshotTaskQueue: List = []
        self._connection = connection
        loop = self._connection._loop

        def _dummy_callback() -> Awaitable[None]:
            fut = loop.create_future()
            fut.set_result(None)
            return fut

        if closeCallback:
            self._closeCallback = closeCallback
        else:
            self._closeCallback = _dummy_callback

        self._defaultContext = BrowserContext(self, None)
        self._contexts: Dict[str, BrowserContext] = dict()
        for contextId in contextIds:
            self._contexts[contextId] = BrowserContext(self, contextId)

        self._targets: Dict[str, Target] = dict()
        self._connection.setClosedCallback(
            lambda: self.emit(Browser.Events.Disconnected)
        )
        self._connection.on(
            'Target.targetCreated',
            lambda event: loop.create_task(self._targetCreated(event)),
        )
        self._connection.on(
            'Target.targetDestroyed',
            lambda event: loop.create_task(self._targetDestroyed(event)),
        )
        self._connection.on(
            'Target.targetInfoChanged',
            lambda event: loop.create_task(self._targetInfoChanged(event)),
        )

    @property
    def process(self) -> Optional[Popen]:
        """Return process of this browser.

        If browser instance is created by :func:`pyppeteer.launcher.connect`,
        return ``None``.
        """
        return self._process

    async def createIncogniteBrowserContext(self) -> 'BrowserContext':
        """[Deprecated] Miss spelled method.

        Use :meth:`createIncognitoBrowserContext` method instead.
        """
        logger.warning(
            'createIncogniteBrowserContext is deprecated. '
            'Use createIncognitoBrowserContext instead.'
        )
        return await self.createIncognitoBrowserContext()

    async def createIncognitoBrowserContext(self) -> 'BrowserContext':
        """Create a new incognito browser context.

        This won't share cookies/cache with other browser contexts.

        .. code::

            browser = await launch()
            # Create a new incognito browser context.
            context = await browser.createIncognitoBrowserContext()
            # Create a new page in a pristine context.
            page = await context.newPage()
            # Do stuff
            await page.goto('https://example.com')
            ...
        """
        obj = await self._connection.send('Target.createBrowserContext')
        browserContextId = obj['browserContextId']
        context = BrowserContext(self, browserContextId)  # noqa: E501
        self._contexts[browserContextId] = context
        return context

    @property
    def browserContexts(self) -> List['BrowserContext']:
        """Return a list of all open browser contexts.

        In a newly created browser, this will return a single instance of
        ``[BrowserContext]``
        """
        return [self._defaultContext] + [context for context in self._contexts.values()]  # noqa: E501

    async def _disposeContext(self, contextId: str) -> None:
        await self._connection.send('Target.disposeBrowserContext', {
            'browserContextId': contextId,
        })
        self._contexts.pop(contextId, None)

    @staticmethod
    async def create(connection: Connection, contextIds: List[str],
                     ignoreHTTPSErrors: bool, defaultViewport: Optional[Dict],
                     process: Optional[Popen] = None,
                     closeCallback: Callable[[], Awaitable[None]] = None,
                     **kwargs: Any) -> 'Browser':
        """Create browser object."""
        browser = Browser(connection, contextIds, ignoreHTTPSErrors,
                          defaultViewport, process, closeCallback)
        await connection.send('Target.setDiscoverTargets', {'discover': True})
        return browser

    async def _targetCreated(self, event: Dict) -> None:
        targetInfo = event['targetInfo']
        browserContextId = targetInfo.get('browserContextId')

        if browserContextId and browserContextId in self._contexts:
            context = self._contexts[browserContextId]
        else:
            context = self._defaultContext

        target = Target(
            targetInfo,
            context,
            lambda: self._connection.createSession(targetInfo),
            self._ignoreHTTPSErrors,
            self._defaultViewport,
            self._screenshotTaskQueue,
            self._connection._loop,
        )
        if targetInfo['targetId'] in self._targets:
            raise BrowserError('target should not exist before create.')
        self._targets[targetInfo['targetId']] = target
        if await target._initializedPromise:
            self.emit(Browser.Events.TargetCreated, target)
            context.emit(BrowserContext.Events.TargetCreated, target)

    async def _targetDestroyed(self, event: Dict) -> None:
        target = self._targets[event['targetId']]
        del self._targets[event['targetId']]
        target._closedCallback()
        if await target._initializedPromise:
            self.emit(Browser.Events.TargetDestroyed, target)
            target.browserContext.emit(BrowserContext.Events.TargetDestroyed, target)  # noqa: E501
        target._initializedCallback(False)

    async def _targetInfoChanged(self, event: Dict) -> None:
        target = self._targets.get(event['targetInfo']['targetId'])
        if not target:
            raise BrowserError('target should exist before targetInfoChanged')
        previousURL = target.url
        wasInitialized = target._isInitialized
        target._targetInfoChanged(event['targetInfo'])
        if wasInitialized and previousURL != target.url:
            self.emit(Browser.Events.TargetChanged, target)
            target.browserContext.emit(BrowserContext.Events.TargetChanged, target)  # noqa: E501

    @property
    def wsEndpoint(self) -> str:
        """Return websocket end point url."""
        return self._connection.url

    async def newPage(self) -> Page:
        """Make new page on this browser and return its object."""
        return await self._defaultContext.newPage()

    async def _createPageInContext(self, contextId: Optional[str]) -> Page:
        options = {'url': 'about:blank'}
        if contextId:
            options['browserContextId'] = contextId

        targetId = (await self._connection.send(
            'Target.createTarget', options)).get('targetId')
        target = self._targets.get(targetId)
        if target is None:
            raise BrowserError('Failed to create target for page.')
        if not await target._initializedPromise:
            raise BrowserError('Failed to create target for page.')
        page = await target.page()
        if page is None:
            raise BrowserError('Failed to create page.')
        return page

    def targets(self) -> List[Target]:
        """Get a list of all active targets inside the browser.

        In case of multiple browser contexts, the method will return a list
        with all the targets in all browser contexts.
        """
        return [target for target in self._targets.values()
                if target._isInitialized]

    async def pages(self) -> List[Page]:
        """Get all pages of this browser.

        Non visible pages, such as ``"background_page"``, will not be listed
        here. You can find then using :meth:`pyppeteer.target.Target.page`.

        In case of multiple browser contexts, this method will return a list
        with all the pages in all browser contexts.
        """
        # Using asyncio.gather is better for performance
        pages: List[Page] = list()
        for context in self.browserContexts:
            pages.extend(await context.pages())
        return pages

    async def version(self) -> str:
        """Get version of the browser."""
        version = await self._getVersion()
        return version['product']

    async def userAgent(self) -> str:
        """Return browser's original user agent.

        .. note::
            Pages can override browser user agent with
            :meth:`pyppeteer.page.Page.setUserAgent`.
        """
        version = await self._getVersion()
        return version.get('userAgent', '')

    async def close(self) -> None:
        """Close connections and terminate browser process."""
        await self._closeCallback()  # Launcher.killChrome()

    async def disconnect(self) -> None:
        """Disconnect browser."""
        await self._connection.dispose()
        for target in self._targets.values():
            if not target._isInitialized:
                target._initializedCallback(False)

    def _getVersion(self) -> Awaitable:
        return self._connection.send('Browser.getVersion')


class BrowserContext(EventEmitter):
    """BrowserContext provides multiple independent browser sessions.

    When a browser is launched, it has a single BrowserContext used by default.
    The method `browser.newPage()` creates a page in the default browser
    context.

    If a page opens another page, e.g. with a ``window.open`` call, the popup
    will belong to the parent page's browser context.

    Pyppeteer allows creation of "incognito" browser context with
    ``browser.createIncognitoBrowserContext()`` method.
    "incognito" browser contexts don't write any browser data to disk.

    .. code::

        # Create new incognito browser context
        context = await browser.createIncognitoBrowserContext()
        # Create a new page inside context
        page = await context.newPage()
        # ... do stuff with page ...
        await page.goto('https://example.com')
        # Dispose context once it's no longer needed
        await context.close()
    """

    Events = SimpleNamespace(
        TargetCreated='targetcreated',
        TargetDestroyed='targetdestroyed',
        TargetChanged='targetchanged',
    )

    def __init__(self, browser: Browser, contextId: Optional[str]) -> None:
        super().__init__()
        self._browser = browser
        self._id = contextId

    def targets(self) -> List[Target]:
        """Return a list of all active targets inside the browser context."""
        targets = []
        for target in self._browser.targets():
            if target.browserContext == self:
                targets.append(target)
        return targets

    async def pages(self) -> List[Page]:
        """Return list of all open pages.

        Non-visible pages, such as ``"background_page"``, will not be listed
        here. You can find them using :meth:`pyppeteer.target.Target.page`.
        """
        # Using asyncio.gather is better for performance
        pages = []
        for target in self.targets():
            if target.type == 'page':
                page = await target.page()
                if page:
                    pages.append(page)
        return pages

    def isIncognite(self) -> bool:
        """[Deprecated] Miss spelled method.

        Use :meth:`isIncognito` method instead.
        """
        logger.warning(
            'isIncognite is deprecated. '
            'Use isIncognito instead.'
        )
        return self.isIncognito()

    def isIncognito(self) -> bool:
        """Return whether BrowserContext is incognito.

        The default browser context is the only non-incognito browser context.

        .. note::
            The default browser context cannot be closed.
        """
        return bool(self._id)

    async def newPage(self) -> Page:
        """Create a new page in the browser context."""
        return await self._browser._createPageInContext(self._id)

    @property
    def browser(self) -> Browser:
        """Return the browser this browser context belongs to."""
        return self._browser

    async def close(self) -> None:
        """Close the browser context.

        All the targets that belongs to the browser context will be closed.

        .. note::
            Only incognito browser context can be closed.
        """
        if self._id is None:
            raise BrowserError('Non-incognito profile cannot be closed')
        await self._browser._disposeContext(self._id)


================================================
FILE: pyppeteer/chromium_downloader.py
================================================
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""Chromium download module."""

import logging
import os
import stat
import sys
from io import BytesIO
from pathlib import Path
from zipfile import ZipFile

import certifi
import urllib3
from pyppeteer import __chromium_revision__, __pyppeteer_home__
from tqdm import tqdm

logger = logging.getLogger(__name__)
# add our own stream handler - we want some output here
handler = logging.StreamHandler()
handler.setFormatter(fmt=logging.Formatter(fmt="[{levelname}] {msg}", style="{"))
handler.setLevel(logging.INFO)
logger.setLevel(logging.INFO)
logger.addHandler(handler)

DOWNLOADS_FOLDER = Path(__pyppeteer_home__) / 'local-chromium'
DEFAULT_DOWNLOAD_HOST = 'https://storage.googleapis.com'
DOWNLOAD_HOST = os.environ.get('PYPPETEER_DOWNLOAD_HOST', DEFAULT_DOWNLOAD_HOST)
BASE_URL = f'{DOWNLOAD_HOST}/chromium-browser-snapshots'

REVISION = os.environ.get('PYPPETEER_CHROMIUM_REVISION', __chromium_revision__)

NO_PROGRESS_BAR = os.environ.get('PYPPETEER_NO_PROGRESS_BAR', '')
if NO_PROGRESS_BAR.lower() in ('1', 'true'):
    NO_PROGRESS_BAR = True  # type: ignore

windowsArchive = 'chrome-win'

downloadURLs = {
    'linux': f'{BASE_URL}/Linux_x64/{REVISION}/chrome-linux.zip',
    'mac': f'{BASE_URL}/Mac/{REVISION}/chrome-mac.zip',
    'win32': f'{BASE_URL}/Win/{REVISION}/{windowsArchive}.zip',
    'win64': f'{BASE_URL}/Win_x64/{REVISION}/{windowsArchive}.zip',
}

chromiumExecutable = {
    'linux': DOWNLOADS_FOLDER / REVISION / 'chrome-linux' / 'chrome',
    'mac': (DOWNLOADS_FOLDER / REVISION / 'chrome-mac' / 'Chromium.app' / 'Contents' / 'MacOS' / 'Chromium'),
    'win32': DOWNLOADS_FOLDER / REVISION / windowsArchive / 'chrome.exe',
    'win64': DOWNLOADS_FOLDER / REVISION / windowsArchive / 'chrome.exe',
}


def current_platform() -> str:
    """Get current platform name by short string."""
    if sys.platform.startswith('linux'):
        return 'linux'
    elif sys.platform.startswith('darwin'):
        return 'mac'
    elif sys.platform.startswith('win') or sys.platform.startswith('msys') or sys.platform.startswith('cyg'):
        if sys.maxsize > 2 ** 31 - 1:
            return 'win64'
        return 'win32'
    raise OSError('Unsupported platform: ' + sys.platform)


def get_url() -> str:
    """Get chromium download url."""
    return downloadURLs[current_platform()]


def download_zip(url: str) -> BytesIO:
    """Download data from url."""
    logger.info('Starting Chromium download.')

    with urllib3.PoolManager(cert_reqs='CERT_REQUIRED', ca_certs=certifi.where()) as http:
        # Get data from url.
        # set preload_content=False means using stream later.
        r = http.request('GET', url, preload_content=False)
        if r.status >= 400:
            raise OSError(f'Chromium downloadable not found at {url}: ' f'Received {r.data.decode()}.\n')

        # 10 * 1024
        _data = BytesIO()
        if NO_PROGRESS_BAR:
            for chunk in r.stream(10240):
                _data.write(chunk)
        else:
            try:
                total_length = int(r.headers['content-length'])
            except (KeyError, ValueError, AttributeError):
                total_length = 0
            process_bar = tqdm(total=total_length, unit_scale=True, unit='b')
            for chunk in r.stream(10240):
                _data.write(chunk)
                process_bar.update(len(chunk))
            process_bar.close()

    return _data


def extract_zip(data: BytesIO, path: Path) -> None:
    """Extract zipped data to path."""
    # On mac zipfile module cannot extract correctly, so use unzip instead.
    logger.info('Beginning extraction')
    if current_platform() == 'mac':
        import subprocess
        import shutil

        zip_path = path / 'chrome.zip'
        if not path.exists():
            path.mkdir(parents=True)
        with zip_path.open('wb') as f:
            f.write(data.getvalue())
        if not shutil.which('unzip'):
            raise OSError('Failed to automatically extract chromium.' f'Please unzip {zip_path} manually.')
        proc = subprocess.run(
            ['unzip', str(zip_path)], cwd=str(path), stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
        )
        if proc.returncode != 0:
            logger.error(proc.stdout.decode())
            raise OSError(f'Failed to unzip {zip_path}.')
        if chromium_executable().exists() and zip_path.exists():
            zip_path.unlink()
    else:
        with ZipFile(data) as zf:
            zf.extractall(str(path))
    exec_path = chromium_executable()
    if not exec_path.exists():
        raise IOError('Failed to extract chromium.')
    exec_path.chmod(exec_path.stat().st_mode | stat.S_IXOTH | stat.S_IXGRP | stat.S_IXUSR)
    logger.info(f'Chromium extracted to: {path}')


def download_chromium() -> None:
    """Download and extract chromium."""
    extract_zip(download_zip(get_url()), DOWNLOADS_FOLDER / REVISION)


def chromium_executable() -> Path:
    """Get path of the chromium executable."""
    return chromiumExecutable[current_platform()]


def check_chromium() -> bool:
    """Check if chromium is placed at correct path."""
    return chromium_executable().exists()


================================================
FILE: pyppeteer/command.py
================================================
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""Commands for Pyppeteer."""

import logging

from pyppeteer.chromium_downloader import check_chromium, download_chromium


def install() -> None:
    """Download chromium if not install."""
    if not check_chromium():
        download_chromium()
    else:
        logging.getLogger(__name__).warning('chromium is already installed.')


================================================
FILE: pyppeteer/connection.py
================================================
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""Connection/Session management module."""

import asyncio
import json
import logging
from typing import Awaitable, Callable, Dict, Union, TYPE_CHECKING

from pyee import EventEmitter
import websockets
from websockets.legacy.client import connect as ws_connect

from pyppeteer.errors import NetworkError

if TYPE_CHECKING:
    from typing import Optional  # noqa: F401

logger = logging.getLogger(__name__)
logger_connection = logging.getLogger(__name__ + '.Connection')
logger_session = logging.getLogger(__name__ + '.CDPSession')


class Connection(EventEmitter):
    """Connection management class."""

    def __init__(self, url: str, loop: asyncio.AbstractEventLoop,
                 delay: int = 0) -> None:
        """Make connection.

        :arg str url: WebSocket url to connect devtool.
        :arg int delay: delay to wait before processing received messages.
        """
        super().__init__()
        self._url = url
        self._lastId = 0
        self._callbacks: Dict[int, asyncio.Future] = dict()
        self._delay = delay / 1000
        self._loop = loop
        self._sessions: Dict[str, CDPSession] = dict()
        self.connection: CDPSession
        self._connected = False
        self._ws = ws_connect(self._url, max_size=None, loop=self._loop, ping_interval=None, ping_timeout=None)
        self._recv_fut = self._loop.create_task(self._recv_loop())
        self._closeCallback: Optional[Callable[[], None]] = None

    @property
    def url(self) -> str:
        """Get connected WebSocket url."""
        return self._url

    async def _recv_loop(self) -> None:
        async with self._ws as connection:
            self._connected = True
            self.connection = connection
            while self._connected:
                try:
                    resp = await self.connection.recv()
                    if resp:
                        await self._on_message(resp)
                except (websockets.ConnectionClosed, ConnectionResetError):
                    logger.info('connection closed')
                    break
                await asyncio.sleep(0)
        if self._connected:
            self._loop.create_task(self.dispose())

    async def _async_send(self, msg: str, callback_id: int) -> None:
        while not self._connected:
            await asyncio.sleep(self._delay)
        try:
            await self.connection.send(msg)
        except websockets.ConnectionClosed:
            logger.error('connection unexpectedly closed')
            callback = self._callbacks.get(callback_id, None)
            if callback and not callback.done():
                callback.set_result(None)
                await self.dispose()

    def send(self, method: str, params: dict = None) -> Awaitable:
        """Send message via the connection."""
        # Detect connection availability from the second transmission
        if self._lastId and not self._connected:
            raise ConnectionError('Connection is closed')
        if params is None:
            params = dict()
        self._lastId += 1
        _id = self._lastId
        msg = json.dumps(dict(
            id=_id,
            method=method,
            params=params,
        ))
        logger_connection.debug(f'SEND: {msg}')
        self._loop.create_task(self._async_send(msg, _id))
        callback = self._loop.create_future()
        self._callbacks[_id] = callback
        callback.error: Exception = NetworkError()  # type: ignore
        callback.method: str = method  # type: ignore
        return callback

    def _on_response(self, msg: dict) -> None:
        callback = self._callbacks.pop(msg.get('id', -1))
        if msg.get('error'):
            callback.set_exception(
                _createProtocolError(
                    callback.error,  # type: ignore
                    callback.method,  # type: ignore
                    msg
                )
            )
        else:
            callback.set_result(msg.get('result'))

    def _on_query(self, msg: dict) -> None:
        params = msg.get('params', {})
        method = msg.get('method', '')
        sessionId = params.get('sessionId')
        if method == 'Target.receivedMessageFromTarget':
            session = self._sessions.get(sessionId)
            if session:
                session._on_message(params.get('message'))
        elif method == 'Target.detachedFromTarget':
            session = self._sessions.get(sessionId)
            if session:
                session._on_closed()
                del self._sessions[sessionId]
        else:
            self.emit(method, params)

    def setClosedCallback(self, callback: Callable[[], None]) -> None:
        """Set closed callback."""
        self._closeCallback = callback

    async def _on_message(self, message: str) -> None:
        await asyncio.sleep(self._delay)
        logger_connection.debug(f'RECV: {message}')
        msg = json.loads(message)
        if msg.get('id') in self._callbacks:
            self._on_response(msg)
        else:
            self._on_query(msg)

    async def _on_close(self) -> None:
        if self._closeCallback:
            self._closeCallback()
            self._closeCallback = None

        for cb in self._callbacks.values():
            cb.set_exception(_rewriteError(
                cb.error,  # type: ignore
                f'Protocol error {cb.method}: Target closed.',  # type: ignore
            ))
        self._callbacks.clear()

        for session in self._sessions.values():
            session._on_closed()
        self._sessions.clear()

        # close connection
        if hasattr(self, 'connection'):  # may not have connection
            await self.connection.close()
        if not self._recv_fut.done():
            self._recv_fut.cancel()

    async def dispose(self) -> None:
        """Close all connection."""
        self._connected = False
        await self._on_close()

    async def createSession(self, targetInfo: Dict) -> 'CDPSession':
        """Create new session."""
        resp = await self.send(
            'Target.attachToTarget',
            {'targetId': targetInfo['targetId']}
        )
        sessionId = resp.get('sessionId')
        session = CDPSession(self, targetInfo['type'], sessionId, self._loop)
        self._sessions[sessionId] = session
        return session


class CDPSession(EventEmitter):
    """Chrome Devtools Protocol Session.

    The :class:`CDPSession` instances are used to talk raw Chrome Devtools
    Protocol:

    * protocol methods can be called with :meth:`send` method.
    * protocol events can be subscribed to with :meth:`on` method.

    Documentation on DevTools Protocol can be found
    `here <https://chromedevtools.github.io/devtools-protocol/>`__.
    """

    def __init__(self, connection: Union[Connection, 'CDPSession'],
                 targetType: str, sessionId: str,
                 loop: asyncio.AbstractEventLoop) -> None:
        """Make new session."""
        super().__init__()
        self._lastId = 0
        self._callbacks: Dict[int, asyncio.Future] = {}
        self._connection: Optional[Connection] = connection
        self._targetType = targetType
        self._sessionId = sessionId
        self._sessions: Dict[str, CDPSession] = dict()
        self._loop = loop

    def send(self, method: str, params: dict = None) -> Awaitable:
        """Send message to the connected session.

        :arg str method: Protocol method name.
        :arg dict params: Optional method parameters.
        """
        if not self._connection:
            raise NetworkError(
                f'Protocol Error ({method}): Session closed. Most likely the '
                f'{self._targetType} has been closed.'
            )
        self._lastId += 1
        _id = self._lastId
        msg = json.dumps(dict(id=_id, method=method, params=params))
        logger_session.debug(f'SEND: {msg}')

        callback = self._loop.create_future()
        self._callbacks[_id] = callback
        callback.error: Exception = NetworkError()  # type: ignore
        callback.method: str = method  # type: ignore
        try:
            self._connection.send('Target.sendMessageToTarget', {
                'sessionId': self._sessionId,
                'message': msg,
            })
        except Exception as e:
            # The response from target might have been already dispatched
            if _id in self._callbacks:
                _callback = self._callbacks[_id]
                del self._callbacks[_id]
                _callback.set_exception(_rewriteError(
                    _callback.error,  # type: ignore
                    e.args[0],
                ))
        return callback

    def _on_message(self, msg: str) -> None:  # noqa: C901
        logger_session.debug(f'RECV: {msg}')
        obj = json.loads(msg)
        _id = obj.get('id')
        if _id:
            callback = self._callbacks.get(_id)
            if callback:
                del self._callbacks[_id]
                if obj.get('error'):
                    callback.set_exception(_createProtocolError(
                        callback.error,  # type: ignore
                        callback.method,  # type: ignore
                        obj,
                    ))
                else:
                    result = obj.get('result')
                    if callback and not callback.done():
                        callback.set_result(result)
        else:
            params = obj.get('params', {})
            if obj.get('method') == 'Target.receivedMessageFromTarget':
                session = self._sessions.get(params.get('sessionId'))
                if session:
                    session._on_message(params.get('message'))
            elif obj.get('method') == 'Target.detachFromTarget':
                sessionId = params.get('sessionId')
                session = self._sessions.get(sessionId)
                if session:
                    session._on_closed()
                    del self._sessions[sessionId]
            self.emit(obj.get('method'), obj.get('params'))

    async def detach(self) -> None:
        """Detach session from target.

        Once detached, session won't emit any events and can't be used to send
        messages.
        """
        if not self._connection:
            raise NetworkError('Connection already closed.')
        await self._connection.send('Target.detachFromTarget',
                                    {'sessionId': self._sessionId})

    def _on_closed(self) -> None:
        for cb in self._callbacks.values():
            cb.set_exception(_rewriteError(
                cb.error,  # type: ignore
                f'Protocol error {cb.method}: Target closed.',  # type: ignore
            ))
        self._callbacks.clear()
        self._connection = None

    def _createSession(self, targetType: str, sessionId: str) -> 'CDPSession':
        session = CDPSession(self, targetType, sessionId, self._loop)
        self._sessions[sessionId] = session
        return session


def _createProtocolError(error: Exception, method: str, obj: Dict
                         ) -> Exception:
    message = f'Protocol error ({method}): {obj["error"]["message"]}'
    if 'data' in obj['error']:
        message += f' {obj["error"]["data"]}'
    return _rewriteError(error, message)


def _rewriteError(error: Exception, message: str) -> Exception:
    error.args = (message, )
    return error


================================================
FILE: pyppeteer/coverage.py
================================================
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""Coverage module."""

from functools import cmp_to_key
import logging
from typing import Any, Dict, List

from pyppeteer import helper
from pyppeteer.connection import CDPSession
from pyppeteer.errors import PageError
from pyppeteer.execution_context import EVALUATION_SCRIPT_URL
from pyppeteer.helper import debugError
from pyppeteer.util import merge_dict

logger = logging.getLogger(__name__)


class Coverage(object):
    """Coverage class.

    Coverage gathers information about parts of JavaScript and CSS that were
    used by the page.

    An example of using JavaScript and CSS coverage to get percentage of
    initially executed code::

        # Enable both JavaScript and CSS coverage
        await page.coverage.startJSCoverage()
        await page.coverage.startCSSCoverage()

        # Navigate to page
        await page.goto('https://example.com')
        # Disable JS and CSS coverage and get results
        jsCoverage = await page.coverage.stopJSCoverage()
        cssCoverage = await page.coverage.stopCSSCoverage()
        totalBytes = 0
        usedBytes = 0
        coverage = jsCoverage + cssCoverage
        for entry in coverage:
            totalBytes += len(entry['text'])
            for range in entry['ranges']:
                usedBytes += range['end'] - range['start'] - 1

        print('Bytes used: {}%'.format(usedBytes / totalBytes * 100))
    """

    def __init__(self, client: CDPSession) -> None:
        self._jsCoverage = JSCoverage(client)
        self._cssCoverage = CSSCoverage(client)

    async def startJSCoverage(self, options: Dict = None, **kwargs: Any
                              ) -> None:
        """Start JS coverage measurement.

        Available options are:

        * ``resetOnNavigation`` (bool): Whether to reset coverage on every
          navigation. Defaults to ``True``.
        * ``reportAnonymousScript`` (bool): Whether anonymous script generated
          by the page should be reported. Defaults to ``False``.

        .. note::
            Anonymous scripts are ones that don't have an associated url. These
            are scripts that are dynamically created on the page using ``eval``
            of ``new Function``. If ``reportAnonymousScript`` is set to
            ``True``, anonymous scripts will have
            ``__pyppeteer_evaluation_script__`` as their url.
        """
        options = merge_dict(options, kwargs)
        await self._jsCoverage.start(options)

    async def stopJSCoverage(self) -> List:
        """Stop JS coverage measurement and get result.

        Return list of coverage reports for all scripts. Each report includes:

        * ``url`` (str): Script url.
        * ``text`` (str): Script content.
        * ``ranges`` (List[Dict]): Script ranges that were executed. Ranges are
          sorted and non-overlapping.

          * ``start`` (int): A start offset in text, inclusive.
          * ``end`` (int): An end offset in text, exclusive.

        .. note::
           JavaScript coverage doesn't include anonymous scripts by default.
           However, scripts with sourceURLs are reported.
        """
        return await self._jsCoverage.stop()

    async def startCSSCoverage(self, options: Dict = None, **kwargs: Any
                               ) -> None:
        """Start CSS coverage measurement.

        Available options are:

        * ``resetOnNavigation`` (bool): Whether to reset coverage on every
          navigation. Defaults to ``True``.
        """
        options = merge_dict(options, kwargs)
        await self._cssCoverage.start(options)

    async def stopCSSCoverage(self) -> List:
        """Stop CSS coverage measurement and get result.

        Return list of coverage reports for all non-anonymous scripts. Each
        report includes:

        * ``url`` (str): StyleSheet url.
        * ``text`` (str): StyleSheet content.
        * ``ranges`` (List[Dict]): StyleSheet ranges that were executed. Ranges
          are sorted and non-overlapping.

          * ``start`` (int): A start offset in text, inclusive.
          * ``end`` (int): An end offset in text, exclusive.

        .. note::
           CSS coverage doesn't include dynamically injected style tags without
           sourceURLs (but currently includes... to be fixed).
        """
        return await self._cssCoverage.stop()


class JSCoverage(object):
    """JavaScript Coverage class."""

    def __init__(self, client: CDPSession) -> None:
        self._client = client
        self._enabled = False
        self._scriptURLs: Dict = dict()
        self._scriptSources: Dict = dict()
        self._eventListeners: List = list()
        self._resetOnNavigation = False

    async def start(self, options: Dict = None, **kwargs: Any) -> None:
        """Start coverage measurement."""
        options = merge_dict(options, kwargs)
        if self._enabled:
            raise PageError('JSCoverage is always enabled.')
        self._resetOnNavigation = (True if 'resetOnNavigation' not in options
                                   else bool(options['resetOnNavigation']))
        self._reportAnonymousScript = bool(options.get('reportAnonymousScript'))  # noqa: E501
        self._enabled = True
        self._scriptURLs.clear()
        self._scriptSources.clear()
        self._eventListeners = [
            helper.addEventListener(
                self._client, 'Debugger.scriptParsed',
                lambda e: self._client._loop.create_task(
                    self._onScriptParsed(e))),
            helper.addEventListener(
                self._client, 'Runtime.executionContextsCleared',
                self._onExecutionContextsCleared),
        ]
        await self._client.send('Profiler.enable')
        await self._client.send('Profiler.startPreciseCoverage',
                                {'callCount': False, 'detailed': True})
        await self._client.send('Debugger.enable')
        await self._client.send('Debugger.setSkipAllPauses', {'skip': True})

    def _onExecutionContextsCleared(self, event: Dict) -> None:
        if not self._resetOnNavigation:
            return
        self._scriptURLs.clear()
        self._scriptSources.clear()

    async def _onScriptParsed(self, event: Dict) -> None:
        # Ignore pyppeteer-injected scripts
        if event.get('url') == EVALUATION_SCRIPT_URL:
            return
        # Ignore other anonymous scripts unless the reportAnonymousScript
        # option is True
        if not event.get('url') and not self._reportAnonymousScript:
            return

        scriptId = event.get('scriptId')
        url = event.get('url')
        if not url and self._reportAnonymousScript:
            url = f'debugger://VM{scriptId}'
        try:
            response = await self._client.send(
                'Debugger.getScriptSource',
                {'scriptId': scriptId}
            )
            self._scriptURLs[scriptId] = url
            self._scriptSources[scriptId] = response.get('scriptSource')
        except Exception as e:
            # This might happen if the page has already navigated away.
            debugError(logger, e)

    async def stop(self) -> List:
        """Stop coverage measurement and return results."""
        if not self._enabled:
            raise PageError('JSCoverage is not enabled.')
        self._enabled = False

        result = await self._client.send('Profiler.takePreciseCoverage')
        await self._client.send('Profiler.stopPreciseCoverage')
        await self._client.send('Profiler.disable')
        await self._client.send('Debugger.disable')
        helper.removeEventListeners(self._eventListeners)

        coverage: List = []
        for entry in result.get('result', []):
            url = self._scriptURLs.get(entry.get('scriptId'))
            text = self._scriptSources.get(entry.get('scriptId'))
            if text is None or url is None:
                continue
            flattenRanges: List = []
            for func in entry.get('functions', []):
                flattenRanges.extend(func.get('ranges', []))
            ranges = convertToDisjointRanges(flattenRanges)
            coverage.append({'url': url, 'ranges': ranges, 'text': text})
        return coverage


class CSSCoverage(object):
    """CSS Coverage class."""

    def __init__(self, client: CDPSession) -> None:
        self._client = client
        self._enabled = False
        self._stylesheetURLs: Dict = dict()
        self._stylesheetSources: Dict = dict()
        self._eventListeners: List = []
        self._resetOnNavigation = False

    async def start(self, options: Dict = None, **kwargs: Any) -> None:
        """Start coverage measurement."""
        options = merge_dict(options, kwargs)
        if self._enabled:
            raise PageError('CSSCoverage is already enabled.')
        self._resetOnNavigation = (True if 'resetOnNavigation' not in options
                                   else bool(options['resetOnNavigation']))
        self._enabled = True
        self._stylesheetURLs.clear()
        self._stylesheetSources.clear()
        self._eventListeners = [
            helper.addEventListener(
                self._client, 'CSS.styleSheetAdded',
                lambda e: self._client._loop.create_task(
                    self._onStyleSheet(e))),
            helper.addEventListener(
                self._client, 'Runtime.executionContextsCleared',
                self._onExecutionContextsCleared),
        ]
        await self._client.send('DOM.enable')
        await self._client.send('CSS.enable')
        await self._client.send('CSS.startRuleUsageTracking')

    def _onExecutionContextsCleared(self, event: Dict) -> None:
        if not self._resetOnNavigation:
            return
        self._stylesheetURLs.clear()
        self._stylesheetSources.clear()

    async def _onStyleSheet(self, event: Dict) -> None:
        header = event.get('header', {})
        # Ignore anonymous scripts
        if not header.get('sourceURL'):
            return
        try:
            response = await self._client.send(
                'CSS.getStyleSheetText',
                {'styleSheetId': header['styleSheetId']}
            )
            self._stylesheetURLs[header['styleSheetId']] = header['sourceURL']
            self._stylesheetSources[header['styleSheetId']] = response['text']
        except Exception as e:
            # This might happen if the page has already navigated away.
            debugError(logger, e)

    async def stop(self) -> List:
        """Stop coverage measurement and return results."""
        if not self._enabled:
            raise PageError('CSSCoverage is not enabled.')
        self._enabled = False
        result = await self._client.send('CSS.stopRuleUsageTracking')
        await self._client.send('CSS.disable')
        await self._client.send('DOM.disable')
        helper.removeEventListeners(self._eventListeners)

        # aggregate by styleSheetId
        styleSheetIdToCoverage: Dict = {}
        for entry in result['ruleUsage']:
            ranges = styleSheetIdToCoverage.get(entry['styleSheetId'])
            if not ranges:
                ranges = []
                styleSheetIdToCoverage[entry['styleSheetId']] = ranges
            ranges.append({
                'startOffset': entry['startOffset'],
                'endOffset': entry['endOffset'],
                'count': 1 if entry['used'] else 0
            })

        coverage = []
        for styleSheetId in self._stylesheetURLs:
            url = self._stylesheetURLs.get(styleSheetId)
            text = self._stylesheetSources.get(styleSheetId)
            ranges = convertToDisjointRanges(
                styleSheetIdToCoverage.get(styleSheetId, [])
            )
            coverage.append({'url': url, 'ranges': ranges, 'text': text})

        return coverage


def convertToDisjointRanges(nestedRanges: List[Any]  # noqa: C901
                            ) -> List[Any]:
    """Convert ranges."""
    points: List = []
    for nested_range in nestedRanges:
        points.append({'offset': nested_range['startOffset'], 'type': 0,
                       'range': nested_range})
        points.append({'offset': nested_range['endOffset'], 'type': 1,
                       'range': nested_range})

    # Sort points to form a valid parenthesis sequence.
    def _sort_func(a: Dict, b: Dict) -> int:
        # Sort with increasing offsets.
        if a['offset'] != b['offset']:
            return a['offset'] - b['offset']
        # All "end" points should go before "start" points.
        if a['type'] != b['type']:
            return b['type'] - a['type']
        aLength = a['range']['endOffset'] - a['range']['startOffset']
        bLength = b['range']['endOffset'] - b['range']['startOffset']
        # For two "start" points, the one with longer range goes first.
        if a['type'] == 0:
            return bLength - aLength
        # For two "end" points, the one with shorter range goes first.
        return aLength - bLength

    points.sort(key=cmp_to_key(_sort_func))

    hitCountStack: List[int] = []
    results: List[Dict] = []
    lastOffset = 0
    # Run scanning line to intersect all ranges.
    for point in points:
        if (hitCountStack and
                lastOffset < point['offset'] and
                hitCountStack[len(hitCountStack) - 1] > 0):
            lastResult = results[-1] if results else None
            if lastResult and lastResult['end'] == lastOffset:
                lastResult['end'] = point['offset']
            else:
                results.append({'start': lastOffset, 'end': point['offset']})
        lastOffset = point['offset']
        if point['type'] == 0:
            hitCountStack.append(point['range']['count'])
        else:
            hitCountStack.pop()
    # Filter out empty ranges.
    return [range for range in results if range['end'] - range['start'] > 1]


================================================
FILE: pyppeteer/dialog.py
================================================
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""Dialog module."""

from types import SimpleNamespace

from pyppeteer.connection import CDPSession


class Dialog(object):
    """Dialog class.

    Dialog objects are dispatched by page via the ``dialog`` event.

    An example of using ``Dialog`` class:

    .. code::

        browser = await launch()
        page = await browser.newPage()

        async def close_dialog(dialog):
            print(dialog.message)
            await dialog.dismiss()
            await browser.close()

        page.on(
            'dialog',
            lambda dialog: asyncio.ensure_future(close_dialog(dialog))
        )
        await page.evaluate('() => alert("1")')
    """

    Type = SimpleNamespace(
        Alert='alert',
        BeforeUnload='beforeunload',
        Confirm='confirm',
        Prompt='prompt',
    )

    def __init__(self, client: CDPSession, type: str, message: str,
                 defaultValue: str = '') -> None:
        self._client = client
        self._type = type
        self._message = message
        self._handled = False
        self._defaultValue = defaultValue

    @property
    def type(self) -> str:
        """Get dialog type.

        One of ``alert``, ``beforeunload``, ``confirm``, or ``prompt``.
        """
        return self._type

    @property
    def message(self) -> str:
        """Get dialog message."""
        return self._message

    @property
    def defaultValue(self) -> str:
        """If dialog is prompt, get default prompt value.

        If dialog is not prompt, return empty string (``''``).
        """
        return self._defaultValue

    async def accept(self, promptText: str = '') -> None:
        """Accept the dialog.

        * ``promptText`` (str): A text to enter in prompt. If the dialog's type
          is not prompt, this does not cause any effect.
        """
        self._handled = True
        await self._client.send('Page.handleJavaScriptDialog', {
            'accept': True,
            'promptText': promptText,
        })

    async def dismiss(self) -> None:
        """Dismiss the dialog."""
        self._handled = True
        await self._client.send('Page.handleJavaScriptDialog', {
            'accept': False,
        })


================================================
FILE: pyppeteer/element_handle.py
================================================
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""Element handle module."""

import copy
import logging
import math
import os.path
from typing import Any, Dict, List, Optional, TYPE_CHECKING

from pyppeteer.connection import CDPSession
from pyppeteer.execution_context import ExecutionContext, JSHandle
from pyppeteer.errors import ElementHandleError, NetworkError
from pyppeteer.helper import debugError
from pyppeteer.util import merge_dict

if TYPE_CHECKING:
    from pyppeteer.frame_manager import Frame, FrameManager  # noqa: F401


logger = logging.getLogger(__name__)


class ElementHandle(JSHandle):
    """ElementHandle class.

    This class represents an in-page DOM element. ElementHandle can be created
    by the :meth:`pyppeteer.page.Page.querySelector` method.

    ElementHandle prevents DOM element from garbage collection unless the
    handle is disposed. ElementHandles are automatically disposed when their
    origin frame gets navigated.

    ElementHandle isinstance can be used as arguments in
    :meth:`pyppeteer.page.Page.querySelectorEval` and
    :meth:`pyppeteer.page.Page.evaluate` methods.
    """

    def __init__(self, context: ExecutionContext, client: CDPSession,
                 remoteObject: dict, page: Any,
                 frameManager: 'FrameManager') -> None:
        super().__init__(context, client, remoteObject)
        self._client = client
        self._remoteObject = remoteObject
        self._page = page
        self._frameManager = frameManager
        self._disposed = False

    def asElement(self) -> 'ElementHandle':
        """Return this ElementHandle."""
        return self

    async def contentFrame(self) -> Optional['Frame']:
        """Return the content frame for the element handle.

        Return ``None`` if this handle is not referencing iframe.
        """
        nodeInfo = await self._client.send('DOM.describeNode', {
            'objectId': self._remoteObject.get('objectId'),
        })
        node_obj = nodeInfo.get('node', {})
        if not isinstance(node_obj.get('frameId'), str):
            return None
        return self._frameManager.frame(node_obj['frameId'])

    async def _scrollIntoViewIfNeeded(self) -> None:
        error = await self.executionContext.evaluate('''
            async (element, pageJavascriptEnabled) => {
                if (!element.isConnected)
                    return 'Node is detached from document';
                if (element.nodeType !== Node.ELEMENT_NODE)
                    return 'Node is not of type HTMLElement';
                // force-scroll if page's javascript is disabled.
                if (!pageJavascriptEnabled) {
                    element.scrollIntoView({
                        block: 'center',
                        inline: 'center',
                        behavior: 'instant',
                    });
                    return false;
                }
                const visibleRatio = await new Promise(resolve => {
                    const observer = new IntersectionObserver(entries => {
                        resolve(entries[0].intersectionRatio);
                        observer.disconnect();
                    });
                    observer.observe(element);
                });
                if (visibleRatio !== 1.0)
                    element.scrollIntoView({
                        block: 'center',
                        inline: 'center',
                        behavior: 'instant',
                    });
                return false;
            }''', self, self._page._javascriptEnabled)
        if error:
            raise ElementHandleError(error)

    async def _clickablePoint(self) -> Dict[str, float]:  # noqa: C901
        result = None
        try:
            result = await self._client.send('DOM.getContentQuads', {
                'objectId': self._remoteObject.get('objectId'),
            })
        except Exception as e:
            debugError(logger, e)

        if not result or not result.get('quads'):
            raise ElementHandleError(
                'Node is either not visible or not an HTMLElement')

        quads = []
        for _quad in result.get('quads'):
            _q = self._fromProtocolQuad(_quad)
            if _computeQuadArea(_q) > 1:
                quads.append(_q)
        if not quads:
            raise ElementHandleError(
                'Node is either not visible or not an HTMLElement')

        quad = quads[0]
        x = 0
        y = 0
        for point in quad:
            x += point['x']
            y += point['y']
        return {'x': x / 4, 'y': y / 4}

    async def _getBoxModel(self) -> Optional[Dict]:
        try:
            result: Optional[Dict] = await self._client.send(
                'DOM.getBoxModel',
                {'objectId': self._remoteObject.get('objectId')},
            )
        except NetworkError as e:
            debugError(logger, e)
            result = None
        return result

    def _fromProtocolQuad(self, quad: List[int]) -> List[Dict[str, int]]:
        return [
            {'x': quad[0], 'y': quad[1]},
            {'x': quad[2], 'y': quad[3]},
            {'x': quad[4], 'y': quad[5]},
            {'x': quad[6], 'y': quad[7]},
        ]

    async def hover(self) -> None:
        """Move mouse over to center of this element.

        If needed, this method scrolls element into view. If this element is
        detached from DOM tree, the method raises an ``ElementHandleError``.
        """
        await self._scrollIntoViewIfNeeded()
        obj = await self._clickablePoint()
        x = obj.get('x', 0)
        y = obj.get('y', 0)
        await self._page.mouse.move(x, y)

    async def click(self, options: dict = None, **kwargs: Any) -> None:
        """Click the center of this element.

        If needed, this method scrolls element into view. If the element is
        detached from DOM, the method raises ``ElementHandleError``.

        ``options`` can contain the following fields:

        * ``button`` (str): ``left``, ``right``, of ``middle``, defaults to
          ``left``.
        * ``clickCount`` (int): Defaults to 1.
        * ``delay`` (int|float): Time to wait between ``mousedown`` and
          ``mouseup`` in milliseconds. Defaults to 0.
        """
        options = merge_dict(options, kwargs)
        await self._scrollIntoViewIfNeeded()
        obj = await self._clickablePoint()
        x = obj.get('x', 0)
        y = obj.get('y', 0)
        await self._page.mouse.click(x, y, options)

    async def uploadFile(self, *filePaths: str) -> dict:
        """Upload files."""
        files = [os.path.abspath(p) for p in filePaths]
        objectId = self._remoteObject.get('objectId')
        return await self._client.send(
            'DOM.setFileInputFiles',
            {'objectId': objectId, 'files': files}
        )

    async def tap(self) -> None:
        """Tap the center of this element.

        If needed, this method scrolls element into view. If the element is
        detached from DOM, the method raises ``ElementHandleError``.
        """
        await self._scrollIntoViewIfNeeded()
        center = await self._clickablePoint()
        x = center.get('x', 0)
        y = center.get('y', 0)
        await self._page.touchscreen.tap(x, y)

    async def focus(self) -> None:
        """Focus on this element."""
        await self.executionContext.evaluate(
            'element => element.focus()', self)

    async def type(self, text: str, options: Dict = None, **kwargs: Any
                   ) -> None:
        """Focus the element and then type text.

        Details see :meth:`pyppeteer.input.Keyboard.type` method.
        """
        options = merge_dict(options, kwargs)
        await self.focus()
        await self._page.keyboard.type(text, options)

    async def press(self, key: str, options: Dict = None, **kwargs: Any
                    ) -> None:
        """Press ``key`` onto the element.

        This method focuses the element, and then uses
        :meth:`pyppeteer.input.keyboard.down` and
        :meth:`pyppeteer.input.keyboard.up`.

        :arg str key: Name of key to press, such as ``ArrowLeft``.

        This method accepts the following options:

        * ``text`` (str): If specified, generates an input event with this
          text.
        * ``delay`` (int|float): Time to wait between ``keydown`` and
          ``keyup``. Defaults to 0.
        """
        options = merge_dict(options, kwargs)
        await self.focus()
        await self._page.keyboard.press(key, options)

    async def boundingBox(self) -> Optional[Dict[str, float]]:
        """Return bounding box of this element.

        If the element is not visible, return ``None``.

        This method returns dictionary of bounding box, which contains:

        * ``x`` (int): The X coordinate of the element in pixels.
        * ``y`` (int): The Y coordinate of the element in pixels.
        * ``width`` (int): The width of the element in pixels.
        * ``height`` (int): The height of the element in pixels.
        """
        result = await self._getBoxModel()

        if not result:
            return None

        quad = result['model']['border']
        x = min(quad[0], quad[2], quad[4], quad[6])
        y = min(quad[1], quad[3], quad[5], quad[7])
        width = max(quad[0], quad[2], quad[4], quad[6]) - x
        height = max(quad[1], quad[3], quad[5], quad[7]) - y
        return {'x': x, 'y': y, 'width': width, 'height': height}

    async def boxModel(self) -> Optional[Dict]:
        """Return boxes of element.

        Return ``None`` if element is not visible. Boxes are represented as an
        list of points; each Point is a dictionary ``{x, y}``. Box points are
        sorted clock-wise.

        Returned value is a dictionary with the following fields:

        * ``content`` (List[Dict]): Content box.
        * ``padding`` (List[Dict]): Padding box.
        * ``border`` (List[Dict]): Border box.
        * ``margin`` (List[Dict]): Margin box.
        * ``width`` (int): Element's width.
        * ``height`` (int): Element's height.
        """
        result = await self._getBoxModel()

        if not result:
            return None

        model = result.get('model', {})
        return {
            'content': self._fromProtocolQuad(model.get('content')),
            'padding': self._fromProtocolQuad(model.get('padding')),
            'border': self._fromProtocolQuad(model.get('border')),
            'margin': self._fromProtocolQuad(model.get('margin')),
            'width': model.get('width'),
            'height': model.get('height'),
        }

    async def screenshot(self, options: Dict = None, **kwargs: Any) -> bytes:
        """Take a screenshot of this element.

        If the element is detached from DOM, this method raises an
        ``ElementHandleError``.

        Available options are same as :meth:`pyppeteer.page.Page.screenshot`.
        """
        options = merge_dict(options, kwargs)

        needsViewportReset = False
        boundingBox = await self.boundingBox()
        if not boundingBox:
            raise ElementHandleError(
                'Node is either not visible or not an HTMLElement')

        original_viewport = copy.deepcopy(self._page.viewport)

        if (boundingBox['width'] > original_viewport['width'] or
                boundingBox['height'] > original_viewport['height']):
            newViewport = {
                'width': max(
                    original_viewport['width'],
                    math.ceil(boundingBox['width'])
                ),
                'height': max(
                    original_viewport['height'],
                    math.ceil(boundingBox['height'])
                ),
            }
            new_viewport = copy.deepcopy(original_viewport)
            new_viewport.update(newViewport)
            await self._page.setViewport(new_viewport)
            needsViewportReset = True

        await self._scrollIntoViewIfNeeded()
        boundingBox = await self.boundingBox()
        if not boundingBox:
            raise ElementHandleError(
                'Node is either not visible or not an HTMLElement')

        _obj = await self._client.send('Page.getLayoutMetrics')
        pageX = _obj['layoutViewport']['pageX']
        pageY = _obj['layoutViewport']['pageY']

        clip = {}
        clip.update(boundingBox)
        clip['x'] = clip['x'] + pageX
        clip['y'] = clip['y'] + pageY
        opt = {'clip': clip}
        opt.update(options)
        imageData = await self._page.screenshot(opt)

        if needsViewportReset:
            await self._page.setViewport(original_viewport)

        return imageData

    async def querySelector(self, selector: str) -> Optional['ElementHandle']:
        """Return first element which matches ``selector`` under this element.

        If no element matches the ``selector``, returns ``None``.
        """
        handle = await self.executionContext.evaluateHandle(
            '(element, selector) => element.querySelector(selector)',
            self, selector,
        )
        element = handle.asElement()
        if element:
            return element
        await handle.dispose()
        return None

    async def querySelectorAll(self, selector: str) -> List['ElementHandle']:
        """Return all elements which match ``selector`` under this element.

        If no element matches the ``selector``, returns empty list (``[]``).
        """
        arrayHandle = await self.executionContext.evaluateHandle(
            '(element, selector) => element.querySelectorAll(selector)',
            self, selector,
        )
        properties = await arrayHandle.getProperties()
        await arrayHandle.dispose()
        result = []
        for prop in properties.values():
            elementHandle = prop.asElement()
            if elementHandle:
                result.append(elementHandle)
        return result  # type: ignore

    async def querySelectorEval(self, selector: str, pageFunction: str,
                                *args: Any) -> Any:
        """Run ``Page.querySelectorEval`` within the element.

        This method runs ``document.querySelector`` within the element and
        passes it as the first argument to ``pageFunction``. If there is no
        element matching ``selector``, the method raises
        ``ElementHandleError``.

        If ``pageFunction`` returns a promise, then wait for the promise to
        resolve and return its value.

        ``ElementHandle.Jeval`` is a shortcut of this method.

        Example:

        .. code:: python

            tweetHandle = await page.querySelector('.tweet')
            assert (await tweetHandle.querySelectorEval('.like', 'node => node.innerText')) == 100
            assert (await tweetHandle.Jeval('.retweets', 'node => node.innerText')) == 10
        """  # noqa: E501
        elementHandle = await self.querySelector(selector)
        if not elementHandle:
            raise ElementHandleError(
                f'Error: failed to find element matching selector "{selector}"'
            )
        result = await self.executionContext.evaluate(
            pageFunction, elementHandle, *args)
        await elementHandle.dispose()
        return result

    async def querySelectorAllEval(self, selector: str, pageFunction: str,
                                   *args: Any) -> Any:
        """Run ``Page.querySelectorAllEval`` within the element.

        This method runs ``Array.from(document.querySelectorAll)`` within the
        element and passes it as the first argument to ``pageFunction``. If
        there is no element matching ``selector``, the method raises
        ``ElementHandleError``.

        If ``pageFunction`` returns a promise, then wait for the promise to
        resolve and return its value.

        Example:

        .. code:: html

            <div class="feed">
                <div class="tweet">Hello!</div>
                <div class="tweet">Hi!</div>
            </div>

        .. code:: python

            feedHandle = await page.J('.feed')
            assert (await feedHandle.JJeval('.tweet', '(nodes => nodes.map(n => n.innerText))')) == ['Hello!', 'Hi!']
        """  # noqa: E501
        arrayHandle = await self.executionContext.evaluateHandle(
            '(element, selector) => Array.from(element.querySelectorAll(selector))',  # noqa: E501
            self, selector
        )
        result = await self.executionContext.evaluate(
            pageFunction, arrayHandle, *args)
        await arrayHandle.dispose()
        return result

    #: alias to :meth:`querySelector`
    J = querySelector
    #: alias to :meth:`querySelectorAll`
    JJ = querySelectorAll
    #: alias to :meth:`querySelectorEval`
    Jeval = querySelectorEval
    #: alias to :meth:`querySelectorAllEval`
    JJeval = querySelectorAllEval

    async def xpath(self, expression: str) -> List['ElementHandle']:
        """Evaluate the XPath expression relative to this elementHandle.

        If there are no such elements, return an empty list.

        :arg str expression: XPath string to be evaluated.
        """
        arrayHandle = await self.executionContext.evaluateHandle(
            '''(element, expression) => {
                const document = element.ownerDocument || element;
                const iterator = document.evaluate(expression, element, null,
                    XPathResult.ORDERED_NODE_ITERATOR_TYPE);
                const array = [];
                let item;
                while ((item = iterator.iterateNext()))
                    array.push(item);
                return array;

            }''', self, expression)
        properties = await arrayHandle.getProperties()
        await arrayHandle.dispose()
        result = []
        for property in properties.values():
            elementHandle = property.asElement()
            if elementHandle:
                result.append(elementHandle)
        return result

    #: alias to :meth:`xpath`
    Jx = xpath

    async def isIntersectingViewport(self) -> bool:
        """Return ``True`` if the element is visible in the viewport."""
        return await self.executionContext.evaluate('''async element => {
            const visibleRatio = await new Promise(resolve => {
                const observer = new IntersectionObserver(entries => {
                    resolve(entries[0].intersectionRatio);
                    observer.disconnect();
                });
                observer.observe(element);
            });
            return visibleRatio > 0;
        }''', self)


def _computeQuadArea(quad: List[Dict]) -> float:
    area = 0
    for i, _ in enumerate(quad):
        p1 = quad[i]
        p2 = quad[(i + 1) % len(quad)]
        area += (p1['x'] * p2['y'] - p2['x'] * p1['y']) / 2
    return area


================================================
FILE: pyppeteer/emulation_manager.py
================================================
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""Emulation Manager module."""

from pyppeteer import helper
from pyppeteer.connection import CDPSession


class EmulationManager(object):
    """EmulationManager class."""

    def __init__(self, client: CDPSession) -> None:
        """Make new emulation manager."""
        self._client = client
        self._emulatingMobile = False
        self._hasTouch = False

    async def emulateViewport(self, viewport: dict) -> bool:
        """Evaluate viewport."""
        options = dict()
        mobile = viewport.get('isMobile', False)
        options['mobile'] = mobile
        if 'width' in viewport:
            options['width'] = helper.get_positive_int(viewport, 'width')
        if 'height' in viewport:
            options['height'] = helper.get_positive_int(viewport, 'height')

        options['deviceScaleFactor'] = viewport.get('deviceScaleFactor', 1)
        if viewport.get('isLandscape'):
            options['screenOrientation'] = {'angle': 90,
                                            'type': 'landscapePrimary'}
        else:
            options['screenOrientation'] = {'angle': 0,
                                            'type': 'portraitPrimary'}
        hasTouch = viewport.get('hasTouch', False)

        await self._client.send('Emulation.setDeviceMetricsOverride', options)
        await self._client.send('Emulation.setTouchEmulationEnabled', {
            'enabled': hasTouch,
            'configuration': 'mobile' if mobile else 'desktop'
        })

        reloadNeeded = (self._emulatingMobile != mobile or
                        self._hasTouch != hasTouch)

        self._emulatingMobile = mobile
        self._hasTouch = hasTouch
        return reloadNeeded


================================================
FILE: pyppeteer/errors.py
================================================
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""Exceptions for pyppeteer package."""

import asyncio


class PyppeteerError(Exception):  # noqa: D204
    """Base exception for pyppeteer."""
    pass


class BrowserError(PyppeteerError):  # noqa: D204
    """Exception raised from browser."""
    pass


class ElementHandleError(PyppeteerError):  # noqa: D204
    """ElementHandle related exception."""
    pass


class NetworkError(PyppeteerError):  # noqa: D204
    """Network/Protocol related exception."""
    pass


class PageError(PyppeteerError):  # noqa: D204
    """Page/Frame related exception."""
    pass


class TimeoutError(asyncio.TimeoutError):  # noqa: D204
    """Timeout Error class."""
    pass


================================================
FILE: pyppeteer/execution_context.py
================================================
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""Execution Context Module."""

import logging
import math
import re
from typing import Any, Dict, Optional, TYPE_CHECKING

from pyppeteer import helper
from pyppeteer.connection import CDPSession
from pyppeteer.errors import ElementHandleError, NetworkError
from pyppeteer.helper import debugError

if TYPE_CHECKING:
    from pyppeteer.element_handle import ElementHandle  # noqa: F401
    from pyppeteer.frame_manager import Frame  # noqa: F401

logger = logging.getLogger(__name__)

EVALUATION_SCRIPT_URL = '__pyppeteer_evaluation_script__'
SOURCE_URL_REGEX = re.compile(
    r'^[\040\t]*//[@#] sourceURL=\s*(\S*?)\s*$',
    re.MULTILINE,
)


class ExecutionContext(object):
    """Execution Context class."""

    def __init__(self, client: CDPSession, contextPayload: Dict,
                 objectHandleFactory: Any, frame: 'Frame' = None) -> None:
        self._client = client
        self._frame = frame
        self._contextId = contextPayload.get('id')

        auxData = contextPayload.get('auxData', {'isDefault': False})
        self._isDefault = bool(auxData.get('isDefault'))
        self._objectHandleFactory = objectHandleFactory

    @property
    def frame(self) -> Optional['Frame']:
        """Return frame associated with this execution context."""
        return self._frame

    async def evaluate(self, pageFunction: str, *args: Any,
                       force_expr: bool = False) -> Any:
        """Execute ``pageFunction`` on this context.

        Details see :meth:`pyppeteer.page.Page.evaluate`.
        """
        handle = await self.evaluateHandle(
            pageFunction, *args, force_expr=force_expr)
        try:
            result = await handle.jsonValue()
        except NetworkError as e:
            if 'Object reference chain is too long' in e.args[0]:
                return
            if 'Object couldn\'t be returned by value' in e.args[0]:
                return
            raise
        await handle.dispose()
        return result

    async def evaluateHandle(self, pageFunction: str, *args: Any,  # noqa: C901
                             force_expr: bool = False) -> 'JSHandle':
        """Execute ``pageFunction`` on this context.

        Details see :meth:`pyppeteer.page.Page.evaluateHandle`.
        """
        suffix = f'//# sourceURL={EVALUATION_SCRIPT_URL}'

        if force_expr or (not args and not helper.is_jsfunc(pageFunction)):
            try:
                if SOURCE_URL_REGEX.match(pageFunction):
                    expressionWithSourceUrl = pageFunction
                else:
                    expressionWithSourceUrl = f'{pageFunction}\n{suffix}'
                _obj = await self._client.send('Runtime.evaluate', {
                    'expression': expressionWithSourceUrl,
                    'contextId': self._contextId,
                    'returnByValue': False,
                    'awaitPromise': True,
                    'userGesture': True,
                })
            except Exception as e:
                _rewriteError(e)

            exceptionDetails = _obj.get('exceptionDetails')
            if exceptionDetails:
                raise ElementHandleError(
                    'Evaluation failed: {}'.format(
                        helper.getExceptionMessage(exceptionDetails)))
            remoteObject = _obj.get('result')
            return self._objectHandleFactory(remoteObject)

        try:
            _obj = await self._client.send('Runtime.callFunctionOn', {
                'functionDeclaration': f'{pageFunction}\n{suffix}\n',
                'executionContextId': self._contextId,
                'arguments': [self._convertArgument(arg) for arg in args],
                'returnByValue': False,
                'awaitPromise': True,
                'userGesture': True,
            })
        except Exception as e:
            _rewriteError(e)

        exceptionDetails = _obj.get('exceptionDetails')
        if exceptionDetails:
            raise ElementHandleError('Evaluation failed: {}'.format(
                helper.getExceptionMessage(exceptionDetails)))
        remoteObject = _obj.get('result')
        return self._objectHandleFactory(remoteObject)

    def _convertArgument(self, arg: Any) -> Dict:  # noqa: C901
        if arg == math.inf:
            return {'unserializableValue': 'Infinity'}
        if arg == -math.inf:
            return {'unserializableValue': '-Infinity'}
        objectHandle = arg if isinstance(arg, JSHandle) else None
        if objectHandle:
            if objectHandle._context != self:
                raise ElementHandleError('JSHandles can be evaluated only in the context they were created!')  # noqa: E501
            if objectHandle._disposed:
                raise ElementHandleError('JSHandle is disposed!')
            if objectHandle._remoteObject.get('unserializableValue'):
                return {'unserializableValue': objectHandle._remoteObject.get('unserializableValue')}  # noqa: E501
            if not objectHandle._remoteObject.get('objectId'):
                return {'value': objectHandle._remoteObject.get('value')}
            return {'objectId': objectHandle._remoteObject.get('objectId')}
        return {'value': arg}

    async def queryObjects(self, prototypeHandle: 'JSHandle') -> 'JSHandle':
        """Send query.

        Details see :meth:`pyppeteer.page.Page.queryObjects`.
        """
        if prototypeHandle._disposed:
            raise ElementHandleError('Prototype JSHandle is disposed!')
        if not prototypeHandle._remoteObject.get('objectId'):
            raise ElementHandleError(
                'Prototype JSHandle must not be referencing primitive value')
        response = await self._client.send('Runtime.queryObjects', {
            'prototypeObjectId': prototypeHandle._remoteObject['objectId'],
        })
        return self._objectHandleFactory(response.get('objects'))


class JSHandle(object):
    """JSHandle class.

    JSHandle represents an in-page JavaScript object. JSHandle can be created
    with the :meth:`~pyppeteer.page.Page.evaluateHandle` method.
    """

    def __init__(self, context: ExecutionContext, client: CDPSession,
                 remoteObject: Dict) -> None:
        self._context = context
        self._client = client
        self._remoteObject = remoteObject
        self._disposed = False

    @property
    def executionContext(self) -> ExecutionContext:
        """Get execution context of this handle."""
        return self._context

    async def getProperty(self, propertyName: str) -> 'JSHandle':
        """Get property value of ``propertyName``."""
        objectHandle = await self._context.evaluateHandle(
            '''(object, propertyName) => {
                const result = {__proto__: null};
                result[propertyName] = object[propertyName];
                return result;
            }''', self, propertyName)
        properties = await objectHandle.getProperties()
        result = properties[propertyName]
        await objectHandle.dispose()
        return result

    async def getProperties(self) -> Dict[str, 'JSHandle']:
        """Get all properties of this handle."""
        response = await self._client.send('Runtime.getProperties', {
            'objectId': self._remoteObject.get('objectId', ''),
            'ownProperties': True,
        })
        result = dict()
        for prop in response['result']:
            if not prop.get('enumerable'):
                continue
            result[prop.get('name')] = self._context._objectHandleFactory(
                prop.get('value'))
        return result

    async def jsonValue(self) -> Dict:
        """Get Jsonized value of this object."""
        objectId = self._remoteObject.get('objectId')
        if objectId:
            response = await self._client.send('Runtime.callFunctionOn', {
                'functionDeclaration': 'function() { return this; }',
                'objectId': objectId,
                'returnByValue': True,
                'awaitPromise': True,
            })
            return helper.valueFromRemoteObject(response['result'])
        return helper.valueFromRemoteObject(self._remoteObject)

    def asElement(self) -> Optional['ElementHandle']:
        """Return either null or the object handle itself."""
        return None

    async def dispose(self) -> None:
        """Stop referencing the handle."""
        if self._disposed:
            return
        self._disposed = True
        try:
            await helper.releaseObject(self._client, self._remoteObject)
        except Exception as e:
            debugError(logger, e)

    def toString(self) -> str:
        """Get string representation."""
        if self._remoteObject.get('objectId'):
            _type = (self._remoteObject.get('subtype') or
                     self._remoteObject.get('type'))
            return f'JSHandle@{_type}'
        return 'JSHandle:{}'.format(
            helper.valueFromRemoteObject(self._remoteObject))


def _rewriteError(error: Exception) -> None:
    if error.args[0].endswith('Cannot find context with specified id'):
        msg = 'Execution context was destroyed, most likely because of a navigation.'  # noqa: E501
        raise type(error)(msg)
    raise error


================================================
FILE: pyppeteer/frame_manager.py
================================================
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""Frame Manager module."""

import asyncio
from collections import OrderedDict
import logging
from types import SimpleNamespace
from typing import Any, Awaitable, Dict, Generator, List, Optional, Set, Union

from pyee import EventEmitter

from pyppeteer import helper
from pyppeteer.connection import CDPSession
from pyppeteer.element_handle import ElementHandle
from pyppeteer.errors import NetworkError
from pyppeteer.execution_context import ExecutionContext, JSHandle
from pyppeteer.errors import ElementHandleError, PageError, TimeoutError
from pyppeteer.util import merge_dict

logger = logging.getLogger(__name__)


class FrameManager(EventEmitter):
    """FrameManager class."""

    Events = SimpleNamespace(
        FrameAttached='frameattached',
        FrameNavigated='framenavigated',
        FrameDetached='framedetached',
        LifecycleEvent='lifecycleevent',
        FrameNavigatedWithinDocument='framenavigatedwithindocument',
    )

    def __init__(self, client: CDPSession, frameTree: Dict, page: Any) -> None:
        """Make new frame manager."""
        super().__init__()
        self._client = client
        self._page = page
        self._frames: OrderedDict[str, Frame] = OrderedDict()
        self._mainFrame: Optional[Frame] = None
        self._contextIdToContext: Dict[str, ExecutionContext] = dict()

        client.on('Page.frameAttached',
                  lambda event: self._onFrameAttached(
                      event.get('frameId', ''), event.get('parentFrameId', ''))
                  )
        client.on('Page.frameNavigated',
                  lambda event: self._onFrameNavigated(event.get('frame')))
        client.on('Page.navigatedWithinDocument',
                  lambda event: self._onFrameNavigatedWithinDocument(
                      event.get('frameId'), event.get('url')
                  ))
        client.on('Page.frameDetached',
                  lambda event: self._onFrameDetached(event.get('frameId')))
        client.on('Page.frameStoppedLoading',
                  lambda event: self._onFrameStoppedLoading(
                      event.get('frameId')
                  ))
        client.on('Runtime.executionContextCreated',
                  lambda event: self._onExecutionContextCreated(
                      event.get('context')))
        client.on('Runtime.executionContextDestroyed',
                  lambda event: self._onExecutionContextDestroyed(
                      event.get('executionContextId')))
        client.on('Runtime.executionContextsCleared',
                  lambda event: self._onExecutionContextsCleared())
        client.on('Page.lifecycleEvent',
                  lambda event: self._onLifecycleEvent(event))

        self._handleFrameTree(frameTree)

    def _onLifecycleEvent(self, event: Dict) -> None:
        frame = self._frames.get(event['frameId'])
        if not frame:
            return
        frame._onLifecycleEvent(event['loaderId'], event['name'])
        self.emit(FrameManager.Events.LifecycleEvent, frame)

    def _onFrameStoppedLoading(self, frameId: str) -> None:
        frame = self._frames.get(frameId)
        if not frame:
            return
        frame._onLoadingStopped()
        self.emit(FrameManager.Events.LifecycleEvent, frame)

    def _handleFrameTree(self, frameTree: Dict) -> None:
        frame = frameTree['frame']
        if 'parentId' in frame:
            self._onFrameAttached(
                frame['id'],
                frame['parentId'],
            )
        self._onFrameNavigated(frame)
        if 'childFrames' not in frameTree:
            return
        for child in frameTree['childFrames']:
            self._handleFrameTree(child)

    @property
    def mainFrame(self) -> Optional['Frame']:
        """Return main frame."""
        return self._mainFrame

    def frames(self) -> List['Frame']:
        """Return all frames."""
        return list(self._frames.values())

    def frame(self, frameId: str) -> Optional['Frame']:
        """Return :class:`Frame` of ``frameId``."""
        return self._frames.get(frameId)

    def _onFrameAttached(self, frameId: str, parentFrameId: str) -> None:
        if frameId in self._frames:
            return
        parentFrame = self._frames.get(parentFrameId)
        frame = Frame(self._client, parentFrame, frameId)
        self._frames[frameId] = frame
        self.emit(FrameManager.Events.FrameAttached, frame)

    def _onFrameNavigated(self, framePayload: dict) -> None:
        isMainFrame = not framePayload.get('parentId')
        if isMainFrame:
            frame = self._mainFrame
        else:
            frame = self._frames.get(framePayload.get('id', ''))
        if not (isMainFrame or frame):
            raise PageError('We either navigate top level or have old version '
                            'of the navigated frame')

        # Detach all child frames first.
        if frame:
            for child in frame.childFrames:
                self._removeFramesRecursively(child)

        # Update or create main frame.
        _id = framePayload.get('id', '')
        if isMainFrame:
            if frame:
                # Update frame id to retain frame identity on cross-process navigation.  # noqa: E501
                self._frames.pop(frame._id, None)
                frame._id = _id
            else:
                # Initial main frame navigation.
                frame = Frame(self._client, None, _id)
            self._frames[_id] = frame
            self._mainFrame = frame

        # Update frame payload.
        frame._navigated(framePayload)  # type: ignore
        self.emit(FrameManager.Events.FrameNavigated, frame)

    def _onFrameNavigatedWithinDocument(self, frameId: str, url: str) -> None:
        frame = self._frames.get(frameId)
        if not frame:
            return
        frame._navigatedWithinDocument(url)
        self.emit(FrameManager.Events.FrameNavigatedWithinDocument, frame)
        self.emit(FrameManager.Events.FrameNavigated, frame)

    def _onFrameDetached(self, frameId: str) -> None:
        frame = self._frames.get(frameId)
        if frame:
            self._removeFramesRecursively(frame)

    def _onExecutionContextCreated(self, contextPayload: Dict) -> None:
        if (contextPayload.get('auxData') and
                contextPayload['auxData'].get('frameId')):
            frameId = contextPayload['auxData']['frameId']
        else:
            frameId = None

        frame = self._frames.get(frameId)

        def _createJSHandle(obj: Dict) -> JSHandle:
            context = self.executionContextById(contextPayload['id'])
            return self.createJSHandle(context, obj)

        context = ExecutionContext(
            self._client,
            contextPayload,
            _createJSHandle,
            frame,
        )
        self._contextIdToContext[contextPayload['id']] = context

        if frame:
            frame._addExecutionContext(context)

    def _onExecutionContextDestroyed(self, executionContextId: str) -> None:
        context = self._contextIdToContext.get(executionContextId)
        if not context:
            return
        del self._contextIdToContext[executionContextId]

        frame = context.frame
        if frame:
            frame._removeExecutionContext(context)

    def _onExecutionContextsCleared(self) -> None:
        for context in self._contextIdToContext.values():
            frame = context.frame
            if frame:
                frame._removeExecutionContext(context)
        self._contextIdToContext.clear()

    def executionContextById(self, contextId: str) -> ExecutionContext:
        """Get stored ``ExecutionContext`` by ``id``."""
        context = self._contextIdToContext.get(contextId)
        if not context:
            raise ElementHandleError(
                f'INTERNAL ERROR: missing context with id = {contextId}'
            )
        return context

    def createJSHandle(self, context: ExecutionContext,
                       remoteObject: Dict = None) -> JSHandle:
        """Create JS handle associated to the context id and remote object."""
        if remoteObject is None:
            remoteObject = dict()
        if remoteObject.get('subtype') == 'node':
            return ElementHandle(context, self._client, remoteObject,
                                 self._page, self)
        return JSHandle(context, self._client, remoteObject)

    def _removeFramesRecursively(self, frame: 'Frame') -> None:
        for child in frame.childFrames:
            self._removeFramesRecursively(child)
        frame._detach()
        self._frames.pop(frame._id, None)
        self.emit(FrameManager.Events.FrameDetached, frame)


class Frame(object):
    """Frame class.

    Frame objects can be obtained via :attr:`pyppeteer.page.Page.mainFrame`.
    """

    def __init__(self, client: CDPSession, parentFrame: Optional['Frame'],
                 frameId: str) -> None:
        self._client = client
        self._parentFrame = parentFrame
        self._url = ''
        self._detached = False
        self._id = frameId

        self._documentPromise: Optional[ElementHandle] = None
        self._contextResolveCallback = lambda _: None
        self._setDefaultContext(None)

        self._waitTasks: Set[WaitTask] = set()  # maybe list
        self._loaderId = ''
        self._lifecycleEvents: Set[str] = set()
        self._childFrames: Set[Frame] = set()  # maybe list
        if self._parentFrame:
            self._parentFrame._childFrames.add(self)

    def _addExecutionContext(self, context: ExecutionContext) -> None:
        if context._isDefault:
            self._setDefaultContext(context)

    def _removeExecutionContext(self, context: ExecutionContext) -> None:
        if context._isDefault:
            self._setDefaultContext(None)

    def _setDefaultContext(self, context: Optional[ExecutionContext]) -> None:
        if context is not None:
            self._contextResolveCallback(context)  # type: ignore
            self._contextResolveCallback = lambda _: None
            for waitTask in self._waitTasks:
                self._client._loop.create_task(waitTask.rerun())
        else:
            self._documentPromise = None
            self._contextPromise = self._client._loop.create_future()
            self._contextResolveCallback = (
                lambda _context: self._contextPromise.set_result(_context)
            )

    async def executionContext(self) -> Optional[ExecutionContext]:
        """Return execution context of this frame.

        Return :class:`~pyppeteer.execution_context.ExecutionContext`
        associated to this frame.
        """
        return await self._contextPromise

    async def evaluateHandle(self, pageFunction: str, *args: Any) -> JSHandle:
        """Execute function on this frame.

        Details see :meth:`pyppeteer.page.Page.evaluateHandle`.
        """
        context = await self.executionContext()
        if context is None:
            raise PageError('this frame has no context.')
        return await context.evaluateHandle(pageFunction, *args)

    async def evaluate(self, pageFunction: str, *args: Any,
                       force_expr: bool = False) -> Any:
        """Evaluate pageFunction on this frame.

        Details see :meth:`pyppeteer.page.Page.evaluate`.
        """
        context = await self.executionContext()
        if context is None:
            raise ElementHandleError('ExecutionContext is None.')
        return await context.evaluate(
            pageFunction, *args, force_expr=force_expr)

    async def querySelector(self, selector: str) -> Optional[ElementHandle]:
        """Get element which matches `selector` string.

        Details see :meth:`pyppeteer.page.Page.querySelector`.
        """
        document = await self._document()
        value = await document.querySelector(selector)
        return value

    async def _document(self) -> ElementHandle:
        if self._documentPromise:
            return self._documentPromise
        context = await self.executionContext()
        if context is None:
            raise PageError('No context exists.')
        document = (await context.evaluateHandle('document')).asElement()
        self._documentPromise = document
        if document is None:
            raise PageError('Could not find `document`.')
        return document

    async def xpath(self, expression: str) -> List[ElementHandle]:
        """Evaluate the XPath expression.

        If there are no such elements in this frame, return an empty list.

        :arg str expression: XPath string to be evaluated.
        """
        document = await self._document()
        value = await document.xpath(expression)
        return value

    async def querySelectorEval(self, selector: str, pageFunction: str,
                                *args: Any) -> Any:
        """Execute function on element which matches selector.

        Details see :meth:`pyppeteer.page.Page.querySelectorEval`.
        """
        document = await self._document()
        return await document.querySelectorEval(selector, pageFunction, *args)

    async def querySelectorAllEval(self, selector: str, pageFunction: str,
                                   *args: Any) -> Optional[Dict]:
        """Execute function on all elements which matches selector.

        Details see :meth:`pyppeteer.page.Page.querySelectorAllEval`.
        """
        document = await self._document()
        value = await document.JJeval(selector, pageFunction, *args)
        return value

    async def querySelectorAll(self, selector: str) -> List[ElementHandle]:
        """Get all elements which matches `selector`.

        Details see :meth:`pyppeteer.page.Page.querySelectorAll`.
        """
        document = await self._document()
        value = await document.querySelectorAll(selector)
        return value

    #: Alias to :meth:`querySelector`
    J = querySelector
    #: Alias to :meth:`xpath`
    Jx = xpath
    #: Alias to :meth:`querySelectorEval`
    Jeval = querySelectorEval
    #: Alias to :meth:`querySelectorAll`
    JJ = querySelectorAll
    #: Alias to :meth:`querySelectorAllEval`
    JJeval = querySelectorAllEval

    async def content(self) -> str:
        """Get the whole HTML contents of the page."""
        return await self.evaluate('''
() => {
  let retVal = '';
  if (document.doctype)
    retVal = new XMLSerializer().serializeToString(document.doctype);
  if (document.documentElement)
    retVal += document.documentElement.outerHTML;
  return retVal;
}
        '''.strip())

    async def setContent(self, html: str) -> None:
        """Set content to this page."""
        func = '''
function(html) {
  document.open();
  document.write(html);
  document.close();
}
'''
        await self.evaluate(func, html)

    @property
    def name(self) -> str:
        """Get frame name."""
        return self.__dict__.get('_name', '')

    @property
    def url(self) -> str:
        """Get url of the frame."""
        return self._url

    @property
    def parentFrame(self) -> Optional['Frame']:
        """Get parent frame.

        If this frame is main frame or detached frame, return ``None``.
        """
        return self._parentFrame

    @property
    def childFrames(self) -> List['Frame']:
        """Get child frames."""
        return list(self._childFrames)

    def isDetached(self) -> bool:
        """Return ``True`` if this frame is detached.

        Otherwise return ``False``.
        """
        return self._detached

    async def injectFile(self, filePath: str) -> str:
        """[Deprecated] Inject file to the frame."""
        logger.warning('`injectFile` method is deprecated.'
                       ' Use `addScriptTag` method instead.')
        with open(filePath) as f:
            contents = f.read()
        contents += '/* # sourceURL= {} */'.format(filePath.replace('\n', ''))
        return await self.evaluate(contents)

    async def addScriptTag(self, options: Dict) -> ElementHandle:  # noqa: C901
        """Add script tag to this frame.

        Details see :meth:`pyppeteer.page.Page.addScriptTag`.
        """
        context = await self.executionContext()
        if context is None:
            raise ElementHandleError('ExecutionContext is None.')

        addScriptUrl = '''
        async function addScriptUrl(url, type) {
            const script = document.createElement('script');
            script.src = url;
            if (type)
                script.type = type;
            const promise = new Promise((res, rej) => {
                script.onload = res;
                script.onerror = rej;
            });
            document.head.appendChild(script);
            await promise;
            return script;
        }'''

        addScriptContent = '''
        function addScriptContent(content, type = 'text/javascript') {
            const script = document.createElement('script');
            script.type = type;
            script.text = content;
            let error = null;
            script.onerror = e => error = e;
            document.head.appendChild(script);
            if (error)
                throw error;
            return script;
        }'''

        if isinstance(options.get('url'), str):
            url = options['url']
            args = [addScriptUrl, url]
            if 'type' in options:
                args.append(options['type'])
            try:
                return (await context.evaluateHandle(*args)  # type: ignore
                        ).asElement()
            except ElementHandleError as e:
                raise PageError(f'Loading script from {url} failed') from e

        if isinstance(options.get('path'), str):
            with open(options['path']) as f:
                contents = f.read()
            contents = contents + '//# sourceURL={}'.format(
                options['path'].replace('\n', ''))
            args = [addScriptContent, contents]
            if 'type' in options:
                args.append(options['type'])
            return (await context.evaluateHandle(*args)  # type: ignore
                    ).asElement()

        if isinstance(options.get('content'), str):
            args = [addScriptContent, options['content']]
            if 'type' in options:
                args.append(options['type'])
            return (await context.evaluateHandle(*args)  # type: ignore
                    ).asElement()

        raise ValueError(
            'Provide an object with a `url`, `path` or `content` property')

    async def addStyleTag(self, options: Dict) -> ElementHandle:
        """Add style tag to this frame.

        Details see :meth:`pyppeteer.page.Page.addStyleTag`.
        """
        context = await self.executionContext()
        if context is None:
            raise ElementHandleError('ExecutionContext is None.')

        addStyleUrl = '''
        async function (url) {
            const link = document.createElement('link');
            link.rel = 'stylesheet';
            link.href = url;
            const promise = new Promise((res, rej) => {
                link.onload = res;
                link.onerror = rej;
            });
            document.head.appendChild(link);
            await promise;
            return link;
        }'''

        addStyleContent = '''
        async function (content) {
            const style = document.createElement('style');
            style.type = 'text/css';
            style.appendChild(document.createTextNode(content));
            const promise = new Promise((res, rej) => {
                style.onload = res;
                style.onerror = rej;
            });
            document.head.appendChild(style);
            await promise;
            return style;
        }'''

        if isinstance(options.get('url'), str):
            url = options['url']
            try:
                return (await context.evaluateHandle(  # type: ignore
                    addStyleUrl, url)).asElement()
            except ElementHandleError as e:
                raise PageError(f'Loading style from {url} failed') from e

        if isinstance(options.get('path'), str):
            with open(options['path']) as f:
                contents = f.read()
            contents = contents + '/*# sourceURL={}*/'.format(
                options['path'].replace('\n', ''))
            return (await context.evaluateHandle(  # type: ignore
                addStyleContent, contents)).asElement()

        if isinstance(options.get('content'), str):
            return (await context.evaluateHandle(  # type: ignore
                addStyleContent, options['content'])).asElement()

        raise ValueError(
            'Provide an object with a `url`, `path` or `content` property')

    async def click(self, selector: str, options: dict = None, **kwargs: Any
                    ) -> None:
        """Click element which matches ``selector``.

        Details see :meth:`pyppeteer.page.Page.click`.
        """
        options = merge_dict(options, kwargs)
        handle = await self.J(selector)
        if not handle:
            raise PageError('No node found for selector: ' + selector)
        await handle.click(options)
        await handle.dispose()

    async def focus(self, selector: str) -> None:
        """Focus element which matches ``selector``.

        Details see :meth:`pyppeteer.page.Page.focus`.
        """
        handle = await self.J(selector)
        if not handle:
            raise PageError('No node found for selector: ' + selector)
        await self.evaluate('element => element.focus()', handle)
        await handle.dispose()

    async def hover(self, selector: str) -> None:
        """Mouse hover the element which matches ``selector``.

        Details see :meth:`pyppeteer.page.Page.hover`.
        """
        handle = await self.J(selector)
        if not handle:
            raise PageError('No node found for selector: ' + selector)
        await handle.hover()
        await handle.dispose()

    async def select(self, selector: str, *values: str) -> List[str]:
        """Select options and return selected values.

        Details see :meth:`pyppeteer.page.Page.select`.
        """
        for value in values:
            if not isinstance(value, str):
                raise TypeError(
                    'Values must be string. '
                    f'Found {value} of type {type(value)}'
                )
        return await self.querySelectorEval(  # type: ignore
            selector, '''
(element, values) => {
    if (element.nodeName.toLowerCase() !== 'select')
        throw new Error('Element is not a <select> element.');

    const options = Array.from(element.options);
    element.value = undefined;
    for (const option of options) {
        option.selected = values.includes(option.value);
        if (option.selected && !element.multiple)
            break;
    }

    element.dispatchEvent(new Event('input', { 'bubbles': true }));
    element.dispatchEvent(new Event('change', { 'bubbles': true }));
    return options.filter(option => option.selected).map(options => options.value)
}
        ''', values)  # noqa: E501

    async def tap(self, selector: str) -> None:
        """Tap the element which matches the ``selector``.

        Details see :meth:`pyppeteer.page.Page.tap`.
        """
        handle = await self.J(selector)
        if not handle:
            raise PageError('No node found for selector: ' + selector)
        await handle.tap()
        await handle.dispose()

    async def type(self, selector: str, text: str, options: dict = None,
                   **kwargs: Any) -> None:
        """Type ``text`` on the element which matches ``selector``.

        Details see :meth:`pyppeteer.page.Page.type`.
        """
        options = merge_dict(options, kwargs)
        handle = await self.querySelector(selector)
        if handle is None:
            raise PageError('Cannot find {} on this page'.format(selector))
        await handle.type(text, options)
        await handle.dispose()

    def waitFor(self, selectorOrFunctionOrTimeout: Union[str, int, float],
                options: dict = None, *args: Any, **kwargs: Any
                ) -> Union[Awaitable, 'WaitTask']:
        """Wait until `selectorOrFunctionOrTimeout`.

        Details see :meth:`pyppeteer.page.Page.waitFor`.
        """
        options = merge_dict(options, kwargs)
        if isinstance(selectorOrFunctionOrTimeout, (int, float)):
            fut: Awaitable[None] = self._client._loop.create_task(
                asyncio.sleep(selectorOrFunctionOrTimeout / 1000))
            return fut
        if not isinstance(selectorOrFunctionOrTimeout, str):
            fut = self._client._loop.create_future()
            fut.set_exception(TypeError(
                'Unsupported target type: ' +
                str(type(selectorOrFunctionOrTimeout))
            ))
            return fut

        if args or helper.is_jsfunc(selectorOrFunctionOrTimeout):
            return self.waitForFunction(
                selectorOrFunctionOrTimeout, options, *args)
        if selectorOrFunctionOrTimeout.startswith('//'):
            return self.waitForXPath(selectorOrFunctionOrTimeout, options)
        return self.waitForSelector(selectorOrFunctionOrTimeout, options)

    def waitForSelector(self, selector: str, options: dict = None,
                        **kwargs: Any) -> 'WaitTask':
        """Wait until element which matches ``selector`` appears on page.

        Details see :meth:`pyppeteer.page.Page.waitForSelector`.
        """
        options = merge_dict(options, kwargs)
        return self._waitForSelectorOrXPath(selector, False, options)

    def waitForXPath(self, xpath: str, options: dict = None,
                     **kwargs: Any) -> 'WaitTask':
        """Wait until element which matches ``xpath`` appears on page.

        Details see :meth:`pyppeteer.page.Page.waitForXPath`.
        """
        options = merge_dict(options, kwargs)
        return self._waitForSelectorOrXPath(xpath, True, options)

    def waitForFunction(self, pageFunction: str, options: dict = None,
                        *args: Any, **kwargs: Any) -> 'WaitTask':
        """Wait until the function completes.

        Details see :meth:`pyppeteer.page.Page.waitForFunction`.
        """
        options = merge_dict(options, kwargs)
        timeout = options.get('timeout',  30000)  # msec
        polling = options.get('polling', 'raf')
        return WaitTask(self, pageFunction, 'function', polling, timeout,
                        self._client._loop, *args)

    def _waitForSelectorOrXPath(self, selectorOrXPath: str, isXPath: bool,
                                options: dict = None, **kwargs: Any
                                ) -> 'WaitTask':
        options = merge_dict(options, kwargs)
        timeout = options.get('timeout', 30000)
        waitForVisible = bool(options.get('visible'))
        waitForHidden = bool(options.get('hidden'))
        polling = 'raf' if waitForHidden or waitForVisible else 'mutation'
        title = '{} "{}"{}'.format(
            'XPath' if isXPath else 'selector',
            selectorOrXPath,
            ' to be hidden' if waitForHidden else '',
        )

        predicate = '''
(selectorOrXPath, isXPath, waitForVisible, waitForHidden) => {
    const node = isXPath
        ? document.evaluate(selectorOrXPath, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue
        : document.querySelector(selectorOrXPath);
    if (!node)
        return waitForHidden;
    if (!waitForVisible && !waitForHidden)
        return node;
    const element = /** @type {Element} */ (node.nodeType === Node.TEXT_NODE ? node.parentElement : node);

    const style = window.getComputedStyle(element);
    const isVisible = style && style.visibility !== 'hidden' && hasVisibleBoundingBox();
    const success = (waitForVisible === isVisible || waitForHidden === !isVisible)
    return success ? node : null

    function hasVisibleBoundingBox() {
        const rect = element.getBoundingClientRect();
        return !!(rect.top || rect.bottom || rect.width || rect.height);
    }
}
        '''  # noqa: E501

        return WaitTask(
            self,
            predicate,
            title,
            polling,
            timeout,
            self._client._loop,
            selectorOrXPath,
            isXPath,
            waitForVisible,
            waitForHidden,
        )

    async def title(self) -> str:
        """Get title of the frame."""
        return await self.evaluate('() => document.title')

    def _navigated(self, framePayload: dict) -> None:
        self._name = framePayload.get('name', '')
        self._navigationURL = framePayload.get('url', '')
        self._url = framePayload.get('url', '')

    def _navigatedWithinDocument(self, url: str) -> None:
        self._url = url

    def _onLifecycleEvent(self, loaderId: str, name: str) -> None:
        if name == 'init':
            self._loaderId = loaderId
            self._lifecycleEvents.clear()
        else:
            self._lifecycleEvents.add(name)

    def _onLoadingStopped(self) -> None:
        self._lifecycleEvents.add('DOMContentLoaded')
        self._lifecycleEvents.add('load')

    def _detach(self) -> None:
        for waitTask in self._waitTasks:
            waitTask.terminate(
                PageError('waitForFunction failed: frame got detached.'))
        self._detached = True
        if self._parentFrame:
            self._parentFrame._childFrames.remove(self)
        self._parentFrame = None


class WaitTask(object):
    """WaitTask class.

    Instance of this class is awaitable.
    """

    def __init__(self, frame: Frame, predicateBody: str,  # noqa: C901
                 title: str, polling: Union[str, int], timeout: float,
                 loop: asyncio.AbstractEventLoop, *args: Any) -> None:
        if isinstance(polling, str):
            if polling not in ['raf', 'mutation']:
                raise ValueError(f'Unknown polling: {polling}')
        elif isinstance(polling, (int, float)):
            if polling <= 0:
                raise ValueError(
                    f'Cannot poll with non-positive interval: {polling}'
                )
        else:
            raise ValueError(f'Unknown polling option: {polling}')

        self._frame = frame
        self._polling = polling
        self._timeout = timeout
        self._loop = loop
        if args or helper.is_jsfunc(predicateBody):
            self._predicateBody = f'return ({predicateBody})(...args)'
        else:
            self._predicateBody = f'return {predicateBody}'
        self._args = args
        self._runCount = 0
        self._terminated = False
        self._timeoutError = False
        frame._waitTasks.add(self)

        self.promise = self._loop.create_future()

        async def timer(timeout: Union[int, float]) -> None:
            await asyncio.sleep(timeout / 1000)
            self._timeoutError = True
            self.terminate(TimeoutError(
                f'Waiting for {title} failed: timeout {timeout}ms exceeds.'
            ))

        if timeout:
            self._timeoutTimer = self._loop.create_task(timer(self._timeout))
        self._runningTask = self._loop.create_task(self.rerun())

    def __await__(self) -> Generator:
        """Make this class **awaitable**."""
        result = yield from self.promise
        if isinstance(result, Exception):
            raise result
        return result

    def terminate(self, error: Exception) -> None:
        """Terminate this task."""
        self._terminated = True
        if not self.promise.done():
            self.promise.set_result(error)
        self._cleanup()

    async def rerun(self) -> None:  # noqa: C901
        """Start polling."""
        runCount = self._runCount = self._runCount + 1
        success: Optional[JSHandle] = None
        error = None

        try:
            context = await self._frame.executionContext()
            if context is None:
                raise PageError('No execution context.')
            success = await context.evaluateHandle(
                waitForPredicatePageFunction,
                self._predicateBody,
                self._polling,
                self._timeout,
                *self._args,
            )
        except Exception as e:
            error = e

        if self.promise.done():
            return

        if self._terminated or runCount != self._runCount:
            if success:
                await success.dispose()
            return

        # Add try/except referring to puppeteer.
        try:
            if not error and success and (
                    await self._frame.evaluate('s => !s', success)):
                await success.dispose()
                return
        except NetworkError:
            if success is not None:
                await success.dispose()
            return

        # page is navigated and context is destroyed.
        # Try again in the new execution context.
        if (isinstance(error, NetworkError) and
                'Execution context was destroyed' in error.args[0]):
            return

        # Try again in the new execution context.
        if (isinstance(error, NetworkError) and
                'Cannot find context with specified id' in error.args[0]):
            return

        if error:
            self.promise.set_exception(error)
        else:
            self.promise.set_result(success)

        self._cleanup()

    def _cleanup(self) -> None:
        if self._timeout and not self._timeoutError:
            self._timeoutTimer.cancel()
        self._frame._waitTasks.remove(self)


waitForPredicatePageFunction = """
async function waitForPredicatePageFunction(predicateBody, polling, timeout, ...args) {
  const predicate = new Function('...args', predicateBody);
  let timedOut = false;
  if (timeout)
    setTimeout(() => timedOut = true, timeout);
  if (polling === 'raf')
    return await pollRaf();
  if (polling === 'mutation')
    return await pollMutation();
  if (typeof polling === 'number')
    return await pollInterval(polling);

  /**
   * @return {!Promise<*>}
   */
  function pollMutation() {
    const success = predicate.apply(null, args);
    if (success)
      return Promise.resolve(success);

    let fulfill;
    const result = new Promise(x => fulfill = x);
    const observer = new MutationObserver(mutations => {
      if (timedOut) {
        observer.disconnect();
        fulfill();
      }
      const success = predicate.apply(null, args);
      if (success) {
        observer.disconnect();
        fulfill(success);
      }
    });
    observer.observe(document, {
      childList: true,
      subtree: true,
      attributes: true
    });
    return result;
  }

  /**
   * @return {!Promise<*>}
   */
  function pollRaf() {
    let fulfill;
    const result = new Promise(x => fulfill = x);
    onRaf();
    return result;

    function onRaf() {
      if (timedOut) {
        fulfill();
        return;
      }
      const success = predicate.apply(null, args);
      if (success)
        fulfill(success);
      else
        requestAnimationFrame(onRaf);
    }
  }

  /**
   * @param {number} pollInterval
   * @return {!Promise<*>}
   */
  function pollInterval(pollInterval) {
    let fulfill;
    const result = new Promise(x => fulfill = x);
    onTimeout();
    return result;

    function onTimeout() {
      if (timedOut) {
        fulfill();
        return;
      }
      const success = predicate.apply(null, args);
      if (success)
        fulfill(success);
      else
        setTimeout(onTimeout, pollInterval);
    }
  }
}
"""  # noqa: E501


================================================
FILE: pyppeteer/helper.py
================================================
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""Helper functions."""

import asyncio
import json
import logging
import math
from typing import Any, Awaitable, Callable, Dict, List

from pyee import EventEmitter

import pyppeteer
from pyppeteer.connection import CDPSession
from pyppeteer.errors import ElementHandleError, TimeoutError

logger = logging.getLogger(__name__)


def debugError(_logger: logging.Logger, msg: Any) -> None:
    """Log error messages."""
    if pyppeteer.DEBUG:
        _logger.error(msg)
    else:
        _logger.debug(msg)


def evaluationString(fun: str, *args: Any) -> str:
    """Convert function and arguments to str."""
    _args = ', '.join([
        json.dumps('undefined' if arg is None else arg) for arg in args
    ])
    expr = f'({fun})({_args})'
    return expr


def getExceptionMessage(exceptionDetails: dict) -> str:
    """Get exception message from `exceptionDetails` object."""
    exception = exceptionDetails.get('exception')
    if exception:
        return exception.get('description') or exception.get('value')
    message = exceptionDetails.get('text', '')
    stackTrace = exceptionDetails.get('stackTrace', dict())
    if stackTrace:
        for callframe in stackTrace.get('callFrames'):
            location = (
                str(callframe.get('url', '')) + ':' +
                str(callframe.get('lineNumber', '')) + ':' +
                str(callframe.get('columnNumber'))
            )
            functionName = callframe.get('functionName', '<anonymous>')
            message = message + f'\n    at {functionName} ({location})'
    return message


def addEventListener(emitter: EventEmitter, eventName: str, handler: Callable
                     ) -> Dict[str, Any]:
    """Add handler to the emitter and return emitter/handler."""
    emitter.on(eventName, handler)
    return {'emitter': emitter, 'eventName': eventName, 'handler': handler}


def removeEventListeners(listeners: List[dict]) -> None:
    """Remove listeners from emitter."""
    for listener in listeners:
        emitter = listener['emitter']
        eventName = listener['eventName']
        handler = listener['handler']
        emitter.remove_listener(eventName, handler)
    listeners.clear()


unserializableValueMap = {
    '-0': -0,
    'NaN': None,
    None: None,
    'Infinity': math.inf,
    '-Infinity': -math.inf,
}


def valueFromRemoteObject(remoteObject: Dict) -> Any:
    """Serialize value of remote object."""
    if remoteObject.get('objectId'):
        raise ElementHandleError('Cannot extract value when objectId is given')
    value = remoteObject.get('unserializableValue')
    if value:
        if value == '-0':
            return -0
        elif value == 'NaN':
            return None
        elif value == 'Infinity':
            return math.inf
        elif value == '-Infinity':
            return -math.inf
        else:
            raise ElementHandleError(
                'Unsupported unserializable value: {}'.format(value))
    return remoteObject.get('value')


def releaseObject(client: CDPSession, remoteObject: dict
                  ) -> Awaitable:
    """Release remote object."""
    objectId = remoteObject.get('objectId')
    fut_none = client._loop.create_future()
    fut_none.set_result(None)
    if not objectId:
        return fut_none
    try:
        return client.send('Runtime.releaseObject', {
            'objectId': objectId
        })
    except Exception as e:
        # Exceptions might happen in case of a page been navigated or closed.
        # Swallow these since they are harmless and we don't leak anything in this case.  # noqa
        debugError(logger, e)
    return fut_none


def waitForEvent(emitter: EventEmitter, eventName: str,  # noqa: C901
                 predicate: Callable[[Any], bool], timeout: float,
                 loop: asyncio.AbstractEventLoop) -> Awaitable:
    """Wait for an event emitted from the emitter."""
    promise = loop.create_future()

    def resolveCallback(target: Any) -> None:
        promise.set_result(target)

    def rejectCallback(exception: Exception) -> None:
        promise.set_exception(exception)

    async def timeoutTimer() -> None:
        await asyncio.sleep(timeout / 1000)
        rejectCallback(
            TimeoutError('Timeout exceeded while waiting for event'))

    def _listener(target: Any) -> None:
        if not predicate(target):
            return
        cleanup()
        resolveCallback(target)

    listener = addEventListener(emitter, eventName, _listener)
    if timeout:
        eventTimeout = loop.create_task(timeoutTimer())

    def cleanup() -> None:
        removeEventListeners([listener])
        if timeout:
            eventTimeout.cancel()

    return promise


def get_positive_int(obj: dict, name: str) -> int:
    """Get and check the value of name in obj is positive integer."""
    value = obj[name]
    if not isinstance(value, int):
        raise TypeError(
            f'{name} must be integer: {type(value)}')
    elif value < 0:
        raise ValueError(
            f'{name} must be positive integer: {value}')
    return value


def is_jsfunc(func: str) -> bool:  # not in puppeteer
    """Heuristically check function or expression."""
    func = func.strip()
    if func.startswith('function') or func.startswith('async '):
        return True
    elif '=>' in func:
        return True
    return False


================================================
FILE: pyppeteer/input.py
================================================
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""Keyboard and Mouse module."""

import asyncio
from typing import Any, Dict, TYPE_CHECKING

from pyppeteer.connection import CDPSession
from pyppeteer.errors import PyppeteerError
from pyppeteer.us_keyboard_layout import keyDefinitions
from pyppeteer.util import merge_dict

if TYPE_CHECKING:
    from typing import Set  # noqa: F401


class Keyboard(object):
    """Keyboard class provides as api for managing a virtual keyboard.

    The high level api is :meth:`type`, which takes raw characters and
    generate proper keydown, keypress/input, and keyup events on your page.

    For finer control, you can use :meth:`down`, :meth:`up`, and
    :meth:`sendCharacter` to manually fire events as if they were generated
    from a real keyboard.

    An example of holding down ``Shift`` in order to select and delete some
    text:

    .. code::

        await page.keyboard.type('Hello, World!')
        await page.keyboard.press('ArrowLeft')

        await page.keyboard.down('Shift')
        for i in ' World':
            await page.keyboard.press('ArrowLeft')
        await page.keyboard.up('Shift')

        await page.keyboard.press('Backspace')
        # Result text will end up saying 'Hello!'.

    An example of pressing ``A``:

    .. code::

        await page.keyboard.down('Shift')
        await page.keyboard.press('KeyA')
        await page.keyboard.up('Shift')
    """

    def __init__(self, client: CDPSession) -> None:
        self._client = client
        self._modifiers = 0
        self._pressedKeys: Set[str] = set()

    async def down(self, key: str, options: dict = None, **kwargs: Any
                   ) -> None:
        """Dispatch a ``keydown`` event with ``key``.

        If ``key`` is a single character and no modifier keys besides ``Shift``
        are being held down, and a ``keypress``/``input`` event will also
        generated. The ``text`` option can be specified to force an ``input``
        event to be generated.

        If ``key`` is a modifier key, like ``Shift``, ``Meta``, or ``Alt``,
        subsequent key presses will be sent with that modifier active. To
        release the modifier key, use :meth:`up` method.

        :arg str key: Name of key to press, such as ``ArrowLeft``.
        :arg dict options: Option can have ``text`` field, and if this option
            specified, generate an input event with this text.

        .. note::
            Modifier keys DO influence :meth:`down`. Holding down ``shift``
            will type the text in upper case.
        """
        options = merge_dict(options, kwargs)

        description = self._keyDescriptionForString(key)
        autoRepeat = description['code'] in self._pressedKeys
        self._pressedKeys.add(description['code'])
        self._modifiers |= self._modifierBit(description['key'])

        text = options.get('text')
        if text is None:
            text = description['text']

        await self._client.send('Input.dispatchKeyEvent', {
            'type': 'keyDown' if text else 'rawKeyDown',
            'modifiers': self._modifiers,
            'windowsVirtualKeyCode': description['keyCode'],
            'code': description['code'],
            'key': description['key'],
            'text': text,
            'unmodifiedText': text,
            'autoRepeat': autoRepeat,
            'location': description['location'],
            'isKeypad': description['location'] == 3,
        })

    def _modifierBit(self, key: str) -> int:
        if key == 'Alt':
            return 1
        if key == 'Control':
            return 2
        if key == 'Meta':
            return 4
        if key == 'Shift':
            return 8
        return 0

    def _keyDescriptionForString(self, keyString: str) -> Dict:  # noqa: C901
        shift = self._modifiers & 8
        description = {
            'key': '',
            'keyCode': 0,
            'code': '',
            'text': '',
            'location': 0,
        }

        definition: Dict = keyDefinitions.get(keyString)  # type: ignore
        if not definition:
            raise PyppeteerError(f'Unknown key: {keyString}')

        if 'key' in definition:
            description['key'] = definition['key']
        if shift and definition.get('shiftKey'):
            description['key'] = definition['shiftKey']

        if 'keyCode' in definition:
            description['keyCode'] = definition['keyCode']
        if shift and definition.get('shiftKeyCode'):
            description['keyCode'] = definition['shiftKeyCode']

        if 'code' in definition:
            description['code'] = definition['code']

        if 'location' in definition:
            description['location'] = definition['location']

        if len(description['key']) == 1:  # type: ignore
            description['text'] = description['key']

        if 'text' in definition:
            description['text'] = definition['text']
        if shift and definition.get('shiftText'):
            description['text'] = definition['shiftText']

        if self._modifiers & ~8:
            description['text'] = ''

        return description

    async def up(self, key: str) -> None:
        """Dispatch a ``keyup`` event of the ``key``.

        :arg str key: Name of key to release, such as ``ArrowLeft``.
        """
        description = self._keyDescriptionForString(key)

        self._modifiers &= ~self._modifierBit(description['key'])
        if description['code'] in self._pressedKeys:
            self._pressedKeys.remove(description['code'])
        await self._client.send('Input.dispatchKeyEvent', {
            'type': 'keyUp',
            'modifiers': self._modifiers,
            'key': description['key'],
            'windowsVirtualKeyCode': description['keyCode'],
            'code': description['code'],
            'location': description['location'],
        })

    async def sendCharacter(self, char: str) -> None:
        """Send character into the page.

        This method dispatches a ``keypress`` and ``input`` event. This does
        not send a ``keydown`` or ``keyup`` event.

        .. note::
            Modifier keys DO NOT effect :meth:`sendCharacter`. Holding down
            ``shift`` will not type the text in upper case.
        """
        await self._client.send('Input.insertText', {'text': char})

    async def type(self, text: str, options: Dict = None, **kwargs: Any
                   ) -> None:
        """Type characters into a focused element.

        This method sends ``keydown``, ``keypress``/``input``, and ``keyup``
        event for each character in the ``text``.

        To press a special key, like ``Control`` or ``ArrowDown``, use
        :meth:`press` method.

        :arg str text: Text to type into a focused element.
        :arg dict options: Options can have ``delay`` (int|float) field, which
          specifies time to wait between key presses in milliseconds. Defaults
          to 0.

        .. note::
            Modifier keys DO NOT effect :meth:`type`. Holding down ``shift``
            will not type the text in upper case.
        """
        options = merge_dict(options, kwargs)
        delay = options.get('delay', 0)
        for char in text:
            if char in keyDefinitions:
                await self.press(char, {'delay': delay})
            else:
                await self.sendCharacter(char)
            if delay:
                await asyncio.sleep(delay / 1000)

    async def press(self, key: str, options: Dict = None, **kwargs: Any
                    ) -> None:
        """Press ``key``.

        If ``key`` is a single character and no modifier keys besides
        ``Shift`` are being held down, a ``keypress``/``input`` event will also
        generated. The ``text`` option can be specified to force an input event
        to be generated.

        :arg str key: Name of key to press, such as ``ArrowLeft``.

        This method accepts the following options:

        * ``text`` (str): If specified, generates an input event with this
          text.
        * ``delay`` (int|float): Time to wait between ``keydown`` and
          ``keyup``. Defaults to 0.

        .. note::
            Modifier keys DO effect :meth:`press`. Holding down ``Shift`` will
            type the text in upper case.
        """
        options = merge_dict(options, kwargs)

        await self.down(key, options)
        if 'delay' in options:
            await asyncio.sleep(options['delay'] / 1000)
        await self.up(key)


class Mouse(object):
    """Mouse class.

    The :class:`Mouse` operates in main-frame CSS pixels relative to the
    top-left corner of the viewport.
    """

    def __init__(self, client: CDPSession, keyboard: Keyboard) -> None:
        self._client = client
        self._keyboard = keyboard
        self._x = 0.0
        self._y = 0.0
        self._button = 'none'

    async def move(self, x: float, y: float, options: dict = None,
                   **kwargs: Any) -> None:
        """Move mouse cursor (dispatches a ``mousemove`` event).

        Options can accepts ``steps`` (int) field. If this ``steps`` option
        specified, Sends intermediate ``mousemove`` events. Defaults to 1.
        """
        options = merge_dict(options, kwargs)
        fromX = self._x
        fromY = self._y
        self._x = x
        self._y = y
        steps = options.get('steps', 1)
        for i in range(1, steps + 1):
            x = round(fromX + (self._x - fromX) * (i / steps))
            y = round(fromY + (self._y - fromY) * (i / steps))
            await self._client.send('Input.dispatchMouseEvent', {
                'type': 'mouseMoved',
                'button': self._button,
                'x': x,
                'y': y,
                'modifiers': self._keyboard._modifiers,
            })

    async def click(self, x: float, y: float, options: dict = None,
                    **kwargs: Any) -> None:
        """Click button at (``x``, ``y``).

        Shortcut to :meth:`move`, :meth:`down`, and :meth:`up`.

        This method accepts the following options:

        * ``button`` (str): ``left``, ``right``, or ``middle``, defaults to
          ``left``.
        * ``clickCount`` (int): defaults to 1.
        * ``delay`` (int|float): Time to wait between ``mousedown`` and
          ``mouseup`` in milliseconds. Defaults to 0.
        """
        options = merge_dict(options, kwargs)
        await self.move(x, y)
        await self.down(options)
        if options and options.get('delay'):
            await asyncio.sleep(options.get('delay', 0) / 1000)
        await self.up(options)

    async def down(self, options: dict = None, **kwargs: Any) -> None:
        """Press down button (dispatches ``mousedown`` event).

        This method accepts the following options:

        * ``button`` (str): ``left``, ``right``, or ``middle``, defaults to
          ``left``.
        * ``clickCount`` (int): defaults to 1.
        """
        options = merge_dict(options, kwargs)
        self._button = options.get('button', 'left')
        await self._client.send('Input.dispatchMouseEvent', {
            'type': 'mousePressed',
            'button': self._button,
            'x': self._x,
            'y': self._y,
            'modifiers': self._keyboard._modifiers,
            'clickCount': options.get('clickCount') or 1,
        })

    async def up(self, options: dict = None, **kwargs: Any) -> None:
        """Release pressed button (dispatches ``mouseup`` event).

        This method accepts the following options:

        * ``button`` (str): ``left``, ``right``, or ``middle``, defaults to
          ``left``.
        * ``clickCount`` (int): defaults to 1.
        """
        options = merge_dict(options, kwargs)
        self._button = 'none'
        await self._client.send('Input.dispatchMouseEvent', {
            'type': 'mouseReleased',
            'button': options.get('button', 'left'),
            'x': self._x,
            'y': self._y,
            'modifiers': self._keyboard._modifiers,
            'clickCount': options.get('clickCount') or 1,
        })


class Touchscreen(object):
    """Touchscreen class."""

    def __init__(self, client: CDPSession, keyboard: Keyboard) -> None:
        """Make new touchscreen object."""
        self._client = client
        self._keyboard = keyboard

    async def tap(self, x: float, y: float) -> None:
        """Tap (``x``, ``y``).

        Dispatches a ``touchstart`` and ``touchend`` event.
        """
        touchPoints = [{'x': round(x), 'y': round(y)}]
        await self._client.send('Input.dispatchTouchEvent', {
            'type': 'touchStart',
            'touchPoints': touchPoints,
            'modifiers': self._keyboard._modifiers,
        })
        await self._client.send('Input.dispatchTouchEvent', {
            'type': 'touchEnd',
            'touchPoints': [],
            'modifiers': self._keyboard._modifiers,
        })


================================================
FILE: pyppeteer/launcher.py
================================================
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""Chromium process launcher module."""

import asyncio
import atexit
from copy import copy
import json
from urllib.request import urlopen
from urllib.error import URLError
from http.client import HTTPException
import logging
import os
import os.path
from pathlib import Path
import shutil
import signal
import subprocess
import sys
import tempfile
import time
from typing import Any, Dict, List, TYPE_CHECKING

from pyppeteer import __pyppeteer_home__
from pyppeteer.browser import Browser
from pyppeteer.connection import Connection
from pyppeteer.chromium_downloader import current_platform
from pyppeteer.errors import BrowserError
from pyppeteer.helper import addEventListener, debugError, removeEventListeners
from pyppeteer.target import Target
from pyppeteer.util import check_chromium, chromium_executable
from pyppeteer.util import download_chromium, merge_dict, get_free_port

if TYPE_CHECKING:
    from typing import Optional  # noqa: F401

logger = logging.getLogger(__name__)

pyppeteer_home = Path(__pyppeteer_home__)
CHROME_PROFILE_PATH = pyppeteer_home / '.dev_profile'

DEFAULT_ARGS = [
    '--disable-background-networking',
    '--disable-background-timer-throttling',
    '--disable-breakpad',
    '--disable-browser-side-navigation',
    '--disable-client-side-phishing-detection',
    '--disable-default-apps',
    '--disable-dev-shm-usage',
    '--disable-extensions',
    '--disable-features=site-per-process',
    '--disable-hang-monitor',
    '--disable-popup-blocking',
    '--disable-prompt-on-repost',
    '--disable-sync',
    '--disable-translate',
    '--metrics-recording-only',
    '--no-first-run',
    '--safebrowsing-disable-auto-update',
    '--enable-automation',
    '--password-store=basic',
    '--use-mock-keychain',
]


class Launcher(object):
    """Chrome process launcher class."""

    def __init__(self, options: Dict[str, Any] = None,  # noqa: C901
                 **kwargs: Any) -> None:
        """Make new launcher."""
        options = merge_dict(options, kwargs)

        self.port = get_free_port()
        self.url = f'http://127.0.0.1:{self.port}'
        self._loop = options.get('loop', asyncio.get_event_loop())
        self.chromeClosed = True

        ignoreDefaultArgs = options.get('ignoreDefaultArgs', False)
        args: List[str] = options.get('args', list())
        self.dumpio = options.get('dumpio', False)
        executablePath = options.get('executablePath')
        self.env = options.get('env')
        self.handleSIGINT = options.get('handleSIGINT', True)
        self.handleSIGTERM = options.get('handleSIGTERM', True)
        self.handleSIGHUP = options.get('handleSIGHUP', True)
        self.ignoreHTTPSErrors = options.get('ignoreHTTPSErrors', False)
        self.defaultViewport = options.get('defaultViewport', {'width': 800, 'height': 600})  # noqa: E501
        self.slowMo = options.get('slowMo', 0)
        self.timeout = options.get('timeout', 30000)
        self.autoClose = options.get('autoClose', True)

        logLevel = options.get('logLevel')
        if logLevel:
            logging.getLogger('pyppeteer').setLevel(logLevel)

        self.chromeArguments: List[str] = list()
        if not ignoreDefaultArgs:
            self.chromeArguments.extend(defaultArgs(options))
        elif isinstance(ignoreDefaultArgs, list):
            self.chromeArguments.extend(filter(lambda arg: arg not in ignoreDefaultArgs, defaultArgs(options), ))
        else:
            self.chromeArguments.extend(args)

        self.temporaryUserDataDir: Optional[str] = None

        if not any(arg for arg in self.chromeArguments if arg.startswith('--remote-debugging-')):
            self.chromeArguments.append(f'--remote-debugging-port={self.port}')

        if not any(arg for arg in self.chromeArguments if arg.startswith('--user-data-dir')):
            if not CHROME_PROFILE_PATH.exists():
                CHROME_PROFILE_PATH.mkdir(parents=True)
            self.temporaryUserDataDir = tempfile.mkdtemp(dir=str(CHROME_PROFILE_PATH))  # noqa: E501
            self.chromeArguments.append(f'--user-data-dir={self.temporaryUserDataDir}')  # noqa: E501

        self.chromeExecutable = executablePath
        if not self.chromeExecutable:
            if not check_chromium():
                download_chromium()
            self.chromeExecutable = str(chromium_executable())

        self.cmd = [self.chromeExecutable] + self.chromeArguments

    def _cleanup_tmp_user_data_dir(self) -> None:
        for retry in range(100):
            if self.temporaryUserDataDir and os.path.exists(self.temporaryUserDataDir):
                shutil.rmtree(self.temporaryUserDataDir, ignore_errors=True)
                if os.path.exists(self.temporaryUserDataDir):
                    time.sleep(0.01)
            else:
                break
        else:
            raise IOError('Unable to remove Temporary User Data')

    async def launch(self) -> Browser:  # noqa: C901
        """Start chrome process and return `Browser` object."""
        self.chromeClosed = False
        self.connection: Optional[Connection] = None

        options = dict()
        options['env'] = self.env
        if not self.dumpio:
            # discard stdout, it's never read in any case.
            options['stdout'] = subprocess.DEVNULL
            options['stderr'] = subprocess.STDOUT

        self.proc = subprocess.Popen(  # type: ignore
            self.cmd, **options, )

        def _close_process(*args: Any, **kwargs: Any) -> None:
            if not self.chromeClosed:
                self._loop.run_until_complete(self.killChrome())

        # don't forget to close browser process
        if self.autoClose:
            atexit.register(_close_process)
        if self.handleSIGINT:
            signal.signal(signal.SIGINT, _close_process)
        if self.handleSIGTERM:
            signal.signal(signal.SIGTERM, _close_process)
        if not sys.platform.startswith('win'):
            # SIGHUP is not defined on windows
            if self.handleSIGHUP:
                signal.signal(signal.SIGHUP, _close_process)

        connectionDelay = self.slowMo
        self.browserWSEndpoint = get_ws_endpoint(self.url)
        logger.info(f'Browser listening on: {self.browserWSEndpoint}')
        self.connection = Connection(self.browserWSEndpoint, self._loop, connectionDelay, )
        browser = await Browser.create(self.connection, [], self.ignoreHTTPSErrors, self.defaultViewport, self.proc,
                                       self.killChrome)
        await self.ensureInitialPage(browser)
        return browser

    async def ensureInitialPage(self, browser: Browser) -> None:
        """Wait for initial page target to be created."""
        for target in browser.targets():
            if target.type == 'page':
                return

        initialPagePromise = self._loop.create_future()

        def initialPageCallback() -> None:
            initialPagePromise.set_result(True)

        def check_target(target: Target) -> None:
            if target.type == 'page':
                initialPageCallback()

        listeners = [addEventListener(browser, 'targetcreated', check_target)]
        await initialPagePromise
        removeEventListeners(listeners)

    def waitForChromeToClose(self) -> None:
        """Terminate chrome."""
        if self.proc.poll() is None and not self.chromeClosed:
            self.chromeClosed = True
            try:
                self.proc.terminate()
                self.proc.wait()
            except Exception:
                # browser process may be already closed
                pass

    async def killChrome(self) -> None:
        """Terminate chromium process."""
        logger.info('terminate chrome process...')
        if self.connection and self.connection._connected:
            try:
                await self.connection.send('Browser.close')
                await self.connection.dispose()
            except Exception as e:
                # ignore errors on browser termination process
                debugError(logger, e)
        if self.temporaryUserDataDir and os.path.exists(self.temporaryUserDataDir):  # noqa: E501
            # Force kill chrome only when using temporary userDataDir
            self.waitForChromeToClose()
            self._cleanup_tmp_user_data_dir()


def get_ws_endpoint(url) -> str:
    url = url + '/json/version'
    timeout = time.time() + 30
    while (True):
        if time.time() > timeout:
            raise BrowserError('Browser closed unexpectedly:\n')
        try:
            with urlopen(url) as f:
                data = json.loads(f.read().decode())
            break
        except (URLError, HTTPException):
            pass
        time.sleep(0.1)

    return data['webSocketDebuggerUrl']


async def launch(options: dict = None, **kwargs: Any) -> Browser:
    """Start chrome process and return :class:`~pyppeteer.browser.Browser`.
    This function is a shortcut to :meth:`Launcher(options, **kwargs).launch`.
    Available options are:
    * ``ignoreHTTPSErrors`` (bool): Whether to ignore HTTPS errors. Defaults to
      ``False``.
    * ``headless`` (bool): Whether to run browser in headless mode. Defaults to
      ``True`` unless ``appMode`` or ``devtools`` options is ``True``.
    * ``executablePath`` (str): Path to a Chromium or Chrome executable to run
      instead of default bundled Chromium.
    * ``slowMo`` (int|float): Slow down pyppeteer operations by the specified
      amount of milliseconds.
    * ``defaultViewport`` (dict): Set a consistent viewport for each page.
      Defaults to an 800x600 viewport. ``None`` disables default viewport.
      * ``width`` (int): page width in pixels.
      * ``height`` (int): page height in pixels.
      * ``deviceScaleFactor`` (int|float): Specify device scale factor (can be
        thought as dpr). Defaults to ``1``.
      * ``isMobile`` (bool): Whether the ``meta viewport`` tag is taken into
        account. Defaults to ``False``.
      * ``hasTouch`` (bool): Specify if viewport supports touch events.
        Defaults to ``False``.
      * ``isLandscape`` (bool): Specify if viewport is in landscape mode.
        Defaults to ``False``.
    * ``args`` (List[str]): Additional arguments (flags) to pass to the browser
      process.
    * ``ignoreDefaultArgs`` (bool or List[str]): If ``True``, do not use
      :func:`~pyppeteer.defaultArgs`. If list is given, then filter out given
      default arguments. Dangerous option; use with care. Defaults to
      ``False``.
    * ``handleSIGINT`` (bool): Close the browser process on Ctrl+C. Defaults to
      ``True``.
    * 
Download .txt
gitextract_a3lak9ot/

├── .circleci/
│   └── config.yml
├── .coveragerc
├── .gitignore
├── .noserc
├── .pre-commit-config.yaml
├── CHANGELOG.md
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── docs/
│   ├── Makefile
│   ├── _static/
│   │   └── custom.css
│   ├── _templates/
│   │   └── layout.html
│   ├── changes.md
│   ├── conf.py
│   ├── index.md
│   ├── make.bat
│   ├── reference.md
│   └── server.py
├── pyppeteer/
│   ├── __init__.py
│   ├── browser.py
│   ├── chromium_downloader.py
│   ├── command.py
│   ├── connection.py
│   ├── coverage.py
│   ├── dialog.py
│   ├── element_handle.py
│   ├── emulation_manager.py
│   ├── errors.py
│   ├── execution_context.py
│   ├── frame_manager.py
│   ├── helper.py
│   ├── input.py
│   ├── launcher.py
│   ├── multimap.py
│   ├── navigator_watcher.py
│   ├── network_manager.py
│   ├── options.py
│   ├── page.py
│   ├── target.py
│   ├── tracing.py
│   ├── us_keyboard_layout.py
│   ├── util.py
│   └── worker.py
├── pyproject.toml
├── spell.txt
├── tests/
│   ├── __init__.py
│   ├── base.py
│   ├── closeme.py
│   ├── dumpio.py
│   ├── file-to-upload.txt
│   ├── frame_utils.py
│   ├── server.py
│   ├── static/
│   │   ├── beforeunload.html
│   │   ├── button.html
│   │   ├── cached/
│   │   │   ├── one-style.css
│   │   │   └── one-style.html
│   │   ├── checkbox.html
│   │   ├── csp.html
│   │   ├── csscoverage/
│   │   │   ├── involved.html
│   │   │   ├── media.html
│   │   │   ├── multiple.html
│   │   │   ├── simple.html
│   │   │   ├── sourceurl.html
│   │   │   ├── stylesheet1.css
│   │   │   ├── stylesheet2.css
│   │   │   └── unused.html
│   │   ├── detect-touch.html
│   │   ├── error.html
│   │   ├── es6/
│   │   │   ├── es6import.js
│   │   │   ├── es6module.js
│   │   │   └── es6pathimport.js
│   │   ├── fileupload.html
│   │   ├── frame-204.html
│   │   ├── frame.html
│   │   ├── grid.html
│   │   ├── historyapi.html
│   │   ├── huge-page.html
│   │   ├── injectedfile.js
│   │   ├── injectedstyle.css
│   │   ├── jscoverage/
│   │   │   ├── eval.html
│   │   │   ├── involved.html
│   │   │   ├── multiple.html
│   │   │   ├── ranges.html
│   │   │   ├── script1.js
│   │   │   ├── script2.js
│   │   │   ├── simple.html
│   │   │   ├── sourceurl.html
│   │   │   └── unused.html
│   │   ├── keyboard.html
│   │   ├── mobile.html
│   │   ├── modernizr.js
│   │   ├── mouse-helper.js
│   │   ├── nested-frames.html
│   │   ├── offscreenbuttons.html
│   │   ├── one-frame.html
│   │   ├── one-style.css
│   │   ├── one-style.html
│   │   ├── popup/
│   │   │   ├── popup.html
│   │   │   └── window-open.html
│   │   ├── resetcss.html
│   │   ├── script.js
│   │   ├── scrollable.html
│   │   ├── select.html
│   │   ├── self-request.html
│   │   ├── serviceworkers/
│   │   │   ├── empty/
│   │   │   │   ├── sw.html
│   │   │   │   └── sw.js
│   │   │   └── fetch/
│   │   │       ├── style.css
│   │   │       ├── sw.html
│   │   │       └── sw.js
│   │   ├── shadow.html
│   │   ├── simple-extension/
│   │   │   ├── index.js
│   │   │   └── manifest.json
│   │   ├── simple.json
│   │   ├── style.css
│   │   ├── sw.js
│   │   ├── temperable.html
│   │   ├── textarea.html
│   │   ├── touches.html
│   │   ├── two-frames.html
│   │   ├── worker/
│   │   │   ├── worker.html
│   │   │   └── worker.js
│   │   └── wrappedlink.html
│   ├── test_abnormal_crash.py
│   ├── test_browser.py
│   ├── test_browser_context.py
│   ├── test_connection.py
│   ├── test_coverage.py
│   ├── test_dialog.py
│   ├── test_element_handle.py
│   ├── test_execution_context.py
│   ├── test_frame.py
│   ├── test_input.py
│   ├── test_launcher.py
│   ├── test_misc.py
│   ├── test_network.py
│   ├── test_page.py
│   ├── test_pyppeteer.py
│   ├── test_screenshot.py
│   ├── test_target.py
│   ├── test_tracing.py
│   ├── test_worker.py
│   └── utils.py
└── tox.ini
Download .txt
SYMBOL INDEX (1110 symbols across 51 files)

FILE: docs/server.py
  function cmd (line 21) | def cmd() -> None:
  function docs (line 32) | def docs(p: str) -> str:

FILE: pyppeteer/browser.py
  class Browser (line 21) | class Browser(EventEmitter):
    method __init__ (line 36) | def __init__(self, connection: Connection, contextIds: List[str],
    method process (line 82) | def process(self) -> Optional[Popen]:
    method createIncogniteBrowserContext (line 90) | async def createIncogniteBrowserContext(self) -> 'BrowserContext':
    method createIncognitoBrowserContext (line 101) | async def createIncognitoBrowserContext(self) -> 'BrowserContext':
    method browserContexts (line 124) | def browserContexts(self) -> List['BrowserContext']:
    method _disposeContext (line 132) | async def _disposeContext(self, contextId: str) -> None:
    method create (line 139) | async def create(connection: Connection, contextIds: List[str],
    method _targetCreated (line 150) | async def _targetCreated(self, event: Dict) -> None:
    method _targetDestroyed (line 175) | async def _targetDestroyed(self, event: Dict) -> None:
    method _targetInfoChanged (line 184) | async def _targetInfoChanged(self, event: Dict) -> None:
    method wsEndpoint (line 196) | def wsEndpoint(self) -> str:
    method newPage (line 200) | async def newPage(self) -> Page:
    method _createPageInContext (line 204) | async def _createPageInContext(self, contextId: Optional[str]) -> Page:
    method targets (line 221) | def targets(self) -> List[Target]:
    method pages (line 230) | async def pages(self) -> List[Page]:
    method version (line 245) | async def version(self) -> str:
    method userAgent (line 250) | async def userAgent(self) -> str:
    method close (line 260) | async def close(self) -> None:
    method disconnect (line 264) | async def disconnect(self) -> None:
    method _getVersion (line 271) | def _getVersion(self) -> Awaitable:
  class BrowserContext (line 275) | class BrowserContext(EventEmitter):
    method __init__ (line 307) | def __init__(self, browser: Browser, contextId: Optional[str]) -> None:
    method targets (line 312) | def targets(self) -> List[Target]:
    method pages (line 320) | async def pages(self) -> List[Page]:
    method isIncognite (line 335) | def isIncognite(self) -> bool:
    method isIncognito (line 346) | def isIncognito(self) -> bool:
    method newPage (line 356) | async def newPage(self) -> Page:
    method browser (line 361) | def browser(self) -> Browser:
    method close (line 365) | async def close(self) -> None:

FILE: pyppeteer/chromium_downloader.py
  function current_platform (line 55) | def current_platform() -> str:
  function get_url (line 68) | def get_url() -> str:
  function download_zip (line 73) | def download_zip(url: str) -> BytesIO:
  function extract_zip (line 103) | def extract_zip(data: BytesIO, path: Path) -> None:
  function download_chromium (line 136) | def download_chromium() -> None:
  function chromium_executable (line 141) | def chromium_executable() -> Path:
  function check_chromium (line 146) | def check_chromium() -> bool:

FILE: pyppeteer/command.py
  function install (line 11) | def install() -> None:

FILE: pyppeteer/connection.py
  class Connection (line 25) | class Connection(EventEmitter):
    method __init__ (line 28) | def __init__(self, url: str, loop: asyncio.AbstractEventLoop,
    method url (line 49) | def url(self) -> str:
    method _recv_loop (line 53) | async def _recv_loop(self) -> None:
    method _async_send (line 69) | async def _async_send(self, msg: str, callback_id: int) -> None:
    method send (line 81) | def send(self, method: str, params: dict = None) -> Awaitable:
    method _on_response (line 103) | def _on_response(self, msg: dict) -> None:
    method _on_query (line 116) | def _on_query(self, msg: dict) -> None:
    method setClosedCallback (line 132) | def setClosedCallback(self, callback: Callable[[], None]) -> None:
    method _on_message (line 136) | async def _on_message(self, message: str) -> None:
    method _on_close (line 145) | async def _on_close(self) -> None:
    method dispose (line 167) | async def dispose(self) -> None:
    method createSession (line 172) | async def createSession(self, targetInfo: Dict) -> 'CDPSession':
  class CDPSession (line 184) | class CDPSession(EventEmitter):
    method __init__ (line 197) | def __init__(self, connection: Union[Connection, 'CDPSession'],
    method send (line 210) | def send(self, method: str, params: dict = None) -> Awaitable:
    method _on_message (line 246) | def _on_message(self, msg: str) -> None:  # noqa: C901
    method detach (line 278) | async def detach(self) -> None:
    method _on_closed (line 289) | def _on_closed(self) -> None:
    method _createSession (line 298) | def _createSession(self, targetType: str, sessionId: str) -> 'CDPSessi...
  function _createProtocolError (line 304) | def _createProtocolError(error: Exception, method: str, obj: Dict
  function _rewriteError (line 312) | def _rewriteError(error: Exception, message: str) -> Exception:

FILE: pyppeteer/coverage.py
  class Coverage (line 20) | class Coverage(object):
    method __init__ (line 49) | def __init__(self, client: CDPSession) -> None:
    method startJSCoverage (line 53) | async def startJSCoverage(self, options: Dict = None, **kwargs: Any
    method stopJSCoverage (line 74) | async def stopJSCoverage(self) -> List:
    method startCSSCoverage (line 93) | async def startCSSCoverage(self, options: Dict = None, **kwargs: Any
    method stopCSSCoverage (line 105) | async def stopCSSCoverage(self) -> List:
  class JSCoverage (line 126) | class JSCoverage(object):
    method __init__ (line 129) | def __init__(self, client: CDPSession) -> None:
    method start (line 137) | async def start(self, options: Dict = None, **kwargs: Any) -> None:
    method _onExecutionContextsCleared (line 163) | def _onExecutionContextsCleared(self, event: Dict) -> None:
    method _onScriptParsed (line 169) | async def _onScriptParsed(self, event: Dict) -> None:
    method stop (line 193) | async def stop(self) -> List:
  class CSSCoverage (line 219) | class CSSCoverage(object):
    method __init__ (line 222) | def __init__(self, client: CDPSession) -> None:
    method start (line 230) | async def start(self, options: Dict = None, **kwargs: Any) -> None:
    method _onExecutionContextsCleared (line 253) | def _onExecutionContextsCleared(self, event: Dict) -> None:
    method _onStyleSheet (line 259) | async def _onStyleSheet(self, event: Dict) -> None:
    method stop (line 275) | async def stop(self) -> List:
  function convertToDisjointRanges (line 310) | def convertToDisjointRanges(nestedRanges: List[Any]  # noqa: C901

FILE: pyppeteer/dialog.py
  class Dialog (line 11) | class Dialog(object):
    method __init__ (line 42) | def __init__(self, client: CDPSession, type: str, message: str,
    method type (line 51) | def type(self) -> str:
    method message (line 59) | def message(self) -> str:
    method defaultValue (line 64) | def defaultValue(self) -> str:
    method accept (line 71) | async def accept(self, promptText: str = '') -> None:
    method dismiss (line 83) | async def dismiss(self) -> None:

FILE: pyppeteer/element_handle.py
  class ElementHandle (line 25) | class ElementHandle(JSHandle):
    method __init__ (line 40) | def __init__(self, context: ExecutionContext, client: CDPSession,
    method asElement (line 50) | def asElement(self) -> 'ElementHandle':
    method contentFrame (line 54) | async def contentFrame(self) -> Optional['Frame']:
    method _scrollIntoViewIfNeeded (line 67) | async def _scrollIntoViewIfNeeded(self) -> None:
    method _clickablePoint (line 101) | async def _clickablePoint(self) -> Dict[str, float]:  # noqa: C901
    method _getBoxModel (line 131) | async def _getBoxModel(self) -> Optional[Dict]:
    method _fromProtocolQuad (line 142) | def _fromProtocolQuad(self, quad: List[int]) -> List[Dict[str, int]]:
    method hover (line 150) | async def hover(self) -> None:
    method click (line 162) | async def click(self, options: dict = None, **kwargs: Any) -> None:
    method uploadFile (line 183) | async def uploadFile(self, *filePaths: str) -> dict:
    method tap (line 192) | async def tap(self) -> None:
    method focus (line 204) | async def focus(self) -> None:
    method type (line 209) | async def type(self, text: str, options: Dict = None, **kwargs: Any
    method press (line 219) | async def press(self, key: str, options: Dict = None, **kwargs: Any
    method boundingBox (line 240) | async def boundingBox(self) -> Optional[Dict[str, float]]:
    method boxModel (line 264) | async def boxModel(self) -> Optional[Dict]:
    method screenshot (line 295) | async def screenshot(self, options: Dict = None, **kwargs: Any) -> bytes:
    method querySelector (line 353) | async def querySelector(self, selector: str) -> Optional['ElementHandl...
    method querySelectorAll (line 368) | async def querySelectorAll(self, selector: str) -> List['ElementHandle']:
    method querySelectorEval (line 386) | async def querySelectorEval(self, selector: str, pageFunction: str,
    method querySelectorAllEval (line 418) | async def querySelectorAllEval(self, selector: str, pageFunction: str,
    method xpath (line 462) | async def xpath(self, expression: str) -> List['ElementHandle']:
    method isIntersectingViewport (line 493) | async def isIntersectingViewport(self) -> bool:
  function _computeQuadArea (line 507) | def _computeQuadArea(quad: List[Dict]) -> float:

FILE: pyppeteer/emulation_manager.py
  class EmulationManager (line 10) | class EmulationManager(object):
    method __init__ (line 13) | def __init__(self, client: CDPSession) -> None:
    method emulateViewport (line 19) | async def emulateViewport(self, viewport: dict) -> bool:

FILE: pyppeteer/errors.py
  class PyppeteerError (line 9) | class PyppeteerError(Exception):  # noqa: D204
  class BrowserError (line 14) | class BrowserError(PyppeteerError):  # noqa: D204
  class ElementHandleError (line 19) | class ElementHandleError(PyppeteerError):  # noqa: D204
  class NetworkError (line 24) | class NetworkError(PyppeteerError):  # noqa: D204
  class PageError (line 29) | class PageError(PyppeteerError):  # noqa: D204
  class TimeoutError (line 34) | class TimeoutError(asyncio.TimeoutError):  # noqa: D204

FILE: pyppeteer/execution_context.py
  class ExecutionContext (line 29) | class ExecutionContext(object):
    method __init__ (line 32) | def __init__(self, client: CDPSession, contextPayload: Dict,
    method frame (line 43) | def frame(self) -> Optional['Frame']:
    method evaluate (line 47) | async def evaluate(self, pageFunction: str, *args: Any,
    method evaluateHandle (line 66) | async def evaluateHandle(self, pageFunction: str, *args: Any,  # noqa:...
    method _convertArgument (line 117) | def _convertArgument(self, arg: Any) -> Dict:  # noqa: C901
    method queryObjects (line 135) | async def queryObjects(self, prototypeHandle: 'JSHandle') -> 'JSHandle':
  class JSHandle (line 151) | class JSHandle(object):
    method __init__ (line 158) | def __init__(self, context: ExecutionContext, client: CDPSession,
    method executionContext (line 166) | def executionContext(self) -> ExecutionContext:
    method getProperty (line 170) | async def getProperty(self, propertyName: str) -> 'JSHandle':
    method getProperties (line 183) | async def getProperties(self) -> Dict[str, 'JSHandle']:
    method jsonValue (line 197) | async def jsonValue(self) -> Dict:
    method asElement (line 210) | def asElement(self) -> Optional['ElementHandle']:
    method dispose (line 214) | async def dispose(self) -> None:
    method toString (line 224) | def toString(self) -> str:
  function _rewriteError (line 234) | def _rewriteError(error: Exception) -> None:

FILE: pyppeteer/frame_manager.py
  class FrameManager (line 25) | class FrameManager(EventEmitter):
    method __init__ (line 36) | def __init__(self, client: CDPSession, frameTree: Dict, page: Any) -> ...
    method _onLifecycleEvent (line 74) | def _onLifecycleEvent(self, event: Dict) -> None:
    method _onFrameStoppedLoading (line 81) | def _onFrameStoppedLoading(self, frameId: str) -> None:
    method _handleFrameTree (line 88) | def _handleFrameTree(self, frameTree: Dict) -> None:
    method mainFrame (line 102) | def mainFrame(self) -> Optional['Frame']:
    method frames (line 106) | def frames(self) -> List['Frame']:
    method frame (line 110) | def frame(self, frameId: str) -> Optional['Frame']:
    method _onFrameAttached (line 114) | def _onFrameAttached(self, frameId: str, parentFrameId: str) -> None:
    method _onFrameNavigated (line 122) | def _onFrameNavigated(self, framePayload: dict) -> None:
    method _onFrameNavigatedWithinDocument (line 154) | def _onFrameNavigatedWithinDocument(self, frameId: str, url: str) -> N...
    method _onFrameDetached (line 162) | def _onFrameDetached(self, frameId: str) -> None:
    method _onExecutionContextCreated (line 167) | def _onExecutionContextCreated(self, contextPayload: Dict) -> None:
    method _onExecutionContextDestroyed (line 191) | def _onExecutionContextDestroyed(self, executionContextId: str) -> None:
    method _onExecutionContextsCleared (line 201) | def _onExecutionContextsCleared(self) -> None:
    method executionContextById (line 208) | def executionContextById(self, contextId: str) -> ExecutionContext:
    method createJSHandle (line 217) | def createJSHandle(self, context: ExecutionContext,
    method _removeFramesRecursively (line 227) | def _removeFramesRecursively(self, frame: 'Frame') -> None:
  class Frame (line 235) | class Frame(object):
    method __init__ (line 241) | def __init__(self, client: CDPSession, parentFrame: Optional['Frame'],
    method _addExecutionContext (line 260) | def _addExecutionContext(self, context: ExecutionContext) -> None:
    method _removeExecutionContext (line 264) | def _removeExecutionContext(self, context: ExecutionContext) -> None:
    method _setDefaultContext (line 268) | def _setDefaultContext(self, context: Optional[ExecutionContext]) -> N...
    method executionContext (line 281) | async def executionContext(self) -> Optional[ExecutionContext]:
    method evaluateHandle (line 289) | async def evaluateHandle(self, pageFunction: str, *args: Any) -> JSHan...
    method evaluate (line 299) | async def evaluate(self, pageFunction: str, *args: Any,
    method querySelector (line 311) | async def querySelector(self, selector: str) -> Optional[ElementHandle]:
    method _document (line 320) | async def _document(self) -> ElementHandle:
    method xpath (line 332) | async def xpath(self, expression: str) -> List[ElementHandle]:
    method querySelectorEval (line 343) | async def querySelectorEval(self, selector: str, pageFunction: str,
    method querySelectorAllEval (line 352) | async def querySelectorAllEval(self, selector: str, pageFunction: str,
    method querySelectorAll (line 362) | async def querySelectorAll(self, selector: str) -> List[ElementHandle]:
    method content (line 382) | async def content(self) -> str:
    method setContent (line 395) | async def setContent(self, html: str) -> None:
    method name (line 407) | def name(self) -> str:
    method url (line 412) | def url(self) -> str:
    method parentFrame (line 417) | def parentFrame(self) -> Optional['Frame']:
    method childFrames (line 425) | def childFrames(self) -> List['Frame']:
    method isDetached (line 429) | def isDetached(self) -> bool:
    method injectFile (line 436) | async def injectFile(self, filePath: str) -> str:
    method addScriptTag (line 445) | async def addScriptTag(self, options: Dict) -> ElementHandle:  # noqa:...
    method addStyleTag (line 514) | async def addStyleTag(self, options: Dict) -> ElementHandle:
    method click (line 574) | async def click(self, selector: str, options: dict = None, **kwargs: Any
    method focus (line 587) | async def focus(self, selector: str) -> None:
    method hover (line 598) | async def hover(self, selector: str) -> None:
    method select (line 609) | async def select(self, selector: str, *values: str) -> List[str]:
    method tap (line 640) | async def tap(self, selector: str) -> None:
    method type (line 651) | async def type(self, selector: str, text: str, options: dict = None,
    method waitFor (line 664) | def waitFor(self, selectorOrFunctionOrTimeout: Union[str, int, float],
    method waitForSelector (line 691) | def waitForSelector(self, selector: str, options: dict = None,
    method waitForXPath (line 700) | def waitForXPath(self, xpath: str, options: dict = None,
    method waitForFunction (line 709) | def waitForFunction(self, pageFunction: str, options: dict = None,
    method _waitForSelectorOrXPath (line 721) | def _waitForSelectorOrXPath(self, selectorOrXPath: str, isXPath: bool,
    method title (line 771) | async def title(self) -> str:
    method _navigated (line 775) | def _navigated(self, framePayload: dict) -> None:
    method _navigatedWithinDocument (line 780) | def _navigatedWithinDocument(self, url: str) -> None:
    method _onLifecycleEvent (line 783) | def _onLifecycleEvent(self, loaderId: str, name: str) -> None:
    method _onLoadingStopped (line 790) | def _onLoadingStopped(self) -> None:
    method _detach (line 794) | def _detach(self) -> None:
  class WaitTask (line 804) | class WaitTask(object):
    method __init__ (line 810) | def __init__(self, frame: Frame, predicateBody: str,  # noqa: C901
    method __await__ (line 851) | def __await__(self) -> Generator:
    method terminate (line 858) | def terminate(self, error: Exception) -> None:
    method rerun (line 865) | async def rerun(self) -> None:  # noqa: C901
    method _cleanup (line 922) | def _cleanup(self) -> None:

FILE: pyppeteer/helper.py
  function debugError (line 21) | def debugError(_logger: logging.Logger, msg: Any) -> None:
  function evaluationString (line 29) | def evaluationString(fun: str, *args: Any) -> str:
  function getExceptionMessage (line 38) | def getExceptionMessage(exceptionDetails: dict) -> str:
  function addEventListener (line 57) | def addEventListener(emitter: EventEmitter, eventName: str, handler: Cal...
  function removeEventListeners (line 64) | def removeEventListeners(listeners: List[dict]) -> None:
  function valueFromRemoteObject (line 83) | def valueFromRemoteObject(remoteObject: Dict) -> Any:
  function releaseObject (line 103) | def releaseObject(client: CDPSession, remoteObject: dict
  function waitForEvent (line 122) | def waitForEvent(emitter: EventEmitter, eventName: str,  # noqa: C901
  function get_positive_int (line 157) | def get_positive_int(obj: dict, name: str) -> int:
  function is_jsfunc (line 169) | def is_jsfunc(func: str) -> bool:  # not in puppeteer

FILE: pyppeteer/input.py
  class Keyboard (line 18) | class Keyboard(object):
    method __init__ (line 53) | def __init__(self, client: CDPSession) -> None:
    method down (line 58) | async def down(self, key: str, options: dict = None, **kwargs: Any
    method _modifierBit (line 103) | def _modifierBit(self, key: str) -> int:
    method _keyDescriptionForString (line 114) | def _keyDescriptionForString(self, keyString: str) -> Dict:  # noqa: C901
    method up (line 157) | async def up(self, key: str) -> None:
    method sendCharacter (line 176) | async def sendCharacter(self, char: str) -> None:
    method type (line 188) | async def type(self, text: str, options: Dict = None, **kwargs: Any
    method press (line 217) | async def press(self, key: str, options: Dict = None, **kwargs: Any
  class Mouse (line 247) | class Mouse(object):
    method __init__ (line 254) | def __init__(self, client: CDPSession, keyboard: Keyboard) -> None:
    method move (line 261) | async def move(self, x: float, y: float, options: dict = None,
    method click (line 285) | async def click(self, x: float, y: float, options: dict = None,
    method down (line 306) | async def down(self, options: dict = None, **kwargs: Any) -> None:
    method up (line 326) | async def up(self, options: dict = None, **kwargs: Any) -> None:
  class Touchscreen (line 347) | class Touchscreen(object):
    method __init__ (line 350) | def __init__(self, client: CDPSession, keyboard: Keyboard) -> None:
    method tap (line 355) | async def tap(self, x: float, y: float) -> None:

FILE: pyppeteer/launcher.py
  class Launcher (line 67) | class Launcher(object):
    method __init__ (line 70) | def __init__(self, options: Dict[str, Any] = None,  # noqa: C901
    method _cleanup_tmp_user_data_dir (line 125) | def _cleanup_tmp_user_data_dir(self) -> None:
    method launch (line 136) | async def launch(self) -> Browser:  # noqa: C901
    method ensureInitialPage (line 176) | async def ensureInitialPage(self, browser: Browser) -> None:
    method waitForChromeToClose (line 195) | def waitForChromeToClose(self) -> None:
    method killChrome (line 206) | async def killChrome(self) -> None:
  function get_ws_endpoint (line 222) | def get_ws_endpoint(url) -> str:
  function launch (line 239) | async def launch(options: dict = None, **kwargs: Any) -> Browser:
  function connect (line 310) | async def connect(options: dict = None, **kwargs: Any) -> Browser:
  function executablePath (line 360) | def executablePath() -> str:
  function defaultArgs (line 365) | def defaultArgs(options: Dict = None, **kwargs: Any) -> List[str]:  # no...

FILE: pyppeteer/multimap.py
  class Multimap (line 10) | class Multimap(object):
    method __init__ (line 13) | def __init__(self) -> None:
    method set (line 18) | def set(self, key: Optional[str], value: Any) -> None:
    method get (line 27) | def get(self, key: Optional[str]) -> List[Any]:
    method has (line 31) | def has(self, key: Optional[str]) -> bool:
    method hasValue (line 35) | def hasValue(self, key: Optional[str], value: Any) -> bool:
    method size (line 40) | def size(self) -> int:
    method delete (line 44) | def delete(self, key: Optional[str], value: Any) -> bool:
    method deleteAll (line 54) | def deleteAll(self, key: Optional[str]) -> None:
    method firstValue (line 58) | def firstValue(self, key: Optional[str]) -> Any:
    method firstKey (line 65) | def firstKey(self) -> Optional[str]:
    method valuesArray (line 69) | def valuesArray(self) -> List[Any]:
    method clear (line 76) | def clear(self) -> None:

FILE: pyppeteer/navigator_watcher.py
  class NavigatorWatcher (line 16) | class NavigatorWatcher:
    method __init__ (line 19) | def __init__(self, frameManager: FrameManager, frame: Frame, timeout: ...
    method _validate_options (line 56) | def _validate_options(self, options: Dict) -> None:  # noqa: C901
    method _createTimeoutPromise (line 90) | def _createTimeoutPromise(self) -> Awaitable[None]:
    method navigationPromise (line 104) | def navigationPromise(self) -> Any:
    method _navigatedWithinDocument (line 108) | def _navigatedWithinDocument(self, frame: Frame = None) -> None:
    method _checkLifecycleComplete (line 114) | def _checkLifecycleComplete(self, frame: Frame = None) -> None:
    method _checkLifecycle (line 124) | def _checkLifecycle(self, frame: Frame, expectedLifecycle: List[str]
    method cancel (line 134) | def cancel(self) -> None:
    method _cleanup (line 138) | def _cleanup(self) -> None:

FILE: pyppeteer/network_manager.py
  class NetworkManager (line 30) | class NetworkManager(EventEmitter):
    method __init__ (line 40) | def __init__(self, client: CDPSession, frameManager: FrameManager) -> ...
    method authenticate (line 68) | async def authenticate(self, credentials: Dict[str, str]) -> None:
    method setExtraHTTPHeaders (line 73) | async def setExtraHTTPHeaders(self, extraHTTPHeaders: Dict[str, str]
    method extraHTTPHeaders (line 86) | def extraHTTPHeaders(self) -> Dict[str, str]:
    method setOfflineMode (line 90) | async def setOfflineMode(self, value: bool) -> None:
    method setUserAgent (line 102) | async def setUserAgent(self, userAgent: str) -> None:
    method setRequestInterception (line 107) | async def setRequestInterception(self, value: bool) -> None:
    method _updateProtocolRequestInterception (line 112) | async def _updateProtocolRequestInterception(self) -> None:
    method _onRequestWillBeSent (line 130) | async def _onRequestWillBeSent(self, event: Dict) -> None:
    method _send (line 143) | async def _send(self, method: str, msg: dict) -> None:
    method _onRequestIntercepted (line 149) | def _onRequestIntercepted(self, event: dict) -> None:  # noqa: C901
    method _onRequest (line 190) | def _onRequest(self, event: Dict, interceptionId: Optional[str]) -> None:
    method _onRequestServedFromCache (line 221) | def _onRequestServedFromCache(self, event: Dict) -> None:
    method _handleRequestRedirect (line 226) | def _handleRequestRedirect(self, request: 'Request', redirectStatus: int,
    method _handleRequestStart (line 243) | def _handleRequestStart(self, requestId: str,
    method _onResponseReceived (line 260) | def _onResponseReceived(self, event: dict) -> None:
    method _onLoadingFinished (line 275) | def _onLoadingFinished(self, event: dict) -> None:
    method _onLoadingFailed (line 288) | def _onLoadingFailed(self, event: dict) -> None:
  class Request (line 303) | class Request(object):
    method __init__ (line 323) | def __init__(self, client: CDPSession, requestId: Optional[str],
    method url (line 350) | def url(self) -> str:
    method resourceType (line 355) | def resourceType(self) -> str:
    method method (line 366) | def method(self) -> Optional[str]:
    method postData (line 371) | def postData(self) -> Optional[str]:
    method headers (line 376) | def headers(self) -> Dict:
    method response (line 384) | def response(self) -> Optional['Response']:
    method frame (line 392) | def frame(self) -> Optional[Frame]:
    method isNavigationRequest (line 399) | def isNavigationRequest(self) -> bool:
    method redirectChain (line 404) | def redirectChain(self) -> List['Request']:
    method failure (line 416) | def failure(self) -> Optional[Dict]:
    method continue_ (line 430) | async def continue_(self, overrides: Dict = None) -> None:
    method respond (line 460) | async def respond(self, response: Dict) -> None:  # noqa: C901
    method abort (line 519) | async def abort(self, errorCode: str = 'failed') -> None:
  class Response (line 587) | class Response(object):
    method __init__ (line 590) | def __init__(self, client: CDPSession, request: Request, status: int,
    method _bodyLoadedPromiseFulfill (line 614) | def _bodyLoadedPromiseFulfill(self, value: Optional[Exception]) -> None:
    method url (line 618) | def url(self) -> str:
    method ok (line 623) | def ok(self) -> bool:
    method status (line 628) | def status(self) -> int:
    method headers (line 633) | def headers(self) -> Dict:
    method securityDetails (line 641) | def securityDetails(self) -> Union[Dict, 'SecurityDetails']:
    method _bufread (line 649) | async def _bufread(self) -> bytes:
    method buffer (line 661) | def buffer(self) -> Awaitable[bytes]:
    method text (line 667) | async def text(self) -> str:
    method json (line 675) | async def json(self) -> dict:
    method request (line 681) | def request(self) -> Request:
    method fromCache (line 686) | def fromCache(self) -> bool:
    method fromServiceWorker (line 694) | def fromServiceWorker(self) -> bool:
  function generateRequestHash (line 699) | def generateRequestHash(request: dict) -> str:
  class SecurityDetails (line 731) | class SecurityDetails(object):
    method __init__ (line 734) | def __init__(self, subjectName: str, issuer: str, validFrom: int,
    method subjectName (line 743) | def subjectName(self) -> str:
    method issuer (line 748) | def issuer(self) -> str:
    method validFrom (line 753) | def validFrom(self) -> int:
    method validTo (line 758) | def validTo(self) -> int:
    method protocol (line 763) | def protocol(self) -> str:

FILE: pyppeteer/page.py
  class Page (line 40) | class Page(EventEmitter):
    method create (line 86) | async def create(
    method __init__ (line 113) | def __init__(
    method target (line 199) | def target(self) -> 'Target':
    method browser (line 204) | def browser(self) -> 'Browser':
    method _onTargetCrashed (line 208) | def _onTargetCrashed(self, *args: Any, **kwargs: Any) -> None:
    method _onLogEntryAdded (line 211) | def _onLogEntryAdded(self, event: Dict) -> None:
    method mainFrame (line 224) | def mainFrame(self) -> Optional['Frame']:
    method keyboard (line 229) | def keyboard(self) -> Keyboard:
    method touchscreen (line 234) | def touchscreen(self) -> Touchscreen:
    method coverage (line 239) | def coverage(self) -> Coverage:
    method tap (line 243) | async def tap(self, selector: str) -> None:
    method tracing (line 254) | def tracing(self) -> 'Tracing':
    method frames (line 259) | def frames(self) -> List['Frame']:
    method workers (line 264) | def workers(self) -> List[Worker]:
    method setRequestInterception (line 268) | async def setRequestInterception(self, value: bool) -> None:
    method setOfflineMode (line 303) | async def setOfflineMode(self, enabled: bool) -> None:
    method setDefaultNavigationTimeout (line 307) | def setDefaultNavigationTimeout(self, timeout: int) -> None:
    method _send (line 324) | async def _send(self, method: str, msg: dict) -> None:
    method _onCertificateError (line 330) | def _onCertificateError(self, event: Any) -> None:
    method querySelector (line 337) | async def querySelector(self, selector: str) -> Optional[ElementHandle]:
    method evaluateHandle (line 351) | async def evaluateHandle(self, pageFunction: str, *args: Any) -> JSHan...
    method queryObjects (line 367) | async def queryObjects(self, prototypeHandle: JSHandle) -> JSHandle:
    method querySelectorEval (line 379) | async def querySelectorEval(self, selector: str, pageFunction: str, *a...
    method querySelectorAllEval (line 395) | async def querySelectorAllEval(self, selector: str, pageFunction: str,...
    method querySelectorAll (line 409) | async def querySelectorAll(self, selector: str) -> List[ElementHandle]:
    method xpath (line 423) | async def xpath(self, expression: str) -> List[ElementHandle]:
    method cookies (line 446) | async def cookies(self, *urls: str) -> List[Dict[str, Union[str, int, ...
    method deleteCookie (line 471) | async def deleteCookie(self, *cookies: dict) -> None:
    method setCookie (line 489) | async def setCookie(self, *cookies: dict) -> None:
    method addScriptTag (line 522) | async def addScriptTag(self, options: Dict = None, **kwargs: str) -> E...
    method addStyleTag (line 541) | async def addStyleTag(self, options: Dict = None, **kwargs: str) -> El...
    method injectFile (line 558) | async def injectFile(self, filePath: str) -> str:
    method exposeFunction (line 568) | async def exposeFunction(self, name: str, pyppeteerFunction: Callable[...
    method authenticate (line 613) | async def authenticate(self, credentials: Dict[str, str]) -> Any:
    method setExtraHTTPHeaders (line 621) | async def setExtraHTTPHeaders(self, headers: Dict[str, str]) -> None:
    method setUserAgent (line 637) | async def setUserAgent(self, userAgent: str) -> None:
    method metrics (line 644) | async def metrics(self) -> Dict[str, Any]:
    method _emitMetrics (line 671) | def _emitMetrics(self, event: Dict) -> None:
    method _buildMetricsObject (line 676) | def _buildMetricsObject(self, metrics: List) -> Dict[str, Any]:
    method _handleException (line 683) | def _handleException(self, exceptionDetails: Dict) -> None:
    method _onConsoleAPI (line 687) | def _onConsoleAPI(self, event: dict) -> None:
    method _onBindingCalled (line 695) | def _onBindingCalled(self, event: Dict) -> None:
    method _addConsoleMessage (line 715) | def _addConsoleMessage(self, type: str, args: List[JSHandle]) -> None:
    method _onDialog (line 732) | def _onDialog(self, event: Any) -> None:
    method url (line 747) | def url(self) -> str:
    method content (line 754) | async def content(self) -> str:
    method setContent (line 764) | async def setContent(self, html: str) -> None:
    method goto (line 774) | async def goto(self, url: str, options: dict = None, **kwargs: Any) ->...
    method _navigate (line 843) | async def _navigate(self, url: str, referrer: str) -> Optional[str]:
    method reload (line 849) | async def reload(self, options: dict = None, **kwargs: Any) -> Optiona...
    method waitForNavigation (line 858) | async def waitForNavigation(self, options: dict = None, **kwargs: Any)...
    method waitForRequest (line 912) | async def waitForRequest(
    method waitForResponse (line 946) | async def waitForResponse(
    method goBack (line 980) | async def goBack(self, options: dict = None, **kwargs: Any) -> Optiona...
    method goForward (line 990) | async def goForward(self, options: dict = None, **kwargs: Any) -> Opti...
    method _go (line 1000) | async def _go(self, delta: int, options: dict) -> Optional[Response]:
    method bringToFront (line 1015) | async def bringToFront(self) -> None:
    method emulate (line 1019) | async def emulate(self, options: dict = None, **kwargs: Any) -> None:
    method setJavaScriptEnabled (line 1050) | async def setJavaScriptEnabled(self, enabled: bool) -> None:
    method setBypassCSP (line 1057) | async def setBypassCSP(self, enabled: bool) -> None:
    method emulateMedia (line 1067) | async def emulateMedia(self, mediaType: str = None) -> None:
    method setViewport (line 1079) | async def setViewport(self, viewport: dict) -> None:
    method viewport (line 1096) | def viewport(self) -> Optional[Dict]:
    method evaluate (line 1103) | async def evaluate(self, pageFunction: str, *args: Any, force_expr: bo...
    method evaluateOnNewDocument (line 1119) | async def evaluateOnNewDocument(self, pageFunction: str, *args: str) -...
    method setCacheEnabled (line 1131) | async def setCacheEnabled(self, enabled: bool = True) -> None:
    method screenshot (line 1138) | async def screenshot(self, options: dict = None, **kwargs: Any) -> Uni...
    method _screenshotTask (line 1182) | async def _screenshotTask(self, format: str, options: dict) -> Union[b...
    method pdf (line 1251) | async def pdf(self, options: dict = None, **kwargs: Any) -> bytes:
    method plainText (line 1401) | async def plainText(self) -> str:
    method title (line 1406) | async def title(self) -> str:
    method close (line 1413) | async def close(self, options: Dict = None, **kwargs: Any) -> None:
    method isClosed (line 1440) | def isClosed(self) -> bool:
    method mouse (line 1445) | def mouse(self) -> Mouse:
    method click (line 1449) | async def click(self, selector: str, options: dict = None, **kwargs: A...
    method hover (line 1480) | async def hover(self, selector: str) -> None:
    method focus (line 1490) | async def focus(self, selector: str) -> None:
    method select (line 1500) | async def select(self, selector: str, *values: str) -> List[str]:
    method type (line 1510) | async def type(self, selector: str, text: str, options: dict = None, *...
    method waitFor (line 1522) | def waitFor(
    method waitForSelector (line 1557) | def waitForSelector(self, selector: str, options: dict = None, **kwarg...
    method waitForXPath (line 1585) | def waitForXPath(self, xpath: str, options: dict = None, **kwargs: Any...
    method waitForFunction (line 1614) | def waitForFunction(self, pageFunction: str, options: dict = None, *ar...
  function convertPrintParameterToInches (line 1665) | def convertPrintParameterToInches(parameter: Union[None, int, float, str...
  class ConsoleMessage (line 1689) | class ConsoleMessage(object):
    method __init__ (line 1695) | def __init__(self, type: str, text: str, args: List[JSHandle] = None) ...
    method type (line 1704) | def type(self) -> str:
    method text (line 1709) | def text(self) -> str:
    method args (line 1714) | def args(self) -> List[JSHandle]:

FILE: pyppeteer/target.py
  class Target (line 17) | class Target(object):
    method __init__ (line 20) | def __init__(self, targetInfo: Dict, browserContext: 'BrowserContext',
    method _initializedCallback (line 42) | def _initializedCallback(self, bl: bool) -> None:
    method _closedCallback (line 48) | def _closedCallback(self) -> None:
    method createCDPSession (line 51) | async def createCDPSession(self) -> CDPSession:
    method page (line 55) | async def page(self) -> Optional[Page]:
    method url (line 75) | def url(self) -> str:
    method type (line 80) | def type(self) -> str:
    method browser (line 92) | def browser(self) -> 'Browser':
    method browserContext (line 97) | def browserContext(self) -> 'BrowserContext':
    method opener (line 102) | def opener(self) -> Optional['Target']:
    method _targetInfoChanged (line 112) | def _targetInfoChanged(self, targetInfo: Dict) -> None:

FILE: pyppeteer/tracing.py
  class Tracing (line 13) | class Tracing(object):
    method __init__ (line 27) | def __init__(self, client: CDPSession) -> None:
    method start (line 32) | async def start(self, options: dict = None, **kwargs: Any) -> None:
    method stop (line 66) | async def stop(self) -> str:
    method _readStream (line 84) | async def _readStream(self, handle: str, path: str) -> str:

FILE: pyppeteer/util.py
  function get_free_port (line 22) | def get_free_port() -> int:
  function merge_dict (line 33) | def merge_dict(dict1: Optional[Dict], dict2: Optional[Dict]) -> Dict:

FILE: pyppeteer/worker.py
  class Worker (line 20) | class Worker(EventEmitter):
    method __init__ (line 31) | def __init__(self, client: 'CDPSession', url: str,  # noqa: C901
    method _executionContextCallback (line 75) | def _executionContextCallback(self, value: ExecutionContext) -> None:
    method url (line 79) | def url(self) -> str:
    method executionContext (line 83) | async def executionContext(self) -> ExecutionContext:
    method evaluate (line 87) | async def evaluate(self, pageFunction: str, *args: Any) -> Any:
    method evaluateHandle (line 95) | async def evaluateHandle(self, pageFunction: str, *args: Any) -> JSHan...

FILE: tests/base.py
  class BaseTestCase (line 16) | class BaseTestCase(unittest.TestCase):
    method setUpClass (line 18) | def setUpClass(cls):
    method tearDownClass (line 26) | def tearDownClass(cls):
    method setUp (line 30) | def setUp(self):
    method tearDown (line 35) | def tearDown(self):
    method set_result (line 40) | def set_result(self, value):

FILE: tests/closeme.py
  function main (line 9) | async def main() -> None:

FILE: tests/dumpio.py
  function main (line 12) | async def main():

FILE: tests/frame_utils.py
  function attachFrame (line 8) | async def attachFrame(page: Page, frameId: str, url: str) -> None:
  function detachFrame (line 21) | async def detachFrame(page: Page, frameId: str) -> None:
  function navigateFrame (line 31) | async def navigateFrame(page: Page, frameId: str, url: str) -> None:
  function dumpFrames (line 42) | def dumpFrames(frame: Frame, indentation: str = '') -> str:

FILE: tests/server.py
  class BaseHandler (line 26) | class BaseHandler(web.RequestHandler):
    method get (line 27) | def get(self) -> None:
  class MainHandler (line 34) | class MainHandler(BaseHandler):
    method get (line 35) | def get(self) -> None:
  class EmptyHandler (line 40) | class EmptyHandler(BaseHandler):
    method get (line 41) | def get(self) -> None:
  class LongHandler (line 46) | class LongHandler(BaseHandler):
    method get (line 47) | async def get(self) -> None:
  class LinkHandler1 (line 53) | class LinkHandler1(BaseHandler):
    method get (line 54) | def get(self) -> None:
  class RedirectHandler1 (line 64) | class RedirectHandler1(BaseHandler):
    method get (line 65) | def get(self) -> None:
  class RedirectHandler2 (line 70) | class RedirectHandler2(BaseHandler):
    method get (line 71) | def get(self) -> None:
  class RedirectHandler3 (line 76) | class RedirectHandler3(BaseHandler):
    method get (line 77) | def get(self) -> None:
  class ResourceRedirectHandler (line 82) | class ResourceRedirectHandler(BaseHandler):
    method get (line 83) | def get(self) -> None:
  class CSSRedirectHandler1 (line 91) | class CSSRedirectHandler1(BaseHandler):
    method get (line 92) | def get(self) -> None:
  class CSSRedirectHandler2 (line 97) | class CSSRedirectHandler2(BaseHandler):
    method get (line 98) | def get(self) -> None:
  class CSSRedirectHandler3 (line 103) | class CSSRedirectHandler3(BaseHandler):
    method get (line 104) | def get(self) -> None:
  class CSSRedirectHandler4 (line 109) | class CSSRedirectHandler4(BaseHandler):
    method get (line 110) | def get(self) -> None:
  class CSPHandler (line 115) | class CSPHandler(BaseHandler):
    method get (line 116) | def get(self) -> None:
  function auth_api (line 122) | def auth_api(username: str, password: str) -> bool:
  function basic_auth (line 129) | def basic_auth(auth: Callable[[str, str], bool]) -> Callable:
  class AuthHandler (line 158) | class AuthHandler(BaseHandler):
    method get (line 160) | def get(self) -> None:
  function log_handler (line 165) | def log_handler(handler: Any) -> None:
  function get_application (line 174) | def get_application() -> web.Application:

FILE: tests/static/modernizr.js
  function o (line 3) | function o(e,n){return typeof e===n}
  function s (line 3) | function s(){var e,n,t,s,a,i,r;for(var l in c)if(c.hasOwnProperty(l)){if...
  function a (line 3) | function a(e){var n=u.className,t=Modernizr._config.classPrefix||"";if(p...
  function i (line 3) | function i(){return"function"!=typeof n.createElement?n.createElement(ar...
  function r (line 3) | function r(){var e=n.body;return e||(e=i(p?"svg":"body"),e.fake=!0),e}
  function l (line 3) | function l(e,t,o,s){var a,l,f,c,d="modernizr",p=i("div"),h=r();if(parseI...

FILE: tests/static/mouse-helper.js
  function updateButtons (line 58) | function updateButtons(buttons) {

FILE: tests/static/worker/worker.js
  function workerFunction (line 2) | function workerFunction() {

FILE: tests/test_abnormal_crash.py
  class TestBrowserCrash (line 15) | class TestBrowserCrash(unittest.TestCase):
    method test_browser_crash_send (line 17) | async def test_browser_crash_send(self):

FILE: tests/test_browser.py
  class TestBrowser (line 18) | class TestBrowser(unittest.TestCase):
    method waitForBackgroundPageTarget (line 29) | def waitForBackgroundPageTarget(self, browser):
    method test_browser_process (line 46) | async def test_browser_process(self):
    method test_version (line 56) | async def test_version(self):
    method test_user_agent (line 64) | async def test_user_agent(self):
    method test_disconnect (line 72) | async def test_disconnect(self):
    method test_crash (line 102) | async def test_crash(self):
    method test_background_target_type (line 117) | async def test_background_target_type(self):
    method test_OOPIF (line 127) | async def test_OOPIF(self):
    method test_background_page (line 156) | async def test_background_page(self):
  class TestPageClose (line 165) | class TestPageClose(BaseTestCase):
    method test_not_visible_in_browser_pages (line 167) | async def test_not_visible_in_browser_pages(self):
    method test_before_unload (line 174) | async def test_before_unload(self):
    method test_page_close_state (line 187) | async def test_page_close_state(self):

FILE: tests/test_browser_context.py
  class BrowserBaseTestCase (line 16) | class BrowserBaseTestCase(BaseTestCase):
    method setUp (line 17) | def setUp(self):
    method tearDown (line 20) | def tearDown(self):
  class TestBrowserContext (line 24) | class TestBrowserContext(BrowserBaseTestCase):
    method test_default_context (line 26) | async def test_default_context(self):
    method test_incognito_context (line 36) | async def test_incognito_context(self):
    method test_close_all_targets_once (line 46) | async def test_close_all_targets_once(self):
    method test_window_open_use_parent_tab_context (line 56) | async def test_window_open_use_parent_tab_context(self):
    method test_fire_target_event (line 67) | async def test_fire_target_event(self):
    method test_isolate_local_storage_and_cookie (line 84) | async def test_isolate_local_storage_and_cookie(self):
    method test_across_session (line 125) | async def test_across_session(self):

FILE: tests/test_connection.py
  class TestConnection (line 11) | class TestConnection(BaseTestCase):
    method test_error_msg (line 13) | async def test_error_msg(self):
  class TestCDPSession (line 19) | class TestCDPSession(BaseTestCase):
    method test_create_session (line 21) | async def test_create_session(self):
    method test_send_event (line 30) | async def test_send_event(self):
    method test_enable_disable_domain (line 39) | async def test_enable_disable_domain(self):
    method test_detach (line 47) | async def test_detach(self):

FILE: tests/test_coverage.py
  class TestJSCoverage (line 9) | class TestJSCoverage(BaseTestCase):
    method test_js_coverage (line 11) | async def test_js_coverage(self):
    method test_js_coverage_source_url (line 26) | async def test_js_coverage_source_url(self):
    method test_js_coverage_ignore_empty (line 34) | async def test_js_coverage_ignore_empty(self):
    method test_ignore_eval_script_by_default (line 41) | async def test_ignore_eval_script_by_default(self):
    method test_not_ignore_eval_script_with_reportAnonymousScript (line 48) | async def test_not_ignore_eval_script_with_reportAnonymousScript(self):
    method test_ignore_injected_script (line 57) | async def test_ignore_injected_script(self):
    method test_ignore_injected_script_with_reportAnonymousScript (line 66) | async def test_ignore_injected_script_with_reportAnonymousScript(self):
    method test_js_coverage_multiple_script (line 75) | async def test_js_coverage_multiple_script(self):
    method test_js_coverage_ranges (line 85) | async def test_js_coverage_ranges(self):
    method test_no_coverage (line 99) | async def test_no_coverage(self):
    method test_js_coverage_condition (line 109) | async def test_js_coverage_condition(self):
    method test_js_coverage_no_reset_navigation (line 123) | async def test_js_coverage_no_reset_navigation(self):
    method test_js_coverage_reset_navigation (line 131) | async def test_js_coverage_reset_navigation(self):
  class TestCSSCoverage (line 139) | class TestCSSCoverage(BaseTestCase):
    method test_css_coverage (line 141) | async def test_css_coverage(self):
    method test_css_coverage_url (line 155) | async def test_css_coverage_url(self):
    method test_css_coverage_multiple (line 163) | async def test_css_coverage_multiple(self):
    method test_css_coverage_no_coverage (line 173) | async def test_css_coverage_no_coverage(self):
    method test_css_coverage_media (line 182) | async def test_css_coverage_media(self):
    method test_css_coverage_complicated (line 191) | async def test_css_coverage_complicated(self):
    method test_css_ignore_injected_css (line 203) | async def test_css_ignore_injected_css(self):
    method test_css_coverage_no_reset_navigation (line 215) | async def test_css_coverage_no_reset_navigation(self):
    method test_css_coverage_reset_navigation (line 223) | async def test_css_coverage_reset_navigation(self):

FILE: tests/test_dialog.py
  class TestDialog (line 11) | class TestDialog(BaseTestCase):
    method test_alert (line 13) | async def test_alert(self):
    method test_prompt (line 23) | async def test_prompt(self):
    method test_prompt_dismiss (line 34) | async def test_prompt_dismiss(self):

FILE: tests/test_element_handle.py
  class TestBoundingBox (line 16) | class TestBoundingBox(BaseTestCase):
    method test_bounding_box (line 18) | async def test_bounding_box(self):
    method test_nested_frame (line 26) | async def test_nested_frame(self):
    method test_invisible_element (line 43) | async def test_invisible_element(self):
    method test_force_layout (line 49) | async def test_force_layout(self):
    method test_svg (line 67) | async def test_svg(self):
  class TestBoxModel (line 82) | class TestBoxModel(BaseTestCase):
    method setUp (line 83) | def setUp(self):
    method tearDown (line 87) | def tearDown(self):
    method test_box_model (line 92) | async def test_box_model(self):
    method test_box_model_invisible (line 148) | async def test_box_model_invisible(self):
    method test_debug_error (line 155) | async def test_debug_error(self):
  class TestContentFrame (line 167) | class TestContentFrame(BaseTestCase):
    method test_content_frame (line 169) | async def test_content_frame(self):
  class TestClick (line 177) | class TestClick(BaseTestCase):
    method test_clik (line 179) | async def test_clik(self):
    method test_shadow_dom (line 186) | async def test_shadow_dom(self):
    method test_text_node (line 193) | async def test_text_node(self):
    method test_detached_node (line 203) | async def test_detached_node(self):
    method test_hidden_node (line 213) | async def test_hidden_node(self):
    method test_recursively_hidden_node (line 225) | async def test_recursively_hidden_node(self):
    method test_br_node (line 238) | async def test_br_node(self):
  class TestHover (line 249) | class TestHover(BaseTestCase):
    method test_hover (line 251) | async def test_hover(self):
  class TestIsIntersectingViewport (line 262) | class TestIsIntersectingViewport(BaseTestCase):
    method test_is_intersecting_viewport (line 264) | async def test_is_intersecting_viewport(self):
  class TestScreenshot (line 272) | class TestScreenshot(BaseTestCase):
    method test_screenshot_larger_than_viewport (line 274) | async def test_screenshot_larger_than_viewport(self):
  class TestQuerySelector (line 301) | class TestQuerySelector(BaseTestCase):
    method test_J (line 303) | async def test_J(self):
    method test_J_none (line 314) | async def test_J_none(self):
    method test_Jeval (line 323) | async def test_Jeval(self):
    method test_Jeval_subtree (line 335) | async def test_Jeval_subtree(self):
    method test_Jeval_with_missing_selector (line 343) | async def test_Jeval_with_missing_selector(self):
    method test_JJ (line 353) | async def test_JJ(self):
    method test_JJ_empty (line 369) | async def test_JJ_empty(self):
    method test_JJEval (line 378) | async def test_JJEval(self):
    method test_JJEval_subtree (line 389) | async def test_JJEval_subtree(self):
    method test_JJEval_missing_selector (line 403) | async def test_JJEval_missing_selector(self):
    method test_xpath (line 411) | async def test_xpath(self):
    method test_xpath_not_found (line 422) | async def test_xpath_not_found(self):

FILE: tests/test_execution_context.py
  class TestQueryObject (line 11) | class TestQueryObject(BaseTestCase):
    method test_query_objects (line 13) | async def test_query_objects(self):
    method test_query_objects_disposed (line 32) | async def test_query_objects_disposed(self):
    method test_query_objects_primitive_value_error (line 42) | async def test_query_objects_primitive_value_error(self):
  class TestJSHandle (line 49) | class TestJSHandle(BaseTestCase):
    method test_get_property (line 51) | async def test_get_property(self):
    method test_json_value (line 59) | async def test_json_value(self):
    method test_json_date_fail (line 65) | async def test_json_date_fail(self):
    method test_json_circular_object_error (line 73) | async def test_json_circular_object_error(self):
    method test_get_properties (line 81) | async def test_get_properties(self):
    method test_return_non_own_properties (line 89) | async def test_return_non_own_properties(self):
    method test_as_element (line 109) | async def test_as_element(self):
    method test_as_element_non_element (line 115) | async def test_as_element_non_element(self):
    method test_as_element_text_node (line 121) | async def test_as_element_text_node(self):
    method test_to_string_number (line 133) | async def test_to_string_number(self):
    method test_to_string_str (line 138) | async def test_to_string_str(self):
    method test_to_string_complicated_object (line 143) | async def test_to_string_complicated_object(self):

FILE: tests/test_frame.py
  class TestContext (line 19) | class TestContext(BaseTestCase):
    method test_frame_context (line 21) | async def test_frame_context(self):
  class TestEvaluateHandle (line 43) | class TestEvaluateHandle(BaseTestCase):
    method test_evaluate_handle (line 45) | async def test_evaluate_handle(self):
  class TestEvaluate (line 52) | class TestEvaluate(BaseTestCase):
    method test_frame_evaluate (line 54) | async def test_frame_evaluate(self):
    method test_frame_evaluate_after_navigation (line 68) | async def test_frame_evaluate_after_navigation(self):
    method test_frame_cross_site (line 80) | async def test_frame_cross_site(self):
  class TestWaitForFunction (line 90) | class TestWaitForFunction(BaseTestCase):
    method test_wait_for_expression (line 92) | async def test_wait_for_expression(self):
    method test_wait_for_function (line 100) | async def test_wait_for_function(self):
    method test_wait_for_function_args (line 108) | async def test_wait_for_function_args(self):
    method test_before_execution_context_resolved (line 116) | async def test_before_execution_context_resolved(self):
    method test_poll_on_interval (line 125) | async def test_poll_on_interval(self):
    method test_poll_on_mutation (line 144) | async def test_poll_on_mutation(self):
    method test_poll_on_raf (line 161) | async def test_poll_on_raf(self):
    method test_csp (line 175) | async def test_csp(self):
    method test_bad_polling_value (line 185) | async def test_bad_polling_value(self):
    method test_negative_polling_value (line 191) | async def test_negative_polling_value(self):
    method test_wait_for_function_return_value (line 198) | async def test_wait_for_function_return_value(self):
    method test_wait_for_function_window (line 203) | async def test_wait_for_function_window(self):
    method test_wait_for_function_arg_element (line 207) | async def test_wait_for_function_arg_element(self):
    method test_respect_timeout (line 220) | async def test_respect_timeout(self):
    method test_disable_timeout (line 229) | async def test_disable_timeout(self):
  class TestWaitForSelector (line 243) | class TestWaitForSelector(BaseTestCase):
    method test_wait_for_selector_immediate (line 245) | async def test_wait_for_selector_immediate(self):
    method test_wait_for_selector_after_node_appear (line 261) | async def test_wait_for_selector_after_node_appear(self):
    method test_wait_for_selector_inner_html (line 278) | async def test_wait_for_selector_inner_html(self):
    method test_shortcut_for_main_frame (line 285) | async def test_shortcut_for_main_frame(self):
    method test_run_in_specified_frame (line 298) | async def test_run_in_specified_frame(self):
    method test_wait_for_selector_fail (line 313) | async def test_wait_for_selector_fail(self):
    method test_wait_for_page_navigation (line 319) | async def test_wait_for_page_navigation(self):
    method test_fail_page_closed (line 326) | async def test_fail_page_closed(self):
    method test_fail_frame_detached (line 336) | async def test_fail_frame_detached(self):
    method test_cross_process_navigation (line 345) | async def test_cross_process_navigation(self):
    method test_wait_for_selector_visible (line 359) | async def test_wait_for_selector_visible(self):
    method test_wait_for_selector_visible_inner (line 377) | async def test_wait_for_selector_visible_inner(self):
    method test_wait_for_selector_hidden (line 396) | async def test_wait_for_selector_hidden(self):
    method test_wait_for_selector_display_none (line 409) | async def test_wait_for_selector_display_none(self):
    method test_wait_for_selector_remove (line 422) | async def test_wait_for_selector_remove(self):
    method test_wait_for_selector_timeout (line 435) | async def test_wait_for_selector_timeout(self):
    method test_error_msg_wait_for_hidden (line 444) | async def test_error_msg_wait_for_hidden(self):
    method test_wait_for_selector_node_mutation (line 454) | async def test_wait_for_selector_node_mutation(self):
    method test_wait_for_selector_return_element (line 467) | async def test_wait_for_selector_return_element(self):
  class TestWaitForXPath (line 476) | class TestWaitForXPath(BaseTestCase):
    method test_fancy_xpath (line 478) | async def test_fancy_xpath(self):
    method test_timeout (line 487) | async def test_timeout(self):
    method test_specified_frame (line 496) | async def test_specified_frame(self):
    method test_evaluation_failed (line 510) | async def test_evaluation_failed(self):
    method test_frame_detached (line 519) | async def test_frame_detached(self):
    method test_hidden (line 529) | async def test_hidden(self):
    method test_return_element_handle (line 541) | async def test_return_element_handle(self):
    method test_text_node (line 550) | async def test_text_node(self):
    method test_single_slash (line 559) | async def test_single_slash(self):
  class TestFrames (line 568) | class TestFrames(BaseTestCase):
    method test_frame_nested (line 570) | async def test_frame_nested(self):
    method test_frame_events (line 588) | async def test_frame_events(self):
    method test_anchor_url (line 609) | async def test_anchor_url(self):
    method test_frame_cross_process (line 618) | async def test_frame_cross_process(self):
    method test_frame_events_main (line 625) | async def test_frame_events_main(self):
    method test_frame_events_child (line 637) | async def test_frame_events_child(self):
    method test_frame_name (line 658) | async def test_frame_name(self):
    method test_frame_parent (line 680) | async def test_frame_parent(self):

FILE: tests/test_input.py
  class TestClick (line 17) | class TestClick(BaseTestCase):
    method test_click (line 30) | async def test_click(self):
    method test_click_with_disabled_javascript (line 36) | async def test_click_with_disabled_javascript(self):
    method test_click_offscreen_button (line 49) | async def test_click_offscreen_button(self):
    method test_click_wrapped_links (line 71) | async def test_click_wrapped_links(self):
    method test_click_events (line 81) | async def test_click_events(self):
    method test_click_label (line 101) | async def test_click_label(self):
    method test_click_fail (line 116) | async def test_click_fail(self):
    method test_touch_enabled_viewport (line 126) | async def test_touch_enabled_viewport(self):
    method test_click_after_navigation (line 140) | async def test_click_after_navigation(self):
    method test_resize_textarea (line 148) | async def test_resize_textarea(self):
    method test_scroll_and_click (line 165) | async def test_scroll_and_click(self):
    method test_double_click (line 175) | async def test_double_click(self):
    method test_click_partially_obscured_button (line 190) | async def test_click_partially_obscured_button(self):
    method test_select_text_by_mouse (line 202) | async def test_select_text_by_mouse(self):
    method test_select_text_by_triple_click (line 220) | async def test_select_text_by_triple_click(self):
    method test_trigger_hover (line 232) | async def test_trigger_hover(self):
    method test_right_click (line 245) | async def test_right_click(self):
    method test_click_with_modifier_key (line 252) | async def test_click_with_modifier_key(self):
    method test_click_link (line 273) | async def test_click_link(self):
    method test_mouse_movement (line 279) | async def test_mouse_movement(self):
    method test_tap_button (line 297) | async def test_tap_button(self):
    method test_touches_report (line 304) | async def test_touches_report(self):
    method test_click_insilde_frame (line 312) | async def test_click_insilde_frame(self):
    method test_click_with_device_scale_factor (line 324) | async def test_click_with_device_scale_factor(self):
  class TestFileUpload (line 339) | class TestFileUpload(BaseTestCase):
    method test_file_upload (line 345) | async def test_file_upload(self):
  class TestType (line 365) | class TestType(BaseTestCase):
    method test_key_type (line 367) | async def test_key_type(self):
    method test_key_arrowkey (line 380) | async def test_key_arrowkey(self):
    method test_key_press_element_handle (line 402) | async def test_key_press_element_handle(self):
    method test_key_send_char (line 418) | async def test_key_send_char(self):
    method test_repeat_shift_key (line 437) | async def test_repeat_shift_key(self):
    method test_repeat_multiple_modifiers (line 472) | async def test_repeat_multiple_modifiers(self):
    method test_send_proper_code_while_typing (line 507) | async def test_send_proper_code_while_typing(self):
    method test_send_proper_code_while_typing_with_shift (line 525) | async def test_send_proper_code_while_typing_with_shift(self):
    method test_not_type_prevent_events (line 539) | async def test_not_type_prevent_events(self):
    method test_key_modifiers (line 555) | async def test_key_modifiers(self):
    method test_repeat_properly (line 568) | async def test_repeat_properly(self):
    method test_key_type_long (line 590) | async def test_key_type_long(self):
    method test_key_location (line 603) | async def test_key_location(self):
    method test_key_unknown (line 623) | async def test_key_unknown(self):
    method test_emoji (line 632) | async def test_emoji(self):
    method test_emoji_in_iframe (line 641) | async def test_emoji_in_iframe(self):

FILE: tests/test_launcher.py
  class TestLauncher (line 30) | class TestLauncher(unittest.TestCase):
    method setUp (line 31) | def setUp(self):
    method check_default_args (line 40) | def check_default_args(self, launcher):
    method test_no_option (line 46) | def test_no_option(self):
    method test_disable_headless (line 51) | def test_disable_headless(self):
    method test_disable_default_args (line 56) | def test_disable_default_args(self):
    method test_executable (line 63) | def test_executable(self):
    method test_args (line 67) | def test_args(self):
    method test_filter_ignore_default_args (line 72) | def test_filter_ignore_default_args(self):
    method test_user_data_dir (line 84) | def test_user_data_dir(self):
    method test_close_no_connection (line 92) | async def test_close_no_connection(self):
    method test_launch (line 97) | async def test_launch(self):
    method test_ignore_https_errors (line 104) | async def test_ignore_https_errors(self):
    method test_ignore_https_errors_interception (line 117) | async def test_ignore_https_errors_interception(self):
    method test_await_after_close (line 132) | async def test_await_after_close(self):
    method test_invalid_executable_path (line 141) | async def test_invalid_executable_path(self):
    method test_dumpio_default (line 146) | def test_dumpio_default(self):
    method test_dumpio_enable (line 158) | def test_dumpio_enable(self):
    method test_default_viewport (line 171) | async def test_default_viewport(self):
    method test_disable_default_viewport (line 184) | async def test_disable_default_viewport(self):
  class TestDefaultURL (line 193) | class TestDefaultURL(unittest.TestCase):
    method test_default_url (line 195) | async def test_default_url(self):
    method test_default_url_not_headless (line 206) | async def test_default_url_not_headless(self):
    method test_custom_url (line 218) | async def test_custom_url(self):
  class TestMixedContent (line 231) | class TestMixedContent(unittest.TestCase):
    method test_mixed_content (line 234) | async def test_mixed_content(self) -> None:
  class TestLogLevel (line 244) | class TestLogLevel(unittest.TestCase):
    method setUp (line 245) | def setUp(self):
    method tearDown (line 251) | def tearDown(self):
    method test_level_default (line 256) | async def test_level_default(self):
    method test_level_info (line 267) | async def test_level_info(self):
    method test_level_debug (line 279) | async def test_level_debug(self):
    method test_connect_debug (line 298) | async def test_connect_debug(self):
  class TestUserDataDir (line 317) | class TestUserDataDir(unittest.TestCase):
    method setUpClass (line 319) | def setUpClass(cls):
    method setUp (line 326) | def setUp(self):
    method tearDown (line 329) | def tearDown(self):
    method tearDownClass (line 341) | def tearDownClass(cls):
    method test_user_data_dir_option (line 346) | async def test_user_data_dir_option(self):
    method test_user_data_dir_args (line 356) | async def test_user_data_dir_args(self):
    method test_user_data_dir_restore_state (line 367) | async def test_user_data_dir_restore_state(self):
    method test_user_data_dir_restore_cookie_in_browser (line 383) | async def test_user_data_dir_restore_cookie_in_browser(self):
  class TestTargetEvents (line 399) | class TestTargetEvents(unittest.TestCase):
    method setUpClass (line 401) | def setUpClass(cls):
    method tearDownClass (line 409) | def tearDownClass(cls):
    method test_target_events (line 413) | async def test_target_events(self):
  class TestClose (line 426) | class TestClose(unittest.TestCase):
    method test_close (line 428) | async def test_close(self):
  class TestEventLoop (line 443) | class TestEventLoop(unittest.TestCase):
    method test_event_loop (line 444) | def test_event_loop(self):
  class TestConnect (line 459) | class TestConnect(unittest.TestCase):
    method test_connect (line 461) | async def test_connect(self):
    method test_reconnect (line 473) | async def test_reconnect(self):
    method test_fail_to_connect_closed_chrome (line 485) | async def test_fail_to_connect_closed_chrome(self):
    method test_executable_path (line 493) | async def test_executable_path(self):

FILE: tests/test_misc.py
  class TestVersion (line 12) | class TestVersion(unittest.TestCase):
    method test_version (line 13) | def test_version(self):
  class TestDefaultArgs (line 21) | class TestDefaultArgs(unittest.TestCase):
    method test_default_args (line 22) | def test_default_args(self):
  class TestToInches (line 29) | class TestToInches(unittest.TestCase):
    method test_px (line 30) | def test_px(self):
    method test_inch (line 36) | def test_inch(self):
    method test_cm (line 42) | def test_cm(self):
    method test_mm (line 48) | def test_mm(self):
  class TestPositiveInt (line 55) | class TestPositiveInt(unittest.TestCase):
    method test_badtype (line 56) | def test_badtype(self):
    method test_negative_int (line 60) | def test_negative_int(self):
  class TestDebugError (line 65) | class TestDebugError(unittest.TestCase):
    method setUp (line 66) | def setUp(self):
    method tearDown (line 70) | def tearDown(self):
    method test_debug_default (line 73) | def test_debug_default(self):
    method test_debug_enabled (line 80) | def test_debug_enabled(self):
    method test_debug_enable_disable (line 85) | def test_debug_enable_disable(self):
    method test_debug_logger (line 96) | def test_debug_logger(self):

FILE: tests/test_network.py
  class TestNetworkEvent (line 16) | class TestNetworkEvent(BaseTestCase):
    method test_request (line 18) | async def test_request(self):
    method test_request_post (line 32) | async def test_request_post(self):
    method test_response (line 51) | async def test_response(self):
    method test_response_https (line 66) | async def test_response_https(self):
    method test_from_cache (line 82) | async def test_from_cache(self):
    method test_response_from_service_worker (line 101) | async def test_response_from_service_worker(self):
    method test_response_body (line 125) | async def test_response_body(self):
    method test_fail_get_redirected_body (line 136) | async def test_fail_get_redirected_body(self):
    method test_not_report_body_unless_finished (line 151) | async def test_not_report_body_unless_finished(self):
    method test_request_failed (line 182) | async def test_request_failed(self):
    method test_request_finished (line 206) | async def test_request_finished(self):
    method test_events_order (line 219) | async def test_events_order(self):
    method test_redirects (line 229) | async def test_redirects(self):
  class TestRequestInterception (line 255) | class TestRequestInterception(BaseTestCase):
    method test_request_interception (line 257) | async def test_request_interception(self):
    method test_referer_header (line 277) | async def test_referer_header(self):
    method test_response_with_cookie (line 292) | async def test_response_with_cookie(self):
    method test_request_interception_stop (line 306) | async def test_request_interception_stop(self):
    method test_request_interception_custom_header (line 315) | async def test_request_interception_custom_header(self):
    method test_request_interception_custom_referer_header (line 329) | async def test_request_interception_custom_referer_header(self):
    method test_request_interception_abort (line 344) | async def test_request_interception_abort(self):
    method test_request_interception_custom_error_code (line 363) | async def test_request_interception_custom_error_code(self):
    method test_request_interception_amend_http_header (line 384) | async def test_request_interception_amend_http_header(self):
    method test_request_interception_abort_main (line 388) | async def test_request_interception_abort_main(self):
    method test_request_interception_redirects (line 401) | async def test_request_interception_redirects(self):
    method test_redirect_for_subresource (line 414) | async def test_redirect_for_subresource(self):
    method test_request_interception_abort_redirects (line 438) | async def test_request_interception_abort_redirects(self):
    method test_request_interception_equal_requests (line 443) | async def test_request_interception_equal_requests(self):
    method test_request_interception_data_url (line 447) | async def test_request_interception_data_url(self):
    method test_request_interception_abort_data_url (line 463) | async def test_request_interception_abort_data_url(self):
    method test_request_interception_with_hash (line 476) | async def test_request_interception_with_hash(self):
    method test_request_interception_encoded_server (line 492) | async def test_request_interception_encoded_server(self):
    method test_request_interception_badly_encoded_server (line 504) | async def test_request_interception_badly_encoded_server(self):
    method test_request_interception_encoded_server_2 (line 509) | async def test_request_interception_encoded_server_2(self):
    method test_request_interception_invalid_interception_id (line 514) | async def test_request_interception_invalid_interception_id(self):
    method test_request_interception_disabled (line 518) | async def test_request_interception_disabled(self):
    method test_request_interception_with_file_url (line 534) | async def test_request_interception_with_file_url(self):
    method test_request_respond (line 558) | async def test_request_respond(self):
    method test_request_respond_bytes (line 578) | async def test_request_respond_bytes(self):
  class TestNavigationRequest (line 582) | class TestNavigationRequest(BaseTestCase):
    method test_navigation_request (line 584) | async def test_navigation_request(self):
    method test_interception (line 599) | async def test_interception(self):
    method test_image (line 618) | async def test_image(self):

FILE: tests/test_page.py
  class TestEvaluate (line 35) | class TestEvaluate(BaseTestCase):
    method test_evaluate (line 37) | async def test_evaluate(self):
    method test_await_promise (line 42) | async def test_await_promise(self):
    method test_error_on_reload (line 47) | async def test_error_on_reload(self):
    method test_after_framenavigation (line 58) | async def test_after_framenavigation(self):
    method test_inside_expose_function (line 74) | async def test_inside_expose_function(self):
    method test_promise_reject (line 89) | async def test_promise_reject(self):
    method test_string_as_error_message (line 95) | async def test_string_as_error_message(self):
    method test_number_as_error_message (line 101) | async def test_number_as_error_message(self):
    method test_return_complex_object (line 107) | async def test_return_complex_object(self):
    method test_return_nan (line 114) | async def test_return_nan(self):
    method test_return_minus_zero (line 119) | async def test_return_minus_zero(self):
    method test_return_infinity (line 124) | async def test_return_infinity(self):
    method test_return_infinity_minus (line 129) | async def test_return_infinity_minus(self):
    method test_accept_none (line 134) | async def test_accept_none(self):
    method test_serialize_null_field (line 142) | async def test_serialize_null_field(self):
    method test_fail_window_object (line 147) | async def test_fail_window_object(self):
    method test_fail_for_circular_object (line 152) | async def test_fail_for_circular_object(self):
    method test_accept_string (line 162) | async def test_accept_string(self):
    method test_evaluate_force_expression (line 167) | async def test_evaluate_force_expression(self):
    method test_accept_string_with_semicolon (line 173) | async def test_accept_string_with_semicolon(self):
    method test_accept_string_with_comments (line 178) | async def test_accept_string_with_comments(self):
    method test_element_handle_as_argument (line 183) | async def test_element_handle_as_argument(self):
    method test_element_handle_disposed (line 190) | async def test_element_handle_disposed(self):
    method test_element_handle_from_other_frame (line 200) | async def test_element_handle_from_other_frame(self):
    method test_object_handle_as_argument (line 211) | async def test_object_handle_as_argument(self):
    method test_object_handle_to_primitive_value (line 218) | async def test_object_handle_to_primitive_value(self):
    method test_simulate_user_gesture (line 224) | async def test_simulate_user_gesture(self):
    method test_nice_error_after_navigation (line 234) | async def test_nice_error_after_navigation(self):
  class TestOfflineMode (line 247) | class TestOfflineMode(BaseTestCase):
    method test_offline_mode (line 249) | async def test_offline_mode(self):
    method test_emulate_navigator_offline (line 258) | async def test_emulate_navigator_offline(self):
  class TestEvaluateHandle (line 266) | class TestEvaluateHandle(BaseTestCase):
    method test_evaluate_handle (line 268) | async def test_evaluate_handle(self):
  class TestWaitFor (line 273) | class TestWaitFor(BaseTestCase):
    method test_wait_for_selector (line 275) | async def test_wait_for_selector(self):
    method test_wait_for_xpath (line 285) | async def test_wait_for_xpath(self):
    method test_single_slash_fail (line 295) | async def test_single_slash_fail(self):
    method test_wait_for_timeout (line 301) | async def test_wait_for_timeout(self):
    method test_wait_for_error_type (line 310) | async def test_wait_for_error_type(self):
    method test_wait_for_func_with_args (line 316) | async def test_wait_for_func_with_args(self):
  class TestConsole (line 320) | class TestConsole(BaseTestCase):
    method test_console_event (line 322) | async def test_console_event(self):
    method test_console_event_many (line 337) | async def test_console_event_many(self):
    method test_console_window (line 365) | async def test_console_window(self):
    method test_trigger_correct_log (line 375) | async def test_trigger_correct_log(self):
  class TestDOMContentLoaded (line 388) | class TestDOMContentLoaded(BaseTestCase):
    method test_fired (line 390) | async def test_fired(self):
  class TestMetrics (line 395) | class TestMetrics(BaseTestCase):
    method checkMetrics (line 396) | def checkMetrics(self, metrics):
    method test_metrics (line 419) | async def test_metrics(self):
    method test_metrics_event (line 425) | async def test_metrics_event(self):
  class TestGoto (line 434) | class TestGoto(BaseTestCase):
    method test_get_http (line 436) | async def test_get_http(self):
    method test_goto_blank (line 442) | async def test_goto_blank(self):
    method test_response_when_page_changes_url (line 447) | async def test_response_when_page_changes_url(self):
    method test_goto_subframe_204 (line 453) | async def test_goto_subframe_204(self):
    method test_goto_fail_204 (line 457) | async def test_goto_fail_204(self):
    method test_goto_documentloaded (line 463) | async def test_goto_documentloaded(self):
    method test_goto_domcontentloaded (line 471) | async def test_goto_domcontentloaded(self):
    method test_goto_history_api_beforeunload (line 478) | async def test_goto_history_api_beforeunload(self):
    method test_goto_networkidle (line 492) | async def test_goto_networkidle(self):
    method test_nav_networkidle0 (line 497) | async def test_nav_networkidle0(self):
    method test_nav_networkidle2 (line 503) | async def test_nav_networkidle2(self):
    method test_goto_bad_url (line 509) | async def test_goto_bad_url(self):
    method test_goto_bad_resource (line 514) | async def test_goto_bad_resource(self):
    method test_timeout (line 519) | async def test_timeout(self):
    method test_timeout_default (line 524) | async def test_timeout_default(self):
    method test_no_timeout (line 530) | async def test_no_timeout(self):
    method test_valid_url (line 534) | async def test_valid_url(self):
    method test_data_url (line 539) | async def test_data_url(self):
    method test_404 (line 544) | async def test_404(self):
    method test_redirect (line 550) | async def test_redirect(self):
    method test_wait_for_network_idle (line 557) | async def test_wait_for_network_idle(self):
    method test_data_url_request (line 561) | async def test_data_url_request(self):
    method test_url_with_hash (line 572) | async def test_url_with_hash(self):
    method test_self_request_page (line 582) | async def test_self_request_page(self):
    method test_show_url_in_error_message (line 588) | async def test_show_url_in_error_message(self):
  class TestWaitForNavigation (line 596) | class TestWaitForNavigation(BaseTestCase):
    method test_wait_for_navigatoin (line 598) | async def test_wait_for_navigatoin(self):
    method test_both_domcontentloaded_loaded (line 610) | async def test_both_domcontentloaded_loaded(self):
    method test_click_anchor_link (line 614) | async def test_click_anchor_link(self):
    method test_return_nevigated_response_reload (line 625) | async def test_return_nevigated_response_reload(self):
    method test_history_push_state (line 633) | async def test_history_push_state(self):
    method test_history_replace_state (line 649) | async def test_history_replace_state(self):
    method test_dom_history_back_forward (line 667) | async def test_dom_history_back_forward(self):
    method test_subframe_issues (line 695) | async def test_subframe_issues(self):
  class TestWaitForRequest (line 710) | class TestWaitForRequest(BaseTestCase):
    method test_wait_for_request (line 712) | async def test_wait_for_request(self):
    method test_predicate (line 726) | async def test_predicate(self):
    method test_no_timeout (line 744) | async def test_no_timeout(self):
  class TestWaitForResponse (line 761) | class TestWaitForResponse(BaseTestCase):
    method test_wait_for_response (line 763) | async def test_wait_for_response(self):
    method test_predicate (line 777) | async def test_predicate(self):
    method test_no_timeout (line 795) | async def test_no_timeout(self):
  class TestGoBack (line 812) | class TestGoBack(BaseTestCase):
    method test_back (line 814) | async def test_back(self):
    method test_history_api (line 830) | async def test_history_api(self):
  class TestExposeFunction (line 846) | class TestExposeFunction(BaseTestCase):
    method test_expose_function (line 848) | async def test_expose_function(self):
    method test_call_from_evaluate_on_document (line 855) | async def test_call_from_evaluate_on_document(self):
    method test_expose_function_other_page (line 868) | async def test_expose_function_other_page(self):
    method test_expose_function_return_promise (line 876) | async def test_expose_function_return_promise(self):
    method test_expose_function_frames (line 885) | async def test_expose_function_frames(self):
    method test_expose_function_frames_before_navigation (line 893) | async def test_expose_function_frames_before_navigation(self):
  class TestErrorPage (line 901) | class TestErrorPage(BaseTestCase):
    method test_error_page (line 903) | async def test_error_page(self):
  class TestRequest (line 916) | class TestRequest(BaseTestCase):
    method test_request (line 918) | async def test_request(self):
  class TestQuerySelector (line 932) | class TestQuerySelector(BaseTestCase):
    method test_jeval (line 934) | async def test_jeval(self):
    method test_jeval_argument (line 941) | async def test_jeval_argument(self):
    method test_jeval_argument_element (line 948) | async def test_jeval_argument_element(self):
    method test_jeval_not_found (line 959) | async def test_jeval_not_found(self):
    method test_JJeval (line 969) | async def test_JJeval(self):
    method test_query_selector (line 976) | async def test_query_selector(self):
    method test_query_selector_all (line 983) | async def test_query_selector_all(self):
    method test_query_selector_all_not_found (line 993) | async def test_query_selector_all_not_found(self):
    method test_xpath (line 999) | async def test_xpath(self):
    method test_xpath_alias (line 1005) | async def test_xpath_alias(self):
    method test_xpath_not_found (line 1011) | async def test_xpath_not_found(self):
    method test_xpath_multiple (line 1016) | async def test_xpath_multiple(self):
  class TestUserAgent (line 1022) | class TestUserAgent(BaseTestCase):
    method test_user_agent (line 1024) | async def test_user_agent(self):
    method test_user_agent_mobile_emulate (line 1033) | async def test_user_agent_mobile_emulate(self):
  class TestExtraHTTPHeader (line 1042) | class TestExtraHTTPHeader(BaseTestCase):
    method test_extra_http_header (line 1044) | async def test_extra_http_header(self):
    method test_non_string_value (line 1061) | async def test_non_string_value(self):
  class TestAuthenticate (line 1068) | class TestAuthenticate(BaseTestCase):
    method test_auth (line 1070) | async def test_auth(self):
  class TestAuthenticateFailed (line 1078) | class TestAuthenticateFailed(BaseTestCase):
    method test_auth_fail (line 1080) | async def test_auth_fail(self):
  class TestAuthenticateDisable (line 1086) | class TestAuthenticateDisable(BaseTestCase):
    method test_disable_auth (line 1088) | async def test_disable_auth(self):
  class TestSetContent (line 1098) | class TestSetContent(BaseTestCase):
    method test_set_content (line 1102) | async def test_set_content(self):
    method test_with_doctype (line 1108) | async def test_with_doctype(self):
    method test_with_html4_doctype (line 1115) | async def test_with_html4_doctype(self):
  class TestSetBypassCSP (line 1123) | class TestSetBypassCSP(BaseTestCase):
    method test_bypass_csp_meta_tag (line 1125) | async def test_bypass_csp_meta_tag(self):
    method test_bypass_csp_header (line 1137) | async def test_bypass_csp_header(self):
    method test_bypass_scp_cross_process (line 1149) | async def test_bypass_scp_cross_process(self):
  class TestAddScriptTag (line 1161) | class TestAddScriptTag(BaseTestCase):
    method test_script_tag_error (line 1163) | async def test_script_tag_error(self):
    method test_script_tag_url (line 1169) | async def test_script_tag_url(self):
    method test_script_tag_url_fail (line 1177) | async def test_script_tag_url_fail(self):
    method test_script_tag_path (line 1185) | async def test_script_tag_path(self):
    method test_script_tag_path_source_map (line 1194) | async def test_script_tag_path_source_map(self):
    method test_script_tag_content (line 1203) | async def test_script_tag_content(self):
    method test_scp_error_content (line 1211) | async def test_scp_error_content(self):
    method test_scp_error_url (line 1217) | async def test_scp_error_url(self):
    method test_module_url (line 1225) | async def test_module_url(self):
    method test_module_path (line 1232) | async def test_module_path(self):
    method test_module_content (line 1241) | async def test_module_content(self):
  class TestAddStyleTag (line 1252) | class TestAddStyleTag(BaseTestCase):
    method test_style_tag_error (line 1254) | async def test_style_tag_error(self):
    method get_bgcolor (line 1259) | async def get_bgcolor(self):
    method test_style_tag_url (line 1263) | async def test_style_tag_url(self):
    method test_style_tag_url_fail (line 1271) | async def test_style_tag_url_fail(self):
    method test_style_tag_path (line 1279) | async def test_style_tag_path(self):
    method test_style_tag_path_source_map (line 1289) | async def test_style_tag_path_source_map(self):
    method test_style_tag_content (line 1300) | async def test_style_tag_content(self):
    method test_csp_error_content (line 1308) | async def test_csp_error_content(self):
    method test_csp_error_url (line 1315) | async def test_csp_error_url(self):
  class TestUrl (line 1323) | class TestUrl(BaseTestCase):
    method test_url (line 1325) | async def test_url(self):
  class TestViewport (line 1332) | class TestViewport(BaseTestCase):
    method test_viewport (line 1336) | async def test_viewport(self):
    method test_mobile_emulation (line 1342) | async def test_mobile_emulation(self):
    method test_touch_emulation (line 1351) | async def test_touch_emulation(self):
    method test_detect_by_modernizr (line 1376) | async def test_detect_by_modernizr(self):
    method test_detect_touch_viewport_touch (line 1389) | async def test_detect_touch_viewport_touch(self):
    method test_landscape_emulation (line 1395) | async def test_landscape_emulation(self):
  class TestEmulate (line 1415) | class TestEmulate(BaseTestCase):
    method test_emulate (line 1417) | async def test_emulate(self):
    method test_click (line 1425) | async def test_click(self):
  class TestEmulateMedia (line 1435) | class TestEmulateMedia(BaseTestCase):
    method test_emulate_media (line 1437) | async def test_emulate_media(self):
    method test_emulate_media_bad_arg (line 1454) | async def test_emulate_media_bad_arg(self):
  class TestJavaScriptEnabled (line 1460) | class TestJavaScriptEnabled(BaseTestCase):
    method test_set_javascript_enabled (line 1462) | async def test_set_javascript_enabled(self):
  class TestEvaluateOnNewDocument (line 1476) | class TestEvaluateOnNewDocument(BaseTestCase):
    method test_evaluate_before_else_on_page (line 1478) | async def test_evaluate_before_else_on_page(self):
    method test_csp (line 1484) | async def test_csp(self):
  class TestCacheEnabled (line 1493) | class TestCacheEnabled(BaseTestCase):
    method test_cache_enable_disable (line 1495) | async def test_cache_enable_disable(self):
  class TestPDF (line 1512) | class TestPDF(BaseTestCase):
    method test_pdf (line 1514) | async def test_pdf(self):
  class TestTitle (line 1524) | class TestTitle(BaseTestCase):
    method test_title (line 1526) | async def test_title(self):
  class TestSelect (line 1531) | class TestSelect(BaseTestCase):
    method setUp (line 1532) | def setUp(self):
    method test_select (line 1537) | async def test_select(self):
    method test_select_first_item (line 1551) | async def test_select_first_item(self):
    method test_select_multiple (line 1557) | async def test_select_multiple(self):
    method test_select_not_select_element (line 1567) | async def test_select_not_select_element(self):
    method test_select_no_match (line 1572) | async def test_select_no_match(self):
    method test_return_selected_elements (line 1577) | async def test_return_selected_elements(self):
    method test_select_not_multiple (line 1584) | async def test_select_not_multiple(self):
    method test_select_no_value (line 1589) | async def test_select_no_value(self):
    method test_select_deselect (line 1594) | async def test_select_deselect(self):
    method test_select_deselect_multiple (line 1604) | async def test_select_deselect_multiple(self):
    method test_select_nonstring (line 1615) | async def test_select_nonstring(self):
  class TestCookie (line 1620) | class TestCookie(BaseTestCase):
    method test_cookies (line 1622) | async def test_cookies(self):
    method test_cookie_blank_page (line 1714) | async def test_cookie_blank_page(self):
    method test_cookie_blank_page2 (line 1720) | async def test_cookie_blank_page2(self):
    method test_cookie_data_url_page (line 1730) | async def test_cookie_data_url_page(self):
    method test_cookie_data_url_page2 (line 1736) | async def test_cookie_data_url_page2(self):
  class TestCookieWithPath (line 1746) | class TestCookieWithPath(BaseTestCase):
    method test_set_cookie_with_path (line 1748) | async def test_set_cookie_with_path(self):
  class TestCookieDelete (line 1768) | class TestCookieDelete(BaseTestCase):
    method test_delete_cookie (line 1770) | async def test_delete_cookie(self):
  class TestCookieDomain (line 1793) | class TestCookieDomain(BaseTestCase):
    method test_different_domain (line 1795) | async def test_different_domain(self):
  class TestCookieFrames (line 1817) | class TestCookieFrames(BaseTestCase):
    method test_frame (line 1819) | async def test_frame(self):
  class TestEvents (line 1874) | class TestEvents(BaseTestCase):
    method test_close_window_close (line 1876) | async def test_close_window_close(self):
    method test_close_page_close (line 1898) | async def test_close_page_close(self):
  class TestBrowser (line 1906) | class TestBrowser(BaseTestCase):
    method test_get_browser (line 1908) | async def test_get_browser(self):

FILE: tests/test_pyppeteer.py
  class TestPyppeteer (line 20) | class TestPyppeteer(BaseTestCase):
    method test_get_https (line 22) | async def test_get_https(self):
    method test_get_facebook (line 27) | async def test_get_facebook(self):
    method test_plain_text_depr (line 32) | async def test_plain_text_depr(self):
    method test_inject_file (line 40) | async def test_inject_file(self):  # deprecated
  class TestScreenshot (line 54) | class TestScreenshot(BaseTestCase):
    method setUp (line 55) | def setUp(self):
    method tearDown (line 61) | def tearDown(self):
    method test_screenshot_large (line 67) | async def test_screenshot_large(self):

FILE: tests/test_screenshot.py
  class TestScreenShot (line 17) | class TestScreenShot(TestCase):
    method setUp (line 18) | def setUp(self):
    method tearDown (line 24) | def tearDown(self):
    method test_screenshot (line 30) | async def test_screenshot(self):
    method test_screenshot_binary (line 45) | async def test_screenshot_binary(self):
    method test_screenshot_base64 (line 54) | async def test_screenshot_base64(self):
    method test_screenshot_element (line 64) | async def test_screenshot_element(self):
    method test_unresolved_mimetype (line 74) | async def test_unresolved_mimetype(self):
  class TestPDF (line 82) | class TestPDF(TestCase):
    method setUp (line 83) | def setUp(self):
    method test_pdf (line 90) | async def test_pdf(self):
    method tearDown (line 98) | def tearDown(self):

FILE: tests/test_target.py
  class TestTarget (line 12) | class TestTarget(BaseTestCase):
    method test_targets (line 14) | async def test_targets(self):
    method test_return_all_pages (line 23) | async def test_return_all_pages(self):
    method test_browser_target (line 29) | async def test_browser_target(self):
    method test_default_page (line 35) | async def test_default_page(self):
    method test_report_new_page (line 43) | async def test_report_new_page(self):
    method test_report_service_worker (line 78) | async def test_report_service_worker(self):
    method test_url_change (line 99) | async def test_url_change(self):
    method test_not_report_uninitialized_page (line 118) | async def test_not_report_uninitialized_page(self):
    method test_crash_while_redirect (line 152) | async def test_crash_while_redirect(self):
    method test_opener (line 156) | async def test_opener(self):

FILE: tests/test_tracing.py
  class TestTracing (line 15) | class TestTracing(BaseTestCase):
    method setUp (line 16) | def setUp(self):
    method tearDown (line 22) | def tearDown(self):
    method test_tracing (line 28) | async def test_tracing(self):
    method test_custom_categories (line 37) | async def test_custom_categories(self):
    method test_tracing_two_page_error (line 52) | async def test_tracing_two_page_error(self):
    method test_return_buffer (line 61) | async def test_return_buffer(self):
    method test_return_null_on_error (line 71) | async def test_return_null_on_error(self):
    method test_without_path (line 76) | async def test_without_path(self):

FILE: tests/test_worker.py
  class TestWorker (line 11) | class TestWorker(BaseTestCase):
    method test_worker (line 13) | async def test_worker(self):
    method test_create_destroy_events (line 25) | async def test_create_destroy_events(self):
    method test_report_console_logs (line 40) | async def test_report_console_logs(self):
    method test_jshandle_for_console_log (line 50) | async def test_jshandle_for_console_log(self):
    method test_execution_context (line 64) | async def test_execution_context(self):
    method test_report_error (line 76) | async def test_report_error(self):

FILE: tests/utils.py
  function waitEvent (line 7) | def waitEvent(emitter, event_name):
Condensed preview — 143 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (631K chars).
[
  {
    "path": ".circleci/config.yml",
    "chars": 2929,
    "preview": "version: 2.1\n\norbs:\n  codecov: codecov/codecov@1.0.5\n\nworkflows:\n  main:\n    jobs:\n      - lint\n      - mypy\n      - tes"
  },
  {
    "path": ".coveragerc",
    "chars": 43,
    "preview": "[run]\nomit=setup.py\nsource=pyppeteer,tests\n"
  },
  {
    "path": ".gitignore",
    "chars": 1027,
    "preview": "# Byte-compiled / optimized / DLL files\n__pycache__/\n*.py[cod]\n*$py.class\n\n# C extensions\n*.so\n\n# Distribution / packagi"
  },
  {
    "path": ".noserc",
    "chars": 104,
    "preview": "[nosetests]\nlogging-level=INFO\n# no-path-adjustment=true\n# with-coverage=true\n# cover-package=pyppeteer\n"
  },
  {
    "path": ".pre-commit-config.yaml",
    "chars": 876,
    "preview": "repos:\n  - repo: https://github.com/pre-commit/pre-commit-hooks\n    rev: v2.4.0\n    hooks:\n      - id: trailing-whitespa"
  },
  {
    "path": "CHANGELOG.md",
    "chars": 9959,
    "preview": "History\n=======\n\n## Version 2.0.0\n\n* Bump pyee version, which removes support for Python 3.7\n* Bumped included browser v"
  },
  {
    "path": "CONTRIBUTING.md",
    "chars": 897,
    "preview": "# Contribution guidelines\n\nContributions are welcome as long as they follow core rule of the project:\n\nThe API of pyppet"
  },
  {
    "path": "LICENSE",
    "chars": 1154,
    "preview": "\nMIT License\n\nCopyright (c) 2017, Hiroyuki Takagi\n\nPermission is hereby granted, free of charge, to any person obtaining"
  },
  {
    "path": "README.md",
    "chars": 5754,
    "preview": "### Attention: This repo is unmaintained and has been outside of minor changes for a long time. Please consider [playwri"
  },
  {
    "path": "docs/Makefile",
    "chars": 6774,
    "preview": "# Makefile for Sphinx documentation\n#\n\n# You can set these variables from the command line.\nSPHINXOPTS    =\nSPHINXBUILD "
  },
  {
    "path": "docs/_static/custom.css",
    "chars": 421,
    "preview": "h1.logo {\n  font-family: \"Raleway\";\n  font-weight: 500;\n}\n\na.headerlink {\n  color: rgba(0, 0, 0, 0.1);\n}\n\ndiv.sphinxside"
  },
  {
    "path": "docs/_templates/layout.html",
    "chars": 590,
    "preview": "{% extends 'alabaster/layout.html' %}\n{% block extrahead %}\n  <!-- font -->\n  <link href='https://fonts.googleapis.com/c"
  },
  {
    "path": "docs/changes.md",
    "chars": 29,
    "preview": ".. mdinclude:: ../CHANGES.md\n"
  },
  {
    "path": "docs/conf.py",
    "chars": 9509,
    "preview": "#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n#\n# pyppeteer documentation build configuration file, created by\n# sphinx-"
  },
  {
    "path": "docs/index.md",
    "chars": 248,
    "preview": "Pyppeteer's documentation\n=========================\n\n.. mdinclude:: ../README.md\n\n\nContents\n--------\n\n.. toctree::\n   :m"
  },
  {
    "path": "docs/make.bat",
    "chars": 6465,
    "preview": "@ECHO OFF\n\nREM Command file for Sphinx documentation\n\nif \"%SPHINXBUILD%\" == \"\" (\n\tset SPHINXBUILD=sphinx-build\n)\nset BUI"
  },
  {
    "path": "docs/reference.md",
    "chars": 4375,
    "preview": "API Reference\n=============\n\nCommands\n--------\n\n* ``pyppeteer-install``: Download and install chromium for pyppeteer.\n\nE"
  },
  {
    "path": "docs/server.py",
    "chars": 1433,
    "preview": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n\nfrom os import path\nimport subprocess\n\nfrom livereload import Server\nfro"
  },
  {
    "path": "pyppeteer/__init__.py",
    "chars": 794,
    "preview": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n\n\"\"\"Meta data for pyppeteer.\"\"\"\n\nimport logging\nimport os\n\nfrom appdirs i"
  },
  {
    "path": "pyppeteer/browser.py",
    "chars": 13590,
    "preview": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n\n\"\"\"Browser module.\"\"\"\n\nimport logging\nfrom subprocess import Popen\nfrom "
  },
  {
    "path": "pyppeteer/chromium_downloader.py",
    "chars": 5193,
    "preview": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n\n\"\"\"Chromium download module.\"\"\"\n\nimport logging\nimport os\nimport stat\nim"
  },
  {
    "path": "pyppeteer/command.py",
    "chars": 385,
    "preview": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n\n\"\"\"Commands for Pyppeteer.\"\"\"\n\nimport logging\n\nfrom pyppeteer.chromium_d"
  },
  {
    "path": "pyppeteer/connection.py",
    "chars": 11489,
    "preview": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n\n\"\"\"Connection/Session management module.\"\"\"\n\nimport asyncio\nimport json\n"
  },
  {
    "path": "pyppeteer/coverage.py",
    "chars": 13968,
    "preview": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n\n\"\"\"Coverage module.\"\"\"\n\nfrom functools import cmp_to_key\nimport logging\n"
  },
  {
    "path": "pyppeteer/dialog.py",
    "chars": 2264,
    "preview": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n\n\"\"\"Dialog module.\"\"\"\n\nfrom types import SimpleNamespace\n\nfrom pyppeteer."
  },
  {
    "path": "pyppeteer/element_handle.py",
    "chars": 18882,
    "preview": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n\n\"\"\"Element handle module.\"\"\"\n\nimport copy\nimport logging\nimport math\nimp"
  },
  {
    "path": "pyppeteer/emulation_manager.py",
    "chars": 1746,
    "preview": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n\n\"\"\"Emulation Manager module.\"\"\"\n\nfrom pyppeteer import helper\nfrom pyppe"
  },
  {
    "path": "pyppeteer/errors.py",
    "chars": 717,
    "preview": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n\n\"\"\"Exceptions for pyppeteer package.\"\"\"\n\nimport asyncio\n\n\nclass Pyppetee"
  },
  {
    "path": "pyppeteer/execution_context.py",
    "chars": 9305,
    "preview": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n\n\"\"\"Execution Context Module.\"\"\"\n\nimport logging\nimport math\nimport re\nfr"
  },
  {
    "path": "pyppeteer/frame_manager.py",
    "chars": 35843,
    "preview": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n\n\"\"\"Frame Manager module.\"\"\"\n\nimport asyncio\nfrom collections import Orde"
  },
  {
    "path": "pyppeteer/helper.py",
    "chars": 5414,
    "preview": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n\n\"\"\"Helper functions.\"\"\"\n\nimport asyncio\nimport json\nimport logging\nimpor"
  },
  {
    "path": "pyppeteer/input.py",
    "chars": 12998,
    "preview": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n\n\"\"\"Keyboard and Mouse module.\"\"\"\n\nimport asyncio\nfrom typing import Any,"
  },
  {
    "path": "pyppeteer/launcher.py",
    "chars": 17272,
    "preview": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n\n\"\"\"Chromium process launcher module.\"\"\"\n\nimport asyncio\nimport atexit\nfr"
  },
  {
    "path": "pyppeteer/multimap.py",
    "chars": 2207,
    "preview": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n\n\"\"\"Multimap module.\"\"\"\n\nfrom collections import OrderedDict\nfrom typing "
  },
  {
    "path": "pyppeteer/navigator_watcher.py",
    "chars": 5766,
    "preview": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n\n\"\"\"Navigator Watcher module.\"\"\"\n\nimport asyncio\nimport concurrent.future"
  },
  {
    "path": "pyppeteer/network_manager.py",
    "chars": 31837,
    "preview": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n\n\"\"\"Network Manager module.\"\"\"\n\nimport asyncio\nimport base64\nfrom collect"
  },
  {
    "path": "pyppeteer/options.py",
    "chars": 124,
    "preview": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n\n\"\"\"Options module.\"\"\"\n\nfrom argparse import Namespace\n\nconfig = Namespac"
  },
  {
    "path": "pyppeteer/page.py",
    "chars": 68815,
    "preview": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n\n\"\"\"Page module.\"\"\"\n\nimport asyncio\nimport base64\nimport json\nimport logg"
  },
  {
    "path": "pyppeteer/target.py",
    "chars": 4132,
    "preview": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n\n\"\"\"Target module.\"\"\"\n\nimport asyncio\nfrom typing import Any, Callable, C"
  },
  {
    "path": "pyppeteer/tracing.py",
    "chars": 3319,
    "preview": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n\n\"\"\"Tracing module.\"\"\"\n\nfrom pathlib import Path\nfrom typing import Any\n\n"
  },
  {
    "path": "pyppeteer/us_keyboard_layout.py",
    "chars": 16896,
    "preview": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n# flake8: noqa\n\n\"\"\"US Keyboard Definition.\"\"\"\n\nkeyDefinitions = {\n    '0'"
  },
  {
    "path": "pyppeteer/util.py",
    "chars": 856,
    "preview": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n\n\"\"\"Utility functions.\"\"\"\n\nimport gc\nimport socket\nfrom typing import Dic"
  },
  {
    "path": "pyppeteer/worker.py",
    "chars": 3588,
    "preview": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n\n\"\"\"Worker module.\"\"\"\n\nimport logging\nfrom typing import Any, Callable, D"
  },
  {
    "path": "pyproject.toml",
    "chars": 2266,
    "preview": "[tool.poetry]\nname = \"pyppeteer\"\nversion = \"2.0.0\"\ndescription = \"Headless chrome/chromium automation library (unofficia"
  },
  {
    "path": "spell.txt",
    "chars": 1360,
    "preview": "abstracteventloop\naccessdenied\nack\naddressunreachable\napi\narg\nargs\narrowleft\nasync\nasyncio\nauth\nawaitable\nbeforeunload\nb"
  },
  {
    "path": "tests/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "tests/base.py",
    "chars": 1007,
    "preview": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n\nimport unittest\n\nfrom syncer import sync\n\nfrom pyppeteer import launch\nf"
  },
  {
    "path": "tests/closeme.py",
    "chars": 267,
    "preview": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n\nimport asyncio\n\nfrom pyppeteer import launch\n\n\nasync def main() -> None:"
  },
  {
    "path": "tests/dumpio.py",
    "chars": 414,
    "preview": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n\nimport asyncio\nimport sys\n\nfrom pyppeteer import launch\n\ndumpio = '--dum"
  },
  {
    "path": "tests/file-to-upload.txt",
    "chars": 21,
    "preview": "contents of the file\n"
  },
  {
    "path": "tests/frame_utils.py",
    "chars": 1333,
    "preview": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n\nfrom pyppeteer.frame_manager import Frame\nfrom pyppeteer.page import Pag"
  },
  {
    "path": "tests/server.py",
    "chars": 5082,
    "preview": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n\nimport asyncio\nimport base64\nimport functools\nimport os\nfrom typing impo"
  },
  {
    "path": "tests/static/beforeunload.html",
    "chars": 106,
    "preview": "<script>\nwindow.addEventListener('beforeunload', event => {\n  event.returnValue = 'Leave?';\n});\n</script>\n"
  },
  {
    "path": "tests/static/button.html",
    "chars": 330,
    "preview": "<!DOCTYPE html>\n<html>\n  <head>\n    <title>Button test</title>\n  </head>\n  <body>\n    <script src=\"mouse-helper.js\"></sc"
  },
  {
    "path": "tests/static/cached/one-style.css",
    "chars": 35,
    "preview": "body {\n  background-color: pink;\n}\n"
  },
  {
    "path": "tests/static/cached/one-style.html",
    "chars": 71,
    "preview": "<link rel='stylesheet' href='./one-style.css'>\n<div>hello, world</div>\n"
  },
  {
    "path": "tests/static/checkbox.html",
    "chars": 881,
    "preview": "<!DOCTYPE html>\n<html>\n  <head>\n    <title>Selection Test</title>\n  </head>\n  <body>\n    <label for=\"agree\">Remember Me<"
  },
  {
    "path": "tests/static/csp.html",
    "chars": 73,
    "preview": "<meta http-equiv=\"Content-Security-Policy\" content=\"default-src 'self'\">\n"
  },
  {
    "path": "tests/static/csscoverage/involved.html",
    "chars": 376,
    "preview": "<style>\n@charset \"utf-8\";\n\n#flutty {\n  border: 1px solid black;\n  z-index: 1;\n  /* -webkit-disabled-property: rgb(1, 2, "
  },
  {
    "path": "tests/static/csscoverage/media.html",
    "chars": 82,
    "preview": "<style>\n@media screen { div { color: green; } } </style>\n<div>hello, world</div>\n\n"
  },
  {
    "path": "tests/static/csscoverage/multiple.html",
    "chars": 268,
    "preview": "<link rel=\"stylesheet\" href=\"stylesheet1.css\">\n<link rel=\"stylesheet\" href=\"stylesheet2.css\">\n<script>\nwindow.addEventLi"
  },
  {
    "path": "tests/static/csscoverage/simple.html",
    "chars": 82,
    "preview": "<style>\ndiv { color: green; }\na { color: blue; }\n</style>\n<div>hello, world</div>\n"
  },
  {
    "path": "tests/static/csscoverage/sourceurl.html",
    "chars": 74,
    "preview": "<style>\nbody {\n  padding: 10px;\n}\n/*# sourceURL=nicename.css */\n</style>\n\n"
  },
  {
    "path": "tests/static/csscoverage/stylesheet1.css",
    "chars": 23,
    "preview": "body {\n  color: red;\n}\n"
  },
  {
    "path": "tests/static/csscoverage/stylesheet2.css",
    "chars": 36,
    "preview": "html {\n  margin: 0;\n  padding: 0;\n}\n"
  },
  {
    "path": "tests/static/csscoverage/unused.html",
    "chars": 85,
    "preview": "<style>\n@media screen {\n  a { color: green; }\n}\n/*# sourceURL=unused.css */\n</style>\n"
  },
  {
    "path": "tests/static/detect-touch.html",
    "chars": 274,
    "preview": "<!DOCTYPE html>\n<html>\n  <head>\n    <title>Detect Touch Test</title>\n    <script src=\"./modernizr.js\"></script>\n  </head"
  },
  {
    "path": "tests/static/error.html",
    "chars": 133,
    "preview": "<script>\na();\n\nfunction a() {\n    b();\n}\n\nfunction b() {\n    c();\n}\n\nfunction c() {\n    throw new Error('Fancy error!');"
  },
  {
    "path": "tests/static/es6/es6import.js",
    "chars": 62,
    "preview": "import num from './es6module.js';\nwindow.__es6injected = num;\n"
  },
  {
    "path": "tests/static/es6/es6module.js",
    "chars": 19,
    "preview": "export default 42;\n"
  },
  {
    "path": "tests/static/es6/es6pathimport.js",
    "chars": 72,
    "preview": "import num from '/static/es6/es6module.js';\nwindow.__es6injected = num;\n"
  },
  {
    "path": "tests/static/fileupload.html",
    "chars": 131,
    "preview": "<!DOCTYPE html>\n<html>\n  <head>\n    <title>File upload test</title>\n  </head>\n  <body>\n      <input type=\"file\">\n  </bod"
  },
  {
    "path": "tests/static/frame-204.html",
    "chars": 47,
    "preview": "<iframe src=\"http://httpstat.us/204\"></iframe>\n"
  },
  {
    "path": "tests/static/frame.html",
    "chars": 179,
    "preview": "<link rel='stylesheet' href='./style.css'>\n<script src='./script.js' type='text/javascript'></script>\n<style>\n  div {\n  "
  },
  {
    "path": "tests/static/grid.html",
    "chars": 1209,
    "preview": "<script>\ndocument.addEventListener('DOMContentLoaded', function() {\n    function generatePalette(amount) {\n        var r"
  },
  {
    "path": "tests/static/historyapi.html",
    "chars": 110,
    "preview": "<script>\nwindow.addEventListener('DOMContentLoaded', () => {\n  history.pushState({}, '', '#1');\n});\n</script>\n"
  },
  {
    "path": "tests/static/huge-page.html",
    "chars": 56,
    "preview": "<body style=\"background-image: url(./huge-image.png);\">\n"
  },
  {
    "path": "tests/static/injectedfile.js",
    "chars": 66,
    "preview": "window.__injected = 42;\nwindow.__injectedError = new Error('hi');\n"
  },
  {
    "path": "tests/static/injectedstyle.css",
    "chars": 34,
    "preview": "body {\n  background-color: red;\n}\n"
  },
  {
    "path": "tests/static/jscoverage/eval.html",
    "chars": 44,
    "preview": "<script>eval('console.log(\"foo\")')</script>\n"
  },
  {
    "path": "tests/static/jscoverage/involved.html",
    "chars": 225,
    "preview": "<script>\nfunction foo() {\n  if (1 > 2)\n    console.log(1);\n  if (1 < 2)\n    console.log(2);\n  let x = 1 > 2 ? 'foo' : 'b"
  },
  {
    "path": "tests/static/jscoverage/multiple.html",
    "chars": 70,
    "preview": "<script src=\"script1.js\"></script>\n<script src=\"script2.js\"></script>\n"
  },
  {
    "path": "tests/static/jscoverage/ranges.html",
    "chars": 59,
    "preview": "<script>\nfunction unused(){}console.log('used!');</script>\n"
  },
  {
    "path": "tests/static/jscoverage/script1.js",
    "chars": 16,
    "preview": "console.log(3);\n"
  },
  {
    "path": "tests/static/jscoverage/script2.js",
    "chars": 16,
    "preview": "console.log(3);\n"
  },
  {
    "path": "tests/static/jscoverage/simple.html",
    "chars": 79,
    "preview": "<script>\nfunction foo() {function bar() { } console.log(1); } foo(); </script>\n"
  },
  {
    "path": "tests/static/jscoverage/sourceurl.html",
    "chars": 61,
    "preview": "<script>\ncolsole.log(1);\n//# sourceURL=nicename.js\n</script>\n"
  },
  {
    "path": "tests/static/jscoverage/unused.html",
    "chars": 36,
    "preview": "<script>function foo() { }</script>\n"
  },
  {
    "path": "tests/static/keyboard.html",
    "chars": 1268,
    "preview": "<!DOCTYPE html>\n<html>\n  <head>\n    <title>Keyboard test</title>\n  </head>\n  <body>\n    <textarea></textarea>\n    <scrip"
  },
  {
    "path": "tests/static/mobile.html",
    "chars": 75,
    "preview": "<meta name = \"viewport\" content = \"initial-scale = 1, user-scalable = no\">\n"
  },
  {
    "path": "tests/static/modernizr.js",
    "chars": 2729,
    "preview": "/*! modernizr 3.5.0 (Custom Build) | MIT *\n* https://modernizr.com/download/?-touchevents-setclasses !*/\n!function(e,n,t"
  },
  {
    "path": "tests/static/mouse-helper.js",
    "chars": 1729,
    "preview": "// This injects a box into the page that moves with the mouse;\n// Useful for debugging\n(function(){\n  const box = docume"
  },
  {
    "path": "tests/static/nested-frames.html",
    "chars": 486,
    "preview": "<style>\nbody {\n    display: flex;\n}\n\nbody iframe {\n    flex-grow: 1;\n    flex-shrink: 1;\n}\n\n::-webkit-scrollbar {\n  disp"
  },
  {
    "path": "tests/static/offscreenbuttons.html",
    "chars": 1065,
    "preview": "<style>\n  button {\n    position: absolute;\n    width: 100px;\n    height: 20px;\n  }\n   #btn0 { right: 0; top: 0; }\n  #btn"
  },
  {
    "path": "tests/static/one-frame.html",
    "chars": 38,
    "preview": "<iframe src=\"./frame.html\"></iframe>>\n"
  },
  {
    "path": "tests/static/one-style.css",
    "chars": 37,
    "preview": "body {\n    background-color: pink;\n}\n"
  },
  {
    "path": "tests/static/one-style.html",
    "chars": 72,
    "preview": "<link rel='stylesheet' href='./one-style.css'>\n<div>hello, world!</div>\n"
  },
  {
    "path": "tests/static/popup/popup.html",
    "chars": 111,
    "preview": "<!DOCTYPE html>\n<html>\n  <head>\n    <title>Popup</title>\n  </head>\n  <body>\n    I am a popup\n  </body>\n</html>\n"
  },
  {
    "path": "tests/static/popup/window-open.html",
    "chars": 161,
    "preview": "<!DOCTYPE html>\n<html>\n  <head>\n    <title>Popup test</title>\n  </head>\n  <body>\n    <script>\n      window.open('./popup"
  },
  {
    "path": "tests/static/resetcss.html",
    "chars": 1106,
    "preview": "<style>\n/* http://meyerweb.com/eric/tools/css/reset/\n   v2.0 | 20110126\n   License: none (public domain)\n*/\n html, body,"
  },
  {
    "path": "tests/static/script.js",
    "chars": 24,
    "preview": "console.log('Cheers!');\n"
  },
  {
    "path": "tests/static/scrollable.html",
    "chars": 709,
    "preview": "<!DOCTYPE html>\n<html>\n  <head>\n    <title>Scrollable test</title>\n  </head>\n  <body>\n    <script src='mouse-helper.js'>"
  },
  {
    "path": "tests/static/select.html",
    "chars": 2082,
    "preview": "<!DOCTYPE html>\n<html>\n  <head>\n    <title>Selection Test</title>\n  </head>\n  <body>\n    <select>\n      <option value=\"b"
  },
  {
    "path": "tests/static/self-request.html",
    "chars": 106,
    "preview": "<script>\nvar req = new XMLHttpRequest();\nreq.open('GET', '/self-request.html');\nreq.send(null);\n</script>\n"
  },
  {
    "path": "tests/static/serviceworkers/empty/sw.html",
    "chars": 93,
    "preview": "<script>\n  window.registrationPromise = navigator.serviceWorker.register('sw.js');\n</script>\n"
  },
  {
    "path": "tests/static/serviceworkers/empty/sw.js",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "tests/static/serviceworkers/fetch/style.css",
    "chars": 35,
    "preview": "body {\n  background-color: pink;\n}\n"
  },
  {
    "path": "tests/static/serviceworkers/fetch/sw.html",
    "chars": 215,
    "preview": "<link rel=\"stylesheet\" href=\"./style.css\">\n<script>\n  navigator.serviceWorker.register('sw.js');\n  window.activationProm"
  },
  {
    "path": "tests/static/serviceworkers/fetch/sw.js",
    "chars": 175,
    "preview": "self.addEventListener('fetch', event => {\n  event.respondWith(fetch(event.request));\n});\n\nself.addEventListener('activat"
  },
  {
    "path": "tests/static/shadow.html",
    "chars": 475,
    "preview": "<script>\n\nlet h1 = null;\nlet button = null;\nlet clicked = false;\n\nwindow.addEventListener('DOMContentLoaded', () => {\n  "
  },
  {
    "path": "tests/static/simple-extension/index.js",
    "chars": 40,
    "preview": "// Mock script for background extension\n"
  },
  {
    "path": "tests/static/simple-extension/manifest.json",
    "chars": 184,
    "preview": "{\n  \"name\": \"Simple extension\",\n  \"version\": \"0.1\",\n  \"app\": {\n    \"background\": {\n      \"scripts\": [\"index.js\"]\n    }\n "
  },
  {
    "path": "tests/static/simple.json",
    "chars": 15,
    "preview": "{\"foo\": \"bar\"}\n"
  },
  {
    "path": "tests/static/style.css",
    "chars": 25,
    "preview": "div {\n    color: blue;\n}\n"
  },
  {
    "path": "tests/static/sw.js",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "tests/static/temperable.html",
    "chars": 56,
    "preview": "<script>\n    window.result = window.injected;\n</script>\n"
  },
  {
    "path": "tests/static/textarea.html",
    "chars": 362,
    "preview": "<!DOCTYPE html>\n<html>\n  <head>\n    <title>Textarea test</title>\n  </head>\n  <body>\n    <textarea></textarea>\n    <scrip"
  },
  {
    "path": "tests/static/touches.html",
    "chars": 1101,
    "preview": "<!DOCTYPE html>\n<html>\n  <head>\n    <title>Touch test</title>\n  </head>\n  <body>\n    <script src=\"mouse-helper.js\"></scr"
  },
  {
    "path": "tests/static/two-frames.html",
    "chars": 202,
    "preview": "<style>\nbody {\n    display: flex;\n    flex-direction: column;\n}\n\nbody iframe {\n    flex-grow: 1;\n    flex-shrink: 1;\n}\n<"
  },
  {
    "path": "tests/static/worker/worker.html",
    "chars": 260,
    "preview": "<!DOCTYPE html>\n<html>\n  <head>\n    <title>Worker test</title>\n  </head>\n  <body>\n    <script>\n      var worker = new Wo"
  },
  {
    "path": "tests/static/worker/worker.js",
    "chars": 342,
    "preview": "console.log('hello from the worker');\nfunction workerFunction() {\n  return 'worker function result';\n}\nself.addEventList"
  },
  {
    "path": "tests/static/wrappedlink.html",
    "chars": 360,
    "preview": "<style>\n:root {\n  font-family: monospace;\n}\n body {\n  display: flex;\n  align-items: center;\n  justify-content: center;\n}"
  },
  {
    "path": "tests/test_abnormal_crash.py",
    "chars": 1063,
    "preview": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n\nimport asyncio\nimport logging\nimport unittest\n\nfrom syncer import sync\n\n"
  },
  {
    "path": "tests/test_browser.py",
    "chars": 6680,
    "preview": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n\nimport asyncio\nfrom copy import deepcopy\nimport os\nfrom pathlib import P"
  },
  {
    "path": "tests/test_browser_context.py",
    "chars": 5296,
    "preview": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n\nimport asyncio\nimport unittest\n\nfrom pyppeteer import connect\nfrom pyppe"
  },
  {
    "path": "tests/test_connection.py",
    "chars": 2013,
    "preview": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n\nfrom syncer import sync\n\nfrom pyppeteer.errors import NetworkError\n\nfrom"
  },
  {
    "path": "tests/test_coverage.py",
    "chars": 9616,
    "preview": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n\nfrom syncer import sync\n\nfrom .base import BaseTestCase\n\n\nclass TestJSCo"
  },
  {
    "path": "tests/test_dialog.py",
    "chars": 1309,
    "preview": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n\nimport asyncio\n\nfrom syncer import sync\n\nfrom .base import BaseTestCase\n"
  },
  {
    "path": "tests/test_element_handle.py",
    "chars": 15201,
    "preview": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n\nimport logging\nimport sys\n\nfrom syncer import sync\n\nimport pyppeteer\nfro"
  },
  {
    "path": "tests/test_execution_context.py",
    "chars": 4941,
    "preview": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n\nfrom syncer import sync\n\nfrom pyppeteer.errors import ElementHandleError"
  },
  {
    "path": "tests/test_frame.py",
    "chars": 25933,
    "preview": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n\nimport asyncio\nimport time\nimport unittest\n\nfrom syncer import sync\n\nfro"
  },
  {
    "path": "tests/test_input.py",
    "chars": 25023,
    "preview": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n\nimport asyncio\nfrom pathlib import Path\nimport sys\nimport unittest\n\nfrom"
  },
  {
    "path": "tests/test_launcher.py",
    "chars": 17773,
    "preview": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n\nimport asyncio\nfrom copy import deepcopy\nimport glob\nimport logging\nimpo"
  },
  {
    "path": "tests/test_misc.py",
    "chars": 3104,
    "preview": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n\nimport logging\nimport unittest\n\nimport pyppeteer\nfrom pyppeteer.helper i"
  },
  {
    "path": "tests/test_network.py",
    "chars": 22849,
    "preview": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n\nimport asyncio\nfrom pathlib import Path\nimport sys\nimport unittest\n\nfrom"
  },
  {
    "path": "tests/test_page.py",
    "chars": 68647,
    "preview": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n\nimport asyncio\nimport math\nimport os\nfrom pathlib import Path\nimport sys"
  },
  {
    "path": "tests/test_pyppeteer.py",
    "chars": 2480,
    "preview": "#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n\n\"\"\"\ntest_pyppeteer\n----------------------------------\n\nTests for `pyppete"
  },
  {
    "path": "tests/test_screenshot.py",
    "chars": 3294,
    "preview": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n\nimport base64\nfrom pathlib import Path\nfrom unittest import TestCase\n\nfr"
  },
  {
    "path": "tests/test_target.py",
    "chars": 6420,
    "preview": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n\nimport asyncio\nimport unittest\n\nfrom syncer import sync\n\nfrom .base impo"
  },
  {
    "path": "tests/test_tracing.py",
    "chars": 2488,
    "preview": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n\nimport json\nfrom pathlib import Path\nimport unittest\n\nfrom syncer import"
  },
  {
    "path": "tests/test_worker.py",
    "chars": 3248,
    "preview": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n\nimport asyncio\n\nfrom syncer import sync\n\nfrom .base import BaseTestCase\n"
  },
  {
    "path": "tests/utils.py",
    "chars": 264,
    "preview": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n\nimport asyncio\n\n\ndef waitEvent(emitter, event_name):\n    fut = asyncio.g"
  },
  {
    "path": "tox.ini",
    "chars": 1765,
    "preview": "[tox]\nenvlist = py3{6,7,8},flake8,mypy\nminversion = 3.4.0\nisolated_build = True\n\n[testenv]\npassenv:\n  PYTEST_ADDOPTS\n  C"
  }
]

About this extraction

This page contains the full source code of the pyppeteer/pyppeteer2 GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 143 files (588.3 KB), approximately 144.2k tokens, and a symbol index with 1110 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!