Skip to content

Commit 55b80ce

Browse files
committed
wip: svg serializing
1 parent 7ab0d91 commit 55b80ce

File tree

10 files changed

+130
-44
lines changed

10 files changed

+130
-44
lines changed

components/layout/context.rs

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
use std::sync::Arc;
66

7+
use embedder_traits::UntrustedNodeAddress;
78
use euclid::Size2D;
89
use fnv::FnvHashMap;
910
use fonts::FontContext;
@@ -19,7 +20,7 @@ use parking_lot::{Mutex, RwLock};
1920
use pixels::RasterImage;
2021
use servo_url::{ImmutableOrigin, ServoUrl};
2122
use style::context::SharedStyleContext;
22-
use style::dom::OpaqueNode;
23+
use style::dom::{OpaqueNode, TNode};
2324
use style::values::computed::image::{Gradient, Image};
2425
use webrender_api::units::{DeviceIntSize, DeviceSize};
2526

@@ -86,6 +87,7 @@ pub(crate) struct ImageResolver {
8687
/// size determined by layout. This will be shared with the script thread.
8788
pub pending_rasterization_images: Mutex<Vec<PendingRasterizationImage>>,
8889

90+
pub pending_svg_element_for_serialization: Mutex<Vec<UntrustedNodeAddress>>,
8991
/// A shared reference to script's map of DOM nodes with animated images. This is used
9092
/// to manage image animations in script and inform the script about newly animating
9193
/// nodes.
@@ -104,7 +106,8 @@ impl Drop for ImageResolver {
104106
fn drop(&mut self) {
105107
if !std::thread::panicking() {
106108
assert!(self.pending_images.lock().is_empty());
107-
assert!(self.pending_rasterization_images.lock().is_empty());
109+
assert!(self.pending_images.lock().is_empty());
110+
assert!(self.pending_svg_element_for_serialization.lock().is_empty());
108111
}
109112
}
110113
}
@@ -174,7 +177,7 @@ impl ImageResolver {
174177
}
175178
}
176179

