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