/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ use std::path::PathBuf; use base::id::PipelineId; use constellation_traits::ConstellationMsg; use embedder_traits::{ AllowOrDeny, AuthenticationResponse, ContextMenuResult, Cursor, FilterPattern, GamepadHapticEffectType, InputMethodType, LoadStatus, MediaSessionEvent, Notification, PermissionFeature, SimpleDialog, WebResourceRequest, WebResourceResponse, WebResourceResponseMsg, }; use ipc_channel::ipc::IpcSender; use keyboard_types::KeyboardEvent; use serde::Serialize; use url::Url; use webrender_api::units::{DeviceIntPoint, DeviceIntRect, DeviceIntSize}; use crate::responders::ServoErrorSender; use crate::{ConstellationProxy, WebView}; /// A request to navigate a [`WebView`] or one of its inner frames. This can be handled /// asynchronously. If not handled, the request will automatically be allowed. pub struct NavigationRequest { pub url: Url, pub(crate) pipeline_id: PipelineId, pub(crate) constellation_proxy: ConstellationProxy, pub(crate) response_sent: bool, } impl NavigationRequest { pub fn allow(mut self) { self.constellation_proxy .send(ConstellationMsg::AllowNavigationResponse( self.pipeline_id, true, )); self.response_sent = true; } pub fn deny(mut self) { self.constellation_proxy .send(ConstellationMsg::AllowNavigationResponse( self.pipeline_id, false, )); self.response_sent = true; } } impl Drop for NavigationRequest { fn drop(&mut self) { if !self.response_sent { self.constellation_proxy .send(ConstellationMsg::AllowNavigationResponse( self.pipeline_id, true, )); } } } /// Sends a response over an IPC channel, or a default response on [`Drop`] if no response was sent. pub(crate) struct IpcResponder { response_sender: IpcSender, response_sent: bool, /// Always present, except when taken by [`Drop`]. default_response: Option, } impl IpcResponder { pub(crate) fn new(response_sender: IpcSender, default_response: T) -> Self { Self { response_sender, response_sent: false, default_response: Some(default_response), } } pub(crate) fn send(&mut self, response: T) -> bincode::Result<()> { let result = self.response_sender.send(response); self.response_sent = true; result } pub(crate) fn into_inner(self) -> IpcSender { self.response_sender.clone() } } impl Drop for IpcResponder { fn drop(&mut self) { if !self.response_sent { let response = self .default_response .take() .expect("Guaranteed by inherent impl"); // Don’t notify embedder about send errors for the default response, // since they didn’t send anything and probably don’t care. let _ = self.response_sender.send(response); } } } /// A permissions request for a [`WebView`] The embedder should allow or deny the request, /// either by reading a cached value or querying the user for permission via the user /// interface. pub struct PermissionRequest { pub(crate) requested_feature: PermissionFeature, pub(crate) allow_deny_request: AllowOrDenyRequest, } impl PermissionRequest { pub fn feature(&self) -> PermissionFeature { self.requested_feature } pub fn allow(self) { self.allow_deny_request.allow(); } pub fn deny(self) { self.allow_deny_request.deny(); } } pub struct AllowOrDenyRequest(IpcResponder, ServoErrorSender); impl AllowOrDenyRequest { pub(crate) fn new( response_sender: IpcSender, default_response: AllowOrDeny, error_sender: ServoErrorSender, ) -> Self { Self( IpcResponder::new(response_sender, default_response), error_sender, ) } pub fn allow(mut self) { if let Err(error) = self.0.send(AllowOrDeny::Allow) { self.1.raise_response_send_error(error); } } pub fn deny(mut self) { if let Err(error) = self.0.send(AllowOrDeny::Deny) { self.1.raise_response_send_error(error); } } } /// A request to authenticate a [`WebView`] navigation. Embedders may choose to prompt /// the user to enter credentials or simply ignore this request (in which case credentials /// will not be used). pub struct AuthenticationRequest { pub(crate) url: Url, pub(crate) for_proxy: bool, pub(crate) responder: IpcResponder>, pub(crate) error_sender: ServoErrorSender, } impl AuthenticationRequest { pub(crate) fn new( url: Url, for_proxy: bool, response_sender: IpcSender>, error_sender: ServoErrorSender, ) -> Self { Self { url, for_proxy, responder: IpcResponder::new(response_sender, None), error_sender, } } /// The URL of the request that triggered this authentication. pub fn url(&self) -> &Url { &self.url } /// Whether or not this authentication request is associated with a proxy server authentication. pub fn for_proxy(&self) -> bool { self.for_proxy } /// Respond to the [`AuthenticationRequest`] with the given username and password. pub fn authenticate(mut self, username: String, password: String) { if let Err(error) = self .responder .send(Some(AuthenticationResponse { username, password })) { self.error_sender.raise_response_send_error(error); } } } /// Information related to the loading of a web resource. These are created for all HTTP requests. /// The client may choose to intercept the load of web resources and send an alternate response /// by calling [`WebResourceLoad::intercept`]. pub struct WebResourceLoad { pub request: WebResourceRequest, pub(crate) responder: IpcResponder, pub(crate) error_sender: ServoErrorSender, } impl WebResourceLoad { pub(crate) fn new( web_resource_request: WebResourceRequest, response_sender: IpcSender, error_sender: ServoErrorSender, ) -> Self { Self { request: web_resource_request, responder: IpcResponder::new(response_sender, WebResourceResponseMsg::DoNotIntercept), error_sender, } } /// The [`WebResourceRequest`] associated with this [`WebResourceLoad`]. pub fn request(&self) -> &WebResourceRequest { &self.request } /// Intercept this [`WebResourceLoad`] and control the response via the returned /// [`InterceptedWebResourceLoad`]. pub fn intercept(mut self, response: WebResourceResponse) -> InterceptedWebResourceLoad { if let Err(error) = self.responder.send(WebResourceResponseMsg::Start(response)) { self.error_sender.raise_response_send_error(error); } InterceptedWebResourceLoad { request: self.request.clone(), response_sender: self.responder.into_inner(), finished: false, error_sender: self.error_sender, } } } /// An intercepted web resource load. This struct allows the client to send an alternative response /// after calling [`WebResourceLoad::intercept`]. In order to send chunks of body data, the client /// must call [`InterceptedWebResourceLoad::send_body_data`]. When the interception is complete, the client /// should call [`InterceptedWebResourceLoad::finish`]. If neither `finish()` or `cancel()` are called, /// this interception will automatically be finished when dropped. pub struct InterceptedWebResourceLoad { pub request: WebResourceRequest, pub(crate) response_sender: IpcSender, pub(crate) finished: bool, pub(crate) error_sender: ServoErrorSender, } impl InterceptedWebResourceLoad { /// Send a chunk of response body data. It's possible to make subsequent calls to /// this method when streaming body data. pub fn send_body_data(&self, data: Vec) { if let Err(error) = self .response_sender .send(WebResourceResponseMsg::SendBodyData(data)) { self.error_sender.raise_response_send_error(error); } } /// Finish this [`InterceptedWebResourceLoad`] and complete the response. pub fn finish(mut self) { if let Err(error) = self .response_sender .send(WebResourceResponseMsg::FinishLoad) { self.error_sender.raise_response_send_error(error); } self.finished = true; } /// Cancel this [`InterceptedWebResourceLoad`], which will trigger a network error. pub fn cancel(mut self) { if let Err(error) = self .response_sender .send(WebResourceResponseMsg::CancelLoad) { self.error_sender.raise_response_send_error(error); } self.finished = true; } } impl Drop for InterceptedWebResourceLoad { fn drop(&mut self) { if !self.finished { if let Err(error) = self .response_sender .send(WebResourceResponseMsg::FinishLoad) { self.error_sender.raise_response_send_error(error); } } } } pub trait WebViewDelegate { /// The URL of the currently loaded page in this [`WebView`] has changed. The new /// URL can accessed via [`WebView::url`]. fn notify_url_changed(&self, _webview: WebView, _url: Url) {} /// The page title of the currently loaded page in this [`WebView`] has changed. The new /// title can accessed via [`WebView::page_title`]. fn notify_page_title_changed(&self, _webview: WebView, _title: Option) {} /// The status text of the currently loaded page in this [`WebView`] has changed. The new /// status text can accessed via [`WebView::status_text`]. fn notify_status_text_changed(&self, _webview: WebView, _status: Option) {} /// This [`WebView`] has either become focused or lost focus. Whether or not the /// [`WebView`] is focused can be accessed via [`WebView::focused`]. fn notify_focus_changed(&self, _webview: WebView, _focused: bool) {} /// The `LoadStatus` of the currently loading or loaded page in this [`WebView`] has changed. The new /// status can accessed via [`WebView::load_status`]. fn notify_load_status_changed(&self, _webview: WebView, _status: LoadStatus) {} /// The [`Cursor`] of the currently loaded page in this [`WebView`] has changed. The new /// cursor can accessed via [`WebView::cursor`]. fn notify_cursor_changed(&self, _webview: WebView, _: Cursor) {} /// The favicon [`Url`] of the currently loaded page in this [`WebView`] has changed. The new /// favicon [`Url`] can accessed via [`WebView::favicon_url`]. fn notify_favicon_url_changed(&self, _webview: WebView, _: Url) {} /// Notify the embedder that it needs to present a new frame. fn notify_new_frame_ready(&self, _webview: WebView) {} /// The history state has changed. // changed pattern; maybe wasteful if embedder doesn’t care? fn notify_history_changed(&self, _webview: WebView, _: Vec, _: usize) {} /// Page content has closed this [`WebView`] via `window.close()`. It's the embedder's /// responsibility to remove the [`WebView`] from the interface when this notification /// occurs. fn notify_closed(&self, _webview: WebView) {} /// A keyboard event has been sent to Servo, but remains unprocessed. This allows the /// embedding application to handle key events while first letting the [`WebView`] /// have an opportunity to handle it first. Apart from builtin keybindings, page /// content may expose custom keybindings as well. fn notify_keyboard_event(&self, _webview: WebView, _: KeyboardEvent) {} /// A pipeline in the webview panicked. First string is the reason, second one is the backtrace. fn notify_crashed(&self, _webview: WebView, _reason: String, _backtrace: Option) {} /// Notifies the embedder about media session events /// (i.e. when there is metadata for the active media session, playback state changes...). fn notify_media_session_event(&self, _webview: WebView, _event: MediaSessionEvent) {} /// A notification that the [`WebView`] has entered or exited fullscreen mode. This is an /// opportunity for the embedder to transition the containing window into or out of fullscreen /// mode and to show or hide extra UI elements. Regardless of how the notification is handled, /// the page will enter or leave fullscreen state internally according to the [Fullscreen /// API](https://fullscreen.spec.whatwg.org/). fn notify_fullscreen_state_changed(&self, _webview: WebView, _: bool) {} /// Whether or not to allow a [`WebView`] to load a URL in its main frame or one of its /// nested `