chore: use bindIsolatedHandle (#10810)

This commit is contained in:
jrandolf 2023-09-01 17:13:29 +02:00 committed by GitHub
parent f3c7499e67
commit 690b1c2218
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 191 additions and 114 deletions

View File

@ -1,17 +0,0 @@
---
sidebar_label: ElementHandle.asElement
---
# ElementHandle.asElement() method
#### Signature:
```typescript
class ElementHandle {
asElement(): ElementHandle<ElementType>;
}
```
**Returns:**
[ElementHandle](./puppeteer.elementhandle.md)&lt;ElementType&gt;

View File

@ -54,7 +54,6 @@ The constructor for this class is marked as internal. Third-party code should no
| [$$eval(selector, pageFunction, args)](./puppeteer.elementhandle.__eval.md) | | <p>Runs the given function on an array of elements matching the given selector in the current element.</p><p>If the given function returns a promise, then this method will wait till the promise resolves.</p> | | [$$eval(selector, pageFunction, args)](./puppeteer.elementhandle.__eval.md) | | <p>Runs the given function on an array of elements matching the given selector in the current element.</p><p>If the given function returns a promise, then this method will wait till the promise resolves.</p> |
| [$eval(selector, pageFunction, args)](./puppeteer.elementhandle._eval.md) | | <p>Runs the given function on the first element matching the given selector in the current element.</p><p>If the given function returns a promise, then this method will wait till the promise resolves.</p> | | [$eval(selector, pageFunction, args)](./puppeteer.elementhandle._eval.md) | | <p>Runs the given function on the first element matching the given selector in the current element.</p><p>If the given function returns a promise, then this method will wait till the promise resolves.</p> |
| [$x(expression)](./puppeteer.elementhandle._x.md) | | | | [$x(expression)](./puppeteer.elementhandle._x.md) | | |
| [asElement()](./puppeteer.elementhandle.aselement.md) | | |
| [autofill(data)](./puppeteer.elementhandle.autofill.md) | | If the element is a form input, you can use [ElementHandle.autofill()](./puppeteer.elementhandle.autofill.md) to test if the form is compatible with the browser's autofill implementation. Throws an error if the form cannot be autofilled. | | [autofill(data)](./puppeteer.elementhandle.autofill.md) | | If the element is a form input, you can use [ElementHandle.autofill()](./puppeteer.elementhandle.autofill.md) to test if the form is compatible with the browser's autofill implementation. Throws an error if the form cannot be autofilled. |
| [boundingBox()](./puppeteer.elementhandle.boundingbox.md) | | This method returns the bounding box of the element (relative to the main frame), or <code>null</code> if the element is not visible. | | [boundingBox()](./puppeteer.elementhandle.boundingbox.md) | | This method returns the bounding box of the element (relative to the main frame), or <code>null</code> if the element is not visible. |
| [boxModel()](./puppeteer.elementhandle.boxmodel.md) | | This method returns boxes of the element, or <code>null</code> if the element is not visible. | | [boxModel()](./puppeteer.elementhandle.boxmodel.md) | | This method returns boxes of the element, or <code>null</code> if the element is not visible. |

View File

@ -139,6 +139,58 @@ export interface Point {
export abstract class ElementHandle< export abstract class ElementHandle<
ElementType extends Node = Element, ElementType extends Node = Element,
> extends JSHandle<ElementType> { > extends JSHandle<ElementType> {
/**
* A given method will have it's `this` replaced with an isolated version of
* `this` when decorated with this decorator.
*
* All changes of isolated `this` are reflected on the actual `this`.
*
* @internal
*/
static bindIsolatedHandle<This extends ElementHandle<Node>>(
target: (this: This, ...args: any[]) => Promise<any>,
_: unknown
): typeof target {
return async function (...args) {
// If the handle is already isolated, then we don't need to adopt it
// again.
if (this.realm === this.frame.isolatedRealm()) {
return await target.call(this, ...args);
}
using adoptedThis = await this.frame.isolatedRealm().adoptHandle(this);
const result = await target.call(adoptedThis, ...args);
// If the function returns `adoptedThis`, then we return `this`.
if (result === adoptedThis) {
return this;
}
// If the function returns a handle, transfer it into the current realm.
if (result instanceof JSHandle) {
return await this.realm.transferHandle(result);
}
// If the function returns an array of handlers, transfer them into the
// current realm.
if (Array.isArray(result)) {
await Promise.all(
result.map(async (item, index, result) => {
if (item instanceof JSHandle) {
result[index] = await this.realm.transferHandle(item);
}
})
);
}
if (result instanceof Map) {
await Promise.all(
[...result.entries()].map(async ([key, value]) => {
if (value instanceof JSHandle) {
result.set(key, await this.realm.transferHandle(value));
}
})
);
}
return result;
};
}
/** /**
* @internal * @internal
*/ */
@ -176,6 +228,10 @@ export abstract class ElementHandle<
* @internal * @internal
*/ */
override async getProperty(propertyName: string): Promise<JSHandle<unknown>>; override async getProperty(propertyName: string): Promise<JSHandle<unknown>>;
/**
* @internal
*/
@ElementHandle.bindIsolatedHandle
override async getProperty<K extends keyof ElementType>( override async getProperty<K extends keyof ElementType>(
propertyName: HandleOr<K> propertyName: HandleOr<K>
): Promise<HandleFor<ElementType[K]>> { ): Promise<HandleFor<ElementType[K]>> {
@ -185,6 +241,7 @@ export abstract class ElementHandle<
/** /**
* @internal * @internal
*/ */
@ElementHandle.bindIsolatedHandle
override async getProperties(): Promise<Map<string, JSHandle>> { override async getProperties(): Promise<Map<string, JSHandle>> {
return await this.handle.getProperties(); return await this.handle.getProperties();
} }
@ -224,6 +281,7 @@ export abstract class ElementHandle<
/** /**
* @internal * @internal
*/ */
@ElementHandle.bindIsolatedHandle
override async jsonValue(): Promise<ElementType> { override async jsonValue(): Promise<ElementType> {
return await this.handle.jsonValue(); return await this.handle.jsonValue();
} }
@ -249,6 +307,9 @@ export abstract class ElementHandle<
return this.handle.dispose(); return this.handle.dispose();
} }
/**
* @internal
*/
override asElement(): ElementHandle<ElementType> { override asElement(): ElementHandle<ElementType> {
return this; return this;
} }
@ -265,6 +326,7 @@ export abstract class ElementHandle<
* @returns A {@link ElementHandle | element handle} to the first element * @returns A {@link ElementHandle | element handle} to the first element
* matching the given selector. Otherwise, `null`. * matching the given selector. Otherwise, `null`.
*/ */
@ElementHandle.bindIsolatedHandle
async $<Selector extends string>( async $<Selector extends string>(
selector: Selector selector: Selector
): Promise<ElementHandle<NodeFor<Selector>> | null> { ): Promise<ElementHandle<NodeFor<Selector>> | null> {
@ -283,6 +345,7 @@ export abstract class ElementHandle<
* @returns An array of {@link ElementHandle | element handles} that point to * @returns An array of {@link ElementHandle | element handles} that point to
* elements matching the given selector. * elements matching the given selector.
*/ */
@ElementHandle.bindIsolatedHandle
async $$<Selector extends string>( async $$<Selector extends string>(
selector: Selector selector: Selector
): Promise<Array<ElementHandle<NodeFor<Selector>>>> { ): Promise<Array<ElementHandle<NodeFor<Selector>>>> {
@ -415,6 +478,7 @@ export abstract class ElementHandle<
* If there are no such elements, the method will resolve to an empty array. * If there are no such elements, the method will resolve to an empty array.
* @param expression - Expression to {@link https://developer.mozilla.org/en-US/docs/Web/API/Document/evaluate | evaluate} * @param expression - Expression to {@link https://developer.mozilla.org/en-US/docs/Web/API/Document/evaluate | evaluate}
*/ */
@ElementHandle.bindIsolatedHandle
async $x(expression: string): Promise<Array<ElementHandle<Node>>> { async $x(expression: string): Promise<Array<ElementHandle<Node>>> {
if (expression.startsWith('//')) { if (expression.startsWith('//')) {
expression = `.${expression}`; expression = `.${expression}`;
@ -459,6 +523,7 @@ export abstract class ElementHandle<
* @returns An element matching the given selector. * @returns An element matching the given selector.
* @throws Throws if an element matching the given selector doesn't appear. * @throws Throws if an element matching the given selector doesn't appear.
*/ */
@ElementHandle.bindIsolatedHandle
async waitForSelector<Selector extends string>( async waitForSelector<Selector extends string>(
selector: Selector, selector: Selector,
options: WaitForSelectorOptions = {} options: WaitForSelectorOptions = {}
@ -473,15 +538,13 @@ export abstract class ElementHandle<
} }
async #checkVisibility(visibility: boolean): Promise<boolean> { async #checkVisibility(visibility: boolean): Promise<boolean> {
using element = await this.frame.isolatedRealm().adoptHandle(this); return await this.evaluate(
return await this.frame.isolatedRealm().evaluate( async (element, PuppeteerUtil, visibility) => {
async (PuppeteerUtil, element, visibility) => {
return Boolean(PuppeteerUtil.checkVisibility(element, visibility)); return Boolean(PuppeteerUtil.checkVisibility(element, visibility));
}, },
LazyArg.create(context => { LazyArg.create(context => {
return context.puppeteerUtil; return context.puppeteerUtil;
}), }),
element,
visibility visibility
); );
} }
@ -490,6 +553,7 @@ export abstract class ElementHandle<
* Checks if an element is visible using the same mechanism as * Checks if an element is visible using the same mechanism as
* {@link ElementHandle.waitForSelector}. * {@link ElementHandle.waitForSelector}.
*/ */
@ElementHandle.bindIsolatedHandle
async isVisible(): Promise<boolean> { async isVisible(): Promise<boolean> {
return await this.#checkVisibility(true); return await this.#checkVisibility(true);
} }
@ -498,6 +562,7 @@ export abstract class ElementHandle<
* Checks if an element is hidden using the same mechanism as * Checks if an element is hidden using the same mechanism as
* {@link ElementHandle.waitForSelector}. * {@link ElementHandle.waitForSelector}.
*/ */
@ElementHandle.bindIsolatedHandle
async isHidden(): Promise<boolean> { async isHidden(): Promise<boolean> {
return await this.#checkVisibility(false); return await this.#checkVisibility(false);
} }
@ -564,6 +629,7 @@ export abstract class ElementHandle<
* default value can be changed by using the {@link Page.setDefaultTimeout} * default value can be changed by using the {@link Page.setDefaultTimeout}
* method. * method.
*/ */
@ElementHandle.bindIsolatedHandle
async waitForXPath( async waitForXPath(
xpath: string, xpath: string,
options: { options: {
@ -596,6 +662,7 @@ export abstract class ElementHandle<
* @throws An error if the handle does not match. **The handle will not be * @throws An error if the handle does not match. **The handle will not be
* automatically disposed.** * automatically disposed.**
*/ */
@ElementHandle.bindIsolatedHandle
async toElement< async toElement<
K extends keyof HTMLElementTagNameMap | keyof SVGElementTagNameMap, K extends keyof HTMLElementTagNameMap | keyof SVGElementTagNameMap,
>(tagName: K): Promise<HandleFor<ElementFor<K>>> { >(tagName: K): Promise<HandleFor<ElementFor<K>>> {
@ -618,9 +685,9 @@ export abstract class ElementHandle<
/** /**
* Returns the middle point within an element unless a specific offset is provided. * Returns the middle point within an element unless a specific offset is provided.
*/ */
@ElementHandle.bindIsolatedHandle
async clickablePoint(offset?: Offset): Promise<Point> { async clickablePoint(offset?: Offset): Promise<Point> {
using adoptedThis = await this.frame.isolatedRealm().adoptHandle(this); const box = await this.#clickableBox();
const box = await adoptedThis.#clickableBox();
if (!box) { if (!box) {
throw new Error('Node is either not clickable or not an Element'); throw new Error('Node is either not clickable or not an Element');
} }
@ -641,6 +708,7 @@ export abstract class ElementHandle<
* uses {@link Page} to hover over the center of the element. * uses {@link Page} to hover over the center of the element.
* If the element is detached from DOM, the method throws an error. * If the element is detached from DOM, the method throws an error.
*/ */
@ElementHandle.bindIsolatedHandle
async hover(this: ElementHandle<Element>): Promise<void> { async hover(this: ElementHandle<Element>): Promise<void> {
await this.scrollIntoViewIfNeeded(); await this.scrollIntoViewIfNeeded();
const {x, y} = await this.clickablePoint(); const {x, y} = await this.clickablePoint();
@ -652,6 +720,7 @@ export abstract class ElementHandle<
* uses {@link Page | Page.mouse} to click in the center of the element. * uses {@link Page | Page.mouse} to click in the center of the element.
* If the element is detached from DOM, the method throws an error. * If the element is detached from DOM, the method throws an error.
*/ */
@ElementHandle.bindIsolatedHandle
async click( async click(
this: ElementHandle<Element>, this: ElementHandle<Element>,
options: Readonly<ClickOptions> = {} options: Readonly<ClickOptions> = {}
@ -733,6 +802,7 @@ export abstract class ElementHandle<
* `multiple` attribute, all values are considered, otherwise only the first * `multiple` attribute, all values are considered, otherwise only the first
* one is taken into account. * one is taken into account.
*/ */
@ElementHandle.bindIsolatedHandle
async select(...values: string[]): Promise<string[]> { async select(...values: string[]): Promise<string[]> {
for (const value of values) { for (const value of values) {
assert( assert(
@ -801,6 +871,7 @@ export abstract class ElementHandle<
* {@link Touchscreen.tap} to tap in the center of the element. * {@link Touchscreen.tap} to tap in the center of the element.
* If the element is detached from DOM, the method throws an error. * If the element is detached from DOM, the method throws an error.
*/ */
@ElementHandle.bindIsolatedHandle
async tap(this: ElementHandle<Element>): Promise<void> { async tap(this: ElementHandle<Element>): Promise<void> {
await this.scrollIntoViewIfNeeded(); await this.scrollIntoViewIfNeeded();
const {x, y} = await this.clickablePoint(); const {x, y} = await this.clickablePoint();
@ -808,18 +879,21 @@ export abstract class ElementHandle<
await this.frame.page().touchscreen.touchEnd(); await this.frame.page().touchscreen.touchEnd();
} }
@ElementHandle.bindIsolatedHandle
async touchStart(this: ElementHandle<Element>): Promise<void> { async touchStart(this: ElementHandle<Element>): Promise<void> {
await this.scrollIntoViewIfNeeded(); await this.scrollIntoViewIfNeeded();
const {x, y} = await this.clickablePoint(); const {x, y} = await this.clickablePoint();
await this.frame.page().touchscreen.touchStart(x, y); await this.frame.page().touchscreen.touchStart(x, y);
} }
@ElementHandle.bindIsolatedHandle
async touchMove(this: ElementHandle<Element>): Promise<void> { async touchMove(this: ElementHandle<Element>): Promise<void> {
await this.scrollIntoViewIfNeeded(); await this.scrollIntoViewIfNeeded();
const {x, y} = await this.clickablePoint(); const {x, y} = await this.clickablePoint();
await this.frame.page().touchscreen.touchMove(x, y); await this.frame.page().touchscreen.touchMove(x, y);
} }
@ElementHandle.bindIsolatedHandle
async touchEnd(this: ElementHandle<Element>): Promise<void> { async touchEnd(this: ElementHandle<Element>): Promise<void> {
await this.scrollIntoViewIfNeeded(); await this.scrollIntoViewIfNeeded();
await this.frame.page().touchscreen.touchEnd(); await this.frame.page().touchscreen.touchEnd();
@ -828,6 +902,7 @@ export abstract class ElementHandle<
/** /**
* Calls {@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/focus | focus} on the element. * Calls {@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/focus | focus} on the element.
*/ */
@ElementHandle.bindIsolatedHandle
async focus(): Promise<void> { async focus(): Promise<void> {
await this.evaluate(element => { await this.evaluate(element => {
if (!(element instanceof HTMLElement)) { if (!(element instanceof HTMLElement)) {
@ -862,6 +937,7 @@ export abstract class ElementHandle<
* *
* @param options - Delay in milliseconds. Defaults to 0. * @param options - Delay in milliseconds. Defaults to 0.
*/ */
@ElementHandle.bindIsolatedHandle
async type( async type(
text: string, text: string,
options?: Readonly<KeyboardTypeOptions> options?: Readonly<KeyboardTypeOptions>
@ -884,6 +960,7 @@ export abstract class ElementHandle<
* @param key - Name of key to press, such as `ArrowLeft`. * @param key - Name of key to press, such as `ArrowLeft`.
* See {@link KeyInput} for a list of all key names. * See {@link KeyInput} for a list of all key names.
*/ */
@ElementHandle.bindIsolatedHandle
async press( async press(
key: KeyInput, key: KeyInput,
options?: Readonly<KeyPressOptions> options?: Readonly<KeyPressOptions>
@ -972,9 +1049,9 @@ export abstract class ElementHandle<
* This method returns the bounding box of the element (relative to the main frame), * This method returns the bounding box of the element (relative to the main frame),
* or `null` if the element is not visible. * or `null` if the element is not visible.
*/ */
@ElementHandle.bindIsolatedHandle
async boundingBox(): Promise<BoundingBox | null> { async boundingBox(): Promise<BoundingBox | null> {
using adoptedThis = await this.frame.isolatedRealm().adoptHandle(this); const box = await this.evaluate(element => {
const box = await adoptedThis.evaluate(element => {
if (!(element instanceof Element)) { if (!(element instanceof Element)) {
return null; return null;
} }
@ -988,7 +1065,7 @@ export abstract class ElementHandle<
if (!box) { if (!box) {
return null; return null;
} }
const offset = await adoptedThis.#getTopLeftCornerOfFrame(); const offset = await this.#getTopLeftCornerOfFrame();
if (!offset) { if (!offset) {
return null; return null;
} }
@ -1008,82 +1085,80 @@ export abstract class ElementHandle<
* Boxes are represented as an array of points; * Boxes are represented as an array of points;
* Each Point is an object `{x, y}`. Box points are sorted clock-wise. * Each Point is an object `{x, y}`. Box points are sorted clock-wise.
*/ */
@ElementHandle.bindIsolatedHandle
async boxModel(): Promise<BoxModel | null> { async boxModel(): Promise<BoxModel | null> {
const model = await (async () => { const model = await this.evaluate(element => {
using adoptedThis = await this.frame.isolatedRealm().adoptHandle(this); if (!(element instanceof Element)) {
return await adoptedThis.evaluate(element => { return null;
if (!(element instanceof Element)) { }
return null; // Element is not visible.
} if (element.getClientRects().length === 0) {
// Element is not visible. return null;
if (element.getClientRects().length === 0) { }
return null; const rect = element.getBoundingClientRect();
} const style = window.getComputedStyle(element);
const rect = element.getBoundingClientRect(); const offsets = {
const style = window.getComputedStyle(element); padding: {
const offsets = { left: parseInt(style.paddingLeft, 10),
padding: { top: parseInt(style.paddingTop, 10),
left: parseInt(style.paddingLeft, 10), right: parseInt(style.paddingRight, 10),
top: parseInt(style.paddingTop, 10), bottom: parseInt(style.paddingBottom, 10),
right: parseInt(style.paddingRight, 10), },
bottom: parseInt(style.paddingBottom, 10), margin: {
}, left: -parseInt(style.marginLeft, 10),
margin: { top: -parseInt(style.marginTop, 10),
left: -parseInt(style.marginLeft, 10), right: -parseInt(style.marginRight, 10),
top: -parseInt(style.marginTop, 10), bottom: -parseInt(style.marginBottom, 10),
right: -parseInt(style.marginRight, 10), },
bottom: -parseInt(style.marginBottom, 10), border: {
}, left: parseInt(style.borderLeft, 10),
border: { top: parseInt(style.borderTop, 10),
left: parseInt(style.borderLeft, 10), right: parseInt(style.borderRight, 10),
top: parseInt(style.borderTop, 10), bottom: parseInt(style.borderBottom, 10),
right: parseInt(style.borderRight, 10), },
bottom: parseInt(style.borderBottom, 10), };
}, const border: Quad = [
}; {x: rect.left, y: rect.top},
const border: Quad = [ {x: rect.left + rect.width, y: rect.top},
{x: rect.left, y: rect.top}, {x: rect.left + rect.width, y: rect.top + rect.bottom},
{x: rect.left + rect.width, y: rect.top}, {x: rect.left, y: rect.top + rect.bottom},
{x: rect.left + rect.width, y: rect.top + rect.bottom}, ];
{x: rect.left, y: rect.top + rect.bottom}, const padding = transformQuadWithOffsets(border, offsets.border);
]; const content = transformQuadWithOffsets(padding, offsets.padding);
const padding = transformQuadWithOffsets(border, offsets.border); const margin = transformQuadWithOffsets(border, offsets.margin);
const content = transformQuadWithOffsets(padding, offsets.padding); return {
const margin = transformQuadWithOffsets(border, offsets.margin); content,
return { padding,
content, border,
padding, margin,
border, width: rect.width,
margin, height: rect.height,
width: rect.width, };
height: rect.height,
};
function transformQuadWithOffsets( function transformQuadWithOffsets(
quad: Quad, quad: Quad,
offsets: {top: number; left: number; right: number; bottom: number} offsets: {top: number; left: number; right: number; bottom: number}
): Quad { ): Quad {
return [ return [
{ {
x: quad[0].x + offsets.left, x: quad[0].x + offsets.left,
y: quad[0].y + offsets.top, y: quad[0].y + offsets.top,
}, },
{ {
x: quad[1].x - offsets.right, x: quad[1].x - offsets.right,
y: quad[1].y + offsets.top, y: quad[1].y + offsets.top,
}, },
{ {
x: quad[2].x - offsets.right, x: quad[2].x - offsets.right,
y: quad[2].y - offsets.bottom, y: quad[2].y - offsets.bottom,
}, },
{ {
x: quad[3].x + offsets.left, x: quad[3].x + offsets.left,
y: quad[3].y - offsets.bottom, y: quad[3].y - offsets.bottom,
}, },
]; ];
} }
}); });
})();
if (!model) { if (!model) {
return null; return null;
} }
@ -1198,6 +1273,7 @@ export abstract class ElementHandle<
* @param options - Threshold for the intersection between 0 (no intersection) and 1 * @param options - Threshold for the intersection between 0 (no intersection) and 1
* (full intersection). Defaults to 1. * (full intersection). Defaults to 1.
*/ */
@ElementHandle.bindIsolatedHandle
async isIntersectingViewport( async isIntersectingViewport(
this: ElementHandle<Element>, this: ElementHandle<Element>,
options: { options: {
@ -1227,10 +1303,10 @@ export abstract class ElementHandle<
* Scrolls the element into view using either the automation protocol client * Scrolls the element into view using either the automation protocol client
* or by calling element.scrollIntoView. * or by calling element.scrollIntoView.
*/ */
@ElementHandle.bindIsolatedHandle
async scrollIntoView(this: ElementHandle<Element>): Promise<void> { async scrollIntoView(this: ElementHandle<Element>): Promise<void> {
using adoptedThis = await this.frame.isolatedRealm().adoptHandle(this); await this.assertConnectedElement();
await adoptedThis.assertConnectedElement(); await this.evaluate(async (element): Promise<void> => {
await adoptedThis.evaluate(async (element): Promise<void> => {
element.scrollIntoView({ element.scrollIntoView({
block: 'center', block: 'center',
inline: 'center', inline: 'center',

View File

@ -87,6 +87,7 @@ export class CDPElementHandle<
} }
@throwIfDisposed() @throwIfDisposed()
@ElementHandle.bindIsolatedHandle
override async scrollIntoView( override async scrollIntoView(
this: CDPElementHandle<Element> this: CDPElementHandle<Element>
): Promise<void> { ): Promise<void> {
@ -106,6 +107,7 @@ export class CDPElementHandle<
* This method creates and captures a dragevent from the element. * This method creates and captures a dragevent from the element.
*/ */
@throwIfDisposed() @throwIfDisposed()
@ElementHandle.bindIsolatedHandle
override async drag( override async drag(
this: CDPElementHandle<Element>, this: CDPElementHandle<Element>,
target: Point target: Point
@ -120,6 +122,7 @@ export class CDPElementHandle<
} }
@throwIfDisposed() @throwIfDisposed()
@ElementHandle.bindIsolatedHandle
override async dragEnter( override async dragEnter(
this: CDPElementHandle<Element>, this: CDPElementHandle<Element>,
data: Protocol.Input.DragData = {items: [], dragOperationsMask: 1} data: Protocol.Input.DragData = {items: [], dragOperationsMask: 1}
@ -130,6 +133,7 @@ export class CDPElementHandle<
} }
@throwIfDisposed() @throwIfDisposed()
@ElementHandle.bindIsolatedHandle
override async dragOver( override async dragOver(
this: CDPElementHandle<Element>, this: CDPElementHandle<Element>,
data: Protocol.Input.DragData = {items: [], dragOperationsMask: 1} data: Protocol.Input.DragData = {items: [], dragOperationsMask: 1}
@ -140,6 +144,7 @@ export class CDPElementHandle<
} }
@throwIfDisposed() @throwIfDisposed()
@ElementHandle.bindIsolatedHandle
override async drop( override async drop(
this: CDPElementHandle<Element>, this: CDPElementHandle<Element>,
data: Protocol.Input.DragData = {items: [], dragOperationsMask: 1} data: Protocol.Input.DragData = {items: [], dragOperationsMask: 1}
@ -150,6 +155,7 @@ export class CDPElementHandle<
} }
@throwIfDisposed() @throwIfDisposed()
@ElementHandle.bindIsolatedHandle
override async dragAndDrop( override async dragAndDrop(
this: CDPElementHandle<Element>, this: CDPElementHandle<Element>,
target: CDPElementHandle<Node>, target: CDPElementHandle<Node>,
@ -166,6 +172,7 @@ export class CDPElementHandle<
} }
@throwIfDisposed() @throwIfDisposed()
@ElementHandle.bindIsolatedHandle
override async uploadFile( override async uploadFile(
this: CDPElementHandle<HTMLInputElement>, this: CDPElementHandle<HTMLInputElement>,
...filePaths: string[] ...filePaths: string[]
@ -224,6 +231,7 @@ export class CDPElementHandle<
} }
@throwIfDisposed() @throwIfDisposed()
@ElementHandle.bindIsolatedHandle
override async screenshot( override async screenshot(
this: CDPElementHandle<Element>, this: CDPElementHandle<Element>,
options: ScreenshotOptions = {} options: ScreenshotOptions = {}

View File

@ -328,6 +328,10 @@ export class IsolatedWorld extends Realm {
if (handle.realm === this) { if (handle.realm === this) {
return handle; return handle;
} }
// Implies it's a primitive value, probably.
if (handle.remoteObject().objectId === undefined) {
return handle;
}
const info = await this.client.send('DOM.describeNode', { const info = await this.client.send('DOM.describeNode', {
objectId: handle.remoteObject().objectId, objectId: handle.remoteObject().objectId,
}); });

View File

@ -55,11 +55,6 @@ export class BidiElementHandle<
return this.handle.remoteValue(); return this.handle.remoteValue();
} }
assertElementHasWorld(): asserts this {
// TODO: Should assert element has a Sandbox
return;
}
override async autofill(data: AutofillData): Promise<void> { override async autofill(data: AutofillData): Promise<void> {
const client = this.frame.client; const client = this.frame.client;
const nodeInfo = await client.send('DOM.describeNode', { const nodeInfo = await client.send('DOM.describeNode', {
@ -77,9 +72,9 @@ export class BidiElementHandle<
override async contentFrame( override async contentFrame(
this: BidiElementHandle<HTMLIFrameElement> this: BidiElementHandle<HTMLIFrameElement>
): Promise<BidiFrame>; ): Promise<BidiFrame>;
@ElementHandle.bindIsolatedHandle
override async contentFrame(): Promise<BidiFrame | null> { override async contentFrame(): Promise<BidiFrame | null> {
using adoptedThis = await this.frame.isolatedRealm().adoptHandle(this); using handle = (await this.evaluateHandle(element => {
using handle = (await adoptedThis.evaluateHandle(element => {
if (element instanceof HTMLIFrameElement) { if (element instanceof HTMLIFrameElement) {
return element.contentWindow; return element.contentWindow;
} }

View File

@ -114,7 +114,6 @@ export class Sandbox extends Realm {
const transferredHandle = await this.evaluateHandle(node => { const transferredHandle = await this.evaluateHandle(node => {
return node; return node;
}, handle); }, handle);
await handle.dispose(); await handle.dispose();
return transferredHandle as unknown as T; return transferredHandle as unknown as T;
} }

View File

@ -156,7 +156,8 @@ export class BidiSerializer {
: null; : null;
if (objectHandle) { if (objectHandle) {
if ( if (
objectHandle.realm !== sandbox && objectHandle.realm.environment.context() !==
sandbox.environment.context() &&
!('sharedId' in objectHandle.remoteValue()) !('sharedId' in objectHandle.remoteValue())
) { ) {
throw new Error( throw new Error(

View File

@ -2111,6 +2111,18 @@
"parameters": ["firefox", "webDriverBiDi"], "parameters": ["firefox", "webDriverBiDi"],
"expectations": ["FAIL"] "expectations": ["FAIL"]
}, },
{
"testIdPattern": "[elementhandle.spec] ElementHandle specs ElementHandle.isIntersectingViewport should work",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["cdp", "firefox"],
"expectations": ["FAIL"]
},
{
"testIdPattern": "[elementhandle.spec] ElementHandle specs ElementHandle.isIntersectingViewport should work with svg elements",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["cdp", "firefox"],
"expectations": ["FAIL"]
},
{ {
"testIdPattern": "[emulation.spec] Emulation Page.emulate should support clicking", "testIdPattern": "[emulation.spec] Emulation Page.emulate should support clicking",
"platforms": ["darwin", "linux", "win32"], "platforms": ["darwin", "linux", "win32"],

View File

@ -208,7 +208,7 @@ describe('AriaQueryHandler', () => {
}); });
describe('queryAllArray', () => { describe('queryAllArray', () => {
it('$$eval should handle many elements', async function () { it('$$eval should handle many elements', async function () {
this.timeout(25_000); this.timeout(40_000);
const {page} = await getTestState(); const {page} = await getTestState();
await page.setContent(''); await page.setContent('');