Merging mozilla-inbound into mozilla-central.
authorMounir Lamouri <mounir.lamouri@gmail.com>
Thu, 11 Aug 2011 13:08:47 +0200
changeset 75016 10915aa173656cfb998cb28e04df37938b81b498
parent 74976 42f7ed136034ff21ace377575e79e8d504a5356f (current diff)
parent 75015 ddf95830a967af2ba480da2cdc7926746fc05579 (diff)
child 75017 f4537a268e6f7cefdbbb8274ae17bfbb754f8ef7
child 75031 73351129648ba23724ec373f010d5c5ccf8ea335
child 75075 f803e527662ba9b22c45b767c6bd7e64ec00c70c
push id235
push userbzbarsky@mozilla.com
push dateTue, 27 Sep 2011 17:13:04 +0000
treeherdermozilla-beta@2d1e082d176a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone8.0a1
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
Merging mozilla-inbound into mozilla-central.
config/rules.mk
content/base/src/nsAttrValue.cpp
content/base/src/nsAttrValue.h
js/src/config/rules.mk
testing/xpcshell/runxpcshelltests.py
--- a/build/mobile/sutagent/android/watcher/WatcherService.java
+++ b/build/mobile/sutagent/android/watcher/WatcherService.java
@@ -83,30 +83,36 @@ public class WatcherService extends Serv
     Process    pProc;
     Context myContext = null;
     Timer myTimer = null;
     private PowerManager.WakeLock pwl = null;
     public static final int NOTIFICATION_ID = 1964;
     boolean bInstalling = false;
 
     @SuppressWarnings("unchecked")
+    private static final Class<?>[] mSetForegroundSignature = new Class[] {
+    boolean.class};
+    @SuppressWarnings("unchecked")
     private static final Class[] mStartForegroundSignature = new Class[] {
         int.class, Notification.class};
     @SuppressWarnings("unchecked")
     private static final Class[] mStopForegroundSignature = new Class[] {
         boolean.class};
 
     private NotificationManager mNM;
+    private Method mSetForeground;
     private Method mStartForeground;
     private Method mStopForeground;
+    private Object[] mSetForegroundArgs = new Object[1];
     private Object[] mStartForegroundArgs = new Object[2];
     private Object[] mStopForegroundArgs = new Object[1];
 
 
     private IWatcherService.Stub stub = new IWatcherService.Stub() {
+        @Override
         public int UpdateApplication(String sAppName, String sFileName, String sOutFile, int bReboot) throws RemoteException
             {
             return UpdtApp(sAppName, sFileName, sOutFile, bReboot);
             }
     };
 
     @Override
     public IBinder onBind(Intent arg0) {
@@ -274,16 +280,22 @@ public class WatcherService extends Serv
                     mStartForeground = getClass().getMethod("startForeground", mStartForegroundSignature);
                     mStopForeground = getClass().getMethod("stopForeground", mStopForegroundSignature);
                     }
                 catch (NoSuchMethodException e)
                     {
                     // Running on an older platform.
                     mStartForeground = mStopForeground = null;
                     }
+                try {
+                    mSetForeground = getClass().getMethod("setForeground", mSetForegroundSignature);
+                    }
+                catch (NoSuchMethodException e) {
+                    mSetForeground = null;
+                    }
                 Notification notification = new Notification();
                 startForegroundCompat(R.string.foreground_service_started, notification);
                 }
             };
         t.start();
         }
 
     /**
@@ -303,17 +315,28 @@ public class WatcherService extends Serv
             } catch (IllegalAccessException e) {
                 // Should not happen.
                 Log.w("ApiDemos", "Unable to invoke startForeground", e);
             }
             return;
         }
 
         // Fall back on the old API.
-        setForeground(true);
+        if  (mSetForeground != null) {
+            try {
+                mSetForegroundArgs[0] = Boolean.TRUE;
+                mSetForeground.invoke(this, mSetForegroundArgs);
+            } catch (IllegalArgumentException e) {
+                e.printStackTrace();
+            } catch (IllegalAccessException e) {
+                e.printStackTrace();
+            } catch (InvocationTargetException e) {
+                e.printStackTrace();
+            }
+        }
         mNM.notify(id, notification);
     }
 
     /**
      * This is a wrapper around the new stopForeground method, using the older
      * APIs if it is not available.
      */
     void stopForegroundCompat(int id) {
@@ -330,17 +353,28 @@ public class WatcherService extends Serv
                 Log.w("ApiDemos", "Unable to invoke stopForeground", e);
             }
             return;
         }
 
         // Fall back on the old API.  Note to cancel BEFORE changing the
         // foreground state, since we could be killed at that point.
         mNM.cancel(id);
-        setForeground(false);
+        if  (mSetForeground != null) {
+            try {
+                mSetForegroundArgs[0] = Boolean.FALSE;
+                mSetForeground.invoke(this, mSetForegroundArgs);
+            } catch (IllegalArgumentException e) {
+                e.printStackTrace();
+            } catch (IllegalAccessException e) {
+                e.printStackTrace();
+            } catch (InvocationTargetException e) {
+                e.printStackTrace();
+            }
+        }
     }
 
     public void doToast(String sMsg)
         {
         Toast toast = Toast.makeText(this, sMsg, Toast.LENGTH_LONG);
         toast.setGravity(Gravity.TOP|Gravity.CENTER_HORIZONTAL, 0, 100);
         toast.show();
         }
@@ -835,16 +869,17 @@ public class WatcherService extends Serv
             runner = new Thread(this);
             msPkgName = sPkgName;
             msPkgFileName = sPkgFileName;
             msOutFile = sOutFile;
             mbReboot = bReboot;
             runner.start();
         }
 
+        @Override
         public void run() {
                bInstalling = true;
             UpdtApp(msPkgName, msPkgFileName, msOutFile, mbReboot);
                bInstalling = false;
         }
     }
 
     private class MyTime extends TimerTask
--- a/configure.in
+++ b/configure.in
@@ -298,21 +298,49 @@ case "$target" in
     fi
 
     android_platform_tools="$android_sdk"/../../platform-tools
     if test ! -d "$android_platform_tools" ; then
         android_platform_tools="$android_sdk"/tools # SDK Tools < r8
     fi
 
     if test -z "$android_toolchain" ; then
-        android_toolchain="$android_ndk"/build/prebuilt/`uname -s | tr "[[:upper:]]" "[[:lower:]]"`-x86/arm-eabi-4.4.0
+        AC_MSG_CHECKING([for android toolchain directory])
+
+        kernel_name=`uname -s | tr "[[:upper:]]" "[[:lower:]]"`
+
+        android_toolchain="$android_ndk"/build/prebuilt/$kernel_name-x86/arm-eabi-4.4.0
+
+        # With newer NDK, the toolchain path has changed.
+        if ! test -d "$android_toolchain" ; then
+            android_toolchain="$android_ndk"/toolchains/arm-$kernel_name-androideabi-4.4.3/prebuilt/$kernel_name-x86
+        fi
+
+        if test -d "$android_toolchain" ; then
+            AC_MSG_RESULT([$android_toolchain])
+        else
+            AC_MSG_ERROR([not found. You have to specify --with-android-toolchain=/path/to/ndk/toolchain.])
+        fi
     fi
 
     if test -z "$android_platform" ; then
-       android_platform="$android_ndk"/build/platforms/android-"$android_version"/arch-"$target_cpu"
+        AC_MSG_CHECKING([for android platform directory])
+
+        android_platform="$android_ndk"/build/platforms/android-"$android_version"/arch-"$target_cpu"
+
+        # With newer NDK, the platform path has changed.
+        if ! test -d "$android_platform" ; then
+            android_platform="$android_ndk"/platforms/android-"$android_version"/arch-"$target_cpu"
+        fi
+
+        if test -d "$android_platform" ; then
+            AC_MSG_RESULT([$android_platform])
+        else
+            AC_MSG_ERROR([not found. You have to specify --with-android-platform=/path/to/ndk/platform.])
+        fi
     fi
 
     dnl set up compilers
     AS="$android_toolchain"/bin/"$android_tool_prefix"-as
     CC="$android_toolchain"/bin/"$android_tool_prefix"-gcc
     CXX="$android_toolchain"/bin/"$android_tool_prefix"-g++
     CPP="$android_toolchain"/bin/"$android_tool_prefix"-cpp
     LD="$android_toolchain"/bin/"$android_tool_prefix"-ld
--- a/content/base/src/nsAttrAndChildArray.cpp
+++ b/content/base/src/nsAttrAndChildArray.cpp
@@ -855,13 +855,19 @@ nsAttrAndChildArray::SizeOf() const
 
   if (mImpl) {
     // Don't add the size taken by *mMappedAttrs because it's shared.
 
     // mBuffer cointains InternalAttr and nsIContent* (even if it's void**)
     // so, we just have to compute the size of *mBuffer given that this object
     // doesn't own the children list.
     size += mImpl->mBufferSize * sizeof(*(mImpl->mBuffer)) + NS_IMPL_EXTRA_SIZE;
+
+    PRUint32 slotCount = AttrSlotCount();
+    for (PRUint32 i = 0; i < slotCount && AttrSlotIsTaken(i); ++i) {
+      nsAttrValue* value = &ATTRS(mImpl)[i].mValue;
+      size += value->SizeOf() - sizeof(*value);
+    }
   }
 
   return size;
 }
 
--- a/content/base/src/nsAttrValue.cpp
+++ b/content/base/src/nsAttrValue.cpp
@@ -1443,8 +1443,68 @@ nsAttrValue::StringToInteger(const nsASt
         }
       }
     }
   }
 
   nsAutoString tmp(aValue);
   return tmp.ToInteger(aErrorCode);
 }
+
+PRInt64
+nsAttrValue::SizeOf() const
+{
+  PRInt64 size = sizeof(*this);
+
+  switch (BaseType()) {
+    case eStringBase:
+    {
+      // TODO: we might be counting the string size more than once.
+      // This should be fixed with bug 677487.
+      nsStringBuffer* str = static_cast<nsStringBuffer*>(GetPtr());
+      size += str ? str->StorageSize() : 0;
+      break;
+    }
+    case eOtherBase:
+    {
+      MiscContainer* container = GetMiscContainer();
+
+      if (!container) {
+        break;
+      }
+
+      size += sizeof(*container);
+
+      void* otherPtr = MISC_STR_PTR(container);
+      // We only count the size of the object pointed by otherPtr if it's a
+      // string. When it's an atom, it's counted separatly.
+      if (otherPtr &&
+          static_cast<ValueBaseType>(container->mStringBits & NS_ATTRVALUE_BASETYPE_MASK) == eStringBase) {
+        // TODO: we might be counting the string size more than once.
+        // This should be fixed with bug 677487.
+        nsStringBuffer* str = static_cast<nsStringBuffer*>(otherPtr);
+        size += str ? str->StorageSize() : 0;
+      }
+
+      // TODO: mCSSStyleRule and mSVGValue might be owned by another object
+      // which would make us count them twice, bug 677493.
+      if (Type() == eCSSStyleRule && container->mCSSStyleRule) {
+        // TODO: Add SizeOf() to StyleRule, bug 677503.
+        size += sizeof(*container->mCSSStyleRule);
+      } else if (Type() == eSVGValue && container->mSVGValue) {
+        // TODO: Add SizeOf() to nsSVGValue, bug 677504.
+        size += sizeof(*container->mSVGValue);
+      } else if (Type() == eAtomArray && container->mAtomArray) {
+        size += sizeof(container->mAtomArray) + sizeof(nsTArrayHeader);
+        size += container->mAtomArray->Capacity() * sizeof(nsCOMPtr<nsIAtom>);
+        // Don't count the size of each nsIAtom, they are counted separatly.
+      }
+
+      break;
+    }
+    case eAtomBase:    // Atoms are counted separatly.
+    case eIntegerBase: // The value is in mBits, nothing to do.
+      break;
+  }
+
+  return size;
+}
+
--- a/content/base/src/nsAttrValue.h
+++ b/content/base/src/nsAttrValue.h
@@ -305,16 +305,18 @@ public:
    * Parse a margin string of format 'top, right, bottom, left' into
    * an nsIntMargin.
    *
    * @param aString the string to parse
    * @return whether the value could be parsed
    */
   PRBool ParseIntMarginValue(const nsAString& aString);
 
+  PRInt64 SizeOf() const;
+
 private:
   // These have to be the same as in ValueType
   enum ValueBaseType {
     eStringBase =    eString,    // 00
     eOtherBase =     0x01,       // 01
     eAtomBase =      eAtom,      // 10
     eIntegerBase =   0x03        // 11
   };
--- a/content/base/src/nsMappedAttributes.cpp
+++ b/content/base/src/nsMappedAttributes.cpp
@@ -272,8 +272,24 @@ nsMappedAttributes::IndexOfAttr(nsIAtom*
       if (Attrs()[i].mName.Equals(aLocalName, aNamespaceID)) {
         return i;
       }
     }
   }
 
   return -1;
 }
+
+PRInt64
+nsMappedAttributes::SizeOf() const
+{
+  NS_ASSERTION(mAttrCount == mBufferSize,
+               "mBufferSize and mAttrCount are expected to be the same.");
+
+  PRInt64 size = sizeof(*this) - sizeof(void*) + mAttrCount * sizeof(InternalAttr);
+
+  for (PRUint16 i = 0; i < mAttrCount; ++i) {
+    size += Attrs()[i].mValue.SizeOf() - sizeof(Attrs()[i].mValue);
+  }
+
+  return size;
+}
+
--- a/content/base/src/nsMappedAttributes.h
+++ b/content/base/src/nsMappedAttributes.h
@@ -103,21 +103,17 @@ public:
   
 
   // nsIStyleRule 
   virtual void MapRuleInfoInto(nsRuleData* aRuleData);
 #ifdef DEBUG
   virtual void List(FILE* out = stdout, PRInt32 aIndent = 0) const;
 #endif
 
