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