mirror of
https://github.com/microsoft/playwright.git
synced 2025-06-26 21:40:17 +00:00
feat(engines): move querySelectorAll to css engine (#61)
This commit is contained in:
parent
7633518272
commit
7c69f8c457
@ -27,6 +27,8 @@ import { Protocol } from './protocol';
|
|||||||
import { releaseObject, valueFromRemoteObject } from './protocolHelper';
|
import { releaseObject, valueFromRemoteObject } from './protocolHelper';
|
||||||
import Injected from '../injected/injected';
|
import Injected from '../injected/injected';
|
||||||
|
|
||||||
|
type SelectorRoot = Element | ShadowRoot | Document;
|
||||||
|
|
||||||
type Point = {
|
type Point = {
|
||||||
x: number;
|
x: number;
|
||||||
y: number;
|
y: number;
|
||||||
@ -57,7 +59,7 @@ export class JSHandle {
|
|||||||
return this._context;
|
return this._context;
|
||||||
}
|
}
|
||||||
|
|
||||||
async evaluate(pageFunction: Function | string, ...args: any[]): Promise<(any)> {
|
async evaluate(pageFunction: Function | string, ...args: any[]): Promise<any> {
|
||||||
return await this.executionContext().evaluate(pageFunction, this, ...args);
|
return await this.executionContext().evaluate(pageFunction, this, ...args);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -431,9 +433,7 @@ export class ElementHandle extends JSHandle {
|
|||||||
|
|
||||||
async $(selector: string): Promise<ElementHandle | null> {
|
async $(selector: string): Promise<ElementHandle | null> {
|
||||||
const handle = await this.evaluateHandle(
|
const handle = await this.evaluateHandle(
|
||||||
(element, selector, injected: Injected) => {
|
(root: SelectorRoot, selector: string, injected: Injected) => injected.querySelector('css=' + selector, root),
|
||||||
return injected.querySelector('css=' + selector, element);
|
|
||||||
},
|
|
||||||
selector, await this._context._injected()
|
selector, await this._context._injected()
|
||||||
);
|
);
|
||||||
const element = handle.asElement();
|
const element = handle.asElement();
|
||||||
@ -445,8 +445,8 @@ export class ElementHandle extends JSHandle {
|
|||||||
|
|
||||||
async $$(selector: string): Promise<ElementHandle[]> {
|
async $$(selector: string): Promise<ElementHandle[]> {
|
||||||
const arrayHandle = await this.evaluateHandle(
|
const arrayHandle = await this.evaluateHandle(
|
||||||
(element, selector) => element.querySelectorAll(selector),
|
(root: SelectorRoot, selector: string, injected: Injected) => injected.querySelectorAll('css=' + selector, root),
|
||||||
selector
|
selector, await this._context._injected()
|
||||||
);
|
);
|
||||||
const properties = await arrayHandle.getProperties();
|
const properties = await arrayHandle.getProperties();
|
||||||
await arrayHandle.dispose();
|
await arrayHandle.dispose();
|
||||||
@ -459,7 +459,7 @@ export class ElementHandle extends JSHandle {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
async $eval(selector: string, pageFunction: Function | string, ...args: any[]): Promise<(object | undefined)> {
|
async $eval(selector: string, pageFunction: Function | string, ...args: any[]): Promise<object | undefined> {
|
||||||
const elementHandle = await this.$(selector);
|
const elementHandle = await this.$(selector);
|
||||||
if (!elementHandle)
|
if (!elementHandle)
|
||||||
throw new Error(`Error: failed to find element matching selector "${selector}"`);
|
throw new Error(`Error: failed to find element matching selector "${selector}"`);
|
||||||
@ -468,10 +468,10 @@ export class ElementHandle extends JSHandle {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
async $$eval(selector: string, pageFunction: Function | string, ...args: any[]): Promise<(object | undefined)> {
|
async $$eval(selector: string, pageFunction: Function | string, ...args: any[]): Promise<object | undefined> {
|
||||||
const arrayHandle = await this.evaluateHandle(
|
const arrayHandle = await this.evaluateHandle(
|
||||||
(element, selector) => Array.from(element.querySelectorAll(selector)),
|
(root: SelectorRoot, selector: string, injected: Injected) => injected.querySelectorAll('css=' + selector, root),
|
||||||
selector
|
selector, await this._context._injected()
|
||||||
);
|
);
|
||||||
|
|
||||||
const result = await arrayHandle.evaluate(pageFunction, ...args);
|
const result = await arrayHandle.evaluate(pageFunction, ...args);
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import {TimeoutError} from '../Errors';
|
|||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
import * as util from 'util';
|
import * as util from 'util';
|
||||||
import {ElementHandle, JSHandle} from './JSHandle';
|
import {ElementHandle, JSHandle} from './JSHandle';
|
||||||
|
import { ExecutionContext } from './ExecutionContext';
|
||||||
|
|
||||||
const readFileAsync = util.promisify(fs.readFile);
|
const readFileAsync = util.promisify(fs.readFile);
|
||||||
|
|
||||||
@ -51,7 +52,7 @@ export class DOMWorld {
|
|||||||
waitTask.terminate(new Error('waitForFunction failed: frame got detached.'));
|
waitTask.terminate(new Error('waitForFunction failed: frame got detached.'));
|
||||||
}
|
}
|
||||||
|
|
||||||
async executionContext() {
|
async executionContext(): Promise<ExecutionContext> {
|
||||||
if (this._detached)
|
if (this._detached)
|
||||||
throw new Error(`Execution Context is not available in detached frame "${this.url()}" (are you trying to evaluate?)`);
|
throw new Error(`Execution Context is not available in detached frame "${this.url()}" (are you trying to evaluate?)`);
|
||||||
return this._contextPromise;
|
return this._contextPromise;
|
||||||
@ -60,12 +61,12 @@ export class DOMWorld {
|
|||||||
throw new Error('Method not implemented.');
|
throw new Error('Method not implemented.');
|
||||||
}
|
}
|
||||||
|
|
||||||
async evaluateHandle(pageFunction, ...args) {
|
async evaluateHandle(pageFunction, ...args): Promise<JSHandle> {
|
||||||
const context = await this.executionContext();
|
const context = await this.executionContext();
|
||||||
return context.evaluateHandle(pageFunction, ...args);
|
return context.evaluateHandle(pageFunction, ...args);
|
||||||
}
|
}
|
||||||
|
|
||||||
async evaluate(pageFunction, ...args) {
|
async evaluate(pageFunction, ...args): Promise<any> {
|
||||||
const context = await this.executionContext();
|
const context = await this.executionContext();
|
||||||
return context.evaluate(pageFunction, ...args);
|
return context.evaluate(pageFunction, ...args);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -33,7 +33,7 @@ export class ExecutionContext {
|
|||||||
this._executionContextId = executionContextId;
|
this._executionContextId = executionContextId;
|
||||||
}
|
}
|
||||||
|
|
||||||
async evaluateHandle(pageFunction, ...args) {
|
async evaluateHandle(pageFunction, ...args): Promise<JSHandle> {
|
||||||
if (helper.isString(pageFunction)) {
|
if (helper.isString(pageFunction)) {
|
||||||
const payload = await this._session.send('Runtime.evaluate', {
|
const payload = await this._session.send('Runtime.evaluate', {
|
||||||
expression: pageFunction.trim(),
|
expression: pageFunction.trim(),
|
||||||
@ -105,7 +105,7 @@ export class ExecutionContext {
|
|||||||
return this._frame;
|
return this._frame;
|
||||||
}
|
}
|
||||||
|
|
||||||
async evaluate(pageFunction, ...args) {
|
async evaluate(pageFunction, ...args): Promise<any> {
|
||||||
try {
|
try {
|
||||||
const handle = await this.evaluateHandle(pageFunction, ...args);
|
const handle = await this.evaluateHandle(pageFunction, ...args);
|
||||||
const result = await handle.jsonValue();
|
const result = await handle.jsonValue();
|
||||||
|
|||||||
@ -352,7 +352,7 @@ export class Frame {
|
|||||||
return this._mainWorld.setContent(html);
|
return this._mainWorld.setContent(html);
|
||||||
}
|
}
|
||||||
|
|
||||||
async evaluate(pageFunction, ...args) {
|
async evaluate(pageFunction, ...args): Promise<any> {
|
||||||
return this._mainWorld.evaluate(pageFunction, ...args);
|
return this._mainWorld.evaluate(pageFunction, ...args);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -376,7 +376,7 @@ export class Frame {
|
|||||||
return this._mainWorld.$x(expression);
|
return this._mainWorld.$x(expression);
|
||||||
}
|
}
|
||||||
|
|
||||||
async evaluateHandle(pageFunction, ...args) {
|
async evaluateHandle(pageFunction, ...args): Promise<JSHandle> {
|
||||||
return this._mainWorld.evaluateHandle(pageFunction, ...args);
|
return this._mainWorld.evaluateHandle(pageFunction, ...args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -23,6 +23,8 @@ import { JugglerSession } from './Connection';
|
|||||||
import { MultiClickOptions, ClickOptions, selectFunction, SelectOption } from '../input';
|
import { MultiClickOptions, ClickOptions, selectFunction, SelectOption } from '../input';
|
||||||
import Injected from '../injected/injected';
|
import Injected from '../injected/injected';
|
||||||
|
|
||||||
|
type SelectorRoot = Element | ShadowRoot | Document;
|
||||||
|
|
||||||
export class JSHandle {
|
export class JSHandle {
|
||||||
_context: ExecutionContext;
|
_context: ExecutionContext;
|
||||||
protected _session: JugglerSession;
|
protected _session: JugglerSession;
|
||||||
@ -203,9 +205,7 @@ export class ElementHandle extends JSHandle {
|
|||||||
|
|
||||||
async $(selector: string): Promise<ElementHandle | null> {
|
async $(selector: string): Promise<ElementHandle | null> {
|
||||||
const handle = await this._frame.evaluateHandle(
|
const handle = await this._frame.evaluateHandle(
|
||||||
(element, selector, injected: Injected) => {
|
(root: SelectorRoot, selector: string, injected: Injected) => injected.querySelector('css=' + selector, root),
|
||||||
return injected.querySelector('css=' + selector, element);
|
|
||||||
},
|
|
||||||
this, selector, await this._context._injected()
|
this, selector, await this._context._injected()
|
||||||
);
|
);
|
||||||
const element = handle.asElement();
|
const element = handle.asElement();
|
||||||
@ -215,10 +215,10 @@ export class ElementHandle extends JSHandle {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
async $$(selector: string): Promise<Array<ElementHandle>> {
|
async $$(selector: string): Promise<ElementHandle[]> {
|
||||||
const arrayHandle = await this._frame.evaluateHandle(
|
const arrayHandle = await this._frame.evaluateHandle(
|
||||||
(element, selector) => element.querySelectorAll(selector),
|
(root: SelectorRoot, selector: string, injected: Injected) => injected.querySelectorAll('css=' + selector, root),
|
||||||
this, selector
|
this, selector, await this._context._injected()
|
||||||
);
|
);
|
||||||
const properties = await arrayHandle.getProperties();
|
const properties = await arrayHandle.getProperties();
|
||||||
await arrayHandle.dispose();
|
await arrayHandle.dispose();
|
||||||
@ -231,7 +231,7 @@ export class ElementHandle extends JSHandle {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
async $eval(selector: string, pageFunction: Function | string, ...args: Array<any>): Promise<(object | undefined)> {
|
async $eval(selector: string, pageFunction: Function | string, ...args: Array<any>): Promise<object | undefined> {
|
||||||
const elementHandle = await this.$(selector);
|
const elementHandle = await this.$(selector);
|
||||||
if (!elementHandle)
|
if (!elementHandle)
|
||||||
throw new Error(`Error: failed to find element matching selector "${selector}"`);
|
throw new Error(`Error: failed to find element matching selector "${selector}"`);
|
||||||
@ -240,10 +240,10 @@ export class ElementHandle extends JSHandle {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
async $$eval(selector: string, pageFunction: Function | string, ...args: Array<any>): Promise<(object | undefined)> {
|
async $$eval(selector: string, pageFunction: Function | string, ...args: Array<any>): Promise<object | undefined> {
|
||||||
const arrayHandle = await this._frame.evaluateHandle(
|
const arrayHandle = await this._frame.evaluateHandle(
|
||||||
(element, selector) => Array.from(element.querySelectorAll(selector)),
|
(root: SelectorRoot, selector: string, injected: Injected) => injected.querySelectorAll('css=' + selector, root),
|
||||||
this, selector
|
this, selector, await this._context._injected()
|
||||||
);
|
);
|
||||||
|
|
||||||
const result = await this._frame.evaluate(pageFunction, arrayHandle, ...args);
|
const result = await this._frame.evaluate(pageFunction, arrayHandle, ...args);
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
// Copyright (c) Microsoft Corporation.
|
// Copyright (c) Microsoft Corporation.
|
||||||
// Licensed under the MIT license.
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
import { SelectorEngine } from './selectorEngine';
|
import { SelectorEngine, SelectorRoot } from './selectorEngine';
|
||||||
import { Utils } from './utils';
|
import { Utils } from './utils';
|
||||||
|
|
||||||
type ParsedSelector = { engine: SelectorEngine, selector: string }[];
|
type ParsedSelector = { engine: SelectorEngine, selector: string }[];
|
||||||
@ -17,25 +17,25 @@ export class Injected {
|
|||||||
this.engines.set(engine.name, engine);
|
this.engines.set(engine.name, engine);
|
||||||
}
|
}
|
||||||
|
|
||||||
querySelector(selector: string, root: Element): Element | undefined {
|
querySelector(selector: string, root: SelectorRoot): Element | undefined {
|
||||||
const parsed = this._parseSelector(selector);
|
const parsed = this._parseSelector(selector);
|
||||||
let element = root;
|
let element = root;
|
||||||
for (const { engine, selector } of parsed) {
|
for (const { engine, selector } of parsed) {
|
||||||
const next = engine.query(element.shadowRoot || element, selector);
|
const next = engine.query((element as Element).shadowRoot || element, selector);
|
||||||
if (!next)
|
if (!next)
|
||||||
return;
|
return;
|
||||||
element = next;
|
element = next;
|
||||||
}
|
}
|
||||||
return element;
|
return element as Element;
|
||||||
}
|
}
|
||||||
|
|
||||||
querySelectorAll(selector: string, root: Element): Element[] {
|
querySelectorAll(selector: string, root: SelectorRoot): Element[] {
|
||||||
const parsed = this._parseSelector(selector);
|
const parsed = this._parseSelector(selector);
|
||||||
let set = new Set<Element>([ root ]);
|
let set = new Set<SelectorRoot>([ root ]);
|
||||||
for (const { engine, selector } of parsed) {
|
for (const { engine, selector } of parsed) {
|
||||||
const newSet = new Set<Element>();
|
const newSet = new Set<Element>();
|
||||||
for (const prev of set) {
|
for (const prev of set) {
|
||||||
for (const next of engine.queryAll(prev.shadowRoot || prev, selector)) {
|
for (const next of engine.queryAll((prev as Element).shadowRoot || prev, selector)) {
|
||||||
if (newSet.has(next))
|
if (newSet.has(next))
|
||||||
continue;
|
continue;
|
||||||
newSet.add(next);
|
newSet.add(next);
|
||||||
@ -43,7 +43,7 @@ export class Injected {
|
|||||||
}
|
}
|
||||||
set = newSet;
|
set = newSet;
|
||||||
}
|
}
|
||||||
return Array.from(set);
|
return Array.from(set) as Element[];
|
||||||
}
|
}
|
||||||
|
|
||||||
private _parseSelector(selector: string): ParsedSelector {
|
private _parseSelector(selector: string): ParsedSelector {
|
||||||
|
|||||||
@ -25,6 +25,8 @@ import { Protocol } from './protocol';
|
|||||||
import { releaseObject, valueFromRemoteObject } from './protocolHelper';
|
import { releaseObject, valueFromRemoteObject } from './protocolHelper';
|
||||||
import Injected from '../injected/injected';
|
import Injected from '../injected/injected';
|
||||||
|
|
||||||
|
type SelectorRoot = Element | ShadowRoot | Document;
|
||||||
|
|
||||||
const writeFileAsync = helper.promisify(fs.writeFile);
|
const writeFileAsync = helper.promisify(fs.writeFile);
|
||||||
|
|
||||||
export function createJSHandle(context: ExecutionContext, remoteObject: Protocol.Runtime.RemoteObject) {
|
export function createJSHandle(context: ExecutionContext, remoteObject: Protocol.Runtime.RemoteObject) {
|
||||||
@ -52,7 +54,7 @@ export class JSHandle {
|
|||||||
return this._context;
|
return this._context;
|
||||||
}
|
}
|
||||||
|
|
||||||
async evaluate(pageFunction: Function | string, ...args: any[]): Promise<(any)> {
|
async evaluate(pageFunction: Function | string, ...args: any[]): Promise<any> {
|
||||||
return await this.executionContext().evaluate(pageFunction, this, ...args);
|
return await this.executionContext().evaluate(pageFunction, this, ...args);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -310,9 +312,7 @@ export class ElementHandle extends JSHandle {
|
|||||||
|
|
||||||
async $(selector: string): Promise<ElementHandle | null> {
|
async $(selector: string): Promise<ElementHandle | null> {
|
||||||
const handle = await this.evaluateHandle(
|
const handle = await this.evaluateHandle(
|
||||||
(element, selector, injected: Injected) => {
|
(root: SelectorRoot, selector: string, injected: Injected) => injected.querySelector('css=' + selector, root),
|
||||||
return injected.querySelector('css=' + selector, element);
|
|
||||||
},
|
|
||||||
selector, await this._context._injected()
|
selector, await this._context._injected()
|
||||||
);
|
);
|
||||||
const element = handle.asElement();
|
const element = handle.asElement();
|
||||||
@ -338,7 +338,7 @@ export class ElementHandle extends JSHandle {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
async $eval(selector: string, pageFunction: Function | string, ...args: any[]): Promise<(object | undefined)> {
|
async $eval(selector: string, pageFunction: Function | string, ...args: any[]): Promise<object | undefined> {
|
||||||
const elementHandle = await this.$(selector);
|
const elementHandle = await this.$(selector);
|
||||||
if (!elementHandle)
|
if (!elementHandle)
|
||||||
throw new Error(`Error: failed to find element matching selector "${selector}"`);
|
throw new Error(`Error: failed to find element matching selector "${selector}"`);
|
||||||
@ -347,10 +347,10 @@ export class ElementHandle extends JSHandle {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
async $$eval(selector: string, pageFunction: Function | string, ...args: any[]): Promise<(object | undefined)> {
|
async $$eval(selector: string, pageFunction: Function | string, ...args: any[]): Promise<object | undefined> {
|
||||||
const arrayHandle = await this.evaluateHandle(
|
const arrayHandle = await this.evaluateHandle(
|
||||||
(element, selector) => Array.from(element.querySelectorAll(selector)),
|
(root: SelectorRoot, selector: string, injected: Injected) => injected.querySelectorAll('css=' + selector, root),
|
||||||
selector
|
selector, await this._context._injected()
|
||||||
);
|
);
|
||||||
|
|
||||||
const result = await arrayHandle.evaluate(pageFunction, ...args);
|
const result = await arrayHandle.evaluate(pageFunction, ...args);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user