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 base::cross_process_instant::CrossProcessInstant;
15use base::generic_channel::{GenericCallback, GenericReceiver, GenericSender};
16use base::id::{
17    BrowsingContextId, HistoryStateId, PipelineId, PipelineNamespaceId, PipelineNamespaceRequest,
18    ScriptEventLoopId, WebViewId,
19};
20#[cfg(feature = "bluetooth")]
21use bluetooth_traits::BluetoothRequest;
22use canvas_traits::webgl::WebGLPipeline;
23use constellation_traits::{
24    KeyboardScroll, LoadData, NavigationHistoryBehavior, ScriptToConstellationSender,
25    StructuredSerializedData, WindowSizeType,
26};
27use crossbeam_channel::RecvTimeoutError;
28use devtools_traits::ScriptToDevtoolsControlMsg;
29use embedder_traits::user_contents::{UserContentManagerId, UserContents};
30use embedder_traits::{
31    EmbedderControlId, EmbedderControlResponse, FocusSequenceNumber, InputEventAndId,
32    JavaScriptEvaluationId, MediaSessionActionType, PaintHitTestResult, ScriptToEmbedderChan,
33    Theme, ViewportDetails, WebDriverScriptCommand,
34};
35use euclid::{Scale, Size2D};
36use fonts_traits::SystemFontServiceProxySender;
37use keyboard_types::Modifiers;
38use malloc_size_of_derive::MallocSizeOf;
39use media::WindowGLContext;
40use net_traits::ResourceThreads;
41use paint_api::{CrossProcessPaintApi, PinchZoomInfos};
42use pixels::PixelFormat;
43use profile_traits::mem;
44use rustc_hash::FxHashMap;
45use serde::{Deserialize, Serialize};
46use servo_config::prefs::PrefValue;
47use servo_url::{ImmutableOrigin, ServoUrl};
48use storage_traits::StorageThreads;
49use storage_traits::webstorage_thread::WebStorageType;
50use strum::IntoStaticStr;
51use style_traits::{CSSPixel, SpeculativePainter};
52use stylo_atoms::Atom;
53#[cfg(feature = "webgpu")]
54use webgpu_traits::WebGPUMsg;
55use webrender_api::units::{DevicePixel, LayoutVector2D};
56use webrender_api::{ExternalScrollId, ImageKey};
57
58/// The initial data required to create a new `Pipeline` attached to an existing `ScriptThread`.
59#[derive(Clone, Debug, Deserialize, Serialize)]
60pub struct NewPipelineInfo {
61    /// The ID of the parent pipeline and frame type, if any.
62    /// If `None`, this is a root pipeline.
63    pub parent_info: Option<PipelineId>,
64    /// Id of the newly-created pipeline.
65    pub new_pipeline_id: PipelineId,
66    /// Id of the browsing context associated with this pipeline.
67    pub browsing_context_id: BrowsingContextId,
68    /// Id of the top-level browsing context associated with this pipeline.
69    pub webview_id: WebViewId,
70    /// Id of the opener, if any
71    pub opener: Option<BrowsingContextId>,
72    /// Network request data which will be initiated by the script thread.
73    pub load_data: LoadData,
74    /// Initial [`ViewportDetails`] for this layout.
75    pub viewport_details: ViewportDetails,
76    /// The ID of the `UserContentManager` associated with this new pipeline's `WebView`.
77    pub user_content_manager_id: Option<UserContentManagerId>,
78    /// The [`Theme`] of the new layout.
79    pub theme: Theme,
80}
81
82/// When a pipeline is closed, should its browsing context be discarded too?
83#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
84pub enum DiscardBrowsingContext {
85    /// Discard the browsing context
86    Yes,
87    /// Don't discard the browsing context
88    No,
89}
90
91/// Is a document fully active, active or inactive?
92/// A document is active if it is the current active document in its session history,
93/// it is fuly active if it is active and all of its ancestors are active,
94/// and it is inactive otherwise.
95///
96/// * <https://html.spec.whatwg.org/multipage/#active-document>
97/// * <https://html.spec.whatwg.org/multipage/#fully-active>
98#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize)]
99pub enum DocumentActivity {
100    /// An inactive document
101    Inactive,
102    /// An active but not fully active document
103    Active,
104    /// A fully active document
105    FullyActive,
106}
107
108/// Type of recorded progressive web metric
109#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
110pub enum ProgressiveWebMetricType {
111    /// Time to first Paint
112    FirstPaint,
113    /// Time to first contentful paint
114    FirstContentfulPaint,
115    /// Time for the largest contentful paint
116    LargestContentfulPaint {
117        /// The pixel area of the largest contentful element.
118        area: usize,
119    },
120    /// Time to interactive
121    TimeToInteractive,
122}
123
124impl ProgressiveWebMetricType {
125    /// Returns the area if the metric type is LargestContentfulPaint
126    pub fn area(&self) -> usize {
127        match self {
128            ProgressiveWebMetricType::LargestContentfulPaint { area, .. } => *area,
129            _ => 0,
130        }
131    }
132}
133
134/// The reason why the pipeline id of an iframe is being updated.
135#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize)]
136pub enum UpdatePipelineIdReason {
137    /// The pipeline id is being updated due to a navigation.
138    Navigation,
139    /// The pipeline id is being updated due to a history traversal.
140    Traversal,
141}
142
143/// Messages sent to the `ScriptThread` event loop from the `Constellation`, `Paint`, and (for
144/// now) `Layout`.
145#[derive(Deserialize, IntoStaticStr, Serialize)]
146pub enum ScriptThreadMessage {
147    /// Span a new `Pipeline` in this `ScriptThread` and start fetching the contents
148    /// according to the provided `LoadData`. This will ultimately create a `Window`
149    /// and all associated data structures such as `Layout` in the `ScriptThread`.
150    SpawnPipeline(NewPipelineInfo),
151    /// Takes the associated window proxy out of "delaying-load-events-mode",
152    /// used if a scheduled navigated was refused by the embedder.
153    /// <https://html.spec.whatwg.org/multipage/#delaying-load-events-mode>
154    StopDelayingLoadEventsMode(PipelineId),
155    /// Window resized.  Sends a DOM event eventually, but first we combine events.
156    Resize(PipelineId, ViewportDetails, WindowSizeType),
157    /// Theme changed.
158    ThemeChange(PipelineId, Theme),
159    /// Notifies script that window has been resized but to not take immediate action.
160    ResizeInactive(PipelineId, ViewportDetails),
161    /// Window switched from fullscreen mode.
162    ExitFullScreen(PipelineId),
163    /// Notifies the script that the document associated with this pipeline should 'unload'.
164    UnloadDocument(PipelineId),
165    /// Notifies the script that a pipeline should be closed.
166    ExitPipeline(WebViewId, PipelineId, DiscardBrowsingContext),
167    /// Notifies the script that the whole thread should be closed.
168    ExitScriptThread,
169    /// Sends a DOM event.
170    SendInputEvent(WebViewId, PipelineId, ConstellationInputEvent),
171    /// Request that the given pipeline refresh the cursor by doing a hit test at the most
172    /// recently hovered cursor position and resetting the cursor. This happens after a
173    /// display list update is rendered.
174    RefreshCursor(PipelineId),
175    /// Requests that the script thread immediately send the constellation the title of a pipeline.
176    GetTitle(PipelineId),
177    /// Notifies script thread of a change to one of its document's activity
178    SetDocumentActivity(PipelineId, DocumentActivity),
179    /// Set whether to use less resources by running timers at a heavily limited rate.
180    SetThrottled(WebViewId, PipelineId, bool),
181    /// Notify the containing iframe (in PipelineId) that the nested browsing context (BrowsingContextId) is throttled.
182    SetThrottledInContainingIframe(WebViewId, PipelineId, BrowsingContextId, bool),
183    /// Notifies script thread that a url should be loaded in this iframe.
184    /// PipelineId is for the parent, BrowsingContextId is for the nested browsing context
185    NavigateIframe(
186        PipelineId,
187        BrowsingContextId,
188        LoadData,
189        NavigationHistoryBehavior,
190    ),
191    /// Post a message to a given window.
192    PostMessage {
193        /// The target of the message.
194        target: PipelineId,
195        /// The webview associated with the source pipeline.
196        source_webview: WebViewId,
197        /// The ancestry of browsing context associated with the source,
198        /// starting with the source itself.
199        source_with_ancestry: Vec<BrowsingContextId>,
200        /// The expected origin of the target.
201        target_origin: Option<ImmutableOrigin>,
202        /// The source origin of the message.
203        /// <https://html.spec.whatwg.org/multipage/#dom-messageevent-origin>
204        source_origin: ImmutableOrigin,
205        /// The data to be posted.
206        data: Box<StructuredSerializedData>,
207    },
208    /// Updates the current pipeline ID of a given iframe.
209    /// First PipelineId is for the parent, second is the new PipelineId for the frame.
210    UpdatePipelineId(
211        PipelineId,
212        BrowsingContextId,
213        WebViewId,
214        PipelineId,
215        UpdatePipelineIdReason,
216    ),
217    /// Updates the history state and url of a given pipeline.
218    UpdateHistoryState(PipelineId, Option<HistoryStateId>, ServoUrl),
219    /// Removes inaccesible history states.
220    RemoveHistoryStates(PipelineId, Vec<HistoryStateId>),
221    /// Set an iframe to be focused. Used when an element in an iframe gains focus.
222    /// PipelineId is for the parent, BrowsingContextId is for the nested browsing context
223    FocusIFrame(PipelineId, BrowsingContextId, FocusSequenceNumber),
224    /// Focus the document. Used when the container gains focus.
225    FocusDocument(PipelineId, FocusSequenceNumber),
226    /// Notifies that the document's container (e.g., an iframe) is not included
227    /// in the top-level browsing context's focus chain (not considering system
228    /// focus) anymore.
229    ///
230    /// Obviously, this message is invalid for a top-level document.
231    Unfocus(PipelineId, FocusSequenceNumber),
232    /// Passes a webdriver command to the script thread for execution
233    WebDriverScriptCommand(PipelineId, WebDriverScriptCommand),
234    /// Notifies script thread that all animations are done
235    TickAllAnimations(Vec<WebViewId>),
236    /// Notifies the script thread that a new Web font has been loaded, and thus the page should be
237    /// reflowed.
238    WebFontLoaded(PipelineId, bool /* success */),
239    /// Cause a `load` event to be dispatched at the appropriate iframe element.
240    DispatchIFrameLoadEvent {
241        /// The frame that has been marked as loaded.
242        target: BrowsingContextId,
243        /// The pipeline that contains a frame loading the target pipeline.
244        parent: PipelineId,
245        /// The pipeline that has completed loading.
246        child: PipelineId,
247    },
248    /// Cause a `storage` event to be dispatched at the appropriate window.
249    /// The strings are key, old value and new value.
250    DispatchStorageEvent(
251        PipelineId,
252        WebStorageType,
253        ServoUrl,
254        Option<String>,
255        Option<String>,
256        Option<String>,
257    ),
258    /// Report an error from a CSS parser for the given pipeline
259    ReportCSSError(PipelineId, String, u32, u32, String),
260    /// Reload the given page.
261    Reload(PipelineId),
262    /// Notifies the script thread about a new recorded paint metric.
263    PaintMetric(
264        PipelineId,
265        ProgressiveWebMetricType,
266        CrossProcessInstant,
267        bool, /* first_reflow */
268    ),
269    /// Notifies the media session about a user requested media session action.
270    MediaSessionAction(PipelineId, MediaSessionActionType),
271    /// Notifies script thread that WebGPU server has started
272    #[cfg(feature = "webgpu")]
273    SetWebGPUPort(GenericReceiver<WebGPUMsg>),
274    /// `Paint` scrolled and is updating the scroll states of the nodes in the given
275    /// pipeline via the Constellation.
276    SetScrollStates(PipelineId, FxHashMap<ExternalScrollId, LayoutVector2D>),
277    /// Evaluate the given JavaScript and return a result via a corresponding message
278    /// to the Constellation.
279    EvaluateJavaScript(WebViewId, PipelineId, JavaScriptEvaluationId, String),
280    /// A new batch of keys for the image cache for the specific pipeline.
281    SendImageKeysBatch(PipelineId, Vec<ImageKey>),
282    /// Preferences were updated in the parent process.
283    PreferencesUpdated(Vec<(String, PrefValue)>),
284    /// Notify the `ScriptThread` that the Servo renderer is no longer waiting on
285    /// asynchronous image uploads for the given `Pipeline`. These are mainly used
286    /// by canvas to perform uploads while the display list is being built.
287    NoLongerWaitingOnAsychronousImageUpdates(PipelineId),
288    /// Forward a keyboard scroll operation from an `<iframe>` to a parent pipeline.
289    ForwardKeyboardScroll(PipelineId, KeyboardScroll),
290    /// Request readiness for a screenshot from the given pipeline. The pipeline will
291    /// respond when it is ready to take the screenshot or will not be able to take it
292    /// in the future.
293    RequestScreenshotReadiness(WebViewId, PipelineId),
294    /// A response to a request to show an embedder user interface control.
295    EmbedderControlResponse(EmbedderControlId, EmbedderControlResponse),
296    /// Set the `UserContents` for the given `UserContentManagerId`. A `ScriptThread` can host many
297    /// `WebView`s which share the same `UserContentManager`. Only documents loaded after
298    /// the processing of this message will observe the new `UserContents` of the specified
299    /// `UserContentManagerId`.
300    SetUserContents(UserContentManagerId, UserContents),
301    /// Release all data for the given `UserContentManagerId` from the `ScriptThread`'s
302    /// `user_contents_for_manager_id` map.
303    DestroyUserContentManager(UserContentManagerId),
304    /// Send the embedder an accessibility tree update.
305    AccessibilityTreeUpdate(WebViewId, accesskit::TreeUpdate),
306    /// Update the pinch zoom details of a pipeline. Each `Window` stores a `VisualViewport` DOM
307    /// instance that gets updated according to the changes from the `Compositor``.
308    UpdatePinchZoomInfos(PipelineId, PinchZoomInfos),
309    /// Activate or deactivate accessibility features.
310    SetAccessibilityActive(bool),
311}
312
313impl fmt::Debug for ScriptThreadMessage {
314    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
315        let variant_string: &'static str = self.into();
316        write!(formatter, "ConstellationControlMsg::{variant_string}")
317    }
318}
319
320/// Used to determine if a script has any pending asynchronous activity.
321#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
322pub enum DocumentState {
323    /// The document has been loaded and is idle.
324    Idle,
325    /// The document is either loading or waiting on an event.
326    Pending,
327}
328
329/// Input events from the embedder that are sent via the `Constellation`` to the `ScriptThread`.
330#[derive(Clone, Debug, Deserialize, Serialize)]
331pub struct ConstellationInputEvent {
332    /// The hit test result of this input event, if any.
333    pub hit_test_result: Option<PaintHitTestResult>,
334    /// The pressed mouse button state of the constellation when this input
335    /// event was triggered.
336    pub pressed_mouse_buttons: u16,
337    /// The currently active keyboard modifiers.
338    pub active_keyboard_modifiers: Modifiers,
339    /// The [`InputEventAndId`] itself.
340    pub event: InputEventAndId,
341}
342
343/// All of the information necessary to create a new [`ScriptThread`] for a new [`EventLoop`].
344///
345/// NB: *DO NOT* add any Senders or Receivers here! pcwalton will have to rewrite your code if you
346/// do! Use IPC senders and receivers instead.
347#[derive(Deserialize, Serialize)]
348pub struct InitialScriptState {
349    /// The id of the script event loop that this state will start. This is used to uniquely
350    /// identify an event loop.
351    pub id: ScriptEventLoopId,
352    /// The sender to use to install the `Pipeline` namespace into this process (if necessary).
353    pub namespace_request_sender: GenericSender<PipelineNamespaceRequest>,
354    /// A channel with which messages can be sent to us (the script thread).
355    pub constellation_to_script_sender: GenericSender<ScriptThreadMessage>,
356    /// A port on which messages sent by the constellation to script can be received.
357    pub constellation_to_script_receiver: GenericReceiver<ScriptThreadMessage>,
358    /// A channel on which messages can be sent to the constellation from script.
359    pub script_to_constellation_sender: ScriptToConstellationSender,
360    /// A channel which allows script to send messages directly to the Embedder
361    /// This will pump the embedder event loop.
362    pub script_to_embedder_sender: ScriptToEmbedderChan,
363    /// An IpcSender to the `SystemFontService` used to create a `SystemFontServiceProxy`.
364    pub system_font_service: SystemFontServiceProxySender,
365    /// A channel to the resource manager thread.
366    pub resource_threads: ResourceThreads,
367    /// A channel to the storage manager thread.
368    pub storage_threads: StorageThreads,
369    /// A channel to the bluetooth thread.
370    #[cfg(feature = "bluetooth")]
371    pub bluetooth_sender: GenericSender<BluetoothRequest>,
372    /// A channel to the time profiler thread.
373    pub time_profiler_sender: profile_traits::time::ProfilerChan,
374    /// A channel to the memory profiler thread.
375    pub memory_profiler_sender: mem::ProfilerChan,
376    /// A channel to the developer tools, if applicable.
377    pub devtools_server_sender: Option<GenericCallback<ScriptToDevtoolsControlMsg>>,
378    /// The ID of the pipeline namespace for this script thread.
379    pub pipeline_namespace_id: PipelineNamespaceId,
380    /// A channel to the WebGL thread used in this pipeline.
381    pub webgl_chan: Option<WebGLPipeline>,
382    /// The XR device registry
383    pub webxr_registry: Option<webxr_api::Registry>,
384    /// Access to `Paint` across a process boundary.
385    pub cross_process_paint_api: CrossProcessPaintApi,
386    /// Application window's GL Context for Media player
387    pub player_context: WindowGLContext,
388    /// A list of URLs that can access privileged internal APIs.
389    pub privileged_urls: Vec<ServoUrl>,
390    /// A copy of constellation's `UserContentManagerId` to `UserContents` map.
391    pub user_contents_for_manager_id: FxHashMap<UserContentManagerId, UserContents>,
392    /// Whether this script should be initialized with accessibility already active.
393    pub accessibility_active: bool,
394}
395
396/// Errors from executing a paint worklet
397#[derive(Clone, Debug, Deserialize, Serialize)]
398pub enum PaintWorkletError {
399    /// Execution timed out.
400    Timeout,
401    /// No such worklet.
402    WorkletNotFound,
403}
404
405impl From<RecvTimeoutError> for PaintWorkletError {
406    fn from(_: RecvTimeoutError) -> PaintWorkletError {
407        PaintWorkletError::Timeout
408    }
409}
410
411/// Execute paint code in the worklet thread pool.
412pub trait Painter: SpeculativePainter {
413    /// <https://drafts.css-houdini.org/css-paint-api/#draw-a-paint-image>
414    fn draw_a_paint_image(
415        &self,
416        size: Size2D<f32, CSSPixel>,
417        zoom: Scale<f32, CSSPixel, DevicePixel>,
418        properties: Vec<(Atom, String)>,
419        arguments: Vec<String>,
420    ) -> Result<DrawAPaintImageResult, PaintWorkletError>;
421}
422
423impl fmt::Debug for dyn Painter {
424    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
425        fmt.debug_tuple("Painter")
426            .field(&format_args!(".."))
427            .finish()
428    }
429}
430
431/// The result of executing paint code: the image together with any image URLs that need to be loaded.
432///
433/// TODO: this should return a WR display list. <https://github.com/servo/servo/issues/17497>
434#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
435pub struct DrawAPaintImageResult {
436    /// The image height
437    pub width: u32,
438    /// The image width
439    pub height: u32,
440    /// The image format
441    pub format: PixelFormat,
442    /// The image drawn, or None if an invalid paint image was drawn
443    pub image_key: Option<ImageKey>,
444    /// Drawing the image might have requested loading some image URLs.
445    pub missing_image_urls: Vec<ServoUrl>,
446}