added all files to project

This commit is contained in:
2022-03-10 10:36:59 +01:00
parent 09dd957b33
commit 46a936d7de
25351 changed files with 3883356 additions and 0 deletions
+53
View File
@@ -0,0 +1,53 @@
/**
* Copyright 2020 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.
*/
export * from './common/Accessibility.js';
export * from './common/Browser.js';
export * from './node/BrowserFetcher.js';
export * from './node/Puppeteer.js';
export * from './common/Connection.js';
export * from './common/ConsoleMessage.js';
export * from './common/Coverage.js';
export * from './common/DeviceDescriptors.js';
export * from './common/Dialog.js';
export * from './common/DOMWorld.js';
export * from './common/JSHandle.js';
export * from './common/ExecutionContext.js';
export * from './common/EventEmitter.js';
export * from './common/FileChooser.js';
export * from './common/FrameManager.js';
export * from './common/Input.js';
export * from './common/Page.js';
export * from './common/Product.js';
export * from './common/Puppeteer.js';
export * from './common/BrowserConnector.js';
export * from './node/Launcher.js';
export * from './node/LaunchOptions.js';
export * from './common/HTTPRequest.js';
export * from './common/HTTPResponse.js';
export * from './common/SecurityDetails.js';
export * from './common/Target.js';
export * from './common/Errors.js';
export * from './common/Tracing.js';
export * from './common/NetworkManager.js';
export * from './common/WebWorker.js';
export * from './common/USKeyboardLayout.js';
export * from './common/EvalTypes.js';
export * from './common/PDFOptions.js';
export * from './common/TimeoutSettings.js';
export * from './common/LifecycleWatcher.js';
export * from './common/QueryHandler.js';
export * from 'devtools-protocol/types/protocol';
//# sourceMappingURL=api-docs-entry.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"api-docs-entry.d.ts","sourceRoot":"","sources":["../../../src/api-docs-entry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAcH,cAAc,2BAA2B,CAAC;AAC1C,cAAc,qBAAqB,CAAC;AACpC,cAAc,0BAA0B,CAAC;AACzC,cAAc,qBAAqB,CAAC;AACpC,cAAc,wBAAwB,CAAC;AACvC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,sBAAsB,CAAC;AACrC,cAAc,+BAA+B,CAAC;AAC9C,cAAc,oBAAoB,CAAC;AACnC,cAAc,sBAAsB,CAAC;AACrC,cAAc,sBAAsB,CAAC;AACrC,cAAc,8BAA8B,CAAC;AAC7C,cAAc,0BAA0B,CAAC;AACzC,cAAc,yBAAyB,CAAC;AACxC,cAAc,0BAA0B,CAAC;AACzC,cAAc,mBAAmB,CAAC;AAClC,cAAc,kBAAkB,CAAC;AACjC,cAAc,qBAAqB,CAAC;AACpC,cAAc,uBAAuB,CAAC;AACtC,cAAc,8BAA8B,CAAC;AAC7C,cAAc,oBAAoB,CAAC;AACnC,cAAc,yBAAyB,CAAC;AACxC,cAAc,yBAAyB,CAAC;AACxC,cAAc,0BAA0B,CAAC;AACzC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,oBAAoB,CAAC;AACnC,cAAc,oBAAoB,CAAC;AACnC,cAAc,qBAAqB,CAAC;AACpC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,uBAAuB,CAAC;AACtC,cAAc,8BAA8B,CAAC;AAC7C,cAAc,uBAAuB,CAAC;AACtC,cAAc,wBAAwB,CAAC;AACvC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,8BAA8B,CAAC;AAC7C,cAAc,0BAA0B,CAAC;AACzC,cAAc,kCAAkC,CAAC"}
+76
View File
@@ -0,0 +1,76 @@
"use strict";
/**
* Copyright 2020 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.
*/
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !exports.hasOwnProperty(p)) __createBinding(exports, m, p);
};
Object.defineProperty(exports, "__esModule", { value: true });
/*
* This file re-exports any APIs that we want to have documentation generated
* for. It is used by API Extractor to determine what parts of the system to
* document.
*
* The legacy DocLint system and the unit test coverage system use the list of
* modules defined in coverage-utils.js. src/api-docs-entry.ts is ONLY used by
* API Extractor.
*
* Once we have migrated to API Extractor and removed DocLint we can remove the
* duplication and use this file.
*/
__exportStar(require("./common/Accessibility.js"), exports);
__exportStar(require("./common/Browser.js"), exports);
__exportStar(require("./node/BrowserFetcher.js"), exports);
__exportStar(require("./node/Puppeteer.js"), exports);
__exportStar(require("./common/Connection.js"), exports);
__exportStar(require("./common/ConsoleMessage.js"), exports);
__exportStar(require("./common/Coverage.js"), exports);
__exportStar(require("./common/DeviceDescriptors.js"), exports);
__exportStar(require("./common/Dialog.js"), exports);
__exportStar(require("./common/DOMWorld.js"), exports);
__exportStar(require("./common/JSHandle.js"), exports);
__exportStar(require("./common/ExecutionContext.js"), exports);
__exportStar(require("./common/EventEmitter.js"), exports);
__exportStar(require("./common/FileChooser.js"), exports);
__exportStar(require("./common/FrameManager.js"), exports);
__exportStar(require("./common/Input.js"), exports);
__exportStar(require("./common/Page.js"), exports);
__exportStar(require("./common/Product.js"), exports);
__exportStar(require("./common/Puppeteer.js"), exports);
__exportStar(require("./common/BrowserConnector.js"), exports);
__exportStar(require("./node/Launcher.js"), exports);
__exportStar(require("./node/LaunchOptions.js"), exports);
__exportStar(require("./common/HTTPRequest.js"), exports);
__exportStar(require("./common/HTTPResponse.js"), exports);
__exportStar(require("./common/SecurityDetails.js"), exports);
__exportStar(require("./common/Target.js"), exports);
__exportStar(require("./common/Errors.js"), exports);
__exportStar(require("./common/Tracing.js"), exports);
__exportStar(require("./common/NetworkManager.js"), exports);
__exportStar(require("./common/WebWorker.js"), exports);
__exportStar(require("./common/USKeyboardLayout.js"), exports);
__exportStar(require("./common/EvalTypes.js"), exports);
__exportStar(require("./common/PDFOptions.js"), exports);
__exportStar(require("./common/TimeoutSettings.js"), exports);
__exportStar(require("./common/LifecycleWatcher.js"), exports);
__exportStar(require("./common/QueryHandler.js"), exports);
__exportStar(require("devtools-protocol/types/protocol"), exports);
+176
View File
@@ -0,0 +1,176 @@
/**
* 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.
*/
import { CDPSession } from './Connection.js';
import { ElementHandle } from './JSHandle.js';
/**
* Represents a Node and the properties of it that are relevant to Accessibility.
* @public
*/
export interface SerializedAXNode {
/**
* The {@link https://www.w3.org/TR/wai-aria/#usage_intro | role} of the node.
*/
role: string;
/**
* A human readable name for the node.
*/
name?: string;
/**
* The current value of the node.
*/
value?: string | number;
/**
* An additional human readable description of the node.
*/
description?: string;
/**
* Any keyboard shortcuts associated with this node.
*/
keyshortcuts?: string;
/**
* A human readable alternative to the role.
*/
roledescription?: string;
/**
* A description of the current value.
*/
valuetext?: string;
disabled?: boolean;
expanded?: boolean;
focused?: boolean;
modal?: boolean;
multiline?: boolean;
/**
* Whether more than one child can be selected.
*/
multiselectable?: boolean;
readonly?: boolean;
required?: boolean;
selected?: boolean;
/**
* Whether the checkbox is checked, or in a
* {@link https://www.w3.org/TR/wai-aria-practices/examples/checkbox/checkbox-2/checkbox-2.html | mixed state}.
*/
checked?: boolean | 'mixed';
/**
* Whether the node is checked or in a mixed state.
*/
pressed?: boolean | 'mixed';
/**
* The level of a heading.
*/
level?: number;
valuemin?: number;
valuemax?: number;
autocomplete?: string;
haspopup?: string;
/**
* Whether and in what way this node's value is invalid.
*/
invalid?: string;
orientation?: string;
/**
* Children of this node, if there are any.
*/
children?: SerializedAXNode[];
}
/**
* @public
*/
export interface SnapshotOptions {
/**
* Prune uninteresting nodes from the tree.
* @defaultValue true
*/
interestingOnly?: boolean;
/**
* Root node to get the accessibility tree for
* @defaultValue The root node of the entire page.
*/
root?: ElementHandle;
}
/**
* The Accessibility class provides methods for inspecting Chromium's
* accessibility tree. The accessibility tree is used by assistive technology
* such as {@link https://en.wikipedia.org/wiki/Screen_reader | screen readers} or
* {@link https://en.wikipedia.org/wiki/Switch_access | switches}.
*
* @remarks
*
* 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 then 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.
*
* @public
*/
export declare class Accessibility {
private _client;
/**
* @internal
*/
constructor(client: CDPSession);
/**
* Captures the current state of the accessibility tree.
* The returned object represents the root accessible node of the page.
*
* @remarks
*
* **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`.
*
* @example
* An example of dumping the entire accessibility tree:
* ```js
* const snapshot = await page.accessibility.snapshot();
* console.log(snapshot);
* ```
*
* @example
* 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;
* }
* ```
*
* @returns An AXNode object representing the snapshot.
*
*/
snapshot(options?: SnapshotOptions): Promise<SerializedAXNode>;
private serializeTree;
private collectInterestingNodes;
}
//# sourceMappingURL=Accessibility.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"Accessibility.d.ts","sourceRoot":"","sources":["../../../../src/common/Accessibility.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAG9C;;;GAGG;AACH,MAAM,WAAW,gBAAgB;IAC/B;;OAEG;IACH,IAAI,EAAE,MAAM,CAAC;IACb;;OAEG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACxB;;OAEG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;OAEG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;OAEG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB;;OAEG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC;IAC5B;;OAEG;IACH,OAAO,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC;IAC5B;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;OAEG;IACH,QAAQ,CAAC,EAAE,gBAAgB,EAAE,CAAC;CAC/B;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B;;;OAGG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B;;;OAGG;IACH,IAAI,CAAC,EAAE,aAAa,CAAC;CACtB;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,OAAO,CAAa;IAE5B;;OAEG;gBACS,MAAM,EAAE,UAAU;IAI9B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAsCG;IACU,QAAQ,CACnB,OAAO,GAAE,eAAoB,GAC5B,OAAO,CAAC,gBAAgB,CAAC;IA0B5B,OAAO,CAAC,aAAa;IAerB,OAAO,CAAC,uBAAuB;CAWhC"}
+360
View File
@@ -0,0 +1,360 @@
"use strict";
/**
* 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.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.Accessibility = void 0;
/**
* The Accessibility class provides methods for inspecting Chromium's
* accessibility tree. The accessibility tree is used by assistive technology
* such as {@link https://en.wikipedia.org/wiki/Screen_reader | screen readers} or
* {@link https://en.wikipedia.org/wiki/Switch_access | switches}.
*
* @remarks
*
* 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 then 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.
*
* @public
*/
class Accessibility {
/**
* @internal
*/
constructor(client) {
this._client = client;
}
/**
* Captures the current state of the accessibility tree.
* The returned object represents the root accessible node of the page.
*
* @remarks
*
* **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`.
*
* @example
* An example of dumping the entire accessibility tree:
* ```js
* const snapshot = await page.accessibility.snapshot();
* console.log(snapshot);
* ```
*
* @example
* 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;
* }
* ```
*
* @returns An AXNode object representing the snapshot.
*
*/
async snapshot(options = {}) {
const { interestingOnly = true, root = null } = options;
const { nodes } = await this._client.send('Accessibility.getFullAXTree');
let backendNodeId = null;
if (root) {
const { node } = await this._client.send('DOM.describeNode', {
objectId: root._remoteObject.objectId,
});
backendNodeId = node.backendNodeId;
}
const defaultRoot = AXNode.createTree(nodes);
let needle = defaultRoot;
if (backendNodeId) {
needle = defaultRoot.find((node) => node.payload.backendDOMNodeId === backendNodeId);
if (!needle)
return null;
}
if (!interestingOnly)
return this.serializeTree(needle)[0];
const interestingNodes = new Set();
this.collectInterestingNodes(interestingNodes, defaultRoot, false);
if (!interestingNodes.has(needle))
return null;
return this.serializeTree(needle, interestingNodes)[0];
}
serializeTree(node, interestingNodes) {
const children = [];
for (const child of node.children)
children.push(...this.serializeTree(child, interestingNodes));
if (interestingNodes && !interestingNodes.has(node))
return children;
const serializedNode = node.serialize();
if (children.length)
serializedNode.children = children;
return [serializedNode];
}
collectInterestingNodes(collection, node, insideControl) {
if (node.isInteresting(insideControl))
collection.add(node);
if (node.isLeafNode())
return;
insideControl = insideControl || node.isControl();
for (const child of node.children)
this.collectInterestingNodes(collection, child, insideControl);
}
}
exports.Accessibility = Accessibility;
class AXNode {
constructor(payload) {
this.children = [];
this._richlyEditable = false;
this._editable = false;
this._focusable = false;
this._hidden = false;
this.payload = payload;
this._name = this.payload.name ? this.payload.name.value : '';
this._role = this.payload.role ? this.payload.role.value : 'Unknown';
this._ignored = this.payload.ignored;
for (const property of this.payload.properties || []) {
if (property.name === 'editable') {
this._richlyEditable = property.value.value === 'richtext';
this._editable = true;
}
if (property.name === 'focusable')
this._focusable = property.value.value;
if (property.name === 'hidden')
this._hidden = property.value.value;
}
}
_isPlainTextField() {
if (this._richlyEditable)
return false;
if (this._editable)
return true;
return this._role === 'textbox' || this._role === 'searchbox';
}
_isTextOnlyObject() {
const role = this._role;
return role === 'LineBreak' || role === 'text' || role === 'InlineTextBox';
}
_hasFocusableChild() {
if (this._cachedHasFocusableChild === undefined) {
this._cachedHasFocusableChild = false;
for (const child of this.children) {
if (child._focusable || child._hasFocusableChild()) {
this._cachedHasFocusableChild = true;
break;
}
}
}
return this._cachedHasFocusableChild;
}
find(predicate) {
if (predicate(this))
return this;
for (const child of this.children) {
const result = child.find(predicate);
if (result)
return result;
}
return null;
}
isLeafNode() {
if (!this.children.length)
return true;
// These types of objects may have children that we use as internal
// implementation details, but we want to expose them as leaves to platform
// accessibility APIs because screen readers might be confused if they find
// any children.
if (this._isPlainTextField() || this._isTextOnlyObject())
return true;
// Roles whose children are only presentational according to the ARIA and
// HTML5 Specs should be hidden from screen readers.
// (Note that whilst ARIA buttons can have only presentational children, HTML5
// buttons are allowed to have content.)
switch (this._role) {
case 'doc-cover':
case 'graphics-symbol':
case 'img':
case 'Meter':
case 'scrollbar':
case 'slider':
case 'separator':
case 'progressbar':
return true;
default:
break;
}
// Here and below: Android heuristics
if (this._hasFocusableChild())
return false;
if (this._focusable && this._name)
return true;
if (this._role === 'heading' && this._name)
return true;
return false;
}
isControl() {
switch (this._role) {
case 'button':
case 'checkbox':
case 'ColorWell':
case 'combobox':
case 'DisclosureTriangle':
case 'listbox':
case 'menu':
case 'menubar':
case 'menuitem':
case 'menuitemcheckbox':
case 'menuitemradio':
case 'radio':
case 'scrollbar':
case 'searchbox':
case 'slider':
case 'spinbutton':
case 'switch':
case 'tab':
case 'textbox':
case 'tree':
case 'treeitem':
return true;
default:
return false;
}
}
isInteresting(insideControl) {
const role = this._role;
if (role === 'Ignored' || this._hidden || this._ignored)
return false;
if (this._focusable || this._richlyEditable)
return true;
// If it's not focusable but has a control role, then it's interesting.
if (this.isControl())
return true;
// A non focusable child of a control is not interesting
if (insideControl)
return false;
return this.isLeafNode() && !!this._name;
}
serialize() {
const properties = new Map();
for (const property of this.payload.properties || [])
properties.set(property.name.toLowerCase(), property.value.value);
if (this.payload.name)
properties.set('name', this.payload.name.value);
if (this.payload.value)
properties.set('value', this.payload.value.value);
if (this.payload.description)
properties.set('description', this.payload.description.value);
const node = {
role: this._role,
};
const userStringProperties = [
'name',
'value',
'description',
'keyshortcuts',
'roledescription',
'valuetext',
];
const getUserStringPropertyValue = (key) => properties.get(key);
for (const userStringProperty of userStringProperties) {
if (!properties.has(userStringProperty))
continue;
node[userStringProperty] = getUserStringPropertyValue(userStringProperty);
}
const booleanProperties = [
'disabled',
'expanded',
'focused',
'modal',
'multiline',
'multiselectable',
'readonly',
'required',
'selected',
];
const getBooleanPropertyValue = (key) => properties.get(key);
for (const booleanProperty of booleanProperties) {
// WebArea's treat focus differently than other nodes. They report whether
// their frame has focus, not whether focus is specifically on the root
// node.
if (booleanProperty === 'focused' && this._role === 'WebArea')
continue;
const value = getBooleanPropertyValue(booleanProperty);
if (!value)
continue;
node[booleanProperty] = getBooleanPropertyValue(booleanProperty);
}
const tristateProperties = ['checked', 'pressed'];
for (const tristateProperty of tristateProperties) {
if (!properties.has(tristateProperty))
continue;
const value = properties.get(tristateProperty);
node[tristateProperty] =
value === 'mixed' ? 'mixed' : value === 'true' ? true : false;
}
const numericalProperties = [
'level',
'valuemax',
'valuemin',
];
const getNumericalPropertyValue = (key) => properties.get(key);
for (const numericalProperty of numericalProperties) {
if (!properties.has(numericalProperty))
continue;
node[numericalProperty] = getNumericalPropertyValue(numericalProperty);
}
const tokenProperties = [
'autocomplete',
'haspopup',
'invalid',
'orientation',
];
const getTokenPropertyValue = (key) => properties.get(key);
for (const tokenProperty of tokenProperties) {
const value = getTokenPropertyValue(tokenProperty);
if (!value || value === 'false')
continue;
node[tokenProperty] = getTokenPropertyValue(tokenProperty);
}
return node;
}
static createTree(payloads) {
const nodeById = new Map();
for (const payload of payloads)
nodeById.set(payload.nodeId, new AXNode(payload));
for (const node of nodeById.values()) {
for (const childId of node.payload.childIds || [])
node.children.push(nodeById.get(childId));
}
return nodeById.values().next().value;
}
}
@@ -0,0 +1,21 @@
/**
* Copyright 2020 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.
*/
import { InternalQueryHandler } from './QueryHandler.js';
/**
* @internal
*/
export declare const ariaHandler: InternalQueryHandler;
//# sourceMappingURL=AriaQueryHandler.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"AriaQueryHandler.d.ts","sourceRoot":"","sources":["../../../../src/common/AriaQueryHandler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAmHzD;;GAEG;AACH,eAAO,MAAM,WAAW,EAAE,oBAKzB,CAAC"}
@@ -0,0 +1,84 @@
"use strict";
/**
* Copyright 2020 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.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.ariaHandler = void 0;
async function queryAXTree(client, element, accessibleName, role) {
const { nodes } = await client.send('Accessibility.queryAXTree', {
objectId: element._remoteObject.objectId,
accessibleName,
role,
});
const filteredNodes = nodes.filter((node) => node.role.value !== 'text');
return filteredNodes;
}
function parseAriaSelector(selector) {
const normalize = (value) => value.replace(/ +/g, ' ').trim();
const knownAttributes = new Set(['name', 'role']);
const queryOptions = {};
const attributeRegexp = /\[\s*(?<attribute>\w+)\s*=\s*"(?<value>\\.|[^"\\]*)"\s*\]/;
const defaultName = selector.replace(attributeRegexp, (_, attribute, value) => {
attribute = attribute.trim();
if (!knownAttributes.has(attribute))
throw new Error('Unkown aria attribute "${groups.attribute}" in selector');
queryOptions[attribute] = normalize(value);
return '';
});
if (defaultName && !queryOptions.name)
queryOptions.name = normalize(defaultName);
return queryOptions;
}
const queryOne = async (element, selector) => {
const exeCtx = element.executionContext();
const { name, role } = parseAriaSelector(selector);
const res = await queryAXTree(exeCtx._client, element, name, role);
if (res.length < 1) {
return null;
}
return exeCtx._adoptBackendNodeId(res[0].backendDOMNodeId);
};
const waitFor = async (domWorld, selector, options) => {
const binding = {
name: 'ariaQuerySelector',
pptrFunction: async (selector) => {
const document = await domWorld._document();
const element = await queryOne(document, selector);
return element;
},
};
return domWorld.waitForSelectorInPage((_, selector) => globalThis.ariaQuerySelector(selector), selector, options, binding);
};
const queryAll = async (element, selector) => {
const exeCtx = element.executionContext();
const { name, role } = parseAriaSelector(selector);
const res = await queryAXTree(exeCtx._client, element, name, role);
return Promise.all(res.map((axNode) => exeCtx._adoptBackendNodeId(axNode.backendDOMNodeId)));
};
const queryAllArray = async (element, selector) => {
const elementHandles = await queryAll(element, selector);
const exeCtx = element.executionContext();
const jsHandle = exeCtx.evaluateHandle((...elements) => elements, ...elementHandles);
return jsHandle;
};
/**
* @internal
*/
exports.ariaHandler = {
queryOne,
waitFor,
queryAll,
queryAllArray,
};
+421
View File
@@ -0,0 +1,421 @@
/**
* 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.
*/
/// <reference types="node" />
import { Target } from './Target.js';
import { EventEmitter } from './EventEmitter.js';
import { Connection } from './Connection.js';
import { Page } from './Page.js';
import { ChildProcess } from 'child_process';
import { Viewport } from './PuppeteerViewport.js';
declare type BrowserCloseCallback = () => Promise<void> | void;
/**
* @public
*/
export interface WaitForTargetOptions {
/**
* Maximum wait time in milliseconds. Pass `0` to disable the timeout.
* @defaultValue 30 seconds.
*/
timeout?: number;
}
/**
* All the events a {@link Browser | browser instance} may emit.
*
* @public
*/
export declare const enum BrowserEmittedEvents {
/**
* Emitted when Puppeteer gets disconnected from the Chromium instance. This
* might happen because of one of the following:
*
* - Chromium is closed or crashed
*
* - The {@link Browser.disconnect | browser.disconnect } method was called.
*/
Disconnected = "disconnected",
/**
* Emitted when the url of a target changes. Contains a {@link Target} instance.
*
* @remarks
*
* Note that this includes target changes in incognito browser contexts.
*/
TargetChanged = "targetchanged",
/**
* Emitted when a target is created, for example when a new page is opened by
* {@link https://developer.mozilla.org/en-US/docs/Web/API/Window/open | window.open}
* or by {@link Browser.newPage | browser.newPage}
*
* Contains a {@link Target} instance.
*
* @remarks
*
* Note that this includes target creations in incognito browser contexts.
*/
TargetCreated = "targetcreated",
/**
* Emitted when a target is destroyed, for example when a page is closed.
* Contains a {@link Target} instance.
*
* @remarks
*
* Note that this includes target destructions in incognito browser contexts.
*/
TargetDestroyed = "targetdestroyed"
}
/**
* A Browser is created when Puppeteer connects to a Chromium instance, either through
* {@link PuppeteerNode.launch} or {@link Puppeteer.connect}.
*
* @remarks
*
* The Browser class extends from Puppeteer's {@link EventEmitter} class and will
* emit various events which are documented in the {@link BrowserEmittedEvents} enum.
*
* @example
*
* An example of using a {@link Browser} to create a {@link Page}:
* ```js
* const puppeteer = require('puppeteer');
*
* (async () => {
* const browser = await puppeteer.launch();
* const page = await browser.newPage();
* await page.goto('https://example.com');
* await browser.close();
* })();
* ```
*
* @example
*
* An example of disconnecting from and reconnecting to a {@link Browser}:
* ```js
* const puppeteer = require('puppeteer');
*
* (async () => {
* const browser = await puppeteer.launch();
* // 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();
* })();
* ```
*
* @public
*/
export declare class Browser extends EventEmitter {
/**
* @internal
*/
static create(connection: Connection, contextIds: string[], ignoreHTTPSErrors: boolean, defaultViewport?: Viewport, process?: ChildProcess, closeCallback?: BrowserCloseCallback): Promise<Browser>;
private _ignoreHTTPSErrors;
private _defaultViewport?;
private _process?;
private _connection;
private _closeCallback;
private _defaultContext;
private _contexts;
/**
* @internal
* Used in Target.ts directly so cannot be marked private.
*/
_targets: Map<string, Target>;
/**
* @internal
*/
constructor(connection: Connection, contextIds: string[], ignoreHTTPSErrors: boolean, defaultViewport?: Viewport, process?: ChildProcess, closeCallback?: BrowserCloseCallback);
/**
* The spawned browser process. Returns `null` if the browser instance was created with
* {@link Puppeteer.connect}.
*/
process(): ChildProcess | null;
/**
* Creates a new incognito browser context. This won't share cookies/cache with other
* browser contexts.
*
* @example
* ```js
* (async () => {
* 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');
* })();
* ```
*/
createIncognitoBrowserContext(): Promise<BrowserContext>;
/**
* Returns an array of all open browser contexts. In a newly created browser, this will
* return a single instance of {@link BrowserContext}.
*/
browserContexts(): BrowserContext[];
/**
* Returns the default browser context. The default browser context cannot be closed.
*/
defaultBrowserContext(): BrowserContext;
/**
* @internal
* Used by BrowserContext directly so cannot be marked private.
*/
_disposeContext(contextId?: string): Promise<void>;
private _targetCreated;
private _targetDestroyed;
private _targetInfoChanged;
/**
* The browser websocket endpoint which can be used as an argument to
* {@link Puppeteer.connect}.
*
* @returns The Browser websocket url.
*
* @remarks
*
* The format is `ws://${host}:${port}/devtools/browser/<id>`.
*
* You can find the `webSocketDebuggerUrl` from `http://${host}:${port}/json/version`.
* Learn more about the
* {@link https://chromedevtools.github.io/devtools-protocol | devtools protocol} and
* the {@link
* https://chromedevtools.github.io/devtools-protocol/#how-do-i-access-the-browser-target
* | browser endpoint}.
*/
wsEndpoint(): string;
/**
* Creates a {@link Page} in the default browser context.
*/
newPage(): Promise<Page>;
/**
* @internal
* Used by BrowserContext directly so cannot be marked private.
*/
_createPageInContext(contextId?: string): Promise<Page>;
/**
* All active targets inside the Browser. In case of multiple browser contexts, returns
* an array with all the targets in all browser contexts.
*/
targets(): Target[];
/**
* The target associated with the browser.
*/
target(): Target;
/**
* Searches for a target in all browser contexts.
*
* @param predicate - A function to be run for every target.
* @returns The first target found that matches the `predicate` function.
*
* @example
*
* 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/');
* ```
*/
waitForTarget(predicate: (x: Target) => boolean, options?: WaitForTargetOptions): Promise<Target>;
/**
* An array of all open pages inside the Browser.
*
* @remarks
*
* In case of multiple browser contexts, returns an array with all the pages in all
* browser contexts. Non-visible pages, such as `"background_page"`, will not be listed
* here. You can find them using {@link Target.page}.
*/
pages(): Promise<Page[]>;
/**
* A string representing the browser name and version.
*
* @remarks
*
* For headless Chromium, this is similar to `HeadlessChrome/61.0.3153.0`. For
* non-headless, this is similar to `Chrome/61.0.3153.0`.
*
* The format of browser.version() might change with future releases of Chromium.
*/
version(): Promise<string>;
/**
* The browser's original user agent. Pages can override the browser user agent with
* {@link Page.setUserAgent}.
*/
userAgent(): Promise<string>;
/**
* Closes Chromium and all of its pages (if any were opened). The {@link Browser} object
* itself is considered to be disposed and cannot be used anymore.
*/
close(): Promise<void>;
/**
* Disconnects Puppeteer from the browser, but leaves the Chromium process running.
* After calling `disconnect`, the {@link Browser} object is considered disposed and
* cannot be used anymore.
*/
disconnect(): void;
/**
* Indicates that the browser is connected.
*/
isConnected(): boolean;
private _getVersion;
}
export declare const enum BrowserContextEmittedEvents {
/**
* Emitted when the url of a target inside the browser context changes.
* Contains a {@link Target} instance.
*/
TargetChanged = "targetchanged",
/**
* Emitted when a target is created within the browser context, for example
* when a new page is opened by
* {@link https://developer.mozilla.org/en-US/docs/Web/API/Window/open | window.open}
* or by {@link BrowserContext.newPage | browserContext.newPage}
*
* Contains a {@link Target} instance.
*/
TargetCreated = "targetcreated",
/**
* Emitted when a target is destroyed within the browser context, for example
* when a page is closed. Contains a {@link Target} instance.
*/
TargetDestroyed = "targetdestroyed"
}
/**
* 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 {@link Browser.newPage | Browser.newPage} creates a page
* in the default browser context.
*
* @remarks
*
* The Browser class extends from Puppeteer's {@link EventEmitter} class and
* will emit various events which are documented in the
* {@link BrowserContextEmittedEvents} enum.
*
* 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
* {@link Browser.createIncognitoBrowserContext | Browser.createIncognitoBrowserContext}
* method. "Incognito" browser contexts don't write any browsing data to disk.
*
* @example
* ```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();
* ```
*/
export declare class BrowserContext extends EventEmitter {
private _connection;
private _browser;
private _id?;
/**
* @internal
*/
constructor(connection: Connection, browser: Browser, contextId?: string);
/**
* An array of all active targets inside the browser context.
*/
targets(): Target[];
/**
* This searches for a target in this specific browser context.
*
* @example
* 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/');
* ```
*
* @param predicate - A function to be run for every target
* @param options - An object of options. Accepts a timout,
* which is the maximum wait time in milliseconds.
* Pass `0` to disable the timeout. Defaults to 30 seconds.
* @returns Promise which resolves to the first target found
* that matches the `predicate` function.
*/
waitForTarget(predicate: (x: Target) => boolean, options?: {
timeout?: number;
}): Promise<Target>;
/**
* An array of all pages inside the browser context.
*
* @returns 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 {@link Target.page | the target page}.
*/
pages(): Promise<Page[]>;
/**
* Returns whether BrowserContext is incognito.
* The default browser context is the only non-incognito browser context.
*
* @remarks
* The default browser context cannot be closed.
*/
isIncognito(): boolean;
/**
* @example
* ```js
* const context = browser.defaultBrowserContext();
* await context.overridePermissions('https://html5demos.com', ['geolocation']);
* ```
*
* @param origin - The origin to grant permissions to, e.g. "https://example.com".
* @param permissions - An array of permissions to grant.
* All permissions that are not listed here will be automatically denied.
*/
overridePermissions(origin: string, permissions: string[]): Promise<void>;
/**
* Clears all permission overrides for the browser context.
*
* @example
* ```js
* const context = browser.defaultBrowserContext();
* context.overridePermissions('https://example.com', ['clipboard-read']);
* // do stuff ..
* context.clearPermissionOverrides();
* ```
*/
clearPermissionOverrides(): Promise<void>;
/**
* Creates a new page in the browser context.
*/
newPage(): Promise<Page>;
/**
* The browser this browser context belongs to.
*/
browser(): Browser;
/**
* Closes the browser context. All the targets that belong to the browser context
* will be closed.
*
* @remarks
* Only incognito browser contexts can be closed.
*/
close(): Promise<void>;
}
export {};
//# sourceMappingURL=Browser.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"Browser.d.ts","sourceRoot":"","sources":["../../../../src/common/Browser.ts"],"names":[],"mappings":"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"}
+520
View File
@@ -0,0 +1,520 @@
"use strict";
/**
* 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.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.BrowserContext = exports.Browser = void 0;
const assert_js_1 = require("./assert.js");
const helper_js_1 = require("./helper.js");
const Target_js_1 = require("./Target.js");
const EventEmitter_js_1 = require("./EventEmitter.js");
const Connection_js_1 = require("./Connection.js");
/**
* A Browser is created when Puppeteer connects to a Chromium instance, either through
* {@link PuppeteerNode.launch} or {@link Puppeteer.connect}.
*
* @remarks
*
* The Browser class extends from Puppeteer's {@link EventEmitter} class and will
* emit various events which are documented in the {@link BrowserEmittedEvents} enum.
*
* @example
*
* An example of using a {@link Browser} to create a {@link Page}:
* ```js
* const puppeteer = require('puppeteer');
*
* (async () => {
* const browser = await puppeteer.launch();
* const page = await browser.newPage();
* await page.goto('https://example.com');
* await browser.close();
* })();
* ```
*
* @example
*
* An example of disconnecting from and reconnecting to a {@link Browser}:
* ```js
* const puppeteer = require('puppeteer');
*
* (async () => {
* const browser = await puppeteer.launch();
* // 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();
* })();
* ```
*
* @public
*/
class Browser extends EventEmitter_js_1.EventEmitter {
/**
* @internal
*/
constructor(connection, contextIds, ignoreHTTPSErrors, defaultViewport, process, closeCallback) {
super();
this._ignoreHTTPSErrors = ignoreHTTPSErrors;
this._defaultViewport = defaultViewport;
this._process = process;
this._connection = connection;
this._closeCallback = closeCallback || function () { };
this._defaultContext = new BrowserContext(this._connection, this, null);
this._contexts = new Map();
for (const contextId of contextIds)
this._contexts.set(contextId, new BrowserContext(this._connection, this, contextId));
this._targets = new Map();
this._connection.on(Connection_js_1.ConnectionEmittedEvents.Disconnected, () => this.emit("disconnected" /* Disconnected */));
this._connection.on('Target.targetCreated', this._targetCreated.bind(this));
this._connection.on('Target.targetDestroyed', this._targetDestroyed.bind(this));
this._connection.on('Target.targetInfoChanged', this._targetInfoChanged.bind(this));
}
/**
* @internal
*/
static async create(connection, contextIds, ignoreHTTPSErrors, defaultViewport, process, closeCallback) {
const browser = new Browser(connection, contextIds, ignoreHTTPSErrors, defaultViewport, process, closeCallback);
await connection.send('Target.setDiscoverTargets', { discover: true });
return browser;
}
/**
* The spawned browser process. Returns `null` if the browser instance was created with
* {@link Puppeteer.connect}.
*/
process() {
return this._process;
}
/**
* Creates a new incognito browser context. This won't share cookies/cache with other
* browser contexts.
*
* @example
* ```js
* (async () => {
* 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');
* })();
* ```
*/
async createIncognitoBrowserContext() {
const { browserContextId } = await this._connection.send('Target.createBrowserContext');
const context = new BrowserContext(this._connection, this, browserContextId);
this._contexts.set(browserContextId, context);
return context;
}
/**
* Returns an array of all open browser contexts. In a newly created browser, this will
* return a single instance of {@link BrowserContext}.
*/
browserContexts() {
return [this._defaultContext, ...Array.from(this._contexts.values())];
}
/**
* Returns the default browser context. The default browser context cannot be closed.
*/
defaultBrowserContext() {
return this._defaultContext;
}
/**
* @internal
* Used by BrowserContext directly so cannot be marked private.
*/
async _disposeContext(contextId) {
await this._connection.send('Target.disposeBrowserContext', {
browserContextId: contextId || undefined,
});
this._contexts.delete(contextId);
}
async _targetCreated(event) {
const targetInfo = event.targetInfo;
const { browserContextId } = targetInfo;
const context = browserContextId && this._contexts.has(browserContextId)
? this._contexts.get(browserContextId)
: this._defaultContext;
const target = new Target_js_1.Target(targetInfo, context, () => this._connection.createSession(targetInfo), this._ignoreHTTPSErrors, this._defaultViewport);
assert_js_1.assert(!this._targets.has(event.targetInfo.targetId), 'Target should not exist before targetCreated');
this._targets.set(event.targetInfo.targetId, target);
if (await target._initializedPromise) {
this.emit("targetcreated" /* TargetCreated */, target);
context.emit("targetcreated" /* TargetCreated */, target);
}
}
async _targetDestroyed(event) {
const target = this._targets.get(event.targetId);
target._initializedCallback(false);
this._targets.delete(event.targetId);
target._closedCallback();
if (await target._initializedPromise) {
this.emit("targetdestroyed" /* TargetDestroyed */, target);
target
.browserContext()
.emit("targetdestroyed" /* TargetDestroyed */, target);
}
}
_targetInfoChanged(event) {
const target = this._targets.get(event.targetInfo.targetId);
assert_js_1.assert(target, 'target should exist before targetInfoChanged');
const previousURL = target.url();
const wasInitialized = target._isInitialized;
target._targetInfoChanged(event.targetInfo);
if (wasInitialized && previousURL !== target.url()) {
this.emit("targetchanged" /* TargetChanged */, target);
target
.browserContext()
.emit("targetchanged" /* TargetChanged */, target);
}
}
/**
* The browser websocket endpoint which can be used as an argument to
* {@link Puppeteer.connect}.
*
* @returns The Browser websocket url.
*
* @remarks
*
* The format is `ws://${host}:${port}/devtools/browser/<id>`.
*
* You can find the `webSocketDebuggerUrl` from `http://${host}:${port}/json/version`.
* Learn more about the
* {@link https://chromedevtools.github.io/devtools-protocol | devtools protocol} and
* the {@link
* https://chromedevtools.github.io/devtools-protocol/#how-do-i-access-the-browser-target
* | browser endpoint}.
*/
wsEndpoint() {
return this._connection.url();
}
/**
* Creates a {@link Page} in the default browser context.
*/
async newPage() {
return this._defaultContext.newPage();
}
/**
* @internal
* Used by BrowserContext directly so cannot be marked private.
*/
async _createPageInContext(contextId) {
const { targetId } = await this._connection.send('Target.createTarget', {
url: 'about:blank',
browserContextId: contextId || undefined,
});
const target = await this._targets.get(targetId);
assert_js_1.assert(await target._initializedPromise, 'Failed to create target for page');
const page = await target.page();
return page;
}
/**
* All active targets inside the Browser. In case of multiple browser contexts, returns
* an array with all the targets in all browser contexts.
*/
targets() {
return Array.from(this._targets.values()).filter((target) => target._isInitialized);
}
/**
* The target associated with the browser.
*/
target() {
return this.targets().find((target) => target.type() === 'browser');
}
/**
* Searches for a target in all browser contexts.
*
* @param predicate - A function to be run for every target.
* @returns The first target found that matches the `predicate` function.
*
* @example
*
* 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/');
* ```
*/
async waitForTarget(predicate, options = {}) {
const { timeout = 30000 } = options;
const existingTarget = this.targets().find(predicate);
if (existingTarget)
return existingTarget;
let resolve;
const targetPromise = new Promise((x) => (resolve = x));
this.on("targetcreated" /* TargetCreated */, check);
this.on("targetchanged" /* TargetChanged */, check);
try {
if (!timeout)
return await targetPromise;
return await helper_js_1.helper.waitWithTimeout(targetPromise, 'target', timeout);
}
finally {
this.removeListener("targetcreated" /* TargetCreated */, check);
this.removeListener("targetchanged" /* TargetChanged */, check);
}
function check(target) {
if (predicate(target))
resolve(target);
}
}
/**
* An array of all open pages inside the Browser.
*
* @remarks
*
* In case of multiple browser contexts, returns an array with all the pages in all
* browser contexts. Non-visible pages, such as `"background_page"`, will not be listed
* here. You can find them using {@link Target.page}.
*/
async pages() {
const contextPages = await Promise.all(this.browserContexts().map((context) => context.pages()));
// Flatten array.
return contextPages.reduce((acc, x) => acc.concat(x), []);
}
/**
* A string representing the browser name and version.
*
* @remarks
*
* For headless Chromium, this is similar to `HeadlessChrome/61.0.3153.0`. For
* non-headless, this is similar to `Chrome/61.0.3153.0`.
*
* The format of browser.version() might change with future releases of Chromium.
*/
async version() {
const version = await this._getVersion();
return version.product;
}
/**
* The browser's original user agent. Pages can override the browser user agent with
* {@link Page.setUserAgent}.
*/
async userAgent() {
const version = await this._getVersion();
return version.userAgent;
}
/**
* Closes Chromium and all of its pages (if any were opened). The {@link Browser} object
* itself is considered to be disposed and cannot be used anymore.
*/
async close() {
await this._closeCallback.call(null);
this.disconnect();
}
/**
* Disconnects Puppeteer from the browser, but leaves the Chromium process running.
* After calling `disconnect`, the {@link Browser} object is considered disposed and
* cannot be used anymore.
*/
disconnect() {
this._connection.dispose();
}
/**
* Indicates that the browser is connected.
*/
isConnected() {
return !this._connection._closed;
}
_getVersion() {
return this._connection.send('Browser.getVersion');
}
}
exports.Browser = Browser;
/**
* 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 {@link Browser.newPage | Browser.newPage} creates a page
* in the default browser context.
*
* @remarks
*
* The Browser class extends from Puppeteer's {@link EventEmitter} class and
* will emit various events which are documented in the
* {@link BrowserContextEmittedEvents} enum.
*
* 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
* {@link Browser.createIncognitoBrowserContext | Browser.createIncognitoBrowserContext}
* method. "Incognito" browser contexts don't write any browsing data to disk.
*
* @example
* ```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();
* ```
*/
class BrowserContext extends EventEmitter_js_1.EventEmitter {
/**
* @internal
*/
constructor(connection, browser, contextId) {
super();
this._connection = connection;
this._browser = browser;
this._id = contextId;
}
/**
* An array of all active targets inside the browser context.
*/
targets() {
return this._browser
.targets()
.filter((target) => target.browserContext() === this);
}
/**
* This searches for a target in this specific browser context.
*
* @example
* 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/');
* ```
*
* @param predicate - A function to be run for every target
* @param options - An object of options. Accepts a timout,
* which is the maximum wait time in milliseconds.
* Pass `0` to disable the timeout. Defaults to 30 seconds.
* @returns Promise which resolves to the first target found
* that matches the `predicate` function.
*/
waitForTarget(predicate, options = {}) {
return this._browser.waitForTarget((target) => target.browserContext() === this && predicate(target), options);
}
/**
* An array of all pages inside the browser context.
*
* @returns 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 {@link Target.page | the target page}.
*/
async pages() {
const pages = await Promise.all(this.targets()
.filter((target) => target.type() === 'page')
.map((target) => target.page()));
return pages.filter((page) => !!page);
}
/**
* Returns whether BrowserContext is incognito.
* The default browser context is the only non-incognito browser context.
*
* @remarks
* The default browser context cannot be closed.
*/
isIncognito() {
return !!this._id;
}
/**
* @example
* ```js
* const context = browser.defaultBrowserContext();
* await context.overridePermissions('https://html5demos.com', ['geolocation']);
* ```
*
* @param origin - The origin to grant permissions to, e.g. "https://example.com".
* @param permissions - An array of permissions to grant.
* All permissions that are not listed here will be automatically denied.
*/
async overridePermissions(origin, permissions) {
const webPermissionToProtocol = new Map([
['geolocation', 'geolocation'],
['midi', 'midi'],
['notifications', 'notifications'],
// TODO: push isn't a valid type?
// ['push', 'push'],
['camera', 'videoCapture'],
['microphone', 'audioCapture'],
['background-sync', 'backgroundSync'],
['ambient-light-sensor', 'sensors'],
['accelerometer', 'sensors'],
['gyroscope', 'sensors'],
['magnetometer', 'sensors'],
['accessibility-events', 'accessibilityEvents'],
['clipboard-read', 'clipboardReadWrite'],
['clipboard-write', 'clipboardReadWrite'],
['payment-handler', 'paymentHandler'],
['idle-detection', 'idleDetection'],
// chrome-specific permissions we have.
['midi-sysex', 'midiSysex'],
]);
const protocolPermissions = permissions.map((permission) => {
const protocolPermission = webPermissionToProtocol.get(permission);
if (!protocolPermission)
throw new Error('Unknown permission: ' + permission);
return protocolPermission;
});
await this._connection.send('Browser.grantPermissions', {
origin,
browserContextId: this._id || undefined,
permissions: protocolPermissions,
});
}
/**
* Clears all permission overrides for the browser context.
*
* @example
* ```js
* const context = browser.defaultBrowserContext();
* context.overridePermissions('https://example.com', ['clipboard-read']);
* // do stuff ..
* context.clearPermissionOverrides();
* ```
*/
async clearPermissionOverrides() {
await this._connection.send('Browser.resetPermissions', {
browserContextId: this._id || undefined,
});
}
/**
* Creates a new page in the browser context.
*/
newPage() {
return this._browser._createPageInContext(this._id);
}
/**
* The browser this browser context belongs to.
*/
browser() {
return this._browser;
}
/**
* Closes the browser context. All the targets that belong to the browser context
* will be closed.
*
* @remarks
* Only incognito browser contexts can be closed.
*/
async close() {
assert_js_1.assert(this._id, 'Non-incognito profiles cannot be closed!');
await this._browser._disposeContext(this._id);
}
}
exports.BrowserContext = BrowserContext;
@@ -0,0 +1,38 @@
/**
* Copyright 2020 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.
*/
import { ConnectionTransport } from './ConnectionTransport.js';
import { Browser } from './Browser.js';
import { Viewport } from './PuppeteerViewport.js';
/**
* Generic browser options that can be passed when launching any browser.
* @public
*/
export interface BrowserOptions {
ignoreHTTPSErrors?: boolean;
defaultViewport?: Viewport;
slowMo?: number;
}
/**
* Users should never call this directly; it's called when calling
* `puppeteer.connect`.
* @internal
*/
export declare const connectToBrowser: (options: BrowserOptions & {
browserWSEndpoint?: string;
browserURL?: string;
transport?: ConnectionTransport;
}) => Promise<Browser>;
//# sourceMappingURL=BrowserConnector.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"BrowserConnector.d.ts","sourceRoot":"","sources":["../../../../src/common/BrowserConnector.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAC/D,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAKvC,OAAO,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAGlD;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC7B,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,eAAe,CAAC,EAAE,QAAQ,CAAC;IAC3B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AASD;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,YAClB,cAAc,GAAG;IACxB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,mBAAmB,CAAC;CACjC,KACA,OAAO,CAAC,OAAO,CA6CjB,CAAC"}
@@ -0,0 +1,96 @@
"use strict";
/**
* Copyright 2020 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.
*/
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.connectToBrowser = void 0;
const Browser_js_1 = require("./Browser.js");
const assert_js_1 = require("./assert.js");
const helper_js_1 = require("../common/helper.js");
const Connection_js_1 = require("./Connection.js");
const fetch_js_1 = require("./fetch.js");
const environment_js_1 = require("../environment.js");
const getWebSocketTransportClass = async () => {
return environment_js_1.isNode
? (await Promise.resolve().then(() => __importStar(require('../node/NodeWebSocketTransport.js')))).NodeWebSocketTransport
: (await Promise.resolve().then(() => __importStar(require('./BrowserWebSocketTransport.js'))))
.BrowserWebSocketTransport;
};
/**
* Users should never call this directly; it's called when calling
* `puppeteer.connect`.
* @internal
*/
exports.connectToBrowser = async (options) => {
const { browserWSEndpoint, browserURL, ignoreHTTPSErrors = false, defaultViewport = { width: 800, height: 600 }, transport, slowMo = 0, } = options;
assert_js_1.assert(Number(!!browserWSEndpoint) + Number(!!browserURL) + Number(!!transport) ===
1, 'Exactly one of browserWSEndpoint, browserURL or transport must be passed to puppeteer.connect');
let connection = null;
if (transport) {
connection = new Connection_js_1.Connection('', transport, slowMo);
}
else if (browserWSEndpoint) {
const WebSocketClass = await getWebSocketTransportClass();
const connectionTransport = await WebSocketClass.create(browserWSEndpoint);
connection = new Connection_js_1.Connection(browserWSEndpoint, connectionTransport, slowMo);
}
else if (browserURL) {
const connectionURL = await getWSEndpoint(browserURL);
const WebSocketClass = await getWebSocketTransportClass();
const connectionTransport = await WebSocketClass.create(connectionURL);
connection = new Connection_js_1.Connection(connectionURL, connectionTransport, slowMo);
}
const { browserContextIds } = await connection.send('Target.getBrowserContexts');
return Browser_js_1.Browser.create(connection, browserContextIds, ignoreHTTPSErrors, defaultViewport, null, () => connection.send('Browser.close').catch(helper_js_1.debugError));
};
async function getWSEndpoint(browserURL) {
const endpointURL = new URL('/json/version', browserURL);
const fetch = await fetch_js_1.getFetch();
try {
const result = await fetch(endpointURL.toString(), {
method: 'GET',
});
if (!result.ok) {
throw new Error(`HTTP ${result.statusText}`);
}
const data = await result.json();
return data.webSocketDebuggerUrl;
}
catch (error) {
error.message =
`Failed to fetch browser webSocket URL from ${endpointURL}: ` +
error.message;
throw error;
}
}
@@ -0,0 +1,26 @@
/**
* Copyright 2020 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.
*/
import { ConnectionTransport } from './ConnectionTransport.js';
export declare class BrowserWebSocketTransport implements ConnectionTransport {
static create(url: string): Promise<BrowserWebSocketTransport>;
private _ws;
onmessage?: (message: string) => void;
onclose?: () => void;
constructor(ws: WebSocket);
send(message: string): void;
close(): void;
}
//# sourceMappingURL=BrowserWebSocketTransport.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"BrowserWebSocketTransport.d.ts","sourceRoot":"","sources":["../../../../src/common/BrowserWebSocketTransport.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAE/D,qBAAa,yBAA0B,YAAW,mBAAmB;IACnE,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,yBAAyB,CAAC;IAW9D,OAAO,CAAC,GAAG,CAAY;IACvB,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACtC,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;gBAET,EAAE,EAAE,SAAS;IAczB,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAI3B,KAAK,IAAI,IAAI;CAGd"}
@@ -0,0 +1,34 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.BrowserWebSocketTransport = void 0;
class BrowserWebSocketTransport {
constructor(ws) {
this._ws = ws;
this._ws.addEventListener('message', (event) => {
if (this.onmessage)
this.onmessage.call(null, event.data);
});
this._ws.addEventListener('close', () => {
if (this.onclose)
this.onclose.call(null);
});
// Silently ignore all errors - we don't know what to do with them.
this._ws.addEventListener('error', () => { });
this.onmessage = null;
this.onclose = null;
}
static create(url) {
return new Promise((resolve, reject) => {
const ws = new WebSocket(url);
ws.addEventListener('open', () => resolve(new BrowserWebSocketTransport(ws)));
ws.addEventListener('error', reject);
});
}
send(message) {
this._ws.send(message);
}
close() {
this._ws.close();
}
}
exports.BrowserWebSocketTransport = BrowserWebSocketTransport;
+120
View File
@@ -0,0 +1,120 @@
import { Protocol } from 'devtools-protocol';
import { ProtocolMapping } from 'devtools-protocol/types/protocol-mapping.js';
import { ConnectionTransport } from './ConnectionTransport.js';
import { EventEmitter } from './EventEmitter.js';
interface ConnectionCallback {
resolve: Function;
reject: Function;
error: Error;
method: string;
}
/**
* Internal events that the Connection class emits.
*
* @internal
*/
export declare const ConnectionEmittedEvents: {
readonly Disconnected: symbol;
};
/**
* @internal
*/
export declare class Connection extends EventEmitter {
_url: string;
_transport: ConnectionTransport;
_delay: number;
_lastId: number;
_sessions: Map<string, CDPSession>;
_closed: boolean;
_callbacks: Map<number, ConnectionCallback>;
constructor(url: string, transport: ConnectionTransport, delay?: number);
static fromSession(session: CDPSession): Connection;
/**
* @param {string} sessionId
* @returns {?CDPSession}
*/
session(sessionId: string): CDPSession | null;
url(): string;
send<T extends keyof ProtocolMapping.Commands>(method: T, ...paramArgs: ProtocolMapping.Commands[T]['paramsType']): Promise<ProtocolMapping.Commands[T]['returnType']>;
_rawSend(message: Record<string, unknown>): number;
_onMessage(message: string): Promise<void>;
_onClose(): void;
dispose(): void;
/**
* @param {Protocol.Target.TargetInfo} targetInfo
* @returns {!Promise<!CDPSession>}
*/
createSession(targetInfo: Protocol.Target.TargetInfo): Promise<CDPSession>;
}
interface CDPSessionOnMessageObject {
id?: number;
method: string;
params: Record<string, unknown>;
error: {
message: string;
data: any;
};
result?: any;
}
/**
* Internal events that the CDPSession class emits.
*
* @internal
*/
export declare const CDPSessionEmittedEvents: {
readonly Disconnected: symbol;
};
/**
* The `CDPSession` instances are used to talk raw Chrome Devtools Protocol.
*
* @remarks
*
* Protocol methods can be called with {@link CDPSession.send} method and protocol
* events can be subscribed to with `CDPSession.on` method.
*
* Useful links: {@link https://chromedevtools.github.io/devtools-protocol/ | DevTools Protocol Viewer}
* and {@link https://github.com/aslushnikov/getting-started-with-cdp/blob/master/README.md | Getting Started with DevTools Protocol}.
*
* @example
* ```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
* });
* ```
*
* @public
*/
export declare class CDPSession extends EventEmitter {
/**
* @internal
*/
_connection: Connection;
private _sessionId;
private _targetType;
private _callbacks;
/**
* @internal
*/
constructor(connection: Connection, targetType: string, sessionId: string);
send<T extends keyof ProtocolMapping.Commands>(method: T, ...paramArgs: ProtocolMapping.Commands[T]['paramsType']): Promise<ProtocolMapping.Commands[T]['returnType']>;
/**
* @internal
*/
_onMessage(object: CDPSessionOnMessageObject): void;
/**
* Detaches the cdpSession from the target. Once detached, the cdpSession object
* won't emit any events and can't be used to send messages.
*/
detach(): Promise<void>;
/**
* @internal
*/
_onClosed(): void;
}
export {};
//# sourceMappingURL=Connection.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"Connection.d.ts","sourceRoot":"","sources":["../../../../src/common/Connection.ts"],"names":[],"mappings":"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"}
+271
View File
@@ -0,0 +1,271 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.CDPSession = exports.CDPSessionEmittedEvents = exports.Connection = exports.ConnectionEmittedEvents = void 0;
/**
* 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.
*/
const assert_js_1 = require("./assert.js");
const Debug_js_1 = require("./Debug.js");
const debugProtocolSend = Debug_js_1.debug('puppeteer:protocol:SEND ►');
const debugProtocolReceive = Debug_js_1.debug('puppeteer:protocol:RECV ◀');
const EventEmitter_js_1 = require("./EventEmitter.js");
/**
* Internal events that the Connection class emits.
*
* @internal
*/
exports.ConnectionEmittedEvents = {
Disconnected: Symbol('Connection.Disconnected'),
};
/**
* @internal
*/
class Connection extends EventEmitter_js_1.EventEmitter {
constructor(url, transport, delay = 0) {
super();
this._lastId = 0;
this._sessions = new Map();
this._closed = false;
this._callbacks = new Map();
this._url = url;
this._delay = delay;
this._transport = transport;
this._transport.onmessage = this._onMessage.bind(this);
this._transport.onclose = this._onClose.bind(this);
}
static fromSession(session) {
return session._connection;
}
/**
* @param {string} sessionId
* @returns {?CDPSession}
*/
session(sessionId) {
return this._sessions.get(sessionId) || null;
}
url() {
return this._url;
}
send(method, ...paramArgs) {
// There is only ever 1 param arg passed, but the Protocol defines it as an
// array of 0 or 1 items See this comment:
// https://github.com/ChromeDevTools/devtools-protocol/pull/113#issuecomment-412603285
// which explains why the protocol defines the params this way for better
// type-inference.
// So now we check if there are any params or not and deal with them accordingly.
const params = paramArgs.length ? paramArgs[0] : undefined;
const id = this._rawSend({ method, params });
return new Promise((resolve, reject) => {
this._callbacks.set(id, { resolve, reject, error: new Error(), method });
});
}
_rawSend(message) {
const id = ++this._lastId;
const stringifiedMessage = JSON.stringify(Object.assign({}, message, { id }));
debugProtocolSend(stringifiedMessage);
this._transport.send(stringifiedMessage);
return id;
}
async _onMessage(message) {
if (this._delay)
await new Promise((f) => setTimeout(f, this._delay));
debugProtocolReceive(message);
const object = JSON.parse(message);
if (object.method === 'Target.attachedToTarget') {
const sessionId = object.params.sessionId;
const session = new CDPSession(this, object.params.targetInfo.type, sessionId);
this._sessions.set(sessionId, session);
}
else if (object.method === 'Target.detachedFromTarget') {
const session = this._sessions.get(object.params.sessionId);
if (session) {
session._onClosed();
this._sessions.delete(object.params.sessionId);
}
}
if (object.sessionId) {
const session = this._sessions.get(object.sessionId);
if (session)
session._onMessage(object);
}
else if (object.id) {
const callback = this._callbacks.get(object.id);
// Callbacks could be all rejected if someone has called `.dispose()`.
if (callback) {
this._callbacks.delete(object.id);
if (object.error)
callback.reject(createProtocolError(callback.error, callback.method, object));
else
callback.resolve(object.result);
}
}
else {
this.emit(object.method, object.params);
}
}
_onClose() {
if (this._closed)
return;
this._closed = true;
this._transport.onmessage = null;
this._transport.onclose = null;
for (const callback of this._callbacks.values())
callback.reject(rewriteError(callback.error, `Protocol error (${callback.method}): Target closed.`));
this._callbacks.clear();
for (const session of this._sessions.values())
session._onClosed();
this._sessions.clear();
this.emit(exports.ConnectionEmittedEvents.Disconnected);
}
dispose() {
this._onClose();
this._transport.close();
}
/**
* @param {Protocol.Target.TargetInfo} targetInfo
* @returns {!Promise<!CDPSession>}
*/
async createSession(targetInfo) {
const { sessionId } = await this.send('Target.attachToTarget', {
targetId: targetInfo.targetId,
flatten: true,
});
return this._sessions.get(sessionId);
}
}
exports.Connection = Connection;
/**
* Internal events that the CDPSession class emits.
*
* @internal
*/
exports.CDPSessionEmittedEvents = {
Disconnected: Symbol('CDPSession.Disconnected'),
};
/**
* The `CDPSession` instances are used to talk raw Chrome Devtools Protocol.
*
* @remarks
*
* Protocol methods can be called with {@link CDPSession.send} method and protocol
* events can be subscribed to with `CDPSession.on` method.
*
* Useful links: {@link https://chromedevtools.github.io/devtools-protocol/ | DevTools Protocol Viewer}
* and {@link https://github.com/aslushnikov/getting-started-with-cdp/blob/master/README.md | Getting Started with DevTools Protocol}.
*
* @example
* ```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
* });
* ```
*
* @public
*/
class CDPSession extends EventEmitter_js_1.EventEmitter {
/**
* @internal
*/
constructor(connection, targetType, sessionId) {
super();
this._callbacks = new Map();
this._connection = connection;
this._targetType = targetType;
this._sessionId = sessionId;
}
send(method, ...paramArgs) {
if (!this._connection)
return Promise.reject(new Error(`Protocol error (${method}): Session closed. Most likely the ${this._targetType} has been closed.`));
// See the comment in Connection#send explaining why we do this.
const params = paramArgs.length ? paramArgs[0] : undefined;
const id = this._connection._rawSend({
sessionId: this._sessionId,
method,
/* TODO(jacktfranklin@): once this Firefox bug is solved
* we no longer need the `|| {}` check
* https://bugzilla.mozilla.org/show_bug.cgi?id=1631570
*/
params: params || {},
});
return new Promise((resolve, reject) => {
this._callbacks.set(id, { resolve, reject, error: new Error(), method });
});
}
/**
* @internal
*/
_onMessage(object) {
if (object.id && this._callbacks.has(object.id)) {
const callback = this._callbacks.get(object.id);
this._callbacks.delete(object.id);
if (object.error)
callback.reject(createProtocolError(callback.error, callback.method, object));
else
callback.resolve(object.result);
}
else {
assert_js_1.assert(!object.id);
this.emit(object.method, object.params);
}
}
/**
* Detaches the cdpSession from the target. Once detached, the cdpSession object
* won't emit any events and can't be used to send messages.
*/
async detach() {
if (!this._connection)
throw new Error(`Session already detached. Most likely the ${this._targetType} has been closed.`);
await this._connection.send('Target.detachFromTarget', {
sessionId: this._sessionId,
});
}
/**
* @internal
*/
_onClosed() {
for (const callback of this._callbacks.values())
callback.reject(rewriteError(callback.error, `Protocol error (${callback.method}): Target closed.`));
this._callbacks.clear();
this._connection = null;
this.emit(exports.CDPSessionEmittedEvents.Disconnected);
}
}
exports.CDPSession = CDPSession;
/**
* @param {!Error} error
* @param {string} method
* @param {{error: {message: string, data: any}}} object
* @returns {!Error}
*/
function createProtocolError(error, method, object) {
let message = `Protocol error (${method}): ${object.error.message}`;
if ('data' in object.error)
message += ` ${object.error.data}`;
return rewriteError(error, message);
}
/**
* @param {!Error} error
* @param {string} message
* @returns {!Error}
*/
function rewriteError(error, message) {
error.message = message;
return error;
}
@@ -0,0 +1,22 @@
/**
* Copyright 2020 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.
*/
export interface ConnectionTransport {
send(string: any): any;
close(): any;
onmessage?: (message: string) => void;
onclose?: () => void;
}
//# sourceMappingURL=ConnectionTransport.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"ConnectionTransport.d.ts","sourceRoot":"","sources":["../../../../src/common/ConnectionTransport.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,MAAM,WAAW,mBAAmB;IAClC,IAAI,CAAC,MAAM,KAAA,OAAE;IACb,KAAK,QAAG;IACR,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACtC,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;CACtB"}
@@ -0,0 +1,17 @@
"use strict";
/**
* Copyright 2020 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.
*/
Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,72 @@
/**
* Copyright 2020 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.
*/
import { JSHandle } from './JSHandle.js';
/**
* @public
*/
export interface ConsoleMessageLocation {
/**
* URL of the resource if known or `undefined` otherwise.
*/
url?: string;
/**
* 0-based line number in the resource if known or `undefined` otherwise.
*/
lineNumber?: number;
/**
* 0-based column number in the resource if known or `undefined` otherwise.
*/
columnNumber?: number;
}
/**
* The supported types for console messages.
*/
export declare type ConsoleMessageType = 'log' | 'debug' | 'info' | 'error' | 'warning' | 'dir' | 'dirxml' | 'table' | 'trace' | 'clear' | 'startGroup' | 'startGroupCollapsed' | 'endGroup' | 'assert' | 'profile' | 'profileEnd' | 'count' | 'timeEnd' | 'verbose';
/**
* ConsoleMessage objects are dispatched by page via the 'console' event.
* @public
*/
export declare class ConsoleMessage {
private _type;
private _text;
private _args;
private _stackTraceLocations;
/**
* @public
*/
constructor(type: ConsoleMessageType, text: string, args: JSHandle[], stackTraceLocations: ConsoleMessageLocation[]);
/**
* @returns The type of the console message.
*/
type(): ConsoleMessageType;
/**
* @returns The text of the console message.
*/
text(): string;
/**
* @returns An array of arguments passed to the console.
*/
args(): JSHandle[];
/**
* @returns The location of the console message.
*/
location(): ConsoleMessageLocation;
/**
* @returns The array of locations on the stack of the console message.
*/
stackTrace(): ConsoleMessageLocation[];
}
//# sourceMappingURL=ConsoleMessage.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"ConsoleMessage.d.ts","sourceRoot":"","sources":["../../../../src/common/ConsoleMessage.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAEzC;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC;;OAEG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;IAEb;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB;;OAEG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;GAEG;AACH,oBAAY,kBAAkB,GAC1B,KAAK,GACL,OAAO,GACP,MAAM,GACN,OAAO,GACP,SAAS,GACT,KAAK,GACL,QAAQ,GACR,OAAO,GACP,OAAO,GACP,OAAO,GACP,YAAY,GACZ,qBAAqB,GACrB,UAAU,GACV,QAAQ,GACR,SAAS,GACT,YAAY,GACZ,OAAO,GACP,SAAS,GACT,SAAS,CAAC;AAEd;;;GAGG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,KAAK,CAAqB;IAClC,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,KAAK,CAAa;IAC1B,OAAO,CAAC,oBAAoB,CAA2B;IAEvD;;OAEG;gBAED,IAAI,EAAE,kBAAkB,EACxB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,QAAQ,EAAE,EAChB,mBAAmB,EAAE,sBAAsB,EAAE;IAQ/C;;OAEG;IACH,IAAI,IAAI,kBAAkB;IAI1B;;OAEG;IACH,IAAI,IAAI,MAAM;IAId;;OAEG;IACH,IAAI,IAAI,QAAQ,EAAE;IAIlB;;OAEG;IACH,QAAQ,IAAI,sBAAsB;IAIlC;;OAEG;IACH,UAAU,IAAI,sBAAsB,EAAE;CAGvC"}
+64
View File
@@ -0,0 +1,64 @@
"use strict";
/**
* Copyright 2020 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.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.ConsoleMessage = void 0;
/**
* ConsoleMessage objects are dispatched by page via the 'console' event.
* @public
*/
class ConsoleMessage {
/**
* @public
*/
constructor(type, text, args, stackTraceLocations) {
this._type = type;
this._text = text;
this._args = args;
this._stackTraceLocations = stackTraceLocations;
}
/**
* @returns The type of the console message.
*/
type() {
return this._type;
}
/**
* @returns The text of the console message.
*/
text() {
return this._text;
}
/**
* @returns An array of arguments passed to the console.
*/
args() {
return this._args;
}
/**
* @returns The location of the console message.
*/
location() {
return this._stackTraceLocations.length ? this._stackTraceLocations[0] : {};
}
/**
* @returns The array of locations on the stack of the console message.
*/
stackTrace() {
return this._stackTraceLocations;
}
}
exports.ConsoleMessage = ConsoleMessage;
+179
View File
@@ -0,0 +1,179 @@
/**
* 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.
*/
import { PuppeteerEventListener } from './helper.js';
import { Protocol } from 'devtools-protocol';
import { CDPSession } from './Connection.js';
/**
* The CoverageEntry class represents one entry of the coverage report.
* @public
*/
export interface CoverageEntry {
/**
* The URL of the style sheet or script.
*/
url: string;
/**
* The content of the style sheet or script.
*/
text: string;
/**
* The covered range as start and end positions.
*/
ranges: Array<{
start: number;
end: number;
}>;
}
/**
* Set of configurable options for JS coverage.
* @public
*/
export interface JSCoverageOptions {
/**
* Whether to reset coverage on every navigation.
*/
resetOnNavigation?: boolean;
/**
* Whether anonymous scripts generated by the page should be reported.
*/
reportAnonymousScripts?: boolean;
}
/**
* Set of configurable options for CSS coverage.
* @public
*/
export interface CSSCoverageOptions {
/**
* Whether to reset coverage on every navigation.
*/
resetOnNavigation?: boolean;
}
/**
* The Coverage class provides methods to gathers information about parts of
* JavaScript and CSS that were used by the page.
*
* @remarks
* To output coverage in a form consumable by {@link https://github.com/istanbuljs | Istanbul},
* see {@link https://github.com/istanbuljs/puppeteer-to-istanbul | puppeteer-to-istanbul}.
*
* @example
* 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}%`);
* ```
* @public
*/
export declare class Coverage {
/**
* @internal
*/
_jsCoverage: JSCoverage;
/**
* @internal
*/
_cssCoverage: CSSCoverage;
constructor(client: CDPSession);
/**
* @param options - defaults to
* `{ resetOnNavigation : true, reportAnonymousScripts : false }`
* @returns Promise that resolves when coverage is started.
*
* @remarks
* 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.
*/
startJSCoverage(options?: JSCoverageOptions): Promise<void>;
/**
* @returns Promise that resolves to the array of coverage reports for
* all scripts.
*
* @remarks
* JavaScript Coverage doesn't include anonymous scripts by default.
* However, scripts with sourceURLs are reported.
*/
stopJSCoverage(): Promise<CoverageEntry[]>;
/**
* @param options - defaults to `{ resetOnNavigation : true }`
* @returns Promise that resolves when coverage is started.
*/
startCSSCoverage(options?: CSSCoverageOptions): Promise<void>;
/**
* @returns Promise that resolves to the array of coverage reports
* for all stylesheets.
* @remarks
* CSS Coverage doesn't include dynamically injected style tags
* without sourceURLs.
*/
stopCSSCoverage(): Promise<CoverageEntry[]>;
}
declare class JSCoverage {
_client: CDPSession;
_enabled: boolean;
_scriptURLs: Map<string, string>;
_scriptSources: Map<string, string>;
_eventListeners: PuppeteerEventListener[];
_resetOnNavigation: boolean;
_reportAnonymousScripts: boolean;
constructor(client: CDPSession);
start(options?: {
resetOnNavigation?: boolean;
reportAnonymousScripts?: boolean;
}): Promise<void>;
_onExecutionContextsCleared(): void;
_onScriptParsed(event: Protocol.Debugger.ScriptParsedEvent): Promise<void>;
stop(): Promise<CoverageEntry[]>;
}
declare class CSSCoverage {
_client: CDPSession;
_enabled: boolean;
_stylesheetURLs: Map<string, string>;
_stylesheetSources: Map<string, string>;
_eventListeners: PuppeteerEventListener[];
_resetOnNavigation: boolean;
_reportAnonymousScripts: boolean;
constructor(client: CDPSession);
start(options?: {
resetOnNavigation?: boolean;
}): Promise<void>;
_onExecutionContextsCleared(): void;
_onStyleSheet(event: Protocol.CSS.StyleSheetAddedEvent): Promise<void>;
stop(): Promise<CoverageEntry[]>;
}
export {};
//# sourceMappingURL=Coverage.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"Coverage.d.ts","sourceRoot":"","sources":["../../../../src/common/Coverage.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAGH,OAAO,EAAsB,sBAAsB,EAAE,MAAM,aAAa,CAAC;AACzE,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAC7C,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAI7C;;;GAGG;AACH,MAAM,WAAW,aAAa;IAC5B;;OAEG;IACH,GAAG,EAAE,MAAM,CAAC;IACZ;;OAEG;IACH,IAAI,EAAE,MAAM,CAAC;IACb;;OAEG;IACH,MAAM,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAC/C;AAED;;;GAGG;AACH,MAAM,WAAW,iBAAiB;IAChC;;OAEG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B;;OAEG;IACH,sBAAsB,CAAC,EAAE,OAAO,CAAC;CAClC;AAED;;;GAGG;AACH,MAAM,WAAW,kBAAkB;IACjC;;OAEG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC7B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,qBAAa,QAAQ;IACnB;;OAEG;IACH,WAAW,EAAE,UAAU,CAAC;IACxB;;OAEG;IACH,YAAY,EAAE,WAAW,CAAC;gBAEd,MAAM,EAAE,UAAU;IAK9B;;;;;;;;;;OAUG;IACG,eAAe,CAAC,OAAO,GAAE,iBAAsB,GAAG,OAAO,CAAC,IAAI,CAAC;IAIrE;;;;;;;OAOG;IACG,cAAc,IAAI,OAAO,CAAC,aAAa,EAAE,CAAC;IAIhD;;;OAGG;IACG,gBAAgB,CAAC,OAAO,GAAE,kBAAuB,GAAG,OAAO,CAAC,IAAI,CAAC;IAIvE;;;;;;OAMG;IACG,eAAe,IAAI,OAAO,CAAC,aAAa,EAAE,CAAC;CAGlD;AAED,cAAM,UAAU;IACd,OAAO,EAAE,UAAU,CAAC;IACpB,QAAQ,UAAS;IACjB,WAAW,sBAA6B;IACxC,cAAc,sBAA6B;IAC3C,eAAe,EAAE,sBAAsB,EAAE,CAAM;IAC/C,kBAAkB,UAAS;IAC3B,uBAAuB,UAAS;gBAEpB,MAAM,EAAE,UAAU;IAIxB,KAAK,CACT,OAAO,GAAE;QACP,iBAAiB,CAAC,EAAE,OAAO,CAAC;QAC5B,sBAAsB,CAAC,EAAE,OAAO,CAAC;KAC7B,GACL,OAAO,CAAC,IAAI,CAAC;IAkChB,2BAA2B,IAAI,IAAI;IAM7B,eAAe,CACnB,KAAK,EAAE,QAAQ,CAAC,QAAQ,CAAC,iBAAiB,GACzC,OAAO,CAAC,IAAI,CAAC;IAiBV,IAAI,IAAI,OAAO,CAAC,aAAa,EAAE,CAAC;CAkCvC;AAED,cAAM,WAAW;IACf,OAAO,EAAE,UAAU,CAAC;IACpB,QAAQ,UAAS;IACjB,eAAe,sBAA6B;IAC5C,kBAAkB,sBAA6B;IAC/C,eAAe,EAAE,sBAAsB,EAAE,CAAM;IAC/C,kBAAkB,UAAS;IAC3B,uBAAuB,UAAS;gBAEpB,MAAM,EAAE,UAAU;IAIxB,KAAK,CAAC,OAAO,GAAE;QAAE,iBAAiB,CAAC,EAAE,OAAO,CAAA;KAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IA0BzE,2BAA2B,IAAI,IAAI;IAM7B,aAAa,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,CAAC,oBAAoB,GAAG,OAAO,CAAC,IAAI,CAAC;IAgBtE,IAAI,IAAI,OAAO,CAAC,aAAa,EAAE,CAAC;CAuCvC"}
+319
View File
@@ -0,0 +1,319 @@
"use strict";
/**
* 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.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.Coverage = void 0;
const assert_js_1 = require("./assert.js");
const helper_js_1 = require("./helper.js");
const ExecutionContext_js_1 = require("./ExecutionContext.js");
/**
* The Coverage class provides methods to gathers information about parts of
* JavaScript and CSS that were used by the page.
*
* @remarks
* To output coverage in a form consumable by {@link https://github.com/istanbuljs | Istanbul},
* see {@link https://github.com/istanbuljs/puppeteer-to-istanbul | puppeteer-to-istanbul}.
*
* @example
* 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}%`);
* ```
* @public
*/
class Coverage {
constructor(client) {
this._jsCoverage = new JSCoverage(client);
this._cssCoverage = new CSSCoverage(client);
}
/**
* @param options - defaults to
* `{ resetOnNavigation : true, reportAnonymousScripts : false }`
* @returns Promise that resolves when coverage is started.
*
* @remarks
* 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.
*/
async startJSCoverage(options = {}) {
return await this._jsCoverage.start(options);
}
/**
* @returns Promise that resolves to the array of coverage reports for
* all scripts.
*
* @remarks
* JavaScript Coverage doesn't include anonymous scripts by default.
* However, scripts with sourceURLs are reported.
*/
async stopJSCoverage() {
return await this._jsCoverage.stop();
}
/**
* @param options - defaults to `{ resetOnNavigation : true }`
* @returns Promise that resolves when coverage is started.
*/
async startCSSCoverage(options = {}) {
return await this._cssCoverage.start(options);
}
/**
* @returns Promise that resolves to the array of coverage reports
* for all stylesheets.
* @remarks
* CSS Coverage doesn't include dynamically injected style tags
* without sourceURLs.
*/
async stopCSSCoverage() {
return await this._cssCoverage.stop();
}
}
exports.Coverage = Coverage;
class JSCoverage {
constructor(client) {
this._enabled = false;
this._scriptURLs = new Map();
this._scriptSources = new Map();
this._eventListeners = [];
this._resetOnNavigation = false;
this._reportAnonymousScripts = false;
this._client = client;
}
async start(options = {}) {
assert_js_1.assert(!this._enabled, 'JSCoverage is already enabled');
const { resetOnNavigation = true, reportAnonymousScripts = false, } = options;
this._resetOnNavigation = resetOnNavigation;
this._reportAnonymousScripts = reportAnonymousScripts;
this._enabled = true;
this._scriptURLs.clear();
this._scriptSources.clear();
this._eventListeners = [
helper_js_1.helper.addEventListener(this._client, 'Debugger.scriptParsed', this._onScriptParsed.bind(this)),
helper_js_1.helper.addEventListener(this._client, 'Runtime.executionContextsCleared', this._onExecutionContextsCleared.bind(this)),
];
await Promise.all([
this._client.send('Profiler.enable'),
this._client.send('Profiler.startPreciseCoverage', {
callCount: false,
detailed: true,
}),
this._client.send('Debugger.enable'),
this._client.send('Debugger.setSkipAllPauses', { skip: true }),
]);
}
_onExecutionContextsCleared() {
if (!this._resetOnNavigation)
return;
this._scriptURLs.clear();
this._scriptSources.clear();
}
async _onScriptParsed(event) {
// Ignore puppeteer-injected scripts
if (event.url === ExecutionContext_js_1.EVALUATION_SCRIPT_URL)
return;
// Ignore other anonymous scripts unless the reportAnonymousScripts option is true.
if (!event.url && !this._reportAnonymousScripts)
return;
try {
const response = await this._client.send('Debugger.getScriptSource', {
scriptId: event.scriptId,
});
this._scriptURLs.set(event.scriptId, event.url);
this._scriptSources.set(event.scriptId, response.scriptSource);
}
catch (error) {
// This might happen if the page has already navigated away.
helper_js_1.debugError(error);
}
}
async stop() {
assert_js_1.assert(this._enabled, 'JSCoverage is not enabled');
this._enabled = false;
const result = await Promise.all([
this._client.send('Profiler.takePreciseCoverage'),
this._client.send('Profiler.stopPreciseCoverage'),
this._client.send('Profiler.disable'),
this._client.send('Debugger.disable'),
]);
helper_js_1.helper.removeEventListeners(this._eventListeners);
const coverage = [];
const profileResponse = result[0];
for (const entry of profileResponse.result) {
let url = this._scriptURLs.get(entry.scriptId);
if (!url && this._reportAnonymousScripts)
url = 'debugger://VM' + entry.scriptId;
const text = this._scriptSources.get(entry.scriptId);
if (text === undefined || url === undefined)
continue;
const flattenRanges = [];
for (const func of entry.functions)
flattenRanges.push(...func.ranges);
const ranges = convertToDisjointRanges(flattenRanges);
coverage.push({ url, ranges, text });
}
return coverage;
}
}
class CSSCoverage {
constructor(client) {
this._enabled = false;
this._stylesheetURLs = new Map();
this._stylesheetSources = new Map();
this._eventListeners = [];
this._resetOnNavigation = false;
this._reportAnonymousScripts = false;
this._client = client;
}
async start(options = {}) {
assert_js_1.assert(!this._enabled, 'CSSCoverage is already enabled');
const { resetOnNavigation = true } = options;
this._resetOnNavigation = resetOnNavigation;
this._enabled = true;
this._stylesheetURLs.clear();
this._stylesheetSources.clear();
this._eventListeners = [
helper_js_1.helper.addEventListener(this._client, 'CSS.styleSheetAdded', this._onStyleSheet.bind(this)),
helper_js_1.helper.addEventListener(this._client, 'Runtime.executionContextsCleared', this._onExecutionContextsCleared.bind(this)),
];
await Promise.all([
this._client.send('DOM.enable'),
this._client.send('CSS.enable'),
this._client.send('CSS.startRuleUsageTracking'),
]);
}
_onExecutionContextsCleared() {
if (!this._resetOnNavigation)
return;
this._stylesheetURLs.clear();
this._stylesheetSources.clear();
}
async _onStyleSheet(event) {
const header = event.header;
// Ignore anonymous scripts
if (!header.sourceURL)
return;
try {
const response = await this._client.send('CSS.getStyleSheetText', {
styleSheetId: header.styleSheetId,
});
this._stylesheetURLs.set(header.styleSheetId, header.sourceURL);
this._stylesheetSources.set(header.styleSheetId, response.text);
}
catch (error) {
// This might happen if the page has already navigated away.
helper_js_1.debugError(error);
}
}
async stop() {
assert_js_1.assert(this._enabled, 'CSSCoverage is not enabled');
this._enabled = false;
const ruleTrackingResponse = await this._client.send('CSS.stopRuleUsageTracking');
await Promise.all([
this._client.send('CSS.disable'),
this._client.send('DOM.disable'),
]);
helper_js_1.helper.removeEventListeners(this._eventListeners);
// aggregate by styleSheetId
const styleSheetIdToCoverage = new Map();
for (const entry of ruleTrackingResponse.ruleUsage) {
let ranges = styleSheetIdToCoverage.get(entry.styleSheetId);
if (!ranges) {
ranges = [];
styleSheetIdToCoverage.set(entry.styleSheetId, ranges);
}
ranges.push({
startOffset: entry.startOffset,
endOffset: entry.endOffset,
count: entry.used ? 1 : 0,
});
}
const coverage = [];
for (const styleSheetId of this._stylesheetURLs.keys()) {
const url = this._stylesheetURLs.get(styleSheetId);
const text = this._stylesheetSources.get(styleSheetId);
const ranges = convertToDisjointRanges(styleSheetIdToCoverage.get(styleSheetId) || []);
coverage.push({ url, ranges, text });
}
return coverage;
}
}
function convertToDisjointRanges(nestedRanges) {
const points = [];
for (const range of nestedRanges) {
points.push({ offset: range.startOffset, type: 0, range });
points.push({ offset: range.endOffset, type: 1, range });
}
// Sort points to form a valid parenthesis sequence.
points.sort((a, b) => {
// Sort with increasing offsets.
if (a.offset !== b.offset)
return a.offset - b.offset;
// All "end" points should go before "start" points.
if (a.type !== b.type)
return b.type - a.type;
const aLength = a.range.endOffset - a.range.startOffset;
const bLength = b.range.endOffset - b.range.startOffset;
// For two "start" points, the one with longer range goes first.
if (a.type === 0)
return bLength - aLength;
// For two "end" points, the one with shorter range goes first.
return aLength - bLength;
});
const hitCountStack = [];
const results = [];
let lastOffset = 0;
// Run scanning line to intersect all ranges.
for (const point of points) {
if (hitCountStack.length &&
lastOffset < point.offset &&
hitCountStack[hitCountStack.length - 1] > 0) {
const lastResult = results.length ? results[results.length - 1] : null;
if (lastResult && lastResult.end === lastOffset)
lastResult.end = point.offset;
else
results.push({ start: lastOffset, end: point.offset });
}
lastOffset = point.offset;
if (point.type === 0)
hitCountStack.push(point.range.count);
else
hitCountStack.pop();
}
// Filter out empty ranges.
return results.filter((range) => range.end - range.start > 1);
}
+173
View File
@@ -0,0 +1,173 @@
/**
* 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.
*/
/// <reference types="node" />
import { PuppeteerLifeCycleEvent } from './LifecycleWatcher.js';
import { JSHandle, ElementHandle } from './JSHandle.js';
import { ExecutionContext } from './ExecutionContext.js';
import { TimeoutSettings } from './TimeoutSettings.js';
import { MouseButton } from './Input.js';
import { FrameManager, Frame } from './FrameManager.js';
import { SerializableOrJSHandle, EvaluateHandleFn, WrapElementHandle, EvaluateFn, EvaluateFnReturnType, UnwrapPromiseLike } from './EvalTypes.js';
/**
* @public
*/
export interface WaitForSelectorOptions {
visible?: boolean;
hidden?: boolean;
timeout?: number;
}
/**
* @internal
*/
export interface PageBinding {
name: string;
pptrFunction: Function;
}
/**
* @internal
*/
export declare class DOMWorld {
private _frameManager;
private _frame;
private _timeoutSettings;
private _documentPromise?;
private _contextPromise?;
private _contextResolveCallback?;
private _detached;
/**
* @internal
*/
_waitTasks: Set<WaitTask>;
/**
* @internal
* Contains mapping from functions that should be bound to Puppeteer functions.
*/
_boundFunctions: Map<string, Function>;
private _ctxBindings;
private static bindingIdentifier;
constructor(frameManager: FrameManager, frame: Frame, timeoutSettings: TimeoutSettings);
frame(): Frame;
_setContext(context?: ExecutionContext): Promise<void>;
_hasContext(): boolean;
_detach(): void;
executionContext(): Promise<ExecutionContext>;
evaluateHandle<HandlerType extends JSHandle = JSHandle>(pageFunction: EvaluateHandleFn, ...args: SerializableOrJSHandle[]): Promise<HandlerType>;
evaluate<T extends EvaluateFn>(pageFunction: T, ...args: SerializableOrJSHandle[]): Promise<UnwrapPromiseLike<EvaluateFnReturnType<T>>>;
$(selector: string): Promise<ElementHandle | null>;
_document(): Promise<ElementHandle>;
$x(expression: string): Promise<ElementHandle[]>;
$eval<ReturnType>(selector: string, pageFunction: (element: Element, ...args: unknown[]) => ReturnType | Promise<ReturnType>, ...args: SerializableOrJSHandle[]): Promise<WrapElementHandle<ReturnType>>;
$$eval<ReturnType>(selector: string, pageFunction: (elements: Element[], ...args: unknown[]) => ReturnType | Promise<ReturnType>, ...args: SerializableOrJSHandle[]): Promise<WrapElementHandle<ReturnType>>;
$$(selector: string): Promise<ElementHandle[]>;
content(): Promise<string>;
setContent(html: string, options?: {
timeout?: number;
waitUntil?: PuppeteerLifeCycleEvent | PuppeteerLifeCycleEvent[];
}): Promise<void>;
/**
* Adds a script tag into the current context.
*
* @remarks
*
* You can pass a URL, filepath or string of contents. Note that when running Puppeteer
* in a browser environment you cannot pass a filepath and should use either
* `url` or `content`.
*/
addScriptTag(options: {
url?: string;
path?: string;
content?: string;
type?: string;
}): Promise<ElementHandle>;
/**
* Adds a style tag into the current context.
*
* @remarks
*
* You can pass a URL, filepath or string of contents. Note that when running Puppeteer
* in a browser environment you cannot pass a filepath and should use either
* `url` or `content`.
*
*/
addStyleTag(options: {
url?: string;
path?: string;
content?: string;
}): Promise<ElementHandle>;
click(selector: string, options: {
delay?: number;
button?: MouseButton;
clickCount?: number;
}): Promise<void>;
focus(selector: string): Promise<void>;
hover(selector: string): Promise<void>;
select(selector: string, ...values: string[]): Promise<string[]>;
tap(selector: string): Promise<void>;
type(selector: string, text: string, options?: {
delay: number;
}): Promise<void>;
waitForSelector(selector: string, options: WaitForSelectorOptions): Promise<ElementHandle | null>;
private _settingUpBinding;
/**
* @internal
*/
addBindingToContext(context: ExecutionContext, name: string): Promise<void>;
private _onBindingCalled;
/**
* @internal
*/
waitForSelectorInPage(queryOne: Function, selector: string, options: WaitForSelectorOptions, binding?: PageBinding): Promise<ElementHandle | null>;
waitForXPath(xpath: string, options: WaitForSelectorOptions): Promise<ElementHandle | null>;
waitForFunction(pageFunction: Function | string, options?: {
polling?: string | number;
timeout?: number;
}, ...args: SerializableOrJSHandle[]): Promise<JSHandle>;
title(): Promise<string>;
}
/**
* @internal
*/
export interface WaitTaskOptions {
domWorld: DOMWorld;
predicateBody: Function | string;
title: string;
polling: string | number;
timeout: number;
binding?: PageBinding;
args: SerializableOrJSHandle[];
}
/**
* @internal
*/
export declare class WaitTask {
_domWorld: DOMWorld;
_polling: string | number;
_timeout: number;
_predicateBody: string;
_args: SerializableOrJSHandle[];
_binding: PageBinding;
_runCount: number;
promise: Promise<JSHandle>;
_resolve: (x: JSHandle) => void;
_reject: (x: Error) => void;
_timeoutTimer?: NodeJS.Timeout;
_terminated: boolean;
constructor(options: WaitTaskOptions);
terminate(error: Error): void;
rerun(): Promise<void>;
_cleanup(): void;
}
//# sourceMappingURL=DOMWorld.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"DOMWorld.d.ts","sourceRoot":"","sources":["../../../../src/common/DOMWorld.ts"],"names":[],"mappings":"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"}
+650
View File
@@ -0,0 +1,650 @@
"use strict";
/**
* 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.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.WaitTask = exports.DOMWorld = void 0;
const assert_js_1 = require("./assert.js");
const helper_js_1 = require("./helper.js");
const LifecycleWatcher_js_1 = require("./LifecycleWatcher.js");
const Errors_js_1 = require("./Errors.js");
const QueryHandler_js_1 = require("./QueryHandler.js");
const environment_js_1 = require("../environment.js");
/**
* @internal
*/
class DOMWorld {
constructor(frameManager, frame, timeoutSettings) {
this._documentPromise = null;
this._contextPromise = null;
this._contextResolveCallback = null;
this._detached = false;
/**
* @internal
*/
this._waitTasks = new Set();
/**
* @internal
* Contains mapping from functions that should be bound to Puppeteer functions.
*/
this._boundFunctions = new Map();
// Set of bindings that have been registered in the current context.
this._ctxBindings = new Set();
// If multiple waitFor are set up asynchronously, we need to wait for the
// first one to set up the binding in the page before running the others.
this._settingUpBinding = null;
this._frameManager = frameManager;
this._frame = frame;
this._timeoutSettings = timeoutSettings;
this._setContext(null);
frameManager._client.on('Runtime.bindingCalled', (event) => this._onBindingCalled(event));
}
frame() {
return this._frame;
}
async _setContext(context) {
if (context) {
this._contextResolveCallback.call(null, context);
this._contextResolveCallback = null;
for (const waitTask of this._waitTasks)
waitTask.rerun();
}
else {
this._documentPromise = null;
this._contextPromise = new Promise((fulfill) => {
this._contextResolveCallback = fulfill;
});
}
}
_hasContext() {
return !this._contextResolveCallback;
}
_detach() {
this._detached = true;
for (const waitTask of this._waitTasks)
waitTask.terminate(new Error('waitForFunction failed: frame got detached.'));
}
executionContext() {
if (this._detached)
throw new Error(`Execution context is not available in detached frame "${this._frame.url()}" (are you trying to evaluate?)`);
return this._contextPromise;
}
async evaluateHandle(pageFunction, ...args) {
const context = await this.executionContext();
return context.evaluateHandle(pageFunction, ...args);
}
async evaluate(pageFunction, ...args) {
const context = await this.executionContext();
return context.evaluate(pageFunction, ...args);
}
async $(selector) {
const document = await this._document();
const value = await document.$(selector);
return value;
}
async _document() {
if (this._documentPromise)
return this._documentPromise;
this._documentPromise = this.executionContext().then(async (context) => {
const document = await context.evaluateHandle('document');
return document.asElement();
});
return this._documentPromise;
}
async $x(expression) {
const document = await this._document();
const value = await document.$x(expression);
return value;
}
async $eval(selector, pageFunction, ...args) {
const document = await this._document();
return document.$eval(selector, pageFunction, ...args);
}
async $$eval(selector, pageFunction, ...args) {
const document = await this._document();
const value = await document.$$eval(selector, pageFunction, ...args);
return value;
}
async $$(selector) {
const document = await this._document();
const value = await document.$$(selector);
return value;
}
async content() {
return await this.evaluate(() => {
let retVal = '';
if (document.doctype)
retVal = new XMLSerializer().serializeToString(document.doctype);
if (document.documentElement)
retVal += document.documentElement.outerHTML;
return retVal;
});
}
async setContent(html, options = {}) {
const { waitUntil = ['load'], timeout = this._timeoutSettings.navigationTimeout(), } = options;
// We rely upon the fact that document.open() will reset frame lifecycle with "init"
// lifecycle event. @see https://crrev.com/608658
await this.evaluate((html) => {
document.open();
document.write(html);
document.close();
}, html);
const watcher = new LifecycleWatcher_js_1.LifecycleWatcher(this._frameManager, this._frame, waitUntil, timeout);
const error = await Promise.race([
watcher.timeoutOrTerminationPromise(),
watcher.lifecyclePromise(),
]);
watcher.dispose();
if (error)
throw error;
}
/**
* Adds a script tag into the current context.
*
* @remarks
*
* You can pass a URL, filepath or string of contents. Note that when running Puppeteer
* in a browser environment you cannot pass a filepath and should use either
* `url` or `content`.
*/
async addScriptTag(options) {
const { url = null, path = null, content = null, type = '' } = options;
if (url !== null) {
try {
const context = await this.executionContext();
return (await context.evaluateHandle(addScriptUrl, url, type)).asElement();
}
catch (error) {
throw new Error(`Loading script from ${url} failed`);
}
}
if (path !== null) {
if (!environment_js_1.isNode) {
throw new Error('Cannot pass a filepath to addScriptTag in the browser environment.');
}
const fs = await helper_js_1.helper.importFSModule();
let contents = await fs.promises.readFile(path, 'utf8');
contents += '//# sourceURL=' + path.replace(/\n/g, '');
const context = await this.executionContext();
return (await context.evaluateHandle(addScriptContent, contents, type)).asElement();
}
if (content !== null) {
const context = await this.executionContext();
return (await context.evaluateHandle(addScriptContent, content, type)).asElement();
}
throw new Error('Provide an object with a `url`, `path` or `content` property');
async function addScriptUrl(url, type) {
const script = document.createElement('script');
script.src = url;
if (type)
script.type = type;
const promise = new Promise((res, rej) => {
script.onload = res;
script.onerror = rej;
});
document.head.appendChild(script);
await promise;
return script;
}
function addScriptContent(content, type = 'text/javascript') {
const script = document.createElement('script');
script.type = type;
script.text = content;
let error = null;
script.onerror = (e) => (error = e);
document.head.appendChild(script);
if (error)
throw error;
return script;
}
}
/**
* Adds a style tag into the current context.
*
* @remarks
*
* You can pass a URL, filepath or string of contents. Note that when running Puppeteer
* in a browser environment you cannot pass a filepath and should use either
* `url` or `content`.
*
*/
async addStyleTag(options) {
const { url = null, path = null, content = null } = options;
if (url !== null) {
try {
const context = await this.executionContext();
return (await context.evaluateHandle(addStyleUrl, url)).asElement();
}
catch (error) {
throw new Error(`Loading style from ${url} failed`);
}
}
if (path !== null) {
if (!environment_js_1.isNode) {
throw new Error('Cannot pass a filepath to addStyleTag in the browser environment.');
}
const fs = await helper_js_1.helper.importFSModule();
let contents = await fs.promises.readFile(path, 'utf8');
contents += '/*# sourceURL=' + path.replace(/\n/g, '') + '*/';
const context = await this.executionContext();
return (await context.evaluateHandle(addStyleContent, contents)).asElement();
}
if (content !== null) {
const context = await this.executionContext();
return (await context.evaluateHandle(addStyleContent, content)).asElement();
}
throw new Error('Provide an object with a `url`, `path` or `content` property');
async function addStyleUrl(url) {
const link = document.createElement('link');
link.rel = 'stylesheet';
link.href = url;
const promise = new Promise((res, rej) => {
link.onload = res;
link.onerror = rej;
});
document.head.appendChild(link);
await promise;
return link;
}
async function addStyleContent(content) {
const style = document.createElement('style');
style.type = 'text/css';
style.appendChild(document.createTextNode(content));
const promise = new Promise((res, rej) => {
style.onload = res;
style.onerror = rej;
});
document.head.appendChild(style);
await promise;
return style;
}
}
async click(selector, options) {
const handle = await this.$(selector);
assert_js_1.assert(handle, 'No node found for selector: ' + selector);
await handle.click(options);
await handle.dispose();
}
async focus(selector) {
const handle = await this.$(selector);
assert_js_1.assert(handle, 'No node found for selector: ' + selector);
await handle.focus();
await handle.dispose();
}
async hover(selector) {
const handle = await this.$(selector);
assert_js_1.assert(handle, 'No node found for selector: ' + selector);
await handle.hover();
await handle.dispose();
}
async select(selector, ...values) {
const handle = await this.$(selector);
assert_js_1.assert(handle, 'No node found for selector: ' + selector);
const result = await handle.select(...values);
await handle.dispose();
return result;
}
async tap(selector) {
const handle = await this.$(selector);
await handle.tap();
await handle.dispose();
}
async type(selector, text, options) {
const handle = await this.$(selector);
assert_js_1.assert(handle, 'No node found for selector: ' + selector);
await handle.type(text, options);
await handle.dispose();
}
async waitForSelector(selector, options) {
const { updatedSelector, queryHandler } = QueryHandler_js_1.getQueryHandlerAndSelector(selector);
return queryHandler.waitFor(this, updatedSelector, options);
}
/**
* @internal
*/
async addBindingToContext(context, name) {
// Previous operation added the binding so we are done.
if (this._ctxBindings.has(DOMWorld.bindingIdentifier(name, context._contextId))) {
return;
}
// Wait for other operation to finish
if (this._settingUpBinding) {
await this._settingUpBinding;
return this.addBindingToContext(context, name);
}
const bind = async (name) => {
const expression = helper_js_1.helper.pageBindingInitString('internal', name);
try {
await context._client.send('Runtime.addBinding', {
name,
executionContextId: context._contextId,
});
await context.evaluate(expression);
}
catch (error) {
// We could have tried to evaluate in a context which was already
// destroyed. This happens, for example, if the page is navigated while
// we are trying to add the binding
const ctxDestroyed = error.message.includes('Execution context was destroyed');
const ctxNotFound = error.message.includes('Cannot find context with specified id');
if (ctxDestroyed || ctxNotFound) {
return;
}
else {
helper_js_1.debugError(error);
return;
}
}
this._ctxBindings.add(DOMWorld.bindingIdentifier(name, context._contextId));
};
this._settingUpBinding = bind(name);
await this._settingUpBinding;
this._settingUpBinding = null;
}
async _onBindingCalled(event) {
let payload;
if (!this._hasContext())
return;
const context = await this.executionContext();
try {
payload = JSON.parse(event.payload);
}
catch {
// The binding was either called by something in the page or it was
// called before our wrapper was initialized.
return;
}
const { type, name, seq, args } = payload;
if (type !== 'internal' ||
!this._ctxBindings.has(DOMWorld.bindingIdentifier(name, context._contextId)))
return;
if (context._contextId !== event.executionContextId)
return;
try {
const result = await this._boundFunctions.get(name)(...args);
await context.evaluate(deliverResult, name, seq, result);
}
catch (error) {
// The WaitTask may already have been resolved by timing out, or the
// exection context may have been destroyed.
// In both caes, the promises above are rejected with a protocol error.
// We can safely ignores these, as the WaitTask is re-installed in
// the next execution context if needed.
if (error.message.includes('Protocol error'))
return;
helper_js_1.debugError(error);
}
function deliverResult(name, seq, result) {
globalThis[name].callbacks.get(seq).resolve(result);
globalThis[name].callbacks.delete(seq);
}
}
/**
* @internal
*/
async waitForSelectorInPage(queryOne, selector, options, binding) {
const { visible: waitForVisible = false, hidden: waitForHidden = false, timeout = this._timeoutSettings.timeout(), } = options;
const polling = waitForVisible || waitForHidden ? 'raf' : 'mutation';
const title = `selector \`${selector}\`${waitForHidden ? ' to be hidden' : ''}`;
async function predicate(selector, waitForVisible, waitForHidden) {
const node = predicateQueryHandler
? (await predicateQueryHandler(document, selector))
: document.querySelector(selector);
return checkWaitForOptions(node, waitForVisible, waitForHidden);
}
const waitTaskOptions = {
domWorld: this,
predicateBody: helper_js_1.helper.makePredicateString(predicate, queryOne),
title,
polling,
timeout,
args: [selector, waitForVisible, waitForHidden],
binding,
};
const waitTask = new WaitTask(waitTaskOptions);
const jsHandle = await waitTask.promise;
const elementHandle = jsHandle.asElement();
if (!elementHandle) {
await jsHandle.dispose();
return null;
}
return elementHandle;
}
async waitForXPath(xpath, options) {
const { visible: waitForVisible = false, hidden: waitForHidden = false, timeout = this._timeoutSettings.timeout(), } = options;
const polling = waitForVisible || waitForHidden ? 'raf' : 'mutation';
const title = `XPath \`${xpath}\`${waitForHidden ? ' to be hidden' : ''}`;
function predicate(xpath, waitForVisible, waitForHidden) {
const node = document.evaluate(xpath, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
return checkWaitForOptions(node, waitForVisible, waitForHidden);
}
const waitTaskOptions = {
domWorld: this,
predicateBody: helper_js_1.helper.makePredicateString(predicate),
title,
polling,
timeout,
args: [xpath, waitForVisible, waitForHidden],
};
const waitTask = new WaitTask(waitTaskOptions);
const jsHandle = await waitTask.promise;
const elementHandle = jsHandle.asElement();
if (!elementHandle) {
await jsHandle.dispose();
return null;
}
return elementHandle;
}
waitForFunction(pageFunction, options = {}, ...args) {
const { polling = 'raf', timeout = this._timeoutSettings.timeout(), } = options;
const waitTaskOptions = {
domWorld: this,
predicateBody: pageFunction,
title: 'function',
polling,
timeout,
args,
};
const waitTask = new WaitTask(waitTaskOptions);
return waitTask.promise;
}
async title() {
return this.evaluate(() => document.title);
}
}
exports.DOMWorld = DOMWorld;
DOMWorld.bindingIdentifier = (name, contextId) => `${name}_${contextId}`;
/**
* @internal
*/
class WaitTask {
constructor(options) {
this._runCount = 0;
this._terminated = false;
if (helper_js_1.helper.isString(options.polling))
assert_js_1.assert(options.polling === 'raf' || options.polling === 'mutation', 'Unknown polling option: ' + options.polling);
else if (helper_js_1.helper.isNumber(options.polling))
assert_js_1.assert(options.polling > 0, 'Cannot poll with non-positive interval: ' + options.polling);
else
throw new Error('Unknown polling options: ' + options.polling);
function getPredicateBody(predicateBody) {
if (helper_js_1.helper.isString(predicateBody))
return `return (${predicateBody});`;
return `return (${predicateBody})(...args);`;
}
this._domWorld = options.domWorld;
this._polling = options.polling;
this._timeout = options.timeout;
this._predicateBody = getPredicateBody(options.predicateBody);
this._args = options.args;
this._binding = options.binding;
this._runCount = 0;
this._domWorld._waitTasks.add(this);
if (this._binding) {
this._domWorld._boundFunctions.set(this._binding.name, this._binding.pptrFunction);
}
this.promise = new Promise((resolve, reject) => {
this._resolve = resolve;
this._reject = reject;
});
// Since page navigation requires us to re-install the pageScript, we should track
// timeout on our end.
if (options.timeout) {
const timeoutError = new Errors_js_1.TimeoutError(`waiting for ${options.title} failed: timeout ${options.timeout}ms exceeded`);
this._timeoutTimer = setTimeout(() => this.terminate(timeoutError), options.timeout);
}
this.rerun();
}
terminate(error) {
this._terminated = true;
this._reject(error);
this._cleanup();
}
async rerun() {
const runCount = ++this._runCount;
let success = null;
let error = null;
const context = await this._domWorld.executionContext();
if (this._terminated || runCount !== this._runCount)
return;
if (this._binding) {
await this._domWorld.addBindingToContext(context, this._binding.name);
}
if (this._terminated || runCount !== this._runCount)
return;
try {
success = await context.evaluateHandle(waitForPredicatePageFunction, this._predicateBody, this._polling, this._timeout, ...this._args);
}
catch (error_) {
error = error_;
}
if (this._terminated || runCount !== this._runCount) {
if (success)
await success.dispose();
return;
}
// Ignore timeouts in pageScript - we track timeouts ourselves.
// If the frame's execution context has already changed, `frame.evaluate` will
// throw an error - ignore this predicate run altogether.
if (!error &&
(await this._domWorld.evaluate((s) => !s, success).catch(() => true))) {
await success.dispose();
return;
}
if (error) {
if (error.message.includes('TypeError: binding is not a function')) {
return this.rerun();
}
// When frame is detached the task should have been terminated by the DOMWorld.
// This can fail if we were adding this task while the frame was detached,
// so we terminate here instead.
if (error.message.includes('Execution context is not available in detached frame')) {
this.terminate(new Error('waitForFunction failed: frame got detached.'));
return;
}
// When the page is navigated, the promise is rejected.
// We will try again in the new execution context.
if (error.message.includes('Execution context was destroyed'))
return;
// We could have tried to evaluate in a context which was already
// destroyed.
if (error.message.includes('Cannot find context with specified id'))
return;
this._reject(error);
}
else {
this._resolve(success);
}
this._cleanup();
}
_cleanup() {
clearTimeout(this._timeoutTimer);
this._domWorld._waitTasks.delete(this);
}
}
exports.WaitTask = WaitTask;
async function waitForPredicatePageFunction(predicateBody, polling, timeout, ...args) {
const predicate = new Function('...args', predicateBody);
let timedOut = false;
if (timeout)
setTimeout(() => (timedOut = true), timeout);
if (polling === 'raf')
return await pollRaf();
if (polling === 'mutation')
return await pollMutation();
if (typeof polling === 'number')
return await pollInterval(polling);
/**
* @returns {!Promise<*>}
*/
async function pollMutation() {
const success = await predicate(...args);
if (success)
return Promise.resolve(success);
let fulfill;
const result = new Promise((x) => (fulfill = x));
const observer = new MutationObserver(async () => {
if (timedOut) {
observer.disconnect();
fulfill();
}
const success = await predicate(...args);
if (success) {
observer.disconnect();
fulfill(success);
}
});
observer.observe(document, {
childList: true,
subtree: true,
attributes: true,
});
return result;
}
async function pollRaf() {
let fulfill;
const result = new Promise((x) => (fulfill = x));
await onRaf();
return result;
async function onRaf() {
if (timedOut) {
fulfill();
return;
}
const success = await predicate(...args);
if (success)
fulfill(success);
else
requestAnimationFrame(onRaf);
}
}
async function pollInterval(pollInterval) {
let fulfill;
const result = new Promise((x) => (fulfill = x));
await onTimeout();
return result;
async function onTimeout() {
if (timedOut) {
fulfill();
return;
}
const success = await predicate(...args);
if (success)
fulfill(success);
else
setTimeout(onTimeout, pollInterval);
}
}
}
+53
View File
@@ -0,0 +1,53 @@
/**
* Copyright 2020 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.
*/
/**
* A debug function that can be used in any environment.
*
* @remarks
*
* If used in Node, it falls back to the
* {@link https://www.npmjs.com/package/debug | debug module}. In the browser it
* uses `console.log`.
*
* @param prefix - this will be prefixed to each log.
* @returns a function that can be called to log to that debug channel.
*
* In Node, use the `DEBUG` environment variable to control logging:
*
* ```
* DEBUG=* // logs all channels
* DEBUG=foo // logs the `foo` channel
* DEBUG=foo* // logs any channels starting with `foo`
* ```
*
* In the browser, set `window.__PUPPETEER_DEBUG` to a string:
*
* ```
* window.__PUPPETEER_DEBUG='*'; // logs all channels
* window.__PUPPETEER_DEBUG='foo'; // logs the `foo` channel
* window.__PUPPETEER_DEBUG='foo*'; // logs any channels starting with `foo`
* ```
*
* @example
* ```
* const log = debug('Page');
*
* log('new page created')
* // logs "Page: new page created"
* ```
*/
export declare const debug: (prefix: string) => (...args: unknown[]) => void;
//# sourceMappingURL=Debug.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"Debug.d.ts","sourceRoot":"","sources":["../../../../src/common/Debug.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAIH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,eAAO,MAAM,KAAK,WAAY,MAAM,eAAc,OAAO,EAAE,KAAK,IA4B/D,CAAC"}
+80
View File
@@ -0,0 +1,80 @@
"use strict";
/**
* Copyright 2020 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.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.debug = void 0;
const environment_js_1 = require("../environment.js");
/**
* A debug function that can be used in any environment.
*
* @remarks
*
* If used in Node, it falls back to the
* {@link https://www.npmjs.com/package/debug | debug module}. In the browser it
* uses `console.log`.
*
* @param prefix - this will be prefixed to each log.
* @returns a function that can be called to log to that debug channel.
*
* In Node, use the `DEBUG` environment variable to control logging:
*
* ```
* DEBUG=* // logs all channels
* DEBUG=foo // logs the `foo` channel
* DEBUG=foo* // logs any channels starting with `foo`
* ```
*
* In the browser, set `window.__PUPPETEER_DEBUG` to a string:
*
* ```
* window.__PUPPETEER_DEBUG='*'; // logs all channels
* window.__PUPPETEER_DEBUG='foo'; // logs the `foo` channel
* window.__PUPPETEER_DEBUG='foo*'; // logs any channels starting with `foo`
* ```
*
* @example
* ```
* const log = debug('Page');
*
* log('new page created')
* // logs "Page: new page created"
* ```
*/
exports.debug = (prefix) => {
if (environment_js_1.isNode) {
// eslint-disable-next-line @typescript-eslint/no-var-requires
return require('debug')(prefix);
}
return (...logArgs) => {
const debugLevel = globalThis.__PUPPETEER_DEBUG;
if (!debugLevel)
return;
const everythingShouldBeLogged = debugLevel === '*';
const prefixMatchesDebugLevel = everythingShouldBeLogged ||
/**
* If the debug level is `foo*`, that means we match any prefix that
* starts with `foo`. If the level is `foo`, we match only the prefix
* `foo`.
*/
(debugLevel.endsWith('*')
? prefix.startsWith(debugLevel)
: prefix === debugLevel);
if (!prefixMatchesDebugLevel)
return;
// eslint-disable-next-line no-console
console.log(`${prefix}:`, ...logArgs);
};
};
@@ -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.
*/
interface Device {
name: string;
userAgent: string;
viewport: {
width: number;
height: number;
deviceScaleFactor: number;
isMobile: boolean;
hasTouch: boolean;
isLandscape: boolean;
};
}
export declare type DevicesMap = {
[name: string]: Device;
};
declare const devicesMap: DevicesMap;
export { devicesMap };
//# sourceMappingURL=DeviceDescriptors.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"DeviceDescriptors.d.ts","sourceRoot":"","sources":["../../../../src/common/DeviceDescriptors.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,UAAU,MAAM;IACd,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE;QACR,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,MAAM,CAAC;QACf,iBAAiB,EAAE,MAAM,CAAC;QAC1B,QAAQ,EAAE,OAAO,CAAC;QAClB,QAAQ,EAAE,OAAO,CAAC;QAClB,WAAW,EAAE,OAAO,CAAC;KACtB,CAAC;CACH;AAg6BD,oBAAY,UAAU,GAAG;IACvB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;CACxB,CAAC;AAEF,QAAA,MAAM,UAAU,EAAE,UAAe,CAAC;AAIlC,OAAO,EAAE,UAAU,EAAE,CAAC"}
@@ -0,0 +1,876 @@
"use strict";
/**
* 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.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.devicesMap = void 0;
const devices = [
{
name: 'Blackberry PlayBook',
userAgent: 'Mozilla/5.0 (PlayBook; U; RIM Tablet OS 2.1.0; en-US) AppleWebKit/536.2+ (KHTML like Gecko) Version/7.2.1.0 Safari/536.2+',
viewport: {
width: 600,
height: 1024,
deviceScaleFactor: 1,
isMobile: true,
hasTouch: true,
isLandscape: false,
},
},
{
name: 'Blackberry PlayBook landscape',
userAgent: 'Mozilla/5.0 (PlayBook; U; RIM Tablet OS 2.1.0; en-US) AppleWebKit/536.2+ (KHTML like Gecko) Version/7.2.1.0 Safari/536.2+',
viewport: {
width: 1024,
height: 600,
deviceScaleFactor: 1,
isMobile: true,
hasTouch: true,
isLandscape: true,
},
},
{
name: 'BlackBerry Z30',
userAgent: 'Mozilla/5.0 (BB10; Touch) AppleWebKit/537.10+ (KHTML, like Gecko) Version/10.0.9.2372 Mobile Safari/537.10+',
viewport: {
width: 360,
height: 640,
deviceScaleFactor: 2,
isMobile: true,
hasTouch: true,
isLandscape: false,
},
},
{
name: 'BlackBerry Z30 landscape',
userAgent: 'Mozilla/5.0 (BB10; Touch) AppleWebKit/537.10+ (KHTML, like Gecko) Version/10.0.9.2372 Mobile Safari/537.10+',
viewport: {
width: 640,
height: 360,
deviceScaleFactor: 2,
isMobile: true,
hasTouch: true,
isLandscape: true,
},
},
{
name: 'Galaxy Note 3',
userAgent: 'Mozilla/5.0 (Linux; U; Android 4.3; en-us; SM-N900T Build/JSS15J) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30',
viewport: {
width: 360,
height: 640,
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true,
isLandscape: false,
},
},
{
name: 'Galaxy Note 3 landscape',
userAgent: 'Mozilla/5.0 (Linux; U; Android 4.3; en-us; SM-N900T Build/JSS15J) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30',
viewport: {
width: 640,
height: 360,
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true,
isLandscape: true,
},
},
{
name: 'Galaxy Note II',
userAgent: 'Mozilla/5.0 (Linux; U; Android 4.1; en-us; GT-N7100 Build/JRO03C) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30',
viewport: {
width: 360,
height: 640,
deviceScaleFactor: 2,
isMobile: true,
hasTouch: true,
isLandscape: false,
},
},
{
name: 'Galaxy Note II landscape',
userAgent: 'Mozilla/5.0 (Linux; U; Android 4.1; en-us; GT-N7100 Build/JRO03C) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30',
viewport: {
width: 640,
height: 360,
deviceScaleFactor: 2,
isMobile: true,
hasTouch: true,
isLandscape: true,
},
},
{
name: 'Galaxy S III',
userAgent: 'Mozilla/5.0 (Linux; U; Android 4.0; en-us; GT-I9300 Build/IMM76D) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30',
viewport: {
width: 360,
height: 640,
deviceScaleFactor: 2,
isMobile: true,
hasTouch: true,
isLandscape: false,
},
},
{
name: 'Galaxy S III landscape',
userAgent: 'Mozilla/5.0 (Linux; U; Android 4.0; en-us; GT-I9300 Build/IMM76D) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30',
viewport: {
width: 640,
height: 360,
deviceScaleFactor: 2,
isMobile: true,
hasTouch: true,
isLandscape: true,
},
},
{
name: 'Galaxy S5',
userAgent: 'Mozilla/5.0 (Linux; Android 5.0; SM-G900P Build/LRX21T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3765.0 Mobile Safari/537.36',
viewport: {
width: 360,
height: 640,
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true,
isLandscape: false,
},
},
{
name: 'Galaxy S5 landscape',
userAgent: 'Mozilla/5.0 (Linux; Android 5.0; SM-G900P Build/LRX21T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3765.0 Mobile Safari/537.36',
viewport: {
width: 640,
height: 360,
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true,
isLandscape: true,
},
},
{
name: 'iPad',
userAgent: 'Mozilla/5.0 (iPad; CPU OS 11_0 like Mac OS X) AppleWebKit/604.1.34 (KHTML, like Gecko) Version/11.0 Mobile/15A5341f Safari/604.1',
viewport: {
width: 768,
height: 1024,
deviceScaleFactor: 2,
isMobile: true,
hasTouch: true,
isLandscape: false,
},
},
{
name: 'iPad landscape',
userAgent: 'Mozilla/5.0 (iPad; CPU OS 11_0 like Mac OS X) AppleWebKit/604.1.34 (KHTML, like Gecko) Version/11.0 Mobile/15A5341f Safari/604.1',
viewport: {
width: 1024,
height: 768,
deviceScaleFactor: 2,
isMobile: true,
hasTouch: true,
isLandscape: true,
},
},
{
name: 'iPad Mini',
userAgent: 'Mozilla/5.0 (iPad; CPU OS 11_0 like Mac OS X) AppleWebKit/604.1.34 (KHTML, like Gecko) Version/11.0 Mobile/15A5341f Safari/604.1',
viewport: {
width: 768,
height: 1024,
deviceScaleFactor: 2,
isMobile: true,
hasTouch: true,
isLandscape: false,
},
},
{
name: 'iPad Mini landscape',
userAgent: 'Mozilla/5.0 (iPad; CPU OS 11_0 like Mac OS X) AppleWebKit/604.1.34 (KHTML, like Gecko) Version/11.0 Mobile/15A5341f Safari/604.1',
viewport: {
width: 1024,
height: 768,
deviceScaleFactor: 2,
isMobile: true,
hasTouch: true,
isLandscape: true,
},
},
{
name: 'iPad Pro',
userAgent: 'Mozilla/5.0 (iPad; CPU OS 11_0 like Mac OS X) AppleWebKit/604.1.34 (KHTML, like Gecko) Version/11.0 Mobile/15A5341f Safari/604.1',
viewport: {
width: 1024,
height: 1366,
deviceScaleFactor: 2,
isMobile: true,
hasTouch: true,
isLandscape: false,
},
},
{
name: 'iPad Pro landscape',
userAgent: 'Mozilla/5.0 (iPad; CPU OS 11_0 like Mac OS X) AppleWebKit/604.1.34 (KHTML, like Gecko) Version/11.0 Mobile/15A5341f Safari/604.1',
viewport: {
width: 1366,
height: 1024,
deviceScaleFactor: 2,
isMobile: true,
hasTouch: true,
isLandscape: true,
},
},
{
name: 'iPhone 4',
userAgent: 'Mozilla/5.0 (iPhone; CPU iPhone OS 7_1_2 like Mac OS X) AppleWebKit/537.51.2 (KHTML, like Gecko) Version/7.0 Mobile/11D257 Safari/9537.53',
viewport: {
width: 320,
height: 480,
deviceScaleFactor: 2,
isMobile: true,
hasTouch: true,
isLandscape: false,
},
},
{
name: 'iPhone 4 landscape',
userAgent: 'Mozilla/5.0 (iPhone; CPU iPhone OS 7_1_2 like Mac OS X) AppleWebKit/537.51.2 (KHTML, like Gecko) Version/7.0 Mobile/11D257 Safari/9537.53',
viewport: {
width: 480,
height: 320,
deviceScaleFactor: 2,
isMobile: true,
hasTouch: true,
isLandscape: true,
},
},
{
name: 'iPhone 5',
userAgent: 'Mozilla/5.0 (iPhone; CPU iPhone OS 10_3_1 like Mac OS X) AppleWebKit/603.1.30 (KHTML, like Gecko) Version/10.0 Mobile/14E304 Safari/602.1',
viewport: {
width: 320,
height: 568,
deviceScaleFactor: 2,
isMobile: true,
hasTouch: true,
isLandscape: false,
},
},
{
name: 'iPhone 5 landscape',
userAgent: 'Mozilla/5.0 (iPhone; CPU iPhone OS 10_3_1 like Mac OS X) AppleWebKit/603.1.30 (KHTML, like Gecko) Version/10.0 Mobile/14E304 Safari/602.1',
viewport: {
width: 568,
height: 320,
deviceScaleFactor: 2,
isMobile: true,
hasTouch: true,
isLandscape: true,
},
},
{
name: 'iPhone 6',
userAgent: 'Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A372 Safari/604.1',
viewport: {
width: 375,
height: 667,
deviceScaleFactor: 2,
isMobile: true,
hasTouch: true,
isLandscape: false,
},
},
{
name: 'iPhone 6 landscape',
userAgent: 'Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A372 Safari/604.1',
viewport: {
width: 667,
height: 375,
deviceScaleFactor: 2,
isMobile: true,
hasTouch: true,
isLandscape: true,
},
},
{
name: 'iPhone 6 Plus',
userAgent: 'Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A372 Safari/604.1',
viewport: {
width: 414,
height: 736,
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true,
isLandscape: false,
},
},
{
name: 'iPhone 6 Plus landscape',
userAgent: 'Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A372 Safari/604.1',
viewport: {
width: 736,
height: 414,
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true,
isLandscape: true,
},
},
{
name: 'iPhone 7',
userAgent: 'Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A372 Safari/604.1',
viewport: {
width: 375,
height: 667,
deviceScaleFactor: 2,
isMobile: true,
hasTouch: true,
isLandscape: false,
},
},
{
name: 'iPhone 7 landscape',
userAgent: 'Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A372 Safari/604.1',
viewport: {
width: 667,
height: 375,
deviceScaleFactor: 2,
isMobile: true,
hasTouch: true,
isLandscape: true,
},
},
{
name: 'iPhone 7 Plus',
userAgent: 'Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A372 Safari/604.1',
viewport: {
width: 414,
height: 736,
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true,
isLandscape: false,
},
},
{
name: 'iPhone 7 Plus landscape',
userAgent: 'Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A372 Safari/604.1',
viewport: {
width: 736,
height: 414,
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true,
isLandscape: true,
},
},
{
name: 'iPhone 8',
userAgent: 'Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A372 Safari/604.1',
viewport: {
width: 375,
height: 667,
deviceScaleFactor: 2,
isMobile: true,
hasTouch: true,
isLandscape: false,
},
},
{
name: 'iPhone 8 landscape',
userAgent: 'Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A372 Safari/604.1',
viewport: {
width: 667,
height: 375,
deviceScaleFactor: 2,
isMobile: true,
hasTouch: true,
isLandscape: true,
},
},
{
name: 'iPhone 8 Plus',
userAgent: 'Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A372 Safari/604.1',
viewport: {
width: 414,
height: 736,
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true,
isLandscape: false,
},
},
{
name: 'iPhone 8 Plus landscape',
userAgent: 'Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A372 Safari/604.1',
viewport: {
width: 736,
height: 414,
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true,
isLandscape: true,
},
},
{
name: 'iPhone SE',
userAgent: 'Mozilla/5.0 (iPhone; CPU iPhone OS 10_3_1 like Mac OS X) AppleWebKit/603.1.30 (KHTML, like Gecko) Version/10.0 Mobile/14E304 Safari/602.1',
viewport: {
width: 320,
height: 568,
deviceScaleFactor: 2,
isMobile: true,
hasTouch: true,
isLandscape: false,
},
},
{
name: 'iPhone SE landscape',
userAgent: 'Mozilla/5.0 (iPhone; CPU iPhone OS 10_3_1 like Mac OS X) AppleWebKit/603.1.30 (KHTML, like Gecko) Version/10.0 Mobile/14E304 Safari/602.1',
viewport: {
width: 568,
height: 320,
deviceScaleFactor: 2,
isMobile: true,
hasTouch: true,
isLandscape: true,
},
},
{
name: 'iPhone X',
userAgent: 'Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A372 Safari/604.1',
viewport: {
width: 375,
height: 812,
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true,
isLandscape: false,
},
},
{
name: 'iPhone X landscape',
userAgent: 'Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A372 Safari/604.1',
viewport: {
width: 812,
height: 375,
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true,
isLandscape: true,
},
},
{
name: 'iPhone XR',
userAgent: 'Mozilla/5.0 (iPhone; CPU iPhone OS 12_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.0 Mobile/15E148 Safari/604.1',
viewport: {
width: 414,
height: 896,
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true,
isLandscape: false,
},
},
{
name: 'iPhone XR landscape',
userAgent: 'Mozilla/5.0 (iPhone; CPU iPhone OS 12_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.0 Mobile/15E148 Safari/604.1',
viewport: {
width: 896,
height: 414,
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true,
isLandscape: true,
},
},
{
name: 'JioPhone 2',
userAgent: 'Mozilla/5.0 (Mobile; LYF/F300B/LYF-F300B-001-01-15-130718-i;Android; rv:48.0) Gecko/48.0 Firefox/48.0 KAIOS/2.5',
viewport: {
width: 240,
height: 320,
deviceScaleFactor: 1,
isMobile: true,
hasTouch: true,
isLandscape: false,
},
},
{
name: 'JioPhone 2 landscape',
userAgent: 'Mozilla/5.0 (Mobile; LYF/F300B/LYF-F300B-001-01-15-130718-i;Android; rv:48.0) Gecko/48.0 Firefox/48.0 KAIOS/2.5',
viewport: {
width: 320,
height: 240,
deviceScaleFactor: 1,
isMobile: true,
hasTouch: true,
isLandscape: true,
},
},
{
name: 'Kindle Fire HDX',
userAgent: 'Mozilla/5.0 (Linux; U; en-us; KFAPWI Build/JDQ39) AppleWebKit/535.19 (KHTML, like Gecko) Silk/3.13 Safari/535.19 Silk-Accelerated=true',
viewport: {
width: 800,
height: 1280,
deviceScaleFactor: 2,
isMobile: true,
hasTouch: true,
isLandscape: false,
},
},
{
name: 'Kindle Fire HDX landscape',
userAgent: 'Mozilla/5.0 (Linux; U; en-us; KFAPWI Build/JDQ39) AppleWebKit/535.19 (KHTML, like Gecko) Silk/3.13 Safari/535.19 Silk-Accelerated=true',
viewport: {
width: 1280,
height: 800,
deviceScaleFactor: 2,
isMobile: true,
hasTouch: true,
isLandscape: true,
},
},
{
name: 'LG Optimus L70',
userAgent: 'Mozilla/5.0 (Linux; U; Android 4.4.2; en-us; LGMS323 Build/KOT49I.MS32310c) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/75.0.3765.0 Mobile Safari/537.36',
viewport: {
width: 384,
height: 640,
deviceScaleFactor: 1.25,
isMobile: true,
hasTouch: true,
isLandscape: false,
},
},
{
name: 'LG Optimus L70 landscape',
userAgent: 'Mozilla/5.0 (Linux; U; Android 4.4.2; en-us; LGMS323 Build/KOT49I.MS32310c) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/75.0.3765.0 Mobile Safari/537.36',
viewport: {
width: 640,
height: 384,
deviceScaleFactor: 1.25,
isMobile: true,
hasTouch: true,
isLandscape: true,
},
},
{
name: 'Microsoft Lumia 550',
userAgent: 'Mozilla/5.0 (Windows Phone 10.0; Android 4.2.1; Microsoft; Lumia 550) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2486.0 Mobile Safari/537.36 Edge/14.14263',
viewport: {
width: 640,
height: 360,
deviceScaleFactor: 2,
isMobile: true,
hasTouch: true,
isLandscape: false,
},
},
{
name: 'Microsoft Lumia 950',
userAgent: 'Mozilla/5.0 (Windows Phone 10.0; Android 4.2.1; Microsoft; Lumia 950) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2486.0 Mobile Safari/537.36 Edge/14.14263',
viewport: {
width: 360,
height: 640,
deviceScaleFactor: 4,
isMobile: true,
hasTouch: true,
isLandscape: false,
},
},
{
name: 'Microsoft Lumia 950 landscape',
userAgent: 'Mozilla/5.0 (Windows Phone 10.0; Android 4.2.1; Microsoft; Lumia 950) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2486.0 Mobile Safari/537.36 Edge/14.14263',
viewport: {
width: 640,
height: 360,
deviceScaleFactor: 4,
isMobile: true,
hasTouch: true,
isLandscape: true,
},
},
{
name: 'Nexus 10',
userAgent: 'Mozilla/5.0 (Linux; Android 6.0.1; Nexus 10 Build/MOB31T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3765.0 Safari/537.36',
viewport: {
width: 800,
height: 1280,
deviceScaleFactor: 2,
isMobile: true,
hasTouch: true,
isLandscape: false,
},
},
{
name: 'Nexus 10 landscape',
userAgent: 'Mozilla/5.0 (Linux; Android 6.0.1; Nexus 10 Build/MOB31T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3765.0 Safari/537.36',
viewport: {
width: 1280,
height: 800,
deviceScaleFactor: 2,
isMobile: true,
hasTouch: true,
isLandscape: true,
},
},
{
name: 'Nexus 4',
userAgent: 'Mozilla/5.0 (Linux; Android 4.4.2; Nexus 4 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3765.0 Mobile Safari/537.36',
viewport: {
width: 384,
height: 640,
deviceScaleFactor: 2,
isMobile: true,
hasTouch: true,
isLandscape: false,
},
},
{
name: 'Nexus 4 landscape',
userAgent: 'Mozilla/5.0 (Linux; Android 4.4.2; Nexus 4 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3765.0 Mobile Safari/537.36',
viewport: {
width: 640,
height: 384,
deviceScaleFactor: 2,
isMobile: true,
hasTouch: true,
isLandscape: true,
},
},
{
name: 'Nexus 5',
userAgent: 'Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3765.0 Mobile Safari/537.36',
viewport: {
width: 360,
height: 640,
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true,
isLandscape: false,
},
},
{
name: 'Nexus 5 landscape',
userAgent: 'Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3765.0 Mobile Safari/537.36',
viewport: {
width: 640,
height: 360,
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true,
isLandscape: true,
},
},
{
name: 'Nexus 5X',
userAgent: 'Mozilla/5.0 (Linux; Android 8.0.0; Nexus 5X Build/OPR4.170623.006) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3765.0 Mobile Safari/537.36',
viewport: {
width: 412,
height: 732,
deviceScaleFactor: 2.625,
isMobile: true,
hasTouch: true,
isLandscape: false,
},
},
{
name: 'Nexus 5X landscape',
userAgent: 'Mozilla/5.0 (Linux; Android 8.0.0; Nexus 5X Build/OPR4.170623.006) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3765.0 Mobile Safari/537.36',
viewport: {
width: 732,
height: 412,
deviceScaleFactor: 2.625,
isMobile: true,
hasTouch: true,
isLandscape: true,
},
},
{
name: 'Nexus 6',
userAgent: 'Mozilla/5.0 (Linux; Android 7.1.1; Nexus 6 Build/N6F26U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3765.0 Mobile Safari/537.36',
viewport: {
width: 412,
height: 732,
deviceScaleFactor: 3.5,
isMobile: true,
hasTouch: true,
isLandscape: false,
},
},
{
name: 'Nexus 6 landscape',
userAgent: 'Mozilla/5.0 (Linux; Android 7.1.1; Nexus 6 Build/N6F26U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3765.0 Mobile Safari/537.36',
viewport: {
width: 732,
height: 412,
deviceScaleFactor: 3.5,
isMobile: true,
hasTouch: true,
isLandscape: true,
},
},
{
name: 'Nexus 6P',
userAgent: 'Mozilla/5.0 (Linux; Android 8.0.0; Nexus 6P Build/OPP3.170518.006) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3765.0 Mobile Safari/537.36',
viewport: {
width: 412,
height: 732,
deviceScaleFactor: 3.5,
isMobile: true,
hasTouch: true,
isLandscape: false,
},
},
{
name: 'Nexus 6P landscape',
userAgent: 'Mozilla/5.0 (Linux; Android 8.0.0; Nexus 6P Build/OPP3.170518.006) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3765.0 Mobile Safari/537.36',
viewport: {
width: 732,
height: 412,
deviceScaleFactor: 3.5,
isMobile: true,
hasTouch: true,
isLandscape: true,
},
},
{
name: 'Nexus 7',
userAgent: 'Mozilla/5.0 (Linux; Android 6.0.1; Nexus 7 Build/MOB30X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3765.0 Safari/537.36',
viewport: {
width: 600,
height: 960,
deviceScaleFactor: 2,
isMobile: true,
hasTouch: true,
isLandscape: false,
},
},
{
name: 'Nexus 7 landscape',
userAgent: 'Mozilla/5.0 (Linux; Android 6.0.1; Nexus 7 Build/MOB30X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3765.0 Safari/537.36',
viewport: {
width: 960,
height: 600,
deviceScaleFactor: 2,
isMobile: true,
hasTouch: true,
isLandscape: true,
},
},
{
name: 'Nokia Lumia 520',
userAgent: 'Mozilla/5.0 (compatible; MSIE 10.0; Windows Phone 8.0; Trident/6.0; IEMobile/10.0; ARM; Touch; NOKIA; Lumia 520)',
viewport: {
width: 320,
height: 533,
deviceScaleFactor: 1.5,
isMobile: true,
hasTouch: true,
isLandscape: false,
},
},
{
name: 'Nokia Lumia 520 landscape',
userAgent: 'Mozilla/5.0 (compatible; MSIE 10.0; Windows Phone 8.0; Trident/6.0; IEMobile/10.0; ARM; Touch; NOKIA; Lumia 520)',
viewport: {
width: 533,
height: 320,
deviceScaleFactor: 1.5,
isMobile: true,
hasTouch: true,
isLandscape: true,
},
},
{
name: 'Nokia N9',
userAgent: 'Mozilla/5.0 (MeeGo; NokiaN9) AppleWebKit/534.13 (KHTML, like Gecko) NokiaBrowser/8.5.0 Mobile Safari/534.13',
viewport: {
width: 480,
height: 854,
deviceScaleFactor: 1,
isMobile: true,
hasTouch: true,
isLandscape: false,
},
},
{
name: 'Nokia N9 landscape',
userAgent: 'Mozilla/5.0 (MeeGo; NokiaN9) AppleWebKit/534.13 (KHTML, like Gecko) NokiaBrowser/8.5.0 Mobile Safari/534.13',
viewport: {
width: 854,
height: 480,
deviceScaleFactor: 1,
isMobile: true,
hasTouch: true,
isLandscape: true,
},
},
{
name: 'Pixel 2',
userAgent: 'Mozilla/5.0 (Linux; Android 8.0; Pixel 2 Build/OPD3.170816.012) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3765.0 Mobile Safari/537.36',
viewport: {
width: 411,
height: 731,
deviceScaleFactor: 2.625,
isMobile: true,
hasTouch: true,
isLandscape: false,
},
},
{
name: 'Pixel 2 landscape',
userAgent: 'Mozilla/5.0 (Linux; Android 8.0; Pixel 2 Build/OPD3.170816.012) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3765.0 Mobile Safari/537.36',
viewport: {
width: 731,
height: 411,
deviceScaleFactor: 2.625,
isMobile: true,
hasTouch: true,
isLandscape: true,
},
},
{
name: 'Pixel 2 XL',
userAgent: 'Mozilla/5.0 (Linux; Android 8.0.0; Pixel 2 XL Build/OPD1.170816.004) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3765.0 Mobile Safari/537.36',
viewport: {
width: 411,
height: 823,
deviceScaleFactor: 3.5,
isMobile: true,
hasTouch: true,
isLandscape: false,
},
},
{
name: 'Pixel 2 XL landscape',
userAgent: 'Mozilla/5.0 (Linux; Android 8.0.0; Pixel 2 XL Build/OPD1.170816.004) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3765.0 Mobile Safari/537.36',
viewport: {
width: 823,
height: 411,
deviceScaleFactor: 3.5,
isMobile: true,
hasTouch: true,
isLandscape: true,
},
},
];
const devicesMap = {};
exports.devicesMap = devicesMap;
for (const device of devices)
devicesMap[device.name] = device;
+74
View File
@@ -0,0 +1,74 @@
/**
* 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.
*/
import { CDPSession } from './Connection.js';
import { Protocol } from 'devtools-protocol';
/**
* Dialog instances are dispatched by the {@link Page} via the `dialog` event.
*
* @remarks
*
* @example
* ```js
* const puppeteer = require('puppeteer');
*
* (async () => {
* const browser = await puppeteer.launch();
* const page = await browser.newPage();
* page.on('dialog', async dialog => {
* console.log(dialog.message());
* await dialog.dismiss();
* await browser.close();
* });
* page.evaluate(() => alert('1'));
* })();
* ```
*/
export declare class Dialog {
private _client;
private _type;
private _message;
private _defaultValue;
private _handled;
/**
* @internal
*/
constructor(client: CDPSession, type: Protocol.Page.DialogType, message: string, defaultValue?: string);
/**
* @returns The type of the dialog.
*/
type(): Protocol.Page.DialogType;
/**
* @returns The message displayed in the dialog.
*/
message(): string;
/**
* @returns The default value of the prompt, or an empty string if the dialog
* is not a `prompt`.
*/
defaultValue(): string;
/**
* @param promptText - optional text that will be entered in the dialog
* prompt. Has no effect if the dialog's type is not `prompt`.
*
* @returns A promise that resolves when the dialog has been accepted.
*/
accept(promptText?: string): Promise<void>;
/**
* @returns A promise which will resolve once the dialog has been dismissed
*/
dismiss(): Promise<void>;
}
//# sourceMappingURL=Dialog.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"Dialog.d.ts","sourceRoot":"","sources":["../../../../src/common/Dialog.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAGH,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAE7C;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,qBAAa,MAAM;IACjB,OAAO,CAAC,OAAO,CAAa;IAC5B,OAAO,CAAC,KAAK,CAA2B;IACxC,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,QAAQ,CAAS;IAEzB;;OAEG;gBAED,MAAM,EAAE,UAAU,EAClB,IAAI,EAAE,QAAQ,CAAC,IAAI,CAAC,UAAU,EAC9B,OAAO,EAAE,MAAM,EACf,YAAY,SAAK;IAQnB;;OAEG;IACH,IAAI,IAAI,QAAQ,CAAC,IAAI,CAAC,UAAU;IAIhC;;OAEG;IACH,OAAO,IAAI,MAAM;IAIjB;;;OAGG;IACH,YAAY,IAAI,MAAM;IAItB;;;;;OAKG;IACG,MAAM,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAShD;;OAEG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;CAO/B"}
+96
View File
@@ -0,0 +1,96 @@
"use strict";
/**
* 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.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.Dialog = void 0;
const assert_js_1 = require("./assert.js");
/**
* Dialog instances are dispatched by the {@link Page} via the `dialog` event.
*
* @remarks
*
* @example
* ```js
* const puppeteer = require('puppeteer');
*
* (async () => {
* const browser = await puppeteer.launch();
* const page = await browser.newPage();
* page.on('dialog', async dialog => {
* console.log(dialog.message());
* await dialog.dismiss();
* await browser.close();
* });
* page.evaluate(() => alert('1'));
* })();
* ```
*/
class Dialog {
/**
* @internal
*/
constructor(client, type, message, defaultValue = '') {
this._handled = false;
this._client = client;
this._type = type;
this._message = message;
this._defaultValue = defaultValue;
}
/**
* @returns The type of the dialog.
*/
type() {
return this._type;
}
/**
* @returns The message displayed in the dialog.
*/
message() {
return this._message;
}
/**
* @returns The default value of the prompt, or an empty string if the dialog
* is not a `prompt`.
*/
defaultValue() {
return this._defaultValue;
}
/**
* @param promptText - optional text that will be entered in the dialog
* prompt. Has no effect if the dialog's type is not `prompt`.
*
* @returns A promise that resolves when the dialog has been accepted.
*/
async accept(promptText) {
assert_js_1.assert(!this._handled, 'Cannot accept dialog which is already handled!');
this._handled = true;
await this._client.send('Page.handleJavaScriptDialog', {
accept: true,
promptText: promptText,
});
}
/**
* @returns A promise which will resolve once the dialog has been dismissed
*/
async dismiss() {
assert_js_1.assert(!this._handled, 'Cannot dismiss dialog which is already handled!');
this._handled = true;
await this._client.send('Page.handleJavaScriptDialog', {
accept: false,
});
}
}
exports.Dialog = Dialog;
@@ -0,0 +1,25 @@
/**
* 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.
*/
import { CDPSession } from './Connection.js';
import { Viewport } from './PuppeteerViewport.js';
export declare class EmulationManager {
_client: CDPSession;
_emulatingMobile: boolean;
_hasTouch: boolean;
constructor(client: CDPSession);
emulateViewport(viewport: Viewport): Promise<boolean>;
}
//# sourceMappingURL=EmulationManager.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"EmulationManager.d.ts","sourceRoot":"","sources":["../../../../src/common/EmulationManager.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAGlD,qBAAa,gBAAgB;IAC3B,OAAO,EAAE,UAAU,CAAC;IACpB,gBAAgB,UAAS;IACzB,SAAS,UAAS;gBAEN,MAAM,EAAE,UAAU;IAIxB,eAAe,CAAC,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC;CA6B5D"}
@@ -0,0 +1,37 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.EmulationManager = void 0;
class EmulationManager {
constructor(client) {
this._emulatingMobile = false;
this._hasTouch = false;
this._client = client;
}
async emulateViewport(viewport) {
const mobile = viewport.isMobile || false;
const width = viewport.width;
const height = viewport.height;
const deviceScaleFactor = viewport.deviceScaleFactor || 1;
const screenOrientation = viewport.isLandscape
? { angle: 90, type: 'landscapePrimary' }
: { angle: 0, type: 'portraitPrimary' };
const hasTouch = viewport.hasTouch || false;
await Promise.all([
this._client.send('Emulation.setDeviceMetricsOverride', {
mobile,
width,
height,
deviceScaleFactor,
screenOrientation,
}),
this._client.send('Emulation.setTouchEmulationEnabled', {
enabled: hasTouch,
}),
]);
const reloadNeeded = this._emulatingMobile !== mobile || this._hasTouch !== hasTouch;
this._emulatingMobile = mobile;
this._hasTouch = hasTouch;
return reloadNeeded;
}
}
exports.EmulationManager = EmulationManager;
+34
View File
@@ -0,0 +1,34 @@
/**
* 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.
*/
declare class CustomError extends Error {
constructor(message: string);
}
/**
* TimeoutError is emitted whenever certain operations are terminated due to timeout.
*
* @remarks
*
* Example operations are {@link Page.waitForSelector | page.waitForSelector}
* or {@link PuppeteerNode.launch | puppeteer.launch}.
*
* @public
*/
export declare class TimeoutError extends CustomError {
}
export declare type PuppeteerErrors = Record<string, typeof CustomError>;
export declare const puppeteerErrors: PuppeteerErrors;
export {};
//# sourceMappingURL=Errors.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"Errors.d.ts","sourceRoot":"","sources":["../../../../src/common/Errors.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,cAAM,WAAY,SAAQ,KAAK;gBACjB,OAAO,EAAE,MAAM;CAK5B;AAED;;;;;;;;;GASG;AACH,qBAAa,YAAa,SAAQ,WAAW;CAAG;AAEhD,oBAAY,eAAe,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,WAAW,CAAC,CAAC;AAEjE,eAAO,MAAM,eAAe,EAAE,eAE7B,CAAC"}
+41
View File
@@ -0,0 +1,41 @@
"use strict";
/**
* 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.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.puppeteerErrors = exports.TimeoutError = void 0;
class CustomError extends Error {
constructor(message) {
super(message);
this.name = this.constructor.name;
Error.captureStackTrace(this, this.constructor);
}
}
/**
* TimeoutError is emitted whenever certain operations are terminated due to timeout.
*
* @remarks
*
* Example operations are {@link Page.waitForSelector | page.waitForSelector}
* or {@link PuppeteerNode.launch | puppeteer.launch}.
*
* @public
*/
class TimeoutError extends CustomError {
}
exports.TimeoutError = TimeoutError;
exports.puppeteerErrors = {
TimeoutError,
};
+58
View File
@@ -0,0 +1,58 @@
/**
* Copyright 2020 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.
*/
import { JSHandle, ElementHandle } from './JSHandle.js';
/**
* @public
*/
export declare type EvaluateFn<T = unknown> = string | ((arg1: T, ...args: unknown[]) => unknown);
export declare type UnwrapPromiseLike<T> = T extends PromiseLike<infer U> ? U : T;
/**
* @public
*/
export declare type EvaluateFnReturnType<T extends EvaluateFn> = T extends (...args: unknown[]) => infer R ? R : unknown;
/**
* @public
*/
export declare type EvaluateHandleFn = string | ((...args: unknown[]) => unknown);
/**
* @public
*/
export declare type Serializable = number | string | boolean | null | BigInt | JSONArray | JSONObject;
/**
* @public
*/
export declare type JSONArray = Serializable[];
/**
* @public
*/
export interface JSONObject {
[key: string]: Serializable;
}
/**
* @public
*/
export declare type SerializableOrJSHandle = Serializable | JSHandle;
/**
* Wraps a DOM element into an ElementHandle instance
* @public
**/
export declare type WrapElementHandle<X> = X extends Element ? ElementHandle<X> : X;
/**
* Unwraps a DOM element out of an ElementHandle instance
* @public
**/
export declare type UnwrapElementHandle<X> = X extends ElementHandle<infer E> ? E : X;
//# sourceMappingURL=EvalTypes.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"EvalTypes.d.ts","sourceRoot":"","sources":["../../../../src/common/EvalTypes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAExD;;GAEG;AACH,oBAAY,UAAU,CAAC,CAAC,GAAG,OAAO,IAC9B,MAAM,GACN,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,OAAO,CAAC,CAAC;AAE/C,oBAAY,iBAAiB,CAAC,CAAC,IAAI,CAAC,SAAS,WAAW,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;AAE1E;;GAEG;AACH,oBAAY,oBAAoB,CAAC,CAAC,SAAS,UAAU,IAAI,CAAC,SAAS,CACjE,GAAG,IAAI,EAAE,OAAO,EAAE,KACf,MAAM,CAAC,GACR,CAAC,GACD,OAAO,CAAC;AAEZ;;GAEG;AACH,oBAAY,gBAAgB,GAAG,MAAM,GAAG,CAAC,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,OAAO,CAAC,CAAC;AAE1E;;GAEG;AACH,oBAAY,YAAY,GACpB,MAAM,GACN,MAAM,GACN,OAAO,GACP,IAAI,GACJ,MAAM,GACN,SAAS,GACT,UAAU,CAAC;AAEf;;GAEG;AACH,oBAAY,SAAS,GAAG,YAAY,EAAE,CAAC;AAEvC;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,CAAC,GAAG,EAAE,MAAM,GAAG,YAAY,CAAC;CAC7B;AAED;;GAEG;AACH,oBAAY,sBAAsB,GAAG,YAAY,GAAG,QAAQ,CAAC;AAE7D;;;IAGI;AACJ,oBAAY,iBAAiB,CAAC,CAAC,IAAI,CAAC,SAAS,OAAO,GAAG,aAAa,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;AAE5E;;;IAGI;AACJ,oBAAY,mBAAmB,CAAC,CAAC,IAAI,CAAC,SAAS,aAAa,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC"}
+17
View File
@@ -0,0 +1,17 @@
"use strict";
/**
* Copyright 2020 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.
*/
Object.defineProperty(exports, "__esModule", { value: true });
+89
View File
@@ -0,0 +1,89 @@
import { EventType, Handler } from '../../vendor/mitt/src/index.js';
/**
* @internal
*/
export interface CommonEventEmitter {
on(event: EventType, handler: Handler): CommonEventEmitter;
off(event: EventType, handler: Handler): CommonEventEmitter;
addListener(event: EventType, handler: Handler): CommonEventEmitter;
removeListener(event: EventType, handler: Handler): CommonEventEmitter;
emit(event: EventType, eventData?: any): boolean;
once(event: EventType, handler: Handler): CommonEventEmitter;
listenerCount(event: string): number;
removeAllListeners(event?: EventType): CommonEventEmitter;
}
/**
* The EventEmitter class that many Puppeteer classes extend.
*
* @remarks
*
* This allows you to listen to events that Puppeteer classes fire and act
* accordingly. Therefore you'll mostly use {@link EventEmitter.on | on} and
* {@link EventEmitter.off | off} to bind
* and unbind to event listeners.
*
* @public
*/
export declare class EventEmitter implements CommonEventEmitter {
private emitter;
private eventsMap;
/**
* @internal
*/
constructor();
/**
* Bind an event listener to fire when an event occurs.
* @param event - the event type you'd like to listen to. Can be a string or symbol.
* @param handler - the function to be called when the event occurs.
* @returns `this` to enable you to chain calls.
*/
on(event: EventType, handler: Handler): EventEmitter;
/**
* Remove an event listener from firing.
* @param event - the event type you'd like to stop listening to.
* @param handler - the function that should be removed.
* @returns `this` to enable you to chain calls.
*/
off(event: EventType, handler: Handler): EventEmitter;
/**
* Remove an event listener.
* @deprecated please use `off` instead.
*/
removeListener(event: EventType, handler: Handler): EventEmitter;
/**
* Add an event listener.
* @deprecated please use `on` instead.
*/
addListener(event: EventType, handler: Handler): EventEmitter;
/**
* Emit an event and call any associated listeners.
*
* @param event - the event you'd like to emit
* @param eventData - any data you'd like to emit with the event
* @returns `true` if there are any listeners, `false` if there are not.
*/
emit(event: EventType, eventData?: any): boolean;
/**
* Like `on` but the listener will only be fired once and then it will be removed.
* @param event - the event you'd like to listen to
* @param handler - the handler function to run when the event occurs
* @returns `this` to enable you to chain calls.
*/
once(event: EventType, handler: Handler): EventEmitter;
/**
* Gets the number of listeners for a given event.
*
* @param event - the event to get the listener count for
* @returns the number of listeners bound to the given event
*/
listenerCount(event: EventType): number;
/**
* Removes all listeners. If given an event argument, it will remove only
* listeners for that event.
* @param event - the event to remove listeners for.
* @returns `this` to enable you to chain calls.
*/
removeAllListeners(event?: EventType): EventEmitter;
private eventListenersCount;
}
//# sourceMappingURL=EventEmitter.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"EventEmitter.d.ts","sourceRoot":"","sources":["../../../../src/common/EventEmitter.ts"],"names":[],"mappings":"AAAA,OAAa,EAEX,SAAS,EACT,OAAO,EACR,MAAM,gCAAgC,CAAC;AAExC;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,EAAE,CAAC,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,OAAO,GAAG,kBAAkB,CAAC;IAC3D,GAAG,CAAC,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,OAAO,GAAG,kBAAkB,CAAC;IAK5D,WAAW,CAAC,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,OAAO,GAAG,kBAAkB,CAAC;IACpE,cAAc,CAAC,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,OAAO,GAAG,kBAAkB,CAAC;IACvE,IAAI,CAAC,KAAK,EAAE,SAAS,EAAE,SAAS,CAAC,EAAE,GAAG,GAAG,OAAO,CAAC;IACjD,IAAI,CAAC,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,OAAO,GAAG,kBAAkB,CAAC;IAC7D,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC;IAErC,kBAAkB,CAAC,KAAK,CAAC,EAAE,SAAS,GAAG,kBAAkB,CAAC;CAC3D;AAED;;;;;;;;;;;GAWG;AACH,qBAAa,YAAa,YAAW,kBAAkB;IACrD,OAAO,CAAC,OAAO,CAAU;IACzB,OAAO,CAAC,SAAS,CAAmC;IAEpD;;OAEG;;IAKH;;;;;OAKG;IACH,EAAE,CAAC,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,OAAO,GAAG,YAAY;IAKpD;;;;;OAKG;IACH,GAAG,CAAC,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,OAAO,GAAG,YAAY;IAKrD;;;OAGG;IACH,cAAc,CAAC,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,OAAO,GAAG,YAAY;IAKhE;;;OAGG;IACH,WAAW,CAAC,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,OAAO,GAAG,YAAY;IAK7D;;;;;;OAMG;IACH,IAAI,CAAC,KAAK,EAAE,SAAS,EAAE,SAAS,CAAC,EAAE,GAAG,GAAG,OAAO;IAKhD;;;;;OAKG;IACH,IAAI,CAAC,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,OAAO,GAAG,YAAY;IAStD;;;;;OAKG;IACH,aAAa,CAAC,KAAK,EAAE,SAAS,GAAG,MAAM;IAIvC;;;;;OAKG;IACH,kBAAkB,CAAC,KAAK,CAAC,EAAE,SAAS,GAAG,YAAY;IASnD,OAAO,CAAC,mBAAmB;CAG5B"}
+116
View File
@@ -0,0 +1,116 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.EventEmitter = void 0;
const index_js_1 = __importDefault(require("../../vendor/mitt/src/index.js"));
/**
* The EventEmitter class that many Puppeteer classes extend.
*
* @remarks
*
* This allows you to listen to events that Puppeteer classes fire and act
* accordingly. Therefore you'll mostly use {@link EventEmitter.on | on} and
* {@link EventEmitter.off | off} to bind
* and unbind to event listeners.
*
* @public
*/
class EventEmitter {
/**
* @internal
*/
constructor() {
this.eventsMap = new Map();
this.emitter = index_js_1.default(this.eventsMap);
}
/**
* Bind an event listener to fire when an event occurs.
* @param event - the event type you'd like to listen to. Can be a string or symbol.
* @param handler - the function to be called when the event occurs.
* @returns `this` to enable you to chain calls.
*/
on(event, handler) {
this.emitter.on(event, handler);
return this;
}
/**
* Remove an event listener from firing.
* @param event - the event type you'd like to stop listening to.
* @param handler - the function that should be removed.
* @returns `this` to enable you to chain calls.
*/
off(event, handler) {
this.emitter.off(event, handler);
return this;
}
/**
* Remove an event listener.
* @deprecated please use `off` instead.
*/
removeListener(event, handler) {
this.off(event, handler);
return this;
}
/**
* Add an event listener.
* @deprecated please use `on` instead.
*/
addListener(event, handler) {
this.on(event, handler);
return this;
}
/**
* Emit an event and call any associated listeners.
*
* @param event - the event you'd like to emit
* @param eventData - any data you'd like to emit with the event
* @returns `true` if there are any listeners, `false` if there are not.
*/
emit(event, eventData) {
this.emitter.emit(event, eventData);
return this.eventListenersCount(event) > 0;
}
/**
* Like `on` but the listener will only be fired once and then it will be removed.
* @param event - the event you'd like to listen to
* @param handler - the handler function to run when the event occurs
* @returns `this` to enable you to chain calls.
*/
once(event, handler) {
const onceHandler = (eventData) => {
handler(eventData);
this.off(event, onceHandler);
};
return this.on(event, onceHandler);
}
/**
* Gets the number of listeners for a given event.
*
* @param event - the event to get the listener count for
* @returns the number of listeners bound to the given event
*/
listenerCount(event) {
return this.eventListenersCount(event);
}
/**
* Removes all listeners. If given an event argument, it will remove only
* listeners for that event.
* @param event - the event to remove listeners for.
* @returns `this` to enable you to chain calls.
*/
removeAllListeners(event) {
if (event) {
this.eventsMap.delete(event);
}
else {
this.eventsMap.clear();
}
return this;
}
eventListenersCount(event) {
return this.eventsMap.has(event) ? this.eventsMap.get(event).length : 0;
}
}
exports.EventEmitter = EventEmitter;
+82
View File
@@ -0,0 +1,82 @@
/**
* 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.
*/
/**
* IMPORTANT: we are mid-way through migrating away from this Events.ts file
* in favour of defining events next to the class that emits them.
*
* However we need to maintain this file for now because the legacy DocLint
* system relies on them. Be aware in the mean time if you make a change here
* you probably need to replicate it in the relevant class. For example if you
* add a new Page event, you should update the PageEmittedEvents enum in
* src/common/Page.ts.
*
* Chat to @jackfranklin if you're unsure.
*/
export declare const Events: {
readonly Page: {
readonly Close: "close";
readonly Console: "console";
readonly Dialog: "dialog";
readonly DOMContentLoaded: "domcontentloaded";
readonly Error: "error";
readonly PageError: "pageerror";
readonly Request: "request";
readonly Response: "response";
readonly RequestFailed: "requestfailed";
readonly RequestFinished: "requestfinished";
readonly FrameAttached: "frameattached";
readonly FrameDetached: "framedetached";
readonly FrameNavigated: "framenavigated";
readonly Load: "load";
readonly Metrics: "metrics";
readonly Popup: "popup";
readonly WorkerCreated: "workercreated";
readonly WorkerDestroyed: "workerdestroyed";
};
readonly Browser: {
readonly TargetCreated: "targetcreated";
readonly TargetDestroyed: "targetdestroyed";
readonly TargetChanged: "targetchanged";
readonly Disconnected: "disconnected";
};
readonly BrowserContext: {
readonly TargetCreated: "targetcreated";
readonly TargetDestroyed: "targetdestroyed";
readonly TargetChanged: "targetchanged";
};
readonly NetworkManager: {
readonly Request: symbol;
readonly Response: symbol;
readonly RequestFailed: symbol;
readonly RequestFinished: symbol;
};
readonly FrameManager: {
readonly FrameAttached: symbol;
readonly FrameNavigated: symbol;
readonly FrameDetached: symbol;
readonly LifecycleEvent: symbol;
readonly FrameNavigatedWithinDocument: symbol;
readonly ExecutionContextCreated: symbol;
readonly ExecutionContextDestroyed: symbol;
};
readonly Connection: {
readonly Disconnected: symbol;
};
readonly CDPSession: {
readonly Disconnected: symbol;
};
};
//# sourceMappingURL=Events.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"Events.d.ts","sourceRoot":"","sources":["../../../../src/common/Events.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH;;;;;;;;;;;GAWG;AAEH,eAAO,MAAM,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAmET,CAAC"}
+86
View File
@@ -0,0 +1,86 @@
"use strict";
/**
* 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.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.Events = void 0;
/**
* IMPORTANT: we are mid-way through migrating away from this Events.ts file
* in favour of defining events next to the class that emits them.
*
* However we need to maintain this file for now because the legacy DocLint
* system relies on them. Be aware in the mean time if you make a change here
* you probably need to replicate it in the relevant class. For example if you
* add a new Page event, you should update the PageEmittedEvents enum in
* src/common/Page.ts.
*
* Chat to @jackfranklin if you're unsure.
*/
exports.Events = {
Page: {
Close: 'close',
Console: 'console',
Dialog: 'dialog',
DOMContentLoaded: 'domcontentloaded',
Error: 'error',
// Can't use just 'error' due to node.js special treatment of error events.
// @see https://nodejs.org/api/events.html#events_error_events
PageError: 'pageerror',
Request: 'request',
Response: 'response',
RequestFailed: 'requestfailed',
RequestFinished: 'requestfinished',
FrameAttached: 'frameattached',
FrameDetached: 'framedetached',
FrameNavigated: 'framenavigated',
Load: 'load',
Metrics: 'metrics',
Popup: 'popup',
WorkerCreated: 'workercreated',
WorkerDestroyed: 'workerdestroyed',
},
Browser: {
TargetCreated: 'targetcreated',
TargetDestroyed: 'targetdestroyed',
TargetChanged: 'targetchanged',
Disconnected: 'disconnected',
},
BrowserContext: {
TargetCreated: 'targetcreated',
TargetDestroyed: 'targetdestroyed',
TargetChanged: 'targetchanged',
},
NetworkManager: {
Request: Symbol('Events.NetworkManager.Request'),
Response: Symbol('Events.NetworkManager.Response'),
RequestFailed: Symbol('Events.NetworkManager.RequestFailed'),
RequestFinished: Symbol('Events.NetworkManager.RequestFinished'),
},
FrameManager: {
FrameAttached: Symbol('Events.FrameManager.FrameAttached'),
FrameNavigated: Symbol('Events.FrameManager.FrameNavigated'),
FrameDetached: Symbol('Events.FrameManager.FrameDetached'),
LifecycleEvent: Symbol('Events.FrameManager.LifecycleEvent'),
FrameNavigatedWithinDocument: Symbol('Events.FrameManager.FrameNavigatedWithinDocument'),
ExecutionContextCreated: Symbol('Events.FrameManager.ExecutionContextCreated'),
ExecutionContextDestroyed: Symbol('Events.FrameManager.ExecutionContextDestroyed'),
},
Connection: {
Disconnected: Symbol('Events.Connection.Disconnected'),
},
CDPSession: {
Disconnected: Symbol('Events.CDPSession.Disconnected'),
},
};
@@ -0,0 +1,187 @@
/**
* 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.
*/
import { JSHandle, ElementHandle } from './JSHandle.js';
import { CDPSession } from './Connection.js';
import { DOMWorld } from './DOMWorld.js';
import { Frame } from './FrameManager.js';
import { Protocol } from 'devtools-protocol';
import { EvaluateHandleFn, SerializableOrJSHandle } from './EvalTypes.js';
export declare const EVALUATION_SCRIPT_URL = "__puppeteer_evaluation_script__";
/**
* This class represents a context for JavaScript execution. A [Page] might have
* many execution contexts:
* - each
* {@link https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe |
* frame } has "default" execution context that is always created after frame is
* attached to DOM. This context is returned by the
* {@link frame.executionContext()} method.
* - {@link https://developer.chrome.com/extensions | Extension}'s content scripts
* create additional execution contexts.
*
* Besides pages, execution contexts can be found in
* {@link https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API |
* workers }.
*
* @public
*/
export declare class ExecutionContext {
/**
* @internal
*/
_client: CDPSession;
/**
* @internal
*/
_world: DOMWorld;
/**
* @internal
*/
_contextId: number;
/**
* @internal
*/
constructor(client: CDPSession, contextPayload: Protocol.Runtime.ExecutionContextDescription, world: DOMWorld);
/**
* @remarks
*
* Not every execution context is associated with a frame. For
* example, workers and extensions have execution contexts that are not
* associated with frames.
*
* @returns The frame associated with this execution context.
*/
frame(): Frame | null;
/**
* @remarks
* 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.
*
*
* @example
* ```js
* const executionContext = await page.mainFrame().executionContext();
* const result = await executionContext.evaluate(() => Promise.resolve(8 * 7))* ;
* console.log(result); // prints "56"
* ```
*
* @example
* A string can also be passed in instead of a function.
*
* ```js
* console.log(await executionContext.evaluate('1 + 2')); // prints "3"
* ```
*
* @example
* {@link 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'.
* ```
* @param pageFunction a function to be evaluated in the `executionContext`
* @param args argument to pass to the page function
*
* @returns A promise that resolves to the return value of the given function.
*/
evaluate<ReturnType extends any>(pageFunction: Function | string, ...args: unknown[]): Promise<ReturnType>;
/**
* @remarks
* The only difference between `executionContext.evaluate` and
* `executionContext.evaluateHandle` is that `executionContext.evaluateHandle`
* returns an in-page object (a {@link 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.
*
* @example
* ```js
* const context = await page.mainFrame().executionContext();
* const aHandle = await context.evaluateHandle(() => Promise.resolve(self));
* aHandle; // Handle for the global object.
* ```
*
* @example
* A string can also be passed in instead of a function.
*
* ```js
* // Handle for the '3' * object.
* const aHandle = await context.evaluateHandle('1 + 2');
* ```
*
* @example
* 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();
* ```
*
* @param pageFunction a function to be evaluated in the `executionContext`
* @param args argument to pass to the page function
*
* @returns A promise that resolves to the return value of the given function
* as an in-page object (a {@link JSHandle}).
*/
evaluateHandle<HandleType extends JSHandle | ElementHandle = JSHandle>(pageFunction: EvaluateHandleFn, ...args: SerializableOrJSHandle[]): Promise<HandleType>;
private _evaluateInternal;
/**
* This method iterates the JavaScript heap and finds all the objects with the
* given prototype.
* @remarks
* @example
* ```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();
* ```
*
* @param prototypeHandle a handle to the object prototype
*
* @returns A handle to an array of objects with the given prototype.
*/
queryObjects(prototypeHandle: JSHandle): Promise<JSHandle>;
/**
* @internal
*/
_adoptBackendNodeId(backendNodeId: Protocol.DOM.BackendNodeId): Promise<ElementHandle>;
/**
* @internal
*/
_adoptElementHandle(elementHandle: ElementHandle): Promise<ElementHandle>;
}
//# sourceMappingURL=ExecutionContext.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"ExecutionContext.d.ts","sourceRoot":"","sources":["../../../../src/common/ExecutionContext.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAIH,OAAO,EAAkB,QAAQ,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AACxE,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAC1C,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAC7C,OAAO,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,MAAM,gBAAgB,CAAC;AAE1E,eAAO,MAAM,qBAAqB,oCAAoC,CAAC;AAGvE;;;;;;;;;;;;;;;;GAgBG;AACH,qBAAa,gBAAgB;IAC3B;;OAEG;IACH,OAAO,EAAE,UAAU,CAAC;IACpB;;OAEG;IACH,MAAM,EAAE,QAAQ,CAAC;IACjB;;OAEG;IACH,UAAU,EAAE,MAAM,CAAC;IAEnB;;OAEG;gBAED,MAAM,EAAE,UAAU,EAClB,cAAc,EAAE,QAAQ,CAAC,OAAO,CAAC,2BAA2B,EAC5D,KAAK,EAAE,QAAQ;IAOjB;;;;;;;;OAQG;IACH,KAAK,IAAI,KAAK,GAAG,IAAI;IAIrB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA0CG;IACG,QAAQ,CAAC,UAAU,SAAS,GAAG,EACnC,YAAY,EAAE,QAAQ,GAAG,MAAM,EAC/B,GAAG,IAAI,EAAE,OAAO,EAAE,GACjB,OAAO,CAAC,UAAU,CAAC;IAQtB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAyCG;IACG,cAAc,CAAC,UAAU,SAAS,QAAQ,GAAG,aAAa,GAAG,QAAQ,EACzE,YAAY,EAAE,gBAAgB,EAC9B,GAAG,IAAI,EAAE,sBAAsB,EAAE,GAChC,OAAO,CAAC,UAAU,CAAC;YAIR,iBAAiB;IAuI/B;;;;;;;;;;;;;;;;;;;;;OAqBG;IACG,YAAY,CAAC,eAAe,EAAE,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC;IAYhE;;OAEG;IACG,mBAAmB,CACvB,aAAa,EAAE,QAAQ,CAAC,GAAG,CAAC,aAAa,GACxC,OAAO,CAAC,aAAa,CAAC;IAQzB;;OAEG;IACG,mBAAmB,CACvB,aAAa,EAAE,aAAa,GAC3B,OAAO,CAAC,aAAa,CAAC;CAW1B"}
@@ -0,0 +1,317 @@
"use strict";
/**
* 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.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.ExecutionContext = exports.EVALUATION_SCRIPT_URL = void 0;
const assert_js_1 = require("./assert.js");
const helper_js_1 = require("./helper.js");
const JSHandle_js_1 = require("./JSHandle.js");
exports.EVALUATION_SCRIPT_URL = '__puppeteer_evaluation_script__';
const SOURCE_URL_REGEX = /^[\040\t]*\/\/[@#] sourceURL=\s*(\S*?)\s*$/m;
/**
* This class represents a context for JavaScript execution. A [Page] might have
* many execution contexts:
* - each
* {@link https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe |
* frame } has "default" execution context that is always created after frame is
* attached to DOM. This context is returned by the
* {@link frame.executionContext()} method.
* - {@link https://developer.chrome.com/extensions | Extension}'s content scripts
* create additional execution contexts.
*
* Besides pages, execution contexts can be found in
* {@link https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API |
* workers }.
*
* @public
*/
class ExecutionContext {
/**
* @internal
*/
constructor(client, contextPayload, world) {
this._client = client;
this._world = world;
this._contextId = contextPayload.id;
}
/**
* @remarks
*
* Not every execution context is associated with a frame. For
* example, workers and extensions have execution contexts that are not
* associated with frames.
*
* @returns The frame associated with this execution context.
*/
frame() {
return this._world ? this._world.frame() : null;
}
/**
* @remarks
* 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.
*
*
* @example
* ```js
* const executionContext = await page.mainFrame().executionContext();
* const result = await executionContext.evaluate(() => Promise.resolve(8 * 7))* ;
* console.log(result); // prints "56"
* ```
*
* @example
* A string can also be passed in instead of a function.
*
* ```js
* console.log(await executionContext.evaluate('1 + 2')); // prints "3"
* ```
*
* @example
* {@link 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'.
* ```
* @param pageFunction a function to be evaluated in the `executionContext`
* @param args argument to pass to the page function
*
* @returns A promise that resolves to the return value of the given function.
*/
async evaluate(pageFunction, ...args) {
return await this._evaluateInternal(true, pageFunction, ...args);
}
/**
* @remarks
* The only difference between `executionContext.evaluate` and
* `executionContext.evaluateHandle` is that `executionContext.evaluateHandle`
* returns an in-page object (a {@link 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.
*
* @example
* ```js
* const context = await page.mainFrame().executionContext();
* const aHandle = await context.evaluateHandle(() => Promise.resolve(self));
* aHandle; // Handle for the global object.
* ```
*
* @example
* A string can also be passed in instead of a function.
*
* ```js
* // Handle for the '3' * object.
* const aHandle = await context.evaluateHandle('1 + 2');
* ```
*
* @example
* 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();
* ```
*
* @param pageFunction a function to be evaluated in the `executionContext`
* @param args argument to pass to the page function
*
* @returns A promise that resolves to the return value of the given function
* as an in-page object (a {@link JSHandle}).
*/
async evaluateHandle(pageFunction, ...args) {
return this._evaluateInternal(false, pageFunction, ...args);
}
async _evaluateInternal(returnByValue, pageFunction, ...args) {
const suffix = `//# sourceURL=${exports.EVALUATION_SCRIPT_URL}`;
if (helper_js_1.helper.isString(pageFunction)) {
const contextId = this._contextId;
const expression = pageFunction;
const expressionWithSourceUrl = SOURCE_URL_REGEX.test(expression)
? expression
: expression + '\n' + suffix;
const { exceptionDetails, result: remoteObject } = await this._client
.send('Runtime.evaluate', {
expression: expressionWithSourceUrl,
contextId,
returnByValue,
awaitPromise: true,
userGesture: true,
})
.catch(rewriteError);
if (exceptionDetails)
throw new Error('Evaluation failed: ' + helper_js_1.helper.getExceptionMessage(exceptionDetails));
return returnByValue
? helper_js_1.helper.valueFromRemoteObject(remoteObject)
: JSHandle_js_1.createJSHandle(this, remoteObject);
}
if (typeof pageFunction !== 'function')
throw new Error(`Expected to get |string| or |function| as the first argument, but got "${pageFunction}" instead.`);
let functionText = pageFunction.toString();
try {
new Function('(' + functionText + ')');
}
catch (error) {
// This means we might have a function shorthand. Try another
// time prefixing 'function '.
if (functionText.startsWith('async '))
functionText =
'async function ' + functionText.substring('async '.length);
else
functionText = 'function ' + functionText;
try {
new Function('(' + functionText + ')');
}
catch (error) {
// We tried hard to serialize, but there's a weird beast here.
throw new Error('Passed function is not well-serializable!');
}
}
let callFunctionOnPromise;
try {
callFunctionOnPromise = this._client.send('Runtime.callFunctionOn', {
functionDeclaration: functionText + '\n' + suffix + '\n',
executionContextId: this._contextId,
arguments: args.map(convertArgument.bind(this)),
returnByValue,
awaitPromise: true,
userGesture: true,
});
}
catch (error) {
if (error instanceof TypeError &&
error.message.startsWith('Converting circular structure to JSON'))
error.message += ' Are you passing a nested JSHandle?';
throw error;
}
const { exceptionDetails, result: remoteObject, } = await callFunctionOnPromise.catch(rewriteError);
if (exceptionDetails)
throw new Error('Evaluation failed: ' + helper_js_1.helper.getExceptionMessage(exceptionDetails));
return returnByValue
? helper_js_1.helper.valueFromRemoteObject(remoteObject)
: JSHandle_js_1.createJSHandle(this, remoteObject);
/**
* @param {*} arg
* @returns {*}
* @this {ExecutionContext}
*/
function convertArgument(arg) {
if (typeof arg === 'bigint')
// eslint-disable-line valid-typeof
return { unserializableValue: `${arg.toString()}n` };
if (Object.is(arg, -0))
return { unserializableValue: '-0' };
if (Object.is(arg, Infinity))
return { unserializableValue: 'Infinity' };
if (Object.is(arg, -Infinity))
return { unserializableValue: '-Infinity' };
if (Object.is(arg, NaN))
return { unserializableValue: 'NaN' };
const objectHandle = arg && arg instanceof JSHandle_js_1.JSHandle ? arg : null;
if (objectHandle) {
if (objectHandle._context !== this)
throw new Error('JSHandles can be evaluated only in the context they were created!');
if (objectHandle._disposed)
throw new Error('JSHandle is disposed!');
if (objectHandle._remoteObject.unserializableValue)
return {
unserializableValue: objectHandle._remoteObject.unserializableValue,
};
if (!objectHandle._remoteObject.objectId)
return { value: objectHandle._remoteObject.value };
return { objectId: objectHandle._remoteObject.objectId };
}
return { value: arg };
}
function rewriteError(error) {
if (error.message.includes('Object reference chain is too long'))
return { result: { type: 'undefined' } };
if (error.message.includes("Object couldn't be returned by value"))
return { result: { type: 'undefined' } };
if (error.message.endsWith('Cannot find context with specified id') ||
error.message.endsWith('Inspected target navigated or closed'))
throw new Error('Execution context was destroyed, most likely because of a navigation.');
throw error;
}
}
/**
* This method iterates the JavaScript heap and finds all the objects with the
* given prototype.
* @remarks
* @example
* ```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();
* ```
*
* @param prototypeHandle a handle to the object prototype
*
* @returns A handle to an array of objects with the given prototype.
*/
async queryObjects(prototypeHandle) {
assert_js_1.assert(!prototypeHandle._disposed, 'Prototype JSHandle is disposed!');
assert_js_1.assert(prototypeHandle._remoteObject.objectId, 'Prototype JSHandle must not be referencing primitive value');
const response = await this._client.send('Runtime.queryObjects', {
prototypeObjectId: prototypeHandle._remoteObject.objectId,
});
return JSHandle_js_1.createJSHandle(this, response.objects);
}
/**
* @internal
*/
async _adoptBackendNodeId(backendNodeId) {
const { object } = await this._client.send('DOM.resolveNode', {
backendNodeId: backendNodeId,
executionContextId: this._contextId,
});
return JSHandle_js_1.createJSHandle(this, object);
}
/**
* @internal
*/
async _adoptElementHandle(elementHandle) {
assert_js_1.assert(elementHandle.executionContext() !== this, 'Cannot adopt handle that already belongs to this execution context');
assert_js_1.assert(this._world, 'Cannot adopt handle without DOMWorld');
const nodeInfo = await this._client.send('DOM.describeNode', {
objectId: elementHandle._remoteObject.objectId,
});
return this._adoptBackendNodeId(nodeInfo.node.backendNodeId);
}
}
exports.ExecutionContext = ExecutionContext;
+58
View File
@@ -0,0 +1,58 @@
/**
* Copyright 2020 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.
*/
import { ElementHandle } from './JSHandle.js';
import { Protocol } from 'devtools-protocol';
/**
* File choosers let you react to the page requesting for a file.
* @remarks
* `FileChooser` objects are returned via the `page.waitForFileChooser` method.
* @example
* An example of using `FileChooser`:
* ```js
* const [fileChooser] = await Promise.all([
* page.waitForFileChooser(),
* page.click('#upload-file-button'), // some button that triggers file selection
* ]);
* await fileChooser.accept(['/tmp/myfile.pdf']);
* ```
* **NOTE** In browsers, only one file chooser can be opened at a time.
* All file choosers must be accepted or canceled. Not doing so will prevent
* subsequent file choosers from appearing.
*/
export declare class FileChooser {
private _element;
private _multiple;
private _handled;
/**
* @internal
*/
constructor(element: ElementHandle, event: Protocol.Page.FileChooserOpenedEvent);
/**
* Whether file chooser allow for {@link https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file#attr-multiple | multiple} file selection.
*/
isMultiple(): boolean;
/**
* Accept the file chooser request with given paths.
* @param filePaths - If some of the `filePaths` are relative paths,
* then they are resolved relative to the {@link https://nodejs.org/api/process.html#process_process_cwd | current working directory}.
*/
accept(filePaths: string[]): Promise<void>;
/**
* Closes the file chooser without selecting any files.
*/
cancel(): Promise<void>;
}
//# sourceMappingURL=FileChooser.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"FileChooser.d.ts","sourceRoot":"","sources":["../../../../src/common/FileChooser.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAG7C;;;;;;;;;;;;;;;;GAgBG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,QAAQ,CAAgB;IAChC,OAAO,CAAC,SAAS,CAAU;IAC3B,OAAO,CAAC,QAAQ,CAAS;IAEzB;;OAEG;gBAED,OAAO,EAAE,aAAa,EACtB,KAAK,EAAE,QAAQ,CAAC,IAAI,CAAC,sBAAsB;IAM7C;;OAEG;IACH,UAAU,IAAI,OAAO;IAIrB;;;;OAIG;IACG,MAAM,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAShD;;OAEG;IACG,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;CAO9B"}
+70
View File
@@ -0,0 +1,70 @@
"use strict";
/**
* Copyright 2020 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.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.FileChooser = void 0;
const assert_js_1 = require("./assert.js");
/**
* File choosers let you react to the page requesting for a file.
* @remarks
* `FileChooser` objects are returned via the `page.waitForFileChooser` method.
* @example
* An example of using `FileChooser`:
* ```js
* const [fileChooser] = await Promise.all([
* page.waitForFileChooser(),
* page.click('#upload-file-button'), // some button that triggers file selection
* ]);
* await fileChooser.accept(['/tmp/myfile.pdf']);
* ```
* **NOTE** In browsers, only one file chooser can be opened at a time.
* All file choosers must be accepted or canceled. Not doing so will prevent
* subsequent file choosers from appearing.
*/
class FileChooser {
/**
* @internal
*/
constructor(element, event) {
this._handled = false;
this._element = element;
this._multiple = event.mode !== 'selectSingle';
}
/**
* Whether file chooser allow for {@link https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file#attr-multiple | multiple} file selection.
*/
isMultiple() {
return this._multiple;
}
/**
* Accept the file chooser request with given paths.
* @param filePaths - If some of the `filePaths` are relative paths,
* then they are resolved relative to the {@link https://nodejs.org/api/process.html#process_process_cwd | current working directory}.
*/
async accept(filePaths) {
assert_js_1.assert(!this._handled, 'Cannot accept FileChooser which is already handled!');
this._handled = true;
await this._element.uploadFile(...filePaths);
}
/**
* Closes the file chooser without selecting any files.
*/
async cancel() {
assert_js_1.assert(!this._handled, 'Cannot cancel FileChooser which is already handled!');
this._handled = true;
}
}
exports.FileChooser = FileChooser;
+733
View File
@@ -0,0 +1,733 @@
/**
* 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.
*/
import { EventEmitter } from './EventEmitter.js';
import { ExecutionContext } from './ExecutionContext.js';
import { PuppeteerLifeCycleEvent } from './LifecycleWatcher.js';
import { DOMWorld, WaitForSelectorOptions } from './DOMWorld.js';
import { NetworkManager } from './NetworkManager.js';
import { TimeoutSettings } from './TimeoutSettings.js';
import { CDPSession } from './Connection.js';
import { JSHandle, ElementHandle } from './JSHandle.js';
import { MouseButton } from './Input.js';
import { Page } from './Page.js';
import { HTTPResponse } from './HTTPResponse.js';
import { Protocol } from 'devtools-protocol';
import { SerializableOrJSHandle, EvaluateHandleFn, WrapElementHandle, EvaluateFn, EvaluateFnReturnType, UnwrapPromiseLike } from './EvalTypes.js';
/**
* We use symbols to prevent external parties listening to these events.
* They are internal to Puppeteer.
*
* @internal
*/
export declare const FrameManagerEmittedEvents: {
FrameAttached: symbol;
FrameNavigated: symbol;
FrameDetached: symbol;
LifecycleEvent: symbol;
FrameNavigatedWithinDocument: symbol;
ExecutionContextCreated: symbol;
ExecutionContextDestroyed: symbol;
};
/**
* @internal
*/
export declare class FrameManager extends EventEmitter {
_client: CDPSession;
private _page;
private _networkManager;
_timeoutSettings: TimeoutSettings;
private _frames;
private _contextIdToContext;
private _isolatedWorlds;
private _mainFrame;
constructor(client: CDPSession, page: Page, ignoreHTTPSErrors: boolean, timeoutSettings: TimeoutSettings);
initialize(): Promise<void>;
networkManager(): NetworkManager;
navigateFrame(frame: Frame, url: string, options?: {
referer?: string;
timeout?: number;
waitUntil?: PuppeteerLifeCycleEvent | PuppeteerLifeCycleEvent[];
}): Promise<HTTPResponse | null>;
waitForFrameNavigation(frame: Frame, options?: {
timeout?: number;
waitUntil?: PuppeteerLifeCycleEvent | PuppeteerLifeCycleEvent[];
}): Promise<HTTPResponse | null>;
private _onFrameMoved;
_onLifecycleEvent(event: Protocol.Page.LifecycleEventEvent): void;
_onFrameStoppedLoading(frameId: string): void;
_handleFrameTree(frameTree: Protocol.Page.FrameTree): void;
page(): Page;
mainFrame(): Frame;
frames(): Frame[];
frame(frameId: string): Frame | null;
_onFrameAttached(frameId: string, parentFrameId?: string): void;
_onFrameNavigated(framePayload: Protocol.Page.Frame): void;
_ensureIsolatedWorld(name: string): Promise<void>;
_onFrameNavigatedWithinDocument(frameId: string, url: string): void;
_onFrameDetached(frameId: string): void;
_onExecutionContextCreated(contextPayload: Protocol.Runtime.ExecutionContextDescription): void;
private _onExecutionContextDestroyed;
private _onExecutionContextsCleared;
executionContextById(contextId: number): ExecutionContext;
private _removeFramesRecursively;
}
/**
* @public
*/
export interface FrameWaitForFunctionOptions {
/**
* 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.
*/
polling?: string | number;
/**
* Maximum time to wait in milliseconds. Defaults to `30000` (30 seconds).
* Pass `0` to disable the timeout. Puppeteer's default timeout can be changed
* using {@link Page.setDefaultTimeout}.
*/
timeout?: number;
}
/**
* @public
*/
export interface FrameAddScriptTagOptions {
/**
* the URL of the script to be added.
*/
url?: string;
/**
* The path to a JavaScript file to be injected into the frame.
* @remarks
* If `path` is a relative path, it is resolved relative to the current
* working directory (`process.cwd()` in Node.js).
*/
path?: string;
/**
* Raw JavaScript content to be injected into the frame.
*/
content?: string;
/**
* Set the script's `type`. Use `module` in order to load an ES2015 module.
*/
type?: string;
}
/**
* @public
*/
export interface FrameAddStyleTagOptions {
/**
* the URL of the CSS file to be added.
*/
url?: string;
/**
* The path to a CSS file to be injected into the frame.
* @remarks
* If `path` is a relative path, it is resolved relative to the current
* working directory (`process.cwd()` in Node.js).
*/
path?: string;
/**
* Raw CSS content to be injected into the frame.
*/
content?: string;
}
/**
* At every point of time, page exposes its current frame tree via the
* {@link Page.mainFrame | page.mainFrame} and
* {@link Frame.childFrames | frame.childFrames} methods.
*
* @remarks
*
* `Frame` object lifecycles are controlled by three events that are all
* dispatched on the page object:
*
* - {@link PageEmittedEvents.FrameAttached}
*
* - {@link PageEmittedEvents.FrameNavigated}
*
* - {@link PageEmittedEvents.FrameDetached}
*
* @Example
* An example of dumping frame tree:
*
* ```js
* const puppeteer = require('puppeteer');
*
* (async () => {
* const browser = await puppeteer.launch();
* 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 (const child of frame.childFrames()) {
* dumpFrameTree(child, indent + ' ');
* }
* }
* })();
* ```
*
* @Example
* 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);
* ```
*
* @public
*/
export declare class Frame {
/**
* @internal
*/
_frameManager: FrameManager;
private _parentFrame?;
/**
* @internal
*/
_id: string;
private _url;
private _detached;
/**
* @internal
*/
_loaderId: string;
/**
* @internal
*/
_name?: string;
/**
* @internal
*/
_lifecycleEvents: Set<string>;
/**
* @internal
*/
_mainWorld: DOMWorld;
/**
* @internal
*/
_secondaryWorld: DOMWorld;
/**
* @internal
*/
_childFrames: Set<Frame>;
/**
* @internal
*/
constructor(frameManager: FrameManager, parentFrame: Frame | null, frameId: string);
/**
* @remarks
*
* `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 remote server does not respond or is unreachable.
*
* - the main resource failed to load.
*
* `frame.goto` will not throw an error when any valid HTTP status code is
* returned by the remote server, including 404 "Not Found" and 500 "Internal
* Server Error". The status code for such responses can be retrieved by
* calling {@link HTTPResponse.status}.
*
* NOTE: `frame.goto` either throws an error or returns 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 {@link https://bugs.chromium.org/p/chromium/issues/detail?id=761295 | upstream
* issue}.
*
* @param url - the URL to navigate the frame to. This should include the
* scheme, e.g. `https://`.
* @param options - navigation options. `waitUntil` is useful to define when
* the navigation should be considered successful - see the docs for
* {@link PuppeteerLifeCycleEvent} for more details.
*
* @returns A promise which resolves to the main resource response. In case of
* multiple redirects, the navigation will resolve with the response of the
* last redirect.
*/
goto(url: string, options?: {
referer?: string;
timeout?: number;
waitUntil?: PuppeteerLifeCycleEvent | PuppeteerLifeCycleEvent[];
}): Promise<HTTPResponse | null>;
/**
* @remarks
*
* 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([
* // The navigation promise resolves after navigation has finished
* frame.waitForNavigation(),
* // Clicking the link will indirectly cause a navigation
* frame.click('a.my-link'),
* ]);
* ```
*
* Usage of the {@link https://developer.mozilla.org/en-US/docs/Web/API/History_API | History API} to change the URL is considered a navigation.
*
* @param options - options to configure when the navigation is consided finished.
* @returns a promise that resolves when the frame navigates to a new URL.
*/
waitForNavigation(options?: {
timeout?: number;
waitUntil?: PuppeteerLifeCycleEvent | PuppeteerLifeCycleEvent[];
}): Promise<HTTPResponse | null>;
/**
* @returns a promise that resolves to the frame's default execution context.
*/
executionContext(): Promise<ExecutionContext>;
/**
* @remarks
*
* The only difference between {@link Frame.evaluate} and
* `frame.evaluateHandle` is that `evaluateHandle` will return the value
* wrapped in an in-page object.
*
* This method behaves identically to {@link Page.evaluateHandle} except it's
* run within the context of the `frame`, rather than the entire page.
*
* @param pageFunction - a function that is run within the frame
* @param args - arguments to be passed to the pageFunction
*/
evaluateHandle<HandlerType extends JSHandle = JSHandle>(pageFunction: EvaluateHandleFn, ...args: SerializableOrJSHandle[]): Promise<HandlerType>;
/**
* @remarks
*
* This method behaves identically to {@link Page.evaluate} except it's run
* within the context of the `frame`, rather than the entire page.
*
* @param pageFunction - a function that is run within the frame
* @param args - arguments to be passed to the pageFunction
*/
evaluate<T extends EvaluateFn>(pageFunction: T, ...args: SerializableOrJSHandle[]): Promise<UnwrapPromiseLike<EvaluateFnReturnType<T>>>;
/**
* This method queries the frame for the given selector.
*
* @param selector - a selector to query for.
* @returns A promise which resolves to an `ElementHandle` pointing at the
* element, or `null` if it was not found.
*/
$(selector: string): Promise<ElementHandle | null>;
/**
* This method evaluates the given XPath expression and returns the results.
*
* @param expression - the XPath expression to evaluate.
*/
$x(expression: string): Promise<ElementHandle[]>;
/**
* @remarks
*
* This method runs `document.querySelector` 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.
*
* @example
*
* ```js
* const searchValue = await frame.$eval('#search', el => el.value);
* ```
*
* @param selector - the selector to query for
* @param pageFunction - the function to be evaluated in the frame's context
* @param args - additional arguments to pass to `pageFuncton`
*/
$eval<ReturnType>(selector: string, pageFunction: (element: Element, ...args: unknown[]) => ReturnType | Promise<ReturnType>, ...args: SerializableOrJSHandle[]): Promise<WrapElementHandle<ReturnType>>;
/**
* @remarks
*
* 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.
*
* @example
*
* ```js
* const divsCounts = await frame.$$eval('div', divs => divs.length);
* ```
*
* @param selector - the selector to query for
* @param pageFunction - the function to be evaluated in the frame's context
* @param args - additional arguments to pass to `pageFuncton`
*/
$$eval<ReturnType>(selector: string, pageFunction: (elements: Element[], ...args: unknown[]) => ReturnType | Promise<ReturnType>, ...args: SerializableOrJSHandle[]): Promise<WrapElementHandle<ReturnType>>;
/**
* This runs `document.querySelectorAll` in the frame and returns the result.
*
* @param selector - a selector to search for
* @returns An array of element handles pointing to the found frame elements.
*/
$$(selector: string): Promise<ElementHandle[]>;
/**
* @returns the full HTML contents of the frame, including the doctype.
*/
content(): Promise<string>;
/**
* Set the content of the frame.
*
* @param html - HTML markup to assign to the page.
* @param options - options to configure how long before timing out and at
* what point to consider the content setting successful.
*/
setContent(html: string, options?: {
timeout?: number;
waitUntil?: PuppeteerLifeCycleEvent | PuppeteerLifeCycleEvent[];
}): Promise<void>;
/**
* @remarks
*
* If the name is empty, it 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.
*
* @returns the frame's `name` attribute as specified in the tag.
*/
name(): string;
/**
* @returns the frame's URL.
*/
url(): string;
/**
* @returns the parent `Frame`, if any. Detached and main frames return `null`.
*/
parentFrame(): Frame | null;
/**
* @returns an array of child frames.
*/
childFrames(): Frame[];
/**
* @returns `true` if the frame has been detached, or `false` otherwise.
*/
isDetached(): boolean;
/**
* Adds a `<script>` tag into the page with the desired url or content.
*
* @param options - configure the script to add to the page.
*
* @returns a promise that resolves to the added tag when the script's
* `onload` event fires or when the script content was injected into the
* frame.
*/
addScriptTag(options: FrameAddScriptTagOptions): Promise<ElementHandle>;
/**
* Adds a `<link rel="stylesheet">` tag into the page with the desired url or
* a `<style type="text/css">` tag with the content.
*
* @param options - configure the CSS to add to the page.
*
* @returns a promise that resolves to the added tag when the stylesheets's
* `onload` event fires or when the CSS content was injected into the
* frame.
*/
addStyleTag(options: FrameAddStyleTagOptions): Promise<ElementHandle>;
/**
*
* This method clicks the first element found that matches `selector`.
*
* @remarks
*
* This method scrolls the element into view if needed, and then uses
* {@link Page.mouse} 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),
* ]);
* ```
* @param selector - the selector to search for to click. If there are
* multiple elements, the first will be clicked.
*/
click(selector: string, options?: {
delay?: number;
button?: MouseButton;
clickCount?: number;
}): Promise<void>;
/**
* This method fetches an element with `selector` and focuses it.
*
* @remarks
* If there's no element matching `selector`, the method throws an error.
*
* @param selector - the selector for the element to focus. If there are
* multiple elements, the first will be focused.
*/
focus(selector: string): Promise<void>;
/**
* This method fetches an element with `selector`, scrolls it into view if
* needed, and then uses {@link Page.mouse} to hover over the center of the
* element.
*
* @remarks
* If there's no element matching `selector`, the method throws an
*
* @param selector - the selector for the element to hover. If there are
* multiple elements, the first will be hovered.
*/
hover(selector: string): Promise<void>;
/**
* Triggers a `change` and `input` event once all the provided options have
* been selected.
*
* @remarks
*
* If there's no `<select>` element matching `selector`, the
* method throws an error.
*
* @example
* ```js
* frame.select('select#colors', 'blue'); // single selection
* frame.select('select#colors', 'red', 'green', 'blue'); // multiple selections
* ```
*
* @param selector - a selector to query the frame for
* @param values - an array of values to select. If the `<select>` has the
* `multiple` attribute, all values are considered, otherwise only the first
* one is taken into account.
* @returns the list of values that were successfully selected.
*/
select(selector: string, ...values: string[]): Promise<string[]>;
/**
* This method fetches an element with `selector`, scrolls it into view if
* needed, and then uses {@link Page.touchscreen} to tap in the center of the
* element.
*
* @remarks
*
* If there's no element matching `selector`, the method throws an error.
*
* @param selector - the selector to tap.
* @returns a promise that resolves when the element has been tapped.
*/
tap(selector: string): Promise<void>;
/**
* Sends a `keydown`, `keypress`/`input`, and `keyup` event for each character
* in the text.
*
* @remarks
* To press a special key, like `Control` or `ArrowDown`, use
* {@link Keyboard.press}.
*
* @example
* ```js
* await frame.type('#mytextarea', 'Hello'); // Types instantly
* await frame.type('#mytextarea', 'World', {delay: 100}); // Types slower, like a user
* ```
*
* @param selector - the selector for the element to type into. If there are
* multiple the first will be used.
* @param text - text to type into the element
* @param options - takes one option, `delay`, which sets the time to wait
* between key presses in milliseconds. Defaults to `0`.
*
* @returns a promise that resolves when the typing is complete.
*/
type(selector: string, text: string, options?: {
delay: number;
}): Promise<void>;
/**
* @remarks
*
* This method behaves differently depending on the first parameter. If it's a
* `string`, it will be treated as a `selector` or `xpath` (if the string
* starts with `//`). This method then is a shortcut for
* {@link Frame.waitForSelector} or {@link Frame.waitForXPath}.
*
* If the first argument is a function this method is a shortcut for
* {@link Frame.waitForFunction}.
*
* If the first argument is a `number`, it's treated as a timeout in
* milliseconds and the method returns a promise which resolves after the
* timeout.
*
* @param selectorOrFunctionOrTimeout - a selector, predicate or timeout to
* wait for.
* @param options - optional waiting parameters.
* @param args - arguments to pass to `pageFunction`.
*
* @deprecated Don't use this method directly. Instead use the more explicit
* methods available: {@link Frame.waitForSelector},
* {@link Frame.waitForXPath}, {@link Frame.waitForFunction} or
* {@link Frame.waitForTimeout}.
*/
waitFor(selectorOrFunctionOrTimeout: string | number | Function, options?: Record<string, unknown>, ...args: SerializableOrJSHandle[]): Promise<JSHandle | null>;
/**
* Causes your script to wait for the given number of milliseconds.
*
* @remarks
* It's generally recommended to not wait for a number of seconds, but instead
* use {@link Frame.waitForSelector}, {@link Frame.waitForXPath} or
* {@link Frame.waitForFunction} to wait for exactly the conditions you want.
*
* @example
*
* Wait for 1 second:
*
* ```
* await frame.waitForTimeout(1000);
* ```
*
* @param milliseconds - the number of milliseconds to wait.
*/
waitForTimeout(milliseconds: number): Promise<void>;
/**
* @remarks
*
*
* 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.
*
* @example
* ```js
* const puppeteer = require('puppeteer');
*
* (async () => {
* const browser = await puppeteer.launch();
* 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();
* })();
* ```
* @param selector - the selector to wait for.
* @param options - options to define if the element should be visible and how
* long to wait before timing out.
* @returns a promise which resolves when an element matching the selector
* string is added to the DOM.
*/
waitForSelector(selector: string, options?: WaitForSelectorOptions): Promise<ElementHandle | null>;
/**
* @remarks
* 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.
*
* For a code example, see the example for {@link Frame.waitForSelector}. That
* function behaves identically other than taking a CSS selector rather than
* an XPath.
*
* @param xpath - the XPath expression to wait for.
* @param options - options to configure the visiblity of the element and how
* long to wait before timing out.
*/
waitForXPath(xpath: string, options?: WaitForSelectorOptions): Promise<ElementHandle | null>;
/**
* @remarks
*
* @example
*
* The `waitForFunction` can be used to observe viewport size change:
* ```js
* const puppeteer = require('puppeteer');
*
* (async () => {
* . const browser = await puppeteer.launch();
* . 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 frame.waitForFunction(
* selector => !!document.querySelector(selector),
* {}, // empty options object
* selector
*);
* ```
*
* @param pageFunction - the function to evaluate in the frame context.
* @param options - options to configure the polling method and timeout.
* @param args - arguments to pass to the `pageFunction`.
* @returns the promise which resolve when the `pageFunction` returns a truthy value.
*/
waitForFunction(pageFunction: Function | string, options?: FrameWaitForFunctionOptions, ...args: SerializableOrJSHandle[]): Promise<JSHandle>;
/**
* @returns the frame's title.
*/
title(): Promise<string>;
/**
* @internal
*/
_navigated(framePayload: Protocol.Page.Frame): void;
/**
* @internal
*/
_navigatedWithinDocument(url: string): void;
/**
* @internal
*/
_onLifecycleEvent(loaderId: string, name: string): void;
/**
* @internal
*/
_onLoadingStopped(): void;
/**
* @internal
*/
_detach(): void;
}
//# sourceMappingURL=FrameManager.d.ts.map
File diff suppressed because one or more lines are too long
+965
View File
@@ -0,0 +1,965 @@
"use strict";
/**
* 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.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.Frame = exports.FrameManager = exports.FrameManagerEmittedEvents = void 0;
const Debug_js_1 = require("../common/Debug.js");
const EventEmitter_js_1 = require("./EventEmitter.js");
const assert_js_1 = require("./assert.js");
const helper_js_1 = require("./helper.js");
const ExecutionContext_js_1 = require("./ExecutionContext.js");
const LifecycleWatcher_js_1 = require("./LifecycleWatcher.js");
const DOMWorld_js_1 = require("./DOMWorld.js");
const NetworkManager_js_1 = require("./NetworkManager.js");
const UTILITY_WORLD_NAME = '__puppeteer_utility_world__';
/**
* We use symbols to prevent external parties listening to these events.
* They are internal to Puppeteer.
*
* @internal
*/
exports.FrameManagerEmittedEvents = {
FrameAttached: Symbol('FrameManager.FrameAttached'),
FrameNavigated: Symbol('FrameManager.FrameNavigated'),
FrameDetached: Symbol('FrameManager.FrameDetached'),
LifecycleEvent: Symbol('FrameManager.LifecycleEvent'),
FrameNavigatedWithinDocument: Symbol('FrameManager.FrameNavigatedWithinDocument'),
ExecutionContextCreated: Symbol('FrameManager.ExecutionContextCreated'),
ExecutionContextDestroyed: Symbol('FrameManager.ExecutionContextDestroyed'),
};
/**
* @internal
*/
class FrameManager extends EventEmitter_js_1.EventEmitter {
constructor(client, page, ignoreHTTPSErrors, timeoutSettings) {
super();
this._frames = new Map();
this._contextIdToContext = new Map();
this._isolatedWorlds = new Set();
this._client = client;
this._page = page;
this._networkManager = new NetworkManager_js_1.NetworkManager(client, ignoreHTTPSErrors, this);
this._timeoutSettings = timeoutSettings;
this._client.on('Page.frameAttached', (event) => this._onFrameAttached(event.frameId, event.parentFrameId));
this._client.on('Page.frameNavigated', (event) => this._onFrameNavigated(event.frame));
this._client.on('Page.navigatedWithinDocument', (event) => this._onFrameNavigatedWithinDocument(event.frameId, event.url));
this._client.on('Page.frameDetached', (event) => this._onFrameDetached(event.frameId));
this._client.on('Page.frameStoppedLoading', (event) => this._onFrameStoppedLoading(event.frameId));
this._client.on('Runtime.executionContextCreated', (event) => this._onExecutionContextCreated(event.context));
this._client.on('Runtime.executionContextDestroyed', (event) => this._onExecutionContextDestroyed(event.executionContextId));
this._client.on('Runtime.executionContextsCleared', () => this._onExecutionContextsCleared());
this._client.on('Page.lifecycleEvent', (event) => this._onLifecycleEvent(event));
this._client.on('Target.attachedToTarget', async (event) => this._onFrameMoved(event));
}
async initialize() {
const result = await Promise.all([
this._client.send('Page.enable'),
this._client.send('Page.getFrameTree'),
]);
const { frameTree } = result[1];
this._handleFrameTree(frameTree);
await Promise.all([
this._client.send('Page.setLifecycleEventsEnabled', { enabled: true }),
this._client
.send('Runtime.enable')
.then(() => this._ensureIsolatedWorld(UTILITY_WORLD_NAME)),
this._networkManager.initialize(),
]);
}
networkManager() {
return this._networkManager;
}
async navigateFrame(frame, url, options = {}) {
assertNoLegacyNavigationOptions(options);
const { referer = this._networkManager.extraHTTPHeaders()['referer'], waitUntil = ['load'], timeout = this._timeoutSettings.navigationTimeout(), } = options;
const watcher = new LifecycleWatcher_js_1.LifecycleWatcher(this, frame, waitUntil, timeout);
let ensureNewDocumentNavigation = false;
let error = await Promise.race([
navigate(this._client, url, referer, frame._id),
watcher.timeoutOrTerminationPromise(),
]);
if (!error) {
error = await Promise.race([
watcher.timeoutOrTerminationPromise(),
ensureNewDocumentNavigation
? watcher.newDocumentNavigationPromise()
: watcher.sameDocumentNavigationPromise(),
]);
}
watcher.dispose();
if (error)
throw error;
return watcher.navigationResponse();
async function navigate(client, url, referrer, frameId) {
try {
const response = await client.send('Page.navigate', {
url,
referrer,
frameId,
});
ensureNewDocumentNavigation = !!response.loaderId;
return response.errorText
? new Error(`${response.errorText} at ${url}`)
: null;
}
catch (error) {
return error;
}
}
}
async waitForFrameNavigation(frame, options = {}) {
assertNoLegacyNavigationOptions(options);
const { waitUntil = ['load'], timeout = this._timeoutSettings.navigationTimeout(), } = options;
const watcher = new LifecycleWatcher_js_1.LifecycleWatcher(this, frame, waitUntil, timeout);
const error = await Promise.race([
watcher.timeoutOrTerminationPromise(),
watcher.sameDocumentNavigationPromise(),
watcher.newDocumentNavigationPromise(),
]);
watcher.dispose();
if (error)
throw error;
return watcher.navigationResponse();
}
async _onFrameMoved(event) {
if (event.targetInfo.type !== 'iframe') {
return;
}
// TODO(sadym): Remove debug message once proper OOPIF support is
// implemented: https://github.com/puppeteer/puppeteer/issues/2548
Debug_js_1.debug('puppeteer:frame')(`The frame '${event.targetInfo.targetId}' moved to another session. ` +
`Out-of-process iframes (OOPIF) are not supported by Puppeteer yet. ` +
`https://github.com/puppeteer/puppeteer/issues/2548`);
}
_onLifecycleEvent(event) {
const frame = this._frames.get(event.frameId);
if (!frame)
return;
frame._onLifecycleEvent(event.loaderId, event.name);
this.emit(exports.FrameManagerEmittedEvents.LifecycleEvent, frame);
}
_onFrameStoppedLoading(frameId) {
const frame = this._frames.get(frameId);
if (!frame)
return;
frame._onLoadingStopped();
this.emit(exports.FrameManagerEmittedEvents.LifecycleEvent, frame);
}
_handleFrameTree(frameTree) {
if (frameTree.frame.parentId)
this._onFrameAttached(frameTree.frame.id, frameTree.frame.parentId);
this._onFrameNavigated(frameTree.frame);
if (!frameTree.childFrames)
return;
for (const child of frameTree.childFrames)
this._handleFrameTree(child);
}
page() {
return this._page;
}
mainFrame() {
return this._mainFrame;
}
frames() {
return Array.from(this._frames.values());
}
frame(frameId) {
return this._frames.get(frameId) || null;
}
_onFrameAttached(frameId, parentFrameId) {
if (this._frames.has(frameId))
return;
assert_js_1.assert(parentFrameId);
const parentFrame = this._frames.get(parentFrameId);
const frame = new Frame(this, parentFrame, frameId);
this._frames.set(frame._id, frame);
this.emit(exports.FrameManagerEmittedEvents.FrameAttached, frame);
}
_onFrameNavigated(framePayload) {
const isMainFrame = !framePayload.parentId;
let frame = isMainFrame
? this._mainFrame
: this._frames.get(framePayload.id);
assert_js_1.assert(isMainFrame || frame, 'We either navigate top level or have old version of the navigated frame');
// Detach all child frames first.
if (frame) {
for (const child of frame.childFrames())
this._removeFramesRecursively(child);
}
// Update or create main frame.
if (isMainFrame) {
if (frame) {
// Update frame id to retain frame identity on cross-process navigation.
this._frames.delete(frame._id);
frame._id = framePayload.id;
}
else {
// Initial main frame navigation.
frame = new Frame(this, null, framePayload.id);
}
this._frames.set(framePayload.id, frame);
this._mainFrame = frame;
}
// Update frame payload.
frame._navigated(framePayload);
this.emit(exports.FrameManagerEmittedEvents.FrameNavigated, frame);
}
async _ensureIsolatedWorld(name) {
if (this._isolatedWorlds.has(name))
return;
this._isolatedWorlds.add(name);
await this._client.send('Page.addScriptToEvaluateOnNewDocument', {
source: `//# sourceURL=${ExecutionContext_js_1.EVALUATION_SCRIPT_URL}`,
worldName: name,
}),
await Promise.all(this.frames().map((frame) => this._client
.send('Page.createIsolatedWorld', {
frameId: frame._id,
grantUniveralAccess: true,
worldName: name,
})
.catch(helper_js_1.debugError))); // frames might be removed before we send this
}
_onFrameNavigatedWithinDocument(frameId, url) {
const frame = this._frames.get(frameId);
if (!frame)
return;
frame._navigatedWithinDocument(url);
this.emit(exports.FrameManagerEmittedEvents.FrameNavigatedWithinDocument, frame);
this.emit(exports.FrameManagerEmittedEvents.FrameNavigated, frame);
}
_onFrameDetached(frameId) {
const frame = this._frames.get(frameId);
if (frame)
this._removeFramesRecursively(frame);
}
_onExecutionContextCreated(contextPayload) {
const auxData = contextPayload.auxData;
const frameId = auxData ? auxData.frameId : null;
const frame = this._frames.get(frameId) || null;
let world = null;
if (frame) {
if (contextPayload.auxData && !!contextPayload.auxData['isDefault']) {
world = frame._mainWorld;
}
else if (contextPayload.name === UTILITY_WORLD_NAME &&
!frame._secondaryWorld._hasContext()) {
// In case of multiple sessions to the same target, there's a race between
// connections so we might end up creating multiple isolated worlds.
// We can use either.
world = frame._secondaryWorld;
}
}
if (contextPayload.auxData && contextPayload.auxData['type'] === 'isolated')
this._isolatedWorlds.add(contextPayload.name);
const context = new ExecutionContext_js_1.ExecutionContext(this._client, contextPayload, world);
if (world)
world._setContext(context);
this._contextIdToContext.set(contextPayload.id, context);
}
_onExecutionContextDestroyed(executionContextId) {
const context = this._contextIdToContext.get(executionContextId);
if (!context)
return;
this._contextIdToContext.delete(executionContextId);
if (context._world)
context._world._setContext(null);
}
_onExecutionContextsCleared() {
for (const context of this._contextIdToContext.values()) {
if (context._world)
context._world._setContext(null);
}
this._contextIdToContext.clear();
}
executionContextById(contextId) {
const context = this._contextIdToContext.get(contextId);
assert_js_1.assert(context, 'INTERNAL ERROR: missing context with id = ' + contextId);
return context;
}
_removeFramesRecursively(frame) {
for (const child of frame.childFrames())
this._removeFramesRecursively(child);
frame._detach();
this._frames.delete(frame._id);
this.emit(exports.FrameManagerEmittedEvents.FrameDetached, frame);
}
}
exports.FrameManager = FrameManager;
/**
* At every point of time, page exposes its current frame tree via the
* {@link Page.mainFrame | page.mainFrame} and
* {@link Frame.childFrames | frame.childFrames} methods.
*
* @remarks
*
* `Frame` object lifecycles are controlled by three events that are all
* dispatched on the page object:
*
* - {@link PageEmittedEvents.FrameAttached}
*
* - {@link PageEmittedEvents.FrameNavigated}
*
* - {@link PageEmittedEvents.FrameDetached}
*
* @Example
* An example of dumping frame tree:
*
* ```js
* const puppeteer = require('puppeteer');
*
* (async () => {
* const browser = await puppeteer.launch();
* 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 (const child of frame.childFrames()) {
* dumpFrameTree(child, indent + ' ');
* }
* }
* })();
* ```
*
* @Example
* 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);
* ```
*
* @public
*/
class Frame {
/**
* @internal
*/
constructor(frameManager, parentFrame, frameId) {
this._url = '';
this._detached = false;
/**
* @internal
*/
this._loaderId = '';
/**
* @internal
*/
this._lifecycleEvents = new Set();
this._frameManager = frameManager;
this._parentFrame = parentFrame;
this._url = '';
this._id = frameId;
this._detached = false;
this._loaderId = '';
this._mainWorld = new DOMWorld_js_1.DOMWorld(frameManager, this, frameManager._timeoutSettings);
this._secondaryWorld = new DOMWorld_js_1.DOMWorld(frameManager, this, frameManager._timeoutSettings);
this._childFrames = new Set();
if (this._parentFrame)
this._parentFrame._childFrames.add(this);
}
/**
* @remarks
*
* `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 remote server does not respond or is unreachable.
*
* - the main resource failed to load.
*
* `frame.goto` will not throw an error when any valid HTTP status code is
* returned by the remote server, including 404 "Not Found" and 500 "Internal
* Server Error". The status code for such responses can be retrieved by
* calling {@link HTTPResponse.status}.
*
* NOTE: `frame.goto` either throws an error or returns 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 {@link https://bugs.chromium.org/p/chromium/issues/detail?id=761295 | upstream
* issue}.
*
* @param url - the URL to navigate the frame to. This should include the
* scheme, e.g. `https://`.
* @param options - navigation options. `waitUntil` is useful to define when
* the navigation should be considered successful - see the docs for
* {@link PuppeteerLifeCycleEvent} for more details.
*
* @returns A promise which resolves to the main resource response. In case of
* multiple redirects, the navigation will resolve with the response of the
* last redirect.
*/
async goto(url, options = {}) {
return await this._frameManager.navigateFrame(this, url, options);
}
/**
* @remarks
*
* 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([
* // The navigation promise resolves after navigation has finished
* frame.waitForNavigation(),
* // Clicking the link will indirectly cause a navigation
* frame.click('a.my-link'),
* ]);
* ```
*
* Usage of the {@link https://developer.mozilla.org/en-US/docs/Web/API/History_API | History API} to change the URL is considered a navigation.
*
* @param options - options to configure when the navigation is consided finished.
* @returns a promise that resolves when the frame navigates to a new URL.
*/
async waitForNavigation(options = {}) {
return await this._frameManager.waitForFrameNavigation(this, options);
}
/**
* @returns a promise that resolves to the frame's default execution context.
*/
executionContext() {
return this._mainWorld.executionContext();
}
/**
* @remarks
*
* The only difference between {@link Frame.evaluate} and
* `frame.evaluateHandle` is that `evaluateHandle` will return the value
* wrapped in an in-page object.
*
* This method behaves identically to {@link Page.evaluateHandle} except it's
* run within the context of the `frame`, rather than the entire page.
*
* @param pageFunction - a function that is run within the frame
* @param args - arguments to be passed to the pageFunction
*/
async evaluateHandle(pageFunction, ...args) {
return this._mainWorld.evaluateHandle(pageFunction, ...args);
}
/**
* @remarks
*
* This method behaves identically to {@link Page.evaluate} except it's run
* within the context of the `frame`, rather than the entire page.
*
* @param pageFunction - a function that is run within the frame
* @param args - arguments to be passed to the pageFunction
*/
async evaluate(pageFunction, ...args) {
return this._mainWorld.evaluate(pageFunction, ...args);
}
/**
* This method queries the frame for the given selector.
*
* @param selector - a selector to query for.
* @returns A promise which resolves to an `ElementHandle` pointing at the
* element, or `null` if it was not found.
*/
async $(selector) {
return this._mainWorld.$(selector);
}
/**
* This method evaluates the given XPath expression and returns the results.
*
* @param expression - the XPath expression to evaluate.
*/
async $x(expression) {
return this._mainWorld.$x(expression);
}
/**
* @remarks
*
* This method runs `document.querySelector` 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.
*
* @example
*
* ```js
* const searchValue = await frame.$eval('#search', el => el.value);
* ```
*
* @param selector - the selector to query for
* @param pageFunction - the function to be evaluated in the frame's context
* @param args - additional arguments to pass to `pageFuncton`
*/
async $eval(selector, pageFunction, ...args) {
return this._mainWorld.$eval(selector, pageFunction, ...args);
}
/**
* @remarks
*
* 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.
*
* @example
*
* ```js
* const divsCounts = await frame.$$eval('div', divs => divs.length);
* ```
*
* @param selector - the selector to query for
* @param pageFunction - the function to be evaluated in the frame's context
* @param args - additional arguments to pass to `pageFuncton`
*/
async $$eval(selector, pageFunction, ...args) {
return this._mainWorld.$$eval(selector, pageFunction, ...args);
}
/**
* This runs `document.querySelectorAll` in the frame and returns the result.
*
* @param selector - a selector to search for
* @returns An array of element handles pointing to the found frame elements.
*/
async $$(selector) {
return this._mainWorld.$$(selector);
}
/**
* @returns the full HTML contents of the frame, including the doctype.
*/
async content() {
return this._secondaryWorld.content();
}
/**
* Set the content of the frame.
*
* @param html - HTML markup to assign to the page.
* @param options - options to configure how long before timing out and at
* what point to consider the content setting successful.
*/
async setContent(html, options = {}) {
return this._secondaryWorld.setContent(html, options);
}
/**
* @remarks
*
* If the name is empty, it 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.
*
* @returns the frame's `name` attribute as specified in the tag.
*/
name() {
return this._name || '';
}
/**
* @returns the frame's URL.
*/
url() {
return this._url;
}
/**
* @returns the parent `Frame`, if any. Detached and main frames return `null`.
*/
parentFrame() {
return this._parentFrame;
}
/**
* @returns an array of child frames.
*/
childFrames() {
return Array.from(this._childFrames);
}
/**
* @returns `true` if the frame has been detached, or `false` otherwise.
*/
isDetached() {
return this._detached;
}
/**
* Adds a `<script>` tag into the page with the desired url or content.
*
* @param options - configure the script to add to the page.
*
* @returns a promise that resolves to the added tag when the script's
* `onload` event fires or when the script content was injected into the
* frame.
*/
async addScriptTag(options) {
return this._mainWorld.addScriptTag(options);
}
/**
* Adds a `<link rel="stylesheet">` tag into the page with the desired url or
* a `<style type="text/css">` tag with the content.
*
* @param options - configure the CSS to add to the page.
*
* @returns a promise that resolves to the added tag when the stylesheets's
* `onload` event fires or when the CSS content was injected into the
* frame.
*/
async addStyleTag(options) {
return this._mainWorld.addStyleTag(options);
}
/**
*
* This method clicks the first element found that matches `selector`.
*
* @remarks
*
* This method scrolls the element into view if needed, and then uses
* {@link Page.mouse} 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),
* ]);
* ```
* @param selector - the selector to search for to click. If there are
* multiple elements, the first will be clicked.
*/
async click(selector, options = {}) {
return this._secondaryWorld.click(selector, options);
}
/**
* This method fetches an element with `selector` and focuses it.
*
* @remarks
* If there's no element matching `selector`, the method throws an error.
*
* @param selector - the selector for the element to focus. If there are
* multiple elements, the first will be focused.
*/
async focus(selector) {
return this._secondaryWorld.focus(selector);
}
/**
* This method fetches an element with `selector`, scrolls it into view if
* needed, and then uses {@link Page.mouse} to hover over the center of the
* element.
*
* @remarks
* If there's no element matching `selector`, the method throws an
*
* @param selector - the selector for the element to hover. If there are
* multiple elements, the first will be hovered.
*/
async hover(selector) {
return this._secondaryWorld.hover(selector);
}
/**
* Triggers a `change` and `input` event once all the provided options have
* been selected.
*
* @remarks
*
* If there's no `<select>` element matching `selector`, the
* method throws an error.
*
* @example
* ```js
* frame.select('select#colors', 'blue'); // single selection
* frame.select('select#colors', 'red', 'green', 'blue'); // multiple selections
* ```
*
* @param selector - a selector to query the frame for
* @param values - an array of values to select. If the `<select>` has the
* `multiple` attribute, all values are considered, otherwise only the first
* one is taken into account.
* @returns the list of values that were successfully selected.
*/
select(selector, ...values) {
return this._secondaryWorld.select(selector, ...values);
}
/**
* This method fetches an element with `selector`, scrolls it into view if
* needed, and then uses {@link Page.touchscreen} to tap in the center of the
* element.
*
* @remarks
*
* If there's no element matching `selector`, the method throws an error.
*
* @param selector - the selector to tap.
* @returns a promise that resolves when the element has been tapped.
*/
async tap(selector) {
return this._secondaryWorld.tap(selector);
}
/**
* Sends a `keydown`, `keypress`/`input`, and `keyup` event for each character
* in the text.
*
* @remarks
* To press a special key, like `Control` or `ArrowDown`, use
* {@link Keyboard.press}.
*
* @example
* ```js
* await frame.type('#mytextarea', 'Hello'); // Types instantly
* await frame.type('#mytextarea', 'World', {delay: 100}); // Types slower, like a user
* ```
*
* @param selector - the selector for the element to type into. If there are
* multiple the first will be used.
* @param text - text to type into the element
* @param options - takes one option, `delay`, which sets the time to wait
* between key presses in milliseconds. Defaults to `0`.
*
* @returns a promise that resolves when the typing is complete.
*/
async type(selector, text, options) {
return this._mainWorld.type(selector, text, options);
}
/**
* @remarks
*
* This method behaves differently depending on the first parameter. If it's a
* `string`, it will be treated as a `selector` or `xpath` (if the string
* starts with `//`). This method then is a shortcut for
* {@link Frame.waitForSelector} or {@link Frame.waitForXPath}.
*
* If the first argument is a function this method is a shortcut for
* {@link Frame.waitForFunction}.
*
* If the first argument is a `number`, it's treated as a timeout in
* milliseconds and the method returns a promise which resolves after the
* timeout.
*
* @param selectorOrFunctionOrTimeout - a selector, predicate or timeout to
* wait for.
* @param options - optional waiting parameters.
* @param args - arguments to pass to `pageFunction`.
*
* @deprecated Don't use this method directly. Instead use the more explicit
* methods available: {@link Frame.waitForSelector},
* {@link Frame.waitForXPath}, {@link Frame.waitForFunction} or
* {@link Frame.waitForTimeout}.
*/
waitFor(selectorOrFunctionOrTimeout, options = {}, ...args) {
const xPathPattern = '//';
console.warn('waitFor is deprecated and will be removed in a future release. See https://github.com/puppeteer/puppeteer/issues/6214 for details and how to migrate your code.');
if (helper_js_1.helper.isString(selectorOrFunctionOrTimeout)) {
const string = selectorOrFunctionOrTimeout;
if (string.startsWith(xPathPattern))
return this.waitForXPath(string, options);
return this.waitForSelector(string, options);
}
if (helper_js_1.helper.isNumber(selectorOrFunctionOrTimeout))
return new Promise((fulfill) => setTimeout(fulfill, selectorOrFunctionOrTimeout));
if (typeof selectorOrFunctionOrTimeout === 'function')
return this.waitForFunction(selectorOrFunctionOrTimeout, options, ...args);
return Promise.reject(new Error('Unsupported target type: ' + typeof selectorOrFunctionOrTimeout));
}
/**
* Causes your script to wait for the given number of milliseconds.
*
* @remarks
* It's generally recommended to not wait for a number of seconds, but instead
* use {@link Frame.waitForSelector}, {@link Frame.waitForXPath} or
* {@link Frame.waitForFunction} to wait for exactly the conditions you want.
*
* @example
*
* Wait for 1 second:
*
* ```
* await frame.waitForTimeout(1000);
* ```
*
* @param milliseconds - the number of milliseconds to wait.
*/
waitForTimeout(milliseconds) {
return new Promise((resolve) => {
setTimeout(resolve, milliseconds);
});
}
/**
* @remarks
*
*
* 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.
*
* @example
* ```js
* const puppeteer = require('puppeteer');
*
* (async () => {
* const browser = await puppeteer.launch();
* 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();
* })();
* ```
* @param selector - the selector to wait for.
* @param options - options to define if the element should be visible and how
* long to wait before timing out.
* @returns a promise which resolves when an element matching the selector
* string is added to the DOM.
*/
async waitForSelector(selector, options = {}) {
const handle = await this._secondaryWorld.waitForSelector(selector, options);
if (!handle)
return null;
const mainExecutionContext = await this._mainWorld.executionContext();
const result = await mainExecutionContext._adoptElementHandle(handle);
await handle.dispose();
return result;
}
/**
* @remarks
* 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.
*
* For a code example, see the example for {@link Frame.waitForSelector}. That
* function behaves identically other than taking a CSS selector rather than
* an XPath.
*
* @param xpath - the XPath expression to wait for.
* @param options - options to configure the visiblity of the element and how
* long to wait before timing out.
*/
async waitForXPath(xpath, options = {}) {
const handle = await this._secondaryWorld.waitForXPath(xpath, options);
if (!handle)
return null;
const mainExecutionContext = await this._mainWorld.executionContext();
const result = await mainExecutionContext._adoptElementHandle(handle);
await handle.dispose();
return result;
}
/**
* @remarks
*
* @example
*
* The `waitForFunction` can be used to observe viewport size change:
* ```js
* const puppeteer = require('puppeteer');
*
* (async () => {
* . const browser = await puppeteer.launch();
* . 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 frame.waitForFunction(
* selector => !!document.querySelector(selector),
* {}, // empty options object
* selector
*);
* ```
*
* @param pageFunction - the function to evaluate in the frame context.
* @param options - options to configure the polling method and timeout.
* @param args - arguments to pass to the `pageFunction`.
* @returns the promise which resolve when the `pageFunction` returns a truthy value.
*/
waitForFunction(pageFunction, options = {}, ...args) {
return this._mainWorld.waitForFunction(pageFunction, options, ...args);
}
/**
* @returns the frame's title.
*/
async title() {
return this._secondaryWorld.title();
}
/**
* @internal
*/
_navigated(framePayload) {
this._name = framePayload.name;
this._url = `${framePayload.url}${framePayload.urlFragment || ''}`;
}
/**
* @internal
*/
_navigatedWithinDocument(url) {
this._url = url;
}
/**
* @internal
*/
_onLifecycleEvent(loaderId, name) {
if (name === 'init') {
this._loaderId = loaderId;
this._lifecycleEvents.clear();
}
this._lifecycleEvents.add(name);
}
/**
* @internal
*/
_onLoadingStopped() {
this._lifecycleEvents.add('DOMContentLoaded');
this._lifecycleEvents.add('load');
}
/**
* @internal
*/
_detach() {
this._detached = true;
this._mainWorld._detach();
this._secondaryWorld._detach();
if (this._parentFrame)
this._parentFrame._childFrames.delete(this);
this._parentFrame = null;
}
}
exports.Frame = Frame;
function assertNoLegacyNavigationOptions(options) {
assert_js_1.assert(options['networkIdleTimeout'] === undefined, 'ERROR: networkIdleTimeout option is no longer supported.');
assert_js_1.assert(options['networkIdleInflight'] === undefined, 'ERROR: networkIdleInflight option is no longer supported.');
assert_js_1.assert(options.waitUntil !== 'networkidle', 'ERROR: "networkidle" option is no longer supported. Use "networkidle2" instead');
}
+274
View File
@@ -0,0 +1,274 @@
/// <reference types="node" />
/**
* Copyright 2020 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.
*/
import { CDPSession } from './Connection.js';
import { Frame } from './FrameManager.js';
import { HTTPResponse } from './HTTPResponse.js';
import { Protocol } from 'devtools-protocol';
/**
* @public
*/
export interface ContinueRequestOverrides {
/**
* If set, the request URL will change. This is not a redirect.
*/
url?: string;
method?: string;
postData?: string;
headers?: Record<string, string>;
}
/**
* Required response data to fulfill a request with.
*
* @public
*/
export interface ResponseForRequest {
status: number;
headers: Record<string, string>;
contentType: string;
body: string | Buffer;
}
/**
*
* Represents an HTTP request sent by a page.
* @remarks
*
* Whenever the page sends a request, such as for a network resource, the
* following events are emitted by Puppeteer's `page`:
*
* - `request`: emitted when the request is issued by the page.
* - `requestfinished` - emitted when the response body is downloaded and the
* request is complete.
*
* If request fails at some point, then instead of `requestfinished` event the
* `requestfailed` event is emitted.
*
* All of these events provide an instance of `HTTPRequest` representing the
* request that occurred:
*
* ```
* page.on('request', request => ...)
* ```
*
* NOTE: HTTP Error responses, such as 404 or 503, are still successful
* responses from HTTP standpoint, so request will complete with
* `requestfinished` event.
*
* 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.
*
* @public
*/
export declare class HTTPRequest {
/**
* @internal
*/
_requestId: string;
/**
* @internal
*/
_interceptionId: string;
/**
* @internal
*/
_failureText: any;
/**
* @internal
*/
_response: HTTPResponse | null;
/**
* @internal
*/
_fromMemoryCache: boolean;
/**
* @internal
*/
_redirectChain: HTTPRequest[];
private _client;
private _isNavigationRequest;
private _allowInterception;
private _interceptionHandled;
private _url;
private _resourceType;
private _method;
private _postData?;
private _headers;
private _frame;
/**
* @internal
*/
constructor(client: CDPSession, frame: Frame, interceptionId: string, allowInterception: boolean, event: Protocol.Network.RequestWillBeSentEvent, redirectChain: HTTPRequest[]);
/**
* @returns the URL of the request
*/
url(): string;
/**
* Contains the request's resource type as it was perceived by the rendering
* engine.
* @remarks
* @returns one of the following: `document`, `stylesheet`, `image`, `media`,
* `font`, `script`, `texttrack`, `xhr`, `fetch`, `eventsource`, `websocket`,
* `manifest`, `other`.
*/
resourceType(): string;
/**
* @returns the method used (`GET`, `POST`, etc.)
*/
method(): string;
/**
* @returns the request's post body, if any.
*/
postData(): string | undefined;
/**
* @returns an object with HTTP headers associated with the request. All
* header names are lower-case.
*/
headers(): Record<string, string>;
/**
* @returns the response for this request, if a response has been received.
*/
response(): HTTPResponse | null;
/**
* @returns the frame that initiated the request.
*/
frame(): Frame | null;
/**
* @returns true if the request is the driver of the current frame's navigation.
*/
isNavigationRequest(): boolean;
/**
* @remarks
*
* `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
* ```
*
* @returns the chain of requests - if a server responds with at least a
* single redirect, this chain will contain all requests that were redirected.
*/
redirectChain(): HTTPRequest[];
/**
* Access information about the request's failure.
*
* @remarks
*
* @example
*
* Example of logging all failed requests:
*
* ```js
* page.on('requestfailed', request => {
* console.log(request.url() + ' ' + request.failure().errorText);
* });
* ```
*
* @returns `null` unless the request failed. If the request fails this can
* return an object with `errorText` containing a human-readable error
* message, e.g. `net::ERR_FAILED`. It is not guaranteeded that there will be
* failure text if the request fails.
*/
failure(): {
errorText: string;
} | null;
/**
* Continues request with optional request overrides.
*
* @remarks
*
* To use this, request
* interception should be enabled with {@link Page.setRequestInterception}.
*
* Exception is immediately thrown if the request interception is not enabled.
*
* @example
* ```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});
* });
* ```
*
* @param overrides - optional overrides to apply to the request.
*/
continue(overrides?: ContinueRequestOverrides): Promise<void>;
/**
* Fulfills a request with the given response.
*
* @remarks
*
* To use this, request
* interception should be enabled with {@link Page.setRequestInterception}.
*
* Exception is immediately thrown if the request interception is not enabled.
*
* @example
* 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.
*
* @param response - the response to fulfill the request with.
*/
respond(response: ResponseForRequest): Promise<void>;
/**
* Aborts a request.
*
* @remarks
* To use this, request interception should be enabled with
* {@link Page.setRequestInterception}. If it is not enabled, this method will
* throw an exception immediately.
*
* @param errorCode - optional error code to provide.
*/
abort(errorCode?: ErrorCode): Promise<void>;
}
/**
* @public
*/
export declare type ErrorCode = 'aborted' | 'accessdenied' | 'addressunreachable' | 'blockedbyclient' | 'blockedbyresponse' | 'connectionaborted' | 'connectionclosed' | 'connectionfailed' | 'connectionrefused' | 'connectionreset' | 'internetdisconnected' | 'namenotresolved' | 'timedout' | 'failed';
//# sourceMappingURL=HTTPRequest.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"HTTPRequest.d.ts","sourceRoot":"","sources":["../../../../src/common/HTTPRequest.ts"],"names":[],"mappings":"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"}
+424
View File
@@ -0,0 +1,424 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.HTTPRequest = void 0;
const assert_js_1 = require("./assert.js");
const helper_js_1 = require("./helper.js");
/**
*
* Represents an HTTP request sent by a page.
* @remarks
*
* Whenever the page sends a request, such as for a network resource, the
* following events are emitted by Puppeteer's `page`:
*
* - `request`: emitted when the request is issued by the page.
* - `requestfinished` - emitted when the response body is downloaded and the
* request is complete.
*
* If request fails at some point, then instead of `requestfinished` event the
* `requestfailed` event is emitted.
*
* All of these events provide an instance of `HTTPRequest` representing the
* request that occurred:
*
* ```
* page.on('request', request => ...)
* ```
*
* NOTE: HTTP Error responses, such as 404 or 503, are still successful
* responses from HTTP standpoint, so request will complete with
* `requestfinished` event.
*
* 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.
*
* @public
*/
class HTTPRequest {
/**
* @internal
*/
constructor(client, frame, interceptionId, allowInterception, event, redirectChain) {
/**
* @internal
*/
this._failureText = null;
/**
* @internal
*/
this._response = null;
/**
* @internal
*/
this._fromMemoryCache = false;
this._interceptionHandled = false;
this._headers = {};
this._client = client;
this._requestId = event.requestId;
this._isNavigationRequest =
event.requestId === event.loaderId && event.type === 'Document';
this._interceptionId = interceptionId;
this._allowInterception = allowInterception;
this._url = event.request.url;
this._resourceType = event.type.toLowerCase();
this._method = event.request.method;
this._postData = event.request.postData;
this._frame = frame;
this._redirectChain = redirectChain;
for (const key of Object.keys(event.request.headers))
this._headers[key.toLowerCase()] = event.request.headers[key];
}
/**
* @returns the URL of the request
*/
url() {
return this._url;
}
/**
* Contains the request's resource type as it was perceived by the rendering
* engine.
* @remarks
* @returns one of the following: `document`, `stylesheet`, `image`, `media`,
* `font`, `script`, `texttrack`, `xhr`, `fetch`, `eventsource`, `websocket`,
* `manifest`, `other`.
*/
resourceType() {
// TODO (@jackfranklin): protocol.d.ts has a type for this, but all the
// string values are uppercase. The Puppeteer docs explicitly say the
// potential values are all lower case, and the constructor takes the event
// type and calls toLowerCase() on it, so we can't reuse the type from the
// protocol.d.ts. Why do we lower case?
return this._resourceType;
}
/**
* @returns the method used (`GET`, `POST`, etc.)
*/
method() {
return this._method;
}
/**
* @returns the request's post body, if any.
*/
postData() {
return this._postData;
}
/**
* @returns an object with HTTP headers associated with the request. All
* header names are lower-case.
*/
headers() {
return this._headers;
}
/**
* @returns the response for this request, if a response has been received.
*/
response() {
return this._response;
}
/**
* @returns the frame that initiated the request.
*/
frame() {
return this._frame;
}
/**
* @returns true if the request is the driver of the current frame's navigation.
*/
isNavigationRequest() {
return this._isNavigationRequest;
}
/**
* @remarks
*
* `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
* ```
*
* @returns the chain of requests - if a server responds with at least a
* single redirect, this chain will contain all requests that were redirected.
*/
redirectChain() {
return this._redirectChain.slice();
}
/**
* Access information about the request's failure.
*
* @remarks
*
* @example
*
* Example of logging all failed requests:
*
* ```js
* page.on('requestfailed', request => {
* console.log(request.url() + ' ' + request.failure().errorText);
* });
* ```
*
* @returns `null` unless the request failed. If the request fails this can
* return an object with `errorText` containing a human-readable error
* message, e.g. `net::ERR_FAILED`. It is not guaranteeded that there will be
* failure text if the request fails.
*/
failure() {
if (!this._failureText)
return null;
return {
errorText: this._failureText,
};
}
/**
* Continues request with optional request overrides.
*
* @remarks
*
* To use this, request
* interception should be enabled with {@link Page.setRequestInterception}.
*
* Exception is immediately thrown if the request interception is not enabled.
*
* @example
* ```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});
* });
* ```
*
* @param overrides - optional overrides to apply to the request.
*/
async continue(overrides = {}) {
// Request interception is not supported for data: urls.
if (this._url.startsWith('data:'))
return;
assert_js_1.assert(this._allowInterception, 'Request Interception is not enabled!');
assert_js_1.assert(!this._interceptionHandled, 'Request is already handled!');
const { url, method, postData, headers } = overrides;
this._interceptionHandled = true;
const postDataBinaryBase64 = postData
? Buffer.from(postData).toString('base64')
: undefined;
await this._client
.send('Fetch.continueRequest', {
requestId: this._interceptionId,
url,
method,
postData: postDataBinaryBase64,
headers: headers ? headersArray(headers) : undefined,
})
.catch((error) => {
// In certain cases, protocol will return error if the request was
// already canceled or the page was closed. We should tolerate these
// errors.
helper_js_1.debugError(error);
});
}
/**
* Fulfills a request with the given response.
*
* @remarks
*
* To use this, request
* interception should be enabled with {@link Page.setRequestInterception}.
*
* Exception is immediately thrown if the request interception is not enabled.
*
* @example
* 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.
*
* @param response - the response to fulfill the request with.
*/
async respond(response) {
// Mocking responses for dataURL requests is not currently supported.
if (this._url.startsWith('data:'))
return;
assert_js_1.assert(this._allowInterception, 'Request Interception is not enabled!');
assert_js_1.assert(!this._interceptionHandled, 'Request is already handled!');
this._interceptionHandled = true;
const responseBody = response.body && helper_js_1.helper.isString(response.body)
? Buffer.from(response.body)
: response.body || null;
const responseHeaders = {};
if (response.headers) {
for (const header of Object.keys(response.headers))
responseHeaders[header.toLowerCase()] = response.headers[header];
}
if (response.contentType)
responseHeaders['content-type'] = response.contentType;
if (responseBody && !('content-length' in responseHeaders))
responseHeaders['content-length'] = String(Buffer.byteLength(responseBody));
await this._client
.send('Fetch.fulfillRequest', {
requestId: this._interceptionId,
responseCode: response.status || 200,
responsePhrase: STATUS_TEXTS[response.status || 200],
responseHeaders: headersArray(responseHeaders),
body: responseBody ? responseBody.toString('base64') : undefined,
})
.catch((error) => {
// In certain cases, protocol will return error if the request was
// already canceled or the page was closed. We should tolerate these
// errors.
helper_js_1.debugError(error);
});
}
/**
* Aborts a request.
*
* @remarks
* To use this, request interception should be enabled with
* {@link Page.setRequestInterception}. If it is not enabled, this method will
* throw an exception immediately.
*
* @param errorCode - optional error code to provide.
*/
async abort(errorCode = 'failed') {
// Request interception is not supported for data: urls.
if (this._url.startsWith('data:'))
return;
const errorReason = errorReasons[errorCode];
assert_js_1.assert(errorReason, 'Unknown error code: ' + errorCode);
assert_js_1.assert(this._allowInterception, 'Request Interception is not enabled!');
assert_js_1.assert(!this._interceptionHandled, 'Request is already handled!');
this._interceptionHandled = true;
await this._client
.send('Fetch.failRequest', {
requestId: this._interceptionId,
errorReason,
})
.catch((error) => {
// In certain cases, protocol will return error if the request was
// already canceled or the page was closed. We should tolerate these
// errors.
helper_js_1.debugError(error);
});
}
}
exports.HTTPRequest = HTTPRequest;
const errorReasons = {
aborted: 'Aborted',
accessdenied: 'AccessDenied',
addressunreachable: 'AddressUnreachable',
blockedbyclient: 'BlockedByClient',
blockedbyresponse: 'BlockedByResponse',
connectionaborted: 'ConnectionAborted',
connectionclosed: 'ConnectionClosed',
connectionfailed: 'ConnectionFailed',
connectionrefused: 'ConnectionRefused',
connectionreset: 'ConnectionReset',
internetdisconnected: 'InternetDisconnected',
namenotresolved: 'NameNotResolved',
timedout: 'TimedOut',
failed: 'Failed',
};
function headersArray(headers) {
const result = [];
for (const name in headers) {
if (!Object.is(headers[name], undefined))
result.push({ name, value: headers[name] + '' });
}
return result;
}
// List taken from
// https://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml
// with extra 306 and 418 codes.
const STATUS_TEXTS = {
'100': 'Continue',
'101': 'Switching Protocols',
'102': 'Processing',
'103': 'Early Hints',
'200': 'OK',
'201': 'Created',
'202': 'Accepted',
'203': 'Non-Authoritative Information',
'204': 'No Content',
'205': 'Reset Content',
'206': 'Partial Content',
'207': 'Multi-Status',
'208': 'Already Reported',
'226': 'IM Used',
'300': 'Multiple Choices',
'301': 'Moved Permanently',
'302': 'Found',
'303': 'See Other',
'304': 'Not Modified',
'305': 'Use Proxy',
'306': 'Switch Proxy',
'307': 'Temporary Redirect',
'308': 'Permanent Redirect',
'400': 'Bad Request',
'401': 'Unauthorized',
'402': 'Payment Required',
'403': 'Forbidden',
'404': 'Not Found',
'405': 'Method Not Allowed',
'406': 'Not Acceptable',
'407': 'Proxy Authentication Required',
'408': 'Request Timeout',
'409': 'Conflict',
'410': 'Gone',
'411': 'Length Required',
'412': 'Precondition Failed',
'413': 'Payload Too Large',
'414': 'URI Too Long',
'415': 'Unsupported Media Type',
'416': 'Range Not Satisfiable',
'417': 'Expectation Failed',
'418': "I'm a teapot",
'421': 'Misdirected Request',
'422': 'Unprocessable Entity',
'423': 'Locked',
'424': 'Failed Dependency',
'425': 'Too Early',
'426': 'Upgrade Required',
'428': 'Precondition Required',
'429': 'Too Many Requests',
'431': 'Request Header Fields Too Large',
'451': 'Unavailable For Legal Reasons',
'500': 'Internal Server Error',
'501': 'Not Implemented',
'502': 'Bad Gateway',
'503': 'Service Unavailable',
'504': 'Gateway Timeout',
'505': 'HTTP Version Not Supported',
'506': 'Variant Also Negotiates',
'507': 'Insufficient Storage',
'508': 'Loop Detected',
'510': 'Not Extended',
'511': 'Network Authentication Required',
};
+126
View File
@@ -0,0 +1,126 @@
/// <reference types="node" />
/**
* Copyright 2020 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.
*/
import { CDPSession } from './Connection.js';
import { Frame } from './FrameManager.js';
import { HTTPRequest } from './HTTPRequest.js';
import { SecurityDetails } from './SecurityDetails.js';
import { Protocol } from 'devtools-protocol';
/**
* @public
*/
export interface RemoteAddress {
ip: string;
port: number;
}
/**
* The HTTPResponse class represents responses which are received by the
* {@link Page} class.
*
* @public
*/
export declare class HTTPResponse {
private _client;
private _request;
private _contentPromise;
private _bodyLoadedPromise;
private _bodyLoadedPromiseFulfill;
private _remoteAddress;
private _status;
private _statusText;
private _url;
private _fromDiskCache;
private _fromServiceWorker;
private _headers;
private _securityDetails;
/**
* @internal
*/
constructor(client: CDPSession, request: HTTPRequest, responsePayload: Protocol.Network.Response);
/**
* @internal
*/
_resolveBody(err: Error | null): void;
/**
* @returns The IP address and port number used to connect to the remote
* server.
*/
remoteAddress(): RemoteAddress;
/**
* @returns The URL of the response.
*/
url(): string;
/**
* @returns True if the response was successful (status in the range 200-299).
*/
ok(): boolean;
/**
* @returns The status code of the response (e.g., 200 for a success).
*/
status(): number;
/**
* @returns The status text of the response (e.g. usually an "OK" for a
* success).
*/
statusText(): string;
/**
* @returns An object with HTTP headers associated with the response. All
* header names are lower-case.
*/
headers(): Record<string, string>;
/**
* @returns {@link SecurityDetails} if the response was received over the
* secure connection, or `null` otherwise.
*/
securityDetails(): SecurityDetails | null;
/**
* @returns Promise which resolves to a buffer with response body.
*/
buffer(): Promise<Buffer>;
/**
* @returns Promise which resolves to a text representation of response body.
*/
text(): Promise<string>;
/**
*
* @returns Promise which resolves to a JSON representation of response body.
*
* @remarks
*
* This method will throw if the response body is not parsable via
* `JSON.parse`.
*/
json(): Promise<any>;
/**
* @returns A matching {@link HTTPRequest} object.
*/
request(): HTTPRequest;
/**
* @returns True if the response was served from either the browser's disk
* cache or memory cache.
*/
fromCache(): boolean;
/**
* @returns True if the response was served by a service worker.
*/
fromServiceWorker(): boolean;
/**
* @returns A {@link Frame} that initiated this response, or `null` if
* navigating to error pages.
*/
frame(): Frame | null;
}
//# sourceMappingURL=HTTPResponse.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"HTTPResponse.d.ts","sourceRoot":"","sources":["../../../../src/common/HTTPResponse.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAC1C,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAE7C;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;;;;GAKG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,OAAO,CAAa;IAC5B,OAAO,CAAC,QAAQ,CAAc;IAC9B,OAAO,CAAC,eAAe,CAAgC;IACvD,OAAO,CAAC,kBAAkB,CAAwB;IAClD,OAAO,CAAC,yBAAyB,CAA8B;IAC/D,OAAO,CAAC,cAAc,CAAgB;IACtC,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,IAAI,CAAS;IACrB,OAAO,CAAC,cAAc,CAAU;IAChC,OAAO,CAAC,kBAAkB,CAAU;IACpC,OAAO,CAAC,QAAQ,CAA8B;IAC9C,OAAO,CAAC,gBAAgB,CAAyB;IAEjD;;OAEG;gBAED,MAAM,EAAE,UAAU,EAClB,OAAO,EAAE,WAAW,EACpB,eAAe,EAAE,QAAQ,CAAC,OAAO,CAAC,QAAQ;IAyB5C;;OAEG;IACH,YAAY,CAAC,GAAG,EAAE,KAAK,GAAG,IAAI,GAAG,IAAI;IAIrC;;;OAGG;IACH,aAAa,IAAI,aAAa;IAI9B;;OAEG;IACH,GAAG,IAAI,MAAM;IAIb;;OAEG;IACH,EAAE,IAAI,OAAO;IAKb;;OAEG;IACH,MAAM,IAAI,MAAM;IAIhB;;;OAGG;IACH,UAAU,IAAI,MAAM;IAIpB;;;OAGG;IACH,OAAO,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;IAIjC;;;OAGG;IACH,eAAe,IAAI,eAAe,GAAG,IAAI;IAIzC;;OAEG;IACH,MAAM,IAAI,OAAO,CAAC,MAAM,CAAC;IAgBzB;;OAEG;IACG,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC;IAK7B;;;;;;;;OAQG;IACG,IAAI,IAAI,OAAO,CAAC,GAAG,CAAC;IAK1B;;OAEG;IACH,OAAO,IAAI,WAAW;IAItB;;;OAGG;IACH,SAAS,IAAI,OAAO;IAIpB;;OAEG;IACH,iBAAiB,IAAI,OAAO;IAI5B;;;OAGG;IACH,KAAK,IAAI,KAAK,GAAG,IAAI;CAGtB"}
+154
View File
@@ -0,0 +1,154 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.HTTPResponse = void 0;
const SecurityDetails_js_1 = require("./SecurityDetails.js");
/**
* The HTTPResponse class represents responses which are received by the
* {@link Page} class.
*
* @public
*/
class HTTPResponse {
/**
* @internal
*/
constructor(client, request, responsePayload) {
this._contentPromise = null;
this._headers = {};
this._client = client;
this._request = request;
this._bodyLoadedPromise = new Promise((fulfill) => {
this._bodyLoadedPromiseFulfill = fulfill;
});
this._remoteAddress = {
ip: responsePayload.remoteIPAddress,
port: responsePayload.remotePort,
};
this._status = responsePayload.status;
this._statusText = responsePayload.statusText;
this._url = request.url();
this._fromDiskCache = !!responsePayload.fromDiskCache;
this._fromServiceWorker = !!responsePayload.fromServiceWorker;
for (const key of Object.keys(responsePayload.headers))
this._headers[key.toLowerCase()] = responsePayload.headers[key];
this._securityDetails = responsePayload.securityDetails
? new SecurityDetails_js_1.SecurityDetails(responsePayload.securityDetails)
: null;
}
/**
* @internal
*/
_resolveBody(err) {
return this._bodyLoadedPromiseFulfill(err);
}
/**
* @returns The IP address and port number used to connect to the remote
* server.
*/
remoteAddress() {
return this._remoteAddress;
}
/**
* @returns The URL of the response.
*/
url() {
return this._url;
}
/**
* @returns True if the response was successful (status in the range 200-299).
*/
ok() {
// TODO: document === 0 case?
return this._status === 0 || (this._status >= 200 && this._status <= 299);
}
/**
* @returns The status code of the response (e.g., 200 for a success).
*/
status() {
return this._status;
}
/**
* @returns The status text of the response (e.g. usually an "OK" for a
* success).
*/
statusText() {
return this._statusText;
}
/**
* @returns An object with HTTP headers associated with the response. All
* header names are lower-case.
*/
headers() {
return this._headers;
}
/**
* @returns {@link SecurityDetails} if the response was received over the
* secure connection, or `null` otherwise.
*/
securityDetails() {
return this._securityDetails;
}
/**
* @returns Promise which resolves to a buffer with response body.
*/
buffer() {
if (!this._contentPromise) {
this._contentPromise = this._bodyLoadedPromise.then(async (error) => {
if (error)
throw error;
const response = await this._client.send('Network.getResponseBody', {
requestId: this._request._requestId,
});
return Buffer.from(response.body, response.base64Encoded ? 'base64' : 'utf8');
});
}
return this._contentPromise;
}
/**
* @returns Promise which resolves to a text representation of response body.
*/
async text() {
const content = await this.buffer();
return content.toString('utf8');
}
/**
*
* @returns Promise which resolves to a JSON representation of response body.
*
* @remarks
*
* This method will throw if the response body is not parsable via
* `JSON.parse`.
*/
async json() {
const content = await this.text();
return JSON.parse(content);
}
/**
* @returns A matching {@link HTTPRequest} object.
*/
request() {
return this._request;
}
/**
* @returns True if the response was served from either the browser's disk
* cache or memory cache.
*/
fromCache() {
return this._fromDiskCache || this._request._fromMemoryCache;
}
/**
* @returns True if the response was served by a service worker.
*/
fromServiceWorker() {
return this._fromServiceWorker;
}
/**
* @returns A {@link Frame} that initiated this response, or `null` if
* navigating to error pages.
*/
frame() {
return this._request.frame();
}
}
exports.HTTPResponse = HTTPResponse;
+322
View File
@@ -0,0 +1,322 @@
/**
* 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.
*/
import { CDPSession } from './Connection.js';
import { KeyInput } from './USKeyboardLayout.js';
/**
* Keyboard provides an api for managing a virtual keyboard.
* The high level api is {@link Keyboard."type"},
* which takes raw characters and generates proper keydown, keypress/input,
* and keyup events on your page.
*
* @remarks
* For finer control, you can use {@link Keyboard.down},
* {@link Keyboard.up}, and {@link Keyboard.sendCharacter}
* to manually fire events as if they were generated from a real keyboard.
*
* On MacOS, keyboard shortcuts like `⌘ A` -\> Select All do not work.
* See {@link https://github.com/puppeteer/puppeteer/issues/1313 | #1313}.
*
* @example
* 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!'
* ```
*
* @example
* An example of pressing `A`
* ```js
* await page.keyboard.down('Shift');
* await page.keyboard.press('KeyA');
* await page.keyboard.up('Shift');
* ```
*
* @public
*/
export declare class Keyboard {
private _client;
/** @internal */
_modifiers: number;
private _pressedKeys;
/** @internal */
constructor(client: CDPSession);
/**
* Dispatches a `keydown` event.
*
* @remarks
* 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 {@link Keyboard.up}.
*
* After the key is pressed once, subsequent calls to
* {@link Keyboard.down} will have
* {@link https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/repeat | repeat}
* set to true. To release the key, use {@link Keyboard.up}.
*
* Modifier keys DO influence {@link Keyboard.down}.
* Holding down `Shift` will type the text in upper case.
*
* @param key - Name of key to press, such as `ArrowLeft`.
* See {@link KeyInput} for a list of all key names.
*
* @param options - An object of options. Accepts text which, if specified,
* generates an input event with this text.
*/
down(key: KeyInput, options?: {
text?: string;
}): Promise<void>;
private _modifierBit;
private _keyDescriptionForString;
/**
* Dispatches a `keyup` event.
*
* @param key - Name of key to release, such as `ArrowLeft`.
* See {@link KeyInput | KeyInput}
* for a list of all key names.
*/
up(key: KeyInput): Promise<void>;
/**
* Dispatches a `keypress` and `input` event.
* This does not send a `keydown` or `keyup` event.
*
* @remarks
* Modifier keys DO NOT effect {@link Keyboard.sendCharacter | Keyboard.sendCharacter}.
* Holding down `Shift` will not type the text in upper case.
*
* @example
* ```js
* page.keyboard.sendCharacter('嗨');
* ```
*
* @param char - Character to send into the page.
*/
sendCharacter(char: string): Promise<void>;
private charIsKey;
/**
* Sends a `keydown`, `keypress`/`input`,
* and `keyup` event for each character in the text.
*
* @remarks
* To press a special key, like `Control` or `ArrowDown`,
* use {@link Keyboard.press}.
*
* Modifier keys DO NOT effect `keyboard.type`.
* Holding down `Shift` will not type the text in upper case.
*
* @example
* ```js
* await page.keyboard.type('Hello'); // Types instantly
* await page.keyboard.type('World', {delay: 100}); // Types slower, like a user
* ```
*
* @param text - A text to type into a focused element.
* @param options - An object of options. Accepts delay which,
* if specified, is the time to wait between `keydown` and `keyup` in milliseconds.
* Defaults to 0.
*/
type(text: string, options?: {
delay?: number;
}): Promise<void>;
/**
* Shortcut for {@link Keyboard.down}
* and {@link Keyboard.up}.
*
* @remarks
* 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.
*
* Modifier keys DO effect {@link Keyboard.press}.
* Holding down `Shift` will type the text in upper case.
*
* @param key - Name of key to press, such as `ArrowLeft`.
* See {@link KeyInput} for a list of all key names.
*
* @param options - An object of options. Accepts text which, if specified,
* generates an input event with this text. Accepts delay which,
* if specified, is the time to wait between `keydown` and `keyup` in milliseconds.
* Defaults to 0.
*/
press(key: KeyInput, options?: {
delay?: number;
text?: string;
}): Promise<void>;
}
/**
* @public
*/
export declare type MouseButton = 'left' | 'right' | 'middle';
/**
* @public
*/
export interface MouseOptions {
button?: MouseButton;
clickCount?: number;
}
/**
* @public
*/
export interface MouseWheelOptions {
deltaX?: number;
deltaY?: number;
}
/**
* The Mouse class operates in main-frame CSS pixels
* relative to the top-left corner of the viewport.
* @remarks
* Every `page` object has its own Mouse, accessible with [`page.mouse`](#pagemouse).
*
* @example
* ```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();
* ```
*
* **Note**: The mouse events trigger synthetic `MouseEvent`s.
* This means that it does not fully replicate the functionality of what a normal user
* would be able to do with their mouse.
*
* For example, dragging and selecting text is not possible using `page.mouse`.
* Instead, you can use the {@link https://developer.mozilla.org/en-US/docs/Web/API/DocumentOrShadowRoot/getSelection | `DocumentOrShadowRoot.getSelection()`} functionality implemented in the platform.
*
* @example
* For example, if you want to select all content between nodes:
* ```js
* await page.evaluate((from, to) => {
* const selection = from.getRootNode().getSelection();
* const range = document.createRange();
* range.setStartBefore(from);
* range.setEndAfter(to);
* selection.removeAllRanges();
* selection.addRange(range);
* }, fromJSHandle, toJSHandle);
* ```
* If you then would want to copy-paste your selection, you can use the clipboard api:
* ```js
* // The clipboard api does not allow you to copy, unless the tab is focused.
* await page.bringToFront();
* await page.evaluate(() => {
* // Copy the selected content to the clipboard
* document.execCommand('copy');
* // Obtain the content of the clipboard as a string
* return navigator.clipboard.readText();
* });
* ```
* **Note**: If you want access to the clipboard API,
* you have to give it permission to do so:
* ```js
* await browser.defaultBrowserContext().overridePermissions(
* '<your origin>', ['clipboard-read', 'clipboard-write']
* );
* ```
* @public
*/
export declare class Mouse {
private _client;
private _keyboard;
private _x;
private _y;
private _button;
/**
* @internal
*/
constructor(client: CDPSession, keyboard: Keyboard);
/**
* Dispatches a `mousemove` event.
* @param x - Horizontal position of the mouse.
* @param y - Vertical position of the mouse.
* @param options - Optional object. If specified, the `steps` property
* sends intermediate `mousemove` events when set to `1` (default).
*/
move(x: number, y: number, options?: {
steps?: number;
}): Promise<void>;
/**
* Shortcut for `mouse.move`, `mouse.down` and `mouse.up`.
* @param x - Horizontal position of the mouse.
* @param y - Vertical position of the mouse.
* @param options - Optional `MouseOptions`.
*/
click(x: number, y: number, options?: MouseOptions & {
delay?: number;
}): Promise<void>;
/**
* Dispatches a `mousedown` event.
* @param options - Optional `MouseOptions`.
*/
down(options?: MouseOptions): Promise<void>;
/**
* Dispatches a `mouseup` event.
* @param options - Optional `MouseOptions`.
*/
up(options?: MouseOptions): Promise<void>;
/**
* Dispatches a `mousewheel` event.
* @param options - Optional: `MouseWheelOptions`.
*
* @example
* An example of zooming into an element:
* ```js
* await page.goto('https://mdn.mozillademos.org/en-US/docs/Web/API/Element/wheel_event$samples/Scaling_an_element_via_the_wheel?revision=1587366');
*
* const elem = await page.$('div');
* const boundingBox = await elem.boundingBox();
* await page.mouse.move(
* boundingBox.x + boundingBox.width / 2,
* boundingBox.y + boundingBox.height / 2
* );
*
* await page.mouse.wheel({ deltaY: -100 })
* ```
*/
wheel(options?: MouseWheelOptions): Promise<void>;
}
/**
* The Touchscreen class exposes touchscreen events.
* @public
*/
export declare class Touchscreen {
private _client;
private _keyboard;
/**
* @internal
*/
constructor(client: CDPSession, keyboard: Keyboard);
/**
* Dispatches a `touchstart` and `touchend` event.
* @param x - Horizontal position of the tap.
* @param y - Vertical position of the tap.
*/
tap(x: number, y: number): Promise<void>;
}
//# sourceMappingURL=Input.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"Input.d.ts","sourceRoot":"","sources":["../../../../src/common/Input.ts"],"names":[],"mappings":"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"}
+469
View File
@@ -0,0 +1,469 @@
"use strict";
/**
* 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.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.Touchscreen = exports.Mouse = exports.Keyboard = void 0;
const assert_js_1 = require("./assert.js");
const USKeyboardLayout_js_1 = require("./USKeyboardLayout.js");
/**
* Keyboard provides an api for managing a virtual keyboard.
* The high level api is {@link Keyboard."type"},
* which takes raw characters and generates proper keydown, keypress/input,
* and keyup events on your page.
*
* @remarks
* For finer control, you can use {@link Keyboard.down},
* {@link Keyboard.up}, and {@link Keyboard.sendCharacter}
* to manually fire events as if they were generated from a real keyboard.
*
* On MacOS, keyboard shortcuts like `⌘ A` -\> Select All do not work.
* See {@link https://github.com/puppeteer/puppeteer/issues/1313 | #1313}.
*
* @example
* 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!'
* ```
*
* @example
* An example of pressing `A`
* ```js
* await page.keyboard.down('Shift');
* await page.keyboard.press('KeyA');
* await page.keyboard.up('Shift');
* ```
*
* @public
*/
class Keyboard {
/** @internal */
constructor(client) {
/** @internal */
this._modifiers = 0;
this._pressedKeys = new Set();
this._client = client;
}
/**
* Dispatches a `keydown` event.
*
* @remarks
* 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 {@link Keyboard.up}.
*
* After the key is pressed once, subsequent calls to
* {@link Keyboard.down} will have
* {@link https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/repeat | repeat}
* set to true. To release the key, use {@link Keyboard.up}.
*
* Modifier keys DO influence {@link Keyboard.down}.
* Holding down `Shift` will type the text in upper case.
*
* @param key - Name of key to press, such as `ArrowLeft`.
* See {@link KeyInput} for a list of all key names.
*
* @param options - An object of options. Accepts text which, if specified,
* generates an input event with this text.
*/
async down(key, options = { text: undefined }) {
const description = this._keyDescriptionForString(key);
const autoRepeat = this._pressedKeys.has(description.code);
this._pressedKeys.add(description.code);
this._modifiers |= this._modifierBit(description.key);
const text = options.text === undefined ? description.text : options.text;
await this._client.send('Input.dispatchKeyEvent', {
type: text ? 'keyDown' : 'rawKeyDown',
modifiers: this._modifiers,
windowsVirtualKeyCode: description.keyCode,
code: description.code,
key: description.key,
text: text,
unmodifiedText: text,
autoRepeat,
location: description.location,
isKeypad: description.location === 3,
});
}
_modifierBit(key) {
if (key === 'Alt')
return 1;
if (key === 'Control')
return 2;
if (key === 'Meta')
return 4;
if (key === 'Shift')
return 8;
return 0;
}
_keyDescriptionForString(keyString) {
const shift = this._modifiers & 8;
const description = {
key: '',
keyCode: 0,
code: '',
text: '',
location: 0,
};
const definition = USKeyboardLayout_js_1.keyDefinitions[keyString];
assert_js_1.assert(definition, `Unknown key: "${keyString}"`);
if (definition.key)
description.key = definition.key;
if (shift && definition.shiftKey)
description.key = definition.shiftKey;
if (definition.keyCode)
description.keyCode = definition.keyCode;
if (shift && definition.shiftKeyCode)
description.keyCode = definition.shiftKeyCode;
if (definition.code)
description.code = definition.code;
if (definition.location)
description.location = definition.location;
if (description.key.length === 1)
description.text = description.key;
if (definition.text)
description.text = definition.text;
if (shift && definition.shiftText)
description.text = definition.shiftText;
// if any modifiers besides shift are pressed, no text should be sent
if (this._modifiers & ~8)
description.text = '';
return description;
}
/**
* Dispatches a `keyup` event.
*
* @param key - Name of key to release, such as `ArrowLeft`.
* See {@link KeyInput | KeyInput}
* for a list of all key names.
*/
async up(key) {
const description = this._keyDescriptionForString(key);
this._modifiers &= ~this._modifierBit(description.key);
this._pressedKeys.delete(description.code);
await this._client.send('Input.dispatchKeyEvent', {
type: 'keyUp',
modifiers: this._modifiers,
key: description.key,
windowsVirtualKeyCode: description.keyCode,
code: description.code,
location: description.location,
});
}
/**
* Dispatches a `keypress` and `input` event.
* This does not send a `keydown` or `keyup` event.
*
* @remarks
* Modifier keys DO NOT effect {@link Keyboard.sendCharacter | Keyboard.sendCharacter}.
* Holding down `Shift` will not type the text in upper case.
*
* @example
* ```js
* page.keyboard.sendCharacter('嗨');
* ```
*
* @param char - Character to send into the page.
*/
async sendCharacter(char) {
await this._client.send('Input.insertText', { text: char });
}
charIsKey(char) {
return !!USKeyboardLayout_js_1.keyDefinitions[char];
}
/**
* Sends a `keydown`, `keypress`/`input`,
* and `keyup` event for each character in the text.
*
* @remarks
* To press a special key, like `Control` or `ArrowDown`,
* use {@link Keyboard.press}.
*
* Modifier keys DO NOT effect `keyboard.type`.
* Holding down `Shift` will not type the text in upper case.
*
* @example
* ```js
* await page.keyboard.type('Hello'); // Types instantly
* await page.keyboard.type('World', {delay: 100}); // Types slower, like a user
* ```
*
* @param text - A text to type into a focused element.
* @param options - An object of options. Accepts delay which,
* if specified, is the time to wait between `keydown` and `keyup` in milliseconds.
* Defaults to 0.
*/
async type(text, options = {}) {
const delay = options.delay || null;
for (const char of text) {
if (this.charIsKey(char)) {
await this.press(char, { delay });
}
else {
if (delay)
await new Promise((f) => setTimeout(f, delay));
await this.sendCharacter(char);
}
}
}
/**
* Shortcut for {@link Keyboard.down}
* and {@link Keyboard.up}.
*
* @remarks
* 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.
*
* Modifier keys DO effect {@link Keyboard.press}.
* Holding down `Shift` will type the text in upper case.
*
* @param key - Name of key to press, such as `ArrowLeft`.
* See {@link KeyInput} for a list of all key names.
*
* @param options - An object of options. Accepts text which, if specified,
* generates an input event with this text. Accepts delay which,
* if specified, is the time to wait between `keydown` and `keyup` in milliseconds.
* Defaults to 0.
*/
async press(key, options = {}) {
const { delay = null } = options;
await this.down(key, options);
if (delay)
await new Promise((f) => setTimeout(f, options.delay));
await this.up(key);
}
}
exports.Keyboard = Keyboard;
/**
* The Mouse class operates in main-frame CSS pixels
* relative to the top-left corner of the viewport.
* @remarks
* Every `page` object has its own Mouse, accessible with [`page.mouse`](#pagemouse).
*
* @example
* ```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();
* ```
*
* **Note**: The mouse events trigger synthetic `MouseEvent`s.
* This means that it does not fully replicate the functionality of what a normal user
* would be able to do with their mouse.
*
* For example, dragging and selecting text is not possible using `page.mouse`.
* Instead, you can use the {@link https://developer.mozilla.org/en-US/docs/Web/API/DocumentOrShadowRoot/getSelection | `DocumentOrShadowRoot.getSelection()`} functionality implemented in the platform.
*
* @example
* For example, if you want to select all content between nodes:
* ```js
* await page.evaluate((from, to) => {
* const selection = from.getRootNode().getSelection();
* const range = document.createRange();
* range.setStartBefore(from);
* range.setEndAfter(to);
* selection.removeAllRanges();
* selection.addRange(range);
* }, fromJSHandle, toJSHandle);
* ```
* If you then would want to copy-paste your selection, you can use the clipboard api:
* ```js
* // The clipboard api does not allow you to copy, unless the tab is focused.
* await page.bringToFront();
* await page.evaluate(() => {
* // Copy the selected content to the clipboard
* document.execCommand('copy');
* // Obtain the content of the clipboard as a string
* return navigator.clipboard.readText();
* });
* ```
* **Note**: If you want access to the clipboard API,
* you have to give it permission to do so:
* ```js
* await browser.defaultBrowserContext().overridePermissions(
* '<your origin>', ['clipboard-read', 'clipboard-write']
* );
* ```
* @public
*/
class Mouse {
/**
* @internal
*/
constructor(client, keyboard) {
this._x = 0;
this._y = 0;
this._button = 'none';
this._client = client;
this._keyboard = keyboard;
}
/**
* Dispatches a `mousemove` event.
* @param x - Horizontal position of the mouse.
* @param y - Vertical position of the mouse.
* @param options - Optional object. If specified, the `steps` property
* sends intermediate `mousemove` events when set to `1` (default).
*/
async move(x, y, options = {}) {
const { steps = 1 } = options;
const fromX = this._x, fromY = this._y;
this._x = x;
this._y = y;
for (let i = 1; i <= steps; i++) {
await this._client.send('Input.dispatchMouseEvent', {
type: 'mouseMoved',
button: this._button,
x: fromX + (this._x - fromX) * (i / steps),
y: fromY + (this._y - fromY) * (i / steps),
modifiers: this._keyboard._modifiers,
});
}
}
/**
* Shortcut for `mouse.move`, `mouse.down` and `mouse.up`.
* @param x - Horizontal position of the mouse.
* @param y - Vertical position of the mouse.
* @param options - Optional `MouseOptions`.
*/
async click(x, y, options = {}) {
const { delay = null } = options;
if (delay !== null) {
await Promise.all([this.move(x, y), this.down(options)]);
await new Promise((f) => setTimeout(f, delay));
await this.up(options);
}
else {
await Promise.all([
this.move(x, y),
this.down(options),
this.up(options),
]);
}
}
/**
* Dispatches a `mousedown` event.
* @param options - Optional `MouseOptions`.
*/
async down(options = {}) {
const { button = 'left', clickCount = 1 } = options;
this._button = button;
await this._client.send('Input.dispatchMouseEvent', {
type: 'mousePressed',
button,
x: this._x,
y: this._y,
modifiers: this._keyboard._modifiers,
clickCount,
});
}
/**
* Dispatches a `mouseup` event.
* @param options - Optional `MouseOptions`.
*/
async up(options = {}) {
const { button = 'left', clickCount = 1 } = options;
this._button = 'none';
await this._client.send('Input.dispatchMouseEvent', {
type: 'mouseReleased',
button,
x: this._x,
y: this._y,
modifiers: this._keyboard._modifiers,
clickCount,
});
}
/**
* Dispatches a `mousewheel` event.
* @param options - Optional: `MouseWheelOptions`.
*
* @example
* An example of zooming into an element:
* ```js
* await page.goto('https://mdn.mozillademos.org/en-US/docs/Web/API/Element/wheel_event$samples/Scaling_an_element_via_the_wheel?revision=1587366');
*
* const elem = await page.$('div');
* const boundingBox = await elem.boundingBox();
* await page.mouse.move(
* boundingBox.x + boundingBox.width / 2,
* boundingBox.y + boundingBox.height / 2
* );
*
* await page.mouse.wheel({ deltaY: -100 })
* ```
*/
async wheel(options = {}) {
const { deltaX = 0, deltaY = 0 } = options;
await this._client.send('Input.dispatchMouseEvent', {
type: 'mouseWheel',
x: this._x,
y: this._y,
deltaX,
deltaY,
modifiers: this._keyboard._modifiers,
pointerType: 'mouse',
});
}
}
exports.Mouse = Mouse;
/**
* The Touchscreen class exposes touchscreen events.
* @public
*/
class Touchscreen {
/**
* @internal
*/
constructor(client, keyboard) {
this._client = client;
this._keyboard = keyboard;
}
/**
* Dispatches a `touchstart` and `touchend` event.
* @param x - Horizontal position of the tap.
* @param y - Vertical position of the tap.
*/
async tap(x, y) {
const touchPoints = [{ x: Math.round(x), y: Math.round(y) }];
await this._client.send('Input.dispatchTouchEvent', {
type: 'touchStart',
touchPoints,
modifiers: this._keyboard._modifiers,
});
await this._client.send('Input.dispatchTouchEvent', {
type: 'touchEnd',
touchPoints: [],
modifiers: this._keyboard._modifiers,
});
}
}
exports.Touchscreen = Touchscreen;
+437
View File
@@ -0,0 +1,437 @@
/**
* 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.
*/
/// <reference types="node" />
import { ExecutionContext } from './ExecutionContext.js';
import { Page } from './Page.js';
import { CDPSession } from './Connection.js';
import { KeyInput } from './USKeyboardLayout.js';
import { FrameManager, Frame } from './FrameManager.js';
import { Protocol } from 'devtools-protocol';
import { EvaluateFn, SerializableOrJSHandle, EvaluateFnReturnType, EvaluateHandleFn, WrapElementHandle, UnwrapPromiseLike } from './EvalTypes.js';
export interface BoxModel {
content: Array<{
x: number;
y: number;
}>;
padding: Array<{
x: number;
y: number;
}>;
border: Array<{
x: number;
y: number;
}>;
margin: Array<{
x: number;
y: number;
}>;
width: number;
height: number;
}
/**
* @public
*/
export interface BoundingBox {
/**
* the x coordinate of the element in pixels.
*/
x: number;
/**
* the y coordinate of the element in pixels.
*/
y: number;
/**
* the width of the element in pixels.
*/
width: number;
/**
* the height of the element in pixels.
*/
height: number;
}
/**
* @internal
*/
export declare function createJSHandle(context: ExecutionContext, remoteObject: Protocol.Runtime.RemoteObject): JSHandle;
/**
* Represents an in-page JavaScript object. JSHandles can be created with the
* {@link Page.evaluateHandle | page.evaluateHandle} method.
*
* @example
* ```js
* const windowHandle = await page.evaluateHandle(() => window);
* ```
*
* JSHandle prevents the referenced JavaScript object from being garbage-collected
* unless the handle is {@link JSHandle.dispose | disposed}. JSHandles are auto-
* disposed when their origin frame gets navigated or the parent context gets destroyed.
*
* JSHandle instances can be used as arguments for {@link Page.$eval},
* {@link Page.evaluate}, and {@link Page.evaluateHandle}.
*
* @public
*/
export declare class JSHandle {
/**
* @internal
*/
_context: ExecutionContext;
/**
* @internal
*/
_client: CDPSession;
/**
* @internal
*/
_remoteObject: Protocol.Runtime.RemoteObject;
/**
* @internal
*/
_disposed: boolean;
/**
* @internal
*/
constructor(context: ExecutionContext, client: CDPSession, remoteObject: Protocol.Runtime.RemoteObject);
/** Returns the execution context the handle belongs to.
*/
executionContext(): ExecutionContext;
/**
* This method passes this handle as the first argument to `pageFunction`.
* If `pageFunction` returns a Promise, then `handle.evaluate` would wait
* for the promise to resolve and return its value.
*
* @example
* ```js
* const tweetHandle = await page.$('.tweet .retweets');
* expect(await tweetHandle.evaluate(node => node.innerText)).toBe('10');
* ```
*/
evaluate<T extends EvaluateFn>(pageFunction: T | string, ...args: SerializableOrJSHandle[]): Promise<UnwrapPromiseLike<EvaluateFnReturnType<T>>>;
/**
* This method passes this handle as the first argument to `pageFunction`.
*
* @remarks
*
* The only difference between `jsHandle.evaluate` and
* `jsHandle.evaluateHandle` is that `jsHandle.evaluateHandle`
* returns an in-page object (JSHandle).
*
* If the function passed to `jsHandle.evaluateHandle` returns a Promise,
* then `evaluateHandle.evaluateHandle` waits for the promise to resolve and
* returns its value.
*
* See {@link Page.evaluateHandle} for more details.
*/
evaluateHandle<HandleType extends JSHandle = JSHandle>(pageFunction: EvaluateHandleFn, ...args: SerializableOrJSHandle[]): Promise<HandleType>;
/** Fetches a single property from the referenced object.
*/
getProperty(propertyName: string): Promise<JSHandle | undefined>;
/**
* The method returns a map with property names as keys and JSHandle
* instances for the property values.
*
* @example
* ```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
* ```
*/
getProperties(): Promise<Map<string, JSHandle>>;
/**
* Returns a JSON representation of the object.
*
* @remarks
*
* The JSON is generated by running {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify | JSON.stringify}
* on the object in page and consequent {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse | JSON.parse} in puppeteer.
* **NOTE** The method throws if the referenced object is not stringifiable.
*/
jsonValue(): Promise<Record<string, unknown>>;
/**
* Returns either `null` or the object handle itself, if the object handle is
* an instance of {@link ElementHandle}.
*/
asElement(): ElementHandle | null;
/**
* Stops referencing the element handle, and resolves when the object handle is
* successfully disposed of.
*/
dispose(): Promise<void>;
/**
* Returns a string representation of the JSHandle.
*
* @remarks Useful during debugging.
*/
toString(): string;
}
/**
* ElementHandle represents an in-page DOM element.
*
* @remarks
*
* ElementHandles can be created with the {@link Page.$} method.
*
* ```js
* const puppeteer = require('puppeteer');
*
* (async () => {
* const browser = await puppeteer.launch();
* const page = await browser.newPage();
* await page.goto('https://example.com');
* const hrefElement = await page.$('a');
* await hrefElement.click();
* // ...
* })();
* ```
*
* ElementHandle prevents the DOM element from being garbage-collected unless the
* handle is {@link JSHandle.dispose | disposed}. ElementHandles are auto-disposed
* when their origin frame gets navigated.
*
* ElementHandle instances can be used as arguments in {@link Page.$eval} and
* {@link Page.evaluate} methods.
*
* If you're using TypeScript, ElementHandle takes a generic argument that
* denotes the type of element the handle is holding within. For example, if you
* have a handle to a `<select>` element, you can type it as
* `ElementHandle<HTMLSelectElement>` and you get some nicer type checks.
*
* @public
*/
export declare class ElementHandle<ElementType extends Element = Element> extends JSHandle {
private _page;
private _frameManager;
/**
* @internal
*/
constructor(context: ExecutionContext, client: CDPSession, remoteObject: Protocol.Runtime.RemoteObject, page: Page, frameManager: FrameManager);
asElement(): ElementHandle<ElementType> | null;
/**
* Resolves to the content frame for element handles referencing
* iframe nodes, or null otherwise
*/
contentFrame(): Promise<Frame | null>;
private _scrollIntoViewIfNeeded;
private _clickablePoint;
private _getBoxModel;
private _fromProtocolQuad;
private _intersectQuadWithViewport;
/**
* This method scrolls element into view if needed, and then
* uses {@link Page.mouse} to hover over the center of the element.
* If the element is detached from DOM, the method throws an error.
*/
hover(): Promise<void>;
/**
* This method scrolls element into view if needed, and then
* uses {@link Page.mouse} to click in the center of the element.
* If the element is detached from DOM, the method throws an error.
*/
click(options?: ClickOptions): Promise<void>;
/**
* 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.
*
* @example
* ```js
* handle.select('blue'); // single selection
* handle.select('red', 'green', 'blue'); // multiple selections
* ```
* @param values - 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.
*/
select(...values: string[]): Promise<string[]>;
/**
* This method expects `elementHandle` to point to an
* {@link https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input | input element}.
* @param filePaths - Sets the value of the file input to these paths.
* If some of the `filePaths` are relative paths, then they are resolved
* relative to the {@link https://nodejs.org/api/process.html#process_process_cwd | current working directory}
*/
uploadFile(...filePaths: string[]): Promise<void>;
/**
* This method scrolls element into view if needed, and then uses
* {@link Touchscreen.tap} to tap in the center of the element.
* If the element is detached from DOM, the method throws an error.
*/
tap(): Promise<void>;
/**
* Calls {@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/focus | focus} on the element.
*/
focus(): Promise<void>;
/**
* 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 {@link ElementHandle.press}.
*
* @example
* ```js
* await elementHandle.type('Hello'); // Types instantly
* await elementHandle.type('World', {delay: 100}); // Types slower, like a user
* ```
*
* @example
* 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');
* ```
*/
type(text: string, options?: {
delay: number;
}): Promise<void>;
/**
* Focuses the element, and then uses {@link Keyboard.down} and {@link Keyboard.up}.
*
* @remarks
* 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 affect `elementHandle.press`. Holding down `Shift`
* will type the text in upper case.
*
* @param key - Name of key to press, such as `ArrowLeft`.
* See {@link KeyInput} for a list of all key names.
*/
press(key: KeyInput, options?: PressOptions): Promise<void>;
/**
* This method returns the bounding box of the element (relative to the main frame),
* or `null` if the element is not visible.
*/
boundingBox(): Promise<BoundingBox | null>;
/**
* This method returns boxes of the element, or `null` if the element is not visible.
*
* @remarks
*
* Boxes are represented as an array of points;
* Each Point is an object `{x, y}`. Box points are sorted clock-wise.
*/
boxModel(): Promise<BoxModel | null>;
/**
* This method scrolls element into view if needed, and then uses
* {@link Page.screenshot} to take a screenshot of the element.
* If the element is detached from DOM, the method throws an error.
*/
screenshot(options?: {}): Promise<string | Buffer | void>;
/**
* Runs `element.querySelector` within the page. If no element matches the selector,
* the return value resolves to `null`.
*/
$(selector: string): Promise<ElementHandle | null>;
/**
* Runs `element.querySelectorAll` within the page. If no elements match the selector,
* the return value resolves to `[]`.
*/
$$(selector: string): Promise<ElementHandle[]>;
/**
* 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.
*
* @example
* ```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');
* ```
*/
$eval<ReturnType>(selector: string, pageFunction: (element: Element, ...args: unknown[]) => ReturnType | Promise<ReturnType>, ...args: SerializableOrJSHandle[]): Promise<WrapElementHandle<ReturnType>>;
/**
* 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.
*
* @example
* ```html
* <div class="feed">
* <div class="tweet">Hello!</div>
* <div class="tweet">Hi!</div>
* </div>
* ```
*
* @example
* ```js
* const feedHandle = await page.$('.feed');
* expect(await feedHandle.$$eval('.tweet', nodes => nodes.map(n => n.innerText)))
* .toEqual(['Hello!', 'Hi!']);
* ```
*/
$$eval<ReturnType>(selector: string, pageFunction: (elements: Element[], ...args: unknown[]) => ReturnType | Promise<ReturnType>, ...args: SerializableOrJSHandle[]): Promise<WrapElementHandle<ReturnType>>;
/**
* The method evaluates the XPath expression relative to the elementHandle.
* If there are no such elements, the method will resolve to an empty array.
* @param expression - Expression to {@link https://developer.mozilla.org/en-US/docs/Web/API/Document/evaluate | evaluate}
*/
$x(expression: string): Promise<ElementHandle[]>;
/**
* Resolves to true if the element is visible in the current viewport.
*/
isIntersectingViewport(): Promise<boolean>;
}
/**
* @public
*/
export interface ClickOptions {
/**
* Time to wait between `mousedown` and `mouseup` in milliseconds.
*
* @defaultValue 0
*/
delay?: number;
/**
* @defaultValue 'left'
*/
button?: 'left' | 'right' | 'middle';
/**
* @defaultValue 1
*/
clickCount?: number;
}
/**
* @public
*/
export interface PressOptions {
/**
* Time to wait between `keydown` and `keyup` in milliseconds. Defaults to 0.
*/
delay?: number;
/**
* If specified, generates an input event with this text.
*/
text?: string;
}
//# sourceMappingURL=JSHandle.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"JSHandle.d.ts","sourceRoot":"","sources":["../../../../src/common/JSHandle.ts"],"names":[],"mappings":"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"}
+747
View File
@@ -0,0 +1,747 @@
"use strict";
/**
* 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.
*/
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ElementHandle = exports.JSHandle = exports.createJSHandle = void 0;
const assert_js_1 = require("./assert.js");
const helper_js_1 = require("./helper.js");
const QueryHandler_js_1 = require("./QueryHandler.js");
const environment_js_1 = require("../environment.js");
/**
* @internal
*/
function createJSHandle(context, remoteObject) {
const frame = context.frame();
if (remoteObject.subtype === 'node' && frame) {
const frameManager = frame._frameManager;
return new ElementHandle(context, context._client, remoteObject, frameManager.page(), frameManager);
}
return new JSHandle(context, context._client, remoteObject);
}
exports.createJSHandle = createJSHandle;
/**
* Represents an in-page JavaScript object. JSHandles can be created with the
* {@link Page.evaluateHandle | page.evaluateHandle} method.
*
* @example
* ```js
* const windowHandle = await page.evaluateHandle(() => window);
* ```
*
* JSHandle prevents the referenced JavaScript object from being garbage-collected
* unless the handle is {@link JSHandle.dispose | disposed}. JSHandles are auto-
* disposed when their origin frame gets navigated or the parent context gets destroyed.
*
* JSHandle instances can be used as arguments for {@link Page.$eval},
* {@link Page.evaluate}, and {@link Page.evaluateHandle}.
*
* @public
*/
class JSHandle {
/**
* @internal
*/
constructor(context, client, remoteObject) {
/**
* @internal
*/
this._disposed = false;
this._context = context;
this._client = client;
this._remoteObject = remoteObject;
}
/** Returns the execution context the handle belongs to.
*/
executionContext() {
return this._context;
}
/**
* This method passes this handle as the first argument to `pageFunction`.
* If `pageFunction` returns a Promise, then `handle.evaluate` would wait
* for the promise to resolve and return its value.
*
* @example
* ```js
* const tweetHandle = await page.$('.tweet .retweets');
* expect(await tweetHandle.evaluate(node => node.innerText)).toBe('10');
* ```
*/
async evaluate(pageFunction, ...args) {
return await this.executionContext().evaluate(pageFunction, this, ...args);
}
/**
* This method passes this handle as the first argument to `pageFunction`.
*
* @remarks
*
* The only difference between `jsHandle.evaluate` and
* `jsHandle.evaluateHandle` is that `jsHandle.evaluateHandle`
* returns an in-page object (JSHandle).
*
* If the function passed to `jsHandle.evaluateHandle` returns a Promise,
* then `evaluateHandle.evaluateHandle` waits for the promise to resolve and
* returns its value.
*
* See {@link Page.evaluateHandle} for more details.
*/
async evaluateHandle(pageFunction, ...args) {
return await this.executionContext().evaluateHandle(pageFunction, this, ...args);
}
/** Fetches a single property from the referenced object.
*/
async getProperty(propertyName) {
const objectHandle = await this.evaluateHandle((object, propertyName) => {
const result = { __proto__: null };
result[propertyName] = object[propertyName];
return result;
}, propertyName);
const properties = await objectHandle.getProperties();
const result = properties.get(propertyName) || null;
await objectHandle.dispose();
return result;
}
/**
* The method returns a map with property names as keys and JSHandle
* instances for the property values.
*
* @example
* ```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
* ```
*/
async getProperties() {
const response = await this._client.send('Runtime.getProperties', {
objectId: this._remoteObject.objectId,
ownProperties: true,
});
const result = new Map();
for (const property of response.result) {
if (!property.enumerable)
continue;
result.set(property.name, createJSHandle(this._context, property.value));
}
return result;
}
/**
* Returns a JSON representation of the object.
*
* @remarks
*
* The JSON is generated by running {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify | JSON.stringify}
* on the object in page and consequent {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse | JSON.parse} in puppeteer.
* **NOTE** The method throws if the referenced object is not stringifiable.
*/
async jsonValue() {
if (this._remoteObject.objectId) {
const response = await this._client.send('Runtime.callFunctionOn', {
functionDeclaration: 'function() { return this; }',
objectId: this._remoteObject.objectId,
returnByValue: true,
awaitPromise: true,
});
return helper_js_1.helper.valueFromRemoteObject(response.result);
}
return helper_js_1.helper.valueFromRemoteObject(this._remoteObject);
}
/**
* Returns either `null` or the object handle itself, if the object handle is
* an instance of {@link ElementHandle}.
*/
asElement() {
// This always returns null, but subclasses can override this and return an
// ElementHandle.
return null;
}
/**
* Stops referencing the element handle, and resolves when the object handle is
* successfully disposed of.
*/
async dispose() {
if (this._disposed)
return;
this._disposed = true;
await helper_js_1.helper.releaseObject(this._client, this._remoteObject);
}
/**
* Returns a string representation of the JSHandle.
*
* @remarks Useful during debugging.
*/
toString() {
if (this._remoteObject.objectId) {
const type = this._remoteObject.subtype || this._remoteObject.type;
return 'JSHandle@' + type;
}
return 'JSHandle:' + helper_js_1.helper.valueFromRemoteObject(this._remoteObject);
}
}
exports.JSHandle = JSHandle;
/**
* ElementHandle represents an in-page DOM element.
*
* @remarks
*
* ElementHandles can be created with the {@link Page.$} method.
*
* ```js
* const puppeteer = require('puppeteer');
*
* (async () => {
* const browser = await puppeteer.launch();
* const page = await browser.newPage();
* await page.goto('https://example.com');
* const hrefElement = await page.$('a');
* await hrefElement.click();
* // ...
* })();
* ```
*
* ElementHandle prevents the DOM element from being garbage-collected unless the
* handle is {@link JSHandle.dispose | disposed}. ElementHandles are auto-disposed
* when their origin frame gets navigated.
*
* ElementHandle instances can be used as arguments in {@link Page.$eval} and
* {@link Page.evaluate} methods.
*
* If you're using TypeScript, ElementHandle takes a generic argument that
* denotes the type of element the handle is holding within. For example, if you
* have a handle to a `<select>` element, you can type it as
* `ElementHandle<HTMLSelectElement>` and you get some nicer type checks.
*
* @public
*/
class ElementHandle extends JSHandle {
/**
* @internal
*/
constructor(context, client, remoteObject, page, frameManager) {
super(context, client, remoteObject);
this._client = client;
this._remoteObject = remoteObject;
this._page = page;
this._frameManager = frameManager;
}
asElement() {
return this;
}
/**
* Resolves to the content frame for element handles referencing
* iframe nodes, or null otherwise
*/
async contentFrame() {
const nodeInfo = await this._client.send('DOM.describeNode', {
objectId: this._remoteObject.objectId,
});
if (typeof nodeInfo.node.frameId !== 'string')
return null;
return this._frameManager.frame(nodeInfo.node.frameId);
}
async _scrollIntoViewIfNeeded() {
const error = await this.evaluate(async (element, pageJavascriptEnabled) => {
if (!element.isConnected)
return 'Node is detached from document';
if (element.nodeType !== Node.ELEMENT_NODE)
return 'Node is not of type HTMLElement';
// force-scroll if page's javascript is disabled.
if (!pageJavascriptEnabled) {
element.scrollIntoView({
block: 'center',
inline: 'center',
// @ts-expect-error Chrome still supports behavior: instant but
// it's not in the spec so TS shouts We don't want to make this
// breaking change in Puppeteer yet so we'll ignore the line.
behavior: 'instant',
});
return false;
}
const visibleRatio = await new Promise((resolve) => {
const observer = new IntersectionObserver((entries) => {
resolve(entries[0].intersectionRatio);
observer.disconnect();
});
observer.observe(element);
});
if (visibleRatio !== 1.0) {
element.scrollIntoView({
block: 'center',
inline: 'center',
// @ts-expect-error Chrome still supports behavior: instant but
// it's not in the spec so TS shouts We don't want to make this
// breaking change in Puppeteer yet so we'll ignore the line.
behavior: 'instant',
});
}
return false;
}, this._page.isJavaScriptEnabled());
if (error)
throw new Error(error);
}
async _clickablePoint() {
const [result, layoutMetrics] = await Promise.all([
this._client
.send('DOM.getContentQuads', {
objectId: this._remoteObject.objectId,
})
.catch(helper_js_1.debugError),
this._client.send('Page.getLayoutMetrics'),
]);
if (!result || !result.quads.length)
throw new Error('Node is either not visible or not an HTMLElement');
// Filter out quads that have too small area to click into.
const { clientWidth, clientHeight } = layoutMetrics.layoutViewport;
const quads = result.quads
.map((quad) => this._fromProtocolQuad(quad))
.map((quad) => this._intersectQuadWithViewport(quad, clientWidth, clientHeight))
.filter((quad) => computeQuadArea(quad) > 1);
if (!quads.length)
throw new Error('Node is either not visible or not an HTMLElement');
// Return the middle point of the first quad.
const quad = quads[0];
let x = 0;
let y = 0;
for (const point of quad) {
x += point.x;
y += point.y;
}
return {
x: x / 4,
y: y / 4,
};
}
_getBoxModel() {
const params = {
objectId: this._remoteObject.objectId,
};
return this._client
.send('DOM.getBoxModel', params)
.catch((error) => helper_js_1.debugError(error));
}
_fromProtocolQuad(quad) {
return [
{ x: quad[0], y: quad[1] },
{ x: quad[2], y: quad[3] },
{ x: quad[4], y: quad[5] },
{ x: quad[6], y: quad[7] },
];
}
_intersectQuadWithViewport(quad, width, height) {
return quad.map((point) => ({
x: Math.min(Math.max(point.x, 0), width),
y: Math.min(Math.max(point.y, 0), height),
}));
}
/**
* This method scrolls element into view if needed, and then
* uses {@link Page.mouse} to hover over the center of the element.
* If the element is detached from DOM, the method throws an error.
*/
async hover() {
await this._scrollIntoViewIfNeeded();
const { x, y } = await this._clickablePoint();
await this._page.mouse.move(x, y);
}
/**
* This method scrolls element into view if needed, and then
* uses {@link Page.mouse} to click in the center of the element.
* If the element is detached from DOM, the method throws an error.
*/
async click(options = {}) {
await this._scrollIntoViewIfNeeded();
const { x, y } = await this._clickablePoint();
await this._page.mouse.click(x, y, options);
}
/**
* 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.
*
* @example
* ```js
* handle.select('blue'); // single selection
* handle.select('red', 'green', 'blue'); // multiple selections
* ```
* @param values - 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.
*/
async select(...values) {
for (const value of values)
assert_js_1.assert(helper_js_1.helper.isString(value), 'Values must be strings. Found value "' +
value +
'" of type "' +
typeof value +
'"');
return this.evaluate((element, values) => {
if (element.nodeName.toLowerCase() !== 'select')
throw new Error('Element is not a <select> element.');
const options = Array.from(element.options);
element.value = undefined;
for (const option of options) {
option.selected = values.includes(option.value);
if (option.selected && !element.multiple)
break;
}
element.dispatchEvent(new Event('input', { bubbles: true }));
element.dispatchEvent(new Event('change', { bubbles: true }));
return options
.filter((option) => option.selected)
.map((option) => option.value);
}, values);
}
/**
* This method expects `elementHandle` to point to an
* {@link https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input | input element}.
* @param filePaths - Sets the value of the file input to these paths.
* If some of the `filePaths` are relative paths, then they are resolved
* relative to the {@link https://nodejs.org/api/process.html#process_process_cwd | current working directory}
*/
async uploadFile(...filePaths) {
const isMultiple = await this.evaluate((element) => element.multiple);
assert_js_1.assert(filePaths.length <= 1 || isMultiple, 'Multiple file uploads only work with <input type=file multiple>');
if (!environment_js_1.isNode) {
throw new Error(`JSHandle#uploadFile can only be used in Node environments.`);
}
// This import is only needed for `uploadFile`, so keep it scoped here to avoid paying
// the cost unnecessarily.
const path = await Promise.resolve().then(() => __importStar(require('path')));
const fs = await helper_js_1.helper.importFSModule();
// Locate all files and confirm that they exist.
const files = await Promise.all(filePaths.map(async (filePath) => {
const resolvedPath = path.resolve(filePath);
try {
await fs.promises.access(resolvedPath, fs.constants.R_OK);
}
catch (error) {
if (error.code === 'ENOENT')
throw new Error(`${filePath} does not exist or is not readable`);
}
return resolvedPath;
}));
const { objectId } = this._remoteObject;
const { node } = await this._client.send('DOM.describeNode', { objectId });
const { backendNodeId } = node;
// The zero-length array is a special case, it seems that DOM.setFileInputFiles does
// not actually update the files in that case, so the solution is to eval the element
// value to a new FileList directly.
if (files.length === 0) {
await this.evaluate((element) => {
element.files = new DataTransfer().files;
// Dispatch events for this case because it should behave akin to a user action.
element.dispatchEvent(new Event('input', { bubbles: true }));
element.dispatchEvent(new Event('change', { bubbles: true }));
});
}
else {
await this._client.send('DOM.setFileInputFiles', {
objectId,
files,
backendNodeId,
});
}
}
/**
* This method scrolls element into view if needed, and then uses
* {@link Touchscreen.tap} to tap in the center of the element.
* If the element is detached from DOM, the method throws an error.
*/
async tap() {
await this._scrollIntoViewIfNeeded();
const { x, y } = await this._clickablePoint();
await this._page.touchscreen.tap(x, y);
}
/**
* Calls {@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/focus | focus} on the element.
*/
async focus() {
await this.evaluate((element) => element.focus());
}
/**
* 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 {@link ElementHandle.press}.
*
* @example
* ```js
* await elementHandle.type('Hello'); // Types instantly
* await elementHandle.type('World', {delay: 100}); // Types slower, like a user
* ```
*
* @example
* 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');
* ```
*/
async type(text, options) {
await this.focus();
await this._page.keyboard.type(text, options);
}
/**
* Focuses the element, and then uses {@link Keyboard.down} and {@link Keyboard.up}.
*
* @remarks
* 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 affect `elementHandle.press`. Holding down `Shift`
* will type the text in upper case.
*
* @param key - Name of key to press, such as `ArrowLeft`.
* See {@link KeyInput} for a list of all key names.
*/
async press(key, options) {
await this.focus();
await this._page.keyboard.press(key, options);
}
/**
* This method returns the bounding box of the element (relative to the main frame),
* or `null` if the element is not visible.
*/
async boundingBox() {
const result = await this._getBoxModel();
if (!result)
return null;
const quad = result.model.border;
const x = Math.min(quad[0], quad[2], quad[4], quad[6]);
const y = Math.min(quad[1], quad[3], quad[5], quad[7]);
const width = Math.max(quad[0], quad[2], quad[4], quad[6]) - x;
const height = Math.max(quad[1], quad[3], quad[5], quad[7]) - y;
return { x, y, width, height };
}
/**
* This method returns boxes of the element, or `null` if the element is not visible.
*
* @remarks
*
* Boxes are represented as an array of points;
* Each Point is an object `{x, y}`. Box points are sorted clock-wise.
*/
async boxModel() {
const result = await this._getBoxModel();
if (!result)
return null;
const { content, padding, border, margin, width, height } = result.model;
return {
content: this._fromProtocolQuad(content),
padding: this._fromProtocolQuad(padding),
border: this._fromProtocolQuad(border),
margin: this._fromProtocolQuad(margin),
width,
height,
};
}
/**
* This method scrolls element into view if needed, and then uses
* {@link Page.screenshot} to take a screenshot of the element.
* If the element is detached from DOM, the method throws an error.
*/
async screenshot(options = {}) {
let needsViewportReset = false;
let boundingBox = await this.boundingBox();
assert_js_1.assert(boundingBox, 'Node is either not visible or not an HTMLElement');
const viewport = this._page.viewport();
if (viewport &&
(boundingBox.width > viewport.width ||
boundingBox.height > viewport.height)) {
const newViewport = {
width: Math.max(viewport.width, Math.ceil(boundingBox.width)),
height: Math.max(viewport.height, Math.ceil(boundingBox.height)),
};
await this._page.setViewport(Object.assign({}, viewport, newViewport));
needsViewportReset = true;
}
await this._scrollIntoViewIfNeeded();
boundingBox = await this.boundingBox();
assert_js_1.assert(boundingBox, 'Node is either not visible or not an HTMLElement');
assert_js_1.assert(boundingBox.width !== 0, 'Node has 0 width.');
assert_js_1.assert(boundingBox.height !== 0, 'Node has 0 height.');
const { layoutViewport: { pageX, pageY }, } = await this._client.send('Page.getLayoutMetrics');
const clip = Object.assign({}, boundingBox);
clip.x += pageX;
clip.y += pageY;
const imageData = await this._page.screenshot(Object.assign({}, {
clip,
}, options));
if (needsViewportReset)
await this._page.setViewport(viewport);
return imageData;
}
/**
* Runs `element.querySelector` within the page. If no element matches the selector,
* the return value resolves to `null`.
*/
async $(selector) {
const { updatedSelector, queryHandler } = QueryHandler_js_1.getQueryHandlerAndSelector(selector);
return queryHandler.queryOne(this, updatedSelector);
}
/**
* Runs `element.querySelectorAll` within the page. If no elements match the selector,
* the return value resolves to `[]`.
*/
async $$(selector) {
const { updatedSelector, queryHandler } = QueryHandler_js_1.getQueryHandlerAndSelector(selector);
return queryHandler.queryAll(this, updatedSelector);
}
/**
* 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.
*
* @example
* ```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');
* ```
*/
async $eval(selector, pageFunction, ...args) {
const elementHandle = await this.$(selector);
if (!elementHandle)
throw new Error(`Error: failed to find element matching selector "${selector}"`);
const result = await elementHandle.evaluate(pageFunction, ...args);
await elementHandle.dispose();
/**
* This `as` is a little unfortunate but helps TS understand the behavior of
* `elementHandle.evaluate`. If evaluate returns an element it will return an
* ElementHandle instance, rather than the plain object. All the
* WrapElementHandle type does is wrap ReturnType into
* ElementHandle<ReturnType> if it is an ElementHandle, or leave it alone as
* ReturnType if it isn't.
*/
return result;
}
/**
* 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.
*
* @example
* ```html
* <div class="feed">
* <div class="tweet">Hello!</div>
* <div class="tweet">Hi!</div>
* </div>
* ```
*
* @example
* ```js
* const feedHandle = await page.$('.feed');
* expect(await feedHandle.$$eval('.tweet', nodes => nodes.map(n => n.innerText)))
* .toEqual(['Hello!', 'Hi!']);
* ```
*/
async $$eval(selector, pageFunction, ...args) {
const { updatedSelector, queryHandler } = QueryHandler_js_1.getQueryHandlerAndSelector(selector);
const arrayHandle = await queryHandler.queryAllArray(this, updatedSelector);
const result = await arrayHandle.evaluate(pageFunction, ...args);
await arrayHandle.dispose();
/* This `as` exists for the same reason as the `as` in $eval above.
* See the comment there for a full explanation.
*/
return result;
}
/**
* The method evaluates the XPath expression relative to the elementHandle.
* If there are no such elements, the method will resolve to an empty array.
* @param expression - Expression to {@link https://developer.mozilla.org/en-US/docs/Web/API/Document/evaluate | evaluate}
*/
async $x(expression) {
const arrayHandle = await this.evaluateHandle((element, expression) => {
const document = element.ownerDocument || element;
const iterator = document.evaluate(expression, element, null, XPathResult.ORDERED_NODE_ITERATOR_TYPE);
const array = [];
let item;
while ((item = iterator.iterateNext()))
array.push(item);
return array;
}, expression);
const properties = await arrayHandle.getProperties();
await arrayHandle.dispose();
const result = [];
for (const property of properties.values()) {
const elementHandle = property.asElement();
if (elementHandle)
result.push(elementHandle);
}
return result;
}
/**
* Resolves to true if the element is visible in the current viewport.
*/
async isIntersectingViewport() {
return await this.evaluate(async (element) => {
const visibleRatio = await new Promise((resolve) => {
const observer = new IntersectionObserver((entries) => {
resolve(entries[0].intersectionRatio);
observer.disconnect();
});
observer.observe(element);
});
return visibleRatio > 0;
});
}
}
exports.ElementHandle = ElementHandle;
function computeQuadArea(quad) {
// Compute sum of all directed areas of adjacent triangles
// https://en.wikipedia.org/wiki/Polygon#Simple_polygons
let area = 0;
for (let i = 0; i < quad.length; ++i) {
const p1 = quad[i];
const p2 = quad[(i + 1) % quad.length];
area += (p1.x * p2.y - p2.x * p1.y) / 2;
}
return Math.abs(area);
}
@@ -0,0 +1,61 @@
/**
* 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.
*/
/// <reference types="node" />
import { PuppeteerEventListener } from './helper.js';
import { TimeoutError } from './Errors.js';
import { FrameManager, Frame } from './FrameManager.js';
import { HTTPRequest } from './HTTPRequest.js';
import { HTTPResponse } from './HTTPResponse.js';
export declare type PuppeteerLifeCycleEvent = 'load' | 'domcontentloaded' | 'networkidle0' | 'networkidle2';
declare type ProtocolLifeCycleEvent = 'load' | 'DOMContentLoaded' | 'networkIdle' | 'networkAlmostIdle';
/**
* @internal
*/
export declare class LifecycleWatcher {
_expectedLifecycle: ProtocolLifeCycleEvent[];
_frameManager: FrameManager;
_frame: Frame;
_timeout: number;
_navigationRequest?: HTTPRequest;
_eventListeners: PuppeteerEventListener[];
_initialLoaderId: string;
_sameDocumentNavigationPromise: Promise<Error | null>;
_sameDocumentNavigationCompleteCallback: (x?: Error) => void;
_lifecyclePromise: Promise<void>;
_lifecycleCallback: () => void;
_newDocumentNavigationPromise: Promise<Error | null>;
_newDocumentNavigationCompleteCallback: (x?: Error) => void;
_terminationPromise: Promise<Error | null>;
_terminationCallback: (x?: Error) => void;
_timeoutPromise: Promise<TimeoutError | null>;
_maximumTimer?: NodeJS.Timeout;
_hasSameDocumentNavigation?: boolean;
constructor(frameManager: FrameManager, frame: Frame, waitUntil: PuppeteerLifeCycleEvent | PuppeteerLifeCycleEvent[], timeout: number);
_onRequest(request: HTTPRequest): void;
_onFrameDetached(frame: Frame): void;
navigationResponse(): HTTPResponse | null;
_terminate(error: Error): void;
sameDocumentNavigationPromise(): Promise<Error | null>;
newDocumentNavigationPromise(): Promise<Error | null>;
lifecyclePromise(): Promise<void>;
timeoutOrTerminationPromise(): Promise<Error | TimeoutError | null>;
_createTimeoutPromise(): Promise<TimeoutError | null>;
_navigatedWithinDocument(frame: Frame): void;
_checkLifecycleComplete(): void;
dispose(): void;
}
export {};
//# sourceMappingURL=LifecycleWatcher.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"LifecycleWatcher.d.ts","sourceRoot":"","sources":["../../../../src/common/LifecycleWatcher.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;;AAGH,OAAO,EAAU,sBAAsB,EAAE,MAAM,aAAa,CAAC;AAC7D,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EACL,YAAY,EACZ,KAAK,EAEN,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAIjD,oBAAY,uBAAuB,GAC/B,MAAM,GACN,kBAAkB,GAClB,cAAc,GACd,cAAc,CAAC;AACnB,aAAK,sBAAsB,GACvB,MAAM,GACN,kBAAkB,GAClB,aAAa,GACb,mBAAmB,CAAC;AAYxB;;GAEG;AACH,qBAAa,gBAAgB;IAC3B,kBAAkB,EAAE,sBAAsB,EAAE,CAAC;IAC7C,aAAa,EAAE,YAAY,CAAC;IAC5B,MAAM,EAAE,KAAK,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,kBAAkB,CAAC,EAAE,WAAW,CAAC;IACjC,eAAe,EAAE,sBAAsB,EAAE,CAAC;IAC1C,gBAAgB,EAAE,MAAM,CAAC;IAEzB,8BAA8B,EAAE,OAAO,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC;IACtD,uCAAuC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,KAAK,IAAI,CAAC;IAE7D,iBAAiB,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;IACjC,kBAAkB,EAAE,MAAM,IAAI,CAAC;IAE/B,6BAA6B,EAAE,OAAO,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC;IACrD,sCAAsC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,KAAK,IAAI,CAAC;IAE5D,mBAAmB,EAAE,OAAO,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC;IAC3C,oBAAoB,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,KAAK,IAAI,CAAC;IAE1C,eAAe,EAAE,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAAC;IAE9C,aAAa,CAAC,EAAE,MAAM,CAAC,OAAO,CAAC;IAC/B,0BAA0B,CAAC,EAAE,OAAO,CAAC;gBAGnC,YAAY,EAAE,YAAY,EAC1B,KAAK,EAAE,KAAK,EACZ,SAAS,EAAE,uBAAuB,GAAG,uBAAuB,EAAE,EAC9D,OAAO,EAAE,MAAM;IAmEjB,UAAU,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI;IAMtC,gBAAgB,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI;IAWpC,kBAAkB,IAAI,YAAY,GAAG,IAAI;IAIzC,UAAU,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI;IAI9B,6BAA6B,IAAI,OAAO,CAAC,KAAK,GAAG,IAAI,CAAC;IAItD,4BAA4B,IAAI,OAAO,CAAC,KAAK,GAAG,IAAI,CAAC;IAIrD,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC;IAIjC,2BAA2B,IAAI,OAAO,CAAC,KAAK,GAAG,YAAY,GAAG,IAAI,CAAC;IAInE,qBAAqB,IAAI,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC;IASrD,wBAAwB,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI;IAM5C,uBAAuB,IAAI,IAAI;IAiC/B,OAAO,IAAI,IAAI;CAIhB"}
@@ -0,0 +1,148 @@
"use strict";
/**
* 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.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.LifecycleWatcher = void 0;
const assert_js_1 = require("./assert.js");
const helper_js_1 = require("./helper.js");
const Errors_js_1 = require("./Errors.js");
const FrameManager_js_1 = require("./FrameManager.js");
const NetworkManager_js_1 = require("./NetworkManager.js");
const Connection_js_1 = require("./Connection.js");
const puppeteerToProtocolLifecycle = new Map([
['load', 'load'],
['domcontentloaded', 'DOMContentLoaded'],
['networkidle0', 'networkIdle'],
['networkidle2', 'networkAlmostIdle'],
]);
/**
* @internal
*/
class LifecycleWatcher {
constructor(frameManager, frame, waitUntil, timeout) {
if (Array.isArray(waitUntil))
waitUntil = waitUntil.slice();
else if (typeof waitUntil === 'string')
waitUntil = [waitUntil];
this._expectedLifecycle = waitUntil.map((value) => {
const protocolEvent = puppeteerToProtocolLifecycle.get(value);
assert_js_1.assert(protocolEvent, 'Unknown value for options.waitUntil: ' + value);
return protocolEvent;
});
this._frameManager = frameManager;
this._frame = frame;
this._initialLoaderId = frame._loaderId;
this._timeout = timeout;
this._navigationRequest = null;
this._eventListeners = [
helper_js_1.helper.addEventListener(frameManager._client, Connection_js_1.CDPSessionEmittedEvents.Disconnected, () => this._terminate(new Error('Navigation failed because browser has disconnected!'))),
helper_js_1.helper.addEventListener(this._frameManager, FrameManager_js_1.FrameManagerEmittedEvents.LifecycleEvent, this._checkLifecycleComplete.bind(this)),
helper_js_1.helper.addEventListener(this._frameManager, FrameManager_js_1.FrameManagerEmittedEvents.FrameNavigatedWithinDocument, this._navigatedWithinDocument.bind(this)),
helper_js_1.helper.addEventListener(this._frameManager, FrameManager_js_1.FrameManagerEmittedEvents.FrameDetached, this._onFrameDetached.bind(this)),
helper_js_1.helper.addEventListener(this._frameManager.networkManager(), NetworkManager_js_1.NetworkManagerEmittedEvents.Request, this._onRequest.bind(this)),
];
this._sameDocumentNavigationPromise = new Promise((fulfill) => {
this._sameDocumentNavigationCompleteCallback = fulfill;
});
this._lifecyclePromise = new Promise((fulfill) => {
this._lifecycleCallback = fulfill;
});
this._newDocumentNavigationPromise = new Promise((fulfill) => {
this._newDocumentNavigationCompleteCallback = fulfill;
});
this._timeoutPromise = this._createTimeoutPromise();
this._terminationPromise = new Promise((fulfill) => {
this._terminationCallback = fulfill;
});
this._checkLifecycleComplete();
}
_onRequest(request) {
if (request.frame() !== this._frame || !request.isNavigationRequest())
return;
this._navigationRequest = request;
}
_onFrameDetached(frame) {
if (this._frame === frame) {
this._terminationCallback.call(null, new Error('Navigating frame was detached'));
return;
}
this._checkLifecycleComplete();
}
navigationResponse() {
return this._navigationRequest ? this._navigationRequest.response() : null;
}
_terminate(error) {
this._terminationCallback.call(null, error);
}
sameDocumentNavigationPromise() {
return this._sameDocumentNavigationPromise;
}
newDocumentNavigationPromise() {
return this._newDocumentNavigationPromise;
}
lifecyclePromise() {
return this._lifecyclePromise;
}
timeoutOrTerminationPromise() {
return Promise.race([this._timeoutPromise, this._terminationPromise]);
}
_createTimeoutPromise() {
if (!this._timeout)
return new Promise(() => { });
const errorMessage = 'Navigation timeout of ' + this._timeout + ' ms exceeded';
return new Promise((fulfill) => (this._maximumTimer = setTimeout(fulfill, this._timeout))).then(() => new Errors_js_1.TimeoutError(errorMessage));
}
_navigatedWithinDocument(frame) {
if (frame !== this._frame)
return;
this._hasSameDocumentNavigation = true;
this._checkLifecycleComplete();
}
_checkLifecycleComplete() {
// We expect navigation to commit.
if (!checkLifecycle(this._frame, this._expectedLifecycle))
return;
this._lifecycleCallback();
if (this._frame._loaderId === this._initialLoaderId &&
!this._hasSameDocumentNavigation)
return;
if (this._hasSameDocumentNavigation)
this._sameDocumentNavigationCompleteCallback();
if (this._frame._loaderId !== this._initialLoaderId)
this._newDocumentNavigationCompleteCallback();
/**
* @param {!Frame} frame
* @param {!Array<string>} expectedLifecycle
* @returns {boolean}
*/
function checkLifecycle(frame, expectedLifecycle) {
for (const event of expectedLifecycle) {
if (!frame._lifecycleEvents.has(event))
return false;
}
for (const child of frame.childFrames()) {
if (!checkLifecycle(child, expectedLifecycle))
return false;
}
return true;
}
}
dispose() {
helper_js_1.helper.removeEventListeners(this._eventListeners);
clearTimeout(this._maximumTimer);
}
}
exports.LifecycleWatcher = LifecycleWatcher;
@@ -0,0 +1,78 @@
/**
* 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.
*/
import { EventEmitter } from './EventEmitter.js';
import { Protocol } from 'devtools-protocol';
import { CDPSession } from './Connection.js';
import { FrameManager } from './FrameManager.js';
import { HTTPRequest } from './HTTPRequest.js';
/**
* @public
*/
export interface Credentials {
username: string;
password: string;
}
/**
* We use symbols to prevent any external parties listening to these events.
* They are internal to Puppeteer.
*
* @internal
*/
export declare const NetworkManagerEmittedEvents: {
readonly Request: symbol;
readonly Response: symbol;
readonly RequestFailed: symbol;
readonly RequestFinished: symbol;
};
/**
* @internal
*/
export declare class NetworkManager extends EventEmitter {
_client: CDPSession;
_ignoreHTTPSErrors: boolean;
_frameManager: FrameManager;
_requestIdToRequest: Map<string, HTTPRequest>;
_requestIdToRequestWillBeSentEvent: Map<string, Protocol.Network.RequestWillBeSentEvent>;
_extraHTTPHeaders: Record<string, string>;
_offline: boolean;
_credentials?: Credentials;
_attemptedAuthentications: Set<string>;
_userRequestInterceptionEnabled: boolean;
_protocolRequestInterceptionEnabled: boolean;
_userCacheDisabled: boolean;
_requestIdToInterceptionId: Map<string, string>;
constructor(client: CDPSession, ignoreHTTPSErrors: boolean, frameManager: FrameManager);
initialize(): Promise<void>;
authenticate(credentials?: Credentials): Promise<void>;
setExtraHTTPHeaders(extraHTTPHeaders: Record<string, string>): Promise<void>;
extraHTTPHeaders(): Record<string, string>;
setOfflineMode(value: boolean): Promise<void>;
setUserAgent(userAgent: string): Promise<void>;
setCacheEnabled(enabled: boolean): Promise<void>;
setRequestInterception(value: boolean): Promise<void>;
_updateProtocolRequestInterception(): Promise<void>;
_updateProtocolCacheDisabled(): Promise<void>;
_onRequestWillBeSent(event: Protocol.Network.RequestWillBeSentEvent): void;
_onAuthRequired(event: Protocol.Fetch.AuthRequiredEvent): void;
_onRequestPaused(event: Protocol.Fetch.RequestPausedEvent): void;
_onRequest(event: Protocol.Network.RequestWillBeSentEvent, interceptionId?: string): void;
_onRequestServedFromCache(event: Protocol.Network.RequestServedFromCacheEvent): void;
_handleRequestRedirect(request: HTTPRequest, responsePayload: Protocol.Network.Response): void;
_onResponseReceived(event: Protocol.Network.ResponseReceivedEvent): void;
_onLoadingFinished(event: Protocol.Network.LoadingFinishedEvent): void;
_onLoadingFailed(event: Protocol.Network.LoadingFailedEvent): void;
}
//# sourceMappingURL=NetworkManager.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"NetworkManager.d.ts","sourceRoot":"","sources":["../../../../src/common/NetworkManager.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAGjD,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAC7C,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAG/C;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;;;GAKG;AACH,eAAO,MAAM,2BAA2B;;;;;CAK9B,CAAC;AAEX;;GAEG;AACH,qBAAa,cAAe,SAAQ,YAAY;IAC9C,OAAO,EAAE,UAAU,CAAC;IACpB,kBAAkB,EAAE,OAAO,CAAC;IAC5B,aAAa,EAAE,YAAY,CAAC;IAC5B,mBAAmB,2BAAkC;IACrD,kCAAkC,uDAG9B;IACJ,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAM;IAC/C,QAAQ,UAAS;IACjB,YAAY,CAAC,EAAE,WAAW,CAAQ;IAClC,yBAAyB,cAAqB;IAC9C,+BAA+B,UAAS;IACxC,mCAAmC,UAAS;IAC5C,kBAAkB,UAAS;IAC3B,0BAA0B,sBAA6B;gBAGrD,MAAM,EAAE,UAAU,EAClB,iBAAiB,EAAE,OAAO,EAC1B,YAAY,EAAE,YAAY;IA4BtB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAQ3B,YAAY,CAAC,WAAW,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAKtD,mBAAmB,CACvB,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GACvC,OAAO,CAAC,IAAI,CAAC;IAehB,gBAAgB,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;IAIpC,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAY7C,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAI9C,eAAe,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAKhD,sBAAsB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAKrD,kCAAkC,IAAI,OAAO,CAAC,IAAI,CAAC;IAoBnD,4BAA4B,IAAI,OAAO,CAAC,IAAI,CAAC;IAOnD,oBAAoB,CAAC,KAAK,EAAE,QAAQ,CAAC,OAAO,CAAC,sBAAsB,GAAG,IAAI;IAmB1E,eAAe,CAAC,KAAK,EAAE,QAAQ,CAAC,KAAK,CAAC,iBAAiB,GAAG,IAAI;IAwB9D,gBAAgB,CAAC,KAAK,EAAE,QAAQ,CAAC,KAAK,CAAC,kBAAkB,GAAG,IAAI;IAyBhE,UAAU,CACR,KAAK,EAAE,QAAQ,CAAC,OAAO,CAAC,sBAAsB,EAC9C,cAAc,CAAC,EAAE,MAAM,GACtB,IAAI;IA0BP,yBAAyB,CACvB,KAAK,EAAE,QAAQ,CAAC,OAAO,CAAC,2BAA2B,GAClD,IAAI;IAKP,sBAAsB,CACpB,OAAO,EAAE,WAAW,EACpB,eAAe,EAAE,QAAQ,CAAC,OAAO,CAAC,QAAQ,GACzC,IAAI;IAaP,mBAAmB,CAAC,KAAK,EAAE,QAAQ,CAAC,OAAO,CAAC,qBAAqB,GAAG,IAAI;IASxE,kBAAkB,CAAC,KAAK,EAAE,QAAQ,CAAC,OAAO,CAAC,oBAAoB,GAAG,IAAI;IActE,gBAAgB,CAAC,KAAK,EAAE,QAAQ,CAAC,OAAO,CAAC,kBAAkB,GAAG,IAAI;CAYnE"}
+265
View File
@@ -0,0 +1,265 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.NetworkManager = exports.NetworkManagerEmittedEvents = void 0;
/**
* 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.
*/
const EventEmitter_js_1 = require("./EventEmitter.js");
const assert_js_1 = require("./assert.js");
const helper_js_1 = require("./helper.js");
const HTTPRequest_js_1 = require("./HTTPRequest.js");
const HTTPResponse_js_1 = require("./HTTPResponse.js");
/**
* We use symbols to prevent any external parties listening to these events.
* They are internal to Puppeteer.
*
* @internal
*/
exports.NetworkManagerEmittedEvents = {
Request: Symbol('NetworkManager.Request'),
Response: Symbol('NetworkManager.Response'),
RequestFailed: Symbol('NetworkManager.RequestFailed'),
RequestFinished: Symbol('NetworkManager.RequestFinished'),
};
/**
* @internal
*/
class NetworkManager extends EventEmitter_js_1.EventEmitter {
constructor(client, ignoreHTTPSErrors, frameManager) {
super();
this._requestIdToRequest = new Map();
this._requestIdToRequestWillBeSentEvent = new Map();
this._extraHTTPHeaders = {};
this._offline = false;
this._credentials = null;
this._attemptedAuthentications = new Set();
this._userRequestInterceptionEnabled = false;
this._protocolRequestInterceptionEnabled = false;
this._userCacheDisabled = false;
this._requestIdToInterceptionId = new Map();
this._client = client;
this._ignoreHTTPSErrors = ignoreHTTPSErrors;
this._frameManager = frameManager;
this._client.on('Fetch.requestPaused', this._onRequestPaused.bind(this));
this._client.on('Fetch.authRequired', this._onAuthRequired.bind(this));
this._client.on('Network.requestWillBeSent', this._onRequestWillBeSent.bind(this));
this._client.on('Network.requestServedFromCache', this._onRequestServedFromCache.bind(this));
this._client.on('Network.responseReceived', this._onResponseReceived.bind(this));
this._client.on('Network.loadingFinished', this._onLoadingFinished.bind(this));
this._client.on('Network.loadingFailed', this._onLoadingFailed.bind(this));
}
async initialize() {
await this._client.send('Network.enable');
if (this._ignoreHTTPSErrors)
await this._client.send('Security.setIgnoreCertificateErrors', {
ignore: true,
});
}
async authenticate(credentials) {
this._credentials = credentials;
await this._updateProtocolRequestInterception();
}
async setExtraHTTPHeaders(extraHTTPHeaders) {
this._extraHTTPHeaders = {};
for (const key of Object.keys(extraHTTPHeaders)) {
const value = extraHTTPHeaders[key];
assert_js_1.assert(helper_js_1.helper.isString(value), `Expected value of header "${key}" to be String, but "${typeof value}" is found.`);
this._extraHTTPHeaders[key.toLowerCase()] = value;
}
await this._client.send('Network.setExtraHTTPHeaders', {
headers: this._extraHTTPHeaders,
});
}
extraHTTPHeaders() {
return Object.assign({}, this._extraHTTPHeaders);
}
async setOfflineMode(value) {
if (this._offline === value)
return;
this._offline = value;
await this._client.send('Network.emulateNetworkConditions', {
offline: this._offline,
// values of 0 remove any active throttling. crbug.com/456324#c9
latency: 0,
downloadThroughput: -1,
uploadThroughput: -1,
});
}
async setUserAgent(userAgent) {
await this._client.send('Network.setUserAgentOverride', { userAgent });
}
async setCacheEnabled(enabled) {
this._userCacheDisabled = !enabled;
await this._updateProtocolCacheDisabled();
}
async setRequestInterception(value) {
this._userRequestInterceptionEnabled = value;
await this._updateProtocolRequestInterception();
}
async _updateProtocolRequestInterception() {
const enabled = this._userRequestInterceptionEnabled || !!this._credentials;
if (enabled === this._protocolRequestInterceptionEnabled)
return;
this._protocolRequestInterceptionEnabled = enabled;
if (enabled) {
await Promise.all([
this._updateProtocolCacheDisabled(),
this._client.send('Fetch.enable', {
handleAuthRequests: true,
patterns: [{ urlPattern: '*' }],
}),
]);
}
else {
await Promise.all([
this._updateProtocolCacheDisabled(),
this._client.send('Fetch.disable'),
]);
}
}
async _updateProtocolCacheDisabled() {
await this._client.send('Network.setCacheDisabled', {
cacheDisabled: this._userCacheDisabled || this._protocolRequestInterceptionEnabled,
});
}
_onRequestWillBeSent(event) {
// Request interception doesn't happen for data URLs with Network Service.
if (this._protocolRequestInterceptionEnabled &&
!event.request.url.startsWith('data:')) {
const requestId = event.requestId;
const interceptionId = this._requestIdToInterceptionId.get(requestId);
if (interceptionId) {
this._onRequest(event, interceptionId);
this._requestIdToInterceptionId.delete(requestId);
}
else {
this._requestIdToRequestWillBeSentEvent.set(event.requestId, event);
}
return;
}
this._onRequest(event, null);
}
_onAuthRequired(event) {
let response = 'Default';
if (this._attemptedAuthentications.has(event.requestId)) {
response = 'CancelAuth';
}
else if (this._credentials) {
response = 'ProvideCredentials';
this._attemptedAuthentications.add(event.requestId);
}
const { username, password } = this._credentials || {
username: undefined,
password: undefined,
};
this._client
.send('Fetch.continueWithAuth', {
requestId: event.requestId,
authChallengeResponse: { response, username, password },
})
.catch(helper_js_1.debugError);
}
_onRequestPaused(event) {
if (!this._userRequestInterceptionEnabled &&
this._protocolRequestInterceptionEnabled) {
this._client
.send('Fetch.continueRequest', {
requestId: event.requestId,
})
.catch(helper_js_1.debugError);
}
const requestId = event.networkId;
const interceptionId = event.requestId;
if (requestId && this._requestIdToRequestWillBeSentEvent.has(requestId)) {
const requestWillBeSentEvent = this._requestIdToRequestWillBeSentEvent.get(requestId);
this._onRequest(requestWillBeSentEvent, interceptionId);
this._requestIdToRequestWillBeSentEvent.delete(requestId);
}
else {
this._requestIdToInterceptionId.set(requestId, interceptionId);
}
}
_onRequest(event, interceptionId) {
let redirectChain = [];
if (event.redirectResponse) {
const request = this._requestIdToRequest.get(event.requestId);
// If we connect late to the target, we could have missed the
// requestWillBeSent event.
if (request) {
this._handleRequestRedirect(request, event.redirectResponse);
redirectChain = request._redirectChain;
}
}
const frame = event.frameId
? this._frameManager.frame(event.frameId)
: null;
const request = new HTTPRequest_js_1.HTTPRequest(this._client, frame, interceptionId, this._userRequestInterceptionEnabled, event, redirectChain);
this._requestIdToRequest.set(event.requestId, request);
this.emit(exports.NetworkManagerEmittedEvents.Request, request);
}
_onRequestServedFromCache(event) {
const request = this._requestIdToRequest.get(event.requestId);
if (request)
request._fromMemoryCache = true;
}
_handleRequestRedirect(request, responsePayload) {
const response = new HTTPResponse_js_1.HTTPResponse(this._client, request, responsePayload);
request._response = response;
request._redirectChain.push(request);
response._resolveBody(new Error('Response body is unavailable for redirect responses'));
this._requestIdToRequest.delete(request._requestId);
this._attemptedAuthentications.delete(request._interceptionId);
this.emit(exports.NetworkManagerEmittedEvents.Response, response);
this.emit(exports.NetworkManagerEmittedEvents.RequestFinished, request);
}
_onResponseReceived(event) {
const request = this._requestIdToRequest.get(event.requestId);
// FileUpload sends a response without a matching request.
if (!request)
return;
const response = new HTTPResponse_js_1.HTTPResponse(this._client, request, event.response);
request._response = response;
this.emit(exports.NetworkManagerEmittedEvents.Response, response);
}
_onLoadingFinished(event) {
const request = this._requestIdToRequest.get(event.requestId);
// For certain requestIds we never receive requestWillBeSent event.
// @see https://crbug.com/750469
if (!request)
return;
// Under certain conditions we never get the Network.responseReceived
// event from protocol. @see https://crbug.com/883475
if (request.response())
request.response()._resolveBody(null);
this._requestIdToRequest.delete(request._requestId);
this._attemptedAuthentications.delete(request._interceptionId);
this.emit(exports.NetworkManagerEmittedEvents.RequestFinished, request);
}
_onLoadingFailed(event) {
const request = this._requestIdToRequest.get(event.requestId);
// For certain requestIds we never receive requestWillBeSent event.
// @see https://crbug.com/750469
if (!request)
return;
request._failureText = event.errorText;
const response = request.response();
if (response)
response._resolveBody(null);
this._requestIdToRequest.delete(request._requestId);
this._attemptedAuthentications.delete(request._interceptionId);
this.emit(exports.NetworkManagerEmittedEvents.RequestFailed, request);
}
}
exports.NetworkManager = NetworkManager;
+152
View File
@@ -0,0 +1,152 @@
/**
* Copyright 2020 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.
*/
/**
* @public
*/
export interface PDFMargin {
top?: string | number;
bottom?: string | number;
left?: string | number;
right?: string | number;
}
/**
* All the valid paper format types when printing a PDF.
*
* @remarks
*
* The sizes of each format are as follows:
* - `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.54in x 23.4in
*
* - `A3`: 11.7in x 16.54in
*
* - `A4`: 8.27in x 11.7in
*
* - `A5`: 5.83in x 8.27in
*
* - `A6`: 4.13in x 5.83in
*
* @public
*/
export declare type PaperFormat = 'letter' | 'legal' | 'tabloid' | 'ledger' | 'a0' | 'a1' | 'a2' | 'a3' | 'a4' | 'a5' | 'a6';
/**
* Valid options to configure PDF generation via {@link Page.pdf}.
* @public
*/
export interface PDFOptions {
/**
* Scales the rendering of the web page. Amount must be between `0.1` and `2`.
* @defaultValue 1
*/
scale?: number;
/**
* Whether to show the header and footer.
* @defaultValue false
*/
displayHeaderFooter?: boolean;
/**
* HTML template for the print header. Should be valid HTML with the following
* classes used to inject values into them:
* - `date` formatted print date
*
* - `title` document title
*
* - `url` document location
*
* - `pageNumber` current page number
*
* - `totalPages` total pages in the document
*/
headerTemplate?: string;
/**
* HTML template for the print footer. Has the same constraints and support
* for special classes as {@link PDFOptions.headerTemplate}.
*/
footerTemplate?: string;
/**
* Set to `true` to print background graphics.
* @defaultValue false
*/
printBackground?: boolean;
/**
* Whether to print in landscape orientation.
* @defaultValue = false
*/
landscape?: boolean;
/**
* Paper ranges to print, e.g. `1-5, 8, 11-13`.
* @defaultValue The empty string, which means all pages are printed.
*/
pageRanges?: string;
/**
* @remarks
* If set, this takes priority over the `width` and `height` options.
* @defaultValue `letter`.
*/
format?: PaperFormat;
/**
* Sets the width of paper. You can pass in a number or a string with a unit.
*/
width?: string | number;
/**
* Sets the height of paper. You can pass in a number or a string with a unit.
*/
height?: string | number;
/**
* Give any CSS `@page` size declared in the page priority over what is
* declared in the `width` or `height` or `format` option.
* @defaultValue `false`, which will scale the content to fit the paper size.
*/
preferCSSPageSize?: boolean;
/**
* Set the PDF margins.
* @defaultValue no margins are set.
*/
margin?: PDFMargin;
/**
* The path to save the file to.
*
* @remarks
*
* If the path is relative, it's resolved relative to the current working directory.
*
* @defaultValue the empty string, which means the PDF will not be written to disk.
*/
path?: string;
}
/**
* @internal
*/
export interface PaperFormatDimensions {
width: number;
height: number;
}
/**
* @internal
*/
export declare const paperFormats: Record<PaperFormat, PaperFormatDimensions>;
//# sourceMappingURL=PDFOptions.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"PDFOptions.d.ts","sourceRoot":"","sources":["../../../../src/common/PDFOptions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH;;GAEG;AACH,MAAM,WAAW,SAAS;IACxB,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACzB,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;CACzB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,oBAAY,WAAW,GACnB,QAAQ,GACR,OAAO,GACP,SAAS,GACT,QAAQ,GACR,IAAI,GACJ,IAAI,GACJ,IAAI,GACJ,IAAI,GACJ,IAAI,GACJ,IAAI,GACJ,IAAI,CAAC;AAET;;;GAGG;AACH,MAAM,WAAW,UAAU;IACzB;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;OAGG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B;;;;;;;;;;;;OAYG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;OAGG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B;;;OAGG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;OAIG;IACH,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACxB;;OAEG;IACH,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACzB;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B;;;OAGG;IACH,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB;;;;;;;;OAQG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,eAAO,MAAM,YAAY,EAAE,MAAM,CAAC,WAAW,EAAE,qBAAqB,CAY1D,CAAC"}
+34
View File
@@ -0,0 +1,34 @@
"use strict";
/**
* Copyright 2020 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.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.paperFormats = void 0;
/**
* @internal
*/
exports.paperFormats = {
letter: { width: 8.5, height: 11 },
legal: { width: 8.5, height: 14 },
tabloid: { width: 11, height: 17 },
ledger: { width: 17, height: 11 },
a0: { width: 33.1, height: 46.8 },
a1: { width: 23.4, height: 33.1 },
a2: { width: 16.54, height: 23.4 },
a3: { width: 11.7, height: 16.54 },
a4: { width: 8.27, height: 11.7 },
a5: { width: 5.83, height: 8.27 },
a6: { width: 4.13, height: 5.83 },
};
+908
View File
@@ -0,0 +1,908 @@
/**
* 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.
*/
/// <reference types="node" />
import { EventEmitter } from './EventEmitter.js';
import { CDPSession } from './Connection.js';
import { Frame } from './FrameManager.js';
import { Keyboard, Mouse, Touchscreen, MouseButton } from './Input.js';
import { Tracing } from './Tracing.js';
import { Coverage } from './Coverage.js';
import { WebWorker } from './WebWorker.js';
import { Browser, BrowserContext } from './Browser.js';
import { Target } from './Target.js';
import { JSHandle, ElementHandle } from './JSHandle.js';
import { Viewport } from './PuppeteerViewport.js';
import { Credentials } from './NetworkManager.js';
import { HTTPRequest } from './HTTPRequest.js';
import { HTTPResponse } from './HTTPResponse.js';
import { Accessibility } from './Accessibility.js';
import { FileChooser } from './FileChooser.js';
import { PuppeteerLifeCycleEvent } from './LifecycleWatcher.js';
import { Protocol } from 'devtools-protocol';
import { SerializableOrJSHandle, EvaluateHandleFn, WrapElementHandle, EvaluateFn, EvaluateFnReturnType, UnwrapPromiseLike } from './EvalTypes.js';
import { PDFOptions } from './PDFOptions.js';
/**
* @public
*/
export interface Metrics {
Timestamp?: number;
Documents?: number;
Frames?: number;
JSEventListeners?: number;
Nodes?: number;
LayoutCount?: number;
RecalcStyleCount?: number;
LayoutDuration?: number;
RecalcStyleDuration?: number;
ScriptDuration?: number;
TaskDuration?: number;
JSHeapUsedSize?: number;
JSHeapTotalSize?: number;
}
/**
* @public
*/
export interface WaitTimeoutOptions {
/**
* Maximum wait time in milliseconds, defaults to 30 seconds, pass `0` to
* disable the timeout.
*
* @remarks
* The default value can be changed by using the
* {@link Page.setDefaultTimeout} method.
*/
timeout?: number;
}
/**
* @public
*/
export interface WaitForOptions {
/**
* Maximum wait time in milliseconds, defaults to 30 seconds, pass `0` to
* disable the timeout.
*
* @remarks
* The default value can be changed by using the
* {@link Page.setDefaultTimeout} or {@link Page.setDefaultNavigationTimeout}
* methods.
*/
timeout?: number;
waitUntil?: PuppeteerLifeCycleEvent | PuppeteerLifeCycleEvent[];
}
/**
* @public
*/
export interface GeolocationOptions {
/**
* Latitude between -90 and 90.
*/
longitude: number;
/**
* Longitude between -180 and 180.
*/
latitude: number;
/**
* Optional non-negative accuracy value.
*/
accuracy?: number;
}
interface MediaFeature {
name: string;
value: string;
}
interface ScreenshotClip {
x: number;
y: number;
width: number;
height: number;
}
interface ScreenshotOptions {
type?: 'png' | 'jpeg';
path?: string;
fullPage?: boolean;
clip?: ScreenshotClip;
quality?: number;
omitBackground?: boolean;
encoding?: string;
}
/**
* All the events that a page instance may emit.
*
* @public
*/
export declare const enum PageEmittedEvents {
/** Emitted when the page closes. */
Close = "close",
/**
* 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.
*
* @remarks
*
* A `console` event provides a {@link ConsoleMessage} representing the
* console message that was logged.
*
* @example
* 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'}));
* ```
*/
Console = "console",
/**
* Emitted when a JavaScript dialog appears, such as `alert`, `prompt`,
* `confirm` or `beforeunload`. Puppeteer can respond to the dialog via
* {@link Dialog.accept} or {@link Dialog.dismiss}.
*/
Dialog = "dialog",
/**
* Emitted when the JavaScript
* {@link https://developer.mozilla.org/en-US/docs/Web/Events/DOMContentLoaded | DOMContentLoaded } event is dispatched.
*/
DOMContentLoaded = "domcontentloaded",
/**
* Emitted when the page crashes. Will contain an `Error`.
*/
Error = "error",
/** Emitted when a frame is attached. Will contain a {@link Frame}. */
FrameAttached = "frameattached",
/** Emitted when a frame is detached. Will contain a {@link Frame}. */
FrameDetached = "framedetached",
/** Emitted when a frame is navigated to a new URL. Will contain a {@link Frame}. */
FrameNavigated = "framenavigated",
/**
* Emitted when the JavaScript
* {@link https://developer.mozilla.org/en-US/docs/Web/Events/load | load}
* event is dispatched.
*/
Load = "load",
/**
* Emitted when the JavaScript code makes a call to `console.timeStamp`. For
* the list of metrics see {@link Page.metrics | page.metrics}.
*
* @remarks
* Contains an object with two properties:
* - `title`: the title passed to `console.timeStamp`
* - `metrics`: objec containing metrics as key/value pairs. The values will
* be `number`s.
*/
Metrics = "metrics",
/**
* Emitted when an uncaught exception happens within the page.
* Contains an `Error`.
*/
PageError = "pageerror",
/**
* Emitted when the page opens a new tab or window.
*
* Contains a {@link Page} corresponding to the popup window.
*
* @example
*
* ```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')),
* ]);
* ```
*/
Popup = "popup",
/**
* Emitted when a page issues a request and contains a {@link HTTPRequest}.
*
* @remarks
* The object is readonly. See {@Page.setRequestInterception} for intercepting
* and mutating requests.
*/
Request = "request",
/**
* Emitted when a request fails, for example by timing out.
*
* Contains a {@link HTTPRequest}.
*
* @remarks
*
* NOTE: HTTP Error responses, such as 404 or 503, are still successful
* responses from HTTP standpoint, so request will complete with
* `requestfinished` event and not with `requestfailed`.
*/
RequestFailed = "requestfailed",
/**
* Emitted when a request finishes successfully. Contains a {@link HTTPRequest}.
*/
RequestFinished = "requestfinished",
/**
* Emitted when a response is received. Contains a {@link HTTPResponse}.
*/
Response = "response",
/**
* Emitted when a dedicated
* {@link https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API | WebWorker}
* is spawned by the page.
*/
WorkerCreated = "workercreated",
/**
* Emitted when a dedicated
* {@link https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API | WebWorker}
* is destroyed by the page.
*/
WorkerDestroyed = "workerdestroyed"
}
/**
* Page provides methods to interact with a single tab or
* {@link https://developer.chrome.com/extensions/background_pages | extension background page} in Chromium.
*
* @remarks
*
* One Browser instance might have multiple Page instances.
*
* @example
* This example creates a page, navigates it to a URL, and then * saves a screenshot:
* ```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: 'screenshot.png'});
* await browser.close();
* })();
* ```
*
* The Page class extends from Puppeteer's {@link EventEmitter} class and will
* emit various events which are documented in the {@link PageEmittedEvents} enum.
*
* @example
* 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 `off` method:
*
* ```js
* function logRequest(interceptedRequest) {
* console.log('A request was made:', interceptedRequest.url());
* }
* page.on('request', logRequest);
* // Sometime later...
* page.off('request', logRequest);
* ```
* @public
*/
export declare class Page extends EventEmitter {
/**
* @internal
*/
static create(client: CDPSession, target: Target, ignoreHTTPSErrors: boolean, defaultViewport: Viewport | null): Promise<Page>;
private _closed;
private _client;
private _target;
private _keyboard;
private _mouse;
private _timeoutSettings;
private _touchscreen;
private _accessibility;
private _frameManager;
private _emulationManager;
private _tracing;
private _pageBindings;
private _coverage;
private _javascriptEnabled;
private _viewport;
private _screenshotTaskQueue;
private _workers;
private _fileChooserInterceptors;
private _disconnectPromise?;
/**
* @internal
*/
constructor(client: CDPSession, target: Target, ignoreHTTPSErrors: boolean);
private _initialize;
private _onFileChooser;
/**
* @returns `true` if the page has JavaScript enabled, `false` otherwise.
*/
isJavaScriptEnabled(): boolean;
/**
* @param options - Optional waiting parameters
* @returns Resolves after a page requests a file picker.
*/
waitForFileChooser(options?: WaitTimeoutOptions): Promise<FileChooser>;
/**
* Sets the page's geolocation.
*
* @remarks
* Consider using {@link BrowserContext.overridePermissions} to grant
* permissions for the page to read its geolocation.
*
* @example
* ```js
* await page.setGeolocation({latitude: 59.95, longitude: 30.31667});
* ```
*/
setGeolocation(options: GeolocationOptions): Promise<void>;
/**
* @returns A target this page was created from.
*/
target(): Target;
/**
* @returns The browser this page belongs to.
*/
browser(): Browser;
/**
* @returns The browser context that the page belongs to
*/
browserContext(): BrowserContext;
private _onTargetCrashed;
private _onLogEntryAdded;
/**
* @returns The page's main frame.
*/
mainFrame(): Frame;
get keyboard(): Keyboard;
get touchscreen(): Touchscreen;
get coverage(): Coverage;
get tracing(): Tracing;
get accessibility(): Accessibility;
/**
* @returns An array of all frames attached to the page.
*/
frames(): Frame[];
/**
* @returns all of the dedicated
* {@link https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API | WebWorkers}
* associated with the page.
*/
workers(): WebWorker[];
/**
* @param value - Whether to enable request interception.
*
* @remarks
* Activating request interception enables {@link HTTPRequest.abort},
* {@link HTTPRequest.continue} and {@link HTTPRequest.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.
*
* **NOTE** Enabling request interception disables page caching.
*
* @example
* An example of a naïve request interceptor that aborts all image requests:
* ```js
* const puppeteer = require('puppeteer');
* (async () => {
* const browser = await puppeteer.launch();
* 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();
* })();
* ```
*/
setRequestInterception(value: boolean): Promise<void>;
/**
* @param enabled - When `true`, enables offline mode for the page.
*/
setOfflineMode(enabled: boolean): Promise<void>;
/**
* @param timeout - Maximum navigation time in milliseconds.
*/
setDefaultNavigationTimeout(timeout: number): void;
/**
* @param timeout - Maximum time in milliseconds.
*/
setDefaultTimeout(timeout: number): void;
/**
* Runs `document.querySelector` within the page. If no element matches the
* selector, the return value resolves to `null`.
*
* @remarks
* Shortcut for {@link Frame.$ | Page.mainFrame().$(selector) }.
*
* @param selector - A
* {@link https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors | selector}
* to query page for.
*/
$(selector: string): Promise<ElementHandle | null>;
/**
* @remarks
*
* The only difference between {@link Page.evaluate | page.evaluate} and
* `page.evaluateHandle` is that `evaluateHandle` will return the value
* wrapped in an in-page object.
*
* If the function passed to `page.evaluteHandle` returns a Promise, the
* function will wait for the promise to resolve and return its value.
*
* You can pass a string instead of a function (although functions are
* recommended as they are easier to debug and use with TypeScript):
*
* @example
* ```
* const aHandle = await page.evaluateHandle('document')
* ```
*
* @example
* {@link JSHandle} instances can be passed as arguments to the `pageFunction`:
* ```
* const aHandle = await page.evaluateHandle(() => document.body);
* const resultHandle = await page.evaluateHandle(body => body.innerHTML, aHandle);
* console.log(await resultHandle.jsonValue());
* await resultHandle.dispose();
* ```
*
* Most of the time this function returns a {@link JSHandle},
* but if `pageFunction` returns a reference to an element,
* you instead get an {@link ElementHandle} back:
*
* @example
* ```
* const button = await page.evaluateHandle(() => document.querySelector('button'));
* // can call `click` because `button` is an `ElementHandle`
* await button.click();
* ```
*
* The TypeScript definitions assume that `evaluateHandle` returns
* a `JSHandle`, but if you know it's going to return an
* `ElementHandle`, pass it as the generic argument:
*
* ```
* const button = await page.evaluateHandle<ElementHandle>(...);
* ```
*
* @param pageFunction - a function that is run within the page
* @param args - arguments to be passed to the pageFunction
*/
evaluateHandle<HandlerType extends JSHandle = JSHandle>(pageFunction: EvaluateHandleFn, ...args: SerializableOrJSHandle[]): Promise<HandlerType>;
/**
* This method iterates the JavaScript heap and finds all objects with the
* given prototype.
*
* @remarks
*
* @example
*
* ```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();
* ```
* @param prototypeHandle - a handle to the object prototype.
*/
queryObjects(prototypeHandle: JSHandle): Promise<JSHandle>;
/**
* This method runs `document.querySelector` within the page and passes the
* result as the first argument to the `pageFunction`.
*
* @remarks
*
* If no element is found matching `selector`, the method will throw an error.
*
* If `pageFunction` returns a promise `$eval` will wait for the promise to
* resolve and then return its value.
*
* @example
*
* ```
* 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', el => el.outerHTML);
* ```
*
* If you are using TypeScript, you may have to provide an explicit type to the
* first argument of the `pageFunction`.
* By default it is typed as `Element`, but you may need to provide a more
* specific sub-type:
*
* @example
*
* ```
* // if you don't provide HTMLInputElement here, TS will error
* // as `value` is not on `Element`
* const searchValue = await page.$eval('#search', (el: HTMLInputElement) => el.value);
* ```
*
* The compiler should be able to infer the return type
* from the `pageFunction` you provide. If it is unable to, you can use the generic
* type to tell the compiler what return type you expect from `$eval`:
*
* @example
*
* ```
* // The compiler can infer the return type in this case, but if it can't
* // or if you want to be more explicit, provide it as the generic type.
* const searchValue = await page.$eval<string>(
* '#search', (el: HTMLInputElement) => el.value
* );
* ```
*
* @param selector - the
* {@link https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors | selector}
* to query for
* @param pageFunction - the function to be evaluated in the page context.
* Will be passed the result of `document.querySelector(selector)` as its
* first argument.
* @param args - any additional arguments to pass through to `pageFunction`.
*
* @returns The result of calling `pageFunction`. If it returns an element it
* is wrapped in an {@link ElementHandle}, else the raw value itself is
* returned.
*/
$eval<ReturnType>(selector: string, pageFunction: (element: Element, ...args: unknown[]) => ReturnType | Promise<ReturnType>, ...args: SerializableOrJSHandle[]): Promise<WrapElementHandle<ReturnType>>;
/**
* This method runs `Array.from(document.querySelectorAll(selector))` within
* the page and passes the result as the first argument to the `pageFunction`.
*
* @remarks
*
* If `pageFunction` returns a promise `$$eval` will wait for the promise to
* resolve and then return its value.
*
* @example
*
* ```
* // get the amount of divs on the page
* const divCount = await page.$$eval('div', divs => divs.length);
*
* // get the text content of all the `.options` elements:
* const options = await page.$$eval('div > span.options', options => {
* return options.map(option => option.textContent)
* });
* ```
*
* If you are using TypeScript, you may have to provide an explicit type to the
* first argument of the `pageFunction`.
* By default it is typed as `Element[]`, but you may need to provide a more
* specific sub-type:
*
* @example
*
* ```
* // if you don't provide HTMLInputElement here, TS will error
* // as `value` is not on `Element`
* await page.$$eval('input', (elements: HTMLInputElement[]) => {
* return elements.map(e => e.value);
* });
* ```
*
* The compiler should be able to infer the return type
* from the `pageFunction` you provide. If it is unable to, you can use the generic
* type to tell the compiler what return type you expect from `$$eval`:
*
* @example
*
* ```
* // The compiler can infer the return type in this case, but if it can't
* // or if you want to be more explicit, provide it as the generic type.
* const allInputValues = await page.$$eval<string[]>(
* 'input', (elements: HTMLInputElement[]) => elements.map(e => e.textContent)
* );
* ```
*
* @param selector the
* {@link https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors | selector}
* to query for
* @param pageFunction the function to be evaluated in the page context. Will
* be passed the result of `Array.from(document.querySelectorAll(selector))`
* as its first argument.
* @param args any additional arguments to pass through to `pageFunction`.
*
* @returns The result of calling `pageFunction`. If it returns an element it
* is wrapped in an {@link ElementHandle}, else the raw value itself is
* returned.
*/
$$eval<ReturnType>(selector: string, pageFunction: (elements: Element[], ...args: unknown[]) => ReturnType | Promise<ReturnType>, ...args: SerializableOrJSHandle[]): Promise<WrapElementHandle<ReturnType>>;
$$(selector: string): Promise<ElementHandle[]>;
$x(expression: string): Promise<ElementHandle[]>;
/**
* 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.
*/
cookies(...urls: string[]): Promise<Protocol.Network.Cookie[]>;
deleteCookie(...cookies: Protocol.Network.DeleteCookiesRequest[]): Promise<void>;
setCookie(...cookies: Protocol.Network.CookieParam[]): Promise<void>;
addScriptTag(options: {
url?: string;
path?: string;
content?: string;
type?: string;
}): Promise<ElementHandle>;
addStyleTag(options: {
url?: string;
path?: string;
content?: string;
}): Promise<ElementHandle>;
exposeFunction(name: string, puppeteerFunction: Function): Promise<void>;
authenticate(credentials: Credentials): Promise<void>;
setExtraHTTPHeaders(headers: Record<string, string>): Promise<void>;
setUserAgent(userAgent: string): Promise<void>;
metrics(): Promise<Metrics>;
private _emitMetrics;
private _buildMetricsObject;
private _handleException;
private _onConsoleAPI;
private _onBindingCalled;
private _addConsoleMessage;
private _onDialog;
url(): string;
content(): Promise<string>;
setContent(html: string, options?: WaitForOptions): Promise<void>;
goto(url: string, options?: WaitForOptions & {
referer?: string;
}): Promise<HTTPResponse>;
reload(options?: WaitForOptions): Promise<HTTPResponse | null>;
waitForNavigation(options?: WaitForOptions): Promise<HTTPResponse | null>;
private _sessionClosePromise;
waitForRequest(urlOrPredicate: string | Function, options?: {
timeout?: number;
}): Promise<HTTPRequest>;
waitForResponse(urlOrPredicate: string | Function, options?: {
timeout?: number;
}): Promise<HTTPResponse>;
goBack(options?: WaitForOptions): Promise<HTTPResponse | null>;
goForward(options?: WaitForOptions): Promise<HTTPResponse | null>;
private _go;
bringToFront(): Promise<void>;
emulate(options: {
viewport: Viewport;
userAgent: string;
}): Promise<void>;
setJavaScriptEnabled(enabled: boolean): Promise<void>;
setBypassCSP(enabled: boolean): Promise<void>;
emulateMediaType(type?: string): Promise<void>;
emulateMediaFeatures(features?: MediaFeature[]): Promise<void>;
emulateTimezone(timezoneId?: string): Promise<void>;
/**
* Emulates the idle state.
* If no arguments set, clears idle state emulation.
*
* @example
* ```js
* // set idle emulation
* await page.emulateIdleState({isUserActive: true, isScreenUnlocked: false});
*
* // do some checks here
* ...
*
* // clear idle emulation
* await page.emulateIdleState();
* ```
*
* @param overrides Mock idle state. If not set, clears idle overrides
* @param isUserActive Mock isUserActive
* @param isScreenUnlocked Mock isScreenUnlocked
*/
emulateIdleState(overrides?: {
isUserActive: boolean;
isScreenUnlocked: boolean;
}): Promise<void>;
/**
* Simulates the given vision deficiency on the page.
*
* @example
* ```js
* const puppeteer = require('puppeteer');
*
* (async () => {
* const browser = await puppeteer.launch();
* const page = await browser.newPage();
* await page.goto('https://v8.dev/blog/10-years');
*
* await page.emulateVisionDeficiency('achromatopsia');
* await page.screenshot({ path: 'achromatopsia.png' });
*
* await page.emulateVisionDeficiency('deuteranopia');
* await page.screenshot({ path: 'deuteranopia.png' });
*
* await page.emulateVisionDeficiency('blurredVision');
* await page.screenshot({ path: 'blurred-vision.png' });
*
* await browser.close();
* })();
* ```
*
* @param type - the type of deficiency to simulate, or `'none'` to reset.
*/
emulateVisionDeficiency(type?: Protocol.Emulation.SetEmulatedVisionDeficiencyRequest['type']): Promise<void>;
setViewport(viewport: Viewport): Promise<void>;
viewport(): Viewport | null;
/**
* @remarks
*
* Evaluates a function in the page's context and returns the result.
*
* If the function passed to `page.evaluteHandle` returns a Promise, the
* function will wait for the promise to resolve and return its value.
*
* @example
*
* ```js
* const result = await frame.evaluate(() => {
* return Promise.resolve(8 * 7);
* });
* console.log(result); // prints "56"
* ```
*
* You can pass a string instead of a function (although functions are
* recommended as they are easier to debug and use with TypeScript):
*
* @example
* ```
* const aHandle = await page.evaluate('1 + 2');
* ```
*
* To get the best TypeScript experience, you should pass in as the
* generic the type of `pageFunction`:
*
* ```
* const aHandle = await page.evaluate<() => number>(() => 2);
* ```
*
* @example
*
* {@link ElementHandle} instances (including {@link JSHandle}s) can be passed
* as arguments to the `pageFunction`:
*
* ```
* const bodyHandle = await page.$('body');
* const html = await page.evaluate(body => body.innerHTML, bodyHandle);
* await bodyHandle.dispose();
* ```
*
* @param pageFunction - a function that is run within the page
* @param args - arguments to be passed to the pageFunction
*
* @returns the return value of `pageFunction`.
*/
evaluate<T extends EvaluateFn>(pageFunction: T, ...args: SerializableOrJSHandle[]): Promise<UnwrapPromiseLike<EvaluateFnReturnType<T>>>;
evaluateOnNewDocument(pageFunction: Function | string, ...args: unknown[]): Promise<void>;
setCacheEnabled(enabled?: boolean): Promise<void>;
screenshot(options?: ScreenshotOptions): Promise<Buffer | string | void>;
private _screenshotTask;
/**
* Generatees a PDF of the page with the `print` CSS media type.
* @remarks
*
* IMPORTANT: PDF generation is only supported in Chrome headless mode.
*
* To generate a PDF with the `screen` media type, call
* {@link Page.emulateMediaType | `page.emulateMediaType('screen')`} before
* calling `page.pdf()`.
*
* By default, `page.pdf()` generates a pdf with modified colors for printing.
* Use the
* {@link https://developer.mozilla.org/en-US/docs/Web/CSS/-webkit-print-color-adjust | `-webkit-print-color-adjust`}
* property to force rendering of exact colors.
*
*
* @param options - options for generating the PDF.
*/
pdf(options?: PDFOptions): Promise<Buffer>;
title(): Promise<string>;
close(options?: {
runBeforeUnload?: boolean;
}): Promise<void>;
isClosed(): boolean;
get mouse(): Mouse;
click(selector: string, options?: {
delay?: number;
button?: MouseButton;
clickCount?: number;
}): Promise<void>;
focus(selector: string): Promise<void>;
hover(selector: string): Promise<void>;
select(selector: string, ...values: string[]): Promise<string[]>;
tap(selector: string): Promise<void>;
type(selector: string, text: string, options?: {
delay: number;
}): Promise<void>;
/**
* @remarks
*
* This method behaves differently depending on the first parameter. If it's a
* `string`, it will be treated as a `selector` or `xpath` (if the string
* starts with `//`). This method then is a shortcut for
* {@link Page.waitForSelector} or {@link Page.waitForXPath}.
*
* If the first argument is a function this method is a shortcut for
* {@link Page.waitForFunction}.
*
* If the first argument is a `number`, it's treated as a timeout in
* milliseconds and the method returns a promise which resolves after the
* timeout.
*
* @param selectorOrFunctionOrTimeout - a selector, predicate or timeout to
* wait for.
* @param options - optional waiting parameters.
* @param args - arguments to pass to `pageFunction`.
*
* @deprecated Don't use this method directly. Instead use the more explicit
* methods available: {@link Page.waitForSelector},
* {@link Page.waitForXPath}, {@link Page.waitForFunction} or
* {@link Page.waitForTimeout}.
*/
waitFor(selectorOrFunctionOrTimeout: string | number | Function, options?: {
visible?: boolean;
hidden?: boolean;
timeout?: number;
polling?: string | number;
}, ...args: SerializableOrJSHandle[]): Promise<JSHandle>;
/**
* Causes your script to wait for the given number of milliseconds.
*
* @remarks
*
* It's generally recommended to not wait for a number of seconds, but instead
* use {@link Page.waitForSelector}, {@link Page.waitForXPath} or
* {@link Page.waitForFunction} to wait for exactly the conditions you want.
*
* @example
*
* Wait for 1 second:
*
* ```
* await page.waitForTimeout(1000);
* ```
*
* @param milliseconds - the number of milliseconds to wait.
*/
waitForTimeout(milliseconds: number): Promise<void>;
waitForSelector(selector: string, options?: {
visible?: boolean;
hidden?: boolean;
timeout?: number;
}): Promise<ElementHandle | null>;
waitForXPath(xpath: string, options?: {
visible?: boolean;
hidden?: boolean;
timeout?: number;
}): Promise<ElementHandle | null>;
waitForFunction(pageFunction: Function | string, options?: {
timeout?: number;
polling?: string | number;
}, ...args: SerializableOrJSHandle[]): Promise<JSHandle>;
}
export {};
//# sourceMappingURL=Page.d.ts.map
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large Load Diff
+21
View File
@@ -0,0 +1,21 @@
/**
* Copyright 2020 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.
*/
/**
* Supported products.
* @public
*/
export declare type Product = 'chrome' | 'firefox';
//# sourceMappingURL=Product.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"Product.d.ts","sourceRoot":"","sources":["../../../../src/common/Product.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH;;;GAGG;AACH,oBAAY,OAAO,GAAG,QAAQ,GAAG,SAAS,CAAC"}
+17
View File
@@ -0,0 +1,17 @@
"use strict";
/**
* Copyright 2020 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.
*/
Object.defineProperty(exports, "__esModule", { value: true });
+134
View File
@@ -0,0 +1,134 @@
/**
* 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.
*/
import { PuppeteerErrors } from './Errors.js';
import { ConnectionTransport } from './ConnectionTransport.js';
import { DevicesMap } from './DeviceDescriptors.js';
import { Browser } from './Browser.js';
import { CustomQueryHandler } from './QueryHandler.js';
import { Product } from './Product.js';
import { BrowserOptions } from './BrowserConnector.js';
/**
* Settings that are common to the Puppeteer class, regardless of enviroment.
* @internal
*/
export interface CommonPuppeteerSettings {
isPuppeteerCore: boolean;
}
export interface ConnectOptions extends BrowserOptions {
browserWSEndpoint?: string;
browserURL?: string;
transport?: ConnectionTransport;
product?: Product;
}
/**
* The main Puppeteer class.
*
* IMPORTANT: if you are using Puppeteer in a Node environment, you will get an
* instance of {@link PuppeteerNode} when you import or require `puppeteer`.
* That class extends `Puppeteer`, so has all the methods documented below as
* well as all that are defined on {@link PuppeteerNode}.
* @public
*/
export declare class Puppeteer {
protected _isPuppeteerCore: boolean;
protected _changedProduct: boolean;
/**
* @internal
*/
constructor(settings: CommonPuppeteerSettings);
/**
* This method attaches Puppeteer to an existing browser instance.
*
* @remarks
*
* @param options - Set of configurable options to set on the browser.
* @returns Promise which resolves to browser instance.
*/
connect(options: ConnectOptions): Promise<Browser>;
/**
* @remarks
* A list of devices to be used with `page.emulate(options)`. Actual list of devices can be found in {@link https://github.com/puppeteer/puppeteer/blob/main/src/common/DeviceDescriptors.ts | src/common/DeviceDescriptors.ts}.
*
* @example
*
* ```js
* const puppeteer = require('puppeteer');
* const iPhone = puppeteer.devices['iPhone 6'];
*
* (async () => {
* const browser = await puppeteer.launch();
* const page = await browser.newPage();
* await page.emulate(iPhone);
* await page.goto('https://www.google.com');
* // other actions...
* await browser.close();
* })();
* ```
*
*/
get devices(): DevicesMap;
/**
* @remarks
*
* Puppeteer methods might throw errors if they are unable to fulfill a request.
* For example, `page.waitForSelector(selector[, 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`.
*
* @example
* 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.
* }
* }
* ```
*/
get errors(): PuppeteerErrors;
/**
* Registers a {@link CustomQueryHandler | custom query handler}. After
* registration, the handler can be used everywhere where a selector is
* expected by prepending the selection string with `<name>/`. The name is
* only allowed to consist of lower- and upper case latin letters.
* @example
* ```
* puppeteer.registerCustomQueryHandler('text', { … });
* const aHandle = await page.$('text/…');
* ```
* @param name - The name that the custom query handler will be registered under.
* @param queryHandler - The {@link CustomQueryHandler | custom query handler} to
* register.
*/
registerCustomQueryHandler(name: string, queryHandler: CustomQueryHandler): void;
/**
* @param name - The name of the query handler to unregistered.
*/
unregisterCustomQueryHandler(name: string): void;
/**
* @returns a list with the names of all registered custom query handlers.
*/
customQueryHandlerNames(): string[];
/**
* Clears all registered handlers.
*/
clearCustomQueryHandlers(): void;
}
//# sourceMappingURL=Puppeteer.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"Puppeteer.d.ts","sourceRoot":"","sources":["../../../../src/common/Puppeteer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,EAAmB,eAAe,EAAE,MAAM,aAAa,CAAC;AAC/D,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAC/D,OAAO,EAAc,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAChE,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAKL,kBAAkB,EACnB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAoB,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAEzE;;;GAGG;AACH,MAAM,WAAW,uBAAuB;IACtC,eAAe,EAAE,OAAO,CAAC;CAC1B;AAED,MAAM,WAAW,cAAe,SAAQ,cAAc;IACpD,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,mBAAmB,CAAC;IAChC,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED;;;;;;;;GAQG;AACH,qBAAa,SAAS;IACpB,SAAS,CAAC,gBAAgB,EAAE,OAAO,CAAC;IACpC,SAAS,CAAC,eAAe,UAAS;IAElC;;OAEG;gBACS,QAAQ,EAAE,uBAAuB;IAI7C;;;;;;;OAOG;IACH,OAAO,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,OAAO,CAAC;IAIlD;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,IAAI,OAAO,IAAI,UAAU,CAExB;IAED;;;;;;;;;;;;;;;;;;;;;OAqBG;IACH,IAAI,MAAM,IAAI,eAAe,CAE5B;IAED;;;;;;;;;;;;;OAaG;IACH,0BAA0B,CACxB,IAAI,EAAE,MAAM,EACZ,YAAY,EAAE,kBAAkB,GAC/B,IAAI;IAIP;;OAEG;IACH,4BAA4B,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAIhD;;OAEG;IACH,uBAAuB,IAAI,MAAM,EAAE;IAInC;;OAEG;IACH,wBAAwB,IAAI,IAAI;CAGjC"}
+136
View File
@@ -0,0 +1,136 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Puppeteer = void 0;
/**
* 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.
*/
const Errors_js_1 = require("./Errors.js");
const DeviceDescriptors_js_1 = require("./DeviceDescriptors.js");
const QueryHandler_js_1 = require("./QueryHandler.js");
const BrowserConnector_js_1 = require("./BrowserConnector.js");
/**
* The main Puppeteer class.
*
* IMPORTANT: if you are using Puppeteer in a Node environment, you will get an
* instance of {@link PuppeteerNode} when you import or require `puppeteer`.
* That class extends `Puppeteer`, so has all the methods documented below as
* well as all that are defined on {@link PuppeteerNode}.
* @public
*/
class Puppeteer {
/**
* @internal
*/
constructor(settings) {
this._changedProduct = false;
this._isPuppeteerCore = settings.isPuppeteerCore;
}
/**
* This method attaches Puppeteer to an existing browser instance.
*
* @remarks
*
* @param options - Set of configurable options to set on the browser.
* @returns Promise which resolves to browser instance.
*/
connect(options) {
return BrowserConnector_js_1.connectToBrowser(options);
}
/**
* @remarks
* A list of devices to be used with `page.emulate(options)`. Actual list of devices can be found in {@link https://github.com/puppeteer/puppeteer/blob/main/src/common/DeviceDescriptors.ts | src/common/DeviceDescriptors.ts}.
*
* @example
*
* ```js
* const puppeteer = require('puppeteer');
* const iPhone = puppeteer.devices['iPhone 6'];
*
* (async () => {
* const browser = await puppeteer.launch();
* const page = await browser.newPage();
* await page.emulate(iPhone);
* await page.goto('https://www.google.com');
* // other actions...
* await browser.close();
* })();
* ```
*
*/
get devices() {
return DeviceDescriptors_js_1.devicesMap;
}
/**
* @remarks
*
* Puppeteer methods might throw errors if they are unable to fulfill a request.
* For example, `page.waitForSelector(selector[, 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`.
*
* @example
* 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.
* }
* }
* ```
*/
get errors() {
return Errors_js_1.puppeteerErrors;
}
/**
* Registers a {@link CustomQueryHandler | custom query handler}. After
* registration, the handler can be used everywhere where a selector is
* expected by prepending the selection string with `<name>/`. The name is
* only allowed to consist of lower- and upper case latin letters.
* @example
* ```
* puppeteer.registerCustomQueryHandler('text', { … });
* const aHandle = await page.$('text/…');
* ```
* @param name - The name that the custom query handler will be registered under.
* @param queryHandler - The {@link CustomQueryHandler | custom query handler} to
* register.
*/
registerCustomQueryHandler(name, queryHandler) {
QueryHandler_js_1.registerCustomQueryHandler(name, queryHandler);
}
/**
* @param name - The name of the query handler to unregistered.
*/
unregisterCustomQueryHandler(name) {
QueryHandler_js_1.unregisterCustomQueryHandler(name);
}
/**
* @returns a list with the names of all registered custom query handlers.
*/
customQueryHandlerNames() {
return QueryHandler_js_1.customQueryHandlerNames();
}
/**
* Clears all registered handlers.
*/
clearCustomQueryHandlers() {
QueryHandler_js_1.clearCustomQueryHandlers();
}
}
exports.Puppeteer = Puppeteer;
@@ -0,0 +1,24 @@
/**
* Copyright 2020 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.
*/
export interface Viewport {
width: number;
height: number;
deviceScaleFactor?: number;
isMobile?: boolean;
isLandscape?: boolean;
hasTouch?: boolean;
}
//# sourceMappingURL=PuppeteerViewport.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"PuppeteerViewport.d.ts","sourceRoot":"","sources":["../../../../src/common/PuppeteerViewport.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB"}
@@ -0,0 +1,2 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
+65
View File
@@ -0,0 +1,65 @@
/**
* Copyright 2020 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.
*/
import { WaitForSelectorOptions, DOMWorld } from './DOMWorld.js';
import { ElementHandle, JSHandle } from './JSHandle.js';
/**
* @internal
*/
export interface InternalQueryHandler {
queryOne?: (element: ElementHandle, selector: string) => Promise<ElementHandle | null>;
waitFor?: (domWorld: DOMWorld, selector: string, options: WaitForSelectorOptions) => Promise<ElementHandle | null>;
queryAll?: (element: ElementHandle, selector: string) => Promise<ElementHandle[]>;
queryAllArray?: (element: ElementHandle, selector: string) => Promise<JSHandle>;
}
/**
* Contains two functions `queryOne` and `queryAll` that can
* be {@link Puppeteer.registerCustomQueryHandler | registered}
* as alternative querying strategies. The functions `queryOne` and `queryAll`
* are executed in the page context. `queryOne` should take an `Element` and a
* selector string as argument and return a single `Element` or `null` if no
* element is found. `queryAll` takes the same arguments but should instead
* return a `NodeListOf<Element>` or `Array<Element>` with all the elements
* that match the given query selector.
* @public
*/
export interface CustomQueryHandler {
queryOne?: (element: Element | Document, selector: string) => Element | null;
queryAll?: (element: Element | Document, selector: string) => Element[] | NodeListOf<Element>;
}
/**
* @internal
*/
export declare function registerCustomQueryHandler(name: string, handler: CustomQueryHandler): void;
/**
* @internal
*/
export declare function unregisterCustomQueryHandler(name: string): void;
/**
* @internal
*/
export declare function customQueryHandlerNames(): string[];
/**
* @internal
*/
export declare function clearCustomQueryHandlers(): void;
/**
* @internal
*/
export declare function getQueryHandlerAndSelector(selector: string): {
updatedSelector: string;
queryHandler: InternalQueryHandler;
};
//# sourceMappingURL=QueryHandler.d.ts.map

Some files were not shown because too many files have changed in this diff Show More