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