Bug 629200 part 20 - Remove unnecessary serialisation from setting SVGPointList; r=jwatt
authorBrian Birtles <birtles@gmail.com>
Thu, 16 Feb 2012 08:40:46 +0900
changeset 89832 1348090e5c2716e0881e319556946bd698cd8c7d
parent 89831 e6b4f0dbd601a0c04fde82090855237aa14b1418
child 89833 99b03e5c027c6b782acf92b562567f5dc0a124ca
push idunknown
push userunknown
push dateunknown
reviewersjwatt
bugs629200
milestone13.0a1
Bug 629200 part 20 - Remove unnecessary serialisation from setting SVGPointList; r=jwatt
content/base/src/nsAttrValue.cpp
content/base/src/nsAttrValue.h
content/svg/content/src/DOMSVGPoint.cpp
content/svg/content/src/DOMSVGPointList.cpp
content/svg/content/src/Makefile.in
content/svg/content/src/nsSVGElement.cpp
content/svg/content/src/nsSVGElement.h
content/svg/content/test/Makefile.in
content/svg/content/test/test_SVGPointList.xhtml
--- a/content/base/src/nsAttrValue.cpp
+++ b/content/base/src/nsAttrValue.cpp
@@ -54,16 +54,17 @@
 #include "nsSVGAngle.h"
 #include "nsSVGIntegerPair.h"
 #include "nsSVGLength2.h"
 #include "nsSVGNumberPair.h"
 #include "nsSVGViewBox.h"
 #include "SVGAnimatedPreserveAspectRatio.h"
 #include "SVGLengthList.h"
 #include "SVGNumberList.h"
+#include "SVGPointList.h"
 
 namespace css = mozilla::css;
 
 #define MISC_STR_PTR(_cont) \
   reinterpret_cast<void*>((_cont)->mStringBits & NS_ATTRVALUE_POINTERVALUE_MASK)
 
 nsTArray<const nsAttrValue::EnumTable*>* nsAttrValue::sEnumTableArray = nsnull;
 
@@ -297,16 +298,21 @@ nsAttrValue::SetTo(const nsAttrValue& aO
       cont->mSVGNumberList = otherCont->mSVGNumberList;
       break;
     }
     case eSVGNumberPair:
     {
       cont->mSVGNumberPair = otherCont->mSVGNumberPair;
       break;
     }
+    case eSVGPointList:
+    {
+      cont->mSVGPointList = otherCont->mSVGPointList;
+      break;
+    }
     case eSVGPreserveAspectRatio:
     {
       cont->mSVGPreserveAspectRatio = otherCont->mSVGPreserveAspectRatio;
       break;
     }
     case eSVGViewBox:
     {
       cont->mSVGViewBox = otherCont->mSVGViewBox;
@@ -477,16 +483,30 @@ nsAttrValue::SetTo(const nsSVGNumberPair
     MiscContainer* cont = GetMiscContainer();
     cont->mSVGNumberPair = &aValue;
     cont->mType = eSVGNumberPair;
     SetMiscAtomOrString(aSerialized);
   }
 }
 
 void
