Bug 553917 - Add helper methods and logic for error logging to MathML module. r=karlt
authorrfw2nd@gmail.com
Thu, 27 Dec 2012 11:12:05 -0500
changeset 126209 169708ebc9231e786b0d8ff4e9fd3232a10093a2
parent 126208 c9a232a7c3189646e2d60a22664e679e2b6e3d2f
child 126210 206cc6939194fd5b3b490000ce42ed9c907be2f0
push id2151
push userlsblakk@mozilla.com
push dateTue, 19 Feb 2013 18:06:57 +0000
treeherdermozilla-beta@4952e88741ec [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskarlt
bugs553917
milestone20.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 553917 - Add helper methods and logic for error logging to MathML module. r=karlt
content/mathml/content/src/nsMathMLElement.cpp
content/mathml/content/src/nsMathMLElement.h
layout/mathml/nsMathMLContainerFrame.cpp
layout/mathml/nsMathMLContainerFrame.h
layout/mathml/nsMathMLFrame.cpp
layout/mathml/nsMathMLmfracFrame.cpp
layout/mathml/nsMathMLmmultiscriptsFrame.cpp
layout/mathml/nsMathMLmoFrame.cpp
layout/mathml/nsMathMLmpaddedFrame.cpp
layout/mathml/nsMathMLmrootFrame.cpp
layout/mathml/nsMathMLmsubFrame.cpp
layout/mathml/nsMathMLmsubsupFrame.cpp
layout/mathml/nsMathMLmsupFrame.cpp
layout/mathml/nsMathMLmunderoverFrame.cpp
--- a/content/mathml/content/src/nsMathMLElement.cpp
+++ b/content/mathml/content/src/nsMathMLElement.cpp
@@ -13,16 +13,18 @@
 #include "nsCSSValue.h"
 #include "nsMappedAttributes.h"
 #include "nsStyleConsts.h"
 #include "nsIDocument.h"
 #include "nsEventStates.h"
 #include "nsIPresShell.h"
 #include "nsPresContext.h"
 #include "mozAutoDocUpdate.h"
+#include "nsIScriptError.h"
+#include "nsContentUtils.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 //----------------------------------------------------------------------
 // nsISupports methods:
 
 DOMCI_NODE_DATA(MathMLElement, nsMathMLElement)
@@ -36,16 +38,51 @@ NS_INTERFACE_TABLE_HEAD(nsMathMLElement)
   NS_OFFSET_AND_INTERFACE_TABLE_END
   NS_ELEMENT_INTERFACE_TABLE_TO_MAP_SEGUE
   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(MathMLElement)
 NS_ELEMENT_INTERFACE_MAP_END
 
 NS_IMPL_ADDREF_INHERITED(nsMathMLElement, nsMathMLElementBase)
 NS_IMPL_RELEASE_INHERITED(nsMathMLElement, nsMathMLElementBase)
 
+static nsresult 
+WarnDeprecated(const PRUnichar* aDeprecatedAttribute, 
+               const PRUnichar* aFavoredAttribute, nsIDocument* aDocument)
+{
+  const PRUnichar *argv[] = 
+    { aDeprecatedAttribute, aFavoredAttribute };
+  return nsContentUtils::
+          ReportToConsole(nsIScriptError::warningFlag, "MathML", aDocument,
+                          nsContentUtils::eMATHML_PROPERTIES,
+                          "DeprecatedSupersededBy", argv, 2);
+}
+
+static nsresult 
+ReportLengthParseError(const nsString& aValue, nsIDocument* aDocument)
+{
+  const PRUnichar *arg = aValue.get();
+  return nsContentUtils::
+         ReportToConsole(nsIScriptError::errorFlag, "MathML", aDocument,
+                         nsContentUtils::eMATHML_PROPERTIES,
+                         "LengthParsingError", &arg, 1);
+}
+
+static nsresult
+ReportParseErrorNoTag(const nsString& aValue, 
+                      nsIAtom*        aAtom,
+                      nsIDocument*    aDocument)
+{
+  const PRUnichar *argv[] = 
+    { aValue.get(), aAtom->GetUTF16String() };
+  return nsContentUtils::
+         ReportToConsole(nsIScriptError::errorFlag, "MathML", aDocument,
+                         nsContentUtils::eMATHML_PROPERTIES,
+                         "AttributeParsingErrorNoTag", argv, 2);
+}
+
 nsresult
 nsMathMLElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
                             nsIContent* aBindingParent,
                             bool aCompileEventHandlers)
 {
   static const char kMathMLStyleSheetURI[] = "resource://gre-resources/mathml.css";
 
   Link::ResetLinkState(false, Link::ElementHasHref());
@@ -95,16 +132,24 @@ nsMathMLElement::UnbindFromTree(bool aDe
 
 bool
 nsMathMLElement::ParseAttribute(int32_t aNamespaceID,
                                 nsIAtom* aAttribute,
                                 const nsAString& aValue,
                                 nsAttrValue& aResult)
 {
   if (aNamespaceID == kNameSpaceID_None) {
+    if (Tag() == 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);
     }
   }
 
@@ -289,24 +334,29 @@ nsMathMLElement::ParseNamedSpaceValue(co
 //   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)
+                                   uint32_t        aFlags,
+                                   nsIDocument*    aDocument)
 {
   nsAutoString str(aString);
   str.CompressWhitespace(); // aString is const in this code...
 
   int32_t stringLength = str.Length();
-  if (!stringLength)
+  if (!stringLength) {
+    if (!(aFlags & PARSE_SUPPRESS_WARNINGS)) {
+      ReportLengthParseError(aString, aDocument);
+    }
     return false;
+  }
 
   if (ParseNamedSpaceValue(aString, aCSSValue, aFlags)) {
     return true;
   }
 
   nsAutoString number, unit;
 
   // see if the negative sign is there
@@ -316,65 +366,91 @@ nsMathMLElement::ParseNumericValue(const
     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 (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 (NS_FAILED(errorCode)) {
+    if (!(aFlags & PARSE_SUPPRESS_WARNINGS)) {
+      ReportLengthParseError(aString, aDocument);
+    }
     return false;
-  if (floatValue < 0 && !(aFlags & PARSE_ALLOW_NEGATIVE))
+  }
+  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
       cssUnit = eCSSUnit_Number;
+      if (!(aFlags & PARSE_SUPPRESS_WARNINGS)) {
+        nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
+                                        "MathML", aDocument,
+                                        nsContentUtils::eMATHML_PROPERTIES,
+                                        "UnitlessValuesAreDeprecated");
+      }
     } 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 (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 // unexpected unit
+  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)
@@ -398,16 +474,20 @@ nsMathMLElement::MapMathMLAttributesInto
       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."
@@ -418,17 +498,18 @@ nsMathMLElement::MapMathMLAttributesInto
     // We don't allow negative values.
     // XXXfredw Should we allow unitless values? (bug 411227)
     // XXXfredw Does a relative unit 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, 0);
+      ParseNumericValue(value->GetStringValue(), *scriptMinSize, 0,
+                        aData->mPresContext->Document());
     }
 
     // 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
@@ -452,16 +533,20 @@ nsMathMLElement::MapMathMLAttributesInto
           // Integer to indicate that. Otherwise we store it as a Number
           // to indicate that the scriptlevel is absolute.
           PRUnichar 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
@@ -483,23 +568,28 @@ nsMathMLElement::MapMathMLAttributesInto
     // XXXfredw Should we allow unitless values? (bug 411227)
     // XXXfredw Does a relative unit 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, 0) &&
-          parseSizeKeywords) {
+      if (!ParseNumericValue(str, *fontSize, PARSE_SUPPRESS_WARNINGS, nullptr)
+          && parseSizeKeywords) {
         static const char sizes[3][7] = { "small", "normal", "big" };
         static const int32_t values[NS_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])) {
@@ -515,16 +605,21 @@ nsMathMLElement::MapMathMLAttributesInto
     // "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) {
       fontFamily->SetStringValue(value->GetStringValue(), eCSSUnit_Families);
     }
   }
 
   // mathbackground
   // 
