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 "nsStyleConsts.h"
21 #include "mozilla/dom/Document.h"
22 #include "nsPresContext.h"
23 #include "mozAutoDocUpdate.h"
24 #include "nsIScriptError.h"
25 #include "nsContentUtils.h"
28 #include "mozilla/EventDispatcher.h"
29 #include "mozilla/MappedDeclarationsBuilder.h"
30 #include "mozilla/dom/MathMLElementBinding.h"
32 using namespace mozilla
;
33 using namespace mozilla::dom
;
35 //----------------------------------------------------------------------
36 // nsISupports methods:
38 NS_IMPL_ISUPPORTS_INHERITED(MathMLElement
, MathMLElementBase
, Link
)
40 static nsresult
ReportLengthParseError(const nsString
& aValue
,
41 Document
* aDocument
) {
42 AutoTArray
<nsString
, 1> arg
= {aValue
};
43 return nsContentUtils::ReportToConsole(
44 nsIScriptError::errorFlag
, "MathML"_ns
, aDocument
,
45 nsContentUtils::eMATHML_PROPERTIES
, "LengthParsingError", arg
);
48 static nsresult
ReportParseErrorNoTag(const nsString
& aValue
, nsAtom
* aAtom
,
49 Document
& aDocument
) {
50 AutoTArray
<nsString
, 2> argv
= {aValue
, nsDependentAtomString(aAtom
)};
51 return nsContentUtils::ReportToConsole(
52 nsIScriptError::errorFlag
, "MathML"_ns
, &aDocument
,
53 nsContentUtils::eMATHML_PROPERTIES
, "AttributeParsingErrorNoTag", argv
);
56 MathMLElement::MathMLElement(
57 already_AddRefed
<mozilla::dom::NodeInfo
>& aNodeInfo
)
58 : MathMLElementBase(std::move(aNodeInfo
)),
59 ALLOW_THIS_IN_INITIALIZER_LIST(Link(this)) {}
61 MathMLElement::MathMLElement(
62 already_AddRefed
<mozilla::dom::NodeInfo
>&& aNodeInfo
)
63 : MathMLElementBase(std::move(aNodeInfo
)),
64 ALLOW_THIS_IN_INITIALIZER_LIST(Link(this)) {}
66 nsresult
MathMLElement::BindToTree(BindContext
& aContext
, nsINode
& aParent
) {
67 nsresult rv
= MathMLElementBase::BindToTree(aContext
, aParent
);
68 NS_ENSURE_SUCCESS(rv
, rv
);
70 Link::BindToTree(aContext
);
72 // Set the bit in the document for telemetry.
73 if (Document
* doc
= aContext
.GetComposedDoc()) {
74 doc
->SetMathMLEnabled();
80 void MathMLElement::UnbindFromTree(UnbindContext
& aContext
) {
81 MathMLElementBase::UnbindFromTree(aContext
);
82 // Without removing the link state we risk a dangling pointer in the
83 // mStyledLinks hashtable
84 Link::UnbindFromTree();
87 bool MathMLElement::ParseAttribute(int32_t aNamespaceID
, nsAtom
* aAttribute
,
88 const nsAString
& aValue
,
89 nsIPrincipal
* aMaybeScriptedPrincipal
,
90 nsAttrValue
& aResult
) {
91 MOZ_ASSERT(IsMathMLElement());
93 if (aNamespaceID
== kNameSpaceID_None
) {
94 if (aAttribute
== nsGkAtoms::color
|| aAttribute
== nsGkAtoms::mathcolor_
||
95 aAttribute
== nsGkAtoms::background
||
96 aAttribute
== nsGkAtoms::mathbackground_
) {
97 return aResult
.ParseColor(aValue
);
99 if (aAttribute
== nsGkAtoms::tabindex
) {
100 return aResult
.ParseIntValue(aValue
);
102 if (mNodeInfo
->Equals(nsGkAtoms::mtd_
)) {
103 if (aAttribute
== nsGkAtoms::columnspan_
) {
104 aResult
.ParseClampedNonNegativeInt(aValue
, 1, 1, MAX_COLSPAN
);
107 if (aAttribute
== nsGkAtoms::rowspan
) {
108 aResult
.ParseClampedNonNegativeInt(aValue
, 1, 0, MAX_ROWSPAN
);
114 return MathMLElementBase::ParseAttribute(aNamespaceID
, aAttribute
, aValue
,
115 aMaybeScriptedPrincipal
, aResult
);
118 // https://mathml-refresh.github.io/mathml-core/#global-attributes
119 static Element::MappedAttributeEntry sGlobalAttributes
[] = {
121 {nsGkAtoms::mathbackground_
},
122 {nsGkAtoms::mathcolor_
},
123 {nsGkAtoms::mathsize_
},
124 {nsGkAtoms::scriptlevel_
},
125 {nsGkAtoms::displaystyle_
},
128 bool MathMLElement::IsAttributeMapped(const nsAtom
* aAttribute
) const {
129 MOZ_ASSERT(IsMathMLElement());
131 static const MappedAttributeEntry
* const globalMap
[] = {sGlobalAttributes
};
133 return FindAttributeDependence(aAttribute
, globalMap
) ||
134 ((!StaticPrefs::mathml_legacy_mathvariant_attribute_disabled() ||
135 mNodeInfo
->Equals(nsGkAtoms::mi_
)) &&
136 aAttribute
== nsGkAtoms::mathvariant_
) ||
137 (mNodeInfo
->Equals(nsGkAtoms::mtable_
) &&
138 aAttribute
== nsGkAtoms::width
);
141 nsMapRuleToAttributesFunc
MathMLElement::GetAttributeMappingFunction() const {
142 if (mNodeInfo
->Equals(nsGkAtoms::mtable_
)) {
143 return &MapMTableAttributesInto
;
145 if (StaticPrefs::mathml_legacy_mathvariant_attribute_disabled() &&
146 mNodeInfo
->Equals(nsGkAtoms::mi_
)) {
147 return &MapMiAttributesInto
;
149 return &MapGlobalMathMLAttributesInto
;
153 bool MathMLElement::ParseNamedSpaceValue(const nsString
& aString
,
154 nsCSSValue
& aCSSValue
, uint32_t aFlags
,
155 const Document
& aDocument
) {
156 if (StaticPrefs::mathml_mathspace_names_disabled()) {
160 // See if it is one of the 'namedspace' (ranging -7/18em, -6/18, ... 7/18em)
161 if (aString
.EqualsLiteral("veryverythinmathspace")) {
163 } else if (aString
.EqualsLiteral("verythinmathspace")) {
165 } else if (aString
.EqualsLiteral("thinmathspace")) {
167 } else if (aString
.EqualsLiteral("mediummathspace")) {
169 } else if (aString
.EqualsLiteral("thickmathspace")) {
171 } else if (aString
.EqualsLiteral("verythickmathspace")) {
173 } else if (aString
.EqualsLiteral("veryverythickmathspace")) {
175 } else if (aFlags
& PARSE_ALLOW_NEGATIVE
) {
176 if (aString
.EqualsLiteral("negativeveryverythinmathspace")) {
178 } else if (aString
.EqualsLiteral("negativeverythinmathspace")) {
180 } else if (aString
.EqualsLiteral("negativethinmathspace")) {
182 } else if (aString
.EqualsLiteral("negativemediummathspace")) {
184 } else if (aString
.EqualsLiteral("negativethickmathspace")) {
186 } else if (aString
.EqualsLiteral("negativeverythickmathspace")) {
188 } else if (aString
.EqualsLiteral("negativeveryverythickmathspace")) {
193 AutoTArray
<nsString
, 1> params
;
194 params
.AppendElement(aString
);
195 aDocument
.WarnOnceAbout(
196 dom::DeprecatedOperations::eMathML_DeprecatedMathSpaceValue2
, false,
198 aCSSValue
.SetFloatValue(float(i
) / float(18), eCSSUnit_EM
);
207 // "Most presentation elements have attributes that accept values representing
208 // lengths to be used for size, spacing or similar properties. The syntax of a
209 // length is specified as
211 // number | number unit | namedspace
213 // There should be no space between the number and the unit of a length."
215 // "A trailing '%' represents a percent of the default value. The default
216 // value, or how it is obtained, is listed in the table of attributes for each
217 // element. [...] A number without a unit is intepreted as a multiple of the
220 // "The possible units in MathML are:
223 // em an em (font-relative unit traditionally used for horizontal lengths)
224 // ex an ex (font-relative unit traditionally used for vertical lengths)
225 // px pixels, or size of a pixel in the current display
226 // in inches (1 inch = 2.54 centimeters)
229 // pt points (1 point = 1/72 inch)
230 // pc picas (1 pica = 12 points)
231 // % percentage of default value"
233 // The numbers are defined that way:
234 // - unsigned-number: "a string of decimal digits with up to one decimal point
235 // (U+002E), representing a non-negative terminating decimal number (a type of
237 // - number: "an optional prefix of '-' (U+002D), followed by an unsigned
238 // number, representing a terminating decimal number (a type of rational
242 // XXXfredw: Deprecate legacy MathML syntax and use the CSS parser instead.
243 // See https://github.com/mathml-refresh/mathml/issues/63
244 bool MathMLElement::ParseNumericValue(const nsString
& aString
,
245 nsCSSValue
& aCSSValue
, uint32_t aFlags
,
246 Document
* aDocument
) {
247 nsAutoString
str(aString
);
248 str
.CompressWhitespace(); // aString is const in this code...
250 int32_t stringLength
= str
.Length();
252 if (!(aFlags
& PARSE_SUPPRESS_WARNINGS
)) {
253 ReportLengthParseError(aString
, aDocument
);
258 if (aDocument
&& ParseNamedSpaceValue(str
, aCSSValue
, aFlags
, *aDocument
)) {
262 nsAutoString number
, unit
;
264 // see if the negative sign is there
272 // Gather up characters that make up the number
274 for (; i
< stringLength
; i
++) {
276 if (gotDot
&& c
== '.') {
277 if (!(aFlags
& PARSE_SUPPRESS_WARNINGS
)) {
278 ReportLengthParseError(aString
, aDocument
);
280 return false; // two dots encountered
283 else if (!IsAsciiDigit(c
)) {
284 str
.Right(unit
, stringLength
- i
);
285 // some authors leave blanks before the unit, but that shouldn't
286 // be allowed, so don't CompressWhitespace on 'unit'.
291 if (gotDot
&& str
[i
- 1] == '.') {
292 if (!(aFlags
& PARSE_SUPPRESS_WARNINGS
)) {
293 ReportLengthParseError(aString
, aDocument
);
295 return false; // Number ending with a dot.
298 // Convert number to floating point
300 float floatValue
= number
.ToFloat(&errorCode
);
301 if (NS_FAILED(errorCode
)) {
302 if (!(aFlags
& PARSE_SUPPRESS_WARNINGS
)) {
303 ReportLengthParseError(aString
, aDocument
);
307 if (floatValue
< 0 && !(aFlags
& PARSE_ALLOW_NEGATIVE
)) {
308 if (!(aFlags
& PARSE_SUPPRESS_WARNINGS
)) {
309 ReportLengthParseError(aString
, aDocument
);
315 if (unit
.IsEmpty()) {
316 // We are supposed to have a unit, but there isn't one.
317 // If the value is 0 we can just call it "pixels" otherwise
319 if (floatValue
!= 0.0) {
320 if (!(aFlags
& PARSE_SUPPRESS_WARNINGS
)) {
321 ReportLengthParseError(aString
, aDocument
);
325 cssUnit
= eCSSUnit_Pixel
;
326 } else if (unit
.EqualsLiteral("%")) {
327 aCSSValue
.SetPercentValue(floatValue
/ 100.0f
);
329 } else if (unit
.LowerCaseEqualsLiteral("em"))
330 cssUnit
= eCSSUnit_EM
;
331 else if (unit
.LowerCaseEqualsLiteral("ex"))
332 cssUnit
= eCSSUnit_XHeight
;
333 else if (unit
.LowerCaseEqualsLiteral("px"))
334 cssUnit
= eCSSUnit_Pixel
;
335 else if (unit
.LowerCaseEqualsLiteral("in"))
336 cssUnit
= eCSSUnit_Inch
;
337 else if (unit
.LowerCaseEqualsLiteral("cm"))
338 cssUnit
= eCSSUnit_Centimeter
;
339 else if (unit
.LowerCaseEqualsLiteral("mm"))
340 cssUnit
= eCSSUnit_Millimeter
;
341 else if (unit
.LowerCaseEqualsLiteral("pt"))
342 cssUnit
= eCSSUnit_Point
;
343 else if (unit
.LowerCaseEqualsLiteral("pc"))
344 cssUnit
= eCSSUnit_Pica
;
345 else if (unit
.LowerCaseEqualsLiteral("q"))
346 cssUnit
= eCSSUnit_Quarter
;
347 else { // unexpected unit
348 if (!(aFlags
& PARSE_SUPPRESS_WARNINGS
)) {
349 ReportLengthParseError(aString
, aDocument
);
354 aCSSValue
.SetFloatValue(floatValue
, cssUnit
);
358 void MathMLElement::MapMTableAttributesInto(
359 MappedDeclarationsBuilder
& aBuilder
) {
362 // "Specifies the desired width of the entire table and is intended for
363 // visual user agents. When the value is a percentage value, the value is
364 // relative to the horizontal space a MathML renderer has available for the
365 // math element. When the value is "auto", the MathML renderer should
366 // calculate the table width from its contents using whatever layout
367 // algorithm it chooses. "
369 // values: "auto" | length
372 if (!aBuilder
.PropertyIsSet(eCSSProperty_width
)) {
373 const nsAttrValue
* value
= aBuilder
.GetAttr(nsGkAtoms::width
);
375 // This does not handle auto and unitless values
376 if (value
&& value
->Type() == nsAttrValue::eString
) {
377 ParseNumericValue(value
->GetStringValue(), width
, 0,
378 &aBuilder
.Document());
379 if (width
.GetUnit() == eCSSUnit_Percent
) {
380 aBuilder
.SetPercentValue(eCSSProperty_width
, width
.GetPercentValue());
381 } else if (width
.GetUnit() != eCSSUnit_Null
) {
382 aBuilder
.SetLengthValue(eCSSProperty_width
, width
);
386 MapGlobalMathMLAttributesInto(aBuilder
);
389 void MathMLElement::MapMiAttributesInto(MappedDeclarationsBuilder
& aBuilder
) {
391 // https://w3c.github.io/mathml-core/#dfn-mathvariant
392 if (!aBuilder
.PropertyIsSet(eCSSProperty_text_transform
)) {
393 const nsAttrValue
* value
= aBuilder
.GetAttr(nsGkAtoms::mathvariant_
);
394 if (value
&& value
->Type() == nsAttrValue::eString
) {
395 auto str
= value
->GetStringValue();
396 str
.CompressWhitespace();
397 if (value
->GetStringValue().LowerCaseEqualsASCII("normal")) {
398 aBuilder
.SetKeywordValue(eCSSProperty_text_transform
,
399 StyleTextTransformCase::None
);
403 MapGlobalMathMLAttributesInto(aBuilder
);
406 void MathMLElement::MapGlobalMathMLAttributesInto(
407 MappedDeclarationsBuilder
& aBuilder
) {
409 // https://w3c.github.io/mathml-core/#dfn-scriptlevel
410 const nsAttrValue
* value
= aBuilder
.GetAttr(nsGkAtoms::scriptlevel_
);
411 if (value
&& value
->Type() == nsAttrValue::eString
&&
412 !aBuilder
.PropertyIsSet(eCSSProperty_math_depth
)) {
413 auto str
= value
->GetStringValue();
414 // FIXME: Should we remove whitespace trimming?
415 // See https://github.com/w3c/mathml/issues/122
416 str
.CompressWhitespace();
417 if (str
.Length() > 0) {
419 int32_t intValue
= str
.ToInteger(&errorCode
);
420 bool reportParseError
= true;
421 if (NS_SUCCEEDED(errorCode
)) {
422 char16_t ch
= str
.CharAt(0);
423 bool isRelativeScriptLevel
= (ch
== '+' || ch
== '-');
424 // ToInteger is not very strict, check this is really <unsigned>.
425 reportParseError
= false;
426 for (uint32_t i
= isRelativeScriptLevel
? 1 : 0; i
< str
.Length();
428 if (!IsAsciiDigit(str
.CharAt(i
))) {
429 reportParseError
= true;
433 if (!reportParseError
) {
434 aBuilder
.SetMathDepthValue(intValue
, isRelativeScriptLevel
);
437 if (reportParseError
) {
438 ReportParseErrorNoTag(str
, nsGkAtoms::scriptlevel_
,
439 aBuilder
.Document());
445 // https://w3c.github.io/mathml-core/#dfn-mathsize
446 value
= aBuilder
.GetAttr(nsGkAtoms::mathsize_
);
447 if (value
&& value
->Type() == nsAttrValue::eString
&&
448 !aBuilder
.PropertyIsSet(eCSSProperty_font_size
)) {
449 auto str
= value
->GetStringValue();
451 ParseNumericValue(str
, fontSize
, 0, nullptr);
452 if (fontSize
.GetUnit() == eCSSUnit_Percent
) {
453 aBuilder
.SetPercentValue(eCSSProperty_font_size
,
454 fontSize
.GetPercentValue());
455 } else if (fontSize
.GetUnit() != eCSSUnit_Null
) {
456 aBuilder
.SetLengthValue(eCSSProperty_font_size
, fontSize
);
460 if (!StaticPrefs::mathml_legacy_mathvariant_attribute_disabled()) {
463 // "Specifies the logical class of the token. Note that this class is more
464 // than styling, it typically conveys semantic intent;"
466 // values: "normal" | "bold" | "italic" | "bold-italic" | "double-struck" |
467 // "bold-fraktur" | "script" | "bold-script" | "fraktur" | "sans-serif" |
468 // "bold-sans-serif" | "sans-serif-italic" | "sans-serif-bold-italic" |
469 // "monospace" | "initial" | "tailed" | "looped" | "stretched"
470 // default: normal (except on <mi>)
472 value
= aBuilder
.GetAttr(nsGkAtoms::mathvariant_
);
473 if (value
&& value
->Type() == nsAttrValue::eString
&&
474 !aBuilder
.PropertyIsSet(eCSSProperty__moz_math_variant
)) {
475 auto str
= value
->GetStringValue();
476 str
.CompressWhitespace();
477 static const char sizes
[19][23] = {"normal",
489 "sans-serif-bold-italic",
495 static const StyleMathVariant values
[MOZ_ARRAY_LENGTH(sizes
)] = {
496 StyleMathVariant::Normal
,
497 StyleMathVariant::Bold
,
498 StyleMathVariant::Italic
,
499 StyleMathVariant::BoldItalic
,
500 StyleMathVariant::Script
,
501 StyleMathVariant::BoldScript
,
502 StyleMathVariant::Fraktur
,
503 StyleMathVariant::DoubleStruck
,
504 StyleMathVariant::BoldFraktur
,
505 StyleMathVariant::SansSerif
,
506 StyleMathVariant::BoldSansSerif
,
507 StyleMathVariant::SansSerifItalic
,
508 StyleMathVariant::SansSerifBoldItalic
,
509 StyleMathVariant::Monospace
,
510 StyleMathVariant::Initial
,
511 StyleMathVariant::Tailed
,
512 StyleMathVariant::Looped
,
513 StyleMathVariant::Stretched
};
514 for (uint32_t i
= 0; i
< ArrayLength(sizes
); ++i
) {
515 if (str
.LowerCaseEqualsASCII(sizes
[i
])) {
516 if (values
[i
] != StyleMathVariant::Normal
) {
517 // Warn about deprecated mathvariant attribute values. Strictly
518 // speaking, we should also warn about mathvariant="normal" if the
519 // element is not an <mi>. However this would require exposing the
520 // tag name via aBuilder. Moreover, this use case is actually to
521 // revert the effect of a non-normal mathvariant value on an
522 // ancestor element, which should consequently have already
523 // triggered a warning.
524 AutoTArray
<nsString
, 1> params
;
525 params
.AppendElement(str
);
526 aBuilder
.Document().WarnOnceAbout(
527 dom::DeprecatedOperations::eMathML_DeprecatedMathVariant
, false,
530 aBuilder
.SetKeywordValue(eCSSProperty__moz_math_variant
, values
[i
]);
538 // https://w3c.github.io/mathml-core/#dfn-mathbackground
539 value
= aBuilder
.GetAttr(nsGkAtoms::mathbackground_
);
542 if (value
->GetColorValue(color
)) {
543 aBuilder
.SetColorValueIfUnset(eCSSProperty_background_color
, color
);
548 // https://w3c.github.io/mathml-core/#dfn-mathcolor
549 value
= aBuilder
.GetAttr(nsGkAtoms::mathcolor_
);
551 if (value
&& value
->GetColorValue(color
)) {
552 aBuilder
.SetColorValueIfUnset(eCSSProperty_color
, color
);
556 // https://w3c.github.io/mathml-core/#dfn-dir
557 value
= aBuilder
.GetAttr(nsGkAtoms::dir
);
558 if (value
&& value
->Type() == nsAttrValue::eString
&&
559 !aBuilder
.PropertyIsSet(eCSSProperty_direction
)) {
560 auto str
= value
->GetStringValue();
561 static const char dirs
[][4] = {"ltr", "rtl"};
562 static const StyleDirection dirValues
[MOZ_ARRAY_LENGTH(dirs
)] = {
563 StyleDirection::Ltr
, StyleDirection::Rtl
};
564 for (uint32_t i
= 0; i
< ArrayLength(dirs
); ++i
) {
565 if (str
.LowerCaseEqualsASCII(dirs
[i
])) {
566 aBuilder
.SetKeywordValue(eCSSProperty_direction
, dirValues
[i
]);
573 // https://mathml-refresh.github.io/mathml-core/#dfn-displaystyle
574 value
= aBuilder
.GetAttr(nsGkAtoms::displaystyle_
);
575 if (value
&& value
->Type() == nsAttrValue::eString
&&
576 !aBuilder
.PropertyIsSet(eCSSProperty_math_style
)) {
577 auto str
= value
->GetStringValue();
578 static const char displaystyles
[][6] = {"false", "true"};
579 static const StyleMathStyle mathStyle
[MOZ_ARRAY_LENGTH(displaystyles
)] = {
580 StyleMathStyle::Compact
, StyleMathStyle::Normal
};
581 for (uint32_t i
= 0; i
< ArrayLength(displaystyles
); ++i
) {
582 if (str
.LowerCaseEqualsASCII(displaystyles
[i
])) {
583 aBuilder
.SetKeywordValue(eCSSProperty_math_style
, mathStyle
[i
]);
590 void MathMLElement::GetEventTargetParent(EventChainPreVisitor
& aVisitor
) {
591 Element::GetEventTargetParent(aVisitor
);
593 GetEventTargetParentForLinks(aVisitor
);
596 nsresult
MathMLElement::PostHandleEvent(EventChainPostVisitor
& aVisitor
) {
597 return PostHandleEventForLinks(aVisitor
);
600 NS_IMPL_ELEMENT_CLONE(MathMLElement
)
602 void MathMLElement::SetIncrementScriptLevel(bool aIncrementScriptLevel
,
604 NS_ASSERTION(aNotify
, "We always notify!");
605 if (aIncrementScriptLevel
) {
606 AddStates(ElementState::INCREMENT_SCRIPT_LEVEL
);
608 RemoveStates(ElementState::INCREMENT_SCRIPT_LEVEL
);
612 int32_t MathMLElement::TabIndexDefault() { return IsLink() ? 0 : -1; }
614 // XXX Bug 1586011: Share logic with other element classes.
615 Focusable
MathMLElement::IsFocusableWithoutStyle(bool aWithMouse
) {
616 if (!IsInComposedDoc() || IsInDesignMode()) {
617 // In designMode documents we only allow focusing the document.
621 int32_t tabIndex
= TabIndex();
623 // If a tabindex is specified at all we're focusable
624 if (GetTabIndexAttrValue().isSome()) {
625 return {true, tabIndex
};
630 if (!OwnerDoc()->LinkHandlingEnabled()) {
634 // Links that are in an editable region should never be focusable, even if
635 // they are in a contenteditable="false" region.
636 if (nsContentUtils::IsNodeInEditableRegion(this)) {
640 if ((sTabFocusModel
& eTabFocus_linksMask
) == 0) {
644 return {true, tabIndex
};
647 already_AddRefed
<nsIURI
> MathMLElement::GetHrefURI() const {
649 // The REC says: "When user agents encounter MathML elements with both href
650 // and xlink:href attributes, the href attribute should take precedence."
651 const nsAttrValue
* href
= mAttrs
.GetAttr(nsGkAtoms::href
, kNameSpaceID_None
);
656 nsAutoString hrefStr
;
657 href
->ToString(hrefStr
);
658 nsCOMPtr
<nsIURI
> hrefURI
;
659 nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(hrefURI
), hrefStr
,
660 OwnerDoc(), GetBaseURI());
661 return hrefURI
.forget();
664 bool MathMLElement::IsEventAttributeNameInternal(nsAtom
* aName
) {
665 // The intent is to align MathML event attributes on HTML5, so the flag
666 // EventNameType_HTML is used here.
667 return nsContentUtils::IsEventAttributeName(aName
, EventNameType_HTML
);
670 void MathMLElement::BeforeSetAttr(int32_t aNamespaceID
, nsAtom
* aName
,
671 const nsAttrValue
* aValue
, bool aNotify
) {
672 if (aNamespaceID
== kNameSpaceID_None
) {
673 if (!aValue
&& IsEventAttributeName(aName
)) {
674 if (EventListenerManager
* manager
= GetExistingListenerManager()) {
675 manager
->RemoveEventHandler(GetEventNameForAttr(aName
));
680 return MathMLElementBase::BeforeSetAttr(aNamespaceID
, aName
, aValue
, aNotify
);
683 void MathMLElement::AfterSetAttr(int32_t aNameSpaceID
, nsAtom
* aName
,
684 const nsAttrValue
* aValue
,
685 const nsAttrValue
* aOldValue
,
686 nsIPrincipal
* aSubjectPrincipal
,
688 // It is important that this be done after the attribute is set/unset.
689 // We will need the updated attribute value because notifying the document
690 // that content states have changed will call IntrinsicState, which will try
691 // to get updated information about the visitedness from Link.
692 if (aName
== nsGkAtoms::href
&& aNameSpaceID
== kNameSpaceID_None
) {
693 Link::ResetLinkState(aNotify
, aValue
|| Link::ElementHasHref());
696 if (aNameSpaceID
== kNameSpaceID_None
) {
697 if (IsEventAttributeName(aName
) && aValue
) {
698 MOZ_ASSERT(aValue
->Type() == nsAttrValue::eString
,
699 "Expected string value for script body");
700 SetEventHandler(GetEventNameForAttr(aName
), aValue
->GetStringValue());
704 return MathMLElementBase::AfterSetAttr(aNameSpaceID
, aName
, aValue
, aOldValue
,
705 aSubjectPrincipal
, aNotify
);
708 JSObject
* MathMLElement::WrapNode(JSContext
* aCx
,
709 JS::Handle
<JSObject
*> aGivenProto
) {
710 return MathMLElement_Binding::Wrap(aCx
, this, aGivenProto
);