Bug 409383 - Make dynamic changes to switch children work and fix transient switch errors. r=jwatt,sr=roc
authorRobert Longson <longsonr@gmail.com>
Sun, 13 Jul 2008 12:30:48 +0100
changeset 15895 c1598626dbd3cedccda558182a87a3340dbc464d
parent 15894 6687dc112c14261528f6188d6da1f4103cb090b9
child 15896 8d0afedeb4b2c3295ce280de11be0779a37fe76e
push id585
push userlongsonr@gmail.com
push dateSun, 13 Jul 2008 11:32:13 +0000
treeherdermozilla-central@c1598626dbd3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjwatt, roc
bugs409383
milestone1.9.1a1pre
Bug 409383 - Make dynamic changes to switch children work and fix transient switch errors. r=jwatt,sr=roc
content/base/src/nsGenericElement.cpp
content/base/src/nsGkAtomList.h
content/svg/content/src/nsSVGElement.cpp
content/svg/content/src/nsSVGElementList.h
content/svg/content/src/nsSVGFeatures.cpp
content/svg/content/src/nsSVGSwitchElement.cpp
content/svg/content/src/nsSVGSwitchElement.h
layout/base/nsCSSFrameConstructor.cpp
layout/base/nsCSSFrameConstructor.h
layout/reftests/svg/dynamic-switch-01.svg
layout/reftests/svg/switch-01.svg
layout/svg/base/src/Makefile.in
layout/svg/base/src/nsSVGSwitchFrame.cpp
--- a/content/base/src/nsGenericElement.cpp
+++ b/content/base/src/nsGenericElement.cpp
@@ -136,17 +136,17 @@
 #endif /* ACCESSIBILITY */
 
 #include "nsCycleCollectionParticipant.h"
 #include "nsCCUncollectableMarker.h"
 
 #include "mozAutoDocUpdate.h"
 
 #ifdef MOZ_SVG
-PRBool NS_SVG_TestFeature(const nsAString &fstr);
+PRBool NS_SVG_HaveFeature(const nsAString &aFeature);
 #endif /* MOZ_SVG */
 
 #ifdef DEBUG_waterson
 
 /**
  * List a content tree to stdout. Meant to be called from gdb.
  */
 void
