merge mozilla-central with devtools
authorRob Campbell <rcampbell@mozilla.com>
Wed, 30 Mar 2011 07:50:53 -0300
changeset 64422 5d2fa64b8048f40e73c816b26fd7710954667b63
parent 64421 5867ed624b37f8a912748a82eb81dd264ded0af4 (current diff)
parent 64410 2f45c30741c574de4748202c2d72fca2c00a5c11 (diff)
child 64423 41a7fb6cb4f95efcc9e84cc0caf970a4dc9cdd96
push idunknown
push userunknown
push dateunknown
milestone2.2a1pre
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
merge mozilla-central with devtools
dom/interfaces/load-save/nsIDOMDOMImplementationLS.idl
dom/interfaces/load-save/nsIDOMLSException.idl
dom/interfaces/load-save/nsIDOMLSInput.idl
dom/interfaces/load-save/nsIDOMLSLoadEvent.idl
dom/interfaces/load-save/nsIDOMLSOutput.idl
dom/interfaces/load-save/nsIDOMLSParser.idl
dom/interfaces/load-save/nsIDOMLSParserFilter.idl
dom/interfaces/load-save/nsIDOMLSResourceResolver.idl
dom/interfaces/load-save/nsIDOMLSSerializer.idl
dom/interfaces/load-save/nsIDOMLSSerializerFilter.idl
intl/uconv/tests/unit/test_bug335531.js
intl/uconv/ucvlatin/nsUTF32ToUnicode.cpp
intl/uconv/ucvlatin/nsUTF32ToUnicode.h
intl/uconv/ucvlatin/nsUnicodeToUTF32.cpp
intl/uconv/ucvlatin/nsUnicodeToUTF32.h
jpeg/cdjpeg.c
jpeg/change.log
jpeg/cjpeg.c
jpeg/ckconfig.c
jpeg/coderules.doc
jpeg/djpeg.c
jpeg/example.c
jpeg/filelist.doc
jpeg/install.doc
jpeg/jconfig-mac-cw.h
jpeg/jconfig.doc
jpeg/jconfig.wat
jpeg/jmemansi.c
jpeg/jmemdos.c
jpeg/jmemdosa.asm
jpeg/jmemname.c
jpeg/jos2fig.h
jpeg/jwinfig.h
jpeg/libjpeg.doc
jpeg/makefile.gen
jpeg/netscape_mods.doc
jpeg/structure.doc
jpeg/usage.doc
jpeg/wizard.doc
js/src/jit-test/tests/basic/bug633890.js
js/src/tests/js1_8/extensions/regress-476871-01.js
--- a/accessible/src/base/nsAccUtils.h
+++ b/accessible/src/base/nsAccUtils.h
@@ -370,26 +370,16 @@ public:
   {
     PRUint32 role = Role(aAcc);
     return role != nsIAccessibleRole::ROLE_TEXT_LEAF &&
            role != nsIAccessibleRole::ROLE_WHITESPACE &&
            role != nsIAccessibleRole::ROLE_STATICTEXT;
   }
 
   /**
-   * Return true if the given accessible hasn't children.
-   */
-  static inline PRBool IsLeaf(nsIAccessible *aAcc)
-  {
-    PRInt32 numChildren = 0;
-    aAcc->GetChildCount(&numChildren);
-    return numChildren == 0;
-  }
-
-  /**
    * Return true if the given accessible can't have children. Used when exposing
    * to platform accessibility APIs, should the children be pruned off?
    */
   static PRBool MustPrune(nsIAccessible *aAccessible);
 
   /**
    * Search hint enum constants. Used by GetHeaderCellsFor() method.
    */
--- a/accessible/src/base/nsAccessibilityService.cpp
+++ b/accessible/src/base/nsAccessibilityService.cpp
@@ -95,16 +95,17 @@
 #endif
 
 #ifndef DISABLE_XFORMS_HOOKS
 #include "nsXFormsFormControlsAccessible.h"
 #include "nsXFormsWidgetsAccessible.h"
 #endif
 
 #include "mozilla/FunctionTimer.h"
+#include "mozilla/dom/Element.h"
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsAccessibilityService
 ////////////////////////////////////////////////////////////////////////////////
 
 nsAccessibilityService *nsAccessibilityService::gAccessibilityService = nsnull;
 PRBool nsAccessibilityService::gIsShutdown = PR_TRUE;
 
@@ -270,17 +271,17 @@ nsAccessibilityService::CreateHTMLImageA
   if (htmlDoc) {
     nsAutoString mapElmName;
     aContent->GetAttr(kNameSpaceID_None, nsAccessibilityAtoms::usemap,
                       mapElmName);
 
     if (!mapElmName.IsEmpty()) {
       if (mapElmName.CharAt(0) == '#')
         mapElmName.Cut(0,1);
-      mapElm = htmlDoc->GetImageMap(mapElmName);
+      mapElm = do_QueryInterface(htmlDoc->GetImageMap(mapElmName));
     }
   }
 
   nsCOMPtr<nsIWeakReference> weakShell(do_GetWeakReference(aPresShell));
   nsAccessible* accessible = mapElm ?
     new nsHTMLImageMapAccessible(aContent, weakShell, mapElm) :
     new nsHTMLImageAccessibleWrap(aContent, weakShell);
   NS_IF_ADDREF(accessible);
--- a/accessible/src/base/nsAccessible.cpp
+++ b/accessible/src/base/nsAccessible.cpp
@@ -760,70 +760,64 @@ nsAccessible::GetFocusedChild(nsIAccessi
       focusedChild = nsnull;
   }
 
   NS_IF_ADDREF(*aFocusedChild = focusedChild);
   return NS_OK;
 }
 
 // nsAccessible::GetChildAtPoint()
-nsresult
-nsAccessible::GetChildAtPoint(PRInt32 aX, PRInt32 aY, PRBool aDeepestChild,
-                              nsIAccessible **aChild)
+nsAccessible*
+nsAccessible::GetChildAtPoint(PRInt32 aX, PRInt32 aY,
+                              EWhichChildAtPoint aWhichChild)
 {
   // If we can't find the point in a child, we will return the fallback answer:
   // we return |this| if the point is within it, otherwise nsnull.
   PRInt32 x = 0, y = 0, width = 0, height = 0;
   nsresult rv = GetBounds(&x, &y, &width, &height);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  nsCOMPtr<nsIAccessible> fallbackAnswer;
+  NS_ENSURE_SUCCESS(rv, nsnull);
+
+  nsAccessible* fallbackAnswer = nsnull;
   if (aX >= x && aX < x + width && aY >= y && aY < y + height)
     fallbackAnswer = this;
 
-  if (nsAccUtils::MustPrune(this)) {  // Do not dig any further
-    NS_IF_ADDREF(*aChild = fallbackAnswer);
-    return NS_OK;
-  }
+  if (nsAccUtils::MustPrune(this))  // Do not dig any further
+    return fallbackAnswer;
 
   // Search an accessible at the given point starting from accessible document
   // because containing block (see CSS2) for out of flow element (for example,
   // absolutely positioned element) may be different from its DOM parent and
   // therefore accessible for containing block may be different from accessible
   // for DOM parent but GetFrameForPoint() should be called for containing block
   // to get an out of flow element.
   nsDocAccessible *accDocument = GetDocAccessible();
-  NS_ENSURE_TRUE(accDocument, NS_ERROR_FAILURE);
+  NS_ENSURE_TRUE(accDocument, nsnull);
 
   nsIFrame *frame = accDocument->GetFrame();
-  NS_ENSURE_STATE(frame);
+  NS_ENSURE_TRUE(frame, nsnull);
 
   nsPresContext *presContext = frame->PresContext();
 
   nsIntRect screenRect = frame->GetScreenRectExternal();
   nsPoint offset(presContext->DevPixelsToAppUnits(aX - screenRect.x),
                  presContext->DevPixelsToAppUnits(aY - screenRect.y));
 
   nsCOMPtr<nsIPresShell> presShell = presContext->PresShell();
   nsIFrame *foundFrame = presShell->GetFrameForPoint(frame, offset);
 
   nsIContent* content = nsnull;
-  if (!foundFrame || !(content = foundFrame->GetContent())) {
-    NS_IF_ADDREF(*aChild = fallbackAnswer);
-    return NS_OK;
-  }
+  if (!foundFrame || !(content = foundFrame->GetContent()))
+    return fallbackAnswer;
 
   // Get accessible for the node with the point or the first accessible in
   // the DOM parent chain.
   nsAccessible* accessible =
    GetAccService()->GetAccessibleOrContainer(content, mWeakShell);
-  if (!accessible) {
-    NS_IF_ADDREF(*aChild = fallbackAnswer);
-    return NS_OK;
-  }
+  if (!accessible)
+    return fallbackAnswer;
 
   if (accessible == this) {
     // Manually walk through accessible children and see if the are within this
     // point. Skip offscreen or invisible accessibles. This takes care of cases
     // where layout won't walk into things for us, such as image map areas and
     // sub documents (XXX: subdocuments should be handled by methods of
     // nsOuterDocAccessibles).
     PRInt32 childCount = GetChildCount();
@@ -831,78 +825,76 @@ nsAccessible::GetChildAtPoint(PRInt32 aX
       nsAccessible *child = GetChildAt(childIdx);
 
       PRInt32 childX, childY, childWidth, childHeight;
       child->GetBounds(&childX, &childY, &childWidth, &childHeight);
       if (aX >= childX && aX < childX + childWidth &&
           aY >= childY && aY < childY + childHeight &&
           (nsAccUtils::State(child) & nsIAccessibleStates::STATE_INVISIBLE) == 0) {
 
-        if (aDeepestChild)
-          return child->GetDeepestChildAtPoint(aX, aY, aChild);
-
-        NS_IF_ADDREF(*aChild = child);
-        return NS_OK;
+        if (aWhichChild == eDeepestChild)
+          return child->GetChildAtPoint(aX, aY, eDeepestChild);
+
+        return child;
       }
     }
 
     // The point is in this accessible but not in a child. We are allowed to
     // return |this| as the answer.
-    NS_IF_ADDREF(*aChild = accessible);
-    return NS_OK;
+    return accessible;
   }
 
   // Since DOM node of obtained accessible may be out of flow then we should
   // ensure obtained accessible is a child of this accessible.
-  nsCOMPtr<nsIAccessible> parent, child(accessible);
-  while (PR_TRUE) {
-    child->GetParent(getter_AddRefs(parent));
+  nsAccessible* child = accessible;
+  while (true) {
+    nsAccessible* parent = child->GetParent();
     if (!parent) {
       // Reached the top of the hierarchy. These bounds were inside an
       // accessible that is not a descendant of this one.
-      NS_IF_ADDREF(*aChild = fallbackAnswer);      
-      return NS_OK;
+      return fallbackAnswer;
     }
 
-    if (parent == this) {
-      NS_ADDREF(*aChild = (aDeepestChild ? accessible : child));
-      return NS_OK;
-    }
-    child.swap(parent);
+    if (parent == this)
+      return aWhichChild == eDeepestChild ? accessible : child;
+
+    child = parent;
   }
 
-  return NS_OK;
+  return nsnull;
 }
 
 // nsIAccessible getChildAtPoint(in long x, in long y)
 NS_IMETHODIMP
 nsAccessible::GetChildAtPoint(PRInt32 aX, PRInt32 aY,
                               nsIAccessible **aAccessible)
 {
   NS_ENSURE_ARG_POINTER(aAccessible);
   *aAccessible = nsnull;
 
   if (IsDefunct())
     return NS_ERROR_FAILURE;
 
-  return GetChildAtPoint(aX, aY, PR_FALSE, aAccessible);
+  NS_IF_ADDREF(*aAccessible = GetChildAtPoint(aX, aY, eDirectChild));
+  return NS_OK;
 }
 
 // nsIAccessible getDeepestChildAtPoint(in long x, in long y)
 NS_IMETHODIMP
 nsAccessible::GetDeepestChildAtPoint(PRInt32 aX, PRInt32 aY,
                                      nsIAccessible **aAccessible)
 {
   NS_ENSURE_ARG_POINTER(aAccessible);
   *aAccessible = nsnull;
 
   if (IsDefunct())
     return NS_ERROR_FAILURE;
 
-  return GetChildAtPoint(aX, aY, PR_TRUE, aAccessible);
+  NS_IF_ADDREF(*aAccessible = GetChildAtPoint(aX, aY, eDeepestChild));
+  return NS_OK;
 }
 
 void nsAccessible::GetBoundsRect(nsRect& aTotalBounds, nsIFrame** aBoundingFrame)
 {
 /*
  * This method is used to determine the bounds of a content node.
  * Because HTML wraps and links are not always rectangular, this
  * method uses the following algorithm:
@@ -2711,17 +2703,17 @@ nsAccessible::GetNameInternal(nsAString&
 void
 nsAccessible::BindToParent(nsAccessible* aParent, PRUint32 aIndexInParent)
 {
   NS_PRECONDITION(aParent, "This method isn't used to set null parent!");
 
   if (mParent) {
     if (mParent != aParent) {
       NS_ERROR("Adopting child!");
-      mParent->InvalidateChildren();
+      mParent->RemoveChild(this);
     } else {
       NS_ERROR("Binding to the same parent!");
       return;
     }
   }
 
   mParent = aParent;
   mIndexInParent = aIndexInParent;
--- a/accessible/src/base/nsAccessible.h
+++ b/accessible/src/base/nsAccessible.h
@@ -185,26 +185,33 @@ public:
 
   /**
    * Returns attributes for accessible without explicitly setted ARIA
    * attributes.
    */
   virtual nsresult GetAttributesInternal(nsIPersistentProperties *aAttributes);
 
   /**
+   * Used by GetChildAtPoint() method to get direct or deepest child at point.
+   */
+  enum EWhichChildAtPoint {
+    eDirectChild,
+    eDeepestChild
+  };
+
+  /**
    * Return direct or deepest child at the given point.
    *
-   * @param  aX             [in] x coordinate relative screen
-   * @param  aY             [in] y coordinate relative screen
-   * @param  aDeepestChild  [in] flag points if deep child should be returned
-   * @param  aChild         [out] found child
+   * @param  aX           [in] x coordinate relative screen
+   * @param  aY           [in] y coordinate relative screen
+   * @param  aWhichChild  [in] flag points if deepest or direct child
+   *                        should be returned
    */
-  virtual nsresult GetChildAtPoint(PRInt32 aX, PRInt32 aY,
-                                   PRBool aDeepestChild,
-                                   nsIAccessible **aChild);
+  virtual nsAccessible* GetChildAtPoint(PRInt32 aX, PRInt32 aY,
+                                        EWhichChildAtPoint aWhichChild);
 
   /**
    * Return calculated group level based on accessible hierarchy.
    */
   virtual PRInt32 GetLevelInternal();
 
   /**
    * Calculate position in group and group size ('posinset' and 'setsize') based
--- a/accessible/src/base/nsApplicationAccessible.cpp
+++ b/accessible/src/base/nsApplicationAccessible.cpp
@@ -165,32 +165,21 @@ nsApplicationAccessible::GroupPosition(P
   *aGroupLevel = 0;
   NS_ENSURE_ARG_POINTER(aSimilarItemsInGroup);
   *aSimilarItemsInGroup = 0;
   NS_ENSURE_ARG_POINTER(aPositionInGroup);
   *aPositionInGroup = 0;
   return NS_OK;
 }
 
-NS_IMETHODIMP
+nsAccessible*
 nsApplicationAccessible::GetChildAtPoint(PRInt32 aX, PRInt32 aY,
-                                         nsIAccessible **aChild)
+                                         EWhichChildAtPoint aWhichChild)
 {
-  NS_ENSURE_ARG_POINTER(aChild);
-  *aChild = nsnull;
-  return NS_ERROR_NOT_IMPLEMENTED;
-}
-
-NS_IMETHODIMP
-nsApplicationAccessible::GetDeepestChildAtPoint(PRInt32 aX, PRInt32 aY,
-                                                nsIAccessible **aChild)
-{
-  NS_ENSURE_ARG_POINTER(aChild);
-  *aChild = nsnull;
-  return NS_ERROR_NOT_IMPLEMENTED;
+  return nsnull;
 }
 
 NS_IMETHODIMP
 nsApplicationAccessible::GetRelationByType(PRUint32 aRelationType,
                                            nsIAccessibleRelation **aRelation)
 {
   NS_ENSURE_ARG_POINTER(aRelation);
   *aRelation = nsnull;
--- a/accessible/src/base/nsApplicationAccessible.h
+++ b/accessible/src/base/nsApplicationAccessible.h
@@ -93,18 +93,16 @@ public:
   NS_IMETHOD GetName(nsAString &aName);
   NS_IMETHOD GetValue(nsAString &aValue);
   NS_IMETHOD GetDescription(nsAString &aDescription);
   NS_IMETHOD GetKeyboardShortcut(nsAString &aKeyboardShortcut);
   NS_IMETHOD GetState(PRUint32 *aState , PRUint32 *aExtraState );
   NS_IMETHOD GetAttributes(nsIPersistentProperties **aAttributes);
   NS_IMETHOD GroupPosition(PRInt32 *aGroupLevel, PRInt32 *aSimilarItemsInGroup,
                            PRInt32 *aPositionInGroup);
-  NS_IMETHOD GetChildAtPoint(PRInt32 aX, PRInt32 aY, nsIAccessible **aChild);
-  NS_IMETHOD GetDeepestChildAtPoint(PRInt32 aX, PRInt32 aY, nsIAccessible **aChild);
   NS_IMETHOD GetRelationByType(PRUint32 aRelationType,
                                nsIAccessibleRelation **aRelation);
   NS_IMETHOD GetRelationsCount(PRUint32 *aRelationsCount);
   NS_IMETHOD GetRelation(PRUint32 aIndex, nsIAccessibleRelation **aRelation);
   NS_IMETHOD GetRelations(nsIArray **aRelations);
   NS_IMETHOD GetBounds(PRInt32 *aX, PRInt32 *aY,
                        PRInt32 *aWidth, PRInt32 *aHeight);
   NS_IMETHOD SetSelected(PRBool aIsSelected);
@@ -123,16 +121,18 @@ public:
   virtual PRBool Init();
   virtual void Shutdown();
   virtual bool IsPrimaryForNode() const;
 
   // nsAccessible
   virtual nsresult GetARIAState(PRUint32 *aState, PRUint32 *aExtraState);
   virtual PRUint32 NativeRole();
   virtual nsresult GetStateInternal(PRUint32 *aState, PRUint32 *aExtraState);
+  virtual nsAccessible* GetChildAtPoint(PRInt32 aX, PRInt32 aY,
+                                        EWhichChildAtPoint aWhichChild);
 
   virtual void InvalidateChildren();
 
 protected:
 
   // nsAccessible
   virtual void CacheChildren();
   virtual nsAccessible* GetSiblingAtOffset(PRInt32 aOffset,
--- a/accessible/src/base/nsBaseWidgetAccessible.cpp
+++ b/accessible/src/base/nsBaseWidgetAccessible.cpp
@@ -62,24 +62,22 @@ nsLeafAccessible::
 {
 }
 
 NS_IMPL_ISUPPORTS_INHERITED0(nsLeafAccessible, nsAccessible)
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsLeafAccessible: nsAccessible public
 
-nsresult
+nsAccessible*
 nsLeafAccessible::GetChildAtPoint(PRInt32 aX, PRInt32 aY,
-                                  PRBool aDeepestChild,
-                                  nsIAccessible **aChild)
+                                  EWhichChildAtPoint aWhichChild)
 {
   // Don't walk into leaf accessibles.
-  NS_ADDREF(*aChild = this);
-  return NS_OK;
+  return this;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsLeafAccessible: nsAccessible private
 
 void
 nsLeafAccessible::CacheChildren()
 {
--- a/accessible/src/base/nsBaseWidgetAccessible.h
+++ b/accessible/src/base/nsBaseWidgetAccessible.h
@@ -58,19 +58,18 @@ public:
   using nsAccessible::GetChildAtPoint;
 
   nsLeafAccessible(nsIContent *aContent, nsIWeakReference *aShell);
 
   // nsISupports
   NS_DECL_ISUPPORTS_INHERITED
 
   // nsAccessible
-  virtual nsresult GetChildAtPoint(PRInt32 aX, PRInt32 aY,
-                                   PRBool aDeepestChild,
-                                   nsIAccessible **aChild);
+  virtual nsAccessible* GetChildAtPoint(PRInt32 aX, PRInt32 aY,
+                                        EWhichChildAtPoint aWhichChild);
 
 protected:
 
   // nsAccessible
   virtual void CacheChildren();
 };
 
 /**
--- a/accessible/src/base/nsDocAccessible.cpp
+++ b/accessible/src/base/nsDocAccessible.cpp
@@ -1226,29 +1226,27 @@ nsDocAccessible::ARIAAttributeChanged(ns
 
 void nsDocAccessible::ContentAppended(nsIDocument *aDocument,
                                       nsIContent* aContainer,
                                       nsIContent* aFirstNewContent,
                                       PRInt32 /* unused */)
 {
 }
 
-void nsDocAccessible::ContentStatesChanged(nsIDocument* aDocument,
-                                           nsIContent* aContent1,
-                                           nsIContent* aContent2,
-                                           nsEventStates aStateMask)
+void nsDocAccessible::ContentStateChanged(nsIDocument* aDocument,
+                                          nsIContent* aContent,
+                                          nsEventStates aStateMask)
 {
   if (aStateMask.HasState(NS_EVENT_STATE_CHECKED)) {
-    nsHTMLSelectOptionAccessible::SelectionChangedIfOption(aContent1);
-    nsHTMLSelectOptionAccessible::SelectionChangedIfOption(aContent2);
+    nsHTMLSelectOptionAccessible::SelectionChangedIfOption(aContent);
   }
 
   if (aStateMask.HasState(NS_EVENT_STATE_INVALID)) {
     nsRefPtr<AccEvent> event =
-      new AccStateChangeEvent(aContent1, nsIAccessibleStates::STATE_INVALID,
+      new AccStateChangeEvent(aContent, nsIAccessibleStates::STATE_INVALID,
                               PR_FALSE, PR_TRUE);
     FireDelayedAccessibleEvent(event);
    }
 }
 
 void nsDocAccessible::DocumentStatesChanged(nsIDocument* aDocument,
                                             nsEventStates aStateMask)
 {
--- a/accessible/src/base/nsOuterDocAccessible.cpp
+++ b/accessible/src/base/nsOuterDocAccessible.cpp
@@ -71,42 +71,35 @@ nsOuterDocAccessible::GetStateInternal(P
 {
   nsresult rv = nsAccessible::GetStateInternal(aState, aExtraState);
   NS_ENSURE_A11Y_SUCCESS(rv, rv);
 
   *aState &= ~nsIAccessibleStates::STATE_FOCUSABLE;
   return NS_OK;
 }
 
-nsresult
+nsAccessible*
 nsOuterDocAccessible::GetChildAtPoint(PRInt32 aX, PRInt32 aY,
-                                      PRBool aDeepestChild,
-                                      nsIAccessible **aChild)
+                                      EWhichChildAtPoint aWhichChild)
 {
   PRInt32 docX = 0, docY = 0, docWidth = 0, docHeight = 0;
   nsresult rv = GetBounds(&docX, &docY, &docWidth, &docHeight);
-  NS_ENSURE_SUCCESS(rv, rv);
+  NS_ENSURE_SUCCESS(rv, nsnull);
 
   if (aX < docX || aX >= docX + docWidth || aY < docY || aY >= docY + docHeight)
-    return NS_OK;
+    return nsnull;
 
   // Always return the inner doc as direct child accessible unless bounds
   // outside of it.
-  nsCOMPtr<nsIAccessible> childAcc;
-  rv = GetFirstChild(getter_AddRefs(childAcc));
-  NS_ENSURE_SUCCESS(rv, rv);
+  nsAccessible* child = GetChildAt(0);
+  NS_ENSURE_TRUE(child, nsnull);
 
-  if (!childAcc)
-    return NS_OK;
-
-  if (aDeepestChild)
-    return childAcc->GetDeepestChildAtPoint(aX, aY, aChild);
-
-  NS_ADDREF(*aChild = childAcc);
-  return NS_OK;
+  if (aWhichChild = eDeepestChild)
+    return child->GetChildAtPoint(aX, aY, eDeepestChild);
+  return child;
 }
 
 nsresult
 nsOuterDocAccessible::GetAttributesInternal(nsIPersistentProperties *aAttributes)
 {
   nsAutoString tag;
   aAttributes->GetStringProperty(NS_LITERAL_CSTRING("tag"), tag);
   if (!tag.IsEmpty()) {
--- a/accessible/src/base/nsOuterDocAccessible.h
+++ b/accessible/src/base/nsOuterDocAccessible.h
@@ -65,19 +65,18 @@ public:
 
   // nsAccessNode
   virtual void Shutdown();
 
   // nsAccessible
   virtual PRUint32 NativeRole();
   virtual nsresult GetStateInternal(PRUint32 *aState, PRUint32 *aExtraState);
   virtual nsresult GetAttributesInternal(nsIPersistentProperties *aAttributes);
-  virtual nsresult GetChildAtPoint(PRInt32 aX, PRInt32 aY,
-                                   PRBool aDeepestChild,
-                                   nsIAccessible **aChild);
+  virtual nsAccessible* GetChildAtPoint(PRInt32 aX, PRInt32 aY,
+                                        EWhichChildAtPoint aWhichChild);
 
   virtual void InvalidateChildren();
   virtual PRBool AppendChild(nsAccessible *aAccessible);
   virtual PRBool RemoveChild(nsAccessible *aAccessible);
 
 protected:
   // nsAccessible
   virtual void CacheChildren();
--- a/accessible/src/html/nsHTMLImageMapAccessible.cpp
+++ b/accessible/src/html/nsHTMLImageMapAccessible.cpp
@@ -241,24 +241,22 @@ nsHTMLAreaAccessible::GetStateInternal(P
       mRoleMapEntry->role != nsIAccessibleRole::ROLE_NOTHING &&
       mRoleMapEntry->role != nsIAccessibleRole::ROLE_LINK) {
     return nsAccessible::GetStateInternal(aState,aExtraState);
   }
 
   return nsHTMLLinkAccessible::GetStateInternal(aState, aExtraState);
 }
 
-nsresult
+nsAccessible*
 nsHTMLAreaAccessible::GetChildAtPoint(PRInt32 aX, PRInt32 aY,
-                                      PRBool aDeepestChild,
-                                      nsIAccessible **aChild)
+                                      EWhichChildAtPoint aWhichChild)
 {
   // Don't walk into area accessibles.
-  NS_ADDREF(*aChild = this);
-  return NS_OK;
+  return this;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsHTMLImageMapAccessible: HyperLinkAccessible
 
 PRUint32
 nsHTMLAreaAccessible::StartOffset()
 {
--- a/accessible/src/html/nsHTMLImageMapAccessible.h
+++ b/accessible/src/html/nsHTMLImageMapAccessible.h
@@ -89,19 +89,18 @@ public:
   // nsIAccessible
   NS_IMETHOD GetDescription(nsAString& aDescription);
 
   NS_IMETHOD GetBounds(PRInt32 *x, PRInt32 *y, PRInt32 *width, PRInt32 *height);
 
   // nsAccessible
   virtual nsresult GetNameInternal(nsAString& aName);
   virtual nsresult GetStateInternal(PRUint32 *aState, PRUint32 *aExtraState);
-  virtual nsresult GetChildAtPoint(PRInt32 aX, PRInt32 aY,
-                                   PRBool aDeepestChild,
-                                   nsIAccessible **aChild);
+  virtual nsAccessible* GetChildAtPoint(PRInt32 aX, PRInt32 aY,
+                                        EWhichChildAtPoint aWhichChild);
 
   // HyperLinkAccessible
   virtual PRUint32 StartOffset();
   virtual PRUint32 EndOffset();
 
 protected:
 
   // nsAccessible
--- a/accessible/src/html/nsHyperTextAccessible.cpp
+++ b/accessible/src/html/nsHyperTextAccessible.cpp
@@ -190,21 +190,18 @@ nsHyperTextAccessible::GetStateInternal(
     if (0 == (flags & nsIPlaintextEditor::eEditorReadonlyMask)) {
       *aExtraState |= nsIAccessibleStates::EXT_STATE_EDITABLE;
     }
   } else if (mContent->Tag() == nsAccessibilityAtoms::article) {
     // We want <article> to behave like a document in terms of readonly state.
     *aState |= nsIAccessibleStates::STATE_READONLY;
   }
 
-  PRInt32 childCount;
-  GetChildCount(&childCount);
-  if (childCount > 0) {
+  if (GetChildCount() > 0)
     *aExtraState |= nsIAccessibleStates::EXT_STATE_SELECTABLE_TEXT;
-  }
 
   return NS_OK;
 }
 
 // Substring must be entirely within the same text node
 nsIntRect nsHyperTextAccessible::GetBoundsForString(nsIFrame *aFrame, PRUint32 aStartRenderedOffset,
                                                     PRUint32 aEndRenderedOffset)
 {
--- a/accessible/src/msaa/nsAccessibleWrap.cpp
+++ b/accessible/src/msaa/nsAccessibleWrap.cpp
@@ -231,19 +231,17 @@ STDMETHODIMP nsAccessibleWrap::get_accPa
 
 STDMETHODIMP nsAccessibleWrap::get_accChildCount( long __RPC_FAR *pcountChildren)
 {
 __try {
   *pcountChildren = 0;
   if (nsAccUtils::MustPrune(this))
     return NS_OK;
 
-  PRInt32 numChildren;
-  GetChildCount(&numChildren);
-  *pcountChildren = numChildren;
+  *pcountChildren = GetChildCount();
 } __except(FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
 
   return S_OK;
 }
 
 STDMETHODIMP nsAccessibleWrap::get_accChild(
       /* [in] */ VARIANT varChild,
       /* [retval][out] */ IDispatch __RPC_FAR *__RPC_FAR *ppdispChild)
@@ -1009,19 +1007,17 @@ STDMETHODIMP
 nsAccessibleWrap::Skip(ULONG aNumElements)
 {
 __try {
   if (mEnumVARIANTPosition == kIEnumVariantDisconnected)
     return CO_E_OBJNOTCONNECTED;
 
   mEnumVARIANTPosition += aNumElements;
 
-  PRInt32 numChildren;
-  GetChildCount(&numChildren);
-
+  PRInt32 numChildren = GetChildCount();
   if (mEnumVARIANTPosition > numChildren)
   {
     mEnumVARIANTPosition = numChildren;
     return S_FALSE;
   }
 } __except(nsAccessNodeWrap::FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
   return NOERROR;
 }
--- a/accessible/src/xul/nsXULTreeAccessible.cpp
+++ b/accessible/src/xul/nsXULTreeAccessible.cpp
@@ -223,59 +223,57 @@ nsXULTreeAccessible::GetFocusedChild(nsI
   }
 
   return NS_OK;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsXULTreeAccessible: nsAccessible implementation (DON'T put methods here)
 
-nsresult
+nsAccessible*
 nsXULTreeAccessible::GetChildAtPoint(PRInt32 aX, PRInt32 aY,
-                                     PRBool aDeepestChild,
-                                     nsIAccessible **aChild)
+                                     EWhichChildAtPoint aWhichChild)
 {
   nsIFrame *frame = GetFrame();
   if (!frame)
-    return NS_ERROR_FAILURE;
+    return nsnull;
 
   nsPresContext *presContext = frame->PresContext();
   nsCOMPtr<nsIPresShell> presShell = presContext->PresShell();
 
   nsIFrame *rootFrame = presShell->GetRootFrame();
-  NS_ENSURE_STATE(rootFrame);
+  NS_ENSURE_TRUE(rootFrame, nsnull);
 
   nsIntRect rootRect = rootFrame->GetScreenRectExternal();
 
   PRInt32 clientX = presContext->DevPixelsToIntCSSPixels(aX - rootRect.x);
   PRInt32 clientY = presContext->DevPixelsToIntCSSPixels(aY - rootRect.y);
 
   PRInt32 row = -1;
   nsCOMPtr<nsITreeColumn> column;
   nsCAutoString childEltUnused;
   mTree->GetCellAt(clientX, clientY, &row, getter_AddRefs(column),
                    childEltUnused);
 
   // If we failed to find tree cell for the given point then it might be
   // tree columns.
   if (row == -1 || !column)
-    return nsAccessibleWrap::GetChildAtPoint(aX, aY, aDeepestChild, aChild);
+    return nsAccessibleWrap::GetChildAtPoint(aX, aY, aWhichChild);
 
   nsAccessible *child = GetTreeItemAccessible(row);
-  if (aDeepestChild && child) {
+  if (aWhichChild == eDeepestChild && child) {
     // Look for accessible cell for the found item accessible.
     nsRefPtr<nsXULTreeItemAccessibleBase> treeitem = do_QueryObject(child);
 
     nsAccessible *cell = treeitem->GetCellAccessible(column);
     if (cell)
       child = cell;
   }
 
-  NS_IF_ADDREF(*aChild = child);
-  return NS_OK;
+  return child;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsXULTreeAccessible: SelectAccessible
 
 bool
 nsXULTreeAccessible::IsSelect()
 {
--- a/accessible/src/xul/nsXULTreeAccessible.h
+++ b/accessible/src/xul/nsXULTreeAccessible.h
@@ -81,19 +81,18 @@ public:
 
   // nsAccessNode
   virtual PRBool IsDefunct();
   virtual void Shutdown();
 
   // nsAccessible
   virtual PRUint32 NativeRole();
   virtual nsresult GetStateInternal(PRUint32 *aState, PRUint32 *aExtraState);
-  virtual nsresult GetChildAtPoint(PRInt32 aX, PRInt32 aY,
-                                   PRBool aDeepestChild,
-                                   nsIAccessible **aChild);
+  virtual nsAccessible* GetChildAtPoint(PRInt32 aX, PRInt32 aY,
+                                        EWhichChildAtPoint aWhichChild);
 
   virtual nsAccessible* GetChildAt(PRUint32 aIndex);
   virtual PRInt32 GetChildCount();
 
   // SelectAccessible
   virtual bool IsSelect();
   virtual already_AddRefed<nsIArray> SelectedItems();
   virtual PRUint32 SelectedItemCount();
--- a/accessible/src/xul/nsXULTreeGridAccessible.cpp
+++ b/accessible/src/xul/nsXULTreeGridAccessible.cpp
@@ -659,48 +659,46 @@ nsXULTreeGridRowAccessible::Shutdown()
 // nsXULTreeGridRowAccessible: nsAccessible implementation
 
 PRUint32
 nsXULTreeGridRowAccessible::NativeRole()
 {
   return nsIAccessibleRole::ROLE_ROW;
 }
 
-nsresult
+nsAccessible*
 nsXULTreeGridRowAccessible::GetChildAtPoint(PRInt32 aX, PRInt32 aY,
-                                            PRBool aDeepestChild,
-                                            nsIAccessible **aChild)
+                                            EWhichChildAtPoint aWhichChild)
 {
   nsIFrame *frame = GetFrame();
   if (!frame)
-    return NS_ERROR_FAILURE;
+    return nsnull;
 
   nsPresContext *presContext = frame->PresContext();
   nsCOMPtr<nsIPresShell> presShell = presContext->PresShell();
 
   nsIFrame *rootFrame = presShell->GetRootFrame();
-  NS_ENSURE_STATE(rootFrame);
+  NS_ENSURE_TRUE(rootFrame, nsnull);
 
   nsIntRect rootRect = rootFrame->GetScreenRectExternal();
 
   PRInt32 clientX = presContext->DevPixelsToIntCSSPixels(aX - rootRect.x);
   PRInt32 clientY = presContext->DevPixelsToIntCSSPixels(aY - rootRect.y);
 
   PRInt32 row = -1;
   nsCOMPtr<nsITreeColumn> column;
   nsCAutoString childEltUnused;
   mTree->GetCellAt(clientX, clientY, &row, getter_AddRefs(column),
                    childEltUnused);
 
   // Return if we failed to find tree cell in the row for the given point.
   if (row != mRow || !column)
-    return NS_OK;
+    return nsnull;
 
-  NS_IF_ADDREF(*aChild = GetCellAccessible(column));
-  return NS_OK;
+  return GetCellAccessible(column);
 }
 
 nsAccessible*
 nsXULTreeGridRowAccessible::GetChildAt(PRUint32 aIndex)
 {
   if (IsDefunct())
     return nsnull;
 
--- a/accessible/src/xul/nsXULTreeGridAccessible.h
+++ b/accessible/src/xul/nsXULTreeGridAccessible.h
@@ -88,19 +88,18 @@ public:
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(nsXULTreeGridRowAccessible,
                                            nsAccessible)
 
   // nsAccessNode
   virtual void Shutdown();
 
   // nsAccessible
   virtual PRUint32 NativeRole();
-  virtual nsresult GetChildAtPoint(PRInt32 aX, PRInt32 aY,
-                                   PRBool aDeepestChild,
-                                   nsIAccessible **aChild);
+  virtual nsAccessible* GetChildAtPoint(PRInt32 aX, PRInt32 aY,
+                                        EWhichChildAtPoint aWhichChild);
 
   virtual nsAccessible* GetChildAt(PRUint32 aIndex);
   virtual PRInt32 GetChildCount();
 
   // nsXULTreeItemAccessibleBase
   virtual nsAccessible* GetCellAccessible(nsITreeColumn *aColumn);
   virtual void RowInvalidated(PRInt32 aStartColIdx, PRInt32 aEndColIdx);
 
--- a/browser/base/content/test/Makefile.in
+++ b/browser/base/content/test/Makefile.in
@@ -200,16 +200,17 @@ endif
                  browser_pluginnotification.js \
                  browser_relatedTabs.js \
                  browser_sanitize-passwordDisabledHosts.js \
                  browser_sanitize-sitepermissions.js \
                  browser_sanitize-timespans.js \
                  browser_clearplugindata.js \
                  browser_clearplugindata.html \
                  browser_clearplugindata_noage.html \
+                 browser_popupUI.js \
                  browser_sanitizeDialog.js \
                  browser_scope.js \
                  browser_selectTabAtIndex.js \
                  browser_tab_dragdrop.js \
                  browser_tab_dragdrop2.js \
                  browser_tab_dragdrop2_frame1.xul \
                  browser_tabfocus.js \
                  browser_tabs_isActive.js \
@@ -244,19 +245,16 @@ endif
                  app_subframe_bug575561.html \
                  browser_contentAreaClick.js \
                  browser_addon_bar_close_button.js \
                  browser_addon_bar_shortcut.js \
                  browser_addon_bar_aomlistener.js \
                  test_bug628179.html \
                  $(NULL)
 
-# compartment-disabled
-#                 browser_popupUI.js \
-
 ifneq (cocoa,$(MOZ_WIDGET_TOOLKIT))
 _BROWSER_FILES += \
 		browser_bug462289.js \
 		$(NULL)
 else
 _BROWSER_FILES += \
 		browser_bug565667.js \
 		browser_customize.js \
--- a/browser/base/content/test/browser_popupUI.js
+++ b/browser/base/content/test/browser_popupUI.js
@@ -16,17 +16,17 @@ function test() {
     "data:text/html,<html><script>popup=open('about:blank','','width=300,height=200')</script>";
 }
 
 function findPopup() {
   var enumerator = Services.wm.getEnumerator("navigator:browser");
 
   while (enumerator.hasMoreElements()) {
     let win = enumerator.getNext();
-    if (win.content == content.wrappedJSObject.popup) {
+    if (win.content.wrappedJSObject == content.wrappedJSObject.popup) {
       testPopupUI(win);
       return;
     }
   }
 
   throw "couldn't find the popup";
 }
 
--- a/browser/components/preferences/advanced.xul
+++ b/browser/components/preferences/advanced.xul
@@ -122,19 +122,16 @@
                   name="security.disable_button.openCertManager"
                   type="bool"/>
       <preference id="security.OCSP.disable_button.managecrl"
                   name="security.OCSP.disable_button.managecrl"
                   type="bool"/>
       <preference id="security.disable_button.openDeviceManager"
                   name="security.disable_button.openDeviceManager"
                   type="bool"/>
-      <preference id="privacy.donottrackheader.enabled"
-                  name="privacy.donottrackheader.enabled"
-                  type="bool"/>
     </preferences>
     
 #ifdef HAVE_SHELL_SERVICE
     <stringbundle id="bundleShell" src="chrome://browser/locale/shellservice.properties"/>
     <stringbundle id="bundleBrand" src="chrome://branding/locale/brand.properties"/>
 #endif
 
     <script type="application/javascript" src="chrome://browser/content/preferences/advanced.js"/>
@@ -189,20 +186,16 @@
                       accesskey="&allowHWAccel.accesskey;"
                       preference="layers.acceleration.disabled"/>
             <checkbox id="checkSpelling"
                       label="&checkSpelling.label;"
                       accesskey="&checkSpelling.accesskey;"
                       onsyncfrompreference="return gAdvancedPane.readCheckSpelling();"
                       onsynctopreference="return gAdvancedPane.writeCheckSpelling();"
                       preference="layout.spellcheckDefault"/>
-            <checkbox id="privacyDoNotTrackPrefs"
-                      label="&doNotTrack.label;"
-                      accesskey="&doNotTrack.accesskey;"
-                      preference="privacy.donottrackheader.enabled"/>
           </groupbox>
 
 #ifdef HAVE_SHELL_SERVICE
           <!-- System Defaults -->
           <groupbox id="systemDefaultsGroup" orient="vertical">
             <caption label="&systemDefaults.label;"/>
 
             <hbox id="checkDefaultBox" align="center" flex="1">      
--- a/browser/components/preferences/privacy.xul
+++ b/browser/components/preferences/privacy.xul
@@ -52,16 +52,21 @@
          xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
          xmlns:html="http://www.w3.org/1999/xhtml">
 
   <prefpane id="panePrivacy"
             onpaneload="gPrivacyPane.init();"
             helpTopic="prefs-privacy">
 
     <preferences id="privacyPreferences">
+  
+      <!-- Tracking -->
+      <preference id="privacy.donottrackheader.enabled"
+                  name="privacy.donottrackheader.enabled"
+                  type="bool"/>
 
       <!-- XXX button prefs -->
       <preference id="pref.privacy.disable_button.cookie_exceptions"
                   name="pref.privacy.disable_button.cookie_exceptions"
                   type="bool"/>
       <preference id="pref.privacy.disable_button.view_cookies"
                   name="pref.privacy.disable_button.view_cookies"
                   type="bool"/>
@@ -111,16 +116,26 @@
                   type="bool"/>
 
     </preferences>
     
     <stringbundle id="bundlePreferences" src="chrome://browser/locale/preferences/preferences.properties"/>
     
     <script type="application/javascript" src="chrome://browser/content/preferences/privacy.js"/>
 
+    <!-- Tracking -->
+    <groupbox id="trackingGroup">
+      <caption label="&tracking.label;"/>
+
+      <checkbox id="privacyDoNotTrackPrefs"
+                label="&doNotTrack.label;"
+                accesskey="&doNotTrack.accesskey;"
+                preference="privacy.donottrackheader.enabled"/>
+    </groupbox>
+
     <!-- History -->
     <groupbox id="historyGroup">
       <caption label="&history.label;"/>
 
       <hbox align="center">
         <label id="historyModeLabel"
                control="historyMode"
                accesskey="&historyHeader.pre.accesskey;">&historyHeader.pre.label;</label>
--- a/browser/components/privatebrowsing/src/nsPrivateBrowsingService.js
+++ b/browser/components/privatebrowsing/src/nsPrivateBrowsingService.js
@@ -462,17 +462,17 @@ PrivateBrowsingService.prototype = {
         break;
     }
   },
 
   // nsICommandLineHandler
 
   handle: function PBS_handle(aCmdLine) {
     if (aCmdLine.handleFlag("private", false))
-      ; // It has already been handled
+      aCmdLine.preventDefault = true; // It has already been handled
     else if (aCmdLine.handleFlag("private-toggle", false)) {
       if (this._autoStarted) {
         throw Cr.NS_ERROR_ABORT;
       }
       this.privateBrowsingEnabled = !this.privateBrowsingEnabled;
       this._lastChangedByCommandLine = true;
     }
   },
--- a/browser/components/search/content/search.xml
+++ b/browser/components/search/content/search.xml
@@ -635,16 +635,22 @@
 
           // Add observer for suggest preference
           var ps2 = Components.classes["@mozilla.org/preferences-service;1"]
                               .getService(Components.interfaces.nsIPrefBranch2);
           ps2.addObserver("browser.search.suggest.enabled", this, false);
         ]]></body>
       </method>
 
+      <!--
+        This method overrides the autocomplete binding's openPopup (essentially
+        duplicating the logic from the autocomplete popup binding's
+        openAutocompletePopup method), modifying it so that the popup is aligned with
+        the inner textbox, but sized to not extend beyond the search bar border.
+      -->
       <method name="openPopup">
         <body><![CDATA[
           var popup = this.popup;
           if (!popup.mPopupOpen) {
             // Initially the panel used for the searchbar (PopupAutoComplete
             // in browser.xul) is hidden to avoid impacting startup / new
             // window performance. The base binding's openPopup would normally
             // call the overriden openAutocompletePopup in urlbarBindings.xml's
@@ -657,28 +663,36 @@
             popup.view = this.controller.QueryInterface(Components.interfaces.nsITreeView);
             popup.invalidate();
 
             popup.showCommentColumn = this.showCommentColumn;
             popup.showImageColumn = this.showImageColumn;
 
             document.popupNode = null;
 
+            const isRTL = getComputedStyle(this, "").direction == "rtl";
+
             var outerRect = this.getBoundingClientRect();
             var innerRect = this.inputField.getBoundingClientRect();
-            var width = outerRect.right - innerRect.left;
+            if (isRTL) {
+              var width = innerRect.right - outerRect.left;
+            } else {
+              var width = outerRect.right - innerRect.left;
+            }
             popup.setAttribute("width", width > 100 ? width : 100);
 
+            var yOffset = outerRect.bottom - innerRect.bottom;
+
             // setConsumeRollupEvent() before we call openPopup(), 
             // see bug #404438 for more details
             popup.popupBoxObject.setConsumeRollupEvent(
               this.consumeRollupEvent ? 
                 Ci.nsIPopupBoxObject.ROLLUP_CONSUME : 
                 Ci.nsIPopupBoxObject.ROLLUP_NO_CONSUME);
-            popup.openPopup(null, "", innerRect.left, outerRect.bottom, false, false);
+            popup.openPopup(this.inputField, "after_start", 0, yOffset, false, false);
           }
         ]]></body>
       </method>
 
       <method name="observe">
         <parameter name="aSubject"/>
         <parameter name="aTopic"/>
         <parameter name="aData"/>
--- a/browser/locales/en-US/chrome/browser/preferences/advanced.dtd
+++ b/browser/locales/en-US/chrome/browser/preferences/advanced.dtd
@@ -16,18 +16,16 @@
 <!ENTITY useAutoScroll.label             "Use autoscrolling">
 <!ENTITY useAutoScroll.accesskey         "a">
 <!ENTITY useSmoothScrolling.label        "Use smooth scrolling">
 <!ENTITY useSmoothScrolling.accesskey    "m">
 <!ENTITY allowHWAccel.label              "Use hardware acceleration when available">
 <!ENTITY allowHWAccel.accesskey          "h">
 <!ENTITY checkSpelling.label             "Check my spelling as I type">
 <!ENTITY checkSpelling.accesskey         "t">
-<!ENTITY doNotTrack.label                "Tell web sites I do not want to be tracked">
-<!ENTITY doNotTrack.accesskey            "d">
 
 <!ENTITY systemDefaults.label            "System Defaults">
 <!ENTITY alwaysCheckDefault.label        "Always check to see if &brandShortName; is the default browser on startup"><!--XXX-->
 <!ENTITY alwaysCheckDefault.accesskey    "w">
 <!ENTITY checkNow.label                  "Check Now">
 <!ENTITY checkNow.accesskey              "N">
 <!ENTITY submitCrashes.label             "Submit crash reports">
 <!ENTITY submitCrashes.accesskey         "S">
--- a/browser/locales/en-US/chrome/browser/preferences/privacy.dtd
+++ b/browser/locales/en-US/chrome/browser/preferences/privacy.dtd
@@ -1,8 +1,13 @@
+<!ENTITY tracking.label                 "Tracking">
+
+<!ENTITY doNotTrack.label               "Tell web sites I do not want to be tracked">
+<!ENTITY doNotTrack.accesskey           "d">
+
 <!ENTITY  history.label                 "History">
 
 <!ENTITY  locationBar.label             "Location Bar">
 
 <!ENTITY  locbar.pre.label              "When using the location bar, suggest:">
 <!ENTITY  locbar.pre.accessKey          "u">
 <!ENTITY  locbar.post.label             "">
 <!ENTITY  locbar.both.label             "History and Bookmarks">
@@ -25,17 +30,16 @@
 <!ENTITY  askEachTime.label             "ask me every time">
 
 <!ENTITY  cookieExceptions.label        "Exceptions…">
 <!ENTITY  cookieExceptions.accesskey    "E">
 
 <!ENTITY  showCookies.label             "Show Cookies…">
 <!ENTITY  showCookies.accesskey         "S">
 
-
 <!ENTITY  historyHeader.pre.label          "&brandShortName; will:">
 <!ENTITY  historyHeader.pre.accesskey      "w">
 <!ENTITY  historyHeader.remember.label     "Remember history">
 <!ENTITY  historyHeader.dontremember.label "Never remember history">
 <!ENTITY  historyHeader.custom.label       "Use custom settings for history">
 <!ENTITY  historyHeader.post.label         "">
 
 <!ENTITY  rememberDescription.label      "&brandShortName; will remember your browsing, download, form and search history, and keep cookies from Web sites you visit.">
--- a/caps/src/nsNullPrincipal.cpp
+++ b/caps/src/nsNullPrincipal.cpp
@@ -61,26 +61,26 @@ NS_IMPL_QUERY_INTERFACE2_CI(nsNullPrinci
 NS_IMPL_CI_INTERFACE_GETTER2(nsNullPrincipal,
                              nsIPrincipal,
                              nsISerializable)
 
 NS_IMETHODIMP_(nsrefcnt) 
 nsNullPrincipal::AddRef()
 {
   NS_PRECONDITION(PRInt32(mJSPrincipals.refcount) >= 0, "illegal refcnt");
-  nsrefcnt count = PR_AtomicIncrement((PRInt32 *)&mJSPrincipals.refcount);
+  nsrefcnt count = PR_ATOMIC_INCREMENT(&mJSPrincipals.refcount);
   NS_LOG_ADDREF(this, count, "nsNullPrincipal", sizeof(*this));
   return count;
 }
 
 NS_IMETHODIMP_(nsrefcnt)
 nsNullPrincipal::Release()
 {
   NS_PRECONDITION(0 != mJSPrincipals.refcount, "dup release");
-  nsrefcnt count = PR_AtomicDecrement((PRInt32 *)&mJSPrincipals.refcount);
+  nsrefcnt count = PR_ATOMIC_DECREMENT(&mJSPrincipals.refcount);
   NS_LOG_RELEASE(this, count, "nsNullPrincipal");
   if (count == 0) {
     delete this;
   }
 
   return count;
 }
 
--- a/caps/src/nsPrincipal.cpp
+++ b/caps/src/nsPrincipal.cpp
@@ -149,26 +149,26 @@ NS_IMPL_CI_INTERFACE_GETTER2(nsPrincipal
                              nsIPrincipal,
                              nsISerializable)
 
 NS_IMETHODIMP_(nsrefcnt)
 nsPrincipal::AddRef()
 {
   NS_PRECONDITION(PRInt32(mJSPrincipals.refcount) >= 0, "illegal refcnt");
   // XXXcaa does this need to be threadsafe?  See bug 143559.
-  nsrefcnt count = PR_AtomicIncrement((PRInt32 *)&mJSPrincipals.refcount);
+  nsrefcnt count = PR_ATOMIC_INCREMENT(&mJSPrincipals.refcount);
   NS_LOG_ADDREF(this, count, "nsPrincipal", sizeof(*this));
   return count;
 }
 
 NS_IMETHODIMP_(nsrefcnt)
 nsPrincipal::Release()
 {
   NS_PRECONDITION(0 != mJSPrincipals.refcount, "dup release");
-  nsrefcnt count = PR_AtomicDecrement((PRInt32 *)&mJSPrincipals.refcount);
+  nsrefcnt count = PR_ATOMIC_DECREMENT(&mJSPrincipals.refcount);
   NS_LOG_RELEASE(this, count, "nsPrincipal");
   if (count == 0) {
     delete this;
   }
 
   return count;
 }
 
--- a/caps/src/nsSystemPrincipal.cpp
+++ b/caps/src/nsSystemPrincipal.cpp
@@ -58,26 +58,26 @@ NS_IMPL_QUERY_INTERFACE2_CI(nsSystemPrin
 NS_IMPL_CI_INTERFACE_GETTER2(nsSystemPrincipal,
                              nsIPrincipal,
                              nsISerializable)
 
 NS_IMETHODIMP_(nsrefcnt) 
 nsSystemPrincipal::AddRef()
 {
   NS_PRECONDITION(PRInt32(mJSPrincipals.refcount) >= 0, "illegal refcnt");
-  nsrefcnt count = PR_AtomicIncrement((PRInt32 *)&mJSPrincipals.refcount);
+  nsrefcnt count = PR_ATOMIC_INCREMENT(&mJSPrincipals.refcount);
   NS_LOG_ADDREF(this, count, "nsSystemPrincipal", sizeof(*this));
   return count;
 }
 
 NS_IMETHODIMP_(nsrefcnt)
 nsSystemPrincipal::Release()
 {
   NS_PRECONDITION(0 != mJSPrincipals.refcount, "dup release");
-  nsrefcnt count = PR_AtomicDecrement((PRInt32 *)&mJSPrincipals.refcount);
+  nsrefcnt count = PR_ATOMIC_DECREMENT(&mJSPrincipals.refcount);
   NS_LOG_RELEASE(this, count, "nsSystemPrincipal");
   if (count == 0) {
     delete this;
   }
 
   return count;
 }
 
--- a/config/autoconf.mk.in
+++ b/config/autoconf.mk.in
@@ -163,16 +163,20 @@ MOZ_TREMOR = @MOZ_TREMOR@
 MOZ_WEBM = @MOZ_WEBM@
 VPX_AS = @VPX_AS@
 VPX_ASFLAGS = @VPX_ASFLAGS@
 VPX_DASH_C_FLAG = @VPX_DASH_C_FLAG@
 VPX_AS_CONVERSION = @VPX_AS_CONVERSION@
 VPX_ASM_SUFFIX = @VPX_ASM_SUFFIX@
 VPX_X86_ASM = @VPX_X86_ASM@
 VPX_ARM_ASM = @VPX_ARM_ASM@
+LIBJPEG_TURBO_AS = @LIBJPEG_TURBO_AS@
+LIBJPEG_TURBO_ASFLAGS = @LIBJPEG_TURBO_ASFLAGS@
+LIBJPEG_TURBO_X86_ASM = @LIBJPEG_TURBO_X86_ASM@
+LIBJPEG_TURBO_X64_ASM = @LIBJPEG_TURBO_X64_ASM@
 NS_PRINTING = @NS_PRINTING@
 MOZ_PDF_PRINTING = @MOZ_PDF_PRINTING@
 MOZ_CRASHREPORTER = @MOZ_CRASHREPORTER@
 MOZ_HELP_VIEWER = @MOZ_HELP_VIEWER@
 MOC= @MOC@
 MOZ_NSS_PATCH = @MOZ_NSS_PATCH@
 MOZ_WEBGL = @MOZ_WEBGL@
 MOZ_ANGLE = @MOZ_ANGLE@
new file mode 100755
--- /dev/null
+++ b/config/find_OOM_errors.py
@@ -0,0 +1,335 @@
+#!/usr/bin/env python
+
+usage = """%prog: A test for OOM conditions in the shell.
+
+%prog finds segfaults and other errors caused by incorrect handling of
+allocation during OOM (out-of-memory) conditions.
+"""
+
+help = """Check for regressions only. This runs a set of files with a known
+number of OOM errors (specified by REGRESSION_COUNT), and exits with a non-zero
+result if more or less errors are found. See js/src/Makefile.in for invocation.
+"""
+
+
+import hashlib
+import re
+import shlex
+import subprocess
+import sys
+import threading
+import time
+
+from optparse import OptionParser
+
+#####################################################################
+# Utility functions
+#####################################################################
+def run(args, stdin=None):
+  class ThreadWorker(threading.Thread):
+    def __init__(self, pipe):
+      super(ThreadWorker, self).__init__()
+      self.all = ""
+      self.pipe = pipe
+      self.setDaemon(True)
+
+    def run(self):
+      while True:
+        line = self.pipe.readline()
+        if line == '': break
+        else:
+          self.all += line
+
+  try:
+    if type(args) == str:
+      args = shlex.split(args)
+
+    args = [str(a) for a in args] # convert to strs
+
+    stdin_pipe = subprocess.PIPE if stdin else None
+    proc = subprocess.Popen(args, stdin=stdin_pipe, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+    if stdin_pipe:
+      proc.stdin.write(stdin)
+      proc.stdin.close()
+
+    stdout_worker = ThreadWorker(proc.stdout)
+    stderr_worker = ThreadWorker(proc.stderr)
+    stdout_worker.start()
+    stderr_worker.start()
+
+    proc.wait()
+    stdout_worker.join()
+    stderr_worker.join()
+
+  except KeyboardInterrupt, e:
+    sys.exit(-1)
+
+  stdout, stderr = stdout_worker.all, stderr_worker.all
+  result = (stdout, stderr, proc.returncode)
+  return result
+
+def get_js_files():
+  (out, err, exit) = run('find ../jit-test/tests -name "*.js"')
+  if (err, exit) == ("", 0):
+    sys.exit("Wrong directory, run from an objdir")
+  return out.split()
+
+
+
+#####################################################################
+# Blacklisting
+#####################################################################
+def in_blacklist(sig):
+  return sig in blacklist
+
+def add_to_blacklist(sig):
+  blacklist[sig] = blacklist.get(sig, 0)
+  blacklist[sig] += 1
+
+# How often is a particular lines important for this.
+def count_lines():
+  """Keep track of the amount of times individual lines occur, in order to
+     prioritize the errors which occur most frequently."""
+  counts = {}
+  for string,count in blacklist.items():
+    for line in string.split("\n"):
+      counts[line] = counts.get(line, 0) + count
+
+  lines = []
+  for k,v in counts.items():
+    lines.append("%6d: %s" % (v,k))
+
+  lines.sort()
+
+  countlog = file("../OOM_count_log", "w")
+  countlog.write("\n".join(lines))
+  countlog.flush()
+  countlog.close()
+
+
+#####################################################################
+# Output cleaning
+#####################################################################
+def clean_voutput(err):
+  # Skip what we can't reproduce
+  err = re.sub(r"^--\d+-- run: /usr/bin/dsymutil \"shell/js\"$", "", err, flags=re.MULTILINE)
+  err = re.sub(r"^==\d+==", "", err, flags=re.MULTILINE)
+  err = re.sub(r"^\*\*\d+\*\*", "", err, flags=re.MULTILINE)
+  err = re.sub(r"^\s+by 0x[0-9A-Fa-f]+: ", "by: ", err, flags=re.MULTILINE)
+  err = re.sub(r"^\s+at 0x[0-9A-Fa-f]+: ", "at: ", err, flags=re.MULTILINE)
+  err = re.sub(r"(^\s+Address 0x)[0-9A-Fa-f]+( is not stack'd)", r"\1\2", err, flags=re.MULTILINE)
+  err = re.sub(r"(^\s+Invalid write of size )\d+", r"\1x", err, flags=re.MULTILINE)
+  err = re.sub(r"(^\s+Invalid read of size )\d+", r"\1x", err, flags=re.MULTILINE)
+  err = re.sub(r"(^\s+Address 0x)[0-9A-Fa-f]+( is )\d+( bytes inside a block of size )[0-9,]+( free'd)", r"\1\2\3\4", err, flags=re.MULTILINE)
+
+  # Skip the repeating bit due to the segfault
+  lines = []
+  for l in err.split('\n'):
+    if l == " Process terminating with default action of signal 11 (SIGSEGV)":
+      break
+    lines.append(l)
+  err = '\n'.join(lines)
+
+  return err
+
+def remove_failed_allocation_backtraces(err):
+  lines = []
+
+  add = True
+  for l in err.split('\n'):
+
+    # Set start and end conditions for including text
+    if l == " The site of the failed allocation is:":
+      add = False
+    elif l[:2] not in ['by: ', 'at:']:
+      add = True
+
+    if add:
+      lines.append(l)
+
+
+  err = '\n'.join(lines)
+
+  return err
+
+
+def clean_output(err):
+  err = re.sub(r"^js\(\d+,0x[0-9a-f]+\) malloc: \*\*\* error for object 0x[0-9a-f]+: pointer being freed was not allocated\n\*\*\* set a breakppoint in malloc_error_break to debug\n$", "pointer being freed was not allocated", err, flags=re.MULTILINE)
+
+  return err
+
+
+#####################################################################
+# Consts, etc
+#####################################################################
+
+command_template = 'shell/js' \
+                 + ' -m -j -p' \
+                 + ' -e "const platform=\'darwin\'; const libdir=\'../jit-test/lib/\';"' \
+                 + ' -f ../jit-test/lib/prolog.js' \
+                 + ' -f %s'
+
+
+# Blacklists are things we don't want to see in our logs again (though we do
+# want to count them when they happen). Whitelists we do want to see in our
+# logs again, principally because the information we have isn't enough.
+
+blacklist = {}
+add_to_blacklist(r"('', '', 1)") # 1 means OOM if the shell hasn't launched yet.
+add_to_blacklist(r"('', 'out of memory\n', 1)")
+
+whitelist = set()
+whitelist.add(r"('', 'out of memory\n', -11)") # -11 means OOM
+whitelist.add(r"('', 'out of memory\nout of memory\n', -11)")
+
+
+
+#####################################################################
+# Program
+#####################################################################
+
+# Options
+parser = OptionParser(usage=usage)
+parser.add_option("-r", "--regression", action="store", metavar="REGRESSION_COUNT", help=help,
+                  type="int", dest="regression", default=0) # TODO: support a value of zero, eventually
+                  
+(OPTIONS, args) = parser.parse_args()
+
+
+if OPTIONS.regression:
+  # TODO: This should be expanded as we get a better hang of the OOM problems.
+  # For now, we'll just check that the number of OOMs in one short file does not
+  # increase.
+  files = ["../jit-test/tests/arguments/args-createontrace.js"]
+else:
+  files = get_js_files()
+
+  # Use a command-line arg to reduce the set of files
+  if len (args):
+    files = [f for f in files if f.find(args[0]) != -1]
+
+
+if OPTIONS.regression:
+  # Don't use a logfile, this is automated for tinderbox.
+  log = file("../OOM_log", "w")
+
+
+num_failures = 0
+for f in files:
+
+  # Run it once to establish boundaries
+  command = (command_template + ' -O') % (f)
+  out, err, exit = run(command)
+  max = re.match(".*OOM max count: (\d+).*", out, flags=re.DOTALL).groups()[0]
+  max = int(max)
+  
+  # OOMs don't recover well for the first 20 allocations or so.
+  # TODO: revisit this.
+  for i in range(20, max): 
+
+    if OPTIONS.regression == None:
+      print "Testing allocation %d/%d in %s" % (i,max,f)
+
+    command = (command_template + ' -A %d') % (f, i)
+    out, err, exit = run(command)
+
+    # Success (5 is SM's exit code for controlled errors)
+    if exit == 5 and err.find("out of memory") != -1:
+      continue
+
+    # Failure
+    else:
+
+      if OPTIONS.regression:
+        # Just count them
+        num_failures += 1
+        continue
+
+      #########################################################################
+      # The regression tests ends above. The rest of this is for running  the
+      # script manually.
+      #########################################################################
+
+      problem = str((out, err, exit))
+      if in_blacklist(problem) and problem not in whitelist:
+        add_to_blacklist(problem)
+        continue
+
+      add_to_blacklist(problem)
+
+
+      # Get valgrind output for a good stack trace
+      vcommand = "valgrind --dsymutil=yes -q --log-file=OOM_valgrind_log_file " + command
+      run(vcommand)
+      vout = file("OOM_valgrind_log_file").read()
+      vout = clean_voutput(vout)
+      sans_alloc_sites = remove_failed_allocation_backtraces(vout)
+
+      # Don't print duplicate information
+      if in_blacklist(sans_alloc_sites):
+        add_to_blacklist(sans_alloc_sites)
+        continue
+
+      add_to_blacklist(sans_alloc_sites)
+
+      log.write ("\n")
+      log.write ("\n")
+      log.write ("=========================================================================")
+      log.write ("\n")
+      log.write ("An allocation failure at\n\tallocation %d/%d in %s\n\tcauses problems (detected using bug 624094)" % (i, max, f))
+      log.write ("\n")
+      log.write ("\n")
+
+      log.write ("Command (from obj directory, using patch from bug 624094):\n  " + command)
+      log.write ("\n")
+      log.write ("\n")
+      log.write ("stdout, stderr, exitcode:\n  " + problem)
+      log.write ("\n")
+      log.write ("\n")
+
+      double_free = err.find("pointer being freed was not allocated") != -1
+      oom_detected = err.find("out of memory") != -1
+      multiple_oom_detected = err.find("out of memory\nout of memory") != -1
+      segfault_detected = exit == -11
+
+      log.write ("Diagnosis: ")
+      log.write ("\n")
+      if multiple_oom_detected:
+        log.write ("  - Multiple OOMs reported")
+        log.write ("\n")
+      if segfault_detected:
+        log.write ("  - segfault")
+        log.write ("\n")
+      if not oom_detected:
+        log.write ("  - No OOM checking")
+        log.write ("\n")
+      if double_free:
+        log.write ("  - Double free")
+        log.write ("\n")
+
+      log.write ("\n")
+
+      log.write ("Valgrind info:\n" + vout)
+      log.write ("\n")
+      log.write ("\n")
+      log.flush()
+
+  if not OPTIONS.regression == None:
+    count_lines()
+
+
+# Do the actual regression check
+if OPTIONS.regression:
+  expected_num_failures = OPTIONS.regression
+
+  if num_failures != expected_num_failures:
+
+    print "TEST-UNEXPECTED-FAIL |",
+    if num_failures > expected_num_failures:
+      print "More out-of-memory errors were found (%s) than expected (%d). This probably means an allocation site has been added without a NULL-check. If this is unavoidable, you can account for it by updating Makefile.in." % (num_failures, expected_num_failures),
+    else:
+      print "Congratulations, you have removed %d out-of-memory error(s) (%d remain)! Please account for it by updating Makefile.in." % (expected_num_failures - num_failures, num_failures),
+    sys.exit(-1)
+  else:
+    print 'TEST-PASS | find_OOM_errors | Found the expected number of OOM errors (%d)' % (expected_num_failures)
+
--- a/configure.in
+++ b/configure.in
@@ -4966,16 +4966,20 @@ MOZ_MEDIA=
 MOZ_WEBM=1
 VPX_AS=
 VPX_ASFLAGS=
 VPX_AS_DASH_C_FLAG=
 VPX_AS_CONVERSION=
 VPX_ASM_SUFFIX=
 VPX_X86_ASM=
 VPX_ARM_ASM=
+LIBJPEG_TURBO_AS=
+LIBJPEG_TURBO_ASFLAGS=
+LIBJPEG_TURBO_X86_ASM=
+LIBJPEG_TURBO_X64_ASM=
 MOZ_PANGO=1
 MOZ_PERMISSIONS=1
 MOZ_PLACES=1
 MOZ_PLUGINS=1
 MOZ_PREF_EXTENSIONS=1
 MOZ_PROFILELOCKING=1
 MOZ_PSM=1
 MOZ_RDF=1
@@ -6437,16 +6441,77 @@ MOZ_ARG_WITH_STRING(crashreporter-enable
     MOZ_CRASHREPORTER_ENABLE_PERCENT="$val"])
 
 if test -z "$MOZ_CRASHREPORTER_ENABLE_PERCENT"; then
    MOZ_CRASHREPORTER_ENABLE_PERCENT=100
 fi
 AC_DEFINE_UNQUOTED(MOZ_CRASHREPORTER_ENABLE_PERCENT, $MOZ_CRASHREPORTER_ENABLE_PERCENT)
 
 dnl ========================================================
+dnl = libjpeg-turbo configuration
+dnl ========================================================
+
+dnl Detect if we can use yasm to compile libjpeg-turbo's optimized assembly
+dnl files.
+AC_MSG_CHECKING([for YASM assembler])
+AC_CHECK_PROGS(LIBJPEG_TURBO_AS, yasm, "")
+
+dnl XXX jlebar -- need a yasm version check here.
+
+if test -n "LIBJPEG_TURBO_AS"; then
+
+  LIBJPEG_TURBO_AS="yasm"
+
+  dnl We have YASM; see if we support it on this platform.
+  case "$OS_ARCH:$OS_TEST" in
+  Linux:x86|Linux:i?86)
+    LIBJPEG_TURBO_ASFLAGS="-f elf32 -rnasm -pnasm -DPIC -DELF"
+    LIBJPEG_TURBO_X86_ASM=1
+  ;;
+  Linux:x86_64)
+    LIBJPEG_TURBO_ASFLAGS="-f elf64 -rnasm -pnasm -D__x86_64__ -DPIC -DELF"
+    LIBJPEG_TURBO_X64_ASM=1
+  ;;
+  SunOS:i?86)
+    LIBJPEG_TURBO_ASFLAGS="-f elf32 -rnasm -pnasm -DPIC -DELF"
+    LIBJPEG_TURBO_X86_ASM=1
+  ;;
+  SunOS:x86_64)
+    LIBJPEG_TURBO_ASFLAGS="-f elf64 -rnasm -pnasm -D__x86_64__ -DPIC -DELF"
+    LIBJPEG_TURBO_X64_ASM=1
+  ;;
+  Darwin:i?86)
+    LIBJPEG_TURBO_ASFLAGS="-f macho32 -rnasm -pnasm -DPIC -DMACHO"
+    LIBJPEG_TURBO_X86_ASM=1
+  ;;
+  Darwin:x86_64)
+    LIBJPEG_TURBO_ASFLAGS="-f macho64 -rnasm -pnasm -D__x86_64__ -DPIC -DMACHO"
+    LIBJPEG_TURBO_X64_ASM=1
+  ;;
+  WINNT:x86|WINNT:i?86)
+    LIBJPEG_TURBO_ASFLAGS="-f win32 -rnasm -pnasm -DPIC -DWIN32"
+    LIBJPEG_TURBO_X86_ASM=1
+  ;;
+  WINNT:x86_64)
+    LIBJPEG_TURBO_ASFLAGS="-f win64 -rnasm -pnasm -D__x86_64__ -DPIC -DWIN64"
+    LIBJPEG_TURBO_X64_ASM=1
+  ;;
+  esac
+
+fi # end have YASM
+
+if test -n "$LIBJPEG_TURBO_X86_ASM"; then
+  AC_DEFINE(LIBJPEG_TURBO_X86_ASM)
+elif test -n "$LIBJPEG_TURBO_X64_ASM"; then
+  AC_DEFINE(LIBJPEG_TURBO_X64_ASM)
+else
+  AC_MSG_WARN([No assembler or assembly support for libjpeg-turbo.  Using unoptimized C routines.])
+fi
+
+dnl ========================================================
 dnl = Enable compilation of specific extension modules
 dnl ========================================================
 
 MOZ_ARG_ENABLE_STRING(extensions,
 [  --enable-extensions     Enable extensions],
 [ for option in `echo $enableval | sed 's/,/ /g'`; do
     if test "$option" = "yes" -o "$option" = "all"; then
         AC_MSG_ERROR([--enable-extensions=$option is no longer supported.])
@@ -9204,16 +9269,20 @@ AC_SUBST(MOZ_OGG)
 AC_SUBST(MOZ_ALSA_LIBS)
 AC_SUBST(VPX_AS)
 AC_SUBST(VPX_ASFLAGS)
 AC_SUBST(VPX_DASH_C_FLAG)
 AC_SUBST(VPX_AS_CONVERSION)
 AC_SUBST(VPX_ASM_SUFFIX)
 AC_SUBST(VPX_X86_ASM)
 AC_SUBST(VPX_ARM_ASM)
+AC_SUBST(LIBJPEG_TURBO_AS)
+AC_SUBST(LIBJPEG_TURBO_ASFLAGS)
+AC_SUBST(LIBJPEG_TURBO_X86_ASM)
+AC_SUBST(LIBJPEG_TURBO_X64_ASM)
 
 if test "$USING_HCC"; then
    CC='${topsrcdir}/build/hcc'
    CC="$CC '$_OLDCC'"
    CXX='${topsrcdir}/build/hcpp'
    CXX="$CXX '$_OLDCXX'"
    AC_SUBST(CC)
    AC_SUBST(CXX)
--- a/content/base/public/nsContentUtils.h
+++ b/content/base/public/nsContentUtils.h
@@ -540,17 +540,17 @@ public:
    * @param aCharset the name of the charset; if empty, we assume UTF8
    */
   static nsresult ConvertStringFromCharset(const nsACString& aCharset,
                                            const nsACString& aInput,
                                            nsAString& aOutput);
 
   /**
    * Determine whether a buffer begins with a BOM for UTF-8, UTF-16LE,
-   * UTF-16BE, UTF-32LE, UTF-32BE.
+   * UTF-16BE
    *
    * @param aBuffer the buffer to check
    * @param aLength the length of the buffer
    * @param aCharset empty if not found
    * @return boolean indicating whether a BOM was detected.
    */
   static PRBool CheckForBOM(const unsigned char* aBuffer, PRUint32 aLength,
                             nsACString& aCharset, PRBool *bigEndian = nsnull);
--- a/content/base/public/nsIContent.h
+++ b/content/base/public/nsIContent.h
@@ -71,18 +71,18 @@ enum nsLinkState {
   eLinkState_Unknown    = 0,
   eLinkState_Unvisited  = 1,
   eLinkState_Visited    = 2,
   eLinkState_NotLink    = 3
 };
 
 // IID for the nsIContent interface
 #define NS_ICONTENT_IID       \
-{ 0x8331ca9f, 0x8717, 0x4ab4, \
-  { 0xad, 0x17, 0xb4, 0x9d, 0xdc, 0xe8, 0xb6, 0x77 } }
+{ 0x5788c9eb, 0x646a, 0x4285, \
+  { 0xa2, 0x8c, 0xde, 0x0d, 0x43, 0x6b, 0x47, 0x72 } }
 
 /**
  * A node of content in a document's content model. This interface
  * is supported by all content objects.
  */
 class nsIContent : public nsINode {
 public:
 #ifdef MOZILLA_INTERNAL_API
@@ -901,25 +901,25 @@ public:
   /*
    * Returns a new nsISMILAttr that allows the caller to animate the given
    * attribute on this element.
    *
    * The CALLER OWNS the result and is responsible for deleting it.
    */
   virtual nsISMILAttr* GetAnimatedAttr(PRInt32 aNamespaceID, nsIAtom* aName) = 0;
 
-   /**
-    * Get the SMIL override style for this content node.  This is a style
-    * declaration that is applied *after* the inline style, and it can be used
-    * e.g. to store animated style values.
-    *
-    * Note: This method is analogous to the 'GetStyle' method in
-    * nsGenericHTMLElement and nsStyledElement.
-    */
-  virtual nsresult GetSMILOverrideStyle(nsIDOMCSSStyleDeclaration** aStyle) = 0;
+  /**
+   * Get the SMIL override style for this content node.  This is a style
+   * declaration that is applied *after* the inline style, and it can be used
+   * e.g. to store animated style values.
+   *
+   * Note: This method is analogous to the 'GetStyle' method in
+   * nsGenericHTMLElement and nsStyledElement.
+   */
+  virtual nsIDOMCSSStyleDeclaration* GetSMILOverrideStyle() = 0;
 
   /**
    * Get the SMIL override style rule for this content node.  If the rule
    * hasn't been created (or if this nsIContent object doesn't support SMIL
    * override style), this method simply returns null.
    */
   virtual mozilla::css::StyleRule* GetSMILOverrideStyleRule() = 0;
 
--- a/content/base/public/nsIContentSerializer.h
+++ b/content/base/public/nsIContentSerializer.h
@@ -30,26 +30,30 @@
  * 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 ***** */
 
-#ifndef _nsIContentSerializer_h__
-#define _nsIContentSerializer_h__
+#ifndef nsIContentSerializer_h
+#define nsIContentSerializer_h
 
 #include "nsISupports.h"
 
 class nsIContent;
 class nsIDocument;
 class nsAString;
 
-/* starting interface:    nsIContentSerializer */
+namespace mozilla {
+namespace dom {
+class Element;
+} // namespace dom
+} // namespace mozilla
 
 #define NS_ICONTENTSERIALIZER_IID \
 { 0xb1ee32f2, 0xb8c4, 0x49b9, \
   { 0x93, 0xdf, 0xb6, 0xfa, 0xb5, 0xd5, 0x46, 0x88 } }
 
 class nsIContentSerializer : public nsISupports {
  public: 
 
@@ -72,21 +76,21 @@ class nsIContentSerializer : public nsIS
                                          nsAString& aStr) = 0;
 
   NS_IMETHOD AppendComment(nsIContent* aComment, PRInt32 aStartOffset,
                            PRInt32 aEndOffset, nsAString& aStr) = 0;
 
   NS_IMETHOD AppendDoctype(nsIContent *aDoctype,
                            nsAString& aStr) = 0;
 
-  NS_IMETHOD AppendElementStart(nsIContent *aElement,
-                                nsIContent *aOriginalElement,
+  NS_IMETHOD AppendElementStart(mozilla::dom::Element* aElement,
+                                mozilla::dom::Element* aOriginalElement,
                                 nsAString& aStr) = 0;
 
-  NS_IMETHOD AppendElementEnd(nsIContent *aElement,
+  NS_IMETHOD AppendElementEnd(mozilla::dom::Element* aElement,
                               nsAString& aStr) = 0;
 
   NS_IMETHOD Flush(nsAString& aStr) = 0;
 
   /**
    * Append any items in the beginning of the document that won't be 
    * serialized by other methods. XML declaration is the most likely 
    * thing this method can produce.
@@ -95,9 +99,9 @@ class nsIContentSerializer : public nsIS
                                  nsAString& aStr) = 0;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsIContentSerializer, NS_ICONTENTSERIALIZER_IID)
 
 #define NS_CONTENTSERIALIZER_CONTRACTID_PREFIX \
 "@mozilla.org/layout/contentserializer;1?mimetype="
 
-#endif /* __gen_nsIContentSerializer_h__ */
+#endif /* nsIContentSerializer_h */
--- a/content/base/public/nsIDocument.h
+++ b/content/base/public/nsIDocument.h
@@ -118,18 +118,18 @@ class Loader;
 namespace dom {
 class Link;
 class Element;
 } // namespace dom
 } // namespace mozilla
 
 
 #define NS_IDOCUMENT_IID      \
-{ 0xc38a7935, 0xc854, 0x4df7, \
-  { 0x8f, 0xd4, 0xa2, 0x6f, 0x0d, 0x27, 0x9f, 0x31 } }
+{ 0x2c6ad63f, 0xb7b9, 0x42f8, \
+ { 0xbd, 0xde, 0x76, 0x0a, 0x83, 0xe3, 0xb0, 0x49 } }
 
 // Flag for AddStyleSheet().
 #define NS_STYLESHEET_FROM_CATALOG                (1 << 0)
 
 // Document states
 
 // RTL locale: specific to the XUL localedir attribute
 #define NS_DOCUMENT_STATE_RTL_LOCALE              NS_DEFINE_EVENT_STATE_MACRO(0)
@@ -752,21 +752,19 @@ public:
   virtual void EndUpdate(nsUpdateType aUpdateType) = 0;
   virtual void BeginLoad() = 0;
   virtual void EndLoad() = 0;
 
   enum ReadyState { READYSTATE_UNINITIALIZED = 0, READYSTATE_LOADING = 1, READYSTATE_INTERACTIVE = 3, READYSTATE_COMPLETE = 4};
   virtual void SetReadyStateInternal(ReadyState rs) = 0;
   virtual ReadyState GetReadyStateEnum() = 0;
 
-  // notify that one or two content nodes changed state
-  // either may be nsnull, but not both
-  virtual void ContentStatesChanged(nsIContent* aContent1,
-                                    nsIContent* aContent2,
-                                    nsEventStates aStateMask) = 0;
+  // notify that a content node changed state
+  virtual void ContentStateChanged(nsIContent* aContent,
+                                   nsEventStates aStateMask) = 0;
 
   // Notify that a document state has changed.
   // This should only be called by callers whose state is also reflected in the
   // implementation of nsDocument::GetDocumentState.
   virtual void DocumentStatesChanged(nsEventStates aStateMask) = 0;
 
   // Observation hooks for style data to propagate notifications
   // to document observers
--- a/content/base/public/nsIDocumentObserver.h
+++ b/content/base/public/nsIDocumentObserver.h
@@ -44,18 +44,18 @@
 class nsIAtom;
 class nsIContent;
 class nsIStyleSheet;
 class nsIStyleRule;
 class nsString;
 class nsIDocument;
 
 #define NS_IDOCUMENT_OBSERVER_IID \
-{ 0x3d005225, 0x210f, 0x4b07, \
-  { 0xb1, 0xd9, 0x96, 0x02, 0x05, 0x74, 0xc4, 0x37 } }
+{ 0x900bc4bc, 0x8b6c, 0x4cba, \
+ { 0x82, 0xfa, 0x56, 0x8a, 0x80, 0xff, 0xfd, 0x3e } }
 
 typedef PRUint32 nsUpdateType;
 
 #define UPDATE_CONTENT_MODEL 0x00000001
 #define UPDATE_STYLE         0x00000002
 #define UPDATE_CONTENT_STATE 0x00000004
 #define UPDATE_ALL (UPDATE_CONTENT_MODEL | UPDATE_STYLE | UPDATE_CONTENT_STATE)
 
@@ -98,30 +98,22 @@ public:
    * no need to invoke this method directly).  The notification 
    * is passed to any IDocumentObservers. The notification is 
    * passed on to all of the document observers. <p>
    *
    * This notification is not sent when a piece of content is
    * added/removed from the document or the content itself changed 
    * (the other notifications are used for that).
    *
-   * The optional second content node is to allow optimization
-   * of the case where state moves from one node to another
-   * (as is likely for :focus and :hover)
-   *
-   * Either content node may be nsnull, but not both
-   *
    * @param aDocument The document being observed
-   * @param aContent1 the piece of content that changed
-   * @param aContent2 optional second piece of content that changed
+   * @param aContent the piece of content that changed
    */
-  virtual void ContentStatesChanged(nsIDocument* aDocument,
-                                    nsIContent* aContent1,
-                                    nsIContent* aContent2,
-                                    nsEventStates aStateMask) = 0;
+  virtual void ContentStateChanged(nsIDocument* aDocument,
+                                   nsIContent* aContent,
+                                   nsEventStates aStateMask) = 0;
 
   /**
    * Notification that the state of the document has changed.
    *
    * @param aDocument The document being observed
    * @param aStateMask the state that changed
    */
   virtual void DocumentStatesChanged(nsIDocument* aDocument,
@@ -242,21 +234,20 @@ NS_DEFINE_STATIC_IID_ACCESSOR(nsIDocumen
     virtual void EndUpdate(nsIDocument* aDocument, nsUpdateType aUpdateType);
 
 #define NS_DECL_NSIDOCUMENTOBSERVER_BEGINLOAD                                \
     virtual void BeginLoad(nsIDocument* aDocument);
 
 #define NS_DECL_NSIDOCUMENTOBSERVER_ENDLOAD                                  \
     virtual void EndLoad(nsIDocument* aDocument);
 
-#define NS_DECL_NSIDOCUMENTOBSERVER_CONTENTSTATESCHANGED                     \
-    virtual void ContentStatesChanged(nsIDocument* aDocument,                \
-                                      nsIContent* aContent1,                 \
-                                      nsIContent* aContent2,                 \
-                                      nsEventStates aStateMask);
+#define NS_DECL_NSIDOCUMENTOBSERVER_CONTENTSTATECHANGED                      \
+    virtual void ContentStateChanged(nsIDocument* aDocument,                 \
+                                     nsIContent* aContent,                   \
+                                     nsEventStates aStateMask);
 
 #define NS_DECL_NSIDOCUMENTOBSERVER_DOCUMENTSTATESCHANGED                    \
     virtual void DocumentStatesChanged(nsIDocument* aDocument,               \
                                        nsEventStates aStateMask);
 
 #define NS_DECL_NSIDOCUMENTOBSERVER_STYLESHEETADDED                          \
     virtual void StyleSheetAdded(nsIDocument* aDocument,                     \
                                  nsIStyleSheet* aStyleSheet,                 \
@@ -288,17 +279,17 @@ NS_DEFINE_STATIC_IID_ACCESSOR(nsIDocumen
                                   nsIStyleSheet* aStyleSheet,                \
                                   nsIStyleRule* aStyleRule);
 
 #define NS_DECL_NSIDOCUMENTOBSERVER                                          \
     NS_DECL_NSIDOCUMENTOBSERVER_BEGINUPDATE                                  \
     NS_DECL_NSIDOCUMENTOBSERVER_ENDUPDATE                                    \
     NS_DECL_NSIDOCUMENTOBSERVER_BEGINLOAD                                    \
     NS_DECL_NSIDOCUMENTOBSERVER_ENDLOAD                                      \
-    NS_DECL_NSIDOCUMENTOBSERVER_CONTENTSTATESCHANGED                         \
+    NS_DECL_NSIDOCUMENTOBSERVER_CONTENTSTATECHANGED                          \
     NS_DECL_NSIDOCUMENTOBSERVER_DOCUMENTSTATESCHANGED                        \
     NS_DECL_NSIDOCUMENTOBSERVER_STYLESHEETADDED                              \
     NS_DECL_NSIDOCUMENTOBSERVER_STYLESHEETREMOVED                            \
     NS_DECL_NSIDOCUMENTOBSERVER_STYLESHEETAPPLICABLESTATECHANGED             \
     NS_DECL_NSIDOCUMENTOBSERVER_STYLERULECHANGED                             \
     NS_DECL_NSIDOCUMENTOBSERVER_STYLERULEADDED                               \
     NS_DECL_NSIDOCUMENTOBSERVER_STYLERULEREMOVED                             \
     NS_DECL_NSIMUTATIONOBSERVER
@@ -322,19 +313,18 @@ void                                    
 }                                                                         \
 void                                                                      \
 _class::EndLoad(nsIDocument* aDocument)                                   \
 {                                                                         \
 }
 
 #define NS_IMPL_NSIDOCUMENTOBSERVER_STATE_STUB(_class)                    \
 void                                                                      \
-_class::ContentStatesChanged(nsIDocument* aDocument,                      \
-                             nsIContent* aContent1,                       \
-                             nsIContent* aContent2,                       \
+_class::ContentStateChanged(nsIDocument* aDocument,                       \
+                             nsIContent* aContent,                        \
                              nsEventStates aStateMask)                    \
 {                                                                         \
 }                                                                         \
                                                                           \
 void                                                                      \
 _class::DocumentStatesChanged(nsIDocument* aDocument,                     \
                               nsEventStates aStateMask)                   \
 {                                                                         \
--- a/content/base/src/Link.cpp
+++ b/content/base/src/Link.cpp
@@ -97,17 +97,17 @@ Link::SetLinkState(nsLinkState aState)
   nsIContent *content = Content();
   nsIDocument *doc = content->GetCurrentDoc();
   NS_ASSERTION(doc, "Registered but we have no document?!");
   nsEventStates newLinkState = LinkState();
   NS_ASSERTION(newLinkState == NS_EVENT_STATE_VISITED ||
                newLinkState == NS_EVENT_STATE_UNVISITED,
                "Unexpected state obtained from LinkState()!");
   mozAutoDocUpdate update(doc, UPDATE_CONTENT_STATE, PR_TRUE);
-  doc->ContentStatesChanged(content, nsnull, oldLinkState ^ newLinkState);
+  doc->ContentStateChanged(content, oldLinkState ^ newLinkState);
 }
 
 nsEventStates
 Link::LinkState() const
 {
   // We are a constant method, but we are just lazily doing things and have to
   // track that state.  Cast away that constness!
   Link *self = const_cast<Link *>(this);
@@ -488,17 +488,17 @@ Link::ResetLinkState(bool aNotify)
   // If aNotify is true, notify both of the visited-related states.  We have
   // to do that, because we might be racing with a response from history and
   // hence need to make sure that we get restyled whether we were visited or
   // not before.  In particular, we need to make sure that our LinkState() is
   // called so that we'll start a new history query as needed.
   if (aNotify && doc) {
     nsEventStates changedState = NS_EVENT_STATE_VISITED ^ NS_EVENT_STATE_UNVISITED;
     MOZ_AUTO_DOC_UPDATE(doc, UPDATE_STYLE, aNotify);
-    doc->ContentStatesChanged(content, nsnull, changedState);
+    doc->ContentStateChanged(content, changedState);
   }
 }
 
 void
 Link::UnregisterFromHistory()
 {
   // If we are not registered, we have nothing to do.
   if (!mRegistered) {
--- a/content/base/src/Makefile.in
+++ b/content/base/src/Makefile.in
@@ -146,16 +146,30 @@ CPPSRCS		= \
 		nsXMLNameSpaceMap.cpp \
 		Link.cpp \
 		nsFileDataProtocolHandler.cpp \
 		nsFrameMessageManager.cpp \
 		nsInProcessTabChildGlobal.cpp \
 		ThirdPartyUtil.cpp \
 		$(NULL)
 
+# Are we targeting x86-32 or x86-64?  If so, we want to include SSE2 code for
+# nsTextFragment.cpp
+ifneq (,$(INTEL_ARCHITECTURE))
+
+CPPSRCS += nsTextFragmentSSE2.cpp
+
+# gcc requires -msse2 for this file since it uses SSE2 intrinsics.  (See bug
+# 585538 comment 12.)
+ifdef GNU_CC
+nsTextFragmentSSE2.$(OBJ_SUFFIX): CXXFLAGS+=-msse2
+endif
+
+endif
+
 GQI_SRCS = contentbase.gqi
 
 # we don't want the shared lib, but we want to force the creation of a
 # static lib.
 FORCE_STATIC_LIB = 1
 
 EXTRA_COMPONENTS = \
 		$(srcdir)/nsBadCertHandler.js \
--- a/content/base/src/mozSanitizingSerializer.cpp
+++ b/content/base/src/mozSanitizingSerializer.cpp
@@ -58,18 +58,19 @@
 #include "nsContentUtils.h"
 #include "nsReadableUtils.h"
 #include "plstr.h"
 #include "nsIProperties.h"
 #include "nsUnicharUtils.h"
 #include "nsIURI.h"
 #include "nsNetUtil.h"
 #include "nsEscape.h"
+#include "mozilla/dom/Element.h"
 
-//#define DEBUG_BenB
+using namespace mozilla::dom;
 
 static inline PRUnichar* escape(const nsString& source)
 {
   return nsEscapeHTML2(source.get(), source.Length()); 
 }
 
 /* XXX: |printf|s in some error conditions. They are intended as information
    for the user, because they complain about malformed pref values.
@@ -236,18 +237,18 @@ mozSanitizingHTMLSerializer::AppendText(
 
   nsAutoString linebuffer;
   rv = DoAddLeaf(eHTMLTag_text, linebuffer);
 
   return rv;
 }
 
 NS_IMETHODIMP 
-mozSanitizingHTMLSerializer::AppendElementStart(nsIContent *aElement,
-                                                nsIContent *aOriginalElement,
+mozSanitizingHTMLSerializer::AppendElementStart(Element* aElement,
+                                                Element* aOriginalElement,
                                                 nsAString& aStr)
 {
   NS_ENSURE_ARG(aElement);
 
   mContent = aElement;
 
   mOutputString = &aStr;
 
@@ -265,17 +266,17 @@ mozSanitizingHTMLSerializer::AppendEleme
 
   mContent = 0;
   mOutputString = nsnull;
 
   return rv;
 } 
  
 NS_IMETHODIMP 
-mozSanitizingHTMLSerializer::AppendElementEnd(nsIContent *aElement,
+mozSanitizingHTMLSerializer::AppendElementEnd(Element* aElement,
                                               nsAString& aStr)
 {
   NS_ENSURE_ARG(aElement);
 
   mContent = aElement;
 
   mOutputString = &aStr;
 
--- a/content/base/src/mozSanitizingSerializer.h
+++ b/content/base/src/mozSanitizingSerializer.h
@@ -84,20 +84,21 @@ public:
                                          PRInt32 aEndOffset,
                                          nsAString& aStr)
                       { return NS_OK; }
   NS_IMETHOD AppendComment(nsIContent* aComment, PRInt32 aStartOffset,
                            PRInt32 aEndOffset, nsAString& aStr)
                       { return NS_OK; }
   NS_IMETHOD AppendDoctype(nsIContent *aDoctype, nsAString& aStr)
                       { return NS_OK; }
-  NS_IMETHOD AppendElementStart(nsIContent *aElement,
-                                nsIContent *aOriginalElement,
-                                nsAString& aStr); 
-  NS_IMETHOD AppendElementEnd(nsIContent *aElement, nsAString& aStr);
+  NS_IMETHOD AppendElementStart(mozilla::dom::Element* aElement,
+                                mozilla::dom::Element* aOriginalElement,
+                                nsAString& aStr);
+  NS_IMETHOD AppendElementEnd(mozilla::dom::Element* aElement,
+                              nsAString& aStr);
   NS_IMETHOD Flush(nsAString& aStr);
 
   NS_IMETHOD AppendDocumentStart(nsIDocument *aDocument,
                                  nsAString& aStr);
 
   // nsIContentSink
   NS_IMETHOD WillParse(void) { return NS_OK; }
   NS_IMETHOD WillInterrupt(void) { return NS_OK; }
@@ -145,17 +146,17 @@ protected:
   nsresult DoAddLeaf(PRInt32 aTag, const nsAString& aText);
   void Write(const nsAString& aString);
 
 protected:
   PRInt32                      mFlags;
   PRUint32                     mSkipLevel;
   nsHashtable                  mAllowedTags;
 
-  nsCOMPtr<nsIContent>         mContent;
+  nsRefPtr<mozilla::dom::Element> mContent;
   nsAString*                   mOutputString;
   nsIParserNode*               mParserNode;
   nsCOMPtr<nsIParserService>   mParserService;
 };
 
 nsresult
 NS_NewSanitizingHTMLSerializer(nsIContentSerializer** aSerializer);
 
--- a/content/base/src/nsContentUtils.cpp
+++ b/content/base/src/nsContentUtils.cpp
@@ -3529,34 +3529,16 @@ nsContentUtils::CheckForBOM(const unsign
   PRBool found = PR_TRUE;
   aCharset.Truncate();
   if (aLength >= 3 &&
       aBuffer[0] == 0xEF &&
       aBuffer[1] == 0xBB &&
       aBuffer[2] == 0xBF) {
     aCharset = "UTF-8";
   }
-  else if (aLength >= 4 &&
-           aBuffer[0] == 0x00 &&
-           aBuffer[1] == 0x00 &&
-           aBuffer[2] == 0xFE &&
-           aBuffer[3] == 0xFF) {
-    aCharset = "UTF-32";
-    if (bigEndian)
-      *bigEndian = PR_TRUE;
-  }
-  else if (aLength >= 4 &&
-           aBuffer[0] == 0xFF &&
-           aBuffer[1] == 0xFE &&
-           aBuffer[2] == 0x00 &&
-           aBuffer[3] == 0x00) {
-    aCharset = "UTF-32";
-    if (bigEndian)
-      *bigEndian = PR_FALSE;
-  }
   else if (aLength >= 2 &&
            aBuffer[0] == 0xFE && aBuffer[1] == 0xFF) {
     aCharset = "UTF-16";
     if (bigEndian)
       *bigEndian = PR_TRUE;
   }
   else if (aLength >= 2 &&
            aBuffer[0] == 0xFF && aBuffer[1] == 0xFE) {
--- a/content/base/src/nsDOMFileReader.cpp
+++ b/content/base/src/nsDOMFileReader.cpp
@@ -708,33 +708,21 @@ nsDOMFileReader::GuessCharset(const char
     NS_ENSURE_SUCCESS(rv, rv);
 
     rv = detector->Done();
     NS_ENSURE_SUCCESS(rv, rv);
 
     aCharset = mCharset;
   } else {
     // no charset detector available, check the BOM
-    unsigned char sniffBuf[4];
+    unsigned char sniffBuf[3];
     PRUint32 numRead = (aDataLen >= sizeof(sniffBuf) ? sizeof(sniffBuf) : aDataLen);
     memcpy(sniffBuf, aFileData, numRead);
 
-    if (numRead >= 4 &&
-        sniffBuf[0] == 0x00 &&
-        sniffBuf[1] == 0x00 &&
-        sniffBuf[2] == 0xfe &&
-        sniffBuf[3] == 0xff) {
-      aCharset = "UTF-32BE";
-    } else if (numRead >= 4 &&
-               sniffBuf[0] == 0xff &&
-               sniffBuf[1] == 0xfe &&
-               sniffBuf[2] == 0x00 &&
-               sniffBuf[3] == 0x00) {
-      aCharset = "UTF-32LE";
-    } else if (numRead >= 2 &&
+    if (numRead >= 2 &&
                sniffBuf[0] == 0xfe &&
                sniffBuf[1] == 0xff) {
       aCharset = "UTF-16BE";
     } else if (numRead >= 2 &&
                sniffBuf[0] == 0xff &&
                sniffBuf[1] == 0xfe) {
       aCharset = "UTF-16LE";
     } else if (numRead >= 3 &&
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -4261,21 +4261,20 @@ nsDocument::EndLoad()
       NS_NewRunnableMethod(this, &nsDocument::DispatchContentLoadedEvents);
     NS_DispatchToCurrentThread(ev);
   } else {
     DispatchContentLoadedEvents();
   }
 }
 
 void
-nsDocument::ContentStatesChanged(nsIContent* aContent1, nsIContent* aContent2,
-                                 nsEventStates aStateMask)
-{
-  NS_DOCUMENT_NOTIFY_OBSERVERS(ContentStatesChanged,
-                               (this, aContent1, aContent2, aStateMask));
+nsDocument::ContentStateChanged(nsIContent* aContent, nsEventStates aStateMask)
+{
+  NS_DOCUMENT_NOTIFY_OBSERVERS(ContentStateChanged,
+                               (this, aContent, aStateMask));
 }
 
 void
 nsDocument::DocumentStatesChanged(nsEventStates aStateMask)
 {
   // Invalidate our cached state.
   mGotDocumentState &= ~aStateMask;
   mDocumentState &= ~aStateMask;
--- a/content/base/src/nsDocument.h
+++ b/content/base/src/nsDocument.h
@@ -701,19 +701,18 @@ public:
   virtual void BeginUpdate(nsUpdateType aUpdateType);
   virtual void EndUpdate(nsUpdateType aUpdateType);
   virtual void BeginLoad();
   virtual void EndLoad();
 
   virtual void SetReadyStateInternal(ReadyState rs);
   virtual ReadyState GetReadyStateEnum();
 
-  virtual void ContentStatesChanged(nsIContent* aContent1,
-                                    nsIContent* aContent2,
-                                    nsEventStates aStateMask);
+  virtual void ContentStateChanged(nsIContent* aContent,
+                                   nsEventStates aStateMask);
   virtual void DocumentStatesChanged(nsEventStates aStateMask);
 
   virtual void StyleRuleChanged(nsIStyleSheet* aStyleSheet,
                                 nsIStyleRule* aOldStyleRule,
                                 nsIStyleRule* aNewStyleRule);
   virtual void StyleRuleAdded(nsIStyleSheet* aStyleSheet,
                               nsIStyleRule* aStyleRule);
   virtual void StyleRuleRemoved(nsIStyleSheet* aStyleSheet,
--- a/content/base/src/nsDocumentEncoder.cpp
+++ b/content/base/src/nsDocumentEncoder.cpp
@@ -417,17 +417,17 @@ nsDocumentEncoder::SerializeNodeStart(ns
 nsresult
 nsDocumentEncoder::SerializeNodeEnd(nsINode* aNode,
                                     nsAString& aStr)
 {
   if (!IsVisibleNode(aNode))
     return NS_OK;
 
   if (aNode->IsElement()) {
-    mSerializer->AppendElementEnd(static_cast<nsIContent*>(aNode), aStr);
+    mSerializer->AppendElementEnd(aNode->AsElement(), aStr);
   }
   return NS_OK;
 }
 
 nsresult
 nsDocumentEncoder::SerializeToStringRecursive(nsINode* aNode,
                                               nsAString& aStr,
                                               PRBool aDontSerializeRoot)
--- a/content/base/src/nsFrameMessageManager.cpp
+++ b/content/base/src/nsFrameMessageManager.cpp
@@ -622,19 +622,17 @@ nsFrameScriptExecutor::LoadFrameScriptIn
     nsContentUtils::ThreadJSContextStack()->Push(mCx);
     {
       // Need to scope JSAutoRequest to happen after Push but before Pop,
       // at least for now. See bug 584673.
       JSAutoRequest ar(mCx);
       JSObject* global = nsnull;
       mGlobal->GetJSObject(&global);
       if (global) {
-        JS_ExecuteScript(mCx, global,
-                         (JSScript*)JS_GetPrivate(mCx, holder->mObject),
-                         nsnull);
+        JS_ExecuteScript(mCx, global, holder->mObject, nsnull);
       }
     }
     JSContext* unused;
     nsContentUtils::ThreadJSContextStack()->Pop(&unused);
     return;
   }
 
   nsCString url = NS_ConvertUTF16toUTF8(aURL);
@@ -680,41 +678,37 @@ nsFrameScriptExecutor::LoadFrameScriptIn
       if (global) {
         JSPrincipals* jsprin = nsnull;
         mPrincipal->GetJSPrincipals(mCx, &jsprin);
         nsContentUtils::XPConnect()->FlagSystemFilenamePrefix(url.get(), PR_TRUE);
 
         uint32 oldopts = JS_GetOptions(mCx);
         JS_SetOptions(mCx, oldopts | JSOPTION_NO_SCRIPT_RVAL);
 
-        JSScript* script =
+        JSObject* scriptObj =
           JS_CompileUCScriptForPrincipals(mCx, nsnull, jsprin,
                                          (jschar*)dataString.get(),
                                           dataString.Length(),
                                           url.get(), 1);
 
         JS_SetOptions(mCx, oldopts);
 
-        if (script) {
-          JSObject* scriptObj = JS_NewScriptObject(mCx, script);
-          JS_AddObjectRoot(mCx, &scriptObj);
+        if (scriptObj) {
           nsCAutoString scheme;
           uri->GetScheme(scheme);
           // We don't cache data: scripts!
           if (!scheme.EqualsLiteral("data")) {
             nsFrameScriptExecutorJSObjectHolder* holder =
               new nsFrameScriptExecutorJSObjectHolder(scriptObj);
             // Root the object also for caching.
             JS_AddNamedObjectRoot(mCx, &(holder->mObject),
                                   "Cached message manager script");
             sCachedScripts->Put(aURL, holder);
           }
-          JS_ExecuteScript(mCx, global,
-                           (JSScript*)JS_GetPrivate(mCx, scriptObj), nsnull);
-          JS_RemoveObjectRoot(mCx, &scriptObj);
+          JS_ExecuteScript(mCx, global, scriptObj, nsnull);
         }
         //XXX Argh, JSPrincipals are manually refcounted!
         JSPRINCIPALS_DROP(mCx, jsprin);
       }
     } 
     JSContext* unused;
     nsContentUtils::ThreadJSContextStack()->Pop(&unused);
   }
--- a/content/base/src/nsGenericDOMDataNode.cpp
+++ b/content/base/src/nsGenericDOMDataNode.cpp
@@ -1110,21 +1110,20 @@ nsGenericDOMDataNode::DoGetClasses() con
 
 NS_IMETHODIMP
 nsGenericDOMDataNode::WalkContentStyleRules(nsRuleWalker* aRuleWalker)
 {
   return NS_OK;
 }
 
 #ifdef MOZ_SMIL
-nsresult
-nsGenericDOMDataNode::GetSMILOverrideStyle(nsIDOMCSSStyleDeclaration** aStyle)
+nsIDOMCSSStyleDeclaration*
+nsGenericDOMDataNode::GetSMILOverrideStyle()
 {
-  *aStyle = nsnull;
-  return NS_ERROR_NOT_IMPLEMENTED;
+  return nsnull;
 }
 
 css::StyleRule*
 nsGenericDOMDataNode::GetSMILOverrideStyleRule()
 {
   return nsnull;
 }
 
--- a/content/base/src/nsGenericDOMDataNode.h
+++ b/content/base/src/nsGenericDOMDataNode.h
@@ -229,17 +229,17 @@ public:
   virtual void DestroyContent();
   virtual void SaveSubtreeState();
 
 #ifdef MOZ_SMIL
   virtual nsISMILAttr* GetAnimatedAttr(PRInt32 /*aNamespaceID*/, nsIAtom* /*aName*/)
   {
     return nsnull;
   }
-  virtual nsresult GetSMILOverrideStyle(nsIDOMCSSStyleDeclaration** aStyle);
+  virtual nsIDOMCSSStyleDeclaration* GetSMILOverrideStyle();
   virtual mozilla::css::StyleRule* GetSMILOverrideStyleRule();
   virtual nsresult SetSMILOverrideStyleRule(mozilla::css::StyleRule* aStyleRule,
                                             PRBool aNotify);
 #endif // MOZ_SMIL
 
 #ifdef DEBUG
   virtual void List(FILE* out, PRInt32 aIndent) const;
   virtual void DumpContent(FILE* out, PRInt32 aIndent, PRBool aDumpAll) const;
--- a/content/base/src/nsGenericElement.cpp
+++ b/content/base/src/nsGenericElement.cpp
@@ -3344,29 +3344,26 @@ nsGenericElement::DoGetClasses() const
 
 NS_IMETHODIMP
 nsGenericElement::WalkContentStyleRules(nsRuleWalker* aRuleWalker)
 {
   return NS_OK;
 }
 
 #ifdef MOZ_SMIL
-nsresult
-nsGenericElement::GetSMILOverrideStyle(nsIDOMCSSStyleDeclaration** aStyle)
+nsIDOMCSSStyleDeclaration*
+nsGenericElement::GetSMILOverrideStyle()
 {
   nsGenericElement::nsDOMSlots *slots = DOMSlots();
 
   if (!slots->mSMILOverrideStyle) {
     slots->mSMILOverrideStyle = new nsDOMCSSAttributeDeclaration(this, PR_TRUE);
-    NS_ENSURE_TRUE(slots->mSMILOverrideStyle, NS_ERROR_OUT_OF_MEMORY);
-  }
-
-  // Why bother with QI?
-  NS_ADDREF(*aStyle = slots->mSMILOverrideStyle);
-  return NS_OK;
+  }
+
+  return slots->mSMILOverrideStyle;
 }
 
 css::StyleRule*
 nsGenericElement::GetSMILOverrideStyleRule()
 {
   nsGenericElement::nsDOMSlots *slots = GetExistingDOMSlots();
   return slots ? slots->mSMILOverrideStyleRule.get() : nsnull;
 }
@@ -4761,17 +4758,17 @@ nsGenericElement::SetAttrAndNotify(PRInt
       }
     }
   }
 
   if (aNotify) {
     stateMask ^= IntrinsicState();
     if (document && !stateMask.IsEmpty()) {
       MOZ_AUTO_DOC_UPDATE(document, UPDATE_CONTENT_STATE, aNotify);
-      document->ContentStatesChanged(this, nsnull, stateMask);
+      document->ContentStateChanged(this, stateMask);
     }
     nsNodeUtils::AttributeChanged(this, aNamespaceID, aName, aModType);
   }
 
   if (aNamespaceID == kNameSpaceID_XMLEvents && 
       aName == nsGkAtoms::event && mNodeInfo->GetDocument()) {
     mNodeInfo->GetDocument()->AddXMLEventsContent(this);
   }
@@ -5008,17 +5005,17 @@ nsGenericElement::UnsetAttr(PRInt32 aNam
       }
     }
   }
 
   if (aNotify) {
     stateMask ^= IntrinsicState();
     if (document && !stateMask.IsEmpty()) {
       MOZ_AUTO_DOC_UPDATE(document, UPDATE_CONTENT_STATE, aNotify);
-      document->ContentStatesChanged(this, nsnull, stateMask);
+      document->ContentStateChanged(this, stateMask);
     }
     nsNodeUtils::AttributeChanged(this, aNameSpaceID, aName,
                                   nsIDOMMutationEvent::REMOVAL);
   }
 
   rv = AfterSetAttr(aNameSpaceID, aName, nsnull, aNotify);
   NS_ENSURE_SUCCESS(rv, rv);
 
@@ -5481,18 +5478,17 @@ nsGenericElement::GetLinkTarget(nsAStrin
 }
 
 // NOTE: The aPresContext pointer is NOT addrefed.
 // *aSelectorList might be null even if NS_OK is returned; this
 // happens when all the selectors were pseudo-element selectors.
 static nsresult
 ParseSelectorList(nsINode* aNode,
                   const nsAString& aSelectorString,
-                  nsCSSSelectorList** aSelectorList,
-                  nsPresContext** aPresContext)
+                  nsCSSSelectorList** aSelectorList)
 {
   NS_ENSURE_ARG(aNode);
 
   nsIDocument* doc = aNode->GetOwnerDoc();
   NS_ENSURE_STATE(doc);
 
   nsCSSParser parser(doc->CSSLoader());
   NS_ENSURE_TRUE(parser, NS_ERROR_OUT_OF_MEMORY);
@@ -5513,195 +5509,96 @@ ParseSelectorList(nsINode* aNode,
       cur->mNext = nsnull;
       delete cur;
     } else {
       slot = &cur->mNext;
     }
   } while (*slot);
   *aSelectorList = selectorList;
 
-  // It's not strictly necessary to have a prescontext here, but it's
-  // a bit of an optimization for various stuff.
-  *aPresContext = nsnull;
-  nsIPresShell* shell = doc->GetShell();
-  if (shell) {
-    *aPresContext = shell->GetPresContext();
-  }
-
   return NS_OK;
 }
 
-/*
- * Callback to be called as we iterate over the tree and match elements.  If
- * the callbacks returns false, the iteration should be stopped.
- */
-typedef PRBool
-(* ElementMatchedCallback)(nsIContent* aMatchingElement, void* aClosure);
-
-// returning false means stop iteration
-static PRBool
-TryMatchingElementsInSubtree(nsINode* aRoot,
-                             RuleProcessorData* aParentData,
-                             nsPresContext* aPresContext,
-                             nsCSSSelectorList* aSelectorList,
-                             ElementMatchedCallback aCallback,
-                             void* aClosure)
-{
-  /* To improve the performance of '+' and '~' combinators and the :nth-*
-   * selectors, we keep track of the immediately previous sibling data.  That's
-   * cheaper than heap-allocating all the datas and keeping track of them all,
-   * and helps a good bit in the common cases.  We also keep track of the whole
-   * parent data chain, since we have those Around anyway */
-  union { char c[2 * sizeof(RuleProcessorData)]; void *p; } databuf;
-  RuleProcessorData* prevSibling = nsnull;
-  RuleProcessorData* data = reinterpret_cast<RuleProcessorData*>(databuf.c);
-
-  PRBool continueIteration = PR_TRUE;
-  for (nsINode::ChildIterator iter(aRoot); !iter.IsDone(); iter.Next()) {
-    nsIContent* kid = iter;
-    if (!kid->IsElement()) {
-      continue;
-    }
-    /* See whether we match */
-    new (data) RuleProcessorData(aPresContext, kid->AsElement(), nsnull);
-    NS_ASSERTION(!data->mParentData, "Shouldn't happen");
-    NS_ASSERTION(!data->mPreviousSiblingData, "Shouldn't happen");
-    data->mParentData = aParentData;
-    data->mPreviousSiblingData = prevSibling;
-
-    if (nsCSSRuleProcessor::SelectorListMatches(*data, aSelectorList)) {
-      continueIteration = (*aCallback)(kid, aClosure);
-    }
-
-    if (continueIteration) {
-      continueIteration =
-        TryMatchingElementsInSubtree(kid, data, aPresContext, aSelectorList,
-                                     aCallback, aClosure);
-    }
-    
-    /* Clear out the parent and previous sibling data if we set them, so that
-     * ~RuleProcessorData won't try to delete a placement-new'd object. Make
-     * sure this happens before our possible early break.  Note that we can
-     * have null aParentData but non-null data->mParentData if we're scoped to
-     * an element.  However, prevSibling and data->mPreviousSiblingData must
-     * always match.
-     */
-    NS_ASSERTION(!aParentData || data->mParentData == aParentData,
-                 "Unexpected parent");
-    NS_ASSERTION(data->mPreviousSiblingData == prevSibling,
-                 "Unexpected prev sibling");
-    data->mPreviousSiblingData = nsnull;
-    if (prevSibling) {
-      if (aParentData) {
-        prevSibling->mParentData = nsnull;
-      }
-      prevSibling->~RuleProcessorData();
-    } else {
-      /* This is the first time through, so point |prevSibling| to the location
-         we want to have |data| end up pointing to. */
-      prevSibling = data + 1;
-    }
-
-    /* Now swap |prevSibling| and |data|.  Again, before the early break */
-    RuleProcessorData* temp = prevSibling;
-    prevSibling = data;
-    data = temp;
-    if (!continueIteration) {
-      break;
-    }
-  }
-  if (prevSibling) {
-    if (aParentData) {
-      prevSibling->mParentData = nsnull;
-    }
-    /* Make sure to clean this up */
-    prevSibling->~RuleProcessorData();
-  }
-
-  return continueIteration;
-}
-
-static PRBool
-FindFirstMatchingElement(nsIContent* aMatchingElement,
-                         void* aClosure)
-{
-  NS_PRECONDITION(aMatchingElement && aClosure, "How did that happen?");
-  nsIContent** slot = static_cast<nsIContent**>(aClosure);
-  *slot = aMatchingElement;
-  return PR_FALSE;
-}
-
 /* static */
 nsIContent*
 nsGenericElement::doQuerySelector(nsINode* aRoot, const nsAString& aSelector,
                                   nsresult *aResult)
 {
   NS_PRECONDITION(aResult, "Null out param?");
 
   nsAutoPtr<nsCSSSelectorList> selectorList;
-  nsPresContext* presContext;
   *aResult = ParseSelectorList(aRoot, aSelector,
-                               getter_Transfers(selectorList),
-                               &presContext);
+                               getter_Transfers(selectorList));
   NS_ENSURE_SUCCESS(*aResult, nsnull);
 
-  nsIContent* foundElement = nsnull;
-  TryMatchingElementsInSubtree(aRoot, nsnull, presContext, selectorList,
-                               FindFirstMatchingElement, &foundElement);
-
-  return foundElement;
-}
-
-static PRBool
-AppendAllMatchingElements(nsIContent* aMatchingElement,
-                          void* aClosure)
-{
-  NS_PRECONDITION(aMatchingElement && aClosure, "How did that happen?");
-  static_cast<nsBaseContentList*>(aClosure)->AppendElement(aMatchingElement);
-  return PR_TRUE;
+  TreeMatchContext matchingContext(PR_FALSE,
+                                   nsRuleWalker::eRelevantLinkUnvisited,
+                                   aRoot->GetOwnerDoc());
+  for (nsIContent* cur = aRoot->GetFirstChild();
+       cur;
+       cur = cur->GetNextNode(aRoot)) {
+    if (cur->IsElement() &&
+        nsCSSRuleProcessor::SelectorListMatches(cur->AsElement(),
+                                                matchingContext,
+                                                selectorList)) {
+      return cur;
+    }
+  }
+
+  return nsnull;
 }
 
 /* static */
 nsresult
 nsGenericElement::doQuerySelectorAll(nsINode* aRoot,
                                      const nsAString& aSelector,
                                      nsIDOMNodeList **aReturn)
 {
   NS_PRECONDITION(aReturn, "Null out param?");
 
   nsBaseContentList* contentList = new nsBaseContentList();
   NS_ENSURE_TRUE(contentList, NS_ERROR_OUT_OF_MEMORY);
   NS_ADDREF(*aReturn = contentList);
   
   nsAutoPtr<nsCSSSelectorList> selectorList;
-  nsPresContext* presContext;
   nsresult rv = ParseSelectorList(aRoot, aSelector,
-                                  getter_Transfers(selectorList),
-                                  &presContext);
+                                  getter_Transfers(selectorList));
   NS_ENSURE_SUCCESS(rv, rv);
 
-  TryMatchingElementsInSubtree(aRoot, nsnull, presContext, selectorList,
-                               AppendAllMatchingElements, contentList);
+  TreeMatchContext matchingContext(PR_FALSE,
+                                   nsRuleWalker::eRelevantLinkUnvisited,
+                                   aRoot->GetOwnerDoc());
+  for (nsIContent* cur = aRoot->GetFirstChild();
+       cur;
+       cur = cur->GetNextNode(aRoot)) {
+    if (cur->IsElement() &&
+        nsCSSRuleProcessor::SelectorListMatches(cur->AsElement(),
+                                                matchingContext,
+                                                selectorList)) {
+      contentList->AppendElement(cur);
+    }
+  }
   return NS_OK;
 }
 
 
 PRBool
 nsGenericElement::MozMatchesSelector(const nsAString& aSelector, nsresult* aResult)
 {
   nsAutoPtr<nsCSSSelectorList> selectorList;
-  nsPresContext* presContext;
   PRBool matches = PR_FALSE;
 
-  *aResult = ParseSelectorList(this, aSelector, getter_Transfers(selectorList),
-                               &presContext);
+  *aResult = ParseSelectorList(this, aSelector, getter_Transfers(selectorList));
 
   if (NS_SUCCEEDED(*aResult)) {
-    RuleProcessorData data(presContext, this, nsnull);
-    matches = nsCSSRuleProcessor::SelectorListMatches(data, selectorList);
+    TreeMatchContext matchingContext(PR_FALSE,
+                                     nsRuleWalker::eRelevantLinkUnvisited,
+                                     GetOwnerDoc());
+    matches = nsCSSRuleProcessor::SelectorListMatches(this, matchingContext,
+                                                      selectorList);
   }
 
   return matches;
 }
 
 NS_IMETHODIMP
 nsNSElementTearoff::MozMatchesSelector(const nsAString& aSelector, PRBool* aReturn)
 {
--- a/content/base/src/nsGenericElement.h
+++ b/content/base/src/nsGenericElement.h
@@ -450,17 +450,17 @@ public:
   virtual void DestroyContent();
   virtual void SaveSubtreeState();
 
 #ifdef MOZ_SMIL
   virtual nsISMILAttr* GetAnimatedAttr(PRInt32 /*aNamespaceID*/, nsIAtom* /*aName*/)
   {
     return nsnull;
   }
-  virtual nsresult GetSMILOverrideStyle(nsIDOMCSSStyleDeclaration** aStyle);
+  virtual nsIDOMCSSStyleDeclaration* GetSMILOverrideStyle();
   virtual mozilla::css::StyleRule* GetSMILOverrideStyleRule();
   virtual nsresult SetSMILOverrideStyleRule(mozilla::css::StyleRule* aStyleRule,
                                             PRBool aNotify);
 #endif // MOZ_SMIL
 
 #ifdef DEBUG
   virtual void List(FILE* out, PRInt32 aIndent) const
   {
--- a/content/base/src/nsHTMLContentSerializer.cpp
+++ b/content/base/src/nsHTMLContentSerializer.cpp
@@ -67,16 +67,19 @@
 #include "nsContentUtils.h"
 #include "nsLWBrkCIID.h"
 #include "nsIScriptElement.h"
 #include "nsAttrName.h"
 #include "nsIDocShell.h"
 #include "nsIEditorDocShell.h"
 #include "nsIEditor.h"
 #include "nsIHTMLEditor.h"
+#include "mozilla/dom/Element.h"
+
+using namespace mozilla::dom;
 
 static const PRInt32 kLongLineLen = 128;
 
 nsresult NS_NewHTMLContentSerializer(nsIContentSerializer** aSerializer)
 {
   nsHTMLContentSerializer* it = new nsHTMLContentSerializer();
   if (!it) {
     return NS_ERROR_OUT_OF_MEMORY;
@@ -223,18 +226,18 @@ nsHTMLContentSerializer::SerializeHTMLAt
     if (IsShorthandAttr(attrName, aTagName) && valueStr.IsEmpty()) {
       valueStr = nameStr;
     }
     SerializeAttr(EmptyString(), nameStr, valueStr, aStr, !isJS);
   }
 }
 
 NS_IMETHODIMP
-nsHTMLContentSerializer::AppendElementStart(nsIContent *aElement,
-                                            nsIContent *aOriginalElement,
+nsHTMLContentSerializer::AppendElementStart(Element* aElement,
+                                            Element* aOriginalElement,
                                             nsAString& aStr)
 {
   NS_ENSURE_ARG(aElement);
 
   nsIContent* content = aElement;
 
   PRBool forceFormat = PR_FALSE;
   if (!CheckElementStart(content, forceFormat, aStr)) {
@@ -337,17 +340,17 @@ nsHTMLContentSerializer::AppendElementSt
   }
 
   AfterElementStart(content, aOriginalElement, aStr);
 
   return NS_OK;
 }
   
 NS_IMETHODIMP 
-nsHTMLContentSerializer::AppendElementEnd(nsIContent *aElement,
+nsHTMLContentSerializer::AppendElementEnd(Element* aElement,
                                           nsAString& aStr)
 {
   NS_ENSURE_ARG(aElement);
 
   nsIContent* content = aElement;
 
   nsIAtom *name = content->Tag();
 
--- a/content/base/src/nsHTMLContentSerializer.h
+++ b/content/base/src/nsHTMLContentSerializer.h
@@ -52,21 +52,21 @@
 class nsIContent;
 class nsIAtom;
 
 class nsHTMLContentSerializer : public nsXHTMLContentSerializer {
  public:
   nsHTMLContentSerializer();
   virtual ~nsHTMLContentSerializer();
 
-  NS_IMETHOD AppendElementStart(nsIContent *aElement,
-                                nsIContent *aOriginalElement,
+  NS_IMETHOD AppendElementStart(mozilla::dom::Element* aElement,
+                                mozilla::dom::Element* aOriginalElement,
                                 nsAString& aStr);
-  
-  NS_IMETHOD AppendElementEnd(nsIContent *aElement,
+
+  NS_IMETHOD AppendElementEnd(mozilla::dom::Element* aElement,
                               nsAString& aStr);
 
   NS_IMETHOD AppendDocumentStart(nsIDocument *aDocument,
                                  nsAString& aStr);
  protected:
 
   virtual void SerializeHTMLAttributes(nsIContent* aContent,
                                        nsIContent *aOriginalElement,
--- a/content/base/src/nsImageLoadingContent.cpp
+++ b/content/base/src/nsImageLoadingContent.cpp
@@ -824,17 +824,17 @@ nsImageLoadingContent::UpdateImageState(
 
   if (aNotify) {
     nsIDocument* doc = thisContent->GetCurrentDoc();
     if (doc) {
       NS_ASSERTION(thisContent->IsInDoc(), "Something is confused");
       nsEventStates changedBits = oldState ^ ImageState();
       if (!changedBits.IsEmpty()) {
         mozAutoDocUpdate upd(doc, UPDATE_CONTENT_STATE, PR_TRUE);
-        doc->ContentStatesChanged(thisContent, nsnull, changedBits);
+        doc->ContentStateChanged(thisContent, changedBits);
       }
     }
   }
 }
 
 void
 nsImageLoadingContent::CancelImageRequests(PRBool aNotify)
 {
--- a/content/base/src/nsObjectLoadingContent.cpp
+++ b/content/base/src/nsObjectLoadingContent.cpp
@@ -1652,17 +1652,17 @@ nsObjectLoadingContent::NotifyStateChang
 
   if (newState != aOldState) {
     // This will trigger frame construction
     NS_ASSERTION(thisContent->IsInDoc(), "Something is confused");
     nsEventStates changedBits = aOldState ^ newState;
 
     {
       mozAutoDocUpdate upd(doc, UPDATE_CONTENT_STATE, PR_TRUE);
-      doc->ContentStatesChanged(thisContent, nsnull, changedBits);
+      doc->ContentStateChanged(thisContent, changedBits);
     }
     if (aSync) {
       // Make sure that frames are actually constructed, and do it after
       // EndUpdate was called.
       doc->FlushPendingNotifications(Flush_Frames);
     }
   } else if (aOldType != mType) {
     // If our state changed, then we already recreated frames
--- a/content/base/src/nsPlainTextSerializer.cpp
+++ b/content/base/src/nsPlainTextSerializer.cpp
@@ -52,16 +52,19 @@
 #include "nsIDOMElement.h"
 #include "nsINameSpaceManager.h"
 #include "nsTextFragment.h"
 #include "nsContentUtils.h"
 #include "nsReadableUtils.h"
 #include "nsUnicharUtils.h"
 #include "nsCRT.h"
 #include "nsIParserService.h"
+#include "mozilla/dom/Element.h"
+
+using namespace mozilla::dom;
 
 #define PREF_STRUCTS "converter.html2txt.structs"
 #define PREF_HEADER_STRATEGY "converter.html2txt.header_strategy"
 
 static const  PRInt32 kTabSize=4;
 static const  PRInt32 kOLNumberWidth = 3;
 static const  PRInt32 kIndentSizeHeaders = 2;  /* Indention of h1, if
                                                 mHeaderStrategy = 1 or = 2.
@@ -376,18 +379,18 @@ nsPlainTextSerializer::AppendCDATASectio
                                           PRInt32 aStartOffset,
                                           PRInt32 aEndOffset,
                                           nsAString& aStr)
 {
   return AppendText(aCDATASection, aStartOffset, aEndOffset, aStr);
 }
 
 NS_IMETHODIMP
-nsPlainTextSerializer::AppendElementStart(nsIContent *aElement,
-                                          nsIContent *aOriginalElement,
+nsPlainTextSerializer::AppendElementStart(Element* aElement,
+                                          Element* aOriginalElement,
                                           nsAString& aStr)
 {
   NS_ENSURE_ARG(aElement);
 
   mContent = aElement;
 
   nsresult rv;
   PRInt32 id = GetIdForContent(mContent);
@@ -409,17 +412,17 @@ nsPlainTextSerializer::AppendElementStar
   if (id == eHTMLTag_head) {
     ++mHeadLevel;
   }
 
   return rv;
 } 
  
 NS_IMETHODIMP 
-nsPlainTextSerializer::AppendElementEnd(nsIContent *aElement,
+nsPlainTextSerializer::AppendElementEnd(Element* aElement,
                                         nsAString& aStr)
 {
   NS_ENSURE_ARG(aElement);
 
   mContent = aElement;
 
   nsresult rv;
   PRInt32 id = GetIdForContent(mContent);
--- a/content/base/src/nsPlainTextSerializer.h
+++ b/content/base/src/nsPlainTextSerializer.h
@@ -51,16 +51,22 @@
 #include "nsString.h"
 #include "nsILineBreaker.h"
 #include "nsIContent.h"
 #include "nsIAtom.h"
 #include "nsIHTMLToTextSink.h"
 #include "nsIDocumentEncoder.h"
 #include "nsTArray.h"
 
+namespace mozilla {
+namespace dom {
+class Element;
+} // namespace dom
+} // namespace mozilla
+
 class nsPlainTextSerializer : public nsIContentSerializer,
                               public nsIHTMLContentSink,
                               public nsIHTMLToTextSink
 {
 public:
   nsPlainTextSerializer();
   virtual ~nsPlainTextSerializer();
 
@@ -79,20 +85,20 @@ public:
   NS_IMETHOD AppendProcessingInstruction(nsIContent* aPI,
                                          PRInt32 aStartOffset,
                                          PRInt32 aEndOffset,
                                          nsAString& aStr)  { return NS_OK; }
   NS_IMETHOD AppendComment(nsIContent* aComment, PRInt32 aStartOffset,
                            PRInt32 aEndOffset, nsAString& aStr)  { return NS_OK; }
   NS_IMETHOD AppendDoctype(nsIContent *aDoctype,
                            nsAString& aStr)  { return NS_OK; }
-  NS_IMETHOD AppendElementStart(nsIContent *aElement,
-                                nsIContent *aOriginalElement,
+  NS_IMETHOD AppendElementStart(mozilla::dom::Element* aElement,
+                                mozilla::dom::Element* aOriginalElement,
                                 nsAString& aStr); 
-  NS_IMETHOD AppendElementEnd(nsIContent *aElement,
+  NS_IMETHOD AppendElementEnd(mozilla::dom::Element* aElement,
                               nsAString& aStr);
   NS_IMETHOD Flush(nsAString& aStr);
 
   NS_IMETHOD AppendDocumentStart(nsIDocument *aDocument,
                                  nsAString& aStr);
 
   // nsIContentSink
   NS_IMETHOD WillParse(void) { return NS_OK; }
@@ -230,17 +236,17 @@ protected:
                                               header level (default)
                                           2 = numbering and slight indention */
   PRInt32          mHeaderCounter[7];  /* For header-numbering:
                                           Number of previous headers of
                                           the same depth and in the same
                                           section.
                                           mHeaderCounter[1] for <h1> etc. */
 
-  nsCOMPtr<nsIContent> mContent;
+  nsRefPtr<mozilla::dom::Element> mContent;
 
   // For handling table rows
   nsAutoTArray<PRPackedBool, 8> mHasWrittenCellsForRow;
   
   // Values gotten in OpenContainer that is (also) needed in CloseContainer
   nsAutoTArray<PRPackedBool, 8> mCurrentNodeIsConverted;
   nsAutoTArray<PRPackedBool, 8> mIsInCiteBlockquote;
 
--- a/content/base/src/nsTextFragment.cpp
+++ b/content/base/src/nsTextFragment.cpp
@@ -43,16 +43,17 @@
 
 #include "nsTextFragment.h"
 #include "nsCRT.h"
 #include "nsReadableUtils.h"
 #include "nsMemory.h"
 #include "nsBidiUtils.h"
 #include "nsUnicharUtils.h"
 #include "nsUTF8Utils.h"
+#include "mozilla/SSE.h"
 
 #define TEXTFRAG_WHITE_AFTER_NEWLINE 50
 #define TEXTFRAG_MAX_NEWLINES 7
 
 // Static buffer used for common fragments
 static char* sSpaceSharedString[TEXTFRAG_MAX_NEWLINES + 1];
 static char* sTabSharedString[TEXTFRAG_MAX_NEWLINES + 1];
 static char sSingleCharSharedString[256];
@@ -139,16 +140,79 @@ nsTextFragment::operator=(const nsTextFr
     if (m1b) {
       mAllBits = aOther.mAllBits;
     }
   }
 
   return *this;
 }
 
+static inline PRBool
+Is8BitUnvectorized(const PRUnichar *str, const PRUnichar *end)
+{
+#if PR_BYTES_PER_WORD == 4
+  const size_t mask = 0xff00ff00;
+  const PRUint32 alignMask = 0x3;
+  const PRUint32 numUnicharsPerWord = 2;
+#elif PR_BYTES_PER_WORD == 8
+  const size_t mask = 0xff00ff00ff00ff00;
+  const PRUint32 alignMask = 0x7;
+  const PRUint32 numUnicharsPerWord = 4;
+#else
+#error Unknown platform!
+#endif
+
+  const PRInt32 len = end - str;
+  PRInt32 i = 0;
+
+  // Align ourselves to a word boundary.
+  PRInt32 alignLen =
+    PR_MIN(len, PRInt32(((-NS_PTR_TO_UINT32(str)) & alignMask) / sizeof(PRUnichar)));
+  for (; i < alignLen; i++) {
+    if (str[i] > 255)
+      return PR_FALSE;
+  }
+
+  // Check one word at a time.
+  const PRInt32 wordWalkEnd = ((len - i) / numUnicharsPerWord) * numUnicharsPerWord;
+  for (; i < wordWalkEnd; i += numUnicharsPerWord) {
+    const size_t word = *reinterpret_cast<const size_t*>(str + i);
+    if (word & mask)
+      return PR_FALSE;
+  }
+
+  // Take care of the remainder one character at a time.
+  for (; i < len; i++) {
+    if (str[i] > 255)
+      return PR_FALSE;
+  }
+
+  return PR_TRUE;
+}
+
+#ifdef MOZILLA_MAY_SUPPORT_SSE2
+namespace mozilla {
+  namespace SSE2 {
+    PRBool Is8Bit(const PRUnichar *str, const PRUnichar *end);
+  }
+}
+#endif
+
+static inline PRBool
+Is8Bit(const PRUnichar *str, const PRUnichar *end)
+{
+#ifdef MOZILLA_MAY_SUPPORT_SSE2
+  if (mozilla::supports_sse2()) {
+    return mozilla::SSE2::Is8Bit(str, end);
+  }
+#endif
+
+  return Is8BitUnvectorized(str, end);
+}
+
 void
 nsTextFragment::SetTo(const PRUnichar* aBuffer, PRInt32 aLength)
 {
   ReleaseText();
 
   if (aLength == 0) {
     return;
   }
@@ -198,24 +262,17 @@ nsTextFragment::SetTo(const PRUnichar* a
       mState.mIs2b = PR_FALSE;
       mState.mLength = aLength;
 
       return;        
     }
   }
 
   // See if we need to store the data in ucs2 or not
-  PRBool need2 = PR_FALSE;
-  while (ucp < uend) {
-    PRUnichar ch = *ucp++;
-    if (ch >= 256) {
-      need2 = PR_TRUE;
-      break;
-    }
-  }
+  PRBool need2 = !Is8Bit(ucp, uend);
 
   if (need2) {
     // Use ucs2 storage because we have to
     m2b = (PRUnichar *)nsMemory::Clone(aBuffer,
                                        aLength * sizeof(PRUnichar));
     if (!m2b) {
       return;
     }
@@ -288,28 +345,17 @@ nsTextFragment::Append(const PRUnichar* 
     mState.mLength += aLength;
     m2b = buff;
 
     return;
   }
 
   // Current string is a 1-byte string, check if the new data fits in one byte too.
 
-  const PRUnichar* ucp = aBuffer;
-  const PRUnichar* uend = ucp + aLength;
-  PRBool need2 = PR_FALSE;
-  while (ucp < uend) {
-    PRUnichar ch = *ucp++;
-    if (ch >= 256) {
-      need2 = PR_TRUE;
-      break;
-    }
-  }
-
-  if (need2) {
+  if (!Is8Bit(aBuffer, aBuffer + aLength)) {
     // The old data was 1-byte, but the new is not so we have to expand it
     // all to 2-byte
     PRUnichar* buff = (PRUnichar*)nsMemory::Alloc((mState.mLength + aLength) *
                                                   sizeof(PRUnichar));
     if (!buff) {
       return;
     }
 
new file mode 100644
--- /dev/null
+++ b/content/base/src/nsTextFragmentSSE2.cpp
@@ -0,0 +1,72 @@
+// This file should only be compiled if you're on x86 or x86_64.  Additionally,
+// you'll need to compile this file with -msse2 if you're using gcc.
+
+#include <emmintrin.h>
+#include "nscore.h"
+
+namespace mozilla {
+namespace SSE2 {
+
+static inline bool
+is_zero (__m128i x)
+{
+  return
+    _mm_movemask_epi8(_mm_cmpeq_epi8(x, _mm_setzero_si128())) == 0xffff;
+}
+
+PRBool
+Is8Bit(const PRUnichar *str, const PRUnichar *end)
+{
+  const PRUint32 numUnicharsPerVector = 8;
+
+#if PR_BYTES_PER_WORD == 4
+  const size_t mask = 0xff00ff00;
+  const PRUint32 numUnicharsPerWord = 2;
+#elif PR_BYTES_PER_WORD == 8
+  const size_t mask = 0xff00ff00ff00ff00;
+  const PRUint32 numUnicharsPerWord = 4;
+#else
+#error Unknown platform!
+#endif
+
+  const PRInt32 len = end - str;
+  PRInt32 i = 0;
+
+  // Align ourselves to a 16-byte boundary, as required by _mm_load_si128
+  // (i.e. MOVDQA).
+  PRInt32 alignLen =
+    PR_MIN(len, PRInt32(((-NS_PTR_TO_UINT32(str)) & 0xf) / sizeof(PRUnichar)));
+  for (; i < alignLen; i++) {
+    if (str[i] > 255)
+      return PR_FALSE;
+  }
+
+  // Check one XMM register (16 bytes) at a time.
+  const PRInt32 vectWalkEnd = ((len - i) / numUnicharsPerVector) * numUnicharsPerVector;
+  __m128i vectmask = _mm_set1_epi16(0xff00);
+  for(; i < vectWalkEnd; i += numUnicharsPerVector) {
+    const __m128i vect = *reinterpret_cast<const __m128i*>(str + i);
+    if (!is_zero(_mm_and_si128(vect, vectmask)))
+      return PR_FALSE;
+  }
+
+  // Check one word at a time.
+  const PRInt32 wordWalkEnd = ((len - i) / numUnicharsPerWord) * numUnicharsPerWord;
+  for(; i < wordWalkEnd; i += numUnicharsPerWord) {
+    const size_t word = *reinterpret_cast<const size_t*>(str + i);
+    if (word & mask)
+      return PR_FALSE;
+  }
+
+  // Take care of the remainder one character at a time.
+  for (; i < len; i++) {
+    if (str[i] > 255) {
+      return PR_FALSE;
+    }
+  }
+
+  return PR_TRUE;
+}
+
+} // namespace SSE2
+} // namespace mozilla
--- a/content/base/src/nsXMLContentSerializer.cpp
+++ b/content/base/src/nsXMLContentSerializer.cpp
@@ -59,16 +59,19 @@
 #include "nsTextFragment.h"
 #include "nsString.h"
 #include "prprf.h"
 #include "nsUnicharUtils.h"
 #include "nsCRT.h"
 #include "nsContentUtils.h"
 #include "nsAttrName.h"
 #include "nsILineBreaker.h"
+#include "mozilla/dom/Element.h"
+
+using namespace mozilla::dom;
 
 static const char kMozStr[] = "moz";
 
 #define kXMLNS "xmlns"
 
 // to be readable, we assume that an indented line contains
 // at least this number of characters (arbitrary value here).
 // This is a limit for the indentation.
@@ -910,18 +913,18 @@ nsXMLContentSerializer::SerializeAttribu
                    "Namespaced attributes must have a prefix");
       SerializeAttr(xmlnsStr, prefixStr, uriStr, aStr, PR_TRUE);
       PushNameSpaceDecl(prefixStr, uriStr, aOriginalElement);
     }
   }
 }
 
 NS_IMETHODIMP 
-nsXMLContentSerializer::AppendElementStart(nsIContent *aElement,
-                                           nsIContent *aOriginalElement,
+nsXMLContentSerializer::AppendElementStart(Element* aElement,
+                                           Element* aOriginalElement,
                                            nsAString& aStr)
 {
   NS_ENSURE_ARG(aElement);
 
   nsIContent* content = aElement;
 
   PRBool forceFormat = PR_FALSE;
   if (!CheckElementStart(content, forceFormat, aStr)) {
@@ -1011,17 +1014,17 @@ nsXMLContentSerializer::AppendEndOfEleme
     AppendToString(NS_LITERAL_STRING("/>"), aStr);
   }
   else {
     AppendToString(kGreaterThan, aStr);
   }
 }
 
 NS_IMETHODIMP 
-nsXMLContentSerializer::AppendElementEnd(nsIContent *aElement,
+nsXMLContentSerializer::AppendElementEnd(Element* aElement,
                                          nsAString& aStr)
 {
   NS_ENSURE_ARG(aElement);
 
   nsIContent* content = aElement;
 
   PRBool forceFormat = PR_FALSE, outputElementEnd;
   outputElementEnd = CheckElementEnd(content, forceFormat, aStr);
--- a/content/base/src/nsXMLContentSerializer.h
+++ b/content/base/src/nsXMLContentSerializer.h
@@ -83,21 +83,21 @@ class nsXMLContentSerializer : public ns
                                          nsAString& aStr);
 
   NS_IMETHOD AppendComment(nsIContent* aComment, PRInt32 aStartOffset,
                            PRInt32 aEndOffset, nsAString& aStr);
   
   NS_IMETHOD AppendDoctype(nsIContent *aDoctype,
                            nsAString& aStr);
 
-  NS_IMETHOD AppendElementStart(nsIContent *aElement,
-                                nsIContent *aOriginalElement,
+  NS_IMETHOD AppendElementStart(mozilla::dom::Element* aElement,
+                                mozilla::dom::Element* aOriginalElement,
                                 nsAString& aStr);
-  
-  NS_IMETHOD AppendElementEnd(nsIContent *aElement,
+
+  NS_IMETHOD AppendElementEnd(mozilla::dom::Element* aElement,
                               nsAString& aStr);
 
   NS_IMETHOD Flush(nsAString& aStr) { return NS_OK; }
 
   NS_IMETHOD AppendDocumentStart(nsIDocument *aDocument,
                                  nsAString& aStr);
 
  protected:
--- a/content/base/test/Makefile.in
+++ b/content/base/test/Makefile.in
@@ -275,16 +275,17 @@ include $(topsrcdir)/config/rules.mk
 		file_CrossSiteXHR_server.sjs \
 		test_CrossSiteXHR_cache.html \
 		file_CrossSiteXHR_cache_server.sjs \
 		test_XHRDocURI.html \
 		file_XHRDocURI.xml \
 		file_XHRDocURI.xml^headers^ \
 		file_XHRDocURI.text \
 		file_XHRDocURI.text^headers^ \
+		test_DOMException.html \
 		$(NULL)
 
 _TEST_FILES2 = \
 		test_bug459424.html \
 		bug461735-redirect1.sjs \
 		bug461735-redirect2.sjs \
 		bug461735-post-redirect.js \
 		test_bug513194.html \
@@ -426,16 +427,17 @@ include $(topsrcdir)/config/rules.mk
 		test_createHTMLDocument.html \
 		test_bug622088.html \
 		file_bug622088_inner.html \
 		file_bug622088.sjs \
 		test_bug564047.html \
 		test_bug567350.html \
 		test_bug574596.html \
 		test_bug578096.html \
+		test_bug585978.html \
 		test_bug592366.html \
 		test_bug597345.html \
 		script-1_bug597345.sjs \
 		script-2_bug597345.js \
 		test_bug598877.html \
 		test_bug599588.html \
 		test_bug600466.html \
 		test_bug600468.html \
new file mode 100644
--- /dev/null
+++ b/content/base/test/test_DOMException.html
@@ -0,0 +1,51 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test for DOMException constants</title>
+  <script src="/MochiKit/packed.js"></script>
+  <script src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+<script>
+var constants = [
+  null,
+  "INDEX_SIZE_ERR",
+  "DOMSTRING_SIZE_ERR",
+  "HIERARCHY_REQUEST_ERR",
+  "WRONG_DOCUMENT_ERR",
+  "INVALID_CHARACTER_ERR",
+  "NO_DATA_ALLOWED_ERR",
+  "NO_MODIFICATION_ALLOWED_ERR",
+  "NOT_FOUND_ERR",
+  "NOT_SUPPORTED_ERR",
+  "INUSE_ATTRIBUTE_ERR",
+  "INVALID_STATE_ERR",
+  "SYNTAX_ERR",
+  "INVALID_MODIFICATION_ERR",
+  "NAMESPACE_ERR",
+  "INVALID_ACCESS_ERR",
+  "VALIDATION_ERR",
+  "TYPE_MISMATCH_ERR",
+  null,
+  null,
+  null,
+  null,
+  null,
+  null,
+  null,
+  "DATA_CLONE_ERR"
+];
+for (var i = 0; i < constants.length; ++i) {
+  var constant = constants[i];
+  if (constant)
+    is(DOMException[constant], i, constant)
+}
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/base/test/test_bug585978.html
@@ -0,0 +1,39 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=585978
+-->
+<head>
+  <title>Test for Bug 585978</title>
+  <script type="application/javascript" src="/MochiKit/packed.js"></script>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=585978">Mozilla Bug 585978</a>
+
+<script type="application/javascript;version=1.7">
+
+/* Test that if we have a unicode character in the middle of an ascii string,
+   the unicode character survives translation into and out of a text node. */
+
+for (let i = 0; i < 128; i++) {
+  let node = document.createTextNode('');
+  let str = '';
+  for (let j = 0; j < i; j++) {
+    str += 'a';
+  }
+  str += '\uA0A9'
+  node.data = str;
+
+  for (let j = 0; j < 32; j++) {
+    is(node.data, str);
+
+    str += 'b';
+    node.appendData('b');
+  }
+}
+
+</script>
+</body>
+</html>
--- a/content/base/test/test_fileapi.html
+++ b/content/base/test/test_fileapi.html
@@ -110,23 +110,16 @@ expectedTestCount++;
 
 r = new FileReader();
 r.readAsText(createFileWithData(convertToUTF16(testTextData)), "utf-16");
 r.onload = getLoadHandler(testTextData,
                           convertToUTF16(testTextData).length,
                           "utf16 reading");
 expectedTestCount++;
 
-r = new FileReader();
-r.onload = getLoadHandler(testTextData,
-                          convertToUTF32(testTextData).length,
-                          "utf32 reading");
-r.readAsText(createFileWithData(convertToUTF32(testTextData)), "UTF-32");
-expectedTestCount++;
-
 
 // Test loading an empty file works (and doesn't crash!)
 var emptyFile = createFileWithData("");
 dump("hello nurse");
 r = new FileReader();
 r.onload = getLoadHandler("", 0, "empty no encoding reading");
 r.readAsText(emptyFile, "");
 expectedTestCount++;
@@ -346,25 +339,16 @@ function convertToUTF16(s) {
   res = "";
   for (var i = 0; i < s.length; ++i) {
     c = s.charCodeAt(i);
     res += String.fromCharCode(c >>> 8, c & 255);
   }
   return res;
 }
 
-function convertToUTF32(s) {
-  res = "";
-  for (var i = 0; i < s.length; ++i) {
-    c = s.charCodeAt(i);
-    res += "\0\0" + String.fromCharCode(c >>> 8, c & 255);
-  }
-  return res;
-}
-
 function convertToUTF8(s) {
   return unescape(encodeURIComponent(s));
 }
 
 function convertToDataURL(s) {
   return "data:application/octet-stream;base64," + btoa(s);
 }
 
--- a/content/canvas/public/nsICanvasRenderingContextInternal.h
+++ b/content/canvas/public/nsICanvasRenderingContextInternal.h
@@ -38,25 +38,24 @@
 #ifndef nsICanvasRenderingContextInternal_h___
 #define nsICanvasRenderingContextInternal_h___
 
 #include "nsISupports.h"
 #include "nsIInputStream.h"
 #include "nsIDocShell.h"
 #include "gfxPattern.h"
 
+// {EC90F32E-7848-4819-A1E3-02E64C682A72}
 #define NS_ICANVASRENDERINGCONTEXTINTERNAL_IID \
-{ 0xffb42d3c, 0x8281, 0x44c8, \
-  { 0xac, 0xba, 0x73, 0x15, 0x31, 0xaa, 0xe5, 0x07 } }
+{ 0xec90f32e, 0x7848, 0x4819, { 0xa1, 0xe3, 0x2, 0xe6, 0x4c, 0x68, 0x2a, 0x72 } }
 
 class nsHTMLCanvasElement;
 class gfxContext;
 class gfxASurface;
 class nsIPropertyBag;
-class nsDisplayListBuilder;
 
 namespace mozilla {
 namespace layers {
 class CanvasLayer;
 class LayerManager;
 }
 namespace ipc {
 class Shmem;
@@ -104,18 +103,17 @@ public:
   NS_IMETHOD SetIsOpaque(PRBool isOpaque) = 0;
 
   // Invalidate this context and release any held resources, in preperation
   // for possibly reinitializing with SetDimensions/InitializeWithSurface.
   NS_IMETHOD Reset() = 0;
 
   // Return the CanvasLayer for this context, creating
   // one for the given layer manager if not available.
-  virtual already_AddRefed<CanvasLayer> GetCanvasLayer(nsDisplayListBuilder* aBuilder,
-                                                       CanvasLayer *aOldLayer,
+  virtual already_AddRefed<CanvasLayer> GetCanvasLayer(CanvasLayer *aOldLayer,
                                                        LayerManager *aManager) = 0;
 
   virtual void MarkContextClean() = 0;
 
   // Redraw the dirty rectangle of this canvas.
   NS_IMETHOD Redraw(const gfxRect &dirty) = 0;
 
   // Passes a generic nsIPropertyBag options argument, along with the
--- a/content/canvas/src/WebGLContext.cpp
+++ b/content/canvas/src/WebGLContext.cpp
@@ -53,17 +53,16 @@
 
 #include "imgIEncoder.h"
 
 #include "gfxContext.h"
 #include "gfxPattern.h"
 #include "gfxUtils.h"
 
 #include "CanvasUtils.h"
-#include "nsDisplayList.h"
 
 #include "GLContextProvider.h"
 
 #include "gfxCrashReporterUtils.h"
 
 #ifdef MOZ_SVG
 #include "nsSVGEffects.h"
 #endif
@@ -243,26 +242,26 @@ WebGLContext::DestroyResourcesAndContext
 #endif
 
     gl = nsnull;
 }
 
 void
 WebGLContext::Invalidate()
 {
-    if (mInvalidated)
-        return;
-
     if (!mCanvasElement)
         return;
 
 #ifdef MOZ_SVG
     nsSVGEffects::InvalidateDirectRenderingObservers(HTMLCanvasElement());
 #endif
 
+    if (mInvalidated)
+        return;
+
     mInvalidated = PR_TRUE;
     HTMLCanvasElement()->InvalidateFrame();
 }
 
 /* readonly attribute nsIDOMHTMLCanvasElement canvas; */
 NS_IMETHODIMP
 WebGLContext::GetCanvas(nsIDOMHTMLCanvasElement **canvas)
 {
@@ -615,64 +614,37 @@ WebGLContext::GetInputStream(const char*
 NS_IMETHODIMP
 WebGLContext::GetThebesSurface(gfxASurface **surface)
 {
     return NS_ERROR_NOT_AVAILABLE;
 }
 
 static PRUint8 gWebGLLayerUserData;
 
-class WebGLContextUserData : public LayerUserData {
-public:
-    WebGLContextUserData(nsHTMLCanvasElement *aContent)
-    : mContent(aContent) {}
-  static void DidTransactionCallback(void* aData)
-  {
-    static_cast<WebGLContextUserData*>(aData)->mContent->MarkContextClean();
-  }
-
-private:
-  nsRefPtr<nsHTMLCanvasElement> mContent;
-};
-
 already_AddRefed<layers::CanvasLayer>
-WebGLContext::GetCanvasLayer(nsDisplayListBuilder* aBuilder,
-                             CanvasLayer *aOldLayer,
+WebGLContext::GetCanvasLayer(CanvasLayer *aOldLayer,
                              LayerManager *aManager)
 {
     if (!mResetLayer && aOldLayer &&
         aOldLayer->HasUserData(&gWebGLLayerUserData)) {
         NS_ADDREF(aOldLayer);
+        if (mInvalidated) {
+            aOldLayer->Updated(nsIntRect(0, 0, mWidth, mHeight));
+            mInvalidated = PR_FALSE;
+            HTMLCanvasElement()->GetPrimaryCanvasFrame()->MarkLayersActive();
+        }
         return aOldLayer;
     }
 
     nsRefPtr<CanvasLayer> canvasLayer = aManager->CreateCanvasLayer();
     if (!canvasLayer) {
         NS_WARNING("CreateCanvasLayer returned null!");
         return nsnull;
     }
-    WebGLContextUserData *userData = nsnull;
-    if (aBuilder->IsPaintingToWindow()) {
-      // Make the layer tell us whenever a transaction finishes (including
-      // the current transaction), so we can clear our invalidation state and
-      // start invalidating again. We need to do this for the layer that is
-      // being painted to a window (there shouldn't be more than one at a time,
-      // and if there is, flushing the invalidation state more often than
-      // necessary is harmless).
-
-      // The layer will be destroyed when we tear down the presentation
-      // (at the latest), at which time this userData will be destroyed,
-      // releasing the reference to the element.
-      // The userData will receive DidTransactionCallbacks, which flush the
-      // the invalidation state to indicate that the canvas is up to date.
-      userData = new WebGLContextUserData(HTMLCanvasElement());
-      canvasLayer->SetDidTransactionCallback(
-              WebGLContextUserData::DidTransactionCallback, userData);
-    }
-    canvasLayer->SetUserData(&gWebGLLayerUserData, userData);
+    canvasLayer->SetUserData(&gWebGLLayerUserData, nsnull);
 
     CanvasLayer::Data data;
 
     // the gl context may either provide a native PBuffer, in which case we want to initialize
     // data with the gl context directly, or may provide a surface to which it renders (this is the case
     // of OSMesa contexts), in which case we want to initialize data with that surface.
 
     void* native_surface = gl->GetNativeData(gl::GLContext::NativeImageSurface);
@@ -684,18 +656,19 @@ WebGLContext::GetCanvasLayer(nsDisplayLi
     }
 
     data.mSize = nsIntSize(mWidth, mHeight);
     data.mGLBufferIsPremultiplied = mOptions.premultipliedAlpha ? PR_TRUE : PR_FALSE;
 
     canvasLayer->Initialize(data);
     PRUint32 flags = gl->CreationFormat().alpha == 0 ? Layer::CONTENT_OPAQUE : 0;
     canvasLayer->SetContentFlags(flags);
-    canvasLayer->Updated();
+    canvasLayer->Updated(nsIntRect(0, 0, mWidth, mHeight));
 
+    mInvalidated = PR_FALSE;
     mResetLayer = PR_FALSE;
 
     return canvasLayer.forget().get();
 }
 
 NS_IMETHODIMP
 WebGLContext::GetContextAttributes(jsval *aResult)
 {
--- a/content/canvas/src/WebGLContext.h
+++ b/content/canvas/src/WebGLContext.h
@@ -363,20 +363,19 @@ public:
     }
     nsresult ErrorOutOfMemory(const char *fmt = 0, ...);
 
     WebGLTexture *activeBoundTextureForTarget(WebGLenum target) {
         return target == LOCAL_GL_TEXTURE_2D ? mBound2DTextures[mActiveTexture]
                                              : mBoundCubeMapTextures[mActiveTexture];
     }
 
-    already_AddRefed<CanvasLayer> GetCanvasLayer(nsDisplayListBuilder* aBuilder,
-                                                 CanvasLayer *aOldLayer,
+    already_AddRefed<CanvasLayer> GetCanvasLayer(CanvasLayer *aOldLayer,
                                                  LayerManager *aManager);
-    void MarkContextClean() { mInvalidated = PR_FALSE; }
+    void MarkContextClean() { }
 
     // a number that increments every time we have an event that causes
     // all context resources to be lost.
     PRUint32 Generation() { return mGeneration.value(); }
 
 protected:
     void SetDontKnowIfNeedFakeBlack() {
         mFakeBlackStatus = DontKnowIfNeedFakeBlack;
--- a/content/canvas/src/nsCanvasRenderingContext2D.cpp
+++ b/content/canvas/src/nsCanvasRenderingContext2D.cpp
@@ -91,17 +91,16 @@
 #include "nsIDocShell.h"
 #include "nsIDOMWindow.h"
 #include "nsPIDOMWindow.h"
 #include "nsIDocShell.h"
 #include "nsIDocShellTreeItem.h"
 #include "nsIDocShellTreeNode.h"
 #include "nsIXPConnect.h"
 #include "jsapi.h"
-#include "nsDisplayList.h"
 
 #include "nsTArray.h"
 
 #include "imgIEncoder.h"
 
 #include "gfxContext.h"
 #include "gfxASurface.h"
 #include "gfxImageSurface.h"
@@ -403,18 +402,17 @@ public:
     NS_IMETHOD InitializeWithSurface(nsIDocShell *shell, gfxASurface *surface, PRInt32 width, PRInt32 height);
     NS_IMETHOD Render(gfxContext *ctx, gfxPattern::GraphicsFilter aFilter);
     NS_IMETHOD GetInputStream(const char* aMimeType,
                               const PRUnichar* aEncoderOptions,
                               nsIInputStream **aStream);
     NS_IMETHOD GetThebesSurface(gfxASurface **surface);
     NS_IMETHOD SetIsOpaque(PRBool isOpaque);
     NS_IMETHOD Reset();
-    already_AddRefed<CanvasLayer> GetCanvasLayer(nsDisplayListBuilder* aBuilder,
-                                                 CanvasLayer *aOldLayer,
+    already_AddRefed<CanvasLayer> GetCanvasLayer(CanvasLayer *aOldLayer,
                                                  LayerManager *aManager);
     void MarkContextClean();
     NS_IMETHOD SetIsIPC(PRBool isIPC);
     // this rect is in canvas device space
     NS_IMETHOD Redraw(const gfxRect &r);
     // this rect is in mThebes's current user space
     NS_IMETHOD RedrawUser(const gfxRect &r);
 
@@ -452,16 +450,17 @@ public:
         }
     private:
         gfxContext *mContext;
         nsRefPtr<gfxPath> mPath;
     };
     friend class PathAutoSaveRestore;
 
 protected:
+
     /**
      * The number of living nsCanvasRenderingContexts.  When this goes down to
      * 0, we free the premultiply and unpremultiply tables, if they exist.
      */
     static PRUint32 sNumLivingContexts;
 
     /**
      * Lookup table used to speed up GetImageData().
@@ -4095,81 +4094,59 @@ nsCanvasRenderingContext2D::SetMozImageS
         DirtyAllStyles();
     }
 
     return NS_OK;
 }
 
 static PRUint8 g2DContextLayerUserData;
 
-class CanvasRenderingContext2DUserData : public LayerUserData {
-public:
-  CanvasRenderingContext2DUserData(nsHTMLCanvasElement *aContent)
-    : mContent(aContent) {}
-  static void DidTransactionCallback(void* aData)
-  {
-    static_cast<CanvasRenderingContext2DUserData*>(aData)->mContent->MarkContextClean();
-  }
-
-private:
-  nsRefPtr<nsHTMLCanvasElement> mContent;
-};
-
 already_AddRefed<CanvasLayer>
-nsCanvasRenderingContext2D::GetCanvasLayer(nsDisplayListBuilder* aBuilder,
-                                           CanvasLayer *aOldLayer,
+nsCanvasRenderingContext2D::GetCanvasLayer(CanvasLayer *aOldLayer,
                                            LayerManager *aManager)
 {
     if (!mValid)
         return nsnull;
 
     if (!mResetLayer && aOldLayer &&
         aOldLayer->HasUserData(&g2DContextLayerUserData)) {
         NS_ADDREF(aOldLayer);
+        if (mIsEntireFrameInvalid || mInvalidateCount > 0) {
+            // XXX Need to just update the changed area here; we should keep track
+            // of the rectangle based on Redraw args.
+            aOldLayer->Updated(nsIntRect(0, 0, mWidth, mHeight));
+            MarkContextClean();
+            HTMLCanvasElement()->GetPrimaryCanvasFrame()->MarkLayersActive();
+        }
+
         return aOldLayer;
     }
 
     nsRefPtr<CanvasLayer> canvasLayer = aManager->CreateCanvasLayer();
     if (!canvasLayer) {
         NS_WARNING("CreateCanvasLayer returned null!");
         return nsnull;
     }
-    CanvasRenderingContext2DUserData *userData = nsnull;
-    if (aBuilder->IsPaintingToWindow()) {
-      // Make the layer tell us whenever a transaction finishes (including
-      // the current transaction), so we can clear our invalidation state and
-      // start invalidating again. We need to do this for the layer that is
-      // being painted to a window (there shouldn't be more than one at a time,
-      // and if there is, flushing the invalidation state more often than
-      // necessary is harmless).
-
-      // The layer will be destroyed when we tear down the presentation
-      // (at the latest), at which time this userData will be destroyed,
-      // releasing the reference to the element.
-      // The userData will receive DidTransactionCallbacks, which flush the
-      // the invalidation state to indicate that the canvas is up to date.
-      userData = new CanvasRenderingContext2DUserData(HTMLCanvasElement());
-      canvasLayer->SetDidTransactionCallback(
-              CanvasRenderingContext2DUserData::DidTransactionCallback, userData);
-    }
-    canvasLayer->SetUserData(&g2DContextLayerUserData, userData);
+    canvasLayer->SetUserData(&g2DContextLayerUserData, nsnull);
 
     CanvasLayer::Data data;
 
     data.mSurface = mSurface.get();
     data.mSize = nsIntSize(mWidth, mHeight);
 
     canvasLayer->Initialize(data);
     PRUint32 flags = mOpaque ? Layer::CONTENT_OPAQUE : 0;
     canvasLayer->SetContentFlags(flags);
-    canvasLayer->Updated();
+    canvasLayer->Updated(nsIntRect(0, 0, mWidth, mHeight));
 
     mResetLayer = PR_FALSE;
 
-    return canvasLayer.forget();
+    MarkContextClean();
+
+    return canvasLayer.forget().get();
 }
 
 void
 nsCanvasRenderingContext2D::MarkContextClean()
 {
     if (mInvalidateCount > 0) {
         mPredictManyRedrawCalls = mInvalidateCount > kCanvasMaxInvalidateCount;
     }
--- a/content/canvas/test/test_canvas.html
+++ b/content/canvas/test/test_canvas.html
@@ -7762,24 +7762,24 @@ var _thrown_outer = false;
 try {
 
 ctx.fillStyle = '#000';
 ctx.fillRect(0, 0, 100, 50);
 ctx.fillStyle = '#fff';
 ctx.fillRect(20, 10, 60, 10);
 
 var imgdata1 = ctx.getImageData(85, 25, -10, -10);
-ok(imgdata1.data[0] === 0, "imgdata1.data[\""+(0)+"\"] === 0");
-ok(imgdata1.data[1] === 0, "imgdata1.data[\""+(1)+"\"] === 0");
-ok(imgdata1.data[2] === 0, "imgdata1.data[\""+(2)+"\"] === 0");
+ok(imgdata1.data[0] === 255, "imgdata1.data[\""+(0)+"\"] === 255");
+ok(imgdata1.data[1] === 255, "imgdata1.data[\""+(1)+"\"] === 255");
+ok(imgdata1.data[2] === 255, "imgdata1.data[\""+(2)+"\"] === 255");
 ok(imgdata1.data[3] === 255, "imgdata1.data[\""+(3)+"\"] === 255");
-ok(imgdata1.data[imgdata1.length-4+0] === 255, "imgdata1.data[imgdata1.length-4+0] === 255");
-ok(imgdata1.data[imgdata1.length-4+1] === 255, "imgdata1.data[imgdata1.length-4+1] === 255");
-ok(imgdata1.data[imgdata1.length-4+2] === 255, "imgdata1.data[imgdata1.length-4+2] === 255");
-ok(imgdata1.data[imgdata1.length-4+3] === 255, "imgdata1.data[imgdata1.length-4+3] === 255");
+ok(imgdata1.data[imgdata1.data.length-4+0] === 0, "imgdata1.data[imgdata1.data.length-4+0] === 0");
+ok(imgdata1.data[imgdata1.data.length-4+1] === 0, "imgdata1.data[imgdata1.data.length-4+1] === 0");
+ok(imgdata1.data[imgdata1.data.length-4+2] === 0, "imgdata1.data[imgdata1.data.length-4+2] === 0");
+ok(imgdata1.data[imgdata1.data.length-4+3] === 255, "imgdata1.data[imgdata1.data.length-4+3] === 255");
 
 var imgdata2 = ctx.getImageData(0, 0, -1, -1);
 ok(imgdata2.data[0] === 0, "imgdata2.data[\""+(0)+"\"] === 0");
 ok(imgdata2.data[1] === 0, "imgdata2.data[\""+(1)+"\"] === 0");
 ok(imgdata2.data[2] === 0, "imgdata2.data[\""+(2)+"\"] === 0");
 ok(imgdata2.data[3] === 0, "imgdata2.data[\""+(3)+"\"] === 0");
 
 } catch (e) {
--- a/content/events/public/nsIEventStateManager.h
+++ b/content/events/public/nsIEventStateManager.h
@@ -95,19 +95,22 @@ public:
    *                      will pretend that those states are also set on aContent.
    * @return              The content state.
    */
   virtual nsEventStates GetContentState(nsIContent *aContent,
                                         PRBool aFollowLabels = PR_FALSE) = 0;
 
   /**
    * Notify that the given NS_EVENT_STATE_* bit has changed for this content.
-   * @param aContent Content which has changed states
-   * @param aState   Corresponding state flags such as NS_EVENT_STATE_FOCUS 
-   *                 defined in the nsIEventStateManager interface
+   * @param aContent Content which has changed states.  This may be null to
+   *                 indicate that nothing is in this state anymore.
+   * @param aState   One of NS_EVENT_STATE_ACTIVE, NS_EVENT_STATE_HOVER,
+   *                 NS_EVENT_STATE_DRAGOVER, NS_EVENT_STATE_URLTARGET.  Don't
+   *                 pass anything else!  Passing in a state object that has
+   *                 more than one of those states set is not supported.
    * @return  Whether the content was able to change all states. Returns PR_FALSE
    *                  if a resulting DOM event causes the content node passed in
    *                  to not change states. Note, the frame for the content may
    *                  change as a result of the content state change, because of
    *                  frame reconstructions that may occur, but this does not
    *                  affect the return value.
    */
   virtual PRBool SetContentState(nsIContent *aContent, nsEventStates aState) = 0;
--- a/content/events/src/nsEventStateManager.cpp
+++ b/content/events/src/nsEventStateManager.cpp
@@ -153,16 +153,17 @@
 #include "nsDOMDataTransfer.h"
 #include "nsContentAreaDragDrop.h"
 #ifdef MOZ_XUL
 #include "nsTreeBodyFrame.h"
 #endif
 #include "nsIController.h"
 #include "nsICommandParams.h"
 #include "mozilla/Services.h"
+#include "mozAutoDocUpdate.h"
 
 #ifdef XP_MACOSX
 #import <ApplicationServices/ApplicationServices.h>
 #endif
 
 #ifdef MOZ_IPC
 using namespace mozilla::dom;
 #endif
@@ -4217,16 +4218,28 @@ IsAncestorOf(nsIContent* aPossibleAncest
       nsCOMPtr<nsIContent> labelTarget = GetLabelTarget(aPossibleDescendant);
       if (labelTarget == aPossibleAncestor)
         return true;
     }
   }
   return false;
 }
 
+static bool
+ShouldShowFocusRing(nsIContent* aContent)
+{
+  nsIDocument* doc = aContent->GetOwnerDoc();
+  if (doc) {
+    nsPIDOMWindow* window = doc->GetWindow();
+    return window && window->ShouldShowFocusRing();
+  }
+
+  return false;
+}
+
 nsEventStates
 nsEventStateManager::GetContentState(nsIContent *aContent, PRBool aFollowLabels)
 {
   nsEventStates state = aContent->IntrinsicState();
 
   if (IsAncestorOf(aContent, mActiveContent, aFollowLabels)) {
     state |= NS_EVENT_STATE_ACTIVE;
   }
@@ -4234,22 +4247,18 @@ nsEventStateManager::GetContentState(nsI
     state |= NS_EVENT_STATE_HOVER;
   }
 
   nsFocusManager* fm = nsFocusManager::GetFocusManager();
   nsIContent* focusedContent = fm ? fm->GetFocusedContent() : nsnull;
   if (aContent == focusedContent) {
     state |= NS_EVENT_STATE_FOCUS;
 
-    nsIDocument* doc = focusedContent->GetOwnerDoc();
-    if (doc) {
-      nsPIDOMWindow* window = doc->GetWindow();
-      if (window && window->ShouldShowFocusRing()) {
-        state |= NS_EVENT_STATE_FOCUSRING;
-      }
+    if (ShouldShowFocusRing(aContent)) {
+      state |= NS_EVENT_STATE_FOCUSRING;
     }
   }
   if (aContent == mDragOverContent) {
     state |= NS_EVENT_STATE_DRAGOVER;
   }
   if (aContent == mURLTargetContent) {
     state |= NS_EVENT_STATE_URLTARGET;
   }
@@ -4300,214 +4309,129 @@ static nsIContent* FindCommonAncestor(ns
   return nsnull;
 }
 
 static void
 NotifyAncestors(nsIDocument* aDocument, nsIContent* aStartNode,
                 nsIContent* aStopBefore, nsEventStates aState)
 {
   while (aStartNode && aStartNode != aStopBefore) {
-    aDocument->ContentStatesChanged(aStartNode, nsnull, aState);
+    aDocument->ContentStateChanged(aStartNode, aState);
     nsCOMPtr<nsIContent> labelTarget = GetLabelTarget(aStartNode);
     if (labelTarget) {
-      aDocument->ContentStatesChanged(labelTarget, nsnull, aState);
+      aDocument->ContentStateChanged(labelTarget, aState);
     }
     aStartNode = aStartNode->GetParent();
   }
 }
 
 PRBool
 nsEventStateManager::SetContentState(nsIContent *aContent, nsEventStates aState)
 {
-  const PRInt32 maxNotify = 5;
-  // We must initialize this array with memset for the sake of the boneheaded
-  // OS X compiler.  See bug 134934.
-  nsIContent  *notifyContent[maxNotify];
-  memset(notifyContent, 0, sizeof(notifyContent));
-
-  // check to see that this state is allowed by style. Check dragover too?
-  // XXX This doesn't consider that |aState| is a bitfield.
-  // XXX Is this even what we want?
-  if (mCurrentTarget && (aState == NS_EVENT_STATE_ACTIVE || aState == NS_EVENT_STATE_HOVER))
-  {
-    const nsStyleUserInterface* ui = mCurrentTarget->GetStyleUserInterface();
-    if (ui->mUserInput == NS_STYLE_USER_INPUT_NONE)
-      return PR_FALSE;
-  }
-
-  if (aState.HasState(NS_EVENT_STATE_DRAGOVER) && aContent != mDragOverContent) {
-    notifyContent[3] = mDragOverContent; // notify dragover first, since more common case
-    NS_IF_ADDREF(notifyContent[3]);
-    mDragOverContent = aContent;
-  }
-
-  if (aState.HasState(NS_EVENT_STATE_URLTARGET) && aContent != mURLTargetContent) {
-    notifyContent[4] = mURLTargetContent;
-    NS_IF_ADDREF(notifyContent[4]);
-    mURLTargetContent = aContent;
-  }
-
-  nsCOMPtr<nsIContent> commonActiveAncestor, oldActive, newActive;
-  if (aState.HasState(NS_EVENT_STATE_ACTIVE) && aContent != mActiveContent) {
-    oldActive = mActiveContent;
-    newActive = aContent;
-    commonActiveAncestor = FindCommonAncestor(mActiveContent, aContent);
-    mActiveContent = aContent;
-  }
-
-  nsCOMPtr<nsIContent> commonHoverAncestor, oldHover, newHover;
-  if (aState.HasState(NS_EVENT_STATE_HOVER) && aContent != mHoverContent) {
-    oldHover = mHoverContent;
-
-    if (mPresContext->IsDynamic()) {
-      newHover = aContent;
+  // We manage 4 states here: ACTIVE, HOVER, DRAGOVER, URLTARGET
+  // The input must be exactly one of them.
+  NS_PRECONDITION(aState == NS_EVENT_STATE_ACTIVE ||
+                  aState == NS_EVENT_STATE_HOVER ||
+                  aState == NS_EVENT_STATE_DRAGOVER ||
+                  aState == NS_EVENT_STATE_URLTARGET,
+                  "Unexpected state");
+
+  nsCOMPtr<nsIContent> notifyContent1;
+  nsCOMPtr<nsIContent> notifyContent2;
+  PRBool notifyAncestors;
+
+  if (aState == NS_EVENT_STATE_HOVER || aState == NS_EVENT_STATE_ACTIVE) {
+    // Hover and active are hierarchical
+    notifyAncestors = PR_TRUE;
+
+    // check to see that this state is allowed by style. Check dragover too?
+    // XXX Is this even what we want?
+    if (mCurrentTarget)
+    {
+      const nsStyleUserInterface* ui = mCurrentTarget->GetStyleUserInterface();
+      if (ui->mUserInput == NS_STYLE_USER_INPUT_NONE)
+        return PR_FALSE;
+    }
+
+    if (aState == NS_EVENT_STATE_ACTIVE) {
+      if (aContent != mActiveContent) {
+        notifyContent1 = aContent;
+        notifyContent2 = mActiveContent;
+        mActiveContent = aContent;
+      }
     } else {
-      NS_ASSERTION(!aContent ||
-                   aContent->GetCurrentDoc() == mPresContext->PresShell()->GetDocument(),
-                   "Unexpected document");
-      nsIFrame *frame = aContent ? aContent->GetPrimaryFrame() : nsnull;
-      if (frame && nsLayoutUtils::IsViewportScrollbarFrame(frame)) {
-        // The scrollbars of viewport should not ignore the hover state.
-        // Because they are *not* the content of the web page.
+      NS_ASSERTION(aState == NS_EVENT_STATE_HOVER, "How did that happen?");
+      nsIContent* newHover;
+      
+      if (mPresContext->IsDynamic()) {
         newHover = aContent;
       } else {
-        // All contents of the web page should ignore the hover state.
-        newHover = nsnull;
+        NS_ASSERTION(!aContent ||
+                     aContent->GetCurrentDoc() == mPresContext->PresShell()->GetDocument(),
+                     "Unexpected document");
+        nsIFrame *frame = aContent ? aContent->GetPrimaryFrame() : nsnull;
+        if (frame && nsLayoutUtils::IsViewportScrollbarFrame(frame)) {
+          // The scrollbars of viewport should not ignore the hover state.
+          // Because they are *not* the content of the web page.
+          newHover = aContent;
+        } else {
+          // All contents of the web page should ignore the hover state.
+          newHover = nsnull;
+        }
+      }
+
+      if (newHover != mHoverContent) {
+        notifyContent1 = newHover;
+        notifyContent2 = mHoverContent;
+        mHoverContent = newHover;
       }
     }
-
-    commonHoverAncestor = FindCommonAncestor(mHoverContent, aContent);
-    mHoverContent = aContent;
-  }
-
-  if (aState.HasState(NS_EVENT_STATE_FOCUS)) {
-    aState |= NS_EVENT_STATE_FOCUSRING;
-    notifyContent[2] = aContent;
-    NS_IF_ADDREF(notifyContent[2]);
-  }
-
-  nsEventStates simpleStates = aState;
-  simpleStates &= ~(NS_EVENT_STATE_ACTIVE|NS_EVENT_STATE_HOVER);
-
-  if (aContent && !simpleStates.IsEmpty()) {
-    // notify about new content too
-    notifyContent[0] = aContent;
-    NS_ADDREF(aContent);  // everything in notify array has a ref
-  }
-
-  // remove duplicates
-  if ((notifyContent[4] == notifyContent[3]) || (notifyContent[4] == notifyContent[2]) || (notifyContent[4] == notifyContent[1])) {
-    NS_IF_RELEASE(notifyContent[4]);
-  }
-  // remove duplicates
-  if ((notifyContent[3] == notifyContent[2]) || (notifyContent[3] == notifyContent[1])) {
-    NS_IF_RELEASE(notifyContent[3]);
-  }
-  if (notifyContent[2] == notifyContent[1]) {
-    NS_IF_RELEASE(notifyContent[2]);
-  }
-
-  // remove notifications for content not in document.
-  // we may decide this is possible later but right now it has problems.
-  for  (int i = 0; i < maxNotify; i++) {
-    if (notifyContent[i] &&
-        !notifyContent[i]->GetDocument()) {
-      NS_RELEASE(notifyContent[i]);
-    }
-  }
-
-  // compress the notify array to group notifications tighter
-  nsIContent** from = &(notifyContent[0]);
-  nsIContent** to   = &(notifyContent[0]);
-  nsIContent** end  = &(notifyContent[maxNotify]);
-
-  while (from < end) {
-    if (! *from) {
-      while (++from < end) {
-        if (*from) {
-          *to++ = *from;
-          *from = nsnull;
-          break;
-        }
+  } else {
+    notifyAncestors = PR_FALSE;
+    if (aState == NS_EVENT_STATE_DRAGOVER) {
+      if (aContent != mDragOverContent) {
+        notifyContent1 = aContent;
+        notifyContent2 = mDragOverContent;
+        mDragOverContent = aContent;
       }
-    }
-    else {
-      if (from == to) {
-        to++;
-        from++;
-      }
-      else {
-        *to++ = *from;
-        *from++ = nsnull;
+    } else if (aState == NS_EVENT_STATE_URLTARGET) {
+      if (aContent != mURLTargetContent) {
+        notifyContent1 = aContent;
+        notifyContent2 = mURLTargetContent;
+        mURLTargetContent = aContent;
       }
     }
   }
 
-  if (notifyContent[0] || newHover || oldHover || newActive || oldActive) {
-    // have at least one to notify about
-    nsCOMPtr<nsIDocument> doc1, doc2;  // this presumes content can't get/lose state if not connected to doc
-    if (notifyContent[0]) {
-      doc1 = notifyContent[0]->GetDocument();
-      if (notifyContent[1]) {
-        //For :focus this might be a different doc so check
-        doc2 = notifyContent[1]->GetDocument();
-        if (doc1 == doc2) {
-          doc2 = nsnull;
+  if (!notifyContent1) {
+    // This is ok because FindCommonAncestor wouldn't find anything
+    // anyway if notifyContent1 is null.
+    notifyContent1 = notifyContent2;
+    notifyContent2 = nsnull;
+  }
+
+  if (notifyContent1 && mPresContext) {
+    EnsureDocument(mPresContext);
+    if (mDocument) {
+      MOZ_AUTO_DOC_UPDATE(mDocument, UPDATE_CONTENT_STATE, PR_TRUE);
+
+      if (notifyAncestors) {
+        nsCOMPtr<nsIContent> commonAncestor =
+          FindCommonAncestor(notifyContent1, notifyContent2);
+        NotifyAncestors(mDocument, notifyContent1, commonAncestor, aState);
+        if (notifyContent2) {
+          NotifyAncestors(mDocument, notifyContent2, commonAncestor, aState);
+        }
+      } else {
+        mDocument->ContentStateChanged(notifyContent1, aState);
+        if (notifyContent2) {
+          mDocument->ContentStateChanged(notifyContent2, aState);
         }
       }
     }
-    else {
-      EnsureDocument(mPresContext);
-      doc1 = mDocument;
-    }
-    if (doc1) {
-      doc1->BeginUpdate(UPDATE_CONTENT_STATE);
-
-      NotifyAncestors(doc1, newActive, commonActiveAncestor, NS_EVENT_STATE_ACTIVE);
-      NotifyAncestors(doc1, oldActive, commonActiveAncestor, NS_EVENT_STATE_ACTIVE);
-      NotifyAncestors(doc1, newHover, commonHoverAncestor, NS_EVENT_STATE_HOVER);
-      NotifyAncestors(doc1, oldHover, commonHoverAncestor, NS_EVENT_STATE_HOVER);
-
-      if (notifyContent[0]) {
-        doc1->ContentStatesChanged(notifyContent[0], notifyContent[1],
-                                   simpleStates);
-        if (notifyContent[2]) {
-          // more that two notifications are needed (should be rare)
-          // XXX a further optimization here would be to group the
-          // notification pairs together by parent/child, only needed if
-          // more than two content changed (ie: if [0] and [2] are
-          // parent/child, then notify (0,2) (1,3))
-          doc1->ContentStatesChanged(notifyContent[2], notifyContent[3],
-                                     simpleStates);
-          if (notifyContent[4]) {
-            // more that four notifications are needed (should be rare)
-            doc1->ContentStatesChanged(notifyContent[4], nsnull,
-                                       simpleStates);
-          }
-        }
-      }
-      doc1->EndUpdate(UPDATE_CONTENT_STATE);
-
-      if (doc2) {
-        doc2->BeginUpdate(UPDATE_CONTENT_STATE);
-        doc2->ContentStatesChanged(notifyContent[1], notifyContent[2],
-                                   simpleStates);
-        if (notifyContent[3]) {
-          doc1->ContentStatesChanged(notifyContent[3], notifyContent[4],
-                                     simpleStates);
-        }
-        doc2->EndUpdate(UPDATE_CONTENT_STATE);
-      }
-    }
-
-    from = &(notifyContent[0]);
-    while (from < to) {  // release old refs now that we are through
-      nsIContent* notify = *from++;
-      NS_RELEASE(notify);
-    }
   }
 
   return PR_TRUE;
 }
 
 NS_IMETHODIMP
 nsEventStateManager::ContentRemoved(nsIDocument* aDocument, nsIContent* aContent)
 {
--- a/content/html/content/public/nsHTMLCanvasElement.h
+++ b/content/html/content/public/nsHTMLCanvasElement.h
@@ -152,22 +152,22 @@ public:
                            PRBool aNotify);
   virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const;
   nsresult CopyInnerTo(nsGenericElement* aDest) const;
 
   /*
    * Helpers called by various users of Canvas
    */
 
-  already_AddRefed<CanvasLayer> GetCanvasLayer(nsDisplayListBuilder* aBuilder,
-                                               CanvasLayer *aOldLayer,
+  already_AddRefed<CanvasLayer> GetCanvasLayer(CanvasLayer *aOldLayer,
                                                LayerManager *aManager);
 
-  // Any invalidates requested by the context have been processed by updating
-  // the window. Future changes to the canvas need to trigger more invalidation.
+  // Tell the Context that all the current rendering that it's
+  // invalidated has been displayed to the screen, so that it should
+  // start requesting invalidates again as needed.
   void MarkContextClean();
 
   virtual nsXPCClassInfo* GetClassInfo();
 protected:
   nsIntSize GetWidthHeight();
 
   nsresult UpdateContext(nsIPropertyBag *aNewContextOptions = nsnull);
   nsresult ExtractData(const nsAString& aType,
--- a/content/html/content/src/nsFormSubmission.cpp
+++ b/content/html/content/src/nsFormSubmission.cpp
@@ -705,20 +705,19 @@ nsEncodingFormSubmission::nsEncodingForm
 {
   nsCAutoString charset(aCharset);
   // canonical name is passed so that we just have to check against
   // *our* canonical names listed in charsetaliases.properties
   if (charset.EqualsLiteral("ISO-8859-1")) {
     charset.AssignLiteral("windows-1252");
   }
 
-  // use UTF-8 for UTF-16* and UTF-32* (per WHATWG and existing practice of
+  // use UTF-8 for UTF-16* (per WHATWG and existing practice of
   // MS IE/Opera). 
-  if (StringBeginsWith(charset, NS_LITERAL_CSTRING("UTF-16")) || 
-      StringBeginsWith(charset, NS_LITERAL_CSTRING("UTF-32"))) {
+  if (StringBeginsWith(charset, NS_LITERAL_CSTRING("UTF-16"))) {
     charset.AssignLiteral("UTF-8");
   }
 
   mEncoder = do_CreateInstance(NS_SAVEASCHARSET_CONTRACTID);
   if (mEncoder) {
     nsresult rv =
       mEncoder->Init(charset.get(),
                      (nsISaveAsCharset::attr_EntityAfterCharsetConv + 
--- a/content/html/content/src/nsGenericHTMLElement.cpp
+++ b/content/html/content/src/nsGenericHTMLElement.cpp
@@ -2618,17 +2618,17 @@ nsGenericHTMLFormElement::BeforeSetAttr(
       mForm->RemoveElement(this, false);
 
       // Removing the element from the form can make it not be the default
       // control anymore.  Go ahead and notify on that change, though we might
       // end up readding and becoming the default control again in
       // AfterSetAttr.
       if (doc && aNotify) {
         MOZ_AUTO_DOC_UPDATE(doc, UPDATE_CONTENT_STATE, PR_TRUE);
-        doc->ContentStatesChanged(this, nsnull, NS_EVENT_STATE_DEFAULT);
+        doc->ContentStateChanged(this, NS_EVENT_STATE_DEFAULT);
       }
     }
 
     if (aName == nsGkAtoms::form) {
       // If @form isn't set or set to the empty string, there were no observer
       // so we don't have to remove it.
       if (nsContentUtils::HasNonEmptyAttr(this, kNameSpaceID_None,
                                           nsGkAtoms::form)) {
@@ -2676,17 +2676,17 @@ nsGenericHTMLFormElement::AfterSetAttr(P
       mForm->AddElement(this, false, aNotify);
 
       // Adding the element to the form can make it be the default control .
       // Go ahead and notify on that change.
       // Note: no need to notify on CanBeDisabled(), since type attr
       // changes can't affect that.
       if (doc && aNotify) {
         MOZ_AUTO_DOC_UPDATE(doc, UPDATE_CONTENT_STATE, PR_TRUE);
-        doc->ContentStatesChanged(this, nsnull, NS_EVENT_STATE_DEFAULT);
+        doc->ContentStateChanged(this, NS_EVENT_STATE_DEFAULT);
       }
     }
 
     if (aName == nsGkAtoms::form) {
       // We need a new form id observer.
       nsIDocument* doc = GetCurrentDoc();
       if (doc) {
         Element* formIdElement = nsnull;
@@ -2992,17 +2992,17 @@ nsGenericHTMLFormElement::FieldSetDisabl
     return;
   }
 
   aStates |= NS_EVENT_STATE_DISABLED | NS_EVENT_STATE_ENABLED;
 
   nsIDocument* doc = GetCurrentDoc();
   if (doc) {
     MOZ_AUTO_DOC_UPDATE(doc, UPDATE_CONTENT_STATE, PR_TRUE);
-    doc->ContentStatesChanged(this, nsnull, aStates);
+    doc->ContentStateChanged(this, aStates);
   }
 }
 
 //----------------------------------------------------------------------
 
 nsGenericHTMLFrameElement::~nsGenericHTMLFrameElement()
 {
   if (mFrameLoader) {
@@ -3490,19 +3490,19 @@ nsGenericHTMLElement::IsEditableRoot() c
 static void
 MakeContentDescendantsEditable(nsIContent *aContent, nsIDocument *aDocument)
 {
   nsEventStates stateBefore = aContent->IntrinsicState();
 
   aContent->UpdateEditableState();
 
   if (aDocument && stateBefore != aContent->IntrinsicState()) {
-    aDocument->ContentStatesChanged(aContent, nsnull,
-                                    NS_EVENT_STATE_MOZ_READONLY |
-                                    NS_EVENT_STATE_MOZ_READWRITE);
+    aDocument->ContentStateChanged(aContent,
+                                   NS_EVENT_STATE_MOZ_READONLY |
+                                   NS_EVENT_STATE_MOZ_READWRITE);
   }
 
   PRUint32 i, n = aContent->GetChildCount();
   for (i = 0; i < n; ++i) {
     nsIContent *child = aContent->GetChildAt(i);
     if (!child->HasAttr(kNameSpaceID_None, nsGkAtoms::contenteditable)) {
       MakeContentDescendantsEditable(child, aDocument);
     }
@@ -3524,14 +3524,14 @@ nsGenericHTMLElement::ChangeEditableStat
       htmlDocument->ChangeContentEditableCount(this, aChange);
     }
   }
 
   if (document->HasFlag(NODE_IS_EDITABLE)) {
     document = nsnull;
   }
 
-  // MakeContentDescendantsEditable is going to call ContentStatesChanged for
+  // MakeContentDescendantsEditable is going to call ContentStateChanged for
   // this element and all descendants if editable state has changed.
   // We have to create a document update batch now so it's created once.
   MOZ_AUTO_DOC_UPDATE(document, UPDATE_CONTENT_STATE, PR_TRUE);
   MakeContentDescendantsEditable(this, document);
 }
--- a/content/html/content/src/nsHTMLButtonElement.cpp
+++ b/content/html/content/src/nsHTMLButtonElement.cpp
@@ -627,17 +627,17 @@ nsHTMLButtonElement::AfterSetAttr(PRInt3
 
       states |= NS_EVENT_STATE_MOZ_SUBMITINVALID;
     }
 
     if (aNotify && !states.IsEmpty()) {
       nsIDocument* doc = GetCurrentDoc();
       if (doc) {
         MOZ_AUTO_DOC_UPDATE(doc, UPDATE_CONTENT_STATE, PR_TRUE);
-        doc->ContentStatesChanged(this, nsnull, states);
+        doc->ContentStateChanged(this, states);
       }
     }
   }
 
   return nsGenericHTMLFormElement::AfterSetAttr(aNameSpaceID, aName,
                                                 aValue, aNotify);
 }
 
--- a/content/html/content/src/nsHTMLCanvasElement.cpp
+++ b/content/html/content/src/nsHTMLCanvasElement.cpp
@@ -643,42 +643,39 @@ void
 nsHTMLCanvasElement::InvalidateFrame(const gfxRect* damageRect)
 {
   // We don't need to flush anything here; if there's no frame or if
   // we plan to reframe we don't need to invalidate it anyway.
   nsIFrame *frame = GetPrimaryFrame();
   if (!frame)
     return;
 
-  frame->MarkLayersActive();
-
-  nsRect invalRect;
-  nsRect contentArea = frame->GetContentRect();
   if (damageRect) {
+    nsRect contentArea(frame->GetContentRect());
     nsIntSize size = GetWidthHeight();
 
     // damageRect and size are in CSS pixels; contentArea is in appunits
     // We want a rect in appunits; so avoid doing pixels-to-appunits and
     // vice versa conversion here.
     gfxRect realRect(*damageRect);
     realRect.Scale(contentArea.width / gfxFloat(size.width),
                    contentArea.height / gfxFloat(size.height));
     realRect.RoundOut();
 
     // then make it a nsRect
-    invalRect = nsRect(realRect.X(), realRect.Y(),
-                       realRect.Width(), realRect.Height());
+    nsRect invalRect(realRect.X(), realRect.Y(),
+                     realRect.Width(), realRect.Height());
+
+    // account for border/padding
+    invalRect.MoveBy(contentArea.TopLeft() - frame->GetPosition());
+
+    frame->InvalidateLayer(invalRect, nsDisplayItem::TYPE_CANVAS);
   } else {
-    invalRect = nsRect(nsPoint(0, 0), contentArea.Size());
-  }
-  invalRect.MoveBy(contentArea.TopLeft() - frame->GetPosition());
-
-  Layer* layer = frame->InvalidateLayer(invalRect, nsDisplayItem::TYPE_CANVAS);
-  if (layer) {
-    static_cast<CanvasLayer*>(layer)->Updated();
+    nsRect r(frame->GetContentRect() - frame->GetPosition());
+    frame->InvalidateLayer(r, nsDisplayItem::TYPE_CANVAS);
   }
 }
 
 PRInt32
 nsHTMLCanvasElement::CountContexts()
 {
   if (mCurrentContext)
     return 1;
@@ -697,24 +694,23 @@ nsHTMLCanvasElement::GetContextAtIndex (
 
 PRBool
 nsHTMLCanvasElement::GetIsOpaque()
 {
   return HasAttr(kNameSpaceID_None, nsGkAtoms::moz_opaque);
 }
 
 already_AddRefed<CanvasLayer>
-nsHTMLCanvasElement::GetCanvasLayer(nsDisplayListBuilder* aBuilder,
-                                    CanvasLayer *aOldLayer,
+nsHTMLCanvasElement::GetCanvasLayer(CanvasLayer *aOldLayer,
                                     LayerManager *aManager)
 {
   if (!mCurrentContext)
     return nsnull;
 
-  return mCurrentContext->GetCanvasLayer(aBuilder, aOldLayer, aManager);
+  return mCurrentContext->GetCanvasLayer(aOldLayer, aManager);
 }
 
 void
 nsHTMLCanvasElement::MarkContextClean()
 {
   if (!mCurrentContext)
     return;
 
--- a/content/html/content/src/nsHTMLFormElement.cpp
+++ b/content/html/content/src/nsHTMLFormElement.cpp
@@ -389,26 +389,26 @@ nsHTMLFormElement::AfterSetAttr(PRInt32 
     // Update all form elements states because they might be [no longer]
     // affected by :-moz-ui-valid or :-moz-ui-invalid.
     nsIDocument* doc = GetCurrentDoc();
     if (doc) {
       MOZ_AUTO_DOC_UPDATE(doc, UPDATE_CONTENT_STATE, PR_TRUE);
 
       for (PRUint32 i = 0, length = mControls->mElements.Length();
            i < length; ++i) {
-        doc->ContentStatesChanged(mControls->mElements[i], nsnull,
-                                  NS_EVENT_STATE_MOZ_UI_VALID |
-                                  NS_EVENT_STATE_MOZ_UI_INVALID);
+        doc->ContentStateChanged(mControls->mElements[i],
+                                 NS_EVENT_STATE_MOZ_UI_VALID |
+                                 NS_EVENT_STATE_MOZ_UI_INVALID);
       }
 
       for (PRUint32 i = 0, length = mControls->mNotInElements.Length();
            i < length; ++i) {
-        doc->ContentStatesChanged(mControls->mNotInElements[i], nsnull,
-                                  NS_EVENT_STATE_MOZ_UI_VALID |
-                                  NS_EVENT_STATE_MOZ_UI_INVALID);
+        doc->ContentStateChanged(mControls->mNotInElements[i],
+                                 NS_EVENT_STATE_MOZ_UI_VALID |
+                                 NS_EVENT_STATE_MOZ_UI_INVALID);
       }
     }
   }
 
   return nsGenericHTMLElement::AfterSetAttr(aNameSpaceID, aName, aValue, aNotify);
 }
 
 NS_IMPL_STRING_ATTR(nsHTMLFormElement, AcceptCharset, acceptcharset)
@@ -542,17 +542,17 @@ CollectOrphans(nsINode* aRemovalRoot, ns
 
         // In addition, submit controls shouldn't have
         // NS_EVENT_STATE_MOZ_SUBMITINVALID applying if they do not have a form.
         if (node->IsSubmitControl()) {
           states |= NS_EVENT_STATE_MOZ_SUBMITINVALID;
         }
 
         if (doc) {
-          doc->ContentStatesChanged(node, nsnull, states);
+          doc->ContentStateChanged(node, states);
         }
 #ifdef DEBUG
         removed = PR_TRUE;
 #endif
       }
     }
 
 #ifdef DEBUG
@@ -1229,26 +1229,25 @@ nsHTMLFormElement::AddElement(nsGenericH
     }
     NS_POSTCONDITION(mDefaultSubmitElement == mFirstSubmitInElements ||
                      mDefaultSubmitElement == mFirstSubmitNotInElements ||
                      !mDefaultSubmitElement,
                      "What happened here?");
 
     // Notify that the state of the previous default submit element has changed
     // if the element which is the default submit element has changed.  The new
-    // default submit element is responsible for its own ContentStatesChanged
+    // default submit element is responsible for its own ContentStateChanged
     // call.
     if (aNotify && oldDefaultSubmit &&
         oldDefaultSubmit != mDefaultSubmitElement) {
       nsIDocument* document = GetCurrentDoc();
       if (document) {
         MOZ_AUTO_DOC_UPDATE(document, UPDATE_CONTENT_STATE, PR_TRUE);
         nsCOMPtr<nsIContent> oldElement(do_QueryInterface(oldDefaultSubmit));
-        document->ContentStatesChanged(oldElement, nsnull,
-                                       NS_EVENT_STATE_DEFAULT);
+        document->ContentStateChanged(oldElement, NS_EVENT_STATE_DEFAULT);
       }
     }
   }
 
   // If the element is subject to constraint validaton and is invalid, we need
   // to update our internal counter.
   if (aUpdateValidity) {
     nsCOMPtr<nsIConstraintValidation> cvElmt =
@@ -1375,18 +1374,18 @@ nsHTMLFormElement::HandleDefaultSubmitRe
                    mDefaultSubmitElement == mFirstSubmitNotInElements,
                    "What happened here?");
 
   // Notify about change if needed.
   if (mDefaultSubmitElement) {
     nsIDocument* document = GetCurrentDoc();
     if (document) {
       MOZ_AUTO_DOC_UPDATE(document, UPDATE_CONTENT_STATE, PR_TRUE);
-      document->ContentStatesChanged(mDefaultSubmitElement, nsnull,
-                                     NS_EVENT_STATE_DEFAULT);
+      document->ContentStateChanged(mDefaultSubmitElement,
+                                    NS_EVENT_STATE_DEFAULT);
     }
   }
 }
 
 nsresult
 nsHTMLFormElement::RemoveElementFromTable(nsGenericHTMLFormElement* aElement,
                                           const nsAString& aName)
 {
@@ -1749,46 +1748,46 @@ nsHTMLFormElement::CheckValidFormSubmiss
       // We have to do that _before_ calling the observers so we are sure they
       // will not interfere (like focusing the element).
       if (!mEverTriedInvalidSubmit) {
         mEverTriedInvalidSubmit = true;
 
         nsIDocument* doc = GetCurrentDoc();
         if (doc) {
           /*
-           * We are going to call ContentStatesChanged assuming elements want to
+           * We are going to call ContentStateChanged assuming elements want to
            * be notified because we can't know.
            * Submissions shouldn't happen during parsing so it _should_ be safe.
            */
 
           MOZ_AUTO_DOC_UPDATE(doc, UPDATE_CONTENT_STATE, PR_TRUE);
 
           for (PRUint32 i = 0, length = mControls->mElements.Length();
                i < length; ++i) {
             // Input elements can trigger a form submission and we want to
             // update the style in that case.
             if (mControls->mElements[i]->IsHTML(nsGkAtoms::input) &&
                 nsContentUtils::IsFocusedContent(mControls->mElements[i])) {
               static_cast<nsHTMLInputElement*>(mControls->mElements[i])
                 ->UpdateValidityUIBits(true);
             }
 
-            doc->ContentStatesChanged(mControls->mElements[i], nsnull,
-                                      NS_EVENT_STATE_MOZ_UI_VALID |
-                                      NS_EVENT_STATE_MOZ_UI_INVALID);
+            doc->ContentStateChanged(mControls->mElements[i],
+                                     NS_EVENT_STATE_MOZ_UI_VALID |
+                                     NS_EVENT_STATE_MOZ_UI_INVALID);
           }
 
           // Because of backward compatibility, <input type='image'> is not in
           // elements but can be invalid.
           // TODO: should probably be removed when bug 606491 will be fixed.
           for (PRUint32 i = 0, length = mControls->mNotInElements.Length();
                i < length; ++i) {
-            doc->ContentStatesChanged(mControls->mNotInElements[i], nsnull,
-                                      NS_EVENT_STATE_MOZ_UI_VALID |
-                                      NS_EVENT_STATE_MOZ_UI_INVALID);
+            doc->ContentStateChanged(mControls->mNotInElements[i],
+                                     NS_EVENT_STATE_MOZ_UI_VALID |
+                                     NS_EVENT_STATE_MOZ_UI_INVALID);
           }
         }
       }
 
       nsCOMPtr<nsISupports> inst;
       nsCOMPtr<nsIFormSubmitObserver> observer;
       PRBool more = PR_TRUE;
       while (NS_SUCCEEDED(theEnum->HasMoreElements(&more)) && more) {
@@ -1833,40 +1832,40 @@ nsHTMLFormElement::UpdateValidity(PRBool
   }
 
   nsIDocument* doc = GetCurrentDoc();
   if (!doc) {
     return;
   }
 
   /*
-   * We are going to call ContentStatesChanged assuming submit controls want to
+   * We are going to call ContentStateChanged assuming submit controls want to
    * be notified because we can't know.
    * UpdateValidity shouldn't be called so much during parsing so it _should_
    * be safe.
    */
 
   MOZ_AUTO_DOC_UPDATE(doc, UPDATE_CONTENT_STATE, PR_TRUE);
 
   // Inform submit controls that the form validity has changed.
   for (PRUint32 i = 0, length = mControls->mElements.Length();
        i < length; ++i) {
     if (mControls->mElements[i]->IsSubmitControl()) {
-      doc->ContentStatesChanged(mControls->mElements[i], nsnull,
-                                NS_EVENT_STATE_MOZ_SUBMITINVALID);
+      doc->ContentStateChanged(mControls->mElements[i],
+                               NS_EVENT_STATE_MOZ_SUBMITINVALID);
     }
   }
 
   // Because of backward compatibility, <input type='image'> is not in elements
   // so we have to check for controls not in elements too.
   PRUint32 length = mControls->mNotInElements.Length();
   for (PRUint32 i = 0; i < length; ++i) {
     if (mControls->mNotInElements[i]->IsSubmitControl()) {
-      doc->ContentStatesChanged(mControls->mNotInElements[i], nsnull,
-                                NS_EVENT_STATE_MOZ_SUBMITINVALID);
+      doc->ContentStateChanged(mControls->mNotInElements[i],
+                               NS_EVENT_STATE_MOZ_SUBMITINVALID);
     }
   }
 }
 
 // nsIWebProgressListener
 NS_IMETHODIMP
 nsHTMLFormElement::OnStateChange(nsIWebProgress* aWebProgress,
                                  nsIRequest* aRequest,
--- a/content/html/content/src/nsHTMLInputElement.cpp
+++ b/content/html/content/src/nsHTMLInputElement.cpp
@@ -794,17 +794,17 @@ nsHTMLInputElement::BeforeSetAttr(PRInt3
                                                  aValue, aNotify);
 }
 
 nsresult
 nsHTMLInputElement::AfterSetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
                                  const nsAString* aValue,
                                  PRBool aNotify)
 {
-  // States changes that have to be passed to ContentStatesChanged().
+  // States changes that have to be passed to ContentStateChanged().
   nsEventStates states;
 
   if (aNameSpaceID == kNameSpaceID_None) {
     //
     // When name or type changes, radio should be added to radio group.
     // (type changes are handled in the form itself currently)
     // If the parser is not done creating the radio, we also should not do it.
     //
@@ -948,17 +948,17 @@ nsHTMLInputElement::AfterSetAttr(PRInt32
         UpdateEditableState();
       } else if (IsSingleLineTextControl(PR_FALSE) && aName == nsGkAtoms::readonly) {
         UpdateEditableState();
         states |= NS_EVENT_STATE_MOZ_READONLY | NS_EVENT_STATE_MOZ_READWRITE;
       }
 
       if (doc && !states.IsEmpty()) {
         MOZ_AUTO_DOC_UPDATE(doc, UPDATE_CONTENT_STATE, PR_TRUE);
-        doc->ContentStatesChanged(this, nsnull, states);
+        doc->ContentStateChanged(this, states);
       }
     }
   }
 
   return nsGenericHTMLFormElement::AfterSetAttr(aNameSpaceID, aName,
                                                 aValue, aNotify);
 }
 
@@ -1022,17 +1022,17 @@ nsHTMLInputElement::SetIndeterminateInte
     if (frame)
       frame->InvalidateFrameSubtree();
   }
 
   // Notify the document so it can update :indeterminate pseudoclass rules
   nsIDocument* document = GetCurrentDoc();
   if (document) {
     mozAutoDocUpdate upd(document, UPDATE_CONTENT_STATE, PR_TRUE);
-    document->ContentStatesChanged(this, nsnull, NS_EVENT_STATE_INDETERMINATE);
+    document->ContentStateChanged(this, NS_EVENT_STATE_INDETERMINATE);
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsHTMLInputElement::SetIndeterminate(PRBool aValue)
 {
@@ -1439,17 +1439,17 @@ nsHTMLInputElement::SetValueInternal(con
     }
     mInputData.mState->SetValue(value, aUserInput);
 
     if (PlaceholderApplies() &&
         HasAttr(kNameSpaceID_None, nsGkAtoms::placeholder)) {
       nsIDocument* doc = GetCurrentDoc();
       if (doc) {
         mozAutoDocUpdate upd(doc, UPDATE_CONTENT_STATE, PR_TRUE);
-        doc->ContentStatesChanged(this, nsnull, NS_EVENT_STATE_MOZ_PLACEHOLDER);
+        doc->ContentStateChanged(this, NS_EVENT_STATE_MOZ_PLACEHOLDER);
       }
     }
 
     return NS_OK;
   }
 
   // If the value of a hidden input was changed, we mark it changed so that we
   // will know we need to save / restore the value.  Yes, we are overloading
@@ -1478,18 +1478,18 @@ nsHTMLInputElement::SetValueChanged(PRBo
       FreeData();
     }
   }
 
   if (valueChangedBefore != aValueChanged) {
     nsIDocument* doc = GetCurrentDoc();
     if (doc) {
       mozAutoDocUpdate upd(doc, UPDATE_CONTENT_STATE, PR_TRUE);
-      doc->ContentStatesChanged(this, nsnull, NS_EVENT_STATE_MOZ_UI_VALID |
-                                              NS_EVENT_STATE_MOZ_UI_INVALID);
+      doc->ContentStateChanged(this, NS_EVENT_STATE_MOZ_UI_VALID |
+                                     NS_EVENT_STATE_MOZ_UI_INVALID);
     }
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP 
 nsHTMLInputElement::GetChecked(PRBool* aChecked)
@@ -1528,19 +1528,19 @@ nsHTMLInputElement::SetCheckedChangedInt
   SET_BOOLBIT(mBitField, BF_CHECKED_CHANGED, aCheckedChanged);
 
   // This method can't be called when we are not authorized to notify
   // so we do not need a aNotify parameter.
   if (checkedChangedBefore != aCheckedChanged) {
     nsIDocument* document = GetCurrentDoc();
     if (document) {
       mozAutoDocUpdate upd(document, UPDATE_CONTENT_STATE, PR_TRUE);
-      document->ContentStatesChanged(this, nsnull,
-                                     NS_EVENT_STATE_MOZ_UI_VALID |
-                                     NS_EVENT_STATE_MOZ_UI_INVALID);
+      document->ContentStateChanged(this,
+                                    NS_EVENT_STATE_MOZ_UI_VALID |
+                                    NS_EVENT_STATE_MOZ_UI_INVALID);
     }
   }
 }
 
 NS_IMETHODIMP
 nsHTMLInputElement::SetChecked(PRBool aChecked)
 {
   return DoSetChecked(aChecked, PR_TRUE, PR_TRUE);
@@ -1728,17 +1728,17 @@ nsHTMLInputElement::SetCheckedInternal(P
   }
 
   // Notify the document that the CSS :checked pseudoclass for this element
   // has changed state.
   if (aNotify) {
     nsIDocument* document = GetCurrentDoc();
     if (document) {
       mozAutoDocUpdate upd(document, UPDATE_CONTENT_STATE, aNotify);
-      document->ContentStatesChanged(this, nsnull, NS_EVENT_STATE_CHECKED);
+      document->ContentStateChanged(this, NS_EVENT_STATE_CHECKED);
     }
   }
 
   if (mType == NS_FORM_INPUT_CHECKBOX) {
     UpdateAllValidityStates(aNotify);
   }
 
   if (mType == NS_FORM_INPUT_RADIO) {
@@ -2142,17 +2142,17 @@ nsHTMLInputElement::PostHandleEvent(nsEv
         // TODO: checking if the value is empty could be a good idea but we do not
       // have a simple way to do that, see bug 585100
       states |= NS_EVENT_STATE_MOZ_PLACEHOLDER;
     }
 
     nsIDocument* doc = GetCurrentDoc();
     if (doc) {
       MOZ_AUTO_DOC_UPDATE(doc, UPDATE_CONTENT_STATE, PR_TRUE);
-      doc->ContentStatesChanged(this, nsnull, states);
+      doc->ContentStateChanged(this, states);
     }
   }
 
   // ignore the activate event fired by the "Browse..." button
   // (file input controls fire their own) (bug 500885)
   if (mType == NS_FORM_INPUT_FILE) {
     nsCOMPtr<nsIContent> maybeButton =
       do_QueryInterface(aVisitor.mEvent->originalTarget);
@@ -3779,20 +3779,20 @@ nsHTMLInputElement::DoesPatternApply() c
 NS_IMETHODIMP
 nsHTMLInputElement::SetCustomValidity(const nsAString& aError)
 {
   nsIConstraintValidation::SetCustomValidity(aError);
 
   nsIDocument* doc = GetCurrentDoc();
   if (doc) {
     MOZ_AUTO_DOC_UPDATE(doc, UPDATE_CONTENT_STATE, PR_TRUE);
-    doc->ContentStatesChanged(this, nsnull, NS_EVENT_STATE_INVALID |
-                                            NS_EVENT_STATE_VALID |
-                                            NS_EVENT_STATE_MOZ_UI_INVALID |
-                                            NS_EVENT_STATE_MOZ_UI_VALID);
+    doc->ContentStateChanged(this, NS_EVENT_STATE_INVALID |
+                                   NS_EVENT_STATE_VALID |
+                                   NS_EVENT_STATE_MOZ_UI_INVALID |
+                                   NS_EVENT_STATE_MOZ_UI_VALID);
   }
 
   return NS_OK;
 }
 
 PRBool
 nsHTMLInputElement::IsTooLong()
 {
@@ -3995,19 +3995,20 @@ nsHTMLInputElement::UpdateAllValiditySta
   UpdateValueMissingValidityState();
   UpdateTypeMismatchValidityState();
   UpdatePatternMismatchValidityState();
 
   if (validBefore != IsValid() && aNotify) {
     nsIDocument* doc = GetCurrentDoc();
     if (doc) {
       MOZ_AUTO_DOC_UPDATE(doc, UPDATE_CONTENT_STATE, PR_TRUE);
-      doc->ContentStatesChanged(this, nsnull,
-                                NS_EVENT_STATE_VALID | NS_EVENT_STATE_INVALID |
-                                NS_EVENT_STATE_MOZ_UI_VALID | NS_EVENT_STATE_MOZ_UI_INVALID);
+      doc->ContentStateChanged(this,
+                               NS_EVENT_STATE_VALID | NS_EVENT_STATE_INVALID |
+                               NS_EVENT_STATE_MOZ_UI_VALID |
+                               NS_EVENT_STATE_MOZ_UI_INVALID);
     }
   }
 }
 
 void
 nsHTMLInputElement::UpdateBarredFromConstraintValidation()
 {
   SetBarredFromConstraintValidation(mType == NS_FORM_INPUT_HIDDEN ||
@@ -4317,21 +4318,21 @@ public:
     }
 
     nsHTMLInputElement* input = static_cast<nsHTMLInputElement*>(aRadio);
 
     input->SetValidityState(nsIConstraintValidation::VALIDITY_STATE_VALUE_MISSING,
                             mValidity);
 
     if (mNotify && mDocument) {
-      mDocument->ContentStatesChanged(input, nsnull,
-                                      NS_EVENT_STATE_VALID |
-                                      NS_EVENT_STATE_INVALID |
-                                      NS_EVENT_STATE_MOZ_UI_VALID |
-                                      NS_EVENT_STATE_MOZ_UI_INVALID);
+      mDocument->ContentStateChanged(input,
+                                     NS_EVENT_STATE_VALID |
+                                     NS_EVENT_STATE_INVALID |
+                                     NS_EVENT_STATE_MOZ_UI_VALID |
+                                     NS_EVENT_STATE_MOZ_UI_INVALID);
     }
 
     return NS_OK;
   }
 
 protected:
   nsIFormControl* mExcludeElement;
   nsIDocument* mDocument;
@@ -4537,17 +4538,17 @@ nsHTMLInputElement::OnValueChanged(PRBoo
   // :-moz-placeholder pseudo-class may change when the value changes.
   // However, we don't want to waste cycles if the state doesn't apply.
   if (aNotify && PlaceholderApplies()
       && HasAttr(kNameSpaceID_None, nsGkAtoms::placeholder)
       && !nsContentUtils::IsFocusedContent((nsIContent*)(this))) {
     nsIDocument* doc = GetCurrentDoc();
     if (doc) {
       MOZ_AUTO_DOC_UPDATE(doc, UPDATE_CONTENT_STATE, PR_TRUE);
-      doc->ContentStatesChanged(this, nsnull, NS_EVENT_STATE_MOZ_PLACEHOLDER);
+      doc->ContentStateChanged(this, NS_EVENT_STATE_MOZ_PLACEHOLDER);
     }
   }
 }
 
 void
 nsHTMLInputElement::FieldSetDisabledChanged(nsEventStates aStates, PRBool aNotify)
 {
   UpdateValueMissingValidityState();
--- a/content/html/content/src/nsHTMLOptionElement.cpp
+++ b/content/html/content/src/nsHTMLOptionElement.cpp
@@ -152,17 +152,17 @@ nsHTMLOptionElement::SetSelectedInternal
   mIsSelected = aValue;
 
   // When mIsInSetDefaultSelected is true, the notification will be handled by
   // SetAttr/UnsetAttr.
   if (aNotify && !mIsInSetDefaultSelected) {
     nsIDocument* document = GetCurrentDoc();
     if (document) {
       mozAutoDocUpdate upd(document, UPDATE_CONTENT_STATE, aNotify);
-      document->ContentStatesChanged(this, nsnull, NS_EVENT_STATE_CHECKED);
+      document->ContentStateChanged(this, NS_EVENT_STATE_CHECKED);
     }
   }
 }
 
 NS_IMETHODIMP
 nsHTMLOptionElement::SetValue(const nsAString& aValue)
 {
   SetAttr(kNameSpaceID_None, nsGkAtoms::value, aValue, PR_TRUE);
--- a/content/html/content/src/nsHTMLOutputElement.cpp
+++ b/content/html/content/src/nsHTMLOutputElement.cpp
@@ -155,20 +155,20 @@ NS_IMPL_NSICONSTRAINTVALIDATION_EXCEPT_S
 NS_IMETHODIMP
 nsHTMLOutputElement::SetCustomValidity(const nsAString& aError)
 {
   nsIConstraintValidation::SetCustomValidity(aError);
 
   nsIDocument* doc = GetCurrentDoc();
   if (doc) {
     MOZ_AUTO_DOC_UPDATE(doc, UPDATE_CONTENT_STATE, PR_TRUE);
-    doc->ContentStatesChanged(this, nsnull, NS_EVENT_STATE_INVALID |
-                                            NS_EVENT_STATE_VALID |
-                                            NS_EVENT_STATE_MOZ_UI_INVALID |
-                                            NS_EVENT_STATE_MOZ_UI_VALID);
+    doc->ContentStateChanged(this, NS_EVENT_STATE_INVALID |
+                                   NS_EVENT_STATE_VALID |
+                                   NS_EVENT_STATE_MOZ_UI_INVALID |
+                                   NS_EVENT_STATE_MOZ_UI_VALID);
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsHTMLOutputElement::Reset()
 {
--- a/content/html/content/src/nsHTMLSelectElement.cpp
+++ b/content/html/content/src/nsHTMLSelectElement.cpp
@@ -207,20 +207,20 @@ NS_IMPL_NSICONSTRAINTVALIDATION_EXCEPT_S
 NS_IMETHODIMP
 nsHTMLSelectElement::SetCustomValidity(const nsAString& aError)
 {
   nsIConstraintValidation::SetCustomValidity(aError);
 
   nsIDocument* doc = GetCurrentDoc();
   if (doc) {
     MOZ_AUTO_DOC_UPDATE(doc, UPDATE_CONTENT_STATE, PR_TRUE);
-    doc->ContentStatesChanged(this, nsnull, NS_EVENT_STATE_INVALID |
-                                            NS_EVENT_STATE_VALID |
-                                            NS_EVENT_STATE_MOZ_UI_INVALID |
-                                            NS_EVENT_STATE_MOZ_UI_VALID);
+    doc->ContentStateChanged(this, NS_EVENT_STATE_INVALID |
+                                   NS_EVENT_STATE_VALID |
+                                   NS_EVENT_STATE_MOZ_UI_INVALID |
+                                   NS_EVENT_STATE_MOZ_UI_VALID);
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsHTMLSelectElement::GetForm(nsIDOMHTMLFormElement** aForm)
 {
@@ -361,20 +361,20 @@ nsHTMLSelectElement::RemoveOptionsFromLi
       // Update the validity state in case of we've just removed the last
       // option.
       UpdateValueMissingValidityState();
 
       if (aNotify) {
         nsIDocument* doc = GetCurrentDoc();
         if (doc) {
           MOZ_AUTO_DOC_UPDATE(doc, UPDATE_CONTENT_STATE, PR_TRUE);
-          doc->ContentStatesChanged(this, nsnull, NS_EVENT_STATE_VALID |
-                                                  NS_EVENT_STATE_INVALID |
-                                                  NS_EVENT_STATE_MOZ_UI_INVALID |
-                                                  NS_EVENT_STATE_MOZ_UI_VALID);
+          doc->ContentStateChanged(this, NS_EVENT_STATE_VALID |
+                                         NS_EVENT_STATE_INVALID |
+                                         NS_EVENT_STATE_MOZ_UI_INVALID |
+                                         NS_EVENT_STATE_MOZ_UI_VALID);
         }
       }
     }
   }
 
   return NS_OK;
 }
 
@@ -894,20 +894,20 @@ nsHTMLSelectElement::OnOptionSelected(ns
     aSelectFrame->OnOptionSelected(aIndex, aSelected);
   }
 
   UpdateValueMissingValidityState();
   if (aNotify) {
     nsIDocument* doc = GetCurrentDoc();
     if (doc) {
       MOZ_AUTO_DOC_UPDATE(doc, UPDATE_CONTENT_STATE, PR_TRUE);
-      doc->ContentStatesChanged(this, nsnull, NS_EVENT_STATE_VALID |
-                                              NS_EVENT_STATE_INVALID |
-                                              NS_EVENT_STATE_MOZ_UI_INVALID |
-                                              NS_EVENT_STATE_MOZ_UI_VALID);
+      doc->ContentStateChanged(this, NS_EVENT_STATE_VALID |
+                                     NS_EVENT_STATE_INVALID |
+                                     NS_EVENT_STATE_MOZ_UI_INVALID |
+                                     NS_EVENT_STATE_MOZ_UI_VALID);
     }
   }
 }
 
 void
 nsHTMLSelectElement::FindSelectedIndex(PRInt32 aStartIndex, PRBool aNotify)
 {
   mSelectedIndex = -1;
@@ -1339,20 +1339,20 @@ nsHTMLSelectElement::SelectSomething(PRB
       rv = SetSelectedIndexInternal(i, aNotify);
       NS_ENSURE_SUCCESS(rv, PR_FALSE);
 
       UpdateValueMissingValidityState();
       if (aNotify) {
         nsIDocument* doc = GetCurrentDoc();
         if (doc) {
           MOZ_AUTO_DOC_UPDATE(doc, UPDATE_CONTENT_STATE, PR_TRUE);
-          doc->ContentStatesChanged(this, nsnull, NS_EVENT_STATE_VALID |
-                                                  NS_EVENT_STATE_INVALID |
-                                                  NS_EVENT_STATE_MOZ_UI_INVALID |
-                                                  NS_EVENT_STATE_MOZ_UI_VALID);
+          doc->ContentStateChanged(this, NS_EVENT_STATE_VALID |
+                                         NS_EVENT_STATE_INVALID |
+                                         NS_EVENT_STATE_MOZ_UI_INVALID |
+                                         NS_EVENT_STATE_MOZ_UI_VALID);
         }
       }
 
       return PR_TRUE;
     }
   }
 
   return PR_FALSE;
@@ -1405,17 +1405,17 @@ nsHTMLSelectElement::AfterSetAttr(PRInt3
                 NS_EVENT_STATE_MOZ_UI_VALID | NS_EVENT_STATE_MOZ_UI_INVALID;
     }
   }
 
   if (aNotify && !states.IsEmpty()) {
     nsIDocument* doc = GetCurrentDoc();
     if (doc) {
       MOZ_AUTO_DOC_UPDATE(doc, UPDATE_CONTENT_STATE, PR_TRUE);
-      doc->ContentStatesChanged(this, nsnull, states);
+      doc->ContentStateChanged(this, states);
     }
   }
 
   return nsGenericHTMLFormElement::AfterSetAttr(aNameSpaceID, aName,
                                                 aValue, aNotify);
 }
 
 nsresult
@@ -1581,18 +1581,18 @@ nsHTMLSelectElement::PostHandleEvent(nsE
     // NS_EVENT_STATE_MOZ_UI_VALID given that the states should not change.
   } else if (aVisitor.mEvent->message == NS_BLUR_CONTENT) {
     mCanShowInvalidUI = PR_TRUE;
     mCanShowValidUI = PR_TRUE;
 
     nsIDocument* doc = GetCurrentDoc();
     if (doc) {
       MOZ_AUTO_DOC_UPDATE(doc, UPDATE_CONTENT_STATE, PR_TRUE);
-      doc->ContentStatesChanged(this, nsnull, NS_EVENT_STATE_MOZ_UI_VALID |
-                                              NS_EVENT_STATE_MOZ_UI_INVALID);
+      doc->ContentStateChanged(this, NS_EVENT_STATE_MOZ_UI_VALID |
+                                     NS_EVENT_STATE_MOZ_UI_INVALID);
     }
   }
 
   return nsGenericHTMLFormElement::PostHandleEvent(aVisitor);
 }
 
 nsEventStates
 nsHTMLSelectElement::IntrinsicState() const
@@ -2323,14 +2323,14 @@ nsHTMLSelectElement::SetSelectionChanged
 
   PRBool previousSelectionChangedValue = mSelectionHasChanged;
   mSelectionHasChanged = aValue;
 
   if (aNotify && mSelectionHasChanged != previousSelectionChangedValue) {
     nsIDocument* doc = GetCurrentDoc();
     if (doc) {
       MOZ_AUTO_DOC_UPDATE(doc, UPDATE_CONTENT_STATE, PR_TRUE);
-      doc->ContentStatesChanged(this, nsnull, NS_EVENT_STATE_MOZ_UI_INVALID |
-                                              NS_EVENT_STATE_MOZ_UI_VALID);
+      doc->ContentStateChanged(this, NS_EVENT_STATE_MOZ_UI_INVALID |
+                                     NS_EVENT_STATE_MOZ_UI_VALID);
     }
   }
 }
 
--- a/content/html/content/src/nsHTMLTextAreaElement.cpp
+++ b/content/html/content/src/nsHTMLTextAreaElement.cpp
@@ -598,17 +598,17 @@ nsHTMLTextAreaElement::SetValueChanged(P
 
     if (HasAttr(kNameSpaceID_None, nsGkAtoms::placeholder)) {
       states |= NS_EVENT_STATE_MOZ_PLACEHOLDER;
     }
 
     nsIDocument* doc = GetCurrentDoc();
     if (doc) {
       mozAutoDocUpdate upd(doc, UPDATE_CONTENT_STATE, PR_TRUE);
-      doc->ContentStatesChanged(this, nsnull, states);
+      doc->ContentStateChanged(this, states);
     }
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsHTMLTextAreaElement::GetDefaultValue(nsAString& aDefaultValue)
@@ -776,17 +776,17 @@ nsHTMLTextAreaElement::PostHandleEvent(n
       // TODO: checking if the value is empty could be a good idea but we do not
       // have a simple way to do that, see bug 585100
       states |= NS_EVENT_STATE_MOZ_PLACEHOLDER;
     }
 
     nsIDocument* doc = GetCurrentDoc();
     if (doc) {
       MOZ_AUTO_DOC_UPDATE(doc, UPDATE_CONTENT_STATE, PR_TRUE);
-      doc->ContentStatesChanged(this, nsnull, states);
+      doc->ContentStateChanged(this, states);
     }
   }
 
   // Reset the flag for other content besides this text field
   aVisitor.mEvent->flags |= (aVisitor.mItemFlags & NS_NO_CONTENT_DISPATCH)
     ? NS_EVENT_FLAG_NO_CONTENT_DISPATCH : NS_EVENT_FLAG_NONE;
 
   return NS_OK;
@@ -1229,17 +1229,17 @@ nsHTMLTextAreaElement::AfterSetAttr(PRIn
 
       if (aName == nsGkAtoms::readonly) {
         UpdateEditableState();
         states |= NS_EVENT_STATE_MOZ_READONLY | NS_EVENT_STATE_MOZ_READWRITE;
       }
 
       if (doc && !states.IsEmpty()) {
         MOZ_AUTO_DOC_UPDATE(doc, UPDATE_CONTENT_STATE, PR_TRUE);
-        doc->ContentStatesChanged(this, nsnull, states);
+        doc->ContentStateChanged(this, states);
       }
     }
   }
 
   return nsGenericHTMLFormElement::AfterSetAttr(aNameSpaceID, aName, aValue,
                                                 aNotify);
 }
 
@@ -1268,20 +1268,20 @@ nsHTMLTextAreaElement::IsMutable() const
 NS_IMETHODIMP
 nsHTMLTextAreaElement::SetCustomValidity(const nsAString& aError)
 {
   nsIConstraintValidation::SetCustomValidity(aError);
 
   nsIDocument* doc = GetCurrentDoc();
   if (doc) {
     MOZ_AUTO_DOC_UPDATE(doc, UPDATE_CONTENT_STATE, PR_TRUE);
-    doc->ContentStatesChanged(this, nsnull, NS_EVENT_STATE_INVALID |
-                                            NS_EVENT_STATE_VALID |
-                                            NS_EVENT_STATE_MOZ_UI_INVALID |
-                                            NS_EVENT_STATE_MOZ_UI_VALID);
+    doc->ContentStateChanged(this, NS_EVENT_STATE_INVALID |
+                                   NS_EVENT_STATE_VALID |
+                                   NS_EVENT_STATE_MOZ_UI_INVALID |
+                                   NS_EVENT_STATE_MOZ_UI_VALID);
   }
 
   return NS_OK;
 }
 
 PRBool
 nsHTMLTextAreaElement::IsTooLong()
 {
@@ -1502,17 +1502,17 @@ nsHTMLTextAreaElement::OnValueChanged(PR
         && !nsContentUtils::IsFocusedContent((nsIContent*)(this))) {
       states |= NS_EVENT_STATE_MOZ_PLACEHOLDER;
     }
 
     if (!states.IsEmpty()) {
       nsIDocument* doc = GetCurrentDoc();
       if (doc) {
         MOZ_AUTO_DOC_UPDATE(doc, UPDATE_CONTENT_STATE, PR_TRUE);
-        doc->ContentStatesChanged(this, nsnull, states);
+        doc->ContentStateChanged(this, states);
       }
     }
   }
 }
 
 void
 nsHTMLTextAreaElement::FieldSetDisabledChanged(nsEventStates aStates, PRBool aNotify)
 {
--- a/content/html/content/src/nsImageMapUtils.cpp
+++ b/content/html/content/src/nsImageMapUtils.cpp
@@ -34,21 +34,23 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsString.h"
 #include "nsReadableUtils.h"
 #include "nsCOMPtr.h"
 #include "nsIDocument.h"
-#include "nsIContent.h"
+#include "mozilla/dom/Element.h"
 #include "nsIHTMLDocument.h"
 #include "nsIDOMHTMLMapElement.h"
 #include "nsImageMapUtils.h"
 
+using namespace mozilla::dom;
+
 /*static*/
 already_AddRefed<nsIDOMHTMLMapElement>
 nsImageMapUtils::FindImageMap(nsIDocument *aDocument, 
                               const nsAString &aUsemap)
 {
   if (!aDocument)
     return nsnull;
 
@@ -74,27 +76,27 @@ nsImageMapUtils::FindImageMap(nsIDocumen
   } else {
     return nsnull;
   }
 
   const nsAString& usemap = Substring(start, end);
 
   nsCOMPtr<nsIHTMLDocument> htmlDoc(do_QueryInterface(aDocument));
   if (htmlDoc) {
-    nsIDOMHTMLMapElement* map = htmlDoc->GetImageMap(usemap);
-    NS_IF_ADDREF(map);
-    return map;
+    nsCOMPtr<nsIDOMHTMLMapElement> map =
+      do_QueryInterface(htmlDoc->GetImageMap(usemap));
+    return map.forget();
   } else {
     // For XHTML elements embedded in non-XHTML documents we get the
     // map by id since XHTML requires that where a "name" attribute
     // was used in HTML 4.01, the "id" attribute must be used in
     // XHTML. The attribute "name" is officially deprecated.  This
     // simplifies our life becase we can simply get the map with
     // getElementById().
-    nsIContent *element = aDocument->GetElementById(usemap);
+    Element* element = aDocument->GetElementById(usemap);
 
     if (element) {
       nsIDOMHTMLMapElement* map;
       CallQueryInterface(element, &map);
       return map;
     }
   }
   
--- a/content/html/document/src/nsHTMLDocument.cpp
+++ b/content/html/document/src/nsHTMLDocument.cpp
@@ -1153,45 +1153,32 @@ nsHTMLDocument::EndLoad()
 }
 
 NS_IMETHODIMP
 nsHTMLDocument::SetTitle(const nsAString& aTitle)
 {
   return nsDocument::SetTitle(aTitle);
 }
 
-nsIDOMHTMLMapElement *
+Element*
 nsHTMLDocument::GetImageMap(const nsAString& aMapName)
 {
   if (!mImageMaps) {
     mImageMaps = new nsContentList(this, kNameSpaceID_XHTML, nsGkAtoms::map, nsGkAtoms::map);
   }
 
   nsAutoString name;
   PRUint32 i, n = mImageMaps->Length(PR_TRUE);
   for (i = 0; i < n; ++i) {
-    nsCOMPtr<nsIDOMHTMLMapElement> map(
-      do_QueryInterface(mImageMaps->GetNodeAt(i)));
-
-    PRBool match;
-    nsresult rv;
-
-    rv = map->GetId(name);
-    NS_ENSURE_SUCCESS(rv, nsnull);
-
-    match = name.Equals(aMapName);
-    if (!match) {
-      rv = map->GetName(name);
-      NS_ENSURE_SUCCESS(rv, nsnull);
-
-      match = name.Equals(aMapName, nsCaseInsensitiveStringComparator());
-    }
-
-    if (match) {
-      return map;
+    nsIContent* map = mImageMaps->GetNodeAt(i);
+    if (map->AttrValueIs(kNameSpaceID_None, nsGkAtoms::id, aMapName,
+                         eCaseMatters) ||
+        map->AttrValueIs(kNameSpaceID_None, nsGkAtoms::name, aMapName,
+                         eIgnoreCase)) {
+      return map->AsElement();
     }
   }
 
   return NULL;
 }
 
 void
 nsHTMLDocument::SetCompatibilityMode(nsCompatibility aMode)
@@ -3153,19 +3140,19 @@ nsHTMLDocument::GetDocumentAllResult(con
 static void
 NotifyEditableStateChange(nsINode *aNode, nsIDocument *aDocument,
                           PRBool aEditable)
 {
   PRUint32 i, n = aNode->GetChildCount();
   for (i = 0; i < n; ++i) {
     nsIContent *child = aNode->GetChildAt(i);
     if (child->HasFlag(NODE_IS_EDITABLE) != aEditable) {
-      aDocument->ContentStatesChanged(child, nsnull,
-                                      NS_EVENT_STATE_MOZ_READONLY |
-                                      NS_EVENT_STATE_MOZ_READWRITE);
+      aDocument->ContentStateChanged(child,
+                                     NS_EVENT_STATE_MOZ_READONLY |
+                                     NS_EVENT_STATE_MOZ_READWRITE);
     }
     NotifyEditableStateChange(child, aDocument, aEditable);
   }
 }
 
 void
 nsHTMLDocument::TearingDownEditor(nsIEditor *aEditor)
 {
--- a/content/html/document/src/nsHTMLDocument.h
+++ b/content/html/document/src/nsHTMLDocument.h
@@ -38,17 +38,16 @@
 #ifndef nsHTMLDocument_h___
 #define nsHTMLDocument_h___
 
 #include "nsDocument.h"
 #include "nsIHTMLDocument.h"
 #include "nsIDOMHTMLDocument.h"
 #include "nsIDOMNSHTMLDocument.h"
 #include "nsIDOMHTMLBodyElement.h"
-#include "nsIDOMHTMLMapElement.h"
 #include "nsIDOMHTMLCollection.h"
 #include "nsIScriptElement.h"
 #include "jsapi.h"
 #include "nsTArray.h"
 
 #include "pldhash.h"
 #include "nsIHttpChannel.h"
 #include "nsHTMLStyleSheet.h"
@@ -101,17 +100,17 @@ public:
                                      PRBool aReset = PR_TRUE,
                                      nsIContentSink* aSink = nsnull);
   virtual void StopDocumentLoad();
 
   virtual void BeginLoad();
 
   virtual void EndLoad();
 
-  virtual nsIDOMHTMLMapElement *GetImageMap(const nsAString& aMapName);
+  virtual mozilla::dom::Element* GetImageMap(const nsAString& aMapName);
 
   virtual void SetCompatibilityMode(nsCompatibility aMode);
 
   virtual PRBool IsWriting()
   {
     return mWriteLevel != PRUint32(0);
   }
 
--- a/content/html/document/src/nsIHTMLDocument.h
+++ b/content/html/document/src/nsIHTMLDocument.h
@@ -29,49 +29,50 @@
  * 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 ***** */
-#ifndef nsIHTMLDocument_h___
-#define nsIHTMLDocument_h___
+
+#ifndef nsIHTMLDocument_h
+#define nsIHTMLDocument_h
 
 #include "nsISupports.h"
 #include "nsCompatibility.h"
-#include "nsContentList.h"
 
-class nsIImageMap;
-class nsString;
-class nsIDOMNodeList;
-class nsIDOMHTMLCollection;
-class nsIDOMHTMLMapElement;
-class nsHTMLStyleSheet;
-class nsIStyleSheet;
+class nsIDOMHTMLFormElement;
 class nsIContent;
-class nsIDOMHTMLBodyElement;
 class nsIScriptElement;
 class nsIEditor;
+class nsContentList;
+class nsWrapperCache;
+
+namespace mozilla {
+namespace dom {
+class Element;
+} // namespace dom
+} // namespace mozilla
 
 #define NS_IHTMLDOCUMENT_IID \
-{ 0x840cacc9, 0x1956, 0x4987, \
-  { 0x80, 0x6e, 0xc6, 0xab, 0x19, 0x1b, 0x92, 0xd2 } }
+{ 0xe43a4bfd, 0xff5a, 0x40b0, \
+  { 0x8c, 0x31, 0x24, 0xac, 0xe8, 0x15, 0xda, 0xf2 } }
 
 
 /**
  * HTML document extensions to nsIDocument.
  */
 class nsIHTMLDocument : public nsISupports
 {
 public:
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_IHTMLDOCUMENT_IID)
 
-  virtual nsIDOMHTMLMapElement *GetImageMap(const nsAString& aMapName) = 0;
+  virtual mozilla::dom::Element* GetImageMap(const nsAString& aMapName) = 0;
 
   /**
    * Set compatibility mode for this document
    */
   virtual void SetCompatibilityMode(nsCompatibility aMode) = 0;
 
   virtual nsresult ResolveName(const nsAString& aName,
                                nsIDOMHTMLFormElement *aForm,
@@ -184,9 +185,9 @@ public:
 
   virtual void SetIsXHTML(PRBool aXHTML) = 0;
 
   virtual void SetDocWriteDisabled(PRBool aDisabled) = 0;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsIHTMLDocument, NS_IHTMLDOCUMENT_IID)
 
-#endif /* nsIHTMLDocument_h___ */
+#endif /* nsIHTMLDocument_h */
--- a/content/mathml/content/src/nsMathMLElement.cpp
+++ b/content/mathml/content/src/nsMathMLElement.cpp
@@ -463,11 +463,10 @@ nsMathMLElement::SetIncrementScriptLevel
 
   NS_ASSERTION(aNotify, "We always notify!");
 
   nsIDocument* doc = GetCurrentDoc();
   if (!doc)
     return;
 
   mozAutoDocUpdate upd(doc, UPDATE_CONTENT_STATE, PR_TRUE);
-  doc->ContentStatesChanged(this, nsnull,
-                            NS_EVENT_STATE_INCREMENT_SCRIPT_LEVEL);
+  doc->ContentStateChanged(this, NS_EVENT_STATE_INCREMENT_SCRIPT_LEVEL);
 }
--- a/content/media/nsBuiltinDecoder.h
+++ b/content/media/nsBuiltinDecoder.h
@@ -431,17 +431,20 @@ class nsBuiltinDecoder : public nsMediaD
   // state.
   Monitor& GetMonitor() { 
     return mMonitor; 
   }
 
   // Constructs the time ranges representing what segments of the media
   // are buffered and playable.
   virtual nsresult GetBuffered(nsTimeRanges* aBuffered) {
-    return mDecoderStateMachine->GetBuffered(aBuffered);
+    if (mDecoderStateMachine) {
+      return mDecoderStateMachine->GetBuffered(aBuffered);
+    }
+    return NS_ERROR_FAILURE;
   }
 
   virtual void NotifyDataArrived(const char* aBuffer, PRUint32 aLength, PRUint32 aOffset) {
     return mDecoderStateMachine->NotifyDataArrived(aBuffer, aLength, aOffset);
   }
 
  public:
   // Return the current state. Can be called on any thread. If called from
--- a/content/media/nsBuiltinDecoderReader.h
+++ b/content/media/nsBuiltinDecoderReader.h
@@ -114,25 +114,27 @@ typedef short SoundDataValue;
 
 #define MOZ_SOUND_DATA_FORMAT (nsAudioStream::FORMAT_S16_LE)
 #define MOZ_CLIP_TO_15(x) ((x)<-32768?-32768:(x)<=32767?(x):32767)
 // Convert the output of vorbis_synthesis_pcmout to a SoundDataValue
 #define MOZ_CONVERT_VORBIS_SAMPLE(x) \
  (static_cast<SoundDataValue>(MOZ_CLIP_TO_15((x)>>9)))
 // Convert a SoundDataValue to a float for the Audio API
 #define MOZ_CONVERT_SOUND_SAMPLE(x) ((x)*(1.F/32768))
+#define MOZ_SAMPLE_TYPE_S16LE 1
 
 #else /*MOZ_VORBIS*/
 
 typedef float VorbisPCMValue;
 typedef float SoundDataValue;
 
 #define MOZ_SOUND_DATA_FORMAT (nsAudioStream::FORMAT_FLOAT32)
 #define MOZ_CONVERT_VORBIS_SAMPLE(x) (x)
 #define MOZ_CONVERT_SOUND_SAMPLE(x) (x)
+#define MOZ_SAMPLE_TYPE_FLOAT32 1
 
 #endif
 
 // Holds chunk a decoded sound samples.
 class SoundData {
 public:
   SoundData(PRInt64 aOffset,
             PRInt64 aTime,
--- a/content/media/nsBuiltinDecoderStateMachine.cpp
+++ b/content/media/nsBuiltinDecoderStateMachine.cpp
@@ -592,17 +592,17 @@ void nsBuiltinDecoderStateMachine::Audio
 }
 
 PRUint32 nsBuiltinDecoderStateMachine::PlaySilence(PRUint32 aSamples,
                                                    PRUint32 aChannels,
                                                    PRUint64 aSampleOffset)
 
 {
   MonitorAutoEnter audioMon(mAudioMonitor);
-  if (mAudioStream->IsPaused()) {
+  if (!mAudioStream || mAudioStream->IsPaused()) {
     // The state machine has paused since we've released the decoder
     // monitor and acquired the audio monitor. Don't write any audio.
     return 0;
   }
   PRUint32 maxSamples = SILENCE_BYTES_CHUNK / aChannels;
   PRUint32 samples = NS_MIN(aSamples, maxSamples);
   PRUint32 numValues = samples * aChannels;
   nsAutoArrayPtr<SoundDataValue> buf(new SoundDataValue[numValues]);
--- a/content/media/nsMediaDecoder.cpp
+++ b/content/media/nsMediaDecoder.cpp
@@ -84,26 +84,26 @@ nsMediaDecoder::nsMediaDecoder() :
   mShuttingDown(PR_FALSE)
 {
   MOZ_COUNT_CTOR(nsMediaDecoder);
 }
 
 nsMediaDecoder::~nsMediaDecoder()
 {
   if (mVideoUpdateLock) {
-    PR_DestroyLock(mVideoUpdateLock);
+    nsAutoLock::DestroyLock(mVideoUpdateLock);
     mVideoUpdateLock = nsnull;
   }
   MOZ_COUNT_DTOR(nsMediaDecoder);
 }
 
 PRBool nsMediaDecoder::Init(nsHTMLMediaElement* aElement)
 {
   mElement = aElement;
-  mVideoUpdateLock = PR_NewLock();
+  mVideoUpdateLock = nsAutoLock::NewLock("nsMediaDecoder::mVideoUpdateLock");
 
   return mVideoUpdateLock != nsnull;
 }
 
 void nsMediaDecoder::Shutdown()
 {
   StopProgress();
   mElement = nsnull;
--- a/content/media/test/Makefile.in
+++ b/content/media/test/Makefile.in
@@ -224,16 +224,18 @@ endif
 		big.wav \
 		bogus.wav \
 		r11025_msadpcm_c1.wav \
 		r11025_s16_c1.wav \
 		r11025_s16_c1_trailing.wav \
 		r11025_u8_c1.wav \
 		r11025_u8_c1_trunc.wav \
 		r16000_u8_c1_list.wav \
+		wavedata_u8.wav \
+		wavedata_s16.wav \
 		$(NULL)
 
 # Other files
 _TEST_FILES += \
 		bogus.duh \
 		$(NULL)
 
 # These tests contain backend-specific tests. Try to write backend
@@ -275,16 +277,18 @@ else
 _TEST_FILES += \
 		test_can_play_type_no_webm.html \
 		$(NULL)
 endif
 
 ifdef MOZ_WAVE
 _TEST_FILES += \
 		test_can_play_type_wave.html \
+		test_wave_data_u8.html \
+		test_wave_data_s16.html \
 		$(NULL)
 else
 _TEST_FILES += \
 		test_can_play_type_no_wave.html \
 		$(NULL)
 endif
 
 libs:: $(_TEST_FILES)
--- a/content/media/test/test_autoplay_contentEditable.html
+++ b/content/media/test/test_autoplay_contentEditable.html
@@ -11,17 +11,17 @@
 <pre id="test">
 
 <script>
 
 var manager = new MediaTestManager;
 
 var tokens = {
   0:                ["canplay"],
-  "canplay":        ["canplaythrough"],
+  "canplay":        ["canplay", "canplaythrough"],
   "canplaythrough": ["canplay", "canplaythrough"]
 };
 
 function gotPlayEvent(event) {
   var v = event.target;
   ok(tokens[v._state].indexOf(event.type) >= 0,
      "Check expected event got " + event.type + " at " + v._state + " for " + v.src +
      " uneval(event.type)=" + uneval(event.type) + " typeof(event.type)=" + typeof(event.type) +
new file mode 100644
--- /dev/null
+++ b/content/media/test/test_wave_data_s16.html
@@ -0,0 +1,53 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Wave Media test: ended</title>
+  <script type="text/javascript" src="/MochiKit/packed.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+// Test if the ended event works correctly.
+var endPassed = false;
+var completed = false;
+
+function audioavailable(e) {
+  if (completed)
+    return false;
+
+  completed = true;
+  var samples = e.frameBuffer;
+  var time = e.time;
+
+  ok(samples.length >= 3, "Must be 3 or more samples. There were " + samples.length);  
+  if (samples.length >= 3) {
+    ok(samples[0] > 0.99 && samples[0] < 1.01, "First sound sample should be close to 1.0. It was " + samples[0]);
+    ok(samples[1] > -1.01 && samples [1] < 0.01, "Second sound sample should be close to -1.0. It was " + samples[1]);
+    ok(samples[2] > -0.01 && samples[2] < 0.01, "Third sound sample should be close to 0. It was " + samples[2]);
+  }
+
+  // Only care about the first few samples
+  SimpleTest.finish();
+  return false;
+}
+
+function startTest() {
+  if (completed)
+    return false;
+  var v = document.getElementById('v');
+  v.addEventListener('MozAudioAvailable', audioavailable, false);
+  v.play();
+  return false;
+}
+
+SimpleTest.waitForExplicitFinish();
+</script>
+</pre>
+<audio id='v'
+       onloadedmetadata='return startTest();'>
+  <source type='audio/x-wav' src='wavedata_s16.wav'>
+</audio>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/media/test/test_wave_data_u8.html
@@ -0,0 +1,53 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Wave Media test: ended</title>
+  <script type="text/javascript" src="/MochiKit/packed.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+// Test if the ended event works correctly.
+var endPassed = false;
+var completed = false;
+
+function audioavailable(e) {
+  if (completed)
+    return false;
+
+  completed = true;
+  var samples = e.frameBuffer;
+  var time = e.time;
+
+  ok(samples.length >= 3, "Must be 3 or more samples. There were " + samples.length);  
+  if (samples.length >= 3) {
+    ok(samples[0] > 0.99 && samples[0] < 1.01, "First sound sample should be close to 1.0. It was " + samples[0]);
+    ok(samples[1] > -1.01 && samples [1] < 0.01, "Second sound sample should be close to -1.0. It was " + samples[1]);
+    ok(samples[2] > -0.01 && samples[2] < 0.01, "Third sound sample should be close to 0. It was " + samples[2]);
+  }
+
+  // Only care about the first few samples
+  SimpleTest.finish();
+  return false;
+}
+
+function startTest() {
+  if (completed)
+    return false;
+  var v = document.getElementById('v');
+  v.addEventListener('MozAudioAvailable', audioavailable, false);
+  v.play();
+  return false;
+}
+
+SimpleTest.waitForExplicitFinish();
+</script>
+</pre>
+<audio id='v'
+       onloadedmetadata='return startTest();'>
+  <source type='audio/x-wav' src='wavedata_u8.wav'>
+</audio>
+</body>
+</html>
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..6a69cd78f6e29f9851f231d67837654a55d87a82
GIT binary patch
literal 22062
zc%1FYAr6B;07TI#C!jYVSWZ#FCbbCABm`H7hnu(CJ0M@4`I(vCrIa~YUh}wrj;>Ae
ssGdX8*S$%bZ9Ue1f6rX?z5oCK00000000000000000000006k#52p}`fdBvi
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..1d895c2ce0867c03ba384b84b81918fa51bbb12a
GIT binary patch
literal 11037
zc%1FXs||zz07SvXA)$lw2owb%Ai<shO>#MhqX0VR(*<JYWzBx4aqOC8>F43U?Vl=1
faaZ5pvm#}F>%P`)a^?sC00000000000KC=(p0)@M
--- a/content/media/wave/Makefile.in
+++ b/content/media/wave/Makefile.in
@@ -47,16 +47,17 @@ LIBXUL_LIBRARY 	= 1
 
 
 EXPORTS		+= \
 		nsWaveDecoder.h \
 		$(NULL)
 
 CPPSRCS		= \
 		nsWaveDecoder.cpp \
+		nsWaveReader.cpp \
 		$(NULL)
 
 FORCE_STATIC_LIB = 1
 
 include $(topsrcdir)/config/rules.mk
 
 INCLUDES	+= \
 		-I$(srcdir)/../../base/src \
--- a/content/media/wave/nsWaveDecoder.cpp
+++ b/content/media/wave/nsWaveDecoder.cpp
@@ -10,18 +10,18 @@
  *
  * 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 Mozilla code.
  *
- * The Initial Developer of the Original Code is the Mozilla Corporation.
- * Portions created by the Initial Developer are Copyright (C) 2008
+ * The Initial Developer of the Original Code is the Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2010
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
  *  Matthew Gregan <kinetik@flim.org>
  *
  * Alternatively, the contents of this file may be used under the terms of
  * either 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"),
@@ -30,1731 +30,16 @@
  * 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 "limits"
-#include "prlog.h"
-#include "prmem.h"
-#include "nsIDOMHTMLMediaElement.h"
-#include "nsIDocument.h"
-#include "nsIFrame.h"
-#include "nsIObserver.h"
-#include "nsISeekableStream.h"
-#include "nsAudioStream.h"
-#include "nsAutoLock.h"
-#include "nsHTMLMediaElement.h"
-#include "nsNetUtil.h"
-#include "nsThreadUtils.h"
+#include "nsBuiltinDecoderStateMachine.h"
+#include "nsWaveReader.h"
 #include "nsWaveDecoder.h"
-#include "nsTimeRanges.h"
-
-using mozilla::TimeDuration;
-using mozilla::TimeStamp;
-
-#ifdef PR_LOGGING
-static PRLogModuleInfo* gWaveDecoderLog;
-#define LOG(type, msg) PR_LOG(gWaveDecoderLog, type, msg)
-#else
-#define LOG(type, msg)
-#endif
-
-// Maximum number of seconds to wait when buffering.
-#define BUFFERING_TIMEOUT 3
-
-// Duration the playback loop will sleep after refilling the backend's audio
-// buffers.  The loop's goal is to keep AUDIO_BUFFER_LENGTH milliseconds of
-// audio buffered to allow time to refill before the backend underruns.
-// Should be a multiple of 10 to deal with poor timer granularity on some
-// platforms.
-#define AUDIO_BUFFER_WAKEUP 100
-#define AUDIO_BUFFER_LENGTH (2 * AUDIO_BUFFER_WAKEUP)
-
-// Magic values that identify RIFF chunks we're interested in.
-#define RIFF_CHUNK_MAGIC 0x52494646
-#define WAVE_CHUNK_MAGIC 0x57415645
-#define FRMT_CHUNK_MAGIC 0x666d7420
-#define DATA_CHUNK_MAGIC 0x64617461
-
-// Size of RIFF chunk header.  4 byte chunk header type and 4 byte size field.
-#define RIFF_CHUNK_HEADER_SIZE 8
-
-// Size of RIFF header.  RIFF chunk and 4 byte RIFF type.
-#define RIFF_INITIAL_SIZE (RIFF_CHUNK_HEADER_SIZE + 4)
-
-// Size of required part of format chunk.  Actual format chunks may be
-// extended (for non-PCM encodings), but we skip any extended data.
-#define WAVE_FORMAT_CHUNK_SIZE 16
-
-// PCM encoding type from format chunk.  Linear PCM is the only encoding
-// supported by nsAudioStream.
-#define WAVE_FORMAT_ENCODING_PCM 1
-
-enum State {
-  STATE_LOADING_METADATA,
-  STATE_BUFFERING,
-  STATE_PLAYING,
-  STATE_SEEKING,
-  STATE_PAUSED,
-  STATE_ENDED,
-  STATE_ERROR,
-  STATE_SHUTDOWN
-};
-
-/*
-  A single nsWaveStateMachine instance is owned by the decoder, created
-   on-demand at load time.  Upon creation, the decoder immediately
-   dispatches the state machine event to the decode thread to begin
-   execution.  Once running, metadata loading begins immediately.  If this
-   completes successfully, the state machine will move into a paused state
-   awaiting further commands.  The state machine provides a small set of
-   threadsafe methods enabling the main thread to play, pause, seek, and
-   query parameters.
-
-   An weak (raw) pointer to the decoder's nsMediaStream is used by the state
-   machine to read data, seek, and query stream information.  The decoder is
-   responsible for creating and opening the stream, and may also cancel it.
-   Every other stream operation is performed on the playback thread by the
-   state machine.  A cancel from the main thread will force any in-flight
-   stream operations to abort.
- */
-class nsWaveStateMachine : public nsRunnable
-{
-public:
-  nsWaveStateMachine(nsWaveDecoder* aDecoder,
-                     TimeDuration aBufferWaitTime, double aInitialVolume);
-  ~nsWaveStateMachine();
-
-  void SetStream(nsMediaStream* aStream) { mStream = aStream; }
-
-  // Set specified volume.  aVolume must be in range [0.0, 1.0].
-  // Threadsafe.
-  void SetVolume(double aVolume);
-
-  /*
-    The following four member functions initiate the appropriate state
-    transition suggested by the function name.  Threadsafe.
-   */
-  void Play();
-  void Pause();
-  void Seek(double aTime);
-  void Shutdown();
-
-  // Returns the playback length of the audio data in seconds, calculated
-  // from the length extracted from the metadata.  Returns NaN if called
-  // before metadata validation has completed.  Threadsafe.
-  double GetDuration();
-
-  // Returns the number of channels extracted from the metadata.  Returns 0
-  // if called before metadata validation has completed.  Threadsafe.
-  PRUint32 GetChannels();
-
-  // Returns the audio sample rate (number of samples per second) extracted
-  // from the metadata.  Returns 0 if called before metadata validation has
-  // completed.  Threadsafe.
-  PRUint32 GetSampleRate();
-
-  // Returns true if the state machine is seeking.  Threadsafe.
-  PRBool IsSeeking();
-
-  // Returns true if the state machine has reached the end of playback.  Threadsafe.
-  PRBool IsEnded();
-
-  // Main state machine loop. Runs forever, until shutdown state is reached.
-  NS_IMETHOD Run();
-
-  // Called by the decoder, on the main thread.
-  nsMediaDecoder::Statistics GetStatistics();
-
-  // Called on the decoder thread
-  void NotifyBytesConsumed(PRInt64 aBytes);
-
-  // Called by decoder and main thread.
-  nsHTMLMediaElement::NextFrameStatus GetNextFrameStatus();
-
-  // Clear the flag indicating that a playback position change event is
-  // currently queued and return the current time. This is called from the
-  // main thread.
-  double GetTimeForPositionChange();
-
-  nsresult GetBuffered(nsTimeRanges* aBuffered);
-
-private:
-  // Returns PR_TRUE if we're in shutdown state. Threadsafe.
-  PRBool IsShutdown();
-
-  // Reads from the media stream. Returns PR_FALSE on failure or EOF.  If
-  // aBytesRead is non-null, the number of bytes read will be returned via
-  // this.
-  PRBool ReadAll(char* aBuf, PRInt64 aSize, PRInt64* aBytesRead);
-
-  void UpdateReadyState() {
-    PR_ASSERT_CURRENT_THREAD_IN_MONITOR(mMonitor);
-
-    nsCOMPtr<nsIRunnable> event;
-    switch (GetNextFrameStatus()) {
-      case nsHTMLMediaElement::NEXT_FRAME_UNAVAILABLE_BUFFERING:
-        event = NS_NewRunnableMethod(mDecoder, &nsWaveDecoder::NextFrameUnavailableBuffering);
-        break;
-      case nsHTMLMediaElement::NEXT_FRAME_AVAILABLE:
-        event = NS_NewRunnableMethod(mDecoder, &nsWaveDecoder::NextFrameAvailable);
-        break;
-      case nsHTMLMediaElement::NEXT_FRAME_UNAVAILABLE:
-        event = NS_NewRunnableMethod(mDecoder, &nsWaveDecoder::NextFrameUnavailable);
-        break;
-      default:
-        PR_NOT_REACHED("unhandled frame state");
-    }
-
-    NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
-  }
-
-  // Change the current state and wake the playback thread if it is waiting
-  // on mMonitor.  Used by public member functions called from both threads,
-  // so must hold mMonitor.  Threadsafe.
-  void ChangeState(State aState);
-
-  // Create and initialize audio stream using current audio parameters.
-  void OpenAudioStream(nsAutoMonitor& aMonitor);
-
-  // Shut down and dispose audio stream.
-  void CloseAudioStream();
-
-  // Read RIFF_INITIAL_SIZE from the beginning of the stream and verify that
-  // the stream data is a RIFF bitstream containing WAVE data.
-  PRBool LoadRIFFChunk();
-
-  // Read forward in the stream until aWantedChunk is found.  Return chunk
-  // size in aChunkSize.  aChunkSize will not be rounded up if the chunk
-  // size is odd.
-  PRBool ScanForwardUntil(PRUint32 aWantedChunk, PRUint32* aChunkSize);
-
-  // Scan forward in the stream looking for the WAVE format chunk.  If
-  // found, parse and validate required metadata, then use it to set
-  // mSampleRate, mChannels, mSampleSize, and mSampleFormat.
-  PRBool LoadFormatChunk();
-
-  // Scan forward in the stream looking for the start of the PCM data.  If
-  // found, record the data length and offset in mWaveLength and
-  // mWavePCMOffset.
-  PRBool FindDataOffset();
-
-  // Return the length of the PCM data.
-  PRInt64 GetDataLength();
-
-  // Fire a PlaybackPositionChanged event.  If aCoalesce is true and a
-  // PlaybackPositionChanged event is already pending, an event is not
-  // fired.
-  void FirePositionChanged(PRBool aCoalesce);
-
-  // Returns the number of seconds that aBytes represents based on the
-  // current audio parameters.  e.g.  176400 bytes is 1 second at 16-bit
-  // stereo 44.1kHz.
-  double BytesToTime(PRInt64 aBytes) const
-  {
-    NS_ABORT_IF_FALSE(mMetadataValid, "Requires valid metadata");
-    NS_ABORT_IF_FALSE(aBytes >= 0, "Must be >= 0");
-    return double(aBytes) / mSampleRate / mSampleSize;
-  }
-
-  // Returns the number of bytes that aTime represents based on the current
-  // audio parameters.  e.g.  1 second is 176400 bytes at 16-bit stereo
-  // 44.1kHz.
-  PRInt64 TimeToBytes(double aTime) const
-  {
-    NS_ABORT_IF_FALSE(mMetadataValid, "Requires valid metadata");
-    NS_ABORT_IF_FALSE(aTime >= 0.0f, "Must be >= 0");
-    return RoundDownToSample(PRInt64(aTime * mSampleRate * mSampleSize));
-  }
-
-  // Rounds aBytes down to the nearest complete sample.  Assumes beginning
-  // of byte range is already sample aligned by caller.
-  PRInt64 RoundDownToSample(PRInt64 aBytes) const
-  {
-    NS_ABORT_IF_FALSE(mMetadataValid, "Requires valid metadata");
-    NS_ABORT_IF_FALSE(aBytes >= 0, "Must be >= 0");
-    return aBytes - (aBytes % mSampleSize);
-  }
-
-  // Weak (raw) pointer to our decoder instance.  The decoder manages the
-  // lifetime of the state machine object, so it is guaranteed that the
-  // state machine will not outlive the decoder.  The decoder is not
-  // threadsafe, so this pointer must only be used to create runnable events
-  // targeted at the main thread.
-  nsWaveDecoder* mDecoder;
-
-  // Weak (raw) pointer to a media stream.  The decoder manages the lifetime
-  // of the stream, so it is guaranteed that the stream will live as long as
-  // the state machine.  The stream is threadsafe, but is only used on the
-  // playback thread except for create, open, and cancel, which are called
-  // from the main thread.
-  nsMediaStream* mStream;
-
-  // Our audio stream.  Created on demand when entering playback state.  It
-  // is destroyed when seeking begins and will not be reinitialized until
-  // playback resumes, so it is possible for this to be null.
-  nsRefPtr<nsAudioStream> mAudioStream;
-
-  // Maximum time to spend waiting for data during buffering.
-  TimeDuration mBufferingWait;
-
-  // Machine time that buffering began, used with mBufferingWait to time out
-  // buffering.
-  TimeStamp mBufferingStart;
-
-  // Download position where we should stop buffering.  Only accessed
-  // in the decoder thread.
-  PRInt64 mBufferingEndOffset;
-
-  /*
-    Metadata extracted from the WAVE header.  Used to initialize the audio
-    stream, and for byte<->time domain conversions.
-  */
-
-  // Number of samples per second.  Limited to range [100, 96000] in LoadFormatChunk.
-  PRUint32 mSampleRate;
-
-  // Number of channels.  Limited to range [1, 2] in LoadFormatChunk.
-  PRUint32 mChannels;
-
-  // Size of a single sample segment, which includes a sample for each
-  // channel (interleaved).
-  PRUint32 mSampleSize;
-
-  // The sample format of the PCM data.
-  nsAudioStream::SampleFormat mSampleFormat;
-
-  // Size of PCM data stored in the WAVE as reported by the data chunk in
-  // the media.
-  PRInt64 mWaveLength;
-
-  // Start offset of the PCM data in the media stream.  Extends mWaveLength
-  // bytes.
-  PRInt64 mWavePCMOffset;
-
-  /*
-    All member variables following this comment are accessed by both
-    threads and must be synchronized via mMonitor.
-  */
-  PRMonitor* mMonitor;
-
-  // The state to enter when the state machine loop iterates next.
-  State mState;
-
-  // A queued state transition.  This is used to record the next state
-  // transition when play or pause is requested during seeking or metadata
-  // loading to ensure a completed metadata load or seek returns to the most
-  // recently requested state on completion.
-  State mNextState;
-
-  // Current playback position in the stream.
-  PRInt64 mPlaybackPosition;
-
-  // Volume that the audio backend will be initialized with.
-  double mInitialVolume;
-
-  // Time position (in seconds) to seek to.  Set by Seek(double).
-  double mSeekTime;
-
-  // True once metadata has been parsed and validated. Users of mSampleRate,
-  // mChannels, mSampleSize, mSampleFormat, mWaveLength, mWavePCMOffset must
-  // check this flag before assuming the values are valid.
-  PRPackedBool mMetadataValid;
-
-  // True if an event to notify about a change in the playback position has
-  // been queued, but not yet run.  It is set to false when the event is
-  // run.  This allows coalescing of these events as they can be produced
-  // many times per second.
-  PRPackedBool mPositionChangeQueued;
-
-  // True if paused.  Tracks only the play/paused state.
-  PRPackedBool mPaused;
-
-  // True if playback of the audio stream has finished, and the audio stream
-  // has been drained. This means playback of the file has ended.
-  PRPackedBool mPlaybackEnded;
-};
-
-nsWaveStateMachine::nsWaveStateMachine(nsWaveDecoder* aDecoder,
-                                       TimeDuration aBufferWaitTime,
-                                       double aInitialVolume)
-  : mDecoder(aDecoder),
-    mStream(nsnull),
-    mBufferingWait(aBufferWaitTime),
-    mBufferingStart(),
-    mBufferingEndOffset(0),
-    mSampleRate(0),
-    mChannels(0),
-    mSampleSize(0),
-    mSampleFormat(nsAudioStream::FORMAT_S16_LE),
-    mWaveLength(0),
-    mWavePCMOffset(0),
-    mMonitor(nsnull),
-    mState(STATE_LOADING_METADATA),
-    mNextState(STATE_PAUSED),
-    mPlaybackPosition(0),
-    mInitialVolume(aInitialVolume),
-    mSeekTime(0.0f),
-    mMetadataValid(PR_FALSE),
-    mPositionChangeQueued(PR_FALSE),
-    mPaused(mNextState == STATE_PAUSED),
-    mPlaybackEnded(PR_FALSE)
-{
-  mMonitor = nsAutoMonitor::NewMonitor("nsWaveStateMachine");
-}
-
-nsWaveStateMachine::~nsWaveStateMachine()
-{
-  nsAutoMonitor::DestroyMonitor(mMonitor);
-}
-
-void
-nsWaveStateMachine::Shutdown()
-{
-  ChangeState(STATE_SHUTDOWN);
-}
-
-void
-nsWaveStateMachine::Play()
-{
-  nsAutoMonitor monitor(mMonitor);
-  mPaused = PR_FALSE;
-  mPlaybackEnded = PR_FALSE;
-  if (mState == STATE_ENDED) {
-    Seek(0);
-    return;
-  }
-  if (mState == STATE_LOADING_METADATA || mState == STATE_SEEKING) {
-    mNextState = STATE_PLAYING;
-  } else {
-    ChangeState(STATE_PLAYING);
-  }
-}
-
-void
-nsWaveStateMachine::SetVolume(double aVolume)
-{
-  nsAutoMonitor monitor(mMonitor);
-  mInitialVolume = aVolume;
-  if (mAudioStream) {
-    mAudioStream->SetVolume(aVolume);
-  }
-}
-
-void
-nsWaveStateMachine::Pause()
-{
-  nsAutoMonitor monitor(mMonitor);
-  mPaused = PR_TRUE;
-  if (mState == STATE_LOADING_METADATA || mState == STATE_SEEKING ||
-      mState == STATE_BUFFERING || mState == STATE_ENDED) {
-    mNextState = STATE_PAUSED;
-  } else if (mState == STATE_PLAYING) {
-    ChangeState(STATE_PAUSED);
-  }
-}
 
-void
-nsWaveStateMachine::Seek(double aTime)
-{
-  nsAutoMonitor monitor(mMonitor);
-  mPlaybackEnded = PR_FALSE;
-  mSeekTime = aTime;
-  if (mSeekTime < 0.0f) {
-    mSeekTime = 0.0f;
-  }
-  if (mState == STATE_LOADING_METADATA) {
-    mNextState = STATE_SEEKING;
-  } else if (mState != STATE_SEEKING) {
-    if (mState == STATE_ENDED) {
-      mNextState = mPaused ? STATE_PAUSED : STATE_PLAYING;
-    } else if (mState != STATE_BUFFERING) {
-      mNextState = mState;
-    }
-    ChangeState(STATE_SEEKING);
-  }
-  NS_ASSERTION(IsSeeking(), "IsSeeking() must return true when seeking");
-}
-
-double
-nsWaveStateMachine::GetDuration()
-{
-  nsAutoMonitor monitor(mMonitor);
-  if (mMetadataValid) {
-    return BytesToTime(GetDataLength());
-  }
-  return std::numeric_limits<double>::quiet_NaN();
-}
-
-PRUint32
-nsWaveStateMachine::GetChannels()
-{
-  nsAutoMonitor monitor(mMonitor);
-  if (mMetadataValid) {
-    return mChannels;
-  }
-  return 0;
-}
-
-PRUint32
-nsWaveStateMachine::GetSampleRate()
-{
-  nsAutoMonitor monitor(mMonitor);
-  if (mMetadataValid) {
-    return mSampleRate;
-  }
-  return 0;
-}
-
-PRBool
-nsWaveStateMachine::IsSeeking()
-{
-  nsAutoMonitor monitor(mMonitor);
-  return mState == STATE_SEEKING || mNextState == STATE_SEEKING;
-}
-
-PRBool
-nsWaveStateMachine::IsEnded()
-{
-  nsAutoMonitor monitor(mMonitor);
-  return mPlaybackEnded;
-}
-
-nsHTMLMediaElement::NextFrameStatus
-nsWaveStateMachine::GetNextFrameStatus()
-{
-  nsAutoMonitor monitor(mMonitor);
-  if (mState == STATE_BUFFERING)
-    return nsHTMLMediaElement::NEXT_FRAME_UNAVAILABLE_BUFFERING;
-  // If mMetadataValid is false then we can't call GetDataLength because
-  // we haven't got the length from the Wave header yet. But we know that
-  // if we haven't read the metadata then we don't have playable data.
-  if (mMetadataValid &&
-      mPlaybackPosition < mStream->GetCachedDataEnd(mPlaybackPosition) &&
-      mPlaybackPosition < mWavePCMOffset + GetDataLength())
-    return nsHTMLMediaElement::NEXT_FRAME_AVAILABLE;
-  return nsHTMLMediaElement::NEXT_FRAME_UNAVAILABLE;
-}
-
-double
-nsWaveStateMachine::GetTimeForPositionChange()
-{
-  nsAutoMonitor monitor(mMonitor);
-  mPositionChangeQueued = PR_FALSE;
-  return BytesToTime(mPlaybackPosition - mWavePCMOffset);
-}
-
-NS_IMETHODIMP
-nsWaveStateMachine::Run()
-{
-  // Monitor is held by this thread almost permanently, but must be manually
-  // dropped during long operations to prevent the main thread from blocking
-  // when calling methods on the state machine object.
-  nsAutoMonitor monitor(mMonitor);
-
-  for (;;) {
-    switch (mState) {
-    case STATE_LOADING_METADATA:
-      {
-        monitor.Exit();
-        PRBool loaded = LoadRIFFChunk() && LoadFormatChunk() && FindDataOffset();
-        monitor.Enter();
-
-        if (!loaded) {
-          ChangeState(STATE_ERROR);
-        }
-
-        if (mState == STATE_LOADING_METADATA) {
-          mMetadataValid = PR_TRUE;
-          if (mNextState != STATE_SEEKING) {
-            nsCOMPtr<nsIRunnable> event = NS_NewRunnableMethod(mDecoder, &nsWaveDecoder::MetadataLoaded);
-            NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
-          }
-          ChangeState(mNextState);
-        }
-      }
-      break;
-
-    case STATE_BUFFERING: {
-      TimeStamp now = TimeStamp::Now();
-      if (now - mBufferingStart < mBufferingWait &&
-          mStream->GetCachedDataEnd(mPlaybackPosition) < mBufferingEndOffset &&
-          !mStream->IsDataCachedToEndOfStream(mPlaybackPosition) &&
-          !mStream->IsSuspendedByCache()) {
-        LOG(PR_LOG_DEBUG,
-            ("In buffering: buffering data until %d bytes available or %f seconds\n",
-             PRUint32(mBufferingEndOffset - mStream->GetCachedDataEnd(mPlaybackPosition)),
-             (mBufferingWait - (now - mBufferingStart)).ToSeconds()));
-        monitor.Wait(PR_MillisecondsToInterval(1000));
-      } else {
-        ChangeState(mNextState);
-        UpdateReadyState();
-      }
-
-      break;
-    }
-
-    case STATE_PLAYING: {
-      if (!mAudioStream) {
-        OpenAudioStream(monitor);
-        if (!mAudioStream) {
-          ChangeState(STATE_ERROR);
-          break;
-        }
-      }
-
-      TimeStamp now = TimeStamp::Now();
-      TimeStamp lastWakeup = now -
-        TimeDuration::FromMilliseconds(AUDIO_BUFFER_LENGTH);
-
-      do {
-        TimeDuration sleepTime = now - lastWakeup;
-        lastWakeup = now;
-
-        // We aim to have AUDIO_BUFFER_LENGTH milliseconds of audio
-        // buffered, but only sleep for AUDIO_BUFFER_WAKEUP milliseconds
-        // (waking early to refill before the backend underruns).  Since we
-        // wake early, we only buffer sleepTime milliseconds of audio since
-        // there is still AUDIO_BUFFER_LENGTH - sleepTime milliseconds of
-        // audio buffered.
-        TimeDuration targetTime =
-          TimeDuration::FromMilliseconds(AUDIO_BUFFER_LENGTH);
-        if (sleepTime < targetTime) {
-          targetTime = sleepTime;
-        }
-
-        PRInt64 len = TimeToBytes(double(targetTime.ToSeconds()));
-
-        PRInt64 leftToPlay =
-          GetDataLength() - (mPlaybackPosition - mWavePCMOffset);
-        if (leftToPlay <= len) {
-          len = leftToPlay;
-          ChangeState(STATE_ENDED);
-        }
-
-        PRInt64 availableOffset = mStream->GetCachedDataEnd(mPlaybackPosition);
-
-        // Don't buffer if we're at the end of the stream, or if the
-        // load has been suspended by the cache (in the latter case
-        // we need to advance playback to free up cache space).
-        if (mState != STATE_ENDED &&
-            availableOffset < mPlaybackPosition + len &&
-            !mStream->IsSuspendedByCache()) {
-          mBufferingStart = now;
-          mBufferingEndOffset = mPlaybackPosition +
-            TimeToBytes(double(mBufferingWait.ToSeconds()));
-          mBufferingEndOffset = PR_MAX(mPlaybackPosition + len,
-                                       mBufferingEndOffset);
-          mNextState = mState;
-          ChangeState(STATE_BUFFERING);
-
-          UpdateReadyState();
-          break;
-        }
-
-        if (len > 0) {
-          nsAutoArrayPtr<char> buf(new char[size_t(len)]);
-          PRInt64 got = 0;
-
-          monitor.Exit();
-          PRBool ok = ReadAll(buf.get(), len, &got);
-          monitor.Enter();
-
-          // Reached EOF.
-          if (!ok) {
-            ChangeState(STATE_ENDED);
-            if (got == 0) {
-              break;
-            }
-          }
-
-          // Calculate difference between the current media stream position
-          // and the expected end of the PCM data.
-          PRInt64 endDelta = mWavePCMOffset + mWaveLength - mPlaybackPosition;
-          if (endDelta < 0) {
-            // Read past the end of PCM data.  Adjust got to avoid playing
-            // back trailing data.
-            got -= -endDelta;
-            ChangeState(STATE_ENDED);
-          }
-
-          if (mState == STATE_ENDED) {
-            got = RoundDownToSample(got);
-          }
-
-          PRUint32 sampleSize = mSampleFormat == nsAudioStream::FORMAT_U8 ? 1 : 2;
-          NS_ABORT_IF_FALSE(got % sampleSize == 0, "Must write complete samples");
-          PRUint32 lengthInSamples = PRUint32(got / sampleSize);
-
-          monitor.Exit();
-          mAudioStream->Write(buf.get(), lengthInSamples, PR_FALSE);
-          monitor.Enter();
-
-          FirePositionChanged(PR_FALSE);
-        }
-
-        if (mState == STATE_PLAYING) {
-          monitor.Wait(PR_MillisecondsToInterval(AUDIO_BUFFER_WAKEUP));
-          now = TimeStamp::Now();
-        }
-      } while (mState == STATE_PLAYING);
-      break;
-    }
-
-    case STATE_SEEKING:
-      {
-        CloseAudioStream();
-
-        mSeekTime = NS_MIN(mSeekTime, GetDuration());
-        double seekTime = mSeekTime;
-
-        // Calculate relative offset within PCM data.
-        PRInt64 position = RoundDownToSample(TimeToBytes(seekTime));
-        NS_ABORT_IF_FALSE(position >= 0 && position <= GetDataLength(),
-                          "Invalid seek position");
-        // Convert to absolute offset within stream.
-        position += mWavePCMOffset;
-
-        // If in the midst of a seek, report the requested seek time
-        // as the current time as required by step 8 of 4.8.10.9 'Seeking'
-        // in the WHATWG spec.
-        PRInt64 oldPosition = mPlaybackPosition;
-        mPlaybackPosition = position;
-        FirePositionChanged(PR_TRUE);
-
-        monitor.Exit();
-        nsCOMPtr<nsIRunnable> startEvent =
-          NS_NewRunnableMethod(mDecoder, &nsWaveDecoder::SeekingStarted);
-        NS_DispatchToMainThread(startEvent, NS_DISPATCH_SYNC);
-        monitor.Enter();
-
-        if (mState == STATE_SHUTDOWN) {
-          break;
-        }
-
-        monitor.Exit();
-        nsresult rv;
-        rv = mStream->Seek(nsISeekableStream::NS_SEEK_SET, position);
-        monitor.Enter();
-        if (NS_FAILED(rv)) {
-          NS_WARNING("Seek failed");
-          mPlaybackPosition = oldPosition;
-          FirePositionChanged(PR_TRUE);
-        }
-
-        if (mState == STATE_SHUTDOWN) {
-          break;
-        }
-
-        if (mState == STATE_SEEKING && mSeekTime == seekTime) {
-          // Special case #1: if a seek was requested during metadata load,
-          // mNextState will have been clobbered.  This can only happen when
-          // we're instantiating a decoder to service a seek request after
-          // playback has ended, so we know that the clobbered mNextState
-          // was PAUSED.
-          // Special case #2: if a seek is requested after the state machine
-          // entered STATE_ENDED but before the user has seen the ended
-          // event, playback has not ended as far as the user's
-          // concerned--the state machine needs to return to the last
-          // playback state.
-          // Special case #3: if seeking to the end of the media, transition
-          // directly into STATE_ENDED.
-          State nextState = mNextState;
-          if (nextState == STATE_SEEKING) {
-            nextState = STATE_PAUSED;
-          } else if (nextState == STATE_ENDED) {
-            nextState = mPaused ? STATE_PAUSED : STATE_PLAYING;
-          } else if (GetDuration() == seekTime) {
-            nextState = STATE_ENDED;
-          }
-          ChangeState(nextState);
-        }
-
-        if (mState != STATE_SEEKING) {
-          monitor.Exit();
-          nsCOMPtr<nsIRunnable> stopEvent =
-            NS_NewRunnableMethod(mDecoder, &nsWaveDecoder::SeekingStopped);
-          NS_DispatchToMainThread(stopEvent, NS_DISPATCH_SYNC);
-          monitor.Enter();
-        }
-      }
-      break;
-
-    case STATE_PAUSED:
-      monitor.Wait();
-      break;
-
-    case STATE_ENDED:
-      FirePositionChanged(PR_TRUE);
-
-      if (mAudioStream) {
-        monitor.Exit();
-        mAudioStream->Drain();
-        monitor.Enter();
-
-        // After the drain call the audio stream is unusable. Close it so that
-        // next time audio is used a new stream is created.
-        CloseAudioStream();
-      }
-
-      mPlaybackEnded = PR_TRUE;
-
-      if (mState == STATE_ENDED) {
-        nsCOMPtr<nsIRunnable> event =
-          NS_NewRunnableMethod(mDecoder, &nsWaveDecoder::PlaybackEnded);
-        NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
-
-        // We've finished playback. Shutdown the state machine thread, 
-        // in order to save memory on thread stacks, particuarly on Linux.
-        event = new ShutdownThreadEvent(mDecoder->mPlaybackThread);
-        NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
-        mDecoder->mPlaybackThread = nsnull;
-        return NS_OK;
-      }
-      break;
-
-    case STATE_ERROR:
-      {
-        nsCOMPtr<nsIRunnable> event = NS_NewRunnableMethod(mDecoder, &nsWaveDecoder::DecodeError);
-        NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
-
-        monitor.Wait();
-
-        if (mState != STATE_SHUTDOWN) {
-          NS_WARNING("Invalid state transition");
-          ChangeState(STATE_ERROR);
-        }
-      }
-      break;
-
-    case STATE_SHUTDOWN:
-      mPlaybackEnded = PR_TRUE;
-      CloseAudioStream();
-      return NS_OK;
-    }
-  }
-
-  return NS_OK;
-}
-
-#if defined(DEBUG)
-static PRBool
-IsValidStateTransition(State aStartState, State aEndState)
-{
-  if (aEndState == STATE_SHUTDOWN) {
-    return PR_TRUE;
-  }
-
-  if (aStartState == aEndState) {
-    LOG(PR_LOG_WARNING, ("Transition to current state requested"));
-    return PR_TRUE;
-  }
-
-  switch (aStartState) {
-  case STATE_LOADING_METADATA:
-    if (aEndState == STATE_PLAYING || aEndState == STATE_SEEKING ||
-        aEndState == STATE_PAUSED || aEndState == STATE_ERROR)
-      return PR_TRUE;
-    break;
-  case STATE_BUFFERING:
-    if (aEndState == STATE_PLAYING || aEndState == STATE_PAUSED ||
-        aEndState == STATE_SEEKING)
-      return PR_TRUE;
-    break;
-  case STATE_PLAYING:
-    if (aEndState == STATE_BUFFERING || aEndState == STATE_SEEKING ||
-        aEndState == STATE_ENDED || aEndState == STATE_PAUSED)
-      return PR_TRUE;
-    break;
-  case STATE_SEEKING:
-    if (aEndState == STATE_PLAYING || aEndState == STATE_PAUSED ||
-        aEndState == STATE_ENDED)
-      return PR_TRUE;
-    break;
-  case STATE_PAUSED:
-    if (aEndState == STATE_PLAYING || aEndState == STATE_SEEKING)
-      return PR_TRUE;
-    break;
-  case STATE_ENDED:
-    if (aEndState == STATE_SEEKING)
-      return PR_TRUE;
-    /* fallthrough */
-  case STATE_ERROR:
-  case STATE_SHUTDOWN:
-    break;
-  }
-
-  LOG(PR_LOG_ERROR, ("Invalid state transition from %d to %d", aStartState, aEndState));
-  return PR_FALSE;
-}
-#endif
-
-void
-nsWaveStateMachine::ChangeState(State aState)
+nsDecoderStateMachine* nsWaveDecoder::CreateStateMachine()
 {
-  nsAutoMonitor monitor(mMonitor);
-  if (mState == STATE_SHUTDOWN) {
-    LOG(PR_LOG_WARNING, ("In shutdown, state transition ignored"));
-    return;
-  }
-#if defined(DEBUG)
-  NS_ABORT_IF_FALSE(IsValidStateTransition(mState, aState), "Invalid state transition");
-#endif
-  mState = aState;
-  monitor.NotifyAll();
-}
-
-void
-nsWaveStateMachine::OpenAudioStream(nsAutoMonitor& aMonitor)
-{
-  NS_ABORT_IF_FALSE(mMetadataValid,
-                    "Attempting to initialize audio stream with invalid metadata");
-
-  nsRefPtr<nsAudioStream> audioStream = nsAudioStream::AllocateStream();
-  if (!audioStream) {
-    LOG(PR_LOG_ERROR, ("Could not create audio stream"));
-    return;
-  }
-
-  // Drop the monitor while initializing the stream because remote
-  // audio streams wait on a synchronous event running on the main
-  // thread, and holding the decoder monitor while waiting for this
-  // can result in deadlocks.
-  aMonitor.Exit();
-  audioStream->Init(mChannels, mSampleRate, mSampleFormat);
-  aMonitor.Enter();
-
-  mAudioStream = audioStream;
-  mAudioStream->SetVolume(mInitialVolume);
-}
-
-void
-nsWaveStateMachine::CloseAudioStream()
-{
-  if (mAudioStream) {
-    mAudioStream->Shutdown();
-    mAudioStream = nsnull;
-  }
-}
-
-nsMediaDecoder::Statistics
-nsWaveStateMachine::GetStatistics()
-{
-  nsMediaDecoder::Statistics result;
-  nsAutoMonitor monitor(mMonitor);
-  result.mDownloadRate = mStream->GetDownloadRate(&result.mDownloadRateReliable);
-  result.mPlaybackRate = mSampleRate*mChannels*mSampleSize;
-  result.mPlaybackRateReliable = PR_TRUE;
-  result.mTotalBytes = mStream->GetLength();
-  result.mDownloadPosition = mStream->GetCachedDataEnd(mPlaybackPosition);
-  result.mDecoderPosition = mPlaybackPosition;
-  result.mPlaybackPosition = mPlaybackPosition;
-  return result;
-}
-
-void
-nsWaveStateMachine::NotifyBytesConsumed(PRInt64 aBytes)
-{
-  nsAutoMonitor monitor(mMonitor);
-  mPlaybackPosition += aBytes;
-}
-
-static PRUint32
-ReadUint32BE(const char** aBuffer)
-{
-  PRUint32 result =
-    PRUint8((*aBuffer)[0]) << 24 |
-    PRUint8((*aBuffer)[1]) << 16 |
-    PRUint8((*aBuffer)[2]) << 8 |
-    PRUint8((*aBuffer)[3]);
-  *aBuffer += sizeof(PRUint32);
-  return result;
-}
-
-static PRUint32
-ReadUint32LE(const char** aBuffer)
-{
-  PRUint32 result =
-    PRUint8((*aBuffer)[3]) << 24 |
-    PRUint8((*aBuffer)[2]) << 16 |
-    PRUint8((*aBuffer)[1]) << 8 |
-    PRUint8((*aBuffer)[0]);
-  *aBuffer += sizeof(PRUint32);
-  return result;
-}
-
-static PRUint16
-ReadUint16LE(const char** aBuffer)
-{
-  PRUint16 result =
-    PRUint8((*aBuffer)[1]) << 8 |
-    PRUint8((*aBuffer)[0]) << 0;
-  *aBuffer += sizeof(PRUint16);
-  return result;
-}
-
-PRBool
-nsWaveStateMachine::IsShutdown()
-{
-  nsAutoMonitor monitor(mMonitor);
-  return mState == STATE_SHUTDOWN;
-}
-
-PRBool
-nsWaveStateMachine::ReadAll(char* aBuf, PRInt64 aSize, PRInt64* aBytesRead = nsnull)
-{
-  PRUint32 got = 0;
-  if (aBytesRead) {
-    *aBytesRead = 0;
-  }
-  do {
-    PRUint32 read = 0;
-    if (NS_FAILED(mStream->Read(aBuf + got, PRUint32(aSize - got), &read))) {
-      NS_WARNING("Stream read failed");
-      return PR_FALSE;
-    }
-    if (IsShutdown() || read == 0) {
-      return PR_FALSE;
-    }
-    NotifyBytesConsumed(read);
-    got += read;
-    if (aBytesRead) {
-      *aBytesRead = got;
-    }
-  } while (got != aSize);
-  return PR_TRUE;
-}
-
-PRBool
-nsWaveStateMachine::LoadRIFFChunk()
-{
-  char riffHeader[RIFF_INITIAL_SIZE];
-  const char* p = riffHeader;
-
-  NS_ABORT_IF_FALSE(mStream->Tell() == 0,
-                    "LoadRIFFChunk called when stream in invalid state");
-
-  if (!ReadAll(riffHeader, sizeof(riffHeader))) {
-    return PR_FALSE;
-  }
-
-  if (ReadUint32BE(&p) != RIFF_CHUNK_MAGIC) {
-    NS_WARNING("Stream data not in RIFF format");
-    return PR_FALSE;
-  }
-
-  // Skip over RIFF size field.
-  p += 4;
-
-  if (ReadUint32BE(&p) != WAVE_CHUNK_MAGIC) {
-    NS_WARNING("Expected WAVE chunk");
-    return PR_FALSE;
-  }
-
-  return PR_TRUE;
-}
-
-PRBool
-nsWaveStateMachine::ScanForwardUntil(PRUint32 aWantedChunk, PRUint32* aChunkSize)
-{
-  NS_ABORT_IF_FALSE(aChunkSize, "Require aChunkSize argument");
-  *aChunkSize = 0;
-
-  for (;;) {
-    char chunkHeader[8];
-    const char* p = chunkHeader;
-
-    if (!ReadAll(chunkHeader, sizeof(chunkHeader))) {
-      return PR_FALSE;
-    }
-
-    PRUint32 magic = ReadUint32BE(&p);
-    PRUint32 chunkSize = ReadUint32LE(&p);
-
-    if (magic == aWantedChunk) {
-      *aChunkSize = chunkSize;
-      return PR_TRUE;
-    }
-
-    // RIFF chunks are two-byte aligned, so round up if necessary.
-    chunkSize += chunkSize % 2;
-
-    while (chunkSize > 0) {
-      PRUint32 size = PR_MIN(chunkSize, 1 << 16);
-      nsAutoArrayPtr<char> chunk(new char[size]);
-      if (!ReadAll(chunk.get(), size)) {
-        return PR_FALSE;
-      }
-      chunkSize -= size;
-    }
-  }
-}
-
-PRBool
-nsWaveStateMachine::LoadFormatChunk()
-{
-  PRUint32 fmtSize, rate, channels, sampleSize, sampleFormat;
-  char waveFormat[WAVE_FORMAT_CHUNK_SIZE];
-  const char* p = waveFormat;
-
-  // RIFF chunks are always word (two byte) aligned.
-  NS_ABORT_IF_FALSE(mStream->Tell() % 2 == 0,
-                    "LoadFormatChunk called with unaligned stream");
-
-  // The "format" chunk may not directly follow the "riff" chunk, so skip
-  // over any intermediate chunks.
-  if (!ScanForwardUntil(FRMT_CHUNK_MAGIC, &fmtSize)) {
-      return PR_FALSE;
-  }
-
-  if (!ReadAll(waveFormat, sizeof(waveFormat))) {
-    return PR_FALSE;
-  }
-
-  if (ReadUint16LE(&p) != WAVE_FORMAT_ENCODING_PCM) {
-    NS_WARNING("WAVE is not uncompressed PCM, compressed encodings are not supported");
-    return PR_FALSE;
-  }
-
-  channels = ReadUint16LE(&p);
-  rate = ReadUint32LE(&p);
-
-  // Skip over average bytes per second field.
-  p += 4;
-
-  sampleSize = ReadUint16LE(&p);
-
-  sampleFormat = ReadUint16LE(&p);
-
-  // PCM encoded WAVEs are not expected to have an extended "format" chunk,
-  // but I have found WAVEs that have a extended "format" chunk with an
-  // extension size of 0 bytes.  Be polite and handle this rather than
-  // considering the file invalid.  This code skips any extension of the
-  // "format" chunk.
-  if (fmtSize > WAVE_FORMAT_CHUNK_SIZE) {
-    char extLength[2];
-    const char* p = extLength;
-
-    if (!ReadAll(extLength, sizeof(extLength))) {
-      return PR_FALSE;
-    }
-
-    PRUint16 extra = ReadUint16LE(&p);
-    if (fmtSize - (WAVE_FORMAT_CHUNK_SIZE + 2) != extra) {
-      NS_WARNING("Invalid extended format chunk size");
-      return PR_FALSE;
-    }
-    extra += extra % 2;
-
-    if (extra > 0) {
-      nsAutoArrayPtr<char> chunkExtension(new char[extra]);
-      if (!ReadAll(chunkExtension.get(), extra)) {
-        return PR_FALSE;
-      }
-    }
-  }
-
-  // RIFF chunks are always word (two byte) aligned.
-  NS_ABORT_IF_FALSE(mStream->Tell() % 2 == 0,
-                    "LoadFormatChunk left stream unaligned");
-
-  // Make sure metadata is fairly sane.  The rate check is fairly arbitrary,
-  // but the channels check is intentionally limited to mono or stereo
-  // because that's what the audio backend currently supports.
-  if (rate < 100 || rate > 96000 ||
-      channels < 1 || channels > 2 ||
-      (sampleSize != 1 && sampleSize != 2 && sampleSize != 4) ||
-      (sampleFormat != 8 && sampleFormat != 16)) {
-    NS_WARNING("Invalid WAVE metadata");
-    return PR_FALSE;
-  }
-
-  nsAutoMonitor monitor(mMonitor);
-  mSampleRate = rate;
-  mChannels = channels;
-  mSampleSize = sampleSize;
-  if (sampleFormat == 8) {
-    mSampleFormat = nsAudioStream::FORMAT_U8;
-  } else {
-    mSampleFormat = nsAudioStream::FORMAT_S16_LE;
-  }
-  return PR_TRUE;
-}
-
-PRBool
-nsWaveStateMachine::FindDataOffset()
-{
-  // RIFF chunks are always word (two byte) aligned.
-  NS_ABORT_IF_FALSE(mStream->Tell() % 2 == 0,
-                    "FindDataOffset called with unaligned stream");
-
-  // The "data" chunk may not directly follow the "format" chunk, so skip
-  // over any intermediate chunks.
-  PRUint32 length;
-  if (!ScanForwardUntil(DATA_CHUNK_MAGIC, &length)) {
-    return PR_FALSE;
-  }
-
-  PRInt64 offset = mStream->Tell();
-  if (offset <= 0 || offset > PR_UINT32_MAX) {
-    NS_WARNING("PCM data offset out of range");
-    return PR_FALSE;
-  }
-
-  nsAutoMonitor monitor(mMonitor);
-  mWaveLength = length;
-  mWavePCMOffset = PRUint32(offset);
-  return PR_TRUE;
-}
-
-PRInt64
-nsWaveStateMachine::GetDataLength()
-{
-  NS_ABORT_IF_FALSE(mMetadataValid,
-                    "Attempting to initialize audio stream with invalid metadata");
-
-  PRInt64 length = mWaveLength;
-  // If the decoder has a valid content length, and it's shorter than the
-  // expected length of the PCM data, calculate the playback duration from
-  // the content length rather than the expected PCM data length.
-  PRInt64 streamLength = mStream->GetLength();
-  if (streamLength >= 0) {
-    PRInt64 dataLength = PR_MAX(0, streamLength - mWavePCMOffset);
-    length = PR_MIN(dataLength, length);
-  }
-  return length;
-}
-
-void
-nsWaveStateMachine::FirePositionChanged(PRBool aCoalesce)
-{
-  if (aCoalesce && mPositionChangeQueued) {
-    return;
-  }
-
-  mPositionChangeQueued = PR_TRUE;
-  nsCOMPtr<nsIRunnable> event = NS_NewRunnableMethod(mDecoder, &nsWaveDecoder::PlaybackPositionChanged);
-  NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
-}
-
-nsresult
-nsWaveStateMachine::GetBuffered(nsTimeRanges* aBuffered)
-{
-  PRInt64 startOffset = mStream->GetNextCachedData(mWavePCMOffset);
-  while (startOffset >= 0) {
-    PRInt64 endOffset = mStream->GetCachedDataEnd(startOffset);
-    // Bytes [startOffset..endOffset] are cached.
-    aBuffered->Add(BytesToTime(startOffset - mWavePCMOffset),
-                   BytesToTime(endOffset - mWavePCMOffset));
-    startOffset = mStream->GetNextCachedData(endOffset);
-  }
-  return NS_OK;
-}
-
-NS_IMPL_THREADSAFE_ISUPPORTS1(nsWaveDecoder, nsIObserver)
-
-nsWaveDecoder::nsWaveDecoder()
-  : mInitialVolume(1.0f),
-    mCurrentTime(0.0f),
-    mEndedDuration(std::numeric_limits<double>::quiet_NaN()),
-    mEnded(PR_FALSE),
-    mSeekable(PR_TRUE),
-    mResourceLoaded(PR_FALSE),
-    mMetadataLoadedReported(PR_FALSE),
-    mResourceLoadedReported(PR_FALSE)
-{
-  MOZ_COUNT_CTOR(nsWaveDecoder);
-
-#ifdef PR_LOGGING
-  if (!gWaveDecoderLog) {
-    gWaveDecoderLog = PR_NewLogModule("nsWaveDecoder");
-  }
-#endif
-}
-
-nsWaveDecoder::~nsWaveDecoder()
-{
-  MOZ_COUNT_DTOR(nsWaveDecoder);
-  UnpinForSeek();
-}
-
-PRBool
-nsWaveDecoder::Init(nsHTMLMediaElement* aElement)
-{
-  nsMediaDecoder::Init(aElement);
-
-  nsContentUtils::RegisterShutdownObserver(this);
-
-  mPlaybackStateMachine = new nsWaveStateMachine(this,
-    TimeDuration::FromMilliseconds(BUFFERING_TIMEOUT),
-    mInitialVolume);
-  NS_ENSURE_TRUE(mPlaybackStateMachine, PR_FALSE);
-
-  return PR_TRUE;
-}
-
-nsMediaStream*
-nsWaveDecoder::GetCurrentStream()
-{
-  return mStream;
-}
-
-already_AddRefed<nsIPrincipal>
-nsWaveDecoder::GetCurrentPrincipal()
-{
-  if (!mStream) {
-    return nsnull;
-  }
-  return mStream->GetCurrentPrincipal();
-}
-
-double
-nsWaveDecoder::GetCurrentTime()
-{
-  return mCurrentTime;
+  return new nsBuiltinDecoderStateMachine(this, new nsWaveReader(this));
 }
-
-nsresult
-nsWaveDecoder::StartStateMachineThread()
-{
-  NS_ASSERTION(mPlaybackStateMachine, "Must have state machine");
-  if (mPlaybackThread) {
-    return NS_OK;
-  }
-  nsresult rv = NS_NewThread(getter_AddRefs(mPlaybackThread));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  return mPlaybackThread->Dispatch(mPlaybackStateMachine, NS_DISPATCH_NORMAL);
-}
-
-nsresult
-nsWaveDecoder::Seek(double aTime)
-{
-  if (mPlaybackStateMachine) {
-    mEnded = PR_FALSE;
-    mCurrentTime = aTime;
-    PinForSeek();
-    mPlaybackStateMachine->Seek(aTime);
-    return StartStateMachineThread();
-  }
-
-  return NS_ERROR_FAILURE;
-}
-
-nsresult
-nsWaveDecoder::PlaybackRateChanged()
-{
-  return NS_ERROR_NOT_IMPLEMENTED;
-}
-
-double
-nsWaveDecoder::GetDuration()
-{
-  if (mPlaybackStateMachine) {
-    return mPlaybackStateMachine->GetDuration();
-  }
-  return mEndedDuration;
-}
-
-void
-nsWaveDecoder::Pause()
-{
-  if (mPlaybackStateMachine) {
-    mPlaybackStateMachine->Pause();
-  }
-}
-
-void
-nsWaveDecoder::SetVolume(double aVolume)
-{
-  mInitialVolume = aVolume;
-  if (mPlaybackStateMachine) {
-    mPlaybackStateMachine->SetVolume(aVolume);
-  }
-}
-
-nsresult
-nsWaveDecoder::Play()
-{
-  if (mPlaybackStateMachine) {
-    mEnded = PR_FALSE;
-    mPlaybackStateMachine->Play();
-    return StartStateMachineThread();
-  }
-
-  return NS_ERROR_FAILURE;
-}
-
-void
-nsWaveDecoder::Stop()
-{
-  if (mPlaybackStateMachine) {
-    mPlaybackStateMachine->Shutdown();
-  }
-
-  if (mStream) {
-    mStream->Close();
-  }
-
-  if (mPlaybackThread) {
-    mPlaybackThread->Shutdown();
-  }
-
-  if (mPlaybackStateMachine) {
-    mEndedDuration = mPlaybackStateMachine->GetDuration();
-    mEnded = mPlaybackStateMachine->IsEnded();
-  }
-
-  mPlaybackThread = nsnull;
-  mPlaybackStateMachine = nsnull;
-  mStream = nsnull;
-
-  nsContentUtils::UnregisterShutdownObserver(this);
-}
-
-nsresult
-nsWaveDecoder::Load(nsMediaStream* aStream, nsIStreamListener** aStreamListener,
-                    nsMediaDecoder* aCloneDonor)
-{
-  NS_ASSERTION(aStream, "A stream should be provided");
-
-  if (aStreamListener) {
-    *aStreamListener = nsnull;
-  }
-
-  mStream = aStream;
-
-  nsresult rv = mStream->Open(aStreamListener);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  mPlaybackStateMachine->SetStream(mStream);
-
-  rv = NS_NewThread(getter_AddRefs(mPlaybackThread));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  rv = mPlaybackThread->Dispatch(mPlaybackStateMachine, NS_DISPATCH_NORMAL);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  return NS_OK;
-}
-
-void
-nsWaveDecoder::MetadataLoaded()
-{
-  if (mShuttingDown) {
-    return;
-  }
-
-  if (mElement) {
-    mElement->MetadataLoaded(mPlaybackStateMachine->GetChannels(),
-                             mPlaybackStateMachine->GetSampleRate());
-    mElement->FirstFrameLoaded(mResourceLoaded);
-  }
-
-  mMetadataLoadedReported = PR_TRUE;
-
-  if (mResourceLoaded) {
-    ResourceLoaded();
-  } else {
-    StartProgress();
-  }
-}
-
-void
-nsWaveDecoder::PlaybackEnded()
-{
-  if (mShuttingDown) {
-    return;
-  }
-
-  if (!mPlaybackStateMachine->IsEnded()) {
-    return;
-  }
-  mEnded = PR_TRUE;
-
-  // Update ready state; now that we've finished playback, we should
-  // switch to HAVE_CURRENT_DATA.
-  UpdateReadyStateForData();
-  if (mElement) {
-    mElement->PlaybackEnded();
-  }
-}
-
-void
-nsWaveDecoder::ResourceLoaded()
-{
-  if (mShuttingDown) {
-    return;
-  }
-
-  mResourceLoaded = PR_TRUE;
-
-  if (!mMetadataLoadedReported || mResourceLoadedReported)
-    return;
-
-  StopProgress();
-
-  if (mElement) {
-    // Ensure the final progress event gets fired
-    mElement->ResourceLoaded();
-  }
-
-  mResourceLoadedReported = PR_TRUE;
-}
-
-void
-nsWaveDecoder::NetworkError()
-{
-  if (mShuttingDown) {
-    return;
-  }
-  if (mElement) {
-    mElement->NetworkError();
-  }
-  Shutdown();
-}
-
-PRBool
-nsWaveDecoder::IsSeeking() const
-{
-  if (mPlaybackStateMachine) {
-    return mPlaybackStateMachine->IsSeeking();
-  }
-  return PR_FALSE;
-}
-
-PRBool
-nsWaveDecoder::IsEnded() const
-{
-  return mEnded;
-}
-
-nsMediaDecoder::Statistics
-nsWaveDecoder::GetStatistics()
-{
-  if (!mPlaybackStateMachine)
-    return Statistics();
-  return mPlaybackStateMachine->GetStatistics();
-}
-
-void
-nsWaveDecoder::NotifySuspendedStatusChanged()
-{
-  if (mStream->IsSuspendedByCache() && mElement) {
-    // if this is an autoplay element, we need to kick off its autoplaying
-    // now so we consume data and hopefully free up cache space
-    mElement->NotifyAutoplayDataReady();
-  }
-}
-
-void
-nsWaveDecoder::NotifyBytesDownloaded()
-{
-  UpdateReadyStateForData();
-  Progress(PR_FALSE);
-}
-
-void
-nsWaveDecoder::NotifyDownloadEnded(nsresult aStatus)
-{
-  if (NS_SUCCEEDED(aStatus)) {
-    ResourceLoaded();
-  } else if (aStatus == NS_BINDING_ABORTED) {
-    // Download has been cancelled by user.
-    if (mElement) {
-      mElement->LoadAborted();
-    }
-  } else if (aStatus != NS_BASE_STREAM_CLOSED) {
-    NetworkError();
-  }
-  UpdateReadyStateForData();
-}
-
-void
-nsWaveDecoder::Shutdown()
-{
-  if (mShuttingDown)
-    return;
-
-  mShuttingDown = PR_TRUE;
-
-  nsMediaDecoder::Shutdown();
-
-  // An event that gets posted to the main thread, when the media element is
-  // being destroyed, to destroy the decoder. Since the decoder shutdown can
-  // block and post events this cannot be done inside destructor calls. So
-  // this event is posted asynchronously to the main thread to perform the
-  // shutdown.
-  nsCOMPtr<nsIRunnable> event =
-    NS_NewRunnableMethod(this, &nsWaveDecoder::Stop);
-  NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
-}
-
-nsresult
-nsWaveDecoder::Observe(nsISupports* aSubject, const char* aTopic, const PRUnichar* aData)
-{
-  if (strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) {
-    Shutdown();
-  }
-  return NS_OK;
-}
-
-void
-nsWaveDecoder::NextFrameUnavailableBuffering()
-{
-  NS_ASSERTION(NS_IsMainThread(), "Should be called on main thread");
-  if (!mElement || mShuttingDown || !mPlaybackStateMachine)
-    return;
-
-  mElement->UpdateReadyStateForData(nsHTMLMediaElement::NEXT_FRAME_UNAVAILABLE_BUFFERING);
-}
-
-void
-nsWaveDecoder::NextFrameAvailable()
-{
-  NS_ASSERTION(NS_IsMainThread(), "Should be called on main thread");
-  if (!mElement || mShuttingDown || !mPlaybackStateMachine)
-    return;
-
-  if (!mMetadataLoadedReported) {
-    mElement->UpdateReadyStateForData(nsHTMLMediaElement::NEXT_FRAME_UNAVAILABLE);
-  } else {
-    mElement->UpdateReadyStateForData(nsHTMLMediaElement::NEXT_FRAME_AVAILABLE);
-  }
-}
-
-void
-nsWaveDecoder::NextFrameUnavailable()
-{
-  NS_ASSERTION(NS_IsMainThread(), "Should be called on main thread");
-  if (!mElement || mShuttingDown || !mPlaybackStateMachine)
-    return;
-
-  mElement->UpdateReadyStateForData(nsHTMLMediaElement::NEXT_FRAME_UNAVAILABLE);
-}
-
-void
-nsWaveDecoder::UpdateReadyStateForData()
-{
-  NS_ASSERTION(NS_IsMainThread(), "Should be called on main thread");
-  if (!mElement || mShuttingDown || !mPlaybackStateMachine)
-    return;
-
-  nsHTMLMediaElement::NextFrameStatus frameStatus =
-    mPlaybackStateMachine->GetNextFrameStatus();
-  if (frameStatus == nsHTMLMediaElement::NEXT_FRAME_AVAILABLE &&
-      !mMetadataLoadedReported) {
-    frameStatus = nsHTMLMediaElement::NEXT_FRAME_UNAVAILABLE;
-  }
-  mElement->UpdateReadyStateForData(frameStatus);
-}
-
-void
-nsWaveDecoder::SeekingStarted()
-{
-  if (mShuttingDown) {
-    return;
-  }
-
-  if (mElement) {
-    UpdateReadyStateForData();
-    mElement->SeekStarted();
-  }
-}
-
-void
-nsWaveDecoder::SeekingStopped()
-{
-  UnpinForSeek();
-  if (mShuttingDown) {
-    return;
-  }
-
-  if (mElement) {
-    UpdateReadyStateForData();
-    mElement->SeekCompleted();
-  }
-}
-
-void
-nsWaveDecoder::DecodeError()
-{
-  if (mShuttingDown) {
-    return;
-  }
-  if (mElement) {
-    mElement->DecodeError();
-  }
-  Shutdown();
-}
-
-void
-nsWaveDecoder::PlaybackPositionChanged()
-{
-  if (mShuttingDown) {
-    return;
-  }
-
-  double lastTime = mCurrentTime;
-
-  if (mPlaybackStateMachine) {
-    mCurrentTime = mPlaybackStateMachine->GetTimeForPositionChange();
-  }
-
-  if (mElement && lastTime != mCurrentTime) {
-    UpdateReadyStateForData();
-    FireTimeUpdate();
-  }
-}
-
-void
-nsWaveDecoder::SetDuration(PRInt64 /* aDuration */)
-{
-  // Ignored by the wave decoder since we can compute the
-  // duration directly from the wave data itself.
-}
-
-void
-nsWaveDecoder::SetSeekable(PRBool aSeekable)
-{
-  mSeekable = aSeekable;
-}
-
-PRBool
-nsWaveDecoder::GetSeekable()
-{
-  return mSeekable;
-}
-
-void
-nsWaveDecoder::Suspend()
-{
-  if (mStream) {
-    mStream->Suspend(PR_TRUE);
-  }
-}
-
-void
-nsWaveDecoder::Resume(PRBool aForceBuffering)
-{
-  if (mStream) {
-    mStream->Resume();
-  }
-}
-
-void 
-nsWaveDecoder::MoveLoadsToBackground()
-{
-  if (mStream) {
-    mStream->MoveLoadsToBackground();
-  }
-}
-
-nsresult
-nsWaveDecoder::GetBuffered(nsTimeRanges* aBuffered)
-{
-  NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
-  return mPlaybackStateMachine->GetBuffered(aBuffered);
-}
--- a/content/media/wave/nsWaveDecoder.h
+++ b/content/media/wave/nsWaveDecoder.h
@@ -10,18 +10,18 @@
  *
  * 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 Mozilla code.
  *
- * The Initial Developer of the Original Code is the Mozilla Corporation.
- * Portions created by the Initial Developer are Copyright (C) 2008
+ * The Initial Developer of the Original Code is the Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2010
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
  *  Matthew Gregan <kinetik@flim.org>
  *
  * Alternatively, the contents of this file may be used under the terms of
  * either 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"),
@@ -33,276 +33,30 @@
  * 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 ***** */
 #if !defined(nsWaveDecoder_h_)
 #define nsWaveDecoder_h_
 
-#include "nsISupports.h"
-#include "nsCOMPtr.h"
-#include "nsMediaDecoder.h"
-#include "nsMediaStream.h"
-
-/*
-  nsWaveDecoder provides an implementation of the abstract nsMediaDecoder
-  class that supports parsing and playback of Waveform Audio (WAVE) chunks
-  embedded in Resource Interchange File Format (RIFF) bitstreams as
-  specified by the Multimedia Programming Interface and Data Specification
-  1.0.
-
-  Each decoder instance starts one thread (the playback thread).  A single
-  nsWaveStateMachine event is dispatched to this thread to start the
-  thread's state machine running.  The Run method of the event is a loop
-  that executes the current state.  The state can be changed by the state
-  machine, or from the main thread via threadsafe methods on the event.
-  During playback, the playback thread reads data from the network and
-  writes it to the audio backend, attempting to keep the backend's audio
-  buffers full.  It is also responsible for seeking, buffering, and
-  pausing/resuming audio.
-
-  The decoder also owns an nsMediaStream instance that provides a threadsafe
-  blocking interface to read from network channels.  The state machine is
-  the primary user of this stream and holds a weak (raw) pointer to it as
-  the thread, state machine, and stream's lifetimes are all managed by the
-  decoder.
-
-  nsWaveStateMachine has the following states:
-
-  LOADING_METADATA
-    RIFF/WAVE chunks are being read from the stream, the metadata describing
-    the audio data is parsed.
-
-  BUFFERING
-    Playback is paused while waiting for additional data.
-
-  PLAYING
-    If data is available in the stream and the audio backend can consume
-    more data, it is read from the stream and written to the audio backend.
-    Sleep until approximately half of the backend's buffers have drained.
-
-  SEEKING
-    Decoder is seeking to a specified time in the media.
-
-  PAUSED
-    Pause the audio backend, then wait for a state transition.
-
-  ENDED
-    Expected PCM data (or stream EOF) reached, wait for the audio backend to
-    play any buffered data, then wait for shutdown.
-
-  ERROR
-    Metadata loading/parsing failed, wait for shutdown.
-
-  SHUTDOWN
-    Close the audio backend and return from the run loop.
-
-  State transitions within the state machine are:
-
-  LOADING_METADATA -> PLAYING
-                   -> PAUSED
-                   -> ERROR
-
-  BUFFERING        -> PLAYING
-                   -> PAUSED
-
-  PLAYING          -> BUFFERING
-                   -> ENDED
-
-  SEEKING          -> PLAYING
-                   -> PAUSED
-
-  PAUSED           -> waits for caller to play, seek, or shutdown
-
-  ENDED            -> waits for caller to shutdown
-
-  ERROR            -> waits for caller to shutdown
-
-  SHUTDOWN         -> exits state machine
-
-  In addition, the following methods cause state transitions:
-
-  Shutdown(), Play(), Pause(), Seek(double)
-
-  The decoder implementation is currently limited to Linear PCM encoded
-  audio data with one or two channels of 8- or 16-bit samples at sample
-  rates from 100 Hz to 96 kHz.  The number of channels is limited by what
-  the audio backend (sydneyaudio via nsAudioStream) currently supports.  The
-  supported sample rate is artificially limited to arbitrarily selected sane
-  values.  Support for additional channels (and other new features) would
-  require extending nsWaveDecoder to support parsing the newer
-  WAVE_FORMAT_EXTENSIBLE chunk format.
- */
-
-class nsWaveStateMachine;
-class nsTimeRanges;
-
-class nsWaveDecoder : public nsMediaDecoder
-{
-  friend class nsWaveStateMachine;
-
-  NS_DECL_ISUPPORTS
-  NS_DECL_NSIOBSERVER
-
- public:
-  nsWaveDecoder();
-  ~nsWaveDecoder();
-
-  virtual nsMediaDecoder* Clone() { return new nsWaveDecoder(); }
-
-  virtual PRBool Init(nsHTMLMediaElement* aElement);
-
-  virtual nsMediaStream* GetCurrentStream();
-  virtual already_AddRefed<nsIPrincipal> GetCurrentPrincipal();
-
-  // Return the current playback position in the media in seconds.
-  virtual double GetCurrentTime();
-
-  // Return the total playback length of the media in seconds.
-  virtual double GetDuration();
-
-  // Set the audio playback volume; must be in range [0.0, 1.0].
-  virtual void SetVolume(double aVolume);
-
-  virtual nsresult Play();
-  virtual void Pause();
-
-  // Set the current time of the media to aTime.  This may cause mStream to
-  // create a new channel to fetch data from the appropriate position in the
-  // stream.
-  virtual nsresult Seek(double aTime);
+#include "nsBuiltinDecoder.h"
 
-  // Report whether the decoder is currently seeking.
-  virtual PRBool IsSeeking() const;
-
-  // Report whether the decoder has reached end of playback.
-  virtual PRBool IsEnded() const;
-
-  // Start downloading the media at the specified URI.  The media's metadata
-  // will be parsed and made available as the load progresses.
-  virtual nsresult Load(nsMediaStream* aStream,
-                        nsIStreamListener** aStreamListener,
-                        nsMediaDecoder* aCloneDonor);
-
-  // Called by mStream (and possibly the nsChannelToPipeListener used
-  // internally by mStream) when the stream has completed loading.
-  virtual void ResourceLoaded();
-
-  // Called by mStream (and possibly the nsChannelToPipeListener used
-  // internally by mStream) if the stream encounters a network error.
-  virtual void NetworkError();
-
-  // Element is notifying us that the requested playback rate has changed.
-  virtual nsresult PlaybackRateChanged();
-
-  virtual void NotifySuspendedStatusChanged();
-  virtual void NotifyBytesDownloaded();
-  virtual void NotifyDownloadEnded(nsresult aStatus);
-
-  virtual Statistics GetStatistics();
-
-  void PlaybackPositionChanged();
-
-  // Setter for the duration. This is ignored by the wave decoder since it can
-  // compute the duration directly from the wave data.
-  virtual void SetDuration(PRInt64 aDuration);
-
-  // Getter/setter for mSeekable.
-  virtual void SetSeekable(PRBool aSeekable);
-  virtual PRBool GetSeekable();
-
-  // Must be called by the owning object before disposing the decoder.
-  virtual void Shutdown();
-
-  // Suspend any media downloads that are in progress. Called by the
-  // media element when it is sent to the bfcache. Call on the main
-  // thread only.
-  virtual void Suspend();
-
-  // Resume any media downloads that have been suspended. Called by the
-  // media element when it is restored from the bfcache. Call on the
-  // main thread only.
-  virtual void Resume(PRBool aForceBuffering);
-
-  // Calls mElement->UpdateReadyStateForData, telling it which state we have
-  // entered.  Main thread only.
-  void NextFrameUnavailableBuffering();
-  void NextFrameAvailable();
-  void NextFrameUnavailable();
-
-  // Change the element's ready state as necessary. Main thread only.
-  void UpdateReadyStateForData();
-
-  // Tells mStream to put all loads in the background.
-  virtual void MoveLoadsToBackground();
+/**
+ * The decoder implementation is currently limited to Linear PCM encoded
+ * audio data with one or two channels of 8- or 16-bit samples at sample
+ * rates from 100 Hz to 96 kHz.  The number of channels is limited by what
+ * the audio backend (sydneyaudio via nsAudioStream) currently supports.  The
+ * supported sample rate is artificially limited to arbitrarily selected sane
+ * values.  Support for additional channels (and other new features) would
+ * require extending nsWaveDecoder to support parsing the newer
+ * WAVE_FORMAT_EXTENSIBLE chunk format.
+**/
 
-  // Called asynchronously to shut down the decoder
-  void Stop();
 
-  // Constructs the time ranges representing what segments of the media
-  // are buffered and playable.
-  virtual nsresult GetBuffered(nsTimeRanges* aBuffered);
-
-  virtual void NotifyDataArrived(const char* aBuffer, PRUint32 aLength, PRUint32 aOffset) {}
-
-private:
-  // Notifies the element that seeking has started.
-  void SeekingStarted();
-
-  // Notifies the element that seeking has completed.
-  void SeekingStopped();
-
-  // Notifies the element that metadata loading has completed.  Only fired
-  // if metadata is valid.
-  void MetadataLoaded();
-
-  // Notifies the element that playback has completed.
-  void PlaybackEnded();
-
-  // Notifies the element that decoding has failed.
-  void DecodeError();
-
-  // Ensures that state machine thread is running, starting a new one
-  // if necessary.
-  nsresult StartStateMachineThread();
-
-  // Volume that the audio backend will be initialized with.
-  double mInitialVolume;
-
-  // Thread that handles audio playback, including data download.
-  nsCOMPtr<nsIThread> mPlaybackThread;
-
-  // State machine that runs on mPlaybackThread.  Methods on this object are
-  // safe to call from any thread.
-  nsCOMPtr<nsWaveStateMachine> mPlaybackStateMachine;
-
-  // Threadsafe wrapper around channels that provides seeking based on the
-  // underlying channel type.
-  nsAutoPtr<nsMediaStream> mStream;
-
-  // The current playback position of the media resource in units of
-  // seconds. This is updated every time a block of audio is passed to the
-  // backend (unless an prior update is still pending).  It is read and
-  // written from the main thread only.
-  double mCurrentTime;
-
-  // Copy of the duration and ended state when the state machine was
-  // disposed.  Used to respond to duration and ended queries with sensible
-  // values after the state machine has been destroyed.
-  double mEndedDuration;
-  PRPackedBool mEnded;
-
-  // True if the media resource is seekable.
-  PRPackedBool mSeekable;
-
-  // True when the media resource has completely loaded. Accessed on
-  // the main thread only.
-  PRPackedBool mResourceLoaded;
-
-  // True if MetadataLoaded has been reported to the element.
-  PRPackedBool mMetadataLoadedReported;
-
-  // True if ResourceLoaded has been reported to the element.
-  PRPackedBool mResourceLoadedReported;
+class nsWaveDecoder : public nsBuiltinDecoder
+{
+public:
+   virtual nsMediaDecoder* Clone() { return new nsWaveDecoder(); }
+   virtual nsDecoderStateMachine* CreateStateMachine();
 };
 
 #endif
new file mode 100644
--- /dev/null
+++ b/content/media/wave/nsWaveReader.cpp
@@ -0,0 +1,551 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* ***** 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 Mozilla code.
+ *
+ * The Initial Developer of the Original Code is the Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *  Matthew Gregan <kinetik@flim.org>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either 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 "nsError.h"
+#include "nsBuiltinDecoderStateMachine.h"
+#include "nsBuiltinDecoder.h"
+#include "nsMediaStream.h"
+#include "nsWaveReader.h"
+#include "nsTimeRanges.h"
+#include "VideoUtils.h"
+
+using namespace mozilla;
+
+// Un-comment to enable logging of seek bisections.
+//#define SEEK_LOGGING
+
+#ifdef PR_LOGGING
+extern PRLogModuleInfo* gBuiltinDecoderLog;
+#define LOG(type, msg) PR_LOG(gBuiltinDecoderLog, type, msg)
+#ifdef SEEK_LOGGING
+#define SEEK_LOG(type, msg) PR_LOG(gBuiltinDecoderLog, type, msg)
+#else
+#define SEEK_LOG(type, msg)
+#endif
+#else
+#define LOG(type, msg)
+#define SEEK_LOG(type, msg)
+#endif
+
+// Magic values that identify RIFF chunks we're interested in.
+#define RIFF_CHUNK_MAGIC 0x52494646
+#define WAVE_CHUNK_MAGIC 0x57415645
+#define FRMT_CHUNK_MAGIC 0x666d7420
+#define DATA_CHUNK_MAGIC 0x64617461
+
+// Size of RIFF chunk header.  4 byte chunk header type and 4 byte size field.
+#define RIFF_CHUNK_HEADER_SIZE 8
+
+// Size of RIFF header.  RIFF chunk and 4 byte RIFF type.
+#define RIFF_INITIAL_SIZE (RIFF_CHUNK_HEADER_SIZE + 4)
+
+// Size of required part of format chunk.  Actual format chunks may be
+// extended (for non-PCM encodings), but we skip any extended data.
+#define WAVE_FORMAT_CHUNK_SIZE 16
+
+// PCM encoding type from format chunk.  Linear PCM is the only encoding
+// supported by nsAudioStream.
+#define WAVE_FORMAT_ENCODING_PCM 1
+
+// Maximum number of channels supported
+#define MAX_CHANNELS 2
+
+namespace {
+  PRUint32
+  ReadUint32BE(const char** aBuffer)
+  {
+    PRUint32 result =
+      PRUint8((*aBuffer)[0]) << 24 |
+      PRUint8((*aBuffer)[1]) << 16 |
+      PRUint8((*aBuffer)[2]) << 8 |
+      PRUint8((*aBuffer)[3]);
+    *aBuffer += sizeof(PRUint32);
+    return result;
+  }
+
+  PRUint32
+  ReadUint32LE(const char** aBuffer)
+  {
+    PRUint32 result =
+      PRUint8((*aBuffer)[3]) << 24 |
+      PRUint8((*aBuffer)[2]) << 16 |
+      PRUint8((*aBuffer)[1]) << 8 |
+      PRUint8((*aBuffer)[0]);
+    *aBuffer += sizeof(PRUint32);
+    return result;
+  }
+
+  PRUint16
+  ReadUint16LE(const char** aBuffer)
+  {
+    PRUint16 result =
+      PRUint8((*aBuffer)[1]) << 8 |
+      PRUint8((*aBuffer)[0]) << 0;
+    *aBuffer += sizeof(PRUint16);
+    return result;
+  }
+
+  PRInt16
+  ReadInt16LE(const char** aBuffer)
+  {
+    return static_cast<PRInt16>(ReadUint16LE(aBuffer));
+  }
+
+  PRUint8
+  ReadUint8(const char** aBuffer)
+  {
+    PRUint8 result = PRUint8((*aBuffer)[0]);
+    *aBuffer += sizeof(PRUint8);
+    return result;
+  }
+}
+
+nsWaveReader::nsWaveReader(nsBuiltinDecoder* aDecoder)
+  : nsBuiltinDecoderReader(aDecoder)
+{
+  MOZ_COUNT_CTOR(nsWaveReader);
+}
+
+nsWaveReader::~nsWaveReader()
+{
+  MOZ_COUNT_DTOR(nsWaveReader);
+}
+
+nsresult nsWaveReader::Init(nsBuiltinDecoderReader* aCloneDonor)
+{
+  return NS_OK;
+}
+
+nsresult nsWaveReader::ReadMetadata(nsVideoInfo* aInfo)
+{
+  NS_ASSERTION(mDecoder->OnStateMachineThread(), "Should be on state machine thread.");
+  MonitorAutoEnter mon(mMonitor);
+
+  PRBool loaded = LoadRIFFChunk() && LoadFormatChunk() && FindDataOffset();
+  if (!loaded) {
+    return NS_ERROR_FAILURE;
+  }
+
+  mInfo.mHasAudio = PR_TRUE;
+  mInfo.mHasVideo = PR_FALSE;
+  mInfo.mAudioRate = mSampleRate;
+  mInfo.mAudioChannels = mChannels;
+  mInfo.mDataOffset = -1;
+
+  *aInfo = mInfo;
+
+  MonitorAutoExit exitReaderMon(mMonitor);
+  MonitorAutoEnter decoderMon(mDecoder->GetMonitor());
+
+  float d = floorf(BytesToTime(GetDataLength() * 1000));
+  NS_ASSERTION(d <= PR_INT64_MAX, "Duration overflow");
+  mDecoder->GetStateMachine()->SetDuration(static_cast<PRInt64>(d));
+
+  return NS_OK;
+}
+
+PRBool nsWaveReader::DecodeAudioData()
+{
+  MonitorAutoEnter mon(mMonitor);
+  NS_ASSERTION(mDecoder->OnStateMachineThread() || mDecoder->OnDecodeThread(),
+               "Should be on state machine thread or decode thread.");
+
+  PRInt64 pos = GetPosition();
+  PRInt64 len = GetDataLength();
+  PRInt64 remaining = len - pos;
+  NS_ASSERTION(remaining >= 0, "Current wave position is greater than wave file length");
+
+  static const PRInt64 BLOCK_SIZE = 4096;
+  PRInt64 readSize = NS_MIN(BLOCK_SIZE, remaining);
+  PRInt64 samples = readSize / mSampleSize;
+
+  PR_STATIC_ASSERT(PRUint64(BLOCK_SIZE) < UINT_MAX / sizeof(SoundDataValue) / MAX_CHANNELS);
+  const size_t bufferSize = static_cast<size_t>(samples * mChannels);
+  nsAutoArrayPtr<SoundDataValue> sampleBuffer(new SoundDataValue[bufferSize]);
+
+  PR_STATIC_ASSERT(PRUint64(BLOCK_SIZE) < UINT_MAX / sizeof(char));
+  nsAutoArrayPtr<char> dataBuffer(new char[static_cast<size_t>(readSize)]);
+
+  if (!ReadAll(dataBuffer, readSize)) {
+    mAudioQueue.Finish();
+    return PR_FALSE;
+  }
+
+  // convert data to samples
+  const char* d = dataBuffer.get();
+  SoundDataValue* s = sampleBuffer.get();
+  for (int i = 0; i < samples; ++i) {
+    for (unsigned int j = 0; j < mChannels; ++j) {
+      if (mSampleFormat == nsAudioStream::FORMAT_U8) {
+        PRUint8 v =  ReadUint8(&d);
+#if defined(MOZ_SAMPLE_TYPE_S16LE)
+        *s++ = (v * (1.F/PR_UINT8_MAX)) * PR_UINT16_MAX + PR_INT16_MIN;
+#elif defined(MOZ_SAMPLE_TYPE_FLOAT32)
+        *s++ = (v * (1.F/PR_UINT8_MAX)) * 2.F - 1.F;
+#endif
+      }
+      else if (mSampleFormat == nsAudioStream::FORMAT_S16_LE) {
+        PRInt16 v =  ReadInt16LE(&d);
+#if defined(MOZ_SAMPLE_TYPE_S16LE)
+        *s++ = v;
+#elif defined(MOZ_SAMPLE_TYPE_FLOAT32)
+        *s++ = (PRInt32(v) - PR_INT16_MIN) / float(PR_UINT16_MAX) * 2.F - 1.F;
+#endif
+      }
+    }
+  }
+
+  float posTime = BytesToTime(pos);
+  float readSizeTime = BytesToTime(readSize);
+  NS_ASSERTION(posTime <= PR_INT64_MAX / 1000, "posTime overflow");
+  NS_ASSERTION(readSizeTime <= PR_INT64_MAX / 1000, "readSizeTime overflow");
+  NS_ASSERTION(samples < PR_INT32_MAX, "samples overflow");
+
+  mAudioQueue.Push(new SoundData(pos, static_cast<PRInt64>(posTime * 1000),
+                                 static_cast<PRInt64>(readSizeTime * 1000),
+                                 static_cast<PRInt32>(samples),
+                                 sampleBuffer.forget(), mChannels));
+
+  return PR_TRUE;
+}
+
+PRBool nsWaveReader::DecodeVideoFrame(PRBool &aKeyframeSkip,
+                                      PRInt64 aTimeThreshold)
+{
+  MonitorAutoEnter mon(mMonitor);
+  NS_ASSERTION(mDecoder->OnStateMachineThread() || mDecoder->OnDecodeThread(),
+               "Should be on state machine or decode thread.");
+
+  return PR_FALSE;
+}
+
+nsresult nsWaveReader::Seek(PRInt64 aTarget, PRInt64 aStartTime, PRInt64 aEndTime, PRInt64 aCurrentTime)
+{
+  MonitorAutoEnter mon(mMonitor);
+  NS_ASSERTION(mDecoder->OnStateMachineThread(),
+               "Should be on state machine thread.");
+  LOG(PR_LOG_DEBUG, ("%p About to seek to %lldms", mDecoder, aTarget));
+  if (NS_FAILED(ResetDecode())) {
+    return NS_ERROR_FAILURE;
+  }
+  float d = BytesToTime(GetDataLength());
+  NS_ASSERTION(d < PR_INT64_MAX / 1000, "Duration overflow"); 
+  PRInt64 duration = static_cast<PRInt64>(d) * 1000;
+  PRInt64 seekTime = NS_MIN(aTarget, duration);
+  PRInt64 position = RoundDownToSample(static_cast<PRInt64>(TimeToBytes(seekTime) / 1000.f));
+  NS_ASSERTION(PR_INT64_MAX - mWavePCMOffset > position, "Integer overflow during wave seek");
+  position += mWavePCMOffset;
+  return mDecoder->GetCurrentStream()->Seek(nsISeekableStream::NS_SEEK_SET, position);
+}
+
+nsresult nsWaveReader::GetBuffered(nsTimeRanges* aBuffered, PRInt64 aStartTime)
+{
+  PRInt64 startOffset = mDecoder->GetCurrentStream()->GetNextCachedData(mWavePCMOffset);
+  while (startOffset >= 0) {
+    PRInt64 endOffset = mDecoder->GetCurrentStream()->GetCachedDataEnd(startOffset);
+    // Bytes [startOffset..endOffset] are cached.
+    NS_ASSERTION(startOffset >= mWavePCMOffset, "Integer underflow in GetBuffered");
+    NS_ASSERTION(endOffset >= mWavePCMOffset, "Integer underflow in GetBuffered");
+
+    aBuffered->Add(floorf(BytesToTime(startOffset - mWavePCMOffset) * 1000.f) / 1000.0,
+                   floorf(BytesToTime(endOffset - mWavePCMOffset) * 1000.f) / 1000.0);
+    startOffset = mDecoder->GetCurrentStream()->GetNextCachedData(endOffset);
+  }
+  return NS_OK;
+}
+
+PRBool
+nsWaveReader::ReadAll(char* aBuf, PRInt64 aSize, PRInt64* aBytesRead)
+{
+  PRUint32 got = 0;
+  if (aBytesRead) {
+    *aBytesRead = 0;
+  }
+  do {
+    PRUint32 read = 0;
+    if (NS_FAILED(mDecoder->GetCurrentStream()->Read(aBuf + got, PRUint32(aSize - got), &read))) {
+      NS_WARNING("Stream read failed");
+      return PR_FALSE;
+    }
+    if (read == 0) {
+      return PR_FALSE;
+    }
+    mDecoder->NotifyBytesConsumed(read);
+    got += read;
+    if (aBytesRead) {
+      *aBytesRead = got;
+    }
+  } while (got != aSize);
+  return PR_TRUE;
+}
+
+PRBool
+nsWaveReader::LoadRIFFChunk()
+{
+  char riffHeader[RIFF_INITIAL_SIZE];
+  const char* p = riffHeader;
+
+  NS_ABORT_IF_FALSE(mDecoder->GetCurrentStream()->Tell() == 0,
+                    "LoadRIFFChunk called when stream in invalid state");
+
+  if (!ReadAll(riffHeader, sizeof(riffHeader))) {
+    return PR_FALSE;
+  }
+
+  PR_STATIC_ASSERT(sizeof(PRUint32) * 2 <= RIFF_INITIAL_SIZE);
+  if (ReadUint32BE(&p) != RIFF_CHUNK_MAGIC) {
+    NS_WARNING("Stream data not in RIFF format");
+    return PR_FALSE;
+  }
+
+  // Skip over RIFF size field.
+  p += 4;
+
+  if (ReadUint32BE(&p) != WAVE_CHUNK_MAGIC) {
+    NS_WARNING("Expected WAVE chunk");
+    return PR_FALSE;
+  }
+
+  return PR_TRUE;
+}
+
+PRBool
+nsWaveReader::ScanForwardUntil(PRUint32 aWantedChunk, PRUint32* aChunkSize)
+{
+  NS_ABORT_IF_FALSE(aChunkSize, "Require aChunkSize argument");
+  *aChunkSize = 0;
+
+  for (;;) {
+    static const unsigned int CHUNK_HEADER_SIZE = 8;
+    char chunkHeader[CHUNK_HEADER_SIZE];
+    const char* p = chunkHeader;
+
+    if (!ReadAll(chunkHeader, sizeof(chunkHeader))) {
+      return PR_FALSE;
+    }
+
+    PR_STATIC_ASSERT(sizeof(PRUint32) * 2 <= CHUNK_HEADER_SIZE);
+    PRUint32 magic = ReadUint32BE(&p);
+    PRUint32 chunkSize = ReadUint32LE(&p);
+
+    if (magic == aWantedChunk) {
+      *aChunkSize = chunkSize;
+      return PR_TRUE;
+    }
+
+    // RIFF chunks are two-byte aligned, so round up if necessary.
+    chunkSize += chunkSize % 2;
+
+    static const unsigned int MAX_CHUNK_SIZE = 1 << 16;
+    PR_STATIC_ASSERT(MAX_CHUNK_SIZE < UINT_MAX / sizeof(char));
+    nsAutoArrayPtr<char> chunk(new char[MAX_CHUNK_SIZE]);
+    while (chunkSize > 0) {
+      PRUint32 size = PR_MIN(chunkSize, MAX_CHUNK_SIZE);
+      if (!ReadAll(chunk.get(), size)) {
+        return PR_FALSE;
+      }
+      chunkSize -= size;
+    }
+  }
+}
+
+PRBool
+nsWaveReader::LoadFormatChunk()
+{
+  PRUint32 fmtSize, rate, channels, sampleSize, sampleFormat;
+  char waveFormat[WAVE_FORMAT_CHUNK_SIZE];
+  const char* p = waveFormat;
+
+  // RIFF chunks are always word (two byte) aligned.
+  NS_ABORT_IF_FALSE(mDecoder->GetCurrentStream()->Tell() % 2 == 0,
+                    "LoadFormatChunk called with unaligned stream");
+
+  // The "format" chunk may not directly follow the "riff" chunk, so skip
+  // over any intermediate chunks.
+  if (!ScanForwardUntil(FRMT_CHUNK_MAGIC, &fmtSize)) {
+    return PR_FALSE;
+  }
+
+  if (!ReadAll(waveFormat, sizeof(waveFormat))) {
+    return PR_FALSE;
+  }
+
+  PR_STATIC_ASSERT(sizeof(PRUint16) +
+                   sizeof(PRUint16) +
+                   sizeof(PRUint32) +
+                   4 +
+                   sizeof(PRUint16) +
+                   sizeof(PRUint16) <= sizeof(waveFormat));
+  if (ReadUint16LE(&p) != WAVE_FORMAT_ENCODING_PCM) {
+    NS_WARNING("WAVE is not uncompressed PCM, compressed encodings are not supported");
+    return PR_FALSE;
+  }
+
+  channels = ReadUint16LE(&p);
+  rate = ReadUint32LE(&p);
+
+  // Skip over average bytes per second field.
+  p += 4;
+
+  sampleSize = ReadUint16LE(&p);
+
+  sampleFormat = ReadUint16LE(&p);
+
+  // PCM encoded WAVEs are not expected to have an extended "format" chunk,
+  // but I have found WAVEs that have a extended "format" chunk with an
+  // extension size of 0 bytes.  Be polite and handle this rather than
+  // considering the file invalid.  This code skips any extension of the
+  // "format" chunk.
+  if (fmtSize > WAVE_FORMAT_CHUNK_SIZE) {
+    char extLength[2];
+    const char* p = extLength;
+
+    if (!ReadAll(extLength, sizeof(extLength))) {
+      return PR_FALSE;
+    }
+
+    PR_STATIC_ASSERT(sizeof(PRUint16) <= sizeof(extLength));
+    PRUint16 extra = ReadUint16LE(&p);
+    if (fmtSize - (WAVE_FORMAT_CHUNK_SIZE + 2) != extra) {
+      NS_WARNING("Invalid extended format chunk size");
+      return PR_FALSE;
+    }
+    extra += extra % 2;
+
+    if (extra > 0) {
+      PR_STATIC_ASSERT(PR_UINT16_MAX + (PR_UINT16_MAX % 2) < UINT_MAX / sizeof(char));
+      nsAutoArrayPtr<char> chunkExtension(new char[extra]);
+      if (!ReadAll(chunkExtension.get(), extra)) {
+        return PR_FALSE;
+      }
+    }
+  }
+
+  // RIFF chunks are always word (two byte) aligned.
+  NS_ABORT_IF_FALSE(mDecoder->GetCurrentStream()->Tell() % 2 == 0,
+                    "LoadFormatChunk left stream unaligned");
+
+  // Make sure metadata is fairly sane.  The rate check is fairly arbitrary,
+  // but the channels check is intentionally limited to mono or stereo
+  // because that's what the audio backend currently supports.
+  if (rate < 100 || rate > 96000 ||
+      channels < 1 || channels > MAX_CHANNELS ||
+      (sampleSize != 1 && sampleSize != 2 && sampleSize != 4) ||
+      (sampleFormat != 8 && sampleFormat != 16)) {
+    NS_WARNING("Invalid WAVE metadata");
+    return PR_FALSE;
+  }
+
+  MonitorAutoEnter monitor(mDecoder->GetMonitor());
+  mSampleRate = rate;
+  mChannels = channels;
+  mSampleSize = sampleSize;
+  if (sampleFormat == 8) {
+    mSampleFormat = nsAudioStream::FORMAT_U8;
+  } else {
+    mSampleFormat = nsAudioStream::FORMAT_S16_LE;
+  }
+  return PR_TRUE;
+}
+
+PRBool
+nsWaveReader::FindDataOffset()
+{
+  // RIFF chunks are always word (two byte) aligned.
+  NS_ABORT_IF_FALSE(mDecoder->GetCurrentStream()->Tell() % 2 == 0,
+                    "FindDataOffset called with unaligned stream");
+
+  // The "data" chunk may not directly follow the "format" chunk, so skip
+  // over any intermediate chunks.
+  PRUint32 length;
+  if (!ScanForwardUntil(DATA_CHUNK_MAGIC, &length)) {
+    return PR_FALSE;
+  }
+
+  PRInt64 offset = mDecoder->GetCurrentStream()->Tell();
+  if (offset <= 0 || offset > PR_UINT32_MAX) {
+    NS_WARNING("PCM data offset out of range");
+    return PR_FALSE;
+  }
+
+  MonitorAutoEnter monitor(mDecoder->GetMonitor());
+  mWaveLength = length;
+  mWavePCMOffset = PRUint32(offset);
+  return PR_TRUE;
+}
+
+float
+nsWaveReader::BytesToTime(PRInt64 aBytes) const
+{
+  NS_ABORT_IF_FALSE(aBytes >= 0, "Must be >= 0");
+  return float(aBytes) / mSampleRate / mSampleSize;
+}
+
+PRInt64
+nsWaveReader::TimeToBytes(float aTime) const
+{
+  NS_ABORT_IF_FALSE(aTime >= 0.0f, "Must be >= 0");
+  return RoundDownToSample(PRInt64(aTime * mSampleRate * mSampleSize));
+}
+
+PRInt64
+nsWaveReader::RoundDownToSample(PRInt64 aBytes) const
+{
+  NS_ABORT_IF_FALSE(aBytes >= 0, "Must be >= 0");
+  return aBytes - (aBytes % mSampleSize);
+}
+
+PRInt64
+nsWaveReader::GetDataLength()
+{
+  PRInt64 length = mWaveLength;
+  // If the decoder has a valid content length, and it's shorter than the
+  // expected length of the PCM data, calculate the playback duration from
+  // the content length rather than the expected PCM data length.
+  PRInt64 streamLength = mDecoder->GetCurrentStream()->GetLength();
+  if (streamLength >= 0) {
+    PRInt64 dataLength = PR_MAX(0, streamLength - mWavePCMOffset);
+    length = PR_MIN(dataLength, length);
+  }
+  return length;
+}
+
+PRInt64
+nsWaveReader::GetPosition()
+{
+  return mDecoder->GetCurrentStream()->Tell();
+}
new file mode 100644
--- /dev/null
+++ b/content/media/wave/nsWaveReader.h
@@ -0,0 +1,120 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* ***** 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 Mozilla code.
+ *
+ * The Initial Developer of the Original Code is the Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *  Matthew Gregan <kinetik@flim.org>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either 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 ***** */
+#if !defined(nsWaveReader_h_)
+#define nsWaveReader_h_
+
+#include "nsBuiltinDecoderReader.h"
+
+class nsMediaDecoder;
+
+class nsWaveReader : public nsBuiltinDecoderReader
+{
+public:
+  nsWaveReader(nsBuiltinDecoder* aDecoder);
+  ~nsWaveReader();
+
+  virtual nsresult Init(nsBuiltinDecoderReader* aCloneDonor);
+  virtual PRBool DecodeAudioData();
+  virtual PRBool DecodeVideoFrame(PRBool &aKeyframeSkip,
+                                  PRInt64 aTimeThreshold);
+
+  virtual PRBool HasAudio()
+  {
+    return PR_TRUE;
+  }
+
+  virtual PRBool HasVideo()
+  {
+    return PR_FALSE;
+  }
+
+  virtual nsresult ReadMetadata(nsVideoInfo* aInfo);
+  virtual nsresult Seek(PRInt64 aTime, PRInt64 aStartTime, PRInt64 aEndTime, PRInt64 aCurrentTime);
+  virtual nsresult GetBuffered(nsTimeRanges* aBuffered, PRInt64 aStartTime);
+
+private:
+  PRBool ReadAll(char* aBuf, PRInt64 aSize, PRInt64* aBytesRead = nsnull);
+  PRBool LoadRIFFChunk();
+  PRBool ScanForwardUntil(PRUint32 aWantedChunk, PRUint32* aChunkSize);
+  PRBool LoadFormatChunk();
+  PRBool FindDataOffset();
+
+  // Returns the number of seconds that aBytes represents based on the
+  // current audio parameters.  e.g.  176400 bytes is 1 second at 16-bit
+  // stereo 44.1kHz. The time is rounded to the nearest millisecond.
+  float BytesToTime(PRInt64 aBytes) const;
+
+  // Returns the number of bytes that aTime represents based on the current
+  // audio parameters.  e.g.  1 second is 176400 bytes at 16-bit stereo
+  // 44.1kHz.
+  PRInt64 TimeToBytes(float aTime) const;
+
+  // Rounds aBytes down to the nearest complete sample.  Assumes beginning
+  // of byte range is already sample aligned by caller.
+  PRInt64 RoundDownToSample(PRInt64 aBytes) const;
+  PRInt64 GetDataLength();
+  PRInt64 GetPosition();
+
+  /*
+    Metadata extracted from the WAVE header.  Used to initialize the audio
+    stream, and for byte<->time domain conversions.
+  */
+
+  // Number of samples per second.  Limited to range [100, 96000] in LoadFormatChunk.
+  PRUint32 mSampleRate;
+
+  // Number of channels.  Limited to range [1, 2] in LoadFormatChunk.
+  PRUint32 mChannels;
+
+  // Size of a single sample segment, which includes a sample for each
+  // channel (interleaved).
+  PRUint32 mSampleSize;
+
+  // The sample format of the PCM data.
+  nsAudioStream::SampleFormat mSampleFormat;
+
+  // Size of PCM data stored in the WAVE as reported by the data chunk in
+  // the media.
+  PRInt64 mWaveLength;
+
+  // Start offset of the PCM data in the media stream.  Extends mWaveLength
+  // bytes.
+  PRInt64 mWavePCMOffset;
+};
+
+#endif
--- a/content/smil/nsSMILCSSProperty.cpp
+++ b/content/smil/nsSMILCSSProperty.cpp
@@ -37,17 +37,17 @@
 
 /* representation of a SMIL-animatable CSS property on an element */
 
 #include "nsSMILCSSProperty.h"
 #include "nsSMILCSSValueType.h"
 #include "nsSMILValue.h"
 #include "nsComputedDOMStyle.h"
 #include "nsStyleAnimation.h"
-#include "nsIContent.h"
+#include "mozilla/dom/Element.h"
 #include "nsIDOMElement.h"
 
 using namespace mozilla::dom;
 
 // Helper function
 static PRBool
 GetCSSComputedValue(nsIContent* aElem,
                     nsCSSProperty aPropID,
@@ -118,19 +118,18 @@ nsSMILCSSProperty::GetBaseValue() const
     nsSMILValue tmpVal(&nsSMILCSSValueType::sSingleton);
     baseValue.Swap(tmpVal);
     return baseValue;
   }
 
   // GENERAL CASE: Non-Shorthands
   // (1) Put empty string in override style for property mPropID
   // (saving old override style value, so we can set it again when we're done)
-  nsCOMPtr<nsIDOMCSSStyleDeclaration> overrideStyle;
-  mElement->GetSMILOverrideStyle(getter_AddRefs(overrideStyle));
-  nsCOMPtr<nsICSSDeclaration> overrideDecl = do_QueryInterface(overrideStyle);
+  nsCOMPtr<nsICSSDeclaration> overrideDecl =
+    do_QueryInterface(mElement->GetSMILOverrideStyle());
   nsAutoString cachedOverrideStyleVal;
   if (overrideDecl) {
     overrideDecl->GetPropertyValue(mPropID, cachedOverrideStyleVal);
     // (Don't bother clearing override style if it's already empty)
     if (!cachedOverrideStyleVal.IsEmpty()) {
       overrideDecl->SetPropertyValue(mPropID, EmptyString());
     }
   }
@@ -187,34 +186,30 @@ nsSMILCSSProperty::SetAnimValue(const ns
   // Convert nsSMILValue to string
   nsAutoString valStr;
   if (!nsSMILCSSValueType::ValueToString(aValue, valStr)) {
     NS_WARNING("Failed to convert nsSMILValue for CSS property into a string");
     return NS_ERROR_FAILURE;
   }
 
   // Use string value to style the target element
-  nsCOMPtr<nsIDOMCSSStyleDeclaration> overrideStyle;
-  mElement->GetSMILOverrideStyle(getter_AddRefs(overrideStyle));
-  NS_ABORT_IF_FALSE(overrideStyle, "Need a non-null overrideStyle");
-
-  nsCOMPtr<nsICSSDeclaration> overrideDecl = do_QueryInterface(overrideStyle);
+  nsCOMPtr<nsICSSDeclaration> overrideDecl =
+    do_QueryInterface(mElement->GetSMILOverrideStyle());
   if (overrideDecl) {
     overrideDecl->SetPropertyValue(mPropID, valStr);
   }
   return NS_OK;
 }
 
 void
 nsSMILCSSProperty::ClearAnimValue()
 {
   // Put empty string in override style for our property
-  nsCOMPtr<nsIDOMCSSStyleDeclaration> overrideStyle;
-  mElement->GetSMILOverrideStyle(getter_AddRefs(overrideStyle));
-  nsCOMPtr<nsICSSDeclaration> overrideDecl = do_QueryInterface(overrideStyle);
+  nsCOMPtr<nsICSSDeclaration> overrideDecl =
+    do_QueryInterface(mElement->GetSMILOverrideStyle());
   if (overrideDecl) {
     overrideDecl->SetPropertyValue(mPropID, EmptyString());
   }
 }
 
 // Based on http://www.w3.org/TR/SVG/propidx.html
 // static
 PRBool
--- a/content/smil/nsSMILCSSValueType.cpp
+++ b/content/smil/nsSMILCSSValueType.cpp
@@ -40,19 +40,21 @@
 #include "nsSMILCSSValueType.h"
 #include "nsString.h"
 #include "nsStyleAnimation.h"
 #include "nsSMILParserUtils.h"
 #include "nsSMILValue.h"
 #include "nsCSSValue.h"
 #include "nsColor.h"
 #include "nsPresContext.h"
-#include "nsIContent.h"
+#include "mozilla/dom/Element.h"
 #include "nsDebug.h"
 
+using namespace mozilla::dom;
+
 /*static*/ nsSMILCSSValueType nsSMILCSSValueType::sSingleton;
 
 struct ValueWrapper {
   ValueWrapper(nsCSSProperty aPropID, const nsStyleAnimation::Value& aValue,
                nsPresContext* aPresContext) :
     mPropID(aPropID), mCSSValue(aValue), mPresContext(aPresContext) {}
 
   nsCSSProperty mPropID;
@@ -345,33 +347,33 @@ nsSMILCSSValueType::Interpolate(const ns
                                        endWrapper->mPresContext);
     return NS_OK;
   }
   return NS_ERROR_FAILURE;
 }
 
 // Helper function to extract presContext
 static nsPresContext*
-GetPresContextForElement(nsIContent* aElem)
+GetPresContextForElement(Element* aElem)
 {
   nsIDocument* doc = aElem->GetCurrentDoc();
   if (!doc) {
     // This can happen if we process certain types of restyles mid-sample
     // and remove anonymous animated content from the document as a result.
     // See bug 534975.
     return nsnull;
   }
   nsIPresShell* shell = doc->GetShell();
   return shell ? shell->GetPresContext() : nsnull;
 }
 
 // Helper function to parse a string into a nsStyleAnimation::Value
 static PRBool
 ValueFromStringHelper(nsCSSProperty aPropID,
-                      nsIContent* aTargetElement,
+                      Element* aTargetElement,
                       nsPresContext* aPresContext,
                       const nsAString& aString,
                       nsStyleAnimation::Value& aStyleAnimValue)
 {
   // If value is negative, we'll strip off the "-" so the CSS parser won't
   // barf, and then manually make the parsed value negative.
   // (This is a partial solution to let us accept some otherwise out-of-bounds
   // CSS values. Bug 501188 will provide a more complete fix.)
@@ -400,21 +402,20 @@ ValueFromStringHelper(nsCSSProperty aPro
                                   aPresContext->TextZoom());
   }
   return PR_TRUE;
 }
 
 // static
 void
 nsSMILCSSValueType::ValueFromString(nsCSSProperty aPropID,
-                                    nsIContent* aTargetElement,
+                                    Element* aTargetElement,
                                     const nsAString& aString,
                                     nsSMILValue& aValue)
 {
-  // XXXbz aTargetElement should be an Element
   NS_ABORT_IF_FALSE(aValue.IsNull(), "Outparam should be null-typed");
   nsPresContext* presContext = GetPresContextForElement(aTargetElement);
   if (!presContext) {
     NS_WARNING("Not parsing animation value; unable to get PresContext");
     return;
   }
 
   nsStyleAnimation::Value parsedValue;
--- a/content/smil/nsSMILCSSValueType.h
+++ b/content/smil/nsSMILCSSValueType.h
@@ -39,25 +39,32 @@
 
 #ifndef NS_SMILCSSVALUETYPE_H_
 #define NS_SMILCSSVALUETYPE_H_
 
 #include "nsISMILType.h"
 #include "nsCSSProperty.h"
 #include "nscore.h" // For NS_OVERRIDE
 
-class nsIContent;
 class nsAString;
 
+namespace mozilla {
+namespace dom {
+class Element;
+} // namespace dom
+} // namespace mozilla
+
 /*
  * nsSMILCSSValueType: Represents a SMIL-animated CSS value.
  */
 class nsSMILCSSValueType : public nsISMILType
 {
 public:
+  typedef mozilla::dom::Element Element;
+
   // Singleton for nsSMILValue objects to hold onto.
   static nsSMILCSSValueType sSingleton;
 
 protected:
   // nsISMILType Methods
   // -------------------
   NS_OVERRIDE virtual void     Init(nsSMILValue& aValue) const;
   NS_OVERRIDE virtual void     Destroy(nsSMILValue&) const;
@@ -92,17 +99,17 @@ public:
    *                              setting applies.
    * @param       aString         The string to be parsed as a CSS value.
    * @param [out] aValue          The nsSMILValue to be populated. Should
    *                              initially be null-typed.
    * @pre  aValue.IsNull()
    * @post aValue.IsNull() || aValue.mType == nsSMILCSSValueType::sSingleton
    */
   static void ValueFromString(nsCSSProperty aPropID,
-                              nsIContent* aTargetElement,
+                              Element* aTargetElement,
                               const nsAString& aString,
                               nsSMILValue& aValue);
 
   /**
    * Creates a string representation of the given nsSMILValue.
    *
    * Note: aValue is expected to be of this type (that is, it's expected to
    * have been initialized by nsSMILCSSValueType::sSingleton).  If aValue is a
--- a/content/svg/content/src/DOMSVGAnimatedLengthList.cpp
+++ b/content/svg/content/src/DOMSVGAnimatedLengthList.cpp
@@ -117,17 +117,23 @@ DOMSVGAnimatedLengthList::InternalBaseVa
 {
   // When the number of items in our internal counterpart's baseVal changes,
   // we MUST keep our baseVal in sync. If we don't, script will either see a
   // list that is too short and be unable to access indexes that should be
   // valid, or else, MUCH WORSE, script will see a list that is too long and be
   // able to access "items" at indexes that are out of bounds (read/write to
   // bad memory)!!
 
+  nsRefPtr<DOMSVGAnimatedLengthList> kungFuDeathGrip;
   if (mBaseVal) {
+    if (!aNewValue.Length()) {
+      // InternalListLengthWillChange might clear last reference to |this|.
+      // Retain a temporary reference to keep from dying before returning.
+      kungFuDeathGrip = this;
+    }
     mBaseVal->InternalListLengthWillChange(aNewValue.Length());
   }
 
   // If our attribute is not animating, then our animVal mirrors our baseVal
   // and we must sync its length too. (If our attribute is animating, then the
   // SMIL engine takes care of calling InternalAnimValListWillChangeTo() if
   // necessary.)
 
--- a/content/svg/content/src/DOMSVGAnimatedNumberList.cpp
+++ b/content/svg/content/src/DOMSVGAnimatedNumberList.cpp
@@ -116,17 +116,23 @@ DOMSVGAnimatedNumberList::InternalBaseVa
 {
   // When the number of items in our internal counterpart's baseVal changes,
   // we MUST keep our baseVal in sync. If we don't, script will either see a
   // list that is too short and be unable to access indexes that should be
   // valid, or else, MUCH WORSE, script will see a list that is too long and be
   // able to access "items" at indexes that are out of bounds (read/write to
   // bad memory)!!
 
+  nsRefPtr<DOMSVGAnimatedNumberList> kungFuDeathGrip;
   if (mBaseVal) {
+    if (!aNewValue.Length()) {
+      // InternalListLengthWillChange might clear last reference to |this|.
+      // Retain a temporary reference to keep from dying before returning.
+      kungFuDeathGrip = this;
+    }
     mBaseVal->InternalListLengthWillChange(aNewValue.Length());
   }
 
   // If our attribute is not animating, then our animVal mirrors our baseVal
   // and we must sync its length too. (If our attribute is animating, then the
   // SMIL engine takes care of calling InternalAnimValListWillChangeTo() if
   // necessary.)
 
--- a/content/svg/content/src/DOMSVGLengthList.cpp
+++ b/content/svg/content/src/DOMSVGLengthList.cpp
@@ -98,16 +98,23 @@ DOMSVGLengthList::InternalListLengthWill
   PRUint32 oldLength = mItems.Length();
 
   if (aNewLength > DOMSVGLength::MaxListIndex()) {
     // It's safe to get out of sync with our internal list as long as we have
     // FEWER items than it does.
     aNewLength = DOMSVGLength::MaxListIndex();
   }
 
+  nsRefPtr<DOMSVGLengthList> kungFuDeathGrip;
+  if (oldLength && !aNewLength) {
+    // RemovingFromList() might clear last reference to |this|.
+    // Retain a temporary reference to keep from dying before returning.
+    kungFuDeathGrip = this;
+  }
+
   // If our length will decrease, notify the items that will be removed:
   for (PRUint32 i = aNewLength; i < oldLength; ++i) {
     if (mItems[i]) {
       mItems[i]->RemovingFromList();
     }
   }
 
   if (!mItems.SetLength(aNewLength)) {
@@ -388,17 +395,19 @@ DOMSVGLengthList::MaybeInsertNullInAnimV
   UpdateListIndicesFromIndex(animVal->mItems, aIndex + 1);
 }
 
 void
 DOMSVGLengthList::MaybeRemoveItemFromAnimValListAt(PRUint32 aIndex)
 {
   NS_ABORT_IF_FALSE(!IsAnimValList(), "call from baseVal to animVal");
 
-  DOMSVGLengthList* animVal = mAList->mAnimVal;
+  // This needs to be a strong reference; otherwise, the RemovingFromList call
+  // below might drop the last reference to animVal before we're done with it.
+  nsRefPtr<DOMSVGLengthList> animVal = mAList->mAnimVal;
 
   if (!animVal || mAList->IsAnimating()) {
     // No animVal list wrapper, or animVal not a clone of baseVal
     return;
   }
 
   NS_ABORT_IF_FALSE(animVal->mItems.Length() == mItems.Length(),
                     "animVal list not in sync!");
--- a/content/svg/content/src/DOMSVGNumberList.cpp
+++ b/content/svg/content/src/DOMSVGNumberList.cpp
@@ -98,16 +98,23 @@ DOMSVGNumberList::InternalListLengthWill
   PRUint32 oldLength = mItems.Length();
 
   if (aNewLength > DOMSVGNumber::MaxListIndex()) {
     // It's safe to get out of sync with our internal list as long as we have
     // FEWER items than it does.
     aNewLength = DOMSVGNumber::MaxListIndex();
   }
 
+  nsRefPtr<DOMSVGNumberList> kungFuDeathGrip;
+  if (oldLength && !aNewLength) {
+    // RemovingFromList() might clear last reference to |this|.
+    // Retain a temporary reference to keep from dying before returning.
+    kungFuDeathGrip = this;
+  }
+
   // If our length will decrease, notify the items that will be removed:
   for (PRUint32 i = aNewLength; i < oldLength; ++i) {
     if (mItems[i]) {
       mItems[i]->RemovingFromList();
     }
   }
 
   if (!mItems.SetLength(aNewLength)) {
@@ -388,17 +395,19 @@ DOMSVGNumberList::MaybeInsertNullInAnimV
   UpdateListIndicesFromIndex(animVal->mItems, aIndex + 1);
 }
 
 void
 DOMSVGNumberList::MaybeRemoveItemFromAnimValListAt(PRUint32 aIndex)
 {
   NS_ABORT_IF_FALSE(!IsAnimValList(), "call from baseVal to animVal");
 
-  DOMSVGNumberList* animVal = mAList->mAnimVal;
+  // This needs to be a strong reference; otherwise, the RemovingFromList call
+  // below might drop the last reference to animVal before we're done with it.
+  nsRefPtr<DOMSVGNumberList> animVal = mAList->mAnimVal;
 
   if (!animVal || mAList->IsAnimating()) {
     // No animVal list wrapper, or animVal not a clone of baseVal
     return;
   }
 
   NS_ABORT_IF_FALSE(animVal->mItems.Length() == mItems.Length(),
                     "animVal list not in sync!");
--- a/content/svg/content/src/DOMSVGPathSegList.cpp
+++ b/content/svg/content/src/DOMSVGPathSegList.cpp
@@ -135,16 +135,23 @@ DOMSVGPathSegList::InternalListWillChang
   PRUint32 length = mItems.Length();
   PRUint32 index = 0;
 
   PRUint32 dataLength = aNewValue.mData.Length();
   PRUint32 dataIndex = 0; // index into aNewValue's raw data array
 
   PRUint32 newSegType;
 
+  nsRefPtr<DOMSVGPathSegList> kungFuDeathGrip;
+  if (length && aNewValue.IsEmpty()) {
+    // RemovingFromList() might clear last reference to |this|.
+    // Retain a temporary reference to keep from dying before returning.
+    kungFuDeathGrip = this;
+  }
+
   while (index < length && dataIndex < dataLength) {
     newSegType = SVGPathSegUtils::DecodeType(aNewValue.mData[dataIndex]);
     if (ItemAt(index) && ItemAt(index)->Type() != newSegType) {
       ItemAt(index)->RemovingFromList();
       ItemAt(index) = nsnull;
     }
     // Only after the RemovingFromList() can we touch mInternalDataIndex!
     mItems[index].mInternalDataIndex = dataIndex;
@@ -535,17 +542,19 @@ DOMSVGPathSegList::
 {
   NS_ABORT_IF_FALSE(!IsAnimValList(), "call from baseVal to animVal");
 
   if (AttrIsAnimating()) {
     // animVal not a clone of baseVal
     return;
   }
 
-  DOMSVGPathSegList *animVal =
+  // This needs to be a strong reference; otherwise, the RemovingFromList call
+  // below might drop the last reference to animVal before we're done with it.
+  nsRefPtr<DOMSVGPathSegList> animVal =
     GetDOMWrapperIfExists(InternalAList().GetAnimValKey());
   if (!animVal) {
     // No animVal list wrapper
     return;
   }
 
   NS_ABORT_IF_FALSE(animVal->mItems.Length() == mItems.Length(),
                     "animVal list not in sync!");
--- a/content/svg/content/src/DOMSVGPointList.cpp
+++ b/content/svg/content/src/DOMSVGPointList.cpp
@@ -127,16 +127,23 @@ DOMSVGPointList::InternalListWillChangeT
 
   PRUint32 newLength = aNewValue.Length();
   if (newLength > DOMSVGPoint::MaxListIndex()) {
     // It's safe to get out of sync with our internal list as long as we have
     // FEWER items than it does.
     newLength = DOMSVGPoint::MaxListIndex();
   }
 
+  nsRefPtr<DOMSVGPointList> kungFuDeathGrip;
+  if (oldLength && !newLength) {
+    // RemovingFromList() might clear last reference to |this|.
+    // Retain a temporary reference to keep from dying before returning.
+    kungFuDeathGrip = this;
+  }
+
   // If our length will decrease, notify the items that will be removed:
   for (PRUint32 i = newLength; i < oldLength; ++i) {
     if (mItems[i]) {
       mItems[i]->RemovingFromList();
     }
   }
 
   if (!mItems.SetLength(newLength)) {
@@ -450,17 +457,19 @@ DOMSVGPointList::MaybeRemoveItemFromAnim
 {
   NS_ABORT_IF_FALSE(!IsAnimValList(), "call from baseVal to animVal");
 
   if (AttrIsAnimating()) {
     // animVal not a clone of baseVal
     return;
   }
 
-  DOMSVGPointList *animVal =
+  // This needs to be a strong reference; otherwise, the RemovingFromList call
+  // below might drop the last reference to animVal before we're done with it.
+  nsRefPtr<DOMSVGPointList> animVal =
     GetDOMWrapperIfExists(InternalAList().GetAnimValKey());
   if (!animVal) {
     // No animVal list wrapper
     return;
   }
 
   NS_ABORT_IF_FALSE(animVal->mItems.Length() == mItems.Length(),
                     "animVal list not in sync!");
--- a/content/svg/content/src/Makefile.in
+++ b/content/svg/content/src/Makefile.in
@@ -62,16 +62,17 @@ CPPSRCS		= \
 		nsDOMSVGZoomEvent.cpp \
 		nsDOMSVGEvent.cpp \
 		nsSVGAElement.cpp \
 		nsSVGAltGlyphElement.cpp \
 		nsSVGAngle.cpp \
 		nsSVGAnimatedTransformList.cpp \
 		nsSVGBoolean.cpp \
 		nsSVGCircleElement.cpp \
+		nsSVGClass.cpp \
 		nsSVGClipPathElement.cpp \
 		nsSVGDataParser.cpp \
 		nsSVGDefsElement.cpp \
 		nsSVGDescElement.cpp \
 		nsSVGElement.cpp \
 		nsSVGElementFactory.cpp \
 		nsSVGEllipseElement.cpp \
 		nsSVGEnum.cpp \
--- a/content/svg/content/src/nsSVGAnimationElement.cpp
+++ b/content/svg/content/src/nsSVGAnimationElement.cpp
@@ -265,26 +265,24 @@ nsSVGAnimationElement::BindToTree(nsIDoc
   NS_ABORT_IF_FALSE(!mHrefTarget.get(),
                     "Shouldn't have href-target yet "
                     "(or it should've been cleared)");
   nsresult rv = nsSVGAnimationElementBase::BindToTree(aDocument, aParent,
                                                       aBindingParent,
                                                       aCompileEventHandlers);
   NS_ENSURE_SUCCESS(rv,rv);
 
-  // XXXdholbert is ownerDOMSVG (as a check for SVG parent) still needed here?
-  nsCOMPtr<nsIDOMSVGSVGElement> ownerDOMSVG;
-  rv = GetOwnerSVGElement(getter_AddRefs(ownerDOMSVG));
-
-  if (NS_FAILED(rv) || !ownerDOMSVG)
+  // XXXdholbert is GetCtx (as a check for SVG parent) still needed here?
+  if (!GetCtx()) {
     // No use proceeding. We don't have an SVG parent (yet) so we won't be able
     // to register ourselves etc. Maybe next time we'll have more luck.
     // (This sort of situation will arise a lot when trees are being constructed
     // piece by piece via script)
     return NS_OK;
+  }
 
   // Add myself to the animation controller's master set of animation elements.
   if (aDocument) {
     nsSMILAnimationController *controller = aDocument->GetAnimationController();
     if (controller) {
       controller->RegisterAnimationElement(this);
     }
     const nsAttrValue* href = mAttrsAndChildren.GetAttr(nsGkAtoms::href,
@@ -415,28 +413,23 @@ nsSVGAnimationElement::IsNodeOfType(PRUi
 }
 
 //----------------------------------------------------------------------
 // Implementation helpers
 
 nsSMILTimeContainer*
 nsSVGAnimationElement::GetTimeContainer()
 {
-  nsSMILTimeContainer *result = nsnull;
-  nsCOMPtr<nsIDOMSVGSVGElement> ownerDOMSVG;
-
-  nsresult rv = GetOwnerSVGElement(getter_AddRefs(ownerDOMSVG));
+  nsSVGSVGElement *element = nsSVGUtils::GetOuterSVGElement(this);
 
-  if (NS_SUCCEEDED(rv) && ownerDOMSVG) {
-    nsSVGSVGElement *ownerSVG =
-      static_cast<nsSVGSVGElement*>(ownerDOMSVG.get());
-    result = ownerSVG->GetTimedDocumentRoot();
+  if (element) {
+    return element->GetTimedDocumentRoot();
   }
 
-  return result;
+  return nsnull;
 }
 
 // nsIDOMElementTimeControl
 /* void beginElement (); */
 NS_IMETHODIMP
 nsSVGAnimationElement::BeginElement(void)
 {
   return BeginElementAt(0.f);
new file mode 100644
--- /dev/null
+++ b/content/svg/content/src/nsSVGClass.cpp
@@ -0,0 +1,158 @@
+/* -*- 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) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either 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 "nsSVGClass.h"
+#ifdef MOZ_SMIL
+#include "nsSMILValue.h"
+#include "SMILStringType.h"
+#endif // MOZ_SMIL
+
+using namespace mozilla;
+
+NS_SVG_VAL_IMPL_CYCLE_COLLECTION(nsSVGClass::DOMAnimatedString, mSVGElement)
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(nsSVGClass::DOMAnimatedString)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(nsSVGClass::DOMAnimatedString)
+
+DOMCI_DATA(SVGAnimatedClass, nsSVGClass::DOMAnimatedString)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsSVGClass::DOMAnimatedString)
+  NS_INTERFACE_MAP_ENTRY(nsIDOMSVGAnimatedString)
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
+  NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(SVGAnimatedString)
+NS_INTERFACE_MAP_END
+
+/* Implementation */
+
+void
+nsSVGClass::SetBaseValue(const nsAString& aValue,
+                         nsSVGElement *aSVGElement,
+                         PRBool aDoSetAttr)
+{
+  NS_ASSERTION(aSVGElement, "Null element passed to SetBaseValue");
+
+  aSVGElement->SetFlags(NODE_MAY_HAVE_CLASS);
+  if (aDoSetAttr) {
+    aSVGElement->SetAttr(kNameSpaceID_None, nsGkAtoms::_class, aValue, PR_TRUE);
+  }
+#ifdef MOZ_SMIL
+  if (mAnimVal) {
+    aSVGElement->AnimationNeedsResample();
+  }
+#endif
+}
+
+void
+nsSVGClass::GetAnimValue(nsAString& aResult, const nsSVGElement *aSVGElement) const
+{
+  if (mAnimVal) {
+    aResult = *mAnimVal;
+    return;
+  }
+
+  aSVGElement->GetAttr(kNameSpaceID_None, nsGkAtoms::_class, aResult);
+}
+
+void
+nsSVGClass::SetAnimValue(const nsAString& aValue, nsSVGElement *aSVGElement)
+{
+  if (!mAnimVal) {
+    mAnimVal = new nsString();
+  }
+  *mAnimVal = aValue;
+  aSVGElement->SetFlags(NODE_MAY_HAVE_CLASS);
+  aSVGElement->DidAnimateClass();
+}
+
+nsresult
+nsSVGClass::ToDOMAnimatedString(nsIDOMSVGAnimatedString **aResult,
+                                nsSVGElement *aSVGElement)
+{
+  *aResult = new DOMAnimatedString(this, aSVGElement);
+  NS_ADDREF(*aResult);
+  return NS_OK;
+}
+
+#ifdef MOZ_SMIL
+nsISMILAttr*
+nsSVGClass::ToSMILAttr(nsSVGElement *aSVGElement)
+{
+  return new SMILString(this, aSVGElement);
+}
+
+nsresult
+nsSVGClass::SMILString::ValueFromString(const nsAString& aStr,
+                                        const nsISMILAnimationElement* /*aSrcElement*/,
+                                        nsSMILValue& aValue,
+                                        PRBool& aPreventCachingOfSandwich) const
+{
+  nsSMILValue val(&SMILStringType::sSingleton);
+
+  *static_cast<nsAString*>(val.mU.mPtr) = aStr;
+  aValue.Swap(val);
+  aPreventCachingOfSandwich = PR_FALSE;
+  return NS_OK;
+}
+
+nsSMILValue
+nsSVGClass::SMILString::GetBaseValue() const
+{
+  nsSMILValue val(&SMILStringType::sSingleton);
+  mSVGElement->GetAttr(kNameSpaceID_None, nsGkAtoms::_class,
+                       *static_cast<nsAString*>(val.mU.mPtr));
+  return val;
+}
+
+void
+nsSVGClass::SMILString::ClearAnimValue()
+{
+  if (mVal->mAnimVal) {
+    mVal->mAnimVal = nsnull;
+    mSVGElement->DidAnimateClass();
+  }
+}
+
+nsresult
+nsSVGClass::SMILString::SetAnimValue(const nsSMILValue& aValue)
+{
+  NS_ASSERTION(aValue.mType == &SMILStringType::sSingleton,
+               "Unexpected type to assign animated value");
+  if (aValue.mType == &SMILStringType::sSingleton) {
+    mVal->SetAnimValue(*static_cast<nsAString*>(aValue.mU.mPtr), mSVGElement);
+  }
+  return NS_OK;
+}
+#endif // MOZ_SMIL
new file mode 100644
--- /dev/null
+++ b/content/svg/content/src/nsSVGClass.h
@@ -0,0 +1,124 @@
+/* -*- 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) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * 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 ***** */
+
+#ifndef __NS_SVGCLASS_H__
+#define __NS_SVGCLASS_H__
+
+#include "nsIDOMSVGAnimatedString.h"
+#include "nsSVGElement.h"
+#include "nsDOMError.h"
+
+class nsSVGClass
+{
+
+public:
+  void Init() {
+    mAnimVal = nsnull;
+  }
+
+  void SetBaseValue(const nsAString& aValue,
+                    nsSVGElement *aSVGElement,
+                    PRBool aDoSetAttr);
+  void GetBaseValue(nsAString& aValue, nsSVGElement *aSVGElement) const
+    { aSVGElement->GetAttr(kNameSpaceID_None, nsGkAtoms::_class, aValue); }
+
+  void SetAnimValue(const nsAString& aValue, nsSVGElement *aSVGElement);
+  void GetAnimValue(nsAString& aValue, const nsSVGElement *aSVGElement) const;
+  PRBool IsAnimated() const
+    { return !!mAnimVal; }
+
+  nsresult ToDOMAnimatedString(nsIDOMSVGAnimatedString **aResult,
+                               nsSVGElement *aSVGElement);
+#ifdef MOZ_SMIL
+  // Returns a new nsISMILAttr object that the caller must delete
+  nsISMILAttr* ToSMILAttr(nsSVGElement *aSVGElement);
+#endif // MOZ_SMIL
+
+private:
+
+  nsAutoPtr<nsString> mAnimVal;
+
+public:
+  struct DOMAnimatedString : public nsIDOMSVGAnimatedString
+  {
+    NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+    NS_DECL_CYCLE_COLLECTION_CLASS(DOMAnimatedString)
+
+    DOMAnimatedString(nsSVGClass *aVal, nsSVGElement *aSVGElement)
+      : mVal(aVal), mSVGElement(aSVGElement) {}
+
+    nsSVGClass* mVal; // kept alive because it belongs to content
+    nsRefPtr<nsSVGElement> mSVGElement;
+
+    NS_IMETHOD GetBaseVal(nsAString& aResult)
+      { mVal->GetBaseValue(aResult, mSVGElement); return NS_OK; }
+    NS_IMETHOD SetBaseVal(const nsAString& aValue)
+      { mVal->SetBaseValue(aValue, mSVGElement, PR_TRUE); return NS_OK; }
+
+    NS_IMETHOD GetAnimVal(nsAString& aResult)
+    { 
+#ifdef MOZ_SMIL
+      mSVGElement->FlushAnimations();
+#endif
+      mVal->GetAnimValue(aResult, mSVGElement); return NS_OK;
+    }
+
+  };
+#ifdef MOZ_SMIL
+  struct SMILString : public nsISMILAttr
+  {
+  public:
+    SMILString(nsSVGClass *aVal, nsSVGElement *aSVGElement)
+      : mVal(aVal), mSVGElement(aSVGElement) {}
+
+    // These will stay alive because a nsISMILAttr only lives as long
+    // as the Compositing step, and DOM elements don't get a chance to
+    // die during that.
+    nsSVGClass* mVal;
+    nsSVGElement* mSVGElement;
+
+    // nsISMILAttr methods
+    virtual nsresult ValueFromString(const nsAString& aStr,
+                                     const nsISMILAnimationElement *aSrcElement,
+                                     nsSMILValue& aValue,
+                                     PRBool& aPreventCachingOfSandwich) const;
+    virtual nsSMILValue GetBaseValue() const;
+    virtual void ClearAnimValue();
+    virtual nsresult SetAnimValue(const nsSMILValue& aValue);
+  };
+#endif // MOZ_SMIL
+};
+#endif //__NS_SVGCLASS_H__
--- a/content/svg/content/src/nsSVGElement.cpp
+++ b/content/svg/content/src/nsSVGElement.cpp
@@ -69,16 +69,17 @@
 #include "nsSVGLength2.h"
 #include "nsSVGNumber2.h"
 #include "nsSVGInteger.h"
 #include "nsSVGAngle.h"
 #include "nsSVGBoolean.h"
 #include "nsSVGEnum.h"
 #include "nsSVGViewBox.h"
 #include "nsSVGString.h"
+#include "nsSVGClass.h"
 #include "SVGAnimatedNumberList.h"
 #include "SVGAnimatedLengthList.h"
 #include "SVGAnimatedPointList.h"
 #include "SVGAnimatedPathSegList.h"
 #include "nsIDOMSVGUnitTypes.h"
 #include "nsIDOMSVGPointList.h"
 #include "nsIDOMSVGAnimatedPoints.h"
 #include "nsIDOMSVGTransformList.h"
@@ -542,16 +543,24 @@ nsSVGElement::ParseAttribute(PRInt32 aNa
           GetPreserveAspectRatio();
         if (preserveAspectRatio) {
           rv = preserveAspectRatio->SetBaseValueString(aValue, this, PR_FALSE);
           if (NS_FAILED(rv)) {
             preserveAspectRatio->Init();
           }
           foundMatch = PR_TRUE;
         }
+      // Check for class attribute
+      } else if (aAttribute == nsGkAtoms::_class) {
+        nsSVGClass *svgClass = GetClass();
+        if (svgClass) {
+          svgClass->SetBaseValue(aValue, this, PR_FALSE);
+          aResult.ParseAtomArray(aValue);
+          return PR_TRUE;
+        }
       }
     }
   }
 
   if (!foundMatch) {
     // Check for nsSVGString attribute
     StringAttributesInfo stringInfo = GetStringInfo();
     for (PRUint32 i = 0; i < stringInfo.mStringCount; i++) {
@@ -580,226 +589,199 @@ nsSVGElement::ParseAttribute(PRInt32 aNa
 nsresult
 nsSVGElement::UnsetAttr(PRInt32 aNamespaceID, nsIAtom* aName,
                         PRBool aNotify)
 {
   // XXXbz there's a bunch of redundancy here with AfterSetAttr.
   // Maybe consolidate?
   nsresult rv = nsSVGElementBase::UnsetAttr(aNamespaceID, aName, aNotify);
 
-  PRBool foundMatch = PR_FALSE;
-
   if (aNamespaceID == kNameSpaceID_None) {
     // If this is an svg presentation attribute, remove rule to force an update
     if (IsAttributeMapped(aName))
       mContentStyleRule = nsnull;
 
     if (IsEventName(aName)) {
       nsIEventListenerManager* manager = GetListenerManager(PR_FALSE);
       if (manager) {
         nsIAtom* eventName = GetEventNameForAttr(aName);
         manager->RemoveScriptEventListener(eventName);
       }
-      foundMatch = PR_TRUE;
+      return rv;
     }
     
-    if (!foundMatch) {
-      // Check if this is a length attribute going away
-      LengthAttributesInfo lenInfo = GetLengthInfo();
+    // Check if this is a length attribute going away
+    LengthAttributesInfo lenInfo = GetLengthInfo();
 
-      for (PRUint32 i = 0; i < lenInfo.mLengthCount; i++) {
-        if (aName == *lenInfo.mLengthInfo[i].mName) {
-          lenInfo.Reset(i);
-          DidChangeLength(i, PR_FALSE);
-          foundMatch = PR_TRUE;
-          break;
-        }
+    for (PRUint32 i = 0; i < lenInfo.mLengthCount; i++) {
+      if (aName == *lenInfo.mLengthInfo[i].mName) {
+        lenInfo.Reset(i);
+        DidChangeLength(i, PR_FALSE);
+        return rv;
       }
     }
 
-    if (!foundMatch) {
-      // Check if this is a length list attribute going away
-      LengthListAttributesInfo lengthListInfo = GetLengthListInfo();
+    // Check if this is a length list attribute going away
+    LengthListAttributesInfo lengthListInfo = GetLengthListInfo();
 
-      for (PRUint32 i = 0; i < lengthListInfo.mLengthListCount; i++) {
-        if (aName == *lengthListInfo.mLengthListInfo[i].mName) {
-          lengthListInfo.Reset(i);
-          DidChangeLengthList(i, PR_FALSE);
-          foundMatch = PR_TRUE;
-          break;
-        }
+    for (PRUint32 i = 0; i < lengthListInfo.mLengthListCount; i++) {
+      if (aName == *lengthListInfo.mLengthListInfo[i].mName) {
+        lengthListInfo.Reset(i);
+        DidChangeLengthList(i, PR_FALSE);
+        return rv;
       }
     }
 
-    if (!foundMatch) {
-      // Check if this is a number list attribute going away
-      NumberListAttributesInfo numberListInfo = GetNumberListInfo();
+    // Check if this is a number list attribute going away
+    NumberListAttributesInfo numberListInfo = GetNumberListInfo();
 
-      for (PRUint32 i = 0; i < numberListInfo.mNumberListCount; i++) {
-        if (aName == *numberListInfo.mNumberListInfo[i].mName) {
-          numberListInfo.Reset(i);
-          DidChangeNumberList(i, PR_FALSE);
-          foundMatch = PR_TRUE;
-          break;
-        }
+    for (PRUint32 i = 0; i < numberListInfo.mNumberListCount; i++) {
+      if (aName == *numberListInfo.mNumberListInfo[i].mName) {
+        numberListInfo.Reset(i);
+        DidChangeNumberList(i, PR_FALSE);
+        return rv;
+      }
+    }
+
+    // Check if this is a point list attribute going away
+    if (GetPointListAttrName() == aName) {
+      SVGAnimatedPointList *pointList = GetAnimatedPointList();
+      if (pointList) {
+        pointList->ClearBaseValue();
+        return rv;
       }
     }
 
-    if (!foundMatch) {
-      // Check if this is a point list attribute going away
-      if (GetPointListAttrName() == aName) {
-        SVGAnimatedPointList *pointList = GetAnimatedPointList();
-        if (pointList) {
-          pointList->ClearBaseValue();
-          DidChangePointList(PR_FALSE);
-          foundMatch = PR_TRUE;
-        }
+    // Check if this is a path segment list attribute going away
+    if (GetPathDataAttrName() == aName) {
+      SVGAnimatedPathSegList *segList = GetAnimPathSegList();
+      if (segList) {
+        segList->ClearBaseValue();
+        DidChangePathSegList(PR_FALSE);
+        return rv;
       }
     }
 
-    if (!foundMatch) {
-      // Check if this is a path segment list attribute going away
-      if (GetPathDataAttrName() == aName) {
-        SVGAnimatedPathSegList *segList = GetAnimPathSegList();
-        if (segList) {
-          segList->ClearBaseValue();
-          DidChangePathSegList(PR_FALSE);
-          foundMatch = PR_TRUE;
+    // Check if this is a number attribute going away
+    NumberAttributesInfo numInfo = GetNumberInfo();
+
+    for (PRUint32 i = 0; i < numInfo.mNumberCount; i++) {
+      if (aName == *numInfo.mNumberInfo[i].mName) {
+        if (i + 1 < numInfo.mNumberCount &&
+            aName == *numInfo.mNumberInfo[i + 1].mName) {
+          // found a number-optional-number
+          numInfo.Reset(i + 1);
+          DidChangeNumber(i + 1, PR_FALSE);
         }
+        numInfo.Reset(i);
+        DidChangeNumber(i, PR_FALSE);
+        return rv;
       }
     }
 
-    if (!foundMatch) {
-      // Check if this is a number attribute going away
-      NumberAttributesInfo numInfo = GetNumberInfo();
+    // Check if this is an integer attribute going away
+    IntegerAttributesInfo intInfo = GetIntegerInfo();
 
-      for (PRUint32 i = 0; i < numInfo.mNumberCount; i++) {
-        if (aName == *numInfo.mNumberInfo[i].mName) {
-          if (i + 1 < numInfo.mNumberCount &&
-              aName == *numInfo.mNumberInfo[i + 1].mName) {
-            // found a number-optional-number
-            numInfo.Reset(i + 1);
-            DidChangeNumber(i + 1, PR_FALSE);
-          }
-          numInfo.Reset(i);
-          DidChangeNumber(i, PR_FALSE);
-          foundMatch = PR_TRUE;
-          break;
+    for (PRUint32 i = 0; i < intInfo.mIntegerCount; i++) {
+      if (aName == *intInfo.mIntegerInfo[i].mName) {
+        if (i + 1 < intInfo.mIntegerCount &&
+            aName == *intInfo.mIntegerInfo[i + 1].mName) {
+          // found a number-optional-number
+          intInfo.Reset(i + 1);
+          DidChangeNumber(i + 1, PR_FALSE);
         }
+        intInfo.Reset(i);
+        DidChangeInteger(i, PR_FALSE);
+        return rv;
       }
     }
 
-    if (!foundMatch) {
-      // Check if this is an integer attribute going away
-      IntegerAttributesInfo intInfo = GetIntegerInfo();
+    // Check if this is an angle attribute going away
+    AngleAttributesInfo angleInfo = GetAngleInfo();
 
-      for (PRUint32 i = 0; i < intInfo.mIntegerCount; i++) {
-        if (aName == *intInfo.mIntegerInfo[i].mName) {
-          if (i + 1 < intInfo.mIntegerCount &&
-              aName == *intInfo.mIntegerInfo[i + 1].mName) {
-            // found a number-optional-number
-            intInfo.Reset(i + 1);
-            DidChangeNumber(i + 1, PR_FALSE);
-          }
-          intInfo.Reset(i);
-          DidChangeInteger(i, PR_FALSE);
-          foundMatch = PR_TRUE;
-          break;
-        }
+    for (PRUint32 i = 0; i < angleInfo.mAngleCount; i++) {
+      if (aName == *angleInfo.mAngleInfo[i].mName) {
+        angleInfo.Reset(i);
+        DidChangeAngle(i, PR_FALSE);
+        return rv;
+      }
+    }
+
+    // Check if this is a boolean attribute going away
+    BooleanAttributesInfo boolInfo = GetBooleanInfo();
+
+    for (PRUint32 i = 0; i < boolInfo.mBooleanCount; i++) {
+      if (aName == *boolInfo.mBooleanInfo[i].mName) {
+        boolInfo.Reset(i);
+        DidChangeBoolean(i, PR_FALSE);
+        return rv;
       }
     }
 
-    if (!foundMatch) {
-      // Check if this is an angle attribute going away
-      AngleAttributesInfo angleInfo = GetAngleInfo();
+    // Check if this is an enum attribute going away
+    EnumAttributesInfo enumInfo = GetEnumInfo();
 
-      for (PRUint32 i = 0; i < angleInfo.mAngleCount; i++) {
-        if (aName == *angleInfo.mAngleInfo[i].mName) {
-          angleInfo.Reset(i);
-          DidChangeAngle(i, PR_FALSE);
-          foundMatch = PR_TRUE;
-          break;
-        }
-      }
-    }
-
-    if (!foundMatch) {
-      // Check if this is a boolean attribute going away
-      BooleanAttributesInfo boolInfo = GetBooleanInfo();
-
-      for (PRUint32 i = 0; i < boolInfo.mBooleanCount; i++) {
-        if (aName == *boolInfo.mBooleanInfo[i].mName) {
-          boolInfo.Reset(i);
-          DidChangeBoolean(i, PR_FALSE);
-          foundMatch = PR_TRUE;
-        }
+    for (PRUint32 i = 0; i < enumInfo.mEnumCount; i++) {
+      if (aName == *enumInfo.mEnumInfo[i].mName) {
+        enumInfo.Reset(i);
+        DidChangeEnum(i, PR_FALSE);
+        return rv;
       }
     }
 
-    if (!foundMatch) {
-      // Check if this is an enum attribute going away
-      EnumAttributesInfo enumInfo = GetEnumInfo();
-
-      for (PRUint32 i = 0; i < enumInfo.mEnumCount; i++) {
-        if (aName == *enumInfo.mEnumInfo[i].mName) {
-          enumInfo.Reset(i);
-          DidChangeEnum(i, PR_FALSE);
-          foundMatch = PR_TRUE;
-          break;
-        }
+    // Check if this is a nsViewBox attribute going away
+    if (aName == nsGkAtoms::viewBox) {
+      nsSVGViewBox* viewBox = GetViewBox();
+      if (viewBox) {
+        viewBox->Init();
+        DidChangeViewBox(PR_FALSE);
+        return rv;
       }
     }
+    // Check if this is a preserveAspectRatio attribute going away
+    if (aName == nsGkAtoms::preserveAspectRatio) {
+      SVGAnimatedPreserveAspectRatio *preserveAspectRatio =
+        GetPreserveAspectRatio();
 
-    if (!foundMatch) {
-      // Check if this is a nsViewBox attribute going away
-      if (aName == nsGkAtoms::viewBox) {
-        nsSVGViewBox* viewBox = GetViewBox();
-        if (viewBox) {
-          viewBox->Init();
-          DidChangeViewBox(PR_FALSE);
-          foundMatch = PR_TRUE;
-        }
-      // Check if this is a preserveAspectRatio attribute going away
-      } else if (aName == nsGkAtoms::preserveAspectRatio) {
-        SVGAnimatedPreserveAspectRatio *preserveAspectRatio =
-          GetPreserveAspectRatio();
+      if (preserveAspectRatio) {
+        preserveAspectRatio->Init();
+        DidChangePreserveAspectRatio(PR_FALSE);
+        return rv;
+      }
+    }
+    // Check if this is a class attribute going away
+    if (aName == nsGkAtoms::_class) {
+      nsSVGClass *svgClass = GetClass();
 
-        if (preserveAspectRatio) {
-          preserveAspectRatio->Init();
-          DidChangePreserveAspectRatio(PR_FALSE);
-          foundMatch = PR_TRUE;
-        }
+      if (svgClass) {
+        svgClass->Init();
+        return rv;
       }
     }
   }
 
-  if (!foundMatch) {
-    // Check if this is a string attribute going away
-    StringAttributesInfo stringInfo = GetStringInfo();
+  // Check if this is a string attribute going away
+  StringAttributesInfo stringInfo = GetStringInfo();
 
-    for (PRUint32 i = 0; i < stringInfo.mStringCount; i++) {
-      if (aNamespaceID == stringInfo.mStringInfo[i].mNamespaceID &&
-          aName == *stringInfo.mStringInfo[i].mName) {
-        stringInfo.Reset(i);
-        DidChangeString(i);
-        foundMatch = PR_TRUE;
-        break;
-      }
+  for (PRUint32 i = 0; i < stringInfo.mStringCount; i++) {
+    if (aNamespaceID == stringInfo.mStringInfo[i].mNamespaceID &&
+        aName == *stringInfo.mStringInfo[i].mName) {
+      stringInfo.Reset(i);
+      DidChangeString(i);
+      return rv;
     }
   }
 
-  if (!foundMatch) {
-    // Now check for one of the old style basetypes going away
-    nsCOMPtr<nsISVGValue> svg_value = GetMappedAttribute(aNamespaceID, aName);
+  // Now check for one of the old style basetypes going away
+  nsCOMPtr<nsISVGValue> svg_value = GetMappedAttribute(aNamespaceID, aName);
 
-    if (svg_value) {
-      mSuppressNotification = PR_TRUE;
-      ResetOldStyleBaseType(svg_value);
-      mSuppressNotification = PR_FALSE;
-    }
+  if (svg_value) {
+    mSuppressNotification = PR_TRUE;
+    ResetOldStyleBaseType(svg_value);
+    mSuppressNotification = PR_FALSE;
   }
 
   return rv;
 }
 
 void
 nsSVGElement::ResetOldStyleBaseType(nsISVGValue *svg_value)
 {
@@ -1037,44 +1019,23 @@ NS_IMETHODIMP nsSVGElement::SetId(const 
 {
   return SetAttr(kNameSpaceID_None, nsGkAtoms::id, aId, PR_TRUE);
 }
 
 /* readonly attribute nsIDOMSVGSVGElement ownerSVGElement; */
 NS_IMETHODIMP
 nsSVGElement::GetOwnerSVGElement(nsIDOMSVGSVGElement * *aOwnerSVGElement)
 {
-  *aOwnerSVGElement = nsnull;
-
-  nsIContent* ancestor = nsSVGUtils::GetParentElement(this);
+  NS_IF_ADDREF(*aOwnerSVGElement = GetCtx());
 
-  while (ancestor && ancestor->GetNameSpaceID() == kNameSpaceID_SVG) {
-    nsIAtom* tag = ancestor->Tag();
-    if (tag == nsGkAtoms::foreignObject) {
-      // SVG in a foreignObject must have its own <svg> (nsSVGOuterSVGFrame).
-      // Leave *aOwnerSVGElement nulled out, but don't throw.
-      return NS_OK;
-    }
-    if (tag == nsGkAtoms::svg) {
-      *aOwnerSVGElement = static_cast<nsSVGSVGElement*>(ancestor);
-      NS_ADDREF(*aOwnerSVGElement);
-      return NS_OK;
-    }
-    ancestor = nsSVGUtils::GetParentElement(ancestor);
-  }
-
-  // we don't have a parent SVG element...
-
-  // are _we_ the outermost SVG element? If yes, return nsnull, but don't fail
-  if (Tag() == nsGkAtoms::svg) {
+  if (*aOwnerSVGElement || Tag() == nsGkAtoms::svg) {
+    // If we found something or we're the outermost SVG element, that's OK.
     return NS_OK;
   }
-  
-  // no owner found and we aren't the outermost SVG element either.
-  // this situation can e.g. occur during content tree teardown. 
+  // Otherwise, we've got an invalid structure
   return NS_ERROR_FAILURE;
 }
 
 /* readonly attribute nsIDOMSVGElement viewportElement; */
 NS_IMETHODIMP
 nsSVGElement::GetViewportElement(nsIDOMSVGElement * *aViewportElement)
 {
   *aViewportElement = nsSVGUtils::GetNearestViewportElement(this).get();
@@ -1445,19 +1406,31 @@ nsIAtom* nsSVGElement::GetEventNameForAt
 #endif // MOZ_SMIL
 
   return aAttr;
 }
 
 nsSVGSVGElement *
 nsSVGElement::GetCtx()
 {
-  nsCOMPtr<nsIDOMSVGSVGElement> svg;
-  GetOwnerSVGElement(getter_AddRefs(svg));
-  return static_cast<nsSVGSVGElement*>(svg.get());
+  dom::Element* ancestor = nsSVGUtils::GetParentElement(this);
+
+  while (ancestor && ancestor->GetNameSpaceID() == kNameSpaceID_SVG) {
+    nsIAtom* tag = ancestor->Tag();
+    if (tag == nsGkAtoms::foreignObject) {
+      return nsnull;
+    }
+    if (tag == nsGkAtoms::svg) {
+      return static_cast<nsSVGSVGElement*>(ancestor);
+    }
+    ancestor = nsSVGUtils::GetParentElement(ancestor);
+  }
+
+  // we don't have an ancestor <svg> element...
+  return nsnull;
 }
 
 /* virtual */ gfxMatrix
 nsSVGElement::PrependLocalTransformTo(const gfxMatrix &aMatrix)
 {
   return aMatrix;
 }
 
@@ -2161,16 +2134,33 @@ nsSVGElement::DidAnimateString(PRUint8 a
   if (frame) {
     StringAttributesInfo info = GetStringInfo();
     frame->AttributeChanged(info.mStringInfo[aAttrEnum].mNamespaceID,
                             *info.mStringInfo[aAttrEnum].mName,
                             nsIDOMMutationEvent::MODIFICATION);
   }
 }
 
+nsSVGClass *
+nsSVGElement::GetClass()
+{
+  return nsnull;
+}
+
+void
+nsSVGElement::DidAnimateClass()
+{
+  nsIFrame* frame = GetPrimaryFrame();
+
+  if (frame) {
+    frame->AttributeChanged(kNameSpaceID_None, nsGkAtoms::_class,
+                            nsIDOMMutationEvent::MODIFICATION);
+  }
+}
+
 nsresult
 nsSVGElement::ParseNumberOptionalNumber(const nsAString& aValue,
                                         PRUint32 aIndex1, PRUint32 aIndex2)
 {
   NS_ConvertUTF16toUTF8 value(aValue);
   const char *str = value.get();
 
   if (NS_IsAsciiWhitespace(*str))
@@ -2406,16 +2396,21 @@ nsSVGElement::GetAnimatedAttr(PRInt32 aN
     // preserveAspectRatio:
     if (aName == nsGkAtoms::preserveAspectRatio) {
       SVGAnimatedPreserveAspectRatio *preserveAspectRatio =
         GetPreserveAspectRatio();
       return preserveAspectRatio ?
         preserveAspectRatio->ToSMILAttr(this) : nsnull;
     }
 
+    if (aName == nsGkAtoms::_class) {
+      nsSVGClass *svgClass = GetClass();
+      return svgClass ? svgClass->ToSMILAttr(this) : nsnull;
+    }
+
     // NumberLists:
     {
       NumberListAttributesInfo info = GetNumberListInfo();
       for (PRUint32 i = 0; i < info.mNumberListCount; i++) {
         if (aName == *info.mNumberListInfo[i].mName) {
           NS_ABORT_IF_FALSE(i <= UCHAR_MAX, "Too many attributes");
           return info.mNumberLists[i].ToSMILAttr(this, PRUint8(i));
         }
@@ -2430,61 +2425,61 @@ nsSVGElement::GetAnimatedAttr(PRInt32 aN
           NS_ABORT_IF_FALSE(i <= UCHAR_MAX, "Too many attributes");
           return info.mLengthLists[i].ToSMILAttr(this,
                                                  PRUint8(i),
                                                  info.mLengthListInfo[i].mAxis,
                                                  info.mLengthListInfo[i].mCouldZeroPadList);
         }
       }
     }
+
+    // PointLists:
+    {
+      if (GetPointListAttrName() == aName) {
+        SVGAnimatedPointList *pointList = GetAnimatedPointList();
+        if (pointList) {
+          return pointList->ToSMILAttr(this);
+        }
+      }
+    }
+
+    // PathSegLists:
+    {
+      if (GetPathDataAttrName() == aName) {
+        SVGAnimatedPathSegList *segList = GetAnimPathSegList();
+        if (segList) {
+          return segList->ToSMILAttr(this);
+        }
+      }
+    }
+
+    // Mapped attributes:
+    if (IsAttributeMapped(aName)) {
+      nsCSSProperty prop =
+        nsCSSProps::LookupProperty(nsDependentAtomString(aName));
+      // Check IsPropertyAnimatable to avoid attributes that...
+      //  - map to explicitly unanimatable properties (e.g. 'direction')
+      //  - map to unsupported attributes (e.g. 'glyph-orientation-horizontal')
+      if (nsSMILCSSProperty::IsPropertyAnimatable(prop)) {
+        return new nsSMILMappedAttribute(prop, this);
+      }
+    }
   }
 
   // Strings
   {
     StringAttributesInfo info = GetStringInfo();
     for (PRUint32 i = 0; i < info.mStringCount; i++) {
       if (aNamespaceID == info.mStringInfo[i].mNamespaceID &&
           aName == *info.mStringInfo[i].mName) {
         return info.mStrings[i].ToSMILAttr(this);
       }
     }
   }
 
-  // PointLists:
-  {
-    if (GetPointListAttrName() == aName) {
-      SVGAnimatedPointList *pointList = GetAnimatedPointList();
-      if (pointList) {
-        return pointList->ToSMILAttr(this);
-      }
-    }
-  }
-
-  // PathSegLists:
-  {
-    if (GetPathDataAttrName() == aName) {
-      SVGAnimatedPathSegList *segList = GetAnimPathSegList();
-      if (segList) {
-        return segList->ToSMILAttr(this);
-      }
-    }
-  }
-
-  // Mapped attributes:
-  if (IsAttributeMapped(aName)) {
-    nsCSSProperty prop =
-      nsCSSProps::LookupProperty(nsDependentAtomString(aName));
-    // Check IsPropertyAnimatable to avoid attributes that...
-    //  - map to explicitly unanimatable properties (e.g. 'direction')
-    //  - map to unsupported attributes (e.g. 'glyph-orientation-horizontal')
-    if (nsSMILCSSProperty::IsPropertyAnimatable(prop)) {
-      return new nsSMILMappedAttribute(prop, this);
-    }
-  }
-
   return nsnull;
 }
 
 void
 nsSVGElement::AnimationNeedsResample()
 {
   nsIDocument* doc = GetCurrentDoc();
   if (doc && doc->HasAnimationController()) {
--- a/content/svg/content/src/nsSVGElement.h
+++ b/content/svg/content/src/nsSVGElement.h
@@ -63,16 +63,17 @@ class nsSVGLength2;
 class nsSVGNumber2;
 class nsSVGInteger;
 class nsSVGAngle;
 class nsSVGBoolean;
 class nsSVGEnum;
 struct nsSVGEnumMapping;
 class nsSVGViewBox;
 class nsSVGString;
+class nsSVGClass;
 struct gfxMatrix;
 namespace mozilla {
 class SVGAnimatedNumberList;
 class SVGNumberList;
 class SVGAnimatedLengthList;
 class SVGUserUnitList;
 class SVGAnimatedPointList;
 class SVGAnimatedPathSegList;
@@ -193,16 +194,17 @@ public:
   virtual void DidAnimateViewBox();
   virtual void DidAnimatePreserveAspectRatio();
   virtual void DidAnimateNumberList(PRUint8 aAttrEnum);
   virtual void DidAnimateLengthList(PRUint8 aAttrEnum);
   virtual void DidAnimatePointList();
   virtual void DidAnimatePathSegList();
   virtual void DidAnimateTransform();
   virtual void DidAnimateString(PRUint8 aAttrEnum);
+  virtual void DidAnimateClass();
 
   void GetAnimatedLengthValues(float *aFirst, ...);
   void GetAnimatedNumberValues(float *aFirst, ...);
   void GetAnimatedIntegerValues(PRInt32 *aFirst, ...);
   SVGAnimatedNumberList* GetAnimatedNumberList(PRUint8 aAttrEnum);
   SVGAnimatedNumberList* GetAnimatedNumberList(nsIAtom *aAttrName);
   void GetAnimatedLengthListValues(SVGUserUnitList *aFirst, ...);
   SVGAnimatedLengthList* GetAnimatedLengthList(PRUint8 aAttrEnum);
@@ -461,16 +463,17 @@ protected:
   virtual EnumAttributesInfo GetEnumInfo();
   // We assume all viewboxes and preserveAspectRatios are alike
   // so we don't need to wrap the class
   virtual nsSVGViewBox *GetViewBox();
   virtual SVGAnimatedPreserveAspectRatio *GetPreserveAspectRatio();
   virtual NumberListAttributesInfo GetNumberListInfo();
   virtual LengthListAttributesInfo GetLengthListInfo();
   virtual StringAttributesInfo GetStringInfo();
+  virtual nsSVGClass *GetClass();
 
   static nsSVGEnumMapping sSVGUnitTypesMap[];
 
 private:
   /* read <number-optional-number> */
   nsresult
   ParseNumberOptionalNumber(const nsAString& aValue,
                             PRUint32 aIndex1, PRUint32 aIndex2);
--- a/content/svg/content/src/nsSVGGraphicElement.cpp
+++ b/content/svg/content/src/nsSVGGraphicElement.cpp
@@ -33,16 +33,17 @@
  * 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 "nsSVGSVGElement.h"
 #include "nsSVGTransformList.h"
 #include "nsSVGAnimatedTransformList.h"
 #include "nsGkAtoms.h"
 #include "nsSVGMatrix.h"
 #include "nsIDOMEventTarget.h"
 #include "nsIFrame.h"
 #include "nsISVGChildFrame.h"
 #include "nsIDOMSVGPoint.h"
@@ -77,17 +78,17 @@ NS_IMETHODIMP nsSVGGraphicElement::GetNe
 {
   *aNearestViewportElement = nsSVGUtils::GetNearestViewportElement(this).get();
   return NS_OK;
 }
 
 /* readonly attribute nsIDOMSVGElement farthestViewportElement; */
 NS_IMETHODIMP nsSVGGraphicElement::GetFarthestViewportElement(nsIDOMSVGElement * *aFarthestViewportElement)
 {
-  *aFarthestViewportElement = nsSVGUtils::GetFarthestViewportElement(this).get();
+  NS_IF_ADDREF(*aFarthestViewportElement = nsSVGUtils::GetOuterSVGElement(this));
   return NS_OK;
 }
 
 /* nsIDOMSVGRect getBBox (); */
 NS_IMETHODIMP nsSVGGraphicElement::GetBBox(nsIDOMSVGRect **_retval)
 {
   *_retval = nsnull;
 
--- a/content/svg/content/src/nsSVGSVGElement.cpp
+++ b/content/svg/content/src/nsSVGSVGElement.cpp
@@ -634,17 +634,17 @@ nsSVGSVGElement::CreateSVGNumber(nsIDOMS
   NS_ADDREF(*_retval = new DOMSVGNumber());
   return NS_OK;
 }
 
 /* nsIDOMSVGLength createSVGLength (); */
 NS_IMETHODIMP
 nsSVGSVGElement::CreateSVGLength(nsIDOMSVGLength **_retval)
 {
-  NS_IF_ADDREF(*_retval = new DOMSVGLength());
+  NS_ADDREF(*_retval = new DOMSVGLength());
   return NS_OK;
 }
 
 /* nsIDOMSVGAngle createSVGAngle (); */
 NS_IMETHODIMP
 nsSVGSVGElement::CreateSVGAngle(nsIDOMSVGAngle **_retval)
 {
   return NS_NewDOMSVGAngle(_retval);
@@ -729,17 +729,17 @@ nsSVGSVGElement::GetNearestViewportEleme
   *aNearestViewportElement = nsSVGUtils::GetNearestViewportElement(this).get();
   return NS_OK;
 }
 
 /* readonly attribute nsIDOMSVGElement farthestViewportElement; */
 NS_IMETHODIMP
 nsSVGSVGElement::GetFarthestViewportElement(nsIDOMSVGElement * *aFarthestViewportElement)
 {