merge the last green changeset on m-c to fx-team
authorTim Taubert <tim.taubert@gmx.de>
Fri, 09 Sep 2011 13:53:06 +0200
changeset 78257 8b0512ae3405e012af4cf099986acd884838e5c7
parent 78256 0cb195525cbc102e95d21f679ef8e3c39235dbf0 (current diff)
parent 78081 59e530a7f0dd03ffe0ba2cec1510d1bc11e204b1 (diff)
child 78258 43f1efa4b17d442d0cd3c3867ed452431828f9e3
push id78
push userclegnitto@mozilla.com
push dateFri, 16 Dec 2011 17:32:24 +0000
treeherdermozilla-release@79d24e644fdd [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone9.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
merge the last green changeset on m-c to fx-team
--- a/browser/base/content/nsContextMenu.js
+++ b/browser/base/content/nsContextMenu.js
@@ -1031,17 +1031,17 @@ nsContextMenu.prototype = {
     this.saveHelper(this.linkURL, this.linkText(), null, true, doc);
   },
 
   sendLink: function() {
     // we don't know the title of the link so pass in an empty string
     MailIntegration.sendMessage( this.linkURL, "" );
   },
 
-  // Backwards-compatability wrapper
+  // Backwards-compatibility wrapper
   saveImage : function() {
     if (this.onCanvas || this.onImage)
         this.saveMedia();
   },
 
   // Save URL of the clicked upon image, video, or audio.
   saveMedia: function() {
     var doc =  this.target.ownerDocument;
@@ -1057,17 +1057,17 @@ nsContextMenu.prototype = {
     }
     else if (this.onVideo || this.onAudio) {
       urlSecurityCheck(this.mediaURL, doc.nodePrincipal);
       var dialogTitle = this.onVideo ? "SaveVideoTitle" : "SaveAudioTitle";
       this.saveHelper(this.mediaURL, null, dialogTitle, false, doc);
     }
   },
 
-  // Backwards-compatability wrapper
+  // Backwards-compatibility wrapper
   sendImage : function() {
     if (this.onCanvas || this.onImage)
         this.sendMedia();
   },
 
   sendMedia: function() {
     MailIntegration.sendMessage(this.mediaURL, "");
   },
--- a/browser/base/content/syncGenericChange.js
+++ b/browser/base/content/syncGenericChange.js
@@ -192,16 +192,17 @@ let Change = {
 
   doChangePassphrase: function Change_doChangePassphrase() {
     let pp = Weave.Utils.normalizePassphrase(this._passphraseBox.value);
     if (this._updatingPassphrase) {
       Weave.Service.passphrase = pp;
       if (Weave.Service.login()) {
         this._updateStatus("change.recoverykey.success", "success");
         Weave.Service.persistLogin();
+        Weave.SyncScheduler.delayedAutoConnect(0);
       }
       else {
         this._updateStatus("new.passphrase.status.incorrect", "error");
       }
     }
     else {
       this._updateStatus("change.recoverykey.label", "active");
 
--- a/browser/base/content/syncSetup.js
+++ b/browser/base/content/syncSetup.js
@@ -108,16 +108,22 @@ var gSyncSetup = {
           Weave.Svc.Obs.add(topic, self[func], self);
         else
           Weave.Svc.Obs.remove(topic, self[func], self);
       });
     };
     addRem(true);
     window.addEventListener("unload", function() addRem(false), false);
 
+    window.setTimeout(function () {
+      // Force Service to be loaded so that engines are registered.
+      // See Bug 670082.
+      Weave.Service;
+    }, 0);
+
     this.captchaBrowser = document.getElementById("captcha");
     this.wizard = document.getElementById("accountSetup");
 
     if (window.arguments && window.arguments[0] == true) {
       // we're resetting sync
       this._resettingSync = true;
       this.wizard.pageIndex = OPTIONS_PAGE;
     }
--- a/browser/base/content/test/browser_sanitize-download-history.js
+++ b/browser/base/content/test/browser_sanitize-download-history.js
@@ -68,17 +68,17 @@ function test()
   {
     let doc = aWin.document;
     let downloads = doc.getElementById("downloads-checkbox");
     let history = doc.getElementById("history-checkbox");
 
     // Add download to DB
     let file = Cc["@mozilla.org/file/directory_service;1"].
                getService(Ci.nsIProperties).get("TmpD", Ci.nsIFile);
-    file.append("satitize-dm-test.file");
+    file.append("sanitize-dm-test.file");
     file.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0666);
     let testPath = Services.io.newFileURI(file).spec;
     let data = {
       name: "381603.patch",
       source: "https://bugzilla.mozilla.org/attachment.cgi?id=266520",
       target: testPath,
       startTime: 1180493839859230,
       endTime: 1180493839859239,
--- a/browser/branding/official/pref/firefox-branding.js
+++ b/browser/branding/official/pref/firefox-branding.js
@@ -1,9 +1,10 @@
-pref("startup.homepage_override_url","http://www.mozilla.com/%LOCALE%/%APP%/%VERSION%/whatsnew/");
+// Fight update fatigue by suppressing whatsnew tab opening after update (bug 685727)
+pref("startup.homepage_override_url","");
 pref("startup.homepage_welcome_url","http://www.mozilla.com/%LOCALE%/%APP%/%VERSION%/firstrun/");
 // Interval: Time between checks for a new version (in seconds)
 // nightly=6 hours, official=24 hours
 pref("app.update.interval", 86400);
 // The time interval between the downloading of mar file chunks in the
 // background (in seconds)
 pref("app.update.download.backgroundInterval", 600);
 // URL user can browse to manually if for some reason all update installation
--- a/content/base/public/nsIContentPolicy.idl
+++ b/content/base/public/nsIContentPolicy.idl
@@ -131,16 +131,18 @@ interface nsIContentPolicy : nsISupports
    */
   const unsigned long TYPE_FONT = 14;
 
   /**
    * Indicates a video or audio load.
    */
   const unsigned long TYPE_MEDIA = 15;  
 
+  /* Please update nsContentBlocker when adding new content types. */
+
   //////////////////////////////////////////////////////////////////////
 
   /**
    * Returned from shouldLoad or shouldProcess if the load or process request
    * is rejected based on details of the request.
    */
   const short REJECT_REQUEST = -1;
 
--- a/content/html/content/src/nsHTMLFrameSetElement.cpp
+++ b/content/html/content/src/nsHTMLFrameSetElement.cpp
@@ -317,17 +317,17 @@ nsHTMLFrameSetElement::ParseRowCol(const
       // Treat 0* as 1* in quirks mode (bug 40383)
       if (isInQuirks) {
         if ((eFramesetUnit_Relative == specs[i].mUnit) &&
           (0 == specs[i].mValue)) {
           specs[i].mValue = 1;
         }
       }
         
-      // Catch zero and negative frame sizes for Nav compatability
+      // Catch zero and negative frame sizes for Nav compatibility
       // Nav resized absolute and relative frames to "1" and
       // percent frames to an even percentage of the width
       //
       //if (isInQuirks && (specs[i].mValue <= 0)) {
       //  if (eFramesetUnit_Percent == specs[i].mUnit) {
       //    specs[i].mValue = 100 / count;
       //  } else {
       //    specs[i].mValue = 1;
--- a/content/media/ogg/nsOggCodecState.cpp
+++ b/content/media/ogg/nsOggCodecState.cpp
@@ -392,17 +392,17 @@ nsTheoraState::PageIn(ogg_page* aPage)
       mPackets.Append(packet);
     }
     mUnstamped.Clear();
   }
   return NS_OK;
 }
 
 // Returns 1 if the Theora info struct is decoding a media of Theora
-// verion (maj,min,sub) or later, otherwise returns 0.
+// version (maj,min,sub) or later, otherwise returns 0.
 int
 TheoraVersion(th_info* info,
               unsigned char maj,
               unsigned char min,
               unsigned char sub)
 {
   ogg_uint32_t ver = (maj << 16) + (min << 8) + sub;
   ogg_uint32_t th_ver = (info->version_major << 16) +
--- a/content/media/ogg/nsOggCodecState.h
+++ b/content/media/ogg/nsOggCodecState.h
@@ -268,17 +268,17 @@ public:
   // Asserts that the number of samples predicted for aPacket is aSamples.
   // This function has no effect if VALIDATE_VORBIS_SAMPLE_CALCULATION
   // is not defined.
   void ValidateVorbisPacketSamples(ogg_packet* aPacket, long aSamples);
 
 };
 
 // Returns 1 if the Theora info struct is decoding a media of Theora
-// verion (maj,min,sub) or later, otherwise returns 0.
+// version (maj,min,sub) or later, otherwise returns 0.
 int TheoraVersion(th_info* info,
                   unsigned char maj,
                   unsigned char min,
                   unsigned char sub);
 
 class nsTheoraState : public nsOggCodecState {
 public:
   nsTheoraState(ogg_page* aBosPage);
--- a/docshell/base/nsDocShell.h
+++ b/docshell/base/nsDocShell.h
@@ -726,17 +726,21 @@ protected:
     nsCOMPtr<nsISHistory>      mSessionHistory;
     nsCOMPtr<nsIGlobalHistory2> mGlobalHistory;
     nsCOMPtr<nsIWebBrowserFind> mFind;
     nsCOMPtr<nsICommandManager> mCommandManager;
     // Reference to the SHEntry for this docshell until the page is destroyed.
     // Somebody give me better name
     nsCOMPtr<nsISHEntry>       mOSHE;
     // Reference to the SHEntry for this docshell until the page is loaded
-    // Somebody give me better name
+    // Somebody give me better name.
+    // If mLSHE is non-null, non-pushState subframe loads don't create separate
+    // root history entries. That is, frames loaded during the parent page
+    // load don't generate history entries the way frame navigation after the
+    // parent has loaded does. (This isn't the only purpose of mLSHE.)
     nsCOMPtr<nsISHEntry>       mLSHE;
 
     // Holds a weak pointer to a RestorePresentationEvent object if any that
     // holds a weak pointer back to us.  We use this pointer to possibly revoke
     // the event whenever necessary.
     nsRevocableEventPtr<RestorePresentationEvent> mRestorePresentationEvent;
 
     // Editor data, if this document is designMode or contentEditable.
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -521,16 +521,17 @@ static const char kDOMStringBundleURL[] 
   nsIXPCScriptable::WANT_FINALIZE |                                           \
   nsIXPCScriptable::WANT_EQUALITY |                                           \
   nsIXPCScriptable::WANT_ENUMERATE |                                          \
   nsIXPCScriptable::DONT_ENUM_QUERY_INTERFACE |                               \
   nsIXPCScriptable::WANT_OUTER_OBJECT)
 
 #define NODE_SCRIPTABLE_FLAGS                                                 \
  ((DOM_DEFAULT_SCRIPTABLE_FLAGS |                                             \
+   nsIXPCScriptable::USE_STUB_EQUALITY_HOOK |                                 \
    nsIXPCScriptable::WANT_GETPROPERTY |                                       \
    nsIXPCScriptable::WANT_ADDPROPERTY |                                       \
    nsIXPCScriptable::WANT_SETPROPERTY) &                                      \
   ~nsIXPCScriptable::USE_JSSTUB_FOR_ADDPROPERTY)
 
 // We need to let JavaScript QI elements to interfaces that are not in
 // the classinfo since XBL can be used to dynamically implement new
 // unknown interfaces on elements, accessibility relies on this being
@@ -6380,29 +6381,40 @@ LocationSetterGuts(JSContext *cx, JSObje
 
   nsCOMPtr<Interface> xpcomObj = do_QueryWrappedNative(wrapper);
   NS_ENSURE_TRUE(xpcomObj, NS_ERROR_UNEXPECTED);
 
   nsCOMPtr<nsIDOMLocation> location;
   nsresult rv = xpcomObj->GetLocation(getter_AddRefs(location));
   NS_ENSURE_SUCCESS(rv, rv);
 
+  // Grab the value we're being set to before we stomp on |vp|
   JSString *val = ::JS_ValueToString(cx, *vp);
   NS_ENSURE_TRUE(val, NS_ERROR_UNEXPECTED);
 
+  // Make sure |val| stays alive below
+  JS::Anchor<JSString *> anchor(val);
+
+  // We have to wrap location into vp before null-checking location, to
+  // avoid assigning the wrong thing into the slot.
+  nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
+  rv = WrapNative(cx, JS_GetGlobalForScopeChain(cx), location,
+                  &NS_GET_IID(nsIDOMLocation), PR_TRUE, vp,
+                  getter_AddRefs(holder));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (!location) {
+    // Make this a no-op
+    return NS_OK;
+  }
+
   nsDependentJSString depStr;
   NS_ENSURE_TRUE(depStr.init(cx, val), NS_ERROR_UNEXPECTED);
   
-  rv = location->SetHref(depStr);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
-  return WrapNative(cx, JS_GetGlobalForScopeChain(cx), location,
-                    &NS_GET_IID(nsIDOMLocation), PR_TRUE, vp,
-                    getter_AddRefs(holder));
+  return location->SetHref(depStr);
 }
 
 template<class Interface>
 static JSBool
 LocationSetter(JSContext *cx, JSObject *obj, jsid id, JSBool strict,
                jsval *vp)
 {
   nsresult rv = LocationSetterGuts<Interface>(cx, obj, vp);
--- a/dom/tests/mochitest/bugs/Makefile.in
+++ b/dom/tests/mochitest/bugs/Makefile.in
@@ -136,16 +136,17 @@ include $(topsrcdir)/config/rules.mk
 		test_bug617296.html \
 		test_bug620947.html \
 		test_bug622361.html \
 		test_bug633133.html \
 		test_bug641552.html \
 		test_bug642026.html \
 		test_bug648465.html \
 		test_bug654137.html \
+		test_bug684544.html \
 		test_window_bar.html \
 		file_window_bar.html \
 		test_resize_move_windows.html \
 		test_devicemotion_multiple_listeners.html \
 		devicemotion_outer.html \
 		devicemotion_inner.html \
 		$(NULL)
 
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/bugs/test_bug684544.html
@@ -0,0 +1,48 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=
+-->
+<head>
+  <title>Test for Bug </title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=">Mozilla Bug </a>
+<p id="display"></p>
+<div id="content" style="display: none">
+  
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug  **/
+
+var f = document.createElement("iframe");
+document.body.appendChild(f);
+var win = f.contentWindow;
+
+// Set location once to make sure it's resolved
+win.location = "data:text/html,1";
+
+// Now try to make the location object go away.
+f.parentNode.removeChild(f);
+
+// Check that location is now null.  If it's not, the test needs changing
+// (e.g. to use window.close() so that it's null).
+is("location" in win, true, "Should still have a location property");
+todo_is(win.location, null, "There should be no location object now");
+
+// Just set the location.  This should not crash.
+win.location = "data:text/html,2";
+
+// And check that we didn't override the slot in the process.
+is(typeof(win.location), "object", "Location should not have become a string");
+is(win.location, null,
+   "There should be no location object for real after the set");
+
+</script>
+</pre>
+</body>
+</html>
--- a/editor/composer/src/crashtests/crashtests.list
+++ b/editor/composer/src/crashtests/crashtests.list
@@ -1,6 +1,6 @@
 asserts-if(Android,2) load 351236-1.html
 load 407062-1.html
 load 419563-1.xhtml
 skip-if(winWidget) load 428844-1.html # bug 471185
 load 461049-1.html
-asserts(0-1) asserts-if(winWidget,0-2) load removing-editable-xslt.html # bug 500847
+asserts(0-1) asserts-if(winWidget||Android,0-2) load removing-editable-xslt.html # bug 500847
--- a/editor/libeditor/html/nsHTMLAnonymousUtils.cpp
+++ b/editor/libeditor/html/nsHTMLAnonymousUtils.cpp
@@ -363,39 +363,41 @@ nsHTMLEditor::CheckSelectionStateForAnon
   if (mIsInlineTableEditingEnabled && mInlineEditedCell &&
       mInlineEditedCell != cellElement) {
     res = HideInlineTableEditingUI();
     NS_ENSURE_SUCCESS(res, res);
     NS_ASSERTION(!mInlineEditedCell, "HideInlineTableEditingUI failed");
   }
 
   // now, let's display all contextual UI for good
+  nsIContent* hostContent = GetActiveEditingHost();
+  nsCOMPtr<nsIDOMNode> hostNode = do_QueryInterface(hostContent);
 
   if (mIsObjectResizingEnabled && focusElement &&
-      IsModifiableNode(focusElement)) {
+      IsModifiableNode(focusElement) && focusElement != hostNode) {
     if (nsEditProperty::img == focusTagAtom)
       mResizedObjectIsAnImage = PR_TRUE;
     if (mResizedObject)
       res = RefreshResizers();
     else
       res = ShowResizers(focusElement);
     NS_ENSURE_SUCCESS(res, res);
   }
 
   if (mIsAbsolutelyPositioningEnabled && absPosElement &&
-      IsModifiableNode(absPosElement)) {
+      IsModifiableNode(absPosElement) && absPosElement != hostNode) {
     if (mAbsolutelyPositionedObject)
       res = RefreshGrabber();
     else
       res = ShowGrabberOnElement(absPosElement);
     NS_ENSURE_SUCCESS(res, res);
   }
 
   if (mIsInlineTableEditingEnabled && cellElement &&
-      IsModifiableNode(cellElement)) {
+      IsModifiableNode(cellElement) && cellElement != hostNode) {
     if (mInlineEditedCell)
       res = RefreshInlineTableEditingUI();
     else
       res = ShowInlineTableEditingUI(cellElement);
   }
 
   return res;
 }
--- a/editor/libeditor/text/nsInternetCiter.cpp
+++ b/editor/libeditor/text/nsInternetCiter.cpp
@@ -78,17 +78,17 @@ nsInternetCiter::GetCiteString(const nsA
 
   // Loop over the string:
   while (beginIter != endIter)
   {
     if (uch == nl)
     {
       aOutString.Append(gt);
       // No space between >: this is ">>> " style quoting, for
-      // compatability with RFC 2646 and format=flowed.
+      // compatibility with RFC 2646 and format=flowed.
       if (*beginIter != gt)
         aOutString.Append(space);
     }
 
     uch = *beginIter;
     ++beginIter;
 
     aOutString += uch;
--- a/embedding/android/AndroidManifest.xml.in
+++ b/embedding/android/AndroidManifest.xml.in
@@ -5,17 +5,17 @@
       android:installLocation="auto"
       android:versionCode="@ANDROID_VERSION_CODE@"
       android:versionName="@MOZ_APP_VERSION@"
 #ifdef MOZ_ANDROID_SHARED_ID
       android:sharedUserId="@MOZ_ANDROID_SHARED_ID@"
 #endif
       >
     <uses-sdk android:minSdkVersion="5"
-              android:targetSdkVersion="5"/>
+              android:targetSdkVersion="11"/>
 
     <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
     <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
     <uses-permission android:name="android.permission.INTERNET"/>
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
     <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
     <uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT"/>
 
--- a/extensions/permissions/nsContentBlocker.cpp
+++ b/extensions/permissions/nsContentBlocker.cpp
@@ -48,30 +48,34 @@
 
 // Possible behavior pref values
 // Those map to the nsIPermissionManager values where possible
 #define BEHAVIOR_ACCEPT nsIPermissionManager::ALLOW_ACTION
 #define BEHAVIOR_REJECT nsIPermissionManager::DENY_ACTION
 #define BEHAVIOR_NOFOREIGN 3
 
 // From nsIContentPolicy
-static const char *kTypeString[NUMBER_OF_TYPES] = {"other", 
-                                                   "script",
-                                                   "image",
-                                                   "stylesheet",
-                                                   "object",
-                                                   "document",
-                                                   "subdocument",
-                                                   "refresh",
-                                                   "xbl",
-                                                   "ping",
-                                                   "xmlhttprequest",
-                                                   "objectsubrequest",
-                                                   "dtd"};
+static const char *kTypeString[] = {"other",
+                                    "script",
+                                    "image",
+                                    "stylesheet",
+                                    "object",
+                                    "document",
+                                    "subdocument",
+                                    "refresh",
+                                    "xbl",
+                                    "ping",
+                                    "xmlhttprequest",
+                                    "objectsubrequest",
+                                    "dtd",
+                                    "font",
+                                    "media"};
 
+#define NUMBER_OF_TYPES NS_ARRAY_LENGTH(kTypeString)
+PRUint8 nsContentBlocker::mBehaviorPref[NUMBER_OF_TYPES];
 
 NS_IMPL_ISUPPORTS3(nsContentBlocker, 
                    nsIContentPolicy,
                    nsIObserver,
                    nsSupportsWeakReference)
 
 nsContentBlocker::nsContentBlocker()
 {
--- a/extensions/permissions/nsContentBlocker.h
+++ b/extensions/permissions/nsContentBlocker.h
@@ -41,19 +41,16 @@
 #include "nsWeakReference.h"
 #include "nsIPermissionManager.h"
 #include "nsIPrefBranch2.h"
 
 class nsIPrefBranch;
 
 ////////////////////////////////////////////////////////////////////////////////
 
-// number of permission types in nsIContentPolicy
-#define NUMBER_OF_TYPES 13
-
 class nsContentBlocker : public nsIContentPolicy,
                          public nsIObserver,
                          public nsSupportsWeakReference
 {
 public:
 
   // nsISupports
   NS_DECL_ISUPPORTS
@@ -70,17 +67,17 @@ private:
   nsresult TestPermission(nsIURI *aCurrentURI,
                           nsIURI *aFirstURI,
                           PRInt32 aContentType,
                           PRBool *aPermission,
                           PRBool *aFromPrefs);
 
   nsCOMPtr<nsIPermissionManager> mPermissionManager;
   nsCOMPtr<nsIPrefBranch2> mPrefBranchInternal;
-  PRUint8 mBehaviorPref[NUMBER_OF_TYPES];
+  static PRUint8 mBehaviorPref[];
 };
 
 #define NS_CONTENTBLOCKER_CID \
 { 0x4ca6b67b, 0x5cc7, 0x4e71, \
   { 0xa9, 0x8a, 0x97, 0xaf, 0x1c, 0x13, 0x48, 0x62 } }
 
 #define NS_CONTENTBLOCKER_CONTRACTID "@mozilla.org/permissions/contentblocker;1"
 
--- a/gfx/thebes/GLContext.cpp
+++ b/gfx/thebes/GLContext.cpp
@@ -431,17 +431,16 @@ static const char *sExtensionNames[] = {
     "GL_IMG_read_format",
     "GL_EXT_read_format_bgra",
     "GL_APPLE_client_storage",
     "GL_ARB_texture_non_power_of_two",
     "GL_ARB_pixel_buffer_object",
     "GL_ARB_ES2_compatibility",
     "GL_OES_texture_float",
     "GL_ARB_texture_float",
-    "GL_EXT_unpack_subimage",
     NULL
 };
 
 void
 GLContext::InitExtensions()
 {
     MakeCurrent();
     const GLubyte *extensions = fGetString(LOCAL_GL_EXTENSIONS);
@@ -1835,27 +1834,19 @@ GLContext::TexImage2D(GLenum target, GLi
                       GLint pixelsize, GLint border, GLenum format, 
                       GLenum type, const GLvoid *pixels)
 {
     fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, 
                  NS_MIN(GetAddressAlignment((ptrdiff_t)pixels),
                         GetAddressAlignment((ptrdiff_t)stride)));
 
 #ifndef USE_GLES2
-    bool useUnpackRowLength = true;
+    fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH, stride/pixelsize);
 #else
-    // A Khronos extension, GL_EXT_unpack_subimage, that restores support
-    // for GL_UNPACK_ROW_LENGTH, GL_UNPACK_SKIP_ROWS and GL_UNPACK_SKIP_PIXELS
-    // exists on Tegra 2 (and possibly other chipsets)
-    bool useUnpackRowLength = IsExtensionSupported(EXT_unpack_subimage);
-#endif
-
-    if (useUnpackRowLength)
-        fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH, stride/pixelsize);
-    else if (stride != width * pixelsize) {
+    if (stride != width * pixelsize) {
         // Not using the whole row of texture data and GLES doesn't 
         // support GL_UNPACK_ROW_LENGTH. We need to upload each row
         // separately.
         fTexImage2D(target,
                     border,
                     internalformat,
                     width,
                     height,
@@ -1877,29 +1868,31 @@ GLContext::TexImage2D(GLenum target, GLi
                            row);
 
             row += stride;
         }
 
         fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, 4);
         return;
     }
+#endif
 
     fTexImage2D(target,
                 level,
                 internalformat,
                 width,
                 height,
                 border,
                 format,
                 type,
                 pixels);
 
-    if (useUnpackRowLength)
-        fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH, 0);
+#ifndef USE_GLES2
+    fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH, 0);
+#endif
     fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, 4);
 }
 
 void
 GLContext::TexSubImage2D(GLenum target, GLint level, 
                          GLint xoffset, GLint yoffset, 
                          GLsizei width, GLsizei height, GLsizei stride,
                          GLint pixelsize, GLenum format, 
--- a/gfx/thebes/GLContext.h
+++ b/gfx/thebes/GLContext.h
@@ -975,17 +975,16 @@ public:
         IMG_read_format,
         EXT_read_format_bgra,
         APPLE_client_storage,
         ARB_texture_non_power_of_two,
         ARB_pixel_buffer_object,
         ARB_ES2_compatibility,
         OES_texture_float,
         ARB_texture_float,
-        EXT_unpack_subimage,
         Extensions_Max
     };
 
     PRBool IsExtensionSupported(GLExtensions aKnownExtension) {
         return mAvailableExtensions[aKnownExtension];
     }
 
     // for unknown extensions
--- a/gfx/thebes/gfxFT2FontList.cpp
+++ b/gfx/thebes/gfxFT2FontList.cpp
@@ -222,17 +222,19 @@ gfxFT2FontList::FindFonts()
         {
             nsCString s("/system/fonts");
             s.Append("/");
             s.Append(nsDependentCString(ent->d_name));
 
             AppendFacesFromFontFile(nsPromiseFlatCString(s).get());
         }
     }
-
+    if (d) {
+      closedir(d);
+    }
     mCodepointsWithNoFonts.SetRange(0,0x1f);     // C0 controls
     mCodepointsWithNoFonts.SetRange(0x7f,0x9f);  // C1 controls
 
 #endif // XP_WIN && ANDROID
 }
 
 nsresult
 gfxFT2FontList::InitFontList()
--- a/intl/lwbrk/src/nsJISx4501LineBreaker.cpp
+++ b/intl/lwbrk/src/nsJISx4501LineBreaker.cpp
@@ -455,17 +455,17 @@ GetClass(PRUnichar u)
      c = GETCLASSFROMTABLE(gLBClass20, l);
    } else if (0x2100 == h) {
      c = GETCLASSFROMTABLE(gLBClass21, l);
    } else if (0x3000 == h) {
      c = GETCLASSFROMTABLE(gLBClass30, l);
    } else if (((0x3200 <= u) && (u <= 0xA4CF)) || // CJK and Yi
               ((0xAC00 <= h) && (h <= 0xD7FF)) || // Hangul
               ((0xf900 <= h) && (h <= 0xfaff))) {
-     c = CLASS_BREAKABLE; // CJK character, Han, and Han Compatability
+     c = CLASS_BREAKABLE; // CJK character, Han, and Han Compatibility
    } else if (0xff00 == h) {
      if (l < 0x0060) { // Fullwidth ASCII variant
        c = GETCLASSFROMTABLE(gLBClass00, (l+0x20));
      } else if (l < 0x00a0) {
        switch (l) {
          case 0x61: c = GetClass(0x3002); break;
          case 0x62: c = GetClass(0x300c); break;
          case 0x63: c = GetClass(0x300d); break;
--- a/intl/uconv/src/nsUnicodeToUTF8.cpp
+++ b/intl/uconv/src/nsUnicodeToUTF8.cpp
@@ -48,17 +48,17 @@ NS_IMPL_ISUPPORTS1(nsUnicodeToUTF8, nsIU
 
 NS_IMETHODIMP nsUnicodeToUTF8::GetMaxLength(const PRUnichar * aSrc, 
                                               PRInt32 aSrcLength,
                                               PRInt32 * aDestLength)
 {
   // aSrc is interpreted as UTF16, 3 is normally enough.
   // But when previous buffer only contains part of the surrogate pair, we 
   // need to complete it here. If the first word in following buffer is not
-  // in valid surrogate rang, we need to convert the remaining of last buffer 
+  // in valid surrogate range, we need to convert the remaining of last buffer
   // to 3 bytes.
   *aDestLength = 3*aSrcLength + 3;
   return NS_OK;
 }
 
 NS_IMETHODIMP nsUnicodeToUTF8::Convert(const PRUnichar * aSrc, 
                                 PRInt32 * aSrcLength, 
                                 char * aDest, 
--- a/ipc/chromium/src/base/histogram.cc
+++ b/ipc/chromium/src/base/histogram.cc
@@ -125,16 +125,27 @@ void Histogram::Add(int value) {
   if (value < 0)
     value = 0;
   size_t index = BucketIndex(value);
   DCHECK_GE(value, ranges(index));
   DCHECK_LT(value, ranges(index + 1));
   Accumulate(value, 1, index);
 }
 
+void Histogram::Subtract(int value) {
+  if (value > kSampleType_MAX - 1)
+    value = kSampleType_MAX - 1;
+  if (value < 0)
+    value = 0;
+  size_t index = BucketIndex(value);
+  DCHECK_GE(value, ranges(index));
+  DCHECK_LT(value, ranges(index + 1));
+  Accumulate(value, -1, index);
+}
+
 void Histogram::AddBoolean(bool value) {
   DCHECK(false);
 }
 
 void Histogram::AddSampleSet(const SampleSet& sample) {
   sample_.Add(sample);
 }
 
--- a/ipc/chromium/src/base/histogram.h
+++ b/ipc/chromium/src/base/histogram.h
@@ -376,16 +376,17 @@ class Histogram {
                                Flags flags);
   static Histogram* FactoryTimeGet(const std::string& name,
                                    base::TimeDelta minimum,
                                    base::TimeDelta maximum,
                                    size_t bucket_count,
                                    Flags flags);
 
   void Add(int value);
+  void Subtract(int value);
 
   // This method is an interface, used only by BooleanHistogram.
   virtual void AddBoolean(bool value);
 
   // Accept a TimeDelta to increment.
   void AddTime(TimeDelta time) {
     Add(static_cast<int>(time.InMilliseconds()));
   }
--- a/js/src/Makefile.in
+++ b/js/src/Makefile.in
@@ -150,16 +150,17 @@ CPPSRCS		= \
 		jsutil.cpp \
 		jswatchpoint.cpp \
 		jsweakmap.cpp \
 		jswrapper.cpp \
 		jsxdrapi.cpp \
 		jsxml.cpp \
 		prmjtime.cpp \
 		sharkctl.cpp \
+		CallObject.cpp \
 		Debugger.cpp \
 		GlobalObject.cpp \
 		Stack.cpp \
 		String.cpp \
 		ParseMaps.cpp \
 		Unicode.cpp \
 		$(NULL)
 
@@ -242,16 +243,17 @@ VPATH		+= \
 		$(srcdir)/vm \
 		$(srcdir)/frontend \
 		$(NULL)
 
 EXPORTS_NAMESPACES = vm
 
 EXPORTS_vm = \
 		ArgumentsObject.h \
+		CallObject.h \
 		GlobalObject.h \
 		Stack.h \
 		String.h \
 		StringObject.h \
 		Unicode.h  \
 		$(NULL)
 
 ###############################################
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/closures/bug684178.js
@@ -0,0 +1,9 @@
+var obj = {};
+(function() {
+    if (obj) {
+        function f() { obj.x = 1; }
+        obj.m = function() { f(); };
+    }
+})();
+obj.m();
+assertEq(obj.x, 1);
--- a/js/src/jit-test/tests/sunspider/check-3d-raytrace.js
+++ b/js/src/jit-test/tests/sunspider/check-3d-raytrace.js
@@ -307,17 +307,16 @@ Camera.prototype.render = function(scene
     var row = 0;
     renderRows(cam, scene, pixels, width, height, 0, height);
 }
 
 
 
 function raytraceScene()
 {
-    var startDate = new Date().getTime();
     var numTriangles = 2 * 6;
     var triangles = new Array();//numTriangles);
     var tfl = createVector(-10,  10, -10);
     var tfr = createVector( 10,  10, -10);
     var tbl = createVector(-10,  10,  10);
     var tbr = createVector( 10,  10,  10);
     var bfl = createVector(-10, -10, -10);
     var bfr = createVector( 10, -10, -10);
--- a/js/src/jit-test/tests/sunspider/check-crypto-aes.js
+++ b/js/src/jit-test/tests/sunspider/check-crypto-aes.js
@@ -171,17 +171,18 @@ function AESEncryptCtr(plaintext, passwo
   for (var i=0; i<nBytes; i++) pwBytes[i] = password.charCodeAt(i) & 0xff;
   var key = Cipher(pwBytes, KeyExpansion(pwBytes));
   key = key.concat(key.slice(0, nBytes-16));  // key is now 16/24/32 bytes long
 
   // initialise counter block (NIST SP800-38A §B.2): millisecond time-stamp for nonce in 1st 8 bytes,
   // block counter in 2nd 8 bytes
   var blockSize = 16;  // block size fixed at 16 bytes / 128 bits (Nb=4) for AES
   var counterBlock = new Array(blockSize);  // block size fixed at 16 bytes / 128 bits (Nb=4) for AES
-  var nonce = (new Date()).getTime();  // milliseconds since 1-Jan-1970
+  var nonce = (new Date("2000-01-01")).getTime();  // milliseconds since 1-Jan-1970;
+                                                   // fixed for repeatability
 
   // encode nonce in two stages to cater for JavaScript 32-bit limit on bitwise ops
   for (var i=0; i<4; i++) counterBlock[i] = (nonce >>> i*8) & 0xff;
   for (var i=0; i<4; i++) counterBlock[i+4] = (nonce/0x100000000 >>> i*8) & 0xff; 
 
   // generate key schedule - an expansion of the key into distinct Key Rounds for each round
   var keySchedule = KeyExpansion(key);
 
--- a/js/src/jit-test/tests/sunspider/check-math-cordic.js
+++ b/js/src/jit-test/tests/sunspider/check-math-cordic.js
@@ -79,22 +79,16 @@ function cordicsincos() {
     return CurrAngle;
 }
 
 ///// End CORDIC
 
 function cordic( runs ) {
   var actual;
 
-  var start = new Date();
-
   for ( var i = 0 ; i < runs ; i++ ) {
     actual = cordicsincos();
   }
 
-  var end = new Date();
-
   assertEq(actual, 1834995.3515519998)
-
-  return end.getTime() - start.getTime();
 }
 
 cordic(25000);
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -1990,17 +1990,17 @@ struct JSClass {
     JSCheckAccessOp     checkAccess;
     JSNative            call;
     JSNative            construct;
     JSXDRObjectOp       xdrObject;
     JSHasInstanceOp     hasInstance;
     JSTraceOp           trace;
 
     JSClassInternal     reserved1;
-    void                *reserved[19];
+    void                *reserved[26];
 };
 
 #define JSCLASS_HAS_PRIVATE             (1<<0)  /* objects have private slot */
 #define JSCLASS_NEW_ENUMERATE           (1<<1)  /* has JSNewEnumerateOp hook */
 #define JSCLASS_NEW_RESOLVE             (1<<2)  /* has JSNewResolveOp hook */
 #define JSCLASS_PRIVATE_IS_NSISUPPORTS  (1<<3)  /* private is (nsISupports *) */
 #define JSCLASS_CONCURRENT_FINALIZER    (1<<4)  /* finalize is called on background thread */
 #define JSCLASS_NEW_RESOLVE_GETS_START  (1<<5)  /* JSNewResolveOp gets starting
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -718,16 +718,26 @@ array_lookupProperty(JSContext *cx, JSOb
     if (!proto) {
         *objp = NULL;
         *propp = NULL;
         return JS_TRUE;
     }
     return proto->lookupProperty(cx, id, objp, propp);
 }
 
+static JSBool
+array_lookupElement(JSContext *cx, JSObject *obj, uint32 index, JSObject **objp,
+                    JSProperty **propp)
+{
+    jsid id;
+    if (!IndexToId(cx, index, &id))
+        return false;
+    return array_lookupProperty(cx, obj, id, objp, propp);
+}
+
 JSBool
 js_GetDenseArrayElementValue(JSContext *cx, JSObject *obj, jsid id, Value *vp)
 {
     JS_ASSERT(obj->isDenseArray());
 
     uint32 i;
     if (!js_IdIsIndex(id, &i)) {
         JS_ASSERT(JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom));
@@ -785,16 +795,25 @@ array_getProperty(JSContext *cx, JSObjec
     /* Type information for dense array elements must be correct. */
     JS_ASSERT_IF(!obj->hasSingletonType(),
                  js::types::TypeHasProperty(cx, obj->type(), JSID_VOID, *vp));
 
     return JS_TRUE;
 }
 
 static JSBool
+array_getElement(JSContext *cx, JSObject *obj, JSObject *receiver, uint32 index, Value *vp)
+{
+    jsid id;
+    if (!IndexToId(cx, index, &id))
+        return false;
+    return array_getProperty(cx, obj, receiver, id, vp);
+}
+
+static JSBool
 slowarray_addProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp)
 {
     jsuint index, length;
 
     if (!js_IdIsIndex(id, &index))
         return JS_TRUE;
     length = obj->getArrayLength();
     if (index >= length)
@@ -839,16 +858,25 @@ array_setProperty(JSContext *cx, JSObjec
         return true;
     } while (false);
 
     if (!obj->makeDenseArraySlow(cx))
         return false;
     return js_SetPropertyHelper(cx, obj, id, 0, vp, strict);
 }
 
+static JSBool
+array_setElement(JSContext *cx, JSObject *obj, uint32 index, Value *vp, JSBool strict)
+{
+    jsid id;
+    if (!IndexToId(cx, index, &id))
+        return false;
+    return array_setProperty(cx, obj, id, vp, strict);
+}
+
 JSBool
 js_PrototypeHasIndexedProperties(JSContext *cx, JSObject *obj)
 {
     /*
      * Walk up the prototype chain and see if this indexed element already
      * exists. If we hit the end of the prototype chain, it's safe to set the
      * element on the original object.
      */
@@ -863,16 +891,17 @@ js_PrototypeHasIndexedProperties(JSConte
         if (obj->isIndexed())
             return JS_TRUE;
     }
     return JS_FALSE;
 }
 
 namespace js {
 
+/* non-static for direct definition of array elements within the engine */
 JSBool
 array_defineProperty(JSContext *cx, JSObject *obj, jsid id, const Value *value,
                      PropertyOp getter, StrictPropertyOp setter, uintN attrs)
 {
     if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom))
         return JS_TRUE;
 
     if (!obj->isDenseArray())
@@ -898,36 +927,66 @@ array_defineProperty(JSContext *cx, JSOb
         return true;
     } while (false);
 
     if (!obj->makeDenseArraySlow(cx))
         return false;
     return js_DefineProperty(cx, obj, id, value, getter, setter, attrs);
 }
 
+/* non-static for direct definition of array elements within the engine */
+JSBool
+array_defineElement(JSContext *cx, JSObject *obj, uint32 index, const Value *value,
+                    PropertyOp getter, StrictPropertyOp setter, uintN attrs)
+{
+    jsid id;
+    if (!IndexToId(cx, index, &id))
+        return false;
+    return array_defineProperty(cx, obj, id, value, getter, setter, attrs);
+}
+
 } // namespace js
 
 static JSBool
 array_getAttributes(JSContext *cx, JSObject *obj, jsid id, uintN *attrsp)
 {
     *attrsp = JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)
         ? JSPROP_PERMANENT : JSPROP_ENUMERATE;
     return JS_TRUE;
 }
 
 static JSBool
+array_getElementAttributes(JSContext *cx, JSObject *obj, uint32 index, uintN *attrsp)
+{
+    jsid id;
+    if (!IndexToId(cx, index, &id))
+        return false;
+    return array_getAttributes(cx, obj, id, attrsp);
+}
+
+static JSBool
 array_setAttributes(JSContext *cx, JSObject *obj, jsid id, uintN *attrsp)
 {
     JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
                          JSMSG_CANT_SET_ARRAY_ATTRS);
     return JS_FALSE;
 }
 
+static JSBool
+array_setElementAttributes(JSContext *cx, JSObject *obj, uint32 index, uintN *attrsp)
+{
+    jsid id;
+    if (!IndexToId(cx, index, &id))
+        return false;
+    return array_setAttributes(cx, obj, id, attrsp);
+}
+
 namespace js {
 
+/* non-static for direct deletion of array elements within the engine */
 JSBool
 array_deleteProperty(JSContext *cx, JSObject *obj, jsid id, Value *rval, JSBool strict)
 {
     uint32 i;
 
     if (!obj->isDenseArray())
         return js_DeleteProperty(cx, obj, id, rval, strict);
 
@@ -943,16 +1002,26 @@ array_deleteProperty(JSContext *cx, JSOb
 
     if (!js_SuppressDeletedProperty(cx, obj, id))
         return false;
 
     rval->setBoolean(true);
     return JS_TRUE;
 }
 
+/* non-static for direct deletion of array elements within the engine */
+JSBool
+array_deleteElement(JSContext *cx, JSObject *obj, uint32 index, Value *rval, JSBool strict)
+{
+    jsid id;
+    if (!IndexToId(cx, index, &id))
+        return false;
+    return array_deleteProperty(cx, obj, id, rval, strict);
+}
+
 } // namespace js
 
 static void
 array_trace(JSTracer *trc, JSObject *obj)
 {
     JS_ASSERT(obj->isDenseArray());
 
     uint32 initLength = obj->getDenseArrayInitializedLength();
@@ -992,22 +1061,29 @@ Class js::ArrayClass = {
     NULL,           /* call        */
     NULL,           /* construct   */
     NULL,           /* xdrObject   */
     NULL,           /* hasInstance */
     array_trace,    /* trace       */
     JS_NULL_CLASS_EXT,
     {
         array_lookupProperty,
+        array_lookupElement,
         array_defineProperty,
+        array_defineElement,
         array_getProperty,
+        array_getElement,
         array_setProperty,
+        array_setElement,
         array_getAttributes,
+        array_getElementAttributes,
         array_setAttributes,
+        array_setElementAttributes,
         array_deleteProperty,
+        array_deleteElement,
         NULL,       /* enumerate      */
         array_typeOf,
         array_fix,
         NULL,       /* thisObject     */
         NULL,       /* clear          */
     }
 };
 
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -644,17 +644,17 @@ ReportError(JSContext *cx, const char *m
         reportp->errorNumber == JSMSG_UNCAUGHT_EXCEPTION)
         reportp->flags |= JSREPORT_EXCEPTION;
 
     /*
      * Call the error reporter only if an exception wasn't raised.
      *
      * If an exception was raised, then we call the debugErrorHook
      * (if present) to give it a chance to see the error before it
-     * propagates out of scope.  This is needed for compatability
+     * propagates out of scope.  This is needed for compatibility
      * with the old scheme.
      */
     if (!JS_IsRunning(cx) ||
         !js_ErrorToException(cx, message, reportp, callback, userRef)) {
         js_ReportErrorAgain(cx, message, reportp);
     } else if (cx->debugHooks->debugErrorHook && cx->errorReporter) {
         JSDebugErrorHook hook = cx->debugHooks->debugErrorHook;
         /* test local in case debugErrorHook changed on another thread */
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -1087,17 +1087,17 @@ struct JSContext
             clearVersionOverride();
         }
     }
 
     /*
      * Return:
      * - The override version, if there is an override version.
      * - The newest scripted frame's version, if there is such a frame.
-     * - The default verion.
+     * - The default version.
      *
      * Note: if this ever shows up in a profile, just add caching!
      */
     JSVersion findVersion() const {
         if (hasVersionOverride)
             return versionOverride;
 
         if (stack.hasfp()) {
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -591,22 +591,24 @@ JSCompartment::sweep(JSContext *cx, uint
 #endif
 
     if (activeAnalysis) {
         /*
          * Analysis information is in use, so don't clear the analysis pool.
          * jitcode still needs to be released, if this is a shape-regenerating
          * GC then shape numbers baked into the code may change.
          */
+#ifdef JS_METHODJIT
         if (types.inferenceEnabled) {
             for (CellIterUnderGC i(this, FINALIZE_SCRIPT); !i.done(); i.next()) {
                 JSScript *script = i.get<JSScript>();
                 mjit::ReleaseScriptCode(cx, script);
             }
         }
+#endif
     } else {
         /*
          * Clear the analysis pool, but don't release its data yet. While
          * sweeping types any live data will be allocated into the pool.
          */
         JSArenaPool oldPool;
         MoveArenaPool(&pool, &oldPool);
 
--- a/js/src/jscompartment.h
+++ b/js/src/jscompartment.h
@@ -280,19 +280,19 @@ struct TraceMonitor {
     void sweep(JSContext *cx);
 
     /* Mark any tracer stacks that are active. */
     void mark(JSTracer *trc);
 
     bool outOfMemory() const;
 
     JS_FRIEND_API(void) getCodeAllocStats(size_t &total, size_t &frag_size, size_t &free_size) const;
-    JS_FRIEND_API(size_t) getVMAllocatorsMainSize() const;
-    JS_FRIEND_API(size_t) getVMAllocatorsReserveSize() const;
-    JS_FRIEND_API(size_t) getTraceMonitorSize() const;
+    JS_FRIEND_API(size_t) getVMAllocatorsMainSize(JSUsableSizeFun usf) const;
+    JS_FRIEND_API(size_t) getVMAllocatorsReserveSize(JSUsableSizeFun usf) const;
+    JS_FRIEND_API(size_t) getTraceMonitorSize(JSUsableSizeFun usf) const;
 };
 
 namespace mjit {
 class JaegerCompartment;
 }
 }
 
 /* Defined in jsapi.cpp */
--- a/js/src/jsemit.h
+++ b/js/src/jsemit.h
@@ -257,17 +257,17 @@ struct JSStmtInfo {
  */
 #define TCF_HAS_SINGLETONS       0x8000000
 
 /*
  * Some enclosing scope is a with-statement or E4X filter-expression.
  */
 #define TCF_IN_WITH             0x10000000
 
-/* 
+/*
  * This function does something that can extend the set of bindings in its
  * call objects --- it does a direct eval in non-strict code, or includes a
  * function statement (as opposed to a function definition).
  *
  * This flag is *not* inherited by enclosed or enclosing functions; it
  * applies only to the function in whose flags it appears.
  */
 #define TCF_FUN_EXTENSIBLE_SCOPE 0x20000000
@@ -421,17 +421,17 @@ struct JSTreeContext {              /* t
     bool inStatement(JSStmtType type);
 
     bool inStrictMode() const {
         return flags & TCF_STRICT_MODE_CODE;
     }
 
     inline bool needStrictChecks();
 
-    /* 
+    /*
      * sharpSlotBase is -1 or first slot of pair for [sharpArray, sharpDepth].
      * The parser calls ensureSharpSlots to allocate these two stack locals.
      */
     int sharpSlotBase;
     bool ensureSharpSlots();
 
     js::Compiler *compiler() { return (js::Compiler *)parser; }
 
@@ -673,17 +673,17 @@ struct JSCodeGenerator : public JSTreeCo
      * the arena pool "tops-of-stack" space above their codeMark, noteMark, and
      * tempMark points.  This means you cannot alloc from tempPool and save the
      * pointer beyond the next JSCodeGenerator destructor call.
      */
     ~JSCodeGenerator();
 
     /*
      * Adds a use of a variable that is statically known to exist on the
-     * global object. 
+     * global object.
      *
      * The actual slot of the variable on the global object is not known
      * until after compilation. Properties must be resolved before being
      * added, to avoid aliasing properties that should be resolved. This makes
      * slot prediction based on the global object's free slot impossible. So,
      * we use the slot to index into cg->globalScope->defs, and perform a
      * fixup of the script at the very end of compilation.
      *
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -67,16 +67,17 @@
 #include "jsproxy.h"
 #include "jsscan.h"
 #include "jsscope.h"
 #include "jsscript.h"
 #include "jsstr.h"
 #include "jsexn.h"
 #include "jsstaticcheck.h"
 #include "jstracer.h"
+#include "vm/CallObject.h"
 #include "vm/Debugger.h"
 
 #if JS_HAS_GENERATORS
 # include "jsiter.h"
 #endif
 
 #if JS_HAS_XDR
 # include "jsxdrapi.h"
@@ -86,17 +87,17 @@
 #include "methodjit/MethodJIT.h"
 #endif
 
 #include "jsatominlines.h"
 #include "jsfuninlines.h"
 #include "jsinferinlines.h"
 #include "jsobjinlines.h"
 #include "jsscriptinlines.h"
-
+#include "vm/CallObject-inl.h"
 #include "vm/ArgumentsObject-inl.h"
 #include "vm/Stack-inl.h"
 
 using namespace js;
 using namespace js::gc;
 using namespace js::types;
 
 inline JSObject *
@@ -278,32 +279,32 @@ js_GetArgsObject(JSContext *cx, StackFra
      * ensures this by synthesizing an arguments access at the start of any
      * strict mode function that contains an assignment to a parameter, or
      * that calls eval.)  Non-strict mode arguments use the frame pointer to
      * retrieve up-to-date parameter values.
      */
     if (argsobj->isStrictArguments())
         fp->forEachCanonicalActualArg(PutArg(argsobj->data()->slots));
     else
-        argsobj->setPrivate(fp);
+        argsobj->setStackFrame(fp);
 
     fp->setArgsObj(*argsobj);
     return argsobj;
 }
 
 void
 js_PutArgsObject(StackFrame *fp)
 {
     ArgumentsObject &argsobj = fp->argsObj();
     if (argsobj.isNormalArguments()) {
-        JS_ASSERT(argsobj.getPrivate() == fp);
+        JS_ASSERT(argsobj.maybeStackFrame() == fp);
         fp->forEachCanonicalActualArg(PutArg(argsobj.data()->slots));
-        argsobj.setPrivate(NULL);
+        argsobj.setStackFrame(NULL);
     } else {
-        JS_ASSERT(!argsobj.getPrivate());
+        JS_ASSERT(!argsobj.maybeStackFrame());
     }
 }
 
 #ifdef JS_TRACER
 
 /*
  * Traced versions of js_GetArgsObject and js_PutArgsObject.
  */
@@ -314,47 +315,47 @@ js_NewArgumentsOnTrace(JSContext *cx, ui
     if (!argsobj)
         return NULL;
 
     if (argsobj->isStrictArguments()) {
         /*
          * Strict mode callers must copy arguments into the created arguments
          * object. The trace-JITting code is in TraceRecorder::newArguments.
          */
-        JS_ASSERT(!argsobj->getPrivate());
+        JS_ASSERT(!argsobj->maybeStackFrame());
     } else {
-        argsobj->setPrivate(JS_ARGUMENTS_OBJECT_ON_TRACE);
+        argsobj->setOnTrace();
     }
 
     return argsobj;
 }
 JS_DEFINE_CALLINFO_3(extern, OBJECT, js_NewArgumentsOnTrace, CONTEXT, UINT32, OBJECT,
                      0, nanojit::ACCSET_STORE_ANY)
 
 /* FIXME change the return type to void. */
 JSBool JS_FASTCALL
 js_PutArgumentsOnTrace(JSContext *cx, JSObject *obj, Value *argv)
 {
     NormalArgumentsObject *argsobj = obj->asNormalArguments();
 
-    JS_ASSERT(argsobj->getPrivate() == JS_ARGUMENTS_OBJECT_ON_TRACE);
+    JS_ASSERT(argsobj->onTrace());
 
     /*
      * TraceRecorder::putActivationObjects builds a single, contiguous array of
      * the arguments, regardless of whether #actuals > #formals so there is no
      * need to worry about actual vs. formal arguments.
      */
     Value *srcend = argv + argsobj->initialLength();
     Value *dst = argsobj->data()->slots;
     for (Value *src = argv; src < srcend; ++src, ++dst) {
         if (!dst->isMagic(JS_ARGS_HOLE))
             *dst = *src;
     }
 
-    argsobj->setPrivate(NULL);
+    argsobj->clearOnTrace();
     return true;
 }
 JS_DEFINE_CALLINFO_3(extern, BOOL, js_PutArgumentsOnTrace, CONTEXT, OBJECT, VALUEPTR, 0,
                      nanojit::ACCSET_STORE_ANY)
 
 #endif /* JS_TRACER */
 
 static JSBool
@@ -385,17 +386,17 @@ ArgGetter(JSContext *cx, JSObject *obj, 
     if (JSID_IS_INT(id)) {
         /*
          * arg can exceed the number of arguments if a script changed the
          * prototype to point to another Arguments object with a bigger argc.
          */
         uintN arg = uintN(JSID_TO_INT(id));
         if (arg < argsobj->initialLength()) {
             JS_ASSERT(!argsobj->element(arg).isMagic(JS_ARGS_HOLE));
-            if (StackFrame *fp = reinterpret_cast<StackFrame *>(argsobj->getPrivate()))
+            if (StackFrame *fp = argsobj->maybeStackFrame())
                 *vp = fp->canonicalActualArg(arg);
             else
                 *vp = argsobj->element(arg);
         }
     } else if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)) {
         if (!argsobj->hasOverriddenLength())
             vp->setInt32(argsobj->initialLength());
     } else {
@@ -422,17 +423,17 @@ ArgSetter(JSContext *cx, JSObject *obj, 
     if (!obj->isNormalArguments())
         return true;
 
     NormalArgumentsObject *argsobj = obj->asNormalArguments();
 
     if (JSID_IS_INT(id)) {
         uintN arg = uintN(JSID_TO_INT(id));
         if (arg < argsobj->initialLength()) {
-            if (StackFrame *fp = reinterpret_cast<StackFrame *>(argsobj->getPrivate())) {
+            if (StackFrame *fp = argsobj->maybeStackFrame()) {
                 JSScript *script = fp->functionScript();
                 if (script->usesArguments) {
                     if (arg < fp->numFormalArgs())
                         TypeScript::SetArgument(cx, script, arg, *vp);
                     fp->canonicalActualArg(arg) = *vp;
                 }
                 return true;
             }
@@ -668,17 +669,17 @@ MaybeMarkGenerator(JSTracer *trc, JSObje
     }
 #endif
 }
 
 static void
 args_trace(JSTracer *trc, JSObject *obj)
 {
     ArgumentsObject *argsobj = obj->asArguments();
-    if (argsobj->getPrivate() == JS_ARGUMENTS_OBJECT_ON_TRACE) {
+    if (argsobj->onTrace()) {
         JS_ASSERT(!argsobj->isStrictArguments());
         return;
     }
 
     ArgumentsData *data = argsobj->data();
     if (data->callee.isObject())
         MarkObject(trc, data->callee.toObject(), js_callee_str);
     MarkValueRange(trc, argsobj->initialLength(), data->slots, js_arguments_str);
@@ -752,67 +753,16 @@ Class js::DeclEnvClass = {
     PropertyStub,         /* delProperty */
     PropertyStub,         /* getProperty */
     StrictPropertyStub,   /* setProperty */
     EnumerateStub,
     ResolveStub,
     ConvertStub
 };
 
-/*
- * Construct a call object for the given bindings.  If this is a call object
- * for a function invocation, callee should be the function being called.
- * Otherwise it must be a call object for eval of strict mode code, and callee
- * must be null.
- */
-static JSObject *
-NewCallObject(JSContext *cx, JSScript *script, JSObject &scopeChain, JSObject *callee)
-{
-    Bindings &bindings = script->bindings;
-    size_t argsVars = bindings.countArgsAndVars();
-    size_t slots = JSObject::CALL_RESERVED_SLOTS + argsVars;
-    gc::AllocKind kind = gc::GetGCObjectKind(slots);
-
-    /*
-     * Make sure that the arguments and variables in the call object all end up
-     * in a contiguous range of slots. We need this to be able to embed the
-     * args/vars arrays in the TypeScriptNesting for the function, after the
-     * call object's frame has finished.
-     */
-    if (cx->typeInferenceEnabled() && gc::GetGCKindSlots(kind) < slots) {
-        kind = gc::GetGCObjectKind(JSObject::CALL_RESERVED_SLOTS);
-        JS_ASSERT(gc::GetGCKindSlots(kind) == JSObject::CALL_RESERVED_SLOTS);
-    }
-
-    JSObject *callobj = js_NewGCObject(cx, kind);
-    if (!callobj)
-        return NULL;
-
-    /* Init immediately to avoid GC seeing a half-init'ed object. */
-    callobj->initCall(cx, bindings, &scopeChain);
-    callobj->makeVarObj();
-
-    /* This must come after callobj->lastProp has been set. */
-    if (!callobj->ensureInstanceReservedSlots(cx, argsVars))
-        return NULL;
-
-#ifdef DEBUG
-    for (Shape::Range r = callobj->lastProp; !r.empty(); r.popFront()) {
-        const Shape &s = r.front();
-        if (s.slot != SHAPE_INVALID_SLOT) {
-            JS_ASSERT(s.slot + 1 == callobj->slotSpan());
-            break;
-        }
-    }
-#endif
-
-    callobj->setCallObjCallee(callee);
-    return callobj;
-}
-
 static inline JSObject *
 NewDeclEnvObject(JSContext *cx, StackFrame *fp)
 {
     JSObject *envobj = js_NewGCObject(cx, FINALIZE_OBJECT2);
     if (!envobj)
         return NULL;
 
     EmptyShape *emptyDeclEnvShape = EmptyShape::getEmptyDeclEnvShape(cx);
@@ -821,17 +771,17 @@ NewDeclEnvObject(JSContext *cx, StackFra
     envobj->init(cx, &DeclEnvClass, &emptyTypeObject, &fp->scopeChain(), fp, false);
     envobj->setMap(emptyDeclEnvShape);
 
     return envobj;
 }
 
 namespace js {
 
-JSObject *
+CallObject *
 CreateFunCallObject(JSContext *cx, StackFrame *fp)
 {
     JS_ASSERT(fp->isNonEvalFunctionFrame());
     JS_ASSERT(!fp->hasCallObj());
 
     JSObject *scopeChain = &fp->scopeChain();
     JS_ASSERT_IF(scopeChain->isWith() || scopeChain->isBlock() || scopeChain->isCall(),
                  scopeChain->getPrivate() != fp);
@@ -847,260 +797,261 @@ CreateFunCallObject(JSContext *cx, Stack
 
         if (!DefineNativeProperty(cx, scopeChain, ATOM_TO_JSID(lambdaName),
                                   ObjectValue(fp->callee()), NULL, NULL,
                                   JSPROP_PERMANENT | JSPROP_READONLY, 0, 0)) {
             return NULL;
         }
     }
 
-    JSObject *callobj = NewCallObject(cx, fp->script(), *scopeChain, &fp->callee());
+    CallObject *callobj = CallObject::create(cx, fp->script(), *scopeChain, &fp->callee());
     if (!callobj)
         return NULL;
 
-    callobj->setPrivate(fp);
+    callobj->setStackFrame(fp);
     fp->setScopeChainWithOwnCallObj(*callobj);
     return callobj;
 }
 
-JSObject *
+CallObject *
 CreateEvalCallObject(JSContext *cx, StackFrame *fp)
 {
-    JSObject *callobj = NewCallObject(cx, fp->script(), fp->scopeChain(), NULL);
+    CallObject *callobj = CallObject::create(cx, fp->script(), fp->scopeChain(), NULL);
     if (!callobj)
         return NULL;
 
-    callobj->setPrivate(fp);
+    callobj->setStackFrame(fp);
     fp->setScopeChainWithOwnCallObj(*callobj);
     return callobj;
 }
 
 } // namespace js
 
 JSObject * JS_FASTCALL
 js_CreateCallObjectOnTrace(JSContext *cx, JSFunction *fun, JSObject *callee, JSObject *scopeChain)
 {
     JS_ASSERT(!js_IsNamedLambda(fun));
     JS_ASSERT(scopeChain);
     JS_ASSERT(callee);
-    return NewCallObject(cx, fun->script(), *scopeChain, callee);
+    return CallObject::create(cx, fun->script(), *scopeChain, callee);
 }
 
 JS_DEFINE_CALLINFO_4(extern, OBJECT, js_CreateCallObjectOnTrace, CONTEXT, FUNCTION, OBJECT, OBJECT,
                      0, nanojit::ACCSET_STORE_ANY)
 
-inline static void
-CopyValuesToCallObject(JSObject &callobj, uintN nargs, Value *argv, uintN nvars, Value *slots)
-{
-    JS_ASSERT(callobj.numSlots() >= JSObject::CALL_RESERVED_SLOTS + nargs + nvars);
-    callobj.copySlotRange(JSObject::CALL_RESERVED_SLOTS, argv, nargs);
-    callobj.copySlotRange(JSObject::CALL_RESERVED_SLOTS + nargs, slots, nvars);
-}
-
 void
 js_PutCallObject(StackFrame *fp)
 {
-    JSObject &callobj = fp->callObj();
-    JS_ASSERT(callobj.getPrivate() == fp);
+    CallObject &callobj = fp->callObj().asCall();
+    JS_ASSERT(callobj.maybeStackFrame() == fp);
     JS_ASSERT_IF(fp->isEvalFrame(), fp->isStrictEvalFrame());
-    JS_ASSERT(fp->isEvalFrame() == callobj.callIsForEval());
+    JS_ASSERT(fp->isEvalFrame() == callobj.isForEval());
 
     /* Get the arguments object to snapshot fp's actual argument values. */
     if (fp->hasArgsObj()) {
         if (!fp->hasOverriddenArgs())
-            callobj.setCallObjArguments(ObjectValue(fp->argsObj()));
+            callobj.setArguments(ObjectValue(fp->argsObj()));
         js_PutArgsObject(fp);
     }
 
     JSScript *script = fp->script();
     Bindings &bindings = script->bindings;
 
-    if (callobj.callIsForEval()) {
+    if (callobj.isForEval()) {
         JS_ASSERT(script->strictModeCode);
         JS_ASSERT(bindings.countArgs() == 0);
 
         /* This could be optimized as below, but keep it simple for now. */
-        CopyValuesToCallObject(callobj, 0, NULL, bindings.countVars(), fp->slots());
+        callobj.copyValues(0, NULL, bindings.countVars(), fp->slots());
     } else {
         JSFunction *fun = fp->fun();
-        JS_ASSERT(fun == callobj.getCallObjCalleeFunction());
+        JS_ASSERT(fun == callobj.getCalleeFunction());
         JS_ASSERT(script == fun->script());
 
         uintN n = bindings.countArgsAndVars();
         if (n > 0) {
-            JS_ASSERT(JSObject::CALL_RESERVED_SLOTS + n <= callobj.numSlots());
+            JS_ASSERT(CallObject::RESERVED_SLOTS + n <= callobj.numSlots());
 
             uint32 nvars = bindings.countVars();
             uint32 nargs = bindings.countArgs();
             JS_ASSERT(fun->nargs == nargs);
             JS_ASSERT(nvars + nargs == n);
 
             JSScript *script = fun->script();
             if (script->usesEval
 #ifdef JS_METHODJIT
                 || script->debugMode
 #endif
                 ) {
-                CopyValuesToCallObject(callobj, nargs, fp->formalArgs(), nvars, fp->slots());
+                callobj.copyValues(nargs, fp->formalArgs(), nvars, fp->slots());
             } else {
                 /*
                  * For each arg & var that is closed over, copy it from the stack
                  * into the call object.
                  */
                 uint32 nclosed = script->nClosedArgs;
                 for (uint32 i = 0; i < nclosed; i++) {
                     uint32 e = script->getClosedArg(i);
-                    callobj.setSlot(JSObject::CALL_RESERVED_SLOTS + e, fp->formalArg(e));
+                    callobj.setArg(e, fp->formalArg(e));
                 }
 
                 nclosed = script->nClosedVars;
                 for (uint32 i = 0; i < nclosed; i++) {
                     uint32 e = script->getClosedVar(i);
-                    callobj.setSlot(JSObject::CALL_RESERVED_SLOTS + nargs + e, fp->slots()[e]);
+                    callobj.setVar(e, fp->slots()[e]);
                 }
             }
 
             /*
              * Update the args and vars for the active call if this is an outer
              * function in a script nesting.
              */
             types::TypeScriptNesting *nesting = script->nesting();
             if (nesting && script->isOuterFunction) {
-                nesting->argArray = callobj.callObjArgArray();
-                nesting->varArray = callobj.callObjVarArray();
+                nesting->argArray = callobj.argArray();
+                nesting->varArray = callobj.varArray();
             }
         }
 
         /* Clear private pointers to fp, which is about to go away. */
         if (js_IsNamedLambda(fun)) {
             JSObject *env = callobj.getParent();
 
             JS_ASSERT(env->isDeclEnv());
             JS_ASSERT(env->getPrivate() == fp);
             env->setPrivate(NULL);
         }
     }
 
-    callobj.setPrivate(NULL);
+    callobj.setStackFrame(NULL);
 }
 
 JSBool JS_FASTCALL
-js_PutCallObjectOnTrace(JSObject *callobj, uint32 nargs, Value *argv,
+js_PutCallObjectOnTrace(JSObject *obj, uint32 nargs, Value *argv,
                         uint32 nvars, Value *slots)
 {
-    JS_ASSERT(callobj->isCall());
-    JS_ASSERT(!callobj->getPrivate());
+    CallObject &callobj = obj->asCall();
+    JS_ASSERT(!callobj.maybeStackFrame());
 
     uintN n = nargs + nvars;
     if (n != 0)
-        CopyValuesToCallObject(*callobj, nargs, argv, nvars, slots);
+        callobj.copyValues(nargs, argv, nvars, slots);
 
     return true;
 }
 
 JS_DEFINE_CALLINFO_5(extern, BOOL, js_PutCallObjectOnTrace, OBJECT, UINT32, VALUEPTR,
                      UINT32, VALUEPTR, 0, nanojit::ACCSET_STORE_ANY)
 
 namespace js {
 
 static JSBool
 GetCallArguments(JSContext *cx, JSObject *obj, jsid id, Value *vp)
 {
-    StackFrame *fp = obj->maybeCallObjStackFrame();
+    CallObject &callobj = obj->asCall();
+
+    StackFrame *fp = callobj.maybeStackFrame();
     if (fp && !fp->hasOverriddenArgs()) {
         JSObject *argsobj = js_GetArgsObject(cx, fp);
         if (!argsobj)
             return false;
         vp->setObject(*argsobj);
     } else {
-        *vp = obj->getCallObjArguments();
+        *vp = callobj.getArguments();
     }
     return true;
 }
 
 static JSBool
 SetCallArguments(JSContext *cx, JSObject *obj, jsid id, JSBool strict, Value *vp)
 {
-    if (StackFrame *fp = obj->maybeCallObjStackFrame())
+    CallObject &callobj = obj->asCall();
+
+    if (StackFrame *fp = callobj.maybeStackFrame())
         fp->setOverriddenArgs();
-    obj->setCallObjArguments(*vp);
+    callobj.setArguments(*vp);
     return true;
 }
 
 JSBool
 GetCallArg(JSContext *cx, JSObject *obj, jsid id, Value *vp)
 {
+    CallObject &callobj = obj->asCall();
     JS_ASSERT((int16) JSID_TO_INT(id) == JSID_TO_INT(id));
     uintN i = (uint16) JSID_TO_INT(id);
 
-    if (StackFrame *fp = obj->maybeCallObjStackFrame())
+    if (StackFrame *fp = callobj.maybeStackFrame())
         *vp = fp->formalArg(i);
     else
-        *vp = obj->callObjArg(i);
+        *vp = callobj.arg(i);
     return true;
 }
 
 JSBool
 SetCallArg(JSContext *cx, JSObject *obj, jsid id, JSBool strict, Value *vp)
 {
+    CallObject &callobj = obj->asCall();
     JS_ASSERT((int16) JSID_TO_INT(id) == JSID_TO_INT(id));
     uintN i = (uint16) JSID_TO_INT(id);
 
-    if (StackFrame *fp = obj->maybeCallObjStackFrame())
+    if (StackFrame *fp = callobj.maybeStackFrame())
         fp->formalArg(i) = *vp;
     else
-        obj->setCallObjArg(i, *vp);
-
-    JSFunction *fun = obj->getCallObjCalleeFunction();
+        callobj.setArg(i, *vp);
+
+    JSFunction *fun = callobj.getCalleeFunction();
     JSScript *script = fun->script();
     if (!script->ensureHasTypes(cx, fun))
         return false;
 
     TypeScript::SetArgument(cx, script, i, *vp);
 
     return true;
 }
 
 JSBool
 GetCallUpvar(JSContext *cx, JSObject *obj, jsid id, Value *vp)
 {
+    CallObject &callobj = obj->asCall();
     JS_ASSERT((int16) JSID_TO_INT(id) == JSID_TO_INT(id));
     uintN i = (uint16) JSID_TO_INT(id);
 
-    *vp = obj->getCallObjCallee()->getFlatClosureUpvar(i);
+    *vp = callobj.getCallee()->getFlatClosureUpvar(i);
     return true;
 }
 
 JSBool
 SetCallUpvar(JSContext *cx, JSObject *obj, jsid id, JSBool strict, Value *vp)
 {
+    CallObject &callobj = obj->asCall();
     JS_ASSERT((int16) JSID_TO_INT(id) == JSID_TO_INT(id));
     uintN i = (uint16) JSID_TO_INT(id);
 
-    obj->getCallObjCallee()->setFlatClosureUpvar(i, *vp);
+    callobj.getCallee()->setFlatClosureUpvar(i, *vp);
     return true;
 }
 
 JSBool
 GetCallVar(JSContext *cx, JSObject *obj, jsid id, Value *vp)
 {
+    CallObject &callobj = obj->asCall();
     JS_ASSERT((int16) JSID_TO_INT(id) == JSID_TO_INT(id));
     uintN i = (uint16) JSID_TO_INT(id);
 
-    if (StackFrame *fp = obj->maybeCallObjStackFrame())
+    if (StackFrame *fp = callobj.maybeStackFrame())
         *vp = fp->varSlot(i);
     else
-        *vp = obj->callObjVar(i);
+        *vp = callobj.var(i);
     return true;
 }
 
 JSBool
 SetCallVar(JSContext *cx, JSObject *obj, jsid id, JSBool strict, Value *vp)
 {
-    JS_ASSERT(obj->isCall());
+    CallObject &callobj = obj->asCall();
 
     JS_ASSERT((int16) JSID_TO_INT(id) == JSID_TO_INT(id));
     uintN i = (uint16) JSID_TO_INT(id);
 
     /*
      * As documented in TraceRecorder::attemptTreeCall(), when recording an
      * inner tree call, the recorder assumes the inner tree does not mutate
      * any tracked upvars. The abort here is a pessimistic precaution against
@@ -1110,22 +1061,22 @@ SetCallVar(JSContext *cx, JSObject *obj,
 #ifdef JS_TRACER
     if (JS_ON_TRACE(cx)) {
         TraceMonitor *tm = JS_TRACE_MONITOR_ON_TRACE(cx);
         if (tm->recorder && tm->tracecx)
             AbortRecording(cx, "upvar write in nested tree");
     }
 #endif
 
-    if (StackFrame *fp = obj->maybeCallObjStackFrame())
+    if (StackFrame *fp = callobj.maybeStackFrame())
         fp->varSlot(i) = *vp;
     else
-        obj->setCallObjVar(i, *vp);
-
-    JSFunction *fun = obj->getCallObjCalleeFunction();
+        callobj.setVar(i, *vp);
+
+    JSFunction *fun = callobj.getCalleeFunction();
     JSScript *script = fun->script();
     if (!script->ensureHasTypes(cx, fun))
         return false;
 
     TypeScript::SetLocal(cx, script, i, *vp);
 
     return true;
 }
@@ -1148,26 +1099,24 @@ js_SetCallVar(JSContext *cx, JSObject *o
     Value argcopy = ValueArgToConstRef(arg);
     return SetCallVar(cx, obj, slotid, false /* STRICT DUMMY */, &argcopy);
 }
 JS_DEFINE_CALLINFO_4(extern, BOOL, js_SetCallVar, CONTEXT, OBJECT, JSID, VALUE, 0,
                      nanojit::ACCSET_STORE_ANY)
 #endif
 
 static JSBool
-call_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags,
-             JSObject **objp)
+call_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags, JSObject **objp)
 {
-    JS_ASSERT(obj->isCall());
     JS_ASSERT(!obj->getProto());
 
     if (!JSID_IS_ATOM(id))
         return true;
 
-    JSObject *callee = obj->getCallObjCallee();
+    JSObject *callee = obj->asCall().getCallee();
 #ifdef DEBUG
     if (callee) {
         JSScript *script = callee->getFunctionPrivate()->script();
         JS_ASSERT(!script->bindings.hasBinding(cx, JSID_TO_ATOM(id)));
     }
 #endif
 
     /*
@@ -1199,17 +1148,17 @@ call_trace(JSTracer *trc, JSObject *obj)
     JS_ASSERT(obj->isCall());
 
     MaybeMarkGenerator(trc, obj);
 }
 
 JS_PUBLIC_DATA(Class) js::CallClass = {
     "Call",
     JSCLASS_HAS_PRIVATE |
-    JSCLASS_HAS_RESERVED_SLOTS(JSObject::CALL_RESERVED_SLOTS) |
+    JSCLASS_HAS_RESERVED_SLOTS(CallObject::RESERVED_SLOTS) |
     JSCLASS_NEW_RESOLVE | JSCLASS_IS_ANONYMOUS,
     PropertyStub,         /* addProperty */
     PropertyStub,         /* delProperty */
     PropertyStub,         /* getProperty */
     StrictPropertyStub,   /* setProperty */
     JS_EnumerateStub,
     (JSResolveOp)call_resolve,
     NULL,                 /* convert: Leave it NULL so we notice if calls ever escape */
--- a/js/src/jsfun.h
+++ b/js/src/jsfun.h
@@ -498,20 +498,20 @@ extern void
 js_PutCallObject(js::StackFrame *fp);
 
 extern JSBool JS_FASTCALL
 js_PutCallObjectOnTrace(JSObject *scopeChain, uint32 nargs, js::Value *argv,
                         uint32 nvars, js::Value *slots);
 
 namespace js {
 
-JSObject *
+CallObject *
 CreateFunCallObject(JSContext *cx, StackFrame *fp);
 
-JSObject *
+CallObject *
 CreateEvalCallObject(JSContext *cx, StackFrame *fp);
 
 extern JSBool
 GetCallArg(JSContext *cx, JSObject *obj, jsid id, js::Value *vp);
 
 extern JSBool
 GetCallVar(JSContext *cx, JSObject *obj, jsid id, js::Value *vp);
 
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -90,16 +90,17 @@
 
 #include "methodjit/MethodJIT.h"
 #include "vm/String.h"
 #include "vm/Debugger.h"
 
 #include "jsobjinlines.h"
 
 #include "vm/String-inl.h"
+#include "vm/CallObject-inl.h"
 
 #ifdef MOZ_VALGRIND
 # define JS_VALGRIND
 #endif
 #ifdef JS_VALGRIND
 # include <valgrind/memcheck.h>
 #endif
 
@@ -233,38 +234,34 @@ Arena::finalize(JSContext *cx, AllocKind
 
     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
+    DebugOnly<size_t> nmarked = 0;
     for (;; thing += thingSize) {
         JS_ASSERT(thing <= lastByte + 1);
         if (thing == nextFree.first) {
             JS_ASSERT(nextFree.last <= lastByte);
             if (nextFree.last == lastByte)
                 break;
             JS_ASSERT(Arena::isAligned(nextFree.last, thingSize));
             if (!newFreeSpanStart)
                 newFreeSpanStart = thing;
             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(thingKind) + thingSize);
                     newListTail->first = newFreeSpanStart;
                     newListTail->last = thing - thingSize;
                     newListTail = newListTail->nextSpanUnchecked(thingSize);
                     newFreeSpanStart = 0;
                 }
             } else {
@@ -1552,19 +1549,17 @@ GCMarker::GCMarker(JSContext *cx)
     unmarkedArenaStackTop(MarkingDelay::stackBottom()),
     objStack(cx->runtime->gcMarkStackObjs, sizeof(cx->runtime->gcMarkStackObjs)),
     ropeStack(cx->runtime->gcMarkStackRopes, sizeof(cx->runtime->gcMarkStackRopes)),
     typeStack(cx->runtime->gcMarkStackTypes, sizeof(cx->runtime->gcMarkStackTypes)),
     xmlStack(cx->runtime->gcMarkStackXMLs, sizeof(cx->runtime->gcMarkStackXMLs)),
     largeStack(cx->runtime->gcMarkStackLarges, sizeof(cx->runtime->gcMarkStackLarges))
 {
     JS_TRACER_INIT(this, cx, NULL);
-#ifdef DEBUG
     markLaterArenas = 0;
-#endif
 #ifdef JS_DUMP_CONSERVATIVE_GC_ROOTS
     conservativeDumpFileName = getenv("JS_DUMP_CONSERVATIVE_GC_ROOTS");
     memset(&conservativeStats, 0, sizeof(conservativeStats));
 #endif
 }
 
 GCMarker::~GCMarker()
 {
@@ -1579,19 +1574,17 @@ GCMarker::delayMarkingChildren(const voi
     const Cell *cell = reinterpret_cast<const Cell *>(thing);
     ArenaHeader *aheader = cell->arenaHeader();
     if (aheader->getMarkingDelay()->link) {
         /* Arena already scheduled to be marked later */
         return;
     }
     aheader->getMarkingDelay()->link = unmarkedArenaStackTop;
     unmarkedArenaStackTop = aheader;
-#ifdef DEBUG
     markLaterArenas++;
-#endif
 }
 
 static void
 MarkDelayedChildren(JSTracer *trc, ArenaHeader *aheader)
 {
     AllocKind thingKind = aheader->getAllocKind();
     JSGCTraceKind traceKind = MapAllocToTraceKind(thingKind);
     size_t thingSize = aheader->getThingSize();
@@ -1612,20 +1605,18 @@ GCMarker::markDelayedChildren()
          * If marking gets delayed at the same arena again, we must repeat
          * marking of its things. For that we pop arena from the stack and
          * clear its nextDelayedMarking before we begin the marking.
          */
         ArenaHeader *aheader = unmarkedArenaStackTop;
         unmarkedArenaStackTop = aheader->getMarkingDelay()->link;
         JS_ASSERT(unmarkedArenaStackTop);
         aheader->getMarkingDelay()->link = NULL;
-#ifdef DEBUG
         JS_ASSERT(markLaterArenas);
         markLaterArenas--;
-#endif
         MarkDelayedChildren(this, aheader);
     }
     JS_ASSERT(!markLaterArenas);
 }
 
 } /* namespace js */
 
 #ifdef DEBUG
--- a/js/src/jsgc.h
+++ b/js/src/jsgc.h
@@ -1475,21 +1475,20 @@ static const size_t TYPE_MARK_STACK_SIZE
 static const size_t LARGE_MARK_STACK_SIZE = 64 * sizeof(LargeMarkItem);
 
 struct GCMarker : public JSTracer {
   private:
     /* The color is only applied to objects, functions and xml. */
     uint32 color;
 
   public:
-    /* See comments before delayMarkingChildren is jsgc.cpp. */
+    /* Pointer to the top of the stack of arenas we are delaying marking on. */
     js::gc::ArenaHeader *unmarkedArenaStackTop;
-#ifdef DEBUG
-    size_t              markLaterArenas;
-#endif
+    /* Count of arenas that are currently in the stack. */
+    DebugOnly<size_t> markLaterArenas;
 
 #ifdef JS_DUMP_CONSERVATIVE_GC_ROOTS
     js::gc::ConservativeGCStats conservativeStats;
     Vector<void *, 0, SystemAllocPolicy> conservativeRoots;
     const char *conservativeDumpFileName;
     void dumpConservativeRoots();
 #endif
 
--- a/js/src/jshashtable.h
+++ b/js/src/jshashtable.h
@@ -645,18 +645,24 @@ class HashTable : private AllocPolicy
     uint32 count() const {
         return entryCount;
     }
 
     uint32 generation() const {
         return gen;
     }
 
-    size_t tableSize() const {
-        return tableCapacity * sizeof(Entry);
+    /*
+     * This counts the HashTable's |table| array.  If |countMe| is true is also
+     * counts the HashTable object itself.
+     */
+    size_t sizeOf(JSUsableSizeFun usf, bool countMe) const {
+        size_t usable = usf(table) + (countMe ? usf((void*)this) : 0);
+        return usable ? usable
+                      : (tableCapacity * sizeof(Entry)) + (countMe ? sizeof(HashTable) : 0);
     }
 
     Ptr lookup(const Lookup &l) const {
         ReentrancyGuard g(*this);
         HashNumber keyHash = prepareHash(l);
         return Ptr(lookup(l, keyHash, 0));
     }
 
@@ -1077,17 +1083,17 @@ class HashMap
      *   for (HM::Range r = h.all(); !r.empty(); r.popFront())
      *     char c = r.front().value;
      *
      * Also see the definition of Range in HashTable above (with T = Entry).
      */
     typedef typename Impl::Range Range;
     Range all() const                                 { return impl.all(); }
     size_t count() const                              { return impl.count(); }
-    size_t tableSize() const                          { return impl.tableSize(); }
+    size_t sizeOf(JSUsableSizeFun usf, bool cm) const { return impl.sizeOf(usf, cm); }
 
     /*
      * Typedef for the enumeration class. An Enum may be used to examine and
      * remove table entries:
      *
      *   typedef HashMap<int,char> HM;
      *   HM s;
      *   for (HM::Enum e(s); !e.empty(); e.popFront())
@@ -1279,17 +1285,17 @@ class HashSet
      *   for (HS::Range r = h.all(); !r.empty(); r.popFront())
      *     int i = r.front();
      *
      * Also see the definition of Range in HashTable above.
      */
     typedef typename Impl::Range Range;
     Range all() const                                 { return impl.all(); }
     size_t count() const                              { return impl.count(); }
-    size_t tableSize() const                          { return impl.tableSize(); }
+    size_t sizeOf(JSUsableSizeFun usf, bool cm) const { return impl.sizeOf(usf, cm); }
 
     /*
      * Typedef for the enumeration class. An Enum may be used to examine and
      * remove table entries:
      *
      *   typedef HashSet<int> HS;
      *   HS s;
      *   for (HS::Enum e(s); !e.empty(); e.popFront())
--- a/js/src/jsinfer.cpp
+++ b/js/src/jsinfer.cpp
@@ -5050,18 +5050,18 @@ TypeScript::SetScope(JSContext *cx, JSSc
     JS_ASSERT(script->hasFunction == (fun != NULL));
     JS_ASSERT_IF(!fun, !script->isOuterFunction && !script->isInnerFunction);
     JS_ASSERT_IF(!scope, fun && !script->isInnerFunction);
 
     /*
      * The scope object must be the initial one for the script, before any call
      * object has been created in the heavyweight case.
      */
-    JS_ASSERT_IF(scope && scope->isCall() && !scope->callIsForEval(),
-                 scope->getCallObjCalleeFunction() != fun);
+    JS_ASSERT_IF(scope && scope->isCall() && !scope->asCall().isForEval(),
+                 scope->asCall().getCalleeFunction() != fun);
 
     if (!script->compileAndGo) {
         script->types->global = NULL;
         return true;
     }
 
     JS_ASSERT_IF(fun && scope, fun->getGlobal() == scope->getGlobal());
     script->types->global = fun ? fun->getGlobal() : scope->getGlobal();
@@ -5084,21 +5084,23 @@ TypeScript::SetScope(JSContext *cx, JSSc
 
     /*
      * Walk the scope chain to the next call object, which will be the function
      * the script is nested inside.
      */
     while (!scope->isCall())
         scope = scope->getParent();
 
+    CallObject &call = scope->asCall();
+
     /* The isInnerFunction test ensures there is no intervening strict eval call object. */
-    JS_ASSERT(!scope->callIsForEval());
+    JS_ASSERT(!call.isForEval());
 
     /* Don't track non-heavyweight parents, NAME ops won't reach into them. */
-    JSFunction *parentFun = scope->getCallObjCalleeFunction();
+    JSFunction *parentFun = call.getCalleeFunction();
     if (!parentFun || !parentFun->isHeavyweight())
         return true;
     JSScript *parent = parentFun->script();
     JS_ASSERT(parent->isOuterFunction);
 
     /*
      * We only need the nesting in the child if it has NAME accesses going
      * into the parent. We won't know for sure whether this is the case until
@@ -5116,18 +5118,18 @@ TypeScript::SetScope(JSContext *cx, JSSc
      * marked as reentrant.
      */
     if (!parent->ensureHasTypes(cx, parentFun))
         return false;
     if (!parent->types->hasScope()) {
         if (!SetScope(cx, parent, scope->getParent()))
             return false;
         parent->nesting()->activeCall = scope;
-        parent->nesting()->argArray = scope->callObjArgArray();
-        parent->nesting()->varArray = scope->callObjVarArray();
+        parent->nesting()->argArray = call.argArray();
+        parent->nesting()->varArray = call.varArray();
     }
 
     JS_ASSERT(!script->types->nesting);
 
     /* Construct and link nesting information for the two functions. */
 
     script->types->nesting = cx->new_<TypeScriptNesting>();
     if (!script->types->nesting)
@@ -5207,17 +5209,17 @@ ClearActiveNesting(JSScript *start)
  */
 static void
 CheckNestingParent(JSContext *cx, JSObject *scope, JSScript *script)
 {
   restart:
     JSScript *parent = script->nesting()->parent;
     JS_ASSERT(parent);
 
-    while (!scope->isCall() || scope->getCallObjCalleeFunction()->script() != parent)
+    while (!scope->isCall() || scope->asCall().getCalleeFunction()->script() != parent)
         scope = scope->getParent();
 
     if (scope != parent->nesting()->activeCall) {
         parent->reentrantOuterFunction = true;
         MarkTypeObjectFlags(cx, parent->function(), OBJECT_FLAG_REENTRANT_FUNCTION);
 
         /*
          * Continue checking parents to see if this is reentrant for them too.
--- a/js/src/jsinfer.h
+++ b/js/src/jsinfer.h
@@ -50,17 +50,17 @@
 #include "jsvalue.h"
 #include "jshashtable.h"
 
 namespace js {
     class CallArgs;
     namespace analyze {
         class ScriptAnalysis;
     }
-    struct GlobalObject;
+    class GlobalObject;
 }
 
 namespace js {
 namespace types {
 
 /* Forward declarations. */
 class TypeSet;
 struct TypeCallsite;
--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -3895,17 +3895,17 @@ BEGIN_CASE(JSOP_GETELEM)
             }
         } else if (obj->isArguments()) {
             uint32 arg = uint32(i);
             ArgumentsObject *argsobj = obj->asArguments();
 
             if (arg < argsobj->initialLength()) {
                 copyFrom = &argsobj->element(arg);
                 if (!copyFrom->isMagic(JS_ARGS_HOLE)) {
-                    if (StackFrame *afp = reinterpret_cast<StackFrame *>(argsobj->getPrivate()))
+                    if (StackFrame *afp = argsobj->maybeStackFrame())
                         copyFrom = &afp->canonicalActualArg(arg);
                     goto end_getelem;
                 }
             }
         }
         if (JS_LIKELY(INT_FITS_IN_JSID(i)))
             id = INT_TO_JSID(i);
         else
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -2713,21 +2713,21 @@ obj_preventExtensions(JSContext *cx, uin
     if (!obj->isExtensible())
         return true;
 
     AutoIdVector props(cx);
     return obj->preventExtensions(cx, &props);
 }
 
 size_t
-JSObject::sizeOfSlotsArray(size_t(*mus)(void *))
+JSObject::sizeOfSlotsArray(JSUsableSizeFun usf)
 {
     if (!hasSlotsArray())
         return 0;
-    size_t usable = mus((void *)slots);
+    size_t usable = usf((void *)slots);
     return usable ? usable : numSlots() * sizeof(js::Value);
 }
 
 bool
 JSObject::sealOrFreeze(JSContext *cx, ImmutabilityType it)
 {
     assertSameCompartment(cx, this);
     JS_ASSERT(it == SEAL || it == FREEZE);
@@ -3264,46 +3264,101 @@ with_LookupProperty(JSContext *cx, JSObj
     if (flags == RESOLVE_INFER)
         flags = js_InferFlags(cx, flags);
     flags |= JSRESOLVE_WITH;
     JSAutoResolveFlags rf(cx, flags);
     return obj->getProto()->lookupProperty(cx, id, objp, propp);
 }
 
 static JSBool
+with_LookupElement(JSContext *cx, JSObject *obj, uint32 index, JSObject **objp,
+                   JSProperty **propp)
+{
+    jsid id;
+    if (!IndexToId(cx, index, &id))
+        return false;
+    return with_LookupProperty(cx, obj, id, objp, propp);
+}
+
+static JSBool
 with_GetProperty(JSContext *cx, JSObject *obj, JSObject *receiver, jsid id, Value *vp)
 {
     return obj->getProto()->getProperty(cx, id, vp);
 }
 
 static JSBool
+with_GetElement(JSContext *cx, JSObject *obj, JSObject *receiver, uint32 index, Value *vp)
+{
+    jsid id;
+    if (!IndexToId(cx, index, &id))
+        return false;
+    return with_GetProperty(cx, obj, receiver, id, vp);
+}
+
+static JSBool
 with_SetProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp, JSBool strict)
 {
     return obj->getProto()->setProperty(cx, id, vp, strict);
 }
 
 static JSBool
+with_SetElement(JSContext *cx, JSObject *obj, uint32 index, Value *vp, JSBool strict)
+{
+    jsid id;
+    if (!IndexToId(cx, index, &id))
+        return false;
+    return with_SetProperty(cx, obj, id, vp, strict);
+}
+
+static JSBool
 with_GetAttributes(JSContext *cx, JSObject *obj, jsid id, uintN *attrsp)
 {
     return obj->getProto()->getAttributes(cx, id, attrsp);
 }
 
 static JSBool
+with_GetElementAttributes(JSContext *cx, JSObject *obj, uint32 index, uintN *attrsp)
+{
+    jsid id;
+    if (!IndexToId(cx, index, &id))
+        return false;
+    return with_GetAttributes(cx, obj, id, attrsp);
+}
+
+static JSBool
 with_SetAttributes(JSContext *cx, JSObject *obj, jsid id, uintN *attrsp)
 {
     return obj->getProto()->setAttributes(cx, id, attrsp);
 }
 
 static JSBool
+with_SetElementAttributes(JSContext *cx, JSObject *obj, uint32 index, uintN *attrsp)
+{
+    jsid id;
+    if (!IndexToId(cx, index, &id))
+        return false;
+    return with_SetAttributes(cx, obj, id, attrsp);
+}
+
+static JSBool
 with_DeleteProperty(JSContext *cx, JSObject *obj, jsid id, Value *rval, JSBool strict)
 {
     return obj->getProto()->deleteProperty(cx, id, rval, strict);
 }
 
 static JSBool
+with_DeleteElement(JSContext *cx, JSObject *obj, uint32 index, Value *rval, JSBool strict)
+{
+    jsid id;
+    if (!IndexToId(cx, index, &id))
+        return false;
+    return with_DeleteProperty(cx, obj, id, rval, strict);
+}
+
+static JSBool
 with_Enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
                Value *statep, jsid *idp)
 {
     return obj->getProto()->enumerate(cx, enum_op, statep, idp);
 }
 
 static JSType
 with_TypeOf(JSContext *cx, JSObject *obj)
@@ -3333,22 +3388,29 @@ Class js::WithClass = {
     NULL,                 /* call        */
     NULL,                 /* construct   */
     NULL,                 /* xdrObject   */
     NULL,                 /* hasInstance */
     NULL,                 /* trace       */
     JS_NULL_CLASS_EXT,
     {
         with_LookupProperty,
+        with_LookupElement,
         NULL,             /* defineProperty */
+        NULL,             /* defineElement */
         with_GetProperty,
+        with_GetElement,
         with_SetProperty,
+        with_SetElement,
         with_GetAttributes,
+        with_GetElementAttributes,
         with_SetAttributes,
+        with_SetElementAttributes,
         with_DeleteProperty,
+        with_DeleteElement,
         with_Enumerate,
         with_TypeOf,
         NULL,             /* fix   */
         with_ThisObject,
         NULL,             /* clear */
     }
 };
 
@@ -4424,17 +4486,17 @@ bool
 JSObject::growSlots(JSContext *cx, size_t newcap)
 {
     /*
      * Slots are only allocated for call objects when new properties are
      * added to them, which can only happen while the call is still on the
      * stack (and an eval, DEFFUN, etc. happens). We thus do not need to
      * worry about updating any active outer function args/vars.
      */
-    JS_ASSERT_IF(isCall(), maybeCallObjStackFrame() != NULL);
+    JS_ASSERT_IF(isCall(), asCall().maybeStackFrame() != NULL);
 
     /*
      * When an object with CAPACITY_DOUBLING_MAX or fewer slots needs to
      * grow, double its capacity, to add N elements in amortized O(N) time.
      *
      * Above this limit, grow by 12.5% each time. Speed is still amortized
      * O(N), with a higher constant factor, and we waste less space.
      */
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -61,16 +61,17 @@
 
 namespace nanojit { class ValidateWriter; }
 
 namespace js {
 
 class AutoPropDescArrayRooter;
 class JSProxyHandler;
 class RegExp;
+class CallObject;
 struct GCMarker;
 struct NativeIterator;
 
 namespace mjit { class Compiler; }
 
 static inline PropertyOp
 CastAsPropertyOp(JSObject *object)
 {
@@ -473,17 +474,17 @@ struct JSObject : js::gc::Cell {
     union {
         /* If prototype, type of values using this as their prototype. */
         js::types::TypeObject *newType;
 
         /* If dense array, the initialized length (see jsarray.cpp). */
         jsuword initializedLength;
     };
 
-    JS_FRIEND_API(size_t) sizeOfSlotsArray(size_t(*mus)(void *));
+    JS_FRIEND_API(size_t) sizeOfSlotsArray(JSUsableSizeFun usf);
 
     JSObject    *parent;                    /* object's parent */
     void        *privateData;               /* private data */
     jsuword     capacity;                   /* total number of available slots */
 
   private:
     js::Value   *slots;                     /* dynamically allocated slots,
                                                or pointer to fixedSlots() for
@@ -728,16 +729,18 @@ struct JSObject : js::gc::Cell {
     /* Whether this object has any dynamic slots at all. */
     inline bool hasSlotsArray() const;
 
     /* Get the number of dynamic slots required for a given capacity. */
     inline size_t numDynamicSlots(size_t capacity) const;
 
   private:
     inline js::Value* fixedSlots() const;
+
+  protected:
     inline bool hasContiguousSlots(size_t start, size_t count) const;
 
   public:
     /* Minimum size for dynamically allocated slots. */
     static const uint32 SLOT_CAPACITY_MIN = 8;
 
     bool allocSlots(JSContext *cx, size_t nslots);
     bool growSlots(JSContext *cx, size_t nslots);
@@ -1060,68 +1063,20 @@ struct JSObject : js::gc::Cell {
     inline uint32 arrayBufferByteLength();
     inline uint8 * arrayBufferDataOffset();
 
   public:
     inline js::ArgumentsObject *asArguments();
     inline js::NormalArgumentsObject *asNormalArguments();
     inline js::StrictArgumentsObject *asStrictArguments();
 
-  private:
-    /*
-     * Reserved slot structure for Call objects:
-     *
-     * private               - the stack frame corresponding to the Call object
-     *                         until js_PutCallObject or its on-trace analog
-     *                         is called, null thereafter
-     * JSSLOT_CALL_CALLEE    - callee function for the stack frame, or null if
-     *                         the stack frame is for strict mode eval code
-     * JSSLOT_CALL_ARGUMENTS - arguments object for non-strict mode eval stack
-     *                         frames (not valid for strict mode eval frames)
-     */
-    static const uint32 JSSLOT_CALL_CALLEE = 0;
-    static const uint32 JSSLOT_CALL_ARGUMENTS = 1;
+  public:
+    inline js::CallObject &asCall();
 
   public:
-    /* Number of reserved slots. */
-    static const uint32 CALL_RESERVED_SLOTS = 2;
-
-    /* True if this is for a strict mode eval frame or for a function call. */
-    inline bool callIsForEval() const;
-
-    /* The stack frame for this Call object, if the frame is still active. */
-    inline js::StackFrame *maybeCallObjStackFrame() const;
-
-    /*
-     * The callee function if this Call object was created for a function
-     * invocation, or null if it was created for a strict mode eval frame.
-     */
-    inline JSObject *getCallObjCallee() const;
-    inline JSFunction *getCallObjCalleeFunction() const; 
-    inline void setCallObjCallee(JSObject *callee);
-
-    inline const js::Value &getCallObjArguments() const;
-    inline void setCallObjArguments(const js::Value &v);
-
-    /* Returns the formal argument at the given index. */
-    inline const js::Value &callObjArg(uintN i) const;
-    inline void setCallObjArg(uintN i, const js::Value &v);
-
-    /* Returns the variable at the given index. */
-    inline const js::Value &callObjVar(uintN i) const;
-    inline void setCallObjVar(uintN i, const js::Value &v);
-
-    /*
-     * Get the actual arrays of arguments and variables. Only call if type
-     * inference is enabled, where we ensure that call object variables are in
-     * contiguous slots (see NewCallObject).
-     */
-    inline js::Value *callObjArgArray();
-    inline js::Value *callObjVarArray();
-
     /*
      * Date-specific getters and setters.
      */
 
     static const uint32 JSSLOT_DATE_UTC_TIME = 0;
 
     /*
      * Cached slots holding local properties of the date.
@@ -1921,20 +1876,17 @@ static const uintN RESOLVE_INFER = 0xfff
  * non-global objects without prototype or with prototype that never mutates,
  * see bug 462734 and bug 487039.
  */
 static inline bool
 IsCacheableNonGlobalScope(JSObject *obj)
 {
     JS_ASSERT(obj->getParent());
 
-    js::Class *clasp = obj->getClass();
-    bool cacheable = (clasp == &CallClass ||
-                      clasp == &BlockClass ||
-                      clasp == &DeclEnvClass);
+    bool cacheable = (obj->isCall() || obj->isBlock() || obj->isDeclEnv());
 
     JS_ASSERT_IF(cacheable, !obj->getOps()->lookupProperty);
     return cacheable;
 }
 
 }
 
 /*
--- a/js/src/jsobjinlines.h
+++ b/js/src/jsobjinlines.h
@@ -535,122 +535,16 @@ JSObject::shrinkDenseArrayElements(JSCon
 
 inline bool
 JSObject::denseArrayHasInlineSlots() const
 {
     JS_ASSERT(isDenseArray() && slots);
     return slots == fixedSlots();
 }
 
-inline bool
-JSObject::callIsForEval() const
-{
-    JS_ASSERT(isCall());
-    JS_ASSERT(getFixedSlot(JSSLOT_CALL_CALLEE).isObjectOrNull());
-    JS_ASSERT_IF(getFixedSlot(JSSLOT_CALL_CALLEE).isObject(),
-                 getFixedSlot(JSSLOT_CALL_CALLEE).toObject().isFunction());
-    return getFixedSlot(JSSLOT_CALL_CALLEE).isNull();
-}
-
-inline js::StackFrame *
-JSObject::maybeCallObjStackFrame() const
-{
-    JS_ASSERT(isCall());
-    return reinterpret_cast<js::StackFrame *>(getPrivate());
-}
-
-inline void
-JSObject::setCallObjCallee(JSObject *callee)
-{
-    JS_ASSERT(isCall());
-    JS_ASSERT_IF(callee, callee->isFunction());
-    setFixedSlot(JSSLOT_CALL_CALLEE, js::ObjectOrNullValue(callee));
-}
-
-inline JSObject *
-JSObject::getCallObjCallee() const
-{
-    JS_ASSERT(isCall());
-    return getFixedSlot(JSSLOT_CALL_CALLEE).toObjectOrNull();
-}
-
-inline JSFunction *
-JSObject::getCallObjCalleeFunction() const
-{
-    JS_ASSERT(isCall());
-    return getFixedSlot(JSSLOT_CALL_CALLEE).toObject().getFunctionPrivate();
-}
-
-inline const js::Value &
-JSObject::getCallObjArguments() const
-{
-    JS_ASSERT(isCall());
-    JS_ASSERT(!callIsForEval());
-    return getFixedSlot(JSSLOT_CALL_ARGUMENTS);
-}
-
-inline void
-JSObject::setCallObjArguments(const js::Value &v)
-{
-    JS_ASSERT(isCall());
-    JS_ASSERT(!callIsForEval());
-    setFixedSlot(JSSLOT_CALL_ARGUMENTS, v);
-}
-
-inline const js::Value &
-JSObject::callObjArg(uintN i) const
-{
-    JS_ASSERT(isCall());
-    JS_ASSERT(i < getCallObjCalleeFunction()->nargs);
-    return getSlot(JSObject::CALL_RESERVED_SLOTS + i);
-}
-
-inline void
-JSObject::setCallObjArg(uintN i, const js::Value &v)
-{
-    JS_ASSERT(isCall());
-    JS_ASSERT(i < getCallObjCalleeFunction()->nargs);
-    setSlot(JSObject::CALL_RESERVED_SLOTS + i, v);
-}
-
-inline js::Value *
-JSObject::callObjArgArray()
-{
-    js::DebugOnly<JSFunction*> fun = getCallObjCalleeFunction();
-    JS_ASSERT(hasContiguousSlots(JSObject::CALL_RESERVED_SLOTS, fun->nargs));
-    return getSlotAddress(JSObject::CALL_RESERVED_SLOTS);
-}
-
-inline const js::Value &
-JSObject::callObjVar(uintN i) const
-{
-    JSFunction *fun = getCallObjCalleeFunction();
-    JS_ASSERT(fun->nargs == fun->script()->bindings.countArgs());
-    JS_ASSERT(i < fun->script()->bindings.countVars());
-    return getSlot(JSObject::CALL_RESERVED_SLOTS + fun->nargs + i);
-}
-
-inline void
-JSObject::setCallObjVar(uintN i, const js::Value &v)
-{
-    JSFunction *fun = getCallObjCalleeFunction();
-    JS_ASSERT(fun->nargs == fun->script()->bindings.countArgs());
-    JS_ASSERT(i < fun->script()->bindings.countVars());
-    setSlot(JSObject::CALL_RESERVED_SLOTS + fun->nargs + i, v);
-}
-
-inline js::Value *
-JSObject::callObjVarArray()
-{
-    JSFunction *fun = getCallObjCalleeFunction();
-    JS_ASSERT(hasContiguousSlots(JSObject::CALL_RESERVED_SLOTS + fun->nargs,
-                                 fun->script()->bindings.countVars()));
-    return getSlotAddress(JSObject::CALL_RESERVED_SLOTS + fun->nargs);
-}
-
 namespace js {
 
 /*
  * Any name atom for a function which will be added as a DeclEnv object to the
  * scope chain above call objects for fun.
  */
 static inline JSAtom *
 CallObjectLambdaName(JSFunction *fun)
--- a/js/src/jsparse.cpp
+++ b/js/src/jsparse.cpp
@@ -315,17 +315,17 @@ JSFunctionBox::joinable() const
                        TCF_FUN_USES_OWN_NAME |
                        TCF_COMPILE_N_GO)) == TCF_COMPILE_N_GO;
 }
 
 bool
 JSFunctionBox::inAnyDynamicScope() const
 {
     for (const JSFunctionBox *funbox = this; funbox; funbox = funbox->parent) {
-        if (funbox->tcflags & (TCF_IN_WITH | TCF_FUN_CALLS_EVAL))
+        if (funbox->tcflags & (TCF_IN_WITH | TCF_FUN_EXTENSIBLE_SCOPE))
             return true;
     }
     return false;
 }
 
 bool
 JSFunctionBox::scopeIsExtensible() const
 {
@@ -6729,17 +6729,17 @@ class GenexpGuard {
         }
         startYieldCount = tc->yieldCount;
         startArgumentsCount = tc->argumentsCount;
         tc->parenDepth++;
     }
 
     void endBody();
     bool checkValidBody(JSParseNode *pn);
-    bool maybeNoteGenerator();
+    bool maybeNoteGenerator(JSParseNode *pn);
 };
 
 void
 GenexpGuard::endBody()
 {
     tc->parenDepth--;
 }
 
@@ -6775,25 +6775,32 @@ GenexpGuard::checkValidBody(JSParseNode 
 /*
  * Check whether a |yield| token has been encountered in the body expression,
  * and if so, note that the current function is a generator function.
  *
  * Call this after endBody() when determining that the body *was not* in a
  * generator expression.
  */
 bool
-GenexpGuard::maybeNoteGenerator()
+GenexpGuard::maybeNoteGenerator(JSParseNode *pn)
 {
     if (tc->yieldCount > 0) {
         tc->flags |= TCF_FUN_IS_GENERATOR;
         if (!tc->inFunction()) {
             tc->parser->reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_BAD_RETURN_OR_YIELD,
                                           js_yield_str);
             return false;
         }
+        if (tc->flags & TCF_RETURN_EXPR) {
+            /* At the time we saw the yield, we might not have set TCF_FUN_IS_GENERATOR yet. */
+            ReportBadReturn(tc->parser->context, tc, pn, JSREPORT_ERROR,
+                            JSMSG_BAD_GENERATOR_RETURN,
+                            JSMSG_BAD_ANON_GENERATOR_RETURN);
+            return false;
+        }
     }
     return true;
 }
 
 /*
  * Any definitions nested within the comprehension expression of a generator
  * expression must move "down" one static level, which of course increases the
  * upvar-frame-skip count.
@@ -7124,17 +7131,17 @@ Parser::comprehensionTail(JSParseNode *k
         MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FOR_CTRL);
 
         guard.endBody();
 
         if (isGenexp) {
             if (!guard.checkValidBody(pn2))
                 return NULL;
         } else {
-            if (!guard.maybeNoteGenerator())
+            if (!guard.maybeNoteGenerator(pn2))
                 return NULL;
         }
 
         switch (tt) {
 #if JS_HAS_DESTRUCTURING
           case TOK_LB:
           case TOK_LC:
             if (!CheckDestructuring(context, &data, pn3, tc))
@@ -7354,17 +7361,17 @@ Parser::argumentList(JSParseNode *listNo
             if (listNode->pn_count > 1 ||
                 tokenStream.peekToken() == TOK_COMMA) {
                 reportErrorNumber(argNode, JSREPORT_ERROR, JSMSG_BAD_GENERATOR_SYNTAX,
                                   js_generator_str);
                 return JS_FALSE;
             }
         } else
 #endif
-        if (arg0 && !guard.maybeNoteGenerator())
+        if (arg0 && !guard.maybeNoteGenerator(argNode))
             return JS_FALSE;
 
         arg0 = false;
 
         listNode->append(argNode);
     } while (tokenStream.matchToken(TOK_COMMA));
 
     if (tokenStream.getToken() != TOK_RP) {
@@ -8449,17 +8456,16 @@ Parser::primaryExpr(TokenKind tt, JSBool
             MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_AFTER_LIST);
         }
         pn->pn_pos.end = tokenStream.currentToken().pos.end;
         return pn;
       }
 
       case TOK_LC:
       {
-        JSBool afterComma;
         JSParseNode *pnval;
 
         /*
          * A map from property names we've seen thus far to a mask of property
          * assignment types, stored and retrieved with ALE_SET_INDEX/ALE_INDEX.
          */
         AtomIndexMap seen(context);
 
@@ -8471,17 +8477,16 @@ Parser::primaryExpr(TokenKind tt, JSBool
 
         pn = ListNode::create(tc);
         if (!pn)
             return NULL;
         pn->pn_type = TOK_RC;
         pn->pn_op = JSOP_NEWINIT;
         pn->makeEmpty();
 
-        afterComma = JS_FALSE;
         for (;;) {
             JSAtom *atom;
             tt = tokenStream.getToken(TSF_KEYWORD_IS_NAME);
             switch (tt) {
               case TOK_NUMBER:
                 pn3 = NullaryNode::create(tc);
                 if (!pn3)
                     return NULL;
@@ -8630,17 +8635,16 @@ Parser::primaryExpr(TokenKind tt, JSBool
 
             tt = tokenStream.getToken();
             if (tt == TOK_RC)
                 goto end_obj_init;
             if (tt != TOK_COMMA) {
                 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_CURLY_AFTER_LIST);
                 return NULL;
             }
-            afterComma = JS_TRUE;
         }
 
       end_obj_init:
         pn->pn_pos.end = tokenStream.currentToken().pos.end;
         return pn;
       }
 
 #if JS_HAS_BLOCK_SCOPE
@@ -8975,17 +8979,17 @@ Parser::parenExpr(JSBool *genexp)
                 return NULL;
             }
             pn->pn_pos.end = tokenStream.currentToken().pos.end;
             *genexp = JS_TRUE;
         }
     } else
 #endif /* JS_HAS_GENERATOR_EXPRS */
 
-    if (!guard.maybeNoteGenerator())
+    if (!guard.maybeNoteGenerator(pn))
         return NULL;
 
     return pn;
 }
 
 /*
  * Fold from one constant type to another.
  * XXX handles only strings and numbers for now
--- a/js/src/jsproxy.cpp
+++ b/js/src/jsproxy.cpp
@@ -897,73 +897,138 @@ proxy_LookupProperty(JSContext *cx, JSOb
     } else {
         *objp = NULL;
         *propp = NULL;
     }
     return true;
 }
 
 static JSBool
+proxy_LookupElement(JSContext *cx, JSObject *obj, uint32 index, JSObject **objp,
+                    JSProperty **propp)
+{
+    jsid id;
+    if (!IndexToId(cx, index, &id))
+        return false;
+    return proxy_LookupProperty(cx, obj, id, objp, propp);
+}
+
+static JSBool
 proxy_DefineProperty(JSContext *cx, JSObject *obj, jsid id, const Value *value,
                      PropertyOp getter, StrictPropertyOp setter, uintN attrs)
 {
     AutoPropertyDescriptorRooter desc(cx);
     desc.obj = obj;
     desc.value = *value;
     desc.attrs = (attrs & (~JSPROP_SHORTID));
     desc.getter = getter;
     desc.setter = setter;
     desc.shortid = 0;
     return JSProxy::defineProperty(cx, obj, id, &desc);
 }
 
 static JSBool
+proxy_DefineElement(JSContext *cx, JSObject *obj, uint32 index, const Value *value,
+                    PropertyOp getter, StrictPropertyOp setter, uintN attrs)
+{
+    jsid id;
+    if (!IndexToId(cx, index, &id))
+        return false;
+    return proxy_DefineProperty(cx, obj, id, value, getter, setter, attrs);
+}
+
+static JSBool
 proxy_GetProperty(JSContext *cx, JSObject *obj, JSObject *receiver, jsid id, Value *vp)
 {
     return JSProxy::get(cx, obj, receiver, id, vp);
 }
 
 static JSBool
+proxy_GetElement(JSContext *cx, JSObject *obj, JSObject *receiver, uint32 index, Value *vp)
+{
+    jsid id;
+    if (!IndexToId(cx, index, &id))
+        return false;
+    return proxy_GetProperty(cx, obj, receiver, id, vp);
+}
+
+static JSBool
 proxy_SetProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp, JSBool strict)
 {
     return JSProxy::set(cx, obj, obj, id, strict, vp);
 }
 
 static JSBool
+proxy_SetElement(JSContext *cx, JSObject *obj, uint32 index, Value *vp, JSBool strict)
+{
+    jsid id;
+    if (!IndexToId(cx, index, &id))
+        return false;
+    return proxy_SetProperty(cx, obj, id, vp, strict);
+}
+
+static JSBool
 proxy_GetAttributes(JSContext *cx, JSObject *obj, jsid id, uintN *attrsp)
 {
     AutoPropertyDescriptorRooter desc(cx);
     if (!JSProxy::getOwnPropertyDescriptor(cx, obj, id, false, &desc))
         return false;
     *attrsp = desc.attrs;
     return true;
 }
 
 static JSBool
+proxy_GetElementAttributes(JSContext *cx, JSObject *obj, uint32 index, uintN *attrsp)
+{
+    jsid id;
+    if (!IndexToId(cx, index, &id))
+        return false;
+    return proxy_GetAttributes(cx, obj, id, attrsp);
+}
+
+static JSBool
 proxy_SetAttributes(JSContext *cx, JSObject *obj, jsid id, uintN *attrsp)
 {
     /* Lookup the current property descriptor so we have setter/getter/value. */
     AutoPropertyDescriptorRooter desc(cx);
     if (!JSProxy::getOwnPropertyDescriptor(cx, obj, id, true, &desc))
         return false;
     desc.attrs = (*attrsp & (~JSPROP_SHORTID));
     return JSProxy::defineProperty(cx, obj, id, &desc);
 }
 
 static JSBool
+proxy_SetElementAttributes(JSContext *cx, JSObject *obj, uint32 index, uintN *attrsp)
+{
+    jsid id;
+    if (!IndexToId(cx, index, &id))
+        return false;
+    return proxy_SetAttributes(cx, obj, id, attrsp);
+}
+
+static JSBool
 proxy_DeleteProperty(JSContext *cx, JSObject *obj, jsid id, Value *rval, JSBool strict)
 {
     // TODO: throwing away strict
     bool deleted;
     if (!JSProxy::delete_(cx, obj, id, &deleted) || !js_SuppressDeletedProperty(cx, obj, id))
         return false;
     rval->setBoolean(deleted);
     return true;
 }
 
+static JSBool
+proxy_DeleteElement(JSContext *cx, JSObject *obj, uint32 index, Value *rval, JSBool strict)
+{
+    jsid id;
+    if (!IndexToId(cx, index, &id))
+        return false;
+    return proxy_DeleteProperty(cx, obj, id, rval, strict);
+}
+
 static void
 proxy_TraceObject(JSTracer *trc, JSObject *obj)
 {
     obj->getProxyHandler()->trace(trc, obj);
     MarkCrossCompartmentValue(trc, obj->getProxyPrivate(), "private");
     MarkCrossCompartmentValue(trc, obj->getProxyExtra(), "extra");
     if (obj->isFunctionProxy()) {
         MarkCrossCompartmentValue(trc, GetCall(obj), "call");
@@ -1041,22 +1106,29 @@ JS_FRIEND_DATA(Class) js::ObjectProxyCla
     NULL,                 /* call        */
     NULL,                 /* construct   */
     NULL,                 /* xdrObject   */
     proxy_HasInstance,    /* hasInstance */
     proxy_TraceObject,    /* trace       */
     JS_NULL_CLASS_EXT,
     {
         proxy_LookupProperty,
+        proxy_LookupElement,
         proxy_DefineProperty,
+        proxy_DefineElement,
         proxy_GetProperty,
+        proxy_GetElement,
         proxy_SetProperty,
+        proxy_SetElement,
         proxy_GetAttributes,
+        proxy_GetElementAttributes,
         proxy_SetAttributes,
+        proxy_SetElementAttributes,
         proxy_DeleteProperty,
+        proxy_DeleteElement,
         NULL,             /* enumerate       */
         proxy_TypeOf,
         proxy_Fix,        /* fix             */
         NULL,             /* thisObject      */
         NULL,             /* clear           */
     }
 };
 
@@ -1081,22 +1153,29 @@ JS_FRIEND_DATA(Class) js::OuterWindowPro
     {
         NULL,             /* equality    */
         NULL,             /* outerObject */
         proxy_innerObject,
         NULL        /* unused */
     },
     {
         proxy_LookupProperty,
+        proxy_LookupElement,
         proxy_DefineProperty,
+        proxy_DefineElement,
         proxy_GetProperty,
+        proxy_GetElement,
         proxy_SetProperty,
+        proxy_SetElement,
         proxy_GetAttributes,
+        proxy_GetElementAttributes,
         proxy_SetAttributes,
+        proxy_SetElementAttributes,
         proxy_DeleteProperty,
+        proxy_DeleteElement,
         NULL,             /* enumerate       */
         NULL,             /* typeof          */
         NULL,             /* fix             */
         NULL,             /* thisObject      */
         NULL,             /* clear           */
     }
 };
 
@@ -1133,22 +1212,29 @@ JS_FRIEND_DATA(Class) js::FunctionProxyC
     proxy_Call,
     proxy_Construct,
     NULL,                 /* xdrObject   */
     FunctionClass.hasInstance,
     proxy_TraceFunction,  /* trace       */
     JS_NULL_CLASS_EXT,
     {
         proxy_LookupProperty,
+        proxy_LookupElement,
         proxy_DefineProperty,
+        proxy_DefineElement,
         proxy_GetProperty,
+        proxy_GetElement,
         proxy_SetProperty,
+        proxy_SetElement,
         proxy_GetAttributes,
+        proxy_GetElementAttributes,
         proxy_SetAttributes,
+        proxy_SetElementAttributes,
         proxy_DeleteProperty,
+        proxy_DeleteElement,
         NULL,             /* enumerate       */
         proxy_TypeOf,
         NULL,             /* fix             */
         NULL,             /* thisObject      */
         NULL,             /* clear           */
     }
 };
 
--- a/js/src/jsprvtd.h
+++ b/js/src/jsprvtd.h
@@ -323,21 +323,16 @@ typedef struct JSDebugHooks {
  * exception, true on success.  On success, return null in *propp if id was
  * not found.  If id was found, return the first object searching from obj
  * along its prototype chain in which id names a direct property in *objp, and
  * return a non-null, opaque property pointer in *propp.
  *
  * If JSLookupPropOp succeeds and returns with *propp non-null, that pointer
  * may be passed as the prop parameter to a JSAttributesOp, as a short-cut
  * that bypasses id re-lookup.
- *
- * NB: successful return with non-null *propp means the implementation may
- * have locked *objp and added a reference count associated with *propp, so
- * callers should not risk deadlock by nesting or interleaving other lookups
- * or any obj-bearing ops before dropping *propp.
  */
 typedef JSBool
 (* JSLookupPropOp)(JSContext *cx, JSObject *obj, jsid id, JSObject **objp,
                    JSProperty **propp);
 
 /*
  * Get or set attributes of the property obj[id]. Return false on error or
  * exception, true with current attributes in *attrsp.
--- a/js/src/jsscope.h
+++ b/js/src/jsscope.h
@@ -246,23 +246,23 @@ struct PropertyTable {
     }
 
     /* By definition, hashShift = JS_DHASH_BITS - log2(capacity). */
     uint32 capacity() const { return JS_BIT(JS_DHASH_BITS - hashShift); }
 
     /* Computes the size of the entries array for a given capacity. */
     static size_t sizeOfEntries(size_t cap) { return cap * sizeof(Shape *); }
 
-    size_t sizeOf(size_t(*mus)(void *)) const {
-        if (mus) {
-            size_t usable = mus((void*)this) + mus(entries);
-            if (usable)
-                return usable;
-        }
-        return sizeOfEntries(capacity()) + sizeof(PropertyTable);
+    /*
+     * This counts the PropertyTable object itself (which must be
+     * heap-allocated) and its |entries| array.
+     */
+    size_t sizeOf(JSUsableSizeFun usf) const {
+        size_t usable = usf((void*)this) + usf(entries);
+        return usable ? usable : sizeOfEntries(capacity()) + sizeof(PropertyTable);
     }
 
     /* Whether we need to grow.  We want to do this if the load factor is >= 0.75 */
     bool needsToGrow() const {
         uint32 size = capacity();
         return entryCount + removedCount >= size - (size >> 2);
     }
 
@@ -352,16 +352,17 @@ struct Shape : public js::gc::Cell
   private:
     uint8               attrs;          /* attributes, see jsapi.h JSPROP_* */
     mutable uint8       flags;          /* flags, see below for defines */
   public:
     int16               shortid;        /* tinyid, or local arg/var index */
 
   protected:
     mutable js::Shape   *parent;        /* parent node, reverse for..in order */
+    /* kids is valid when !inDictionary(), listp is valid when inDictionary(). */
     union {
         mutable js::KidsPointer kids;   /* null, single child, or a tagged ptr
                                            to many-kids data structure */
         mutable js::Shape **listp;      /* dictionary list starting at lastProp
                                            has a double-indirect back pointer,
                                            either to shape->parent if not last,
                                            else to obj->lastProp */
     };
@@ -440,16 +441,27 @@ struct Shape : public js::gc::Cell
         return numLinearSearches > PropertyTable::MAX_LINEAR_SEARCHES;
     }
 
     js::PropertyTable *getTable() const {
         JS_ASSERT(hasTable());
         return table;
     }
 
+    size_t sizeOfPropertyTable(JSUsableSizeFun usf) const {
+        return hasTable() ? getTable()->sizeOf(usf) : 0;
+    }
+
+    size_t sizeOfKids(JSUsableSizeFun usf) const {
+        /* Nb: |countMe| is true because the kids HashTable is on the heap. */
+        return (!inDictionary() && kids.isHash())
+             ? kids.toHash()->sizeOf(usf, /* countMe */true)
+             : 0;
+    }
+
     bool isNative() const { return this != &sharedNonNative; }
 
     const js::Shape *previous() const {
         return parent;
     }
 
     class Range {
       protected:
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -115,17 +115,17 @@ Bindings::add(JSContext *cx, JSAtom *nam
      * of the Call objects enumerable. ES5 reformulated all of its Clause 10 to
      * avoid objects as activations, something we should do too.
      */
     uintN attrs = JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED;
 
     uint16 *indexp;
     PropertyOp getter;
     StrictPropertyOp setter;
-    uint32 slot = JSObject::CALL_RESERVED_SLOTS;
+    uint32 slot = CallObject::RESERVED_SLOTS;
 
     if (kind == ARGUMENT) {
         JS_ASSERT(nvars == 0);
         JS_ASSERT(nupvars == 0);
         indexp = &nargs;
         getter = GetCallArg;
         setter = SetCallArg;
         slot += nargs;
@@ -1284,16 +1284,28 @@ JSScript::dataSize()
         return 0;
 #endif
 
     uint8 *dataEnd = code + length * sizeof(jsbytecode) + numNotes() * sizeof(jssrcnote);
     JS_ASSERT(dataEnd >= data);
     return dataEnd - data;
 }
 
+size_t
+JSScript::dataSize(JSUsableSizeFun usf)
+{
+#if JS_SCRIPT_INLINE_DATA_LIMIT
+    if (data == inlineData)
+        return 0;
+#endif
+
+    size_t usable = usf(data);
+    return usable ? usable : dataSize();
+}
+
 void
 JSScript::setOwnerObject(JSObject *owner)
 {
 #ifdef JS_CRASH_DIAGNOSTICS
     CheckScriptOwner(this, JS_NEW_SCRIPT);
     ownerObject = owner;
 #endif
 }
--- a/js/src/jsscript.h
+++ b/js/src/jsscript.h
@@ -678,25 +678,31 @@ struct JSScript : public js::gc::Cell {
         if (addr == NULL)
             return JITScript_None;
         if (addr == JS_UNJITTABLE_SCRIPT)
             return JITScript_Invalid;
         return JITScript_Valid;
     }
 
     /* Size of the JITScript and all sections.  (This method is implemented in MethodJIT.h.) */
-    JS_FRIEND_API(size_t) jitDataSize(size_t(*mus)(void *));
+    JS_FRIEND_API(size_t) jitDataSize(JSUsableSizeFun usf);
     
 #endif
 
     jsbytecode *main() {
         return code + mainOffset;
     }
 
-    JS_FRIEND_API(size_t) dataSize();   /* Size of all data sections */
+    /*
+     * The first dataSize() is the in-use size of all the data sections, the
+     * second is the size of the block allocated to hold all the data sections
+     * (which can be larger than the in-use size).
+     */
+    JS_FRIEND_API(size_t) dataSize();                       /* Size of all data sections */
+    JS_FRIEND_API(size_t) dataSize(JSUsableSizeFun usf);    /* Size of all data sections */
     uint32 numNotes();                  /* Number of srcnote slots in the srcnotes section */
 
     /* Script notes are allocated right after the code. */
     jssrcnote *notes() { return (jssrcnote *)(code + length); }
 
     static const uint8 INVALID_OFFSET = 0xFF;
     static bool isValidOffset(uint8 offset) { return offset != INVALID_OFFSET; }
 
--- a/js/src/jstracer.cpp
+++ b/js/src/jstracer.cpp
@@ -88,16 +88,17 @@
 #include "jspropertycacheinlines.h"
 #include "jsobjinlines.h"
 #include "jsscopeinlines.h"
 #include "jsscriptinlines.h"
 #include "jscntxtinlines.h"
 #include "jsopcodeinlines.h"
 #include "jstypedarrayinlines.h"
 
+#include "vm/CallObject-inl.h"
 #include "vm/Stack-inl.h"
 
 #ifdef JS_METHODJIT
 #include "methodjit/MethodJIT.h"
 #endif
 
 #include "tracejit/Writer-inl.h"
 
@@ -2493,43 +2494,53 @@ TraceMonitor::getCodeAllocStats(size_t &
     } else {
         total = 0;
         frag_size = 0;
         free_size = 0;
     }
 }
 
 size_t
-TraceMonitor::getVMAllocatorsMainSize() const
+TraceMonitor::getVMAllocatorsMainSize(JSUsableSizeFun usf) const
 {
     size_t n = 0;
     if (dataAlloc)
-        n += dataAlloc->getBytesAllocated();
+        n += dataAlloc->getBytesAllocated(usf);
     if (traceAlloc)
-        n += traceAlloc->getBytesAllocated();
+        n += traceAlloc->getBytesAllocated(usf);
     if (tempAlloc)
-        n += tempAlloc->getBytesAllocated();
+        n += tempAlloc->getBytesAllocated(usf);
     return n;
 }
 
 size_t
-TraceMonitor::getVMAllocatorsReserveSize() const
-{
-    return dataAlloc->mReserveSize +
-           traceAlloc->mReserveSize +
-           tempAlloc->mReserveSize;
+TraceMonitor::getVMAllocatorsReserveSize(JSUsableSizeFun usf) const
+{
+    size_t usable = usf(dataAlloc->mReserve) +
+                    usf(traceAlloc->mReserve) +
+                    usf(tempAlloc->mReserve);
+    return usable ? usable : dataAlloc->mReserveSize +
+                             traceAlloc->mReserveSize +
+                             tempAlloc->mReserveSize;
 }
 
 size_t
-TraceMonitor::getTraceMonitorSize() const
-{
-    return sizeof(TraceMonitor) +           // TraceMonitor
-           sizeof(*storage) +               // TraceNativeStorage
-           recordAttempts->tableSize() +    // RecordAttemptMap
-           loopProfiles->tableSize();       // LoopProfileMap
+TraceMonitor::getTraceMonitorSize(JSUsableSizeFun usf) const
+{
+    /*
+     * Measures: TraceMonitor, TraceNativeStorage, RecordAttemptMap,
+     * LoopProfileMap.  |countMe| is true for both sizeOf() calls because the
+     * two HashMaps are not inline in TraceMonitor.
+     */
+    size_t usableTM  = usf((void *)this);
+    size_t usableTNS = usf(storage);
+    return (usableTM  ? usableTM  : sizeof(*this)) +
+           (usableTNS ? usableTNS : sizeof(*storage)) +
+           recordAttempts->sizeOf(usf, /* countMe */true) +
+           loopProfiles->sizeOf(usf, /* countMe */true);
 }
 
 /*
  * This function destroys the recorder after a successful recording, possibly
  * starting a suspended outer recorder.
  */
 AbortableRecordingStatus
 TraceRecorder::finishSuccessfully()
@@ -3271,37 +3282,37 @@ public:
         debug_only_printf(LC_TMTracer, "%s%u=", stackSlotKind(), 0);
         JSObject *frameobj = *(JSObject **)mStack;
         JS_ASSERT((frameobj == NULL) == (*mTypeMap == JSVAL_TYPE_NULL));
         if (p == fp->addressOfArgs()) {
             if (frameobj) {
                 JS_ASSERT_IF(fp->hasArgsObj(), frameobj == &fp->argsObj());
                 fp->setArgsObj(*frameobj->asArguments());
                 if (frameobj->isNormalArguments())
-                    frameobj->setPrivate(fp);
+                    frameobj->asArguments()->setStackFrame(fp);
                 else
-                    JS_ASSERT(!frameobj->getPrivate());
+                    JS_ASSERT(!frameobj->asArguments()->maybeStackFrame());
                 debug_only_printf(LC_TMTracer,
                                   "argsobj<%p> ",
                                   (void *)frameobj);
             } else {
                 JS_ASSERT(!fp->hasArgsObj());
                 debug_only_print0(LC_TMTracer,
                                   "argsobj<null> ");
             }
             /* else, SynthesizeFrame has initialized fp->args.nactual */
         } else {
             JS_ASSERT(p == fp->addressOfScopeChain());
             if (frameobj->isCall() &&
-                !frameobj->getPrivate() &&
-                fp->maybeCalleev().toObjectOrNull() == frameobj->getCallObjCallee())
+                !frameobj->asCall().maybeStackFrame() &&
+                fp->maybeCalleev().toObjectOrNull() == frameobj->asCall().getCallee())
             {
                 JS_ASSERT(&fp->scopeChain() == StackFrame::sInvalidScopeChain);
-                frameobj->setPrivate(fp);
-                fp->setScopeChainWithOwnCallObj(*frameobj);
+                frameobj->asCall().setStackFrame(fp);
+                fp->setScopeChainWithOwnCallObj(frameobj->asCall());
             } else {
                 fp->setScopeChainNoCallObj(*frameobj);
             }
             debug_only_printf(LC_TMTracer,
                               "scopechain<%p> ",
                               (void *)frameobj);
         }
 #ifdef DEBUG
@@ -3524,17 +3535,17 @@ GetFromClosure(JSContext* cx, JSObject* 
             //   is different.
             JS_NOT_REACHED("JSOP_NAME variable found in outer trace");
         }
     }
 #endif
 
     // We already guarded on trace that we aren't touching an outer tree's entry frame
     VOUCH_DOES_NOT_REQUIRE_STACK();
-    StackFrame* fp = (StackFrame*) call->getPrivate();
+    StackFrame* fp = call->asCall().maybeStackFrame();
     JS_ASSERT(fp != cx->fp());
 
     Value v;
     if (fp) {
         v = T::get_slot(fp, cv->slot);
     } else {
         /*
          * Get the value from the object. We know we have a Call object, and
@@ -3562,22 +3573,22 @@ struct ArgClosureTraits
 
     // Get the right object slots to use our slot index with.
     static inline Value get_slot(JSObject* obj, unsigned slot) {
         return obj->getSlot(slot_offset(obj) + slot);
     }
 
     // Get the offset of our object slots from the object's slots pointer.
     static inline uint32 slot_offset(JSObject* obj) {
-        return JSObject::CALL_RESERVED_SLOTS;
+        return CallObject::RESERVED_SLOTS;
     }
 
     // Get the maximum slot index of this type that should be allowed
     static inline uint16 slot_count(JSObject* obj) {
-        return obj->getCallObjCalleeFunction()->nargs;
+        return obj->asCall().getCalleeFunction()->nargs;
     }
 
 private:
     ArgClosureTraits();
 };
 
 uint32 JS_FASTCALL
 GetClosureArg(JSContext* cx, JSObject* callee, const ClosureVarInfo* cv, double* result)
@@ -3593,22 +3604,22 @@ struct VarClosureTraits
         return fp->slots()[slot];
     }
 
     static inline Value get_slot(JSObject* obj, unsigned slot) {
         return obj->getSlot(slot_offset(obj) + slot);
     }
 
     static inline uint32 slot_offset(JSObject* obj) {
-        return JSObject::CALL_RESERVED_SLOTS +
-               obj->getCallObjCalleeFunction()->nargs;
+        return CallObject::RESERVED_SLOTS +
+               obj->asCall().getCalleeFunction()->nargs;
     }
 
     static inline uint16 slot_count(JSObject* obj) {
-        return obj->getCallObjCalleeFunction()->script()->bindings.countVars();
+        return obj->asCall().getCalleeFunction()->script()->bindings.countVars();
     }
 
 private:
     VarClosureTraits();
 };
 
 uint32 JS_FASTCALL
 GetClosureVar(JSContext* cx, JSObject* callee, const ClosureVarInfo* cv, double* result)
@@ -8134,19 +8145,20 @@ TraceRecorder::entryFrameIns() const
 }
 
 /*
  * Return the frame of a call object if that frame is part of the current
  * trace. |depthp| is an optional outparam: if it is non-null, it will be
  * filled in with the depth of the call object's frame relevant to cx->fp().
  */
 JS_REQUIRES_STACK StackFrame*
-TraceRecorder::frameIfInRange(JSObject* obj, unsigned* depthp) const
-{
-    StackFrame* ofp = (StackFrame*) obj->getPrivate();
+TraceRecorder::frameIfInRange(JSObject *obj, unsigned* depthp) const
+{
+    JS_ASSERT(obj->isCall() || obj->isArguments());
+    StackFrame* ofp = (StackFrame *) obj->getPrivate();
     StackFrame* fp = cx->fp();
     for (unsigned depth = 0; depth <= callDepth; ++depth) {
         if (fp == ofp) {
             if (depthp)
                 *depthp = depth;
             return ofp;
         }
         if (!(fp = fp->prev()))
@@ -8249,34 +8261,35 @@ TraceRecorder::callProp(JSObject* obj, J
     JSOp op = JSOp(*cx->regs().pc);
     uint32 setflags = (js_CodeSpec[op].format & (JOF_SET | JOF_INCDEC | JOF_FOR));
     if (setflags && !shape->writable())
         RETURN_STOP("writing to a read-only property");
 
     uintN slot = uint16(shape->shortid);
 
     vp = NULL;
-    StackFrame* cfp = (StackFrame*) obj->getPrivate();
+    CallObject &callobj = obj->asCall();
+    StackFrame* cfp = callobj.maybeStackFrame();
     if (cfp) {
         if (shape->getterOp() == GetCallArg) {
             JS_ASSERT(slot < cfp->numFormalArgs());
             vp = &cfp->formalArg(slot);
             nr.v = *vp;
         } else if (shape->getterOp() == GetCallVar) {
             JS_ASSERT(slot < cfp->numSlots());
             vp = &cfp->slots()[slot];
             nr.v = *vp;
         } else {
             RETURN_STOP("dynamic property of Call object");
         }
 
         // Now assert that our use of shape->shortid was in fact kosher.
         JS_ASSERT(shape->hasShortID());
 
-        if (frameIfInRange(obj)) {
+        if (frameIfInRange(&callobj)) {
             // At this point we are guaranteed to be looking at an active call oject
             // whose properties are stored in the corresponding StackFrame.
             ins = get(vp);
             nr.tracked = true;
             return RECORD_CONTINUE;
         }
     } else {
         // Call objects do not yet have shape->isMethod() properties, but they
@@ -12284,21 +12297,23 @@ TraceRecorder::setUpwardTrackedVar(Value
     }
 
     set(stackVp, v_ins, promote);
 
     return RECORD_CONTINUE;
 }
 
 JS_REQUIRES_STACK RecordingStatus
-TraceRecorder::setCallProp(JSObject *callobj, LIns *callobj_ins, const Shape *shape,
+TraceRecorder::setCallProp(JSObject *obj, LIns *callobj_ins, const Shape *shape,
                            LIns *v_ins, const Value &v)
 {
+    CallObject &callobj = obj->asCall();
+
     // Set variables in on-trace-stack call objects by updating the tracker.
-    StackFrame *fp = frameIfInRange(callobj);
+    StackFrame *fp = frameIfInRange(&callobj);
     if (fp) {
         if (shape->setterOp() == SetCallArg) {
             JS_ASSERT(shape->hasShortID());
             uintN slot = uint16(shape->shortid);
             Value *vp2 = &fp->formalArg(slot);
             CHECK_STATUS(setUpwardTrackedVar(vp2, v, v_ins));
             return RECORD_CONTINUE;
         }
@@ -12307,40 +12322,40 @@ TraceRecorder::setCallProp(JSObject *cal
             uintN slot = uint16(shape->shortid);
             Value *vp2 = &fp->slots()[slot];
             CHECK_STATUS(setUpwardTrackedVar(vp2, v, v_ins));
             return RECORD_CONTINUE;
         }
         RETURN_STOP("can't trace special CallClass setter");
     }
 
-    if (!callobj->getPrivate()) {
+    if (!callobj.maybeStackFrame()) {
         // Because the parent guard in guardCallee ensures this Call object
         // will be the same object now and on trace, and because once a Call
         // object loses its frame it never regains one, on trace we will also
         // have a null private in the Call object. So all we need to do is
         // write the value to the Call object's slot.
         intN slot = uint16(shape->shortid);
         if (shape->setterOp() == SetCallArg) {
-            JS_ASSERT(slot < ArgClosureTraits::slot_count(callobj));
-            slot += ArgClosureTraits::slot_offset(callobj);
+            JS_ASSERT(slot < ArgClosureTraits::slot_count(&callobj));
+            slot += ArgClosureTraits::slot_offset(obj);
         } else if (shape->setterOp() == SetCallVar) {
-            JS_ASSERT(slot < VarClosureTraits::slot_count(callobj));
-            slot += VarClosureTraits::slot_offset(callobj);
+            JS_ASSERT(slot < VarClosureTraits::slot_count(&callobj));
+            slot += VarClosureTraits::slot_offset(obj);
         } else {
             RETURN_STOP("can't trace special CallClass setter");
         }
 
         // Now assert that the shortid get we did above was ok. Have to do it
         // after the RETURN_STOP above, since in that case we may in fact not
         // have a valid shortid; but we don't use it in that case anyway.
         JS_ASSERT(shape->hasShortID());
 
         LIns* slots_ins = NULL;
-        stobj_set_slot(callobj, callobj_ins, slot, slots_ins, v, v_ins);
+        stobj_set_slot(&callobj, callobj_ins, slot, slots_ins, v, v_ins);
         return RECORD_CONTINUE;
     }
 
     // This is the hard case: we have a StackFrame private, but it's not in
     // range.  During trace execution we may or may not have a StackFrame
     // anymore.  Call the standard builtins, which handle that situation.
 
     // Set variables in off-trace-stack call objects by calling standard builtins.
@@ -14982,17 +14997,17 @@ TraceRecorder::record_JSOP_POPN()
 {
     return ARECORD_CONTINUE;
 }
 
 static inline bool
 IsFindableCallObj(JSObject *obj)
 {
     return obj->isCall() &&
-           (obj->callIsForEval() || obj->getCallObjCalleeFunction()->isHeavyweight());
+           (obj->asCall().isForEval() || obj->asCall().getCalleeFunction()->isHeavyweight());
 }
 
 /*
  * Generate LIR to reach |obj2| from |obj| by traversing the scope chain. The
  * generated code also ensures that any call objects found have not changed shape.
  *
  *      obj               starting object
  *      obj_ins           LIR instruction representing obj
@@ -15119,18 +15134,17 @@ TraceRecorder::record_JSOP_BINDNAME()
             JS_ASSERT(obj);
         }
 
         /*
          * If this is a strict mode eval frame, we will have a Call object for
          * it. For now just don't trace this case.
          */
         if (obj != globalObj) {
-            JS_ASSERT(obj->isCall());
-            JS_ASSERT(obj->callIsForEval());
+            JS_ASSERT(obj->asCall().isForEval());
             RETURN_STOP_A("BINDNAME within strict eval code");
         }
 
         /*
          * The trace is specialized to this global object. Furthermore, we know
          * it is the sole 'global' object on the scope chain: we set globalObj
          * to the scope chain element with no parent, and we reached it
          * starting from the function closure or the current scopeChain, so
--- a/js/src/jstracer.h
+++ b/js/src/jstracer.h
@@ -1267,17 +1267,17 @@ class TraceRecorder
         JSObject         *obj;           // Call object where name was found
         nanojit::LIns    *obj_ins;       // LIR value for obj
         js::Shape        *shape;         // shape name was resolved to
     };
 
     JS_REQUIRES_STACK nanojit::LIns* scopeChain();
     JS_REQUIRES_STACK nanojit::LIns* entryScopeChain() const;
     JS_REQUIRES_STACK nanojit::LIns* entryFrameIns() const;
-    JS_REQUIRES_STACK StackFrame* frameIfInRange(JSObject* obj, unsigned* depthp = NULL) const;
+    JS_REQUIRES_STACK StackFrame* frameIfInRange(JSObject *obj, unsigned* depthp = NULL) const;
     JS_REQUIRES_STACK RecordingStatus traverseScopeChain(JSObject *obj, nanojit::LIns *obj_ins, JSObject *obj2, nanojit::LIns *&obj2_ins);
     JS_REQUIRES_STACK AbortableRecordingStatus scopeChainProp(JSObject* obj, const Value*& vp, nanojit::LIns*& ins, NameResult& nr, JSObject **scopeObjp = NULL);
     JS_REQUIRES_STACK RecordingStatus callProp(JSObject* obj, JSProperty* shape, jsid id, const Value*& vp, nanojit::LIns*& ins, NameResult& nr);
 
     JS_REQUIRES_STACK nanojit::LIns* arg(unsigned n);
     JS_REQUIRES_STACK void arg(unsigned n, nanojit::LIns* i);
     JS_REQUIRES_STACK nanojit::LIns* var(unsigned n);
     JS_REQUIRES_STACK void var(unsigned n, nanojit::LIns* i);
--- a/js/src/jstypedarray.cpp
+++ b/js/src/jstypedarray.cpp
@@ -60,16 +60,17 @@
 #include "jsstaticcheck.h"
 #include "jsbit.h"
 #include "jsvector.h"
 #include "jstypedarray.h"
 #include "jsutil.h"
 
 #include "vm/GlobalObject.h"
 
+#include "jsatominlines.h"
 #include "jsinferinlines.h"
 #include "jsobjinlines.h"
 #include "jstypedarrayinlines.h"
 
 using namespace js;
 using namespace js::gc;
 using namespace js::types;
 
@@ -272,44 +273,73 @@ ArrayBuffer::obj_lookupProperty(JSContex
         *propp = NULL;
         return true;
     }
 
     return proto->lookupProperty(cx, id, objp, propp);
 }
 
 JSBool
+ArrayBuffer::obj_lookupElement(JSContext *cx, JSObject *obj, uint32 index,
+                               JSObject **objp, JSProperty **propp)
+{
+    jsid id;
+    if (!IndexToId(cx, index, &id))
+        return false;
+    return obj_lookupProperty(cx, obj, id, objp, propp);
+}
+
+JSBool
 ArrayBuffer::obj_defineProperty(JSContext *cx, JSObject *obj, jsid id, const Value *v,
                    PropertyOp getter, StrictPropertyOp setter, uintN attrs)
 {
     if (JSID_IS_ATOM(id, cx->runtime->atomState.byteLengthAtom))
         return true;
 
     JSObject *delegate = DelegateObject(cx, obj);
     if (!delegate)
         return false;
     return js_DefineProperty(cx, delegate, id, v, getter, setter, attrs);
 }
 
 JSBool
+ArrayBuffer::obj_defineElement(JSContext *cx, JSObject *obj, uint32 index, const Value *v,
+                   PropertyOp getter, StrictPropertyOp setter, uintN attrs)
+{
+    jsid id;
+    if (!IndexToId(cx, index, &id))
+        return false;
+    return obj_defineProperty(cx, obj, id, v, getter, setter, attrs);
+}
+
+JSBool
 ArrayBuffer::obj_getProperty(JSContext *cx, JSObject *obj, JSObject *receiver, jsid id, Value *vp)
 {
     obj = getArrayBuffer(obj);
     if (JSID_IS_ATOM(id, cx->runtime->atomState.byteLengthAtom)) {
         vp->setInt32(obj->arrayBufferByteLength());
         return true;
     }
 
     JSObject *delegate = DelegateObject(cx, obj);
     if (!delegate)
         return false;
     return js_GetProperty(cx, delegate, receiver, id, vp);
 }
 
 JSBool
+ArrayBuffer::obj_getElement(JSContext *cx, JSObject *obj, JSObject *receiver, uint32 index, Value *vp)
+{
+    jsid id;
+    if (!IndexToId(cx, index, &id))
+        return false;
+    return obj_getProperty(cx, obj, receiver, id, vp);
+}
+
+JSBool
 ArrayBuffer::obj_setProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp, JSBool strict)
 {
     if (JSID_IS_ATOM(id, cx->runtime->atomState.byteLengthAtom))
         return true;
 
     if (JSID_IS_ATOM(id, cx->runtime->atomState.protoAtom)) {
         // setting __proto__ = null
         // effectively removes the prototype chain.
@@ -354,59 +384,95 @@ ArrayBuffer::obj_setProperty(JSContext *
     JSObject *delegate = DelegateObject(cx, obj);
     if (!delegate)
         return false;
 
     return js_SetPropertyHelper(cx, delegate, id, 0, vp, strict);
 }
 
 JSBool
+ArrayBuffer::obj_setElement(JSContext *cx, JSObject *obj, uint32 index, Value *vp, JSBool strict)
+{
+    jsid id;
+    if (!IndexToId(cx, index, &id))
+        return false;
+    return obj_setProperty(cx, obj, id, vp, strict);
+}
+
+JSBool
 ArrayBuffer::obj_getAttributes(JSContext *cx, JSObject *obj, jsid id, uintN *attrsp)
 {
     if (JSID_IS_ATOM(id, cx->runtime->atomState.byteLengthAtom)) {
         *attrsp = JSPROP_PERMANENT | JSPROP_READONLY;
         return true;
     }
 
     JSObject *delegate = DelegateObject(cx, obj);
     if (!delegate)
         return false;
     return js_GetAttributes(cx, delegate, id, attrsp);
 }
 
 JSBool
+ArrayBuffer::obj_getElementAttributes(JSContext *cx, JSObject *obj, uint32 index, uintN *attrsp)
+{
+    jsid id;
+    if (!IndexToId(cx, index, &id))
+        return false;
+    return obj_getAttributes(cx, obj, id, attrsp);
+}
+
+JSBool
 ArrayBuffer::obj_setAttributes(JSContext *cx, JSObject *obj, jsid id, uintN *attrsp)
 {
     if (JSID_IS_ATOM(id, cx->runtime->atomState.byteLengthAtom)) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
                              JSMSG_CANT_SET_ARRAY_ATTRS);
         return false;
     }
 
     JSObject *delegate = DelegateObject(cx, obj);
     if (!delegate)
         return false;
     return js_SetAttributes(cx, delegate, id, attrsp);
 }
 
 JSBool
+ArrayBuffer::obj_setElementAttributes(JSContext *cx, JSObject *obj, uint32 index, uintN *attrsp)
+{
+    jsid id;
+    if (!IndexToId(cx, index, &id))
+        return false;
+    return obj_setAttributes(cx, obj, id, attrsp);
+}
+
+JSBool
 ArrayBuffer::obj_deleteProperty(JSContext *cx, JSObject *obj, jsid id, Value *rval, JSBool strict)
 {
     if (JSID_IS_ATOM(id, cx->runtime->atomState.byteLengthAtom)) {
         rval->setBoolean(false);
         return true;
     }
 
     JSObject *delegate = DelegateObject(cx, obj);
     if (!delegate)
         return false;
     return js_DeleteProperty(cx, delegate, id, rval, strict);
 }
 
 JSBool
+ArrayBuffer::obj_deleteElement(JSContext *cx, JSObject *obj, uint32 index, Value *rval, JSBool strict)
+{
+    jsid id;
+    if (!IndexToId(cx, index, &id))
+        return false;
+    return obj_deleteElement(cx, obj, index, rval, strict);
+}
+
+JSBool
 ArrayBuffer::obj_enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
               Value *statep, jsid *idp)
 {
     statep->setNull();
     return true;
 }
 
 JSType
@@ -534,32 +600,60 @@ TypedArray::obj_lookupProperty(JSContext
         *propp = NULL;
         return true;
     }
 
     return proto->lookupProperty(cx, id, objp, propp);
 }
 
 JSBool
+TypedArray::obj_lookupElement(JSContext *cx, JSObject *obj, uint32 index,
+                              JSObject **objp, JSProperty **propp)
+{
+    jsid id;
+    if (!IndexToId(cx, index, &id))
+        return false;
+    return obj_lookupProperty(cx, obj, id, objp, propp);
+}
+
+JSBool
 TypedArray::obj_getAttributes(JSContext *cx, JSObject *obj, jsid id, uintN *attrsp)
 {
     *attrsp = (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom))
               ? JSPROP_PERMANENT | JSPROP_READONLY
               : JSPROP_PERMANENT | JSPROP_ENUMERATE;
     return true;
 }
 
 JSBool
+TypedArray::obj_getElementAttributes(JSContext *cx, JSObject *obj, uint32 index, uintN *attrsp)
+{
+    jsid id;
+    if (!IndexToId(cx, index, &id))
+        return false;
+    return obj_getAttributes(cx, obj, id, attrsp);
+}
+
+JSBool
 TypedArray::obj_setAttributes(JSContext *cx, JSObject *obj, jsid id, uintN *attrsp)
 {
     JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
                          JSMSG_CANT_SET_ARRAY_ATTRS);
     return false;
 }
 
+JSBool
+TypedArray::obj_setElementAttributes(JSContext *cx, JSObject *obj, uint32 index, uintN *attrsp)
+{
+    jsid id;
+    if (!IndexToId(cx, index, &id))
+        return false;
+    return obj_setAttributes(cx, obj, id, attrsp);
+}
+
 /* static */ int
 TypedArray::lengthOffset()
 {
     return JSObject::getFixedSlotOffset(FIELD_LENGTH) + offsetof(jsval_layout, s.payload);
 }
 
 /* static */ int
 TypedArray::dataOffset()
@@ -773,16 +867,25 @@ class TypedArrayTemplate
                 }
             }
         }
 
         return true;
     }
 
     static JSBool
+    obj_getElement(JSContext *cx, JSObject *obj, JSObject *receiver, uint32 index, Value *vp)
+    {
+        jsid id;
+        if (!IndexToId(cx, index, &id))
+            return false;
+        return obj_getProperty(cx, obj, receiver, id, vp);
+    }
+
+    static JSBool
     obj_setProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp, JSBool strict)
     {
         JSObject *tarray = getTypedArray(obj);
         JS_ASSERT(tarray);
 
         if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)) {
             vp->setNumber(getLength(tarray));
             return true;
@@ -850,27 +953,46 @@ class TypedArrayTemplate
             int32 n = js_DoubleToECMAInt32(d);
             setIndex(tarray, index, NativeType(n));
         }
 
         return true;
     }
 
     static JSBool
+    obj_setElement(JSContext *cx, JSObject *obj, uint32 index, Value *vp, JSBool strict)
+    {
+        jsid id;
+        if (!IndexToId(cx, index, &id))
+            return false;
+        return obj_setProperty(cx, obj, id, vp, strict);
+    }
+
+    static JSBool
     obj_defineProperty(JSContext *cx, JSObject *obj, jsid id, const Value *v,
                        PropertyOp getter, StrictPropertyOp setter, uintN attrs)
     {
         if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom))
             return true;
 
         Value tmp = *v;
         return obj_setProperty(cx, obj, id, &tmp, false);
     }
 
     static JSBool
+    obj_defineElement(JSContext *cx, JSObject *obj, uint32 index, const Value *v,
+                       PropertyOp getter, StrictPropertyOp setter, uintN attrs)
+    {
+        jsid id;
+        if (!IndexToId(cx, index, &id))
+            return false;
+        return obj_defineProperty(cx, obj, id, v, getter, setter, attrs);
+    }
+
+    static JSBool
     obj_deleteProperty(JSContext *cx, JSObject *obj, jsid id, Value *rval, JSBool strict)
     {
         if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)) {
             rval->setBoolean(false);
             return true;
         }
 
         JSObject *tarray = TypedArray::getTypedArray(obj);
@@ -881,16 +1003,25 @@ class TypedArrayTemplate
             return true;
         }
 
         rval->setBoolean(true);
         return true;
     }
 
     static JSBool
+    obj_deleteElement(JSContext *cx, JSObject *obj, uint32 index, Value *rval, JSBool strict)
+    {
+        jsid id;
+        if (!IndexToId(cx, index, &id))
+            return false;
+        return obj_deleteProperty(cx, obj, id, rval, strict);
+    }
+
+    static JSBool
     obj_enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
                   Value *statep, jsid *idp)
     {
         JSObject *tarray = getTypedArray(obj);
         JS_ASSERT(tarray);
 
         /*
          * Iteration is "length" (if JSENUMERATE_INIT_ALL), then [0, length).
@@ -1713,22 +1844,29 @@ Class js::ArrayBufferClass = {
     NULL,           /* call        */
     NULL,           /* construct   */
     NULL,           /* xdrObject   */
     NULL,           /* hasInstance */
     ArrayBuffer::obj_trace,
     JS_NULL_CLASS_EXT,
     {
         ArrayBuffer::obj_lookupProperty,
+        ArrayBuffer::obj_lookupElement,
         ArrayBuffer::obj_defineProperty,
+        ArrayBuffer::obj_defineElement,
         ArrayBuffer::obj_getProperty,
+        ArrayBuffer::obj_getElement,
         ArrayBuffer::obj_setProperty,
+        ArrayBuffer::obj_setElement,
         ArrayBuffer::obj_getAttributes,
+        ArrayBuffer::obj_getElementAttributes,
         ArrayBuffer::obj_setAttributes,
+        ArrayBuffer::obj_setElementAttributes,
         ArrayBuffer::obj_deleteProperty,
+        ArrayBuffer::obj_deleteElement,
         ArrayBuffer::obj_enumerate,
         ArrayBuffer::obj_typeOf,
         NULL,       /* thisObject      */
         NULL,       /* clear           */
     }
 };
 
 JSPropertySpec ArrayBuffer::jsprops[] = {
@@ -1804,22 +1942,29 @@ JSFunctionSpec _typedArray::jsfuncs[] = 
     NULL,           /* call        */                                          \
     NULL,           /* construct   */                                          \
     NULL,           /* xdrObject   */                                          \
     NULL,           /* hasInstance */                                          \
     _typedArray::obj_trace,           /* trace       */                                          \
     JS_NULL_CLASS_EXT,                                                         \
     {                                                                          \
         _typedArray::obj_lookupProperty,                                       \
+        _typedArray::obj_lookupElement,                                        \
         _typedArray::obj_defineProperty,                                       \
+        _typedArray::obj_defineElement,                                        \
         _typedArray::obj_getProperty,                                          \
+        _typedArray::obj_getElement,                                           \
         _typedArray::obj_setProperty,                                          \
+        _typedArray::obj_setElement,                                           \
         _typedArray::obj_getAttributes,                                        \
+        _typedArray::obj_getElementAttributes,                                 \
         _typedArray::obj_setAttributes,                                        \
+        _typedArray::obj_setElementAttributes,                                 \
         _typedArray::obj_deleteProperty,                                       \
+        _typedArray::obj_deleteElement,                                        \
         _typedArray::obj_enumerate,                                            \
         _typedArray::obj_typeOf,                                               \
         NULL,       /* thisObject      */                                      \
         NULL,       /* clear           */                                      \
     }                                                                          \
 }
 
 template<class ArrayType>
--- a/js/src/jstypedarray.h
+++ b/js/src/jstypedarray.h
@@ -74,35 +74,58 @@ struct JS_FRIEND_API(ArrayBuffer) {
     static void
     obj_trace(JSTracer *trc, JSObject *obj);
 
     static JSBool
     obj_lookupProperty(JSContext *cx, JSObject *obj, jsid id,
                        JSObject **objp, JSProperty **propp);
 
     static JSBool
+    obj_lookupElement(JSContext *cx, JSObject *obj, uint32 index,
+                      JSObject **objp, JSProperty **propp);
+
+    static JSBool
     obj_defineProperty(JSContext *cx, JSObject *obj, jsid id, const Value *v,
                        PropertyOp getter, StrictPropertyOp setter, uintN attrs);
 
     static JSBool
+    obj_defineElement(JSContext *cx, JSObject *obj, uint32 index, const Value *v,
+                      PropertyOp getter, StrictPropertyOp setter, uintN attrs);
+
+    static JSBool
     obj_getProperty(JSContext *cx, JSObject *obj, JSObject *receiver, jsid id, Value *vp);
 
     static JSBool
+    obj_getElement(JSContext *cx, JSObject *obj, JSObject *receiver, uint32 index, Value *vp);
+
+    static JSBool
     obj_setProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp, JSBool strict);
 
     static JSBool
+    obj_setElement(JSContext *cx, JSObject *obj, uint32 index, Value *vp, JSBool strict);
+
+    static JSBool
     obj_getAttributes(JSContext *cx, JSObject *obj, jsid id, uintN *attrsp);
 
     static JSBool
+    obj_getElementAttributes(JSContext *cx, JSObject *obj, uint32 index, uintN *attrsp);
+
+    static JSBool
     obj_setAttributes(JSContext *cx, JSObject *obj, jsid id, uintN *attrsp);
 
     static JSBool
+    obj_setElementAttributes(JSContext *cx, JSObject *obj, uint32 index, uintN *attrsp);
+
+    static JSBool
     obj_deleteProperty(JSContext *cx, JSObject *obj, jsid id, Value *rval, JSBool strict);
 
     static JSBool
+    obj_deleteElement(JSContext *cx, JSObject *obj, uint32 index, Value *rval, JSBool strict);
+
+    static JSBool
     obj_enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
                   Value *statep, jsid *idp);
 
     static JSType
     obj_typeOf(JSContext *cx, JSObject *obj);
 
     static JSObject *
     getArrayBuffer(JSObject *obj);
@@ -159,20 +182,24 @@ struct JS_FRIEND_API(TypedArray) {
 
     static JSBool prop_getBuffer(JSContext *cx, JSObject *obj, jsid id, Value *vp);
     static JSBool prop_getByteOffset(JSContext *cx, JSObject *obj, jsid id, Value *vp);
     static JSBool prop_getByteLength(JSContext *cx, JSObject *obj, jsid id, Value *vp);
     static JSBool prop_getLength(JSContext *cx, JSObject *obj, jsid id, Value *vp);
 
     static JSBool obj_lookupProperty(JSContext *cx, JSObject *obj, jsid id,
                                      JSObject **objp, JSProperty **propp);
+    static JSBool obj_lookupElement(JSContext *cx, JSObject *obj, uint32 index,
+                                    JSObject **objp, JSProperty **propp);
 
     static JSBool obj_getAttributes(JSContext *cx, JSObject *obj, jsid id, uintN *attrsp);
+    static JSBool obj_getElementAttributes(JSContext *cx, JSObject *obj, uint32 index, uintN *attrsp);
 
     static JSBool obj_setAttributes(JSContext *cx, JSObject *obj, jsid id, uintN *attrsp);
+    static JSBool obj_setElementAttributes(JSContext *cx, JSObject *obj, uint32 index, uintN *attrsp);
 
     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);
 
--- a/js/src/jsutil.h
+++ b/js/src/jsutil.h
@@ -262,16 +262,23 @@ static JS_INLINE void* js_realloc(void* 
     return realloc(p, bytes);
 }
 
 static JS_INLINE void js_free(void* p) {
     free(p);
 }
 #endif/* JS_USE_CUSTOM_ALLOCATOR */
 
+/* 
+ * This signature is for malloc_usable_size-like functions used to measure
+ * memory usage.  A return value of zero indicates that the size is unknown,
+ * and so a fall-back computation should be done for the size.
+ */
+typedef size_t(*JSUsableSizeFun)(void *p);
+
 JS_END_EXTERN_C
 
 
 
 #ifdef __cplusplus
 
 /* 
  * User guide to memory management within SpiderMonkey:
--- a/js/src/jsvalue.h
+++ b/js/src/jsvalue.h
@@ -944,28 +944,42 @@ typedef JSBool
 (* CheckAccessOp)(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode,
                   Value *vp);
 typedef JSBool
 (* EqualityOp)(JSContext *cx, JSObject *obj, const Value *v, JSBool *bp);
 typedef JSBool
 (* DefinePropOp)(JSContext *cx, JSObject *obj, jsid id, const Value *value,
                  PropertyOp getter, StrictPropertyOp setter, uintN attrs);
 typedef JSBool
+(* DefineElementOp)(JSContext *cx, JSObject *obj, uint32 index, const Value *value,
+                    PropertyOp getter, StrictPropertyOp setter, uintN attrs);
+typedef JSBool
 (* PropertyIdOp)(JSContext *cx, JSObject *obj, JSObject *receiver, jsid id, Value *vp);
 typedef JSBool
+(* ElementIdOp)(JSContext *cx, JSObject *obj, JSObject *receiver, uint32 index, Value *vp);
+typedef JSBool
 (* StrictPropertyIdOp)(JSContext *cx, JSObject *obj, jsid id, Value *vp, JSBool strict);
 typedef JSBool
+(* StrictElementIdOp)(JSContext *cx, JSObject *obj, uint32 index, Value *vp, JSBool strict);
+typedef JSBool
 (* DeleteIdOp)(JSContext *cx, JSObject *obj, jsid id, Value *vp, JSBool strict);
 typedef JSBool
+(* DeleteElementOp)(JSContext *cx, JSObject *obj, uint32 index, Value *vp, JSBool strict);
+typedef JSBool
 (* CallOp)(JSContext *cx, uintN argc, Value *vp);
 typedef JSBool
 (* LookupPropOp)(JSContext *cx, JSObject *obj, jsid id, JSObject **objp,
                  JSProperty **propp);
 typedef JSBool
+(* LookupElementOp)(JSContext *cx, JSObject *obj, uint32 index, JSObject **objp,
+                    JSProperty **propp);
+typedef JSBool
 (* AttributesOp)(JSContext *cx, JSObject *obj, jsid id, uintN *attrsp);
+typedef JSBool
+(* ElementAttributesOp)(JSContext *cx, JSObject *obj, uint32 index, uintN *attrsp);
 typedef JSType
 (* TypeOfOp)(JSContext *cx, JSObject *obj);
 typedef JSObject *
 (* ObjectOp)(JSContext *cx, JSObject *obj);
 typedef void
 (* FinalizeOp)(JSContext *cx, JSObject *obj);
 
 class AutoIdVector;
@@ -1053,30 +1067,40 @@ struct ClassExtension {
      */
     bool                isWrappedNative;
 };
 
 #define JS_NULL_CLASS_EXT   {NULL,NULL,NULL,NULL,NULL,false}
 
 struct ObjectOps {
     js::LookupPropOp        lookupProperty;
+    js::LookupElementOp     lookupElement;
     js::DefinePropOp        defineProperty;
+    js::DefineElementOp     defineElement;
     js::PropertyIdOp        getProperty;
+    js::ElementIdOp         getElement;
     js::StrictPropertyIdOp  setProperty;
+    js::StrictElementIdOp   setElement;
     js::AttributesOp        getAttributes;
+    js::ElementAttributesOp getElementAttributes;
     js::AttributesOp        setAttributes;
+    js::ElementAttributesOp setElementAttributes;
     js::DeleteIdOp          deleteProperty;
+    js::DeleteElementOp     deleteElement;
+
     js::NewEnumerateOp      enumerate;
     js::TypeOfOp            typeOf;
     js::FixOp               fix;
     js::ObjectOp            thisObject;
     js::FinalizeOp          clear;
 };
 
-#define JS_NULL_OBJECT_OPS  {NULL,NULL,NULL,NULL,NULL,NULL, NULL,NULL,NULL,NULL,NULL,NULL}
+#define JS_NULL_OBJECT_OPS                                                    \
+    {NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,   \
+     NULL,NULL,NULL,NULL,NULL}
 
 struct Class {
     JS_CLASS_MEMBERS;
     ClassExtension      ext;
     ObjectOps           ops;
     uint8               pad[sizeof(JSClass) - sizeof(ClassSizeMeasurement) -
                             sizeof(ClassExtension) - sizeof(ObjectOps)];
 
--- a/js/src/jsweakmap.h
+++ b/js/src/jsweakmap.h
@@ -94,17 +94,17 @@ namespace js {
 //        became marked.
 //
 //        To ensure that the WeakMap's behavior isn't visibly affected by
 //        garbage collection, this should leave k unmarked only when no key
 //        matching k could ever be produced after this GC cycle completes ---
 //        removing entries whose keys this function leaves unmarked should never
 //        make future lookups fail.
 //
-//        A typical definition of markIteratively would be:
+//        A typical definition of markEntryIfLive would be:
 //
 //          if (keyMarked(k) && !valueMarked(v)) {
 //              markObject(*v, "WeakMap entry value");
 //              return true;
 //          }
 //          return false;
 //
 //        This meets the above constraint when, for example, Key is JSObject *:
@@ -112,21 +112,22 @@ namespace js {
 //        and thus can't be supplied as a key.
 //
 //        Note that this may mark entries where keyMarked(k) is not initially
 //        true. For example, you could have a table whose keys match when the
 //        values of one of their properties are equal: k1.x === k2.x. An entry
 //        in such a table could be live even when its key is not marked. The
 //        markEntryIfLive function for such a table would generally mark both k and v.
 //
-//     void markEntry(Key &k, Value &v)
-//        Mark the table entry's key and value, k and v, as reachable by the
-//        collector. WeakMap uses this function for non-marking tracers: other
-//        code using the GC heap tracing functions to map the heap for some
-//        purpose or other.
+//     void markEntry(Value &v)
+//        Mark the table entry's value v as reachable by the collector. WeakMap
+//        uses this function for non-marking tracers: other code using the GC
+//        heap tracing functions to map the heap for some purpose or other.
+//        This provides a conservative approximation of the true reachability
+//        relation of the heap graph.
 //
 //   If omitted, the MarkPolicy parameter defaults to js::DefaultMarkPolicy<Key,
 //   Value>, a policy template with the obvious definitions for some typical
 //   SpiderMonkey type combinations.
 
 // A policy template holding default marking algorithms for common type combinations. This
 // provides default types for WeakMap's MarkPolicy template parameter.
 template <class Key, class Value> class DefaultMarkPolicy;
@@ -192,17 +193,17 @@ class WeakMap : public HashMap<Key, Valu
   public:
     explicit WeakMap(JSRuntime *rt) : Base(rt) { }
     explicit WeakMap(JSContext *cx) : Base(cx) { }
 
   private:
     void nonMarkingTrace(JSTracer *tracer) {
         MarkPolicy t(tracer);
         for (Range r = Base::all(); !r.empty(); r.popFront())
-            t.markEntry(r.front().key, r.front().value);
+            t.markEntry(r.front().value);
     }
 
     bool markIteratively(JSTracer *tracer) {
         MarkPolicy t(tracer);
         bool markedAny = false;
         for (Range r = Base::all(); !r.empty(); r.popFront()) {
             /* If the entry is live, ensure its key and value are marked. */
             if (t.markEntryIfLive(r.front().key, r.front().value)) {
@@ -276,18 +277,17 @@ class DefaultMarkPolicy<JSObject *, Valu
         if (keyMarked(k))
             return markUnmarkedValue(v);
         if (!overrideKeyMarking(k))
             return false;
         js::gc::MarkObject(tracer, *k, "WeakMap entry wrapper key");
         markUnmarkedValue(v);
         return true;
     }
-    void markEntry(JSObject *k, const Value &v) {
-        js::gc::MarkObject(tracer, *k, "WeakMap entry key");
+    void markEntry(const Value &v) {
         js::gc::MarkValue(tracer, v, "WeakMap entry value");
     }
 };
 
 template <>
 class DefaultMarkPolicy<JSObject *, JSObject *> {
   protected:
     JSTracer *tracer;
@@ -297,18 +297,17 @@ class DefaultMarkPolicy<JSObject *, JSOb
     bool valueMarked(JSObject *v) { return !IsAboutToBeFinalized(tracer->context, v); }
     bool markEntryIfLive(JSObject *k, JSObject *v) {
         if (keyMarked(k) && !valueMarked(v)) {
             js::gc::MarkObject(tracer, *v, "WeakMap entry value");
             return true;
         }
         return false;
     }
-    void markEntry(JSObject *k, JSObject *v) {
-        js::gc::MarkObject(tracer, *k, "WeakMap entry key");
+    void markEntry(JSObject *v) {
         js::gc::MarkObject(tracer, *v, "WeakMap entry value");
     }
 };
 
 // A MarkPolicy for WeakMaps whose keys and values may be objects in arbitrary
 // compartments within a runtime.
 //
 // With the current GC, the implementation turns out to be identical to the
--- a/js/src/jsxml.cpp
+++ b/js/src/jsxml.cpp
@@ -4729,73 +4729,129 @@ xml_lookupProperty(JSContext *cx, JSObje
 
         *objp = obj;
         *propp = (JSProperty *) shape;
     }
     return JS_TRUE;
 }
 
 static JSBool
+xml_lookupElement(JSContext *cx, JSObject *obj, uint32 index, JSObject **objp,
+                  JSProperty **propp)
+{
+    jsid id;
+    if (!IndexToId(cx, index, &id))
+        return false;
+    return xml_lookupProperty(cx, obj, id, objp, propp);
+}
+
+static JSBool
 xml_defineProperty(JSContext *cx, JSObject *obj, jsid id, const Value *v,
                    PropertyOp getter, StrictPropertyOp setter, uintN attrs)
 {
     if (IsFunctionObject(*v) || getter || setter ||
         (attrs & JSPROP_ENUMERATE) == 0 ||
         (attrs & (JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_SHARED))) {
         return js_DefineProperty(cx, obj, id, v, getter, setter, attrs);
     }
 
     jsval tmp = Jsvalify(*v);
     return PutProperty(cx, obj, id, false, &tmp);
 }
 
 static JSBool
+xml_defineElement(JSContext *cx, JSObject *obj, uint32 index, const Value *v,
+                  PropertyOp getter, StrictPropertyOp setter, uintN attrs)
+{
+    jsid id;
+    if (!IndexToId(cx, index, &id))
+        return false;
+    return xml_defineProperty(cx, obj, id, v, getter, setter, attrs);
+}
+
+static JSBool
 xml_getProperty(JSContext *cx, JSObject *obj, JSObject *receiver, jsid id, Value *vp)
 {
     if (JSID_IS_DEFAULT_XML_NAMESPACE(id)) {
         vp->setUndefined();
         return JS_TRUE;
     }
 
     return GetProperty(cx, obj, id, Jsvalify(vp));
 }
 
 static JSBool
+xml_getElement(JSContext *cx, JSObject *obj, JSObject *receiver, uint32 index, Value *vp)
+{
+    jsid id;
+    if (!IndexToId(cx, index, &id))
+        return false;
+    return xml_getProperty(cx, obj, receiver, id, vp);
+}
+
+static JSBool
 xml_setProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp, JSBool strict)
 {
     return PutProperty(cx, obj, id, strict, Jsvalify(vp));
 }
 
 static JSBool
+xml_setElement(JSContext *cx, JSObject *obj, uint32 index, Value *vp, JSBool strict)
+{
+    jsid id;
+    if (!IndexToId(cx, index, &id))
+        return false;
+    return xml_setProperty(cx, obj, id, vp, strict);
+}
+
+static JSBool
 xml_getAttributes(JSContext *cx, JSObject *obj, jsid id, uintN *attrsp)
 {
     JSBool found;
     if (!HasProperty(cx, obj, IdToJsval(id), &found))
         return false;
 
     *attrsp = found ? JSPROP_ENUMERATE : 0;
     return JS_TRUE;
 }
 
 static JSBool
+xml_getElementAttributes(JSContext *cx, JSObject *obj, uint32 index, uintN *attrsp)
+{
+    jsid id;
+    if (!IndexToId(cx, index, &id))
+        return false;
+    return xml_getAttributes(cx, obj, id, attrsp);
+}
+
+static JSBool
 xml_setAttributes(JSContext *cx, JSObject *obj, jsid id, uintN *attrsp)
 {
     JSBool found;
     if (!HasProperty(cx, obj, IdToJsval(id), &found))
         return false;
 
     if (found) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
                              JSMSG_CANT_SET_XML_ATTRS);
         return false;
     }
     return true;
 }
 
 static JSBool
+xml_setElementAttributes(JSContext *cx, JSObject *obj, uint32 index, uintN *attrsp)
+{
+    jsid id;
+    if (!IndexToId(cx, index, &id))
+        return false;
+    return xml_setAttributes(cx, obj, id, attrsp);
+}
+
+static JSBool
 xml_deleteProperty(JSContext *cx, JSObject *obj, jsid id, Value *rval, JSBool strict)
 {
     JSXML *xml;
     jsval idval;
     uint32 index;
     JSObject *nameqn;
     jsid funid;
 
@@ -4830,16 +4886,25 @@ xml_deleteProperty(JSContext *cx, JSObje
      */
     if (!obj->nativeEmpty() && !js_DeleteProperty(cx, obj, id, rval, false))
         return false;
 
     rval->setBoolean(true);
     return true;
 }
 
+static JSBool
+xml_deleteElement(JSContext *cx, JSObject *obj, uint32 index, Value *rval, JSBool strict)
+{
+    jsid id;
+    if (!IndexToId(cx, index, &id))
+        return false;
+    return xml_deleteProperty(cx, obj, id, rval, strict);
+}
+
 static JSString *
 xml_toString_helper(JSContext *cx, JSXML *xml);
 
 JSBool
 xml_convert(JSContext *cx, JSObject *obj, JSType hint, Value *rval)
 {
     JS_ASSERT(hint == JSTYPE_NUMBER || hint == JSTYPE_STRING || hint == JSTYPE_VOID);
     JS_ASSERT(obj->isXML());
@@ -5129,22 +5194,29 @@ JS_FRIEND_DATA(Class) js::XMLClass = {
     NULL,                 /* call        */
     NULL,                 /* construct   */
     NULL,                 /* xdrObject   */
     xml_hasInstance,
     xml_trace,
     JS_NULL_CLASS_EXT,
     {
         xml_lookupProperty,
+        xml_lookupElement,
         xml_defineProperty,
+        xml_defineElement,
         xml_getProperty,
+        xml_getElement,
         xml_setProperty,
+        xml_setElement,
         xml_getAttributes,
+        xml_getElementAttributes,
         xml_setAttributes,
+        xml_setElementAttributes,
         xml_deleteProperty,
+        xml_deleteElement,
         xml_enumerate,
         xml_typeOf,
         xml_fix,
         NULL,       /* thisObject     */
         xml_clear
     }
 };
 
--- a/js/src/methodjit/MethodJIT.cpp
+++ b/js/src/methodjit/MethodJIT.cpp
@@ -1133,31 +1133,31 @@ mjit::JITScript::~JITScript()
         repatch.repatch(ic->funGuard, NULL);
         repatch.relink(ic->funJump, ic->slowPathStart);
         ic->purgeGuardedObject();
     }
 #endif
 }
 
 size_t
-JSScript::jitDataSize(size_t(*mus)(void *))
+JSScript::jitDataSize(JSUsableSizeFun usf)
 {
     size_t n = 0;
     if (jitNormal)
-        n += jitNormal->scriptDataSize(mus); 
+        n += jitNormal->scriptDataSize(usf); 
     if (jitCtor)
-        n += jitCtor->scriptDataSize(mus); 
+        n += jitCtor->scriptDataSize(usf); 
     return n;
 }
 
 /* Please keep in sync with Compiler::finishThisUp! */
 size_t
-mjit::JITScript::scriptDataSize(size_t(*mus)(void *))
+mjit::JITScript::scriptDataSize(JSUsableSizeFun usf)
 {
-    size_t usable = mus ? mus(this) : 0;
+    size_t usable = usf ? usf(this) : 0;
     return usable ? usable :
         sizeof(JITScript) +
         sizeof(NativeMapEntry) * nNmapPairs +
         sizeof(InlineFrame) * nInlineFrames +
         sizeof(CallSite) * nCallSites +
         sizeof(JSObject *) * nRootedObjects +
 #if defined JS_MONOIC
         sizeof(ic::GetGlobalNameIC) * nGetGlobalNames +
--- a/js/src/methodjit/MethodJIT.h
+++ b/js/src/methodjit/MethodJIT.h
@@ -638,17 +638,18 @@ struct JITScript {
 
     void nukeScriptDependentICs();
     void sweepCallICs(JSContext *cx, bool purgeAll);
     void purgeMICs();
     void purgePICs();
 
     void trace(JSTracer *trc);
 
-    size_t scriptDataSize(size_t(*mus)(void *));
+    /* |usf| can be NULL here, in which case the fallback size computation will be used. */
+    size_t scriptDataSize(JSUsableSizeFun usf);
 
     jsbytecode *nativeToPC(void *returnAddress, CallSite **pinline) const;
 
   private:
     /* Helpers used to navigate the variable-length sections. */
     char *commonSectionLimit() const;
     char *monoICSectionsLimit() const;
     char *polyICSectionsLimit() const;
--- a/js/src/methodjit/PolyIC.cpp
+++ b/js/src/methodjit/PolyIC.cpp
@@ -49,16 +49,18 @@
 #include "jsatominlines.h"
 #include "jsobjinlines.h"
 #include "jsscopeinlines.h"
 #include "jspropertycache.h"
 #include "jspropertycacheinlines.h"
 #include "jsinterpinlines.h"
 #include "jsautooplen.h"
 
+#include "vm/CallObject-inl.h"
+
 #if defined JS_POLYIC
 
 using namespace js;
 using namespace js::mjit;
 using namespace js::mjit::ic;
 
 typedef JSC::FunctionPtr FunctionPtr;
 typedef JSC::MacroAssembler::RegisterID RegisterID;
@@ -416,17 +418,17 @@ class SetPropCompiler : public PICStubCo
         } else {
             //   \ /        In general, two function objects with different JSFunctions
             //    #         can have the same shape, thus we must not rely on the identity
             // >--+--<      of 'fun' remaining the same. However, since:
             //   |||         1. the shape includes all arguments and locals and their setters
             //    \\     V     and getters, and
             //      \===/    2. arguments and locals have different getters
             //              then we can rely on fun->nargs remaining invariant.
-            JSFunction *fun = obj->getCallObjCalleeFunction();
+            JSFunction *fun = obj->asCall().getCalleeFunction();
             uint16 slot = uint16(shape->shortid);
 
             /* Guard that the call object has a frame. */
             masm.loadObjPrivate(pic.objReg, pic.shapeReg);
             Jump escapedFrame = masm.branchTestPtr(Assembler::Zero, pic.shapeReg, pic.shapeReg);
 
             {
                 Address addr(pic.shapeReg, shape->setterOp() == SetCallArg
@@ -436,17 +438,17 @@ class SetPropCompiler : public PICStubCo
                 skipOver = masm.jump();
             }
 
             escapedFrame.linkTo(masm.label(), &masm);
             {
                 if (shape->setterOp() == SetCallVar)
                     slot += fun->nargs;
 
-                slot += JSObject::CALL_RESERVED_SLOTS;
+                slot += CallObject::RESERVED_SLOTS;
                 Address address = masm.objPropAddress(obj, pic.objReg, slot);
 
                 masm.storeValue(pic.u.vr, address);
             }
 
             pic.shapeRegHasBaseShape = false;
         }
 
@@ -686,17 +688,17 @@ class SetPropCompiler : public PICStubCo
                  * shape guards we have performed do not by themselves
                  * guarantee that future call objects hit will be for the same
                  * script. We also depend on the fact that the scope chains hit
                  * at the same bytecode are all isomorphic: the same scripts,
                  * in the same order (though the properties on their call
                  * objects may differ due to eval(), DEFFUN, etc.).
                  */
                 RecompilationMonitor monitor(cx);
-                JSFunction *fun = obj->getCallObjCalleeFunction();
+                JSFunction *fun = obj->asCall().getCalleeFunction();
                 JSScript *script = fun->script();
                 uint16 slot = uint16(shape->shortid);
                 if (!script->ensureHasTypes(cx, fun))
                     return error();
                 {
                     types::AutoEnterTypeInference enter(cx);
                     if (shape->setterOp() == SetCallArg)
                         pic.rhsTypes->addSubset(cx, types::TypeScript::ArgTypes(script, slot));
@@ -1499,17 +1501,17 @@ class ScopeNameCompiler : public PICStub
             Value *thisVp = &cx->regs().sp[1];
             Address thisSlot(JSFrameReg, StackFrame::offsetOfFixed(thisVp - cx->fp()->slots()));
             masm.storeValue(UndefinedValue(), thisSlot);
         }
 
         /* Get callobj's stack frame. */
         masm.loadObjPrivate(pic.objReg, pic.shapeReg);
 
-        JSFunction *fun = getprop.holder->getCallObjCalleeFunction();
+        JSFunction *fun = getprop.holder->asCall().getCalleeFunction();
         uint16 slot = uint16(shape->shortid);
 
         Jump skipOver;
         Jump escapedFrame = masm.branchTestPtr(Assembler::Zero, pic.shapeReg, pic.shapeReg);
 
         /* Not-escaped case. */
         {
             Address addr(pic.shapeReg, kind == ARG ? StackFrame::offsetOfFormalArg(fun, slot)
@@ -1520,17 +1522,17 @@ class ScopeNameCompiler : public PICStub
         }
 
         escapedFrame.linkTo(masm.label(), &masm);
 
         {
             if (kind == VAR)
                 slot += fun->nargs;
 
-            slot += JSObject::CALL_RESERVED_SLOTS;
+            slot += CallObject::RESERVED_SLOTS;
             Address address = masm.objPropAddress(obj, pic.objReg, slot);
 
             /* Safe because type is loaded first. */
             masm.loadValueAsComponents(address, pic.shapeReg, pic.objReg);
         }
 
         skipOver.linkTo(masm.label(), &masm);
         Jump done = masm.jump();
--- a/js/src/methodjit/StubCalls.cpp
+++ b/js/src/methodjit/StubCalls.cpp
@@ -477,17 +477,17 @@ stubs::GetElem(VMFrame &f)
             }
         } else if (obj->isArguments()) {
             uint32 arg = uint32(i);
             ArgumentsObject *argsobj = obj->asArguments();
 
             if (arg < argsobj->initialLength()) {
                 copyFrom = &argsobj->element(arg);
                 if (!copyFrom->isMagic()) {
-                    if (StackFrame *afp = (StackFrame *) argsobj->getPrivate())
+                    if (StackFrame *afp = argsobj->maybeStackFrame())
                         copyFrom = &afp->canonicalActualArg(arg);
                     goto end_getelem;
                 }
             }
         }
         if (JS_LIKELY(INT_FITS_IN_JSID(i)))
             id = INT_TO_JSID(i);
         else
--- a/js/src/nanojit-import-rev
+++ b/js/src/nanojit-import-rev
@@ -1,1 +1,1 @@
-55c10227eece4a02b593997eda3dedef39af7beb
+d66d2e24ef1688e3b6d47e824354f5a78ac22487
--- a/js/src/nanojit/Allocator.cpp
+++ b/js/src/nanojit/Allocator.cpp
@@ -95,21 +95,22 @@ namespace nanojit
             current_limit = (char*)mem + chunkbytes;
             return true;
         } else {
             NanoAssert(fallible);
             return false;
         }
     }
 
-    size_t Allocator::getBytesAllocated()
+    size_t Allocator::getBytesAllocated(size_t(*my_malloc_usable_size)(void *))
     {
         size_t n = 0;
         Chunk *c = current_chunk;
         while (c) {
-            n += c->size;
+            size_t usable = my_malloc_usable_size(c);
+            n += usable ? usable : c->size;
             c = c->prev;
         }
         return n;
     }
 }
 
 #endif // FEATURE_NANOJIT
--- a/js/src/nanojit/Allocator.h
+++ b/js/src/nanojit/Allocator.h
@@ -85,17 +85,17 @@ namespace nanojit
                 p = current_top;
                 current_top += nbytes;
             } else {
                 p = allocSlow(nbytes, /* fallible = */true);
             }
             return p;
         }
 
-        size_t getBytesAllocated();
+        size_t getBytesAllocated(size_t(*my_malloc_usable_size)(void *));
 
     protected:
         void* allocSlow(size_t nbytes, bool fallible = false);
         bool fill(size_t minbytes, bool fallible);
 
         class Chunk {
         public:
             Chunk* prev;
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -4064,24 +4064,34 @@ MJitCodeStats(JSContext *cx, uintN argc,
 #else
     JS_SET_RVAL(cx, vp, JSVAL_VOID);
 #endif
     return true;
 }
 
 #ifdef JS_METHODJIT
 
+static size_t
+zero_usable_size(void *p)
+{
+    return 0;
+}
+
 static void
 SumJitDataSizeCallback(JSContext *cx, void *data, void *thing,
                        JSGCTraceKind traceKind, size_t thingSize)
 {
     size_t *sump = static_cast<size_t *>(data);
     JS_ASSERT(traceKind == JSTRACE_SCRIPT);
     JSScript *script = static_cast<JSScript *>(thing);
-    *sump += script->jitDataSize(NULL);
+    /*
+     * Passing in zero_usable_size causes jitDataSize to fall back to its
+     * secondary size computation.
+     */
+    *sump += script->jitDataSize(zero_usable_size);
 }
 
 #endif
 
 JSBool
 MJitDataStats(JSContext *cx, uintN argc, jsval *vp)
 {
 #ifdef JS_METHODJIT
--- a/js/src/tests/js1_8/genexps/jstests.list
+++ b/js/src/tests/js1_8/genexps/jstests.list
@@ -7,8 +7,9 @@ script regress-380237-01.js
 script regress-380237-02.js
 script regress-380237-03.js
 skip script regress-380237-04.js # obsolete test, need to remove minor failures to reenable.
 script regress-384991.js
 script regress-634472.js
 script regress-665286.js
 script regress-666852.js
 script regress-667131.js
+script regress-683738.js
new file mode 100644
--- /dev/null
+++ b/js/src/tests/js1_8/genexps/regress-683738.js
@@ -0,0 +1,103 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is JavaScript Engine testing utilities.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2007
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s): Dave Herman
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+
+//-----------------------------------------------------------------------------
+var BUGNUMBER = 683738;
+var summary = 'return with argument and lazy generator detection';
+var actual = '';
+var expect = '';
+
+//-----------------------------------------------------------------------------
+test();
+//-----------------------------------------------------------------------------
+
+function test()
+{
+  enterFunc ('test');
+  printBugNumber(BUGNUMBER);
+  printStatus (summary);
+
+  expect = "generator function foo returns a value";
+  try
+  {
+    actual = 'No Error';
+    eval("function foo(x) { if (x) { return this; } else { yield 3; } }");
+  }
+  catch(ex)
+  {
+    actual = ex.message;
+  }
+  reportCompare(expect, actual, summary + ": 1");
+
+  expect = "generator function foo returns a value";
+  try
+  {
+    actual = 'No Error';
+    eval("function foo(x) { if (x) { yield 3; } else { return this; } }");
+  }
+  catch(ex)
+  {
+    actual = ex.message;
+  }
+  reportCompare(expect, actual, summary + ": 2");
+
+  expect = "generator function foo returns a value";
+  try
+  {
+    actual = 'No Error';
+    eval("function foo(x) { if (x) { return this; } else { (yield 3); } }");
+  }
+  catch(ex)
+  {
+    actual = ex.message;
+  }
+  reportCompare(expect, actual, summary + ": 3");
+
+  expect = "generator function foo returns a value";
+  try
+  {
+    actual = 'No Error';
+    eval("function foo(x) { if (x) { (yield 3); } else { return this; } }");
+  }
+  catch(ex)
+  {
+    actual = ex.message;
+  }
+  reportCompare(expect, actual, summary + ": 4");
+
+}
--- a/js/src/vm/ArgumentsObject-inl.h
+++ b/js/src/vm/ArgumentsObject-inl.h
@@ -105,16 +105,47 @@ ArgumentsObject::elements() const
 
 inline void
 ArgumentsObject::setElement(uint32 i, const js::Value &v)
 {
     JS_ASSERT(i < initialLength());
     data()->slots[i] = v;
 }
 
+inline js::StackFrame *
+ArgumentsObject::maybeStackFrame() const
+{
+    return reinterpret_cast<js::StackFrame *>(getPrivate());
+}
+
+inline void
+ArgumentsObject::setStackFrame(StackFrame *frame)
+{
+    return setPrivate(frame);
+}
+
+#define JS_ARGUMENTS_OBJECT_ON_TRACE ((void *)0xa126)
+inline bool
+ArgumentsObject::onTrace() const
+{
+    return getPrivate() == JS_ARGUMENTS_OBJECT_ON_TRACE;
+}
+
+inline void
+ArgumentsObject::setOnTrace()
+{
+    return setPrivate(JS_ARGUMENTS_OBJECT_ON_TRACE);
+}
+
+inline void
+ArgumentsObject::clearOnTrace()
+{
+    return setPrivate(NULL);
+}
+
 inline const js::Value &
 NormalArgumentsObject::callee() const
 {
     return data()->callee;
 }
 
 inline void
 NormalArgumentsObject::clearCallee()
--- a/js/src/vm/ArgumentsObject.h
+++ b/js/src/vm/ArgumentsObject.h
@@ -42,18 +42,16 @@
 #define ArgumentsObject_h___
 
 #include "jsfun.h"
 
 #ifdef JS_POLYIC
 class GetPropCompiler;
 #endif
 
-#define JS_ARGUMENTS_OBJECT_ON_TRACE ((void *)0xa126)
-
 #ifdef JS_TRACER
 namespace nanojit {
 class ValidateWriter;
 }
 #endif
 
 namespace js {
 
@@ -224,16 +222,24 @@ class ArgumentsObject : public ::JSObjec
      */
     inline bool getElements(uint32 start, uint32 count, js::Value *vp);
 
     inline js::ArgumentsData *data() const;
 
     inline const js::Value &element(uint32 i) const;
     inline const js::Value *elements() const;
     inline void setElement(uint32 i, const js::Value &v);
+
+    /* The stack frame for this ArgumentsObject, if the frame is still active. */
+    inline js::StackFrame *maybeStackFrame() const;
+    inline void setStackFrame(js::StackFrame *frame);
+
+    inline bool onTrace() const;
+    inline void setOnTrace();
+    inline void clearOnTrace();
 };
 
 /*
  * Non-strict arguments have a private: the function's stack frame until the
  * function returns, when it is replaced with null.  When an arguments object
  * is created on-trace its private is JS_ARGUMENTS_OBJECT_ON_TRACE, and when
  * the trace exits its private is replaced with the stack frame or null, as
  * appropriate.
new file mode 100644
--- /dev/null
+++ b/js/src/vm/CallObject-inl.h
@@ -0,0 +1,161 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sw=4 et tw=78:
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is SpiderMonkey call object code.
+ *
+ * The Initial Developer of the Original Code is
+ * the Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Paul Biggar <pbiggar@mozilla.com> (original author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef CallObject_inl_h___
+#define CallObject_inl_h___
+
+#include "CallObject.h"
+
+namespace js {
+
+inline bool
+CallObject::isForEval() const
+{
+    JS_ASSERT(getFixedSlot(CALLEE_SLOT).isObjectOrNull());
+    JS_ASSERT_IF(getFixedSlot(CALLEE_SLOT).isObject(),
+                 getFixedSlot(CALLEE_SLOT).toObject().isFunction());
+    return getFixedSlot(CALLEE_SLOT).isNull();
+}
+
+inline js::StackFrame *
+CallObject::maybeStackFrame() const
+{
+    return reinterpret_cast<js::StackFrame *>(getPrivate());
+}
+
+inline void
+CallObject::setStackFrame(StackFrame *frame)
+{
+    return setPrivate(frame);
+}
+
+inline void
+CallObject::setCallee(JSObject *callee)
+{
+    JS_ASSERT_IF(callee, callee->isFunction());
+    setFixedSlot(CALLEE_SLOT, js::ObjectOrNullValue(callee));
+}
+
+inline JSObject *
+CallObject::getCallee() const
+{
+    return getFixedSlot(CALLEE_SLOT).toObjectOrNull();
+}
+
+inline JSFunction *
+CallObject::getCalleeFunction() const
+{
+    return getFixedSlot(CALLEE_SLOT).toObject().getFunctionPrivate();
+}
+
+inline const js::Value &
+CallObject::getArguments() const
+{
+    JS_ASSERT(!isForEval());
+    return getFixedSlot(ARGUMENTS_SLOT);
+}
+
+inline void
+CallObject::setArguments(const js::Value &v)
+{
+    JS_ASSERT(!isForEval());
+    setFixedSlot(ARGUMENTS_SLOT, v);
+}
+
+inline const js::Value &
+CallObject::arg(uintN i) const
+{
+    JS_ASSERT(i < getCalleeFunction()->nargs);
+    return getSlot(RESERVED_SLOTS + i);
+}
+
+inline void
+CallObject::setArg(uintN i, const js::Value &v)
+{
+    JS_ASSERT(i < getCalleeFunction()->nargs);
+    setSlot(RESERVED_SLOTS + i, v);
+}
+
+inline const js::Value &
+CallObject::var(uintN i) const
+{
+    JSFunction *fun = getCalleeFunction();
+    JS_ASSERT(fun->nargs == fun->script()->bindings.countArgs());
+    JS_ASSERT(i < fun->script()->bindings.countVars());
+    return getSlot(RESERVED_SLOTS + fun->nargs + i);
+}
+
+inline void
+CallObject::setVar(uintN i, const js::Value &v)
+{
+    JSFunction *fun = getCalleeFunction();
+    JS_ASSERT(fun->nargs == fun->script()->bindings.countArgs());
+    JS_ASSERT(i < fun->script()->bindings.countVars());
+    setSlot(RESERVED_SLOTS + fun->nargs + i, v);
+}
+
+inline void
+CallObject::copyValues(uintN nargs, Value *argv, uintN nvars, Value *slots)
+{
+    JS_ASSERT(numSlots() >= RESERVED_SLOTS + nargs + nvars);
+    copySlotRange(RESERVED_SLOTS, argv, nargs);
+    copySlotRange(RESERVED_SLOTS + nargs, slots, nvars);
+}
+
+inline js::Value *
+CallObject::argArray()
+{
+    js::DebugOnly<JSFunction*> fun = getCalleeFunction();
+    JS_ASSERT(hasContiguousSlots(RESERVED_SLOTS, fun->nargs));
+    return getSlotAddress(RESERVED_SLOTS);
+}
+
+inline js::Value *
+CallObject::varArray()
+{
+    JSFunction *fun = getCalleeFunction();
+    JS_ASSERT(hasContiguousSlots(RESERVED_SLOTS + fun->nargs,
+                                 fun->script()->bindings.countVars()));
+    return getSlotAddress(RESERVED_SLOTS + fun->nargs);
+}
+
+}
+
+#endif /* CallObject_inl_h___ */
new file mode 100644
--- /dev/null
+++ b/js/src/vm/CallObject.cpp
@@ -0,0 +1,100 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sw=4 et tw=78:
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is SpiderMonkey call object code.
+ *
+ * The Initial Developer of the Original Code is
+ * the Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Paul Biggar <pbiggar@mozilla.com> (original author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "jsobjinlines.h"
+#include "CallObject.h"
+
+#include "CallObject-inl.h"
+
+namespace js {
+
+/*
+ * Construct a call object for the given bindings.  If this is a call object
+ * for a function invocation, callee should be the function being called.
+ * Otherwise it must be a call object for eval of strict mode code, and callee
+ * must be null.
+ */
+CallObject *
+CallObject::create(JSContext *cx, JSScript *script, JSObject &scopeChain, JSObject *callee)
+{
+    Bindings &bindings = script->bindings;
+    size_t argsVars = bindings.countArgsAndVars();
+    size_t slots = RESERVED_SLOTS + argsVars;
+    gc::AllocKind kind = gc::GetGCObjectKind(slots);
+
+    /*
+     * Make sure that the arguments and variables in the call object all end up
+     * in a contiguous range of slots. We need this to be able to embed the
+     * args/vars arrays in the TypeScriptNesting for the function, after the
+     * call object's frame has finished.
+     */
+    if (cx->typeInferenceEnabled() && gc::GetGCKindSlots(kind) < slots) {
+        kind = gc::GetGCObjectKind(RESERVED_SLOTS);
+        JS_ASSERT(gc::GetGCKindSlots(kind) == RESERVED_SLOTS);
+    }
+
+    JSObject *obj = js_NewGCObject(cx, kind);
+    if (!obj)
+        return NULL;
+
+    /* Init immediately to avoid GC seeing a half-init'ed object. */
+    obj->initCall(cx, bindings, &scopeChain);
+    obj->makeVarObj();
+
+    /* This must come after callobj->lastProp has been set. */
+    if (!obj->ensureInstanceReservedSlots(cx, argsVars))
+        return NULL;
+
+#ifdef DEBUG
+    for (Shape::Range r = obj->lastProp; !r.empty(); r.popFront()) {
+        const Shape &s = r.front();
+        if (s.slot != SHAPE_INVALID_SLOT) {
+            JS_ASSERT(s.slot + 1 == obj->slotSpan());
+            break;
+        }
+    }
+#endif
+
+    CallObject &callobj = obj->asCall();
+    callobj.setCallee(callee);
+    return &callobj;
+}
+
+}
new file mode 100644
--- /dev/null
+++ b/js/src/vm/CallObject.h
@@ -0,0 +1,116 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sw=4 et tw=78:
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is SpiderMonkey call object code.
+ *
+ * The Initial Developer of the Original Code is
+ * the Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Paul Biggar <pbiggar@mozilla.com> (original author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef CallObject_h___
+#define CallObject_h___
+
+namespace js {
+
+class CallObject : public ::JSObject
+{
+    /*
+     * Reserved slot structure for Call objects:
+     *
+     * private               - the stack frame corresponding to the Call object
+     *                         until js_PutCallObject or its on-trace analog
+     *                         is called, null thereafter
+     * JSSLOT_CALL_CALLEE    - callee function for the stack frame, or null if
+     *                         the stack frame is for strict mode eval code
+     * JSSLOT_CALL_ARGUMENTS - arguments object for non-strict mode eval stack
+     *                         frames (not valid for strict mode eval frames)
+     */
+    static const uint32 CALLEE_SLOT = 0;
+    static const uint32 ARGUMENTS_SLOT = 1;
+
+public:
+    /* Create a CallObject for the given callee function. */
+    static CallObject *
+    create(JSContext *cx, JSScript *script, JSObject &scopeChain, JSObject *callee);
+
+    static const uint32 RESERVED_SLOTS = 2;
+
+    /* True if this is for a strict mode eval frame or for a function call. */
+    inline bool isForEval() const;
+
+    /* The stack frame for this CallObject, if the frame is still active. */
+    inline js::StackFrame *maybeStackFrame() const;
+    inline void setStackFrame(js::StackFrame *frame);
+
+    /*
+     * The callee function if this CallObject was created for a function
+     * invocation, or null if it was created for a strict mode eval frame.
+     */
+    inline JSObject *getCallee() const;
+    inline JSFunction *getCalleeFunction() const; 
+    inline void setCallee(JSObject *callee);
+
+    /* Returns the callee's arguments object. */
+    inline const js::Value &getArguments() const;
+    inline void setArguments(const js::Value &v);
+
+    /* Returns the formal argument at the given index. */
+    inline const js::Value &arg(uintN i) const;
+    inline void setArg(uintN i, const js::Value &v);
+
+    /* Returns the variable at the given index. */
+    inline const js::Value &var(uintN i) const;
+    inline void setVar(uintN i, const js::Value &v);
+
+    /*
+     * Get the actual arrays of arguments and variables. Only call if type
+     * inference is enabled, where we ensure that call object variables are in
+     * contiguous slots (see NewCallObject).
+     */
+    inline js::Value *argArray();
+    inline js::Value *varArray();
+
+    inline void copyValues(uintN nargs, Value *argv, uintN nvars, Value *slots);
+};
+
+}
+
+js::CallObject &
+JSObject::asCall()
+{
+    JS_ASSERT(isCall());
+    return *reinterpret_cast<js::CallObject *>(this);
+}
+
+#endif /* CallObject_h___ */
--- a/js/src/vm/Stack-inl.h
+++ b/js/src/vm/Stack-inl.h
@@ -43,16 +43,18 @@
 
 #include "jscntxt.h"
 #include "jscompartment.h"
 
 #include "Stack.h"
 
 #include "jsscriptinlines.h"
 #include "ArgumentsObject-inl.h"
+#include "CallObject-inl.h"
+
 #include "methodjit/MethodJIT.h"
 
 namespace js {
 
 inline void
 StackFrame::initPrev(JSContext *cx)
 {
     JS_ASSERT(flags_ & HAS_PREVPC);
@@ -335,35 +337,35 @@ StackFrame::setScopeChainNoCallObj(JSObj
         }
     }
 #endif
     scopeChain_ = &obj;
     flags_ |= HAS_SCOPECHAIN;
 }
 
 inline void
-StackFrame::setScopeChainWithOwnCallObj(JSObject &obj)
+StackFrame::setScopeChainWithOwnCallObj(CallObject &obj)
 {
     JS_ASSERT(&obj != NULL);
-    JS_ASSERT(!hasCallObj() && obj.isCall() && obj.getPrivate() == this);
+    JS_ASSERT(!hasCallObj() && obj.maybeStackFrame() == this);
     scopeChain_ = &obj;
     flags_ |= HAS_SCOPECHAIN | HAS_CALL_OBJ;
 }
 
-inline JSObject &
+inline CallObject &
 StackFrame::callObj() const
 {
     JS_ASSERT_IF(isNonEvalFunctionFrame() || isStrictEvalFrame(), hasCallObj());
 
     JSObject *pobj = &scopeChain();
     while (JS_UNLIKELY(!pobj->isCall())) {
         JS_ASSERT(IsCacheableNonGlobalScope(pobj) || pobj->isWith());
         pobj = pobj->getParent();
     }
-    return *pobj;
+    return pobj->asCall();
 }
 
 inline bool
 StackFrame::maintainNestingState() const
 {
     /*
      * Whether to invoke the nesting epilogue/prologue to maintain active
      * frame counts and check for reentrant outer functions.
@@ -410,21 +412,21 @@ StackFrame::functionEpilogue(bool object
     if (!objectsOnly && maintainNestingState())
         types::NestingEpilogue(this);
 }
 
 inline void
 StackFrame::markFunctionEpilogueDone(bool activationOnly)
 {
     if (flags_ & (HAS_ARGS_OBJ | HAS_CALL_OBJ)) {
-        if (hasArgsObj() && !argsObj().getPrivate()) {
+        if (hasArgsObj() && !argsObj().maybeStackFrame()) {
             args.nactual = args.obj->initialLength();
             flags_ &= ~HAS_ARGS_OBJ;
         }
-        if (hasCallObj() && !callObj().getPrivate()) {
+        if (hasCallObj() && !callObj().maybeStackFrame()) {
             /*
              * For function frames, the call object may or may not have have an
              * enclosing DeclEnv object, so we use the callee's parent, since
              * it was the initial scope chain. For global (strict) eval frames,
              * there is no calle, but the call object's parent is the initial
              * scope chain.
              */
             scopeChain_ = isFunctionFrame()
@@ -682,57 +684,57 @@ ArgumentsObject::getElement(uint32 i, Va
      */
     if (vp->isMagic(JS_ARGS_HOLE))
         return false;
 
     /*
      * If this arguments object was created on trace the actual argument value
      * could be in a register or something, so we can't optimize.
      */
-    StackFrame *fp = reinterpret_cast<StackFrame *>(getPrivate());
-    if (fp == JS_ARGUMENTS_OBJECT_ON_TRACE)
+    if (onTrace())
         return false;
 
     /*
      * If this arguments object has an associated stack frame, that contains
      * the canonical argument value.  Note that strict arguments objects do not
      * alias named arguments and never have a stack frame.
      */
+    StackFrame *fp = maybeStackFrame();
     JS_ASSERT_IF(isStrictArguments(), !fp);
     if (fp)
         *vp = fp->canonicalActualArg(i);
     return true;
 }
 
 inline bool
 ArgumentsObject::getElements(uint32 start, uint32 count, Value *vp)
 {
     JS_ASSERT(start + count >= start);
 
     uint32 length = initialLength();
     if (start > length || start + count > length)
         return false;
 
-    StackFrame *fp = reinterpret_cast<StackFrame *>(getPrivate());
+    StackFrame *fp = maybeStackFrame();
 
     /* If there's no stack frame for this, argument values are in elements(). */
     if (!fp) {
         const Value *srcbeg = elements() + start;
         const Value *srcend = srcbeg + count;
         const Value *src = srcbeg;
         for (Value *dst = vp; src < srcend; ++dst, ++src) {
             if (src->isMagic(JS_ARGS_HOLE))
                 return false;
             *dst = *src;
         }
         return true;
     }
 
     /* If we're on trace, there's no canonical location for elements: fail. */
-    if (fp == JS_ARGUMENTS_OBJECT_ON_TRACE)
+    if (onTrace())
         return false;
 
     /* Otherwise, element values are on the stack. */
     JS_ASSERT(fp->numActualArgs() <= StackSpace::ARGS_LENGTH_MAX);
     return fp->forEachCanonicalActualArg(detail::CopyNonHoleArgsTo(this, vp), start, count);
 }
 
 } /* namespace js */
--- a/js/src/vm/Stack.cpp
+++ b/js/src/vm/Stack.cpp
@@ -154,19 +154,19 @@ StackFrame::stealFrameAndSlots(Value *vp
             JSObject *env = obj.getParent();
             JS_ASSERT(env->isDeclEnv());
             env->setPrivate(this);
         }
     }
     if (hasArgsObj()) {
         ArgumentsObject &argsobj = argsObj();
         if (argsobj.isNormalArguments())
-            argsobj.setPrivate(this);
+            argsobj.setStackFrame(this);
         else
-            JS_ASSERT(!argsobj.getPrivate());
+            JS_ASSERT(!argsobj.maybeStackFrame());
         otherfp->flags_ &= ~HAS_ARGS_OBJ;
     }
 }
 
 #ifdef DEBUG
 JSObject *const StackFrame::sInvalidScopeChain = (JSObject *)0xbeef;
 #endif
 
--- a/js/src/vm/Stack.h
+++ b/js/src/vm/Stack.h
@@ -846,19 +846,19 @@ class StackFrame
     }
 
     bool hasCallObj() const {
         bool ret = !!(flags_ & HAS_CALL_OBJ);
         JS_ASSERT_IF(ret, !isNonStrictEvalFrame());
         return ret;
     }
 
-    inline JSObject &callObj() const;
+    inline CallObject &callObj() const;
     inline void setScopeChainNoCallObj(JSObject &obj);
-    inline void setScopeChainWithOwnCallObj(JSObject &obj);
+    inline void setScopeChainWithOwnCallObj(CallObject &obj);
 
     /*
      * Prologue for function frames: make a call object for heavyweight
      * functions, and maintain type nesting invariants.
      */
     inline bool functionPrologue(JSContext *cx);
 
     /*
--- a/js/src/vm/String.cpp
+++ b/js/src/vm/String.cpp
@@ -78,50 +78,55 @@ void
 JSLinearString::mark(JSTracer *)
 {
     JSLinearString *str = this;
     while (!str->isStaticAtom() && str->markIfUnmarked() && str->isDependent())
         str = str->asDependent().base();
 }
 
 size_t
-JSString::charsHeapSize()
+JSString::charsHeapSize(JSUsableSizeFun usf)
 {
     /* JSRope: do nothing, we'll count all children chars when we hit the leaf strings. */
     if (isRope())
         return 0;
 
     JS_ASSERT(isLinear());
 
     /* JSDependentString: do nothing, we'll count the chars when we hit the base string. */
     if (isDependent())
         return 0;
 
     JS_ASSERT(isFlat());
 
     /* JSExtensibleString: count the full capacity, not just the used space. */
-    if (isExtensible())
-        return asExtensible().capacity() * sizeof(jschar);
+    if (isExtensible()) {
+        JSExtensibleString &extensible = asExtensible();
+        size_t usable = usf((void *)extensible.chars());
+        return usable ? usable : asExtensible().capacity() * sizeof(jschar);
+    }
 
     JS_ASSERT(isFixed());
 
     /* JSExternalString: don't count, the chars could be stored anywhere. */
     if (isExternal())
         return 0;
 
     /* JSInlineString, JSShortString, JSInlineAtom, JSShortAtom: the chars are inline. */
     if (isInline())
         return 0;
 
     /* JSStaticAtom: the chars are static and so not part of the heap. */
     if (isStaticAtom())
         return 0;
 
     /* JSAtom, JSFixedString: count the chars. */
-    return length() * sizeof(jschar);
+    JSFixedString &fixed = asFixed();
+    size_t usable = usf((void *)fixed.chars());
+    return usable ? usable : length() * sizeof(jschar);
 }
 
 static JS_ALWAYS_INLINE bool
 AllocChars(JSContext *maybecx, size_t length, jschar **chars, size_t *capacity)
 {
     /*
      * String length doesn't include the null char, so include it here before
      * doubling. Adding the null char after doubling would interact poorly with
--- a/js/src/vm/String.h
+++ b/js/src/vm/String.h
@@ -406,17 +406,17 @@ class JSString : public js::gc::Cell
     }
 
     /* Only called by the GC for strings with the FINALIZE_STRING kind. */
 
     inline void finalize(JSContext *cx);
 
     /* Gets the number of bytes that the chars take on the heap. */
 
-    JS_FRIEND_API(size_t) charsHeapSize();
+    JS_FRIEND_API(size_t) charsHeapSize(JSUsableSizeFun usf);
 
     /* Offsets for direct field from jit code. */
 
     static size_t offsetOfLengthAndFlags() {
         return offsetof(JSString, d.lengthAndFlags);
     }
 
     static size_t offsetOfChars() {
--- a/js/src/xpconnect/idl/nsIXPCScriptable.idl
+++ b/js/src/xpconnect/idl/nsIXPCScriptable.idl
@@ -103,16 +103,17 @@ interface nsIXPCScriptable : nsISupports
     const PRUint32 DONT_ASK_INSTANCE_FOR_SCRIPTABLE = 1 << 22;
     const PRUint32 CLASSINFO_INTERFACES_ONLY        = 1 << 23;
     const PRUint32 ALLOW_PROP_MODS_DURING_RESOLVE   = 1 << 24;
     const PRUint32 ALLOW_PROP_MODS_TO_PROTOTYPE     = 1 << 25;
     const PRUint32 DONT_SHARE_PROTOTYPE             = 1 << 26;
     const PRUint32 DONT_REFLECT_INTERFACE_NAMES     = 1 << 27;
     const PRUint32 WANT_EQUALITY                    = 1 << 28;
     const PRUint32 WANT_OUTER_OBJECT                = 1 << 29;
+    const PRUint32 USE_STUB_EQUALITY_HOOK           = 1 << 30;
 
     // The high order bit is RESERVED for consumers of these flags. 
     // No implementor of this interface should ever return flags 
     // with this bit set.
     const PRUint32 RESERVED                         = 1 << 31;
 
     readonly attribute string   className;
     readonly attribute PRUint32 scriptableFlags;
--- a/js/src/xpconnect/idl/nsIXPConnect.idl
+++ b/js/src/xpconnect/idl/nsIXPConnect.idl
@@ -387,17 +387,17 @@ interface nsIXPCFunctionThisTranslator :
 %{ C++
 // For use with the service manager
 // {CB6593E0-F9B2-11d2-BDD6-000064657374}
 #define NS_XPCONNECT_CID \
 { 0xcb6593e0, 0xf9b2, 0x11d2, \
     { 0xbd, 0xd6, 0x0, 0x0, 0x64, 0x65, 0x73, 0x74 } }
 %}
 
-[uuid(f99ffb06-4e7b-4bab-83d4-7d573235a08a)]
+[uuid(a995b541-d514-43f1-ac0e-f49746c0b063)]
 interface nsIXPConnect : nsISupports
 {
 %{ C++
   NS_DEFINE_STATIC_CID_ACCESSOR(NS_XPCONNECT_CID)
 %}
 
     /**
      * Initializes classes on a global object that has already been created.
@@ -720,23 +720,16 @@ interface nsIXPConnect : nsISupports
      * Note aJSContext as a child to the cycle collector.
      * @param aJSContext The JSContext to note.
      * @param aCb The cycle collection traversal callback.
      */
     [noscript,notxpcom] void noteJSContext(in JSContextPtr aJSContext,
                                            in nsCCTraversalCallbackRef aCb);
 
     /**
-     * Get the JSEqualityOp pointer to use for identifying JSObjects that hold
-     * a pointer to a nsIXPConnectWrappedNative or to the native in their
-     * private date. See IS_WRAPPER_CLASS in xpcprivate.h for details.
-     */
-    void GetXPCWrappedNativeJSClassInfo(out JSEqualityOp equality);
-
-    /**
      * Whether or not XPConnect should report all JS exceptions when returning
      * from JS into C++. False by default, although any value set in the
      * MOZ_REPORT_ALL_JS_EXCEPTIONS environment variable will override the value
      * passed here.
      */
     void setReportAllJSExceptions(in boolean reportAllJSExceptions);
 
     /**
--- a/js/src/xpconnect/src/nsXPConnect.cpp
+++ b/js/src/xpconnect/src/nsXPConnect.cpp
@@ -2103,28 +2103,16 @@ nsXPConnect::EvalInSandboxObject(const n
     nsresult rv = sandbox->GetJSObject(&obj);
     NS_ENSURE_SUCCESS(rv, rv);
 
     return xpc_EvalInSandbox(cx, obj, source,
                              NS_ConvertUTF16toUTF8(source).get(), 1,
                              JSVERSION_DEFAULT, returnStringOnly, rval);
 }
 
-/* void GetXPCWrappedNativeJSClassInfo(out JSEqualityOp equality); */
-NS_IMETHODIMP
-nsXPConnect::GetXPCWrappedNativeJSClassInfo(JSEqualityOp *equality)
-{
-    // Expose the equality pointer used by IS_WRAPPER_CLASS(). If that macro
-    // ever changes, this function needs to stay in sync.
-
-    *equality = &XPC_WN_Equality;
-
-    return NS_OK;
-}
-
 /* nsIXPConnectJSObjectHolder getWrappedNativePrototype (in JSContextPtr aJSContext, in JSObjectPtr aScope, in nsIClassInfo aClassInfo); */
 NS_IMETHODIMP 
 nsXPConnect::GetWrappedNativePrototype(JSContext * aJSContext, 
                                        JSObject * aScope, 
                                        nsIClassInfo *aClassInfo, 
                                        nsIXPConnectJSObjectHolder **_retval)
 {
     XPCCallContext ccx(NATIVE_CALLER, aJSContext);
--- a/js/src/xpconnect/src/xpcjsruntime.cpp
+++ b/js/src/xpconnect/src/xpcjsruntime.cpp
@@ -1263,33 +1263,33 @@ GetCompartmentTjitCodeSize(JSCompartment
     }
     return 0;
 }
 
 PRInt64
 GetCompartmentTjitDataAllocatorsMainSize(JSCompartment *c)
 {
     return c->hasTraceMonitor()
-         ? c->traceMonitor()->getVMAllocatorsMainSize()
+         ? c->traceMonitor()->getVMAllocatorsMainSize(moz_malloc_usable_size)
          : 0;
 }
 
 PRInt64
 GetCompartmentTjitDataAllocatorsReserveSize(JSCompartment *c)
 {
     return c->hasTraceMonitor()
-         ? c->traceMonitor()->getVMAllocatorsReserveSize()
+         ? c->traceMonitor()->getVMAllocatorsReserveSize(moz_malloc_usable_size)
          : 0;
 }
 
 PRInt64
 GetCompartmentTjitDataTraceMonitorSize(JSCompartment *c)
 {
     return c->hasTraceMonitor()
-         ? c->traceMonitor()->getTraceMonitorSize()
+         ? c->traceMonitor()->getTraceMonitorSize(moz_malloc_usable_size)
          : 0;
 }
 
 #endif  // JS_TRACER
 
 void
 CompartmentCallback(JSContext *cx, void *vdata, JSCompartment *compartment)
 {
@@ -1347,37 +1347,30 @@ CellCallback(JSContext *cx, void *vdata,
         {
             JSObject *obj = static_cast<JSObject *>(thing);
             curr->objectSlots += obj->sizeOfSlotsArray(moz_malloc_usable_size);
             break;
         }
         case JSTRACE_STRING:
         {
             JSString *str = static_cast<JSString *>(thing);
-            curr->stringChars += str->charsHeapSize();
+            curr->stringChars += str->charsHeapSize(moz_malloc_usable_size);
             break;
         }
         case JSTRACE_SHAPE:
         {
             js::Shape *shape = static_cast<js::Shape *>(thing);
-            if(shape->hasTable())
-                curr->propertyTables +=
-                    shape->getTable()->sizeOf(moz_malloc_usable_size);
+            curr->propertyTables += shape->sizeOfPropertyTable(moz_malloc_usable_size);
+            curr->shapeKids += shape->sizeOfKids(moz_malloc_usable_size);
             break;
         }
         case JSTRACE_SCRIPT:
         {
             JSScript *script = static_cast<JSScript *>(thing);
-#if JS_SCRIPT_INLINE_DATA_LIMIT
-            if (script->data != script->inlineData)
-#endif
-            {
-                size_t usable = moz_malloc_usable_size(script->data);
-                curr->scriptData += usable ? usable : script->dataSize();
-            }
+            curr->scriptData += script->dataSize(moz_malloc_usable_size);
 #ifdef JS_METHODJIT
             curr->mjitData += script->jitDataSize(moz_malloc_usable_size);
 #endif
             break;
         }
         case JSTRACE_TYPE_OBJECT:
         {
             js::types::TypeObject *obj = static_cast<js::types::TypeObject *>(thing);
@@ -1597,18 +1590,22 @@ CollectCompartmentStatsForRuntime(JSRunt
 
         js::IterateCompartmentsArenasCells(cx, data, CompartmentCallback,
                                            ArenaCallback, CellCallback);
 
         for(js::ThreadDataIter i(rt); !i.empty(); i.popFront())
             data->stackSize += i.threadData()->stackSpace.committedSize();
 
         size_t usable = moz_malloc_usable_size(rt);
-        data->runtimeObjectSize += usable ? usable : sizeof(JSRuntime);
-        data->atomsTableSize += rt->atomState.atoms.tableSize();
+        data->runtimeObjectSize = usable ? usable : sizeof(JSRuntime);
+
+        // Nb: |countMe| is false because atomState.atoms is within JSRuntime,
+        // and so counted when JSRuntime is counted.
+        data->atomsTableSize =
+            rt->atomState.atoms.sizeOf(moz_malloc_usable_size, /* countMe */false);
     }
 
     JS_DestroyContextNoGC(cx);
 
     // This is initialized to all bytes stored in used chunks, and then we
     // subtract used space from it each time around the loop.
     data->gcHeapChunkDirtyUnused = data->gcHeapChunkTotal -
                                    data->gcHeapChunkCleanUnused;
@@ -1758,16 +1755,24 @@ ReportCompartmentStats(const Compartment
     ReportMemoryBytes0(MakeMemoryReporterPath(pathPrefix, stats.name,
                                               "object-empty-shapes"),
                        nsIMemoryReporter::KIND_HEAP,
                        stats.typeInferenceMemory.emptyShapes,
     "Arrays attached to prototype JS objects managing shape information.",
                        callback, closure);
 
     ReportMemoryBytes0(MakeMemoryReporterPath(pathPrefix, stats.name,
+                                              "shape-kids"),
+                       nsIMemoryReporter::KIND_HEAP, stats.shapeKids,
+    "Memory allocated for the compartment's shape kids.  A shape kid "
+    "is an internal data structure that makes JavaScript property accesses "
+    "fast.",
+                       callback, closure);
+
+    ReportMemoryBytes0(MakeMemoryReporterPath(pathPrefix, stats.name,
                                               "script-data"),
                        nsIMemoryReporter::KIND_HEAP, stats.scriptData,
     "Memory allocated for JSScript bytecode and various variable-length "
     "tables." SLOP_BYTES_STRING,
                        callback, closure);
 
 #ifdef JS_METHODJIT
     ReportMemoryBytes0(MakeMemoryReporterPath(pathPrefix, stats.name,
--- a/js/src/xpconnect/src/xpcprivate.h
+++ b/js/src/xpconnect/src/xpcprivate.h
@@ -1384,38 +1384,52 @@ XPC_WN_JSOp_Clear(JSContext *cx, JSObjec
 
 extern JSObject*
 XPC_WN_JSOp_ThisObject(JSContext *cx, JSObject *obj);
 
 // Macros to initialize Object or Function like XPC_WN classes
 #define XPC_WN_WithCall_ObjectOps                                             \
     {                                                                         \
         nsnull, /* lookupProperty */                                          \
+        nsnull, /* lookupElement */                                           \
         nsnull, /* defineProperty */                                          \
+        nsnull, /* defineElement */                                           \
         nsnull, /* getProperty    */                                          \
+        nsnull, /* getElement    */                                           \
         nsnull, /* setProperty    */                                          \
+        nsnull, /* setElement    */                                           \
         nsnull, /* getAttributes  */                                          \
+        nsnull, /* getElementAttributes  */                                   \
         nsnull, /* setAttributes  */                                          \
+        nsnull, /* setElementAttributes  */                                   \
         nsnull, /* deleteProperty */                                          \
+        nsnull, /* deleteElement */                                           \
         js::Valueify(XPC_WN_JSOp_Enumerate),                                  \
         XPC_WN_JSOp_TypeOf_Function,                                          \
         nsnull, /* fix            */                                          \
         XPC_WN_JSOp_ThisObject,                                               \
         XPC_WN_JSOp_Clear                                                     \
     }
 
 #define XPC_WN_NoCall_ObjectOps                                               \
     {                                                                         \
         nsnull, /* lookupProperty */                                          \
+        nsnull, /* lookupElement */                                           \
         nsnull, /* defineProperty */                                          \
+        nsnull, /* defineElement */                                           \
         nsnull, /* getProperty    */                                          \
+        nsnull, /* getElement    */                                           \
         nsnull, /* setProperty    */                                          \
+        nsnull, /* setElement    */                                           \
         nsnull, /* getAttributes  */                                          \
+        nsnull, /* getElementAttributes  */                                   \
         nsnull, /* setAttributes  */                                          \
+        nsnull, /* setElementAttributes  */                                   \
         nsnull, /* deleteProperty */                                          \
+        nsnull, /* deleteElement */                                           \
         js::Valueify(XPC_WN_JSOp_Enumerate),                                  \
         XPC_WN_JSOp_TypeOf_Object,                                            \
         nsnull, /* fix            */                                          \
         XPC_WN_JSOp_ThisObject,                                               \
         XPC_WN_JSOp_Clear                                                     \
     }
 
 // Maybe this macro should check for class->enumerate ==
@@ -1947,16 +1961,17 @@ public:
     JSBool DontEnumStaticProps()          GET_IT(DONT_ENUM_STATIC_PROPS)
     JSBool DontEnumQueryInterface()       GET_IT(DONT_ENUM_QUERY_INTERFACE)
     JSBool DontAskInstanceForScriptable() GET_IT(DONT_ASK_INSTANCE_FOR_SCRIPTABLE)
     JSBool ClassInfoInterfacesOnly()      GET_IT(CLASSINFO_INTERFACES_ONLY)
     JSBool AllowPropModsDuringResolve()   GET_IT(ALLOW_PROP_MODS_DURING_RESOLVE)
     JSBool AllowPropModsToPrototype()     GET_IT(ALLOW_PROP_MODS_TO_PROTOTYPE)
     JSBool DontSharePrototype()           GET_IT(DONT_SHARE_PROTOTYPE)
     JSBool DontReflectInterfaceNames()    GET_IT(DONT_REFLECT_INTERFACE_NAMES)
+    JSBool UseStubEqualityHook()          GET_IT(USE_STUB_EQUALITY_HOOK)
 
 #undef GET_IT
 };
 
 /***************************************************************************/
 
 // XPCNativeScriptableShared is used to hold the JSClass and the
 // associated scriptable flags for XPCWrappedNatives. These are shared across
--- a/js/src/xpconnect/src/xpcpublic.h
+++ b/js/src/xpconnect/src/xpcpublic.h
@@ -70,21 +70,18 @@ xpc_CreateMTGlobalObject(JSContext *cx, 
 
 // XXX where should this live?
 NS_EXPORT_(void)
 xpc_LocalizeContext(JSContext *cx);
 
 nsresult
 xpc_MorphSlimWrapper(JSContext *cx, nsISupports *tomorph);
 
-extern JSBool
-XPC_WN_Equality(JSContext *cx, JSObject *obj, const jsval *v, JSBool *bp);
-
 #define IS_WRAPPER_CLASS(clazz)                                               \
-    (clazz->ext.equality == js::Valueify(XPC_WN_Equality))
+    ((clazz)->ext.isWrappedNative)
 
 inline JSBool
 DebugCheckWrapperClass(JSObject* obj)
 {
     NS_ASSERTION(IS_WRAPPER_CLASS(obj->getClass()),
                  "Forgot to check if this is a wrapper?");
     return JS_TRUE;
 }
@@ -202,16 +199,17 @@ struct CompartmentStats
     PRInt64 gcHeapArenaPadding;
     PRInt64 gcHeapArenaUnused;
 
     PRInt64 gcHeapKinds[JSTRACE_LAST + 1];
 
     PRInt64 objectSlots;
     PRInt64 stringChars;
     PRInt64 propertyTables;
+    PRInt64 shapeKids;
     PRInt64 scriptData;
 
 #ifdef JS_METHODJIT
     PRInt64 mjitCodeMethod;
     PRInt64 mjitCodeRegexp;
     PRInt64 mjitCodeUnused;
     PRInt64 mjitData;
 #endif
--- a/js/src/xpconnect/src/xpcwrappednativejsops.cpp
+++ b/js/src/xpconnect/src/xpcwrappednativejsops.cpp
@@ -902,22 +902,29 @@ js::Class XPC_WN_NoHelper_JSClass = {
         nsnull, // iteratorObject
         nsnull, // unused
         true,   // isWrappedNative
     },
    
     // ObjectOps
     {
         nsnull, // lookupProperty
+        nsnull, // lookupElement
         nsnull, // defineProperty
+        nsnull, // defineElement
         nsnull, // getProperty
+        nsnull, // getElement
         nsnull, // setProperty
+        nsnull, // setElement
         nsnull, // getAttributes
+        nsnull, // getElementAttributes
         nsnull, // setAttributes
+        nsnull, // setElementAttributes
         nsnull, // deleteProperty
+        nsnull, // deleteElement
         JS_VALUEIFY(js::NewEnumerateOp, XPC_WN_JSOp_Enumerate),
         XPC_WN_JSOp_TypeOf_Object,
         nsnull, // fix
         XPC_WN_JSOp_ThisObject,
         XPC_WN_JSOp_Clear
     }
 };
 
@@ -1536,18 +1543,27 @@ XPCNativeScriptableShared::PopulateJSCla
         if(mFlags.WantConstruct())
             mJSClass.base.construct = js::Valueify(XPC_WN_Helper_Construct);
     }
     else
     {
         ops->typeOf = XPC_WN_JSOp_TypeOf_Object;
     }
 
-    // Equality is a required hook.
-    mJSClass.base.ext.equality = js::Valueify(XPC_WN_Equality);
+    if(mFlags.UseStubEqualityHook())
+    {
+        NS_ASSERTION(!mFlags.WantEquality(),
+                     "If you want an Equality callback, you can't use a stub "
+                     "equality hook");
+        mJSClass.base.ext.equality = nsnull;
+    }
+    else
+    {
+        mJSClass.base.ext.equality = js::Valueify(XPC_WN_Equality);
+    }
 
     if(mFlags.WantHasInstance())
         mJSClass.base.hasInstance = js::Valueify(XPC_WN_Helper_HasInstance);
 
     if(mFlags.WantTrace())
         mJSClass.base.trace = XPC_WN_Helper_Trace;
     else
         mJSClass.base.trace = XPC_WN_Shared_Trace;
--- a/layout/base/crashtests/crashtests.list
+++ b/layout/base/crashtests/crashtests.list
@@ -253,21 +253,19 @@ load 468645-1.xhtml
 load 468645-2.xhtml
 load 468645-3.xhtml
 load 469861-1.xhtml
 load 469861-2.xhtml
 load 471594-1.xhtml
 load 479114-1.html
 load 477333-1.xhtml
 asserts-if(Android,6) load 477731-1.html
-# 479360-1.xhtml will assert 6 times due to bug 439258 and then make the test
-# after the test after it also assert 6 times.
-asserts-if(!Android,6) load 479360-1.xhtml # Bug 439258
+load 479360-1.xhtml
 asserts-if(Android,6) load 480686-1.html
-asserts-if(!Android,6) load 481806-1.html  # Bug 439258
+load 481806-1.html
 load 483604-1.xhtml
 load 485501-1.html
 load 487544-1.html
 load 488390-1.xhtml
 load 489691.html
 load 490376-1.xhtml
 load 490559-1.html
 load 490747.html
--- a/layout/base/nsDisplayList.cpp
+++ b/layout/base/nsDisplayList.cpp
@@ -2577,17 +2577,17 @@ PRBool nsDisplayTransform::ComputeVisibi
                        mFrame, 
                        aBuilder->ToReferenceFrame(mFrame), 
                        &untransformedVisibleRect)) 
   {
     untransformedVisibleRect = mFrame->GetVisualOverflowRectRelativeToSelf() +  
                                aBuilder->ToReferenceFrame(mFrame);
   }
   nsRegion untransformedVisible = untransformedVisibleRect;
-  // Call RecomputeVisiblity instead of ComputeVisibilty since
+  // Call RecomputeVisiblity instead of ComputeVisibility since
   // nsDisplayItem::ComputeVisibility should only be called from
   // nsDisplayList::ComputeVisibility (which sets mVisibleRect on the item)
   mStoredList.RecomputeVisibility(aBuilder, &untransformedVisible);
   return PR_TRUE;
 }
 
 #ifdef DEBUG_HIT
 #include <time.h>
@@ -2619,16 +2619,17 @@ void nsDisplayTransform::HitTest(nsDispl
   /* We want to go from transformed-space to regular space.
    * Thus we have to invert the matrix, which normally does
    * the reverse operation (e.g. regular->transformed)
    */
 
   /* Now, apply the transform and pass it down the channel. */
   nsRect resultingRect;
   if (aRect.width == 1 && aRect.height == 1) {
+    // Magic width/height indicating we're hit testing a point, not a rect
     gfxPoint point = matrix.Inverse().ProjectPoint(
                        gfxPoint(NSAppUnitsToFloatPixels(aRect.x, factor),
                                 NSAppUnitsToFloatPixels(aRect.y, factor)));
 
     resultingRect = nsRect(NSFloatPixelsToAppUnits(float(point.x), factor),
                            NSFloatPixelsToAppUnits(float(point.y), factor),
                            1, 1);
 
--- a/layout/base/nsDisplayList.h
+++ b/layout/base/nsDisplayList.h
@@ -617,17 +617,20 @@ public:
    * display items to be individually wrapped --- currently nsDisplayClip
    * and nsDisplayClipRoundedRect only.
    */
   virtual PRUint32 GetPerFrameKey() { return PRUint32(GetType()); }
   /**
    * This is called after we've constructed a display list for event handling.
    * When this is called, we've already ensured that aRect intersects the
    * item's bounds.
-   * 
+   *
+   * @param aRect the point or rect being tested, relative to the reference
+   * frame. If the width and height are both 1 app unit, it indicates we're
+   * hit testing a point, not a rect.
    * @param aState must point to a HitTestState. If you don't have one,
    * just create one with the default constructor and pass it in.
    * @param aOutFrames each item appends the frame(s) in this display item that
    * the rect is considered over (if any) to aOutFrames.
    */
   virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
                        HitTestState* aState, nsTArray<nsIFrame*> *aOutFrames) {}
   /**
@@ -2059,17 +2062,17 @@ private:
 
 /* A display item that applies a transformation to all of its descendant
  * elements.  This wrapper should only be used if there is a transform applied
  * to the root element.
  *
  * The reason that a "bounds" rect is involved in transform calculations is
  * because CSS-transforms allow percentage values for the x and y components
  * of <translation-value>s, where percentages are percentages of the element's
- * content box.
+ * border box.
  *
  * INVARIANT: The wrapped frame is transformed.
  * INVARIANT: The wrapped frame is non-null.
  */ 
 class nsDisplayTransform: public nsDisplayItem
 {
 public:
   /* Constructor accepts a display list, empties it, and wraps it up.  It also
@@ -2120,17 +2123,17 @@ public:
   virtual already_AddRefed<Layer> BuildLayer(nsDisplayListBuilder* aBuilder,
                                              LayerManager* aManager,
                                              const ContainerParameters& aContainerParameters);
   virtual PRBool ComputeVisibility(nsDisplayListBuilder *aBuilder,
                                    nsRegion *aVisibleRegion,
                                    const nsRect& aAllowVisibleRegionExpansion);
   virtual PRBool TryMerge(nsDisplayListBuilder *aBuilder, nsDisplayItem *aItem);
 
-  const gfx3DMatrix& GetTransform(float aFactor);
+  const gfx3DMatrix& GetTransform(float aFactor);
 
   /**
    * TransformRect takes in as parameters a rectangle (in aFrame's coordinate
    * space) and returns the smallest rectangle (in aFrame's coordinate space)
    * containing the transformed image of that rectangle.  That is, it takes
    * the four corners of the rectangle, transforms them according to the
    * matrix associated with the specified frame, then returns the smallest
    * rectangle containing the four transformed points.
--- a/layout/base/nsPresShell.cpp
+++ b/layout/base/nsPresShell.cpp
@@ -1429,16 +1429,19 @@ public:
     NS_DECL_NSIMEMORYMULTIREPORTER
   protected:
     static PLDHashOperator SizeEnumerator(PresShellPtrKey *aEntry, void *userArg);
   };
 
 protected:
   void QueryIsActive();
   nsresult UpdateImageLockingState();
+
+private:
+  nscolor GetDefaultBackgroundColorToDraw();
 };
 
 NS_IMPL_ISUPPORTS1(PresShell::MemoryReporter, nsIMemoryMultiReporter)
 
 namespace {
 
 struct MemoryReporterData
 {
@@ -4039,23 +4042,29 @@ PresShell::ScrollToAnchor()
  * Helper (per-continuation) for ScrollContentIntoView.
  *
  * @param aContainerFrame [in] the frame which aRect is relative to
  * @param aFrame [in] Frame whose bounds should be unioned
  * @param aUseWholeLineHeightForInlines [in] if true, then for inline frames
  * we should include the top of the line in the added rectangle
  * @param aRect [inout] rect into which its bounds should be unioned
  * @param aHaveRect [inout] whether aRect contains data yet
+ * @param aPrevBlock [inout] the block aLines is a line iterator for
+ * @param aLines [inout] the line iterator we're using
+ * @param aCurLine [inout] the line to start looking from in this iterator
  */
 static void
 AccumulateFrameBounds(nsIFrame* aContainerFrame,
                       nsIFrame* aFrame,
                       PRBool aUseWholeLineHeightForInlines,
                       nsRect& aRect,
-                      PRBool& aHaveRect)
+                      PRBool& aHaveRect,
+                      nsIFrame*& aPrevBlock,
+                      nsAutoLineIterator& aLines,
+                      PRInt32& aCurLine)
 {
   nsRect frameBounds = aFrame->GetRect() +
     aFrame->GetParent()->GetOffsetTo(aContainerFrame);
 
   // If this is an inline frame and either the bounds height is 0 (quirks
   // layout model) or aUseWholeLineHeightForInlines is set, we need to
   // change the top of the bounds to include the whole line.
   if (frameBounds.height == 0 || aUseWholeLineHeightForInlines) {
@@ -4068,27 +4077,32 @@ AccumulateFrameBounds(nsIFrame* aContain
       prevFrame = f;
       f = prevFrame->GetParent();
     }
 
     if (f != aFrame &&
         f &&
         frameType == nsGkAtoms::blockFrame) {
       // find the line containing aFrame and increase the top of |offset|.
-      nsAutoLineIterator lines = f->GetLineIterator();
-      if (lines) {
-        PRInt32 index = lines->FindLineContaining(prevFrame);
+      if (f != aPrevBlock) {
+        aLines = f->GetLineIterator();
+        aPrevBlock = f;
+        aCurLine = 0;
+      }
+      if (aLines) {
+        PRInt32 index = aLines->FindLineContaining(prevFrame, aCurLine);
         if (index >= 0) {
+          aCurLine = index;
           nsIFrame *trash1;
           PRInt32 trash2;
           nsRect lineBounds;
           PRUint32 trash3;
 
-          if (NS_SUCCEEDED(lines->GetLine(index, &trash1, &trash2,
-                                          lineBounds, &trash3))) {
+          if (NS_SUCCEEDED(aLines->GetLine(index, &trash1, &trash2,
+                                           lineBounds, &trash3))) {
             lineBounds += f->GetOffsetTo(aContainerFrame);
             if (lineBounds.y < frameBounds.y) {
               frameBounds.height = frameBounds.YMost() - lineBounds.y;
               frameBounds.y = lineBounds.y;
             }
           }
         }
       }
@@ -4281,19 +4295,26 @@ PresShell::DoScrollContentIntoView(nsICo
   //         appropriately.
   // frameBounds is relative to container. We're assuming
   // that scrollframes don't split so every continuation of frame will
   // be a descendant of container. (Things would still mostly work
   // even if that assumption was false.)
   nsRect frameBounds;
   PRBool haveRect = PR_FALSE;
   PRBool useWholeLineHeightForInlines = aVPercent != NS_PRESSHELL_SCROLL_ANYWHERE;
+  // Reuse the same line iterator across calls to AccumulateFrameBounds.  We set
+  // it every time we detect a new block (stored in prevBlock).
+  nsIFrame* prevBlock = nsnull;
+  nsAutoLineIterator lines;
+  // The last line we found a continuation on in |lines|.  We assume that later
+  // continuations cannot come on earlier lines.
+  PRInt32 curLine = 0;
   do {
     AccumulateFrameBounds(container, frame, useWholeLineHeightForInlines,
-                          frameBounds, haveRect);
+                          frameBounds, haveRect, prevBlock, lines, curLine);
   } while ((frame = frame->GetNextContinuation()));
 
   ScrollFrameRectIntoView(container, frameBounds, aVPercent, aHPercent,
                           aFlags);
 }
 
 PRBool
 PresShell::ScrollFrameRectIntoView(nsIFrame*     aFrame,
@@ -5828,16 +5849,24 @@ static PRBool IsTransparentContainerElem
   if (!pwin)
     return PR_FALSE;
   nsCOMPtr<nsIContent> containerElement =
     do_QueryInterface(pwin->GetFrameElementInternal());
   return containerElement &&
          containerElement->HasAttr(kNameSpaceID_None, nsGkAtoms::transparent);
 }
 
+nscolor PresShell::GetDefaultBackgroundColorToDraw()
+{
+  if (!mPresContext || !mPresContext->GetBackgroundColorDraw()) {
+    return NS_RGB(255,255,255);
+  }
+  return mPresContext->DefaultBackgroundColor();
+}
+
 void PresShell::UpdateCanvasBackground()
 {
   // If we have a frame tree and it has style information that
   // specifies the background color of the canvas, update our local
   // cache of that color.
   nsIFrame* rootStyleFrame = FrameConstructor()->GetRootElementStyleFrame();
   if (rootStyleFrame) {
     nsStyleContext* bgStyle =
@@ -5847,40 +5876,40 @@ void PresShell::UpdateCanvasBackground()
     // a problem because only a few frames can return something other than true
     // and none of them would be a canvas frame or root element style frame.
     mCanvasBackgroundColor =
       nsCSSRendering::DetermineBackgroundColor(mPresContext, bgStyle,
                                                rootStyleFrame);
     if (GetPresContext()->IsRootContentDocument() &&
         !IsTransparentContainerElement(mPresContext)) {
       mCanvasBackgroundColor =
-        NS_ComposeColors(mPresContext->DefaultBackgroundColor(), mCanvasBackgroundColor);
+        NS_ComposeColors(GetDefaultBackgroundColorToDraw(), mCanvasBackgroundColor);
     }
   }
 
   // If the root element of the document (ie html) has style 'display: none'
   // then the document's background color does not get drawn; cache the
   // color we actually draw.
   if (!FrameConstructor()->GetRootElementFrame()) {
-    mCanvasBackgroundColor = mPresContext->DefaultBackgroundColor();
+    mCanvasBackgroundColor = GetDefaultBackgroundColorToDraw();
   }
 }
 
 nscolor PresShell::ComputeBackstopColor(nsIView* aDisplayRoot)
 {
   nsIWidget* widget = aDisplayRoot->GetWidget();
   if (widget && widget->GetTransparencyMode() != eTransparencyOpaque) {
     // Within a transparent widget, so the backstop color must be
     // totally transparent.
     return NS_RGBA(0,0,0,0);
   }
   // Within an opaque widget (or no widget at all), so the backstop
   // color must be totally opaque. The user's default background
   // as reported by the prescontext is guaranteed to be opaque.
-  return GetPresContext()->DefaultBackgroundColor();
+  return GetDefaultBackgroundColorToDraw();
 }
 
 struct PaintParams {
   nscolor mBackgroundColor;
 };
 
 LayerManager* PresShell::GetLayerManager()
 {
--- a/layout/base/tests/test_reftests_with_caret.html
+++ b/layout/base/tests/test_reftests_with_caret.html
@@ -92,45 +92,45 @@ function endTest() {
   } else {
     prefs.clearUserPref("ui.caretBlinkTime");
   }
 }
 
 var isWindows = /WINNT/.test(SpecialPowers.OS);
 
 var tests = [
-    [ 'bug106855-1.html' , 'bug106855-1-ref.html' ] ,
     [ 'bug389321-1.html' , 'bug389321-1-ref.html' ] ,
     [ 'bug389321-2.html' , 'bug389321-2-ref.html' ] ,
     [ 'bug389321-3.html' , 'bug389321-3-ref.html' ] ,
     [ 'bug482484.html'   , 'bug482484-ref.html'   ] ,
     [ 'bug585922.html'   , 'bug585922-ref.html'   ] ,
-    [ 'bug602141-2.html' , 'bug602141-2-ref.html' ] ,
     [ 'bug602141-3.html' , 'bug602141-3-ref.html' ] ,
-    [ 'bug613433-2.html' , 'bug613433-ref.html' ] ,
-    [ 'bug613433-3.html' , 'bug613433-ref.html' ] ,
     [ 'bug632215-1.html' , 'bug632215-ref.html'   ] ,
     [ 'bug632215-2.html' , 'bug632215-ref.html'   ] ,
     [ 'bug633044-1.html' , 'bug633044-1-ref.html' ] ,
     [ 'bug644428-1.html' , 'bug644428-1-ref.html' ] ,
 ];
 
 if (!isWindows) {
+  tests.push([ 'bug106855-1.html' , 'bug106855-1-ref.html' ]); // bug 682837
   tests.push([ 'bug106855-2.html' , 'bug106855-1-ref.html' ]); // bug 681138
   tests.push([ 'bug240933-1.html' , 'bug240933-1-ref.html' ]); // bug 681144
   tests.push([ 'bug240933-2.html' , 'bug240933-1-ref.html' ]); // bug 681162
   tests.push([ 'bug512295-1.html' , 'bug512295-1-ref.html' ]); // bug 681152
   tests.push([ 'bug512295-2.html' , 'bug512295-2-ref.html' ]); // bug 681331
   tests.push([ 'bug597519-1.html' , 'bug597519-1-ref.html' ]); // bug 680579
   tests.push([ 'bug602141-1.html' , 'bug602141-1-ref.html' ]); // bug 681334
+  tests.push([ 'bug602141-2.html' , 'bug602141-2-ref.html' ]); // bug 682836
   tests.push([ 'bug602141-4.html' , 'bug602141-4-ref.html' ]); // bug 681167
   tests.push([ 'bug612271-1.html' , 'bug612271-ref.html' ]);   // bug 681032
   tests.push([ 'bug612271-2.html' , 'bug612271-ref.html' ]);   // bug 680581
   tests.push([ 'bug612271-3.html' , 'bug612271-ref.html' ]);   // bug 681035
   tests.push([ 'bug613433-1.html' , 'bug613433-ref.html' ]);   // bug 681332
+  tests.push([ 'bug613433-2.html' , 'bug613433-ref.html' ]);   // bug 681332
+  tests.push([ 'bug613433-3.html' , 'bug613433-ref.html' ]);   // bug 681332
   tests.push([ 'bug613807-1.html' , 'bug613807-1-ref.html' ]); // bug 680574
   tests.push([ 'bug634406-1.html' , 'bug634406-1-ref.html' ]); // bug 681146
 }
 
 tests.push(function() {SpecialPowers.setBoolPref("bidi.browser.ui", true);});
 
 if (!isWindows) {
   tests.push([ 'bug646382-1.html' , 'bug646382-1-ref.html' ]);  // bug 681076
--- a/layout/doc/obsolete/nav4-html.html
+++ b/layout/doc/obsolete/nav4-html.html
@@ -71,17 +71,17 @@ the tag. The HTML parser serves as the t
 tags documented below. Tag attribute handling is done during translation
 of tags into content. This mapping translates the tag attributes into content
 data and into style data. The translation to style data is documented below
 by indicating the mapping from tag attributes to their CSS1 (plus extensions)
 equivalents.
 <H3>
 Special Hacks</H3>
 The following list describes hacks added to the magellan parsing engine
-to deal with navigator compatability. These are just the parser hacks,
+to deal with navigator compatibility. These are just the parser hacks,
 not the layout or presentation hacks. Most hacks are intriduced for HTML
 syntax error recovering. HTML doesn't specify much how to handle those
 error conditions. Netscape has made big effort to render pages with non-prefect
 HTML. For many reasons, new browsers need to keep compatible in thsi area.
 <UL>
 <LI>
 Entities can be used as escape in quoted string. For value string in name-value
 pair,&nbsp; see <A HREF="../testhtml/quote001.html">compatibility test
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -1541,23 +1541,30 @@ nsIFrame::BuildDisplayListForStackingCon
   if (disp->mOpacity == 0.0 && aBuilder->IsForPainting())
     return NS_OK;
 
   PRBool applyAbsPosClipping =
       ApplyAbsPosClipping(aBuilder, disp, this, &absPosClip);
   nsRect dirtyRect = aDirtyRect;
 
   PRBool inTransform = aBuilder->IsInTransform();
-  /* If we're being transformed, we need to invert the matrix transform so that we don't 
-   * grab points in the wrong coordinate system!
-   */
   if ((mState & NS_FRAME_MAY_BE_TRANSFORMED) &&
       disp->HasTransform()) {
-    /* If we have a complex transform, just grab the entire overflow rect instead. */
+    // Transform dirtyRect into our frame's local coordinate space. Note that
+    // the new value is the bounds of the old value's transformed vertices, so
+    // the area covered by dirtyRect may increase here.
+    //
+    // Although we don't bother to check for and maintain the 1x1 size of the
+    // magic rect indicating a hit test point, in reality this is extremely
+    // unlikely to matter. The rect starts off with dimensions of 1x1 *app*
+    // units, and it would require a very large number of elements with
+    // transforms along a parent chain to noticably expand this by an entire
+    // device pixel.
     if (Preserves3DChildren() || !nsDisplayTransform::UntransformRect(dirtyRect, this, nsPoint(0, 0), &dirtyRect)) {
+      // we have a singular transform - just grab the entire overflow rect
       dirtyRect = GetVisualOverflowRectRelativeToSelf();
     }
     inTransform = PR_TRUE;
   }
 
   if (applyAbsPosClipping) {
     dirtyRect.IntersectRect(dirtyRect,
                             absPosClip - aBuilder->ToReferenceFrame(this));
@@ -1640,38 +1647,45 @@ nsIFrame::BuildDisplayListForStackingCon
   set.Outlines()->SortByContentOrder(aBuilder, GetContent());
 #ifdef NS_DEBUG
   DisplayDebugBorders(aBuilder, this, set);
 #endif
   resultList.AppendToTop(set.Outlines());
   // 8, 9: non-negative z-index children
   resultList.AppendToTop(set.PositionedDescendants());
 
-  if (applyAbsPosClipping) {
+  /* If we have absolute position clipping and we have, or will have, items to
+   * be clipped, wrap the list in a clip wrapper.
+   */
+  if (applyAbsPosClipping &&
+      (!resultList.IsEmpty() || usingSVGEffects)) {
     nsAbsPosClipWrapper wrapper(absPosClip);
     nsDisplayItem* item = wrapper.WrapList(aBuilder, this, &resultList);
     if (!item)
       return NS_ERROR_OUT_OF_MEMORY;
     // resultList was emptied
     resultList.AppendToTop(item);
   }
- 
-  /* If there are any SVG effects, wrap up the list in an effects list. */
+
+  /* If there are any SVG effects, wrap the list up in an SVG effects item
+   * (which also handles CSS group opacity). Note that we create an SVG effects
+   * item even if resultList is empty, since a filter can produce graphical
+   * output even if the element being filtered wouldn't otherwise do so.
+   */
   if (usingSVGEffects) {
     /* List now emptied, so add the new list to the top. */
     rv = resultList.AppendNewToTop(
         new (aBuilder) nsDisplaySVGEffects(aBuilder, this, &resultList));
     if (NS_FAILED(rv))
       return rv;
-  } else
-
-  /* If there is any opacity, wrap it up in an opacity list.
-   * If there's nothing in the list, don't add anything.
+  }
+  /* Else, if the list is non-empty and there is CSS group opacity without SVG
+   * effects, wrap it up in an opacity item.
    */
-  if (disp->mOpacity < 1.0f && !resultList.IsEmpty()) {
+  else if (disp->mOpacity < 1.0f && !resultList.IsEmpty()) {
     rv = resultList.AppendNewToTop(
         new (aBuilder) nsDisplayOpacity(aBuilder, this, &resultList));
     if (NS_FAILED(rv))
       return rv;
   }
 
   /* If we're going to apply a transformation and don't have preserve-3d set, wrap 
    * everything in an nsDisplayTransform. If there's nothing in the list, don't add 
@@ -1804,21 +1818,22 @@ nsIFrame::BuildDisplayListForChild(nsDis
   // REVIEW: Taken from nsBoxFrame::Paint
   // Don't paint our children if the theme object is a leaf.
   if (IsThemed(ourDisp) &&
       !PresContext()->GetTheme()->WidgetIsContainer(ourDisp->mAppearance))
     return NS_OK;
 
   // Child is composited if it's transformed, partially transparent, or has
   // SVG effects.
-  PRBool isComposited = disp->mOpacity != 1.0f || aChild->IsTransformed()
+  PRBool isVisuallyAtomic = disp->mOpacity != 1.0f
+    || aChild->IsTransformed()
     || nsSVGIntegrationUtils::UsingEffectsForFrame(aChild);
 
   PRBool isPositioned = disp->IsPositioned();
-  if (isComposited || isPositioned || disp->IsFloating() ||
+  if (isVisuallyAtomic || isPositioned || disp->IsFloating() ||
       (aFlags & DISPLAY_CHILD_FORCE_STACKING_CONTEXT)) {
     // If you change this, also change IsPseudoStackingContextFromStyle()
     pseudoStackingContext = PR_TRUE;
   }
   
   nsRect overflowClip;
   nscoord overflowClipRadii[8];
   PRBool applyOverflowClip =
@@ -1853,29 +1868,29 @@ nsIFrame::BuildDisplayListForChild(nsDis
 #endif
     return rv;
   }
   
   nsDisplayList list;
   nsDisplayList extraPositionedDescendants;
   const nsStylePosition* pos = aChild->GetStylePosition();
   if ((isPositioned && pos->mZIndex.GetUnit() == eStyleUnit_Integer) ||
-      isComposited || (aFlags & DISPLAY_CHILD_FORCE_STACKING_CONTEXT)) {
+      isVisuallyAtomic || (aFlags & DISPLAY_CHILD_FORCE_STACKING_CONTEXT)) {
     // True stacking context
     rv = aChild->BuildDisplayListForStackingContext(aBuilder, dirty, &list);
     if (NS_SUCCEEDED(rv)) {
       rv = aBuilder->DisplayCaret(aChild, dirty, &list);
     }
   } else {
     nsRect clipRect;
     PRBool applyAbsPosClipping =
         ApplyAbsPosClipping(aBuilder, disp, aChild, &clipRect);
     // A pseudo-stacking context (e.g., a positioned element with z-index auto).
-    // we allow positioned descendants of this element to escape to our
-    // container's positioned descendant list, because they might be
+    // We allow positioned descendants of the child to escape to our parent
+    // stacking context's positioned descendant list, because they might be
     // z-index:non-auto
     nsDisplayListCollection pseudoStack;
     nsRect clippedDirtyRect = dirty;
     if (applyAbsPosClipping) {
       // clipRect is in builder-reference-frame coordinates,
       // dirty/clippedDirtyRect are in aChild coordinates
       clippedDirtyRect.IntersectRect(clippedDirtyRect,
                                      clipRect - aBuilder->ToReferenceFrame(aChild));
@@ -1905,17 +1920,17 @@ nsIFrame::BuildDisplayListForChild(nsDis
     list.AppendToTop(pseudoStack.Outlines());
     extraPositionedDescendants.AppendToTop(pseudoStack.PositionedDescendants());
 #ifdef NS_DEBUG
     DisplayDebugBorders(aBuilder, aChild, aLists);
 #endif
   }
   NS_ENSURE_SUCCESS(rv, rv);
     
-  if (isPositioned || isComposited ||
+  if (isPositioned || isVisuallyAtomic ||
       (aFlags & DISPLAY_CHILD_FORCE_STACKING_CONTEXT)) {
     // Genuine stacking contexts, and positioned pseudo-stacking-contexts,
     // go in this level.
     rv = aLists.PositionedDescendants()->AppendNewToTop(new (aBuilder)
         nsDisplayWrapList(aBuilder, aChild, &list));
     NS_ENSURE_SUCCESS(rv, rv);
   } else if (disp->IsFloating()) {
     rv = aLists.Floats()->AppendNewToTop(new (aBuilder)
--- a/layout/generic/nsIFrame.h
+++ b/layout/generic/nsIFrame.h
@@ -1,17 +1,17 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=2 sw=2 et tw=78: */
 /* ***** BEGIN LICENSE BLOCK *****
  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  *
  * The contents of this file are subject to the Mozilla Public License Version
  * 1.1 (the "License"); you may not use this file except in compliance with
  * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/I
+ * http://www.mozilla.org/MPL/
  *
  * Software distributed under the License is distributed on an "AS IS" basis,
  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  * for the specific language governing rights and limitations under the
  * License.
  *
  * The Original Code is mozilla.org code.
  *
@@ -556,17 +556,17 @@ public:
    */
   NS_IMETHOD  Init(nsIContent*      aContent,
                    nsIFrame*        aParent,
                    nsIFrame*        aPrevInFlow) = 0;
 
   /**
    * Destroys this frame and each of its child frames (recursively calls
    * Destroy() for each child). If this frame is a first-continuation, this
-   * also removes the frame from the primary frame man and clears undisplayed
+   * also removes the frame from the primary frame map and clears undisplayed
    * content for its content node.
    * If the frame is a placeholder, it also ensures the out-of-flow frame's
    * removal and destruction.
    */
   void Destroy() { DestroyFrom(this); }
 
 protected:
   /**
--- a/layout/generic/nsILineIterator.h
+++ b/layout/generic/nsILineIterator.h
@@ -99,19 +99,22 @@ public:
   NS_IMETHOD GetLine(PRInt32 aLineNumber,
                      nsIFrame** aFirstFrameOnLine,
                      PRInt32* aNumFramesOnLine,
                      nsRect& aLineBounds,
                      PRUint32* aLineFlags) = 0;
 
   /**
    * Given a frame that's a child of the block, find which line its on
-   * and return that line index. Returns -1 if the frame cannot be found.
+   * and return that line index, as long as it's at least as big as
+   * aStartLine.  Returns -1 if the frame cannot be found on lines
+   * starting with aStartLine.
    */
-  virtual PRInt32 FindLineContaining(nsIFrame* aFrame) = 0;
+  virtual PRInt32 FindLineContaining(nsIFrame* aFrame,
+                                     PRInt32 aStartLine = 0) = 0;
 
   // Given a line number and an X coordinate, find the frame on the
   // line that is nearest to the X coordinate. The
   // aXIsBeforeFirstFrame and aXIsAfterLastFrame flags are updated
   // appropriately.
   NS_IMETHOD FindFrameAt(PRInt32 aLineNumber,
                          nscoord aX,
                          nsIFrame** aFrameFound,
--- a/layout/generic/nsImageMap.cpp
+++ b/layout/generic/nsImageMap.cpp
@@ -398,17 +398,17 @@ void RectArea::ParseCoords(const nsAStri
 
   if (!saneRect) {
     logMessage(mArea, aSpec, flag, "ImageMapRectBoundsError");
   }
 }
 
 PRBool RectArea::IsInside(nscoord x, nscoord y) const
 {
-  if (mNumCoords >= 4) {       // Note: > is for nav compatability
+  if (mNumCoords >= 4) {       // Note: > is for nav compatibility
     nscoord x1 = mCoords[0];
     nscoord y1 = mCoords[1];
     nscoord x2 = mCoords[2];
     nscoord y2 = mCoords[3];
     NS_ASSERTION(x1 <= x2 && y1 <= y2,
                  "Someone screwed up RectArea::ParseCoords");
     if ((x >= x1) && (x <= x2) && (y >= y1) && (y <= y2)) {
       return PR_TRUE;
@@ -633,17 +633,17 @@ void CircleArea::ParseCoords(const nsASt
                aSpec,
                flag,
                "ImageMapCircleWrongNumberOfCoords");
   }
 }
 
 PRBool CircleArea::IsInside(nscoord x, nscoord y) const
 {
-  // Note: > is for nav compatability
+  // Note: > is for nav compatibility
   if (mNumCoords >= 3) {
     nscoord x1 = mCoords[0];
     nscoord y1 = mCoords[1];
     nscoord radius = mCoords[2];
     if (radius < 0) {
       return PR_FALSE;
     }
     nscoord dx = x1 - x;
--- a/layout/generic/nsLineBox.cpp
+++ b/layout/generic/nsLineBox.cpp
@@ -621,25 +621,26 @@ nsLineIterator::GetLine(PRInt32 aLineNum
       flags |= NS_LINE_FLAG_ENDS_IN_BREAK;
   }
   *aLineFlags = flags;
 
   return NS_OK;
 }
 
 PRInt32
-nsLineIterator::FindLineContaining(nsIFrame* aFrame)
+nsLineIterator::FindLineContaining(nsIFrame* aFrame, PRInt32 aStartLine)
 {
-  nsLineBox* line = mLines[0];
-  PRInt32 lineNumber = 0;
+  NS_PRECONDITION(aStartLine <= mNumLines, "Bogus line numbers");
+  PRInt32 lineNumber = aStartLine;
   while (lineNumber != mNumLines) {
+    nsLineBox* line = mLines[lineNumber];
     if (line->Contains(aFrame)) {
       return lineNumber;
     }
-    line = mLines[++lineNumber];
+    ++lineNumber;
   }
   return -1;
 }
 
 #ifdef IBMBIDI
 NS_IMETHODIMP
 nsLineIterator::CheckLineOrder(PRInt32                  aLine,
                                PRBool                   *aIsReordered,
--- a/layout/generic/nsLineBox.h
+++ b/layout/generic/nsLineBox.h
@@ -1576,17 +1576,17 @@ public:
 
   virtual PRInt32 GetNumLines();
   virtual PRBool GetDirection();
   NS_IMETHOD GetLine(PRInt32 aLineNumber,
                      nsIFrame** aFirstFrameOnLine,
                      PRInt32* aNumFramesOnLine,
                      nsRect& aLineBounds,
                      PRUint32* aLineFlags);
-  virtual PRInt32 FindLineContaining(nsIFrame* aFrame);
+  virtual PRInt32 FindLineContaining(nsIFrame* aFrame, PRInt32 aStartLine = 0);
   NS_IMETHOD FindFrameAt(PRInt32 aLineNumber,
                          nscoord aX,
                          nsIFrame** aFrameFound,
                          PRBool* aXIsBeforeFirstFrame,
                          PRBool* aXIsAfterLastFrame);
 
   NS_IMETHOD GetNextSiblingOnLine(nsIFrame*& aFrame, PRInt32 aLineNumber);
 #ifdef IBMBIDI
new file mode 100644
--- /dev/null
+++ b/layout/reftests/editor/462758-grabbers-resizers-ref.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<head>
+  <script type="text/javascript">
+    function init() {
+      var editor = document.querySelector("div[contenteditable]");
+      editor.addEventListener("focus", function() {
+        setTimeout(function() {
+          document.documentElement.className = "";
+        }, 0);
+      }, false);
+      editor.focus();
+    }
+  </script>
+  <style type="text/css">
+    html, body, div {
+      margin: 0;
+      padding: 0;
+    }
+    div {
+      border: 1px solid black;
+      margin: 50px;
+      height: 200px;
+      width: 200px;
+    }
+  </style>
+</head>
+<body onload="init()">
+  <div contenteditable>
+    this editable container should be neither draggable nor resizable.
+  </div>
+</body>
+</html>
+
new file mode 100644
--- /dev/null
+++ b/layout/reftests/editor/462758-grabbers-resizers.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<head>
+  <script type="text/javascript">
+    function init() {
+      var editor = document.querySelector("div[contenteditable]");
+      editor.addEventListener("focus", function() {
+        setTimeout(function() {
+          document.documentElement.className = "";
+        }, 0);
+      }, false);
+      editor.focus();
+    }
+  </script>
+  <style type="text/css">
+    html, body, div {
+      margin: 0;
+      padding: 0;
+    }
+    div {
+      border: 1px solid black;
+      margin: 50px;
+      height: 200px;
+      width: 200px;
+    }
+  </style>
+</head>
+<body onload="init()">
+  <div contenteditable style="position: absolute">
+    this editable container should be neither draggable nor resizable.
+  </div>
+</body>
+</html>
--- a/layout/reftests/editor/reftest.list
+++ b/layout/reftests/editor/reftest.list
@@ -63,8 +63,9 @@ fails-if(Android) != spellcheck-hyphen-m
 == selection_visibility_after_reframe.html selection_visibility_after_reframe-ref.html
 != selection_visibility_after_reframe-2.html selection_visibility_after_reframe-ref.html
 != selection_visibility_after_reframe-3.html selection_visibility_after_reframe-ref.html
 == 672709.html 672709-ref.html
 == 338427-1.html 338427-1-ref.html
 skip-if(Android) == 674212-spellcheck.html 674212-spellcheck-ref.html
 skip-if(Android) == 338427-2.html 338427-2-ref.html
 skip-if(Android) == 338427-3.html 338427-3-ref.html
+skip-if(Android) == 462758-grabbers-resizers.html 462758-grabbers-resizers-ref.html
--- a/layout/style/nsComputedDOMStyle.cpp
+++ b/layout/style/nsComputedDOMStyle.cpp
@@ -1053,22 +1053,22 @@ nsComputedDOMStyle::DoGetMozTransform()
    gfx3DMatrix matrix =
      nsStyleTransformMatrix::ReadTransforms(display->mSpecifiedTransform,
                                             mStyleContextHolder,
                                             mStyleContextHolder->PresContext(),
                                             dummy,
                                             bounds,
                                             float(nsDeviceContext::AppUnitsPerCSSPixel()));
 
-  if (!matrix.Is2D()) {
-    nsROCSSPrimitiveValue* val = GetROCSSPrimitiveValue();
-
-    /* Set it to "none." */
-    val->SetIdent(eCSSKeyword_none);
-    return val;
+  if (!matrix.Is2D()) {
+    nsROCSSPrimitiveValue* val = GetROCSSPrimitiveValue();
+
+    /* Set it to "none." */
+    val->SetIdent(eCSSKeyword_none);
+    return val;
   }
 
   nsAutoString resultString(NS_LITERAL_STRING("matrix("));
   resultString.AppendFloat(matrix._11);
   resultString.Append(NS_LITERAL_STRING(", "));
   resultString.AppendFloat(matrix._12);
   resultString.Append(NS_LITERAL_STRING(", "));
   resultString.AppendFloat(matrix._21);
--- a/layout/style/nsROCSSPrimitiveValue.h
+++ b/layout/style/nsROCSSPrimitiveValue.h
@@ -42,16 +42,20 @@
 
 #include "nsIDOMCSSPrimitiveValue.h"
 #include "nsCoord.h"
 #include "nsCSSKeywords.h"
 
 class nsIURI;
 class nsDOMCSSRGBColor;
 
+/**
+ * Read-only CSS primitive value - a DOM object representing values in DOM
+ * computed style.
+ */
 class nsROCSSPrimitiveValue : public nsIDOMCSSPrimitiveValue
 {
 public:
   NS_DECL_ISUPPORTS
 
   // nsIDOMCSSPrimitiveValue
   NS_DECL_NSIDOMCSSPRIMITIVEVALUE
 
--- a/layout/style/nsRuleProcessorData.h
+++ b/layout/style/nsRuleProcessorData.h
@@ -39,17 +39,17 @@
 /*
  * data structures passed to nsIStyleRuleProcessor methods (to pull loop
  * invariant computations out of the loop)
  */
 
 #ifndef nsRuleProcessorData_h_
 #define nsRuleProcessorData_h_
 
-#include "nsPresContext.h" // for nsCompatability
+#include "nsPresContext.h" // for nsCompatibility
 #include "nsString.h"
 #include "nsChangeHint.h"
 #include "nsIContent.h"
 #include "nsCSSPseudoElements.h"
 #include "nsRuleWalker.h"
 #include "nsNthIndexCache.h"
 
 class nsIStyleSheet;
--- a/layout/style/nsStyleTransformMatrix.cpp
+++ b/layout/style/nsStyleTransformMatrix.cpp
@@ -213,27 +213,27 @@ nsStyleTransformMatrix::ProcessInterpola
                                        aContext, aPresContext,
                                        aCanStoreInRuleTree,
                                        aBounds, aAppUnitsPerMatrixUnit);
   double coeff2 = aData->Item(3).GetPercentValue();
   gfx3DMatrix matrix2 = ReadTransforms(aData->Item(4).GetListValue(),
                                        aContext, aPresContext,
                                        aCanStoreInRuleTree,
                                        aBounds, aAppUnitsPerMatrixUnit);
-
-  gfxMatrix matrix2d1, matrix2d2;
-#ifdef DEBUG
-  PRBool is2d =
-#endif
-  matrix1.Is2D(&matrix2d1);
-  NS_ABORT_IF_FALSE(is2d, "Can't do animations with 3d transforms!");
-#ifdef DEBUG
-  is2d =
-#endif
-  matrix2.Is2D(&matrix2d2);
+
+  gfxMatrix matrix2d1, matrix2d2;
+#ifdef DEBUG
+  PRBool is2d =
+#endif
+  matrix1.Is2D(&matrix2d1);
+  NS_ABORT_IF_FALSE(is2d, "Can't do animations with 3d transforms!");
+#ifdef DEBUG
+  is2d =
+#endif
+  matrix2.Is2D(&matrix2d2);
   NS_ABORT_IF_FALSE(is2d, "Can't do animations with 3d transforms!");
 
   return gfx3DMatrix::From2D(
     nsStyleAnimation::InterpolateTransformMatrix(matrix2d1, coeff1, 
                                                  matrix2d2, coeff2));
 }
 
 /* Helper function to process a translatex function. */
--- a/layout/svg/base/src/nsSVGPathGeometryFrame.cpp
+++ b/layout/svg/base/src/nsSVGPathGeometryFrame.cpp
@@ -191,60 +191,16 @@ nsSVGPathGeometryFrame::GetFrameForPoint
     return this;
 
   return nsnull;
 }
 
 NS_IMETHODIMP_(nsRect)
 nsSVGPathGeometryFrame::GetCoveredRegion()
 {
-  // XXX why are we adding in markers here each time someone gets the covered
-  // region? Isn't UpdateCoveredRegion called whenever markers change?
-  // And why are the answers to these questions not documented here??!!
-
-  if (static_cast<nsSVGPathGeometryElement*>(mContent)->IsMarkable()) {
-    MarkerProperties properties = GetMarkerProperties(this);
-
-    if (!properties.MarkersExist())
-      return mRect;
-
-    nsRect rect(mRect);
-
-    float strokeWidth = GetStrokeWidth();
-
-    nsTArray<nsSVGMark> marks;
-    static_cast<nsSVGPathGeometryElement*>(mContent)->GetMarkPoints(&marks);
-
-    PRUint32 num = marks.Length();
-
-    if (num) {
-      nsSVGMarkerFrame *frame = properties.GetMarkerStartFrame();
-      if (frame) {
-        nsRect mark = frame->RegionMark(this, &marks[0], strokeWidth);
-        rect.UnionRect(rect, mark);
-      }
-
-      frame = properties.GetMarkerMidFrame();
-      if (frame) {
-        for (PRUint32 i = 1; i < num - 1; i++) {
-          nsRect mark = frame->RegionMark(this, &marks[i], strokeWidth);
-          rect.UnionRect(rect, mark);
-        }
-      }
-
-      frame = properties.GetMarkerEndFrame();
-      if (frame) {
-        nsRect mark = frame->RegionMark(this, &marks[num-1], strokeWidth);
-        rect.UnionRect(rect, mark);
-      }
-    }
-
-    return rect;
-  }
-
   return mRect;
 }
 
 NS_IMETHODIMP
 nsSVGPathGeometryFrame::UpdateCoveredRegion()
 {
   mRect.SetEmpty();
 
@@ -283,17 +239,49 @@ nsSVGPathGeometryFrame::UpdateCoveredReg
     extent = gfxRect(0, 0, 0, 0);
   }
 
   if (!extent.IsEmpty()) {
     mRect = nsSVGUtils::ToAppPixelRect(PresContext(), extent);
   }
 
   // Add in markers
-  mRect = GetCoveredRegion();
+  if (static_cast<nsSVGPathGeometryElement*>(mContent)->IsMarkable()) {
+
+    float strokeWidth = GetStrokeWidth();
+    MarkerProperties properties = GetMarkerProperties(this);
+
+    if (properties.MarkersExist()) {
+      nsTArray<nsSVGMark> marks;
+      static_cast<nsSVGPathGeometryElement*>(mContent)->GetMarkPoints(&marks);
+      PRUint32 num = marks.Length();
+
+      if (num) {
+        nsSVGMarkerFrame *frame = properties.GetMarkerStartFrame();
+        if (frame) {
+          nsRect rect = frame->RegionMark(this, &marks[0], strokeWidth);
+          mRect.UnionRect(mRect, rect);
+        }
+
+        frame = properties.GetMarkerMidFrame();
+        if (frame) {
+          for (PRUint32 i = 1; i < num - 1; i++) {
+            nsRect rect = frame->RegionMark(this, &marks[i], strokeWidth);
+            mRect.UnionRect(mRect, rect);
+          }
+        }
+
+        frame = properties.GetMarkerEndFrame();
+        if (frame) {
+          nsRect rect = frame->RegionMark(this, &marks[num-1], strokeWidth);
+          mRect.UnionRect(mRect, rect);
+        }
+      }
+    }
+  }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsSVGPathGeometryFrame::InitialUpdate()
 {
   NS_ASSERTION(GetStateBits() & NS_FRAME_FIRST_REFLOW,
--- a/layout/tables/nsTableFrame.h
+++ b/layout/tables/nsTableFrame.h
@@ -548,18 +548,18 @@ protected:
 
   /** return the width of the table taking into account visibility collapse
     * on columns and colgroups
     * @param aBorderPadding  the border and padding of the table
     */
   nscoord GetCollapsedWidth(nsMargin aBorderPadding);
 
   
-  /** Adjust the table for visibilty.collapse set on rowgroups, rows, colgroups
-    * and cols
+  /** Adjust the table for visibility.collapse set on rowgroups, rows,
+    * colgroups and cols
     * @param aDesiredSize    the metrics of the table
     * @param aBorderPadding  the border and padding of the table
     */
   void AdjustForCollapsingRowsCols(nsHTMLReflowMetrics& aDesiredSize,
                                    nsMargin             aBorderPadding);
 
   nsITableLayoutStrategy* LayoutStrategy() const {
     return static_cast<nsTableFrame*>(GetFirstInFlow())->
--- a/layout/tables/nsTableRowGroupFrame.cpp
+++ b/layout/tables/nsTableRowGroupFrame.cpp
@@ -1701,24 +1701,26 @@ nsTableRowGroupFrame::GetLine(PRInt32   
       return NS_OK;
     }
   }
   NS_ERROR("cellmap is lying");
   return NS_ERROR_FAILURE;
 }
   
 PRInt32
-nsTableRowGroupFrame::FindLineContaining(nsIFrame* aFrame)
+nsTableRowGroupFrame::FindLineContaining(nsIFrame* aFrame, PRInt32 aStartLine)
 {
   NS_ENSURE_ARG_POINTER(aFrame);
   
   nsTableRowFrame *rowFrame = do_QueryFrame(aFrame);
   NS_ASSERTION(rowFrame, "RowGroup contains a frame that is not a row");
 
-  return rowFrame->GetRowIndex() - GetStartRowIndex();
+  PRInt32 rowIndexInGroup = rowFrame->GetRowIndex() - GetStartRowIndex();
+
+  return rowIndexInGroup >= aStartLine ? rowIndexInGroup : -1;
 }
 
 #ifdef IBMBIDI
 NS_IMETHODIMP
 nsTableRowGroupFrame::CheckLineOrder(PRInt32                  aLine,
                                      PRBool                   *aIsReordered,
                                      nsIFrame                 **aFirstVisual,
                                      nsIFrame                 **aLastVisual)
--- a/layout/tables/nsTableRowGroupFrame.h
+++ b/layout/tables/nsTableRowGroupFrame.h
@@ -253,20 +253,22 @@ public:
   NS_IMETHOD GetLine(PRInt32 aLineNumber,
                      nsIFrame** aFirstFrameOnLine,
                      PRInt32* aNumFramesOnLine,
                      nsRect& aLineBounds,
                      PRUint32* aLineFlags);
   
   /** Given a frame that's a child of the rowgroup, find which line its on.
     * @param aFrame       - frame, should be a row
+    * @param aStartLine   - minimal index to return
     * @return               row index relative to the row group if this a row
-    *                       frame. -1 if the frame cannot be found.
+    *                       frame and the index is at least aStartLine.
+    *                       -1 if the frame cannot be found.
     */
-  virtual PRInt32 FindLineContaining(nsIFrame* aFrame);
+  virtual PRInt32 FindLineContaining(nsIFrame* aFrame, PRInt32 aStartLine = 0);
 
   /** Find the orginating cell frame on a row that is the nearest to the
     * coordinate X.
     * @param aLineNumber          - the index of the row relative to the row group
     * @param aX                   - X coordinate in twips relative to the
     *                               origin of the row group
     * @param aFrameFound          - pointer to the cellframe
     * @param aXIsBeforeFirstFrame - the point is before the first originating
--- a/mfbt/Util.h
+++ b/mfbt/Util.h
@@ -160,26 +160,34 @@ struct DebugOnly
     T value;
 
     DebugOnly() {}
     DebugOnly(const T& other) : value(other) {}
     DebugOnly& operator=(const T& rhs) {
         value = rhs;
         return *this;
     }
+    void operator++(int) {
+        value++;
+    }
+    void operator--(int) {
+        value--;
+    }
 
     operator T&() { return value; }
     operator const T&() const { return value; }
 
     T& operator->() { return value; }
 
 #else
     DebugOnly() {}
     DebugOnly(const T&) {}
     DebugOnly& operator=(const T&) { return *this; }
+    void operator++(int) {}
+    void operator--(int) {}
 #endif
 
     /*
      * DebugOnly must always have a destructor or else it will
      * generate "unused variable" warnings, exactly what it's intended
      * to avoid!
      */
     ~DebugOnly() {}
--- a/mobile/chrome/content/AnimatedZoom.js
+++ b/mobile/chrome/content/AnimatedZoom.js
@@ -71,32 +71,32 @@ const AnimatedZoom = {
       event.initEvent("AnimatedZoomBegin", true, true);
       window.dispatchEvent(event);
     }
   },
 
   start: function start() {
     this.tab = Browser.selectedTab;
     this.browser = this.tab.browser;
+    this.bcr = this.browser.getBoundingClientRect();
     this.zoomFrom = this.zoomRect || this.getStartRect();
     this.startScale = this.browser.scale;
     this.beginTime = mozAnimationStartTime;
   },
 
   /** Get the visible rect, in device pixels relative to the content origin. */
   getStartRect: function getStartRect() {
     let browser = this.browser;
-    let bcr = browser.getBoundingClientRect();
     let scroll = browser.getRootView().getPosition();
-    return new Rect(scroll.x, scroll.y, bcr.width, bcr.height);
+    return new Rect(scroll.x, scroll.y, this.bcr.width, this.bcr.height);
   },
 
   /** Update the visible rect, in device pixels relative to the content origin. */
   updateTo: function(nextRect) {
-    let zoomRatio = window.innerWidth / nextRect.width;
+    let zoomRatio = this.bcr.width / nextRect.width;
     let scale = this.startScale * zoomRatio;
     let scrollX = nextRect.left * zoomRatio;
     let scrollY = nextRect.top * zoomRatio;
 
     this.browser.fuzzyZoom(scale, scrollX, scrollY);
 
     this.zoomRect = nextRect;
   },
--- a/mobile/chrome/content/Util.js
+++ b/mobile/chrome/content/Util.js
@@ -191,16 +191,23 @@ let Util = {
     return (screen.width <= screen.height);
 #elifdef ANDROID
     return (screen.width <= screen.height);
 #else
     return (window.innerWidth <= window.innerHeight);
 #endif
   },
 
+  modifierMaskFromEvent: function modifierMaskFromEvent(aEvent) {
+    return (aEvent.altKey   ? Ci.nsIDOMNSEvent.ALT_MASK     : 0) |
+           (aEvent.ctrlKey  ? Ci.nsIDOMNSEvent.CONTROL_MASK : 0) |
+           (aEvent.shiftKey ? Ci.nsIDOMNSEvent.SHIFT_MASK   : 0) |
+           (aEvent.metaKey  ? Ci.nsIDOMNSEvent.META_MASK    : 0);
+  },
+
   get isKeyboardOpened() {
     // This might get called from the child process, or from a frame script in the
     // parent process (which does not have access to the main "window" global).
     let isChromeWindow = this.isParentProcess() && typeof window == "object" && window["ViewableAreaObserver"];
     if (isChromeWindow)
       return ViewableAreaObserver.isKeyboardOpened;
 
     return (sendSyncMessage("Content:IsKeyboardOpened", {}))[0];
--- a/mobile/chrome/content/bindings/arrowbox.xml
+++ b/mobile/chrome/content/bindings/arrowbox.xml
@@ -40,17 +40,17 @@
     xmlns="http://www.mozilla.org/xbl"
     xmlns:xbl="http://www.mozilla.org/xbl"
     xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
 
   <binding id="arrowbox" extends="xul:box">
     <content orient="vertical">
       <xul:box anonid="container" class="panel-arrowcontainer" flex="1">
         <xul:box anonid="arrowbox" class="panel-arrowbox" dir="ltr">
-          <xul:image anonid="arrow" class="panel-arrow"/>
+          <xul:image anonid="arrow" class="panel-arrow" xbl:inherits="type"/>
         </xul:box>
         <xul:scrollbox anonid="arrowcontent" class="panel-arrowcontent" flex="1">
           <xul:box class="panel-inner-arrowcontent" xbl:inherits="align,dir,orient,pack,flex">
             <children/>
           </xul:box>
         </xul:scrollbox>
       </xul:box>
     </content>
--- a/mobile/chrome/content/bindings/browser.xml
+++ b/mobile/chrome/content/bindings/browser.xml
@@ -373,16 +373,18 @@
           ]]>
         </body>
       </method>
 
       <field name="_frameLoader">null</field>
       <field name="_contentViewManager">null</field>
 
       <!-- Dimensions of content window -->
+      <field name="_contentWindowWidth">0</field>
+      <field name="_contentWindowHeight">0</field>
       <property name="contentWindowWidth"
                 onget="return this._contentWindowWidth;"
                 readonly="true"/>
       <property name="contentWindowHeight"
                 onget="return this._contentWindowHeight;"
                 readonly="true"/>
 
       <!-- Dimensions of content document -->
--- a/mobile/chrome/content/browser.js
+++ b/mobile/chrome/content/browser.js
@@ -1791,17 +1791,17 @@ const ContentTouchHandler = {
             this.tapDown(aEvent.clientX, aEvent.clientY);
             break;
           case "TapOver":
             this.tapOver(aEvent.clientX, aEvent.clientY);
             break;
           case "TapUp":
             if (aEvent.isClick) {
               if (!Browser.selectedTab.allowZoom) {
-                this.tapSingle(aEvent.clientX, aEvent.clientY, aEvent.modifiers);
+                this.tapSingle(aEvent.clientX, aEvent.clientY, Util.modifierMaskFromEvent(aEvent));
                 aEvent.preventDefault();
               }
             }
             this._dispatchMouseEvent("Browser:MouseUp", aEvent.clientX, aEvent.clientY);
             break;
           case "TapSingle":
             this.tapSingle(aEvent.clientX, aEvent.clientY, aEvent.modifiers);
             this._dispatchMouseEvent("Browser:MouseUp", aEvent.clientX, aEvent.clientY);
@@ -2847,18 +2847,20 @@ Tab.prototype = {
     let browser = this._createBrowser(aURI, null);
 
     // Should we fully load the new browser, or wait until later
     if ("delayLoad" in aParams && aParams.delayLoad)
       return;
 
     try {
       let flags = aParams.flags || Ci.nsIWebNavigation.LOAD_FLAGS_NONE;
-      let postData = aParams.postData ? aParams.postData.value : null;
-      browser.loadURIWithFlags(aURI, flags, aParams.referrerURI, aParams.charset, postData);
+      let postData = "postData" in aParams ? aParams.postData.value : null;
+      let referrerURI = "referrerURI" in aParams ? aParams.referrerURI : null;
+      let charset = "charset" in aParams ? aParams.charset : null;
+      browser.loadURIWithFlags(aURI, flags, referrerURI, charset, postData);
     } catch(e) {
       dump("Error: " + e + "\n");
     }
   },
 
   destroy: function destroy() {
     document.getElementById("tabs").removeTab(this._chromeTab);
     this._chromeTab = null;
@@ -3043,16 +3045,18 @@ Tab.prototype = {
     let screenW = aScreenWidth || this._browser.getBoundingClientRect().width;
     return screenW / browserW;
   },
 
   get allowZoom() {
     return this.metadata.allowZoom && !Util.isURLEmpty(this.browser.currentURI.spec);
   },
 
+  _thumbnailWindowId: null,
+
   updateThumbnail: function updateThumbnail(options) {
     let options = options || {};
     let browser = this._browser;
 
     if (this._loading) {
       this._drawThumb = true;
       return;
     }
--- a/mobile/chrome/content/browser.xul
+++ b/mobile/chrome/content/browser.xul
@@ -311,17 +311,17 @@
     </scrollbox>
 
     <!-- Form Helper suggestions helper popup -->
     <arrowbox id="form-helper-suggestions-container" flex="1" hidden="true" offset="0" top="0" left="0">
       <arrowscrollbox id="form-helper-suggestions" align="center" flex="1" orient="horizontal" onclick="FormHelperUI.doAutoComplete(event.target);"/>
     </arrowbox>
 
     <!-- popup for site identity information -->
-    <arrowbox id="identity-container" hidden="true" mode="unknownIdentity" offset="18" flex="1">
+    <arrowbox id="identity-container" hidden="true" mode="unknownIdentity" offset="18" flex="1" type="dialog">
       <hbox id="identity-popup-container" flex="1" align="top">
         <image id="identity-popup-icon"/>
         <vbox id="identity-popup-content-box" flex="1">
           <hbox flex="1">
             <label id="identity-popup-connectedToLabel" value="&identity.connectedTo2;"/>
             <label id="identity-popup-connectedToLabel2" flex="1">&identity.unverifiedsite2;</label>
             <description id="identity-popup-content-host" flex="1"/>
           </hbox>
@@ -354,17 +354,17 @@
         <pageaction id="pageaction-reset" title="&pageactions.reset;"
           onclick="PageActions.clearPagePermissions(event);"/>
         <pageaction id="pageaction-search" title="&pageactions.search.addNew;"/>
         <pageaction id="pageaction-charset" title="&pageactions.charEncoding;" onclick="CharsetMenu.show();"/>
         <pageaction id="pageaction-webapps-install" title="&pageactions.webapps.install;" onclick="WebappsUI.show();"/>
       </hbox>
     </arrowbox>
 
-    <arrowbox id="bookmark-popup" hidden="true" align="stretch" offset="12">
+    <arrowbox id="bookmark-popup" hidden="true" align="stretch" offset="12" type="dialog">
       <hbox id="bookmark-popup-title">
         <label value="&bookmarkPopup.label;"/>
       </hbox>
       <richlistbox id="bookmark-popup-commands" class="action-buttons" onclick="BookmarkPopup.hide();" flex="1">
         <richlistitem class="action-button" id="bookmark-popup-edit" onclick="BookmarkHelper.edit();">
           <label value="&bookmarkEdit.label;"/>
         </richlistitem>
         <richlistitem class="action-button" id="bookmark-popup-remove" onclick="BookmarkPopup.hide(); BookmarkHelper.removeBookmarksForURI(getBrowser().currentURI);">
@@ -386,17 +386,17 @@
         <separator id="bookmark-form-line" class="prompt-line"/>
         <scrollbox id="bookmark-form" align="start" class="prompt-message" flex="1"/>
         <hbox id="bookmark-form-buttons" pack="center" class="prompt-buttons">
           <button label="&editBookmarkDone.label;" class="prompt-button" oncommand="BookmarkHelper.save();"/>
         </hbox>
       </dialog>
     </box>
 
-    <arrowbox id="appmenu-popup" hidden="true">
+    <arrowbox id="appmenu-popup" hidden="true" type="dialog">
       <box id="appmenu-popup-sitecommands"/>
       <richlistbox id="appmenu-popup-appcommands"/>
     </arrowbox>
 
     <box id="panel-container" hidden="true" class="window-width window-height panel-dark"
          style="-moz-stack-sizing: ignore" left="10000">
       <box id="panel-container-inner" flex="1" class="panel-dark">
         <box id="panel-controls" class="panel-row-header" oncommand="BrowserUI.switchPane(event.target.getAttribute('linkedpanel'));">
--- a/mobile/chrome/content/common-ui.js
+++ b/mobile/chrome/content/common-ui.js
@@ -1344,54 +1344,31 @@ var SelectionHelper = {
     window.removeEventListener("keypress", this, true);
     Elements.browsers.removeEventListener("URLChanged", this, true);
     Elements.browsers.removeEventListener("SizeChanged", this, true);
     Elements.browsers.removeEventListener("ZoomChanged", this, true);
   },
 
   handleEvent: function handleEvent(aEvent) {
     switch (aEvent.type) {
-      case "PanBegin":
-        window.removeEventListener("PanBegin", this, true);
-        window.removeEventListener("TapUp", this, true);
-        window.addEventListener("PanFinished", this, true);
-        this._start.hidden = true;
-        this._end.hidden = true;
-        break;
-      case "PanFinished":
-        window.removeEventListener("PanFinished", this, true);
-        try {
-          this.popupState.target.messageManager.sendAsyncMessage("Browser:SelectionMeasure", {});
-        } catch (e) {
-          Cu.reportError(e);
-        }
-        break
       case "TapDown":
         if (aEvent.target == this._start || aEvent.target == this._end) {
           this.target = aEvent.target;
           this.deltaX = (aEvent.clientX - this.target.left);
           this.deltaY = (aEvent.clientY - this.target.top);
           window.addEventListener("TapMove", this, true);
         } else {
-          window.addEventListener("PanBegin", this, true);
-          window.addEventListener("TapUp", this, true);
-          this.target = null;
+          this.hide(aEvent);
         }
         break;
       case "TapUp":
-        if (this.target) {
-          window.removeEventListener("TapMove", this, true);
-          this.target = null;
-          this.deltaX = -1;
-          this.deltaY = -1;
-        } else {
-          window.removeEventListener("PanBegin", this, true);
-          window.removeEventListener("TapUp", this, true);
-          this.hide(aEvent);
-        }
+        window.removeEventListener("TapMove", this, true);
+        this.target = null;
+        this.deltaX = -1;
+        this.deltaY = -1;
         break;
       case "TapMove":
         if (this.target) {
           this.target.left = aEvent.clientX - this.deltaX;
           this.target.top = aEvent.clientY - this.deltaY;
           let rect = this.target.getBoundingClientRect();
           let data = this.target == this._start ? { x: rect.right, y: rect.top, type: "start" } : { x: rect.left, y: rect.top, type: "end" };
           let pos = this.popupState.target.transformClientToBrowser(data.x || 0, data.y || 0);
@@ -1401,24 +1378,16 @@ var SelectionHelper = {
             y: pos.y
           };
           this.popupState.target.messageManager.sendAsyncMessage("Browser:SelectionMove", json);
         }
         break;
       case "resize":
       case "SizeChanged":
       case "ZoomChanged":
-      {
-        try {
-          this.popupState.target.messageManager.sendAsyncMessage("Browser:SelectionMeasure", {});
-        } catch (e) {
-          Cu.reportError(e);
-        }
-        break        
-      }
       case "URLChanged":
       case "keypress":
         this.hide(aEvent);
         break;
     }
   },
 
   receiveMessage: function sh_receiveMessage(aMessage) {
--- a/mobile/chrome/content/content.js
+++ b/mobile/chrome/content/content.js
@@ -1272,16 +1272,17 @@ var TouchEventHandler = {
 
   receiveMessage: function(aMessage) {
     let json = aMessage.json;
     if (Util.isParentProcess())
       return;
 
     if (!content.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils).mayHaveTouchEventListeners) {
       sendAsyncMessage("Browser:CaptureEvents", {
+        type: null,
         messageId: json.messageId,
         click: false, panning: false,
         contentMightCaptureMouse: false
       });
       return;
     }
 
     let type;
@@ -1348,17 +1349,16 @@ var SelectionHandler = {
   cache: {},
   selectedText: "",
   contentWindow: null,
   
   init: function sh_init() {
     addMessageListener("Browser:SelectionStart", this);
     addMessageListener("Browser:SelectionEnd", this);
     addMessageListener("Browser:SelectionMove", this);
-    addMessageListener("Browser:SelectionMeasure", this);
   },
 
   getCurrentWindowAndOffset: function(x, y, offset) {
     let utils = Util.getWindowUtils(content);
     let elem = utils.elementFromPoint(x, y, true, false);
     while (elem && (elem instanceof HTMLIFrameElement || elem instanceof HTMLFrameElement)) {
       // adjust client coordinates' origin to be top left of iframe viewport
       let rect = elem.getBoundingClientRect();
@@ -1396,21 +1396,22 @@ var SelectionHandler = {
 
         let currentDocShell = contentWindow.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIWebNavigation).QueryInterface(Ci.nsIDocShell);
 
         // Remove any previous selected or created ranges. Tapping anywhere on a
         // page will create an empty range.
         let selection = contentWindow.getSelection();
         selection.removeAllRanges();
 
+          // Position the caret using a fake mouse click
+          utils.sendMouseEventToWindow("mousedown", x, y, 0, 1, 0, true);
+          utils.sendMouseEventToWindow("mouseup", x, y, 0, 1, 0, true);
+
         try {
-          let caretPos = contentWindow.document.caretPositionFromPoint(json.x - scrollOffset.x, json.y - scrollOffset.y);
           let selcon = currentDocShell.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsISelectionDisplay).QueryInterface(Ci.nsISelectionController);
-          let sel = selcon.getSelection(1);
-          sel.collapse(caretPos.offsetNode, caretPos.offset);
 
           // Select the word nearest the caret
           selcon.wordMove(false, false);
           selcon.wordMove(true, true);
         } catch(e) {
           // If we couldn't select the word at the given point, bail
           return;
         }
@@ -1457,83 +1458,56 @@ var SelectionHandler = {
           clipboard.copyString(this.selectedText);
           sendAsyncMessage("Browser:SelectionCopied", { succeeded: true });
         } else {
           sendAsyncMessage("Browser:SelectionCopied", { succeeded: false });
         }
         break;
       }
 
-      case "Browser:SelectionMove":
+      case "Browser:SelectionMove": {
         if (!this.contentWindow)
           return;
 
-        let x = json.x - scrollOffset.x;
-        let y = json.y - scrollOffset.y;
-
-        try {
-          let caretPos = this.contentWindow.document.caretPositionFromPoint(x, y);
-          if (caretPos.offsetNode == null ||
-              caretPos.offsetNode instanceof Ci.nsIDOMHTMLInputElement || 
-              caretPos.offsetNode instanceof Ci.nsIDOMHTMLTextAreaElement ||
-              caretPos.offsetNode.ownerDocument.defaultView != this.contentWindow)
-            return;
+        // Hack to avoid setting focus in a textbox [Bugs 654352 & 667243]
+        let elemUnder = elementFromPoint(json.x - scrollOffset.x, json.y - scrollOffset.y);
+        if (elemUnder && elemUnder instanceof Ci.nsIDOMHTMLInputElement || elemUnder instanceof Ci.nsIDOMHTMLTextAreaElement)
 
-          // Keep the cache in "client" coordinates
-          if (json.type == "end")
-            this.cache.end = { x: json.x, y: json.y };
-          else
-            this.cache.start = { x: json.x, y: json.y };
-
-          let currentDocShell = this.contentWindow.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIWebNavigation).QueryInterface(Ci.nsIDocShell);
-          let selcon = currentDocShell.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsISelectionDisplay).QueryInterface(Ci.nsISelectionController);
-          let sel = selcon.getSelection(1);
-          if (json.type != "end") {
-            let focusOffset = sel.focusOffset;
-            let focusNode = sel.focusNode;
-            sel.collapse(caretPos.offsetNode, caretPos.offset);
-            sel.extend(focusNode, focusOffset);
-          } else {
-            sel.extend(caretPos.offsetNode, caretPos.offset);
-          }
-        } catch(e) {
-          Cu.reportError(e);
+        // Limit the selection to the initial content window (don't leave or enter iframes)
+        if (elemUnder && elemUnder.ownerDocument.defaultView != this.contentWindow)
           return;
+        
+        // Use fake mouse events to update the selection
+        if (json.type == "end") {
+          // Keep the cache in "client" coordinates, but translate for the mouse event
+          this.cache.end = { x: json.x, y: json.y };
+          let end = { x: this.cache.end.x - scrollOffset.x, y: this.cache.end.y - scrollOffset.y };
+          utils.sendMouseEventToWindow("mousedown", end.x, end.y, 0, 1, Ci.nsIDOMNSEvent.SHIFT_MASK, true);
+          utils.sendMouseEventToWindow("mouseup", end.x, end.y, 0, 1, Ci.nsIDOMNSEvent.SHIFT_MASK, true);
+        } else {
+          // Keep the cache in "client" coordinates, but translate for the mouse event
+          this.cache.start = { x: json.x, y: json.y };
+          let start = { x: this.cache.start.x - scrollOffset.x, y: this.cache.start.y - scrollOffset.y };
+          let end = { x: this.cache.end.x - scrollOffset.x, y: this.cache.end.y - scrollOffset.y };
+        
+          utils.sendMouseEventToWindow("mousedown", start.x, start.y, 0, 0, 0, true);
+          utils.sendMouseEventToWindow("mouseup", start.x, start.y, 0, 0, 0, true);
+        
+          utils.sendMouseEventToWindow("mousedown", end.x, end.y, 0, 1, Ci.nsIDOMNSEvent.SHIFT_MASK, true);
+          utils.sendMouseEventToWindow("mouseup", end.x, end.y, 0, 1, Ci.nsIDOMNSEvent.SHIFT_MASK, true);
         }
 
         // Cache the selected text since the selection might be gone by the time we get the "end" message
         let selection = this.contentWindow.getSelection()
         this.selectedText = selection.toString().trim();
 
         // Update the rect we use to test when finishing the clipboard operation
         let range = selection.getRangeAt(0).QueryInterface(Ci.nsIDOMNSRange);
         this.cache.rect = this._extractFromRange(range, this.cache.offset).rect;
         break;
-      case "Browser:SelectionMeasure": {
-        if (!this.contentWindow)
-          return;
-
-        let selection = this.contentWindow.getSelection();
-        let range = selection.getRangeAt(0).QueryInterface(Ci.nsIDOMNSRange);
-        if (!range)
-          return;
-
-        // Cache the selected text since the selection might be gone by the time we get the "end" message
-        this.selectedText = selection.toString().trim();
-
-        // If the range didn't have any text, let's bail
-        if (!this.selectedText.length) {
-          selection.removeAllRanges();
-          return;
-        }
-
-        this.cache = this._extractFromRange(range, this.cache.offset);
-
-        sendAsyncMessage("Browser:SelectionRange", this.cache);
-        break;
       }
     }
   },
 
   _extractFromRange: function sh_extractFromRange(aRange, aOffset) {
     let cache = { start: {}, end: {}, rect: { left: Number.MAX_VALUE, top: Number.MAX_VALUE, right: 0, bottom: 0 } };
     let rects = aRange.getClientRects();
     for (let i=0; i<rects.length; i++) {
--- a/mobile/chrome/content/downloads.js
+++ b/mobile/chrome/content/downloads.js
@@ -345,17 +345,17 @@ var DownloadsView = {
     // Figure out if the end time is from today, yesterday, this week, etc.
     let dateTime;
     if (end >= today) {
       // Download finished after today started, show the time
       dateTime = dts.FormatTime("", dts.timeFormatNoSeconds, end.getHours(), end.getMinutes(), 0);
     }
     else if (today - end < (24 * 60 * 60 * 1000)) {
       // Download finished after yesterday started, show yesterday
-      dateTime = strings.GetStringFromName("donwloadsYesterday");
+      dateTime = strings.GetStringFromName("downloadsYesterday");
     }
     else if (today - end < (6 * 24 * 60 * 60 * 1000)) {
       // Download finished after last week started, show day of week
       dateTime = end.toLocaleFormat("%A");
     }
     else {
       // Download must have been from some time ago.. show month/day
       let month = end.toLocaleFormat("%B");
--- a/mobile/chrome/content/extensions.js
+++ b/mobile/chrome/content/extensions.js
@@ -216,27 +216,16 @@ var ExtensionsView = {
 
     this._dloadmgr = new AddonInstallListener();
     AddonManager.addInstallListener(this._dloadmgr);
 
     // Watch for add-on update notifications
     let os = Services.obs;
     os.addObserver(this, "addon-update-started", false);
     os.addObserver(this, "addon-update-ended", false);
-
-    if (!Services.prefs.getBoolPref("extensions.hideUpdateButton"))
-      document.getElementById("addons-update-all").hidden = false;
-
-#ifdef ANDROID
-    // Hide the notification
-    let alertsService = Cc["@mozilla.org/alerts-service;1"].getService(Ci.nsIAlertsService);
-    let progressListener = alertsService.QueryInterface(Ci.nsIAlertsProgressListener);
-    if (progressListener)
-      progressListener.onCancel(ADDONS_NOTIFICATION_NAME);
-#endif
   },
 
   delayedInit: function ev__delayedInit() {
     if (this._list)
       return;
 
     this.init(); // In case the panel is selected before init has been called.
 
@@ -254,29 +243,34 @@ var ExtensionsView = {
     }
 
     let strings = Strings.browser;
     this._strings["addonType.extension"] = strings.GetStringFromName("addonType.2");
     this._strings["addonType.theme"] = strings.GetStringFromName("addonType.4");
     this._strings["addonType.locale"] = strings.GetStringFromName("addonType.8");
     this._strings["addonType.search"] = strings.GetStringFromName("addonType.1024");
 
+    if (!Services.prefs.getBoolPref("extensions.hideUpdateButton"))
+      document.getElementById("addons-update-all").hidden = false;
+
     let self = this;
     setTimeout(function() {
       self.getAddonsFromLocal();
       self.getAddonsFromRepo("");
     }, 0);
   },
 
   uninit: function ev_uninit() {
     let os = Services.obs;
     os.removeObserver(this, "addon-update-started");
     os.removeObserver(this, "addon-update-ended");
 
     AddonManager.removeInstallListener(this._dloadmgr);
+
+    this.hideAlerts();
   },
 
   hideOnSelect: function ev_handleEvent(aEvent) {
     // When list selection changes, be sure to close up any open options sections
     if (aEvent.target == this._list)
       this.hideOptions();
   },
 
@@ -835,26 +829,34 @@ var ExtensionsView = {
       else
         toaster.showAlertNotification(image, strings.GetStringFromName("alertAddons"), aMessage, true, "", observer);
     } else {
       // Only show an alert for a normal add-on if the manager is not visible
       if (!this.visible) {
         let alerts = Cc["@mozilla.org/alerts-service;1"].getService(Ci.nsIAlertsService);
         alerts.showAlertNotification(URI_GENERIC_ICON_XPINSTALL, strings.GetStringFromName("alertAddons"),
                                      aMessage, true, "", observer, ADDONS_NOTIFICATION_NAME);
+
+        // Use a preference to help us cleanup this notification in case we don't shutdown correctly
+        Services.prefs.setBoolPref("browser.notifications.pending.addons", true);
+        Services.prefs.savePrefFile(null);
       }
     }
   },
 
   hideAlerts: function ev_hideAlerts() {
 #ifdef ANDROID
     let alertsService = Cc["@mozilla.org/alerts-service;1"].getService(Ci.nsIAlertsService);
     let progressListener = alertsService.QueryInterface(Ci.nsIAlertsProgressListener);
-    progressListener.onCancel(ADDONS_NOTIFICATION_NAME);
+    if (progressListener)
+      progressListener.onCancel(ADDONS_NOTIFICATION_NAME);
 #endif
+
+    // Keep our preference in sync
+    Services.prefs.clearUserPref("browser.notifications.pending.addons");
   },
 };
 
 
 function searchFailed() {
   ExtensionsView.clearSection("repo");
 
   let strings = Strings.browser;
@@ -994,18 +996,20 @@ AddonInstallListener.prototype = {
   },
 
   onDownloadProgress: function xpidm_onDownloadProgress(aInstall) {
     let element = ExtensionsView.getElementForAddon(aInstall.sourceURI.spec);
 
 #ifdef ANDROID
     let alertsService = Cc["@mozilla.org/alerts-service;1"].getService(Ci.nsIAlertsService);
     let progressListener = alertsService.QueryInterface(Ci.nsIAlertsProgressListener);
-    progressListener.onProgress(ADDONS_NOTIFICATION_NAME, aInstall.progress, aInstall.maxProgress);
+    if (progressListener)
+      progressListener.onProgress(ADDONS_NOTIFICATION_NAME, aInstall.progress, aInstall.maxProgress);
 #endif
+
     if (!element)
       return;
 
     let opType = element.getAttribute("opType");
     if (!opType)
       element.setAttribute("opType", "needs-install");
 
     let progress = Math.round((aInstall.progress / aInstall.maxProgress) * 100);
--- a/mobile/chrome/content/input.js
+++ b/mobile/chrome/content/input.js
@@ -501,28 +501,21 @@ MouseModule.prototype = {
       this._dispatchTap("TapDouble", mouseUp1);
     } else {
       this._dispatchTap("TapSingle", mouseUp1);
       this._dispatchTap("TapSingle", mouseUp2);
     }
   },
 
   _dispatchTap: function _dispatchTap(aType, aMouseEvent) {
-    // borrowed from nsIDOMNSEvent.idl
-    let modifiers =
-      (aMouseEvent.altKey   ? Ci.nsIDOMNSEvent.ALT_MASK     : 0) |
-      (aMouseEvent.ctrlKey  ? Ci.nsIDOMNSEvent.CONTROL_MASK : 0) |
-      (aMouseEvent.shiftKey ? Ci.nsIDOMNSEvent.SHIFT_MASK   : 0) |
-      (aMouseEvent.metaKey  ? Ci.nsIDOMNSEvent.META_MASK    : 0);
-
     let event = document.createEvent("Events");
     event.initEvent(aType, true, false);
     event.clientX = aMouseEvent.clientX;
     event.clientY = aMouseEvent.clientY;
-    event.modifiers = modifiers;
+    event.modifiers = Util.modifierMaskFromEvent(aMouseEvent);
     aMouseEvent.originalTarget.dispatchEvent(event);
   },
 
   /**
    * Record a mousedown/mouseup event for later redispatch via
    * _redispatchDownUpEvents()
    */
   _recordEvent: function _recordEvent(aEvent) {
@@ -1211,37 +1204,38 @@ GestureModule.prototype = {
 
   _pinchUpdate: function _pinchUpdate(aEvent) {
     if (!AnimatedZoom.isZooming() || !aEvent.delta)
       return;
 
     let delta = 0;
     let browser = AnimatedZoom.browser;
     let oldScale = browser.scale;
+    let bcr = this._browserBCR;
 
     // Accumulate pinch delta. Small changes are just jitter.
     this._pinchDelta += aEvent.delta;
     if (Math.abs(this._pinchDelta) >= oldScale) {
       delta = this._pinchDelta;
       this._pinchDelta = 0;
     }
 
     // decrease the pinchDelta min/max values to limit zooming out/in speed
     delta = Util.clamp(delta, -this._maxShrink, this._maxGrowth);
 
     let newScale = Browser.selectedTab.clampZoomLevel(oldScale * (1 + delta / this._scalingFactor));
     let startScale = AnimatedZoom.startScale;
     let scaleRatio = startScale / newScale;
-    let cX = aEvent.clientX - this._browserBCR.left;
-    let cY = aEvent.clientY - this._browserBCR.top;
+    let cX = aEvent.clientX - bcr.left;
+    let cY = aEvent.clientY - bcr.top;
 
     // Calculate the new zoom rect.
     let rect = AnimatedZoom.zoomFrom.clone();
-    rect.translate(this._pinchStartX - cX + (1-scaleRatio) * cX * rect.width / window.innerWidth,
-                   this._pinchStartY - cY + (1-scaleRatio) * cY * rect.height / window.innerHeight);
+    rect.translate(this._pinchStartX - cX + (1-scaleRatio) * cX * rect.width / bcr.width,
+                   this._pinchStartY - cY + (1-scaleRatio) * cY * rect.height / bcr.height);
 
     rect.width *= scaleRatio;
     rect.height *= scaleRatio;
 
     this.translateInside(rect, new Rect(0, 0, browser.contentDocumentWidth * startScale,
                                               browser.contentDocumentHeight * startScale));
 
     // redraw zoom canvas according to new zoom rect
--- a/mobile/chrome/content/tabs.xml
+++ b/mobile/chrome/content/tabs.xml
@@ -157,17 +157,17 @@
           ]]>
         </body>
       </method>
     </implementation>
   </binding>
 
   <binding id="tablist">
     <content>
-      <xul:scrollbox anonid="tabs-scrollbox" flex="1">
+      <xul:scrollbox anonid="tabs-scrollbox" class="tabs-scrollbox" flex="1">
         <xul:vbox class="tabs-list" anonid="tabs-children" />
       </xul:scrollbox>
       <xul:box class="tabs-list" anonid="tabs-undo"/>
     </content>
     <implementation>
       <field name="children">document.getAnonymousElementByAttribute(this, "anonid", "tabs-children");</field>
       <field name="_scrollbox">document.getAnonymousElementByAttribute(this, "anonid", "tabs-scrollbox");</field>
       <field name="_tabsUndo">document.getAnonymousElementByAttribute(this, "anonid", "tabs-undo");</field>
@@ -280,17 +280,17 @@
             // XXX we can do better than using a constant
             const COLUMN_MARGIN = 20;
             let firstBox = this.children.firstChild.getBoundingClientRect();
             let lastBox = this.children.lastChild.getBoundingClientRect();
 
             // We can't rely on getBoundingClientRect() for this.children height
             // it is not synced (sometimes, especially during resize) with the 
             // style.height rule
-            let columnsCount = Math.ceil(this.children.childNodes.length / Math.floor(parseInt(this.children.style.height) / (firstBox.height + 4)));
+            let columnsCount = Util.isTablet() ? 1 : Math.ceil(this.children.childNodes.length / Math.floor(parseInt(this.children.style.height) / (firstBox.height + 4)));
             if (this._columnsCount != columnsCount && window.innerWidth > 1) { // > 1 to ignore column resizing while the main window is loading
               let width = columnsCount * (COLUMN_MARGIN + firstBox.width);
               this.children.style.width = width + "px";
 
               // Clamp the sidebar width so it won't overflow the window. Only clamp
               // the scrollbox. The children need to be the full width.
               if (width > window.innerWidth - firstBox.width)
                 width = window.innerWidth - firstBox.width;
--- a/mobile/components/BrowserCLH.js
+++ b/mobile/components/BrowserCLH.js
@@ -121,17 +121,17 @@ function showPanelWhenReady(aWindow, aPa
 function haveSystemLocale() {
   let localeService = Cc["@mozilla.org/intl/nslocaleservice;1"].getService(Ci.nsILocaleService);
   let systemLocale = localeService.getSystemLocale().getCategory("NSILOCALE_CTYPE");
   return isLocaleAvailable(systemLocale);
 }
 
 function checkCurrentLocale() {
   if (Services.prefs.prefHasUserValue("general.useragent.locale")) {
-    // if the user has a compatable locale from a different buildid, we need to update
+    // if the user has a compatible locale from a different buildid, we need to update
     var buildID = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULAppInfo).appBuildID;
     let localeBuildID = Services.prefs.getCharPref("extensions.compatability.locales.buildid");
     if (buildID != localeBuildID)
       return false;
 
     let currentLocale = Services.prefs.getCharPref("general.useragent.locale");
     return isLocaleAvailable(currentLocale);
   }
--- a/mobile/components/BrowserStartup.js
+++ b/mobile/components/BrowserStartup.js
@@ -33,16 +33,19 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cu = Components.utils;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
+
+const ADDONS_NOTIFICATION_NAME = "addons";
 
 // Custom factory object to ensure that we're a singleton
 const BrowserStartupServiceFactory = {
   _instance: null,
   createInstance: function (outer, iid) {
     if (outer != null)
       throw Components.results.NS_ERROR_NO_AGGREGATION;
     return this._instance || (this._instance = new BrowserStartup());
@@ -50,29 +53,28 @@ const BrowserStartupServiceFactory = {
 };
 
 function BrowserStartup() {
   this._init();
 }
 
 BrowserStartup.prototype = {
   // for XPCOM
-  classID:          Components.ID("{1d542abc-c88b-4636-a4ef-075b49806317}"),
+  classID: Components.ID("{1d542abc-c88b-4636-a4ef-075b49806317}"),
 
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver, Ci.nsISupportsWeakReference]),
 
   _xpcom_factory: BrowserStartupServiceFactory,
 
-  _init: function () {
-    this._observerService = Cc["@mozilla.org/observer-service;1"].
-                            getService(Ci.nsIObserverService);
-    this._observerService.addObserver(this, "places-init-complete", false);
+  _init: function() {
+    Services.obs.addObserver(this, "places-init-complete", false);
+    Services.obs.addObserver(this, "final-ui-startup", false);
   },
 
-  _initDefaultBookmarks: function () {
+  _initDefaultBookmarks: function() {
     // We must instantiate the history service since it will tell us if we
     // need to import or restore bookmarks due to first-run, corruption or
     // forced migration (due to a major schema change).
     let histsvc = Cc["@mozilla.org/browser/nav-history-service;1"].
                   getService(Ci.nsINavHistoryService);
 
     // If the database is corrupt or has been newly created we should
     // import bookmarks.
@@ -123,20 +125,39 @@ BrowserStartup.prototype = {
       sl.init(observer);
       channel.asyncOpen(sl, channel);
     } catch (err) {
       // Report the error, but ignore it.
       Cu.reportError("Failed to load default bookmarks from bookmarks.json: " + err);
     }
   },
 
+  _startupActions: function() {
+#ifdef ANDROID
+    // Hide the notification if we had any pending operations
+    try {
+      if (Services.prefs.getBoolPref("browser.notifications.pending.addons")) {
+        Services.prefs.clearUserPref("browser.notifications.pending.addons")
+        let alertsService = Cc["@mozilla.org/alerts-service;1"].getService(Ci.nsIAlertsService);
+        let progressListener = alertsService.QueryInterface(Ci.nsIAlertsProgressListener);
+        if (progressListener)
+          progressListener.onCancel(ADDONS_NOTIFICATION_NAME);
+      }
+    } catch (e) {}
+#endif
+  },
+
   // nsIObserver
   observe: function(aSubject, aTopic, aData) {
     switch (aTopic) {
       case "places-init-complete":
+        Services.obs.removeObserver(this, "places-init-complete");
         this._initDefaultBookmarks();
-        this._observerService.removeObserver(this, "places-init-complete");
+        break;
+      case "final-ui-startup":
+        Services.obs.removeObserver(this, "final-ui-startup");
+        this._startupActions();
         break;
     }
   }
 };
 
 const NSGetFactory = XPCOMUtils.generateNSGetFactory([BrowserStartup]);
--- a/mobile/components/Makefile.in
+++ b/mobile/components/Makefile.in
@@ -51,25 +51,25 @@ XPIDLSRCS = \
         LoginManagerPrompter.idl \
         WebappsSupport.idl \
         $(NULL)
 
 EXTRA_PP_COMPONENTS = \
         MobileComponents.manifest \
         AboutRedirector.js \
         BrowserCLH.js \
+        BrowserStartup.js \
         DirectoryProvider.js\
         HelperAppDialog.js \
         Sidebar.js \
         SessionStore.js \
         $(NULL)
 
 EXTRA_COMPONENTS = \
         AlertsService.js \
-        BrowserStartup.js \
         ContentPermissionPrompt.js \
         XPIDialogService.js \
         DownloadManagerUI.js \
         PromptService.js \
         ContentDispatchChooser.js \
         AutoCompleteCache.js \
         AddonUpdateService.js \
         FormAutoComplete.js \
--- a/mobile/locales/en-US/chrome/browser.properties
+++ b/mobile/locales/en-US/chrome/browser.properties
@@ -73,17 +73,17 @@ addonErrorBlocklisted=#1 could not be in
 # LOCALIZATION NOTE (Status): — is the "em dash" (long dash)
 # #1 download size for FINISHED or download state; #2 host (e.g., eTLD + 1, IP)
 downloadsStatus=#1 — #2
 # LOCALIZATION NOTE (Time): #1 left time for UNFINISHED, total time for FINISHED
 downloadsTime= — #1
 downloadsUnknownSize=Unknown size
 # LOCALIZATION NOTE (KnownSize): #1 size number; #2 size unit
 downloadsKnownSize=#1 #2
-donwloadsYesterday=Yesterday
+downloadsYesterday=Yesterday
 # LOCALIZATION NOTE (MonthDate): #1 month name; #2 date number; e.g., January 22
 downloadsMonthDate=#1 #2
 downloadsEmpty=No downloads
 downloadsDeleteTitle=Delete File
 
 # Alerts
 alertAddons=Add-ons
 alertAddonsDownloading=Downloading add-on
--- a/mobile/themes/core/browser.css
+++ b/mobile/themes/core/browser.css
@@ -924,17 +924,17 @@ autocompleteresult.noresults > .autocomp
   -moz-border-end: @border_width_large@ solid #262629;
   background: #5e6166;
 }
 
 #tabs:-moz-locale-dir(rtl) {
   -moz-box-align: start;
 }
 
-#tabs > scrollbox {
+#tabs > .tabs-scrollbox {
   max-width: 350px;
 }
 
 #tabs .tabs-list {
   display: block;
   -moz-column-width: 121px;
   -moz-column-gap: 0;
   -moz-user-focus: ignore;
--- a/mobile/themes/core/gingerbread/browser.css
+++ b/mobile/themes/core/gingerbread/browser.css
@@ -874,17 +874,17 @@ autocompleteresult.noresults > .autocomp
   -moz-border-end: @border_width_large@ solid #262629;
   background: @color_background_default@;
 }
 
 #tabs:-moz-locale-dir(rtl) {
   -moz-box-align: start;
 }
 
-#tabs > scrollbox {
+#tabs > .tabs-scrollbox {
   max-width: 350px;
 }
 
 #tabs .tabs-list {
   display: block;
   -moz-column-width: 121px;
   -moz-column-gap: 0;
   -moz-user-focus: ignore;
index 478e9c85e270e51428f181c423d04d5aa448eadb..1767f2db21d9fdcfda14aa9e474b8dea4be0a17f
GIT binary patch
literal 1589
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
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..f8017dcd6090938e4da76f3ec888446c70060718
GIT binary patch
literal 4600
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
G`TqcGR=q0#
--- a/mobile/themes/core/honeycomb/browser.css
+++ b/mobile/themes/core/honeycomb/browser.css
@@ -958,17 +958,17 @@ autocompleteresult.noresults > .autocomp
   -moz-border-end: @border_width_large@ solid #262629;
   background: @color_toolbar_background@;
 }
 
 #tabs:-moz-locale-dir(rtl) {
   -moz-box-align: start;
 }
 
-#tabs > scrollbox {
+#tabs > .tabs-scrollbox {
   max-width: 350px;
 }
 
 #tabs .tabs-list {
   display: block;
   -moz-column-width: 121px;
   -moz-column-gap: 0;
   -moz-user-focus: ignore;
index 478e9c85e270e51428f181c423d04d5aa448eadb..1767f2db21d9fdcfda14aa9e474b8dea4be0a17f
GIT binary patch
literal 1589
zc$@(>2Fm$~P)<h;3K|Lk000e1NJLTq001cf004Ce1^@s6(=5n(0000PbVXQnQ*UN;
zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU%*GWV{RCwC#oLx*~RS<xC+uPfpTS{95
zacL8N!r#goC}qK@uu)b`F+mGxBEgk_BoM@~F28HCXo!&)2=KyB0&Ij=@W~fm2n}xn
z#y4UjkQjm}K|~jj((9S#o_cJGur9P?a&Iy@p`p#!nK?6aPUjp>k|Y(!ajNs4ot+(3
zB%g$Ygdh~JLioslxFKagDiT&mc+|4^d8DQWIE_3?YNV#tQ1N=b={#ac0scr#Of0|%
z34mG|(y!O+i+P0pNVTXVT9gDqc#}t{MF2Dq(T|Uh7m-9`<q>I7EyryDG=j-<a&kUE
z6U-Si>xqZ}IWaNuk|>HV(twF2F6xMwh{R-PXWL3kOYgB!9Rna}FosAhfX>X!+$Xcz
z9`)QU`=CbFAOKnba&d9-uHA0G6`CI-u00}j7K<{R&FOSHR#sNN$jZukEA%~lgf&NJ
zuppZle9+v}(^FJiTl>IZFeHY1FMq}U#uf{_oJn+Ym=U|XyYnh5E8i#PedXUii@#(b
zjw!J;A#efh6&Dvj)zHvzD?dNKA}%h@b&=2KF9A#w0z`9f04^mZMQm$p%g@Nj$W2O0
zdfsNU-6UD$vE~B*1SX^!qFM@!ot>R&g@uJqhr^LaG?hyNa>f&vMHgI%v0BqIjvtVr
zp`oIJf`XTcZ@sG1>7Kr9|GtPOSvWm)`pnEs%fP_E+a$kMlfe7TRYr#MAgNhtPBjs~
zvXH!bhrqq3)oNoR5x)3y00p@s0T^>fM@L>`V`Hz;Xmms}0r2MligG>$?UlRT?rybO
z9r0Akp8=?qH3%uoIyyS~Mp;?eCyyICE^}=gZju<N28V}-UoS5&zaK5YaFDE-%_QDk
z^LRY%(J=8@keqCOI#Eo6gM%MNY6f2vlAX<_uCA{9)YR15SoT|3U)0HvhN`NncQ9=V
zv8E1`Q7)G&js)!@%#cETef`a7&gQcq*{X&oCH!?vI|k}7MT90kK0X;MWU!hhHDDrE
zND9gcf?&c3$%1kkjmCf#l7gx*&FpZRWJk1`MENL?3Zw$5Kq`<5qynixDv%1K0;xbM
zkP4&%sX!`_3Zw$5Kq`<5WRxH!%#hOmMUwz3`Fy@3%$oGW==b{%F+x(n0OaA};T}dv
zfIB7PgK>X<|3|Ev1js)DbaQia4XY-7GUUnp{QNhVAZh<3{OAe&7tmD&{g1Zw_4O}B
zQM6+-H)!&h$Rik6S69Er7=rw=xdHM~S6A0pM@L7y7~|xL40*7&wzj*lurP_qCs}A{
z1v7(Rf-EmDf9`NNN-(8GzpTwai0p$@R8)Mnx3{;336g@s1UewHx3RJDQ(ax%N4vYb
z-(fL#@O9PShj^g<CN?cCEq;1>`W_j}u|Tq*wCOW&pl4)c<hI-G{`jifwl4}vL1~XK
zZCf<j?RLx1(2&RJbiQje8k3_9$@(q}OPk^iQ&Ur=E|;s^X0sLYJTFEQGH|{-%$XW0
zy5!_!vA4IkBr`L!$YQbFFq_RcE~^QO`!9#kLk{J#r$&P9jaIAGK%kujI+y(aa|HZZ
z_T1N%L$VJ8%!-DA4xVIcYHCWWtgI|cN=kZxe2LtML7o8}3jU9SLooSTT3W8x)YR0H
zZ1&oDE#ll&-#Ku`86FxbOhmIO?d|PN#HZa+gFM4EX#Ax;?Xi7*eI?b^)ei(gh>a>_
z7+9?w%AlaJdOV(t{{H>}5__*jeVc`RVTlM`Q$aEFE7S?Iv$Nl~wzl3qK0f{h&0Y`z
zrBxTK^M8Y?Yi@4thsnvwPl<(3qH69i=BK6yv&X@>w6xTeo}PXO&8`}wQCJoH4r6n3
z^VsR>>Cf1<{v>vdMHYg4+uPd@Q9;6FVm2tiM~}zG#-`yl4$}oPa%iIUD}*NB*4EY>
zs-4~tZNd`9N0!xd81KPgw|uyRgM$@}kW`y6+tpsL_gfC*rAG2edU}D(Db}lag1gQt
n#P<bJNc5K)(O*NPzXTWnPYtsajQjO!00000NkvXXu0mjfzbft4
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..f8017dcd6090938e4da76f3ec888446c70060718
GIT binary patch
literal 4600
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
G`TqcGR=q0#
--- a/mobile/themes/core/honeycomb/platform.css
+++ b/mobile/themes/core/honeycomb/platform.css
@@ -627,16 +627,37 @@ dialog {
 }
 
 .prompt-edit {
   margin: @margin_xnormal@;
   font-size: @font_normal@;
   text-align: start;
 }
 
+.panel-arrow[side="top"]:not([type="dialog"]) {
+  list-style-image: url("chrome://browser/skin/images/arrowbox-up.png");
+  margin-bottom: -@margin_normal@;
+}
+
+.panel-arrow[side="bottom"]:not([type="dialog"]) {
+  list-style-image: url("chrome://browser/skin/images/arrowbox-down.png");
+  margin-top: -@margin_normal@;
+}
+
+.panel-arrow[side="left"]:not([type="dialog"]) {
+  list-style-image: url("chrome://browser/skin/images/arrowbox-horiz.png");
+  margin-right: -@margin_normal@;
+  -moz-transform: scaleX(-1);
+}
+
+.panel-arrow[side="right"]:not([type="dialog"]) {
+  list-style-image: url("chrome://browser/skin/images/arrowbox-horiz.png");
+  margin-left: -@margin_normal@;
+}
+
 /*.panel-row-header ------------------------------------------------------------ */
 .panel-row-header {
   background-color: @color_background_panelrow@ !important;
   color: @color_text_panelrow@;
   padding: 0 !important;
 }
 
 .panel-row-button {
index 478e9c85e270e51428f181c423d04d5aa448eadb..1767f2db21d9fdcfda14aa9e474b8dea4be0a17f
GIT binary patch
literal 1589
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
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..f8017dcd6090938e4da76f3ec888446c70060718
GIT binary patch
literal 4600
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
G`TqcGR=q0#
--- a/mobile/themes/core/jar.mn
+++ b/mobile/themes/core/jar.mn
@@ -90,16 +90,17 @@ chrome.jar:
   skin/images/row-header-bg.png             (images/row-header-bg.png)
   skin/images/console-default-hdpi.png        (images/console-default-hdpi.png)
   skin/images/newtab-default-hdpi.png         (images/newtab-default-hdpi.png)
   skin/images/newtab-default-tablet-hdpi.png (images/newtab-default-tablet-hdpi.png)
   skin/images/tab-active-hdpi.png           (images/tab-active-hdpi.png)
   skin/images/tab-inactive-hdpi.png         (images/tab-inactive-hdpi.png)
   skin/images/tab-closed-hdpi.png           (images/tab-closed-hdpi.png)
   skin/images/tab-reopen-hdpi.png           (images/tab-reopen-hdpi.png)
+  skin/images/tab-reopen-tablet-hdpi.png    (images/tab-reopen-tablet-hdpi.png)
   skin/images/remotetabs-48.png             (images/remotetabs-48.png)
   skin/images/remotetabs-32.png             (images/remotetabs-32.png)
   skin/images/mozilla-32.png                (images/mozilla-32.png)
   skin/images/toggle-on.png                 (images/toggle-on.png)
   skin/images/toggle-off.png                (images/toggle-off.png)
   skin/images/sidebarbutton-active-hdpi.png (images/sidebarbutton-active-hdpi.png)
   skin/images/previous-hdpi.png             (images/previous-hdpi.png)
   skin/images/previous-disabled-hdpi.png    (images/previous-disabled-hdpi.png)
@@ -219,16 +220,17 @@ chrome.jar:
   skin/gingerbread/images/row-header-bg.png             (gingerbread/images/row-header-bg.png)
   skin/gingerbread/images/console-default-hdpi.png        (gingerbread/images/console-default-hdpi.png)
   skin/gingerbread/images/newtab-default-hdpi.png         (gingerbread/images/newtab-default-hdpi.png)
   skin/gingerbread/images/newtab-default-tablet-hdpi.png (gingerbread/images/newtab-default-tablet-hdpi.png)
   skin/gingerbread/images/tab-active-hdpi.png           (gingerbread/images/tab-active-hdpi.png)
   skin/gingerbread/images/tab-inactive-hdpi.png         (gingerbread/images/tab-inactive-hdpi.png)
   skin/gingerbread/images/tab-closed-hdpi.png           (gingerbread/images/tab-closed-hdpi.png)
   skin/gingerbread/images/tab-reopen-hdpi.png           (gingerbread/images/tab-reopen-hdpi.png)
+  skin/gingerbread/images/tab-reopen-tablet-hdpi.png    (gingerbread/images/tab-reopen-tablet-hdpi.png)
   skin/gingerbread/images/remotetabs-48.png             (gingerbread/images/remotetabs-48.png)
   skin/gingerbread/images/remotetabs-32.png             (gingerbread/images/remotetabs-32.png)
   skin/gingerbread/images/mozilla-32.png                (images/mozilla-32.png)
   skin/gingerbread/images/toggle-on.png                 (gingerbread/images/toggle-on.png)
   skin/gingerbread/images/toggle-off.png                (gingerbread/images/toggle-off.png)
   skin/gingerbread/images/previous-hdpi.png             (gingerbread/images/previous-hdpi.png)
   skin/gingerbread/images/previous-disabled-hdpi.png    (gingerbread/images/previous-disabled-hdpi.png)
   skin/gingerbread/images/next-hdpi.png                 (gingerbread/images/next-hdpi.png)
@@ -349,16 +351,17 @@ chrome.jar:
   skin/honeycomb/images/row-header-bg.png             (honeycomb/images/row-header-bg.png)
   skin/honeycomb/images/console-default-hdpi.png        (honeycomb/images/console-default-hdpi.png)
   skin/honeycomb/images/newtab-default-hdpi.png         (honeycomb/images/newtab-default-hdpi.png)
   skin/honeycomb/images/newtab-default-tablet-hdpi.png (honeycomb/images/newtab-default-tablet-hdpi.png)
   skin/honeycomb/images/tab-active-hdpi.png           (honeycomb/images/tab-active-hdpi.png)
   skin/honeycomb/images/tab-inactive-hdpi.png         (honeycomb/images/tab-inactive-hdpi.png)
   skin/honeycomb/images/tab-closed-hdpi.png           (honeycomb/images/tab-closed-hdpi.png)
   skin/honeycomb/images/tab-reopen-hdpi.png           (honeycomb/images/tab-reopen-hdpi.png)
+  skin/honeycomb/images/tab-reopen-tablet-hdpi.png    (honeycomb/images/tab-reopen-tablet-hdpi.png)
   skin/honeycomb/images/remotetabs-48.png             (honeycomb/images/remotetabs-48.png)
   skin/honeycomb/images/remotetabs-32.png             (honeycomb/images/remotetabs-32.png)
   skin/honeycomb/images/mozilla-32.png                (images/mozilla-32.png)
   skin/honeycomb/images/toggle-on.png                 (honeycomb/images/toggle-on.png)
   skin/honeycomb/images/toggle-off.png                (honeycomb/images/toggle-off.png)
   skin/honeycomb/images/sidebarbutton-active-hdpi.png (honeycomb/images/sidebarbutton-active-hdpi.png)
   skin/honeycomb/images/previous-hdpi.png             (honeycomb/images/previous-hdpi.png)
   skin/honeycomb/images/previous-disabled-hdpi.png    (honeycomb/images/previous-disabled-hdpi.png)
--- a/mobile/themes/core/tablet.css
+++ b/mobile/themes/core/tablet.css
@@ -30,29 +30,35 @@
   visibility: collapse;
 }
 
 #tabs-container[tablet="true"] {
   -moz-border-end: 0px;
   background: #000;
 }
 
-#tabs[tablet="true"] > scrollbox {
+#tabs[tablet="true"] > .tabs-scrollbox {
   max-width: 200px;
+  -moz-box-orient: vertical;
 }
 
 #tabs[tablet="true"] .tabs-list {
-  -moz-column-width: 200px;
+}
+
+#tabs[tablet="true"] .tabs-list {
+  width: 200px;
+  -moz-column-width: auto;
   -moz-padding-end: 0px;
   -moz-padding-start: 0px;
 }
 
 documenttab[tablet="true"] {
+  /* Use px instead of mozmm because tab bar items have hard-coded pixel sizes */
   width: 200px;
-  padding: @padding_xxnormal@;
+  padding: 12px;
   -moz-margin-start: 0px;
   background: none;
 }
 
 documenttab[tablet="true"][selected="true"] {
   background: none;
 }
 
@@ -67,17 +73,17 @@ documenttab[tablet="true"][selected="tru
   -moz-box-pack: end;
   -moz-box-align: center;
 }
 
 documenttab[selected="true"] > stack > .documenttab-close-container[tablet="true"] {
   background: url("chrome://browser/skin/images/close-background-hdpi.png");
   background-repeat: no-repeat;
   background-position: right;
-  -moz-margin-end: -@padding_xxnormal@;
+  -moz-margin-end: -12px;
   padding-right: 2px;
 }
 
 .documenttab-close[tablet="true"] {
   width: 30px;
   height: 44px;
   list-style-image: url("chrome://browser/skin/images/close-inactive-tab-tablet-hdpi.png");
 }
@@ -85,16 +91,20 @@ documenttab[selected="true"] > stack > .
 documenttab[selected="true"] > stack > hbox > .documenttab-close[tablet="true"] {
   list-style-image: url("chrome://browser/skin/images/close-default-tablet-hdpi.png");
 }
 
 .documenttab-close-container:hover:active > .documenttab-close[tablet="true"] {
   list-style-image: url("chrome://browser/skin/images/close-active-tablet-hdpi.png");
 }
 
+.documenttab-reload[tablet="true"] {
+  background: url("chrome://browser/skin/images/tab-reopen-tablet-hdpi.png");
+}
+
 #newtab-button[tablet="true"] {
   list-style-image: url("images/newtab-default-tablet-hdpi.png");
 }
 
 @media (@orientation@: portrait) {
   #toolbar-main[tablet="true"] > #tool-tabs {
     visibility: visible;
   }
--- a/modules/libpr0n/src/Makefile.in
+++ b/modules/libpr0n/src/Makefile.in
@@ -73,13 +73,14 @@ LOCAL_INCLUDES += \
 			-I$(topsrcdir)/content/svg/content/src \
 			-I$(topsrcdir)/content/base/src \
 			$(NULL)
 
 # Because VectorImage.cpp includes nsSVGUtils.h and nsSVGEffects.h
 LOCAL_INCLUDES += -I$(topsrcdir)/layout/svg/base/src
 
 include $(topsrcdir)/config/rules.mk
+include $(topsrcdir)/ipc/chromium/chromium-config.mk
 
 # Because imgFrame.cpp includes "cairo.h"
 CXXFLAGS += $(MOZ_CAIRO_CFLAGS)
 
 
--- a/modules/libpr0n/src/RasterImage.cpp
+++ b/modules/libpr0n/src/RasterImage.cpp
@@ -37,16 +37,17 @@
  * use your version of this file under the terms of the MPL, indicate your
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
+#include "base/histogram.h"
 #include "nsComponentManagerUtils.h"
 #include "imgIContainerObserver.h"
 #include "ImageErrors.h"
 #include "Decoder.h"
 #include "imgIDecoderObserver.h"
 #include "RasterImage.h"
 #include "nsIInterfaceRequestor.h"
 #include "nsIInterfaceRequestorUtils.h"
@@ -186,16 +187,17 @@ RasterImage::RasterImage(imgStatusTracke
   mFrameDecodeFlags(DECODE_FLAGS_DEFAULT),
   mAnim(nsnull),
   mLoopCount(-1),
   mObserver(nsnull),
   mLockCount(0),
   mDecoder(nsnull),
   mWorker(nsnull),
   mBytesDecoded(0),
+  mDecodeCount(0),
 #ifdef DEBUG
   mFramesNotified(0),
 #endif
   mHasSize(PR_FALSE),
   mDecodeOnDraw(PR_FALSE),
   mMultipart(PR_FALSE),
   mDiscardable(PR_FALSE),
   mHasSourceData(PR_FALSE),
@@ -203,16 +205,17 @@ RasterImage::RasterImage(imgStatusTracke
   mHasBeenDecoded(PR_FALSE),
   mWorkerPending(PR_FALSE),
   mInDecoder(PR_FALSE),
   mAnimationFinished(PR_FALSE)
 {
   // Set up the discard tracker node.
   mDiscardTrackerNode.curr = this;
   mDiscardTrackerNode.prev = mDiscardTrackerNode.next = nsnull;
+  Telemetry::GetHistogramById(Telemetry::IMAGE_DECODE_COUNT)->Add(0);
 
   // Statistics
   num_containers++;
 }
 
 //******************************************************************************
 RasterImage::~RasterImage()
 {
@@ -2192,16 +2195,22 @@ RasterImage::InitDecoder(bool aDoSizeDec
   nsCOMPtr<imgIDecoderObserver> observer(do_QueryReferent(mObserver));
   mDecoder->SetSizeDecode(aDoSizeDecode);
   mDecoder->SetDecodeFlags(mFrameDecodeFlags);
   mDecoder->Init(this, observer);
   CONTAINER_ENSURE_SUCCESS(mDecoder->GetDecoderError());
 
   // Create a decode worker
   mWorker = new imgDecodeWorker(this);
+
+  if (!aDoSizeDecode) {
+    Telemetry::GetHistogramById(Telemetry::IMAGE_DECODE_COUNT)->Subtract(mDecodeCount);
+    mDecodeCount++;
+    Telemetry::GetHistogramById(Telemetry::IMAGE_DECODE_COUNT)->Add(mDecodeCount);
+  }
   CONTAINER_ENSURE_TRUE(mWorker, NS_ERROR_OUT_OF_MEMORY);
 
   return NS_OK;
 }
 
 // Flushes, closes, and nulls-out a decoder. Cleans up any related decoding
 // state. It is an error to call this function when there is no initialized
 // decoder.
--- a/modules/libpr0n/src/RasterImage.h
+++ b/modules/libpr0n/src/RasterImage.h
@@ -498,16 +498,20 @@ private: // data
   friend class imgDecodeWorker;
   friend class DiscardTracker;
 
   // Decoder and friends
   nsRefPtr<Decoder>              mDecoder;
   nsRefPtr<imgDecodeWorker>      mWorker;
   PRUint32                       mBytesDecoded;
 
+  // How many times we've decoded this image.
+  // This is currently only used for statistics
+  PRInt32                        mDecodeCount;
+
 #ifdef DEBUG
   PRUint32                       mFramesNotified;
 #endif
 
   // Boolean flags (clustered together to conserve space):
   PRPackedBool               mHasSize:1;       // Has SetSize() been called?
   PRPackedBool               mDecodeOnDraw:1;  // Decoding on draw?
   PRPackedBool               mMultipart:1;     // Multipart?
--- a/modules/libpr0n/src/imgLoader.cpp
+++ b/modules/libpr0n/src/imgLoader.cpp
@@ -586,26 +586,26 @@ void imgCacheEntry::Touch(PRBool updateT
 }
 
 void imgCacheEntry::UpdateCache(PRInt32 diff /* = 0 */)
 {
   // Don't update the cache if we've been removed from it or it doesn't care
   // about our size or usage.
   if (!Evicted() && HasNoProxies()) {
     nsCOMPtr<nsIURI> uri;
-    mRequest->GetKeyURI(getter_AddRefs(uri));
+    mRequest->GetURI(getter_AddRefs(uri));
     imgLoader::CacheEntriesChanged(uri, diff);
   }
 }
 
 void imgCacheEntry::SetHasNoProxies(PRBool hasNoProxies)
 {
 #if defined(PR_LOGGING)
   nsCOMPtr<nsIURI> uri;
-  mRequest->GetKeyURI(getter_AddRefs(uri));
+  mRequest->GetURI(getter_AddRefs(uri));
   nsCAutoString spec;
   if (uri)
     uri->GetSpec(spec);
   if (hasNoProxies)
     LOG_FUNC_WITH_PARAM(gImgLog, "imgCacheEntry::SetHasNoProxies true", "uri", spec.get());
   else
     LOG_FUNC_WITH_PARAM(gImgLog, "imgCacheEntry::SetHasNoProxies false", "uri", spec.get());
 #endif
@@ -791,17 +791,17 @@ void imgCacheExpirationTracker::NotifyEx
   // Hold on to a reference to this entry, because the expiration tracker
   // mechanism doesn't.
   nsRefPtr<imgCacheEntry> kungFuDeathGrip(entry);
 
 #if defined(PR_LOGGING)
   nsRefPtr<imgRequest> req(entry->GetRequest());
   if (req) {
     nsCOMPtr<nsIURI> uri;
-    req->GetKeyURI(getter_AddRefs(uri));
+    req->GetURI(getter_AddRefs(uri));
     nsCAutoString spec;
     uri->GetSpec(spec);
     LOG_FUNC_WITH_PARAM(gImgLog, "imgCacheExpirationTracker::NotifyExpired", "entry", spec.get());
   }
 #endif
 
   // We can be called multiple times on the same entry. Don't do work multiple
   // times.
@@ -1180,17 +1180,17 @@ void imgLoader::CheckCacheLimits(imgCach
     nsRefPtr<imgCacheEntry> entry(queue.Pop());
 
     NS_ASSERTION(entry, "imgLoader::CheckCacheLimits -- NULL entry pointer");
 
 #if defined(PR_LOGGING)
     nsRefPtr<imgRequest> req(entry->GetRequest());
     if (req) {
       nsCOMPtr<nsIURI> uri;
-      req->GetKeyURI(getter_AddRefs(uri));
+      req->GetURI(getter_AddRefs(uri));
       nsCAutoString spec;
       uri->GetSpec(spec);
       LOG_STATIC_FUNC_WITH_PARAM(gImgLog, "imgLoader::CheckCacheLimits", "entry", spec.get());
     }
 #endif
 
     if (entry)
       RemoveFromCache(entry);
@@ -1487,17 +1487,17 @@ PRBool imgLoader::RemoveFromCache(nsIURI
 
 PRBool imgLoader::RemoveFromCache(imgCacheEntry *entry)
 {
   LOG_STATIC_FUNC(gImgLog, "imgLoader::RemoveFromCache entry");
 
   nsRefPtr<imgRequest> request(getter_AddRefs(entry->GetRequest()));
   if (request) {
     nsCOMPtr<nsIURI> key;
-    if (NS_SUCCEEDED(request->GetKeyURI(getter_AddRefs(key))) && key) {
+    if (NS_SUCCEEDED(request->GetURI(getter_AddRefs(key))) && key) {
       imgCacheTable &cache = GetCache(key);
       imgCacheQueue &queue = GetCacheQueue(key);
       nsCAutoString spec;
       key->GetSpec(spec);
 
       LOG_STATIC_FUNC_WITH_PARAM(gImgLog, "imgLoader::RemoveFromCache", "entry's uri", spec.get());
 
       cache.Remove(spec);
--- a/modules/libpr0n/src/imgRequest.cpp
+++ b/modules/libpr0n/src/imgRequest.cpp
@@ -406,29 +406,16 @@ nsresult imgRequest::GetURI(nsIURI **aUR
     *aURI = mURI;
     NS_ADDREF(*aURI);
     return NS_OK;
   }
 
   return NS_ERROR_FAILURE;
 }
 
-nsresult imgRequest::GetKeyURI(nsIURI **aKeyURI)
-{
-  LOG_FUNC(gImgLog, "imgRequest::GetKeyURI");
-
-  if (mURI) {
-    *aKeyURI = mURI;
-    NS_ADDREF(*aKeyURI);
-    return NS_OK;
-  }
-
-  return NS_ERROR_FAILURE;
-}
-
 nsresult imgRequest::GetSecurityInfo(nsISupports **aSecurityInfo)
 {
   LOG_FUNC(gImgLog, "imgRequest::GetSecurityInfo");
 
   // Missing security info means this is not a security load
   // i.e. it is not an error when security info is missing
   NS_IF_ADDREF(*aSecurityInfo = mSecurityInfo);
   return NS_OK;
--- a/modules/libpr0n/src/imgRequest.h
+++ b/modules/libpr0n/src/imgRequest.h
@@ -159,17 +159,16 @@ private:
 
   inline void SetLoadId(void *aLoadId) {
     mLoadId = aLoadId;
   }
   void Cancel(nsresult aStatus);
   void RemoveFromCache();
 
   nsresult GetURI(nsIURI **aURI);
-  nsresult GetKeyURI(nsIURI **aURI);
   nsresult GetSecurityInfo(nsISupports **aSecurityInfo);
 
   inline const char *GetMimeType() const {
     return mContentType.get();
   }
   inline nsIProperties *Properties() {
     return mProperties;
   }
--- a/netwerk/base/src/nsProtocolProxyService.cpp
+++ b/netwerk/base/src/nsProtocolProxyService.cpp
@@ -389,17 +389,17 @@ nsProtocolProxyService::PrefsChanged(nsI
     nsresult rv = NS_OK;
     PRBool reloadPAC = PR_FALSE;
     nsXPIDLCString tempString;
 
     if (!pref || !strcmp(pref, PROXY_PREF("type"))) {
         PRInt32 type = -1;
         rv = prefBranch->GetIntPref(PROXY_PREF("type"), &type);
         if (NS_SUCCEEDED(rv)) {
-            // bug 115720 - for ns4.x backwards compatability
+            // bug 115720 - for ns4.x backwards compatibility
             if (type == PROXYCONFIG_DIRECT4X) {
                 type = PROXYCONFIG_DIRECT;
                 // Reset the type so that the dialog looks correct, and we
                 // don't have to handle this case everywhere else
                 // I'm paranoid about a loop of some sort - only do this
                 // if we're enumerating all prefs, and ignore any error
                 if (!pref)
                     prefBranch->SetIntPref(PROXY_PREF("type"), type);
--- a/netwerk/cache/nsMemoryCacheDevice.cpp
+++ b/netwerk/cache/nsMemoryCacheDevice.cpp
@@ -44,17 +44,17 @@
 #include "nsCacheService.h"
 #include "nsICacheService.h"
 #include "nsIStorageStream.h"
 #include "nsICacheVisitor.h"
 #include "nsCRT.h"
 #include "nsCache.h"
 #include "nsReadableUtils.h"
 
-// The memory cache implements a variation of the "LRU-SP" caching algorithm
+// The memory cache implements the "LRU-SP" caching algorithm
 // described in "LRU-SP: A Size-Adjusted and Popularity-Aware LRU Replacement
 // Algorithm for Web Caching" by Kai Cheng and Yahiko Kambayashi.
 
 // We keep kQueueCount LRU queues, which should be about ceil(log2(mHardLimit))
 // The queues hold exponentially increasing ranges of floor(log2((size/nref)))
 // values for entries.
 // Entries larger than 2^(kQueueCount-1) go in the last queue.
 // Entries with no expiration go in the first queue.
@@ -374,41 +374,59 @@ nsMemoryCacheDevice::EvictEntry(nsCacheE
     
     if (deleteEntry)  delete entry;
 }
 
 
 void
 nsMemoryCacheDevice::EvictEntriesIfNecessary(void)
 {
-    nsCacheEntry * entry, * next;
-
+    nsCacheEntry * entry;
+    nsCacheEntry * maxEntry;
     CACHE_LOG_DEBUG(("EvictEntriesIfNecessary.  mTotalSize: %d, mHardLimit: %d,"
                      "mInactiveSize: %d, mSoftLimit: %d\n",
                      mTotalSize, mHardLimit, mInactiveSize, mSoftLimit));
     
     if ((mTotalSize < mHardLimit) && (mInactiveSize < mSoftLimit))
         return;
 
-    for (int i = kQueueCount - 1; i >= 0; --i) {
-        entry = (nsCacheEntry *)PR_LIST_HEAD(&mEvictionList[i]);
-        while (entry != &mEvictionList[i]) {
-            if (entry->IsInUse()) {
+    PRUint32 now = SecondsFromPRTime(PR_Now());
+    PRUint64 entryCost = 0;
+    PRUint64 maxCost = 0;
+    do {
+        // LRU-SP eviction selection: Check the head of each segment (each
+        // eviction list, kept in LRU order) and select the maximal-cost
+        // entry for eviction. Cost is time-since-accessed * size / nref.
+        maxEntry = 0;
+        for (int i = kQueueCount - 1; i >= 0; --i) {
+            entry = (nsCacheEntry *)PR_LIST_HEAD(&mEvictionList[i]);
+
+            // If the head of a list is in use, check the next available entry
+            while ((entry != &mEvictionList[i]) &&
+                   (entry->IsInUse())) {
                 entry = (nsCacheEntry *)PR_NEXT_LINK(entry);
-                continue;
             }
 
-            next = (nsCacheEntry *)PR_NEXT_LINK(entry);
-            EvictEntry(entry, DELETE_ENTRY);
-            entry = next;
-
-            if ((mTotalSize < mHardLimit) && (mInactiveSize < mSoftLimit))
-                return;
+            if (entry != &mEvictionList[i]) {
+                entryCost = (PRUint64)
+                    (now - entry->LastFetched()) * entry->Size() / 
+                    PR_MAX(1, entry->FetchCount());
+                if (!maxEntry || (entryCost > maxCost)) {
+                    maxEntry = entry;
+                    maxCost = entryCost;
+                }
+            }
+        }
+        if (maxEntry) {
+            EvictEntry(maxEntry, DELETE_ENTRY);
+        } else {
+            break;
         }
     }
+    while ((mTotalSize >= mHardLimit) || (mInactiveSize >= mSoftLimit));
 }
 
 
 int
 nsMemoryCacheDevice::EvictionList(nsCacheEntry * entry, PRInt32  deltaSize)
 {
     // favor items which never expire by putting them in the lowest-index queue
     if (entry->ExpirationTime() == nsICache::NO_EXPIRATION_TIME)
--- a/parser/htmlparser/tests/html/span001.html
+++ b/parser/htmlparser/tests/html/span001.html
@@ -6,17 +6,17 @@
 
 </HEAD>
 
 <BODY>
 <p>
 In HTML standard, SPANs are not allowed to cross Paragraphs.
 <br> In Vav4.0, SPAN tags go thour paragraphs and other block-level elements,
 except tbles.
-<br>It be compatable with Nav4.0 and HTML standard, HTMLParser
+<br>It be compatible with Nav4.0 and HTML standard, HTMLParser
 close all SPANs before open a new paragraph, and reopen SPANs
 inside the new paragraph
 <br>
 
 <FONT color=red>
   Text ater the color=red SPAN is opend.
 </P>
   Text in a new paragraph.
--- a/services/sync/modules/policies.js
+++ b/services/sync/modules/policies.js
@@ -102,18 +102,20 @@ let SyncScheduler = {
       Svc.Idle.addIdleObserver(this, Svc.Prefs.get("scheduler.idleTime"));
     }
 
   },
 
   observe: function observe(subject, topic, data) {
     switch(topic) {
       case "weave:engine:score:updated":
-        Utils.namedTimer(this.calculateScore, SCORE_UPDATE_DELAY, this,
-                         "_scoreTimer");
+        if (Status.login == LOGIN_SUCCEEDED) {
+          Utils.namedTimer(this.calculateScore, SCORE_UPDATE_DELAY, this,
+                           "_scoreTimer");
+        }
         break;
       case "network:offline-status-changed":
         // Whether online or offline, we'll reschedule syncs
         this._log.trace("Network offline status change: " + data);
         this.checkSyncStatus();
         break;
       case "weave:service:sync:start":
         // Clear out any potentially pending syncs now that we're syncing
@@ -488,59 +490,63 @@ let ErrorHandler = {
 
         Status.engines = [engine_name, exception.failureCode || ENGINE_UNKNOWN_FAIL];
         this._log.debug(engine_name + " failed: " + Utils.exceptionStr(exception));
         break;
       case "weave:service:login:error":
         if (this.shouldReportError()) {
           this.resetFileLog(Svc.Prefs.get("log.appender.file.logOnError"),
                             LOG_PREFIX_ERROR);
-          Svc.Obs.notify("weave:ui:login:error");
+          this.notifyOnNextTick("weave:ui:login:error");
         } else {
-          Svc.Obs.notify("weave:ui:clear-error");
+          this.notifyOnNextTick("weave:ui:clear-error");
         }
 
         this.dontIgnoreErrors = false;
         break;
       case "weave:service:sync:error":
         if (Status.sync == CREDENTIALS_CHANGED) {
           Weave.Service.logout();
         }
 
         if (this.shouldReportError()) {
           this.resetFileLog(Svc.Prefs.get("log.appender.file.logOnError"),
                             LOG_PREFIX_ERROR);
-          Svc.Obs.notify("weave:ui:sync:error");
+          this.notifyOnNextTick("weave:ui:sync:error");
         } else {
-          Svc.Obs.notify("weave:ui:sync:finish");
+          this.notifyOnNextTick("weave:ui:sync:finish");
         }
 
         this.dontIgnoreErrors = false;
         break;
       case "weave:service:sync:finish":
         if (Status.service == SYNC_FAILED_PARTIAL) {
           this._log.debug("Some engines did not sync correctly.");
           this.resetFileLog(Svc.Prefs.get("log.appender.file.logOnError"),
                             LOG_PREFIX_ERROR);
 
           if (this.shouldReportError()) {
             this.dontIgnoreErrors = false;
-            Svc.Obs.notify("weave:ui:sync:error");
+            this.notifyOnNextTick("weave:ui:sync:error");
             break;
           }
         } else {
           this.resetFileLog(Svc.Prefs.get("log.appender.file.logOnSuccess"),
                             LOG_PREFIX_SUCCESS);
         }
         this.dontIgnoreErrors = false;
-        Svc.Obs.notify("weave:ui:sync:finish");
+        this.notifyOnNextTick("weave:ui:sync:finish");
         break;
     }
   },
 
+  notifyOnNextTick: function notifyOnNextTick(topic) {
+    Utils.nextTick(function() Svc.Obs.notify(topic));
+  },
+
   /**
    * Trigger a sync and don't muffle any errors, particularly network errors.
    */
   syncAndReportErrors: function syncAndReportErrors() {
     this._log.debug("Beginning user-triggered sync.");
 
     this.dontIgnoreErrors = true;
     Utils.nextTick(Weave.Service.sync, Weave.Service);
@@ -618,17 +624,17 @@ let ErrorHandler = {
 
     let lastSync = Svc.Prefs.get("lastSync");
     if (lastSync && ((Date.now() - Date.parse(lastSync)) >
         Svc.Prefs.get("errorhandler.networkFailureReportTimeout") * 1000)) {
       Status.sync = PROLONGED_SYNC_FAILURE;
       return true;
     }
 
-    return (Status.sync != SERVER_MAINTENANCE &&
+    return ([Status.login, Status.sync].indexOf(SERVER_MAINTENANCE) == -1 &&
             [Status.login, Status.sync].indexOf(LOGIN_FAILED_NETWORK_ERROR) == -1);
   },
 
   /**
    * Handle HTTP response results or exceptions and set the appropriate
    * Status.* bits.
    */
   checkServerError: function checkServerError(resp) {
@@ -645,29 +651,37 @@ let ErrorHandler = {
         break;
 
       case 500:
       case 502:
       case 503:
       case 504:
         Status.enforceBackoff = true;
         if (resp.status == 503 && resp.headers["retry-after"]) {
-          Status.sync = SERVER_MAINTENANCE;
+          if (Weave.Service.isLoggedIn) {
+            Status.sync = SERVER_MAINTENANCE;
+          } else {
+            Status.login = SERVER_MAINTENANCE;
+          }
           Svc.Obs.notify("weave:service:backoff:interval",
                          parseInt(resp.headers["retry-after"], 10));
         }
         break;
     }
 
     switch (resp.result) {
       case Cr.NS_ERROR_UNKNOWN_HOST:
       case Cr.NS_ERROR_CONNECTION_REFUSED:
       case Cr.NS_ERROR_NET_TIMEOUT:
       case Cr.NS_ERROR_NET_RESET:
       case Cr.NS_ERROR_NET_INTERRUPT:
       case Cr.NS_ERROR_PROXY_CONNECTION_REFUSED:
         // The constant says it's about login, but in fact it just
         // indicates general network error.
-        Status.sync = LOGIN_FAILED_NETWORK_ERROR;
+        if (Weave.Service.isLoggedIn) {
+          Status.sync = LOGIN_FAILED_NETWORK_ERROR;
+        } else {
+          Status.login = LOGIN_FAILED_NETWORK_ERROR;
+        }
         break;
     }
   },
 };
--- a/services/sync/modules/service.js
+++ b/services/sync/modules/service.js
@@ -522,22 +522,24 @@ WeaveSvc.prototype = {
           this._log.debug("Using serverURL as data cluster (multi-cluster support disabled)");
           return this.serverURL;
         case 0:
         case 200:
           if (node == "null")
             node = null;
           return node;
         default:
+          ErrorHandler.checkServerError(node);
           fail = "Unexpected response code: " + node.status;
           break;
       }
     } catch (e) {
       this._log.debug("Network error on findCluster");
       Status.login = LOGIN_FAILED_NETWORK_ERROR;
+      ErrorHandler.checkServerError(e);
       fail = e;
     }
     throw fail;
   },
 
   // gets cluster from central LDAP server and sets this.clusterURL
   _setCluster: function _setCluster() {
     // Make sure we didn't get some unexpected response for the cluster
@@ -616,16 +618,17 @@ WeaveSvc.prototype = {
     try {
       if (!infoResponse)
         infoResponse = this._fetchInfo();    // Will throw an exception on failure.
 
       // This only applies when the server is already at version 4.
       if (infoResponse.status != 200) {
         this._log.warn("info/collections returned non-200 response. Failing key fetch.");
         Status.login = LOGIN_FAILED_SERVER_ERROR;
+        ErrorHandler.checkServerError(infoResponse);
         return false;
       }
 
       let infoCollections = infoResponse.obj;
 
       this._log.info("Testing info/collections: " + JSON.stringify(infoCollections));
 
       if (CollectionKeys.updateNeeded(infoCollections)) {
@@ -648,18 +651,19 @@ WeaveSvc.prototype = {
             else if (cryptoResp.status == 404) {
               // On failure, ask CollectionKeys to generate new keys and upload them.
               // Fall through to the behavior below.
               this._log.warn("Got 404 for crypto/keys, but 'crypto' in info/collections. Regenerating.");
               cryptoKeys = null;
             }
             else {
               // Some other problem.
+              Status.login = LOGIN_FAILED_SERVER_ERROR;
+              ErrorHandler.checkServerError(cryptoResp);
               this._log.warn("Got status " + cryptoResp.status + " fetching crypto keys.");
-              Status.login = LOGIN_FAILED_SERVER_ERROR;
               return false;
             }
           }
           catch (ex) {
             this._log.warn("Got exception \"" + ex + "\" fetching cryptoKeys.");
             // TODO: Um, what exceptions might we get here? Should we re-throw any?
 
             // One kind of exception: HMAC failure.
@@ -793,25 +797,26 @@ WeaveSvc.prototype = {
               return this.verifyLogin();
 
             // We must have the right cluster, but the server doesn't expect us
             Status.login = LOGIN_FAILED_LOGIN_REJECTED;
             return false;
 
           default:
             // Server didn't respond with something that we expected
+            Status.login = LOGIN_FAILED_SERVER_ERROR;
             ErrorHandler.checkServerError(test);
-            Status.login = LOGIN_FAILED_SERVER_ERROR;
             return false;
         }
       }
       catch (ex) {
         // Must have failed on some network issue
         this._log.debug("verifyLogin failed: " + Utils.exceptionStr(ex));
         Status.login = LOGIN_FAILED_NETWORK_ERROR;
+        ErrorHandler.checkServerError(ex);
         return false;
       }
     })(),
 
   generateNewSymmetricKeys:
   function WeaveSvc_generateNewSymmetricKeys() {
     this._log.info("Generating new keys WBO...");
     let wbo = CollectionKeys.generateNewKeysWBO();
@@ -1131,18 +1136,18 @@ WeaveSvc.prototype = {
     if (!meta || !meta.payload.storageVersion || !meta.payload.syncID ||
         STORAGE_VERSION > parseFloat(remoteVersion)) {
 
       this._log.info("One of: no meta, no meta storageVersion, or no meta syncID. Fresh start needed.");
 
       // abort the server wipe if the GET status was anything other than 404 or 200
       let status = Records.response.status;
       if (status != 200 && status != 404) {
+        Status.sync = METARECORD_DOWNLOAD_FAIL;
         ErrorHandler.checkServerError(Records.response);
-        Status.sync = METARECORD_DOWNLOAD_FAIL;
         this._log.warn("Unknown error while downloading metadata record. " +
                        "Aborting sync.");
         return false;
       }
 
       if (!meta)
         this._log.info("No metadata record, server wipe needed");
       if (meta && !meta.payload.syncID)
--- a/services/sync/tests/tps/mozmill_sanity.js
+++ b/services/sync/tests/tps/mozmill_sanity.js
@@ -30,31 +30,33 @@
  * use your version of this file under the terms of the MPL, indicate your
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * **** END LICENSE BLOCK ***** */
 
-var jum = {}; Components.utils.import('resource://mozmill/modules/jum.js', jum);
+Components.utils.import('resource://tps/sync.jsm');
 
 var setupModule = function(module) {
   controller = mozmill.getBrowserController();
-  jum.assert(true, "SetupModule passes");
+  assert.ok(true, "SetupModule passes");
 }
 
 var setupTest = function(module) {
-  jum.assert(true, "SetupTest passes");
+  assert.ok(true, "SetupTest passes");
 }
 
 var testTestStep = function() {
-  jum.assert(true, "test Passes");
+  assert.ok(true, "test Passes");
   controller.open("http://www.mozilla.org");
+  TPS.SetupSyncAccount();
+  assert.equal(TPS.Sync(SYNC_WIPE_SERVER), 0, "sync succeeded");
 }
 
 var teardownTest = function () {
-  jum.assert(true, "teardownTest passes");
+  assert.ok(true, "teardownTest passes");
 }
 
 var teardownModule = function() {
-  jum.assert(true, "teardownModule passes");
+  assert.ok(true, "teardownModule passes");
 }
--- a/services/sync/tests/tps/test_mozmill_sanity.js
+++ b/services/sync/tests/tps/test_mozmill_sanity.js
@@ -11,15 +11,14 @@ var phases = { "phase1": "profile1",
                "phase2": "profile2" };
 
 /*
  * Test phases
  */
 
 Phase('phase1', [
   [RunMozmillTest, 'mozmill_sanity.js'],
-  [Sync, SYNC_WIPE_SERVER]
 ]);
 
 Phase('phase2', [
   [Sync],
   [RunMozmillTest, 'mozmill_sanity2.js'],
 ]);
--- a/services/sync/tests/unit/test_errorhandler.js
+++ b/services/sync/tests/unit/test_errorhandler.js
@@ -52,16 +52,23 @@ function generateCredentialsChangedFailu
   // the keys with a different Sync Key, without changing the local one.
   let newSyncKeyBundle = new SyncKeyBundle(PWDMGR_PASSPHRASE_REALM, Service.username);
   newSyncKeyBundle.keyStr = "23456234562345623456234562";
   let keys = CollectionKeys.asWBO();
   keys.encrypt(newSyncKeyBundle);
   keys.upload(Service.cryptoKeysURL);
 }
 
+function service_unavailable(request, response) {
+  let body = "Service Unavailable";
+  response.setStatusLine(request.httpVersion, 503, "Service Unavailable");
+  response.setHeader("Retry-After", "42");
+  response.bodyOutputStream.write(body, body.length);
+}
+
 function sync_httpd_setup() {
   let global = new ServerWBO("global", {
     syncID: Service.syncID,
     storageVersion: STORAGE_VERSION,
     engines: {clients: {version: Clients.version,
                         syncID: Clients.syncID}}
   });
   let clientsColl = new ServerCollection({}, true);
@@ -75,31 +82,46 @@ function sync_httpd_setup() {
     "/1.1/johndoe/storage/meta/global": upd("meta", global.handler()),
     "/1.1/johndoe/info/collections": collectionsHelper.handler,
     "/1.1/johndoe/storage/crypto/keys":
       upd("crypto", (new ServerWBO("keys")).handler()),
     "/1.1/johndoe/storage/clients": upd("clients", clientsColl.handler()),
 
     "/1.1/janedoe/storage/meta/global": handler_401,
     "/1.1/janedoe/info/collections": handler_401,
+
+    "/maintenance/1.1/johnsmith/info/collections": service_unavailable,
+
+    "/maintenance/1.1/janesmith/storage/meta/global": service_unavailable,
+    "/maintenance/1.1/janesmith/info/collections": collectionsHelper.handler,
+
+    "/maintenance/1.1/foo/storage/meta/global": upd("meta", global.handler()),
+    "/maintenance/1.1/foo/info/collections": collectionsHelper.handler,
+    "/maintenance/1.1/foo/storage/crypto/keys": service_unavailable,
   });
 }
 
 function setUp() {
   Service.username = "johndoe";
   Service.password = "ilovejane";
   Service.passphrase = "abcdeabcdeabcdeabcdeabcdea";
   Service.clusterURL = "http://localhost:8080/";
 
   generateNewKeys();
   let serverKeys = CollectionKeys.asWBO("crypto", "keys");
   serverKeys.encrypt(Service.syncKeyBundle);
   return serverKeys.upload(Service.cryptoKeysURL).success;
 }
 
+function clean() {
+  Service.startOver();
+  Status.resetSync();
+  Status.resetBackoff();
+}
+
 add_test(function test_401_logout() {
   let server = sync_httpd_setup();
   setUp();
 
   // By calling sync, we ensure we're logged in.
   Service.sync();
   do_check_eq(Status.sync, SYNC_SUCCEEDED);
   do_check_true(Service.isLoggedIn);
@@ -263,45 +285,74 @@ add_test(function test_shouldReportError
 
   // Test network, non-prolonged, sync error reported
   Status.resetSync();
   setLastSync(NON_PROLONGED_ERROR_DURATION);
   ErrorHandler.dontIgnoreErrors = false;
   Status.sync = LOGIN_FAILED_NETWORK_ERROR;
   do_check_false(ErrorHandler.shouldReportError());
 
-  // Test server maintenance errors are not reported
+  // Test server maintenance, sync errors are not reported
   Status.resetSync();
   setLastSync(NON_PROLONGED_ERROR_DURATION);
   ErrorHandler.dontIgnoreErrors = false;
   Status.sync = SERVER_MAINTENANCE;
   do_check_false(ErrorHandler.shouldReportError());
 
-  // Test prolonged server maintenance errors are reported
+  // Test server maintenance, login errors are not reported
+  Status.resetSync();
+  setLastSync(NON_PROLONGED_ERROR_DURATION);
+  ErrorHandler.dontIgnoreErrors = false;
+  Status.login = SERVER_MAINTENANCE;
+  do_check_false(ErrorHandler.shouldReportError());
+
+  // Test prolonged, server maintenance, sync errors are reported
   Status.resetSync();
   setLastSync(PROLONGED_ERROR_DURATION);
   ErrorHandler.dontIgnoreErrors = false;
   Status.sync = SERVER_MAINTENANCE;
   do_check_true(ErrorHandler.shouldReportError());
 
-  // Test dontIgnoreErrors, server maintenance errors are reported
+  // Test prolonged, server maintenance, login errors are reported
+  Status.resetSync();
+  setLastSync(PROLONGED_ERROR_DURATION);
+  ErrorHandler.dontIgnoreErrors = false;
+  Status.login = SERVER_MAINTENANCE;
+  do_check_true(ErrorHandler.shouldReportError());
+
+  // Test dontIgnoreErrors, server maintenance, sync errors are reported
   Status.resetSync();
   setLastSync(NON_PROLONGED_ERROR_DURATION);
   ErrorHandler.dontIgnoreErrors = true;
   Status.sync = SERVER_MAINTENANCE;
   do_check_true(ErrorHandler.shouldReportError());
 
-  // Test dontIgnoreErrors, prolonged, server maintenance
-  // errors are reported
+  // Test dontIgnoreErrors, server maintenance, login errors are reported
+  Status.resetSync();
+  setLastSync(NON_PROLONGED_ERROR_DURATION);
+  ErrorHandler.dontIgnoreErrors = true;
+  Status.login = SERVER_MAINTENANCE;
+  do_check_true(ErrorHandler.shouldReportError());
+
+  // Test dontIgnoreErrors, prolonged, server maintenance,
+  // sync errors are reported
   Status.resetSync();
   setLastSync(PROLONGED_ERROR_DURATION);
   ErrorHandler.dontIgnoreErrors = true;
   Status.sync = SERVER_MAINTENANCE;
   do_check_true(ErrorHandler.shouldReportError());
 
+  // Test dontIgnoreErrors, prolonged, server maintenance,
+  // login errors are reported
+  Status.resetSync();
+  setLastSync(PROLONGED_ERROR_DURATION);
+  ErrorHandler.dontIgnoreErrors = true;
+  Status.login = SERVER_MAINTENANCE;
+  do_check_true(ErrorHandler.shouldReportError());
+
   run_next_test();
 });
 
 add_test(function test_shouldReportError_master_password() {
   _("Test error ignored due to locked master password");
   let server = sync_httpd_setup();
   setUp();
 
@@ -314,32 +365,32 @@ add_test(function test_shouldReportError
   };
 
   setLastSync(NON_PROLONGED_ERROR_DURATION);
   Service.sync();
   do_check_false(ErrorHandler.shouldReportError());
 
   // Clean up.
   Service.verifyLogin = Service._verifyLogin;
-  Service.startOver();
+  clean();
   server.stop(run_next_test);
 });
 
 add_test(function test_login_syncAndReportErrors_non_network_error() {
   // Test non-network errors are reported
   // when calling syncAndReportErrors
   let server = sync_httpd_setup();
   setUp();
   Service.password = "";
 
   Svc.Obs.add("weave:ui:login:error", function onSyncError() {
     Svc.Obs.remove("weave:ui:login:error", onSyncError);
     do_check_eq(Status.login, LOGIN_FAILED_NO_PASSWORD);
 
-    Service.startOver();
+    clean();
     server.stop(run_next_test);
   });
 
   setLastSync(NON_PROLONGED_ERROR_DURATION);
   ErrorHandler.syncAndReportErrors();
 });
 
 add_test(function test_sync_syncAndReportErrors_non_network_error() {
@@ -354,17 +405,17 @@ add_test(function test_sync_syncAndRepor
   do_check_true(Service.isLoggedIn);
 
   generateCredentialsChangedFailure();
 
   Svc.Obs.add("weave:ui:sync:error", function onSyncError() {
     Svc.Obs.remove("weave:ui:sync:error", onSyncError);
     do_check_eq(Status.sync, CREDENTIALS_CHANGED);
 
-    Service.startOver();
+    clean();
     server.stop(run_next_test);
   });
 
   setLastSync(NON_PROLONGED_ERROR_DURATION);
   ErrorHandler.syncAndReportErrors();
 });
 
 add_test(function test_login_syncAndReportErrors_prolonged_non_network_error() {
@@ -373,17 +424,17 @@ add_test(function test_login_syncAndRepo
   let server = sync_httpd_setup();
   setUp();
   Service.password = "";
 
   Svc.Obs.add("weave:ui:login:error", function onSyncError() {
     Svc.Obs.remove("weave:ui:login:error", onSyncError);
     do_check_eq(Status.login, LOGIN_FAILED_NO_PASSWORD);
 
-    Service.startOver();
+    clean();
     server.stop(run_next_test);
   });
 
   setLastSync(PROLONGED_ERROR_DURATION);
   ErrorHandler.syncAndReportErrors();
 });
 
 add_test(function test_sync_syncAndReportErrors_prolonged_non_network_error() {
@@ -398,17 +449,17 @@ add_test(function test_sync_syncAndRepor
   do_check_true(Service.isLoggedIn);
 
   generateCredentialsChangedFailure();
 
   Svc.Obs.add("weave:ui:sync:error", function onSyncError() {
     Svc.Obs.remove("weave:ui:sync:error", onSyncError);
     do_check_eq(Status.sync, CREDENTIALS_CHANGED);
 
-    Service.startOver();
+    clean();
     server.stop(run_next_test);
   });
 
   setLastSync(PROLONGED_ERROR_DURATION);
   ErrorHandler.syncAndReportErrors();
 });
 
 add_test(function test_login_syncAndReportErrors_network_error() {
@@ -417,17 +468,17 @@ add_test(function test_login_syncAndRepo
   Service.password = "ilovejane";
   Service.passphrase = "abcdeabcdeabcdeabcdeabcdea";
   Service.clusterURL = "http://localhost:8080/";
 
   Svc.Obs.add("weave:ui:login:error", function onSyncError() {
     Svc.Obs.remove("weave:ui:login:error", onSyncError);
     do_check_eq(Status.login, LOGIN_FAILED_NETWORK_ERROR);
 
-    Service.startOver();
+    clean();
     run_next_test();
   });
 
   setLastSync(NON_PROLONGED_ERROR_DURATION);
   ErrorHandler.syncAndReportErrors();
 });
 
 
@@ -435,17 +486,17 @@ add_test(function test_sync_syncAndRepor
   // Test network errors are reported when calling syncAndReportErrors.
   Services.io.offline = true;
 
   Svc.Obs.add("weave:ui:sync:error", function onSyncError() {
     Svc.Obs.remove("weave:ui:sync:error", onSyncError);
     do_check_eq(Status.sync, LOGIN_FAILED_NETWORK_ERROR);
 
     Services.io.offline = false;
-    Service.startOver();
+    clean();
     run_next_test();
   });
 
   setLastSync(NON_PROLONGED_ERROR_DURATION);
   ErrorHandler.syncAndReportErrors();
 });
 
 add_test(function test_login_syncAndReportErrors_prolonged_network_error() {
@@ -453,19 +504,19 @@ add_test(function test_login_syncAndRepo
   // when calling syncAndReportErrors.
   Service.username = "johndoe";
   Service.password = "ilovejane";
   Service.passphrase = "abcdeabcdeabcdeabcdeabcdea";
   Service.clusterURL = "http://localhost:8080/";
 
   Svc.Obs.add("weave:ui:login:error", function onSyncError() {
     Svc.Obs.remove("weave:ui:login:error", onSyncError);
-    do_check_eq(Status.sync, LOGIN_FAILED_NETWORK_ERROR);
+    do_check_eq(Status.login, LOGIN_FAILED_NETWORK_ERROR);
 
-    Service.startOver();
+    clean();
     run_next_test();
   });
 
   setLastSync(PROLONGED_ERROR_DURATION);
   ErrorHandler.syncAndReportErrors();
 });
 
 add_test(function test_sync_syncAndReportErrors_prolonged_network_error() {
@@ -473,17 +524,17 @@ add_test(function test_sync_syncAndRepor
   // when calling syncAndReportErrors.
   Services.io.offline = true;
 
   Svc.Obs.add("weave:ui:sync:error", function onSyncError() {
     Svc.Obs.remove("weave:ui:sync:error", onSyncError);
     do_check_eq(Status.sync, LOGIN_FAILED_NETWORK_ERROR);
 
     Services.io.offline = false;
-    Service.startOver();
+    clean();
     run_next_test();
   });
 
   setLastSync(PROLONGED_ERROR_DURATION);
   ErrorHandler.syncAndReportErrors();
 });
 
 add_test(function test_login_prolonged_non_network_error() {
@@ -491,17 +542,17 @@ add_test(function test_login_prolonged_n
   let server = sync_httpd_setup();
   setUp();
   Service.password = "";
 
   Svc.Obs.add("weave:ui:login:error", function onSyncError() {
     Svc.Obs.remove("weave:ui:login:error", onSyncError);
     do_check_eq(Status.sync, PROLONGED_SYNC_FAILURE);
 
-    Service.startOver();
+    clean();
     server.stop(run_next_test);
   });
 
   setLastSync(PROLONGED_ERROR_DURATION);
   Service.sync();
 });
 
 add_test(function test_sync_prolonged_non_network_error() {
@@ -515,17 +566,17 @@ add_test(function test_sync_prolonged_no
   do_check_true(Service.isLoggedIn);
 
   generateCredentialsChangedFailure();
 
   Svc.Obs.add("weave:ui:sync:error", function onSyncError() {
     Svc.Obs.remove("weave:ui:sync:error", onSyncError);
     do_check_eq(Status.sync, PROLONGED_SYNC_FAILURE);
 
-    Service.startOver();
+    clean();
     server.stop(run_next_test);
   });
 
   setLastSync(PROLONGED_ERROR_DURATION);
   Service.sync();
 });
 
 add_test(function test_login_prolonged_network_error() {
@@ -534,34 +585,34 @@ add_test(function test_login_prolonged_n
   Service.password = "ilovejane";
   Service.passphrase = "abcdeabcdeabcdeabcdeabcdea";
   Service.clusterURL = "http://localhost:8080/";
 
   Svc.Obs.add("weave:ui:login:error", function onSyncError() {
     Svc.Obs.remove("weave:ui:login:error", onSyncError);
     do_check_eq(Status.sync, PROLONGED_SYNC_FAILURE);
 
-    Service.startOver();
+    clean();
     run_next_test();
   });
 
   setLastSync(PROLONGED_ERROR_DURATION);
   Service.sync();
 });
 
 add_test(function test_sync_prolonged_network_error() {
   // Test prolonged, network errors are reported
   Services.io.offline = true;
 
   Svc.Obs.add("weave:ui:sync:error", function onSyncError() {
     Svc.Obs.remove("weave:ui:sync:error", onSyncError);
     do_check_eq(Status.sync, PROLONGED_SYNC_FAILURE);
 
     Services.io.offline = false;
-    Service.startOver();
+    clean();
     run_next_test();
   });
 
   setLastSync(PROLONGED_ERROR_DURATION);
   Service.sync();
 });
 
 add_test(function test_login_non_network_error() {
@@ -569,42 +620,41 @@ add_test(function test_login_non_network
   let server = sync_httpd_setup();
   setUp();
   Service.password = "";
 
   Svc.Obs.add("weave:ui:login:error", function onSyncError() {
     Svc.Obs.remove("weave:ui:login:error", onSyncError);
     do_check_eq(Status.login, LOGIN_FAILED_NO_PASSWORD);
 
-    Service.startOver();
+    clean();
     server.stop(run_next_test);
   });
 
   setLastSync(NON_PROLONGED_ERROR_DURATION);
   Service.sync();
 });
 
-
 add_test(function test_sync_non_network_error() {
   // Test non-network errors are reported
   let server = sync_httpd_setup();
   setUp();
 
   // By calling sync, we ensure we're logged in.
   Service.sync();
   do_check_eq(Status.sync, SYNC_SUCCEEDED);
   do_check_true(Service.isLoggedIn);
 
   generateCredentialsChangedFailure();
 
   Svc.Obs.add("weave:ui:sync:error", function onSyncError() {
     Svc.Obs.remove("weave:ui:sync:error", onSyncError);
     do_check_eq(Status.sync, CREDENTIALS_CHANGED);
 
-    Service.startOver();
+    clean();
     server.stop(run_next_test);
   });
 
   setLastSync(NON_PROLONGED_ERROR_DURATION);
   Service.sync();
 });
 
 add_test(function test_login_network_error() {
@@ -614,37 +664,35 @@ add_test(function test_login_network_err
   Service.clusterURL = "http://localhost:8080/";
 
   // Test network errors are not reported.
   Svc.Obs.add("weave:ui:clear-error", function onClearError() {
     Svc.Obs.remove("weave:ui:clear-error", onClearError);
 
     do_check_eq(Status.login, LOGIN_FAILED_NETWORK_ERROR);
 
-    Service.startOver();
-    Status.resetSync();
     Services.io.offline = false;
+    clean();
     run_next_test();
   });
 
   setLastSync(NON_PROLONGED_ERROR_DURATION);
   Service.sync();
 });
 
 add_test(function test_sync_network_error() {
   // Test network errors are not reported.
   Services.io.offline = true;
 
   Svc.Obs.add("weave:ui:sync:finish", function onUIUpdate() {
     Svc.Obs.remove("weave:ui:sync:finish", onUIUpdate);
     do_check_eq(Status.sync, LOGIN_FAILED_NETWORK_ERROR);
 
-    Service.startOver();
     Services.io.offline = false;
-    Status.resetSync();
+    clean();
     run_next_test();
   });
 
   setLastSync(NON_PROLONGED_ERROR_DURATION);
   Service.sync();
 });
 
 add_test(function test_sync_server_maintenance_error() {
@@ -653,34 +701,155 @@ add_test(function test_sync_server_maint
   setUp();
 
   const BACKOFF = 42;
   let engine = Engines.get("catapult");
   engine.enabled = true;
   engine.exception = {status: 503,
                       headers: {"retry-after": BACKOFF}};
 
-  function onUIUpdate() {
+  function onSyncError() {
     do_throw("Shouldn't get here!");
   }
-  Svc.Obs.add("weave:ui:sync:error", onUIUpdate);
+  Svc.Obs.add("weave:ui:sync:error", onSyncError);
 
   do_check_eq(Status.service, STATUS_OK);
 
-  // Last sync was 2 days ago
-  Svc.Prefs.set("lastSync", (new Date(Date.now() - 172800000)).toString());
+  Svc.Obs.add("weave:ui:sync:finish", function onSyncFinish() {
+    Svc.Obs.remove("weave:ui:sync:finish", onSyncFinish);
+
+    do_check_eq(Status.service, SYNC_FAILED_PARTIAL);
+    do_check_eq(Status.sync, SERVER_MAINTENANCE);
+
+    Svc.Obs.remove("weave:ui:sync:error", onSyncError);
+    clean();
+    server.stop(run_next_test);
+  });
+
+  setLastSync(NON_PROLONGED_ERROR_DURATION);
   Service.sync();
+});
+
+add_test(function test_info_collections_login_server_maintenance_error() {
+  // Test info/collections server maintenance errors are not reported.
+  let server = sync_httpd_setup();
+  setUp();
+
+  Service.clusterURL = "http://localhost:8080/maintenance/";
+
+  Service.username = "johnsmith";
+  let backoffInterval;
+  Svc.Obs.add("weave:service:backoff:interval", function observe(subject, data) {
+    Svc.Obs.remove("weave:service:backoff:interval", observe);
+    backoffInterval = subject;
+  });
+
+  function onUIUpdate() {
+    do_throw("Shouldn't get here!");
+  }
+  Svc.Obs.add("weave:ui:login:error", onUIUpdate);
+
+  do_check_false(Status.enforceBackoff);
+  do_check_eq(Status.service, STATUS_OK);
+
+  Svc.Obs.add("weave:ui:clear-error", function onLoginFinish() {
+    Svc.Obs.remove("weave:ui:clear-error", onLoginFinish);
+
+    do_check_true(Status.enforceBackoff);
+    do_check_eq(backoffInterval, 42);
+    do_check_eq(Status.service, LOGIN_FAILED);
+    do_check_eq(Status.login, SERVER_MAINTENANCE);
+
+    Svc.Obs.remove("weave:ui:login:error", onUIUpdate);
+    clean();
+    server.stop(run_next_test);
+  });
+
+  setLastSync(NON_PROLONGED_ERROR_DURATION);
+  Service.sync();
+});
+
+add_test(function test_meta_global_login_server_maintenance_error() {
+  // Test meta/global server maintenance errors are not reported.
+  let server = sync_httpd_setup();
+  setUp();
+
+  Service.clusterURL = "http://localhost:8080/maintenance/";
 
-  do_check_eq(Status.service, SYNC_FAILED_PARTIAL);
-  do_check_eq(Status.sync, SERVER_MAINTENANCE);
+  Service.username = "janesmith";
+  let backoffInterval;
+  Svc.Obs.add("weave:service:backoff:interval", function observe(subject, data) {
+    Svc.Obs.remove("weave:service:backoff:interval", observe);
+    backoffInterval = subject;
+  });
+
+  function onUIUpdate() {
+    do_throw("Shouldn't get here!");
+  }
+  Svc.Obs.add("weave:ui:login:error", onUIUpdate);
+
+  do_check_false(Status.enforceBackoff);
+  do_check_eq(Status.service, STATUS_OK);
+
+  Svc.Obs.add("weave:ui:clear-error", function onLoginFinish() {
+    Svc.Obs.remove("weave:ui:clear-error", onLoginFinish);
+
+    do_check_true(Status.enforceBackoff);
+    do_check_eq(backoffInterval, 42);
+    do_check_eq(Status.service, LOGIN_FAILED);
+    do_check_eq(Status.login, SERVER_MAINTENANCE);
+
+    Svc.Obs.remove("weave:ui:login:error", onUIUpdate);
+    clean();
+    server.stop(run_next_test);
+  });
+
+  setLastSync(NON_PROLONGED_ERROR_DURATION);
+  Service.sync();
+});
 
-  Svc.Obs.remove("weave:ui:sync:error", onUIUpdate);
-  Service.startOver();
-  Status.resetSync();
-  server.stop(run_next_test);
+add_test(function test_crypto_keys_login_server_maintenance_error() {
+  // Test crypto/keys server maintenance errors are not reported.
+  let server = sync_httpd_setup();
+  setUp();
+
+  Service.clusterURL = "http://localhost:8080/maintenance/";
+  Service.username = "foo";
+  // Force re-download of keys
+  CollectionKeys.clear();
+
+  let backoffInterval;
+  Svc.Obs.add("weave:service:backoff:interval", function observe(subject, data) {
+    Svc.Obs.remove("weave:service:backoff:interval", observe);
+    backoffInterval = subject;
+  });
+
+  function onUIUpdate() {
+    do_throw("Shouldn't get here!");
+  }
+  Svc.Obs.add("weave:ui:login:error", onUIUpdate);
+
+  do_check_false(Status.enforceBackoff);
+  do_check_eq(Status.service, STATUS_OK);
+
+  Svc.Obs.add("weave:ui:clear-error", function onLoginFinish() {
+    Svc.Obs.remove("weave:ui:clear-error", onLoginFinish);
+
+    do_check_true(Status.enforceBackoff);
+    do_check_eq(backoffInterval, 42);
+    do_check_eq(Status.service, LOGIN_FAILED);
+    do_check_eq(Status.login, SERVER_MAINTENANCE);
+
+    Svc.Obs.remove("weave:ui:login:error", onUIUpdate);
+    clean();
+    server.stop(run_next_test);
+  });
+
+  setLastSync(NON_PROLONGED_ERROR_DURATION);
+  Service.sync();
 });
 
 add_test(function test_sync_prolonged_server_maintenance_error() {
   // Test prolonged server maintenance errors are reported.
   let server = sync_httpd_setup();
   setUp();
 
   const BACKOFF = 42;
@@ -689,77 +858,374 @@ add_test(function test_sync_prolonged_se
   engine.exception = {status: 503,
                       headers: {"retry-after": BACKOFF}};
 
   Svc.Obs.add("weave:ui:sync:error", function onUIUpdate() {
     Svc.Obs.remove("weave:ui:sync:error", onUIUpdate);
     do_check_eq(Status.service, SYNC_FAILED);
     do_check_eq(Status.sync, PROLONGED_SYNC_FAILURE);
 
-    Service.startOver();
-    Status.resetSync();
+    clean();
     server.stop(run_next_test);
   });
 
   do_check_eq(Status.service, STATUS_OK);
 
   setLastSync(PROLONGED_ERROR_DURATION);
   Service.sync();
 });
 
-add_test(function test_syncAndReportErrors_server_maintenance_error() {
+add_test(function test_info_collections_login_prolonged_server_maintenance_error(){
+  // Test info/collections prolonged server maintenance errors are reported.
+  let server = sync_httpd_setup();
+  setUp();
+
+  Service.clusterURL = "http://localhost:8080/maintenance/";
+
+  Service.username = "johnsmith";
+  let backoffInterval;
+  Svc.Obs.add("weave:service:backoff:interval", function observe(subject, data) {
+    Svc.Obs.remove("weave:service:backoff:interval", observe);
+    backoffInterval = subject;
+  });
+
+  Svc.Obs.add("weave:ui:login:error", function onUIUpdate() {
+    Svc.Obs.remove("weave:ui:login:error", onUIUpdate);
+    do_check_true(Status.enforceBackoff);
+    do_check_eq(backoffInterval, 42);
+    do_check_eq(Status.service, SYNC_FAILED);
+    do_check_eq(Status.sync, PROLONGED_SYNC_FAILURE);
+
+    clean();
+    server.stop(run_next_test);
+  });
+
+  do_check_false(Status.enforceBackoff);
+  do_check_eq(Status.service, STATUS_OK);
+
+  setLastSync(PROLONGED_ERROR_DURATION);
+  Service.sync();
+});
+
+add_test(function test_meta_global_login_prolonged_server_maintenance_error(){
+  // Test meta/global prolonged server maintenance errors are reported.
+  let server = sync_httpd_setup();
+  setUp();
+
+  Service.clusterURL = "http://localhost:8080/maintenance/";
+
+  Service.username = "janesmith";
+  let backoffInterval;
+  Svc.Obs.add("weave:service:backoff:interval", function observe(subject, data) {
+    Svc.Obs.remove("weave:service:backoff:interval", observe);
+    backoffInterval = subject;
+  });
+
+  Svc.Obs.add("weave:ui:login:error", function onUIUpdate() {
+    Svc.Obs.remove("weave:ui:login:error", onUIUpdate);
+    do_check_true(Status.enforceBackoff);
+    do_check_eq(backoffInterval, 42);
+    do_check_eq(Status.service, SYNC_FAILED);
+    do_check_eq(Status.sync, PROLONGED_SYNC_FAILURE);
+
+    clean();
+    server.stop(run_next_test);
+  });
+
+  do_check_false(Status.enforceBackoff);
+  do_check_eq(Status.service, STATUS_OK);
+
+  setLastSync(PROLONGED_ERROR_DURATION);
+  Service.sync();
+});
+
+add_test(function test_crypto_keys_login_prolonged_server_maintenance_error(){
+  // Test crypto/keys prolonged server maintenance errors are reported.
+  let server = sync_httpd_setup();
+  setUp();
+
+  Service.clusterURL = "http://localhost:8080/maintenance/";
+  Service.username = "foo";
+  // Force re-download of keys
+  CollectionKeys.clear();
+
+  let backoffInterval;
+  Svc.Obs.add("weave:service:backoff:interval", function observe(subject, data) {
+    Svc.Obs.remove("weave:service:backoff:interval", observe);
+    backoffInterval = subject;
+  });
+
+  Svc.Obs.add("weave:ui:login:error", function onUIUpdate() {
+    Svc.Obs.remove("weave:ui:login:error", onUIUpdate);
+    do_check_true(Status.enforceBackoff);
+    do_check_eq(backoffInterval, 42);
+    do_check_eq(Status.service, SYNC_FAILED);
+    do_check_eq(Status.sync, PROLONGED_SYNC_FAILURE);
+
+    clean();
+    server.stop(run_next_test);
+  });
+
+  do_check_false(Status.enforceBackoff);
+  do_check_eq(Status.service, STATUS_OK);
+
+  setLastSync(PROLONGED_ERROR_DURATION);
+  Service.sync();
+});
+
+add_test(function test_sync_syncAndReportErrors_server_maintenance_error() {
   // Test server maintenance errors are reported
   // when calling syncAndReportErrors.
   let server = sync_httpd_setup();
   setUp();
 
   const BACKOFF = 42;
   let engine = Engines.get("catapult");
   engine.enabled = true;
   engine.exception = {status: 503,
                       headers: {"retry-after": BACKOFF}};
 
   Svc.Obs.add("weave:ui:sync:error", function onUIUpdate() {
     Svc.Obs.remove("weave:ui:sync:error", onUIUpdate);
     do_check_eq(Status.service, SYNC_FAILED_PARTIAL);
     do_check_eq(Status.sync, SERVER_MAINTENANCE);
 
-    Service.startOver();
-    Status.resetSync();
+    clean();
     server.stop(run_next_test);
   });
 
   do_check_eq(Status.service, STATUS_OK);
 
   setLastSync(NON_PROLONGED_ERROR_DURATION);
   ErrorHandler.syncAndReportErrors();
 });
 
-add_test(function test_syncAndReportErrors_prolonged_server_maintenance_error() {
+add_test(function test_info_collections_login_syncAndReportErrors_server_maintenance_error() {
+  // Test info/collections server maintenance errors are reported
+  // when calling syncAndReportErrors.
+  let server = sync_httpd_setup();
+  setUp();
+
+  Service.clusterURL = "http://localhost:8080/maintenance/";
+
+  Service.username = "johnsmith";
+  let backoffInterval;
+  Svc.Obs.add("weave:service:backoff:interval", function observe(subject, data) {
+    Svc.Obs.remove("weave:service:backoff:interval", observe);
+    backoffInterval = subject;
+  });
+
+  Svc.Obs.add("weave:ui:login:error", function onUIUpdate() {
+    Svc.Obs.remove("weave:ui:login:error", onUIUpdate);
+    do_check_true(Status.enforceBackoff);
+    do_check_eq(backoffInterval, 42);
+    do_check_eq(Status.service, LOGIN_FAILED);
+    do_check_eq(Status.login, SERVER_MAINTENANCE);
+
+    clean();
+    server.stop(run_next_test);
+  });
+
+  do_check_false(Status.enforceBackoff);
+  do_check_eq(Status.service, STATUS_OK);
+
+  setLastSync(NON_PROLONGED_ERROR_DURATION);
+  ErrorHandler.syncAndReportErrors();
+});
+
+add_test(function test_meta_global_login_syncAndReportErrors_server_maintenance_error() {
+  // Test meta/global server maintenance errors are reported
+  // when calling syncAndReportErrors.
+  let server = sync_httpd_setup();
+  setUp();
+
+  Service.clusterURL = "http://localhost:8080/maintenance/";
+
+  Service.username = "janesmith";
+  let backoffInterval;
+  Svc.Obs.add("weave:service:backoff:interval", function observe(subject, data) {
+    Svc.Obs.remove("weave:service:backoff:interval", observe);
+    backoffInterval = subject;
+  });
+
+  Svc.Obs.add("weave:ui:login:error", function onUIUpdate() {
+    Svc.Obs.remove("weave:ui:login:error", onUIUpdate);
+    do_check_true(Status.enforceBackoff);
+    do_check_eq(backoffInterval, 42);
+    do_check_eq(Status.service, LOGIN_FAILED);
+    do_check_eq(Status.login, SERVER_MAINTENANCE);
+
+    clean();
+    server.stop(run_next_test);
+  });
+
+  do_check_false(Status.enforceBackoff);
+  do_check_eq(Status.service, STATUS_OK);
+
+  setLastSync(NON_PROLONGED_ERROR_DURATION);
+  ErrorHandler.syncAndReportErrors();
+});
+
+add_test(function test_crypto_keys_login_syncAndReportErrors_server_maintenance_error() {
+  // Test crypto/keys server maintenance errors are reported
+  // when calling syncAndReportErrors.
+  let server = sync_httpd_setup();
+  setUp();
+
+  Service.clusterURL = "http://localhost:8080/maintenance/";
+  Service.username = "foo";
+  // Force re-download of keys
+  CollectionKeys.clear();
+
+  let backoffInterval;
+  Svc.Obs.add("weave:service:backoff:interval", function observe(subject, data) {
+    Svc.Obs.remove("weave:service:backoff:interval", observe);
+    backoffInterval = subject;
+  });
+
+  Svc.Obs.add("weave:ui:login:error", function onUIUpdate() {
+    Svc.Obs.remove("weave:ui:login:error", onUIUpdate);
+    do_check_true(Status.enforceBackoff);
+    do_check_eq(backoffInterval, 42);
+    do_check_eq(Status.service, LOGIN_FAILED);
+    do_check_eq(Status.login, SERVER_MAINTENANCE);
+
+    clean();
+    server.stop(run_next_test);
+  });
+
+  do_check_false(Status.enforceBackoff);
+  do_check_eq(Status.service, STATUS_OK);
+
+  setLastSync(NON_PROLONGED_ERROR_DURATION);
+  ErrorHandler.syncAndReportErrors();
+});
+
+add_test(function test_sync_syncAndReportErrors_prolonged_server_maintenance_error() {
   // Test prolonged server maintenance errors are
   // reported when calling syncAndReportErrors.
   let server = sync_httpd_setup();
   setUp();
 
   const BACKOFF = 42;
   let engine = Engines.get("catapult");
   engine.enabled = true;
   engine.exception = {status: 503,
                       headers: {"retry-after": BACKOFF}};
 
   Svc.Obs.add("weave:ui:sync:error", function onUIUpdate() {
     Svc.Obs.remove("weave:ui:sync:error", onUIUpdate);
     do_check_eq(Status.service, SYNC_FAILED_PARTIAL);
     do_check_eq(Status.sync, SERVER_MAINTENANCE);
 
-    Service.startOver();
-    Status.resetSync();
+    clean();
+    server.stop(run_next_test);
+  });
+
+  do_check_eq(Status.service, STATUS_OK);
+
+  setLastSync(PROLONGED_ERROR_DURATION);
+  ErrorHandler.syncAndReportErrors();
+});
+
+add_test(function test_info_collections_login_syncAndReportErrors_prolonged_server_maintenance_error() {
+  // Test info/collections server maintenance errors are reported
+  // when calling syncAndReportErrors.
+  let server = sync_httpd_setup();
+  setUp();
+
+  Service.clusterURL = "http://localhost:8080/maintenance/";
+
+  Service.username = "johnsmith";
+  let backoffInterval;
+  Svc.Obs.add("weave:service:backoff:interval", function observe(subject, data) {
+    Svc.Obs.remove("weave:service:backoff:interval", observe);
+    backoffInterval = subject;
+  });
+
+  Svc.Obs.add("weave:ui:login:error", function onUIUpdate() {
+    Svc.Obs.remove("weave:ui:login:error", onUIUpdate);
+    do_check_true(Status.enforceBackoff);
+    do_check_eq(backoffInterval, 42);
+    do_check_eq(Status.service, LOGIN_FAILED);
+    do_check_eq(Status.login, SERVER_MAINTENANCE);
+
+    clean();
     server.stop(run_next_test);
   });
 
+  do_check_false(Status.enforceBackoff);
+  do_check_eq(Status.service, STATUS_OK);
+
+  setLastSync(PROLONGED_ERROR_DURATION);
+  ErrorHandler.syncAndReportErrors();
+});
+
+add_test(function test_meta_global_login_syncAndReportErrors_prolonged_server_maintenance_error() {
+  // Test meta/global server maintenance errors are reported
+  // when calling syncAndReportErrors.
+  let server = sync_httpd_setup();
+  setUp();
+
+  Service.clusterURL = "http://localhost:8080/maintenance/";
+
+  Service.username = "janesmith";
+  let backoffInterval;
+  Svc.Obs.add("weave:service:backoff:interval", function observe(subject, data) {
+    Svc.Obs.remove("weave:service:backoff:interval", observe);
+    backoffInterval = subject;
+  });
+
+  Svc.Obs.add("weave:ui:login:error", function onUIUpdate() {
+    Svc.Obs.remove("weave:ui:login:error", onUIUpdate);
+    do_check_true(Status.enforceBackoff);
+    do_check_eq(backoffInterval, 42);
+    do_check_eq(Status.service, LOGIN_FAILED);
+    do_check_eq(Status.login, SERVER_MAINTENANCE);
+
+    clean();
+    server.stop(run_next_test);
+  });
+
+  do_check_false(Status.enforceBackoff);
+  do_check_eq(Status.service, STATUS_OK);
+
+  setLastSync(PROLONGED_ERROR_DURATION);
+  ErrorHandler.syncAndReportErrors();
+});
+
+add_test(function test_crypto_keys_login_syncAndReportErrors_prolonged_server_maintenance_error() {
+  // Test crypto/keys server maintenance errors are reported
+  // when calling syncAndReportErrors.
+  let server = sync_httpd_setup();
+  setUp();
+
+  Service.clusterURL = "http://localhost:8080/maintenance/";
+  Service.username = "foo";
+  // Force re-download of keys
+  CollectionKeys.clear();
+
+  let backoffInterval;
+  Svc.Obs.add("weave:service:backoff:interval", function observe(subject, data) {
+    Svc.Obs.remove("weave:service:backoff:interval", observe);
+    backoffInterval = subject;
+  });
+
+  Svc.Obs.add("weave:ui:login:error", function onUIUpdate() {
+    Svc.Obs.remove("weave:ui:login:error", onUIUpdate);
+    do_check_true(Status.enforceBackoff);
+    do_check_eq(backoffInterval, 42);
+    do_check_eq(Status.service, LOGIN_FAILED);
+    do_check_eq(Status.login, SERVER_MAINTENANCE);
+
+    clean();
+    server.stop(run_next_test);
+  });
+
+  do_check_false(Status.enforceBackoff);
   do_check_eq(Status.service, STATUS_OK);
 
   setLastSync(PROLONGED_ERROR_DURATION);
   ErrorHandler.syncAndReportErrors();
 });
 
 add_test(function test_sync_engine_generic_fail() {
   let server = sync_httpd_setup();
@@ -784,26 +1250,22 @@ add_test(function test_sync_engine_gener
 
     // Test Error log was written on SYNC_FAILED_PARTIAL.
     let entries = logsdir.directoryEntries;
     do_check_true(entries.hasMoreElements());
     let logfile = entries.getNext().QueryInterface(Ci.nsILocalFile);
     do_check_eq(logfile.leafName.slice(0, LOG_PREFIX_ERROR.length),
                 LOG_PREFIX_ERROR);
 
-    Status.resetSync();
-    Service.startOver();
-
+    clean();
     server.stop(run_next_test);
   });
 
   do_check_eq(Status.engines["catapult"], undefined);
-
   do_check_true(setUp());
-
   Service.sync();
 });
 
 // This test should be the last one since it monkeypatches the engine object
 // and we should only have one engine object throughout the file (bug 629664).
 add_test(function test_engine_applyFailed() {
   let server = sync_httpd_setup();
 
@@ -825,20 +1287,16 @@ add_test(function test_engine_applyFaile
 
     // Test Error log was written on SYNC_FAILED_PARTIAL.
     let entries = logsdir.directoryEntries;
     do_check_true(entries.hasMoreElements());
     let logfile = entries.getNext().QueryInterface(Ci.nsILocalFile);
     do_check_eq(logfile.leafName.slice(0, LOG_PREFIX_ERROR.length),
                 LOG_PREFIX_ERROR);
 
-    Status.resetSync();
-    Service.startOver();
-
+    clean();
     server.stop(run_next_test);
   });
 
   do_check_eq(Status.engines["catapult"], undefined);
-
   do_check_true(setUp());
-
   Service.sync();
 });
--- a/services/sync/tests/unit/test_score_triggers.js
+++ b/services/sync/tests/unit/test_score_triggers.js
@@ -1,48 +1,49 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 Cu.import("resource://services-sync/engines.js");
 Cu.import("resource://services-sync/engines/clients.js");
 Cu.import("resource://services-sync/constants.js");
 Cu.import("resource://services-sync/policies.js");
+Cu.import("resource://services-sync/status.js");
 
 Svc.DefaultPrefs.set("registerEngines", "");
 Cu.import("resource://services-sync/service.js");
 
 Engines.register(RotaryEngine);
 let engine = Engines.get("rotary");
 let tracker = engine._tracker;
 engine.enabled = true;
 
 // Tracking info/collections.
 let collectionsHelper = track_collections_helper();
 let upd = collectionsHelper.with_updated_collection;
 
 function sync_httpd_setup() {
-  let handlers = {};  
+  let handlers = {};
 
-  handlers["/1.1/johndoe/storage/meta/global"] = 
+  handlers["/1.1/johndoe/storage/meta/global"] =
     new ServerWBO("global", {}).handler();
-  handlers["/1.1/johndoe/storage/steam"] = 
+  handlers["/1.1/johndoe/storage/steam"] =
     new ServerWBO("steam", {}).handler();
 
   handlers["/1.1/johndoe/info/collections"] = collectionsHelper.handler;
   delete collectionsHelper.collections.crypto;
   delete collectionsHelper.collections.meta;
-  
+
   let cr = new ServerWBO("keys");
   handlers["/1.1/johndoe/storage/crypto/keys"] =
     upd("crypto", cr.handler());
-  
+
   let cl = new ServerCollection();
   handlers["/1.1/johndoe/storage/clients"] =
     upd("clients", cl.handler());
-  
+
   return httpd_setup(handlers);
 }
 
 function setUp() {
   Service.username = "johndoe";
   Service.password = "ilovejane";
   Service.passphrase = "sekrit";
   Service.clusterURL = "http://localhost:8080/";
@@ -88,16 +89,17 @@ add_test(function test_sync_triggered() 
 
   SyncScheduler.syncThreshold = MULTI_DEVICE_THRESHOLD;
   Svc.Obs.add("weave:service:sync:finish", function onSyncFinish() {
     Svc.Obs.remove("weave:service:sync:finish", onSyncFinish);
     _("Sync completed!");
     server.stop(run_next_test);
   });
 
+  do_check_eq(Status.login, LOGIN_SUCCEEDED);
   tracker.score += SCORE_INCREMENT_XLARGE;
 });
 
 add_test(function test_clients_engine_sync_triggered() {
   _("Ensure that client engine score changes trigger a sync.");
 
   // The clients engine is not registered like other engines. Therefore,
   // it needs special treatment throughout the code. Here, we verify the
@@ -110,11 +112,41 @@ add_test(function test_clients_engine_sy
   const TOPIC = "weave:service:sync:finish";
   Svc.Obs.add(TOPIC, function onSyncFinish() {
     Svc.Obs.remove(TOPIC, onSyncFinish);
     _("Sync due to clients engine change completed.");
     server.stop(run_next_test);
   });
 
   SyncScheduler.syncThreshold = MULTI_DEVICE_THRESHOLD;
+  do_check_eq(Status.login, LOGIN_SUCCEEDED);
   Clients._tracker.score += SCORE_INCREMENT_XLARGE;
 });
 
+add_test(function test_incorrect_credentials_sync_not_triggered() {
+  _("Ensure that score changes don't trigger a sync if Status.login != LOGIN_SUCCEEDED.");
+  let server = sync_httpd_setup();
+  setUp();
+
+  // Ensure we don't actually try to sync.
+  function onSyncStart() {
+    do_throw("Should not get here!");
+  }
+  Svc.Obs.add("weave:service:sync:start", onSyncStart);
+
+  // First wait >100ms (nsITimers can take up to that much time to fire, so
+  // we can account for the timer in delayedAutoconnect) and then one event
+  // loop tick (to account for a possible call to weave:service:sync:start).
+  Utils.namedTimer(function() {
+    Utils.nextTick(function() {
+      Svc.Obs.remove("weave:service:sync:start", onSyncStart);
+
+      do_check_eq(Status.login, LOGIN_FAILED_LOGIN_REJECTED);
+
+      Service.startOver();
+      server.stop(run_next_test);
+    });
+  }, 150, {}, "timer");
+
+  // Faking incorrect credentials to prevent score update.
+  Status.login = LOGIN_FAILED_LOGIN_REJECTED;
+  tracker.score += SCORE_INCREMENT_XLARGE;
+});
--- a/services/sync/tests/unit/test_service_verifyLogin.js
+++ b/services/sync/tests/unit/test_service_verifyLogin.js
@@ -74,24 +74,25 @@ function run_test() {
     do_check_eq(Status.service, STATUS_OK);
     do_check_eq(Status.login, LOGIN_SUCCEEDED);
 
     _("If verifyLogin() encounters a server error, it flips on the backoff flag and notifies observers on a 503 with Retry-After.");
     Status.resetSync();
     Service.username = "janedoe";
     do_check_false(Status.enforceBackoff);
     let backoffInterval;    
-    Svc.Obs.add("weave:service:backoff:interval", function(subject, data) {
+    Svc.Obs.add("weave:service:backoff:interval", function observe(subject, data) {
+      Svc.Obs.remove("weave:service:backoff:interval", observe);
       backoffInterval = subject;
     });
     do_check_false(Service.verifyLogin());
     do_check_true(Status.enforceBackoff);
     do_check_eq(backoffInterval, 42);
     do_check_eq(Status.service, LOGIN_FAILED);
-    do_check_eq(Status.login, LOGIN_FAILED_SERVER_ERROR);
+    do_check_eq(Status.login, SERVER_MAINTENANCE);
 
     _("Ensure a network error when finding the cluster sets the right Status bits.");
     Status.resetSync();
     Service.serverURL = "http://localhost:12345/";
     do_check_false(Service.verifyLogin());
     do_check_eq(Status.service, LOGIN_FAILED);
     do_check_eq(Status.login, LOGIN_FAILED_NETWORK_ERROR);
 
--- a/services/sync/tps/extensions/tps/modules/bookmarks.jsm
+++ b/services/sync/tps/extensions/tps/modules/bookmarks.jsm
@@ -36,26 +36,46 @@
  * ***** END LICENSE BLOCK ***** */
 
  /* This is a JavaScript module (JSM) to be imported via 
   * Components.utils.import() and acts as a singleton. Only the following 
   * listed symbols will exposed on import, and only when and where imported.
   */
 
 var EXPORTED_SYMBOLS = ["PlacesItem", "Bookmark", "Separator", "Livemark",
-                        "BookmarkFolder"];
+                        "BookmarkFolder", "DumpBookmarks"];
 
 const CC = Components.classes;
 const CI = Components.interfaces;
 const CU = Components.utils;
 
 CU.import("resource://tps/logger.jsm");
 CU.import("resource://gre/modules/Services.jsm");
 CU.import("resource://gre/modules/PlacesUtils.jsm");
 
+
+var DumpBookmarks = function TPS_Bookmarks__DumpBookmarks() {
+  let writer = {
+    value: "",
+    write: function PlacesItem__dump__write(aStr, aLen) {
+      this.value += aStr;
+    }
+  };
+
+  let options = PlacesUtils.history.getNewQueryOptions();
+  options.queryType = options.QUERY_TYPE_BOOKMARKS;
+  let query = PlacesUtils.history.getNewQuery();
+  query.setFolders([PlacesUtils.placesRootId], 1);
+  let root = PlacesUtils.history.executeQuery(query, options).root;
+  root.containerOpen = true;
+  PlacesUtils.serializeNodeAsJSONToOutputStream(root, writer, true, false);
+  let value = JSON.parse(writer.value);
+  Logger.logInfo("dumping bookmarks\n\n" + JSON.stringify(value, null, ' ') + "\n\n");
+};
+
 /**
  * extend, causes a child object to inherit from a parent
  */
 function extend(child, supertype)
 {
    child.prototype.__proto__ = supertype.prototype;
 }
 
--- a/services/sync/tps/extensions/tps/modules/logger.jsm
+++ b/services/sync/tps/extensions/tps/modules/logger.jsm
@@ -52,16 +52,25 @@ var Logger =
   _potentialError: null,
 
   init: function (path) {
     if (this._converter != null) {
       // we're already open!
       return;
     }
 
+    let prefs = CC["@mozilla.org/preferences-service;1"]
+                .getService(CI.nsIPrefBranch);
+    if (path) {
+      prefs.setCharPref("tps.logfile", path);
+    }
+    else {
+      path = prefs.getCharPref("tps.logfile");
+    }
+
     this._file = CC["@mozilla.org/file/local;1"]
                  .createInstance(CI.nsILocalFile);
     this._file.initWithPath(path);
     var exists = this._file.exists();
 
     // Make a file output stream and converter to handle it.
     this._foStream = CC["@mozilla.org/network/file-output-stream;1"]
                      .createInstance(CI.nsIFileOutputStream);
new file mode 100644
--- /dev/null
+++ b/services/sync/tps/extensions/tps/modules/sync.jsm
@@ -0,0 +1,135 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is TPS.
+ *
+ * The Initial Developer of the Original Code is Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Jonathan Griffin <jgriffin@mozilla.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+
+var EXPORTED_SYMBOLS = ["TPS", "SYNC_WIPE_SERVER", "SYNC_RESET_CLIENT",
+                        "SYNC_WIPE_CLIENT"];
+
+const CC = Components.classes;
+const CI = Components.interfaces;
+const CU = Components.utils;
+
+CU.import("resource://gre/modules/XPCOMUtils.jsm");
+CU.import("resource://gre/modules/Services.jsm");
+CU.import("resource://tps/logger.jsm");
+CU.import("resource://services-sync/service.js");
+CU.import("resource://services-sync/util.js");
+var utils = {}; CU.import('resource://mozmill/modules/utils.js', utils);
+
+const SYNC_WIPE_SERVER = "wipe-server";
+const SYNC_RESET_CLIENT = "reset-client";
+const SYNC_WIPE_CLIENT = "wipe-client";
+
+var prefs = CC["@mozilla.org/preferences-service;1"]
+            .getService(CI.nsIPrefBranch);
+
+var syncFinishedCallback = function() {
+  Logger.logInfo('syncFinishedCallback returned ' + !TPS._waitingForSync);
+  return !TPS._waitingForSync;
+};
+
+var TPS = {
+  _waitingForSync: false,
+  _syncErrors: 0,
+
+  QueryInterface: XPCOMUtils.generateQI([CI.nsIObserver,
+                                         CI.nsISupportsWeakReference]),
+
+  observe: function TPS__observe(subject, topic, data) {