177-
fn get_cached_image_for_url(
180+
pub(crate) fn get_cached_image_for_url(
178181
&self,
179182
node: OpaqueNode,
180183
url: ServoUrl,
@@ -234,6 +237,15 @@ impl ImageResolver {
234237
result
235238
}
236239

240+
pub(crate) fn queue_svg_element_for_serialization(
241+
&self,
242+
element: script::layout_dom::ServoLayoutNode<'_>,
243+
) {
244+
self.pending_svg_element_for_serialization
245+
.lock()
246+
.push(element.opaque().into())
247+
}
248+
237249
pub(crate) fn resolve_image<'a>(
238250
&self,
239251
node: Option<OpaqueNode>,

components/layout/dom.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ use layout_api::wrapper_traits::{
1212
LayoutDataTrait, LayoutNode, ThreadSafeLayoutElement, ThreadSafeLayoutNode,
1313
};
1414
use layout_api::{
15-
GenericLayoutDataTrait, LayoutDamage, LayoutElementType, LayoutNodeType as ScriptLayoutNodeType,
15+
GenericLayoutDataTrait, LayoutDamage, LayoutElementType,
16+
LayoutNodeType as ScriptLayoutNodeType, SVGElementData,
1617
};
1718
use malloc_size_of_derive::MallocSizeOf;
1819
use net_traits::image_cache::Image;
@@ -232,6 +233,7 @@ pub(crate) trait NodeExt<'dom> {
232233
fn as_canvas(&self) -> Option<(CanvasInfo, PhysicalSize<f64>)>;
233234
fn as_iframe(&self) -> Option<(PipelineId, BrowsingContextId)>;
234235
fn as_video(&self) -> Option<(Option<webrender_api::ImageKey>, Option<PhysicalSize<f64>>)>;
236+
fn as_svg(&self) -> Option<SVGElementData>;
235237
fn as_typeless_object_with_data_attribute(&self) -> Option<String>;
236238
fn style(&self, context: &SharedStyleContext) -> ServoArc<ComputedValues>;
237239

@@ -273,6 +275,11 @@ impl<'dom> NodeExt<'dom> for ServoLayoutNode<'dom> {
273275
Some((resource, PhysicalSize::new(width, height)))
274276
}
275277

278+
fn as_svg(&self) -> Option<SVGElementData> {
279+
let node = self.to_threadsafe();
280+
node.svg_data()
281+
}
282+
276283
fn as_video(&self) -> Option<(Option<webrender_api::ImageKey>, Option<PhysicalSize<f64>>)> {
277284
let node = self.to_threadsafe();
278285
let data = node.media_data()?;

components/layout/layout_impl.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -660,6 +660,7 @@ impl LayoutThread {
660660
resolved_images_cache: self.resolved_images_cache.clone(),
661661
pending_images: Mutex::default(),
662662
pending_rasterization_images: Mutex::default(),
663+
pending_svg_element_for_serialization: Mutex::default(),
663664
node_to_animating_image_map: reflow_request.node_to_animating_image_map.clone(),
664665
animation_timeline_value: reflow_request.animation_timeline_value,
665666
});
@@ -682,11 +683,14 @@ impl LayoutThread {
682683
let pending_images = std::mem::take(&mut *image_resolver.pending_images.lock());
683684
let pending_rasterization_images =
684685
std::mem::take(&mut *image_resolver.pending_rasterization_images.lock());
686+
let pending_svg_element_for_serialization =
687+
std::mem::take(&mut *image_resolver.pending_svg_element_for_serialization.lock());
685688

686689
Some(ReflowResult {
687690
built_display_list,
688691
pending_images,
689692
pending_rasterization_images,
693+
pending_svg_element_for_serialization,
690694
iframe_sizes,
691695
})
692696
}

components/layout/replaced.rs

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use embedder_traits::ViewportDetails;
99
use euclid::{Scale, Size2D};
1010
use layout_api::IFrameSize;
1111
use malloc_size_of_derive::MallocSizeOf;
12-
use net_traits::image_cache::{Image, ImageOrMetadataAvailable, UsePlaceholder};
12+
use net_traits::image_cache::{Image, ImageOrMetadataAvailable, UsePlaceholder, VectorImage};
1313
use script::layout_dom::ServoLayoutNode;
1414
use servo_arc::Arc as ServoArc;
1515
use style::Zero;
@@ -115,6 +115,7 @@ pub(crate) enum ReplacedContentKind {
115115
IFrame(IFrameInfo),
116116
Canvas(CanvasInfo),
117117
Video(Option<VideoInfo>),
118+
SVGElement(VectorImage),
118119
}
119120

120121
impl ReplacedContents {
@@ -153,6 +154,29 @@ impl ReplacedContents {
153154
ReplacedContentKind::Video(image_key.map(|key| VideoInfo { image_key: key })),
154155
natural_size_in_dots,
155156
)
157+
} else if let Some(svg_data) = element.as_svg() {
158+
let Some(svg_source) = svg_data.source else {
159+
context
160+
.image_resolver
161+
.queue_svg_element_for_serialization(element);
162+
return None;
163+
};
164+
let result = context
165+
.image_resolver
166+
.get_cached_image_for_url(element.opaque(), svg_source, UsePlaceholder::No)
167+
.ok()?;
168+
169+
let Image::Vector(vector_image) = result else {
170+
unreachable!("SVG Element can't contain a raster image.")
171+
};
172+
let physical_size = PhysicalSize::new(
173+
vector_image.metadata.width as f64,
174+
vector_image.metadata.height as f64,
175+
);
176+
(
177+
ReplacedContentKind::SVGElement(vector_image),
178+
Some(physical_size),
179+
)
156180
} else {
157181
return None;
158182
}
@@ -390,6 +414,28 @@ impl ReplacedContents {
390414
image_key: Some(image_key),
391415
}))]
392416
},
417+
ReplacedContentKind::SVGElement(vector_image) => {
418+
let scale = layout_context.style_context.device_pixel_ratio();
419+
let width = object_fit_size.width.scale_by(scale.0).to_px();
420+
let height = object_fit_size.height.scale_by(scale.0).to_px();
421+
let size = Size2D::new(width, height);
422+
let tag = self.base_fragment_info.tag.unwrap();
423+
layout_context
424+
.image_resolver
425+
.rasterize_vector_image(vector_image.id, size, tag.node)
426+
.and_then(|i| i.id)
427+
.map(|image_key| {
428+
Fragment::Image(ArcRefCell::new(ImageFragment {
429+
base: self.base_fragment_info.into(),
430+
style: style.clone(),
431+
rect,
432+
clip,
433+
image_key: Some(image_key),
434+
}))
435+
})
436+
.into_iter()
437+
.collect()
438+
},
393439
}
394440
}
395441

components/script/dom/node.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ use js::rust::HandleObject;
2727
use keyboard_types::Modifiers;
2828
use layout_api::{
2929
GenericLayoutData, HTMLCanvasData, HTMLMediaData, LayoutElementType, LayoutNodeType, QueryMsg,
30-
SVGSVGData, StyleData, TrustedNodeAddress,
30+
SVGElementData, StyleData, TrustedNodeAddress,
3131
};
3232
use libc::{self, c_void, uintptr_t};
3333
use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
@@ -1667,7 +1667,7 @@ pub(crate) trait LayoutNodeHelpers<'dom> {
16671667
fn image_data(self) -> Option<(Option<Image>, Option<ImageMetadata>)>;
16681668
fn canvas_data(self) -> Option<HTMLCanvasData>;
16691669
fn media_data(self) -> Option<HTMLMediaData>;
1670-
fn svg_data(self) -> Option<SVGSVGData>;
1670+
fn svg_data(self) -> Option<SVGElementData>;
16711671
fn iframe_browsing_context_id(self) -> Option<BrowsingContextId>;
16721672
fn iframe_pipeline_id(self) -> Option<PipelineId>;
16731673
fn opaque(self) -> OpaqueNode;
@@ -1907,7 +1907,7 @@ impl<'dom> LayoutNodeHelpers<'dom> for LayoutDom<'dom, Node> {
19071907
.map(|media| media.data())
19081908
}
19091909

