script/dom/
svgsvgelement.rs1use std::cell::RefCell;
6
7use base64::Engine as _;
8use dom_struct::dom_struct;
9use html5ever::{LocalName, Prefix, local_name, ns};
10use js::rust::HandleObject;
11use layout_api::SVGElementData;
12use servo_url::ServoUrl;
13use style::attr::{AttrValue, parse_integer, parse_unsigned_integer};
14use style::str::char_is_whitespace;
15use xml5ever::serialize::TraversalScope;
16
17use crate::dom::attr::Attr;
18use crate::dom::bindings::inheritance::Castable;
19use crate::dom::bindings::root::{DomRoot, LayoutDom};
20use crate::dom::bindings::str::DOMString;
21use crate::dom::document::Document;
22use crate::dom::element::{AttributeMutation, Element, LayoutElementHelpers};
23use crate::dom::node::{Node, NodeDamage};
24use crate::dom::svggraphicselement::SVGGraphicsElement;
25use crate::dom::virtualmethods::VirtualMethods;
26use crate::script_runtime::CanGc;
27
28#[dom_struct]
29pub(crate) struct SVGSVGElement {
30 svggraphicselement: SVGGraphicsElement,
31 #[no_trace]
35 cached_serialized_data_url: RefCell<Option<Result<ServoUrl, ()>>>,
36}
37
38impl SVGSVGElement {
39 fn new_inherited(
40 local_name: LocalName,
41 prefix: Option<Prefix>,
42 document: &Document,
43 ) -> SVGSVGElement {
44 SVGSVGElement {
45 svggraphicselement: SVGGraphicsElement::new_inherited(local_name, prefix, document),
46 cached_serialized_data_url: Default::default(),
47 }
48 }
49
50 #[cfg_attr(crown, allow(crown::unrooted_must_root))]
51 pub(crate) fn new(
52 local_name: LocalName,
53 prefix: Option<Prefix>,
54 document: &Document,
55 proto: Option<HandleObject>,
56 can_gc: CanGc,
57 ) -> DomRoot<SVGSVGElement> {
58 Node::reflect_node_with_proto(
59 Box::new(SVGSVGElement::new_inherited(local_name, prefix, document)),
60 document,
61 proto,
62 can_gc,
63 )
64 }
65
66 pub(crate) fn serialize_and_cache_subtree(&self) {
67 let Ok(xml_source) = self
68 .upcast::<Node>()
69 .xml_serialize(TraversalScope::IncludeNode)
70 else {
71 *self.cached_serialized_data_url.borrow_mut() = Some(Err(()));
72 return;
73 };
74
75 let xml_source: String = xml_source.into();
76 let base64_encoded_source = base64::engine::general_purpose::STANDARD.encode(xml_source);
77 let data_url = format!("data:image/svg+xml;base64,{}", base64_encoded_source);
78 match ServoUrl::parse(&data_url) {
79 Ok(url) => *self.cached_serialized_data_url.borrow_mut() = Some(Ok(url)),
80 Err(error) => error!("Unable to parse serialized SVG data url: {error}"),
81 };
82 }
83
84 fn invalidate_cached_serialized_subtree(&self) {
85 *self.cached_serialized_data_url.borrow_mut() = None;
86 self.upcast::<Node>().dirty(NodeDamage::Other);
87 }
88}
89
90pub(crate) trait LayoutSVGSVGElementHelpers {
91 fn data(self) -> SVGElementData;
92}
93
94fn ratio_from_view_box(view_box: &AttrValue) -> Option<f32> {
95 let mut iter = view_box.chars();
96 let _min_x = parse_integer(&mut iter).ok()?;
97 let _min_y = parse_integer(&mut iter).ok()?;
98 let width = parse_unsigned_integer(&mut iter).ok()?;
99 if width == 0 {
100 return None;
101 }
102 let height = parse_unsigned_integer(&mut iter).ok()?;
103 if height == 0 {
104 return None;
105 }
106 let mut iter = iter.skip_while(|c| char_is_whitespace(*c));
107 iter.next().is_none().then(|| width as f32 / height as f32)
108}
109
110impl LayoutSVGSVGElementHelpers for LayoutDom<'_, SVGSVGElement> {
111 fn data(self) -> SVGElementData {
112 let element = self.upcast::<Element>();
113 let get_size = |attr| {
114 element
115 .get_attr_for_layout(&ns!(), &attr)
116 .map(|val| val.as_int())
117 .filter(|val| *val >= 0)
118 };
119 let width = get_size(local_name!("width"));
120 let height = get_size(local_name!("height"));
121 let ratio = match (width, height) {
122 (Some(width), Some(height)) if width != 0 && height != 0 => {
123 Some(width as f32 / height as f32)
124 },
125 _ => element
126 .get_attr_for_layout(&ns!(), &local_name!("viewBox"))
127 .and_then(ratio_from_view_box),
128 };
129 SVGElementData {
130 source: self
131 .unsafe_get()
132 .cached_serialized_data_url
133 .borrow()
134 .clone(),
135 width,
136 height,
137 ratio,
138 }
139 }
140}
141
142impl VirtualMethods for SVGSVGElement {
143 fn super_type(&self) -> Option<&dyn VirtualMethods> {
144 Some(self.upcast::<SVGGraphicsElement>() as &dyn VirtualMethods)
145 }
146
147 fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation, can_gc: CanGc) {
148 self.super_type()
149 .unwrap()
150 .attribute_mutated(attr, mutation, can_gc);
151
152 self.invalidate_cached_serialized_subtree();
153 }
154
155 fn parse_plain_attribute(&self, name: &LocalName, value: DOMString) -> AttrValue {
156 match *name {
157 local_name!("width") | local_name!("height") => AttrValue::from_i32(value.into(), -1),
159 _ => self
160 .super_type()
161 .unwrap()
162 .parse_plain_attribute(name, value),
163 }
164 }
165
166 fn children_changed(&self, mutation: &super::node::ChildrenMutation) {
167 if let Some(super_type) = self.super_type() {
168 super_type.children_changed(mutation);
169 }
170
171 self.invalidate_cached_serialized_subtree();
172 }
173}