bug 1540655: remote: vendor Puppeteer; r=remote-protocol-reviewers,jdescottes draft
authorpulselistener
Fri, 16 Aug 2019 13:22:06 +0000
changeset 2220727 2936da435c51787d88e2eb0ff6c3787c431593d1
parent 2220726 58122219d1c1efbb80bd1c4b637edef0372684f8
child 2220728 1c85cbf0c5308ba92ecb7f46a17aab3f40305067
push id406994
push userreviewbot
push dateFri, 16 Aug 2019 13:23:14 +0000
treeherdertry@5f50de08beda [default view] [failures only]
reviewersremote-protocol-reviewers, jdescottes
bugs1540655
milestone70.0a1
bug 1540655: remote: vendor Puppeteer; r=remote-protocol-reviewers,jdescottes Puppeteer is licensed under the Apache-2.0 license. No code from Puppeteer gets included in Firefox. Differential Revision: https://phabricator.services.mozilla.com/D37008 Differential Diff: PHID-DIFF-qqa4nkyr5smpajh7fzxw
remote/test/puppeteer/.appveyor.yml
remote/test/puppeteer/.ci/node6/Dockerfile.linux
remote/test/puppeteer/.ci/node8/Dockerfile.linux
remote/test/puppeteer/.cirrus.yml
remote/test/puppeteer/.editorconfig
remote/test/puppeteer/.eslintignore
remote/test/puppeteer/.eslintrc.js
remote/test/puppeteer/.npmignore
remote/test/puppeteer/.travis.yml
remote/test/puppeteer/CONTRIBUTING.md
remote/test/puppeteer/DeviceDescriptors.js
remote/test/puppeteer/Errors.js
remote/test/puppeteer/LICENSE
remote/test/puppeteer/README.md
remote/test/puppeteer/docs/api.md
remote/test/puppeteer/docs/issue_template.md
remote/test/puppeteer/docs/troubleshooting.md
remote/test/puppeteer/examples/README.md
remote/test/puppeteer/examples/block-images.js
remote/test/puppeteer/examples/custom-event.js
remote/test/puppeteer/examples/detect-sniff.js
remote/test/puppeteer/examples/pdf.js
remote/test/puppeteer/examples/proxy.js
remote/test/puppeteer/examples/screenshot-fullpage.js
remote/test/puppeteer/examples/screenshot.js
remote/test/puppeteer/examples/search.js
remote/test/puppeteer/experimental/puppeteer-firefox/.ci/node6/Dockerfile.linux
remote/test/puppeteer/experimental/puppeteer-firefox/.ci/node8/Dockerfile.linux
remote/test/puppeteer/experimental/puppeteer-firefox/.ci/node8/Dockerfile.windows
remote/test/puppeteer/experimental/puppeteer-firefox/.cirrus.yml
remote/test/puppeteer/experimental/puppeteer-firefox/.gitignore
remote/test/puppeteer/experimental/puppeteer-firefox/.npmignore
remote/test/puppeteer/experimental/puppeteer-firefox/DeviceDescriptors.js
remote/test/puppeteer/experimental/puppeteer-firefox/Errors.js
remote/test/puppeteer/experimental/puppeteer-firefox/LICENSE
remote/test/puppeteer/experimental/puppeteer-firefox/README.md
remote/test/puppeteer/experimental/puppeteer-firefox/examples/screenshot.js
remote/test/puppeteer/experimental/puppeteer-firefox/examples/search.js
remote/test/puppeteer/experimental/puppeteer-firefox/index.js
remote/test/puppeteer/experimental/puppeteer-firefox/install.js
remote/test/puppeteer/experimental/puppeteer-firefox/lib/Accessibility.js
remote/test/puppeteer/experimental/puppeteer-firefox/lib/Browser.js
remote/test/puppeteer/experimental/puppeteer-firefox/lib/BrowserFetcher.js
remote/test/puppeteer/experimental/puppeteer-firefox/lib/Connection.js
remote/test/puppeteer/experimental/puppeteer-firefox/lib/DOMWorld.js
remote/test/puppeteer/experimental/puppeteer-firefox/lib/DeviceDescriptors.js
remote/test/puppeteer/experimental/puppeteer-firefox/lib/Dialog.js
remote/test/puppeteer/experimental/puppeteer-firefox/lib/Errors.js
remote/test/puppeteer/experimental/puppeteer-firefox/lib/Events.js
remote/test/puppeteer/experimental/puppeteer-firefox/lib/ExecutionContext.js
remote/test/puppeteer/experimental/puppeteer-firefox/lib/FrameManager.js
remote/test/puppeteer/experimental/puppeteer-firefox/lib/Input.js
remote/test/puppeteer/experimental/puppeteer-firefox/lib/JSHandle.js
remote/test/puppeteer/experimental/puppeteer-firefox/lib/Launcher.js
remote/test/puppeteer/experimental/puppeteer-firefox/lib/NavigationWatchdog.js
remote/test/puppeteer/experimental/puppeteer-firefox/lib/NetworkManager.js
remote/test/puppeteer/experimental/puppeteer-firefox/lib/Page.js
remote/test/puppeteer/experimental/puppeteer-firefox/lib/Puppeteer.js
remote/test/puppeteer/experimental/puppeteer-firefox/lib/TimeoutSettings.js
remote/test/puppeteer/experimental/puppeteer-firefox/lib/USKeyboardLayout.js
remote/test/puppeteer/experimental/puppeteer-firefox/lib/WebSocketTransport.js
remote/test/puppeteer/experimental/puppeteer-firefox/lib/api.js
remote/test/puppeteer/experimental/puppeteer-firefox/lib/externs.d.ts
remote/test/puppeteer/experimental/puppeteer-firefox/lib/helper.js
remote/test/puppeteer/experimental/puppeteer-firefox/misc/00-puppeteer-prefs.js
remote/test/puppeteer/experimental/puppeteer-firefox/misc/install-preferences.js
remote/test/puppeteer/experimental/puppeteer-firefox/misc/puppeteer.cfg
remote/test/puppeteer/experimental/puppeteer-firefox/package.json
remote/test/puppeteer/experimental/puppeteer-firefox/tsconfig.json
remote/test/puppeteer/index.js
remote/test/puppeteer/install.js
remote/test/puppeteer/lib/.eslintrc.js
remote/test/puppeteer/lib/Accessibility.js
remote/test/puppeteer/lib/Browser.js
remote/test/puppeteer/lib/BrowserFetcher.js
remote/test/puppeteer/lib/Connection.js
remote/test/puppeteer/lib/Coverage.js
remote/test/puppeteer/lib/DOMWorld.js
remote/test/puppeteer/lib/DeviceDescriptors.js
remote/test/puppeteer/lib/Dialog.js
remote/test/puppeteer/lib/EmulationManager.js
remote/test/puppeteer/lib/Errors.js
remote/test/puppeteer/lib/Events.js
remote/test/puppeteer/lib/ExecutionContext.js
remote/test/puppeteer/lib/FrameManager.js
remote/test/puppeteer/lib/Input.js
remote/test/puppeteer/lib/JSHandle.js
remote/test/puppeteer/lib/Launcher.js
remote/test/puppeteer/lib/LifecycleWatcher.js
remote/test/puppeteer/lib/Multimap.js
remote/test/puppeteer/lib/NetworkManager.js
remote/test/puppeteer/lib/Page.js
remote/test/puppeteer/lib/PipeTransport.js
remote/test/puppeteer/lib/Puppeteer.js
remote/test/puppeteer/lib/Target.js
remote/test/puppeteer/lib/TaskQueue.js
remote/test/puppeteer/lib/TimeoutSettings.js
remote/test/puppeteer/lib/Tracing.js
remote/test/puppeteer/lib/USKeyboardLayout.js
remote/test/puppeteer/lib/WebSocketTransport.js
remote/test/puppeteer/lib/Worker.js
remote/test/puppeteer/lib/api.js
remote/test/puppeteer/lib/externs.d.ts
remote/test/puppeteer/lib/helper.js
remote/test/puppeteer/moz.yaml
remote/test/puppeteer/package.json
remote/test/puppeteer/test/CDPSession.spec.js
remote/test/puppeteer/test/accessibility.spec.js
remote/test/puppeteer/test/assets/beforeunload.html
remote/test/puppeteer/test/assets/cached/one-style.css
remote/test/puppeteer/test/assets/cached/one-style.html
remote/test/puppeteer/test/assets/chromium-linux.zip
remote/test/puppeteer/test/assets/consolelog.html
remote/test/puppeteer/test/assets/csp.html
remote/test/puppeteer/test/assets/csscoverage/Dosis-Regular.ttf
remote/test/puppeteer/test/assets/csscoverage/OFL.txt
remote/test/puppeteer/test/assets/csscoverage/involved.html
remote/test/puppeteer/test/assets/csscoverage/media.html
remote/test/puppeteer/test/assets/csscoverage/multiple.html
remote/test/puppeteer/test/assets/csscoverage/simple.html
remote/test/puppeteer/test/assets/csscoverage/sourceurl.html
remote/test/puppeteer/test/assets/csscoverage/stylesheet1.css
remote/test/puppeteer/test/assets/csscoverage/stylesheet2.css
remote/test/puppeteer/test/assets/csscoverage/unused.html
remote/test/puppeteer/test/assets/detect-touch.html
remote/test/puppeteer/test/assets/digits/0.png
remote/test/puppeteer/test/assets/digits/1.png
remote/test/puppeteer/test/assets/digits/2.png
remote/test/puppeteer/test/assets/digits/3.png
remote/test/puppeteer/test/assets/digits/4.png
remote/test/puppeteer/test/assets/digits/5.png
remote/test/puppeteer/test/assets/digits/6.png
remote/test/puppeteer/test/assets/digits/7.png
remote/test/puppeteer/test/assets/digits/8.png
remote/test/puppeteer/test/assets/digits/9.png
remote/test/puppeteer/test/assets/empty.html
remote/test/puppeteer/test/assets/error.html
remote/test/puppeteer/test/assets/es6/.eslintrc
remote/test/puppeteer/test/assets/es6/es6import.js
remote/test/puppeteer/test/assets/es6/es6module.js
remote/test/puppeteer/test/assets/es6/es6pathimport.js
remote/test/puppeteer/test/assets/file-to-upload.txt
remote/test/puppeteer/test/assets/frames/frame.html
remote/test/puppeteer/test/assets/frames/frameset.html
remote/test/puppeteer/test/assets/frames/nested-frames.html
remote/test/puppeteer/test/assets/frames/one-frame.html
remote/test/puppeteer/test/assets/frames/script.js
remote/test/puppeteer/test/assets/frames/style.css
remote/test/puppeteer/test/assets/frames/two-frames.html
remote/test/puppeteer/test/assets/global-var.html
remote/test/puppeteer/test/assets/grid.html
remote/test/puppeteer/test/assets/historyapi.html
remote/test/puppeteer/test/assets/injectedfile.js
remote/test/puppeteer/test/assets/injectedstyle.css
remote/test/puppeteer/test/assets/input/button.html
remote/test/puppeteer/test/assets/input/checkbox.html
remote/test/puppeteer/test/assets/input/fileupload.html
remote/test/puppeteer/test/assets/input/keyboard.html
remote/test/puppeteer/test/assets/input/mouse-helper.js
remote/test/puppeteer/test/assets/input/rotatedButton.html
remote/test/puppeteer/test/assets/input/scrollable.html
remote/test/puppeteer/test/assets/input/select.html
remote/test/puppeteer/test/assets/input/textarea.html
remote/test/puppeteer/test/assets/input/touches.html
remote/test/puppeteer/test/assets/jscoverage/eval.html
remote/test/puppeteer/test/assets/jscoverage/involved.html
remote/test/puppeteer/test/assets/jscoverage/multiple.html
remote/test/puppeteer/test/assets/jscoverage/ranges.html
remote/test/puppeteer/test/assets/jscoverage/script1.js
remote/test/puppeteer/test/assets/jscoverage/script2.js
remote/test/puppeteer/test/assets/jscoverage/simple.html
remote/test/puppeteer/test/assets/jscoverage/sourceurl.html
remote/test/puppeteer/test/assets/jscoverage/unused.html
remote/test/puppeteer/test/assets/mobile.html
remote/test/puppeteer/test/assets/modernizr.js
remote/test/puppeteer/test/assets/networkidle.html
remote/test/puppeteer/test/assets/offscreenbuttons.html
remote/test/puppeteer/test/assets/one-style.css
remote/test/puppeteer/test/assets/one-style.html
remote/test/puppeteer/test/assets/playground.html
remote/test/puppeteer/test/assets/popup/popup.html
remote/test/puppeteer/test/assets/popup/window-open.html
remote/test/puppeteer/test/assets/pptr.png
remote/test/puppeteer/test/assets/resetcss.html
remote/test/puppeteer/test/assets/self-request.html
remote/test/puppeteer/test/assets/serviceworkers/empty/sw.html
remote/test/puppeteer/test/assets/serviceworkers/empty/sw.js
remote/test/puppeteer/test/assets/serviceworkers/fetch/style.css
remote/test/puppeteer/test/assets/serviceworkers/fetch/sw.html
remote/test/puppeteer/test/assets/serviceworkers/fetch/sw.js
remote/test/puppeteer/test/assets/shadow.html
remote/test/puppeteer/test/assets/simple-extension/content-script.js
remote/test/puppeteer/test/assets/simple-extension/index.js
remote/test/puppeteer/test/assets/simple-extension/manifest.json
remote/test/puppeteer/test/assets/simple.json
remote/test/puppeteer/test/assets/tamperable.html
remote/test/puppeteer/test/assets/title.html
remote/test/puppeteer/test/assets/worker/worker.html
remote/test/puppeteer/test/assets/worker/worker.js
remote/test/puppeteer/test/assets/wrappedlink.html
remote/test/puppeteer/test/browser.spec.js
remote/test/puppeteer/test/browsercontext.spec.js
remote/test/puppeteer/test/chromiumonly.spec.js
remote/test/puppeteer/test/click.spec.js
remote/test/puppeteer/test/cookies.spec.js
remote/test/puppeteer/test/coverage.spec.js
remote/test/puppeteer/test/dialog.spec.js
remote/test/puppeteer/test/diffstyle.css
remote/test/puppeteer/test/elementhandle.spec.js
remote/test/puppeteer/test/emulation.spec.js
remote/test/puppeteer/test/evaluation.spec.js
remote/test/puppeteer/test/fixtures.spec.js
remote/test/puppeteer/test/fixtures/closeme.js
remote/test/puppeteer/test/fixtures/dumpio.js
remote/test/puppeteer/test/frame.spec.js
remote/test/puppeteer/test/golden-chromium/csscoverage-involved.txt
remote/test/puppeteer/test/golden-chromium/grid-cell-0.png
remote/test/puppeteer/test/golden-chromium/grid-cell-1.png
remote/test/puppeteer/test/golden-chromium/grid-cell-2.png
remote/test/puppeteer/test/golden-chromium/grid-cell-3.png
remote/test/puppeteer/test/golden-chromium/jscoverage-involved.txt
remote/test/puppeteer/test/golden-chromium/mock-binary-response.png
remote/test/puppeteer/test/golden-chromium/screenshot-clip-odd-size.png
remote/test/puppeteer/test/golden-chromium/screenshot-clip-rect.png
remote/test/puppeteer/test/golden-chromium/screenshot-element-bounding-box.png
remote/test/puppeteer/test/golden-chromium/screenshot-element-fractional-offset.png
remote/test/puppeteer/test/golden-chromium/screenshot-element-fractional.png
remote/test/puppeteer/test/golden-chromium/screenshot-element-larger-than-viewport.png
remote/test/puppeteer/test/golden-chromium/screenshot-element-padding-border.png
remote/test/puppeteer/test/golden-chromium/screenshot-element-rotate.png
remote/test/puppeteer/test/golden-chromium/screenshot-element-scrolled-into-view.png
remote/test/puppeteer/test/golden-chromium/screenshot-grid-fullpage.png
remote/test/puppeteer/test/golden-chromium/screenshot-offscreen-clip.png
remote/test/puppeteer/test/golden-chromium/screenshot-sanity.png
remote/test/puppeteer/test/golden-chromium/transparent.png
remote/test/puppeteer/test/golden-chromium/white.jpg
remote/test/puppeteer/test/golden-firefox/grid-cell-0.png
remote/test/puppeteer/test/golden-firefox/grid-cell-1.png
remote/test/puppeteer/test/golden-firefox/screenshot-clip-odd-size.png
remote/test/puppeteer/test/golden-firefox/screenshot-clip-rect.png
remote/test/puppeteer/test/golden-firefox/screenshot-element-bounding-box.png
remote/test/puppeteer/test/golden-firefox/screenshot-element-fractional-offset.png
remote/test/puppeteer/test/golden-firefox/screenshot-element-fractional.png
remote/test/puppeteer/test/golden-firefox/screenshot-element-larger-than-viewport.png
remote/test/puppeteer/test/golden-firefox/screenshot-element-padding-border.png
remote/test/puppeteer/test/golden-firefox/screenshot-element-rotate.png
remote/test/puppeteer/test/golden-firefox/screenshot-element-scrolled-into-view.png
remote/test/puppeteer/test/golden-firefox/screenshot-grid-fullpage.png
remote/test/puppeteer/test/golden-firefox/screenshot-offscreen-clip.png
remote/test/puppeteer/test/golden-firefox/screenshot-sanity.png
remote/test/puppeteer/test/golden-utils.js
remote/test/puppeteer/test/headful.spec.js
remote/test/puppeteer/test/ignorehttpserrors.spec.js
remote/test/puppeteer/test/input.spec.js
remote/test/puppeteer/test/jshandle.spec.js
remote/test/puppeteer/test/keyboard.spec.js
remote/test/puppeteer/test/launcher.spec.js
remote/test/puppeteer/test/mouse.spec.js
remote/test/puppeteer/test/navigation.spec.js
remote/test/puppeteer/test/network.spec.js
remote/test/puppeteer/test/page.spec.js
remote/test/puppeteer/test/puppeteer.spec.js
remote/test/puppeteer/test/queryselector.spec.js
remote/test/puppeteer/test/requestinterception.spec.js
remote/test/puppeteer/test/run_static_server.js
remote/test/puppeteer/test/screenshot.spec.js
remote/test/puppeteer/test/target.spec.js
remote/test/puppeteer/test/test.js
remote/test/puppeteer/test/touchscreen.spec.js
remote/test/puppeteer/test/tracing.spec.js
remote/test/puppeteer/test/utils.js
remote/test/puppeteer/test/waittask.spec.js
remote/test/puppeteer/test/worker.spec.js
remote/test/puppeteer/tsconfig.json
remote/test/puppeteer/utils/ESTreeWalker.js
remote/test/puppeteer/utils/apply_next_version.js
remote/test/puppeteer/utils/bisect.js
remote/test/puppeteer/utils/browser/README.md
remote/test/puppeteer/utils/browser/WebSocket.js
remote/test/puppeteer/utils/browser/test.js
remote/test/puppeteer/utils/check_availability.js
remote/test/puppeteer/utils/doclint/.gitignore
remote/test/puppeteer/utils/doclint/Message.js
remote/test/puppeteer/utils/doclint/README.md
remote/test/puppeteer/utils/doclint/Source.js
remote/test/puppeteer/utils/doclint/check_public_api/Documentation.js
remote/test/puppeteer/utils/doclint/check_public_api/JSBuilder.js
remote/test/puppeteer/utils/doclint/check_public_api/MDBuilder.js
remote/test/puppeteer/utils/doclint/check_public_api/index.js
remote/test/puppeteer/utils/doclint/check_public_api/test/.gitignore
remote/test/puppeteer/utils/doclint/check_public_api/test/check-duplicates/doc.md
remote/test/puppeteer/utils/doclint/check_public_api/test/check-duplicates/foo.js
remote/test/puppeteer/utils/doclint/check_public_api/test/check-duplicates/result.txt
remote/test/puppeteer/utils/doclint/check_public_api/test/check-returns/doc.md
remote/test/puppeteer/utils/doclint/check_public_api/test/check-returns/foo.js
remote/test/puppeteer/utils/doclint/check_public_api/test/check-returns/result.txt
remote/test/puppeteer/utils/doclint/check_public_api/test/check-sorting/Events.js
remote/test/puppeteer/utils/doclint/check_public_api/test/check-sorting/doc.md
remote/test/puppeteer/utils/doclint/check_public_api/test/check-sorting/foo.js
remote/test/puppeteer/utils/doclint/check_public_api/test/check-sorting/result.txt
remote/test/puppeteer/utils/doclint/check_public_api/test/diff-arguments/doc.md
remote/test/puppeteer/utils/doclint/check_public_api/test/diff-arguments/foo.js
remote/test/puppeteer/utils/doclint/check_public_api/test/diff-arguments/result.txt
remote/test/puppeteer/utils/doclint/check_public_api/test/diff-classes/doc.md
remote/test/puppeteer/utils/doclint/check_public_api/test/diff-classes/foo.js
remote/test/puppeteer/utils/doclint/check_public_api/test/diff-classes/other.js
remote/test/puppeteer/utils/doclint/check_public_api/test/diff-classes/result.txt
remote/test/puppeteer/utils/doclint/check_public_api/test/diff-events/Events.js
remote/test/puppeteer/utils/doclint/check_public_api/test/diff-events/doc.md
remote/test/puppeteer/utils/doclint/check_public_api/test/diff-events/foo.js
remote/test/puppeteer/utils/doclint/check_public_api/test/diff-events/result.txt
remote/test/puppeteer/utils/doclint/check_public_api/test/diff-methods/doc.md
remote/test/puppeteer/utils/doclint/check_public_api/test/diff-methods/foo.js
remote/test/puppeteer/utils/doclint/check_public_api/test/diff-methods/result.txt
remote/test/puppeteer/utils/doclint/check_public_api/test/diff-properties/doc.md
remote/test/puppeteer/utils/doclint/check_public_api/test/diff-properties/foo.js
remote/test/puppeteer/utils/doclint/check_public_api/test/diff-properties/result.txt
remote/test/puppeteer/utils/doclint/check_public_api/test/js-builder-common/Events.js
remote/test/puppeteer/utils/doclint/check_public_api/test/js-builder-common/foo.js
remote/test/puppeteer/utils/doclint/check_public_api/test/js-builder-common/result.txt
remote/test/puppeteer/utils/doclint/check_public_api/test/js-builder-inheritance/Events.js
remote/test/puppeteer/utils/doclint/check_public_api/test/js-builder-inheritance/foo.js
remote/test/puppeteer/utils/doclint/check_public_api/test/js-builder-inheritance/result.txt
remote/test/puppeteer/utils/doclint/check_public_api/test/md-builder-common/doc.md
remote/test/puppeteer/utils/doclint/check_public_api/test/md-builder-common/result.txt
remote/test/puppeteer/utils/doclint/check_public_api/test/test.js
remote/test/puppeteer/utils/doclint/cli.js
remote/test/puppeteer/utils/doclint/generate_types/index.js
remote/test/puppeteer/utils/doclint/generate_types/test/test.ts
remote/test/puppeteer/utils/doclint/generate_types/test/tsconfig.json
remote/test/puppeteer/utils/doclint/preprocessor/index.js
remote/test/puppeteer/utils/doclint/preprocessor/test.js
remote/test/puppeteer/utils/fetch_devices.js
remote/test/puppeteer/utils/node6-transform/TransformAsyncFunctions.js
remote/test/puppeteer/utils/node6-transform/index.js
remote/test/puppeteer/utils/node6-transform/test/test.js
remote/test/puppeteer/utils/prepare_puppeteer_core.js
remote/test/puppeteer/utils/protocol-types-generator/index.js
remote/test/puppeteer/utils/testrunner/.npmignore
remote/test/puppeteer/utils/testrunner/LICENSE
remote/test/puppeteer/utils/testrunner/Matchers.js
remote/test/puppeteer/utils/testrunner/Multimap.js
remote/test/puppeteer/utils/testrunner/README.md
remote/test/puppeteer/utils/testrunner/Reporter.js
remote/test/puppeteer/utils/testrunner/TestRunner.js
remote/test/puppeteer/utils/testrunner/examples/fail.js
remote/test/puppeteer/utils/testrunner/examples/hookfail.js
remote/test/puppeteer/utils/testrunner/examples/hooktimeout.js
remote/test/puppeteer/utils/testrunner/examples/timeout.js
remote/test/puppeteer/utils/testrunner/examples/unhandledpromiserejection.js
remote/test/puppeteer/utils/testrunner/index.js
remote/test/puppeteer/utils/testrunner/package.json
remote/test/puppeteer/utils/testserver/LICENSE
remote/test/puppeteer/utils/testserver/README.md
remote/test/puppeteer/utils/testserver/cert.pem
remote/test/puppeteer/utils/testserver/index.js
remote/test/puppeteer/utils/testserver/key.pem
remote/test/puppeteer/utils/testserver/package.json
new file mode 100644
--- /dev/null
+++ b/remote/test/puppeteer/.appveyor.yml
@@ -0,0 +1,18 @@
+environment:
+  matrix:
+    - nodejs_version: "6.12.3"
+    - nodejs_version: "8.11.3"
+
+build: off
+
+install:
+  - ps: Install-Product node $env:nodejs_version
+  - npm install
+  - if "%nodejs_version%" == "8.11.3" (
+      npm run lint &&
+      npm run coverage  &&
+      npm run test-doclint &&
+      npm run test-types
+    ) else (
+      npm run unit-node6
+    )
new file mode 100644
--- /dev/null
+++ b/remote/test/puppeteer/.ci/node6/Dockerfile.linux
@@ -0,0 +1,17 @@
+FROM node:6.12.3
+
+RUN apt-get update && \
+    apt-get -y install xvfb gconf-service libasound2 libatk1.0-0 libc6 libcairo2 libcups2 \
+      libdbus-1-3 libexpat1 libfontconfig1 libgcc1 libgconf-2-4 libgdk-pixbuf2.0-0 libglib2.0-0 \
+      libgtk-3-0 libnspr4 libpango-1.0-0 libpangocairo-1.0-0 libstdc++6 libx11-6 libx11-xcb1 libxcb1 \
+      libxcomposite1 libxcursor1 libxdamage1 libxext6 libxfixes3 libxi6 libxrandr2 libxrender1 libxss1 \
+      libxtst6 ca-certificates fonts-liberation libappindicator1 libnss3 lsb-release xdg-utils wget && \
+    rm -rf /var/lib/apt/lists/*
+
+# Add user so we don't need --no-sandbox.
+RUN groupadd -r pptruser && useradd -r -g pptruser -G audio,video pptruser \
+    && mkdir -p /home/pptruser/Downloads \
+    && chown -R pptruser:pptruser /home/pptruser
+
+# Run everything after as non-privileged user.
+USER pptruser
new file mode 100644
--- /dev/null
+++ b/remote/test/puppeteer/.ci/node8/Dockerfile.linux
@@ -0,0 +1,17 @@
+FROM node:8.11.3
+
+RUN apt-get update && \
+    apt-get -y install xvfb gconf-service libasound2 libatk1.0-0 libc6 libcairo2 libcups2 \
+      libdbus-1-3 libexpat1 libfontconfig1 libgcc1 libgconf-2-4 libgdk-pixbuf2.0-0 libglib2.0-0 \
+      libgtk-3-0 libnspr4 libpango-1.0-0 libpangocairo-1.0-0 libstdc++6 libx11-6 libx11-xcb1 libxcb1 \
+      libxcomposite1 libxcursor1 libxdamage1 libxext6 libxfixes3 libxi6 libxrandr2 libxrender1 libxss1 \
+      libxtst6 ca-certificates fonts-liberation libappindicator1 libnss3 lsb-release xdg-utils wget && \
+    rm -rf /var/lib/apt/lists/*
+
+# Add user so we don't need --no-sandbox.
+RUN groupadd -r pptruser && useradd -r -g pptruser -G audio,video pptruser \
+    && mkdir -p /home/pptruser/Downloads \
+    && chown -R pptruser:pptruser /home/pptruser
+
+# Run everything after as non-privileged user.
+USER pptruser
new file mode 100644
--- /dev/null
+++ b/remote/test/puppeteer/.cirrus.yml
@@ -0,0 +1,47 @@
+env:
+  DISPLAY: :99.0
+
+task:
+  matrix:
+    - name: Chromium (node6 + linux)
+      container:
+        dockerfile: .ci/node6/Dockerfile.linux
+      xvfb_start_background_script: Xvfb :99 -ac -screen 0 1024x768x24
+  install_script: npm install --unsafe-perm
+  test_script: npm run unit-node6
+
+task:
+  matrix:
+    - name: Chromium (node8 + linux)
+      container:
+        dockerfile: .ci/node8/Dockerfile.linux
+      xvfb_start_background_script: Xvfb :99 -ac -screen 0 1024x768x24
+  install_script: npm install --unsafe-perm
+  lint_script: npm run lint
+  coverage_script: npm run coverage
+  test_doclint_script: npm run test-doclint
+  test_types_script: npm run test-types
+
+task:
+  matrix:
+    - name: Firefox (node8 + linux)
+      container:
+        dockerfile: .ci/node8/Dockerfile.linux
+      xvfb_start_background_script: Xvfb :99 -ac -screen 0 1024x768x24
+  install_script: npm install --unsafe-perm && cd experimental/puppeteer-firefox && npm install --unsafe-perm
+  test_script: npm run funit
+
+task:
+  osx_instance:
+    image: high-sierra-base
+  name: Chromium (node8 + macOS)
+  env:
+    HOMEBREW_NO_AUTO_UPDATE: 1
+  node_install_script:
+    - brew install node@8
+    - brew link --force node@8
+  install_script: npm install --unsafe-perm
+  lint_script: npm run lint
+  coverage_script: npm run coverage
+  test_doclint_script: npm run test-doclint
+  test_types_script: npm run test-types
new file mode 100644
--- /dev/null
+++ b/remote/test/puppeteer/.editorconfig
@@ -0,0 +1,9 @@
+root = true
+
+[*]
+indent_style = space
+indent_size = 2
+end_of_line = lf
+charset = utf-8
+trim_trailing_whitespace = true
+insert_final_newline = true
new file mode 100644
--- /dev/null
+++ b/remote/test/puppeteer/.eslintignore
@@ -0,0 +1,9 @@
+test/assets/modernizr.js
+third_party/*
+utils/browser/puppeteer-web.js
+utils/doclint/check_public_api/test/
+utils/testrunner/examples/
+node6/*
+node6-test/*
+node6-testrunner/*
+experimental/
new file mode 100644
--- /dev/null
+++ b/remote/test/puppeteer/.eslintrc.js
@@ -0,0 +1,112 @@
+module.exports = {
+    "root": true,
+
+    "env": {
+        "node": true,
+        "es6": true
+    },
+
+    "parserOptions": {
+        "ecmaVersion": 9
+    },
+
+    /**
+     * ESLint rules
+     *
+     * All available rules: http://eslint.org/docs/rules/
+     *
+     * Rules take the following form:
+     *   "rule-name", [severity, { opts }]
+     * Severity: 2 == error, 1 == warning, 0 == off.
+     */
+    "rules": {
+        /**
+         * Enforced rules
+         */
+
+
+        // syntax preferences
+        "quotes": [2, "single", {
+            "avoidEscape": true,
+            "allowTemplateLiterals": true
+        }],
+        "semi": 2,
+        "no-extra-semi": 2,
+        "comma-style": [2, "last"],
+        "wrap-iife": [2, "inside"],
+        "spaced-comment": [2, "always", {
+            "markers": ["*"]
+        }],
+        "eqeqeq": [2],
+        "arrow-body-style": [2, "as-needed"],
+        "accessor-pairs": [2, {
+            "getWithoutSet": false,
+            "setWithoutGet": false
+        }],
+        "brace-style": [2, "1tbs", {"allowSingleLine": true}],
+        "curly": [2, "multi-or-nest", "consistent"],
+        "new-parens": 2,
+        "func-call-spacing": 2,
+        "arrow-parens": [2, "as-needed"],
+        "prefer-const": 2,
+        "quote-props": [2, "consistent"],
+
+        // anti-patterns
+        "no-var": 2,
+        "no-with": 2,
+        "no-multi-str": 2,
+        "no-caller": 2,
+        "no-implied-eval": 2,
+        "no-labels": 2,
+        "no-new-object": 2,
+        "no-octal-escape": 2,
+        "no-self-compare": 2,
+        "no-shadow-restricted-names": 2,
+        "no-cond-assign": 2,
+        "no-debugger": 2,
+        "no-dupe-keys": 2,
+        "no-duplicate-case": 2,
+        "no-empty-character-class": 2,
+        "no-unreachable": 2,
+        "no-unsafe-negation": 2,
+        "radix": 2,
+        "valid-typeof": 2,
+        "no-unused-vars": [2, { "args": "none", "vars": "local", "varsIgnorePattern": "([fx]?describe|[fx]?it|beforeAll|beforeEach|afterAll|afterEach)" }],
+        "no-implicit-globals": [2],
+
+        // es2015 features
+        "require-yield": 2,
+        "template-curly-spacing": [2, "never"],
+
+        // spacing details
+        "space-infix-ops": 2,
+        "space-in-parens": [2, "never"],
+        "space-before-function-paren": [2, "never"],
+        "no-whitespace-before-property": 2,
+        "keyword-spacing": [2, {
+            "overrides": {
+                "if": {"after": true},
+                "else": {"after": true},
+                "for": {"after": true},
+                "while": {"after": true},
+                "do": {"after": true},
+                "switch": {"after": true},
+                "return": {"after": true}
+            }
+        }],
+        "arrow-spacing": [2, {
+            "after": true,
+            "before": true
+        }],
+
+        // file whitespace
+        "no-multiple-empty-lines": [2, {"max": 2}],
+        "no-mixed-spaces-and-tabs": 2,
+        "no-trailing-spaces": 2,
+        "linebreak-style": [ process.platform === "win32" ? 0 : 2, "unix" ],
+        "indent": [2, 2, { "SwitchCase": 1, "CallExpression": {"arguments": 2}, "MemberExpression": 2 }],
+        "key-spacing": [2, {
+            "beforeColon": false
+        }]
+    }
+};
new file mode 100644
--- /dev/null
+++ b/remote/test/puppeteer/.npmignore
@@ -0,0 +1,39 @@
+# exclude all tests
+test
+utils/node6-transform
+
+# exclude internal type definition files
+/lib/*.d.ts
+/node6/lib/*.d.ts
+
+# repeats from .gitignore
+node_modules
+.local-chromium
+.dev_profile*
+.DS_Store
+*.swp
+*.pyc
+.vscode
+package-lock.json
+/node6/test
+/node6/utils
+/test
+/utils
+/docs
+yarn.lock
+
+# other
+/.ci
+/examples
+.appveyour.yml
+.cirrus.yml
+.editorconfig
+.eslintignore
+.eslintrc.js
+.travis.yml
+README.md
+tsconfig.json
+experimental
+
+# exclude types, see https://github.com/GoogleChrome/puppeteer/issues/3878
+/index.d.ts
new file mode 100644
--- /dev/null
+++ b/remote/test/puppeteer/.travis.yml
@@ -0,0 +1,44 @@
+language: node_js
+dist: trusty
+addons:
+  apt:
+    packages:
+      # This is required to run new chrome on old trusty
+      - libnss3
+notifications:
+  email: false
+cache:
+  directories:
+    - node_modules
+# allow headful tests
+before_install:
+  - "sysctl kernel.unprivileged_userns_clone=1"
+  - "export DISPLAY=:99.0"
+  - "sh -e /etc/init.d/xvfb start"
+  - 'if [ "$NODE8" = "true" ]; then cd experimental/puppeteer-firefox && npm i && cd ../..; fi'
+script:
+  - 'if [ "$NODE8" = "true" ]; then npm run lint; fi'
+  - 'if [ "$NODE8" = "true" ]; then npm run coverage; fi'
+  - 'if [ "$NODE8" = "true" ]; then npm run funit; fi'
+  - 'if [ "$NODE8" = "true" ]; then npm run test-doclint; fi'
+  - 'if [ "$NODE8" = "true" ]; then npm run test-types; fi'
+  - 'if [ "$NODE8" = "true" ]; then npm run bundle; fi'
+  - 'if [ "$NODE8" = "true" ]; then npm run unit-bundle; fi'
+  - 'if [ "$NODE6" = "true" ]; then npm run unit-node6; fi'
+jobs:
+  include:
+    - node_js: "8.11.3"
+      env: NODE8=true
+    - node_js: "6.12.3"
+      env: NODE6=true
+before_deploy: "npm run apply-next-version"
+deploy:
+  provider: npm
+  email: aslushnikov@gmail.com
+  api_key:
+    secure: Ng8o2KwJf90XCBNgUKK3jRZnwtdBSJatjYNmZBERJEqBWFTadFAp1NdhxZaqjnuG8aFYaH5bRJdL+EQBYUksVCbrv/gcaXeEFkwsfPfVX1QXGqu7NnZmtme2hbxppLQ7dEJ8hz2Z9K4vehqVOxmLabxvoupOumxEQMLCphVHh2FOmsm/S5JrRZqZ4V9k76eIc0/PiyfXNMdx5WTZjHbIRDIHRy9nqOXjFp2Rx3PMa3uU2fS8mTshYEYs151TA6e6VdHjqmBwEQC/M5tXbDlLCMNUr4JBtLTcL4OipNYjzkwD1N2xYlbSRqtvqqF4ifdvFhoI65a31GinlMC7Z/SH1Zy+d+/z3Mo7D63eYcsJVnsg9OYxTFy2piUntr0JqTBHtQoe/CvGxJmkcVt+H6YSkcBibSG9s9tG3qpAD5wBCFqqOYnfClX+YZziEd+Hngd9inxAf87qdvgVIZ5tPD2dygtE+te2/qoEHtvccv/HuS8MxNj5iKwlP7JaBPM6uAkazYqZP2R99I2ph9gNOEVuQLtk+3+OIdb8HWrEKUrJBgKhdKY1dvcKYElI+D8NRlyzrr6BnZfudACuAt2EtfKpfJ3mL+iRMFdBJ3ntLt93xBrB+j4z3pD0iWZcg1g3I742PFzQEHzyd/DDTP1yRTUoJeQWwoQRJyNO1m6Qk4wx77c=
+  on:
+    branch: master
+    condition: "$NODE8 = true"
+  skip_cleanup: true
+  tag: next
new file mode 100644
--- /dev/null
+++ b/remote/test/puppeteer/CONTRIBUTING.md
@@ -0,0 +1,266 @@
+<!-- gen:toc -->
+- [How to Contribute](#how-to-contribute)
+  * [Contributor License Agreement](#contributor-license-agreement)
+  * [Getting setup](#getting-setup)
+  * [Code reviews](#code-reviews)
+  * [Code Style](#code-style)
+  * [API guidelines](#api-guidelines)
+  * [Commit Messages](#commit-messages)
+  * [Writing Documentation](#writing-documentation)
+  * [Adding New Dependencies](#adding-new-dependencies)
+  * [Writing Tests](#writing-tests)
+  * [Public API Coverage](#public-api-coverage)
+  * [Debugging Puppeteer](#debugging-puppeteer)
+- [For Project Maintainers](#for-project-maintainers)
+  * [Releasing to NPM](#releasing-to-npm)
+  * [Updating NPM dist tags](#updating-npm-dist-tags)
+<!-- gen:stop -->
+
+# How to Contribute
+
+First of all, thank you for your interest in Puppeteer!
+We'd love to accept your patches and contributions!
+
+## Contributor License Agreement
+
+Contributions to this project must be accompanied by a Contributor License
+Agreement. You (or your employer) retain the copyright to your contribution,
+this simply gives us permission to use and redistribute your contributions as
+part of the project. Head over to <https://cla.developers.google.com/> to see
+your current agreements on file or to sign a new one.
+
+You generally only need to submit a CLA once, so if you've already submitted one
+(even if it was for a different project), you probably don't need to do it
+again.
+
+## Getting setup
+
+1. Clone this repository
+
+```bash
+git clone https://github.com/GoogleChrome/puppeteer
+cd puppeteer
+```
+
+2. Install dependencies
+
+```bash
+npm install
+```
+
+## Code reviews
+
+All submissions, including submissions by project members, require review. We
+use GitHub pull requests for this purpose. Consult
+[GitHub Help](https://help.github.com/articles/about-pull-requests/) for more
+information on using pull requests.
+
+## Code Style
+
+- Coding style is fully defined in [.eslintrc](https://github.com/GoogleChrome/puppeteer/blob/master/.eslintrc.js)
+- Code should be annotated with [closure annotations](https://github.com/google/closure-compiler/wiki/Annotating-JavaScript-for-the-Closure-Compiler).
+- Comments should be generally avoided. If the code would not be understood without comments, consider re-writing the code to make it self-explanatory.
+
+To run code linter, use:
+
+```bash
+npm run lint
+```
+
+## API guidelines
+
+When authoring new API methods, consider the following:
+- Expose as little information as needed. When in doubt, don’t expose new information.
+- Methods are used in favor of getters/setters.
+  - The only exception is namespaces, e.g. `page.keyboard` and `page.coverage`
+- All string literals must be small case. This includes event names and option values.
+- Avoid adding "sugar" API (API that is trivially implementable in user-space) unless they're **very** demanded.
+
+## Commit Messages
+
+Commit messages should follow the Semantic Commit Messages format:
+
+```
+label(namespace): title
+
+description
+
+footer
+```
+
+1. *label* is one of the following:
+    - `fix` - puppeteer bug fixes.
+    - `feat` - puppeteer features.
+    - `docs` - changes to docs, e.g. `docs(api.md): ..` to change documentation.
+    - `test` - changes to puppeteer tests infrastructure.
+    - `style` - puppeteer code style: spaces/alignment/wrapping etc.
+    - `chore` - build-related work, e.g. doclint changes / travis / appveyor.
+2. *namespace* is put in parenthesis after label and is optional.
+3. *title* is a brief summary of changes.
+4. *description* is **optional**, new-line separated from title and is in present tense.
+5. *footer* is **optional**, new-line separated from *description* and contains "fixes" / "references" attribution to github issues.
+6. *footer* should also include "BREAKING CHANGE" if current API clients will break due to this change. It should explain what changed and how to get the old behavior.
+
+Example:
+
+```
+fix(Page): fix page.pizza method
+
+This patch fixes page.pizza so that it works with iframes.
+
+Fixes #123, Fixes #234
+
+BREAKING CHANGE: page.pizza now delivers pizza at home by default.
+To deliver to a different location, use "deliver" option:
+  `page.pizza({deliver: 'work'})`.
+```
+
+## Writing Documentation
+
+All public API should have a descriptive entry in the [docs/api.md](https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md). There's a [documentation linter](https://github.com/GoogleChrome/puppeteer/tree/master/utils/doclint) which makes sure documentation is aligned with the codebase.
+
+To run documentation linter, use:
+
+```bash
+npm run doc
+```
+
+## Adding New Dependencies
+
+For all dependencies (both installation and development):
+- **Do not add** a dependency if the desired functionality is easily implementable.
+- If adding a dependency, it should be well-maintained and trustworthy.
+
+A barrier for introducing new installation dependencies is especially high:
+- **Do not add** installation dependency unless it's critical to project success.
+
+## Writing Tests
+
+- Every feature should be accompanied by a test.
+- Every public api event/method should be accompanied by a test.
+- Tests should be *hermetic*. Tests should not depend on external services.
+- Tests should work on all three platforms: Mac, Linux and Win. This is especially important for screenshot tests.
+
+Puppeteer tests are located in [test/test.js](https://github.com/GoogleChrome/puppeteer/blob/master/test/test.js)
+and are written with a [TestRunner](https://github.com/GoogleChrome/puppeteer/tree/master/utils/testrunner) framework.
+Despite being named 'unit', these are integration tests, making sure public API methods and events work as expected.
+
+- To run all tests:
+
+```bash
+npm run unit
+```
+
+- To run tests in parallel, use `-j` flag:
+
+```bash
+npm run unit -- -j 4
+```
+
+- To run a specific test, substitute the `it` with `fit` (mnemonic rule: '*focus it*'):
+
+```js
+  ...
+  // Using "fit" to run specific test
+  fit('should work', async function({server, page}) {
+    const response = await page.goto(server.EMPTY_PAGE);
+    expect(response.ok).toBe(true);
+  })
+```
+
+- To disable a specific test, substitute the `it` with `xit` (mnemonic rule: '*cross it*'):
+
+```js
+  ...
+  // Using "xit" to skip specific test
+  xit('should work', async function({server, page}) {
+    const response = await page.goto(server.EMPTY_PAGE);
+    expect(response.ok).toBe(true);
+  })
+```
+
+- To run tests in non-headless mode:
+
+```bash
+HEADLESS=false npm run unit
+```
+
+- To run tests with custom Chromium executable:
+
+```bash
+CHROME=<path-to-executable> npm run unit
+```
+
+- To run tests in slow-mode:
+
+```bash
+HEADLESS=false SLOW_MO=500 npm run unit
+```
+
+- To debug a test, "focus" a test first and then run:
+
+```bash
+node --inspect-brk test/test.js
+```
+
+## Public API Coverage
+
+Every public API method or event should be called at least once in tests. To ensure this, there's a coverage command which tracks calls to public API and reports back if some methods/events were not called.
+
+Run coverage:
+
+```bash
+npm run coverage
+```
+
+## Debugging Puppeteer
+
+See [Debugging Tips](README.md#debugging-tips) in the readme.
+
+# For Project Maintainers
+
+## Releasing to NPM
+
+Releasing to NPM consists of 3 phases:
+1. Source Code: mark a release.
+    1. Bump `package.json` version following the SEMVER rules and send a PR titled `'chore: mark version vXXX.YYY.ZZZ'` ([example](https://github.com/GoogleChrome/puppeteer/commit/808bf8e5582482a1d849ff22a51e52024810905c)).
+    2. Make sure the PR passes **all checks**.
+        - **WHY**: there are linters in place that help to avoid unnecessary errors, e.g. [like this](https://github.com/GoogleChrome/puppeteer/pull/2446)
+    3. Merge the PR.
+    4. Once merged, publish release notes using the "create new tag" option.
+        - **NOTE**: tag names are prefixed with `'v'`, e.g. for version `1.4.0` tag is `v1.4.0`.
+2. Publish `puppeteer` to NPM.
+    1. On your local machine, pull from [upstream](https://github.com/GoogleChrome/puppeteer) and make sure the last commit is the one just merged.
+    2. Run `git status` and make sure there are no untracked files.
+        - **WHY**: this is to avoid bundling unnecessary files to NPM package
+    3. Run [`pkgfiles`](https://www.npmjs.com/package/pkgfiles) to make sure you don't publish anything unnecessary.
+    4. Run `npm publish`. This will publish `puppeteer` package.
+3. Publish `puppeteer-core` to NPM.
+    1. Run `./utils/prepare_puppeteer_core.js`. The script will change the name inside `package.json` to `puppeteer-core`.
+    2. Run `npm publish`. This will publish `puppeteer-core` package.
+    3. Run `git reset --hard` to reset the changes to `package.json`.
+4. Source Code: mark post-release.
+    1. Bump `package.json` version to `-post` version and send a PR titled `'chore: bump version to vXXX.YYY.ZZZ-post'` ([example](https://github.com/GoogleChrome/puppeteer/commit/d02440d1eac98028e29f4e1cf55413062a259156))
+        - **NOTE**: make sure to update the "released APIs" section in the top of `docs/api.md`.
+        - **NOTE**: no other commits should be landed in-between release commit and bump commit.
+
+## Updating NPM dist tags
+
+For both `puppeteer` and `puppeteer-firefox` we maintain the following NPM Tags:
+- `chrome-*` tags, e.g. `chrome-75` and so on. These tags match Puppeteer version that corresponds to the `chrome-*` release.
+- `chrome-stable` tag. This tag points to the Puppeteer version that works with current Chrome stable.
+
+These tags are updated on every Puppeteer release.
+
+> **NOTE**: due to Chrome's rolling release, we take [omahaproxy's linux stable version](https://omahaproxy.appspot.com/) as *stable*.
+
+Manging tags 101:
+
+```bash
+# list tags
+$ npm dist-tag ls puppeteer
+# Removing a tag
+$ npm dist-tag rm puppeteer-core chrome-stable
+# Adding a tag
+$ npm dist-tag add puppeteer-core@1.13.0 chrome-stable
+```
new file mode 100644
--- /dev/null
+++ b/remote/test/puppeteer/DeviceDescriptors.js
@@ -0,0 +1,28 @@
+/**
+ * Copyright 2019 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+let asyncawait = true;
+try {
+  new Function('async function test(){await 1}');
+} catch (error) {
+  asyncawait = false;
+}
+
+// If node does not support async await, use the compiled version.
+if (asyncawait)
+  module.exports = require('./lib/DeviceDescriptors');
+else
+  module.exports = require('./node6/lib/DeviceDescriptors');
new file mode 100644
--- /dev/null
+++ b/remote/test/puppeteer/Errors.js
@@ -0,0 +1,28 @@
+/**
+ * Copyright 2018 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+let asyncawait = true;
+try {
+  new Function('async function test(){await 1}');
+} catch (error) {
+  asyncawait = false;
+}
+
+// If node does not support async await, use the compiled version.
+if (asyncawait)
+  module.exports = require('./lib/Errors');
+else
+  module.exports = require('./node6/lib/Errors');
new file mode 100644
--- /dev/null
+++ b/remote/test/puppeteer/LICENSE
@@ -0,0 +1,202 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright 2017 Google Inc.
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
new file mode 100644
--- /dev/null
+++ b/remote/test/puppeteer/README.md
@@ -0,0 +1,401 @@
+# Puppeteer
+
+<!-- [START badges] -->
+[![Linux Build Status](https://img.shields.io/travis/com/GoogleChrome/puppeteer/master.svg)](https://travis-ci.com/GoogleChrome/puppeteer) [![Windows Build Status](https://img.shields.io/appveyor/ci/aslushnikov/puppeteer/master.svg?logo=appveyor)](https://ci.appveyor.com/project/aslushnikov/puppeteer/branch/master) [![Build Status](https://api.cirrus-ci.com/github/GoogleChrome/puppeteer.svg)](https://cirrus-ci.com/github/GoogleChrome/puppeteer) [![NPM puppeteer package](https://img.shields.io/npm/v/puppeteer.svg)](https://npmjs.org/package/puppeteer)
+<!-- [END badges] -->
+
+<img src="https://user-images.githubusercontent.com/10379601/29446482-04f7036a-841f-11e7-9872-91d1fc2ea683.png" height="200" align="right">
+
+###### [API](https://github.com/GoogleChrome/puppeteer/blob/v1.17.0/docs/api.md) | [FAQ](#faq) | [Contributing](https://github.com/GoogleChrome/puppeteer/blob/master/CONTRIBUTING.md) | [Troubleshooting](https://github.com/GoogleChrome/puppeteer/blob/master/docs/troubleshooting.md)
+
+> Puppeteer is a Node library which provides a high-level API to control Chrome or Chromium over the [DevTools Protocol](https://chromedevtools.github.io/devtools-protocol/). Puppeteer runs [headless](https://developers.google.com/web/updates/2017/04/headless-chrome) by default, but can be configured to run full (non-headless) Chrome or Chromium.
+
+<!-- [START usecases] -->
+###### What can I do?
+
+Most things that you can do manually in the browser can be done using Puppeteer! Here are a few examples to get you started:
+
+* Generate screenshots and PDFs of pages.
+* Crawl a SPA (Single-Page Application) and generate pre-rendered content (i.e. "SSR" (Server-Side Rendering)).
+* Automate form submission, UI testing, keyboard input, etc.
+* Create an up-to-date, automated testing environment. Run your tests directly in the latest version of Chrome using the latest JavaScript and browser features.
+* Capture a [timeline trace](https://developers.google.com/web/tools/chrome-devtools/evaluate-performance/reference) of your site to help diagnose performance issues.
+* Test Chrome Extensions.
+<!-- [END usecases] -->
+
+Give it a spin: https://try-puppeteer.appspot.com/
+
+<!-- [START getstarted] -->
+## Getting Started
+
+### Installation
+
+To use Puppeteer in your project, run:
+
+```bash
+npm i puppeteer
+# or "yarn add puppeteer"
+```
+
+Note: When you install Puppeteer, it downloads a recent version of Chromium (~170MB Mac, ~282MB Linux, ~280MB Win) that is guaranteed to work with the API. To skip the download, see [Environment variables](https://github.com/GoogleChrome/puppeteer/blob/v1.17.0/docs/api.md#environment-variables).
+
+
+### puppeteer-core
+
+Since version 1.7.0 we publish the [`puppeteer-core`](https://www.npmjs.com/package/puppeteer-core) package,
+a version of Puppeteer that doesn't download Chromium by default.
+
+```bash
+npm i puppeteer-core
+# or "yarn add puppeteer-core"
+```
+
+`puppeteer-core` is intended to be a lightweight version of Puppeteer for launching an existing browser installation or for connecting to a remote one. Be sure that the version of puppeteer-core you install is compatible with the
+browser you intend to connect to.
+
+See [puppeteer vs puppeteer-core](https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#puppeteer-vs-puppeteer-core).
+
+### Usage
+
+Note: Puppeteer requires at least Node v6.4.0, but the examples below use async/await which is only supported in Node v7.6.0 or greater.
+
+Puppeteer will be familiar to people using other browser testing frameworks. You create an instance
+of `Browser`, open pages, and then manipulate them with [Puppeteer's API](https://github.com/GoogleChrome/puppeteer/blob/v1.17.0/docs/api.md#).
+
+**Example** - navigating to https://example.com and saving a screenshot as *example.png*:
+
+Save file as **example.js**
+
+```js
+const puppeteer = require('puppeteer');
+
+(async () => {
+  const browser = await puppeteer.launch();
+  const page = await browser.newPage();
+  await page.goto('https://example.com');
+  await page.screenshot({path: 'example.png'});
+
+  await browser.close();
+})();
+```
+
+Execute script on the command line
+
+```bash
+node example.js
+```
+
+Puppeteer sets an initial page size to 800px x 600px, which defines the screenshot size. The page size can be customized  with [`Page.setViewport()`](https://github.com/GoogleChrome/puppeteer/blob/v1.17.0/docs/api.md#pagesetviewportviewport).
+
+**Example** - create a PDF.
+
+Save file as **hn.js**
+
+```js
+const puppeteer = require('puppeteer');
+
+(async () => {
+  const browser = await puppeteer.launch();
+  const page = await browser.newPage();
+  await page.goto('https://news.ycombinator.com', {waitUntil: 'networkidle2'});
+  await page.pdf({path: 'hn.pdf', format: 'A4'});
+
+  await browser.close();
+})();
+```
+
+Execute script on the command line
+
+```bash
+node hn.js
+```
+
+See [`Page.pdf()`](https://github.com/GoogleChrome/puppeteer/blob/v1.17.0/docs/api.md#pagepdfoptions) for more information about creating pdfs.
+
+**Example** - evaluate script in the context of the page
+
+Save file as **get-dimensions.js**
+
+```js
+const puppeteer = require('puppeteer');
+
+(async () => {
+  const browser = await puppeteer.launch();
+  const page = await browser.newPage();
+  await page.goto('https://example.com');
+
+  // Get the "viewport" of the page, as reported by the page.
+  const dimensions = await page.evaluate(() => {
+    return {
+      width: document.documentElement.clientWidth,
+      height: document.documentElement.clientHeight,
+      deviceScaleFactor: window.devicePixelRatio
+    };
+  });
+
+  console.log('Dimensions:', dimensions);
+
+  await browser.close();
+})();
+```
+
+Execute script on the command line
+
+```bash
+node get-dimensions.js
+```
+
+See [`Page.evaluate()`](https://github.com/GoogleChrome/puppeteer/blob/v1.17.0/docs/api.md#pageevaluatepagefunction-args) for more information on `evaluate` and related methods like `evaluateOnNewDocument` and `exposeFunction`.
+
+<!-- [END getstarted] -->
+
+<!-- [START runtimesettings] -->
+## Default runtime settings
+
+**1. Uses Headless mode**
+
+Puppeteer launches Chromium in [headless mode](https://developers.google.com/web/updates/2017/04/headless-chrome). To launch a full version of Chromium, set the ['headless' option](https://github.com/GoogleChrome/puppeteer/blob/v1.17.0/docs/api.md#puppeteerlaunchoptions) when launching a browser:
+
+```js
+const browser = await puppeteer.launch({headless: false}); // default is true
+```
+
+**2. Runs a bundled version of Chromium**
+
+By default, Puppeteer downloads and uses a specific version of Chromium so its API
+is guaranteed to work out of the box. To use Puppeteer with a different version of Chrome or Chromium,
+pass in the executable's path when creating a `Browser` instance:
+
+```js
+const browser = await puppeteer.launch({executablePath: '/path/to/Chrome'});
+```
+
+See [`Puppeteer.launch()`](https://github.com/GoogleChrome/puppeteer/blob/v1.17.0/docs/api.md#puppeteerlaunchoptions) for more information.
+
+See [`this article`](https://www.howtogeek.com/202825/what%E2%80%99s-the-difference-between-chromium-and-chrome/) for a description of the differences between Chromium and Chrome. [`This article`](https://chromium.googlesource.com/chromium/src/+/master/docs/chromium_browser_vs_google_chrome.md) describes some differences for Linux users.
+
+**3. Creates a fresh user profile**
+
+Puppeteer creates its own Chromium user profile which it **cleans up on every run**.
+
+<!-- [END runtimesettings] -->
+
+## Resources
+
+- [API Documentation](https://github.com/GoogleChrome/puppeteer/blob/v1.17.0/docs/api.md)
+- [Examples](https://github.com/GoogleChrome/puppeteer/tree/master/examples/)
+- [Community list of Puppeteer resources](https://github.com/transitive-bullshit/awesome-puppeteer)
+
+
+<!-- [START debugging] -->
+
+## Debugging tips
+
+1. Turn off headless mode - sometimes it's useful to see what the browser is
+   displaying. Instead of launching in headless mode, launch a full version of
+   the browser using  `headless: false`:
+
+        const browser = await puppeteer.launch({headless: false});
+
+2. Slow it down - the `slowMo` option slows down Puppeteer operations by the
+   specified amount of milliseconds. It's another way to help see what's going on.
+
+        const browser = await puppeteer.launch({
+          headless: false,
+          slowMo: 250 // slow down by 250ms
+        });
+
+3. Capture console output - You can listen for the `console` event.
+   This is also handy when debugging code in `page.evaluate()`:
+
+        page.on('console', msg => console.log('PAGE LOG:', msg.text()));
+
+        await page.evaluate(() => console.log(`url is ${location.href}`));
+
+4. Use debugger in application code browser
+
+    There are two execution context: node.js that is running test code, and the browser
+    running application code being tested. This lets you debug code in the
+    application code browser; ie code inside `evaluate()`.
+
+    - Use `{devtools: true}` when launching Puppeteer:
+
+        `const browser = await puppeteer.launch({devtools: true});`
+
+    - Change default test timeout:
+
+        jest: `jest.setTimeout(100000);`
+
+        jasmine: `jasmine.DEFAULT_TIMEOUT_INTERVAL = 100000;`
+
+        mocha: `this.timeout(100000);` (don't forget to change test to use [function and not '=>'](https://stackoverflow.com/a/23492442))
+
+    - Add an evaluate statement with `debugger` inside / add  `debugger` to an existing evaluate statement:
+
+      `await page.evaluate(() => {debugger;});`
+
+       The test will now stop executing in the above evaluate statement, and chromium will stop in debug mode.
+
+5. Use debugger in node.js
+
+    This will let you debug test code. For example, you can step over `await page.click()` in the node.js script and see the click happen in the application code browser.
+
+    Note that you won't be able to run `await page.click()` in
+    DevTools console due to this [Chromium bug](https://bugs.chromium.org/p/chromium/issues/detail?id=833928). So if
+    you want to try something out, you have to add it to your test file.
+
+    - Add `debugger;` to your test, eg:
+      ```
+      debugger;
+      await page.click('a[target=_blank]');
+      ```
+    - Set `headless` to `false`
+    - Run `node --inspect-brk`, eg `node --inspect-brk node_modules/.bin/jest tests`
+    - In Chrome open `chrome://inspect/#devices` and click `inspect`
+    - In the newly opened test browser, type `F8` to resume test execution
+    - Now your `debugger` will be hit and you can debug in the test browser
+
+
+6. Enable verbose logging - internal DevTools protocol traffic
+   will be logged via the [`debug`](https://github.com/visionmedia/debug) module under the `puppeteer` namespace.
+
+        # Basic verbose logging
+        env DEBUG="puppeteer:*" node script.js
+
+        # Protocol traffic can be rather noisy. This example filters out all Network domain messages
+        env DEBUG="puppeteer:*" env DEBUG_COLORS=true node script.js 2>&1 | grep -v '"Network'
+
+7. Debug your Puppeteer (node) code easily, using [ndb](https://github.com/GoogleChromeLabs/ndb)
+
+  - `npm install -g ndb` (or even better, use [npx](https://github.com/zkat/npx)!)
+
+  - add a `debugger` to your Puppeteer (node) code
+
+  - add `ndb` (or `npx ndb`) before your test command. For example:
+
+    `ndb jest` or `ndb mocha` (or `npx ndb jest` / `npx ndb mocha`)
+
+  - debug your test inside chromium like a boss!
+
+
+<!-- [END debugging] -->
+
+## Contributing to Puppeteer
+
+Check out [contributing guide](https://github.com/GoogleChrome/puppeteer/blob/master/CONTRIBUTING.md) to get an overview of Puppeteer development.
+
+<!-- [START faq] -->
+
+# FAQ
+
+#### Q: Who maintains Puppeteer?
+
+The Chrome DevTools team maintains the library, but we'd love your help and expertise on the project!
+See [Contributing](https://github.com/GoogleChrome/puppeteer/blob/master/CONTRIBUTING.md).
+
+#### Q: What are Puppeteer’s goals and principles?
+
+The goals of the project are:
+
+- Provide a slim, canonical library that highlights the capabilities of the [DevTools Protocol](https://chromedevtools.github.io/devtools-protocol/).
+- Provide a reference implementation for similar testing libraries. Eventually, these other frameworks could adopt Puppeteer as their foundational layer.
+- Grow the adoption of headless/automated browser testing.
+- Help dogfood new DevTools Protocol features...and catch bugs!
+- Learn more about the pain points of automated browser testing and help fill those gaps.
+
+We adapt [Chromium principles](https://www.chromium.org/developers/core-principles) to help us drive product decisions:
+- **Speed**: Puppeteer has almost zero performance overhead over an automated page.
+- **Security**: Puppeteer operates off-process with respect to Chromium, making it safe to automate potentially malicious pages.
+- **Stability**: Puppeteer should not be flaky and should not leak memory.
+- **Simplicity**: Puppeteer provides a high-level API that’s easy to use, understand, and debug.
+
+#### Q: Is Puppeteer replacing Selenium/WebDriver?
+
+**No**. Both projects are valuable for very different reasons:
+- Selenium/WebDriver focuses on cross-browser automation; its value proposition is a single standard API that works across all major browsers.
+- Puppeteer focuses on Chromium; its value proposition is richer functionality and higher reliability.
+
+That said, you **can** use Puppeteer to run tests against Chromium, e.g. using the community-driven [jest-puppeteer](https://github.com/smooth-code/jest-puppeteer). While this probably shouldn’t be your only testing solution, it does have a few good points compared to WebDriver:
+
+- Puppeteer requires zero setup and comes bundled with the Chromium version it works best with, making it [very easy to start with](https://github.com/GoogleChrome/puppeteer/#getting-started). At the end of the day, it’s better to have a few tests running chromium-only, than no tests at all.
+- Puppeteer has event-driven architecture, which removes a lot of potential flakiness. There’s no need for evil “sleep(1000)” calls in puppeteer scripts.
+- Puppeteer runs headless by default, which makes it fast to run. Puppeteer v1.5.0 also exposes browser contexts, making it possible to efficiently parallelize test execution.
+- Puppeteer shines when it comes to debugging: flip the “headless” bit to false, add “slowMo”, and you’ll see what the browser is doing. You can even open Chrome DevTools to inspect the test environment.
+
+#### Q: Why doesn’t Puppeteer v.XXX work with Chromium v.YYY?
+
+We see Puppeteer as an **indivisible entity** with Chromium. Each version of Puppeteer bundles a specific version of Chromium – **the only** version it is guaranteed to work with.
+
+This is not an artificial constraint: A lot of work on Puppeteer is actually taking place in the Chromium repository. Here’s a typical story:
+- A Puppeteer bug is reported: https://github.com/GoogleChrome/puppeteer/issues/2709
+- It turned out this is an issue with the DevTools protocol, so we’re fixing it in Chromium: https://chromium-review.googlesource.com/c/chromium/src/+/1102154
+- Once the upstream fix is landed, we roll updated Chromium into Puppeteer: https://github.com/GoogleChrome/puppeteer/pull/2769
+
+However, oftentimes it is desirable to use Puppeteer with the official Google Chrome rather than Chromium. For this to work, you should install a `puppeteer-core` version that corresponds to the Chrome version.
+
+For example, in order to drive Chrome 71 with puppeteer-core, use `chrome-71` npm tag:
+```bash
+npm install puppeteer-core@chrome-71
+```
+
+#### Q: Which Chromium version does Puppeteer use?
+
+Look for `chromium_revision` in [package.json](https://github.com/GoogleChrome/puppeteer/blob/master/package.json).
+
+#### Q: What’s considered a “Navigation”?
+
+From Puppeteer’s standpoint, **“navigation” is anything that changes a page’s URL**.
+Aside from regular navigation where the browser hits the network to fetch a new document from the web server, this includes [anchor navigations](https://www.w3.org/TR/html5/single-page.html#scroll-to-fragid) and [History API](https://developer.mozilla.org/en-US/docs/Web/API/History_API) usage.
+
+With this definition of “navigation,” **Puppeteer works seamlessly with single-page applications.**
+
+#### Q: What’s the difference between a “trusted" and "untrusted" input event?
+
+In browsers, input events could be divided into two big groups: trusted vs. untrusted.
+
+- **Trusted events**: events generated by users interacting with the page, e.g. using a mouse or keyboard.
+- **Untrusted event**: events generated by Web APIs, e.g. `document.createEvent` or `element.click()` methods.
+
+Websites can distinguish between these two groups:
+- using an [`Event.isTrusted`](https://developer.mozilla.org/en-US/docs/Web/API/Event/isTrusted) event flag
+- sniffing for accompanying events. For example, every trusted `'click'` event is preceded by `'mousedown'` and `'mouseup'` events.
+
+For automation purposes it’s important to generate trusted events. **All input events generated with Puppeteer are trusted and fire proper accompanying events.** If, for some reason, one needs an untrusted event, it’s always possible to hop into a page context with `page.evaluate` and generate a fake event:
+
+```js
+await page.evaluate(() => {
+  document.querySelector('button[type=submit]').click();
+});
+```
+
+#### Q: What features does Puppeteer not support?
+
+You may find that Puppeteer does not behave as expected when controlling pages that incorporate audio and video. (For example, [video playback/screenshots is likely to fail](https://github.com/GoogleChrome/puppeteer/issues/291).) There are two reasons for this:
+
+* Puppeteer is bundled with Chromium--not Chrome--and so by default, it inherits all of [Chromium's media-related limitations](https://www.chromium.org/audio-video). This means that Puppeteer does not support licensed formats such as AAC or H.264. (However, it is possible to force Puppeteer to use a separately-installed version Chrome instead of Chromium via the [`executablePath` option to `puppeteer.launch`](https://github.com/GoogleChrome/puppeteer/blob/v1.17.0/docs/api.md#puppeteerlaunchoptions). You should only use this configuration if you need an official release of Chrome that supports these media formats.)
+* Since Puppeteer (in all configurations) controls a desktop version of Chromium/Chrome, features that are only supported by the mobile version of Chrome are not supported. This means that Puppeteer [does not support HTTP Live Streaming (HLS)](https://caniuse.com/#feat=http-live-streaming).
+
+#### Q: I am having trouble installing / running Puppeteer in my test environment. Where should I look for help?
+We have a [troubleshooting](https://github.com/GoogleChrome/puppeteer/blob/master/docs/troubleshooting.md) guide for various operating systems that lists the required dependencies.
+
+#### Q: How do I try/test a prerelease version of Puppeteer?
+
+You can check out this repo or install the latest prerelease from npm:
+
+```bash
+npm i --save puppeteer@next
+```
+
+Please note that prerelease may be unstable and contain bugs.
+
+#### Q: I have more questions! Where do I ask?
+
+There are many ways to get help on Puppeteer:
+- [bugtracker](https://github.com/GoogleChrome/puppeteer/issues)
+- [stackoverflow](https://stackoverflow.com/questions/tagged/puppeteer)
+- [slack channel](https://join.slack.com/t/puppeteer/shared_invite/enQtMzU4MjIyMDA5NTM4LTM1OTdkNDhlM2Y4ZGUzZDdjYjM5ZWZlZGFiZjc4MTkyYTVlYzIzYjU5NDIyNzgyMmFiNDFjN2UzNWU0N2ZhZDc)
+
+Make sure to search these channels before posting your question.
+
+
+<!-- [END faq] -->
new file mode 100644
--- /dev/null
+++ b/remote/test/puppeteer/docs/api.md
@@ -0,0 +1,3652 @@
+
+# Puppeteer API <!-- GEN:version -->Tip-Of-Tree<!-- GEN:stop-->
+<!-- GEN:empty-if-release -->
+#### Next Release: `June 20, 2019`
+<!-- GEN:stop -->
+
+- Interactive Documentation: https://pptr.dev
+- API Translations: [中文|Chinese](https://zhaoqize.github.io/puppeteer-api-zh_CN/#/)
+- Troubleshooting: [troubleshooting.md](https://github.com/GoogleChrome/puppeteer/blob/master/docs/troubleshooting.md)
+- Releases per Chromium Version:
+  * Chromium 76.0.3803.0 - [Puppeteer v1.17.0](https://github.com/GoogleChrome/puppeteer/blob/v1.17.0/docs/api.md)
+  * Chromium 75.0.3765.0 - [Puppeteer v1.15.0](https://github.com/GoogleChrome/puppeteer/blob/v1.15.0/docs/api.md)
+  * Chromium 74.0.3723.0 - [Puppeteer v1.13.0](https://github.com/GoogleChrome/puppeteer/blob/v1.13.0/docs/api.md)
+  * Chromium 73.0.3679.0 - [Puppeteer v1.12.2](https://github.com/GoogleChrome/puppeteer/blob/v1.12.2/docs/api.md)
+  * Chromium 72.0.3582.0 - [Puppeteer v1.11.0](https://github.com/GoogleChrome/puppeteer/blob/v1.11.0/docs/api.md)
+  * [All releases](https://github.com/GoogleChrome/puppeteer/releases)
+
+
+##### Table of Contents
+
+<!-- GEN:toc -->
+- [Overview](#overview)
+- [puppeteer vs puppeteer-core](#puppeteer-vs-puppeteer-core)
+- [Environment Variables](#environment-variables)
+- [Working with Chrome Extensions](#working-with-chrome-extensions)
+- [class: Puppeteer](#class-puppeteer)
+  * [puppeteer.connect(options)](#puppeteerconnectoptions)
+  * [puppeteer.createBrowserFetcher([options])](#puppeteercreatebrowserfetcheroptions)
+  * [puppeteer.defaultArgs([options])](#puppeteerdefaultargsoptions)
+  * [puppeteer.devices](#puppeteerdevices)
+  * [puppeteer.errors](#puppeteererrors)
+  * [puppeteer.executablePath()](#puppeteerexecutablepath)
+  * [puppeteer.launch([options])](#puppeteerlaunchoptions)
+- [class: BrowserFetcher](#class-browserfetcher)
+  * [browserFetcher.canDownload(revision)](#browserfetchercandownloadrevision)
+  * [browserFetcher.download(revision[, progressCallback])](#browserfetcherdownloadrevision-progresscallback)
+  * [browserFetcher.localRevisions()](#browserfetcherlocalrevisions)
+  * [browserFetcher.platform()](#browserfetcherplatform)
+  * [browserFetcher.remove(revision)](#browserfetcherremoverevision)
+  * [browserFetcher.revisionInfo(revision)](#browserfetcherrevisioninforevision)
+- [class: Browser](#class-browser)
+  * [event: 'disconnected'](#event-disconnected)
+  * [event: 'targetchanged'](#event-targetchanged)
+  * [event: 'targetcreated'](#event-targetcreated)
+  * [event: 'targetdestroyed'](#event-targetdestroyed)
+  * [browser.browserContexts()](#browserbrowsercontexts)
+  * [browser.close()](#browserclose)
+  * [browser.createIncognitoBrowserContext()](#browsercreateincognitobrowsercontext)
+  * [browser.defaultBrowserContext()](#browserdefaultbrowsercontext)
+  * [browser.disconnect()](#browserdisconnect)
+  * [browser.isConnected()](#browserisconnected)
+  * [browser.newPage()](#browsernewpage)
+  * [browser.pages()](#browserpages)
+  * [browser.process()](#browserprocess)
+  * [browser.target()](#browsertarget)
+  * [browser.targets()](#browsertargets)
+  * [browser.userAgent()](#browseruseragent)
+  * [browser.version()](#browserversion)
+  * [browser.waitForTarget(predicate[, options])](#browserwaitfortargetpredicate-options)
+  * [browser.wsEndpoint()](#browserwsendpoint)
+- [class: BrowserContext](#class-browsercontext)
+  * [event: 'targetchanged'](#event-targetchanged-1)
+  * [event: 'targetcreated'](#event-targetcreated-1)
+  * [event: 'targetdestroyed'](#event-targetdestroyed-1)
+  * [browserContext.browser()](#browsercontextbrowser)
+  * [browserContext.clearPermissionOverrides()](#browsercontextclearpermissionoverrides)
+  * [browserContext.close()](#browsercontextclose)
+  * [browserContext.isIncognito()](#browsercontextisincognito)
+  * [browserContext.newPage()](#browsercontextnewpage)
+  * [browserContext.overridePermissions(origin, permissions)](#browsercontextoverridepermissionsorigin-permissions)
+  * [browserContext.pages()](#browsercontextpages)
+  * [browserContext.targets()](#browsercontexttargets)
+  * [browserContext.waitForTarget(predicate[, options])](#browsercontextwaitfortargetpredicate-options)
+- [class: Page](#class-page)
+  * [event: 'close'](#event-close)
+  * [event: 'console'](#event-console)
+  * [event: 'dialog'](#event-dialog)
+  * [event: 'domcontentloaded'](#event-domcontentloaded)
+  * [event: 'error'](#event-error)
+  * [event: 'frameattached'](#event-frameattached)
+  * [event: 'framedetached'](#event-framedetached)
+  * [event: 'framenavigated'](#event-framenavigated)
+  * [event: 'load'](#event-load)
+  * [event: 'metrics'](#event-metrics)
+  * [event: 'pageerror'](#event-pageerror)
+  * [event: 'popup'](#event-popup)
+  * [event: 'request'](#event-request)
+  * [event: 'requestfailed'](#event-requestfailed)
+  * [event: 'requestfinished'](#event-requestfinished)
+  * [event: 'response'](#event-response)
+  * [event: 'workercreated'](#event-workercreated)
+  * [event: 'workerdestroyed'](#event-workerdestroyed)
+  * [page.$(selector)](#pageselector)
+  * [page.$$(selector)](#pageselector-1)
+  * [page.$$eval(selector, pageFunction[, ...args])](#pageevalselector-pagefunction-args)
+  * [page.$eval(selector, pageFunction[, ...args])](#pageevalselector-pagefunction-args-1)
+  * [page.$x(expression)](#pagexexpression)
+  * [page.accessibility](#pageaccessibility)
+  * [page.addScriptTag(options)](#pageaddscripttagoptions)
+  * [page.addStyleTag(options)](#pageaddstyletagoptions)
+  * [page.authenticate(credentials)](#pageauthenticatecredentials)
+  * [page.bringToFront()](#pagebringtofront)
+  * [page.browser()](#pagebrowser)
+  * [page.browserContext()](#pagebrowsercontext)
+  * [page.click(selector[, options])](#pageclickselector-options)
+  * [page.close([options])](#pagecloseoptions)
+  * [page.content()](#pagecontent)
+  * [page.cookies([...urls])](#pagecookiesurls)
+  * [page.coverage](#pagecoverage)
+  * [page.deleteCookie(...cookies)](#pagedeletecookiecookies)
+  * [page.emulate(options)](#pageemulateoptions)
+  * [page.emulateMedia(mediaType)](#pageemulatemediamediatype)
+  * [page.evaluate(pageFunction[, ...args])](#pageevaluatepagefunction-args)
+  * [page.evaluateHandle(pageFunction[, ...args])](#pageevaluatehandlepagefunction-args)
+  * [page.evaluateOnNewDocument(pageFunction[, ...args])](#pageevaluateonnewdocumentpagefunction-args)
+  * [page.exposeFunction(name, puppeteerFunction)](#pageexposefunctionname-puppeteerfunction)
+  * [page.focus(selector)](#pagefocusselector)
+  * [page.frames()](#pageframes)
+  * [page.goBack([options])](#pagegobackoptions)
+  * [page.goForward([options])](#pagegoforwardoptions)
+  * [page.goto(url[, options])](#pagegotourl-options)
+  * [page.hover(selector)](#pagehoverselector)
+  * [page.isClosed()](#pageisclosed)
+  * [page.keyboard](#pagekeyboard)
+  * [page.mainFrame()](#pagemainframe)
+  * [page.metrics()](#pagemetrics)
+  * [page.mouse](#pagemouse)
+  * [page.pdf([options])](#pagepdfoptions)
+  * [page.queryObjects(prototypeHandle)](#pagequeryobjectsprototypehandle)
+  * [page.reload([options])](#pagereloadoptions)
+  * [page.screenshot([options])](#pagescreenshotoptions)
+  * [page.select(selector, ...values)](#pageselectselector-values)
+  * [page.setBypassCSP(enabled)](#pagesetbypasscspenabled)
+  * [page.setCacheEnabled([enabled])](#pagesetcacheenabledenabled)
+  * [page.setContent(html[, options])](#pagesetcontenthtml-options)
+  * [page.setCookie(...cookies)](#pagesetcookiecookies)
+  * [page.setDefaultNavigationTimeout(timeout)](#pagesetdefaultnavigationtimeouttimeout)
+  * [page.setDefaultTimeout(timeout)](#pagesetdefaulttimeouttimeout)
+  * [page.setExtraHTTPHeaders(headers)](#pagesetextrahttpheadersheaders)
+  * [page.setGeolocation(options)](#pagesetgeolocationoptions)
+  * [page.setJavaScriptEnabled(enabled)](#pagesetjavascriptenabledenabled)
+  * [page.setOfflineMode(enabled)](#pagesetofflinemodeenabled)
+  * [page.setRequestInterception(value)](#pagesetrequestinterceptionvalue)
+  * [page.setUserAgent(userAgent)](#pagesetuseragentuseragent)
+  * [page.setViewport(viewport)](#pagesetviewportviewport)
+  * [page.tap(selector)](#pagetapselector)
+  * [page.target()](#pagetarget)
+  * [page.title()](#pagetitle)
+  * [page.touchscreen](#pagetouchscreen)
+  * [page.tracing](#pagetracing)
+  * [page.type(selector, text[, options])](#pagetypeselector-text-options)
+  * [page.url()](#pageurl)
+  * [page.viewport()](#pageviewport)
+  * [page.waitFor(selectorOrFunctionOrTimeout[, options[, ...args]])](#pagewaitforselectororfunctionortimeout-options-args)
+  * [page.waitForFunction(pageFunction[, options[, ...args]])](#pagewaitforfunctionpagefunction-options-args)
+  * [page.waitForNavigation([options])](#pagewaitfornavigationoptions)
+  * [page.waitForRequest(urlOrPredicate[, options])](#pagewaitforrequesturlorpredicate-options)
+  * [page.waitForResponse(urlOrPredicate[, options])](#pagewaitforresponseurlorpredicate-options)
+  * [page.waitForSelector(selector[, options])](#pagewaitforselectorselector-options)
+  * [page.waitForXPath(xpath[, options])](#pagewaitforxpathxpath-options)
+  * [page.workers()](#pageworkers)
+- [class: Worker](#class-worker)
+  * [worker.evaluate(pageFunction[, ...args])](#workerevaluatepagefunction-args)
+  * [worker.evaluateHandle(pageFunction[, ...args])](#workerevaluatehandlepagefunction-args)
+  * [worker.executionContext()](#workerexecutioncontext)
+  * [worker.url()](#workerurl)
+- [class: Accessibility](#class-accessibility)
+  * [accessibility.snapshot([options])](#accessibilitysnapshotoptions)
+- [class: Keyboard](#class-keyboard)
+  * [keyboard.down(key[, options])](#keyboarddownkey-options)
+  * [keyboard.press(key[, options])](#keyboardpresskey-options)
+  * [keyboard.sendCharacter(char)](#keyboardsendcharacterchar)
+  * [keyboard.type(text[, options])](#keyboardtypetext-options)
+  * [keyboard.up(key)](#keyboardupkey)
+- [class: Mouse](#class-mouse)
+  * [mouse.click(x, y[, options])](#mouseclickx-y-options)
+  * [mouse.down([options])](#mousedownoptions)
+  * [mouse.move(x, y[, options])](#mousemovex-y-options)
+  * [mouse.up([options])](#mouseupoptions)
+- [class: Touchscreen](#class-touchscreen)
+  * [touchscreen.tap(x, y)](#touchscreentapx-y)
+- [class: Tracing](#class-tracing)
+  * [tracing.start([options])](#tracingstartoptions)
+  * [tracing.stop()](#tracingstop)
+- [class: Dialog](#class-dialog)
+  * [dialog.accept([promptText])](#dialogacceptprompttext)
+  * [dialog.defaultValue()](#dialogdefaultvalue)
+  * [dialog.dismiss()](#dialogdismiss)
+  * [dialog.message()](#dialogmessage)
+  * [dialog.type()](#dialogtype)
+- [class: ConsoleMessage](#class-consolemessage)
+  * [consoleMessage.args()](#consolemessageargs)
+  * [consoleMessage.location()](#consolemessagelocation)
+  * [consoleMessage.text()](#consolemessagetext)
+  * [consoleMessage.type()](#consolemessagetype)
+- [class: Frame](#class-frame)
+  * [frame.$(selector)](#frameselector)
+  * [frame.$$(selector)](#frameselector-1)
+  * [frame.$$eval(selector, pageFunction[, ...args])](#frameevalselector-pagefunction-args)
+  * [frame.$eval(selector, pageFunction[, ...args])](#frameevalselector-pagefunction-args-1)
+  * [frame.$x(expression)](#framexexpression)
+  * [frame.addScriptTag(options)](#frameaddscripttagoptions)
+  * [frame.addStyleTag(options)](#frameaddstyletagoptions)
+  * [frame.childFrames()](#framechildframes)
+  * [frame.click(selector[, options])](#frameclickselector-options)
+  * [frame.content()](#framecontent)
+  * [frame.evaluate(pageFunction[, ...args])](#frameevaluatepagefunction-args)
+  * [frame.evaluateHandle(pageFunction[, ...args])](#frameevaluatehandlepagefunction-args)
+  * [frame.executionContext()](#frameexecutioncontext)
+  * [frame.focus(selector)](#framefocusselector)
+  * [frame.goto(url[, options])](#framegotourl-options)
+  * [frame.hover(selector)](#framehoverselector)
+  * [frame.isDetached()](#frameisdetached)
+  * [frame.name()](#framename)
+  * [frame.parentFrame()](#frameparentframe)
+  * [frame.select(selector, ...values)](#frameselectselector-values)
+  * [frame.setContent(html[, options])](#framesetcontenthtml-options)
+  * [frame.tap(selector)](#frametapselector)
+  * [frame.title()](#frametitle)
+  * [frame.type(selector, text[, options])](#frametypeselector-text-options)
+  * [frame.url()](#frameurl)
+  * [frame.waitFor(selectorOrFunctionOrTimeout[, options[, ...args]])](#framewaitforselectororfunctionortimeout-options-args)
+  * [frame.waitForFunction(pageFunction[, options[, ...args]])](#framewaitforfunctionpagefunction-options-args)
+  * [frame.waitForNavigation([options])](#framewaitfornavigationoptions)
+  * [frame.waitForSelector(selector[, options])](#framewaitforselectorselector-options)
+  * [frame.waitForXPath(xpath[, options])](#framewaitforxpathxpath-options)
+- [class: ExecutionContext](#class-executioncontext)
+  * [executionContext.evaluate(pageFunction[, ...args])](#executioncontextevaluatepagefunction-args)
+  * [executionContext.evaluateHandle(pageFunction[, ...args])](#executioncontextevaluatehandlepagefunction-args)
+  * [executionContext.frame()](#executioncontextframe)
+  * [executionContext.queryObjects(prototypeHandle)](#executioncontextqueryobjectsprototypehandle)
+- [class: JSHandle](#class-jshandle)
+  * [jsHandle.asElement()](#jshandleaselement)
+  * [jsHandle.dispose()](#jshandledispose)
+  * [jsHandle.executionContext()](#jshandleexecutioncontext)
+  * [jsHandle.getProperties()](#jshandlegetproperties)
+  * [jsHandle.getProperty(propertyName)](#jshandlegetpropertypropertyname)
+  * [jsHandle.jsonValue()](#jshandlejsonvalue)
+- [class: ElementHandle](#class-elementhandle)
+  * [elementHandle.$(selector)](#elementhandleselector)
+  * [elementHandle.$$(selector)](#elementhandleselector-1)
+  * [elementHandle.$$eval(selector, pageFunction[, ...args])](#elementhandleevalselector-pagefunction-args)
+  * [elementHandle.$eval(selector, pageFunction[, ...args])](#elementhandleevalselector-pagefunction-args-1)
+  * [elementHandle.$x(expression)](#elementhandlexexpression)
+  * [elementHandle.asElement()](#elementhandleaselement)
+  * [elementHandle.boundingBox()](#elementhandleboundingbox)
+  * [elementHandle.boxModel()](#elementhandleboxmodel)
+  * [elementHandle.click([options])](#elementhandleclickoptions)
+  * [elementHandle.contentFrame()](#elementhandlecontentframe)
+  * [elementHandle.dispose()](#elementhandledispose)
+  * [elementHandle.executionContext()](#elementhandleexecutioncontext)
+  * [elementHandle.focus()](#elementhandlefocus)
+  * [elementHandle.getProperties()](#elementhandlegetproperties)
+  * [elementHandle.getProperty(propertyName)](#elementhandlegetpropertypropertyname)
+  * [elementHandle.hover()](#elementhandlehover)
+  * [elementHandle.isIntersectingViewport()](#elementhandleisintersectingviewport)
+  * [elementHandle.jsonValue()](#elementhandlejsonvalue)
+  * [elementHandle.press(key[, options])](#elementhandlepresskey-options)
+  * [elementHandle.screenshot([options])](#elementhandlescreenshotoptions)
+  * [elementHandle.tap()](#elementhandletap)
+  * [elementHandle.toString()](#elementhandletostring)
+  * [elementHandle.type(text[, options])](#elementhandletypetext-options)
+  * [elementHandle.uploadFile(...filePaths)](#elementhandleuploadfilefilepaths)
+- [class: Request](#class-request)
+  * [request.abort([errorCode])](#requestaborterrorcode)
+  * [request.continue([overrides])](#requestcontinueoverrides)
+  * [request.failure()](#requestfailure)
+  * [request.frame()](#requestframe)
+  * [request.headers()](#requestheaders)
+  * [request.isNavigationRequest()](#requestisnavigationrequest)
+  * [request.method()](#requestmethod)
+  * [request.postData()](#requestpostdata)
+  * [request.redirectChain()](#requestredirectchain)
+  * [request.resourceType()](#requestresourcetype)
+  * [request.respond(response)](#requestrespondresponse)
+  * [request.response()](#requestresponse)
+  * [request.url()](#requesturl)
+- [class: Response](#class-response)
+  * [response.buffer()](#responsebuffer)
+  * [response.frame()](#responseframe)
+  * [response.fromCache()](#responsefromcache)
+  * [response.fromServiceWorker()](#responsefromserviceworker)
+  * [response.headers()](#responseheaders)
+  * [response.json()](#responsejson)
+  * [response.ok()](#responseok)
+  * [response.remoteAddress()](#responseremoteaddress)
+  * [response.request()](#responserequest)
+  * [response.securityDetails()](#responsesecuritydetails)
+  * [response.status()](#responsestatus)
+  * [response.statusText()](#responsestatustext)
+  * [response.text()](#responsetext)
+  * [response.url()](#responseurl)
+- [class: SecurityDetails](#class-securitydetails)
+  * [securityDetails.issuer()](#securitydetailsissuer)
+  * [securityDetails.protocol()](#securitydetailsprotocol)
+  * [securityDetails.subjectName()](#securitydetailssubjectname)
+  * [securityDetails.validFrom()](#securitydetailsvalidfrom)
+  * [securityDetails.validTo()](#securitydetailsvalidto)
+- [class: Target](#class-target)
+  * [target.browser()](#targetbrowser)
+  * [target.browserContext()](#targetbrowsercontext)
+  * [target.createCDPSession()](#targetcreatecdpsession)
+  * [target.opener()](#targetopener)
+  * [target.page()](#targetpage)
+  * [target.type()](#targettype)
+  * [target.url()](#targeturl)
+  * [target.worker()](#targetworker)
+- [class: CDPSession](#class-cdpsession)
+  * [cdpSession.detach()](#cdpsessiondetach)
+  * [cdpSession.send(method[, params])](#cdpsessionsendmethod-params)
+- [class: Coverage](#class-coverage)
+  * [coverage.startCSSCoverage([options])](#coveragestartcsscoverageoptions)
+  * [coverage.startJSCoverage([options])](#coveragestartjscoverageoptions)
+  * [coverage.stopCSSCoverage()](#coveragestopcsscoverage)
+  * [coverage.stopJSCoverage()](#coveragestopjscoverage)
+- [class: TimeoutError](#class-timeouterror)
+<!-- GEN:stop -->
+
+### Overview
+
+Puppeteer is a Node library which provides a high-level API to control Chromium or Chrome over the DevTools Protocol.
+
+The Puppeteer API is hierarchical and mirrors the browser structure.
+
+> **NOTE** On the following diagram, faded entities are not currently represented in Puppeteer.
+
+![puppeteer overview](https://user-images.githubusercontent.com/746130/40333229-5df5480c-5d0c-11e8-83cb-c3e371de7374.png)
+
+- [`Puppeteer`](#class-puppeteer) communicates with the browser using [DevTools Protocol](https://chromedevtools.github.io/devtools-protocol/).
+- [`Browser`](#class-browser) instance can own multiple browser contexts.
+- [`BrowserContext`](#class-browsercontext) instance defines a browsing session and can own multiple pages.
+- [`Page`](#class-page) has at least one frame: main frame. There might be other frames created by [iframe](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe) or [frame](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/frame) tags.
+- [`Frame`](#class-frame) has at least one execution context - the default execution context - where the frame's JavaScript is executed. A Frame might have additional execution contexts that are associated with [extensions](https://developer.chrome.com/extensions).
+- [`Worker`](#class-worker) has a single execution context and facilitates interacting with [WebWorkers](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API).
+
+(Diagram source: [link](https://docs.google.com/drawings/d/1Q_AM6KYs9kbyLZF-Lpp5mtpAWth73Cq8IKCsWYgi8MM/edit?usp=sharing))
+
+### puppeteer vs puppeteer-core
+
+Every release since v1.7.0 we publish two packages:
+- [puppeteer](https://www.npmjs.com/package/puppeteer)
+- [puppeteer-core](https://www.npmjs.com/package/puppeteer-core)
+
+`puppeteer` is a *product* for browser automation. When installed, it downloads a version of
+Chromium, which it then drives using `puppeteer-core`. Being an end-user product, `puppeteer` supports a bunch of convenient `PUPPETEER_*` env variables to tweak its behavior.
+
+`puppeteer-core` is a *library* to help drive anything that supports DevTools protocol. `puppeteer-core` doesn't download Chromium when installed. Being a library, `puppeteer-core` is fully driven
+through its programmatic interface and disregards all the `PUPPETEER_*` env variables.
+
+To sum up, the only differences between `puppeteer-core` and `puppeteer` are:
+- `puppeteer-core` doesn't automatically download Chromium when installed.
+- `puppeteer-core` ignores all `PUPPETEER_*` env variables.
+
+In most cases, you'll be fine using the `puppeteer` package.
+
+However, you should use `puppeteer-core` if:
+- you're building another end-user product or library atop of DevTools protocol. For example, one might build a PDF generator using `puppeteer-core` and write a custom `install.js` script that downloads [`headless_shell`](https://chromium.googlesource.com/chromium/src/+/lkgr/headless/README.md) instead of Chromium to save disk space.
+- you're bundling Puppeteer to use in Chrome Extension / browser with the DevTools protocol where downloading an additional Chromium binary is unnecessary.
+
+When using `puppeteer-core`, remember to change the *include* line:
+
+```js
+const puppeteer = require('puppeteer-core');
+```
+
+You will then need to call [`puppeteer.connect([options])`](#puppeteerconnectoptions) or [`puppeteer.launch([options])`](#puppeteerlaunchoptions) with an explicit `executablePath` option.
+
+### Environment Variables
+
+Puppeteer looks for certain [environment variables](https://en.wikipedia.org/wiki/Environment_variable) to aid its operations.
+If Puppeteer doesn't find them in the environment during the installation step, a lowercased variant of these variables will be used from the [npm config](https://docs.npmjs.com/cli/config).
+
+- `HTTP_PROXY`, `HTTPS_PROXY`, `NO_PROXY` - defines HTTP proxy settings that are used to download and run Chromium.
+- `PUPPETEER_SKIP_CHROMIUM_DOWNLOAD` - do not download bundled Chromium during installation step.
+- `PUPPETEER_DOWNLOAD_HOST` - overwrite URL prefix that is used to download Chromium. Note: this includes protocol and might even include path prefix. Defaults to `https://storage.googleapis.com`.
+- `PUPPETEER_CHROMIUM_REVISION` - specify a certain version of Chromium you'd like Puppeteer to use. See [puppeteer.launch([options])](#puppeteerlaunchoptions) on how executable path is inferred. **BEWARE**: Puppeteer is only [guaranteed to work](https://github.com/GoogleChrome/puppeteer/#q-why-doesnt-puppeteer-vxxx-work-with-chromium-vyyy) with the bundled Chromium, use at your own risk.
+- `PUPPETEER_EXECUTABLE_PATH` - specify an executable path to be used in `puppeteer.launch`. See [puppeteer.launch([options])](#puppeteerlaunchoptions) on how the executable path is inferred. **BEWARE**: Puppeteer is only [guaranteed to work](https://github.com/GoogleChrome/puppeteer/#q-why-doesnt-puppeteer-vxxx-work-with-chromium-vyyy) with the bundled Chromium, use at your own risk.
+
+> **NOTE** PUPPETEER_* env variables are not accounted for in the [`puppeteer-core`](https://www.npmjs.com/package/puppeteer-core) package.
+
+
+### Working with Chrome Extensions
+
+Puppeteer can be used for testing Chrome Extensions.
+
+> **NOTE** Extensions in Chrome / Chromium currently only work in non-headless mode.
+
+The following is code for getting a handle to the [background page](https://developer.chrome.com/extensions/background_pages) of an extension whose source is located in `./my-extension`:
+```js
+const puppeteer = require('puppeteer');
+
+(async () => {
+  const pathToExtension = require('path').join(__dirname, 'my-extension');
+  const browser = await puppeteer.launch({
+    headless: false,
+    args: [
+      `--disable-extensions-except=${pathToExtension}`,
+      `--load-extension=${pathToExtension}`
+    ]
+  });
+  const targets = await browser.targets();
+  const backgroundPageTarget = targets.find(target => target.type() === 'background_page');
+  const backgroundPage = await backgroundPageTarget.page();
+  // Test the background page as you would any other page.
+  await browser.close();
+})();
+```
+
+> **NOTE** It is not yet possible to test extension popups or content scripts.
+
+### class: Puppeteer
+
+Puppeteer module provides a method to launch a Chromium instance.
+The following is a typical example of using Puppeteer to drive automation:
+```js
+const puppeteer = require('puppeteer');
+
+puppeteer.launch().then(async browser => {
+  const page = await browser.newPage();
+  await page.goto('https://www.google.com');
+  // other actions...
+  await browser.close();
+});
+```
+
+#### puppeteer.connect(options)
+- `options` <[Object]>
+  - `browserWSEndpoint` <?[string]> a [browser websocket endpoint](#browserwsendpoint) to connect to.
+  - `browserURL` <?[string]> a browser url to connect to, in format `http://${host}:${port}`. Use interchangeably with `browserWSEndpoint` to let Puppeteer fetch it from [metadata endpoint](https://chromedevtools.github.io/devtools-protocol/#how-do-i-access-the-browser-target).
+  - `ignoreHTTPSErrors` <[boolean]> Whether to ignore HTTPS errors during navigation. Defaults to `false`.
+  - `defaultViewport` <?[Object]> Sets a consistent viewport for each page. Defaults to an 800x600 viewport. `null` disables the default viewport.
+    - `width` <[number]> page width in pixels.
+    - `height` <[number]> page height in pixels.
+    - `deviceScaleFactor` <[number]> Specify device scale factor (can be thought of as dpr). Defaults to `1`.
+    - `isMobile` <[boolean]> Whether the `meta viewport` tag is taken into account. Defaults to `false`.
+    - `hasTouch`<[boolean]> Specifies if viewport supports touch events. Defaults to `false`
+    - `isLandscape` <[boolean]> Specifies if viewport is in landscape mode. Defaults to `false`.
+  - `slowMo` <[number]> Slows down Puppeteer operations by the specified amount of milliseconds. Useful so that you can see what is going on.
+  - `transport` <[ConnectionTransport]> **Experimental** Specify a custom transport object for Puppeteer to use.
+- returns: <[Promise]<[Browser]>>
+
+This methods attaches Puppeteer to an existing Chromium instance.
+
+#### puppeteer.createBrowserFetcher([options])
+- `options` <[Object]>
+  - `host` <[string]> A download host to be used. Defaults to `https://storage.googleapis.com`.
+  - `path` <[string]> A path for the downloads folder. Defaults to `<root>/.local-chromium`, where `<root>` is puppeteer's package root.
+  - `platform` <[string]> Possible values are: `mac`, `win32`, `win64`, `linux`. Defaults to the current platform.
+- returns: <[BrowserFetcher]>
+
+#### puppeteer.defaultArgs([options])
+- `options` <[Object]>  Set of configurable options to set on the browser. Can have the following fields:
+  - `headless` <[boolean]> Whether to run browser in [headless mode](https://developers.google.com/web/updates/2017/04/headless-chrome). Defaults to `true` unless the `devtools` option is `true`.
+  - `args` <[Array]<[string]>> Additional arguments to pass to the browser instance. The list of Chromium flags can be found [here](http://peter.sh/experiments/chromium-command-line-switches/).
+  - `userDataDir` <[string]> Path to a [User Data Directory](https://chromium.googlesource.com/chromium/src/+/master/docs/user_data_dir.md).
+  - `devtools` <[boolean]> Whether to auto-open a DevTools panel for each tab. If this option is `true`, the `headless` option will be set `false`.
+- returns: <[Array]<[string]>>
+
+The default flags that Chromium will be launched with.
+
+#### puppeteer.devices
+- returns: <[Object]>
+
+Returns a list of devices to be used with [`page.emulate(options)`](#pageemulateoptions). Actual list of
+devices can be found in [lib/DeviceDescriptors.js](https://github.com/GoogleChrome/puppeteer/blob/master/lib/DeviceDescriptors.js).
+
+```js
+const puppeteer = require('puppeteer');
+const iPhone = puppeteer.devices['iPhone 6'];
+
+puppeteer.launch().then(async browser => {
+  const page = await browser.newPage();
+  await page.emulate(iPhone);
+  await page.goto('https://www.google.com');
+  // other actions...
+  await browser.close();
+});
+```
+
+> **NOTE** The old way (Puppeteer versions <= v1.14.0) devices can be obtained with `require('puppeteer/DeviceDescriptors')`.
+
+#### puppeteer.errors
+- returns: <[Object]>
+  - `TimeoutError` <[function]> A class of [TimeoutError].
+
+Puppeteer methods might throw errors if they are unable to fufill a request. For example, [page.waitForSelector(selector[, options])](#pagewaitforselectorselector-options)
+might fail if the selector doesn't match any nodes during the given timeframe.
+
+For certain types of errors Puppeteer uses specific error classes.
+These classes are available via [`puppeteer.errors`](#puppeteererrors)
+
+An example of handling a timeout error:
+```js
+try {
+  await page.waitForSelector('.foo');
+} catch (e) {
+  if (e instanceof puppeteer.errors.TimeoutError) {
+    // Do something if this is a timeout.
+  }
+}
+```
+
+> **NOTE** The old way (Puppeteer versions <= v1.14.0) errors can be obtained with `require('puppeteer/Errors')`.
+
+#### puppeteer.executablePath()
+- returns: <[string]> A path where Puppeteer expects to find bundled Chromium. Chromium might not exist there if the download was skipped with [`PUPPETEER_SKIP_CHROMIUM_DOWNLOAD`](#environment-variables).
+
+> **NOTE** `puppeteer.executablePath()` is affected by the `PUPPETEER_EXECUTABLE_PATH` and `PUPPETEER_CHROMIUM_REVISION` env variables. See [Environment Variables](#environment-variables) for details.
+
+
+#### puppeteer.launch([options])
+- `options` <[Object]>  Set of configurable options to set on the browser. Can have the following fields:
+  - `ignoreHTTPSErrors` <[boolean]> Whether to ignore HTTPS errors during navigation. Defaults to `false`.
+  - `headless` <[boolean]> Whether to run browser in [headless mode](https://developers.google.com/web/updates/2017/04/headless-chrome). Defaults to `true` unless the `devtools` option is `true`.
+  - `executablePath` <[string]> Path to a Chromium or Chrome executable to run instead of the bundled Chromium. If `executablePath` is a relative path, then it is resolved relative to [current working directory](https://nodejs.org/api/process.html#process_process_cwd). **BEWARE**: Puppeteer is only [guaranteed to work](https://github.com/GoogleChrome/puppeteer/#q-why-doesnt-puppeteer-vxxx-work-with-chromium-vyyy) with the bundled Chromium, use at your own risk.
+  - `slowMo` <[number]> Slows down Puppeteer operations by the specified amount of milliseconds. Useful so that you can see what is going on.
+  - `defaultViewport` <?[Object]> Sets a consistent viewport for each page. Defaults to an 800x600 viewport. `null` disables the default viewport.
+    - `width` <[number]> page width in pixels.
+    - `height` <[number]> page height in pixels.
+    - `deviceScaleFactor` <[number]> Specify device scale factor (can be thought of as dpr). Defaults to `1`.
+    - `isMobile` <[boolean]> Whether the `meta viewport` tag is taken into account. Defaults to `false`.
+    - `hasTouch`<[boolean]> Specifies if viewport supports touch events. Defaults to `false`
+    - `isLandscape` <[boolean]> Specifies if viewport is in landscape mode. Defaults to `false`.
+  - `args` <[Array]<[string]>> Additional arguments to pass to the browser instance. The list of Chromium flags can be found [here](http://peter.sh/experiments/chromium-command-line-switches/).
+  - `ignoreDefaultArgs` <[boolean]|[Array]<[string]>> If `true`, then do not use [`puppeteer.defaultArgs()`](#puppeteerdefaultargs-options). If an array is given, then filter out the given default arguments. Dangerous option; use with care. Defaults to `false`.
+  - `handleSIGINT` <[boolean]> Close the browser process on Ctrl-C. Defaults to `true`.
+  - `handleSIGTERM` <[boolean]> Close the browser process on SIGTERM. Defaults to `true`.
+  - `handleSIGHUP` <[boolean]> Close the browser process on SIGHUP. Defaults to `true`.
+  - `timeout` <[number]> Maximum time in milliseconds to wait for the browser instance to start. Defaults to `30000` (30 seconds). Pass `0` to disable timeout.
+  - `dumpio` <[boolean]> Whether to pipe the browser process stdout and stderr into `process.stdout` and `process.stderr`. Defaults to `false`.
+  - `userDataDir` <[string]> Path to a [User Data Directory](https://chromium.googlesource.com/chromium/src/+/master/docs/user_data_dir.md).
+  - `env` <[Object]> Specify environment variables that will be visible to the browser. Defaults to `process.env`.
+  - `devtools` <[boolean]> Whether to auto-open a DevTools panel for each tab. If this option is `true`, the `headless` option will be set `false`.
+  - `pipe` <[boolean]> Connects to the browser over a pipe instead of a WebSocket. Defaults to `false`.
+- returns: <[Promise]<[Browser]>> Promise which resolves to browser instance.
+
+
+You can use `ignoreDefaultArgs` to filter out `--mute-audio` from default arguments:
+```js
+const browser = await puppeteer.launch({
+  ignoreDefaultArgs: ['--mute-audio']
+});
+```
+
+> **NOTE** Puppeteer can also be used to control the Chrome browser, but it works best with the version of Chromium it is bundled with. There is no guarantee it will work with any other version. Use `executablePath` option with extreme caution.
+>
+> If Google Chrome (rather than Chromium) is preferred, a [Chrome Canary](https://www.google.com/chrome/browser/canary.html) or [Dev Channel](https://www.chromium.org/getting-involved/dev-channel) build is suggested.
+>
+> In [puppeteer.launch([options])](#puppeteerlaunchoptions) above, any mention of Chromium also applies to Chrome.
+>
+> See [`this article`](https://www.howtogeek.com/202825/what%E2%80%99s-the-difference-between-chromium-and-chrome/) for a description of the differences between Chromium and Chrome. [`This article`](https://chromium.googlesource.com/chromium/src/+/lkgr/docs/chromium_browser_vs_google_chrome.md) describes some differences for Linux users.
+
+### class: BrowserFetcher
+
+BrowserFetcher can download and manage different versions of Chromium.
+
+BrowserFetcher operates on revision strings that specify a precise version of Chromium, e.g. `"533271"`. Revision strings can be obtained from [omahaproxy.appspot.com](http://omahaproxy.appspot.com/).
+
+An example of using BrowserFetcher to download a specific version of Chromium and running
+Puppeteer against it:
+
+```js
+const browserFetcher = puppeteer.createBrowserFetcher();
+const revisionInfo = await browserFetcher.download('533271');
+const browser = await puppeteer.launch({executablePath: revisionInfo.executablePath})
+```
+
+> **NOTE** BrowserFetcher is not designed to work concurrently with other
+> instances of BrowserFetcher that share the same downloads directory.
+
+#### browserFetcher.canDownload(revision)
+- `revision` <[string]> a revision to check availability.
+- returns: <[Promise]<[boolean]>>  returns `true` if the revision could be downloaded from the host.
+
+The method initiates a HEAD request to check if the revision is available.
+
+#### browserFetcher.download(revision[, progressCallback])
+- `revision` <[string]> a revision to download.
+- `progressCallback` <[function]([number], [number])> A function that will be called with two arguments:
+  - `downloadedBytes` <[number]> how many bytes have been downloaded
+  - `totalBytes` <[number]> how large is the total download.
+- returns: <[Promise]<[Object]>> Resolves with revision information when the revision is downloaded and extracted
+  - `revision` <[string]> the revision the info was created from
+  - `folderPath` <[string]> path to the extracted revision folder
+  - `executablePath` <[string]> path to the revision executable
+  - `url` <[string]> URL this revision can be downloaded from
+  - `local` <[boolean]> whether the revision is locally available on disk
+
+The method initiates a GET request to download the revision from the host.
+
+#### browserFetcher.localRevisions()
+- returns: <[Promise]<[Array]<[string]>>> A list of all revisions available locally on disk.
+
+#### browserFetcher.platform()
+- returns: <[string]> One of `mac`, `linux`, `win32` or `win64`.
+
+#### browserFetcher.remove(revision)
+- `revision` <[string]> a revision to remove. The method will throw if the revision has not been downloaded.
+- returns: <[Promise]> Resolves when the revision has been removed.
+
+#### browserFetcher.revisionInfo(revision)
+- `revision` <[string]> a revision to get info for.
+- returns: <[Object]>
+  - `revision` <[string]> the revision the info was created from
+  - `folderPath` <[string]> path to the extracted revision folder
+  - `executablePath` <[string]> path to the revision executable
+  - `url` <[string]> URL this revision can be downloaded from
+  - `local` <[boolean]> whether the revision is locally available on disk
+
+### class: Browser
+
+* extends: [EventEmitter](https://nodejs.org/api/events.html#events_class_eventemitter)
+
+A Browser is created when Puppeteer connects to a Chromium instance, either through [`puppeteer.launch`](#puppeteerlaunchoptions) or [`puppeteer.connect`](#puppeteerconnectoptions).
+
+An example of using a [Browser] to create a [Page]:
+```js
+const puppeteer = require('puppeteer');
+
+puppeteer.launch().then(async browser => {
+  const page = await browser.newPage();
+  await page.goto('https://example.com');
+  await browser.close();
+});
+```
+
+An example of disconnecting from and reconnecting to a [Browser]:
+```js
+const puppeteer = require('puppeteer');
+
+puppeteer.launch().then(async browser => {
+  // Store the endpoint to be able to reconnect to Chromium
+  const browserWSEndpoint = browser.wsEndpoint();
+  // Disconnect puppeteer from Chromium
+  browser.disconnect();
+
+  // Use the endpoint to reestablish a connection
+  const browser2 = await puppeteer.connect({browserWSEndpoint});
+  // Close Chromium
+  await browser2.close();
+});
+```
+#### event: 'disconnected'
+Emitted when Puppeteer gets disconnected from the Chromium instance. This might happen because of one of the following:
+- Chromium is closed or crashed
+- The [`browser.disconnect`](#browserdisconnect) method was called
+
+#### event: 'targetchanged'
+- <[Target]>
+
+Emitted when the url of a target changes.
+
+> **NOTE** This includes target changes in incognito browser contexts.
+
+
+#### event: 'targetcreated'
+- <[Target]>
+
+Emitted when a target is created, for example when a new page is opened by [`window.open`](https://developer.mozilla.org/en-US/docs/Web/API/Window/open) or [`browser.newPage`](#browsernewpage).
+
+> **NOTE** This includes target creations in incognito browser contexts.
+
+#### event: 'targetdestroyed'
+- <[Target]>
+
+Emitted when a target is destroyed, for example when a page is closed.
+
+> **NOTE** This includes target destructions in incognito browser contexts.
+
+#### browser.browserContexts()
+- returns: <[Array]<[BrowserContext]>>
+
+Returns an array of all open browser contexts. In a newly created browser, this will return
+a single instance of [BrowserContext].
+
+#### browser.close()
+- returns: <[Promise]>
+
+Closes Chromium and all of its pages (if any were opened). The [Browser] object itself is considered to be disposed and cannot be used anymore.
+
+#### browser.createIncognitoBrowserContext()
+- returns: <[Promise]<[BrowserContext]>>
+
+Creates a new incognito browser context. This won't share cookies/cache with other browser contexts.
+
+```js
+const browser = await puppeteer.launch();
+// Create a new incognito browser context.
+const context = await browser.createIncognitoBrowserContext();
+// Create a new page in a pristine context.
+const page = await context.newPage();
+// Do stuff
+await page.goto('https://example.com');
+```
+
+#### browser.defaultBrowserContext()
+- returns: <[BrowserContext]>
+
+Returns the default browser context. The default browser context can not be closed.
+
+#### browser.disconnect()
+
+Disconnects Puppeteer from the browser, but leaves the Chromium process running. After calling `disconnect`, the [Browser] object is considered disposed and cannot be used anymore.
+
+#### browser.isConnected()
+
+- returns: <[boolean]>
+
+Indicates that the browser is connected.
+
+#### browser.newPage()
+- returns: <[Promise]<[Page]>>
+
+Promise which resolves to a new [Page] object. The [Page] is created in a default browser context.
+
+#### browser.pages()
+- returns: <[Promise]<[Array]<[Page]>>> Promise which resolves to an array of all open pages. Non visible pages, such as `"background_page"`, will not be listed here. You can find them using [target.page()](#targetpage).
+
+An array of all pages inside the Browser. In case of multiple browser contexts,
+the method will return an array with all the pages in all browser contexts.
+
+#### browser.process()
+- returns: <?[ChildProcess]> Spawned browser process. Returns `null` if the browser instance was created with [`puppeteer.connect`](#puppeteerconnectoptions) method.
+
+#### browser.target()
+- returns: <[Target]>
+
+A target associated with the browser.
+
+#### browser.targets()
+- returns: <[Array]<[Target]>>
+
+An array of all active targets inside the Browser. In case of multiple browser contexts,
+the method will return an array with all the targets in all browser contexts.
+
+#### browser.userAgent()
+- returns: <[Promise]<[string]>> Promise which resolves to the browser's original user agent.
+
+> **NOTE** Pages can override browser user agent with [page.setUserAgent](#pagesetuseragentuseragent)
+
+#### browser.version()
+- returns: <[Promise]<[string]>> For headless Chromium, this is similar to `HeadlessChrome/61.0.3153.0`. For non-headless, this is similar to `Chrome/61.0.3153.0`.
+
+> **NOTE** the format of browser.version() might change with future releases of Chromium.
+
+#### browser.waitForTarget(predicate[, options])
+- `predicate` <[function]\([Target]\):[boolean]> A function to be run for every target
+- `options` <[Object]>
+  - `timeout` <[number]> Maximum wait time in milliseconds. Pass `0` to disable the timeout. Defaults to 30 seconds.
+- returns: <[Promise]<[Target]>> Promise which resolves to the first target found that matches the `predicate` function.
+
+This searches for a target in all browser contexts.
+
+An example of finding a target for a page opened via `window.open`:
+```js
+await page.evaluate(() => window.open('https://www.example.com/'));
+const newWindowTarget = await browser.waitForTarget(target => target.url() === 'https://www.example.com/');
+```
+
+#### browser.wsEndpoint()
+- returns: <[string]> Browser websocket url.
+
+Browser websocket endpoint which can be used as an argument to
+[puppeteer.connect](#puppeteerconnectoptions). The format is `ws://${host}:${port}/devtools/browser/<id>`
+
+You can find the `webSocketDebuggerUrl` from `http://${host}:${port}/json/version`. Learn more about the [devtools protocol](https://chromedevtools.github.io/devtools-protocol) and the [browser endpoint](https://chromedevtools.github.io/devtools-protocol/#how-do-i-access-the-browser-target).
+
+### class: BrowserContext
+
+* extends: [EventEmitter](https://nodejs.org/api/events.html#events_class_eventemitter)
+
+BrowserContexts provide a way to operate 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.
+
+Puppeteer allows creation of "incognito" browser contexts with `browser.createIncognitoBrowserContext()` method.
+"Incognito" browser contexts don't write any browsing data to disk.
+
+```js
+// Create a new incognito browser context
+const context = await browser.createIncognitoBrowserContext();
+// Create a new page inside context.
+const 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();
+```
+
+#### event: 'targetchanged'
+- <[Target]>
+
+Emitted when the url of a target inside the browser context changes.
+
+#### event: 'targetcreated'
+- <[Target]>
+
+Emitted when a new target is created inside the browser context, for example when a new page is opened by [`window.open`](https://developer.mozilla.org/en-US/docs/Web/API/Window/open) or [`browserContext.newPage`](#browsercontextnewpage).
+
+#### event: 'targetdestroyed'
+- <[Target]>
+
+Emitted when a target inside the browser context is destroyed, for example when a page is closed.
+
+#### browserContext.browser()
+- returns: <[Browser]>
+
+The browser this browser context belongs to.
+
+#### browserContext.clearPermissionOverrides()
+- returns: <[Promise]>
+
+Clears all permission overrides for the browser context.
+
+```js
+const context = browser.defaultBrowserContext();
+context.overridePermissions('https://example.com', ['clipboard-read']);
+// do stuff ..
+context.clearPermissionOverrides();
+```
+
+#### browserContext.close()
+- returns: <[Promise]>
+
+Closes the browser context. All the targets that belong to the browser context
+will be closed.
+
+> **NOTE** only incognito browser contexts can be closed.
+
+#### browserContext.isIncognito()
+- returns: <[boolean]>
+
+Returns whether BrowserContext is incognito.
+The default browser context is the only non-incognito browser context.
+
+> **NOTE** the default browser context cannot be closed.
+
+#### browserContext.newPage()
+- returns: <[Promise]<[Page]>>
+
+Creates a new page in the browser context.
+
+
+#### browserContext.overridePermissions(origin, permissions)
+- `origin` <[string]> The [origin] to grant permissions to, e.g. "https://example.com".
+- `permissions` <[Array]<[string]>> An array of permissions to grant. All permissions that are not listed here will be automatically denied. Permissions can be one of the following values:
+    - `'geolocation'`
+    - `'midi'`
+    - `'midi-sysex'` (system-exclusive midi)
+    - `'notifications'`
+    - `'push'`
+    - `'camera'`
+    - `'microphone'`
+    - `'background-sync'`
+    - `'ambient-light-sensor'`
+    - `'accelerometer'`
+    - `'gyroscope'`
+    - `'magnetometer'`
+    - `'accessibility-events'`
+    - `'clipboard-read'`
+    - `'clipboard-write'`
+    - `'payment-handler'`
+- returns: <[Promise]>
+
+
+```js
+const context = browser.defaultBrowserContext();
+await context.overridePermissions('https://html5demos.com', ['geolocation']);
+```
+
+
+#### browserContext.pages()
+- returns: <[Promise]<[Array]<[Page]>>> Promise which resolves to an array of all open pages. Non visible pages, such as `"background_page"`, will not be listed here. You can find them using [target.page()](#targetpage).
+
+An array of all pages inside the browser context.
+
+#### browserContext.targets()
+- returns: <[Array]<[Target]>>
+
+An array of all active targets inside the browser context.
+
+#### browserContext.waitForTarget(predicate[, options])
+- `predicate` <[function]\([Target]\):[boolean]> A function to be run for every target
+- `options` <[Object]>
+  - `timeout` <[number]> Maximum wait time in milliseconds. Pass `0` to disable the timeout. Defaults to 30 seconds.
+- returns: <[Promise]<[Target]>> Promise which resolves to the first target found that matches the `predicate` function.
+
+This searches for a target in this specific browser context.
+
+An example of finding a target for a page opened via `window.open`:
+```js
+await page.evaluate(() => window.open('https://www.example.com/'));
+const newWindowTarget = await browserContext.waitForTarget(target => target.url() === 'https://www.example.com/');
+```
+
+### class: Page
+
+* extends: [EventEmitter](https://nodejs.org/api/events.html#events_class_eventemitter)
+
+Page provides methods to interact with a single tab or [extension background page](https://developer.chrome.com/extensions/background_pages) in Chromium. One [Browser] instance might have multiple [Page] instances.
+
+This example creates a page, navigates it to a URL, and then saves a screenshot:
+```js
+const puppeteer = require('puppeteer');
+
+puppeteer.launch().then(async browser => {
+  const page = await browser.newPage();
+  await page.goto('https://example.com');
+  await page.screenshot({path: 'screenshot.png'});
+  await browser.close();
+});
+```
+
+The Page class emits various events (described below) which can be handled using any of Node's native [`EventEmitter`](https://nodejs.org/api/events.html#events_class_eventemitter) methods, such as `on`, `once` or `removeListener`.
+
+This example logs a message for a single page `load` event:
+```js
+page.once('load', () => console.log('Page loaded!'));
+```
+
+To unsubscribe from events use the `removeListener` method:
+
+```js
+function logRequest(interceptedRequest) {
+  console.log('A request was made:', interceptedRequest.url());
+}
+page.on('request', logRequest);
+// Sometime later...
+page.removeListener('request', logRequest);
+```
+
+#### event: 'close'
+
+Emitted when the page closes.
+
+#### event: 'console'
+- <[ConsoleMessage]>
+
+Emitted when JavaScript within the page calls one of console API methods, e.g. `console.log` or `console.dir`. Also emitted if the page throws an error or a warning.
+
+The arguments passed into `console.log` appear as arguments on the event handler.
+
+An example of handling `console` event:
+```js
+page.on('console', msg => {
+  for (let i = 0; i < msg.args().length; ++i)
+    console.log(`${i}: ${msg.args()[i]}`);
+});
+page.evaluate(() => console.log('hello', 5, {foo: 'bar'}));
+```
+
+#### event: 'dialog'
+- <[Dialog]>
+
+Emitted when a JavaScript dialog appears, such as `alert`, `prompt`, `confirm` or `beforeunload`. Puppeteer can respond to the dialog via [Dialog]'s [accept](#dialogacceptprompttext) or [dismiss](#dialogdismiss) methods.
+
+#### event: 'domcontentloaded'
+
+Emitted when the JavaScript [`DOMContentLoaded`](https://developer.mozilla.org/en-US/docs/Web/Events/DOMContentLoaded) event is dispatched.
+
+#### event: 'error'
+- <[Error]>
+
+Emitted when the page crashes.
+
+> **NOTE** `error` event has a special meaning in Node, see [error events](https://nodejs.org/api/events.html#events_error_events) for details.
+
+#### event: 'frameattached'
+- <[Frame]>
+
+Emitted when a frame is attached.
+
+#### event: 'framedetached'
+- <[Frame]>
+
+Emitted when a frame is detached.
+
+#### event: 'framenavigated'
+- <[Frame]>
+
+Emitted when a frame is navigated to a new url.
+
+#### event: 'load'
+
+Emitted when the JavaScript [`load`](https://developer.mozilla.org/en-US/docs/Web/Events/load) event is dispatched.
+
+#### event: 'metrics'
+- <[Object]>
+  - `title` <[string]> The title passed to `console.timeStamp`.
+  - `metrics` <[Object]> Object containing metrics as key/value pairs. The values
+    of metrics are of <[number]> type.
+
+Emitted when the JavaScript code makes a call to `console.timeStamp`. For the list
+of metrics see `page.metrics`.
+
+#### event: 'pageerror'
+- <[Error]> The exception message
+
+Emitted when an uncaught exception happens within the page.
+
+#### event: 'popup'
+- <[Page]> Page corresponding to "popup" window
+
+Emitted when the page opens a new tab or window.
+
+```js
+const [popup] = await Promise.all([
+  new Promise(resolve => page.once('popup', resolve)),
+  page.click('a[target=_blank]'),
+]);
+```
+
+```js
+const [popup] = await Promise.all([
+  new Promise(resolve => page.once('popup', resolve)),
+  page.evaluate(() => window.open('https://example.com')),
+]);
+```
+
+#### event: 'request'
+- <[Request]>
+
+Emitted when a page issues a request. The [request] object is read-only.
+In order to intercept and mutate requests, see `page.setRequestInterception`.
+
+#### event: 'requestfailed'
+- <[Request]>
+
+Emitted when a request fails, for example by timing out.
+
+#### event: 'requestfinished'
+- <[Request]>
+
+Emitted when a request finishes successfully.
+
+#### event: 'response'
+- <[Response]>
+
+Emitted when a [response] is received.
+
+#### event: 'workercreated'
+- <[Worker]>
+
+Emitted when a dedicated [WebWorker](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API) is spawned by the page.
+
+#### event: 'workerdestroyed'
+- <[Worker]>
+
+Emitted when a dedicated [WebWorker](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API) is terminated.
+
+#### page.$(selector)
+- `selector` <[string]> A [selector] to query page for
+- returns: <[Promise]<?[ElementHandle]>>
+
+The method runs `document.querySelector` within the page. If no element matches the selector, the return value resolves to `null`.
+
+Shortcut for [page.mainFrame().$(selector)](#frameselector).
+
+#### page.$$(selector)
+- `selector` <[string]> A [selector] to query page for
+- returns: <[Promise]<[Array]<[ElementHandle]>>>
+
+The method runs `document.querySelectorAll` within the page. If no elements match the selector, the return value resolves to `[]`.
+
+Shortcut for [page.mainFrame().$$(selector)](#frameselector-1).
+
+#### page.$$eval(selector, pageFunction[, ...args])
+- `selector` <[string]> A [selector] to query page for
+- `pageFunction` <[function]\([Array]<[Element]>\)> Function to be evaluated in browser context
+- `...args` <...[Serializable]|[JSHandle]> Arguments to pass to `pageFunction`
+- returns: <[Promise]<[Serializable]>> Promise which resolves to the return value of `pageFunction`
+
+This method runs `Array.from(document.querySelectorAll(selector))` within the page and passes it as the first argument to `pageFunction`.
+
+If `pageFunction` returns a [Promise], then `page.$$eval` would wait for the promise to resolve and return its value.
+
+Examples:
+```js
+const divsCounts = await page.$$eval('div', divs => divs.length);
+```
+
+#### page.$eval(selector, pageFunction[, ...args])
+- `selector` <[string]> A [selector] to query page for
+- `pageFunction` <[function]\([Element]\)> Function to be evaluated in browser context
+- `...args` <...[Serializable]|[JSHandle]> Arguments to pass to `pageFunction`
+- returns: <[Promise]<[Serializable]>> Promise which resolves to the return value of `pageFunction`
+
+This method runs `document.querySelector` within the page and passes it as the first argument to `pageFunction`. If there's no element matching `selector`, the method throws an error.
+
+If `pageFunction` returns a [Promise], then `page.$eval` would wait for the promise to resolve and return its value.
+
+Examples:
+```js
+const searchValue = await page.$eval('#search', el => el.value);
+const preloadHref = await page.$eval('link[rel=preload]', el => el.href);
+const html = await page.$eval('.main-container', e => e.outerHTML);
+```
+
+Shortcut for [page.mainFrame().$eval(selector, pageFunction)](#frameevalselector-pagefunction-args).
+
+#### page.$x(expression)
+- `expression` <[string]> Expression to [evaluate](https://developer.mozilla.org/en-US/docs/Web/API/Document/evaluate).
+- returns: <[Promise]<[Array]<[ElementHandle]>>>
+
+The method evaluates the XPath expression.
+
+Shortcut for [page.mainFrame().$x(expression)](#framexexpression)
+
+#### page.accessibility
+- returns: <[Accessibility]>
+
+#### page.addScriptTag(options)
+- `options` <[Object]>
+  - `url` <[string]> URL of a script to be added.
+  - `path` <[string]> Path to the JavaScript file to be injected into frame. If `path` is a relative path, then it is resolved relative to [current working directory](https://nodejs.org/api/process.html#process_process_cwd).
+  - `content` <[string]> Raw JavaScript content to be injected into frame.
+  - `type` <[string]> Script type. Use 'module' in order to load a Javascript ES6 module. See [script](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script) for more details.
+- returns: <[Promise]<[ElementHandle]>> which resolves to the added tag when the script's onload fires or when the script content was injected into frame.
+
+Adds a `<script>` tag into the page with the desired url or content.
+
+Shortcut for [page.mainFrame().addScriptTag(options)](#frameaddscripttagoptions).
+
+#### page.addStyleTag(options)
+- `options` <[Object]>
+  - `url` <[string]> URL of the `<link>` tag.
+  - `path` <[string]> Path to the CSS file to be injected into frame. If `path` is a relative path, then it is resolved relative to [current working directory](https://nodejs.org/api/process.html#process_process_cwd).
+  - `content` <[string]> Raw CSS content to be injected into frame.
+- returns: <[Promise]<[ElementHandle]>> which resolves to the added tag when the stylesheet's onload fires or when the CSS content was injected into frame.
+
+Adds a `<link rel="stylesheet">` tag into the page with the desired url or a `<style type="text/css">` tag with the content.
+
+Shortcut for [page.mainFrame().addStyleTag(options)](#frameaddstyletagoptions).
+
+#### page.authenticate(credentials)
+- `credentials` <?[Object]>
+  - `username` <[string]>
+  - `password` <[string]>
+- returns: <[Promise]>
+
+Provide credentials for [HTTP authentication](https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication).
+
+To disable authentication, pass `null`.
+
+#### page.bringToFront()
+
+- returns: <[Promise]>
+
+Brings page to front (activates tab).
+
+#### page.browser()
+
+- returns: <[Browser]>
+
+Get the browser the page belongs to.
+
+#### page.browserContext()
+
+- returns: <[BrowserContext]>
+
+Get the browser context that the page belongs to.
+
+#### page.click(selector[, options])
+- `selector` <[string]> A [selector] to search for element to click. If there are multiple elements satisfying the selector, the first will be clicked.
+- `options` <[Object]>
+  - `button` <"left"|"right"|"middle"> Defaults to `left`.
+  - `clickCount` <[number]> defaults to 1. See [UIEvent.detail].
+  - `delay` <[number]> Time to wait between `mousedown` and `mouseup` in milliseconds. Defaults to 0.
+- returns: <[Promise]> Promise which resolves when the element matching `selector` is successfully clicked. The Promise will be rejected if there is no element matching `selector`.
+
+This method fetches an element with `selector`, scrolls it into view if needed, and then uses [page.mouse](#pagemouse) to click in the center of the element.
+If there's no element matching `selector`, the method throws an error.
+
+Bear in mind that if `click()` triggers a navigation event and there's a separate `page.waitForNavigation()` promise to be resolved, you may end up with a race condition that yields unexpected results. The correct pattern for click and wait for navigation is the following:
+
+```javascript
+const [response] = await Promise.all([
+  page.waitForNavigation(waitOptions),
+  page.click(selector, clickOptions),
+]);
+```
+
+Shortcut for [page.mainFrame().click(selector[, options])](#frameclickselector-options).
+
+#### page.close([options])
+- `options` <[Object]>
+  - `runBeforeUnload` <[boolean]> Defaults to `false`. Whether to run the
+    [before unload](https://developer.mozilla.org/en-US/docs/Web/Events/beforeunload)
+    page handlers.
+- returns: <[Promise]>
+
+By default, `page.close()` **does not** run beforeunload handlers.
+
+> **NOTE** if `runBeforeUnload` is passed as true, a `beforeunload` dialog might be summoned
+> and should be handled manually via page's ['dialog'](#event-dialog) event.
+
+#### page.content()
+- returns: <[Promise]<[string]>>
+
+Gets the full HTML contents of the page, including the doctype.
+
+#### page.cookies([...urls])
+- `...urls` <...[string]>
+- returns: <[Promise]<[Array]<[Object]>>>
+  - `name` <[string]>
+  - `value` <[string]>
+  - `domain` <[string]>
+  - `path` <[string]>
+  - `expires` <[number]> Unix time in seconds.
+  - `size` <[number]>
+  - `httpOnly` <[boolean]>
+  - `secure` <[boolean]>
+  - `session` <[boolean]>
+  - `sameSite` <"Strict"|"Lax"|"Extended"|"None">
+
+If no URLs are specified, this method returns cookies for the current page URL.
+If URLs are specified, only cookies for those URLs are returned.
+
+#### page.coverage
+
+- returns: <[Coverage]>
+
+#### page.deleteCookie(...cookies)
+- `...cookies` <...[Object]>
+  - `name` <[string]> **required**
+  - `url` <[string]>
+  - `domain` <[string]>
+  - `path` <[string]>
+- returns: <[Promise]>
+
+#### page.emulate(options)
+- `options` <[Object]>
+  - `viewport` <[Object]>
+    - `width` <[number]> page width in pixels.
+    - `height` <[number]> page height in pixels.
+    - `deviceScaleFactor` <[number]> Specify device scale factor (can be thought of as dpr). Defaults to `1`.
+    - `isMobile` <[boolean]> Whether the `meta viewport` tag is taken into account. Defaults to `false`.
+    - `hasTouch`<[boolean]> Specifies if viewport supports touch events. Defaults to `false`
+    - `isLandscape` <[boolean]> Specifies if viewport is in landscape mode. Defaults to `false`.
+  - `userAgent` <[string]>
+- returns: <[Promise]>
+
+Emulates given device metrics and user agent. This method is a shortcut for calling two methods:
+- [page.setUserAgent(userAgent)](#pagesetuseragentuseragent)
+- [page.setViewport(viewport)](#pagesetviewportviewport)
+
+To aid emulation, puppeteer provides a list of device descriptors which can be obtained via the [`puppeteer.devices`](#puppeteerdevices).
+Below is an example of emulating an iPhone 6 in puppeteer:
+
+```js
+const puppeteer = require('puppeteer');
+const iPhone = puppeteer.devices['iPhone 6'];
+
+puppeteer.launch().then(async browser => {
+  const page = await browser.newPage();
+  await page.emulate(iPhone);
+  await page.goto('https://www.google.com');
+  // other actions...
+  await browser.close();
+});
+```
+
+List of all available devices is available in the source code: [DeviceDescriptors.js](https://github.com/GoogleChrome/puppeteer/blob/master/lib/DeviceDescriptors.js).
+
+#### page.emulateMedia(mediaType)
+- `mediaType` <?[string]> Changes the CSS media type of the page. The only allowed values are `'screen'`, `'print'` and `null`. Passing `null` disables media emulation.
+- returns: <[Promise]>
+
+#### page.evaluate(pageFunction[, ...args])
+- `pageFunction` <[function]|[string]> Function to be evaluated in the page context
+- `...args` <...[Serializable]|[JSHandle]> Arguments to pass to `pageFunction`
+- returns: <[Promise]<[Serializable]>> Promise which resolves to the return value of `pageFunction`
+
+If the function passed to the `page.evaluate` returns a [Promise], then `page.evaluate` would wait for the promise to resolve and return its value.
+
+If the function passed to the `page.evaluate` returns a non-[Serializable] value, then `page.evaluate` resolves to `undefined`. DevTools Protocol also supports transferring some additional values that are not serializable by `JSON`: `-0`, `NaN`, `Infinity`, `-Infinity`, and bigint literals.
+
+Passing arguments to `pageFunction`:
+```js
+const result = await page.evaluate(x => {
+  return Promise.resolve(8 * x);
+}, 7);
+console.log(result); // prints "56"
+```
+
+A string can also be passed in instead of a function:
+```js
+console.log(await page.evaluate('1 + 2')); // prints "3"
+const x = 10;
+console.log(await page.evaluate(`1 + ${x}`)); // prints "11"
+```
+
+[ElementHandle] instances can be passed as arguments to the `page.evaluate`:
+```js
+const bodyHandle = await page.$('body');
+const html = await page.evaluate(body => body.innerHTML, bodyHandle);
+await bodyHandle.dispose();
+```
+
+Shortcut for [page.mainFrame().evaluate(pageFunction, ...args)](#frameevaluatepagefunction-args).
+
+#### page.evaluateHandle(pageFunction[, ...args])
+- `pageFunction` <[function]|[string]> Function to be evaluated in the page context
+- `...args` <...[Serializable]|[JSHandle]> Arguments to pass to `pageFunction`
+- returns: <[Promise]<[JSHandle]>> Promise which resolves to the return value of `pageFunction` as in-page object (JSHandle)
+
+The only difference between `page.evaluate` and `page.evaluateHandle` is that `page.evaluateHandle` returns in-page object (JSHandle).
+
+If the function passed to the `page.evaluateHandle` returns a [Promise], then `page.evaluateHandle` would wait for the promise to resolve and return its value.
+
+A string can also be passed in instead of a function:
+```js
+const aHandle = await page.evaluateHandle('document'); // Handle for the 'document'
+```
+
+[JSHandle] instances can be passed as arguments to the `page.evaluateHandle`:
+```js
+const aHandle = await page.evaluateHandle(() => document.body);
+const resultHandle = await page.evaluateHandle(body => body.innerHTML, aHandle);
+console.log(await resultHandle.jsonValue());
+await resultHandle.dispose();
+```
+
+Shortcut for [page.mainFrame().executionContext().evaluateHandle(pageFunction, ...args)](#executioncontextevaluatehandlepagefunction-args).
+
+#### page.evaluateOnNewDocument(pageFunction[, ...args])
+- `pageFunction` <[function]|[string]> Function to be evaluated in browser context
+- `...args` <...[Serializable]> Arguments to pass to `pageFunction`
+- returns: <[Promise]>
+
+Adds a function which would be invoked in one of the following scenarios:
+- whenever the page is navigated
+- whenever the child frame is attached or navigated. In this case, the function is invoked in the context of the newly attached frame
+
+The function is invoked after the document was created but before any of its scripts were run. This is useful to amend  the JavaScript environment, e.g. to seed `Math.random`.
+
+An example of overriding the navigator.languages property before the page loads:
+
+```js
+// preload.js
+
+// overwrite the `languages` property to use a custom getter
+Object.defineProperty(navigator, "languages", {
+  get: function() {
+    return ["en-US", "en", "bn"];
+  }
+});
+
+// In your puppeteer script, assuming the preload.js file is in same folder of our script
+const preloadFile = fs.readFileSync('./preload.js', 'utf8');
+await page.evaluateOnNewDocument(preloadFile);
+```
+
+#### page.exposeFunction(name, puppeteerFunction)
+- `name` <[string]> Name of the function on the window object
+- `puppeteerFunction` <[function]> Callback function which will be called in Puppeteer's context.
+- returns: <[Promise]>
+
+The method adds a function called `name` on the page's `window` object.
+When called, the function executes `puppeteerFunction` in node.js and returns a [Promise] which resolves to the return value of `puppeteerFunction`.
+
+If the `puppeteerFunction` returns a [Promise], it will be awaited.
+
+> **NOTE** Functions installed via `page.exposeFunction` survive navigations.
+
+An example of adding an `md5` function into the page:
+```js
+const puppeteer = require('puppeteer');
+const crypto = require('crypto');
+
+puppeteer.launch().then(async browser => {
+  const page = await browser.newPage();
+  page.on('console', msg => console.log(msg.text()));
+  await page.exposeFunction('md5', text =>
+    crypto.createHash('md5').update(text).digest('hex')
+  );
+  await page.evaluate(async () => {
+    // use window.md5 to compute hashes
+    const myString = 'PUPPETEER';
+    const myHash = await window.md5(myString);
+    console.log(`md5 of ${myString} is ${myHash}`);
+  });
+  await browser.close();
+});
+```
+
+An example of adding a `window.readfile` function into the page:
+
+```js
+const puppeteer = require('puppeteer');
+const fs = require('fs');
+
+puppeteer.launch().then(async browser => {
+  const page = await browser.newPage();
+  page.on('console', msg => console.log(msg.text()));
+  await page.exposeFunction('readfile', async filePath => {
+    return new Promise((resolve, reject) => {
+      fs.readFile(filePath, 'utf8', (err, text) => {
+        if (err)
+          reject(err);
+        else
+          resolve(text);
+      });
+    });
+  });
+  await page.evaluate(async () => {
+    // use window.readfile to read contents of a file
+    const content = await window.readfile('/etc/hosts');
+    console.log(content);
+  });
+  await browser.close();
+});
+
+```
+
+#### page.focus(selector)
+- `selector` <[string]> A [selector] of an element to focus. If there are multiple elements satisfying the selector, the first will be focused.
+- returns: <[Promise]> Promise which resolves when the element matching `selector` is successfully focused. The promise will be rejected if there is no element matching `selector`.
+
+This method fetches an element with `selector` and focuses it.
+If there's no element matching `selector`, the method throws an error.
+
+Shortcut for [page.mainFrame().focus(selector)](#framefocusselector).
+
+#### page.frames()
+- returns: <[Array]<[Frame]>> An array of all frames attached to the page.
+
+#### page.goBack([options])
+- `options` <[Object]> Navigation parameters which might have the following properties:
+  - `timeout` <[number]> Maximum navigation time in milliseconds, defaults to 30 seconds, pass `0` to disable timeout. The default value can be changed by using the [page.setDefaultNavigationTimeout(timeout)](#pagesetdefaultnavigationtimeouttimeout) or [page.setDefaultTimeout(timeout)](#pagesetdefaulttimeouttimeout) methods.
+  - `waitUntil` <[string]|[Array]<[string]>> When to consider navigation succeeded, defaults to `load`. Given an array of event strings, navigation is considered to be successful after all events have been fired. Events can be either:
+    - `load` - consider navigation to be finished when the `load` event is fired.
+    - `domcontentloaded` - consider navigation to be finished when the `DOMContentLoaded` event is fired.
+    - `networkidle0` - consider navigation to be finished when there are no more than 0 network connections for at least `500` ms.
+    - `networkidle2` - consider navigation to be finished when there are no more than 2 network connections for at least `500` ms.
+- returns: <[Promise]<?[Response]>> Promise which resolves to the main resource response. In case of multiple redirects, the navigation will resolve with the response of the last redirect. If
+can not go back, resolves to `null`.
+
+Navigate to the previous page in history.
+
+#### page.goForward([options])
+- `options` <[Object]> Navigation parameters which might have the following properties:
+  - `timeout` <[number]> Maximum navigation time in milliseconds, defaults to 30 seconds, pass `0` to disable timeout. The default value can be changed by using the [page.setDefaultNavigationTimeout(timeout)](#pagesetdefaultnavigationtimeouttimeout) or [page.setDefaultTimeout(timeout)](#pagesetdefaulttimeouttimeout) methods.
+  - `waitUntil` <[string]|[Array]<[string]>> When to consider navigation succeeded, defaults to `load`. Given an array of event strings, navigation is considered to be successful after all events have been fired. Events can be either:
+    - `load` - consider navigation to be finished when the `load` event is fired.
+    - `domcontentloaded` - consider navigation to be finished when the `DOMContentLoaded` event is fired.
+    - `networkidle0` - consider navigation to be finished when there are no more than 0 network connections for at least `500` ms.
+    - `networkidle2` - consider navigation to be finished when there are no more than 2 network connections for at least `500` ms.
+- returns: <[Promise]<?[Response]>> Promise which resolves to the main resource response. In case of multiple redirects, the navigation will resolve with the response of the last redirect. If
+can not go forward, resolves to `null`.
+
+Navigate to the next page in history.
+
+#### page.goto(url[, options])
+- `url` <[string]> URL to navigate page to. The url should include scheme, e.g. `https://`.
+- `options` <[Object]> Navigation parameters which might have the following properties:
+  - `timeout` <[number]> Maximum navigation time in milliseconds, defaults to 30 seconds, pass `0` to disable timeout. The default value can be changed by using the [page.setDefaultNavigationTimeout(timeout)](#pagesetdefaultnavigationtimeouttimeout) or [page.setDefaultTimeout(timeout)](#pagesetdefaulttimeouttimeout) methods.
+  - `waitUntil` <[string]|[Array]<[string]>> When to consider navigation succeeded, defaults to `load`. Given an array of event strings, navigation is considered to be successful after all events have been fired. Events can be either:
+    - `load` - consider navigation to be finished when the `load` event is fired.
+    - `domcontentloaded` - consider navigation to be finished when the `DOMContentLoaded` event is fired.
+    - `networkidle0` - consider navigation to be finished when there are no more than 0 network connections for at least `500` ms.
+    - `networkidle2` - consider navigation to be finished when there are no more than 2 network connections for at least `500` ms.
+  - `referer` <[string]> Referer header value. If provided it will take preference over the referer header value set by [page.setExtraHTTPHeaders()](#pagesetextrahttpheadersheaders).
+- returns: <[Promise]<?[Response]>> Promise which resolves to the main resource response. In case of multiple redirects, the navigation will resolve with the response of the last redirect.
+
+The `page.goto` will throw an error if:
+- there's an SSL error (e.g. in case of self-signed certificates).
+- target URL is invalid.
+- the `timeout` is exceeded during navigation.
+- the main resource failed to load.
+
+> **NOTE** `page.goto` either throw or return a main resource response. The only exceptions are navigation to `about:blank` or navigation to the same URL with a different hash, which would succeed and return `null`.
+
+> **NOTE** Headless mode doesn't support navigation to a PDF document. See the [upstream issue](https://bugs.chromium.org/p/chromium/issues/detail?id=761295).
+
+Shortcut for [page.mainFrame().goto(url, options)](#framegotourl-options)
+
+#### page.hover(selector)
+- `selector` <[string]> A [selector] to search for element to hover. If there are multiple elements satisfying the selector, the first will be hovered.
+- returns: <[Promise]> Promise which resolves when the element matching `selector` is successfully hovered. Promise gets rejected if there's no element matching `selector`.
+
+This method fetches an element with `selector`, scrolls it into view if needed, and then uses [page.mouse](#pagemouse) to hover over the center of the element.
+If there's no element matching `selector`, the method throws an error.
+
+Shortcut for [page.mainFrame().hover(selector)](#framehoverselector).
+
+#### page.isClosed()
+
+- returns: <[boolean]>
+
+Indicates that the page has been closed.
+
+#### page.keyboard
+
+- returns: <[Keyboard]>
+
+#### page.mainFrame()
+- returns: <[Frame]> The page's main frame.
+
+Page is guaranteed to have a main frame which persists during navigations.
+
+#### page.metrics()
+- returns: <[Promise]<[Object]>> Object containing metrics as key/value pairs.
+  - `Timestamp` <[number]> The timestamp when the metrics sample was taken.
+  - `Documents` <[number]> Number of documents in the page.
+  - `Frames` <[number]> Number of frames in the page.
+  - `JSEventListeners` <[number]> Number of events in the page.
+  - `Nodes` <[number]> Number of DOM nodes in the page.
+  - `LayoutCount` <[number]> Total number of full or partial page layout.
+  - `RecalcStyleCount` <[number]> Total number of page style recalculations.
+  - `LayoutDuration` <[number]> Combined durations of all page layouts.
+  - `RecalcStyleDuration` <[number]> Combined duration of all page style recalculations.
+  - `ScriptDuration` <[number]> Combined duration of JavaScript execution.
+  - `TaskDuration` <[number]> Combined duration of all tasks performed by the browser.
+  - `JSHeapUsedSize` <[number]> Used JavaScript heap size.
+  - `JSHeapTotalSize` <[number]> Total JavaScript heap size.
+
+> **NOTE** All timestamps are in monotonic time: monotonically increasing time in seconds since an arbitrary point in the past.
+
+#### page.mouse
+
+- returns: <[Mouse]>
+
+#### page.pdf([options])
+- `options` <[Object]> Options object which might have the following properties:
+  - `path` <[string]> The file path to save the PDF to. If `path` is a relative path, then it is resolved relative to [current working directory](https://nodejs.org/api/process.html#process_process_cwd). If no path is provided, the PDF won't be saved to the disk.
+  - `scale` <[number]> Scale of the webpage rendering. Defaults to `1`. Scale amount must be between 0.1 and 2.
+  - `displayHeaderFooter` <[boolean]> Display header and footer. Defaults to `false`.
+  - `headerTemplate` <[string]> HTML template for the print header. Should be valid HTML markup with following classes used to inject printing values into them:
+    - `date` formatted print date
+    - `title` document title
+    - `url` document location
+    - `pageNumber` current page number
+    - `totalPages` total pages in the document
+  - `footerTemplate` <[string]> HTML template for the print footer. Should use the same format as the `headerTemplate`.
+  - `printBackground` <[boolean]> Print background graphics. Defaults to `false`.
+  - `landscape` <[boolean]> Paper orientation. Defaults to `false`.
+  - `pageRanges` <[string]> Paper ranges to print, e.g., '1-5, 8, 11-13'. Defaults to the empty string, which means print all pages.
+  - `format` <[string]> Paper format. If set, takes priority over `width` or `height` options. Defaults to 'Letter'.
+  - `width` <[string]|[number]> Paper width, accepts values labeled with units.
+  - `height` <[string]|[number]> Paper height, accepts values labeled with units.
+  - `margin` <[Object]> Paper margins, defaults to none.
+    - `top` <[string]|[number]> Top margin, accepts values labeled with units.
+    - `right` <[string]|[number]> Right margin, accepts values labeled with units.
+    - `bottom` <[string]|[number]> Bottom margin, accepts values labeled with units.
+    - `left` <[string]|[number]> Left margin, accepts values labeled with units.
+  - `preferCSSPageSize` <[boolean]> Give any CSS `@page` size declared in the page priority over what is declared in `width` and `height` or `format` options. Defaults to `false`, which will scale the content to fit the paper size.
+- returns: <[Promise]<[Buffer]>> Promise which resolves with PDF buffer.
+
+> **NOTE** Generating a pdf is currently only supported in Chrome headless.
+
+`page.pdf()` generates a pdf of the page with `print` css media. To generate a pdf with `screen` media, call [page.emulateMedia('screen')](#pageemulatemediamediatype) before calling `page.pdf()`:
+
+> **NOTE** By default, `page.pdf()` generates a pdf with modified colors for printing. Use the [`-webkit-print-color-adjust`](https://developer.mozilla.org/en-US/docs/Web/CSS/-webkit-print-color-adjust) property to force rendering of exact colors.
+
+```js
+// Generates a PDF with 'screen' media type.
+await page.emulateMedia('screen');
+await page.pdf({path: 'page.pdf'});
+```
+
+The `width`, `height`, and `margin` options accept values labeled with units. Unlabeled values are treated as pixels.
+
+A few examples:
+- `page.pdf({width: 100})` - prints with width set to 100 pixels
+- `page.pdf({width: '100px'})` - prints with width set to 100 pixels
+- `page.pdf({width: '10cm'})` - prints with width set to 10 centimeters.
+
+All possible units are:
+- `px` - pixel
+- `in` - inch
+- `cm` - centimeter
+- `mm` - millimeter
+
+The `format` options are:
+- `Letter`: 8.5in x 11in
+- `Legal`: 8.5in x 14in
+- `Tabloid`: 11in x 17in
+- `Ledger`: 17in x 11in
+- `A0`: 33.1in x 46.8in
+- `A1`: 23.4in x 33.1in
+- `A2`: 16.5in x 23.4in
+- `A3`: 11.7in x 16.5in
+- `A4`: 8.27in x 11.7in
+- `A5`: 5.83in x 8.27in
+- `A6`: 4.13in x 5.83in
+
+> **NOTE** `headerTemplate` and `footerTemplate` markup have the following limitations:
+> 1. Script tags inside templates are not evaluated.
+> 2. Page styles are not visible inside templates.
+
+#### page.queryObjects(prototypeHandle)
+- `prototypeHandle` <[JSHandle]> A handle to the object prototype.
+- returns: <[Promise]<[JSHandle]>> Promise which resolves to a handle to an array of objects with this prototype.
+
+The method iterates the JavaScript heap and finds all the objects with the given prototype.
+
+```js
+// Create a Map object
+await page.evaluate(() => window.map = new Map());
+// Get a handle to the Map object prototype
+const mapPrototype = await page.evaluateHandle(() => Map.prototype);
+// Query all map instances into an array
+const mapInstances = await page.queryObjects(mapPrototype);
+// Count amount of map objects in heap
+const count = await page.evaluate(maps => maps.length, mapInstances);
+await mapInstances.dispose();
+await mapPrototype.dispose();
+```
+
+Shortcut for [page.mainFrame().executionContext().queryObjects(prototypeHandle)](#executioncontextqueryobjectsprototypehandle).
+
+#### page.reload([options])
+- `options` <[Object]> Navigation parameters which might have the following properties:
+  - `timeout` <[number]> Maximum navigation time in milliseconds, defaults to 30 seconds, pass `0` to disable timeout. The default value can be changed by using the [page.setDefaultNavigationTimeout(timeout)](#pagesetdefaultnavigationtimeouttimeout) or [page.setDefaultTimeout(timeout)](#pagesetdefaulttimeouttimeout) methods.
+  - `waitUntil` <[string]|[Array]<[string]>> When to consider navigation succeeded, defaults to `load`. Given an array of event strings, navigation is considered to be successful after all events have been fired. Events can be either:
+    - `load` - consider navigation to be finished when the `load` event is fired.
+    - `domcontentloaded` - consider navigation to be finished when the `DOMContentLoaded` event is fired.
+    - `networkidle0` - consider navigation to be finished when there are no more than 0 network connections for at least `500` ms.
+    - `networkidle2` - consider navigation to be finished when there are no more than 2 network connections for at least `500` ms.
+- returns: <[Promise]<[Response]>> Promise which resolves to the main resource response. In case of multiple redirects, the navigation will resolve with the response of the last redirect.
+
+#### page.screenshot([options])
+- `options` <[Object]> Options object which might have the following properties:
+  - `path` <[string]> The file path to save the image to. The screenshot type will be inferred from file extension. If `path` is a relative path, then it is resolved relative to [current working directory](https://nodejs.org/api/process.html#process_process_cwd). If no path is provided, the image won't be saved to the disk.
+  - `type` <[string]> Specify screenshot type, can be either `jpeg` or `png`. Defaults to 'png'.
+  - `quality` <[number]> The quality of the image, between 0-100. Not applicable to `png` images.
+  - `fullPage` <[boolean]> When true, takes a screenshot of the full scrollable page. Defaults to `false`.
+  - `clip` <[Object]> An object which specifies clipping region of the page. Should have the following fields:
+    - `x` <[number]> x-coordinate of top-left corner of clip area
+    - `y` <[number]> y-coordinate of top-left corner of clip area
+    - `width` <[number]> width of clipping area
+    - `height` <[number]> height of clipping area
+  - `omitBackground` <[boolean]> Hides default white background and allows capturing screenshots with transparency. Defaults to `false`.
+  - `encoding` <[string]> The encoding of the image, can be either `base64` or `binary`. Defaults to `binary`.
+- returns: <[Promise]<[string]|[Buffer]>> Promise which resolves to buffer or a base64 string (depending on the value of `encoding`) with captured screenshot.
+
+> **NOTE** Screenshots take at least 1/6 second on OS X. See https://crbug.com/741689 for discussion.
+
+#### page.select(selector, ...values)
+- `selector` <[string]> A [selector] to query page for
+- `...values` <...[string]> Values of options to select. If the `<select>` has the `multiple` attribute, all values are considered, otherwise only the first one is taken into account.
+- returns: <[Promise]<[Array]<[string]>>> An array of option values that have been successfully selected.
+
+Triggers a `change` and `input` event once all the provided options have been selected.
+If there's no `<select>` element matching `selector`, the method throws an error.
+
+```js
+page.select('select#colors', 'blue'); // single selection
+page.select('select#colors', 'red', 'green', 'blue'); // multiple selections
+```
+
+Shortcut for [page.mainFrame().select()](#frameselectselector-values)
+
+#### page.setBypassCSP(enabled)
+- `enabled` <[boolean]> sets bypassing of page's Content-Security-Policy.
+- returns: <[Promise]>
+
+Toggles bypassing page's Content-Security-Policy.
+
+> **NOTE** CSP bypassing happens at the moment of CSP initialization rather then evaluation. Usually this means
+that `page.setBypassCSP` should be called before navigating to the domain.
+
+#### page.setCacheEnabled([enabled])
+- `enabled` <[boolean]> sets the `enabled` state of the cache.
+- returns: <[Promise]>
+
+Toggles ignoring cache for each request based on the enabled state. By default, caching is enabled.
+
+#### page.setContent(html[, options])
+- `html` <[string]> HTML markup to assign to the page.
+- `options` <[Object]> Parameters which might have the following properties:
+  - `timeout` <[number]> Maximum time in milliseconds for resources to load, defaults to 30 seconds, pass `0` to disable timeout. The default value can be changed by using the [page.setDefaultNavigationTimeout(timeout)](#pagesetdefaultnavigationtimeouttimeout) or [page.setDefaultTimeout(timeout)](#pagesetdefaulttimeouttimeout) methods.
+  - `waitUntil` <[string]|[Array]<[string]>> When to consider setting markup succeeded, defaults to `load`. Given an array of event strings, setting content is considered to be successful after all events have been fired. Events can be either:
+    - `load` - consider setting content to be finished when the `load` event is fired.
+    - `domcontentloaded` - consider setting content to be finished when the `DOMContentLoaded` event is fired.
+    - `networkidle0` - consider setting content to be finished when there are no more than 0 network connections for at least `500` ms.
+    - `networkidle2` - consider setting content to be finished when there are no more than 2 network connections for at least `500` ms.
+- returns: <[Promise]>
+
+#### page.setCookie(...cookies)
+- `...cookies` <...[Object]>
+  - `name` <[string]> **required**
+  - `value` <[string]> **required**
+  - `url` <[string]>
+  - `domain` <[string]>
+  - `path` <[string]>
+  - `expires` <[number]> Unix time in seconds.
+  - `httpOnly` <[boolean]>
+  - `secure` <[boolean]>
+  - `sameSite` <"Strict"|"Lax">
+- returns: <[Promise]>
+
+```js
+await page.setCookie(cookieObject1, cookieObject2);
+```
+
+#### page.setDefaultNavigationTimeout(timeout)
+- `timeout` <[number]> Maximum navigation time in milliseconds
+
+This setting will change the default maximum navigation time for the following methods and related shortcuts:
+- [page.goBack([options])](#pagegobackoptions)
+- [page.goForward([options])](#pagegoforwardoptions)
+- [page.goto(url[, options])](#pagegotourl-options)
+- [page.reload([options])](#pagereloadoptions)
+- [page.setContent(html[, options])](#pagesetcontenthtml-options)
+- [page.waitForNavigation([options])](#pagewaitfornavigationoptions)
+
+> **NOTE** [`page.setDefaultNavigationTimeout`](#pagesetdefaultnavigationtimeouttimeout) takes priority over [`page.setDefaultTimeout`](#pagesetdefaulttimeouttimeout)
+
+
+#### page.setDefaultTimeout(timeout)
+- `timeout` <[number]> Maximum time in milliseconds
+
+This setting will change the default maximum time for the following methods and related shortcuts:
+- [page.goBack([options])](#pagegobackoptions)
+- [page.goForward([options])](#pagegoforwardoptions)
+- [page.goto(url[, options])](#pagegotourl-options)
+- [page.reload([options])](#pagereloadoptions)
+- [page.setContent(html[, options])](#pagesetcontenthtml-options)
+- [page.waitFor(selectorOrFunctionOrTimeout[, options[, ...args]])](#pagewaitforselectororfunctionortimeout-options-args)
+- [page.waitForFunction(pageFunction[, options[, ...args]])](#pagewaitforfunctionpagefunction-options-args)
+- [page.waitForNavigation([options])](#pagewaitfornavigationoptions)
+- [page.waitForRequest(urlOrPredicate[, options])](#pagewaitforrequesturlorpredicate-options)
+- [page.waitForResponse(urlOrPredicate[, options])](#pagewaitforresponseurlorpredicate-options)
+- [page.waitForSelector(selector[, options])](#pagewaitforselectorselector-options)
+- [page.waitForXPath(xpath[, options])](#pagewaitforxpathxpath-options)
+
+> **NOTE** [`page.setDefaultNavigationTimeout`](#pagesetdefaultnavigationtimeouttimeout) takes priority over [`page.setDefaultTimeout`](#pagesetdefaulttimeouttimeout)
+
+#### page.setExtraHTTPHeaders(headers)
+- `headers` <[Object]> An object containing additional HTTP headers to be sent with every request. All header values must be strings.
+- returns: <[Promise]>
+
+The extra HTTP headers will be sent with every request the page initiates.
+
+> **NOTE** page.setExtraHTTPHeaders does not guarantee the order of headers in the outgoing requests.
+
+#### page.setGeolocation(options)
+- `options` <[Object]>
+  - `latitude` <[number]> Latitude between -90 and 90.
+  - `longitude` <[number]> Longitude between -180 and 180.
+  - `accuracy` <[number]> Optional non-negative accuracy value.
+- returns: <[Promise]>
+
+Sets the page's geolocation.
+
+```js
+await page.setGeolocation({latitude: 59.95, longitude: 30.31667});
+```
+
+> **NOTE** Consider using [browserContext.overridePermissions](#browsercontextoverridepermissionsorigin-permissions) to grant permissions for the page to read its geolocation.
+
+#### page.setJavaScriptEnabled(enabled)
+- `enabled` <[boolean]> Whether or not to enable JavaScript on the page.
+- returns: <[Promise]>
+
+> **NOTE** changing this value won't affect scripts that have already been run. It will take full effect on the next [navigation](#pagegotourl-options).
+
+#### page.setOfflineMode(enabled)
+- `enabled` <[boolean]> When `true`, enables offline mode for the page.
+- returns: <[Promise]>
+
+#### page.setRequestInterception(value)
+- `value` <[boolean]> Whether to enable request interception.
+- returns: <[Promise]>
+
+Activating request interception enables `request.abort`, `request.continue` and
+`request.respond` methods.  This provides the capability to modify network requests that are made by a page.
+
+Once request interception is enabled, every request will stall unless it's continued, responded or aborted.
+An example of a naïve request interceptor that aborts all image requests:
+
+```js
+const puppeteer = require('puppeteer');
+
+puppeteer.launch().then(async browser => {
+  const page = await browser.newPage();
+  await page.setRequestInterception(true);
+  page.on('request', interceptedRequest => {
+    if (interceptedRequest.url().endsWith('.png') || interceptedRequest.url().endsWith('.jpg'))
+      interceptedRequest.abort();
+    else
+      interceptedRequest.continue();
+  });
+  await page.goto('https://example.com');
+  await browser.close();
+});
+```
+
+> **NOTE** Enabling request interception disables page caching.
+
+#### page.setUserAgent(userAgent)
+- `userAgent` <[string]> Specific user agent to use in this page
+- returns: <[Promise]> Promise which resolves when the user agent is set.
+
+#### page.setViewport(viewport)
+- `viewport` <[Object]>
+  - `width` <[number]> page width in pixels. **required**
+  - `height` <[number]> page height in pixels. **required**
+  - `deviceScaleFactor` <[number]> Specify device scale factor (can be thought of as dpr). Defaults to `1`.
+  - `isMobile` <[boolean]> Whether the `meta viewport` tag is taken into account. Defaults to `false`.
+  - `hasTouch`<[boolean]> Specifies if viewport supports touch events. Defaults to `false`
+  - `isLandscape` <[boolean]> Specifies if viewport is in landscape mode. Defaults to `false`.
+- returns: <[Promise]>
+
+> **NOTE** in certain cases, setting viewport will reload the page in order to set the `isMobile` or `hasTouch` properties.
+
+In the case of multiple pages in a single browser, each page can have its own viewport size.
+
+#### page.tap(selector)
+- `selector` <[string]> A [selector] to search for element to tap. If there are multiple elements satisfying the selector, the first will be tapped.
+- returns: <[Promise]>
+
+This method fetches an element with `selector`, scrolls it into view if needed, and then uses [page.touchscreen](#pagetouchscreen) to tap in the center of the element.
+If there's no element matching `selector`, the method throws an error.
+
+Shortcut for [page.mainFrame().tap(selector)](#frametapselector).
+
+#### page.target()
+- returns: <[Target]> a target this page was created from.
+
+#### page.title()
+- returns: <[Promise]<[string]>> The page's title.
+
+Shortcut for [page.mainFrame().title()](#frametitle).
+
+#### page.touchscreen
+- returns: <[Touchscreen]>
+
+#### page.tracing
+- returns: <[Tracing]>
+
+#### page.type(selector, text[, options])
+- `selector` <[string]> A [selector] of an element to type into. If there are multiple elements satisfying the selector, the first will be used.
+- `text` <[string]> A text to type into a focused element.
+- `options` <[Object]>
+  - `delay` <[number]> Time to wait between key presses in milliseconds. Defaults to 0.
+- returns: <[Promise]>
+
+Sends a `keydown`, `keypress`/`input`, and `keyup` event for each character in the text.
+
+To press a special key, like `Control` or `ArrowDown`, use [`keyboard.press`](#keyboardpresskey-options).
+
+```js
+page.type('#mytextarea', 'Hello'); // Types instantly
+page.type('#mytextarea', 'World', {delay: 100}); // Types slower, like a user
+```
+
+Shortcut for [page.mainFrame().type(selector, text[, options])](#frametypeselector-text-options).
+
+#### page.url()
+- returns: <[string]>
+
+This is a shortcut for [page.mainFrame().url()](#frameurl)
+
+#### page.viewport()
+- returns: <?[Object]>
+  - `width` <[number]> page width in pixels.
+  - `height` <[number]> page height in pixels.
+  - `deviceScaleFactor` <[number]> Specify device scale factor (can be though of as dpr). Defaults to `1`.
+  - `isMobile` <[boolean]> Whether the `meta viewport` tag is taken into account. Defaults to `false`.
+  - `hasTouch`<[boolean]> Specifies if viewport supports touch events. Defaults to `false`
+  - `isLandscape` <[boolean]> Specifies if viewport is in landscape mode. Defaults to `false`.
+
+#### page.waitFor(selectorOrFunctionOrTimeout[, options[, ...args]])
+- `selectorOrFunctionOrTimeout` <[string]|[number]|[function]> A [selector], predicate or timeout to wait for
+- `options` <[Object]> Optional waiting parameters
+- `...args` <...[Serializable]|[JSHandle]> Arguments to pass to  `pageFunction`
+- returns: <[Promise]<[JSHandle]>> Promise which resolves to a JSHandle of the success value
+
+This method behaves differently with respect to the type of the first parameter:
+- if `selectorOrFunctionOrTimeout` is a `string`, then the first argument is treated as a [selector] or [xpath], depending on whether or not it starts with '//', and the method is a shortcut for
+  [page.waitForSelector](#pagewaitforselectorselector-options) or [page.waitForXPath](#pagewaitforxpathxpath-options)
+- if `selectorOrFunctionOrTimeout` is a `function`, then the first argument is treated as a predicate to wait for and the method is a shortcut for [page.waitForFunction()](#pagewaitforfunctionpagefunction-options-args).
+- if `selectorOrFunctionOrTimeout` is a `number`, then the first argument is treated as a timeout in milliseconds and the method returns a promise which resolves after the timeout
+- otherwise, an exception is thrown
+
+```js
+// wait for selector
+await page.waitFor('.foo');
+// wait for 1 second
+await page.waitFor(1000);
+// wait for predicate
+await page.waitFor(() => !!document.querySelector('.foo'));
+```
+
+To pass arguments from node.js to the predicate of `page.waitFor` function:
+
+```js
+const selector = '.foo';
+await page.waitFor(selector => !!document.querySelector(selector), {}, selector);
+```
+
+Shortcut for [page.mainFrame().waitFor(selectorOrFunctionOrTimeout[, options[, ...args]])](#framewaitforselectororfunctionortimeout-options-args).
+
+#### page.waitForFunction(pageFunction[, options[, ...args]])
+- `pageFunction` <[function]|[string]> Function to be evaluated in browser context
+- `options` <[Object]> Optional waiting parameters
+  - `polling` <[string]|[number]> An interval at which the `pageFunction` is executed, defaults to `raf`. If `polling` is a number, then it is treated as an interval in milliseconds at which the function would be executed. If `polling` is a string, then it can be one of the following values:
+    - `raf` - to constantly execute `pageFunction` in `requestAnimationFrame` callback. This is the tightest polling mode which is suitable to observe styling changes.
+    - `mutation` - to execute `pageFunction` on every DOM mutation.
+  - `timeout` <[number]> maximum time to wait for in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can be changed by using the [page.setDefaultTimeout(timeout)](#pagesetdefaulttimeouttimeout) method.
+- `...args` <...[Serializable]|[JSHandle]> Arguments to pass to  `pageFunction`
+- returns: <[Promise]<[JSHandle]>> Promise which resolves when the `pageFunction` returns a truthy value. It resolves to a JSHandle of the truthy value.
+
+The `waitForFunction` can be used to observe viewport size change:
+```js
+const puppeteer = require('puppeteer');
+
+puppeteer.launch().then(async browser => {
+  const page = await browser.newPage();
+  const watchDog = page.waitForFunction('window.innerWidth < 100');
+  await page.setViewport({width: 50, height: 50});
+  await watchDog;
+  await browser.close();
+});
+```
+
+To pass arguments from node.js to the predicate of `page.waitForFunction` function:
+
+```js
+const selector = '.foo';
+await page.waitForFunction(selector => !!document.querySelector(selector), {}, selector);
+```
+
+Shortcut for [page.mainFrame().waitForFunction(pageFunction[, options[, ...args]])](#framewaitforfunctionpagefunction-options-args).
+
+#### page.waitForNavigation([options])
+- `options` <[Object]> Navigation parameters which might have the following properties:
+  - `timeout` <[number]> Maximum navigation time in milliseconds, defaults to 30 seconds, pass `0` to disable timeout. The default value can be changed by using the [page.setDefaultNavigationTimeout(timeout)](#pagesetdefaultnavigationtimeouttimeout) or [page.setDefaultTimeout(timeout)](#pagesetdefaulttimeouttimeout) methods.
+  - `waitUntil` <[string]|[Array]<[string]>> When to consider navigation succeeded, defaults to `load`. Given an array of event strings, navigation is considered to be successful after all events have been fired. Events can be either:
+    - `load` - consider navigation to be finished when the `load` event is fired.
+    - `domcontentloaded` - consider navigation to be finished when the `DOMContentLoaded` event is fired.
+    - `networkidle0` - consider navigation to be finished when there are no more than 0 network connections for at least `500` ms.
+    - `networkidle2` - consider navigation to be finished when there are no more than 2 network connections for at least `500` ms.
+- returns: <[Promise]<?[Response]>> Promise which resolves to the main resource response. In case of multiple redirects, the navigation will resolve with the response of the last redirect. In case of navigation to a different anchor or navigation due to History API usage, the navigation will resolve with `null`.
+
+This resolves when the page navigates to a new URL or reloads. It is useful for when you run code
+which will indirectly cause the page to navigate. Consider this example:
+
+```js
+const [response] = await Promise.all([
+  page.waitForNavigation(), // The promise resolves after navigation has finished
+  page.click('a.my-link'), // Clicking the link will indirectly cause a navigation
+]);
+```
+
+**NOTE** Usage of the [History API](https://developer.mozilla.org/en-US/docs/Web/API/History_API) to change the URL is considered a navigation.
+
+Shortcut for [page.mainFrame().waitForNavigation(options)](#framewaitfornavigationoptions).
+
+#### page.waitForRequest(urlOrPredicate[, options])
+- `urlOrPredicate` <[string]|[Function]> A URL or predicate to wait for.
+- `options` <[Object]> Optional waiting parameters
+  - `timeout` <[number]> Maximum wait time in milliseconds, defaults to 30 seconds, pass `0` to disable the timeout. The default value can be changed by using the [page.setDefaultTimeout(timeout)](#pagesetdefaulttimeouttimeout) method.
+- returns: <[Promise]<[Request]>> Promise which resolves to the matched request.
+
+```js
+const firstRequest = await page.waitForRequest('http://example.com/resource');
+const finalRequest = await page.waitForRequest(request => request.url() === 'http://example.com' && request.method() === 'GET');
+return firstRequest.url();
+```
+
+#### page.waitForResponse(urlOrPredicate[, options])
+- `urlOrPredicate` <[string]|[Function]> A URL or predicate to wait for.
+- `options` <[Object]> Optional waiting parameters
+  - `timeout` <[number]> Maximum wait time in milliseconds, defaults to 30 seconds, pass `0` to disable the timeout. The default value can be changed by using the [page.setDefaultTimeout(timeout)](#pagesetdefaulttimeouttimeout) method.
+- returns: <[Promise]<[Response]>> Promise which resolves to the matched response.
+
+```js
+const firstResponse = await page.waitForResponse('https://example.com/resource');
+const finalResponse = await page.waitForResponse(response => response.url() === 'https://example.com' && response.status() === 200);
+return finalResponse.ok();
+```
+
+#### page.waitForSelector(selector[, options])
+- `selector` <[string]> A [selector] of an element to wait for
+- `options` <[Object]> Optional waiting parameters
+  - `visible` <[boolean]> wait for element to be present in DOM and to be visible, i.e. to not have `display: none` or `visibility: hidden` CSS properties. Defaults to `false`.
+  - `hidden` <[boolean]> wait for element to not be found in the DOM or to be hidden, i.e. have `display: none` or `visibility: hidden` CSS properties. Defaults to `false`.
+  - `timeout` <[number]> maximum time to wait for in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can be changed by using the [page.setDefaultTimeout(timeout)](#pagesetdefaulttimeouttimeout) method.
+- returns: <[Promise]<?[ElementHandle]>> Promise which resolves when element specified by selector string is added to DOM. Resolves to `null` if waiting for `hidden: true` and selector is not found in DOM.
+
+Wait for the `selector` to appear in page. If at the moment of calling
+the method the `selector` already exists, the method will return
+immediately. If the selector doesn't appear after the `timeout` milliseconds of waiting, the function will throw.
+
+This method works across navigations:
+```js
+const puppeteer = require('puppeteer');
+
+puppeteer.launch().then(async browser => {
+  const page = await browser.newPage();
+  let currentURL;
+  page
+    .waitForSelector('img')
+    .then(() => console.log('First URL with image: ' + currentURL));
+  for (currentURL of ['https://example.com', 'https://google.com', 'https://bbc.com'])
+    await page.goto(currentURL);
+  await browser.close();
+});
+```
+Shortcut for [page.mainFrame().waitForSelector(selector[, options])](#framewaitforselectorselector-options).
+
+#### page.waitForXPath(xpath[, options])
+- `xpath` <[string]> A [xpath] of an element to wait for
+- `options` <[Object]> Optional waiting parameters
+  - `visible` <[boolean]> wait for element to be present in DOM and to be visible, i.e. to not have `display: none` or `visibility: hidden` CSS properties. Defaults to `false`.
+  - `hidden` <[boolean]> wait for element to not be found in the DOM or to be hidden, i.e. have `display: none` or `visibility: hidden` CSS properties. Defaults to `false`.
+  - `timeout` <[number]> maximum time to wait for in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can be changed by using the [page.setDefaultTimeout(timeout)](#pagesetdefaulttimeouttimeout) method.
+- returns: <[Promise]<?[ElementHandle]>> Promise which resolves when element specified by xpath string is added to DOM. Resolves to `null` if waiting for `hidden: true` and xpath is not found in DOM.
+
+Wait for the `xpath` to appear in page. If at the moment of calling
+the method the `xpath` already exists, the method will return
+immediately. If the xpath doesn't appear after the `timeout` milliseconds of waiting, the function will throw.
+
+This method works across navigations:
+```js
+const puppeteer = require('puppeteer');
+
+puppeteer.launch().then(async browser => {
+  const page = await browser.newPage();
+  let currentURL;
+  page
+    .waitForXPath('//img')
+    .then(() => console.log('First URL with image: ' + currentURL));
+  for (currentURL of ['https://example.com', 'https://google.com', 'https://bbc.com'])
+    await page.goto(currentURL);
+  await browser.close();
+});
+```
+Shortcut for [page.mainFrame().waitForXPath(xpath[, options])](#framewaitforxpathxpath-options).
+
+#### page.workers()
+- returns: <[Array]<[Worker]>>
+This method returns all of the dedicated [WebWorkers](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API) associated with the page.
+
+> **NOTE** This does not contain ServiceWorkers
+
+### class: Worker
+
+The Worker class represents a [WebWorker](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API).
+The events `workercreated` and `workerdestroyed` are emitted on the page object to signal the worker lifecycle.
+
+```js
+page.on('workercreated', worker => console.log('Worker created: ' + worker.url()));
+page.on('workerdestroyed', worker => console.log('Worker destroyed: ' + worker.url()));
+
+console.log('Current workers:');
+for (const worker of page.workers())
+  console.log('  ' + worker.url());
+```
+
+#### worker.evaluate(pageFunction[, ...args])
+- `pageFunction` <[function]|[string]> Function to be evaluated in the worker context
+- `...args` <...[Serializable]|[JSHandle]> Arguments to pass to `pageFunction`
+- returns: <[Promise]<[Serializable]>> Promise which resolves to the return value of `pageFunction`
+
+If the function passed to the `worker.evaluate` returns a [Promise], then `worker.evaluate` would wait for the promise to resolve and return its value.
+
+If the function passed to the `worker.evaluate` returns a non-[Serializable] value, then `worker.evaluate` resolves to `undefined`. DevTools Protocol also supports transferring some additional values that are not serializable by `JSON`: `-0`, `NaN`, `Infinity`, `-Infinity`, and bigint literals.
+
+Shortcut for [(await worker.executionContext()).evaluate(pageFunction, ...args)](#executioncontextevaluatepagefunction-args).
+
+#### worker.evaluateHandle(pageFunction[, ...args])
+- `pageFunction` <[function]|[string]> Function to be evaluated in the page context
+- `...args` <...[Serializable]|[JSHandle]> Arguments to pass to `pageFunction`
+- returns: <[Promise]<[JSHandle]>> Promise which resolves to the return value of `pageFunction` as in-page object (JSHandle)
+
+The only difference between `worker.evaluate` and `worker.evaluateHandle` is that `worker.evaluateHandle` returns in-page object (JSHandle).
+
+If the function passed to the `worker.evaluateHandle` returns a [Promise], then `worker.evaluateHandle` would wait for the promise to resolve and return its value.
+
+Shortcut for [(await worker.executionContext()).evaluateHandle(pageFunction, ...args)](#executioncontextevaluatehandlepagefunction-args).
+
+#### worker.executionContext()
+- returns: <[Promise]<[ExecutionContext]>>
+
+#### worker.url()
+- returns: <[string]>
+
+### class: Accessibility
+
+The Accessibility class provides methods for inspecting Chromium's accessibility tree. The accessibility tree is used by assistive technology such as [screen readers](https://en.wikipedia.org/wiki/Screen_reader) or [switches](https://en.wikipedia.org/wiki/Switch_access).
+
+Accessibility is a very platform-specific thing. On different platforms, there are different screen readers that might have wildly different output.
+
+Blink - Chrome's rendering engine - has a concept of "accessibility tree", which is than translated into different platform-specific APIs. Accessibility namespace gives users
+access to the Blink Accessibility Tree.
+
+Most of the accessibility tree gets filtered out when converting from Blink AX Tree to Platform-specific AX-Tree or by assistive technologies themselves. By default, Puppeteer tries to approximate this filtering, exposing only the "interesting" nodes of the tree.
+
+
+
+#### accessibility.snapshot([options])
+- `options` <[Object]>
+  - `interestingOnly` <[boolean]> Prune uninteresting nodes from the tree. Defaults to `true`.
+  - `root` <[ElementHandle]> The root DOM element for the snapshot. Defaults to the whole page.
+- returns: <[Promise]<[Object]>> An [AXNode] object with the following properties:
+  - `role` <[string]> The [role](https://www.w3.org/TR/wai-aria/#usage_intro).
+  - `name` <[string]> A human readable name for the node.
+  - `value` <[string]|[number]> The current value of the node.
+  - `description` <[string]> An additional human readable description of the node.
+  - `keyshortcuts` <[string]> Keyboard shortcuts associated with this node.
+  - `roledescription` <[string]> A human readable alternative to the role.
+  - `valuetext` <[string]> A description of the current value.
+  - `disabled` <[boolean]> Whether the node is disabled.
+  - `expanded` <[boolean]> Whether the node is expanded or collapsed.
+  - `focused` <[boolean]> Whether the node is focused.
+  - `modal` <[boolean]> Whether the node is [modal](https://en.wikipedia.org/wiki/Modal_window).
+  - `multiline` <[boolean]> Whether the node text input supports multiline.
+  - `multiselectable` <[boolean]> Whether more than one child can be selected.
+  - `readonly` <[boolean]> Whether the node is read only.
+  - `required` <[boolean]> Whether the node is required.
+  - `selected` <[boolean]> Whether the node is selected in its parent node.
+  - `checked` <[boolean]|"mixed"> Whether the checkbox is checked, or "mixed".
+  - `pressed` <[boolean]|"mixed"> Whether the toggle button is checked, or "mixed".
+  - `level` <[number]> The level of a heading.
+  - `valuemin` <[number]> The minimum value in a node.
+  - `valuemax` <[number]> The maximum value in a node.
+  - `autocomplete` <[string]> What kind of autocomplete is supported by a control.
+  - `haspopup` <[string]> What kind of popup is currently being shown for a node.
+  - `invalid` <[string]> Whether and in what way this node's value is invalid.
+  - `orientation` <[string]> Whether the node is oriented horizontally or vertically.
+  - `children` <[Array]<[Object]>> Child [AXNode]s of this node, if any.
+
+Captures the current state of the accessibility tree. The returned object represents the root accessible node of the page.
+
+> **NOTE** The Chromium accessibility tree contains nodes that go unused on most platforms and by
+most screen readers. Puppeteer will discard them as well for an easier to process tree,
+unless `interestingOnly` is set to `false`.
+
+An example of dumping the entire accessibility tree:
+```js
+const snapshot = await page.accessibility.snapshot();
+console.log(snapshot);
+```
+
+An example of logging the focused node's name:
+```js
+const snapshot = await page.accessibility.snapshot();
+const node = findFocusedNode(snapshot);
+console.log(node && node.name);
+
+function findFocusedNode(node) {
+  if (node.focused)
+    return node;
+  for (const child of node.children || []) {
+    const foundNode = findFocusedNode(child);
+    return foundNode;
+  }
+  return null;
+}
+```
+
+### class: Keyboard
+
+Keyboard provides an api for managing a virtual keyboard. The high level api is [`keyboard.type`](#keyboardtypetext-options), which takes raw characters and generates proper keydown, keypress/input, and keyup events on your page.
+
+For finer control, you can use [`keyboard.down`](#keyboarddownkey-options), [`keyboard.up`](#keyboardupkey), and [`keyboard.sendCharacter`](#keyboardsendcharacterchar) 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:
+```js
+await page.keyboard.type('Hello World!');
+await page.keyboard.press('ArrowLeft');
+
+await page.keyboard.down('Shift');
+for (let i = 0; i < ' World'.length; i++)
+  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`
+```js
+await page.keyboard.down('Shift');
+await page.keyboard.press('KeyA');
+await page.keyboard.up('Shift');
+```
+
+> **NOTE** On MacOS, keyboard shortcuts like `⌘ A` -> Select All do not work. See [#1313](https://github.com/GoogleChrome/puppeteer/issues/1313)
+
+#### keyboard.down(key[, options])
+- `key` <[string]> Name of key to press, such as `ArrowLeft`. See [USKeyboardLayout] for a list of all key names.
+- `options` <[Object]>
+  - `text` <[string]> If specified, generates an input event with this text.
+- returns: <[Promise]>
+
+Dispatches a `keydown` event.
+
+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.
+
+If `key` is a modifier key, `Shift`, `Meta`, `Control`, or `Alt`, subsequent key presses will be sent with that modifier active. To release the modifier key, use [`keyboard.up`](#keyboardupkey).
+
+After the key is pressed once, subsequent calls to [`keyboard.down`](#keyboarddownkey-options) will have [repeat](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/repeat) set to true. To release the key, use [`keyboard.up`](#keyboardupkey).
+
+> **NOTE** Modifier keys DO influence `keyboard.down`. Holding down `Shift` will type the text in upper case.
+
+#### keyboard.press(key[, options])
+- `key` <[string]> Name of key to press, such as `ArrowLeft`. See [USKeyboardLayout] for a list of all key names.
+- `options` <[Object]>
+  - `text` <[string]> If specified, generates an input event with this text.
+  - `delay` <[number]> Time to wait between `keydown` and `keyup` in milliseconds. Defaults to 0.
+- returns: <[Promise]>
+
+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.
+
+> **NOTE** Modifier keys DO effect `keyboard.press`. Holding down `Shift` will type the text in upper case.
+
+Shortcut for [`keyboard.down`](#keyboarddownkey-options) and [`keyboard.up`](#keyboardupkey).
+
+#### keyboard.sendCharacter(char)
+- `char` <[string]> Character to send into the page.
+- returns: <[Promise]>
+
+Dispatches a `keypress` and `input` event. This does not send a `keydown` or `keyup` event.
+
+```js
+page.keyboard.sendCharacter('嗨');
+```
+
+> **NOTE** Modifier keys DO NOT effect `keyboard.sendCharacter`. Holding down `Shift` will not type the text in upper case.
+
+#### keyboard.type(text[, options])
+- `text` <[string]> A text to type into a focused element.
+- `options` <[Object]>
+  - `delay` <[number]> Time to wait between key presses in milliseconds. Defaults to 0.
+- returns: <[Promise]>
+
+Sends a `keydown`, `keypress`/`input`, and `keyup` event for each character in the text.
+
+To press a special key, like `Control` or `ArrowDown`, use [`keyboard.press`](#keyboardpresskey-options).
+
+```js
+page.keyboard.type('Hello'); // Types instantly
+page.keyboard.type('World', {delay: 100}); // Types slower, like a user
+```
+
+> **NOTE** Modifier keys DO NOT effect `keyboard.type`. Holding down `Shift` will not type the text in upper case.
+
+#### keyboard.up(key)
+- `key` <[string]> Name of key to release, such as `ArrowLeft`. See [USKeyboardLayout] for a list of all key names.
+- returns: <[Promise]>
+
+Dispatches a `keyup` event.
+
+### class: Mouse
+
+The Mouse class operates in main-frame CSS pixels relative to the top-left corner of the viewport.
+
+Every `page` object has its own Mouse, accessible with [`page.mouse`](#pagemouse).
+
+```js
+// Using ‘page.mouse’ to trace a 100x100 square.
+await page.mouse.move(0, 0);
+await page.mouse.down();
+await page.mouse.move(0, 100);
+await page.mouse.move(100, 100);
+await page.mouse.move(100, 0);
+await page.mouse.move(0, 0);
+await page.mouse.up();
+```
+
+#### mouse.click(x, y[, options])
+- `x` <[number]>
+- `y` <[number]>
+- `options` <[Object]>
+  - `button` <"left"|"right"|"middle"> Defaults to `left`.
+  - `clickCount` <[number]> defaults to 1. See [UIEvent.detail].
+  - `delay` <[number]> Time to wait between `mousedown` and `mouseup` in milliseconds. Defaults to 0.
+- returns: <[Promise]>
+
+Shortcut for [`mouse.move`](#mousemovex-y-options), [`mouse.down`](#mousedownoptions) and [`mouse.up`](#mouseupoptions).
+
+#### mouse.down([options])
+- `options` <[Object]>
+  - `button` <"left"|"right"|"middle"> Defaults to `left`.
+  - `clickCount` <[number]> defaults to 1. See [UIEvent.detail].
+- returns: <[Promise]>
+
+Dispatches a `mousedown` event.
+
+#### mouse.move(x, y[, options])
+- `x` <[number]>
+- `y` <[number]>
+- `options` <[Object]>
+  - `steps` <[number]> defaults to 1. Sends intermediate `mousemove` events.
+- returns: <[Promise]>
+
+Dispatches a `mousemove` event.
+
+#### mouse.up([options])
+- `options` <[Object]>
+  - `button` <"left"|"right"|"middle"> Defaults to `left`.
+  - `clickCount` <[number]> defaults to 1. See [UIEvent.detail].
+- returns: <[Promise]>
+
+Dispatches a `mouseup` event.
+
+### class: Touchscreen
+
+#### touchscreen.tap(x, y)
+- `x` <[number]>
+- `y` <[number]>
+- returns: <[Promise]>
+
+Dispatches a `touchstart` and `touchend` event.
+
+### class: Tracing
+
+You can use [`tracing.start`](#tracingstartoptions) and [`tracing.stop`](#tracingstop) to create a trace file which can be opened in Chrome DevTools or [timeline viewer](https://chromedevtools.github.io/timeline-viewer/).
+
+```js
+await page.tracing.start({path: 'trace.json'});
+await page.goto('https://www.google.com');
+await page.tracing.stop();
+```
+
+#### tracing.start([options])
+- `options` <[Object]>
+  - `path` <[string]> A path to write the trace file to.
+  - `screenshots` <[boolean]> captures screenshots in the trace.
+  - `categories` <[Array]<[string]>> specify custom categories to use instead of default.
+- returns: <[Promise]>
+
+Only one trace can be active at a time per browser.
+
+#### tracing.stop()
+- returns: <[Promise]<[Buffer]>> Promise which resolves to buffer with trace data.
+
+### class: Dialog
+
+[Dialog] objects are dispatched by page via the ['dialog'](#event-dialog) event.
+
+An example of using `Dialog` class:
+```js
+const puppeteer = require('puppeteer');
+
+puppeteer.launch().then(async browser => {
+  const page = await browser.newPage();
+  page.on('dialog', async dialog => {
+    console.log(dialog.message());
+    await dialog.dismiss();
+    await browser.close();
+  });
+  page.evaluate(() => alert('1'));
+});
+```
+
+#### dialog.accept([promptText])
+- `promptText` <[string]> A text to enter in prompt. Does not cause any effects if the dialog's `type` is not prompt.
+- returns: <[Promise]> Promise which resolves when the dialog has been accepted.
+
+#### dialog.defaultValue()
+- returns: <[string]> If dialog is prompt, returns default prompt value. Otherwise, returns empty string.
+
+#### dialog.dismiss()
+- returns: <[Promise]> Promise which resolves when the dialog has been dismissed.
+
+#### dialog.message()
+- returns: <[string]> A message displayed in the dialog.
+
+#### dialog.type()
+- returns: <[string]> Dialog's type, can be one of `alert`, `beforeunload`, `confirm` or `prompt`.
+
+### class: ConsoleMessage
+
+[ConsoleMessage] objects are dispatched by page via the ['console'](#event-console) event.
+
+#### consoleMessage.args()
+- returns: <[Array]<[JSHandle]>>
+
+#### consoleMessage.location()
+- returns: <[Object]>
+  - `url` <[string]> URL of the resource if known or `undefined` otherwise.
+  - `lineNumber` <[number]> 0-based line number in the resource if known or `undefined` otherwise.
+  - `columnNumber` <[number]> 0-based column number in the resource if known or `undefined` otherwise.
+
+#### consoleMessage.text()
+- returns: <[string]>
+
+#### consoleMessage.type()
+- returns: <[string]>
+
+One of the following values: `'log'`, `'debug'`, `'info'`, `'error'`, `'warning'`, `'dir'`, `'dirxml'`, `'table'`, `'trace'`, `'clear'`, `'startGroup'`, `'startGroupCollapsed'`, `'endGroup'`, `'assert'`, `'profile'`, `'profileEnd'`, `'count'`, `'timeEnd'`.
+
+### class: Frame
+
+At every point of time, page exposes its current frame tree via the [page.mainFrame()](#pagemainframe) and [frame.childFrames()](#framechildframes) methods.
+
+[Frame] object's lifecycle is controlled by three events, dispatched on the page object:
+- ['frameattached'](#event-frameattached) - fired when the frame gets attached to the page. A Frame can be attached to the page only once.
+- ['framenavigated'](#event-framenavigated) - fired when the frame commits navigation to a different URL.
+- ['framedetached'](#event-framedetached) - fired when the frame gets detached from the page.  A Frame can be detached from the page only once.
+
+An example of dumping frame tree:
+
+```js
+const puppeteer = require('puppeteer');
+
+puppeteer.launch().then(async browser => {
+  const page = await browser.newPage();
+  await page.goto('https://www.google.com/chrome/browser/canary.html');
+  dumpFrameTree(page.mainFrame(), '');
+  await browser.close();
+
+  function dumpFrameTree(frame, indent) {
+    console.log(indent + frame.url());
+    for (let child of frame.childFrames())
+      dumpFrameTree(child, indent + '  ');
+  }
+});
+```
+
+An example of getting text from an iframe element:
+
+```js
+  const frame = page.frames().find(frame => frame.name() === 'myframe');
+  const text = await frame.$eval('.selector', element => element.textContent);
+  console.log(text);
+```
+
+#### frame.$(selector)
+- `selector` <[string]> A [selector] to query frame for
+- returns: <[Promise]<?[ElementHandle]>> Promise which resolves to ElementHandle pointing to the frame element.
+
+The method queries frame for the selector. If there's no such element within the frame, the method will resolve to `null`.
+
+#### frame.$$(selector)
+- `selector` <[string]> A [selector] to query frame for
+- returns: <[Promise]<[Array]<[ElementHandle]>>> Promise which resolves to ElementHandles pointing to the frame elements.
+
+The method runs `document.querySelectorAll` within the frame. If no elements match the selector, the return value resolves to `[]`.
+
+#### frame.$$eval(selector, pageFunction[, ...args])
+- `selector` <[string]> A [selector] to query frame for
+- `pageFunction` <[function]\([Array]<[Element]>\)> Function to be evaluated in browser context
+- `...args` <...[Serializable]|[JSHandle]> Arguments to pass to `pageFunction`
+- returns: <[Promise]<[Serializable]>> Promise which resolves to the return value of `pageFunction`
+
+This method runs `Array.from(document.querySelectorAll(selector))` within the frame and passes it as the first argument to `pageFunction`.
+
+If `pageFunction` returns a [Promise], then `frame.$$eval` would wait for the promise to resolve and return its value.
+
+Examples:
+```js
+const divsCounts = await frame.$$eval('div', divs => divs.length);
+```
+
+#### frame.$eval(selector, pageFunction[, ...args])
+- `selector` <[string]> A [selector] to query frame for
+- `pageFunction` <[function]\([Element]\)> Function to be evaluated in browser context
+- `...args` <...[Serializable]|[JSHandle]> Arguments to pass to `pageFunction`
+- returns: <[Promise]<[Serializable]>> Promise which resolves to the return value of `pageFunction`
+
+This method runs `document.querySelector` within the frame and passes it as the first argument to `pageFunction`. If there's no element matching `selector`, the method throws an error.
+
+If `pageFunction` returns a [Promise], then `frame.$eval` would wait for the promise to resolve and return its value.
+
+Examples:
+```js
+const searchValue = await frame.$eval('#search', el => el.value);
+const preloadHref = await frame.$eval('link[rel=preload]', el => el.href);
+const html = await frame.$eval('.main-container', e => e.outerHTML);
+```
+
+#### frame.$x(expression)
+- `expression` <[string]> Expression to [evaluate](https://developer.mozilla.org/en-US/docs/Web/API/Document/evaluate).
+- returns: <[Promise]<[Array]<[ElementHandle]>>>
+
+The method evaluates the XPath expression.
+
+#### frame.addScriptTag(options)
+- `options` <[Object]>
+  - `url` <[string]> URL of a script to be added.
+  - `path` <[string]> Path to the JavaScript file to be injected into frame. If `path` is a relative path, then it is resolved relative to [current working directory](https://nodejs.org/api/process.html#process_process_cwd).
+  - `content` <[string]> Raw JavaScript content to be injected into frame.
+  - `type` <[string]> Script type. Use 'module' in order to load a Javascript ES6 module. See [script](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script) for more details.
+- returns: <[Promise]<[ElementHandle]>> which resolves to the added tag when the script's onload fires or when the script content was injected into frame.
+
+Adds a `<script>` tag into the page with the desired url or content.
+
+#### frame.addStyleTag(options)
+- `options` <[Object]>
+  - `url` <[string]> URL of the `<link>` tag.
+  - `path` <[string]> Path to the CSS file to be injected into frame. If `path` is a relative path, then it is resolved relative to [current working directory](https://nodejs.org/api/process.html#process_process_cwd).
+  - `content` <[string]> Raw CSS content to be injected into frame.
+- returns: <[Promise]<[ElementHandle]>> which resolves to the added tag when the stylesheet's onload fires or when the CSS content was injected into frame.
+
+Adds a `<link rel="stylesheet">` tag into the page with the desired url or a `<style type="text/css">` tag with the content.
+
+#### frame.childFrames()
+- returns: <[Array]<[Frame]>>
+
+#### frame.click(selector[, options])
+- `selector` <[string]> A [selector] to search for element to click. If there are multiple elements satisfying the selector, the first will be clicked.
+- `options` <[Object]>
+  - `button` <"left"|"right"|"middle"> Defaults to `left`.
+  - `clickCount` <[number]> defaults to 1. See [UIEvent.detail].
+  - `delay` <[number]> Time to wait between `mousedown` and `mouseup` in milliseconds. Defaults to 0.
+- returns: <[Promise]> Promise which resolves when the element matching `selector` is successfully clicked. The Promise will be rejected if there is no element matching `selector`.
+
+This method fetches an element with `selector`, scrolls it into view if needed, and then uses [page.mouse](#pagemouse) to click in the center of the element.
+If there's no element matching `selector`, the method throws an error.
+
+Bear in mind that if `click()` triggers a navigation event and there's a separate `page.waitForNavigation()` promise to be resolved, you may end up with a race condition that yields unexpected results. The correct pattern for click and wait for navigation is the following:
+
+```javascript
+const [response] = await Promise.all([
+  page.waitForNavigation(waitOptions),
+  frame.click(selector, clickOptions),
+]);
+```
+
+#### frame.content()
+- returns: <[Promise]<[string]>>
+
+Gets the full HTML contents of the frame, including the doctype.
+
+#### frame.evaluate(pageFunction[, ...args])
+- `pageFunction` <[function]|[string]> Function to be evaluated in browser context
+- `...args` <...[Serializable]|[JSHandle]> Arguments to pass to  `pageFunction`
+- returns: <[Promise]<[Serializable]>> Promise which resolves to the return value of `pageFunction`
+
+If the function passed to the `frame.evaluate` returns a [Promise], then `frame.evaluate` would wait for the promise to resolve and return its value.
+
+If the function passed to the `frame.evaluate` returns a non-[Serializable] value, then `frame.evaluate` resolves to `undefined`. DevTools Protocol also supports transferring some additional values that are not serializable by `JSON`: `-0`, `NaN`, `Infinity`, `-Infinity`, and bigint literals.
+
+```js
+const result = await frame.evaluate(() => {
+  return Promise.resolve(8 * 7);
+});
+console.log(result); // prints "56"
+```
+
+A string can also be passed in instead of a function.
+
+```js
+console.log(await frame.evaluate('1 + 2')); // prints "3"
+```
+
+[ElementHandle] instances can be passed as arguments to the `frame.evaluate`:
+```js
+const bodyHandle = await frame.$('body');
+const html = await frame.evaluate(body => body.innerHTML, bodyHandle);
+await bodyHandle.dispose();
+```
+
+#### frame.evaluateHandle(pageFunction[, ...args])
+- `pageFunction` <[function]|[string]> Function to be evaluated in the page context
+- `...args` <...[Serializable]|[JSHandle]> Arguments to pass to `pageFunction`
+- returns: <[Promise]<[JSHandle]>> Promise which resolves to the return value of `pageFunction` as in-page object (JSHandle)
+
+The only difference between `frame.evaluate` and `frame.evaluateHandle` is that `frame.evaluateHandle` returns in-page object (JSHandle).
+
+If the function, passed to the `frame.evaluateHandle`, returns a [Promise], then `frame.evaluateHandle` would wait for the promise to resolve and return its value.
+
+```js
+const aWindowHandle = await frame.evaluateHandle(() => Promise.resolve(window));
+aWindowHandle; // Handle for the window object.
+```
+
+A string can also be passed in instead of a function.
+
+```js
+const aHandle = await frame.evaluateHandle('document'); // Handle for the 'document'.
+```
+
+[JSHandle] instances can be passed as arguments to the `frame.evaluateHandle`:
+```js
+const aHandle = await frame.evaluateHandle(() => document.body);
+const resultHandle = await frame.evaluateHandle(body => body.innerHTML, aHandle);
+console.log(await resultHandle.jsonValue());
+await resultHandle.dispose();
+```
+
+
+#### frame.executionContext()
+- returns: <[Promise]<[ExecutionContext]>>
+
+Returns promise that resolves to the frame's default execution context.
+
+#### frame.focus(selector)
+- `selector` <[string]> A [selector] of an element to focus. If there are multiple elements satisfying the selector, the first will be focused.
+- returns: <[Promise]> Promise which resolves when the element matching `selector` is successfully focused. The promise will be rejected if there is no element matching `selector`.
+
+This method fetches an element with `selector` and focuses it.
+If there's no element matching `selector`, the method throws an error.
+
+#### frame.goto(url[, options])
+- `url` <[string]> URL to navigate frame to. The url should include scheme, e.g. `https://`.
+- `options` <[Object]> Navigation parameters which might have the following properties:
+  - `timeout` <[number]> Maximum navigation time in milliseconds, defaults to 30 seconds, pass `0` to disable timeout. The default value can be changed by using the [page.setDefaultNavigationTimeout(timeout)](#pagesetdefaultnavigationtimeouttimeout) or [page.setDefaultTimeout(timeout)](#pagesetdefaulttimeouttimeout) methods.
+  - `waitUntil` <[string]|[Array]<[string]>> When to consider navigation succeeded, defaults to `load`. Given an array of event strings, navigation is considered to be successful after all events have been fired. Events can be either:
+    - `load` - consider navigation to be finished when the `load` event is fired.
+    - `domcontentloaded` - consider navigation to be finished when the `DOMContentLoaded` event is fired.
+    - `networkidle0` - consider navigation to be finished when there are no more than 0 network connections for at least `500` ms.
+    - `networkidle2` - consider navigation to be finished when there are no more than 2 network connections for at least `500` ms.
+  - `referer` <[string]> Referer header value. If provided it will take preference over the referer header value set by [page.setExtraHTTPHeaders()](#pagesetextrahttpheadersheaders).
+- returns: <[Promise]<?[Response]>> Promise which resolves to the main resource response. In case of multiple redirects, the navigation will resolve with the response of the last redirect.
+
+The `frame.goto` will throw an error if:
+- there's an SSL error (e.g. in case of self-signed certificates).
+- target URL is invalid.
+- the `timeout` is exceeded during navigation.
+- the main resource failed to load.
+
+> **NOTE** `frame.goto` either throw or return a main resource response. The only exceptions are navigation to `about:blank` or navigation to the same URL with a different hash, which would succeed and return `null`.
+
+> **NOTE** Headless mode doesn't support navigation to a PDF document. See the [upstream issue](https://bugs.chromium.org/p/chromium/issues/detail?id=761295).
+
+
+#### frame.hover(selector)
+- `selector` <[string]> A [selector] to search for element to hover. If there are multiple elements satisfying the selector, the first will be hovered.
+- returns: <[Promise]> Promise which resolves when the element matching `selector` is successfully hovered. Promise gets rejected if there's no element matching `selector`.
+
+This method fetches an element with `selector`, scrolls it into view if needed, and then uses [page.mouse](#pagemouse) to hover over the center of the element.
+If there's no element matching `selector`, the method throws an error.
+
+#### frame.isDetached()
+- returns: <[boolean]>
+
+Returns `true` if the frame has been detached, or `false` otherwise.
+
+#### frame.name()
+- returns: <[string]>
+
+Returns frame's name attribute as specified in the tag.
+
+If the name is empty, returns the id attribute instead.
+
+> **NOTE** This value is calculated once when the frame is created, and will not update if the attribute is changed later.
+
+#### frame.parentFrame()
+- returns: <?[Frame]> Parent frame, if any. Detached frames and main frames return `null`.
+
+#### frame.select(selector, ...values)
+- `selector` <[string]> A [selector] to query frame for
+- `...values` <...[string]> Values of options to select. If the `<select>` has the `multiple` attribute, all values are considered, otherwise only the first one is taken into account.
+- returns: <[Promise]<[Array]<[string]>>> An array of option values that have been successfully selected.
+
+Triggers a `change` and `input` event once all the provided options have been selected.
+If there's no `<select>` element matching `selector`, the method throws an error.
+
+```js
+frame.select('select#colors', 'blue'); // single selection
+frame.select('select#colors', 'red', 'green', 'blue'); // multiple selections
+```
+
+#### frame.setContent(html[, options])
+- `html` <[string]> HTML markup to assign to the page.
+- `options` <[Object]> Parameters which might have the following properties:
+  - `timeout` <[number]> Maximum time in milliseconds for resources to load, defaults to 30 seconds, pass `0` to disable timeout. The default value can be changed by using the [page.setDefaultNavigationTimeout(timeout)](#pagesetdefaultnavigationtimeouttimeout) or [page.setDefaultTimeout(timeout)](#pagesetdefaulttimeouttimeout) methods.
+  - `waitUntil` <[string]|[Array]<[string]>> When to consider setting markup succeeded, defaults to `load`. Given an array of event strings, setting content is considered to be successful after all events have been fired. Events can be either:
+    - `load` - consider setting content to be finished when the `load` event is fired.
+    - `domcontentloaded` - consider setting content to be finished when the `DOMContentLoaded` event is fired.
+    - `networkidle0` - consider setting content to be finished when there are no more than 0 network connections for at least `500` ms.
+    - `networkidle2` - consider setting content to be finished when there are no more than 2 network connections for at least `500` ms.
+- returns: <[Promise]>
+
+#### frame.tap(selector)
+- `selector` <[string]> A [selector] to search for element to tap. If there are multiple elements satisfying the selector, the first will be tapped.
+- returns: <[Promise]>
+
+This method fetches an element with `selector`, scrolls it into view if needed, and then uses [page.touchscreen](#pagetouchscreen) to tap in the center of the element.
+If there's no element matching `selector`, the method throws an error.
+
+#### frame.title()
+- returns: <[Promise]<[string]>> The page's title.
+
+#### frame.type(selector, text[, options])
+- `selector` <[string]> A [selector] of an element to type into. If there are multiple elements satisfying the selector, the first will be used.
+- `text` <[string]> A text to type into a focused element.
+- `options` <[Object]>
+  - `delay` <[number]> Time to wait between key presses in milliseconds. Defaults to 0.
+- returns: <[Promise]>
+
+Sends a `keydown`, `keypress`/`input`, and `keyup` event for each character in the text.
+
+To press a special key, like `Control` or `ArrowDown`, use [`keyboard.press`](#keyboardpresskey-options).
+
+```js
+frame.type('#mytextarea', 'Hello'); // Types instantly
+frame.type('#mytextarea', 'World', {delay: 100}); // Types slower, like a user
+```
+
+#### frame.url()
+- returns: <[string]>
+
+Returns frame's url.
+
+#### frame.waitFor(selectorOrFunctionOrTimeout[, options[, ...args]])
+- `selectorOrFunctionOrTimeout` <[string]|[number]|[function]> A [selector], predicate or timeout to wait for
+- `options` <[Object]> Optional waiting parameters
+- `...args` <...[Serializable]|[JSHandle]> Arguments to pass to  `pageFunction`
+- returns: <[Promise]<[JSHandle]>> Promise which resolves to a JSHandle of the success value
+
+This method behaves differently with respect to the type of the first parameter:
+- if `selectorOrFunctionOrTimeout` is a `string`, then the first argument is treated as a [selector] or [xpath], depending on whether or not it starts with '//', and the method is a shortcut for
+  [frame.waitForSelector](#framewaitforselectorselector-options) or [frame.waitForXPath](#framewaitforxpathxpath-options)
+- if `selectorOrFunctionOrTimeout` is a `function`, then the first argument is treated as a predicate to wait for and the method is a shortcut for [frame.waitForFunction()](#framewaitforfunctionpagefunction-options-args).
+- if `selectorOrFunctionOrTimeout` is a `number`, then the first argument is treated as a timeout in milliseconds and the method returns a promise which resolves after the timeout
+- otherwise, an exception is thrown
+
+```js
+// wait for selector
+await page.waitFor('.foo');
+// wait for 1 second
+await page.waitFor(1000);
+// wait for predicate
+await page.waitFor(() => !!document.querySelector('.foo'));
+```
+
+To pass arguments from node.js to the predicate of `page.waitFor` function:
+
+```js
+const selector = '.foo';
+await page.waitFor(selector => !!document.querySelector(selector), {}, selector);
+```
+
+#### frame.waitForFunction(pageFunction[, options[, ...args]])
+- `pageFunction` <[function]|[string]> Function to be evaluated in browser context
+- `options` <[Object]> Optional waiting parameters
+  - `polling` <[string]|[number]> An interval at which the `pageFunction` is executed, defaults to `raf`. If `polling` is a number, then it is treated as an interval in milliseconds at which the function would be executed. If `polling` is a string, then it can be one of the following values:
+    - `raf` - to constantly execute `pageFunction` in `requestAnimationFrame` callback. This is the tightest polling mode which is suitable to observe styling changes.
+    - `mutation` - to execute `pageFunction` on every DOM mutation.
+  - `timeout` <[number]> maximum time to wait for in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can be changed by using the [page.setDefaultTimeout(timeout)](#pagesetdefaulttimeouttimeout) method.
+- `...args` <...[Serializable]|[JSHandle]> Arguments to pass to  `pageFunction`
+- returns: <[Promise]<[JSHandle]>> Promise which resolves when the `pageFunction` returns a truthy value. It resolves to a JSHandle of the truthy value.
+
+The `waitForFunction` can be used to observe viewport size change:
+```js
+const puppeteer = require('puppeteer');
+
+puppeteer.launch().then(async browser => {
+  const page = await browser.newPage();
+  const watchDog = page.mainFrame().waitForFunction('window.innerWidth < 100');
+  page.setViewport({width: 50, height: 50});
+  await watchDog;
+  await browser.close();
+});
+```
+
+To pass arguments from node.js to the predicate of `page.waitForFunction` function:
+
+```js
+const selector = '.foo';
+await page.waitForFunction(selector => !!document.querySelector(selector), {}, selector);
+```
+
+#### frame.waitForNavigation([options])
+- `options` <[Object]> Navigation parameters which might have the following properties:
+  - `timeout` <[number]> Maximum navigation time in milliseconds, defaults to 30 seconds, pass `0` to disable timeout. The default value can be changed by using the [page.setDefaultNavigationTimeout(timeout)](#pagesetdefaultnavigationtimeouttimeout) or [page.setDefaultTimeout(timeout)](#pagesetdefaulttimeouttimeout) methods.
+  - `waitUntil` <[string]|[Array]<[string]>> When to consider navigation succeeded, defaults to `load`. Given an array of event strings, navigation is considered to be successful after all events have been fired. Events can be either:
+    - `load` - consider navigation to be finished when the `load` event is fired.
+    - `domcontentloaded` - consider navigation to be finished when the `DOMContentLoaded` event is fired.
+    - `networkidle0` - consider navigation to be finished when there are no more than 0 network connections for at least `500` ms.
+    - `networkidle2` - consider navigation to be finished when there are no more than 2 network connections for at least `500` ms.
+- returns: <[Promise]<?[Response]>> Promise which resolves to the main resource response. In case of multiple redirects, the navigation will resolve with the response of the last redirect. In case of navigation to a different anchor or navigation due to History API usage, the navigation will resolve with `null`.
+
+This resolves when the frame navigates to a new URL. It is useful for when you run code
+which will indirectly cause the frame to navigate. Consider this example:
+
+```js
+const [response] = await Promise.all([
+  frame.waitForNavigation(), // The navigation promise resolves after navigation has finished
+  frame.click('a.my-link'), // Clicking the link will indirectly cause a navigation
+]);
+```
+
+**NOTE** Usage of the [History API](https://developer.mozilla.org/en-US/docs/Web/API/History_API) to change the URL is considered a navigation.
+
+
+#### frame.waitForSelector(selector[, options])
+- `selector` <[string]> A [selector] of an element to wait for
+- `options` <[Object]> Optional waiting parameters
+  - `visible` <[boolean]> wait for element to be present in DOM and to be visible, i.e. to not have `display: none` or `visibility: hidden` CSS properties. Defaults to `false`.
+  - `hidden` <[boolean]> wait for element to not be found in the DOM or to be hidden, i.e. have `display: none` or `visibility: hidden` CSS properties. Defaults to `false`.
+  - `timeout` <[number]> maximum time to wait for in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can be changed by using the [page.setDefaultTimeout(timeout)](#pagesetdefaulttimeouttimeout) method.
+- returns: <[Promise]<?[ElementHandle]>> Promise which resolves when element specified by selector string is added to DOM. Resolves to `null` if waiting for `hidden: true` and selector is not found in DOM.
+
+Wait for the `selector` to appear in page. If at the moment of calling
+the method the `selector` already exists, the method will return
+immediately. If the selector doesn't appear after the `timeout` milliseconds of waiting, the function will throw.
+
+This method works across navigations:
+```js
+const puppeteer = require('puppeteer');
+
+puppeteer.launch().then(async browser => {
+  const page = await browser.newPage();
+  let currentURL;
+  page.mainFrame()
+    .waitForSelector('img')
+    .then(() => console.log('First URL with image: ' + currentURL));
+  for (currentURL of ['https://example.com', 'https://google.com', 'https://bbc.com'])
+    await page.goto(currentURL);
+  await browser.close();
+});
+```
+
+#### frame.waitForXPath(xpath[, options])
+- `xpath` <[string]> A [xpath] of an element to wait for
+- `options` <[Object]> Optional waiting parameters
+  - `visible` <[boolean]> wait for element to be present in DOM and to be visible, i.e. to not have `display: none` or `visibility: hidden` CSS properties. Defaults to `false`.
+  - `hidden` <[boolean]> wait for element to not be found in the DOM or to be hidden, i.e. have `display: none` or `visibility: hidden` CSS properties. Defaults to `false`.
+  - `timeout` <[number]> maximum time to wait for in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can be changed by using the [page.setDefaultTimeout(timeout)](#pagesetdefaulttimeouttimeout) method.
+- returns: <[Promise]<?[ElementHandle]>> Promise which resolves when element specified by xpath string is added to DOM. Resolves to `null` if waiting for `hidden: true` and xpath is not found in DOM.
+
+Wait for the `xpath` to appear in page. If at the moment of calling
+the method the `xpath` already exists, the method will return
+immediately. If the xpath doesn't appear after the `timeout` milliseconds of waiting, the function will throw.
+
+This method works across navigations:
+```js
+const puppeteer = require('puppeteer');
+
+puppeteer.launch().then(async browser => {
+  const page = await browser.newPage();
+  let currentURL;
+  page.mainFrame()
+    .waitForXPath('//img')
+    .then(() => console.log('First URL with image: ' + currentURL));
+  for (currentURL of ['https://example.com', 'https://google.com', 'https://bbc.com'])
+    await page.goto(currentURL);
+  await browser.close();
+});
+```
+
+### class: ExecutionContext
+
+The class represents a context for JavaScript execution. A [Page] might have many execution contexts:
+- each [frame](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe) has "default" execution context that is
+  always created after frame is attached to DOM. This context is returned by the [`frame.executionContext()`](#frameexecutioncontext) method.
+- [Extensions](https://developer.chrome.com/extensions)'s content scripts create additional execution contexts.
+
+Besides pages, execution contexts can be found in [workers](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API).
+
+#### executionContext.evaluate(pageFunction[, ...args])
+- `pageFunction` <[function]|[string]> Function to be evaluated in `executionContext`
+- `...args` <...[Serializable]|[JSHandle]> Arguments to pass to `pageFunction`
+- returns: <[Promise]<[Serializable]>> Promise which resolves to the return value of `pageFunction`
+
+If the function passed to the `executionContext.evaluate` returns a [Promise], then `executionContext.evaluate` would wait for the promise to resolve and return its value.
+
+If the function passed to the `executionContext.evaluate` returns a non-[Serializable] value, then `executionContext.evaluate` resolves to `undefined`. DevTools Protocol also supports transferring some additional values that are not serializable by `JSON`: `-0`, `NaN`, `Infinity`, `-Infinity`, and bigint literals.
+
+```js
+const executionContext = await page.mainFrame().executionContext();
+const result = await executionContext.evaluate(() => Promise.resolve(8 * 7));
+console.log(result); // prints "56"
+```
+
+A string can also be passed in instead of a function.
+
+```js
+console.log(await executionContext.evaluate('1 + 2')); // prints "3"
+```
+
+[JSHandle] instances can be passed as arguments to the `executionContext.evaluate`:
+```js
+const oneHandle = await executionContext.evaluateHandle(() => 1);
+const twoHandle = await executionContext.evaluateHandle(() => 2);
+const result = await executionContext.evaluate((a, b) => a + b, oneHandle, twoHandle);
+await oneHandle.dispose();
+await twoHandle.dispose();
+console.log(result); // prints '3'.
+```
+
+#### executionContext.evaluateHandle(pageFunction[, ...args])
+- `pageFunction` <[function]|[string]> Function to be evaluated in the `executionContext`
+- `...args` <...[Serializable]|[JSHandle]> Arguments to pass to `pageFunction`
+- returns: <[Promise]<[JSHandle]>> Promise which resolves to the return value of `pageFunction` as in-page object (JSHandle)
+
+The only difference between `executionContext.evaluate` and `executionContext.evaluateHandle` is that `executionContext.evaluateHandle` returns in-page object (JSHandle).
+
+If the function passed to the `executionContext.evaluateHandle` returns a [Promise], then `executionContext.evaluateHandle` would wait for the promise to resolve and return its value.
+
+```js
+const context = await page.mainFrame().executionContext();
+const aHandle = await context.evaluateHandle(() => Promise.resolve(self));
+aHandle; // Handle for the global object.
+```
+
+A string can also be passed in instead of a function.
+
+```js
+const aHandle = await context.evaluateHandle('1 + 2'); // Handle for the '3' object.
+```
+
+[JSHandle] instances can be passed as arguments to the `executionContext.evaluateHandle`:
+```js
+const aHandle = await context.evaluateHandle(() => document.body);
+const resultHandle = await context.evaluateHandle(body => body.innerHTML, aHandle);
+console.log(await resultHandle.jsonValue()); // prints body's innerHTML
+await aHandle.dispose();
+await resultHandle.dispose();
+```
+
+#### executionContext.frame()
+- returns: <?[Frame]> Frame associated with this execution context.
+
+> **NOTE** Not every execution context is associated with a frame. For example, workers and extensions have execution contexts that are not associated with frames.
+
+
+#### executionContext.queryObjects(prototypeHandle)
+- `prototypeHandle` <[JSHandle]> A handle to the object prototype.
+- returns: <[Promise]<[JSHandle]>> A handle to an array of objects with this prototype
+
+The method iterates the JavaScript heap and finds all the objects with the given prototype.
+
+```js
+// Create a Map object
+await page.evaluate(() => window.map = new Map());
+// Get a handle to the Map object prototype
+const mapPrototype = await page.evaluateHandle(() => Map.prototype);
+// Query all map instances into an array
+const mapInstances = await page.queryObjects(mapPrototype);
+// Count amount of map objects in heap
+const count = await page.evaluate(maps => maps.length, mapInstances);
+await mapInstances.dispose();
+await mapPrototype.dispose();
+```
+
+### class: JSHandle
+
+JSHandle represents an in-page JavaScript object. JSHandles can be created with the [page.evaluateHandle](#pageevaluatehandlepagefunction-args) method.
+
+```js
+const windowHandle = await page.evaluateHandle(() => window);
+// ...
+```
+
+JSHandle prevents the referenced JavaScript object being garbage collected unless the handle is [disposed](#jshandledispose). JSHandles are auto-disposed when their origin frame gets navigated or the parent context gets destroyed.
+
+JSHandle instances can be used as arguments in [`page.$eval()`](#pageevalselector-pagefunction-args), [`page.evaluate()`](#pageevaluatepagefunction-args) and [`page.evaluateHandle`](#pageevaluatehandlepagefunction-args) methods.
+
+#### jsHandle.asElement()
+- returns: <?[ElementHandle]>
+
+Returns either `null` or the object handle itself, if the object handle is an instance of [ElementHandle].
+
+#### jsHandle.dispose()
+- returns: <[Promise]> Promise which resolves when the object handle is successfully disposed.
+
+The `jsHandle.dispose` method stops referencing the element handle.
+
+#### jsHandle.executionContext()
+- returns: <[ExecutionContext]>
+
+Returns execution context the handle belongs to.
+
+#### jsHandle.getProperties()
+- returns: <[Promise]<[Map]<[string], [JSHandle]>>>
+
+The method returns a map with property names as keys and JSHandle instances for the property values.
+
+```js
+const handle = await page.evaluateHandle(() => ({window, document}));
+const properties = await handle.getProperties();
+const windowHandle = properties.get('window');
+const documentHandle = properties.get('document');
+await handle.dispose();
+```
+
+#### jsHandle.getProperty(propertyName)
+- `propertyName` <[string]> property to get
+- returns: <[Promise]<[JSHandle]>>
+
+Fetches a single property from the referenced object.
+
+#### jsHandle.jsonValue()
+- returns: <[Promise]<[Object]>>
+
+Returns a JSON representation of the object. If the object has a
+[`toJSON`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#toJSON()_behavior)
+function, it **will not be called**.
+
+> **NOTE** The method will return an empty JSON object if the referenced object is not stringifiable. It will throw an error if the object has circular references.
+
+### class: ElementHandle
+* extends: [JSHandle]
+
+ElementHandle represents an in-page DOM element. ElementHandles can be created with the [page.$](#pageselector) method.
+
+```js
+const puppeteer = require('puppeteer');
+
+puppeteer.launch().then(async browser => {
+  const page = await browser.newPage();
+  await page.goto('https://example.com');
+  const hrefElement = await page.$('a');
+  await hrefElement.click();
+  // ...
+});
+```
+
+ElementHandle prevents DOM element from garbage collection unless the handle is [disposed](#elementhandledispose). ElementHandles are auto-disposed when their origin frame gets navigated.
+
+ElementHandle instances can be used as arguments in [`page.$eval()`](#pageevalselector-pagefunction-args) and [`page.evaluate()`](#pageevaluatepagefunction-args) methods.
+
+#### elementHandle.$(selector)
+- `selector` <[string]> A [selector] to query element for
+- returns: <[Promise]<?[ElementHandle]>>
+
+The method runs `element.querySelector` within the page. If no element matches the selector, the return value resolves to `null`.
+
+#### elementHandle.$$(selector)
+- `selector` <[string]> A [selector] to query element for
+- returns: <[Promise]<[Array]<[ElementHandle]>>>
+
+The method runs `element.querySelectorAll` within the page. If no elements match the selector, the return value resolves to `[]`.
+
+#### elementHandle.$$eval(selector, pageFunction[, ...args])
+- `selector` <[string]> A [selector] to query page for
+- `pageFunction` <[function]\([Array]<[Element]>\)> Function to be evaluated in browser context
+- `...args` <...[Serializable]|[JSHandle]> Arguments to pass to `pageFunction`
+- returns: <[Promise]<[Serializable]>> Promise which resolves to the return value of `pageFunction`
+
+This method runs `document.querySelectorAll` within the element and passes it as the first argument to `pageFunction`. If there's no element matching `selector`, the method throws an error.
+
+If `pageFunction` returns a [Promise], then `frame.$$eval` would wait for the promise to resolve and return its value.
+
+Examples:
+```html
+<div class="feed">
+  <div class="tweet">Hello!</div>
+  <div class="tweet">Hi!</div>
+</div>
+```
+```js
+const feedHandle = await page.$('.feed');
+expect(await feedHandle.$$eval('.tweet', nodes => nodes.map(n => n.innerText))).toEqual(['Hello!', 'Hi!']);
+```
+
+#### elementHandle.$eval(selector, pageFunction[, ...args])
+- `selector` <[string]> A [selector] to query page for
+- `pageFunction` <[function]\([Element]\)> Function to be evaluated in browser context
+- `...args` <...[Serializable]|[JSHandle]> Arguments to pass to `pageFunction`
+- returns: <[Promise]<[Serializable]>> Promise which resolves to the return value of `pageFunction`
+
+This method runs `document.querySelector` within the element and passes it as the first argument to `pageFunction`. If there's no element matching `selector`, the method throws an error.
+
+If `pageFunction` returns a [Promise], then `frame.$eval` would wait for the promise to resolve and return its value.
+
+Examples:
+```js
+const tweetHandle = await page.$('.tweet');
+expect(await tweetHandle.$eval('.like', node => node.innerText)).toBe('100');
+expect(await tweetHandle.$eval('.retweets', node => node.innerText)).toBe('10');
+```
+
+#### elementHandle.$x(expression)
+- `expression` <[string]> Expression to [evaluate](https://developer.mozilla.org/en-US/docs/Web/API/Document/evaluate).
+- returns: <[Promise]<[Array]<[ElementHandle]>>>
+
+The method evaluates the XPath expression relative to the elementHandle. If there are no such elements, the method will resolve to an empty array.
+
+#### elementHandle.asElement()
+- returns: <[ElementHandle]>
+
+#### elementHandle.boundingBox()
+- returns: <[Promise]<?[Object]>>
+  - x <[number]> the x coordinate of the element in pixels.
+  - y <[number]> the y coordinate of the element in pixels.
+  - width <[number]> the width of the element in pixels.
+  - height <[number]> the height of the element in pixels.
+
+This method returns the bounding box of the element (relative to the main frame), or `null` if the element is not visible.
+
+#### elementHandle.boxModel()
+- returns: <[Promise]<?[Object]>>
+  - content <[Array]<[Object]>> Content box.
+    - x <[number]>
+    - y <[number]>
+  - padding <[Array]<[Object]>> Padding box.
+    - x <[number]>
+    - y <[number]>
+  - border <[Array]<[Object]>> Border box.
+    - x <[number]>
+    - y <[number]>
+  - margin <[Array]<[Object]>> Margin box.
+    - x <[number]>
+    - y <[number]>
+  - width <[number]> Element's width.
+  - height <[number]> Element's height.
+
+This method returns boxes of the element, or `null` if the element is not visible. Boxes are represented as an array of points; each Point is an object `{x, y}`. Box points are sorted clock-wise.
+
+#### elementHandle.click([options])
+- `options` <[Object]>
+  - `button` <"left"|"right"|"middle"> Defaults to `left`.
+  - `clickCount` <[number]> defaults to 1. See [UIEvent.detail].
+  - `delay` <[number]> Time to wait between `mousedown` and `mouseup` in milliseconds. Defaults to 0.
+- returns: <[Promise]> Promise which resolves when the element is successfully clicked. Promise gets rejected if the element is detached from DOM.
+
+This method scrolls element into view if needed, and then uses [page.mouse](#pagemouse) to click in the center of the element.
+If the element is detached from DOM, the method throws an error.
+
+#### elementHandle.contentFrame()
+- returns: <[Promise]<?[Frame]>> Resolves to the content frame for element handles referencing iframe nodes, or null otherwise
+
+#### elementHandle.dispose()
+- returns: <[Promise]> Promise which resolves when the element handle is successfully disposed.
+
+The `elementHandle.dispose` method stops referencing the element handle.
+
+#### elementHandle.executionContext()
+- returns: <[ExecutionContext]>
+
+#### elementHandle.focus()
+- returns: <[Promise]>
+
+Calls [focus](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/focus) on the element.
+
+#### elementHandle.getProperties()
+- returns: <[Promise]<[Map]<[string], [JSHandle]>>>
+
+The method returns a map with property names as keys and JSHandle instances for the property values.
+
+```js
+const listHandle = await page.evaluateHandle(() => document.body.children);
+const properties = await listHandle.getProperties();
+const children = [];
+for (const property of properties.values()) {
+  const element = property.asElement();
+  if (element)
+    children.push(element);
+}
+children; // holds elementHandles to all children of document.body
+```
+
+#### elementHandle.getProperty(propertyName)
+- `propertyName` <[string]> property to get
+- returns: <[Promise]<[JSHandle]>>
+
+Fetches a single property from the objectHandle.
+
+#### elementHandle.hover()
+- returns: <[Promise]> Promise which resolves when the element is successfully hovered.
+
+This method scrolls element into view if needed, and then uses [page.mouse](#pagemouse) to hover over the center of the element.
+If the element is detached from DOM, the method throws an error.
+
+#### elementHandle.isIntersectingViewport()
+- returns: <[Promise]<[boolean]>> Resolves to true if the element is visible in the current viewport.
+
+#### elementHandle.jsonValue()
+- returns: <[Promise]<[Object]>>
+
+Returns a JSON representation of the object. The JSON is generated by running [`JSON.stringify`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify) on the object in page and consequent [`JSON.parse`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse) in puppeteer.
+
+> **NOTE** The method will throw if the referenced object is not stringifiable.
+
+#### elementHandle.press(key[, options])
+- `key` <[string]> Name of key to press, such as `ArrowLeft`. See [USKeyboardLayout] for a list of all key names.
+- `options` <[Object]>
+  - `text` <[string]> If specified, generates an input event with this text.
+  - `delay` <[number]> Time to wait between `keydown` and `keyup` in milliseconds. Defaults to 0.
+- returns: <[Promise]>
+
+Focuses the element, and then uses [`keyboard.down`](#keyboarddownkey-options) and [`keyboard.up`](#keyboardupkey).
+
+If `key` is a single character and no modifier keys besides `Shift` are being held down, a `keypress`/`input` event will also be generated. The `text` option can be specified to force an input event to be generated.
+
+> **NOTE** Modifier keys DO effect `elementHandle.press`. Holding down `Shift` will type the text in upper case.
+
+#### elementHandle.screenshot([options])
+- `options` <[Object]> Same options as in [page.screenshot](#pagescreenshotoptions).
+- returns: <[Promise]<[string]|[Buffer]>> Promise which resolves to buffer or a base64 string (depending on the value of `options.encoding`) with captured screenshot.
+
+This method scrolls element into view if needed, and then uses [page.screenshot](#pagescreenshotoptions) to take a screenshot of the element.
+If the element is detached from DOM, the method throws an error.
+
+#### elementHandle.tap()
+- returns: <[Promise]> Promise which resolves when the element is successfully tapped. Promise gets rejected if the element is detached from DOM.
+
+This method scrolls element into view if needed, and then uses [touchscreen.tap](#touchscreentapx-y) to tap in the center of the element.
+If the element is detached from DOM, the method throws an error.
+
+#### elementHandle.toString()
+- returns: <[string]>
+
+#### elementHandle.type(text[, options])
+- `text` <[string]> A text to type into a focused element.
+- `options` <[Object]>
+  - `delay` <[number]> Time to wait between key presses in milliseconds. Defaults to 0.
+- returns: <[Promise]>
+
+Focuses the element, and then sends a `keydown`, `keypress`/`input`, and `keyup` event for each character in the text.
+
+To press a special key, like `Control` or `ArrowDown`, use [`elementHandle.press`](#elementhandlepresskey-options).
+
+```js
+elementHandle.type('Hello'); // Types instantly
+elementHandle.type('World', {delay: 100}); // Types slower, like a user
+```
+
+An example of typing into a text field and then submitting the form:
+```js
+const elementHandle = await page.$('input');
+await elementHandle.type('some text');
+await elementHandle.press('Enter');
+```
+
+#### elementHandle.uploadFile(...filePaths)
+- `...filePaths` <...[string]> Sets the value of the file input these paths. If some of the  `filePaths` are relative paths, then they are resolved relative to [current working directory](https://nodejs.org/api/process.html#process_process_cwd).
+- returns: <[Promise]>
+
+This method expects `elementHandle` to point to an [input element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input).
+
+### class: Request
+
+Whenever the page sends a request, such as for a network resource, the following events are emitted by puppeteer's page:
+- ['request'](#event-request) emitted when the request is issued by the page.
+- ['response'](#event-response) emitted when/if the response is received for the request.
+- ['requestfinished'](#event-requestfinished) emitted when the response body is downloaded and the request is complete.
+
+If request fails at some point, then instead of 'requestfinished' event (and possibly instead of 'response' event), the  ['requestfailed'](#event-requestfailed) event is emitted.
+
+If request gets a 'redirect' response, the request is successfully finished with the 'requestfinished' event, and a new request is  issued to a redirected url.
+
+#### request.abort([errorCode])
+- `errorCode` <[string]> Optional error code. Defaults to `failed`, could be
+  one of the following:
+  - `aborted` - An operation was aborted (due to user action)
+  - `accessdenied` - Permission to access a resource, other than the network, was denied
+  - `addressunreachable` - The IP address is unreachable. This usually means
+    that there is no route to the specified host or network.
+  - `blockedbyclient` - The client chose to block the request.
+  - `blockedbyresponse` - The request failed because the response was delivered along with requirements which are not met ('X-Frame-Options' and 'Content-Security-Policy' ancestor checks, for instance).
+  - `connectionaborted` - A connection timed out as a result of not receiving an ACK for data sent.
+  - `connectionclosed` - A connection was closed (corresponding to a TCP FIN).
+  - `connectionfailed` - A connection attempt failed.
+  - `connectionrefused` - A connection attempt was refused.
+  - `connectionreset` - A connection was reset (corresponding to a TCP RST).
+  - `internetdisconnected` - The Internet connection has been lost.
+  - `namenotresolved` - The host name could not be resolved.
+  - `timedout` - An operation timed out.
+  - `failed` - A generic failure occurred.
+- returns: <[Promise]>
+
+Aborts request. To use this, request interception should be enabled with `page.setRequestInterception`.
+Exception is immediately thrown if the request interception is not enabled.
+
+#### request.continue([overrides])
+- `overrides` <[Object]> Optional request overwrites, which can be one of the following:
+  - `url` <[string]> If set, the request url will be changed. This is not a redirect. The request will be silently forwarded to the new url. For example, the address bar will show the original url.
+  - `method` <[string]> If set changes the request method (e.g. `GET` or `POST`)
+  - `postData` <[string]> If set changes the post data of request
+  - `headers` <[Object]> If set changes the request HTTP headers. Header values will be converted to a string.
+- returns: <[Promise]>
+
+Continues request with optional request overrides. To use this, request interception should be enabled with `page.setRequestInterception`.
+Exception is immediately thrown if the request interception is not enabled.
+
+```js
+await page.setRequestInterception(true);
+page.on('request', request => {
+  // Override headers
+  const headers = Object.assign({}, request.headers(), {
+    foo: 'bar', // set "foo" header
+    origin: undefined, // remove "origin" header
+  });
+  request.continue({headers});
+});
+```
+
+#### request.failure()
+- returns: <?[Object]> Object describing request failure, if any
+  - `errorText` <[string]> Human-readable error message, e.g. `'net::ERR_FAILED'`.
+
+The method returns `null` unless this request was failed, as reported by
+`requestfailed` event.
+
+Example of logging all failed requests:
+
+```js
+page.on('requestfailed', request => {
+  console.log(request.url() + ' ' + request.failure().errorText);
+});
+```
+
+#### request.frame()
+- returns: <?[Frame]> A [Frame] that initiated this request, or `null` if navigating to error pages.
+
+#### request.headers()
+- returns: <[Object]> An object with HTTP headers associated with the request. All header names are lower-case.
+
+#### request.isNavigationRequest()
+- returns: <[boolean]>
+
+Whether this request is driving frame's navigation.
+
+#### request.method()
+- returns: <[string]> Request's method (GET, POST, etc.)
+
+#### request.postData()
+- returns: <[string]> Request's post body, if any.
+
+#### request.redirectChain()
+- returns: <[Array]<[Request]>>
+
+A `redirectChain` is a chain of requests initiated to fetch a resource.
+- If there are no redirects and the request was successful, the chain will be empty.
+- If a server responds with at least a single redirect, then the chain will
+contain all the requests that were redirected.
+
+`redirectChain` is shared between all the requests of the same chain.
+
+For example, if the website `http://example.com` has a single redirect to
+`https://example.com`, then the chain will contain one request:
+
+```js
+const response = await page.goto('http://example.com');
+const chain = response.request().redirectChain();
+console.log(chain.length); // 1
+console.log(chain[0].url()); // 'http://example.com'
+```
+
+If the website `https://google.com` has no redirects, then the chain will be empty:
+```js
+const response = await page.goto('https://google.com');
+const chain = response.request().redirectChain();
+console.log(chain.length); // 0
+```
+
+#### request.resourceType()
+- returns: <[string]>
+
+Contains the request's resource type as it was perceived by the rendering engine.
+ResourceType will be one of the following: `document`, `stylesheet`, `image`, `media`, `font`, `script`, `texttrack`, `xhr`, `fetch`, `eventsource`, `websocket`, `manifest`, `other`.
+
+#### request.respond(response)
+- `response` <[Object]> Response that will fulfill this request
+  - `status` <[number]> Response status code, defaults to `200`.
+  - `headers` <[Object]> Optional response headers. Header values will be converted to a string.
+  - `contentType` <[string]> If set, equals to setting `Content-Type` response header
+  - `body` <[string]|[Buffer]> Optional response body
+- returns: <[Promise]>
+
+Fulfills request with given response. To use this, request interception should
+be enabled with `page.setRequestInterception`. Exception is thrown if
+request interception is not enabled.
+
+An example of fulfilling all requests with 404 responses:
+
+```js
+await page.setRequestInterception(true);
+page.on('request', request => {
+  request.respond({
+    status: 404,
+    contentType: 'text/plain',
+    body: 'Not Found!'
+  });
+});
+```
+
+> **NOTE** Mocking responses for dataURL requests is not supported.
+> Calling `request.respond` for a dataURL request is a noop.
+
+#### request.response()
+- returns: <?[Response]> A matching [Response] object, or `null` if the response has not been received yet.
+
+#### request.url()
+- returns: <[string]> URL of the request.
+
+### class: Response
+
+[Response] class represents responses which are received by page.
+
+#### response.buffer()
+- returns: <Promise<[Buffer]>> Promise which resolves to a buffer with response body.
+
+#### response.frame()
+- returns: <?[Frame]> A [Frame] that initiated this response, or `null` if navigating to error pages.
+
+#### response.fromCache()
+- returns: <[boolean]>
+
+True if the response was served from either the browser's disk cache or memory cache.
+
+#### response.fromServiceWorker()
+- returns: <[boolean]>
+
+True if the response was served by a service worker.
+
+#### response.headers()
+- returns: <[Object]> An object with HTTP headers associated with the response. All header names are lower-case.
+
+#### response.json()
+- returns: <Promise<[Object]>> Promise which resolves to a JSON representation of response body.
+
+This method will throw if the response body is not parsable via `JSON.parse`.
+
+#### response.ok()
+- returns: <[boolean]>
+
+Contains a boolean stating whether the response was successful (status in the range 200-299) or not.
+
+#### response.remoteAddress()
+- returns: <[Object]>
+  - `ip` <[string]> the IP address of the remote server
+  - `port` <[number]> the port used to connect to the remote server
+
+#### response.request()
+- returns: <[Request]> A matching [Request] object.
+
+#### response.securityDetails()
+- returns: <?[SecurityDetails]> Security details if the response was received over the secure connection, or `null` otherwise.
+
+#### response.status()
+- returns: <[number]>
+
+Contains the status code of the response (e.g., 200 for a success).
+
+#### response.statusText()
+- returns: <[string]>
+
+Contains the status text of the response (e.g. usually an "OK" for a success).
+
+#### response.text()
+- returns: <[Promise]<[string]>> Promise which resolves to a text representation of response body.
+
+#### response.url()
+- returns: <[string]>
+
+Contains the URL of the response.
+
+### class: SecurityDetails
+
+[SecurityDetails] class represents the security details when response was received over the secure connection.
+
+#### securityDetails.issuer()
+- returns: <[string]> A string with the name of issuer of the certificate.
+
+#### securityDetails.protocol()
+- returns: <[string]> String with the security protocol, eg. "TLS 1.2".
+
+#### securityDetails.subjectName()
+- returns: <[string]> Name of the subject to which the certificate was issued to.
+
+#### securityDetails.validFrom()
+- returns: <[number]> [UnixTime] stating the start of validity of the certificate.
+
+#### securityDetails.validTo()
+- returns: <[number]> [UnixTime] stating the end of validity of the certificate.
+
+### class: Target
+
+#### target.browser()
+
+- returns: <[Browser]>
+
+Get the browser the target belongs to.
+
+#### target.browserContext()
+
+- returns: <[BrowserContext]>
+
+The browser context the target belongs to.
+
+#### target.createCDPSession()
+- returns: <[Promise]<[CDPSession]>>
+
+Creates a Chrome Devtools Protocol session attached to the target.
+
+#### target.opener()
+- returns: <?[Target]>
+
+Get the target that opened this target. Top-level targets return `null`.
+
+#### target.page()
+- returns: <[Promise]<?[Page]>>
+
+If the target is not of type `"page"` or `"background_page"`, returns `null`.
+
+#### target.type()
+- returns: <"page"|"background_page"|"service_worker"|"shared_worker"|"other"|"browser">
+
+Identifies what kind of target this is. Can be `"page"`, [`"background_page"`](https://developer.chrome.com/extensions/background_pages), `"service_worker"`, `"shared_worker"`, `"browser"` or `"other"`.
+
+#### target.url()
+- returns: <[string]>
+
+#### target.worker()
+- returns: <[Promise]<?[Worker]>>
+
+If the target is not of type `"service_worker"` or `"shared_worker"`, returns `null`.
+
+### class: CDPSession
+
+* extends: [EventEmitter](https://nodejs.org/api/events.html#events_class_eventemitter)
+
+The `CDPSession` instances are used to talk raw Chrome Devtools Protocol:
+- protocol methods can be called with `session.send` method.
+- protocol events can be subscribed to with `session.on` method.
+
+Useful links:
+- Documentation on DevTools Protocol can be found here: [DevTools Protocol Viewer](https://chromedevtools.github.io/devtools-protocol/).
+- Getting Started with DevTools Protocol: https://github.com/aslushnikov/getting-started-with-cdp/blob/master/README.md
+
+```js
+const client = await page.target().createCDPSession();
+await client.send('Animation.enable');
+client.on('Animation.animationCreated', () => console.log('Animation created!'));
+const response = await client.send('Animation.getPlaybackRate');
+console.log('playback rate is ' + response.playbackRate);
+await client.send('Animation.setPlaybackRate', {
+  playbackRate: response.playbackRate / 2
+});
+```
+
+#### cdpSession.detach()
+- returns: <[Promise]>
+
+Detaches the cdpSession from the target. Once detached, the cdpSession object won't emit any events and can't be used
+to send messages.
+
+#### cdpSession.send(method[, params])
+- `method` <[string]> protocol method name
+- `params` <[Object]> Optional method parameters
+- returns: <[Promise]<[Object]>>
+
+### class: Coverage
+
+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:
+
+```js
+// Enable both JavaScript and CSS coverage
+await Promise.all([
+  page.coverage.startJSCoverage(),
+  page.coverage.startCSSCoverage()
+]);
+// Navigate to page
+await page.goto('https://example.com');
+// Disable both JavaScript and CSS coverage
+const [jsCoverage, cssCoverage] = await Promise.all([
+  page.coverage.stopJSCoverage(),
+  page.coverage.stopCSSCoverage(),
+]);
+let totalBytes = 0;
+let usedBytes = 0;
+const coverage = [...jsCoverage, ...cssCoverage];
+for (const entry of coverage) {
+  totalBytes += entry.text.length;
+  for (const range of entry.ranges)
+    usedBytes += range.end - range.start - 1;
+}
+console.log(`Bytes used: ${usedBytes / totalBytes * 100}%`);
+```
+
+_To output coverage in a form consumable by [Istanbul](https://github.com/istanbuljs),
+  see [puppeteer-to-istanbul](https://github.com/istanbuljs/puppeteer-to-istanbul)._
+
+#### coverage.startCSSCoverage([options])
+- `options` <[Object]>  Set of configurable options for coverage
+  - `resetOnNavigation` <[boolean]> Whether to reset coverage on every navigation. Defaults to `true`.
+- returns: <[Promise]> Promise that resolves when coverage is started
+
+#### coverage.startJSCoverage([options])
+- `options` <[Object]>  Set of configurable options for coverage
+  - `resetOnNavigation` <[boolean]> Whether to reset coverage on every navigation. Defaults to `true`.
+  - `reportAnonymousScripts` <[boolean]> Whether anonymous scripts generated by the page should be reported. Defaults to `false`.
+- returns: <[Promise]> Promise that resolves when coverage is started
+
+> **NOTE** Anonymous scripts are ones that don't have an associated url. These are scripts that are dynamically created on the page using `eval` or `new Function`. If `reportAnonymousScripts` is set to `true`, anonymous scripts will have `__puppeteer_evaluation_script__` as their URL.
+
+#### coverage.stopCSSCoverage()
+- returns: <[Promise]<[Array]<[Object]>>> Promise that resolves to the array of coverage reports for all stylesheets
+  - `url` <[string]> StyleSheet URL
+  - `text` <[string]> StyleSheet content
+  - `ranges` <[Array]<[Object]>> StyleSheet ranges that were used. Ranges are sorted and non-overlapping.
+    - `start` <[number]> A start offset in text, inclusive
+    - `end` <[number]> An end offset in text, exclusive
+
+> **NOTE** CSS Coverage doesn't include dynamically injected style tags without sourceURLs.
+
+#### coverage.stopJSCoverage()
+- returns: <[Promise]<[Array]<[Object]>>> Promise that resolves to the array of coverage reports for all scripts
+  - `url` <[string]> Script URL
+  - `text` <[string]> Script content
+  - `ranges` <[Array]<[Object]>> Script ranges that were executed. Ranges are sorted and non-overlapping.
+    - `start` <[number]> A start offset in text, inclusive
+    - `end` <[number]> An end offset in text, exclusive
+
+> **NOTE** JavaScript Coverage doesn't include anonymous scripts by default. However, scripts with sourceURLs are
+reported.
+
+### class: TimeoutError
+
+* extends: [Error]
+
+TimeoutError is emitted whenever certain operations are terminated due to timeout, e.g. [page.waitForSelector(selector[, options])](#pagewaitforselectorselector-options) or [puppeteer.launch([options])](#puppeteerlaunchoptions).
+
+
+
+[Array]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array "Array"
+[boolean]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#Boolean_type "Boolean"
+[Buffer]: https://nodejs.org/api/buffer.html#buffer_class_buffer "Buffer"
+[function]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function "Function"
+[number]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#Number_type "Number"
+[Object]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object "Object"
+[origin]: https://developer.mozilla.org/en-US/docs/Glossary/Origin "Origin"
+[Page]: #class-page "Page"
+[Promise]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise "Promise"
+[string]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#String_type "String"
+[stream.Readable]: https://nodejs.org/api/stream.html#stream_class_stream_readable "stream.Readable"
+[CDPSession]: #class-cdpsession  "CDPSession"
+[BrowserFetcher]: #class-browserfetcher  "BrowserFetcher"
+[BrowserContext]: #class-browsercontext  "BrowserContext"
+[Error]: https://nodejs.org/api/errors.html#errors_class_error "Error"
+[Frame]: #class-frame "Frame"
+[ConsoleMessage]: #class-consolemessage "ConsoleMessage"
+[ChildProcess]: https://nodejs.org/api/child_process.html "ChildProcess"
+[Coverage]: #class-coverage "Coverage"
+[iterator]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols "Iterator"
+[Response]: #class-response  "Response"
+[Request]: #class-request  "Request"
+[Browser]: #class-browser  "Browser"
+[TimeoutError]: #class-timeouterror "TimeoutError"
+[Body]: #class-body  "Body"
+[Element]: https://developer.mozilla.org/en-US/docs/Web/API/element "Element"
+[Keyboard]: #class-keyboard "Keyboard"
+[Dialog]: #class-dialog  "Dialog"
+[JSHandle]: #class-jshandle "JSHandle"
+[ExecutionContext]: #class-executioncontext "ExecutionContext"
+[Mouse]: #class-mouse "Mouse"
+[Map]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map "Map"
+[selector]: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors "selector"
+[Tracing]: #class-tracing "Tracing"
+[ElementHandle]: #class-elementhandle "ElementHandle"
+[UIEvent.detail]: https://developer.mozilla.org/en-US/docs/Web/API/UIEvent/detail "UIEvent.detail"
+[Serializable]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#Description "Serializable"
+[Touchscreen]: #class-touchscreen "Touchscreen"
+[Target]: #class-target "Target"
+[USKeyboardLayout]: ../lib/USKeyboardLayout.js "USKeyboardLayout"
+[xpath]: https://developer.mozilla.org/en-US/docs/Web/XPath "xpath"
+[UnixTime]: https://en.wikipedia.org/wiki/Unix_time "Unix Time"
+[SecurityDetails]: #class-securitydetails "SecurityDetails"
+[Worker]: #class-worker "Worker"
+[Accessibility]: #class-accessibility "Accessibility"
+[AXNode]: #accessibilitysnapshotoptions "AXNode"
+[ConnectionTransport]: ../lib/WebSocketTransport.js "ConnectionTransport"
new file mode 100644
--- /dev/null
+++ b/remote/test/puppeteer/docs/issue_template.md
@@ -0,0 +1,40 @@
+<!--
+STEP 1: Are you in the right place?
+
+- For general technical questions or "how to" guidance, please search StackOverflow for questions tagged "puppeteer" or create a new post.
+
+https://stackoverflow.com/questions/tagged/puppeteer
+
+- For issues or feature requests related to the DevTools Protocol (https://chromedevtools.github.io/devtools-protocol/), file an issue there:
+
+https://github.com/ChromeDevTools/devtools-protocol/issues/new.
+
+- Problem in Headless Chrome? File an issue against Chromium's issue tracker:
+
+https://bugs.chromium.org/p/chromium/issues/entry?components=Internals%3EHeadless&blocking=705916
+
+For issues, feature requests, or setup troubles with Puppeteer, file an issue right here!
+-->
+
+### Steps to reproduce
+
+**Tell us about your environment:**
+
+* Puppeteer version:
+* Platform / OS version:
+* URLs (if applicable):
+* Node.js version:
+
+**What steps will reproduce the problem?**
+
+_Please include code that reproduces the issue._
+
+1.
+2.
+3.
+
+**What is the expected result?**
+
+
+**What happens instead?**
+
new file mode 100644
--- /dev/null
+++ b/remote/test/puppeteer/docs/troubleshooting.md
@@ -0,0 +1,401 @@
+# Troubleshooting
+
+<!-- GEN:toc -->
+- [Chrome headless doesn't launch on Windows](#chrome-headless-doesnt-launch-on-windows)
+- [Chrome headless doesn't launch on UNIX](#chrome-headless-doesnt-launch-on-unix)
+- [Setting Up Chrome Linux Sandbox](#setting-up-chrome-linux-sandbox)
+  * [[recommended] Enable user namespace cloning](#recommended-enable-user-namespace-cloning)
+  * [[alternative] Setup setuid sandbox](#alternative-setup-setuid-sandbox)
+- [Running Puppeteer on Travis CI](#running-puppeteer-on-travis-ci)
+- [Running Puppeteer in Docker](#running-puppeteer-in-docker)
+  * [Running on Alpine](#running-on-alpine)
+    - [Tips](#tips)
+- [Running Puppeteer in the cloud](#running-puppeteer-in-the-cloud)
+  * [Running Puppeteer on Google App Engine](#running-puppeteer-on-google-app-engine)
+  * [Running Puppeteer on Google Cloud Functions](#running-puppeteer-on-google-cloud-functions)
+  * [Running Puppeteer on Heroku](#running-puppeteer-on-heroku)
+  * [Running Puppeteer on AWS Lambda](#running-puppeteer-on-aws-lambda)
+- [Code Transpilation Issues](#code-transpilation-issues)
+<!-- GEN:stop -->
+
+## Chrome headless doesn't launch on Windows
+
+Some [chrome policies](https://support.google.com/chrome/a/answer/7532015?hl=en) might enforce running Chrome/Chromium
+with certain extensions.
+
+Puppeteer passes `--disable-extensions` flag by default and will fail to launch when such policies are active.
+
+To work around this, try running without the flag:
+
+```js
+const browser = await puppeteer.launch({
+  ignoreDefaultArgs: ['--disable-extensions'],
+});
+```
+
+> Context: [issue 3681](https://github.com/GoogleChrome/puppeteer/issues/3681#issuecomment-447865342).
+
+## Chrome headless doesn't launch on UNIX
+
+Make sure all the necessary dependencies are installed. You can run `ldd chrome | grep not` on a Linux
+machine to check which dependencies are missing. The common ones are provided below.
+
+<details>
+<summary>Debian (e.g. Ubuntu) Dependencies</summary>
+
+```
+gconf-service
+libasound2
+libatk1.0-0
+libatk-bridge2.0-0
+libc6
+libcairo2
+libcups2
+libdbus-1-3
+libexpat1
+libfontconfig1
+libgcc1
+libgconf-2-4
+libgdk-pixbuf2.0-0
+libglib2.0-0
+libgtk-3-0
+libnspr4
+libpango-1.0-0
+libpangocairo-1.0-0
+libstdc++6
+libx11-6
+libx11-xcb1
+libxcb1
+libxcomposite1
+libxcursor1
+libxdamage1
+libxext6
+libxfixes3
+libxi6
+libxrandr2
+libxrender1
+libxss1
+libxtst6
+ca-certificates
+fonts-liberation
+libappindicator1
+libnss3
+lsb-release
+xdg-utils
+wget
+```
+</details>
+
+<details>
+<summary>CentOS Dependencies</summary>
+
+```
+pango.x86_64
+libXcomposite.x86_64
+libXcursor.x86_64
+libXdamage.x86_64
+libXext.x86_64
+libXi.x86_64
+libXtst.x86_64
+cups-libs.x86_64
+libXScrnSaver.x86_64
+libXrandr.x86_64
+GConf2.x86_64
+alsa-lib.x86_64
+atk.x86_64
+gtk3.x86_64
+ipa-gothic-fonts
+xorg-x11-fonts-100dpi
+xorg-x11-fonts-75dpi
+xorg-x11-utils
+xorg-x11-fonts-cyrillic
+xorg-x11-fonts-Type1
+xorg-x11-fonts-misc
+```
+
+After installing dependencies you need to update nss library using this command
+
+```
+yum update nss -y
+```
+</details>
+
+<details>
+  <summary>Check out discussions</summary>
+  
+- [#290](https://github.com/GoogleChrome/puppeteer/issues/290) - Debian troubleshooting <br/>
+- [#391](https://github.com/GoogleChrome/puppeteer/issues/391) - CentOS troubleshooting <br/>
+- [#379](https://github.com/GoogleChrome/puppeteer/issues/379) - Alpine troubleshooting <br/>
+</details>
+
+## Setting Up Chrome Linux Sandbox
+
+In order to protect the host environment from untrusted web content, Chrome uses [multiple layers of sandboxing](https://chromium.googlesource.com/chromium/src/+/HEAD/docs/linux_sandboxing.md). For this to work properly,
+the host should be configured first. If there's no good sandbox for Chrome to use, it will crash
+with the error `No usable sandbox!`.
+
+If you **absolutely trust** the content you open in Chrome, you can launch Chrome
+with the `--no-sandbox` argument:
+
+```js
+const browser = await puppeteer.launch({args: ['--no-sandbox', '--disable-setuid-sandbox']});
+```
+
+> **NOTE**: Running without a sandbox is **strongly discouraged**. Consider configuring a sandbox instead.
+
+There are 2 ways to configure a sandbox in Chromium.
+
+### [recommended] Enable [user namespace cloning](http://man7.org/linux/man-pages/man7/user_namespaces.7.html)
+
+User namespace cloning is only supported by modern kernels. Unprivileged user namespaces are generally fine to enable,
+but in some cases they open up more kernel attack surface for (unsandboxed) non-root processes to elevate to
+kernel privileges.
+
+```bash
+sudo sysctl -w kernel.unprivileged_userns_clone=1
+```
+
+### [alternative] Setup [setuid sandbox](https://chromium.googlesource.com/chromium/src/+/HEAD/docs/linux_suid_sandbox_development.md)
+
+The setuid sandbox comes as a standalone executable and is located next to the Chromium that Puppeteer downloads. It is
+fine to re-use the same sandbox executable for different Chromium versions, so the following could be
+done only once per host environment:
+
+```bash
+# cd to the downloaded instance
+cd <project-dir-path>/node_modules/puppeteer/.local-chromium/linux-<revision>/chrome-linux/
+sudo chown root:root chrome_sandbox
+sudo chmod 4755 chrome_sandbox
+# copy sandbox executable to a shared location
+sudo cp -p chrome_sandbox /usr/local/sbin/chrome-devel-sandbox
+# export CHROME_DEVEL_SANDBOX env variable
+export CHROME_DEVEL_SANDBOX=/usr/local/sbin/chrome-devel-sandbox
+```
+
+You might want to export the `CHROME_DEVEL_SANDBOX` env variable by default. In this case, add the following to the `~/.bashrc`
+or `.zshenv`:
+
+```bash
+export CHROME_DEVEL_SANDBOX=/usr/local/sbin/chrome-devel-sandbox
+```
+
+
+## Running Puppeteer on Travis CI
+
+> 👋 We run our tests for Puppeteer on Travis CI - see our [`.travis.yml`](https://github.com/GoogleChrome/puppeteer/blob/master/.travis.yml) for reference.
+
+Tips-n-tricks:
+- The `libnss3` package must be installed in order to run Chromium on Ubuntu Trusty
+- [user namespace cloning](http://man7.org/linux/man-pages/man7/user_namespaces.7.html) should be enabled to support
+  proper sandboxing
+- [xvfb](https://en.wikipedia.org/wiki/Xvfb) should be launched in order to run Chromium in non-headless mode (e.g. to test Chrome Extensions)
+
+To sum up, your `.travis.yml` might look like this:
+
+```yml
+language: node_js
+dist: trusty
+addons:
+  apt:
+    packages:
+      # This is required to run new chrome on old trusty
+      - libnss3
+notifications:
+  email: false
+cache:
+  directories:
+    - node_modules
+# allow headful tests
+before_install:
+  # Enable user namespace cloning
+  - "sysctl kernel.unprivileged_userns_clone=1"
+  # Launch XVFB
+  - "export DISPLAY=:99.0"
+  - "sh -e /etc/init.d/xvfb start"
+```
+
+
+## Running Puppeteer in Docker
+
+> 👋 We use [Cirrus Ci](https://cirrus-ci.org/) to run our tests for Puppeteer in a Docker container - see our [`Dockerfile.linux`](https://github.com/GoogleChrome/puppeteer/blob/master/.ci/node8/Dockerfile.linux) for reference.
+
+Getting headless Chrome up and running in Docker can be tricky.
+The bundled Chromium that Puppeteer installs is missing the necessary
+shared library dependencies.
+
+To fix, you'll need to install the missing dependencies and the
+latest Chromium package in your Dockerfile:
+
+```Dockerfile
+FROM node:10-slim
+
+# Install latest chrome dev package and fonts to support major charsets (Chinese, Japanese, Arabic, Hebrew, Thai and a few others)
+# Note: this installs the necessary libs to make the bundled version of Chromium that Puppeteer
+# installs, work.
+RUN wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add - \
+    && sh -c 'echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google.list' \
+    && apt-get update \
+    && apt-get install -y google-chrome-unstable fonts-ipafont-gothic fonts-wqy-zenhei fonts-thai-tlwg fonts-kacst ttf-freefont \
+      --no-install-recommends \
+    && rm -rf /var/lib/apt/lists/*
+
+# If running Docker >= 1.13.0 use docker run's --init arg to reap zombie processes, otherwise
+# uncomment the following lines to have `dumb-init` as PID 1
+# ADD https://github.com/Yelp/dumb-init/releases/download/v1.2.0/dumb-init_1.2.0_amd64 /usr/local/bin/dumb-init
+# RUN chmod +x /usr/local/bin/dumb-init
+# ENTRYPOINT ["dumb-init", "--"]
+
+# Uncomment to skip the chromium download when installing puppeteer. If you do,
+# you'll need to launch puppeteer with:
+#     browser.launch({executablePath: 'google-chrome-unstable'})
+# ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD true
+
+# Install puppeteer so it's available in the container.
+RUN npm i puppeteer \
+    # Add user so we don't need --no-sandbox.
+    # same layer as npm install to keep re-chowned files from using up several hundred MBs more space
+    && groupadd -r pptruser && useradd -r -g pptruser -G audio,video pptruser \
+    && mkdir -p /home/pptruser/Downloads \
+    && chown -R pptruser:pptruser /home/pptruser \
+    && chown -R pptruser:pptruser /node_modules
+
+# Run everything after as non-privileged user.
+USER pptruser
+
+CMD ["google-chrome-unstable"]
+```
+
+Build the container:
+
+```bash
+docker build -t puppeteer-chrome-linux .
+```
+
+Run the container by passing `node -e "<yourscript.js content as a string>` as the command:
+
+```bash
+ docker run -i --init --rm --cap-add=SYS_ADMIN \
+   --name puppeteer-chrome puppeteer-chrome-linux \
+   node -e "`cat yourscript.js`"
+```
+
+There's a full example at https://github.com/ebidel/try-puppeteer that shows
+how to run this Dockerfile from a webserver running on App Engine Flex (Node).
+
+### Running on Alpine
+
+The [newest Chromium package](https://pkgs.alpinelinux.org/package/edge/community/x86_64/chromium) supported on Alpine is 72, which was corresponding to [Puppeteer v1.11.0](https://github.com/GoogleChrome/puppeteer/releases/tag/v1.11.0).
+
+Example Dockerfile:
+
+```Dockerfile
+FROM node:10-alpine
+
+# Installs latest Chromium (72) package.
+RUN apk update && apk upgrade && \
+    echo @edge http://nl.alpinelinux.org/alpine/edge/community >> /etc/apk/repositories && \
+    echo @edge http://nl.alpinelinux.org/alpine/edge/main >> /etc/apk/repositories && \
+    apk add --no-cache \
+      chromium@edge=72.0.3626.121-r0 \
+      nss@edge \
+      freetype@edge \
+      harfbuzz@edge \
+      ttf-freefont@edge
+
+...
+
+# Tell Puppeteer to skip installing Chrome. We'll be using the installed package.
+ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD true
+
+# Puppeteer v1.11.0 works with Chromium 72.
+RUN yarn add puppeteer@1.11.0
+
+# Add user so we don't need --no-sandbox.
+RUN addgroup -S pptruser && adduser -S -g pptruser pptruser \
+    && mkdir -p /home/pptruser/Downloads /app \
+    && chown -R pptruser:pptruser /home/pptruser \
+    && chown -R pptruser:pptruser /app
+
+# Run everything after as non-privileged user.
+USER pptruser
+
+...
+```
+
+And when launching Chrome, be sure to use the `chromium-browser` executable:
+
+```js
+const browser = await puppeteer.launch({
+  executablePath: '/usr/bin/chromium-browser'
+});
+```
+
+#### Tips
+
+By default, Docker runs a container with a `/dev/shm` shared memory space 64MB.
+This is [typically too small](https://github.com/c0b/chrome-in-docker/issues/1) for Chrome
+and will cause Chrome to crash when rendering large pages. To fix, run the container with
+`docker run --shm-size=1gb` to increase the size of `/dev/shm`. Since Chrome 65, this is no
+longer necessary. Instead, launch the browser with the `--disable-dev-shm-usage` flag:
+
+```js
+const browser = await puppeteer.launch({
+  args: ['--disable-dev-shm-usage']
+});
+```
+
+This will write shared memory files into `/tmp` instead of `/dev/shm`. See [crbug.com/736452](https://bugs.chromium.org/p/chromium/issues/detail?id=736452) for more details.
+
+Seeing other weird errors when launching Chrome? Try running your container
+with `docker run --cap-add=SYS_ADMIN` when developing locally. Since the Dockerfile
+adds a `pptr` user as a non-privileged user, it may not have all the necessary privileges.
+
+[dumb-init](https://github.com/Yelp/dumb-init) is worth checking out if you're
+experiencing a lot of zombies Chrome processes sticking around. There's special
+treatment for processes with PID=1, which makes it hard to terminate Chrome
+properly in some cases (e.g. in Docker).
+
+## Running Puppeteer in the cloud
+
+### Running Puppeteer on Google App Engine
+
+The Node.js runtime of the [App Engine standard environment](https://cloud.google.com/appengine/docs/standard/nodejs/) comes with all system packages needed to run Headless Chrome.
+
+To use `puppeteer`, simply list the module as a dependency in your `package.json` and deploy to Google App Engine. Read more about using `puppeteer` on App Engine by following [the official tutorial](https://cloud.google.com/appengine/docs/standard/nodejs/using-headless-chrome-with-puppeteer).
+
+### Running Puppeteer on Google Cloud Functions
+
+The Node.js 8 runtime of [Google Cloud Functions](https://cloud.google.com/functions/docs/) comes with all system packages needed to run Headless Chrome.
+
+To use `puppeteer`, simply list the module as a dependency in your `package.json` and deploy your function to Google Cloud Functions using the `nodejs8` runtime.
+
+### Running Puppeteer on Heroku
+
+Running Puppeteer on Heroku requires some additional dependencies that aren't included on the Linux box that Heroku spins up for you. To add the dependencies on deploy, add the Puppeteer Heroku buildpack to the list of buildpacks for your app under Settings > Buildpacks.
+
+The url for the buildpack is https://github.com/jontewks/puppeteer-heroku-buildpack
+
+When you click add buildpack, simply paste that url into the input, and click save. On the next deploy, your app will also install the dependencies that Puppeteer needs to run.
+
+If you need to render Chinese, Japanese, or Korean characters you may need to use a buildpack with additional font files like https://github.com/CoffeeAndCode/puppeteer-heroku-buildpack
+
+There's also another [simple guide](https://timleland.com/headless-chrome-on-heroku/) from @timleland that includes a sample project: https://timleland.com/headless-chrome-on-heroku/.
+
+### Running Puppeteer on AWS Lambda
+
+AWS Lambda [limits](https://docs.aws.amazon.com/lambda/latest/dg/limits.html) deployment package sizes to ~50MB. This presents challenges for running headless Chrome (and therefore Puppeteer) on Lambda. The community has put together a few resources that work around the issues:
+
+- https://github.com/alixaxel/chrome-aws-lambda (kept updated with the latest stable release of puppeteer)
+- https://github.com/adieuadieu/serverless-chrome/blob/master/docs/chrome.md (serverless plugin - outdated)
+
+## Code Transpilation Issues
+
+If you are using a JavaScript transpiler like babel or TypeScript, calling `evaluate()` with an async function might not work. This is because while `puppeteer` uses `Function.prototype.toString()` to serialize functions while transpilers could be changing the output code in such a way it's incompatible with `puppeteer`.
+
+Some workarounds to this problem would be to instruct the transpiler not to mess up with the code, for example, configure TypeScript to use latest ecma version (`"target": "es2018"`). Another workaround could be using string templates instead of functions:
+
+```js
+await page.evaluate(`(async() => {
+   console.log('1');
+})()`);
+```
new file mode 100644
--- /dev/null
+++ b/remote/test/puppeteer/examples/README.md
@@ -0,0 +1,37 @@
+# Running the examples
+
+Assuming you have a checkout of the Puppeteer repo and have run npm i (or yarn) to install the dependencies, the examples can be run from the root folder like so:
+
+```sh
+NODE_PATH=../ node examples/search.js
+```
+
+## Larger examples
+
+More complex and use case driven examples can be found at [github.com/GoogleChromeLabs/puppeteer-examples](https://github.com/GoogleChromeLabs/puppeteer-examples).
+
+# Other resources
+
+> Other useful tools, articles, and projects that use Puppeteer.
+
+## Rendering and web scraping
+
+- [Puppetron](https://github.com/cheeaun/puppetron) - Demo site that shows how to use Puppeteer and Headless Chrome to render pages. Inspired by [GoogleChrome/rendertron](https://github.com/GoogleChrome/rendertron).
+- [Thal](https://medium.com/@e_mad_ehsan/getting-started-with-puppeteer-and-chrome-headless-for-web-scrapping-6bf5979dee3e "An article on medium") - Getting started with Puppeteer and Chrome Headless for Web Scraping.
+- [pupperender](https://github.com/LasaleFamine/pupperender) - Express middleware that checks the User-Agent header of incoming requests, and if it matches one of a configurable set of bots, render the page using Puppeteer. Useful for PWA rendering.
+- [headless-chrome-crawler](https://github.com/yujiosaka/headless-chrome-crawler) - Crawler that provides simple APIs to manipulate Headless Chrome and allows you to crawl dynamic websites.
+- [puppeteer-examples](https://github.com/checkly/puppeteer-examples) - Puppeteer Headless Chrome examples for real life use cases such as getting useful info from the web pages or common login scenarios.
+- [browserless](https://github.com/joelgriffith/browserless) - Headless Chrome as a service letting you execute Puppeteer scripts remotely. Provides a docker image with configuration for concurrency, launch arguments and more.
+- [Puppeteer Sandbox](https://puppeteersandbox.com) - Puppeteer sandbox environment as a service. Runs Puppeteer scripts and allows saving and embedding them in external sites and markdown files.
+
+## Testing
+
+- [angular-puppeteer-demo](https://github.com/Quramy/angular-puppeteer-demo) - Demo repository explaining how to use Puppeteer in Karma.
+- [mocha-headless-chrome](https://github.com/direct-adv-interfaces/mocha-headless-chrome) - Tool which runs client-side **mocha** tests in the command line through headless Chrome.
+- [puppeteer-to-istanbul-example](https://github.com/bcoe/puppeteer-to-istanbul-example) - Demo repository demonstrating how to output Puppeteer coverage in Istanbul format.
+- [jest-puppeteer](https://github.com/smooth-code/jest-puppeteer) - (almost) Zero configuration tool for setting up and running Jest and Puppeteer easily. Also includes an assertion library for Puppeteer.
+- [puppeteer-har](https://github.com/Everettss/puppeteer-har) - Generate HAR file with puppeteer.
+- [puppetry](https://puppetry.app/) - A desktop app to build Puppeteer/Jest driven tests without coding.
+
+## Services
+- [Checkly](https://checklyhq.com) - Monitoring SaaS that uses Puppeteer to check availability and correctness of web pages and apps.
new file mode 100644
--- /dev/null
+++ b/remote/test/puppeteer/examples/block-images.js
@@ -0,0 +1,36 @@
+/**
+ * Copyright 2017 Google Inc., PhantomJS Authors All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+'use strict';
+
+const puppeteer = require('puppeteer');
+
+(async() => {
+  const browser = await puppeteer.launch();
+  const page = await browser.newPage();
+  await page.setRequestInterception(true);
+  page.on('request', request => {
+    if (request.resourceType() === 'image')
+      request.abort();
+    else
+      request.continue();
+  });
+  await page.goto('https://news.google.com/news/');
+  await page.screenshot({path: 'news.png', fullPage: true});
+
+  await browser.close();
+})();
+
new file mode 100644
--- /dev/null
+++ b/remote/test/puppeteer/examples/custom-event.js
@@ -0,0 +1,48 @@
+/**
+ * Copyright 2017 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+'use strict';
+
+const puppeteer = require('puppeteer');
+
+(async() => {
+  const browser = await puppeteer.launch();
+  const page = await browser.newPage();
+
+  // Define a window.onCustomEvent function on the page.
+  await page.exposeFunction('onCustomEvent', e => {
+    console.log(`${e.type} fired`, e.detail || '');
+  });
+
+  /**
+   * Attach an event listener to page to capture a custom event on page load/navigation.
+   * @param {string} type Event name.
+   * @return {!Promise}
+   */
+  function listenFor(type) {
+    return page.evaluateOnNewDocument(type => {
+      document.addEventListener(type, e => {
+        window.onCustomEvent({type, detail: e.detail});
+      });
+    }, type);
+  }
+
+  await listenFor('app-ready'); // Listen for "app-ready" custom event on page load.
+
+  await page.goto('https://www.chromestatus.com/features', {waitUntil: 'networkidle0'});
+
+  await browser.close();
+})();
new file mode 100644
--- /dev/null
+++ b/remote/test/puppeteer/examples/detect-sniff.js
@@ -0,0 +1,44 @@
+/**
+ * Copyright 2017 Google Inc., PhantomJS Authors All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+'use strict';
+
+const puppeteer = require('puppeteer');
+
+function sniffDetector() {
+  const userAgent = window.navigator.userAgent;
+  const platform = window.navigator.platform;
+
+  window.navigator.__defineGetter__('userAgent', function() {
+    window.navigator.sniffed = true;
+    return userAgent;
+  });
+
+  window.navigator.__defineGetter__('platform', function() {
+    window.navigator.sniffed = true;
+    return platform;
+  });
+}
+
+(async() => {
+  const browser = await puppeteer.launch();
+  const page = await browser.newPage();
+  await page.evaluateOnNewDocument(sniffDetector);
+  await page.goto('https://www.google.com', {waitUntil: 'networkidle2'});
+  console.log('Sniffed: ' + (await page.evaluate(() => !!navigator.sniffed)));
+
+  await browser.close();
+})();
new file mode 100644
--- /dev/null
+++ b/remote/test/puppeteer/examples/pdf.js
@@ -0,0 +1,33 @@
+/**
+ * Copyright 2017 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+'use strict';
+
+const puppeteer = require('puppeteer');
+
+(async() => {
+  const browser = await puppeteer.launch();
+  const page = await browser.newPage();
+  await page.goto('https://news.ycombinator.com', {waitUntil: 'networkidle2'});
+  // page.pdf() is currently supported only in headless mode.
+  // @see https://bugs.chromium.org/p/chromium/issues/detail?id=753118
+  await page.pdf({
+    path: 'hn.pdf',
+    format: 'letter'
+  });
+
+  await browser.close();
+})();
new file mode 100644
--- /dev/null
+++ b/remote/test/puppeteer/examples/proxy.js
@@ -0,0 +1,35 @@
+/**
+ * Copyright 2017 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+'use strict';
+
+const puppeteer = require('puppeteer');
+
+(async() => {
+  const browser = await puppeteer.launch({
+    // Launch chromium using a proxy server on port 9876.
+    // More on proxying:
+    //    https://www.chromium.org/developers/design-documents/network-settings
+    args: [
+      '--proxy-server=127.0.0.1:9876',
+      // Use proxy for localhost URLs
+      '--proxy-bypass-list=<-loopback>',
+    ]
+  });
+  const page = await browser.newPage();
+  await page.goto('https://google.com');
+  await browser.close();
+})();
new file mode 100644
--- /dev/null
+++ b/remote/test/puppeteer/examples/screenshot-fullpage.js
@@ -0,0 +1,29 @@
+/**
+ * Copyright 2017 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+'use strict';
+
+const puppeteer = require('puppeteer');
+const devices = require('puppeteer/DeviceDescriptors');
+
+(async() => {
+  const browser = await puppeteer.launch();
+  const page = await browser.newPage();
+  await page.emulate(devices['iPhone 6']);
+  await page.goto('https://www.nytimes.com/');
+  await page.screenshot({path: 'full.png', fullPage: true});
+  await browser.close();
+})();
new file mode 100644
--- /dev/null
+++ b/remote/test/puppeteer/examples/screenshot.js
@@ -0,0 +1,27 @@
+/**
+ * Copyright 2017 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+'use strict';
+
+const puppeteer = require('puppeteer');
+
+(async() => {
+  const browser = await puppeteer.launch();
+  const page = await browser.newPage();
+  await page.goto('http://example.com');
+  await page.screenshot({path: 'example.png'});
+  await browser.close();
+})();
new file mode 100644
--- /dev/null
+++ b/remote/test/puppeteer/examples/search.js
@@ -0,0 +1,55 @@
+/**
+ * Copyright 2017 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @fileoverview Search developers.google.com/web for articles tagged
+ * "Headless Chrome" and scrape results from the results page.
+ */
+
+'use strict';
+
+const puppeteer = require('puppeteer');
+
+(async() => {
+  const browser = await puppeteer.launch();
+  const page = await browser.newPage();
+
+  await page.goto('https://developers.google.com/web/');
+
+  // Type into search box.
+  await page.type('#searchbox input', 'Headless Chrome');
+
+  // Wait for suggest overlay to appear and click "show all results".
+  const allResultsSelector = '.devsite-suggest-all-results';
+  await page.waitForSelector(allResultsSelector);
+  await page.click(allResultsSelector);
+
+  // Wait for the results page to load and display the results.
+  const resultsSelector = '.gsc-results .gsc-thumbnail-inside a.gs-title';
+  await page.waitForSelector(resultsSelector);
+
+  // Extract the results from the page.
+  const links = await page.evaluate(resultsSelector => {
+    const anchors = Array.from(document.querySelectorAll(resultsSelector));
+    return anchors.map(anchor => {
+      const title = anchor.textContent.split('|')[0].trim();
+      return `${title} - ${anchor.href}`;
+    });
+  }, resultsSelector);
+  console.log(links.join('\n'));
+
+  await browser.close();
+})();
new file mode 100644
--- /dev/null
+++ b/remote/test/puppeteer/experimental/puppeteer-firefox/.ci/node6/Dockerfile.linux
@@ -0,0 +1,17 @@
+FROM node:6.12.3
+
+RUN apt-get update && \
+    apt-get -y install xvfb gconf-service libasound2 libatk1.0-0 libc6 libcairo2 libcups2 \
+      libdbus-1-3 libexpat1 libfontconfig1 libgcc1 libgconf-2-4 libgdk-pixbuf2.0-0 libglib2.0-0 \
+      libgtk-3-0 libnspr4 libpango-1.0-0 libpangocairo-1.0-0 libstdc++6 libx11-6 libx11-xcb1 libxcb1 \
+      libxcomposite1 libxcursor1 libxdamage1 libxext6 libxfixes3 libxi6 libxrandr2 libxrender1 libxss1 \
+      libxtst6 ca-certificates fonts-liberation libappindicator1 libnss3 lsb-release xdg-utils wget && \
+    rm -rf /var/lib/apt/lists/*
+
+# Add user so we don't need --no-sandbox.
+RUN groupadd -r pptruser && useradd -r -g pptruser -G audio,video pptruser \
+    && mkdir -p /home/pptruser/Downloads \
+    && chown -R pptruser:pptruser /home/pptruser
+
+# Run everything after as non-privileged user.
+USER pptruser
new file mode 100644
--- /dev/null
+++ b/remote/test/puppeteer/experimental/puppeteer-firefox/.ci/node8/Dockerfile.linux
@@ -0,0 +1,17 @@
+FROM node:8.11.3-stretch
+
+RUN apt-get update && \
+    apt-get -y install xvfb gconf-service libasound2 libatk1.0-0 libc6 libcairo2 libcups2 \
+      libdbus-1-3 libexpat1 libfontconfig1 libgcc1 libgconf-2-4 libgdk-pixbuf2.0-0 libglib2.0-0 \
+      libgtk-3-0 libnspr4 libpango-1.0-0 libpangocairo-1.0-0 libstdc++6 libx11-6 libx11-xcb1 libxcb1 \
+      libxcomposite1 libxcursor1 libxdamage1 libxext6 libxfixes3 libxi6 libxrandr2 libxrender1 libxss1 \
+      libxtst6 ca-certificates fonts-liberation libappindicator1 libnss3 lsb-release xdg-utils wget && \
+    rm -rf /var/lib/apt/lists/*
+
+# Add user so we don't need --no-sandbox.
+RUN groupadd -r pptruser && useradd -r -g pptruser -G audio,video pptruser \
+    && mkdir -p /home/pptruser/Downloads \
+    && chown -R pptruser:pptruser /home/pptruser
+
+# Run everything after as non-privileged user.
+USER pptruser
new file mode 100644
--- /dev/null
+++ b/remote/test/puppeteer/experimental/puppeteer-firefox/.ci/node8/Dockerfile.windows
@@ -0,0 +1,11 @@
+FROM microsoft/windowsservercore:latest
+
+ENV NODE_VERSION 8.11.3
+
+RUN setx /m PATH "%PATH%;C:\nodejs"
+
+RUN powershell -Command \
+    netsh interface ipv4 set subinterface 18 mtu=1460 store=persistent ; \
+    Invoke-WebRequest $('https://nodejs.org/dist/v{0}/node-v{0}-win-x64.zip' -f $env:NODE_VERSION) -OutFile 'node.zip' -UseBasicParsing ; \
+    Expand-Archive node.zip -DestinationPath C:\ ; \
+    Rename-Item -Path $('C:\node-v{0}-win-x64' -f $env:NODE_VERSION) -NewName 'C:\nodejs'
new file mode 100644
--- /dev/null
+++ b/remote/test/puppeteer/experimental/puppeteer-firefox/.cirrus.yml
@@ -0,0 +1,31 @@
+env:
+  DISPLAY: :99.0
+
+task:
+  name: node8 (linux)
+  container:
+    dockerfile: .ci/node8/Dockerfile.linux
+  xvfb_start_background_script: Xvfb :99 -ac -screen 0 1024x768x24
+  install_script: npm install
+  test_script: npm run funit
+
+task:
+  name: node8 (macOS)
+  osx_instance:
+    image: high-sierra-base
+  env:
+    HOMEBREW_NO_AUTO_UPDATE: 1
+  node_install_script:
+    - brew install node@8
+    - brew link --force node@8
+  install_script: npm install
+  test_script: npm run funit
+
+# task:
+#   allow_failures: true
+#  windows_container:
+#    dockerfile: .ci/node8/Dockerfile.windows
+#    os_version: 2016
+#  name: node8 (windows)
+#  install_script: npm install --unsafe-perm
+#  test_script: npm run funit
new file mode 100644
--- /dev/null
+++ b/remote/test/puppeteer/experimental/puppeteer-firefox/.gitignore
@@ -0,0 +1,10 @@
+/node_modules/
+.DS_Store
+*.swp
+*.pyc
+.vscode
+package-lock.json
+yarn.lock
+.local-browser
+/test/output-chromium
+/test/output-firefox
new file mode 100644
--- /dev/null
+++ b/remote/test/puppeteer/experimental/puppeteer-firefox/.npmignore
@@ -0,0 +1,37 @@
+# exclude all tests
+test
+utils/node6-transform
+
+# exclude internal type definition files
+/lib/*.d.ts
+/node6/lib/*.d.ts
+
+# repeats from .gitignore
+node_modules
+.local-chromium
+.local-browser
+.dev_profile*
+.DS_Store
+*.swp
+*.pyc
+.vscode
+package-lock.json
+/node6/test
+/node6/utils
+/test
+/utils
+/docs
+yarn.lock
+
+# other
+/.ci
+/examples
+.appveyour.yml
+.cirrus.yml
+.editorconfig
+.eslintignore
+.eslintrc.js
+.travis.yml
+README.md
+tsconfig.json
+
new file mode 100644
--- /dev/null
+++ b/remote/test/puppeteer/experimental/puppeteer-firefox/DeviceDescriptors.js
@@ -0,0 +1,28 @@
+/**
+ * Copyright 2019 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+let asyncawait = true;
+try {
+  new Function('async function test(){await 1}');
+} catch (error) {
+  asyncawait = false;
+}
+
+// If node does not support async await, use the compiled version.
+if (asyncawait)
+  module.exports = require('./lib/DeviceDescriptors');
+else
+  module.exports = require('./node6/lib/DeviceDescriptors');
new file mode 100644
--- /dev/null
+++ b/remote/test/puppeteer/experimental/puppeteer-firefox/Errors.js
@@ -0,0 +1,1 @@
+module.exports = require('./lib/Errors');
new file mode 100644
--- /dev/null
+++ b/remote/test/puppeteer/experimental/puppeteer-firefox/LICENSE
@@ -0,0 +1,202 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      &