mirror of
https://github.com/puppeteer/puppeteer
synced 2024-06-14 14:02:48 +00:00
chore: infrastructure for injecting scripts into DOMWorlds (#8801)
This commit is contained in:
parent
a5f1078feb
commit
bdcb748b98
@ -7,7 +7,7 @@ build/
|
||||
lib/
|
||||
|
||||
# Generated files
|
||||
tsconfig.tsbuildinfo
|
||||
**/*.tsbuildinfo
|
||||
puppeteer.api.json
|
||||
puppeteer*.tgz
|
||||
yarn.lock
|
||||
@ -18,9 +18,11 @@ yarn.lock
|
||||
test/output-*/
|
||||
.dev_profile*
|
||||
coverage/
|
||||
src/generated
|
||||
|
||||
# IDE Artifacts
|
||||
.vscode
|
||||
.devcontainer
|
||||
|
||||
# Misc
|
||||
.DS_Store
|
||||
@ -32,6 +34,7 @@ coverage/
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
## [END] Keep in sync with .gitignore
|
||||
|
||||
# ESLint ignores.
|
||||
|
2
.github/workflows/pre-release.yml
vendored
2
.github/workflows/pre-release.yml
vendored
@ -20,7 +20,7 @@ jobs:
|
||||
run: npm install
|
||||
- name: Build
|
||||
run: |
|
||||
node utils/generate_version_file.js
|
||||
ts-node utils/generate_sources.ts
|
||||
npm run docs
|
||||
- name: Version docs
|
||||
working-directory: ./website
|
||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -17,6 +17,7 @@ yarn.lock
|
||||
test/output-*/
|
||||
.dev_profile*
|
||||
coverage/
|
||||
src/generated
|
||||
|
||||
# IDE Artifacts
|
||||
.vscode
|
||||
|
@ -7,7 +7,7 @@ build/
|
||||
lib/
|
||||
|
||||
# Generated files
|
||||
tsconfig.tsbuildinfo
|
||||
**/*.tsbuildinfo
|
||||
puppeteer.api.json
|
||||
puppeteer*.tgz
|
||||
yarn.lock
|
||||
@ -18,9 +18,11 @@ yarn.lock
|
||||
test/output-*/
|
||||
.dev_profile*
|
||||
coverage/
|
||||
src/generated
|
||||
|
||||
# IDE Artifacts
|
||||
.vscode
|
||||
.devcontainer
|
||||
|
||||
# Misc
|
||||
.DS_Store
|
||||
|
1117
package-lock.json
generated
1117
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -41,8 +41,9 @@
|
||||
"lint:prettier": "prettier --check .",
|
||||
"lint:eslint": "([ \"$CI\" = true ] && eslint --ext js --ext ts --quiet -f codeframe . || eslint --ext js --ext ts .)",
|
||||
"install": "node install.js",
|
||||
"generate:sources": "ts-node utils/generate_sources.ts",
|
||||
"generate:types": "node utils/export_all.js && api-extractor run --local --verbose && eslint --ext ts --no-ignore --no-eslintrc -c .eslintrc.types.cjs --fix lib/types.d.ts",
|
||||
"generate:markdown": "ts-node -O '{\"module\":\"commonjs\"}' utils/generate_docs.ts && prettier --ignore-path none --write docs",
|
||||
"generate:markdown": "ts-node utils/generate_docs.ts && prettier --ignore-path none --write docs",
|
||||
"generate:esm-package-json": "echo '{\"type\": \"module\"}' > lib/esm/package.json",
|
||||
"format": "run-s format:*",
|
||||
"format:prettier": "prettier --write .",
|
||||
@ -54,7 +55,7 @@
|
||||
"check": "run-p check:*",
|
||||
"check:protocol-revision": "ts-node -s scripts/ensure-correct-devtools-protocol-package",
|
||||
"check:pinned-deps": "ts-node -s scripts/ensure-pinned-deps",
|
||||
"build": "run-s build:tsc generate:types generate:esm-package-json",
|
||||
"build": "run-s generate:sources build:tsc generate:types generate:esm-package-json",
|
||||
"build:tsc": "tsc --version && run-p build:tsc:*",
|
||||
"build:tsc:esm": "tsc -b src/tsconfig.esm.json",
|
||||
"build:tsc:cjs": "tsc -b src/tsconfig.cjs.json",
|
||||
@ -109,6 +110,7 @@
|
||||
"commonmark": "0.30.0",
|
||||
"cross-env": "7.0.3",
|
||||
"diff": "5.1.0",
|
||||
"esbuild": "0.15.5",
|
||||
"eslint": "8.21.0",
|
||||
"eslint-config-prettier": "8.5.0",
|
||||
"eslint-formatter-codeframe": "7.32.1",
|
||||
|
@ -15,7 +15,7 @@
|
||||
*/
|
||||
|
||||
import {Protocol} from 'devtools-protocol';
|
||||
import {assert} from './assert.js';
|
||||
import {assert} from '../util/assert.js';
|
||||
import {CDPSession} from './Connection.js';
|
||||
import {
|
||||
IsolatedWorld,
|
||||
|
@ -16,7 +16,7 @@
|
||||
|
||||
import {ChildProcess} from 'child_process';
|
||||
import {Protocol} from 'devtools-protocol';
|
||||
import {assert} from './assert.js';
|
||||
import {assert} from '../util/assert.js';
|
||||
import {CDPSession, Connection, ConnectionEmittedEvents} from './Connection.js';
|
||||
import {EventEmitter} from './EventEmitter.js';
|
||||
import {waitWithTimeout} from './util.js';
|
||||
|
@ -14,9 +14,10 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {debugError, isErrorLike} from './util.js';
|
||||
import {debugError} from './util.js';
|
||||
import {isErrorLike} from '../util/ErrorLike.js';
|
||||
import {isNode} from '../environment.js';
|
||||
import {assert} from './assert.js';
|
||||
import {assert} from '../util/assert.js';
|
||||
import {
|
||||
Browser,
|
||||
IsPageTargetCallback,
|
||||
|
@ -15,7 +15,7 @@
|
||||
*/
|
||||
|
||||
import Protocol from 'devtools-protocol';
|
||||
import {assert} from './assert.js';
|
||||
import {assert} from '../util/assert.js';
|
||||
import {CDPSession, Connection} from './Connection.js';
|
||||
import {EventEmitter} from './EventEmitter.js';
|
||||
import {Target} from './Target.js';
|
||||
|
@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import {assert} from './assert.js';
|
||||
import {assert} from '../util/assert.js';
|
||||
import {debug} from './Debug.js';
|
||||
const debugProtocolSend = debug('puppeteer:protocol:SEND ►');
|
||||
const debugProtocolReceive = debug('puppeteer:protocol:RECV ◀');
|
||||
|
@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {assert} from './assert.js';
|
||||
import {assert} from '../util/assert.js';
|
||||
import {addEventListener, debugError, PuppeteerEventListener} from './util.js';
|
||||
import {Protocol} from 'devtools-protocol';
|
||||
import {CDPSession} from './Connection.js';
|
||||
|
@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {assert} from './assert.js';
|
||||
import {assert} from '../util/assert.js';
|
||||
import {CDPSession} from './Connection.js';
|
||||
import {Protocol} from 'devtools-protocol';
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
import {Protocol} from 'devtools-protocol';
|
||||
import {assert} from './assert.js';
|
||||
import {assert} from '../util/assert.js';
|
||||
import {CDPSession} from './Connection.js';
|
||||
import {ExecutionContext} from './ExecutionContext.js';
|
||||
import {Frame, FrameManager} from './FrameManager.js';
|
||||
|
@ -15,7 +15,7 @@
|
||||
*/
|
||||
|
||||
import {Protocol} from 'devtools-protocol';
|
||||
import {assert} from './assert.js';
|
||||
import {assert} from '../util/assert.js';
|
||||
import {CDPSession} from './Connection.js';
|
||||
import {Frame} from './FrameManager.js';
|
||||
import {IsolatedWorld} from './IsolatedWorld.js';
|
||||
|
@ -15,7 +15,7 @@
|
||||
*/
|
||||
|
||||
import {Protocol} from 'devtools-protocol';
|
||||
import {assert} from './assert.js';
|
||||
import {assert} from '../util/assert.js';
|
||||
import {ElementHandle} from './ElementHandle.js';
|
||||
|
||||
/**
|
||||
|
@ -15,7 +15,7 @@
|
||||
*/
|
||||
|
||||
import Protocol from 'devtools-protocol';
|
||||
import {assert} from './assert.js';
|
||||
import {assert} from '../util/assert.js';
|
||||
import {CDPSession, Connection} from './Connection.js';
|
||||
import {Target} from './Target.js';
|
||||
import {TargetFilterCallback} from './Browser.js';
|
||||
|
@ -15,7 +15,7 @@
|
||||
*/
|
||||
|
||||
import {Protocol} from 'devtools-protocol';
|
||||
import {assert} from './assert.js';
|
||||
import {assert} from '../util/assert.js';
|
||||
import {CDPSession} from './Connection.js';
|
||||
import {ElementHandle} from './ElementHandle.js';
|
||||
import {EventEmitter} from './EventEmitter.js';
|
||||
@ -35,12 +35,12 @@ import {Page} from './Page.js';
|
||||
import {Target} from './Target.js';
|
||||
import {TimeoutSettings} from './TimeoutSettings.js';
|
||||
import {EvaluateFunc, HandleFor, NodeFor} from './types.js';
|
||||
import {debugError} from './util.js';
|
||||
import {isErrorLike} from '../util/ErrorLike.js';
|
||||
import {
|
||||
createDeferredPromiseWithTimer,
|
||||
debugError,
|
||||
DeferredPromise,
|
||||
isErrorLike,
|
||||
} from './util.js';
|
||||
} from '../util/DeferredPromise.js';
|
||||
|
||||
const UTILITY_WORLD_NAME = '__puppeteer_utility_world__';
|
||||
|
||||
|
@ -15,7 +15,7 @@
|
||||
*/
|
||||
import {Protocol} from 'devtools-protocol';
|
||||
import {ProtocolMapping} from 'devtools-protocol/types/protocol-mapping.js';
|
||||
import {assert} from './assert.js';
|
||||
import {assert} from '../util/assert.js';
|
||||
import {ProtocolError} from './Errors.js';
|
||||
import {EventEmitter} from './EventEmitter.js';
|
||||
import {Frame} from './FrameManager.js';
|
||||
|
@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {assert} from './assert.js';
|
||||
import {assert} from '../util/assert.js';
|
||||
import {CDPSession} from './Connection.js';
|
||||
import {_keyDefinitions, KeyDefinition, KeyInput} from './USKeyboardLayout.js';
|
||||
import {Protocol} from 'devtools-protocol';
|
||||
|
@ -15,7 +15,7 @@
|
||||
*/
|
||||
|
||||
import {Protocol} from 'devtools-protocol';
|
||||
import {assert} from './assert.js';
|
||||
import {assert} from '../util/assert.js';
|
||||
import {CDPSession} from './Connection.js';
|
||||
import {ElementHandle} from './ElementHandle.js';
|
||||
import {TimeoutError} from './Errors.js';
|
||||
@ -28,16 +28,18 @@ import {getQueryHandlerAndSelector} from './QueryHandler.js';
|
||||
import {TimeoutSettings} from './TimeoutSettings.js';
|
||||
import {EvaluateFunc, HandleFor, NodeFor} from './types.js';
|
||||
import {
|
||||
createDeferredPromise,
|
||||
createJSHandle,
|
||||
debugError,
|
||||
DeferredPromise,
|
||||
importFS,
|
||||
isNumber,
|
||||
isString,
|
||||
makePredicateString,
|
||||
pageBindingInitString,
|
||||
} from './util.js';
|
||||
import {
|
||||
createDeferredPromise,
|
||||
DeferredPromise,
|
||||
} from '../util/DeferredPromise.js';
|
||||
|
||||
// predicateQueryHandler and checkWaitForOptions are declared here so that
|
||||
// TypeScript knows about them when used in the predicate function below.
|
||||
@ -722,9 +724,7 @@ export class IsolatedWorld {
|
||||
waitForVisible: boolean,
|
||||
waitForHidden: boolean
|
||||
): Promise<Node | null | boolean> {
|
||||
const node = predicateQueryHandler
|
||||
? ((await predicateQueryHandler(root, selector)) as Element)
|
||||
: root.querySelector(selector);
|
||||
const node = (await predicateQueryHandler(root, selector)) as Element;
|
||||
return checkWaitForOptions(node, waitForVisible, waitForHidden);
|
||||
}
|
||||
const waitTaskOptions: WaitTaskOptions = {
|
||||
|
@ -15,7 +15,7 @@
|
||||
*/
|
||||
|
||||
import {Protocol} from 'devtools-protocol';
|
||||
import {assert} from './assert.js';
|
||||
import {assert} from '../util/assert.js';
|
||||
import {CDPSession} from './Connection.js';
|
||||
import type {ElementHandle} from './ElementHandle.js';
|
||||
import {ExecutionContext} from './ExecutionContext.js';
|
||||
|
@ -14,14 +14,16 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {assert} from './assert.js';
|
||||
import {assert} from '../util/assert.js';
|
||||
import {
|
||||
addEventListener,
|
||||
PuppeteerEventListener,
|
||||
removeEventListeners,
|
||||
} from './util.js';
|
||||
import {
|
||||
DeferredPromise,
|
||||
createDeferredPromise,
|
||||
} from './util.js';
|
||||
} from '../util/DeferredPromise.js';
|
||||
import {TimeoutError} from './Errors.js';
|
||||
import {
|
||||
FrameManager,
|
||||
|
@ -16,18 +16,17 @@
|
||||
|
||||
import {Protocol} from 'devtools-protocol';
|
||||
import {ProtocolMapping} from 'devtools-protocol/types/protocol-mapping.js';
|
||||
import {assert} from './assert.js';
|
||||
import {assert} from '../util/assert.js';
|
||||
import {EventEmitter} from './EventEmitter.js';
|
||||
import {Frame} from './FrameManager.js';
|
||||
import {HTTPRequest} from './HTTPRequest.js';
|
||||
import {HTTPResponse} from './HTTPResponse.js';
|
||||
import {FetchRequestId, NetworkEventManager} from './NetworkEventManager.js';
|
||||
import {debugError, isString} from './util.js';
|
||||
import {
|
||||
debugError,
|
||||
isString,
|
||||
createDeferredPromiseWithTimer,
|
||||
DeferredPromise,
|
||||
} from './util.js';
|
||||
} from '../util/DeferredPromise.js';
|
||||
|
||||
/**
|
||||
* @public
|
||||
|
@ -17,7 +17,7 @@
|
||||
import {Protocol} from 'devtools-protocol';
|
||||
import type {Readable} from 'stream';
|
||||
import {Accessibility} from './Accessibility.js';
|
||||
import {assert} from './assert.js';
|
||||
import {assert} from '../util/assert.js';
|
||||
import {Browser, BrowserContext} from './Browser.js';
|
||||
import {CDPSession, CDPSessionEmittedEvents} from './Connection.js';
|
||||
import {ConsoleMessage, ConsoleMessageType} from './ConsoleMessage.js';
|
||||
@ -59,7 +59,6 @@ import {
|
||||
importFS,
|
||||
getReadableAsBuffer,
|
||||
getReadableFromProtocolStream,
|
||||
isErrorLike,
|
||||
isNumber,
|
||||
isString,
|
||||
pageBindingDeliverErrorString,
|
||||
@ -70,9 +69,12 @@ import {
|
||||
valueFromRemoteObject,
|
||||
waitForEvent,
|
||||
waitWithTimeout,
|
||||
} from './util.js';
|
||||
import {isErrorLike} from '../util/ErrorLike.js';
|
||||
import {
|
||||
createDeferredPromiseWithTimer,
|
||||
DeferredPromise,
|
||||
} from './util.js';
|
||||
} from '../util/DeferredPromise.js';
|
||||
import {WebWorker} from './WebWorker.js';
|
||||
|
||||
/**
|
||||
|
@ -13,12 +13,9 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import {assert} from './assert.js';
|
||||
import {
|
||||
getReadableAsBuffer,
|
||||
getReadableFromProtocolStream,
|
||||
isErrorLike,
|
||||
} from './util.js';
|
||||
import {assert} from '../util/assert.js';
|
||||
import {getReadableAsBuffer, getReadableFromProtocolStream} from './util.js';
|
||||
import {isErrorLike} from '../util/ErrorLike.js';
|
||||
import {CDPSession} from './Connection.js';
|
||||
|
||||
/**
|
||||
|
@ -17,10 +17,11 @@
|
||||
import {Protocol} from 'devtools-protocol';
|
||||
import type {Readable} from 'stream';
|
||||
import {isNode} from '../environment.js';
|
||||
import {assert} from './assert.js';
|
||||
import {assert} from '../util/assert.js';
|
||||
import {CDPSession} from './Connection.js';
|
||||
import {debug} from './Debug.js';
|
||||
import {ElementHandle} from './ElementHandle.js';
|
||||
import {isErrorLike} from '../util/ErrorLike.js';
|
||||
import {TimeoutError} from './Errors.js';
|
||||
import {CommonEventEmitter} from './EventEmitter.js';
|
||||
import {ExecutionContext} from './ExecutionContext.js';
|
||||
@ -341,7 +342,7 @@ export function pageBindingDeliverErrorValueString(
|
||||
*/
|
||||
export function makePredicateString(
|
||||
predicate: Function,
|
||||
predicateQueryHandler?: Function
|
||||
predicateQueryHandler: Function
|
||||
): string {
|
||||
function checkWaitForOptions(
|
||||
node: Node | null,
|
||||
@ -371,12 +372,10 @@ export function makePredicateString(
|
||||
return !!(rect.top || rect.bottom || rect.width || rect.height);
|
||||
}
|
||||
}
|
||||
const predicateQueryHandlerDef = predicateQueryHandler
|
||||
? `const predicateQueryHandler = ${predicateQueryHandler};`
|
||||
: '';
|
||||
|
||||
return `
|
||||
(() => {
|
||||
${predicateQueryHandlerDef}
|
||||
const predicateQueryHandler = ${predicateQueryHandler};
|
||||
const checkWaitForOptions = ${checkWaitForOptions};
|
||||
return (${predicate})(...args)
|
||||
})() `;
|
||||
@ -496,104 +495,3 @@ export async function getReadableFromProtocolStream(
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export interface ErrorLike extends Error {
|
||||
name: string;
|
||||
message: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export function isErrorLike(obj: unknown): obj is ErrorLike {
|
||||
return (
|
||||
typeof obj === 'object' && obj !== null && 'name' in obj && 'message' in obj
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export function isErrnoException(obj: unknown): obj is NodeJS.ErrnoException {
|
||||
return (
|
||||
isErrorLike(obj) &&
|
||||
('errno' in obj || 'code' in obj || 'path' in obj || 'syscall' in obj)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export interface DeferredPromise<T> extends Promise<T> {
|
||||
resolved: () => boolean;
|
||||
resolve: (_: T) => void;
|
||||
reject: (_: Error) => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an returns a promise along with the resolve/reject functions.
|
||||
*
|
||||
* If the promise has not been resolved/rejected withing the `timeout` period,
|
||||
* the promise gets rejected with a timeout error.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
export function createDeferredPromiseWithTimer<T>(
|
||||
timeoutMessage: string,
|
||||
timeout = 5000
|
||||
): DeferredPromise<T> {
|
||||
let isResolved = false;
|
||||
let resolver = (_: T): void => {};
|
||||
let rejector = (_: Error) => {};
|
||||
const taskPromise = new Promise<T>((resolve, reject) => {
|
||||
resolver = resolve;
|
||||
rejector = reject;
|
||||
});
|
||||
const timeoutId = setTimeout(() => {
|
||||
rejector(new TimeoutError(timeoutMessage));
|
||||
}, timeout);
|
||||
return Object.assign(taskPromise, {
|
||||
resolved: () => {
|
||||
return isResolved;
|
||||
},
|
||||
resolve: (value: T) => {
|
||||
clearTimeout(timeoutId);
|
||||
isResolved = true;
|
||||
resolver(value);
|
||||
},
|
||||
reject: (err: Error) => {
|
||||
clearTimeout(timeoutId);
|
||||
rejector(err);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an returns a promise along with the resolve/reject functions.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
export function createDeferredPromise<T>(): DeferredPromise<T> {
|
||||
let isResolved = false;
|
||||
let resolver = (_: T): void => {};
|
||||
let rejector = (_: Error) => {};
|
||||
const taskPromise = new Promise<T>((resolve, reject) => {
|
||||
resolver = resolve;
|
||||
rejector = reject;
|
||||
});
|
||||
return Object.assign(taskPromise, {
|
||||
resolved: () => {
|
||||
return isResolved;
|
||||
},
|
||||
resolve: (value: T) => {
|
||||
isResolved = true;
|
||||
resolver(value);
|
||||
},
|
||||
reject: (err: Error) => {
|
||||
rejector(err);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
@ -1,3 +0,0 @@
|
||||
# Generated Artifacts
|
||||
|
||||
**Do not edit manually edit any TypeScript files in this folder** All TS files are generated from their respectively named template file (ext. `tmpl`) in the `templates` directory. Edit them there is needed.
|
@ -1,4 +0,0 @@
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export const packageVersion = '16.1.1';
|
156
src/injected/Poller.ts
Normal file
156
src/injected/Poller.ts
Normal file
@ -0,0 +1,156 @@
|
||||
import {
|
||||
createDeferredPromise,
|
||||
DeferredPromise,
|
||||
} from '../util/DeferredPromise.js';
|
||||
import {assert} from '../util/assert.js';
|
||||
|
||||
interface Poller<T> {
|
||||
start(): Promise<T>;
|
||||
stop(): Promise<void>;
|
||||
result(): Promise<T>;
|
||||
}
|
||||
|
||||
export class MutationPoller<T> implements Poller<T> {
|
||||
#fn: () => Promise<T>;
|
||||
|
||||
#root: Node;
|
||||
|
||||
#observer?: MutationObserver;
|
||||
#promise?: DeferredPromise<T>;
|
||||
constructor(fn: () => Promise<T>, root: Node) {
|
||||
this.#fn = fn;
|
||||
this.#root = root;
|
||||
}
|
||||
|
||||
async start(): Promise<T> {
|
||||
const promise = (this.#promise = createDeferredPromise<T>());
|
||||
const result = await this.#fn();
|
||||
if (result) {
|
||||
promise.resolve(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
this.#observer = new MutationObserver(async () => {
|
||||
const result = await this.#fn();
|
||||
if (!result) {
|
||||
return;
|
||||
}
|
||||
promise.resolve(result);
|
||||
await this.stop();
|
||||
});
|
||||
this.#observer.observe(this.#root, {
|
||||
childList: true,
|
||||
subtree: true,
|
||||
attributes: true,
|
||||
});
|
||||
|
||||
return this.#promise;
|
||||
}
|
||||
|
||||
async stop(): Promise<void> {
|
||||
assert(this.#promise, 'Polling never started.');
|
||||
if (!this.#promise.finished()) {
|
||||
this.#promise.reject(new Error('Polling stopped'));
|
||||
}
|
||||
if (this.#observer) {
|
||||
this.#observer.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
result(): Promise<T> {
|
||||
assert(this.#promise, 'Polling never started.');
|
||||
return this.#promise;
|
||||
}
|
||||
}
|
||||
|
||||
export class RAFPoller<T> implements Poller<T> {
|
||||
#fn: () => Promise<T>;
|
||||
#promise?: DeferredPromise<T>;
|
||||
constructor(fn: () => Promise<T>) {
|
||||
this.#fn = fn;
|
||||
}
|
||||
|
||||
async start(): Promise<T> {
|
||||
const promise = (this.#promise = createDeferredPromise<T>());
|
||||
const result = await this.#fn();
|
||||
if (result) {
|
||||
promise.resolve(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
const poll = async () => {
|
||||
if (promise.finished()) {
|
||||
return;
|
||||
}
|
||||
const result = await this.#fn();
|
||||
if (!result) {
|
||||
window.requestAnimationFrame(poll);
|
||||
return;
|
||||
}
|
||||
promise.resolve(result);
|
||||
await this.stop();
|
||||
};
|
||||
window.requestAnimationFrame(poll);
|
||||
|
||||
return this.#promise;
|
||||
}
|
||||
|
||||
async stop(): Promise<void> {
|
||||
assert(this.#promise, 'Polling never started.');
|
||||
if (!this.#promise.finished()) {
|
||||
this.#promise.reject(new Error('Polling stopped'));
|
||||
}
|
||||
}
|
||||
|
||||
result(): Promise<T> {
|
||||
assert(this.#promise, 'Polling never started.');
|
||||
return this.#promise;
|
||||
}
|
||||
}
|
||||
|
||||
export class IntervalPoller<T> implements Poller<T> {
|
||||
#fn: () => Promise<T>;
|
||||
#ms: number;
|
||||
|
||||
#interval?: NodeJS.Timer;
|
||||
#promise?: DeferredPromise<T>;
|
||||
constructor(fn: () => Promise<T>, ms: number) {
|
||||
this.#fn = fn;
|
||||
this.#ms = ms;
|
||||
}
|
||||
|
||||
async start(): Promise<T> {
|
||||
const promise = (this.#promise = createDeferredPromise<T>());
|
||||
const result = await this.#fn();
|
||||
if (result) {
|
||||
promise.resolve(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
this.#interval = setInterval(async () => {
|
||||
const result = await this.#fn();
|
||||
if (!result) {
|
||||
return;
|
||||
}
|
||||
promise.resolve(result);
|
||||
await this.stop();
|
||||
}, this.#ms);
|
||||
|
||||
return this.#promise;
|
||||
}
|
||||
|
||||
async stop(): Promise<void> {
|
||||
assert(this.#promise, 'Polling never started.');
|
||||
if (!this.#promise.finished()) {
|
||||
this.#promise.reject(new Error('Polling stopped'));
|
||||
}
|
||||
if (this.#interval) {
|
||||
clearInterval(this.#interval);
|
||||
}
|
||||
}
|
||||
|
||||
result(): Promise<T> {
|
||||
assert(this.#promise, 'Polling never started.');
|
||||
return this.#promise;
|
||||
}
|
||||
}
|
5
src/injected/README.md
Normal file
5
src/injected/README.md
Normal file
@ -0,0 +1,5 @@
|
||||
# Injected
|
||||
|
||||
This folder contains code that is injected into every Puppeteer execution context. Each file is transpiled using esbuild into a script in `src/generated` which is then imported into server code.
|
||||
|
||||
See `utils/generate_injected.ts` for more information.
|
1
src/injected/injected.ts
Normal file
1
src/injected/injected.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './Poller.js';
|
@ -33,7 +33,7 @@ import createHttpsProxyAgent, {
|
||||
HttpsProxyAgentOptions,
|
||||
} from 'https-proxy-agent';
|
||||
import {getProxyForUrl} from 'proxy-from-env';
|
||||
import {assert} from '../common/assert.js';
|
||||
import {assert} from '../util/assert.js';
|
||||
|
||||
import tar from 'tar-fs';
|
||||
import bzip from 'unbzip2-stream';
|
||||
|
@ -20,18 +20,17 @@ import * as path from 'path';
|
||||
import * as readline from 'readline';
|
||||
import removeFolder from 'rimraf';
|
||||
import {promisify} from 'util';
|
||||
import {assert} from '../common/assert.js';
|
||||
import {assert} from '../util/assert.js';
|
||||
import {Connection} from '../common/Connection.js';
|
||||
import {debug} from '../common/Debug.js';
|
||||
import {TimeoutError} from '../common/Errors.js';
|
||||
import {
|
||||
debugError,
|
||||
addEventListener,
|
||||
isErrnoException,
|
||||
isErrorLike,
|
||||
PuppeteerEventListener,
|
||||
removeEventListeners,
|
||||
} from '../common/util.js';
|
||||
import {isErrnoException, isErrorLike} from '../util/ErrorLike.js';
|
||||
import {Product} from '../common/Product.js';
|
||||
import {NodeWebSocketTransport as WebSocketTransport} from '../node/NodeWebSocketTransport.js';
|
||||
import {LaunchOptions} from './LaunchOptions.js';
|
||||
|
@ -1,6 +1,6 @@
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import {assert} from '../common/assert.js';
|
||||
import {assert} from '../util/assert.js';
|
||||
import {Browser} from '../common/Browser.js';
|
||||
import {Product} from '../common/Product.js';
|
||||
import {BrowserRunner} from './BrowserRunner.js';
|
||||
|
@ -1,7 +1,7 @@
|
||||
import fs from 'fs';
|
||||
import os from 'os';
|
||||
import path from 'path';
|
||||
import {assert} from '../common/assert.js';
|
||||
import {assert} from '../util/assert.js';
|
||||
import {Browser} from '../common/Browser.js';
|
||||
import {Product} from '../common/Product.js';
|
||||
import {BrowserFetcher} from './BrowserFetcher.js';
|
||||
|
@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import {assert} from '../common/assert.js';
|
||||
import {assert} from '../util/assert.js';
|
||||
import {ConnectionTransport} from '../common/ConnectionTransport.js';
|
||||
import {
|
||||
addEventListener,
|
||||
|
3
src/templates/README.md
Normal file
3
src/templates/README.md
Normal file
@ -0,0 +1,3 @@
|
||||
# Templated Artifacts
|
||||
|
||||
These files are generated as TypeScript files in the `src/generated` folder.
|
@ -52,7 +52,6 @@ export * from './common/TimeoutSettings.js';
|
||||
export * from './common/Tracing.js';
|
||||
export * from './common/USKeyboardLayout.js';
|
||||
export * from './common/WebWorker.js';
|
||||
export * from './common/assert.js';
|
||||
export * from './common/fetch.js';
|
||||
export * from './common/types.js';
|
||||
export * from './common/util.js';
|
||||
@ -71,4 +70,5 @@ export * from './node/install.js';
|
||||
export * from './node/util.js';
|
||||
|
||||
// Exports from `generated`
|
||||
export * from './generated/injected.js';
|
||||
export * from './generated/version.js';
|
||||
|
88
src/util/DeferredPromise.ts
Normal file
88
src/util/DeferredPromise.ts
Normal file
@ -0,0 +1,88 @@
|
||||
import {TimeoutError} from '../common/Errors.js';
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
|
||||
export interface DeferredPromise<T> extends Promise<T> {
|
||||
finished: () => boolean;
|
||||
resolved: () => boolean;
|
||||
resolve: (_: T) => void;
|
||||
reject: (_: Error) => void;
|
||||
}
|
||||
/**
|
||||
* Creates an returns a promise along with the resolve/reject functions.
|
||||
*
|
||||
* If the promise has not been resolved/rejected withing the `timeout` period,
|
||||
* the promise gets rejected with a timeout error.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
|
||||
export function createDeferredPromiseWithTimer<T>(
|
||||
timeoutMessage: string,
|
||||
timeout = 5000
|
||||
): DeferredPromise<T> {
|
||||
let isResolved = false;
|
||||
let isRejected = false;
|
||||
let resolver = (_: T): void => {};
|
||||
let rejector = (_: Error) => {};
|
||||
const taskPromise = new Promise<T>((resolve, reject) => {
|
||||
resolver = resolve;
|
||||
rejector = reject;
|
||||
});
|
||||
const timeoutId = setTimeout(() => {
|
||||
isRejected = true;
|
||||
rejector(new TimeoutError(timeoutMessage));
|
||||
}, timeout);
|
||||
return Object.assign(taskPromise, {
|
||||
resolved: () => {
|
||||
return isResolved;
|
||||
},
|
||||
finished: () => {
|
||||
return isResolved || isRejected;
|
||||
},
|
||||
resolve: (value: T) => {
|
||||
clearTimeout(timeoutId);
|
||||
isResolved = true;
|
||||
resolver(value);
|
||||
},
|
||||
reject: (err: Error) => {
|
||||
clearTimeout(timeoutId);
|
||||
isRejected = true;
|
||||
rejector(err);
|
||||
},
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Creates an returns a promise along with the resolve/reject functions.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
|
||||
export function createDeferredPromise<T>(): DeferredPromise<T> {
|
||||
let isResolved = false;
|
||||
let isRejected = false;
|
||||
let resolver = (_: T): void => {};
|
||||
let rejector = (_: Error) => {};
|
||||
const taskPromise = new Promise<T>((resolve, reject) => {
|
||||
resolver = resolve;
|
||||
rejector = reject;
|
||||
});
|
||||
return Object.assign(taskPromise, {
|
||||
resolved: () => {
|
||||
return isResolved;
|
||||
},
|
||||
finished: () => {
|
||||
return isResolved || isRejected;
|
||||
},
|
||||
resolve: (value: T) => {
|
||||
isResolved = true;
|
||||
resolver(value);
|
||||
},
|
||||
reject: (err: Error) => {
|
||||
isRejected = true;
|
||||
rejector(err);
|
||||
},
|
||||
});
|
||||
}
|
27
src/util/ErrorLike.ts
Normal file
27
src/util/ErrorLike.ts
Normal file
@ -0,0 +1,27 @@
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
|
||||
export interface ErrorLike extends Error {
|
||||
name: string;
|
||||
message: string;
|
||||
}
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
|
||||
export function isErrorLike(obj: unknown): obj is ErrorLike {
|
||||
return (
|
||||
typeof obj === 'object' && obj !== null && 'name' in obj && 'message' in obj
|
||||
);
|
||||
}
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
|
||||
export function isErrnoException(obj: unknown): obj is NodeJS.ErrnoException {
|
||||
return (
|
||||
isErrorLike(obj) &&
|
||||
('errno' in obj || 'code' in obj || 'path' in obj || 'syscall' in obj)
|
||||
);
|
||||
}
|
@ -22,7 +22,7 @@ import {
|
||||
setupTestPageAndContextHooks,
|
||||
describeChromeOnly,
|
||||
} from './mocha-utils.js';
|
||||
import {isErrorLike} from '../../lib/cjs/puppeteer/common/util.js';
|
||||
import {isErrorLike} from '../../lib/cjs/puppeteer/util/ErrorLike.js';
|
||||
|
||||
describeChromeOnly('Target.createCDPSession', function () {
|
||||
setupTestBrowserHooks();
|
||||
|
@ -26,7 +26,7 @@ import {
|
||||
BrowserContext,
|
||||
} from '../../lib/cjs/puppeteer/common/Browser.js';
|
||||
import {Page} from '../../lib/cjs/puppeteer/common/Page.js';
|
||||
import {isErrorLike} from '../../lib/cjs/puppeteer/common/util.js';
|
||||
import {isErrorLike} from '../../lib/cjs/puppeteer/util/ErrorLike.js';
|
||||
import {
|
||||
PuppeteerLaunchOptions,
|
||||
PuppeteerNode,
|
||||
|
@ -15,7 +15,7 @@
|
||||
*/
|
||||
|
||||
import expect from 'expect';
|
||||
import {isErrorLike} from '../../lib/cjs/puppeteer/common/util.js';
|
||||
import {isErrorLike} from '../../lib/cjs/puppeteer/util/ErrorLike.js';
|
||||
import {
|
||||
getTestState,
|
||||
itFailsFirefox,
|
||||
|
@ -45,7 +45,7 @@ const fileExists = async filePath => {
|
||||
* place.
|
||||
*/
|
||||
async function compileTypeScript() {
|
||||
return exec('npm run build:tsc').catch(error => {
|
||||
return exec('npm run build').catch(error => {
|
||||
console.error('Error running TypeScript', error);
|
||||
process.exit(1);
|
||||
});
|
||||
|
6
utils/.eslintrc.js
Normal file
6
utils/.eslintrc.js
Normal file
@ -0,0 +1,6 @@
|
||||
module.exports = {
|
||||
extends: ['../.eslintrc.js'],
|
||||
rules: {
|
||||
'import/extensions': 0,
|
||||
},
|
||||
};
|
@ -20,8 +20,6 @@ import {chdir} from 'process';
|
||||
import semver from 'semver';
|
||||
import {versionsPerRelease} from '../versions.js';
|
||||
import versionsArchived from '../website/versionsArchived.json';
|
||||
|
||||
// eslint-disable-next-line import/extensions
|
||||
import {generateDocs} from './internal/custom_markdown_action';
|
||||
|
||||
function getOffsetAndLimit(
|
||||
|
69
utils/generate_sources.ts
Normal file
69
utils/generate_sources.ts
Normal file
@ -0,0 +1,69 @@
|
||||
#!/usr/bin/env node
|
||||
import esbuild from 'esbuild';
|
||||
import {mkdir, mkdtemp, readFile, writeFile} from 'fs/promises';
|
||||
import path from 'path';
|
||||
import rimraf from 'rimraf';
|
||||
import {job} from './internal/job';
|
||||
|
||||
(async () => {
|
||||
await job('', async ({outputs}) => {
|
||||
await Promise.all(
|
||||
outputs.map(outputs => {
|
||||
return mkdir(outputs, {recursive: true});
|
||||
})
|
||||
);
|
||||
})
|
||||
.outputs(['src/generated'])
|
||||
.build();
|
||||
|
||||
await job('', async ({name, inputs, outputs}) => {
|
||||
const tmp = await mkdtemp(name);
|
||||
await esbuild.build({
|
||||
entryPoints: [inputs[0]!],
|
||||
bundle: true,
|
||||
outdir: tmp,
|
||||
format: 'cjs',
|
||||
platform: 'browser',
|
||||
target: 'ES2019',
|
||||
});
|
||||
const baseName = path.basename(inputs[0]!);
|
||||
const content = await readFile(
|
||||
path.join(tmp, baseName.replace('.ts', '.js')),
|
||||
'utf-8'
|
||||
);
|
||||
const scriptContent = `/** @internal */
|
||||
export const source = ${JSON.stringify(content)};
|
||||
`;
|
||||
await writeFile(outputs[0]!, scriptContent);
|
||||
await rimraf.sync(tmp);
|
||||
})
|
||||
.inputs(['src/injected/**.ts'])
|
||||
.outputs(['src/generated/injected.ts'])
|
||||
.build();
|
||||
|
||||
job('', async ({inputs, outputs}) => {
|
||||
const version = JSON.parse(await readFile(inputs[0]!, 'utf8')).version;
|
||||
await writeFile(
|
||||
outputs[0]!,
|
||||
(
|
||||
await readFile(outputs[0]!, {
|
||||
encoding: 'utf-8',
|
||||
})
|
||||
).replace("'NEXT'", `v${version}`)
|
||||
);
|
||||
})
|
||||
.inputs(['package.json'])
|
||||
.outputs(['versions.js'])
|
||||
.build();
|
||||
|
||||
job('', async ({inputs, outputs}) => {
|
||||
const version = JSON.parse(await readFile(inputs[0]!, 'utf8')).version;
|
||||
await writeFile(
|
||||
outputs[0]!,
|
||||
(await readFile(inputs[1]!, 'utf8')).replace('PACKAGE_VERSION', version)
|
||||
);
|
||||
})
|
||||
.inputs(['package.json', 'src/templates/version.ts.tmpl'])
|
||||
.outputs(['src/generated/version.ts'])
|
||||
.build();
|
||||
})();
|
@ -1,18 +0,0 @@
|
||||
const {writeFileSync, readFileSync} = require('fs');
|
||||
const {join} = require('path');
|
||||
|
||||
const version = require('../package.json').version;
|
||||
|
||||
writeFileSync(
|
||||
join(__dirname, '../src/generated/version.ts'),
|
||||
readFileSync(join(__dirname, '../src/templates/version.ts.tmpl'), {
|
||||
encoding: 'utf-8',
|
||||
}).replace('PACKAGE_VERSION', version)
|
||||
);
|
||||
|
||||
writeFileSync(
|
||||
join(__dirname, '../versions.js'),
|
||||
readFileSync(join(__dirname, '../versions.js'), {
|
||||
encoding: 'utf-8',
|
||||
}).replace('NEXT', `v${version}`)
|
||||
);
|
96
utils/internal/job.ts
Normal file
96
utils/internal/job.ts
Normal file
@ -0,0 +1,96 @@
|
||||
import {Stats} from 'fs';
|
||||
import {stat} from 'fs/promises';
|
||||
import {glob} from 'glob';
|
||||
import path from 'path';
|
||||
|
||||
interface JobContext {
|
||||
name: string;
|
||||
inputs: string[];
|
||||
outputs: string[];
|
||||
}
|
||||
|
||||
class JobBuilder {
|
||||
#inputs: string[] = [];
|
||||
#outputs: string[] = [];
|
||||
#callback: (ctx: JobContext) => Promise<void>;
|
||||
#name: string;
|
||||
|
||||
constructor(name: string, callback: (ctx: JobContext) => Promise<void>) {
|
||||
this.#name = name;
|
||||
this.#callback = callback;
|
||||
}
|
||||
|
||||
inputs(inputs: string[]): JobBuilder {
|
||||
this.#inputs = inputs.flatMap(value => {
|
||||
value = path.resolve(__dirname, '..', '..', value);
|
||||
const paths = glob.sync(value);
|
||||
return paths.length ? paths : [value];
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
outputs(outputs: string[]): JobBuilder {
|
||||
if (!this.#name) {
|
||||
this.#name = outputs[0]!;
|
||||
}
|
||||
|
||||
this.#outputs = outputs.map(value => {
|
||||
return path.resolve(__dirname, '..', '..', value);
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
async build(): Promise<void> {
|
||||
console.log(`Running job ${this.#name}...`);
|
||||
|
||||
let shouldRun = true;
|
||||
|
||||
const inputStats = await Promise.all(
|
||||
this.#inputs.map(input => {
|
||||
return stat(input);
|
||||
})
|
||||
);
|
||||
let outputStats: Stats[];
|
||||
try {
|
||||
outputStats = await Promise.all(
|
||||
this.#outputs.map(output => {
|
||||
return stat(output);
|
||||
})
|
||||
);
|
||||
|
||||
if (
|
||||
outputStats.reduce(reduceMaxTime, 0) >=
|
||||
inputStats.reduce(reduceMinTime, Infinity)
|
||||
) {
|
||||
shouldRun = false;
|
||||
}
|
||||
} catch {}
|
||||
|
||||
if (shouldRun) {
|
||||
this.#run();
|
||||
}
|
||||
}
|
||||
|
||||
#run(): Promise<void> {
|
||||
return this.#callback({
|
||||
name: this.#name,
|
||||
inputs: this.#inputs,
|
||||
outputs: this.#outputs,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export const job = (
|
||||
name: string,
|
||||
callback: (ctx: JobContext) => Promise<void>
|
||||
): JobBuilder => {
|
||||
return new JobBuilder(name, callback);
|
||||
};
|
||||
|
||||
const reduceMaxTime = (time: number, stat: Stats) => {
|
||||
return time < stat.mtimeMs ? stat.mtimeMs : time;
|
||||
};
|
||||
|
||||
const reduceMinTime = (time: number, stat: Stats) => {
|
||||
return time > stat.mtimeMs ? stat.mtimeMs : time;
|
||||
};
|
12
utils/tsconfig.json
Normal file
12
utils/tsconfig.json
Normal file
@ -0,0 +1,12 @@
|
||||
/**
|
||||
* This configuration only exists for the API Extractor tool and for VSCode to use. It is NOT the tsconfig used for compilation.
|
||||
* For CJS builds, `tsconfig.cjs.json` is used, and for ESM, it's `tsconfig.esm.json`.
|
||||
* See the details in CONTRIBUTING.md that describes our TypeScript setup.
|
||||
*/
|
||||
{
|
||||
"extends": "../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"noEmit": true,
|
||||
"module": "CommonJS"
|
||||
}
|
||||
}
|
@ -16,7 +16,7 @@
|
||||
|
||||
const versionsPerRelease = new Map([
|
||||
// This is a mapping from Chromium version => Puppeteer version.
|
||||
// In Chromium roll patches, use 'v16.1.1' for the Puppeteer version.
|
||||
// In Chromium roll patches, use `NEXT` for the Puppeteer version.
|
||||
['105.0.5173.0', 'v15.5.0'],
|
||||
['104.0.5109.0', 'v15.1.0'],
|
||||
['103.0.5059.0', 'v14.2.0'],
|
||||
|
Loading…
Reference in New Issue
Block a user