Skip to main content

script_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//! This module contains traits in script used generically in the rest of Servo.
6//! The traits are here instead of in script so that these modules won't have
7//! to depend on script.
8
9#![deny(missing_docs)]
10#![deny(unsafe_code)]
11
12use std::cell::Cell;
13use std::fmt;
14
15use accesskit::ActionRequest;
16use bitflags::bitflags;
17use crossbeam_channel::RecvTimeoutError;
18#[cfg(feature = "devtools")]
19use devtools_traits::ScriptToDevtoolsControlMsg;
20use embedder_traits::user_contents::{UserContentManagerId, UserContents};
21use embedder_traits::{
22    EmbedderControlId, EmbedderControlResponse, FocusSequenceNumber, InputEvent, InputEventAndId,
23    JavaScriptEvaluationId, MediaSessionActionType, MouseButton, PaintHitTestResult,
24    ScriptToEmbedderChan, Theme, ViewportDetails, WebDriverScriptCommand,
25};
26use euclid::{Scale, Size2D};
27use fonts_traits::{SystemFontServiceProxySender, WebFontLoadEvent};
28use keyboard_types::Modifiers;
29use malloc_size_of::malloc_size_of_is_0;
30use malloc_size_of_derive::MallocSizeOf;
31use media::WindowGLContext;
32use net_traits::ResourceThreads;
33use paint_api::{CrossProcessPaintApi, PinchZoomInfos};
34use pixels::PixelFormat;
35use profile_traits::mem;
36use rustc_hash::FxHashMap;
37use serde::{Deserialize, Serialize};
38use servo_base::Epoch;
39#[cfg(feature = "devtools")]
40use servo_base::generic_channel::GenericCallback;
41use servo_base::generic_channel::{GenericReceiver, GenericSender};
42use servo_base::id::{
43    BrowsingContextId, HistoryStateId, LCPCandidateID, PipelineId, PipelineNamespaceId,
44    PipelineNamespaceRequest, ScriptEventLoopId, ServiceWorkerId, WebViewId,
45};
46#[cfg(feature = "bluetooth")]
47use servo_bluetooth_traits::BluetoothRequest;
48#[cfg(feature = "webgl")]
49use servo_canvas_traits::webgl::WebGLPipeline;
50use servo_config::prefs::PrefValue;
51use servo_constellation_traits::{
52    KeyboardScroll, LoadData, NavigationHistoryBehavior, PaintMetricEvent, RemoteFocusOperation,
53    ScriptToConstellationSender, ScrollStateUpdate, ServiceWorkerState, StructuredSerializedData,
54    TargetSnapshotParams, WindowSizeType,
55};
56use servo_url::{ImmutableOrigin, OriginSnapshot, ServoUrl};
57use storage_traits::StorageThreads;
58use storage_traits::webstorage_thread::WebStorageType;
59use strum::IntoStaticStr;
60use style_traits::{CSSPixel, SpeculativePainter};
61use stylo_atoms::Atom;
62#[cfg(feature = "webgpu")]
63use webgpu_traits::WebGPUMsg;
64use webrender_api::ImageKey;
65use webrender_api::units::DevicePixel;
66
67/// A representation of the `WebView` state that is kept in each `EventLoop`
68/// and in the `Constellation`.
69#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
70pub struct WebViewState {
71    /// The [`WebViewId`] of this `WebView`.
72    pub id: WebViewId,
73    /// The platform [`Theme`] to use for this `WebView`.
74    pub theme: Cell<Theme>,
75    /// Whether or not the `WebView` has system focus. More than one `WebView` may have
76    /// system focus at one time.
77    pub has_system_focus: Cell<bool>,
78}
79
80/// The initial data required to create a new `Pipeline` attached to an existing `ScriptThread`.
81#[derive(Clone, Debug, Deserialize, Serialize)]
82pub struct NewPipelineInfo {
83    /// The [`WebViewState`] of the `WebView` that this pipeline belongs to.
84    pub webview_state: WebViewState,
85    /// The ID of the parent pipeline and frame type, if any.
86    /// If `None`, this is a root pipeline.
87    pub parent_info: Option<PipelineId>,
88    /// Id of the newly-created pipeline.
89    pub new_pipeline_id: PipelineId,
90    /// Id of the browsing context associated with this pipeline.
91    pub browsing_context_id: BrowsingContextId,
92    /// Id of the opener, if any
93    pub opener: Option<BrowsingContextId>,
94    /// Network request data which will be initiated by the script thread.
95    pub load_data: LoadData,
96    /// Initial [`ViewportDetails`] for this layout.
97    pub viewport_details: ViewportDetails,
98    /// The ID of the `UserContentManager` associated with this new pipeline's `WebView`.
99    pub user_content_manager_id: Option<UserContentManagerId>,
100    /// A snapshot of the navigation parameters of the target of this navigation.
101    pub target_snapshot_params: TargetSnapshotParams,
102    /// Name of this iframe, if any
103    pub frame_name: Option<String>,
104}
105
106/// When a pipeline is closed, should its browsing context be discarded too?
107#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
108pub enum DiscardBrowsingContext {
109    /// Discard the browsing context
110    Yes,
111    /// Don't discard the browsing context
112    No,
113}
114
115/// Is a document fully active, active or inactive?
116/// A document is active if it is the current active document in its session history,
117/// it is fuly active if it is active and all of its ancestors are active,
118/// and it is inactive otherwise.
119///
120/// * <https://html.spec.whatwg.org/multipage/#active-document>
121/// * <https://html.spec.whatwg.org/multipage/#fully-active>
122#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize)]
123pub enum DocumentActivity {
124    /// An inactive document
125    Inactive,
126    /// An active but not fully active document
127    Active,
128    /// A fully active document
129    FullyActive,
130}
131
132/// Type of recorded progressive web metric
133#[derive(Clone, Debug, Deserialize, Serialize)]
134pub enum ProgressiveWebMetricType {
135    /// Time to first Paint
136    FirstPaint,
137    /// Time to first contentful paint
138    FirstContentfulPaint,
139    /// Time for the largest contentful paint
140    LargestContentfulPaint {
141        /// The identity of the element, if any.
142        id: LCPCandidateID,
143    },
144    /// Time to interactive
145    TimeToInteractive,
146}
147
148/// The reason why the pipeline id of an iframe is being updated.
149#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize)]
150pub enum UpdatePipelineIdReason {
151    /// The pipeline id is being updated due to a navigation.
152    Navigation,
153    /// The pipeline id is being updated due to a history traversal.
154    Traversal,
155}
156
157/// Messages sent to the `ScriptThread` event loop from the `Constellation`, `Paint`, and (for
158/// now) `Layout`.
159#[derive(Deserialize, IntoStaticStr, Serialize)]
160pub enum ScriptThreadMessage {
161    /// Update the state of a service worker.
162    UpdateServiceWorkerState {
163        /// The identity of the service worker.
164        worker_id: ServiceWorkerId,
165        /// The new state of the service worker.
166        state: ServiceWorkerState,
167        /// The pipeline.
168        pipeline_id: PipelineId,
169    },
170    /// Span a new `Pipeline` in this `ScriptThread` and start fetching the contents
171    /// according to the provided `LoadData`. This will ultimately create a `Window`
172    /// and all associated data structures such as `Layout` in the `ScriptThread`.
173    SpawnPipeline(NewPipelineInfo),
174    /// Takes the associated window proxy out of "delaying-load-events-mode",
175    /// used if a scheduled navigated was refused by the embedder.
176    /// <https://html.spec.whatwg.org/multipage/#delaying-load-events-mode>
177    StopDelayingLoadEventsMode(PipelineId),
178    /// Window resized.  Sends a DOM event eventually, but first we combine events.
179    Resize(PipelineId, ViewportDetails, WindowSizeType),
180    /// Inform the ScriptThread that some aspect of the WebViewState has changed.
181    UpdateWebViewState(WebViewState),
182    /// Notifies script that window has been resized but to not take immediate action.
183    ResizeInactive(PipelineId, ViewportDetails),
184    /// Window switched from fullscreen mode.
185    ExitFullScreen(PipelineId),
186    /// Notifies the script that the document associated with this pipeline should 'unload'.
187    UnloadDocument(PipelineId),
188    /// Notifies the script that a pipeline should be closed.
189    ExitPipeline(WebViewId, PipelineId, DiscardBrowsingContext),
190    /// Notifies the script that the whole thread should be closed.
191    ExitScriptThread,
192    /// Sends a DOM event.
193    SendInputEvent(WebViewId, PipelineId, ConstellationInputEvent),
194    /// Request that the given pipeline refresh the cursor by doing a hit test at the most
195    /// recently hovered cursor position and resetting the cursor. This happens after a
196    /// display list update is rendered.
197    RefreshCursor(PipelineId),
198    /// Requests that the script thread immediately send the constellation the title of a pipeline.
199    GetTitle(PipelineId),
200    /// Retrieve the origin of a document for a pipeline, in case a child needs to retrieve the
201    /// origin of a parent in a different script thread.
202    GetDocumentOrigin(PipelineId, GenericSender<Option<OriginSnapshot>>),
203    /// Retrieve the origin and internal ancestor origin objects list of a
204    /// `Document` for a given `PipelineId`, in case a child needs to retrieve
205    /// the origin of a parent in a different event loop.
206    GetDocumentOriginDetails(
207        PipelineId,
208        GenericSender<Option<(OriginSnapshot, Vec<ImmutableOrigin>)>>,
209    ),
210    /// Notifies script thread of a change to one of its document's activity
211    SetDocumentActivity(PipelineId, DocumentActivity),
212    /// Set whether to use less resources by running timers at a heavily limited rate.
213    SetThrottled(PipelineId, bool),
214    /// Notifies script thread that a url should be loaded in this iframe.
215    /// PipelineId is for the parent, BrowsingContextId is for the nested browsing context
216    NavigateIframe(
217        PipelineId,
218        BrowsingContextId,
219        LoadData,
220        NavigationHistoryBehavior,
221        TargetSnapshotParams,
222    ),
223    /// Post a message to a given window.
224    PostMessage {
225        /// The target of the message.
226        target: PipelineId,
227        /// The webview associated with the source pipeline.
228        source_webview: WebViewId,
229        /// The ancestry of browsing context associated with the source,
230        /// starting with the source itself.
231        source_with_ancestry: Vec<BrowsingContextId>,
232        /// The expected origin of the target.
233        target_origin: Option<ImmutableOrigin>,
234        /// The source origin of the message.
235        /// <https://html.spec.whatwg.org/multipage/#dom-messageevent-origin>
236        source_origin: ImmutableOrigin,
237        /// The data to be posted.
238        data: Box<StructuredSerializedData>,
239    },
240    /// Updates the current pipeline ID of a given iframe.
241    /// First PipelineId is for the parent, second is the new PipelineId for the frame.
242    UpdatePipelineId(
243        PipelineId,
244        BrowsingContextId,
245        WebViewId,
246        PipelineId,
247        UpdatePipelineIdReason,
248    ),
249    /// Updates the history state and url of a given pipeline.
250    UpdateHistoryState(PipelineId, Option<HistoryStateId>, ServoUrl),
251    /// Removes inaccesible history states.
252    RemoveHistoryStates(PipelineId, Vec<HistoryStateId>),
253    /// Focus a `Document` as part of the focusing steps which focuses all parent `Document`s of a
254    /// newly focused `<iframe>`. Note that this is not used for the `Document` and `Element` that
255    /// is gaining focus as that is handled locally in the originating `ScriptThread`.
256    FocusDocumentAsPartOfFocusingSteps(PipelineId, FocusSequenceNumber, Option<BrowsingContextId>),
257    /// Unfocus a `Document` as part of the focusing steps which unfocuses all parent `Document`s of an
258    /// `<iframe>` losing focus. This does not do anything for a top-level `Document`, which can never
259    /// lose focus (apart from losing system focus, which is a separate concept).
260    UnfocusDocumentAsPartOfFocusingSteps(PipelineId, FocusSequenceNumber),
261    /// Focus a `Document` and run the focusing steps. This is used in two situations:
262    /// - When calling the DOM `focus()` API on a remote `Window` as well as from
263    ///   WebDriver. The difference between this and `FocusDocumentAsPartOfFocusingSteps` is that this
264    ///   version actually does run the focusing steps and may result in blur and focus events firing
265    ///   up the frame tree.
266    /// - When doing sequential focus navigation into and out of frames.
267    FocusDocument(PipelineId, RemoteFocusOperation),
268    /// Passes a webdriver command to the script thread for execution
269    WebDriverScriptCommand(PipelineId, WebDriverScriptCommand),
270    /// Notifies script thread that all animations are done
271    TickAllAnimations(Vec<WebViewId>),
272    /// Notifies the script thread that a web font has finished loading.
273    ///
274    /// This is sent if either the web font loaded successfully, or to notify the script thread
275    /// that it should try to resolve `document.fonts.ready` because the font was the last one
276    /// loading.
277    WebFontLoadFinished(PipelineId, WebFontLoadEvent),
278    /// Notify a parent pipeline that one of its `<iframe>` children has finished loading,
279    /// either by completing or by being aborted. When completing this causes the `load` event
280    /// to be dispatched at the appropriate iframe element.
281    IFrameLoadFinished {
282        /// The frame that has been marked as loaded.
283        target: BrowsingContextId,
284        /// The pipeline that contains a frame loading the target pipeline.
285        parent: PipelineId,
286        /// The pipeline that has completed loading.
287        child: PipelineId,
288        /// Whether or not the load was finished by aborting.
289        aborted: bool,
290    },
291    /// Cause a `storage` event to be dispatched at the appropriate window.
292    /// The strings are key, old value and new value.
293    DispatchStorageEvent(
294        PipelineId,
295        WebStorageType,
296        ServoUrl,
297        Option<String>,
298        Option<String>,
299        Option<String>,
300    ),
301    /// Report an error from a CSS parser for the given pipeline
302    #[cfg(feature = "devtools")]
303    ReportCSSError(PipelineId, String, u32, u32, String),
304    /// Reload the given page.
305    Reload(PipelineId),
306    /// Notifies the script thread about a new recorded paint metric.
307    PaintMetric(PipelineId, PaintMetricEvent),
308    /// Notifies the media session about a user requested media session action.
309    MediaSessionAction(PipelineId, MediaSessionActionType),
310    /// Notifies script thread that WebGPU server has started
311    #[cfg(feature = "webgpu")]
312    SetWebGPUPort(GenericReceiver<WebGPUMsg>),
313    /// `Paint` scrolled and is updating the scroll states of the nodes in the given
314    /// pipeline via the Constellation.
315    SetScrollStates(PipelineId, ScrollStateUpdate),
316    /// Evaluate the given JavaScript and return a result via a corresponding message
317    /// to the Constellation.
318    EvaluateJavaScript(WebViewId, PipelineId, JavaScriptEvaluationId, String),
319    /// A new batch of keys for the image cache for the specific pipeline.
320    SendImageKeysBatch(PipelineId, Vec<ImageKey>),
321    /// Preferences were updated in the parent process.
322    PreferencesUpdated(Vec<(String, PrefValue)>),
323    /// Notify the `ScriptThread` that the Servo renderer is no longer waiting on
324    /// asynchronous image uploads for the given `Pipeline`. These are mainly used
325    /// by canvas to perform uploads while the display list is being built.
326    NoLongerWaitingOnAsychronousImageUpdates(PipelineId),
327    /// Forward a keyboard scroll operation from an `<iframe>` to a parent pipeline.
328    ForwardKeyboardScroll(PipelineId, KeyboardScroll),
329    /// Request readiness for a screenshot from the given pipeline. The pipeline will
330    /// respond when it is ready to take the screenshot or will not be able to take it
331    /// in the future.
332    RequestScreenshotReadiness(WebViewId, PipelineId),
333    /// A response to a request to show an embedder user interface control.
334    EmbedderControlResponse(EmbedderControlId, EmbedderControlResponse),
335    /// Set the `UserContents` for the given `UserContentManagerId`. A `ScriptThread` can host many
336    /// `WebView`s which share the same `UserContentManager`. Only documents loaded after
337    /// the processing of this message will observe the new `UserContents` of the specified
338    /// `UserContentManagerId`.
339    SetUserContents(UserContentManagerId, UserContents),
340    /// Release all data for the given `UserContentManagerId` from the `ScriptThread`'s
341    /// `user_contents_for_manager_id` map.
342    DestroyUserContentManager(UserContentManagerId),
343    /// Update the pinch zoom details of a pipeline. Each `Window` stores a `VisualViewport` DOM
344    /// instance that gets updated according to the changes from the `Compositor``.
345    UpdatePinchZoomInfos(PipelineId, PinchZoomInfos),
346    /// Activate or deactivate accessibility features for the given pipeline, assuming it represents
347    /// a document.
348    ///
349    /// Why only one pipeline? In the Servo API, accessibility is activated on a per-webview basis,
350    /// and webviews have a simple one-to-many mapping to pipelines that represent documents. But
351    /// those pipelines run in script threads, which complicates things: the pipelines in a webview
352    /// may be split across multiple script threads, and the pipelines in a script thread may belong
353    /// to multiple webviews. So the simplest approach is to activate it for one pipeline at a time.
354    SetAccessibilityActive(PipelineId, bool, Epoch),
355    /// Forward the given [`accesskit::ActionRequest`] to the given pipeline.
356    ForwardAccessibilityAction(PipelineId, ActionRequest),
357    /// Force a garbage collection in this script thread.
358    TriggerGarbageCollection,
359    /// Inform the `ScriptThread` that the embedder has indicated that the network has gone
360    /// online or offline.
361    SetNetworkOnlineState(bool),
362}
363
364impl fmt::Debug for ScriptThreadMessage {
365    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
366        let variant_string: &'static str = self.into();
367        write!(formatter, "ConstellationControlMsg::{variant_string}")
368    }
369}
370
371/// Used to determine if a script has any pending asynchronous activity.
372#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
373pub enum DocumentState {
374    /// The document has been loaded and is idle.
375    Idle,
376    /// The document is either loading or waiting on an event.
377    Pending,
378}
379
380bitflags! {
381    #[derive(Clone, Copy, Default, Debug, Deserialize, Eq, PartialEq, Serialize)]
382    /// <https://w3c.github.io/pointerevents/#dom-mouseevent-buttons>
383    pub struct MouseButtons: u16 {
384        /// > 1 MUST indicate the primary button of the device (in general, the left
385        /// > button or the only button on single-button devices, used to activate a user
386        /// > interface control or select text).
387        const Primary = 1;
388        /// > 2 MUST indicate the secondary button (in general, the right button, often
389        /// > used to display a context menu), if present.
390        const Secondary = 2;
391        /// > 4 MUST indicate the auxiliary button (in general, the middle button, often
392        /// > combined with a mouse wheel).
393        const Auxiliary = 4;
394        /// The 'back' button:
395        ///
396        /// > Some pointing devices provide or simulate more buttons. To represent such
397        /// > buttons, the value MUST be doubled for each successive button (in the binary
398        /// > series 8, 16, 32, ... ).
399        const Back = 8;
400        /// The 'forward' button:
401        ///
402        /// > Some pointing devices provide or simulate more buttons. To represent such
403        /// > buttons, the value MUST be doubled for each successive button (in the binary
404        /// > series 8, 16, 32, ... ).
405        const Forward = 16;
406    }
407}
408
409impl MouseButtons {
410    /// Returns whether exactly one button is pressed.
411    pub fn exactly_one_button_pressed(&self) -> bool {
412        // Exactly one button is pressed iff mouse_button_state is a power of 2
413        !self.is_empty() && (self.bits() & (self.bits() - 1)) == 0
414    }
415}
416
417malloc_size_of_is_0!(MouseButtons);
418
419impl TryFrom<MouseButton> for MouseButtons {
420    type Error = ();
421
422    fn try_from(button: MouseButton) -> Result<Self, Self::Error> {
423        match button {
424            MouseButton::Primary => Ok(Self::Primary),
425            MouseButton::Secondary => Ok(Self::Secondary),
426            MouseButton::Auxiliary => Ok(Self::Auxiliary),
427            MouseButton::Back => Ok(Self::Back),
428            MouseButton::Forward => Ok(Self::Forward),
429            MouseButton::None | MouseButton::Other(_) => Err(()),
430        }
431    }
432}
433
434/// Input events from the embedder that are sent via the `Constellation`` to the `ScriptThread`.
435#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
436pub struct ConstellationInputEvent {
437    /// The hit test result of this input event, if any.
438    pub hit_test_result: Option<PaintHitTestResult>,
439    /// The pressed mouse button state of the constellation when this input
440    /// event was triggered.
441    pub pressed_mouse_buttons: MouseButtons,
442    /// The currently active keyboard modifiers.
443    pub active_keyboard_modifiers: Modifiers,
444    /// The [`InputEventAndId`] itself.
445    pub event: InputEventAndId,
446}
447
448impl ConstellationInputEvent {
449    /// Returns whether `pressed_mouse_buttons` includes the primary button
450    pub fn primary_button_is_pressed(&self) -> bool {
451        self.pressed_mouse_buttons.contains(MouseButtons::Primary)
452    }
453
454    /// Returns whether `pressed_mouse_buttons` includes the auxiliary (middle) button
455    pub fn auxiliary_button_is_pressed(&self) -> bool {
456        self.pressed_mouse_buttons.contains(MouseButtons::Auxiliary)
457    }
458
459    /// Returns whether this is a compatibility mouse event synthesized for a touch.
460    pub fn is_compatibility_event_for_touch(&self) -> bool {
461        match self.event.event {
462            InputEvent::MouseMove(ref event) => event.is_compatibility_event_for_touch,
463            InputEvent::MouseButton(ref event) => event.is_compatibility_event_for_touch,
464            _ => false,
465        }
466    }
467}
468
469/// All of the information necessary to create a new [`ScriptThread`] for a new [`EventLoop`].
470///
471/// NB: *DO NOT* add any Senders or Receivers here! pcwalton will have to rewrite your code if you
472/// do! Use IPC senders and receivers instead.
473#[derive(Deserialize, Serialize)]
474pub struct InitialScriptState {
475    /// The id of the script event loop that this state will start. This is used to uniquely
476    /// identify an event loop.
477    pub id: ScriptEventLoopId,
478    /// The sender to use to install the `Pipeline` namespace into this process (if necessary).
479    pub namespace_request_sender: GenericSender<PipelineNamespaceRequest>,
480    /// A channel with which messages can be sent to us (the script thread).
481    pub constellation_to_script_sender: GenericSender<ScriptThreadMessage>,
482    /// A port on which messages sent by the constellation to script can be received.
483    pub constellation_to_script_receiver: GenericReceiver<ScriptThreadMessage>,
484    /// A channel on which messages can be sent to the constellation from script.
485    pub script_to_constellation_sender: ScriptToConstellationSender,
486    /// A channel which allows script to send messages directly to the Embedder
487    /// This will pump the embedder event loop.
488    pub script_to_embedder_sender: ScriptToEmbedderChan,
489    /// An IpcSender to the `SystemFontService` used to create a `SystemFontServiceProxy`.
490    pub system_font_service: SystemFontServiceProxySender,
491    /// A channel to the resource manager thread.
492    pub resource_threads: ResourceThreads,
493    /// A channel to the storage manager thread.
494    pub storage_threads: StorageThreads,
495    /// A channel to the bluetooth thread.
496    #[cfg(feature = "bluetooth")]
497    pub bluetooth_sender: GenericSender<BluetoothRequest>,
498    /// A channel to the time profiler thread.
499    pub time_profiler_sender: profile_traits::time::ProfilerChan,
500    /// A channel to the memory profiler thread.
501    pub memory_profiler_sender: mem::ProfilerChan,
502    /// A channel to the developer tools, if applicable.
503    #[cfg(feature = "devtools")]
504    pub devtools_server_sender: Option<GenericCallback<ScriptToDevtoolsControlMsg>>,
505    /// The ID of the pipeline namespace for this script thread.
506    pub pipeline_namespace_id: PipelineNamespaceId,
507    /// A channel to the WebGL thread used in this pipeline.
508    #[cfg(feature = "webgl")]
509    pub webgl_chan: Option<WebGLPipeline>,
510    /// The XR device registry
511    pub webxr_registry: Option<webxr_api::Registry>,
512    /// Access to `Paint` across a process boundary.
513    pub cross_process_paint_api: CrossProcessPaintApi,
514    /// Application window's GL Context for Media player
515    pub player_context: WindowGLContext,
516    /// A list of URLs that can access privileged internal APIs.
517    pub privileged_urls: Vec<ServoUrl>,
518    /// A copy of constellation's `UserContentManagerId` to `UserContents` map.
519    pub user_contents_for_manager_id: FxHashMap<UserContentManagerId, UserContents>,
520    /// Whether or not the embedder has indicated that networking is online.
521    pub online: bool,
522}
523
524/// Errors from executing a paint worklet
525#[derive(Clone, Debug, Deserialize, Serialize)]
526pub enum PaintWorkletError {
527    /// Execution timed out.
528    Timeout,
529    /// No such worklet.
530    WorkletNotFound,
531}
532
533impl From<RecvTimeoutError> for PaintWorkletError {
534    fn from(_: RecvTimeoutError) -> PaintWorkletError {
535        PaintWorkletError::Timeout
536    }
537}
538
539/// Execute paint code in the worklet thread pool.
540pub trait Painter: SpeculativePainter {
541    /// <https://drafts.css-houdini.org/css-paint-api/#draw-a-paint-image>
542    fn draw_a_paint_image(
543        &self,
544        size: Size2D<f32, CSSPixel>,
545        zoom: Scale<f32, CSSPixel, DevicePixel>,
546        properties: Vec<(Atom, String)>,
547        arguments: Vec<String>,
548    ) -> Result<DrawAPaintImageResult, PaintWorkletError>;
549}
550
551impl fmt::Debug for dyn Painter {
552    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
553        fmt.debug_tuple("Painter")
554            .field(&format_args!(".."))
555            .finish()
556    }
557}
558
559/// The result of executing paint code: the image together with any image URLs that need to be loaded.
560///
561/// TODO: this should return a WR display list. <https://github.com/servo/servo/issues/17497>
562#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
563pub struct DrawAPaintImageResult {
564    /// The image height
565    pub width: u32,
566    /// The image width
567    pub height: u32,
568    /// The image format
569    pub format: PixelFormat,
570    /// The image drawn, or None if an invalid paint image was drawn
571    pub image_key: Option<ImageKey>,
572    /// Drawing the image might have requested loading some image URLs.
573    pub missing_image_urls: Vec<ServoUrl>,
574}