async def wait_for_event(
    target: Union[Page, "BrowserContext"],
    timeout_settings: TimeoutSettings,
    event: str,
    predicate: Callable[[Any], bool] = None,
    timeout: int = None,
) -> Any:
    if timeout is None:
        timeout = timeout_settings.timeout()
    if timeout == 0:
        timeout = 3600 * 24 * 7 * 30 * 365
    timeout_future: asyncio.Future = asyncio.ensure_future(
        asyncio.sleep(timeout / 1000))

    future = target._scope._loop.create_future()

    def listener(e: Any = None) -> None:
        if not predicate or predicate(e):
            future.set_result(e)

    target.on(event, listener)

    pending_event = PendingWaitEvent(event, future, timeout_future)
    target._pending_wait_for_events.append(pending_event)
    done, _ = await asyncio.wait({timeout_future, future},
                                 return_when=asyncio.FIRST_COMPLETED)
    target.remove_listener(event, listener)
    target._pending_wait_for_events.remove(pending_event)
    if future in done:
        timeout_future.cancel()
        return future.result()
    raise TimeoutError("Timeout exceeded")
Пример #2
0
class Page(ChannelOwner):

    Events = SimpleNamespace(
        Close="close",
        Crash="crash",
        Console="console",
        Dialog="dialog",
        Download="download",
        FileChooser="filechooser",
        DOMContentLoaded="domcontentloaded",
        PageError="pageerror",
        Request="request",
        Response="response",
        RequestFailed="requestfailed",
        RequestFinished="requestfinished",
        FrameAttached="frameattached",
        FrameDetached="framedetached",
        FrameNavigated="framenavigated",
        Load="load",
        Popup="popup",
        Worker="worker",
    )
    accessibility: Accessibility
    keyboard: Keyboard
    mouse: Mouse

    def __init__(
        self, parent: ChannelOwner, type: str, guid: str, initializer: Dict
    ) -> None:
        super().__init__(parent, type, guid, initializer)
        self.accessibility = Accessibility(self._channel)
        self.keyboard = Keyboard(self._channel)
        self.mouse = Mouse(self._channel)

        self._main_frame: Frame = from_channel(initializer["mainFrame"])
        self._main_frame._page = self
        self._frames = [self._main_frame]
        self._viewport_size = initializer.get("viewportSize")
        self._is_closed = False
        self._workers: List[Worker] = []
        self._bindings: Dict[str, Any] = {}
        self._pending_wait_for_events: List[PendingWaitEvent] = []
        self._routes: List[RouteHandlerEntry] = []
        self._owned_context: Optional["BrowserContext"] = None
        self._timeout_settings: TimeoutSettings = TimeoutSettings(None)

        self._channel.on(
            "bindingCall",
            lambda params: self._on_binding(from_channel(params["binding"])),
        )
        self._channel.on("close", lambda _: self._on_close())
        self._channel.on(
            "console",
            lambda params: self.emit(
                Page.Events.Console, from_channel(params["message"])
            ),
        )
        self._channel.on("crash", lambda _: self._on_crash())
        self._channel.on(
            "dialog",
            lambda params: self.emit(
                Page.Events.Dialog, from_channel(params["dialog"])
            ),
        )
        self._channel.on(
            "domcontentloaded", lambda _: self.emit(Page.Events.DOMContentLoaded)
        )
        self._channel.on(
            "download",
            lambda params: self.emit(
                Page.Events.Download, from_channel(params["download"])
            ),
        )
        self._channel.on(
            "fileChooser",
            lambda params: self.emit(
                Page.Events.FileChooser,
                FileChooser(
                    self, from_channel(params["element"]), params["isMultiple"]
                ),
            ),
        )
        self._channel.on(
            "frameAttached",
            lambda params: self._on_frame_attached(from_channel(params["frame"])),
        )
        self._channel.on(
            "frameDetached",
            lambda params: self._on_frame_detached(from_channel(params["frame"])),
        )
        self._channel.on("load", lambda _: self.emit(Page.Events.Load))
        self._channel.on(
            "pageError",
            lambda params: self.emit(
                Page.Events.PageError, parse_error(params["error"]["error"])
            ),
        )
        self._channel.on(
            "popup",
            lambda params: self.emit(Page.Events.Popup, from_channel(params["page"])),
        )
        self._channel.on(
            "request",
            lambda params: self.emit(
                Page.Events.Request, from_channel(params["request"])
            ),
        )
        self._channel.on(
            "requestFailed",
            lambda params: self._on_request_failed(
                from_channel(params["request"]), params["failureText"]
            ),
        )
        self._channel.on(
            "requestFinished",
            lambda params: self.emit(
                Page.Events.RequestFinished, from_channel(params["request"])
            ),
        )
        self._channel.on(
            "response",
            lambda params: self.emit(
                Page.Events.Response, from_channel(params["response"])
            ),
        )
        self._channel.on(
            "route",
            lambda params: self._on_route(
                from_channel(params["route"]), from_channel(params["request"])
            ),
        )
        self._channel.on(
            "worker", lambda params: self._on_worker(from_channel(params["worker"]))
        )

    def _set_browser_context(self, context: "BrowserContext") -> None:
        self._browser_context = context
        self._timeout_settings = TimeoutSettings(context._timeout_settings)

    def _on_request_failed(self, request: Request, failure_text: str = None) -> None:
        request._failure_text = failure_text
        self.emit(Page.Events.RequestFailed, request)

    def _on_frame_attached(self, frame: Frame) -> None:
        frame._page = self
        self._frames.append(frame)
        self.emit(Page.Events.FrameAttached, frame)

    def _on_frame_detached(self, frame: Frame) -> None:
        self._frames.remove(frame)
        frame._detached = True
        self.emit(Page.Events.FrameDetached, frame)

    def _on_route(self, route: Route, request: Request) -> None:
        for handler_entry in self._routes:
            if handler_entry.matcher.matches(request.url):
                handler_entry.handler(route, request)
                return
        self._browser_context._on_route(route, request)

    def _on_binding(self, binding_call: "BindingCall") -> None:
        func = self._bindings.get(binding_call._initializer["name"])
        if func:
            asyncio.create_task(binding_call.call(func))
        self._browser_context._on_binding(binding_call)

    def _on_worker(self, worker: Worker) -> None:
        self._workers.append(worker)
        worker._page = self
        self.emit(Page.Events.Worker, worker)

    def _on_close(self) -> None:
        self._is_closed = True
        self._browser_context._pages.remove(self)
        self._reject_pending_operations(False)
        self.emit(Page.Events.Close)

    def _on_crash(self) -> None:
        self._reject_pending_operations(True)
        self.emit(Page.Events.Crash)

    def _reject_pending_operations(self, is_crash: bool) -> None:
        for pending_event in self._pending_wait_for_events:
            pending_event.reject(is_crash, "Page")

    @property
    def context(self) -> "BrowserContext":
        return self._browser_context

    async def opener(self) -> Optional["Page"]:
        return from_nullable_channel(await self._channel.send("opener"))

    @property
    def mainFrame(self) -> Frame:
        return self._main_frame

    def frame(self, name: str = None, url: URLMatch = None) -> Optional[Frame]:
        matcher = URLMatcher(url) if url else None
        for frame in self._frames:
            if name and frame.name == name:
                return frame
            if url and matcher and matcher.matches(frame.url):
                return frame
        return None

    @property
    def frames(self) -> List[Frame]:
        return self._frames.copy()

    def setDefaultNavigationTimeout(self, timeout: int) -> None:
        self._timeout_settings.set_navigation_timeout(timeout)
        self._channel.send_no_reply(
            "setDefaultNavigationTimeoutNoReply", dict(timeout=timeout)
        )

    def setDefaultTimeout(self, timeout: int) -> None:
        self._timeout_settings.set_timeout(timeout)
        self._channel.send_no_reply("setDefaultTimeoutNoReply", dict(timeout=timeout))

    async def querySelector(self, selector: str) -> Optional[ElementHandle]:
        return await self._main_frame.querySelector(selector)

    async def querySelectorAll(self, selector: str) -> List[ElementHandle]:
        return await self._main_frame.querySelectorAll(selector)

    async def waitForSelector(
        self,
        selector: str,
        timeout: int = None,
        state: Literal["attached", "detached", "visible", "hidden"] = None,
    ) -> Optional[ElementHandle]:
        return await self._main_frame.waitForSelector(**locals_to_params(locals()))

    async def dispatchEvent(
        self, selector: str, type: str, eventInit: Dict = None, timeout: int = None
    ) -> None:
        return await self._main_frame.dispatchEvent(**locals_to_params(locals()))

    async def evaluate(
        self, expression: str, arg: Any = None, force_expr: bool = False
    ) -> Any:
        return await self._main_frame.evaluate(expression, arg, force_expr=force_expr)

    async def evaluateHandle(
        self, expression: str, arg: Any = None, force_expr: bool = False
    ) -> JSHandle:
        return await self._main_frame.evaluateHandle(
            expression, arg, force_expr=force_expr
        )

    async def evalOnSelector(
        self, selector: str, expression: str, arg: Any = None, force_expr: bool = False
    ) -> Any:
        return await self._main_frame.evalOnSelector(
            selector, expression, arg, force_expr=force_expr
        )

    async def evalOnSelectorAll(
        self, selector: str, expression: str, arg: Any = None, force_expr: bool = False
    ) -> Any:
        return await self._main_frame.evalOnSelectorAll(
            selector, expression, arg, force_expr=force_expr
        )

    async def addScriptTag(
        self, url: str = None, path: str = None, content: str = None, type: str = None
    ) -> ElementHandle:
        return await self._main_frame.addScriptTag(**locals_to_params(locals()))

    async def addStyleTag(
        self, url: str = None, path: str = None, content: str = None
    ) -> ElementHandle:
        return await self._main_frame.addStyleTag(**locals_to_params(locals()))

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

    async def exposeBinding(self, name: str, binding: FunctionWithSource) -> None:
        if name in self._bindings:
            raise Error(f'Function "{name}" has been already registered')
        if name in self._browser_context._bindings:
            raise Error(
                f'Function "{name}" has been already registered in the browser context'
            )
        self._bindings[name] = binding
        await self._channel.send("exposeBinding", dict(name=name))

    async def setExtraHTTPHeaders(self, headers: Dict) -> None:
        await self._channel.send(
            "setExtraHTTPHeaders", dict(headers=serialize_headers(headers))
        )

    @property
    def url(self) -> str:
        return self._main_frame.url

    async def content(self) -> str:
        return await self._main_frame.content()

    async def setContent(
        self, html: str, timeout: int = None, waitUntil: DocumentLoadState = None,
    ) -> None:
        return await self._main_frame.setContent(**locals_to_params(locals()))

    async def goto(
        self,
        url: str,
        timeout: int = None,
        waitUntil: DocumentLoadState = "load",
        referer: str = None,
    ) -> Optional[Response]:
        return await self._main_frame.goto(**locals_to_params(locals()))

    async def reload(
        self, timeout: int = None, waitUntil: DocumentLoadState = "load",
    ) -> Optional[Response]:
        return from_nullable_channel(
            await self._channel.send("reload", locals_to_params(locals()))
        )

    async def waitForLoadState(
        self, state: DocumentLoadState = "load", timeout: int = None
    ) -> None:
        return await self._main_frame.waitForLoadState(**locals_to_params(locals()))

    async def waitForNavigation(
        self,
        timeout: int = None,
        waitUntil: DocumentLoadState = "load",
        url: str = None,  # TODO: add url, callback
    ) -> Optional[Response]:
        return await self._main_frame.waitForNavigation(**locals_to_params(locals()))

    async def waitForRequest(
        self,
        url: URLMatch = None,
        predicate: Callable[[Request], bool] = None,
        timeout: int = None,
    ) -> Optional[Request]:
        matcher = URLMatcher(url) if url else None

        def my_predicate(request: Request) -> bool:
            if matcher:
                return matcher.matches(request.url)
            if predicate:
                return predicate(request)
            return True

        return cast(
            Optional[Request],
            await self.waitForEvent(
                Page.Events.Request, predicate=my_predicate, timeout=timeout
            ),
        )

    async def waitForResponse(
        self,
        url: URLMatch = None,
        predicate: Callable[[Response], bool] = None,
        timeout: int = None,
    ) -> Optional[Response]:
        matcher = URLMatcher(url) if url else None

        def my_predicate(response: Response) -> bool:
            if matcher:
                return matcher.matches(response.url)
            if predicate:
                return predicate(response)
            return True

        return cast(
            Optional[Response],
            await self.waitForEvent(
                Page.Events.Response, predicate=my_predicate, timeout=timeout
            ),
        )

    async def waitForEvent(
        self, event: str, predicate: Callable[[Any], bool] = None, timeout: int = None
    ) -> Any:
        if timeout is None:
            timeout = self._timeout_settings.timeout()
        wait_helper = WaitHelper(self._loop)
        wait_helper.reject_on_timeout(
            timeout, f'Timeout while waiting for event "${event}"'
        )
        if event != Page.Events.Crash:
            wait_helper.reject_on_event(self, Page.Events.Crash, Error("Page crashed"))
        if event != Page.Events.Close:
            wait_helper.reject_on_event(self, Page.Events.Close, Error("Page closed"))
        return await wait_helper.wait_for_event(self, event, predicate)

    async def goBack(
        self, timeout: int = None, waitUntil: DocumentLoadState = None,
    ) -> Optional[Response]:
        return from_nullable_channel(
            await self._channel.send("goBack", locals_to_params(locals()))
        )

    async def goForward(
        self, timeout: int = None, waitUntil: DocumentLoadState = None,
    ) -> Optional[Response]:
        return from_nullable_channel(
            await self._channel.send("goForward", locals_to_params(locals()))
        )

    async def emulateMedia(
        self, media: Literal["screen", "print"] = None, colorScheme: ColorScheme = None,
    ) -> None:
        await self._channel.send("emulateMedia", locals_to_params(locals()))

    async def setViewportSize(self, width: int, height: int) -> None:
        self._viewport_size = dict(width=width, height=height)
        await self._channel.send(
            "setViewportSize", dict(viewportSize=locals_to_params(locals()))
        )

    def viewportSize(self) -> Optional[Viewport]:
        return self._viewport_size

    async def addInitScript(self, source: str = None, path: str = None) -> None:
        if path:
            with open(path, "r") as file:
                source = file.read()
        if not isinstance(source, str):
            raise Error("Either path or source parameter must be specified")
        await self._channel.send("addInitScript", dict(source=source))

    async def route(self, url: URLMatch, handler: RouteHandler) -> None:
        self._routes.append(RouteHandlerEntry(URLMatcher(url), handler))
        if len(self._routes) == 1:
            await self._channel.send(
                "setNetworkInterceptionEnabled", dict(enabled=True)
            )

    async def unroute(
        self, url: URLMatch, handler: Optional[RouteHandler] = None
    ) -> None:
        self._routes = list(
            filter(
                lambda r: r.matcher.match != url or (handler and r.handler != handler),
                self._routes,
            )
        )
        if len(self._routes) == 0:
            await self._channel.send(
                "setNetworkInterceptionEnabled", dict(enabled=False)
            )

    async def screenshot(
        self,
        timeout: int = None,
        type: Literal["png", "jpeg"] = None,
        path: str = None,
        quality: int = None,
        omitBackground: bool = None,
        fullPage: bool = None,
        clip: Dict = None,
    ) -> bytes:
        binary = await self._channel.send("screenshot", locals_to_params(locals()))
        return base64.b64decode(binary)

    async def title(self) -> str:
        return await self._main_frame.title()

    async def close(self, runBeforeUnload: bool = None) -> None:
        await self._channel.send("close", locals_to_params(locals()))
        if self._owned_context:
            await self._owned_context.close()

    def isClosed(self) -> bool:
        return self._is_closed

    async def click(
        self,
        selector: str,
        modifiers: List[KeyboardModifier] = None,
        position: Dict = None,
        delay: int = None,
        button: MouseButton = None,
        clickCount: int = None,
        timeout: int = None,
        force: bool = None,
        noWaitAfter: bool = None,
    ) -> None:
        return await self._main_frame.click(**locals_to_params(locals()))

    async def dblclick(
        self,
        selector: str,
        modifiers: List[KeyboardModifier] = None,
        position: Dict = None,
        delay: int = None,
        button: MouseButton = None,
        timeout: int = None,
        force: bool = None,
    ) -> None:
        return await self._main_frame.dblclick(**locals_to_params(locals()))

    async def fill(
        self, selector: str, value: str, timeout: int = None, noWaitAfter: bool = None
    ) -> None:
        return await self._main_frame.fill(**locals_to_params(locals()))

    async def focus(self, selector: str, timeout: int = None) -> None:
        return await self._main_frame.focus(**locals_to_params(locals()))

    async def textContent(self, selector: str, timeout: int = None) -> str:
        return await self._main_frame.textContent(**locals_to_params(locals()))

    async def innerText(self, selector: str, timeout: int = None) -> str:
        return await self._main_frame.innerText(**locals_to_params(locals()))

    async def innerHTML(self, selector: str, timeout: int = None) -> str:
        return await self._main_frame.innerHTML(**locals_to_params(locals()))

    async def getAttribute(self, selector: str, name: str, timeout: int = None) -> str:
        return await self._main_frame.getAttribute(**locals_to_params(locals()))

    async def hover(
        self,
        selector: str,
        modifiers: List[KeyboardModifier] = None,
        position: Dict = None,
        timeout: int = None,
        force: bool = None,
    ) -> None:
        return await self._main_frame.hover(**locals_to_params(locals()))

    async def selectOption(
        self,
        selector: str,
        values: ValuesToSelect,
        timeout: int = None,
        noWaitAfter: bool = None,
    ) -> List[str]:
        params = locals_to_params(locals())
        if "values" not in params:
            params["values"] = None
        return await self._main_frame.selectOption(**params)

    async def setInputFiles(
        self,
        selector: str,
        files: Union[str, FilePayload, List[str], List[FilePayload]],
        timeout: int = None,
        noWaitAfter: bool = None,
    ) -> None:
        return await self._main_frame.setInputFiles(**locals_to_params(locals()))

    async def type(
        self,
        selector: str,
        text: str,
        delay: int = None,
        timeout: int = None,
        noWaitAfter: bool = None,
    ) -> None:
        return await self._main_frame.type(**locals_to_params(locals()))

    async def press(
        self,
        selector: str,
        key: str,
        delay: int = None,
        timeout: int = None,
        noWaitAfter: bool = None,
    ) -> None:
        return await self._main_frame.press(**locals_to_params(locals()))

    async def check(
        self,
        selector: str,
        timeout: int = None,
        force: bool = None,
        noWaitAfter: bool = None,
    ) -> None:
        return await self._main_frame.check(**locals_to_params(locals()))

    async def uncheck(
        self,
        selector: str,
        timeout: int = None,
        force: bool = None,
        noWaitAfter: bool = None,
    ) -> None:
        return await self._main_frame.uncheck(**locals_to_params(locals()))

    async def waitForTimeout(self, timeout: int) -> Awaitable[None]:
        return await self._main_frame.waitForTimeout(timeout)

    async def waitForFunction(
        self,
        expression: str,
        arg: Any = None,
        force_expr: bool = False,
        timeout: int = None,
        polling: Union[int, Literal["raf"]] = None,
    ) -> JSHandle:
        if not is_function_body(expression):
            force_expr = True
        return await self._main_frame.waitForFunction(**locals_to_params(locals()))

    @property
    def workers(self) -> List[Worker]:
        return self._workers.copy()

    # on(event: str | symbol, listener: Listener): self {
    #   if (event === Page.Events.FileChooser) {
    #     if (!self.listenerCount(event))
    #       self._channel.setFileChooserInterceptedNoReply({ intercepted: True });
    #   }
    #   super.on(event, listener);
    #   return self;
    # }

    # removeListener(event: str | symbol, listener: Listener): self {
    #   super.removeListener(event, listener);
    #   if (event === Page.Events.FileChooser && !self.listenerCount(event))
    #     self._channel.setFileChooserInterceptedNoReply({ intercepted: False });
    #   return self;
    # }

    async def pdf(
        self,
        scale: int = None,
        displayHeaderFooter: bool = None,
        headerTemplate: str = None,
        footerTemplate: str = None,
        printBackground: bool = None,
        landscape: bool = None,
        pageRanges: str = None,
        format: str = None,
        width: Union[str, float] = None,
        height: Union[str, float] = None,
        preferCSSPageSize: bool = None,
        margin: Dict = None,
        path: str = None,
    ) -> bytes:
        params = locals_to_params(locals())
        del params["path"]
        encoded_binary = await self._channel.send("pdf", params)
        decoded_binary = base64.b64decode(encoded_binary)
        if path:
            with open(path, "wb") as fd:
                fd.write(decoded_binary)
        return decoded_binary

    def expect_event(
        self, event: str, predicate: Callable[[Any], bool] = None, timeout: int = None,
    ) -> EventContextManagerImpl:
        return EventContextManagerImpl(self, event, predicate, timeout)

    def expect_console_message(
        self, predicate: Callable[[ConsoleMessage], bool] = None, timeout: int = None,
    ) -> EventContextManagerImpl[ConsoleMessage]:
        return EventContextManagerImpl(self, "console", predicate, timeout)

    def expect_dialog(
        self, predicate: Callable[[Dialog], bool] = None, timeout: int = None,
    ) -> EventContextManagerImpl[Dialog]:
        return EventContextManagerImpl(self, "dialog", predicate, timeout)

    def expect_download(
        self, predicate: Callable[[Download], bool] = None, timeout: int = None,
    ) -> EventContextManagerImpl[Download]:
        return EventContextManagerImpl(self, "download", predicate, timeout)

    def expect_file_chooser(
        self, predicate: Callable[[FileChooser], bool] = None, timeout: int = None,
    ) -> EventContextManagerImpl[FileChooser]:
        return EventContextManagerImpl(self, "filechooser", predicate, timeout)

    def expect_request(
        self, predicate: Callable[[Request], bool] = None, timeout: int = None,
    ) -> EventContextManagerImpl[Request]:
        return EventContextManagerImpl(self, "request", predicate, timeout)

    def expect_response(
        self, predicate: Callable[[Response], bool] = None, timeout: int = None,
    ) -> EventContextManagerImpl[Response]:
        return EventContextManagerImpl(self, "response", predicate, timeout)

    def expect_popup(
        self, predicate: Callable[["Page"], bool] = None, timeout: int = None,
    ) -> EventContextManagerImpl["Page"]:
        return EventContextManagerImpl(self, "popup", predicate, timeout)

    def expect_worker(
        self, predicate: Callable[[Worker], bool] = None, timeout: int = None,
    ) -> EventContextManagerImpl[Worker]:
        return EventContextManagerImpl(self, "worker", predicate, timeout)