-  PRInt64 SizeOf() const {
-    NS_ASSERTION(mAttrCount == mBufferSize,
-                 "mBufferSize and mAttrCount are expected to be the same.");
-    return sizeof(*this) - sizeof(void*) + mAttrCount * sizeof(InternalAttr);
-  }
+  PRInt64 SizeOf() const;
 
 private:
   nsMappedAttributes(const nsMappedAttributes& aCopy);
   ~nsMappedAttributes();
 
   struct InternalAttr
   {
     nsAttrName mName;
--- a/content/html/document/src/ImageDocument.cpp
+++ b/content/html/document/src/ImageDocument.cpp
@@ -644,36 +644,42 @@ ImageDocument::CreateSyntheticDocument()
   NS_ENSURE_SUCCESS(rv, rv);
 
   // We must declare the image as a block element. If we stay as
   // an inline element, our parent LineBox will be inline too and
   // ignore the available height during reflow.
   // This is bad during printing, it means tall image frames won't know
   // the size of the paper and cannot break into continuations along
   // multiple pages.
-  Element* body = GetBodyElement();
-  if (!body) {
-    NS_WARNING("no body on image document!");
+  Element* head = GetHeadElement();
+  if (!head) {
+    NS_WARNING("no head on image document!");
     return NS_ERROR_FAILURE;
   }
 
   nsCOMPtr<nsINodeInfo> nodeInfo;
   nodeInfo = mNodeInfoManager->GetNodeInfo(nsGkAtoms::style, nsnull,
                                            kNameSpaceID_XHTML,
                                            nsIDOMNode::ELEMENT_NODE);
   NS_ENSURE_TRUE(nodeInfo, NS_ERROR_OUT_OF_MEMORY);
   nsRefPtr<nsGenericHTMLElement> styleContent = NS_NewHTMLStyleElement(nodeInfo.forget());
   if (!styleContent) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
   styleContent->SetTextContent(NS_LITERAL_STRING("img { display: block; }"));
-  body->AppendChildTo(styleContent, PR_FALSE);
+  head->AppendChildTo(styleContent, PR_FALSE);
 
   // Add the image element
+  Element* body = GetBodyElement();
+  if (!body) {
+    NS_WARNING("no body on image document!");
+    return NS_ERROR_FAILURE;
+  }
+
   nodeInfo = mNodeInfoManager->GetNodeInfo(nsGkAtoms::img, nsnull,
                                            kNameSpaceID_XHTML,
                                            nsIDOMNode::ELEMENT_NODE);
   NS_ENSURE_TRUE(nodeInfo, NS_ERROR_OUT_OF_MEMORY);
 
   mImageContent = NS_NewHTMLImageElement(nodeInfo.forget());
   if (!mImageContent) {
     return NS_ERROR_OUT_OF_MEMORY;
--- a/content/html/document/test/Makefile.in
+++ b/content/html/document/test/Makefile.in
@@ -98,16 +98,18 @@ include $(topsrcdir)/config/rules.mk
 		test_bug486741.html \
 		test_bug489532.html \
 		test_bug497242.xhtml \
 		test_bug499092.html \
 		bug499092.xml \
 		bug499092.html \
 		test_bug512367.html \
 		test_bug571981.html \
+		test_bug677495.html \
+		test_bug677495-1.html \
 		$(NULL)
 
 ifneq (mobile,$(MOZ_BUILD_APP))
 _BROWSER_TEST_FILES = \
 		browser_bug592641.js \
 		bug592641_img.jpg \
 		$(NULL)
 endif
new file mode 100644
--- /dev/null
+++ b/content/html/document/test/test_bug677495-1.html
@@ -0,0 +1,35 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=677495
+
+As mandated by the spec, the body of a media document must only contain one child.
+-->
+<head>
+  <title>Test for Bug 571981</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"/>
+
+<script type="application/javascript">
+  SimpleTest.waitForExplicitFinish();
+
+  function frameLoaded() {
+    var testframe = document.getElementById('testframe');
+    var testframeChildren = testframe.contentDocument.body.childNodes;
+    is(testframeChildren.length, 1, "Body of video document has 1 child");
+    is(testframeChildren[0].nodeName, "VIDEO", "Only child of body must be a <video> element");
+
+    SimpleTest.finish();
+  }
+</script>
+
+</head>
+<body>
+  <p id="display"></p>
+
+  <iframe id="testframe" name="testframe" onload="frameLoaded()"
+   src="data:video/webm,"></iframe>
+
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/html/document/test/test_bug677495.html
@@ -0,0 +1,35 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=677495
+
+As mandated by the spec, the body of a media document must only contain one child.
+-->
+<head>
+  <title>Test for Bug 571981</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"/>
+
+<script type="application/javascript">
+  SimpleTest.waitForExplicitFinish();
+
+  function frameLoaded() {
+    var testframe = document.getElementById('testframe');
+    var testframeChildren = testframe.contentDocument.body.childNodes;
+    is(testframeChildren.length, 1, "Body of image document has 1 child");
+    is(testframeChildren[0].nodeName, "IMG", "Only child of body must be an <img> element");
+
+    SimpleTest.finish();
+  }
+</script>
+
+</head>
+<body>
+  <p id="display"></p>
+
+  <iframe id="testframe" name="testframe" onload="frameLoaded()"
+   src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAAAXNSR0IArs4c6QAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9oMFgQGMyFwHucAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAADElEQVQI12P4//8/AAX+Av7czFnnAAAAAElFTkSuQmCC"></iframe>
+
+</body>
+</html>
--- a/dom/plugins/ipc/PluginInstanceChild.cpp
+++ b/dom/plugins/ipc/PluginInstanceChild.cpp
@@ -3513,17 +3513,23 @@ PluginInstanceChild::SwapSurfaces()
         mDoubleBufferCARenderer.HasBackSurface() &&
         (mDoubleBufferCARenderer.GetFrontSurfaceWidth() != 
             mDoubleBufferCARenderer.GetBackSurfaceWidth() ||
         mDoubleBufferCARenderer.GetFrontSurfaceHeight() != 
             mDoubleBufferCARenderer.GetBackSurfaceHeight())) {
 
         mDoubleBufferCARenderer.ClearFrontSurface();
     }
-#endif //MOZ_WIDGET_COCOA
+#else
+    if (mCurrentSurface && mBackSurface &&
+        (mCurrentSurface->GetSize() != mBackSurface->GetSize() ||
+         mCurrentSurface->GetContentType() != mBackSurface->GetContentType())) {
+        ClearCurrentSurface();
+    }
+#endif
 }
 
 void
 PluginInstanceChild::ClearCurrentSurface()
 {
     mCurrentSurface = nsnull;
 #ifdef MOZ_WIDGET_COCOA
     if (mDoubleBufferCARenderer.HasFrontSurface()) {
--- a/editor/libeditor/html/nsHTMLEditRules.cpp
+++ b/editor/libeditor/html/nsHTMLEditRules.cpp
@@ -4719,16 +4719,20 @@ nsHTMLEditRules::WillAlign(nsISelection 
   nsCOMPtr<nsIDOMNode> curParent;
   nsCOMPtr<nsIDOMNode> curDiv;
   PRBool useCSS;
   mHTMLEditor->GetIsCSSEnabled(&useCSS);
   for (i=0; i<listCount; i++)
   {
     // here's where we actually figure out what to do
     nsCOMPtr<nsIDOMNode> curNode = arrayOfNodes[i];
+
+    // Ignore all non-editable nodes.  Leave them be.
+    if (!mHTMLEditor->IsEditable(curNode)) continue;
+
     PRInt32 offset;
     res = nsEditor::GetNodeLocation(curNode, address_of(curParent), &offset);
     NS_ENSURE_SUCCESS(res, res);
 
     // the node is a table element, an horiz rule, a paragraph, a div
     // or a section header; in HTML 4, it can directly carry the ALIGN
     // attribute and we don't need to nest it, just set the alignment.
     // In CSS, assign the corresponding CSS styles in AlignBlock
@@ -8699,17 +8703,33 @@ nsHTMLEditRules::RemoveAlignment(nsIDOMN
     else
     {
       tmp = nsnull;
     }
     PRBool isBlock;
     res = mHTMLEditor->NodeIsBlockStatic(child, &isBlock);
     NS_ENSURE_SUCCESS(res, res);
 
-    if ((isBlock && !nsHTMLEditUtils::IsDiv(child)) || nsHTMLEditUtils::IsHR(child))
+    if (nsEditor::NodeIsType(child, nsEditProperty::center))
+    {
+      // the current node is a CENTER element
+      // first remove children's alignment
+      res = RemoveAlignment(child, aAlignType, PR_TRUE);
+      NS_ENSURE_SUCCESS(res, res);
+
+      // we may have to insert BRs in first and last position of element's children
+      // if the nodes before/after are not blocks and not BRs
+      res = MakeSureElemStartsOrEndsOnCR(child);
+      NS_ENSURE_SUCCESS(res, res);
+
+      // now remove the CENTER container
+      res = mHTMLEditor->RemoveContainer(child);
+      NS_ENSURE_SUCCESS(res, res);
+    }
+    else if (isBlock || nsHTMLEditUtils::IsHR(child))
     {
       // the current node is a block element
       nsCOMPtr<nsIDOMElement> curElem = do_QueryInterface(child);
       if (nsHTMLEditUtils::SupportsAlignAttr(child))
       {
         // remove the ALIGN attribute if this element can have it
         res = mHTMLEditor->RemoveAttribute(curElem, NS_LITERAL_STRING("align"));
         NS_ENSURE_SUCCESS(res, res);
@@ -8729,57 +8749,16 @@ nsHTMLEditRules::RemoveAlignment(nsIDOMN
       }
       if (!nsHTMLEditUtils::IsTable(child))
       {
         // unless this is a table, look at children
         res = RemoveAlignment(child, aAlignType, PR_TRUE);
         NS_ENSURE_SUCCESS(res, res);
       }
     }
-    else if (nsEditor::NodeIsType(child, nsEditProperty::center)
-             || nsHTMLEditUtils::IsDiv(child))
-    {
-      // this is a CENTER or a DIV element and we have to remove it
-      // first remove children's alignment
-      res = RemoveAlignment(child, aAlignType, PR_TRUE);
-      NS_ENSURE_SUCCESS(res, res);
-
-      if (useCSS && nsHTMLEditUtils::IsDiv(child))
-      {
-        // if we are in CSS mode and if the element is a DIV, let's remove it
-        // if it does not carry any style hint (style attr, class or ID)
-        nsAutoString dummyCssValue;
-        res = mHTMLEditor->mHTMLCSSUtils->RemoveCSSInlineStyle(child, nsEditProperty::cssTextAlign, dummyCssValue);
-        NS_ENSURE_SUCCESS(res, res);
-        nsCOMPtr<nsIDOMElement> childElt = do_QueryInterface(child);
-        PRBool hasStyleOrIdOrClass;
-        res = mHTMLEditor->HasStyleOrIdOrClass(childElt, &hasStyleOrIdOrClass);
-        NS_ENSURE_SUCCESS(res, res);
-        if (!hasStyleOrIdOrClass)
-        {
-          // we may have to insert BRs in first and last position of DIV's children
-          // if the nodes before/after are not blocks and not BRs
-          res = MakeSureElemStartsOrEndsOnCR(child);
-          NS_ENSURE_SUCCESS(res, res);
-          res = mHTMLEditor->RemoveContainer(child);
-          NS_ENSURE_SUCCESS(res, res);
-        }
-      }
-      else
-      {
-        // we may have to insert BRs in first and last position of element's children
-        // if the nodes before/after are not blocks and not BRs
-        res = MakeSureElemStartsOrEndsOnCR(child);
-        NS_ENSURE_SUCCESS(res, res);
-
-        // in HTML mode, let's remove the element
-        res = mHTMLEditor->RemoveContainer(child);
-        NS_ENSURE_SUCCESS(res, res);
-      }
-    }
     child = tmp;
   }
   return NS_OK;
 }
 
 // Let's insert a BR as first (resp. last) child of aNode if its
 // first (resp. last) child is not a block nor a BR, and if the
 // previous (resp. next) sibling is not a block nor a BR
--- a/editor/libeditor/html/tests/Makefile.in
+++ b/editor/libeditor/html/tests/Makefile.in
@@ -52,16 +52,17 @@ include $(topsrcdir)/config/rules.mk
 		test_bug332636.html^headers^ \
 		test_bug366682.html \
 		test_bug372345.html \
 		test_bug410986.html \
 		test_bug414526.html \
 		test_bug417418.html \
 		test_bug432225.html \
 		test_bug439808.html \
+		test_bug442186.html \
 		test_bug449243.html \
 		test_bug455992.html \
 		test_bug456244.html \
 		test_bug460740.html \
 		test_bug478725.html \
 		test_bug480972.html \
 		test_bug484181.html \
 		test_bug487524.html \
new file mode 100644
--- /dev/null
+++ b/editor/libeditor/html/tests/test_bug442186.html
@@ -0,0 +1,102 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=442186
+-->
+<head>
+  <title>Test for Bug 442186</title>
+  <script type="application/javascript" src="/MochiKit/packed.js"></script>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/EventUtils.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=442186">Mozilla Bug 442186</a>
+<p id="display"></p>
+<div id="content">
+  <h2> two &lt;div&gt; containers </h2>
+  <section contenteditable id="test1">
+    <div> First paragraph with some text. </div>
+    <div> Second paragraph with some text. </div>
+  </section>
+
+  <h2> two paragraphs </h2>
+  <section contenteditable id="test2">
+    <p> First paragraph with some text. </p>
+    <p> Second paragraph with some text. </p>
+  </section>
+
+  <h2> one text node, one paragraph </h2>
+  <section contenteditable id="test3">
+    First paragraph with some text.
+    <p> Second paragraph with some text. </p>
+  </section>
+</div>
+
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 442186 **/
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(runTests);
+
+function justify(textNode, pos) {
+  if (!pos) pos = 10;
+
+  // put the caret on the requested character
+  var range = document.createRange();
+  var sel = window.getSelection();
+  range.setStart(textNode, pos);
+  range.setEnd(textNode, pos);
+  sel.addRange(range);
+
+  // align
+  document.execCommand("justifyright", false, null);
+}
+
+function runTests() {
+  const test1 = document.getElementById("test1");
+  const test2 = document.getElementById("test2");
+  const test3 = document.getElementById("test3");
+
+  // #test1: two <div> containers
+  const line1 = test1.querySelector("div").firstChild;
+  test1.focus();
+  justify(line1);
+  is(test1.querySelectorAll("*").length, 2,
+    "Aligning the first child should not create nor remove any element.");
+  is(line1.parentNode.nodeName.toLowerCase(), "div",
+    "Aligning the first <div> should not modify its node type.");
+  is(line1.parentNode.style.textAlign, "right",
+    "Aligning the first <div> should set a 'text-align: right' style rule.");
+
+  // #test2: two paragraphs
+  const line2 = test2.querySelector("p").firstChild;
+  test2.focus();
+  justify(line2);
+  is(test2.querySelectorAll("*").length, 2,
+    "Aligning the first child should not create nor remove any element.");
+  is(line2.parentNode.nodeName.toLowerCase(), "p",
+    "Aligning the first paragraph should not modify its node type.");
+  is(line2.parentNode.style.textAlign, "right",
+    "Aligning the first paragraph should set a 'text-align: right' style rule.");
+
+  // #test3: one text node, two paragraphs
+  const line3 = test3.firstChild;
+  test3.focus();
+  justify(line3);
+  is(test3.querySelectorAll("*").length, 2,
+    "Aligning the first child should create a block element.");
+  is(line3.parentNode.nodeName.toLowerCase(), "div",
+    "Aligning the first child should create a block element.");
+  is(line3.parentNode.style.textAlign, "right",
+    "Aligning the first line should set a 'text-align: right' style rule.");
+
+  // done
+  SimpleTest.finish();
+}
+
+</script>
+</pre>
+</body>
+</html>
--- a/gfx/cairo/cairo/src/cairo-d2d-surface.cpp
+++ b/gfx/cairo/cairo/src/cairo-d2d-surface.cpp
@@ -2553,16 +2553,25 @@ static cairo_status_t
 	return _cairo_error(CAIRO_STATUS_NO_DEVICE);
     }
     *image_out = 
 	(cairo_image_surface_t*)cairo_image_surface_create_for_data((unsigned char*)data.pData,
 										  d2dsurf->format,
 										  size.width,
 										  size.height,
 										  data.RowPitch);
+
+    if (cairo_surface_status(&((*image_out)->base))) {
+	volatile cairo_status_t flambo[10];
+	for (int i=0; i<10; i++) {
+	    flambo[i] = cairo_surface_status(&((*image_out)->base));
+	}
+	volatile int p = 0;
+	p = 5/p;
+    }
     *image_extra = softTexture.forget();
 
     return CAIRO_STATUS_SUCCESS;
 }
 
 static void
 _cairo_d2d_release_source_image(void                   *abstract_surface,
 				cairo_image_surface_t  *image,
--- a/js/src/assembler/assembler/ARMAssembler.h
+++ b/js/src/assembler/assembler/ARMAssembler.h
@@ -1600,17 +1600,17 @@ namespace JSC {
             // TODO: emitInst doesn't work for VFP instructions, though it
             // seems to work for current usage.
             emitInst(static_cast<ARMWord>(cc) | FADDD, dd, dn, dm);
         }
 
         void fnegd_r(int dd, int dm, Condition cc = AL)
         {
             js::JaegerSpew(js::JSpew_Insns,
-                    IPFX   "%-15s %s, %s, %s, %s\n", MAYBE_PAD, "fnegd", nameFpRegD(dd), nameFpRegD(dm));
+                    IPFX   "%-15s %s, %s\n", MAYBE_PAD, "fnegd", nameFpRegD(dd), nameFpRegD(dm));
             m_buffer.putInt(static_cast<ARMWord>(cc) | FNEGD | DD(dd) | DM(dm));
         }
 
         void fdivd_r(int dd, int dn, int dm, Condition cc = AL)
         {
             js::JaegerSpew(js::JSpew_Insns,
                     IPFX   "%-15s %s, %s, %s\n", MAYBE_PAD, "vdiv.f64", nameFpRegD(dd), nameFpRegD(dn), nameFpRegD(dm));
             // TODO: emitInst doesn't work for VFP instructions, though it
--- a/js/src/configure.in
+++ b/js/src/configure.in
@@ -289,21 +289,49 @@ case "$target" in
     fi
 
     android_platform_tools="$android_sdk"/../../platform-tools
     if test ! -d "$android_platform_tools" ; then
         android_platform_tools="$android_sdk"/tools # SDK Tools < r8
     fi
 
     if test -z "$android_toolchain" ; then
-        android_toolchain="$android_ndk"/build/prebuilt/`uname -s | tr "[[:upper:]]" "[[:lower:]]"`-x86/arm-eabi-4.4.0
+        AC_MSG_CHECKING([for android toolchain directory])
+
+        kernel_name=`uname -s | tr "[[:upper:]]" "[[:lower:]]"`
+
+        android_toolchain="$android_ndk"/build/prebuilt/$kernel_name-x86/arm-eabi-4.4.0
+
+        # With newer NDK, the toolchain path has changed.
+        if ! test -d "$android_toolchain" ; then
+            android_toolchain="$android_ndk"/toolchains/arm-$kernel_name-androideabi-4.4.3/prebuilt/$kernel_name-x86
+        fi
+
+        if test -d "$android_toolchain" ; then
+            AC_MSG_RESULT([$android_toolchain])
+        else
+            AC_MSG_ERROR([not found. You have to specify --with-android-toolchain=/path/to/ndk/toolchain.])
+        fi
     fi
 
     if test -z "$android_platform" ; then
-       android_platform="$android_ndk"/build/platforms/android-"$android_version"/arch-"$target_cpu"
+        AC_MSG_CHECKING([for android platform directory])
+
+        android_platform="$android_ndk"/build/platforms/android-"$android_version"/arch-"$target_cpu"
+
+        # With newer NDK, the platform path has changed.
+        if ! test -d "$android_platform" ; then
+            android_platform="$android_ndk"/platforms/android-"$android_version"/arch-"$target_cpu"
+        fi
+
+        if test -d "$android_platform" ; then
+            AC_MSG_RESULT([$android_platform])
+        else
+            AC_MSG_ERROR([not found. You have to specify --with-android-platform=/path/to/ndk/platform.])
+        fi
     fi
 
     dnl set up compilers
     AS="$android_toolchain"/bin/"$android_tool_prefix"-as
     CC="$android_toolchain"/bin/"$android_tool_prefix"-gcc
     CXX="$android_toolchain"/bin/"$android_tool_prefix"-g++
     CPP="$android_toolchain"/bin/"$android_tool_prefix"-cpp
     LD="$android_toolchain"/bin/"$android_tool_prefix"-ld
--- a/js/src/ctypes/CTypes.cpp
+++ b/js/src/ctypes/CTypes.cpp
@@ -4543,16 +4543,21 @@ GetABI(JSContext* cx, jsval abiType, ffi
   case ABI_DEFAULT:
     *result = FFI_DEFAULT_ABI;
     return true;
   case ABI_STDCALL:
   case ABI_WINAPI:
 #if (defined(_WIN32) && !defined(_WIN64)) || defined(_OS2)
     *result = FFI_STDCALL;
     return true;
+#elif (defined(_WIN64))
+    // We'd like the same code to work across Win32 and Win64, so stdcall_api
+    // and winapi_abi become aliases to the lone Win64 ABI.
+    *result = FFI_WIN64;
+    return true;
 #endif
   case INVALID_ABI:
     break;
   }
   return false;
 }
 
 static JSObject*
@@ -4687,32 +4692,38 @@ FunctionType::BuildSymbolName(JSContext*
   switch (GetABICode(cx, fninfo->mABI)) {
   case ABI_DEFAULT:
   case ABI_WINAPI:
     // For cdecl or WINAPI functions, no mangling is necessary.
     AppendString(result, name);
     break;
 
   case ABI_STDCALL: {
+#if (defined(_WIN32) && !defined(_WIN64)) || defined(_OS2)
     // On WIN32, stdcall functions look like:
     //   _foo@40
     // where 'foo' is the function name, and '40' is the aligned size of the
     // arguments.
     AppendString(result, "_");
     AppendString(result, name);
     AppendString(result, "@");
 
     // Compute the suffix by aligning each argument to sizeof(ffi_arg).
     size_t size = 0;
     for (size_t i = 0; i < fninfo->mArgTypes.length(); ++i) {
       JSObject* argType = fninfo->mArgTypes[i];
       size += Align(CType::GetSize(cx, argType), sizeof(ffi_arg));
     }
 
     IntegerToString(size, 10, result);
+#elif defined(_WIN64)
+    // On Win64, stdcall is an alias to the default ABI for compatibility, so no
+    // mangling is done.
+    AppendString(result, name);
+#endif
     break;
   }
 
   case INVALID_ABI:
     JS_NOT_REACHED("invalid abi");
     break;
   }
 }
--- a/js/src/jit-test/tests/basic/bug596502-version.js
+++ b/js/src/jit-test/tests/basic/bug596502-version.js
@@ -10,14 +10,14 @@ function syntaxErrorFromXML() {
 }
 
 version(150);
 assertEq(syntaxErrorFromXML(), true);
 revertVersion();
 
 for (vno in {160: null, 170: null, 180: null}) {
     print('Setting version to: ' + vno);
-    version(vno);
+    version(Number(vno));
     assertEq(syntaxErrorFromXML(), false);
     revertVersion();
 }
 
 print('PASS!')
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/typed-array-offsets.js
@@ -0,0 +1,11 @@
+function f(x, y) {
+  for (var i = 0; i < 100; i++)
+    assertEq(x[0], y);
+}
+var a = ArrayBuffer(20);
+var b = Int32Array(a, 12, 2);
+var c = Int32Array(a, 0, 2);
+b[0] = 10;
+f(b, 10);
+c[0] = 20;
+f(c, 20);
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -171,107 +171,106 @@ ArenaHeader::checkSynchronizedWithFreeLi
     /*
      * We can be called from the background finalization thread when the free
      * list in the compartment can mutate at any moment. We cannot do any
      * checks in this case.
      */
     if (!compartment->rt->gcRunning)
         return;
 
-    FreeSpan firstSpan(address() + firstFreeSpanStart, address() + firstFreeSpanEnd);
+    FreeSpan firstSpan = FreeSpan::decodeOffsets(arenaAddress(), firstFreeSpanOffsets);
     if (firstSpan.isEmpty())
         return;
     FreeSpan *list = &compartment->freeLists.lists[getThingKind()];
     if (list->isEmpty() || firstSpan.arenaAddress() != list->arenaAddress())
         return;
 
     /*
      * Here this arena has free things, FreeList::lists[thingKind] is not
      * empty and also points to this arena. Thus they must the same.
      */
-    JS_ASSERT(firstSpan.start == list->start);
-    JS_ASSERT(firstSpan.end == list->end);
+    JS_ASSERT(firstSpan.isSameNonEmptySpan(list));
 }
 #endif
 
 template<typename T>
 inline bool
 Arena::finalize(JSContext *cx)
 {
     JS_ASSERT(aheader.allocated());
     JS_ASSERT(!aheader.getMarkingDelay()->link);
 
     uintptr_t thing = thingsStart(sizeof(T));
-    uintptr_t end = thingsEnd();
+    uintptr_t lastByte = thingsEnd() - 1;
 
     FreeSpan nextFree(aheader.getFirstFreeSpan());
     nextFree.checkSpan();
 
     FreeSpan newListHead;
     FreeSpan *newListTail = &newListHead;
     uintptr_t newFreeSpanStart = 0;
     bool allClear = true;
 #ifdef DEBUG
     size_t nmarked = 0;
 #endif
     for (;; thing += sizeof(T)) {
-        JS_ASSERT(thing <= end);
-        if (thing == nextFree.start) {
-            JS_ASSERT(nextFree.end <= end);
-            if (nextFree.end == end)
+        JS_ASSERT(thing <= lastByte + 1);
+        if (thing == nextFree.first) {
+            JS_ASSERT(nextFree.last <= lastByte);
+            if (nextFree.last == lastByte)
                 break;
-            JS_ASSERT(Arena::isAligned(nextFree.end, sizeof(T)));
+            JS_ASSERT(Arena::isAligned(nextFree.last, sizeof(T)));
             if (!newFreeSpanStart)
                 newFreeSpanStart = thing;
-            thing = nextFree.end;
+            thing = nextFree.last;
             nextFree = *nextFree.nextSpan();
             nextFree.checkSpan();
         } else {
             T *t = reinterpret_cast<T *>(thing);
             if (t->isMarked()) {
                 allClear = false;
 #ifdef DEBUG
                 nmarked++;
 #endif
                 if (newFreeSpanStart) {
                     JS_ASSERT(thing >= thingsStart(sizeof(T)) + sizeof(T));
-                    newListTail->start = newFreeSpanStart;
-                    newListTail->end = thing - sizeof(T);
-                    newListTail = newListTail->nextSpanUnchecked();
-                    newFreeSpanStart = 0;
+                    newListTail->first = newFreeSpanStart;
+                    newListTail->last = thing - sizeof(T);
+                    newListTail = newListTail->nextSpanUnchecked(sizeof(T));
+                    newFreeSpanStart = NULL;
                 }
             } else {
                 if (!newFreeSpanStart)
                     newFreeSpanStart = thing;
                 t->finalize(cx);
                 memset(t, JS_FREE_PATTERN, sizeof(T));
             }
         }
     }
 
     if (allClear) {
         JS_ASSERT(newListTail == &newListHead);
         JS_ASSERT(newFreeSpanStart == thingsStart(sizeof(T)));
         return true;
     }
 
-    newListTail->start = newFreeSpanStart ? newFreeSpanStart : nextFree.start;
-    JS_ASSERT(Arena::isAligned(newListTail->start, sizeof(T)));
-    newListTail->end = end;
+    newListTail->first = newFreeSpanStart ? newFreeSpanStart : nextFree.first;
+    JS_ASSERT(Arena::isAligned(newListTail->first, sizeof(T)));
+    newListTail->last = lastByte;
 
 #ifdef DEBUG
     size_t nfree = 0;
-    for (FreeSpan *span = &newListHead; span != newListTail; span = span->nextSpan()) {
+    for (const FreeSpan *span = &newListHead; span != newListTail; span = span->nextSpan()) {
         span->checkSpan();
-        JS_ASSERT(Arena::isAligned(span->start, sizeof(T)));
-        JS_ASSERT(Arena::isAligned(span->end, sizeof(T)));
-        nfree += (span->end - span->start) / sizeof(T) + 1;
+        JS_ASSERT(Arena::isAligned(span->first, sizeof(T)));
+        JS_ASSERT(Arena::isAligned(span->last, sizeof(T)));
+        nfree += (span->last - span->first) / sizeof(T) + 1;
         JS_ASSERT(nfree + nmarked <= thingsPerArena(sizeof(T)));
     }
-    nfree += (newListTail->end - newListTail->start) / sizeof(T);
+    nfree += (newListTail->last + 1 - newListTail->first) / sizeof(T);
     JS_ASSERT(nfree + nmarked == thingsPerArena(sizeof(T)));
 #endif
     aheader.setFirstFreeSpan(&newListHead);
 
     return false;
 }
 
 /*
@@ -624,27 +623,27 @@ namespace js {
 inline bool
 InFreeList(ArenaHeader *aheader, uintptr_t addr)
 {
     if (!aheader->hasFreeThings())
         return false;
 
     FreeSpan firstSpan(aheader->getFirstFreeSpan());
 
-    for (FreeSpan *span = &firstSpan;;) {
+    for (const FreeSpan *span = &firstSpan;;) {
         /* If the thing comes fore the current span, it's not free. */
-        if (addr < span->start)
+        if (addr < span->first)
             return false;
 
         /*
          * If we find it inside the span, it's dead. We use here "<=" and not
          * "<" even for the last span as we know that thing is inside the
          * arena. Thus for the last span thing < span->end.
          */
-        if (addr <= span->end)
+        if (addr <= span->last)
             return true;
 
         /*
          * The last possible empty span is an the end of the arena. Here
          * span->end < thing < thingsEnd and so we must have more spans.
          */
         span = span->nextSpan();
     }
@@ -1359,17 +1358,17 @@ RunLastDitchGC(JSContext *cx)
 
 static inline bool
 IsGCAllowed(JSContext *cx)
 {
     return !JS_ON_TRACE(cx) && !JS_THREAD_DATA(cx)->waiveGCQuota;
 }
 
 template <typename T>
-inline Cell *
+inline void *
 RefillTypedFreeList(JSContext *cx, unsigned thingKind)
 {
     JS_ASSERT(!cx->runtime->gcRunning);
 
     /*
      * For compatibility with older code we tolerate calling the allocator
      * during the GC in optimized builds.
      */
@@ -1386,17 +1385,17 @@ RefillTypedFreeList(JSContext *cx, unsig
             if (!RunLastDitchGC(cx))
                 break;
 
             /*
              * The JSGC_END callback can legitimately allocate new GC
              * things and populate the free list. If that happens, just
              * return that list head.
              */
-            if (Cell *thing = compartment->freeLists.getNext(thingKind, sizeof(T)))
+            if (void *thing = compartment->freeLists.getNext(thingKind, sizeof(T)))
                 return thing;
         }
         ArenaHeader *aheader =
             compartment->arenas[thingKind].getArenaWithFreeList<sizeof(T)>(cx, thingKind);
         if (aheader) {
             JS_ASSERT(sizeof(T) == aheader->getThingSize());
             return compartment->freeLists.populate(aheader, thingKind, sizeof(T));
         }
@@ -1409,17 +1408,17 @@ RefillTypedFreeList(JSContext *cx, unsig
             break;
         runGC = true;
     }
 
     js_ReportOutOfMemory(cx);
     return NULL;
 }
 
-Cell *
+void *
 RefillFinalizableFreeList(JSContext *cx, unsigned thingKind)
 {
     switch (thingKind) {
       case FINALIZE_OBJECT0:
       case FINALIZE_OBJECT0_BACKGROUND:
         return RefillTypedFreeList<JSObject>(cx, thingKind);
       case FINALIZE_OBJECT2:
       case FINALIZE_OBJECT2_BACKGROUND:
@@ -1447,17 +1446,17 @@ RefillFinalizableFreeList(JSContext *cx,
       case FINALIZE_SHAPE:
         return RefillTypedFreeList<Shape>(cx, thingKind);
 #if JS_HAS_XML_SUPPORT
       case FINALIZE_XML:
         return RefillTypedFreeList<JSXML>(cx, thingKind);
 #endif
       default:
         JS_NOT_REACHED("bad finalize kind");
-        return NULL;
+        return 0;
     }
 }
 
 } /* namespace gc */
 } /* namespace js */
 
 uint32
 js_GetGCThingTraceKind(void *thing)
@@ -2315,17 +2314,17 @@ MarkAndSweep(JSContext *cx, JSCompartmen
     WeakMapBase::sweepAll(&gcmarker);
 
     js_SweepAtomState(cx);
 
     /* Collect watch points associated with unreachable objects. */
     WatchpointMap::sweepAll(rt);
 
     /*
-     * We finalize objects before other GC things to ensure that object's finalizer 
+     * We finalize objects before other GC things to ensure that object's finalizer
      * can access them even if they will be freed. Sweep the runtime's property trees
      * after finalizing objects, in case any had watchpoints referencing tree nodes.
      * Do this before sweeping compartments, so that we sweep all shapes in
      * unreachable compartments.
      */
     if (comp) {
         comp->sweep(cx, 0);
         comp->finalizeObjectArenaLists(cx);
@@ -2827,28 +2826,28 @@ IterateCompartmentsArenasCells(JSContext
             size_t traceKind = GetFinalizableTraceKind(thingKind);
             size_t thingSize = GCThingSizeMap[thingKind];
             ArenaHeader *aheader = compartment->arenas[thingKind].getHead();
 
             for (; aheader; aheader = aheader->next) {
                 Arena *arena = aheader->getArena();
                 (*arenaCallback)(cx, data, arena, traceKind, thingSize);
                 FreeSpan firstSpan(aheader->getFirstFreeSpan());
-                FreeSpan *span = &firstSpan;
+                const FreeSpan *span = &firstSpan;
 
                 for (uintptr_t thing = arena->thingsStart(thingSize); ; thing += thingSize) {
                     JS_ASSERT(thing <= arena->thingsEnd());
-                    if (thing == span->start) {
+                    if (thing == span->first) {
                         if (!span->hasNext())
                             break;
-                        thing = span->end;
+                        thing = span->last;
                         span = span->nextSpan();
                     } else {
-                        (*cellCallback)(cx, data, reinterpret_cast<void *>(thing), traceKind,
-                                        thingSize);
+                        void *t = reinterpret_cast<void *>(thing);
+                        (*cellCallback)(cx, data, t, traceKind, thingSize);
                     }
                 }
             }
         }
     }
 }
 
 namespace gc {
--- a/js/src/jsgc.h
+++ b/js/src/jsgc.h
@@ -121,127 +121,245 @@ const size_t ArenaMask = ArenaSize - 1;
  */
 const size_t ArenaCellCount = size_t(1) << (ArenaShift - Cell::CellShift);
 const size_t ArenaBitmapBits = ArenaCellCount;
 const size_t ArenaBitmapBytes = ArenaBitmapBits / 8;
 const size_t ArenaBitmapWords = ArenaBitmapBits / JS_BITS_PER_WORD;
 
 /*
  * A FreeSpan represents a contiguous sequence of free cells in an Arena.
- * |start| is the address of the first free cell in the span. |end| is the
- * address of the last free cell in the span. The last cell (starting at
- * |end|) holds a FreeSpan data structure for the next span. However, the last
- * FreeSpan in an Arena is special: |end| points to the end of the Arena (an
- * unusable address), and no next FreeSpan is stored there.
+ * |first| is the address of the first free cell in the span. |last| is the
+ * address of the last free cell in the span. This last cell holds a FreeSpan
+ * data structure for the next span unless this is the last span on the list
+ * of spans in the arena. For this last span |last| points to the last byte of
+ * the last thing in the arena and no linkage is stored there, so
+ * |last| == arenaStart + ArenaSize - 1. If the space at the arena end is
+ * fully used this last span is empty and |first| == |last + 1|.
  *
- * As things in the arena ends on its boundary that is aligned on ArenaSize,
- * end & ArenaMask is zero if and only if the span is last. Also, since the
- * first thing in the arena comes after the header, start & ArenaSize is zero
- * if and only if the span is the empty span at the end of the arena.
+ * Thus |first| < |last| implies that we have either the last span with at least
+ * one element or that the span is not the last and contains at least 2
+ * elements. In both cases to allocate a thing from this span we need simply
+ * to increment |first| by the allocation size.
  *
- * The type of the start and end fields is uintptr_t, not a pointer type, to
- * minimize the amount of casting when doing mask operations.
+ * |first| == |last| implies that we have a one element span that records the
+ * next span. So to allocate from it we need to update the span list head
+ * with a copy of the span stored at |last| address so the following
+ * allocations will use that span.
+ *
+ * |first| > |last| implies that we have an empty last span and the arena is
+ * fully used.
+ *
+ * Also only for the last span (|last| & 1)! = 0 as all allocation sizes are
+ * multiples of Cell::CellSize.
  */
 struct FreeSpan {
-    uintptr_t   start;
-    uintptr_t   end;
+    uintptr_t   first;
+    uintptr_t   last;
 
   public:
-    FreeSpan() { }
+    FreeSpan() {}
+
+    FreeSpan(uintptr_t first, uintptr_t last)
+      : first(first), last(last) {
+        checkSpan();
+    }
+
+    /*
+     * To minimize the size of the arena header the first span is encoded
+     * there as offsets from the arena start.
+     */
+    static size_t encodeOffsets(size_t firstOffset, size_t lastOffset = ArenaSize - 1) {
+        /* Check that we can pack the offsets into uint16. */
+        JS_STATIC_ASSERT(ArenaShift < 16);
+        JS_ASSERT(firstOffset <= ArenaSize);
+        JS_ASSERT(lastOffset < ArenaSize);
+        JS_ASSERT(firstOffset <= ((lastOffset + 1) & ~size_t(1)));
+        return firstOffset | (lastOffset << 16);
+    }
 
-    FreeSpan(uintptr_t start, uintptr_t end)
-      : start(start), end(end) {
-        checkSpan();
+    static const size_t EmptyOffsets = ArenaSize | ((ArenaSize - 1) << 16);
+
+    static FreeSpan decodeOffsets(uintptr_t arenaAddr, size_t offsets) {
+        JS_ASSERT(!(arenaAddr & ArenaMask));
+
+        size_t firstOffset = offsets & 0xFFFF;
+        size_t lastOffset = offsets >> 16;
+        JS_ASSERT(firstOffset <= ArenaSize);
+        JS_ASSERT(lastOffset < ArenaSize);
+
+        /*
+         * We must not use | when calculating first as firstOffset is
+         * ArenaMask + 1 for the empty span.
+         */
+        return FreeSpan(arenaAddr + firstOffset, arenaAddr | lastOffset);
+    }
+
+    void initAsEmpty(uintptr_t arenaAddr = 0) {
+        JS_ASSERT(!(arenaAddr & ArenaMask));
+        first = arenaAddr + ArenaSize;
+        last = arenaAddr | (ArenaSize  - 1);
+        JS_ASSERT(isEmpty());
     }
 
     bool isEmpty() const {
         checkSpan();
-        return !(start & ArenaMask);
+        return first > last;
     }
 
     bool hasNext() const {
         checkSpan();
-        return !!(end & ArenaMask);
+        return !(last & uintptr_t(1));
+    }
+
+    const FreeSpan *nextSpan() const {
+        JS_ASSERT(hasNext());
+        return reinterpret_cast<FreeSpan *>(last);
     }
 
-    FreeSpan *nextSpan() const {
-        JS_ASSERT(hasNext());
-        return reinterpret_cast<FreeSpan *>(end);
+    FreeSpan *nextSpanUnchecked(size_t thingSize) const {
+#ifdef DEBUG
+        uintptr_t lastOffset = last & ArenaMask;
+        JS_ASSERT(!(lastOffset & 1));
+        JS_ASSERT((ArenaSize - lastOffset) % thingSize == 0);
+#endif
+        return reinterpret_cast<FreeSpan *>(last);
     }
 
-    FreeSpan *nextSpanUnchecked() const {
-        JS_ASSERT(end & ArenaMask);
-        return reinterpret_cast<FreeSpan *>(end);
+    uintptr_t arenaAddressUnchecked() const {
+        return last & ~ArenaMask;
     }
 
     uintptr_t arenaAddress() const {
+        checkSpan();
+        return arenaAddressUnchecked();
+    }
+
+    ArenaHeader *arenaHeader() const {
+        return reinterpret_cast<ArenaHeader *>(arenaAddress());
+    }
+
+    bool isSameNonEmptySpan(const FreeSpan *another) const {
         JS_ASSERT(!isEmpty());
-        return start & ~ArenaMask;
+        JS_ASSERT(!another->isEmpty());
+        return first == another->first && last == another->last;
+    }
+
+    bool isWithinArena(uintptr_t arenaAddr) const {
+        JS_ASSERT(!(arenaAddr & ArenaMask));
+
+        /* Return true for the last empty span as well. */
+        return arenaAddress() == arenaAddr;
+    }
+
+    size_t encodeAsOffsets() const {
+        /*
+         * We must use first - arenaAddress(), not first & ArenaMask as
+         * first == ArenaMask + 1 for an empty span.
+         */
+        uintptr_t arenaAddr = arenaAddress();
+        return encodeOffsets(first - arenaAddr, last & ArenaMask);
+    }
+
+    /* See comments before FreeSpan for details. */
+    JS_ALWAYS_INLINE void *allocate(size_t thingSize) {
+        JS_ASSERT(thingSize % Cell::CellSize == 0);
+        checkSpan();
+        uintptr_t thing = first;
+        if (thing < last) {
+            /* Bump-allocate from the current span. */
+            first = thing + thingSize;
+        } else if (JS_LIKELY(thing == last)) {
+            /*
+             * Move to the next span. We use JS_LIKELY as without PGO
+             * compilers mis-predict == here as unlikely to succeed.
+             */
+            *this = *reinterpret_cast<FreeSpan *>(thing);
+        } else {
+            return NULL;
+        }
+        checkSpan();
+        return reinterpret_cast<void *>(thing);
     }
 
     void checkSpan() const {
 #ifdef DEBUG
-        JS_ASSERT(start <= end);
-        JS_ASSERT(end - start <= ArenaSize);
-        if (!(start & ArenaMask)) {
-            /* The span is last and empty. */
-            JS_ASSERT(start == end);
+        /* We do not allow spans at the end of the address space. */
+        JS_ASSERT(last != uintptr_t(-1));
+        JS_ASSERT(first);
+        JS_ASSERT(last);
+        JS_ASSERT(first - 1 <= last);
+        uintptr_t arenaAddr = arenaAddressUnchecked();
+        if (last & 1) {
+            /* The span is the last. */
+            JS_ASSERT((last & ArenaMask) == ArenaMask);
+
+            if (first - 1 == last) {
+                /* The span is last and empty. The above start != 0 check
+                 * implies that we are not at the end of the address space.
+                 */
+                return;
+            }
+            size_t spanLength = last - first + 1;
+            JS_ASSERT(spanLength % Cell::CellSize == 0);
+
+            /* Start and end must belong to the same arena. */
+            JS_ASSERT((first & ~ArenaMask) == arenaAddr);
             return;
         }
 
-        JS_ASSERT(start);
-        JS_ASSERT(end);
-        uintptr_t arena = start & ~ArenaMask;
-        if (!(end & ArenaMask)) {
-            /* The last span with few free things at the end of the arena. */
-            JS_ASSERT(arena + ArenaSize == end);
-            return;
-        }
+        /* The span is not the last and we have more spans to follow. */
+        JS_ASSERT(first <= last);
+        size_t spanLengthWithoutOneThing = last - first;
+        JS_ASSERT(spanLengthWithoutOneThing % Cell::CellSize == 0);
+
+        JS_ASSERT((first & ~ArenaMask) == arenaAddr);
 
-        /* The span is not last and we have at least one span that follows it.*/
-        JS_ASSERT(arena == (end & ~ArenaMask));
-        FreeSpan *next = reinterpret_cast<FreeSpan *>(end);
+        /*
+         * If there is not enough space before the arena end to allocate one
+         * more thing, then the span must be marked as the last one to avoid
+         * storing useless empty span reference.
+         */
+        size_t beforeTail = ArenaSize - (last & ArenaMask);
+        JS_ASSERT(beforeTail >= sizeof(FreeSpan) + Cell::CellSize);
+
+        FreeSpan *next = reinterpret_cast<FreeSpan *>(last);
 
         /*
          * The GC things on the list of free spans come from one arena
          * and the spans are linked in ascending address order with
          * at least one non-free thing between spans.
          */
-        JS_ASSERT(end < next->start);
+        JS_ASSERT(last < next->first);
+        JS_ASSERT(arenaAddr == next->arenaAddressUnchecked());
 
-        if (!(next->start & ArenaMask)) {
+        if (next->first > next->last) {
             /*
              * The next span is the empty span that terminates the list for
              * arenas that do not have any free things at the end.
              */
-            JS_ASSERT(next->start == next->end);
-            JS_ASSERT(arena + ArenaSize == next->start);
-        } else {
-            /* The next spans is not empty and must starts inside the arena. */
-            JS_ASSERT(arena == (next->start & ~ArenaMask));
+            JS_ASSERT(next->first - 1 == next->last);
+            JS_ASSERT(arenaAddr + ArenaSize == next->first);
         }
 #endif
     }
+
 };
 
 /* Every arena has a header. */
 struct ArenaHeader {
     JSCompartment   *compartment;
     ArenaHeader     *next;
 
   private:
     /*
      * The first span of free things in the arena. We encode it as the start
      * and end offsets within the arena, not as FreeSpan structure, to
-     * minimize the header size. When the arena has no free things, the span
-     * must be the empty one pointing to the arena's end. For such a span the
-     * start and end offsets must be ArenaSize.
+     * minimize the header size.
      */
-    uint16_t        firstFreeSpanStart;
-    uint16_t        firstFreeSpanEnd;
+    size_t          firstFreeSpanOffsets;
 
     /*
      * One of FinalizeKind constants or FINALIZE_LIMIT when the arena does not
      * contain any GC things and is on the list of empty arenas in the GC
      * chunk. The later allows to quickly check if the arena is allocated
      * during the conservative GC scanning without searching the arena in the
      * list.
      */
@@ -258,46 +376,47 @@ struct ArenaHeader {
     }
 
     bool allocated() const {
         return thingKind < FINALIZE_LIMIT;
     }
 
     inline void init(JSCompartment *comp, unsigned thingKind, size_t thingSize);
 
+    uintptr_t arenaAddress() const {
+        return address();
+    }
+
     Arena *getArena() {
-        return reinterpret_cast<Arena *>(address());
+        return reinterpret_cast<Arena *>(arenaAddress());
     }
 
     unsigned getThingKind() const {
         JS_ASSERT(allocated());
         return thingKind;
     }
 
     bool hasFreeThings() const {
-        return firstFreeSpanStart != ArenaSize;
+        return firstFreeSpanOffsets != FreeSpan::EmptyOffsets;
     }
 
     void setAsFullyUsed() {
-        firstFreeSpanStart = firstFreeSpanEnd = uint16_t(ArenaSize);
+        firstFreeSpanOffsets = FreeSpan::EmptyOffsets;
     }
 
     FreeSpan getFirstFreeSpan() const {
 #ifdef DEBUG
         checkSynchronizedWithFreeList();
 #endif
-        return FreeSpan(address() + firstFreeSpanStart, address() + firstFreeSpanEnd);
+        return FreeSpan::decodeOffsets(arenaAddress(), firstFreeSpanOffsets);
     }
 
     void setFirstFreeSpan(const FreeSpan *span) {
-        span->checkSpan();
-        JS_ASSERT(span->start - address() <= ArenaSize);
-        JS_ASSERT(span->end - address() <= ArenaSize);
-        firstFreeSpanStart = uint16_t(span->start - address());
-        firstFreeSpanEnd = uint16_t(span->end - address());
+        JS_ASSERT(span->isWithinArena(arenaAddress()));
+        firstFreeSpanOffsets = span->encodeAsOffsets();
     }
 
     inline MarkingDelay *getMarkingDelay() const;
 
     size_t getThingSize() const {
         return GCThingSizeMap[getThingKind()];
     }
 
@@ -544,18 +663,17 @@ Cell::isAligned() const
 
 inline void
 ArenaHeader::init(JSCompartment *comp, unsigned kind, size_t thingSize)
 {
     JS_ASSERT(!allocated());
     JS_ASSERT(!getMarkingDelay()->link);
     compartment = comp;
     thingKind = kind;
-    firstFreeSpanStart = uint16_t(Arena::thingsStartOffset(thingSize));
-    firstFreeSpanEnd = uint16_t(ArenaSize);
+    firstFreeSpanOffsets = FreeSpan::encodeOffsets(Arena::thingsStartOffset(thingSize));
 }
 
 inline uintptr_t
 ArenaHeader::address() const
 {
     uintptr_t addr = reinterpret_cast<uintptr_t>(this);
     JS_ASSERT(!(addr & ArenaMask));
     JS_ASSERT(Chunk::withinArenasRange(addr));
@@ -801,113 +919,88 @@ struct FreeLists {
      * header after the initial allocation. When starting the GC We only move
      * the head of the of the list of spans back to the arena only for the
      * arena that was not fully allocated.
      */
     FreeSpan       lists[FINALIZE_LIMIT];
 
     void init() {
         for (size_t i = 0; i != JS_ARRAY_LENGTH(lists); ++i)
-            lists[i].start = lists[i].end = 0;
+            lists[i].initAsEmpty();
     }
 
     /*
      * Return the free list back to the arena so the GC finalization will not
      * run the finalizers over unitialized bytes from free things.
      */
     void purge() {
         for (size_t i = 0; i != size_t(FINALIZE_LIMIT); ++i) {
             FreeSpan *list = &lists[i];
             if (!list->isEmpty()) {
-                ArenaHeader *aheader = reinterpret_cast<Cell *>(list->start)->arenaHeader();
+                ArenaHeader *aheader = list->arenaHeader();
                 JS_ASSERT(!aheader->hasFreeThings());
                 aheader->setFirstFreeSpan(list);
-                list->start = list->end = 0;
+                list->initAsEmpty();
             }
         }
     }
 
     /*
      * Temporarily copy the free list heads to the arenas so the code can see
      * the proper value in ArenaHeader::freeList when accessing the latter
      * outside the GC.
      */
     void copyToArenas() {
         for (size_t i = 0; i != size_t(FINALIZE_LIMIT); ++i) {
             FreeSpan *list = &lists[i];
             if (!list->isEmpty()) {
-                ArenaHeader *aheader = reinterpret_cast<Cell *>(list->start)->arenaHeader();
+                ArenaHeader *aheader = list->arenaHeader();
                 JS_ASSERT(!aheader->hasFreeThings());
                 aheader->setFirstFreeSpan(list);
             }
         }
     }
 
     /*
      * Clear the free lists in arenas that were temporarily set there using
      * copyToArenas.
      */
     void clearInArenas() {
         for (size_t i = 0; i != size_t(FINALIZE_LIMIT); ++i) {
             FreeSpan *list = &lists[i];
             if (!list->isEmpty()) {
-                ArenaHeader *aheader = reinterpret_cast<Cell *>(list->start)->arenaHeader();
-#ifdef DEBUG
-                FreeSpan span(aheader->getFirstFreeSpan());
-                JS_ASSERT(span.start == list->start);
-                JS_ASSERT(span.end == list->end);
-#endif
+                ArenaHeader *aheader = list->arenaHeader();
+                JS_ASSERT(aheader->getFirstFreeSpan().isSameNonEmptySpan(list));
                 aheader->setAsFullyUsed();
             }
         }
     }
 
-    JS_ALWAYS_INLINE Cell *getNext(unsigned thingKind, size_t thingSize) {
-        FreeSpan *list = &lists[thingKind];
-        list->checkSpan();
-        uintptr_t thing = list->start;
-        if (thing != list->end) {
-            /*
-             * We either have at least one thing in the span that ends the
-             * arena list or we have at least two things in the non-last span.
-             * In both cases we just need to bump the start pointer to account
-             * for the allocation.
-             */
-            list->start += thingSize;
-            JS_ASSERT(list->start <= list->end);
-        } else if (thing & ArenaMask) {
-            /*
-             * The thing points to the last thing in the span that has at
-             * least one more span to follow. Return the thing and update
-             * the list with that next span.
-             */
-            *list = *list->nextSpan();
-        } else {
-            return NULL;
-        }
-        return reinterpret_cast<Cell *>(thing);
+    JS_ALWAYS_INLINE void *getNext(unsigned thingKind, size_t thingSize) {
+        return lists[thingKind].allocate(thingSize);
     }
 
-    Cell *populate(ArenaHeader *aheader, unsigned thingKind, size_t thingSize) {
-        lists[thingKind] = aheader->getFirstFreeSpan();
+    void *populate(ArenaHeader *aheader, unsigned thingKind, size_t thingSize) {
+        FreeSpan *list = &lists[thingKind];
+        *list = aheader->getFirstFreeSpan();
         aheader->setAsFullyUsed();
-        Cell *t = getNext(thingKind, thingSize);
+        void *t = list->allocate(thingSize);
         JS_ASSERT(t);
         return t;
     }
 
     void checkEmpty() {
 #ifdef DEBUG
         for (size_t i = 0; i != JS_ARRAY_LENGTH(lists); ++i)
             JS_ASSERT(lists[i].isEmpty());
 #endif
     }
 };
 
-extern Cell *
+extern void *
 RefillFinalizableFreeList(JSContext *cx, unsigned thingKind);
 
 } /* namespace gc */
 
 typedef Vector<gc::Chunk *, 32, SystemAllocPolicy> GCChunks;
 
 struct GCPtrHasher
 {
--- a/js/src/jsgcinlines.h
+++ b/js/src/jsgcinlines.h
@@ -198,18 +198,18 @@ NewGCThing(JSContext *cx, unsigned thing
 #endif
     JS_ASSERT(!cx->runtime->gcRunning);
 
 #ifdef JS_GC_ZEAL
     if (cx->runtime->needZealousGC())
         js::gc::RunDebugGC(cx);
 #endif
 
-    js::gc::Cell *cell = cx->compartment->freeLists.getNext(thingKind, thingSize);
-    return static_cast<T *>(cell ? cell : js::gc::RefillFinalizableFreeList(cx, thingKind));
+    void *t = cx->compartment->freeLists.getNext(thingKind, thingSize);
+    return static_cast<T *>(t ? t : js::gc::RefillFinalizableFreeList(cx, thingKind));
 }
 
 inline JSObject *
 js_NewGCObject(JSContext *cx, js::gc::FinalizeKind kind)
 {
     JS_ASSERT(kind >= js::gc::FINALIZE_OBJECT0 && kind <= js::gc::FINALIZE_OBJECT_LAST);
     JSObject *obj = NewGCThing<JSObject>(cx, kind, js::gc::GCThingSizeMap[kind]);
     if (obj) {
--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -149,16 +149,29 @@ js::GetBlockChain(JSContext *cx, StackFr
     if (!fp->isScriptFrame())
         return NULL;
 
     /* Assume that imacros don't affect blockChain */
     jsbytecode *target = fp->hasImacropc() ? fp->imacropc() : fp->pcQuadratic(cx);
 
     JSScript *script = fp->script();
     jsbytecode *start = script->code;
+
+    /*
+     * If the debugger asks for the scope chain at a pc where we are about to
+     * fix it up, advance target past the fixup. See bug 672804.
+     */
+    JSOp op = js_GetOpcode(cx, script, target);
+    while (op == JSOP_NOP || op == JSOP_INDEXBASE || op == JSOP_INDEXBASE1 ||
+           op == JSOP_INDEXBASE2 || op == JSOP_INDEXBASE3 ||
+           op == JSOP_BLOCKCHAIN || op == JSOP_NULLBLOCKCHAIN)
+    {
+        target += js_CodeSpec[op].length;
+        op = js_GetOpcode(cx, script, target);
+    }
     JS_ASSERT(target >= start && target < start + script->length);
 
     JSObject *blockChain = NULL;
     uintN indexBase = 0;
     ptrdiff_t oplen;
     for (jsbytecode *pc = start; pc < target; pc += oplen) {
         JSOp op = js_GetOpcode(cx, script, pc);
         const JSCodeSpec *cs = &js_CodeSpec[op];
--- a/js/src/jstracer.cpp
+++ b/js/src/jstracer.cpp
@@ -13207,23 +13207,17 @@ TraceRecorder::setElem(int lval_spindex,
         LIns* slots_ins = w.ldpObjFixedSlots(obj_ins);
         // Ensure idx >= 0 && idx < length (by using uint32)
         CHECK_STATUS_A(guard(true,
                              w.name(w.ltui(idx_ins, w.ldiConstTypedArrayLength(slots_ins)),
                                     "inRange"),
                              OVERFLOW_EXIT, /* abortIfAlwaysExits = */true));
 
         // We're now ready to store
-        LIns* data_base_ins = w.ldpConstTypedArrayData(slots_ins);
-        LIns* offset_ins = w.ldiConstTypedArrayByteOffset(slots_ins);
-#ifdef NANOJIT_64BIT
-        LIns* data_ins = w.addp(data_base_ins, w.ui2uq(offset_ins));
-#else
-        LIns* data_ins = w.addp(data_base_ins, offset_ins);
-#endif
+        LIns* data_ins = w.ldpConstTypedArrayData(obj_ins);
 
         LIns* pidx_ins = w.ui2p(idx_ins);
         LIns* typed_v_ins = v_ins;
 
         // If it's not a number, convert objects to NaN,
         // null to 0, and call StringToNumber or BooleanOrUndefinedToNumber
         // for those.
         if (!v.isNumber()) {
@@ -14306,23 +14300,17 @@ TraceRecorder::typedArrayElement(Value& 
      * length.
      */
     guard(true,
           w.name(w.ltui(idx_ins, w.ldiConstTypedArrayLength(slots_ins)), "inRange"),
           BRANCH_EXIT);
 
     /* We are now ready to load.  Do a different type of load
      * depending on what type of thing we're loading. */
-    LIns* data_base_ins = w.ldpConstTypedArrayData(slots_ins);
-    LIns* offset_ins = w.ldiConstTypedArrayByteOffset(slots_ins);
-#ifdef NANOJIT_64BIT
-    LIns* data_ins = w.addp(data_base_ins, w.ui2uq(offset_ins));
-#else
-    LIns* data_ins = w.addp(data_base_ins, offset_ins);
-#endif
+    LIns* data_ins = w.ldpConstTypedArrayData(obj_ins);
 
     switch (js::TypedArray::getType(tarray)) {
       case js::TypedArray::TYPE_INT8:
         v_ins = w.i2d(w.ldc2iTypedArrayElement(data_ins, pidx_ins));
         break;
       case js::TypedArray::TYPE_UINT8:
       case js::TypedArray::TYPE_UINT8_CLAMPED:
         // i2d on purpose here:  it's safe, because an 8-bit uint is guaranteed
--- a/js/src/jstypedarray.cpp
+++ b/js/src/jstypedarray.cpp
@@ -920,27 +920,23 @@ class TypedArrayTemplate
         JSObject *obj = NewBuiltinClassInstance(cx, slowClass());
         if (!obj)
             return NULL;
 
         obj->setSlot(FIELD_TYPE, Int32Value(ArrayTypeID()));
 
         do {
             obj->setSlot(FIELD_BUFFER, ObjectValue(*bufobj));
+
             /*
-             * NOTE: unlike the earlier implementation where the 'data' pointed
-             * directly to the right offset in the ArrayBuffer
-             * this points to the base of the ArrayBuffer.
-             * getIndex is modified to get the right index.
-             *
-             * This is because on 64 bit systems the jsval.h Private API
-             * requires pointers stored in jsvals to be two-byte aligned.
-             * TM and JM both need a few extra instructions to add the offset.
+             * N.B. The base of the array's data is stored in the object's
+             * private data rather than a slot, to avoid alignment restrictions
+             * on private Values.
              */
-            obj->setSlot(FIELD_DATA, PrivateValue(ArrayBuffer::getDataOffset(bufobj)));
+            obj->setPrivate(ArrayBuffer::getDataOffset(bufobj) + byteOffset);
         } while(0);
 
         obj->setSlot(FIELD_LENGTH, Int32Value(len));
         obj->setSlot(FIELD_BYTEOFFSET, Int32Value(byteOffset));
         obj->setSlot(FIELD_BYTELENGTH, Int32Value(len * sizeof(NativeType)));
 
         JS_ASSERT(ArrayBuffer::getByteLength(getBuffer(obj)) - getByteOffset(obj) >= getByteLength(obj));
         JS_ASSERT(getByteOffset(obj) <= ArrayBuffer::getByteLength(getBuffer(obj)));
@@ -1737,31 +1733,33 @@ JSFunctionSpec _typedArray::jsfuncs[] = 
     JS_FN("set", _typedArray::fun_set, 2, JSFUN_GENERIC_NATIVE),               \
     JS_FS_END                                                                  \
 }
 
 #define IMPL_TYPED_ARRAY_SLOW_CLASS(_typedArray)                               \
 {                                                                              \
     #_typedArray,                                                              \
     JSCLASS_HAS_RESERVED_SLOTS(TypedArray::FIELD_MAX) |                        \
+    JSCLASS_HAS_PRIVATE |                                                      \
     JSCLASS_HAS_CACHED_PROTO(JSProto_##_typedArray),                           \
     PropertyStub,         /* addProperty */                                    \
     PropertyStub,         /* delProperty */                                    \
     PropertyStub,         /* getProperty */                                    \
     StrictPropertyStub,   /* setProperty */                                    \
     EnumerateStub,                                                             \
     ResolveStub,                                                               \
     ConvertStub,                                                               \
     FinalizeStub                                                               \
 }
 
 #define IMPL_TYPED_ARRAY_FAST_CLASS(_typedArray)                               \
 {                                                                              \
     #_typedArray,                                                              \
     JSCLASS_HAS_RESERVED_SLOTS(TypedArray::FIELD_MAX) |                        \
+    JSCLASS_HAS_PRIVATE |                                                      \
     Class::NON_NATIVE,                                                         \
     PropertyStub,         /* addProperty */                                    \
     PropertyStub,         /* delProperty */                                    \
     PropertyStub,         /* getProperty */                                    \
     StrictPropertyStub,   /* setProperty */                                    \
     EnumerateStub,                                                             \
     ResolveStub,                                                               \
     ConvertStub,                                                               \
--- a/js/src/jstypedarray.h
+++ b/js/src/jstypedarray.h
@@ -137,22 +137,22 @@ struct JS_FRIEND_API(TypedArray) {
          * Treat the raw data type as a uint8.
          */
         TYPE_UINT8_CLAMPED,
 
         TYPE_MAX
     };
 
     enum {
+        /* Properties of the typed array stored in reserved slots. */
         FIELD_LENGTH = 0,
         FIELD_BYTEOFFSET,
         FIELD_BYTELENGTH,
         FIELD_TYPE,
         FIELD_BUFFER,
-        FIELD_DATA,
         FIELD_MAX
     };
 
     // and MUST NOT be used to construct new objects.
     static Class fastClasses[TYPE_MAX];
 
     // These are the slow/original classes, used
     // fo constructing new objects
@@ -176,18 +176,16 @@ struct JS_FRIEND_API(TypedArray) {
 
     static JSUint32 getLength(JSObject *obj);
     static JSUint32 getByteOffset(JSObject *obj);
     static JSUint32 getByteLength(JSObject *obj);
     static JSUint32 getType(JSObject *obj);
     static JSObject * getBuffer(JSObject *obj);
     static void * getDataOffset(JSObject *obj);
 
-    static void *offsetData(JSObject *obj, uint32 offs);
-
   public:
     static bool
     isArrayIndex(JSContext *cx, JSObject *obj, jsid id, jsuint *ip = NULL);
 
     static inline int slotWidth(JSObject *obj) {
         switch (getType(obj)) {
           case js::TypedArray::TYPE_INT8:
           case js::TypedArray::TYPE_UINT8:
--- a/js/src/jstypedarrayinlines.h
+++ b/js/src/jstypedarrayinlines.h
@@ -79,12 +79,12 @@ TypedArray::getType(JSObject *obj) {
 
 inline JSObject *
 TypedArray::getBuffer(JSObject *obj) {
     return &obj->getSlot(FIELD_BUFFER).toObject();
 }
 
 inline void *
 TypedArray::getDataOffset(JSObject *obj) {
-    return (void *)((uint8*)obj->getSlot(FIELD_DATA).toPrivate() + getByteOffset(obj));
+    return (void *)obj->getPrivate();
 }
 }
 #endif /* jstypedarrayinlines_h */
--- a/js/src/jswrapper.cpp
+++ b/js/src/jswrapper.cpp
@@ -390,21 +390,22 @@ bool
 ForceFrame::enter()
 {
     frame = context->new_<DummyFrameGuard>();
     if (!frame)
        return false;
     LeaveTrace(context);
 
     JS_ASSERT(context->compartment == target->compartment());
+    JSCompartment *destination = context->compartment;
 
     JSObject *scopeChain = target->getGlobal();
     JS_ASSERT(scopeChain->isNative());
 
-    return context->stack.pushDummyFrame(context, REPORT_ERROR, *scopeChain, frame);
+    return context->stack.pushDummyFrame(context, *scopeChain, frame);
 }
 
 AutoCompartment::AutoCompartment(JSContext *cx, JSObject *target)
     : context(cx),
       origin(cx->compartment),
       target(target),
       destination(target->getCompartment()),
       entered(false)
@@ -423,31 +424,18 @@ AutoCompartment::enter()
     JS_ASSERT(!entered);
     if (origin != destination) {
         LeaveTrace(context);
 
         JSObject *scopeChain = target->getGlobal();
         JS_ASSERT(scopeChain->isNative());
 
         frame.construct();
-
-        /*
-         * Set the compartment eagerly so that pushDummyFrame associates the
-         * resource allocation request with 'destination' instead of 'origin'.
-         * (This is important when content has overflowed the stack and chrome
-         * is preparing to run JS to throw up a slow script dialog.) However,
-         * if an exception is thrown, we need it to be in origin's compartment
-         * so be careful to only report after restoring.
-         */
-        context->compartment = destination;
-        if (!context->stack.pushDummyFrame(context, DONT_REPORT_ERROR, *scopeChain, &frame.ref())) {
-            context->compartment = origin;
-            js_ReportOverRecursed(context);
+        if (!context->stack.pushDummyFrame(context, *scopeChain, &frame.ref()))
             return false;
-        }
 
         if (context->isExceptionPending())
             context->wrapPendingException();
     }
     entered = true;
     return true;
 }
 
--- a/js/src/methodjit/Logging.h
+++ b/js/src/methodjit/Logging.h
@@ -70,17 +70,21 @@ enum JaegerSpewChannel {
 # define JS_METHODJIT_SPEW
 #endif
 
 #if defined(JS_METHODJIT_SPEW)
 
 void JMCheckLogging();
 
 bool IsJaegerSpewChannelActive(JaegerSpewChannel channel);
+#ifdef __GNUC__
+void JaegerSpew(JaegerSpewChannel channel, const char *fmt, ...) __attribute__ ((format (printf, 2, 3)));
+#else
 void JaegerSpew(JaegerSpewChannel channel, const char *fmt, ...);
+#endif
 
 struct Profiler {
     JSInt64 t_start;
     JSInt64 t_stop;
 
     static inline JSInt64 now() {
         return PRMJ_Now();
     }
--- a/js/src/methodjit/PolyIC.cpp
+++ b/js/src/methodjit/PolyIC.cpp
@@ -2461,40 +2461,34 @@ GetElementIC::attachTypedArray(JSContext
     // known to be int32, either via type inference or the inline type check.
     JS_ASSERT(hasInlineTypeGuard() || idRemat.knownType() == JSVAL_TYPE_INT32);
 
     Assembler masm;
 
     // Guard on this typed array's clasp.
     Jump claspGuard = masm.testObjClass(Assembler::NotEqual, objReg, obj->getClass());
 
-    // Get the internal typed array.
-    masm.loadPtr(Address(objReg, JSObject::offsetOfSlots()), objReg);
-
     // Bounds check.
     Jump outOfBounds;
-    Address typedArrayLength(objReg, sizeof(uint64) * js::TypedArray::FIELD_LENGTH);
+    masm.loadPtr(Address(objReg, JSObject::offsetOfSlots()), typeReg);
+    Address typedArrayLength(typeReg, sizeof(uint64) * js::TypedArray::FIELD_LENGTH);
     typedArrayLength = masm.payloadOf(typedArrayLength);
     if (idRemat.isConstant()) {
         JS_ASSERT(idRemat.value().toInt32() == v.toInt32());
         outOfBounds = masm.branch32(Assembler::BelowOrEqual, typedArrayLength, Imm32(v.toInt32()));
     } else {
         outOfBounds = masm.branch32(Assembler::BelowOrEqual, typedArrayLength, idRemat.dataReg());
     }
 
     // Load the array's packed data vector.
-    Address data_base(objReg, sizeof(Value) * js::TypedArray::FIELD_DATA);
-    masm.loadPrivate(data_base, objReg);
+    masm.loadPtr(Address(objReg, offsetof(JSObject, privateData)), objReg);
 
     JSObject *tarray = js::TypedArray::getTypedArray(obj);
     int shift = js::TypedArray::slotWidth(tarray);
 
-    int byteOffset = js::TypedArray::getByteOffset(tarray);
-    masm.addPtr(Imm32(byteOffset), objReg);
-
     if (idRemat.isConstant()) {
         int32 index = v.toInt32();
         Address addr(objReg, index * shift);
         LoadFromTypedArray(masm, tarray, addr, typeReg, objReg);
     } else {
         Assembler::Scale scale = Assembler::TimesOne;
         switch (shift) {
           case 2:
@@ -2810,34 +2804,32 @@ SetElementIC::attachTypedArray(JSContext
     JS_ASSERT(!inlineClaspGuardPatched);
 
     Assembler masm;
     //masm.breakpoint();
 
     // Guard on this typed array's clasp.
     Jump claspGuard = masm.testObjClass(Assembler::NotEqual, objReg, obj->getClass());
 
-    // Get the internal typed array.
-    masm.loadPtr(Address(objReg, JSObject::offsetOfSlots()), objReg);
-
     // Bounds check.
     Jump outOfBounds;
+    masm.loadPtr(Address(objReg, JSObject::offsetOfSlots()), objReg);
     Address typedArrayLength(objReg, sizeof(uint64) * js::TypedArray::FIELD_LENGTH);
     typedArrayLength = masm.payloadOf(typedArrayLength);
     if (hasConstantKey)
         outOfBounds = masm.branch32(Assembler::BelowOrEqual, typedArrayLength, Imm32(keyValue));
     else
         outOfBounds = masm.branch32(Assembler::BelowOrEqual, typedArrayLength, keyReg);
 
+    // Restore |obj|.
+    masm.rematPayload(StateRemat::FromInt32(objRemat), objReg);
+
     // Load the array's packed data vector.
     JSObject *tarray = js::TypedArray::getTypedArray(obj);
-    int byteOffset = js::TypedArray::getByteOffset(tarray);
-    Address base_data(objReg, sizeof(uint64) * js::TypedArray::FIELD_DATA);
-    masm.loadPrivate(base_data, objReg);
-    masm.addPtr(Imm32(byteOffset), objReg);
+    masm.loadPtr(Address(objReg, offsetof(JSObject, privateData)), objReg);
 
     int shift = js::TypedArray::slotWidth(obj);
     if (hasConstantKey) {
         Address addr(objReg, keyValue * shift);
         if (!StoreToTypedArray(cx, masm, tarray, addr, vr, volatileMask))
             return error(cx);
     } else {
         Assembler::Scale scale = Assembler::TimesOne;
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -678,21 +678,36 @@ ParseZealArg(JSContext *cx, const char *
     JS_SetGCZeal(cx, (uint8)zeal, freq, !!compartment);
 }
 #endif
 
 static JSBool
 Version(JSContext *cx, uintN argc, jsval *vp)
 {
     jsval *argv = JS_ARGV(cx, vp);
-    if (argc > 0 && JSVAL_IS_INT(argv[0]))
-        *vp = INT_TO_JSVAL(JS_SetVersion(cx, (JSVersion) JSVAL_TO_INT(argv[0])));
-    else
+    if (argc == 0 || JSVAL_IS_VOID(argv[0])) {
+        /* Get version. */
         *vp = INT_TO_JSVAL(JS_GetVersion(cx));
-    return JS_TRUE;
+    } else {
+        /* Set version. */
+        int32 v = -1;
+        if (JSVAL_IS_INT(argv[0])) {
+            v = JSVAL_TO_INT(argv[0]);
+        } else if (JSVAL_IS_DOUBLE(argv[0])) {
+            jsdouble fv = JSVAL_TO_DOUBLE(argv[0]);
+            if (int32(fv) == fv)
+                v = int32(fv);
+        }
+        if (v < 0 || v > JSVERSION_LATEST) {
+            JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_INVALID_ARGS, "version");
+            return false;
+        }
+        *vp = INT_TO_JSVAL(JS_SetVersion(cx, JSVersion(v)));
+    }
+    return true;
 }
 
 static JSBool
 RevertVersion(JSContext *cx, uintN argc, jsval *vp)
 {
     js_RevertVersion(cx);
     JS_SET_RVAL(cx, vp, JSVAL_VOID);
     return JS_TRUE;
--- a/js/src/tests/js1_8_5/extensions/jstests.list
+++ b/js/src/tests/js1_8_5/extensions/jstests.list
@@ -47,8 +47,12 @@ script regress-631723.js
 skip-if(!xulRuntime.shell) script regress-636818.js
 script regress-636697.js
 script regress-637985.js
 script is-generator.js
 script weakmap.js
 script regress-645160.js
 script regress-650753.js
 script regress-668438.js
+require-or(debugMode,skip) script regress-672804-1.js
+require-or(debugMode,skip) script regress-672804-2.js
+require-or(debugMode,skip) script regress-672804-3.js
+script regress-677924.js
new file mode 100644
--- /dev/null
+++ b/js/src/tests/js1_8_5/extensions/regress-672804-1.js
@@ -0,0 +1,12 @@
+// Any copyright is dedicated to the Public Domain.
+// http://creativecommons.org/licenses/publicdomain/
+
+var a = 0;
+function f() {
+    let (a = let (x = 1) x) {}
+}
+
+trap(f, 3, 'assertEq(evalInFrame(1, "a"), 0)');
+f();
+
+reportCompare(0, 0, 'ok');
new file mode 100644
--- /dev/null
+++ b/js/src/tests/js1_8_5/extensions/regress-672804-2.js
@@ -0,0 +1,12 @@
+// Any copyright is dedicated to the Public Domain.
+// http://creativecommons.org/licenses/publicdomain/
+
+var a = 0;
+function f() {
+    let (a = let (x = 1) x) {}
+}
+
+trap(f, 4, 'assertEq(evalInFrame(1, "a"), 0)');
+f();
+
+reportCompare(0, 0, 'ok');
new file mode 100644
--- /dev/null
+++ b/js/src/tests/js1_8_5/extensions/regress-672804-3.js
@@ -0,0 +1,11 @@
+// Any copyright is dedicated to the Public Domain.
+// http://creativecommons.org/licenses/publicdomain/
+
+var e = [], x = {b: []};
+function f() {
+    let (a = [[] for (x in e)], {b: []} = x) {}
+}
+trap(f, 4, '');
+f();
+
+reportCompare(0, 0, 'ok');
new file mode 100644
--- /dev/null
+++ b/js/src/tests/js1_8_5/extensions/regress-677924.js
@@ -0,0 +1,14 @@
+// Any copyright is dedicated to the Public Domain.
+// http://creativecommons.org/licenses/publicdomain/
+
+try {
+    version(4096);  // don't assert
+} catch (exc) {
+}
+
+try {
+    version(-1);  // don't assert
+} catch (exc) {
+}
+
+reportCompare(0, 0, 'ok');
--- a/js/src/tracejit/Writer.h
+++ b/js/src/tracejit/Writer.h
@@ -550,29 +550,19 @@ class Writer
     }
 
     nj::LIns *ldiConstTypedArrayByteOffset(nj::LIns *array) const {
         return name(lir->insLoad(nj::LIR_ldi, array, sizeof(Value) * js::TypedArray::FIELD_BYTEOFFSET + sPayloadOffset, ACCSET_TARRAY,
                                  nj::LOAD_CONST),
                     "typedArrayByteOffset");
     }
 
-    nj::LIns *ldpConstTypedArrayData(nj::LIns *array) const {
-        //return name(lir->insLoad(nj::LIR_ldp, array, sizeof(Value) * js::TypedArray::FIELD_DATA + sPayloadOffset, ACCSET_TARRAY,
-                                 //nj::LOAD_CONST),
-                    //"typedElems");
-        uint32 offset = sizeof(Value) * js::TypedArray::FIELD_DATA + sPayloadOffset;
-#if JS_BITS_PER_WORD == 32
-        return name(lir->insLoad(nj::LIR_ldi, array, offset, ACCSET_TARRAY, nj::LOAD_CONST), "typedArrayData");
-#elif JS_BITS_PER_WORD == 64
-        /* N.B. On 64-bit, privatized value are encoded differently from other pointers. */
-        nj::LIns *v_ins = lir->insLoad(nj::LIR_ldq, array, offset,
-                                       ACCSET_TARRAY, nj::LOAD_CONST);
-        return name(lshqN(v_ins, 1), "typedArrayData");
-#endif
+    nj::LIns *ldpConstTypedArrayData(nj::LIns *obj) const {
+        uint32 offset = offsetof(JSObject, privateData);
+        return name(lir->insLoad(nj::LIR_ldp, obj, offset, ACCSET_TARRAY, nj::LOAD_CONST), "typedArrayData");
     }
 
     nj::LIns *ldc2iTypedArrayElement(nj::LIns *elems, nj::LIns *index) const {
         return lir->insLoad(nj::LIR_ldc2i, addp(elems, index), 0, ACCSET_TARRAY_DATA);
     }
 
     nj::LIns *lduc2uiTypedArrayElement(nj::LIns *elems, nj::LIns *index) const {
         return lir->insLoad(nj::LIR_lduc2ui, addp(elems, index), 0, ACCSET_TARRAY_DATA);
--- a/js/src/vm/Stack-inl.h
+++ b/js/src/vm/Stack-inl.h
@@ -383,28 +383,35 @@ StackSpace::ensureEnoughSpaceToEnterTrac
     ptrdiff_t needed = TraceNativeStorage::MAX_NATIVE_STACK_SLOTS +
                        TraceNativeStorage::MAX_CALL_STACK_ENTRIES * VALUES_PER_STACK_FRAME;
     return ensureSpace(cx, DONT_REPORT_ERROR, firstUnused(), needed);
 }
 #endif
 
 STATIC_POSTCONDITION(!return || ubound(from) >= nvals)
 JS_ALWAYS_INLINE bool
-StackSpace::ensureSpace(JSContext *cx, MaybeReportError report, Value *from, ptrdiff_t nvals) const
+StackSpace::ensureSpace(JSContext *cx, MaybeReportError report, Value *from, ptrdiff_t nvals,
+                        JSCompartment *dest) const
 {
     assertInvariants();
     JS_ASSERT(from >= firstUnused());
 #ifdef XP_WIN
     JS_ASSERT(from <= commitEnd_);
 #endif
     if (JS_UNLIKELY(conservativeEnd_ - from < nvals))
-        return ensureSpaceSlow(cx, report, from, nvals);
+        return ensureSpaceSlow(cx, report, from, nvals, dest);
     return true;
 }
 
+bool
+StackSpace::ensureSpace(JSContext *cx, MaybeReportError report, Value *from, ptrdiff_t nvals) const
+{
+    return ensureSpace(cx, report, from, nvals, cx->compartment);
+}
+
 inline Value *
 StackSpace::getStackLimit(JSContext *cx, MaybeReportError report)
 {
     FrameRegs &regs = cx->regs();
     uintN nvals = regs.fp()->numSlots() + VALUES_PER_STACK_FRAME;
     return ensureSpace(cx, report, regs.sp, nvals)
            ? conservativeEnd_
            : NULL;
--- a/js/src/vm/Stack.cpp
+++ b/js/src/vm/Stack.cpp
@@ -409,23 +409,23 @@ StackSpace::mark(JSTracer *trc)
             slotsEnd = (Value *)fp;
         }
         MarkStackRangeConservatively(trc, seg->slotsBegin(), slotsEnd);
         nextSegEnd = (Value *)seg;
     }
 }
 
 JS_FRIEND_API(bool)
-StackSpace::ensureSpaceSlow(JSContext *cx, MaybeReportError report,
-                            Value *from, ptrdiff_t nvals) const
+StackSpace::ensureSpaceSlow(JSContext *cx, MaybeReportError report, Value *from, ptrdiff_t nvals,
+                            JSCompartment *dest) const
 {
     assertInvariants();
 
-    bool trusted = !cx->compartment ||
-                   cx->compartment->principals == cx->runtime->trustedPrincipals();
+    JS_ASSERT_IF(dest, cx);
+    bool trusted = !dest || dest->principals == cx->runtime->trustedPrincipals();
     Value *end = trusted ? trustedEnd_ : defaultEnd_;
 
     /*
      * conservativeEnd_ must stay below defaultEnd_: if conservativeEnd_ were
      * to be bumped past defaultEnd_, untrusted JS would be able to consume the
      * buffer space at the end of the stack reserved for trusted JS.
      */
 
@@ -543,27 +543,27 @@ ContextStack::containsSlow(const StackFr
  * pushing a StackSegment. The 'pushedSeg' outparam indicates whether such a
  * segment was pushed (and hence whether the caller needs to call popSegment).
  *
  * Additionally, to minimize calls to ensureSpace, ensureOnTop ensures that
  * there is space for nvars slots on top of the stack.
  */
 Value *
 ContextStack::ensureOnTop(JSContext *cx, MaybeReportError report, uintN nvars,
-                          MaybeExtend extend, bool *pushedSeg)
+                          MaybeExtend extend, bool *pushedSeg, JSCompartment *dest)
 {
     Value *firstUnused = space().firstUnused();
 
     if (onTop() && extend) {
-        if (!space().ensureSpace(cx, report, firstUnused, nvars))
+        if (!space().ensureSpace(cx, report, firstUnused, nvars, dest))
             return NULL;
         return firstUnused;
     }
 
-    if (!space().ensureSpace(cx, report, firstUnused, VALUES_PER_STACK_SEGMENT + nvars))
+    if (!space().ensureSpace(cx, report, firstUnused, VALUES_PER_STACK_SEGMENT + nvars, dest))
         return NULL;
 
     FrameRegs *regs;
     CallArgsList *calls;
     if (seg_ && extend) {
         regs = seg_->maybeRegs();
         calls = seg_->maybeCalls();
     } else {
@@ -572,16 +572,23 @@ ContextStack::ensureOnTop(JSContext *cx,
     }
 
     seg_ = new(firstUnused) StackSegment(seg_, space().seg_, regs, calls);
     space().seg_ = seg_;
     *pushedSeg = true;
     return seg_->slotsBegin();
 }
 
+Value *
+ContextStack::ensureOnTop(JSContext *cx, MaybeReportError report, uintN nvars,
+                          MaybeExtend extend, bool *pushedSeg)
+{
+    return ensureOnTop(cx, report, nvars, extend, pushedSeg, cx->compartment);
+}
+
 void
 ContextStack::popSegment()
 {
     space().seg_ = seg_->prevInMemory();
     seg_ = seg_->prevInContext();
 
     if (!seg_)
         cx_->maybeMigrateVersionOverride();
@@ -691,28 +698,30 @@ ContextStack::pushExecuteFrame(JSContext
 
     efg->prevRegs_ = seg_->pushRegs(efg->regs_);
     JS_ASSERT(space().firstUnused() == efg->regs_.sp);
     efg->setPushed(*this);
     return true;
 }
 
 bool
-ContextStack::pushDummyFrame(JSContext *cx, MaybeReportError report, JSObject &scopeChain,
-                             DummyFrameGuard *dfg)
+ContextStack::pushDummyFrame(JSContext *cx, JSObject &scopeChain, DummyFrameGuard *dfg)
 {
+    JSCompartment *dest = scopeChain.compartment();
+
     uintN nvars = VALUES_PER_STACK_FRAME;
-    Value *firstUnused = ensureOnTop(cx, report, nvars, CAN_EXTEND, &dfg->pushedSeg_);
+    Value *firstUnused = ensureOnTop(cx, REPORT_ERROR, nvars, CAN_EXTEND, &dfg->pushedSeg_, dest);
     if (!firstUnused)
         return NULL;
 
     StackFrame *fp = reinterpret_cast<StackFrame *>(firstUnused);
     fp->initDummyFrame(cx, scopeChain);
     dfg->regs_.initDummyFrame(*fp);
 
+    cx->compartment = dest;
     dfg->prevRegs_ = seg_->pushRegs(dfg->regs_);
     JS_ASSERT(space().firstUnused() == dfg->regs_.sp);
     dfg->setPushed(*this);
     return true;
 }
 
 void
 ContextStack::popFrame(const FrameGuard &fg)
@@ -785,34 +794,21 @@ ContextStack::popGeneratorFrame(const Ge
 
     /* ~FrameGuard/popFrame will finish the popping. */
     JS_ASSERT(ImplicitCast<const FrameGuard>(gfg).pushed());
 }
 
 bool
 ContextStack::saveFrameChain()
 {
-    /*
-     * The StackSpace uses the context's current compartment to determine
-     * whether to allow access to the privileged end-of-stack buffer.
-     * However, we always want saveFrameChain to have access to this privileged
-     * buffer since it gets used to prepare calling trusted JS. To force this,
-     * we clear the current compartment (which is interpreted by ensureSpace as
-     * 'trusted') and either restore it on OOM or let resetCompartment()
-     * clobber it.
-     */
-    JSCompartment *original = cx_->compartment;
-    cx_->compartment = NULL;
+    JSCompartment *dest = NULL;
 
     bool pushedSeg;
-    if (!ensureOnTop(cx_, DONT_REPORT_ERROR, 0, CANT_EXTEND, &pushedSeg)) {
-        cx_->compartment = original;
-        js_ReportOverRecursed(cx_);
+    if (!ensureOnTop(cx_, REPORT_ERROR, 0, CANT_EXTEND, &pushedSeg, dest))
         return false;
-    }
 
     JS_ASSERT(pushedSeg);
     JS_ASSERT(!hasfp());
     JS_ASSERT(onTop() && seg_->isEmpty());
 
     cx_->resetCompartment();
     return true;
 }
--- a/js/src/vm/Stack.h
+++ b/js/src/vm/Stack.h
@@ -39,16 +39,17 @@
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef Stack_h__
 #define Stack_h__
 
 #include "jsfun.h"
 
 struct JSContext;
+struct JSCompartment;
 
 namespace js {
 
 class StackFrame;
 class FrameRegs;
 class StackSegment;
 class StackSpace;
 class ContextStack;
@@ -1325,19 +1326,24 @@ class StackSpace
         JS_STATIC_ASSERT(CAPACITY_VALS % COMMIT_VALS == 0);
     }
 
     friend class AllFramesIter;
     friend class ContextStack;
     friend class StackFrame;
 
     inline bool ensureSpace(JSContext *cx, MaybeReportError report,
+                            Value *from, ptrdiff_t nvals,
+                            JSCompartment *dest) const;
+    inline bool ensureSpace(JSContext *cx, MaybeReportError report,
                             Value *from, ptrdiff_t nvals) const;
     JS_FRIEND_API(bool) ensureSpaceSlow(JSContext *cx, MaybeReportError report,
-                                        Value *from, ptrdiff_t nvals) const;
+                                        Value *from, ptrdiff_t nvals,
+                                        JSCompartment *dest) const;
+
     StackSegment &findContainingSegment(const StackFrame *target) const;
 
   public:
     StackSpace();
     bool init();
     ~StackSpace();
 
     /*
@@ -1416,16 +1422,19 @@ class ContextStack
 #else
     void assertSpaceInSync() const {}
 #endif
 
     /* Implementation details of push* public interface. */
     StackSegment *pushSegment(JSContext *cx);
     enum MaybeExtend { CAN_EXTEND = true, CANT_EXTEND = false };
     Value *ensureOnTop(JSContext *cx, MaybeReportError report, uintN nvars,
+                       MaybeExtend extend, bool *pushedSeg,
+                       JSCompartment *dest);
+    Value *ensureOnTop(JSContext *cx, MaybeReportError report, uintN nvars,
                        MaybeExtend extend, bool *pushedSeg);
 
     inline StackFrame *
     getCallFrame(JSContext *cx, MaybeReportError report, const CallArgs &args,
                  JSFunction *fun, JSScript *script, StackFrame::Flags *pflags) const;
 
     /* Make pop* functions private since only called by guard classes. */
     void popSegment();
@@ -1497,19 +1506,26 @@ class ContextStack
     /*
      * Called by SendToGenerator to resume a yielded generator. In addition to
      * pushing a frame onto the VM stack, this function copies over the
      * floating frame stored in 'gen'. When 'gfg' is destroyed, the destructor
      * will copy the frame back to the floating frame.
      */
     bool pushGeneratorFrame(JSContext *cx, JSGenerator *gen, GeneratorFrameGuard *gfg);
 
-    /* Pushes a "dummy" frame; should be removed one day. */
-    bool pushDummyFrame(JSContext *cx, MaybeReportError report, JSObject &scopeChain,
-                        DummyFrameGuard *dfg);
+    /*
+     * When changing the compartment of a cx, it is necessary to immediately
+     * change the scope chain to a global in the right compartment since any
+     * amount of general VM code can run before the first scripted frame is
+     * pushed (if at all). This is currently and hackily accomplished by
+     * pushing a "dummy frame" with the correct scope chain. On success, this
+     * function will change the compartment to 'scopeChain.compartment()' and
+     * push a dummy frame for 'scopeChain'. On failure, nothing is changed.
+     */
+    bool pushDummyFrame(JSContext *cx, JSObject &scopeChain, DummyFrameGuard *dfg);
 
     /*
      * An "inline frame" may only be pushed from within the top, active
      * segment. This is the case for calls made inside mjit code and Interpret.
      * The 'stackLimit' overload updates 'stackLimit' if it changes.
      */
     bool pushInlineFrame(JSContext *cx, FrameRegs &regs, const CallArgs &args,
                          JSObject &callee, JSFunction *fun, JSScript *script,
--- a/layout/build/nsLayoutStatics.cpp
+++ b/layout/build/nsLayoutStatics.cpp
@@ -257,22 +257,17 @@ nsLayoutStatics::Initialize()
   nsRefreshDriver::InitializeStatics();
 
   nsCORSListenerProxy::Startup();
 
   nsFrameList::Init();
 
   NS_SealStaticAtomTable();
 
-// TODO: DOM_MEMORY_REPORTER should not be defined in a regular build for the
-// moment. This protection will be removed when bug 663271 will be close enough
-// to a shippable state.
-#ifdef DOM_MEMORY_REPORTER
   nsDOMMemoryReporter::Init();
-#endif
 
   return NS_OK;
 }
 
 void
 nsLayoutStatics::Shutdown()
 {
   // Don't need to shutdown nsDOMMemoryReporter, that will be done by the memory
--- a/layout/generic/nsLineLayout.cpp
+++ b/layout/generic/nsLineLayout.cpp
@@ -1490,17 +1490,17 @@ nsLineLayout::VerticalAlignLine()
   if (rootPFD.mFrame->GetStyleContext()->HasTextDecorationLines()) {
     for (const PerFrameData* pfd = psd->mFirstFrame; pfd; pfd = pfd->mNext) {
       const nsIFrame *const f = pfd->mFrame;
       const nsStyleCoord& vAlign =
           f->GetStyleContext()->GetStyleTextReset()->mVerticalAlign;
 
       if (vAlign.GetUnit() != eStyleUnit_Enumerated ||
           vAlign.GetIntValue() != NS_STYLE_VERTICAL_ALIGN_BASELINE) {
-        const nscoord offset = baselineY - (pfd->mBounds.y);
+        const nscoord offset = baselineY - pfd->mBounds.y;
         f->Properties().Set(nsIFrame::LineBaselineOffset(),
                             NS_INT32_TO_PTR(offset));
       }
     }
   }
 
   // Fill in returned line-box and max-element-width data
   mLineBox->mBounds.x = psd->mLeftEdge;
--- a/layout/generic/nsTextFrameThebes.cpp
+++ b/layout/generic/nsTextFrameThebes.cpp
@@ -4274,17 +4274,22 @@ nsTextFrame::GetTextDecorations(nsPresCo
   // baselineOffset represents the offset from our baseline to f's baseline or
   // the nearest block's baseline, in our coordinate space, whichever is closest
   // during the particular iteration
   nscoord frameTopOffset = mAscent,
           baselineOffset = 0;
 
   bool nearestBlockFound = false;
 
-  for (nsIFrame* f = this, *fParent; f; f = fParent) {
+  for (nsIFrame* f = this, *fChild = nsnull;
+       f;
+       fChild = f,
+       f = nsLayoutUtils::GetParentOrPlaceholderFor(
+             aPresContext->FrameManager(), f))
+  {
     nsStyleContext *const context = f->GetStyleContext();
     if (!context->HasTextDecorationLines()) {
       break;
     }
 
     const nsStyleTextReset *const styleText = context->GetStyleTextReset();
     const PRUint8 textDecorations = styleText->mTextDecorationLine;
 
@@ -4292,40 +4297,46 @@ nsTextFrame::GetTextDecorations(nsPresCo
         (NS_STYLE_TEXT_DECORATION_LINE_OVERRIDE_ALL & textDecorations)) {
       // This handles the <a href="blah.html"><font color="green">La 
       // la la</font></a> case. The link underline should be green.
       useOverride = PR_TRUE;
       overrideColor =
         nsLayoutUtils::GetColor(f, eCSSProperty_text_decoration_color);
     }
 
-    fParent = nsLayoutUtils::GetParentOrPlaceholderFor(
-                aPresContext->FrameManager(), f);
-    const bool firstBlock = !nearestBlockFound &&
-                            nsLayoutUtils::GetAsBlock(fParent);
+    const bool firstBlock = !nearestBlockFound && nsLayoutUtils::GetAsBlock(f);
 
     // Not updating positions once we hit a parent block is equivalent to
     // the CSS 2.1 spec that blocks should propagate decorations down to their
     // children (albeit the style should be preserved)
     // However, if we're vertically aligned within a block, then we need to
     // recover the right baseline from the line by querying the FrameProperty
     // that should be set (see nsLineLayout::VerticalAlignLine).
-    if (firstBlock &&
-        (styleText->mVerticalAlign.GetUnit() != eStyleUnit_Enumerated ||
-         styleText->mVerticalAlign.GetIntValue() !=
-           NS_STYLE_VERTICAL_ALIGN_BASELINE)) {
-      baselineOffset = frameTopOffset -
-        NS_PTR_TO_INT32(f->Properties().Get(nsIFrame::LineBaselineOffset()));
+    if (firstBlock) {
+      // At this point, fChild can't be null since TextFrames can't be blocks
+      const nsStyleCoord& vAlign =
+        fChild->GetStyleContext()->GetStyleTextReset()->mVerticalAlign;
+      if (vAlign.GetUnit() != eStyleUnit_Enumerated ||
+          vAlign.GetIntValue() != NS_STYLE_VERTICAL_ALIGN_BASELINE)
+      {
+        // Since offset is the offset in the child's coordinate space, we have
+        // to undo the accumulation to bring the transform out of the block's
+        // coordinate space
+        baselineOffset =
+          frameTopOffset - (fChild->GetRect().y - fChild->GetRelativeOffset().y)
+          - NS_PTR_TO_INT32(
+              fChild->Properties().Get(nsIFrame::LineBaselineOffset()));
+      }
     }
     else if (!nearestBlockFound) {
       baselineOffset = frameTopOffset - f->GetBaseline();
     }
 
     nearestBlockFound = nearestBlockFound || firstBlock;
-    frameTopOffset += f->GetRect().Y() - f->GetRelativeOffset().y;
+    frameTopOffset += f->GetRect().y - f->GetRelativeOffset().y;
 
     const PRUint8 style = styleText->GetDecorationStyle();
     // Accumulate only elements that have decorations with a genuine style
     if (textDecorations && style != NS_STYLE_TEXT_DECORATION_STYLE_NONE) {
       const nscolor color = useOverride ? overrideColor
         : nsLayoutUtils::GetColor(f, eCSSProperty_text_decoration_color);
 
       if (textDecorations & NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE) {
new file mode 100644
--- /dev/null
+++ b/layout/reftests/text-decoration/676538-1-ref.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html lang="en-US">
+<head>
+<title>text-decoration alignment</title>
+<meta charset=UTF-8>
+<style>
+
+span.block { position: absolute; top: 0; left: 0; }
+span.dec { text-decoration: underline }
+span.hide { color: transparent }
+
+</style>
+</head>
+<body>
+
+<p style="position: relative">
+  <span class="block" style="margin-top: 16px"><span class="hide"></span><span class="dec" style="color:black  ">x<span class="hide">xx</span></span></span>
+  <span class="block" style="margin-top: 08px"><span class="hide">x</span><span class="dec" style="color:fuchsia">x<span class="hide">x</span></span></span>
+  <span class="block" style="margin-top: 00px"><span class="hide">xx</span><span class="dec" style="color:aqua   ">x<span class="hide"></span></span></span>
+</p>
+
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/text-decoration/676538-1.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html lang="en-US">
+<head>
+<title>text-decoration alignment</title>
+<meta charset=UTF-8>
+<style>
+
+p, span { text-decoration: underline }
+span { vertical-align: 8px }
+p > span { color: fuchsia }
+p > span > span { color: aqua }
+
+em { font-style: normal; font-size: 5em }
+
+</style>
+</head>
+<body>
+
+<p>x<span>x<span>x</span></span></p>
+
+</body>
+</html>
--- a/layout/reftests/text-decoration/reftest.list
+++ b/layout/reftests/text-decoration/reftest.list
@@ -91,8 +91,9 @@ fails == underline-block-propagation-2-q
 == 641444-1.html 641444-1-ref.html
 == decoration-css21.html decoration-css21-ref.html
 == decoration-color-override-quirks.html decoration-color-override-quirks-ref.html
 == decoration-color-override-standards.html decoration-color-override-standards-ref.html
 != decoration-color-override-standards-ref.html decoration-color-override-quirks-ref.html
 == decoration-css21-block.html decoration-css21-block-ref.html
 != inline-baseline-almost-standards.html inline-baseline-almost-standards-ref.html
 != inline-baseline-quirks.html inline-baseline-quirks-ref.html
+== 676538-1.html 676538-1-ref.html
--- a/mobile/chrome/content/bindings/browser.js
+++ b/mobile/chrome/content/bindings/browser.js
@@ -251,43 +251,40 @@ let WebNavigation =  {
         aIdMap.used[id] = true;
       }
       shEntry.ID = id;
     }
 
     if (aEntry.docshellID)
       shEntry.docshellID = aEntry.docshellID;
 
-    if (aEntry.stateData)
-      shEntry.stateData = aEntry.stateData;
+    if (aEntry.structuredCloneState && aEntry.structuredCloneVersion) {
+      shEntry.stateData =
+        Cc["@mozilla.org/docshell/structured-clone-container;1"].
+        createInstance(Ci.nsIStructuredCloneContainer);
+
+      shEntry.stateData.initFromBase64(aEntry.structuredCloneState, aEntry.structuredCloneVersion);
+    }
 
     if (aEntry.scroll) {
       let scrollPos = aEntry.scroll.split(",");
       scrollPos = [parseInt(scrollPos[0]) || 0, parseInt(scrollPos[1]) || 0];
       shEntry.setScrollPosition(scrollPos[0], scrollPos[1]);
     }
 
     if (aEntry.docIdentifier) {
-      // Get a new document identifier for this entry to ensure that history
-      // entries after a session restore are considered to have different
-      // documents from the history entries before the session restore.
-      // Document identifiers are 64-bit ints, so JS will loose precision and
-      // start assigning all entries the same doc identifier if these ever get
-      // large enough.
-      //
-      // It's a potential security issue if document identifiers aren't
-      // globally unique, but shEntry.setUniqueDocIdentifier() below guarantees
-      // that we won't re-use a doc identifier within a given instance of the
-      // application.
-      let ident = aDocIdentMap[aEntry.docIdentifier];
-      if (!ident) {
-        shEntry.setUniqueDocIdentifier();
-        aDocIdentMap[aEntry.docIdentifier] = shEntry.docIdentifier;
+      // If we have a serialized document identifier, try to find an SHEntry
+      // which matches that doc identifier and adopt that SHEntry's
+      // BFCacheEntry.  If we don't find a match, insert shEntry as the match
+      // for the document identifier.
+      let matchingEntry = aDocIdentMap[aEntry.docIdentifier];
+      if (!matchingEntry) {
+        aDocIdentMap[aEntry.docIdentifier] = shEntry;
       } else {
-        shEntry.docIdentifier = ident;
+        shEntry.adoptBFCacheEntry(matchingEntry);
       }
     }
 
     if (aEntry.owner_b64) {
       let ownerInput = Cc["@mozilla.org/io/string-input-stream;1"].createInstance(Ci.nsIStringInputStream);
       let binaryData = atob(aEntry.owner_b64);
       ownerInput.setData(binaryData, binaryData.length);
       let binaryStream = Cc["@mozilla.org/binaryinputstream;1"].createInstance(Ci.nsIObjectInputStream);
@@ -362,21 +359,22 @@ let WebNavigation =  {
         let ownerBytes = scriptableStream.readByteArray(scriptableStream.available());
         // We can stop doing base64 encoding once our serialization into JSON
         // is guaranteed to handle all chars in strings, including embedded
         // nulls.
         entry.owner_b64 = btoa(String.fromCharCode.apply(null, ownerBytes));
       } catch (e) { dump(e); }
     }
 
-    if (aEntry.docIdentifier)
-      entry.docIdentifier = aEntry.docIdentifier;
+    entry.docIdentifier = aEntry.BFCacheEntry.ID;
 
-    if (aEntry.stateData)
-      entry.stateData = aEntry.stateData;
+    if (aEntry.stateData != null) {
+      entry.structuredCloneState = aEntry.stateData.getDataAsBase64();
+      entry.structuredCloneVersion = aEntry.stateData.formatVersion;
+    }
 
     if (!(aEntry instanceof Ci.nsISHContainer))
       return entry;
 
     if (aEntry.childCount > 0) {
       entry.children = [];
       for (let i = 0; i < aEntry.childCount; i++) {
         let child = aEntry.GetChildAt(i);
--- a/mobile/chrome/content/browser.js
+++ b/mobile/chrome/content/browser.js
@@ -350,20 +350,22 @@ var Browser = {
       // First open any commandline URLs, except the homepage
       if (commandURL && commandURL != this.getHomePage()) {
         this.addTab(commandURL, true);
       } else {
         bringFront = true;
         // Initial window resizes call functions that assume a tab is in the tab list
         // and restored tabs are added too late. We add a dummy to to satisfy the resize
         // code and then remove the dummy after the session has been restored.
-        let dummy = this.addTab("about:blank");
+        let dummy = this.addTab("about:blank", true);
         let dummyCleanup = {
-          observe: function() {
+          observe: function(aSubject, aTopic, aData) {
             Services.obs.removeObserver(dummyCleanup, "sessionstore-windows-restored");
+            if (aData == "fail")
+              Browser.addTab(commandURL || Browser.getHomePage(), true);
             dummy.chromeTab.ignoreUndo = true;
             Browser.closeTab(dummy, { forceClose: true });
           }
         };
         Services.obs.addObserver(dummyCleanup, "sessionstore-windows-restored", false);
       }
       ss.restoreLastSession(bringFront);
     } else {
--- a/mobile/chrome/content/content.js
+++ b/mobile/chrome/content/content.js
@@ -395,17 +395,17 @@ let Content = {
     let json = aMessage.json;
     let x = json.x;
     let y = json.y;
     let modifiers = json.modifiers;
 
     switch (aMessage.name) {
       case "Browser:ContextCommand": {
         let wrappedTarget = elementFromPoint(x, y);
-        if (!wrappedTarget)
+        if (!wrappedTarget || !(wrappedTarget instanceof Ci.nsIDOMNSEditableElement))
           break;
         let target = wrappedTarget.QueryInterface(Ci.nsIDOMNSEditableElement);
         if (!target)
           break;
         switch (json.command) {
           case "select-all":
             target.editor.selectAll();
             break;
--- a/mobile/components/SessionStore.js
+++ b/mobile/components/SessionStore.js
@@ -702,33 +702,35 @@ SessionStore.prototype = {
       throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG);
   },
 
   shouldRestore: function ss_shouldRestore() {
     return this._shouldRestore;
   },
 
   restoreLastSession: function ss_restoreLastSession(aBringToFront) {
-    // The previous session data has already been renamed to the backup file
-    if (!this._sessionFileBackup.exists())
-      return;
+    let self = this;
+    function notifyObservers(aMessage) {
+      self._clearCache();
+      Services.obs.notifyObservers(null, "sessionstore-windows-restored", aMessage || "");
+    }
 
-    let self = this;
-    function notifyObservers() {
-      self._clearCache();
-      Services.obs.notifyObservers(null, "sessionstore-windows-restored", "");
+    // The previous session data has already been renamed to the backup file
+    if (!this._sessionFileBackup.exists()) {
+      notifyObservers("fail")
+      return;
     }
 
     try {
       let channel = NetUtil.newChannel(this._sessionFileBackup);
       channel.contentType = "application/json";
       NetUtil.asyncFetch(channel, function(aStream, aResult) {
         if (!Components.isSuccessCode(aResult)) {
           Cu.reportError("SessionStore: Could not read from sessionstore.bak file");
-          notifyObservers();
+          notifyObservers("fail");
           return;
         }
 
         // Read session state file into a string and let observers modify the state before it's being used
         let state = Cc["@mozilla.org/supports-string;1"].createInstance(Ci.nsISupportsString);
         state.data = NetUtil.readInputStreamToString(aStream, aStream.available()) || "";
         aStream.close();
 
@@ -737,24 +739,27 @@ SessionStore.prototype = {
         let data = null;
         try {
           data = JSON.parse(state.data);
         } catch (ex) {
           Cu.reportError("SessionStore: Could not parse JSON: " + ex);
         }
 
         if (!data || data.windows.length == 0) {
-          notifyObservers();
+          notifyObservers("fail");
           return;
         }
 
         let window = Services.wm.getMostRecentWindow("navigator:browser");
 
+        let tabs = data.windows[0].tabs;
         let selected = data.windows[0].selected;
-        let tabs = data.windows[0].tabs;
+        if (selected > tabs.length) // Clamp the selected index if it's bogus
+          selected = 1;
+
         for (let i=0; i<tabs.length; i++) {
           let tabData = tabs[i];
 
           // Add a tab, but don't load the URL until we need to
           let params = { getAttention: false, delayLoad: true };
 
           // We must have selected tabs as soon as possible, so we let all tabs be selected
           // until we get the real selected tab. Then we stop selecting tabs. The end result
@@ -790,14 +795,14 @@ SessionStore.prototype = {
 
           tab.browser.__SS_extdata = tabData.extData;
         }
     
         notifyObservers();
       });
     } catch (ex) {
       Cu.reportError("SessionStore: Could not read from sessionstore.bak file: " + ex);
-      notifyObservers();
+      notifyObservers("fail");
     }
   }
 };
 
 const NSGetFactory = XPCOMUtils.generateNSGetFactory([SessionStore]);
--- a/mobile/themes/core/config.css
+++ b/mobile/themes/core/config.css
@@ -51,17 +51,17 @@ richlistitem .preferences-title {
   margin-right: 8px;
 }
 
 /* XXX look  + sync */
 richlistitem[default="false"] .preferences-title {
   font-weight: bold;
 }
 
-richlistitem .prefvalue {
+richlistitem .preferences-value {
   min-width: 200px;
   pointer-events: none;
   -moz-box-flex: 4;
   text-align: end;
   color: grey;
 }
 
 /* Editor */
--- a/security/manager/ssl/src/nsNSSIOLayer.cpp
+++ b/security/manager/ssl/src/nsNSSIOLayer.cpp
@@ -3334,16 +3334,83 @@ done:
 
 static SECStatus
 cancel_and_failure(nsNSSSocketInfo* infoObject)
 {
   infoObject->SetCanceled(PR_TRUE);
   return SECFailure;
 }
 
+class nsIsStsHostRunnable : public nsIRunnable
+{
+ public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIRUNNABLE
+
+  nsIsStsHostRunnable(const nsCOMPtr<nsIStrictTransportSecurityService> &stss)
+    : stss(stss), stsEnabled(PR_FALSE), nsrv(NS_ERROR_UNEXPECTED)
+  {}
+
+  nsXPIDLCString hostName;
+
+  nsresult GetResult(PRBool &b) const { b = stsEnabled; return nsrv; }
+
+ private:
+  nsCOMPtr<nsIStrictTransportSecurityService> stss;
+  PRBool stsEnabled;
+  nsresult nsrv;
+};
+
+NS_IMPL_THREADSAFE_ISUPPORTS1(nsIsStsHostRunnable,
+                              nsIRunnable)
+
+NS_IMETHODIMP nsIsStsHostRunnable::Run()
+{
+  nsrv = stss->IsStsHost(hostName, &stsEnabled);
+  return NS_OK;
+}
+
+class nsNotifyCertProblemRunnable : public nsIRunnable
+{
+ public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIRUNNABLE
+
+  nsNotifyCertProblemRunnable(nsIInterfaceRequestor *cb,
+                              nsIInterfaceRequestor *csi,
+                              nsSSLStatus* status,
+                              const nsCString &hostWithPortString)
+  : cb(cb),
+    csi(csi),
+    status(status),
+    hostWithPortString(hostWithPortString),
+    suppressMessage(PR_FALSE)
+  {}
+
+  PRBool GetSuppressMessage() { return suppressMessage; }
+
+ private:
+  nsIInterfaceRequestor* cb;
+  nsIInterfaceRequestor* csi;
+  nsSSLStatus* status;
+  const nsCString& hostWithPortString;
+  PRBool suppressMessage;
+};
+
+NS_IMPL_THREADSAFE_ISUPPORTS1(nsNotifyCertProblemRunnable,
+                              nsIRunnable)
+
+NS_IMETHODIMP nsNotifyCertProblemRunnable::Run()
+{
+  nsCOMPtr<nsIBadCertListener2> bcl = do_GetInterface(cb);
+  if (bcl)
+    bcl->NotifyCertProblem(csi, status, hostWithPortString, &suppressMessage);
+  return NS_OK;
+}
+
 static SECStatus
 nsNSSBadCertHandler(void *arg, PRFileDesc *sslSocket)
 {
   // cert was revoked, don't do anything else
   // Calling cancel_and_failure is not necessary, and would be wrong,
   // [for errors other than the ones explicitly handled below,] 
   // because it suppresses error reporting.
   if (PR_GetError() == SEC_ERROR_REVOKED_CERTIFICATE)
@@ -3517,31 +3584,35 @@ nsNSSBadCertHandler(void *arg, PRFileDes
   remaining_display_errors = collected_errors;
 
   // Enforce Strict-Transport-Security for hosts that are "STS" hosts:
   // connections must be dropped when there are any certificate errors
   // (STS Spec section 7.3).
 
   nsCOMPtr<nsIStrictTransportSecurityService> stss
     = do_GetService(NS_STSSERVICE_CONTRACTID);
-  nsCOMPtr<nsIStrictTransportSecurityService> proxied_stss;
-
-  nsrv = NS_GetProxyForObject(NS_PROXY_TO_MAIN_THREAD,
-                              NS_GET_IID(nsIStrictTransportSecurityService),
-                              stss, NS_PROXY_SYNC,
-                              getter_AddRefs(proxied_stss));
-  NS_ENSURE_SUCCESS(nsrv, SECFailure);
+
+  nsCOMPtr<nsIsStsHostRunnable> runnable(new nsIsStsHostRunnable(stss));
+  if (!runnable)
+    return SECFailure;
 
   // now grab the host name to pass to the STS Service
-  nsXPIDLCString hostName;
-  nsrv = infoObject->GetHostName(getter_Copies(hostName));
+  nsrv = infoObject->GetHostName(getter_Copies(runnable->hostName));
+  NS_ENSURE_SUCCESS(nsrv, SECFailure);
+
+  nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
+  if (!mainThread)
+    return SECFailure;
+
+  // Dispatch SYNC since the result is used below
+  nsrv = mainThread->Dispatch(runnable, NS_DISPATCH_SYNC);
   NS_ENSURE_SUCCESS(nsrv, SECFailure);
 
   PRBool strictTransportSecurityEnabled;
-  nsrv = proxied_stss->IsStsHost(hostName, &strictTransportSecurityEnabled);
+  nsrv = runnable->GetResult(strictTransportSecurityEnabled);
   NS_ENSURE_SUCCESS(nsrv, SECFailure);
 
   if (!strictTransportSecurityEnabled) {
     nsCOMPtr<nsICertOverrideService> overrideService =
       do_GetService(NS_CERTOVERRIDE_CONTRACTID);
     // it is fine to continue without the nsICertOverrideService
 
     PRUint32 overrideBits = 0;
@@ -3571,43 +3642,33 @@ nsNSSBadCertHandler(void *arg, PRFileDes
     PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("Strict-Transport-Security is violated: untrusted transport layer\n"));
   }
 
   // Ok, this is a full stop.
   // First, deliver the technical details of the broken SSL status,
   // giving the caller a chance to suppress the error messages.
 
   PRBool suppressMessage = PR_FALSE;
-  nsresult rv;
 
   // Try to get a nsIBadCertListener2 implementation from the socket consumer.
   nsCOMPtr<nsIInterfaceRequestor> cb;
   infoObject->GetNotificationCallbacks(getter_AddRefs(cb));
   if (cb) {
-    nsCOMPtr<nsIInterfaceRequestor> callbacks;
-    NS_GetProxyForObject(NS_PROXY_TO_MAIN_THREAD,
-                         NS_GET_IID(nsIInterfaceRequestor),
-                         cb,
-                         NS_PROXY_SYNC,
-                         getter_AddRefs(callbacks));
-
-    nsCOMPtr<nsIBadCertListener2> bcl = do_GetInterface(callbacks);
-    if (bcl) {
-      nsCOMPtr<nsIBadCertListener2> proxy_bcl;
-      NS_GetProxyForObject(NS_PROXY_TO_MAIN_THREAD,
-                           NS_GET_IID(nsIBadCertListener2),
-                           bcl,
-                           NS_PROXY_SYNC,
-                           getter_AddRefs(proxy_bcl));
-      if (proxy_bcl) {
-        nsIInterfaceRequestor *csi = static_cast<nsIInterfaceRequestor*>(infoObject);
-        rv = proxy_bcl->NotifyCertProblem(csi, status, hostWithPortString, 
-                                          &suppressMessage);
-      }
-    }
+    nsIInterfaceRequestor *csi = static_cast<nsIInterfaceRequestor*>(infoObject);
+
+    nsCOMPtr<nsNotifyCertProblemRunnable> runnable(
+        new nsNotifyCertProblemRunnable(cb, csi, status, hostWithPortString));
+    if (!runnable)
+      return SECFailure;
+
+    // Dispatch SYNC since the result is used below
+    nsrv = mainThread->Dispatch(runnable, NS_DISPATCH_SYNC);
+    NS_ENSURE_SUCCESS(nsrv, SECFailure);
+
+    suppressMessage = runnable->GetSuppressMessage();
   }
 
   nsCOMPtr<nsIRecentBadCertsService> recentBadCertsService = 
     do_GetService(NS_RECENTBADCERTS_CONTRACTID);
 
   if (recentBadCertsService) {
     recentBadCertsService->AddBadCert(hostWithPortStringUTF16, status);
   }
--- a/toolkit/components/aboutmemory/content/aboutMemory.js
+++ b/toolkit/components/aboutmemory/content/aboutMemory.js
@@ -130,86 +130,92 @@ function sendHeapMinNotifications()
     else
       runSoon(update);
   }
 
   var j = 0;
   sendHeapMinNotificationsInner();
 }
 
+function Reporter(aPath, aKind, aUnits, aAmount, aDescription)
+{
+  this._path        = aPath;
+  this._kind        = aKind;
+  this._units       = aUnits;
+  this._amount      = aAmount;
+  this._description = aDescription;
+  // this._nMerged is only defined if > 1
+  // this._done is defined when getBytes is called
+}
+
+Reporter.prototype = {
+  // Sum the values (accounting for possible kUnknown amounts), and mark |this|
+  // as a dup.  We mark dups because it's useful to know when a reporter is
+  // duplicated;  it might be worth investigating and splitting up to have
+  // non-duplicated names.
+  merge: function(r) {
+    if (this._amount !== kUnknown && r._amount !== kUnknown) {
+      this._amount += r._amount;
+    } else if (this._amount === kUnknown && r._amount !== kUnknown) {
+      this._amount = r._amount;
+    }
+    this._nMerged = this._nMerged ? this._nMerged + 1 : 2;
+  },
+
+  treeNameMatches: function(aTreeName) {
+    return this._path.slice(0, aTreeName.length) === aTreeName;
+  }
+};
+
 function getReportersByProcess()
 {
   var mgr = Cc["@mozilla.org/memory-reporter-manager;1"].
       getService(Ci.nsIMemoryReporterManager);
 
   // Process each memory reporter:
   // - Make a copy of it into a sub-table indexed by its process.  Each copy
-  //   looks like this:
-  //
-  //     interface Reporter {
-  //       _path:        string;
-  //       _kind:        number;
-  //       _units:       number;
-  //       _amount:      number;
-  //       _description: string;
-  //       _nMerged:     number;  (only defined if >= 2)
-  //     }
-  //
-  //   After this point we never use the original memory reporter again.
+  //   is a Reporter object.  After this point we never use the original memory
+  //   reporter again.
   //
   // - Note that copying rOrig.amount (which calls a C++ function under the
   //   IDL covers) to r._amount for every reporter now means that the
   //   results as consistent as possible -- measurements are made all at
   //   once before most of the memory required to generate this page is
   //   allocated.
   var reportersByProcess = {};
 
   function addReporter(aProcess, aPath, aKind, aUnits, aAmount, aDescription)
   {
     var process = aProcess === "" ? "Main" : aProcess;
-    var r = {
-      _path:        aPath,
-      _kind:        aKind,
-      _units:       aUnits,
-      _amount:      aAmount,
-      _description: aDescription
-    };
+    var r = new Reporter(aPath, aKind, aUnits, aAmount, aDescription);
     if (!reportersByProcess[process]) {
       reportersByProcess[process] = {};
     }
     var reporters = reportersByProcess[process];
     var reporter = reporters[r._path];
     if (reporter) {
-      // Already an entry;  must be a duplicated reporter.  This can
-      // happen legitimately.  Sum the values (accounting for possible kUnknown
-      // amounts), and mark the reporter as a dup.  We mark dups because it's
-      // useful to know when a reporter is duplicated;  it might be worth
-      // investigating and splitting up to have non-duplicated names.
-      if (reporter._amount !== kUnknown && r._amount !== kUnknown) {
-        reporter._amount += r._amount;
-      } else if (reporter._amount === kUnknown && r._amount !== kUnknown) {
-        reporter._amount = r._amount;
-      }
-      reporter._nMerged = reporter._nMerged ? reporter._nMerged + 1 : 2;
+      // Already an entry;  must be a duplicated reporter.  This can happen
+      // legitimately.  Merge them.
+      reporter.merge(r);
     } else {
       reporters[r._path] = r;
     }
   }
 
   // Process vanilla reporters first, then multi-reporters.
   var e = mgr.enumerateReporters();
   while (e.hasMoreElements()) {
     var rOrig = e.getNext().QueryInterface(Ci.nsIMemoryReporter);
     addReporter(rOrig.process, rOrig.path, rOrig.kind, rOrig.units,
                 rOrig.amount, rOrig.description);
   }
   var e = mgr.enumerateMultiReporters();
   while (e.hasMoreElements()) {
-    var r = e.getNext().QueryInterface(Ci.nsIMemoryMultiReporter);
-    r.collectReports(addReporter, null);
+    var mrOrig = e.getNext().QueryInterface(Ci.nsIMemoryMultiReporter);
+    mrOrig.collectReports(addReporter, null);
   }
 
   return reportersByProcess;
 }
 
 /**
  * Top-level function that does the work of generating the page.
  */
@@ -261,161 +267,148 @@ function update()
           "reporter to see a detailed description of what it measures.</span>"
           "</div>";
 
   var div = document.createElement("div");
   div.innerHTML = text;
   content.appendChild(div);
 }
 
-// Compare two 'explicit' memory reporter nodes.
-function cmpExplicitReporters(a, b)
+// There are two kinds of TreeNode.  Those that correspond to Reporters
+// have more properties.  The remainder are just scaffolding nodes for the
+// tree, whose values are derived from their children.
+function TreeNode(aName)
 {
-  assert(a._units === undefined && b._units === undefined,
-         "'explicit' tree nodes must not have _units defined");
-  return b._amount - a._amount;
+  // Nb: _units is not needed, it's always UNITS_BYTES.
+  this._name = aName;
+  this._kids = [];
+  // All TreeNodes have these properties added later:
+  // - _amount (which is never |kUnknown|)
+  // - _description
+  //
+  // TreeNodes corresponding to Reporters have these properties added later:
+  // - _kind
+  // - _nMerged (if > 1)
+  // - _hasProblem (only defined if true)
+}
+
+TreeNode.prototype = {
+  findKid: function(aName) {
+    for (var i = 0; i < this._kids.length; i++) {
+      if (this._kids[i]._name === aName) {
+        return this._kids[i];
+      }
+    }
+    return undefined;
+  },
+
+  toString: function() {
+    return formatBytes(this._amount);
+  }
 };
 
-// Compare two memory reporter nodes from the 'other measurements' list.
-function cmpOtherReporters(a, b)
-{
-  return a._path < b._path ? -1 :
-         a._path > b._path ?  1 :
-         0;
+TreeNode.compare = function(a, b) {
+  return b._amount - a._amount;
 };
 
-function findKid(aName, aKids)
-{
-  for (var i = 0; i < aKids.length; i++) {
-    if (aKids[i]._name === aName) {
-      return aKids[i];
-    }
-  }
-  return undefined;
-}
-
 /**
  * From a list of memory reporters, builds a tree that mirrors the tree
  * structure that will be shown as output.
  *
  * @param aReporters
- *        The list of memory reporters.
- * @return The built tree.  The tree nodes have this structure:
- *         interface Node {
- *           _name:        string;
- *           _kind:        number;
- *           _amount:      number;    (non-negative or 'kUnknown')
- *           _description: string;
- *           _kids:        [Node];
- *           _hasReporter: boolean;   (only defined if 'true')
- *           _hasProblem:  boolean;   (only defined if 'true')
- *           _nMerged:     number;    (only defined if >= 2)
- *         }
- * _units isn't needed because it's always UNITS_BYTES for 'explicit'
- * reporters.
+ *        The table of Reporters, indexed by path.
+ * @return The built tree.
  */
 function buildTree(aReporters)
 {
   const treeName = "explicit";
 
-  // We want to process all reporters that begin with 'treeName'.  First we
-  // build the tree but only fill in '_name', '_kind', '_kids', maybe
-  // '_hasReporter' and maybe '_nMerged'.  This is done top-down from the
-  // reporters.
-  var t = {
-    _name: "falseRoot",
-    _kind: KIND_OTHER,
-    _kids: []
-  };
+  // We want to process all reporters that begin with |treeName|.  First we
+  // build the tree but only fill the properties that we can with a top-down
+  // traversal.
+  var t = new TreeNode("falseRoot");
   for (var path in aReporters) {
+    // Add any missing nodes in the tree implied by the path.
     var r = aReporters[path];
-    if (r._path.slice(0, treeName.length) === treeName) {
+    if (r.treeNameMatches(treeName)) {
       assert(r._kind === KIND_HEAP || r._kind === KIND_NONHEAP,
              "reporters in the tree must have KIND_HEAP or KIND_NONHEAP");
       assert(r._units === UNITS_BYTES);
       var names = r._path.split('/');
       var u = t;
       for (var i = 0; i < names.length; i++) {
         var name = names[i];
-        var uMatch = findKid(name, u._kids);
+        var uMatch = u.findKid(name);
         if (uMatch) {
           u = uMatch;
         } else {
-          var v = {
-            _name: name,
-            _kind: KIND_OTHER,
-            _kids: []
-          };
+          var v = new TreeNode(name);
           u._kids.push(v);
           u = v;
         }
       }
+      // Fill in extra details from the Reporter.
       u._kind = r._kind;
-      u._hasReporter = true;
       if (r._nMerged) {
         u._nMerged = r._nMerged;
       }
     }
   }
   // Using falseRoot makes the above code simpler.  Now discard it, leaving
   // treeName at the root.
   t = t._kids[0];
 
-  // Next, fill in '_description' and '_amount', and maybe '_hasProblem'
-  // for each node.  This is done bottom-up because for most non-leaf nodes
-  // '_amount' and '_description' are determined from the child nodes.
+  // Next, fill in the remaining properties bottom-up.
+  // Note that this function never returns kUnknown.
   function fillInTree(aT, aPrepath)
   {
     var path = aPrepath ? aPrepath + '/' + aT._name : aT._name;
     if (aT._kids.length === 0) {
       // Leaf node.  Must have a reporter.
+      assert(aT._kind !== undefined);
       aT._description = getDescription(aReporters, path);
       var amount = getBytes(aReporters, path);
       if (amount !== kUnknown) {
         aT._amount = amount;
       } else {
         aT._amount = 0;
         aT._hasProblem = true;
       }
     } else {
       // Non-leaf node.  Get the size of the children.
       var childrenBytes = 0;
       for (var i = 0; i < aT._kids.length; i++) {
         // Allow for kUnknown, treat it like 0.
-        var b = fillInTree(aT._kids[i], path);
-        childrenBytes += (b === kUnknown ? 0 : b);
+        childrenBytes += fillInTree(aT._kids[i], path);
       }
-      if (aT._hasReporter === true) {
+      if (aT._kind !== undefined) {
         aT._description = getDescription(aReporters, path);
         var amount = getBytes(aReporters, path);
         if (amount !== kUnknown) {
           // Non-leaf node with its own reporter.  Use the reporter and add
           // an "other" child node.
           aT._amount = amount;
-          var other = {
-            _name: "other",
-            _kind: KIND_OTHER,
-            _description: "All unclassified " + aT._name + " memory.",
-            _amount: aT._amount - childrenBytes,
-            _kids: []
-          };
+          var other = new TreeNode("other");
+          other._description = "All unclassified " + aT._name + " memory.",
+          other._amount = aT._amount - childrenBytes,
           aT._kids.push(other);
         } else {
           // Non-leaf node with a reporter that returns kUnknown.
           // Use the sum of the children and mark it as problematic.
           aT._amount = childrenBytes;
           aT._hasProblem = true;
         }
       } else {
         // Non-leaf node without its own reporter.  Derive its size and
         // description entirely from its children.
         aT._amount = childrenBytes;
         aT._description = "The sum of all entries below '" + aT._name + "'.";
       }
     }
+    assert(aT._amount !== kUnknown);
     return aT._amount;
   }
   fillInTree(t, "");
 
   // Determine how many bytes are reported by heap reporters.  Be careful
   // with non-leaf reporters;  if we count a non-leaf reporter we don't want
   // to count any of its child reporters.
   var s = "";
@@ -437,139 +430,117 @@ function buildTree(aReporters)
   // in the "Other Measurements" list.
   var heapUsedBytes = getBytes(aReporters, "heap-allocated", true);
   var unknownHeapUsedBytes = 0;
   var hasProblem = true;
   if (heapUsedBytes !== kUnknown) {
     unknownHeapUsedBytes = heapUsedBytes - getKnownHeapUsedBytes(t);
     hasProblem = false;
   }
-  var heapUnclassified = {
-    _name: "heap-unclassified",
-    _kind: KIND_HEAP,
-    _description:
+  var heapUnclassified = new TreeNode("heap-unclassified");
+  // This kindToString() ensures the "(Heap)" prefix is set without having to
+  // set the _kind property, which would mean that there is a corresponding
+  // Reporter for this TreeNode (which isn't true).
+  heapUnclassified._description =
+      kindToString(KIND_HEAP) +
       "Memory not classified by a more specific reporter. This includes " +
       "waste due to internal fragmentation in the heap allocator (caused " +
-      "when the allocator rounds up request sizes).",
-    _amount: unknownHeapUsedBytes,
-    _hasProblem: hasProblem,
-    _kids: []
+      "when the allocator rounds up request sizes).";
+  heapUnclassified._amount = unknownHeapUsedBytes;
+  if (hasProblem) {
+    heapUnclassified._hasProblem = true;
   }
+
   t._kids.push(heapUnclassified);
   t._amount += unknownHeapUsedBytes;
 
   return t;
 }
 
 /**
- * Sort all kid nodes from largest to smallest and aggregate
- * insignificant nodes.
+ * Sort all kid nodes from largest to smallest and aggregate insignificant
+ * nodes.
  *
  * @param aTotalBytes
- *        The size of the tree's root node
+ *        The size of the tree's root node.
  * @param aT
- *        The tree
+ *        The tree.
  */
 function filterTree(aTotalBytes, aT)
 {
   const omitThresholdPerc = 0.5; /* percent */
 
   function shouldOmit(aBytes)
   {
     return !gVerbose &&
            aTotalBytes !== kUnknown &&
            (100 * aBytes / aTotalBytes) < omitThresholdPerc;
   }
 
-  aT._kids.sort(cmpExplicitReporters);
+  aT._kids.sort(TreeNode.compare);
 
   for (var i = 0; i < aT._kids.length; i++) {
     if (shouldOmit(aT._kids[i]._amount)) {
       // This sub-tree is below the significance threshold
       // Remove it and all remaining (smaller) sub-trees, and
       // replace them with a single aggregate node.
       var i0 = i;
       var aggBytes = 0;
       for ( ; i < aT._kids.length; i++) {
         aggBytes += aT._kids[i]._amount;
       }
       aT._kids.splice(i0);
       var n = i - i0;
-      var rSub = {
-        _name: "(" + n + " omitted)",
-        _kind: KIND_OTHER,
-        _amount: aggBytes,
-        _description: n + " sub-trees that were below the " + 
-                      omitThresholdPerc + "% significance threshold.  " +
-                      "Click 'More verbose' at the bottom of this page " +
-                      "to see them.",
-        _kids: []
-      };
-      // Add the "omitted" sub-tree at the end and then resort, because the
-      // sum of the omitted sub-trees may be larger than some of the
-      // shown sub-trees.
+      var rSub = new TreeNode("(" + n + " omitted)");
+      rSub._amount = aggBytes;
+      rSub._description =
+        n + " sub-trees that were below the " + omitThresholdPerc +
+        "% significance threshold.  Click 'More verbose' at the bottom of " +
+        "this page to see them.";
+
+      // Add the "omitted" sub-tree at the end and then re-sort, because the
+      // sum of the omitted sub-trees may be larger than some of the shown
+      // sub-trees.
       aT._kids[i0] = rSub;
-      aT._kids.sort(cmpExplicitReporters);
+      aT._kids.sort(TreeNode.compare);
       break;
     }
     filterTree(aTotalBytes, aT._kids[i]);
   }
 }
 
 /**
  * Generates the text for a single process.
  *
  * @param aProcess
- *        The name of the process
+ *        The name of the process.
  * @param aReporters
- *        Table of reporters for this process, indexed by _path
- * @return The generated text
+ *        Table of Reporters for this process, indexed by _path.
+ * @return The generated text.
  */
 function genProcessText(aProcess, aReporters)
 {
   var tree = buildTree(aReporters);
   filterTree(tree._amount, tree);
 
   // Nb: the newlines give nice spacing if we cut+paste into a text buffer.
   var text = "";
   text += "<h1>" + aProcess + " Process</h1>\n\n";
   text += genTreeText(tree);
   text += genOtherText(aReporters);
   text += "<hr></hr>";
   return text;
 }
 
 /**
- * Returns the reporter's amount formatted as a human-readable string (with
- * units, if applicable).
- *
- * @param aReporter
- *        The reporter whose usage we're formatting
- * @return The reporter's amount formatted as a human-readable string
- */
-function formatReporterAmount(aReporter)
-{
-  switch(aReporter._units) {
-    case UNITS_BYTES:
-      return formatBytes(aReporter._amount);
-    case UNITS_COUNT:
-    case UNITS_COUNT_CUMULATIVE:
-      return formatInt(aReporter._amount);
-    case UNITS_PERCENTAGE:
-      return formatPercentage(aReporter._amount);
-    default: return "(???)"
-  }
-}
-
-/**
  * Formats an int as a human-readable string.
  *
  * @param aN
- *        The integer to format
- * @return A human-readable string representing the int
+ *        The integer to format.
+ * @return A human-readable string representing the int.
  */
 function formatInt(aN)
 {
   var neg = false;
   if (aN < 0) {
     neg = true;
     aN = -aN;
   }
@@ -592,18 +563,18 @@ function formatInt(aN)
   }
   return neg ? "-" + s : s;
 }
 
 /**
  * Converts a byte count to an appropriate string representation.
  *
  * @param aBytes
- *        The byte count
- * @return The string representation
+ *        The byte count.
+ * @return The string representation.
  */
 function formatBytes(aBytes)
 {
   var unit = gVerbose ? "B" : "MB";
 
   var s;
   if (gVerbose) {
     s = formatInt(aBytes) + " " + unit;
@@ -614,100 +585,96 @@ function formatBytes(aBytes)
   }
   return s;
 }
 
 /**
  * Converts a percentage to an appropriate string representation.
  *
  * @param aPerc100x
- *        The percentage, multiplied by 100 (see nsIMemoryReporter)
+ *        The percentage, multiplied by 100 (see nsIMemoryReporter).
  * @return The string representation
  */
 function formatPercentage(aPerc100x)
 {
   return (aPerc100x / 100).toFixed(2) + "%";
 }
 
 /**
- * Right-justifies a string in a field of a given width, padding as necessary
+ * Right-justifies a string in a field of a given width, padding as necessary.
  *
  * @param aS
- *        The string
+ *        The string.
  * @param aN
- *        The field width
+ *        The field width.
  * @param aC
- *        The char used to pad
- * @return The string representation
+ *        The char used to pad.
+ * @return The string representation.
  */
 function pad(aS, aN, aC)
 {
   var padding = "";
   var n2 = aN - aS.length;
   for (var i = 0; i < n2; i++) {
     padding += aC;
   }
   return padding + aS;
 }
 
 /**
- * Gets the byte count for a particular memory reporter and sets its _done
+ * Gets the byte count for a particular Reporter and sets its _done
  * property.
  *
  * @param aReporters
- *        Table of reporters for this process, indexed by _path
+ *        Table of Reporters for this process, indexed by _path.
  * @param aPath
- *        The path of the memory reporter
+ *        The path of the R.
  * @param aDoNotMark
- *        If set, the _done property is not set.
- * @return The byte count
+ *        If true, the _done property is not set.
+ * @return The byte count.
  */
 function getBytes(aReporters, aPath, aDoNotMark)
 {
   var r = aReporters[aPath];
-  if (r) {
-    var bytes = r._amount;
-    if (!aDoNotMark) {
-      r._done = true;
-    }
-    return bytes;
+  assert(r, "getBytes: no such Reporter: " + aPath);
+  if (!aDoNotMark) {
+    r._done = true;
   }
-  // Nb: this should never occur; all paths have been extracted from
-  // the original list of reporters and so the lookup should succeed.  Return
-  // an obviously wrong number that will likely be noticed.
-  return -2 * 1024 * 1024;
+  return r._amount;
 }
 
 /**
- * Gets the description for a particular memory reporter.
+ * Gets the description for a particular Reporter.
  *
  * @param aReporters
- *        Table of reporters for this process, indexed by _path
+ *        Table of Reporters for this process, indexed by _path.
  * @param aPath
- *        The path of the memory reporter
- * @return The description
+ *        The path of the Reporter.
+ * @return The description.
  */
 function getDescription(aReporters, aPath)
 {
   var r = aReporters[aPath];
-  return r ? r._description : "???";
+  assert(r, "getDescription: no such Reporter: " + aPath);
+  return r._description;
 }
 
 function genMrValueText(aValue)
 {
   return "<span class='mrValue'>" + aValue + "</span>";
 }
 
 function kindToString(aKind)
 {
   switch (aKind) {
    case KIND_NONHEAP: return "(Non-heap) ";
    case KIND_HEAP:    return "(Heap) ";
-   case KIND_OTHER:   return "";
-   default:           return "(???) ";
+   case KIND_OTHER:
+   case undefined:    return "";
+   default:           assert(false, "bad kind in kindToString");
   }
 }
 
 function escapeQuotes(aStr)
 {
   return aStr.replace(/\&/g, '&amp;').replace(/'/g, '&#39;');
 }
 
@@ -765,39 +732,39 @@ function genMrNameText(aKind, aDesc, aNa
   }
   return text + '\n';
 }
 
 /**
  * Generates the text for the tree, including its heading.
  *
  * @param aT
- *        The tree
- * @return The generated text
+ *        The tree.
+ * @return The generated text.
  */
 function genTreeText(aT)
 {
   var treeBytes = aT._amount;
-  var treeBytesLength = formatBytes(treeBytes).length;
+  var rootStringLength = aT.toString().length;
 
   /**
    * Generates the text for a particular tree, without a heading.
    *
    * @param aT
-   *        The tree
+   *        The tree.
    * @param aIndentGuide
    *        Records what indentation is required for this tree.  It has one
    *        entry per level of indentation.  For each entry, ._isLastKid
    *        records whether the node in question is the last child, and
    *        ._depth records how many chars of indentation are required.
-   * @param aParentBytesLength
-   *        The length of the formatted byte count of the top node in the tree
-   * @return The generated text
+   * @param aParentStringLength
+   *        The length of the formatted byte count of the top node in the tree.
+   * @return The generated text.
    */
-  function genTreeText2(aT, aIndentGuide, aParentBytesLength)
+  function genTreeText2(aT, aIndentGuide, aParentStringLength)
   {
     function repeatStr(aC, aN)
     {
       var s = "";
       for (var i = 0; i < aN; i++) {
         s += aC;
       }
       return s;
@@ -819,19 +786,18 @@ function genTreeText(aT)
         indent += repeatStr(" ", aIndentGuide[i]._depth - 1);
       }
       indent += aIndentGuide[i]._isLastKid ? kUpAndRight : kVerticalAndRight;
       indent += repeatStr(kHorizontal, aIndentGuide[i]._depth - 1);
     }
 
     // Indent more if this entry is narrower than its parent, and update
     // aIndentGuide accordingly.
-    var tMemoryUsedStr = formatBytes(aT._amount);
-    var tBytesLength = tMemoryUsedStr.length;
-    var extraIndentLength = Math.max(aParentBytesLength - tBytesLength, 0);
+    var tString = aT.toString();
+    var extraIndentLength = Math.max(aParentStringLength - tString.length, 0);
     if (extraIndentLength > 0) {
       for (var i = 0; i < extraIndentLength; i++) {
         indent += kHorizontal;
       }
       aIndentGuide[aIndentGuide.length - 1]._depth += extraIndentLength;
     }
     indent += "</span>";
 
@@ -840,30 +806,30 @@ function genTreeText(aT)
     if (aT._amount === treeBytes) {
       perc = "100.0";
     } else {
       perc = (100 * aT._amount / treeBytes).toFixed(2);
       perc = pad(perc, 5, '0');
     }
     perc = "<span class='mrPerc'>(" + perc + "%)</span> ";
 
-    var text = indent + genMrValueText(tMemoryUsedStr) + " " + perc +
+    var text = indent + genMrValueText(tString) + " " + perc +
                genMrNameText(aT._kind, aT._description, aT._name,
                              aT._hasProblem, aT._nMerged);
 
     for (var i = 0; i < aT._kids.length; i++) {
       // 3 is the standard depth, the callee adjusts it if necessary.
       aIndentGuide.push({ _isLastKid: (i === aT._kids.length - 1), _depth: 3 });
-      text += genTreeText2(aT._kids[i], aIndentGuide, tBytesLength);
+      text += genTreeText2(aT._kids[i], aIndentGuide, tString.length);
       aIndentGuide.pop();
     }
     return text;
   }
 
-  var text = genTreeText2(aT, [], treeBytesLength);
+  var text = genTreeText2(aT, [], rootStringLength);
   // Nb: the newlines give nice spacing if we cut+paste into a text buffer.
   const desc =
     "This tree covers explicit memory allocations by the application, " +
     "both at the operating system level (via calls to functions such as " +
     "VirtualAlloc, vm_allocate, and mmap), and at the heap allocation level " +
     "(via functions such as malloc, calloc, realloc, memalign, operator " +
     "new, and operator new[]).  It excludes memory that is mapped implicitly " +
     "such as code and data segments, and thread stacks.  It also excludes " +
@@ -872,65 +838,89 @@ function genTreeText(aT)
     "explicit allocation, but it does cover most (including the entire " +
     "heap), and therefore it is the single best number to focus on when " +
     "trying to reduce memory usage.";
                
   return "<h2 class='hasDesc' title='" + escapeQuotes(desc) +
          "'>Explicit Allocations</h2>\n" + "<pre>" + text + "</pre>\n";
 }
 
+function OtherReporter(aPath, aUnits, aAmount, aDescription, 
+                       aNMerged)
+{
+  // Nb: _kind is not needed, it's always KIND_OTHER.
+  this._path        = aPath;
+  this._units       = aUnits;
+  if (aAmount === kUnknown) {
+    this._amount     = 0;
+    this._hasProblem = true;
+  } else {
+    this._amount = aAmount;
+  }
+  this._description = aDescription;
+  this.asString = this.toString();
+}
+
+OtherReporter.prototype = {
+  toString: function() {
+    switch(this._units) {
+      case UNITS_BYTES:            return formatBytes(this._amount);
+      case UNITS_COUNT:
+      case UNITS_COUNT_CUMULATIVE: return formatInt(this._amount);
+      case UNITS_PERCENTAGE:       return formatPercentage(this._amount);
+      default:
+        assert(false, "bad units in OtherReporter.toString");
+    }
+  }
+};
+
+OtherReporter.compare = function(a, b) {
+  return a._path < b._path ? -1 :
+         a._path > b._path ?  1 :
+         0;
+};
+
 /**
  * Generates the text for the "Other Measurements" section.
  *
- * @param aReporters
- *        Table of reporters for this process, indexed by _path
- * @return The generated text
+ * @param aReportersByProcess
+ *        Table of Reporters for this process, indexed by _path.
+ * @return The generated text.
  */
-function genOtherText(aReporters)
+function genOtherText(aReportersByProcess)
 {
   // Generate an array of Reporter-like elements, stripping out all the
-  // reporters that have already been handled.  Also find the width of the
+  // Reporters that have already been handled.  Also find the width of the
   // widest element, so we can format things nicely.
-  var maxAmountLength = 0;
-  var rArray = [];
-  for (var path in aReporters) {
-    var r = aReporters[path];
+  var maxStringLength = 0;
+  var otherReporters = [];
+  for (var path in aReportersByProcess) {
+    var r = aReportersByProcess[path];
     if (!r._done) {
+      assert(r._kind === KIND_OTHER, "_kind !== KIND_OTHER for " + r._path);
+      assert(r.nMerged === undefined);  // we don't allow dup'd OTHER reporters 
       var hasProblem = false;
       if (r._amount === kUnknown) {
         hasProblem = true;
       }
-      var elem = {
-        _path:        r._path,
-        _kind:        r._kind,
-        _units:       r._units,
-        _amount:      hasProblem ? 0 : r._amount,
-        _description: r._description,
-        _hasProblem:  hasProblem,
-        _nMerged:     r._nMerged
-      };
-      rArray.push(elem);
-      var thisAmountLength = formatReporterAmount(elem).length;
-      if (thisAmountLength > maxAmountLength) {
-        maxAmountLength = thisAmountLength;
+      var o = new OtherReporter(r._path, r._units, r._amount, r._description);
+      otherReporters.push(o);
+      if (o.asString.length > maxStringLength) {
+        maxStringLength = o.asString.length;
       }
     }
   }
-  rArray.sort(cmpOtherReporters);
+  otherReporters.sort(OtherReporter.compare);
 
   // Generate text for the not-yet-printed values.
   var text = "";
-  for (var i = 0; i < rArray.length; i++) {
-    var elem = rArray[i];
-    assert(elem._kind === KIND_OTHER,
-           "elem._kind is not KIND_OTHER for " + elem._path);
-    text += genMrValueText(
-              pad(formatReporterAmount(elem), maxAmountLength, ' ')) + " ";
-    text += genMrNameText(elem._kind, elem._description, elem._path,
-                          elem._hasProblem, elem._nMerged);
+  for (var i = 0; i < otherReporters.length; i++) {
+    var o = otherReporters[i];
+    text += genMrValueText(pad(o.asString, maxStringLength, ' ')) + " ";
+    text += genMrNameText(KIND_OTHER, o._description, o._path, o._hasProblem);
   }
 
   // Nb: the newlines give nice spacing if we cut+paste into a text buffer.
   const desc = "This list contains other memory measurements that cross-cut " +
                "the requested memory measurements above."
   return "<h2 class='hasDesc' title='" + desc + "'>Other Measurements</h2>\n" +
          "<pre>" + text + "</pre>\n";
 }
--- a/toolkit/components/aboutmemory/tests/chrome/test_aboutmemory.xul
+++ b/toolkit/components/aboutmemory/tests/chrome/test_aboutmemory.xul
@@ -80,18 +80,17 @@
     f("", "explicit/b/c/a",     HEAP,     70 * MB),
     f("", "explicit/b/c/b",     HEAP,      2 * MB), // omitted
     f("", "explicit/c",         NONHEAP, 100 * MB),
     f("", "explicit/c/d",       NONHEAP,  13 * MB), // subsumed by parent
     f("", "explicit/g",         HEAP,      1 * MB), // internal, dup: merge
     f("", "explicit/g/a",       HEAP,      6 * MB),
     f("", "explicit/g/b",       HEAP,      5 * MB),
     f("", "other1",             OTHER,   111 * MB),
-    f2("", "other4",            OTHER,   COUNT_CUMULATIVE, 888),
-    f2("", "unknown-unit",      OTHER,   /*bogus unit*/999, 999)
+    f2("", "other4",            OTHER,   COUNT_CUMULATIVE, 888)
   ];
   var fakeMultiReporters = [
      { collectReports: function(cbObj, closure) {
           function f(p, k, u, a) { cbObj.callback("", p, k, u, a, "(desc)", closure); }
           f("explicit/c/d",     NONHEAP, BYTES,  10 * MB), // dup, subsumed by parent
           f("explicit/cc",      NONHEAP, BYTES,  13 * MB);
           f("explicit/cc",      NONHEAP, BYTES,  10 * MB); // dup
           f("explicit/d",       NONHEAP, BYTES, 499 * KB); // omitted
@@ -198,17 +197,16 @@ Other Measurements\n\
 500.00 MB -- heap-allocated\n\
 100.00 MB -- heap-unallocated\n\
 111.00 MB -- other1\n\
 222.00 MB -- other2\n\
       777 -- other3\n\
       888 -- other4\n\
    45.67% -- perc1\n\
   100.00% -- perc2\n\
-    (???) -- unknown-unit\n\
 \n\
 2nd Process\n\
 \n\
 Explicit Allocations\n\
 1,000.00 MB (100.0%) -- explicit\n\
 ├────499.00 MB (49.90%) -- a\n\
 │    └──499.00 MB (49.90%) -- b\n\
 │       └──499.00 MB (49.90%) -- c [3]\n\
@@ -271,17 +269,16 @@ Other Measurements\n\
 524,288,000 B -- heap-allocated\n\
 104,857,600 B -- heap-unallocated\n\
 116,391,936 B -- other1\n\
 232,783,872 B -- other2\n\
           777 -- other3\n\
           888 -- other4\n\
        45.67% -- perc1\n\
       100.00% -- perc2\n\
-        (???) -- unknown-unit\n\
 \n\
 2nd Process\n\
 \n\
 Explicit Allocations\n\
 1,048,576,000 B (100.0%) -- explicit\n\
 ├────523,239,424 B (49.90%) -- a\n\
 │    └──523,239,424 B (49.90%) -- b\n\
 │       └──523,239,424 B (49.90%) -- c [3]\n\
--- a/toolkit/components/ctypes/tests/jsctypes-test.cpp
+++ b/toolkit/components/ctypes/tests/jsctypes-test.cpp
@@ -115,31 +115,31 @@ sum_many_##name##_##suffix(             
                                      j, k, l, m, n, o, p, q, r);               \
 }
 
 #define ABI /* cdecl */
 #define DEFINE_TYPE(x, y, z) FUNCTION_TESTS(x, y, z, cdecl)
 #include "typedefs.h"
 #undef ABI
 
-#if defined(_WIN32) && !defined(_WIN64)
+#if defined(_WIN32)
 
 void NS_STDCALL
 test_void_t_stdcall()
 {
   // do nothing
   return;
 }
 
 #define ABI NS_STDCALL
 #define DEFINE_TYPE(x, y, z) FUNCTION_TESTS(x, y, z, stdcall)
 #include "typedefs.h"
 #undef ABI
 
-#endif /* defined(_WIN32) && !defined(_WIN64) */
+#endif /* defined(_WIN32) */
 
 #define DEFINE_TYPE(name, type, ffiType)                                       \
 struct align_##name {                                                          \
   char x;                                                                      \
   type y;                                                                      \
 };                                                                             \
 struct nested_##name {                                                         \
   char a;                                                                      \
@@ -316,23 +316,23 @@ test_fnptr()
 }
 
 PRInt32
 test_closure_cdecl(PRInt8 i, test_func_ptr f)
 {
   return f(i);
 }
 
-#if defined(_WIN32) && !defined(_WIN64)
+#if defined(_WIN32)
 PRInt32
 test_closure_stdcall(PRInt8 i, test_func_ptr_stdcall f)
 {
   return f(i);
 }
-#endif /* defined(_WIN32) && !defined(_WIN64) */
+#endif /* defined(_WIN32) */
 
 template <typename T> struct PromotedTraits {
   typedef T type;
 };
 #define DECL_PROMOTED(FROM, TO)                 \
   template <> struct PromotedTraits<FROM> {     \
     typedef TO type;                            \
   }
--- a/toolkit/components/ctypes/tests/jsctypes-test.h
+++ b/toolkit/components/ctypes/tests/jsctypes-test.h
@@ -64,17 +64,17 @@ NS_EXTERN_C
     type, type, type, type, type, type, type, type, type);                     \
                                                                                \
   EXPORT_CDECL(void) get_##name##_stats(size_t* align, size_t* size,           \
                                         size_t* nalign, size_t* nsize,         \
                                         size_t offsets[]);
 
 #include "typedefs.h"
 
-#if defined(_WIN32) && !defined(_WIN64)
+#if defined(_WIN32)
   EXPORT_STDCALL(void) test_void_t_stdcall();
 
   EXPORT_STDCALL(void*) get_voidptr_t_stdcall();
   EXPORT_STDCALL(void*) set_voidptr_t_stdcall(void*);
 
 #define DEFINE_TYPE(name, type, ffiType)                                       \
   EXPORT_STDCALL(type) get_##name##_stdcall();                                 \
   EXPORT_STDCALL(type) set_##name##_stdcall(type);                             \
@@ -84,17 +84,17 @@ NS_EXTERN_C
   EXPORT_STDCALL(type) sum_alignf_##name##_stdcall(                            \
     float, type, float, type, float);                                          \
   EXPORT_STDCALL(type) sum_many_##name##_stdcall(                              \
     type, type, type, type, type, type, type, type, type,                      \
     type, type, type, type, type, type, type, type, type);
 
 #include "typedefs.h"
 
-#endif /* defined(_WIN32) && !defined(_WIN64) */
+#endif /* defined(_WIN32) */
 
   NS_EXPORT PRInt32 test_ansi_len(const char*);
   NS_EXPORT PRInt32 test_wide_len(const PRUnichar*);
   NS_EXPORT const char* test_ansi_ret();
   NS_EXPORT const PRUnichar* test_wide_ret();
   NS_EXPORT char* test_ansi_echo(const char*);
 
   struct ONE_BYTE {
@@ -185,20 +185,20 @@ NS_EXTERN_C
   NS_EXPORT FIVE_BYTE test_5_byte_struct_return(RECT);
   NS_EXPORT SIX_BYTE test_6_byte_struct_return(RECT);
   NS_EXPORT SEVEN_BYTE test_7_byte_struct_return(RECT);
 
   NS_EXPORT void * test_fnptr();
 
   typedef PRInt32 (* test_func_ptr)(PRInt8);
   NS_EXPORT PRInt32 test_closure_cdecl(PRInt8, test_func_ptr);
-#if defined(_WIN32) && !defined(_WIN64)
+#if defined(_WIN32)
   typedef PRInt32 (NS_STDCALL * test_func_ptr_stdcall)(PRInt8);
   NS_EXPORT PRInt32 test_closure_stdcall(PRInt8, test_func_ptr_stdcall);
-#endif /* defined(_WIN32) && !defined(_WIN64) */
+#endif /* defined(_WIN32) */
 
   NS_EXPORT PRInt32 test_callme(PRInt8);
   NS_EXPORT void* test_getfn();
 
   EXPORT_CDECL(PRInt32) test_sum_va_cdecl(PRUint8 n, ...);
   EXPORT_CDECL(PRUint8) test_count_true_va_cdecl(PRUint8 n, ...);
   EXPORT_CDECL(void) test_add_char_short_int_va_cdecl(PRUint32* result, ...);
   EXPORT_CDECL(PRInt32*) test_vector_add_va_cdecl(PRUint8 num_vecs,
--- a/toolkit/components/ctypes/tests/unit/test_jsctypes.js.in
+++ b/toolkit/components/ctypes/tests/unit/test_jsctypes.js.in
@@ -676,33 +676,31 @@ function run_basic_abi_tests(library, t,
   // Test the function call ABI for calls involving the type.
   function declare_fn_cdecl(fn_t, prefix) {
     return library.declare(prefix + name + "_cdecl", fn_t);
   }
   run_single_abi_tests(declare_fn_cdecl, ctypes.default_abi, t,
     toprimitive, get_test, set_tests, sum_tests, sum_many_tests);
 
 #ifdef WIN32
-#ifndef HAVE_64BIT_OS
   function declare_fn_stdcall(fn_t, prefix) {
     return library.declare(prefix + name + "_stdcall", fn_t);
   }
   run_single_abi_tests(declare_fn_stdcall, ctypes.stdcall_abi, t,
     toprimitive, get_test, set_tests, sum_tests, sum_many_tests);
 
   // Check that declaring a WINAPI function gets the right symbol name.
   let libuser32 = ctypes.open("user32.dll");
   let charupper = libuser32.declare("CharUpperA",
                                     ctypes.winapi_abi,
                                     ctypes.char.ptr,
                                     ctypes.char.ptr);
   let hello = ctypes.char.array()("hello!");
   do_check_eq(charupper(hello).readString(), "HELLO!");
 #endif
-#endif
 
   // Check the alignment of the type, and its behavior in a struct,
   // against what C says.
   check_struct_stats(library, t);
 }
 
 function run_single_abi_tests(decl, abi, t, toprimitive,
                               get_test, set_tests, sum_tests, sum_many_tests) {
@@ -1872,25 +1870,23 @@ function run_FunctionType_tests() {
   do_check_eq(fp_t.array().name, "g_t(*[])()");
   do_check_eq(fp_t.array().ptr.name, "g_t(*(*)[])()");
 
   let f3_t = ctypes.FunctionType(ctypes.default_abi,
     ctypes.char.ptr.array().ptr).ptr.ptr.array(8).array();
   do_check_eq(f3_t.name, "char*(*(**[][8])())[]");
 
 #ifdef WIN32
-#ifndef HAVE_64BIT_OS
   f3_t = ctypes.FunctionType(ctypes.stdcall_abi,
     ctypes.char.ptr.array().ptr).ptr.ptr.array(8).array();
   do_check_eq(f3_t.name, "char*(*(__stdcall **[][8])())[]");
   f3_t = ctypes.FunctionType(ctypes.winapi_abi,
     ctypes.char.ptr.array().ptr).ptr.ptr.array(8).array();
   do_check_eq(f3_t.name, "char*(*(WINAPI **[][8])())[]");
 #endif
-#endif
 
   let f4_t = ctypes.FunctionType(ctypes.default_abi,
     ctypes.char.ptr.array().ptr, [ ctypes.int32_t, fp_t ]);
   do_check_true(f4_t.argTypes.length == 2);
   do_check_true(f4_t.argTypes[0] === ctypes.int32_t);
   do_check_true(f4_t.argTypes[1] === fp_t);
 /* disabled temporarily per bug 598225.
   do_check_throws(function() { f4_t.argTypes.z = 0; }, Error);
@@ -2085,21 +2081,22 @@ function run_void_tests(library) {
   do_check_eq(test_void_t(), undefined);
 
   // Test that library.declare throws with void function args.
   do_check_throws(function() {
     library.declare("test_void_t_cdecl", ctypes.default_abi, ctypes.void_t, ctypes.void_t);
   }, Error);
 
 #ifdef WIN32
-#ifndef HAVE_64BIT_OS
   test_void_t = library.declare("test_void_t_stdcall", ctypes.stdcall_abi, ctypes.void_t);
   do_check_eq(test_void_t(), undefined);
 
-  // Check that WINAPI symbol lookup for a regular stdcall function fails.
+  // Check that WINAPI symbol lookup for a regular stdcall function fails on
+  // Win32 (it's all the same on Win64 though).
+#ifndef HAVE_64BIT_OS
   do_check_throws(function() {
     let test_winapi_t = library.declare("test_void_t_stdcall", ctypes.winapi_abi, ctypes.void_t);
   }, Error);
 #endif
 #endif
 }
 
 function run_string_tests(library) {
@@ -2248,28 +2245,26 @@ function run_function_tests(library)
   do_check_eq(ptrValue(test_ansi_len_2), ptrValue(ptr));
 */
 }
 
 function run_closure_tests(library)
 {
   run_single_closure_tests(library, ctypes.default_abi, "cdecl");
 #ifdef WIN32
-#ifndef HAVE_64BIT_OS
   run_single_closure_tests(library, ctypes.stdcall_abi, "stdcall");
 
   // Check that attempting to construct a ctypes.winapi_abi closure throws.
   function closure_fn()
   {
     return 1;
   }
   let fn_t = ctypes.FunctionType(ctypes.winapi_abi, ctypes.int32_t, []).ptr;
   do_check_throws(function() { fn_t(closure_fn) }, Error);
 #endif
-#endif
 }
 
 function run_single_closure_tests(library, abi, suffix)
 {
   let b = 23;
 
   function closure_fn(i)
   {
@@ -2319,27 +2314,25 @@ function run_variadic_tests(library) {
                         [ctypes.bool, "...", ctypes.bool]);
   }, Error);
 
   do_check_throws(function() {
     ctypes.FunctionType(ctypes.default_abi, ctypes.bool, ["..."]);
   }, Error);
 
 #ifdef WIN32
-#ifndef HAVE_64BIT_OS
   do_check_throws(function() {
       ctypes.FunctionType(ctypes.stdcall_abi, ctypes.bool,
                           [ctypes.bool, "..."]);
   }, Error);
   do_check_throws(function() {
       ctypes.FunctionType(ctypes.winapi_abi, ctypes.bool,
                           [ctypes.bool, "..."]);
   }, Error);
 #endif
-#endif
 
   do_check_throws(function() {
     // No variadic closure callbacks allowed.
     sum_va_type(function(){});
   }, Error);
 
   let count_true_va = library.declare("test_sum_va_cdecl", ctypes.default_abi, ctypes.uint8_t,
                                       ctypes.uint8_t, "...");
--- a/toolkit/components/telemetry/TelemetryHistograms.h
+++ b/toolkit/components/telemetry/TelemetryHistograms.h
@@ -45,16 +45,18 @@
  */
 
 HISTOGRAM(CYCLE_COLLECTOR, 1, 10000, 50, EXPONENTIAL, "Time spent on one cycle collection (ms)")
 HISTOGRAM(CYCLE_COLLECTOR_VISITED_REF_COUNTED, 1, 300000, 50, EXPONENTIAL, "Number of ref counted objects visited by the cycle collector")
 HISTOGRAM(CYCLE_COLLECTOR_VISITED_GCED, 1, 300000, 50, EXPONENTIAL, "Number of JS objects visited by the cycle collector")
 HISTOGRAM(CYCLE_COLLECTOR_COLLECTED, 1, 100000, 50, EXPONENTIAL, "Number of objects collected by the cycle collector")
 HISTOGRAM(TELEMETRY_PING, 1, 3000, 10, EXPONENTIAL, "Time taken to submit telemetry info (ms)")
 HISTOGRAM(TELEMETRY_SUCCESS, 0, 1, 2, BOOLEAN,  "Successful telemetry submission")
+HISTOGRAM(MEMORY_JS_COMPARTMENTS_SYSTEM, 1, 1000, 10, EXPONENTIAL, "Total JavaScript compartments used for add-ons and internals.")
+HISTOGRAM(MEMORY_JS_COMPARTMENTS_USER, 1, 1000, 10, EXPONENTIAL, "Total JavaScript compartments used for web pages")
 HISTOGRAM(MEMORY_JS_GC_HEAP, 1024, 512 * 1024, 10, EXPONENTIAL, "Memory used by the garbage-collected JavaScript heap (KB)")
 HISTOGRAM(MEMORY_RESIDENT, 32 * 1024, 1024 * 1024, 10, EXPONENTIAL, "Resident memory size (KB)")
 HISTOGRAM(MEMORY_LAYOUT_ALL, 1024, 64 * 1024, 10, EXPONENTIAL, "Memory used by layout (KB)")
 HISTOGRAM(MEMORY_IMAGES_CONTENT_USED_UNCOMPRESSED, 1024, 1024 * 1024, 10, EXPONENTIAL, "Memory used for uncompressed, in-use content images (KB)")
 HISTOGRAM(MEMORY_HEAP_ALLOCATED, 1024, 1024 * 1024, 10, EXPONENTIAL, "Heap memory allocated (KB)")
 HISTOGRAM(MEMORY_EXPLICIT, 1024, 1024 * 1024, 10, EXPONENTIAL, "Explicit memory allocations (KB)")
 #if defined(XP_WIN)
 HISTOGRAM(EARLY_GLUESTARTUP_READ_OPS, 1, 100, 12, LINEAR, "ProcessIoCounters.ReadOperationCount before glue startup")
--- a/toolkit/components/telemetry/TelemetryPing.js
+++ b/toolkit/components/telemetry/TelemetryPing.js
@@ -50,16 +50,18 @@ const PREF_SERVER = "toolkit.telemetry.s
 const PREF_ENABLED = "toolkit.telemetry.enabled";
 // Do not gather data more than once a minute
 const TELEMETRY_INTERVAL = 60;
 // Delay before intializing telemetry (ms)
 const TELEMETRY_DELAY = 60000;
 // about:memory values to turn into histograms
 const MEM_HISTOGRAMS = {
   "js-gc-heap": "MEMORY_JS_GC_HEAP",
+  "js-compartments-system": "MEMORY_JS_COMPARTMENTS_SYSTEM",
+  "js-compartments-user": "MEMORY_JS_COMPARTMENTS_USER",
   "resident": "MEMORY_RESIDENT",
   "explicit/layout/all": "MEMORY_LAYOUT_ALL",
   "explicit/images/content/used/uncompressed":
     "MEMORY_IMAGES_CONTENT_USED_UNCOMPRESSED",
   "heap-allocated": "MEMORY_HEAP_ALLOCATED",
   "page-faults-hard": "PAGE_FAULTS_HARD"
 };
 
--- a/toolkit/content/tests/chrome/test_arrowpanel.xul
+++ b/toolkit/content/tests/chrome/test_arrowpanel.xul
@@ -22,16 +22,20 @@
   <iframe id="frame" type="content"
           src="data:text/html,&lt;input id='input'&gt;" width="100" height="100" left="225" top="120"/>
 </stack>
 
 <panel id="panel" type="arrow" onpopupshown="checkPanelPosition(this)" onpopuphidden="runNextTest.next()">
   <label id="panellabel" value="This is some text." height="65"/>
 </panel>
 
+<panel id="bigpanel" type="arrow" onpopupshown="checkBigPanel(this)" onpopuphidden="SimpleTest.finish()">
+  <button label="This is some text." height="3000"/>
+</panel>
+
 <script type="application/javascript">
 <![CDATA[
 
 SimpleTest.waitForExplicitFinish();
 
 var expectedAnchor = null;
 var expectedSide = "", expectedAnchorEdge = "";
 var zoomFactor = 1;
@@ -131,16 +135,19 @@ function nextTest()
 
   setScale(frames[0], 2.5);
   openPopup("before_start", frames[0].document.getElementById("input"), "bottom", "left");
   yield;
 
   setScale(frames[0], 1);
 */
 
+  $("bigpanel").openPopup($("topleft"), "after_start", 0, 0, false, false, null);
+  yield;
+
   SimpleTest.finish();
   yield;
 }
 
 function setScale(win, scale)
 {
   var wn = win.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
               .getInterface(Components.interfaces.nsIWebNavigation);
@@ -215,16 +222,22 @@ function checkPanelPosition(panel)
 
   var arrow = document.getAnonymousElementByAttribute(panel, "anonid", "arrow");
   is(arrow.getAttribute("side"), expectedSide, "panel arrow side");
   is(arrow.hidden, false, "panel hidden");
 
   panel.hidePopup();
 }
 
+function checkBigPanel(panel)
+{
+  ok(panel.firstChild.getBoundingClientRect().height < 2800, "big panel height");
+  panel.hidePopup();
+}
+
 SimpleTest.waitForFocus(startTest);
 
 ]]>
 </script>
 
 <body xmlns="http://www.w3.org/1999/xhtml"/>
 
 </window>
--- a/toolkit/content/widgets/popup.xml
+++ b/toolkit/content/widgets/popup.xml
@@ -297,22 +297,22 @@
           }
         }
       ]]></handler>
     </handlers>
   </binding>
 
   <binding id="arrowpanel" extends="chrome://global/content/bindings/popup.xml#panel">
     <content flip="both" side="top" position="bottomcenter topleft">
-      <xul:box anonid="container" class="panel-arrowcontainer">
+      <xul:box anonid="container" class="panel-arrowcontainer" flex="1">
         <xul:box anonid="arrowbox" class="panel-arrowbox">
           <xul:image anonid="arrow" class="panel-arrow"/>
         </xul:box>
-        <xul:box class="panel-arrowcontent">
-          <xul:box class="panel-inner-arrowcontent" xbl:inherits="align,dir,orient,pack">
+        <xul:box class="panel-arrowcontent" flex="1">
+          <xul:box class="panel-inner-arrowcontent" xbl:inherits="align,dir,orient,pack" flex="1">
             <children/>
             <xul:box class="panel-inner-arrowcontentfooter" xbl:inherits="footertype" hidden="true"/>
           </xul:box>
         </xul:box>
       </xul:box>
     </content>
     <implementation>
       <field name="_fadeTimer"/>
--- a/widget/src/cocoa/nsChildView.h
+++ b/widget/src/cocoa/nsChildView.h
@@ -289,26 +289,26 @@ extern "C" long TSMProcessRawKeyEvent(Ev
 @end
 
 class ChildViewMouseTracker {
 
 public:
 
   static void MouseMoved(NSEvent* aEvent);
   static void OnDestroyView(ChildView* aView);
+  static void OnDestroyWindow(NSWindow* aWindow);
   static BOOL WindowAcceptsEvent(NSWindow* aWindow, NSEvent* aEvent,
                                  ChildView* aView, BOOL isClickThrough = NO);
+  static void MouseExitedWindow(NSEvent* aEvent);
+  static void MouseEnteredWindow(NSEvent* aEvent);
   static void ReEvaluateMouseEnterState(NSEvent* aEvent = nil);
   static ChildView* ViewForEvent(NSEvent* aEvent);
 
   static ChildView* sLastMouseEventView;
-
-private:
-
-  static NSWindow* WindowForEvent(NSEvent* aEvent);
+  static NSWindow* sWindowUnderMouse;
 };
 
 //-------------------------------------------------------------------------
 //
 // nsChildView
 //
 //-------------------------------------------------------------------------
 
--- a/widget/src/cocoa/nsChildView.mm
+++ b/widget/src/cocoa/nsChildView.mm
@@ -107,37 +107,30 @@ using namespace mozilla;
 #ifdef PR_LOGGING
 PRLogModuleInfo* sCocoaLog = nsnull;
 #endif
 
 extern "C" {
   CG_EXTERN void CGContextResetCTM(CGContextRef);
   CG_EXTERN void CGContextSetCTM(CGContextRef, CGAffineTransform);
   CG_EXTERN void CGContextResetClip(CGContextRef);
-
-  // CGSPrivate.h
-  typedef NSInteger CGSConnection;
-  typedef NSInteger CGSWindow;
-  extern CGSConnection _CGSDefaultConnection();
-  extern CGError CGSGetScreenRectForWindow(const CGSConnection cid, CGSWindow wid, CGRect *outRect);
-  extern CGError CGSGetWindowLevel(const CGSConnection cid, CGSWindow wid, CGWindowLevel *level);
-  extern CGError CGSGetWindowAlpha(const CGSConnection cid, const CGSWindow wid, float* alpha);
 }
 
 // defined in nsMenuBarX.mm
 extern NSMenu* sApplicationMenu; // Application menu shared by all menubars
 
 // these are defined in nsCocoaWindow.mm
 extern PRBool gConsumeRollupEvent;
 
 PRBool gChildViewMethodsSwizzled = PR_FALSE;
 
 extern nsISupportsArray *gDraggedTransferables;
 
 ChildView* ChildViewMouseTracker::sLastMouseEventView = nil;
+NSWindow* ChildViewMouseTracker::sWindowUnderMouse = nil;
 
 #ifdef INVALIDATE_DEBUGGING
 static void blinkRect(Rect* r);
 static void blinkRgn(RgnHandle rgn);
 #endif
 
 nsIRollupListener * gRollupListener = nsnull;
 nsIMenuRollup     * gMenuRollup = nsnull;
@@ -1231,16 +1224,34 @@ nsresult nsChildView::SynthesizeNativeMo
                                        context:nil
                                    eventNumber:0
                                     clickCount:1
                                       pressure:0.0];
 
   if (!event)
     return NS_ERROR_FAILURE;
 
+  if ([[mView window] isKindOfClass:[BaseWindow class]]) {
+    // Tracking area events don't end up in their tracking areas when sent
+    // through [NSApp sendEvent:], so pass them directly to the right methods.
+    BaseWindow* window = (BaseWindow*)[mView window];
+    if (aNativeMessage == NSMouseEntered) {
+      [window mouseEntered:event];
+      return NS_OK;
+    }
+    if (aNativeMessage == NSMouseExited) {
+      [window mouseExited:event];
+      return NS_OK;
+    }
+    if (aNativeMessage == NSMouseMoved) {
+      [window mouseMoved:event];
+      return NS_OK;
+    }
+  }
+
   [NSApp sendEvent:event];
   return NS_OK;
 
   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
 }
 
 // First argument has to be an NSMenu representing the application's top-level
 // menu bar. The returned item is *not* retained.
@@ -3229,21 +3240,16 @@ NSEvent* gLastDragMouseDownEvent = nil;
   }
 
   event.exit = aType;
 
   nsEventStatus status; // ignored
   mGeckoChild->DispatchEvent(&event, status);
 }
 
-- (void)mouseMoved:(NSEvent*)aEvent
-{
-  ChildViewMouseTracker::MouseMoved(aEvent);
-}
-
 - (void)handleMouseMoved:(NSEvent*)theEvent
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
 
   if (!mGeckoChild)
     return;
 
   nsMouseEvent geckoEvent(PR_TRUE, NS_MOUSE_MOVE, nsnull, nsMouseEvent::eReal);
@@ -4751,18 +4757,43 @@ NSEvent* gLastDragMouseDownEvent = nil;
 
 @end
 
 #pragma mark -
 
 void
 ChildViewMouseTracker::OnDestroyView(ChildView* aView)
 {
-  if (sLastMouseEventView == aView)
+  if (sLastMouseEventView == aView) {
     sLastMouseEventView = nil;
+  }
+}
+
+void
+ChildViewMouseTracker::OnDestroyWindow(NSWindow* aWindow)
+{
+  if (sWindowUnderMouse == aWindow) {
+    sWindowUnderMouse = nil;
+  }
+}
+
+void
+ChildViewMouseTracker::MouseEnteredWindow(NSEvent* aEvent)
+{
+  sWindowUnderMouse = [aEvent window];
+  ReEvaluateMouseEnterState(aEvent);
+}
+
+void
+ChildViewMouseTracker::MouseExitedWindow(NSEvent* aEvent)
+{
+  if (sWindowUnderMouse == [aEvent window]) {
+    sWindowUnderMouse = nil;
+    ReEvaluateMouseEnterState(aEvent);
+  }
 }
 
 void
 ChildViewMouseTracker::ReEvaluateMouseEnterState(NSEvent* aEvent)
 {
   ChildView* oldView = sLastMouseEventView;
   sLastMouseEventView = ViewForEvent(aEvent);
   if (sLastMouseEventView != oldView) {
@@ -4776,136 +4807,39 @@ ChildViewMouseTracker::ReEvaluateMouseEn
     }
     [sLastMouseEventView sendMouseEnterOrExitEvent:aEvent enter:YES type:type];
   }
 }
 
 void
 ChildViewMouseTracker::MouseMoved(NSEvent* aEvent)
 {
-  ReEvaluateMouseEnterState(aEvent);
+  MouseEnteredWindow(aEvent);
   [sLastMouseEventView handleMouseMoved:aEvent];
 }
 
 ChildView*
 ChildViewMouseTracker::ViewForEvent(NSEvent* aEvent)
 {
-  NSWindow* window = WindowForEvent(aEvent);
+  NSWindow* window = sWindowUnderMouse;
   if (!window)
     return nil;
 
   NSPoint windowEventLocation = nsCocoaUtils::EventLocationForWindow(aEvent, window);
   NSView* view = [[[window contentView] superview] hitTest:windowEventLocation];
-  NS_ASSERTION(view, "How can the mouse be over a window but not over a view in that window?");
   if (![view isKindOfClass:[ChildView class]])
     return nil;
 
   ChildView* childView = (ChildView*)view;
   // If childView is being destroyed return nil.
   if (![childView widget])
     return nil;
   return WindowAcceptsEvent(window, aEvent, childView) ? childView : nil;
 }
 
-static CGWindowLevel kDockWindowLevel = 0;
-static CGWindowLevel kPopupWindowLevel = 0;
-
-static BOOL WindowNumberIsUnderPoint(NSInteger aWindowNumber, NSPoint aPoint) {
-  NSWindow* window = [NSApp windowWithWindowNumber:aWindowNumber];
-  if (window) {
-    // This is one of our own windows.
-    return NSMouseInRect(aPoint, [window frame], NO);
-  }
-
-  CGSConnection cid = _CGSDefaultConnection();
-
-  if (!kDockWindowLevel) {
-    // These constants are in fact function calls, so cache them.
-    kDockWindowLevel = kCGDockWindowLevel;
-    kPopupWindowLevel = kCGPopUpMenuWindowLevel;
-  }
-
-  // Some things put transparent windows on top of everything. Ignore them.
-  CGWindowLevel level;
-  if ((kCGErrorSuccess == CGSGetWindowLevel(cid, aWindowNumber, &level)) &&
-      (level == kDockWindowLevel ||     // Transparent layer, spanning the whole screen
-       level > kPopupWindowLevel))      // Snapz Pro X while recording a screencast
-    return false;
-
-  // Ignore transparent windows.
-  float alpha;
-  if ((kCGErrorSuccess == CGSGetWindowAlpha(cid, aWindowNumber, &alpha)) &&
-      alpha < 0.1f)
-    return false;
-
-  CGRect rect;
-  if (kCGErrorSuccess != CGSGetScreenRectForWindow(cid, aWindowNumber, &rect))
-    return false;
-
-  CGPoint point = { aPoint.x, nsCocoaUtils::FlippedScreenY(aPoint.y) };
-  return CGRectContainsPoint(rect, point);
-}
-
-// Find the window number of the window under the given point, regardless of
-// which app the window belongs to. Returns 0 if no window was found.
-static NSInteger WindowNumberAtPoint(NSPoint aPoint) {
-  // We'd like to use the new windowNumberAtPoint API on 10.6 but we can't rely
-  // on it being up-to-date. For example, if we've just opened a window,
-  // windowNumberAtPoint might not know about it yet, so we'd send events to the
-  // wrong window. See bug 557986.
-  // So we'll have to find the right window manually by iterating over all
-  // windows on the screen and testing whether the mouse is inside the window's
-  // rect. We do this using private CGS functions.
-  // Another way of doing it would be to use tracking rects, but those are
-  // view-controlled, so they need to be reset whenever an NSView changes its
-  // size or position, which is expensive. See bug 300904 comment 20.
-  // A problem with using the CGS functions is that we only look at the windows'
-  // rects, not at the transparency of the actual pixel the mouse is over. This
-  // means that we won't treat transparent pixels as transparent to mouse
-  // events, which is a disadvantage over using tracking rects and leads to the
-  // crummy window level workarounds in WindowNumberIsUnderPoint.
-  // But speed is more important.
-
-  // Get the window list.
-  NSInteger windowCount;
-  NSCountWindows(&windowCount);
-  NSInteger* windowList = (NSInteger*)malloc(sizeof(NSInteger) * windowCount);
-  if (!windowList)
-    return nil;
-
-  // The list we get back here is in order from front to back.
-  NSWindowList(windowCount, windowList);
-  for (NSInteger i = 0; i < windowCount; i++) {
-    NSInteger windowNumber = windowList[i];
-    if (WindowNumberIsUnderPoint(windowNumber, aPoint)) {
-      free(windowList);
-      return windowNumber;
-    }
-  }
-
-  free(windowList);
-  return 0;
-}
-
-// Find Gecko window under the mouse. Returns nil if the mouse isn't over
-// any of our windows.
-NSWindow*
-ChildViewMouseTracker::WindowForEvent(NSEvent* anEvent)
-{
-  NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
-
-  NSPoint screenPoint = nsCocoaUtils::ScreenLocationForEvent(anEvent);
-  NSInteger windowNumber = WindowNumberAtPoint(screenPoint);
-
-  // This will return nil if windowNumber belongs to a window that we don't own.
-  return [NSApp windowWithWindowNumber:windowNumber];
-
-  NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
-}
-
 BOOL
 ChildViewMouseTracker::WindowAcceptsEvent(NSWindow* aWindow, NSEvent* aEvent,
                                           ChildView* aView, BOOL aIsClickThrough)
 {
   // Right mouse down events may get through to all windows, even to a top level
   // window with an open sheet.
   if (!aWindow || [aEvent type] == NSRightMouseDown)
     return YES;
--- a/widget/src/cocoa/nsCocoaWindow.h
+++ b/widget/src/cocoa/nsCocoaWindow.h
@@ -76,29 +76,37 @@ typedef struct _nsCocoaWindowList {
 
   // Shadow
   BOOL mScheduledShadowInvalidation;
 
   // DPI cache. Getting the physical screen size (CGDisplayScreenSize)
   // is ridiculously slow, so we cache it in the toplevel window for all
   // descendants to use.
   float mDPI;
+
+  NSTrackingArea* mTrackingArea;
 }
 
 - (void)importState:(NSDictionary*)aState;
 - (NSMutableDictionary*)exportState;
 - (void)setDrawsContentsIntoWindowFrame:(BOOL)aState;
 - (BOOL)drawsContentsIntoWindowFrame;
 - (void)setTitlebarColor:(NSColor*)aColor forActiveWindow:(BOOL)aActive;
 - (NSColor*)titlebarColorForActiveWindow:(BOOL)aActive;
 
 - (void)deferredInvalidateShadow;
 - (void)invalidateShadow;
 - (float)getDPI;
 
+- (void)mouseEntered:(NSEvent*)aEvent;
+- (void)mouseExited:(NSEvent*)aEvent;
+- (void)mouseMoved:(NSEvent*)aEvent;
+- (void)updateTrackingArea;
+- (NSView*)trackingAreaView;
+
 @end
 
 @interface NSWindow (Undocumented)
 
 // If a window has been explicitly removed from the "window cache" (to
 // deactivate it), it's sometimes necessary to "reset" it to reactivate it
 // (and put it back in the "window cache").  One way to do this, which Apple
 // often uses, is to set the "window number" to '-1' and then back to its
--- a/widget/src/cocoa/nsCocoaWindow.mm
+++ b/widget/src/cocoa/nsCocoaWindow.mm
@@ -637,32 +637,30 @@ NS_IMETHODIMP nsCocoaWindow::Show(PRBool
       // this sheet can be displayed. Leave the parent mSheetNeedsShow alone,
       // that is only used to handle sibling sheet contention. The parent will
       // return once there are no more child sheets.
       PRBool parentIsSheet = PR_FALSE;
       if (NS_SUCCEEDED(piParentWidget->GetIsSheet(&parentIsSheet)) &&
           parentIsSheet) {
         piParentWidget->GetSheetWindowParent(&topNonSheetWindow);
         [NSApp endSheet:nativeParentWindow];
-        [nativeParentWindow setAcceptsMouseMovedEvents:NO];
       }
 
       nsCocoaWindow* sheetShown = nsnull;
       if (NS_SUCCEEDED(piParentWidget->GetChildSheet(PR_TRUE, &sheetShown)) &&
           (!sheetShown || sheetShown == this)) {
         // If this sheet is already the sheet actually being shown, don't
         // tell it to show again. Otherwise the number of calls to
         // [NSApp beginSheet...] won't match up with [NSApp endSheet...].
         if (![mWindow isVisible]) {
           mSheetNeedsShow = PR_FALSE;
           mSheetWindowParent = topNonSheetWindow;
           // Only set contextInfo if our parent isn't a sheet.
           NSWindow* contextInfo = parentIsSheet ? nil : mSheetWindowParent;
           [TopLevelWindowData deactivateInWindow:mSheetWindowParent];
-          [mWindow setAcceptsMouseMovedEvents:YES];
           [NSApp beginSheet:mWindow
              modalForWindow:mSheetWindowParent
               modalDelegate:mDelegate
              didEndSelector:@selector(didEndSheet:returnCode:contextInfo:)
                 contextInfo:contextInfo];
           [TopLevelWindowData activateInWindow:mWindow];
           SendSetZLevelEvent();
         }
@@ -679,17 +677,16 @@ NS_IMETHODIMP nsCocoaWindow::Show(PRBool
       // for it to receive any mouse events aside from mouse-moved events
       // (because it was removed from the "window cache" when it was hidden
       // -- see below).  Setting the window number to -1 and then back to its
       // original value seems to accomplish this.  The idea was "borrowed"
       // from the Java Embedding Plugin.
       NSInteger windowNumber = [mWindow windowNumber];
       [mWindow _setWindowNumber:-1];
       [mWindow _setWindowNumber:windowNumber];
-      [mWindow setAcceptsMouseMovedEvents:YES];
       // For reasons that aren't yet clear, calls to [NSWindow orderFront:] or
       // [NSWindow makeKeyAndOrderFront:] can sometimes trigger "Error (1000)
       // creating CGSWindow", which in turn triggers an internal inconsistency
       // NSException.  These errors shouldn't be fatal.  So we need to wrap
       // calls to ...orderFront: in LOGONLY blocks.  See bmo bug 470864.
       NS_OBJC_BEGIN_TRY_LOGONLY_BLOCK;
       [mWindow orderFront:nil];
       NS_OBJC_END_TRY_LOGONLY_BLOCK;
@@ -711,17 +708,16 @@ NS_IMETHODIMP nsCocoaWindow::Show(PRBool
       // appear above the parent and move when the parent does. Setting this
       // needs to happen after the _setWindowNumber calls above, otherwise the
       // window doesn't focus properly.
       if (nativeParentWindow && mPopupLevel == ePopupLevelParent)
         [nativeParentWindow addChildWindow:mWindow
                             ordered:NSWindowAbove];
     }
     else {
-      [mWindow setAcceptsMouseMovedEvents:YES];
       NS_OBJC_BEGIN_TRY_LOGONLY_BLOCK;
       [mWindow makeKeyAndOrderFront:nil];
       NS_OBJC_END_TRY_LOGONLY_BLOCK;
       SendSetZLevelEvent();
     }
   }
   else {
     // roll up any popups if a top-level window is going away
@@ -738,18 +734,16 @@ NS_IMETHODIMP nsCocoaWindow::Show(PRBool
       }
       else {
         // get sheet's parent *before* hiding the sheet (which breaks the linkage)
         NSWindow* sheetParent = mSheetWindowParent;
         
         // hide the sheet
         [NSApp endSheet:mWindow];
         
-        [mWindow setAcceptsMouseMovedEvents:NO];
-
         [TopLevelWindowData deactivateInWindow:mWindow];
 
         nsCocoaWindow* siblingSheetToShow = nsnull;
         PRBool parentIsSheet = PR_FALSE;
 
         if (nativeParentWindow && piParentWidget &&
             NS_SUCCEEDED(piParentWidget->GetChildSheet(PR_FALSE, &siblingSheetToShow)) &&
             siblingSheetToShow) {
@@ -769,30 +763,28 @@ NS_IMETHODIMP nsCocoaWindow::Show(PRBool
             if (piGrandparentWidget && NS_SUCCEEDED(piGrandparentWidget->GetIsSheet(&grandparentIsSheet)) &&
                 grandparentIsSheet) {
                 contextInfo = nil;
             }
           }
           // If there are no sibling sheets, but the parent is a sheet, restore
           // it.  It wasn't sent any deactivate events when it was hidden, so
           // don't call through Show, just let the OS put it back up.
-          [nativeParentWindow setAcceptsMouseMovedEvents:YES];
           [NSApp beginSheet:nativeParentWindow
              modalForWindow:sheetParent
               modalDelegate:[nativeParentWindow delegate]
              didEndSelector:@selector(didEndSheet:returnCode:contextInfo:)
                 contextInfo:contextInfo];
         }
         else {
           // Sheet, that was hard.  No more siblings or parents, going back
           // to a real window.
           NS_OBJC_BEGIN_TRY_LOGONLY_BLOCK;
           [sheetParent makeKeyAndOrderFront:nil];
           NS_OBJC_END_TRY_LOGONLY_BLOCK;
-          [sheetParent setAcceptsMouseMovedEvents:YES];
         }
         SendSetZLevelEvent();
       }
     }
     else {
       // If the window is a popup window with a parent window we need to
       // unhook it here before ordering it out. When you order out the child
       // of a window it hides the parent window.
@@ -808,20 +800,16 @@ NS_IMETHODIMP nsCocoaWindow::Show(PRBool
       // surely an Apple bug.  The "window cache" is an undocumented subsystem,
       // all of whose methods are included in the NSWindowCache category of
       // the NSApplication class (in header files generated using class-dump).
       // This workaround was "borrowed" from the Java Embedding Plugin (which
       // uses it for a different purpose).
       if (mWindowType == eWindowType_popup)
         [NSApp _removeWindowFromCache:mWindow];
 
-      // it's very important to turn off mouse moved events when hiding a window, otherwise
-      // the windows' tracking rects will interfere with each other. (bug 356528)
-      [mWindow setAcceptsMouseMovedEvents:NO];
-
       // If our popup window is a non-native context menu, tell the OS (and
       // other programs) that a menu has closed.
       if ([mWindow isKindOfClass:[PopupWindow class]] &&
           [(PopupWindow*) mWindow isContextMenu]) {
         [[NSDistributedNotificationCenter defaultCenter]
           postNotificationName:@"com.apple.HIToolbox.endMenuTrackingNotification"
                         object:@"org.mozilla.gecko.PopupWindow"];
       }
@@ -1409,17 +1397,16 @@ void nsCocoaWindow::SetMenuBar(nsMenuBar
 }
 
 NS_IMETHODIMP nsCocoaWindow::SetFocus(PRBool aState)
 {
   if (mPopupContentView) {
     mPopupContentView->SetFocus(aState);
   }
   else if (aState && ([mWindow isVisible] || [mWindow isMiniaturized])) {
-    [mWindow setAcceptsMouseMovedEvents:YES];
     [mWindow makeKeyAndOrderFront:nil];
     SendSetZLevelEvent();
   }
 
   return NS_OK;
 }
 
 nsIntPoint nsCocoaWindow::WidgetToScreenOffset()
@@ -1758,16 +1745,19 @@ PRBool nsCocoaWindow::ShouldFocusPlugin(
 {
   RollUpPopups();
   
   return proposedFrameSize;
 }
 
 - (void)windowDidResize:(NSNotification *)aNotification
 {
+  BaseWindow* window = [aNotification object];
+  [window updateTrackingArea];
+
   if (!mGeckoWindow)
     return;
 
   // Resizing might have changed our zoom state.
   mGeckoWindow->DispatchSizeModeEvent();
   mGeckoWindow->ReportSizeEvent();
 }
 
@@ -1965,35 +1955,44 @@ GetDPI(NSWindow* aWindow)
   // Currently we don't do our own scaling to take account
   // of userSpaceScaleFactor, so every "pixel" we draw is actually
   // userSpaceScaleFactor screen pixels. So divide the screen height
   // by userSpaceScaleFactor to get the number of "device pixels"
   // available.
   return (heightPx / scaleFactor) / (heightMM / MM_PER_INCH_FLOAT);
 }
 
+@interface BaseWindow(Private)
+- (void)removeTrackingArea;
+- (void)cursorUpdated:(NSEvent*)aEvent;
+@end
+
 @implementation BaseWindow
 
 - (id)initWithContentRect:(NSRect)aContentRect styleMask:(NSUInteger)aStyle backing:(NSBackingStoreType)aBufferingType defer:(BOOL)aFlag
 {
   [super initWithContentRect:aContentRect styleMask:aStyle backing:aBufferingType defer:aFlag];
   mState = nil;
   mDrawsIntoWindowFrame = NO;
   mActiveTitlebarColor = nil;
   mInactiveTitlebarColor = nil;
   mScheduledShadowInvalidation = NO;
   mDPI = GetDPI(self);
+  mTrackingArea = nil;
+  [self updateTrackingArea];
 
   return self;
 }
 
 - (void)dealloc
 {
   [mActiveTitlebarColor release];
   [mInactiveTitlebarColor release];
+  [self removeTrackingArea];
+  ChildViewMouseTracker::OnDestroyWindow(self);
   [super dealloc];
 }
 
 static const NSString* kStateTitleKey = @"title";
 static const NSString* kStateDrawsContentsIntoWindowFrameKey = @"drawsContentsIntoWindowFrame";
 static const NSString* kStateActiveTitlebarColorKey = @"activeTitlebarColor";
 static const NSString* kStateInactiveTitlebarColorKey = @"inactiveTitlebarColor";
 static const NSString* kStateShowsToolbarButton = @"showsToolbarButton";
@@ -2069,16 +2068,65 @@ static const NSString* kStateShowsToolba
   mScheduledShadowInvalidation = NO;
 }
 
 - (float)getDPI
 {
   return mDPI;
 }
 
+- (NSView*)trackingAreaView
+{
+  NSView* contentView = [self contentView];
+  return [contentView superview] ? [contentView superview] : contentView;
+}
+
+- (void)removeTrackingArea
+{
+  if (mTrackingArea) {
+    [[self trackingAreaView] removeTrackingArea:mTrackingArea];
+    [mTrackingArea release];
+    mTrackingArea = nil;
+  }
+}
+
+- (void)updateTrackingArea
+{
+  [self removeTrackingArea];
+
+  NSView* view = [self trackingAreaView];
+  const NSTrackingAreaOptions options =
+    NSTrackingMouseEnteredAndExited | NSTrackingMouseMoved | NSTrackingActiveAlways;
+  mTrackingArea = [[NSTrackingArea alloc] initWithRect:[view bounds]
+                                               options:options
+                                                 owner:self
+                                              userInfo:nil];
+  [view addTrackingArea:mTrackingArea];
+}
+
+- (void)mouseEntered:(NSEvent*)aEvent
+{
+  ChildViewMouseTracker::MouseEnteredWindow(aEvent);
+}
+
+- (void)mouseExited:(NSEvent*)aEvent
+{
+  ChildViewMouseTracker::MouseExitedWindow(aEvent);
+}
+
+- (void)mouseMoved:(NSEvent*)aEvent
+{
+  ChildViewMouseTracker::MouseMoved(aEvent);
+}
+
+- (void)cursorUpdated:(NSEvent*)aEvent
+{
+  // Nothing to do here, but NSTrackingArea wants us to implement this method.
+}
+
 - (BOOL)respondsToSelector:(SEL)aSelector
 {
   // Claim the window doesn't respond to this so that the system
   // doesn't steal keyboard equivalents for it. Bug 613710.
   if (aSelector == @selector(cancelOperation:)) {
     return NO;
   }
 
@@ -2451,97 +2499,16 @@ TitlebarDrawCallback(void* aInfo, CGCont
 {
   return NSDeviceRGBColorSpace;
 }
 
 @end
 
 @implementation PopupWindow
 
-// The OS treats our custom popup windows very strangely -- many mouse events
-// sent to them never reach their target NSView objects.  (That these windows
-// are borderless and of level NSPopUpMenuWindowLevel may have something to do
-// with it.)  The best solution is to pre-empt the OS, as follows.  (All
-// events for a given NSWindow object go through its sendEvent: method.)
-- (void)sendEvent:(NSEvent *)anEvent
-{
-  NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
-
-  NSView *target = nil;
-  NSView *contentView = nil;
-  NSEventType type = [anEvent type];
-  NSPoint windowLocation = NSZeroPoint;
-  switch (type) {
-    case NSScrollWheel:
-    case NSLeftMouseDown:
-    case NSLeftMouseUp:
-    case NSRightMouseDown:
-    case NSRightMouseUp:
-    case NSOtherMouseDown:
-    case NSOtherMouseUp:
-    case NSMouseMoved:
-    case NSLeftMouseDragged:
-    case NSRightMouseDragged:
-    case NSOtherMouseDragged:
-      if ((contentView = [self contentView])) {
-        // Since [anEvent window] might not be us, we can't use [anEvent locationInWindow].
-        windowLocation = nsCocoaUtils::EventLocationForWindow(anEvent, self);
-        target = [contentView hitTest:[contentView convertPoint:windowLocation fromView:nil]];
-        // If the hit test failed, the event is targeted here but is not over the window.
-        // Send it to our content view.
-        if (!target)
-          target = contentView;
-      }
-      break;
-    default:
-      break;
-  }
-  if (target) {
-    switch (type) {
-      case NSScrollWheel:
-        [target scrollWheel:anEvent];
-        break;
-      case NSLeftMouseUp:
-        [target mouseUp:anEvent];
-        break;
-      case NSRightMouseDown:
-        [target rightMouseDown:anEvent];
-        break;
-      case NSRightMouseUp:
-        [target rightMouseUp:anEvent];
-        break;
-      case NSOtherMouseDown:
-        [target otherMouseDown:anEvent];
-        break;
-      case NSOtherMouseUp:
-        [target otherMouseUp:anEvent];
-        break;
-      case NSMouseMoved:
-        [target mouseMoved:anEvent];
-        break;
-      case NSLeftMouseDragged:
-        [target mouseDragged:anEvent];
-        break;
-      case NSRightMouseDragged:
-        [target rightMouseDragged:anEvent];
-        break;
-      case NSOtherMouseDragged:
-        [target otherMouseDragged:anEvent];
-        break;
-      default:
-        [super sendEvent:anEvent];
-        break;
-    }
-  } else {
-    [super sendEvent:anEvent];
-  }
-
-  NS_OBJC_END_TRY_ABORT_BLOCK;
-}
-
 - (id)initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)styleMask
       backing:(NSBackingStoreType)bufferingType defer:(BOOL)deferCreation
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
 
   mIsContextMenu = false;
   return [super initWithContentRect:contentRect styleMask:styleMask
           backing:bufferingType defer:deferCreation];
--- a/widget/src/cocoa/nsToolkit.h
+++ b/widget/src/cocoa/nsToolkit.h
@@ -78,16 +78,15 @@ protected:
 
 protected:
 
   bool               mInited;
 
   CFRunLoopSourceRef mSleepWakeNotificationRLS;
   io_object_t        mPowerNotifier;
 
-  EventHandlerRef    mEventMonitorHandler;
   CFMachPortRef      mEventTapPort;
   CFRunLoopSourceRef mEventTapRLS;
 };
 
 extern nsToolkit* NS_CreateToolkitInstance();
 
 #endif // nsToolkit_h_
--- a/widget/src/cocoa/nsToolkit.mm
+++ b/widget/src/cocoa/nsToolkit.mm
@@ -82,17 +82,16 @@ static io_connect_t gRootPort = MACH_POR
 
 // Static thread local storage index of the Toolkit 
 // object associated with a given thread...
 static PRUintn gToolkitTLSIndex = 0;
 
 nsToolkit::nsToolkit()
 : mInited(false)
 , mSleepWakeNotificationRLS(nsnull)
-, mEventMonitorHandler(nsnull)
 , mEventTapPort(nsnull)
 , mEventTapRLS(nsnull)
 {
 }
 
 nsToolkit::~nsToolkit()
 {
   RemoveSleepWakeNotifcations();
@@ -197,28 +196,16 @@ nsToolkit::RemoveSleepWakeNotifcations()
                             kCFRunLoopDefaultMode);
 
     mSleepWakeNotificationRLS = nsnull;
   }
 
   NS_OBJC_END_TRY_ABORT_BLOCK;
 }
 
-// This is the callback used in RegisterForAllProcessMouseEvents.
-static OSStatus EventMonitorHandler(EventHandlerCallRef aCaller, EventRef aEvent, void* aRefcon)
-{
-  // Up to Mac OS 10.4 (or when building with the 10.4 SDK), installing a Carbon
-  // event handler like this one caused the OS to post the equivalent Cocoa
-  // events to [NSApp sendEvent:]. When using the 10.5 SDK, this doesn't happen
-  // any more, so we need to do it manually.
-  [NSApp sendEvent:[NSEvent eventWithEventRef:aEvent]];
-
-  return eventNotHandledErr;
-}
-
 // Converts aPoint from the CoreGraphics "global display coordinate" system
 // (which includes all displays/screens and has a top-left origin) to its
 // (presumed) Cocoa counterpart (assumed to be the same as the "screen
 // coordinates" system), which has a bottom-left origin.
 static NSPoint ConvertCGGlobalToCocoaScreen(CGPoint aPoint)
 {
   NSPoint cocoaPoint;
   cocoaPoint.x = aPoint.x;
@@ -270,22 +257,16 @@ nsToolkit::RegisterForAllProcessMouseEve
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
 
   // Don't do this for apps that (like Camino) use native context menus.
 #ifdef MOZ_USE_NATIVE_POPUP_WINDOWS
   return;
 #endif /* MOZ_USE_NATIVE_POPUP_WINDOWS */
 
-  if (!mEventMonitorHandler) {
-    EventTypeSpec kEvents[] = {{kEventClassMouse, kEventMouseMoved}};
-    InstallEventHandler(GetEventMonitorTarget(), EventMonitorHandler,
-                        GetEventTypeCount(kEvents), kEvents, 0,
-                        &mEventMonitorHandler);
-  }
   if (!mEventTapRLS) {
     // Using an event tap for mouseDown events (instead of installing a
     // handler for them on the EventMonitor target) works around an Apple
     // bug that causes OS menus (like the Clock menu) not to work properly
     // on OS X 10.4.X and below (bmo bug 381448).
     // We install our event tap "listen only" to get around yet another Apple
     // bug -- when we install it as an event filter on any kind of mouseDown
     // event, that kind of event stops working in the main menu, and usually
@@ -315,20 +296,16 @@ nsToolkit::RegisterForAllProcessMouseEve
   NS_OBJC_END_TRY_ABORT_BLOCK;
 }
 
 void
 nsToolkit::UnregisterAllProcessMouseEventHandlers()
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
 
-  if (mEventMonitorHandler) {
-    RemoveEventHandler(mEventMonitorHandler);
-    mEventMonitorHandler = nsnull;
-  }
   if (mEventTapRLS) {
     CFRunLoopRemoveSource(CFRunLoopGetCurrent(), mEventTapRLS,
                           kCFRunLoopDefaultMode);
     CFRelease(mEventTapRLS);
     mEventTapRLS = nsnull;
   }
   if (mEventTapPort) {
     // mEventTapPort must be invalidated as well as released.  Otherwise the
--- a/widget/tests/native_mouse_mac_window.xul
+++ b/widget/tests/native_mouse_mac_window.xul
@@ -67,22 +67,24 @@
       window.opener.wrappedJSObject.SimpleTest.todo(condition, message);
     }
 
     function todo_is(a, b, message) {
       window.opener.wrappedJSObject.SimpleTest.todo_is(a, b, message);
     }
 
     function onTestsFinished() {
+      clearTimeout(gAfterLoopExecution);
       observe(window, eventMonitor, false);
       observe(gRightWindow, eventMonitor, false);
       observe(gPopup, eventMonitor, false);
       gRightWindow.close();
+      var openerSimpleTest = window.opener.wrappedJSObject.SimpleTest;
       window.close();
-      window.opener.wrappedJSObject.SimpleTest.finish();
+      openerSimpleTest.finish();
     }
 
     const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
     const xulWin = 'data:application/vnd.mozilla.xul+xml,<?xml version="1.0"?><?xml-stylesheet href="chrome://global/skin" type="text/css"?><window xmlns="' + XUL_NS + '"/>';
 
     const NSLeftMouseDown      = 1,
           NSLeftMouseUp        = 2,
           NSRightMouseDown     = 3,
@@ -123,33 +125,37 @@
           NSFunctionKeyMask   = 1 << 23;
 
     const gDebug = false;
 
     function printDebug(msg) { if (gDebug) dump(msg); }
 
     var gExpectedEvents = [];
     var gRightWindow = null, gPopup = null;
+    var gCurrentMouseX = 0, gCurrentMouseY = 0;
+    var gAfterLoopExecution = 0;
 
     function testMouse(x, y, msg, elem, win, exp, flags, callback) {
       clearExpectedEvents();
       exp.forEach(function (expEv) {
         expEv.screenX = x;
         expEv.screenY = y;
         gExpectedEvents.push(expEv);
       });
       printDebug("sending event: " + x + ", " + y + " (" + msg + ")\n");
+      gCurrentMouseX = x;
+      gCurrentMouseY = y;
       netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
       var utils = win.QueryInterface(Components.interfaces.nsIInterfaceRequestor).
                                      getInterface(Components.interfaces.nsIDOMWindowUtils);
       utils.sendNativeMouseEvent(x, y, msg, flags || 0, elem);
-      SimpleTest.executeSoon(function () {
+      gAfterLoopExecution = setTimeout(function () {
         clearExpectedEvents();
         callback();
-      });
+      }, 0);
     }
 
     function eventListenOnce(elem, name, callback) {
       elem.addEventListener(name, function(e) {
         elem.removeEventListener(name, arguments.callee, false);
         callback(e);
       }, false);
     }
@@ -177,20 +183,25 @@
     var gEventNum = 0;
 
     function eventMonitor(e) {
       printDebug("got event: " + eventToString(e) + "\n");
       processEvent(e);
     }
 
     function processEvent(e) {
+      if (e.screenX != gCurrentMouseX || e.screenY != gCurrentMouseY) {
+        todo(false, "Oh no! Received a stray event from a confused tracking area. Aborting test.");
+        onTestsFinished();
+        return;
+      }
       var expectedEvent = gExpectedEvents.shift();
       if (!expectedEvent) {
         ok(false, "received event I didn't expect: " + eventToString(e));
-        return true;
+        return;
       }
       if (e.type != expectedEvent.type) {
         // Didn't get expectedEvent.
         var errFun = expectedEvent.shouldFireButDoesnt ? todo : ok;
         errFun(false, "Didn't receive expected event: " + eventToString(expectedEvent));
         return processEvent(e);
       }
       gEventNum++;
@@ -301,16 +312,20 @@
         [410, 150, NSLeftMouseDragged, null, right, [
           { type: "mousemove", target: rightElem },
         ]],
         // Let go of the mouse.
         [410, 150, NSLeftMouseUp, null, right, [
           { type: "mouseup", target: rightElem },
           { type: "click", target: rightElem },
         ]],
+        // Move the mouse back over the left window, which is inactive.
+        [150, 170, NSMouseMoved, null, left, [
+          { type: "mouseout", target: rightElem },
+        ]],
         // Now we're being sneaky. The left window is inactive, but *right*-clicks to it
         // should still get through. Test that.
         // Ideally we'd be bracketing that event with over and out events, too, but it
         // probably doesn't matter too much.
         [150, 170, NSRightMouseDown, null, left, [
           { type: "mouseover", target: leftElem, shouldFireButDoesnt: true },
           { type: "mousedown", target: leftElem },
           { type: "mouseout", target: leftElem, shouldFireButDoesnt: true },
@@ -322,17 +337,16 @@
           { type: "click", target: leftElem },
           { type: "mouseout", target: leftElem, shouldFireButDoesnt: true },
         ]],
         // Right clicking hasn't focused it, so the window is still inactive.
         // Let's focus it; this time without the mouse, for variaton's sake.
         // Still, mouseout and mouseover events should fire.
         function raiseLeftWindow(callback) {
           clearExpectedEvents();
-          gExpectedEvents.push({ screenX: 150, screenY: 170, type: "mouseout", target: rightElem });
           gExpectedEvents.push({ screenX: 150, screenY: 170, type: "mouseover", target: leftElem });
           focusAndThen(left, function () { SimpleTest.executeSoon(callback); });
         },
         // It's active, so it should respond to mousemove events now.
         [150, 170, NSMouseMoved, null, left, [
           { type: "mousemove", target: leftElem },
         ]],
         // This was boring... let's introduce a popup. It will overlap both the left
@@ -386,35 +400,30 @@
           { type: "mouseup", target: rightElem },
           { type: "click", target: rightElem },
         ]],
 
         // Time for our next trick: a tooltip!
         // Install the tooltip, but don't show it yet.
         function setTooltip(callback) {
           rightElem.setAttribute("tooltip", "tip");
-          callback();
+          gExpectedEvents.push({ screenX: 410, screenY: 180, type: "mousemove", target: rightElem });
+          eventListenOnce(rightElem, "popupshown", callback);
+          gCurrentMouseX = 410;
+          gCurrentMouseY = 180;
+          var utils = right.QueryInterface(Components.interfaces.nsIInterfaceRequestor).
+                               getInterface(Components.interfaces.nsIDOMWindowUtils);
+          utils.sendNativeMouseEvent(410, 180, NSMouseMoved, 0, null);
         },
-        // Move the mouse to trigger the appearance of the tooltip.
-        [410, 180, NSMouseMoved, null, right, [
+        // Now the tooltip is visible.
+        // Move the mouse a little to the right.
+        [411, 180, NSMouseMoved, null, right, [
           { type: "mousemove", target: rightElem },
         ]],
-        // Wait for the tooltip to appear.
-        function (callback) {
-          eventListenOnce(rightElem, "popupshown", callback);
-        },
-        // Now the tooltip is visible.
-        // Move the mouse a little to the right, but send the event to the tooltip's
-        // widget, even though the mouse is not over the tooltip, because that's what
-        // Mac OS X does.
-        [411, 180, NSMouseMoved, tooltip, right, [
-          { type: "mousemove", target: rightElem },
-        ]],
-        // Move another pixel. This time send the event to the right widget.
-        // However, that must not make a difference.
+        // Move another pixel.
         [412, 180, NSMouseMoved, null, right, [
           { type: "mousemove", target: rightElem },
         ]],
         // Move up and click to make the tooltip go away.
         [412, 80, NSMouseMoved, null, right, [
           { type: "mousemove", target: rightElem },
         ]],
         [412, 80, NSLeftMouseDown, null, right, [
@@ -429,51 +438,50 @@
           eventListenOnce(panel, "popupshown", callback);
           panel.openPopupAtScreen(150, 150, false);
         },
         // The panel is parented, so it will be z-ordered over its parent but
         // under the active window.
         // Now we move the mouse over the part where the panel rect intersects the
         // right window's rect. Since the panel is under the window, all the events
         // should target the right window.
-        // Try with sending to three different targets.
         [390, 170, NSMouseMoved, null, right, [
           { type: "mousemove", target: rightElem },
         ]],
-        [390, 171, NSMouseMoved, null, left, [
+        [390, 171, NSMouseMoved, null, right, [
           { type: "mousemove", target: rightElem },
         ]],
-        [391, 171, NSMouseMoved, panel, left, [
+        [391, 171, NSMouseMoved, null, right, [
           { type: "mousemove", target: rightElem },
         ]],
         // Now move off the right window, so that the mouse is directly over the
         // panel.
-        [260, 170, NSMouseMoved, null, left, [
+        [260, 170, NSMouseMoved, panel, left, [
           { type: "mouseout", target: rightElem },
         ]],
-        [260, 171, NSMouseMoved, null, left, [
+        [260, 171, NSMouseMoved, panel, left, [
         ]],
         [261, 171, NSMouseMoved, panel, left, [
         ]],
         // Let's be evil and click it.
         [261, 171, NSLeftMouseDown, panel, left, [
         ]],
         [261, 171, NSLeftMouseUp, panel, left, [
         ]],
         // This didn't focus the window, unfortunately, so let's do it ourselves.
         function raiseLeftWindowTakeTwo(callback) {
           focusAndThen(left, callback);
         },
         // Now mouse events should get through to the panel (which is now over the
         // right window).
-        [387, 170, NSMouseMoved, null, right, [
+        [387, 170, NSMouseMoved, panel, left, [
           { type: "mouseover", target: panel },
           { type: "mousemove", target: panel },
         ]],
-        [387, 171, NSMouseMoved, null, left, [
+        [387, 171, NSMouseMoved, panel, left, [
           { type: "mousemove", target: panel },
         ]],
         [388, 171, NSMouseMoved, panel, left, [
           { type: "mousemove", target: panel },
         ]],
         // Click the panel.
         [388, 171, NSLeftMouseDown, panel, left, [
           { type: "mousedown", target: panel }
@@ -481,22 +489,22 @@
         [388, 171, NSLeftMouseUp, panel, left, [
           { type: "mouseup", target: panel },
           { type: "click", target: panel },
         ]],
 
         // Last test for this part: Hit testing in the Canyon of Nowhere -
         // the pixel row directly south of the panel, over the left window.
         // Before bug 515003 we wrongly thought the mouse wasn't over any window.
-        [173, 200, NSMouseMoved, panel, left, [
+        [173, 200, NSMouseMoved, null, left, [
           { type: "mouseout", target: panel },
           { type: "mouseover", target: leftElem },
           { type: "mousemove", target: leftElem },
         ]],
-        [173, 201, NSMouseMoved, panel, left, [
+        [173, 201, NSMouseMoved, null, left, [
           { type: "mousemove", target: leftElem },
         ]],
 
         // Part 2: Allow click-through
 
         function hideThatPanel(callback) {
           eventListenOnce(panel, "popuphidden", callback);
           panel.hidePopup();
@@ -562,42 +570,38 @@
         [410, 150, NSLeftMouseDragged, null, right, [
           { type: "mousemove", target: rightElem },
         ]],
         // Let go of the mouse.
         [410, 150, NSLeftMouseUp, null, right, [
           { type: "mouseup", target: rightElem },
           { type: "click", target: rightElem },
         ]],
-        // Now we're being sneaky. The left window is inactive, but *right*-clicks to it
-        // should still get through. Test that.
-        // Ideally we'd be bracketing that event with over and out events, too, but it
-        // probably doesn't matter too much.
+        // Move the mouse back over the left window, which is inactive.
+        [150, 170, NSMouseMoved, null, left, [
+          { type: "mouseout", target: rightElem },
+          { type: "mouseover", target: leftElem },
+          { type: "mousemove", target: leftElem },
+        ]],
+        // Right-click it.
         [150, 170, NSRightMouseDown, null, left, [
-          { type: "mouseover", target: leftElem, shouldFireButDoesnt: true },
           { type: "mousedown", target: leftElem },
-          { type: "mouseout", target: leftElem, shouldFireButDoesnt: true },
         ]],
         // Let go of the mouse.
         [150, 170, NSRightMouseUp, null, left, [
-          { type: "mouseover", target: leftElem, shouldFireButDoesnt: true },
           { type: "mouseup", target: leftElem },
           { type: "click", target: leftElem },
-          { type: "mouseout", target: leftElem, shouldFireButDoesnt: true },
         ]],
         // Right clicking hasn't focused it, so the window is still inactive.
         // Let's focus it; this time without the mouse, for variaton's sake.
-        // Still, mouseout and mouseover events should fire.
         function raiseLeftWindow(callback) {
           clearExpectedEvents();
-          gExpectedEvents.push({ screenX: 150, screenY: 170, type: "mouseout", target: rightElem });
-          gExpectedEvents.push({ screenX: 150, screenY: 170, type: "mouseover", target: leftElem });
           focusAndThen(left, function () { SimpleTest.executeSoon(callback); });
         },
-        // It's active, so it should respond to mousemove events now.
+        // It's active and should still respond to mousemove events.
         [150, 170, NSMouseMoved, null, left, [
           { type: "mousemove", target: leftElem },
         ]],
 
         // This was boring... let's introduce a popup. It will overlap both the left
         // and the right window.
         function openPopupInLeftWindow(callback) {
           eventListenOnce(gPopup, "popupshown", callback);
@@ -648,37 +652,32 @@
         ]],
         [400, 180, NSLeftMouseUp, null, right, [
           { type: "mouseup", target: rightElem },
           { type: "click", target: rightElem },
         ]],
 
         // Time for our next trick: a tooltip!
         // Install the tooltip, but don't show it yet.
-        function setTooltip(callback) {
+        function setTooltip2(callback) {
           rightElem.setAttribute("tooltip", "tip");
-          callback();
+          gExpectedEvents.push({ screenX: 410, screenY: 180, type: "mousemove", target: rightElem });
+          eventListenOnce(rightElem, "popupshown", callback);
+          gCurrentMouseX = 410;
+          gCurrentMouseY = 180;
+          var utils = right.QueryInterface(Components.interfaces.nsIInterfaceRequestor).
+                               getInterface(Components.interfaces.nsIDOMWindowUtils);
+          utils.sendNativeMouseEvent(410, 180, NSMouseMoved, 0, null);
         },
-        // Move the mouse to trigger the appearance of the tooltip.
-        [410, 180, NSMouseMoved, null, right, [
+        // Now the tooltip is visible.
+        // Move the mouse a little to the right.
+        [411, 180, NSMouseMoved, null, right, [
           { type: "mousemove", target: rightElem },
         ]],
-        // Wait for the tooltip to appear.
-        function (callback) {
-          eventListenOnce(rightElem, "popupshown", callback);
-        },
-        // Now the tooltip is visible.
-        // Move the mouse a little to the right, but send the event to the tooltip's
-        // widget, even though the mouse is not over the tooltip, because that's what
-        // Mac OS X does.
-        [411, 180, NSMouseMoved, tooltip, right, [
-          { type: "mousemove", target: rightElem },
-        ]],
-        // Move another pixel. This time send the event to the right widget.
-        // However, that must not make a difference.
+        // Move another pixel.
         [412, 180, NSMouseMoved, null, right, [
           { type: "mousemove", target: rightElem },
         ]],
         // Move up and click to make the tooltip go away.
         [412, 80, NSMouseMoved, null, right, [
           { type: "mousemove", target: rightElem },
         ]],
         [412, 80, NSLeftMouseDown, null, right, [
@@ -693,34 +692,33 @@
           eventListenOnce(panel, "popupshown", callback);
           panel.openPopupAtScreen(150, 150, false);
         },
         // The panel is parented, so it will be z-ordered over its parent but
         // under the active window.
         // Now we move the mouse over the part where the panel rect intersects the
         // right window's rect. Since the panel is under the window, all the events
         // should target the right window.
-        // Try with sending to three different targets.
         [390, 170, NSMouseMoved, null, right, [
           { type: "mousemove", target: rightElem },
         ]],
-        [390, 171, NSMouseMoved, null, left, [
+        [390, 171, NSMouseMoved, null, right, [
           { type: "mousemove", target: rightElem },
         ]],
-        [391, 171, NSMouseMoved, panel, left, [
+        [391, 171, NSMouseMoved, null, right, [
           { type: "mousemove", target: rightElem },
         ]],
         // Now move off the right window, so that the mouse is directly over the
         // panel.
-        [260, 170, NSMouseMoved, null, left, [
+        [260, 170, NSMouseMoved, panel, left, [
           { type: "mouseout", target: rightElem },
           { type: "mouseover", target: panel },
           { type: "mousemove", target: panel },
         ]],
-        [260, 171, NSMouseMoved, null, left, [
+        [260, 171, NSMouseMoved, panel, left, [
           { type: "mousemove", target: panel },
         ]],
         [261, 171, NSMouseMoved, panel, left, [
           { type: "mousemove", target: panel },
         ]],
         // Let's be evil and click it.
         [261, 171, NSLeftMouseDown, panel, left, [
           { type: "mousedown", target: panel },
@@ -728,20 +726,20 @@
         [261, 171, NSLeftMouseUp, panel, left, [
           { type: "mouseup", target: panel },
           { type: "click", target: panel },
         ]],
         // This didn't focus the window, unfortunately, so let's do it ourselves.
         function raiseLeftWindowTakeTwo(callback) {
           focusAndThen(left, callback);
         },
-        [387, 170, NSMouseMoved, null, right, [
+        [387, 170, NSMouseMoved, panel, left, [
           { type: "mousemove", target: panel },
         ]],
-        [387, 171, NSMouseMoved, null, left, [
+        [387, 171, NSMouseMoved, panel, left, [
           { type: "mousemove", target: panel },
         ]],
         [388, 171, NSMouseMoved, panel, left, [
           { type: "mousemove", target: panel },
         ]],
         // Click the panel.
         [388, 171, NSLeftMouseDown, panel, left, [
           { type: "mousedown", target: panel }
@@ -749,22 +747,22 @@
         [388, 171, NSLeftMouseUp, panel, left, [
           { type: "mouseup", target: panel },
           { type: "click", target: panel },
         ]],
 
         // Last test for today: Hit testing in the Canyon of Nowhere -
         // the pixel row directly south of the panel, over the left window.
         // Before bug 515003 we wrongly thought the mouse wasn't over any window.
-        [173, 200, NSMouseMoved, panel, left, [
+        [173, 200, NSMouseMoved, null, left, [
           { type: "mouseout", target: panel },
           { type: "mouseover", target: leftElem },
           { type: "mousemove", target: leftElem },
         ]],
-        [173, 201, NSMouseMoved, panel, left, [
+        [173, 201, NSMouseMoved, null, left, [
           { type: "mousemove", target: leftElem },
         ]],
       ];
       function runNextTest() {
         if (!tests.length)
           return onTestsFinished();
 
         var test = tests.shift();
--- a/xpcom/components/ManifestParser.cpp
+++ b/xpcom/components/ManifestParser.cpp
@@ -15,16 +15,17 @@
  * The Original Code is Mozilla Firefox
  *
  * The Initial Developer of the Original Code is
  * the Mozilla Foundation <http://www.mozilla.org/>.
  * Portions created by the Initial Developer are Copyright (C) 2010
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
+ *   Philipp Kewisch <mozilla@kewis.ch>
  *
  * Alternatively, the contents of this file may be used under the terms of
  * either the GNU General Public License Version 2 or later (the "GPL"), or
  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  * in which case the provisions of the GPL or the LGPL are applicable instead
  * of those above. If you wish to allow use of your version of this file only
  * under the terms of either the GPL or the LGPL, and not to allow others to
  * use your version of this file under the terms of the MPL, indicate your
@@ -425,39 +426,45 @@ ParseManifestCommon(NSLocationType aType
                     const char* aPath, char* buf, bool aChromeOnly)
 {
   nsresult rv;
 
   NS_NAMED_LITERAL_STRING(kPlatform, "platform");
   NS_NAMED_LITERAL_STRING(kContentAccessible, "contentaccessible");
   NS_NAMED_LITERAL_STRING(kApplication, "application");
   NS_NAMED_LITERAL_STRING(kAppVersion, "appversion");
+  NS_NAMED_LITERAL_STRING(kGeckoVersion, "platformversion");
   NS_NAMED_LITERAL_STRING(kOs, "os");
   NS_NAMED_LITERAL_STRING(kOsVersion, "osversion");
   NS_NAMED_LITERAL_STRING(kABI, "abi");
 
   // Obsolete
   NS_NAMED_LITERAL_STRING(kXPCNativeWrappers, "xpcnativewrappers");
 
   nsAutoString appID;
   nsAutoString appVersion;
+  nsAutoString geckoVersion;
   nsAutoString osTarget;
   nsAutoString abi;
 
   nsCOMPtr<nsIXULAppInfo> xapp (do_GetService(XULAPPINFO_SERVICE_CONTRACTID));
   if (xapp) {
     nsCAutoString s;
     rv = xapp->GetID(s);
     if (NS_SUCCEEDED(rv))
       CopyUTF8toUTF16(s, appID);
 
     rv = xapp->GetVersion(s);
     if (NS_SUCCEEDED(rv))
       CopyUTF8toUTF16(s, appVersion);
 
+    rv = xapp->GetPlatformVersion(s);
+    if (NS_SUCCEEDED(rv))
+      CopyUTF8toUTF16(s, geckoVersion);
+
     nsCOMPtr<nsIXULRuntime> xruntime (do_QueryInterface(xapp));
     if (xruntime) {
       rv = xruntime->GetOS(s);
       if (NS_SUCCEEDED(rv)) {
         ToLowerCase(s);
         CopyUTF8toUTF16(s, osTarget);
       }
 
@@ -571,32 +578,34 @@ ParseManifestCommon(NSLocationType aType
       LogMessageWithContext(aFile, aPath, line,
                             "Not enough arguments for chrome manifest directive '%s', expected %i.",
                             token, directive->argc);
       continue;
     }
 
     bool ok = true;
     TriState stAppVersion = eUnspecified;
+    TriState stGeckoVersion = eUnspecified;
     TriState stApp = eUnspecified;
     TriState stOsVersion = eUnspecified;
     TriState stOs = eUnspecified;
     TriState stABI = eUnspecified;
     bool platform = false;
     bool contentAccessible = false;
 
     while (NULL != (token = nsCRT::strtok(whitespace, kWhitespace, &whitespace)) && ok) {
       ToLowerCase(token);
       NS_ConvertASCIItoUTF16 wtoken(token);
 
       if (CheckStringFlag(kApplication, wtoken, appID, stApp) ||
           CheckStringFlag(kOs, wtoken, osTarget, stOs) ||
           CheckStringFlag(kABI, wtoken, abi, stABI) ||
           CheckVersionFlag(kOsVersion, wtoken, osVersion, stOsVersion) ||
-          CheckVersionFlag(kAppVersion, wtoken, appVersion, stAppVersion))
+          CheckVersionFlag(kAppVersion, wtoken, appVersion, stAppVersion) ||
+          CheckVersionFlag(kGeckoVersion, wtoken, geckoVersion, stGeckoVersion))
         continue;
 
       if (directive->contentflags &&
           (CheckFlag(kPlatform, wtoken, platform) ||
            CheckFlag(kContentAccessible, wtoken, contentAccessible)))
         continue;
 
       bool xpcNativeWrappers = true; // Dummy for CheckFlag.
@@ -611,16 +620,17 @@ ParseManifestCommon(NSLocationType aType
                             "Unrecognized chrome manifest modifier '%s'.",
                             token);
       ok = false;
     }
 
     if (!ok ||
         stApp == eBad ||
         stAppVersion == eBad ||
+        stGeckoVersion == eBad ||
         stOs == eBad ||
         stOsVersion == eBad ||
         stABI == eBad)
       continue;
 
     if (directive->regfunc) {
       if (GeckoProcessType_Default != XRE_GetProcessType())
         continue;
--- a/xpcom/proxy/src/nsProxyObjectManager.cpp
+++ b/xpcom/proxy/src/nsProxyObjectManager.cpp
@@ -47,16 +47,17 @@
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsProxyEventPrivate.h"
 
 #include "nsIComponentManager.h"
 #include "nsIProxyObjectManager.h"
 #include "nsIServiceManager.h"
 #include "nsIThread.h"
+#include "nsIXPConnect.h"
 
 #include "nsCOMPtr.h"
 #include "nsThreadUtils.h"
 #include "xptiprivate.h"
 
 using namespace mozilla;
 
 #ifdef PR_LOGGING
@@ -203,16 +204,24 @@ nsProxyObjectManager::GetProxyForObject(
     nsCOMPtr<nsIThread> thread;
     if (aTarget == NS_PROXY_TO_CURRENT_THREAD) {
       aTarget = NS_GetCurrentThread();
     } else if (aTarget == NS_PROXY_TO_MAIN_THREAD) {
       thread = do_GetMainThread();
       aTarget = thread.get();
     }
 
+    if (nsCOMPtr<nsIXPConnectWrappedJS> wjs = do_QueryInterface(aObj)) {
+      // Only proxy wrapped JS from the main thread to the main thread
+      if (!NS_IsMainThread() || aTarget != NS_GetCurrentThread()) {
+        NS_ABORT_IF_FALSE(false, "GetProxyForObject on wrapped JS not allowed");
+        return NS_ERROR_FAILURE;
+      }
+    }
+
     // check to see if the target is on our thread.  If so, just return the
     // real object.
     
     if (!(proxyType & NS_PROXY_ASYNC) && !(proxyType & NS_PROXY_ALWAYS))
     {
         PRBool result;
         aTarget->IsOnCurrentThread(&result);