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