Skip to content

Commit bdd9c5d

Browse files
authored
chore(roll): roll Playwright to 1.42.0-alpha-2024-02-19 (#2313)
1 parent 3e44464 commit bdd9c5d

24 files changed

+965
-88
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@ Playwright is a Python library to automate [Chromium](https://www.chromium.org/H
44

55
| | Linux | macOS | Windows |
66
| :--- | :---: | :---: | :---: |
7-
| Chromium <!-- GEN:chromium-version -->121.0.6167.57<!-- GEN:stop --> ||||
7+
| Chromium <!-- GEN:chromium-version -->122.0.6261.39<!-- GEN:stop --> ||||
88
| WebKit <!-- GEN:webkit-version -->17.4<!-- GEN:stop --> ||||
9-
| Firefox <!-- GEN:firefox-version -->121.0<!-- GEN:stop --> ||||
9+
| Firefox <!-- GEN:firefox-version -->122.0<!-- GEN:stop --> ||||
1010

1111
## Documentation
1212

playwright/_impl/_connection.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,12 @@
3333
cast,
3434
)
3535

36-
from greenlet import greenlet
3736
from pyee import EventEmitter
3837
from pyee.asyncio import AsyncIOEventEmitter
3938

4039
import playwright
4140
from playwright._impl._errors import TargetClosedError
41+
from playwright._impl._greenlets import EventGreenlet
4242
from playwright._impl._helper import Error, ParsedMessagePayload, parse_error
4343
from playwright._impl._transport import Transport
4444

@@ -417,7 +417,7 @@ def dispatch(self, msg: ParsedMessagePayload) -> None:
417417
# Each event handler is a potentilly blocking context, create a fiber for each
418418
# and switch to them in order, until they block inside and pass control to each
419419
# other and then eventually back to dispatcher as listener functions return.
420-
g = greenlet(listener)
420+
g = EventGreenlet(listener)
421421
if should_replace_guids_with_channels:
422422
g.switch(self._replace_guids_with_channels(params))
423423
else:

playwright/_impl/_frame.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
from playwright._impl._js_handle import (
5454
JSHandle,
5555
Serializable,
56+
add_source_url_to_script,
5657
parse_result,
5758
serialize_argument,
5859
)
@@ -450,10 +451,8 @@ async def add_script_tag(
450451
) -> ElementHandle:
451452
params = locals_to_params(locals())
452453
if path:
453-
params["content"] = (
454-
(await async_readfile(path)).decode()
455-
+ "\n//# sourceURL="
456-
+ str(Path(path))
454+
params["content"] = add_source_url_to_script(
455+
(await async_readfile(path)).decode(), path
457456
)
458457
del params["path"]
459458
return from_channel(await self._channel.send("addScriptTag", params))

playwright/_impl/_greenlets.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
# Copyright (c) Microsoft Corporation.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
import os
15+
from typing import Tuple
16+
17+
import greenlet
18+
19+
20+
def _greenlet_trace_callback(
21+
event: str, args: Tuple[greenlet.greenlet, greenlet.greenlet]
22+
) -> None:
23+
if event in ("switch", "throw"):
24+
origin, target = args
25+
print(f"Transfer from {origin} to {target} with {event}")
26+
27+
28+
if os.environ.get("INTERNAL_PW_GREENLET_DEBUG"):
29+
greenlet.settrace(_greenlet_trace_callback)
30+
31+
32+
class MainGreenlet(greenlet.greenlet):
33+
def __str__(self) -> str:
34+
return "<MainGreenlet>"
35+
36+
37+
class RouteGreenlet(greenlet.greenlet):
38+
def __str__(self) -> str:
39+
return "<RouteGreenlet>"
40+
41+
42+
class LocatorHandlerGreenlet(greenlet.greenlet):
43+
def __str__(self) -> str:
44+
return "<LocatorHandlerGreenlet>"
45+
46+
47+
class EventGreenlet(greenlet.greenlet):
48+
def __str__(self) -> str:
49+
return "<EventGreenlet>"

playwright/_impl/_har_router.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,13 @@ async def _handle(self, route: "Route") -> None:
7575
return
7676

7777
if action == "fulfill":
78+
# If the response status is -1, the request was canceled or stalled, so we just stall it here.
79+
# See https://github.com/microsoft/playwright/issues/29311.
80+
# TODO: it'd be better to abort such requests, but then we likely need to respect the timing,
81+
# because the request might have been stalled for a long time until the very end of the
82+
# test when HAR was recorded but we'd abort it immediately.
83+
if response.get("status") == -1:
84+
return
7885
body = response["body"]
7986
assert body is not None
8087
await route.fulfill(

playwright/_impl/_helper.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,10 @@
3636
)
3737
from urllib.parse import urljoin
3838

39-
from greenlet import greenlet
40-
4139
from playwright._impl._api_structures import NameValue
4240
from playwright._impl._errors import Error, TargetClosedError, TimeoutError
4341
from playwright._impl._glob import glob_to_regex
42+
from playwright._impl._greenlets import RouteGreenlet
4443
from playwright._impl._str_utils import escape_regex_flags
4544

4645
if TYPE_CHECKING: # pragma: no cover
@@ -302,7 +301,7 @@ async def _handle_internal(self, route: "Route") -> bool:
302301
if self._is_sync:
303302
# As with event handlers, each route handler is a potentially blocking context
304303
# so it needs a fiber.
305-
g = greenlet(lambda: self.handler(route, route.request)) # type: ignore
304+
g = RouteGreenlet(lambda: self.handler(route, route.request)) # type: ignore
306305
g.switch()
307306
else:
308307
coro_or_future = self.handler(route, route.request) # type: ignore

playwright/_impl/_js_handle.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@
1515
import collections.abc
1616
import math
1717
from datetime import datetime
18-
from typing import TYPE_CHECKING, Any, Dict, List, Optional
18+
from pathlib import Path
19+
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union
1920
from urllib.parse import ParseResult, urlparse, urlunparse
2021

2122
from playwright._impl._connection import Channel, ChannelOwner, from_channel
@@ -226,3 +227,7 @@ def parse_value(value: Any, refs: Optional[Dict[int, Any]] = None) -> Any:
226227

227228
def parse_result(result: Any) -> Any:
228229
return parse_value(result)
230+
231+
232+
def add_source_url_to_script(source: str, path: Union[str, Path]) -> str:
233+
return source + "\n//# sourceURL=" + str(path).replace("\n", "")

playwright/_impl/_page.py

Lines changed: 68 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
from playwright._impl._event_context_manager import EventContextManagerImpl
5656
from playwright._impl._file_chooser import FileChooser
5757
from playwright._impl._frame import Frame
58+
from playwright._impl._greenlets import LocatorHandlerGreenlet
5859
from playwright._impl._har_router import HarRouter
5960
from playwright._impl._helper import (
6061
ColorScheme,
@@ -82,6 +83,7 @@
8283
from playwright._impl._js_handle import (
8384
JSHandle,
8485
Serializable,
86+
add_source_url_to_script,
8587
parse_result,
8688
serialize_argument,
8789
)
@@ -150,6 +152,7 @@ def __init__(
150152
self._close_reason: Optional[str] = None
151153
self._close_was_called = False
152154
self._har_routers: List[HarRouter] = []
155+
self._locator_handlers: Dict[str, Callable] = {}
153156

154157
self._channel.on(
155158
"bindingCall",
@@ -175,6 +178,12 @@ def __init__(
175178
"frameDetached",
176179
lambda params: self._on_frame_detached(from_channel(params["frame"])),
177180
)
181+
self._channel.on(
182+
"locatorHandlerTriggered",
183+
lambda params: asyncio.create_task(
184+
self._on_locator_handler_triggered(params["uid"])
185+
),
186+
)
178187
self._channel.on(
179188
"route",
180189
lambda params: asyncio.create_task(
@@ -194,17 +203,21 @@ def __init__(
194203
self._closed_or_crashed_future: asyncio.Future = asyncio.Future()
195204
self.on(
196205
Page.Events.Close,
197-
lambda _: self._closed_or_crashed_future.set_result(
198-
self._close_error_with_reason()
199-
)
200-
if not self._closed_or_crashed_future.done()
201-
else None,
206+
lambda _: (
207+
self._closed_or_crashed_future.set_result(
208+
self._close_error_with_reason()
209+
)
210+
if not self._closed_or_crashed_future.done()
211+
else None
212+
),
202213
)
203214
self.on(
204215
Page.Events.Crash,
205-
lambda _: self._closed_or_crashed_future.set_result(TargetClosedError())
206-
if not self._closed_or_crashed_future.done()
207-
else None,
216+
lambda _: (
217+
self._closed_or_crashed_future.set_result(TargetClosedError())
218+
if not self._closed_or_crashed_future.done()
219+
else None
220+
),
208221
)
209222

210223
self._set_event_to_subscription_mapping(
@@ -567,7 +580,9 @@ async def add_init_script(
567580
self, script: str = None, path: Union[str, Path] = None
568581
) -> None:
569582
if path:
570-
script = (await async_readfile(path)).decode()
583+
script = add_source_url_to_script(
584+
(await async_readfile(path)).decode(), path
585+
)
571586
if not isinstance(script, str):
572587
raise Error("Either path or script parameter must be specified")
573588
await self._channel.send("addInitScript", dict(source=script))
@@ -1029,6 +1044,8 @@ async def pdf(
10291044
preferCSSPageSize: bool = None,
10301045
margin: PdfMargins = None,
10311046
path: Union[str, Path] = None,
1047+
outline: bool = None,
1048+
tagged: bool = None,
10321049
) -> bytes:
10331050
params = locals_to_params(locals())
10341051
if "path" in params:
@@ -1238,6 +1255,48 @@ async def set_checked(
12381255
trial=trial,
12391256
)
12401257

1258+
async def add_locator_handler(self, locator: "Locator", handler: Callable) -> None:
1259+
if locator._frame != self._main_frame:
1260+
raise Error("Locator must belong to the main frame of this page")
1261+
uid = await self._channel.send(
1262+
"registerLocatorHandler",
1263+
{
1264+
"selector": locator._selector,
1265+
},
1266+
)
1267+
self._locator_handlers[uid] = handler
1268+
1269+
async def _on_locator_handler_triggered(self, uid: str) -> None:
1270+
try:
1271+
if self._dispatcher_fiber:
1272+
handler_finished_future = self._loop.create_future()
1273+
1274+
def _handler() -> None:
1275+
try:
1276+
self._locator_handlers[uid]()
1277+
handler_finished_future.set_result(None)
1278+
except Exception as e:
1279+
handler_finished_future.set_exception(e)
1280+
1281+
g = LocatorHandlerGreenlet(_handler)
1282+
g.switch()
1283+
await handler_finished_future
1284+
else:
1285+
coro_or_future = self._locator_handlers[uid]()
1286+
if coro_or_future:
1287+
await coro_or_future
1288+
1289+
finally:
1290+
try:
1291+
await self._connection.wrap_api_call(
1292+
lambda: self._channel.send(
1293+
"resolveLocatorHandlerNoReply", {"uid": uid}
1294+
),
1295+
is_internal=True,
1296+
)
1297+
except Error:
1298+
pass
1299+
12411300

12421301
class Worker(ChannelOwner):
12431302
Events = SimpleNamespace(Close="close")

0 commit comments

Comments
 (0)