chore: implement removeExposedFunction (#11984)

This commit is contained in:
jrandolf 2024-02-26 11:42:51 +01:00 committed by GitHub
parent 9ddfa5314c
commit 627bd00399
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 43 additions and 30 deletions

View File

@ -29,6 +29,17 @@ type CallbackChannel<Args, Ret> = (
* @internal * @internal
*/ */
export class ExposeableFunction<Args extends unknown[], Ret> { export class ExposeableFunction<Args extends unknown[], Ret> {
static async from<Args extends unknown[], Ret>(
frame: BidiFrame,
name: string,
apply: (...args: Args) => Awaitable<Ret>,
isolate = false
): Promise<ExposeableFunction<Args, Ret>> {
const func = new ExposeableFunction(frame, name, apply, isolate);
await func.#initialize();
return func;
}
readonly #frame; readonly #frame;
readonly name; readonly name;
@ -54,7 +65,7 @@ export class ExposeableFunction<Args extends unknown[], Ret> {
this.#channel = `__puppeteer__${this.#frame._id}_page_exposeFunction_${this.name}`; this.#channel = `__puppeteer__${this.#frame._id}_page_exposeFunction_${this.name}`;
} }
async expose(): Promise<void> { async #initialize() {
const connection = this.#connection; const connection = this.#connection;
const channel = { const channel = {
type: 'channel' as const, type: 'channel' as const,
@ -98,23 +109,16 @@ export class ExposeableFunction<Args extends unknown[], Ret> {
frames.map(async frame => { frames.map(async frame => {
const realm = this.#isolate ? frame.isolatedRealm() : frame.mainRealm(); const realm = this.#isolate ? frame.isolatedRealm() : frame.mainRealm();
try { try {
this.#scripts.push([ const [script] = await Promise.all([
frame, frame.browsingContext.addPreloadScript(functionDeclaration, {
await frame.browsingContext.addPreloadScript(functionDeclaration, {
arguments: [channel], arguments: [channel],
sandbox: realm.sandbox, sandbox: realm.sandbox,
}), }),
]); realm.realm.callFunction(functionDeclaration, false, {
} catch (error) {
// If it errors, the frame probably doesn't support adding preload
// scripts. We fail gracefully.
debugError(error);
}
try {
await realm.realm.callFunction(functionDeclaration, false, {
arguments: [channel], arguments: [channel],
}); }),
]);
this.#scripts.push([frame, script]);
} catch (error) { } catch (error) {
// If it errors, the frame probably doesn't support call function. We // If it errors, the frame probably doesn't support call function. We
// fail gracefully. // fail gracefully.
@ -233,7 +237,17 @@ export class ExposeableFunction<Args extends unknown[], Ret> {
this.#disposables.dispose(); this.#disposables.dispose();
await Promise.all( await Promise.all(
this.#scripts.map(async ([frame, script]) => { this.#scripts.map(async ([frame, script]) => {
await frame.browsingContext.removePreloadScript(script); const realm = this.#isolate ? frame.isolatedRealm() : frame.mainRealm();
try {
await Promise.all([
realm.evaluate(name => {
delete (globalThis as any)[name];
}, this.name),
frame.browsingContext.removePreloadScript(script),
]);
} catch (error) {
debugError(error);
}
}) })
); );
} }

View File

@ -408,14 +408,20 @@ export class BidiFrame extends Frame {
`Failed to add page binding with name ${name}: globalThis['${name}'] already exists!` `Failed to add page binding with name ${name}: globalThis['${name}'] already exists!`
); );
} }
const exposeable = new ExposeableFunction(this, name, apply); const exposeable = await ExposeableFunction.from(this, name, apply);
this.#exposedFunctions.set(name, exposeable); this.#exposedFunctions.set(name, exposeable);
try {
await exposeable.expose();
} catch (error) {
this.#exposedFunctions.delete(name);
throw error;
} }
async removeExposedFunction(name: string): Promise<void> {
const exposedFunction = this.#exposedFunctions.get(name);
if (!exposedFunction) {
throw new Error(
`Failed to remove page binding with name ${name}: window['${name}'] does not exists!`
);
}
this.#exposedFunctions.delete(name);
await exposedFunction[Symbol.asyncDispose]();
} }
override waitForSelector<Selector extends string>( override waitForSelector<Selector extends string>(

View File

@ -577,9 +577,8 @@ export class BidiPage extends Page {
throw new UnsupportedOperation(); throw new UnsupportedOperation();
} }
override removeExposedFunction(): never { override async removeExposedFunction(name: string): Promise<void> {
// TODO: Quick win? await this.#frame.removeExposedFunction(name);
throw new UnsupportedOperation();
} }
override authenticate(): never { override authenticate(): never {

View File

@ -674,12 +674,6 @@
"parameters": ["firefox"], "parameters": ["firefox"],
"expectations": ["SKIP"] "expectations": ["SKIP"]
}, },
{
"testIdPattern": "[page.spec] Page Page.removeExposedFunction should work",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["webDriverBiDi"],
"expectations": ["SKIP"]
},
{ {
"testIdPattern": "[page.spec] Page Page.setBypassCSP *", "testIdPattern": "[page.spec] Page Page.setBypassCSP *",
"platforms": ["darwin", "linux", "win32"], "platforms": ["darwin", "linux", "win32"],