chore: remove injected -> types dependency (#3606)
This way, injected is self-contained and we can ensure it does not depend on anything node-specific.
This commit is contained in:
parent
3bdf0e804a
commit
bdbcae16cb
|
|
@ -16,7 +16,7 @@
|
||||||
|
|
||||||
import * as frames from './frames';
|
import * as frames from './frames';
|
||||||
import { assert } from '../utils/utils';
|
import { assert } from '../utils/utils';
|
||||||
import type InjectedScript from './injected/injectedScript';
|
import type { InjectedScript, InjectedScriptPoll } from './injected/injectedScript';
|
||||||
import * as injectedScriptSource from '../generated/injectedScriptSource';
|
import * as injectedScriptSource from '../generated/injectedScriptSource';
|
||||||
import * as debugScriptSource from '../generated/debugScriptSource';
|
import * as debugScriptSource from '../generated/debugScriptSource';
|
||||||
import * as js from './javascript';
|
import * as js from './javascript';
|
||||||
|
|
@ -718,9 +718,9 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
|
||||||
// - cancels the poll when progress cancels.
|
// - cancels the poll when progress cancels.
|
||||||
export class InjectedScriptPollHandler<T> {
|
export class InjectedScriptPollHandler<T> {
|
||||||
private _progress: Progress;
|
private _progress: Progress;
|
||||||
private _poll: js.JSHandle<types.InjectedScriptPoll<T>> | null;
|
private _poll: js.JSHandle<InjectedScriptPoll<T>> | null;
|
||||||
|
|
||||||
constructor(progress: Progress, poll: js.JSHandle<types.InjectedScriptPoll<T>>) {
|
constructor(progress: Progress, poll: js.JSHandle<InjectedScriptPoll<T>>) {
|
||||||
this._progress = progress;
|
this._progress = progress;
|
||||||
this._poll = poll;
|
this._poll = poll;
|
||||||
// Ensure we cancel the poll before progress aborts and returns:
|
// Ensure we cancel the poll before progress aborts and returns:
|
||||||
|
|
@ -836,7 +836,7 @@ function compensateHalfIntegerRoundingError(point: types.Point) {
|
||||||
point.y -= 0.02;
|
point.y -= 0.02;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type SchedulableTask<T> = (injectedScript: js.JSHandle<InjectedScript>) => Promise<js.JSHandle<types.InjectedScriptPoll<T>>>;
|
export type SchedulableTask<T> = (injectedScript: js.JSHandle<InjectedScript>) => Promise<js.JSHandle<InjectedScriptPoll<T>>>;
|
||||||
|
|
||||||
export function waitForSelectorTask(selector: SelectorInfo, state: 'attached' | 'detached' | 'visible' | 'hidden', root?: ElementHandle): SchedulableTask<Element | undefined> {
|
export function waitForSelectorTask(selector: SelectorInfo, state: 'attached' | 'detached' | 'visible' | 'hidden', root?: ElementHandle): SchedulableTask<Element | undefined> {
|
||||||
return injectedScript => injectedScript.evaluateHandle((injected, { parsed, state, root }) => {
|
return injectedScript => injectedScript.evaluateHandle((injected, { parsed, state, root }) => {
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,6 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type * as types from '../types';
|
|
||||||
import { createAttributeEngine } from './attributeSelectorEngine';
|
import { createAttributeEngine } from './attributeSelectorEngine';
|
||||||
import { createCSSEngine } from './cssSelectorEngine';
|
import { createCSSEngine } from './cssSelectorEngine';
|
||||||
import { SelectorEngine, SelectorRoot } from './selectorEngine';
|
import { SelectorEngine, SelectorRoot } from './selectorEngine';
|
||||||
|
|
@ -23,9 +22,24 @@ import { XPathEngine } from './xpathSelectorEngine';
|
||||||
import { ParsedSelector } from '../common/selectorParser';
|
import { ParsedSelector } from '../common/selectorParser';
|
||||||
import { FatalDOMError } from '../common/domErrors';
|
import { FatalDOMError } from '../common/domErrors';
|
||||||
|
|
||||||
type Predicate<T> = (progress: types.InjectedScriptProgress, continuePolling: symbol) => T | symbol;
|
type Predicate<T> = (progress: InjectedScriptProgress, continuePolling: symbol) => T | symbol;
|
||||||
|
|
||||||
export default class InjectedScript {
|
export type InjectedScriptProgress = {
|
||||||
|
aborted: boolean,
|
||||||
|
log: (message: string) => void,
|
||||||
|
logRepeating: (message: string) => void,
|
||||||
|
};
|
||||||
|
|
||||||
|
export type InjectedScriptPoll<T> = {
|
||||||
|
result: Promise<T>,
|
||||||
|
// Takes more logs, waiting until at least one message is available.
|
||||||
|
takeNextLogs: () => Promise<string[]>,
|
||||||
|
// Takes all current logs without waiting.
|
||||||
|
takeLastLogs: () => string[],
|
||||||
|
cancel: () => void,
|
||||||
|
};
|
||||||
|
|
||||||
|
export class InjectedScript {
|
||||||
readonly engines: Map<string, SelectorEngine>;
|
readonly engines: Map<string, SelectorEngine>;
|
||||||
|
|
||||||
constructor(customEngines: { name: string, engine: SelectorEngine}[]) {
|
constructor(customEngines: { name: string, engine: SelectorEngine}[]) {
|
||||||
|
|
@ -105,7 +119,7 @@ export default class InjectedScript {
|
||||||
return rect.width > 0 && rect.height > 0;
|
return rect.width > 0 && rect.height > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
pollRaf<T>(predicate: Predicate<T>): types.InjectedScriptPoll<T> {
|
pollRaf<T>(predicate: Predicate<T>): InjectedScriptPoll<T> {
|
||||||
return this._runAbortableTask(progress => {
|
return this._runAbortableTask(progress => {
|
||||||
let fulfill: (result: T) => void;
|
let fulfill: (result: T) => void;
|
||||||
let reject: (error: Error) => void;
|
let reject: (error: Error) => void;
|
||||||
|
|
@ -131,7 +145,7 @@ export default class InjectedScript {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
pollInterval<T>(pollInterval: number, predicate: Predicate<T>): types.InjectedScriptPoll<T> {
|
pollInterval<T>(pollInterval: number, predicate: Predicate<T>): InjectedScriptPoll<T> {
|
||||||
return this._runAbortableTask(progress => {
|
return this._runAbortableTask(progress => {
|
||||||
let fulfill: (result: T) => void;
|
let fulfill: (result: T) => void;
|
||||||
let reject: (error: Error) => void;
|
let reject: (error: Error) => void;
|
||||||
|
|
@ -157,7 +171,7 @@ export default class InjectedScript {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private _runAbortableTask<T>(task: (progess: types.InjectedScriptProgress) => Promise<T>): types.InjectedScriptPoll<T> {
|
private _runAbortableTask<T>(task: (progess: InjectedScriptProgress) => Promise<T>): InjectedScriptPoll<T> {
|
||||||
let unsentLogs: string[] = [];
|
let unsentLogs: string[] = [];
|
||||||
let takeNextLogsCallback: ((logs: string[]) => void) | undefined;
|
let takeNextLogsCallback: ((logs: string[]) => void) | undefined;
|
||||||
const logReady = () => {
|
const logReady = () => {
|
||||||
|
|
@ -175,7 +189,7 @@ export default class InjectedScript {
|
||||||
});
|
});
|
||||||
|
|
||||||
let lastLog = '';
|
let lastLog = '';
|
||||||
const progress: types.InjectedScriptProgress = {
|
const progress: InjectedScriptProgress = {
|
||||||
aborted: false,
|
aborted: false,
|
||||||
log: (message: string) => {
|
log: (message: string) => {
|
||||||
lastLog = message;
|
lastLog = message;
|
||||||
|
|
@ -203,7 +217,7 @@ export default class InjectedScript {
|
||||||
return { left: parseInt(style.borderLeftWidth || '', 10), top: parseInt(style.borderTopWidth || '', 10) };
|
return { left: parseInt(style.borderLeftWidth || '', 10), top: parseInt(style.borderTopWidth || '', 10) };
|
||||||
}
|
}
|
||||||
|
|
||||||
selectOptions(node: Node, optionsToSelect: (Node | types.SelectOption)[]): string[] | 'error:notconnected' | FatalDOMError {
|
selectOptions(node: Node, optionsToSelect: (Node | { value?: string, label?: string, index?: number })[]): string[] | 'error:notconnected' | FatalDOMError {
|
||||||
if (node.nodeName.toLowerCase() !== 'select')
|
if (node.nodeName.toLowerCase() !== 'select')
|
||||||
return 'error:notselect';
|
return 'error:notselect';
|
||||||
if (!node.isConnected)
|
if (!node.isConnected)
|
||||||
|
|
@ -234,7 +248,7 @@ export default class InjectedScript {
|
||||||
return options.filter(option => option.selected).map(option => option.value);
|
return options.filter(option => option.selected).map(option => option.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
waitForEnabledAndFill(node: Node, value: string): types.InjectedScriptPoll<FatalDOMError | 'error:notconnected' | 'needsinput' | 'done'> {
|
waitForEnabledAndFill(node: Node, value: string): InjectedScriptPoll<FatalDOMError | 'error:notconnected' | 'needsinput' | 'done'> {
|
||||||
return this.pollRaf((progress, continuePolling) => {
|
return this.pollRaf((progress, continuePolling) => {
|
||||||
if (node.nodeType !== Node.ELEMENT_NODE)
|
if (node.nodeType !== Node.ELEMENT_NODE)
|
||||||
return 'error:notelement';
|
return 'error:notelement';
|
||||||
|
|
@ -299,7 +313,7 @@ export default class InjectedScript {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
waitForVisibleAndSelectText(node: Node): types.InjectedScriptPoll<FatalDOMError | 'error:notconnected' | 'done'> {
|
waitForVisibleAndSelectText(node: Node): InjectedScriptPoll<FatalDOMError | 'error:notconnected' | 'done'> {
|
||||||
return this.pollRaf((progress, continuePolling) => {
|
return this.pollRaf((progress, continuePolling) => {
|
||||||
if (node.nodeType !== Node.ELEMENT_NODE)
|
if (node.nodeType !== Node.ELEMENT_NODE)
|
||||||
return 'error:notelement';
|
return 'error:notelement';
|
||||||
|
|
@ -344,7 +358,7 @@ export default class InjectedScript {
|
||||||
return 'done';
|
return 'done';
|
||||||
}
|
}
|
||||||
|
|
||||||
waitForNodeVisible(node: Node): types.InjectedScriptPoll<'error:notconnected' | 'done'> {
|
waitForNodeVisible(node: Node): InjectedScriptPoll<'error:notconnected' | 'done'> {
|
||||||
return this.pollRaf((progress, continuePolling) => {
|
return this.pollRaf((progress, continuePolling) => {
|
||||||
const element = node.nodeType === Node.ELEMENT_NODE ? node as Element : node.parentElement;
|
const element = node.nodeType === Node.ELEMENT_NODE ? node as Element : node.parentElement;
|
||||||
if (!node.isConnected || !element)
|
if (!node.isConnected || !element)
|
||||||
|
|
@ -357,7 +371,7 @@ export default class InjectedScript {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
waitForNodeHidden(node: Node): types.InjectedScriptPoll<'done'> {
|
waitForNodeHidden(node: Node): InjectedScriptPoll<'done'> {
|
||||||
return this.pollRaf((progress, continuePolling) => {
|
return this.pollRaf((progress, continuePolling) => {
|
||||||
const element = node.nodeType === Node.ELEMENT_NODE ? node as Element : node.parentElement;
|
const element = node.nodeType === Node.ELEMENT_NODE ? node as Element : node.parentElement;
|
||||||
if (!node.isConnected || !element)
|
if (!node.isConnected || !element)
|
||||||
|
|
@ -370,7 +384,7 @@ export default class InjectedScript {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
waitForNodeEnabled(node: Node): types.InjectedScriptPoll<'error:notconnected' | 'done'> {
|
waitForNodeEnabled(node: Node): InjectedScriptPoll<'error:notconnected' | 'done'> {
|
||||||
return this.pollRaf((progress, continuePolling) => {
|
return this.pollRaf((progress, continuePolling) => {
|
||||||
const element = node.nodeType === Node.ELEMENT_NODE ? node as Element : node.parentElement;
|
const element = node.nodeType === Node.ELEMENT_NODE ? node as Element : node.parentElement;
|
||||||
if (!node.isConnected || !element)
|
if (!node.isConnected || !element)
|
||||||
|
|
@ -383,7 +397,7 @@ export default class InjectedScript {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
waitForNodeDisabled(node: Node): types.InjectedScriptPoll<'error:notconnected' | 'done'> {
|
waitForNodeDisabled(node: Node): InjectedScriptPoll<'error:notconnected' | 'done'> {
|
||||||
return this.pollRaf((progress, continuePolling) => {
|
return this.pollRaf((progress, continuePolling) => {
|
||||||
const element = node.nodeType === Node.ELEMENT_NODE ? node as Element : node.parentElement;
|
const element = node.nodeType === Node.ELEMENT_NODE ? node as Element : node.parentElement;
|
||||||
if (!node.isConnected || !element)
|
if (!node.isConnected || !element)
|
||||||
|
|
@ -438,7 +452,7 @@ export default class InjectedScript {
|
||||||
throw new Error('Not a checkbox');
|
throw new Error('Not a checkbox');
|
||||||
}
|
}
|
||||||
|
|
||||||
async setInputFiles(node: Node, payloads: types.FilePayload[]) {
|
async setInputFiles(node: Node, payloads: { name: string, mimeType: string, buffer: string }[]) {
|
||||||
if (node.nodeType !== Node.ELEMENT_NODE)
|
if (node.nodeType !== Node.ELEMENT_NODE)
|
||||||
return 'Node is not of type HTMLElement';
|
return 'Node is not of type HTMLElement';
|
||||||
const element: Element | undefined = node as Element;
|
const element: Element | undefined = node as Element;
|
||||||
|
|
@ -461,8 +475,8 @@ export default class InjectedScript {
|
||||||
input.dispatchEvent(new Event('change', { 'bubbles': true }));
|
input.dispatchEvent(new Event('change', { 'bubbles': true }));
|
||||||
}
|
}
|
||||||
|
|
||||||
waitForDisplayedAtStablePosition(node: Node, rafCount: number, waitForEnabled: boolean): types.InjectedScriptPoll<'error:notconnected' | 'done'> {
|
waitForDisplayedAtStablePosition(node: Node, rafCount: number, waitForEnabled: boolean): InjectedScriptPoll<'error:notconnected' | 'done'> {
|
||||||
let lastRect: types.Rect | undefined;
|
let lastRect: { x: number, y: number, width: number, height: number } | undefined;
|
||||||
let counter = 0;
|
let counter = 0;
|
||||||
let samePositionCounter = 0;
|
let samePositionCounter = 0;
|
||||||
let lastTime = 0;
|
let lastTime = 0;
|
||||||
|
|
@ -517,7 +531,7 @@ export default class InjectedScript {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
checkHitTargetAt(node: Node, point: types.Point): 'error:notconnected' | 'done' | { hitTargetDescription: string } {
|
checkHitTargetAt(node: Node, point: { x: number, y: number }): 'error:notconnected' | 'done' | { hitTargetDescription: string } {
|
||||||
let element: Element | null | undefined = node.nodeType === Node.ELEMENT_NODE ? (node as Element) : node.parentElement;
|
let element: Element | null | undefined = node.nodeType === Node.ELEMENT_NODE ? (node as Element) : node.parentElement;
|
||||||
if (!element || !element.isConnected)
|
if (!element || !element.isConnected)
|
||||||
return 'error:notconnected';
|
return 'error:notconnected';
|
||||||
|
|
@ -683,3 +697,5 @@ const eventType = new Map<string, 'mouse'|'keyboard'|'touch'|'pointer'|'focus'|'
|
||||||
['dragexit', 'drag'],
|
['dragexit', 'drag'],
|
||||||
['drop', 'drag'],
|
['drop', 'drag'],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
export default InjectedScript;
|
||||||
|
|
|
||||||
|
|
@ -15,8 +15,6 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// NOTE: No imports allowed - only primitive, self-contained types are allowed here.
|
|
||||||
|
|
||||||
export type Size = { width: number, height: number };
|
export type Size = { width: number, height: number };
|
||||||
export type Point = { x: number, y: number };
|
export type Point = { x: number, y: number };
|
||||||
export type Rect = Size & Point;
|
export type Rect = Size & Point;
|
||||||
|
|
@ -158,21 +156,6 @@ export type JSCoverageEntry = {
|
||||||
}[]
|
}[]
|
||||||
};
|
};
|
||||||
|
|
||||||
export type InjectedScriptProgress = {
|
|
||||||
aborted: boolean,
|
|
||||||
log: (message: string) => void,
|
|
||||||
logRepeating: (message: string) => void,
|
|
||||||
};
|
|
||||||
|
|
||||||
export type InjectedScriptPoll<T> = {
|
|
||||||
result: Promise<T>,
|
|
||||||
// Takes more logs, waiting until at least one message is available.
|
|
||||||
takeNextLogs: () => Promise<string[]>,
|
|
||||||
// Takes all current logs without waiting.
|
|
||||||
takeLastLogs: () => string[],
|
|
||||||
cancel: () => void,
|
|
||||||
};
|
|
||||||
|
|
||||||
export type ProxySettings = {
|
export type ProxySettings = {
|
||||||
server: string,
|
server: string,
|
||||||
bypass?: string,
|
bypass?: string,
|
||||||
|
|
|
||||||
|
|
@ -108,11 +108,10 @@ DEPS['src/server/'] = [
|
||||||
|
|
||||||
// No dependencies for code shared between node and page.
|
// No dependencies for code shared between node and page.
|
||||||
DEPS['src/server/common/'] = [];
|
DEPS['src/server/common/'] = [];
|
||||||
|
|
||||||
// Strict dependencies for injected code.
|
// Strict dependencies for injected code.
|
||||||
// TODO: remove the injected->types dependency.
|
DEPS['src/server/injected/'] = ['src/server/common/'];
|
||||||
DEPS['src/server/injected/'] = ['src/server/common/', 'src/server/types.ts'];
|
|
||||||
|
|
||||||
|
// Electron uses chromium internally.
|
||||||
DEPS['src/server/electron/'] = [...DEPS['src/server/'], 'src/server/chromium/'];
|
DEPS['src/server/electron/'] = [...DEPS['src/server/'], 'src/server/chromium/'];
|
||||||
|
|
||||||
DEPS['src/server/playwright.ts'] = [...DEPS['src/server/'], 'src/server/chromium/', 'src/server/webkit/', 'src/server/firefox/'];
|
DEPS['src/server/playwright.ts'] = [...DEPS['src/server/'], 'src/server/chromium/', 'src/server/webkit/', 'src/server/firefox/'];
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue