[tbb-commits] [tor-browser/tor-browser-38.1.0esr-5.0-1] fixup! Bug 12827: Create preference to disable SVG.

mikeperry at torproject.org mikeperry at torproject.org
Fri Jul 24 07:01:35 UTC 2015


commit 9e59dae7fdb9527f688b03fa314e917712024d4b
Author: Kathy Brade <brade at pearlcrescent.com>
Date:   Tue Jul 21 15:46:12 2015 -0400

    fixup! Bug 12827: Create preference to disable SVG.
    
    Add NS_SVGEnabled() checks to the nsIContent::IsSVG() methods to
    avoid crashing due to code that assumes elements with an SVG namespace
    are always represented by nsSVGElement objects (some existing Mozilla
    code uses static_cast to convert to an nsSVGElement pointer).
    Also, cache the SVG enabled/disabled status within each document so
    that it persists until the document is reloaded.
    
    Also fix another case where the HTML parser failed to check for a
    failed QI.
    
    Fixes ticket #16495.
---
 dom/base/Element.cpp                   |   20 ++++++++++++++++
 dom/base/nsDocument.cpp                |    5 +++-
 dom/base/nsIContent.h                  |   10 ++------
 dom/base/nsIDocument.h                 |   25 ++++++++++++++++++++
 layout/svg/nsSVGUtils.cpp              |   39 +++++++++++++++++++++++++++-----
 layout/svg/nsSVGUtils.h                |    1 +
 parser/html/nsHtml5DocumentBuilder.cpp |   24 ++++++++++----------
 7 files changed, 97 insertions(+), 27 deletions(-)

diff --git a/dom/base/Element.cpp b/dom/base/Element.cpp
index 02035f4..bd795b2 100644
--- a/dom/base/Element.cpp
+++ b/dom/base/Element.cpp
@@ -143,6 +143,26 @@
 using namespace mozilla;
 using namespace mozilla::dom;
 
+// These IsSVG() methods were moved here from nsIContent.h because including
+// nsSVGUtils.h in nsIContent.h (needed to to pick up the NS_SVGEnabled()
+// prototype) creates a circular dependency: nsSVGUtils.h includes other
+// headers that define functions that require the complete definition of
+// nsPresContext... but nsPresContext.h includes nsIPresShell.h, which in turn
+// includes nsIContent.h.
+bool
+nsIContent::IsSVG() const
+{
+  return NS_SVGEnabled(mNodeInfo->GetDocument()) &&
+         IsInNamespace(kNameSpaceID_SVG);
+}
+
+bool
+nsIContent::IsSVG(nsIAtom* aTag) const
+{
+  return NS_SVGEnabled(mNodeInfo->GetDocument()) &&
+         mNodeInfo->Equals(aTag, kNameSpaceID_SVG);
+}
+
 nsIAtom*
 nsIContent::DoGetID() const
 {
diff --git a/dom/base/nsDocument.cpp b/dom/base/nsDocument.cpp
index 47f611e..c30306f 100644
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -1575,7 +1575,8 @@ nsIDocument::nsIDocument()
     mIsBeingUsedAsImage(false),
     mHasLinksToUpdate(false),
     mPartID(0),
-    mDidFireDOMContentLoaded(true)
+    mDidFireDOMContentLoaded(true),
+    mSVGStatus(mozilla::dom::SVGStatus_Unknown)
 {
   SetInDocument();
 
@@ -2420,6 +2421,8 @@ nsDocument::ResetToURI(nsIURI *aURI, nsILoadGroup *aLoadGroup,
 
   mXMLDeclarationBits = 0;
 
+  mSVGStatus = SVGStatus_Unknown;
+
   // Now get our new principal
   if (aPrincipal) {
     SetPrincipal(aPrincipal);
diff --git a/dom/base/nsIContent.h b/dom/base/nsIContent.h
index b0a251f3..135e79e 100644
--- a/dom/base/nsIContent.h
+++ b/dom/base/nsIContent.h
@@ -274,15 +274,9 @@ public:
     return mNodeInfo->Equals(aTag, kNameSpaceID_XHTML);
   }
 
-  inline bool IsSVG() const
-  {
-    return IsInNamespace(kNameSpaceID_SVG);
-  }
+  bool IsSVG() const;
 
-  inline bool IsSVG(nsIAtom* aTag) const
-  {
-    return mNodeInfo->Equals(aTag, kNameSpaceID_SVG);
-  }
+  bool IsSVG(nsIAtom* aTag) const;
 
   inline bool IsXUL() const
   {
diff --git a/dom/base/nsIDocument.h b/dom/base/nsIDocument.h
index 8c8fd7b..201d59f 100644
--- a/dom/base/nsIDocument.h
+++ b/dom/base/nsIDocument.h
@@ -143,6 +143,12 @@ struct FullScreenOptions {
   nsRefPtr<gfx::VRHMDInfo> mVRHMDDevice;
 };
 
+typedef enum {
+  SVGStatus_Unknown = 0,
+  SVGStatus_Enabled,
+  SVGStatus_Disabled
+} SVGStatus;
+
 } // namespace dom
 } // namespace mozilla
 
@@ -617,6 +623,22 @@ public:
   }
 
   /**
+   * Get the cached SVG status for this document.
+   */
+  mozilla::dom::SVGStatus GetSVGStatus() const
+  {
+    return mSVGStatus;
+  }
+
+  /**
+   * Set the cached SVG status for this document.
+   */
+  void SetSVGStatus(mozilla::dom::SVGStatus svgStatus)
+  {
+    mSVGStatus = svgStatus;
+  }
+
+  /**
    * Access HTTP header data (this may also get set from other
    * sources, like HTML META tags).
    */
@@ -2874,6 +2896,9 @@ protected:
 
   // Our live MediaQueryLists
   PRCList mDOMMediaQueryLists;
+
+  // Cached value that indicates whether SVG is enabled for this document.
+  mozilla::dom::SVGStatus mSVGStatus;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsIDocument, NS_IDOCUMENT_IID)
diff --git a/layout/svg/nsSVGUtils.cpp b/layout/svg/nsSVGUtils.cpp
index 0b820ee..c2b6608 100644
--- a/layout/svg/nsSVGUtils.cpp
+++ b/layout/svg/nsSVGUtils.cpp
@@ -54,6 +54,7 @@
 #include "nsContentUtils.h"
 #include "SVGContentUtils.h"
 #include "mozilla/unused.h"
