chore: use handle instead of globals for injection (#8946)

This commit is contained in:
jrandolf 2022-09-13 14:06:40 +02:00 committed by GitHub
parent a4e444d217
commit 6fd05d963e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 73 additions and 42 deletions

View File

@ -220,7 +220,7 @@ export class Frame {
this.#client = client; this.#client = client;
this.worlds = { this.worlds = {
[MAIN_WORLD]: new IsolatedWorld(this), [MAIN_WORLD]: new IsolatedWorld(this),
[PUPPETEER_WORLD]: new IsolatedWorld(this, true), [PUPPETEER_WORLD]: new IsolatedWorld(this),
}; };
} }
@ -776,8 +776,8 @@ export class Frame {
return this.worlds[MAIN_WORLD].transferHandle( return this.worlds[MAIN_WORLD].transferHandle(
await this.worlds[PUPPETEER_WORLD].evaluateHandle( await this.worlds[PUPPETEER_WORLD].evaluateHandle(
async ({url, id, type, content}) => { async ({createDeferredPromise}, {url, id, type, content}) => {
const promise = InjectedUtil.createDeferredPromise<void>(); const promise = createDeferredPromise<void>();
const script = document.createElement('script'); const script = document.createElement('script');
script.type = type; script.type = type;
script.text = content; script.text = content;
@ -809,6 +809,7 @@ export class Frame {
await promise; await promise;
return script; return script;
}, },
await this.worlds[PUPPETEER_WORLD].puppeteerUtil,
{...options, type, content} {...options, type, content}
) )
); );
@ -858,8 +859,8 @@ export class Frame {
return this.worlds[MAIN_WORLD].transferHandle( return this.worlds[MAIN_WORLD].transferHandle(
await this.worlds[PUPPETEER_WORLD].evaluateHandle( await this.worlds[PUPPETEER_WORLD].evaluateHandle(
async ({url, content}) => { async ({createDeferredPromise}, {url, content}) => {
const promise = InjectedUtil.createDeferredPromise<void>(); const promise = createDeferredPromise<void>();
let element: HTMLStyleElement | HTMLLinkElement; let element: HTMLStyleElement | HTMLLinkElement;
if (!url) { if (!url) {
element = document.createElement('style'); element = document.createElement('style');
@ -892,6 +893,7 @@ export class Frame {
await promise; await promise;
return element; return element;
}, },
await this.worlds[PUPPETEER_WORLD].puppeteerUtil,
options options
) )
); );

View File

@ -16,6 +16,7 @@
import {Protocol} from 'devtools-protocol'; import {Protocol} from 'devtools-protocol';
import {source as injectedSource} from '../generated/injected.js'; import {source as injectedSource} from '../generated/injected.js';
import type PuppeteerUtil from '../injected/injected.js';
import {assert} from '../util/assert.js'; import {assert} from '../util/assert.js';
import {createDeferredPromise} from '../util/DeferredPromise.js'; import {createDeferredPromise} from '../util/DeferredPromise.js';
import {CDPSession} from './Connection.js'; import {CDPSession} from './Connection.js';
@ -114,7 +115,6 @@ export interface IsolatedWorldChart {
*/ */
export class IsolatedWorld { export class IsolatedWorld {
#frame: Frame; #frame: Frame;
#injected: boolean;
#document?: ElementHandle<Document>; #document?: ElementHandle<Document>;
#context = createDeferredPromise<ExecutionContext>(); #context = createDeferredPromise<ExecutionContext>();
#detached = false; #detached = false;
@ -125,6 +125,11 @@ export class IsolatedWorld {
// Contains mapping from functions that should be bound to Puppeteer functions. // Contains mapping from functions that should be bound to Puppeteer functions.
#boundFunctions = new Map<string, Function>(); #boundFunctions = new Map<string, Function>();
#waitTasks = new Set<WaitTask>(); #waitTasks = new Set<WaitTask>();
#puppeteerUtil = createDeferredPromise<JSHandle<PuppeteerUtil>>();
get puppeteerUtil(): Promise<JSHandle<PuppeteerUtil>> {
return this.#puppeteerUtil;
}
get _waitTasks(): Set<WaitTask> { get _waitTasks(): Set<WaitTask> {
return this.#waitTasks; return this.#waitTasks;
@ -138,11 +143,10 @@ export class IsolatedWorld {
return `${name}_${contextId}`; return `${name}_${contextId}`;
}; };
constructor(frame: Frame, injected = false) { constructor(frame: Frame) {
// Keep own reference to client because it might differ from the FrameManager's // Keep own reference to client because it might differ from the FrameManager's
// client for OOP iframes. // client for OOP iframes.
this.#frame = frame; this.#frame = frame;
this.#injected = injected;
this.#client.on('Runtime.bindingCalled', this.#onBindingCalled); this.#client.on('Runtime.bindingCalled', this.#onBindingCalled);
} }
@ -164,13 +168,12 @@ export class IsolatedWorld {
clearContext(): void { clearContext(): void {
this.#document = undefined; this.#document = undefined;
this.#puppeteerUtil = createDeferredPromise();
this.#context = createDeferredPromise(); this.#context = createDeferredPromise();
} }
setContext(context: ExecutionContext): void { setContext(context: ExecutionContext): void {
if (this.#injected) { this.#injectPuppeteerUtil(context);
context.evaluate(injectedSource).catch(debugError);
}
this.#ctxBindings.clear(); this.#ctxBindings.clear();
this.#context.resolve(context); this.#context.resolve(context);
for (const waitTask of this._waitTasks) { for (const waitTask of this._waitTasks) {
@ -178,6 +181,22 @@ export class IsolatedWorld {
} }
} }
async #injectPuppeteerUtil(context: ExecutionContext): Promise<void> {
try {
this.#puppeteerUtil.resolve(
(await context.evaluateHandle(
`(() => {
const module = {};
${injectedSource}
return module.exports.default;
})()`
)) as JSHandle<PuppeteerUtil>
);
} catch (error: unknown) {
debugError(error);
}
}
hasContext(): boolean { hasContext(): boolean {
return this.#context.resolved(); return this.#context.resolved();
} }

View File

@ -1,14 +1,11 @@
import {createDeferredPromise} from '../util/DeferredPromise.js'; import {createDeferredPromise} from '../util/DeferredPromise.js';
import * as Poller from './Poller.js';
import * as util from './util.js'; import * as util from './util.js';
Object.assign( const PuppeteerUtil = Object.freeze({
self, ...util,
Object.freeze({ createDeferredPromise,
InjectedUtil: { });
...Poller,
...util, type PuppeteerUtil = typeof PuppeteerUtil;
createDeferredPromise,
}, export default PuppeteerUtil;
})
);

View File

@ -1,10 +1,8 @@
import {createDeferredPromise} from '../util/DeferredPromise.js'; /**
* CommonJS JavaScript code that provides the puppeteer utilities. See the
declare global { * [README](https://github.com/puppeteer/puppeteer/blob/main/src/injected/README.md)
const InjectedUtil: { * for injection for more information.
createDeferredPromise: typeof createDeferredPromise; *
}; * @internal
} */
/** @internal */
export const source = SOURCE_CODE; export const source = SOURCE_CODE;

View File

@ -8,6 +8,5 @@
"references": [ "references": [
{"path": "../vendor/tsconfig.cjs.json"}, {"path": "../vendor/tsconfig.cjs.json"},
{"path": "../compat/cjs/tsconfig.json"} {"path": "../compat/cjs/tsconfig.json"}
], ]
"exclude": ["injected/injected.ts"]
} }

View File

@ -8,6 +8,5 @@
"references": [ "references": [
{"path": "../vendor/tsconfig.esm.json"}, {"path": "../vendor/tsconfig.esm.json"},
{"path": "../compat/esm/tsconfig.json"} {"path": "../compat/esm/tsconfig.json"}
], ]
"exclude": ["injected/injected.ts"]
} }

View File

@ -23,18 +23,35 @@ import {
setupTestPageAndContextHooks, setupTestPageAndContextHooks,
} from './mocha-utils.js'; } from './mocha-utils.js';
describe('InjectedUtil tests', function () { describe('PuppeteerUtil tests', function () {
setupTestBrowserHooks(); setupTestBrowserHooks();
setupTestPageAndContextHooks(); setupTestPageAndContextHooks();
it('should work', async () => { it('should work', async () => {
const {page} = getTestState(); const {page} = getTestState();
const handle = await page const world = page.mainFrame().worlds[PUPPETEER_WORLD];
.mainFrame() const value = await world.evaluate(PuppeteerUtil => {
.worlds[PUPPETEER_WORLD].evaluate(() => { return typeof PuppeteerUtil === 'object';
return typeof InjectedUtil === 'object'; }, world.puppeteerUtil);
}); expect(value).toBeTruthy();
expect(handle).toBeTruthy(); });
describe('createFunction tests', function () {
it('should work', async () => {
const {page} = getTestState();
const world = page.mainFrame().worlds[PUPPETEER_WORLD];
const value = await world.evaluate(
({createFunction}, fnString) => {
return createFunction(fnString)(4);
},
await world.puppeteerUtil,
(() => {
return 4;
}).toString()
);
expect(value).toBe(4);
});
}); });
}); });