Bug 1835529 [wpt PR 40276] - Update wpt metadata, a=testonly
[gecko.git] / dom / mathml / MathMLElement.cpp
blob25931fa0d6c92f52de1855049929585522942165
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "mozilla/dom/MathMLElement.h"
9 #include "base/compiler_specific.h"
10 #include "mozilla/dom/BindContext.h"
11 #include "mozilla/ArrayUtils.h"
12 #include "mozilla/EventListenerManager.h"
13 #include "mozilla/FontPropertyTypes.h"
14 #include "mozilla/StaticPrefs_mathml.h"
15 #include "mozilla/TextUtils.h"
16 #include "nsGkAtoms.h"
17 #include "nsIContentInlines.h"
18 #include "nsITableCellLayout.h" // for MAX_COLSPAN / MAX_ROWSPAN
19 #include "nsCSSValue.h"
20 #include "nsMappedAttributes.h"
21 #include "nsStyleConsts.h"
22 #include "mozilla/dom/Document.h"
23 #include "nsPresContext.h"
24 #include "mozAutoDocUpdate.h"
25 #include "nsIScriptError.h"
26 #include "nsContentUtils.h"
27 #include "nsIURI.h"
29 #include "mozilla/EventDispatcher.h"
30 #include "mozilla/MappedDeclarations.h"
31 #include "mozilla/dom/MathMLElementBinding.h"
33 using namespace mozilla;
34 using namespace mozilla::dom;
36 //----------------------------------------------------------------------
37 // nsISupports methods:
39 NS_IMPL_ISUPPORTS_INHERITED(MathMLElement, MathMLElementBase, Link)
41 static nsresult ReportLengthParseError(const nsString& aValue,
42 Document* aDocument) {
43 AutoTArray<nsString, 1> arg = {aValue};
44 return nsContentUtils::ReportToConsole(
45 nsIScriptError::errorFlag, "MathML"_ns, aDocument,
46 nsContentUtils::eMATHML_PROPERTIES, "LengthParsingError", arg);
49 static nsresult ReportParseErrorNoTag(const nsString& aValue, nsAtom* aAtom,
50 Document* aDocument) {
51 AutoTArray<nsString, 2> argv = {aValue, nsDependentAtomString(aAtom)};
52 return nsContentUtils::ReportToConsole(
53 nsIScriptError::errorFlag, "MathML"_ns, aDocument,
54 nsContentUtils::eMATHML_PROPERTIES, "AttributeParsingErrorNoTag", argv);
57 MathMLElement::MathMLElement(
58 already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo)
59 : MathMLElementBase(std::move(aNodeInfo)),
60 ALLOW_THIS_IN_INITIALIZER_LIST(Link(this)),
61 mIncrementScriptLevel(false) {}
63 MathMLElement::MathMLElement(
64 already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)
65 : MathMLElementBase(std::move(aNodeInfo)),
66 ALLOW_THIS_IN_INITIALIZER_LIST(Link(this)),
67 mIncrementScriptLevel(false) {}
69 nsresult MathMLElement::BindToTree(BindContext& aContext, nsINode& aParent) {
70 Link::ResetLinkState(false, Link::ElementHasHref());
72 nsresult rv = MathMLElementBase::BindToTree(aContext, aParent);
73 NS_ENSURE_SUCCESS(rv, rv);
75 // FIXME(emilio): Probably should be composed, this uses all the other link
76 // infrastructure.
77 if (Document* doc = aContext.GetUncomposedDoc()) {
78 doc->RegisterPendingLinkUpdate(this);
81 // Set the bit in the document for telemetry.
82 if (Document* doc = aContext.GetComposedDoc()) {
83 doc->SetMathMLEnabled();
86 return rv;
89 void MathMLElement::UnbindFromTree(bool aNullParent) {
90 // Without removing the link state we risk a dangling pointer
91 // in the mStyledLinks hashtable
92 Link::ResetLinkState(false, Link::ElementHasHref());
94 MathMLElementBase::UnbindFromTree(aNullParent);
97 bool MathMLElement::ParseAttribute(int32_t aNamespaceID, nsAtom* aAttribute,
98 const nsAString& aValue,
99 nsIPrincipal* aMaybeScriptedPrincipal,
100 nsAttrValue& aResult) {
101 MOZ_ASSERT(IsMathMLElement());
103 if (aNamespaceID == kNameSpaceID_None) {
104 if (aAttribute == nsGkAtoms::color || aAttribute == nsGkAtoms::mathcolor_ ||
105 aAttribute == nsGkAtoms::background ||
106 aAttribute == nsGkAtoms::mathbackground_) {
107 return aResult.ParseColor(aValue);
109 if (aAttribute == nsGkAtoms::tabindex) {
110 return aResult.ParseIntValue(aValue);
112 if (mNodeInfo->Equals(nsGkAtoms::mtd_)) {
113 if (aAttribute == nsGkAtoms::columnspan_) {
114 aResult.ParseClampedNonNegativeInt(aValue, 1, 1, MAX_COLSPAN);
115 return true;
117 if (aAttribute == nsGkAtoms::rowspan) {
118 aResult.ParseClampedNonNegativeInt(aValue, 1, 0, MAX_ROWSPAN);
119 return true;
124 return MathMLElementBase::ParseAttribute(aNamespaceID, aAttribute, aValue,
125 aMaybeScriptedPrincipal, aResult);
128 // https://mathml-refresh.github.io/mathml-core/#global-attributes
129 static Element::MappedAttributeEntry sGlobalAttributes[] = {
130 {nsGkAtoms::dir}, {nsGkAtoms::mathbackground_},
131 {nsGkAtoms::mathcolor_}, {nsGkAtoms::mathsize_},
132 {nsGkAtoms::mathvariant_}, {nsGkAtoms::scriptlevel_},
133 {nsGkAtoms::displaystyle_}, {nullptr}};
135 bool MathMLElement::IsAttributeMapped(const nsAtom* aAttribute) const {
136 MOZ_ASSERT(IsMathMLElement());
138 static const MappedAttributeEntry* const globalMap[] = {sGlobalAttributes};
140 return FindAttributeDependence(aAttribute, globalMap) ||
141 (!StaticPrefs::mathml_scriptminsize_attribute_disabled() &&
142 aAttribute == nsGkAtoms::scriptminsize_) ||
143 (!StaticPrefs::mathml_scriptsizemultiplier_attribute_disabled() &&
144 aAttribute == nsGkAtoms::scriptsizemultiplier_) ||
145 (mNodeInfo->Equals(nsGkAtoms::mtable_) &&
146 aAttribute == nsGkAtoms::width);
149 nsMapRuleToAttributesFunc MathMLElement::GetAttributeMappingFunction() const {
150 // It doesn't really matter what our tag is here, because only attributes
151 // that satisfy IsAttributeMapped will be stored in the mapped attributes
152 // list and available to the mapping function
153 return &MapMathMLAttributesInto;
156 /* static */
157 bool MathMLElement::ParseNamedSpaceValue(const nsString& aString,
158 nsCSSValue& aCSSValue, uint32_t aFlags,
159 const Document& aDocument) {
160 if (StaticPrefs::mathml_mathspace_names_disabled()) {
161 return false;
163 int32_t i = 0;
164 // See if it is one of the 'namedspace' (ranging -7/18em, -6/18, ... 7/18em)
165 if (aString.EqualsLiteral("veryverythinmathspace")) {
166 i = 1;
167 } else if (aString.EqualsLiteral("verythinmathspace")) {
168 i = 2;
169 } else if (aString.EqualsLiteral("thinmathspace")) {
170 i = 3;
171 } else if (aString.EqualsLiteral("mediummathspace")) {
172 i = 4;
173 } else if (aString.EqualsLiteral("thickmathspace")) {
174 i = 5;
175 } else if (aString.EqualsLiteral("verythickmathspace")) {
176 i = 6;
177 } else if (aString.EqualsLiteral("veryverythickmathspace")) {
178 i = 7;
179 } else if (aFlags & PARSE_ALLOW_NEGATIVE) {
180 if (aString.EqualsLiteral("negativeveryverythinmathspace")) {
181 i = -1;
182 } else if (aString.EqualsLiteral("negativeverythinmathspace")) {
183 i = -2;
184 } else if (aString.EqualsLiteral("negativethinmathspace")) {
185 i = -3;
186 } else if (aString.EqualsLiteral("negativemediummathspace")) {
187 i = -4;
188 } else if (aString.EqualsLiteral("negativethickmathspace")) {
189 i = -5;
190 } else if (aString.EqualsLiteral("negativeverythickmathspace")) {
191 i = -6;
192 } else if (aString.EqualsLiteral("negativeveryverythickmathspace")) {
193 i = -7;
196 if (0 != i) {
197 aDocument.WarnOnceAbout(
198 dom::DeprecatedOperations::eMathML_DeprecatedMathSpaceValue);
199 aCSSValue.SetFloatValue(float(i) / float(18), eCSSUnit_EM);
200 return true;
203 return false;
206 // The REC says:
208 // "Most presentation elements have attributes that accept values representing
209 // lengths to be used for size, spacing or similar properties. The syntax of a
210 // length is specified as
212 // number | number unit | namedspace
214 // There should be no space between the number and the unit of a length."
216 // "A trailing '%' represents a percent of the default value. The default
217 // value, or how it is obtained, is listed in the table of attributes for each
218 // element. [...] A number without a unit is intepreted as a multiple of the
219 // default value."
221 // "The possible units in MathML are:
223 // Unit Description
224 // em an em (font-relative unit traditionally used for horizontal lengths)
225 // ex an ex (font-relative unit traditionally used for vertical lengths)
226 // px pixels, or size of a pixel in the current display
227 // in inches (1 inch = 2.54 centimeters)
228 // cm centimeters
229 // mm millimeters
230 // pt points (1 point = 1/72 inch)
231 // pc picas (1 pica = 12 points)
232 // % percentage of default value"
234 // The numbers are defined that way:
235 // - unsigned-number: "a string of decimal digits with up to one decimal point
236 // (U+002E), representing a non-negative terminating decimal number (a type of
237 // rational number)"
238 // - number: "an optional prefix of '-' (U+002D), followed by an unsigned
239 // number, representing a terminating decimal number (a type of rational
240 // number)"
242 /* static */
243 // XXXfredw: Deprecate legacy MathML syntax and use the CSS parser instead.
244 // See https://github.com/mathml-refresh/mathml/issues/63
245 bool MathMLElement::ParseNumericValue(const nsString& aString,
246 nsCSSValue& aCSSValue, uint32_t aFlags,
247 Document* aDocument) {
248 nsAutoString str(aString);
249 str.CompressWhitespace(); // aString is const in this code...
251 int32_t stringLength = str.Length();
252 if (!stringLength) {
253 if (!(aFlags & PARSE_SUPPRESS_WARNINGS)) {
254 ReportLengthParseError(aString, aDocument);
256 return false;
259 if (aDocument && ParseNamedSpaceValue(str, aCSSValue, aFlags, *aDocument)) {
260 return true;
263 nsAutoString number, unit;
265 // see if the negative sign is there
266 int32_t i = 0;
267 char16_t c = str[0];
268 if (c == '-') {
269 number.Append(c);
270 i++;
273 // Gather up characters that make up the number
274 bool gotDot = false;
275 for (; i < stringLength; i++) {
276 c = str[i];
277 if (gotDot && c == '.') {
278 if (!(aFlags & PARSE_SUPPRESS_WARNINGS)) {
279 ReportLengthParseError(aString, aDocument);
281 return false; // two dots encountered
282 } else if (c == '.')
283 gotDot = true;
284 else if (!IsAsciiDigit(c)) {
285 str.Right(unit, stringLength - i);
286 // some authors leave blanks before the unit, but that shouldn't
287 // be allowed, so don't CompressWhitespace on 'unit'.
288 break;
290 number.Append(c);
292 if (gotDot && str[i - 1] == '.') {
293 if (!(aFlags & PARSE_SUPPRESS_WARNINGS)) {
294 ReportLengthParseError(aString, aDocument);
296 return false; // Number ending with a dot.
299 // Convert number to floating point
300 nsresult errorCode;
301 float floatValue = number.ToFloat(&errorCode);
302 if (NS_FAILED(errorCode)) {
303 if (!(aFlags & PARSE_SUPPRESS_WARNINGS)) {
304 ReportLengthParseError(aString, aDocument);
306 return false;
308 if (floatValue < 0 && !(aFlags & PARSE_ALLOW_NEGATIVE)) {
309 if (!(aFlags & PARSE_SUPPRESS_WARNINGS)) {
310 ReportLengthParseError(aString, aDocument);
312 return false;
315 nsCSSUnit cssUnit;
316 if (unit.IsEmpty()) {
317 // We are supposed to have a unit, but there isn't one.
318 // If the value is 0 we can just call it "pixels" otherwise
319 // this is illegal.
320 if (floatValue != 0.0) {
321 if (!(aFlags & PARSE_SUPPRESS_WARNINGS)) {
322 ReportLengthParseError(aString, aDocument);
324 return false;
326 cssUnit = eCSSUnit_Pixel;
327 } else if (unit.EqualsLiteral("%")) {
328 aCSSValue.SetPercentValue(floatValue / 100.0f);
329 return true;
330 } else if (unit.LowerCaseEqualsLiteral("em"))
331 cssUnit = eCSSUnit_EM;
332 else if (unit.LowerCaseEqualsLiteral("ex"))
333 cssUnit = eCSSUnit_XHeight;
334 else if (unit.LowerCaseEqualsLiteral("px"))
335 cssUnit = eCSSUnit_Pixel;
336 else if (unit.LowerCaseEqualsLiteral("in"))
337 cssUnit = eCSSUnit_Inch;
338 else if (unit.LowerCaseEqualsLiteral("cm"))
339 cssUnit = eCSSUnit_Centimeter;
340 else if (unit.LowerCaseEqualsLiteral("mm"))
341 cssUnit = eCSSUnit_Millimeter;
342 else if (unit.LowerCaseEqualsLiteral("pt"))
343 cssUnit = eCSSUnit_Point;
344 else if (unit.LowerCaseEqualsLiteral("pc"))
345 cssUnit = eCSSUnit_Pica;
346 else if (unit.LowerCaseEqualsLiteral("q"))
347 cssUnit = eCSSUnit_Quarter;
348 else { // unexpected unit
349 if (!(aFlags & PARSE_SUPPRESS_WARNINGS)) {
350 ReportLengthParseError(aString, aDocument);
352 return false;
355 aCSSValue.SetFloatValue(floatValue, cssUnit);
356 return true;
359 void MathMLElement::MapMathMLAttributesInto(
360 const nsMappedAttributes* aAttributes, MappedDeclarations& aDecls) {
361 // scriptsizemultiplier
363 // "Specifies the multiplier to be used to adjust font size due to changes
364 // in scriptlevel.
366 // values: number
367 // default: 0.71
369 const nsAttrValue* value =
370 aAttributes->GetAttr(nsGkAtoms::scriptsizemultiplier_);
371 if (value && value->Type() == nsAttrValue::eString &&
372 !aDecls.PropertyIsSet(eCSSProperty__moz_script_size_multiplier)) {
373 aDecls.Document()->WarnOnceAbout(
374 dom::DeprecatedOperations::
375 eMathML_DeprecatedScriptsizemultiplierAttribute);
376 auto str = value->GetStringValue();
377 str.CompressWhitespace();
378 // MathML numbers can't have leading '+'
379 if (str.Length() > 0 && str.CharAt(0) != '+') {
380 nsresult errorCode;
381 float floatValue = str.ToFloat(&errorCode);
382 // Negative scriptsizemultipliers are not parsed
383 if (NS_SUCCEEDED(errorCode) && floatValue >= 0.0f) {
384 aDecls.SetNumberValue(eCSSProperty__moz_script_size_multiplier,
385 floatValue);
386 } else {
387 ReportParseErrorNoTag(str, nsGkAtoms::scriptsizemultiplier_,
388 aDecls.Document());
393 // scriptminsize
395 // "Specifies the minimum font size allowed due to changes in scriptlevel.
396 // Note that this does not limit the font size due to changes to mathsize."
398 // values: length
399 // default: 8pt
401 // We don't allow negative values.
402 // Unitless and percent values give a multiple of the default value.
404 value = aAttributes->GetAttr(nsGkAtoms::scriptminsize_);
405 if (value && value->Type() == nsAttrValue::eString &&
406 !aDecls.PropertyIsSet(eCSSProperty__moz_script_min_size)) {
407 aDecls.Document()->WarnOnceAbout(
408 dom::DeprecatedOperations::eMathML_DeprecatedScriptminsizeAttribute);
409 nsCSSValue scriptMinSize;
410 ParseNumericValue(value->GetStringValue(), scriptMinSize, 0,
411 aDecls.Document());
413 if (scriptMinSize.GetUnit() == eCSSUnit_Percent) {
414 scriptMinSize.SetFloatValue(8.0 * scriptMinSize.GetPercentValue(),
415 eCSSUnit_Point);
417 if (scriptMinSize.GetUnit() != eCSSUnit_Null) {
418 aDecls.SetLengthValue(eCSSProperty__moz_script_min_size, scriptMinSize);
422 // scriptlevel
424 // "Changes the scriptlevel in effect for the children. When the value is
425 // given without a sign, it sets scriptlevel to the specified value; when a
426 // sign is given, it increments ("+") or decrements ("-") the current
427 // value. (Note that large decrements can result in negative values of
428 // scriptlevel, but these values are considered legal.)"
430 // values: ( "+" | "-" )? unsigned-integer
431 // default: inherited
433 value = aAttributes->GetAttr(nsGkAtoms::scriptlevel_);
434 if (value && value->Type() == nsAttrValue::eString &&
435 !aDecls.PropertyIsSet(eCSSProperty_math_depth)) {
436 auto str = value->GetStringValue();
437 str.CompressWhitespace();
438 if (str.Length() > 0) {
439 nsresult errorCode;
440 int32_t intValue = str.ToInteger(&errorCode);
441 if (NS_SUCCEEDED(errorCode)) {
442 char16_t ch = str.CharAt(0);
443 bool isRelativeScriptLevel = (ch == '+' || ch == '-');
444 aDecls.SetMathDepthValue(intValue, isRelativeScriptLevel);
445 } else {
446 ReportParseErrorNoTag(str, nsGkAtoms::scriptlevel_, aDecls.Document());
451 // mathsize
452 // https://w3c.github.io/mathml-core/#dfn-mathsize
453 value = aAttributes->GetAttr(nsGkAtoms::mathsize_);
454 if (value && value->Type() == nsAttrValue::eString &&
455 !aDecls.PropertyIsSet(eCSSProperty_font_size)) {
456 auto str = value->GetStringValue();
457 nsCSSValue fontSize;
458 ParseNumericValue(str, fontSize, 0, nullptr);
459 if (fontSize.GetUnit() == eCSSUnit_Percent) {
460 aDecls.SetPercentValue(eCSSProperty_font_size,
461 fontSize.GetPercentValue());
462 } else if (fontSize.GetUnit() != eCSSUnit_Null) {
463 aDecls.SetLengthValue(eCSSProperty_font_size, fontSize);
467 // mathvariant
469 // "Specifies the logical class of the token. Note that this class is more
470 // than styling, it typically conveys semantic intent;"
472 // values: "normal" | "bold" | "italic" | "bold-italic" | "double-struck" |
473 // "bold-fraktur" | "script" | "bold-script" | "fraktur" | "sans-serif" |
474 // "bold-sans-serif" | "sans-serif-italic" | "sans-serif-bold-italic" |
475 // "monospace" | "initial" | "tailed" | "looped" | "stretched"
476 // default: normal (except on <mi>)
478 value = aAttributes->GetAttr(nsGkAtoms::mathvariant_);
479 if (value && value->Type() == nsAttrValue::eString &&
480 !aDecls.PropertyIsSet(eCSSProperty__moz_math_variant)) {
481 auto str = value->GetStringValue();
482 str.CompressWhitespace();
483 static const char sizes[19][23] = {"normal",
484 "bold",
485 "italic",
486 "bold-italic",
487 "script",
488 "bold-script",
489 "fraktur",
490 "double-struck",
491 "bold-fraktur",
492 "sans-serif",
493 "bold-sans-serif",
494 "sans-serif-italic",
495 "sans-serif-bold-italic",
496 "monospace",
497 "initial",
498 "tailed",
499 "looped",
500 "stretched"};
501 static const StyleMathVariant values[MOZ_ARRAY_LENGTH(sizes)] = {
502 StyleMathVariant::Normal,
503 StyleMathVariant::Bold,
504 StyleMathVariant::Italic,
505 StyleMathVariant::BoldItalic,
506 StyleMathVariant::Script,
507 StyleMathVariant::BoldScript,
508 StyleMathVariant::Fraktur,
509 StyleMathVariant::DoubleStruck,
510 StyleMathVariant::BoldFraktur,
511 StyleMathVariant::SansSerif,
512 StyleMathVariant::BoldSansSerif,
513 StyleMathVariant::SansSerifItalic,
514 StyleMathVariant::SansSerifBoldItalic,
515 StyleMathVariant::Monospace,
516 StyleMathVariant::Initial,
517 StyleMathVariant::Tailed,
518 StyleMathVariant::Looped,
519 StyleMathVariant::Stretched};
520 for (uint32_t i = 0; i < ArrayLength(sizes); ++i) {
521 if (str.LowerCaseEqualsASCII(sizes[i])) {
522 aDecls.SetKeywordValue(eCSSProperty__moz_math_variant, values[i]);
523 break;
528 // mathbackground
529 // https://w3c.github.io/mathml-core/#dfn-mathbackground
530 value = aAttributes->GetAttr(nsGkAtoms::mathbackground_);
531 if (value) {
532 nscolor color;
533 if (value->GetColorValue(color)) {
534 aDecls.SetColorValueIfUnset(eCSSProperty_background_color, color);
538 // mathcolor
539 // https://w3c.github.io/mathml-core/#dfn-mathcolor
540 value = aAttributes->GetAttr(nsGkAtoms::mathcolor_);
541 nscolor color;
542 if (value && value->GetColorValue(color)) {
543 aDecls.SetColorValueIfUnset(eCSSProperty_color, color);
546 // width
548 // "Specifies the desired width of the entire table and is intended for
549 // visual user agents. When the value is a percentage value, the value is
550 // relative to the horizontal space a MathML renderer has available for the
551 // math element. When the value is "auto", the MathML renderer should
552 // calculate the table width from its contents using whatever layout
553 // algorithm it chooses. "
555 // values: "auto" | length
556 // default: auto
558 if (!aDecls.PropertyIsSet(eCSSProperty_width)) {
559 const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::width);
560 nsCSSValue width;
561 // This does not handle auto and unitless values
562 if (value && value->Type() == nsAttrValue::eString) {
563 ParseNumericValue(value->GetStringValue(), width, 0, aDecls.Document());
564 if (width.GetUnit() == eCSSUnit_Percent) {
565 aDecls.SetPercentValue(eCSSProperty_width, width.GetPercentValue());
566 } else if (width.GetUnit() != eCSSUnit_Null) {
567 aDecls.SetLengthValue(eCSSProperty_width, width);
572 // dir
573 // https://w3c.github.io/mathml-core/#dfn-dir
574 value = aAttributes->GetAttr(nsGkAtoms::dir);
575 if (value && value->Type() == nsAttrValue::eString &&
576 !aDecls.PropertyIsSet(eCSSProperty_direction)) {
577 auto str = value->GetStringValue();
578 static const char dirs[][4] = {"ltr", "rtl"};
579 static const StyleDirection dirValues[MOZ_ARRAY_LENGTH(dirs)] = {
580 StyleDirection::Ltr, StyleDirection::Rtl};
581 for (uint32_t i = 0; i < ArrayLength(dirs); ++i) {
582 if (str.LowerCaseEqualsASCII(dirs[i])) {
583 aDecls.SetKeywordValue(eCSSProperty_direction, dirValues[i]);
584 break;
589 // displaystyle
590 // https://mathml-refresh.github.io/mathml-core/#dfn-displaystyle
591 value = aAttributes->GetAttr(nsGkAtoms::displaystyle_);
592 if (value && value->Type() == nsAttrValue::eString &&
593 !aDecls.PropertyIsSet(eCSSProperty_math_style)) {
594 auto str = value->GetStringValue();
595 static const char displaystyles[][6] = {"false", "true"};
596 static const StyleMathStyle mathStyle[MOZ_ARRAY_LENGTH(displaystyles)] = {
597 StyleMathStyle::Compact, StyleMathStyle::Normal};
598 for (uint32_t i = 0; i < ArrayLength(displaystyles); ++i) {
599 if (str.LowerCaseEqualsASCII(displaystyles[i])) {
600 aDecls.SetKeywordValue(eCSSProperty_math_style, mathStyle[i]);
601 break;
607 void MathMLElement::GetEventTargetParent(EventChainPreVisitor& aVisitor) {
608 Element::GetEventTargetParent(aVisitor);
610 GetEventTargetParentForLinks(aVisitor);
613 nsresult MathMLElement::PostHandleEvent(EventChainPostVisitor& aVisitor) {
614 return PostHandleEventForLinks(aVisitor);
617 NS_IMPL_ELEMENT_CLONE(MathMLElement)
619 ElementState MathMLElement::IntrinsicState() const {
620 return Link::LinkState() | MathMLElementBase::IntrinsicState() |
621 (mIncrementScriptLevel ? ElementState::INCREMENT_SCRIPT_LEVEL
622 : ElementState());
625 void MathMLElement::SetIncrementScriptLevel(bool aIncrementScriptLevel,
626 bool aNotify) {
627 if (aIncrementScriptLevel == mIncrementScriptLevel) return;
628 mIncrementScriptLevel = aIncrementScriptLevel;
630 NS_ASSERTION(aNotify, "We always notify!");
632 UpdateState(true);
635 int32_t MathMLElement::TabIndexDefault() { return IsLink() ? 0 : -1; }
637 // XXX Bug 1586011: Share logic with other element classes.
638 bool MathMLElement::IsFocusableInternal(int32_t* aTabIndex, bool aWithMouse) {
639 if (!IsInComposedDoc() || IsInDesignMode()) {
640 // In designMode documents we only allow focusing the document.
641 if (aTabIndex) {
642 *aTabIndex = -1;
644 return false;
647 int32_t tabIndex = TabIndex();
648 if (aTabIndex) {
649 *aTabIndex = tabIndex;
652 if (!IsLink()) {
653 // If a tabindex is specified at all we're focusable
654 return GetTabIndexAttrValue().isSome();
657 if (!OwnerDoc()->LinkHandlingEnabled()) {
658 return false;
661 // Links that are in an editable region should never be focusable, even if
662 // they are in a contenteditable="false" region.
663 if (nsContentUtils::IsNodeInEditableRegion(this)) {
664 if (aTabIndex) {
665 *aTabIndex = -1;
667 return false;
670 if (aTabIndex && (sTabFocusModel & eTabFocus_linksMask) == 0) {
671 *aTabIndex = -1;
674 return true;
677 already_AddRefed<nsIURI> MathMLElement::GetHrefURI() const {
678 // MathML href
679 // The REC says: "When user agents encounter MathML elements with both href
680 // and xlink:href attributes, the href attribute should take precedence."
681 const nsAttrValue* href = mAttrs.GetAttr(nsGkAtoms::href, kNameSpaceID_None);
682 if (!href) {
683 return nullptr;
685 // Get absolute URI
686 nsAutoString hrefStr;
687 href->ToString(hrefStr);
688 nsCOMPtr<nsIURI> hrefURI;
689 nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(hrefURI), hrefStr,
690 OwnerDoc(), GetBaseURI());
691 return hrefURI.forget();
694 bool MathMLElement::IsEventAttributeNameInternal(nsAtom* aName) {
695 // The intent is to align MathML event attributes on HTML5, so the flag
696 // EventNameType_HTML is used here.
697 return nsContentUtils::IsEventAttributeName(aName, EventNameType_HTML);
700 void MathMLElement::BeforeSetAttr(int32_t aNamespaceID, nsAtom* aName,
701 const nsAttrValue* aValue, bool aNotify) {
702 if (aNamespaceID == kNameSpaceID_None) {
703 if (!aValue && IsEventAttributeName(aName)) {
704 if (EventListenerManager* manager = GetExistingListenerManager()) {
705 manager->RemoveEventHandler(GetEventNameForAttr(aName));
710 return MathMLElementBase::BeforeSetAttr(aNamespaceID, aName, aValue, aNotify);
713 void MathMLElement::AfterSetAttr(int32_t aNameSpaceID, nsAtom* aName,
714 const nsAttrValue* aValue,
715 const nsAttrValue* aOldValue,
716 nsIPrincipal* aSubjectPrincipal,
717 bool aNotify) {
718 // It is important that this be done after the attribute is set/unset.
719 // We will need the updated attribute value because notifying the document
720 // that content states have changed will call IntrinsicState, which will try
721 // to get updated information about the visitedness from Link.
722 if (aName == nsGkAtoms::href && aNameSpaceID == kNameSpaceID_None) {
723 Link::ResetLinkState(aNotify, aValue || Link::ElementHasHref());
726 if (aNameSpaceID == kNameSpaceID_None) {
727 if (IsEventAttributeName(aName) && aValue) {
728 MOZ_ASSERT(aValue->Type() == nsAttrValue::eString,
729 "Expected string value for script body");
730 SetEventHandler(GetEventNameForAttr(aName), aValue->GetStringValue());
734 return MathMLElementBase::AfterSetAttr(aNameSpaceID, aName, aValue, aOldValue,
735 aSubjectPrincipal, aNotify);
738 JSObject* MathMLElement::WrapNode(JSContext* aCx,
739 JS::Handle<JSObject*> aGivenProto) {
740 return MathMLElement_Binding::Wrap(aCx, this, aGivenProto);