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::parser::ParserContext;
13use style::properties::{PropertyDeclaration, longhands};
14use style::stylesheets::{CssRuleType, Origin, UrlExtraData};
15use style::values::specified::FontSize;
16use style_traits::ParsingMode;
17use stylo_dom::ElementState;
18
19use crate::dom::bindings::codegen::Bindings::HTMLOrSVGOrMathMLElementBinding::FocusOptions;
20use crate::dom::bindings::codegen::Bindings::MathMLElementBinding::MathMLElementMethods;
21use crate::dom::bindings::inheritance::Castable;
22use crate::dom::bindings::root::{DomRoot, LayoutDom};
23use crate::dom::document::Document;
24use crate::dom::document::focus::FocusableArea;
25use crate::dom::element::attributes::storage::AttrRef;
26use crate::dom::element::{AttributeMutation, Element};
27use crate::dom::node::focus::FocusTrigger;
28use crate::dom::node::virtualmethods::VirtualMethods;
29use crate::dom::node::{Node, NodeTraits};
30use crate::dom::window::scrolling_box::{ScrollAxisState, ScrollRequirement};
31
32#[dom_struct]
33pub(crate) struct MathMLElement {
34 element: Element,
35}
36
37impl MathMLElement {
39 pub(crate) fn new_inherited(
40 tag_name: LocalName,
41 prefix: Option<Prefix>,
42 document: &Document,
43 ) -> MathMLElement {
44 MathMLElement {
45 element: Element::new_inherited_with_state(
46 ElementState::empty(),
47 tag_name,
48 ns!(mathml),
49 prefix,
50 document,
51 ),
52 }
53 }
54
55 pub(crate) fn new(
56 cx: &mut JSContext,
57 tag_name: LocalName,
58 prefix: Option<Prefix>,
59 document: &Document,
60 proto: Option<HandleObject>,
61 ) -> DomRoot<MathMLElement> {
62 Node::reflect_node_with_proto(
63 cx,
64 Box::new(MathMLElement::new_inherited(tag_name, prefix, document)),
65 document,
66 proto,
67 )
68 }
69
70 fn as_element(&self) -> &Element {
71 self.upcast::<Element>()
72 }
73}
74
75impl VirtualMethods for MathMLElement {
76 fn super_type(&self) -> Option<&dyn VirtualMethods> {
77 Some(self.as_element() as &dyn VirtualMethods)
78 }
79
80 fn attribute_mutated(
81 &self,
82 cx: &mut JSContext,
83 attr: AttrRef<'_>,
84 mutation: AttributeMutation,
85 ) {
86 self.super_type()
87 .unwrap()
88 .attribute_mutated(cx, attr, mutation);
89
90 let element = self.as_element();
91 if let (&local_name!("nonce"), mutation) = (attr.local_name(), mutation) {
92 match mutation {
93 AttributeMutation::Set(..) => {
94 let nonce = &**attr.value();
95 element.update_nonce_internal_slot(nonce.to_owned(), cx.no_gc());
96 },
97 AttributeMutation::Removed => {
98 element.update_nonce_internal_slot(String::new(), cx.no_gc());
99 },
100 }
101 }
102 }
103
104 fn attribute_affects_presentational_hints(&self, attr: AttrRef<'_>) -> bool {
105 match attr.local_name() {
107 &local_name!("mathcolor") |
108 &local_name!("mathbackground") |
109 &local_name!("mathsize") |
110 &local_name!("dir") => true,
111 _ => self
112 .super_type()
113 .unwrap()
114 .attribute_affects_presentational_hints(attr),
115 }
116 }
117}
118
119impl<'dom> LayoutDom<'dom, MathMLElement> {
120 pub(crate) fn synthesize_presentational_hints(
124 self,
125 document: LayoutDom<'dom, Document>,
126 push: &mut impl FnMut(PropertyDeclaration),
127 ) {
128 let url_data = UrlExtraData(document.url_for_layout().get_arc());
129 let parsing_mode =
130 ParsingMode::ALLOW_UNITLESS_LENGTH | ParsingMode::ALLOW_ALL_NUMERIC_VALUES;
131
132 let parser_context = ParserContext::new(
133 Origin::Author,
134 &url_data,
135 Some(CssRuleType::Style),
136 parsing_mode,
137 document.quirks_mode(),
138 Default::default(),
139 None,
140 None,
141 Default::default(),
142 );
143
144 self.parse_mathml_attribute(
152 &parser_context,
153 "mathcolor",
154 longhands::color::parse_declared,
155 push,
156 );
157 self.parse_mathml_attribute(
158 &parser_context,
159 "mathbackground",
160 longhands::background_color::parse_declared,
161 push,
162 );
163
164 self.parse_mathml_attribute_matching(
170 &parser_context,
171 "mathsize",
172 |decl| {
173 matches!(
174 decl,
175 PropertyDeclaration::FontSize(size)
176 if matches!(size, FontSize::Length(..))
177 )
178 },
179 longhands::font_size::parse_declared,
180 push,
181 );
182 self.parse_mathml_attribute(
188 &parser_context,
189 "dir",
190 longhands::direction::parse_declared,
191 push,
192 );
193 }
194
195 fn parse_mathml_attribute<F>(
196 self,
197 parser_context: &ParserContext,
198 attr_name: &str,
199 parse: F,
200 push: &mut impl FnMut(PropertyDeclaration),
201 ) where
202 F: for<'i> FnOnce(
203 &ParserContext,
204 &mut cssparser::Parser<'i>,
205 ) -> Result<PropertyDeclaration, style_traits::ParseError>,
206 {
207 self.parse_mathml_attribute_matching(parser_context, attr_name, |_| true, parse, push)
208 }
209
210 fn parse_mathml_attribute_matching<P, F>(
211 self,
212 parser_context: &ParserContext,
213 attr_name: &str,
214 predicate: P,
215 parse: F,
216 push: &mut impl FnMut(PropertyDeclaration),
217 ) where
218 P: FnOnce(&PropertyDeclaration) -> bool,
219 F: for<'i> FnOnce(
220 &ParserContext,
221 &mut cssparser::Parser<'i>,
222 ) -> Result<PropertyDeclaration, style_traits::ParseError>,
223 {
224 let element = self.upcast::<Element>();
225 if let Some(value) = element.get_attr_val_for_layout(&ns!(), &LocalName::from(attr_name)) {
226 let mut parser = cssparser::Parser::new(value);
227 if let Ok(property) =
228 parser.parse_entirely(|parse_input| parse(parser_context, parse_input)) &&
229 predicate(&property)
230 {
231 push(property);
232 }
233 }
234 }
235}
236
237impl MathMLElementMethods<crate::DomTypeHolder> for MathMLElement {
238 global_event_handlers!();
239
240 fn Nonce(&self) -> DOMString {
242 self.as_element().nonce_value().into()
243 }
244
245 fn SetNonce(&self, cx: &mut JSContext, value: DOMString) {
247 self.as_element()
248 .update_nonce_internal_slot(String::from(value), cx.no_gc());
249 }
250
251 make_bool_getter!(Autofocus, "autofocus");
253
254 make_bool_setter!(SetAutofocus, "autofocus");
256
257 fn Focus(&self, cx: &mut JSContext, options: &FocusOptions) {
259 if !self
260 .upcast::<Node>()
261 .run_the_focusing_steps(cx, None, FocusTrigger::Other)
262 {
263 return;
264 }
265
266 if !options.preventScroll {
267 let scroll_axis = ScrollAxisState {
268 position: ScrollLogicalPosition::Center,
269 requirement: ScrollRequirement::IfNotVisible,
270 };
271 self.upcast::<Element>().scroll_into_view_with_options(
272 cx,
273 ScrollBehavior::Smooth,
274 scroll_axis,
275 scroll_axis,
276 None,
277 None,
278 );
279 }
280 }
281
282 fn Blur(&self, cx: &mut JSContext) {
284 if !self.as_element().focus_state() {
285 return;
286 }
287 self.owner_document()
288 .focus_handler()
289 .focus(cx, &FocusableArea::Viewport);
290 }
291
292 fn TabIndex(&self) -> i32 {
294 self.element.tab_index()
295 }
296
297 fn SetTabIndex(&self, cx: &mut JSContext, tab_index: i32) {
299 self.element
300 .set_attribute(cx, &local_name!("tabindex"), tab_index.into());
301 }
302}