Update dashboard, kb, memory +4 more (+28 ~18 -1)
This commit is contained in:
41
node_modules/puppeteer-core/src/injected/ARIAQuerySelector.ts
generated
vendored
Normal file
41
node_modules/puppeteer-core/src/injected/ARIAQuerySelector.ts
generated
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2022 Google Inc.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
__ariaQuerySelector(root: Node, selector: string): Promise<Node | null>;
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
__ariaQuerySelectorAll(root: Node, selector: string): Promise<Node[]>;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export const ariaQuerySelector = (
|
||||
root: Node,
|
||||
selector: string,
|
||||
): Promise<Node | null> => {
|
||||
// In Firefox sandboxes globalThis !== window and we expose bindings on globalThis.
|
||||
return (globalThis as unknown as Window).__ariaQuerySelector(root, selector);
|
||||
};
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export const ariaQuerySelectorAll = async function* (
|
||||
root: Node,
|
||||
selector: string,
|
||||
): AsyncIterable<Node> {
|
||||
// In Firefox sandboxes globalThis !== window and we expose bindings on globalThis.
|
||||
yield* await (globalThis as unknown as Window).__ariaQuerySelectorAll(
|
||||
root,
|
||||
selector,
|
||||
);
|
||||
};
|
||||
26
node_modules/puppeteer-core/src/injected/CSSSelector.ts
generated
vendored
Normal file
26
node_modules/puppeteer-core/src/injected/CSSSelector.ts
generated
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2024 Google Inc.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export const cssQuerySelector = (
|
||||
root: Node,
|
||||
selector: string,
|
||||
): Element | null => {
|
||||
// @ts-expect-error assume element root
|
||||
return root.querySelector(selector);
|
||||
};
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export const cssQuerySelectorAll = function (
|
||||
root: Node,
|
||||
selector: string,
|
||||
): Iterable<Element> {
|
||||
// @ts-expect-error assume element root
|
||||
return root.querySelectorAll(selector);
|
||||
};
|
||||
65
node_modules/puppeteer-core/src/injected/CustomQuerySelector.ts
generated
vendored
Normal file
65
node_modules/puppeteer-core/src/injected/CustomQuerySelector.ts
generated
vendored
Normal file
@@ -0,0 +1,65 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2023 Google Inc.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import type {CustomQueryHandler} from '../common/CustomQueryHandler.js';
|
||||
import type {Awaitable, AwaitableIterable} from '../common/types.js';
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface CustomQuerySelector {
|
||||
querySelector(root: Node, selector: string): Awaitable<Node | null>;
|
||||
querySelectorAll(root: Node, selector: string): AwaitableIterable<Node>;
|
||||
}
|
||||
|
||||
/**
|
||||
* This class mimics the injected {@link CustomQuerySelectorRegistry}.
|
||||
*/
|
||||
class CustomQuerySelectorRegistry {
|
||||
#selectors = new Map<string, CustomQuerySelector>();
|
||||
|
||||
register(name: string, handler: CustomQueryHandler): void {
|
||||
if (!handler.queryOne && handler.queryAll) {
|
||||
const querySelectorAll = handler.queryAll;
|
||||
handler.queryOne = (node, selector) => {
|
||||
for (const result of querySelectorAll(node, selector)) {
|
||||
return result;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
} else if (handler.queryOne && !handler.queryAll) {
|
||||
const querySelector = handler.queryOne;
|
||||
handler.queryAll = (node, selector) => {
|
||||
const result = querySelector(node, selector);
|
||||
return result ? [result] : [];
|
||||
};
|
||||
} else if (!handler.queryOne || !handler.queryAll) {
|
||||
throw new Error('At least one query method must be defined.');
|
||||
}
|
||||
|
||||
this.#selectors.set(name, {
|
||||
querySelector: handler.queryOne,
|
||||
querySelectorAll: handler.queryAll!,
|
||||
});
|
||||
}
|
||||
|
||||
unregister(name: string): void {
|
||||
this.#selectors.delete(name);
|
||||
}
|
||||
|
||||
get(name: string): CustomQuerySelector | undefined {
|
||||
return this.#selectors.get(name);
|
||||
}
|
||||
|
||||
clear() {
|
||||
this.#selectors.clear();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export const customQuerySelectors = new CustomQuerySelectorRegistry();
|
||||
294
node_modules/puppeteer-core/src/injected/PQuerySelector.ts
generated
vendored
Normal file
294
node_modules/puppeteer-core/src/injected/PQuerySelector.ts
generated
vendored
Normal file
@@ -0,0 +1,294 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2023 Google Inc.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import type {AwaitableIterable} from '../common/types.js';
|
||||
import {AsyncIterableUtil} from '../util/AsyncIterableUtil.js';
|
||||
|
||||
import {ariaQuerySelectorAll} from './ARIAQuerySelector.js';
|
||||
import {customQuerySelectors} from './CustomQuerySelector.js';
|
||||
import {textQuerySelectorAll} from './TextQuerySelector.js';
|
||||
import {pierce, pierceAll} from './util.js';
|
||||
import {xpathQuerySelectorAll} from './XPathQuerySelector.js';
|
||||
|
||||
const IDENT_TOKEN_START = /[-\w\P{ASCII}*]/u;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export type CSSSelector = string;
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export interface PPseudoSelector {
|
||||
name: string;
|
||||
value: string;
|
||||
}
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export const enum PCombinator {
|
||||
Descendent = '>>>',
|
||||
Child = '>>>>',
|
||||
}
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export type CompoundPSelector = Array<CSSSelector | PPseudoSelector>;
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export type ComplexPSelector = Array<CompoundPSelector | PCombinator>;
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export type ComplexPSelectorList = ComplexPSelector[];
|
||||
|
||||
interface QueryableNode extends Node {
|
||||
querySelectorAll: typeof Document.prototype.querySelectorAll;
|
||||
}
|
||||
|
||||
const isQueryableNode = (node: Node): node is QueryableNode => {
|
||||
return 'querySelectorAll' in node;
|
||||
};
|
||||
|
||||
class PQueryEngine {
|
||||
#complexSelector: ComplexPSelector;
|
||||
#compoundSelector: CompoundPSelector = [];
|
||||
#selector: CSSSelector | PPseudoSelector | undefined = undefined;
|
||||
|
||||
elements: AwaitableIterable<Node>;
|
||||
|
||||
constructor(element: Node, complexSelector: ComplexPSelector) {
|
||||
this.elements = [element];
|
||||
this.#complexSelector = complexSelector;
|
||||
this.#next();
|
||||
}
|
||||
|
||||
async run(): Promise<void> {
|
||||
if (typeof this.#selector === 'string') {
|
||||
switch (this.#selector.trimStart()) {
|
||||
case ':scope':
|
||||
// `:scope` has some special behavior depending on the node. It always
|
||||
// represents the current node within a compound selector, but by
|
||||
// itself, it depends on the node. For example, Document is
|
||||
// represented by `<html>`, but any HTMLElement is not represented by
|
||||
// itself (i.e. `null`). This can be troublesome if our combinators
|
||||
// are used right after so we treat this selector specially.
|
||||
this.#next();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (; this.#selector !== undefined; this.#next()) {
|
||||
const selector = this.#selector;
|
||||
if (typeof selector === 'string') {
|
||||
// The regular expression tests if the selector is a type/universal
|
||||
// selector. Any other case means we want to apply the selector onto
|
||||
// the element itself (e.g. `element.class`, `element>div`,
|
||||
// `element:hover`, etc.).
|
||||
if (selector[0] && IDENT_TOKEN_START.test(selector[0])) {
|
||||
this.elements = AsyncIterableUtil.flatMap(
|
||||
this.elements,
|
||||
async function* (element) {
|
||||
if (isQueryableNode(element)) {
|
||||
yield* element.querySelectorAll(selector);
|
||||
}
|
||||
},
|
||||
);
|
||||
} else {
|
||||
this.elements = AsyncIterableUtil.flatMap(
|
||||
this.elements,
|
||||
async function* (element) {
|
||||
if (!element.parentElement) {
|
||||
if (!isQueryableNode(element)) {
|
||||
return;
|
||||
}
|
||||
yield* element.querySelectorAll(selector);
|
||||
return;
|
||||
}
|
||||
|
||||
let index = 0;
|
||||
for (const child of element.parentElement.children) {
|
||||
++index;
|
||||
if (child === element) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
yield* element.parentElement.querySelectorAll(
|
||||
`:scope>:nth-child(${index})${selector}`,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
} else {
|
||||
this.elements = AsyncIterableUtil.flatMap(
|
||||
this.elements,
|
||||
async function* (element) {
|
||||
switch (selector.name) {
|
||||
case 'text':
|
||||
yield* textQuerySelectorAll(element, selector.value);
|
||||
break;
|
||||
case 'xpath':
|
||||
yield* xpathQuerySelectorAll(element, selector.value);
|
||||
break;
|
||||
case 'aria':
|
||||
yield* ariaQuerySelectorAll(element, selector.value);
|
||||
break;
|
||||
default:
|
||||
const querySelector = customQuerySelectors.get(selector.name);
|
||||
if (!querySelector) {
|
||||
throw new Error(`Unknown selector type: ${selector.name}`);
|
||||
}
|
||||
yield* querySelector.querySelectorAll(element, selector.value);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#next() {
|
||||
if (this.#compoundSelector.length !== 0) {
|
||||
this.#selector = this.#compoundSelector.shift();
|
||||
return;
|
||||
}
|
||||
if (this.#complexSelector.length === 0) {
|
||||
this.#selector = undefined;
|
||||
return;
|
||||
}
|
||||
const selector = this.#complexSelector.shift();
|
||||
switch (selector) {
|
||||
case PCombinator.Child: {
|
||||
this.elements = AsyncIterableUtil.flatMap(this.elements, pierce);
|
||||
this.#next();
|
||||
break;
|
||||
}
|
||||
case PCombinator.Descendent: {
|
||||
this.elements = AsyncIterableUtil.flatMap(this.elements, pierceAll);
|
||||
this.#next();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
this.#compoundSelector = selector as CompoundPSelector;
|
||||
this.#next();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class DepthCalculator {
|
||||
#cache = new WeakMap<Node, number[]>();
|
||||
|
||||
calculate(node: Node | null, depth: number[] = []): number[] {
|
||||
if (node === null) {
|
||||
return depth;
|
||||
}
|
||||
if (node instanceof ShadowRoot) {
|
||||
node = node.host;
|
||||
}
|
||||
|
||||
const cachedDepth = this.#cache.get(node);
|
||||
if (cachedDepth) {
|
||||
return [...cachedDepth, ...depth];
|
||||
}
|
||||
|
||||
let index = 0;
|
||||
for (
|
||||
let prevSibling = node.previousSibling;
|
||||
prevSibling;
|
||||
prevSibling = prevSibling.previousSibling
|
||||
) {
|
||||
++index;
|
||||
}
|
||||
|
||||
const value = this.calculate(node.parentNode, [index]);
|
||||
this.#cache.set(node, value);
|
||||
return [...value, ...depth];
|
||||
}
|
||||
}
|
||||
|
||||
const compareDepths = (a: number[], b: number[]): -1 | 0 | 1 => {
|
||||
if (a.length + b.length === 0) {
|
||||
return 0;
|
||||
}
|
||||
const [i = -1, ...otherA] = a;
|
||||
const [j = -1, ...otherB] = b;
|
||||
if (i === j) {
|
||||
return compareDepths(otherA, otherB);
|
||||
}
|
||||
return i < j ? -1 : 1;
|
||||
};
|
||||
|
||||
const domSort = async function* (elements: AwaitableIterable<Node>) {
|
||||
const results = new Set<Node>();
|
||||
for await (const element of elements) {
|
||||
results.add(element);
|
||||
}
|
||||
const calculator = new DepthCalculator();
|
||||
yield* [...results.values()]
|
||||
.map(result => {
|
||||
return [result, calculator.calculate(result)] as const;
|
||||
})
|
||||
.sort(([, a], [, b]) => {
|
||||
return compareDepths(a, b);
|
||||
})
|
||||
.map(([result]) => {
|
||||
return result;
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Queries the given node for all nodes matching the given text selector.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
export const pQuerySelectorAll = function (
|
||||
root: Node,
|
||||
selector: string,
|
||||
): AwaitableIterable<Node> {
|
||||
const selectors = JSON.parse(selector) as ComplexPSelectorList;
|
||||
// If there are any empty elements, then this implies the selector has
|
||||
// contiguous combinators (e.g. `>>> >>>>`) or starts/ends with one which we
|
||||
// treat as illegal, similar to existing behavior.
|
||||
if (
|
||||
selectors.some(parts => {
|
||||
let i = 0;
|
||||
return parts.some(parts => {
|
||||
if (typeof parts === 'string') {
|
||||
++i;
|
||||
} else {
|
||||
i = 0;
|
||||
}
|
||||
return i > 1;
|
||||
});
|
||||
})
|
||||
) {
|
||||
throw new Error('Multiple deep combinators found in sequence.');
|
||||
}
|
||||
|
||||
return domSort(
|
||||
AsyncIterableUtil.flatMap(selectors, selectorParts => {
|
||||
const query = new PQueryEngine(root, selectorParts);
|
||||
void query.run();
|
||||
return query.elements;
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Queries the given node for all nodes matching the given text selector.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
export const pQuerySelector = async function (
|
||||
root: Node,
|
||||
selector: string,
|
||||
): Promise<Node | null> {
|
||||
for await (const element of pQuerySelectorAll(root, selector)) {
|
||||
return element;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
65
node_modules/puppeteer-core/src/injected/PierceQuerySelector.ts
generated
vendored
Normal file
65
node_modules/puppeteer-core/src/injected/PierceQuerySelector.ts
generated
vendored
Normal file
@@ -0,0 +1,65 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2022 Google Inc.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export const pierceQuerySelector = (
|
||||
root: Node,
|
||||
selector: string,
|
||||
): Element | null => {
|
||||
let found: Node | null = null;
|
||||
const search = (root: Node) => {
|
||||
const iter = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT);
|
||||
do {
|
||||
const currentNode = iter.currentNode as Element;
|
||||
if (currentNode.shadowRoot) {
|
||||
search(currentNode.shadowRoot);
|
||||
}
|
||||
if (currentNode instanceof ShadowRoot) {
|
||||
continue;
|
||||
}
|
||||
if (currentNode !== root && !found && currentNode.matches(selector)) {
|
||||
found = currentNode;
|
||||
}
|
||||
} while (!found && iter.nextNode());
|
||||
};
|
||||
if (root instanceof Document) {
|
||||
root = root.documentElement;
|
||||
}
|
||||
search(root);
|
||||
return found;
|
||||
};
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export const pierceQuerySelectorAll = (
|
||||
element: Node,
|
||||
selector: string,
|
||||
): Element[] => {
|
||||
const result: Element[] = [];
|
||||
const collect = (root: Node) => {
|
||||
const iter = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT);
|
||||
do {
|
||||
const currentNode = iter.currentNode as Element;
|
||||
if (currentNode.shadowRoot) {
|
||||
collect(currentNode.shadowRoot);
|
||||
}
|
||||
if (currentNode instanceof ShadowRoot) {
|
||||
continue;
|
||||
}
|
||||
if (currentNode !== root && currentNode.matches(selector)) {
|
||||
result.push(currentNode);
|
||||
}
|
||||
} while (iter.nextNode());
|
||||
};
|
||||
if (element instanceof Document) {
|
||||
element = element.documentElement;
|
||||
}
|
||||
collect(element);
|
||||
return result;
|
||||
};
|
||||
168
node_modules/puppeteer-core/src/injected/Poller.ts
generated
vendored
Normal file
168
node_modules/puppeteer-core/src/injected/Poller.ts
generated
vendored
Normal file
@@ -0,0 +1,168 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2022 Google Inc.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import {assert} from '../util/assert.js';
|
||||
import {Deferred} from '../util/Deferred.js';
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export interface Poller<T> {
|
||||
start(): Promise<void>;
|
||||
stop(): Promise<void>;
|
||||
result(): Promise<T>;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export class MutationPoller<T> implements Poller<T> {
|
||||
#fn: () => Promise<T>;
|
||||
|
||||
#root: Node;
|
||||
|
||||
#observer?: MutationObserver;
|
||||
#deferred?: Deferred<T>;
|
||||
constructor(fn: () => Promise<T>, root: Node) {
|
||||
this.#fn = fn;
|
||||
this.#root = root;
|
||||
}
|
||||
|
||||
async start(): Promise<void> {
|
||||
const deferred = (this.#deferred = Deferred.create<T>());
|
||||
const result = await this.#fn();
|
||||
if (result) {
|
||||
deferred.resolve(result);
|
||||
return;
|
||||
}
|
||||
|
||||
this.#observer = new MutationObserver(async () => {
|
||||
const result = await this.#fn();
|
||||
if (!result) {
|
||||
return;
|
||||
}
|
||||
deferred.resolve(result);
|
||||
await this.stop();
|
||||
});
|
||||
this.#observer.observe(this.#root, {
|
||||
childList: true,
|
||||
subtree: true,
|
||||
attributes: true,
|
||||
});
|
||||
}
|
||||
|
||||
async stop(): Promise<void> {
|
||||
assert(this.#deferred, 'Polling never started.');
|
||||
if (!this.#deferred.finished()) {
|
||||
this.#deferred.reject(new Error('Polling stopped'));
|
||||
}
|
||||
if (this.#observer) {
|
||||
this.#observer.disconnect();
|
||||
this.#observer = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
result(): Promise<T> {
|
||||
assert(this.#deferred, 'Polling never started.');
|
||||
return this.#deferred.valueOrThrow();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export class RAFPoller<T> implements Poller<T> {
|
||||
#fn: () => Promise<T>;
|
||||
#deferred?: Deferred<T>;
|
||||
constructor(fn: () => Promise<T>) {
|
||||
this.#fn = fn;
|
||||
}
|
||||
|
||||
async start(): Promise<void> {
|
||||
const deferred = (this.#deferred = Deferred.create<T>());
|
||||
const result = await this.#fn();
|
||||
if (result) {
|
||||
deferred.resolve(result);
|
||||
return;
|
||||
}
|
||||
|
||||
const poll = async () => {
|
||||
if (deferred.finished()) {
|
||||
return;
|
||||
}
|
||||
const result = await this.#fn();
|
||||
if (!result) {
|
||||
window.requestAnimationFrame(poll);
|
||||
return;
|
||||
}
|
||||
deferred.resolve(result);
|
||||
await this.stop();
|
||||
};
|
||||
window.requestAnimationFrame(poll);
|
||||
}
|
||||
|
||||
async stop(): Promise<void> {
|
||||
assert(this.#deferred, 'Polling never started.');
|
||||
if (!this.#deferred.finished()) {
|
||||
this.#deferred.reject(new Error('Polling stopped'));
|
||||
}
|
||||
}
|
||||
|
||||
result(): Promise<T> {
|
||||
assert(this.#deferred, 'Polling never started.');
|
||||
return this.#deferred.valueOrThrow();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
|
||||
export class IntervalPoller<T> implements Poller<T> {
|
||||
#fn: () => Promise<T>;
|
||||
#ms: number;
|
||||
|
||||
#interval?: NodeJS.Timeout;
|
||||
#deferred?: Deferred<T>;
|
||||
constructor(fn: () => Promise<T>, ms: number) {
|
||||
this.#fn = fn;
|
||||
this.#ms = ms;
|
||||
}
|
||||
|
||||
async start(): Promise<void> {
|
||||
const deferred = (this.#deferred = Deferred.create<T>());
|
||||
const result = await this.#fn();
|
||||
if (result) {
|
||||
deferred.resolve(result);
|
||||
return;
|
||||
}
|
||||
|
||||
this.#interval = setInterval(async () => {
|
||||
const result = await this.#fn();
|
||||
if (!result) {
|
||||
return;
|
||||
}
|
||||
deferred.resolve(result);
|
||||
await this.stop();
|
||||
}, this.#ms);
|
||||
}
|
||||
|
||||
async stop(): Promise<void> {
|
||||
assert(this.#deferred, 'Polling never started.');
|
||||
if (!this.#deferred.finished()) {
|
||||
this.#deferred.reject(new Error('Polling stopped'));
|
||||
}
|
||||
if (this.#interval) {
|
||||
clearInterval(this.#interval);
|
||||
this.#interval = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
result(): Promise<T> {
|
||||
assert(this.#deferred, 'Polling never started.');
|
||||
return this.#deferred.valueOrThrow();
|
||||
}
|
||||
}
|
||||
5
node_modules/puppeteer-core/src/injected/README.md
generated
vendored
Normal file
5
node_modules/puppeteer-core/src/injected/README.md
generated
vendored
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 `tools/generate_sources.ts` for more information.
|
||||
146
node_modules/puppeteer-core/src/injected/TextContent.ts
generated
vendored
Normal file
146
node_modules/puppeteer-core/src/injected/TextContent.ts
generated
vendored
Normal file
@@ -0,0 +1,146 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2022 Google Inc.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
interface NonTrivialValueNode extends Node {
|
||||
value: string;
|
||||
}
|
||||
|
||||
const TRIVIAL_VALUE_INPUT_TYPES = new Set(['checkbox', 'image', 'radio']);
|
||||
|
||||
/**
|
||||
* Determines if the node has a non-trivial value property.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
const isNonTrivialValueNode = (node: Node): node is NonTrivialValueNode => {
|
||||
if (node instanceof HTMLSelectElement) {
|
||||
return true;
|
||||
}
|
||||
if (node instanceof HTMLTextAreaElement) {
|
||||
return true;
|
||||
}
|
||||
if (
|
||||
node instanceof HTMLInputElement &&
|
||||
!TRIVIAL_VALUE_INPUT_TYPES.has(node.type)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
const UNSUITABLE_NODE_NAMES = new Set(['SCRIPT', 'STYLE']);
|
||||
|
||||
/**
|
||||
* Determines whether a given node is suitable for text matching.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
export const isSuitableNodeForTextMatching = (node: Node): boolean => {
|
||||
return (
|
||||
!UNSUITABLE_NODE_NAMES.has(node.nodeName) && !document.head?.contains(node)
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export interface TextContent {
|
||||
// Contains the full text of the node.
|
||||
full: string;
|
||||
// Contains the text immediately beneath the node.
|
||||
immediate: string[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps {@link Node}s to their computed {@link TextContent}.
|
||||
*/
|
||||
const textContentCache = new WeakMap<Node, TextContent>();
|
||||
const eraseFromCache = (node: Node | null) => {
|
||||
while (node) {
|
||||
textContentCache.delete(node);
|
||||
if (node instanceof ShadowRoot) {
|
||||
node = node.host;
|
||||
} else {
|
||||
node = node.parentNode;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Erases the cache when the tree has mutated text.
|
||||
*/
|
||||
const observedNodes = new WeakSet<Node>();
|
||||
const textChangeObserver = new MutationObserver(mutations => {
|
||||
for (const mutation of mutations) {
|
||||
eraseFromCache(mutation.target);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Builds the text content of a node using some custom logic.
|
||||
*
|
||||
* @remarks
|
||||
* The primary reason this function exists is due to {@link ShadowRoot}s not having
|
||||
* text content.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
export const createTextContent = (root: Node): TextContent => {
|
||||
let value = textContentCache.get(root);
|
||||
if (value) {
|
||||
return value;
|
||||
}
|
||||
value = {full: '', immediate: []};
|
||||
if (!isSuitableNodeForTextMatching(root)) {
|
||||
return value;
|
||||
}
|
||||
|
||||
let currentImmediate = '';
|
||||
if (isNonTrivialValueNode(root)) {
|
||||
value.full = root.value;
|
||||
value.immediate.push(root.value);
|
||||
|
||||
root.addEventListener(
|
||||
'input',
|
||||
event => {
|
||||
eraseFromCache(event.target as HTMLInputElement);
|
||||
},
|
||||
{once: true, capture: true},
|
||||
);
|
||||
} else {
|
||||
for (let child = root.firstChild; child; child = child.nextSibling) {
|
||||
if (child.nodeType === Node.TEXT_NODE) {
|
||||
value.full += child.nodeValue ?? '';
|
||||
currentImmediate += child.nodeValue ?? '';
|
||||
continue;
|
||||
}
|
||||
if (currentImmediate) {
|
||||
value.immediate.push(currentImmediate);
|
||||
}
|
||||
currentImmediate = '';
|
||||
if (child.nodeType === Node.ELEMENT_NODE) {
|
||||
value.full += createTextContent(child).full;
|
||||
}
|
||||
}
|
||||
if (currentImmediate) {
|
||||
value.immediate.push(currentImmediate);
|
||||
}
|
||||
if (root instanceof Element && root.shadowRoot) {
|
||||
value.full += createTextContent(root.shadowRoot).full;
|
||||
}
|
||||
|
||||
if (!observedNodes.has(root)) {
|
||||
textChangeObserver.observe(root, {
|
||||
childList: true,
|
||||
characterData: true,
|
||||
subtree: true,
|
||||
});
|
||||
observedNodes.add(root);
|
||||
}
|
||||
}
|
||||
textContentCache.set(root, value);
|
||||
return value;
|
||||
};
|
||||
46
node_modules/puppeteer-core/src/injected/TextQuerySelector.ts
generated
vendored
Normal file
46
node_modules/puppeteer-core/src/injected/TextQuerySelector.ts
generated
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2022 Google Inc.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import {
|
||||
createTextContent,
|
||||
isSuitableNodeForTextMatching,
|
||||
} from './TextContent.js';
|
||||
|
||||
/**
|
||||
* Queries the given node for all nodes matching the given text selector.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
export const textQuerySelectorAll = function* (
|
||||
root: Node,
|
||||
selector: string,
|
||||
): Generator<Element> {
|
||||
let yielded = false;
|
||||
for (const node of root.childNodes) {
|
||||
if (node instanceof Element && isSuitableNodeForTextMatching(node)) {
|
||||
let matches: Generator<Element, boolean>;
|
||||
if (!node.shadowRoot) {
|
||||
matches = textQuerySelectorAll(node, selector);
|
||||
} else {
|
||||
matches = textQuerySelectorAll(node.shadowRoot, selector);
|
||||
}
|
||||
for (const match of matches) {
|
||||
yield match;
|
||||
yielded = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (yielded) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (root instanceof Element && isSuitableNodeForTextMatching(root)) {
|
||||
const textContent = createTextContent(root);
|
||||
if (textContent.full.includes(selector)) {
|
||||
yield root;
|
||||
}
|
||||
}
|
||||
};
|
||||
39
node_modules/puppeteer-core/src/injected/XPathQuerySelector.ts
generated
vendored
Normal file
39
node_modules/puppeteer-core/src/injected/XPathQuerySelector.ts
generated
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2022 Google Inc.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export const xpathQuerySelectorAll = function* (
|
||||
root: Node,
|
||||
selector: string,
|
||||
maxResults = -1,
|
||||
): Iterable<Node> {
|
||||
const doc = root.ownerDocument || document;
|
||||
const iterator = doc.evaluate(
|
||||
selector,
|
||||
root,
|
||||
null,
|
||||
XPathResult.ORDERED_NODE_ITERATOR_TYPE,
|
||||
);
|
||||
const items = [];
|
||||
let item;
|
||||
|
||||
// Read all results upfront to avoid
|
||||
// https://stackoverflow.com/questions/48235278/xpath-error-the-document-has-mutated-since-the-result-was-returned.
|
||||
while ((item = iterator.iterateNext())) {
|
||||
items.push(item);
|
||||
if (maxResults && items.length === maxResults) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
item = items[i];
|
||||
yield item as Node;
|
||||
items[i] = null;
|
||||
}
|
||||
};
|
||||
53
node_modules/puppeteer-core/src/injected/injected.ts
generated
vendored
Normal file
53
node_modules/puppeteer-core/src/injected/injected.ts
generated
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2022 Google Inc.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import {Deferred} from '../util/Deferred.js';
|
||||
import {createFunction} from '../util/Function.js';
|
||||
|
||||
import * as ARIAQuerySelector from './ARIAQuerySelector.js';
|
||||
import * as CSSSelector from './CSSSelector.js';
|
||||
import * as CustomQuerySelectors from './CustomQuerySelector.js';
|
||||
import * as PierceQuerySelector from './PierceQuerySelector.js';
|
||||
import {IntervalPoller, MutationPoller, RAFPoller} from './Poller.js';
|
||||
import * as PQuerySelector from './PQuerySelector.js';
|
||||
import {
|
||||
createTextContent,
|
||||
isSuitableNodeForTextMatching,
|
||||
} from './TextContent.js';
|
||||
import * as TextQuerySelector from './TextQuerySelector.js';
|
||||
import * as util from './util.js';
|
||||
import * as XPathQuerySelector from './XPathQuerySelector.js';
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
const PuppeteerUtil = Object.freeze({
|
||||
...ARIAQuerySelector,
|
||||
...CustomQuerySelectors,
|
||||
...PierceQuerySelector,
|
||||
...PQuerySelector,
|
||||
...TextQuerySelector,
|
||||
...util,
|
||||
...XPathQuerySelector,
|
||||
...CSSSelector,
|
||||
Deferred,
|
||||
createFunction,
|
||||
createTextContent,
|
||||
IntervalPoller,
|
||||
isSuitableNodeForTextMatching,
|
||||
MutationPoller,
|
||||
RAFPoller,
|
||||
});
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export type PuppeteerInjectedUtil = typeof PuppeteerUtil;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export default PuppeteerUtil;
|
||||
72
node_modules/puppeteer-core/src/injected/util.ts
generated
vendored
Normal file
72
node_modules/puppeteer-core/src/injected/util.ts
generated
vendored
Normal file
@@ -0,0 +1,72 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2024 Google Inc.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
const HIDDEN_VISIBILITY_VALUES = ['hidden', 'collapse'];
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export const checkVisibility = (
|
||||
node: Node | null,
|
||||
visible?: boolean,
|
||||
): Node | boolean => {
|
||||
if (!node) {
|
||||
return visible === false;
|
||||
}
|
||||
if (visible === undefined) {
|
||||
return node;
|
||||
}
|
||||
const element = (
|
||||
node.nodeType === Node.TEXT_NODE ? node.parentElement : node
|
||||
) as Element;
|
||||
|
||||
const style = window.getComputedStyle(element);
|
||||
const isVisible =
|
||||
style &&
|
||||
!HIDDEN_VISIBILITY_VALUES.includes(style.visibility) &&
|
||||
!isBoundingBoxEmpty(element);
|
||||
return visible === isVisible ? node : false;
|
||||
};
|
||||
|
||||
function isBoundingBoxEmpty(element: Element): boolean {
|
||||
const rect = element.getBoundingClientRect();
|
||||
return rect.width === 0 || rect.height === 0;
|
||||
}
|
||||
|
||||
const hasShadowRoot = (node: Node): node is Node & {shadowRoot: ShadowRoot} => {
|
||||
return 'shadowRoot' in node && node.shadowRoot instanceof ShadowRoot;
|
||||
};
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export function* pierce(root: Node): IterableIterator<Node | ShadowRoot> {
|
||||
if (hasShadowRoot(root)) {
|
||||
yield root.shadowRoot;
|
||||
} else {
|
||||
yield root;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export function* pierceAll(root: Node): IterableIterator<Node | ShadowRoot> {
|
||||
root = pierce(root).next().value;
|
||||
yield root;
|
||||
const walkers = [document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT)];
|
||||
for (const walker of walkers) {
|
||||
let node: Element | null;
|
||||
while ((node = walker.nextNode() as Element | null)) {
|
||||
if (!node.shadowRoot) {
|
||||
continue;
|
||||
}
|
||||
yield node.shadowRoot;
|
||||
walkers.push(
|
||||
document.createTreeWalker(node.shadowRoot, NodeFilter.SHOW_ELEMENT),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user