+#include "nsIDocument.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
@@ -67,11 +68,27 @@ static bool sSVGNewGetBBoxEnabled;
 
 // Determine if SVG should be enabled for aDoc.  The svg.in-content.enabled
 // preference is checked as well as whether aDoc is a content or chrome doc.
-// If aChannel is NULL, the pref. value is returned.
+// If aDoc is NULL, the pref. value is returned.
+// Once we determine whether SVG is allowed for a given document, we record
+// that fact inside the document.  This is necessary to avoid crashes due
+// to code that uses static_cast to cast an element object to an nsSVGElement
+// object.  When SVG is disabled, <svg> and related tags are not represented
+// by nsSVGElement objects.
 bool
 NS_SVGEnabled(nsIDocument *aDoc)
 {
-  return NS_SVGEnabledForChannel(aDoc ? aDoc->GetChannel() : nullptr);
+  if (!aDoc)
+    return NS_SVGEnabledForChannel(nullptr);
+
+  mozilla::dom::SVGStatus svgStatus = aDoc->GetSVGStatus();
+  if (svgStatus == mozilla::dom::SVGStatus_Unknown)
+  {
+    svgStatus = NS_SVGEnabledForChannel(aDoc->GetChannel()) ?
+           mozilla::dom::SVGStatus_Enabled : mozilla::dom::SVGStatus_Disabled;
+    aDoc->SetSVGStatus(svgStatus);
+  }
+
+  return (svgStatus == mozilla::dom::SVGStatus_Enabled);
 }
 
 // Determine if SVG should be enabled for aChannel.  The svg.in-content.enabled
@@ -948,10 +965,11 @@ nsSVGUtils::GetBBox(nsIFrame *aFrame, uint32_t aFlags)
       // needs investigation to check that we won't break too much content.
       // NOTE: When changing this to apply to other frame types, make sure to
       // also update nsSVGUtils::FrameSpaceInCSSPxToUserSpaceOffset.
