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