diff options
Diffstat (limited to 'components')
88 files changed, 1252 insertions, 866 deletions
diff --git a/components/compositing/compositor.rs b/components/compositing/compositor.rs index 098b22a2b68..4550188a7fa 100644 --- a/components/compositing/compositor.rs +++ b/components/compositing/compositor.rs @@ -19,7 +19,7 @@ use bitflags::bitflags; use compositing_traits::display_list::{CompositorDisplayListInfo, HitTestInfo, ScrollTree}; use compositing_traits::rendering_context::RenderingContext; use compositing_traits::{ - CompositionPipeline, CompositorMsg, ImageUpdate, RendererWebView, SendableFrameTree, + CompositionPipeline, CompositorMsg, ImageUpdate, SendableFrameTree, WebViewTrait, }; use constellation_traits::{AnimationTickType, EmbedderToConstellationMessage, PaintMetricEvent}; use crossbeam_channel::{Receiver, Sender}; @@ -54,8 +54,8 @@ use webrender_api::{ }; use crate::InitialCompositorState; -use crate::webview::{UnknownWebView, WebView}; use crate::webview_manager::WebViewManager; +use crate::webview_renderer::{UnknownWebView, WebViewRenderer}; #[derive(Debug, PartialEq)] enum UnableToComposite { @@ -133,8 +133,8 @@ pub struct IOCompositor { /// Data that is shared by all WebView renderers. global: Rc<RefCell<ServoRenderer>>, - /// Our top-level browsing contexts. - webviews: WebViewManager<WebView>, + /// Our [`WebViewRenderer`]s, one for every `WebView`. + webview_renderers: WebViewManager<WebViewRenderer>, /// Tracks whether or not the view needs to be repainted. needs_repaint: Cell<RepaintReason>, @@ -424,7 +424,7 @@ impl IOCompositor { cursor: Cursor::None, cursor_pos: DevicePoint::new(0.0, 0.0), })), - webviews: WebViewManager::default(), + webview_renderers: WebViewManager::default(), needs_repaint: Cell::default(), ready_to_save_state: ReadyState::Unknown, webrender: Some(state.webrender), @@ -544,9 +544,9 @@ impl IOCompositor { animation_state, ) => { let mut throttled = true; - if let Some(webview) = self.webviews.get_mut(webview_id) { - throttled = - webview.change_running_animations_state(pipeline_id, animation_state); + if let Some(webview_renderer) = self.webview_renderers.get_mut(webview_id) { + throttled = webview_renderer + .change_running_animations_state(pipeline_id, animation_state); } // These operations should eventually happen per-WebView, but they are global now as rendering @@ -557,8 +557,8 @@ impl IOCompositor { if !throttled && animation_state == AnimationState::AnimationCallbacksPresent { // We need to fetch the WebView again in order to avoid a double borrow. - if let Some(webview) = self.webviews.get(webview_id) { - webview.tick_animations_for_pipeline(pipeline_id, self); + if let Some(webview_renderer) = self.webview_renderers.get(webview_id) { + webview_renderer.tick_animations_for_pipeline(pipeline_id, self); } } }, @@ -572,11 +572,11 @@ impl IOCompositor { }, CompositorMsg::TouchEventProcessed(webview_id, result) => { - let Some(webview) = self.webviews.get_mut(webview_id) else { + let Some(webview_renderer) = self.webview_renderers.get_mut(webview_id) else { warn!("Handling input event for unknown webview: {webview_id}"); return; }; - webview.on_touch_event_processed(result); + webview_renderer.on_touch_event_processed(result); }, CompositorMsg::CreatePng(webview_id, page_rect, reply) => { @@ -604,8 +604,8 @@ impl IOCompositor { }, CompositorMsg::SetThrottled(webview_id, pipeline_id, throttled) => { - if let Some(webview) = self.webviews.get_mut(webview_id) { - webview.set_throttled(pipeline_id, throttled); + if let Some(webview_renderer) = self.webview_renderers.get_mut(webview_id) { + webview_renderer.set_throttled(pipeline_id, throttled); self.process_animations(true); } }, @@ -615,8 +615,8 @@ impl IOCompositor { "Compositor got pipeline exited: {:?} {:?}", webview_id, pipeline_id ); - if let Some(webview) = self.webviews.get_mut(webview_id) { - webview.remove_pipeline(pipeline_id); + if let Some(webview_renderer) = self.webview_renderers.get_mut(webview_id) { + webview_renderer.remove_pipeline(pipeline_id); } let _ = sender.send(()); }, @@ -648,13 +648,13 @@ impl IOCompositor { }, CompositorMsg::WebDriverMouseButtonEvent(webview_id, action, button, x, y) => { - let Some(webview) = self.webviews.get_mut(webview_id) else { + let Some(webview_renderer) = self.webview_renderers.get_mut(webview_id) else { warn!("Handling input event for unknown webview: {webview_id}"); return; }; - let dppx = webview.device_pixels_per_page_pixel(); + let dppx = webview_renderer.device_pixels_per_page_pixel(); let point = dppx.transform_point(Point2D::new(x, y)); - webview.dispatch_input_event(InputEvent::MouseButton(MouseButtonEvent { + webview_renderer.dispatch_input_event(InputEvent::MouseButton(MouseButtonEvent { point, action, button, @@ -662,13 +662,14 @@ impl IOCompositor { }, CompositorMsg::WebDriverMouseMoveEvent(webview_id, x, y) => { - let Some(webview) = self.webviews.get_mut(webview_id) else { + let Some(webview_renderer) = self.webview_renderers.get_mut(webview_id) else { warn!("Handling input event for unknown webview: {webview_id}"); return; }; - let dppx = webview.device_pixels_per_page_pixel(); + let dppx = webview_renderer.device_pixels_per_page_pixel(); let point = dppx.transform_point(Point2D::new(x, y)); - webview.dispatch_input_event(InputEvent::MouseMove(MouseMoveEvent { point })); + webview_renderer + .dispatch_input_event(InputEvent::MouseMove(MouseMoveEvent { point })); }, CompositorMsg::SendInitialTransaction(pipeline) => { @@ -679,12 +680,13 @@ impl IOCompositor { }, CompositorMsg::SendScrollNode(webview_id, pipeline_id, point, external_scroll_id) => { - let Some(webview) = self.webviews.get_mut(webview_id) else { + let Some(webview_renderer) = self.webview_renderers.get_mut(webview_id) else { return; }; let pipeline_id = pipeline_id.into(); - let Some(pipeline_details) = webview.pipelines.get_mut(&pipeline_id) else { + let Some(pipeline_details) = webview_renderer.pipelines.get_mut(&pipeline_id) + else { return; }; @@ -771,12 +773,12 @@ impl IOCompositor { ) .entered(); - let Some(webview) = self.webviews.get_mut(webview_id) else { + let Some(webview_renderer) = self.webview_renderers.get_mut(webview_id) else { return warn!("Could not find WebView for incoming display list"); }; let pipeline_id = display_list_info.pipeline_id; - let details = webview.ensure_pipeline_details(pipeline_id.into()); + let details = webview_renderer.ensure_pipeline_details(pipeline_id.into()); details.most_recent_display_list_epoch = Some(display_list_info.epoch); details.hit_test_items = display_list_info.hit_test_info; details.install_new_scroll_tree(display_list_info.scroll_tree); @@ -900,9 +902,11 @@ impl IOCompositor { }, CompositorMsg::GetClientWindowRect(webview_id, response_sender) => { let client_window_rect = self - .webviews + .webview_renderers .get(webview_id) - .map(|webview| webview.client_window_rect(self.rendering_context.size2d())) + .map(|webview_renderer| { + webview_renderer.client_window_rect(self.rendering_context.size2d()) + }) .unwrap_or_default(); if let Err(error) = response_sender.send(client_window_rect) { warn!("Sending response to get client window failed ({error:?})."); @@ -910,9 +914,9 @@ impl IOCompositor { }, CompositorMsg::GetScreenSize(webview_id, response_sender) => { let screen_size = self - .webviews + .webview_renderers .get(webview_id) - .map(WebView::screen_size) + .map(WebViewRenderer::screen_size) .unwrap_or_default(); if let Err(error) = response_sender.send(screen_size) { warn!("Sending response to get screen size failed ({error:?})."); @@ -920,9 +924,9 @@ impl IOCompositor { }, CompositorMsg::GetAvailableScreenSize(webview_id, response_sender) => { let available_screen_size = self - .webviews + .webview_renderers .get(webview_id) - .map(WebView::available_screen_size) + .map(WebViewRenderer::available_screen_size) .unwrap_or_default(); if let Err(error) = response_sender.send(available_screen_size) { warn!("Sending response to get screen size failed ({error:?})."); @@ -947,8 +951,8 @@ impl IOCompositor { "Compositor got pipeline exited: {:?} {:?}", webview_id, pipeline_id ); - if let Some(webview) = self.webviews.get_mut(webview_id) { - webview.remove_pipeline(pipeline_id); + if let Some(webview_renderer) = self.webview_renderers.get_mut(webview_id) { + webview_renderer.remove_pipeline(pipeline_id); } let _ = sender.send(()); }, @@ -1040,12 +1044,12 @@ impl IOCompositor { let root_clip_id = builder.define_clip_rect(root_reference_frame, viewport_rect); let clip_chain_id = builder.define_clip_chain(None, [root_clip_id]); - for (_, webview) in self.webviews.painting_order() { - let Some(pipeline_id) = webview.root_pipeline_id else { + for (_, webview_renderer) in self.webview_renderers.painting_order() { + let Some(pipeline_id) = webview_renderer.root_pipeline_id else { continue; }; - let device_pixels_per_page_pixel = webview.device_pixels_per_page_pixel().0; + let device_pixels_per_page_pixel = webview_renderer.device_pixels_per_page_pixel().0; let webview_reference_frame = builder.push_reference_frame( LayoutPoint::zero(), root_reference_frame, @@ -1063,7 +1067,7 @@ impl IOCompositor { SpatialTreeItemKey::new(0, 0), ); - let scaled_webview_rect = webview.rect / device_pixels_per_page_pixel; + let scaled_webview_rect = webview_renderer.rect / device_pixels_per_page_pixel; builder.push_iframe( LayoutRect::from_untyped(&scaled_webview_rect.to_untyped()), LayoutRect::from_untyped(&scaled_webview_rect.to_untyped()), @@ -1094,8 +1098,8 @@ impl IOCompositor { /// TODO(mrobinson): Could we only send offsets for the branch being modified /// and not the entire scene? fn update_transaction_with_all_scroll_offsets(&self, transaction: &mut Transaction) { - for webview in self.webviews.iter() { - for details in webview.pipelines.values() { + for webview_renderer in self.webview_renderers.iter() { + for details in webview_renderer.pipelines.values() { for node in details.scroll_tree.nodes.iter() { let (Some(offset), Some(external_id)) = (node.offset(), node.external_id()) else { @@ -1117,34 +1121,36 @@ impl IOCompositor { pub fn add_webview( &mut self, - webview: Box<dyn RendererWebView>, + webview: Box<dyn WebViewTrait>, viewport_details: ViewportDetails, ) { - self.webviews.entry(webview.id()).or_insert(WebView::new( - self.global.clone(), - webview, - viewport_details, - )); + self.webview_renderers + .entry(webview.id()) + .or_insert(WebViewRenderer::new( + self.global.clone(), + webview, + viewport_details, + )); } fn set_frame_tree_for_webview(&mut self, frame_tree: &SendableFrameTree) { debug!("{}: Setting frame tree for webview", frame_tree.pipeline.id); let webview_id = frame_tree.pipeline.webview_id; - let Some(webview) = self.webviews.get_mut(webview_id) else { + let Some(webview_renderer) = self.webview_renderers.get_mut(webview_id) else { warn!( "Attempted to set frame tree on unknown WebView (perhaps closed?): {webview_id:?}" ); return; }; - webview.set_frame_tree(frame_tree); + webview_renderer.set_frame_tree(frame_tree); self.send_root_pipeline_display_list(); } fn remove_webview(&mut self, webview_id: WebViewId) { debug!("{}: Removing", webview_id); - if self.webviews.remove(webview_id).is_err() { + if self.webview_renderers.remove(webview_id).is_err() { warn!("{webview_id}: Removing unknown webview"); return; }; @@ -1160,15 +1166,15 @@ impl IOCompositor { debug!("{webview_id}: Showing webview; hide_others={hide_others}"); let painting_order_changed = if hide_others { let result = self - .webviews + .webview_renderers .painting_order() .map(|(&id, _)| id) .ne(once(webview_id)); - self.webviews.hide_all(); - self.webviews.show(webview_id)?; + self.webview_renderers.hide_all(); + self.webview_renderers.show(webview_id)?; result } else { - self.webviews.show(webview_id)? + self.webview_renderers.show(webview_id)? }; if painting_order_changed { self.send_root_pipeline_display_list(); @@ -1178,7 +1184,7 @@ impl IOCompositor { pub fn hide_webview(&mut self, webview_id: WebViewId) -> Result<(), UnknownWebView> { debug!("{webview_id}: Hiding webview"); - if self.webviews.hide(webview_id)? { + if self.webview_renderers.hide(webview_id)? { self.send_root_pipeline_display_list(); } Ok(()) @@ -1192,15 +1198,15 @@ impl IOCompositor { debug!("{webview_id}: Raising webview to top; hide_others={hide_others}"); let painting_order_changed = if hide_others { let result = self - .webviews + .webview_renderers .painting_order() .map(|(&id, _)| id) .ne(once(webview_id)); - self.webviews.hide_all(); - self.webviews.raise_to_top(webview_id)?; + self.webview_renderers.hide_all(); + self.webview_renderers.raise_to_top(webview_id)?; result } else { - self.webviews.raise_to_top(webview_id)? + self.webview_renderers.raise_to_top(webview_id)? }; if painting_order_changed { self.send_root_pipeline_display_list(); @@ -1212,10 +1218,10 @@ impl IOCompositor { if self.global.borrow().shutdown_state() != ShutdownState::NotShuttingDown { return; } - let Some(webview) = self.webviews.get_mut(webview_id) else { + let Some(webview_renderer) = self.webview_renderers.get_mut(webview_id) else { return; }; - if !webview.set_rect(rect) { + if !webview_renderer.set_rect(rect) { return; } @@ -1231,10 +1237,10 @@ impl IOCompositor { if self.global.borrow().shutdown_state() != ShutdownState::NotShuttingDown { return; } - let Some(webview) = self.webviews.get_mut(webview_id) else { + let Some(webview_renderer) = self.webview_renderers.get_mut(webview_id) else { return; }; - if !webview.set_hidpi_scale_factor(new_scale_factor) { + if !webview_renderer.set_hidpi_scale_factor(new_scale_factor) { return; } @@ -1277,8 +1283,8 @@ impl IOCompositor { } self.last_animation_tick = Instant::now(); - for webview in self.webviews.iter() { - webview.tick_all_animations(self); + for webview_renderer in self.webview_renderers.iter() { + webview_renderer.tick_all_animations(self); } } @@ -1287,8 +1293,8 @@ impl IOCompositor { return; } - if let Some(webview) = self.webviews.get_mut(webview_id) { - webview.set_page_zoom(1.0); + if let Some(webview_renderer) = self.webview_renderers.get_mut(webview_id) { + webview_renderer.set_page_zoom(1.0); } self.send_root_pipeline_display_list(); } @@ -1298,8 +1304,8 @@ impl IOCompositor { return; } - if let Some(webview) = self.webviews.get_mut(webview_id) { - webview.set_page_zoom(magnification); + if let Some(webview_renderer) = self.webview_renderers.get_mut(webview_id) { + webview_renderer.set_page_zoom(magnification); } self.send_root_pipeline_display_list(); } @@ -1311,21 +1317,24 @@ impl IOCompositor { .pipeline_to_webview_map .get(&pipeline_id) .cloned()?; - self.webviews.get(webview_id)?.pipelines.get(&pipeline_id) + self.webview_renderers + .get(webview_id)? + .pipelines + .get(&pipeline_id) } // Check if any pipelines currently have active animations or animation callbacks. fn animations_or_animation_callbacks_running(&self) -> bool { - self.webviews + self.webview_renderers .iter() - .any(WebView::animations_or_animation_callbacks_running) + .any(WebViewRenderer::animations_or_animation_callbacks_running) } /// Returns true if any animation callbacks (ie `requestAnimationFrame`) are waiting for a response. fn animation_callbacks_running(&self) -> bool { - self.webviews + self.webview_renderers .iter() - .any(WebView::animation_callbacks_running) + .any(WebViewRenderer::animation_callbacks_running) } /// Query the constellation to see if the current compositor @@ -1341,7 +1350,11 @@ impl IOCompositor { // This gets sent to the constellation for comparison with the current // frame tree. let mut pipeline_epochs = HashMap::new(); - for id in self.webviews.iter().flat_map(WebView::pipeline_ids) { + for id in self + .webview_renderers + .iter() + .flat_map(WebViewRenderer::pipeline_ids) + { if let Some(WebRenderEpoch(epoch)) = self .webrender .as_ref() @@ -1408,9 +1421,9 @@ impl IOCompositor { let size = self.rendering_context.size2d().to_i32(); let rect = if let Some(rect) = page_rect { let scale = self - .webviews + .webview_renderers .get(webview_id) - .map(WebView::device_pixels_per_page_pixel) + .map(WebViewRenderer::device_pixels_per_page_pixel) .unwrap_or_else(|| Scale::new(1.0)); let rect = scale.transform_rect(&rect); @@ -1507,8 +1520,8 @@ impl IOCompositor { fn send_pending_paint_metrics_messages_after_composite(&mut self) { let paint_time = CrossProcessInstant::now(); let document_id = self.webrender_document(); - for webview_details in self.webviews.iter_mut() { - for (pipeline_id, pipeline) in webview_details.pipelines.iter_mut() { + for webview_renderer in self.webview_renderers.iter_mut() { + for (pipeline_id, pipeline) in webview_renderer.pipelines.iter_mut() { let Some(current_epoch) = self .webrender .as_ref() @@ -1646,11 +1659,11 @@ impl IOCompositor { if let Err(err) = self.rendering_context.make_current() { warn!("Failed to make the rendering context current: {:?}", err); } - let mut webviews = take(&mut self.webviews); - for webview in webviews.iter_mut() { - webview.process_pending_scroll_events(self); + let mut webview_renderers = take(&mut self.webview_renderers); + for webview_renderer in webview_renderers.iter_mut() { + webview_renderer.process_pending_scroll_events(self); } - self.webviews = webviews; + self.webview_renderers = webview_renderers; self.global.borrow().shutdown_state() != ShutdownState::FinishedShuttingDown } @@ -1735,8 +1748,8 @@ impl IOCompositor { } pub fn notify_input_event(&mut self, webview_id: WebViewId, event: InputEvent) { - if let Some(webview) = self.webviews.get_mut(webview_id) { - webview.notify_input_event(event); + if let Some(webview_renderer) = self.webview_renderers.get_mut(webview_id) { + webview_renderer.notify_input_event(event); } } @@ -1747,20 +1760,20 @@ impl IOCompositor { cursor: DeviceIntPoint, event_type: TouchEventType, ) { - if let Some(webview) = self.webviews.get_mut(webview_id) { - webview.notify_scroll_event(scroll_location, cursor, event_type); + if let Some(webview_renderer) = self.webview_renderers.get_mut(webview_id) { + webview_renderer.notify_scroll_event(scroll_location, cursor, event_type); } } pub fn on_vsync(&mut self, webview_id: WebViewId) { - if let Some(webview) = self.webviews.get_mut(webview_id) { - webview.on_vsync(); + if let Some(webview_renderer) = self.webview_renderers.get_mut(webview_id) { + webview_renderer.on_vsync(); } } pub fn set_pinch_zoom(&mut self, webview_id: WebViewId, magnification: f32) { - if let Some(webview) = self.webviews.get_mut(webview_id) { - webview.set_pinch_zoom(magnification); + if let Some(webview_renderer) = self.webview_renderers.get_mut(webview_id) { + webview_renderer.set_pinch_zoom(magnification); } } diff --git a/components/compositing/lib.rs b/components/compositing/lib.rs index 65bd18929e1..a66c61499e5 100644 --- a/components/compositing/lib.rs +++ b/components/compositing/lib.rs @@ -23,8 +23,8 @@ mod tracing; mod compositor; mod touch; -pub mod webview; pub mod webview_manager; +pub mod webview_renderer; /// Data used to construct a compositor. pub struct InitialCompositorState { diff --git a/components/compositing/webview_manager.rs b/components/compositing/webview_manager.rs index 1f1ced3a468..42e9bfe1cb7 100644 --- a/components/compositing/webview_manager.rs +++ b/components/compositing/webview_manager.rs @@ -7,7 +7,7 @@ use std::collections::hash_map::{Entry, Values, ValuesMut}; use base::id::WebViewId; -use crate::webview::UnknownWebView; +use crate::webview_renderer::UnknownWebView; #[derive(Debug)] pub struct WebViewManager<WebView> { @@ -117,8 +117,8 @@ mod test { BrowsingContextId, BrowsingContextIndex, PipelineNamespace, PipelineNamespaceId, WebViewId, }; - use crate::webview::UnknownWebView; use crate::webview_manager::WebViewManager; + use crate::webview_renderer::UnknownWebView; fn top_level_id(namespace_id: u32, index: u32) -> WebViewId { WebViewId(BrowsingContextId { diff --git a/components/compositing/webview.rs b/components/compositing/webview_renderer.rs index cc6b030a05e..6ad77d46043 100644 --- a/components/compositing/webview.rs +++ b/components/compositing/webview_renderer.rs @@ -8,7 +8,7 @@ use std::collections::hash_map::Keys; use std::rc::Rc; use base::id::{PipelineId, WebViewId}; -use compositing_traits::{RendererWebView, SendableFrameTree}; +use compositing_traits::{SendableFrameTree, WebViewTrait}; use constellation_traits::{EmbedderToConstellationMessage, ScrollState, WindowSizeType}; use embedder_traits::{ AnimationState, CompositorHitTestResult, InputEvent, MouseButton, MouseButtonAction, @@ -55,13 +55,16 @@ enum ScrollZoomEvent { Scroll(ScrollEvent), } -pub(crate) struct WebView { +/// A renderer for a libservo `WebView`. This is essentially the [`ServoRenderer`]'s interface to a +/// libservo `WebView`, but the code here cannot depend on libservo in order to prevent circular +/// dependencies, which is why we store a `dyn WebViewTrait` here instead of the `WebView` itself. +pub(crate) struct WebViewRenderer { /// The [`WebViewId`] of the `WebView` associated with this [`WebViewDetails`]. pub id: WebViewId, /// The renderer's view of the embedding layer `WebView` as a trait implementation, /// so that the renderer doesn't need to depend on the embedding layer. This avoids /// a dependency cycle. - pub renderer_webview: Box<dyn RendererWebView>, + pub webview: Box<dyn WebViewTrait>, /// The root [`PipelineId`] of the currently displayed page in this WebView. pub root_pipeline_id: Option<PipelineId>, pub rect: DeviceRect, @@ -85,7 +88,7 @@ pub(crate) struct WebView { hidpi_scale_factor: Scale<f32, DeviceIndependentPixel, DevicePixel>, } -impl Drop for WebView { +impl Drop for WebViewRenderer { fn drop(&mut self) { self.global .borrow_mut() @@ -94,17 +97,17 @@ impl Drop for WebView { } } -impl WebView { +impl WebViewRenderer { pub(crate) fn new( global: Rc<RefCell<ServoRenderer>>, - renderer_webview: Box<dyn RendererWebView>, + renderer_webview: Box<dyn WebViewTrait>, viewport_details: ViewportDetails, ) -> Self { let hidpi_scale_factor = viewport_details.hidpi_scale_factor; let size = viewport_details.size * viewport_details.hidpi_scale_factor; Self { id: renderer_webview.id(), - renderer_webview, + webview: renderer_webview, root_pipeline_id: None, rect: DeviceRect::from_origin_and_size(DevicePoint::origin(), size), pipelines: Default::default(), @@ -269,7 +272,7 @@ impl WebView { }; let animating = self.pipelines.values().any(PipelineDetails::animating); - self.renderer_webview.set_animating(animating); + self.webview.set_animating(animating); throttled } @@ -954,7 +957,7 @@ impl WebView { &self, rendering_context_size: Size2D<u32, DevicePixel>, ) -> Box2D<i32, DeviceIndependentPixel> { - let screen_geometry = self.renderer_webview.screen_geometry().unwrap_or_default(); + let screen_geometry = self.webview.screen_geometry().unwrap_or_default(); let rect = DeviceIntRect::from_origin_and_size( screen_geometry.offset, rendering_context_size.to_i32(), @@ -965,12 +968,12 @@ impl WebView { } pub(crate) fn screen_size(&self) -> Size2D<i32, DeviceIndependentPixel> { - let screen_geometry = self.renderer_webview.screen_geometry().unwrap_or_default(); + let screen_geometry = self.webview.screen_geometry().unwrap_or_default(); (screen_geometry.size.to_f32() / self.hidpi_scale_factor).to_i32() } pub(crate) fn available_screen_size(&self) -> Size2D<i32, DeviceIndependentPixel> { - let screen_geometry = self.renderer_webview.screen_geometry().unwrap_or_default(); + let screen_geometry = self.webview.screen_geometry().unwrap_or_default(); (screen_geometry.available_size.to_f32() / self.hidpi_scale_factor).to_i32() } } diff --git a/components/layout/dom.rs b/components/layout/dom.rs index 39cd74293c8..6db4dbccd41 100644 --- a/components/layout/dom.rs +++ b/components/layout/dom.rs @@ -7,7 +7,7 @@ use std::sync::Arc; use atomic_refcell::{AtomicRef, AtomicRefCell, AtomicRefMut}; use base::id::{BrowsingContextId, PipelineId}; -use html5ever::{local_name, namespace_url, ns}; +use html5ever::{local_name, ns}; use pixels::Image; use script_layout_interface::wrapper_traits::{ LayoutDataTrait, LayoutNode, ThreadSafeLayoutElement, ThreadSafeLayoutNode, diff --git a/components/script/document_loader.rs b/components/script/document_loader.rs index 422652272b6..33f51cd8517 100644 --- a/components/script/document_loader.rs +++ b/components/script/document_loader.rs @@ -50,10 +50,18 @@ impl LoadBlocker { /// Remove this load from the associated document's list of blocking loads. pub(crate) fn terminate(blocker: &DomRefCell<Option<LoadBlocker>>, can_gc: CanGc) { - if let Some(this) = blocker.borrow().as_ref() { - let load_data = this.load.clone().unwrap(); - this.doc.finish_load(load_data, can_gc); + let Some(load) = blocker + .borrow_mut() + .as_mut() + .and_then(|blocker| blocker.load.take()) + else { + return; + }; + + if let Some(blocker) = blocker.borrow().as_ref() { + blocker.doc.finish_load(load, can_gc); } + *blocker.borrow_mut() = None; } } diff --git a/components/script/dom/attr.rs b/components/script/dom/attr.rs index 9b33213335e..52d0ca7e20c 100644 --- a/components/script/dom/attr.rs +++ b/components/script/dom/attr.rs @@ -8,7 +8,7 @@ use std::mem; use devtools_traits::AttrInfo; use dom_struct::dom_struct; -use html5ever::{LocalName, Namespace, Prefix, namespace_url, ns}; +use html5ever::{LocalName, Namespace, Prefix, ns}; use style::attr::{AttrIdentifier, AttrValue}; use style::values::GenericAtomIdent; use stylo_atoms::Atom; diff --git a/components/script/dom/bindings/constructor.rs b/components/script/dom/bindings/constructor.rs index 138dd4520d0..141fb8c1391 100644 --- a/components/script/dom/bindings/constructor.rs +++ b/components/script/dom/bindings/constructor.rs @@ -5,7 +5,7 @@ use std::ptr; use html5ever::interface::QualName; -use html5ever::{LocalName, local_name, namespace_url, ns}; +use html5ever::{LocalName, local_name, ns}; use js::conversions::ToJSValConvertible; use js::glue::{UnwrapObjectDynamic, UnwrapObjectStatic}; use js::jsapi::{CallArgs, CurrentGlobalOrNull, JSAutoRealm, JSObject}; diff --git a/components/script/dom/bindings/utils.rs b/components/script/dom/bindings/utils.rs index f8edab1879d..ba7db745832 100644 --- a/components/script/dom/bindings/utils.rs +++ b/components/script/dom/bindings/utils.rs @@ -118,6 +118,19 @@ pub(crate) const DOM_CALLBACKS: DOMCallbacks = DOMCallbacks { instanceClassMatchesProto: Some(instance_class_has_proto_at_depth), }; +/// Eagerly define all relevant WebIDL interface constructors on the +/// provided global object. +pub(crate) fn define_all_exposed_interfaces( + global: &GlobalScope, + _in_realm: InRealm, + _can_gc: CanGc, +) { + let cx = GlobalScope::get_cx(); + for (_, interface) in &InterfaceObjectMap::MAP { + (interface.define)(cx, global.reflector().get_jsobject()); + } +} + impl DomHelpers<crate::DomTypeHolder> for crate::DomTypeHolder { fn throw_dom_exception( cx: SafeJSContext, diff --git a/components/script/dom/bindings/xmlname.rs b/components/script/dom/bindings/xmlname.rs index 31436278c12..af02569d9c4 100644 --- a/components/script/dom/bindings/xmlname.rs +++ b/components/script/dom/bindings/xmlname.rs @@ -4,7 +4,7 @@ //! Functions for validating and extracting qualified XML names. -use html5ever::{LocalName, Namespace, Prefix, namespace_url, ns}; +use html5ever::{LocalName, Namespace, Prefix, ns}; use crate::dom::bindings::error::{Error, Fallible}; use crate::dom::bindings::str::DOMString; diff --git a/components/script/dom/create.rs b/components/script/dom/create.rs index c2e038eb66b..5722dc4f6ac 100644 --- a/components/script/dom/create.rs +++ b/components/script/dom/create.rs @@ -2,7 +2,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -use html5ever::{LocalName, Prefix, QualName, local_name, namespace_url, ns}; +use html5ever::{LocalName, Prefix, QualName, local_name, ns}; use js::rust::HandleObject; use servo_config::pref; diff --git a/components/script/dom/customelementregistry.rs b/components/script/dom/customelementregistry.rs index 049e3c482a1..be672585529 100644 --- a/components/script/dom/customelementregistry.rs +++ b/components/script/dom/customelementregistry.rs @@ -9,7 +9,7 @@ use std::rc::Rc; use std::{mem, ptr}; use dom_struct::dom_struct; -use html5ever::{LocalName, Namespace, Prefix, namespace_url, ns}; +use html5ever::{LocalName, Namespace, Prefix, ns}; use js::conversions::ToJSValConvertible; use js::glue::UnwrapObjectStatic; use js::jsapi::{HandleValueArray, Heap, IsCallable, IsConstructor, JSAutoRealm, JSObject}; diff --git a/components/script/dom/dedicatedworkerglobalscope.rs b/components/script/dom/dedicatedworkerglobalscope.rs index b014a85701b..972a9d90948 100644 --- a/components/script/dom/dedicatedworkerglobalscope.rs +++ b/components/script/dom/dedicatedworkerglobalscope.rs @@ -42,6 +42,7 @@ use crate::dom::bindings::root::{DomRoot, RootCollection, ThreadLocalStackRoots} use crate::dom::bindings::str::DOMString; use crate::dom::bindings::structuredclone; use crate::dom::bindings::trace::{CustomTraceable, RootedTraceableBox}; +use crate::dom::bindings::utils::define_all_exposed_interfaces; use crate::dom::errorevent::ErrorEvent; use crate::dom::event::{Event, EventBubbles, EventCancelable, EventStatus}; use crate::dom::eventtarget::EventTarget; @@ -478,6 +479,7 @@ impl DedicatedWorkerGlobalScope { Ok((metadata, bytes)) => (metadata, bytes), }; scope.set_url(metadata.final_url); + scope.set_csp_list(GlobalScope::parse_csp_list_from_metadata(&metadata.headers)); global_scope.set_https_state(metadata.https_state); let source = String::from_utf8_lossy(&bytes); @@ -493,7 +495,12 @@ impl DedicatedWorkerGlobalScope { { let _ar = AutoWorkerReset::new(&global, worker.clone()); - let _ac = enter_realm(scope); + let realm = enter_realm(scope); + define_all_exposed_interfaces( + global.upcast(), + InRealm::entered(&realm), + CanGc::note(), + ); scope.execute_script(DOMString::from(source), CanGc::note()); } diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs index 0d39a12c15e..852a12fc7c5 100644 --- a/components/script/dom/document.rs +++ b/components/script/dom/document.rs @@ -37,7 +37,7 @@ use embedder_traits::{ }; use encoding_rs::{Encoding, UTF_8}; use euclid::default::{Point2D, Rect, Size2D}; -use html5ever::{LocalName, Namespace, QualName, local_name, namespace_url, ns}; +use html5ever::{LocalName, Namespace, QualName, local_name, ns}; use hyper_serde::Serde; use ipc_channel::ipc; use js::rust::{HandleObject, HandleValue}; diff --git a/components/script/dom/domimplementation.rs b/components/script/dom/domimplementation.rs index 521febc17fc..a6c95fad820 100644 --- a/components/script/dom/domimplementation.rs +++ b/components/script/dom/domimplementation.rs @@ -3,7 +3,7 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ use dom_struct::dom_struct; -use html5ever::{local_name, namespace_url, ns}; +use html5ever::{local_name, ns}; use script_traits::DocumentActivity; use crate::document_loader::DocumentLoader; diff --git a/components/script/dom/domtokenlist.rs b/components/script/dom/domtokenlist.rs index 8d479aba80d..83c3d09be23 100644 --- a/components/script/dom/domtokenlist.rs +++ b/components/script/dom/domtokenlist.rs @@ -3,7 +3,7 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ use dom_struct::dom_struct; -use html5ever::{LocalName, namespace_url, ns}; +use html5ever::{LocalName, ns}; use style::str::HTML_SPACE_CHARACTERS; use stylo_atoms::Atom; diff --git a/components/script/dom/element.rs b/components/script/dom/element.rs index 4aebe893bef..3a8ac8f0cd8 100644 --- a/components/script/dom/element.rs +++ b/components/script/dom/element.rs @@ -19,9 +19,7 @@ use embedder_traits::InputMethodType; use euclid::default::{Rect, Size2D}; use html5ever::serialize::TraversalScope; use html5ever::serialize::TraversalScope::{ChildrenOnly, IncludeNode}; -use html5ever::{ - LocalName, Namespace, Prefix, QualName, local_name, namespace_prefix, namespace_url, ns, -}; +use html5ever::{LocalName, Namespace, Prefix, QualName, local_name, namespace_prefix, ns}; use js::jsapi::Heap; use js::jsval::JSVal; use js::rust::HandleObject; @@ -80,7 +78,7 @@ use crate::dom::bindings::codegen::Bindings::ShadowRootBinding::{ use crate::dom::bindings::codegen::Bindings::WindowBinding::{ ScrollBehavior, ScrollToOptions, WindowMethods, }; -use crate::dom::bindings::codegen::UnionTypes::NodeOrString; +use crate::dom::bindings::codegen::UnionTypes::{NodeOrString, TrustedScriptURLOrUSVString}; use crate::dom::bindings::conversions::DerivedFrom; use crate::dom::bindings::error::{Error, ErrorResult, Fallible}; use crate::dom::bindings::inheritance::{Castable, ElementTypeId, HTMLElementTypeId, NodeTypeId}; @@ -151,6 +149,7 @@ use crate::dom::raredata::ElementRareData; use crate::dom::servoparser::ServoParser; use crate::dom::shadowroot::{IsUserAgentWidget, ShadowRoot}; use crate::dom::text::Text; +use crate::dom::types::TrustedTypePolicyFactory; use crate::dom::validation::Validatable; use crate::dom::validitystate::ValidationFlags; use crate::dom::virtualmethods::{VirtualMethods, vtable_for}; @@ -1930,6 +1929,53 @@ impl Element { self.set_attribute(local_name, AttrValue::String(value.to_string()), can_gc); } + pub(crate) fn get_trusted_type_url_attribute( + &self, + local_name: &LocalName, + ) -> TrustedScriptURLOrUSVString { + assert_eq!(*local_name, local_name.to_ascii_lowercase()); + let attr = match self.get_attribute(&ns!(), local_name) { + Some(attr) => attr, + None => return TrustedScriptURLOrUSVString::USVString(USVString::default()), + }; + let value = &**attr.value(); + // XXXManishearth this doesn't handle `javascript:` urls properly + self.owner_document() + .base_url() + .join(value) + .map(|parsed| TrustedScriptURLOrUSVString::USVString(USVString(parsed.into_string()))) + .unwrap_or_else(|_| TrustedScriptURLOrUSVString::USVString(USVString(value.to_owned()))) + } + + pub(crate) fn set_trusted_type_url_attribute( + &self, + local_name: &LocalName, + value: TrustedScriptURLOrUSVString, + can_gc: CanGc, + ) -> Fallible<()> { + assert_eq!(*local_name, local_name.to_ascii_lowercase()); + let value = match value { + TrustedScriptURLOrUSVString::USVString(url) => { + let global = self.owner_global(); + // TODO(36258): Reflectively get the name of the class + let sink = format!("{} {}", "HTMLScriptElement", &local_name); + let result = TrustedTypePolicyFactory::get_trusted_type_compliant_string( + &global, + url.to_string(), + &sink, + "'script'", + can_gc, + ); + result? + }, + // This partially implements <https://w3c.github.io/trusted-types/dist/spec/#get-trusted-type-compliant-string-algorithm> + // Step 1: If input is an instance of expectedType, return stringified input and abort these steps. + TrustedScriptURLOrUSVString::TrustedScriptURL(script_url) => script_url.to_string(), + }; + self.set_attribute(local_name, AttrValue::String(value), can_gc); + Ok(()) + } + pub(crate) fn get_string_attribute(&self, local_name: &LocalName) -> DOMString { match self.get_attribute(&ns!(), local_name) { Some(x) => x.Value(), diff --git a/components/script/dom/globalscope.rs b/components/script/dom/globalscope.rs index 2582291ed87..2658911c795 100644 --- a/components/script/dom/globalscope.rs +++ b/components/script/dom/globalscope.rs @@ -22,7 +22,7 @@ use constellation_traits::{ ScriptToConstellationChan, ScriptToConstellationMessage, }; use content_security_policy::{ - CheckResult, CspList, PolicyDisposition, Violation, ViolationResource, + CheckResult, CspList, PolicyDisposition, PolicySource, Violation, ViolationResource, }; use crossbeam_channel::Sender; use devtools_traits::{PageError, ScriptToDevtoolsControlMsg}; @@ -30,6 +30,8 @@ use dom_struct::dom_struct; use embedder_traits::{ EmbedderMsg, GamepadEvent, GamepadSupportedHapticEffects, GamepadUpdateType, }; +use http::HeaderMap; +use hyper_serde::Serde; use ipc_channel::ipc::{self, IpcSender}; use ipc_channel::router::ROUTER; use js::glue::{IsWrapper, UnwrapObjectDynamic}; @@ -2415,6 +2417,27 @@ impl GlobalScope { unreachable!(); } + /// <https://www.w3.org/TR/CSP/#initialize-document-csp> + pub(crate) fn parse_csp_list_from_metadata( + headers: &Option<Serde<HeaderMap>>, + ) -> Option<CspList> { + // TODO: Implement step 1 (local scheme special case) + let mut csp = headers.as_ref()?.get_all("content-security-policy").iter(); + // This silently ignores the CSP if it contains invalid Unicode. + // We should probably report an error somewhere. + let c = csp.next().and_then(|c| c.to_str().ok())?; + let mut csp_list = CspList::parse(c, PolicySource::Header, PolicyDisposition::Enforce); + for c in csp { + let c = c.to_str().ok()?; + csp_list.append(CspList::parse( + c, + PolicySource::Header, + PolicyDisposition::Enforce, + )); + } + Some(csp_list) + } + /// Get the [base url](https://html.spec.whatwg.org/multipage/#api-base-url) /// for this global scope. pub(crate) fn api_base_url(&self) -> ServoUrl { @@ -3089,10 +3112,10 @@ impl GlobalScope { /// <https://www.w3.org/TR/CSP/#get-csp-of-object> pub(crate) fn get_csp_list(&self) -> Option<CspList> { - if self.downcast::<Window>().is_some() { + if self.downcast::<Window>().is_some() || self.downcast::<WorkerGlobalScope>().is_some() { return self.policy_container().csp_list; } - // TODO: Worker and Worklet global scopes. + // TODO: Worklet global scopes. None } @@ -3352,7 +3375,7 @@ impl GlobalScope { let data = structuredclone::write(cx, value, Some(guard))?; - structuredclone::read(self, data, retval).map_err(|_| Error::DataClone(None))?; + structuredclone::read(self, data, retval)?; Ok(()) } @@ -3456,11 +3479,16 @@ impl GlobalScope { ViolationResource::TrustedTypePolicy { sample } => { (Some(sample), "trusted-types-policy".to_owned()) }, + ViolationResource::TrustedTypeSink { sample } => { + (Some(sample), "trusted-types-sink".to_owned()) + }, }; let report = CSPViolationReportBuilder::default() .resource(resource) .sample(sample) .effective_directive(violation.directive.name) + .original_policy(violation.policy.to_string()) + .report_only(violation.policy.disposition == PolicyDisposition::Report) .build(self); let task = CSPViolationReportTask::new(self, report); self.task_manager() diff --git a/components/script/dom/htmlaudioelement.rs b/components/script/dom/htmlaudioelement.rs index dbf0a88eb2a..938c2e7ccd6 100644 --- a/components/script/dom/htmlaudioelement.rs +++ b/components/script/dom/htmlaudioelement.rs @@ -3,7 +3,7 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ use dom_struct::dom_struct; -use html5ever::{LocalName, Prefix, QualName, local_name, namespace_url, ns}; +use html5ever::{LocalName, Prefix, QualName, local_name, ns}; use js::rust::HandleObject; use crate::dom::bindings::codegen::Bindings::ElementBinding::Element_Binding::ElementMethods; diff --git a/components/script/dom/htmlbaseelement.rs b/components/script/dom/htmlbaseelement.rs index 45a13f83772..9f263e1056c 100644 --- a/components/script/dom/htmlbaseelement.rs +++ b/components/script/dom/htmlbaseelement.rs @@ -3,7 +3,7 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ use dom_struct::dom_struct; -use html5ever::{LocalName, Prefix, local_name, namespace_url, ns}; +use html5ever::{LocalName, Prefix, local_name, ns}; use js::rust::HandleObject; use servo_url::ServoUrl; diff --git a/components/script/dom/htmlbodyelement.rs b/components/script/dom/htmlbodyelement.rs index 5cd877cdf82..b4efba9bed9 100644 --- a/components/script/dom/htmlbodyelement.rs +++ b/components/script/dom/htmlbodyelement.rs @@ -4,7 +4,7 @@ use dom_struct::dom_struct; use embedder_traits::{EmbedderMsg, LoadStatus}; -use html5ever::{LocalName, Prefix, local_name, namespace_url, ns}; +use html5ever::{LocalName, Prefix, local_name, ns}; use js::rust::HandleObject; use servo_url::ServoUrl; use style::attr::AttrValue; diff --git a/components/script/dom/htmlbuttonelement.rs b/components/script/dom/htmlbuttonelement.rs index 09d0ae6e73f..6f83360e187 100644 --- a/components/script/dom/htmlbuttonelement.rs +++ b/components/script/dom/htmlbuttonelement.rs @@ -6,7 +6,7 @@ use std::cell::Cell; use std::default::Default; use dom_struct::dom_struct; -use html5ever::{LocalName, Prefix, local_name, namespace_url}; +use html5ever::{LocalName, Prefix, local_name}; use js::rust::HandleObject; use stylo_dom::ElementState; diff --git a/components/script/dom/htmlcanvaselement.rs b/components/script/dom/htmlcanvaselement.rs index 77e8b412146..9e20539ceca 100644 --- a/components/script/dom/htmlcanvaselement.rs +++ b/components/script/dom/htmlcanvaselement.rs @@ -12,7 +12,7 @@ use constellation_traits::BlobImpl; use constellation_traits::ScriptToConstellationMessage; use dom_struct::dom_struct; use euclid::default::Size2D; -use html5ever::{LocalName, Prefix, local_name, namespace_url, ns}; +use html5ever::{LocalName, Prefix, local_name, ns}; use image::codecs::jpeg::JpegEncoder; use image::codecs::png::PngEncoder; use image::codecs::webp::WebPEncoder; diff --git a/components/script/dom/htmldialogelement.rs b/components/script/dom/htmldialogelement.rs index 3872932049d..fbd84e4efd3 100644 --- a/components/script/dom/htmldialogelement.rs +++ b/components/script/dom/htmldialogelement.rs @@ -3,7 +3,7 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ use dom_struct::dom_struct; -use html5ever::{LocalName, Prefix, local_name, namespace_url, ns}; +use html5ever::{LocalName, Prefix, local_name, ns}; use js::rust::HandleObject; use crate::dom::bindings::cell::DomRefCell; diff --git a/components/script/dom/htmlelement.rs b/components/script/dom/htmlelement.rs index 0cdfebf5342..9505d5182c7 100644 --- a/components/script/dom/htmlelement.rs +++ b/components/script/dom/htmlelement.rs @@ -7,7 +7,7 @@ use std::default::Default; use std::rc::Rc; use dom_struct::dom_struct; -use html5ever::{LocalName, Prefix, local_name, namespace_url, ns}; +use html5ever::{LocalName, Prefix, local_name, ns}; use js::rust::HandleObject; use script_layout_interface::QueryMsg; use style::attr::AttrValue; diff --git a/components/script/dom/htmlfontelement.rs b/components/script/dom/htmlfontelement.rs index ee7015fb66d..fb02a10a4e4 100644 --- a/components/script/dom/htmlfontelement.rs +++ b/components/script/dom/htmlfontelement.rs @@ -4,7 +4,7 @@ use cssparser::match_ignore_ascii_case; use dom_struct::dom_struct; -use html5ever::{LocalName, Prefix, local_name, namespace_url, ns}; +use html5ever::{LocalName, Prefix, local_name, ns}; use js::rust::HandleObject; use style::attr::AttrValue; use style::color::AbsoluteColor; diff --git a/components/script/dom/htmlformelement.rs b/components/script/dom/htmlformelement.rs index 1294e11043a..ce6dcca66f3 100644 --- a/components/script/dom/htmlformelement.rs +++ b/components/script/dom/htmlformelement.rs @@ -9,7 +9,7 @@ use constellation_traits::{LoadData, LoadOrigin, NavigationHistoryBehavior}; use dom_struct::dom_struct; use encoding_rs::{Encoding, UTF_8}; use headers::{ContentType, HeaderMapExt}; -use html5ever::{LocalName, Prefix, local_name, namespace_url, ns}; +use html5ever::{LocalName, Prefix, local_name, ns}; use http::Method; use js::rust::HandleObject; use mime::{self, Mime}; diff --git a/components/script/dom/htmlheadelement.rs b/components/script/dom/htmlheadelement.rs index b4845fc949c..7b2d715318f 100644 --- a/components/script/dom/htmlheadelement.rs +++ b/components/script/dom/htmlheadelement.rs @@ -4,7 +4,7 @@ use content_security_policy::{CspList, PolicyDisposition, PolicySource}; use dom_struct::dom_struct; -use html5ever::{LocalName, Prefix, local_name, namespace_url, ns}; +use html5ever::{LocalName, Prefix, local_name, ns}; use js::rust::HandleObject; use crate::dom::bindings::codegen::Bindings::DocumentBinding::DocumentMethods; diff --git a/components/script/dom/htmlhrelement.rs b/components/script/dom/htmlhrelement.rs index 5754b2f3dd3..c88a0fcf184 100644 --- a/components/script/dom/htmlhrelement.rs +++ b/components/script/dom/htmlhrelement.rs @@ -3,7 +3,7 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ use dom_struct::dom_struct; -use html5ever::{LocalName, Prefix, local_name, namespace_url, ns}; +use html5ever::{LocalName, Prefix, local_name, ns}; use js::rust::HandleObject; use style::attr::{AttrValue, LengthOrPercentageOrAuto}; use style::color::AbsoluteColor; diff --git a/components/script/dom/htmlhyperlinkelementutils.rs b/components/script/dom/htmlhyperlinkelementutils.rs index 52b16f796e5..699db4d62f4 100644 --- a/components/script/dom/htmlhyperlinkelementutils.rs +++ b/components/script/dom/htmlhyperlinkelementutils.rs @@ -2,7 +2,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -use html5ever::{local_name, namespace_url, ns}; +use html5ever::{local_name, ns}; use servo_url::ServoUrl; use crate::dom::bindings::cell::DomRefCell; diff --git a/components/script/dom/htmliframeelement.rs b/components/script/dom/htmliframeelement.rs index 33457cb29d4..c5194c4527f 100644 --- a/components/script/dom/htmliframeelement.rs +++ b/components/script/dom/htmliframeelement.rs @@ -13,7 +13,7 @@ use constellation_traits::{ }; use dom_struct::dom_struct; use embedder_traits::ViewportDetails; -use html5ever::{LocalName, Prefix, local_name, namespace_url, ns}; +use html5ever::{LocalName, Prefix, local_name, ns}; use js::rust::HandleObject; use net_traits::ReferrerPolicy; use profile_traits::ipc as ProfiledIpc; diff --git a/components/script/dom/htmlimageelement.rs b/components/script/dom/htmlimageelement.rs index e6b4336fe54..adff445ae1c 100644 --- a/components/script/dom/htmlimageelement.rs +++ b/components/script/dom/htmlimageelement.rs @@ -14,7 +14,7 @@ use content_security_policy as csp; use cssparser::{Parser, ParserInput}; use dom_struct::dom_struct; use euclid::Point2D; -use html5ever::{LocalName, Prefix, QualName, local_name, namespace_url, ns}; +use html5ever::{LocalName, Prefix, QualName, local_name, ns}; use js::jsapi::JSAutoRealm; use js::rust::HandleObject; use mime::{self, Mime}; diff --git a/components/script/dom/htmlinputelement.rs b/components/script/dom/htmlinputelement.rs index 42165499032..1999c7193ff 100644 --- a/components/script/dom/htmlinputelement.rs +++ b/components/script/dom/htmlinputelement.rs @@ -14,7 +14,7 @@ use std::{f64, ptr}; use dom_struct::dom_struct; use embedder_traits::{FilterPattern, InputMethodType}; use encoding_rs::Encoding; -use html5ever::{LocalName, Prefix, local_name, namespace_url, ns}; +use html5ever::{LocalName, Prefix, local_name, ns}; use js::jsapi::{ ClippedTime, DateGetMsecSinceEpoch, Handle, JS_ClearPendingException, JSObject, NewDateObject, NewUCRegExpObject, ObjectIsDate, RegExpFlag_UnicodeSets, RegExpFlags, diff --git a/components/script/dom/htmllabelelement.rs b/components/script/dom/htmllabelelement.rs index efa386c7cdd..9733c3b7c6e 100644 --- a/components/script/dom/htmllabelelement.rs +++ b/components/script/dom/htmllabelelement.rs @@ -3,7 +3,7 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ use dom_struct::dom_struct; -use html5ever::{LocalName, Prefix, local_name, namespace_url, ns}; +use html5ever::{LocalName, Prefix, local_name, ns}; use js::rust::HandleObject; use style::attr::AttrValue; diff --git a/components/script/dom/htmllinkelement.rs b/components/script/dom/htmllinkelement.rs index 969551f98c8..db5c14af450 100644 --- a/components/script/dom/htmllinkelement.rs +++ b/components/script/dom/htmllinkelement.rs @@ -10,7 +10,7 @@ use base::id::WebViewId; use content_security_policy as csp; use dom_struct::dom_struct; use embedder_traits::EmbedderMsg; -use html5ever::{LocalName, Prefix, local_name, namespace_url, ns}; +use html5ever::{LocalName, Prefix, local_name, ns}; use js::rust::HandleObject; use net_traits::policy_container::PolicyContainer; use net_traits::request::{ diff --git a/components/script/dom/htmlmediaelement.rs b/components/script/dom/htmlmediaelement.rs index 8ddef6d33cb..c07f866ab41 100644 --- a/components/script/dom/htmlmediaelement.rs +++ b/components/script/dom/htmlmediaelement.rs @@ -16,7 +16,7 @@ use embedder_traits::resources::{self, Resource as EmbedderResource}; use embedder_traits::{MediaPositionState, MediaSessionEvent, MediaSessionPlaybackState}; use euclid::default::Size2D; use headers::{ContentLength, ContentRange, HeaderMapExt}; -use html5ever::{LocalName, Prefix, local_name, namespace_url, ns}; +use html5ever::{LocalName, Prefix, local_name, ns}; use http::StatusCode; use http::header::{self, HeaderMap, HeaderValue}; use ipc_channel::ipc::{self, IpcSharedMemory, channel}; diff --git a/components/script/dom/htmlmetaelement.rs b/components/script/dom/htmlmetaelement.rs index 74b79fadad3..e94a5e1ff33 100644 --- a/components/script/dom/htmlmetaelement.rs +++ b/components/script/dom/htmlmetaelement.rs @@ -3,7 +3,7 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ use dom_struct::dom_struct; -use html5ever::{LocalName, Prefix, local_name, namespace_url, ns}; +use html5ever::{LocalName, Prefix, local_name, ns}; use js::rust::HandleObject; use style::str::HTML_SPACE_CHARACTERS; diff --git a/components/script/dom/htmlobjectelement.rs b/components/script/dom/htmlobjectelement.rs index a6941461e5d..276bddca986 100644 --- a/components/script/dom/htmlobjectelement.rs +++ b/components/script/dom/htmlobjectelement.rs @@ -5,7 +5,7 @@ use std::default::Default; use dom_struct::dom_struct; -use html5ever::{LocalName, Prefix, local_name, namespace_url, ns}; +use html5ever::{LocalName, Prefix, local_name, ns}; use js::rust::HandleObject; use pixels::Image; use servo_arc::Arc; diff --git a/components/script/dom/htmloptionelement.rs b/components/script/dom/htmloptionelement.rs index d6e8be04b64..b573388c73a 100644 --- a/components/script/dom/htmloptionelement.rs +++ b/components/script/dom/htmloptionelement.rs @@ -6,7 +6,7 @@ use std::cell::Cell; use std::convert::TryInto; use dom_struct::dom_struct; -use html5ever::{LocalName, Prefix, QualName, local_name, namespace_url, ns}; +use html5ever::{LocalName, Prefix, QualName, local_name, ns}; use js::rust::HandleObject; use style::str::{split_html_space_chars, str_join}; use stylo_dom::ElementState; diff --git a/components/script/dom/htmlscriptelement.rs b/components/script/dom/htmlscriptelement.rs index 3a71de1fa66..9452dcb17a6 100644 --- a/components/script/dom/htmlscriptelement.rs +++ b/components/script/dom/htmlscriptelement.rs @@ -44,6 +44,8 @@ use crate::dom::bindings::codegen::Bindings::DocumentBinding::DocumentMethods; use crate::dom::bindings::codegen::Bindings::HTMLScriptElementBinding::HTMLScriptElementMethods; use crate::dom::bindings::codegen::Bindings::NodeBinding::NodeMethods; use crate::dom::bindings::codegen::GenericBindings::HTMLElementBinding::HTMLElement_Binding::HTMLElementMethods; +use crate::dom::bindings::codegen::UnionTypes::TrustedScriptURLOrUSVString; +use crate::dom::bindings::error::Fallible; use crate::dom::bindings::inheritance::Castable; use crate::dom::bindings::refcounted::Trusted; use crate::dom::bindings::reflector::DomGlobal; @@ -1342,10 +1344,10 @@ impl VirtualMethods for HTMLScriptElement { impl HTMLScriptElementMethods<crate::DomTypeHolder> for HTMLScriptElement { // https://html.spec.whatwg.org/multipage/#dom-script-src - make_url_getter!(Src, "src"); + make_trusted_type_url_getter!(Src, "src"); // https://html.spec.whatwg.org/multipage/#dom-script-src - make_url_setter!(SetSrc, "src"); + make_trusted_type_url_setter!(SetSrc, "src"); // https://html.spec.whatwg.org/multipage/#dom-script-type make_getter!(Type, "type"); diff --git a/components/script/dom/htmlslotelement.rs b/components/script/dom/htmlslotelement.rs index 022cbbf388a..68fa105e64d 100644 --- a/components/script/dom/htmlslotelement.rs +++ b/components/script/dom/htmlslotelement.rs @@ -5,7 +5,7 @@ use std::cell::{Cell, Ref, RefCell}; use dom_struct::dom_struct; -use html5ever::{LocalName, Prefix, local_name, namespace_url, ns}; +use html5ever::{LocalName, Prefix, local_name, ns}; use js::gc::RootedVec; use js::rust::HandleObject; use script_bindings::codegen::InheritTypes::{CharacterDataTypeId, NodeTypeId}; diff --git a/components/script/dom/htmltablecellelement.rs b/components/script/dom/htmltablecellelement.rs index 623dfdea54b..4f312e928c4 100644 --- a/components/script/dom/htmltablecellelement.rs +++ b/components/script/dom/htmltablecellelement.rs @@ -3,7 +3,7 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ use dom_struct::dom_struct; -use html5ever::{LocalName, Prefix, local_name, namespace_url, ns}; +use html5ever::{LocalName, Prefix, local_name, ns}; use js::rust::HandleObject; use style::attr::{AttrValue, LengthOrPercentageOrAuto}; use style::color::AbsoluteColor; diff --git a/components/script/dom/htmltablecolelement.rs b/components/script/dom/htmltablecolelement.rs index b2821fd38e2..c7ad4afd944 100644 --- a/components/script/dom/htmltablecolelement.rs +++ b/components/script/dom/htmltablecolelement.rs @@ -3,7 +3,7 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ use dom_struct::dom_struct; -use html5ever::{LocalName, Prefix, local_name, namespace_url, ns}; +use html5ever::{LocalName, Prefix, local_name, ns}; use js::rust::HandleObject; use style::attr::{AttrValue, LengthOrPercentageOrAuto}; diff --git a/components/script/dom/htmltableelement.rs b/components/script/dom/htmltableelement.rs index 6783da6e97d..c24ec5490c5 100644 --- a/components/script/dom/htmltableelement.rs +++ b/components/script/dom/htmltableelement.rs @@ -5,7 +5,7 @@ use std::cell::Cell; use dom_struct::dom_struct; -use html5ever::{LocalName, Prefix, local_name, namespace_url, ns}; +use html5ever::{LocalName, Prefix, local_name, ns}; use js::rust::HandleObject; use style::attr::{AttrValue, LengthOrPercentageOrAuto, parse_unsigned_integer}; use style::color::AbsoluteColor; diff --git a/components/script/dom/htmltablerowelement.rs b/components/script/dom/htmltablerowelement.rs index a13238ca2e4..6c2d502ce88 100644 --- a/components/script/dom/htmltablerowelement.rs +++ b/components/script/dom/htmltablerowelement.rs @@ -3,7 +3,7 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ use dom_struct::dom_struct; -use html5ever::{LocalName, Prefix, local_name, namespace_url, ns}; +use html5ever::{LocalName, Prefix, local_name, ns}; use js::rust::HandleObject; use style::attr::{AttrValue, LengthOrPercentageOrAuto}; use style::color::AbsoluteColor; diff --git a/components/script/dom/htmltablesectionelement.rs b/components/script/dom/htmltablesectionelement.rs index d97e7445590..60978c64fc1 100644 --- a/components/script/dom/htmltablesectionelement.rs +++ b/components/script/dom/htmltablesectionelement.rs @@ -3,7 +3,7 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ use dom_struct::dom_struct; -use html5ever::{LocalName, Prefix, local_name, namespace_url, ns}; +use html5ever::{LocalName, Prefix, local_name, ns}; use js::rust::HandleObject; use style::attr::{AttrValue, LengthOrPercentageOrAuto}; use style::color::AbsoluteColor; diff --git a/components/script/dom/htmltemplateelement.rs b/components/script/dom/htmltemplateelement.rs index 2c98035736e..128309b816b 100644 --- a/components/script/dom/htmltemplateelement.rs +++ b/components/script/dom/htmltemplateelement.rs @@ -3,7 +3,7 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ use dom_struct::dom_struct; -use html5ever::{LocalName, Prefix, namespace_url}; +use html5ever::{LocalName, Prefix}; use js::rust::HandleObject; use crate::dom::bindings::codegen::Bindings::DocumentBinding::DocumentMethods; diff --git a/components/script/dom/htmltextareaelement.rs b/components/script/dom/htmltextareaelement.rs index 7f47ee5a90a..a7435b40fdd 100644 --- a/components/script/dom/htmltextareaelement.rs +++ b/components/script/dom/htmltextareaelement.rs @@ -7,7 +7,7 @@ use std::default::Default; use std::ops::Range; use dom_struct::dom_struct; -use html5ever::{LocalName, Prefix, local_name, namespace_url, ns}; +use html5ever::{LocalName, Prefix, local_name, ns}; use js::rust::HandleObject; use style::attr::AttrValue; use stylo_dom::ElementState; diff --git a/components/script/dom/htmlvideoelement.rs b/components/script/dom/htmlvideoelement.rs index a6f9103200b..6f27c164d02 100644 --- a/components/script/dom/htmlvideoelement.rs +++ b/components/script/dom/htmlvideoelement.rs @@ -8,7 +8,7 @@ use std::sync::Arc; use content_security_policy as csp; use dom_struct::dom_struct; use euclid::default::Size2D; -use html5ever::{LocalName, Prefix, local_name, namespace_url, ns}; +use html5ever::{LocalName, Prefix, local_name, ns}; use ipc_channel::ipc; use js::rust::HandleObject; use net_traits::image_cache::{ @@ -233,8 +233,10 @@ impl HTMLVideoElement { .credentials_mode(CredentialsMode::Include) .use_url_credentials(true) .origin(document.origin().immutable().clone()) - .pipeline_id(Some(document.global().pipeline_id())); - + .pipeline_id(Some(document.global().pipeline_id())) + .insecure_requests_policy(document.insecure_requests_policy()) + .has_trustworthy_ancestor_origin(document.has_trustworthy_ancestor_origin()) + .policy_container(document.policy_container().to_owned()); // Step 5. // This delay must be independent from the ones created by HTMLMediaElement during // its media load algorithm, otherwise a code like diff --git a/components/script/dom/macros.rs b/components/script/dom/macros.rs index 7679e4a546d..b3f222af0da 100644 --- a/components/script/dom/macros.rs +++ b/components/script/dom/macros.rs @@ -122,6 +122,32 @@ macro_rules! make_url_setter( ); #[macro_export] +macro_rules! make_trusted_type_url_getter( + ( $attr:ident, $htmlname:tt ) => ( + fn $attr(&self) -> TrustedScriptURLOrUSVString { + use $crate::dom::bindings::inheritance::Castable; + use $crate::dom::element::Element; + let element = self.upcast::<Element>(); + element.get_trusted_type_url_attribute(&html5ever::local_name!($htmlname)) + } + ); +); + +#[macro_export] +macro_rules! make_trusted_type_url_setter( + ( $attr:ident, $htmlname:tt ) => ( + fn $attr(&self, value: TrustedScriptURLOrUSVString, can_gc: CanGc) -> Fallible<()> { + use $crate::dom::bindings::inheritance::Castable; + use $crate::dom::element::Element; + use $crate::script_runtime::CanGc; + let element = self.upcast::<Element>(); + element.set_trusted_type_url_attribute(&html5ever::local_name!($htmlname), + value, can_gc) + } + ); +); + +#[macro_export] macro_rules! make_form_action_getter( ( $attr:ident, $htmlname:tt ) => ( fn $attr(&self) -> DOMString { diff --git a/components/script/dom/messageport.rs b/components/script/dom/messageport.rs index 3e25602b365..c3904dbb19a 100644 --- a/components/script/dom/messageport.rs +++ b/components/script/dom/messageport.rs @@ -20,7 +20,7 @@ use crate::dom::bindings::codegen::Bindings::MessagePortBinding::{ MessagePortMethods, StructuredSerializeOptions, }; use crate::dom::bindings::conversions::root_from_object; -use crate::dom::bindings::error::{Error, ErrorResult}; +use crate::dom::bindings::error::{Error, ErrorResult, ErrorToJsval}; use crate::dom::bindings::inheritance::Castable; use crate::dom::bindings::reflector::{DomGlobal, reflect_dom_object}; use crate::dom::bindings::root::DomRoot; @@ -180,14 +180,13 @@ impl MessagePort { let result = self.pack_and_post_message(type_, value, can_gc); // If result is an abrupt completion, - if result.is_err() { + if let Err(error) = result.as_ref() { // Perform ! CrossRealmTransformSendError(port, result.[[Value]]). - // Note: we send UndefinedValue across, - // because somehow sending an error results in another error. - // The Error::DataClone, which is the only one that is sent across, - // will be created upon receipt. let cx = GlobalScope::get_cx(); rooted!(in(*cx) let mut rooted_error = UndefinedValue()); + error + .clone() + .to_jsval(cx, &self.global(), rooted_error.handle_mut(), can_gc); self.cross_realm_transform_send_error(rooted_error.handle(), can_gc); } diff --git a/components/script/dom/mutationobserver.rs b/components/script/dom/mutationobserver.rs index 558453f9235..473698d5df1 100644 --- a/components/script/dom/mutationobserver.rs +++ b/components/script/dom/mutationobserver.rs @@ -6,7 +6,7 @@ use std::cell::LazyCell; use std::rc::Rc; use dom_struct::dom_struct; -use html5ever::{LocalName, Namespace, namespace_url, ns}; +use html5ever::{LocalName, Namespace, ns}; use js::rust::HandleObject; use crate::dom::bindings::callback::ExceptionHandling; diff --git a/components/script/dom/node.rs b/components/script/dom/node.rs index bcaff69adb9..2a01370085a 100644 --- a/components/script/dom/node.rs +++ b/components/script/dom/node.rs @@ -21,7 +21,7 @@ use dom_struct::dom_struct; use embedder_traits::UntrustedNodeAddress; use euclid::default::{Rect, Size2D, Vector2D}; use html5ever::serialize::HtmlSerializer; -use html5ever::{Namespace, Prefix, QualName, namespace_url, ns, serialize as html_serialize}; +use html5ever::{Namespace, Prefix, QualName, ns, serialize as html_serialize}; use js::jsapi::JSObject; use js::rust::HandleObject; use libc::{self, c_void, uintptr_t}; diff --git a/components/script/dom/readablestream.rs b/components/script/dom/readablestream.rs index bf39c43e923..49e740c6d6e 100644 --- a/components/script/dom/readablestream.rs +++ b/components/script/dom/readablestream.rs @@ -2088,19 +2088,8 @@ impl CrossRealmTransformReadable { // Otherwise, if type is "error", if type_string == "error" { - if value.is_undefined() { - // Note: for DataClone errors, we send UndefinedValue across, - // because somehow sending the error results in another error. - // The error is then created here. - rooted!(in(*cx) let mut rooted_error = UndefinedValue()); - Error::DataClone(None).to_jsval(cx, global, rooted_error.handle_mut(), can_gc); - - // Perform ! ReadableStreamDefaultControllerError(controller, value). - self.controller.error(rooted_error.handle(), can_gc); - } else { - // Perform ! ReadableStreamDefaultControllerError(controller, value). - self.controller.error(value.handle(), can_gc); - } + // Perform ! ReadableStreamDefaultControllerError(controller, value). + self.controller.error(value.handle(), can_gc); // Disentangle port. global.disentangle_port(port); diff --git a/components/script/dom/serviceworkerglobalscope.rs b/components/script/dom/serviceworkerglobalscope.rs index defed40f0ef..9b431da9c0e 100644 --- a/components/script/dom/serviceworkerglobalscope.rs +++ b/components/script/dom/serviceworkerglobalscope.rs @@ -38,6 +38,7 @@ use crate::dom::bindings::root::{DomRoot, RootCollection, ThreadLocalStackRoots} use crate::dom::bindings::str::DOMString; use crate::dom::bindings::structuredclone; use crate::dom::bindings::trace::CustomTraceable; +use crate::dom::bindings::utils::define_all_exposed_interfaces; use crate::dom::dedicatedworkerglobalscope::AutoWorkerReset; use crate::dom::event::Event; use crate::dom::eventtarget::EventTarget; @@ -379,6 +380,11 @@ impl ServiceWorkerGlobalScope { { // TODO: use AutoWorkerReset as in dedicated worker? let realm = enter_realm(scope); + define_all_exposed_interfaces( + scope.upcast(), + InRealm::entered(&realm), + CanGc::note(), + ); scope.execute_script(DOMString::from(source), CanGc::note()); global.dispatch_activate(CanGc::note(), InRealm::entered(&realm)); } diff --git a/components/script/dom/servoparser/async_html.rs b/components/script/dom/servoparser/async_html.rs index 7ddc425678e..1d6f4af0b01 100644 --- a/components/script/dom/servoparser/async_html.rs +++ b/components/script/dom/servoparser/async_html.rs @@ -18,9 +18,7 @@ use html5ever::tokenizer::{Tokenizer as HtmlTokenizer, TokenizerOpts}; use html5ever::tree_builder::{ ElementFlags, NodeOrText as HtmlNodeOrText, QuirksMode, TreeBuilder, TreeBuilderOpts, TreeSink, }; -use html5ever::{ - Attribute as HtmlAttribute, ExpandedName, QualName, local_name, namespace_url, ns, -}; +use html5ever::{Attribute as HtmlAttribute, ExpandedName, QualName, local_name, ns}; use markup5ever::TokenizerResult; use servo_url::ServoUrl; use style::context::QuirksMode as ServoQuirksMode; diff --git a/components/script/dom/servoparser/html.rs b/components/script/dom/servoparser/html.rs index 3bd486b5cb7..97856256268 100644 --- a/components/script/dom/servoparser/html.rs +++ b/components/script/dom/servoparser/html.rs @@ -12,7 +12,7 @@ use html5ever::serialize::TraversalScope::IncludeNode; use html5ever::serialize::{AttrRef, Serialize, Serializer, TraversalScope}; use html5ever::tokenizer::{Tokenizer as HtmlTokenizer, TokenizerOpts}; use html5ever::tree_builder::{TreeBuilder, TreeBuilderOpts}; -use html5ever::{QualName, local_name, namespace_url, ns}; +use html5ever::{QualName, local_name, ns}; use markup5ever::TokenizerResult; use script_bindings::trace::CustomTraceable; use servo_url::ServoUrl; diff --git a/components/script/dom/servoparser/mod.rs b/components/script/dom/servoparser/mod.rs index 970c1543d09..0650fde676e 100644 --- a/components/script/dom/servoparser/mod.rs +++ b/components/script/dom/servoparser/mod.rs @@ -9,7 +9,7 @@ use base::cross_process_instant::CrossProcessInstant; use base::id::PipelineId; use base64::Engine as _; use base64::engine::general_purpose; -use content_security_policy::{self as csp, CspList}; +use content_security_policy as csp; use dom_struct::dom_struct; use embedder_traits::resources::{self, Resource}; use encoding_rs::Encoding; @@ -17,7 +17,7 @@ use html5ever::buffer_queue::BufferQueue; use html5ever::tendril::fmt::UTF8; use html5ever::tendril::{ByteTendril, StrTendril, TendrilSink}; use html5ever::tree_builder::{ElementFlags, NodeOrText, QuirksMode, TreeSink}; -use html5ever::{Attribute, ExpandedName, LocalName, QualName, local_name, namespace_url, ns}; +use html5ever::{Attribute, ExpandedName, LocalName, QualName, local_name, ns}; use hyper_serde::Serde; use markup5ever::TokenizerResult; use mime::{self, Mime}; @@ -59,6 +59,7 @@ use crate::dom::document::{Document, DocumentSource, HasBrowsingContext, IsHTMLD use crate::dom::documentfragment::DocumentFragment; use crate::dom::documenttype::DocumentType; use crate::dom::element::{CustomElementCreationMode, Element, ElementCreator}; +use crate::dom::globalscope::GlobalScope; use crate::dom::htmlformelement::{FormControlElementHelpers, HTMLFormElement}; use crate::dom::htmlimageelement::HTMLImageElement; use crate::dom::htmlinputelement::HTMLInputElement; @@ -850,29 +851,9 @@ impl FetchResponseListener for ParserContext { .map(Serde::into_inner) .map(Into::into); - // https://www.w3.org/TR/CSP/#initialize-document-csp - // TODO: Implement step 1 (local scheme special case) - let csp_list = metadata.as_ref().and_then(|m| { - let h = m.headers.as_ref()?; - let mut csp = h.get_all("content-security-policy").iter(); - // This silently ignores the CSP if it contains invalid Unicode. - // We should probably report an error somewhere. - let c = csp.next().and_then(|c| c.to_str().ok())?; - let mut csp_list = CspList::parse( - c, - csp::PolicySource::Header, - csp::PolicyDisposition::Enforce, - ); - for c in csp { - let c = c.to_str().ok()?; - csp_list.append(CspList::parse( - c, - csp::PolicySource::Header, - csp::PolicyDisposition::Enforce, - )); - } - Some(csp_list) - }); + let csp_list = metadata + .as_ref() + .and_then(|m| GlobalScope::parse_csp_list_from_metadata(&m.headers)); let parser = match ScriptThread::page_headers_available(&self.id, metadata, CanGc::note()) { Some(parser) => parser, @@ -1462,7 +1443,7 @@ impl TreeSink for Sink { clonable, serializable, delegatesfocus, - SlotAssignmentMode::Manual, + SlotAssignmentMode::Named, CanGc::note(), ) { Ok(shadow_root) => { diff --git a/components/script/dom/svgelement.rs b/components/script/dom/svgelement.rs index 9a48653b768..9c8b990826d 100644 --- a/components/script/dom/svgelement.rs +++ b/components/script/dom/svgelement.rs @@ -3,7 +3,7 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ use dom_struct::dom_struct; -use html5ever::{LocalName, Prefix, local_name, namespace_url, ns}; +use html5ever::{LocalName, Prefix, local_name, ns}; use js::rust::HandleObject; use script_bindings::str::DOMString; use stylo_dom::ElementState; diff --git a/components/script/dom/svgsvgelement.rs b/components/script/dom/svgsvgelement.rs index 61b90a5ef39..12b7fa6bc71 100644 --- a/components/script/dom/svgsvgelement.rs +++ b/components/script/dom/svgsvgelement.rs @@ -3,7 +3,7 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ use dom_struct::dom_struct; -use html5ever::{LocalName, Prefix, local_name, namespace_url, ns}; +use html5ever::{LocalName, Prefix, local_name, ns}; use js::rust::HandleObject; use script_layout_interface::SVGSVGData; use style::attr::AttrValue; diff --git a/components/script/dom/trustedscripturl.rs b/components/script/dom/trustedscripturl.rs index 01a82a4fff7..ba1e0335abc 100644 --- a/components/script/dom/trustedscripturl.rs +++ b/components/script/dom/trustedscripturl.rs @@ -2,6 +2,8 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ +use std::fmt; + use dom_struct::dom_struct; use crate::dom::bindings::codegen::Bindings::TrustedScriptURLBinding::TrustedScriptURLMethods; @@ -32,6 +34,13 @@ impl TrustedScriptURL { } } +impl fmt::Display for TrustedScriptURL { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_str(&self.data) + } +} + impl TrustedScriptURLMethods<crate::DomTypeHolder> for TrustedScriptURL { /// <https://www.w3.org/TR/trusted-types/#trustedscripturl-stringification-behavior> fn Stringifier(&self) -> DOMString { diff --git a/components/script/dom/trustedtypepolicy.rs b/components/script/dom/trustedtypepolicy.rs index 387e53faeb3..2ec5015eb88 100644 --- a/components/script/dom/trustedtypepolicy.rs +++ b/components/script/dom/trustedtypepolicy.rs @@ -59,6 +59,13 @@ impl TrustedTypePolicy { reflect_dom_object(Box::new(Self::new_inherited(name, options)), global, can_gc) } + // TODO(36258): Remove when we refactor get_trusted_type_policy_value to take an enum + // value to handle which callback to call. The callback should not be exposed outside + // of the policy object, but is currently used in TrustedPolicyFactory::process_value_with_default_policy + pub(crate) fn create_script_url(&self) -> Option<Rc<CreateScriptURLCallback>> { + self.create_script_url.clone() + } + /// This does not take all arguments as specified. That's because the return type of the /// trusted type function and object are not the same. 2 of the 3 string callbacks return /// a DOMString, while the other one returns an USVString. Additionally, all three callbacks diff --git a/components/script/dom/trustedtypepolicyfactory.rs b/components/script/dom/trustedtypepolicyfactory.rs index 64ae1f8ab11..0dcc78b7cd0 100644 --- a/components/script/dom/trustedtypepolicyfactory.rs +++ b/components/script/dom/trustedtypepolicyfactory.rs @@ -5,9 +5,12 @@ use std::cell::RefCell; use content_security_policy::CheckResult; use dom_struct::dom_struct; -use html5ever::{LocalName, Namespace, QualName, local_name, namespace_url, ns}; +use html5ever::{LocalName, Namespace, QualName, local_name, ns}; +use js::jsapi::JSObject; +use js::jsval::NullValue; use js::rust::HandleValue; +use crate::dom::bindings::callback::ExceptionHandling; use crate::dom::bindings::codegen::Bindings::TrustedTypePolicyFactoryBinding::{ TrustedTypePolicyFactoryMethods, TrustedTypePolicyOptions, }; @@ -21,6 +24,7 @@ use crate::dom::trustedhtml::TrustedHTML; use crate::dom::trustedscript::TrustedScript; use crate::dom::trustedscripturl::TrustedScriptURL; use crate::dom::trustedtypepolicy::TrustedTypePolicy; +use crate::js::conversions::ToJSValConvertible; use crate::script_runtime::{CanGc, JSContext}; #[dom_struct] @@ -137,6 +141,122 @@ impl TrustedTypePolicyFactory { // Step 4: Return data. data } + /// <https://w3c.github.io/trusted-types/dist/spec/#process-value-with-a-default-policy-algorithm> + #[allow(unsafe_code)] + pub(crate) fn process_value_with_default_policy( + global: &GlobalScope, + input: String, + sink: &str, + can_gc: CanGc, + ) -> Fallible<Option<DomRoot<TrustedScriptURL>>> { + // Step 1: Let defaultPolicy be the value of global’s trusted type policy factory's default policy. + let global_policy_factory = global.trusted_types(can_gc); + let default_policy = match global_policy_factory.default_policy.get() { + None => return Ok(Some(TrustedScriptURL::new(input, global, can_gc))), + Some(default_policy) => default_policy, + }; + let cx = GlobalScope::get_cx(); + // Step 2: Let policyValue be the result of executing Get Trusted Type policy value, + // with the following arguments: + let policy_value = default_policy.get_trusted_type_policy_value( + || { + // TODO(36258): support other trusted types as well by changing get_trusted_type_policy_value to accept + // the trusted type as enum and call the appropriate callback based on that. + default_policy.create_script_url().map(|callback| { + rooted!(in(*cx) let this_object: *mut JSObject); + rooted!(in(*cx) let mut trusted_type_name_value = NullValue()); + unsafe { + "TrustedScriptURL".to_jsval(*cx, trusted_type_name_value.handle_mut()); + } + + rooted!(in(*cx) let mut sink_value = NullValue()); + unsafe { + sink.to_jsval(*cx, sink_value.handle_mut()); + } + + let args = vec![trusted_type_name_value.handle(), sink_value.handle()]; + // Step 4: Let policyValue be the result of invoking function with value as a first argument, + // items of arguments as subsequent arguments, and callback **this** value set to null, + // rethrowing any exceptions. + callback.Call_( + &this_object.handle(), + DOMString::from(input.to_owned()), + args, + ExceptionHandling::Rethrow, + can_gc, + ) + }) + }, + false, + ); + let data_string = match policy_value { + // Step 3: If the algorithm threw an error, rethrow the error and abort the following steps. + Err(error) => return Err(error), + Ok(policy_value) => match policy_value { + // Step 4: If policyValue is null or undefined, return policyValue. + None => return Ok(None), + // Step 5: Let dataString be the result of stringifying policyValue. + Some(policy_value) => policy_value.as_ref().into(), + }, + }; + Ok(Some(TrustedScriptURL::new(data_string, global, can_gc))) + } + /// Step 1 is implemented by the caller + /// <https://w3c.github.io/trusted-types/dist/spec/#get-trusted-type-compliant-string-algorithm> + pub(crate) fn get_trusted_type_compliant_string( + global: &GlobalScope, + input: String, + sink: &str, + sink_group: &str, + can_gc: CanGc, + ) -> Fallible<String> { + let csp_list = match global.get_csp_list() { + None => return Ok(input), + Some(csp_list) => csp_list, + }; + // Step 2: Let requireTrustedTypes be the result of executing Does sink type require trusted types? + // algorithm, passing global, sinkGroup, and true. + let require_trusted_types = csp_list.does_sink_type_require_trusted_types(sink_group, true); + // Step 3: If requireTrustedTypes is false, return stringified input and abort these steps. + if !require_trusted_types { + return Ok(input); + } + // Step 4: Let convertedInput be the result of executing Process value with a default policy + // with the same arguments as this algorithm. + let converted_input = TrustedTypePolicyFactory::process_value_with_default_policy( + global, + input.clone(), + sink, + can_gc, + ); + // Step 5: If the algorithm threw an error, rethrow the error and abort the following steps. + match converted_input? { + // Step 6: If convertedInput is null or undefined, execute the following steps: + None => { + // Step 6.1: Let disposition be the result of executing Should sink type mismatch violation + // be blocked by Content Security Policy? algorithm, passing global, + // stringified input as source, sinkGroup and sink. + let (disposition, violations) = csp_list + .should_sink_type_mismatch_violation_be_blocked_by_csp( + sink, sink_group, &input, + ); + global.report_csp_violations(violations); + // Step 6.2: If disposition is “Allowed”, return stringified input and abort further steps. + if disposition == CheckResult::Allowed { + Ok(input) + } else { + // Step 6.3: Throw a TypeError and abort further steps. + Err(Error::Type( + "Cannot set value, expected trusted type".to_owned(), + )) + } + }, + // Step 8: Return stringified convertedInput. + Some(converted_input) => Ok((*converted_input).to_string()), + } + // Step 7: Assert: convertedInput is an instance of expectedType. + // TODO(https://github.com/w3c/trusted-types/issues/566): Implement when spec is resolved + } } impl TrustedTypePolicyFactoryMethods<crate::DomTypeHolder> for TrustedTypePolicyFactory { diff --git a/components/script/dom/workerglobalscope.rs b/components/script/dom/workerglobalscope.rs index 5775375c385..695119715e4 100644 --- a/components/script/dom/workerglobalscope.rs +++ b/components/script/dom/workerglobalscope.rs @@ -12,6 +12,7 @@ use std::time::Duration; use base::cross_process_instant::CrossProcessInstant; use base::id::{PipelineId, PipelineNamespace}; use constellation_traits::WorkerGlobalScopeInit; +use content_security_policy::CspList; use crossbeam_channel::Receiver; use devtools_traits::{DevtoolScriptControlMsg, WorkerId}; use dom_struct::dom_struct; @@ -246,6 +247,10 @@ impl WorkerGlobalScope { self.policy_container.borrow() } + pub(crate) fn set_csp_list(&self, csp_list: Option<CspList>) { + self.policy_container.borrow_mut().set_csp_list(csp_list); + } + /// Get a mutable reference to the [`TimerScheduler`] for this [`ServiceWorkerGlobalScope`]. pub(crate) fn timer_scheduler(&self) -> RefMut<TimerScheduler> { self.timer_scheduler.borrow_mut() @@ -300,6 +305,7 @@ impl WorkerGlobalScopeMethods<crate::DomTypeHolder> for WorkerGlobalScope { .use_url_credentials(true) .origin(global_scope.origin().immutable().clone()) .insecure_requests_policy(self.insecure_requests_policy()) + .policy_container(global_scope.policy_container()) .has_trustworthy_ancestor_origin( global_scope.has_trustworthy_ancestor_or_current_origin(), ) diff --git a/components/script/dom/workletglobalscope.rs b/components/script/dom/workletglobalscope.rs index 0196d6a83ea..6e81220731c 100644 --- a/components/script/dom/workletglobalscope.rs +++ b/components/script/dom/workletglobalscope.rs @@ -15,6 +15,7 @@ use js::rust::Runtime; use net_traits::ResourceThreads; use net_traits::image_cache::ImageCache; use profile_traits::{mem, time}; +use script_bindings::realms::InRealm; use script_traits::Painter; use servo_url::{ImmutableOrigin, MutableOrigin, ServoUrl}; use stylo_atoms::Atom; @@ -22,6 +23,7 @@ use stylo_atoms::Atom; use crate::dom::bindings::inheritance::Castable; use crate::dom::bindings::root::DomRoot; use crate::dom::bindings::trace::CustomTraceable; +use crate::dom::bindings::utils::define_all_exposed_interfaces; use crate::dom::globalscope::GlobalScope; use crate::dom::paintworkletglobalscope::{PaintWorkletGlobalScope, PaintWorkletTask}; use crate::dom::testworkletglobalscope::{TestWorkletGlobalScope, TestWorkletTask}; @@ -29,6 +31,7 @@ use crate::dom::testworkletglobalscope::{TestWorkletGlobalScope, TestWorkletTask use crate::dom::webgpu::identityhub::IdentityHub; use crate::dom::worklet::WorkletExecutor; use crate::messaging::MainThreadScriptMsg; +use crate::realms::enter_realm; use crate::script_module::ScriptFetchOptions; use crate::script_runtime::{CanGc, JSContext}; @@ -56,7 +59,7 @@ impl WorkletGlobalScope { executor: WorkletExecutor, init: &WorkletGlobalScopeInit, ) -> DomRoot<WorkletGlobalScope> { - match scope_type { + let scope: DomRoot<WorkletGlobalScope> = match scope_type { WorkletGlobalScopeType::Test => DomRoot::upcast(TestWorkletGlobalScope::new( runtime, pipeline_id, @@ -71,7 +74,12 @@ impl WorkletGlobalScope { executor, init, )), - } + }; + + let realm = enter_realm(&*scope); + define_all_exposed_interfaces(scope.upcast(), InRealm::entered(&realm), CanGc::note()); + + scope } /// Create a new stack-allocated `WorkletGlobalScope`. diff --git a/components/script/layout_dom/element.rs b/components/script/layout_dom/element.rs index ce7ca96f464..9b50c9f3a2b 100644 --- a/components/script/layout_dom/element.rs +++ b/components/script/layout_dom/element.rs @@ -8,7 +8,7 @@ use std::{fmt, slice}; use atomic_refcell::{AtomicRef, AtomicRefMut}; use embedder_traits::UntrustedNodeAddress; -use html5ever::{LocalName, Namespace, local_name, namespace_url, ns}; +use html5ever::{LocalName, Namespace, local_name, ns}; use js::jsapi::JSObject; use script_layout_interface::wrapper_traits::{ LayoutNode, ThreadSafeLayoutElement, ThreadSafeLayoutNode, diff --git a/components/script/layout_dom/node.rs b/components/script/layout_dom/node.rs index 213070181ef..9d36f73bbc0 100644 --- a/components/script/layout_dom/node.rs +++ b/components/script/layout_dom/node.rs @@ -10,7 +10,7 @@ use std::sync::Arc as StdArc; use base::id::{BrowsingContextId, PipelineId}; use fonts_traits::ByteIndex; -use html5ever::{local_name, namespace_url, ns}; +use html5ever::{local_name, ns}; use pixels::{Image, ImageMetadata}; use range::Range; use script_layout_interface::wrapper_traits::{LayoutDataTrait, LayoutNode, ThreadSafeLayoutNode}; diff --git a/components/script/links.rs b/components/script/links.rs index f7094adbfde..f38ba6f7767 100644 --- a/components/script/links.rs +++ b/components/script/links.rs @@ -5,7 +5,7 @@ //! Defines shared hyperlink behaviour for `<link>`, `<a>`, `<area>` and `<form>` elements. use constellation_traits::{LoadData, LoadOrigin, NavigationHistoryBehavior}; -use html5ever::{local_name, namespace_url, ns}; +use html5ever::{local_name, ns}; use malloc_size_of::malloc_size_of_is_0; use net_traits::request::Referrer; use style::str::HTML_SPACE_CHARACTERS; diff --git a/components/script/script_module.rs b/components/script/script_module.rs index 689f4a3b0a7..c7697adeea6 100644 --- a/components/script/script_module.rs +++ b/components/script/script_module.rs @@ -1760,7 +1760,10 @@ fn fetch_single_module_script( .integrity_metadata(options.integrity_metadata.clone()) .credentials_mode(options.credentials_mode) .referrer_policy(options.referrer_policy) - .mode(mode); + .mode(mode) + .insecure_requests_policy(global.insecure_requests_policy()) + .has_trustworthy_ancestor_origin(global.has_trustworthy_ancestor_origin()) + .policy_container(global.policy_container().to_owned()); let context = Arc::new(Mutex::new(ModuleContext { owner, diff --git a/components/script/script_thread.rs b/components/script/script_thread.rs index bec4d59be0f..da37a12c8a7 100644 --- a/components/script/script_thread.rs +++ b/components/script/script_thread.rs @@ -57,7 +57,7 @@ use euclid::Point2D; use euclid::default::Rect; use fonts::{FontContext, SystemFontServiceProxy}; use headers::{HeaderMapExt, LastModified, ReferrerPolicy as ReferrerPolicyHeader}; -use html5ever::{local_name, namespace_url, ns}; +use html5ever::{local_name, ns}; use http::header::REFRESH; use hyper_serde::Serde; use ipc_channel::ipc; @@ -336,11 +336,6 @@ pub struct ScriptThread { #[no_trace] layout_factory: Arc<dyn LayoutFactory>, - // Mouse down button: TO BE REMOVED. Not needed as click event should only - // triggered for primary button - #[no_trace] - mouse_down_button: Cell<Option<MouseButton>>, - // Mouse down point. // In future, this shall be mouse_down_point for primary button #[no_trace] @@ -959,7 +954,6 @@ impl ScriptThread { gpu_id_hub: Arc::new(IdentityHub::default()), inherited_secure_context: state.inherited_secure_context, layout_factory, - mouse_down_button: Cell::new(None), relative_mouse_down_point: Cell::new(Point2D::zero()), } } @@ -3354,14 +3348,6 @@ impl ScriptThread { return; }; - if let InputEvent::MouseButton(mouse_button_event) = event.event { - if mouse_button_event.action == MouseButtonAction::Down { - self.mouse_down_button.set(Some(mouse_button_event.button)); - self.relative_mouse_down_point.set(mouse_button_event.point) - } - } - document.note_pending_input_event(event.clone()); - // Also send a 'click' event with same hit-test result if this is release // MAYBE? TODO: https://developer.mozilla.org/en-US/docs/Web/API/Element/click_event @@ -3370,30 +3356,42 @@ impl ScriptThread { // that contained both elements. // But spec doesn't specify this https://w3c.github.io/uievents/#event-type-click + // "The click event type MUST be dispatched on the topmost event target indicated by + // the pointer, when the user presses down and releases the primary pointer button" // Servo-specific: Trigger if within 10px of the down point if let InputEvent::MouseButton(mouse_button_event) = event.event { - if let (Some(mouse_down_button), MouseButtonAction::Up) = - (self.mouse_down_button.get(), mouse_button_event.action) - { - let pixel_dist = self.relative_mouse_down_point.get() - mouse_button_event.point; - let pixel_dist = (pixel_dist.x * pixel_dist.x + pixel_dist.y * pixel_dist.y).sqrt(); - if mouse_down_button == mouse_button_event.button && - pixel_dist < 10.0 * document.window().device_pixel_ratio().get() - { - document.note_pending_input_event(ConstellationInputEvent { - hit_test_result: event.hit_test_result, - pressed_mouse_buttons: event.pressed_mouse_buttons, - active_keyboard_modifiers: event.active_keyboard_modifiers, - event: InputEvent::MouseButton(MouseButtonEvent { - action: MouseButtonAction::Click, - button: mouse_button_event.button, - point: mouse_button_event.point, - }), - }); + if let MouseButton::Left = mouse_button_event.button { + match mouse_button_event.action { + MouseButtonAction::Up => { + let pixel_dist = + self.relative_mouse_down_point.get() - mouse_button_event.point; + let pixel_dist = + (pixel_dist.x * pixel_dist.x + pixel_dist.y * pixel_dist.y).sqrt(); + if pixel_dist < 10.0 * document.window().device_pixel_ratio().get() { + document.note_pending_input_event(event.clone()); + document.note_pending_input_event(ConstellationInputEvent { + hit_test_result: event.hit_test_result, + pressed_mouse_buttons: event.pressed_mouse_buttons, + active_keyboard_modifiers: event.active_keyboard_modifiers, + event: InputEvent::MouseButton(MouseButtonEvent { + action: MouseButtonAction::Click, + button: mouse_button_event.button, + point: mouse_button_event.point, + }), + }); + return; + } + }, + MouseButtonAction::Down => { + self.relative_mouse_down_point.set(mouse_button_event.point) + }, + MouseButtonAction::Click => {}, } } } + + document.note_pending_input_event(event); } /// Handle a "navigate an iframe" message from the constellation. diff --git a/components/script/security_manager.rs b/components/script/security_manager.rs index 60cf2267a2c..ee320206de2 100644 --- a/components/script/security_manager.rs +++ b/components/script/security_manager.rs @@ -62,6 +62,8 @@ pub(crate) struct CSPViolationReportBuilder { pub source_file: String, /// <https://www.w3.org/TR/CSP3/#violation-effective-directive> pub effective_directive: String, + /// <https://www.w3.org/TR/CSP3/#violation-policy> + pub original_policy: String, } impl CSPViolationReportBuilder { @@ -106,6 +108,12 @@ impl CSPViolationReportBuilder { self } + /// <https://www.w3.org/TR/CSP3/#violation-policy> + pub fn original_policy(mut self, original_policy: String) -> CSPViolationReportBuilder { + self.original_policy = original_policy; + self + } + /// <https://w3c.github.io/webappsec-csp/#strip-url-for-use-in-reports> fn strip_url_for_reports(&self, mut url: ServoUrl) -> String { let scheme = url.scheme(); @@ -141,7 +149,7 @@ impl CSPViolationReportBuilder { sample: self.sample, blocked_url: self.resource, source_file: self.source_file, - original_policy: "".to_owned(), + original_policy: self.original_policy, line_number: self.line_number, column_number: self.column_number, status_code: global.status_code().unwrap_or(0), diff --git a/components/script/xpath/eval.rs b/components/script/xpath/eval.rs index 9c95c3d8699..75d7ac5849c 100644 --- a/components/script/xpath/eval.rs +++ b/components/script/xpath/eval.rs @@ -4,7 +4,7 @@ use std::fmt; -use html5ever::{QualName, local_name, namespace_prefix, namespace_url, ns}; +use html5ever::{QualName, local_name, namespace_prefix, ns}; use super::parser::{ AdditiveOp, Axis, EqualityOp, Expr, FilterExpr, KindTest, Literal, MultiplicativeOp, NodeTest, diff --git a/components/script_bindings/codegen/Bindings.conf b/components/script_bindings/codegen/Bindings.conf index f9ab745e4ea..4946177e0b3 100644 --- a/components/script_bindings/codegen/Bindings.conf +++ b/components/script_bindings/codegen/Bindings.conf @@ -416,7 +416,7 @@ DOMInterfaces = { }, 'HTMLScriptElement': { - 'canGc': ['SetAsync', 'SetCrossOrigin', 'SetText'] + 'canGc': ['SetAsync', 'SetCrossOrigin', 'SetSrc', 'SetText'] }, 'HTMLSelectElement': { diff --git a/components/script_bindings/codegen/CodegenRust.py b/components/script_bindings/codegen/CodegenRust.py index 107e047ea3b..48f024be70f 100644 --- a/components/script_bindings/codegen/CodegenRust.py +++ b/components/script_bindings/codegen/CodegenRust.py @@ -2405,15 +2405,22 @@ class CGDOMJSClass(CGThing): "flags": "JSCLASS_FOREGROUND_FINALIZE", "name": str_to_cstr_ptr(self.descriptor.interface.identifier.name), "resolveHook": "None", + "mayResolveHook": "None", "slots": "1", "traceHook": f"{TRACE_HOOK_NAME}::<D>", } if self.descriptor.isGlobal(): assert not self.descriptor.weakReferenceable - args["enumerateHook"] = "Some(enumerate_global::<D>)" args["flags"] = "JSCLASS_IS_GLOBAL | JSCLASS_DOM_GLOBAL | JSCLASS_FOREGROUND_FINALIZE" args["slots"] = "JSCLASS_GLOBAL_SLOT_COUNT + 1" - args["resolveHook"] = "Some(resolve_global::<D>)" + if self.descriptor.interface.getExtendedAttribute("NeedResolve"): + args["enumerateHook"] = "Some(enumerate_window::<D>)" + args["resolveHook"] = "Some(resolve_window::<D>)" + args["mayResolveHook"] = "Some(may_resolve_window::<D>)" + else: + args["enumerateHook"] = "Some(enumerate_global)" + args["resolveHook"] = "Some(resolve_global)" + args["mayResolveHook"] = "Some(may_resolve_global)" args["traceHook"] = "js::jsapi::JS_GlobalObjectTraceHook" elif self.descriptor.weakReferenceable: args["slots"] = "2" @@ -2427,7 +2434,7 @@ pub(crate) fn init_class_ops<D: DomTypes>() {{ enumerate: None, newEnumerate: {args['enumerateHook']}, resolve: {args['resolveHook']}, - mayResolve: None, + mayResolve: {args['mayResolveHook']}, finalize: Some({args['finalizeHook']}), call: None, construct: None, diff --git a/components/script_bindings/import.rs b/components/script_bindings/import.rs index 65e9ee30e1d..16cc92f07bf 100644 --- a/components/script_bindings/import.rs +++ b/components/script_bindings/import.rs @@ -127,10 +127,11 @@ pub(crate) mod module { pub(crate) use crate::script_runtime::CanGc; pub(crate) use crate::utils::{ AsVoidPtr, DOM_PROTO_UNFORGEABLE_HOLDER_SLOT, DOMClass, DOMJSClass, JSCLASS_DOM_GLOBAL, - ProtoOrIfaceArray, enumerate_global, exception_to_promise, generic_getter, - generic_lenient_getter, generic_lenient_setter, generic_method, generic_setter, - generic_static_promise_method, get_array_index_from_id, get_property_on_prototype, - has_property_on_prototype, resolve_global, trace_global, + ProtoOrIfaceArray, enumerate_global, enumerate_window, exception_to_promise, + generic_getter, generic_lenient_getter, generic_lenient_setter, generic_method, + generic_setter, generic_static_promise_method, get_array_index_from_id, + get_property_on_prototype, has_property_on_prototype, may_resolve_global, + may_resolve_window, resolve_global, resolve_window, trace_global, }; pub(crate) use crate::weakref::DOM_WEAK_SLOT; pub(crate) use crate::{JSTraceable, proxyhandler}; diff --git a/components/script_bindings/root.rs b/components/script_bindings/root.rs index 51bc979908f..3d0378f0df1 100644 --- a/components/script_bindings/root.rs +++ b/components/script_bindings/root.rs @@ -412,7 +412,7 @@ impl RootCollection { .rposition(|r| std::ptr::addr_eq(*r as *const (), object as *const ())) { Some(idx) => { - roots.remove(idx); + roots.swap_remove(idx); }, None => panic!("Can't remove a root that was never rooted!"), } diff --git a/components/script_bindings/utils.rs b/components/script_bindings/utils.rs index 03a5783958e..fa4982b9904 100644 --- a/components/script_bindings/utils.rs +++ b/components/script_bindings/utils.rs @@ -18,9 +18,10 @@ use js::jsapi::{ GetLinearStringLength, GetNonCCWObjectGlobal, HandleId as RawHandleId, HandleObject as RawHandleObject, Heap, JS_AtomizeStringN, JS_ClearPendingException, JS_DeprecatedStringHasLatin1Chars, JS_GetLatin1StringCharsAndLength, JS_IsExceptionPending, - JS_IsGlobalObject, JS_NewEnumerateStandardClasses, JS_ResolveStandardClass, JSAtom, JSContext, - JSJitInfo, JSObject, JSTracer, MutableHandleIdVector as RawMutableHandleIdVector, - MutableHandleValue as RawMutableHandleValue, ObjectOpResult, StringIsArrayIndex, + JS_IsGlobalObject, JS_MayResolveStandardClass, JS_NewEnumerateStandardClasses, + JS_ResolveStandardClass, JSAtom, JSAtomState, JSContext, JSJitInfo, JSObject, JSTracer, + MutableHandleIdVector as RawMutableHandleIdVector, MutableHandleValue as RawMutableHandleValue, + ObjectOpResult, PropertyKey, StringIsArrayIndex, jsid, }; use js::jsid::StringId; use js::jsval::{JSVal, UndefinedValue}; @@ -30,7 +31,7 @@ use js::rust::wrappers::{ JS_SetPendingException, JS_SetProperty, }; use js::rust::{ - HandleId, HandleObject, HandleValue, MutableHandleValue, ToString, get_object_class, + HandleId, HandleObject, HandleValue, MutableHandleValue, Runtime, ToString, get_object_class, }; use js::{JS_CALLEE, rooted}; use malloc_size_of::MallocSizeOfOps; @@ -577,15 +578,25 @@ impl AsCCharPtrPtr for [u8] { /// Enumerate lazy properties of a global object. /// Modeled after <https://github.com/mozilla/gecko-dev/blob/3fd619f47/dom/bindings/BindingUtils.cpp#L2814> -/// and <https://github.com/mozilla/gecko-dev/blob/3fd619f47/dom/base/nsGlobalWindowInner.cpp#3297> -pub(crate) unsafe extern "C" fn enumerate_global<D: DomTypes>( +pub(crate) unsafe extern "C" fn enumerate_global( cx: *mut JSContext, obj: RawHandleObject, props: RawMutableHandleIdVector, enumerable_only: bool, ) -> bool { assert!(JS_IsGlobalObject(obj.get())); - if !JS_NewEnumerateStandardClasses(cx, obj, props, enumerable_only) { + JS_NewEnumerateStandardClasses(cx, obj, props, enumerable_only) +} + +/// Enumerate lazy properties of a global object that is a Window. +/// <https://github.com/mozilla/gecko-dev/blob/3fd619f47/dom/base/nsGlobalWindowInner.cpp#3297> +pub(crate) unsafe extern "C" fn enumerate_window<D: DomTypes>( + cx: *mut JSContext, + obj: RawHandleObject, + props: RawMutableHandleIdVector, + enumerable_only: bool, +) -> bool { + if !enumerate_global(cx, obj, props, enumerable_only) { return false; } @@ -610,34 +621,68 @@ pub(crate) unsafe extern "C" fn enumerate_global<D: DomTypes>( true } +/// Returns true if the resolve hook for this global may resolve the provided id. +/// <https://searchfox.org/mozilla-central/rev/f3c8c63a097b61bb1f01e13629b9514e09395947/dom/bindings/BindingUtils.cpp#2809> +/// <https://searchfox.org/mozilla-central/rev/f3c8c63a097b61bb1f01e13629b9514e09395947/js/public/Class.h#283-291> +pub(crate) unsafe extern "C" fn may_resolve_global( + names: *const JSAtomState, + id: PropertyKey, + maybe_obj: *mut JSObject, +) -> bool { + JS_MayResolveStandardClass(names, id, maybe_obj) +} + +/// Returns true if the resolve hook for this window may resolve the provided id. +/// <https://searchfox.org/mozilla-central/rev/f3c8c63a097b61bb1f01e13629b9514e09395947/dom/base/nsGlobalWindowInner.cpp#3275> +/// <https://searchfox.org/mozilla-central/rev/f3c8c63a097b61bb1f01e13629b9514e09395947/js/public/Class.h#283-291> +pub(crate) unsafe extern "C" fn may_resolve_window<D: DomTypes>( + names: *const JSAtomState, + id: PropertyKey, + maybe_obj: *mut JSObject, +) -> bool { + if may_resolve_global(names, id, maybe_obj) { + return true; + } + + let cx = Runtime::get() + .expect("There must be a JSContext active") + .as_ptr(); + let Ok(bytes) = latin1_bytes_from_id(cx, id) else { + return false; + }; + + <D as DomHelpers<D>>::interface_map().contains_key(bytes) +} + /// Resolve a lazy global property, for interface objects and named constructors. -pub(crate) unsafe extern "C" fn resolve_global<D: DomTypes>( +pub(crate) unsafe extern "C" fn resolve_global( cx: *mut JSContext, obj: RawHandleObject, id: RawHandleId, rval: *mut bool, ) -> bool { assert!(JS_IsGlobalObject(obj.get())); - if !JS_ResolveStandardClass(cx, obj, id, rval) { + JS_ResolveStandardClass(cx, obj, id, rval) +} + +/// Resolve a lazy global property for a Window global. +pub(crate) unsafe extern "C" fn resolve_window<D: DomTypes>( + cx: *mut JSContext, + obj: RawHandleObject, + id: RawHandleId, + rval: *mut bool, +) -> bool { + if !resolve_global(cx, obj, id, rval) { return false; } + if *rval { return true; } - if !id.is_string() { - *rval = false; - return true; - } - - let string = id.to_string(); - if !JS_DeprecatedStringHasLatin1Chars(string) { + let Ok(bytes) = latin1_bytes_from_id(cx, *id) else { *rval = false; return true; - } - let mut length = 0; - let ptr = JS_GetLatin1StringCharsAndLength(cx, ptr::null(), string, &mut length); - assert!(!ptr.is_null()); - let bytes = slice::from_raw_parts(ptr, length); + }; if let Some(interface) = <D as DomHelpers<D>>::interface_map().get(bytes) { (interface.define)(SafeJSContext::from_ptr(cx), Handle::from_raw(obj)); @@ -647,3 +692,22 @@ pub(crate) unsafe extern "C" fn resolve_global<D: DomTypes>( } true } + +/// Returns a slice of bytes corresponding to the bytes in the provided string id. +/// Returns an error if the id is not a string, or the string contains non-latin1 characters. +/// # Safety +/// The slice is only valid as long as the original id is not garbage collected. +unsafe fn latin1_bytes_from_id(cx: *mut JSContext, id: jsid) -> Result<&'static [u8], ()> { + if !id.is_string() { + return Err(()); + } + + let string = id.to_string(); + if !JS_DeprecatedStringHasLatin1Chars(string) { + return Err(()); + } + let mut length = 0; + let ptr = JS_GetLatin1StringCharsAndLength(cx, ptr::null(), string, &mut length); + assert!(!ptr.is_null()); + Ok(slice::from_raw_parts(ptr, length)) +} diff --git a/components/script_bindings/webidls/HTMLScriptElement.webidl b/components/script_bindings/webidls/HTMLScriptElement.webidl index b79382dbbb8..6f02bb3cf47 100644 --- a/components/script_bindings/webidls/HTMLScriptElement.webidl +++ b/components/script_bindings/webidls/HTMLScriptElement.webidl @@ -7,8 +7,8 @@ interface HTMLScriptElement : HTMLElement { [HTMLConstructor] constructor(); - [CEReactions] - attribute USVString src; + [CEReactions, SetterThrows] + attribute (TrustedScriptURL or USVString) src; [CEReactions] attribute DOMString type; [CEReactions] diff --git a/components/script_bindings/webidls/Window.webidl b/components/script_bindings/webidls/Window.webidl index c7b6dde617d..d42ba22ea66 100644 --- a/components/script_bindings/webidls/Window.webidl +++ b/components/script_bindings/webidls/Window.webidl @@ -3,7 +3,7 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ // https://html.spec.whatwg.org/multipage/#window -[Global=Window, Exposed=Window /*, LegacyUnenumerableNamedProperties */] +[Global=Window, Exposed=Window, LegacyUnenumerableNamedProperties, NeedResolve] /*sealed*/ interface Window : GlobalScope { // the current browsing context [LegacyUnforgeable, CrossOriginReadable] readonly attribute WindowProxy window; diff --git a/components/servo/webview.rs b/components/servo/webview.rs index f4bf13e3a7e..95eb6dfd154 100644 --- a/components/servo/webview.rs +++ b/components/servo/webview.rs @@ -9,7 +9,7 @@ use std::time::Duration; use base::id::WebViewId; use compositing::IOCompositor; -use compositing_traits::RendererWebView; +use compositing_traits::WebViewTrait; use constellation_traits::{EmbedderToConstellationMessage, TraversalDirection}; use dpi::PhysicalSize; use embedder_traits::{ @@ -558,7 +558,7 @@ struct ServoRendererWebView { weak_handle: Weak<RefCell<WebViewInner>>, } -impl RendererWebView for ServoRendererWebView { +impl WebViewTrait for ServoRendererWebView { fn id(&self) -> WebViewId { self.id } diff --git a/components/shared/compositing/lib.rs b/components/shared/compositing/lib.rs index 7c2bd669d02..31371f87529 100644 --- a/components/shared/compositing/lib.rs +++ b/components/shared/compositing/lib.rs @@ -541,7 +541,7 @@ impl From<SerializableImageData> for ImageData { /// A trait that exposes the embedding layer's `WebView` to the Servo renderer. /// This is to prevent a dependency cycle between the renderer and the embedding /// layer. -pub trait RendererWebView { +pub trait WebViewTrait { fn id(&self) -> WebViewId; fn screen_geometry(&self) -> Option<ScreenGeometry>; fn set_animating(&self, new_value: bool); diff --git a/components/shared/constellation/from_script_message.rs b/components/shared/constellation/from_script_message.rs index bccb3059e24..8346551fd15 100644 --- a/components/shared/constellation/from_script_message.rs +++ b/components/shared/constellation/from_script_message.rs @@ -33,10 +33,8 @@ use strum_macros::IntoStaticStr; use webgpu_traits::{WebGPU, WebGPUAdapterResponse}; use webrender_api::ImageKey; -use crate::message_port::{ - BroadcastMsg, MessagePortMsg, PortMessageTask, StructuredSerializedData, -}; -use crate::{LogEntry, TraversalDirection, WindowSizeType}; +use crate::structured_data::{BroadcastMsg, StructuredSerializedData}; +use crate::{LogEntry, MessagePortMsg, PortMessageTask, TraversalDirection, WindowSizeType}; /// A Script to Constellation channel. #[derive(Clone, Debug, Deserialize, Serialize)] diff --git a/components/shared/constellation/lib.rs b/components/shared/constellation/lib.rs index 627741a40fb..548e17b532c 100644 --- a/components/shared/constellation/lib.rs +++ b/components/shared/constellation/lib.rs @@ -9,15 +9,15 @@ //! on other parts of Servo. mod from_script_message; -mod message_port; +mod structured_data; -use std::collections::HashMap; +use std::collections::{HashMap, VecDeque}; use std::fmt; use std::time::Duration; use base::Epoch; use base::cross_process_instant::CrossProcessInstant; -use base::id::{PipelineId, WebViewId}; +use base::id::{MessagePortId, PipelineId, WebViewId}; use bitflags::bitflags; use embedder_traits::{ CompositorHitTestResult, Cursor, InputEvent, MediaSessionActionType, Theme, ViewportDetails, @@ -27,9 +27,9 @@ use euclid::Vector2D; pub use from_script_message::*; use ipc_channel::ipc::IpcSender; use malloc_size_of_derive::MallocSizeOf; -pub use message_port::*; use serde::{Deserialize, Serialize}; -use servo_url::ServoUrl; +use servo_url::{ImmutableOrigin, ServoUrl}; +pub use structured_data::*; use strum_macros::IntoStaticStr; use webrender_api::ExternalScrollId; use webrender_api::units::LayoutPixel; @@ -158,3 +158,28 @@ pub enum TraversalDirection { /// Travel backward the given number of documents. Back(usize), } + +/// A task on the <https://html.spec.whatwg.org/multipage/#port-message-queue> +#[derive(Debug, Deserialize, MallocSizeOf, Serialize)] +pub struct PortMessageTask { + /// The origin of this task. + pub origin: ImmutableOrigin, + /// A data-holder for serialized data and transferred objects. + pub data: StructuredSerializedData, +} + +/// Messages for communication between the constellation and a global managing ports. +#[derive(Debug, Deserialize, Serialize)] +#[allow(clippy::large_enum_variant)] +pub enum MessagePortMsg { + /// Complete the transfer for a batch of ports. + CompleteTransfer(HashMap<MessagePortId, VecDeque<PortMessageTask>>), + /// Complete the transfer of a single port, + /// whose transfer was pending because it had been requested + /// while a previous failed transfer was being rolled-back. + CompletePendingTransfer(MessagePortId, VecDeque<PortMessageTask>), + /// Remove a port, the entangled one doesn't exists anymore. + RemoveMessagePort(MessagePortId), + /// Handle a new port-message-task. + NewTask(MessagePortId, PortMessageTask), +} diff --git a/components/shared/constellation/message_port.rs b/components/shared/constellation/message_port.rs deleted file mode 100644 index bb2929353af..00000000000 --- a/components/shared/constellation/message_port.rs +++ /dev/null @@ -1,566 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ - -//! This module contains data structures used for message ports and serializing -//! DOM objects to send across them as per -//! <https://html.spec.whatwg.org/multipage/#serializable-objects>. -//! The implementations are here instead of in `script``, because these -//! types can be sent through the Constellation to other ScriptThreads, -//! and Constellation cannot depend directly on `script`. - -use std::cell::RefCell; -use std::collections::{HashMap, VecDeque}; -use std::path::PathBuf; - -use base::id::{BlobId, DomExceptionId, DomPointId, MessagePortId}; -use log::warn; -use malloc_size_of_derive::MallocSizeOf; -use net_traits::filemanager_thread::RelativePos; -use serde::{Deserialize, Serialize}; -use servo_url::ImmutableOrigin; -use strum::{EnumIter, IntoEnumIterator}; -use uuid::Uuid; - -#[derive(Debug, Deserialize, MallocSizeOf, Serialize)] -enum MessagePortState { - /// <https://html.spec.whatwg.org/multipage/#detached> - Detached, - /// <https://html.spec.whatwg.org/multipage/#port-message-queue> - /// The message-queue of this port is enabled, - /// the boolean represents awaiting completion of a transfer. - Enabled(bool), - /// <https://html.spec.whatwg.org/multipage/#port-message-queue> - /// The message-queue of this port is disabled, - /// the boolean represents awaiting completion of a transfer. - Disabled(bool), -} - -#[derive(Debug, Deserialize, MallocSizeOf, Serialize)] -/// The data and logic backing the DOM managed MessagePort. -pub struct MessagePortImpl { - /// The current state of the port. - state: MessagePortState, - - /// <https://html.spec.whatwg.org/multipage/#entangle> - entangled_port: Option<MessagePortId>, - - /// <https://html.spec.whatwg.org/multipage/#port-message-queue> - message_buffer: Option<VecDeque<PortMessageTask>>, - - /// The UUID of this port. - message_port_id: MessagePortId, -} - -impl MessagePortImpl { - /// Create a new messageport impl. - pub fn new(port_id: MessagePortId) -> MessagePortImpl { - MessagePortImpl { - state: MessagePortState::Disabled(false), - entangled_port: None, - message_buffer: None, - message_port_id: port_id, - } - } - - /// Get the Id. - pub fn message_port_id(&self) -> &MessagePortId { - &self.message_port_id - } - - /// Maybe get the Id of the entangled port. - pub fn entangled_port_id(&self) -> Option<MessagePortId> { - self.entangled_port - } - - /// Entanged this port with another. - pub fn entangle(&mut self, other_id: MessagePortId) { - self.entangled_port = Some(other_id); - } - - /// Is this port enabled? - pub fn enabled(&self) -> bool { - matches!(self.state, MessagePortState::Enabled(_)) - } - - /// Mark this port as having been shipped. - /// <https://html.spec.whatwg.org/multipage/#has-been-shipped> - pub fn set_has_been_shipped(&mut self) { - match self.state { - MessagePortState::Detached => { - panic!("Messageport set_has_been_shipped called in detached state") - }, - MessagePortState::Enabled(_) => self.state = MessagePortState::Enabled(true), - MessagePortState::Disabled(_) => self.state = MessagePortState::Disabled(true), - } - } - - /// Handle the completion of the transfer, - /// this is data received from the constellation. - pub fn complete_transfer(&mut self, mut tasks: VecDeque<PortMessageTask>) { - match self.state { - MessagePortState::Detached => return, - MessagePortState::Enabled(_) => self.state = MessagePortState::Enabled(false), - MessagePortState::Disabled(_) => self.state = MessagePortState::Disabled(false), - } - - // Note: these are the tasks that were buffered while the transfer was ongoing, - // hence they need to execute first. - // The global will call `start` if we are enabled, - // which will add tasks on the event-loop to dispatch incoming messages. - match self.message_buffer { - Some(ref mut incoming_buffer) => { - while let Some(task) = tasks.pop_back() { - incoming_buffer.push_front(task); - } - }, - None => self.message_buffer = Some(tasks), - } - } - - /// A message was received from our entangled port, - /// returns an optional task to be dispatched. - pub fn handle_incoming(&mut self, task: PortMessageTask) -> Option<PortMessageTask> { - let should_dispatch = match self.state { - MessagePortState::Detached => return None, - MessagePortState::Enabled(in_transfer) => !in_transfer, - MessagePortState::Disabled(_) => false, - }; - - if should_dispatch { - Some(task) - } else { - match self.message_buffer { - Some(ref mut buffer) => { - buffer.push_back(task); - }, - None => { - let mut queue = VecDeque::new(); - queue.push_back(task); - self.message_buffer = Some(queue); - }, - } - None - } - } - - /// <https://html.spec.whatwg.org/multipage/#dom-messageport-start> - /// returns an optional queue of tasks that were buffered while the port was disabled. - pub fn start(&mut self) -> Option<VecDeque<PortMessageTask>> { - match self.state { - MessagePortState::Detached => return None, - MessagePortState::Enabled(_) => {}, - MessagePortState::Disabled(in_transfer) => { - self.state = MessagePortState::Enabled(in_transfer); - }, - } - if let MessagePortState::Enabled(true) = self.state { - return None; - } - self.message_buffer.take() - } - - /// <https://html.spec.whatwg.org/multipage/#dom-messageport-close> - pub fn close(&mut self) { - // Step 1 - self.state = MessagePortState::Detached; - } -} - -/// A data-holder for serialized data and transferred objects. -/// <https://html.spec.whatwg.org/multipage/#structuredserializewithtransfer> -#[derive(Debug, Default, Deserialize, MallocSizeOf, Serialize)] -pub struct StructuredSerializedData { - /// Data serialized by SpiderMonkey. - pub serialized: Vec<u8>, - /// Serialized in a structured callback, - pub blobs: Option<HashMap<BlobId, BlobImpl>>, - /// Serialized point objects. - pub points: Option<HashMap<DomPointId, DomPoint>>, - /// Serialized exception objects. - pub exceptions: Option<HashMap<DomExceptionId, DomException>>, - /// Transferred objects. - pub ports: Option<HashMap<MessagePortId, MessagePortImpl>>, -} - -pub(crate) trait BroadcastClone -where - Self: Sized, -{ - /// The ID type that uniquely identify each value. - type Id: Eq + std::hash::Hash + Copy; - /// Clone this value so that it can be reused with a broadcast channel. - /// Only return None if cloning is impossible. - fn clone_for_broadcast(&self) -> Option<Self>; - /// The field from which to clone values. - fn source(data: &StructuredSerializedData) -> &Option<HashMap<Self::Id, Self>>; - /// The field into which to place cloned values. - fn destination(data: &mut StructuredSerializedData) -> &mut Option<HashMap<Self::Id, Self>>; -} - -/// All the DOM interfaces that can be serialized. -#[derive(Clone, Copy, Debug, EnumIter)] -pub enum Serializable { - /// The `Blob` interface. - Blob, - /// The `DOMPoint` interface. - DomPoint, - /// The `DOMPointReadOnly` interface. - DomPointReadOnly, - /// The `DOMException` interface. - DomException, -} - -impl Serializable { - fn clone_values(&self) -> fn(&StructuredSerializedData, &mut StructuredSerializedData) { - match self { - Serializable::Blob => StructuredSerializedData::clone_all_of_type::<BlobImpl>, - Serializable::DomPointReadOnly => { - StructuredSerializedData::clone_all_of_type::<DomPoint> - }, - Serializable::DomPoint => StructuredSerializedData::clone_all_of_type::<DomPoint>, - Serializable::DomException => { - StructuredSerializedData::clone_all_of_type::<DomException> - }, - } - } -} - -/// All the DOM interfaces that can be transferred. -#[derive(Clone, Copy, Debug, EnumIter)] -pub enum Transferrable { - /// The `MessagePort` interface. - MessagePort, - /// The `ReadableStream` interface. - ReadableStream, - /// The `WritableStream` interface. - WritableStream, -} - -impl StructuredSerializedData { - fn is_empty(&self, val: Transferrable) -> bool { - fn is_field_empty<K, V>(field: &Option<HashMap<K, V>>) -> bool { - field.as_ref().is_some_and(|h| h.is_empty()) - } - match val { - Transferrable::MessagePort => is_field_empty(&self.ports), - Transferrable::ReadableStream => is_field_empty(&self.ports), - Transferrable::WritableStream => is_field_empty(&self.ports), - } - } - - /// Clone all values of the same type stored in this StructuredSerializedData - /// into another instance. - fn clone_all_of_type<T: BroadcastClone>(&self, cloned: &mut StructuredSerializedData) { - let existing = T::source(self); - let Some(existing) = existing else { return }; - let mut clones = HashMap::with_capacity(existing.len()); - - for (original_id, obj) in existing.iter() { - if let Some(clone) = obj.clone_for_broadcast() { - clones.insert(*original_id, clone); - } - } - - *T::destination(cloned) = Some(clones); - } - - /// Clone the serialized data for use with broadcast-channels. - pub fn clone_for_broadcast(&self) -> StructuredSerializedData { - for transferrable in Transferrable::iter() { - if !self.is_empty(transferrable) { - // Not panicking only because this is called from the constellation. - warn!( - "Attempt to broadcast structured serialized data including {:?} (should never happen).", - transferrable, - ); - } - } - - let serialized = self.serialized.clone(); - - let mut cloned = StructuredSerializedData { - serialized, - ..Default::default() - }; - - for serializable in Serializable::iter() { - let clone_impl = serializable.clone_values(); - clone_impl(self, &mut cloned); - } - - cloned - } -} - -/// A task on the <https://html.spec.whatwg.org/multipage/#port-message-queue> -#[derive(Debug, Deserialize, MallocSizeOf, Serialize)] -pub struct PortMessageTask { - /// The origin of this task. - pub origin: ImmutableOrigin, - /// A data-holder for serialized data and transferred objects. - pub data: StructuredSerializedData, -} - -/// Messages for communication between the constellation and a global managing ports. -#[derive(Debug, Deserialize, Serialize)] -#[allow(clippy::large_enum_variant)] -pub enum MessagePortMsg { - /// Complete the transfer for a batch of ports. - CompleteTransfer(HashMap<MessagePortId, VecDeque<PortMessageTask>>), - /// Complete the transfer of a single port, - /// whose transfer was pending because it had been requested - /// while a previous failed transfer was being rolled-back. - CompletePendingTransfer(MessagePortId, VecDeque<PortMessageTask>), - /// Remove a port, the entangled one doesn't exists anymore. - RemoveMessagePort(MessagePortId), - /// Handle a new port-message-task. - NewTask(MessagePortId, PortMessageTask), -} - -/// Message for communication between the constellation and a global managing broadcast channels. -#[derive(Debug, Deserialize, Serialize)] -pub struct BroadcastMsg { - /// The origin of this message. - pub origin: ImmutableOrigin, - /// The name of the channel. - pub channel_name: String, - /// A data-holder for serialized data. - pub data: StructuredSerializedData, -} - -impl Clone for BroadcastMsg { - fn clone(&self) -> BroadcastMsg { - BroadcastMsg { - data: self.data.clone_for_broadcast(), - origin: self.origin.clone(), - channel_name: self.channel_name.clone(), - } - } -} - -/// File-based blob -#[derive(Debug, Deserialize, MallocSizeOf, Serialize)] -pub struct FileBlob { - #[ignore_malloc_size_of = "Uuid are hard(not really)"] - id: Uuid, - #[ignore_malloc_size_of = "PathBuf are hard"] - name: Option<PathBuf>, - cache: RefCell<Option<Vec<u8>>>, - size: u64, -} - -impl FileBlob { - /// Create a new file blob. - pub fn new(id: Uuid, name: Option<PathBuf>, cache: Option<Vec<u8>>, size: u64) -> FileBlob { - FileBlob { - id, - name, - cache: RefCell::new(cache), - size, - } - } - - /// Get the size of the file. - pub fn get_size(&self) -> u64 { - self.size - } - - /// Get the cached file data, if any. - pub fn get_cache(&self) -> Option<Vec<u8>> { - self.cache.borrow().clone() - } - - /// Cache data. - pub fn cache_bytes(&self, bytes: Vec<u8>) { - *self.cache.borrow_mut() = Some(bytes); - } - - /// Get the file id. - pub fn get_id(&self) -> Uuid { - self.id - } -} - -impl BroadcastClone for BlobImpl { - type Id = BlobId; - - fn source( - data: &StructuredSerializedData, - ) -> &Option<std::collections::HashMap<Self::Id, Self>> { - &data.blobs - } - - fn destination( - data: &mut StructuredSerializedData, - ) -> &mut Option<std::collections::HashMap<Self::Id, Self>> { - &mut data.blobs - } - - fn clone_for_broadcast(&self) -> Option<Self> { - let type_string = self.type_string(); - - if let BlobData::Memory(bytes) = self.blob_data() { - let blob_clone = BlobImpl::new_from_bytes(bytes.clone(), type_string); - - // Note: we insert the blob at the original id, - // otherwise this will not match the storage key as serialized by SM in `serialized`. - // The clone has it's own new Id however. - return Some(blob_clone); - } else { - // Not panicking only because this is called from the constellation. - log::warn!("Serialized blob not in memory format(should never happen)."); - } - None - } -} - -/// The data backing a DOM Blob. -#[derive(Debug, Deserialize, MallocSizeOf, Serialize)] -pub struct BlobImpl { - /// UUID of the blob. - blob_id: BlobId, - /// Content-type string - type_string: String, - /// Blob data-type. - blob_data: BlobData, - /// Sliced blobs referring to this one. - slices: Vec<BlobId>, -} - -/// Different backends of Blob -#[derive(Debug, Deserialize, MallocSizeOf, Serialize)] -pub enum BlobData { - /// File-based blob, whose content lives in the net process - File(FileBlob), - /// Memory-based blob, whose content lives in the script process - Memory(Vec<u8>), - /// Sliced blob, including parent blob-id and - /// relative positions of current slicing range, - /// IMPORTANT: The depth of tree is only two, i.e. the parent Blob must be - /// either File-based or Memory-based - Sliced(BlobId, RelativePos), -} - -impl BlobImpl { - /// Construct memory-backed BlobImpl - pub fn new_from_bytes(bytes: Vec<u8>, type_string: String) -> BlobImpl { - let blob_id = BlobId::new(); - let blob_data = BlobData::Memory(bytes); - BlobImpl { - blob_id, - type_string, - blob_data, - slices: vec![], - } - } - - /// Construct file-backed BlobImpl from File ID - pub fn new_from_file(file_id: Uuid, name: PathBuf, size: u64, type_string: String) -> BlobImpl { - let blob_id = BlobId::new(); - let blob_data = BlobData::File(FileBlob { - id: file_id, - name: Some(name), - cache: RefCell::new(None), - size, - }); - BlobImpl { - blob_id, - type_string, - blob_data, - slices: vec![], - } - } - - /// Construct a BlobImpl from a slice of a parent. - pub fn new_sliced(rel_pos: RelativePos, parent: BlobId, type_string: String) -> BlobImpl { - let blob_id = BlobId::new(); - let blob_data = BlobData::Sliced(parent, rel_pos); - BlobImpl { - blob_id, - type_string, - blob_data, - slices: vec![], - } - } - - /// Get a clone of the blob-id - pub fn blob_id(&self) -> BlobId { - self.blob_id - } - - /// Get a clone of the type-string - pub fn type_string(&self) -> String { - self.type_string.clone() - } - - /// Get a mutable ref to the data - pub fn blob_data(&self) -> &BlobData { - &self.blob_data - } - - /// Get a mutable ref to the data - pub fn blob_data_mut(&mut self) -> &mut BlobData { - &mut self.blob_data - } -} - -#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)] -/// A serializable version of the DOMPoint/DOMPointReadOnly interface. -pub struct DomPoint { - /// The x coordinate. - pub x: f64, - /// The y coordinate. - pub y: f64, - /// The z coordinate. - pub z: f64, - /// The w coordinate. - pub w: f64, -} - -impl BroadcastClone for DomPoint { - type Id = DomPointId; - - fn source( - data: &StructuredSerializedData, - ) -> &Option<std::collections::HashMap<Self::Id, Self>> { - &data.points - } - - fn destination( - data: &mut StructuredSerializedData, - ) -> &mut Option<std::collections::HashMap<Self::Id, Self>> { - &mut data.points - } - - fn clone_for_broadcast(&self) -> Option<Self> { - Some(self.clone()) - } -} - -#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)] -/// A serializable version of the DOMException interface. -pub struct DomException { - pub message: String, - pub name: String, -} - -impl BroadcastClone for DomException { - type Id = DomExceptionId; - - fn source( - data: &StructuredSerializedData, - ) -> &Option<std::collections::HashMap<Self::Id, Self>> { - &data.exceptions - } - - fn destination( - data: &mut StructuredSerializedData, - ) -> &mut Option<std::collections::HashMap<Self::Id, Self>> { - &mut data.exceptions - } - - fn clone_for_broadcast(&self) -> Option<Self> { - Some(self.clone()) - } -} diff --git a/components/shared/constellation/structured_data/mod.rs b/components/shared/constellation/structured_data/mod.rs new file mode 100644 index 00000000000..41fc05493a2 --- /dev/null +++ b/components/shared/constellation/structured_data/mod.rs @@ -0,0 +1,91 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +//! This module contains implementations of structured data as described in +//! <https://html.spec.whatwg.org/multipage/#safe-passing-of-structured-data> + +mod serializable; +mod transferable; + +use std::collections::HashMap; + +use base::id::{BlobId, DomExceptionId, DomPointId, MessagePortId}; +use log::warn; +use malloc_size_of_derive::MallocSizeOf; +use serde::{Deserialize, Serialize}; +pub use serializable::*; +use strum::IntoEnumIterator; +pub use transferable::*; + +/// A data-holder for serialized data and transferred objects. +/// <https://html.spec.whatwg.org/multipage/#structuredserializewithtransfer> +#[derive(Debug, Default, Deserialize, MallocSizeOf, Serialize)] +pub struct StructuredSerializedData { + /// Data serialized by SpiderMonkey. + pub serialized: Vec<u8>, + /// Serialized in a structured callback, + pub blobs: Option<HashMap<BlobId, BlobImpl>>, + /// Serialized point objects. + pub points: Option<HashMap<DomPointId, DomPoint>>, + /// Serialized exception objects. + pub exceptions: Option<HashMap<DomExceptionId, DomException>>, + /// Transferred objects. + pub ports: Option<HashMap<MessagePortId, MessagePortImpl>>, +} + +impl StructuredSerializedData { + fn is_empty(&self, val: Transferrable) -> bool { + fn is_field_empty<K, V>(field: &Option<HashMap<K, V>>) -> bool { + field.as_ref().is_some_and(|h| h.is_empty()) + } + match val { + Transferrable::MessagePort => is_field_empty(&self.ports), + Transferrable::ReadableStream => is_field_empty(&self.ports), + Transferrable::WritableStream => is_field_empty(&self.ports), + } + } + + /// Clone all values of the same type stored in this StructuredSerializedData + /// into another instance. + fn clone_all_of_type<T: BroadcastClone>(&self, cloned: &mut StructuredSerializedData) { + let existing = T::source(self); + let Some(existing) = existing else { return }; + let mut clones = HashMap::with_capacity(existing.len()); + + for (original_id, obj) in existing.iter() { + if let Some(clone) = obj.clone_for_broadcast() { + clones.insert(*original_id, clone); + } + } + + *T::destination(cloned) = Some(clones); + } + + /// Clone the serialized data for use with broadcast-channels. + pub fn clone_for_broadcast(&self) -> StructuredSerializedData { + for transferrable in Transferrable::iter() { + if !self.is_empty(transferrable) { + // Not panicking only because this is called from the constellation. + warn!( + "Attempt to broadcast structured serialized data including {:?} (should never happen).", + transferrable, + ); + } + } + + let serialized = self.serialized.clone(); + + let mut cloned = StructuredSerializedData { + serialized, + ..Default::default() + }; + + for serializable in Serializable::iter() { + let clone_impl = serializable.clone_values(); + clone_impl(self, &mut cloned); + } + + cloned + } +} diff --git a/components/shared/constellation/structured_data/serializable.rs b/components/shared/constellation/structured_data/serializable.rs new file mode 100644 index 00000000000..abc05ad5758 --- /dev/null +++ b/components/shared/constellation/structured_data/serializable.rs @@ -0,0 +1,314 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +//! This module contains implementations in script that are serializable, +//! as per <https://html.spec.whatwg.org/multipage/#serializable-objects>. +//! The implementations are here instead of in script as they need to +//! be passed through the Constellation. + +use std::cell::RefCell; +use std::collections::HashMap; +use std::path::PathBuf; + +use base::id::{BlobId, DomExceptionId, DomPointId}; +use malloc_size_of_derive::MallocSizeOf; +use net_traits::filemanager_thread::RelativePos; +use serde::{Deserialize, Serialize}; +use servo_url::ImmutableOrigin; +use strum::EnumIter; +use uuid::Uuid; + +use super::StructuredSerializedData; + +pub(crate) trait BroadcastClone +where + Self: Sized, +{ + /// The ID type that uniquely identify each value. + type Id: Eq + std::hash::Hash + Copy; + /// Clone this value so that it can be reused with a broadcast channel. + /// Only return None if cloning is impossible. + fn clone_for_broadcast(&self) -> Option<Self>; + /// The field from which to clone values. + fn source(data: &StructuredSerializedData) -> &Option<HashMap<Self::Id, Self>>; + /// The field into which to place cloned values. + fn destination(data: &mut StructuredSerializedData) -> &mut Option<HashMap<Self::Id, Self>>; +} + +/// All the DOM interfaces that can be serialized. +#[derive(Clone, Copy, Debug, EnumIter)] +pub enum Serializable { + /// The `Blob` interface. + Blob, + /// The `DOMPoint` interface. + DomPoint, + /// The `DOMPointReadOnly` interface. + DomPointReadOnly, + /// The `DOMException` interface. + DomException, +} + +impl Serializable { + pub(super) fn clone_values( + &self, + ) -> fn(&StructuredSerializedData, &mut StructuredSerializedData) { + match self { + Serializable::Blob => StructuredSerializedData::clone_all_of_type::<BlobImpl>, + Serializable::DomPointReadOnly => { + StructuredSerializedData::clone_all_of_type::<DomPoint> + }, + Serializable::DomPoint => StructuredSerializedData::clone_all_of_type::<DomPoint>, + Serializable::DomException => { + StructuredSerializedData::clone_all_of_type::<DomException> + }, + } + } +} + +/// Message for communication between the constellation and a global managing broadcast channels. +#[derive(Debug, Deserialize, Serialize)] +pub struct BroadcastMsg { + /// The origin of this message. + pub origin: ImmutableOrigin, + /// The name of the channel. + pub channel_name: String, + /// A data-holder for serialized data. + pub data: StructuredSerializedData, +} + +impl Clone for BroadcastMsg { + fn clone(&self) -> BroadcastMsg { + BroadcastMsg { + data: self.data.clone_for_broadcast(), + origin: self.origin.clone(), + channel_name: self.channel_name.clone(), + } + } +} + +/// File-based blob +#[derive(Debug, Deserialize, MallocSizeOf, Serialize)] +pub struct FileBlob { + #[ignore_malloc_size_of = "Uuid are hard(not really)"] + id: Uuid, + #[ignore_malloc_size_of = "PathBuf are hard"] + name: Option<PathBuf>, + cache: RefCell<Option<Vec<u8>>>, + size: u64, +} + +impl FileBlob { + /// Create a new file blob. + pub fn new(id: Uuid, name: Option<PathBuf>, cache: Option<Vec<u8>>, size: u64) -> FileBlob { + FileBlob { + id, + name, + cache: RefCell::new(cache), + size, + } + } + + /// Get the size of the file. + pub fn get_size(&self) -> u64 { + self.size + } + + /// Get the cached file data, if any. + pub fn get_cache(&self) -> Option<Vec<u8>> { + self.cache.borrow().clone() + } + + /// Cache data. + pub fn cache_bytes(&self, bytes: Vec<u8>) { + *self.cache.borrow_mut() = Some(bytes); + } + + /// Get the file id. + pub fn get_id(&self) -> Uuid { + self.id + } +} + +impl BroadcastClone for BlobImpl { + type Id = BlobId; + + fn source( + data: &StructuredSerializedData, + ) -> &Option<std::collections::HashMap<Self::Id, Self>> { + &data.blobs + } + + fn destination( + data: &mut StructuredSerializedData, + ) -> &mut Option<std::collections::HashMap<Self::Id, Self>> { + &mut data.blobs + } + + fn clone_for_broadcast(&self) -> Option<Self> { + let type_string = self.type_string(); + + if let BlobData::Memory(bytes) = self.blob_data() { + let blob_clone = BlobImpl::new_from_bytes(bytes.clone(), type_string); + + // Note: we insert the blob at the original id, + // otherwise this will not match the storage key as serialized by SM in `serialized`. + // The clone has it's own new Id however. + return Some(blob_clone); + } else { + // Not panicking only because this is called from the constellation. + log::warn!("Serialized blob not in memory format(should never happen)."); + } + None + } +} + +/// The data backing a DOM Blob. +#[derive(Debug, Deserialize, MallocSizeOf, Serialize)] +pub struct BlobImpl { + /// UUID of the blob. + blob_id: BlobId, + /// Content-type string + type_string: String, + /// Blob data-type. + blob_data: BlobData, + /// Sliced blobs referring to this one. + slices: Vec<BlobId>, +} + +/// Different backends of Blob +#[derive(Debug, Deserialize, MallocSizeOf, Serialize)] +pub enum BlobData { + /// File-based blob, whose content lives in the net process + File(FileBlob), + /// Memory-based blob, whose content lives in the script process + Memory(Vec<u8>), + /// Sliced blob, including parent blob-id and + /// relative positions of current slicing range, + /// IMPORTANT: The depth of tree is only two, i.e. the parent Blob must be + /// either File-based or Memory-based + Sliced(BlobId, RelativePos), +} + +impl BlobImpl { + /// Construct memory-backed BlobImpl + pub fn new_from_bytes(bytes: Vec<u8>, type_string: String) -> BlobImpl { + let blob_id = BlobId::new(); + let blob_data = BlobData::Memory(bytes); + BlobImpl { + blob_id, + type_string, + blob_data, + slices: vec![], + } + } + + /// Construct file-backed BlobImpl from File ID + pub fn new_from_file(file_id: Uuid, name: PathBuf, size: u64, type_string: String) -> BlobImpl { + let blob_id = BlobId::new(); + let blob_data = BlobData::File(FileBlob { + id: file_id, + name: Some(name), + cache: RefCell::new(None), + size, + }); + BlobImpl { + blob_id, + type_string, + blob_data, + slices: vec![], + } + } + + /// Construct a BlobImpl from a slice of a parent. + pub fn new_sliced(rel_pos: RelativePos, parent: BlobId, type_string: String) -> BlobImpl { + let blob_id = BlobId::new(); + let blob_data = BlobData::Sliced(parent, rel_pos); + BlobImpl { + blob_id, + type_string, + blob_data, + slices: vec![], + } + } + + /// Get a clone of the blob-id + pub fn blob_id(&self) -> BlobId { + self.blob_id + } + + /// Get a clone of the type-string + pub fn type_string(&self) -> String { + self.type_string.clone() + } + + /// Get a mutable ref to the data + pub fn blob_data(&self) -> &BlobData { + &self.blob_data + } + + /// Get a mutable ref to the data + pub fn blob_data_mut(&mut self) -> &mut BlobData { + &mut self.blob_data + } +} + +#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)] +/// A serializable version of the DOMPoint/DOMPointReadOnly interface. +pub struct DomPoint { + /// The x coordinate. + pub x: f64, + /// The y coordinate. + pub y: f64, + /// The z coordinate. + pub z: f64, + /// The w coordinate. + pub w: f64, +} + +impl BroadcastClone for DomPoint { + type Id = DomPointId; + + fn source( + data: &StructuredSerializedData, + ) -> &Option<std::collections::HashMap<Self::Id, Self>> { + &data.points + } + + fn destination( + data: &mut StructuredSerializedData, + ) -> &mut Option<std::collections::HashMap<Self::Id, Self>> { + &mut data.points + } + + fn clone_for_broadcast(&self) -> Option<Self> { + Some(self.clone()) + } +} + +#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)] +/// A serializable version of the DOMException interface. +pub struct DomException { + pub message: String, + pub name: String, +} + +impl BroadcastClone for DomException { + type Id = DomExceptionId; + + fn source( + data: &StructuredSerializedData, + ) -> &Option<std::collections::HashMap<Self::Id, Self>> { + &data.exceptions + } + + fn destination( + data: &mut StructuredSerializedData, + ) -> &mut Option<std::collections::HashMap<Self::Id, Self>> { + &mut data.exceptions + } + + fn clone_for_broadcast(&self) -> Option<Self> { + Some(self.clone()) + } +} diff --git a/components/shared/constellation/structured_data/transferable.rs b/components/shared/constellation/structured_data/transferable.rs new file mode 100644 index 00000000000..cd6388f5f34 --- /dev/null +++ b/components/shared/constellation/structured_data/transferable.rs @@ -0,0 +1,172 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +//! This module contains implementations in script that are transferable as per +//! <https://html.spec.whatwg.org/multipage/#transferable-objects>. The implementations are here +//! instead of in script as they need to be passed through the Constellation. + +use std::collections::VecDeque; + +use base::id::MessagePortId; +use malloc_size_of_derive::MallocSizeOf; +use serde::{Deserialize, Serialize}; +use strum::EnumIter; + +use crate::PortMessageTask; + +/// All the DOM interfaces that can be transferred. +#[derive(Clone, Copy, Debug, EnumIter)] +pub enum Transferrable { + /// The `MessagePort` interface. + MessagePort, + /// The `ReadableStream` interface. + ReadableStream, + /// The `WritableStream` interface. + WritableStream, +} + +#[derive(Debug, Deserialize, MallocSizeOf, Serialize)] +enum MessagePortState { + /// <https://html.spec.whatwg.org/multipage/#detached> + Detached, + /// <https://html.spec.whatwg.org/multipage/#port-message-queue> + /// The message-queue of this port is enabled, + /// the boolean represents awaiting completion of a transfer. + Enabled(bool), + /// <https://html.spec.whatwg.org/multipage/#port-message-queue> + /// The message-queue of this port is disabled, + /// the boolean represents awaiting completion of a transfer. + Disabled(bool), +} + +#[derive(Debug, Deserialize, MallocSizeOf, Serialize)] +/// The data and logic backing the DOM managed MessagePort. +pub struct MessagePortImpl { + /// The current state of the port. + state: MessagePortState, + + /// <https://html.spec.whatwg.org/multipage/#entangle> + entangled_port: Option<MessagePortId>, + + /// <https://html.spec.whatwg.org/multipage/#port-message-queue> + message_buffer: Option<VecDeque<PortMessageTask>>, + + /// The UUID of this port. + message_port_id: MessagePortId, +} + +impl MessagePortImpl { + /// Create a new messageport impl. + pub fn new(port_id: MessagePortId) -> MessagePortImpl { + MessagePortImpl { + state: MessagePortState::Disabled(false), + entangled_port: None, + message_buffer: None, + message_port_id: port_id, + } + } + + /// Get the Id. + pub fn message_port_id(&self) -> &MessagePortId { + &self.message_port_id + } + + /// Maybe get the Id of the entangled port. + pub fn entangled_port_id(&self) -> Option<MessagePortId> { + self.entangled_port + } + + /// Entanged this port with another. + pub fn entangle(&mut self, other_id: MessagePortId) { + self.entangled_port = Some(other_id); + } + + /// Is this port enabled? + pub fn enabled(&self) -> bool { + matches!(self.state, MessagePortState::Enabled(_)) + } + + /// Mark this port as having been shipped. + /// <https://html.spec.whatwg.org/multipage/#has-been-shipped> + pub fn set_has_been_shipped(&mut self) { + match self.state { + MessagePortState::Detached => { + panic!("Messageport set_has_been_shipped called in detached state") + }, + MessagePortState::Enabled(_) => self.state = MessagePortState::Enabled(true), + MessagePortState::Disabled(_) => self.state = MessagePortState::Disabled(true), + } + } + + /// Handle the completion of the transfer, + /// this is data received from the constellation. + pub fn complete_transfer(&mut self, mut tasks: VecDeque<PortMessageTask>) { + match self.state { + MessagePortState::Detached => return, + MessagePortState::Enabled(_) => self.state = MessagePortState::Enabled(false), + MessagePortState::Disabled(_) => self.state = MessagePortState::Disabled(false), + } + + // Note: these are the tasks that were buffered while the transfer was ongoing, + // hence they need to execute first. + // The global will call `start` if we are enabled, + // which will add tasks on the event-loop to dispatch incoming messages. + match self.message_buffer { + Some(ref mut incoming_buffer) => { + while let Some(task) = tasks.pop_back() { + incoming_buffer.push_front(task); + } + }, + None => self.message_buffer = Some(tasks), + } + } + + /// A message was received from our entangled port, + /// returns an optional task to be dispatched. + pub fn handle_incoming(&mut self, task: PortMessageTask) -> Option<PortMessageTask> { + let should_dispatch = match self.state { + MessagePortState::Detached => return None, + MessagePortState::Enabled(in_transfer) => !in_transfer, + MessagePortState::Disabled(_) => false, + }; + + if should_dispatch { + Some(task) + } else { + match self.message_buffer { + Some(ref mut buffer) => { + buffer.push_back(task); + }, + None => { + let mut queue = VecDeque::new(); + queue.push_back(task); + self.message_buffer = Some(queue); + }, + } + None + } + } + + /// <https://html.spec.whatwg.org/multipage/#dom-messageport-start> + /// returns an optional queue of tasks that were buffered while the port was disabled. + pub fn start(&mut self) -> Option<VecDeque<PortMessageTask>> { + match self.state { + MessagePortState::Detached => return None, + MessagePortState::Enabled(_) => {}, + MessagePortState::Disabled(in_transfer) => { + self.state = MessagePortState::Enabled(in_transfer); + }, + } + if let MessagePortState::Enabled(true) = self.state { + return None; + } + self.message_buffer.take() + } + + /// <https://html.spec.whatwg.org/multipage/#dom-messageport-close> + pub fn close(&mut self) { + // Step 1 + self.state = MessagePortState::Detached; + } +} diff --git a/components/shared/embedder/input_events.rs b/components/shared/embedder/input_events.rs index 16adccf8cd3..0268be6dd9c 100644 --- a/components/shared/embedder/input_events.rs +++ b/components/shared/embedder/input_events.rs @@ -51,7 +51,7 @@ pub struct MouseButtonEvent { pub point: DevicePoint, } -#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)] +#[derive(Clone, Copy, Debug, Deserialize, Serialize)] pub enum MouseButton { Left, Middle, |