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"
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
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();
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
);
117 if (aAttribute
== nsGkAtoms::rowspan
) {
118 aResult
.ParseClampedNonNegativeInt(aValue
, 1, 0, MAX_ROWSPAN
);
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
;
157 bool MathMLElement::ParseNamedSpaceValue(const nsString
& aString
,
158 nsCSSValue
& aCSSValue
, uint32_t aFlags
,
159 const Document
& aDocument
) {
160 if (StaticPrefs::mathml_mathspace_names_disabled()) {
164 // See if it is one of the 'namedspace' (ranging -7/18em, -6/18, ... 7/18em)
165 if (aString
.EqualsLiteral("veryverythinmathspace")) {
167 } else if (aString
.EqualsLiteral("verythinmathspace")) {
169 } else if (aString
.EqualsLiteral("thinmathspace")) {
171 } else if (aString
.EqualsLiteral("mediummathspace")) {
173 } else if (aString
.EqualsLiteral("thickmathspace")) {
175 } else if (aString
.EqualsLiteral("verythickmathspace")) {
177 } else if (aString
.EqualsLiteral("veryverythickmathspace")) {
179 } else if (aFlags
& PARSE_ALLOW_NEGATIVE
) {
180 if (aString
.EqualsLiteral("negativeveryverythinmathspace")) {
182 } else if (aString
.EqualsLiteral("negativeverythinmathspace")) {
184 } else if (aString
.EqualsLiteral("negativethinmathspace")) {
186 } else if (aString
.EqualsLiteral("negativemediummathspace")) {
188 } else if (aString
.EqualsLiteral("negativethickmathspace")) {
190 } else if (aString
.EqualsLiteral("negativeverythickmathspace")) {
192 } else if (aString
.EqualsLiteral("negativeveryverythickmathspace")) {
197 aDocument
.WarnOnceAbout(
198 dom::DeprecatedOperations::eMathML_DeprecatedMathSpaceValue
);
199 aCSSValue
.SetFloatValue(float(i
) / float(18), eCSSUnit_EM
);
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
221 // "The possible units in MathML are:
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)
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
238 // - number: "an optional prefix of '-' (U+002D), followed by an unsigned
239 // number, representing a terminating decimal number (a type of rational
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();
253 if (!(aFlags
& PARSE_SUPPRESS_WARNINGS
)) {
254 ReportLengthParseError(aString
, aDocument
);
259 if (aDocument
&& ParseNamedSpaceValue(str
, aCSSValue
, aFlags
, *aDocument
)) {
263 nsAutoString number
, unit
;
265 // see if the negative sign is there
273 // Gather up characters that make up the number
275 for (; i
< stringLength
; i
++) {
277 if (gotDot
&& c
== '.') {
278 if (!(aFlags
& PARSE_SUPPRESS_WARNINGS
)) {
279 ReportLengthParseError(aString
, aDocument
);
281 return false; // two dots encountered
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'.
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
301 float floatValue
= number
.ToFloat(&errorCode
);
302 if (NS_FAILED(errorCode
)) {
303 if (!(aFlags
& PARSE_SUPPRESS_WARNINGS
)) {
304 ReportLengthParseError(aString
, aDocument
);
308 if (floatValue
< 0 && !(aFlags
& PARSE_ALLOW_NEGATIVE
)) {
309 if (!(aFlags
& PARSE_SUPPRESS_WARNINGS
)) {
310 ReportLengthParseError(aString
, aDocument
);
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
320 if (floatValue
!= 0.0) {
321 if (!(aFlags
& PARSE_SUPPRESS_WARNINGS
)) {
322 ReportLengthParseError(aString
, aDocument
);
326 cssUnit
= eCSSUnit_Pixel
;
327 } else if (unit
.EqualsLiteral("%")) {
328 aCSSValue
.SetPercentValue(floatValue
/ 100.0f
);
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
);
355 aCSSValue
.SetFloatValue(floatValue
, cssUnit
);
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
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) != '+') {
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
,
387 ReportParseErrorNoTag(str
, nsGkAtoms::scriptsizemultiplier_
,
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."
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,
413 if (scriptMinSize
.GetUnit() == eCSSUnit_Percent
) {
414 scriptMinSize
.SetFloatValue(8.0 * scriptMinSize
.GetPercentValue(),
417 if (scriptMinSize
.GetUnit() != eCSSUnit_Null
) {
418 aDecls
.SetLengthValue(eCSSProperty__moz_script_min_size
, scriptMinSize
);
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) {
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
);
446 ReportParseErrorNoTag(str
, nsGkAtoms::scriptlevel_
, aDecls
.Document());
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();
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
);
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",
495 "sans-serif-bold-italic",
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
]);
529 // https://w3c.github.io/mathml-core/#dfn-mathbackground
530 value
= aAttributes
->GetAttr(nsGkAtoms::mathbackground_
);
533 if (value
->GetColorValue(color
)) {
534 aDecls
.SetColorValueIfUnset(eCSSProperty_background_color
, color
);
539 // https://w3c.github.io/mathml-core/#dfn-mathcolor
540 value
= aAttributes
->GetAttr(nsGkAtoms::mathcolor_
);
542 if (value
&& value
->GetColorValue(color
)) {
543 aDecls
.SetColorValueIfUnset(eCSSProperty_color
, color
);
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
558 if (!aDecls
.PropertyIsSet(eCSSProperty_width
)) {
559 const nsAttrValue
* value
= aAttributes
->GetAttr(nsGkAtoms::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
);
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
]);
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
]);
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
625 void MathMLElement::SetIncrementScriptLevel(bool aIncrementScriptLevel
,
627 if (aIncrementScriptLevel
== mIncrementScriptLevel
) return;
628 mIncrementScriptLevel
= aIncrementScriptLevel
;
630 NS_ASSERTION(aNotify
, "We always notify!");
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.
647 int32_t tabIndex
= TabIndex();
649 *aTabIndex
= tabIndex
;
653 // If a tabindex is specified at all we're focusable
654 return GetTabIndexAttrValue().isSome();
657 if (!OwnerDoc()->LinkHandlingEnabled()) {
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)) {
670 if (aTabIndex
&& (sTabFocusModel
& eTabFocus_linksMask
) == 0) {
677 already_AddRefed
<nsIURI
> MathMLElement::GetHrefURI() const {
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
);
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
,
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
);