_page.py 51 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570
  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 asyncio
  15. import base64
  16. import inspect
  17. import re
  18. import sys
  19. from pathlib import Path
  20. from types import SimpleNamespace
  21. from typing import (
  22. TYPE_CHECKING,
  23. Any,
  24. Callable,
  25. Dict,
  26. List,
  27. Literal,
  28. Optional,
  29. Pattern,
  30. Sequence,
  31. Union,
  32. cast,
  33. )
  34. from playwright._impl._api_structures import (
  35. AriaRole,
  36. FilePayload,
  37. FloatRect,
  38. PdfMargins,
  39. Position,
  40. ViewportSize,
  41. )
  42. from playwright._impl._artifact import Artifact
  43. from playwright._impl._clock import Clock
  44. from playwright._impl._connection import (
  45. ChannelOwner,
  46. from_channel,
  47. from_nullable_channel,
  48. )
  49. from playwright._impl._console_message import ConsoleMessage
  50. from playwright._impl._download import Download
  51. from playwright._impl._element_handle import ElementHandle, determine_screenshot_type
  52. from playwright._impl._errors import Error, TargetClosedError, is_target_closed_error
  53. from playwright._impl._event_context_manager import EventContextManagerImpl
  54. from playwright._impl._file_chooser import FileChooser
  55. from playwright._impl._frame import Frame
  56. from playwright._impl._greenlets import LocatorHandlerGreenlet
  57. from playwright._impl._har_router import HarRouter
  58. from playwright._impl._helper import (
  59. ColorScheme,
  60. Contrast,
  61. DocumentLoadState,
  62. ForcedColors,
  63. HarMode,
  64. KeyboardModifier,
  65. MouseButton,
  66. ReducedMotion,
  67. RouteFromHarNotFoundPolicy,
  68. RouteHandler,
  69. RouteHandlerCallback,
  70. TimeoutSettings,
  71. URLMatch,
  72. URLMatchRequest,
  73. URLMatchResponse,
  74. WebSocketRouteHandlerCallback,
  75. async_readfile,
  76. async_writefile,
  77. locals_to_params,
  78. make_dirs_for_file,
  79. parse_error,
  80. serialize_error,
  81. url_matches,
  82. )
  83. from playwright._impl._input import Keyboard, Mouse, Touchscreen
  84. from playwright._impl._js_handle import (
  85. JSHandle,
  86. Serializable,
  87. add_source_url_to_script,
  88. parse_result,
  89. serialize_argument,
  90. )
  91. from playwright._impl._network import (
  92. Request,
  93. Response,
  94. Route,
  95. WebSocketRoute,
  96. WebSocketRouteHandler,
  97. serialize_headers,
  98. )
  99. from playwright._impl._video import Video
  100. from playwright._impl._waiter import Waiter
  101. if TYPE_CHECKING: # pragma: no cover
  102. from playwright._impl._browser_context import BrowserContext
  103. from playwright._impl._fetch import APIRequestContext
  104. from playwright._impl._locator import FrameLocator, Locator
  105. from playwright._impl._network import WebSocket
  106. class LocatorHandler:
  107. locator: "Locator"
  108. handler: Union[Callable[["Locator"], Any], Callable[..., Any]]
  109. times: Union[int, None]
  110. def __init__(
  111. self, locator: "Locator", handler: Callable[..., Any], times: Union[int, None]
  112. ) -> None:
  113. self.locator = locator
  114. self._handler = handler
  115. self.times = times
  116. def __call__(self) -> Any:
  117. arg_count = len(inspect.signature(self._handler).parameters)
  118. if arg_count == 0:
  119. return self._handler()
  120. return self._handler(self.locator)
  121. class Page(ChannelOwner):
  122. Events = SimpleNamespace(
  123. Close="close",
  124. Crash="crash",
  125. Console="console",
  126. Dialog="dialog",
  127. Download="download",
  128. FileChooser="filechooser",
  129. DOMContentLoaded="domcontentloaded",
  130. PageError="pageerror",
  131. Request="request",
  132. Response="response",
  133. RequestFailed="requestfailed",
  134. RequestFinished="requestfinished",
  135. FrameAttached="frameattached",
  136. FrameDetached="framedetached",
  137. FrameNavigated="framenavigated",
  138. Load="load",
  139. Popup="popup",
  140. WebSocket="websocket",
  141. Worker="worker",
  142. )
  143. keyboard: Keyboard
  144. mouse: Mouse
  145. touchscreen: Touchscreen
  146. def __init__(
  147. self, parent: ChannelOwner, type: str, guid: str, initializer: Dict
  148. ) -> None:
  149. super().__init__(parent, type, guid, initializer)
  150. self._browser_context = cast("BrowserContext", parent)
  151. self.keyboard = Keyboard(self._channel)
  152. self.mouse = Mouse(self._channel)
  153. self.touchscreen = Touchscreen(self._channel)
  154. self._main_frame: Frame = from_channel(initializer["mainFrame"])
  155. self._main_frame._page = self
  156. self._frames = [self._main_frame]
  157. self._viewport_size: Optional[ViewportSize] = initializer.get("viewportSize")
  158. self._is_closed = False
  159. self._workers: List["Worker"] = []
  160. self._bindings: Dict[str, Any] = {}
  161. self._routes: List[RouteHandler] = []
  162. self._web_socket_routes: List[WebSocketRouteHandler] = []
  163. self._owned_context: Optional["BrowserContext"] = None
  164. self._timeout_settings: TimeoutSettings = TimeoutSettings(
  165. self._browser_context._timeout_settings
  166. )
  167. self._video: Optional[Video] = None
  168. self._opener = cast("Page", from_nullable_channel(initializer.get("opener")))
  169. self._close_reason: Optional[str] = None
  170. self._close_was_called = False
  171. self._har_routers: List[HarRouter] = []
  172. self._locator_handlers: Dict[str, LocatorHandler] = {}
  173. self._channel.on(
  174. "bindingCall",
  175. lambda params: self._on_binding(from_channel(params["binding"])),
  176. )
  177. self._channel.on("close", lambda _: self._on_close())
  178. self._channel.on("crash", lambda _: self._on_crash())
  179. self._channel.on("download", lambda params: self._on_download(params))
  180. self._channel.on(
  181. "fileChooser",
  182. lambda params: self.emit(
  183. Page.Events.FileChooser,
  184. FileChooser(
  185. self, from_channel(params["element"]), params["isMultiple"]
  186. ),
  187. ),
  188. )
  189. self._channel.on(
  190. "frameAttached",
  191. lambda params: self._on_frame_attached(from_channel(params["frame"])),
  192. )
  193. self._channel.on(
  194. "frameDetached",
  195. lambda params: self._on_frame_detached(from_channel(params["frame"])),
  196. )
  197. self._channel.on(
  198. "locatorHandlerTriggered",
  199. lambda params: self._loop.create_task(
  200. self._on_locator_handler_triggered(params["uid"])
  201. ),
  202. )
  203. self._channel.on(
  204. "route",
  205. lambda params: self._loop.create_task(
  206. self._on_route(from_channel(params["route"]))
  207. ),
  208. )
  209. self._channel.on(
  210. "webSocketRoute",
  211. lambda params: self._loop.create_task(
  212. self._on_web_socket_route(from_channel(params["webSocketRoute"]))
  213. ),
  214. )
  215. self._channel.on("video", lambda params: self._on_video(params))
  216. self._channel.on("viewportSizeChanged", self._on_viewport_size_changed)
  217. self._channel.on(
  218. "webSocket",
  219. lambda params: self.emit(
  220. Page.Events.WebSocket, from_channel(params["webSocket"])
  221. ),
  222. )
  223. self._channel.on(
  224. "worker", lambda params: self._on_worker(from_channel(params["worker"]))
  225. )
  226. self._closed_or_crashed_future: asyncio.Future = asyncio.Future()
  227. self.on(
  228. Page.Events.Close,
  229. lambda _: (
  230. self._closed_or_crashed_future.set_result(
  231. self._close_error_with_reason()
  232. )
  233. if not self._closed_or_crashed_future.done()
  234. else None
  235. ),
  236. )
  237. self.on(
  238. Page.Events.Crash,
  239. lambda _: (
  240. self._closed_or_crashed_future.set_result(TargetClosedError())
  241. if not self._closed_or_crashed_future.done()
  242. else None
  243. ),
  244. )
  245. self._set_event_to_subscription_mapping(
  246. {
  247. Page.Events.Console: "console",
  248. Page.Events.Dialog: "dialog",
  249. Page.Events.Request: "request",
  250. Page.Events.Response: "response",
  251. Page.Events.RequestFinished: "requestFinished",
  252. Page.Events.RequestFailed: "requestFailed",
  253. Page.Events.FileChooser: "fileChooser",
  254. }
  255. )
  256. def __repr__(self) -> str:
  257. return f"<Page url={self.url!r}>"
  258. def _on_frame_attached(self, frame: Frame) -> None:
  259. frame._page = self
  260. self._frames.append(frame)
  261. self.emit(Page.Events.FrameAttached, frame)
  262. def _on_frame_detached(self, frame: Frame) -> None:
  263. self._frames.remove(frame)
  264. frame._detached = True
  265. self.emit(Page.Events.FrameDetached, frame)
  266. async def _on_route(self, route: Route) -> None:
  267. route._context = self.context
  268. route_handlers = self._routes.copy()
  269. for route_handler in route_handlers:
  270. # If the page was closed we stall all requests right away.
  271. if self._close_was_called or self.context._closing_or_closed:
  272. return
  273. if not route_handler.matches(route.request.url):
  274. continue
  275. if route_handler not in self._routes:
  276. continue
  277. if route_handler.will_expire:
  278. self._routes.remove(route_handler)
  279. try:
  280. handled = await route_handler.handle(route)
  281. finally:
  282. if len(self._routes) == 0:
  283. async def _update_interceptor_patterns_ignore_exceptions() -> None:
  284. try:
  285. await self._update_interception_patterns()
  286. except Error:
  287. pass
  288. asyncio.create_task(
  289. self._connection.wrap_api_call(
  290. _update_interceptor_patterns_ignore_exceptions, True
  291. )
  292. )
  293. if handled:
  294. return
  295. await self._browser_context._on_route(route)
  296. async def _on_web_socket_route(self, web_socket_route: WebSocketRoute) -> None:
  297. route_handler = next(
  298. (
  299. route_handler
  300. for route_handler in self._web_socket_routes
  301. if route_handler.matches(web_socket_route.url)
  302. ),
  303. None,
  304. )
  305. if route_handler:
  306. await route_handler.handle(web_socket_route)
  307. else:
  308. await self._browser_context._on_web_socket_route(web_socket_route)
  309. def _on_binding(self, binding_call: "BindingCall") -> None:
  310. func = self._bindings.get(binding_call._initializer["name"])
  311. if func:
  312. asyncio.create_task(binding_call.call(func))
  313. self._browser_context._on_binding(binding_call)
  314. def _on_worker(self, worker: "Worker") -> None:
  315. self._workers.append(worker)
  316. worker._page = self
  317. self.emit(Page.Events.Worker, worker)
  318. def _on_close(self) -> None:
  319. self._is_closed = True
  320. if self in self._browser_context._pages:
  321. self._browser_context._pages.remove(self)
  322. self._dispose_har_routers()
  323. self.emit(Page.Events.Close, self)
  324. def _on_crash(self) -> None:
  325. self.emit(Page.Events.Crash, self)
  326. def _on_download(self, params: Any) -> None:
  327. url = params["url"]
  328. suggested_filename = params["suggestedFilename"]
  329. artifact = cast(Artifact, from_channel(params["artifact"]))
  330. self.emit(
  331. Page.Events.Download, Download(self, url, suggested_filename, artifact)
  332. )
  333. def _on_video(self, params: Any) -> None:
  334. artifact = from_channel(params["artifact"])
  335. self._force_video()._artifact_ready(artifact)
  336. def _on_viewport_size_changed(self, params: Any) -> None:
  337. self._viewport_size = params["viewportSize"]
  338. @property
  339. def context(self) -> "BrowserContext":
  340. return self._browser_context
  341. @property
  342. def clock(self) -> Clock:
  343. return self._browser_context.clock
  344. async def opener(self) -> Optional["Page"]:
  345. if self._opener and self._opener.is_closed():
  346. return None
  347. return self._opener
  348. @property
  349. def main_frame(self) -> Frame:
  350. return self._main_frame
  351. def frame(self, name: str = None, url: URLMatch = None) -> Optional[Frame]:
  352. for frame in self._frames:
  353. if name and frame.name == name:
  354. return frame
  355. if url and url_matches(self._browser_context._base_url, frame.url, url):
  356. return frame
  357. return None
  358. @property
  359. def frames(self) -> List[Frame]:
  360. return self._frames.copy()
  361. def set_default_navigation_timeout(self, timeout: float) -> None:
  362. self._timeout_settings.set_default_navigation_timeout(timeout)
  363. def set_default_timeout(self, timeout: float) -> None:
  364. self._timeout_settings.set_default_timeout(timeout)
  365. async def query_selector(
  366. self,
  367. selector: str,
  368. strict: bool = None,
  369. ) -> Optional[ElementHandle]:
  370. return await self._main_frame.query_selector(selector, strict)
  371. async def query_selector_all(self, selector: str) -> List[ElementHandle]:
  372. return await self._main_frame.query_selector_all(selector)
  373. async def wait_for_selector(
  374. self,
  375. selector: str,
  376. timeout: float = None,
  377. state: Literal["attached", "detached", "hidden", "visible"] = None,
  378. strict: bool = None,
  379. ) -> Optional[ElementHandle]:
  380. return await self._main_frame.wait_for_selector(**locals_to_params(locals()))
  381. async def is_checked(
  382. self, selector: str, strict: bool = None, timeout: float = None
  383. ) -> bool:
  384. return await self._main_frame.is_checked(**locals_to_params(locals()))
  385. async def is_disabled(
  386. self, selector: str, strict: bool = None, timeout: float = None
  387. ) -> bool:
  388. return await self._main_frame.is_disabled(**locals_to_params(locals()))
  389. async def is_editable(
  390. self, selector: str, strict: bool = None, timeout: float = None
  391. ) -> bool:
  392. return await self._main_frame.is_editable(**locals_to_params(locals()))
  393. async def is_enabled(
  394. self, selector: str, strict: bool = None, timeout: float = None
  395. ) -> bool:
  396. return await self._main_frame.is_enabled(**locals_to_params(locals()))
  397. async def is_hidden(
  398. self, selector: str, strict: bool = None, timeout: float = None
  399. ) -> bool:
  400. # timeout is deprecated and does nothing
  401. return await self._main_frame.is_hidden(selector=selector, strict=strict)
  402. async def is_visible(
  403. self, selector: str, strict: bool = None, timeout: float = None
  404. ) -> bool:
  405. # timeout is deprecated and does nothing
  406. return await self._main_frame.is_visible(selector=selector, strict=strict)
  407. async def dispatch_event(
  408. self,
  409. selector: str,
  410. type: str,
  411. eventInit: Dict = None,
  412. timeout: float = None,
  413. strict: bool = None,
  414. ) -> None:
  415. return await self._main_frame.dispatch_event(**locals_to_params(locals()))
  416. async def evaluate(self, expression: str, arg: Serializable = None) -> Any:
  417. return await self._main_frame.evaluate(expression, arg)
  418. async def evaluate_handle(
  419. self, expression: str, arg: Serializable = None
  420. ) -> JSHandle:
  421. return await self._main_frame.evaluate_handle(expression, arg)
  422. async def eval_on_selector(
  423. self,
  424. selector: str,
  425. expression: str,
  426. arg: Serializable = None,
  427. strict: bool = None,
  428. ) -> Any:
  429. return await self._main_frame.eval_on_selector(
  430. selector, expression, arg, strict
  431. )
  432. async def eval_on_selector_all(
  433. self,
  434. selector: str,
  435. expression: str,
  436. arg: Serializable = None,
  437. ) -> Any:
  438. return await self._main_frame.eval_on_selector_all(selector, expression, arg)
  439. async def add_script_tag(
  440. self,
  441. url: str = None,
  442. path: Union[str, Path] = None,
  443. content: str = None,
  444. type: str = None,
  445. ) -> ElementHandle:
  446. return await self._main_frame.add_script_tag(**locals_to_params(locals()))
  447. async def add_style_tag(
  448. self, url: str = None, path: Union[str, Path] = None, content: str = None
  449. ) -> ElementHandle:
  450. return await self._main_frame.add_style_tag(**locals_to_params(locals()))
  451. async def expose_function(self, name: str, callback: Callable) -> None:
  452. await self.expose_binding(name, lambda source, *args: callback(*args))
  453. async def expose_binding(
  454. self, name: str, callback: Callable, handle: bool = None
  455. ) -> None:
  456. if name in self._bindings:
  457. raise Error(f'Function "{name}" has been already registered')
  458. if name in self._browser_context._bindings:
  459. raise Error(
  460. f'Function "{name}" has been already registered in the browser context'
  461. )
  462. self._bindings[name] = callback
  463. await self._channel.send(
  464. "exposeBinding",
  465. None,
  466. dict(name=name, needsHandle=handle or False),
  467. )
  468. async def set_extra_http_headers(self, headers: Dict[str, str]) -> None:
  469. await self._channel.send(
  470. "setExtraHTTPHeaders",
  471. None,
  472. dict(headers=serialize_headers(headers)),
  473. )
  474. @property
  475. def url(self) -> str:
  476. return self._main_frame.url
  477. async def content(self) -> str:
  478. return await self._main_frame.content()
  479. async def set_content(
  480. self,
  481. html: str,
  482. timeout: float = None,
  483. waitUntil: DocumentLoadState = None,
  484. ) -> None:
  485. return await self._main_frame.set_content(**locals_to_params(locals()))
  486. async def goto(
  487. self,
  488. url: str,
  489. timeout: float = None,
  490. waitUntil: DocumentLoadState = None,
  491. referer: str = None,
  492. ) -> Optional[Response]:
  493. return await self._main_frame.goto(**locals_to_params(locals()))
  494. async def reload(
  495. self,
  496. timeout: float = None,
  497. waitUntil: DocumentLoadState = None,
  498. ) -> Optional[Response]:
  499. return from_nullable_channel(
  500. await self._channel.send(
  501. "reload",
  502. self._timeout_settings.navigation_timeout,
  503. locals_to_params(locals()),
  504. )
  505. )
  506. async def wait_for_load_state(
  507. self,
  508. state: Literal["domcontentloaded", "load", "networkidle"] = None,
  509. timeout: float = None,
  510. ) -> None:
  511. return await self._main_frame.wait_for_load_state(**locals_to_params(locals()))
  512. async def wait_for_url(
  513. self,
  514. url: URLMatch,
  515. waitUntil: DocumentLoadState = None,
  516. timeout: float = None,
  517. ) -> None:
  518. return await self._main_frame.wait_for_url(**locals_to_params(locals()))
  519. async def wait_for_event(
  520. self, event: str, predicate: Callable = None, timeout: float = None
  521. ) -> Any:
  522. async with self.expect_event(event, predicate, timeout) as event_info:
  523. pass
  524. return await event_info
  525. async def go_back(
  526. self,
  527. timeout: float = None,
  528. waitUntil: DocumentLoadState = None,
  529. ) -> Optional[Response]:
  530. return from_nullable_channel(
  531. await self._channel.send(
  532. "goBack",
  533. self._timeout_settings.navigation_timeout,
  534. locals_to_params(locals()),
  535. )
  536. )
  537. async def go_forward(
  538. self,
  539. timeout: float = None,
  540. waitUntil: DocumentLoadState = None,
  541. ) -> Optional[Response]:
  542. return from_nullable_channel(
  543. await self._channel.send(
  544. "goForward",
  545. self._timeout_settings.navigation_timeout,
  546. locals_to_params(locals()),
  547. )
  548. )
  549. async def request_gc(self) -> None:
  550. await self._channel.send("requestGC", None)
  551. async def emulate_media(
  552. self,
  553. media: Literal["null", "print", "screen"] = None,
  554. colorScheme: ColorScheme = None,
  555. reducedMotion: ReducedMotion = None,
  556. forcedColors: ForcedColors = None,
  557. contrast: Contrast = None,
  558. ) -> None:
  559. params = locals_to_params(locals())
  560. if "media" in params:
  561. params["media"] = "no-override" if params["media"] == "null" else media
  562. if "colorScheme" in params:
  563. params["colorScheme"] = (
  564. "no-override" if params["colorScheme"] == "null" else colorScheme
  565. )
  566. if "reducedMotion" in params:
  567. params["reducedMotion"] = (
  568. "no-override" if params["reducedMotion"] == "null" else reducedMotion
  569. )
  570. if "forcedColors" in params:
  571. params["forcedColors"] = (
  572. "no-override" if params["forcedColors"] == "null" else forcedColors
  573. )
  574. if "contrast" in params:
  575. params["contrast"] = (
  576. "no-override" if params["contrast"] == "null" else contrast
  577. )
  578. await self._channel.send("emulateMedia", None, params)
  579. async def set_viewport_size(self, viewportSize: ViewportSize) -> None:
  580. self._viewport_size = viewportSize
  581. await self._channel.send(
  582. "setViewportSize",
  583. None,
  584. locals_to_params(locals()),
  585. )
  586. @property
  587. def viewport_size(self) -> Optional[ViewportSize]:
  588. return self._viewport_size
  589. async def bring_to_front(self) -> None:
  590. await self._channel.send("bringToFront", None)
  591. async def add_init_script(
  592. self, script: str = None, path: Union[str, Path] = None
  593. ) -> None:
  594. if path:
  595. script = add_source_url_to_script(
  596. (await async_readfile(path)).decode(), path
  597. )
  598. if not isinstance(script, str):
  599. raise Error("Either path or script parameter must be specified")
  600. await self._channel.send("addInitScript", None, dict(source=script))
  601. async def route(
  602. self, url: URLMatch, handler: RouteHandlerCallback, times: int = None
  603. ) -> None:
  604. self._routes.insert(
  605. 0,
  606. RouteHandler(
  607. self._browser_context._base_url,
  608. url,
  609. handler,
  610. True if self._dispatcher_fiber else False,
  611. times,
  612. ),
  613. )
  614. await self._update_interception_patterns()
  615. async def unroute(
  616. self, url: URLMatch, handler: Optional[RouteHandlerCallback] = None
  617. ) -> None:
  618. removed = []
  619. remaining = []
  620. for route in self._routes:
  621. if route.url != url or (handler and route.handler != handler):
  622. remaining.append(route)
  623. else:
  624. removed.append(route)
  625. await self._unroute_internal(removed, remaining, "default")
  626. async def _unroute_internal(
  627. self,
  628. removed: List[RouteHandler],
  629. remaining: List[RouteHandler],
  630. behavior: Literal["default", "ignoreErrors", "wait"] = None,
  631. ) -> None:
  632. self._routes = remaining
  633. if behavior is not None and behavior != "default":
  634. await asyncio.gather(
  635. *map(
  636. lambda route: route.stop(behavior), # type: ignore
  637. removed,
  638. )
  639. )
  640. await self._update_interception_patterns()
  641. async def route_web_socket(
  642. self, url: URLMatch, handler: WebSocketRouteHandlerCallback
  643. ) -> None:
  644. self._web_socket_routes.insert(
  645. 0,
  646. WebSocketRouteHandler(self._browser_context._base_url, url, handler),
  647. )
  648. await self._update_web_socket_interception_patterns()
  649. def _dispose_har_routers(self) -> None:
  650. for router in self._har_routers:
  651. router.dispose()
  652. self._har_routers = []
  653. async def unroute_all(
  654. self, behavior: Literal["default", "ignoreErrors", "wait"] = None
  655. ) -> None:
  656. await self._unroute_internal(self._routes, [], behavior)
  657. self._dispose_har_routers()
  658. async def route_from_har(
  659. self,
  660. har: Union[Path, str],
  661. url: Union[Pattern[str], str] = None,
  662. notFound: RouteFromHarNotFoundPolicy = None,
  663. update: bool = None,
  664. updateContent: Literal["attach", "embed"] = None,
  665. updateMode: HarMode = None,
  666. ) -> None:
  667. if update:
  668. await self._browser_context._record_into_har(
  669. har=har,
  670. page=self,
  671. url=url,
  672. update_content=updateContent,
  673. update_mode=updateMode,
  674. )
  675. return
  676. router = await HarRouter.create(
  677. local_utils=self._connection.local_utils,
  678. file=str(har),
  679. not_found_action=notFound or "abort",
  680. url_matcher=url,
  681. )
  682. self._har_routers.append(router)
  683. await router.add_page_route(self)
  684. async def _update_interception_patterns(self) -> None:
  685. patterns = RouteHandler.prepare_interception_patterns(self._routes)
  686. await self._channel.send(
  687. "setNetworkInterceptionPatterns",
  688. None,
  689. {"patterns": patterns},
  690. )
  691. async def _update_web_socket_interception_patterns(self) -> None:
  692. patterns = WebSocketRouteHandler.prepare_interception_patterns(
  693. self._web_socket_routes
  694. )
  695. await self._channel.send(
  696. "setWebSocketInterceptionPatterns",
  697. None,
  698. {"patterns": patterns},
  699. )
  700. async def screenshot(
  701. self,
  702. timeout: float = None,
  703. type: Literal["jpeg", "png"] = None,
  704. path: Union[str, Path] = None,
  705. quality: int = None,
  706. omitBackground: bool = None,
  707. fullPage: bool = None,
  708. clip: FloatRect = None,
  709. animations: Literal["allow", "disabled"] = None,
  710. caret: Literal["hide", "initial"] = None,
  711. scale: Literal["css", "device"] = None,
  712. mask: Sequence["Locator"] = None,
  713. maskColor: str = None,
  714. style: str = None,
  715. ) -> bytes:
  716. params = locals_to_params(locals())
  717. if "path" in params:
  718. if "type" not in params:
  719. params["type"] = determine_screenshot_type(params["path"])
  720. del params["path"]
  721. if "mask" in params:
  722. params["mask"] = list(
  723. map(
  724. lambda locator: (
  725. {
  726. "frame": locator._frame._channel,
  727. "selector": locator._selector,
  728. }
  729. ),
  730. params["mask"],
  731. )
  732. )
  733. encoded_binary = await self._channel.send(
  734. "screenshot", self._timeout_settings.timeout, params
  735. )
  736. decoded_binary = base64.b64decode(encoded_binary)
  737. if path:
  738. make_dirs_for_file(path)
  739. await async_writefile(path, decoded_binary)
  740. return decoded_binary
  741. async def title(self) -> str:
  742. return await self._main_frame.title()
  743. async def close(self, runBeforeUnload: bool = None, reason: str = None) -> None:
  744. self._close_reason = reason
  745. self._close_was_called = True
  746. try:
  747. await self._channel.send("close", None, locals_to_params(locals()))
  748. if self._owned_context:
  749. await self._owned_context.close()
  750. except Exception as e:
  751. if not is_target_closed_error(e) and not runBeforeUnload:
  752. raise e
  753. def is_closed(self) -> bool:
  754. return self._is_closed
  755. async def click(
  756. self,
  757. selector: str,
  758. modifiers: Sequence[KeyboardModifier] = None,
  759. position: Position = None,
  760. delay: float = None,
  761. button: MouseButton = None,
  762. clickCount: int = None,
  763. timeout: float = None,
  764. force: bool = None,
  765. noWaitAfter: bool = None,
  766. trial: bool = None,
  767. strict: bool = None,
  768. ) -> None:
  769. return await self._main_frame._click(**locals_to_params(locals()))
  770. async def dblclick(
  771. self,
  772. selector: str,
  773. modifiers: Sequence[KeyboardModifier] = None,
  774. position: Position = None,
  775. delay: float = None,
  776. button: MouseButton = None,
  777. timeout: float = None,
  778. force: bool = None,
  779. noWaitAfter: bool = None,
  780. strict: bool = None,
  781. trial: bool = None,
  782. ) -> None:
  783. return await self._main_frame.dblclick(**locals_to_params(locals()))
  784. async def tap(
  785. self,
  786. selector: str,
  787. modifiers: Sequence[KeyboardModifier] = None,
  788. position: Position = None,
  789. timeout: float = None,
  790. force: bool = None,
  791. noWaitAfter: bool = None,
  792. strict: bool = None,
  793. trial: bool = None,
  794. ) -> None:
  795. return await self._main_frame.tap(**locals_to_params(locals()))
  796. async def fill(
  797. self,
  798. selector: str,
  799. value: str,
  800. timeout: float = None,
  801. noWaitAfter: bool = None,
  802. strict: bool = None,
  803. force: bool = None,
  804. ) -> None:
  805. return await self._main_frame.fill(**locals_to_params(locals()))
  806. def locator(
  807. self,
  808. selector: str,
  809. hasText: Union[str, Pattern[str]] = None,
  810. hasNotText: Union[str, Pattern[str]] = None,
  811. has: "Locator" = None,
  812. hasNot: "Locator" = None,
  813. ) -> "Locator":
  814. return self._main_frame.locator(
  815. selector,
  816. hasText=hasText,
  817. hasNotText=hasNotText,
  818. has=has,
  819. hasNot=hasNot,
  820. )
  821. def get_by_alt_text(
  822. self, text: Union[str, Pattern[str]], exact: bool = None
  823. ) -> "Locator":
  824. return self._main_frame.get_by_alt_text(text, exact=exact)
  825. def get_by_label(
  826. self, text: Union[str, Pattern[str]], exact: bool = None
  827. ) -> "Locator":
  828. return self._main_frame.get_by_label(text, exact=exact)
  829. def get_by_placeholder(
  830. self, text: Union[str, Pattern[str]], exact: bool = None
  831. ) -> "Locator":
  832. return self._main_frame.get_by_placeholder(text, exact=exact)
  833. def get_by_role(
  834. self,
  835. role: AriaRole,
  836. checked: bool = None,
  837. disabled: bool = None,
  838. expanded: bool = None,
  839. includeHidden: bool = None,
  840. level: int = None,
  841. name: Union[str, Pattern[str]] = None,
  842. pressed: bool = None,
  843. selected: bool = None,
  844. exact: bool = None,
  845. ) -> "Locator":
  846. return self._main_frame.get_by_role(
  847. role,
  848. checked=checked,
  849. disabled=disabled,
  850. expanded=expanded,
  851. includeHidden=includeHidden,
  852. level=level,
  853. name=name,
  854. pressed=pressed,
  855. selected=selected,
  856. exact=exact,
  857. )
  858. def get_by_test_id(self, testId: Union[str, Pattern[str]]) -> "Locator":
  859. return self._main_frame.get_by_test_id(testId)
  860. def get_by_text(
  861. self, text: Union[str, Pattern[str]], exact: bool = None
  862. ) -> "Locator":
  863. return self._main_frame.get_by_text(text, exact=exact)
  864. def get_by_title(
  865. self, text: Union[str, Pattern[str]], exact: bool = None
  866. ) -> "Locator":
  867. return self._main_frame.get_by_title(text, exact=exact)
  868. def frame_locator(self, selector: str) -> "FrameLocator":
  869. return self.main_frame.frame_locator(selector)
  870. async def focus(
  871. self, selector: str, strict: bool = None, timeout: float = None
  872. ) -> None:
  873. return await self._main_frame.focus(**locals_to_params(locals()))
  874. async def text_content(
  875. self, selector: str, strict: bool = None, timeout: float = None
  876. ) -> Optional[str]:
  877. return await self._main_frame.text_content(**locals_to_params(locals()))
  878. async def inner_text(
  879. self, selector: str, strict: bool = None, timeout: float = None
  880. ) -> str:
  881. return await self._main_frame.inner_text(**locals_to_params(locals()))
  882. async def inner_html(
  883. self, selector: str, strict: bool = None, timeout: float = None
  884. ) -> str:
  885. return await self._main_frame.inner_html(**locals_to_params(locals()))
  886. async def get_attribute(
  887. self, selector: str, name: str, strict: bool = None, timeout: float = None
  888. ) -> Optional[str]:
  889. return await self._main_frame.get_attribute(**locals_to_params(locals()))
  890. async def hover(
  891. self,
  892. selector: str,
  893. modifiers: Sequence[KeyboardModifier] = None,
  894. position: Position = None,
  895. timeout: float = None,
  896. noWaitAfter: bool = None,
  897. force: bool = None,
  898. strict: bool = None,
  899. trial: bool = None,
  900. ) -> None:
  901. return await self._main_frame.hover(**locals_to_params(locals()))
  902. async def drag_and_drop(
  903. self,
  904. source: str,
  905. target: str,
  906. sourcePosition: Position = None,
  907. targetPosition: Position = None,
  908. force: bool = None,
  909. noWaitAfter: bool = None,
  910. timeout: float = None,
  911. strict: bool = None,
  912. trial: bool = None,
  913. steps: int = None,
  914. ) -> None:
  915. return await self._main_frame.drag_and_drop(**locals_to_params(locals()))
  916. async def select_option(
  917. self,
  918. selector: str,
  919. value: Union[str, Sequence[str]] = None,
  920. index: Union[int, Sequence[int]] = None,
  921. label: Union[str, Sequence[str]] = None,
  922. element: Union["ElementHandle", Sequence["ElementHandle"]] = None,
  923. timeout: float = None,
  924. noWaitAfter: bool = None,
  925. force: bool = None,
  926. strict: bool = None,
  927. ) -> List[str]:
  928. params = locals_to_params(locals())
  929. return await self._main_frame.select_option(**params)
  930. async def input_value(
  931. self, selector: str, strict: bool = None, timeout: float = None
  932. ) -> str:
  933. params = locals_to_params(locals())
  934. return await self._main_frame.input_value(**params)
  935. async def set_input_files(
  936. self,
  937. selector: str,
  938. files: Union[
  939. str, Path, FilePayload, Sequence[Union[str, Path]], Sequence[FilePayload]
  940. ],
  941. timeout: float = None,
  942. strict: bool = None,
  943. noWaitAfter: bool = None,
  944. ) -> None:
  945. return await self._main_frame.set_input_files(**locals_to_params(locals()))
  946. async def type(
  947. self,
  948. selector: str,
  949. text: str,
  950. delay: float = None,
  951. timeout: float = None,
  952. noWaitAfter: bool = None,
  953. strict: bool = None,
  954. ) -> None:
  955. return await self._main_frame.type(**locals_to_params(locals()))
  956. async def press(
  957. self,
  958. selector: str,
  959. key: str,
  960. delay: float = None,
  961. timeout: float = None,
  962. noWaitAfter: bool = None,
  963. strict: bool = None,
  964. ) -> None:
  965. return await self._main_frame.press(**locals_to_params(locals()))
  966. async def check(
  967. self,
  968. selector: str,
  969. position: Position = None,
  970. timeout: float = None,
  971. force: bool = None,
  972. noWaitAfter: bool = None,
  973. strict: bool = None,
  974. trial: bool = None,
  975. ) -> None:
  976. return await self._main_frame.check(**locals_to_params(locals()))
  977. async def uncheck(
  978. self,
  979. selector: str,
  980. position: Position = None,
  981. timeout: float = None,
  982. force: bool = None,
  983. noWaitAfter: bool = None,
  984. strict: bool = None,
  985. trial: bool = None,
  986. ) -> None:
  987. return await self._main_frame.uncheck(**locals_to_params(locals()))
  988. async def wait_for_timeout(self, timeout: float) -> None:
  989. await self._main_frame.wait_for_timeout(timeout)
  990. async def wait_for_function(
  991. self,
  992. expression: str,
  993. arg: Serializable = None,
  994. timeout: float = None,
  995. polling: Union[float, Literal["raf"]] = None,
  996. ) -> JSHandle:
  997. return await self._main_frame.wait_for_function(**locals_to_params(locals()))
  998. @property
  999. def workers(self) -> List["Worker"]:
  1000. return self._workers.copy()
  1001. @property
  1002. def request(self) -> "APIRequestContext":
  1003. return self.context.request
  1004. async def pause(self) -> None:
  1005. default_navigation_timeout = (
  1006. self._browser_context._timeout_settings.default_navigation_timeout()
  1007. )
  1008. default_timeout = self._browser_context._timeout_settings.default_timeout()
  1009. self._browser_context.set_default_navigation_timeout(0)
  1010. self._browser_context.set_default_timeout(0)
  1011. try:
  1012. await asyncio.wait(
  1013. [
  1014. asyncio.create_task(
  1015. self._browser_context._channel.send("pause", None)
  1016. ),
  1017. self._closed_or_crashed_future,
  1018. ],
  1019. return_when=asyncio.FIRST_COMPLETED,
  1020. )
  1021. finally:
  1022. self._browser_context._set_default_navigation_timeout_impl(
  1023. default_navigation_timeout
  1024. )
  1025. self._browser_context._set_default_timeout_impl(default_timeout)
  1026. async def pdf(
  1027. self,
  1028. scale: float = None,
  1029. displayHeaderFooter: bool = None,
  1030. headerTemplate: str = None,
  1031. footerTemplate: str = None,
  1032. printBackground: bool = None,
  1033. landscape: bool = None,
  1034. pageRanges: str = None,
  1035. format: str = None,
  1036. width: Union[str, float] = None,
  1037. height: Union[str, float] = None,
  1038. preferCSSPageSize: bool = None,
  1039. margin: PdfMargins = None,
  1040. path: Union[str, Path] = None,
  1041. outline: bool = None,
  1042. tagged: bool = None,
  1043. ) -> bytes:
  1044. params = locals_to_params(locals())
  1045. if "path" in params:
  1046. del params["path"]
  1047. encoded_binary = await self._channel.send("pdf", None, params)
  1048. decoded_binary = base64.b64decode(encoded_binary)
  1049. if path:
  1050. make_dirs_for_file(path)
  1051. await async_writefile(path, decoded_binary)
  1052. return decoded_binary
  1053. def _force_video(self) -> Video:
  1054. if not self._video:
  1055. self._video = Video(self)
  1056. return self._video
  1057. @property
  1058. def video(
  1059. self,
  1060. ) -> Optional[Video]:
  1061. # Note: we are creating Video object lazily, because we do not know
  1062. # BrowserContextOptions when constructing the page - it is assigned
  1063. # too late during launchPersistentContext.
  1064. if not self._browser_context._videos_dir:
  1065. return None
  1066. return self._force_video()
  1067. def _close_error_with_reason(self) -> TargetClosedError:
  1068. return TargetClosedError(
  1069. self._close_reason or self._browser_context._effective_close_reason()
  1070. )
  1071. def expect_event(
  1072. self,
  1073. event: str,
  1074. predicate: Callable = None,
  1075. timeout: float = None,
  1076. ) -> EventContextManagerImpl:
  1077. return self._expect_event(
  1078. event, predicate, timeout, f'waiting for event "{event}"'
  1079. )
  1080. def _expect_event(
  1081. self,
  1082. event: str,
  1083. predicate: Callable = None,
  1084. timeout: float = None,
  1085. log_line: str = None,
  1086. ) -> EventContextManagerImpl:
  1087. if timeout is None:
  1088. timeout = self._timeout_settings.timeout()
  1089. waiter = Waiter(self, f"page.expect_event({event})")
  1090. waiter.reject_on_timeout(
  1091. timeout, f'Timeout {timeout}ms exceeded while waiting for event "{event}"'
  1092. )
  1093. if log_line:
  1094. waiter.log(log_line)
  1095. if event != Page.Events.Crash:
  1096. waiter.reject_on_event(self, Page.Events.Crash, Error("Page crashed"))
  1097. if event != Page.Events.Close:
  1098. waiter.reject_on_event(
  1099. self, Page.Events.Close, lambda: self._close_error_with_reason()
  1100. )
  1101. waiter.wait_for_event(self, event, predicate)
  1102. return EventContextManagerImpl(waiter.result())
  1103. def expect_console_message(
  1104. self,
  1105. predicate: Callable[[ConsoleMessage], bool] = None,
  1106. timeout: float = None,
  1107. ) -> EventContextManagerImpl[ConsoleMessage]:
  1108. return self.expect_event(Page.Events.Console, predicate, timeout)
  1109. def expect_download(
  1110. self,
  1111. predicate: Callable[[Download], bool] = None,
  1112. timeout: float = None,
  1113. ) -> EventContextManagerImpl[Download]:
  1114. return self.expect_event(Page.Events.Download, predicate, timeout)
  1115. def expect_file_chooser(
  1116. self,
  1117. predicate: Callable[[FileChooser], bool] = None,
  1118. timeout: float = None,
  1119. ) -> EventContextManagerImpl[FileChooser]:
  1120. return self.expect_event(Page.Events.FileChooser, predicate, timeout)
  1121. def expect_navigation(
  1122. self,
  1123. url: URLMatch = None,
  1124. waitUntil: DocumentLoadState = None,
  1125. timeout: float = None,
  1126. ) -> EventContextManagerImpl[Response]:
  1127. return self.main_frame.expect_navigation(url, waitUntil, timeout)
  1128. def expect_popup(
  1129. self,
  1130. predicate: Callable[["Page"], bool] = None,
  1131. timeout: float = None,
  1132. ) -> EventContextManagerImpl["Page"]:
  1133. return self.expect_event(Page.Events.Popup, predicate, timeout)
  1134. def expect_request(
  1135. self,
  1136. urlOrPredicate: URLMatchRequest,
  1137. timeout: float = None,
  1138. ) -> EventContextManagerImpl[Request]:
  1139. def my_predicate(request: Request) -> bool:
  1140. if not callable(urlOrPredicate):
  1141. return url_matches(
  1142. self._browser_context._base_url,
  1143. request.url,
  1144. urlOrPredicate,
  1145. )
  1146. return urlOrPredicate(request)
  1147. trimmed_url = trim_url(urlOrPredicate)
  1148. log_line = f"waiting for request {trimmed_url}" if trimmed_url else None
  1149. return self._expect_event(
  1150. Page.Events.Request,
  1151. predicate=my_predicate,
  1152. timeout=timeout,
  1153. log_line=log_line,
  1154. )
  1155. def expect_request_finished(
  1156. self,
  1157. predicate: Callable[["Request"], bool] = None,
  1158. timeout: float = None,
  1159. ) -> EventContextManagerImpl[Request]:
  1160. return self.expect_event(
  1161. Page.Events.RequestFinished, predicate=predicate, timeout=timeout
  1162. )
  1163. def expect_response(
  1164. self,
  1165. urlOrPredicate: URLMatchResponse,
  1166. timeout: float = None,
  1167. ) -> EventContextManagerImpl[Response]:
  1168. def my_predicate(request: Response) -> bool:
  1169. if not callable(urlOrPredicate):
  1170. return url_matches(
  1171. self._browser_context._base_url,
  1172. request.url,
  1173. urlOrPredicate,
  1174. )
  1175. return urlOrPredicate(request)
  1176. trimmed_url = trim_url(urlOrPredicate)
  1177. log_line = f"waiting for response {trimmed_url}" if trimmed_url else None
  1178. return self._expect_event(
  1179. Page.Events.Response,
  1180. predicate=my_predicate,
  1181. timeout=timeout,
  1182. log_line=log_line,
  1183. )
  1184. def expect_websocket(
  1185. self,
  1186. predicate: Callable[["WebSocket"], bool] = None,
  1187. timeout: float = None,
  1188. ) -> EventContextManagerImpl["WebSocket"]:
  1189. return self.expect_event("websocket", predicate, timeout)
  1190. def expect_worker(
  1191. self,
  1192. predicate: Callable[["Worker"], bool] = None,
  1193. timeout: float = None,
  1194. ) -> EventContextManagerImpl["Worker"]:
  1195. return self.expect_event("worker", predicate, timeout)
  1196. async def set_checked(
  1197. self,
  1198. selector: str,
  1199. checked: bool,
  1200. position: Position = None,
  1201. timeout: float = None,
  1202. force: bool = None,
  1203. noWaitAfter: bool = None,
  1204. strict: bool = None,
  1205. trial: bool = None,
  1206. ) -> None:
  1207. if checked:
  1208. await self.check(
  1209. selector=selector,
  1210. position=position,
  1211. timeout=timeout,
  1212. force=force,
  1213. strict=strict,
  1214. trial=trial,
  1215. )
  1216. else:
  1217. await self.uncheck(
  1218. selector=selector,
  1219. position=position,
  1220. timeout=timeout,
  1221. force=force,
  1222. strict=strict,
  1223. trial=trial,
  1224. )
  1225. async def add_locator_handler(
  1226. self,
  1227. locator: "Locator",
  1228. handler: Union[Callable[["Locator"], Any], Callable[[], Any]],
  1229. noWaitAfter: bool = None,
  1230. times: int = None,
  1231. ) -> None:
  1232. if locator._frame != self._main_frame:
  1233. raise Error("Locator must belong to the main frame of this page")
  1234. if times == 0:
  1235. return
  1236. uid = await self._channel.send(
  1237. "registerLocatorHandler",
  1238. None,
  1239. {
  1240. "selector": locator._selector,
  1241. "noWaitAfter": noWaitAfter,
  1242. },
  1243. )
  1244. self._locator_handlers[uid] = LocatorHandler(
  1245. handler=handler, times=times, locator=locator
  1246. )
  1247. async def _on_locator_handler_triggered(self, uid: str) -> None:
  1248. remove = False
  1249. try:
  1250. handler = self._locator_handlers.get(uid)
  1251. if handler and handler.times != 0:
  1252. if handler.times is not None:
  1253. handler.times -= 1
  1254. if self._dispatcher_fiber:
  1255. handler_finished_future = self._loop.create_future()
  1256. def _handler() -> None:
  1257. try:
  1258. handler()
  1259. handler_finished_future.set_result(None)
  1260. except Exception as e:
  1261. handler_finished_future.set_exception(e)
  1262. g = LocatorHandlerGreenlet(_handler)
  1263. g.switch()
  1264. await handler_finished_future
  1265. else:
  1266. coro_or_future = handler()
  1267. if coro_or_future:
  1268. await coro_or_future
  1269. remove = handler.times == 0
  1270. finally:
  1271. if remove:
  1272. del self._locator_handlers[uid]
  1273. try:
  1274. await self._connection.wrap_api_call(
  1275. lambda: self._channel.send(
  1276. "resolveLocatorHandlerNoReply",
  1277. None,
  1278. {"uid": uid, "remove": remove},
  1279. ),
  1280. is_internal=True,
  1281. )
  1282. except Error:
  1283. pass
  1284. async def remove_locator_handler(self, locator: "Locator") -> None:
  1285. for uid, data in self._locator_handlers.copy().items():
  1286. if data.locator._equals(locator):
  1287. del self._locator_handlers[uid]
  1288. self._channel.send_no_reply(
  1289. "unregisterLocatorHandler",
  1290. None,
  1291. {"uid": uid},
  1292. )
  1293. async def requests(self) -> List[Request]:
  1294. request_objects = await self._channel.send("requests", None)
  1295. return [from_channel(r) for r in request_objects]
  1296. async def console_messages(self) -> List[ConsoleMessage]:
  1297. message_dicts = await self._channel.send("consoleMessages", None)
  1298. return [
  1299. ConsoleMessage(
  1300. {**event, "page": self._channel}, self._loop, self._dispatcher_fiber
  1301. )
  1302. for event in message_dicts
  1303. ]
  1304. async def page_errors(self) -> List[Error]:
  1305. error_objects = await self._channel.send("pageErrors", None)
  1306. return [parse_error(error["error"]) for error in error_objects]
  1307. class Worker(ChannelOwner):
  1308. Events = SimpleNamespace(Close="close", Console="console")
  1309. def __init__(
  1310. self, parent: ChannelOwner, type: str, guid: str, initializer: Dict
  1311. ) -> None:
  1312. super().__init__(parent, type, guid, initializer)
  1313. self._set_event_to_subscription_mapping({Worker.Events.Console: "console"})
  1314. self._channel.on("close", lambda _: self._on_close())
  1315. self._page: Optional[Page] = None
  1316. self._context: Optional["BrowserContext"] = None
  1317. def __repr__(self) -> str:
  1318. return f"<Worker url={self.url!r}>"
  1319. def _on_close(self) -> None:
  1320. if self._page:
  1321. self._page._workers.remove(self)
  1322. if self._context:
  1323. self._context._service_workers.remove(self)
  1324. self.emit(Worker.Events.Close, self)
  1325. @property
  1326. def url(self) -> str:
  1327. return self._initializer["url"]
  1328. async def evaluate(self, expression: str, arg: Serializable = None) -> Any:
  1329. return parse_result(
  1330. await self._channel.send(
  1331. "evaluateExpression",
  1332. None,
  1333. dict(
  1334. expression=expression,
  1335. arg=serialize_argument(arg),
  1336. ),
  1337. )
  1338. )
  1339. async def evaluate_handle(
  1340. self, expression: str, arg: Serializable = None
  1341. ) -> JSHandle:
  1342. return from_channel(
  1343. await self._channel.send(
  1344. "evaluateExpressionHandle",
  1345. None,
  1346. dict(
  1347. expression=expression,
  1348. arg=serialize_argument(arg),
  1349. ),
  1350. )
  1351. )
  1352. def expect_event(
  1353. self,
  1354. event: str,
  1355. predicate: Callable = None,
  1356. timeout: float = None,
  1357. ) -> EventContextManagerImpl:
  1358. if timeout is None:
  1359. if self._page:
  1360. timeout = self._page._timeout_settings.timeout()
  1361. elif self._context:
  1362. timeout = self._context._timeout_settings.timeout()
  1363. else:
  1364. timeout = 30000
  1365. waiter = Waiter(self, f"worker.expect_event({event})")
  1366. waiter.reject_on_timeout(
  1367. cast(float, timeout),
  1368. f'Timeout {timeout}ms exceeded while waiting for event "{event}"',
  1369. )
  1370. if event != Worker.Events.Close:
  1371. waiter.reject_on_event(
  1372. self, Worker.Events.Close, lambda: TargetClosedError()
  1373. )
  1374. waiter.wait_for_event(self, event, predicate)
  1375. return EventContextManagerImpl(waiter.result())
  1376. class BindingCall(ChannelOwner):
  1377. def __init__(
  1378. self, parent: ChannelOwner, type: str, guid: str, initializer: Dict
  1379. ) -> None:
  1380. super().__init__(parent, type, guid, initializer)
  1381. async def call(self, func: Callable) -> None:
  1382. try:
  1383. frame = from_channel(self._initializer["frame"])
  1384. source = dict(context=frame._page.context, page=frame._page, frame=frame)
  1385. if self._initializer.get("handle"):
  1386. result = func(source, from_channel(self._initializer["handle"]))
  1387. else:
  1388. func_args = list(map(parse_result, self._initializer["args"]))
  1389. result = func(source, *func_args)
  1390. if inspect.iscoroutine(result):
  1391. result = await result
  1392. await self._channel.send(
  1393. "resolve", None, dict(result=serialize_argument(result))
  1394. )
  1395. except Exception as e:
  1396. tb = sys.exc_info()[2]
  1397. asyncio.create_task(
  1398. self._channel.send(
  1399. "reject", None, dict(error=dict(error=serialize_error(e, tb)))
  1400. )
  1401. )
  1402. def trim_url(param: Union[URLMatchRequest, URLMatchResponse]) -> Optional[str]:
  1403. if isinstance(param, re.Pattern):
  1404. return trim_end(param.pattern)
  1405. if isinstance(param, str):
  1406. return trim_end(param)
  1407. return None
  1408. def trim_end(s: str) -> str:
  1409. if len(s) > 50:
  1410. return s[:50] + "\u2026"
  1411. return s