Merge TM -> JM
authorBrian Hackett <bhackett1024@gmail.com>
Fri, 08 Apr 2011 19:27:14 -0700
changeset 74916 bdacf8b9c9c4a342d7da79e89ce088bdd65cd189
parent 74915 a4355f02771640641de322b8bf0e0a24f428f974 (current diff)
parent 67937 48489a602029f61689e02e80ef2856772273c760 (diff)
child 74917 d3215d1e985a03eb795203c3a6de4bc86c0b246c
push id20986
push userkhuey@mozilla.com
push dateSun, 14 Aug 2011 11:45:15 +0000
treeherdermozilla-central@2de3cff973b2 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
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 TM -> JM
dom/base/nsGlobalWindow.cpp
dom/src/json/test/unit/fail1.json
dom/src/json/test/unit/fail10.json
dom/src/json/test/unit/fail11.json
dom/src/json/test/unit/fail12.json
dom/src/json/test/unit/fail13.json
dom/src/json/test/unit/fail14.json
dom/src/json/test/unit/fail15.json
dom/src/json/test/unit/fail16.json
dom/src/json/test/unit/fail17.json
dom/src/json/test/unit/fail18.json
dom/src/json/test/unit/fail19.json
dom/src/json/test/unit/fail2.json
dom/src/json/test/unit/fail20.json
dom/src/json/test/unit/fail21.json
dom/src/json/test/unit/fail22.json
dom/src/json/test/unit/fail23.json
dom/src/json/test/unit/fail24.json
dom/src/json/test/unit/fail25.json
dom/src/json/test/unit/fail26.json
dom/src/json/test/unit/fail27.json
dom/src/json/test/unit/fail28.json
dom/src/json/test/unit/fail29.json
dom/src/json/test/unit/fail3.json
dom/src/json/test/unit/fail30.json
dom/src/json/test/unit/fail31.json
dom/src/json/test/unit/fail32.json
dom/src/json/test/unit/fail33.json
dom/src/json/test/unit/fail34.json
dom/src/json/test/unit/fail4.json
dom/src/json/test/unit/fail5.json
dom/src/json/test/unit/fail6.json
dom/src/json/test/unit/fail7.json
dom/src/json/test/unit/fail8.json
dom/src/json/test/unit/fail9.json
dom/src/json/test/unit/head_json.js
dom/src/json/test/unit/json2.js
dom/src/json/test/unit/pass1.json
dom/src/json/test/unit/pass3.json
dom/src/json/test/unit/test_decode.js
dom/src/json/test/unit/test_decode_primitives.js
dom/src/json/test/unit/test_dropping_elements_in_stringify.js
dom/src/json/test/unit/test_encode_primitives.js
dom/src/json/test/unit/test_encoding_errors.js
dom/src/json/test/unit/test_long_input.js
dom/src/json/test/unit/test_replacer.js
dom/src/json/test/unit/test_reviver.js
dom/src/json/test/unit/test_syntax_errors.js
dom/src/json/test/unit/test_wrappers.js
dom/tests/browser/browser_643083.js
dom/tests/browser/test_643083.xul
dom/tests/mochitest/dom-level0/innerWidthHeight_metaviewport.html
dom/tests/mochitest/dom-level0/test_innerWidthHeight_metaviewport.html
editor/idl/nsICiter.idl
gfx/src/thebes/Makefile.in
gfx/src/thebes/mozilla-decoder.cpp
gfx/src/thebes/mozilla-decoder.h
gfx/src/thebes/nsIThebesFontMetrics.h
gfx/src/thebes/nsSystemFontsAndroid.cpp
gfx/src/thebes/nsSystemFontsAndroid.h
gfx/src/thebes/nsSystemFontsGTK2.cpp
gfx/src/thebes/nsSystemFontsGTK2.h
gfx/src/thebes/nsSystemFontsMac.h
gfx/src/thebes/nsSystemFontsMac.mm
gfx/src/thebes/nsSystemFontsOS2.cpp
gfx/src/thebes/nsSystemFontsOS2.h
gfx/src/thebes/nsSystemFontsQt.cpp
gfx/src/thebes/nsSystemFontsQt.h
gfx/src/thebes/nsSystemFontsWin.cpp
gfx/src/thebes/nsSystemFontsWin.h
gfx/src/thebes/nsThebesDeviceContext.cpp
gfx/src/thebes/nsThebesDeviceContext.h
gfx/src/thebes/nsThebesFontEnumerator.cpp
gfx/src/thebes/nsThebesFontEnumerator.h
gfx/src/thebes/nsThebesFontMetrics.cpp
gfx/src/thebes/nsThebesFontMetrics.h
gfx/src/thebes/nsThebesGfxFactory.cpp
gfx/src/thebes/nsThebesRegion.cpp
gfx/src/thebes/nsThebesRegion.h
gfx/src/thebes/nsThebesRenderingContext.cpp
gfx/src/thebes/nsThebesRenderingContext.h
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
js/src/Makefile.in
js/src/config/autoconf.mk.in
js/src/configure.in
js/src/jit-test/jit_test.py
js/src/jsapi-tests/Makefile.in
js/src/jsapi.cpp
js/src/jsapi.h
js/src/jsarray.cpp
js/src/jscntxt.cpp
js/src/jscntxt.h
js/src/jscntxtinlines.h
js/src/jsdbgapi.cpp
js/src/jsfun.cpp
js/src/jsgc.cpp
js/src/jsgc.h
js/src/jshashtable.h
js/src/jsobj.cpp
js/src/jsobj.h
js/src/json.cpp
js/src/jsproxy.cpp
js/src/jsregexp.cpp
js/src/jsscript.cpp
js/src/jsscript.h
js/src/jsstr.cpp
js/src/jstracer.cpp
js/src/jsxml.cpp
js/src/shell/Makefile.in
js/src/shell/js.cpp
js/src/tests/manifest.py
js/src/xpconnect/shell/xpcshell.cpp
js/src/xpconnect/src/xpcjsruntime.cpp
layout/reftests/svg/smil/motion/animateMotion-rotate-1.svg
modules/libpref/src/init/all.js
toolkit/themes/pinstripe/global/tree/sort-asc.gif
toolkit/themes/pinstripe/global/tree/sort-dsc.gif
webshell/Makefile.in
webshell/public/Makefile.in
webshell/public/nsIClipboardCommands.idl
webshell/public/nsIContentViewerContainer.idl
webshell/public/nsIDocumentLoaderFactory.idl
webshell/public/nsILinkHandler.h
webshell/public/nsIRefreshURI.idl
webshell/public/nsIWebShellServices.h
xpcom/ds/nsInt64.h
xpcom/glue/nsAutoLock.cpp
xpcom/glue/nsAutoLock.h
--- a/README.txt
+++ b/README.txt
@@ -18,8 +18,10 @@ on http://developer.mozilla.org, you can
 mozilla.* Usenet group, or on IRC at irc.mozilla.org. [The Mozilla news groups
 are accessible on Google Groups, or news.mozilla.org with a NNTP reader.]
 
 You can download nightly development builds from the Mozilla FTP server.
 Keep in mind that nightly builds, which are used by Mozilla developers for
 testing, may be buggy. Firefox nightlies, for example, can be found at:
 
     ftp://ftp.mozilla.org/pub/firefox/nightly/latest-trunk/
+            - or -
+    http://nightly.mozilla.org/
--- a/accessible/src/base/NotificationController.cpp
+++ b/accessible/src/base/NotificationController.cpp
@@ -176,19 +176,17 @@ NotificationController::ScheduleProcessi
     if (mPresShell->AddRefreshObserver(this, Flush_Display))
       mObservingState = eRefreshObserving;
   }
 }
 
 bool
 NotificationController::IsUpdatePending()
 {
-  nsCOMPtr<nsIPresShell_MOZILLA_2_0_BRANCH2> presShell =
-    do_QueryInterface(mPresShell);
-  return presShell->IsLayoutFlushObserver() ||
+  return mPresShell->IsLayoutFlushObserver() ||
     mObservingState == eRefreshProcessingForUpdate ||
     mContentInsertions.Length() != 0 || mNotifications.Length() != 0 ||
     mTextHash.Count() != 0;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // NotificationCollector: private
 
--- a/accessible/src/base/nsAccTreeWalker.cpp
+++ b/accessible/src/base/nsAccTreeWalker.cpp
@@ -89,18 +89,18 @@ nsAccTreeWalker::~nsAccTreeWalker()
     PopState();
 
   MOZ_COUNT_DTOR(nsAccTreeWalker);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsAccTreeWalker: private
 
-already_AddRefed<nsAccessible>
-nsAccTreeWalker::GetNextChildInternal(PRBool aNoWalkUp)
+nsAccessible*
+nsAccTreeWalker::NextChildInternal(bool aNoWalkUp)
 {
   if (!mState || !mState->content)
     return nsnull;
 
   if (!mState->childList)
     mState->childList = mState->content->GetChildren(mChildFilter);
 
   nsCOMPtr<nsIPresShell> presShell(do_QueryReferent(mWeakShell));
@@ -109,38 +109,38 @@ nsAccTreeWalker::GetNextChildInternal(PR
   if (mState->childList)
     mState->childList->GetLength(&length);
 
   while (mState->childIdx < length) {
     nsIContent* childNode = mState->childList->GetNodeAt(mState->childIdx);
     mState->childIdx++;
 
     bool isSubtreeHidden = false;
-    nsRefPtr<nsAccessible> accessible =
+    nsAccessible* accessible =
       GetAccService()->GetOrCreateAccessible(childNode, presShell, mWeakShell,
                                              &isSubtreeHidden);
 
     if (accessible)
-      return accessible.forget();
+      return accessible;
 
     // Walk down into subtree to find accessibles.
     if (!isSubtreeHidden) {
       if (!PushState(childNode))
         break;
 
-      accessible = GetNextChildInternal(PR_TRUE);
+      accessible = NextChildInternal(true);
       if (accessible)
-        return accessible.forget();
+        return accessible;
     }
   }
 
   // No more children, get back to the parent.
   PopState();
 
-  return aNoWalkUp ? nsnull : GetNextChildInternal(PR_FALSE);
+  return aNoWalkUp ? nsnull : NextChildInternal(false);
 }
 
 void
 nsAccTreeWalker::PopState()
 {
   WalkState* prevToLastState = mState->prevState;
   delete mState;
   mState = prevToLastState;
--- a/accessible/src/base/nsAccTreeWalker.h
+++ b/accessible/src/base/nsAccTreeWalker.h
@@ -54,33 +54,36 @@ class nsAccTreeWalker
 {
 public:
   nsAccTreeWalker(nsIWeakReference *aShell, nsIContent *aNode, 
                   PRBool aWalkAnonymousContent);
   virtual ~nsAccTreeWalker();
 
   /**
    * Return the next child accessible.
+   *
+   * @note Returned accessible is bound to the document, if the accessible is
+   *       rejected during tree creation then the caller should be unbind it
+   *       from the document.
    */
-  already_AddRefed<nsAccessible> GetNextChild()
+  inline nsAccessible* NextChild()
   {
-    return GetNextChildInternal(PR_FALSE);
+    return NextChildInternal(false);
   }
 
 private:
 
   /**
    * Return the next child accessible.
    *
    * @param  aNoWalkUp  [in] specifies the walk direction, true means we
    *                     shouldn't go up through the tree if we failed find
    *                     accessible children.
    */
-  already_AddRefed<nsAccessible>
-    GetNextChildInternal(PRBool aNoWalkUp = PR_FALSE);
+  nsAccessible* NextChildInternal(bool aNoWalkUp);
 
   /**
    * Create new state for the given node and push it on top of stack.
    *
    * @note State stack is used to navigate up/down the DOM subtree during
    *        accessible children search.
    */
   PRBool PushState(nsIContent *aNode);
--- a/accessible/src/base/nsAccUtils.cpp
+++ b/accessible/src/base/nsAccUtils.cpp
@@ -39,17 +39,16 @@
 #include "nsCoreUtils.h"
 #include "nsAccUtils.h"
 
 #include "nsIAccessibleStates.h"
 #include "nsIAccessibleTypes.h"
 
 #include "nsAccessibilityService.h"
 #include "nsAccessibilityAtoms.h"
-#include "nsAccTreeWalker.h"
 #include "nsARIAMap.h"
 #include "nsDocAccessible.h"
 #include "nsHyperTextAccessible.h"
 #include "nsHTMLTableAccessible.h"
 #include "nsTextAccessible.h"
 #include "nsXULTreeGridAccessible.h"
 
 #include "nsIDOMXULContainerElement.h"
@@ -329,33 +328,16 @@ nsAccUtils::HasDefinedARIAToken(nsIConte
                             nsAccessibilityAtoms::_empty, eCaseMatters) ||
       aContent->AttrValueIs(kNameSpaceID_None, aAtom,
                             nsAccessibilityAtoms::_undefined, eCaseMatters)) {
         return PR_FALSE;
   }
   return PR_TRUE;
 }
 
-PRBool
-nsAccUtils::HasAccessibleChildren(nsINode *aNode)
-{
-  if (!aNode)
-    return PR_FALSE;
-
-  nsIPresShell *presShell = nsCoreUtils::GetPresShellFor(aNode);
-  if (!presShell)
-    return PR_FALSE;
-
-  nsIContent *content = nsCoreUtils::GetRoleContent(aNode);
-  nsCOMPtr<nsIWeakReference> weakShell(do_GetWeakReference(presShell));
-  nsAccTreeWalker walker(weakShell, content, PR_FALSE);
-  nsRefPtr<nsAccessible> accessible = walker.GetNextChild();
-  return accessible ? PR_TRUE : PR_FALSE;
-}
-
 nsAccessible *
 nsAccUtils::GetAncestorWithRole(nsAccessible *aDescendant, PRUint32 aRole)
 {
   nsAccessible *document = aDescendant->GetDocAccessible();
   nsAccessible *parent = aDescendant;
   while ((parent = parent->GetParent())) {
     PRUint32 testRole = parent->Role();
     if (testRole == aRole)
--- a/accessible/src/base/nsAccUtils.h
+++ b/accessible/src/base/nsAccUtils.h
@@ -178,21 +178,16 @@ public:
     nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(aContainer));
     nsCOMPtr<nsIPresShell> presShell;
     docShell->GetPresShell(getter_AddRefs(presShell));
     return presShell ?
       GetAccService()->GetDocAccessible(presShell->GetDocument()) : nsnull;
   }
 
   /**
-   * Return true if the given DOM node contains accessible children.
-   */
-  static PRBool HasAccessibleChildren(nsINode *aNode);
-
-  /**
     * Return ancestor in this document with the given role if it exists.
     *
     * @param  aDescendant  [in] descendant to start search with
     * @param  aRole        [in] role to find matching ancestor for
     * @return               the ancestor accessible with the given role, or
     *                       nsnull if no match is found
     */
    static nsAccessible * GetAncestorWithRole(nsAccessible *aDescendant,
--- a/accessible/src/base/nsAccessibilityService.cpp
+++ b/accessible/src/base/nsAccessibilityService.cpp
@@ -96,16 +96,17 @@
 
 #ifndef DISABLE_XFORMS_HOOKS
 #include "nsXFormsFormControlsAccessible.h"
 #include "nsXFormsWidgetsAccessible.h"
 #endif
 
 #include "mozilla/FunctionTimer.h"
 #include "mozilla/dom/Element.h"
+#include "nsImageMapUtils.h"
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsAccessibilityService
 ////////////////////////////////////////////////////////////////////////////////
 
 nsAccessibilityService *nsAccessibilityService::gAccessibilityService = nsnull;
 PRBool nsAccessibilityService::gIsShutdown = PR_TRUE;
 
@@ -259,31 +260,22 @@ nsAccessibilityService::CreateHTMLCombob
   NS_IF_ADDREF(accessible);
   return accessible;
 }
 
 already_AddRefed<nsAccessible>
 nsAccessibilityService::CreateHTMLImageAccessible(nsIContent* aContent,
                                                   nsIPresShell* aPresShell)
 {
-  nsCOMPtr<nsIHTMLDocument> htmlDoc =
-    do_QueryInterface(aContent->GetCurrentDoc());
-
-  nsCOMPtr<nsIDOMHTMLMapElement> mapElm;
-  if (htmlDoc) {
-    nsAutoString mapElmName;
-    aContent->GetAttr(kNameSpaceID_None, nsAccessibilityAtoms::usemap,
-                      mapElmName);
-
-    if (!mapElmName.IsEmpty()) {
-      if (mapElmName.CharAt(0) == '#')
-        mapElmName.Cut(0,1);
-      mapElm = do_QueryInterface(htmlDoc->GetImageMap(mapElmName));
-    }
-  }
+  nsAutoString mapElmName;
+  aContent->GetAttr(kNameSpaceID_None,
+                    nsAccessibilityAtoms::usemap,
+                    mapElmName);
+  nsCOMPtr<nsIDOMHTMLMapElement> mapElm =
+    nsImageMapUtils::FindImageMap(aContent->GetCurrentDoc(), mapElmName);
 
   nsCOMPtr<nsIWeakReference> weakShell(do_GetWeakReference(aPresShell));
   nsAccessible* accessible = mapElm ?
     new nsHTMLImageMapAccessible(aContent, weakShell, mapElm) :
     new nsHTMLImageAccessibleWrap(aContent, weakShell);
   NS_IF_ADDREF(accessible);
   return accessible;
 }
@@ -861,44 +853,40 @@ static PRBool HasRelatedContent(nsIConte
         // ancestor has activedescendant property, this content could be active
       return PR_TRUE;
     }
   }
 
   return PR_FALSE;
 }
 
-already_AddRefed<nsAccessible>
+nsAccessible*
 nsAccessibilityService::GetOrCreateAccessible(nsINode* aNode,
                                               nsIPresShell* aPresShell,
                                               nsIWeakReference* aWeakShell,
                                               bool* aIsSubtreeHidden)
 {
   if (!aPresShell || !aWeakShell || !aNode || gIsShutdown)
     return nsnull;
 
   if (aIsSubtreeHidden)
     *aIsSubtreeHidden = false;
 
   // Check to see if we already have an accessible for this node in the cache.
   nsAccessible* cachedAccessible = GetAccessibleInWeakShell(aNode, aWeakShell);
-  if (cachedAccessible) {
-    NS_ADDREF(cachedAccessible);
+  if (cachedAccessible)
     return cachedAccessible;
-  }
 
   // No cache entry, so we must create the accessible.
 
   if (aNode->IsNodeOfType(nsINode::eDOCUMENT)) {
     // If it's document node then ask accessible document loader for
     // document accessible, otherwise return null.
     nsCOMPtr<nsIDocument> document(do_QueryInterface(aNode));
-    nsAccessible *accessible = GetDocAccessible(document);
-    NS_IF_ADDREF(accessible);
-    return accessible;
+    return GetDocAccessible(document);
   }
 
   // We have a content node.
   if (!aNode->IsInDoc()) {
     NS_WARNING("Creating accessible for node with no document");
     return nsnull;
   }
 
@@ -927,26 +915,23 @@ nsAccessibilityService::GetOrCreateAcces
   }
 
   if (weakFrame.GetFrame()->GetContent() != content) {
     // Not the main content for this frame. This happens because <area>
     // elements return the image frame as their primary frame. The main content
     // for the image frame is the image content. If the frame is not an image
     // frame or the node is not an area element then null is returned.
     // This setup will change when bug 135040 is fixed.
-    nsAccessible* areaAcc = GetAreaAccessible(weakFrame.GetFrame(),
-                                              aNode, aWeakShell);
-    NS_IF_ADDREF(areaAcc);
-    return areaAcc;
+    return GetAreaAccessible(weakFrame.GetFrame(), aNode, aWeakShell);
   }
 
   nsDocAccessible* docAcc =
     GetAccService()->GetDocAccessible(aNode->GetOwnerDoc());
   if (!docAcc) {
-    NS_NOTREACHED("No document for accessible being created!");
+    NS_NOTREACHED("Node has no host document accessible!");
     return nsnull;
   }
 
   // Attempt to create an accessible based on what we know.
   nsRefPtr<nsAccessible> newAcc;
 
   // Create accessible for visible text frames.
   if (content->IsNodeOfType(nsINode::eTEXT)) {
@@ -957,17 +942,17 @@ nsAccessibilityService::GetOrCreateAcces
         *aIsSubtreeHidden = true;
 
       return nsnull;
     }
 
     newAcc = weakFrame->CreateAccessible();
     if (docAcc->BindToDocument(newAcc, nsnull)) {
       newAcc->AsTextLeaf()->SetText(text);
-      return newAcc.forget();
+      return newAcc;
     }
 
     return nsnull;
   }
 
   PRBool isHTML = content->IsHTML();
   if (isHTML && content->Tag() == nsAccessibilityAtoms::map) {
     // Create hyper text accessible for HTML map if it is used to group links
@@ -984,17 +969,17 @@ nsAccessibilityService::GetOrCreateAcces
       if (aIsSubtreeHidden)
         *aIsSubtreeHidden = true;
 
       return nsnull;
     }
 
     newAcc = new nsHyperTextAccessibleWrap(content, aWeakShell);
     if (docAcc->BindToDocument(newAcc, nsAccUtils::GetRoleMapEntry(aNode)))
-      return newAcc.forget();
+      return newAcc;
     return nsnull;
   }
 
   nsRoleMapEntry *roleMapEntry = nsAccUtils::GetRoleMapEntry(aNode);
   if (roleMapEntry && !nsCRT::strcmp(roleMapEntry->roleString, "presentation") &&
       !content->IsFocusable()) { // For presentation only
     // Only create accessible for role of "presentation" if it is focusable --
     // in that case we need an accessible in case it gets focused, we
@@ -1170,19 +1155,17 @@ nsAccessibilityService::GetOrCreateAcces
       newAcc = new nsHyperTextAccessibleWrap(content, aWeakShell);
     }
     else {  // XUL, SVG, MathML etc.
       // Interesting generic non-HTML container
       newAcc = new nsAccessibleWrap(content, aWeakShell);
     }
   }
 
-  if (docAcc->BindToDocument(newAcc, roleMapEntry))
-    return newAcc.forget();
-  return nsnull;
+  return docAcc->BindToDocument(newAcc, roleMapEntry) ? newAcc : nsnull;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsAccessibilityService private
 
 PRBool
 nsAccessibilityService::Init()
 {
--- a/accessible/src/base/nsAccessibilityService.h
+++ b/accessible/src/base/nsAccessibilityService.h
@@ -153,20 +153,19 @@ public:
    * one.
    *
    * @param  aNode             [in] the given node
    * @param  aPresShell        [in] the pres shell of the node
    * @param  aWeakShell        [in] the weak shell for the pres shell
    * @param  aIsSubtreeHidden  [out, optional] indicates whether the node's
    *                             frame and its subtree is hidden
    */
-  already_AddRefed<nsAccessible>
-    GetOrCreateAccessible(nsINode* aNode, nsIPresShell* aPresShell,
-                          nsIWeakReference* aWeakShell,
-                          bool* aIsSubtreeHidden = nsnull);
+  nsAccessible* GetOrCreateAccessible(nsINode* aNode, nsIPresShell* aPresShell,
+                                      nsIWeakReference* aWeakShell,
+                                      bool* aIsSubtreeHidden = nsnull);
 
   /**
    * Return an accessible for the given DOM node.
    */
   nsAccessible* GetAccessible(nsINode* aNode);
 
   /**
    * Return an accessible for a DOM node in the given presshell.
--- a/accessible/src/base/nsAccessible.cpp
+++ b/accessible/src/base/nsAccessible.cpp
@@ -3117,18 +3117,18 @@ nsAccessible::UnselectAll()
 ////////////////////////////////////////////////////////////////////////////////
 // nsAccessible protected methods
 
 void
 nsAccessible::CacheChildren()
 {
   nsAccTreeWalker walker(mWeakShell, mContent, GetAllowsAnonChildAccessibles());
 
-  nsRefPtr<nsAccessible> child;
-  while ((child = walker.GetNextChild()) && AppendChild(child));
+  nsAccessible* child = nsnull;
+  while ((child = walker.NextChild()) && AppendChild(child));
 }
 
 void
 nsAccessible::TestChildCache(nsAccessible* aCachedChild) const
 {
 #ifdef DEBUG
   PRInt32 childCount = mChildren.Length();
   if (childCount == 0) {
--- a/accessible/src/base/nsDocAccessible.cpp
+++ b/accessible/src/base/nsDocAccessible.cpp
@@ -1491,18 +1491,18 @@ nsDocAccessible::NotifyOfCachingEnd(nsAc
 void
 nsDocAccessible::CacheChildren()
 {
   // Search for accessible children starting from the document element since
   // some web pages tend to insert elements under it rather than document body.
   nsAccTreeWalker walker(mWeakShell, mDocument->GetRootElement(),
                          GetAllowsAnonChildAccessibles());
 
-  nsRefPtr<nsAccessible> child;
-  while ((child = walker.GetNextChild()) && AppendChild(child));
+  nsAccessible* child = nsnull;
+  while ((child = walker.NextChild()) && AppendChild(child));
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // Protected members
 
 void
 nsDocAccessible::NotifyOfInitialUpdate()
 {
--- a/accessible/src/html/nsHTMLTableAccessible.cpp
+++ b/accessible/src/html/nsHTMLTableAccessible.cpp
@@ -434,21 +434,21 @@ void
 nsHTMLTableAccessible::CacheChildren()
 {
   // Move caption accessible so that it's the first child. Check for the first
   // caption only, because nsAccessibilityService ensures we don't create
   // accessibles for the other captions, since only the first is actually
   // visible.
   nsAccTreeWalker walker(mWeakShell, mContent, GetAllowsAnonChildAccessibles());
 
-  nsRefPtr<nsAccessible> child;
-  while ((child = walker.GetNextChild())) {
+  nsAccessible* child = nsnull;
+  while ((child = walker.NextChild())) {
     if (child->Role() == nsIAccessibleRole::ROLE_CAPTION) {
       InsertChildAt(0, child);
-      while ((child = walker.GetNextChild()) && AppendChild(child));
+      while ((child = walker.NextChild()) && AppendChild(child));
       break;
     }
     AppendChild(child);
   }
 }
 
 PRUint32
 nsHTMLTableAccessible::NativeRole()
--- a/accessible/src/xforms/nsXFormsAccessible.cpp
+++ b/accessible/src/xforms/nsXFormsAccessible.cpp
@@ -125,17 +125,17 @@ nsXFormsAccessible::CacheSelectChildren(
 
   for (PRUint32 index = 0; index < length; index++) {
     nsCOMPtr<nsIDOMNode> DOMChild;
     children->Item(index, getter_AddRefs(DOMChild));
     if (!DOMChild)
       continue;
 
     nsCOMPtr<nsIContent> child(do_QueryInterface(DOMChild));
-    nsRefPtr<nsAccessible> accessible =
+    nsAccessible* accessible =
       GetAccService()->GetOrCreateAccessible(child, presShell, mWeakShell);
     if (!accessible)
       continue;
 
     AppendChild(accessible);
   }
 }
 
--- a/accessible/src/xul/nsXULColorPickerAccessible.cpp
+++ b/accessible/src/xul/nsXULColorPickerAccessible.cpp
@@ -158,21 +158,21 @@ nsXULColorPickerAccessible::NativeRole()
 ////////////////////////////////////////////////////////////////////////////////
 // nsXULColorPickerAccessible: protected nsAccessible
 
 void
 nsXULColorPickerAccessible::CacheChildren()
 {
   nsAccTreeWalker walker(mWeakShell, mContent, PR_TRUE);
 
-  nsRefPtr<nsAccessible> child;
-  while ((child = walker.GetNextChild())) {
+  nsAccessible* child = nsnull;
+  while ((child = walker.NextChild())) {
     PRUint32 role = child->Role();
 
-    // Get an accessbile for menupopup or panel elements.
+    // Get an accessible for menupopup or panel elements.
     if (role == nsIAccessibleRole::ROLE_ALERT) {
       AppendChild(child);
       return;
     }
 
     // Unbind rejected accessibles from the document.
     GetDocAccessible()->UnbindFromDocument(child);
   }
--- a/accessible/src/xul/nsXULFormControlAccessible.cpp
+++ b/accessible/src/xul/nsXULFormControlAccessible.cpp
@@ -200,47 +200,47 @@ nsXULButtonAccessible::CacheChildren()
   PRBool isMenuButton = isMenu ?
     PR_FALSE :
     mContent->AttrValueIs(kNameSpaceID_None, nsAccessibilityAtoms::type,
                           nsAccessibilityAtoms::menuButton, eCaseMatters);
 
   if (!isMenu && !isMenuButton)
     return;
 
-  nsRefPtr<nsAccessible> menupopupAccessible;
-  nsRefPtr<nsAccessible> buttonAccessible;
+  nsAccessible* menupopup = nsnull;
+  nsAccessible* button = nsnull;
 
   nsAccTreeWalker walker(mWeakShell, mContent, PR_TRUE);
 
-  nsRefPtr<nsAccessible> child;
-  while ((child = walker.GetNextChild())) {
+  nsAccessible* child = nsnull;
+  while ((child = walker.NextChild())) {
     PRUint32 role = child->Role();
 
     if (role == nsIAccessibleRole::ROLE_MENUPOPUP) {
-      // Get an accessbile for menupopup or panel elements.
-      menupopupAccessible.swap(child);
+      // Get an accessible for menupopup or panel elements.
+      menupopup = child;
 
     } else if (isMenuButton && role == nsIAccessibleRole::ROLE_PUSHBUTTON) {
       // Button type="menu-button" contains a real button. Get an accessible
-      // for it. Ignore dropmarker button what is placed as a last child.
-      buttonAccessible.swap(child);
+      // for it. Ignore dropmarker button which is placed as a last child.
+      button = child;
       break;
 
     } else {
       // Unbind rejected accessible from document.
       GetDocAccessible()->UnbindFromDocument(child);
     }
   }
 
-  if (!menupopupAccessible)
+  if (!menupopup)
     return;
 
-  AppendChild(menupopupAccessible);
-  if (buttonAccessible)
-    AppendChild(buttonAccessible);
+  AppendChild(menupopup);
+  if (button)
+    AppendChild(button);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsXULButtonAccessible protected
 
 PRBool
 nsXULButtonAccessible::ContainsMenu()
 {
@@ -1040,18 +1040,18 @@ nsXULTextFieldAccessible::CacheChildren(
   // Create child accessibles for native anonymous content of underlying HTML
   // input element.
   nsCOMPtr<nsIContent> inputContent(GetInputField());
   if (!inputContent)
     return;
 
   nsAccTreeWalker walker(mWeakShell, inputContent, PR_FALSE);
 
-  nsRefPtr<nsAccessible> child;
-  while ((child = walker.GetNextChild()) && AppendChild(child));
+  nsAccessible* child = nsnull;
+  while ((child = walker.NextChild()) && AppendChild(child));
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsXULTextFieldAccessible protected
 
 already_AddRefed<nsIContent>
 nsXULTextFieldAccessible::GetInputField() const
 {
--- a/accessible/tests/mochitest/treeupdate/Makefile.in
+++ b/accessible/tests/mochitest/treeupdate/Makefile.in
@@ -43,16 +43,17 @@ VPATH		= @srcdir@
 relativesrcdir  = accessible/treeupdate
 
 include $(DEPTH)/config/autoconf.mk
 include $(topsrcdir)/config/rules.mk
 
 _TEST_FILES =\
 		test_ariadialog.html \
 		test_doc.html \
+		test_gencontent.html \
 		test_list_editabledoc.html \
 		test_list.html \
 		test_menu.xul \
 		test_recreation.html \
 		test_select.html \
 		test_textleaf.html \
 		test_visibility.html \
 		test_whitespace.html \
new file mode 100644
--- /dev/null
+++ b/accessible/tests/mochitest/treeupdate/test_gencontent.html
@@ -0,0 +1,160 @@
+<html>
+
+<head>
+  <title>Elements with CSS generated content</title>
+
+  <link rel="stylesheet" type="text/css"
+        href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+  <style>
+    .gentext:before {
+      content: "START"
+    }
+    .gentext:after {
+      content: "END"
+    }
+  </style>
+
+  <script type="application/javascript"
+          src="chrome://mochikit/content/MochiKit/packed.js"></script>
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+  <script type="application/javascript"
+          src="../common.js"></script>
+  <script type="application/javascript"
+          src="../events.js"></script>
+
+  <script type="application/javascript">
+
+    ////////////////////////////////////////////////////////////////////////////
+    // Invokers
+    ////////////////////////////////////////////////////////////////////////////
+
+    /**
+     * Insert new node with CSS generated content style applied to container.
+     */
+    function insertNodeHavingGenContent(aContainerID)
+    {
+      this.containerNode = getNode(aContainerID);
+      this.container = getAccessible(this.containerNode);
+
+      this.eventSeq = [
+        new invokerChecker(EVENT_SHOW, getFirstChild, this.container),
+        new invokerChecker(EVENT_REORDER, this.container)
+      ];
+
+      this.invoke = function insertNodeHavingGenContent_invoke()
+      {
+        var node = document.createElement("div");
+        node.textContent = "text";
+        node.setAttribute("class", "gentext");
+        this.containerNode.appendChild(node);
+      }
+
+      this.finalCheck = function insertNodeHavingGenContent_finalCheck()
+      {
+        var accTree =
+          { SECTION: [ // container
+            { SECTION: [ // inserted node
+              { STATICTEXT: [] }, // :before
+              { TEXT_LEAF: [] }, // primary text
+              { STATICTEXT: [] } // :after
+            ] }
+          ] };
+        testAccessibleTree(this.container, accTree);
+      }
+
+      this.getID = function insertNodeHavingGenContent_getID()
+      {
+        return "insert node having generated content to " + prettyName(aContainerID);
+      }
+    }
+
+    /**
+     * Add CSS generated content to the given node contained by container node.
+     */
+    function addGenContent(aContainerID, aNodeID)
+    {
+      this.container = getAccessible(aContainerID);
+      this.node = getNode(aNodeID);
+
+      this.eventSeq = [
+        new invokerChecker(EVENT_HIDE, this.container.firstChild),
+        new invokerChecker(EVENT_SHOW, getFirstChild, this.container),
+        new invokerChecker(EVENT_REORDER, this.container)
+      ];
+
+      this.invoke = function addGenContent_invoke()
+      {
+        this.node.setAttribute("class", "gentext");
+      }
+
+      this.finalCheck = function insertNodeHavingGenContent_finalCheck()
+      {
+        var accTree =
+          { SECTION: [ // container
+            { SECTION: [ // inserted node
+              { STATICTEXT: [] }, // :before
+              { TEXT_LEAF: [] }, // primary text
+              { STATICTEXT: [] } // :after
+            ] }
+          ] };
+        testAccessibleTree(this.container, accTree);
+      }
+
+      this.getID = function addGenContent_getID()
+      {
+        return "add generated content to" + prettyName(aNodeID);
+      }
+    }
+
+    /**
+     * Target getters.
+     */
+    function getFirstChild(aAcc)
+    {
+      try { return aAcc.getChildAt(0); }
+      catch (e) { return null; }
+    }
+
+    ////////////////////////////////////////////////////////////////////////////
+    // Do tests
+    ////////////////////////////////////////////////////////////////////////////
+
+    var gQueue = null;
+    //gA11yEventDumpID = "eventdump"; // debug stuff
+    //gA11yEventDumpToConsole = true;
+
+    function doTests()
+    {
+      gQueue = new eventQueue();
+
+      gQueue.push(new insertNodeHavingGenContent("container1"));
+      gQueue.push(new addGenContent("container2", "container2_child"));
+
+      gQueue.invoke(); // Will call SimpleTest.finish();
+    }
+
+    SimpleTest.waitForExplicitFinish();
+    addA11yLoadEvent(doTests);
+  </script>
+</head>
+
+<body>
+
+  <a target="_blank"
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=646350"
+     title="Add a test for dynamic chnages of CSS generated content">
+    Mozilla Bug 646350</a>
+
+  <p id="display"></p>
+  <div id="content" style="display: none"></div>
+  <pre id="test">
+  </pre>
+  <div id="eventdump"></div>
+
+  <div id="container1"></div>
+  <div id="container2"><div id="container2_child">text</div></div>
+</body>
+</html>
--- a/browser/base/content/browser-sets.inc
+++ b/browser/base/content/browser-sets.inc
@@ -75,17 +75,17 @@
 
 
     <commandset id="editMenuCommands"/>
 
     <command id="View:PageSource" oncommand="BrowserViewSourceOfDocument(content.document);" observes="isImage"/>
     <command id="View:PageInfo" oncommand="BrowserPageInfo();"/>
     <command id="View:FullScreen" oncommand="BrowserFullScreen();"/>
     <command id="cmd_find"
-             oncommand="if (TabView.isVisible()) TabView.enableSearch(event); else gFindBar.onFindCommand();"
+             oncommand="gFindBar.onFindCommand();"
              observes="isImage"/>
     <command id="cmd_findAgain"
              oncommand="gFindBar.onFindAgainCommand(false);"
              observes="isImage"/>
     <command id="cmd_findPrevious"
              oncommand="gFindBar.onFindAgainCommand(true);"
              observes="isImage"/>
     <!-- work-around bug 392512 -->
--- a/browser/base/content/browser-tabview.js
+++ b/browser/base/content/browser-tabview.js
@@ -258,22 +258,16 @@ let TabView = {
       let self = this;
       this._initFrame(function() {
         self._window.GroupItems.moveTabToGroupItem(tab, groupItemId);
       });
     }
   },
 
   // ----------
