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