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