-  enableSearch: function TabView_enableSearch(event) {
-    if (this._window)
-      this._window.UI.enableSearch(event);
-  },
-
-  // ----------
   // Adds new key commands to the browser, for invoking the Tab Candy UI
   // and for switching between groups of tabs when outside of the Tab Candy UI.
   _setBrowserKeyHandlers: function TabView__setBrowserKeyHandlers() {
     if (this._browserKeyHandlerInitialized)
       return;
 
     this._browserKeyHandlerInitialized = true;
 
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -788,16 +788,17 @@
 #ifdef MENUBAR_CAN_AUTOHIDE
 #ifndef CAN_DRAW_IN_TITLEBAR
 #define APPMENU_ON_TABBAR
 #endif
 #endif
 
 
     <toolbar id="TabsToolbar"
+             class="toolbar-primary"
              fullscreentoolbar="true"
              customizable="true"
              mode="icons" lockmode="true"
              iconsize="small" defaulticonsize="small" lockiconsize="true"
              aria-label="&tabsToolbar.label;"
              context="toolbar-context-menu"
 #ifdef APPMENU_ON_TABBAR
              defaultset="appmenu-toolbar-button,tabbrowser-tabs,new-tab-button,alltabs-button,tabs-closebutton"
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -2738,19 +2738,19 @@
             TabsInTitlebar.allowedBy("tabs-visible", val);
 
           return val;
         ]]></setter>
       </property>
 
       <method name="updateVisibility">
         <body><![CDATA[
-          if (this.childNodes.length - this.tabbrowser._removingTabs.length == 1 &&
-              window.toolbar.visible)
-            this.visible = !Services.prefs.getBoolPref("browser.tabs.autoHide");
+          if (this.childNodes.length - this.tabbrowser._removingTabs.length == 1)
+            this.visible = window.toolbar.visible &&
+                           !Services.prefs.getBoolPref("browser.tabs.autoHide");
           else
             this.visible = true;
         ]]></body>
       </method>
 
       <method name="adjustTabstrip">
         <body><![CDATA[
           // modes for tabstrip
--- a/browser/base/content/tabview/groupitems.js
+++ b/browser/base/content/tabview/groupitems.js
@@ -1100,16 +1100,22 @@ GroupItem.prototype = Utils.extend(new I
       item.setParent(null);
       item.removeClass("tabInGroupItem");
       item.removeClass("stacked");
       item.isStacked = false;
       item.setHidden(false);
       item.removeClass("stack-trayed");
       item.setRotation(0);
 
+      // Force tabItem resize if it's dragged out of a stacked groupItem.
+      // The tabItems's title will be visible and that's why we need to
+      // recalculate its height.
+      if (item.isDragging && this.isStacked())
+        item.setBounds(item.getBounds(), true, {force: true});
+
       item.droppable(true);
       item.removeSubscriber(this, "close");
 
       if (typeof item.setResizable == 'function')
         item.setResizable(true, options.immediately);
 
       // if a blank tab is selected while restoring a tab the blank tab gets
       // removed. we need to keep the group alive for the restored tab.
--- a/browser/base/content/tabview/search.js
+++ b/browser/base/content/tabview/search.js
@@ -338,18 +338,17 @@ SearchEventHandlerClass.prototype = {
   },
 
   // ----------
   // Function: init
   // Initializes the searchbox to be focused, and everything
   // else to be hidden, and to have everything have the appropriate
   // event handlers;
   init: function () {
-    var self = this;
-    iQ("#searchbox")[0].focus(); 
+    let self = this;
     iQ("#search").hide();
     iQ("#searchshade").hide().click(function(event) {
       if ( event.target.id != "searchbox")
         hideSearch();
     });
     
     iQ("#searchbox").keyup(function() {
       performSearch();
@@ -385,16 +384,23 @@ SearchEventHandlerClass.prototype = {
         (!event.keyCode && !event.charCode)) {
       return;
     }
 
     // If we are already in an input field, allow typing as normal.
     if (event.target.nodeName == "INPUT")
       return;
 
+    // / is used to activate the search feature so the key shouldn't be entered 
+    // into the search box.
+    if (event.keyCode == KeyEvent.DOM_VK_SLASH) {
+      event.stopPropagation();
+      event.preventDefault();
+    }
+
     this.switchToInMode();
     this.initiatedBy = "keydown";
     ensureSearchShown(true);
   },
 
   // ----------
   // Function: inSearchKeyHandler
   // Handles all keydown while search mode.
@@ -521,31 +527,36 @@ var TabHandlers = {
   
   _mouseDownLocation: null
 };
 
 function createSearchTabMacher() {
   return new TabMatcher(iQ("#searchbox").val());
 }
 
-function hideSearch(event){
+function hideSearch(event) {
   iQ("#searchbox").val("");
   iQ("#searchshade").hide();
   iQ("#search").hide();
 
   iQ("#searchbutton").css({ opacity:.8 });
 
 #ifdef XP_MACOSX
   UI.setTitlebarColors(true);
 #endif
 
   performSearch();
   SearchEventHandler.switchToBeforeMode();
 
-  if (event){
+  if (event) {
+    // when hiding the search mode, we need to prevent the keypress handler
+    // in UI__setTabViewFrameKeyHandlers to handle the key press again. e.g. Esc
+    // which is already handled by the key down in this class.
+    if (event.type == "keydown")
+      UI.ignoreKeypressForSearch = true;
     event.preventDefault();
     event.stopPropagation();
   }
 
   // Return focus to the tab window
   UI.blurAll();
   gTabViewFrame.contentWindow.focus();
 
--- a/browser/base/content/tabview/ui.js
+++ b/browser/base/content/tabview/ui.js
@@ -127,16 +127,24 @@ let UI = {
   // Used to keep track of how many calls to storageBusy vs storageReady.
   _storageBusyCount: 0,
 
   // Variable: isDOMWindowClosing
   // Tells wether we already received the "domwindowclosed" event and the parent
   // windows is about to close.
   isDOMWindowClosing: false,
 
+  // Variable: _browserKeys
+  // Used to keep track of allowed browser keys.
+  _browserKeys: null,
+
+  // Variable: ignoreKeypressForSearch
+  // Used to prevent keypress being handled after quitting search mode.
+  ignoreKeypressForSearch: false,
+
   // ----------
   // Function: toString
   // Prints [UI] for debug use
   toString: function UI_toString() {
     return "[UI]";
   },
 
   // ----------
@@ -927,52 +935,137 @@ let UI = {
         cl = item;
         clDist = testDist;
       }
     });
     return cl;
   },
 
   // ----------
+  // Function: _setupBrowserKeys
+  // Sets up the allowed browser keys using key elements.
+  _setupBrowserKeys: function UI__setupKeyWhiteList() {
+    let keys = {};
+
+    [
+#ifdef XP_UNIX
+      "quitApplication",
+#endif
+#ifdef XP_MACOSX
+      "preferencesCmdMac", "minimizeWindow",
+#endif
+      "newNavigator", "newNavigatorTab", "find"
+     ].forEach(function(key) {
+      let element = gWindow.document.getElementById("key_" + key);
+      keys[key] = element.getAttribute("key").toLocaleLowerCase().charCodeAt(0);
+    });
+
+    // for key combinations with shift key, the charCode of upper case letters 
+    // are different to the lower case ones so need to handle them differently.
+    ["closeWindow", "tabview", "undoCloseTab", "undoCloseWindow",
+     "privatebrowsing"].forEach(function(key) {
+      let element = gWindow.document.getElementById("key_" + key);
+      keys[key] = element.getAttribute("key").toLocaleUpperCase().charCodeAt(0);
+    });
+
+    delete this._browserKeys;
+    this._browserKeys = keys;
+  },
+
+  // ----------
   // Function: _setTabViewFrameKeyHandlers
   // Sets up the key handlers for navigating between tabs within the TabView UI.
   _setTabViewFrameKeyHandlers: function UI__setTabViewFrameKeyHandlers() {
-    var self = this;
+    let self = this;
+
+    this._setupBrowserKeys();
 
     iQ(window).keyup(function(event) {
-      if (!event.metaKey) 
+      if (!event.metaKey)
         Keys.meta = false;
     });
 
-    iQ(window).keydown(function(event) {
-      if (event.metaKey) 
+    iQ(window).keypress(function(event) {
+      if (event.metaKey)
         Keys.meta = true;
 
-      if ((iQ(":focus").length > 0 && iQ(":focus")[0].nodeName == "INPUT") || 
-          isSearchEnabled())
+      function processBrowserKeys(evt) {
+#ifdef XP_MACOSX
+        if (evt.metaKey) {
+#else
+        if (evt.ctrlKey) {
+#endif
+          let preventDefault = true;
+          if (evt.shiftKey) {
+            switch (evt.charCode) {
+              case self._browserKeys.privatebrowsing:
+              case self._browserKeys.undoCloseTab:
+              case self._browserKeys.undoCloseWindow:
+              case self._browserKeys.closeWindow:
+                preventDefault = false;
+                break;
+              case self._browserKeys.tabview:
+                self.exit();
+                break;
+            }
+          } else {
+            switch (evt.charCode) {
+              case self._browserKeys.find:
+                self.enableSearch();
+                break;
+              case self._browserKeys.newNavigator:
+              case self._browserKeys.newNavigatorTab:
+                preventDefault = false;
+                break;
+#ifdef XP_UNIX
+              case self._browserKeys.quitApplication:
+                preventDefault = false;
+                break;
+#endif
+#ifdef XP_MACOSX
+              case self._browserKeys.preferencesCmdMac:
+              case self._browserKeys.minimizeWindow:
+                preventDefault = false;
+                break;
+#endif
+            }
+          }
+          if (preventDefault) {
+            evt.stopPropagation();
+            evt.preventDefault();
+          }
+        }
+      }
+      if ((iQ(":focus").length > 0 && iQ(":focus")[0].nodeName == "INPUT") ||
+          isSearchEnabled() || self.ignoreKeypressForSearch) {
+        self.ignoreKeypressForSearch = false;
+        processBrowserKeys(event);
         return;
+      }
 
       function getClosestTabBy(norm) {
         if (!self.getActiveTab())
           return null;
-        var centers =
+        let centers =
           [[item.bounds.center(), item]
              for each(item in TabItems.getItems()) if (!item.parent || !item.parent.hidden)];
-        var myCenter = self.getActiveTab().bounds.center();
-        var matches = centers
+        let myCenter = self.getActiveTab().bounds.center();
+        let matches = centers
           .filter(function(item){return norm(item[0], myCenter)})
           .sort(function(a,b){
             return myCenter.distance(a[0]) - myCenter.distance(b[0]);
           });
         if (matches.length > 0)
           return matches[0][1];
         return null;
       }
 
-      var norm = null;
+      let preventDefault = true;
+      let activeTab;
+      let norm = null;
       switch (event.keyCode) {
         case KeyEvent.DOM_VK_RIGHT:
           norm = function(a, me){return a.x > me.x};
           break;
         case KeyEvent.DOM_VK_LEFT:
           norm = function(a, me){return a.x < me.x};
           break;
         case KeyEvent.DOM_VK_DOWN:
@@ -985,87 +1078,75 @@ let UI = {
 
       if (norm != null) {
         var nextTab = getClosestTabBy(norm);
         if (nextTab) {
           if (nextTab.isStacked && !nextTab.parent.expanded)
             nextTab = nextTab.parent.getChild(0);
           self.setActiveTab(nextTab);
         }
-        event.stopPropagation();
-        event.preventDefault();
-      } else if (event.keyCode == KeyEvent.DOM_VK_ESCAPE) {
-        let activeGroupItem = GroupItems.getActiveGroupItem();
-        if (activeGroupItem && activeGroupItem.expanded)
-          activeGroupItem.collapse();
-        else 
-          self.exit();
-
-        event.stopPropagation();
-        event.preventDefault();
-      } else if (event.keyCode == KeyEvent.DOM_VK_RETURN ||
-                 event.keyCode == KeyEvent.DOM_VK_ENTER) {
-        let activeTab = self.getActiveTab();
-        if (activeTab)
-          activeTab.zoomIn();
+      } else {
+        switch(event.keyCode) {
+          case KeyEvent.DOM_VK_ESCAPE:
+            let activeGroupItem = GroupItems.getActiveGroupItem();
+            if (activeGroupItem && activeGroupItem.expanded)
+              activeGroupItem.collapse();
+            else
+              self.exit();
+            break;
+          case KeyEvent.DOM_VK_RETURN:
+          case KeyEvent.DOM_VK_ENTER:
+            activeTab = self.getActiveTab();
+            if (activeTab)
+              activeTab.zoomIn();
+            break;
+          case KeyEvent.DOM_VK_TAB:
+            // tab/shift + tab to go to the next tab.
+            activeTab = self.getActiveTab();
+            if (activeTab) {
+              let tabItems = (activeTab.parent ? activeTab.parent.getChildren() :
+                              [activeTab]);
+              let length = tabItems.length;
+              let currentIndex = tabItems.indexOf(activeTab);
 
-        event.stopPropagation();
-        event.preventDefault();
-      } else if (event.keyCode == KeyEvent.DOM_VK_TAB) {
-        // tab/shift + tab to go to the next tab.
-        var activeTab = self.getActiveTab();
-        if (activeTab) {
-          var tabItems = (activeTab.parent ? activeTab.parent.getChildren() :
-                          [activeTab]);
-          var length = tabItems.length;
-          var currentIndex = tabItems.indexOf(activeTab);
-
-          if (length > 1) {
-            if (event.shiftKey) {
-              if (currentIndex == 0)
-                newIndex = (length - 1);
-              else
-                newIndex = (currentIndex - 1);
-            } else {
-              if (currentIndex == (length - 1))
-                newIndex = 0;
-              else
-                newIndex = (currentIndex + 1);
+              if (length > 1) {
+                if (event.shiftKey) {
+                  if (currentIndex == 0)
+                    newIndex = (length - 1);
+                  else
+                    newIndex = (currentIndex - 1);
+                } else {
+                  if (currentIndex == (length - 1))
+                    newIndex = 0;
+                  else
+                    newIndex = (currentIndex + 1);
+                }
+                self.setActiveTab(tabItems[newIndex]);
+              }
             }
-            self.setActiveTab(tabItems[newIndex]);
-          }
+            break;
+          default:
+            processBrowserKeys(event);
+            preventDefault = false;
         }
-        event.stopPropagation();
-        event.preventDefault();
-      } else if (event.keyCode == KeyEvent.DOM_VK_SLASH) {
-        // the / event handler for find bar is defined in the findbar.xml
-        // binding.  To keep things in its own module, we handle our slash here.
-        self.enableSearch(event);
-      } else if (event.keyCode == KeyEvent.DOM_VK_BACK_SPACE) {
-        // prevent navigating backward in the selected tab's history
-        event.stopPropagation();
-        event.preventDefault();
+        if (preventDefault) {
+          event.stopPropagation();
+          event.preventDefault();
+        }
       }
     });
   },
 
   // ----------
   // Function: enableSearch
   // Enables the search feature.
-  // Parameters:
-  //   event - the event triggers this action.
-  enableSearch: function UI_enableSearch(event) {
+  enableSearch: function UI_enableSearch() {
     if (!isSearchEnabled()) {
       ensureSearchShown();
       SearchEventHandler.switchToInMode();
-      
-      if (event) {
-        event.stopPropagation();
-        event.preventDefault();
-      }
     }
   },
 
   // ----------
   // Function: _createGroupItemOnDrag
   // Called in response to a mousedown in empty space in the TabView UI;
   // creates a new groupItem based on the user's drag.
   _createGroupItemOnDrag: function UI__createGroupItemOnDrag(e) {
--- a/browser/base/content/test/tabview/Makefile.in
+++ b/browser/base/content/test/tabview/Makefile.in
@@ -45,16 +45,17 @@ include $(topsrcdir)/config/rules.mk
 
 _BROWSER_FILES = \
                  browser_tabview_alltabs.js \
                  browser_tabview_apptabs.js \
                  browser_tabview_bug580412.js \
                  browser_tabview_bug586553.js \
                  browser_tabview_bug587043.js \
                  browser_tabview_bug587231.js \
+                 browser_tabview_bug587276.js \
                  browser_tabview_bug587351.js \
                  browser_tabview_bug587503.js \
                  browser_tabview_bug587990.js \
                  browser_tabview_bug588265.js \
                  browser_tabview_bug589324.js \
                  browser_tabview_bug590606.js \
                  browser_tabview_bug591706.js \
                  browser_tabview_bug594958.js \
@@ -111,16 +112,17 @@ include $(topsrcdir)/config/rules.mk
                  browser_tabview_bug627736.js \
                  browser_tabview_bug628165.js \
                  browser_tabview_bug628270.js \
                  browser_tabview_bug629189.js \
                  browser_tabview_bug629195.js \
                  browser_tabview_bug630102.js \
                  browser_tabview_bug630157.js \
                  browser_tabview_bug631662.js \
+                 browser_tabview_bug631752.js \
                  browser_tabview_bug633788.js \
                  browser_tabview_bug634077.js \
                  browser_tabview_bug634085.js \
                  browser_tabview_bug634158.js \
                  browser_tabview_bug635696.js \
                  browser_tabview_dragdrop.js \
                  browser_tabview_exit_button.js \
                  browser_tabview_expander.js \
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/tabview/browser_tabview_bug587276.js
@@ -0,0 +1,102 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+let contentWindow;
+
+function test() {
+  waitForExplicitFinish();
+
+  showTabView(test1);
+}
+
+function test1() {
+  ok(TabView.isVisible(), "Tab View is visible");
+
+  contentWindow = document.getElementById("tab-view").contentWindow;
+  whenTabViewIsHidden(function() {
+    ok(!TabView.isVisible(), "Tab View is not visible");
+    showTabView(test2);
+  });
+  EventUtils.synthesizeKey("E", { accelKey: true, shiftKey: true }, contentWindow);
+}
+
+function test2() {
+  ok(TabView.isVisible(), "Tab View is visible");
+
+  whenSearchIsEnabled(function() {
+    ok(contentWindow.isSearchEnabled(), "The search is enabled")
+
+    whenSearchIsDisabled(test3);
+    hideSearch();
+  });
+  EventUtils.synthesizeKey("f", { accelKey: true }, contentWindow);
+}
+
+function test3() {
+  ok(!contentWindow.isSearchEnabled(), "The search is disabled")
+
+  is(gBrowser.tabs.length, 1, "There is one tab before cmd/ctrl + t is pressed");
+  EventUtils.synthesizeKey("t", { accelKey: true }, contentWindow);
+  is(gBrowser.tabs.length, 2, "There are two tabs after cmd/ctrl + t is pressed");
+
+  gBrowser.tabs[0].linkedBrowser.loadURI("about:robots");
+  gBrowser.tabs[1].linkedBrowser.loadURI("http://example.com/");
+
+  afterAllTabsLoaded(function () {
+    showTabView(test4);
+  });
+}
+
+function test4() {
+  is(gBrowser.tabs.length, 2, "There are two tabs");
+  
+  let onTabClose = function() {
+    gBrowser.tabContainer.removeEventListener("TabClose", onTabClose, true);
+    executeSoon(function() {
+      is(gBrowser.tabs.length, 1, "There is one tab after removing one");
+
+      EventUtils.synthesizeKey("T", { accelKey: true, shiftKey: true }, contentWindow);
+      is(gBrowser.tabs.length, 2, "There are two tabs after restoring one");
+
+      gBrowser.tabs[0].linkedBrowser.loadURI("about:blank");
+      gBrowser.removeTab(gBrowser.tabs[1]);
+      test8();
+    });
+  };
+  gBrowser.tabContainer.addEventListener("TabClose", onTabClose, true);
+  gBrowser.removeTab(gBrowser.tabs[1]);
+}
+
+// below key combination shouldn't trigger actions in tabview UI
+function test8() {
+  let newTab = gBrowser.loadOneTab("about:blank", { inBackground: true });
+
+  is(gBrowser.tabs.length, 2, "There are two tabs before cmd/ctrl + w is pressed");
+  EventUtils.synthesizeKey("w", { accelKey: true }, contentWindow);
+  is(gBrowser.tabs.length, 2, "There are two tabs after cmd/ctrl + w is pressed");
+
+  gBrowser.removeTab(newTab);
+  test9();
+}
+
+function test9() {
+  let zoomLevel = ZoomManager.zoom;
+  EventUtils.synthesizeKey("+", { accelKey: true }, contentWindow);
+  is(ZoomManager.zoom, zoomLevel, "The zoom level remains unchanged after cmd/ctrl + + is pressed");
+
+  EventUtils.synthesizeKey("-", { accelKey: true }, contentWindow);
+  is(ZoomManager.zoom, zoomLevel, "The zoom level remains unchanged after cmd/ctrl + - is pressed");
+
+  test10();
+}
+
+function test10() {
+  is(gBrowser.tabs.length, 1, "There is one tab before cmd/ctrl + shift + a is pressed");
+  // it would open about:addons on a new tab if it passes through the white list.
+  EventUtils.synthesizeKey("A", { accelKey: true, shiftKey: true }, contentWindow);
+
+  executeSoon(function() {
+    is(gBrowser.tabs.length, 1, "There is still one tab after cmd/ctrl + shift + a is pressed");
+    hideTabView(finish);
+  })
+}
--- a/browser/base/content/test/tabview/browser_tabview_bug594958.js
+++ b/browser/base/content/test/tabview/browser_tabview_bug594958.js
@@ -4,30 +4,16 @@
 function test() {
   let win;
   let cw;
 
   let getGroupItem = function (index) {
     return cw.GroupItems.groupItems[index];
   }
 
-  let newWindow = function (callback) {
-    newWindowWithTabView(function (tvwin) {
-      registerCleanupFunction(function () {
-        if (!tvwin.closed)
-          tvwin.close();
-      });
-
-      win = tvwin;
-      cw = win.TabView.getContentWindow();
-
-      callback();
-    });
-  }
-
   let finishTest = function () {
     win.close();
     finish();
   }
 
   // very long page that produces black bars at the right
   let html1 = '<html><body style="background-color: #00f;">' +
               '<div style="background: #fff; width: 95%; height: 10000px; ' +
@@ -133,20 +119,21 @@ function test() {
 
   let checkPixelColor = function (ctx, url, color, x, y, edge) {
     let data = ctx.getImageData(x, y, 1, 1).data;
     let check = (data[0] == color[0] && data[1] == color[1] && data[2] == color[2]);
     ok(check, url + ': ' + edge + ' edge color matches pixel value');
   }
 
   waitForExplicitFinish();
-  newWindow(next);
+  newWindowWithTabView(function(newWin) {
+    win = newWin;
+
+    registerCleanupFunction(function () {
+      if (!win.closed)
+        win.close();
+    });
+
+    cw = win.TabView.getContentWindow();
+    next();
+  }, null, 800, 600);
 }
 
-// ---------
-function newWindowWithTabView(callback) {
-  let win = window.openDialog(getBrowserURL(), "_blank", 
-                              "chrome,all,dialog=no,height=600,width=800");
-  win.addEventListener("load", function onLoad() {
-    win.removeEventListener("load", onLoad, false);
-    showTabView(function () callback(win), win);
-  }, false);
-}
--- a/browser/base/content/test/tabview/browser_tabview_bug595191.js
+++ b/browser/base/content/test/tabview/browser_tabview_bug595191.js
@@ -52,10 +52,10 @@ function toggleTabViewTest(contentWindow
   let onTabViewHidden = function() {
     contentWindow.removeEventListener("tabviewhidden", onTabViewHidden, false);
 
     ok(!TabView.isVisible(), "Tab View is hidden");
     finish();
   }
   contentWindow.addEventListener("tabviewhidden", onTabViewHidden, false);
   // Use keyboard shortcut to toggle back to browser view
-  EventUtils.synthesizeKey("e", { accelKey: true, shiftKey: true });
+  EventUtils.synthesizeKey("E", { accelKey: true, shiftKey: true });
 }
--- a/browser/base/content/test/tabview/browser_tabview_bug595518.js
+++ b/browser/base/content/test/tabview/browser_tabview_bug595518.js
@@ -23,17 +23,17 @@ function onTabViewWindowLoaded() {
     
     // verify that the exit button no longer has focus
     is(contentWindow.iQ("#exit-button:focus").length, 0, 
        "The exit button doesn't have the focus");
 
     // verify that the keyboard combo works (this is the crux of bug 595518)
     // Prepare the key combo
     window.addEventListener("tabviewshown", onTabViewShown, false);
-    EventUtils.synthesizeKey("e", { accelKey: true, shiftKey: true }, contentWindow);
+    EventUtils.synthesizeKey("E", { accelKey: true, shiftKey: true }, contentWindow);
   }
   
   let onTabViewShown = function() {
     window.removeEventListener("tabviewshown", onTabViewShown, false);
     
     // test if the key combo worked
     ok(TabView.isVisible(), "Tab View is visible");
 
--- a/browser/base/content/test/tabview/browser_tabview_bug595560.js
+++ b/browser/base/content/test/tabview/browser_tabview_bug595560.js
@@ -28,18 +28,18 @@ function onTabViewWindowLoaded() {
   let contentWindow = document.getElementById("tab-view").contentWindow;
   testOne(contentWindow);
 }
 
 function testOne(contentWindow) {
   onSearchEnabledAndDisabled(contentWindow, function() {
     testTwo(contentWindow); 
   });
-  // execute a find command (i.e. press cmd/ctrl F)
-  document.getElementById("cmd_find").doCommand();
+  // press cmd/ctrl F
+  EventUtils.synthesizeKey("f", { accelKey: true });
 }
 
 function testTwo(contentWindow) {
   onSearchEnabledAndDisabled(contentWindow, function() { 
     testThree(contentWindow);
   });
   // press /
   EventUtils.synthesizeKey("VK_SLASH", { type: "keydown" }, contentWindow);
--- a/browser/base/content/test/tabview/browser_tabview_bug595930.js
+++ b/browser/base/content/test/tabview/browser_tabview_bug595930.js
@@ -36,17 +36,17 @@ function onTabViewWindowLoaded() {
       finish();
     };
     window.addEventListener("tabviewhidden", onTabViewHidden, false);
 
     // delay to give time for hidden group DOM element to be removed so
     // the appropriate group would get selected when the key
     // combination is pressed
     executeSoon(function() { 
-      EventUtils.synthesizeKey("e", {accelKey : true, shiftKey: true}, contentWindow);
+      EventUtils.synthesizeKey("E", {accelKey : true, shiftKey: true}, contentWindow);
     });
   });
 
   group1.addSubscriber(group1, "groupHidden", function() {
     group1.removeSubscriber(group1, "groupHidden");
 
     // close undo group
     let closeButton = group1.$undoContainer.find(".close");
--- a/browser/base/content/test/tabview/browser_tabview_bug597248.js
+++ b/browser/base/content/test/tabview/browser_tabview_bug597248.js
@@ -1,31 +1,34 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 let newTabOne;
 let newTabTwo;
-let restoredNewTabOneLoaded = false;
+let newTabThree;
 let restoredNewTabTwoLoaded = false;
+let restoredNewTabThreeLoaded = false;
 let frameInitialized = false;
 
 function test() {
   waitForExplicitFinish();
   newWindowWithTabView(setupOne);
 }
 
 function setupOne(win) {
   win.TabView.firstUseExperienced = true;
 
   win.gBrowser.addTab("http://mochi.test:8888/browser/browser/base/content/test/tabview/search1.html");
   win.gBrowser.addTab("http://mochi.test:8888/browser/browser/base/content/test/tabview/dummy_page.html");
 
   afterAllTabsLoaded(function () setupTwo(win), win);
 }
 
+let restoreWin;
+
 function setupTwo(win) {
   let contentWindow = win.TabView.getContentWindow();
 
   let tabItems = contentWindow.TabItems.getItems();
   is(tabItems.length, 3, "There should be 3 tab items before closing");
 
   let numTabsToSave = tabItems.length;
 
@@ -41,143 +44,110 @@ function setupTwo(win) {
   // after the window is closed, restore it.
   let xulWindowDestory = function() {
     Services.obs.removeObserver(
        xulWindowDestory, "xul-window-destroyed", false);
 
     // "xul-window-destroyed" is just fired just before a XUL window is
     // destroyed so restore window and test it after a delay
     executeSoon(function() {
-      let restoredWin = undoCloseWindow();
+      restoredWin = undoCloseWindow();
       restoredWin.addEventListener("load", function onLoad(event) {
         restoredWin.removeEventListener("load", onLoad, false);
 
+        registerCleanupFunction(function() restoredWin.close());
+
         // ensure that closed tabs have been saved
         is(numTabsToSave, 0, "All tabs were saved when window was closed.");
+        is(restoredWin.gBrowser.tabs.length, 3, "The total number of tabs is 3");
 
-        // execute code when the frame is initialized.
+        // setup tab variables and listen to the tabs load progress
+        newTabOne = restoredWin.gBrowser.tabs[0];
+        newTabTwo = restoredWin.gBrowser.tabs[1];
+        newTabThree = restoredWin.gBrowser.tabs[2];
+        restoredWin.gBrowser.addTabsProgressListener(gTabsProgressListener);
+
+        // execute code when the frame is initialized
         let onTabViewFrameInitialized = function() {
           restoredWin.removeEventListener(
             "tabviewframeinitialized", onTabViewFrameInitialized, false);
 
-          /*
-          // bug 615954 happens too often so we disable this until we have a fix
-          let restoredContentWindow = 
+          let restoredContentWindow =
             restoredWin.document.getElementById("tab-view").contentWindow;
           // prevent TabItems._update being called before checking cached images
           restoredContentWindow.TabItems._pauseUpdateForTest = true;
-          */
-          restoredWin.close();
-          finish();
+
+          let nextStep = function() {
+            // since we are not sure whether the frame is initialized first or two tabs
+            // compete loading first so we need this.
+            if (restoredNewTabTwoLoaded && restoredNewTabThreeLoaded)
+              updateAndCheck();
+            else
+              frameInitialized = true;
+          }
+
+          let tabItems = restoredContentWindow.TabItems.getItems();
+          let count = tabItems.length;
+
+          tabItems.forEach(function(tabItem) {
+            tabItem.addSubscriber(tabItem, "loadedCachedImageData", function() {
+              tabItem.removeSubscriber(tabItem, "loadedCachedImageData");
+              ok(tabItem.isShowingCachedData(),
+                "Tab item is showing cached data and is just connected. " +
+                tabItem.tab.linkedBrowser.currentURI.spec);
+              if (--count == 0)
+                nextStep();
+            });
+          });
         }
+
         restoredWin.addEventListener(
           "tabviewframeinitialized", onTabViewFrameInitialized, false);
-
-        is(restoredWin.gBrowser.tabs.length, 3, "The total number of tabs is 3");
-
-        /*
-        // bug 615954 happens too often so we disable this until we have a fix
-        restoredWin.addEventListener("tabviewshown", onTabViewShown, false);
-
-        // setup tab variables and listen to the load progress.
-        newTabOne = restoredWin.gBrowser.tabs[0];
-        newTabTwo = restoredWin.gBrowser.tabs[1];
-        restoredWin.gBrowser.addTabsProgressListener(gTabsProgressListener);
-        */
       }, false);
     });
   };
-
-  Services.obs.addObserver(
-    xulWindowDestory, "xul-window-destroyed", false);
+  Services.obs.addObserver(xulWindowDestory, "xul-window-destroyed", false);
 
   win.close();
 }
 
-/*let gTabsProgressListener = {
+let gTabsProgressListener = {
   onStateChange: function(browser, webProgress, request, stateFlags, status) {
     // ensure about:blank doesn't trigger the code
     if ((stateFlags & Ci.nsIWebProgressListener.STATE_STOP) &&
         (stateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW) &&
          browser.currentURI.spec != "about:blank") {
-      if (newTabOne.linkedBrowser == browser)
-        restoredNewTabOneLoaded = true;
-      else if (newTabTwo.linkedBrowser == browser)
+      if (newTabTwo.linkedBrowser == browser)
         restoredNewTabTwoLoaded = true;
+      else if (newTabThree.linkedBrowser == browser)
+        restoredNewTabThreeLoaded = true;
 
       // since we are not sure whether the frame is initialized first or two tabs
       // compete loading first so we need this.
-      if (restoredNewTabOneLoaded && restoredNewTabTwoLoaded) {
+      if (restoredNewTabTwoLoaded && restoredNewTabThreeLoaded) {
         restoredWin.gBrowser.removeTabsProgressListener(gTabsProgressListener);
 
-        if (frameInitialized) {
-          // since a tabs progress listener is used in the code to set 
-          // tabItem.shouldHideCachedData, executeSoon is used to avoid a racing
-          // condition.
-          executeSoon(updateAndCheck); 
-        }
+        if (frameInitialized)
+          updateAndCheck();
       }
     }
   }
 };
 
-function onTabViewShown() {
-  restoredWin.removeEventListener("tabviewshown", onTabViewShown, false);
-
-  let contentWindow = 
-    restoredWin.document.getElementById("tab-view").contentWindow;
-
-  let nextStep = function() {
-    // since we are not sure whether the frame is initialized first or two tabs
-    // compete loading first so we need this.
-    if (restoredNewTabOneLoaded && restoredNewTabTwoLoaded) {
-      // executeSoon is used to ensure tabItem.shouldHideCachedData is set
-      // because tabs progress listener might run at the same time as this test 
-      // code.
-      executeSoon(updateAndCheck);
-    } else {
-      frameInitialized = true;
-    }
-  }
-
-  let tabItems = contentWindow.TabItems.getItems();
-  let count = tabItems.length;
-  tabItems.forEach(function(tabItem) {
-    // tabitem might not be connected so use subscriber for those which are not
-    // connected.
-    if (tabItem._reconnected) {
-      ok(tabItem.isShowingCachedData(), 
-         "Tab item is showing cached data and is already connected. " +
-         tabItem.tab.linkedBrowser.currentURI.spec);
-      if (--count == 0)
-        nextStep();
-    } else {
-      tabItem.addSubscriber(tabItem, "reconnected", function() {
-        tabItem.removeSubscriber(tabItem, "reconnected");
-        ok(tabItem.isShowingCachedData(), 
-           "Tab item is showing cached data and is just connected. "  +
-           tabItem.tab.linkedBrowser.currentURI.spec);
-        if (--count == 0)
-          nextStep();
-      });
-    }
-  });
-}
-
 function updateAndCheck() {
   // force all canvas to update
   let contentWindow = 
     restoredWin.document.getElementById("tab-view").contentWindow;
 
   contentWindow.TabItems._pauseUpdateForTest = false;
 
   let tabItems = contentWindow.TabItems.getItems();
   tabItems.forEach(function(tabItem) {
     contentWindow.TabItems._update(tabItem.tab);
-    ok(!tabItem.isShowingCachedData(), 
+    ok(!tabItem.isShowingCachedData(),
       "Tab item is not showing cached data anymore. " +
       tabItem.tab.linkedBrowser.currentURI.spec);
   });
 
   // clean up and finish
   restoredWin.close();
   finish();
-}*/
+}
--- a/browser/base/content/test/tabview/browser_tabview_bug597980.js
+++ b/browser/base/content/test/tabview/browser_tabview_bug597980.js
@@ -73,13 +73,13 @@ function part2(win) {
 
     win.addEventListener("tabviewhidden", function () {
       win.removeEventListener("tabviewhidden", arguments.callee, false);
       is(win.gBrowser.selectedTab, newTab, "The seleted tab should be the same as before (new tab)");
        win.close();
        finish();
     }, false);
     // show tabview
-    EventUtils.synthesizeKey("e", { accelKey: true, shiftKey: true }, win);
+    EventUtils.synthesizeKey("E", { accelKey: true, shiftKey: true }, win);
     // hide tabview
-    EventUtils.synthesizeKey("e", { accelKey: true, shiftKey: true }, win);
+    EventUtils.synthesizeKey("E", { accelKey: true, shiftKey: true }, win);
   })
 }
--- a/browser/base/content/test/tabview/browser_tabview_bug618828.js
+++ b/browser/base/content/test/tabview/browser_tabview_bug618828.js
@@ -71,24 +71,8 @@ function onTabViewWindowLoaded(win, tab)
   }
 
   enableSearch();
   assertSearchIsEnabled();
 
   testClickOnSearchBox();
   testClickOnOtherSearchResult();
 }
-
-// ---------
-function newWindowWithTabView(callback) {
-  let win = window.openDialog(getBrowserURL(), "_blank", 
-                              "chrome,all,dialog=no,height=800,width=800");
-  let onLoad = function() {
-    win.removeEventListener("load", onLoad, false);
-    let onShown = function() {
-      win.removeEventListener("tabviewshown", onShown, false);
-      callback(win);
-    };
-    win.addEventListener("tabviewshown", onShown, false);
-    win.TabView.toggle();
-  }
-  win.addEventListener("load", onLoad, false);
-}
--- a/browser/base/content/test/tabview/browser_tabview_bug626791.js
+++ b/browser/base/content/test/tabview/browser_tabview_bug626791.js
@@ -37,43 +37,16 @@ function test() {
     if (-1 < pos) {
       currentSet.splice(pos, 1);
       toolbar.setAttribute("currentset", currentSet.join(","));
       toolbar.currentSet = currentSet.join(",");
       win.document.persist(toolbar.id, "currentset");
     }
   }
 
-  let newWindowWithTabView = function (callback) {
-    win = window.openDialog(getBrowserURL(), "_blank", 
-                            "chrome,all,dialog=no,height=800,width=800");
-    let onLoad = function() {
-      win.removeEventListener("load", onLoad, false);
-
-      removeToolbarButton();
-      TabView.firstUseExperienced = false;
-      TabView.init();
-
-      let onShown = function() {
-        win.removeEventListener("tabviewshown", onShown, false);
-
-        cw = win.TabView.getContentWindow();
-        let groupItem = cw.GroupItems.groupItems[0];
-        groupItem.setSize(200, 200, true);
-        groupItem.setUserSize();
-
-        callback();
-      };
-
-      win.addEventListener("tabviewshown", onShown, false);
-      win.TabView.toggle();
-    }
-    win.addEventListener("load", onLoad, false);
-  }
-
   let testNameGroup = function () {
     prefix = 'name-group';
     assertToolbarButtonNotExists();
     let groupItem = cw.GroupItems.groupItems[0];
 
     groupItem.setTitle('title');
     assertToolbarButtonNotExists();
     groupItem.setTitle('');
@@ -170,20 +143,33 @@ function test() {
     if (win)
       win.close();
 
     if (!test) {
       finish();
       return;
     }
 
-    newWindowWithTabView(function () {
-      assertToolbarButtonNotExists();
-      test();
-    });
+    newWindowWithTabView(
+      function (newWin) {
+        cw = win.TabView.getContentWindow();
+        let groupItem = cw.GroupItems.groupItems[0];
+        groupItem.setSize(200, 200, true);
+        groupItem.setUserSize();
+
+        assertToolbarButtonNotExists();
+        test();
+      },
+      function(newWin) {
+        win = newWin;
+        removeToolbarButton();
+        TabView.firstUseExperienced = false;
+        TabView.init();
+      }
+    );
   }
 
   waitForExplicitFinish();
 
   registerCleanupFunction(function () {
     if (win && !win.closed)
       win.close();
   });
--- a/browser/base/content/test/tabview/browser_tabview_bug629195.js
+++ b/browser/base/content/test/tabview/browser_tabview_bug629195.js
@@ -2,54 +2,42 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 let win;
 let contentWindow;
 let originalTab;
 
 function test() {
   waitForExplicitFinish();
-  setup();
-}
 
-function setup() {
-  win = window.openDialog(getBrowserURL(), "_blank", 
-                          "chrome,all,dialog=no,height=800,width=800");
-
-  let onLoad = function() {
-    win.removeEventListener("load", onLoad, false);
-
-    originalTab = win.gBrowser.visibleTabs[0];
-    win.gBrowser.addTab();
-    win.gBrowser.pinTab(originalTab);
-
-    let onTabViewShown = function() {
-      win.removeEventListener("tabviewshown", onTabViewShown, false);
-
+  newWindowWithTabView(
+    function() {
       ok(win.TabView.isVisible(), "Tab View is visible");
 
       contentWindow = win.document.getElementById("tab-view").contentWindow;
       is(contentWindow.GroupItems.groupItems.length, 1, "There is one group");
-      is(contentWindow.GroupItems.groupItems[0].getChildren().length, 1, 
+      is(contentWindow.GroupItems.groupItems[0].getChildren().length, 1,
          "The group has only one tab item");
 
       // show the undo close group button
       let group = contentWindow.GroupItems.groupItems[0];
       group.closeAll();
 
       group.addSubscriber(group, "groupHidden", function() {
         group.removeSubscriber(group, "groupHidden");
         restore(group.id);
       });
-
-    };
-    win.addEventListener("tabviewshown", onTabViewShown, false);
-    win.TabView.toggle();
-  }
-  win.addEventListener("load", onLoad, false);
+    },
+    function(newWin) {
+      win = newWin;
+      originalTab = win.gBrowser.visibleTabs[0];
+      win.gBrowser.addTab();
+      win.gBrowser.pinTab(originalTab);
+    }
+  );
 }
 
 function restore(groupId) {
   // window state ready
   let handleSSWindowStateReady = function() {
     win.removeEventListener("SSWindowStateReady", handleSSWindowStateReady, false);
 
     executeSoon(function() { 
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/tabview/browser_tabview_bug631752.js
@@ -0,0 +1,77 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+function test() {
+  let cw;
+  let groupItem;
+
+  let getTabItemAspect = function (tabItem) {
+    let bounds = cw.iQ('.thumb', tabItem.container).bounds();
+    let padding = cw.TabItems.tabItemPadding;
+    return (bounds.height + padding.y) / (bounds.width + padding.x);
+  }
+
+  let getAspectRange = function () {
+    let aspect = cw.TabItems.tabAspect;
+    let variance = aspect / 100 * 1.5;
+    return new cw.Range(aspect - variance, aspect + variance);
+  }
+
+  let dragTabItem = function (tabItem) {
+    let doc = cw.document.documentElement;
+    let tabItem = groupItem.getChild(0);
+    let container = tabItem.container;
+    let aspectRange = getAspectRange();
+
+    EventUtils.synthesizeMouseAtCenter(container, {type: "mousedown"}, cw);
+    for (let x = 200; x <= 400; x += 100)
+      EventUtils.synthesizeMouse(doc, x, 100, {type: "mousemove"}, cw);
+    ok(aspectRange.contains(getTabItemAspect(tabItem)), "tabItem's aspect is correct");
+
+    ok(!groupItem.getBounds().intersects(tabItem.getBounds()), "tabItem was moved out of group bounds");
+    ok(!tabItem.parent, "tabItem is orphaned");
+
+    EventUtils.synthesizeMouseAtCenter(container, {type: "mouseup"}, cw);
+    ok(aspectRange.contains(getTabItemAspect(tabItem)), "tabItem's aspect is correct");
+
+    tabItem.close();
+  }
+
+  let testDragOutOfStackedGroup = function () {
+    dragTabItem();
+    testDragOutOfExpandedStackedGroup();
+  }
+
+  let testDragOutOfExpandedStackedGroup = function () {
+    groupItem.addSubscriber(groupItem, "expanded", function () {
+      groupItem.removeSubscriber(groupItem, "expanded");
+
+      dragTabItem();
+      closeGroupItem(groupItem, function () hideTabView(finishTest));
+    });
+
+    groupItem.expand();
+  }
+
+  let finishTest = function () {
+    is(cw.GroupItems.groupItems.length, 1, "there is one groupItem");
+    is(gBrowser.tabs.length, 1, "there is one tab");
+    ok(!TabView.isVisible(), "tabview is hidden");
+
+    finish();
+  }
+
+  waitForExplicitFinish();
+
+  newWindowWithTabView(function (win) {
+    registerCleanupFunction(function () {
+      if (!win.closed)
+        win.close();
+    });
+
+    cw = win.TabView.getContentWindow();
+    groupItem = createGroupItemWithBlankTabs(win, 200, 200, 10, 10);
+
+    testDragOutOfStackedGroup();
+  });
+}
--- a/browser/base/content/test/tabview/browser_tabview_bug634158.js
+++ b/browser/base/content/test/tabview/browser_tabview_bug634158.js
@@ -1,40 +1,31 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 function test() {
-  let createOrphan = function (callback) {
-    let tab = gBrowser.loadOneTab('about:blank', {inBackground: true});
-
-    registerCleanupFunction(function () {
-      gBrowser.removeTab(tab);
-    });
-
-    afterAllTabsLoaded(function () {
-      let tabItem = tab._tabViewTabItem;
-      tabItem.parent.remove(tabItem);
-      callback(tabItem);
-    });
-  }
-
   waitForExplicitFinish();
 
-  showTabView(function () {
-    registerCleanupFunction(function () TabView.hide());
+  newWindowWithTabView(function (win) {
+    registerCleanupFunction(function () {
+      if (!win.closed)
+        win.close();
+    });
 
-    createOrphan(function (tabItem) {
-      let cw = TabView.getContentWindow();
-      let container = cw.iQ(tabItem.container);
-      let expander = cw.iQ('.expander', container);
+    let tabItem = win.gBrowser.tabs[0]._tabViewTabItem;
+    tabItem.parent.remove(tabItem);
 
-      let bounds = container.bounds();
-      let halfWidth = bounds.width / 2;
-      let halfHeight = bounds.height / 2;
+    let cw = win.TabView.getContentWindow();
+    let container = cw.iQ(tabItem.container);
+    let expander = cw.iQ('.expander', container);
 
-      let rect = new cw.Rect(bounds.left + halfWidth, bounds.top + halfHeight,
-                          halfWidth, halfHeight);
-      ok(rect.contains(expander.bounds()), "expander is in the tabItem's bottom-right corner");
+    let bounds = container.bounds();
+    let halfWidth = bounds.width / 2;
+    let halfHeight = bounds.height / 2;
 
-      hideTabView(finish);
-    });
+    let rect = new cw.Rect(bounds.left + halfWidth, bounds.top + halfHeight,
+                        halfWidth, halfHeight);
+    ok(rect.contains(expander.bounds()), "expander is in the tabItem's bottom-right corner");
+
+    win.close();
+    finish();
   });
 }
--- a/browser/base/content/test/tabview/browser_tabview_launch.js
+++ b/browser/base/content/test/tabview/browser_tabview_launch.js
@@ -1,52 +1,67 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 let tabViewShownCount = 0;
+let timerId;
 
 // ----------
 function test() {
   waitForExplicitFinish();
 
   // verify initial state
   ok(!TabView.isVisible(), "Tab View starts hidden");
 
   // launch tab view for the first time
   window.addEventListener("tabviewshown", onTabViewLoadedAndShown, false);
-  let tabViewCommand = document.getElementById("Browser:ToggleTabView");
-  tabViewCommand.doCommand();
+  TabView.toggle();
+
+  registerCleanupFunction(function () {
+    window.removeEventListener("tabviewshown", onTabViewLoadedAndShown, false);
+    if (timerId) 
+      clearTimeout(timerId);
+    TabView.hide()
+  });
 }
 
 // ----------
 function onTabViewLoadedAndShown() {
   window.removeEventListener("tabviewshown", onTabViewLoadedAndShown, false);
 
   // Evidently sometimes isVisible (which is based on the selectedIndex of the
   // tabview deck) isn't updated immediately when called from button.doCommand,
   // so we add a little timeout here to get outside of the doCommand call.
   // If the initial timeout isn't enough, we keep waiting in case it's taking
   // longer than expected.
   // See bug 594909.
   let deck = document.getElementById("tab-view-deck");
+  let iframe = document.getElementById("tab-view");
+  ok(iframe, "The tab view iframe exists");
+  
   function waitForSwitch() {
-    if (deck.selectedIndex == 1) {
+    if (deck.selectedPanel == iframe) {
       ok(TabView.isVisible(), "Tab View is visible. Count: " + tabViewShownCount);
       tabViewShownCount++;
 
       // kick off the series
       window.addEventListener("tabviewshown", onTabViewShown, false);
       window.addEventListener("tabviewhidden", onTabViewHidden, false);
+
+      registerCleanupFunction(function () {
+        window.removeEventListener("tabviewshown", onTabViewShown, false);
+        window.removeEventListener("tabviewhidden", onTabViewHidden, false);
+      });
       TabView.toggle();
     } else {
-      setTimeout(waitForSwitch, 10);
+      timerId = setTimeout(waitForSwitch, 10);
     }
   }
 
-  setTimeout(waitForSwitch, 1);
+  timerId = setTimeout(waitForSwitch, 1);
 }
 
 // ----------
 function onTabViewShown() {
   // add the count to the message so we can track things more easily.
   ok(TabView.isVisible(), "Tab View is visible. Count: " + tabViewShownCount);
   tabViewShownCount++;
   TabView.toggle();
@@ -54,15 +69,15 @@ function onTabViewShown() {
 
 // ----------
 function onTabViewHidden() {
   ok(!TabView.isVisible(), "Tab View is hidden. Count: " + tabViewShownCount);
 
   if (tabViewShownCount == 1) {
     document.getElementById("menu_tabview").doCommand();
   } else if (tabViewShownCount == 2) {
-    EventUtils.synthesizeKey("e", { accelKey: true, shiftKey: true });
+    EventUtils.synthesizeKey("E", { accelKey: true, shiftKey: true });
   } else if (tabViewShownCount == 3) {
     window.removeEventListener("tabviewshown", onTabViewShown, false);
     window.removeEventListener("tabviewhidden", onTabViewHidden, false);
     finish();
   }
 }
--- a/browser/base/content/test/tabview/browser_tabview_orphaned_tabs.js
+++ b/browser/base/content/test/tabview/browser_tabview_orphaned_tabs.js
@@ -2,28 +2,20 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 let tabOne;
 let newWin;
 
 function test() {
   waitForExplicitFinish();
 
-  newWin = 
-    window.openDialog(getBrowserURL(), "_blank", "all,dialog=no", "about:blank");
-
-  let onLoad = function() {
-    newWin.removeEventListener("load", onLoad, false);
-
+  newWindowWithTabView(onTabViewWindowLoaded, function(win) {
+    newWin = win;
     tabOne = newWin.gBrowser.addTab();
-
-    newWin.addEventListener("tabviewshown", onTabViewWindowLoaded, false);
-    newWin.TabView.toggle();
-  }
-  newWin.addEventListener("load", onLoad, false);
+  });
 }
 
 function onTabViewWindowLoaded() {
   newWin.removeEventListener("tabviewshown", onTabViewWindowLoaded, false);
 
   ok(newWin.TabView.isVisible(), "Tab View is visible");
 
   let contentWindow = newWin.document.getElementById("tab-view").contentWindow;
@@ -39,17 +31,17 @@ function onTabViewWindowLoaded() {
     newWin.removeEventListener("tabviewhidden", onTabViewHidden, false);
 
     // 3) the new group item should have one child and no orphan tab
     is(groupItem.getChildren().length, 1, "The group item has an item");
     is(contentWindow.GroupItems.getOrphanedTabs().length, 0, "No orphaned tabs");
     
     let checkAndFinish = function() {
       // 4) check existence of stored group data for tab before finishing
-      let tabData = contentWindow.Storage.getTabData(tabItem.tab);
+      let tabData = contentWindow.Storage.getTabData(tabItem.tab, function () {});
       ok(tabData && contentWindow.TabItems.storageSanity(tabData) && tabData.groupID, 
          "Tab two has stored group data");
 
       // clean up and finish the test
       newWin.gBrowser.removeTab(tabOne);
       newWin.gBrowser.removeTab(tabItem.tab);
       whenWindowObservesOnce(newWin, "domwindowclosed", function() {
         finish();
--- a/browser/base/content/test/tabview/browser_tabview_snapping.js
+++ b/browser/base/content/test/tabview/browser_tabview_snapping.js
@@ -1,20 +1,18 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 function test() {
   waitForExplicitFinish();
 
-	newWindowWithTabView(onTabViewWindowLoaded);
+  newWindowWithTabView(onTabViewWindowLoaded, null, 1000, 800);
 }
 
 function onTabViewWindowLoaded(win) {
-  win.removeEventListener("tabviewshown", onTabViewWindowLoaded, false);
-
   let contentWindow = win.document.getElementById("tab-view").contentWindow;
   let [originalTab] = win.gBrowser.visibleTabs;
 
   ok(win.TabView.isVisible(), "Tab View is visible");
   is(contentWindow.GroupItems.groupItems.length, 1, "There is only one group");
   let currentActiveGroup = contentWindow.GroupItems.getActiveGroupItem();
 
   // Create a group
@@ -44,17 +42,17 @@ function onTabViewWindowLoaded(win) {
   let endGame = function() {
     contentWindow.UI.DBLCLICK_INTERVAL = origDBlClickInterval;
 
     firstGroup.container.parentNode.removeChild(firstGroup.container);
     firstGroup.close();
     thirdGroup.container.parentNode.removeChild(thirdGroup.container);
     thirdGroup.close();
 
-		win.close();
+    win.close();
     ok(win.closed, "new window is closed");
     finish();
   }
   
   let continueWithPart2 = function() {
     
     ok(firstGroup.getBounds().equals(firstBox), "The first group should still have its bounds");
     
@@ -159,23 +157,8 @@ function checkSnap(item, offsetX, offset
     if (item.getBounds().left != firstLeft + offsetX)
       snapped = true;
     callback(snapped);
   };
   item.container.addEventListener('drop', onDrop, false);
   simulateDragDrop(item, offsetX, offsetY, contentWindow);
 }
 
-function newWindowWithTabView(callback) {
-  let charsetArg = "charset=" + window.content.document.characterSet;
-  let win = window.openDialog(getBrowserURL(), "_blank", "chrome,all,dialog=no,height=800,width=1000",
-                              "about:blank", charsetArg, null, null, true);
-  let onLoad = function() {
-    win.removeEventListener("load", onLoad, false);
-    let onShown = function() {
-      win.removeEventListener("tabviewshown", onShown, false);
-      callback(win);
-    };
-    win.addEventListener("tabviewshown", onShown, false);
-    win.TabView.toggle();
-  }
-  win.addEventListener("load", onLoad, false);
-}
\ No newline at end of file
--- a/browser/base/content/test/tabview/head.js
+++ b/browser/base/content/test/tabview/head.js
@@ -65,24 +65,30 @@ function afterAllTabItemsUpdated(callbac
     let tabItem = win.gBrowser.tabs[a]._tabViewTabItem;
     if (tabItem)
       tabItems._update(win.gBrowser.tabs[a]);
   }
   callback();
 }
 
 // ---------
-function newWindowWithTabView(callback) {
-  let win = window.openDialog(getBrowserURL(), "_blank", 
-                              "chrome,all,dialog=no,height=800,width=800");
+function newWindowWithTabView(shownCallback, loadCallback, width, height) {
+  let winWidth = width || 800;
+  let winHeight = height || 800;
+  let win = window.openDialog(getBrowserURL(), "_blank",
+                              "chrome,all,dialog=no,height=" + winHeight +
+                              ",width=" + winWidth);
   let onLoad = function() {
     win.removeEventListener("load", onLoad, false);
+    if (typeof loadCallback == "function")
+      loadCallback(win);
+
     let onShown = function() {
       win.removeEventListener("tabviewshown", onShown, false);
-      callback(win);
+      shownCallback(win);
     };
     win.addEventListener("tabviewshown", onShown, false);
     win.TabView.toggle();
   }
   win.addEventListener("load", onLoad, false);
 }
 
 // ----------
@@ -162,16 +168,77 @@ function whenTabViewIsShown(callback, wi
 
   win.addEventListener('tabviewshown', function () {
     win.removeEventListener('tabviewshown', arguments.callee, false);
     callback();
   }, false);
 }
 
 // ----------
+function showSearch(callback, win) {
+  win = win || window;
+
+  let contentWindow = win.document.getElementById("tab-view").contentWindow;
+  if (contentWindow.isSearchEnabled()) {
+    callback();
+    return;
+  }
+
+  whenSearchIsEnabled(callback, win);
+  contentWindow.performSearch();
+}
+
+// ----------
+function hideSearch(callback, win) {
+  win = win || window;
+
+  let contentWindow = win.document.getElementById("tab-view").contentWindow;
+  if (!contentWindow.isSearchEnabled()) {
+    callback();
+    return;
+  }
+
+  whenSearchIsDisabled(callback, win);
+  contentWindow.hideSearch();
+}
+
+// ----------
+function whenSearchIsEnabled(callback, win) {
+  win = win || window;
+
+  let contentWindow = win.document.getElementById("tab-view").contentWindow;
+  if (contentWindow.isSearchEnabled()) {
+    callback();
+    return;
+  }
+
+  contentWindow.addEventListener("tabviewsearchenabled", function () {
+    contentWindow.removeEventListener("tabviewsearchenabled", arguments.callee, false);
+    callback();
+  }, false);
+}
+
+// ----------
+function whenSearchIsDisabled(callback, win) {
+  win = win || window;
+
+  let contentWindow = win.document.getElementById("tab-view").contentWindow;
+  if (!contentWindow.isSearchEnabled()) {
+    callback();
+    return;
+  }
+
+  contentWindow.addEventListener("tabviewsearchdisabled", function () {
+    contentWindow.removeEventListener("tabviewsearchdisabled", arguments.callee, false);
+    callback();
+  }, false);
+}
+
+
+// ----------
 function hideGroupItem(groupItem, callback) {
   if (groupItem.hidden) {
     callback();
     return;
   }
 
   groupItem.addSubscriber(groupItem, "groupHidden", function () {
     groupItem.removeSubscriber(groupItem, "groupHidden");
--- a/browser/components/nsBrowserContentHandler.js
+++ b/browser/components/nsBrowserContentHandler.js
@@ -562,17 +562,25 @@ nsBrowserContentHandler.prototype = {
 
         searchParam = param.substr(2);
         doSearch(searchParam, cmdLine);
       }
     }
 #endif
   },
 
-  helpInfo : "  -browser           Open a browser window.\n",
+  helpInfo : "  -browser           Open a browser window.\n" +
+             "  -new-window  <url> Open <url> in a new window.\n" +
+             "  -new-tab     <url> Open <url> in a new tab.\n" +
+#ifdef XP_WIN
+             "  -preferences       Open Options dialog.\n" +
+#else
+             "  -preferences       Open Preferences dialog.\n" +
+#endif
+             "  -search     <term> Search <term> with your default search engine.\n",
 
   /* nsIBrowserHandler */
 
   get defaultArgs() {
     var prefb = Components.classes["@mozilla.org/preferences-service;1"]
                           .getService(nsIPrefBranch);
 
     var overridePage = "";
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -1461,21 +1461,16 @@ ContentPermissionPrompt.prototype = {
     var mainAction = {
       label: browserBundle.GetStringFromName("geolocation.shareLocation"),
       accessKey: browserBundle.GetStringFromName("geolocation.shareLocation.accesskey"),
       callback: function(notification) {
         request.allow();
       },
     };
 
-    // XXX Bug 573536
-    // browserBundle.GetStringFromName("geolocation.learnMore")
-    //var formatter = Cc["@mozilla.org/toolkit/URLFormatterService;1"].getService(Ci.nsIURLFormatter);
-    //link.href = formatter.formatURLPref("browser.geolocation.warning.infoURL");
-
     var message;
     var secondaryActions = [];
 
     // Different message/options if it is a local file
     if (requestingURI.schemeIs("file")) {
       message = browserBundle.formatStringFromName("geolocation.fileWantsToKnow",
                                                    [requestingURI.path], 1);
     } else {
--- a/browser/components/privatebrowsing/src/nsPrivateBrowsingService.js
+++ b/browser/components/privatebrowsing/src/nsPrivateBrowsingService.js
@@ -651,20 +651,20 @@ PrivateBrowsingService.prototype = {
       //       invoke the cost of creating a URI for each download entry and
       //       ensure that the hostname matches.
       let stmt = db.createStatement(
         "DELETE FROM moz_downloads " +
         "WHERE source LIKE ?1 ESCAPE '/' " +
         "AND state NOT IN (?2, ?3, ?4)"
       );
       let pattern = stmt.escapeStringForLIKE(aDomain, "/");
-      stmt.bindStringParameter(0, "%" + pattern + "%");
-      stmt.bindInt32Parameter(1, Ci.nsIDownloadManager.DOWNLOAD_DOWNLOADING);
-      stmt.bindInt32Parameter(2, Ci.nsIDownloadManager.DOWNLOAD_PAUSED);
-      stmt.bindInt32Parameter(3, Ci.nsIDownloadManager.DOWNLOAD_QUEUED);
+      stmt.bindByIndex(0, "%" + pattern + "%");
+      stmt.bindByIndex(1, Ci.nsIDownloadManager.DOWNLOAD_DOWNLOADING);
+      stmt.bindByIndex(2, Ci.nsIDownloadManager.DOWNLOAD_PAUSED);
+      stmt.bindByIndex(3, Ci.nsIDownloadManager.DOWNLOAD_QUEUED);
       try {
         stmt.execute();
       }
       finally {
         stmt.finalize();
       }
 
       // We want to rebuild the list if the UI is showing, so dispatch the
@@ -714,17 +714,17 @@ PrivateBrowsingService.prototype = {
       // First we need to get the list of "groups" which are really just domains
       let names = [];
       let stmt = db.createStatement(
         "SELECT name " +
         "FROM groups " +
         "WHERE name LIKE ?1 ESCAPE '/'"
       );
       let pattern = stmt.escapeStringForLIKE(aDomain, "/");
-      stmt.bindStringParameter(0, "%" + pattern);
+      stmt.bindByIndex(0, "%" + pattern);
       try {
         while (stmt.executeStep())
           if (stmt.getString(0).hasRootDomain(aDomain))
             names.push(stmt.getString(0));
       }
       finally {
         stmt.finalize();
       }
--- a/browser/components/search/content/search.xml
+++ b/browser/components/search/content/search.xml
@@ -469,18 +469,22 @@
           else {
             var newTabPref = textBox._prefBranch.getBoolPref("browser.search.openintab");
             if ((aEvent && aEvent.altKey) ^ newTabPref)
               where = "tab";
           }
 
           // Save the current value in the form history
           if (textValue) {
-            textBox._formHistSvc.addEntry(textBox.getAttribute("autocompletesearchparam"),
-                                          textValue);
+            try {
+              textBox._formHistSvc.addEntry(textBox.getAttribute("autocompletesearchparam"),
+                                            textValue);
+            } catch (ex) {
+              Components.utils.reportError("Saving search to form history failed: " + ex);
+            }
           }
 
           this.doSearch(textValue, where);
         ]]></body>
       </method>
 
       <method name="doSearch">
         <parameter name="aData"/>
--- a/browser/installer/windows/nsis/defines.nsi.in
+++ b/browser/installer/windows/nsis/defines.nsi.in
@@ -3,17 +3,22 @@
 # Win7: AppVendor, AppName, and AppVersion must match the application.ini values
 # of Vendor, Name, and Version. These values are used in registering shortcuts
 # with the taskbar. ExplicitAppUserModelID registration when the app launches is
 # handled in widget/src/windows/WinTaskbar.cpp.
 
 !define AppVendor             "Mozilla"
 !define AppName               "Firefox"
 !define AppVersion            "@APP_VERSION@"
+#ifdef HAVE_64BIT_OS
+; differentiate 64-bit builds, we do the same in widget.
+!define AppUserModelID        "${AppVendor}.${AppName}.${AppVersion}.Win64"
+#else
 !define AppUserModelID        "${AppVendor}.${AppName}.${AppVersion}"
+#endif
 !define GREVersion            @MOZILLA_VERSION@
 !define AB_CD                 "@AB_CD@"
 
 !define FileMainEXE           "@MOZ_APP_NAME@.exe"
 !define WindowClass           "FirefoxMessageWindow"
 !define DDEApplication        "Firefox"
 !define AppRegName            "Firefox"
 
--- a/browser/locales/en-US/chrome/browser/browser.properties
+++ b/browser/locales/en-US/chrome/browser/browser.properties
@@ -1,11 +1,9 @@
-nv_done=Done
 nv_timeout=Timed Out
-nv_stopped=Stopped
 openFile=Open File
 
 droponhometitle=Set Home Page
 droponhomemsg=Do you want this document to be your new home page?
 
 # context menu strings
 
 # LOCALIZATION NOTE (contextMenuSearchText): %1$S is the search engine,
--- a/browser/themes/winstripe/browser/browser.css
+++ b/browser/themes/winstripe/browser/browser.css
@@ -51,17 +51,17 @@
 %include ../../browserShared.inc
 %filter substitution
 %define toolbarHighlight rgba(255,255,255,.5)
 %define selectedTabHighlight rgba(255,255,255,.7)
 %define toolbarShadowColor rgba(10%,10%,10%,.4)
 %define toolbarShadowOnTab -moz-linear-gradient(bottom, rgba(10%,10%,10%,.4) 1px, transparent 1px)
 %define bgTabTexture -moz-linear-gradient(transparent, hsla(0,0%,45%,.1) 1px, hsla(0,0%,32%,.2) 80%, hsla(0,0%,0%,.2))
 %define bgTabTextureHover -moz-linear-gradient(hsla(0,0%,100%,.3) 1px, hsla(0,0%,75%,.2) 80%, hsla(0,0%,60%,.2))
-%define navbarTextboxCustomBorder border-color: rgba(0,0,0,.25) rgba(0,0,0,.32) rgba(0,0,0,.37);
+%define navbarTextboxCustomBorder border-color: rgba(0,0,0,.32);
 
 #menubar-items {
   -moz-box-orient: vertical; /* for flex hack */
 }
 
 #main-menubar {
   -moz-box-flex: 1; /* make menu items expand to fill toolbar height */
 }
@@ -600,22 +600,21 @@ menuitem.bookmark-item {
 #nav-bar .toolbarbutton-1 > .toolbarbutton-menubutton-dropmarker,
 #nav-bar .toolbarbutton-1 {
   -moz-appearance: none;
   padding: 1px 5px;
   background: rgba(151,152,153,.05)
               -moz-linear-gradient(rgba(251,252,253,.95), rgba(246,247,248,.47) 49%, 
                                    rgba(231,232,233,.45) 51%, rgba(225,226,229,.3));
   background-clip: padding-box;
-  border-radius: 4.5px;
+  border-radius: 3.5px;
   border: 1px solid;
   border-color: rgba(0,0,0,.12) rgba(0,0,0,.19) rgba(0,0,0,.38);
   box-shadow: 0 0 0 1px rgba(255,255,255,.3) inset,
-              0 0 0 2px rgba(255,255,255,.1) inset,
-              0 1px 0 rgba(0,0,0,.15);
+              0 0 0 2px rgba(255,255,255,.1) inset;
   color: black;
   text-shadow: 0 0 2px white;
 }
 
 #nav-bar .toolbarbutton-1 > .toolbarbutton-menubutton-dropmarker,
 #navigator-toolbox[iconsize="small"][mode="icons"] > #nav-bar .toolbarbutton-1 > .toolbarbutton-menubutton-button,
 #navigator-toolbox[iconsize="small"][mode="icons"] > #nav-bar .toolbarbutton-1 {
   padding-left: 3px;
@@ -666,17 +665,16 @@ menuitem.bookmark-item {
 
 #nav-bar .toolbarbutton-1 > .toolbarbutton-menubutton-button:not([disabled="true"]):not(:active):hover,
 #nav-bar .toolbarbutton-1:not([open="true"]):not(:active):hover > .toolbarbutton-menubutton-dropmarker:not([disabled="true"]),
 #nav-bar .toolbarbutton-1:not([type="menu-button"]):not([disabled="true"]):not([checked="true"]):not([open="true"]):not(:active):hover {
   background-color: hsla(190,60%,70%,.5);
   border-color: hsla(190,50%,65%,.8) hsla(190,50%,50%,.8) hsla(190,50%,40%,.8);
   box-shadow: 0 0 0 1px rgba(255,255,255,.3) inset,
               0 0 0 1.5px rgba(255,255,255,.1) inset,
-              0 1px 0 rgba(0,0,0,.1),
               0 0 3.5px hsl(190,90%,80%);
   -moz-transition: background-color .4s ease-in,
                    border-color .3s ease-in,
                    box-shadow .3s ease-in;
 }
 
 #nav-bar .toolbarbutton-1 > .toolbarbutton-menubutton-button:not([disabled="true"]):hover:active,
 #nav-bar .toolbarbutton-1:hover:active > .toolbarbutton-menubutton-dropmarker:not([disabled="true"]),
@@ -796,27 +794,25 @@ toolbar[mode="full"] .toolbarbutton-1 > 
   margin-bottom: -2px;
   border: none;
   background-image: -moz-linear-gradient(rgba(251,252,253,.97), rgba(246,247,248,.5) 49%, 
                                          rgba(231,232,233,.45) 51%, rgba(225,226,229,.2));
   box-shadow: 0 0 0 1px rgba(255,255,255,.3) inset,
               0 0 0 2px rgba(255,255,255,.1) inset,
               0 0 0 1px rgba(0,0,0,.15),
               0 1px 0 rgba(0,0,0,.4),
-              0 1px 1px rgba(0,0,0,.3),
-              1px 2px 1px rgba(0,0,0,.2);
+              0 1px 1px rgba(0,0,0,.3);
 }
 
 #navigator-toolbox[iconsize="large"][mode="icons"] > #nav-bar #back-button:not([disabled="true"]):not([open="true"]):not(:active):hover {
   box-shadow: 0 0 0 1px rgba(255,255,255,.3) inset,
               0 0 0 2px rgba(255,255,255,.1) inset,
               0 0 0 1px hsla(190,50%,40%,.3),
               0 1px 0 rgba(0,0,0,.4),
               0 1px 1px rgba(0,0,0,.3),
-              1px 2px 1px rgba(0,0,0,.2),
               0 0 5px 1px hsl(190,90%,80%);
 }
 
 #navigator-toolbox[iconsize="large"][mode="icons"] > #nav-bar #back-button:not([disabled="true"]):hover:active,
 #navigator-toolbox[iconsize="large"][mode="icons"] > #nav-bar #back-button[open="true"] {
   box-shadow: 0 0 6.5px rgba(0,0,0,.4) inset,
               0 0 2px rgba(0,0,0,.4) inset,
               0 0 0 1px rgba(0,0,0,.65),
@@ -1097,19 +1093,17 @@ toolbar[mode="full"] .toolbarbutton-1 > 
 
 #urlbar,
 .searchbar-textbox {
   -moz-appearance: none;
   margin: 1px 3px;
   padding: 2px;
   background-clip: padding-box;
   border: 1px solid ThreeDDarkShadow;
-  border-radius: 4px;
-  box-shadow: 0 1px 0 rgba(0,0,0,.1) inset,
-              0 1px 0 rgba(255,255,255,.4);
+  border-radius: 3.5px;
 }
 
 @media all and (-moz-windows-default-theme) {
   #urlbar,
   .searchbar-textbox {
     @navbarTextboxCustomBorder@
   }
 }
--- a/chrome/src/nsChromeRegistryChrome.cpp
+++ b/chrome/src/nsChromeRegistryChrome.cpp
@@ -890,23 +890,16 @@ nsChromeRegistryChrome::ManifestContent(
     return;
 
   entry->baseURI = resolved;
 
   if (platform)
     entry->flags |= PLATFORM_PACKAGE;
   if (contentaccessible)
     entry->flags |= CONTENT_ACCESSIBLE;
-  if (cx.GetXPConnect()) {
-    nsCAutoString urlp("chrome://");
-    urlp.Append(package);
-    urlp.Append('/');
-
-    cx.GetXPConnect()->FlagSystemFilenamePrefix(urlp.get(), true);
-  }
 }
 
 void
 nsChromeRegistryChrome::ManifestLocale(ManifestProcessingContext& cx, int lineno,
                                        char *const * argv, bool platform,
                                        bool contentaccessible)
 {
   char* package = argv[0];
--- a/config/autoconf.mk.in
+++ b/config/autoconf.mk.in
@@ -219,18 +219,16 @@ MOZ_TIMELINE=@MOZ_TIMELINE@
 
 ENABLE_STRIP	= @ENABLE_STRIP@
 PKG_SKIP_STRIP	= @PKG_SKIP_STRIP@
 
 ClientWallet=1
 CookieManagement=1
 SingleSignon=1
 
-MOZ_PLUGINS	= @MOZ_PLUGINS@
-
 MOZ_POST_DSO_LIB_COMMAND = @MOZ_POST_DSO_LIB_COMMAND@
 MOZ_POST_PROGRAM_COMMAND = @MOZ_POST_PROGRAM_COMMAND@
 
 MOZ_BUILD_ROOT             = @MOZ_BUILD_ROOT@
 
 MOZ_XUL                    = @MOZ_XUL@
 MOZ_RDF                    = @MOZ_RDF@
 
@@ -436,16 +434,17 @@ UNZIP		= @UNZIP@
 ZIP		= @ZIP@
 XARGS		= @XARGS@
 STRIP		= @STRIP@
 DOXYGEN		= @DOXYGEN@
 PBBUILD_BIN	= @PBBUILD@
 SDP		= @SDP@
 NSINSTALL_BIN	= @NSINSTALL_BIN@
 WGET		= @WGET@
+RPMBUILD	= @RPMBUILD@
 
 ifdef MOZ_NATIVE_JPEG
 JPEG_CFLAGS	= @JPEG_CFLAGS@
 JPEG_LIBS	= @JPEG_LIBS@
 JPEG_REQUIRES	=
 else
 JPEG_CFLAGS	= @MOZ_JPEG_CFLAGS@
 JPEG_LIBS	= @MOZ_JPEG_LIBS@
old mode 100755
new mode 100644
--- a/config/find_OOM_errors.py
+++ b/config/find_OOM_errors.py
@@ -65,17 +65,17 @@ def run(args, stdin=None):
     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):
+  if (err, exit) != ("", 0):
     sys.exit("Wrong directory, run from an objdir")
   return out.split()
 
 
 
 #####################################################################
 # Blacklisting
 #####################################################################
@@ -186,35 +186,35 @@ whitelist.add(r"('', 'out of memory\nout
 
 #####################################################################
 # 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
+                  type="int", dest="regression", default=None)
                   
 (OPTIONS, args) = parser.parse_args()
 
 
-if OPTIONS.regression:
+if OPTIONS.regression != None:
   # 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:
+if OPTIONS.regression == None:
   # 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
@@ -224,28 +224,30 @@ for f in files:
   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)
+    else:
+      sys.stdout.write('.') # something short for tinderbox, no space or \n
 
     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:
+      if OPTIONS.regression != None:
         # Just count them
         num_failures += 1
         continue
 
       #########################################################################
       # The regression tests ends above. The rest of this is for running  the
       # script manually.
       #########################################################################
@@ -309,22 +311,23 @@ for f in files:
 
       log.write ("\n")
 
       log.write ("Valgrind info:\n" + vout)
       log.write ("\n")
       log.write ("\n")
       log.flush()
 
-  if not OPTIONS.regression == None:
+  if OPTIONS.regression == None:
     count_lines()
 
+print '\n',
 
 # Do the actual regression check
-if OPTIONS.regression:
+if OPTIONS.regression != None:
   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:
--- a/config/rules.mk
+++ b/config/rules.mk
@@ -2100,16 +2100,17 @@ showbuild:
 	@echo "INCLUDES           = $(INCLUDES)"
 	@echo "DEFINES            = $(DEFINES)"
 	@echo "ACDEFINES          = $(ACDEFINES)"
 	@echo "BIN_SUFFIX         = $(BIN_SUFFIX)"
 	@echo "LIB_SUFFIX         = $(LIB_SUFFIX)"
 	@echo "DLL_SUFFIX         = $(DLL_SUFFIX)"
 	@echo "IMPORT_LIB_SUFFIX  = $(IMPORT_LIB_SUFFIX)"
 	@echo "INSTALL            = $(INSTALL)"
+	@echo "VPATH              = $(VPATH)"
 
 showhost:
 	@echo "HOST_CC            = $(HOST_CC)"
 	@echo "HOST_CXX           = $(HOST_CXX)"
 	@echo "HOST_CFLAGS        = $(HOST_CFLAGS)"
 	@echo "HOST_LDFLAGS       = $(HOST_LDFLAGS)"
 	@echo "HOST_LIBS          = $(HOST_LIBS)"
 	@echo "HOST_EXTRA_LIBS    = $(HOST_EXTRA_LIBS)"
--- a/configure.in
+++ b/configure.in
@@ -125,17 +125,17 @@ GTK2_VERSION=2.10.0
 WINDRES_VERSION=2.14.90
 W32API_VERSION=3.8
 GNOMEVFS_VERSION=2.0
 GNOMEUI_VERSION=2.2.0
 GCONF_VERSION=1.2.1
 GIO_VERSION=2.0
 STARTUP_NOTIFICATION_VERSION=0.8
 DBUS_VERSION=0.60
-SQLITE_VERSION=3.7.4
+SQLITE_VERSION=3.7.5
 LIBNOTIFY_VERSION=0.4
 
 MSMANIFEST_TOOL=
 
 dnl Set various checks
 dnl ========================================================
 MISSING_X=
 AC_PROG_AWK
@@ -1006,16 +1006,19 @@ if test -z "$ZIP" -o "$ZIP" = ":"; then
     AC_MSG_ERROR([zip not found in \$PATH])
 fi
 MOZ_PATH_PROG(SYSTEM_MAKEDEPEND, makedepend)
 MOZ_PATH_PROG(XARGS, xargs)
 if test -z "$XARGS" -o "$XARGS" = ":"; then
     AC_MSG_ERROR([xargs not found in \$PATH .])
 fi
 
+MOZ_PATH_PROG(RPMBUILD, rpmbuild, :)
+AC_SUBST(RPMBUILD)
+
 if test "$COMPILE_ENVIRONMENT"; then
 
 dnl ========================================================
 dnl = Mac OS X toolchain support
 dnl ========================================================
 
 case "$target_os" in
 darwin*)
@@ -2760,17 +2763,16 @@ alpha*-*-osf*)
        if test -z "$CROSS_COMPILE" -a -f /usr/lib/ld/map.noexstk; then
            _SAVE_LDFLAGS=$LDFLAGS
            LDFLAGS="-M /usr/lib/ld/map.noexstk $LDFLAGS" 
            AC_TRY_LINK([#include <stdio.h>],
                        [printf("Hello World\n");],
                        ,
                        [LDFLAGS=$_SAVE_LDFLAGS])
        fi
-       WARNINGS_AS_ERRORS='-Werror'
        MOZ_OPTIMIZE_FLAGS="-xO4"
        MKSHLIB='$(CXX) $(CXXFLAGS) $(DSO_PIC_FLAGS) $(DSO_LDOPTS) -h $@ -o $@'
        MKCSHLIB='$(CC) $(CFLAGS) $(DSO_PIC_FLAGS) $(DSO_LDOPTS) -h $@ -o $@'
        MKSHLIB_FORCE_ALL='-z allextract'
        MKSHLIB_UNFORCE_ALL='-z defaultextract'
        DSO_LDOPTS='-G'
        AR_LIST="$AR t"
        AR_EXTRACT="$AR x"
@@ -2810,16 +2812,17 @@ alpha*-*-osf*)
        AC_LANG_RESTORE
     else
        LDFLAGS="$LDFLAGS -Wl,-z,ignore -Wl,-R,'\$\$ORIGIN:\$\$ORIGIN/..' -Wl,-z,lazyload -Wl,-z,combreloc -Wl,-z,muldefs"
        LIBS="-lc $LIBS"
        MKSHLIB_FORCE_ALL='-Wl,-z -Wl,allextract'
        MKSHLIB_UNFORCE_ALL='-Wl,-z -Wl,defaultextract'
        ASFLAGS="$ASFLAGS -fPIC"
        DSO_LDOPTS='-shared'
+       WARNINGS_AS_ERRORS='-Werror'
        _WARNINGS_CFLAGS=''
        _WARNINGS_CXXFLAGS=''
        if test "$OS_RELEASE" = "5.3"; then
            AC_DEFINE(MUST_UNDEF_HAVE_BOOLEAN_AFTER_INCLUDES)
        fi
     fi
     if test "$OS_RELEASE" = "5.5.1"; then
         AC_DEFINE(NEED_USLEEP_PROTOTYPE)
@@ -4966,24 +4969,24 @@ 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=
+MOZ_LIBJPEG_TURBO=1
 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
 MOZ_REFLOW_PERF=
 MOZ_SAFE_BROWSING=
 MOZ_FASTSTART=
 MOZ_HELP_VIEWER=
@@ -5845,24 +5848,16 @@ MOZ_ARG_ENABLE_BOOL(ipdl-tests,
 
 if test -n "$MOZ_IPDL_TESTS"; then
     AC_DEFINE(MOZ_IPDL_TESTS)
 fi
 
 AC_SUBST(MOZ_IPDL_TESTS)
 
 dnl ========================================================
-dnl = Disable plugin support
-dnl ========================================================
-MOZ_ARG_DISABLE_BOOL(plugins,
-[  --disable-plugins       Disable plugins support],
-    MOZ_PLUGINS=,
-    MOZ_PLUGINS=1)
-
-dnl ========================================================
 dnl = Disable building dbm
 dnl ========================================================
 MOZ_ARG_DISABLE_BOOL(dbm,
 [  --disable-dbm           Disable building dbm],
     NSS_DISABLE_DBM=1,
     NSS_DISABLE_DBM=)
 
 dnl bi-directional support always on
@@ -6444,28 +6439,27 @@ if test -z "$MOZ_CRASHREPORTER_ENABLE_PE
    MOZ_CRASHREPORTER_ENABLE_PERCENT=100
 fi
 AC_DEFINE_UNQUOTED(MOZ_CRASHREPORTER_ENABLE_PERCENT, $MOZ_CRASHREPORTER_ENABLE_PERCENT)
 
 dnl ========================================================
 dnl = libjpeg-turbo configuration
 dnl ========================================================
 
+MOZ_ARG_DISABLE_BOOL(libjpeg_turbo,
+[ --disable-libjpeg-turbo  Disable optimized jpeg decoding routines],
+    MOZ_LIBJPEG_TURBO=,
+    MOZ_LIBJPEG_TURBO=1)
+
 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.
+
+if test -n "$MOZ_LIBJPEG_TURBO"; then
+
+  dnl Do we support libjpeg-turbo 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
@@ -6491,24 +6485,43 @@ if test -n "LIBJPEG_TURBO_AS"; then
     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
+fi
+
+dnl If we're on a system which supports libjpeg-turbo's asm routines and
+dnl --disable-libjpeg-turbo wasn't passed, check for yasm, and error out if it
+dnl doesn't exist or we have too old of a version.
+if test -n "$LIBJPEG_TURBO_X86_ASM" -o -n "$LIBJPEG_TURBO_X64_ASM" ; then
+    AC_MSG_CHECKING([for YASM assembler])
+    AC_CHECK_PROGS(LIBJPEG_TURBO_AS, yasm, "")
+
+    if test -z "$LIBJPEG_TURBO_AS" ; then
+        AC_MSG_ERROR([yasm is required to build with libjpeg-turbo's optimized JPEG decoding routines, but you do not appear to have yasm installed.  Either install it or configure with --disable-libjpeg-turbo to use the pure C JPEG decoder.  See https://developer.mozilla.org/en/YASM for more details.])
+    fi
+
+    dnl Check for yasm 1.1 or greater.
+    if test "$_YASM_MAJOR_VERSION" -lt "1" -o \( "$_YASM_MAJOR_VERSION" -eq "1" -a "$_YASM_MINOR_VERSION" -lt "1" \) ; then
+        AC_MSG_ERROR([yasm 1.1 or greater is required to build with libjpeg-turbo's optimized JPEG decoding routines, but you appear to have version $_YASM_MAJOR_VERSION.$_YASM_MINOR_VERSION.  Upgrade to the newest version or configure with --disable-libjpeg-turbo to use the pure C JPEG decoder.  See https://developer.mozilla.org/en/YASM for more details.])
+    fi
+fi
 
 if test -n "$LIBJPEG_TURBO_X86_ASM"; then
-  AC_DEFINE(LIBJPEG_TURBO_X86_ASM)
+    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.])
+    AC_DEFINE(LIBJPEG_TURBO_X64_ASM)
+elif test -n "$MOZ_LIBJPEG_TURBO"; then
+    dnl Warn if we're not building the optimized routines, even though the user
+    dnl didn't specify --disable-libjpeg-turbo.
+    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],
@@ -7006,16 +7019,19 @@ else
     AC_MSG_RESULT($ac_cv_sqlite_enable_unlock_notify)
     CFLAGS="$_SAVE_CFLAGS"
     LIBS="$_SAVE_LIBS"
     if test "x$ac_cv_sqlite_enable_unlock_notify" = "xno"; then
         AC_MSG_ERROR([System SQLite library is not compiled with SQLITE_ENABLE_UNLOCK_NOTIFY.])
     fi
 fi
 
+if test -n "$MOZ_NATIVE_SQLITE"; then
+    AC_DEFINE(MOZ_NATIVE_SQLITE)
+fi
 AC_SUBST(MOZ_NATIVE_SQLITE)
 
 dnl ========================================================
 dnl = Enable help viewer (off by default)
 dnl ========================================================
 if test -n "$MOZ_HELP_VIEWER"; then
      dnl Do this if defined in confvars.sh
      AC_DEFINE(MOZ_HELP_VIEWER)
@@ -9028,17 +9044,16 @@ AC_SUBST(MOZ_DEBUG)
 AC_SUBST(MOZ_DEBUG_SYMBOLS)
 AC_SUBST(MOZ_DEBUG_ENABLE_DEFS)
 AC_SUBST(MOZ_DEBUG_DISABLE_DEFS)
 AC_SUBST(MOZ_DEBUG_FLAGS)
 AC_SUBST(MOZ_DEBUG_LDFLAGS)
 AC_SUBST(WARNINGS_AS_ERRORS)
 AC_SUBST(MOZ_EXTENSIONS)
 AC_SUBST(MOZ_JSDEBUGGER)
-AC_SUBST(MOZ_PLUGINS)
 AC_SUBST(MOZ_LOG_REFCNT)
 AC_SUBST(MOZ_LEAKY)
 AC_SUBST(MOZ_JPROF)
 AC_SUBST(MOZ_SHARK)
 AC_SUBST(MOZ_CALLGRIND)
 AC_SUBST(MOZ_VTUNE)
 AC_SUBST(MOZ_PROFILING)
 AC_SUBST(MOZ_USE_NATIVE_UCONV)
--- 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/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) {
@@ -6381,18 +6363,18 @@ LayerManagerForDocumentInternal(nsIDocum
       docShellTreeItem->GetParent(getter_AddRefs(parent));
       docShellTreeItem = parent;
     }
   }
 
   if (shell) {
     nsIViewManager* VM = shell->GetViewManager();
     if (VM) {
-      nsIView* rootView = nsnull;
-      if (NS_SUCCEEDED(VM->GetRootView(rootView)) && rootView) {
+      nsIView* rootView = VM->GetRootView();
+      if (rootView) {
         nsIView* displayRoot = GetDisplayRootFor(rootView);
         if (displayRoot) {
           nsIWidget* widget = displayRoot->GetNearestWidget(nsnull);
           if (widget) {
             nsRefPtr<LayerManager> manager =
               widget->
                 GetLayerManager(aRequirePersistent ? nsIWidget::LAYER_MANAGER_PERSISTENT : 
                                                      nsIWidget::LAYER_MANAGER_CURRENT,
--- 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/nsFrameMessageManager.cpp
+++ b/content/base/src/nsFrameMessageManager.cpp
@@ -252,23 +252,21 @@ nsFrameMessageManager::SendSyncMessage()
       JSObject* dataArray = JS_NewArrayObject(ctx, len, NULL);
       NS_ENSURE_TRUE(dataArray, NS_ERROR_OUT_OF_MEMORY);
 
       for (PRUint32 i = 0; i < len; ++i) {
         if (!retval[i].Length())
           continue;
 
         jsval ret = JSVAL_VOID;
-        JSONParser* parser = JS_BeginJSONParse(ctx, &ret);
-        JSBool ok = JS_ConsumeJSONText(ctx, parser, (jschar*)retval[i].get(),
-                                       (uint32)retval[i].Length());
-        ok = JS_FinishJSONParse(ctx, parser, JSVAL_NULL) && ok;
-        if (ok) {
-          NS_ENSURE_TRUE(JS_SetElement(ctx, dataArray, i, &ret), NS_ERROR_OUT_OF_MEMORY);
+        if (!JS_ParseJSON(ctx, (jschar*)retval[i].get(),
+                          (uint32)retval[i].Length(), &ret)) {
+          return NS_ERROR_UNEXPECTED;
         }
+        NS_ENSURE_TRUE(JS_SetElement(ctx, dataArray, i, &ret), NS_ERROR_OUT_OF_MEMORY);
       }
 
       jsval* retvalPtr;
       ncc->GetRetValPtr(&retvalPtr);
       *retvalPtr = OBJECT_TO_JSVAL(dataArray);
       ncc->SetReturnValueWasSet(PR_TRUE);
     }
   }
@@ -379,25 +377,19 @@ nsFrameMessageManager::ReceiveMessage(ns
           aObjectsArray = JS_NewArrayObject(ctx, 0, NULL);
           if (!aObjectsArray) {
             return NS_ERROR_OUT_OF_MEMORY;
           }
         }
 
         jsval json = JSVAL_NULL;
         if (!aJSON.IsEmpty()) {
-          JSONParser* parser = JS_BeginJSONParse(ctx, &json);
-          if (parser) {
-            JSBool ok = JS_ConsumeJSONText(ctx, parser,
-                                           (jschar*)nsString(aJSON).get(),
-                                           (uint32)aJSON.Length());
-            ok = JS_FinishJSONParse(ctx, parser, JSVAL_NULL) && ok;
-            if (!ok) {
-              json = JSVAL_NULL;
-            }
+          if (!JS_ParseJSON(ctx, (jschar*)nsString(aJSON).get(),
+                            (uint32)aJSON.Length(), &json)) {
+            json = JSVAL_NULL;
           }
         }
         JSString* jsMessage =
           JS_NewUCStringCopyN(ctx,
                               reinterpret_cast<const jschar *>(nsString(aMessage).get()),
                               aMessage.Length());
         NS_ENSURE_TRUE(jsMessage, NS_ERROR_OUT_OF_MEMORY);
         JS_DefineProperty(ctx, param, "target", targetv, NULL, NULL, JSPROP_ENUMERATE);
@@ -673,17 +665,16 @@ nsFrameScriptExecutor::LoadFrameScriptIn
       // 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) {
         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);
 
         JSObject* scriptObj =
           JS_CompileUCScriptForPrincipals(mCx, nsnull, jsprin,
                                          (jschar*)dataString.get(),
                                           dataString.Length(),
--- a/content/base/src/nsGenericElement.cpp
+++ b/content/base/src/nsGenericElement.cpp
@@ -2614,17 +2614,16 @@ nsGenericElement::RemoveAttributeNS(cons
     nsContentUtils::NameSpaceManager()->GetNameSpaceID(aNamespaceURI);
 
   if (nsid == kNameSpaceID_Unknown) {
     // Unknown namespace means no attr...
 
     return NS_OK;
   }
 
-  nsAutoString tmp;
   UnsetAttr(nsid, name, PR_TRUE);
 
   return NS_OK;
 }
 
 nsresult
 nsGenericElement::GetAttributeNodeNS(const nsAString& aNamespaceURI,
                                      const nsAString& aLocalName,
@@ -3541,44 +3540,64 @@ nsGenericElement::InsertChildAt(nsIConte
                                 PRUint32 aIndex,
                                 PRBool aNotify)
 {
   NS_PRECONDITION(aKid, "null ptr");
 
   return doInsertChildAt(aKid, aIndex, aNotify, mAttrsAndChildren);
 }
 
+static nsresult
+AdoptNodeIntoOwnerDoc(nsINode *aParent, nsINode *aNode)
+{
+  nsIDocument *doc = aParent->GetOwnerDoc();
+
+  nsresult rv;
+  nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(doc, &rv);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr<nsIDOMNode> node = do_QueryInterface(aNode, &rv);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr<nsIDOMNode> adoptedNode;
+  rv = domDoc->AdoptNode(node, getter_AddRefs(adoptedNode));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (doc != aParent->GetOwnerDoc()) {
+    return NS_ERROR_DOM_WRONG_DOCUMENT_ERR;
+  }
+
+  NS_ASSERTION(adoptedNode == node, "Uh, adopt node changed nodes?");
+  NS_ASSERTION(aParent->HasSameOwnerDoc(aNode),
+               "ownerDocument changed again after adopting!");
+
+  return NS_OK;
+}
 
 nsresult
 nsINode::doInsertChildAt(nsIContent* aKid, PRUint32 aIndex,
                          PRBool aNotify, nsAttrAndChildArray& aChildArray)
 {
   nsresult rv;
 
   if (!HasSameOwnerDoc(aKid)) {
     nsCOMPtr<nsIDOMNode> kid = do_QueryInterface(aKid, &rv);
     NS_ENSURE_SUCCESS(rv, rv);
  
     PRUint16 nodeType = 0;
     rv = kid->GetNodeType(&nodeType);
     NS_ENSURE_SUCCESS(rv, rv);
 
-    nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(GetOwnerDoc());
-
     // DocumentType nodes are the only nodes that can have a null
     // ownerDocument according to the DOM spec, and we need to allow
     // inserting them w/o calling AdoptNode().
 
-    if (domDoc && (nodeType != nsIDOMNode::DOCUMENT_TYPE_NODE ||
-                   aKid->GetOwnerDoc())) {
-      nsCOMPtr<nsIDOMNode> adoptedKid;
-      rv = domDoc->AdoptNode(kid, getter_AddRefs(adoptedKid));
+    if (nodeType != nsIDOMNode::DOCUMENT_TYPE_NODE || aKid->GetOwnerDoc()) {
+      rv = AdoptNodeIntoOwnerDoc(this, aKid);
       NS_ENSURE_SUCCESS(rv, rv);
-
-      NS_ASSERTION(adoptedKid == kid, "Uh, adopt node changed nodes?");
     }
   }
 
   // The id-handling code, and in the future possibly other code, need to
   // react to unexpected attribute changes.
   nsMutationGuard::DidMutate();
 
   // Do this before checking the child-count since this could cause mutations
@@ -4059,39 +4078,24 @@ nsINode::ReplaceOrInsertBefore(PRBool aR
   res = aNewChild->GetNodeType(&nodeType);
   NS_ENSURE_SUCCESS(res, res);
 
   // Make sure that the inserted node is allowed as a child of its new parent.
   if (!IsAllowedAsChild(newContent, nodeType, this, aReplace, refContent)) {
     return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR;
   }
 
-  nsIDocument *doc = GetOwnerDoc();
-
   // DocumentType nodes are the only nodes that can have a null
   // ownerDocument according to the DOM spec, and we need to allow
   // inserting them w/o calling AdoptNode().
   if (!HasSameOwnerDoc(newContent) &&
       (nodeType != nsIDOMNode::DOCUMENT_TYPE_NODE ||
        newContent->GetOwnerDoc())) {
-    nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(doc);
-
-    if (domDoc) {
-      nsresult rv;
-      nsCOMPtr<nsIDOMNode> newChild = do_QueryInterface(aNewChild, &rv);
-      NS_ENSURE_SUCCESS(rv, rv);
-
-      nsCOMPtr<nsIDOMNode> adoptedKid;
-      rv = domDoc->AdoptNode(newChild, getter_AddRefs(adoptedKid));
-      NS_ENSURE_SUCCESS(rv, rv);
-
-      NS_ASSERTION(adoptedKid == newChild, "Uh, adopt node changed nodes?");
-      NS_ASSERTION(HasSameOwnerDoc(newContent) && doc == GetOwnerDoc(),
-                   "ownerDocument changed again after adopting!");
-    }
+    res = AdoptNodeIntoOwnerDoc(this, aNewChild);
+    NS_ENSURE_SUCCESS(res, res);
   }
 
   // If we're replacing
   if (aReplace) {
     refContent = GetChildAt(insPos + 1);
 
     nsMutationGuard guard;
 
@@ -4107,16 +4111,18 @@ nsINode::ReplaceOrInsertBefore(PRBool aR
       // Passing PR_FALSE for aIsReplace since we now have removed the node
       // to be replaced.
       if (!IsAllowedAsChild(newContent, nodeType, this, PR_FALSE, refContent)) {
         return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR;
       }
     }
   }
 
+  nsIDocument *doc = GetOwnerDoc();
+
   /*
    * Check if we're inserting a document fragment. If we are, we need
    * to remove the children of the document fragment and add them
    * individually (i.e. we don't add the actual document fragment).
    */
   if (nodeType == nsIDOMNode::DOCUMENT_FRAGMENT_NODE) {
     PRUint32 count = newContent->GetChildCount();
 
--- a/content/base/src/nsGkAtomList.h
+++ b/content/base/src/nsGkAtomList.h
@@ -555,16 +555,17 @@ GK_ATOM(maxpos, "maxpos")
 GK_ATOM(maxwidth, "maxwidth")
 GK_ATOM(mayscript, "mayscript")
 GK_ATOM(media, "media")
 GK_ATOM(mediaType, "media-type")
 GK_ATOM(member, "member")
 GK_ATOM(menu, "menu")
 GK_ATOM(menubar, "menubar")
 GK_ATOM(menubutton, "menubutton")
+GK_ATOM(menuButton, "menu-button")
 GK_ATOM(menugenerated, "menugenerated")
 GK_ATOM(menuitem, "menuitem")
 GK_ATOM(menulist, "menulist")
 GK_ATOM(menupopup, "menupopup")
 GK_ATOM(message, "message")
 GK_ATOM(meta, "meta")
 GK_ATOM(meter, "meter")
 GK_ATOM(method, "method")
@@ -910,16 +911,17 @@ GK_ATOM(sortStaticsLast, "sortStaticsLas
 GK_ATOM(source, "source")
 #endif
 GK_ATOM(space, "space")
 GK_ATOM(spacer, "spacer")
 GK_ATOM(span, "span")
 GK_ATOM(spellcheck, "spellcheck")
 GK_ATOM(spinner, "spinner")
 GK_ATOM(split, "split")
+GK_ATOM(splitmenu, "splitmenu")
 GK_ATOM(splitter, "splitter")
 GK_ATOM(spring, "spring")
 GK_ATOM(src, "src")
 GK_ATOM(srclang, "srclang")
 GK_ATOM(stack, "stack")
 GK_ATOM(standalone, "standalone")
 GK_ATOM(standby, "standby")
 GK_ATOM(start, "start")
@@ -1221,17 +1223,17 @@ GK_ATOM(marker_start, "marker-start")
 GK_ATOM(markerHeight, "markerHeight")
 GK_ATOM(markerUnits, "markerUnits")
 GK_ATOM(markerWidth, "markerWidth")
 GK_ATOM(mask, "mask")
 GK_ATOM(maskContentUnits, "maskContentUnits")
 GK_ATOM(maskUnits, "maskUnits")
 GK_ATOM(matrix, "matrix")
 GK_ATOM(metadata, "metadata")
-GK_ATOM(missingGlyph, "missingGlyph")
+GK_ATOM(missingGlyph, "missing-glyph")
 GK_ATOM(mm, "mm")
 GK_ATOM(mpath, "mpath")
 GK_ATOM(noStitch, "noStitch")
 GK_ATOM(numOctaves, "numOctaves")
 GK_ATOM(multiply, "multiply")
 GK_ATOM(objectBoundingBox, "objectBoundingBox")
 GK_ATOM(offset, "offset")
 GK_ATOM(onSVGAbort, "onSVGAbort")
@@ -1375,185 +1377,285 @@ GK_ATOM(_moz_math_fontstyle_, "_moz-math
 GK_ATOM(_moz_math_rowalign_, "_moz-math-rowalign")
 GK_ATOM(_moz_math_rowline_, "_moz-math-rowline")
 
 GK_ATOM(abs_, "abs")
 GK_ATOM(accent_, "accent")
 GK_ATOM(accentunder_, "accentunder")
 GK_ATOM(actiontype_, "actiontype")
 GK_ATOM(alignmentscope_, "alignmentscope")
+GK_ATOM(altimg_, "altimg")
+GK_ATOM(altimg_height_, "altimg-height")
+GK_ATOM(altimg_valign_, "altimg-valign")
+GK_ATOM(altimg_width_, "altimg-width")
 GK_ATOM(annotation_, "annotation")
+GK_ATOM(annotation_xml_, "annotation-xml")
 GK_ATOM(apply_, "apply")
+GK_ATOM(approx_, "approx")
 GK_ATOM(arccos_, "arccos")
+GK_ATOM(arccosh_, "arccosh")
+GK_ATOM(arccot_, "arccot")
+GK_ATOM(arccoth_, "arccoth")
+GK_ATOM(arccsc_, "arccsc")
+GK_ATOM(arccsch_, "arccsch")
+GK_ATOM(arcsec_, "arcsec")
+GK_ATOM(arcsech_, "arcsech")
 GK_ATOM(arcsin_, "arcsin")
+GK_ATOM(arcsinh_, "arcsinh")
 GK_ATOM(arctan_, "arctan")
+GK_ATOM(arctanh_, "arctanh")
+GK_ATOM(arg_, "arg")
 GK_ATOM(bevelled_, "bevelled")
+GK_ATOM(bind_, "bind")
 GK_ATOM(bvar_, "bvar")
+GK_ATOM(card_, "card")
+GK_ATOM(cartesianproduct_, "cartesianproduct")
+GK_ATOM(cbytes_, "cbytes")
+GK_ATOM(cd_, "cd")
+GK_ATOM(cdgroup_, "cdgroup")
+GK_ATOM(cerror_, "cerror")
+GK_ATOM(charalign_, "charalign")
 GK_ATOM(ci_, "ci")
+GK_ATOM(closure_, "closure")
 GK_ATOM(cn_, "cn")
+GK_ATOM(codomain_, "codomain")
 GK_ATOM(columnalign_, "columnalign")
+GK_ATOM(columnalignment_, "columnalignment")
 GK_ATOM(columnlines_, "columnlines")
 GK_ATOM(columnspacing_, "columnspacing")
 GK_ATOM(columnspan_, "columnspan")
 GK_ATOM(columnwidth_, "columnwidth")
+GK_ATOM(complexes_, "complexes")
 GK_ATOM(compose_, "compose")
 GK_ATOM(condition_, "condition")
 GK_ATOM(conjugate_, "conjugate")
 GK_ATOM(cos_, "cos")
 GK_ATOM(cosh_, "cosh")
 GK_ATOM(cot_, "cot")
 GK_ATOM(coth_, "coth")
+GK_ATOM(crossout_, "crossout")
 GK_ATOM(csc_, "csc")
 GK_ATOM(csch_, "csch")
+GK_ATOM(cs_, "cs")
+GK_ATOM(csymbol_, "csymbol")
+GK_ATOM(curl_, "curl")
+GK_ATOM(decimalpoint_, "decimalpoint")
+GK_ATOM(definitionURL_, "definitionURL")
 GK_ATOM(degree_, "degree")
 GK_ATOM(denomalign_, "denomalign")
 GK_ATOM(depth_, "depth")
 GK_ATOM(determinant_, "determinant")
 GK_ATOM(diff_, "diff")
 GK_ATOM(displaystyle_, "displaystyle")
+GK_ATOM(divergence_, "divergence")
 GK_ATOM(divide_, "divide")
+GK_ATOM(domain_, "domain")
+GK_ATOM(domainofapplication_, "domainofapplication")
 GK_ATOM(edge_, "edge")
+GK_ATOM(el_, "el")
+GK_ATOM(emptyset_, "emptyset")
 GK_ATOM(eq_, "eq")
 GK_ATOM(equalcolumns_, "equalcolumns")
 GK_ATOM(equalrows_, "equalrows")
+GK_ATOM(equivalent_, "equivalent")
+GK_ATOM(eulergamma_, "eulergamma")
 GK_ATOM(exists_, "exists")
 GK_ATOM(exp_, "exp")
+GK_ATOM(exponentiale_, "exponentiale")
 GK_ATOM(factorial_, "factorial")
+GK_ATOM(factorof_, "factorof")
 GK_ATOM(fence_, "fence")
 GK_ATOM(fn_, "fn")
 GK_ATOM(fontfamily_, "fontfamily")
 GK_ATOM(fontsize_, "fontsize")
 GK_ATOM(fontstyle_, "fontstyle")
 GK_ATOM(fontweight_, "fontweight")
 GK_ATOM(forall_, "forall")
 GK_ATOM(framespacing_, "framespacing")
+GK_ATOM(gcd_, "gcd")
 GK_ATOM(geq_, "geq")
 GK_ATOM(groupalign_, "groupalign")
 GK_ATOM(gt_, "gt")
 GK_ATOM(ident_, "ident")
+GK_ATOM(imaginaryi_, "imaginaryi")
+GK_ATOM(imaginary_, "imaginary")
 GK_ATOM(implies_, "implies")
-GK_ATOM(int_, "int")
+GK_ATOM(indentalignfirst_, "indentalignfirst")
+GK_ATOM(indentalign_, "indentalign")
+GK_ATOM(indentalignlast_, "indentalignlast")
+GK_ATOM(indentshiftfirst_, "indentshiftfirst")
+GK_ATOM(indentshift_, "indentshift")
+GK_ATOM(indenttarget_, "indenttarget")
+GK_ATOM(integers_, "integers")
 GK_ATOM(intersect_, "intersect")
 GK_ATOM(interval_, "interval")
+GK_ATOM(int_, "int")
 GK_ATOM(inverse_, "inverse")
 GK_ATOM(lambda_, "lambda")
+GK_ATOM(laplacian_, "laplacian")
 GK_ATOM(largeop_, "largeop")
+GK_ATOM(lcm_, "lcm")
 GK_ATOM(leq_, "leq")
 GK_ATOM(limit_, "limit")
 GK_ATOM(linebreak_, "linebreak")
+GK_ATOM(linebreakmultchar_, "linebreakmultchar")
+GK_ATOM(linebreakstyle_, "linebreakstyle")
 GK_ATOM(linethickness_, "linethickness")
 GK_ATOM(list_, "list")
 GK_ATOM(ln_, "ln")
+GK_ATOM(location_, "location")
+GK_ATOM(logbase_, "logbase")
 GK_ATOM(log_, "log")
-GK_ATOM(logbase_, "logbase")
+GK_ATOM(longdivstyle_, "longdivstyle")
 GK_ATOM(lowlimit_, "lowlimit")
 GK_ATOM(lquote_, "lquote")
 GK_ATOM(lspace_, "lspace")
 GK_ATOM(lt_, "lt")
 GK_ATOM(maction_, "maction")
 GK_ATOM(maligngroup_, "maligngroup")
+GK_ATOM(malign_, "malign")
 GK_ATOM(malignmark_, "malignmark")
+GK_ATOM(malignscope_, "malignscope")
 GK_ATOM(mathbackground_, "mathbackground")
 GK_ATOM(mathcolor_, "mathcolor")
 GK_ATOM(mathsize_, "mathsize")
 GK_ATOM(mathvariant_, "mathvariant")
 GK_ATOM(matrixrow_, "matrixrow")
 GK_ATOM(maxsize_, "maxsize")
 GK_ATOM(mean_, "mean")
 GK_ATOM(median_, "median")
 GK_ATOM(mediummathspace_, "mediummathspace")
 GK_ATOM(menclose_, "menclose")
 GK_ATOM(merror_, "merror")
 GK_ATOM(mfenced_, "mfenced")
 GK_ATOM(mfrac_, "mfrac")
+GK_ATOM(mfraction_, "mfraction")
+GK_ATOM(mglyph_, "mglyph")
 GK_ATOM(mi_, "mi")
 GK_ATOM(minlabelspacing_, "minlabelspacing")
 GK_ATOM(minsize_, "minsize")
 GK_ATOM(minus_, "minus")
 GK_ATOM(mlabeledtr_, "mlabeledtr")
+GK_ATOM(mlongdiv_, "mlongdiv")
 GK_ATOM(mmultiscripts_, "mmultiscripts")
 GK_ATOM(mn_, "mn")
+GK_ATOM(momentabout_, "momentabout")
+GK_ATOM(moment_, "moment")
 GK_ATOM(mo_, "mo")
-GK_ATOM(moment_, "moment")
+GK_ATOM(monospaced_, "monospaced")
 GK_ATOM(movablelimits_, "movablelimits")
 GK_ATOM(mover_, "mover")
 GK_ATOM(mpadded_, "mpadded")
 GK_ATOM(mphantom_, "mphantom")
 GK_ATOM(mprescripts_, "mprescripts")
 GK_ATOM(mroot_, "mroot")
 GK_ATOM(mrow_, "mrow")
+GK_ATOM(mscarries_, "mscarries")
+GK_ATOM(mscarry_, "mscarry")
+GK_ATOM(msgroup_, "msgroup")
+GK_ATOM(msline_, "msline")
 GK_ATOM(ms_, "ms")
 GK_ATOM(mspace_, "mspace")
 GK_ATOM(msqrt_, "msqrt")
+GK_ATOM(msrow_, "msrow")
+GK_ATOM(mstack_, "mstack")
 GK_ATOM(mstyle_, "mstyle")
 GK_ATOM(msub_, "msub")
 GK_ATOM(msubsup_, "msubsup")
 GK_ATOM(msup_, "msup")
 GK_ATOM(mtable_, "mtable")
 GK_ATOM(mtd_, "mtd")
 GK_ATOM(mtext_, "mtext")
 GK_ATOM(mtr_, "mtr")
 GK_ATOM(munder_, "munder")
 GK_ATOM(munderover_, "munderover")
+GK_ATOM(naturalnumbers_, "naturalnumbers")
+GK_ATOM(negativemediummathspace_, "negativemediummathspace")
+GK_ATOM(negativethickmathspace_, "negativethickmathspace")
+GK_ATOM(negativethinmathspace_, "negativethinmathspace")
+GK_ATOM(negativeverythickmathspace_, "negativeverythickmathspace")
+GK_ATOM(negativeverythinmathspace_, "negativeverythinmathspace")
+GK_ATOM(negativeveryverythickmathspace_, "negativeveryverythickmathspace")
+GK_ATOM(negativeveryverythinmathspace_, "negativeveryverythinmathspace")
 GK_ATOM(neq_, "neq")
+GK_ATOM(notanumber_, "notanumber")
 GK_ATOM(notation_, "notation")
+GK_ATOM(note_, "note")
 GK_ATOM(notin_, "notin")
 GK_ATOM(notprsubset_, "notprsubset")
 GK_ATOM(notsubset_, "notsubset")
 GK_ATOM(numalign_, "numalign")
 GK_ATOM(other_, "other")
+GK_ATOM(outerproduct_, "outerproduct")
 GK_ATOM(partialdiff_, "partialdiff")
+GK_ATOM(piece_, "piece")
+GK_ATOM(piecewise_, "piecewise")
+GK_ATOM(pi_, "pi")
 GK_ATOM(plus_, "plus")
 GK_ATOM(power_, "power")
+GK_ATOM(primes_, "primes")
 GK_ATOM(product_, "product")
 GK_ATOM(prsubset_, "prsubset")
 GK_ATOM(quotient_, "quotient")
+GK_ATOM(rationals_, "rationals")
+GK_ATOM(real_, "real")
+GK_ATOM(reals_, "reals")
 GK_ATOM(reln_, "reln")
 GK_ATOM(root_, "root")
 GK_ATOM(rowalign_, "rowalign")
 GK_ATOM(rowlines_, "rowlines")
 GK_ATOM(rowspacing_, "rowspacing")
 GK_ATOM(rquote_, "rquote")
 GK_ATOM(rspace_, "rspace")
+GK_ATOM(scalarproduct_, "scalarproduct")
+GK_ATOM(schemaLocation_, "schemaLocation")
 GK_ATOM(scriptlevel_, "scriptlevel")
 GK_ATOM(scriptminsize_, "scriptminsize")
 GK_ATOM(scriptsizemultiplier_, "scriptsizemultiplier")
+GK_ATOM(scriptsize_, "scriptsize")
 GK_ATOM(sdev_, "sdev")
-GK_ATOM(sec_, "sec")
 GK_ATOM(sech_, "sech")
+GK_ATOM(sec_, "sec")
 GK_ATOM(selection_, "selection")
+GK_ATOM(selector_, "selector")
 GK_ATOM(semantics_, "semantics")
-GK_ATOM(sep_, "sep")
 GK_ATOM(separator_, "separator")
 GK_ATOM(separators_, "separators")
+GK_ATOM(sep_, "sep")
+GK_ATOM(setdiff_, "setdiff")
 GK_ATOM(set_, "set")
-GK_ATOM(setdiff_, "setdiff")
+GK_ATOM(share_, "share")
+GK_ATOM(shift_, "shift")
 GK_ATOM(side_, "side")
+GK_ATOM(sinh_, "sinh")
 GK_ATOM(sin_, "sin")
-GK_ATOM(sinh_, "sinh")
+GK_ATOM(stackalign_, "stackalign")
 GK_ATOM(stretchy_, "stretchy")
 GK_ATOM(subscriptshift_, "subscriptshift")
 GK_ATOM(subset_, "subset")
 GK_ATOM(superscriptshift_, "superscriptshift")
 GK_ATOM(symmetric_, "symmetric")
+GK_ATOM(tanh_, "tanh")
 GK_ATOM(tan_, "tan")
-GK_ATOM(tanh_, "tanh")
 GK_ATOM(tendsto_, "tendsto")
 GK_ATOM(thickmathspace_, "thickmathspace")
 GK_ATOM(thinmathspace_, "thinmathspace")
 GK_ATOM(times_, "times")
 GK_ATOM(transpose_, "transpose")
 GK_ATOM(union_, "union")
 GK_ATOM(uplimit_, "uplimit")
-GK_ATOM(var_, "var")
+GK_ATOM(variance_, "variance")
+GK_ATOM(vectorproduct_, "vectorproduct")
 GK_ATOM(vector_, "vector")
 GK_ATOM(verythickmathspace_, "verythickmathspace")
 GK_ATOM(verythinmathspace_, "verythinmathspace")
 GK_ATOM(veryverythickmathspace_, "veryverythickmathspace")
 GK_ATOM(veryverythinmathspace_, "veryverythinmathspace")
+GK_ATOM(voffset_, "voffset")
+GK_ATOM(xref_, "xref")
 GK_ATOM(math, "math") // the only one without an underscore
 #endif
 
 #if defined(MOZ_SVG) || defined(MOZ_MATHML)
 GK_ATOM(xor_, "xor")
 #endif
 
 #ifndef DISABLE_XFORMS_HOOKS
--- a/content/base/src/nsTextFragment.cpp
+++ b/content/base/src/nsTextFragment.cpp
@@ -226,17 +226,18 @@ nsTextFragment::SetTo(const PRUnichar* a
 
     return;
   }
 
   const PRUnichar *ucp = aBuffer;
   const PRUnichar *uend = aBuffer + aLength;
 
   // Check if we can use a shared string
-  if (firstChar == ' ' || firstChar == '\n' || firstChar == '\t') {
+  if (aLength <= 1 + TEXTFRAG_WHITE_AFTER_NEWLINE + TEXTFRAG_MAX_NEWLINES &&
+     (firstChar == ' ' || firstChar == '\n' || firstChar == '\t')) {
     if (firstChar == ' ') {
       ++ucp;
     }
 
     const PRUnichar* start = ucp;
     while (ucp < uend && *ucp == '\n') {
       ++ucp;
     }
--- a/content/base/test/Makefile.in
+++ b/content/base/test/Makefile.in
@@ -468,16 +468,17 @@ include $(topsrcdir)/config/rules.mk
 		test_bug466080.html \
 		bug466080.sjs \
 		test_bug625722.html \
 		test_bug631615.html \
 		test_bug558431.html \
 		file_bug558431.html \
 		file_bug558431.html^headers^ \
 		test_bug628938.html \
+		test_bug626262.html \
 		$(NULL)
 
 # This test fails on the Mac for some reason
 ifneq (,$(filter gtk2 windows,$(MOZ_WIDGET_TOOLKIT)))
 _TEST_FILES2 += 	test_copyimage.html \
 		$(NULL)
 endif
 
new file mode 100644
--- /dev/null
+++ b/content/base/test/test_bug626262.html
@@ -0,0 +1,55 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=626262
+-->
+<head>
+  <title>Test for Bug 626262</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=626262">Mozilla Bug 626262</a>
+<p id="display"><iframe id="f" src="data:text/html,1"></iframe></p>
+<div id="content" style="display: none">
+  
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 626262 **/
+SimpleTest.waitForExplicitFinish();
+
+addLoadEvent(function() {
+  var iframe = document.getElementById("f");
+  var frameDoc = iframe.contentDocument;
+  var parent = frameDoc.createElementNS("http://www.w3.org/1999/xhtml", "div");
+
+  function a()
+  {
+    window.removeEventListener("DOMNodeRemoved", arguments.callee, false);
+    document.adoptNode(parent);
+  }
+
+  var text = document.createTextNode(" ");
+  document.documentElement.appendChild(text);
+
+  var thrown = false;
+  try {
+    window.addEventListener("DOMNodeRemoved", a, false);
+    parent.appendChild(text);
+  }
+  catch (e) {
+    thrown = true;
+  }
+
+  ok(thrown, "changing ownerDocument during adoptNode should throw");
+
+  SimpleTest.finish();
+});
+
+</script>
+</pre>
+</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,24 +38,25 @@
 #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 \
-{ 0xec90f32e, 0x7848, 0x4819, { 0xa1, 0xe3, 0x2, 0xe6, 0x4c, 0x68, 0x2a, 0x72 } }
+{ 0xffb42d3c, 0x8281, 0x44c8, \
+  { 0xac, 0xba, 0x73, 0x15, 0x31, 0xaa, 0xe5, 0x07 } }
 
 class nsHTMLCanvasElement;
 class gfxContext;
 class gfxASurface;
 class nsIPropertyBag;
+class nsDisplayListBuilder;
 
 namespace mozilla {
 namespace layers {
 class CanvasLayer;
 class LayerManager;
 }
 namespace ipc {
 class Shmem;
@@ -103,17 +104,18 @@ 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(CanvasLayer *aOldLayer,
+  virtual already_AddRefed<CanvasLayer> GetCanvasLayer(nsDisplayListBuilder* aBuilder,
+                                                       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,16 +53,17 @@
 
 #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
@@ -242,28 +243,28 @@ 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();
+    HTMLCanvasElement()->InvalidateCanvasContent(nsnull);
 }
 
 /* readonly attribute nsIDOMHTMLCanvasElement canvas; */
 NS_IMETHODIMP
 WebGLContext::GetCanvas(nsIDOMHTMLCanvasElement **canvas)
 {
     NS_IF_ADDREF(*canvas = mCanvasElement);
 
@@ -348,16 +349,20 @@ WebGLContext::SetContextOptions(nsIPrope
     return NS_OK;
 }
 
 NS_IMETHODIMP
 WebGLContext::SetDimensions(PRInt32 width, PRInt32 height)
 {
     ScopedGfxFeatureReporter reporter("WebGL");
 
+    if (mCanvasElement) {
+        HTMLCanvasElement()->InvalidateCanvas();
+    }
+
     if (mWidth == width && mHeight == height)
         return NS_OK;
 
     // If we already have a gl context, then we just need to resize
     // FB0.
     if (gl &&
         gl->ResizeOffscreen(gfxIntSize(width, height)))
     {
@@ -614,37 +619,64 @@ 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(CanvasLayer *aOldLayer,
+WebGLContext::GetCanvasLayer(nsDisplayListBuilder* aBuilder,
+                             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;
     }
-    canvasLayer->SetUserData(&gWebGLLayerUserData, 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::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);
@@ -656,19 +688,18 @@ WebGLContext::GetCanvasLayer(CanvasLayer
     }
 
     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(nsIntRect(0, 0, mWidth, mHeight));
+    canvasLayer->Updated();
 
-    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,19 +363,20 @@ 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(CanvasLayer *aOldLayer,
+    already_AddRefed<CanvasLayer> GetCanvasLayer(nsDisplayListBuilder* aBuilder,
+                                                 CanvasLayer *aOldLayer,
                                                  LayerManager *aManager);
-    void MarkContextClean() { }
+    void MarkContextClean() { mInvalidated = PR_FALSE; }
 
     // 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,16 +91,17 @@
 #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"
@@ -402,17 +403,18 @@ 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(CanvasLayer *aOldLayer,
+    already_AddRefed<CanvasLayer> GetCanvasLayer(nsDisplayListBuilder* aBuilder,
+                                                 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);
 
@@ -450,17 +452,16 @@ 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().
@@ -873,16 +874,20 @@ nsCanvasRenderingContext2D::~nsCanvasRen
         sUnpremultiplyTable = nsnull;
         sPremultiplyTable = nsnull;
     }
 }
 
 nsresult
 nsCanvasRenderingContext2D::Reset()
 {
+    if (mCanvasElement) {
+        HTMLCanvasElement()->InvalidateCanvas();
+    }
+
     // only do this for non-docshell created contexts,
     // since those are the ones that we created a surface for
     if (mValid && !mDocShell)
         gCanvasMemoryUsed -= mWidth * mHeight * 4;
 
     mSurface = nsnull;
     mThebes = nsnull;
     mValid = PR_FALSE;
@@ -1059,17 +1064,17 @@ nsCanvasRenderingContext2D::Redraw()
         NS_ASSERTION(mDocShell, "Redraw with no canvas element or docshell!");
         return NS_OK;
     }
 
 #ifdef MOZ_SVG
     nsSVGEffects::InvalidateDirectRenderingObservers(HTMLCanvasElement());
 #endif
 
-    HTMLCanvasElement()->InvalidateFrame();
+    HTMLCanvasElement()->InvalidateCanvasContent(nsnull);
 
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsCanvasRenderingContext2D::Redraw(const gfxRect& r)
 {
     ++mInvalidateCount;
@@ -1086,17 +1091,17 @@ nsCanvasRenderingContext2D::Redraw(const
         NS_ASSERTION(mDocShell, "Redraw with no canvas element or docshell!");
         return NS_OK;
     }
 
 #ifdef MOZ_SVG
     nsSVGEffects::InvalidateDirectRenderingObservers(HTMLCanvasElement());
 #endif
 
-    HTMLCanvasElement()->InvalidateFrame(&r);
+    HTMLCanvasElement()->InvalidateCanvasContent(&r);
 
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsCanvasRenderingContext2D::RedrawUser(const gfxRect& r)
 {
     if (mIsEntireFrameInvalid) {
@@ -1105,18 +1110,16 @@ nsCanvasRenderingContext2D::RedrawUser(c
     }
 
     return Redraw(mThebes->UserToDevice(r));
 }
 
 NS_IMETHODIMP
 nsCanvasRenderingContext2D::SetDimensions(PRInt32 width, PRInt32 height)
 {
-    Reset();
-
     nsRefPtr<gfxASurface> surface;
 
     // Check that the dimensions are sane
     gfxIntSize size(width, height);
     if (gfxASurface::CheckSurfaceSize(size, 0xffff)) {
 
         gfxASurface::gfxImageFormat format = GetImageFormat();
 
@@ -1412,37 +1415,37 @@ nsCanvasRenderingContext2D::Scale(float 
     mThebes->Scale(x, y);
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsCanvasRenderingContext2D::Rotate(float angle)
 {
     if (!FloatValidate(angle))
-        return NS_ERROR_DOM_SYNTAX_ERR;
+        return NS_OK;
 
     mThebes->Rotate(angle);
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsCanvasRenderingContext2D::Translate(float x, float y)
 {
     if (!FloatValidate(x,y))
-        return NS_ERROR_DOM_SYNTAX_ERR;
+        return NS_OK;
 
     mThebes->Translate(gfxPoint(x, y));
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsCanvasRenderingContext2D::Transform(float m11, float m12, float m21, float m22, float dx, float dy)
 {
     if (!FloatValidate(m11,m12,m21,m22,dx,dy))
-        return NS_ERROR_DOM_SYNTAX_ERR;
+        return NS_OK;
 
     gfxMatrix matrix(m11, m12, m21, m22, dx, dy);
     mThebes->Multiply(matrix);
 
     return NS_OK;
 }
 
 NS_IMETHODIMP
@@ -1459,21 +1462,17 @@ nsCanvasRenderingContext2D::SetTransform
 
 //
 // colors
 //
 
 NS_IMETHODIMP
 nsCanvasRenderingContext2D::SetGlobalAlpha(float aGlobalAlpha)
 {
-    if (!FloatValidate(aGlobalAlpha))
-        return NS_ERROR_DOM_SYNTAX_ERR;
-
-    // ignore invalid values, as per spec
-    if (aGlobalAlpha < 0.0 || aGlobalAlpha > 1.0)
+    if (!FloatValidate(aGlobalAlpha) || aGlobalAlpha < 0.0 || aGlobalAlpha > 1.0)
         return NS_OK;
 
     CurrentState().globalAlpha = aGlobalAlpha;
     DirtyAllStyles();
 
     return NS_OK;
 }
 
@@ -1653,16 +1652,19 @@ nsCanvasRenderingContext2D::CreateLinear
 
 NS_IMETHODIMP
 nsCanvasRenderingContext2D::CreateRadialGradient(float x0, float y0, float r0, float x1, float y1, float r1,
                                                  nsIDOMCanvasGradient **_retval)
 {
     if (!FloatValidate(x0,y0,r0,x1,y1,r1))
         return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
 
+    if (r0 < 0.0 || r1 < 0.0)
+        return NS_ERROR_DOM_INDEX_SIZE_ERR;
+
     nsRefPtr<gfxPattern> gradpat = new gfxPattern(x0, y0, r0, x1, y1, r1);
     if (!gradpat)
         return NS_ERROR_OUT_OF_MEMORY;
 
     nsRefPtr<nsIDOMCanvasGradient> grad = new nsCanvasGradient(gradpat);
     if (!grad)
         return NS_ERROR_OUT_OF_MEMORY;
 
@@ -1670,16 +1672,19 @@ nsCanvasRenderingContext2D::CreateRadial
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsCanvasRenderingContext2D::CreatePattern(nsIDOMHTMLElement *image,
                                           const nsAString& repeat,
                                           nsIDOMCanvasPattern **_retval)
 {
+    if (!image) {
+        return NS_ERROR_DOM_TYPE_MISMATCH_ERR;
+    }
     gfxPattern::GraphicsExtend extend;
 
     if (repeat.IsEmpty() || repeat.EqualsLiteral("repeat")) {
         extend = gfxPattern::EXTEND_REPEAT;
     } else if (repeat.EqualsLiteral("repeat-x")) {
         // XX
         extend = gfxPattern::EXTEND_REPEAT;
     } else if (repeat.EqualsLiteral("repeat-y")) {
@@ -1715,51 +1720,52 @@ nsCanvasRenderingContext2D::CreatePatter
 
 //
 // shadows
 //
 NS_IMETHODIMP
 nsCanvasRenderingContext2D::SetShadowOffsetX(float x)
 {
     if (!FloatValidate(x))
-        return NS_ERROR_DOM_SYNTAX_ERR;
+        return NS_OK;
+
     CurrentState().shadowOffset.x = x;
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsCanvasRenderingContext2D::GetShadowOffsetX(float *x)
 {
     *x = static_cast<float>(CurrentState().shadowOffset.x);
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsCanvasRenderingContext2D::SetShadowOffsetY(float y)
 {
     if (!FloatValidate(y))
-        return NS_ERROR_DOM_SYNTAX_ERR;
+        return NS_OK;
+
     CurrentState().shadowOffset.y = y;
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsCanvasRenderingContext2D::GetShadowOffsetY(float *y)
 {
     *y = static_cast<float>(CurrentState().shadowOffset.y);
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsCanvasRenderingContext2D::SetShadowBlur(float blur)
 {
-    if (!FloatValidate(blur))
-        return NS_ERROR_DOM_SYNTAX_ERR;
-    if (blur < 0.0)
+    if (!FloatValidate(blur) || blur < 0.0)
         return NS_OK;
+
     CurrentState().shadowBlur = blur;
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsCanvasRenderingContext2D::GetShadowBlur(float *blur)
 {
     *blur = CurrentState().shadowBlur;
@@ -1973,17 +1979,17 @@ nsCanvasRenderingContext2D::DrawPath(Sty
 //
 // rects
 //
 
 NS_IMETHODIMP
 nsCanvasRenderingContext2D::ClearRect(float x, float y, float w, float h)
 {
     if (!FloatValidate(x,y,w,h))
-        return NS_ERROR_DOM_SYNTAX_ERR;
+        return NS_OK;
 
     PathAutoSaveRestore pathSR(this);
     gfxContextAutoSaveRestore autoSR(mThebes);
 
     mThebes->SetOperator(gfxContext::OPERATOR_CLEAR);
     mThebes->NewPath();
     mThebes->Rectangle(gfxRect(x, y, w, h));
     mThebes->Fill();
@@ -2067,39 +2073,39 @@ nsCanvasRenderingContext2D::Clip()
     mThebes->Clip();
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsCanvasRenderingContext2D::MoveTo(float x, float y)
 {
     if (!FloatValidate(x,y))
-        return NS_ERROR_DOM_SYNTAX_ERR;
+        return NS_OK;
 
     mHasPath = PR_TRUE;
     mThebes->MoveTo(gfxPoint(x, y));
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsCanvasRenderingContext2D::LineTo(float x, float y)
 {
     if (!FloatValidate(x,y))
-        return NS_ERROR_DOM_SYNTAX_ERR;
+        return NS_OK;
 
     mHasPath = PR_TRUE;
     mThebes->LineTo(gfxPoint(x, y));
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsCanvasRenderingContext2D::QuadraticCurveTo(float cpx, float cpy, float x, float y)
 {
     if (!FloatValidate(cpx,cpy,x,y))
-        return NS_ERROR_DOM_SYNTAX_ERR;
+        return NS_OK;
 
     // we will always have a current point, since beginPath forces
     // a moveto(0,0)
     gfxPoint c = mThebes->CurrentPoint();
     gfxPoint p(x,y);
     gfxPoint cp(cpx, cpy);
 
     mHasPath = PR_TRUE;
@@ -2201,17 +2207,17 @@ nsCanvasRenderingContext2D::Arc(float x,
         mThebes->Arc(p, r, startAngle, endAngle);
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsCanvasRenderingContext2D::Rect(float x, float y, float w, float h)
 {
     if (!FloatValidate(x,y,w,h))
-        return NS_ERROR_DOM_SYNTAX_ERR;
+        return NS_OK;
 
     mHasPath = PR_TRUE;
     mThebes->Rectangle(gfxRect(x, y, w, h));
     return NS_OK;
 }
 
 //
 // text
@@ -3360,17 +3366,19 @@ bitblt(gfxImageSurface *s, int src_x, in
 //   -- render the region defined by (sx,sy,sw,wh) in image-local space into the region (dx,dy,dw,dh) on the canvas
 
 NS_IMETHODIMP
 nsCanvasRenderingContext2D::DrawImage(nsIDOMElement *imgElt, float a1,
                                       float a2, float a3, float a4, float a5,
                                       float a6, float a7, float a8,
                                       PRUint8 optional_argc)
 {
-    NS_ENSURE_ARG(imgElt);
+    if (!imgElt) {
+        return NS_ERROR_DOM_TYPE_MISMATCH_ERR;
+    }
 
     double sx,sy,sw,sh;
     double dx,dy,dw,dh;
 
     gfxMatrix matrix;
     nsRefPtr<gfxPattern> pattern;
     gfxIntSize imgSize;
     nsRefPtr<gfxASurface> imgsurf =
@@ -3435,34 +3443,57 @@ nsCanvasRenderingContext2D::DrawImage(ns
         dy = a6;
         dw = a7;
         dh = a8;
     } else {
         // XXX ERRMSG we need to report an error to developers here! (bug 329026)
         return NS_ERROR_INVALID_ARG;
     }
 
+    if (sw == 0.0 || sh == 0.0) {
+        // zero-sized source -- failure !?
+        return NS_ERROR_DOM_INDEX_SIZE_ERR;
+    }
+
     if (dw == 0.0 || dh == 0.0) {
         // not really failure, but nothing to do --
         // and noone likes a divide-by-zero
         return NS_OK;
     }
 
-    if (!FloatValidate(sx, sy, sw, sh) || !FloatValidate(dx, dy, dw, dh)) {
+    // The following check might do the validation of the float arguments:
+    //   (!FloatValidate(sx, sy, sw, sh) || !FloatValidate(dx, dy, dw, dh))
+    // but we would also need to validate some sums for overflow (e.g. sx + sw).
+    if (!FloatValidate(sx + sw, sy + sh, dx + dw, dy + dh)) {
         return NS_ERROR_DOM_SYNTAX_ERR;
     }
 
-    // check args
-    if (sx < 0.0 || sy < 0.0 ||
-        sw < 0.0 || sw > (double) imgSize.width ||
-        sh < 0.0 || sh > (double) imgSize.height ||
-        dw < 0.0 || dh < 0.0)
-    {
-        // XXX ERRMSG we need to report an error to developers here! (bug 329026)
-        return NS_ERROR_DOM_INDEX_SIZE_ERR;
+    // Handle negative sw, sh, dw and dh by flipping the rectangle over in the
+    // relevant direction.
+    if (sw < 0.0) {
+      sx += sw;
+      sw = -sw;
+    }
+    if (sh < 0.0) {
+      sy += sh;
+      sh = -sh;
+    }
+    if (dw < 0.0) {
+      dx += dw;
+      dw = -dw;
+    }
+    if (dh < 0.0) {
+      dy += dh;
+      dh = -dh;
+    }
+
+    // Checking source image boundaries.
+    if (sx < 0 || sx + sw > (double) imgSize.width || 
+        sy < 0 || sy + sh > (double) imgSize.height) {
+      return NS_ERROR_DOM_INDEX_SIZE_ERR;
     }
 
     matrix.Translate(gfxPoint(sx, sy));
     matrix.Scale(sw/dw, sh/dh);
 #ifdef WINCE
     /* cairo doesn't have consistent semantics for drawing a surface onto
      * itself. Specifically, pixman will not preserve the contents when doing
      * the copy. So to get the desired semantics a temporary copy would be needed.
@@ -4094,59 +4125,81 @@ 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(CanvasLayer *aOldLayer,
+nsCanvasRenderingContext2D::GetCanvasLayer(nsDisplayListBuilder* aBuilder,
+                                           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;
     }
-    canvasLayer->SetUserData(&g2DContextLayerUserData, 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::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(nsIntRect(0, 0, mWidth, mHeight));
+    canvasLayer->Updated();
 
     mResetLayer = PR_FALSE;
 
-    MarkContextClean();
-
-    return canvasLayer.forget().get();
+    return canvasLayer.forget();
 }
 
 void
 nsCanvasRenderingContext2D::MarkContextClean()
 {
     if (mInvalidateCount > 0) {
         mPredictManyRedrawCalls = mInvalidateCount > kCanvasMaxInvalidateCount;
     }
--- a/content/canvas/test/Makefile.in
+++ b/content/canvas/test/Makefile.in
@@ -10,17 +10,17 @@
 # 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.org code.
 #
 # The Initial Developer of the Original Code is
-# Mozilla Corporation.
+# the Mozilla Foundation.
 # Portions created by the Initial Developer are Copyright (C) 2007
 # the Initial Developer. All Rights Reserved.
 #
 # Contributor(s):
 #   Philip Taylor <philip.taylor@cl.cam.ac.uk>
 #
 # 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"),
@@ -74,16 +74,17 @@ include $(topsrcdir)/config/rules.mk
 	test_2d.composite.canvas.source-out.html \
 	test_2d.composite.image.destination-atop.html \
 	test_2d.composite.image.destination-in.html \
 	test_2d.composite.image.source-in.html \
 	test_2d.composite.image.source-out.html \
 	test_2d.composite.uncovered.image.destination-in.html \
 	test_2d.composite.uncovered.image.source-in.html \
 	test_2d.composite.uncovered.image.source-out.html \
+	test_toDataURL_lowercase_ascii.html \
 	test_mozGetAsFile.html \
 	test_canvas_strokeStyle_getter.html \
 	test_bug613794.html \
 	test_drawImage_edge_cases.html \
 	$(NULL)
 
 ifneq (1_Linux,$(MOZ_SUITE)_$(OS_ARCH))
 # This test fails in Suite on Linux for some reason, disable it there
--- a/content/canvas/test/test_canvas.html
+++ b/content/canvas/test/test_canvas.html
@@ -240,17 +240,17 @@ ctx.clearRect(0, Infinity, Infinity, Inf
 ctx.clearRect(0, Infinity, 100, Infinity);
 ctx.clearRect(0, 0, Infinity, Infinity);
 
 isPixel(ctx, 50,25, 0,255,0,255, 0);
 
 } catch (e) {
     _thrown_outer = true;
 }
-todo(!_thrown_outer, 'should not throw exception');
+ok(!_thrown_outer, 'should not throw exception');
 
 
 }
 </script>
 
 <!-- [[[ test_2d.clearRect.path.html ]]] -->
 
 <p>Canvas test: 2d.clearRect.path</p>
@@ -1141,29 +1141,22 @@ isPixel(ctx, 50,25, 2,253,0,255, 2);
 
 function test_2d_composite_globalAlpha_invalid() {
 
 var canvas = document.getElementById('c41');
 var ctx = canvas.getContext('2d');
 
 ctx.globalAlpha = 0.5;
 var a = ctx.globalAlpha; // might not be exactly 0.5, if it is rounded/quantised, so remember for future comparisons
-var _thrown = undefined; try {
-  ctx.globalAlpha = Infinity;
-} catch (e) { _thrown = e }; todo(_thrown && _thrown.code == DOMException.NOT_SUPPORTED_ERR, "should throw NOT_SUPPORTED_ERR");
-ok(ctx.globalAlpha == a, "ctx.globalAlpha == a");
-var _thrown = undefined; try {
-  ctx.globalAlpha = -Infinity;
-} catch (e) { _thrown = e }; todo(_thrown && _thrown.code == DOMException.NOT_SUPPORTED_ERR, "should throw NOT_SUPPORTED_ERR");
-ok(ctx.globalAlpha == a, "ctx.globalAlpha == a");
-var _thrown = undefined; try {
-  ctx.globalAlpha = NaN;
-} catch (e) { _thrown = e }; todo(_thrown && _thrown.code == DOMException.NOT_SUPPORTED_ERR, "should throw NOT_SUPPORTED_ERR");
-ok(ctx.globalAlpha == a, "ctx.globalAlpha == a");
-
+ctx.globalAlpha = Infinity;
+ok(ctx.globalAlpha === a, "ctx.globalAlpha === a");
+ctx.globalAlpha = -Infinity;
+ok(ctx.globalAlpha === a, "ctx.globalAlpha === a");
+ctx.globalAlpha = NaN;
+ok(ctx.globalAlpha === a, "ctx.globalAlpha === a");
 
 }
 </script>
 
 <!-- [[[ test_2d.composite.globalAlpha.range.html ]]] -->
 
 <p>Canvas test: 2d.composite.globalAlpha.range</p>
 <canvas id="c42" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
@@ -3033,51 +3026,75 @@ todo(img.complete === false, "img.comple
 var _thrown = undefined; try {
   ctx.drawImage(img, 0, 0);
 } catch (e) { _thrown = e }; todo(_thrown && _thrown.code == DOMException.INVALID_STATE_ERR, "should throw INVALID_STATE_ERR");
 
 
 }
 </script>
 
+<!-- [[[ test_2d.drawImage.negativedir.html ]]] -->
+
+<p>Canvas test: 2d.drawImage.negativedir</p>
+<canvas id="c117a" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<script>
+
+
+function test_2d_drawImage_negativedir() {
+
+var canvas = document.getElementById('c117a');
+var ctx = canvas.getContext('2d');
+
+ctx.fillStyle = '#f00';
+ctx.fillRect(0, 0, 100, 50);
+ctx.drawImage(document.getElementById('ggrr-256x256_0.png'), 0, 178, 50, -100, 0, 0, 50, 100);
+ctx.drawImage(document.getElementById('ggrr-256x256_0.png'), 0, 78, 50, 100, 50, 100, 50, -100);
+isPixel(ctx, 1,1, 0,255,0,255, 2);
+isPixel(ctx, 1,48, 0,255,0,255, 2);
+isPixel(ctx, 98,1, 0,255,0,255, 2);
+isPixel(ctx, 98,48, 0,255,0,255, 2);
+isPixel(ctx, 48,1, 0,255,0,255, 2);
+isPixel(ctx, 48,48, 0,255,0,255, 2);
+isPixel(ctx, 51,1, 0,255,0,255, 2);
+isPixel(ctx, 51,48, 0,255,0,255, 2);
+isPixel(ctx, 25,25, 0,255,0,255, 2);
+isPixel(ctx, 75,25, 0,255,0,255, 2);
+
+
+}
+</script>
+<img src="image_ggrr-256x256.png" id="ggrr-256x256_0.png" class="resource">
+
 <!-- [[[ test_2d.drawImage.negativedest.html ]]] -->
 
 <p>Canvas test: 2d.drawImage.negativedest</p>
 <canvas id="c117" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
 <script>
 
 
 function test_2d_drawImage_negativedest() {
 
 var canvas = document.getElementById('c117');
 var ctx = canvas.getContext('2d');
 
-var _thrown_outer = false;
-try {
-
 ctx.fillStyle = '#f00';
 ctx.fillRect(0, 0, 100, 50);
 ctx.drawImage(document.getElementById('ggrr-256x256_1.png'), 100, 78, 50, 50, 0, 50, 50, -50);
 ctx.drawImage(document.getElementById('ggrr-256x256_1.png'), 100, 128, 50, -50, 100, 50, -50, -50);
 isPixel(ctx, 1,1, 0,255,0,255, 2);
 isPixel(ctx, 1,48, 0,255,0,255, 2);
 isPixel(ctx, 98,1, 0,255,0,255, 2);
 isPixel(ctx, 98,48, 0,255,0,255, 2);
 isPixel(ctx, 48,1, 0,255,0,255, 2);
 isPixel(ctx, 48,48, 0,255,0,255, 2);
 isPixel(ctx, 51,1, 0,255,0,255, 2);
 isPixel(ctx, 51,48, 0,255,0,255, 2);
 isPixel(ctx, 25,25, 0,255,0,255, 2);
 isPixel(ctx, 75,25, 0,255,0,255, 2);
 
-} catch (e) {
-    _thrown_outer = true;
-}
-todo(!_thrown_outer, 'should not throw exception');
-
 
 }
 </script>
 <img src="image_ggrr-256x256.png" id="ggrr-256x256_1.png" class="resource">
 
 <!-- [[[ test_2d.drawImage.negativesource.html ]]] -->
 
 <p>Canvas test: 2d.drawImage.negativesource</p>
@@ -3085,39 +3102,31 @@ todo(!_thrown_outer, 'should not throw e
 <script>
 
 
 function test_2d_drawImage_negativesource() {
 
 var canvas = document.getElementById('c118');
 var ctx = canvas.getContext('2d');
 
-var _thrown_outer = false;
-try {
-
 ctx.fillStyle = '#f00';
 ctx.fillRect(0, 0, 100, 50);
 ctx.drawImage(document.getElementById('ggrr-256x256_2.png'), 100, 78, -100, 50, 0, 0, 50, 50);
 ctx.drawImage(document.getElementById('ggrr-256x256_2.png'), 100, 128, -100, -50, 50, 0, 50, 50);
 isPixel(ctx, 1,1, 0,255,0,255, 2);
 isPixel(ctx, 1,48, 0,255,0,255, 2);
 isPixel(ctx, 98,1, 0,255,0,255, 2);
 isPixel(ctx, 98,48, 0,255,0,255, 2);
 isPixel(ctx, 48,1, 0,255,0,255, 2);
 isPixel(ctx, 48,48, 0,255,0,255, 2);
 isPixel(ctx, 51,1, 0,255,0,255, 2);
 isPixel(ctx, 51,48, 0,255,0,255, 2);
 isPixel(ctx, 25,25, 0,255,0,255, 2);
 isPixel(ctx, 75,25, 0,255,0,255, 2);
 
-} catch (e) {
-    _thrown_outer = true;
-}
-todo(!_thrown_outer, 'should not throw exception');
-
 
 }
 </script>
 <img src="image_ggrr-256x256.png" id="ggrr-256x256_2.png" class="resource">
 
 <!-- [[[ test_2d.drawImage.nonfinite.html ]]] -->
 
 <p>Canvas test: 2d.drawImage.nonfinite</p>
@@ -3483,17 +3492,17 @@ isPixel(ctx, 55,25, 0,255,0,255, 2);
 
 function test_2d_drawImage_null() {
 
 var canvas = document.getElementById('c121');
 var ctx = canvas.getContext('2d');
 
 var _thrown = undefined; try {
   ctx.drawImage(null, 0, 0);
-} catch (e) { _thrown = e }; todo(_thrown && _thrown.code == DOMException.TYPE_MISMATCH_ERR, "should throw TYPE_MISMATCH_ERR");
+} catch (e) { _thrown = e }; ok(_thrown && _thrown.code == DOMException.TYPE_MISMATCH_ERR, "should throw TYPE_MISMATCH_ERR");
 
 
 }
 </script>
 
 <!-- [[[ test_2d.drawImage.outsidesource.html ]]] -->
 
 <p>Canvas test: 2d.drawImage.outsidesource</p>
@@ -3502,52 +3511,45 @@ var _thrown = undefined; try {
 
 
 
 function test_2d_drawImage_outsidesource() {
 
 var canvas = document.getElementById('c122');
 var ctx = canvas.getContext('2d');
 
-var _thrown_outer = false;
-try {
-
 ctx.drawImage(document.getElementById('green_7.png'), 10.5, 10.5, 89.5, 39.5, 0, 0, 100, 50);
 ctx.drawImage(document.getElementById('green_7.png'), 5.5, 5.5, -5.5, -5.5, 0, 0, 100, 50);
 ctx.drawImage(document.getElementById('green_7.png'), 100, 50, -5, -5, 0, 0, 100, 50);
+
 var _thrown = undefined; try {
   ctx.drawImage(document.getElementById('red_11.png'), -0.001, 0, 100, 50, 0, 0, 100, 50);
 } catch (e) { _thrown = e }; ok(_thrown && _thrown.code == DOMException.INDEX_SIZE_ERR, "should throw INDEX_SIZE_ERR");
 var _thrown = undefined; try {
   ctx.drawImage(document.getElementById('red_11.png'), 0, -0.001, 100, 50, 0, 0, 100, 50);
 } catch (e) { _thrown = e }; ok(_thrown && _thrown.code == DOMException.INDEX_SIZE_ERR, "should throw INDEX_SIZE_ERR");
 var _thrown = undefined; try {
   ctx.drawImage(document.getElementById('red_11.png'), 0, 0, 100.001, 50, 0, 0, 100, 50);
 } catch (e) { _thrown = e }; ok(_thrown && _thrown.code == DOMException.INDEX_SIZE_ERR, "should throw INDEX_SIZE_ERR");
 var _thrown = undefined; try {
   ctx.drawImage(document.getElementById('red_11.png'), 0, 0, 100, 50.001, 0, 0, 100, 50);
 } catch (e) { _thrown = e }; ok(_thrown && _thrown.code == DOMException.INDEX_SIZE_ERR, "should throw INDEX_SIZE_ERR");
 var _thrown = undefined; try {
   ctx.drawImage(document.getElementById('red_11.png'), 50, 0, 50.001, 50, 0, 0, 100, 50);
-} catch (e) { _thrown = e }; todo(_thrown && _thrown.code == DOMException.INDEX_SIZE_ERR, "should throw INDEX_SIZE_ERR");
+} catch (e) { _thrown = e }; ok(_thrown && _thrown.code == DOMException.INDEX_SIZE_ERR, "should throw INDEX_SIZE_ERR");
 var _thrown = undefined; try {
   ctx.drawImage(document.getElementById('red_11.png'), 0, 0, -5, 5, 0, 0, 100, 50);
 } catch (e) { _thrown = e }; ok(_thrown && _thrown.code == DOMException.INDEX_SIZE_ERR, "should throw INDEX_SIZE_ERR");
 var _thrown = undefined; try {
   ctx.drawImage(document.getElementById('red_11.png'), 0, 0, 5, -5, 0, 0, 100, 50);
 } catch (e) { _thrown = e }; ok(_thrown && _thrown.code == DOMException.INDEX_SIZE_ERR, "should throw INDEX_SIZE_ERR");
 var _thrown = undefined; try {
   ctx.drawImage(document.getElementById('red_11.png'), 110, 60, -20, -20, 0, 0, 100, 50);
 } catch (e) { _thrown = e }; ok(_thrown && _thrown.code == DOMException.INDEX_SIZE_ERR, "should throw INDEX_SIZE_ERR");
-todo_isPixel(ctx, 50,25, 0,255,0,255, 2);
-
-} catch (e) {
-    _thrown_outer = true;
-}
-todo(!_thrown_outer, 'should not throw exception');
+isPixel(ctx, 50,25, 0,255,0,255, 2);
 
 
 }
 </script>
 <img src="image_green.png" id="green_7.png" class="resource">
 <img src="image_red.png" id="red_11.png" class="resource">
 
 <!-- [[[ test_2d.drawImage.path.html ]]] -->
@@ -3662,17 +3664,17 @@ function test_2d_drawImage_wrongtype() {
 
 netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
 
 var canvas = document.getElementById('c127');
 var ctx = canvas.getContext('2d');
 
 var _thrown = undefined; try {
   ctx.drawImage(undefined, 0, 0);
-} catch (e) { _thrown = e }; ok(_thrown && _thrown.result == Components.results.NS_ERROR_INVALID_ARG, "should throw NS_ERROR_INVALID_ARG");
+} catch (e) { _thrown = e }; ok(_thrown && _thrown.code == DOMException.TYPE_MISMATCH_ERR, "should throw TYPE_MISMATCH_ERR");
 var _thrown = undefined; try {
   ctx.drawImage(0, 0, 0);
 } catch (e) { _thrown = e }; ok(_thrown && _thrown.result == Components.results.NS_ERROR_XPC_BAD_CONVERT_JS_ZERO_ISNOT_NULL, "should throw NS_ERROR_XPC_BAD_CONVERT_JS_ZERO_ISNOT_NULL");
 var _thrown = undefined; try {
   ctx.drawImage("", 0, 0);
 } catch (e) { _thrown = e }; ok(_thrown && _thrown.result == Components.results.NS_ERROR_XPC_BAD_CONVERT_JS, "should throw NS_ERROR_XPC_BAD_CONVERT_JS");
 var _thrown = undefined; try {
   ctx.drawImage(document.createElement('p'), 0, 0);
@@ -3694,23 +3696,23 @@ function test_2d_drawImage_zerosource() 
 
 var canvas = document.getElementById('c128');
 var ctx = canvas.getContext('2d');
 
 ctx.fillStyle = '#0f0';
 ctx.fillRect(0, 0, 100, 50);
 var _thrown = undefined; try {
   ctx.drawImage(document.getElementById('red_14.png'), 10, 10, 0, 1, 0, 0, 100, 50);
-} catch (e) { _thrown = e }; todo(_thrown && _thrown.code == DOMException.INDEX_SIZE_ERR, "should throw INDEX_SIZE_ERR");
+} catch (e) { _thrown = e }; ok(_thrown && _thrown.code == DOMException.INDEX_SIZE_ERR, "should throw INDEX_SIZE_ERR");
 var _thrown = undefined; try {
   ctx.drawImage(document.getElementById('red_14.png'), 10, 10, 1, 0, 0, 0, 100, 50);
-} catch (e) { _thrown = e }; todo(_thrown && _thrown.code == DOMException.INDEX_SIZE_ERR, "should throw INDEX_SIZE_ERR");
+} catch (e) { _thrown = e }; ok(_thrown && _thrown.code == DOMException.INDEX_SIZE_ERR, "should throw INDEX_SIZE_ERR");
 var _thrown = undefined; try {
   ctx.drawImage(document.getElementById('red_14.png'), 10, 10, 0, 0, 0, 0, 100, 50);
-} catch (e) { _thrown = e }; todo(_thrown && _thrown.code == DOMException.INDEX_SIZE_ERR, "should throw INDEX_SIZE_ERR");
+} catch (e) { _thrown = e }; ok(_thrown && _thrown.code == DOMException.INDEX_SIZE_ERR, "should throw INDEX_SIZE_ERR");
 isPixel(ctx, 50,25, 0,255,0,255, 2);
 
 
 }
 </script>
 <img src="image_red.png" id="red_14.png" class="resource">
 
 <!-- [[[ test_2d.fillRect.basic.html ]]] -->
@@ -6640,23 +6642,23 @@ isPixel(ctx, 98,48, 0,255,0,255, 0);
 
 function test_2d_gradient_radial_negative() {
 
 var canvas = document.getElementById('c243');
 var ctx = canvas.getContext('2d');
 
 var _thrown = undefined; try {
   ctx.createRadialGradient(0, 0, -0.1, 0, 0, 1);
-} catch (e) { _thrown = e }; todo(_thrown && _thrown.code == DOMException.INDEX_SIZE_ERR, "should throw INDEX_SIZE_ERR");
+} catch (e) { _thrown = e }; ok(_thrown && _thrown.code == DOMException.INDEX_SIZE_ERR, "should throw INDEX_SIZE_ERR");
 var _thrown = undefined; try {
   ctx.createRadialGradient(0, 0, 1, 0, 0, -0.1);
-} catch (e) { _thrown = e }; todo(_thrown && _thrown.code == DOMException.INDEX_SIZE_ERR, "should throw INDEX_SIZE_ERR");
+} catch (e) { _thrown = e }; ok(_thrown && _thrown.code == DOMException.INDEX_SIZE_ERR, "should throw INDEX_SIZE_ERR");
 var _thrown = undefined; try {
   ctx.createRadialGradient(0, 0, -0.1, 0, 0, -0.1);
-} catch (e) { _thrown = e }; todo(_thrown && _thrown.code == DOMException.INDEX_SIZE_ERR, "should throw INDEX_SIZE_ERR");
+} catch (e) { _thrown = e }; ok(_thrown && _thrown.code == DOMException.INDEX_SIZE_ERR, "should throw INDEX_SIZE_ERR");
 
 
 }
 </script>
 
 <!-- [[[ test_2d.gradient.radial.nonfinite.html ]]] -->
 
 <p>Canvas test: 2d.gradient.radial.nonfinite</p>
@@ -13213,17 +13215,17 @@ ctx.lineTo(0, 50);
 ctx.fillStyle = '#0f0';
 ctx.fill();
 isPixel(ctx, 50,25, 0,255,0,255, 0);
 isPixel(ctx, 90,45, 0,255,0,255, 0);
 
 } catch (e) {
     _thrown_outer = true;
 }
-todo(!_thrown_outer, 'should not throw exception');
+ok(!_thrown_outer, 'should not throw exception');
 
 
 }
 </script>
 
 <!-- [[[ test_2d.path.moveTo.basic.html ]]] -->
 
 <p>Canvas test: 2d.path.moveTo.basic</p>
@@ -13328,17 +13330,17 @@ ctx.lineTo(100, 50);
 ctx.lineTo(0, 50);
 ctx.fillStyle = '#0f0';
 ctx.fill();
 isPixel(ctx, 50,25, 0,255,0,255, 0);
 
 } catch (e) {
     _thrown_outer = true;
 }
-todo(!_thrown_outer, 'should not throw exception');
+ok(!_thrown_outer, 'should not throw exception');
 
 
 }
 </script>
 
 <!-- [[[ test_2d.path.quadraticCurveTo.basic.html ]]] -->
 
 <p>Canvas test: 2d.path.quadraticCurveTo.basic</p>
@@ -13434,17 +13436,17 @@ ctx.lineTo(0, 50);
 ctx.fillStyle = '#0f0';
 ctx.fill();
 isPixel(ctx, 50,25, 0,255,0,255, 0);
 isPixel(ctx, 90,45, 0,255,0,255, 0);
 
 } catch (e) {
     _thrown_outer = true;
 }
-todo(!_thrown_outer, 'should not throw exception');
+ok(!_thrown_outer, 'should not throw exception');
 
 
 }
 </script>
 
 <!-- [[[ test_2d.path.quadraticCurveTo.scaled.html ]]] -->
 
 <p>Canvas test: 2d.path.quadraticCurveTo.scaled</p>
@@ -13698,17 +13700,17 @@ ctx.lineTo(0, 50);
 ctx.fillStyle = '#0f0';
 ctx.fill();
 isPixel(ctx, 50,25, 0,255,0,255, 0);
 isPixel(ctx, 90,45, 0,255,0,255, 0);
 
 } catch (e) {
     _thrown_outer = true;
 }
-todo(!_thrown_outer, 'should not throw exception');
+ok(!_thrown_outer, 'should not throw exception');
 
 
 }
 </script>
 
 <!-- [[[ test_2d.path.rect.selfintersect.html ]]] -->
 
 <p>Canvas test: 2d.path.rect.selfintersect</p>
@@ -14761,17 +14763,17 @@ var _thrown = undefined; try {
 
 function test_2d_pattern_image_null() {
 
 var canvas = document.getElementById('c467');
 var ctx = canvas.getContext('2d');
 
 var _thrown = undefined; try {
   ctx.createPattern(null, 'repeat');
-} catch (e) { _thrown = e }; todo(_thrown && _thrown.code == DOMException.TYPE_MISMATCH_ERR, "should throw TYPE_MISMATCH_ERR");
+} catch (e) { _thrown = e }; ok(_thrown && _thrown.code == DOMException.TYPE_MISMATCH_ERR, "should throw TYPE_MISMATCH_ERR");
 
 
 }
 </script>
 
 <!-- [[[ test_2d.pattern.image.string.html ]]] -->
 
 <p>Canvas test: 2d.pattern.image.string</p>
@@ -14799,17 +14801,17 @@ var _thrown = undefined; try {
 
 function test_2d_pattern_image_undefined() {
 
 var canvas = document.getElementById('c469');
 var ctx = canvas.getContext('2d');
 
 var _thrown = undefined; try {
   ctx.createPattern(undefined, 'repeat');
-} catch (e) { _thrown = e }; todo(_thrown && _thrown.code == DOMException.TYPE_MISMATCH_ERR, "should throw TYPE_MISMATCH_ERR");
+} catch (e) { _thrown = e }; ok(_thrown && _thrown.code == DOMException.TYPE_MISMATCH_ERR, "should throw TYPE_MISMATCH_ERR");
 
 
 }
 </script>
 
 <!-- [[[ test_2d.pattern.modify.canvas1.html ]]] -->
 
 <p>Canvas test: 2d.pattern.modify.canvas1</p>
@@ -15871,26 +15873,31 @@ ok(ctx.shadowBlur === 0, "ctx.shadowBlur
 <canvas id="c505" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
 <script>
 
 function test_2d_shadow_attributes_shadowBlur_2() {
 
 var canvas = document.getElementById('c505');
 var ctx = canvas.getContext('2d');
 
-var _thrown = undefined; try {
-  ctx.shadowBlur = Infinity;
-} catch (e) { _thrown = e }; todo(_thrown && _thrown.code == DOMException.NOT_SUPPORTED_ERR, "should throw NOT_SUPPORTED_ERR");
-var _thrown = undefined; try {
-  ctx.shadowBlur = -Infinity;
-} catch (e) { _thrown = e }; todo(_thrown && _thrown.code == DOMException.NOT_SUPPORTED_ERR, "should throw NOT_SUPPORTED_ERR");
-var _thrown = undefined; try {
-  ctx.shadowBlur = NaN;
-} catch (e) { _thrown = e }; todo(_thrown && _thrown.code == DOMException.NOT_SUPPORTED_ERR, "should throw NOT_SUPPORTED_ERR");
-
+ctx.shadowBlur = 1;
+ctx.shadowBlur = -2;
+ok(ctx.shadowBlur === 1, "ctx.shadowBlur === 1");
+
+ctx.shadowBlur = 1;
+ctx.shadowBlur = Infinity;
+ok(ctx.shadowBlur === 1, "ctx.shadowBlur === 1");
+
+ctx.shadowBlur = 1;
+ctx.shadowBlur = -Infinity;
+ok(ctx.shadowBlur === 1, "ctx.shadowBlur === 1");
+
+ctx.shadowBlur = 1;
+ctx.shadowBlur = NaN;
+ok(ctx.shadowBlur === 1, "ctx.shadowBlur === 1");
 
 }
 </script>
 
 <!-- [[[ test_2d.shadow.attributes.shadowColor.1.html ]]] -->
 
 <p>Canvas test: 2d.shadow.attributes.shadowColor.1</p>
 <canvas id="c506" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
@@ -15971,35 +15978,36 @@ ok(ctx.shadowOffsetY === 1e6, "ctx.shado
 <canvas id="c509" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
 <script>
 
 function test_2d_shadow_attributes_shadowOffset_2() {
 
 var canvas = document.getElementById('c509');
 var ctx = canvas.getContext('2d');
 
-var _thrown = undefined; try {
-  ctx.shadowOffsetX = Infinity;
-} catch (e) { _thrown = e }; todo(_thrown && _thrown.code == DOMException.NOT_SUPPORTED_ERR, "should throw NOT_SUPPORTED_ERR");
-var _thrown = undefined; try {
-  ctx.shadowOffsetX = -Infinity;
-} catch (e) { _thrown = e }; todo(_thrown && _thrown.code == DOMException.NOT_SUPPORTED_ERR, "should throw NOT_SUPPORTED_ERR");
-var _thrown = undefined; try {
-  ctx.shadowOffsetX = NaN;
-} catch (e) { _thrown = e }; todo(_thrown && _thrown.code == DOMException.NOT_SUPPORTED_ERR, "should throw NOT_SUPPORTED_ERR");
-var _thrown = undefined; try {
-  ctx.shadowOffsetY = Infinity;
-} catch (e) { _thrown = e }; todo(_thrown && _thrown.code == DOMException.NOT_SUPPORTED_ERR, "should throw NOT_SUPPORTED_ERR");
-var _thrown = undefined; try {
-  ctx.shadowOffsetY = -Infinity;
-} catch (e) { _thrown = e }; todo(_thrown && _thrown.code == DOMException.NOT_SUPPORTED_ERR, "should throw NOT_SUPPORTED_ERR");
-var _thrown = undefined; try {
-  ctx.shadowOffsetY = NaN;
-} catch (e) { _thrown = e }; todo(_thrown && _thrown.code == DOMException.NOT_SUPPORTED_ERR, "should throw NOT_SUPPORTED_ERR");
-
+ctx.shadowOffsetX = 1;
+ctx.shadowOffsetY = 2;
+ctx.shadowOffsetX = Infinity;
+ctx.shadowOffsetY = Infinity;
+ok(ctx.shadowOffsetX === 1, "ctx.shadowOffsetX === 1");
+ok(ctx.shadowOffsetY === 2, "ctx.shadowOffsetY === 2");
+
+ctx.shadowOffsetX = 1;
+ctx.shadowOffsetY = 2;
+ctx.shadowOffsetX = -Infinity;
+ctx.shadowOffsetY = -Infinity;
+ok(ctx.shadowOffsetX === 1, "ctx.shadowOffsetX === 1");
+ok(ctx.shadowOffsetY === 2, "ctx.shadowOffsetY === 2");
+
+ctx.shadowOffsetX = 1;
+ctx.shadowOffsetY = 2;
+ctx.shadowOffsetX = NaN;
+ctx.shadowOffsetY = NaN;
+ok(ctx.shadowOffsetX === 1, "ctx.shadowOffsetX === 1");
+ok(ctx.shadowOffsetY === 2, "ctx.shadowOffsetY === 2");
 
 }
 </script>
 
 <!-- [[[ test_2d.shadow.basic.1.html ]]] -->
 
 <p>Canvas test: 2d.shadow.basic.1</p>
 <canvas id="c510" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
@@ -18267,17 +18275,17 @@ ctx.rotate(NaN);
 ctx.fillStyle = '#0f0';
 ctx.fillRect(-100, -10, 100, 50);
 
 isPixel(ctx, 50,25, 0,255,0,255, 0);
 
 } catch (e) {
     _thrown_outer = true;
 }
-todo(!_thrown_outer, 'should not throw exception');
+ok(!_thrown_outer, 'should not throw exception');
 
 
 }
 </script>
 
 <!-- [[[ test_2d.transformation.rotate.radians.html ]]] -->
 
 <p>Canvas test: 2d.transformation.rotate.radians</p>
@@ -18903,17 +18911,17 @@ ctx.transform(0, 0, 0, 0, Infinity, Infi
 ctx.fillStyle = '#0f0';
 ctx.fillRect(-100, -10, 100, 50);
 
 isPixel(ctx, 50,25, 0,255,0,255, 0);
 
 } catch (e) {
     _thrown_outer = true;
 }
-todo(!_thrown_outer, 'should not throw exception');
+ok(!_thrown_outer, 'should not throw exception');
 
 
 }
 </script>
 
 <!-- [[[ test_2d.transformation.transform.skewed.html ]]] -->
 
 <p>Canvas test: 2d.transformation.transform.skewed</p>
@@ -19017,17 +19025,17 @@ ctx.translate(Infinity, Infinity);
 ctx.fillStyle = '#0f0';
 ctx.fillRect(-100, -10, 100, 50);
 
 isPixel(ctx, 50,25, 0,255,0,255, 0);
 
 } catch (e) {
     _thrown_outer = true;
 }
-todo(!_thrown_outer, 'should not throw exception');
+ok(!_thrown_outer, 'should not throw exception');
 
 
 }
 </script>
 
 <!-- [[[ test_2d.type.exists.html ]]] -->
 
 <p>Canvas test: 2d.type.exists</p>
@@ -19163,18 +19171,18 @@ ok(ctx.arcTo(0, 0, 0, 0, 1) === undefine
 ok(ctx.rect(0, 0, 0, 0) === undefined, "ctx.rect(0, 0, 0, 0) === undefined");
 ok(ctx.arc(0, 0, 1, 0, 0, true) === undefined, "ctx.arc(0, 0, 1, 0, 0, true) === undefined");
 ok(ctx.fill() === undefined, "ctx.fill() === undefined");
 ok(ctx.stroke() === undefined, "ctx.stroke() === undefined");
 ok(ctx.clip() === undefined, "ctx.clip() === undefined");
 if (ctx.putImageData) {
     ok(ctx.putImageData(ctx.getImageData(0, 0, 1, 1), 0, 0) === undefined, "ctx.putImageData(ctx.getImageData(0, 0, 1, 1), 0, 0) === undefined");
 }
-ok(ctx.drawImage(document.getElementById('yellow_11.png'), 0, 0, 0, 0, 0, 0, 0, 0) === undefined, "ctx.drawImage(document.getElementById('yellow_11.png'), 0, 0, 0, 0, 0, 0, 0, 0) === undefined");
-ok(ctx.drawImage(canvas, 0, 0, 0, 0, 0, 0, 0, 0) === undefined, "ctx.drawImage(canvas, 0, 0, 0, 0, 0, 0, 0, 0) === undefined");
+ok(ctx.drawImage(document.getElementById('yellow_11.png'), 0, 0, 1, 1, 0, 0, 0, 0) === undefined, "ctx.drawImage(document.getElementById('yellow_11.png'), 0, 0, 1, 1, 0, 0, 0, 0) === undefined");
+ok(ctx.drawImage(canvas, 0, 0, 1, 1, 0, 0, 0, 0) === undefined, "ctx.drawImage(canvas, 0, 0, 1, 1, 0, 0, 0, 0) === undefined");
 ok(ctx.createLinearGradient(0, 0, 0, 0).addColorStop(0, 'white') === undefined, "ctx.createLinearGradient(0, 0, 0, 0).addColorStop(0, 'white') === undefined");
 
 
 }
 </script>
 <img src="image_yellow.png" id="yellow_11.png" class="resource">
 
 <!-- [[[ test_bug397524.html ]]] -->
@@ -21505,16 +21513,21 @@ function runTests() {
   ok(false, "unexpected exception thrown in: test_2d_drawImage_floatsource");
  }
  try {
   test_2d_drawImage_incomplete();
  } catch (e) {
   ok(false, "unexpected exception thrown in: test_2d_drawImage_incomplete");
  }
  try {
+  test_2d_drawImage_negativedir();
+ } catch (e) {
+  ok(false, "unexpected exception thrown in: test_2d_drawImage_negativedir");
+ }
+ try {
   test_2d_drawImage_negativedest();
  } catch (e) {
   ok(false, "unexpected exception thrown in: test_2d_drawImage_negativedest");
  }
  try {
   test_2d_drawImage_negativesource();
  } catch (e) {
   ok(false, "unexpected exception thrown in: test_2d_drawImage_negativesource");
new file mode 100644
--- /dev/null
+++ b/content/canvas/test/test_toDataURL_lowercase_ascii.html
@@ -0,0 +1,21 @@
+<!DOCTYPE HTML>
+<title>Canvas test: toDataURL.lowercase.ascii</title>
+<script src="/MochiKit/MochiKit.js"></script>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" href="/tests/SimpleTest/test.css">
+<body>
+<canvas id="c" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<script>
+var canvas = document.getElementById('c');
+var ctx = canvas.getContext("2d");
+
+var data = canvas.toDataURL('ImAgE/PnG');
+ok(data.match(/^data:image\/png[;,]/), "data =~ /^data:image\\/png[;,]/");
+
+// If JPEG is supported at all, it must be supported case-insensitively
+data = canvas.toDataURL('image/jpeg');
+if (data.match(/^data:image\/jpeg[;,]/)) {
+    data = canvas.toDataURL('ImAgE/JpEg');
+    ok(data.match(/^data:image\/jpeg[;,]/), "data =~ /^data:image\\/jpeg[;,]/");
+}
+</script>
--- a/content/html/content/public/nsHTMLCanvasElement.h
+++ b/content/html/content/public/nsHTMLCanvasElement.h
@@ -104,21 +104,26 @@ public:
    */
   PRBool IsWriteOnly();
 
   /**
    * Force the canvas to be write-only.
    */
   void SetWriteOnly();
 
+  /**
+   * Notify that some canvas content has changed and the window may
+   * need to be updated. aDamageRect is in canvas coordinates.
+   */
+  void InvalidateCanvasContent(const gfxRect* aDamageRect);
   /*
-   * Ask the canvas frame to invalidate itself.  If damageRect is
-   * given, it is relative to the origin of the canvas frame in CSS pixels.
+   * Notify that we need to repaint the entire canvas, including updating of
+   * the layer tree.
    */
-  void InvalidateFrame(const gfxRect* damageRect = nsnull);
+  void InvalidateCanvas();
 
   /*
    * Get the number of contexts in this canvas, and request a context at
    * an index.
    */
   PRInt32 CountContexts ();
   nsICanvasRenderingContextInternal *GetContextAtIndex (PRInt32 index);
 
@@ -152,22 +157,24 @@ 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(CanvasLayer *aOldLayer,
+  already_AddRefed<CanvasLayer> GetCanvasLayer(nsDisplayListBuilder* aBuilder,
+                                               CanvasLayer *aOldLayer,
                                                LayerManager *aManager);
 
-  // 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.
+  // Call this whenever we need future changes to the canvas
+  // to trigger fresh invalidation requests. This needs to be called
+  // whenever we render the canvas contents to the screen, or whenever we
+  // take a snapshot of the canvas that needs to be "live" (e.g. -moz-element).
   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
@@ -378,30 +378,16 @@ nsGenericHTMLElement::GetNodeName(nsAStr
   mNodeInfo->GetQualifiedName(aNodeName);
 
   if (IsInHTMLDocument())
     nsContentUtils::ASCIIToUpper(aNodeName);
 
   return NS_OK;
 }
 
-nsresult
-nsGenericHTMLElement::GetElementsByTagName(const nsAString& aTagname,
-                                           nsIDOMNodeList** aReturn)
-{
-  // Only lowercase the name if this is an HTML document.
-  if (IsInHTMLDocument()) {
-    nsAutoString lower;
-    nsContentUtils::ASCIIToLower(aTagname, lower);
-    return nsGenericHTMLElementBase::GetElementsByTagName(lower, aReturn);
-  }
-
-  return nsGenericHTMLElementBase::GetElementsByTagName(aTagname, aReturn);
-}
-
 // Implementation for nsIDOMHTMLElement
 nsresult
 nsGenericHTMLElement::GetId(nsAString& aId)
 {
   GetAttr(kNameSpaceID_None, nsGkAtoms::id, aId);
   return NS_OK;
 }
 
--- a/content/html/content/src/nsGenericHTMLElement.h
+++ b/content/html/content/src/nsGenericHTMLElement.h
@@ -104,18 +104,16 @@ public:
 
   // Implementation for nsIDOMNode
   NS_METHOD GetNodeName(nsAString& aNodeName);
 
   // Implementation for nsIDOMElement
   NS_METHOD SetAttribute(const nsAString& aName,
                          const nsAString& aValue);
   NS_METHOD GetTagName(nsAString& aTagName);
-  NS_METHOD GetElementsByTagName(const nsAString& aTagname,
-                                 nsIDOMNodeList** aReturn);
 
   // nsIDOMHTMLElement methods. Note that these are non-virtual
   // methods, implementations are expected to forward calls to these
   // methods.
   nsresult GetId(nsAString& aId);
   nsresult SetId(const nsAString& aId);
   nsresult GetTitle(nsAString& aTitle);
   nsresult SetTitle(const nsAString& aTitle);
--- a/content/html/content/src/nsHTMLCanvasElement.cpp
+++ b/content/html/content/src/nsHTMLCanvasElement.cpp
@@ -242,18 +242,17 @@ nsHTMLCanvasElement::ExtractData(const n
   if (!mCurrentContext) {
     emptyCanvas = new gfxImageSurface(gfxIntSize(size.width, size.height), gfxASurface::ImageFormatARGB32);
   }
 
   nsresult rv;
 
   // get image bytes
   nsCOMPtr<nsIInputStream> imgStream;
-  nsCAutoString encoderType;
-  encoderType.Assign(NS_ConvertUTF16toUTF8(aType));
+  NS_ConvertUTF16toUTF8 encoderType(aType);
 
  try_again:
   if (mCurrentContext) {
     rv = mCurrentContext->GetInputStream(nsPromiseFlatCString(encoderType).get(),
                                          nsPromiseFlatString(aOptions).get(),
                                          getter_AddRefs(imgStream));
   } else {
     // no context, so we have to encode the empty image we created above
@@ -329,35 +328,38 @@ nsHTMLCanvasElement::ExtractData(const n
 
 nsresult
 nsHTMLCanvasElement::ToDataURLImpl(const nsAString& aMimeType,
                                    const nsAString& aEncoderOptions,
                                    nsAString& aDataURL)
 {
   bool fallbackToPNG = false;
 
+  nsAutoString type;
+  nsContentUtils::ASCIIToLower(aMimeType, type);
+
   PRUint32 imgSize = 0;
   char* imgData;
 
-  nsresult rv = ExtractData(aMimeType, aEncoderOptions, imgData,
+  nsresult rv = ExtractData(type, aEncoderOptions, imgData,
                             imgSize, fallbackToPNG);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // base 64, result will be NULL terminated
   char* encodedImg = PL_Base64Encode(imgData, imgSize, nsnull);
   PR_Free(imgData);
   if (!encodedImg) // not sure why this would fail
     return NS_ERROR_OUT_OF_MEMORY;
 
   // build data URL string
   if (fallbackToPNG)
     aDataURL = NS_LITERAL_STRING("data:image/png;base64,") +
       NS_ConvertUTF8toUTF16(encodedImg);
   else
-    aDataURL = NS_LITERAL_STRING("data:") + aMimeType +
+    aDataURL = NS_LITERAL_STRING("data:") + type +
       NS_LITERAL_STRING(";base64,") + NS_ConvertUTF8toUTF16(encodedImg);
 
   PR_Free(encodedImg);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
@@ -635,50 +637,65 @@ nsHTMLCanvasElement::IsWriteOnly()
 
 void
 nsHTMLCanvasElement::SetWriteOnly()
 {
   mWriteOnly = PR_TRUE;
 }
 
 void
-nsHTMLCanvasElement::InvalidateFrame(const gfxRect* damageRect)
+nsHTMLCanvasElement::InvalidateCanvasContent(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
-    nsRect invalRect(realRect.X(), realRect.Y(),
-                     realRect.Width(), realRect.Height());
-
-    // account for border/padding
-    invalRect.MoveBy(contentArea.TopLeft() - frame->GetPosition());
+    invalRect = nsRect(realRect.X(), realRect.Y(),
+                       realRect.Width(), realRect.Height());
+  } else {
+    invalRect = nsRect(nsPoint(0, 0), contentArea.Size());
+  }
+  invalRect.MoveBy(contentArea.TopLeft() - frame->GetPosition());
 
-    frame->InvalidateLayer(invalRect, nsDisplayItem::TYPE_CANVAS);
-  } else {
-    nsRect r(frame->GetContentRect() - frame->GetPosition());
-    frame->InvalidateLayer(r, nsDisplayItem::TYPE_CANVAS);
+  Layer* layer = frame->InvalidateLayer(invalRect, nsDisplayItem::TYPE_CANVAS);
+  if (layer) {
+    static_cast<CanvasLayer*>(layer)->Updated();
   }
 }
 
+void
+nsHTMLCanvasElement::InvalidateCanvas()
+{
+  // 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->Invalidate(frame->GetContentRect() - frame->GetPosition());
+}
+
 PRInt32
 nsHTMLCanvasElement::CountContexts()
 {
   if (mCurrentContext)
     return 1;
 
   return 0;
 }
@@ -694,23 +711,24 @@ nsHTMLCanvasElement::GetContextAtIndex (
 
 PRBool
 nsHTMLCanvasElement::GetIsOpaque()
 {
   return HasAttr(kNameSpaceID_None, nsGkAtoms::moz_opaque);
 }
 
 already_AddRefed<CanvasLayer>
-nsHTMLCanvasElement::GetCanvasLayer(CanvasLayer *aOldLayer,
+nsHTMLCanvasElement::GetCanvasLayer(nsDisplayListBuilder* aBuilder,
+                                    CanvasLayer *aOldLayer,
                                     LayerManager *aManager)
 {
   if (!mCurrentContext)
     return nsnull;
 
-  return mCurrentContext->GetCanvasLayer(aOldLayer, aManager);
+  return mCurrentContext->GetCanvasLayer(aBuilder, aOldLayer, aManager);
 }
 
 void
 nsHTMLCanvasElement::MarkContextClean()
 {
   if (!mCurrentContext)
     return;
 
--- a/content/html/content/test/Makefile.in
+++ b/content/html/content/test/Makefile.in
@@ -254,12 +254,13 @@ include $(topsrcdir)/config/rules.mk
 		test_bug623291.html \
 		test_bug619278.html \
 		test_bug622558.html \
 		test_bug622597.html \
 		test_bug636336.html \
 		test_bug630889.html \
 		test_bug610212.html \
 		test_bug633058.html \
+		test_bug641219.html \
 		$(NULL)
 
 libs:: $(_TEST_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/test_bug641219.html
@@ -0,0 +1,35 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=641219
+-->
+<head>
+  <title>Test for Bug 641219</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=641219">Mozilla Bug 641219</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+<div id="div">
+<font></font>
+<svg><font/></svg>
+</div>
+</div>
+<pre id="test">
+<script type="application/javascript">
+/** Test for Bug 641219 **/
+var HTML = "http://www.w3.org/1999/xhtml",
+    SVG = "http://www.w3.org/2000/svg";
+var wrapper = document.getElementById("div");
+is(wrapper.getElementsByTagName("FONT").length, 1);
+is(wrapper.getElementsByTagName("FONT")[0].namespaceURI, HTML);
+is(wrapper.getElementsByTagName("font").length, 2);
+is(wrapper.getElementsByTagName("font")[0].namespaceURI, HTML);
+is(wrapper.getElementsByTagName("font")[1].namespaceURI, SVG);
+</script>
+</pre>
+</body>
+</html>
--- a/content/html/document/src/nsHTMLContentSink.cpp
+++ b/content/html/document/src/nsHTMLContentSink.cpp
@@ -55,17 +55,16 @@
 #include "nsIContentViewer.h"
 #include "nsIMarkupDocumentViewer.h"
 #include "nsINodeInfo.h"
 #include "nsHTMLTokens.h"
 #include "nsIAppShell.h"
 #include "nsCRT.h"
 #include "prtime.h"
 #include "prlog.h"
-#include "nsInt64.h"
 #include "nsNodeUtils.h"
 #include "nsIContent.h"
 #include "mozilla/dom/Element.h"
 
 #include "nsGenericHTMLElement.h"
 
 #include "nsIDOMText.h"
 #include "nsIDOMComment.h"
--- 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
@@ -37,17 +37,16 @@
  *
  * ***** END LICENSE BLOCK ***** */
 #if !defined(nsBuiltinDecoderReader_h_)
 #define nsBuiltinDecoderReader_h_
 
 #include <nsDeque.h>
 #include "Layers.h"
 #include "ImageLayers.h"
-#include "nsAutoLock.h"
 #include "nsClassHashtable.h"
 #include "mozilla/TimeStamp.h"
 #include "nsSize.h"
 #include "nsRect.h"
 #include "mozilla/Monitor.h"
 
 class nsBuiltinDecoderStateMachine;
 
@@ -114,25 +113,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/nsMediaCache.cpp
+++ b/content/media/nsMediaCache.cpp
@@ -31,31 +31,33 @@
  * 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 "mozilla/Monitor.h"
 #include "mozilla/XPCOM.h"
 
 #include "nsMediaCache.h"
-#include "nsAutoLock.h"
 #include "nsContentUtils.h"
 #include "nsDirectoryServiceUtils.h"
 #include "nsDirectoryServiceDefs.h"
 #include "nsNetUtil.h"
 #include "prio.h"
 #include "nsThreadUtils.h"
 #include "nsMediaStream.h"
 #include "nsMathUtils.h"
 #include "prlog.h"
 #include "nsIPrivateBrowsingService.h"
 
+using namespace mozilla;
+
 #ifdef PR_LOGGING
 PRLogModuleInfo* gMediaCacheLog;
 #define LOG(type, msg) PR_LOG(gMediaCacheLog, type, msg)
 #else
 #define LOG(type, msg)
 #endif
 
 // Readahead blocks for non-seekable streams will be limited to this
@@ -72,19 +74,16 @@ static const PRUint32 REPLAY_DELAY = 30;
 
 // When looking for a reusable block, scan forward this many blocks
 // from the desired "best" block location to look for free blocks,
 // before we resort to scanning the whole cache. The idea is to try to
 // store runs of stream blocks close-to-consecutively in the cache if we
 // can.
 static const PRUint32 FREE_BLOCK_SCAN_LIMIT = 16;
 
-using mozilla::TimeStamp;
-using mozilla::TimeDuration;
-
 #ifdef DEBUG
 // Turn this on to do very expensive cache state validation
 // #define DEBUG_VERIFY_CACHE
 #endif
 
 // There is at most one media cache (although that could quite easily be
 // relaxed if we wanted to manage multiple caches with independent
 // size limits).
@@ -130,34 +129,31 @@ class nsMediaCache {
 public:
   friend class nsMediaCacheStream::BlockList;
   typedef nsMediaCacheStream::BlockList BlockList;
   enum {
     BLOCK_SIZE = nsMediaCacheStream::BLOCK_SIZE
   };
 
   nsMediaCache() : mNextResourceID(1),
-    mMonitor(nsAutoMonitor::NewMonitor("media.cache")),
+    mMonitor("nsMediaCache.mMonitor"),
     mFD(nsnull), mFDCurrentPos(0), mUpdateQueued(PR_FALSE)
 #ifdef DEBUG
     , mInUpdate(PR_FALSE)
 #endif
   {
     MOZ_COUNT_CTOR(nsMediaCache);
   }
   ~nsMediaCache() {
     NS_ASSERTION(mStreams.IsEmpty(), "Stream(s) still open!");
     Truncate();
     NS_ASSERTION(mIndex.Length() == 0, "Blocks leaked?");
     if (mFD) {
       PR_Close(mFD);
     }
-    if (mMonitor) {
-      nsAutoMonitor::DestroyMonitor(mMonitor);
-    }
     MOZ_COUNT_DTOR(nsMediaCache);
   }
 
   // Main thread only. Creates the backing cache file. If this fails,
   // then the cache is still in a semi-valid state; mFD will be null,
   // so all I/O on the cache file will fail.
   nsresult Init();
   // Shut down the global cache if it's no longer needed. We shut down
@@ -226,17 +222,17 @@ public:
 
 #ifdef DEBUG_VERIFY_CACHE
   // Verify invariants, especially block list invariants
   void Verify();
 #else
   void Verify() {}
 #endif
 
-  PRMonitor* Monitor() { return mMonitor; }
+  Monitor& GetMonitor() { return mMonitor; }
 
   /**
    * An iterator that makes it easy to iterate through all streams that
    * have a given resource ID and are not closed.
    */
   class ResourceStreamIterator {
   public:
     ResourceStreamIterator(PRInt64 aResourceID) :
@@ -349,17 +345,17 @@ protected:
   // resource IDs to streams.
   PRInt64                       mNextResourceID;
   // This member is main-thread only. It contains all the streams.
   nsTArray<nsMediaCacheStream*> mStreams;
 
   // The monitor protects all the data members here. Also, off-main-thread
   // readers that need to block will Wait() on this monitor. When new
   // data becomes available in the cache, we NotifyAll() on this monitor.
-  PRMonitor*      mMonitor;
+  Monitor         mMonitor;
   // The Blocks describing the cache entries.
   nsTArray<Block> mIndex;
   // The file descriptor of the cache file. The file will be deleted
   // by the operating system when this is closed.
   PRFileDesc*     mFD;
   // The current file offset in the cache file.
   PRInt64         mFDCurrentPos;
   // The list of free blocks; they are not ordered.
@@ -544,21 +540,16 @@ nsMediaCacheStream::BlockList::NotifyBlo
 }
 
 nsresult
 nsMediaCache::Init()
 {
   NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
   NS_ASSERTION(!mFD, "Cache file already open?");
 
-  if (!mMonitor) {
-    // the constructor failed
-    return NS_ERROR_OUT_OF_MEMORY;
-  }
-
   nsCOMPtr<nsIFile> tmp;
   nsresult rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(tmp));
   NS_ENSURE_SUCCESS(rv,rv);
 
   nsCOMPtr<nsILocalFile> tmpFile = do_QueryInterface(tmp);
   NS_ENSURE_TRUE(tmpFile != nsnull, NS_ERROR_FAILURE);
 
   // We put the media cache file in
@@ -612,17 +603,17 @@ nsMediaCache::Flush()
     return;
 
   gMediaCache->FlushInternal();
 }
 
 void
 nsMediaCache::FlushInternal()
 {
-  nsAutoMonitor mon(mMonitor);
+  MonitorAutoEnter mon(mMonitor);
 
   for (PRUint32 blockIndex = 0; blockIndex < mIndex.Length(); ++blockIndex) {
     FreeBlock(blockIndex);
   }
 
   // Truncate file, close it, and reopen
   Truncate();
   NS_ASSERTION(mIndex.Length() == 0, "Blocks leaked?");
@@ -667,17 +658,17 @@ InitMediaCache()
     gMediaCache = nsnull;
   }
 }
 
 nsresult
 nsMediaCache::ReadCacheFile(PRInt64 aOffset, void* aData, PRInt32 aLength,
                             PRInt32* aBytes)
 {
-  PR_ASSERT_CURRENT_THREAD_IN_MONITOR(mMonitor);
+  mMonitor.AssertCurrentThreadIn();
 
   if (!mFD)
     return NS_ERROR_FAILURE;
 
   if (mFDCurrentPos != aOffset) {
     PROffset64 offset = PR_Seek64(mFD, aOffset, PR_SEEK_SET);
     if (offset != aOffset)
       return NS_ERROR_FAILURE;
@@ -689,17 +680,17 @@ nsMediaCache::ReadCacheFile(PRInt64 aOff
   mFDCurrentPos += amount;
   *aBytes = amount;
   return NS_OK;
 }
 
 nsresult
 nsMediaCache::ReadCacheFileAllBytes(PRInt64 aOffset, void* aData, PRInt32 aLength)
 {
-  PR_ASSERT_CURRENT_THREAD_IN_MONITOR(mMonitor);
+  mMonitor.AssertCurrentThreadIn();
 
   PRInt64 offset = aOffset;
   PRInt32 count = aLength;
   // Cast to char* so we can do byte-wise pointer arithmetic
   char* data = static_cast<char*>(aData);
   while (count > 0) {
     PRInt32 bytes;
     nsresult rv = ReadCacheFile(offset, data, count, &bytes);
@@ -712,17 +703,17 @@ nsMediaCache::ReadCacheFileAllBytes(PRIn
     offset += bytes;
   }
   return NS_OK;
 }
 
 nsresult
 nsMediaCache::WriteCacheFile(PRInt64 aOffset, const void* aData, PRInt32 aLength)
 {
-  PR_ASSERT_CURRENT_THREAD_IN_MONITOR(mMonitor);
+  mMonitor.AssertCurrentThreadIn();
 
   if (!mFD)
     return NS_ERROR_FAILURE;
 
   if (mFDCurrentPos != aOffset) {
     PROffset64 offset = PR_Seek64(mFD, aOffset, PR_SEEK_SET);
     if (offset != aOffset)
       return NS_ERROR_FAILURE;
@@ -753,17 +744,17 @@ static PRInt32 GetMaxBlocks()
   maxBlocks = PR_MAX(maxBlocks, 1);
   return PRInt32(PR_MIN(maxBlocks, PR_INT32_MAX));
 }
 
 PRInt32
 nsMediaCache::FindBlockForIncomingData(TimeStamp aNow,
                                        nsMediaCacheStream* aStream)
 {
-  PR_ASSERT_CURRENT_THREAD_IN_MONITOR(mMonitor);
+  mMonitor.AssertCurrentThreadIn();
 
   PRInt32 blockIndex = FindReusableBlock(aNow, aStream,
       aStream->mChannelOffset/BLOCK_SIZE, PR_INT32_MAX);
 
   if (blockIndex < 0 || !IsBlockFree(blockIndex)) {
     // The block returned is already allocated.
     // Don't reuse it if a) there's room to expand the cache or
     // b) the data we're going to store in the free block is not higher
@@ -796,17 +787,17 @@ nsMediaCache::BlockIsReusable(PRInt32 aB
   return PR_TRUE;
 }
 
 void
 nsMediaCache::AppendMostReusableBlock(BlockList* aBlockList,
                                       nsTArray<PRUint32>* aResult,
                                       PRInt32 aBlockIndexLimit)
 {
-  PR_ASSERT_CURRENT_THREAD_IN_MONITOR(mMonitor);
+  mMonitor.AssertCurrentThreadIn();
 
   PRInt32 blockIndex = aBlockList->GetLastBlock();
   if (blockIndex < 0)
     return;
   do {
     // Don't consider blocks for pinned streams, or blocks that are
     // beyond the specified limit, or a block that contains a stream's
     // current read position (such a block contains both played data
@@ -820,17 +811,17 @@ nsMediaCache::AppendMostReusableBlock(Bl
 }
 
 PRInt32
 nsMediaCache::FindReusableBlock(TimeStamp aNow,
                                 nsMediaCacheStream* aForStream,
                                 PRInt32 aForStreamBlock,
                                 PRInt32 aMaxSearchBlockIndex)
 {
-  PR_ASSERT_CURRENT_THREAD_IN_MONITOR(mMonitor);
+  mMonitor.AssertCurrentThreadIn();
 
   PRUint32 length = PR_MIN(PRUint32(aMaxSearchBlockIndex), mIndex.Length());
 
   if (aForStream && aForStreamBlock > 0 &&
       PRUint32(aForStreamBlock) <= aForStream->mBlocks.Length()) {
     PRInt32 prevCacheBlock = aForStream->mBlocks[aForStreamBlock - 1];
     if (prevCacheBlock >= 0) {
       PRUint32 freeBlockScanEnd =
@@ -914,17 +905,17 @@ nsMediaCache::GetBlockOwner(PRInt32 aBlo
       return &block->mOwners[i];
   }
   return nsnull;
 }
 
 void
 nsMediaCache::SwapBlocks(PRInt32 aBlockIndex1, PRInt32 aBlockIndex2)
 {
-  PR_ASSERT_CURRENT_THREAD_IN_MONITOR(mMonitor);
+  mMonitor.AssertCurrentThreadIn();
 
   Block* block1 = &mIndex[aBlockIndex1];
   Block* block2 = &mIndex[aBlockIndex2];
 
   block1->mOwners.SwapElements(block2->mOwners);
 
   // Now all references to block1 have to be replaced with block2 and
   // vice versa.
@@ -994,17 +985,17 @@ nsMediaCache::AddBlockOwnerAsReadahead(P
   aStream->mBlocks[aStreamBlockIndex] = aBlockIndex;
   bo->mClass = READAHEAD_BLOCK;
   InsertReadaheadBlock(bo, aBlockIndex);
 }
 
 void
 nsMediaCache::FreeBlock(PRInt32 aBlock)
 {
-  PR_ASSERT_CURRENT_THREAD_IN_MONITOR(mMonitor);
+  mMonitor.AssertCurrentThreadIn();
 
   Block* block = &mIndex[aBlock];
   if (block->mOwners.IsEmpty()) {
     // already free
     return;
   }
 
   LOG(PR_LOG_DEBUG, ("Released block %d", aBlock));
@@ -1017,17 +1008,17 @@ nsMediaCache::FreeBlock(PRInt32 aBlock)
   block->mOwners.Clear();
   mFreeBlocks.AddFirstBlock(aBlock);
   Verify();
 }
 
 TimeDuration
 nsMediaCache::PredictNextUse(TimeStamp aNow, PRInt32 aBlock)
 {
-  PR_ASSERT_CURRENT_THREAD_IN_MONITOR(mMonitor);
+  mMonitor.AssertCurrentThreadIn();
   NS_ASSERTION(!IsBlockFree(aBlock), "aBlock is free");
 
   Block* block = &mIndex[aBlock];
   // Blocks can be belong to multiple streams. The predicted next use
   // time is the earliest time predicted by any of the streams.
   TimeDuration result;
   for (PRUint32 i = 0; i < block->mOwners.Length(); ++i) {
     BlockOwner* bo = &block->mOwners[i];
@@ -1067,17 +1058,17 @@ nsMediaCache::PredictNextUse(TimeStamp a
     }
   }
   return result;
 }
 
 TimeDuration
 nsMediaCache::PredictNextUseForIncomingData(nsMediaCacheStream* aStream)
 {
-  PR_ASSERT_CURRENT_THREAD_IN_MONITOR(mMonitor);
+  mMonitor.AssertCurrentThreadIn();
 
   PRInt64 bytesAhead = aStream->mChannelOffset - aStream->mStreamOffset;
   if (bytesAhead <= -BLOCK_SIZE) {
     // Hmm, no idea when data behind us will be used. Guess 24 hours.
     return TimeDuration::FromSeconds(24*60*60);
   }
   if (bytesAhead <= 0)
     return TimeDuration(0);
@@ -1095,17 +1086,17 @@ nsMediaCache::Update()
 
   // The action to use for each stream. We store these so we can make
   // decisions while holding the cache lock but implement those decisions
   // without holding the cache lock, since we need to call out to
   // stream, decoder and element code.
   nsAutoTArray<StreamAction,10> actions;
 
   {
-    nsAutoMonitor mon(mMonitor);
+    MonitorAutoEnter mon(mMonitor);
     mUpdateQueued = PR_FALSE;
 #ifdef DEBUG
     mInUpdate = PR_TRUE;
 #endif
 
     PRInt32 maxBlocks = GetMaxBlocks();
     TimeStamp now = TimeStamp::Now();
 
@@ -1377,18 +1368,18 @@ nsMediaCache::Update()
     default:
       break;
     }
 
     if (NS_FAILED(rv)) {
       // Close the streams that failed due to error. This will cause all
       // client Read and Seek operations on those streams to fail. Blocked
       // Reads will also be woken up.
-      nsAutoMonitor mon(mMonitor);
-      stream->CloseInternal(&mon);
+      MonitorAutoEnter mon(mMonitor);
+      stream->CloseInternal(mon);
     }
   }
 }
 
 class UpdateEvent : public nsRunnable
 {
 public:
   NS_IMETHOD Run()
@@ -1398,34 +1389,34 @@ public:
     }
     return NS_OK;
   }
 };
 
 void
 nsMediaCache::QueueUpdate()
 {
-  PR_ASSERT_CURRENT_THREAD_IN_MONITOR(mMonitor);
+  mMonitor.AssertCurrentThreadIn();
 
   // Queuing an update while we're in an update raises a high risk of
   // triggering endless events
   NS_ASSERTION(!mInUpdate,
                "Queuing an update while we're in an update");
   if (mUpdateQueued)
     return;
   mUpdateQueued = PR_TRUE;
   nsCOMPtr<nsIRunnable> event = new UpdateEvent();
   NS_DispatchToMainThread(event);
 }
 
 #ifdef DEBUG_VERIFY_CACHE
 void
 nsMediaCache::Verify()
 {
-  PR_ASSERT_CURRENT_THREAD_IN_MONITOR(mMonitor);
+  mMonitor.AssertCurrentThreadIn();
 
   mFreeBlocks.Verify();
   for (PRUint32 i = 0; i < mStreams.Length(); ++i) {
     nsMediaCacheStream* stream = mStreams[i];
     stream->mReadaheadBlocks.Verify();
     stream->mPlayedBlocks.Verify();
     stream->mMetadataBlocks.Verify();
 
@@ -1447,17 +1438,17 @@ nsMediaCache::Verify()
   }
 }
 #endif
 
 void
 nsMediaCache::InsertReadaheadBlock(BlockOwner* aBlockOwner,
                                    PRInt32 aBlockIndex)
 {
-  PR_ASSERT_CURRENT_THREAD_IN_MONITOR(mMonitor);
+  mMonitor.AssertCurrentThreadIn();
 
   // Find the last block whose stream block is before aBlockIndex's
   // stream block, and insert after it
   nsMediaCacheStream* stream = aBlockOwner->mStream;
   PRInt32 readaheadIndex = stream->mReadaheadBlocks.GetLastBlock();
   while (readaheadIndex >= 0) {
     BlockOwner* bo = GetBlockOwner(readaheadIndex, stream);
     NS_ASSERTION(bo, "stream must own its blocks");
@@ -1473,17 +1464,17 @@ nsMediaCache::InsertReadaheadBlock(Block
   stream->mReadaheadBlocks.AddFirstBlock(aBlockIndex);
   Verify();
 }
 
 void
 nsMediaCache::AllocateAndWriteBlock(nsMediaCacheStream* aStream, const void* aData,
                                     nsMediaCacheStream::ReadMode aMode)
 {
-  PR_ASSERT_CURRENT_THREAD_IN_MONITOR(mMonitor);
+  mMonitor.AssertCurrentThreadIn();
 
   PRInt32 streamBlockIndex = aStream->mChannelOffset/BLOCK_SIZE;
 
   // Remove all cached copies of this block
   ResourceStreamIterator iter(aStream->mResourceID);
   while (nsMediaCacheStream* stream = iter.Next()) {
     while (streamBlockIndex >= PRInt32(stream->mBlocks.Length())) {
       stream->mBlocks.AppendElement(-1);
@@ -1551,39 +1542,39 @@ nsMediaCache::AllocateAndWriteBlock(nsMe
   QueueUpdate();
 }
 
 void
 nsMediaCache::OpenStream(nsMediaCacheStream* aStream)
 {
   NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
 
-  nsAutoMonitor mon(mMonitor);
+  MonitorAutoEnter mon(mMonitor);
   LOG(PR_LOG_DEBUG, ("Stream %p opened", aStream));
   mStreams.AppendElement(aStream);
   aStream->mResourceID = mNextResourceID++;
 
   // Queue an update since a new stream has been opened.
   gMediaCache->QueueUpdate();
 }
 
 void
 nsMediaCache::ReleaseStream(nsMediaCacheStream* aStream)
 {
   NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
 
-  nsAutoMonitor mon(mMonitor);
+  MonitorAutoEnter mon(mMonitor);
   LOG(PR_LOG_DEBUG, ("Stream %p closed", aStream));
   mStreams.RemoveElement(aStream);
 }
 
 void
 nsMediaCache::ReleaseStreamBlocks(nsMediaCacheStream* aStream)
 {
-  PR_ASSERT_CURRENT_THREAD_IN_MONITOR(mMonitor);
+  mMonitor.AssertCurrentThreadIn();
 
   // XXX scanning the entire stream doesn't seem great, if not much of it
   // is cached, but the only easy alternative is to scan the entire cache
   // which isn't better
   PRUint32 length = aStream->mBlocks.Length();
   for (PRUint32 i = 0; i < length; ++i) {
     PRInt32 blockIndex = aStream->mBlocks[i];
     if (blockIndex >= 0) {
@@ -1613,17 +1604,17 @@ nsMediaCache::Truncate()
   }
 }
 
 void
 nsMediaCache::NoteBlockUsage(nsMediaCacheStream* aStream, PRInt32 aBlockIndex,
                              nsMediaCacheStream::ReadMode aMode,
                              TimeStamp aNow)
 {
-  PR_ASSERT_CURRENT_THREAD_IN_MONITOR(mMonitor);
+  mMonitor.AssertCurrentThreadIn();
 
   if (aBlockIndex < 0) {
     // this block is not in the cache yet
     return;
   }
 
   BlockOwner* bo = GetBlockOwner(aBlockIndex, aStream);
   if (!bo) {
@@ -1645,17 +1636,17 @@ nsMediaCache::NoteBlockUsage(nsMediaCach
   GetListForBlock(bo)->AddFirstBlock(aBlockIndex);
   bo->mLastUseTime = aNow;
   Verify();
 }
 
 void
 nsMediaCache::NoteSeek(nsMediaCacheStream* aStream, PRInt64 aOldOffset)
 {
-  PR_ASSERT_CURRENT_THREAD_IN_MONITOR(mMonitor);
+  mMonitor.AssertCurrentThreadIn();
 
   if (aOldOffset < aStream->mStreamOffset) {
     // We seeked forward. Convert blocks from readahead to played.
     // Any readahead block that intersects the seeked-over range must
     // be converted.
     PRInt32 blockIndex = aOldOffset/BLOCK_SIZE;
     PRInt32 endIndex =
       PR_MIN((aStream->mStreamOffset + BLOCK_SIZE - 1)/BLOCK_SIZE,
@@ -1701,26 +1692,26 @@ nsMediaCache::NoteSeek(nsMediaCacheStrea
   }
 }
 
 void
 nsMediaCacheStream::NotifyDataLength(PRInt64 aLength)
 {
   NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
 
-  nsAutoMonitor mon(gMediaCache->Monitor());
+  MonitorAutoEnter mon(gMediaCache->GetMonitor());
   mStreamLength = aLength;
 }
 
 void
 nsMediaCacheStream::NotifyDataStarted(PRInt64 aOffset)
 {
   NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
 
-  nsAutoMonitor mon(gMediaCache->Monitor());
+  MonitorAutoEnter mon(gMediaCache->GetMonitor());
   NS_WARN_IF_FALSE(aOffset == mChannelOffset,
                    "Server is giving us unexpected offset");
   mChannelOffset = aOffset;
   if (mStreamLength >= 0) {
     // If we started reading at a certain offset, then for sure
     // the stream is at least that long.
     mStreamLength = PR_MAX(mStreamLength, mChannelOffset);
   }
@@ -1761,17 +1752,17 @@ nsMediaCacheStream::UpdatePrincipal(nsIP
 }
 
 void
 nsMediaCacheStream::NotifyDataReceived(PRInt64 aSize, const char* aData,
     nsIPrincipal* aPrincipal)
 {
   NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
 
-  nsAutoMonitor mon(gMediaCache->Monitor());
+  MonitorAutoEnter mon(gMediaCache->GetMonitor());
   PRInt64 size = aSize;
   const char* data = aData;
 
   LOG(PR_LOG_DEBUG, ("Stream %p DataReceived at %lld count=%lld",
       this, (long long)mChannelOffset, (long long)aSize));
 
   // We process the data one block (or part of a block) at a time
   while (size > 0) {
@@ -1830,17 +1821,17 @@ nsMediaCacheStream::NotifyDataReceived(P
   mon.NotifyAll();
 }
 
 void
 nsMediaCacheStream::NotifyDataEnded(nsresult aStatus)
 {
   NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
 
-  nsAutoMonitor mon(gMediaCache->Monitor());
+  MonitorAutoEnter mon(gMediaCache->GetMonitor());
 
   PRInt32 blockOffset = PRInt32(mChannelOffset%BLOCK_SIZE);
   if (blockOffset > 0) {
     // Write back the partial block
     memset(reinterpret_cast<char*>(mPartialBlockBuffer) + blockOffset, 0,
            BLOCK_SIZE - blockOffset);
     gMediaCache->AllocateAndWriteBlock(this, mPartialBlockBuffer,
         mMetadataInPartialBlockBuffer ? MODE_METADATA : MODE_PLAYBACK);
@@ -1868,113 +1859,113 @@ nsMediaCacheStream::~nsMediaCacheStream(
     gMediaCache->ReleaseStream(this);
     nsMediaCache::MaybeShutdown();
   }
 }
 
 void
 nsMediaCacheStream::SetSeekable(PRBool aIsSeekable)
 {
-  nsAutoMonitor mon(gMediaCache->Monitor());
+  MonitorAutoEnter mon(gMediaCache->GetMonitor());
   NS_ASSERTION(mIsSeekable || aIsSeekable ||
                mChannelOffset == 0, "channel offset must be zero when we become non-seekable");
   mIsSeekable = aIsSeekable;
   // Queue an Update since we may change our strategy for dealing
   // with this stream
   gMediaCache->QueueUpdate();
 }
 
 PRBool
 nsMediaCacheStream::IsSeekable()
 {
-  nsAutoMonitor mon(gMediaCache->Monitor());
+  MonitorAutoEnter mon(gMediaCache->GetMonitor());
   return mIsSeekable;
 }
 
 void
 nsMediaCacheStream::Close()
 {
   NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
 
-  nsAutoMonitor mon(gMediaCache->Monitor());
-  CloseInternal(&mon);
+  MonitorAutoEnter mon(gMediaCache->GetMonitor());
+  CloseInternal(mon);
   // Queue an Update since we may have created more free space. Don't do
   // it from CloseInternal since that gets called by Update() itself
   // sometimes, and we try to not to queue updates from Update().
   gMediaCache->QueueUpdate();
 }
 
 void
-nsMediaCacheStream::CloseInternal(nsAutoMonitor* aMonitor)
+nsMediaCacheStream::CloseInternal(MonitorAutoEnter& aMonitor)
 {
   NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
 
   if (mClosed)
     return;
   mClosed = PR_TRUE;
   gMediaCache->ReleaseStreamBlocks(this);
   // Wake up any blocked readers
-  aMonitor->NotifyAll();
+  aMonitor.NotifyAll();
 }
 
 void
 nsMediaCacheStream::Pin()
 {
-  nsAutoMonitor mon(gMediaCache->Monitor());
+  MonitorAutoEnter mon(gMediaCache->GetMonitor());
   ++mPinCount;
   // Queue an Update since we may no longer want to read more into the
   // cache, if this stream's block have become non-evictable
   gMediaCache->QueueUpdate();
 }
 
 void
 nsMediaCacheStream::Unpin()
 {
-  nsAutoMonitor mon(gMediaCache->Monitor());
+  MonitorAutoEnter mon(gMediaCache->GetMonitor());
   NS_ASSERTION(mPinCount > 0, "Unbalanced Unpin");
   --mPinCount;
   // Queue an Update since we may be able to read more into the
   // cache, if this stream's block have become evictable
   gMediaCache->QueueUpdate();
 }
 
 PRInt64
 nsMediaCacheStream::GetLength()
 {
-  nsAutoMonitor mon(gMediaCache->Monitor());
+  MonitorAutoEnter mon(gMediaCache->GetMonitor());
   return mStreamLength;
 }
 
 PRInt64
 nsMediaCacheStream::GetNextCachedData(PRInt64 aOffset)
 {
-  nsAutoMonitor mon(gMediaCache->Monitor());
+  MonitorAutoEnter mon(gMediaCache->GetMonitor());
   return GetNextCachedDataInternal(aOffset);
 }
 
 PRInt64
 nsMediaCacheStream::GetCachedDataEnd(PRInt64 aOffset)
 {
-  nsAutoMonitor mon(gMediaCache->Monitor());
+  MonitorAutoEnter mon(gMediaCache->GetMonitor());
   return GetCachedDataEndInternal(aOffset);
 }
 
 PRBool
 nsMediaCacheStream::IsDataCachedToEndOfStream(PRInt64 aOffset)
 {
-  nsAutoMonitor mon(gMediaCache->Monitor());
+  MonitorAutoEnter mon(gMediaCache->GetMonitor());
   if (mStreamLength < 0)
     return PR_FALSE;
   return GetCachedDataEndInternal(aOffset) >= mStreamLength;
 }
 
 PRInt64
 nsMediaCacheStream::GetCachedDataEndInternal(PRInt64 aOffset)
 {
-  PR_ASSERT_CURRENT_THREAD_IN_MONITOR(gMediaCache->Monitor());
+  gMediaCache->GetMonitor().AssertCurrentThreadIn();
   PRUint32 startBlockIndex = aOffset/BLOCK_SIZE;
   PRUint32 blockIndex = startBlockIndex;
   while (blockIndex < mBlocks.Length() && mBlocks[blockIndex] != -1) {
     ++blockIndex;
   }
   PRInt64 result = blockIndex*BLOCK_SIZE;
   if (blockIndex == mChannelOffset/BLOCK_SIZE) {
     // The block containing mChannelOffset may be partially read but not
@@ -1987,17 +1978,17 @@ nsMediaCacheStream::GetCachedDataEndInte
     result = PR_MIN(result, mStreamLength);
   }
   return PR_MAX(result, aOffset);
 }
 
 PRInt64
 nsMediaCacheStream::GetNextCachedDataInternal(PRInt64 aOffset)
 {
-  PR_ASSERT_CURRENT_THREAD_IN_MONITOR(gMediaCache->Monitor());
+  gMediaCache->GetMonitor().AssertCurrentThreadIn();
   if (aOffset == mStreamLength)
     return -1;
 
   PRUint32 startBlockIndex = aOffset/BLOCK_SIZE;
   PRUint32 channelBlockIndex = mChannelOffset/BLOCK_SIZE;
 
   if (startBlockIndex == channelBlockIndex &&
       aOffset < mChannelOffset) {
@@ -2034,40 +2025,40 @@ nsMediaCacheStream::GetNextCachedDataInt
 
   NS_NOTREACHED("Should return in loop");
   return -1;
 }
 
 void
 nsMediaCacheStream::SetReadMode(ReadMode aMode)
 {
-  nsAutoMonitor mon(gMediaCache->Monitor());
+  MonitorAutoEnter mon(gMediaCache->GetMonitor());
   if (aMode == mCurrentMode)
     return;
   mCurrentMode = aMode;
   gMediaCache->QueueUpdate();
 }
 
 void
 nsMediaCacheStream::SetPlaybackRate(PRUint32 aBytesPerSecond)
 {
   NS_ASSERTION(aBytesPerSecond > 0, "Zero playback rate not allowed");
-  nsAutoMonitor mon(gMediaCache->Monitor());
+  MonitorAutoEnter mon(gMediaCache->GetMonitor());
   if (aBytesPerSecond == mPlaybackBytesPerSecond)
     return;
   mPlaybackBytesPerSecond = aBytesPerSecond;
   gMediaCache->QueueUpdate();
 }
 
 nsresult
 nsMediaCacheStream::Seek(PRInt32 aWhence, PRInt64 aOffset)
 {
   NS_ASSERTION(!NS_IsMainThread(), "Don't call on main thread");
 
-  nsAutoMonitor mon(gMediaCache->Monitor());
+  MonitorAutoEnter mon(gMediaCache->GetMonitor());
   if (mClosed)
     return NS_ERROR_FAILURE;
 
   PRInt64 oldOffset = mStreamOffset;
   switch (aWhence) {
   case PR_SEEK_END:
     if (mStreamLength < 0)
       return NS_ERROR_FAILURE;
@@ -2091,26 +2082,26 @@ nsMediaCacheStream::Seek(PRInt32 aWhence
   return NS_OK;
 }
 
 PRInt64
 nsMediaCacheStream::Tell()
 {
   NS_ASSERTION(!NS_IsMainThread(), "Don't call on main thread");
 
-  nsAutoMonitor mon(gMediaCache->Monitor());
+  MonitorAutoEnter mon(gMediaCache->GetMonitor());
   return mStreamOffset;
 }
 
 nsresult
 nsMediaCacheStream::Read(char* aBuffer, PRUint32 aCount, PRUint32* aBytes)
 {
   NS_ASSERTION(!NS_IsMainThread(), "Don't call on main thread");
 
-  nsAutoMonitor mon(gMediaCache->Monitor());
+  MonitorAutoEnter mon(gMediaCache->GetMonitor());
   if (mClosed)
     return NS_ERROR_FAILURE;
 
   PRUint32 count = 0;
   // Read one block (or part of a block) at a time
   while (count < aCount) {
     PRUint32 streamBlock = PRUint32(mStreamOffset/BLOCK_SIZE);
     PRUint32 offsetInStreamBlock =
@@ -2185,17 +2176,17 @@ nsMediaCacheStream::Read(char* aBuffer, 
   return NS_OK;
 }
 
 nsresult
 nsMediaCacheStream::ReadFromCache(char* aBuffer,
                                   PRInt64 aOffset,
                                   PRInt64 aCount)
 {
-  nsAutoMonitor mon(gMediaCache->Monitor());
+  MonitorAutoEnter mon(gMediaCache->GetMonitor());
   if (mClosed)
     return NS_ERROR_FAILURE;
 
   // Read one block (or part of a block) at a time
   PRUint32 count = 0;
   PRInt64 streamOffset = aOffset;
   while (count < aCount) {
     PRUint32 streamBlock = PRUint32(streamOffset/BLOCK_SIZE);
@@ -2263,17 +2254,17 @@ nsMediaCacheStream::InitAsClone(nsMediaC
     return NS_OK;
 
   nsresult rv = Init();
   if (NS_FAILED(rv))
     return rv;
   mResourceID = aOriginal->mResourceID;
 
   // Grab cache blocks from aOriginal as readahead blocks for our stream
-  nsAutoMonitor mon(gMediaCache->Monitor());
+  MonitorAutoEnter mon(gMediaCache->GetMonitor());
 
   mPrincipal = aOriginal->mPrincipal;
   mStreamLength = aOriginal->mStreamLength;
   mIsSeekable = aOriginal->mIsSeekable;
 
   // Cloned streams are initially suspended, since there is no channel open
   // initially for a clone.
   mCacheSuspended = PR_TRUE;
@@ -2293,17 +2284,17 @@ nsMediaCacheStream::InitAsClone(nsMediaC
 
   return NS_OK;
 }
 
 nsresult nsMediaCacheStream::GetCachedRanges(nsTArray<nsByteRange>& aRanges)
 {
   // Take the monitor, so that the cached data ranges can't grow while we're
   // trying to loop over them.
-  nsAutoMonitor mon(gMediaCache->Monitor());
+  MonitorAutoEnter mon(gMediaCache->GetMonitor());
 
   // We must be pinned while running this, otherwise the cached data ranges may
   // shrink while we're trying to loop over them.
   NS_ASSERTION(mPinCount > 0, "Must be pinned");
 
   PRInt64 startOffset = GetNextCachedData(0);
   while (startOffset >= 0) {
     PRInt64 endOffset = GetCachedDataEnd(startOffset);
--- a/content/media/nsMediaCache.h
+++ b/content/media/nsMediaCache.h
@@ -35,21 +35,23 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef nsMediaCache_h_
 #define nsMediaCache_h_
 
 #include "nsTArray.h"
-#include "nsAutoLock.h"
 #include "nsIPrincipal.h"
 #include "nsCOMPtr.h"
 
 class nsByteRange;
+namespace mozilla {
+class MonitorAutoEnter;
+}
 
 /**
  * Media applications want fast, "on demand" random access to media data,
  * for pausing, seeking, etc. But we are primarily interested
  * in transporting media data using HTTP over the Internet, which has
  * high latency to open a connection, requires a new connection for every
  * seek, may not even support seeking on some connections (especially
  * live streams), and uses a push model --- data comes from the server
@@ -204,16 +206,18 @@ class nsMediaChannelStream;
 
 /**
  * If the cache fails to initialize then Init will fail, so nonstatic
  * methods of this class can assume gMediaCache is non-null.
  * 
  * This class can be directly embedded as a value.
  */
 class nsMediaCacheStream {
+  typedef mozilla::MonitorAutoEnter MonitorAutoEnter;
+
 public:
   enum {
     // This needs to be a power of two
     BLOCK_SIZE = 32768
   };
   enum ReadMode {
     MODE_METADATA,
     MODE_PLAYBACK
@@ -424,17 +428,17 @@ private:
   // This method assumes that the cache monitor is held and can be called on
   // any thread.
   PRInt64 GetNextCachedDataInternal(PRInt64 aOffset);
   // A helper function to do the work of closing the stream. Assumes
   // that the cache monitor is held. Main thread only.
   // aMonitor is the nsAutoMonitor wrapper holding the cache monitor.
   // This is used to NotifyAll to wake up threads that might be
   // blocked on reading from this stream.
-  void CloseInternal(nsAutoMonitor* aMonitor);
+  void CloseInternal(MonitorAutoEnter& aMonitor);
   // Update mPrincipal given that data has been received from aPrincipal
   void UpdatePrincipal(nsIPrincipal* aPrincipal);
 
   // These fields are main-thread-only.
   nsMediaChannelStream*  mClient;
   nsCOMPtr<nsIPrincipal> mPrincipal;
   // This is a unique ID representing the resource we're loading.
   // All streams with the same mResourceID are loading the same
--- a/content/media/nsMediaDecoder.cpp
+++ b/content/media/nsMediaDecoder.cpp
@@ -42,26 +42,27 @@
 #include "prlog.h"
 #include "prmem.h"
 #include "nsIFrame.h"
 #include "nsIDocument.h"
 #include "nsThreadUtils.h"
 #include "nsIDOMHTMLMediaElement.h"
 #include "nsNetUtil.h"
 #include "nsHTMLMediaElement.h"
-#include "nsAutoLock.h"
 #include "nsIRenderingContext.h"
 #include "gfxContext.h"
 #include "nsPresContext.h"
 #include "nsDOMError.h"
 #include "nsDisplayList.h"
 #ifdef MOZ_SVG
 #include "nsSVGEffects.h"
 #endif
 
+using namespace mozilla;
+
 // Number of milliseconds between progress events as defined by spec
 #define PROGRESS_MS 350
 
 // Number of milliseconds of no data before a stall event is fired as defined by spec
 #define STALL_MS 3000
 
 // Number of estimated seconds worth of data we need to have buffered 
 // ahead of the current playback position before we allow the media decoder
@@ -70,42 +71,36 @@
 // nsMediaDecoder::CanPlayThrough() calculation more stable in the case of
 // fluctuating bitrates.
 #define CAN_PLAY_THROUGH_MARGIN 10
 
 nsMediaDecoder::nsMediaDecoder() :
   mElement(0),
   mRGBWidth(-1),
   mRGBHeight(-1),
-  mVideoUpdateLock(nsnull),
+  mVideoUpdateLock("nsMediaDecoder.mVideoUpdateLock"),
   mPixelAspectRatio(1.0),
   mFrameBufferLength(0),
   mPinnedForSeek(PR_FALSE),
   mSizeChanged(PR_FALSE),
   mImageContainerSizeChanged(PR_FALSE),
   mShuttingDown(PR_FALSE)
 {
   MOZ_COUNT_CTOR(nsMediaDecoder);
 }
 
 nsMediaDecoder::~nsMediaDecoder()
 {
-  if (mVideoUpdateLock) {
-    nsAutoLock::DestroyLock(mVideoUpdateLock);
-    mVideoUpdateLock = nsnull;
-  }
   MOZ_COUNT_DTOR(nsMediaDecoder);
 }
 
 PRBool nsMediaDecoder::Init(nsHTMLMediaElement* aElement)
 {
   mElement = aElement;
-  mVideoUpdateLock = nsAutoLock::NewLock("nsMediaDecoder::mVideoUpdateLock");
-
-  return mVideoUpdateLock != nsnull;
+  return PR_TRUE;
 }
 
 void nsMediaDecoder::Shutdown()
 {
   StopProgress();
   mElement = nsnull;
 }
 
@@ -137,17 +132,17 @@ void nsMediaDecoder::Invalidate()
 {
   if (!mElement)
     return;
 
   nsIFrame* frame = mElement->GetPrimaryFrame();
   PRBool invalidateFrame = PR_FALSE;
 
   {
-    nsAutoLock lock(mVideoUpdateLock);
+    MutexAutoLock lock(mVideoUpdateLock);
 
     // Get mImageContainerSizeChanged while holding the lock.
     invalidateFrame = mImageContainerSizeChanged;
     mImageContainerSizeChanged = PR_FALSE;
 
     if (mSizeChanged) {
       nsIntSize scaledSize(mRGBWidth, mRGBHeight);
       // Apply the aspect ratio to produce the intrinsic size we report
@@ -253,17 +248,17 @@ void nsMediaDecoder::FireTimeUpdate()
   mElement->FireTimeUpdate(PR_TRUE);
 }
 
 void nsMediaDecoder::SetVideoData(const gfxIntSize& aSize,
                                   float aPixelAspectRatio,
                                   Image* aImage,
                                   TimeStamp aTarget)
 {
-  nsAutoLock lock(mVideoUpdateLock);
+  MutexAutoLock lock(mVideoUpdateLock);
 
   if (mRGBWidth != aSize.width || mRGBHeight != aSize.height ||
       mPixelAspectRatio != aPixelAspectRatio) {
     mRGBWidth = aSize.width;
     mRGBHeight = aSize.height;
     mPixelAspectRatio = aPixelAspectRatio;
     mSizeChanged = PR_TRUE;
   }
@@ -282,17 +277,17 @@ void nsMediaDecoder::SetVideoData(const 
     }
   }
 
   mPaintTarget = aTarget;
 }
 
 double nsMediaDecoder::GetFrameDelay()
 {
-  nsAutoLock lock(mVideoUpdateLock);
+  MutexAutoLock lock(mVideoUpdateLock);
   return mPaintDelay.ToSeconds();
 }
 
 void nsMediaDecoder::PinForSeek()
 {
   nsMediaStream* stream = GetCurrentStream();
   if (!stream || mPinnedForSeek) {
     return;
--- a/content/media/nsMediaDecoder.h
+++ b/content/media/nsMediaDecoder.h
@@ -43,16 +43,17 @@
 #include "nsIPrincipal.h"
 #include "nsSize.h"
 #include "prlog.h"
 #include "gfxContext.h"
 #include "gfxRect.h"
 #include "nsITimer.h"
 #include "ImageLayers.h"
 #include "mozilla/Monitor.h"
+#include "mozilla/Mutex.h"
 
 class nsHTMLMediaElement;
 class nsMediaStream;
 class nsIStreamListener;
 class nsTimeRanges;
 
 // The size to use for audio data frames in MozAudioAvailable events.
 // This value is per channel, and is chosen to give ~43 fps of events,
@@ -84,16 +85,17 @@ private:
 class nsMediaDecoder : public nsIObserver
 {
 public:
   typedef mozilla::TimeStamp TimeStamp;
   typedef mozilla::TimeDuration TimeDuration;
   typedef mozilla::layers::ImageContainer ImageContainer;
   typedef mozilla::layers::Image Image;
   typedef mozilla::Monitor Monitor;
+  typedef mozilla::Mutex Mutex;
 
   nsMediaDecoder();
   virtual ~nsMediaDecoder();
 
   // Create a new decoder of the same type as this one.
   virtual nsMediaDecoder* Clone() = 0;
 
   // Perform any initialization required for the decoder.
@@ -436,17 +438,17 @@ protected:
   // is used in the decoder backend threads and the main thread
   // to ensure that repainting the video does not use these
   // values while they are out of sync (width changed but
   // not height yet, etc).
   // Backends that are updating the height, width or writing
   // to the RGB buffer must obtain this lock first to ensure that
   // the video element does not use video data or sizes that are
   // in the midst of being changed.
-  PRLock* mVideoUpdateLock;
+  Mutex mVideoUpdateLock;
 
   // Pixel aspect ratio (ratio of the pixel width to pixel height)
   float mPixelAspectRatio;
 
   // The framebuffer size to use for audioavailable events.
   PRUint32 mFrameBufferLength;
 
   // PR_TRUE when our media stream has been pinned. We pin the stream
--- a/content/media/nsMediaStream.cpp
+++ b/content/media/nsMediaStream.cpp
@@ -30,21 +30,21 @@
  * 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 "mozilla/Mutex.h"
 #include "nsDebug.h"
 #include "nsMediaStream.h"
 #include "nsMediaDecoder.h"
 #include "nsNetUtil.h"
-#include "nsAutoLock.h"
 #include "nsThreadUtils.h"
 #include "nsIFile.h"
 #include "nsIFileChannel.h"
 #include "nsIHttpChannel.h"
 #include "nsISeekableStream.h"
 #include "nsIInputStream.h"
 #include "nsIOutputStream.h"
 #include "nsIRequestObserver.h"
@@ -56,38 +56,35 @@
 #include "nsDOMError.h"
 #include "nsICachingChannel.h"
 #include "nsURILoader.h"
 #include "nsIAsyncVerifyRedirectCallback.h"
 
 #define HTTP_OK_CODE 200
 #define HTTP_PARTIAL_RESPONSE_CODE 206
 
-using mozilla::TimeStamp;
+using namespace mozilla;
 
 nsMediaChannelStream::nsMediaChannelStream(nsMediaDecoder* aDecoder,
     nsIChannel* aChannel, nsIURI* aURI)
   : nsMediaStream(aDecoder, aChannel, aURI),
     mOffset(0), mSuspendCount(0),
     mReopenOnError(PR_FALSE), mIgnoreClose(PR_FALSE),
     mCacheStream(this),
-    mLock(nsAutoLock::NewLock("media.channel.stream")),
+    mLock("nsMediaChannelStream.mLock"),
     mCacheSuspendCount(0)
 {
 }
 
 nsMediaChannelStream::~nsMediaChannelStream()
 {
   if (mListener) {
     // Kill its reference to us since we're going away
     mListener->Revoke();
   }
-  if (mLock) {
-    nsAutoLock::DestroyLock(mLock);
-  }
 }
 
 // nsMediaChannelStream::Listener just observes the channel and
 // forwards notifications to the nsMediaChannelStream. We use multiple
 // listener objects so that when we open a new stream for a seek we can
 // disconnect the old listener from the nsMediaChannelStream and hook up
 // a new listener, so notifications from the old channel are discarded
 // and don't confuse us.
@@ -266,17 +263,17 @@ nsMediaChannelStream::OnStartRequest(nsI
     PRBool fromCache = PR_FALSE;
     rv = cc->IsFromCache(&fromCache);
     if (NS_SUCCEEDED(rv) && !fromCache) {
       cc->SetCacheAsFile(PR_TRUE);
     }
   }
 
   {
-    nsAutoLock lock(mLock);
+    MutexAutoLock lock(mLock);
     mChannelStatistics.Start(TimeStamp::Now());
   }
 
   mReopenOnError = PR_FALSE;
   mIgnoreClose = PR_FALSE;
   if (mSuspendCount > 0) {
     // Re-suspend the channel if it needs to be suspended
     mChannel->Suspend();
@@ -292,17 +289,17 @@ nsMediaChannelStream::OnStartRequest(nsI
 nsresult
 nsMediaChannelStream::OnStopRequest(nsIRequest* aRequest, nsresult aStatus)
 {
   NS_ASSERTION(mChannel.get() == aRequest, "Wrong channel!");
   NS_ASSERTION(mSuspendCount == 0,
                "How can OnStopRequest fire while we're suspended?");
 
   {
-    nsAutoLock lock(mLock);
+    MutexAutoLock lock(mLock);
     mChannelStatistics.Stop(TimeStamp::Now());
   }
 
   // Note that aStatus might have succeeded --- this might be a normal close
   // --- even in situations where the server cut us off because we were
   // suspended. So we need to "reopen on error" in that case too. The only
   // cases where we don't need to reopen are when *we* closed the stream.
   // But don't reopen if we need to seek and we don't think we can... that would
@@ -378,17 +375,17 @@ nsMediaChannelStream::CopySegmentToCache
 nsresult
 nsMediaChannelStream::OnDataAvailable(nsIRequest* aRequest,
                                       nsIInputStream* aStream,
                                       PRUint32 aCount)
 {
   NS_ASSERTION(mChannel.get() == aRequest, "Wrong channel!");
 
   {
-    nsAutoLock lock(mLock);
+    MutexAutoLock lock(mLock);
     mChannelStatistics.AddBytes(aCount);
   }
 
   CopySegmentClosure closure;
   nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
   if (secMan && mChannel) {
     secMan->GetChannelPrincipal(mChannel, getter_AddRefs(closure.mPrincipal));
   }
@@ -407,18 +404,16 @@ nsMediaChannelStream::OnDataAvailable(ns
 
   return NS_OK;
 }
 
 nsresult nsMediaChannelStream::Open(nsIStreamListener **aStreamListener)
 {
   NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
 
-  if (!mLock)
-    return NS_ERROR_OUT_OF_MEMORY;
   nsresult rv = mCacheStream.Init();
   if (NS_FAILED(rv))
     return rv;
   NS_ASSERTION(mOffset == 0, "Who set mOffset already?");
 
   if (!mChannel) {
     // When we're a clone, the decoder might ask us to Open even though
     // we haven't established an mChannel (because we might not need one)
@@ -544,17 +539,17 @@ nsMediaStream* nsMediaChannelStream::Clo
   return stream;
 }
 
 void nsMediaChannelStream::CloseChannel()
 {
   NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
 
   {
-    nsAutoLock lock(mLock);
+    MutexAutoLock lock(mLock);
     mChannelStatistics.Stop(TimeStamp::Now());
   }
 
   if (mListener) {
     mListener->Revoke();
     mListener = nsnull;
   }
 
@@ -623,17 +618,17 @@ void nsMediaChannelStream::Suspend(PRBoo
   if (mChannel) {
     if (aCloseImmediately && mCacheStream.IsSeekable()) {
       // Kill off our channel right now, but don't tell anyone about it.
       mIgnoreClose = PR_TRUE;
       CloseChannel();
       element->DownloadSuspended();
     } else if (mSuspendCount == 0) {
       {
-        nsAutoLock lock(mLock);
+        MutexAutoLock lock(mLock);
         mChannelStatistics.Stop(TimeStamp::Now());
       }
       mChannel->Suspend();
       element->DownloadSuspended();
     }
   }
 
   ++mSuspendCount;
@@ -651,17 +646,17 @@ void nsMediaChannelStream::Resume()
   }
 
   NS_ASSERTION(mSuspendCount > 0, "Resume without previous Suspend!");
   --mSuspendCount;
   if (mSuspendCount == 0) {
     if (mChannel) {
       // Just wake up our existing channel
       {
-        nsAutoLock lock(mLock);
+        MutexAutoLock lock(mLock);
         mChannelStatistics.Start(TimeStamp::Now());
       }
       // if an error occurs after Resume, assume it's because the server
       // timed out the connection and we should reopen it.
       mReopenOnError = PR_TRUE;
       mChannel->Resume();
       element->DownloadResumed();
     } else {
@@ -757,17 +752,17 @@ nsMediaChannelStream::CacheClientSeek(PR
 
   CloseChannel();
 
   if (aResume) {
     NS_ASSERTION(mSuspendCount > 0, "Too many resumes!");
     // No need to mess with the channel, since we're making a new one
     --mSuspendCount;
     {
-      nsAutoLock lock(mLock);
+      MutexAutoLock lock(mLock);
       NS_ASSERTION(mCacheSuspendCount > 0, "CacheClientSeek(aResume=true) without previous CacheClientSuspend!");
       --mCacheSuspendCount;
     }
   }
 
   nsresult rv = RecreateChannel();
   if (NS_FAILED(rv))
     return rv;
@@ -775,31 +770,31 @@ nsMediaChannelStream::CacheClientSeek(PR
   mOffset = aOffset;
   return OpenChannel(nsnull);
 }
 
 nsresult
 nsMediaChannelStream::CacheClientSuspend()
 {
   {
-    nsAutoLock lock(mLock);
+    MutexAutoLock lock(mLock);
     ++mCacheSuspendCount;
   }
   Suspend(PR_FALSE);
 
   mDecoder->NotifySuspendedStatusChanged();
   return NS_OK;
 }
 
 nsresult
 nsMediaChannelStream::CacheClientResume()
 {
   Resume();
   {
-    nsAutoLock lock(mLock);
+    MutexAutoLock lock(mLock);
     NS_ASSERTION(mCacheSuspendCount > 0, "CacheClientResume without previous CacheClientSuspend!");
     --mCacheSuspendCount;
   }
 
   mDecoder->NotifySuspendedStatusChanged();
   return NS_OK;
 }
 
@@ -819,24 +814,24 @@ PRBool
 nsMediaChannelStream::IsDataCachedToEndOfStream(PRInt64 aOffset)
 {
   return mCacheStream.IsDataCachedToEndOfStream(aOffset);
 }
 
 PRBool
 nsMediaChannelStream::IsSuspendedByCache()
 {
-  nsAutoLock lock(mLock);
+  MutexAutoLock lock(mLock);
   return mCacheSuspendCount > 0;
 }
 
 PRBool
 nsMediaChannelStream::IsSuspended()
 {
-  nsAutoLock lock(mLock);
+  MutexAutoLock lock(mLock);
   return mSuspendCount > 0;
 }
 
 void
 nsMediaChannelStream::SetReadMode(nsMediaCacheStream::ReadMode aMode)
 {
   mCacheStream.SetReadMode(aMode);
 }
@@ -857,39 +852,36 @@ void
 nsMediaChannelStream::Unpin()
 {
   mCacheStream.Unpin();
 }
 
 double
 nsMediaChannelStream::GetDownloadRate(PRPackedBool* aIsReliable)
 {
-  nsAutoLock lock(mLock);
+  MutexAutoLock lock(mLock);
   return mChannelStatistics.GetRate(TimeStamp::Now(), aIsReliable);
 }
 
 PRInt64
 nsMediaChannelStream::GetLength()
 {
   return mCacheStream.GetLength();
 }
 
 class nsMediaFileStream : public nsMediaStream
 {
 public:
   nsMediaFileStream(nsMediaDecoder* aDecoder, nsIChannel* aChannel, nsIURI* aURI) :
     nsMediaStream(aDecoder, aChannel, aURI), mSize(-1),
-    mLock(nsAutoLock::NewLock("media.file.stream"))
+    mLock("nsMediaFileStream.mLock")
   {
   }
   ~nsMediaFileStream()
   {
-    if (mLock) {
-      nsAutoLock::DestroyLock(mLock);
-    }
   }
 
   // Main thread
   virtual nsresult Open(nsIStreamListener** aStreamListener);
   virtual nsresult Close();
   virtual void     Suspend(PRBool aCloseImmediately) {}
   virtual void     Resume() {}
   virtual already_AddRefed<nsIPrincipal> GetCurrentPrincipal();
@@ -930,17 +922,17 @@ private:
   // The file size, or -1 if not known. Immutable after Open().
   PRInt64 mSize;
 
   // This lock handles synchronisation between calls to Close() and
   // the Read, Seek, etc calls. Close must not be called while a
   // Read or Seek is in progress since it resets various internal
   // values to null.
   // This lock protects mSeekable and mInput.
-  PRLock* mLock;
+  Mutex mLock;
 
   // Seekable stream interface to file. This can be used from any
   // thread.
   nsCOMPtr<nsISeekableStream> mSeekable;
 
   // Input stream for the media data. This can be used from any
   // thread.
   nsCOMPtr<nsIInputStream>  mInput;
@@ -1036,17 +1028,17 @@ nsresult nsMediaFileStream::Open(nsIStre
   NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
   return NS_OK;
 }
 
 nsresult nsMediaFileStream::Close()
 {
   NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
 
-  nsAutoLock lock(mLock);
+  MutexAutoLock lock(mLock);
   if (mChannel) {
     mChannel->Cancel(NS_ERROR_PARSED_DATA_CACHED);
     mChannel = nsnull;
     mInput = nsnull;
     mSeekable = nsnull;
   }
 
   return NS_OK;
@@ -1082,17 +1074,17 @@ nsMediaStream* nsMediaFileStream::CloneD
   if (NS_FAILED(rv))
     return nsnull;
 
   return new nsMediaFileStream(aDecoder, channel, mURI);
 }
 
 nsresult nsMediaFileStream::ReadFromCache(char* aBuffer, PRInt64 aOffset, PRUint32 aCount)
 {
-  nsAutoLock lock(mLock);
+  MutexAutoLock lock(mLock);
   if (!mInput || !mSeekable)
     return NS_ERROR_FAILURE;
   PRInt64 offset = 0;
   nsresult res = mSeekable->Tell(&offset);
   NS_ENSURE_SUCCESS(res,res);
   res = mSeekable->Seek(nsISeekableStream::NS_SEEK_SET, aOffset);
   NS_ENSURE_SUCCESS(res,res);
   PRUint32 bytesRead = 0;
@@ -1111,37 +1103,37 @@ nsresult nsMediaFileStream::ReadFromCach
   NS_ENSURE_SUCCESS(res,res);
 
   // Else we succeed if the reset-seek succeeds.
   return seekres;
 }
 
 nsresult nsMediaFileStream::Read(char* aBuffer, PRUint32 aCount, PRUint32* aBytes)
 {
-  nsAutoLock lock(mLock);
+  MutexAutoLock lock(mLock);
   if (!mInput)
     return NS_ERROR_FAILURE;
   return mInput->Read(aBuffer, aCount, aBytes);
 }
 
 nsresult nsMediaFileStream::Seek(PRInt32 aWhence, PRInt64 aOffset)
 {
   NS_ASSERTION(!NS_IsMainThread(), "Don't call on main thread");
 
-  nsAutoLock lock(mLock);
+  MutexAutoLock lock(mLock);
   if (!mSeekable)
     return NS_ERROR_FAILURE;
   return mSeekable->Seek(aWhence, aOffset);
 }
 
 PRInt64 nsMediaFileStream::Tell()
 {
   NS_ASSERTION(!NS_IsMainThread(), "Don't call on main thread");
 
-  nsAutoLock lock(mLock);
+  MutexAutoLock lock(mLock);
   if (!mSeekable)
     return 0;
 
   PRInt64 offset = 0;
   mSeekable->Tell(&offset);
   return offset;
 }
 
--- a/content/media/nsMediaStream.h
+++ b/content/media/nsMediaStream.h
@@ -33,24 +33,24 @@
  * 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(nsMediaStream_h_)
 #define nsMediaStream_h_
 
+#include "mozilla/Mutex.h"
 #include "mozilla/XPCOM.h"
 #include "nsIChannel.h"
 #include "nsIPrincipal.h"
 #include "nsIURI.h"
 #include "nsIStreamListener.h"
 #include "nsIChannelEventSink.h"
 #include "nsIInterfaceRequestor.h"
-#include "prlock.h"
 #include "nsMediaCache.h"
 
 // For HTTP seeking, if number of bytes needing to be
 // seeked forward is less than this value then a read is
 // done rather than a byte range request.
 #define SEEK_VS_READ_THRESHOLD (32*1024)
 
 #define HTTP_REQUESTED_RANGE_NOT_SATISFIABLE_CODE 416
@@ -339,16 +339,18 @@ protected:
  * Much of its functionality is actually delegated to nsMediaCache via
  * an underlying nsMediaCacheStream.
  *
  * All synchronization is performed by nsMediaCacheStream; all off-main-
  * thread operations are delegated directly to that object.
  */
 class nsMediaChannelStream : public nsMediaStream
 {
+  typedef mozilla::Mutex Mutex;
+
 public:
   nsMediaChannelStream(nsMediaDecoder* aDecoder, nsIChannel* aChannel, nsIURI* aURI);
   ~nsMediaChannelStream();
 
   // These are called on the main thread by nsMediaCache. These must
   // not block or grab locks, because the media cache is holding its lock.
   // Notify that data is available from the cache. This can happen even
   // if this stream didn't read any data, since another stream might have
@@ -463,14 +465,14 @@ protected:
   // When this flag is set, we should not report the next close of the
   // channel.
   PRPackedBool       mIgnoreClose;
 
   // Any thread access
   nsMediaCacheStream mCacheStream;
 
   // This lock protects mChannelStatistics and mCacheSuspendCount
-  PRLock* mLock;
+  Mutex               mLock;
   nsChannelStatistics mChannelStatistics;
   PRUint32            mCacheSuspendCount;
 };
 
 #endif
--- a/content/media/ogg/nsOggReader.cpp
+++ b/content/media/ogg/nsOggReader.cpp
@@ -642,75 +642,86 @@ PRBool nsOggReader::DecodeVideoFrame(PRB
       if (DecodeTheora(frames, &packet) == NS_ERROR_OUT_OF_MEMORY) {
         NS_WARNING("Theora decode memory allocation failure!");
         return PR_FALSE;
       }
 
     } while (packet.granulepos <= 0 && !endOfStream);
 
     if (packet.granulepos > 0) {
-      // We have captured a granulepos. Backpropagate the granulepos
-      // to determine buffered packets' timestamps.
-      PRInt64 succGranulepos = packet.granulepos;
-      int version_3_2_1 = TheoraVersion(&mTheoraState->mInfo,3,2,1);
-      int shift = mTheoraState->mInfo.keyframe_granule_shift;
-      for (int i = frames.Length() - 2; i >= 0; --i) {
-        PRInt64 granulepos = succGranulepos;
+      // Reconstruct the granulepos (and thus timestamps) of the decoded
+      // frames. Granulepos are stored as ((keyframe<<shift)+offset). We
+      // know the granulepos of the last frame in the list, so we can infer
+      // the granulepos of the intermediate frames using their frame numbers.
+      ogg_int64_t shift = mTheoraState->mInfo.keyframe_granule_shift;
+      ogg_int64_t version_3_2_1 = TheoraVersion(&mTheoraState->mInfo,3,2,1);
+      ogg_int64_t lastFrame = th_granule_frame(mTheoraState->mCtx,
+                                               packet.granulepos) + version_3_2_1;
+      ogg_int64_t firstFrame = lastFrame - frames.Length() + 1;
+
+      // Until we encounter a keyframe, we'll assume that the "keyframe"
+      // segment of the granulepos is the first frame, or if that causes
+      // the "offset" segment to overflow, we assume the required
+      // keyframe is maximumally offset. Until we encounter a keyframe
+      // the granulepos will probably be wrong, but we can't decode the
+      // frame anyway (since we don't have its keyframe) so it doesn't really
+      // matter.
+      ogg_int64_t keyframe = packet.granulepos >> shift;
+
+      // The lastFrame, firstFrame, keyframe variables, as well as the frame
+      // variable in the loop below, store the frame number for Theora
+      // version >= 3.2.1 streams, and store the frame index for Theora
+      // version < 3.2.1 streams.
+      for (PRUint32 i = 0; i < frames.Length() - 1; ++i) {
+        ogg_int64_t frame = firstFrame + i;
+        ogg_int64_t granulepos;
         if (frames[i]->mKeyframe) {
-          // This frame is a keyframe. It's granulepos is the previous granule
-          // number minus 1, shifted by granuleshift.
-          ogg_int64_t frame_index = th_granule_frame(mTheoraState->mCtx,
-                                                     granulepos);
-          granulepos = (frame_index + version_3_2_1 - 1) << shift;
-          // Theora 3.2.1+ granulepos store frame number [1..N], so granulepos
-          // should be > 0.
-          // Theora 3.2.0 granulepos store the frame index [0..(N-1)], so
-          // granulepos should be >= 0. 
-          NS_ASSERTION((version_3_2_1 && granulepos > 0) ||
-                       granulepos >= 0, "Should have positive granulepos");
+          granulepos = frame << shift;
+          keyframe = frame;
+        } else if (frame >= keyframe &&
+                   frame - keyframe < ((ogg_int64_t)1 << shift))
+        {
+          // (frame - keyframe) won't overflow the "offset" segment of the
+          // granulepos, so it's safe to calculate the granulepos.
+          granulepos = (keyframe << shift) + (frame - keyframe);
         } else {
-          // Packet is not a keyframe. It's granulepos depends on its successor
-          // packet...
-          if (frames[i+1]->mKeyframe) {
-            // The successor frame is a keyframe, so we can't just subtract 1
-            // from the "keyframe offset" part of its granulepos, as it
-            // doesn't have one! So fake it, take the keyframe offset as the
-            // max possible keyframe offset. This means the granulepos (probably)
-            // overshoots and claims that it depends on a frame before its actual
-            // keyframe but at least its granule number will be correct, so the
-            // times we calculate from this granulepos will also be correct.
-            ogg_int64_t frameno = th_granule_frame(mTheoraState->mCtx,
-                                                   granulepos);
-            ogg_int64_t max_offset = NS_MIN((frameno - 1),
-                                         (ogg_int64_t)(1 << shift) - 1);
-            ogg_int64_t granule = frameno +
-                                  TheoraVersion(&mTheoraState->mInfo,3,2,1) -
-                                  1 - max_offset;
-            NS_ASSERTION(granule > 0, "Must have positive granulepos");
-            granulepos = (granule << shift) + max_offset;
-          } else {
-            // Neither previous nor this frame are keyframes, so we can just
-            // decrement the previous granulepos to calculate this frames
-            // granulepos.
-            --granulepos;
-          }
+          // (frame - keyframeno) will overflow the "offset" segment of the
+          // granulepos, so we take "keyframe" to be the max possible offset
+          // frame instead.
+          ogg_int64_t k = NS_MAX(frame - (((ogg_int64_t)1 << shift) - 1), version_3_2_1);
+          granulepos = (k << shift) + (frame - k);
         }
-        // Check that the frame's granule number (it's frame number) is
-        // one less than the successor frame.
-        NS_ASSERTION(th_granule_frame(mTheoraState->mCtx, succGranulepos) ==
-                     th_granule_frame(mTheoraState->mCtx, granulepos) + 1,
+        // Theora 3.2.1+ granulepos store frame number [1..N], so granulepos
+        // should be > 0.
+        // Theora 3.2.0 granulepos store the frame index [0..(N-1)], so
+        // granulepos should be >= 0. 
+        NS_ASSERTION(granulepos >= version_3_2_1,
+                     "Invalid granulepos for Theora version");
+
+        // Check that the frame's granule number is one more than the
+        // previous frame's.
+        NS_ASSERTION(i == 0 ||
+                     th_granule_frame(mTheoraState->mCtx, granulepos) ==
+                     th_granule_frame(mTheoraState->mCtx, frames[i-1]->mTimecode) + 1,
                      "Granulepos calculation is incorrect!");
+
         frames[i]->mTime = mTheoraState->StartTime(granulepos);
         frames[i]->mEndTime = frames[i]->mTime + mTheoraState->mFrameDuration;
         NS_ASSERTION(frames[i]->mEndTime >= frames[i]->mTime, "Frame must start before it ends.");
         frames[i]->mTimecode = granulepos;
-        succGranulepos = granulepos;
-        NS_ASSERTION(frames[i]->mTime < frames[i+1]->mTime, "Times should increase");      
       }
       NS_ASSERTION(AllFrameTimesIncrease(frames), "All frames must have granulepos");
+
+      // Check that the second to last frame's granule number is one less than
+      // the last frame's (the known granule number). If not our granulepos
+      // recovery missed a beat.
+      NS_ASSERTION(frames.Length() < 2 ||
+        th_granule_frame(mTheoraState->mCtx, frames[frames.Length()-2]->mTimecode) + 1 ==
+        th_granule_frame(mTheoraState->mCtx, packet.granulepos),
+        "Granulepos recovery should catch up with packet.granulepos!");
     }
   } else {
     
     NS_ASSERTION(mTheoraGranulepos > 0, "We must Theora granulepos!");
     
     if (!ReadOggPacket(mTheoraState, &packet)) {
       // Failed to read from file, so EOF or other premature failure.
       // Inform the queue that there will be no more frames.
--- 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) +
--- a/content/media/test/test_info_leak.html
+++ b/content/media/test/test_info_leak.html
@@ -21,80 +21,86 @@ https://bugzilla.mozilla.org/show_bug.cg
 
 <pre id="test">
 <script type="application/javascript">
 
 /** Test for Bug 478957 **/
 
 // Tests whether we leak events and state change info when loading stuff from local files from a webserver.
 
+var manager = new MediaTestManager;
 
 var gEventTypes = [ 'loadstart', 'progress', 'suspend', 'abort', 'error', 'emptied', 'stalled', 'play', 'pause', 'loadedmetadata', 'loadeddata', 'waiting', 'playing', 'canplay', 'canplaythrough', 'seeking', 'seeked', 'timeupdate', 'ended', 'ratechange', 'durationchange', 'volumechange' ];
 
-var gMedia = null;
 var gExpectedEvents = ['loadstart', 'error'];
-var gTestNum = 0;
-var gEventNum = 0;
-var gFinished = false;
+
+function createTestArray() {
+  var tests = [];
+  var tmpVid = document.createElement("video");
+
+  for (var testNum=0; testNum<gSeekTests.length; testNum++) {
+    var test = gInfoLeakTests[testNum];
+    if (!tmpVid.canPlayType(test.type)) {
+      continue;
+    }
+
+    var t = new Object;
+    t.name = test.src;
+    t.type = test.type;
+
+    tests.push(t);
+  }
+  return tests;
+}
 
 function log(msg) {
   //dump(msg + "\n");
   var l = document.getElementById('log');
   l.innerHTML += msg + "<br>";
 }
 
-function nextTest() {
-  mediaTestCleanup();
-  if (gTestNum == gInfoLeakTests.length) {
-    gFinished = true;
-    SimpleTest.finish();
-    return;
-  }
-  if (gMedia && gMedia.parentNode) {
-    gMedia.parentNode.removeChild(gMedia);
-    gMedia = null;
-  }
-  var t = gInfoLeakTests[gTestNum];
-
-  log("Testing: " + t.type + " @ " + t.src);
-
-  gTestNum++;
-  gEventNum = 0;
-  createMedia(t.type, t.src);
+function finish(v) {
+  log("finish: " + v.name);
+  v.parentNode.removeChild(v);
+  clearInterval(v.checkStateInterval);
+  manager.finished(v.token);
+  v = null;
 }
 
 function listener(evt) {
-  //log('event ' + evt.type);
-  ok(gEventNum < gExpectedEvents.length, "Too many events received");
-  var expected = (gEventNum < gExpectedEvents.length) ? gExpectedEvents[gEventNum] : "NoEvent";
-  is(evt.type, expected, "Events received in wrong order");
-  gEventNum++;
-  if (gEventNum == gExpectedEvents.length) {
+  var v = evt.target;
+  //log(filename(v.name) + ' got event ' + evt.type);
+  ok(v.eventNum < gExpectedEvents.length, filename(v.name)  + " Too many events received");
+  var expected = (v.eventNum < gExpectedEvents.length) ? gExpectedEvents[v.eventNum] : "NoEvent";
+  is(evt.type, expected, filename(v.name) + " Events received in wrong order");
+  v.eventNum++;
+  if (v.eventNum == gExpectedEvents.length) {
     // In one second, move onto the next test. This give a chance for any
     // other events to come in. Note: we don't expect any events to come
     // in, unless we've leaked some info, and 1 second should be enough time
     // for the leak to show up.
-    setTimeout(nextTest, 1000); 
+    setTimeout(function() {finish(v);}, 1000); 
   }
 }
 
-function createMedia(type, src) {
+function createMedia(type, src, token) {
   var tag = /^video/.test(type) ? "video" : "audio";
-  gMedia = document.createElement(tag);
-  if (!gMedia.canPlayType(type)) {
-    gMedia = null;
-    setTimeout(nextTest, 0);
-    return;
+  var v = document.createElement(tag);
+  for (var i=0; i<gEventTypes.length; i++) {
+    v.addEventListener(gEventTypes[i], listener, false);
   }
-  for (var i=0; i<gEventTypes.length; i++) {
-    gMedia.addEventListener(gEventTypes[i], listener, false);
-  }
-  gMedia.src = src;
-  gMedia._name = src;
-  document.body.appendChild(gMedia);
+  v.src = src;
+  v.name = src;
+  document.body.appendChild(v);
+  v.eventNum = 0;
+  v.token = token;
+  setTimeout(
+  function() {
+    v.checkStateInterval = setInterval(function(){checkState(v);},1);
+  }, 0);
 }
 
 // Define our own ok() and is() functions. The mochitest ones take ages constructing the log
 // of all the passes, so only report failures.
 function test_ok(b, msg) {
   if (!b) {
     log("FAILED test_ok: " + msg);
     ok(b, msg);
@@ -107,49 +113,44 @@ function test_is(a, b, msg) {
     is(a,b,msg);
   }
 }
 
 function filename(uri) {
   return uri.substr(uri.lastIndexOf("/")+1);
 }
 
-function checkState() {
-  if (gMedia != null) {
-    test_ok(gMedia.networkState <= HTMLMediaElement.NETWORK_LOADING || 
-            gMedia.networkState == HTMLMediaElement.NETWORK_NO_SOURCE,
-            "NetworkState of " + gMedia.networkState + " was leaked.");
-    test_ok(gMedia.readyState == HTMLMediaElement.HAVE_NOTHING,
-            "Ready state of " + gMedia.readyState + " was leaked");
-    test_is(gMedia.seeking, false, "Seeking leaked");
-    test_is(gMedia.currentTime, 0, "Leaked currentTime");
-    test_ok(isNaN(gMedia.duration), "Leaked duration");
-    test_is(gMedia.paused, true, "Paused leaked");
-    test_is(gMedia.ended, false, "Ended leaked");
-    test_is(gMedia.autoplay, false, "Autoplay leaked");
-    test_is(gMedia.controls, false, "Controls leaked");
-    test_is(gMedia.muted, false, "muted leaked");
-    test_ok(gMedia.error==null || gMedia.error.code==MediaError.MEDIA_ERR_SRC_NOT_SUPPORTED,
-            "Error code should not exist or be SRC_NOT_SUPPORTED. gMedia.error=" +
-            (gMedia.error ? gMedia.error.code : "null"));
-    test_ok(filename(gMedia.currentSrc) == filename(gMedia._name) ||
-            gMedia.networkState == HTMLMediaElement.NETWORK_NO_SOURCE,
-            "currentSrc should match candidate uri, if we've got a valid source");
-  }
-  if (!gFinished) {
-    setTimeout(checkState, 1);
-  }
+function checkState(v) {
+  test_ok(v.networkState <= HTMLMediaElement.NETWORK_LOADING || 
+          v.networkState == HTMLMediaElement.NETWORK_NO_SOURCE,
+          "NetworkState of " + v.networkState + " was leaked.");
+  test_ok(v.readyState == HTMLMediaElement.HAVE_NOTHING,
+          "Ready state of " + v.readyState + " was leaked");
+  test_is(v.seeking, false, "Seeking leaked");
+  test_is(v.currentTime, 0, "Leaked currentTime");
+  test_ok(isNaN(v.duration), "Leaked duration");
+  test_is(v.paused, true, "Paused leaked");
+  test_is(v.ended, false, "Ended leaked");
+  test_is(v.autoplay, false, "Autoplay leaked");
+  test_is(v.controls, false, "Controls leaked");
+  test_is(v.muted, false, "muted leaked");
+  test_ok(v.error==null || v.error.code==MediaError.MEDIA_ERR_SRC_NOT_SUPPORTED,
+          "Error code should not exist or be SRC_NOT_SUPPORTED. v.error=" +
+          (v.error ? v.error.code : "null"));
+  test_ok(filename(v.currentSrc) == filename(v.name) ||
+          v.networkState == HTMLMediaElement.NETWORK_NO_SOURCE,
+          "currentSrc should match candidate uri, if we've got a valid source");
 }
 
 
-function startTest() {
-  setTimeout(nextTest, 0);
-  setTimeout(checkState,1);
+function startTest(test, token) {
+  manager.started(token);
+  log("Testing: " + test.type + " @ " + test.name);
+  createMedia(test.type, test.name, token);  
 }
 
-addLoadEvent(startTest);
-SimpleTest.waitForExplicitFinish();
+manager.runTests(createTestArray(), startTest);
 
 </script>
 </pre>
 
 </body>
 </html>
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.99, "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.99, "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 0000000000000000000000000000000000000000..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 0000000000000000000000000000000000000000..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) {