Filing this as an issue as asked in #42649.
Version
1.64.0-next. Originally reproduced at aeebee1d2; packages/playwright-core/src/client/jsHandle.ts has not been touched since that commit, so it still applies.
Steps to reproduce
exposeFunctions and the world option added in #42635 cannot be combined, but passing both crashes inside the injected code rather than saying so.
await page.evaluate(async ({ cb }) => await cb(17), { cb: x => x * 2 },
{ exposeFunctions: true, world: 'utility' });
// page.evaluate: TypeError: Cannot read properties of undefined (reading 'callBinding')
Same error from frame.evaluate and locator.evaluate. Reproduced on chromium, firefox and webkit.
Why it happens
PageBinding.createInitScript installs the bindings controller as an init script, and init scripts only run in the main world, so globalThis[kBindingsControllerProperty] is undefined in the utility world. Runtime.addBinding is registered without a world, so the transport is there but the controller that dispatches through it is not.
That also makes a documented claim false. docs/src/api/params.md says an exposed function is "technically accessible from all frames and worlds of the page", in both js-evaluate-expose-functions and js-init-script-expose-functions:
await page.exposeFunction('f', x => x * 2);
await page.evaluate(() => window.f?.(21) ?? 'NOT PRESENT'); // 42
await page.evaluate(() => window.f?.(21) ?? 'NOT PRESENT', undefined, { world: 'utility' }); // 'NOT PRESENT'
Possible directions
Either make bindings reachable from the utility world, or reject the combination with a clear error the way JSHandleDispatcher.evaluateExpression already does for a world it cannot serve. Either way the two doc sentences want correcting, since they promise worlds today.
I had a patch for the second option in #42649 with the guard in assertEvaluateOptions, which every evaluate entry point already calls, plus the doc correction and one test. Happy to reopen that, rework it toward the first option, or leave it entirely, whichever you prefer. I have no view on which layer you want this solved at.
I am a freshman in college trying to make myself useful on real projects, so if I have misread the intent here just say and I will drop it.
Filing this as an issue as asked in #42649.
Version
1.64.0-next. Originally reproduced at
aeebee1d2;packages/playwright-core/src/client/jsHandle.tshas not been touched since that commit, so it still applies.Steps to reproduce
exposeFunctionsand theworldoption added in #42635 cannot be combined, but passing both crashes inside the injected code rather than saying so.Same error from
frame.evaluateandlocator.evaluate. Reproduced on chromium, firefox and webkit.Why it happens
PageBinding.createInitScriptinstalls the bindings controller as an init script, and init scripts only run in the main world, soglobalThis[kBindingsControllerProperty]is undefined in the utility world.Runtime.addBindingis registered without a world, so the transport is there but the controller that dispatches through it is not.That also makes a documented claim false.
docs/src/api/params.mdsays an exposed function is "technically accessible from all frames and worlds of the page", in bothjs-evaluate-expose-functionsandjs-init-script-expose-functions:Possible directions
Either make bindings reachable from the utility world, or reject the combination with a clear error the way
JSHandleDispatcher.evaluateExpressionalready does for a world it cannot serve. Either way the two doc sentences want correcting, since they promise worlds today.I had a patch for the second option in #42649 with the guard in
assertEvaluateOptions, which every evaluate entry point already calls, plus the doc correction and one test. Happy to reopen that, rework it toward the first option, or leave it entirely, whichever you prefer. I have no view on which layer you want this solved at.I am a freshman in college trying to make myself useful on real projects, so if I have misread the intent here just say and I will drop it.