1use dom_struct::dom_struct;
6use html5ever::{LocalName, Prefix, local_name, ns};
7use js::context::JSContext;
8use js::rust::HandleObject;
9use script_bindings::codegen::GenericBindings::ElementBinding::ScrollLogicalPosition;
10use script_bindings::codegen::GenericBindings::WindowBinding::ScrollBehavior;
11use script_bindings::str::DOMString;
12use style::attr::AttrValue;
13use style::parser::ParserContext;
14use style::properties::{PropertyDeclaration, longhands};
15use style::stylesheets::{CssRuleType, Origin, UrlExtraData};
16use style::values::generics::NonNegative;
17use style::values::specified;
18use style_traits::ParsingMode;
19use stylo_dom::ElementState;
20
21use crate::dom::bindings::codegen::Bindings::HTMLOrSVGElementBinding::FocusOptions;
22use crate::dom::bindings::codegen::Bindings::SVGElementBinding::SVGElementMethods;
23use crate::dom::bindings::inheritance::Castable;
24use crate::dom::bindings::root::{Dom, DomRoot, LayoutDom, MutNullableDom};
25use crate::dom::css::cssstyledeclaration::{
26 CSSModificationAccess, CSSStyleDeclaration, CSSStyleOwner,
27};
28use crate::dom::document::Document;
29use crate::dom::document::focus::FocusableArea;
30use crate::dom::element::attributes::storage::AttrRef;
31use crate::dom::element::{AttributeMutation, Element};
32use crate::dom::node::virtualmethods::VirtualMethods;
33use crate::dom::node::{Node, NodeTraits};
34use crate::dom::scrolling_box::{ScrollAxisState, ScrollRequirement};
35use crate::dom::svg::svgcircleelement::SVGCircleElement;
36use crate::dom::svg::svgellipseelement::SVGEllipseElement;
37use crate::dom::svg::svgimageelement::SVGImageElement;
38use crate::dom::svg::svgpathelement::SVGPathElement;
39use crate::dom::svg::svgrectelement::SVGRectElement;
40use crate::dom::svg::svgsvgelement::SVGSVGElement;
41
42#[dom_struct]
43pub(crate) struct SVGElement {
44 element: Element,
45 style_decl: MutNullableDom<CSSStyleDeclaration>,
46}
47
48impl SVGElement {
49 fn new_inherited(
50 tag_name: LocalName,
51 prefix: Option<Prefix>,
52 document: &Document,
53 ) -> SVGElement {
54 SVGElement::new_inherited_with_state(ElementState::empty(), tag_name, prefix, document)
55 }
56
57 pub(crate) fn new_inherited_with_state(
58 state: ElementState,
59 tag_name: LocalName,
60 prefix: Option<Prefix>,
61 document: &Document,
62 ) -> SVGElement {
63 SVGElement {
64 element: Element::new_inherited_with_state(state, tag_name, ns!(svg), prefix, document),
65 style_decl: Default::default(),
66 }
67 }
68
69 pub(crate) fn new(
70 cx: &mut js::context::JSContext,
71 tag_name: LocalName,
72 prefix: Option<Prefix>,
73 document: &Document,
74 proto: Option<HandleObject>,
75 ) -> DomRoot<SVGElement> {
76 Node::reflect_node_with_proto(
77 cx,
78 Box::new(SVGElement::new_inherited(tag_name, prefix, document)),
79 document,
80 proto,
81 )
82 }
83
84 fn as_element(&self) -> &Element {
85 self.upcast::<Element>()
86 }
87}
88
89impl VirtualMethods for SVGElement {
90 fn super_type(&self) -> Option<&dyn VirtualMethods> {
91 Some(self.as_element() as &dyn VirtualMethods)
92 }
93
94 fn attribute_mutated(
95 &self,
96 cx: &mut js::context::JSContext,
97 attr: AttrRef<'_>,
98 mutation: AttributeMutation,
99 ) {
100 self.super_type()
101 .unwrap()
102 .attribute_mutated(cx, attr, mutation);
103 let element = self.as_element();
104 if let (&local_name!("nonce"), mutation) = (attr.local_name(), mutation) {
105 match mutation {
106 AttributeMutation::Set(..) => {
107 let nonce = &**attr.value();
108 element.update_nonce_internal_slot(nonce.to_owned());
109 },
110 AttributeMutation::Removed => {
111 element.update_nonce_internal_slot(String::new());
112 },
113 }
114 }
115 }
116
117 fn attribute_affects_presentational_hints(&self, attr: AttrRef<'_>) -> bool {
118 matches!(
119 attr.local_name(),
120 &local_name!("fill") |
121 &local_name!("fill-opacity") |
122 &local_name!("fill-rule") |
123 &local_name!("stroke") |
124 &local_name!("stroke-width") |
125 &local_name!("stroke-linecap") |
126 &local_name!("stroke-linejoin") |
127 &local_name!("stroke-dasharray") |
128 &local_name!("stroke-dashoffset") |
129 &local_name!("stroke-miterlimit") |
130 &local_name!("stroke-opacity") |
131 &local_name!("display") |
132 &local_name!("visibility") |
133 &local_name!("opacity") |
134 &local_name!("cx") |
135 &local_name!("cy") |
136 &local_name!("r") |
137 &local_name!("rx") |
138 &local_name!("ry") |
139 &local_name!("x") |
140 &local_name!("y") |
141 &local_name!("width") |
142 &local_name!("height") |
143 &local_name!("d")
144 ) || self
145 .super_type()
146 .unwrap()
147 .attribute_affects_presentational_hints(attr)
148 }
149}
150
151impl SVGElementMethods<crate::DomTypeHolder> for SVGElement {
152 fn Style(&self, cx: &mut JSContext) -> DomRoot<CSSStyleDeclaration> {
154 self.style_decl.or_init(|| {
155 let global = self.owner_window();
156 CSSStyleDeclaration::new(
157 cx,
158 &global,
159 CSSStyleOwner::Element(Dom::from_ref(self.upcast())),
160 None,
161 CSSModificationAccess::ReadWrite,
162 )
163 })
164 }
165
166 global_event_handlers!();
168
169 fn Nonce(&self) -> DOMString {
171 self.as_element().nonce_value().into()
172 }
173
174 fn SetNonce(&self, _cx: &mut JSContext, value: DOMString) {
176 self.as_element()
177 .update_nonce_internal_slot(String::from(value))
178 }
179
180 fn Autofocus(&self) -> bool {
182 self.element.has_attribute(&local_name!("autofocus"))
183 }
184
185 fn SetAutofocus(&self, cx: &mut JSContext, autofocus: bool) {
187 self.element
188 .set_bool_attribute(cx, &local_name!("autofocus"), autofocus);
189 }
190
191 fn Focus(&self, cx: &mut js::context::JSContext, options: &FocusOptions) {
193 if !self.upcast::<Node>().run_the_focusing_steps(cx, None) {
198 return;
202 }
203
204 if !options.preventScroll {
212 let scroll_axis = ScrollAxisState {
213 position: ScrollLogicalPosition::Center,
214 requirement: ScrollRequirement::IfNotVisible,
215 };
216 self.upcast::<Element>().scroll_into_view_with_options(
217 cx,
218 ScrollBehavior::Smooth,
219 scroll_axis,
220 scroll_axis,
221 None,
222 None,
223 );
224 }
225 }
226
227 fn Blur(&self, cx: &mut js::context::JSContext) {
229 if !self.as_element().focus_state() {
232 return;
233 }
234 self.owner_document()
236 .focus_handler()
237 .focus(cx, FocusableArea::Viewport);
238 }
239
240 fn TabIndex(&self) -> i32 {
242 self.element.tab_index()
243 }
244
245 fn SetTabIndex(&self, cx: &mut JSContext, tab_index: i32) {
247 self.element
248 .set_attribute(cx, &local_name!("tabindex"), tab_index.into());
249 }
250}
251
252impl<'dom> LayoutDom<'dom, SVGElement> {
253 pub(crate) fn synthesize_presentational_hints(
254 self,
255 document: LayoutDom<'dom, Document>,
256 push: &mut impl FnMut(PropertyDeclaration),
257 ) {
258 let element = self.upcast::<Element>();
259
260 if element.is::<SVGSVGElement>() {
261 if let Some(width) = element
262 .get_attr_for_layout(&ns!(), &local_name!("width"))
263 .and_then(AttrValue::as_length_percentage)
264 {
265 push(PropertyDeclaration::Width(
266 specified::Size::LengthPercentage(NonNegative(width.clone())),
267 ));
268 }
269 if let Some(height) = element
270 .get_attr_for_layout(&ns!(), &local_name!("height"))
271 .and_then(AttrValue::as_length_percentage)
272 {
273 push(PropertyDeclaration::Height(
274 specified::Size::LengthPercentage(NonNegative(height.clone())),
275 ));
276 }
277 }
278 let url_data = UrlExtraData(document.url_for_layout().get_arc());
279 let parsing_mode =
280 ParsingMode::ALLOW_UNITLESS_LENGTH | ParsingMode::ALLOW_ALL_NUMERIC_VALUES;
281 let parser_context = ParserContext::new(
282 Origin::Author,
283 &url_data,
284 Some(CssRuleType::Style),
285 parsing_mode,
286 document.quirks_mode(),
287 Default::default(),
288 None,
289 None,
290 Default::default(),
291 );
292
293 self.parse_svg_attribute(
294 &parser_context,
295 "fill",
296 longhands::fill::parse_declared,
297 push,
298 );
299 self.parse_svg_attribute(
300 &parser_context,
301 "fill-opacity",
302 longhands::fill_opacity::parse_declared,
303 push,
304 );
305 self.parse_svg_attribute(
306 &parser_context,
307 "fill-rule",
308 longhands::fill_rule::parse_declared,
309 push,
310 );
311
312 self.parse_svg_attribute(
313 &parser_context,
314 "stroke",
315 longhands::stroke::parse_declared,
316 push,
317 );
318 self.parse_svg_attribute(
319 &parser_context,
320 "stroke-width",
321 longhands::stroke_width::parse_declared,
322 push,
323 );
324 self.parse_svg_attribute(
325 &parser_context,
326 "stroke-linecap",
327 longhands::stroke_linecap::parse_declared,
328 push,
329 );
330 self.parse_svg_attribute(
331 &parser_context,
332 "stroke-linejoin",
333 longhands::stroke_linejoin::parse_declared,
334 push,
335 );
336 self.parse_svg_attribute(
337 &parser_context,
338 "stroke-dasharray",
339 longhands::stroke_dasharray::parse_declared,
340 push,
341 );
342 self.parse_svg_attribute(
343 &parser_context,
344 "stroke-dashoffset",
345 longhands::stroke_dashoffset::parse_declared,
346 push,
347 );
348 self.parse_svg_attribute(
349 &parser_context,
350 "stroke-miterlimit",
351 longhands::stroke_miterlimit::parse_declared,
352 push,
353 );
354 self.parse_svg_attribute(
355 &parser_context,
356 "stroke-opacity",
357 longhands::stroke_opacity::parse_declared,
358 push,
359 );
360 self.parse_svg_attribute(
361 &parser_context,
362 "display",
363 longhands::display::parse_declared,
364 push,
365 );
366 self.parse_svg_attribute(
367 &parser_context,
368 "visibility",
369 longhands::visibility::parse_declared,
370 push,
371 );
372 self.parse_svg_attribute(
373 &parser_context,
374 "opacity",
375 longhands::opacity::parse_declared,
376 push,
377 );
378
379 if element.downcast::<SVGCircleElement>().is_some() {
382 self.parse_svg_attribute(&parser_context, "cx", longhands::cx::parse_declared, push);
383 self.parse_svg_attribute(&parser_context, "cy", longhands::cy::parse_declared, push);
384 self.parse_svg_attribute(&parser_context, "r", longhands::r::parse_declared, push);
385 }
386 if element.downcast::<SVGEllipseElement>().is_some() {
388 self.parse_svg_attribute(&parser_context, "cx", longhands::cx::parse_declared, push);
389 self.parse_svg_attribute(&parser_context, "cy", longhands::cy::parse_declared, push);
390 self.parse_svg_attribute(&parser_context, "rx", longhands::rx::parse_declared, push);
391 self.parse_svg_attribute(&parser_context, "ry", longhands::ry::parse_declared, push);
392 }
393 if element.downcast::<SVGRectElement>().is_some() {
395 self.parse_svg_attribute(&parser_context, "x", longhands::x::parse_declared, push);
396 self.parse_svg_attribute(&parser_context, "y", longhands::y::parse_declared, push);
397 self.parse_svg_attribute(
398 &parser_context,
399 "width",
400 longhands::width::parse_declared,
401 push,
402 );
403 self.parse_svg_attribute(
404 &parser_context,
405 "height",
406 longhands::height::parse_declared,
407 push,
408 );
409 self.parse_svg_attribute(&parser_context, "rx", longhands::rx::parse_declared, push);
410 self.parse_svg_attribute(&parser_context, "ry", longhands::ry::parse_declared, push);
411 }
412 if element.downcast::<SVGImageElement>().is_some() {
414 self.parse_svg_attribute(&parser_context, "x", longhands::x::parse_declared, push);
415 self.parse_svg_attribute(&parser_context, "y", longhands::y::parse_declared, push);
416 self.parse_svg_attribute(
417 &parser_context,
418 "width",
419 longhands::width::parse_declared,
420 push,
421 );
422 self.parse_svg_attribute(
423 &parser_context,
424 "height",
425 longhands::height::parse_declared,
426 push,
427 );
428 }
429 if element.downcast::<SVGPathElement>().is_some() {
431 if let Some(value) = element.get_attr_val_for_layout(&ns!(), &local_name!("d")) {
435 if value.eq_ignore_ascii_case("none") {
436 let mut input = cssparser::ParserInput::new(value);
437 let mut parser = cssparser::Parser::new(&mut input);
438 if let Ok(property) =
439 parser.parse_entirely(|i| longhands::d::parse_declared(&parser_context, i))
440 {
441 push(property);
442 }
443 } else {
444 let wrapped = format!("path(\"{}\")", value);
445 let mut input = cssparser::ParserInput::new(&wrapped);
446 let mut parser = cssparser::Parser::new(&mut input);
447 if let Ok(property) = parser.parse_entirely(|parse_input| {
448 longhands::d::parse_declared(&parser_context, parse_input)
449 }) {
450 push(property);
451 }
452 }
453 }
454 }
455 }
456
457 fn parse_svg_attribute<F>(
458 self,
459 parser_context: &ParserContext,
460 attr_name: &str,
461 parse: F,
462 push: &mut impl FnMut(PropertyDeclaration),
463 ) where
464 F: for<'i, 't> FnOnce(
465 &ParserContext,
466 &mut cssparser::Parser<'i, 't>,
467 ) -> Result<PropertyDeclaration, style_traits::ParseError<'i>>,
468 {
469 let element = self.upcast::<Element>();
470 if let Some(value) = element.get_attr_val_for_layout(&ns!(), &LocalName::from(attr_name)) {
471 let mut input = cssparser::ParserInput::new(value);
472 let mut parser = cssparser::Parser::new(&mut input);
473 if let Ok(property) =
474 parser.parse_entirely(|parse_input| parse(parser_context, parse_input))
475 {
476 push(property);
477 }
478 }
479 }
480}