@@ -1366,17 +1366,17 @@ nsGenericElement::InternalIsSupported(ns
     if (aVersion.IsEmpty() ||
         PL_strcmp(v, "3.0") == 0) {
       *aReturn = PR_TRUE;
     }
   }
 #ifdef MOZ_SVG
   else if (PL_strcasecmp(f, "SVGEvents") == 0 ||
            PL_strcasecmp(f, "SVGZoomEvents") == 0 ||
-           NS_SVG_TestFeature(aFeature)) {
+           NS_SVG_HaveFeature(aFeature)) {
     if (aVersion.IsEmpty() ||
         PL_strcmp(v, "1.0") == 0 ||
         PL_strcmp(v, "1.1") == 0) {
       *aReturn = PR_TRUE;
     }
   }
 #endif /* MOZ_SVG */
   else {
--- a/content/base/src/nsGkAtomList.h
+++ b/content/base/src/nsGkAtomList.h
@@ -1503,16 +1503,17 @@ GK_ATOM(svgInnerSVGFrame, "SVGInnerSVGFr
 GK_ATOM(svgLinearGradientFrame, "SVGLinearGradientFrame")
 GK_ATOM(svgMarkerFrame, "SVGMarkerFrame")
 GK_ATOM(svgMaskFrame, "SVGMaskFrame")
 GK_ATOM(svgOuterSVGFrame, "SVGOuterSVGFrame")
 GK_ATOM(svgPathGeometryFrame, "SVGPathGeometryFrame")
 GK_ATOM(svgPatternFrame, "SVGPatternFrame")
 GK_ATOM(svgRadialGradientFrame, "SVGRadialGradientFrame")
 GK_ATOM(svgStopFrame, "SVGStopFrame")
+GK_ATOM(svgSwitchFrame, "SVGSwitchFrame")
 GK_ATOM(svgTextFrame, "SVGTextFrame")
 GK_ATOM(svgTextPathFrame, "SVGTextPathFrame")
 GK_ATOM(svgTSpanFrame, "SVGTSpanFrame")
 GK_ATOM(svgUseFrame, "SVGUseFrame")
 #endif
 #ifdef MOZ_MEDIA
 GK_ATOM(HTMLVideoFrame, "VideoFrame")
 GK_ATOM(onloadstart, "onloadstart")
--- a/content/svg/content/src/nsSVGElement.cpp
+++ b/content/svg/content/src/nsSVGElement.cpp
@@ -33,16 +33,17 @@
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsSVGElement.h"
 #include "nsSVGSVGElement.h"
+#include "nsSVGSwitchElement.h"
 #include "nsIDocument.h"
 #include "nsRange.h"
 #include "nsIDOMAttr.h"
 #include "nsIDOMEventTarget.h"
 #include "nsIDOMMutationEvent.h"
 #include "nsMutationEvent.h"
 #include "nsXBLPrototypeBinding.h"
 #include "nsBindingManager.h"
@@ -176,18 +177,18 @@ NS_INTERFACE_MAP_END_INHERITING(nsSVGEle
 //----------------------------------------------------------------------
 // Implementation
   
 //----------------------------------------------------------------------
 // nsIContent methods
 
 nsresult
 nsSVGElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
-                            nsIContent* aBindingParent,
-                            PRBool aCompileEventHandlers)
+                         nsIContent* aBindingParent,
+                         PRBool aCompileEventHandlers)
 {
   nsresult rv = nsSVGElementBase::BindToTree(aDocument, aParent,
                                              aBindingParent,
                                              aCompileEventHandlers);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (!HasFlag(NODE_MAY_HAVE_STYLE)) {
     return NS_OK;
@@ -231,16 +232,43 @@ nsresult
 nsSVGElement::AfterSetAttr(PRInt32 aNamespaceID, nsIAtom* aName,
                            const nsAString* aValue, PRBool aNotify)
 {  
   if (IsEventName(aName) && aValue) {
     nsresult rv = AddScriptEventListener(GetEventNameForAttr(aName), *aValue);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
+  if (aNamespaceID == kNameSpaceID_None &&
+      (aName == nsGkAtoms::requiredFeatures ||
+       aName == nsGkAtoms::requiredExtensions ||
+       aName == nsGkAtoms::systemLanguage)) {
+
+    nsIContent* parent = nsnull;
+  
+    nsIContent* bindingParent = GetBindingParent();
+    if (bindingParent) {
+      nsIDocument* doc = bindingParent->GetOwnerDoc();
+      if (doc) {
+        parent = doc->BindingManager()->GetInsertionParent(bindingParent);
+      }
+    }
+
+    if (!parent) {
+      // if we didn't find an anonymous parent, use the explicit one,
+      // whether it's null or not...
+      parent = GetParent();
+    }
+
+    if (parent &&
+        parent->NodeInfo()->Equals(nsGkAtoms::svgSwitch, kNameSpaceID_SVG)) {
+      static_cast<nsSVGSwitchElement*>(parent)->MaybeInvalidate();
+    }
+  }
+
   return nsSVGElementBase::AfterSetAttr(aNamespaceID, aName, aValue, aNotify);
 }
 
 PRBool
 nsSVGElement::ParseAttribute(PRInt32 aNamespaceID,
                              nsIAtom* aAttribute,
                              const nsAString& aValue,
                              nsAttrValue& aResult)
--- a/content/svg/content/src/nsSVGElementList.h
+++ b/content/svg/content/src/nsSVGElementList.h
@@ -32,108 +32,121 @@
  * use your version of this file under the terms of the MPL, indicate your
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
-/******
+/**
+ * This file is used to help create a mapping from a specified SVG element to
+ * attributes supported by that element. This mapping can be used to help
+ * ensure that we don't accidentally implement support for attributes like
+ * requiredFeatures on elements for which the SVG specification does not
+ * define support.
+ *
+ * To use, include this file into another file after defining the SVG_ELEMENT
+ * C preprocessor macro as appropriate.
+ *
+ * The following constants represent the following attributes:
+ *
+ * ATTRS_CONDITIONAL
+ *   The requiredFeatures, requiredExtensions, and systemLanguage attributes
+ *
+ * ATTRS_EXTERNAL
+ *   The externalResourcesRequired attribute
+ *
+ * ATTRS_ALL
+ *   A convenience value indicating support for all of the above
+ *
+ * ATTRS_NONE
+ *   A convenience value indicating support for none of the above
+ */
 
-  This file contains the list of all SVG elements.  It is designed
-  to be used by the C preprocessor for the purposes of creating a
-  table of support for various elements such as support for the
-  requiredFeatures, requiredExtensions, and requiredLanguage
-  conditionals
-  
- ******/
-
-// Define the LangSpace & Tests support
-#define SUPPORTS_NONE 0
-#define SUPPORTS_LANG 1
-#define SUPPORTS_TEST 2
-#define SUPPORTS_EXTERNAL 4
-#define SUPPORTS_ALL 7
+#define ATTRS_NONE        0x00
+#define ATTRS_CONDITIONAL 0x01
+#define ATTRS_EXTERNAL    0x02
+#define ATTRS_ALL         (ATTRS_CONDITIONAL | ATTRS_EXTERNAL)
 // tags
-SVG_ELEMENT(a, SUPPORTS_LANG|SUPPORTS_TEST)
-SVG_ELEMENT(altGlyph, SUPPORTS_ALL)
-SVG_ELEMENT(altGlyphDef, SUPPORTS_NONE)
-SVG_ELEMENT(altGlyphItem, SUPPORTS_NONE)
-SVG_ELEMENT(animate, SUPPORTS_EXTERNAL|SUPPORTS_TEST)
-SVG_ELEMENT(animateColor, SUPPORTS_EXTERNAL|SUPPORTS_TEST)
-SVG_ELEMENT(animateMotion, SUPPORTS_EXTERNAL|SUPPORTS_TEST)
-SVG_ELEMENT(animateTransform, SUPPORTS_EXTERNAL|SUPPORTS_TEST)
-SVG_ELEMENT(circle, SUPPORTS_ALL)
-SVG_ELEMENT(clipPath, SUPPORTS_ALL)
-SVG_ELEMENT(colorProfile, SUPPORTS_NONE)
-SVG_ELEMENT(cursor, SUPPORTS_ALL)
-SVG_ELEMENT(definition_src, SUPPORTS_NONE)
-SVG_ELEMENT(defs, SUPPORTS_ALL)
-SVG_ELEMENT(desc, SUPPORTS_LANG)
-SVG_ELEMENT(ellipse, SUPPORTS_ALL)
-SVG_ELEMENT(feBlend, SUPPORTS_NONE)
-SVG_ELEMENT(feColorMatrix, SUPPORTS_NONE)
-SVG_ELEMENT(feComponentTransfer, SUPPORTS_NONE)
-SVG_ELEMENT(feComposite, SUPPORTS_NONE)
-SVG_ELEMENT(feConvolveMatrix, SUPPORTS_NONE)
-SVG_ELEMENT(feDiffuseLighting, SUPPORTS_NONE)
-SVG_ELEMENT(feDisplacementMap, SUPPORTS_NONE)
-SVG_ELEMENT(feDistantLight, SUPPORTS_NONE)
-SVG_ELEMENT(feFlood, SUPPORTS_NONE)
-SVG_ELEMENT(feFuncR, SUPPORTS_NONE)
-SVG_ELEMENT(feFuncG, SUPPORTS_NONE)
-SVG_ELEMENT(feFuncB, SUPPORTS_NONE)
-SVG_ELEMENT(feFuncA, SUPPORTS_NONE)
-SVG_ELEMENT(feGaussianBlur, SUPPORTS_NONE)
-SVG_ELEMENT(feImage, SUPPORTS_EXTERNAL|SUPPORTS_LANG)
-SVG_ELEMENT(feMerge, SUPPORTS_NONE)
-SVG_ELEMENT(feMergeNode, SUPPORTS_NONE)
-SVG_ELEMENT(feMorphology, SUPPORTS_NONE)
-SVG_ELEMENT(feOffset, SUPPORTS_NONE)
-SVG_ELEMENT(fePointLight, SUPPORTS_NONE)
-SVG_ELEMENT(feSpecularLighting, SUPPORTS_NONE)
-SVG_ELEMENT(feSpotLight, SUPPORTS_NONE)
-SVG_ELEMENT(feTile, SUPPORTS_NONE)
-SVG_ELEMENT(feTurbulence, SUPPORTS_NONE)
-SVG_ELEMENT(filter, SUPPORTS_LANG|SUPPORTS_EXTERNAL)
-SVG_ELEMENT(font, SUPPORTS_EXTERNAL)
-SVG_ELEMENT(font_face, SUPPORTS_NONE)
-SVG_ELEMENT(font_face_format, SUPPORTS_NONE)
-SVG_ELEMENT(font_face_name, SUPPORTS_NONE)
-SVG_ELEMENT(font_face_src, SUPPORTS_NONE)
-SVG_ELEMENT(font_face_uri, SUPPORTS_NONE)
+SVG_ELEMENT(a, ATTRS_ALL)
+SVG_ELEMENT(altGlyph, ATTRS_ALL)
+SVG_ELEMENT(altGlyphDef, ATTRS_NONE)
+SVG_ELEMENT(altGlyphItem, ATTRS_NONE)
+SVG_ELEMENT(animate, ATTRS_ALL)
+SVG_ELEMENT(animateColor, ATTRS_ALL)
+SVG_ELEMENT(animateMotion, ATTRS_ALL)
+SVG_ELEMENT(animateTransform, ATTRS_ALL)
+SVG_ELEMENT(circle, ATTRS_ALL)
+SVG_ELEMENT(clipPath, ATTRS_ALL)
+SVG_ELEMENT(colorProfile, ATTRS_NONE)
+SVG_ELEMENT(cursor, ATTRS_ALL)
+SVG_ELEMENT(definition_src, ATTRS_NONE)
+SVG_ELEMENT(defs, ATTRS_ALL)
+SVG_ELEMENT(desc, ATTRS_NONE)
+SVG_ELEMENT(ellipse, ATTRS_ALL)
+SVG_ELEMENT(feBlend, ATTRS_NONE)
+SVG_ELEMENT(feColorMatrix, ATTRS_NONE)
+SVG_ELEMENT(feComponentTransfer, ATTRS_NONE)
+SVG_ELEMENT(feComposite, ATTRS_NONE)
+SVG_ELEMENT(feConvolveMatrix, ATTRS_NONE)
+SVG_ELEMENT(feDiffuseLighting, ATTRS_NONE)
+SVG_ELEMENT(feDisplacementMap, ATTRS_NONE)
+SVG_ELEMENT(feDistantLight, ATTRS_NONE)
+SVG_ELEMENT(feFlood, ATTRS_NONE)
+SVG_ELEMENT(feFuncR, ATTRS_NONE)
+SVG_ELEMENT(feFuncG, ATTRS_NONE)
+SVG_ELEMENT(feFuncB, ATTRS_NONE)
+SVG_ELEMENT(feFuncA, ATTRS_NONE)
+SVG_ELEMENT(feGaussianBlur, ATTRS_NONE)
+SVG_ELEMENT(feImage, ATTRS_EXTERNAL)
+SVG_ELEMENT(feMerge, ATTRS_NONE)
+SVG_ELEMENT(feMergeNode, ATTRS_NONE)
+SVG_ELEMENT(feMorphology, ATTRS_NONE)
+SVG_ELEMENT(feOffset, ATTRS_NONE)
+SVG_ELEMENT(fePointLight, ATTRS_NONE)
+SVG_ELEMENT(feSpecularLighting, ATTRS_NONE)
+SVG_ELEMENT(feSpotLight, ATTRS_NONE)
+SVG_ELEMENT(feTile, ATTRS_NONE)
+SVG_ELEMENT(feTurbulence, ATTRS_NONE)
+SVG_ELEMENT(filter, ATTRS_EXTERNAL)
+SVG_ELEMENT(font, ATTRS_EXTERNAL)
+SVG_ELEMENT(font_face, ATTRS_NONE)
+SVG_ELEMENT(font_face_format, ATTRS_NONE)
+SVG_ELEMENT(font_face_name, ATTRS_NONE)
+SVG_ELEMENT(font_face_src, ATTRS_NONE)
+SVG_ELEMENT(font_face_uri, ATTRS_NONE)
 #ifdef MOZ_SVG_FOREIGNOBJECT
-SVG_ELEMENT(foreignObject, SUPPORTS_ALL)
+SVG_ELEMENT(foreignObject, ATTRS_ALL)
 #endif
-SVG_ELEMENT(g, SUPPORTS_ALL)
-SVG_ELEMENT(glyph, SUPPORTS_NONE)
-SVG_ELEMENT(glyphRef, SUPPORTS_NONE)
-SVG_ELEMENT(hkern, SUPPORTS_NONE)
-SVG_ELEMENT(image, SUPPORTS_ALL)
-SVG_ELEMENT(line, SUPPORTS_ALL)
-SVG_ELEMENT(linearGradient, SUPPORTS_EXTERNAL)
-SVG_ELEMENT(marker, SUPPORTS_EXTERNAL|SUPPORTS_LANG)
-SVG_ELEMENT(mask, SUPPORTS_ALL)
-SVG_ELEMENT(metadata, SUPPORTS_NONE)
-SVG_ELEMENT(missingGlyph, SUPPORTS_NONE)
-SVG_ELEMENT(mpath, SUPPORTS_EXTERNAL)
-SVG_ELEMENT(path, SUPPORTS_ALL)
-SVG_ELEMENT(pattern, SUPPORTS_ALL)
-SVG_ELEMENT(polygon, SUPPORTS_ALL)
-SVG_ELEMENT(polyline, SUPPORTS_ALL)
-SVG_ELEMENT(radialGradient, SUPPORTS_EXTERNAL)
-SVG_ELEMENT(rect, SUPPORTS_ALL)
-SVG_ELEMENT(script, SUPPORTS_EXTERNAL)
-SVG_ELEMENT(set, SUPPORTS_TEST|SUPPORTS_EXTERNAL)
-SVG_ELEMENT(stop, SUPPORTS_NONE)
-SVG_ELEMENT(style, SUPPORTS_NONE)
-SVG_ELEMENT(svg, SUPPORTS_ALL)
-SVG_ELEMENT(svgSwitch, SUPPORTS_ALL) // switch is a C++ keyword, hence svgSwitch
-SVG_ELEMENT(symbol, SUPPORTS_LANG|SUPPORTS_EXTERNAL)
-SVG_ELEMENT(text, SUPPORTS_ALL)
-SVG_ELEMENT(textPath, SUPPORTS_ALL)
-SVG_ELEMENT(title, SUPPORTS_LANG)
-SVG_ELEMENT(tref, SUPPORTS_ALL)
-SVG_ELEMENT(tspan, SUPPORTS_ALL)
-SVG_ELEMENT(use, SUPPORTS_ALL)
-SVG_ELEMENT(view, SUPPORTS_EXTERNAL)
-SVG_ELEMENT(vkern, SUPPORTS_NONE)
+SVG_ELEMENT(g, ATTRS_ALL)
+SVG_ELEMENT(glyph, ATTRS_NONE)
+SVG_ELEMENT(glyphRef, ATTRS_NONE)
+SVG_ELEMENT(hkern, ATTRS_NONE)
+SVG_ELEMENT(image, ATTRS_ALL)
+SVG_ELEMENT(line, ATTRS_ALL)
+SVG_ELEMENT(linearGradient, ATTRS_EXTERNAL)
+SVG_ELEMENT(marker, ATTRS_NONE)
+SVG_ELEMENT(mask, ATTRS_ALL)
+SVG_ELEMENT(metadata, ATTRS_NONE)
+SVG_ELEMENT(missingGlyph, ATTRS_NONE)
+SVG_ELEMENT(mpath, ATTRS_EXTERNAL)
+SVG_ELEMENT(path, ATTRS_ALL)
+SVG_ELEMENT(pattern, ATTRS_ALL)
+SVG_ELEMENT(polygon, ATTRS_ALL)
+SVG_ELEMENT(polyline, ATTRS_ALL)
+SVG_ELEMENT(radialGradient, ATTRS_EXTERNAL)
+SVG_ELEMENT(rect, ATTRS_ALL)
+SVG_ELEMENT(script, ATTRS_EXTERNAL)
+SVG_ELEMENT(set, ATTRS_ALL)
+SVG_ELEMENT(stop, ATTRS_NONE)
+SVG_ELEMENT(style, ATTRS_NONE)
+SVG_ELEMENT(svg, ATTRS_ALL)
+SVG_ELEMENT(svgSwitch, ATTRS_ALL) // switch is a C++ keyword, hence svgSwitch
+SVG_ELEMENT(symbol, ATTRS_NONE)
+SVG_ELEMENT(text, ATTRS_ALL)
+SVG_ELEMENT(textPath, ATTRS_ALL)
+SVG_ELEMENT(title, ATTRS_NONE)
+SVG_ELEMENT(tref, ATTRS_ALL)
+SVG_ELEMENT(tspan, ATTRS_ALL)
+SVG_ELEMENT(use, ATTRS_ALL)
+SVG_ELEMENT(view, ATTRS_EXTERNAL)
+SVG_ELEMENT(vkern, ATTRS_NONE)
--- a/content/svg/content/src/nsSVGFeatures.cpp
+++ b/content/svg/content/src/nsSVGFeatures.cpp
@@ -32,81 +32,209 @@
  * use your version of this file under the terms of the MPL, indicate your
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
+/**
+ * This file contains code to help implement the Conditional Processing
+ * section of the SVG specification (i.e. the <switch> element and the
+ * requiredFeatures, requiredExtensions and systemLanguage attributes).
+ *
+ *   http://www.w3.org/TR/SVG11/struct.html#ConditionalProcessing
+ */
+
 #include "nsString.h"
+#include "nsGkAtoms.h"
+#include "nsIContent.h"
+#include "nsContentUtils.h"
+#include "nsWhitespaceTokenizer.h"
+#include "nsStyleUtil.h"
 #include "nsSVGUtils.h"
-#include "nsGkAtoms.h"
+
+class nsSVGCommaTokenizer
+{
+public:
+  nsSVGCommaTokenizer(const nsSubstring& aSource) {
+    aSource.BeginReading(mIter);
+    aSource.EndReading(mEnd);
+
+    while (mIter != mEnd && *mIter == ',') {
+      ++mIter;
+    }
+  }
 
-// Test to see if a feature is implemented
+  /**
+   * Checks if any more tokens are available.
+   */
+  PRBool hasMoreTokens() {
+    return mIter != mEnd;
+  }
+
+  /**
+   * Returns the next token.
+   */
+  const nsDependentSubstring nextToken() {
+    nsSubstring::const_char_iterator begin = mIter;
+    while (mIter != mEnd && *mIter != ',') {
+      ++mIter;
+    }
+    nsSubstring::const_char_iterator end = mIter;
+    while (mIter != mEnd && *mIter == ',') {
+      ++mIter;
+    }
+    return Substring(begin, end);
+  }
+
+private:
+  nsSubstring::const_char_iterator mIter, mEnd;
+};
+
+/**
+ * Check whether we support the given feature string.
+ *
+ * @param aFeature one of the feature strings specified at
+ *    http://www.w3.org/TR/SVG11/feature.html
+ */
 PRBool
-NS_SVG_TestFeature(const nsAString& fstr) {
+NS_SVG_HaveFeature(const nsAString& aFeature)
+{
   if (!NS_SVGEnabled()) {
     return PR_FALSE;
   }
-  nsAutoString lstr(fstr);
-  lstr.StripWhitespace();
 
-#ifdef DEBUG_scooter
-  NS_ConvertUTF16toUTF8 feature(lstr);
-  printf("NS_SVG_TestFeature: testing for %s\n", feature.get());
-#endif
-
-#define SVG_SUPPORTED_FEATURE(str) if (lstr.Equals(NS_LITERAL_STRING(str).get())) return PR_TRUE;
+#define SVG_SUPPORTED_FEATURE(str) if (aFeature.Equals(NS_LITERAL_STRING(str).get())) return PR_TRUE;
 #define SVG_UNSUPPORTED_FEATURE(str)
 #include "nsSVGFeaturesList.h"
 #undef SVG_SUPPORTED_FEATURE
 #undef SVG_UNSUPPORTED_FEATURE
   return PR_FALSE;
 }
 
-// Test to see if a list of features are implemented
-PRBool
-NS_SVG_TestFeatures(const nsAString& fstr) {
-  nsAutoString lstr(fstr);
-  // Get an iterator on the string
-  PRInt32 vbegin = 0;
-  PRInt32 vlen = lstr.Length();
-  while (vbegin < vlen) {
-    PRInt32 vend = lstr.FindChar(PRUnichar(' '), vbegin);
-    if (vend == kNotFound) {
-      vend = vlen;
-    }
-    if (NS_SVG_TestFeature(Substring(lstr, vbegin, vend-vbegin)) == PR_FALSE) {
+/**
+ * Check whether we support the given list of feature strings.
+ *
+ * @param aFeatures a whitespace separated list containing one or more of the
+ *   feature strings specified at http://www.w3.org/TR/SVG11/feature.html
+ */
+static PRBool
+HaveFeatures(const nsSubstring& aFeatures)
+{
+  nsWhitespaceTokenizer tokenizer(aFeatures);
+  while (tokenizer.hasMoreTokens()) {
+    if (!NS_SVG_HaveFeature(tokenizer.nextToken())) {
       return PR_FALSE;
     }
-    vbegin = vend+1;
   }
   return PR_TRUE;
 }
 
-// Test to see if this element supports a specific conditional
+/**
+ * Compare the language name(s) in a systemLanguage attribute to the
+ * user's language preferences, as defined in
+ * http://www.w3.org/TR/SVG11/struct.html#SystemLanguageAttribute
+ * We have a match if a language name in the users language preferences
+ * exactly equals one of the language names or exactly equals a prefix of
+ * one of the language names in the systemLanguage attribute.
+ * XXX This algorithm is O(M*N).
+ */
 static PRBool
-NS_SVG_Conditional(const nsIAtom *atom, PRUint16 cond) {
+MatchesLanguagePreferences(const nsSubstring& aAttribute, const nsSubstring& aLanguagePreferences) 
+{
+  const nsDefaultStringComparator defaultComparator;
+
+  nsSVGCommaTokenizer attributeTokenizer(aAttribute);
 
-#define SVG_ELEMENT(_atom, _supports) if (atom == nsGkAtoms::_atom) return (_supports & cond) != 0;
+  while (attributeTokenizer.hasMoreTokens()) {
+    const nsSubstring &attributeToken = attributeTokenizer.nextToken();
+    nsSVGCommaTokenizer languageTokenizer(aLanguagePreferences);
+    while (languageTokenizer.hasMoreTokens()) {
+      if (nsStyleUtil::DashMatchCompare(attributeToken,
+                                        languageTokenizer.nextToken(),
+                                        defaultComparator)) {
+        return PR_TRUE;
+      }
+    }
+  }
+  return PR_FALSE;
+}
+
+/**
+ * Check whether this element supports the specified attributes
+ * (i.e. whether the SVG specification defines the attributes
+ * for the specified element).
+ *
+ * @param aTagName the tag for the element
+ * @param aAttr the conditional to test for, either
+ *    ATTRS_TEST or ATTRS_EXTERNAL
+ */
+static PRBool
+ElementSupportsAttributes(const nsIAtom *aTagName, PRUint16 aAttr)
+{
+#define SVG_ELEMENT(_atom, _supports) if (aTagName == nsGkAtoms::_atom) return (_supports & aAttr) != 0;
 #include "nsSVGElementList.h"
 #undef SVG_ELEMENT
   return PR_FALSE;
 }
 
+/**
+ * Check whether the conditional processing attributes requiredFeatures,
+ * requiredExtensions and systemLanguage all "return true" if they apply to
+ * and are specified on the given element. Returns true if this element
+ * should be rendered, false if it should not.
+ *
+ * @param aContent the element to test
+ */
 PRBool
-NS_SVG_TestsSupported(const nsIAtom *atom) {
-  return NS_SVG_Conditional(atom, SUPPORTS_TEST);
-}
+NS_SVG_PassesConditionalProcessingTests(nsIContent *aContent)
+{
+  if (!ElementSupportsAttributes(aContent->Tag(), ATTRS_CONDITIONAL)) {
+    return PR_TRUE;
+  }
+
+  // Required Features
+  nsAutoString value;
+  if (aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::requiredFeatures, value)) {
+    if (value.IsEmpty() || !HaveFeatures(value)) {
+      return PR_FALSE;
+    }
+  }
 
-PRBool
-NS_SVG_LangSupported(const nsIAtom *atom) {
-  return NS_SVG_Conditional(atom, SUPPORTS_LANG);
+  // Required Extensions
+  //
+  // The requiredExtensions  attribute defines a list of required language
+  // extensions. Language extensions are capabilities within a user agent that
+  // 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
+  // no extensions.  So, if extensions are required, we don't have
+  // them available.
+  if (aContent->HasAttr(kNameSpaceID_None, nsGkAtoms::requiredExtensions)) {
+    return PR_FALSE;
+  }
+
+  // systemLanguage
+  //
+  // Evaluates to "true" if one of the languages indicated by user preferences
+  // exactly equals one of the languages given in the value of this parameter,
+  // or if one of the languages indicated by user preferences exactly equals a
+  // prefix of one of the languages given in the value of this parameter such
+  // that the first tag character following the prefix is "-".
+  if (aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::systemLanguage,
+                        value)) {
+    // Get our language preferences
+    nsAutoString langPrefs(nsContentUtils::GetLocalizedStringPref("intl.accept_languages"));
+    if (!langPrefs.IsEmpty()) {
+      langPrefs.StripWhitespace();
+      value.StripWhitespace();
+      return MatchesLanguagePreferences(value, langPrefs);
+    } else {
+      // For now, evaluate to true.
+      NS_WARNING("no default language specified for systemLanguage conditional test");
+      return !value.IsEmpty();
+    }
+  }
+
+  return PR_TRUE;
 }
-
-#if 0
-// Enable this when (if?) we support the externalResourcesRequired attribute
-PRBool
-NS_SVG_ExternalSupported(const nsIAtom *atom) {
-  return NS_SVG_Conditional(atom, SUPPORTS_EXTERNAL);
-}
-#endif
--- a/content/svg/content/src/nsSVGSwitchElement.cpp
+++ b/content/svg/content/src/nsSVGSwitchElement.cpp
@@ -29,45 +29,23 @@
  * use your version of this file under the terms of the MPL, indicate your
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
-#include "nsSVGGraphicElement.h"
-#include "nsIDOMSVGSwitchElement.h"
-
-typedef nsSVGGraphicElement nsSVGSwitchElementBase;
-
-class nsSVGSwitchElement : public nsSVGSwitchElementBase,
-                           public nsIDOMSVGSwitchElement
-{
-protected:
-  friend nsresult NS_NewSVGSwitchElement(nsIContent **aResult,
-                                         nsINodeInfo *aNodeInfo);
-  nsSVGSwitchElement(nsINodeInfo *aNodeInfo);
+#include "nsSVGSwitchElement.h"
+#include "nsIFrame.h"
+#include "nsISVGChildFrame.h"
+#include "nsSVGUtils.h"
 
-public:
-  // interfaces:
-
-  NS_DECL_ISUPPORTS_INHERITED
-  NS_DECL_NSIDOMSVGSWITCHELEMENT
-
-  // xxx I wish we could use virtual inheritance
-  NS_FORWARD_NSIDOMNODE(nsSVGSwitchElementBase::)
-  NS_FORWARD_NSIDOMELEMENT(nsSVGSwitchElementBase::)
-  NS_FORWARD_NSIDOMSVGELEMENT(nsSVGSwitchElementBase::)
-
-  // nsIContent
-  NS_IMETHOD_(PRBool) IsAttributeMapped(const nsIAtom* aAttribute) const;
-
-  virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const;
-};
+PRBool 
+NS_SVG_PassesConditionalProcessingTests(nsIContent *aContent);
 
 ////////////////////////////////////////////////////////////////////////
 // implementation
 
 
 NS_IMPL_NS_NEW_SVG_ELEMENT(Switch)
 
 
@@ -86,27 +64,95 @@ NS_INTERFACE_MAP_BEGIN(nsSVGSwitchElemen
 NS_INTERFACE_MAP_END_INHERITING(nsSVGSwitchElementBase)
 
 //----------------------------------------------------------------------
 // Implementation
 
 nsSVGSwitchElement::nsSVGSwitchElement(nsINodeInfo *aNodeInfo)
   : nsSVGSwitchElementBase(aNodeInfo)
 {
-
 }
 
+void
+nsSVGSwitchElement::MaybeInvalidate()
+{
+  // We don't reuse UpdateActiveChild() and check if mActiveChild has changed
+  // to determine if we should call nsSVGUtils::UpdateGraphic. If we did that,
+  // nsSVGUtils::UpdateGraphic would not invalidate the old mActiveChild area!
+
+  PRUint32 count = GetChildCount();
+  for (PRUint32 i = 0; i < count; i++) {
+    nsIContent * child = GetChildAt(i);
+    if (child->IsNodeOfType(nsINode::eELEMENT) &&
+        NS_SVG_PassesConditionalProcessingTests(child)) {
+
+      if (mActiveChild == child) {
+        return;
+      }
+
+      nsIFrame *frame = GetPrimaryFrame();
+      if (frame) {
+        nsISVGChildFrame* svgFrame = nsnull;
+
+        CallQueryInterface(frame, &svgFrame);
+        if (svgFrame) {
+          nsSVGUtils::UpdateGraphic(svgFrame);
+        }
+      }
+      return;
+    }
+  }
+}
+
+void
+nsSVGSwitchElement::UpdateActiveChild()
+{
+  PRUint32 count = GetChildCount();
+  for (PRUint32 i = 0; i < count; i++) {
+    nsIContent * child = GetChildAt(i);
+    if (child->IsNodeOfType(nsINode::eELEMENT) &&
+        NS_SVG_PassesConditionalProcessingTests(child)) {
+      mActiveChild = child;
+      return;
+    }
+  }
+  mActiveChild = nsnull;
+}
 
 //----------------------------------------------------------------------
 // nsIDOMNode methods
 
 
 NS_IMPL_ELEMENT_CLONE_WITH_INIT(nsSVGSwitchElement)
 
+//----------------------------------------------------------------------
+// nsINode methods
 
+nsresult
+nsSVGSwitchElement::InsertChildAt(nsIContent* aKid,
+                                  PRUint32 aIndex,
+                                  PRBool aNotify)
+{
+  nsresult rv = nsSVGSwitchElementBase::InsertChildAt(aKid, aIndex, aNotify);
+  if (NS_SUCCEEDED(rv)) {
+    MaybeInvalidate();
+  }
+  return rv;
+}
+
+nsresult
+nsSVGSwitchElement::RemoveChildAt(PRUint32 aIndex, PRBool aNotify)
+{
+  nsresult rv = nsSVGSwitchElementBase::RemoveChildAt(aIndex, aNotify);
+  if (NS_SUCCEEDED(rv)) {
+    MaybeInvalidate();
+  }
+  return rv;
+}
+ 
 //----------------------------------------------------------------------
 // nsIContent methods
 
 NS_IMETHODIMP_(PRBool)
 nsSVGSwitchElement::IsAttributeMapped(const nsIAtom* name) const
 {
   static const MappedAttributeEntry* const map[] = {
     sFEFloodMap,
new file mode 100644
--- /dev/null
+++ b/content/svg/content/src/nsSVGSwitchElement.h
@@ -0,0 +1,83 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Mozilla SVG project.
+ *
+ * The Initial Developer of the Original Code is
+ * Robert Longson.
+ * Portions created by the Initial Developer are Copyright (C) 2008
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Robert Longson <longsonr@gmail.com> (original author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "nsSVGGraphicElement.h"
+#include "nsIDOMSVGSwitchElement.h"
+
+typedef nsSVGGraphicElement nsSVGSwitchElementBase;
+
+class nsSVGSwitchElement : public nsSVGSwitchElementBase,
+                           public nsIDOMSVGSwitchElement
+{
+  friend class nsSVGSwitchFrame;
+protected:
+  friend nsresult NS_NewSVGSwitchElement(nsIContent **aResult,
+                                         nsINodeInfo *aNodeInfo);
+  nsSVGSwitchElement(nsINodeInfo *aNodeInfo);
+
+public:
+  nsIContent * GetActiveChild()
+  { return mActiveChild; }
+  void MaybeInvalidate();
+    
+  // interfaces:
+
+  NS_DECL_ISUPPORTS_INHERITED
+  NS_DECL_NSIDOMSVGSWITCHELEMENT
+
+  // xxx I wish we could use virtual inheritance
+  NS_FORWARD_NSIDOMNODE(nsSVGSwitchElementBase::)
+  NS_FORWARD_NSIDOMELEMENT(nsSVGSwitchElementBase::)
+  NS_FORWARD_NSIDOMSVGELEMENT(nsSVGSwitchElementBase::)
+
+  // nsINode
+  virtual nsresult InsertChildAt(nsIContent* aKid, PRUint32 aIndex,
+                                 PRBool aNotify);
+  virtual nsresult RemoveChildAt(PRUint32 aIndex, PRBool aNotify);
+
+  // nsIContent
+  NS_IMETHOD_(PRBool) IsAttributeMapped(const nsIAtom* aAttribute) const;
+
+  virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const;
+
+private:
+  void UpdateActiveChild();
+
+  // only this child will be displayed
+  nsCOMPtr<nsIContent> mActiveChild;
+};
--- a/layout/base/nsCSSFrameConstructor.cpp
+++ b/layout/base/nsCSSFrameConstructor.cpp
@@ -175,29 +175,27 @@ NS_NewSVGGenericContainerFrame(nsIPresSh
 nsIFrame*
 NS_NewSVGForeignObjectFrame(nsIPresShell* aPresShell, nsIContent* aContent, nsStyleContext* aContext);
 #endif
 nsIFrame*
 NS_NewSVGAFrame(nsIPresShell* aPresShell, nsIContent* aContent, nsStyleContext* aContext);
 nsIFrame*
 NS_NewSVGGlyphFrame(nsIPresShell* aPresShell, nsIContent* aContent, nsIFrame* parent, nsStyleContext* aContext);
 nsIFrame*
+NS_NewSVGSwitchFrame(nsIPresShell* aPresShell, nsIContent* aContent, nsStyleContext* aContext);
+nsIFrame*
 NS_NewSVGTextFrame(nsIPresShell* aPresShell, nsIContent* aContent, nsStyleContext* aContext);
 nsIFrame*
 NS_NewSVGTSpanFrame(nsIPresShell* aPresShell, nsIContent* aContent, nsIFrame* parent, nsStyleContext* aContext);
 nsIFrame*
 NS_NewSVGContainerFrame(nsIPresShell* aPresShell, nsIContent* aContent, nsStyleContext* aContext);
 nsIFrame*
 NS_NewSVGUseFrame(nsIPresShell* aPresShell, nsIContent* aContent, nsStyleContext* aContext);
 PRBool 
-NS_SVG_TestFeatures (const nsAString& value);
-PRBool 
-NS_SVG_TestsSupported (const nsIAtom *atom);
-PRBool 
-NS_SVG_LangSupported (const nsIAtom *atom);
+NS_SVG_PassesConditionalProcessingTests(nsIContent *aContent);
 extern nsIFrame*
 NS_NewSVGLinearGradientFrame(nsIPresShell *aPresShell, nsIContent *aContent, nsStyleContext* aContext);
 extern nsIFrame*
 NS_NewSVGRadialGradientFrame(nsIPresShell *aPresShell, nsIContent *aContent, nsStyleContext* aContext);
 extern nsIFrame*
 NS_NewSVGStopFrame(nsIPresShell *aPresShell, nsIContent *aContent, nsIFrame *aParentFrame, nsStyleContext* aContext);
 nsIFrame*
 NS_NewSVGMarkerFrame(nsIPresShell* aPresShell, nsIContent* aContent, nsStyleContext* aContext);
@@ -392,54 +390,16 @@ SVG_GetFirstNonAAncestorFrame(nsIFrame *
   for (nsIFrame *ancestorFrame = aParentFrame; ancestorFrame != nsnull;
        ancestorFrame = ancestorFrame->GetParent()) {
     if (ancestorFrame->GetType() != nsGkAtoms::svgAFrame) {
       return ancestorFrame;
     }
   }
   return nsnull;
 }
-
-// Test to see if this language is supported
-static PRBool
-SVG_TestLanguage(const nsSubstring& lstr, const nsSubstring& prefs) 
-{
-  // Compare list to attribute value, which may be a list
-  // According to the SVG 1.1 Spec (at least as I read it), we should take
-  // the first attribute value and check it for any matches in the users
-  // preferences, including any prefix matches.
-  // This algorithm is O(M*N)
-  PRInt32 vbegin = 0;
-  PRInt32 vlen = lstr.Length();
-  while (vbegin < vlen) {
-    PRInt32 vend = lstr.FindChar(PRUnichar(','), vbegin);
-    if (vend == kNotFound) {
-      vend = vlen;
-    }
-    PRInt32 gbegin = 0;
-    PRInt32 glen = prefs.Length();
-    while (gbegin < glen) {
-      PRInt32 gend = prefs.FindChar(PRUnichar(','), gbegin);
-      if (gend == kNotFound) {
-        gend = glen;
-      }
-      const nsDefaultStringComparator defaultComparator;
-      const nsStringComparator& comparator = 
-                  static_cast<const nsStringComparator&>(defaultComparator);
-      if (nsStyleUtil::DashMatchCompare(Substring(lstr, vbegin, vend-vbegin),
-                                        Substring(prefs, gbegin, gend-gbegin),
-                                        comparator)) {
-        return PR_TRUE;
-      }
-      gbegin = gend + 1;
-    }
-    vbegin = vend + 1;
-  }
-  return PR_FALSE;
-}
 #endif
 
 static inline nsIFrame*
 GetFieldSetAreaFrame(nsIFrame* aFieldsetFrame)
 {
   // Depends on the fieldset child frame order - see ConstructFieldSetFrame() below.
   nsIFrame* firstChild = aFieldsetFrame->GetFirstChild(nsnull);
   return firstChild && firstChild->GetNextSibling() ? firstChild->GetNextSibling() : firstChild;
@@ -7063,152 +7023,16 @@ nsCSSFrameConstructor::ConstructMathMLFr
     return NS_ERROR_OUT_OF_MEMORY;
   }
 }
 #endif // MOZ_MATHML
 
 // SVG 
 #ifdef MOZ_SVG
 nsresult
-nsCSSFrameConstructor::TestSVGConditions(nsIContent* aContent,
-                                         PRBool&     aHasRequiredExtensions,
-                                         PRBool&     aHasRequiredFeatures,
-                                         PRBool&     aHasSystemLanguage)
-{
-  nsAutoString value;
-
-  // Only elements can have tests on them
-  if (! aContent->IsNodeOfType(nsINode::eELEMENT)) {
-    aHasRequiredExtensions = PR_FALSE;
-    aHasRequiredFeatures = PR_FALSE;
-    aHasSystemLanguage = PR_FALSE;
-    return NS_OK;
-  }
-
-  // Required Extensions
-  //
-  // The requiredExtensions  attribute defines a list of required language
-  // extensions. Language extensions are capabilities within a user agent that
-  // 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
-  // no extensions.  So, if extensions are required, we don't have
-  // them available. Empty-string should always set aHasRequiredExtensions to
-  // false.
-  aHasRequiredExtensions = !aContent->HasAttr(kNameSpaceID_None,
-                                              nsGkAtoms::requiredExtensions);
-
-  // Required Features
-  aHasRequiredFeatures = PR_TRUE;
-  if (aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::requiredFeatures, value)) {
-    aHasRequiredFeatures = !value.IsEmpty() && NS_SVG_TestFeatures(value);
-  }
-
-  // systemLanguage
-  //
-  // Evaluates to "true" if one of the languages indicated by user preferences
-  // exactly equals one of the languages given in the value of this parameter,
-  // or if one of the languages indicated by user preferences exactly equals a
-  // prefix of one of the languages given in the value of this parameter such
-  // that the first tag character following the prefix is "-".
-  aHasSystemLanguage = PR_TRUE;
-  if (aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::systemLanguage,
-                        value)) {
-    // Get our language preferences
-    nsAutoString langPrefs(nsContentUtils::GetLocalizedStringPref("intl.accept_languages"));
-    if (!langPrefs.IsEmpty()) {
-      langPrefs.StripWhitespace();
-      value.StripWhitespace();
-#ifdef  DEBUG_scooter
-      printf("Calling SVG_TestLanguage('%s','%s')\n", NS_ConvertUTF16toUTF8(value).get(), 
-                                                      NS_ConvertUTF16toUTF8(langPrefs).get());
-#endif
-      aHasSystemLanguage = SVG_TestLanguage(value, langPrefs);
-    } else {
-      // For now, evaluate to true.
-      NS_WARNING("no default language specified for systemLanguage conditional test");
-      aHasSystemLanguage = !value.IsEmpty();
-    }
-  }
-  return NS_OK;
-}
-
-nsresult
-nsCSSFrameConstructor::SVGSwitchProcessChildren(nsFrameConstructorState& aState,
-                                                nsIContent*              aContent,
-                                                nsIFrame*                aFrame,
-                                                nsFrameItems&            aFrameItems)
-{
-  nsresult rv = NS_OK;
-  PRBool hasRequiredExtensions = PR_FALSE;
-  PRBool hasRequiredFeatures = PR_FALSE;
-  PRBool hasSystemLanguage = PR_FALSE;
-
-  // save the incoming pseudo frame state
-  nsPseudoFrames priorPseudoFrames;
-  aState.mPseudoFrames.Reset(&priorPseudoFrames);
-
-  // The 'switch' element evaluates the requiredFeatures,
-  // requiredExtensions and systemLanguage attributes on its direct child
-  // elements in order, and then processes and renders the first child for
-  // which these attributes evaluate to true. All others will be bypassed and
-  // therefore not rendered.
-  PRInt32 childCount = aContent->GetChildCount();
-  for (PRInt32 i = 0; i < childCount; ++i) {
-    nsIContent* child = aContent->GetChildAt(i);
-
-    // Skip over children that aren't elements
-    if (!child->IsNodeOfType(nsINode::eELEMENT)) {
-      continue;
-    }
-
-    rv = TestSVGConditions(child,
-                           hasRequiredExtensions,
-                           hasRequiredFeatures,
-                           hasSystemLanguage);
-#ifdef DEBUG_scooter
-    nsAutoString str;
-    child->Tag()->ToString(str);
-    printf("Child tag: %s\n", NS_ConvertUTF16toUTF8(str).get());
-    printf("SwitchProcessChildren: Required Extensions = %s, Required Features = %s, System Language = %s\n",
-            hasRequiredExtensions ? "true" : "false",
-            hasRequiredFeatures ? "true" : "false",
-            hasSystemLanguage ? "true" : "false");
-#endif
-    if (NS_FAILED(rv))
-      return rv;
-
-    if (hasRequiredExtensions &&
-        hasRequiredFeatures &&
-        hasSystemLanguage) {
-
-      rv = ConstructFrame(aState, child,
-                          aFrame, aFrameItems);
-
-      if (NS_FAILED(rv))
-        return rv;
-
-      // No errors -- break out of loop (only render the first matching element)
-      break;
-    }
-  }
-
-  // process the current pseudo frame state
-  if (!aState.mPseudoFrames.IsEmpty()) {
-    ProcessPseudoFrames(aState, aFrameItems);
-  }
-
-  // restore the incoming pseudo frame state
-  aState.mPseudoFrames = priorPseudoFrames;
-
-
-  return rv;
-}
-
-nsresult
 nsCSSFrameConstructor::ConstructSVGFrame(nsFrameConstructorState& aState,
                                          nsIContent*              aContent,
                                          nsIFrame*                aParentFrame,
                                          nsIAtom*                 aTag,
                                          PRInt32                  aNameSpaceID,
                                          nsStyleContext*          aStyleContext,
                                          nsFrameItems&            aFrameItems,
                                          PRBool                   aHasPseudoParent,
@@ -7265,48 +7089,28 @@ nsCSSFrameConstructor::ConstructSVGFrame
     // Style mutation can't change this situation, so don't bother
     // adding to the undisplayed content map.
     //
     // We don't currently handle any UI for desc/title
     *aHaltProcessing = PR_TRUE;
     return NS_OK;
   }
   
-  // Are we another child of a switch which already has a child
-  if (aParentFrame && 
-      aParentFrame->GetType() == nsGkAtoms::svgSwitch &&
-      aParentFrame->GetFirstChild(nsnull)) {
-    *aHaltProcessing = PR_TRUE;
-    return NS_OK;
-  }
-
-  // See if this element supports conditionals & if it does,
-  // handle it
-  if (((aContent->HasAttr(kNameSpaceID_None, nsGkAtoms::requiredFeatures) ||
-        aContent->HasAttr(kNameSpaceID_None, nsGkAtoms::requiredExtensions)) &&
-        NS_SVG_TestsSupported(aTag)) ||
-      (aContent->HasAttr(kNameSpaceID_None, nsGkAtoms::systemLanguage) &&
-       NS_SVG_LangSupported(aTag))) {
-
-    PRBool hasRequiredExtentions = PR_FALSE;
-    PRBool hasRequiredFeatures = PR_FALSE;
-    PRBool hasSystemLanguage = PR_FALSE;
-    TestSVGConditions(aContent, hasRequiredExtentions, 
-                      hasRequiredFeatures, hasSystemLanguage);
+  // Reduce the number of frames we create unnecessarily. Note that this is not
+  // where we select which frame in a <switch> to render! That happens in
+  // nsSVGSwitchFrame::PaintSVG.
+  if (!NS_SVG_PassesConditionalProcessingTests(aContent)) {
     // Note that just returning is probably not right.  According
     // to the spec, <use> is allowed to use an element that fails its
     // conditional, but because we never actually create the frame when
     // a conditional fails and when we use GetReferencedFrame to find the
     // references, things don't work right.
     // XXX FIXME XXX
-    if (!hasRequiredExtentions || !hasRequiredFeatures ||
-        !hasSystemLanguage) {
-      *aHaltProcessing = PR_TRUE;
-      return NS_OK;
-    }
+    *aHaltProcessing = PR_TRUE;
+    return NS_OK;
   }
 
   // Make sure to keep IsSpecialContent in synch with this code
   if (aTag == nsGkAtoms::svg) {
     if (!parentIsSVG) {
       // This is the outermost <svg> element.
       isOuterSVGNode = PR_TRUE;
 
@@ -7316,20 +7120,22 @@ nsCSSFrameConstructor::ConstructSVGFrame
       forceView = PR_TRUE;
       newFrame = NS_NewSVGOuterSVGFrame(mPresShell, aContent, aStyleContext);
     }
     else {
       // This is an inner <svg> element
       newFrame = NS_NewSVGInnerSVGFrame(mPresShell, aContent, aStyleContext);
     }
   }
-  else if (aTag == nsGkAtoms::g ||
-           aTag == nsGkAtoms::svgSwitch) {
+  else if (aTag == nsGkAtoms::g) {
     newFrame = NS_NewSVGGFrame(mPresShell, aContent, aStyleContext);
   }
+  else if (aTag == nsGkAtoms::svgSwitch) {
+    newFrame = NS_NewSVGSwitchFrame(mPresShell, aContent, aStyleContext);
+  }
   else if (aTag == nsGkAtoms::polygon ||
            aTag == nsGkAtoms::polyline ||
            aTag == nsGkAtoms::circle ||
            aTag == nsGkAtoms::ellipse ||
            aTag == nsGkAtoms::line ||
            aTag == nsGkAtoms::rect ||
            aTag == nsGkAtoms::path)
     newFrame = NS_NewSVGPathGeometryFrame(mPresShell, aContent, aStyleContext);
@@ -7477,24 +7283,18 @@ nsCSSFrameConstructor::ConstructSVGFrame
       // Give the blockFrame a view so that GetOffsetTo works for descendants
       // of blockFrame with views...
       nsHTMLContainerFrame::CreateViewForFrame(blockFrame, nsnull, PR_TRUE);
     } else
 #endif  // MOZ_SVG_FOREIGNOBJECT
     {
       // Process the child content if requested.
       if (!newFrame->IsLeaf()) {
-        if (aTag == nsGkAtoms::svgSwitch) {
-          rv = SVGSwitchProcessChildren(aState, aContent, newFrame,
-                                        childItems);
-        } else {
-          rv = ProcessChildren(aState, aContent, newFrame, PR_FALSE, childItems,
-                               PR_FALSE);
-        }
-
+        rv = ProcessChildren(aState, aContent, newFrame, PR_FALSE, childItems,
+                             PR_FALSE);
       }
       CreateAnonymousFrames(aTag, aState, aContent, newFrame,
                             PR_FALSE, childItems);
     }
 
     // Set the frame's initial child list
     newFrame->SetInitialChildList(nsnull, childItems.childList);
     return rv;
--- a/layout/base/nsCSSFrameConstructor.h
+++ b/layout/base/nsCSSFrameConstructor.h
@@ -668,26 +668,16 @@ private:
                              PRInt32                  aNameSpaceID,
                              nsStyleContext*          aStyleContext,
                              nsFrameItems&            aFrameItems,
                              PRBool                   aHasPseudoParent);
 #endif
 
 // SVG - rods
 #ifdef MOZ_SVG
-  nsresult TestSVGConditions(nsIContent* aContent,
-                             PRBool&     aHasRequiredExtensions,
-                             PRBool&     aHasRequiredFeatures,
-                             PRBool&     aHasSystemLanguage);
- 
-  nsresult SVGSwitchProcessChildren(nsFrameConstructorState& aState,
-                                    nsIContent*              aContent,
-                                    nsIFrame*                aFrame,
-                                    nsFrameItems&            aFrameItems);
-
   nsresult ConstructSVGFrame(nsFrameConstructorState& aState,
                              nsIContent*              aContent,
                              nsIFrame*                aParentFrame,
                              nsIAtom*                 aTag,
                              PRInt32                  aNameSpaceID,
                              nsStyleContext*          aStyleContext,
                              nsFrameItems&            aFrameItems,
                              PRBool                   aHasPseudoParent,
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/dynamic-switch-01.svg
@@ -0,0 +1,56 @@
+<?xml version="1.0"?>
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/licenses/publicdomain/
+-->
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg" onload="m();">
+	<title>Testcase for dynamic switch changes</title>
+	<!-- From https://bugzilla.mozilla.org/show_bug.cgi?id=409383 -->
+	<script>
+		function m() {
+		var svgns = "http://www.w3.org/2000/svg";
+
+		var rect1 = document.getElementById("rect1");
+		rect1.parentNode.removeChild(rect1);
+
+		var rect2a = document.createElementNS(svgns, "rect");
+		rect2a.setAttribute("x", "200");
+		rect2a.setAttribute("y", "100");
+		rect2a.setAttribute("width", "50");
+		rect2a.setAttribute("height", "50")
+		rect2a.setAttribute("fill", "lime");
+		var rect2b = document.getElementById("rect2b");
+		rect2b.parentNode.insertBefore(rect2a, rect2b);
+
+		var rect3a = document.getElementById("rect3a");
+		var rect3b = document.getElementById("rect3b");
+		rect3a.parentNode.insertBefore(rect3a, rect3b);
+
+		var rect4a = document.getElementById("rect4a");
+		rect4a.setAttribute("systemLanguage", "foo");
+		}
+	</script>
+	
+	<rect width="100%" height="100%" fill="lime"/>
+	
+	<switch>
+        <!-- test removing first child -->
+		<rect id="rect1" x="50" y="100" width="50" height="50" fill="red"/>
+		<rect x="50" y="100" width="50" height="50" fill="lime"/>
+		<rect x="50" y="100" width="50" height="50" fill="red"/>
+	</switch>
+	<switch>
+		<!-- test adding first child -->
+		<rect id="rect2b" x="200" y="100" width="50" height="50" fill="red"/>
+	</switch>
+	<switch>
+		<!-- test change child order -->
+		<rect id="rect3b" x="50" y="200" width="50" height="50" fill="red"/>
+		<rect id="rect3a" x="50" y="200" width="50" height="50" fill="lime"/>
+	</switch>
+	<switch>
+		<!-- test change child attribute -->
+		<rect id="rect4a" x="200" y="200" width="50" height="50" fill="red"/>
+		<rect x="200" y="200" width="50" height="50" fill="lime"/>
+	</switch>
+</svg>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/switch-01.svg
@@ -0,0 +1,70 @@
+<?xml version="1.0"?>
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/licenses/publicdomain/
+-->
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg">
+	<title>Testcase for switch</title>
+	<!-- From https://bugzilla.mozilla.org/show_bug.cgi?id=409383 -->
+
+	<rect width="100%" height="100%" fill="lime"/>
+
+	<rect x="50" y="100" width="50" height="50" fill="red"/>
+	<switch>
+        <!-- first switch item is visible -->
+     	<rect x="50" y="100" width="50" height="50" fill="lime"/>
+		<rect x="50" y="100" width="50" height="50" fill="red"/>
+		<rect x="50" y="100" width="50" height="50" fill="red"/>
+		<rect x="50" y="100" width="50" height="50" fill="red"/>
+		<rect x="50" y="100" width="50" height="50" fill="red"/>
+		<rect x="50" y="100" width="50" height="50" fill="red"/>
+		<rect x="50" y="100" width="50" height="50" fill="red"/>
+		<rect x="50" y="100" width="50" height="50" fill="red"/>
+		<rect x="50" y="100" width="50" height="50" fill="red"/>
+		<rect x="50" y="100" width="50" height="50" fill="red"/>
+		<rect x="50" y="100" width="50" height="50" fill="red"/>
+		<rect x="50" y="100" width="50" height="50" fill="red"/>
+	</switch>
+
+	<rect x="200" y="100" width="50" height="50" fill="red"/>
+	<switch>
+		<!-- test non-matching system language -->
+		<rect systemLanguage="foo" x="200" y="100" width="50" height="50" fill="red"/>
+		<rect x="200" y="100" width="50" height="50" fill="lime"/>
+		<rect x="200" y="100" width="50" height="50" fill="red"/>
+		<rect x="200" y="100" width="50" height="50" fill="red"/>
+	</switch>
+
+	<rect x="50" y="200" width="50" height="50" fill="red"/>
+	<switch>
+		<!-- test matching required features -->
+		<rect requiredFeatures="http://www.w3.org/TR/SVG11/feature#CoreAttribute  http://www.w3.org/TR/SVG11/feature#Gradient"
+			  x="50" y="200" width="50" height="50" fill="lime"/>
+		<rect x="50" y="200" width="50" height="50" fill="red"/>
+		<rect x="50" y="200" width="50" height="50" fill="red"/>
+	</switch>
+
+	<rect x="200" y="200" width="50" height="50" fill="red"/>
+	<switch>
+		<!-- test non-matching required features -->
+		<rect requiredFeatures="foo" x="200" y="200" width="50" height="50" fill="red"/>
+		<rect x="200" y="200" width="50" height="50" fill="lime"/>
+		<rect x="200" y="200" width="50" height="50" fill="red"/>
+		<rect x="200" y="200" width="50" height="50" fill="red"/>
+	</switch>
+
+	<rect x="50" y="300" width="50" height="50" fill="red"/>
+	<switch>
+		<!-- test non-matching required extensions -->
+		<rect requiredExtensions="foo" x="50" y="300" width="50" height="50" fill="red"/>
+		<rect x="50" y="300" width="50" height="50" fill="lime"/>
+		<rect x="50" y="300" width="50" height="50" fill="red"/>
+		<rect x="50" y="300" width="50" height="50" fill="red"/>
+	</switch>
+
+	<switch>
+		<!-- test display:none - should see nothing here -->
+		<rect style="display: none;"  x="200" y="300" width="50" height="50" fill="red"/>
+		<rect x="200" y="300" width="50" height="50" fill="red"/>
+	</switch>
+</svg>
--- a/layout/svg/base/src/Makefile.in
+++ b/layout/svg/base/src/Makefile.in
@@ -80,16 +80,17 @@ CPPSRCS		= \
 		nsSVGLeafFrame.cpp \
 		nsSVGMarkerFrame.cpp \
 		nsSVGMaskFrame.cpp \
 		nsSVGOuterSVGFrame.cpp \
 		nsSVGPaintServerFrame.cpp \
 		nsSVGPathGeometryFrame.cpp \
 		nsSVGPatternFrame.cpp \
 		nsSVGStopFrame.cpp \
+		nsSVGSwitchFrame.cpp \
 		nsSVGTextContainerFrame.cpp \
 		nsSVGTextFrame.cpp \
 		nsSVGTextPathFrame.cpp \
 		nsSVGTSpanFrame.cpp \
 		nsSVGUseFrame.cpp \
 		nsSVGUtils.cpp \
 		$(NULL)
 
--- a/layout/svg/base/src/nsSVGSwitchFrame.cpp
+++ b/layout/svg/base/src/nsSVGSwitchFrame.cpp
@@ -32,42 +32,55 @@
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsSVGGFrame.h"
-#include "nsIDOMSVGSwitchElement.h"
+#include "nsSVGSwitchElement.h"
+#include "nsIDOMSVGRect.h"
 
-typedef nsSVGGFrame nsSVGGSwitchFrameBase;
+typedef nsSVGGFrame nsSVGSwitchFrameBase;
 
-class nsSVGSwitchFrame : public nsSVGGSwitchFrameBase
+class nsSVGSwitchFrame : public nsSVGSwitchFrameBase
 {
   friend nsIFrame*
   NS_NewSVGSwitchFrame(nsIPresShell* aPresShell, nsIContent* aContent, nsStyleContext* aContext);
 protected:
   nsSVGSwitchFrame(nsStyleContext* aContext) :
-    nsSVGGSwitchFrameBase(aContext) {}
+    nsSVGSwitchFrameBase(aContext) {}
 
 public:
   /**
    * Get the "type" of the frame
    *
    * @see nsGkAtoms::svgSwitchFrame
    */
   virtual nsIAtom* GetType() const;
 
 #ifdef DEBUG
   NS_IMETHOD GetFrameName(nsAString& aResult) const
   {
     return MakeFrameName(NS_LITERAL_STRING("SVGSwitch"), aResult);
   }
 #endif
+
+  // nsISVGChildFrame interface:
+  NS_IMETHOD PaintSVG(nsSVGRenderState* aContext, nsRect *aDirtyRect);
+  NS_IMETHOD GetFrameForPointSVG(float aX, float aY, nsIFrame** aResult);  
+  NS_IMETHODIMP_(nsRect) GetCoveredRegion();
+  NS_IMETHOD UpdateCoveredRegion();
+  NS_IMETHOD InitialUpdate();
+  NS_IMETHOD NotifyRedrawUnsuspended();
+  NS_IMETHOD GetBBox(nsIDOMSVGRect **aRect);
+
+private:
+  nsIFrame *GetActiveChildFrame();
 };
 
 //----------------------------------------------------------------------
 // Implementation
 
 nsIFrame*
 NS_NewSVGSwitchFrame(nsIPresShell* aPresShell, nsIContent* aContent, nsStyleContext* aContext)
 {  
@@ -80,8 +93,120 @@ NS_NewSVGSwitchFrame(nsIPresShell* aPres
   return new (aPresShell) nsSVGSwitchFrame(aContext);
 }
 
 nsIAtom *
 nsSVGSwitchFrame::GetType() const
 {
   return nsGkAtoms::svgSwitchFrame;
 }
+
+NS_IMETHODIMP
+nsSVGSwitchFrame::PaintSVG(nsSVGRenderState* aContext, nsRect *aDirtyRect)
+{
+  const nsStyleDisplay *display = mStyleContext->GetStyleDisplay();
+  if (display->mOpacity == 0.0)
+    return NS_OK;
+
+  nsIFrame *kid = GetActiveChildFrame();
+  if (kid) {
+    nsSVGUtils::PaintChildWithEffects(aContext, aDirtyRect, kid);
+  }
+  return NS_OK;
+}
+
+
+NS_IMETHODIMP
+nsSVGSwitchFrame::GetFrameForPointSVG(float aX, float aY, nsIFrame** aResult)
+{
+  *aResult = nsnull;
+
+  nsIFrame *kid = GetActiveChildFrame();
+  if (kid) {
+    nsISVGChildFrame* svgFrame;
+    CallQueryInterface(kid, &svgFrame);
+    if (svgFrame) {
+      svgFrame->GetFrameForPointSVG(aX, aY, aResult);
+    }
+  }
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP_(nsRect)
+nsSVGSwitchFrame::GetCoveredRegion()
+{
+  nsRect rect;
+
+  nsIFrame *kid = GetActiveChildFrame();
+  if (kid) {
+    nsISVGChildFrame* child = nsnull;
+    CallQueryInterface(kid, &child);
+    if (child) {
+      rect = child->GetCoveredRegion();
+    }
+  }
+  return rect;
+}
+
+NS_IMETHODIMP
+nsSVGSwitchFrame::UpdateCoveredRegion()
+{
+  static_cast<nsSVGSwitchElement*>(mContent)->UpdateActiveChild();
+
+  return nsSVGSwitchFrameBase::UpdateCoveredRegion();
+}
+
+NS_IMETHODIMP
+nsSVGSwitchFrame::InitialUpdate()
+{
+  nsSVGUtils::UpdateGraphic(this);
+
+  return nsSVGSwitchFrameBase::InitialUpdate();
+}
+
+NS_IMETHODIMP
+nsSVGSwitchFrame::NotifyRedrawUnsuspended()
+{
+  if (GetStateBits() & NS_STATE_SVG_DIRTY)
+    nsSVGUtils::UpdateGraphic(this);
+
+  return nsSVGSwitchFrameBase::NotifyRedrawUnsuspended();
+}
+
+NS_IMETHODIMP
+nsSVGSwitchFrame::GetBBox(nsIDOMSVGRect **aRect)
+{
+  *aRect = nsnull;
+
+  nsIFrame *kid = GetActiveChildFrame();
+  if (kid) {
+    nsISVGChildFrame* svgFrame = nsnull;
+    CallQueryInterface(kid, &svgFrame);
+    if (svgFrame) {
+      nsCOMPtr<nsIDOMSVGRect> box;
+      svgFrame->GetBBox(getter_AddRefs(box));
+      if (box) {
+        box.swap(*aRect);
+        return NS_OK;
+      }
+    }
+  }
+  return NS_ERROR_FAILURE;
+}
+
+nsIFrame *
+nsSVGSwitchFrame::GetActiveChildFrame()
+{
+  nsIContent *activeChild =
+    static_cast<nsSVGSwitchElement*>(mContent)->GetActiveChild();
+
+  if (activeChild) {
+    for (nsIFrame* kid = mFrames.FirstChild(); kid;
+         kid = kid->GetNextSibling()) {
+
+      if (activeChild == kid->GetContent()) {
+        return kid;
+      }
+    }
+  }
+  return nsnull;
+}