Skip to content

Commit 5e20e57

Browse files
authored
feat(exposeBinding): support new handle= parameter from 1.5 (#232)
1 parent 9f491c0 commit 5e20e57

File tree

7 files changed

+68
-18
lines changed

7 files changed

+68
-18
lines changed

playwright/async_api.py

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3663,7 +3663,9 @@ async def exposeFunction(self, name: str, binding: typing.Callable) -> NoneType:
36633663
)
36643664
)
36653665

3666-
async def exposeBinding(self, name: str, binding: typing.Callable) -> NoneType:
3666+
async def exposeBinding(
3667+
self, name: str, binding: typing.Callable, handle: bool = None
3668+
) -> NoneType:
36673669
"""Page.exposeBinding
36683670
36693671
The method adds a function called `name` on the `window` object of every frame in this page.
@@ -3685,10 +3687,12 @@ async def exposeBinding(self, name: str, binding: typing.Callable) -> NoneType:
36853687
Name of the function on the window object.
36863688
binding : Callable
36873689
Callback function that will be called in the Playwright's context.
3690+
handle : Optional[bool]
3691+
Whether to pass the argument as a handle, instead of passing by value. When passing a handle, only one argument is supported. When passing by value, multiple arguments are supported.
36883692
"""
36893693
return mapping.from_maybe_impl(
36903694
await self._impl_obj.exposeBinding(
3691-
name=name, binding=self._wrap_handler(binding)
3695+
name=name, binding=self._wrap_handler(binding), handle=handle
36923696
)
36933697
)
36943698

@@ -5286,7 +5290,9 @@ async def addInitScript(
52865290
await self._impl_obj.addInitScript(source=source, path=path)
52875291
)
52885292

5289-
async def exposeBinding(self, name: str, binding: typing.Callable) -> NoneType:
5293+
async def exposeBinding(
5294+
self, name: str, binding: typing.Callable, handle: bool = None
5295+
) -> NoneType:
52905296
"""BrowserContext.exposeBinding
52915297
52925298
The method adds a function called `name` on the `window` object of every frame in every page in the context.
@@ -5305,10 +5311,12 @@ async def exposeBinding(self, name: str, binding: typing.Callable) -> NoneType:
53055311
Name of the function on the window object.
53065312
binding : Callable
53075313
Callback function that will be called in the Playwright's context.
5314+
handle : Optional[bool]
5315+
Whether to pass the argument as a handle, instead of passing by value. When passing a handle, only one argument is supported. When passing by value, multiple arguments are supported.
53085316
"""
53095317
return mapping.from_maybe_impl(
53105318
await self._impl_obj.exposeBinding(
5311-
name=name, binding=self._wrap_handler(binding)
5319+
name=name, binding=self._wrap_handler(binding), handle=handle
53125320
)
53135321
)
53145322