1910-
fn svg_data(self) -> Option<SVGSVGData> {
1910+
fn svg_data(self) -> Option<SVGElementData> {
19111911
self.downcast::<SVGSVGElement>().map(|svg| svg.data())
19121912
}
19131913

components/script/dom/svgsvgelement.rs

Lines changed: 33 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -2,29 +2,31 @@
22
* License, v. 2.0. If a copy of the MPL was not distributed with this
33
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
44

5+
use std::cell::RefCell;
6+
7+
use base64::Engine as _;
58
use dom_struct::dom_struct;
6-
use html5ever::{LocalName, Prefix, local_name, ns};
9+
use html5ever::{LocalName, Prefix};
710
use js::rust::HandleObject;
8-
use layout_api::SVGSVGData;
9-
use style::attr::AttrValue;
11+
use layout_api::SVGElementData;
12+
use servo_url::ServoUrl;
13+
use xml5ever::serialize::TraversalScope;
1014

1115
use crate::dom::attr::Attr;
1216
use crate::dom::bindings::inheritance::Castable;
1317
use crate::dom::bindings::root::{DomRoot, LayoutDom};
14-
use crate::dom::bindings::str::DOMString;
1518
use crate::dom::document::Document;
16-
use crate::dom::element::{AttributeMutation, Element, LayoutElementHelpers};
19+
use crate::dom::element::AttributeMutation;
1720
use crate::dom::node::Node;
1821
use crate::dom::svggraphicselement::SVGGraphicsElement;
1922
use crate::dom::virtualmethods::VirtualMethods;
2023
use crate::script_runtime::CanGc;
2124

22-
const DEFAULT_WIDTH: u32 = 300;
23-
const DEFAULT_HEIGHT: u32 = 150;
24-
2525
#[dom_struct]
2626
pub(crate) struct SVGSVGElement {
2727
svggraphicselement: SVGGraphicsElement,
28+
#[no_trace]
29+
cached_serialized_data: RefCell<Option<ServoUrl>>,
2830
}
2931

3032
impl SVGSVGElement {
@@ -35,6 +37,7 @@ impl SVGSVGElement {
3537
) -> SVGSVGElement {
3638
SVGSVGElement {
3739
svggraphicselement: SVGGraphicsElement::new_inherited(local_name, prefix, document),
40+
cached_serialized_data: Default::default(),
3841
}
3942
}
4043

@@ -53,23 +56,28 @@ impl SVGSVGElement {
5356
can_gc,
5457
)
5558
}
59+
60+
pub(crate) fn cache_serialized_data(&self) {
61+
let source: String = self
62+
.upcast::<Node>()
63+
.xml_serialize(TraversalScope::IncludeNode)
64+
.into();
65+
let base64 = base64::engine::general_purpose::STANDARD.encode(source);
66+
let source = format!("data:image/svg+xml;base64,{}", base64);
67+
if let Ok(url) = ServoUrl::parse(&source) {
68+
*self.cached_serialized_data.borrow_mut() = Some(url);
69+
}
70+
}
5671
}
5772

5873
pub(crate) trait LayoutSVGSVGElementHelpers {
59-
fn data(self) -> SVGSVGData;
74+
fn data(self) -> SVGElementData;
6075
}
6176