@@ -544,16 +639,21 @@ nsMathMLElement::MapMathMLAttributesInto
   // 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);
       }
     }
@@ -575,33 +675,39 @@ nsMathMLElement::MapMathMLAttributesInto
   //
   // 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: value
     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);
+        ParseNumericValue(value->GetStringValue(), *width, 0,
+                          aData->mPresContext->Document());
       }
     }
   }
 
 }
 
 nsresult
 nsMathMLElement::PreHandleEvent(nsEventChainPreVisitor& aVisitor)
@@ -788,16 +894,20 @@ nsMathMLElement::SetAttr(int32_t aNameSp
   // 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(NS_LITERAL_STRING("xlink:href").get(),
+                     NS_LITERAL_STRING("href").get(), OwnerDoc());
+    }
     Link::ResetLinkState(!!aNotify, true);
   }
 
   return rv;
 }
 
 nsresult
 nsMathMLElement::UnsetAttr(int32_t aNameSpaceID, nsIAtom* aAttr,
--- a/content/mathml/content/src/nsMathMLElement.h
+++ b/content/mathml/content/src/nsMathMLElement.h
@@ -48,25 +48,27 @@ public:
                                 const nsAString& aValue,
                                 nsAttrValue& aResult);
 
   NS_IMETHOD_(bool) IsAttributeMapped(const nsIAtom* aAttribute) const;
   virtual nsMapRuleToAttributesFunc GetAttributeMappingFunction() const;
 
   enum {
     PARSE_ALLOW_UNITLESS = 0x01, // unitless 0 will be turned into 0px
-    PARSE_ALLOW_NEGATIVE = 0x02
+    PARSE_ALLOW_NEGATIVE = 0x02,
+    PARSE_SUPPRESS_WARNINGS = 0x04
   };
   static bool ParseNamedSpaceValue(const nsString& aString,
                                    nsCSSValue&     aCSSValue,
                                    uint32_t        aFlags);
 
   static bool ParseNumericValue(const nsString& aString,
-                                  nsCSSValue&     aCSSValue,
-                                  uint32_t        aFlags);
+                                nsCSSValue&     aCSSValue,
+                                uint32_t        aFlags,
+                                nsIDocument*    aDocument);
 
   static void MapMathMLAttributesInto(const nsMappedAttributes* aAttributes, 
                                       nsRuleData* aRuleData);
   
   virtual nsresult PreHandleEvent(nsEventChainPreVisitor& aVisitor);
   virtual nsresult PostHandleEvent(nsEventChainPostVisitor& aVisitor);
   nsresult Clone(nsINodeInfo*, nsINode**) const;
   virtual nsEventStates IntrinsicState() const;
