Skip to content

Commit

Permalink
chore: move adoption to DOMWorld (#8760)
Browse files Browse the repository at this point in the history
  • Loading branch information
jrandolf committed Aug 9, 2022
1 parent 6934b94 commit 932a053
Show file tree
Hide file tree
Showing 6 changed files with 54 additions and 48 deletions.
11 changes: 7 additions & 4 deletions src/common/AriaQueryHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,9 @@ const queryOne = async (
if (!res[0] || !res[0].backendDOMNodeId) {
return null;
}
return exeCtx._adoptBackendNodeId(res[0].backendDOMNodeId);
return (await exeCtx._world!.adoptBackendNode(
res[0].backendDOMNodeId
)) as ElementHandle<Node>;
};

const waitFor = async (
Expand Down Expand Up @@ -132,11 +134,12 @@ const queryAll = async (
const exeCtx = element.executionContext();
const {name, role} = parseAriaSelector(selector);
const res = await queryAXTree(exeCtx._client, element, name, role);
return Promise.all(
const world = exeCtx._world!;
return (await Promise.all(
res.map(axNode => {
return exeCtx._adoptBackendNodeId(axNode.backendDOMNodeId);
return world.adoptBackendNode(axNode.backendDOMNodeId);
})
);
)) as Array<ElementHandle<Node>>;
};

const queryAllArray = async (
Expand Down
24 changes: 24 additions & 0 deletions src/common/DOMWorld.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import {TimeoutSettings} from './TimeoutSettings.js';
import {EvaluateFunc, HandleFor, NodeFor} from './types.js';
import {
createDeferredPromise,
createJSHandle,
debugError,
DeferredPromise,
importFS,
Expand Down Expand Up @@ -739,6 +740,29 @@ export class DOMWorld {
return document.title;
});
}

async adoptBackendNode(
backendNodeId?: Protocol.DOM.BackendNodeId
): Promise<JSHandle<Node>> {
const executionContext = await this.executionContext();
const {object} = await this.#client.send('DOM.resolveNode', {
backendNodeId: backendNodeId,
executionContextId: executionContext._contextId,
});
return createJSHandle(executionContext, object) as JSHandle<Node>;
}

async adoptHandle<T extends JSHandle<Node>>(handle: T): Promise<T> {
const executionContext = await this.executionContext();
assert(
handle.executionContext() !== executionContext,
'Cannot adopt handle that already belongs to this execution context'
);
const nodeInfo = await this.#client.send('DOM.describeNode', {
objectId: handle._remoteObject.objectId,
});
return (await this.adoptBackendNode(nodeInfo.node.backendNodeId)) as T;
}
}

/**
Expand Down
8 changes: 4 additions & 4 deletions src/common/ElementHandle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,8 +123,7 @@ export class ElementHandle<
): Promise<ElementHandle<NodeFor<Selector>> | null> {
const frame = this._context.frame();
assert(frame);
const secondaryContext = await frame._secondaryWorld.executionContext();
const adoptedRoot = await secondaryContext._adoptElementHandle(this);
const adoptedRoot = await frame._secondaryWorld.adoptHandle(this);
const handle = await frame._secondaryWorld.waitForSelector(selector, {
...options,
root: adoptedRoot,
Expand All @@ -133,8 +132,9 @@ export class ElementHandle<
if (!handle) {
return null;
}
const mainExecutionContext = await frame._mainWorld.executionContext();
const result = await mainExecutionContext._adoptElementHandle(handle);
const result = (await frame._mainWorld.adoptHandle(
handle
)) as ElementHandle<NodeFor<Selector>>;
await handle.dispose();
return result;
}
Expand Down
40 changes: 10 additions & 30 deletions src/common/ExecutionContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,19 +38,16 @@ const SOURCE_URL_REGEX = /^[\040\t]*\/\/[@#] sourceURL=\s*(\S*?)\s*$/m;
/**
* This class represents a context for JavaScript execution. A [Page] might have
* many execution contexts:
* - each
* {@link https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe |
* frame } has "default" execution context that is always created after frame is
* - each {@link
* https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe | frame}
* has "default" execution context that is always created after frame is
* attached to DOM. This context is returned by the
* {@link Frame.executionContext} method.
* - {@link https://developer.chrome.com/extensions | Extension}'s content scripts
* create additional execution contexts.
* - {@link https://developer.chrome.com/extensions | Extension}'s content
* scripts create additional execution contexts.
*
* Besides pages, execution contexts can be found in
* {@link https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API |
* workers }.
*
* @public
* Besides pages, execution contexts can be found in {@link
* https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API | workers}.
*/
export class ExecutionContext {
/**
Expand Down Expand Up @@ -87,9 +84,9 @@ export class ExecutionContext {
/**
* @remarks
*
* Not every execution context is associated with a frame. For
* example, workers and extensions have execution contexts that are not
* associated with frames.
* Not every execution context is associated with a frame. For example,
* workers and extensions have execution contexts that are not associated with
* frames.
*
* @returns The frame associated with this execution context.
*/
Expand Down Expand Up @@ -422,21 +419,4 @@ export class ExecutionContext {
});
return createJSHandle(this, object) as ElementHandle<Node>;
}

/**
* @internal
*/
async _adoptElementHandle<T extends ElementHandle<Node>>(
elementHandle: T
): Promise<T> {
assert(
elementHandle.executionContext() !== this,
'Cannot adopt handle that already belongs to this execution context'
);
assert(this._world, 'Cannot adopt handle without DOMWorld');
const nodeInfo = await this._client.send('DOM.describeNode', {
objectId: elementHandle._remoteObject.objectId,
});
return (await this._adoptBackendNodeId(nodeInfo.node.backendNodeId)) as T;
}
}
7 changes: 4 additions & 3 deletions src/common/FrameManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1369,10 +1369,11 @@ export class Frame {
if (!handle) {
return null;
}
const mainExecutionContext = await this._mainWorld.executionContext();
const result = await mainExecutionContext._adoptElementHandle(handle);
const mainHandle = (await this._mainWorld.adoptHandle(
handle
)) as ElementHandle<NodeFor<Selector>>;
await handle.dispose();
return result;
return mainHandle;
}

/**
Expand Down
12 changes: 5 additions & 7 deletions src/common/Page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -644,15 +644,13 @@ export class Page extends EventEmitter {
}
const frame = this.#frameManager.frame(event.frameId);
assert(frame);
const context = await frame.executionContext();
const element = await context._adoptBackendNodeId(event.backendNodeId);
// This is guaranteed to be an HTMLInputElement handle by the event.
const handle = (await frame._mainWorld.adoptBackendNode(
event.backendNodeId
)) as ElementHandle<HTMLInputElement>;
const interceptors = Array.from(this.#fileChooserInterceptors);
this.#fileChooserInterceptors.clear();
const fileChooser = new FileChooser(
// This is guaranteed by the event.
element as ElementHandle<HTMLInputElement>,
event
);
const fileChooser = new FileChooser(handle, event);
for (const interceptor of interceptors) {
interceptor.call(undefined, fileChooser);
}
Expand Down

0 comments on commit 932a053

Please sign in to comment.