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