Bug 1173199 - Create preference to disable MathML. r?heycam, huseby, smaug draft
authorJonathan Kingston <jkt@mozilla.com>
Tue, 28 Jun 2016 15:24:48 +0100
changeset 395478 687fa2165557292a1904a17fa4334c607e51a36b
parent 394995 ffac2798999c5b84f1b4605a1280994bb665a406
child 527007 8112f544d2f4d7f794a54a353ba611022647be2b
push id24787
push userjkingston@mozilla.com
push dateTue, 02 Aug 2016 12:25:04 +0000
reviewersheycam, huseby, smaug
bugs1173199
milestone51.0a1
Bug 1173199 - Create preference to disable MathML. r?heycam, huseby, smaug If the mathml.disabled preference is true, treat <math> and other MathML elements as generic XML elements. This patch disables the rendering code of MathML however preserves the namespace so to reduce the breakage. Original patch by: Kathy Brade <brade@pearlcrescent.com> MozReview-Commit-ID: A2f2Q2b4eqR
dom/base/Element.cpp
dom/base/NameSpaceConstants.h
dom/base/NodeInfo.cpp
dom/base/nsContentUtils.cpp
dom/base/nsDOMAttributeMap.cpp
dom/base/nsIContent.h
dom/base/nsIContentInlines.h
dom/base/nsNameSpaceManager.cpp
dom/base/nsNameSpaceManager.h
dom/svg/SVGAnimationElement.cpp
dom/svg/SVGAnimationElement.h
dom/svg/SVGGraphicsElement.cpp
dom/svg/SVGGraphicsElement.h
dom/svg/SVGSymbolElement.cpp
dom/svg/SVGSymbolElement.h
dom/svg/SVGTests.cpp
dom/svg/SVGTests.h
dom/svg/nsSVGFeatures.cpp
dom/svg/nsSVGFeatures.h
dom/xbl/nsXBLPrototypeBinding.cpp
dom/xml/nsXMLContentSink.cpp
layout/mathml/moz.build
layout/mathml/tests/chrome.ini
layout/mathml/tests/mathml_example_test.html
layout/mathml/tests/mochitest.ini
layout/mathml/tests/test_disabled.html
layout/mathml/tests/test_disabled_chrome.html
layout/reftests/mathml/disabled-scriptlevel-1-ref.html
layout/reftests/mathml/disabled-scriptlevel-1-ref.xhtml
layout/reftests/mathml/disabled-scriptlevel-1.html
layout/reftests/mathml/disabled-scriptlevel-1.xhtml
layout/reftests/mathml/reftest.list
layout/style/ServoBindings.cpp
layout/style/ServoElementSnapshot.cpp
layout/style/ServoElementSnapshot.h
modules/libpref/init/all.js
testing/web-platform/mozilla/meta/MANIFEST.json
testing/web-platform/mozilla/meta/html/syntax/parsing/math-parse01.html.ini
testing/web-platform/mozilla/tests/html/syntax/parsing/math-parse01.html
--- a/dom/base/Element.cpp
+++ b/dom/base/Element.cpp
@@ -1253,17 +1253,18 @@ Element::RemoveAttributeNode(Attr& aAttr
 }
 
 void
 Element::GetAttributeNS(const nsAString& aNamespaceURI,
                         const nsAString& aLocalName,
                         nsAString& aReturn)
 {
   int32_t nsid =
-    nsContentUtils::NameSpaceManager()->GetNameSpaceID(aNamespaceURI);
+    nsContentUtils::NameSpaceManager()->GetNameSpaceID(aNamespaceURI,
+                                                       nsContentUtils::IsChromeDoc(OwnerDoc()));
 
   if (nsid == kNameSpaceID_Unknown) {
     // Unknown namespace means no attribute.
     SetDOMStringToNull(aReturn);
     return;
   }
 
   nsCOMPtr<nsIAtom> name = NS_Atomize(aLocalName);
@@ -1295,17 +1296,18 @@ Element::SetAttributeNS(const nsAString&
 
 void
 Element::RemoveAttributeNS(const nsAString& aNamespaceURI,
                            const nsAString& aLocalName,
                            ErrorResult& aError)
 {
   nsCOMPtr<nsIAtom> name = NS_Atomize(aLocalName);
   int32_t nsid =
-    nsContentUtils::NameSpaceManager()->GetNameSpaceID(aNamespaceURI);
+    nsContentUtils::NameSpaceManager()->GetNameSpaceID(aNamespaceURI,
+                                                       nsContentUtils::IsChromeDoc(OwnerDoc()));
 
   if (nsid == kNameSpaceID_Unknown) {
     // If the namespace ID is unknown, it means there can't possibly be an
     // existing attribute. We would need a known namespace ID to pass into
     // UnsetAttr, so we return early if we don't have one.
     return;
   }
 
@@ -1372,17 +1374,18 @@ Element::GetElementsByTagNameNS(const ns
   return NS_OK;
 }
 
 bool
 Element::HasAttributeNS(const nsAString& aNamespaceURI,
                         const nsAString& aLocalName) const
 {
   int32_t nsid =
-    nsContentUtils::NameSpaceManager()->GetNameSpaceID(aNamespaceURI);
+    nsContentUtils::NameSpaceManager()->GetNameSpaceID(aNamespaceURI,
+                                                       nsContentUtils::IsChromeDoc(OwnerDoc()));
 
   if (nsid == kNameSpaceID_Unknown) {
     // Unknown namespace means no attr...
     return false;
   }
 
   nsCOMPtr<nsIAtom> name = NS_Atomize(aLocalName);
   return HasAttr(nsid, name);
--- a/dom/base/NameSpaceConstants.h
+++ b/dom/base/NameSpaceConstants.h
@@ -18,11 +18,12 @@ static const int32_t kNameSpaceID_None =
 #define kNameSpaceID_XHTML    3
 #define kNameSpaceID_XLink    4
 #define kNameSpaceID_XSLT     5
 #define kNameSpaceID_XBL      6
 #define kNameSpaceID_MathML   7
 #define kNameSpaceID_RDF      8
 #define kNameSpaceID_XUL      9
 #define kNameSpaceID_SVG      10
-#define kNameSpaceID_LastBuiltin          10 // last 'built-in' namespace
+#define kNameSpaceID_disabled_MathML      11
+#define kNameSpaceID_LastBuiltin          11 // last 'built-in' namespace
 
 #endif // mozilla_dom_NameSpaceConstants_h__
--- a/dom/base/NodeInfo.cpp
+++ b/dom/base/NodeInfo.cpp
@@ -192,17 +192,18 @@ NodeInfo::GetNamespaceURI(nsAString& aNa
     SetDOMStringToNull(aNameSpaceURI);
   }
 }
 
 bool
 NodeInfo::NamespaceEquals(const nsAString& aNamespaceURI) const
 {
   int32_t nsid =
-    nsContentUtils::NameSpaceManager()->GetNameSpaceID(aNamespaceURI);
+    nsContentUtils::NameSpaceManager()->GetNameSpaceID(aNamespaceURI,
+      nsContentUtils::IsChromeDoc(mOwnerManager->GetDocument()));
 
   return mozilla::dom::NodeInfo::NamespaceEquals(nsid);
 }
 
 void
 NodeInfo::DeleteCycleCollectable()
 {
   RefPtr<nsNodeInfoManager> kungFuDeathGrip = mOwnerManager;
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -2887,17 +2887,18 @@ nsContentUtils::SplitQName(const nsICont
     const char16_t* end;
     aQName.EndReading(end);
     nsAutoString nameSpace;
     rv = aNamespaceResolver->LookupNamespaceURIInternal(Substring(aQName.get(),
                                                                   colon),
                                                         nameSpace);
     NS_ENSURE_SUCCESS(rv, rv);
 
-    *aNamespace = NameSpaceManager()->GetNameSpaceID(nameSpace);
+    *aNamespace = NameSpaceManager()->GetNameSpaceID(nameSpace,
+                                                     nsContentUtils::IsChromeDoc(aNamespaceResolver->OwnerDoc()));
     if (*aNamespace == kNameSpaceID_Unknown)
       return NS_ERROR_FAILURE;
 
     *aLocalName = NS_Atomize(Substring(colon + 1, end)).take();
   }
   else {
     *aNamespace = kNameSpaceID_None;
     *aLocalName = NS_Atomize(aQName).take();
--- a/dom/base/nsDOMAttributeMap.cpp
+++ b/dom/base/nsDOMAttributeMap.cpp
@@ -441,17 +441,18 @@ nsDOMAttributeMap::GetAttrNodeInfo(const
   if (!mContent) {
     return nullptr;
   }
 
   int32_t nameSpaceID = kNameSpaceID_None;
 
   if (!aNamespaceURI.IsEmpty()) {
     nameSpaceID =
-      nsContentUtils::NameSpaceManager()->GetNameSpaceID(aNamespaceURI);
+      nsContentUtils::NameSpaceManager()->GetNameSpaceID(aNamespaceURI,
+                                                         nsContentUtils::IsChromeDoc(mContent->OwnerDoc()));
 
     if (nameSpaceID == kNameSpaceID_Unknown) {
       return nullptr;
     }
   }
 
   uint32_t i, count = mContent->GetAttrCount();
   for (i = 0; i < count; ++i) {
--- a/dom/base/nsIContent.h
+++ b/dom/base/nsIContent.h
@@ -238,16 +238,22 @@ public:
   }
 
   /**
    * Return true iff this node is in an HTML document (in the HTML5 sense of
    * the term, i.e. not in an XHTML/XML document).
    */
   inline bool IsInHTMLDocument() const;
 
+
+  /**
+   * Returns true if in a chrome document
+   */
+  virtual bool IsInChromeDocument();
+
   /**
    * Get the namespace that this element's tag is defined in
    * @return the namespace
    */
   inline int32_t GetNameSpaceID() const
   {
     return mNodeInfo->NamespaceID();
   }
--- a/dom/base/nsIContentInlines.h
+++ b/dom/base/nsIContentInlines.h
@@ -4,16 +4,23 @@
  * 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/. */
 
 #ifndef nsIContentInlines_h
 #define nsIContentInlines_h
 
 #include "nsIContent.h"
 #include "nsIDocument.h"
+#include "nsContentUtils.h"
 
 inline bool
 nsIContent::IsInHTMLDocument() const
 {
   return OwnerDoc()->IsHTMLDocument();
 }
 
+inline bool
+nsIContent::IsInChromeDocument()
+{
+  return nsContentUtils::IsChromeDoc(OwnerDoc());
+}
+
 #endif // nsIContentInlines_h
--- a/dom/base/nsNameSpaceManager.cpp
+++ b/dom/base/nsNameSpaceManager.cpp
@@ -10,27 +10,35 @@
  */
 
 #include "nsNameSpaceManager.h"
 
 #include "nscore.h"
 #include "mozilla/dom/NodeInfo.h"
 #include "nsCOMArray.h"
 #include "nsContentCreatorFunctions.h"
+#include "nsContentUtils.h"
 #include "nsGkAtoms.h"
+#include "nsIDocument.h"
 #include "nsString.h"
 #include "mozilla/dom/NodeInfo.h"
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/dom/XBLChildrenElement.h"
 #include "mozilla/dom/Element.h"
+#include "mozilla/Preferences.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
-StaticAutoPtr<nsNameSpaceManager> nsNameSpaceManager::sInstance;
+static const char* kPrefMathMLDisabled = "mathml.disabled";
+static const char* kObservedPrefs[] = {
+  kPrefMathMLDisabled,
+  nullptr
+};
+StaticRefPtr<nsNameSpaceManager> nsNameSpaceManager::sInstance;
 
 /* static */ nsNameSpaceManager*
 nsNameSpaceManager::GetInstance() {
   if (!sInstance) {
     sInstance = new nsNameSpaceManager();
     if (sInstance->Init()) {
       ClearOnShutdown(&sInstance);
     } else {
@@ -44,29 +52,39 @@ nsNameSpaceManager::GetInstance() {
 
 bool nsNameSpaceManager::Init()
 {
   nsresult rv;
 #define REGISTER_NAMESPACE(uri, id) \
   rv = AddNameSpace(dont_AddRef(uri), id); \
   NS_ENSURE_SUCCESS(rv, false)
 
+#define REGISTER_DISABLED_NAMESPACE(uri, id) \
+  rv = AddDisabledNameSpace(dont_AddRef(uri), id); \
+  NS_ENSURE_SUCCESS(rv, false)
+
+  mozilla::Preferences::AddStrongObservers(this, kObservedPrefs);
+  mMathMLDisabled = mozilla::Preferences::GetBool(kPrefMathMLDisabled);
+
+
   // Need to be ordered according to ID.
   REGISTER_NAMESPACE(nsGkAtoms::nsuri_xmlns, kNameSpaceID_XMLNS);
   REGISTER_NAMESPACE(nsGkAtoms::nsuri_xml, kNameSpaceID_XML);
   REGISTER_NAMESPACE(nsGkAtoms::nsuri_xhtml, kNameSpaceID_XHTML);
   REGISTER_NAMESPACE(nsGkAtoms::nsuri_xlink, kNameSpaceID_XLink);
   REGISTER_NAMESPACE(nsGkAtoms::nsuri_xslt, kNameSpaceID_XSLT);
   REGISTER_NAMESPACE(nsGkAtoms::nsuri_xbl, kNameSpaceID_XBL);
   REGISTER_NAMESPACE(nsGkAtoms::nsuri_mathml, kNameSpaceID_MathML);
   REGISTER_NAMESPACE(nsGkAtoms::nsuri_rdf, kNameSpaceID_RDF);
   REGISTER_NAMESPACE(nsGkAtoms::nsuri_xul, kNameSpaceID_XUL);
   REGISTER_NAMESPACE(nsGkAtoms::nsuri_svg, kNameSpaceID_SVG);
+  REGISTER_DISABLED_NAMESPACE(nsGkAtoms::nsuri_mathml, kNameSpaceID_disabled_MathML);
 
 #undef REGISTER_NAMESPACE
+#undef REGISTER_DISABLED_NAMESPACE
 
   return true;
 }
 
 nsresult
 nsNameSpaceManager::RegisterNameSpace(const nsAString& aURI,
                                       int32_t& aNameSpaceID)
 {
@@ -105,34 +123,42 @@ nsNameSpaceManager::GetNameSpaceURI(int3
   }
 
   mURIArray.ElementAt(index)->ToString(aURI);
 
   return NS_OK;
 }
 
 int32_t
-nsNameSpaceManager::GetNameSpaceID(const nsAString& aURI)
+nsNameSpaceManager::GetNameSpaceID(const nsAString& aURI,
+                                   bool aInChromeDoc)
 {
   if (aURI.IsEmpty()) {
     return kNameSpaceID_None; // xmlns="", see bug 75700 for details
   }
 
   nsCOMPtr<nsIAtom> atom = NS_Atomize(aURI);
-  return GetNameSpaceID(atom);
+  return GetNameSpaceID(atom, aInChromeDoc);
 }
 
 int32_t
-nsNameSpaceManager::GetNameSpaceID(nsIAtom* aURI)
+nsNameSpaceManager::GetNameSpaceID(nsIAtom* aURI,
+                                   bool aInChromeDoc)
 {
   if (aURI == nsGkAtoms::_empty) {
     return kNameSpaceID_None; // xmlns="", see bug 75700 for details
   }
 
   int32_t nameSpaceID;
+  if (mMathMLDisabled &&
+      mDisabledURIToIDTable.Get(aURI, &nameSpaceID) &&
+      !aInChromeDoc) {
+    NS_POSTCONDITION(nameSpaceID >= 0, "Bogus namespace ID");
+    return nameSpaceID;
+  }
   if (mURIToIDTable.Get(aURI, &nameSpaceID)) {
     NS_POSTCONDITION(nameSpaceID >= 0, "Bogus namespace ID");
     return nameSpaceID;
   }
 
   return kNameSpaceID_Unknown;
 }
 
@@ -148,17 +174,29 @@ NS_NewElement(Element** aResult,
     return NS_NewHTMLElement(aResult, ni.forget(), aFromParser, aIs);
   }
 #ifdef MOZ_XUL
   if (ns == kNameSpaceID_XUL) {
     return NS_NewXULElement(aResult, ni.forget());
   }
 #endif
   if (ns == kNameSpaceID_MathML) {
-    return NS_NewMathMLElement(aResult, ni.forget());
+    // If the mathml.disabled pref. is true, convert all MathML nodes into
+    // disabled MathML nodes by swapping the namespace.
+    nsNameSpaceManager* nsmgr = nsNameSpaceManager::GetInstance();
+    if ((nsmgr && !nsmgr->mMathMLDisabled) ||
+        nsContentUtils::IsChromeDoc(ni->GetDocument())) {
+      return NS_NewMathMLElement(aResult, ni.forget());
+    }
+
+    RefPtr<mozilla::dom::NodeInfo> genericXMLNI =
+      ni->NodeInfoManager()->
+      GetNodeInfo(ni->NameAtom(), ni->GetPrefixAtom(),
+        kNameSpaceID_disabled_MathML, ni->NodeType(), ni->GetExtraName());
+    return NS_NewXMLElement(aResult, genericXMLNI.forget());
   }
   if (ns == kNameSpaceID_SVG) {
     return NS_NewSVGElement(aResult, ni.forget(), aFromParser);
   }
   if (ns == kNameSpaceID_XBL && ni->Equals(nsGkAtoms::children)) {
     NS_ADDREF(*aResult = new XBLChildrenElement(ni.forget()));
     return NS_OK;
   }
@@ -190,8 +228,40 @@ nsresult nsNameSpaceManager::AddNameSpac
   NS_ASSERTION(aNameSpaceID - 1 == (int32_t) mURIArray.Length(),
                "BAD! AddNameSpace not called in right order!");
 
   mURIArray.AppendElement(uri.forget());
   mURIToIDTable.Put(mURIArray.LastElement(), aNameSpaceID);
 
   return NS_OK;
 }
+
+nsresult
+nsNameSpaceManager::AddDisabledNameSpace(already_AddRefed<nsIAtom> aURI,
+                                         const int32_t aNameSpaceID)
+{
+  nsCOMPtr<nsIAtom> uri = aURI;
+  if (aNameSpaceID < 0) {
+    // We've wrapped...  Can't do anything else here; just bail.
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+
+  NS_ASSERTION(aNameSpaceID - 1 == (int32_t) mURIArray.Length(),
+               "BAD! AddDisabledNameSpace not called in right order!");
+
+  mURIArray.AppendElement(uri.forget());
+  mDisabledURIToIDTable.Put(mURIArray.LastElement(), aNameSpaceID);
+
+  return NS_OK;
+}
+
+// nsISupports
+NS_IMPL_ISUPPORTS(nsNameSpaceManager,
+                  nsIObserver)
+
+// nsIObserver
+NS_IMETHODIMP
+nsNameSpaceManager::Observe(nsISupports* aObject, const char* aTopic,
+                            const char16_t* aMessage)
+{
+  mMathMLDisabled = mozilla::Preferences::GetBool(kPrefMathMLDisabled);
+  return NS_OK;
+}
--- a/dom/base/nsNameSpaceManager.h
+++ b/dom/base/nsNameSpaceManager.h
@@ -5,16 +5,18 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef nsNameSpaceManager_h___
 #define nsNameSpaceManager_h___
 
 #include "nsDataHashtable.h"
 #include "nsHashKeys.h"
 #include "nsIAtom.h"
+#include "nsIDocument.h"
+#include "nsIObserver.h"
 #include "nsTArray.h"
 
 #include "mozilla/StaticPtr.h"
 
 class nsAString;
 
 /**
  * The Name Space Manager tracks the association between a NameSpace
@@ -25,39 +27,47 @@ class nsAString;
  * consistent accross the app. NameSpace IDs are only consistent at runtime
  * ie: they are not guaranteed to be consistent accross app sessions.
  *
  * The nsNameSpaceManager needs to have a live reference for as long as
  * the NameSpace IDs are needed.
  *
  */
 
-class nsNameSpaceManager final
+class nsNameSpaceManager final : public nsIObserver
 {
 public:
-  ~nsNameSpaceManager() {}
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIOBSERVER
+  virtual nsresult RegisterNameSpace(const nsAString& aURI,
+                                     int32_t& aNameSpaceID);
 
-  nsresult RegisterNameSpace(const nsAString& aURI, int32_t& aNameSpaceID);
-
-  nsresult GetNameSpaceURI(int32_t aNameSpaceID, nsAString& aURI);
+  virtual nsresult GetNameSpaceURI(int32_t aNameSpaceID, nsAString& aURI);
 
   nsIAtom* NameSpaceURIAtom(int32_t aNameSpaceID) {
     MOZ_ASSERT(aNameSpaceID > 0 && (int64_t) aNameSpaceID <= (int64_t) mURIArray.Length());
     return mURIArray.ElementAt(aNameSpaceID - 1); // id is index + 1
   }
 
-  int32_t GetNameSpaceID(const nsAString& aURI);
-  int32_t GetNameSpaceID(nsIAtom* aURI);
+  int32_t GetNameSpaceID(const nsAString& aURI,
+                         bool aInChromeDoc);
+  int32_t GetNameSpaceID(nsIAtom* aURI,
+                         bool aInChromeDoc);
 
   bool HasElementCreator(int32_t aNameSpaceID);
 
   static nsNameSpaceManager* GetInstance();
+  bool mMathMLDisabled;
+
 private:
   bool Init();
   nsresult AddNameSpace(already_AddRefed<nsIAtom> aURI, const int32_t aNameSpaceID);
+  nsresult AddDisabledNameSpace(already_AddRefed<nsIAtom> aURI, const int32_t aNameSpaceID);
+  ~nsNameSpaceManager() {};
 
   nsDataHashtable<nsISupportsHashKey, int32_t> mURIToIDTable;
+  nsDataHashtable<nsISupportsHashKey, int32_t> mDisabledURIToIDTable;
   nsTArray<nsCOMPtr<nsIAtom>> mURIArray;
 
-  static mozilla::StaticAutoPtr<nsNameSpaceManager> sInstance;
+  static mozilla::StaticRefPtr<nsNameSpaceManager> sInstance;
 };
  
 #endif // nsNameSpaceManager_h___
--- a/dom/svg/SVGAnimationElement.cpp
+++ b/dom/svg/SVGAnimationElement.cpp
@@ -344,16 +344,25 @@ SVGAnimationElement::UnsetAttr(int32_t a
 
 bool
 SVGAnimationElement::IsNodeOfType(uint32_t aFlags) const
 {
   return !(aFlags & ~(eCONTENT | eANIMATION));
 }
 
 //----------------------------------------------------------------------
+// SVGTests methods
+
+bool
+SVGAnimationElement::IsInChromeDoc() const
+{
+  return nsContentUtils::IsChromeDoc(OwnerDoc());
+}
+
+//----------------------------------------------------------------------
 // SVG utility methods
 
 void
 SVGAnimationElement::ActivateByHyperlink()
 {
   FlushAnimations();
 
   // The behavior for when the target is an animation element is defined in
--- a/dom/svg/SVGAnimationElement.h
+++ b/dom/svg/SVGAnimationElement.h
@@ -81,16 +81,20 @@ public:
   float GetStartTime(ErrorResult& rv);
   float GetCurrentTime();
   float GetSimpleDuration(ErrorResult& rv);
   void BeginElement(ErrorResult& rv) { BeginElementAt(0.f, rv); }
   void BeginElementAt(float offset, ErrorResult& rv);
   void EndElement(ErrorResult& rv) { EndElementAt(0.f, rv); }
   void EndElementAt(float offset, ErrorResult& rv);
 
+  // SVGTests
+  virtual bool IsInChromeDoc() const override;
+
+
  protected:
   // nsSVGElement overrides
 
   void UpdateHrefTarget(nsIContent* aNodeForContext,
                         const nsAString& aHrefStr);
   void AnimationTargetChanged();
 
   class TargetReference : public nsReferencedElement {
--- a/dom/svg/SVGGraphicsElement.cpp
+++ b/dom/svg/SVGGraphicsElement.cpp
@@ -26,10 +26,16 @@ SVGGraphicsElement::SVGGraphicsElement(a
   : SVGGraphicsElementBase(aNodeInfo)
 {
 }
 
 SVGGraphicsElement::~SVGGraphicsElement()
 {
 }
 
+bool
+SVGGraphicsElement::IsInChromeDoc() const
+{
+  return nsContentUtils::IsChromeDoc(OwnerDoc());
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/svg/SVGGraphicsElement.h
+++ b/dom/svg/SVGGraphicsElement.h
@@ -20,14 +20,16 @@ class SVGGraphicsElement : public SVGGra
 {
 protected:
   explicit SVGGraphicsElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo);
   ~SVGGraphicsElement();
 
 public:
   // interfaces:
   NS_DECL_ISUPPORTS_INHERITED
+
+  bool IsInChromeDoc() const override;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_SVGGraphicsElement_h
--- a/dom/svg/SVGSymbolElement.cpp
+++ b/dom/svg/SVGSymbolElement.cpp
@@ -76,16 +76,26 @@ SVGSymbolElement::IsAttributeMapped(cons
     sViewportsMap
    };
 
   return FindAttributeDependence(name, map) ||
     SVGSymbolElementBase::IsAttributeMapped(name);
 }
 
 //----------------------------------------------------------------------
+// SVGTests methods
+
+bool
+SVGSymbolElement::IsInChromeDoc() const
+{
+  return nsContentUtils::IsChromeDoc(OwnerDoc());
+}
+
+
+//----------------------------------------------------------------------
 // nsSVGElement methods
 
 nsSVGViewBox *
 SVGSymbolElement::GetViewBox()
 {
   return &mViewBox;
 }
 
--- a/dom/svg/SVGSymbolElement.h
+++ b/dom/svg/SVGSymbolElement.h
@@ -39,16 +39,19 @@ public:
   NS_IMETHOD_(bool) IsAttributeMapped(const nsIAtom* name) const override;
 
   virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const override;
 
   // WebIDL
   already_AddRefed<SVGAnimatedRect> ViewBox();
   already_AddRefed<DOMSVGAnimatedPreserveAspectRatio> PreserveAspectRatio();
 
+  // SVGTests
+  bool IsInChromeDoc() const override;
+
 protected:
   virtual nsSVGViewBox *GetViewBox() override;
   virtual SVGAnimatedPreserveAspectRatio *GetPreserveAspectRatio() override;
 
   nsSVGViewBox mViewBox;
   SVGAnimatedPreserveAspectRatio mPreserveAspectRatio;
 };
 
--- a/dom/svg/SVGTests.cpp
+++ b/dom/svg/SVGTests.cpp
@@ -52,17 +52,17 @@ SVGTests::SystemLanguage()
   nsSVGElement* element = static_cast<nsSVGElement*>(elem.get());
   return DOMSVGStringList::GetDOMWrapper(
            &mStringListAttributes[LANGUAGE], element, true, LANGUAGE);
 }
 
 bool
 SVGTests::HasExtension(const nsAString& aExtension)
 {
-  return nsSVGFeatures::HasExtension(aExtension);
+  return nsSVGFeatures::HasExtension(aExtension, IsInChromeDoc());
 }
 
 bool
 SVGTests::IsConditionalProcessingAttribute(const nsIAtom* aAttribute) const
 {
   for (uint32_t i = 0; i < ArrayLength(sStringListNames); i++) {
     if (aAttribute == *sStringListNames[i]) {
       return true;
@@ -134,17 +134,17 @@ SVGTests::PassesConditionalProcessingTes
   // go beyond the feature set defined in the SVG specification.
   // Each extension is identified by a URI reference.
   // For now, claim that mozilla's SVG implementation supports XHTML and MathML.
   if (mStringListAttributes[EXTENSIONS].IsExplicitlySet()) {
     if (mStringListAttributes[EXTENSIONS].IsEmpty()) {
       return false;
     }
     for (uint32_t i = 0; i < mStringListAttributes[EXTENSIONS].Length(); i++) {
-      if (!nsSVGFeatures::HasExtension(mStringListAttributes[EXTENSIONS][i])) {
+      if (!nsSVGFeatures::HasExtension(mStringListAttributes[EXTENSIONS][i], IsInChromeDoc())) {
         return false;
       }
     }
   }
 
   if (aAcceptLangs == kIgnoreSystemLanguage) {
     return true;
   }
--- a/dom/svg/SVGTests.h
+++ b/dom/svg/SVGTests.h
@@ -90,16 +90,18 @@ public:
   void MaybeInvalidate();
 
   // WebIDL
   already_AddRefed<DOMSVGStringList> RequiredFeatures();
   already_AddRefed<DOMSVGStringList> RequiredExtensions();
   already_AddRefed<DOMSVGStringList> SystemLanguage();
   bool HasExtension(const nsAString& aExtension);
 
+  virtual bool IsInChromeDoc() const = 0;
+
 protected:
   virtual ~SVGTests() {}
 
 private:
   enum { FEATURES, EXTENSIONS, LANGUAGE };
   SVGStringList mStringListAttributes[3];
   static nsIAtom** sStringListNames[3];
 };
--- a/dom/svg/nsSVGFeatures.cpp
+++ b/dom/svg/nsSVGFeatures.cpp
@@ -10,16 +10,17 @@
  * requiredFeatures, requiredExtensions and systemLanguage attributes).
  *
  *   http://www.w3.org/TR/SVG11/struct.html#ConditionalProcessing
  */
 
 #include "nsSVGFeatures.h"
 #include "nsIContent.h"
 #include "nsIDocument.h"
+#include "nsNameSpaceManager.h"
 #include "mozilla/Preferences.h"
 
 using namespace mozilla;
 
 /*static*/ bool
 nsSVGFeatures::HasFeature(nsISupports* aObject, const nsAString& aFeature)
 {
   if (aFeature.EqualsLiteral("http://www.w3.org/TR/SVG11/feature#Script")) {
@@ -37,17 +38,20 @@ nsSVGFeatures::HasFeature(nsISupports* a
 #define SVG_UNSUPPORTED_FEATURE(str)
 #include "nsSVGFeaturesList.h"
 #undef SVG_SUPPORTED_FEATURE
 #undef SVG_UNSUPPORTED_FEATURE
   return false;
 }
 
 /*static*/ bool
-nsSVGFeatures::HasExtension(const nsAString& aExtension)
+nsSVGFeatures::HasExtension(const nsAString& aExtension, const bool aIsInChrome)
 {
 #define SVG_SUPPORTED_EXTENSION(str) if (aExtension.EqualsLiteral(str)) return true;
   SVG_SUPPORTED_EXTENSION("http://www.w3.org/1999/xhtml")
-  SVG_SUPPORTED_EXTENSION("http://www.w3.org/1998/Math/MathML")
+  nsNameSpaceManager* nameSpaceManager = nsNameSpaceManager::GetInstance();
+  if (aIsInChrome || !nameSpaceManager->mMathMLDisabled) {
+    SVG_SUPPORTED_EXTENSION("http://www.w3.org/1998/Math/MathML")
+  }
 #undef SVG_SUPPORTED_EXTENSION
 
   return false;
 }
--- a/dom/svg/nsSVGFeatures.h
+++ b/dom/svg/nsSVGFeatures.h
@@ -25,12 +25,12 @@ public:
 
   /**
    * Check whether we support the given extension string.
    *
    * @param aExtension the URI of an extension. Known extensions are
    *   "http://www.w3.org/1999/xhtml" and "http://www.w3.org/1998/Math/MathML"
    */
   static bool
-  HasExtension(const nsAString& aExtension);
+  HasExtension(const nsAString& aExtension, const bool aIsInChrome);
 };
 
 #endif // __NS_SVGFEATURES_H__
--- a/dom/xbl/nsXBLPrototypeBinding.cpp
+++ b/dom/xbl/nsXBLPrototypeBinding.cpp
@@ -1594,17 +1594,18 @@ nsXBLPrototypeBinding::ResolveBaseBindin
   }
 
   nsAutoString nameSpace;
 
   if (!prefix.IsEmpty()) {
     mBinding->LookupNamespaceURI(prefix, nameSpace);
     if (!nameSpace.IsEmpty()) {
       int32_t nameSpaceID =
-        nsContentUtils::NameSpaceManager()->GetNameSpaceID(nameSpace);
+        nsContentUtils::NameSpaceManager()->GetNameSpaceID(nameSpace,
+                                                           nsContentUtils::IsChromeDoc(doc));
 
       nsCOMPtr<nsIAtom> tagName = NS_Atomize(display);
       // Check the white list
       if (!CheckTagNameWhiteList(nameSpaceID, tagName)) {
         const char16_t* params[] = { display.get() };
         nsContentUtils::ReportToConsole(nsIScriptError::errorFlag,
                                         NS_LITERAL_CSTRING("XBL"), nullptr,
                                         nsContentUtils::eXBL_PROPERTIES,
--- a/dom/xml/nsXMLContentSink.cpp
+++ b/dom/xml/nsXMLContentSink.cpp
@@ -1044,16 +1044,19 @@ nsXMLContentSink::HandleEndElement(const
                                  getter_AddRefs(debugTagAtom),
                                  &debugNameSpaceID);
   // Check if we are closing a template element because template
   // elements do not get pushed on the stack, the template
   // element content is pushed instead.
   bool isTemplateElement = debugTagAtom == nsGkAtoms::_template &&
                            debugNameSpaceID == kNameSpaceID_XHTML;
   NS_ASSERTION(content->NodeInfo()->Equals(debugTagAtom, debugNameSpaceID) ||
+               (debugNameSpaceID == kNameSpaceID_MathML &&
+                content->NodeInfo()->NamespaceID() == kNameSpaceID_disabled_MathML &&
+                content->NodeInfo()->Equals(debugTagAtom)) ||
                isTemplateElement, "Wrong element being closed");
 #endif
 
   result = CloseElement(content);
 
   if (mCurrentHead == content) {
     mCurrentHead = nullptr;
   }
--- a/layout/mathml/moz.build
+++ b/layout/mathml/moz.build
@@ -7,16 +7,19 @@
 with Files('**'):
     BUG_COMPONENT = ('Core', 'MathML')
 
 if CONFIG['ENABLE_TESTS']:
     MOCHITEST_MANIFESTS += [
         'imptests/mochitest.ini',
         'tests/mochitest.ini',
 ]
+    MOCHITEST_CHROME_MANIFESTS += [
+        'tests/chrome.ini',
+]
 
 UNIFIED_SOURCES += [
     'nsMathMLChar.cpp',
     'nsMathMLContainerFrame.cpp',
     'nsMathMLFrame.cpp',
     'nsMathMLmactionFrame.cpp',
     'nsMathMLmencloseFrame.cpp',
     'nsMathMLmfencedFrame.cpp',
new file mode 100644
--- /dev/null
+++ b/layout/mathml/tests/chrome.ini
@@ -0,0 +1,6 @@
+[DEFAULT]
+
+support-files =
+  mathml_example_test.html
+
+[test_disabled_chrome.html]
new file mode 100644
--- /dev/null
+++ b/layout/mathml/tests/mathml_example_test.html
@@ -0,0 +1,28 @@
+<math xmlns="http://www.w3.org/1998/Math/MathML">
+  <mstyle scriptsizemultiplier="2">
+    <msub>
+      <mtext>O</mtext>
+      <mtext>O</mtext>
+    </msub>
+    <msup>
+      <mtext>O</mtext>
+      <mtext>O</mtext>
+    </msup>
+    <msubsup>
+      <mtext>O</mtext>
+      <mtext>O</mtext>
+      <mtext>O</mtext>
+    </msubsup>
+    <mmultiscripts>
+      <mtext>O</mtext>
+      <mtext>O</mtext>
+      <mtext>O</mtext>
+      <mprescripts/>
+      <mtext>O</mtext>
+      <mtext>O</mtext>
+    </mmultiscripts>
+  </mstyle>
+</math>
+
+<svg id="svgel">
+</svg>
--- a/layout/mathml/tests/mochitest.ini
+++ b/layout/mathml/tests/mochitest.ini
@@ -1,16 +1,17 @@
 [DEFAULT]
 
 [test_bug330964.html]
 [test_bug553917.html]
 [test_bug706406.html]
 [test_bug827713-2.html]
 [test_bug827713.html]
 [test_bug975681.html]
+[test_disabled.html]
 [test_opentype-axis-height.html]
 [test_opentype-fraction.html]
 [test_opentype-limits.html]
 skip-if = os == "win" # Fails on WinXP
 [test_opentype-radical.html]
 skip-if = os == "win" # Fails on WinXP
 [test_opentype-scripts.html]
 [test_opentype-stack.html]
new file mode 100644
--- /dev/null
+++ b/layout/mathml/tests/test_disabled.html
@@ -0,0 +1,47 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+Copied from
+https://bugzilla.mozilla.org/show_bug.cgi?id=744830
+-->
+<head>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=166235">Mozilla Bug 166235</a>
+<div id="testnodes"><span>hi</span> there <!-- mon ami --></div>
+<pre id="test">
+<script type="application/javascript">
+  SimpleTest.waitForExplicitFinish();
+  SpecialPowers.pushPrefEnv({"set": [["mathml.disabled", true]]}, doTest);
+  function doTest() {
+    let t = document.getElementById('testnodes');
+    t.innerHTML = null;
+    t.appendChild(document.createElementNS("http://www.w3.org/1998/Math/MathML", "math:math"));
+    t.firstChild.textContent = "<foo>";
+    is(t.innerHTML, "<math:math>&lt;foo&gt;</math:math>");
+
+    t.innerHTML = null;
+    t.appendChild(document.createElementNS("http://www.w3.org/1998/Math/MathML", "math"));
+    is(t.firstChild.namespaceURI, "http://www.w3.org/1998/Math/MathML");
+    t.firstChild.appendChild(document.createElementNS("http://www.w3.org/1998/Math/MathML", "script"));
+    is(t.firstChild.firstChild.namespaceURI, "http://www.w3.org/1998/Math/MathML");
+    t.firstChild.firstChild.textContent = "1&2<3>4\xA0";
+    is(t.innerHTML, '<math><script>1&amp;2&lt;3&gt;4&nbsp;\u003C/script></math>');
+
+    t.innerHTML = null;
+    t.appendChild(document.createElementNS("http://www.w3.org/1998/Math/MathML", "math"));
+    is(t.firstChild.namespaceURI, "http://www.w3.org/1998/Math/MathML");
+    t.firstChild.appendChild(document.createElementNS("http://www.w3.org/1998/Math/MathML", "style"));
+    is(t.firstChild.firstChild.namespaceURI, "http://www.w3.org/1998/Math/MathML");
+    t.firstChild.firstChild.textContent = "1&2<3>4\xA0";
+    is(t.innerHTML, '<math><style>1&amp;2&lt;3&gt;4&nbsp;\u003C/style></math>');
+
+    SimpleTest.finish();
+  }
+</script>
+</pre>
+</body>
+</html>
+
new file mode 100644
--- /dev/null
+++ b/layout/mathml/tests/test_disabled_chrome.html
@@ -0,0 +1,55 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=744830
+-->
+<head>
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+<!--
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+-->
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SpawnTask.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=166235">Mozilla Bug 166235</a>
+<div id="testnodes"><span>hi</span> there <!-- mon ami --></div>
+<pre id="test">
+<script type="application/javascript">
+  add_task(function* () {
+    const initialPrefValue = SpecialPowers.getBoolPref("mathml.disabled");
+    SpecialPowers.setBoolPref("mathml.disabled", true);
+    const Cu = SpecialPowers.Components.utils;
+    const { ContentTaskUtils } = Cu.import("resource://testing-common/ContentTaskUtils.jsm", {});
+    let t = document.getElementById('testnodes');
+
+    let url = 'chrome://mochitests/content/chrome/layout/mathml/tests/mathml_example_test.html'
+    const chromeIframeEl = document.createElement('iframe');
+    let chromeLoadPromise = ContentTaskUtils.waitForEvent(chromeIframeEl, 'load', false);
+    chromeIframeEl.src = url;
+    t.appendChild(chromeIframeEl);
+
+    yield chromeLoadPromise;
+    const chromeBR = chromeIframeEl.contentDocument.body.getBoundingClientRect();
+
+    url = "http://mochi.test:8888/chrome/layout/mathml/tests/mathml_example_test.html";
+    const iframeEl = document.createElement('iframe');
+    iframeEl.src = url;
+    let loadPromise = ContentTaskUtils.waitForEvent(iframeEl, 'load', false);
+    t.appendChild(iframeEl);
+    yield loadPromise;
+
+    const contentBR = iframeEl.contentDocument.body.getBoundingClientRect();
+
+    ok(chromeBR.height > contentBR.height, "Chrome content height should be bigger than content due to layout");
+
+    ok(!iframeEl.contentDocument.getElementById('svgel').hasExtension("http://www.w3.org/1998/Math/MathML"), 'SVG namespace support is disabled in content iframe');
+    ok(chromeIframeEl.contentDocument.getElementById('svgel').hasExtension("http://www.w3.org/1998/Math/MathML"), 'SVG namespace support is enabled in chrome iframe');
+
+    SpecialPowers.setBoolPref("mathml.disabled", initialPrefValue);
+  });
+</script>
+</pre>
+</body>
+</html>
+
new file mode 100644
--- /dev/null
+++ b/layout/reftests/mathml/disabled-scriptlevel-1-ref.html
@@ -0,0 +1,129 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <title>scriptlevel</title>
+    <meta charset="utf-8"/>
+  </head>
+  <body>
+
+    <!-- Test scriptlevel on mstyle -->
+    <randomelement>
+      <mstyle scriptsizemultiplier="2">
+        <mtext>O</mtext>
+        <mstyle scriptlevel="1"><mtext>O</mtext></mstyle>
+      </mstyle>
+    </randomelement>
+
+    <!-- The mfrac element sets displaystyle to "false", or if it was already
+         false increments scriptlevel by 1, within numerator and denominator.
+      -->
+    <randomelement>
+      <mstyle scriptsizemultiplier="2">
+        <mstyle displaystyle="false">
+          <mfrac>
+            <mtext>O</mtext>
+            <mtext>O</mtext>
+          </mfrac>
+        </mstyle>
+        <mstyle displaystyle="true">
+          <mfrac>
+            <mtext>O</mtext>
+            <mtext>O</mtext>
+          </mfrac>
+        </mstyle>
+      </mstyle>
+    </randomelement>
+
+    <!--    The mroot element increments scriptlevel by 2, and sets
+            displaystyle to "false", within index, but leaves both attributes
+            unchanged within base.
+            The msqrt element leaves both attributes unchanged within its
+            argument. -->
+    <randomelement>
+      <mstyle scriptsizemultiplier="2">
+        <mroot>
+          <mtext>O</mtext>
+          <mtext>O</mtext>
+        </mroot>
+        <msqrt>
+          <mtext>O</mtext>
+        </msqrt>
+      </mstyle>
+    </randomelement>
+
+<!--
+    The msub element [...] increments scriptlevel by 1, and sets displaystyle to
+   "false", within subscript, but leaves both attributes unchanged within base.
+
+   The msup element [...] increments scriptlevel by 1, and sets displaystyle to
+   "false", within superscript, but leaves both attributes unchanged within
+   base.
+
+   The msubsup element [...] increments scriptlevel by 1, and sets displaystyle
+   to "false", within subscript and superscript, but leaves both attributes
+   unchanged within base.
+
+   The mmultiscripts element increments scriptlevel by 1, and sets displaystyle
+   to "false", within each of its arguments except base, but leaves both
+   attributes unchanged within base.
+   -->
+    <randomelement>
+      <mstyle scriptsizemultiplier="2">
+        <msub>
+          <mtext>O</mtext>
+          <mtext>O</mtext>
+        </msub>
+        <msup>
+          <mtext>O</mtext>
+          <mtext>O</mtext>
+        </msup>
+        <msubsup>
+          <mtext>O</mtext>
+          <mtext>O</mtext>
+          <mtext>O</mtext>
+        </msubsup>
+        <mmultiscripts>
+          <mtext>O</mtext>
+          <mtext>O</mtext>
+          <mtext>O</mtext>
+          <mprescripts/>
+          <mtext>O</mtext>
+          <mtext>O</mtext>
+        </mmultiscripts>
+      </mstyle>
+    </randomelement>
+
+<!-- 
+   The munder element [...] always sets displaystyle to "false" within the
+   underscript, but increments scriptlevel by 1 only when accentunder is
+   "false". Within base, it always leaves both attributes unchanged.
+
+   The mover element [...] always sets displaystyle to "false" within
+   overscript, but increments scriptlevel by 1 only when accent is "false".
+   Within base, it always leaves both attributes unchanged.
+
+   The munderover [..] always sets displaystyle to "false" within underscript
+   and overscript, but increments scriptlevel by 1 only when accentunder or
+   accent, respectively, are "false". Within base, it always leaves both
+   attributes unchanged.
+-->   
+    <randomelement>
+      <mstyle scriptsizemultiplier="2">
+        <munder>
+          <mtext>O</mtext>
+          <mtext>O</mtext>
+        </munder>
+        <mover>
+          <mtext>O</mtext>
+          <mtext>O</mtext>
+        </mover>
+        <munderover>
+          <mtext>O</mtext>
+          <mtext>O</mtext>
+          <mtext>O</mtext>
+        </munderover>
+      </mstyle>
+    </randomelement>
+
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/mathml/disabled-scriptlevel-1-ref.xhtml
@@ -0,0 +1,133 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+  <head>
+    <title>scriptlevel</title>
+    <meta charset="utf-8"/>
+    <style>
+      h2 {
+        text-align:center;
+      }
+    </style>
+  </head>
+  <body>
+
+    <!-- Test scriptlevel on mstyle -->
+    <randomelement>
+      <mstyle scriptsizemultiplier="2">
+        <mtext>O</mtext>
+        <mstyle scriptlevel="1"><mtext>O</mtext></mstyle>
+      </mstyle>
+    </randomelement>
+
+    <!-- The mfrac element sets displaystyle to "false", or if it was already
+         false increments scriptlevel by 1, within numerator and denominator.
+      -->
+    <randomelement>
+      <mstyle scriptsizemultiplier="2">
+        <mstyle displaystyle="false">
+          <mfrac>
+            <mtext>O</mtext>
+            <mtext>O</mtext>
+          </mfrac>
+        </mstyle>
+        <mstyle displaystyle="true">
+          <mfrac>
+            <mtext>O</mtext>
+            <mtext>O</mtext>
+          </mfrac>
+        </mstyle>
+      </mstyle>
+    </randomelement>
+
+    <!--    The mroot element increments scriptlevel by 2, and sets
+            displaystyle to "false", within index, but leaves both attributes
+            unchanged within base.
+            The msqrt element leaves both attributes unchanged within its
+            argument. -->
+    <randomelement>
+      <mstyle scriptsizemultiplier="2">
+        <mroot>
+          <mtext>O</mtext>
+          <mtext>O</mtext>
+        </mroot>
+        <msqrt>
+          <mtext>O</mtext>
+        </msqrt>
+      </mstyle>
+    </randomelement>
+
+<!--
+    The msub element [...] increments scriptlevel by 1, and sets displaystyle to
+   "false", within subscript, but leaves both attributes unchanged within base.
+
+   The msup element [...] increments scriptlevel by 1, and sets displaystyle to
+   "false", within superscript, but leaves both attributes unchanged within
+   base.
+
+   The msubsup element [...] increments scriptlevel by 1, and sets displaystyle
+   to "false", within subscript and superscript, but leaves both attributes
+   unchanged within base.
+
+   The mmultiscripts element increments scriptlevel by 1, and sets displaystyle
+   to "false", within each of its arguments except base, but leaves both
+   attributes unchanged within base.
+   -->
+    <randomelement>
+      <mstyle scriptsizemultiplier="2">
+        <msub>
+          <mtext>O</mtext>
+          <mtext>O</mtext>
+        </msub>
+        <msup>
+          <mtext>O</mtext>
+          <mtext>O</mtext>
+        </msup>
+        <msubsup>
+          <mtext>O</mtext>
+          <mtext>O</mtext>
+          <mtext>O</mtext>
+        </msubsup>
+        <mmultiscripts>
+          <mtext>O</mtext>
+          <mtext>O</mtext>
+          <mtext>O</mtext>
+          <mprescripts/>
+          <mtext>O</mtext>
+          <mtext>O</mtext>
+        </mmultiscripts>
+      </mstyle>
+    </randomelement>
+
+<!-- 
+   The munder element [...] always sets displaystyle to "false" within the
+   underscript, but increments scriptlevel by 1 only when accentunder is
+   "false". Within base, it always leaves both attributes unchanged.
+
+   The mover element [...] always sets displaystyle to "false" within
+   overscript, but increments scriptlevel by 1 only when accent is "false".
+   Within base, it always leaves both attributes unchanged.
+
+   The munderover [..] always sets displaystyle to "false" within underscript
+   and overscript, but increments scriptlevel by 1 only when accentunder or
+   accent, respectively, are "false". Within base, it always leaves both
+   attributes unchanged.
+-->   
+    <randomelement>
+      <mstyle scriptsizemultiplier="2">
+        <munder>
+          <mtext>O</mtext>
+          <mtext>O</mtext>
+        </munder>
+        <mover>
+          <mtext>O</mtext>
+          <mtext>O</mtext>
+        </mover>
+        <munderover>
+          <mtext>O</mtext>
+          <mtext>O</mtext>
+          <mtext>O</mtext>
+        </munderover>
+      </mstyle>
+    </randomelement>
+
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/mathml/disabled-scriptlevel-1.html
@@ -0,0 +1,129 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <title>scriptlevel</title>
+    <meta charset="utf-8"/>
+  </head>
+  <body>
+
+    <!-- Test scriptlevel on mstyle -->
+    <math>
+      <mstyle scriptsizemultiplier="2">
+        <mtext>O</mtext>
+        <mstyle scriptlevel="1"><mtext>O</mtext></mstyle>
+      </mstyle>
+    </math>
+
+    <!-- The mfrac element sets displaystyle to "false", or if it was already
+         false increments scriptlevel by 1, within numerator and denominator.
+      -->
+    <math>
+      <mstyle scriptsizemultiplier="2">
+        <mstyle displaystyle="false">
+          <mfrac>
+            <mtext>O</mtext>
+            <mtext>O</mtext>
+          </mfrac>
+        </mstyle>
+        <mstyle displaystyle="true">
+          <mfrac>
+            <mtext>O</mtext>
+            <mtext>O</mtext>
+          </mfrac>
+        </mstyle>
+      </mstyle>
+    </math>
+
+    <!--    The mroot element increments scriptlevel by 2, and sets
+            displaystyle to "false", within index, but leaves both attributes
+            unchanged within base.
+            The msqrt element leaves both attributes unchanged within its
+            argument. -->
+    <math>
+      <mstyle scriptsizemultiplier="2">
+        <mroot>
+          <mtext>O</mtext>
+          <mtext>O</mtext>
+        </mroot>
+        <msqrt>
+          <mtext>O</mtext>
+        </msqrt>
+      </mstyle>
+    </math>
+
+<!--
+    The msub element [...] increments scriptlevel by 1, and sets displaystyle to
+   "false", within subscript, but leaves both attributes unchanged within base.
+
+   The msup element [...] increments scriptlevel by 1, and sets displaystyle to
+   "false", within superscript, but leaves both attributes unchanged within
+   base.
+
+   The msubsup element [...] increments scriptlevel by 1, and sets displaystyle
+   to "false", within subscript and superscript, but leaves both attributes
+   unchanged within base.
+
+   The mmultiscripts element increments scriptlevel by 1, and sets displaystyle
+   to "false", within each of its arguments except base, but leaves both
+   attributes unchanged within base.
+   -->
+    <math>
+      <mstyle scriptsizemultiplier="2">
+        <msub>
+          <mtext>O</mtext>
+          <mtext>O</mtext>
+        </msub>
+        <msup>
+          <mtext>O</mtext>
+          <mtext>O</mtext>
+        </msup>
+        <msubsup>
+          <mtext>O</mtext>
+          <mtext>O</mtext>
+          <mtext>O</mtext>
+        </msubsup>
+        <mmultiscripts>
+          <mtext>O</mtext>
+          <mtext>O</mtext>
+          <mtext>O</mtext>
+          <mprescripts/>
+          <mtext>O</mtext>
+          <mtext>O</mtext>
+        </mmultiscripts>
+      </mstyle>
+    </math>
+
+<!-- 
+   The munder element [...] always sets displaystyle to "false" within the
+   underscript, but increments scriptlevel by 1 only when accentunder is
+   "false". Within base, it always leaves both attributes unchanged.
+
+   The mover element [...] always sets displaystyle to "false" within
+   overscript, but increments scriptlevel by 1 only when accent is "false".
+   Within base, it always leaves both attributes unchanged.
+
+   The munderover [..] always sets displaystyle to "false" within underscript
+   and overscript, but increments scriptlevel by 1 only when accentunder or
+   accent, respectively, are "false". Within base, it always leaves both
+   attributes unchanged.
+-->   
+    <math>
+      <mstyle scriptsizemultiplier="2">
+        <munder>
+          <mtext>O</mtext>
+          <mtext>O</mtext>
+        </munder>
+        <mover>
+          <mtext>O</mtext>
+          <mtext>O</mtext>
+        </mover>
+        <munderover>
+          <mtext>O</mtext>
+          <mtext>O</mtext>
+          <mtext>O</mtext>
+        </munderover>
+      </mstyle>
+    </math>
+
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/mathml/disabled-scriptlevel-1.xhtml
@@ -0,0 +1,133 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+  <head>
+    <title>scriptlevel</title>
+    <meta charset="utf-8"/>
+    <style>
+      h2 {
+        text-align:center;
+      }
+    </style>
+  </head>
+  <body>
+
+    <!-- Test scriptlevel on mstyle -->
+    <math xmlns="http://www.w3.org/1998/Math/MathML">
+      <mstyle scriptsizemultiplier="2">
+        <mtext>O</mtext>
+        <mstyle scriptlevel="1"><mtext>O</mtext></mstyle>
+      </mstyle>
+    </math>
+
+    <!-- The mfrac element sets displaystyle to "false", or if it was already
+         false increments scriptlevel by 1, within numerator and denominator.
+      -->
+    <math xmlns="http://www.w3.org/1998/Math/MathML">
+      <mstyle scriptsizemultiplier="2">
+        <mstyle displaystyle="false">
+          <mfrac>
+            <mtext>O</mtext>
+            <mtext>O</mtext>
+          </mfrac>
+        </mstyle>
+        <mstyle displaystyle="true">
+          <mfrac>
+            <mtext>O</mtext>
+            <mtext>O</mtext>
+          </mfrac>
+        </mstyle>
+      </mstyle>
+    </math>
+
+    <!--    The mroot element increments scriptlevel by 2, and sets
+            displaystyle to "false", within index, but leaves both attributes
+            unchanged within base.
+            The msqrt element leaves both attributes unchanged within its
+            argument. -->
+    <math xmlns="http://www.w3.org/1998/Math/MathML">
+      <mstyle scriptsizemultiplier="2">
+        <mroot>
+          <mtext>O</mtext>
+          <mtext>O</mtext>
+        </mroot>
+        <msqrt>
+          <mtext>O</mtext>
+        </msqrt>
+      </mstyle>
+    </math>
+
+<!--
+    The msub element [...] increments scriptlevel by 1, and sets displaystyle to
+   "false", within subscript, but leaves both attributes unchanged within base.
+
+   The msup element [...] increments scriptlevel by 1, and sets displaystyle to
+   "false", within superscript, but leaves both attributes unchanged within
+   base.
+
+   The msubsup element [...] increments scriptlevel by 1, and sets displaystyle
+   to "false", within subscript and superscript, but leaves both attributes
+   unchanged within base.
+
+   The mmultiscripts element increments scriptlevel by 1, and sets displaystyle
+   to "false", within each of its arguments except base, but leaves both
+   attributes unchanged within base.
+   -->
+    <math xmlns="http://www.w3.org/1998/Math/MathML">
+      <mstyle scriptsizemultiplier="2">
+        <msub>
+          <mtext>O</mtext>
+          <mtext>O</mtext>
+        </msub>
+        <msup>
+          <mtext>O</mtext>
+          <mtext>O</mtext>
+        </msup>
+        <msubsup>
+          <mtext>O</mtext>
+          <mtext>O</mtext>
+          <mtext>O</mtext>
+        </msubsup>
+        <mmultiscripts>
+          <mtext>O</mtext>
+          <mtext>O</mtext>
+          <mtext>O</mtext>
+          <mprescripts/>
+          <mtext>O</mtext>
+          <mtext>O</mtext>
+        </mmultiscripts>
+      </mstyle>
+    </math>
+
+<!-- 
+   The munder element [...] always sets displaystyle to "false" within the
+   underscript, but increments scriptlevel by 1 only when accentunder is
+   "false". Within base, it always leaves both attributes unchanged.
+
+   The mover element [...] always sets displaystyle to "false" within
+   overscript, but increments scriptlevel by 1 only when accent is "false".
+   Within base, it always leaves both attributes unchanged.
+
+   The munderover [..] always sets displaystyle to "false" within underscript
+   and overscript, but increments scriptlevel by 1 only when accentunder or
+   accent, respectively, are "false". Within base, it always leaves both
+   attributes unchanged.
+-->   
+    <math xmlns="http://www.w3.org/1998/Math/MathML">
+      <mstyle scriptsizemultiplier="2">
+        <munder>
+          <mtext>O</mtext>
+          <mtext>O</mtext>
+        </munder>
+        <mover>
+          <mtext>O</mtext>
+          <mtext>O</mtext>
+        </mover>
+        <munderover>
+          <mtext>O</mtext>
+          <mtext>O</mtext>
+          <mtext>O</mtext>
+        </munderover>
+      </mstyle>
+    </math>
+
+  </body>
+</html>
--- a/layout/reftests/mathml/reftest.list
+++ b/layout/reftests/mathml/reftest.list
@@ -6,16 +6,18 @@
 == dir-6.html dir-6-ref.html
 == dir-6a.html dir-6a-ref.html
 == dir-7.html dir-7-ref.html
 fails == dir-8.html dir-8-ref.html
 fails == dir-9.html dir-9-ref.html # Bug 787215
 == dir-10.html dir-10-ref.html
 random-if((B2G&&browserIsRemote)||Mulet) == dir-11.html dir-11-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
 == css-spacing-1.html css-spacing-1-ref.html
+pref(mathml.disabled,true) == disabled-scriptlevel-1.html disabled-scriptlevel-1-ref.html
+pref(mathml.disabled,true) == disabled-scriptlevel-1.xhtml disabled-scriptlevel-1-ref.xhtml
 == displaystyle-1.html displaystyle-1-ref.html
 == displaystyle-2.html displaystyle-2-ref.html
 == displaystyle-3.html displaystyle-3-ref.html
 random-if((B2G&&browserIsRemote)||Mulet) == displaystyle-4.html displaystyle-4-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
 skip-if((B2G&&browserIsRemote)||Mulet) random-if(smallScreen&&Android) fuzzy(255,200) == mirror-op-1.html mirror-op-1-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
 != mirror-op-2.html mirror-op-2-ref.html
 != mirror-op-3.html mirror-op-3-ref.html
 != mirror-op-4.html mirror-op-4-ref.html
--- a/layout/style/ServoBindings.cpp
+++ b/layout/style/ServoBindings.cpp
@@ -227,17 +227,18 @@ AtomAttrValue(Implementor* aElement, nsI
   return attr ? attr->GetAtomValue() : nullptr;
 }
 
 template <typename Implementor, typename MatchFn>
 static bool
 DoMatch(Implementor* aElement, nsIAtom* aNS, nsIAtom* aName, MatchFn aMatch)
 {
   if (aNS) {
-    int32_t ns = nsContentUtils::NameSpaceManager()->GetNameSpaceID(aNS);
+    int32_t ns = nsContentUtils::NameSpaceManager()->GetNameSpaceID(aNS,
+                                                                    aElement->IsInChromeDocument());
     NS_ENSURE_TRUE(ns != kNameSpaceID_Unknown, false);
     const nsAttrValue* value = aElement->GetParsedAttr(aName, ns);
     return value && aMatch(value);
   }
   // No namespace means any namespace - we have to check them all. :-(
   BorrowedAttrInfo attrInfo;
   for (uint32_t i = 0; (attrInfo = aElement->GetAttrInfoAt(i)); ++i) {
     if (attrInfo.mName->LocalName() != aName) {
--- a/layout/style/ServoElementSnapshot.cpp
+++ b/layout/style/ServoElementSnapshot.cpp
@@ -2,27 +2,30 @@
 /* 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 "mozilla/ServoElementSnapshot.h"
 #include "mozilla/dom/Element.h"
 #include "nsIContentInlines.h"
+#include "nsContentUtils.h"
 
 namespace mozilla {
 
 ServoElementSnapshot::ServoElementSnapshot(Element* aElement)
   : mContains(Flags(0))
   , mState(0)
   , mExplicitRestyleHint(nsRestyleHint(0))
   , mExplicitChangeHint(nsChangeHint(0))
 {
   mIsHTMLElementInHTMLDocument =
     aElement->IsHTMLElement() && aElement->IsInHTMLDocument();
+  mIsInChromeDocument =
+    nsContentUtils::IsChromeDoc(aElement->OwnerDoc());
 }
 
 void
 ServoElementSnapshot::AddAttrs(Element* aElement)
 {
   MOZ_ASSERT(aElement);
 
   if (!HasAny(Flags::Attributes)) {
--- a/layout/style/ServoElementSnapshot.h
+++ b/layout/style/ServoElementSnapshot.h
@@ -138,26 +138,32 @@ public:
       if (mAttrs[i].mName.Equals(aLocalName, aNamespaceID)) {
         return &mAttrs[i].mValue;
       }
     }
 
     return nullptr;
   }
 
+  bool IsInChromeDocument()
+  {
+    return mIsInChromeDocument;
+  }
+
   bool HasAny(Flags aFlags) { return bool(mContains & aFlags); }
 
 private:
   // TODO: Profile, a 1 or 2 element AutoTArray could be worth it, given we know
   // we're dealing with attribute changes when we take snapshots of attributes,
   // though it can be wasted space if we deal with a lot of state-only
   // snapshots.
   Flags mContains;
   nsTArray<ServoAttrSnapshot> mAttrs;
   ServoStateType mState;
   nsRestyleHint mExplicitRestyleHint;
   nsChangeHint mExplicitChangeHint;
   bool mIsHTMLElementInHTMLDocument;
+  bool mIsInChromeDocument;
 };
 
 } // namespace mozilla
 
 #endif
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -275,16 +275,19 @@ pref("browser.chrome.image_icons.max_siz
 pref("browser.triple_click_selects_paragraph", true);
 
 // Print/Preview Shrink-To-Fit won't shrink below 20% for text-ish documents.
 pref("print.shrink-to-fit.scale-limit-percent", 20);
 
 // Whether we should display simplify page checkbox on print preview UI
 pref("print.use_simplify_page", false);
 
+// Disable support for MathML
+pref("mathml.disabled",    false);
+
 // Enable scale transform for stretchy MathML operators. See bug 414277.
 pref("mathml.scale_stretchy_operators.enabled", true);
 
 // Media cache size in kilobytes
 pref("media.cache_size", 512000);
 // When a network connection is suspended, don't resume it until the
 // amount of buffered data falls below this threshold (in seconds).
 pref("media.cache_resume_threshold", 999999);
--- a/testing/web-platform/mozilla/meta/MANIFEST.json
+++ b/testing/web-platform/mozilla/meta/MANIFEST.json
@@ -4,16 +4,25 @@
     "reftest": [],
     "stub": [],
     "testharness": [],
     "wdspec": []
   },
   "local_changes": {
     "deleted": [],
     "deleted_reftests": {},
-    "items": {},
+    "items": {
+      "testharness": {
+        "html/syntax/parsing/math-parse01.html": [
+          {
+            "path": "html/syntax/parsing/math-parse01.html",
+            "url": "/html/syntax/parsing/math-parse01.html"
+          }
+        ]
+      }
+    },
     "reftest_nodes": {}
   },
   "reftest_nodes": {},
   "rev": null,
   "url_base": "/_mozilla/",
   "version": 3
 }
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/mozilla/meta/html/syntax/parsing/math-parse01.html.ini
@@ -0,0 +1,2 @@
+[math-parse01.html]
+  prefs: ["mathml.disabled:true"]
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/mozilla/tests/html/syntax/parsing/math-parse01.html
@@ -0,0 +1,62 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>math in html: parsing</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<h1>math in html: parsing</h1>
+
+<div id="log" style="display:block"></div>
+
+<div style="display:none">
+<div><math id="m1"><mtext/></math></div>
+<div id="d1"><math><mrow/><mi/></math></div>
+<div id="d2"><math><mrow><mrow><mn>1</mn></mrow><mi>a</mi></mrow></math></div>
+<div id="d3">&lang;&rang;</div>
+<div id="d4">&Kopf;</div>
+<div id="d5"><math><semantics><mi>a</mi><annotation-xml><foo/><bar/></annotation-xml></semantics></math></div>
+<div id="d6"><math><semantics><mi>a</mi><annotation-xml encoding="text/html"><div></div></annotation-xml></semantics><mn/></math>
+</div>
+
+
+<script>
+
+test(function() {
+assert_equals(document.getElementById("m1"),document.getElementsByTagName("math")[0]);
+},"The id attribute should be recognised on math elements");
+
+test(function() {
+assert_equals(document.getElementById("d1").firstChild.nodeName,"math")
+},"The node name should be math");
+
+test(function() {
+assert_equals(document.getElementById("d1").firstChild.namespaceURI ,"http://www.w3.org/1998/Math/MathML")
+},"math should be in MathML Namespace");
+
+test(function() {
+assert_equals(document.getElementById("d1").firstChild.childNodes.length ,2)
+},"Math has 2 children (empty tag syntax)");
+
+test(function() {
+assert_equals(document.getElementById("d2").firstChild.childNodes.length ,1)
+},"Nested mrow elements should be parsed correctly");
+
+test(function() {
+assert_equals(document.getElementById("d3").firstChild.nodeValue ,"\u27E8\u27E9")
+},"Testing rang and lang entity code points");
+
+test(function() {
+assert_equals(document.getElementById("d4").firstChild.nodeValue ,"\uD835\uDD42")
+},"Testing Kopf (Plane 1) entity code point");
+
+test(function() {
+assert_equals(document.getElementById("d5").firstChild.firstChild.childNodes[1].childNodes.length ,2)
+},"Empty element tags in annotation-xml parsed as per XML.");
+
+test(function() {
+assert_equals(document.getElementById("d6").firstChild.childNodes.length ,2)
+},"html tags allowed in annotation-xml/@encoding='text/html'.");
+
+</script>