--- a/layout/mathml/nsMathMLContainerFrame.cpp
+++ b/layout/mathml/nsMathMLContainerFrame.cpp
@@ -22,16 +22,18 @@
 #include "nsMathMLParts.h"
 #include "nsMathMLContainerFrame.h"
 #include "nsAutoPtr.h"
 #include "nsStyleSet.h"
 #include "nsDisplayList.h"
 #include "nsCSSFrameConstructor.h"
 #include "nsIReflowCallback.h"
 #include "mozilla/Likely.h"
+#include "nsIScriptError.h"
+#include "nsContentUtils.h"
 
 using namespace mozilla;
 
 //
 // nsMathMLContainerFrame implementation
 //
 
 NS_IMPL_FRAMEARENA_HELPERS(nsMathMLContainerFrame)
@@ -1512,16 +1514,43 @@ nsMathMLContainerFrame::TransmitAutomati
   if (childFrame || embellishedOpFound) {
     // The element is not space-like
     mPresentationData.flags &= ~NS_MATHML_SPACE_LIKE;
   }
 
   return NS_OK;
 }
 
+nsresult
+nsMathMLContainerFrame::ReportErrorToConsole(const char*       errorMsgId,
+                                             const PRUnichar** aParams,
+                                             PRUint32          aParamCount)
+{
+  return nsContentUtils::ReportToConsole(nsIScriptError::errorFlag,
+                                         "MathML", mContent->OwnerDoc(),
+                                         nsContentUtils::eMATHML_PROPERTIES,
+                                         errorMsgId, aParams, aParamCount);
+}
+
+nsresult
+nsMathMLContainerFrame::ReportParseError(const PRUnichar* aAttribute,
+                                         const PRUnichar* aValue)
+{
+  const PRUnichar* argv[] = 
+    { aValue, aAttribute, mContent->Tag()->GetUTF16String() };
+  return ReportErrorToConsole("AttributeParsingError", argv, 3);
+}
+
+nsresult
+nsMathMLContainerFrame::ReportChildCountError()
+{
+  const PRUnichar* arg = mContent->Tag()->GetUTF16String();
+  return ReportErrorToConsole("ChildCountIncorrect", &arg, 1);
+}
+
 //==========================
 
 nsIFrame*
 NS_NewMathMLmathBlockFrame(nsIPresShell* aPresShell, nsStyleContext* aContext,
                            uint32_t aFlags)
 {
   nsMathMLmathBlockFrame* it = new (aPresShell) nsMathMLmathBlockFrame(aContext);
   if (it) {
--- a/layout/mathml/nsMathMLContainerFrame.h
+++ b/layout/mathml/nsMathMLContainerFrame.h
@@ -238,16 +238,41 @@ protected:
   TransmitAutomaticDataForMrowLikeElement();
 
 public:
   // error handlers to provide a visual feedback to the user when an error
   // (typically invalid markup) was encountered during reflow.
   nsresult
   ReflowError(nsRenderingContext& aRenderingContext,
               nsHTMLReflowMetrics& aDesiredSize);
+  /*
+   * Helper to call ReportErrorToConsole for parse errors involving 
+   * attribute/value pairs.
+   * @param aAttribute The attribute for which the parse error occured.
+   * @param aValue The value for which the parse error occured.
+   */
+  nsresult
+  ReportParseError(const PRUnichar*           aAttribute,
+                   const PRUnichar*           aValue);
+
+  /*
+   * Helper to call ReportErrorToConsole when certain tags
+   * have more than the expected amount of children.
+   */
+  nsresult
+  ReportChildCountError();
+
+  /*
+   * Helper to call ReportToConsole when an error occurs.
+   * @param aParams see nsContentUtils::ReportToConsole
+   */
+  nsresult
+  ReportErrorToConsole(const char*       aErrorMsgId,
+                       const PRUnichar** aParams = nullptr,
+                       PRUint32          aParamCount = 0);
 
   // helper method to reflow a child frame. We are inline frames, and we don't
   // know our positions until reflow is finished. That's why we ask the
   // base method not to worry about our position.
   nsresult 
   ReflowChild(nsIFrame*                aKidFrame,
               nsPresContext*          aPresContext,
               nsHTMLReflowMetrics&     aDesiredSize,
--- a/layout/mathml/nsMathMLFrame.cpp
+++ b/layout/mathml/nsMathMLFrame.cpp
@@ -8,16 +8,18 @@
 #include "nsMathMLChar.h"
 #include "nsCSSPseudoElements.h"
 
 // used to map attributes into CSS rules
 #include "nsStyleSet.h"
 #include "nsAutoPtr.h"
 #include "nsDisplayList.h"
 #include "nsRenderingContext.h"
+#include "nsContentUtils.h"
+#include "nsIScriptError.h"
 
 eMathMLFrameType
 nsMathMLFrame::GetMathMLFrameType()
 {
   // see if it is an embellished operator (mapped to 'Op' in TeX)
   if (mEmbellishData.coreFrame)
     return GetMathMLFrameTypeFor(mEmbellishData.coreFrame);
 
@@ -350,17 +352,18 @@ nsMathMLFrame::CalcLength(nsPresContext*
 nsMathMLFrame::ParseNumericValue(const nsString&   aString,
                                  nscoord*          aLengthValue,
                                  uint32_t          aFlags,
                                  nsPresContext*    aPresContext,
                                  nsStyleContext*   aStyleContext)
 {
   nsCSSValue cssValue;
 
-  if (!nsMathMLElement::ParseNumericValue(aString, cssValue, aFlags)) {
+  if (!nsMathMLElement::ParseNumericValue(aString, cssValue, aFlags,
+                                          aPresContext->Document())) {
     // Invalid attribute value. aLengthValue remains unchanged, so the default
     // length value is used.
     return;
   }
 
   nsCSSUnit unit = cssValue.GetUnit();
 
   if (unit == eCSSUnit_Percent || unit == eCSSUnit_Number) {
--- a/layout/mathml/nsMathMLmfracFrame.cpp
+++ b/layout/mathml/nsMathMLmfracFrame.cpp
@@ -194,16 +194,19 @@ nsMathMLmfracFrame::PlaceInternal(nsRend
   nsHTMLReflowMetrics sizeNum;
   nsHTMLReflowMetrics sizeDen;
   nsIFrame* frameDen = nullptr;
   nsIFrame* frameNum = mFrames.FirstChild();
   if (frameNum) 
     frameDen = frameNum->GetNextSibling();
   if (!frameNum || !frameDen || frameDen->GetNextSibling()) {
     // report an error, encourage people to get their markups in order
+    if (aPlaceOrigin) {
+      ReportChildCountError();
+    }
     return ReflowError(aRenderingContext, aDesiredSize);
   }
   GetReflowAndBoundingMetricsFor(frameNum, sizeNum, bmNum);
   GetReflowAndBoundingMetricsFor(frameDen, sizeDen, bmDen);
 
   nsPresContext* presContext = PresContext();
   nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1);
 
--- a/layout/mathml/nsMathMLmmultiscriptsFrame.cpp
+++ b/layout/mathml/nsMathMLmmultiscriptsFrame.cpp
@@ -246,16 +246,19 @@ nsMathMLmmultiscriptsFrame::Place(nsRend
   aDesiredSize.width = aDesiredSize.height = 0;
 
   nsIFrame* childFrame = mFrames.FirstChild();
   while (childFrame) {
     if (childFrame->GetContent()->Tag() == nsGkAtoms::mprescripts_) {
       if (mprescriptsFrame) {
         // duplicate <mprescripts/> found
         // report an error, encourage people to get their markups in order
+        if (aPlaceOrigin) {
+          ReportErrorToConsole("DuplicateMprescripts");
+        }
         return ReflowError(aRenderingContext, aDesiredSize);
       }
       mprescriptsFrame = childFrame;
       firstPrescriptsPair = true;
     }
     else {
       if (0 == count) {
         // base
@@ -354,16 +357,23 @@ nsMathMLmmultiscriptsFrame::Place(nsRend
       isSubScript = !isSubScript;
     }
     count++;
     childFrame = childFrame->GetNextSibling();
   }
   // note: width=0 if all sup-sub pairs match correctly
   if ((0 != width) || !baseFrame || !subScriptFrame || !supScriptFrame) {
     // report an error, encourage people to get their markups in order
+    if (aPlaceOrigin) {
+      if (count <= 1 || (count == 2 && mprescriptsFrame)) {
+        ReportErrorToConsole("NoSubSup");
+      } else {
+        ReportErrorToConsole("SubSupMismatch");
+      }
+    }
     return ReflowError(aRenderingContext, aDesiredSize);
   }
 
   // we left out the width of prescripts, so ...
   mBoundingMetrics.rightBearing += prescriptsWidth;
   mBoundingMetrics.width += prescriptsWidth;
 
   // we left out the base during our bounding box updates, so ...
--- a/layout/mathml/nsMathMLmoFrame.cpp
+++ b/layout/mathml/nsMathMLmoFrame.cpp
@@ -401,17 +401,18 @@ nsMathMLmoFrame::ProcessOperatorData()
   // XXXfredw Should we allow relative values? They will give a multiple of the
   // current leading space, which is not necessarily the default one.
   //
   nscoord leadingSpace = mEmbellishData.leadingSpace;
   GetAttribute(mContent, mPresentationData.mstyle, nsGkAtoms::lspace_,
                value);
   if (!value.IsEmpty()) {
     nsCSSValue cssValue;
-    if (nsMathMLElement::ParseNumericValue(value, cssValue, 0)) {
+    if (nsMathMLElement::ParseNumericValue(value, cssValue, 0,
+                                           mContent->OwnerDoc())) {
       if ((eCSSUnit_Number == cssValue.GetUnit()) && !cssValue.GetFloatValue())
         leadingSpace = 0;
       else if (cssValue.IsLengthUnit())
         leadingSpace = CalcLength(presContext, mStyleContext, cssValue);
       mFlags |= NS_MATHML_OPERATOR_LSPACE_ATTR;
     }
   }
 
@@ -427,17 +428,18 @@ nsMathMLmoFrame::ProcessOperatorData()
   // XXXfredw Should we allow relative values? They will give a multiple of the
   // current trailing space, which is not necessarily the default one.
   //
   nscoord trailingSpace = mEmbellishData.trailingSpace;
   GetAttribute(mContent, mPresentationData.mstyle, nsGkAtoms::rspace_,
                value);
   if (!value.IsEmpty()) {
     nsCSSValue cssValue;
-    if (nsMathMLElement::ParseNumericValue(value, cssValue, 0)) {
+    if (nsMathMLElement::ParseNumericValue(value, cssValue, 0,
+                                           mContent->OwnerDoc())) {
       if ((eCSSUnit_Number == cssValue.GetUnit()) && !cssValue.GetFloatValue())
         trailingSpace = 0;
       else if (cssValue.IsLengthUnit())
         trailingSpace = CalcLength(presContext, mStyleContext, cssValue);
       mFlags |= NS_MATHML_OPERATOR_RSPACE_ATTR;
     }
   }
 
@@ -512,17 +514,18 @@ nsMathMLmoFrame::ProcessOperatorData()
   //
   mMinSize = 0.0;
   GetAttribute(mContent, mPresentationData.mstyle, nsGkAtoms::minsize_,
                value);
   if (!value.IsEmpty()) {
     nsCSSValue cssValue;
     if (nsMathMLElement::ParseNumericValue(value, cssValue,
                                            nsMathMLElement::
-                                           PARSE_ALLOW_UNITLESS)) {
+                                           PARSE_ALLOW_UNITLESS,
+                                           mContent->OwnerDoc())) {
       nsCSSUnit unit = cssValue.GetUnit();
       if (eCSSUnit_Number == unit)
         mMinSize = cssValue.GetFloatValue();
       else if (eCSSUnit_Percent == unit)
         mMinSize = cssValue.GetPercentValue();
       else if (eCSSUnit_Null != unit) {
         mMinSize = float(CalcLength(presContext, mStyleContext, cssValue));
         mFlags |= NS_MATHML_OPERATOR_MINSIZE_ABSOLUTE;
@@ -544,17 +547,18 @@ nsMathMLmoFrame::ProcessOperatorData()
   //
   mMaxSize = NS_MATHML_OPERATOR_SIZE_INFINITY;
   GetAttribute(mContent, mPresentationData.mstyle, nsGkAtoms::maxsize_,
                value);
   if (!value.IsEmpty()) {
     nsCSSValue cssValue;
     if (nsMathMLElement::ParseNumericValue(value, cssValue,
                                            nsMathMLElement::
-                                           PARSE_ALLOW_UNITLESS)) {
+                                           PARSE_ALLOW_UNITLESS,
+                                           mContent->OwnerDoc())) {
       nsCSSUnit unit = cssValue.GetUnit();
       if (eCSSUnit_Number == unit)
         mMaxSize = cssValue.GetFloatValue();
       else if (eCSSUnit_Percent == unit)
         mMaxSize = cssValue.GetPercentValue();
       else if (eCSSUnit_Null != unit) {
         mMaxSize = float(CalcLength(presContext, mStyleContext, cssValue));
         mFlags |= NS_MATHML_OPERATOR_MAXSIZE_ABSOLUTE;
--- a/layout/mathml/nsMathMLmpaddedFrame.cpp
+++ b/layout/mathml/nsMathMLmpaddedFrame.cpp
@@ -77,47 +77,57 @@ nsMathMLmpaddedFrame::ProcessAttributes(
   */
 
   // See if attributes are local, don't access mstyle !
 
   // width
   mWidthSign = NS_MATHML_SIGN_INVALID;
   GetAttribute(mContent, nullptr, nsGkAtoms::width, value);
   if (!value.IsEmpty()) {
-    ParseAttribute(value, mWidthSign, mWidth, mWidthPseudoUnit);
+    if (!ParseAttribute(value, mWidthSign, mWidth, mWidthPseudoUnit)) {      
+      ReportParseError(nsGkAtoms::width->GetUTF16String(), value.get());
+    }
   }
 
   // height
   mHeightSign = NS_MATHML_SIGN_INVALID;
   GetAttribute(mContent, nullptr, nsGkAtoms::height, value);
   if (!value.IsEmpty()) {
-    ParseAttribute(value, mHeightSign, mHeight, mHeightPseudoUnit);
+    if (!ParseAttribute(value, mHeightSign, mHeight, mHeightPseudoUnit)) {
+      ReportParseError(nsGkAtoms::height->GetUTF16String(), value.get());
+    }
   }
 
   // depth
   mDepthSign = NS_MATHML_SIGN_INVALID;
   GetAttribute(mContent, nullptr, nsGkAtoms::depth_, value);
   if (!value.IsEmpty()) {
-    ParseAttribute(value, mDepthSign, mDepth, mDepthPseudoUnit);
+    if (!ParseAttribute(value, mDepthSign, mDepth, mDepthPseudoUnit)) {
+      ReportParseError(nsGkAtoms::depth_->GetUTF16String(), value.get());
+    }
   }
 
   // lspace
   mLeadingSpaceSign = NS_MATHML_SIGN_INVALID;
   GetAttribute(mContent, nullptr, nsGkAtoms::lspace_, value);
   if (!value.IsEmpty()) {
-    ParseAttribute(value, mLeadingSpaceSign, mLeadingSpace,
-                   mLeadingSpacePseudoUnit);
+    if (!ParseAttribute(value, mLeadingSpaceSign, mLeadingSpace, 
+                        mLeadingSpacePseudoUnit)) {
+      ReportParseError(nsGkAtoms::lspace_->GetUTF16String(), value.get());
+    }
   }
 
   // voffset
   mVerticalOffsetSign = NS_MATHML_SIGN_INVALID;
   GetAttribute(mContent, nullptr, nsGkAtoms::voffset_, value);
   if (!value.IsEmpty()) {
-    ParseAttribute(value, mVerticalOffsetSign, mVerticalOffset, 
-                   mVerticalOffsetPseudoUnit);
+    if (!ParseAttribute(value, mVerticalOffsetSign, mVerticalOffset,
+                        mVerticalOffsetPseudoUnit)) {
+      ReportParseError(nsGkAtoms::voffset_->GetUTF16String(), value.get());
+    }
   }
   
 }
 
 // parse an input string in the following format (see bug 148326 for testcases):
 // [+|-] unsigned-number (% [pseudo-unit] | pseudo-unit | css-unit | namedspace)
 bool
 nsMathMLmpaddedFrame::ParseAttribute(nsString&   aString,
@@ -169,20 +179,16 @@ nsMathMLmpaddedFrame::ParseAttribute(nsS
     }
     number.Append(c);
   }
 
   // catch error if we didn't enter the loop above... we could simply initialize
   // floatValue = 1, to cater for cases such as width="height", but that wouldn't
   // be in line with the spec which requires an explicit number
   if (number.IsEmpty()) {
-#ifdef DEBUG
-    printf("mpadded: attribute with bad numeric value: %s\n",
-            NS_LossyConvertUTF16toASCII(aString).get());
-#endif
     aSign = NS_MATHML_SIGN_INVALID;
     return false;
   }
 
   nsresult errorCode;
   float floatValue = number.ToFloat(&errorCode);
   if (NS_FAILED(errorCode)) {
     aSign = NS_MATHML_SIGN_INVALID;
@@ -229,17 +235,19 @@ nsMathMLmpaddedFrame::ParseAttribute(nsS
       aPseudoUnit = NS_MATHML_PSEUDO_UNIT_NAMEDSPACE;
       return true;
     }
 
     // see if the input was just a CSS value
     // We are not supposed to have a unitless, percent, negative or namedspace
     // value here.
     number.Append(unit); // leave the sign out if it was there
-    if (nsMathMLElement::ParseNumericValue(number, aCSSValue, 0))
+    if (nsMathMLElement::ParseNumericValue(number, aCSSValue, 
+                                           nsMathMLElement::
+                                           PARSE_SUPPRESS_WARNINGS, nullptr))
       return true;
   }
 
   // if we enter here, we have a number that will act as a multiplier on a pseudo-unit
   if (aPseudoUnit != NS_MATHML_PSEUDO_UNIT_UNSPECIFIED) {
     if (gotPercent)
       aCSSValue.SetPercentValue(floatValue / 100.0f);
     else
--- a/layout/mathml/nsMathMLmrootFrame.cpp
+++ b/layout/mathml/nsMathMLmrootFrame.cpp
@@ -205,16 +205,17 @@ nsMathMLmrootFrame::Reflow(nsPresContext
       indexSize = childDesiredSize;
       bmIndex = childDesiredSize.mBoundingMetrics;
     }
     count++;
     childFrame = childFrame->GetNextSibling();
   }
   if (2 != count) {
     // report an error, encourage people to get their markups in order
+    ReportChildCountError();
     rv = ReflowError(renderingContext, aDesiredSize);
     aStatus = NS_FRAME_COMPLETE;
     NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize);
     // Call DidReflow() for the child frames we successfully did reflow.
     DidReflowChildren(mFrames.FirstChild(), childFrame);
     return rv;
   }
 
--- a/layout/mathml/nsMathMLmsubFrame.cpp
+++ b/layout/mathml/nsMathMLmsubFrame.cpp
@@ -105,16 +105,19 @@ nsMathMLmsubFrame::PlaceSubScript (nsPre
   nsHTMLReflowMetrics baseSize;
   nsHTMLReflowMetrics subScriptSize;
   nsIFrame* baseFrame = aFrame->GetFirstPrincipalChild();
   nsIFrame* subScriptFrame = nullptr;
   if (baseFrame)
     subScriptFrame = baseFrame->GetNextSibling();
   if (!baseFrame || !subScriptFrame || subScriptFrame->GetNextSibling()) {
     // report an error, encourage people to get their markups in order
+    if (aPlaceOrigin) {
+      aFrame->ReportChildCountError();
+    }
     return aFrame->ReflowError(aRenderingContext, aDesiredSize);
   }
   GetReflowAndBoundingMetricsFor(baseFrame, baseSize, bmBase);
   GetReflowAndBoundingMetricsFor(subScriptFrame, subScriptSize, bmSubScript);
 
   // get the subdrop from the subscript font
   nscoord subDrop;
   GetSubDropFromChild(subScriptFrame, subDrop);
--- a/layout/mathml/nsMathMLmsubsupFrame.cpp
+++ b/layout/mathml/nsMathMLmsubsupFrame.cpp
@@ -138,16 +138,19 @@ nsMathMLmsubsupFrame::PlaceSubSupScript(
   nsIFrame* baseFrame = aFrame->GetFirstPrincipalChild();
   if (baseFrame)
     subScriptFrame = baseFrame->GetNextSibling();
   if (subScriptFrame)
     supScriptFrame = subScriptFrame->GetNextSibling();
   if (!baseFrame || !subScriptFrame || !supScriptFrame ||
       supScriptFrame->GetNextSibling()) {
     // report an error, encourage people to get their markups in order
+    if (aPlaceOrigin) {
+      aFrame->ReportChildCountError();
+    }
     return aFrame->ReflowError(aRenderingContext, aDesiredSize);
   }
   GetReflowAndBoundingMetricsFor(baseFrame, baseSize, bmBase);
   GetReflowAndBoundingMetricsFor(subScriptFrame, subScriptSize, bmSubScript);
   GetReflowAndBoundingMetricsFor(supScriptFrame, supScriptSize, bmSupScript);
 
   // get the subdrop from the subscript font
   nscoord subDrop;
--- a/layout/mathml/nsMathMLmsupFrame.cpp
+++ b/layout/mathml/nsMathMLmsupFrame.cpp
@@ -106,16 +106,19 @@ nsMathMLmsupFrame::PlaceSuperScript(nsPr
   nsHTMLReflowMetrics supScriptSize;
   nsBoundingMetrics bmBase, bmSupScript;
   nsIFrame* supScriptFrame = nullptr;
   nsIFrame* baseFrame = aFrame->GetFirstPrincipalChild();
   if (baseFrame)
     supScriptFrame = baseFrame->GetNextSibling();
   if (!baseFrame || !supScriptFrame || supScriptFrame->GetNextSibling()) {
     // report an error, encourage people to get their markups in order
+    if (aPlaceOrigin) {
+      aFrame->ReportChildCountError();
+    }
     return aFrame->ReflowError(aRenderingContext, aDesiredSize);
   }
   GetReflowAndBoundingMetricsFor(baseFrame, baseSize, bmBase);
   GetReflowAndBoundingMetricsFor(supScriptFrame, supScriptSize, bmSupScript);
 
   // get the supdrop from the supscript font
   nscoord supDrop;
   GetSupDropFromChild(supScriptFrame, supDrop);
--- a/layout/mathml/nsMathMLmunderoverFrame.cpp
+++ b/layout/mathml/nsMathMLmunderoverFrame.cpp
@@ -341,46 +341,53 @@ nsMathMLmunderoverFrame::Place(nsRenderi
   nsHTMLReflowMetrics baseSize;
   nsHTMLReflowMetrics underSize;
   nsHTMLReflowMetrics overSize;
   nsIFrame* overFrame = nullptr;
   nsIFrame* underFrame = nullptr;
   nsIFrame* baseFrame = mFrames.FirstChild();
   underSize.ascent = 0; 
   overSize.ascent = 0;
+  bool haveError = false;
   if (baseFrame) {
     if (tag == nsGkAtoms::munder_ ||
         tag == nsGkAtoms::munderover_) {
       underFrame = baseFrame->GetNextSibling();
     } else if (tag == nsGkAtoms::mover_) {
       overFrame = baseFrame->GetNextSibling();
     }
   }
   if (underFrame && tag == nsGkAtoms::munderover_) {
     overFrame = underFrame->GetNextSibling();
   }
   
   if (tag == nsGkAtoms::munder_) {
     if (!baseFrame || !underFrame || underFrame->GetNextSibling()) {
       // report an error, encourage people to get their markups in order
-      return ReflowError(aRenderingContext, aDesiredSize);
+      haveError = true;
     }
   }
   if (tag == nsGkAtoms::mover_) {
     if (!baseFrame || !overFrame || overFrame->GetNextSibling()) {
       // report an error, encourage people to get their markups in order
-      return ReflowError(aRenderingContext, aDesiredSize);
+      haveError = true;
     }
   }
   if (tag == nsGkAtoms::munderover_) {
     if (!baseFrame || !underFrame || !overFrame || overFrame->GetNextSibling()) {
       // report an error, encourage people to get their markups in order
-      return ReflowError(aRenderingContext, aDesiredSize);
+      haveError = true;
     }
   }
+  if (haveError) {
+    if (aPlaceOrigin) {
+      ReportChildCountError();
+    } 
+    return ReflowError(aRenderingContext, aDesiredSize);
+  }
   GetReflowAndBoundingMetricsFor(baseFrame, baseSize, bmBase);
   if (underFrame) {
     GetReflowAndBoundingMetricsFor(underFrame, underSize, bmUnder);
   }
   if (overFrame) {
     GetReflowAndBoundingMetricsFor(overFrame, overSize, bmOver);
   }