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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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