6277
impl LayoutSVGSVGElementHelpers for LayoutDom<'_, SVGSVGElement> {
63-
fn data(self) -> SVGSVGData {
64-
let width_attr = self
65-
.upcast::<Element>()
66-
.get_attr_for_layout(&ns!(), &local_name!("width"));
67-
let height_attr = self
68-
.upcast::<Element>()
69-
.get_attr_for_layout(&ns!(), &local_name!("height"));
70-
SVGSVGData {
71-
width: width_attr.map_or(DEFAULT_WIDTH, |val| val.as_uint()),
72-
height: height_attr.map_or(DEFAULT_HEIGHT, |val| val.as_uint()),
78+
fn data(self) -> SVGElementData {
79+
SVGElementData {
80+
source: self.unsafe_get().cached_serialized_data.borrow().clone(),
7381
}
7482
}
7583
}
@@ -83,16 +91,14 @@ impl VirtualMethods for SVGSVGElement {
8391
self.super_type()
8492
.unwrap()
8593
.attribute_mutated(attr, mutation, can_gc);
94+
*self.cached_serialized_data.borrow_mut() = None;
8695
}
8796

88-
fn parse_plain_attribute(&self, name: &LocalName, value: DOMString) -> AttrValue {
89-
match *name {
90-
local_name!("width") => AttrValue::from_u32(value.into(), DEFAULT_WIDTH),
91-
local_name!("height") => AttrValue::from_u32(value.into(), DEFAULT_HEIGHT),
92-
_ => self
93-
.super_type()
94-
.unwrap()
95-
.parse_plain_attribute(name, value),
97+
fn children_changed(&self, mutation: &super::node::ChildrenMutation) {
98+
if let Some(super_type) = self.super_type() {
99+
super_type.children_changed(mutation);
96100
}
101+
102+
*self.cached_serialized_data.borrow_mut() = None;
97103
}
98104
}

components/script/dom/window.rs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,8 @@ use dom_struct::dom_struct;
3333
use embedder_traits::user_content_manager::{UserContentManager, UserScript};
3434
use embedder_traits::{
3535
AlertResponse, ConfirmResponse, EmbedderMsg, GamepadEvent, GamepadSupportedHapticEffects,
36-
GamepadUpdateType, PromptResponse, SimpleDialog, Theme, ViewportDetails, WebDriverJSError,
37-
WebDriverJSResult,
36+
GamepadUpdateType, PromptResponse, SimpleDialog, Theme, UntrustedNodeAddress, ViewportDetails,
37+
WebDriverJSError, WebDriverJSResult,
3838
};
3939
use euclid::default::{Point2D as UntypedPoint2D, Rect as UntypedRect, Size2D as UntypedSize2D};
4040
use euclid::{Point2D, Scale, Size2D, Vector2D};
@@ -92,6 +92,7 @@ use webrender_api::units::{DeviceIntSize, DevicePixel, LayoutPixel};
9292

9393
use super::bindings::codegen::Bindings::MessagePortBinding::StructuredSerializeOptions;
9494
use super::bindings::trace::HashMapTracedValues;
95+
use super::types::SVGSVGElement;
9596
use crate::dom::bindings::cell::{DomRefCell, Ref};
9697
use crate::dom::bindings::codegen::Bindings::DocumentBinding::{
9798
DocumentMethods, DocumentReadyState, NamedPropertyValue,
@@ -2258,6 +2259,7 @@ impl Window {
22582259
self.handle_pending_images_post_reflow(
22592260
results.pending_images,
22602261
results.pending_rasterization_images,
2262+
results.pending_svg_element_for_serialization,
22612263
);
22622264
document
22632265
.iframes_mut()
@@ -2989,6 +2991,7 @@ impl Window {
29892991
&self,
29902992
pending_images: Vec<PendingImage>,
29912993
pending_rasterization_images: Vec<PendingRasterizationImage>,
2994+
pending_svg_element_for_serialization: Vec<UntrustedNodeAddress>,
29922995
) {
29932996
let pipeline_id = self.pipeline_id();
29942997
for image in pending_images {
@@ -3037,6 +3040,13 @@ impl Window {
30373040
nodes.push(Dom::from_ref(&*node));
30383041
}
30393042
}
3043+
3044+
for node in pending_svg_element_for_serialization.into_iter() {
3045+
let node = unsafe { from_untrusted_node_address(node) };
3046+
let svg = node.downcast::<SVGSVGElement>().unwrap();
3047+
svg.cache_serialized_data();
3048+
node.dirty(NodeDamage::Other);
3049+
}
30403050
}
30413051
}
30423052

components/script/layout_dom/node.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use fonts_traits::ByteIndex;
1212
use html5ever::{local_name, ns};
1313
use layout_api::wrapper_traits::{LayoutDataTrait, LayoutNode, ThreadSafeLayoutNode};
1414
use layout_api::{
15-
GenericLayoutData, HTMLCanvasData, HTMLMediaData, LayoutNodeType, SVGSVGData, StyleData,
15+
GenericLayoutData, HTMLCanvasData, HTMLMediaData, LayoutNodeType, SVGElementData, StyleData,
1616
TrustedNodeAddress,
1717
};
1818
use net_traits::image_cache::Image;
@@ -387,7 +387,7 @@ impl<'dom> ThreadSafeLayoutNode<'dom> for ServoThreadSafeLayoutNode<'dom> {
387387
this.media_data()
388388
}
389389

390-
fn svg_data(&self) -> Option<SVGSVGData> {
390+
fn svg_data(&self) -> Option<SVGElementData> {
391391
let this = unsafe { self.get_jsmanaged() };
392392
this.svg_data()
393393
}

0 commit comments

Comments
 (0)