-      MOZ_ASSERT(content->IsSVG(), "bad cast");
-      nsSVGElement *element = static_cast<nsSVGElement*>(content);
-      matrix = element->PrependLocalTransformsTo(matrix,
+      if (content->IsSVG()) {
+        nsSVGElement *element = static_cast<nsSVGElement*>(content);
+        matrix = element->PrependLocalTransformsTo(matrix,
                           nsSVGElement::eChildToUserSpace);
+      }
     }
     bbox = svg->GetBBoxContribution(ToMatrix(matrix), aFlags).ToThebesRect();
     // Account for 'clipped'.
@@ -1151,7 +1169,9 @@ nsSVGUtils::GetNonScalingStrokeTransform(nsIFrame *aFrame,
   }
 
   nsIContent *content = aFrame->GetContent();
-  MOZ_ASSERT(content->IsSVG(), "bad cast");
+  if (!content->IsSVG()) {
+    return false;
+  }
 
   *aUserToOuterSVG = ThebesMatrix(SVGContentUtils::GetCTM(
                        static_cast<nsSVGElement*>(content), true));
@@ -1442,6 +1462,10 @@ nsSVGUtils::GetStrokeWidth(nsIFrame* aFrame, gfxTextContextPaint *aContextPaint)
     content = content->GetParent();
   }
 
+  if (!aFrame->GetContent()->IsSVG()) {
+    return 0.0;
+  }
+
   nsSVGElement *ctx = static_cast<nsSVGElement*>(content);
 
   return SVGContentUtils::CoordToFloat(ctx, style->mStrokeWidth);
@@ -1455,6 +1479,9 @@ GetStrokeDashData(nsIFrame* aFrame,
 {
   const nsStyleSVG* style = aFrame->StyleSVG();
   nsIContent *content = aFrame->GetContent();
+  if (!content->IsSVG()) {
+    return false;
+  }
   nsSVGElement *ctx = static_cast<nsSVGElement*>
     (content->IsNodeOfType(nsINode::eTEXT) ?
      content->GetParent() : content);
diff --git a/layout/svg/nsSVGUtils.h b/layout/svg/nsSVGUtils.h
index 7b3edbb..16e73b6 100644
--- a/layout/svg/nsSVGUtils.h
+++ b/layout/svg/nsSVGUtils.h
@@ -30,6 +30,7 @@
 class gfxContext;
 class gfxPattern;
 class nsFrameList;
+class nsIChannel;
 class nsIContent;
 class nsIDocument;
 class nsIFrame;
diff --git a/parser/html/nsHtml5DocumentBuilder.cpp b/parser/html/nsHtml5DocumentBuilder.cpp
index 08e4d8a..4a705c5 100644
--- a/parser/html/nsHtml5DocumentBuilder.cpp
+++ b/parser/html/nsHtml5DocumentBuilder.cpp
@@ -67,18 +67,18 @@ nsHtml5DocumentBuilder::UpdateStyleSheet(nsIContent* aElement)
   }
 
   nsCOMPtr<nsIStyleSheetLinkingElement> ssle(do_QueryInterface(aElement));
-  NS_ASSERTION(ssle, "Node didn't QI to style.");
-
-  ssle->SetEnableUpdates(true);
-
-  bool willNotify;
-  bool isAlternate;
-  nsresult rv = ssle->UpdateStyleSheet(mRunsToCompletion ? nullptr : this,
-                                       &willNotify,
-                                       &isAlternate);
-  if (NS_SUCCEEDED(rv) && willNotify && !isAlternate && !mRunsToCompletion) {
-    ++mPendingSheetCount;
-    mScriptLoader->AddExecuteBlocker();
+  if (ssle) {
+    ssle->SetEnableUpdates(true);
+
+    bool willNotify;
+    bool isAlternate;
+    nsresult rv = ssle->UpdateStyleSheet(mRunsToCompletion ? nullptr : this,
+                                         &willNotify,
+                                         &isAlternate);
+    if (NS_SUCCEEDED(rv) && willNotify && !isAlternate && !mRunsToCompletion) {
+      ++mPendingSheetCount;
+      mScriptLoader->AddExecuteBlocker();
+    }
   }
 
   if (aElement->IsHTML(nsGkAtoms::link)) {





More information about the tbb-commits mailing list