Пример #3
0
class BrowserContext(ChannelOwner):

    Events = SimpleNamespace(
        Close="close",
        Page="page",
    )

    def __init__(self, parent: ChannelOwner, type: str, guid: str,
                 initializer: Dict) -> None:
        super().__init__(parent, type, guid, initializer)
        self._pages: List[Page] = []
        self._routes: List[RouteHandlerEntry] = []
        self._bindings: Dict[str, Any] = {}
        self._pending_wait_for_events: List[PendingWaitEvent] = []
        self._timeout_settings = TimeoutSettings(None)
        self._browser: Optional["Browser"] = None
        self._owner_page: Optional[Page] = None
        self._is_closed_or_closing = False
        self._options: Dict[str, Any] = {}

        self._channel.on(
            "bindingCall",
            lambda params: self._on_binding(from_channel(params["binding"])),
        )
        self._channel.on("close", lambda _: self._on_close())
        self._channel.on(
            "page", lambda params: self._on_page(from_channel(params["page"])))
        self._channel.on(
            "route",
            lambda params: self._on_route(from_channel(params.get("route")),
                                          from_channel(params.get("request"))),
        )

    def _on_page(self, page: Page) -> None:
        page._set_browser_context(self)
        self._pages.append(page)
        self.emit(BrowserContext.Events.Page, page)

    def _on_route(self, route: Route, request: Request) -> None:
        for handler_entry in self._routes:
            if handler_entry.matcher.matches(request.url):
                handler_entry.handler(route, request)
                return
        asyncio.create_task(route.continue_())

    def _on_binding(self, binding_call: BindingCall) -> None:
        func = self._bindings.get(binding_call._initializer["name"])
        if func is None:
            return
        asyncio.create_task(binding_call.call(func))

    def setDefaultNavigationTimeout(self, timeout: int) -> None:
        self._timeout_settings.set_navigation_timeout(timeout)
        self._channel.send_no_reply("setDefaultNavigationTimeoutNoReply",
                                    dict(timeout=timeout))

    def setDefaultTimeout(self, timeout: int) -> None:
        self._timeout_settings.set_timeout(timeout)
        self._channel.send_no_reply("setDefaultTimeoutNoReply",
                                    dict(timeout=timeout))

    @property
    def pages(self) -> List[Page]:
        return self._pages.copy()

    @property
    def browser(self) -> Optional["Browser"]:
        return self._browser

    async def newPage(self) -> Page:
        if self._owner_page:
            raise Error("Please use browser.newContext()")
        return from_channel(await self._channel.send("newPage"))

    async def cookies(self,
                      urls: Union[str, List[str]] = None) -> List[Cookie]:
        if urls is None:
            urls = []
        if not isinstance(urls, list):
            urls = [urls]
        return await self._channel.send("cookies", dict(urls=urls))

    async def addCookies(self, cookies: List[Cookie]) -> None:
        await self._channel.send("addCookies", dict(cookies=cookies))

    async def clearCookies(self) -> None:
        await self._channel.send("clearCookies")

    async def grantPermissions(self,
                               permissions: List[str],
                               origin: str = None) -> None:
        await self._channel.send("grantPermissions",
                                 locals_to_params(locals()))

    async def clearPermissions(self) -> None:
        await self._channel.send("clearPermissions")

    async def setGeolocation(self, geolocation: Optional[Geolocation]) -> None:
        await self._channel.send("setGeolocation", locals_to_params(locals()))

    async def setExtraHTTPHeaders(self, headers: Dict[str, str]) -> None:
        await self._channel.send("setExtraHTTPHeaders",
                                 dict(headers=serialize_headers(headers)))

    async def setOffline(self, offline: bool) -> None:
        await self._channel.send("setOffline", dict(offline=offline))

    async def addInitScript(self,
                            source: str = None,
                            path: Union[str, Path] = None) -> None:
        if path:
            with open(path, "r") as file:
                source = file.read()
        if not isinstance(source, str):
            raise Error("Either path or source parameter must be specified")
        await self._channel.send("addInitScript", dict(source=source))

    async def exposeBinding(self,
                            name: str,
                            binding: Callable,
                            handle: bool = None) -> None:
        for page in self._pages:
            if name in page._bindings:
                raise Error(
                    f'Function "{name}" has been already registered in one of the pages'
                )
        if name in self._bindings:
            raise Error(f'Function "{name}" has been already registered')
        self._bindings[name] = binding
        await self._channel.send("exposeBinding",
                                 dict(name=name, needsHandle=handle or False))

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

    async def route(self, url: URLMatch, handler: RouteHandler) -> None:
        self._routes.append(RouteHandlerEntry(URLMatcher(url), handler))
        if len(self._routes) == 1:
            await self._channel.send("setNetworkInterceptionEnabled",
                                     dict(enabled=True))

    async def unroute(self,
                      url: URLMatch,
                      handler: Optional[RouteHandler] = None) -> None:
        self._routes = list(
            filter(
                lambda r: r.matcher.match != url or
                (handler and r.handler != handler),
                self._routes,
            ))
        if len(self._routes) == 0:
            await self._channel.send("setNetworkInterceptionEnabled",
                                     dict(enabled=False))

    async def waitForEvent(self,
                           event: str,
                           predicate: Callable[[Any], bool] = None,
                           timeout: int = None) -> Any:
        if timeout is None:
            timeout = self._timeout_settings.timeout()
        wait_helper = WaitHelper(self._loop)
        wait_helper.reject_on_timeout(
            timeout, f'Timeout while waiting for event "${event}"')
        if event != BrowserContext.Events.Close:
            wait_helper.reject_on_event(self, BrowserContext.Events.Close,
                                        Error("Context closed"))
        return await wait_helper.wait_for_event(self, event, predicate)

    def _on_close(self) -> None:
        self._is_closed_or_closing = True
        if self._browser:
            self._browser._contexts.remove(self)

        for pending_event in self._pending_wait_for_events:
            if pending_event.event == BrowserContext.Events.Close:
                continue
            pending_event.reject(False, "Context")

        self.emit(BrowserContext.Events.Close)

    async def close(self) -> None:
        if self._is_closed_or_closing:
            return
        self._is_closed_or_closing = True
        try:
            await self._channel.send("close")
        except Exception as e:
            if not is_safe_close_error(e):
                raise e

    async def storageState(self) -> StorageState:
        return await self._channel.send_return_as_dict("storageState")

    def expect_event(
        self,
        event: str,
        predicate: Callable[[Any], bool] = None,
        timeout: int = None,
    ) -> EventContextManagerImpl:
        return EventContextManagerImpl(
            self.waitForEvent(event, predicate, timeout))

    def expect_page(
        self,
        predicate: Callable[[Page], bool] = None,
        timeout: int = None,
    ) -> EventContextManagerImpl[Page]:
        return EventContextManagerImpl(
            self.waitForEvent("page", predicate, timeout))