playwright/browser_context.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,9 @@ async def addInitScript(
157157
raise Error("Either path or source parameter must be specified")
158158
await self._channel.send("addInitScript", dict(source=source))
159159

160-
async def exposeBinding(self, name: str, binding: Callable) -> None:
160+
async def exposeBinding(
161+
self, name: str, binding: Callable, handle: bool = None
162+
) -> None:
161163
for page in self._pages:
162164
if name in page._bindings:
163165
raise Error(
@@ -166,7 +168,9 @@ async def exposeBinding(self, name: str, binding: Callable) -> None:
166168
if name in self._bindings:
167169
raise Error(f'Function "{name}" has been already registered')
168170
self._bindings[name] = binding
169-
await self._channel.send("exposeBinding", dict(name=name))
171+
await self._channel.send(
172+
"exposeBinding", dict(name=name, needsHandle=handle or False)
173+
)
170174

171175
async def exposeFunction(self, name: str, binding: Callable) -> None:
172176
await self.exposeBinding(name, lambda source, *args: binding(*args))

playwright/page.py

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -373,15 +373,19 @@ async def addStyleTag(
373373
async def exposeFunction(self, name: str, binding: Callable) -> None:
374374
await self.exposeBinding(name, lambda source, *args: binding(*args))
375375

376-
async def exposeBinding(self, name: str, binding: Callable) -> None:
376+
async def exposeBinding(
377+
self, name: str, binding: Callable, handle: bool = None
378+
) -> None:
377379
if name in self._bindings:
378380
raise Error(f'Function "{name}" has been already registered')
379381
if name in self._browser_context._bindings:
380382
raise Error(
381383
f'Function "{name}" has been already registered in the browser context'
382384
)
383385
self._bindings[name] = binding
384-
await self._channel.send("exposeBinding", dict(name=name))
386+
await self._channel.send(
387+
"exposeBinding", dict(name=name, needsHandle=handle or False)
388+
)
385389

386390
async def setExtraHTTPHeaders(self, headers: Dict[str, str]) -> None:
387391
await self._channel.send(
@@ -920,8 +924,11 @@ async def call(self, func: Callable) -> None:
920924
try:
921925
frame = from_channel(self._initializer["frame"])
922926
source = dict(context=frame._page.context, page=frame._page, frame=frame)
923-
func_args = list(map(parse_result, self._initializer["args"]))
924-
result = func(source, *func_args)
927+
if self._initializer.get("handle"):
928+
result = func(source, from_channel(self._initializer["handle"]))
929+
else:
930+
func_args = list(map(parse_result, self._initializer["args"]))
931+
result = func(source, *func_args)
925932
if asyncio.isfuture(result):
926933
result = await result
927934
await self._channel.send("resolve", dict(result=serialize_argument(result)))

playwright/sync_api.py

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3809,7 +3809,9 @@ def exposeFunction(self, name: str, binding: typing.Callable) -> NoneType:
38093809
)
38103810
)
38113811

3812-
def exposeBinding(self, name: str, binding: typing.Callable) -> NoneType:
3812+
def exposeBinding(
3813+
self, name: str, binding: typing.Callable, handle: bool = None
3814+
) -> NoneType:
38133815
"""Page.exposeBinding
38143816
38153817
The method adds a function called `name` on the `window` object of every frame in this page.
@@ -3831,11 +3833,13 @@ def exposeBinding(self, name: str, binding: typing.Callable) -> NoneType:
38313833
Name of the function on the window object.
38323834
binding : Callable
38333835
Callback function that will be called in the Playwright's context.
3836+
handle : Optional[bool]
3837+
Whether to pass the argument as a handle, instead of passing by value. When passing a handle, only one argument is supported. When passing by value, multiple arguments are supported.
38343838
"""
38353839
return mapping.from_maybe_impl(
38363840
self._sync(
38373841
self._impl_obj.exposeBinding(
3838-
name=name, binding=self._wrap_handler(binding)
3842+
name=name, binding=self._wrap_handler(binding), handle=handle
38393843
)
38403844
)
38413845
)
@@ -5502,7 +5506,9 @@ def addInitScript(
55025506
self._sync(self._impl_obj.addInitScript(source=source, path=path))
55035507
)
55045508

5505-
def exposeBinding(self, name: str, binding: typing.Callable) -> NoneType:
5509+
def exposeBinding(
5510+
self, name: str, binding: typing.Callable, handle: bool = None
5511+
) -> NoneType:
55065512
"""BrowserContext.exposeBinding
55075513
55085514
The method adds a function called `name` on the `window` object of every frame in every page in the context.
@@ -5521,11 +5527,13 @@ def exposeBinding(self, name: str, binding: typing.Callable) -> NoneType:
55215527
Name of the function on the window object.
55225528
binding : Callable
55235529
Callback function that will be called in the Playwright's context.
5530+
handle : Optional[bool]
5531+
Whether to pass the argument as a handle, instead of passing by value. When passing a handle, only one argument is supported. When passing by value, multiple arguments are supported.
55245532
"""
55255533
return mapping.from_maybe_impl(
55265534
self._sync(
55275535
self._impl_obj.exposeBinding(
5528-
name=name, binding=self._wrap_handler(binding)
5536+
name=name, binding=self._wrap_handler(binding), handle=handle
55295537
)
55305538
)
55315539
)

scripts/expected_api_mismatch.txt

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,3 @@ Parameter not implemented: BrowserContext.addInitScript(arg=)
116116
# OptionsOr
117117
Parameter not implemented: Page.waitForEvent(optionsOrPredicate=)
118118
Parameter not implemented: BrowserContext.waitForEvent(optionsOrPredicate=)
119-
120-
# FIXME, 1.5 blockers
121-
Parameter not implemented: Page.exposeBinding(handle=)
122-
Parameter not implemented: BrowserContext.exposeBinding(handle=)

tests/async/test_browsercontext.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -426,6 +426,20 @@ async def test_expose_function_should_be_callable_from_inside_addInitScript(
426426
assert args == ["context", "page"]
427427

428428

429+
async def test_exposebindinghandle_should_work(context):
430+
targets = []
431+
432+
def logme(t):
433+
targets.append(t)
434+
return 17
435+
436+
page = await context.newPage()
437+
await page.exposeBinding("logme", lambda source, t: logme(t), handle=True)
438+
result = await page.evaluate("logme({ foo: 42 })")
439+
assert (await targets[0].evaluate("x => x.foo")) == 42
440+
assert result == 17
441+
442+
429443
async def test_route_should_intercept(context, server):
430444
intercepted = []
431445

tests/async/test_page.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -385,6 +385,19 @@ async def test_expose_function_should_work_with_complex_objects(page, server):
385385
assert result["x"] == 7
386386

387387

388+
async def test_exposebindinghandle_should_work(page, server):
389+
targets = []
390+
391+
def logme(t):
392+
targets.append(t)
393+
return 17
394+
395+
await page.exposeBinding("logme", lambda source, t: logme(t), handle=True)
396+
result = await page.evaluate("logme({ foo: 42 })")
397+
assert (await targets[0].evaluate("x => x.foo")) == 42
398+
assert result == 17
399+
400+
388401
async def test_page_error_should_fire(page, server, is_webkit):
389402
[error, _] = await asyncio.gather(
390403
page.waitForEvent("pageerror"),

0 commit comments

Comments
 (0)