Skip to main content

embedder_traits/
lib.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5//! Types used by the embedding layer and/or exposed to the API. This crate is responsible for
6//! defining types that cross the process boundary from the embedding/rendering layer all the way
7//! to script, thus it should have very minimal dependencies on other parts of Servo. If a type
8//! is not exposed in the API or doesn't involve messages sent to the embedding/libservo layer, it
9//! is probably a better fit for the `servo_constellation_traits` crate.
10
11pub mod embedder_controls;
12pub mod geolocation;
13pub mod input_events;
14pub mod resources;
15pub mod user_contents;
16pub mod webdriver;
17
18use std::collections::HashMap;
19use std::ffi::c_void;
20use std::fmt::{Debug, Display, Error, Formatter};
21use std::hash::Hash;
22use std::ops::Range;
23use std::sync::Arc;
24
25use accesskit::TreeUpdate;
26use content_security_policy::Destination;
27use crossbeam_channel::Sender;
28use euclid::{Box2D, Point2D, Scale, Size2D, Vector2D};
29use http::{HeaderMap, Method, StatusCode};
30use log::warn;
31use malloc_size_of::malloc_size_of_is_0;
32use malloc_size_of_derive::MallocSizeOf;
33use pixels::SharedRasterImage;
34use serde::{Deserialize, Deserializer, Serialize, Serializer};
35use servo_base::Epoch;
36use servo_base::generic_channel::{
37    GenericCallback, GenericSender, GenericSharedMemory, SendResult,
38};
39use servo_base::id::{PipelineId, WebViewId};
40use servo_geometry::{DeviceIndependentIntRect, DeviceIndependentIntSize};
41use servo_url::ServoUrl;
42use strum::{EnumMessage, IntoStaticStr};
43use style::queries::values::PrefersColorScheme;
44use style_traits::CSSPixel;
45use url::Url;
46use uuid::Uuid;
47use webrender_api::ExternalScrollId;
48use webrender_api::units::{
49    DeviceIntPoint, DeviceIntRect, DeviceIntSize, DevicePixel, DevicePoint, DeviceRect,
50    DeviceVector2D, LayoutPoint, LayoutRect, LayoutSize, LayoutVector2D,
51};
52
53pub use crate::embedder_controls::*;
54pub use crate::geolocation::*;
55pub use crate::input_events::*;
56use crate::user_contents::UserContentManagerId;
57pub use crate::webdriver::*;
58
59/// A point in a `WebView`, either expressed in device pixels or page pixels.
60/// Page pixels are CSS pixels, which take into account device pixel scale,
61/// page zoom, and pinch zoom.
62#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)]
63pub enum WebViewPoint {
64    Device(DevicePoint),
65    Page(Point2D<f32, CSSPixel>),
66}
67
68impl WebViewPoint {
69    #[doc(hidden)]
70    pub fn as_device_point(&self, scale: Scale<f32, CSSPixel, DevicePixel>) -> DevicePoint {
71        match self {
72            Self::Device(point) => *point,
73            Self::Page(point) => *point * scale,
74        }
75    }
76}
77
78impl From<DevicePoint> for WebViewPoint {
79    fn from(point: DevicePoint) -> Self {
80        Self::Device(point)
81    }
82}
83
84impl From<LayoutPoint> for WebViewPoint {
85    fn from(point: LayoutPoint) -> Self {
86        Self::Page(Point2D::new(point.x, point.y))
87    }
88}
89
90impl From<Point2D<f32, CSSPixel>> for WebViewPoint {
91    fn from(point: Point2D<f32, CSSPixel>) -> Self {
92        Self::Page(point)
93    }
94}
95
96/// A rectangle in a `WebView`, either expressed in device pixels or page pixels.
97/// Page pixels are CSS pixels, which take into account device pixel scale,
98/// page zoom, and pinch zoom.
99#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)]
100pub enum WebViewRect {
101    Device(DeviceRect),
102    Page(Box2D<f32, CSSPixel>),
103}
104
105impl WebViewRect {
106    #[doc(hidden)]
107    pub fn as_device_rect(&self, scale: Scale<f32, CSSPixel, DevicePixel>) -> DeviceRect {
108        match self {
109            Self::Device(rect) => *rect,
110            Self::Page(rect) => *rect * scale,
111        }
112    }
113}
114
115impl From<DeviceRect> for WebViewRect {
116    fn from(rect: DeviceRect) -> Self {
117        Self::Device(rect)
118    }
119}
120
121impl From<LayoutRect> for WebViewRect {
122    fn from(rect: LayoutRect) -> Self {
123        Self::Page(Box2D::new(
124            Point2D::new(rect.min.x, rect.min.y),
125            Point2D::new(rect.max.x, rect.max.y),
126        ))
127    }
128}
129
130impl From<Box2D<f32, CSSPixel>> for WebViewRect {
131    fn from(rect: Box2D<f32, CSSPixel>) -> Self {
132        Self::Page(rect)
133    }
134}
135
136/// A 2D vector in a `WebView`, either expressed in device pixels or page pixels.
137/// Page pixels are CSS pixels, which take into account device pixel scale,
138/// page zoom, and pinch zoom.
139#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)]
140pub enum WebViewVector {
141    Device(DeviceVector2D),
142    Page(Vector2D<f32, CSSPixel>),
143}
144
145impl WebViewVector {
146    #[doc(hidden)]
147    pub fn as_device_vector(&self, scale: Scale<f32, CSSPixel, DevicePixel>) -> DeviceVector2D {
148        match self {
149            Self::Device(vector) => *vector,
150            Self::Page(vector) => *vector * scale,
151        }
152    }
153}
154
155impl From<DeviceVector2D> for WebViewVector {
156    fn from(vector: DeviceVector2D) -> Self {
157        Self::Device(vector)
158    }
159}
160
161impl From<LayoutVector2D> for WebViewVector {
162    fn from(vector: LayoutVector2D) -> Self {
163        Self::Page(Vector2D::new(vector.x, vector.y))
164    }
165}
166
167impl From<Vector2D<f32, CSSPixel>> for WebViewVector {
168    fn from(vector: Vector2D<f32, CSSPixel>) -> Self {
169        Self::Page(vector)
170    }
171}
172
173/// Represents the destination of a scroll operation.
174#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)]
175pub enum Scroll {
176    /// An offset to scroll by, with positive offsets revealing more content on the bottom
177    /// and right of the scrollable area.
178    Delta(WebViewVector),
179    /// Scroll to the start of the scrollable area.
180    Start,
181    /// Scroll to the end of the scrollable area.
182    End,
183}
184
185/// Tracks whether Servo isn't shutting down, is in the process of shutting down,
186/// or has finished shutting down.
187#[derive(Clone, Copy, Debug, PartialEq)]
188pub enum ShutdownState {
189    NotShuttingDown,
190    ShuttingDown,
191    FinishedShuttingDown,
192}
193
194/// A cursor for the window. This is different from a CSS cursor (see
195/// `CursorKind`) in that it has no `Auto` value.
196#[repr(u8)]
197#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, MallocSizeOf, PartialEq, Serialize)]
198pub enum Cursor {
199    None,
200    #[default]
201    Default,
202    Pointer,
203    ContextMenu,
204    Help,
205    Progress,
206    Wait,
207    Cell,
208    Crosshair,
209    Text,
210    VerticalText,
211    Alias,
212    Copy,
213    Move,
214    NoDrop,
215    NotAllowed,
216    Grab,
217    Grabbing,
218    EResize,
219    NResize,
220    NeResize,
221    NwResize,
222    SResize,
223    SeResize,
224    SwResize,
225    WResize,
226    EwResize,
227    NsResize,
228    NeswResize,
229    NwseResize,
230    ColResize,
231    RowResize,
232    AllScroll,
233    ZoomIn,
234    ZoomOut,
235}
236
237/// A way for Servo to request that the embedder wake up the main event loop.
238///
239/// A trait which embedders should implement to allow Servo to request that the
240/// embedder spin the Servo event loop on the main thread.
241pub trait EventLoopWaker: 'static + Send + Sync {
242    fn clone_box(&self) -> Box<dyn EventLoopWaker>;
243
244    /// This method is called when Servo wants the embedder to wake up the event loop.
245    ///
246    /// Note that this may be called on a different thread than the thread that was used to
247    /// start Servo. When called, the embedder is expected to call [`Servo::spin_event_loop`]
248    /// on the thread where Servo is running.
249    fn wake(&self);
250}
251
252impl Clone for Box<dyn EventLoopWaker> {
253    fn clone(&self) -> Self {
254        self.clone_box()
255    }
256}
257
258/// Sends messages to the embedder.
259pub struct GenericEmbedderProxy<T> {
260    pub sender: Sender<T>,
261    pub event_loop_waker: Box<dyn EventLoopWaker>,
262}
263
264impl<T> GenericEmbedderProxy<T> {
265    pub fn send(&self, message: T) {
266        // Send a message and kick the OS event loop awake.
267        if let Err(err) = self.sender.send(message) {
268            warn!("Failed to send response ({:?}).", err);
269        }
270        self.event_loop_waker.wake();
271    }
272}
273
274impl<T> Clone for GenericEmbedderProxy<T> {
275    fn clone(&self) -> Self {
276        Self {
277            sender: self.sender.clone(),
278            event_loop_waker: self.event_loop_waker.clone(),
279        }
280    }
281}
282
283pub type EmbedderProxy = GenericEmbedderProxy<EmbedderMsg>;
284
285/// A [`RefreshDriver`] is a trait that can be implemented by Servo embedders in
286/// order to drive let Servo know when to start preparing the next frame. For example,
287/// on systems that support Vsync notifications, an embedder may want to implement
288/// this trait to drive Servo animations via those notifications.
289pub trait RefreshDriver {
290    /// Servo will call this method when it wants to be informed of the next frame start
291    /// time. Implementors should call the callback when it is time to start preparing
292    /// the new frame.
293    ///
294    /// Multiple callbacks may be registered for the same frame. It is up to the implementation
295    /// to call *all* callbacks that have been registered since the last frame.
296    fn observe_next_frame(&self, start_frame_callback: Box<dyn Fn() + Send + 'static>);
297}
298
299/// Credentials to use in an HTTP authentication challenge.
300#[derive(Debug, Default, Deserialize, PartialEq, Serialize)]
301pub struct AuthenticationResponse {
302    /// Username for HTTP request authentication
303    pub username: String,
304    /// Password for HTTP request authentication
305    pub password: String,
306}
307
308/// A response to a request to allow or deny an action.
309#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
310pub enum AllowOrDeny {
311    Allow,
312    Deny,
313}
314
315#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
316/// Whether a protocol handler is requested to be registered or unregistered.
317pub enum RegisterOrUnregister {
318    Register,
319    Unregister,
320}
321
322/// A request from Servo to embedder to register or unregister a custom
323/// protocol handler for a scheme, typically triggered by web content.
324/// See <https://html.spec.whatwg.org/multipage/#custom-handlers>
325#[derive(Clone, Debug, Deserialize, Serialize)]
326pub struct ProtocolHandlerUpdateRegistration {
327    /// The scheme for the protocol handler.
328    pub scheme: String,
329    /// The URL to navigate to when handling requests for scheme.
330    pub url: ServoUrl,
331    /// Whether this update is to register or unregister the protocol handler.
332    pub register_or_unregister: RegisterOrUnregister,
333}
334
335/// Data about a `WebView` or `<iframe>` viewport: its size and also the
336/// HiDPI scale factor to use when rendering the contents.
337#[derive(Clone, Copy, Debug, Default, Deserialize, MallocSizeOf, PartialEq, Serialize)]
338pub struct ViewportDetails {
339    /// The size of the layout viewport.
340    pub size: Size2D<f32, CSSPixel>,
341
342    /// The scale factor to use to account for HiDPI scaling. This does not take into account
343    /// any page or pinch zoom applied by `Paint` to the contents.
344    pub hidpi_scale_factor: Scale<f32, CSSPixel, DevicePixel>,
345
346    /// The device dimensions that this viewport is displayed within.
347    pub device_size: Size2D<f32, DevicePixel>,
348}
349
350impl ViewportDetails {
351    /// Convert this [`ViewportDetails`] size to a [`LayoutSize`]. This is the same numerical
352    /// value as [`Self::size`], because a `LayoutPixel` is the same as a `CSSPixel`.
353    pub fn layout_size(&self) -> LayoutSize {
354        Size2D::from_untyped(self.size.to_untyped())
355    }
356}
357
358/// Unlike [`ScreenGeometry`], the data is in device-independent pixels
359/// to be used by DOM APIs
360#[derive(Default, Deserialize, Serialize)]
361pub struct ScreenMetrics {
362    pub screen_size: DeviceIndependentIntSize,
363    pub available_size: DeviceIndependentIntSize,
364}
365
366/// An opaque identifier for a single history traversal operation.
367#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
368pub struct TraversalId(Uuid);
369
370impl TraversalId {
371    #[expect(clippy::new_without_default)]
372    pub fn new() -> Self {
373        Self(Uuid::new_v4())
374    }
375}
376
377/// The pixel format of the buffer representing a raster image.
378#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize, MallocSizeOf)]
379pub enum PixelFormat {
380    /// Luminance channel only
381    K8,
382    /// Luminance + alpha
383    KA8,
384    /// RGB, 8 bits per channel
385    RGB8,
386    /// RGB + alpha, 8 bits per channel
387    RGBA8,
388    /// BGR + alpha, 8 bits per channel
389    BGRA8,
390}
391
392/// A raster image buffer.
393#[derive(Clone, Deserialize, Serialize, MallocSizeOf)]
394pub struct Image {
395    pub width: u32,
396    pub height: u32,
397    pub format: PixelFormat,
398    /// A shared memory block containing the data of one or more image frames.
399    #[conditional_malloc_size_of]
400    data: Arc<GenericSharedMemory>,
401    range: Range<usize>,
402}
403
404impl Image {
405    /// Creates a new [`Image`] with the given `width` and `height`.
406    ///
407    /// `data` is a shared memory block containing the pixel data of one or more image frames, in
408    /// the given `format`.
409    ///
410    /// `range` is the byte offset within `data` that is the start of the first frame.
411    pub fn new(
412        width: u32,
413        height: u32,
414        data: Arc<GenericSharedMemory>,
415        range: Range<usize>,
416        format: PixelFormat,
417    ) -> Self {
418        Self {
419            width,
420            height,
421            format,
422            data,
423            range,
424        }
425    }
426
427    /// Return the bytes belonging to the first image frame.
428    pub fn data(&self) -> &[u8] {
429        &self.data[self.range.clone()]
430    }
431}
432
433/// The severity level of a message logged by page content.
434#[derive(Clone, Debug, Deserialize, Serialize, MallocSizeOf)]
435#[serde(rename_all = "lowercase")]
436pub enum ConsoleLogLevel {
437    Log,
438    Debug,
439    Info,
440    Warn,
441    Error,
442    Trace,
443    Dir,
444}
445
446impl From<ConsoleLogLevel> for log::Level {
447    fn from(value: ConsoleLogLevel) -> Self {
448        match value {
449            ConsoleLogLevel::Log => log::Level::Info,
450            ConsoleLogLevel::Debug => log::Level::Debug,
451            ConsoleLogLevel::Info => log::Level::Info,
452            ConsoleLogLevel::Warn => log::Level::Warn,
453            ConsoleLogLevel::Error => log::Level::Error,
454            ConsoleLogLevel::Trace => log::Level::Trace,
455            ConsoleLogLevel::Dir => log::Level::Info,
456        }
457    }
458}
459
460/// Information about a single Bluetooth device.
461#[derive(Clone, Deserialize, Serialize)]
462pub struct BluetoothDeviceDescription {
463    /// The unique address of this device.
464    pub address: String,
465    /// A human-readable name for this device.
466    pub name: String,
467}
468
469/// Messages towards the embedder.
470#[derive(Deserialize, IntoStaticStr, Serialize)]
471pub enum EmbedderMsg {
472    /// A status message to be displayed by the browser chrome.
473    Status(WebViewId, Option<String>),
474    /// Alerts the embedder that the current page has changed its title.
475    ChangePageTitle(WebViewId, Option<String>),
476    /// Move the window to a point
477    MoveTo(WebViewId, DeviceIntPoint),
478    /// Resize the window to size
479    ResizeTo(WebViewId, DeviceIntSize),
480    /// Show the user a [simple dialog](https://html.spec.whatwg.org/multipage/#simple-dialogs) (`alert()`, `confirm()`,
481    /// or `prompt()`). Since their messages are controlled by web content, they should be presented to the user in a
482    /// way that makes them impossible to mistake for browser UI.
483    ShowSimpleDialog(WebViewId, SimpleDialogRequest),
484    /// Request to (un)register protocol handler by page content.
485    AllowProtocolHandlerRequest(
486        WebViewId,
487        ProtocolHandlerUpdateRegistration,
488        GenericSender<AllowOrDeny>,
489    ),
490    /// Wether or not to unload a document
491    AllowUnload(WebViewId, GenericSender<AllowOrDeny>),
492    /// Inform embedder to clear the clipboard
493    ClearClipboard(WebViewId),
494    /// Gets system clipboard contents
495    GetClipboardText(WebViewId, GenericCallback<Result<String, String>>),
496    /// Sets system clipboard contents
497    SetClipboardText(WebViewId, String),
498    /// Changes the cursor.
499    SetCursor(WebViewId, Cursor),
500    /// A favicon was detected
501    NewFavicon(WebViewId, Image),
502    /// Get the device independent window rectangle.
503    GetWindowRect(WebViewId, GenericSender<DeviceIndependentIntRect>),
504    /// Get the device independent screen size and available size.
505    GetScreenMetrics(WebViewId, GenericSender<ScreenMetrics>),
506    /// Entered or exited fullscreen.
507    NotifyFullscreenStateChanged(WebViewId, bool),
508    /// The [`LoadStatus`] of the Given `WebView` has changed.
509    NotifyLoadStatusChanged(WebViewId, LoadStatus),
510    /// Open dialog to select bluetooth device.
511    GetSelectedBluetoothDevice(
512        WebViewId,
513        Vec<BluetoothDeviceDescription>,
514        GenericSender<Option<String>>,
515    ),
516    /// Open interface to request permission specified by prompt.
517    PromptPermission(WebViewId, PermissionFeature, GenericSender<AllowOrDeny>),
518    /// Request permission without blocking the script thread. The callback is invoked with the user's
519    /// decision once it is known.
520    RequestPermission(WebViewId, PermissionFeature, GenericCallback<AllowOrDeny>),
521    /// Request a single geographic position fix. The callback is invoked exactly once.
522    RequestGeolocationPosition(
523        WebViewId,
524        GeolocationRequestOptions,
525        GenericCallback<GeolocationResult>,
526    ),
527    /// Begin delivering continuous position updates for the given watch.
528    StartGeolocationWatch(
529        GeolocationWatchId,
530        GeolocationRequestOptions,
531        GenericCallback<GeolocationResult>,
532    ),
533    /// Stop delivering updates for a watch started by [`EmbedderMsg::StartGeolocationWatch`].
534    StopGeolocationWatch(GeolocationWatchId),
535    /// Report the status of Devtools Server with a token that can be used to bypass the permission prompt.
536    OnDevtoolsStarted(Result<u16, ()>, String),
537    /// Ask the user to allow a devtools client to connect.
538    RequestDevtoolsConnection(GenericSender<AllowOrDeny>),
539    /// Request to play a haptic effect on a connected gamepad.
540    #[cfg(feature = "gamepad")]
541    PlayGamepadHapticEffect(
542        WebViewId,
543        usize,
544        GamepadHapticEffectType,
545        GenericCallback<bool>,
546    ),
547    /// Request to stop a haptic effect on a connected gamepad.
548    #[cfg(feature = "gamepad")]
549    StopGamepadHapticEffect(WebViewId, usize, GenericCallback<bool>),
550    /// Request to display a notification.
551    ShowNotification(Option<WebViewId>, Notification),
552    /// Let the embedder process a DOM Console API message.
553    /// <https://developer.mozilla.org/en-US/docs/Web/API/Console_API>
554    ShowConsoleApiMessage(Option<WebViewId>, ConsoleLogLevel, String),
555    /// Request to the embedder to display a user interace control.
556    ShowEmbedderControl(EmbedderControlId, DeviceIntRect, EmbedderControlRequest),
557    /// Request to the embedder to hide a user interface control.
558    HideEmbedderControl(EmbedderControlId),
559    /// Inform the embedding layer that a particular `InputEvent` was handled by Servo
560    /// and the embedder can continue processing it, if necessary.
561    InputEventsHandled(WebViewId, Vec<InputEventOutcome>),
562    /// Send the embedder an accessibility tree update.
563    AccessibilityTreeUpdate(WebViewId, TreeUpdate, Epoch),
564}
565
566impl Debug for EmbedderMsg {
567    fn fmt(&self, formatter: &mut Formatter) -> Result<(), Error> {
568        let string: &'static str = self.into();
569        write!(formatter, "{string}")
570    }
571}
572
573/// <https://w3c.github.io/mediasession/#mediametadata>
574#[derive(Clone, Debug, Deserialize, Serialize, MallocSizeOf)]
575pub struct MediaMetadata {
576    /// Title
577    pub title: String,
578    /// Artist
579    pub artist: String,
580    /// Album
581    pub album: String,
582}
583
584impl MediaMetadata {
585    pub fn new(title: String) -> Self {
586        Self {
587            title,
588            artist: String::new(),
589            album: String::new(),
590        }
591    }
592}
593
594/// <https://w3c.github.io/mediasession/#enumdef-mediasessionplaybackstate>
595#[repr(i32)]
596#[derive(Clone, Debug, Deserialize, Serialize)]
597pub enum MediaSessionPlaybackState {
598    /// The browsing context does not specify whether it’s playing or paused.
599    None_ = 1,
600    /// The browsing context is currently playing media and it can be paused.
601    Playing,
602    /// The browsing context has paused media and it can be resumed.
603    Paused,
604}
605
606/// <https://w3c.github.io/mediasession/#dictdef-mediapositionstate>
607#[derive(Clone, Debug, Deserialize, Serialize)]
608pub struct MediaPositionState {
609    pub duration: f64,
610    pub playback_rate: f64,
611    pub position: f64,
612}
613
614impl MediaPositionState {
615    pub fn new(duration: f64, playback_rate: f64, position: f64) -> Self {
616        Self {
617            duration,
618            playback_rate,
619            position,
620        }
621    }
622}
623
624/// Type of events sent from script to the embedder about the media session.
625#[derive(Clone, Debug, Deserialize, Serialize)]
626pub enum MediaSessionEvent {
627    /// Indicates that the media metadata is available.
628    SetMetadata(MediaMetadata),
629    /// Indicates that the playback state has changed.
630    PlaybackStateChange(MediaSessionPlaybackState),
631    /// Indicates that the position state is set.
632    SetPositionState(MediaPositionState),
633}
634
635/// Enum with variants that match the DOM PermissionName enum
636#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
637pub enum PermissionFeature {
638    Geolocation,
639    Notifications,
640    Push,
641    Midi,
642    Camera,
643    Microphone,
644    Speaker,
645    DeviceInfo,
646    BackgroundSync,
647    Bluetooth,
648    PersistentStorage,
649    ScreenWakeLock(WakeLockType),
650    Gamepad,
651}
652
653/// Used to specify the kind of input method editor appropriate to edit a field.
654/// This is a subset of htmlinputelement::InputType because some variants of InputType
655/// don't make sense in this context.
656#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
657pub enum InputMethodType {
658    Color,
659    Date,
660    DatetimeLocal,
661    Email,
662    Month,
663    Number,
664    Password,
665    Search,
666    Tel,
667    Text,
668    Time,
669    Url,
670    Week,
671}
672
673#[cfg(feature = "gamepad")]
674#[derive(Clone, Debug, Deserialize, Serialize)]
675/// <https://w3.org/TR/gamepad/#dom-gamepadhapticeffecttype-dual-rumble>
676pub struct DualRumbleEffectParams {
677    pub duration: f64,
678    pub start_delay: f64,
679    pub strong_magnitude: f64,
680    pub weak_magnitude: f64,
681}
682
683#[cfg(feature = "gamepad")]
684#[derive(Clone, Debug, Deserialize, Serialize)]
685/// <https://w3.org/TR/gamepad/#dom-gamepadhapticeffecttype>
686pub enum GamepadHapticEffectType {
687    DualRumble(DualRumbleEffectParams),
688}
689
690#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
691pub struct WebResourceRequest {
692    #[serde(
693        deserialize_with = "::hyper_serde::deserialize",
694        serialize_with = "::hyper_serde::serialize"
695    )]
696    pub method: Method,
697    #[serde(
698        deserialize_with = "::hyper_serde::deserialize",
699        serialize_with = "::hyper_serde::serialize"
700    )]
701    pub headers: HeaderMap,
702    pub url: ServoUrl,
703    pub destination: Destination,
704    pub referrer_url: Option<ServoUrl>,
705    pub is_for_main_frame: bool,
706    pub is_redirect: bool,
707}
708
709#[derive(Clone, Deserialize, Serialize)]
710pub enum WebResourceResponseMsg {
711    /// Start an interception of this web resource load. It's expected that the client subsequently
712    /// send either a `CancelLoad` or `FinishLoad` message after optionally sending chunks of body
713    /// data via `SendBodyData`.
714    Start(WebResourceResponse),
715    /// Send a chunk of body data.
716    SendBodyData(Vec<u8>),
717    /// Signal that this load has been finished by the interceptor.
718    FinishLoad,
719    /// Signal that this load has been cancelled by the interceptor.
720    CancelLoad,
721    /// Signal that this load will not be intercepted.
722    DoNotIntercept,
723}
724
725#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
726pub struct WebResourceResponse {
727    pub url: Url,
728    #[serde(
729        deserialize_with = "::hyper_serde::deserialize",
730        serialize_with = "::hyper_serde::serialize"
731    )]
732    pub headers: HeaderMap,
733    #[serde(
734        deserialize_with = "::hyper_serde::deserialize",
735        serialize_with = "::hyper_serde::serialize"
736    )]
737    pub status_code: StatusCode,
738    pub status_message: Vec<u8>,
739}
740
741impl WebResourceResponse {
742    pub fn new(url: Url) -> WebResourceResponse {
743        WebResourceResponse {
744            url,
745            headers: HeaderMap::new(),
746            status_code: StatusCode::OK,
747            status_message: b"OK".to_vec(),
748        }
749    }
750
751    pub fn headers(mut self, headers: HeaderMap) -> WebResourceResponse {
752        self.headers = headers;
753        self
754    }
755
756    pub fn status_code(mut self, status_code: StatusCode) -> WebResourceResponse {
757        self.status_code = status_code;
758        self
759    }
760
761    pub fn status_message(mut self, status_message: Vec<u8>) -> WebResourceResponse {
762        self.status_message = status_message;
763        self
764    }
765}
766
767/// The type of platform theme.
768#[derive(Clone, Copy, Debug, Deserialize, Eq, MallocSizeOf, PartialEq, Serialize)]
769pub enum Theme {
770    /// Light theme.
771    Light,
772    /// Dark theme.
773    Dark,
774}
775
776impl From<Theme> for PrefersColorScheme {
777    fn from(value: Theme) -> Self {
778        match value {
779            Theme::Light => PrefersColorScheme::Light,
780            Theme::Dark => PrefersColorScheme::Dark,
781        }
782    }
783}
784
785// The type of MediaSession action.
786/// <https://w3c.github.io/mediasession/#enumdef-mediasessionaction>
787#[derive(Clone, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize)]
788pub enum MediaSessionActionType {
789    /// The action intent is to resume playback.
790    Play,
791    /// The action intent is to pause the currently active playback.
792    Pause,
793    /// The action intent is to move the playback time backward by a short period (i.e. a few
794    /// seconds).
795    SeekBackward,
796    /// The action intent is to move the playback time forward by a short period (i.e. a few
797    /// seconds).
798    SeekForward,
799    /// The action intent is to either start the current playback from the beginning if the
800    /// playback has a notion, of beginning, or move to the previous item in the playlist if the
801    /// playback has a notion of playlist.
802    PreviousTrack,
803    /// The action is to move to the playback to the next item in the playlist if the playback has
804    /// a notion of playlist.
805    NextTrack,
806    /// The action intent is to skip the advertisement that is currently playing.
807    SkipAd,
808    /// The action intent is to stop the playback and clear the state if appropriate.
809    Stop,
810    /// The action intent is to move the playback time to a specific time.
811    SeekTo,
812}
813
814/// The status of the load in this `WebView`.
815#[repr(i32)]
816#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
817pub enum LoadStatus {
818    /// The load has started, but the headers have not yet been parsed.
819    Started,
820    /// The `<head>` tag has been parsed in the currently loading page. At this point the page's
821    /// `HTMLBodyElement` is now available in the DOM.
822    HeadParsed,
823    /// The `Document` and all subresources have loaded. This is equivalent to
824    /// `document.readyState` == `complete`.
825    /// See <https://developer.mozilla.org/en-US/docs/Web/API/Document/readyState>
826    Complete,
827}
828
829/// Data that could be used to display a desktop notification to the end user
830/// when the [Notification API](<https://notifications.spec.whatwg.org/#notifications>) is called.
831#[derive(Clone, Debug, Deserialize, Serialize)]
832pub struct Notification {
833    /// Title of the notification.
834    pub title: String,
835    /// Body string of the notification.
836    pub body: String,
837    /// An identifier tag for the notification. Notification with the same tag
838    /// can be replaced by another to avoid users' screen being filled up with similar notifications.
839    pub tag: String,
840    /// The tag for the language used in the notification's title, body, and the title of each its actions. [RFC 5646](https://datatracker.ietf.org/doc/html/rfc5646)
841    pub language: String,
842    /// A boolean value indicates the notification should remain readily available
843    /// until the end user activates or dismisses the notification.
844    pub require_interaction: bool,
845    /// When `true`, indicates no sounds or vibrations should be made. When `None`,
846    /// the device's default settings should be respected.
847    pub silent: Option<bool>,
848    /// The URL of an icon. The icon will be displayed as part of the notification.
849    pub icon_url: Option<ServoUrl>,
850    /// Icon's raw image data and metadata.
851    pub icon_resource: Option<Arc<SharedRasterImage>>,
852    /// The URL of a badge. The badge is used when there is no enough space to display the notification,
853    /// such as on a mobile device's notification bar.
854    pub badge_url: Option<ServoUrl>,
855    /// Badge's raw image data and metadata.
856    pub badge_resource: Option<Arc<SharedRasterImage>>,
857    /// The URL of an image. The image will be displayed as part of the notification.
858    pub image_url: Option<ServoUrl>,
859    /// Image's raw image data and metadata.
860    pub image_resource: Option<Arc<SharedRasterImage>>,
861    /// Actions available for users to choose from for interacting with the notification.
862    pub actions: Vec<NotificationAction>,
863}
864
865/// Actions available for users to choose from for interacting with the notification.
866#[derive(Clone, Debug, Deserialize, Serialize)]
867pub struct NotificationAction {
868    /// A string that identifies the action.
869    pub name: String,
870    /// The title string of the action to be shown to the user.
871    pub title: String,
872    /// The URL of an icon. The icon will be displayed with the action.
873    pub icon_url: Option<ServoUrl>,
874    /// Icon's raw image data and metadata.
875    pub icon_resource: Option<Arc<SharedRasterImage>>,
876}
877
878/// Information about a `WebView`'s screen geometry and offset. This is used
879/// for the [Screen](https://drafts.csswg.org/cssom-view/#the-screen-interface) CSSOM APIs
880/// and `window.screenLeft` / `window.screenX` / `window.screenTop` / `window.screenY` /
881/// `window.moveBy`/ `window.resizeBy` / `window.outerWidth` / `window.outerHeight` /
882/// `window.screen.availHeight` / `window.screen.availWidth`.
883#[derive(Clone, Copy, Debug, Default)]
884pub struct ScreenGeometry {
885    /// The size of the screen in device pixels. This will be converted to
886    /// CSS pixels based on the pixel scaling of the `WebView`.
887    pub size: DeviceIntSize,
888    /// The available size of the screen in device pixels for the purposes of
889    /// the `window.screen.availHeight` / `window.screen.availWidth`. This is the size
890    /// available for web content on the screen, and should be `size` minus any system
891    /// toolbars, docks, and interface elements. This will be converted to
892    /// CSS pixels based on the pixel scaling of the `WebView`.
893    pub available_size: DeviceIntSize,
894    /// The rectangle the `WebView`'s containing window (including OS decorations)
895    /// in device pixels for the purposes of the
896    /// `window.screenLeft`, `window.outerHeight` and similar APIs.
897    /// This will be converted to CSS pixels based on the pixel scaling of the `WebView`.
898    pub window_rect: DeviceIntRect,
899}
900
901impl From<SelectElementOption> for SelectElementOptionOrOptgroup {
902    fn from(value: SelectElementOption) -> Self {
903        Self::Option(value)
904    }
905}
906
907/// The address of a node. Layout sends these back. They must be validated via
908/// `from_untrusted_node_address` before they can be used, because we do not trust layout.
909#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
910pub struct UntrustedNodeAddress(pub *const c_void);
911
912malloc_size_of_is_0!(UntrustedNodeAddress);
913
914#[expect(unsafe_code)]
915unsafe impl Send for UntrustedNodeAddress {}
916
917impl From<style_traits::dom::OpaqueNode> for UntrustedNodeAddress {
918    fn from(o: style_traits::dom::OpaqueNode) -> Self {
919        UntrustedNodeAddress(o.0 as *const c_void)
920    }
921}
922
923impl Serialize for UntrustedNodeAddress {
924    fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
925        (self.0 as usize).serialize(s)
926    }
927}
928
929impl<'de> Deserialize<'de> for UntrustedNodeAddress {
930    fn deserialize<D: Deserializer<'de>>(d: D) -> Result<UntrustedNodeAddress, D::Error> {
931        let value: usize = Deserialize::deserialize(d)?;
932        Ok(UntrustedNodeAddress::from_id(value))
933    }
934}
935
936impl UntrustedNodeAddress {
937    /// Creates an `UntrustedNodeAddress` from the given pointer address value.
938    #[inline]
939    pub fn from_id(id: usize) -> UntrustedNodeAddress {
940        UntrustedNodeAddress(id as *const c_void)
941    }
942}
943
944/// The result of a hit test in `Paint`.
945#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
946pub struct PaintHitTestResult {
947    /// The pipeline id of the resulting item.
948    pub pipeline_id: PipelineId,
949
950    /// The hit test point in the item's viewport.
951    pub point_in_viewport: Point2D<f32, CSSPixel>,
952
953    /// The [`ExternalScrollId`] of the scroll tree node associated with this hit test item.
954    pub external_scroll_id: ExternalScrollId,
955}
956
957/// For a given pipeline, whether any animations are currently running
958/// and any animation callbacks are queued
959#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
960pub enum AnimationState {
961    /// Animations are active but no callbacks are queued
962    AnimationsPresent,
963    /// Animations are active and callbacks are queued
964    AnimationCallbacksPresent,
965    /// No animations are active and no callbacks are queued
966    NoAnimationsPresent,
967    /// No animations are active but callbacks are queued
968    AnimationCallbacksAbsent,
969}
970
971/// A sequence number generated by a script thread for its pipelines. The
972/// constellation attaches the target pipeline's last seen `FocusSequenceNumber`
973/// to every focus-related message it sends.
974///
975/// This is used to resolve the inconsistency that occurs due to bidirectional
976/// focus state synchronization and provide eventual consistency. Example:
977///
978/// ```text
979/// script                            constellation
980/// -----------------------------------------------------------------------
981/// send ActivateDocument ----------> receive ActivateDocument
982///                             ,---- send FocusDocument
983///                             |
984/// focus an iframe             |
985/// send Focus -----------------|---> receive Focus
986///                             |     focus the iframe's content document
987/// receive FocusDocument <-----'     send FocusDocument to the content pipeline --> ...
988/// unfocus the iframe
989/// focus the document
990///
991/// Final state:                      Final state:
992///  the iframe is not focused         the iframe is focused
993/// ```
994///
995/// When the above sequence completes, from the script thread's point of view,
996/// the iframe is unfocused, but from the constellation's point of view, the
997/// iframe is still focused.
998///
999/// This inconsistency can be resolved by associating a sequence number to each
1000/// message. Whenever a script thread initiates a focus operation, it generates
1001/// and sends a brand new sequence number. The constellation attaches the
1002/// last-received sequence number to each message it sends. This way, the script
1003/// thread can discard out-dated incoming focus messages, and eventually, all
1004/// actors converge to the consistent state which is determined based on the
1005/// last focus message received by the constellation.
1006///
1007/// ```text
1008/// script                            constellation
1009/// -----------------------------------------------------------------------
1010/// send ActivateDocument ----------> receive ActivateDocument
1011///                             ,---- send FocusDocument (0)
1012///                             |
1013/// seq_number += 1             |
1014/// focus an iframe             |
1015/// send Focus (1) -------------|---> receive Focus (1)
1016///                             |     focus the iframe's content document
1017/// receive FocusDocument (0) <-'     send FocusDocument to the content pipeline --> ...
1018/// ignore it because 0 < 1
1019///
1020/// Final state:                      Final state:
1021///  the iframe is focused             the iframe is focused
1022/// ```
1023#[derive(
1024    Clone,
1025    Copy,
1026    Debug,
1027    Default,
1028    Deserialize,
1029    Eq,
1030    Hash,
1031    MallocSizeOf,
1032    PartialEq,
1033    Serialize,
1034    PartialOrd,
1035)]
1036pub struct FocusSequenceNumber(pub u64);
1037
1038impl Display for FocusSequenceNumber {
1039    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
1040        Display::fmt(&self.0, f)
1041    }
1042}
1043
1044/// An identifier for a particular JavaScript evaluation that is used to track the
1045/// evaluation from the embedding layer to the script layer and then back.
1046#[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)]
1047pub struct JavaScriptEvaluationId(pub usize);
1048
1049/// A JavaScript value produced by evaluation of a script.
1050#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
1051pub enum JSValue {
1052    Undefined,
1053    Null,
1054    Boolean(bool),
1055    Number(f64),
1056    String(String),
1057    Element(String),
1058    ShadowRoot(String),
1059    Frame(String),
1060    Window(String),
1061    Array(Vec<JSValue>),
1062    Object(HashMap<String, JSValue>),
1063}
1064
1065/// Information about a JavaScript error that occured during the evaluation of a script.
1066#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
1067pub struct JavaScriptErrorInfo {
1068    pub message: String,
1069    pub filename: String,
1070    pub stack: Option<String>,
1071    pub line_number: u64,
1072    pub column: u64,
1073}
1074
1075/// Indicates the reason that JavaScript evaluation failed due serializing issues the
1076/// result of the evaluation.
1077#[derive(Clone, Debug, Deserialize, EnumMessage, PartialEq, Serialize)]
1078pub enum JavaScriptEvaluationResultSerializationError {
1079    /// Serialization could not complete because a JavaScript value contained a detached
1080    /// shadow root according to <https://w3c.github.io/webdriver/#dfn-internal-json-clone>.
1081    DetachedShadowRoot,
1082    /// Serialization could not complete because a JavaScript value contained a "stale"
1083    /// element reference according to <https://w3c.github.io/webdriver/#dfn-get-a-known-element>.
1084    StaleElementReference,
1085    /// Serialization could not complete because a JavaScript value of an unknown type
1086    /// was encountered.
1087    UnknownType,
1088    /// This is a catch all for other kinds of errors that can happen during JavaScript value
1089    /// serialization. For instances where this can happen, see:
1090    /// <https://w3c.github.io/webdriver/#dfn-clone-an-object>.
1091    OtherJavaScriptError,
1092}
1093
1094/// An error that happens when trying to evaluate JavaScript on a `WebView`.
1095#[derive(Clone, Debug, Deserialize, EnumMessage, PartialEq, Serialize)]
1096pub enum JavaScriptEvaluationError {
1097    /// The `Document` of frame that the script was going to execute in no longer exists.
1098    DocumentNotFound,
1099    /// The script could not be compiled.
1100    CompilationFailure,
1101    /// The script could not be evaluated.
1102    EvaluationFailure(Option<JavaScriptErrorInfo>),
1103    /// An internal Servo error prevented the JavaSript evaluation from completing properly.
1104    /// This indicates a bug in Servo.
1105    InternalError,
1106    /// The `WebView` on which this evaluation request was triggered is not ready. This might
1107    /// happen if the `WebView`'s `Document` is changing due to ongoing load events, for instance.
1108    WebViewNotReady,
1109    /// The script executed successfully, but Servo could not serialize the JavaScript return
1110    /// value into a [`JSValue`].
1111    SerializationError(JavaScriptEvaluationResultSerializationError),
1112}
1113
1114#[repr(i32)]
1115#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
1116pub enum ScreenshotCaptureError {
1117    /// The screenshot request failed to read the screenshot image from the `WebView`'s
1118    /// `RenderingContext`.
1119    CouldNotReadImage,
1120    /// The WebView that this screenshot request was made for no longer exists.
1121    WebViewDoesNotExist,
1122}
1123
1124#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
1125pub struct RgbColor {
1126    pub red: u8,
1127    pub green: u8,
1128    pub blue: u8,
1129}
1130
1131/// A Script to Embedder Channel
1132#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
1133pub struct ScriptToEmbedderChan(GenericCallback<EmbedderMsg>);
1134
1135impl ScriptToEmbedderChan {
1136    /// Create a new Channel allowing script to send messages to the Embedder
1137    pub fn new(
1138        embedder_chan: Sender<EmbedderMsg>,
1139        waker: Box<dyn EventLoopWaker>,
1140    ) -> ScriptToEmbedderChan {
1141        let embedder_callback = GenericCallback::new(move |embedder_msg| {
1142            let msg = match embedder_msg {
1143                Ok(embedder_msg) => embedder_msg,
1144                Err(err) => {
1145                    log::warn!("Script to Embedder message error: {err}");
1146                    return;
1147                },
1148            };
1149            let _ = embedder_chan.send(msg);
1150            waker.wake();
1151        })
1152        .expect("Failed to create channel");
1153        ScriptToEmbedderChan(embedder_callback)
1154    }
1155
1156    /// Send a message to and wake the Embedder
1157    pub fn send(&self, msg: EmbedderMsg) -> SendResult {
1158        self.0.send(msg)
1159    }
1160}
1161
1162/// Used for communicating the details of a new `WebView` created by the embedder
1163/// back to the constellation.
1164#[derive(Deserialize, Serialize)]
1165pub struct NewWebViewDetails {
1166    pub webview_id: WebViewId,
1167    pub viewport_details: ViewportDetails,
1168    pub user_content_manager_id: Option<UserContentManagerId>,
1169}
1170
1171#[derive(Serialize, Deserialize, Debug)]
1172/// A request to load a URL. This can be used to trigger a configurable load in a `WebView`.
1173///
1174/// ```
1175///  let mut headers = http::HeaderMap::new();
1176///  headers.append(HeaderName::from_static("CustomHeader"), "Value".parse().unwrap());
1177///  let url_request = URLRequest::new(url).headers(headers);
1178///  webview.load_request(url_request);
1179/// ```
1180pub struct UrlRequest {
1181    pub url: ServoUrl,
1182    #[serde(
1183        deserialize_with = "hyper_serde::deserialize",
1184        serialize_with = "hyper_serde::serialize"
1185    )]
1186    pub headers: HeaderMap,
1187}
1188
1189impl UrlRequest {
1190    pub fn new(url: Url) -> Self {
1191        UrlRequest {
1192            url: url.into(),
1193            headers: HeaderMap::new(),
1194        }
1195    }
1196
1197    /// Set headers that will be added to the Headers
1198    pub fn headers(mut self, headers: HeaderMap) -> Self {
1199        self.headers = headers;
1200        self
1201    }
1202}
1203
1204/// The type of wake lock to acquire or release.
1205#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
1206pub enum WakeLockType {
1207    Screen,
1208}
1209
1210/// Trait for platform-specific wake lock support.
1211///
1212/// Implementations are responsible for interacting with the OS to prevent
1213/// the screen (or other resources) from sleeping while a wake lock is held.
1214pub trait WakeLockDelegate: Send + Sync {
1215    /// Acquire a wake lock of the given type, preventing the associated
1216    /// resource from sleeping. Called when the aggregate lock count transitions
1217    /// from 0 to 1. Returns an error if the OS fails to grant the lock.
1218    fn acquire(&self, type_: WakeLockType) -> Result<(), Box<dyn std::error::Error>>;
1219
1220    /// Release a previously acquired wake lock of the given type, allowing
1221    /// the resource to sleep. Called when the aggregate lock count transitions
1222    /// from N to 0.
1223    fn release(&self, type_: WakeLockType) -> Result<(), Box<dyn std::error::Error>>;
1224}