+nsAttrValue::SetTo(const mozilla::SVGPointList& aValue,
+                   const nsAString* aSerialized)
+{
+  if (EnsureEmptyMiscContainer()) {
+    MiscContainer* cont = GetMiscContainer();
+    cont->mSVGPointList = &aValue;
+    cont->mType = eSVGPointList;
+    if (aSerialized && aSerialized->Length()) {
+      SetMiscAtomOrString(aSerialized);
+    }
+  }
+}
+
+void
 nsAttrValue::SetTo(const mozilla::SVGAnimatedPreserveAspectRatio& aValue,
                    const nsAString* aSerialized)
 {
   if (EnsureEmptyMiscContainer()) {
     MiscContainer* cont = GetMiscContainer();
     cont->mSVGPreserveAspectRatio = &aValue;
     cont->mType = eSVGPreserveAspectRatio;
     SetMiscAtomOrString(aSerialized);
@@ -626,16 +646,21 @@ nsAttrValue::ToString(nsAString& aResult
       GetMiscContainer()->mSVGNumberList->GetValueAsString(aResult);
       break;
     }
     case eSVGNumberPair:
     {
       GetMiscContainer()->mSVGNumberPair->GetBaseValueString(aResult);
       break;
     }
+    case eSVGPointList:
+    {
+      GetMiscContainer()->mSVGPointList->GetValueAsString(aResult);
+      break;
+    }
     case eSVGPreserveAspectRatio:
     {
       GetMiscContainer()->mSVGPreserveAspectRatio->GetBaseValueString(aResult);
       break;
     }
     case eSVGViewBox:
     {
       GetMiscContainer()->mSVGViewBox->GetBaseValueString(aResult);
@@ -844,16 +869,20 @@ nsAttrValue::HashValue() const
     case eSVGNumberList:
     {
       return NS_PTR_TO_INT32(cont->mSVGNumberList);
     }
     case eSVGNumberPair:
     {
       return NS_PTR_TO_INT32(cont->mSVGNumberPair);
     }
+    case eSVGPointList:
+    {
+      return NS_PTR_TO_INT32(cont->mSVGPointList);
+    }
     case eSVGPreserveAspectRatio:
     {
       return NS_PTR_TO_INT32(cont->mSVGPreserveAspectRatio);
     }
     case eSVGViewBox:
     {
       return NS_PTR_TO_INT32(cont->mSVGViewBox);
     }
@@ -968,16 +997,20 @@ nsAttrValue::Equals(const nsAttrValue& a
     case eSVGNumberList:
     {
       return thisCont->mSVGNumberList == otherCont->mSVGNumberList;
     }
     case eSVGNumberPair:
     {
       return thisCont->mSVGNumberPair == otherCont->mSVGNumberPair;
     }
+    case eSVGPointList:
+    {
+      return thisCont->mSVGPointList == otherCont->mSVGPointList;
+    }
     case eSVGPreserveAspectRatio:
     {
       return thisCont->mSVGPreserveAspectRatio ==
         otherCont->mSVGPreserveAspectRatio;
     }
     case eSVGViewBox:
     {
       return thisCont->mSVGViewBox == otherCont->mSVGViewBox;
--- a/content/base/src/nsAttrValue.h
+++ b/content/base/src/nsAttrValue.h
@@ -66,16 +66,17 @@ class nsSVGViewBox;
 
 namespace mozilla {
 namespace css {
 class StyleRule;
 }
 class SVGAnimatedPreserveAspectRatio;
 class SVGLengthList;
 class SVGNumberList;
+class SVGPointList;
 }
 
 #define NS_ATTRVALUE_MAX_STRINGLENGTH_ATOM 12
 
 #define NS_ATTRVALUE_BASETYPE_MASK (PtrBits(3))
 #define NS_ATTRVALUE_POINTERVALUE_MASK (~NS_ATTRVALUE_BASETYPE_MASK)
 
 #define NS_ATTRVALUE_INTEGERTYPE_BITS 4
@@ -136,18 +137,19 @@ public:
     ,eDoubleValue  =   0x12
     ,eIntMarginValue = 0x13
     ,eSVGAngle =       0x14
     ,eSVGIntegerPair = 0x15
     ,eSVGLength =      0x16
     ,eSVGLengthList =  0x17
     ,eSVGNumberList =  0x18
     ,eSVGNumberPair =  0x19
-    ,eSVGPreserveAspectRatio = 0x20
-    ,eSVGViewBox =     0x21
+    ,eSVGPointList  =  0x20
+    ,eSVGPreserveAspectRatio = 0x21
+    ,eSVGViewBox =     0x22
   };
 
   ValueType Type() const;
 
   void Reset();
 
   void SetTo(const nsAttrValue& aOther);
   void SetTo(const nsAString& aValue);
@@ -160,16 +162,17 @@ public:
   void SetTo(const nsSVGAngle& aValue, const nsAString* aSerialized);
   void SetTo(const nsSVGIntegerPair& aValue, const nsAString* aSerialized);
   void SetTo(const nsSVGLength2& aValue, const nsAString* aSerialized);
   void SetTo(const mozilla::SVGLengthList& aValue,
              const nsAString* aSerialized);
   void SetTo(const mozilla::SVGNumberList& aValue,
              const nsAString* aSerialized);
   void SetTo(const nsSVGNumberPair& aValue, const nsAString* aSerialized);
+  void SetTo(const mozilla::SVGPointList& aValue, const nsAString* aSerialized);
   void SetTo(const mozilla::SVGAnimatedPreserveAspectRatio& aValue,
              const nsAString* aSerialized);
   void SetTo(const nsSVGViewBox& aValue, const nsAString* aSerialized);
 
   /**
    * Sets this object with the string or atom representation of aValue.
    *
    * After calling this method, this object will have type eString unless the
@@ -400,16 +403,17 @@ private:
       double mDoubleValue;
       nsIntMargin* mIntMargin;
       const nsSVGAngle* mSVGAngle;
       const nsSVGIntegerPair* mSVGIntegerPair;
       const nsSVGLength2* mSVGLength;
       const mozilla::SVGLengthList* mSVGLengthList;
       const mozilla::SVGNumberList* mSVGNumberList;
       const nsSVGNumberPair* mSVGNumberPair;
+      const mozilla::SVGPointList* mSVGPointList;
       const mozilla::SVGAnimatedPreserveAspectRatio* mSVGPreserveAspectRatio;
       const nsSVGViewBox* mSVGViewBox;
     };
   };
 
   inline ValueBaseType BaseType() const;
 
   /**
--- a/content/svg/content/src/DOMSVGPoint.cpp
+++ b/content/svg/content/src/DOMSVGPoint.cpp
@@ -93,18 +93,22 @@ DOMSVGPoint::SetX(float aX)
 {
   if (mIsAnimValItem || mIsReadonly) {
     return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR;
   }
 
   NS_ENSURE_FINITE(aX, NS_ERROR_ILLEGAL_VALUE);
 
   if (HasOwner()) {
+    if (InternalItem().mX == aX) {
+      return NS_OK;
+    }
+    nsAttrValue emptyOrOldValue = Element()->WillChangePointList();
     InternalItem().mX = aX;
-    Element()->DidChangePointList(true);
+    Element()->DidChangePointList(emptyOrOldValue);
     if (mList->AttrIsAnimating()) {
       Element()->AnimationNeedsResample();
     }
     return NS_OK;
   }
   mPt.mX = aX;
   return NS_OK;
 }
@@ -124,18 +128,22 @@ DOMSVGPoint::SetY(float aY)
 {
   if (mIsAnimValItem || mIsReadonly) {
     return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR;
   }
 
   NS_ENSURE_FINITE(aY, NS_ERROR_ILLEGAL_VALUE);
 
   if (HasOwner()) {
+    if (InternalItem().mY == aY) {
+      return NS_OK;
+    }
+    nsAttrValue emptyOrOldValue = Element()->WillChangePointList();
     InternalItem().mY = aY;
-    Element()->DidChangePointList(true);
+    Element()->DidChangePointList(emptyOrOldValue);
     if (mList->AttrIsAnimating()) {
       Element()->AnimationNeedsResample();
     }
     return NS_OK;
   }
   mPt.mY = aY;
   return NS_OK;
 }
--- a/content/svg/content/src/DOMSVGPointList.cpp
+++ b/content/svg/content/src/DOMSVGPointList.cpp
@@ -208,33 +208,34 @@ DOMSVGPointList::GetNumberOfItems(PRUint
 NS_IMETHODIMP
 DOMSVGPointList::Clear()
 {
   if (IsAnimValList()) {
     return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR;
   }
 
   if (Length() > 0) {
+    nsAttrValue emptyOrOldValue = Element()->WillChangePointList();
     // DOM list items that are to be removed must be removed before we change
     // the internal list, otherwise they wouldn't be able to copy their
     // internal counterparts' values!
 
     InternalListWillChangeTo(SVGPointList()); // clears mItems
 
     if (!AttrIsAnimating()) {
       // The anim val list is in sync with the base val list
       DOMSVGPointList *animList =
         GetDOMWrapperIfExists(InternalAList().GetAnimValKey());
       if (animList) {
         animList->InternalListWillChangeTo(SVGPointList()); // clears its mItems
       }
     }
 
     InternalList().Clear();
-    Element()->DidChangePointList(true);
+    Element()->DidChangePointList(emptyOrOldValue);
     if (AttrIsAnimating()) {
       Element()->AnimationNeedsResample();
     }
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
@@ -302,30 +303,31 @@ DOMSVGPointList::InsertItemBefore(nsIDOM
   }
 
   // Ensure we have enough memory so we can avoid complex error handling below:
   if (!mItems.SetCapacity(mItems.Length() + 1) ||
       !InternalList().SetCapacity(InternalList().Length() + 1)) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
+  nsAttrValue emptyOrOldValue = Element()->WillChangePointList();
   // Now that we know we're inserting, keep animVal list in sync as necessary.
   MaybeInsertNullInAnimValListAt(aIndex);
 
   InternalList().InsertItem(aIndex, domItem->ToSVGPoint());
   mItems.InsertElementAt(aIndex, domItem.get());
 
   // This MUST come after the insertion into InternalList(), or else under the
   // insertion into InternalList() the values read from domItem would be bad
   // data from InternalList() itself!:
   domItem->InsertingIntoList(this, aIndex, IsAnimValList());
 
   UpdateListIndicesFromIndex(mItems, aIndex + 1);
 
-  Element()->DidChangePointList(true);
+  Element()->DidChangePointList(emptyOrOldValue);
   if (AttrIsAnimating()) {
     Element()->AnimationNeedsResample();
   }
   *_retval = domItem.forget().get();
   return NS_OK;
 }
 
 NS_IMETHODIMP
@@ -344,30 +346,31 @@ DOMSVGPointList::ReplaceItem(nsIDOMSVGPo
   }
   if (aIndex >= Length()) {
     return NS_ERROR_DOM_INDEX_SIZE_ERR;
   }
   if (domItem->HasOwner() || domItem->IsReadonly()) {
     domItem = domItem->Clone(); // must do this before changing anything!
   }
 
+  nsAttrValue emptyOrOldValue = Element()->WillChangePointList();
   if (mItems[aIndex]) {
     // Notify any existing DOM item of removal *before* modifying the lists so
     // that the DOM item can copy the *old* value at its index:
     mItems[aIndex]->RemovingFromList();
   }
 
   InternalList()[aIndex] = domItem->ToSVGPoint();
   mItems[aIndex] = domItem;
 
   // This MUST come after the ToSVGPoint() call, otherwise that call
   // would end up reading bad data from InternalList()!
   domItem->InsertingIntoList(this, aIndex, IsAnimValList());
 
-  Element()->DidChangePointList(true);
+  Element()->DidChangePointList(emptyOrOldValue);
   if (AttrIsAnimating()) {
     Element()->AnimationNeedsResample();
   }
   NS_ADDREF(*_retval = domItem.get());
   return NS_OK;
 }
 
 NS_IMETHODIMP
@@ -378,16 +381,17 @@ DOMSVGPointList::RemoveItem(PRUint32 aIn
   if (IsAnimValList()) {
     return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR;
   }
 
   if (aIndex >= Length()) {
     return NS_ERROR_DOM_INDEX_SIZE_ERR;
   }
 
+  nsAttrValue emptyOrOldValue = Element()->WillChangePointList();
   // Now that we know we're removing, keep animVal list in sync as necessary.
   // Do this *before* touching InternalList() so the removed item can get its
   // internal value.
   MaybeRemoveItemFromAnimValListAt(aIndex);
 
   // We have to return the removed item, so make sure it exists:
   EnsureItemAt(aIndex);
 
@@ -396,17 +400,17 @@ DOMSVGPointList::RemoveItem(PRUint32 aIn
   mItems[aIndex]->RemovingFromList();
   NS_ADDREF(*_retval = mItems[aIndex]);
 
   InternalList().RemoveItem(aIndex);
   mItems.RemoveElementAt(aIndex);
 
   UpdateListIndicesFromIndex(mItems, aIndex);
 
-  Element()->DidChangePointList(true);
+  Element()->DidChangePointList(emptyOrOldValue);
   if (AttrIsAnimating()) {
     Element()->AnimationNeedsResample();
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 DOMSVGPointList::AppendItem(nsIDOMSVGPoint *aNewItem,
--- a/content/svg/content/src/Makefile.in
+++ b/content/svg/content/src/Makefile.in
@@ -174,16 +174,18 @@ EXPORTS =  			\
 	nsSVGLength2.h             \
 	nsSVGNumberPair.h          \
 	nsSVGRect.h                \
 	nsSVGViewBox.h             \
 	SVGAnimatedPreserveAspectRatio.h \
 	SVGLength.h                \
 	SVGLengthList.h            \
 	SVGNumberList.h            \
+	SVGPoint.h                 \
+	SVGPointList.h             \
 	$(NULL)
 
 include $(topsrcdir)/config/rules.mk
 
 INCLUDES += 	\
 		-I$(srcdir)/../../../xml/content/src \
 		-I$(srcdir)/../../../../dom \
 		-I$(srcdir)/../../../base/src \
--- a/content/svg/content/src/nsSVGElement.cpp
+++ b/content/svg/content/src/nsSVGElement.cpp
@@ -360,21 +360,22 @@ nsSVGElement::ParseAttribute(PRInt32 aNa
       }
     }
 
     if (!foundMatch) {
       // Check for SVGAnimatedPointList attribute
       if (GetPointListAttrName() == aAttribute) {
         SVGAnimatedPointList* pointList = GetAnimatedPointList();
         if (pointList) {
-          rv = pointList->SetBaseValueString(aValue);
-          if (NS_FAILED(rv)) {
-            // The spec says we parse everything up to the failure, so we don't
-            // call pointList->ClearBaseValue()
-          }
+          pointList->SetBaseValueString(aValue);
+          // The spec says we parse everything up to the failure, so we DON'T
+          // need to check the result of SetBaseValueString or call
+          // pointList->ClearBaseValue() if it fails
+          aResult.SetTo(pointList->GetBaseValue(), &aValue);
+          didSetResult = true;
           foundMatch = true;
         }
       }
     }
 
     if (!foundMatch) {
       // Check for SVGAnimatedPathSegList attribute
       if (GetPathDataAttrName() == aAttribute) {
@@ -666,16 +667,17 @@ nsSVGElement::UnsetAttrInternal(PRInt32 
         return;
       }
     }
 
     // Check if this is a point list attribute going away
     if (GetPointListAttrName() == aName) {
       SVGAnimatedPointList *pointList = GetAnimatedPointList();
       if (pointList) {
+        MaybeSerializeAttrBeforeRemoval(aName, aNotify);
         pointList->ClearBaseValue();
         return;
       }
     }
 
     // Check if this is a path segment list attribute going away
     if (GetPathDataAttrName() == aName) {
       SVGAnimatedPathSegList *segList = GetAnimPathSegList();
@@ -1749,30 +1751,34 @@ nsSVGElement::GetAnimatedNumberList(nsIA
     if (aAttrName == *info.mNumberListInfo[i].mName) {
       return &info.mNumberLists[i];
     }
   }
   NS_ABORT_IF_FALSE(false, "Bad caller");
   return nsnull;
 }
 
-void
-nsSVGElement::DidChangePointList(bool aDoSetAttr)
+nsAttrValue
+nsSVGElement::WillChangePointList()
 {
-  NS_ABORT_IF_FALSE(GetPointListAttrName(), "Changing non-existent point list?");
+  NS_ABORT_IF_FALSE(GetPointListAttrName(),
+                    "Changing non-existent point list?");
+  return WillChangeValue(GetPointListAttrName());
+}
 
-  if (!aDoSetAttr)
-    return;
+void
+nsSVGElement::DidChangePointList(const nsAttrValue& aEmptyOrOldValue)
+{
+  NS_ABORT_IF_FALSE(GetPointListAttrName(),
+                    "Changing non-existent point list?");
 
-  nsAutoString serializedValue;
-  GetAnimatedPointList()->GetBaseValue().GetValueAsString(serializedValue);
+  nsAttrValue newValue;
+  newValue.SetTo(GetAnimatedPointList()->GetBaseValue(), nsnull);
 
-  nsAttrValue attrValue(serializedValue);
-  SetParsedAttr(kNameSpaceID_None, GetPointListAttrName(), nsnull,
-                attrValue, true);
+  DidChangeValue(GetPointListAttrName(), aEmptyOrOldValue, newValue);
 }
 
 void
 nsSVGElement::DidAnimatePointList()
 {
   NS_ABORT_IF_FALSE(GetPointListAttrName(),
                     "Animating non-existent path data?");
 
--- a/content/svg/content/src/nsSVGElement.h
+++ b/content/svg/content/src/nsSVGElement.h
@@ -173,16 +173,17 @@ public:
   nsAttrValue WillChangeLength(PRUint8 aAttrEnum);
   nsAttrValue WillChangeNumberPair(PRUint8 aAttrEnum);
   nsAttrValue WillChangeIntegerPair(PRUint8 aAttrEnum);
   nsAttrValue WillChangeAngle(PRUint8 aAttrEnum);
   nsAttrValue WillChangeViewBox();
   nsAttrValue WillChangePreserveAspectRatio();
   nsAttrValue WillChangeNumberList(PRUint8 aAttrEnum);
   nsAttrValue WillChangeLengthList(PRUint8 aAttrEnum);
+  nsAttrValue WillChangePointList();
 
   void DidChangeLength(PRUint8 aAttrEnum, const nsAttrValue& aEmptyOrOldValue);
   void DidChangeNumber(PRUint8 aAttrEnum);
   void DidChangeNumberPair(PRUint8 aAttrEnum,
                            const nsAttrValue& aEmptyOrOldValue);
   void DidChangeInteger(PRUint8 aAttrEnum);
   void DidChangeIntegerPair(PRUint8 aAttrEnum,
                             const nsAttrValue& aEmptyOrOldValue);
@@ -190,17 +191,17 @@ public:
   void DidChangeBoolean(PRUint8 aAttrEnum);
   void DidChangeEnum(PRUint8 aAttrEnum);
   void DidChangeViewBox(const nsAttrValue& aEmptyOrOldValue);
   void DidChangePreserveAspectRatio(const nsAttrValue& aEmptyOrOldValue);
   void DidChangeNumberList(PRUint8 aAttrEnum,
                            const nsAttrValue& aEmptyOrOldValue);
   void DidChangeLengthList(PRUint8 aAttrEnum,
                            const nsAttrValue& aEmptyOrOldValue);
-  virtual void DidChangePointList(bool aDoSetAttr);
+  void DidChangePointList(const nsAttrValue& aEmptyOrOldValue);
   virtual void DidChangePathSegList(bool aDoSetAttr);
   virtual void DidChangeTransformList(bool aDoSetAttr);
   void DidChangeString(PRUint8 aAttrEnum) {}
   void DidChangeStringList(bool aIsConditionalProcessingAttribute,
                            PRUint8 aAttrEnum);
 
   virtual void DidAnimateLength(PRUint8 aAttrEnum);
   virtual void DidAnimateNumber(PRUint8 aAttrEnum);
--- a/content/svg/content/test/Makefile.in
+++ b/content/svg/content/test/Makefile.in
@@ -87,16 +87,17 @@ include $(topsrcdir)/config/rules.mk
 		animated-svg-image-helper.svg \
 		test_stroke-linecap-hit-testing.xhtml \
 		test_SVG_namespace_ids.html \
 		test_SVGLengthList.xhtml \
 		test_SVGLengthList-2.xhtml \
 		test_SVGMatrix.xhtml \
 		test_SVGNumberList.xhtml \
 		test_SVGPathSegList.xhtml \
+		test_SVGPointList.xhtml \
 		test_SVGStyleElement.xhtml \
 		test_SVGStringList.xhtml \
 		test_SVGTransformList.xhtml \
 		test_SVGTransformListAddition.xhtml \
 		test_SVGxxxList.xhtml \
 		test_SVGxxxListIndexing.xhtml \
 		test_switch.xhtml \
 		switch-helper.svg \
new file mode 100644
--- /dev/null
+++ b/content/svg/content/test/test_SVGPointList.xhtml
@@ -0,0 +1,64 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=629200
+-->
+<head>
+  <title>Tests specific to SVGPointList</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="MutationEventChecker.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=629200">Mozilla Bug 629200</a>
+<p id="display"></p>
+<div id="content" style="display:none;">
+<svg id="svg" xmlns="http://www.w3.org/2000/svg" width="100" height="100">
+  <polyline id="polyline" points="50,375 150,380"/>
+</svg>
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+<![CDATA[
+
+SimpleTest.waitForExplicitFinish();
+
+/*
+This file runs a series of SVGPointList specific tests. Generic SVGXxxList
+tests can be found in test_SVGxxxList.xhtml. Anything that can be generalized
+to other list types belongs there.
+*/
+
+function run_tests()
+{
+  document.getElementById('svg').pauseAnimations();
+
+  var polyline = document.getElementById("polyline");
+  var points = polyline.points;
+
+  is(points.numberOfItems, 2, 'Checking numberOfItems');
+
+  // Test mutation events
+  // --- Initialization
+  eventChecker = new MutationEventChecker;
+  eventChecker.watchAttr(polyline, "points");
+  // -- Actual changes
+  eventChecker.expect("modify modify");
+  points[0].x = 40;
+  polyline.setAttribute("points", "30,375 150,380");
+  // -- Redundant changes
+  eventChecker.expect("");
+  points[0].x = 30;
+  points[1].y = 380;
+  polyline.setAttribute("points", "30,375 150,380");
+  eventChecker.finish();
+
+  SimpleTest.finish();
+}
+
+window.addEventListener("load", run_tests, false);
+
+]]>
+</script>
+</pre>
+</body>
+</html>