From 2af6fe0b30a275e5fd8a43eca4126d82639fbaa9 Mon Sep 17 00:00:00 2001 From: Mukilan Thiyagarajan Date: Mon, 20 May 2024 16:05:18 +0530 Subject: compositor: Move WebRender-ish messages and types to `webrender_traits` (#32315) * Move WebRender related types to `webrender_traits` This refactor moves several WebRender related types from `compositing_traits`, `script_traits` and `net_traits` crates to the `webrender_traits` crate. This change also moves the `Image` type and associated function out of `net_traits` and into the `pixels` crate. Co-authored-by: Martin Robinson Signed-off-by: Mukilan Thiyagarajan * Move `script_traits::WebrenderIpcSender` to `webrender_traits::WebRenderScriptApi` --------- Signed-off-by: Mukilan Thiyagarajan Co-authored-by: Martin Robinson --- Cargo.lock | 28 +- components/canvas/canvas_data.rs | 13 +- components/canvas/canvas_paint_thread.rs | 9 +- components/compositing/Cargo.toml | 1 + components/compositing/compositor.rs | 93 +++--- components/constellation/constellation.rs | 14 +- components/constellation/pipeline.rs | 8 +- components/layout/Cargo.toml | 2 + components/layout/display_list/builder.rs | 2 +- components/layout/display_list/items.rs | 4 +- .../layout/display_list/webrender_helpers.rs | 6 +- components/layout/fragment.rs | 2 +- components/layout_2020/Cargo.toml | 2 + components/layout_2020/display_list/mod.rs | 4 +- .../layout_2020/display_list/stacking_context.rs | 2 +- components/layout_2020/dom.rs | 6 +- components/layout_2020/flow/root.rs | 2 +- .../layout_2020/fragment_tree/fragment_tree.rs | 2 +- components/layout_2020/replaced.rs | 2 +- components/layout_thread/Cargo.toml | 1 + components/layout_thread/lib.rs | 9 +- components/layout_thread_2020/Cargo.toml | 1 + components/layout_thread_2020/lib.rs | 9 +- components/net/Cargo.toml | 1 + components/net/image_cache.rs | 20 +- components/pixels/Cargo.toml | 4 + components/pixels/lib.rs | 150 +++++++++ components/script/Cargo.toml | 1 + components/script/dom/htmlimageelement.rs | 6 +- components/script/dom/htmlmediaelement.rs | 8 +- components/script/dom/htmlobjectelement.rs | 2 +- components/script/dom/node.rs | 2 +- components/script/dom/window.rs | 9 +- components/script/layout_dom/node.rs | 2 +- components/script/script_thread.rs | 5 +- components/servo/lib.rs | 9 +- components/shared/compositing/Cargo.toml | 4 +- components/shared/compositing/lib.rs | 20 +- components/shared/net/Cargo.toml | 1 + components/shared/net/image/base.rs | 121 -------- components/shared/net/image_cache.rs | 18 +- components/shared/net/lib.rs | 43 --- components/shared/net/tests/image.rs | 28 -- components/shared/script/Cargo.toml | 1 + components/shared/script/compositor.rs | 334 --------------------- components/shared/script/lib.rs | 259 +--------------- components/shared/script/tests/compositor.rs | 6 +- components/shared/script_layout/Cargo.toml | 2 + components/shared/script_layout/lib.rs | 5 +- components/shared/script_layout/wrapper_traits.rs | 2 +- components/shared/webrender/Cargo.toml | 7 + components/shared/webrender/display_list.rs | 334 +++++++++++++++++++++ components/shared/webrender/lib.rs | 324 +++++++++++++++++++- python/servo/testing_commands.py | 1 + 54 files changed, 1000 insertions(+), 951 deletions(-) delete mode 100644 components/shared/net/image/base.rs delete mode 100644 components/shared/net/tests/image.rs delete mode 100644 components/shared/script/compositor.rs create mode 100644 components/shared/webrender/display_list.rs diff --git a/Cargo.lock b/Cargo.lock index 2b8ec154adc..4588765b317 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -879,6 +879,7 @@ dependencies = [ "toml 0.5.11", "webrender", "webrender_api", + "webrender_traits", "webxr", ] @@ -887,7 +888,6 @@ name = "compositing_traits" version = "0.0.1" dependencies = [ "base", - "canvas", "crossbeam-channel", "embedder_traits", "euclid", @@ -895,11 +895,12 @@ dependencies = [ "ipc-channel", "keyboard-types", "log", - "net_traits", + "pixels", "script_traits", "servo_url", "style_traits", "webrender_api", + "webrender_traits", ] [[package]] @@ -3239,6 +3240,7 @@ dependencies = [ "malloc_size_of_derive", "net_traits", "parking_lot", + "pixels", "profile_traits", "range", "rayon", @@ -3258,6 +3260,7 @@ dependencies = [ "unicode-bidi", "unicode-script", "webrender_api", + "webrender_traits", "xi-unicode", ] @@ -3284,6 +3287,7 @@ dependencies = [ "log", "net_traits", "parking_lot", + "pixels", "quickcheck", "range", "rayon", @@ -3301,6 +3305,7 @@ dependencies = [ "unicode-segmentation", "url", "webrender_api", + "webrender_traits", "xi-unicode", ] @@ -3342,6 +3347,7 @@ dependencies = [ "time 0.1.45", "url", "webrender_api", + "webrender_traits", ] [[package]] @@ -3378,6 +3384,7 @@ dependencies = [ "style_traits", "url", "webrender_api", + "webrender_traits", ] [[package]] @@ -4026,6 +4033,7 @@ dependencies = [ "uuid", "webpki-roots", "webrender_api", + "webrender_traits", ] [[package]] @@ -4058,6 +4066,7 @@ dependencies = [ "url", "uuid", "webrender_api", + "webrender_traits", ] [[package]] @@ -4527,9 +4536,13 @@ name = "pixels" version = "0.0.1" dependencies = [ "euclid", + "image", + "ipc-channel", + "log", "malloc_size_of", "malloc_size_of_derive", "serde", + "webrender_api", ] [[package]] @@ -5123,6 +5136,7 @@ dependencies = [ "webdriver", "webgpu", "webrender_api", + "webrender_traits", "webxr-api", "xml5ever", ] @@ -5146,6 +5160,7 @@ dependencies = [ "malloc_size_of_derive", "metrics", "net_traits", + "pixels", "profile_traits", "range", "script_traits", @@ -5157,6 +5172,7 @@ dependencies = [ "style", "style_traits", "webrender_api", + "webrender_traits", ] [[package]] @@ -5206,6 +5222,7 @@ dependencies = [ "webdriver", "webgpu", "webrender_api", + "webrender_traits", "webxr-api", ] @@ -7186,7 +7203,14 @@ dependencies = [ name = "webrender_traits" version = "0.0.1" dependencies = [ + "base", + "crossbeam-channel", + "embedder_traits", "euclid", + "ipc-channel", + "libc", + "log", + "serde", "webrender_api", ] diff --git a/components/canvas/canvas_data.rs b/components/canvas/canvas_data.rs index 0175242521e..5396eaddea6 100644 --- a/components/canvas/canvas_data.rs +++ b/components/canvas/canvas_data.rs @@ -26,8 +26,9 @@ use style::values::computed::font; use style_traits::values::ToCss; use webrender_api::units::{DeviceIntSize, RectExt as RectExt_}; use webrender_api::{ImageData, ImageDescriptor, ImageDescriptorFlags, ImageFormat, ImageKey}; +use webrender_traits::ImageUpdate; -use crate::canvas_paint_thread::{AntialiasMode, ImageUpdate, WebrenderApi}; +use crate::canvas_paint_thread::{AntialiasMode, WebrenderApi}; use crate::raqote_backend::Repetition; /// The canvas data stores a state machine for the current status of @@ -1082,13 +1083,13 @@ impl<'a> CanvasData<'a> { match self.image_key { Some(image_key) => { debug!("Updating image {:?}.", image_key); - updates.push(ImageUpdate::Update(image_key, descriptor, data)); + updates.push(ImageUpdate::UpdateImage(image_key, descriptor, data)); }, None => { let Some(key) = self.webrender_api.generate_key() else { return; }; - updates.push(ImageUpdate::Add(key, descriptor, data)); + updates.push(ImageUpdate::AddImage(key, descriptor, data)); self.image_key = Some(key); debug!("New image {:?}.", self.image_key); }, @@ -1097,7 +1098,7 @@ impl<'a> CanvasData<'a> { if let Some(image_key) = mem::replace(&mut self.very_old_image_key, self.old_image_key.take()) { - updates.push(ImageUpdate::Delete(image_key)); + updates.push(ImageUpdate::DeleteImage(image_key)); } self.webrender_api.update_images(updates); @@ -1215,10 +1216,10 @@ impl<'a> Drop for CanvasData<'a> { fn drop(&mut self) { let mut updates = vec![]; if let Some(image_key) = self.old_image_key.take() { - updates.push(ImageUpdate::Delete(image_key)); + updates.push(ImageUpdate::DeleteImage(image_key)); } if let Some(image_key) = self.very_old_image_key.take() { - updates.push(ImageUpdate::Delete(image_key)); + updates.push(ImageUpdate::DeleteImage(image_key)); } self.webrender_api.update_images(updates); diff --git a/components/canvas/canvas_paint_thread.rs b/components/canvas/canvas_paint_thread.rs index 2176ad8d636..63f881ddacf 100644 --- a/components/canvas/canvas_paint_thread.rs +++ b/components/canvas/canvas_paint_thread.rs @@ -16,7 +16,8 @@ use gfx::font_context::FontContext; use ipc_channel::ipc::{self, IpcSender}; use ipc_channel::router::ROUTER; use log::warn; -use webrender_api::{ImageData, ImageDescriptor, ImageKey}; +use webrender_api::ImageKey; +use webrender_traits::ImageUpdate; use crate::canvas_data::*; @@ -25,12 +26,6 @@ pub enum AntialiasMode { None, } -pub enum ImageUpdate { - Add(ImageKey, ImageDescriptor, ImageData), - Update(ImageKey, ImageDescriptor, ImageData), - Delete(ImageKey), -} - pub trait WebrenderApi { /// Attempt to generate an [`ImageKey`], returning `None` in case of failure. fn generate_key(&self) -> Option; diff --git a/components/compositing/Cargo.toml b/components/compositing/Cargo.toml index 37c1b6b6a21..3269f61ae02 100644 --- a/components/compositing/Cargo.toml +++ b/components/compositing/Cargo.toml @@ -43,6 +43,7 @@ style_traits = { workspace = true } time = { workspace = true } webrender = { workspace = true } webrender_api = { workspace = true } +webrender_traits = { workspace = true } webxr = { git = "https://github.com/servo/webxr" } [build-dependencies] diff --git a/components/compositing/compositor.rs b/components/compositing/compositor.rs index d211a4c4783..826c48dc836 100644 --- a/components/compositing/compositor.rs +++ b/components/compositing/compositor.rs @@ -13,10 +13,9 @@ use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH}; use base::id::{PipelineId, TopLevelBrowsingContextId, WebViewId}; use base::{Epoch, WebRenderEpochToU16}; -use canvas::canvas_paint_thread::ImageUpdate; use compositing_traits::{ - CanvasToCompositorMsg, CompositionPipeline, CompositorMsg, CompositorReceiver, - ConstellationMsg, FontToCompositorMsg, ForwardedToCompositorMsg, SendableFrameTree, + CompositionPipeline, CompositorMsg, CompositorReceiver, ConstellationMsg, + ForwardedToCompositorMsg, SendableFrameTree, }; use crossbeam_channel::Sender; use embedder_traits::Cursor; @@ -27,16 +26,13 @@ use image::{DynamicImage, ImageFormat}; use ipc_channel::ipc; use libc::c_void; use log::{debug, error, info, trace, warn}; -use net_traits::image::base::Image; -use net_traits::image_cache::CorsStatus; -use pixels::PixelFormat; +use pixels::{CorsStatus, Image, PixelFormat}; use profile_traits::time::{self as profile_time, profile, ProfilerCategory}; -use script_traits::compositor::{HitTestInfo, ScrollTree}; use script_traits::CompositorEvent::{MouseButtonEvent, MouseMoveEvent, TouchEvent, WheelEvent}; use script_traits::{ - AnimationState, AnimationTickType, CompositorHitTestResult, ConstellationControlMsg, - LayoutControlMsg, MouseButton, MouseEventType, ScrollState, TouchEventType, TouchId, - UntrustedNodeAddress, WheelDelta, WindowSizeData, WindowSizeType, + AnimationState, AnimationTickType, ConstellationControlMsg, LayoutControlMsg, MouseButton, + MouseEventType, ScrollState, TouchEventType, TouchId, WheelDelta, WindowSizeData, + WindowSizeType, }; use servo_geometry::{DeviceIndependentPixel, FramebufferUintLength}; use style_traits::{CSSPixel, DevicePixel, PinchZoomFactor}; @@ -51,6 +47,11 @@ use webrender_api::{ PropertyBinding, ReferenceFrameKind, RenderReasons, SampledScrollOffset, ScrollLocation, SpaceAndClipInfo, SpatialId, SpatialTreeItemKey, TransformStyle, }; +use webrender_traits::display_list::{HitTestInfo, ScrollTree}; +use webrender_traits::{ + CanvasToCompositorMsg, CompositorHitTestResult, FontToCompositorMsg, ImageUpdate, + NetToCompositorMsg, ScriptToCompositorMsg, SerializedImageUpdate, UntrustedNodeAddress, +}; use crate::gl::RenderTargetInfo; use crate::touch::{TouchAction, TouchHandler}; @@ -695,9 +696,9 @@ impl IOCompositor { /// instance in the parent process. fn handle_webrender_message(&mut self, msg: ForwardedToCompositorMsg) { match msg { - ForwardedToCompositorMsg::Layout( - script_traits::ScriptToCompositorMsg::SendInitialTransaction(pipeline), - ) => { + ForwardedToCompositorMsg::Layout(ScriptToCompositorMsg::SendInitialTransaction( + pipeline, + )) => { let mut txn = Transaction::new(); txn.set_display_list(WebRenderEpoch(0), (pipeline, Default::default())); self.generate_frame(&mut txn, RenderReasons::SCENE); @@ -705,13 +706,11 @@ impl IOCompositor { .send_transaction(self.webrender_document, txn); }, - ForwardedToCompositorMsg::Layout( - script_traits::ScriptToCompositorMsg::SendScrollNode( - pipeline_id, - point, - external_scroll_id, - ), - ) => { + ForwardedToCompositorMsg::Layout(ScriptToCompositorMsg::SendScrollNode( + pipeline_id, + point, + external_scroll_id, + )) => { let pipeline_id = pipeline_id.into(); let pipeline_details = match self.pipeline_details.get_mut(&pipeline_id) { Some(details) => details, @@ -743,13 +742,11 @@ impl IOCompositor { .send_transaction(self.webrender_document, txn); }, - ForwardedToCompositorMsg::Layout( - script_traits::ScriptToCompositorMsg::SendDisplayList { - display_list_info, - display_list_descriptor, - display_list_receiver, - }, - ) => { + ForwardedToCompositorMsg::Layout(ScriptToCompositorMsg::SendDisplayList { + display_list_info, + display_list_descriptor, + display_list_receiver, + }) => { // This must match the order from the sender, currently in `shared/script/lib.rs`. let items_data = match display_list_receiver.recv() { Ok(display_list_data) => display_list_data, @@ -799,7 +796,7 @@ impl IOCompositor { .send_transaction(self.webrender_document, transaction); }, - ForwardedToCompositorMsg::Layout(script_traits::ScriptToCompositorMsg::HitTest( + ForwardedToCompositorMsg::Layout(ScriptToCompositorMsg::HitTest( pipeline, point, flags, @@ -821,31 +818,23 @@ impl IOCompositor { let _ = sender.send(result); }, - ForwardedToCompositorMsg::Layout( - script_traits::ScriptToCompositorMsg::GenerateImageKey(sender), - ) | - ForwardedToCompositorMsg::Net(net_traits::NetToCompositorMsg::GenerateImageKey( - sender, - )) => { + ForwardedToCompositorMsg::Layout(ScriptToCompositorMsg::GenerateImageKey(sender)) | + ForwardedToCompositorMsg::Net(NetToCompositorMsg::GenerateImageKey(sender)) => { let _ = sender.send(self.webrender_api.generate_image_key()); }, - ForwardedToCompositorMsg::Layout( - script_traits::ScriptToCompositorMsg::UpdateImages(updates), - ) => { + ForwardedToCompositorMsg::Layout(ScriptToCompositorMsg::UpdateImages(updates)) => { let mut txn = Transaction::new(); for update in updates { match update { - script_traits::SerializedImageUpdate::AddImage(key, desc, data) => { + SerializedImageUpdate::AddImage(key, desc, data) => { match data.to_image_data() { Ok(data) => txn.add_image(key, desc, data, None), Err(e) => warn!("error when sending image data: {:?}", e), } }, - script_traits::SerializedImageUpdate::DeleteImage(key) => { - txn.delete_image(key) - }, - script_traits::SerializedImageUpdate::UpdateImage(key, desc, data) => { + SerializedImageUpdate::DeleteImage(key) => txn.delete_image(key), + SerializedImageUpdate::UpdateImage(key, desc, data) => { match data.to_image_data() { Ok(data) => txn.update_image(key, desc, data, &DirtyRect::All), Err(e) => warn!("error when sending image data: {:?}", e), @@ -857,11 +846,7 @@ impl IOCompositor { .send_transaction(self.webrender_document, txn); }, - ForwardedToCompositorMsg::Net(net_traits::NetToCompositorMsg::AddImage( - key, - desc, - data, - )) => { + ForwardedToCompositorMsg::Net(NetToCompositorMsg::AddImage(key, desc, data)) => { let mut txn = Transaction::new(); txn.add_image(key, desc, data, None); self.webrender_api @@ -929,13 +914,13 @@ impl IOCompositor { let mut txn = Transaction::new(); for update in updates { match update { - ImageUpdate::Add(key, descriptor, data) => { + ImageUpdate::AddImage(key, descriptor, data) => { txn.add_image(key, descriptor, data, None) }, - ImageUpdate::Update(key, descriptor, data) => { + ImageUpdate::UpdateImage(key, descriptor, data) => { txn.update_image(key, descriptor, data, &DirtyRect::All) }, - ImageUpdate::Delete(key) => txn.delete_image(key), + ImageUpdate::DeleteImage(key) => txn.delete_image(key), } } self.webrender_api @@ -1469,7 +1454,7 @@ impl IOCompositor { event_type, button, result.point_in_viewport.to_untyped(), - Some(result.node), + Some(result.node.into()), Some(result.point_relative_to_item), button as u16, ); @@ -1549,7 +1534,7 @@ impl IOCompositor { None => return, }; - let event = MouseMoveEvent(result.point_in_viewport, Some(result.node), 0); + let event = MouseMoveEvent(result.point_in_viewport, Some(result.node.into()), 0); let msg = ConstellationMsg::ForwardEvent(result.pipeline_id, event); if let Err(e) = self.constellation_chan.send(msg) { warn!("Sending event to constellation failed ({:?}).", e); @@ -1568,7 +1553,7 @@ impl IOCompositor { event_type, identifier, result.point_in_viewport, - Some(result.node), + Some(result.node.into()), ); let msg = ConstellationMsg::ForwardEvent(result.pipeline_id, event); if let Err(e) = self.constellation_chan.send(msg) { @@ -1579,7 +1564,7 @@ impl IOCompositor { pub fn send_wheel_event(&mut self, delta: WheelDelta, point: DevicePoint) { if let Some(result) = self.hit_test_at_point(point) { - let event = WheelEvent(delta, result.point_in_viewport, Some(result.node)); + let event = WheelEvent(delta, result.point_in_viewport, Some(result.node.into())); let msg = ConstellationMsg::ForwardEvent(result.pipeline_id, event); if let Err(e) = self.constellation_chan.send(msg) { warn!("Sending event to constellation failed ({:?}).", e); diff --git a/components/constellation/constellation.rs b/components/constellation/constellation.rs index 14176fc0739..00211847c92 100644 --- a/components/constellation/constellation.rs +++ b/components/constellation/constellation.rs @@ -155,7 +155,7 @@ use style_traits::CSSPixel; use webgpu::{self, WebGPU, WebGPURequest}; use webrender::{RenderApi, RenderApiSender}; use webrender_api::DocumentId; -use webrender_traits::WebrenderExternalImageRegistry; +use webrender_traits::{WebRenderNetApi, WebRenderScriptApi, WebrenderExternalImageRegistry}; use crate::browsingcontext::{ AllBrowsingContextsIterator, BrowsingContext, FullyActiveBrowsingContextsIterator, @@ -392,11 +392,11 @@ pub struct Constellation { /// A channel for content processes to send messages that will /// be relayed to the WebRender thread. - webrender_api_ipc_sender: script_traits::WebrenderIpcSender, + webrender_api_ipc_sender: WebRenderScriptApi, /// A channel for content process image caches to send messages /// that will be relayed to the WebRender thread. - webrender_image_api_sender: net_traits::WebrenderIpcSender, + webrender_image_api_sender: WebRenderNetApi, /// A map of message-port Id to info. message_ports: HashMap, @@ -785,12 +785,8 @@ where scheduler_receiver, document_states: HashMap::new(), webrender_document: state.webrender_document, - webrender_api_ipc_sender: script_traits::WebrenderIpcSender::new( - webrender_ipc_sender, - ), - webrender_image_api_sender: net_traits::WebrenderIpcSender::new( - webrender_image_ipc_sender, - ), + webrender_api_ipc_sender: WebRenderScriptApi::new(webrender_ipc_sender), + webrender_image_api_sender: WebRenderNetApi::new(webrender_image_ipc_sender), webrender_wgpu, shutting_down: false, handled_warnings: VecDeque::new(), diff --git a/components/constellation/pipeline.rs b/components/constellation/pipeline.rs index 000a52768ac..9547aa8a354 100644 --- a/components/constellation/pipeline.rs +++ b/components/constellation/pipeline.rs @@ -185,10 +185,10 @@ pub struct InitialPipelineState { pub prev_throttled: bool, /// Webrender api. - pub webrender_image_api_sender: net_traits::WebrenderIpcSender, + pub webrender_image_api_sender: webrender_traits::WebRenderNetApi, /// Webrender api. - pub webrender_api_sender: script_traits::WebrenderIpcSender, + pub webrender_api_sender: webrender_traits::WebRenderScriptApi, /// The ID of the document processed by this script thread. pub webrender_document: DocumentId, @@ -502,8 +502,8 @@ pub struct UnprivilegedPipelineContent { opts: Opts, prefs: HashMap, pipeline_namespace_id: PipelineNamespaceId, - webrender_api_sender: script_traits::WebrenderIpcSender, - webrender_image_api_sender: net_traits::WebrenderIpcSender, + webrender_api_sender: webrender_traits::WebRenderScriptApi, + webrender_image_api_sender: webrender_traits::WebRenderNetApi, webrender_document: DocumentId, webgl_chan: Option, webxr_registry: webxr_api::Registry, diff --git a/components/layout/Cargo.toml b/components/layout/Cargo.toml index f63002b5fe3..f7b50524604 100644 --- a/components/layout/Cargo.toml +++ b/components/layout/Cargo.toml @@ -30,6 +30,7 @@ log = { workspace = true } malloc_size_of = { workspace = true } malloc_size_of_derive = { workspace = true } net_traits = { workspace = true } +pixels = { path = "../pixels" } parking_lot = { workspace = true } profile_traits = { workspace = true } range = { path = "../range" } @@ -50,4 +51,5 @@ style_traits = { workspace = true } unicode-bidi = { workspace = true, features = ["with_serde"] } unicode-script = { workspace = true } webrender_api = { workspace = true } +webrender_traits = { workspace = true } xi-unicode = { workspace = true } diff --git a/components/layout/display_list/builder.rs b/components/layout/display_list/builder.rs index 0fba6875d2f..62257e17d8b 100644 --- a/components/layout/display_list/builder.rs +++ b/components/layout/display_list/builder.rs @@ -27,7 +27,6 @@ use log::{debug, warn}; use net_traits::image_cache::UsePlaceholder; use range::Range; use script_layout_interface::{combine_id_with_fragment_type, FragmentType}; -use script_traits::compositor::ScrollSensitivity; use servo_config::opts; use servo_geometry::{self, MaxRect}; use style::color::AbsoluteColor; @@ -52,6 +51,7 @@ use webrender_api::{ ExternalScrollId, FilterOp, GlyphInstance, ImageRendering, LineStyle, NinePatchBorder, NinePatchBorderSource, NormalBorder, PropertyBinding, StickyOffsetBounds, }; +use webrender_traits::display_list::ScrollSensitivity; use super::StackingContextId; use crate::block::BlockFlow; diff --git a/components/layout/display_list/items.rs b/components/layout/display_list/items.rs index 6fe14f03143..92117a118df 100644 --- a/components/layout/display_list/items.rs +++ b/components/layout/display_list/items.rs @@ -20,8 +20,7 @@ use base::id::PipelineId; use base::print_tree::PrintTree; use embedder_traits::Cursor; use euclid::{SideOffsets2D, Vector2D}; -use net_traits::image::base::Image; -use script_traits::compositor::{ScrollSensitivity, ScrollTreeNodeId}; +use pixels::Image; use serde::Serialize; use servo_geometry::MaxRect; use style::computed_values::_servo_top_layer::T as InTopLayer; @@ -33,6 +32,7 @@ use webrender_api::{ FilterOp, GlyphInstance, GradientStop, ImageKey, MixBlendMode, PrimitiveFlags, Shadow, SpatialId, StickyOffsetBounds, TransformStyle, }; +use webrender_traits::display_list::{ScrollSensitivity, ScrollTreeNodeId}; use super::StackingContextId; diff --git a/components/layout/display_list/webrender_helpers.rs b/components/layout/display_list/webrender_helpers.rs index 0ac79b4d8ca..c6c2abfdd34 100644 --- a/components/layout/display_list/webrender_helpers.rs +++ b/components/layout/display_list/webrender_helpers.rs @@ -10,15 +10,15 @@ use base::id::PipelineId; use base::WebRenderEpochToU16; use log::trace; -use script_traits::compositor::{ - CompositorDisplayListInfo, ScrollSensitivity, ScrollTreeNodeId, ScrollableNodeInfo, -}; use webrender_api::units::{LayoutPoint, LayoutSize, LayoutVector2D}; use webrender_api::{ self, ClipChainId, ClipId, CommonItemProperties, DisplayItem as WrDisplayItem, DisplayListBuilder, Epoch, HasScrollLinkedEffect, PrimitiveFlags, PropertyBinding, RasterSpace, ReferenceFrameKind, SpaceAndClipInfo, SpatialId, SpatialTreeItemKey, }; +use webrender_traits::display_list::{ + CompositorDisplayListInfo, ScrollSensitivity, ScrollTreeNodeId, ScrollableNodeInfo, +}; use crate::display_list::items::{ BaseDisplayItem, ClipScrollNode, ClipScrollNodeType, ClipType, DisplayItem, DisplayList, diff --git a/components/layout/fragment.rs b/components/layout/fragment.rs index 171c529babc..7ddcbdb2e6b 100644 --- a/components/layout/fragment.rs +++ b/components/layout/fragment.rs @@ -20,8 +20,8 @@ use gfx::text::text_run::{TextRun, TextRunSlice}; use html5ever::{local_name, namespace_url, ns}; use ipc_channel::ipc::IpcSender; use log::debug; -use net_traits::image::base::{Image, ImageMetadata}; use net_traits::image_cache::{ImageOrMetadataAvailable, UsePlaceholder}; +use pixels::{Image, ImageMetadata}; use range::*; use script_layout_interface::wrapper_traits::{ PseudoElementType, ThreadSafeLayoutElement, ThreadSafeLayoutNode, diff --git a/components/layout_2020/Cargo.toml b/components/layout_2020/Cargo.toml index 99446a0f59c..5810cfee983 100644 --- a/components/layout_2020/Cargo.toml +++ b/components/layout_2020/Cargo.toml @@ -30,6 +30,7 @@ ipc-channel = { workspace = true } log = { workspace = true } net_traits = { workspace = true } parking_lot = { workspace = true } +pixels = { path = "../pixels" } range = { path = "../range" } rayon = { workspace = true } script_layout_interface = { workspace = true } @@ -47,6 +48,7 @@ unicode-segmentation = { workspace = true } url = { workspace = true } data-url = { workspace = true } webrender_api = { workspace = true } +webrender_traits = { workspace = true } xi-unicode = { workspace = true } [dev-dependencies] diff --git a/components/layout_2020/display_list/mod.rs b/components/layout_2020/display_list/mod.rs index d4be0e26b71..41236cdc413 100644 --- a/components/layout_2020/display_list/mod.rs +++ b/components/layout_2020/display_list/mod.rs @@ -12,7 +12,6 @@ use euclid::{Point2D, SideOffsets2D, Size2D}; use fnv::FnvHashMap; use gfx::text::glyph::GlyphStore; use net_traits::image_cache::UsePlaceholder; -use script_traits::compositor::{CompositorDisplayListInfo, ScrollSensitivity, ScrollTreeNodeId}; use servo_geometry::MaxRect; use style::color::{AbsoluteColor, ColorSpace}; use style::computed_values::text_decoration_style::T as ComputedTextDecorationStyle; @@ -24,6 +23,9 @@ use style::values::specified::text::TextDecorationLine; use style::values::specified::ui::CursorKind; use style_traits::CSSPixel; use webrender_api::{self as wr, units, BoxShadowClipMode, ClipChainId}; +use webrender_traits::display_list::{ + CompositorDisplayListInfo, ScrollSensitivity, ScrollTreeNodeId, +}; use wr::units::LayoutVector2D; use crate::context::LayoutContext; diff --git a/components/layout_2020/display_list/stacking_context.rs b/components/layout_2020/display_list/stacking_context.rs index 0b37949b1b8..2833fa459fc 100644 --- a/components/layout_2020/display_list/stacking_context.rs +++ b/components/layout_2020/display_list/stacking_context.rs @@ -9,7 +9,6 @@ use base::print_tree::PrintTree; use euclid::default::Rect; use euclid::SideOffsets2D; use log::warn; -use script_traits::compositor::{ScrollSensitivity, ScrollTreeNodeId, ScrollableNodeInfo}; use servo_arc::Arc as ServoArc; use servo_config::opts::DebugOptions; use style::computed_values::float::T as ComputedFloat; @@ -24,6 +23,7 @@ use style::values::specified::box_::DisplayOutside; use style::Zero; use webrender_api as wr; use webrender_api::units::{LayoutPoint, LayoutRect, LayoutTransform, LayoutVector2D}; +use webrender_traits::display_list::{ScrollSensitivity, ScrollTreeNodeId, ScrollableNodeInfo}; use wr::units::{LayoutPixel, LayoutSize}; use wr::{ClipChainId, SpatialTreeItemKey, StickyOffsetBounds}; diff --git a/components/layout_2020/dom.rs b/components/layout_2020/dom.rs index 24d4647e0a5..2c8209c9b3c 100644 --- a/components/layout_2020/dom.rs +++ b/components/layout_2020/dom.rs @@ -8,7 +8,7 @@ use std::sync::{Arc, Mutex}; use atomic_refcell::{AtomicRef, AtomicRefCell, AtomicRefMut}; use base::id::{BrowsingContextId, PipelineId}; use html5ever::{local_name, namespace_url, ns}; -use net_traits::image::base::Image as NetImage; +use pixels::Image; use script_layout_interface::wrapper_traits::{ LayoutDataTrait, LayoutNode, ThreadSafeLayoutElement, ThreadSafeLayoutNode, }; @@ -96,7 +96,7 @@ impl Drop for BoxSlot<'_> { pub(crate) trait NodeExt<'dom>: 'dom + LayoutNode<'dom> { /// Returns the image if it’s loaded, and its size in image pixels /// adjusted for `image_density`. - fn as_image(self) -> Option<(Option>, PhysicalSize)>; + fn as_image(self) -> Option<(Option>, PhysicalSize)>; fn as_canvas(self) -> Option<(CanvasInfo, PhysicalSize)>; fn as_iframe(self) -> Option<(PipelineId, BrowsingContextId)>; fn as_video(self) -> Option<(webrender_api::ImageKey, PhysicalSize)>; @@ -117,7 +117,7 @@ impl<'dom, LayoutNodeType> NodeExt<'dom> for LayoutNodeType where LayoutNodeType: 'dom + LayoutNode<'dom>, { - fn as_image(self) -> Option<(Option>, PhysicalSize)> { + fn as_image(self) -> Option<(Option>, PhysicalSize)> { let node = self.to_threadsafe(); let (resource, metadata) = node.image_data()?; let (width, height) = resource diff --git a/components/layout_2020/flow/root.rs b/components/layout_2020/flow/root.rs index b3b436ce23d..6dae0291a71 100644 --- a/components/layout_2020/flow/root.rs +++ b/components/layout_2020/flow/root.rs @@ -7,13 +7,13 @@ use script_layout_interface::wrapper_traits::{ LayoutNode, ThreadSafeLayoutElement, ThreadSafeLayoutNode, }; use script_layout_interface::{LayoutElementType, LayoutNodeType}; -use script_traits::compositor::ScrollSensitivity; use serde::Serialize; use servo_arc::Arc; use style::dom::OpaqueNode; use style::properties::ComputedValues; use style::values::computed::{Length, Overflow}; use style_traits::CSSPixel; +use webrender_traits::display_list::ScrollSensitivity; use crate::cell::ArcRefCell; use crate::context::LayoutContext; diff --git a/components/layout_2020/fragment_tree/fragment_tree.rs b/components/layout_2020/fragment_tree/fragment_tree.rs index 77d07a65847..085274d4035 100644 --- a/components/layout_2020/fragment_tree/fragment_tree.rs +++ b/components/layout_2020/fragment_tree/fragment_tree.rs @@ -6,12 +6,12 @@ use app_units::Au; use base::print_tree::PrintTree; use euclid::default::{Point2D, Rect, Size2D}; use fxhash::FxHashSet; -use script_traits::compositor::ScrollSensitivity; use serde::Serialize; use style::animation::AnimationSetKey; use style::dom::OpaqueNode; use style::values::computed::Length; use webrender_api::units; +use webrender_traits::display_list::ScrollSensitivity; use super::{ContainingBlockManager, Fragment, Tag}; use crate::cell::ArcRefCell; diff --git a/components/layout_2020/replaced.rs b/components/layout_2020/replaced.rs index ac3f1621d85..fbd359709c2 100644 --- a/components/layout_2020/replaced.rs +++ b/components/layout_2020/replaced.rs @@ -10,8 +10,8 @@ use base::id::{BrowsingContextId, PipelineId}; use canvas_traits::canvas::{CanvasId, CanvasMsg, FromLayoutMsg}; use data_url::DataUrl; use ipc_channel::ipc::{self, IpcSender}; -use net_traits::image::base::Image; use net_traits::image_cache::{ImageOrMetadataAvailable, UsePlaceholder}; +use pixels::Image; use serde::Serialize; use servo_arc::Arc as ServoArc; use style::properties::ComputedValues; diff --git a/components/layout_thread/Cargo.toml b/components/layout_thread/Cargo.toml index 978b597212b..a95c7f9b8aa 100644 --- a/components/layout_thread/Cargo.toml +++ b/components/layout_thread/Cargo.toml @@ -45,3 +45,4 @@ style_traits = { workspace = true } time = { workspace = true } url = { workspace = true } webrender_api = { workspace = true } +webrender_traits = { workspace = true } diff --git a/components/layout_thread/lib.rs b/components/layout_thread/lib.rs index e279f10221d..8cb612bd1de 100644 --- a/components/layout_thread/lib.rs +++ b/components/layout_thread/lib.rs @@ -69,7 +69,7 @@ use script_layout_interface::{ use script_traits::{ ConstellationControlMsg, DrawAPaintImageResult, IFrameSizeMsg, LayoutControlMsg, LayoutMsg as ConstellationMsg, PaintWorkletError, Painter, ScrollState, UntrustedNodeAddress, - WebrenderIpcSender, WindowSizeData, WindowSizeType, + WindowSizeData, WindowSizeType, }; use servo_arc::Arc as ServoArc; use servo_atoms::Atom; @@ -102,6 +102,7 @@ use style::traversal_flags::TraversalFlags; use style_traits::{CSSPixel, DevicePixel, SpeculativePainter}; use url::Url; use webrender_api::{units, ColorF, HitTestFlags}; +use webrender_traits::WebRenderScriptApi; /// Information needed by layout. pub struct LayoutThread { @@ -176,7 +177,7 @@ pub struct LayoutThread { registered_painters: RegisteredPaintersImpl, /// Webrender interface. - webrender_api: WebrenderIpcSender, + webrender_api: WebRenderScriptApi, /// Paint time metrics. paint_time_metrics: PaintTimeMetrics, @@ -365,7 +366,7 @@ impl Layout for LayoutThread { .webrender_api .hit_test(Some(self.id.into()), client_point, flags); - results.iter().map(|result| result.node).collect() + results.iter().map(|result| result.node.into()).collect() } fn query_offset_parent(&self, node: OpaqueNode) -> OffsetParentResponse { @@ -567,7 +568,7 @@ impl LayoutThread { image_cache: Arc, font_cache_thread: FontCacheThread, time_profiler_chan: profile_time::ProfilerChan, - webrender_api: WebrenderIpcSender, + webrender_api: WebRenderScriptApi, paint_time_metrics: PaintTimeMetrics, window_size: WindowSizeData, ) -> LayoutThread { diff --git a/components/layout_thread_2020/Cargo.toml b/components/layout_thread_2020/Cargo.toml index 633b903e509..6b96f358b2a 100644 --- a/components/layout_thread_2020/Cargo.toml +++ b/components/layout_thread_2020/Cargo.toml @@ -41,3 +41,4 @@ style = { workspace = true } style_traits = { workspace = true } url = { workspace = true } webrender_api = { workspace = true } +webrender_traits = { workspace = true } diff --git a/components/layout_thread_2020/lib.rs b/components/layout_thread_2020/lib.rs index 048a5d86216..4dd245a4925 100644 --- a/components/layout_thread_2020/lib.rs +++ b/components/layout_thread_2020/lib.rs @@ -55,7 +55,7 @@ use script_layout_interface::{ use script_traits::{ ConstellationControlMsg, DrawAPaintImageResult, IFrameSizeMsg, LayoutControlMsg, LayoutMsg as ConstellationMsg, PaintWorkletError, Painter, ScrollState, UntrustedNodeAddress, - WebrenderIpcSender, WindowSizeData, WindowSizeType, + WindowSizeData, WindowSizeType, }; use servo_arc::Arc as ServoArc; use servo_atoms::Atom; @@ -89,6 +89,7 @@ use style_traits::{CSSPixel, DevicePixel, SpeculativePainter}; use url::Url; use webrender_api::units::LayoutPixel; use webrender_api::{units, ExternalScrollId, HitTestFlags}; +use webrender_traits::WebRenderScriptApi; /// Information needed by layout. pub struct LayoutThread { @@ -158,7 +159,7 @@ pub struct LayoutThread { registered_painters: RegisteredPaintersImpl, /// Webrender interface. - webrender_api: WebrenderIpcSender, + webrender_api: WebRenderScriptApi, /// Paint time metrics. paint_time_metrics: PaintTimeMetrics, @@ -328,7 +329,7 @@ impl Layout for LayoutThread { .webrender_api .hit_test(Some(self.id.into()), client_point, flags); - results.iter().map(|result| result.node).collect() + results.iter().map(|result| result.node.into()).collect() } fn query_offset_parent(&self, node: OpaqueNode) -> OffsetParentResponse { @@ -484,7 +485,7 @@ impl LayoutThread { image_cache: Arc, font_cache_thread: FontCacheThread, time_profiler_chan: profile_time::ProfilerChan, - webrender_api_sender: WebrenderIpcSender, + webrender_api_sender: WebRenderScriptApi, paint_time_metrics: PaintTimeMetrics, window_size: WindowSizeData, ) -> LayoutThread { diff --git a/components/net/Cargo.toml b/components/net/Cargo.toml index bce568c034a..fb6c47209cb 100644 --- a/components/net/Cargo.toml +++ b/components/net/Cargo.toml @@ -65,6 +65,7 @@ tungstenite = { workspace = true } url = { workspace = true } uuid = { workspace = true } webrender_api = { workspace = true } +webrender_traits = { workspace = true } webpki-roots = { workspace = true } [dev-dependencies] diff --git a/components/net/image_cache.rs b/components/net/image_cache.rs index 9235c8d251e..2f0968f3aa4 100644 --- a/components/net/image_cache.rs +++ b/components/net/image_cache.rs @@ -12,19 +12,17 @@ use embedder_traits::resources::{self, Resource}; use imsz::imsz_from_reader; use ipc_channel::ipc::IpcSender; use log::{debug, warn}; -use net_traits::image::base::{load_from_memory, Image, ImageMetadata}; use net_traits::image_cache::{ - CorsStatus, ImageCache, ImageCacheResult, ImageOrMetadataAvailable, ImageResponder, - ImageResponse, PendingImageId, PendingImageResponse, UsePlaceholder, + ImageCache, ImageCacheResult, ImageOrMetadataAvailable, ImageResponder, ImageResponse, + PendingImageId, PendingImageResponse, UsePlaceholder, }; use net_traits::request::CorsSettings; -use net_traits::{ - FetchMetadata, FetchResponseMsg, FilteredMetadata, NetworkError, WebrenderIpcSender, -}; -use pixels::PixelFormat; +use net_traits::{FetchMetadata, FetchResponseMsg, FilteredMetadata, NetworkError}; +use pixels::{load_from_memory, CorsStatus, Image, ImageMetadata, PixelFormat}; use servo_url::{ImmutableOrigin, ServoUrl}; use webrender_api::units::DeviceIntSize; use webrender_api::{ImageData, ImageDescriptor, ImageDescriptorFlags, ImageFormat}; +use webrender_traits::WebRenderNetApi; use crate::resource_thread::CoreResourceThreadPool; @@ -47,13 +45,13 @@ fn decode_bytes_sync(key: LoadKey, bytes: &[u8], cors: CorsStatus) -> DecoderMsg DecoderMsg { key, image } } -fn get_placeholder_image(webrender_api: &WebrenderIpcSender, data: &[u8]) -> Arc { +fn get_placeholder_image(webrender_api: &WebRenderNetApi, data: &[u8]) -> Arc { let mut image = load_from_memory(data, CorsStatus::Unsafe).unwrap(); set_webrender_image_key(webrender_api, &mut image); Arc::new(image) } -fn set_webrender_image_key(webrender_api: &WebrenderIpcSender, image: &mut Image) { +fn set_webrender_image_key(webrender_api: &WebRenderNetApi, image: &mut Image) { if image.id.is_some() { return; } @@ -331,7 +329,7 @@ struct ImageCacheStore { placeholder_url: ServoUrl, // Webrender API instance. - webrender_api: WebrenderIpcSender, + webrender_api: WebRenderNetApi, } impl ImageCacheStore { @@ -418,7 +416,7 @@ pub struct ImageCacheImpl { } impl ImageCache for ImageCacheImpl { - fn new(webrender_api: WebrenderIpcSender) -> ImageCacheImpl { + fn new(webrender_api: WebRenderNetApi) -> ImageCacheImpl { debug!("New image cache"); let rippy_data = resources::read_bytes(Resource::RippyPNG); diff --git a/components/pixels/Cargo.toml b/components/pixels/Cargo.toml index f201ff7c0e4..3330ca1a34c 100644 --- a/components/pixels/Cargo.toml +++ b/components/pixels/Cargo.toml @@ -12,6 +12,10 @@ path = "lib.rs" [dependencies] euclid = { workspace = true } +ipc-channel = { workspace = true } +image = { workspace = true } malloc_size_of = { workspace = true } malloc_size_of_derive = { workspace = true } +log = { workspace = true } serde = { workspace = true, features = ["derive"] } +webrender_api = { workspace = true } diff --git a/components/pixels/lib.rs b/components/pixels/lib.rs index 2b26af3dd7c..bd2aea5b410 100644 --- a/components/pixels/lib.rs +++ b/components/pixels/lib.rs @@ -3,10 +3,15 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ use std::borrow::Cow; +use std::fmt; use euclid::default::{Point2D, Rect, Size2D}; +use image::ImageFormat; +use ipc_channel::ipc::IpcSharedMemory; +use log::debug; use malloc_size_of_derive::MallocSizeOf; use serde::{Deserialize, Serialize}; +use webrender_api::ImageKey; #[derive(Clone, Copy, Debug, Deserialize, Eq, MallocSizeOf, PartialEq, Serialize)] pub enum PixelFormat { @@ -98,3 +103,148 @@ pub fn clip( .intersection(&Rect::from_size(surface)) .filter(|rect| !rect.is_empty()) } + +/// Whether this response passed any CORS checks, and is thus safe to read from +/// in cross-origin environments. +#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)] +pub enum CorsStatus { + /// The response is either same-origin or cross-origin but passed CORS checks. + Safe, + /// The response is cross-origin and did not pass CORS checks. It is unsafe + /// to expose pixel data to the requesting environment. + Unsafe, +} + +#[derive(Clone, Deserialize, MallocSizeOf, Serialize)] +pub struct Image { + pub width: u32, + pub height: u32, + pub format: PixelFormat, + #[ignore_malloc_size_of = "Defined in ipc-channel"] + pub bytes: IpcSharedMemory, + #[ignore_malloc_size_of = "Defined in webrender_api"] + pub id: Option, + pub cors_status: CorsStatus, +} + +impl fmt::Debug for Image { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!( + f, + "Image {{ width: {}, height: {}, format: {:?}, ..., id: {:?} }}", + self.width, self.height, self.format, self.id + ) + } +} + +#[derive(Clone, Debug, Deserialize, Eq, MallocSizeOf, PartialEq, Serialize)] +pub struct ImageMetadata { + pub width: u32, + pub height: u32, +} + +// FIXME: Images must not be copied every frame. Instead we should atomically +// reference count them. + +pub fn load_from_memory(buffer: &[u8], cors_status: CorsStatus) -> Option { + if buffer.is_empty() { + return None; + } + + let image_fmt_result = detect_image_format(buffer); + match image_fmt_result { + Err(msg) => { + debug!("{}", msg); + None + }, + Ok(_) => match image::load_from_memory(buffer) { + Ok(image) => { + let mut rgba = image.into_rgba8(); + rgba8_byte_swap_colors_inplace(&mut rgba); + Some(Image { + width: rgba.width(), + height: rgba.height(), + format: PixelFormat::BGRA8, + bytes: IpcSharedMemory::from_bytes(&rgba), + id: None, + cors_status, + }) + }, + Err(e) => { + debug!("Image decoding error: {:?}", e); + None + }, + }, + } +} + +// https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img +pub fn detect_image_format(buffer: &[u8]) -> Result { + if is_gif(buffer) { + Ok(ImageFormat::Gif) + } else if is_jpeg(buffer) { + Ok(ImageFormat::Jpeg) + } else if is_png(buffer) { + Ok(ImageFormat::Png) + } else if is_webp(buffer) { + Ok(ImageFormat::WebP) + } else if is_bmp(buffer) { + Ok(ImageFormat::Bmp) + } else if is_ico(buffer) { + Ok(ImageFormat::Ico) + } else { + Err("Image Format Not Supported") + } +} + +fn is_gif(buffer: &[u8]) -> bool { + buffer.starts_with(b"GIF87a") || buffer.starts_with(b"GIF89a") +} + +fn is_jpeg(buffer: &[u8]) -> bool { + buffer.starts_with(&[0xff, 0xd8, 0xff]) +} + +fn is_png(buffer: &[u8]) -> bool { + buffer.starts_with(&[0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A]) +} + +fn is_bmp(buffer: &[u8]) -> bool { + buffer.starts_with(&[0x42, 0x4D]) +} + +fn is_ico(buffer: &[u8]) -> bool { + buffer.starts_with(&[0x00, 0x00, 0x01, 0x00]) +} + +fn is_webp(buffer: &[u8]) -> bool { + buffer.starts_with(b"RIFF") && buffer.len() >= 14 && &buffer[8..14] == b"WEBPVP" +} + +#[cfg(test)] +mod test { + use super::detect_image_format; + + #[test] + fn test_supported_images() { + let gif1 = [b'G', b'I', b'F', b'8', b'7', b'a']; + let gif2 = [b'G', b'I', b'F', b'8', b'9', b'a']; + let jpeg = [0xff, 0xd8, 0xff]; + let png = [0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A]; + let webp = [ + b'R', b'I', b'F', b'F', 0x01, 0x02, 0x03, 0x04, b'W', b'E', b'B', b'P', b'V', b'P', + ]; + let bmp = [0x42, 0x4D]; + let ico = [0x00, 0x00, 0x01, 0x00]; + let junk_format = [0x01, 0x02, 0x03, 0x04, 0x05]; + + assert!(detect_image_format(&gif1).is_ok()); + assert!(detect_image_format(&gif2).is_ok()); + assert!(detect_image_format(&jpeg).is_ok()); + assert!(detect_image_format(&png).is_ok()); + assert!(detect_image_format(&webp).is_ok()); + assert!(detect_image_format(&bmp).is_ok()); + assert!(detect_image_format(&ico).is_ok()); + assert!(detect_image_format(&junk_format).is_err()); + } +} diff --git a/components/script/Cargo.toml b/components/script/Cargo.toml index 45926d07f40..5cf7b65ba4d 100644 --- a/components/script/Cargo.toml +++ b/components/script/Cargo.toml @@ -116,6 +116,7 @@ uuid = { workspace = true, features = ["serde"] } webdriver = { workspace = true } webgpu = { path = "../webgpu" } webrender_api = { workspace = true } +webrender_traits = { workspace = true } webxr-api = { git = "https://github.com/servo/webxr", features = ["ipc"] } xml5ever = { workspace = true } diff --git a/components/script/dom/htmlimageelement.rs b/components/script/dom/htmlimageelement.rs index 96d4b1183cd..301c4e17b22 100644 --- a/components/script/dom/htmlimageelement.rs +++ b/components/script/dom/htmlimageelement.rs @@ -20,10 +20,9 @@ use ipc_channel::router::ROUTER; use js::jsapi::JSAutoRealm; use js::rust::HandleObject; use mime::{self, Mime}; -use net_traits::image::base::{Image, ImageMetadata}; use net_traits::image_cache::{ - CorsStatus, ImageCache, ImageCacheResult, ImageOrMetadataAvailable, ImageResponse, - PendingImageId, PendingImageResponse, UsePlaceholder, + ImageCache, ImageCacheResult, ImageOrMetadataAvailable, ImageResponse, PendingImageId, + PendingImageResponse, UsePlaceholder, }; use net_traits::request::{CorsSettings, Destination, Initiator, Referrer, RequestBuilder}; use net_traits::{ @@ -31,6 +30,7 @@ use net_traits::{ ResourceFetchTiming, ResourceTimingType, }; use num_traits::ToPrimitive; +use pixels::{CorsStatus, Image, ImageMetadata}; use servo_url::origin::{ImmutableOrigin, MutableOrigin}; use servo_url::ServoUrl; use style::attr::{ diff --git a/components/script/dom/htmlmediaelement.rs b/components/script/dom/htmlmediaelement.rs index f64ec676dc1..59f38e42c17 100644 --- a/components/script/dom/htmlmediaelement.rs +++ b/components/script/dom/htmlmediaelement.rs @@ -20,14 +20,13 @@ use ipc_channel::ipc; use ipc_channel::router::ROUTER; use js::jsapi::JSAutoRealm; use media::{glplayer_channel, GLPlayerMsg, GLPlayerMsgForward, WindowGLContext}; -use net_traits::image::base::Image; use net_traits::request::Destination; use net_traits::{ CoreResourceMsg, FetchChannels, FetchMetadata, FetchResponseListener, Metadata, NetworkError, ResourceFetchTiming, ResourceTimingType, }; +use pixels::Image; use script_layout_interface::HTMLMediaData; -use script_traits::{ImageUpdate, WebrenderIpcSender}; use servo_config::pref; use servo_media::player::audio::AudioRenderer; use servo_media::player::video::{VideoFrame, VideoFrameRenderer}; @@ -38,6 +37,7 @@ use webrender_api::{ ExternalImageData, ExternalImageId, ExternalImageType, ImageBufferKind, ImageData, ImageDescriptor, ImageDescriptorFlags, ImageFormat, ImageKey, }; +use webrender_traits::{ImageUpdate, WebRenderScriptApi}; use crate::document_loader::{LoadBlocker, LoadType}; use crate::dom::attr::Attr; @@ -154,7 +154,7 @@ impl FrameHolder { pub struct MediaFrameRenderer { player_id: Option, - api: WebrenderIpcSender, + api: WebRenderScriptApi, current_frame: Option<(ImageKey, i32, i32)>, old_frame: Option, very_old_frame: Option, @@ -163,7 +163,7 @@ pub struct MediaFrameRenderer { } impl MediaFrameRenderer { - fn new(render_api_sender: WebrenderIpcSender) -> Self { + fn new(render_api_sender: WebRenderScriptApi) -> Self { Self { player_id: None, api: render_api_sender, diff --git a/components/script/dom/htmlobjectelement.rs b/components/script/dom/htmlobjectelement.rs index 318b39ee06f..25794aca176 100755 --- a/components/script/dom/htmlobjectelement.rs +++ b/components/script/dom/htmlobjectelement.rs @@ -7,7 +7,7 @@ use std::default::Default; use dom_struct::dom_struct; use html5ever::{local_name, namespace_url, ns, LocalName, Prefix}; use js::rust::HandleObject; -use net_traits::image::base::Image; +use pixels::Image; use servo_arc::Arc; use crate::dom::attr::Attr; diff --git a/components/script/dom/node.rs b/components/script/dom/node.rs index 3531fb1b9f3..bbe09601d57 100644 --- a/components/script/dom/node.rs +++ b/components/script/dom/node.rs @@ -23,7 +23,7 @@ use js::jsapi::JSObject; use js::rust::HandleObject; use libc::{self, c_void, uintptr_t}; use malloc_size_of::{MallocSizeOf, MallocSizeOfOps}; -use net_traits::image::base::{Image, ImageMetadata}; +use pixels::{Image, ImageMetadata}; use script_layout_interface::{ GenericLayoutData, HTMLCanvasData, HTMLMediaData, LayoutElementType, LayoutNodeType, QueryMsg, SVGSVGData, StyleData, TrustedNodeAddress, diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs index b034c9c5d29..fcba7ffcbeb 100644 --- a/components/script/dom/window.rs +++ b/components/script/dom/window.rs @@ -56,7 +56,7 @@ use script_traits::webdriver_msg::{WebDriverJSError, WebDriverJSResult}; use script_traits::{ ConstellationControlMsg, DocumentState, HistoryEntryReplacement, LoadData, ScriptMsg, ScriptToConstellationChan, ScrollState, StructuredSerializedData, TimerEventId, - TimerSchedulerMsg, WebrenderIpcSender, WindowSizeData, WindowSizeType, + TimerSchedulerMsg, WindowSizeData, WindowSizeType, }; use selectors::attr::CaseSensitivity; use servo_arc::Arc as ServoArc; @@ -76,6 +76,7 @@ use style_traits::{CSSPixel, DevicePixel, ParsingMode}; use url::Position; use webrender_api::units::{DeviceIntPoint, DeviceIntSize, LayoutPixel}; use webrender_api::{DocumentId, ExternalScrollId}; +use webrender_traits::WebRenderScriptApi; use super::bindings::trace::HashMapTracedValues; use crate::dom::bindings::cell::{DomRefCell, Ref}; @@ -318,7 +319,7 @@ pub struct Window { /// Webrender API Sender #[ignore_malloc_size_of = "Wraps an IpcSender"] #[no_trace] - webrender_api_sender: WebrenderIpcSender, + webrender_api_sender: WebRenderScriptApi, /// Indicate whether a SetDocumentStatus message has been sent after a reflow is complete. /// It is used to avoid sending idle message more than once, which is unneccessary. @@ -521,7 +522,7 @@ impl Window { self.add_pending_reflow(); } - pub fn get_webrender_api_sender(&self) -> WebrenderIpcSender { + pub fn get_webrender_api_sender(&self) -> WebRenderScriptApi { self.webrender_api_sender.clone() } @@ -2537,7 +2538,7 @@ impl Window { webxr_registry: webxr_api::Registry, microtask_queue: Rc, webrender_document: DocumentId, - webrender_api_sender: WebrenderIpcSender, + webrender_api_sender: WebRenderScriptApi, relayout_event: bool, prepare_for_screenshot: bool, unminify_js: bool, diff --git a/components/script/layout_dom/node.rs b/components/script/layout_dom/node.rs index 7a521d36220..fdbe615513c 100644 --- a/components/script/layout_dom/node.rs +++ b/components/script/layout_dom/node.rs @@ -11,7 +11,7 @@ use std::sync::Arc as StdArc; use base::id::{BrowsingContextId, PipelineId}; use gfx_traits::ByteIndex; use html5ever::{local_name, namespace_url, ns}; -use net_traits::image::base::{Image, ImageMetadata}; +use pixels::{Image, ImageMetadata}; use range::Range; use script_layout_interface::wrapper_traits::{ LayoutDataTrait, LayoutNode, PseudoElementType, ThreadSafeLayoutNode, diff --git a/components/script/script_thread.rs b/components/script/script_thread.rs index 7231dfc54dd..4c67071a7b0 100644 --- a/components/script/script_thread.rs +++ b/components/script/script_thread.rs @@ -83,7 +83,7 @@ use script_traits::{ LayoutMsg, LoadData, LoadOrigin, MediaSessionActionType, MouseButton, MouseEventType, NewLayoutInfo, Painter, ProgressiveWebMetricType, ScriptMsg, ScriptToConstellationChan, StructuredSerializedData, TimerSchedulerMsg, TouchEventType, TouchId, UntrustedNodeAddress, - UpdatePipelineIdReason, WebrenderIpcSender, WheelDelta, WindowSizeData, WindowSizeType, + UpdatePipelineIdReason, WheelDelta, WindowSizeData, WindowSizeType, }; use servo_atoms::Atom; use servo_config::opts; @@ -95,6 +95,7 @@ use url::Position; use webgpu::WebGPUMsg; use webrender_api::units::LayoutPixel; use webrender_api::DocumentId; +use webrender_traits::WebRenderScriptApi; use crate::document_loader::DocumentLoader; use crate::dom::bindings::cell::DomRefCell; @@ -674,7 +675,7 @@ pub struct ScriptThread { /// Webrender API sender. #[no_trace] - webrender_api_sender: WebrenderIpcSender, + webrender_api_sender: WebRenderScriptApi, /// Periodically print out on which events script threads spend their processing time. profile_script_events: bool, diff --git a/components/servo/lib.rs b/components/servo/lib.rs index fc15c830100..755538acf06 100644 --- a/components/servo/lib.rs +++ b/components/servo/lib.rs @@ -36,8 +36,7 @@ use compositing::webview::UnknownWebView; use compositing::windowing::{EmbedderEvent, EmbedderMethods, WindowMethods}; use compositing::{CompositeTarget, IOCompositor, InitialCompositorState, ShutdownState}; use compositing_traits::{ - CanvasToCompositorMsg, CompositorMsg, CompositorProxy, CompositorReceiver, ConstellationMsg, - FontToCompositorMsg, ForwardedToCompositorMsg, + CompositorMsg, CompositorProxy, CompositorReceiver, ConstellationMsg, ForwardedToCompositorMsg, }; #[cfg(all( not(target_os = "windows"), @@ -96,8 +95,8 @@ use webrender_api::{ NativeFontHandle, }; use webrender_traits::{ - WebRenderFontApi, WebrenderExternalImageHandlers, WebrenderExternalImageRegistry, - WebrenderImageHandlerType, + CanvasToCompositorMsg, FontToCompositorMsg, ImageUpdate, WebRenderFontApi, + WebrenderExternalImageHandlers, WebrenderExternalImageRegistry, WebrenderImageHandlerType, }; pub use { background_hang_monitor, base, bluetooth, bluetooth_traits, canvas, canvas_traits, compositing, @@ -1100,7 +1099,7 @@ impl canvas_paint_thread::WebrenderApi for CanvasWebrenderApi { ))); receiver.recv().ok() } - fn update_images(&self, updates: Vec) { + fn update_images(&self, updates: Vec) { self.0 .send(CompositorMsg::Forwarded(ForwardedToCompositorMsg::Canvas( CanvasToCompositorMsg::UpdateImages(updates), diff --git a/components/shared/compositing/Cargo.toml b/components/shared/compositing/Cargo.toml index aed9a0e3565..b454cae37fa 100644 --- a/components/shared/compositing/Cargo.toml +++ b/components/shared/compositing/Cargo.toml @@ -12,7 +12,6 @@ path = "lib.rs" [dependencies] base = { workspace = true } -canvas = { path = "../../canvas" } crossbeam-channel = { workspace = true } embedder_traits = { workspace = true } euclid = { workspace = true } @@ -20,8 +19,9 @@ gfx_traits = { workspace = true } ipc-channel = { workspace = true } keyboard-types = { workspace = true } log = { workspace = true } -net_traits = { workspace = true } +pixels = { path = '../../pixels' } script_traits = { workspace = true } servo_url = { path = "../../url" } style_traits = { workspace = true } webrender_api = { workspace = true } +webrender_traits = { workspace = true } diff --git a/components/shared/compositing/lib.rs b/components/shared/compositing/lib.rs index 34c390cca85..171b0676c2d 100644 --- a/components/shared/compositing/lib.rs +++ b/components/shared/compositing/lib.rs @@ -10,23 +10,20 @@ use std::fmt::{Debug, Error, Formatter}; use base::id::{PipelineId, TopLevelBrowsingContextId}; use base::Epoch; -use canvas::canvas_paint_thread::ImageUpdate; pub use constellation_msg::ConstellationMsg; use crossbeam_channel::{Receiver, Sender}; use embedder_traits::EventLoopWaker; use euclid::Rect; use ipc_channel::ipc::IpcSender; use log::warn; -use net_traits::image::base::Image; -use net_traits::NetToCompositorMsg; +use pixels::Image; use script_traits::{ AnimationState, ConstellationControlMsg, EventResult, MouseButton, MouseEventType, - ScriptToCompositorMsg, }; use style_traits::CSSPixel; use webrender_api::units::{DeviceIntPoint, DeviceIntSize, DeviceRect}; -use webrender_api::{ - self, FontInstanceFlags, FontInstanceKey, FontKey, ImageKey, NativeFontHandle, +use webrender_traits::{ + CanvasToCompositorMsg, FontToCompositorMsg, NetToCompositorMsg, ScriptToCompositorMsg, }; /// Sends messages to the compositor. @@ -140,17 +137,6 @@ pub struct CompositionPipeline { pub script_chan: IpcSender, } -pub enum FontToCompositorMsg { - AddFontInstance(FontKey, f32, FontInstanceFlags, Sender), - AddFont(Sender, u32, ipc_channel::ipc::IpcBytesReceiver), - AddSystemFont(Sender, NativeFontHandle), -} - -pub enum CanvasToCompositorMsg { - GenerateKey(Sender), - UpdateImages(Vec), -} - /// Messages forwarded by the Constellation to the Compositor. pub enum ForwardedToCompositorMsg { Layout(ScriptToCompositorMsg), diff --git a/components/shared/net/Cargo.toml b/components/shared/net/Cargo.toml index 73db752aba6..cf174100feb 100644 --- a/components/shared/net/Cargo.toml +++ b/components/shared/net/Cargo.toml @@ -39,3 +39,4 @@ servo_url = { path = "../../url" } url = { workspace = true } uuid = { workspace = true } webrender_api = { workspace = true } +webrender_traits = { workspace = true } diff --git a/components/shared/net/image/base.rs b/components/shared/net/image/base.rs deleted file mode 100644 index 66257012df2..00000000000 --- a/components/shared/net/image/base.rs +++ /dev/null @@ -1,121 +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/. */ - -use std::fmt; - -use image::ImageFormat; -use ipc_channel::ipc::IpcSharedMemory; -use log::debug; -use malloc_size_of_derive::MallocSizeOf; -use pixels::PixelFormat; -use serde::{Deserialize, Serialize}; -use webrender_api::ImageKey; - -use crate::image_cache::CorsStatus; - -#[derive(Clone, Deserialize, MallocSizeOf, Serialize)] -pub struct Image { - pub width: u32, - pub height: u32, - pub format: PixelFormat, - #[ignore_malloc_size_of = "Defined in ipc-channel"] - pub bytes: IpcSharedMemory, - #[ignore_malloc_size_of = "Defined in webrender_api"] - pub id: Option, - pub cors_status: CorsStatus, -} - -impl fmt::Debug for Image { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!( - f, - "Image {{ width: {}, height: {}, format: {:?}, ..., id: {:?} }}", - self.width, self.height, self.format, self.id - ) - } -} - -#[derive(Clone, Debug, Deserialize, Eq, MallocSizeOf, PartialEq, Serialize)] -pub struct ImageMetadata { - pub width: u32, - pub height: u32, -} - -// FIXME: Images must not be copied every frame. Instead we should atomically -// reference count them. - -pub fn load_from_memory(buffer: &[u8], cors_status: CorsStatus) -> Option { - if buffer.is_empty() { - return None; - } - - let image_fmt_result = detect_image_format(buffer); - match image_fmt_result { - Err(msg) => { - debug!("{}", msg); - None - }, - Ok(_) => match image::load_from_memory(buffer) { - Ok(image) => { - let mut rgba = image.into_rgba8(); - pixels::rgba8_byte_swap_colors_inplace(&mut rgba); - Some(Image { - width: rgba.width(), - height: rgba.height(), - format: PixelFormat::BGRA8, - bytes: IpcSharedMemory::from_bytes(&rgba), - id: None, - cors_status, - }) - }, - Err(e) => { - debug!("Image decoding error: {:?}", e); - None - }, - }, - } -} - -// https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img -pub fn detect_image_format(buffer: &[u8]) -> Result { - if is_gif(buffer) { - Ok(ImageFormat::Gif) - } else if is_jpeg(buffer) { - Ok(ImageFormat::Jpeg) - } else if is_png(buffer) { - Ok(ImageFormat::Png) - } else if is_webp(buffer) { - Ok(ImageFormat::WebP) - } else if is_bmp(buffer) { - Ok(ImageFormat::Bmp) - } else if is_ico(buffer) { - Ok(ImageFormat::Ico) - } else { - Err("Image Format Not Supported") - } -} - -fn is_gif(buffer: &[u8]) -> bool { - buffer.starts_with(b"GIF87a") || buffer.starts_with(b"GIF89a") -} - -fn is_jpeg(buffer: &[u8]) -> bool { - buffer.starts_with(&[0xff, 0xd8, 0xff]) -} - -fn is_png(buffer: &[u8]) -> bool { - buffer.starts_with(&[0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A]) -} - -fn is_bmp(buffer: &[u8]) -> bool { - buffer.starts_with(&[0x42, 0x4D]) -} - -fn is_ico(buffer: &[u8]) -> bool { - buffer.starts_with(&[0x00, 0x00, 0x01, 0x00]) -} - -fn is_webp(buffer: &[u8]) -> bool { - buffer.starts_with(b"RIFF") && buffer.len() >= 14 && &buffer[8..14] == b"WEBPVP" -} diff --git a/components/shared/net/image_cache.rs b/components/shared/net/image_cache.rs index d1087bace63..317fd1b66ef 100644 --- a/components/shared/net/image_cache.rs +++ b/components/shared/net/image_cache.rs @@ -7,12 +7,13 @@ use std::sync::Arc; use ipc_channel::ipc::IpcSender; use log::debug; use malloc_size_of_derive::MallocSizeOf; +use pixels::{Image, ImageMetadata}; use serde::{Deserialize, Serialize}; use servo_url::{ImmutableOrigin, ServoUrl}; +use webrender_traits::WebRenderNetApi; -use crate::image::base::{Image, ImageMetadata}; use crate::request::CorsSettings; -use crate::{FetchResponseMsg, WebrenderIpcSender}; +use crate::FetchResponseMsg; // ====================================================================== // Aux structs and enums. @@ -98,7 +99,7 @@ pub enum ImageCacheResult { } pub trait ImageCache: Sync + Send { - fn new(webrender_api: WebrenderIpcSender) -> Self + fn new(webrender_api: WebRenderNetApi) -> Self where Self: Sized; @@ -140,14 +141,3 @@ pub trait ImageCache: Sync + Send { /// Inform the image cache about a response for a pending request. fn notify_pending_response(&self, id: PendingImageId, action: FetchResponseMsg); } - -/// Whether this response passed any CORS checks, and is thus safe to read from -/// in cross-origin environments. -#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)] -pub enum CorsStatus { - /// The response is either same-origin or cross-origin but passed CORS checks. - Safe, - /// The response is cross-origin and did not pass CORS checks. It is unsafe - /// to expose pixel data to the requesting environment. - Unsafe, -} diff --git a/components/shared/net/lib.rs b/components/shared/net/lib.rs index 436ccf5d645..595d47c7b06 100644 --- a/components/shared/net/lib.rs +++ b/components/shared/net/lib.rs @@ -16,7 +16,6 @@ use ipc_channel::ipc::{self, IpcReceiver, IpcSender}; use ipc_channel::router::ROUTER; use ipc_channel::Error as IpcError; use lazy_static::lazy_static; -use log::warn; use malloc_size_of::malloc_size_of_is_0; use malloc_size_of_derive::MallocSizeOf; use mime::Mime; @@ -25,7 +24,6 @@ use rustls::Certificate; use serde::{Deserialize, Serialize}; use servo_rand::RngCore; use servo_url::{ImmutableOrigin, ServoUrl}; -use webrender_api::{ImageData, ImageDescriptor, ImageKey}; use crate::filemanager_thread::FileManagerThreadMsg; use crate::request::{Request, RequestBuilder}; @@ -41,15 +39,6 @@ pub mod request; pub mod response; pub mod storage_thread; -/// Image handling. -/// -/// It may be surprising that this goes in the network crate as opposed to the graphics crate. -/// However, image handling is generally very integrated with the network stack (especially where -/// caching is involved) and as a result it must live in here. -pub mod image { - pub mod base; -} - /// An implementation of the [Fetch specification](https://fetch.spec.whatwg.org/) pub mod fetch { pub mod headers; @@ -829,38 +818,6 @@ pub fn http_percent_encode(bytes: &[u8]) -> String { percent_encoding::percent_encode(bytes, HTTP_VALUE).to_string() } -#[derive(Deserialize, Serialize)] -pub enum NetToCompositorMsg { - AddImage(ImageKey, ImageDescriptor, ImageData), - GenerateImageKey(IpcSender), -} - -#[derive(Clone, Deserialize, Serialize)] -pub struct WebrenderIpcSender(IpcSender); - -impl WebrenderIpcSender { - pub fn new(sender: IpcSender) -> Self { - Self(sender) - } - - pub fn generate_image_key(&self) -> ImageKey { - let (sender, receiver) = ipc::channel().unwrap(); - self.0 - .send(NetToCompositorMsg::GenerateImageKey(sender)) - .expect("error sending image key generation"); - receiver.recv().expect("error receiving image key result") - } - - pub fn add_image(&self, key: ImageKey, descriptor: ImageDescriptor, data: ImageData) { - if let Err(e) = self - .0 - .send(NetToCompositorMsg::AddImage(key, descriptor, data)) - { - warn!("Error sending image update: {}", e); - } - } -} - lazy_static! { pub static ref PRIVILEGED_SECRET: u32 = servo_rand::ServoRng::default().next_u32(); } diff --git a/components/shared/net/tests/image.rs b/components/shared/net/tests/image.rs deleted file mode 100644 index a4963702b57..00000000000 --- a/components/shared/net/tests/image.rs +++ /dev/null @@ -1,28 +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/. */ - -use net_traits::image::base::detect_image_format; - -#[test] -fn test_supported_images() { - let gif1 = [b'G', b'I', b'F', b'8', b'7', b'a']; - let gif2 = [b'G', b'I', b'F', b'8', b'9', b'a']; - let jpeg = [0xff, 0xd8, 0xff]; - let png = [0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A]; - let webp = [ - b'R', b'I', b'F', b'F', 0x01, 0x02, 0x03, 0x04, b'W', b'E', b'B', b'P', b'V', b'P', - ]; - let bmp = [0x42, 0x4D]; - let ico = [0x00, 0x00, 0x01, 0x00]; - let junk_format = [0x01, 0x02, 0x03, 0x04, 0x05]; - - assert!(detect_image_format(&gif1).is_ok()); - assert!(detect_image_format(&gif2).is_ok()); - assert!(detect_image_format(&jpeg).is_ok()); - assert!(detect_image_format(&png).is_ok()); - assert!(detect_image_format(&webp).is_ok()); - assert!(detect_image_format(&bmp).is_ok()); - assert!(detect_image_format(&ico).is_ok()); - assert!(detect_image_format(&junk_format).is_err()); -} diff --git a/components/shared/script/Cargo.toml b/components/shared/script/Cargo.toml index f95a0ffdcc9..6b3a2384d41 100644 --- a/components/shared/script/Cargo.toml +++ b/components/shared/script/Cargo.toml @@ -44,4 +44,5 @@ uuid = { workspace = true } webdriver = { workspace = true } webgpu = { path = "../../webgpu" } webrender_api = { workspace = true } +webrender_traits = { workspace = true } webxr-api = { git = "https://github.com/servo/webxr", features = ["ipc"] } diff --git a/components/shared/script/compositor.rs b/components/shared/script/compositor.rs deleted file mode 100644 index e66c3bf8227..00000000000 --- a/components/shared/script/compositor.rs +++ /dev/null @@ -1,334 +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/. */ - -//! Defines data structures which are consumed by the Compositor. - -use embedder_traits::Cursor; -use serde::{Deserialize, Serialize}; -use webrender_api::units::{LayoutSize, LayoutVector2D}; -use webrender_api::{Epoch, ExternalScrollId, PipelineId, ScrollLocation, SpatialId}; - -/// The scroll sensitivity of a scroll node ie whether it can be scrolled due to input event and -/// script events or only script events. -#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)] -pub enum ScrollSensitivity { - /// This node can be scrolled by input and script events. - ScriptAndInputEvents, - /// This node can only be scrolled by script events. - Script, -} - -/// Information that Servo keeps alongside WebRender display items -/// in order to add more context to hit test results. -#[derive(Debug, Deserialize, Serialize)] -pub struct HitTestInfo { - /// The id of the node of this hit test item. - pub node: u64, - - /// The cursor of this node's hit test item. - pub cursor: Option, - - /// The id of the [ScrollTree] associated with this hit test item. - pub scroll_tree_node: ScrollTreeNodeId, -} - -/// An id for a ScrollTreeNode in the ScrollTree. This contains both the index -/// to the node in the tree's array of nodes as well as the corresponding SpatialId -/// for the SpatialNode in the WebRender display list. -#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize)] -pub struct ScrollTreeNodeId { - /// The index of this scroll tree node in the tree's array of nodes. - pub index: usize, - - /// The WebRender spatial id of this scroll tree node. - pub spatial_id: SpatialId, -} - -/// Data stored for nodes in the [ScrollTree] that actually scroll, -/// as opposed to reference frames and sticky nodes which do not. -#[derive(Debug, Deserialize, Serialize)] -pub struct ScrollableNodeInfo { - /// The external scroll id of this node, used to track - /// it between successive re-layouts. - pub external_id: ExternalScrollId, - - /// Amount that this `ScrollableNode` can scroll in both directions. - pub scrollable_size: LayoutSize, - - /// Whether this `ScrollableNode` is sensitive to input events. - pub scroll_sensitivity: ScrollSensitivity, - - /// The current offset of this scroll node. - pub offset: LayoutVector2D, -} - -#[derive(Debug, Deserialize, Serialize)] -/// A node in a tree of scroll nodes. This may either be a scrollable -/// node which responds to scroll events or a non-scrollable one. -pub struct ScrollTreeNode { - /// The index of the parent of this node in the tree. If this is - /// None then this is the root node. - pub parent: Option, - - /// Scrolling data which will not be None if this is a scrolling node. - pub scroll_info: Option, -} - -impl ScrollTreeNode { - /// Get the external id of this node. - pub fn external_id(&self) -> Option { - self.scroll_info.as_ref().map(|info| info.external_id) - } - - /// Get the offset id of this node if it applies. - pub fn offset(&self) -> Option { - self.scroll_info.as_ref().map(|info| info.offset) - } - - /// Set the offset for this node, returns false if this was a - /// non-scrolling node for which you cannot set the offset. - pub fn set_offset(&mut self, new_offset: LayoutVector2D) -> bool { - match self.scroll_info { - Some(ref mut info) => { - info.offset = new_offset; - true - }, - _ => false, - } - } - - /// Scroll this node given a WebRender ScrollLocation. Returns a tuple that can - /// be used to scroll an individual WebRender scroll frame if the operation - /// actually changed an offset. - pub fn scroll( - &mut self, - scroll_location: ScrollLocation, - ) -> Option<(ExternalScrollId, LayoutVector2D)> { - let info = match self.scroll_info { - Some(ref mut data) => data, - None => return None, - }; - - if info.scroll_sensitivity != ScrollSensitivity::ScriptAndInputEvents { - return None; - } - - let delta = match scroll_location { - ScrollLocation::Delta(delta) => delta, - ScrollLocation::Start => { - if info.offset.y.round() >= 0.0 { - // Nothing to do on this layer. - return None; - } - - info.offset.y = 0.0; - return Some((info.external_id, info.offset)); - }, - ScrollLocation::End => { - let end_pos = -info.scrollable_size.height; - if info.offset.y.round() <= end_pos { - // Nothing to do on this layer. - return None; - } - - info.offset.y = end_pos; - return Some((info.external_id, info.offset)); - }, - }; - - let scrollable_width = info.scrollable_size.width; - let scrollable_height = info.scrollable_size.height; - let original_layer_scroll_offset = info.offset; - - if scrollable_width > 0. { - info.offset.x = (info.offset.x + delta.x).min(0.0).max(-scrollable_width); - } - - if scrollable_height > 0. { - info.offset.y = (info.offset.y + delta.y).min(0.0).max(-scrollable_height); - } - - if info.offset != original_layer_scroll_offset { - Some((info.external_id, info.offset)) - } else { - None - } - } -} - -/// A tree of spatial nodes, which mirrors the spatial nodes in the WebRender -/// display list, except these are used to scrolling in the compositor so that -/// new offsets can be sent to WebRender. -#[derive(Debug, Default, Deserialize, Serialize)] -pub struct ScrollTree { - /// A list of compositor-side scroll nodes that describe the tree - /// of WebRender spatial nodes, used by the compositor to scroll the - /// contents of the display list. - pub nodes: Vec, -} - -impl ScrollTree { - /// Add a scroll node to this ScrollTree returning the id of the new node. - pub fn add_scroll_tree_node( - &mut self, - parent: Option<&ScrollTreeNodeId>, - spatial_id: SpatialId, - scroll_info: Option, - ) -> ScrollTreeNodeId { - self.nodes.push(ScrollTreeNode { - parent: parent.cloned(), - scroll_info, - }); - ScrollTreeNodeId { - index: self.nodes.len() - 1, - spatial_id, - } - } - - /// Get a mutable reference to the node with the given index. - pub fn get_node_mut(&mut self, id: &ScrollTreeNodeId) -> &mut ScrollTreeNode { - &mut self.nodes[id.index] - } - - /// Get an immutable reference to the node with the given index. - pub fn get_node(&mut self, id: &ScrollTreeNodeId) -> &ScrollTreeNode { - &self.nodes[id.index] - } - - /// Scroll the given scroll node on this scroll tree. If the node cannot be scrolled, - /// because it isn't a scrollable node or it's already scrolled to the maximum scroll - /// extent, try to scroll an ancestor of this node. Returns the node scrolled and the - /// new offset if a scroll was performed, otherwise returns None. - pub fn scroll_node_or_ancestor( - &mut self, - scroll_node_id: &ScrollTreeNodeId, - scroll_location: ScrollLocation, - ) -> Option<(ExternalScrollId, LayoutVector2D)> { - let parent = { - let node = &mut self.get_node_mut(scroll_node_id); - let result = node.scroll(scroll_location); - if result.is_some() { - return result; - } - node.parent - }; - - parent.and_then(|parent| self.scroll_node_or_ancestor(&parent, scroll_location)) - } - - /// Given an [`ExternalScrollId`] and an offset, update the scroll offset of the scroll node - /// with the given id. - pub fn set_scroll_offsets_for_node_with_external_scroll_id( - &mut self, - external_scroll_id: ExternalScrollId, - offset: LayoutVector2D, - ) -> bool { - for node in self.nodes.iter_mut() { - match node.scroll_info { - Some(ref mut scroll_info) if scroll_info.external_id == external_scroll_id => { - scroll_info.offset = offset; - return true; - }, - _ => {}, - } - } - false - } -} - -/// A data structure which stores compositor-side information about -/// display lists sent to the compositor. -#[derive(Debug, Deserialize, Serialize)] -pub struct CompositorDisplayListInfo { - /// The WebRender [PipelineId] of this display list. - pub pipeline_id: PipelineId, - - /// The size of the viewport that this display list renders into. - pub viewport_size: LayoutSize, - - /// The size of this display list's content. - pub content_size: LayoutSize, - - /// The epoch of the display list. - pub epoch: Epoch, - - /// An array of `HitTestInfo` which is used to store information - /// to assist the compositor to take various actions (set the cursor, - /// scroll without layout) using a WebRender hit test result. - pub hit_test_info: Vec, - - /// A ScrollTree used by the compositor to scroll the contents of the - /// display list. - pub scroll_tree: ScrollTree, - - /// The `ScrollTreeNodeId` of the root reference frame of this info's scroll - /// tree. - pub root_reference_frame_id: ScrollTreeNodeId, - - /// The `ScrollTreeNodeId` of the topmost scrolling frame of this info's scroll - /// tree. - pub root_scroll_node_id: ScrollTreeNodeId, -} - -impl CompositorDisplayListInfo { - /// Create a new CompositorDisplayListInfo with the root reference frame - /// and scroll frame already added to the scroll tree. - pub fn new( - viewport_size: LayoutSize, - content_size: LayoutSize, - pipeline_id: PipelineId, - epoch: Epoch, - root_scroll_sensitivity: ScrollSensitivity, - ) -> Self { - let mut scroll_tree = ScrollTree::default(); - let root_reference_frame_id = scroll_tree.add_scroll_tree_node( - None, - SpatialId::root_reference_frame(pipeline_id), - None, - ); - let root_scroll_node_id = scroll_tree.add_scroll_tree_node( - Some(&root_reference_frame_id), - SpatialId::root_scroll_node(pipeline_id), - Some(ScrollableNodeInfo { - external_id: ExternalScrollId(0, pipeline_id), - scrollable_size: content_size - viewport_size, - scroll_sensitivity: root_scroll_sensitivity, - offset: LayoutVector2D::zero(), - }), - ); - - CompositorDisplayListInfo { - pipeline_id, - viewport_size, - content_size, - epoch, - hit_test_info: Default::default(), - scroll_tree, - root_reference_frame_id, - root_scroll_node_id, - } - } - - /// Add or re-use a duplicate HitTestInfo entry in this `CompositorHitTestInfo` - /// and return the index. - pub fn add_hit_test_info( - &mut self, - node: u64, - cursor: Option, - scroll_tree_node: ScrollTreeNodeId, - ) -> usize { - if let Some(last) = self.hit_test_info.last() { - if node == last.node && cursor == last.cursor { - return self.hit_test_info.len() - 1; - } - } - - self.hit_test_info.push(HitTestInfo { - node, - cursor, - scroll_tree_node, - }); - self.hit_test_info.len() - 1 - } -} diff --git a/components/shared/script/lib.rs b/components/shared/script/lib.rs index 2f9822d2711..16f1e07710d 100644 --- a/components/shared/script/lib.rs +++ b/components/shared/script/lib.rs @@ -9,7 +9,6 @@ #![deny(missing_docs)] #![deny(unsafe_code)] -pub mod compositor; mod script_msg; pub mod serializable; pub mod transferable; @@ -29,14 +28,13 @@ use base::Epoch; use bitflags::bitflags; use bluetooth_traits::BluetoothRequest; use canvas_traits::webgl::WebGLPipeline; -use compositor::ScrollTreeNodeId; use crossbeam_channel::{RecvTimeoutError, Sender}; use devtools_traits::{DevtoolScriptControlMsg, ScriptToDevtoolsControlMsg, WorkerId}; -use embedder_traits::{CompositorEventVariant, Cursor}; +use embedder_traits::CompositorEventVariant; use euclid::default::Point2D; use euclid::{Length, Rect, Scale, Size2D, UnknownUnit, Vector2D}; use http::{HeaderMap, Method}; -use ipc_channel::ipc::{self, IpcReceiver, IpcSender}; +use ipc_channel::ipc::{IpcReceiver, IpcSender}; use ipc_channel::Error as IpcError; use keyboard_types::webdriver::Event as WebDriverInputEvent; use keyboard_types::{CompositionEvent, KeyboardEvent}; @@ -45,25 +43,21 @@ use log::warn; use malloc_size_of::malloc_size_of_is_0; use malloc_size_of_derive::MallocSizeOf; use media::WindowGLContext; -use net_traits::image::base::Image; use net_traits::image_cache::ImageCache; use net_traits::request::{Referrer, RequestBody}; use net_traits::storage_thread::StorageType; use net_traits::{FetchResponseMsg, ReferrerPolicy, ResourceThreads}; -use pixels::PixelFormat; +use pixels::{Image, PixelFormat}; use profile_traits::{mem, time as profile_time}; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use servo_atoms::Atom; use servo_url::{ImmutableOrigin, ServoUrl}; use style_traits::{CSSPixel, SpeculativePainter}; use webgpu::WebGPUMsg; -use webrender_api::units::{DeviceIntSize, DevicePixel, DevicePoint, LayoutPixel, LayoutPoint}; -use webrender_api::{ - BuiltDisplayList, BuiltDisplayListDescriptor, DocumentId, ExternalImageData, ExternalScrollId, - HitTestFlags, ImageData, ImageDescriptor, ImageKey, PipelineId as WebRenderPipelineId, -}; +use webrender_api::units::{DeviceIntSize, DevicePixel, LayoutPixel}; +use webrender_api::{DocumentId, ExternalScrollId, ImageKey}; +use webrender_traits::{UntrustedNodeAddress as WebRenderUntrustedNodeAddress, WebRenderScriptApi}; -use crate::compositor::CompositorDisplayListInfo; pub use crate::script_msg::{ DOMMessage, EventResult, HistoryEntryReplacement, IFrameSizeMsg, Job, JobError, JobResult, JobResultValue, JobType, LayoutMsg, LogEntry, SWManagerMsg, SWManagerSenders, ScopeThings, @@ -83,6 +77,12 @@ malloc_size_of_is_0!(UntrustedNodeAddress); #[allow(unsafe_code)] unsafe impl Send for UntrustedNodeAddress {} +impl From for UntrustedNodeAddress { + fn from(o: WebRenderUntrustedNodeAddress) -> Self { + UntrustedNodeAddress(o.0 as *const c_void) + } +} + impl From for UntrustedNodeAddress { fn from(o: style_traits::dom::OpaqueNode) -> Self { UntrustedNodeAddress(o.0 as *const c_void) @@ -691,7 +691,7 @@ pub struct InitialScriptState { /// The Webrender document ID associated with this thread. pub webrender_document: DocumentId, /// FIXME(victor): The Webrender API sender in this constellation's pipeline - pub webrender_api_sender: WebrenderIpcSender, + pub webrender_api_sender: WebRenderScriptApi, /// Application window's GL Context for Media player pub player_context: WindowGLContext, } @@ -1093,239 +1093,6 @@ impl From for MediaSessionActionType { } } -/// The result of a hit test in the compositor. -#[derive(Clone, Debug, Deserialize, Serialize)] -pub struct CompositorHitTestResult { - /// The pipeline id of the resulting item. - pub pipeline_id: PipelineId, - - /// The hit test point in the item's viewport. - pub point_in_viewport: euclid::default::Point2D, - - /// The hit test point relative to the item itself. - pub point_relative_to_item: euclid::default::Point2D, - - /// The node address of the hit test result. - pub node: UntrustedNodeAddress, - - /// The cursor that should be used when hovering the item hit by the hit test. - pub cursor: Option, - - /// The scroll tree node associated with this hit test item. - pub scroll_tree_node: ScrollTreeNodeId, -} - -/// The set of WebRender operations that can be initiated by the content process. -#[derive(Deserialize, Serialize)] -pub enum ScriptToCompositorMsg { - /// Inform WebRender of the existence of this pipeline. - SendInitialTransaction(WebRenderPipelineId), - /// Perform a scroll operation. - SendScrollNode(WebRenderPipelineId, LayoutPoint, ExternalScrollId), - /// Inform WebRender of a new display list for the given pipeline. - SendDisplayList { - /// The [CompositorDisplayListInfo] that describes the display list being sent. - display_list_info: CompositorDisplayListInfo, - /// A descriptor of this display list used to construct this display list from raw data. - display_list_descriptor: BuiltDisplayListDescriptor, - /// An [ipc::IpcBytesReceiver] used to send the raw data of the display list. - display_list_receiver: ipc::IpcBytesReceiver, - }, - /// Perform a hit test operation. The result will be returned via - /// the provided channel sender. - HitTest( - Option, - DevicePoint, - HitTestFlags, - IpcSender>, - ), - /// Create a new image key. The result will be returned via the - /// provided channel sender. - GenerateImageKey(IpcSender), - /// Perform a resource update operation. - UpdateImages(Vec), -} - -#[derive(Clone, Deserialize, Serialize)] -/// A mechanism to communicate with the parent process' WebRender instance. -pub struct WebrenderIpcSender(IpcSender); - -impl WebrenderIpcSender { - /// Create a new WebrenderIpcSender object that wraps the provided channel sender. - pub fn new(sender: IpcSender) -> Self { - Self(sender) - } - - /// Inform WebRender of the existence of this pipeline. - pub fn send_initial_transaction(&self, pipeline: WebRenderPipelineId) { - if let Err(e) = self - .0 - .send(ScriptToCompositorMsg::SendInitialTransaction(pipeline)) - { - warn!("Error sending initial transaction: {}", e); - } - } - - /// Perform a scroll operation. - pub fn send_scroll_node( - &self, - pipeline_id: WebRenderPipelineId, - point: LayoutPoint, - scroll_id: ExternalScrollId, - ) { - if let Err(e) = self.0.send(ScriptToCompositorMsg::SendScrollNode( - pipeline_id, - point, - scroll_id, - )) { - warn!("Error sending scroll node: {}", e); - } - } - - /// Inform WebRender of a new display list for the given pipeline. - pub fn send_display_list( - &self, - display_list_info: CompositorDisplayListInfo, - list: BuiltDisplayList, - ) { - let (display_list_data, display_list_descriptor) = list.into_data(); - let (display_list_sender, display_list_receiver) = ipc::bytes_channel().unwrap(); - if let Err(e) = self.0.send(ScriptToCompositorMsg::SendDisplayList { - display_list_info, - display_list_descriptor, - display_list_receiver, - }) { - warn!("Error sending display list: {}", e); - } - - if let Err(error) = display_list_sender.send(&display_list_data.items_data) { - warn!("Error sending display list items: {}", error); - } - if let Err(error) = display_list_sender.send(&display_list_data.cache_data) { - warn!("Error sending display list cache data: {}", error); - } - if let Err(error) = display_list_sender.send(&display_list_data.spatial_tree) { - warn!("Error sending display spatial tree: {}", error); - } - } - - /// Perform a hit test operation. Blocks until the operation is complete and - /// and a result is available. - pub fn hit_test( - &self, - pipeline: Option, - point: DevicePoint, - flags: HitTestFlags, - ) -> Vec { - let (sender, receiver) = ipc::channel().unwrap(); - self.0 - .send(ScriptToCompositorMsg::HitTest( - pipeline, point, flags, sender, - )) - .expect("error sending hit test"); - receiver.recv().expect("error receiving hit test result") - } - - /// Create a new image key. Blocks until the key is available. - pub fn generate_image_key(&self) -> Option { - let (sender, receiver) = ipc::channel().unwrap(); - self.0 - .send(ScriptToCompositorMsg::GenerateImageKey(sender)) - .ok()?; - receiver.recv().ok() - } - - /// Perform a resource update operation. - pub fn update_images(&self, updates: Vec) { - let mut senders = Vec::new(); - // Convert `ImageUpdate` to `SerializedImageUpdate` because `ImageData` may contain large - // byes. With this conversion, we send `IpcBytesReceiver` instead and use it to send the - // actual bytes. - let updates = updates - .into_iter() - .map(|update| match update { - ImageUpdate::AddImage(k, d, data) => { - let data = match data { - ImageData::Raw(r) => { - let (sender, receiver) = ipc::bytes_channel().unwrap(); - senders.push((sender, r)); - SerializedImageData::Raw(receiver) - }, - ImageData::External(e) => SerializedImageData::External(e), - }; - SerializedImageUpdate::AddImage(k, d, data) - }, - ImageUpdate::DeleteImage(k) => SerializedImageUpdate::DeleteImage(k), - ImageUpdate::UpdateImage(k, d, data) => { - let data = match data { - ImageData::Raw(r) => { - let (sender, receiver) = ipc::bytes_channel().unwrap(); - senders.push((sender, r)); - SerializedImageData::Raw(receiver) - }, - ImageData::External(e) => SerializedImageData::External(e), - }; - SerializedImageUpdate::UpdateImage(k, d, data) - }, - }) - .collect(); - - if let Err(e) = self.0.send(ScriptToCompositorMsg::UpdateImages(updates)) { - warn!("error sending image updates: {}", e); - } - - senders.into_iter().for_each(|(tx, data)| { - if let Err(e) = tx.send(&data) { - warn!("error sending image data: {}", e); - } - }); - } -} - -#[derive(Deserialize, Serialize)] -/// Serializable image updates that must be performed by WebRender. -pub enum ImageUpdate { - /// Register a new image. - AddImage(ImageKey, ImageDescriptor, ImageData), - /// Delete a previously registered image registration. - DeleteImage(ImageKey), - /// Update an existing image registration. - UpdateImage(ImageKey, ImageDescriptor, ImageData), -} - -#[derive(Deserialize, Serialize)] -/// Serialized `ImageUpdate`. -pub enum SerializedImageUpdate { - /// Register a new image. - AddImage(ImageKey, ImageDescriptor, SerializedImageData), - /// Delete a previously registered image registration. - DeleteImage(ImageKey), - /// Update an existing image registration. - UpdateImage(ImageKey, ImageDescriptor, SerializedImageData), -} - -#[derive(Debug, Deserialize, Serialize)] -/// Serialized `ImageData`. It contains IPC byte channel receiver to prevent from loading bytes too -/// slow. -pub enum SerializedImageData { - /// A simple series of bytes, provided by the embedding and owned by WebRender. - /// The format is stored out-of-band, currently in ImageDescriptor. - Raw(ipc::IpcBytesReceiver), - /// An image owned by the embedding, and referenced by WebRender. This may - /// take the form of a texture or a heap-allocated buffer. - External(ExternalImageData), -} - -impl SerializedImageData { - /// Convert to ``ImageData`. - pub fn to_image_data(&self) -> Result { - match self { - SerializedImageData::Raw(rx) => rx.recv().map(ImageData::new), - SerializedImageData::External(image) => Ok(ImageData::External(*image)), - } - } -} - #[derive( Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, Ord, PartialEq, PartialOrd, Serialize, )] diff --git a/components/shared/script/tests/compositor.rs b/components/shared/script/tests/compositor.rs index 289f4ffebf1..ee3fd72ad49 100644 --- a/components/shared/script/tests/compositor.rs +++ b/components/shared/script/tests/compositor.rs @@ -3,11 +3,11 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ use euclid::Size2D; -use script_traits::compositor::{ - ScrollSensitivity, ScrollTree, ScrollTreeNodeId, ScrollableNodeInfo, -}; use webrender_api::units::LayoutVector2D; use webrender_api::{ExternalScrollId, PipelineId, ScrollLocation, SpatialId}; +use webrender_traits::display_list::{ + ScrollSensitivity, ScrollTree, ScrollTreeNodeId, ScrollableNodeInfo, +}; fn add_mock_scroll_node(tree: &mut ScrollTree) -> ScrollTreeNodeId { let pipeline_id = PipelineId(0, 0); diff --git a/components/shared/script_layout/Cargo.toml b/components/shared/script_layout/Cargo.toml index 57c4059b859..803a247bc2d 100644 --- a/components/shared/script_layout/Cargo.toml +++ b/components/shared/script_layout/Cargo.toml @@ -26,6 +26,7 @@ malloc_size_of = { workspace = true } malloc_size_of_derive = { workspace = true } metrics = { path = "../../metrics" } net_traits = { workspace = true } +pixels = { path = "../../pixels" } profile_traits = { workspace = true } range = { path = "../../range" } script_traits = { workspace = true } @@ -37,3 +38,4 @@ servo_url = { path = "../../url" } style = { workspace = true } style_traits = { workspace = true } webrender_api = { workspace = true } +webrender_traits = { workspace = true } diff --git a/components/shared/script_layout/lib.rs b/components/shared/script_layout/lib.rs index 7c1f4ee2162..d07a009af0e 100644 --- a/components/shared/script_layout/lib.rs +++ b/components/shared/script_layout/lib.rs @@ -33,7 +33,7 @@ use profile_traits::mem::Report; use profile_traits::time; use script_traits::{ ConstellationControlMsg, InitialScriptState, LayoutControlMsg, LayoutMsg, LoadData, Painter, - ScrollState, UntrustedNodeAddress, WebrenderIpcSender, WindowSizeData, + ScrollState, UntrustedNodeAddress, WindowSizeData, }; use serde::{Deserialize, Serialize}; use servo_arc::Arc as ServoArc; @@ -51,6 +51,7 @@ use style::stylesheets::Stylesheet; use style::Atom; use style_traits::CSSPixel; use webrender_api::ImageKey; +use webrender_traits::WebRenderScriptApi; pub type GenericLayoutData = dyn Any + Send + Sync; @@ -165,7 +166,7 @@ pub struct LayoutConfig { pub image_cache: Arc, pub font_cache_thread: FontCacheThread, pub time_profiler_chan: time::ProfilerChan, - pub webrender_api_sender: WebrenderIpcSender, + pub webrender_api_sender: WebRenderScriptApi, pub paint_time_metrics: PaintTimeMetrics, pub window_size: WindowSizeData, } diff --git a/components/shared/script_layout/wrapper_traits.rs b/components/shared/script_layout/wrapper_traits.rs index 0adb51be244..399c8bd3362 100644 --- a/components/shared/script_layout/wrapper_traits.rs +++ b/components/shared/script_layout/wrapper_traits.rs @@ -12,7 +12,7 @@ use atomic_refcell::AtomicRef; use base::id::{BrowsingContextId, PipelineId}; use gfx_traits::ByteIndex; use html5ever::{local_name, namespace_url, ns, LocalName, Namespace}; -use net_traits::image::base::{Image, ImageMetadata}; +use pixels::{Image, ImageMetadata}; use range::Range; use servo_arc::Arc; use servo_url::ServoUrl; diff --git a/components/shared/webrender/Cargo.toml b/components/shared/webrender/Cargo.toml index f078b96101d..158872b0494 100644 --- a/components/shared/webrender/Cargo.toml +++ b/components/shared/webrender/Cargo.toml @@ -11,5 +11,12 @@ name = "webrender_traits" path = "lib.rs" [dependencies] +base = { workspace = true } +crossbeam-channel = { workspace = true } +embedder_traits = { workspace = true } euclid = { workspace = true } +ipc-channel = { workspace = true } +log = { workspace = true } +libc = { workspace = true } webrender_api = { workspace = true } +serde = { workspace = true } diff --git a/components/shared/webrender/display_list.rs b/components/shared/webrender/display_list.rs new file mode 100644 index 00000000000..e66c3bf8227 --- /dev/null +++ b/components/shared/webrender/display_list.rs @@ -0,0 +1,334 @@ +/* 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/. */ + +//! Defines data structures which are consumed by the Compositor. + +use embedder_traits::Cursor; +use serde::{Deserialize, Serialize}; +use webrender_api::units::{LayoutSize, LayoutVector2D}; +use webrender_api::{Epoch, ExternalScrollId, PipelineId, ScrollLocation, SpatialId}; + +/// The scroll sensitivity of a scroll node ie whether it can be scrolled due to input event and +/// script events or only script events. +#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)] +pub enum ScrollSensitivity { + /// This node can be scrolled by input and script events. + ScriptAndInputEvents, + /// This node can only be scrolled by script events. + Script, +} + +/// Information that Servo keeps alongside WebRender display items +/// in order to add more context to hit test results. +#[derive(Debug, Deserialize, Serialize)] +pub struct HitTestInfo { + /// The id of the node of this hit test item. + pub node: u64, + + /// The cursor of this node's hit test item. + pub cursor: Option, + + /// The id of the [ScrollTree] associated with this hit test item. + pub scroll_tree_node: ScrollTreeNodeId, +} + +/// An id for a ScrollTreeNode in the ScrollTree. This contains both the index +/// to the node in the tree's array of nodes as well as the corresponding SpatialId +/// for the SpatialNode in the WebRender display list. +#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize)] +pub struct ScrollTreeNodeId { + /// The index of this scroll tree node in the tree's array of nodes. + pub index: usize, + + /// The WebRender spatial id of this scroll tree node. + pub spatial_id: SpatialId, +} + +/// Data stored for nodes in the [ScrollTree] that actually scroll, +/// as opposed to reference frames and sticky nodes which do not. +#[derive(Debug, Deserialize, Serialize)] +pub struct ScrollableNodeInfo { + /// The external scroll id of this node, used to track + /// it between successive re-layouts. + pub external_id: ExternalScrollId, + + /// Amount that this `ScrollableNode` can scroll in both directions. + pub scrollable_size: LayoutSize, + + /// Whether this `ScrollableNode` is sensitive to input events. + pub scroll_sensitivity: ScrollSensitivity, + + /// The current offset of this scroll node. + pub offset: LayoutVector2D, +} + +#[derive(Debug, Deserialize, Serialize)] +/// A node in a tree of scroll nodes. This may either be a scrollable +/// node which responds to scroll events or a non-scrollable one. +pub struct ScrollTreeNode { + /// The index of the parent of this node in the tree. If this is + /// None then this is the root node. + pub parent: Option, + + /// Scrolling data which will not be None if this is a scrolling node. + pub scroll_info: Option, +} + +impl ScrollTreeNode { + /// Get the external id of this node. + pub fn external_id(&self) -> Option { + self.scroll_info.as_ref().map(|info| info.external_id) + } + + /// Get the offset id of this node if it applies. + pub fn offset(&self) -> Option { + self.scroll_info.as_ref().map(|info| info.offset) + } + + /// Set the offset for this node, returns false if this was a + /// non-scrolling node for which you cannot set the offset. + pub fn set_offset(&mut self, new_offset: LayoutVector2D) -> bool { + match self.scroll_info { + Some(ref mut info) => { + info.offset = new_offset; + true + }, + _ => false, + } + } + + /// Scroll this node given a WebRender ScrollLocation. Returns a tuple that can + /// be used to scroll an individual WebRender scroll frame if the operation + /// actually changed an offset. + pub fn scroll( + &mut self, + scroll_location: ScrollLocation, + ) -> Option<(ExternalScrollId, LayoutVector2D)> { + let info = match self.scroll_info { + Some(ref mut data) => data, + None => return None, + }; + + if info.scroll_sensitivity != ScrollSensitivity::ScriptAndInputEvents { + return None; + } + + let delta = match scroll_location { + ScrollLocation::Delta(delta) => delta, + ScrollLocation::Start => { + if info.offset.y.round() >= 0.0 { + // Nothing to do on this layer. + return None; + } + + info.offset.y = 0.0; + return Some((info.external_id, info.offset)); + }, + ScrollLocation::End => { + let end_pos = -info.scrollable_size.height; + if info.offset.y.round() <= end_pos { + // Nothing to do on this layer. + return None; + } + + info.offset.y = end_pos; + return Some((info.external_id, info.offset)); + }, + }; + + let scrollable_width = info.scrollable_size.width; + let scrollable_height = info.scrollable_size.height; + let original_layer_scroll_offset = info.offset; + + if scrollable_width > 0. { + info.offset.x = (info.offset.x + delta.x).min(0.0).max(-scrollable_width); + } + + if scrollable_height > 0. { + info.offset.y = (info.offset.y + delta.y).min(0.0).max(-scrollable_height); + } + + if info.offset != original_layer_scroll_offset { + Some((info.external_id, info.offset)) + } else { + None + } + } +} + +/// A tree of spatial nodes, which mirrors the spatial nodes in the WebRender +/// display list, except these are used to scrolling in the compositor so that +/// new offsets can be sent to WebRender. +#[derive(Debug, Default, Deserialize, Serialize)] +pub struct ScrollTree { + /// A list of compositor-side scroll nodes that describe the tree + /// of WebRender spatial nodes, used by the compositor to scroll the + /// contents of the display list. + pub nodes: Vec, +} + +impl ScrollTree { + /// Add a scroll node to this ScrollTree returning the id of the new node. + pub fn add_scroll_tree_node( + &mut self, + parent: Option<&ScrollTreeNodeId>, + spatial_id: SpatialId, + scroll_info: Option, + ) -> ScrollTreeNodeId { + self.nodes.push(ScrollTreeNode { + parent: parent.cloned(), + scroll_info, + }); + ScrollTreeNodeId { + index: self.nodes.len() - 1, + spatial_id, + } + } + + /// Get a mutable reference to the node with the given index. + pub fn get_node_mut(&mut self, id: &ScrollTreeNodeId) -> &mut ScrollTreeNode { + &mut self.nodes[id.index] + } + + /// Get an immutable reference to the node with the given index. + pub fn get_node(&mut self, id: &ScrollTreeNodeId) -> &ScrollTreeNode { + &self.nodes[id.index] + } + + /// Scroll the given scroll node on this scroll tree. If the node cannot be scrolled, + /// because it isn't a scrollable node or it's already scrolled to the maximum scroll + /// extent, try to scroll an ancestor of this node. Returns the node scrolled and the + /// new offset if a scroll was performed, otherwise returns None. + pub fn scroll_node_or_ancestor( + &mut self, + scroll_node_id: &ScrollTreeNodeId, + scroll_location: ScrollLocation, + ) -> Option<(ExternalScrollId, LayoutVector2D)> { + let parent = { + let node = &mut self.get_node_mut(scroll_node_id); + let result = node.scroll(scroll_location); + if result.is_some() { + return result; + } + node.parent + }; + + parent.and_then(|parent| self.scroll_node_or_ancestor(&parent, scroll_location)) + } + + /// Given an [`ExternalScrollId`] and an offset, update the scroll offset of the scroll node + /// with the given id. + pub fn set_scroll_offsets_for_node_with_external_scroll_id( + &mut self, + external_scroll_id: ExternalScrollId, + offset: LayoutVector2D, + ) -> bool { + for node in self.nodes.iter_mut() { + match node.scroll_info { + Some(ref mut scroll_info) if scroll_info.external_id == external_scroll_id => { + scroll_info.offset = offset; + return true; + }, + _ => {}, + } + } + false + } +} + +/// A data structure which stores compositor-side information about +/// display lists sent to the compositor. +#[derive(Debug, Deserialize, Serialize)] +pub struct CompositorDisplayListInfo { + /// The WebRender [PipelineId] of this display list. + pub pipeline_id: PipelineId, + + /// The size of the viewport that this display list renders into. + pub viewport_size: LayoutSize, + + /// The size of this display list's content. + pub content_size: LayoutSize, + + /// The epoch of the display list. + pub epoch: Epoch, + + /// An array of `HitTestInfo` which is used to store information + /// to assist the compositor to take various actions (set the cursor, + /// scroll without layout) using a WebRender hit test result. + pub hit_test_info: Vec, + + /// A ScrollTree used by the compositor to scroll the contents of the + /// display list. + pub scroll_tree: ScrollTree, + + /// The `ScrollTreeNodeId` of the root reference frame of this info's scroll + /// tree. + pub root_reference_frame_id: ScrollTreeNodeId, + + /// The `ScrollTreeNodeId` of the topmost scrolling frame of this info's scroll + /// tree. + pub root_scroll_node_id: ScrollTreeNodeId, +} + +impl CompositorDisplayListInfo { + /// Create a new CompositorDisplayListInfo with the root reference frame + /// and scroll frame already added to the scroll tree. + pub fn new( + viewport_size: LayoutSize, + content_size: LayoutSize, + pipeline_id: PipelineId, + epoch: Epoch, + root_scroll_sensitivity: ScrollSensitivity, + ) -> Self { + let mut scroll_tree = ScrollTree::default(); + let root_reference_frame_id = scroll_tree.add_scroll_tree_node( + None, + SpatialId::root_reference_frame(pipeline_id), + None, + ); + let root_scroll_node_id = scroll_tree.add_scroll_tree_node( + Some(&root_reference_frame_id), + SpatialId::root_scroll_node(pipeline_id), + Some(ScrollableNodeInfo { + external_id: ExternalScrollId(0, pipeline_id), + scrollable_size: content_size - viewport_size, + scroll_sensitivity: root_scroll_sensitivity, + offset: LayoutVector2D::zero(), + }), + ); + + CompositorDisplayListInfo { + pipeline_id, + viewport_size, + content_size, + epoch, + hit_test_info: Default::default(), + scroll_tree, + root_reference_frame_id, + root_scroll_node_id, + } + } + + /// Add or re-use a duplicate HitTestInfo entry in this `CompositorHitTestInfo` + /// and return the index. + pub fn add_hit_test_info( + &mut self, + node: u64, + cursor: Option, + scroll_tree_node: ScrollTreeNodeId, + ) -> usize { + if let Some(last) = self.hit_test_info.last() { + if node == last.node && cursor == last.cursor { + return self.hit_test_info.len() - 1; + } + } + + self.hit_test_info.push(HitTestInfo { + node, + cursor, + scroll_tree_node, + }); + self.hit_test_info.len() - 1 + } +} diff --git a/components/shared/webrender/lib.rs b/components/shared/webrender/lib.rs index 8af3a17b609..d4883d150e4 100644 --- a/components/shared/webrender/lib.rs +++ b/components/shared/webrender/lib.rs @@ -4,14 +4,26 @@ #![deny(unsafe_code)] +pub mod display_list; + use std::collections::HashMap; use std::sync::{Arc, Mutex}; +use base::id::PipelineId; +use crossbeam_channel::Sender; +use display_list::{CompositorDisplayListInfo, ScrollTreeNodeId}; +use embedder_traits::Cursor; use euclid::default::Size2D; -use webrender_api::units::TexelRect; +use ipc_channel::ipc::{self, IpcBytesReceiver, IpcSender}; +use libc::c_void; +use log::warn; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; +use webrender_api::units::{DevicePoint, LayoutPoint, TexelRect}; use webrender_api::{ - ExternalImage, ExternalImageHandler, ExternalImageId, ExternalImageSource, FontInstanceFlags, - FontInstanceKey, FontKey, NativeFontHandle, + BuiltDisplayList, BuiltDisplayListDescriptor, ExternalImage, ExternalImageData, + ExternalImageHandler, ExternalImageId, ExternalImageSource, ExternalScrollId, + FontInstanceFlags, FontInstanceKey, FontKey, HitTestFlags, ImageData, ImageDescriptor, + ImageKey, NativeFontHandle, PipelineId as WebRenderPipelineId, }; /// This trait is used as a bridge between the different GL clients @@ -178,3 +190,309 @@ pub trait WebRenderFontApi { fn add_font(&self, data: Arc>, index: u32) -> FontKey; fn add_system_font(&self, handle: NativeFontHandle) -> FontKey; } + +pub enum CanvasToCompositorMsg { + GenerateKey(Sender), + UpdateImages(Vec), +} + +pub enum FontToCompositorMsg { + AddFontInstance(FontKey, f32, FontInstanceFlags, Sender), + AddFont(Sender, u32, IpcBytesReceiver), + AddSystemFont(Sender, NativeFontHandle), +} + +#[derive(Deserialize, Serialize)] +pub enum NetToCompositorMsg { + AddImage(ImageKey, ImageDescriptor, ImageData), + GenerateImageKey(IpcSender), +} + +/// The set of WebRender operations that can be initiated by the content process. +#[derive(Deserialize, Serialize)] +pub enum ScriptToCompositorMsg { + /// Inform WebRender of the existence of this pipeline. + SendInitialTransaction(WebRenderPipelineId), + /// Perform a scroll operation. + SendScrollNode(WebRenderPipelineId, LayoutPoint, ExternalScrollId), + /// Inform WebRender of a new display list for the given pipeline. + SendDisplayList { + /// The [CompositorDisplayListInfo] that describes the display list being sent. + display_list_info: CompositorDisplayListInfo, + /// A descriptor of this display list used to construct this display list from raw data. + display_list_descriptor: BuiltDisplayListDescriptor, + /// An [ipc::IpcBytesReceiver] used to send the raw data of the display list. + display_list_receiver: ipc::IpcBytesReceiver, + }, + /// Perform a hit test operation. The result will be returned via + /// the provided channel sender. + HitTest( + Option, + DevicePoint, + HitTestFlags, + IpcSender>, + ), + /// Create a new image key. The result will be returned via the + /// provided channel sender. + GenerateImageKey(IpcSender), + /// Perform a resource update operation. + UpdateImages(Vec), +} + +/// A mechanism to send messages from networking to the WebRender instance. +#[derive(Clone, Deserialize, Serialize)] +pub struct WebRenderNetApi(IpcSender); + +impl WebRenderNetApi { + pub fn new(sender: IpcSender) -> Self { + Self(sender) + } + + pub fn generate_image_key(&self) -> ImageKey { + let (sender, receiver) = ipc::channel().unwrap(); + self.0 + .send(NetToCompositorMsg::GenerateImageKey(sender)) + .expect("error sending image key generation"); + receiver.recv().expect("error receiving image key result") + } + + pub fn add_image(&self, key: ImageKey, descriptor: ImageDescriptor, data: ImageData) { + if let Err(e) = self + .0 + .send(NetToCompositorMsg::AddImage(key, descriptor, data)) + { + warn!("Error sending image update: {}", e); + } + } +} + +/// A mechanism to send messages from ScriptThread to the parent process' WebRender instance. +#[derive(Clone, Deserialize, Serialize)] +pub struct WebRenderScriptApi(IpcSender); + +impl WebRenderScriptApi { + /// Create a new WebrenderIpcSender object that wraps the provided channel sender. + pub fn new(sender: IpcSender) -> Self { + Self(sender) + } + + /// Inform WebRender of the existence of this pipeline. + pub fn send_initial_transaction(&self, pipeline: WebRenderPipelineId) { + if let Err(e) = self + .0 + .send(ScriptToCompositorMsg::SendInitialTransaction(pipeline)) + { + warn!("Error sending initial transaction: {}", e); + } + } + + /// Perform a scroll operation. + pub fn send_scroll_node( + &self, + pipeline_id: WebRenderPipelineId, + point: LayoutPoint, + scroll_id: ExternalScrollId, + ) { + if let Err(e) = self.0.send(ScriptToCompositorMsg::SendScrollNode( + pipeline_id, + point, + scroll_id, + )) { + warn!("Error sending scroll node: {}", e); + } + } + + /// Inform WebRender of a new display list for the given pipeline. + pub fn send_display_list( + &self, + display_list_info: CompositorDisplayListInfo, + list: BuiltDisplayList, + ) { + let (display_list_data, display_list_descriptor) = list.into_data(); + let (display_list_sender, display_list_receiver) = ipc::bytes_channel().unwrap(); + if let Err(e) = self.0.send(ScriptToCompositorMsg::SendDisplayList { + display_list_info, + display_list_descriptor, + display_list_receiver, + }) { + warn!("Error sending display list: {}", e); + } + + if let Err(error) = display_list_sender.send(&display_list_data.items_data) { + warn!("Error sending display list items: {}", error); + } + if let Err(error) = display_list_sender.send(&display_list_data.cache_data) { + warn!("Error sending display list cache data: {}", error); + } + if let Err(error) = display_list_sender.send(&display_list_data.spatial_tree) { + warn!("Error sending display spatial tree: {}", error); + } + } + + /// Perform a hit test operation. Blocks until the operation is complete and + /// and a result is available. + pub fn hit_test( + &self, + pipeline: Option, + point: DevicePoint, + flags: HitTestFlags, + ) -> Vec { + let (sender, receiver) = ipc::channel().unwrap(); + self.0 + .send(ScriptToCompositorMsg::HitTest( + pipeline, point, flags, sender, + )) + .expect("error sending hit test"); + receiver.recv().expect("error receiving hit test result") + } + + /// Create a new image key. Blocks until the key is available. + pub fn generate_image_key(&self) -> Option { + let (sender, receiver) = ipc::channel().unwrap(); + self.0 + .send(ScriptToCompositorMsg::GenerateImageKey(sender)) + .ok()?; + receiver.recv().ok() + } + + /// Perform a resource update operation. + pub fn update_images(&self, updates: Vec) { + let mut senders = Vec::new(); + // Convert `ImageUpdate` to `SerializedImageUpdate` because `ImageData` may contain large + // byes. With this conversion, we send `IpcBytesReceiver` instead and use it to send the + // actual bytes. + let updates = updates + .into_iter() + .map(|update| match update { + ImageUpdate::AddImage(k, d, data) => { + let data = match data { + ImageData::Raw(r) => { + let (sender, receiver) = ipc::bytes_channel().unwrap(); + senders.push((sender, r)); + SerializedImageData::Raw(receiver) + }, + ImageData::External(e) => SerializedImageData::External(e), + }; + SerializedImageUpdate::AddImage(k, d, data) + }, + ImageUpdate::DeleteImage(k) => SerializedImageUpdate::DeleteImage(k), + ImageUpdate::UpdateImage(k, d, data) => { + let data = match data { + ImageData::Raw(r) => { + let (sender, receiver) = ipc::bytes_channel().unwrap(); + senders.push((sender, r)); + SerializedImageData::Raw(receiver) + }, + ImageData::External(e) => SerializedImageData::External(e), + }; + SerializedImageUpdate::UpdateImage(k, d, data) + }, + }) + .collect(); + + if let Err(e) = self.0.send(ScriptToCompositorMsg::UpdateImages(updates)) { + warn!("error sending image updates: {}", e); + } + + senders.into_iter().for_each(|(tx, data)| { + if let Err(e) = tx.send(&data) { + warn!("error sending image data: {}", e); + } + }); + } +} + +#[derive(Deserialize, Serialize)] +/// Serializable image updates that must be performed by WebRender. +pub enum ImageUpdate { + /// Register a new image. + AddImage(ImageKey, ImageDescriptor, ImageData), + /// Delete a previously registered image registration. + DeleteImage(ImageKey), + /// Update an existing image registration. + UpdateImage(ImageKey, ImageDescriptor, ImageData), +} + +#[derive(Deserialize, Serialize)] +/// Serialized `ImageUpdate`. +pub enum SerializedImageUpdate { + /// Register a new image. + AddImage(ImageKey, ImageDescriptor, SerializedImageData), + /// Delete a previously registered image registration. + DeleteImage(ImageKey), + /// Update an existing image registration. + UpdateImage(ImageKey, ImageDescriptor, SerializedImageData), +} + +#[derive(Debug, Deserialize, Serialize)] +/// Serialized `ImageData`. It contains IPC byte channel receiver to prevent from loading bytes too +/// slow. +pub enum SerializedImageData { + /// A simple series of bytes, provided by the embedding and owned by WebRender. + /// The format is stored out-of-band, currently in ImageDescriptor. + Raw(ipc::IpcBytesReceiver), + /// An image owned by the embedding, and referenced by WebRender. This may + /// take the form of a texture or a heap-allocated buffer. + External(ExternalImageData), +} + +impl SerializedImageData { + /// Convert to ``ImageData`. + pub fn to_image_data(&self) -> Result { + match self { + SerializedImageData::Raw(rx) => rx.recv().map(ImageData::new), + SerializedImageData::External(image) => Ok(ImageData::External(*image)), + } + } +} + +/// The address of a node. Layout sends these back. They must be validated via +/// `from_untrusted_node_address` before they can be used, because we do not trust layout. +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub struct UntrustedNodeAddress(pub *const c_void); + +#[allow(unsafe_code)] +unsafe impl Send for UntrustedNodeAddress {} + +impl Serialize for UntrustedNodeAddress { + fn serialize(&self, s: S) -> Result { + (self.0 as usize).serialize(s) + } +} + +impl<'de> Deserialize<'de> for UntrustedNodeAddress { + fn deserialize>(d: D) -> Result { + let value: usize = Deserialize::deserialize(d)?; + Ok(UntrustedNodeAddress::from_id(value)) + } +} + +impl UntrustedNodeAddress { + /// Creates an `UntrustedNodeAddress` from the given pointer address value. + #[inline] + pub fn from_id(id: usize) -> UntrustedNodeAddress { + UntrustedNodeAddress(id as *const c_void) + } +} + +/// The result of a hit test in the compositor. +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct CompositorHitTestResult { + /// The pipeline id of the resulting item. + pub pipeline_id: PipelineId, + + /// The hit test point in the item's viewport. + pub point_in_viewport: euclid::default::Point2D, + + /// The hit test point relative to the item itself. + pub point_relative_to_item: euclid::default::Point2D, + + /// The node address of the hit test result. + pub node: UntrustedNodeAddress, + + /// The cursor that should be used when hovering the item hit by the hit test. + pub cursor: Option, + + /// The scroll tree node associated with this hit test item. + pub scroll_tree_node: ScrollTreeNodeId, +} diff --git a/python/servo/testing_commands.py b/python/servo/testing_commands.py index cf131f8b319..d6c4bea60e5 100644 --- a/python/servo/testing_commands.py +++ b/python/servo/testing_commands.py @@ -162,6 +162,7 @@ class MachCommands(CommandBase): "constellation", "style_config", "compositing", + "pixels", ] if not packages: packages = set(os.listdir(path.join(self.context.topdir, "tests", "unit"))) - set(['.DS_Store']) -- cgit v1.2.3