summaryrefslogtreecommitdiffstats
path: root/dom/mathml/nsMathMLElement.cpp
diff options
context:
space:
mode:
authorMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
committerMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
commit5f8de423f190bbb79a62f804151bc24824fa32d8 (patch)
tree10027f336435511475e392454359edea8e25895d /dom/mathml/nsMathMLElement.cpp
parent49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff)
downloadUXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip
Add m-esr52 at 52.6.0
Diffstat (limited to 'dom/mathml/nsMathMLElement.cpp')
-rw-r--r--dom/mathml/nsMathMLElement.cpp1124
1 files changed, 1124 insertions, 0 deletions
diff --git a/dom/mathml/nsMathMLElement.cpp b/dom/mathml/nsMathMLElement.cpp
new file mode 100644
index 000000000..d6aef876c
--- /dev/null
+++ b/dom/mathml/nsMathMLElement.cpp
@@ -0,0 +1,1124 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+
+#include "nsMathMLElement.h"
+#include "base/compiler_specific.h"
+#include "mozilla/ArrayUtils.h"
+#include "nsGkAtoms.h"
+#include "nsCRT.h"
+#include "nsLayoutStylesheetCache.h"
+#include "nsRuleData.h"
+#include "nsCSSValue.h"
+#include "nsCSSParser.h"
+#include "nsMappedAttributes.h"
+#include "nsStyleConsts.h"
+#include "nsIDocument.h"
+#include "nsIPresShell.h"
+#include "nsPresContext.h"
+#include "mozAutoDocUpdate.h"
+#include "nsIScriptError.h"
+#include "nsContentUtils.h"
+#include "nsIURI.h"
+
+#include "mozilla/EventDispatcher.h"
+#include "mozilla/EventStates.h"
+#include "mozilla/dom/ElementBinding.h"
+
+using namespace mozilla;
+using namespace mozilla::dom;
+
+//----------------------------------------------------------------------
+// nsISupports methods:
+
+NS_IMPL_ISUPPORTS_INHERITED(nsMathMLElement, nsMathMLElementBase,
+ nsIDOMElement, nsIDOMNode, Link)
+
+static nsresult
+WarnDeprecated(const char16_t* aDeprecatedAttribute,
+ const char16_t* aFavoredAttribute, nsIDocument* aDocument)
+{
+ const char16_t *argv[] =
+ { aDeprecatedAttribute, aFavoredAttribute };
+ return nsContentUtils::
+ ReportToConsole(nsIScriptError::warningFlag,
+ NS_LITERAL_CSTRING("MathML"), aDocument,
+ nsContentUtils::eMATHML_PROPERTIES,
+ "DeprecatedSupersededBy", argv, 2);
+}
+
+static nsresult
+ReportLengthParseError(const nsString& aValue, nsIDocument* aDocument)
+{
+ const char16_t *arg = aValue.get();
+ return nsContentUtils::
+ ReportToConsole(nsIScriptError::errorFlag,
+ NS_LITERAL_CSTRING("MathML"), aDocument,
+ nsContentUtils::eMATHML_PROPERTIES,
+ "LengthParsingError", &arg, 1);
+}
+
+static nsresult
+ReportParseErrorNoTag(const nsString& aValue,
+ nsIAtom* aAtom,
+ nsIDocument* aDocument)
+{
+ const char16_t *argv[] =
+ { aValue.get(), aAtom->GetUTF16String() };
+ return nsContentUtils::
+ ReportToConsole(nsIScriptError::errorFlag,
+ NS_LITERAL_CSTRING("MathML"), aDocument,
+ nsContentUtils::eMATHML_PROPERTIES,
+ "AttributeParsingErrorNoTag", argv, 2);
+}
+
+nsMathMLElement::nsMathMLElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo)
+: nsMathMLElementBase(aNodeInfo),
+ ALLOW_THIS_IN_INITIALIZER_LIST(Link(this)),
+ mIncrementScriptLevel(false)
+{
+}
+
+nsMathMLElement::nsMathMLElement(already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)
+: nsMathMLElementBase(aNodeInfo),
+ ALLOW_THIS_IN_INITIALIZER_LIST(Link(this)),
+ mIncrementScriptLevel(false)
+{
+}
+
+nsresult
+nsMathMLElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
+ nsIContent* aBindingParent,
+ bool aCompileEventHandlers)
+{
+ Link::ResetLinkState(false, Link::ElementHasHref());
+
+ nsresult rv = nsMathMLElementBase::BindToTree(aDocument, aParent,
+ aBindingParent,
+ aCompileEventHandlers);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (aDocument) {
+ aDocument->RegisterPendingLinkUpdate(this);
+ }
+
+ nsIDocument* doc = GetComposedDoc();
+ if (doc) {
+ if (!doc->GetMathMLEnabled()) {
+ // Enable MathML and setup the style sheet during binding, not element
+ // construction, because we could move a MathML element from the document
+ // that created it to another document.
+ auto cache = nsLayoutStylesheetCache::For(doc->GetStyleBackendType());
+ doc->SetMathMLEnabled();
+ doc->EnsureOnDemandBuiltInUASheet(cache->MathMLSheet());
+
+ // Rebuild style data for the presshell, because style system
+ // optimizations may have taken place assuming MathML was disabled.
+ // (See nsRuleNode::CheckSpecifiedProperties.)
+ nsCOMPtr<nsIPresShell> shell = doc->GetShell();
+ if (shell) {
+ shell->GetPresContext()->
+ PostRebuildAllStyleDataEvent(nsChangeHint(0), eRestyle_Subtree);
+ }
+ }
+ }
+
+ return rv;
+}
+
+void
+nsMathMLElement::UnbindFromTree(bool aDeep, bool aNullParent)
+{
+ // If this link is ever reinserted into a document, it might
+ // be under a different xml:base, so forget the cached state now.
+ Link::ResetLinkState(false, Link::ElementHasHref());
+
+ nsIDocument* doc = GetUncomposedDoc();
+ if (doc) {
+ doc->UnregisterPendingLinkUpdate(this);
+ }
+
+ nsMathMLElementBase::UnbindFromTree(aDeep, aNullParent);
+}
+
+bool
+nsMathMLElement::ParseAttribute(int32_t aNamespaceID,
+ nsIAtom* aAttribute,
+ const nsAString& aValue,
+ nsAttrValue& aResult)
+{
+ if (aNamespaceID == kNameSpaceID_None) {
+ if (IsMathMLElement(nsGkAtoms::math) && aAttribute == nsGkAtoms::mode) {
+ WarnDeprecated(nsGkAtoms::mode->GetUTF16String(),
+ nsGkAtoms::display->GetUTF16String(), OwnerDoc());
+ }
+ if (aAttribute == nsGkAtoms::color) {
+ WarnDeprecated(nsGkAtoms::color->GetUTF16String(),
+ nsGkAtoms::mathcolor_->GetUTF16String(), OwnerDoc());
+ }
+ if (aAttribute == nsGkAtoms::color ||
+ aAttribute == nsGkAtoms::mathcolor_ ||
+ aAttribute == nsGkAtoms::background ||
+ aAttribute == nsGkAtoms::mathbackground_) {
+ return aResult.ParseColor(aValue);
+ }
+ }
+
+ return nsMathMLElementBase::ParseAttribute(aNamespaceID, aAttribute,
+ aValue, aResult);
+}
+
+static Element::MappedAttributeEntry sMtableStyles[] = {
+ { &nsGkAtoms::width },
+ { nullptr }
+};
+
+static Element::MappedAttributeEntry sTokenStyles[] = {
+ { &nsGkAtoms::mathsize_ },
+ { &nsGkAtoms::fontsize_ },
+ { &nsGkAtoms::color },
+ { &nsGkAtoms::fontfamily_ },
+ { &nsGkAtoms::fontstyle_ },
+ { &nsGkAtoms::fontweight_ },
+ { &nsGkAtoms::mathvariant_},
+ { nullptr }
+};
+
+static Element::MappedAttributeEntry sEnvironmentStyles[] = {
+ { &nsGkAtoms::scriptlevel_ },
+ { &nsGkAtoms::scriptminsize_ },
+ { &nsGkAtoms::scriptsizemultiplier_ },
+ { &nsGkAtoms::background },
+ { nullptr }
+};
+
+static Element::MappedAttributeEntry sCommonPresStyles[] = {
+ { &nsGkAtoms::mathcolor_ },
+ { &nsGkAtoms::mathbackground_ },
+ { nullptr }
+};
+
+static Element::MappedAttributeEntry sDirStyles[] = {
+ { &nsGkAtoms::dir },
+ { nullptr }
+};
+
+bool
+nsMathMLElement::IsAttributeMapped(const nsIAtom* aAttribute) const
+{
+ static const MappedAttributeEntry* const mtableMap[] = {
+ sMtableStyles,
+ sCommonPresStyles
+ };
+ static const MappedAttributeEntry* const tokenMap[] = {
+ sTokenStyles,
+ sCommonPresStyles,
+ sDirStyles
+ };
+ static const MappedAttributeEntry* const mstyleMap[] = {
+ sTokenStyles,
+ sEnvironmentStyles,
+ sCommonPresStyles,
+ sDirStyles
+ };
+ static const MappedAttributeEntry* const commonPresMap[] = {
+ sCommonPresStyles
+ };
+ static const MappedAttributeEntry* const mrowMap[] = {
+ sCommonPresStyles,
+ sDirStyles
+ };
+
+ // We don't support mglyph (yet).
+ if (IsAnyOfMathMLElements(nsGkAtoms::ms_, nsGkAtoms::mi_, nsGkAtoms::mn_,
+ nsGkAtoms::mo_, nsGkAtoms::mtext_,
+ nsGkAtoms::mspace_))
+ return FindAttributeDependence(aAttribute, tokenMap);
+ if (IsAnyOfMathMLElements(nsGkAtoms::mstyle_, nsGkAtoms::math))
+ return FindAttributeDependence(aAttribute, mstyleMap);
+
+ if (IsMathMLElement(nsGkAtoms::mtable_))
+ return FindAttributeDependence(aAttribute, mtableMap);
+
+ if (IsMathMLElement(nsGkAtoms::mrow_))
+ return FindAttributeDependence(aAttribute, mrowMap);
+
+ if (IsAnyOfMathMLElements(nsGkAtoms::maction_,
+ nsGkAtoms::maligngroup_,
+ nsGkAtoms::malignmark_,
+ nsGkAtoms::menclose_,
+ nsGkAtoms::merror_,
+ nsGkAtoms::mfenced_,
+ nsGkAtoms::mfrac_,
+ nsGkAtoms::mover_,
+ nsGkAtoms::mpadded_,
+ nsGkAtoms::mphantom_,
+ nsGkAtoms::mprescripts_,
+ nsGkAtoms::mroot_,
+ nsGkAtoms::msqrt_,
+ nsGkAtoms::msub_,
+ nsGkAtoms::msubsup_,
+ nsGkAtoms::msup_,
+ nsGkAtoms::mtd_,
+ nsGkAtoms::mtr_,
+ nsGkAtoms::munder_,
+ nsGkAtoms::munderover_,
+ nsGkAtoms::none)) {
+ return FindAttributeDependence(aAttribute, commonPresMap);
+ }
+
+ return false;
+}
+
+nsMapRuleToAttributesFunc
+nsMathMLElement::GetAttributeMappingFunction() const
+{
+ // It doesn't really matter what our tag is here, because only attributes
+ // that satisfy IsAttributeMapped will be stored in the mapped attributes
+ // list and available to the mapping function
+ return &MapMathMLAttributesInto;
+}
+
+/* static */ bool
+nsMathMLElement::ParseNamedSpaceValue(const nsString& aString,
+ nsCSSValue& aCSSValue,
+ uint32_t aFlags)
+{
+ int32_t i = 0;
+ // See if it is one of the 'namedspace' (ranging -7/18em, -6/18, ... 7/18em)
+ if (aString.EqualsLiteral("veryverythinmathspace")) {
+ i = 1;
+ } else if (aString.EqualsLiteral("verythinmathspace")) {
+ i = 2;
+ } else if (aString.EqualsLiteral("thinmathspace")) {
+ i = 3;
+ } else if (aString.EqualsLiteral("mediummathspace")) {
+ i = 4;
+ } else if (aString.EqualsLiteral("thickmathspace")) {
+ i = 5;
+ } else if (aString.EqualsLiteral("verythickmathspace")) {
+ i = 6;
+ } else if (aString.EqualsLiteral("veryverythickmathspace")) {
+ i = 7;
+ } else if (aFlags & PARSE_ALLOW_NEGATIVE) {
+ if (aString.EqualsLiteral("negativeveryverythinmathspace")) {
+ i = -1;
+ } else if (aString.EqualsLiteral("negativeverythinmathspace")) {
+ i = -2;
+ } else if (aString.EqualsLiteral("negativethinmathspace")) {
+ i = -3;
+ } else if (aString.EqualsLiteral("negativemediummathspace")) {
+ i = -4;
+ } else if (aString.EqualsLiteral("negativethickmathspace")) {
+ i = -5;
+ } else if (aString.EqualsLiteral("negativeverythickmathspace")) {
+ i = -6;
+ } else if (aString.EqualsLiteral("negativeveryverythickmathspace")) {
+ i = -7;
+ }
+ }
+ if (0 != i) {
+ aCSSValue.SetFloatValue(float(i)/float(18), eCSSUnit_EM);
+ return true;
+ }
+
+ return false;
+}
+
+// The REC says:
+//
+// "Most presentation elements have attributes that accept values representing
+// lengths to be used for size, spacing or similar properties. The syntax of a
+// length is specified as
+//
+// number | number unit | namedspace
+//
+// There should be no space between the number and the unit of a length."
+//
+// "A trailing '%' represents a percent of the default value. The default
+// value, or how it is obtained, is listed in the table of attributes for each
+// element. [...] A number without a unit is intepreted as a multiple of the
+// default value."
+//
+// "The possible units in MathML are:
+//
+// Unit Description
+// em an em (font-relative unit traditionally used for horizontal lengths)
+// ex an ex (font-relative unit traditionally used for vertical lengths)
+// px pixels, or size of a pixel in the current display
+// in inches (1 inch = 2.54 centimeters)
+// cm centimeters
+// mm millimeters
+// pt points (1 point = 1/72 inch)
+// pc picas (1 pica = 12 points)
+// % percentage of default value"
+//
+// The numbers are defined that way:
+// - unsigned-number: "a string of decimal digits with up to one decimal point
+// (U+002E), representing a non-negative terminating decimal number (a type of
+// rational number)"
+// - number: "an optional prefix of '-' (U+002D), followed by an unsigned
+// number, representing a terminating decimal number (a type of rational
+// number)"
+//
+/* static */ bool
+nsMathMLElement::ParseNumericValue(const nsString& aString,
+ nsCSSValue& aCSSValue,
+ uint32_t aFlags,
+ nsIDocument* aDocument)
+{
+ nsAutoString str(aString);
+ str.CompressWhitespace(); // aString is const in this code...
+
+ int32_t stringLength = str.Length();
+ if (!stringLength) {
+ if (!(aFlags & PARSE_SUPPRESS_WARNINGS)) {
+ ReportLengthParseError(aString, aDocument);
+ }
+ return false;
+ }
+
+ if (ParseNamedSpaceValue(str, aCSSValue, aFlags)) {
+ return true;
+ }
+
+ nsAutoString number, unit;
+
+ // see if the negative sign is there
+ int32_t i = 0;
+ char16_t c = str[0];
+ if (c == '-') {
+ number.Append(c);
+ i++;
+ }
+
+ // Gather up characters that make up the number
+ bool gotDot = false;
+ for ( ; i < stringLength; i++) {
+ c = str[i];
+ if (gotDot && c == '.') {
+ if (!(aFlags & PARSE_SUPPRESS_WARNINGS)) {
+ ReportLengthParseError(aString, aDocument);
+ }
+ return false; // two dots encountered
+ }
+ else if (c == '.')
+ gotDot = true;
+ else if (!nsCRT::IsAsciiDigit(c)) {
+ str.Right(unit, stringLength - i);
+ // some authors leave blanks before the unit, but that shouldn't
+ // be allowed, so don't CompressWhitespace on 'unit'.
+ break;
+ }
+ number.Append(c);
+ }
+
+ // Convert number to floating point
+ nsresult errorCode;
+ float floatValue = number.ToFloat(&errorCode);
+ if (NS_FAILED(errorCode)) {
+ if (!(aFlags & PARSE_SUPPRESS_WARNINGS)) {
+ ReportLengthParseError(aString, aDocument);
+ }
+ return false;
+ }
+ if (floatValue < 0 && !(aFlags & PARSE_ALLOW_NEGATIVE)) {
+ if (!(aFlags & PARSE_SUPPRESS_WARNINGS)) {
+ ReportLengthParseError(aString, aDocument);
+ }
+ return false;
+ }
+
+ nsCSSUnit cssUnit;
+ if (unit.IsEmpty()) {
+ if (aFlags & PARSE_ALLOW_UNITLESS) {
+ // no explicit unit, this is a number that will act as a multiplier
+ if (!(aFlags & PARSE_SUPPRESS_WARNINGS)) {
+ nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
+ NS_LITERAL_CSTRING("MathML"), aDocument,
+ nsContentUtils::eMATHML_PROPERTIES,
+ "UnitlessValuesAreDeprecated");
+ }
+ if (aFlags & CONVERT_UNITLESS_TO_PERCENT) {
+ aCSSValue.SetPercentValue(floatValue);
+ return true;
+ }
+ else
+ cssUnit = eCSSUnit_Number;
+ } else {
+ // We are supposed to have a unit, but there isn't one.
+ // If the value is 0 we can just call it "pixels" otherwise
+ // this is illegal.
+ if (floatValue != 0.0) {
+ if (!(aFlags & PARSE_SUPPRESS_WARNINGS)) {
+ ReportLengthParseError(aString, aDocument);
+ }
+ return false;
+ }
+ cssUnit = eCSSUnit_Pixel;
+ }
+ }
+ else if (unit.EqualsLiteral("%")) {
+ aCSSValue.SetPercentValue(floatValue / 100.0f);
+ return true;
+ }
+ else if (unit.EqualsLiteral("em")) cssUnit = eCSSUnit_EM;
+ else if (unit.EqualsLiteral("ex")) cssUnit = eCSSUnit_XHeight;
+ else if (unit.EqualsLiteral("px")) cssUnit = eCSSUnit_Pixel;
+ else if (unit.EqualsLiteral("in")) cssUnit = eCSSUnit_Inch;
+ else if (unit.EqualsLiteral("cm")) cssUnit = eCSSUnit_Centimeter;
+ else if (unit.EqualsLiteral("mm")) cssUnit = eCSSUnit_Millimeter;
+ else if (unit.EqualsLiteral("pt")) cssUnit = eCSSUnit_Point;
+ else if (unit.EqualsLiteral("pc")) cssUnit = eCSSUnit_Pica;
+ else if (unit.EqualsLiteral("q")) cssUnit = eCSSUnit_Quarter;
+ else { // unexpected unit
+ if (!(aFlags & PARSE_SUPPRESS_WARNINGS)) {
+ ReportLengthParseError(aString, aDocument);
+ }
+ return false;
+ }
+
+ aCSSValue.SetFloatValue(floatValue, cssUnit);
+ return true;
+}
+
+void
+nsMathMLElement::MapMathMLAttributesInto(const nsMappedAttributes* aAttributes,
+ nsRuleData* aData)
+{
+ if (aData->mSIDs & NS_STYLE_INHERIT_BIT(Font)) {
+ // scriptsizemultiplier
+ //
+ // "Specifies the multiplier to be used to adjust font size due to changes
+ // in scriptlevel.
+ //
+ // values: number
+ // default: 0.71
+ //
+ const nsAttrValue* value =
+ aAttributes->GetAttr(nsGkAtoms::scriptsizemultiplier_);
+ nsCSSValue* scriptSizeMultiplier =
+ aData->ValueForScriptSizeMultiplier();
+ if (value && value->Type() == nsAttrValue::eString &&
+ scriptSizeMultiplier->GetUnit() == eCSSUnit_Null) {
+ nsAutoString str(value->GetStringValue());
+ str.CompressWhitespace();
+ // MathML numbers can't have leading '+'
+ if (str.Length() > 0 && str.CharAt(0) != '+') {
+ nsresult errorCode;
+ float floatValue = str.ToFloat(&errorCode);
+ // Negative scriptsizemultipliers are not parsed
+ if (NS_SUCCEEDED(errorCode) && floatValue >= 0.0f) {
+ scriptSizeMultiplier->SetFloatValue(floatValue, eCSSUnit_Number);
+ } else {
+ ReportParseErrorNoTag(str,
+ nsGkAtoms::scriptsizemultiplier_,
+ aData->mPresContext->Document());
+ }
+ }
+ }
+
+ // scriptminsize
+ //
+ // "Specifies the minimum font size allowed due to changes in scriptlevel.
+ // Note that this does not limit the font size due to changes to mathsize."
+ //
+ // values: length
+ // default: 8pt
+ //
+ // We don't allow negative values.
+ // Unitless and percent values give a multiple of the default value.
+ //
+ value = aAttributes->GetAttr(nsGkAtoms::scriptminsize_);
+ nsCSSValue* scriptMinSize = aData->ValueForScriptMinSize();
+ if (value && value->Type() == nsAttrValue::eString &&
+ scriptMinSize->GetUnit() == eCSSUnit_Null) {
+ ParseNumericValue(value->GetStringValue(), *scriptMinSize,
+ PARSE_ALLOW_UNITLESS | CONVERT_UNITLESS_TO_PERCENT,
+ aData->mPresContext->Document());
+
+ if (scriptMinSize->GetUnit() == eCSSUnit_Percent) {
+ scriptMinSize->SetFloatValue(8.0 * scriptMinSize->GetPercentValue(),
+ eCSSUnit_Point);
+ }
+ }
+
+ // scriptlevel
+ //
+ // "Changes the scriptlevel in effect for the children. When the value is
+ // given without a sign, it sets scriptlevel to the specified value; when a
+ // sign is given, it increments ("+") or decrements ("-") the current
+ // value. (Note that large decrements can result in negative values of
+ // scriptlevel, but these values are considered legal.)"
+ //
+ // values: ( "+" | "-" )? unsigned-integer
+ // default: inherited
+ //
+ value = aAttributes->GetAttr(nsGkAtoms::scriptlevel_);
+ nsCSSValue* scriptLevel = aData->ValueForScriptLevel();
+ if (value && value->Type() == nsAttrValue::eString &&
+ scriptLevel->GetUnit() == eCSSUnit_Null) {
+ nsAutoString str(value->GetStringValue());
+ str.CompressWhitespace();
+ if (str.Length() > 0) {
+ nsresult errorCode;
+ int32_t intValue = str.ToInteger(&errorCode);
+ if (NS_SUCCEEDED(errorCode)) {
+ // This is kind of cheesy ... if the scriptlevel has a sign,
+ // then it's a relative value and we store the nsCSSValue as an
+ // Integer to indicate that. Otherwise we store it as a Number
+ // to indicate that the scriptlevel is absolute.
+ char16_t ch = str.CharAt(0);
+ if (ch == '+' || ch == '-') {
+ scriptLevel->SetIntValue(intValue, eCSSUnit_Integer);
+ } else {
+ scriptLevel->SetFloatValue(intValue, eCSSUnit_Number);
+ }
+ } else {
+ ReportParseErrorNoTag(str,
+ nsGkAtoms::scriptlevel_,
+ aData->mPresContext->Document());
+ }
+ }
+ }
+
+ // mathsize
+ //
+ // "Specifies the size to display the token content. The values 'small' and
+ // 'big' choose a size smaller or larger than the current font size, but
+ // leave the exact proportions unspecified; 'normal' is allowed for
+ // completeness, but since it is equivalent to '100%' or '1em', it has no
+ // effect."
+ //
+ // values: "small" | "normal" | "big" | length
+ // default: inherited
+ //
+ // fontsize
+ //
+ // "Specified the size for the token. Deprecated in favor of mathsize."
+ //
+ // values: length
+ // default: inherited
+ //
+ // In both cases, we don't allow negative values.
+ // Unitless values give a multiple of the default value.
+ //
+ bool parseSizeKeywords = true;
+ value = aAttributes->GetAttr(nsGkAtoms::mathsize_);
+ if (!value) {
+ parseSizeKeywords = false;
+ value = aAttributes->GetAttr(nsGkAtoms::fontsize_);
+ if (value) {
+ WarnDeprecated(nsGkAtoms::fontsize_->GetUTF16String(),
+ nsGkAtoms::mathsize_->GetUTF16String(),
+ aData->mPresContext->Document());
+ }
+ }
+ nsCSSValue* fontSize = aData->ValueForFontSize();
+ if (value && value->Type() == nsAttrValue::eString &&
+ fontSize->GetUnit() == eCSSUnit_Null) {
+ nsAutoString str(value->GetStringValue());
+ if (!ParseNumericValue(str, *fontSize, PARSE_SUPPRESS_WARNINGS |
+ PARSE_ALLOW_UNITLESS | CONVERT_UNITLESS_TO_PERCENT,
+ nullptr)
+ && parseSizeKeywords) {
+ static const char sizes[3][7] = { "small", "normal", "big" };
+ static const int32_t values[MOZ_ARRAY_LENGTH(sizes)] = {
+ NS_STYLE_FONT_SIZE_SMALL, NS_STYLE_FONT_SIZE_MEDIUM,
+ NS_STYLE_FONT_SIZE_LARGE
+ };
+ str.CompressWhitespace();
+ for (uint32_t i = 0; i < ArrayLength(sizes); ++i) {
+ if (str.EqualsASCII(sizes[i])) {
+ fontSize->SetIntValue(values[i], eCSSUnit_Enumerated);
+ break;
+ }
+ }
+ }
+ }
+
+ // fontfamily
+ //
+ // "Should be the name of a font that may be available to a MathML renderer,
+ // or a CSS font specification; See Section 6.5 Using CSS with MathML and
+ // CSS for more information. Deprecated in favor of mathvariant."
+ //
+ // values: string
+ //
+ value = aAttributes->GetAttr(nsGkAtoms::fontfamily_);
+ nsCSSValue* fontFamily = aData->ValueForFontFamily();
+ if (value) {
+ WarnDeprecated(nsGkAtoms::fontfamily_->GetUTF16String(),
+ nsGkAtoms::mathvariant_->GetUTF16String(),
+ aData->mPresContext->Document());
+ }
+ if (value && value->Type() == nsAttrValue::eString &&
+ fontFamily->GetUnit() == eCSSUnit_Null) {
+ nsCSSParser parser;
+ parser.ParseFontFamilyListString(value->GetStringValue(),
+ nullptr, 0, *fontFamily);
+ }
+
+ // fontstyle
+ //
+ // "Specified the font style to use for the token. Deprecated in favor of
+ // mathvariant."
+ //
+ // values: "normal" | "italic"
+ // default: normal (except on <mi>)
+ //
+ // Note that the font-style property is reset in layout/style/ when
+ // -moz-math-variant is specified.
+ nsCSSValue* fontStyle = aData->ValueForFontStyle();
+ value = aAttributes->GetAttr(nsGkAtoms::fontstyle_);
+ if (value) {
+ WarnDeprecated(nsGkAtoms::fontstyle_->GetUTF16String(),
+ nsGkAtoms::mathvariant_->GetUTF16String(),
+ aData->mPresContext->Document());
+ if (value->Type() == nsAttrValue::eString &&
+ fontStyle->GetUnit() == eCSSUnit_Null) {
+ nsAutoString str(value->GetStringValue());
+ str.CompressWhitespace();
+ if (str.EqualsASCII("normal")) {
+ fontStyle->SetIntValue(NS_STYLE_FONT_STYLE_NORMAL,
+ eCSSUnit_Enumerated);
+ } else if (str.EqualsASCII("italic")) {
+ fontStyle->SetIntValue(NS_STYLE_FONT_STYLE_ITALIC,
+ eCSSUnit_Enumerated);
+ }
+ }
+ }
+
+ // fontweight
+ //
+ // "Specified the font weight for the token. Deprecated in favor of
+ // mathvariant."
+ //
+ // values: "normal" | "bold"
+ // default: normal
+ //
+ // Note that the font-weight property is reset in layout/style/ when
+ // -moz-math-variant is specified.
+ nsCSSValue* fontWeight = aData->ValueForFontWeight();
+ value = aAttributes->GetAttr(nsGkAtoms::fontweight_);
+ if (value) {
+ WarnDeprecated(nsGkAtoms::fontweight_->GetUTF16String(),
+ nsGkAtoms::mathvariant_->GetUTF16String(),
+ aData->mPresContext->Document());
+ if (value->Type() == nsAttrValue::eString &&
+ fontWeight->GetUnit() == eCSSUnit_Null) {
+ nsAutoString str(value->GetStringValue());
+ str.CompressWhitespace();
+ if (str.EqualsASCII("normal")) {
+ fontWeight->SetIntValue(NS_STYLE_FONT_WEIGHT_NORMAL,
+ eCSSUnit_Enumerated);
+ } else if (str.EqualsASCII("bold")) {
+ fontWeight->SetIntValue(NS_STYLE_FONT_WEIGHT_BOLD,
+ eCSSUnit_Enumerated);
+ }
+ }
+ }
+
+ // mathvariant
+ //
+ // "Specifies the logical class of the token. Note that this class is more
+ // than styling, it typically conveys semantic intent;"
+ //
+ // values: "normal" | "bold" | "italic" | "bold-italic" | "double-struck" |
+ // "bold-fraktur" | "script" | "bold-script" | "fraktur" | "sans-serif" |
+ // "bold-sans-serif" | "sans-serif-italic" | "sans-serif-bold-italic" |
+ // "monospace" | "initial" | "tailed" | "looped" | "stretched"
+ // default: normal (except on <mi>)
+ //
+ nsCSSValue* mathVariant = aData->ValueForMathVariant();
+ value = aAttributes->GetAttr(nsGkAtoms::mathvariant_);
+ if (value && value->Type() == nsAttrValue::eString &&
+ mathVariant->GetUnit() == eCSSUnit_Null) {
+ nsAutoString str(value->GetStringValue());
+ str.CompressWhitespace();
+ static const char sizes[19][23] = {
+ "normal", "bold", "italic", "bold-italic", "script", "bold-script",
+ "fraktur", "double-struck", "bold-fraktur", "sans-serif",
+ "bold-sans-serif", "sans-serif-italic", "sans-serif-bold-italic",
+ "monospace", "initial", "tailed", "looped", "stretched"
+ };
+ static const int32_t values[MOZ_ARRAY_LENGTH(sizes)] = {
+ NS_MATHML_MATHVARIANT_NORMAL, NS_MATHML_MATHVARIANT_BOLD,
+ NS_MATHML_MATHVARIANT_ITALIC, NS_MATHML_MATHVARIANT_BOLD_ITALIC,
+ NS_MATHML_MATHVARIANT_SCRIPT, NS_MATHML_MATHVARIANT_BOLD_SCRIPT,
+ NS_MATHML_MATHVARIANT_FRAKTUR, NS_MATHML_MATHVARIANT_DOUBLE_STRUCK,
+ NS_MATHML_MATHVARIANT_BOLD_FRAKTUR, NS_MATHML_MATHVARIANT_SANS_SERIF,
+ NS_MATHML_MATHVARIANT_BOLD_SANS_SERIF,
+ NS_MATHML_MATHVARIANT_SANS_SERIF_ITALIC,
+ NS_MATHML_MATHVARIANT_SANS_SERIF_BOLD_ITALIC,
+ NS_MATHML_MATHVARIANT_MONOSPACE, NS_MATHML_MATHVARIANT_INITIAL,
+ NS_MATHML_MATHVARIANT_TAILED, NS_MATHML_MATHVARIANT_LOOPED,
+ NS_MATHML_MATHVARIANT_STRETCHED
+ };
+ for (uint32_t i = 0; i < ArrayLength(sizes); ++i) {
+ if (str.EqualsASCII(sizes[i])) {
+ mathVariant->SetIntValue(values[i], eCSSUnit_Enumerated);
+ break;
+ }
+ }
+ }
+ }
+
+ // mathbackground
+ //
+ // "Specifies the background color to be used to fill in the bounding box of
+ // the element and its children. The default, 'transparent', lets the
+ // background color, if any, used in the current rendering context to show
+ // through."
+ //
+ // values: color | "transparent"
+ // default: "transparent"
+ //
+ // background
+ //
+ // "Specified the background color to be used to fill in the bounding box of
+ // the element and its children. Deprecated in favor of mathbackground."
+ //
+ // values: color | "transparent"
+ // default: "transparent"
+ //
+ if (aData->mSIDs & NS_STYLE_INHERIT_BIT(Background)) {
+ const nsAttrValue* value =
+ aAttributes->GetAttr(nsGkAtoms::mathbackground_);
+ if (!value) {
+ value = aAttributes->GetAttr(nsGkAtoms::background);
+ if (value) {
+ WarnDeprecated(nsGkAtoms::background->GetUTF16String(),
+ nsGkAtoms::mathbackground_->GetUTF16String(),
+ aData->mPresContext->Document());
+ }
+ }
+ nsCSSValue* backgroundColor = aData->ValueForBackgroundColor();
+ if (value && backgroundColor->GetUnit() == eCSSUnit_Null) {
+ nscolor color;
+ if (value->GetColorValue(color)) {
+ backgroundColor->SetColorValue(color);
+ }
+ }
+ }
+
+ // mathcolor
+ //
+ // "Specifies the foreground color to use when drawing the components of this
+ // element, such as the content for token elements or any lines, surds, or
+ // other decorations. It also establishes the default mathcolor used for
+ // child elements when used on a layout element."
+ //
+ // values: color
+ // default: inherited
+ //
+ // color
+ //
+ // "Specified the color for the token. Deprecated in favor of mathcolor."
+ //
+ // values: color
+ // default: inherited
+ //
+ if (aData->mSIDs & NS_STYLE_INHERIT_BIT(Color)) {
+ const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::mathcolor_);
+ if (!value) {
+ value = aAttributes->GetAttr(nsGkAtoms::color);
+ if (value) {
+ WarnDeprecated(nsGkAtoms::color->GetUTF16String(),
+ nsGkAtoms::mathcolor_->GetUTF16String(),
+ aData->mPresContext->Document());
+ }
+ }
+ nscolor color;
+ nsCSSValue* colorValue = aData->ValueForColor();
+ if (value && value->GetColorValue(color) &&
+ colorValue->GetUnit() == eCSSUnit_Null) {
+ colorValue->SetColorValue(color);
+ }
+ }
+
+ if (aData->mSIDs & NS_STYLE_INHERIT_BIT(Position)) {
+ // width
+ //
+ // "Specifies the desired width of the entire table and is intended for
+ // visual user agents. When the value is a percentage value, the value is
+ // relative to the horizontal space a MathML renderer has available for the
+ // math element. When the value is "auto", the MathML renderer should
+ // calculate the table width from its contents using whatever layout
+ // algorithm it chooses. "
+ //
+ // values: "auto" | length
+ // default: auto
+ //
+ nsCSSValue* width = aData->ValueForWidth();
+ if (width->GetUnit() == eCSSUnit_Null) {
+ const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::width);
+ // This does not handle auto and unitless values
+ if (value && value->Type() == nsAttrValue::eString) {
+ ParseNumericValue(value->GetStringValue(), *width, 0,
+ aData->mPresContext->Document());
+ }
+ }
+ }
+
+ // dir
+ //
+ // Overall Directionality of Mathematics Formulas:
+ // "The overall directionality for a formula, basically the direction of the
+ // Layout Schemata, is specified by the dir attribute on the containing math
+ // element (see Section 2.2 The Top-Level math Element). The default is ltr.
+ // [...] The overall directionality is usually set on the math, but may also
+ // be switched for individual subformula by using the dir attribute on mrow
+ // or mstyle elements."
+ //
+ // Bidirectional Layout in Token Elements:
+ // "Specifies the initial directionality for text within the token:
+ // ltr (Left To Right) or rtl (Right To Left). This attribute should only be
+ // needed in rare cases involving weak or neutral characters;
+ // see Section 3.1.5.1 Overall Directionality of Mathematics Formulas for
+ // further discussion. It has no effect on mspace."
+ //
+ // values: "ltr" | "rtl"
+ // default: inherited
+ //
+ if (aData->mSIDs & NS_STYLE_INHERIT_BIT(Visibility)) {
+ const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::dir);
+ nsCSSValue* direction = aData->ValueForDirection();
+ if (value && value->Type() == nsAttrValue::eString &&
+ direction->GetUnit() == eCSSUnit_Null) {
+ nsAutoString str(value->GetStringValue());
+ static const char dirs[][4] = { "ltr", "rtl" };
+ static const int32_t dirValues[MOZ_ARRAY_LENGTH(dirs)] = {
+ NS_STYLE_DIRECTION_LTR, NS_STYLE_DIRECTION_RTL
+ };
+ for (uint32_t i = 0; i < ArrayLength(dirs); ++i) {
+ if (str.EqualsASCII(dirs[i])) {
+ direction->SetIntValue(dirValues[i], eCSSUnit_Enumerated);
+ break;
+ }
+ }
+ }
+ }
+}
+
+nsresult
+nsMathMLElement::PreHandleEvent(EventChainPreVisitor& aVisitor)
+{
+ nsresult rv = Element::PreHandleEvent(aVisitor);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return PreHandleEventForLinks(aVisitor);
+}
+
+nsresult
+nsMathMLElement::PostHandleEvent(EventChainPostVisitor& aVisitor)
+{
+ return PostHandleEventForLinks(aVisitor);
+}
+
+NS_IMPL_ELEMENT_CLONE(nsMathMLElement)
+
+EventStates
+nsMathMLElement::IntrinsicState() const
+{
+ return Link::LinkState() | nsMathMLElementBase::IntrinsicState() |
+ (mIncrementScriptLevel ?
+ NS_EVENT_STATE_INCREMENT_SCRIPT_LEVEL : EventStates());
+}
+
+bool
+nsMathMLElement::IsNodeOfType(uint32_t aFlags) const
+{
+ return !(aFlags & ~eCONTENT);
+}
+
+void
+nsMathMLElement::SetIncrementScriptLevel(bool aIncrementScriptLevel,
+ bool aNotify)
+{
+ if (aIncrementScriptLevel == mIncrementScriptLevel)
+ return;
+ mIncrementScriptLevel = aIncrementScriptLevel;
+
+ NS_ASSERTION(aNotify, "We always notify!");
+
+ UpdateState(true);
+}
+
+bool
+nsMathMLElement::IsFocusableInternal(int32_t* aTabIndex, bool aWithMouse)
+{
+ nsCOMPtr<nsIURI> uri;
+ if (IsLink(getter_AddRefs(uri))) {
+ if (aTabIndex) {
+ *aTabIndex = ((sTabFocusModel & eTabFocus_linksMask) == 0 ? -1 : 0);
+ }
+ return true;
+ }
+
+ if (aTabIndex) {
+ *aTabIndex = -1;
+ }
+
+ return false;
+}
+
+bool
+nsMathMLElement::IsLink(nsIURI** aURI) const
+{
+ // http://www.w3.org/TR/2010/REC-MathML3-20101021/chapter6.html#interf.link
+ // The REC says that the following elements should not be linking elements:
+ if (IsAnyOfMathMLElements(nsGkAtoms::mprescripts_, nsGkAtoms::none,
+ nsGkAtoms::malignmark_, nsGkAtoms::maligngroup_)) {
+ *aURI = nullptr;
+ return false;
+ }
+
+ bool hasHref = false;
+ const nsAttrValue* href = mAttrsAndChildren.GetAttr(nsGkAtoms::href,
+ kNameSpaceID_None);
+ if (href) {
+ // MathML href
+ // The REC says: "When user agents encounter MathML elements with both href
+ // and xlink:href attributes, the href attribute should take precedence."
+ hasHref = true;
+ } else {
+ // To be a clickable XLink for styling and interaction purposes, we require:
+ //
+ // xlink:href - must be set
+ // xlink:type - must be unset or set to "" or set to "simple"
+ // xlink:show - must be unset or set to "", "new" or "replace"
+ // xlink:actuate - must be unset or set to "" or "onRequest"
+ //
+ // For any other values, we're either not a *clickable* XLink, or the end
+ // result is poorly specified. Either way, we return false.
+
+ static nsIContent::AttrValuesArray sTypeVals[] =
+ { &nsGkAtoms::_empty, &nsGkAtoms::simple, nullptr };
+
+ static nsIContent::AttrValuesArray sShowVals[] =
+ { &nsGkAtoms::_empty, &nsGkAtoms::_new, &nsGkAtoms::replace, nullptr };
+
+ static nsIContent::AttrValuesArray sActuateVals[] =
+ { &nsGkAtoms::_empty, &nsGkAtoms::onRequest, nullptr };
+
+ // Optimization: check for href first for early return
+ href = mAttrsAndChildren.GetAttr(nsGkAtoms::href,
+ kNameSpaceID_XLink);
+ if (href &&
+ FindAttrValueIn(kNameSpaceID_XLink, nsGkAtoms::type,
+ sTypeVals, eCaseMatters) !=
+ nsIContent::ATTR_VALUE_NO_MATCH &&
+ FindAttrValueIn(kNameSpaceID_XLink, nsGkAtoms::show,
+ sShowVals, eCaseMatters) !=
+ nsIContent::ATTR_VALUE_NO_MATCH &&
+ FindAttrValueIn(kNameSpaceID_XLink, nsGkAtoms::actuate,
+ sActuateVals, eCaseMatters) !=
+ nsIContent::ATTR_VALUE_NO_MATCH) {
+ hasHref = true;
+ }
+ }
+
+ if (hasHref) {
+ nsCOMPtr<nsIURI> baseURI = GetBaseURI();
+ // Get absolute URI
+ nsAutoString hrefStr;
+ href->ToString(hrefStr);
+ nsContentUtils::NewURIWithDocumentCharset(aURI, hrefStr,
+ OwnerDoc(), baseURI);
+ // must promise out param is non-null if we return true
+ return !!*aURI;
+ }
+
+ *aURI = nullptr;
+ return false;
+}
+
+void
+nsMathMLElement::GetLinkTarget(nsAString& aTarget)
+{
+ const nsAttrValue* target = mAttrsAndChildren.GetAttr(nsGkAtoms::target,
+ kNameSpaceID_XLink);
+ if (target) {
+ target->ToString(aTarget);
+ }
+
+ if (aTarget.IsEmpty()) {
+
+ static nsIContent::AttrValuesArray sShowVals[] =
+ { &nsGkAtoms::_new, &nsGkAtoms::replace, nullptr };
+
+ switch (FindAttrValueIn(kNameSpaceID_XLink, nsGkAtoms::show,
+ sShowVals, eCaseMatters)) {
+ case 0:
+ aTarget.AssignLiteral("_blank");
+ return;
+ case 1:
+ return;
+ }
+ OwnerDoc()->GetBaseTarget(aTarget);
+ }
+}
+
+already_AddRefed<nsIURI>
+nsMathMLElement::GetHrefURI() const
+{
+ nsCOMPtr<nsIURI> hrefURI;
+ return IsLink(getter_AddRefs(hrefURI)) ? hrefURI.forget() : nullptr;
+}
+
+nsresult
+nsMathMLElement::SetAttr(int32_t aNameSpaceID, nsIAtom* aName,
+ nsIAtom* aPrefix, const nsAString& aValue,
+ bool aNotify)
+{
+ nsresult rv = nsMathMLElementBase::SetAttr(aNameSpaceID, aName, aPrefix,
+ aValue, aNotify);
+
+ // The ordering of the parent class's SetAttr call and Link::ResetLinkState
+ // is important here! The attribute is not set until SetAttr returns, and
+ // we will need the updated attribute value because notifying the document
+ // that content states have changed will call IntrinsicState, which will try
+ // to get updated information about the visitedness from Link.
+ if (aName == nsGkAtoms::href &&
+ (aNameSpaceID == kNameSpaceID_None ||
+ aNameSpaceID == kNameSpaceID_XLink)) {
+ if (aNameSpaceID == kNameSpaceID_XLink) {
+ WarnDeprecated(u"xlink:href", u"href", OwnerDoc());
+ }
+ Link::ResetLinkState(!!aNotify, true);
+ }
+
+ return rv;
+}
+
+nsresult
+nsMathMLElement::UnsetAttr(int32_t aNameSpaceID, nsIAtom* aAttr,
+ bool aNotify)
+{
+ nsresult rv = nsMathMLElementBase::UnsetAttr(aNameSpaceID, aAttr, aNotify);
+
+ // The ordering of the parent class's UnsetAttr call and Link::ResetLinkState
+ // is important here! The attribute is not unset until UnsetAttr returns, and
+ // we will need the updated attribute value because notifying the document
+ // that content states have changed will call IntrinsicState, which will try
+ // to get updated information about the visitedness from Link.
+ if (aAttr == nsGkAtoms::href &&
+ (aNameSpaceID == kNameSpaceID_None ||
+ aNameSpaceID == kNameSpaceID_XLink)) {
+ // Note: just because we removed a single href attr doesn't mean there's no href,
+ // since there are 2 possible namespaces.
+ Link::ResetLinkState(!!aNotify, Link::ElementHasHref());
+ }
+
+ return rv;
+}
+
+JSObject*
+nsMathMLElement::WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto)
+{
+ return ElementBinding::Wrap(aCx, this, aGivenProto);
+}