Bug 615146 - Ensure outer SVG elements get an nsSVGOuterSVGFrame even they fail conditional processing attributes (v2) r=longsonr, a=blocking
authorCameron McCormack <cam@mcc.id.au>
Tue, 07 Dec 2010 09:57:18 +1300
changeset 58894 395fac6a7de431827a5254d47ed92c5dafa8b489
parent 58893 10fc5a720ed06d276207b518fcc847650378fa22
child 58895 e741896a62ae01e23f682e9cafad80c98faf7926
push idunknown
push userunknown
push dateunknown
reviewerslongsonr, blocking
bugs615146
milestone2.0b8pre
Bug 615146 - Ensure outer SVG elements get an nsSVGOuterSVGFrame even they fail conditional processing attributes (v2) r=longsonr, a=blocking
layout/base/crashtests/615146-1.html
layout/base/crashtests/crashtests.list
layout/base/nsCSSFrameConstructor.cpp
layout/reftests/svg/conditions-05.svg
layout/reftests/svg/dynamic-conditions-02.svg
layout/reftests/svg/dynamic-conditions-03.svg
layout/reftests/svg/dynamic-conditions-04.svg
layout/reftests/svg/dynamic-conditions-05.svg
layout/reftests/svg/dynamic-conditions-06.svg
layout/reftests/svg/dynamic-conditions-07.svg
layout/reftests/svg/dynamic-conditions-08.svg
layout/reftests/svg/dynamic-conditions-09.svg
layout/reftests/svg/dynamic-conditions-10.svg
layout/reftests/svg/dynamic-conditions-11.svg
layout/reftests/svg/dynamic-conditions-12.svg
layout/reftests/svg/dynamic-conditions-13.svg
layout/reftests/svg/reftest.list
layout/reftests/svg/svg-integration/conditions-outer-svg-01.xhtml
layout/reftests/svg/svg-integration/conditions-outer-svg-02.xhtml
layout/reftests/svg/svg-integration/dynamic-conditions-outer-svg-01.xhtml
layout/reftests/svg/svg-integration/dynamic-conditions-outer-svg-02.xhtml
layout/reftests/svg/svg-integration/dynamic-conditions-outer-svg-03.xhtml
layout/reftests/svg/svg-integration/dynamic-conditions-outer-svg-04.xhtml
layout/reftests/svg/svg-integration/reftest.list
layout/svg/base/src/nsSVGClipPathFrame.cpp
layout/svg/base/src/nsSVGClipPathFrame.h
layout/svg/base/src/nsSVGContainerFrame.cpp
layout/svg/base/src/nsSVGForeignObjectFrame.cpp
layout/svg/base/src/nsSVGGeometryFrame.cpp
layout/svg/base/src/nsSVGGeometryFrame.h
layout/svg/base/src/nsSVGOuterSVGFrame.cpp
layout/svg/base/src/nsSVGOuterSVGFrame.h
layout/svg/base/src/nsSVGPathGeometryFrame.cpp
layout/svg/base/src/nsSVGUtils.h
new file mode 100644
--- /dev/null
+++ b/layout/base/crashtests/615146-1.html
@@ -0,0 +1,1 @@
+<!DOCTYPE html><svg requiredExtensions=e><foreignObject>
--- a/layout/base/crashtests/crashtests.list
+++ b/layout/base/crashtests/crashtests.list
@@ -310,10 +310,11 @@ load 567292-1.xhtml
 load 569018-1.html
 load 572003.xul
 load 572582-1.xhtml
 load 579655.html
 load 580494-1.html
 load 580834-1.xhtml
 load 606432-1.html
 load 609821-1.xhtml
+load 615146-1.html
 load 615781-1.xhtml
 load 616495-single-side-composite-color-border.html
--- a/layout/base/nsCSSFrameConstructor.cpp
+++ b/layout/base/nsCSSFrameConstructor.cpp
@@ -4801,38 +4801,36 @@ nsCSSFrameConstructor::FindSVGData(nsICo
     return &sSuppressData;
   }
 
   // We don't need frames for animation elements
   if (aContent->IsNodeOfType(nsINode::eANIMATION)) {
     return &sSuppressData;
   }
 
