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