Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 19 additions & 1 deletion packages/injected/src/bindingsController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,11 +59,29 @@ export class BindingsController {
});
}
const payload: BindingPayload = { name: bindingName, seq, serializedArgs };
(this._global as any)[this._globalBindingName](JSON.stringify(payload));
(this._global as any)[this._globalBindingName](this._stringifyPayload(payload));
return promise;
};
}

private _stringifyPayload(payload: BindingPayload): string {
// The page may define Array.prototype.toJSON/Object.prototype.toJSON (e.g. Prototype.js),
// which would corrupt JSON.stringify(payload) into something other than an object with
// a serializedArgs array. Temporarily remove them so our own payload serializes correctly.
const arrayToJSON = (Array.prototype as any).toJSON;
const objectToJSON = (Object.prototype as any).toJSON;
try {
delete (Array.prototype as any).toJSON;
delete (Object.prototype as any).toJSON;
return JSON.stringify(payload);
} finally {
if (arrayToJSON !== undefined)
(Array.prototype as any).toJSON = arrayToJSON;
if (objectToJSON !== undefined)
(Object.prototype as any).toJSON = objectToJSON;
}
}

removeBinding(bindingName: string) {
const data = this._bindings.get(bindingName);
if (data)
Expand Down
5 changes: 3 additions & 2 deletions tests/page/page-expose-function.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -235,12 +235,13 @@ it('should work with busted Array.prototype.map/push', async ({ page, server })
expect(await page.evaluate('add(5, 6)')).toBe(11);
});

it('should fail with busted Array.prototype.toJSON', async ({ page }) => {
it('should work with busted Array.prototype.toJSON', { annotation: { type: 'issue', description: 'https://github.com/microsoft/playwright/issues/41359' } }, async ({ page }) => {
await page.evaluateHandle(() => (Array.prototype as any).toJSON = () => '"[]"');

await page.exposeFunction('add', (a, b) => a + b);
await expect(() => page.evaluate(`add(5, 6)`)).rejects.toThrowError('serializedArgs is not an array. This can happen when Array.prototype.toJSON is defined incorrectly');
expect(await page.evaluate(`add(5, 6)`)).toBe(11);

// The page's own override must still be intact after the binding call.
expect.soft(await page.evaluate(() => ([] as any).toJSON())).toBe('"[]"');
});

Expand Down