-  // Elements with failing conditional processing attributes never get
-  // rendered.  Note that this is not where we select which frame in a
-  // <switch> to render!  That happens in nsSVGSwitchFrame::PaintSVG.
-  if (!nsSVGFeatures::PassesConditionalProcessingTests(aContent)) {
-    return &sContainerData;
-  }
-
-  // Special case for aTag == nsGkAtoms::svg because we don't want to
-  // have to recompute parentIsSVG for it.
-  if (aTag == nsGkAtoms::svg) {
-    if (parentIsSVG) {
-      static const FrameConstructionData sInnerSVGData =
-        SIMPLE_SVG_FCDATA(NS_NewSVGInnerSVGFrame);
-      return &sInnerSVGData;
-    }
-
+  if (aTag == nsGkAtoms::svg && !parentIsSVG) {
+    // We need outer <svg> elements to have an nsSVGOuterSVGFrame regardless
+    // of whether they fail conditional processing attributes, since various
+    // SVG frames assume that one exists.  We handle the non-rendering
+    // of failing outer <svg> element contents like <switch> statements,
+    // and do the PassesConditionalProcessingTests call in
+    // nsSVGOuterSVGFrame::Init.
     static const FrameConstructionData sOuterSVGData =
       FCDATA_DECL(FCDATA_FORCE_VIEW | FCDATA_SKIP_ABSPOS_PUSH |
                   FCDATA_DISALLOW_GENERATED_CONTENT,
                   NS_NewSVGOuterSVGFrame);
     return &sOuterSVGData;
   }
+  
+  if (!nsSVGFeatures::PassesConditionalProcessingTests(aContent)) {
+    // Elements with failing conditional processing attributes never get
+    // rendered.  Note that this is not where we select which frame in a
+    // <switch> to render!  That happens in nsSVGSwitchFrame::PaintSVG.
+    return &sContainerData;
+  }
 
   // Special cases for text/tspan/textPath, because the kind of frame
   // they get depends on the parent frame.  We ignore 'a' elements when
   // determining the parent, however.
   nsIFrame *ancestorFrame =
     nsSVGUtils::GetFirstNonAAncestorFrame(aParentFrame);
   if (ancestorFrame) {
     if (aTag == nsGkAtoms::tspan || aTag == nsGkAtoms::altGlyph) {
@@ -4853,16 +4851,17 @@ nsCSSFrameConstructor::FindSVGData(nsICo
       nsSVGTextContainerFrame* metrics = do_QueryFrame(ancestorFrame);
       if (metrics) {
         return &sSuppressData;
       }
     }
   }
 
   static const FrameConstructionDataByTag sSVGData[] = {
+    SIMPLE_SVG_CREATE(svg, NS_NewSVGInnerSVGFrame),
     SIMPLE_SVG_CREATE(g, NS_NewSVGGFrame),
     SIMPLE_SVG_CREATE(svgSwitch, NS_NewSVGSwitchFrame),
     SIMPLE_SVG_CREATE(polygon, NS_NewSVGPathGeometryFrame),
     SIMPLE_SVG_CREATE(polyline, NS_NewSVGPathGeometryFrame),
     SIMPLE_SVG_CREATE(circle, NS_NewSVGPathGeometryFrame),
     SIMPLE_SVG_CREATE(ellipse, NS_NewSVGPathGeometryFrame),
     SIMPLE_SVG_CREATE(line, NS_NewSVGPathGeometryFrame),
     SIMPLE_SVG_CREATE(rect, NS_NewSVGPathGeometryFrame),
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/conditions-05.svg
@@ -0,0 +1,11 @@
+<?xml version="1.0"?>
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/licenses/publicdomain/
+-->
+<svg xmlns="http://www.w3.org/2000/svg" requiredExtensions="x">
+  <title>Test that conditional processing attributes on outer 'svg' elements are honored</title>
+  <!-- https://bugzilla.mozilla.org/show_bug.cgi?id=615146 -->
+
+  <rect width="100%" height="100%" fill="red"/>
+</svg>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/dynamic-conditions-02.svg
@@ -0,0 +1,11 @@
+<?xml version="1.0"?>
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/licenses/publicdomain/
+-->
+<svg xmlns="http://www.w3.org/2000/svg" onload="this.setAttribute('requiredExtensions', 'x')">
+  <title>Test that dynamically changed conditional processing attributes on outer 'svg' elements are honored</title>
+  <!-- https://bugzilla.mozilla.org/show_bug.cgi?id=615146 -->
+
+  <rect width="100%" height="100%" fill="red"/>
+</svg>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/dynamic-conditions-03.svg
@@ -0,0 +1,11 @@
+<?xml version="1.0"?>
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/licenses/publicdomain/
+-->
+<svg xmlns="http://www.w3.org/2000/svg" requiredExtensions="x" onload="this.removeAttribute('requiredExtensions')">
+  <title>Test that dynamically changed conditional processing attributes on outer 'svg' elements are honored</title>
+  <!-- https://bugzilla.mozilla.org/show_bug.cgi?id=615146 -->
+
+  <rect width="100%" height="100%" fill="lime"/>
+</svg>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/dynamic-conditions-04.svg
@@ -0,0 +1,11 @@
+<?xml version="1.0"?>
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/licenses/publicdomain/
+-->
+<svg xmlns="http://www.w3.org/2000/svg" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Gradient" onload="this.setAttribute('requiredFeatures', 'x')">
+  <title>Test that dynamically changed conditional processing attributes on outer 'svg' elements are honored</title>
+  <!-- https://bugzilla.mozilla.org/show_bug.cgi?id=615146 -->
+
+  <rect width="100%" height="100%" fill="red"/>
+</svg>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/dynamic-conditions-05.svg
@@ -0,0 +1,11 @@
+<?xml version="1.0"?>
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/licenses/publicdomain/
+-->
+<svg xmlns="http://www.w3.org/2000/svg" requiredFeatures="x" onload="this.setAttribute('requiredFeatures', 'http://www.w3.org/TR/SVG11/feature#Gradient')">
+  <title>Test that dynamically changed conditional processing attributes on outer 'svg' elements are honored</title>
+  <!-- https://bugzilla.mozilla.org/show_bug.cgi?id=615146 -->
+
+  <rect width="100%" height="100%" fill="lime"/>
+</svg>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/dynamic-conditions-06.svg
@@ -0,0 +1,24 @@
+<?xml version="1.0"?>
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/licenses/publicdomain/
+-->
+<svg xmlns="http://www.w3.org/2000/svg" class="reftest-wait" onload="f()">
+  <title>Test that changing conditional processing attributes dynamically on outer 'svg' elements while redraw is suspended is honored</title>
+  <!-- https://bugzilla.mozilla.org/show_bug.cgi?id=615146 -->
+
+  <script>
+    function f() {
+      var svg = document.documentElement;
+      svg.suspendRedraw(10000);
+      setTimeout(function() {
+        svg.setAttribute("requiredFeatures", "x");
+        svg.unsuspendRedrawAll();
+        setTimeout(function() {
+          svg.removeAttribute("class");
+        }, 1);
+      }, 1);
+    }
+  </script>
+  <rect width="100%" height="100%" fill="red"/>
+</svg>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/dynamic-conditions-07.svg
@@ -0,0 +1,24 @@
+<?xml version="1.0"?>
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/licenses/publicdomain/
+-->
+<svg xmlns="http://www.w3.org/2000/svg" class="reftest-wait" onload="f()" requiredFeatures="x">
+  <title>Test that changing conditional processing attributes dynamically on outer 'svg' elements while redraw is suspended is honored</title>
+  <!-- https://bugzilla.mozilla.org/show_bug.cgi?id=615146 -->
+
+  <script>
+    function f() {
+      var svg = document.documentElement;
+      svg.suspendRedraw(10000);
+      setTimeout(function() {
+        svg.removeAttribute("requiredFeatures");
+        svg.unsuspendRedrawAll();
+        setTimeout(function() {
+          svg.removeAttribute("class");
+        }, 1);
+      }, 1);
+    }
+  </script>
+  <rect width="100%" height="100%" fill="lime"/>
+</svg>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/dynamic-conditions-08.svg
@@ -0,0 +1,24 @@
+<?xml version="1.0"?>
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/licenses/publicdomain/
+-->
+<svg xmlns="http://www.w3.org/2000/svg" class="reftest-wait" onload="f()" requiredFeatures="x">
+  <title>Test that changing conditional processing attributes dynamically on outer 'svg' elements while redraw is suspended is honored</title>
+  <!-- https://bugzilla.mozilla.org/show_bug.cgi?id=615146 -->
+
+  <script>
+    function f() {
+      var svg = document.documentElement;
+      svg.suspendRedraw(10000);
+      setTimeout(function() {
+        svg.setAttribute("requiredFeatures", "http://www.w3.org/TR/SVG11/feature#Gradient");
+        svg.unsuspendRedrawAll();
+        setTimeout(function() {
+          svg.removeAttribute("class");
+        }, 1);
+      }, 1);
+    }
+  </script>
+  <rect width="100%" height="100%" fill="lime"/>
+</svg>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/dynamic-conditions-09.svg
@@ -0,0 +1,24 @@
+<?xml version="1.0"?>
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/licenses/publicdomain/
+-->
+<svg xmlns="http://www.w3.org/2000/svg" class="reftest-wait" onload="f()" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Gradient">
+  <title>Test that changing conditional processing attributes dynamically on outer 'svg' elements while redraw is suspended is honored</title>
+  <!-- https://bugzilla.mozilla.org/show_bug.cgi?id=615146 -->
+
+  <script>
+    function f() {
+      var svg = document.documentElement;
+      svg.suspendRedraw(10000);
+      setTimeout(function() {
+        svg.setAttribute("requiredFeatures", "x");
+        svg.unsuspendRedrawAll();
+        setTimeout(function() {
+          svg.removeAttribute("class");
+        }, 1);
+      }, 1);
+    }
+  </script>
+  <rect width="100%" height="100%" fill="red"/>
+</svg>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/dynamic-conditions-10.svg
@@ -0,0 +1,24 @@
+<?xml version="1.0"?>
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/licenses/publicdomain/
+-->
+<svg xmlns="http://www.w3.org/2000/svg" class="reftest-wait" onload="f()">
+  <title>Test that changing conditional processing attributes dynamically while redraw is suspended is honored</title>
+  <!-- https://bugzilla.mozilla.org/show_bug.cgi?id=615146 -->
+
+  <script>
+    function f() {
+      var svg = document.documentElement;
+      svg.suspendRedraw(10000);
+      setTimeout(function() {
+        document.getElementById("r").setAttribute("requiredFeatures", "x");
+        svg.unsuspendRedrawAll();
+        setTimeout(function() {
+          svg.removeAttribute("class");
+        }, 1);
+      }, 1);
+    }
+  </script>
+  <rect id="r" width="100%" height="100%" fill="red"/>
+</svg>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/dynamic-conditions-11.svg
@@ -0,0 +1,24 @@
+<?xml version="1.0"?>
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/licenses/publicdomain/
+-->
+<svg xmlns="http://www.w3.org/2000/svg" class="reftest-wait" onload="f()">
+  <title>Test that changing conditional processing attributes dynamically elements while redraw is suspended is honored</title>
+  <!-- https://bugzilla.mozilla.org/show_bug.cgi?id=615146 -->
+
+  <script>
+    function f() {
+      var svg = document.documentElement;
+      svg.suspendRedraw(10000);
+      setTimeout(function() {
+        document.getElementById("r").removeAttribute("requiredFeatures");
+        svg.unsuspendRedrawAll();
+        setTimeout(function() {
+          svg.removeAttribute("class");
+        }, 1);
+      }, 1);
+    }
+  </script>
+  <rect id="r" width="100%" height="100%" fill="lime" requiredFeatures="x"/>
+</svg>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/dynamic-conditions-12.svg
@@ -0,0 +1,24 @@
+<?xml version="1.0"?>
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/licenses/publicdomain/
+-->
+<svg xmlns="http://www.w3.org/2000/svg" class="reftest-wait" onload="f()">
+  <title>Test that changing conditional processing attributes dynamically elements while redraw is suspended is honored</title>
+  <!-- https://bugzilla.mozilla.org/show_bug.cgi?id=615146 -->
+
+  <script>
+    function f() {
+      var svg = document.documentElement;
+      svg.suspendRedraw(10000);
+      setTimeout(function() {
+        document.getElementById("r").setAttribute("requiredFeatures", "http://www.w3.org/TR/SVG11/feature#Gradient");
+        svg.unsuspendRedrawAll();
+        setTimeout(function() {
+          svg.removeAttribute("class");
+        }, 1);
+      }, 1);
+    }
+  </script>
+  <rect id="r" width="100%" height="100%" fill="lime" requiredFeatures="x"/>
+</svg>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/dynamic-conditions-13.svg
@@ -0,0 +1,24 @@
+<?xml version="1.0"?>
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/licenses/publicdomain/
+-->
+<svg xmlns="http://www.w3.org/2000/svg" class="reftest-wait" onload="f()">
+  <title>Test that changing conditional processing attributes dynamically elements while redraw is suspended is honored</title>
+  <!-- https://bugzilla.mozilla.org/show_bug.cgi?id=615146 -->
+
+  <script>
+    function f() {
+      var svg = document.documentElement;
+      svg.suspendRedraw(10000);
+      setTimeout(function() {
+        document.getElementById("r").setAttribute("requiredFeatures", "x");
+        svg.unsuspendRedrawAll();
+        setTimeout(function() {
+          svg.removeAttribute("class");
+        }, 1);
+      }, 1);
+    }
+  </script>
+  <rect id="r" width="100%" height="100%" fill="red" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Gradient"/>
+</svg>
--- a/layout/reftests/svg/reftest.list
+++ b/layout/reftests/svg/reftest.list
@@ -31,23 +31,36 @@ include svg-integration/reftest.list
 == clipPath-basic-03.svg pass.svg
 == clipPath-basic-04.svg pass.svg
 == clipPath-winding-01.svg pass.svg
 == clip-surface-clone-01.svg clip-surface-clone-01-ref.svg
 == conditions-01.svg pass.svg
 == conditions-02.svg pass.svg
 == conditions-03.svg pass.svg
 == conditions-04.svg pass.svg
+== conditions-05.svg about:blank
 == currentColor-01.svg pass.svg
 == currentColor-02.svg pass.svg
 == currentColor-03.svg pass.svg
 == dynamic-attr-removal-1.svg pass.svg
 == dynamic-attr-removal-2.svg pass.svg
 == dynamic-attr-change-1.svg pass.svg
 == dynamic-conditions-01.svg pass.svg
+== dynamic-conditions-02.svg about:blank
+== dynamic-conditions-03.svg pass.svg
+== dynamic-conditions-04.svg about:blank
+== dynamic-conditions-05.svg pass.svg
+== dynamic-conditions-06.svg about:blank
+== dynamic-conditions-07.svg pass.svg
+== dynamic-conditions-08.svg pass.svg
+== dynamic-conditions-09.svg about:blank
+== dynamic-conditions-10.svg about:blank
+== dynamic-conditions-11.svg pass.svg
+== dynamic-conditions-12.svg pass.svg
+== dynamic-conditions-13.svg about:blank
 == dynamic-clipPath-01.svg pass.svg
 == dynamic-feFlood-01.svg pass.svg
 == dynamic-feImage-01.svg pass.svg
 == dynamic-filter-contents-01.svg dynamic-filter-contents-01-ref.svg
 == dynamic-gradient-contents-01.svg pass.svg
 == dynamic-gradient-contents-02.svg pass.svg
 == dynamic-inner-svg-01.svg pass.svg
 == dynamic-link-style-01.svg pass.svg
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/svg-integration/conditions-outer-svg-01.xhtml
@@ -0,0 +1,18 @@
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/licenses/publicdomain/
+-->
+<html xmlns="http://www.w3.org/1999/xhtml"
+      xmlns:svg="http://www.w3.org/2000/svg">
+<head>
+  <title>Test that conditional processing attributes on outer 'svg' elements are honored</title>
+  <!-- https://bugzilla.mozilla.org/show_bug.cgi?id=615146 -->
+  <style>svg { position: absolute; top: 0; left: 0 }</style>
+</head>
+<body>
+
+<svg:svg><svg:rect width="100%" height="100%" fill="lime"/></svg:svg>
+<svg:svg requiredExtensions="x"><svg:rect width="100%" height="100%" fill="red"/></svg:svg>
+
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/svg-integration/conditions-outer-svg-02.xhtml
@@ -0,0 +1,19 @@
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/licenses/publicdomain/
+-->
+<html xmlns="http://www.w3.org/1999/xhtml"
+      xmlns:svg="http://www.w3.org/2000/svg"
+      xmlns:xlink="http://www.w3.org/1999/xlink">
+<head>
+  <title>Test that using elements from conditional-failing outer 'svg' elements works</title>
+  <!-- https://bugzilla.mozilla.org/show_bug.cgi?id=615146 -->
+  <style>svg { position: absolute; top: 0; left: 0 }</style>
+</head>
+<body>
+
+<svg:svg requiredExtensions="x"><svg:rect id="r" width="100%" height="100%" fill="lime"/></svg:svg>
+<svg:svg><svg:use xlink:href="#r"/></svg:svg>
+
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/svg-integration/dynamic-conditions-outer-svg-01.xhtml
@@ -0,0 +1,18 @@
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/licenses/publicdomain/
+-->
+<html xmlns="http://www.w3.org/1999/xhtml"
+      xmlns:svg="http://www.w3.org/2000/svg">
+<head>
+  <title>Test that conditional processing attributes on outer 'svg' elements are honored</title>
+  <!-- https://bugzilla.mozilla.org/show_bug.cgi?id=615146 -->
+  <style>svg { position: absolute; top: 0; left: 0 }</style>
+</head>
+<body onload="document.getElementById('a').setAttribute('requiredExtensions', 'x')">
+
+<svg:svg><svg:rect width="100%" height="100%" fill="lime"/></svg:svg>
+<svg:svg id="a"><svg:rect width="100%" height="100%" fill="red"/></svg:svg>
+
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/svg-integration/dynamic-conditions-outer-svg-02.xhtml
@@ -0,0 +1,18 @@
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/licenses/publicdomain/
+-->
+<html xmlns="http://www.w3.org/1999/xhtml"
+      xmlns:svg="http://www.w3.org/2000/svg">
+<head>
+  <title>Test that conditional processing attributes on outer 'svg' elements are honored</title>
+  <!-- https://bugzilla.mozilla.org/show_bug.cgi?id=615146 -->
+  <style>svg { position: absolute; top: 0; left: 0 }</style>
+</head>
+<body onload="document.getElementById('a').removeAttribute('requiredExtensions')">
+
+<svg:svg><svg:rect width="100%" height="100%" fill="red"/></svg:svg>
+<svg:svg id="a" requiredExtensions="x"><svg:rect width="100%" height="100%" fill="lime"/></svg:svg>
+
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/svg-integration/dynamic-conditions-outer-svg-03.xhtml
@@ -0,0 +1,18 @@
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/licenses/publicdomain/
+-->
+<html xmlns="http://www.w3.org/1999/xhtml"
+      xmlns:svg="http://www.w3.org/2000/svg">
+<head>
+  <title>Test that conditional processing attributes on outer 'svg' elements are honored</title>
+  <!-- https://bugzilla.mozilla.org/show_bug.cgi?id=615146 -->
+  <style>svg { position: absolute; top: 0; left: 0 }</style>
+</head>
+<body onload="document.getElementById('a').setAttribute('requiredFeatures', 'x')">
+
+<svg:svg><svg:rect width="100%" height="100%" fill="lime"/></svg:svg>
+<svg:svg id="a" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Gradient"><svg:rect width="100%" height="100%" fill="red"/></svg:svg>
+
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/svg-integration/dynamic-conditions-outer-svg-04.xhtml
@@ -0,0 +1,18 @@
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/licenses/publicdomain/
+-->
+<html xmlns="http://www.w3.org/1999/xhtml"
+      xmlns:svg="http://www.w3.org/2000/svg">
+<head>
+  <title>Test that conditional processing attributes on outer 'svg' elements are honored</title>
+  <!-- https://bugzilla.mozilla.org/show_bug.cgi?id=615146 -->
+  <style>svg { position: absolute; top: 0; left: 0 }</style>
+</head>
+<body onload="document.getElementById('a').setAttribute('requiredFeatures', 'http://www.w3.org/TR/SVG11/feature#Gradient')">
+
+<svg:svg><svg:rect width="100%" height="100%" fill="red"/></svg:svg>
+<svg:svg id="a" requiredFeatures="x"><svg:rect width="100%" height="100%" fill="lime"/></svg:svg>
+
+</body>
+</html>
--- a/layout/reftests/svg/svg-integration/reftest.list
+++ b/layout/reftests/svg/svg-integration/reftest.list
@@ -6,16 +6,22 @@
 == clipPath-html-03-extref.xhtml clipPath-html-03-ref.svg
 == clipPath-html-04.xhtml clipPath-html-04-ref.xhtml
 == clipPath-html-04-extref.xhtml clipPath-html-04-ref.xhtml
 == clipPath-html-05.xhtml clipPath-html-05-ref.xhtml
 == clipPath-html-05-extref.xhtml clipPath-html-05-ref.xhtml
 == clipPath-html-06.xhtml clipPath-html-06-ref.xhtml
 == clipPath-html-06-extref.xhtml clipPath-html-06-ref.xhtml
 == clipPath-html-zoomed-01.xhtml clipPath-html-01-ref.svg
+== conditions-outer-svg-01.xhtml ../pass.svg
+== conditions-outer-svg-02.xhtml ../pass.svg
+== dynamic-conditions-outer-svg-01.xhtml ../pass.svg
+== dynamic-conditions-outer-svg-02.xhtml ../pass.svg
+== dynamic-conditions-outer-svg-03.xhtml ../pass.svg
+== dynamic-conditions-outer-svg-04.xhtml ../pass.svg
 == filter-html-01.xhtml filter-html-01-ref.svg
 == filter-html-01-extref.xhtml filter-html-01-ref.svg
 == filter-html-zoomed-01.xhtml filter-html-01-ref.svg
 == mask-html-01.xhtml mask-html-01-ref.svg
 == mask-html-01-extref-01.xhtml mask-html-01-ref.svg
 == mask-html-01-extref-02.xhtml mask-html-01-ref.svg
 == mask-html-zoomed-01.xhtml mask-html-01-ref.svg
 == mask-html-xbl-bound-01.html mask-html-01-ref.svg
--- a/layout/svg/base/src/nsSVGClipPathFrame.cpp
+++ b/layout/svg/base/src/nsSVGClipPathFrame.cpp
@@ -285,28 +285,29 @@ nsSVGClipPathFrame::AttributeChanged(PRI
     nsSVGUtils::NotifyChildrenOfSVGChange(this,
                                           nsISVGChildFrame::TRANSFORM_CHANGED);
   }
 
   return nsSVGClipPathFrameBase::AttributeChanged(aNameSpaceID,
                                                   aAttribute, aModType);
 }
 
-#ifdef DEBUG
 NS_IMETHODIMP
 nsSVGClipPathFrame::Init(nsIContent* aContent,
                          nsIFrame* aParent,
                          nsIFrame* aPrevInFlow)
 {
+#ifdef DEBUG
   nsCOMPtr<nsIDOMSVGClipPathElement> clipPath = do_QueryInterface(aContent);
   NS_ASSERTION(clipPath, "Content is not an SVG clipPath!");
+#endif
 
+  AddStateBits(NS_STATE_SVG_CLIPPATH_CHILD);
   return nsSVGClipPathFrameBase::Init(aContent, aParent, aPrevInFlow);
 }
-#endif /* DEBUG */
 
 nsIAtom *
 nsSVGClipPathFrame::GetType() const
 {
   return nsGkAtoms::svgClipPathFrame;
 }
 
 gfxMatrix
--- a/layout/svg/base/src/nsSVGClipPathFrame.h
+++ b/layout/svg/base/src/nsSVGClipPathFrame.h
@@ -71,21 +71,19 @@ public:
 
   PRBool IsValid();
 
   // nsIFrame interface:
   NS_IMETHOD AttributeChanged(PRInt32         aNameSpaceID,
                               nsIAtom*        aAttribute,
                               PRInt32         aModType);
 
-#ifdef DEBUG
   NS_IMETHOD Init(nsIContent*      aContent,
                   nsIFrame*        aParent,
                   nsIFrame*        aPrevInFlow);
-#endif
 
   /**
    * Get the "type" of the frame
    *
    * @see nsGkAtoms::svgClipPathFrame
    */
   virtual nsIAtom* GetType() const;
 
--- a/layout/svg/base/src/nsSVGContainerFrame.cpp
+++ b/layout/svg/base/src/nsSVGContainerFrame.cpp
@@ -100,17 +100,18 @@ nsSVGContainerFrame::Init(nsIContent* aC
 
 NS_IMETHODIMP
 nsSVGDisplayContainerFrame::Init(nsIContent* aContent,
                                  nsIFrame* aParent,
                                  nsIFrame* aPrevInFlow)
 {
   AddStateBits(NS_STATE_SVG_PROPAGATE_TRANSFORM);
   if (!(GetStateBits() & NS_STATE_IS_OUTER_SVG)) {
-    AddStateBits(aParent->GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD);
+    AddStateBits(aParent->GetStateBits() &
+      (NS_STATE_SVG_NONDISPLAY_CHILD | NS_STATE_SVG_CLIPPATH_CHILD));
   }
   nsresult rv = nsSVGContainerFrameBase::Init(aContent, aParent, aPrevInFlow);
   return rv;
 }
 
 NS_IMETHODIMP
 nsSVGDisplayContainerFrame::InsertFrames(nsIAtom* aListName,
                                          nsIFrame* aPrevFrame,
--- a/layout/svg/base/src/nsSVGForeignObjectFrame.cpp
+++ b/layout/svg/base/src/nsSVGForeignObjectFrame.cpp
@@ -87,17 +87,18 @@ nsSVGForeignObjectFrame::Init(nsIContent
 {
 #ifdef DEBUG
   nsCOMPtr<nsIDOMSVGForeignObjectElement> foreignObject = do_QueryInterface(aContent);
   NS_ASSERTION(foreignObject, "Content is not an SVG foreignObject!");
 #endif
 
   nsresult rv = nsSVGForeignObjectFrameBase::Init(aContent, aParent, aPrevInFlow);
   AddStateBits(NS_STATE_SVG_PROPAGATE_TRANSFORM | 
-               (aParent->GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD));
+               (aParent->GetStateBits() &
+                (NS_STATE_SVG_NONDISPLAY_CHILD | NS_STATE_SVG_CLIPPATH_CHILD)));
   if (NS_SUCCEEDED(rv)) {
     nsSVGUtils::GetOuterSVGFrame(this)->RegisterForeignObject(this);
   }
   return rv;
 }
 
 void nsSVGForeignObjectFrame::DestroyFrom(nsIFrame* aDestructRoot)
 {
--- a/layout/svg/base/src/nsSVGGeometryFrame.cpp
+++ b/layout/svg/base/src/nsSVGGeometryFrame.cpp
@@ -47,17 +47,18 @@ NS_IMPL_FRAMEARENA_HELPERS(nsSVGGeometry
 //----------------------------------------------------------------------
 // nsIFrame methods
 
 NS_IMETHODIMP
 nsSVGGeometryFrame::Init(nsIContent* aContent,
                          nsIFrame* aParent,
                          nsIFrame* aPrevInFlow)
 {
-  AddStateBits((aParent->GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD) |
+  AddStateBits((aParent->GetStateBits() &
+                (NS_STATE_SVG_NONDISPLAY_CHILD | NS_STATE_SVG_CLIPPATH_CHILD)) |
                NS_STATE_SVG_PROPAGATE_TRANSFORM);
   nsresult rv = nsSVGGeometryFrameBase::Init(aContent, aParent, aPrevInFlow);
   return rv;
 }
 
 //----------------------------------------------------------------------
 
 nsSVGPaintServerFrame *
@@ -157,36 +158,16 @@ nsSVGGeometryFrame::GetStrokeDashoffset(
 }
 
 PRUint16
 nsSVGGeometryFrame::GetClipRule()
 {
   return GetStyleSVG()->mClipRule;
 }
 
-PRBool
-nsSVGGeometryFrame::IsClipChild()
-{
-  nsIContent *node = mContent;
-
-  do {
-    // Return false if we find a non-svg ancestor. Non-SVG elements are not
-    // allowed inside an SVG clipPath element.
-    if (node->GetNameSpaceID() != kNameSpaceID_SVG) {
-      break;
-    }
-    if (node->NodeInfo()->Equals(nsGkAtoms::clipPath, kNameSpaceID_SVG)) {
-      return PR_TRUE;
-    }
-    node = node->GetParent();
-  } while (node);
-    
-  return PR_FALSE;
-}
-
 static void
 SetupCairoColor(gfxContext *aContext, nscolor aRGB, float aOpacity)
 {
   aContext->SetColor(gfxRGBA(NS_GET_R(aRGB)/255.0,
                              NS_GET_G(aRGB)/255.0,
                              NS_GET_B(aRGB)/255.0,
                              NS_GET_A(aRGB)/255.0 * aOpacity));
 }
--- a/layout/svg/base/src/nsSVGGeometryFrame.h
+++ b/layout/svg/base/src/nsSVGGeometryFrame.h
@@ -68,17 +68,16 @@ public:
   virtual PRBool IsFrameOfType(PRUint32 aFlags) const
   {
     return nsSVGGeometryFrameBase::IsFrameOfType(aFlags & ~(nsIFrame::eSVG | nsIFrame::eSVGGeometry));
   }
 
   // nsSVGGeometryFrame methods:
   virtual gfxMatrix GetCanvasTM() = 0;
   PRUint16 GetClipRule();
-  PRBool IsClipChild(); 
 
   float GetStrokeWidth();
 
   /*
    * Set up a cairo context for filling a path
    * @return PR_FALSE to skip rendering
    */
   PRBool SetupCairoFill(gfxContext *aContext);
--- a/layout/svg/base/src/nsSVGOuterSVGFrame.cpp
+++ b/layout/svg/base/src/nsSVGOuterSVGFrame.cpp
@@ -39,16 +39,18 @@
 #include "nsSVGOuterSVGFrame.h"
 #include "nsIDOMSVGSVGElement.h"
 #include "nsSVGSVGElement.h"
 #include "nsSVGTextFrame.h"
 #include "nsSVGForeignObjectFrame.h"
 #include "nsDisplayList.h"
 #include "nsStubMutationObserver.h"
 #include "gfxContext.h"
+#include "gfxMatrix.h"
+#include "gfxRect.h"
 #include "nsIContentViewer.h"
 #include "nsIDocShell.h"
 #include "nsIDOMDocument.h"
 #include "nsIDOMWindowInternal.h"
 #include "nsPIDOMWindow.h"
 #include "nsIObjectLoadingContent.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsSVGMatrix.h"
@@ -156,16 +158,23 @@ nsSVGOuterSVGFrame::Init(nsIContent* aCo
 {
 #ifdef DEBUG
   nsCOMPtr<nsIDOMSVGSVGElement> svgElement = do_QueryInterface(aContent);
   NS_ASSERTION(svgElement, "Content is not an SVG 'svg' element!");
 #endif
 
   AddStateBits(NS_STATE_IS_OUTER_SVG);
 
+  // Check for conditional processing attributes here rather than in
+  // nsCSSFrameConstructor::FindSVGData because we want to avoid
+  // simply giving failing outer <svg> elements an nsSVGContainerFrame.
+  if (!nsSVGFeatures::PassesConditionalProcessingTests(aContent)) {
+    AddStateBits(NS_STATE_SVG_NONDISPLAY_CHILD);
+  }
+
   nsresult rv = nsSVGOuterSVGFrameBase::Init(aContent, aParent, aPrevInFlow);
 
   nsIDocument* doc = mContent->GetCurrentDoc();
   if (doc) {
     // we only care about our content's zoom and pan values if it's the root element
     if (doc->GetRootElement() == mContent) {
       mIsRootContent = PR_TRUE;
     }
@@ -508,28 +517,16 @@ nsSVGOuterSVGFrame::AttributeChanged(PRI
       PresContext()->PresShell()->
         FrameNeedsReflow(this, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY);
     }
   }
 
   return NS_OK;
 }
 
-nsIFrame*
-nsSVGOuterSVGFrame::GetFrameForPoint(const nsPoint& aPoint)
-{
-  nsRect thisRect(nsPoint(0,0), GetSize());
-  if (!thisRect.Contains(aPoint)) {
-    return nsnull;
-  }
-
-  return nsSVGUtils::HitTestChildren(
-    this, aPoint + GetPosition() - GetContentRect().TopLeft());
-}
-
 //----------------------------------------------------------------------
 // painting
 
 NS_IMETHODIMP
 nsSVGOuterSVGFrame::BuildDisplayList(nsDisplayListBuilder*   aBuilder,
                                      const nsRect&           aDirtyRect,
                                      const nsDisplayListSet& aLists)
 {
--- a/layout/svg/base/src/nsSVGOuterSVGFrame.h
+++ b/layout/svg/base/src/nsSVGOuterSVGFrame.h
@@ -38,16 +38,17 @@
 
 #ifndef __NS_SVGOUTERSVGFRAME_H__
 #define __NS_SVGOUTERSVGFRAME_H__
 
 #include "nsSVGContainerFrame.h"
 #include "nsISVGSVGFrame.h"
 #include "nsIDOMSVGPoint.h"
 #include "nsIDOMSVGNumber.h"
+#include "nsSVGFeatures.h"
 #include "gfxMatrix.h"
 
 class nsSVGForeignObjectFrame;
 
 ////////////////////////////////////////////////////////////////////////
 // nsSVGOuterSVGFrame class
 
 typedef nsSVGDisplayContainerFrame nsSVGOuterSVGFrameBase;
@@ -87,18 +88,16 @@ public:
                     nsHTMLReflowMetrics&     aDesiredSize,
                     const nsHTMLReflowState& aReflowState,
                     nsReflowStatus&          aStatus);
 
   NS_IMETHOD  DidReflow(nsPresContext*   aPresContext,
                         const nsHTMLReflowState*  aReflowState,
                         nsDidReflowStatus aStatus);
 
-  NS_IMETHOD_(nsIFrame*) GetFrameForPoint(const nsPoint& aPoint);
-
   NS_IMETHOD BuildDisplayList(nsDisplayListBuilder*   aBuilder,
                               const nsRect&           aDirtyRect,
                               const nsDisplayListSet& aLists);
 
   NS_IMETHOD Init(nsIContent*      aContent,
                   nsIFrame*        aParent,
                   nsIFrame*        aPrevInFlow);
 
--- a/layout/svg/base/src/nsSVGPathGeometryFrame.cpp
+++ b/layout/svg/base/src/nsSVGPathGeometryFrame.cpp
@@ -149,20 +149,17 @@ nsSVGPathGeometryFrame::PaintSVG(nsSVGRe
 
   return NS_OK;
 }
 
 NS_IMETHODIMP_(nsIFrame*)
 nsSVGPathGeometryFrame::GetFrameForPoint(const nsPoint &aPoint)
 {
   PRUint16 fillRule, mask;
-  // check if we're a clipPath - cheaper than IsClipChild(), and we shouldn't
-  // get in here for other nondisplay children
-  if (GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD) {
-    NS_ASSERTION(IsClipChild(), "should be in clipPath but we're not");
+  if (GetStateBits() & NS_STATE_SVG_CLIPPATH_CHILD) {
     mask = HITTEST_MASK_FILL;
     fillRule = GetClipRule();
   } else {
     mask = GetHittestMask();
     if (!mask || (!(mask & HITTEST_MASK_FORCE_TEST) &&
                   !mRect.Contains(aPoint)))
       return nsnull;
     fillRule = GetStyleSVG()->mFillRule;
--- a/layout/svg/base/src/nsSVGUtils.h
+++ b/layout/svg/base/src/nsSVGUtils.h
@@ -86,24 +86,27 @@ class Element;
 } // namespace dom
 } // namespace mozilla
 
 #ifndef M_PI
 #define M_PI 3.14159265358979323846
 #endif
 
 // SVG Frame state bits
-#define NS_STATE_IS_OUTER_SVG         NS_FRAME_STATE_BIT(20)
+#define NS_STATE_IS_OUTER_SVG                    NS_FRAME_STATE_BIT(20)
 
-#define NS_STATE_SVG_DIRTY            NS_FRAME_STATE_BIT(21)
+#define NS_STATE_SVG_DIRTY                       NS_FRAME_STATE_BIT(21)
 
 /* are we the child of a non-display container? */
-#define NS_STATE_SVG_NONDISPLAY_CHILD NS_FRAME_STATE_BIT(22)
+#define NS_STATE_SVG_NONDISPLAY_CHILD            NS_FRAME_STATE_BIT(22)
 
-#define NS_STATE_SVG_PROPAGATE_TRANSFORM NS_FRAME_STATE_BIT(23)
+#define NS_STATE_SVG_PROPAGATE_TRANSFORM         NS_FRAME_STATE_BIT(23)
+
+// If this bit is set, we are a <clipPath> element or descendant.
+#define NS_STATE_SVG_CLIPPATH_CHILD              NS_FRAME_STATE_BIT(24)
 
 /**
  * Byte offsets of channels in a native packed gfxColor or cairo image surface.
  */
 #ifdef IS_BIG_ENDIAN
 #define GFX_ARGB32_OFFSET_A 0
 #define GFX_ARGB32_OFFSET_R 1
 #define GFX_ARGB32_OFFSET_G 2