Merge m-c to m-i.
authorMs2ger <ms2ger@gmail.com>
Fri, 30 Dec 2011 12:17:22 +0100
changeset 84795 9fdaea5d67e2f1f97045700e7fe92b4eb277f12b
parent 84726 0d684c34d1e4e7c0bd6779711119edac141ae106 (current diff)
parent 84794 26acd30cab90ae882f55e3453ab5cbe18a3ed6fa (diff)
child 84796 4dd80e8ee29e8539d0d81aea2b4229919bd7ea5d
child 84801 aee26fea635fca5ee712b161a3e65d589b4b7d0a
push id805
push userakeybl@mozilla.com
push dateWed, 01 Feb 2012 18:17:35 +0000
treeherdermozilla-aurora@6fb3bf232436 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone12.0a1
Merge m-c to m-i.
content/base/src/nsGenericElement.cpp
gfx/ycbcr/QuellGccWarnings.patch
--- a/browser/base/content/browser-context.inc
+++ b/browser/base/content/browser-context.inc
@@ -41,16 +41,20 @@
       <menuseparator id="page-menu-separator"/>
       <menuitem id="spell-no-suggestions"
                 disabled="true"
                 label="&spellNoSuggestions.label;"/>
       <menuitem id="spell-add-to-dictionary"
                 label="&spellAddToDictionary.label;"
                 accesskey="&spellAddToDictionary.accesskey;"
                 oncommand="InlineSpellCheckerUI.addToDictionary();"/>
+      <menuitem id="spell-undo-add-to-dictionary"
+                label="&spellUndoAddToDictionary.label;"
+                accesskey="&spellUndoAddToDictionary.accesskey;"
+                oncommand="InlineSpellCheckerUI.undoAddToDictionary();" />
       <menuseparator id="spell-suggestions-separator"/>
       <menuitem id="context-openlinkincurrent"
                 label="&openLinkCmdInCurrent.label;"
                 accesskey="&openLinkCmdInCurrent.accesskey;"
                 oncommand="gContextMenu.openLinkInCurrent();"/>
       <menuitem id="context-openlinkintab"
                 label="&openLinkCmdInTab.label;"
                 accesskey="&openLinkCmdInTab.accesskey;"
--- a/browser/base/content/nsContextMenu.js
+++ b/browser/base/content/nsContextMenu.js
@@ -332,16 +332,17 @@ nsContextMenu.prototype = {
     var canSpell = InlineSpellCheckerUI.canSpellCheck;
     var onMisspelling = InlineSpellCheckerUI.overMisspelling;
     this.showItem("spell-check-enabled", canSpell);
     this.showItem("spell-separator", canSpell || this.onEditableArea);
     document.getElementById("spell-check-enabled")
             .setAttribute("checked", canSpell && InlineSpellCheckerUI.enabled);
 
     this.showItem("spell-add-to-dictionary", onMisspelling);
+    this.showItem("spell-undo-add-to-dictionary", InlineSpellCheckerUI.canUndo());
 
     // suggestion list
     this.showItem("spell-suggestions-separator", onMisspelling);
     if (onMisspelling) {
       var suggestionsSeparator =
         document.getElementById("spell-add-to-dictionary");
       var numsug =
         InlineSpellCheckerUI.addSuggestionsToMenu(suggestionsSeparator.parentNode,
--- a/browser/base/content/test/test_contextmenu.html
+++ b/browser/base/content/test/test_contextmenu.html
@@ -14,16 +14,17 @@ Browser context menu tests.
 </div>
 
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
 /** Test for Login Manager: multiple login autocomplete. **/
 
 netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
+Components.utils.import("resource://gre/modules/InlineSpellChecker.jsm");
 
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 
 function openContextMenuFor(element, shiftkey, shouldWaitForFocus) {
     // Context menu should be closed before we open it again.
     is(contextMenu.state, "closed", "checking if popup is closed");
 
@@ -509,22 +510,46 @@ function runTest(testNum) {
                           "context-selectall",   true,
                           "---",                 null,
                           "spell-check-enabled", true,
                           "spell-dictionaries",  true,
                               ["spell-check-dictionary-en-US", true,
                                "---",                          null,
                                "spell-add-dictionaries",       true], null,
                          ].concat(inspectItems));
-
+        contextMenu.ownerDocument.getElementById("spell-add-to-dictionary").doCommand(); // Add to dictionary
         closeContextMenu();
-        openContextMenuFor(contenteditable); // Invoke context menu for next test.
+        openContextMenuFor(textarea, false, true); // Invoke context menu for next test.
+        break;
+    
+    case 15:    
+        // Context menu for textarea after a word has been added
+        // to the dictionary
+        checkContextMenu(["spell-undo-add-to-dictionary", true,
+                          "context-undo",        false,
+                          "---",                 null,
+                          "context-cut",         false,
+                          "context-copy",        false,
+                          "context-paste",       null, // ignore clipboard state
+                          "context-delete",      false,
+                          "---",                 null,
+                          "context-selectall",   true,
+                          "---",                 null,
+                          "spell-check-enabled", true,
+                          "spell-dictionaries",  true,
+                              ["spell-check-dictionary-en-US", true,
+                               "---",                          null,
+                               "spell-add-dictionaries",       true], null,
+                         ].concat(inspectItems));
+        contextMenu.ownerDocument.getElementById("spell-undo-add-to-dictionary").doCommand(); // Undo add to dictionary
+        closeContextMenu();
+        openContextMenuFor(contenteditable);
         break;
 
-    case 15:
+    case 16:
         // Context menu for contenteditable
         checkContextMenu(["spell-no-suggestions", false,
                           "spell-add-to-dictionary", true,
                           "---",                 null,
                           "context-undo",        false,
                           "---",                 null,
                           "context-cut",         false,
                           "context-copy",        false,
@@ -539,17 +564,17 @@ function runTest(testNum) {
                                "---",                          null,
                                "spell-add-dictionaries",       true], null
                          ].concat(inspectItems));
 
         closeContextMenu();
         openContextMenuFor(inputspell); // Invoke context menu for next test.
         break;
 
-    case 16:
+    case 17:
         // Context menu for spell-check input
         checkContextMenu(["*prodigality",        true, // spelling suggestion
                           "spell-add-to-dictionary", true,
                           "---",                 null,
                           "context-undo",        false,
                           "---",                 null,
                           "context-cut",         false,
                           "context-copy",        false,
@@ -564,23 +589,23 @@ function runTest(testNum) {
                                "---",                          null,
                                "spell-add-dictionaries",       true], null
                          ].concat(inspectItems));
 
         closeContextMenu();
         openContextMenuFor(link); // Invoke context menu for next test.
         break;
 
-    case 17:
+    case 18:
         executeCopyCommand("cmd_copyLink", "http://mozilla.com/");
         closeContextMenu();
         openContextMenuFor(pagemenu); // Invoke context menu for next test.
         break;
 
-    case 18:
+    case 19:
         // Context menu for element with assigned content context menu
         checkContextMenu(["+Plain item",          {type: "", icon: "", checked: false, disabled: false},
                           "+Disabled item",       {type: "", icon: "", checked: false, disabled: true},
                           "+Item w/ textContent", {type: "", icon: "", checked: false, disabled: false},
                           "---",                  null,
                           "+Checkbox",            {type: "checkbox", icon: "", checked: true, disabled: false},
                           "---",                  null,
                           "+Radio1",              {type: "checkbox", icon: "", checked: true, disabled: false},
@@ -613,17 +638,17 @@ function runTest(testNum) {
                           "context-viewinfo",     true
                          ].concat(inspectItems));
 
         invokeItemAction("0");
         closeContextMenu();
         openContextMenuFor(pagemenu, true); // Invoke context menu for next test.
         break;
 
-    case 19:
+    case 20:
         // Context menu for element with assigned content context menu
         // The shift key should bypass content context menu processing
         checkContextMenu(["context-back",         false,
                           "context-forward",      false,
                           "context-reload",       true,
                           "context-stop",         false,
                           "---",                  null,
                           "context-bookmarkpage", true,
--- a/browser/components/shell/src/nsGNOMEShellService.cpp
+++ b/browser/components/shell/src/nsGNOMEShellService.cpp
@@ -312,21 +312,19 @@ nsGNOMEShellService::SetDefaultBrowser(b
     nsCOMPtr<nsIStringBundleService> bundleService =
       do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv);
     NS_ENSURE_SUCCESS(rv, rv);
 
     nsCOMPtr<nsIStringBundle> brandBundle;
     rv = bundleService->CreateBundle(BRAND_PROPERTIES, getter_AddRefs(brandBundle));
     NS_ENSURE_SUCCESS(rv, rv);
 
-    nsString brandShortName, brandFullName;
+    nsString brandShortName;
     brandBundle->GetStringFromName(NS_LITERAL_STRING("brandShortName").get(),
                                    getter_Copies(brandShortName));
-    brandBundle->GetStringFromName(NS_LITERAL_STRING("brandFullName").get(),
-                                   getter_Copies(brandFullName));
 
     // use brandShortName as the application id.
     NS_ConvertUTF16toUTF8 id(brandShortName);
     nsCOMPtr<nsIGIOMimeApp> appInfo;
     rv = giovfs->CreateAppFromCommand(mAppPath,
                                       id,
                                       getter_AddRefs(appInfo));
     NS_ENSURE_SUCCESS(rv, rv);
--- a/configure.in
+++ b/configure.in
@@ -331,16 +331,17 @@ if test -n "$gonkdir" ; then
     if test -z "$HOST_CXXFLAGS" ; then
         HOST_CXXFLAGS=" "
     fi
     if test -z "$HOST_LDFLAGS" ; then
         HOST_LDFLAGS=" "
     fi
 
     AC_DEFINE(ANDROID)
+    AC_DEFINE(HAVE_SYS_UIO_H)
     CROSS_COMPILE=1
     MOZ_CHROME_FILE_FORMAT=omni
     ZLIB_DIR=yes
     direct_nspr_config=1
 else
 case "$target" in
 *-android*|*-linuxandroid*)
     if test -z "$android_ndk" ; then
@@ -1859,65 +1860,46 @@ if test "$GNU_CC"; then
     fi
     WARNINGS_AS_ERRORS='-Werror'
     DSO_CFLAGS=''
     DSO_PIC_CFLAGS='-fPIC'
     ASFLAGS="$ASFLAGS -fPIC"
     _MOZ_RTTI_FLAGS_ON=-frtti
     _MOZ_RTTI_FLAGS_OFF=-fno-rtti
 
-    # Turn on GNU-specific warnings:
-    # -Wall - turn on a lot of warnings
-    # -pedantic - this is turned on below
-    # -Wpointer-arith - enabled with -pedantic, but good to have even if not
-    # -Werror=declaration-after-statement - MSVC doesn't like these
-    # -Wempty-body - catches bugs, e.g. "if (c); foo();", few false positives
-    #
-    _WARNINGS_CFLAGS="${_WARNINGS_CFLAGS} -Wall -Wpointer-arith -Wdeclaration-after-statement -Wempty-body"
-    
-    # Turn off the following warnings that -Wall/-pedantic turn on:
-    # -Woverlength-strings - we exceed the minimum maximum length all the time
-    #
-    _WARNINGS_CFLAGS="${_WARNINGS_CFLAGS} -Wno-overlength-strings"
-
+    # Turn on GNU specific features
+    # -Wall - turn on all warnings
+    # -pedantic - make compiler warn about non-ANSI stuff, and
+    #             be a little bit stricter
+    # -Wdeclaration-after-statement - MSVC doesn't like these
+    # Warnings slamm took out for now (these were giving more noise than help):
+    # -Wbad-function-cast - warns when casting a function to a new return type
+    # -Wshadow - removed because it generates more noise than help --pete
+    _WARNINGS_CFLAGS="${_WARNINGS_CFLAGS} -Wall -W -Wno-unused -Wpointer-arith -Wdeclaration-after-statement"
     if test -z "$INTEL_CC" -a -z "$CLANG_CC"; then
        # Don't use -Wcast-align with ICC or clang
        case "$CPU_ARCH" in
            # And don't use it on hppa, ia64, sparc, arm, since it's noisy there
            hppa | ia64 | sparc | arm)
            ;;
            *)
         _WARNINGS_CFLAGS="${_WARNINGS_CFLAGS} -Wcast-align"
            ;;
        esac
     fi
 
     dnl Turn pedantic on but disable the warnings for long long
     _PEDANTIC=1
 
+    if test -z "$INTEL_CC"; then
+      _WARNINGS_CFLAGS="${_WARNINGS_CFLAGS} -W"
+    fi
+
     _DEFINES_CFLAGS='-include $(DEPTH)/mozilla-config.h -DMOZILLA_CLIENT'
     _USE_CPP_INCLUDE_FLAG=1
-
-    AC_CACHE_CHECK(whether the compiler supports -Wtype-limits,
-                   ac_cc_has_wtype_limits,
-        [
-            AC_LANG_SAVE
-            AC_LANG_C
-            _SAVE_CFLAGS="$CFLAGS"
-            CFLAGS="$CFLAGS -Wtype-limits"
-            AC_TRY_COMPILE([],
-                           [return(0);],
-                           ac_cc_has_wtype_limits="yes",
-                           ac_cc_has_wtype_limits="no")
-            CFLAGS="$_SAVE_CFLAGS"
-            AC_LANG_RESTORE
-        ])
-    if test "$ac_cc_has_wtype_limits" = "yes"; then
-        _WARNINGS_CFLAGS="${_WARNINGS_CFLAGS} -Wtype-limits"
-    fi
 elif test "$SOLARIS_SUNPRO_CC"; then
     DSO_CFLAGS=''
     if test "$CPU_ARCH" = "sparc"; then
         # for Sun Studio on Solaris/SPARC
         DSO_PIC_CFLAGS='-xcode=pic32'
     else
         DSO_PIC_CFLAGS='-KPIC'
     fi
@@ -1935,32 +1917,18 @@ else
     DSO_CFLAGS=''
     DSO_PIC_CFLAGS='-KPIC'
     _DEFINES_CFLAGS='$(ACDEFINES) -D_MOZILLA_CONFIG_H_ -DMOZILLA_CLIENT'
 fi
 
 if test "$GNU_CXX"; then
     # FIXME: Let us build with strict aliasing. bug 414641.
     CXXFLAGS="$CXXFLAGS -fno-exceptions -fno-strict-aliasing"
-
-    # Turn on GNU-specific warnings:
-    # -Wall - turn on a lot of warnings
-    # -pedantic - this is turned on below
-    # -Wpointer-arith - enabled with -pedantic, but good to have even if not
-    # -Woverloaded-virtual - ???
-    # -Wempty-body - catches bugs, e.g. "if (c); foo();", few false positives
-    #
-    _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Wall -Wpointer-arith -Woverloaded-virtual -Wempty-body"
-
-    # Turn off the following warnings that -Wall/-pedantic turn on:
-    # -Woverlength-strings - we exceed the minimum maximum length all the time
-    # -Wctor-dtor-privacy - ???
-    #
-    _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Wno-overlength-strings -Wno-ctor-dtor-privacy"
-
+    # Turn on GNU specific features
+    _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Wall -Wpointer-arith -Woverloaded-virtual -Wsynth -Wno-ctor-dtor-privacy -Wno-non-virtual-dtor"
     if test -z "$INTEL_CXX" -a -z "$CLANG_CXX"; then
        # Don't use -Wcast-align with ICC or clang
        case "$CPU_ARCH" in
            # And don't use it on hppa, ia64, sparc, arm, since it's noisy there
            hppa | ia64 | sparc | arm)
            ;;
            *)
         _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Wcast-align"
@@ -2051,34 +2019,16 @@ if test "$GNU_CXX"; then
                            ac_has_werror_return_type="no")
             CXXFLAGS="$_SAVE_CXXFLAGS"
             AC_LANG_RESTORE
         ])
     if test "$ac_has_werror_return_type" = "yes"; then
         _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Werror=return-type"
     fi
 
-    AC_CACHE_CHECK(whether the compiler supports -Wtype-limits,
-                   ac_has_wtype_limits,
-        [
-            AC_LANG_SAVE
-            AC_LANG_CPLUSPLUS
-            _SAVE_CXXFLAGS="$CXXFLAGS"
-            CXXFLAGS="$CXXFLAGS -Wtype-limits"
-            AC_TRY_COMPILE([],
-                           [return(0);],
-                           ac_has_wtype_limits="yes",
-                           ac_has_wtype_limits="no")
-            CXXFLAGS="$_SAVE_CXXFLAGS"
-            AC_LANG_RESTORE
-        ])
-    if test "$ac_has_wtype_limits" = "yes"; then
-        _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Wtype-limits"
-    fi
-
 else
     _DEFINES_CXXFLAGS='-DMOZILLA_CLIENT -D_MOZILLA_CONFIG_H_ $(ACDEFINES)'
 fi
 
 dnl gcc can come with its own linker so it is better to use the pass-thru calls
 dnl MKSHLIB_FORCE_ALL is used to force the linker to include all object
 dnl files present in an archive. MKSHLIB_UNFORCE_ALL reverts the linker to
 dnl normal behavior.
--- a/content/base/src/nsGenericElement.cpp
+++ b/content/base/src/nsGenericElement.cpp
@@ -2124,17 +2124,18 @@ nsGenericElement::GetBoundingClientRect(
   
   nsIFrame* frame = GetPrimaryFrame(Flush_Layout);
   if (!frame) {
     // display:none, perhaps? Return the empty rect
     return NS_OK;
   }
 
   nsRect r = nsLayoutUtils::GetAllInFlowRectsUnion(frame,
-          nsLayoutUtils::GetContainingBlockForClientRect(frame));
+          nsLayoutUtils::GetContainingBlockForClientRect(frame),
+          nsLayoutUtils::RECTS_ACCOUNT_FOR_TRANSFORMS);
   rect->SetLayoutRect(r);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsGenericElement::GetElementsByClassName(const nsAString& aClasses,
                                          nsIDOMNodeList** aReturn)
 {
@@ -2152,17 +2153,18 @@ nsGenericElement::GetClientRects(nsIDOMC
   if (!frame) {
     // display:none, perhaps? Return an empty list
     *aResult = rectList.forget().get();
     return NS_OK;
   }
 
   nsLayoutUtils::RectListBuilder builder(rectList);
   nsLayoutUtils::GetAllInFlowRects(frame,
-          nsLayoutUtils::GetContainingBlockForClientRect(frame), &builder);
+          nsLayoutUtils::GetContainingBlockForClientRect(frame), &builder,
+          nsLayoutUtils::RECTS_ACCOUNT_FOR_TRANSFORMS);
   if (NS_FAILED(builder.mRV))
     return builder.mRV;
   *aResult = rectList.forget().get();
   return NS_OK;
 }
 
 
 //----------------------------------------------------------------------
--- a/content/xslt/src/base/txExpandedNameMap.h
+++ b/content/xslt/src/base/txExpandedNameMap.h
@@ -97,26 +97,28 @@ protected:
 
         bool next()
         {
             return ++mCurrentPos < mMap.mItems.Length();
         }
 
         const txExpandedName key()
         {
-            NS_ASSERTION(mCurrentPos < mMap.mItems.Length(),
+            NS_ASSERTION(mCurrentPos >= 0 &&
+                         mCurrentPos < mMap.mItems.Length(),
                          "invalid position in txExpandedNameMap::iterator");
             return txExpandedName(mMap.mItems[mCurrentPos].mNamespaceID,
                                   mMap.mItems[mCurrentPos].mLocalName);
         }
 
     protected:
         void* itemValue()
         {
-            NS_ASSERTION(mCurrentPos < mMap.mItems.Length(),
+            NS_ASSERTION(mCurrentPos >= 0 &&
+                         mCurrentPos < mMap.mItems.Length(),
                          "invalid position in txExpandedNameMap::iterator");
             return mMap.mItems[mCurrentPos].mValue;
         }
 
     private:
         txExpandedNameMap_base& mMap;
         PRUint32 mCurrentPos;
     };
--- a/docshell/test/browser/browser_bug435325.js
+++ b/docshell/test/browser/browser_bug435325.js
@@ -7,18 +7,18 @@
 function test() {
   waitForExplicitFinish();
 
   gBrowser.selectedTab = gBrowser.addTab();
   window.addEventListener("DOMContentLoaded", checkPage, false);
 
   // Go offline and disable the cache, then try to load the test URL.
   Services.io.offline = true;
-  gPrefService.setBoolPref("browser.cache.disk.enable", false);
-  gPrefService.setBoolPref("browser.cache.memory.enable", false);
+  Services.prefs.setBoolPref("browser.cache.disk.enable", false);
+  Services.prefs.setBoolPref("browser.cache.memory.enable", false);
   content.location = "http://example.com/";
 }
 
 function checkPage() {
   if(content.location == "about:blank") {
     info("got about:blank, which is expected once, so return");
     return;
   }
@@ -37,13 +37,13 @@ function checkPage() {
   ok(!Services.io.offline, "After clicking the Try Again button, we're back "
    +" online. This depends on Components.interfaces.nsIDOMWindowUtils being "
    +"available from untrusted content (bug 435325).");
 
   finish();
 }
 
 registerCleanupFunction(function() {
-  gPrefService.setBoolPref("browser.cache.disk.enable", true);
-  gPrefService.setBoolPref("browser.cache.memory.enable", true);
+  Services.prefs.setBoolPref("browser.cache.disk.enable", true);
+  Services.prefs.setBoolPref("browser.cache.memory.enable", true);
   Services.io.offline = false;
   gBrowser.removeCurrentTab();
 });
--- a/dom/base/nsDOMMemoryReporter.cpp
+++ b/dom/base/nsDOMMemoryReporter.cpp
@@ -225,8 +225,17 @@ nsDOMMemoryMultiReporter::CollectReports
   nsRefPtr<nsGlobalWindow> *end = w + windows.Length();
   for (; w != end; ++w) {
     CollectWindowMemoryUsage(*w, aCb, aClosure);
   }
 
   return NS_OK;
 }
 
+NS_IMETHODIMP
+nsDOMMemoryMultiReporter::GetExplicitNonHeap(PRInt64* aAmount)
+{
+  // This reporter only measures heap memory.
+  *aAmount = 0;
+  return NS_OK;
+}
+
+
--- a/dom/base/nsJSEnvironment.cpp
+++ b/dom/base/nsJSEnvironment.cpp
@@ -124,29 +124,32 @@ static PRLogModuleInfo* gJSDiagnostics;
 #ifdef CompareString
 #undef CompareString
 #endif
 
 // The amount of time we wait between a request to GC (due to leaving
 // a page) and doing the actual GC.
 #define NS_GC_DELAY                 4000 // ms
 
+#define NS_SHRINK_GC_BUFFERS_DELAY  4000 // ms
+
 // The amount of time we wait from the first request to GC to actually
 // doing the first GC.
 #define NS_FIRST_GC_DELAY           10000 // ms
 
 // The amount of time we wait between a request to CC (after GC ran)
 // and doing the actual CC.
 #define NS_CC_DELAY                 5000 // ms
 
 #define JAVASCRIPT nsIProgrammingLanguage::JAVASCRIPT
 
 // if you add statics here, add them to the list in nsJSRuntime::Startup
 
 static nsITimer *sGCTimer;
+static nsITimer *sShrinkGCBuffersTimer;
 static nsITimer *sCCTimer;
 
 static bool sGCHasRun;
 
 // The number of currently pending document loads. This count isn't
 // guaranteed to always reflect reality and can't easily as we don't
 // have an easy place to know when a load ends or is interrupted in
 // all cases. This counter also gets reset if we end up GC'ing while
@@ -1093,34 +1096,35 @@ nsJSContext::~nsJSContext()
     NS_IF_RELEASE(sRuntimeService);
     NS_IF_RELEASE(sSecurityManager);
   }
 }
 
 void
 nsJSContext::DestroyJSContext()
 {
-  if (!mContext)
+  if (!mContext) {
     return;
+  }
 
   // Clear our entry in the JSContext, bugzilla bug 66413
   ::JS_SetContextPrivate(mContext, nsnull);
 
   // Unregister our "javascript.options.*" pref-changed callback.
   Preferences::UnregisterCallback(JSOptionChangedCallback,
                                   js_options_dot_str, this);
 
-  bool do_gc = mGCOnDestruction && !sGCTimer;
-
+  if (mGCOnDestruction) {
+    PokeGC();
+  }
+        
   // Let xpconnect destroy the JSContext when it thinks the time is right.
   nsIXPConnect *xpc = nsContentUtils::XPConnect();
   if (xpc) {
-    xpc->ReleaseJSContext(mContext, !do_gc);
-  } else if (do_gc) {
-    ::JS_DestroyContext(mContext);
+    xpc->ReleaseJSContext(mContext, true);
   } else {
     ::JS_DestroyContextNoGC(mContext);
   }
   mContext = nsnull;
 }
 
 // QueryInterface implementation for nsJSContext
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsJSContext)
@@ -3219,31 +3223,44 @@ nsJSContext::ScriptExecuted()
 //static
 void
 nsJSContext::GarbageCollectNow(bool shrinkingGC)
 {
   NS_TIME_FUNCTION_MIN(1.0);
   SAMPLE_LABEL("GC", "GarbageCollectNow");
 
   KillGCTimer();
+  KillShrinkGCBuffersTimer();
 
   // Reset sPendingLoadCount in case the timer that fired was a
   // timer we scheduled due to a normal GC timer firing while
   // documents were loading. If this happens we're waiting for a
   // document that is taking a long time to load, and we effectively
   // ignore the fact that the currently loading documents are still
   // loading and move on as if they weren't.
   sPendingLoadCount = 0;
   sLoadingInProgress = false;
 
   if (nsContentUtils::XPConnect()) {
     nsContentUtils::XPConnect()->GarbageCollect(shrinkingGC);
   }
 }
 
+//static
+void
+nsJSContext::ShrinkGCBuffersNow()
+{
+  NS_TIME_FUNCTION_MIN(1.0);
+  SAMPLE_LABEL("GC", "ShrinkGCBuffersNow");
+
+  KillShrinkGCBuffersTimer();
+
+  JS_ShrinkGCBuffers(nsJSRuntime::sRuntime);
+}
+
 //Static
 void
 nsJSContext::CycleCollectNow(nsICycleCollectorListener *aListener)
 {
   if (!NS_IsMainThread()) {
     return;
   }
 
@@ -3291,16 +3308,24 @@ nsJSContext::CycleCollectNow(nsICycleCol
 void
 GCTimerFired(nsITimer *aTimer, void *aClosure)
 {
   NS_RELEASE(sGCTimer);
 
   nsJSContext::GarbageCollectNow();
 }
 
+void
+ShrinkGCBuffersTimerFired(nsITimer *aTimer, void *aClosure)
+{
+  NS_RELEASE(sShrinkGCBuffersTimer);
+
+  nsJSContext::ShrinkGCBuffersNow();
+}
+
 // static
 void
 CCTimerFired(nsITimer *aTimer, void *aClosure)
 {
   NS_RELEASE(sCCTimer);
 
   nsJSContext::CycleCollectNow();
 }
@@ -3356,16 +3381,36 @@ nsJSContext::PokeGC()
                                  : NS_GC_DELAY,
                                  nsITimer::TYPE_ONE_SHOT);
 
   first = false;
 }
 
 // static
 void
+nsJSContext::PokeShrinkGCBuffers()
+{
+  if (sShrinkGCBuffersTimer) {
+    return;
+  }
+
+  CallCreateInstance("@mozilla.org/timer;1", &sShrinkGCBuffersTimer);
+
+  if (!sShrinkGCBuffersTimer) {
+    // Failed to create timer (probably because we're in XPCOM shutdown)
+    return;
+  }
+
+  sShrinkGCBuffersTimer->InitWithFuncCallback(ShrinkGCBuffersTimerFired, nsnull,
+                                              NS_SHRINK_GC_BUFFERS_DELAY,
+                                              nsITimer::TYPE_ONE_SHOT);
+}
+
+// static
+void
 nsJSContext::MaybePokeCC()
 {
   if (nsCycleCollector_suspectedCount() > 1000) {
     PokeCC();
   }
 }
 
 // static
@@ -3397,16 +3442,27 @@ nsJSContext::KillGCTimer()
     sGCTimer->Cancel();
 
     NS_RELEASE(sGCTimer);
   }
 }
 
 //static
 void
+nsJSContext::KillShrinkGCBuffersTimer()
+{
+  if (sShrinkGCBuffersTimer) {
+    sShrinkGCBuffersTimer->Cancel();
+
+    NS_RELEASE(sShrinkGCBuffersTimer);
+  }
+}
+
+//static
+void
 nsJSContext::KillCCTimer()
 {
   if (sCCTimer) {
     sCCTimer->Cancel();
 
     NS_RELEASE(sCCTimer);
   }
 }
@@ -3460,20 +3516,21 @@ DOMGCFinishedCallback(JSRuntime *rt, JSC
   } else {
     // If this was a full GC, poke the CC to run soon.
     if (!comp) {
       sGCHasRun = true;
       nsJSContext::PokeCC();
     }
   }
 
-  // If we didn't end up scheduling a GC, and there are unused
-  // chunks waiting to expire, make sure we will GC again soon.
-  if (!sGCTimer && JS_GetGCParameter(rt, JSGC_UNUSED_CHUNKS) > 0) {
-    nsJSContext::PokeGC();
+  // If we didn't end up scheduling a GC, make sure that we release GC buffers
+  // soon after canceling previous shrinking attempt 
+  nsJSContext::KillShrinkGCBuffersTimer();
+  if (!sGCTimer) {
+    nsJSContext::PokeShrinkGCBuffers();
   }
 }
 
 // Script object mananagement - note duplicate implementation
 // in nsJSRuntime below...
 nsresult
 nsJSContext::HoldScriptObject(void* aScriptObject)
 {
@@ -3803,16 +3860,17 @@ nsJSRuntime::GetNameSpaceManager()
   return gNameSpaceManager;
 }
 
 /* static */
 void
 nsJSRuntime::Shutdown()
 {
   nsJSContext::KillGCTimer();
+  nsJSContext::KillShrinkGCBuffersTimer();
   nsJSContext::KillCCTimer();
 
   NS_IF_RELEASE(gNameSpaceManager);
 
   if (!sContextCount) {
     // We're being shutdown, and there are no more contexts
     // alive, release the JS runtime service and the security manager.
 
--- a/dom/base/nsJSEnvironment.h
+++ b/dom/base/nsJSEnvironment.h
@@ -178,21 +178,25 @@ public:
   virtual void LeaveModalState();
 
   NS_DECL_NSIXPCSCRIPTNOTIFY
 
   static void LoadStart();
   static void LoadEnd();
 
   static void GarbageCollectNow(bool shrinkingGC = false);
+  static void ShrinkGCBuffersNow();
   static void CycleCollectNow(nsICycleCollectorListener *aListener = nsnull);
 
   static void PokeGC();
   static void KillGCTimer();
 
+  static void PokeShrinkGCBuffers();
+  static void KillShrinkGCBuffersTimer();
+
   static void PokeCC();
   static void MaybePokeCC();
   static void KillCCTimer();
 
   virtual void GC();
 
 protected:
   nsresult InitializeExternalClasses();
--- a/dom/tests/mochitest/general/Makefile.in
+++ b/dom/tests/mochitest/general/Makefile.in
@@ -53,16 +53,17 @@ include $(topsrcdir)/config/rules.mk
 		test_497898.html \
 		test_bug504220.html \
 		test_bug628069_1.html \
 		test_bug628069_2.html \
 		file_bug628069.html \
 		test_bug631440.html \
 		test_bug653364.html \
 		test_bug629535.html \
+		test_clientRects.html \
 		test_consoleAPI.html \
 		test_domWindowUtils.html \
 		test_domWindowUtils_scrollXY.html \
 		test_offsets.html \
 		test_offsets.js \
 		test_windowProperties.html \
 		test_clipboard_events.html \
 		test_nodesFromRect.html \
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/general/test_clientRects.html
@@ -0,0 +1,125 @@
+<!DOCTYPE HTML>
+<html id="d9" style="width:800px; height:1000px">
+<head>
+  <title>Tests for getClientRects/getBoundingClientRect</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css">
+</head>
+<body style="margin:0" onload="doTest()">
+
+<script>
+function isWithinEps(v1, v2, eps, msg) {
+  if (eps) {
+    ok(Math.abs(v1 - v2) < eps, msg + " (within " + eps + "); got " + v1 + ", expected " + v2);
+  } else {
+    is(v1, v2, msg);
+  }
+}
+function checkRect(clientRect, r, eps, exprMsg, restMsg) {
+  isWithinEps(clientRect.left, r[0], eps, exprMsg + ".left" + restMsg);
+  isWithinEps(clientRect.top, r[1], eps, exprMsg + ".top" + restMsg);
+  isWithinEps(clientRect.right, r[2], eps, exprMsg + ".right" + restMsg);
+  isWithinEps(clientRect.bottom, r[3], eps, exprMsg + ".bottom" + restMsg);
+  isWithinEps(clientRect.width, r[2] - r[0], eps, exprMsg + ".width" + restMsg);
+  isWithinEps(clientRect.height, r[3] - r[1], eps, exprMsg + ".height" + restMsg);
+}
+function doc(id) {
+  return document.getElementById(id).contentDocument;
+}
+function checkElement(id, list, eps, doc) {
+  var e = (doc || document).getElementById(id);
+  var clientRects = e.getClientRects();
+  is(clientRects.length, list.length, "getClientRects().length for element '" + id + "'");
+  var bounds = list.length > 0 ? list[0] : [0,0,0,0];
+  for (var i = 0; i < clientRects.length && i < list.length; ++i) {
+    var r = list[i];
+    r[2] += r[0];
+    r[3] += r[1];
+    checkRect(clientRects[i], r, eps, "getClientRects()[" + i + "]", " for element '" + id + "'");
+    if (r[2] != r[0] && r[3] != r[1]) {
+      bounds[0] = Math.min(bounds[0], r[0]);
+      bounds[1] = Math.min(bounds[1], r[1]);
+      bounds[2] = Math.max(bounds[2], r[2]);
+      bounds[3] = Math.max(bounds[3], r[3]);
+    }
+  }
+  checkRect(e.getBoundingClientRect(), bounds, eps, "getBoundingClientRect()", " for element '" + id + "'");
+}
+</script>
+
+<!-- Simple case -->
+<div id="d1" style="position:absolute; left:50px; top:50px; width:20px; height:30px; background:pink;"></div>
+<!-- Multiple boxes -->
+<div style="position:absolute; left:50px; top:100px; width:400px; height:100px; -moz-column-count:2; -moz-column-gap:0; column-count:2; column-gap:0">
+  <div id="d2">
+    <div style="width:200px; height:100px; background:yellow"></div>
+    <div style="width:200px; height:100px; background:lime"></div>
+  </div>
+</div>
+<!-- No boxes -->
+<div id="d3" style="display:none"></div>
+<!-- Element in transform -->
+<div style="-moz-transform:translate(50px, 50px); transform:translate(50px,50px); position:absolute; left:0; top:200px">
+  <div id="d4" style="width:50px; height:50px; background:blue;"></div>  
+</div>
+<svg style="position:absolute; left:50px; top:300px; width:100px; height:100px;">
+  <!-- Element in SVG foreignobject -->
+  <foreignObject x="20" y="30" width="40" height="40">
+    <div id="d5" style="width:40px; height:40px; background:pink;"></div>
+  </foreignObject>
+  <!-- SVG Element -->
+  <circle id="s1" cx="60" cy="60" r="10" fill="yellow"/>
+</svg>
+<!-- Element in transform with bounding-box -->
+<div style="-moz-transform:rotate(45deg); transform:rotate(45deg); position:absolute; left:50px; top:450px; width:100px; height:100px;">
+  <div id="d6" style="width:100px; height:100px; background:orange;"></div>  
+</div>
+<!-- Element in two transforms; we should combine transforms instead of taking bounding-box twice -->
+<div style="-moz-transform:rotate(45deg); transform:rotate(45deg); position:absolute; left:50px; top:550px; width:100px; height:100px;">
+  <div style="-moz-transform:rotate(-45deg); transform:rotate(-45deg); width:100px; height:100px;">
+    <div id="d7" style="width:100px; height:100px; background:lime;"></div>
+  </div>
+</div>
+<!-- Fixed-pos element -->
+<div id="d8" style="position:fixed; left:50px; top:700px; width:100px; height:100px; background:gray;"></div>
+<!-- Root element; see d9 -->
+<!-- Element in iframe -->
+<iframe id="f1" style="position:absolute; left:300px; top:0; width:100px; height:200px; border:none"
+        src="data:text/html,<div id='d10' style='position:absolute; left:0; top:25px; width:100px; height:100px; background:cyan'>">
+</iframe>
+<!-- Root element in iframe -->
+<iframe id="f2" style="position:absolute; left:300px; top:250px; width:100px; height:200px; border:none"
+        src="data:text/html,<html id='d11' style='width:100px; height:100px; background:magenta'>">
+</iframe>
+<!-- Fixed-pos element in iframe -->
+<iframe id="f3" style="position:absolute; left:300px; top:400px; border:none"
+        src="data:text/html,<div id='d12' style='position:fixed; left:0; top:0; width:100px; height:100px;'>"></iframe>
+
+<script>
+function doTest() {
+  checkElement("d1", [[50,50,20,30]]);
+  checkElement("d2", [[50,100,200,100],[250,100,200,100]]);
+  checkElement("d3", []);
+  checkElement("d4", [[50,250,50,50]]);
+  checkElement("d5", [[70,330,40,40]]);
+  checkElement("s1", [[100,350,20,20]]);
+  var sqrt2 = Math.sqrt(2);
+  checkElement("d6", [[100 - 50*sqrt2,500 - 50*sqrt2,100*sqrt2,100*sqrt2]], 0.1);
+  checkElement("d7", [[50,550,100,100]]);
+  checkElement("d8", [[50,700,100,100]]);
+  checkElement("d9", [[0,0,800,1000]]);
+  checkElement("d10", [[0,25,100,100]], 0, doc("f1"));
+  checkElement("d11", [[0,0,100,100]], 0, doc("f2"));
+  checkElement("d12", [[0,0,100,100]], 0, doc("f3"));
+  SimpleTest.finish();
+}
+SimpleTest.waitForExplicitFinish();
+</script>
+
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+
+</body>
+</html>
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -181,27 +181,24 @@ public:
     }
 
     mPathPrefix = NS_LITERAL_CSTRING("explicit/dom/workers(") +
                   escapedDomain + NS_LITERAL_CSTRING(")/worker(") +
                   escapedURL + NS_LITERAL_CSTRING(", ") + mAddressString +
                   NS_LITERAL_CSTRING(")/");
   }
 
-  NS_IMETHOD
-  CollectReports(nsIMemoryMultiReporterCallback* aCallback,
-                 nsISupports* aClosure)
+  nsresult
+  CollectForRuntime(bool aIsQuick, void* aData)
   {
     AssertIsOnMainThread();
 
-    IterateData data;
-
     if (mWorkerPrivate) {
       bool disabled;
-      if (!mWorkerPrivate->BlockAndCollectRuntimeStats(&data, &disabled)) {
+      if (!mWorkerPrivate->BlockAndCollectRuntimeStats(aIsQuick, aData, &disabled)) {
         return NS_ERROR_FAILURE;
       }
 
       // Don't ever try to talk to the worker again.
       if (disabled) {
 #ifdef DEBUG
         {
           nsCAutoString message("Unable to report memory for ");
@@ -212,22 +209,45 @@ public:
                      NS_LITERAL_CSTRING(")! It is either using ctypes or is in "
                                         "the process of being destroyed");
           NS_WARNING(message.get());
         }
 #endif
         mWorkerPrivate = nsnull;
       }
     }
+    return NS_OK;
+  }
+
+  NS_IMETHOD
+  CollectReports(nsIMemoryMultiReporterCallback* aCallback,
+                 nsISupports* aClosure)
+  {
+    AssertIsOnMainThread();
+
+    IterateData data;
+    nsresult rv = CollectForRuntime(/* isQuick = */false, &data);
+    if (NS_FAILED(rv)) {
+      return rv;
+    }
 
     // Always report, even if we're disabled, so that we at least get an entry
     // in about::memory.
     ReportJSRuntimeStats(data, mPathPrefix, aCallback, aClosure);
+
     return NS_OK;
   }
+
+  NS_IMETHOD
+  GetExplicitNonHeap(PRInt64 *aAmount)
+  {
+    AssertIsOnMainThread();
+
+    return CollectForRuntime(/* isQuick = */true, aAmount);
+  }
 };
 
 NS_IMPL_THREADSAFE_ISUPPORTS1(WorkerMemoryReporter, nsIMemoryMultiReporter)
 
 struct WorkerStructuredCloneCallbacks
 {
   static JSObject*
   Read(JSContext* aCx, JSStructuredCloneReader* aReader, uint32_t aTag,
@@ -1373,26 +1393,27 @@ public:
 class CollectRuntimeStatsRunnable : public WorkerControlRunnable
 {
   typedef mozilla::Mutex Mutex;
   typedef mozilla::CondVar CondVar;
 
   Mutex mMutex;
   CondVar mCondVar;
   volatile bool mDone;
-  IterateData* mData;
+  bool mIsQuick;
+  void* mData;
   bool* mSucceeded;
 
 public:
-  CollectRuntimeStatsRunnable(WorkerPrivate* aWorkerPrivate, IterateData* aData,
-                              bool* aSucceeded)
+  CollectRuntimeStatsRunnable(WorkerPrivate* aWorkerPrivate, bool aIsQuick,
+                              void* aData, bool* aSucceeded)
   : WorkerControlRunnable(aWorkerPrivate, WorkerThread, UnchangedBusyCount),
     mMutex("CollectRuntimeStatsRunnable::mMutex"),
     mCondVar(mMutex, "CollectRuntimeStatsRunnable::mCondVar"), mDone(false),
-    mData(aData), mSucceeded(aSucceeded)
+    mIsQuick(aIsQuick), mData(aData), mSucceeded(aSucceeded)
   { }
 
   bool
   PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
   {
     AssertIsOnMainThread();
     return true;
   }
@@ -1424,17 +1445,19 @@ public:
     return true;
   }
 
   bool
   WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
   {
     JSAutoSuspendRequest asr(aCx);
 
-    *mSucceeded = CollectCompartmentStatsForRuntime(JS_GetRuntime(aCx), mData);
+    *mSucceeded = mIsQuick ?
+      mozilla::xpconnect::memory::GetExplicitNonHeapForRuntime(JS_GetRuntime(aCx), mData) :
+      mozilla::xpconnect::memory::CollectCompartmentStatsForRuntime(JS_GetRuntime(aCx), mData);
 
     {
       MutexAutoLock lock(mMutex);
       mDone = true;
       mCondVar.Notify();
     }
 
     return true;
@@ -2564,17 +2587,17 @@ WorkerPrivate::ScheduleDeletion(bool aWa
       new TopLevelWorkerFinishedRunnable(this, currentThread);
     if (NS_FAILED(NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL))) {
       NS_WARNING("Failed to dispatch runnable!");
     }
   }
 }
 
 bool
-WorkerPrivate::BlockAndCollectRuntimeStats(IterateData* aData, bool* aDisabled)
+WorkerPrivate::BlockAndCollectRuntimeStats(bool aIsQuick, void* aData, bool* aDisabled)
 {
   AssertIsOnMainThread();
   NS_ASSERTION(aData, "Null data!");
 
   {
     MutexAutoLock lock(mMutex);
 
     if (mMemoryReporterDisabled) {
@@ -2584,17 +2607,17 @@ WorkerPrivate::BlockAndCollectRuntimeSta
 
     *aDisabled = false;
     mMemoryReporterRunning = true;
   }
 
   bool succeeded;
 
   nsRefPtr<CollectRuntimeStatsRunnable> runnable =
-    new CollectRuntimeStatsRunnable(this, aData, &succeeded);
+    new CollectRuntimeStatsRunnable(this, aIsQuick, aData, &succeeded);
   if (!runnable->Dispatch(nsnull)) {
     NS_WARNING("Failed to dispatch runnable!");
     succeeded = false;
   }
 
   {
     MutexAutoLock lock(mMutex);
     mMemoryReporterRunning = false;
--- a/dom/workers/WorkerPrivate.h
+++ b/dom/workers/WorkerPrivate.h
@@ -656,18 +656,17 @@ public:
 
   void
   UpdateJSContextOptionsInternal(JSContext* aCx, PRUint32 aOptions);
 
   void
   ScheduleDeletion(bool aWasPending);
 
   bool
-  BlockAndCollectRuntimeStats(mozilla::xpconnect::memory::IterateData* aData,
-                              bool* aDisabled);
+  BlockAndCollectRuntimeStats(bool isQuick, void* aData, bool* aDisabled);
 
   bool
   DisableMemoryReporter();
 
 #ifdef JS_GC_ZEAL
   void
   UpdateGCZealInternal(JSContext* aCx, PRUint8 aGCZeal);
 #endif
--- a/editor/txtsvc/public/nsIInlineSpellChecker.idl
+++ b/editor/txtsvc/public/nsIInlineSpellChecker.idl
@@ -38,17 +38,17 @@
 
 #include "nsISupports.idl"
 #include "domstubs.idl"
 
 interface nsISelection;
 interface nsIEditor;
 interface nsIEditorSpellCheck;
 
-[scriptable, uuid(f456dda1-965d-470c-8c55-e51b38e45212)]
+[scriptable, uuid(df635540-d073-47b8-8678-18776130691d)]
 
 interface nsIInlineSpellChecker : nsISupports
 {
   readonly attribute nsIEditorSpellCheck spellChecker;
 
   [noscript] void init(in nsIEditor aEditor);
   [noscript] void cleanup(in boolean aDestroyingFrames);
 
@@ -63,16 +63,17 @@ interface nsIInlineSpellChecker : nsISup
                                    in nsIDOMNode aEndNode,
                                    in long aEndOffset);
 
   void spellCheckRange(in nsIDOMRange aSelection);
 
   nsIDOMRange getMisspelledWord(in nsIDOMNode aNode, in long aOffset);
   void replaceWord(in nsIDOMNode aNode, in long aOffset, in AString aNewword);
   void addWordToDictionary(in AString aWord);
+  void removeWordFromDictionary(in AString aWord);
   
   void ignoreWord(in AString aWord);
   void ignoreWords([array, size_is(aCount)] in wstring aWordsToIgnore, in unsigned long aCount);
   void updateCurrentDictionary();
 };
 
 %{C++
 
--- a/extensions/spellcheck/src/mozInlineSpellChecker.cpp
+++ b/extensions/spellcheck/src/mozInlineSpellChecker.cpp
@@ -864,16 +864,34 @@ mozInlineSpellChecker::AddWordToDictiona
   NS_ENSURE_SUCCESS(rv, rv); 
 
   mozInlineSpellStatus status(this);
   rv = status.InitForSelection();
   NS_ENSURE_SUCCESS(rv, rv);
   return ScheduleSpellCheck(status);
 }
 
+//  mozInlineSpellChecker::RemoveWordFromDictionary
+
+NS_IMETHODIMP
+mozInlineSpellChecker::RemoveWordFromDictionary(const nsAString &word)
+{
+  NS_ENSURE_TRUE(mSpellCheck, NS_ERROR_NOT_INITIALIZED);
+
+  nsAutoString wordstr(word);
+  nsresult rv = mSpellCheck->RemoveWordFromDictionary(wordstr.get());
+  NS_ENSURE_SUCCESS(rv, rv); 
+  
+  mozInlineSpellStatus status(this);
+  nsCOMPtr<nsIRange> range = do_QueryInterface(NULL); // Check everything
+  rv = status.InitForRange(range);
+  NS_ENSURE_SUCCESS(rv, rv);
+  return ScheduleSpellCheck(status);
+}
+
 // mozInlineSpellChecker::IgnoreWord
 
 NS_IMETHODIMP
 mozInlineSpellChecker::IgnoreWord(const nsAString &word)
 {
   NS_ENSURE_TRUE(mSpellCheck, NS_ERROR_NOT_INITIALIZED);
 
   nsAutoString wordstr(word);
deleted file mode 100644
--- a/gfx/ycbcr/QuellGccWarnings.patch
+++ /dev/null
@@ -1,40 +0,0 @@
-diff --git a/gfx/ycbcr/yuv_convert.cpp b/gfx/ycbcr/yuv_convert.cpp
---- a/gfx/ycbcr/yuv_convert.cpp
-+++ b/gfx/ycbcr/yuv_convert.cpp
-@@ -337,16 +337,17 @@ NS_GFX_(void) ScaleYCbCrToRGB32(const ui
-                                          source_dx_uv >> kFractionBits);
-         }
-       }
-       else {
-         ScaleYUVToRGB32Row_C(y_ptr, u_ptr, v_ptr,
-                              dest_pixel, width, source_dx);
-       }
- #else
-+      (void)source_dx_uv;
-       ScaleYUVToRGB32Row(y_ptr, u_ptr, v_ptr,
-                          dest_pixel, width, source_dx);
- #endif
-     }
-   }
-   // MMX used for FastConvertYUVToRGB32Row and FilterRows requires emms.
-   if (has_mmx)
-     EMMS();
-diff --git a/gfx/ycbcr/yuv_row.h b/gfx/ycbcr/yuv_row.h
---- a/gfx/ycbcr/yuv_row.h
-+++ b/gfx/ycbcr/yuv_row.h
-@@ -129,14 +129,14 @@ extern SIMD_ALIGNED(int16 kCoefficientsR
- #if defined(ARCH_CPU_X86) && !defined(ARCH_CPU_X86_64)
- #if defined(_MSC_VER)
- #define EMMS() __asm emms
- #pragma warning(disable: 4799)
- #else
- #define EMMS() asm("emms")
- #endif
- #else
--#define EMMS()
-+#define EMMS() ((void)0)
- #endif
- 
- }  // extern "C"
- 
- #endif  // MEDIA_BASE_YUV_ROW_H_
--- a/gfx/ycbcr/README
+++ b/gfx/ycbcr/README
@@ -20,10 +20,8 @@ convert.patch contains the following cha
   * Add YCbCr 4:4:4 support
   * Bug 619178 - Update CPU detection in yuv_convert to new SSE.h interface.
   * Bug 616778 - Split yuv_convert FilterRows vectorized code into separate files so it can
     be properly guarded with cpuid() calls.
 
 win64.patch: SSE2 optimization for Microsoft Visual C++ x64 version
 
 TypeFromSize.patch: Bug 656185 - Add a method to detect YUVType from plane sizes.
-
-QuellGccWarnings.patch: Bug 711895 - Avoid some GCC compilation warnings.
--- a/gfx/ycbcr/update.sh
+++ b/gfx/ycbcr/update.sh
@@ -4,9 +4,8 @@ cp $1/media/base/yuv_convert.cc yuv_conv
 cp $1/media/base/yuv_row.h .
 cp $1/media/base/yuv_row_table.cc yuv_row_table.cpp
 cp $1/media/base/yuv_row_posix.cc yuv_row_posix.cpp
 cp $1/media/base/yuv_row_win.cc yuv_row_win.cpp
 cp $1/media/base/yuv_row_posix.cc yuv_row_c.cpp
 patch -p3 <convert.patch
 patch -p3 <win64.patch
 patch -p3 <TypeFromSize.patch
-patch -p3 <QuellGccWarnings.patch
--- a/gfx/ycbcr/yuv_convert.cpp
+++ b/gfx/ycbcr/yuv_convert.cpp
@@ -337,17 +337,16 @@ NS_GFX_(void) ScaleYCbCrToRGB32(const ui
                                          source_dx_uv >> kFractionBits);
         }
       }
       else {
         ScaleYUVToRGB32Row_C(y_ptr, u_ptr, v_ptr,
                              dest_pixel, width, source_dx);
       }
 #else
-      (void)source_dx_uv;
       ScaleYUVToRGB32Row(y_ptr, u_ptr, v_ptr,
                          dest_pixel, width, source_dx);
 #endif
     }
   }
   // MMX used for FastConvertYUVToRGB32Row and FilterRows requires emms.
   if (has_mmx)
     EMMS();
--- a/gfx/ycbcr/yuv_row.h
+++ b/gfx/ycbcr/yuv_row.h
@@ -129,14 +129,14 @@ extern SIMD_ALIGNED(int16 kCoefficientsR
 #if defined(ARCH_CPU_X86) && !defined(ARCH_CPU_X86_64)
 #if defined(_MSC_VER)
 #define EMMS() __asm emms
 #pragma warning(disable: 4799)
 #else
 #define EMMS() asm("emms")
 #endif
 #else
-#define EMMS() ((void)0)
+#define EMMS()
 #endif
 
 }  // extern "C"
 
 #endif  // MEDIA_BASE_YUV_ROW_H_
--- a/js/public/HashTable.h
+++ b/js/public/HashTable.h
@@ -176,17 +176,17 @@ class HashTable : private AllocPolicy
      * table operation unless the |generation()| is tested.
      */
     class Range
     {
       protected:
         friend class HashTable;
 
         Range(Entry *c, Entry *e) : cur(c), end(e) {
-            while (cur != end && !cur->isLive())
+            while (cur < end && !cur->isLive())
                 ++cur;
         }
 
         Entry *cur, *end;
 
       public:
         Range() : cur(NULL), end(NULL) {}
 
@@ -196,17 +196,18 @@ class HashTable : private AllocPolicy
 
         T &front() const {
             JS_ASSERT(!empty());
             return cur->t;
         }
 
         void popFront() {
             JS_ASSERT(!empty());
-            while (++cur != end && !cur->isLive());
+            while (++cur < end && !cur->isLive())
+                continue;
         }
     };
 
     /*
      * A Range whose lifetime delimits a mutating enumeration of a hash table.
      * Since rehashing when elements were removed during enumeration would be
      * bad, it is postponed until |endEnumeration()| is called. If
      * |endEnumeration()| is not called before an Enum's constructor, it will
@@ -337,24 +338,24 @@ class HashTable : private AllocPolicy
         return keyHash & ~sCollisionBit;
     }
 
     static Entry *createTable(AllocPolicy &alloc, uint32_t capacity)
     {
         Entry *newTable = (Entry *)alloc.malloc_(capacity * sizeof(Entry));
         if (!newTable)
             return NULL;
-        for (Entry *e = newTable, *end = e + capacity; e != end; ++e)
+        for (Entry *e = newTable, *end = e + capacity; e < end; ++e)
             new(e) Entry();
         return newTable;
     }
 
     static void destroyTable(AllocPolicy &alloc, Entry *oldTable, uint32_t capacity)
     {
-        for (Entry *e = oldTable, *end = e + capacity; e != end; ++e)
+        for (Entry *e = oldTable, *end = e + capacity; e < end; ++e)
             e->~Entry();
         alloc.free_(oldTable);
     }
 
   public:
     HashTable(AllocPolicy ap)
       : AllocPolicy(ap),
         hashShift(sHashBits),
@@ -560,17 +561,17 @@ class HashTable : private AllocPolicy
 
         /* We can't fail from here on, so update table parameters. */
         setTableSizeLog2(newLog2);
         removedCount = 0;
         gen++;
         table = newTable;
 
         /* Copy only live entries, leaving removed ones behind. */
-        for (Entry *src = oldTable, *end = src + oldCap; src != end; ++src) {
+        for (Entry *src = oldTable, *end = src + oldCap; src < end; ++src) {
             if (src->isLive()) {
                 src->unsetCollision();
                 findFreeEntry(src->getKeyHash()) = Move(*src);
             }
         }
 
         destroyTable(*this, oldTable, oldCap);
         return true;
@@ -602,17 +603,17 @@ class HashTable : private AllocPolicy
 
   public:
     void clear()
     {
         if (tl::IsPodType<Entry>::result) {
             memset(table, 0, sizeof(*table) * capacity());
         } else {
             uint32_t tableCapacity = capacity();
-            for (Entry *e = table, *end = table + tableCapacity; e != end; ++e)
+            for (Entry *e = table, *end = table + tableCapacity; e < end; ++e)
                 *e = Move(Entry());
         }
         removedCount = 0;
         entryCount = 0;
 #ifdef DEBUG
         mutationCount++;
 #endif
     }
--- a/js/src/configure.in
+++ b/js/src/configure.in
@@ -1801,65 +1801,45 @@ if test "$GNU_CC"; then
     fi
     WARNINGS_AS_ERRORS='-Werror'
     DSO_CFLAGS=''
     DSO_PIC_CFLAGS='-fPIC'
     ASFLAGS="$ASFLAGS -fPIC"
     _MOZ_RTTI_FLAGS_ON=-frtti
     _MOZ_RTTI_FLAGS_OFF=-fno-rtti
 
-    # Turn on GNU-specific warnings:
-    # -Wall - turn on a lot of warnings
-    # -pedantic - this is turned on below
-    # -Wpointer-arith - enabled with -pedantic, but good to have even if not
-    # -Wdeclaration-after-statement - MSVC doesn't like these
-    # -Wempty-body - catches bugs, e.g. "if (c); foo();", few false positives
-    #
-    _WARNINGS_CFLAGS="${_WARNINGS_CFLAGS} -Wall -Wpointer-arith -Wdeclaration-after-statement -Wempty-body"
-    
-    # Turn off the following warnings that -Wall/-pedantic turn on:
-    # -Woverlength-strings - we exceed the minimum maximum length all the time
-    #
-    _WARNINGS_CFLAGS="${_WARNINGS_CFLAGS} -Wno-overlength-strings"
-
+    # Turn on GNU specific features
+    # -Wall - turn on all warnings
+    # -pedantic - make compiler warn about non-ANSI stuff, and
+    #             be a little bit stricter
+    # Warnings slamm took out for now (these were giving more noise than help):
+    # -Wbad-function-cast - warns when casting a function to a new return type
+    # -Wshadow - removed because it generates more noise than help --pete
+    _WARNINGS_CFLAGS="${_WARNINGS_CFLAGS} -Wall -W -Wno-unused -Wpointer-arith"
     if test -z "$INTEL_CC" -a -z "$CLANG_CC"; then
        # Don't use -Wcast-align with ICC or clang
        case "$CPU_ARCH" in
            # And don't use it on hppa, ia64, sparc, arm, since it's noisy there
            hppa | ia64 | sparc | arm)
            ;;
            *)
         _WARNINGS_CFLAGS="${_WARNINGS_CFLAGS} -Wcast-align"
            ;;
        esac
     fi
 
     dnl Turn pedantic on but disable the warnings for long long
     _PEDANTIC=1
 
+    if test -z "$INTEL_CC"; then
+      _WARNINGS_CFLAGS="${_WARNINGS_CFLAGS} -W"
+    fi
+
     _DEFINES_CFLAGS='-include $(DEPTH)/js-confdefs.h -DMOZILLA_CLIENT'
     _USE_CPP_INCLUDE_FLAG=1
-
-    AC_CACHE_CHECK(whether the compiler supports -Wtype-limits,
-                   ac_cc_has_wtype_limits,
-        [
-            AC_LANG_SAVE
-            AC_LANG_C
-            _SAVE_CFLAGS="$CFLAGS"
-            CFLAGS="$CFLAGS -Wtype-limits"
-            AC_TRY_COMPILE([],
-                           [return(0);],
-                           ac_cc_has_wtype_limits="yes",
-                           ac_cc_has_wtype_limits="no")
-            CFLAGS="$_SAVE_CFLAGS"
-            AC_LANG_RESTORE
-        ])
-    if test "$ac_cc_has_wtype_limits" = "yes"; then
-        _WARNINGS_CFLAGS="${_WARNINGS_CFLAGS} -Wtype-limits"
-    fi
 elif test "$SOLARIS_SUNPRO_CC"; then
     DSO_CFLAGS=''
     if test "$CPU_ARCH" = "sparc"; then
         # for Sun Studio on Solaris/SPARC
         DSO_PIC_CFLAGS='-xcode=pic32'
     else
         DSO_PIC_CFLAGS='-KPIC'
     fi
@@ -1875,31 +1855,18 @@ else
     fi
 
     DSO_CFLAGS=''
     DSO_PIC_CFLAGS='-KPIC'
     _DEFINES_CFLAGS='$(ACDEFINES) -D_JS_CONFDEFS_H_ -DMOZILLA_CLIENT'
 fi
 
 if test "$GNU_CXX"; then
-    # Turn on GNU-specific warnings:
-    # -Wall - turn on a lot of warnings
-    # -pedantic - this is turned on below
-    # -Wpointer-arith - enabled with -pedantic, but good to have even if not
-    # -Woverloaded-virtual - ???
-    # -Wempty-body - catches bugs, e.g. "if (c); foo();", few false positives
-    #
-    _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Wall -Wpointer-arith -Woverloaded-virtual -Wempty-body"
-
-    # Turn off the following warnings that -Wall/-pedantic turn on:
-    # -Woverlength-strings - we exceed the minimum maximum length all the time
-    # -Wctor-dtor-privacy - ???
-    #
-    _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Wno-overlength-strings -Wno-ctor-dtor-privacy"
-
+    # Turn on GNU specific features
+    _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Wall -Wpointer-arith -Woverloaded-virtual -Wsynth -Wno-ctor-dtor-privacy -Wno-non-virtual-dtor"
     if test -z "$INTEL_CXX" -a -z "$CLANG_CXX"; then
        # Don't use -Wcast-align with ICC or clang
        case "$CPU_ARCH" in
            # And don't use it on hppa, ia64, sparc, arm, since it's noisy there
            hppa | ia64 | sparc | arm)
            ;;
            *)
         _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Wcast-align"
@@ -1990,34 +1957,16 @@ if test "$GNU_CXX"; then
                            ac_has_werror_return_type="no")
             CXXFLAGS="$_SAVE_CXXFLAGS"
             AC_LANG_RESTORE
         ])
     if test "$ac_has_werror_return_type" = "yes"; then
         _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Werror=return-type"
     fi
 
-    AC_CACHE_CHECK(whether the compiler supports -Wtype-limits,
-                   ac_has_wtype_limits,
-        [
-            AC_LANG_SAVE
-            AC_LANG_CPLUSPLUS
-            _SAVE_CXXFLAGS="$CXXFLAGS"
-            CXXFLAGS="$CXXFLAGS -Wtype-limits"
-            AC_TRY_COMPILE([],
-                           [return(0);],
-                           ac_has_wtype_limits="yes",
-                           ac_has_wtype_limits="no")
-            CXXFLAGS="$_SAVE_CXXFLAGS"
-            AC_LANG_RESTORE
-        ])
-    if test "$ac_has_wtype_limits" = "yes"; then
-        _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Wtype-limits"
-    fi
-
 else
     _DEFINES_CXXFLAGS='-DMOZILLA_CLIENT -D_JS_CONFDEFS_H_ $(ACDEFINES)'
 fi
 
 dnl gcc can come with its own linker so it is better to use the pass-thru calls
 dnl MKSHLIB_FORCE_ALL is used to force the linker to include all object
 dnl files present in an archive. MKSHLIB_UNFORCE_ALL reverts the linker to
 dnl normal behavior.
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -1858,33 +1858,39 @@ EmitIndexOp(JSContext *cx, JSOp op, uint
  */
 #define EMIT_INDEX_OP(op, index)                                              \
     JS_BEGIN_MACRO                                                            \
         if (!EmitIndexOp(cx, op, index, bce))                                 \
             return JS_FALSE;                                                  \
     JS_END_MACRO
 
 static bool
-EmitAtomOp(JSContext *cx, ParseNode *pn, JSOp op, BytecodeEmitter *bce, JSOp *psuffix = NULL)
+EmitAtomOp(JSContext *cx, JSAtom *atom, JSOp op, BytecodeEmitter *bce, JSOp *psuffix = NULL)
 {
     JS_ASSERT(JOF_OPTYPE(op) == JOF_ATOM);
 
-    if (op == JSOP_GETPROP &&
-        pn->pn_atom == cx->runtime->atomState.lengthAtom) {
+    if (op == JSOP_GETPROP && atom == cx->runtime->atomState.lengthAtom) {
         /* Specialize length accesses for the interpreter. */
         op = JSOP_LENGTH;
     }
 
     jsatomid index;
-    if (!bce->makeAtomIndex(pn->pn_atom, &index))
+    if (!bce->makeAtomIndex(atom, &index))
         return false;
 
     return EmitIndexOp(cx, op, index, bce, psuffix);
 }
 
+static bool
+EmitAtomOp(JSContext *cx, ParseNode *pn, JSOp op, BytecodeEmitter *bce, JSOp *psuffix = NULL)
+{
+    JS_ASSERT(pn->pn_atom != NULL);
+    return EmitAtomOp(cx, pn->pn_atom, op, bce, psuffix);
+}
+
 static JSBool
 EmitObjectOp(JSContext *cx, ObjectBox *objbox, JSOp op, BytecodeEmitter *bce)
 {
     JS_ASSERT(JOF_OPTYPE(op) == JOF_OBJECT);
     return EmitIndexOp(cx, op, bce->objectList.index(objbox), bce);
 }
 
 /*
@@ -5473,26 +5479,26 @@ EmitXMLTag(JSContext *cx, BytecodeEmitte
 
     if ((pn->pn_xflags & PNX_XMLROOT) && Emit1(cx, bce, pn->getOp()) < 0)
         return false;
 
     return true;
 }
 
 static bool
-EmitXMLProcessingInstruction(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
+EmitXMLProcessingInstruction(JSContext *cx, BytecodeEmitter *bce, XMLProcessingInstruction &pi)
 {
     JS_ASSERT(!bce->inStrictMode());
 
     jsatomid index;
-    if (!bce->makeAtomIndex(pn->pn_pidata, &index))
+    if (!bce->makeAtomIndex(pi.data(), &index))
         return false;
     if (!EmitIndexOp(cx, JSOP_QNAMEPART, index, bce))
         return false;
-    if (!EmitAtomOp(cx, pn, JSOP_XMLPI, bce))
+    if (!EmitAtomOp(cx, pi.target(), JSOP_XMLPI, bce))
         return false;
     return true;
 }
 #endif
 
 static bool
 EmitLexicalScope(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
 {
@@ -6761,26 +6767,26 @@ EmitSyntheticStatements(JSContext *cx, B
     for (ParseNode *pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
         if (!EmitTree(cx, bce, pn2))
             return false;
     }
     return PopStatementBCE(cx, bce);
 }
 
 static bool
-EmitConditionalExpression(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
+EmitConditionalExpression(JSContext *cx, BytecodeEmitter *bce, ConditionalExpression &conditional)
 {
     /* Emit the condition, then branch if false to the else part. */
-    if (!EmitTree(cx, bce, pn->pn_kid1))
+    if (!EmitTree(cx, bce, &conditional.condition()))
         return false;
     ptrdiff_t noteIndex = NewSrcNote(cx, bce, SRC_COND);
     if (noteIndex < 0)
         return false;
     ptrdiff_t beq = EmitJump(cx, bce, JSOP_IFEQ, 0);
-    if (beq < 0 || !EmitTree(cx, bce, pn->pn_kid2))
+    if (beq < 0 || !EmitTree(cx, bce, &conditional.thenExpression()))
         return false;
 
     /* Jump around else, fixup the branch, emit else, fixup jump. */
     ptrdiff_t jmp = EmitJump(cx, bce, JSOP_GOTO, 0);
     if (jmp < 0)
         return false;
     CHECK_AND_SET_JUMP_OFFSET_AT(cx, bce, beq);
 
@@ -6791,17 +6797,17 @@ EmitConditionalExpression(JSContext *cx,
      * only one path, so we must decrement bce->stackDepth.
      *
      * Failing to do this will foil code, such as let expression and block
      * code generation, which must use the stack depth to compute local
      * stack indexes correctly.
      */
     JS_ASSERT(bce->stackDepth > 0);
     bce->stackDepth--;
-    if (!EmitTree(cx, bce, pn->pn_kid3))
+    if (!EmitTree(cx, bce, &conditional.elseExpression()))
         return false;
     CHECK_AND_SET_JUMP_OFFSET_AT(cx, bce, jmp);
     return SetSrcNoteOffset(cx, bce, noteIndex, 0, jmp - beq);
 }
 
 static bool
 EmitObject(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, jsint sharpnum)
 {
@@ -7216,18 +7222,18 @@ frontend::EmitTree(JSContext *cx, Byteco
       case PNK_URSHASSIGN:
       case PNK_MULASSIGN:
       case PNK_DIVASSIGN:
       case PNK_MODASSIGN:
         if (!EmitAssignment(cx, bce, pn->pn_left, pn->getOp(), pn->pn_right))
             return false;
         break;
 
-      case PNK_HOOK:
-        ok = EmitConditionalExpression(cx, bce, pn);
+      case PNK_CONDITIONAL:
+        ok = EmitConditionalExpression(cx, bce, pn->asConditionalExpression());
         break;
 
       case PNK_OR:
       case PNK_AND:
         ok = EmitLogical(cx, bce, pn);
         break;
 
       case PNK_ADD:
@@ -7436,17 +7442,17 @@ frontend::EmitTree(JSContext *cx, Byteco
       case PNK_RC:
         ok = EmitObject(cx, bce, pn, -1);
         break;
 
 #if JS_HAS_SHARP_VARS
       case PNK_DEFSHARP:
       {
         JS_ASSERT(bce->hasSharps());
-        int sharpnum = pn->pn_num;
+        jsint sharpnum = pn->asDefSharpExpression().number();
         pn = pn->pn_kid;
         if (pn->isKind(PNK_RB)) {
             ok = EmitArray(cx, bce, pn, sharpnum);
             break;
         }
 # if JS_HAS_GENERATORS
         if (pn->isKind(PNK_ARRAYCOMP)) {
             ok = EmitArray(cx, bce, pn, sharpnum);
@@ -7455,23 +7461,24 @@ frontend::EmitTree(JSContext *cx, Byteco
 # endif
         if (pn->isKind(PNK_RC)) {
             ok = EmitObject(cx, bce, pn, sharpnum);
             break;
         }
 
         if (!EmitTree(cx, bce, pn))
             return JS_FALSE;
-        EMIT_UINT16PAIR_IMM_OP(JSOP_DEFSHARP, bce->sharpSlotBase, (jsatomid) sharpnum);
+        EMIT_UINT16PAIR_IMM_OP(JSOP_DEFSHARP, bce->sharpSlotBase, jsatomid(sharpnum));
         break;
       }
 
       case PNK_USESHARP:
         JS_ASSERT(bce->hasSharps());
-        EMIT_UINT16PAIR_IMM_OP(JSOP_USESHARP, bce->sharpSlotBase, (jsatomid) pn->pn_num);
+        EMIT_UINT16PAIR_IMM_OP(JSOP_USESHARP, bce->sharpSlotBase,
+                               jsatomid(pn->asUseSharpExpression().number()));
         break;
 #endif /* JS_HAS_SHARP_VARS */
 
       case PNK_NAME:
         /*
          * Cope with a left-over function definition that was replaced by a use
          * of a later function definition of the same name. See FunctionDef and
          * MakeDefIntoUse in Parser.cpp.
@@ -7589,17 +7596,17 @@ frontend::EmitTree(JSContext *cx, Byteco
             JS_ASSERT(pn->isArity(PN_NULLARY));
             ok = pn->isOp(JSOP_OBJECT)
                  ? EmitObjectOp(cx, pn->pn_objbox, pn->getOp(), bce)
                  : EmitAtomOp(cx, pn, pn->getOp(), bce);
         }
         break;
 
       case PNK_XMLPI:
-        if (!EmitXMLProcessingInstruction(cx, bce, pn))
+        if (!EmitXMLProcessingInstruction(cx, bce, pn->asXMLProcessingInstruction()))
             return false;
         break;
 #endif /* JS_HAS_XML_SUPPORT */
 
       default:
         JS_ASSERT(0);
     }
 
--- a/js/src/frontend/FoldConstants.cpp
+++ b/js/src/frontend/FoldConstants.cpp
@@ -268,21 +268,23 @@ FoldXMLConstants(JSContext *cx, ParseNod
             break;
 
           case PNK_XMLCOMMENT:
             str = js_MakeXMLCommentString(cx, pn2->pn_atom);
             if (!str)
                 return JS_FALSE;
             break;
 
-          case PNK_XMLPI:
-            str = js_MakeXMLPIString(cx, pn2->pn_pitarget, pn2->pn_pidata);
+          case PNK_XMLPI: {
+            XMLProcessingInstruction &pi = pn2->asXMLProcessingInstruction();
+            str = js_MakeXMLPIString(cx, pi.target(), pi.data());
             if (!str)
                 return JS_FALSE;
             break;
+          }
 
           cantfold:
           default:
             JS_ASSERT(*pnp == pn1);
             if ((kind == PNK_XMLSTAGO || kind == PNK_XMLPTAGC) &&
                 (i & 1) ^ (j & 1)) {
 #ifdef DEBUG_brendanXXX
                 printf("1: %d, %d => ", i, j);
@@ -551,17 +553,17 @@ js::FoldConstants(JSContext *cx, ParseNo
     }
 
     switch (pn->getKind()) {
       case PNK_IF:
         if (ContainsVarOrConst(pn2) || ContainsVarOrConst(pn3))
             break;
         /* FALL THROUGH */
 
-      case PNK_HOOK:
+      case PNK_CONDITIONAL:
         /* Reduce 'if (C) T; else E' into T for true C, E for false. */
         switch (pn1->getKind()) {
           case PNK_NUMBER:
             if (pn1->pn_dval == 0 || JSDOUBLE_IS_NaN(pn1->pn_dval))
                 pn2 = pn3;
             break;
           case PNK_STRING:
             if (pn1->pn_atom->length() == 0)
@@ -586,18 +588,18 @@ js::FoldConstants(JSContext *cx, ParseNo
 
         if (pn2 && !pn2->isDefn())
             pn->become(pn2);
         if (!pn2 || (pn->isKind(PNK_SEMI) && !pn->pn_kid)) {
             /*
              * False condition and no else, or an empty then-statement was
              * moved up over pn.  Either way, make pn an empty block (not an
              * empty statement, which does not decompile, even when labeled).
-             * NB: pn must be a PNK_IF as PNK_HOOK can never have a null kid
-             * or an empty statement for a child.
+             * NB: pn must be a PNK_IF as PNK_CONDITIONAL can never have a null
+             * kid or an empty statement for a child.
              */
             pn->setKind(PNK_STATEMENTLIST);
             pn->setArity(PN_LIST);
             pn->makeEmpty();
         }
         tc->freeTree(pn2);
         if (pn3 && pn3 != pn2)
             tc->freeTree(pn3);
--- a/js/src/frontend/ParseNode.cpp
+++ b/js/src/frontend/ParseNode.cpp
@@ -530,17 +530,17 @@ CloneParseTree(ParseNode *opn, TreeConte
         else
             pn->pn_right = pn->pn_left;
         pn->pn_pval = opn->pn_pval;
         pn->pn_iflags = opn->pn_iflags;
         break;
 
       case PN_UNARY:
         NULLCHECK(pn->pn_kid = CloneParseTree(opn->pn_kid, tc));
-        pn->pn_num = opn->pn_num;
+        pn->pn_u.unary.num = opn->pn_u.unary.num;
         pn->pn_hidden = opn->pn_hidden;
         break;
 
       case PN_NAME:
         // PN_NAME could mean several arms in pn_u, so copy the whole thing.
         pn->pn_u = opn->pn_u;
         if (opn->isUsed()) {
             /*
--- a/js/src/frontend/ParseNode.h
+++ b/js/src/frontend/ParseNode.h
@@ -36,16 +36,18 @@
  * 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 ParseNode_h__
 #define ParseNode_h__
 
+#include "mozilla/Attributes.h"
+
 #include "jsscript.h"
 
 #include "frontend/ParseMaps.h"
 #include "frontend/TokenStream.h"
 
 namespace js {
 
 /*
@@ -56,17 +58,17 @@ namespace js {
  * around <B && C> when A is false).  Nodes are labeled by kind, with a
  * secondary JSOp label when needed.
  *
  * The long comment after this enum block describes the kinds in detail.
  */
 enum ParseNodeKind {
     PNK_SEMI,
     PNK_COMMA,
-    PNK_HOOK,
+    PNK_CONDITIONAL,
     PNK_COLON,
     PNK_OR,
     PNK_AND,
     PNK_BITOR,
     PNK_BITXOR,
     PNK_BITAND,
     PNK_POS,
     PNK_NEG,
@@ -308,17 +310,18 @@ enum ParseNodeKind {
  * PNK_BITXORASSIGN,
  * PNK_BITANDASSIGN,
  * PNK_LSHASSIGN,
  * PNK_RSHASSIGN,
  * PNK_URSHASSIGN,
  * PNK_MULASSIGN,
  * PNK_DIVASSIGN,
  * PNK_MODASSIGN
- * PNK_HOOK     ternary     pn_kid1: cond, pn_kid2: then, pn_kid3: else
+ * PNK_CONDITIONAL ternary  (cond ? trueExpr : falseExpr)
+ *                          pn_kid1: cond, pn_kid2: then, pn_kid3: else
  * PNK_OR       binary      pn_left: first in || chain, pn_right: rest of chain
  * PNK_AND      binary      pn_left: first in && chain, pn_right: rest of chain
  * PNK_BITOR    binary      pn_left: left-assoc | expr, pn_right: ^ expr
  * PNK_BITXOR   binary      pn_left: left-assoc ^ expr, pn_right: & expr
  * PNK_BITAND   binary      pn_left: left-assoc & expr, pn_right: EQ expr
  *
  * PNK_EQ,      binary      pn_left: left-assoc EQ expr, pn_right: REL expr
  * PNK_NE,
@@ -480,26 +483,32 @@ enum ParseNodeArity {
     PN_NAMESET                          /* AtomDefnMapPtr + ParseNode ptr */
 };
 
 struct Definition;
 
 class LoopControlStatement;
 class BreakStatement;
 class ContinueStatement;
+class XMLProcessingInstruction;
+class ConditionalExpression;
+class DefSharpExpression;
+class UseSharpExpression;
 
 struct ParseNode {
   private:
     uint32_t            pn_type   : 16, /* PNK_* type */
                         pn_op     : 8,  /* see JSOp enum and jsopcode.tbl */
                         pn_arity  : 5,  /* see ParseNodeArity enum */
                         pn_parens : 1,  /* this expr was enclosed in parens */
                         pn_used   : 1,  /* name node is on a use-chain */
                         pn_defn   : 1;  /* this node is a Definition */
 
+    void operator=(const ParseNode &other) MOZ_DELETE;
+
   public:
     ParseNode(ParseNodeKind kind, JSOp op, ParseNodeArity arity)
       : pn_type(kind), pn_op(op), pn_arity(arity), pn_parens(0), pn_used(0), pn_defn(0),
         pn_offset(0), pn_next(NULL), pn_link(NULL)
     {
         JS_ASSERT(kind < PNK_LIMIT);
         pn_pos.begin.index = 0;
         pn_pos.begin.lineno = 0;
@@ -585,17 +594,17 @@ struct ParseNode {
         struct {                        /* two kids if binary */
             ParseNode   *left;
             ParseNode   *right;
             Value       *pval;          /* switch case value */
             uintN       iflags;         /* JSITER_* flags for PNK_FOR node */
         } binary;
         struct {                        /* one kid if unary */
             ParseNode   *kid;
-            jsint       num;            /* -1 or sharp variable number */
+            jsint       num;            /* sharp variable number or unused */
             JSBool      hidden;         /* hidden genexp-induced JSOP_YIELD
                                            or directive prologue member (as
                                            pn_prologue) */
         } unary;
         struct {                        /* name, labeled statement, etc. */
             union {
                 JSAtom        *atom;    /* lexical name or label atom */
                 FunctionBox   *funbox;  /* function object */
@@ -612,25 +621,30 @@ struct ParseNode {
             uint32_t    dflags:12,      /* definition/use flags, see below */
                         blockid:20;     /* block number, for subset dominance
                                            computation */
         } name;
         struct {                        /* lexical dependencies + sub-tree */
             AtomDefnMapPtr   defnMap;
             ParseNode        *tree;     /* sub-tree containing name uses */
         } nameset;
-        struct {                        /* PN_NULLARY variant for E4X XML PI */
-            PropertyName     *target;   /* target in <?target data?> */
-            JSAtom           *data;     /* data (or null) in <?target data?> */
-        } xmlpi;
         jsdouble        dval;           /* aligned numeric literal value */
         class {
             friend class LoopControlStatement;
             PropertyName     *label;    /* target of break/continue statement */
         } loopControl;
+        class {                         /* E4X <?target data?> XML PI */
+            friend class XMLProcessingInstruction;
+            PropertyName     *target;   /* non-empty */
+            JSAtom           *data;     /* may be empty, never null */
+        } xmlpi;
+        class {
+            friend class UseSharpExpression;
+            jsint            number;    /* #number# */
+        } usesharp;
     } pn_u;
 
 #define pn_funbox       pn_u.name.funbox
 #define pn_body         pn_u.name.expr
 #define pn_cookie       pn_u.name.cookie
 #define pn_dflags       pn_u.name.dflags
 #define pn_blockid      pn_u.name.blockid
 #define pn_index        pn_u.name.blockid /* reuse as object table index */
@@ -641,28 +655,25 @@ struct ParseNode {
 #define pn_kid1         pn_u.ternary.kid1
 #define pn_kid2         pn_u.ternary.kid2
 #define pn_kid3         pn_u.ternary.kid3
 #define pn_left         pn_u.binary.left
 #define pn_right        pn_u.binary.right
 #define pn_pval         pn_u.binary.pval
 #define pn_iflags       pn_u.binary.iflags
 #define pn_kid          pn_u.unary.kid
-#define pn_num          pn_u.unary.num
 #define pn_hidden       pn_u.unary.hidden
 #define pn_prologue     pn_u.unary.hidden
 #define pn_atom         pn_u.name.atom
 #define pn_objbox       pn_u.name.objbox
 #define pn_expr         pn_u.name.expr
 #define pn_lexdef       pn_u.name.lexdef
 #define pn_names        pn_u.nameset.defnMap
 #define pn_tree         pn_u.nameset.tree
 #define pn_dval         pn_u.dval
-#define pn_pitarget     pn_u.xmlpi.target
-#define pn_pidata       pn_u.xmlpi.data
 
   protected:
     void init(TokenKind type, JSOp op, ParseNodeArity arity) {
         pn_type = type;
         pn_op = op;
         pn_arity = arity;
         pn_parens = false;
         JS_ASSERT(!pn_used);
@@ -919,16 +930,22 @@ struct ParseNode {
     }
 
     bool getConstantValue(JSContext *cx, bool strictChecks, Value *vp);
     inline bool isConstant();
 
     /* Casting operations. */
     inline BreakStatement &asBreakStatement();
     inline ContinueStatement &asContinueStatement();
+#if JS_HAS_XML_SUPPORT
+    inline XMLProcessingInstruction &asXMLProcessingInstruction();
+#endif
+    inline ConditionalExpression &asConditionalExpression();
+    inline DefSharpExpression &asDefSharpExpression();
+    inline UseSharpExpression &asUseSharpExpression();
 };
 
 struct NullaryNode : public ParseNode {
     static inline NullaryNode *create(ParseNodeKind kind, TreeContext *tc) {
         return (NullaryNode *)ParseNode::create(kind, PN_NULLARY, tc);
     }
 };
 
@@ -1054,16 +1071,155 @@ inline ContinueStatement &
 ParseNode::asContinueStatement()
 {
     JS_ASSERT(isKind(PNK_CONTINUE));
     JS_ASSERT(isOp(JSOP_NOP));
     JS_ASSERT(pn_arity == PN_NULLARY);
     return *static_cast<ContinueStatement *>(this);
 }
 
+class DebuggerStatement : public ParseNode {
+  public:
+    DebuggerStatement(const TokenPos &pos)
+      : ParseNode(PNK_DEBUGGER, JSOP_NOP, PN_NULLARY, pos)
+    { }
+};
+
+#if JS_HAS_XML_SUPPORT
+class XMLProcessingInstruction : public ParseNode {
+  public:
+    XMLProcessingInstruction(PropertyName *target, JSAtom *data, const TokenPos &pos)
+      : ParseNode(PNK_XMLPI, JSOP_NOP, PN_NULLARY, pos)
+    {
+        pn_u.xmlpi.target = target;
+        pn_u.xmlpi.data = data;
+    }
+
+    PropertyName *target() const {
+        return pn_u.xmlpi.target;
+    }
+
+    JSAtom *data() const {
+        return pn_u.xmlpi.data;
+    }
+};
+
+inline XMLProcessingInstruction &
+ParseNode::asXMLProcessingInstruction()
+{
+    JS_ASSERT(isKind(PNK_XMLPI));
+    JS_ASSERT(isOp(JSOP_NOP));
+    JS_ASSERT(pn_arity == PN_NULLARY);
+    return *static_cast<XMLProcessingInstruction *>(this);
+}
+#endif
+
+class ConditionalExpression : public ParseNode {
+  public:
+    ConditionalExpression(ParseNode *condition, ParseNode *thenExpr, ParseNode *elseExpr)
+      : ParseNode(PNK_CONDITIONAL, JSOP_NOP, PN_TERNARY,
+                  TokenPos::make(condition->pn_pos.begin, elseExpr->pn_pos.end))
+    {
+        JS_ASSERT(condition);
+        JS_ASSERT(thenExpr);
+        JS_ASSERT(elseExpr);
+        pn_u.ternary.kid1 = condition;
+        pn_u.ternary.kid2 = thenExpr;
+        pn_u.ternary.kid3 = elseExpr;
+    }
+
+    ParseNode &condition() const {
+        return *pn_u.ternary.kid1;
+    }
+
+    ParseNode &thenExpression() const {
+        return *pn_u.ternary.kid2;
+    }
+
+    ParseNode &elseExpression() const {
+        return *pn_u.ternary.kid3;
+    }
+};
+
+inline ConditionalExpression &
+ParseNode::asConditionalExpression()
+{
+    JS_ASSERT(isKind(PNK_CONDITIONAL));
+    JS_ASSERT(isOp(JSOP_NOP));
+    JS_ASSERT(pn_arity == PN_TERNARY);
+    return *static_cast<ConditionalExpression *>(this);
+}
+
+class DefSharpExpression : public ParseNode {
+  public:
+    DefSharpExpression(uint16_t number, ParseNode *expr,
+                       const TokenPtr &begin, const TokenPtr &end)
+      : ParseNode(PNK_DEFSHARP, JSOP_NOP, PN_UNARY, TokenPos::make(begin, end))
+    {
+        pn_u.unary.num = number;
+        pn_u.unary.kid = expr;
+    }
+
+    jsint number() const {
+        return pn_u.unary.num;
+    }
+
+    ParseNode &expression() const {
+        return *pn_u.unary.kid;
+    }
+};
+
+inline DefSharpExpression &
+ParseNode::asDefSharpExpression()
+{
+    JS_ASSERT(isKind(PNK_DEFSHARP));
+    JS_ASSERT(isOp(JSOP_NOP));
+    JS_ASSERT(pn_arity == PN_UNARY);
+    return *static_cast<DefSharpExpression *>(this);
+}
+
+class UseSharpExpression : public ParseNode {
+  public:
+    UseSharpExpression(uint16_t number, const TokenPos &pos)
+      : ParseNode(PNK_USESHARP, JSOP_NOP, PN_NULLARY, pos)
+    {
+        pn_u.usesharp.number = number;
+    }
+
+    jsint number() const {
+        return pn_u.usesharp.number;
+    }
+};
+
+inline UseSharpExpression &
+ParseNode::asUseSharpExpression()
+{
+    JS_ASSERT(isKind(PNK_USESHARP));
+    JS_ASSERT(isOp(JSOP_NOP));
+    JS_ASSERT(pn_arity == PN_NULLARY);
+    return *static_cast<UseSharpExpression *>(this);
+}
+
+class ThisLiteral : public ParseNode {
+  public:
+    ThisLiteral(const TokenPos &pos) : ParseNode(PNK_THIS, JSOP_THIS, PN_NULLARY, pos) { }
+};
+
+class NullLiteral : public ParseNode {
+  public:
+    NullLiteral(const TokenPos &pos) : ParseNode(PNK_NULL, JSOP_NULL, PN_NULLARY, pos) { }
+};
+
+class BooleanLiteral : public ParseNode {
+  public:
+    BooleanLiteral(bool b, const TokenPos &pos)
+      : ParseNode(b ? PNK_TRUE : PNK_FALSE, b ? JSOP_TRUE : JSOP_FALSE, PN_NULLARY, pos)
+    { }
+};
+
 ParseNode *
 CloneLeftHandSide(ParseNode *opn, TreeContext *tc);
 
 /*
  * js::Definition is a degenerate subtype of the PN_FUNC and PN_NAME variants
  * of js::ParseNode, allocated only for function, var, const, and let
  * declarations that define truly lexical bindings. This means that a child of
  * a PNK_VAR list may be a Definition as well as a ParseNode. The pn_defn bit
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -2926,17 +2926,16 @@ Parser::letBlock(LetContext letContext)
          * If this is really an expression in let statement guise, then we
          * need to wrap the TOK_LET node in a TOK_SEMI node so that we pop
          * the return value of the expression.
          */
         ParseNode *semi = UnaryNode::create(PNK_SEMI, tc);
         if (!semi)
             return NULL;
 
-        semi->pn_num = -1;
         semi->pn_kid = pnlet;
 
         letContext = LetExpresion;
         ret = semi;
     } else {
         ret = pnlet;
     }
 
@@ -4221,17 +4220,17 @@ Parser::statement()
 
       case TOK_SEMI:
         pn = UnaryNode::create(PNK_SEMI, tc);
         if (!pn)
             return NULL;
         return pn;
 
       case TOK_DEBUGGER:
-        pn = NullaryNode::create(PNK_DEBUGGER, tc);
+        pn = tc->parser->new_<DebuggerStatement>(tokenStream.currentToken().pos);
         if (!pn)
             return NULL;
         tc->flags |= TCF_FUN_HEAVYWEIGHT;
         break;
 
 #if JS_HAS_XML_SUPPORT
       case TOK_DEFAULT:
       {
@@ -4638,47 +4637,40 @@ Parser::orExpr1()
     while (pn && tokenStream.isCurrentTokenType(TOK_OR))
         pn = ParseNode::newBinaryOrAppend(PNK_OR, JSOP_OR, pn, andExpr1n(), tc);
     return pn;
 }
 
 JS_ALWAYS_INLINE ParseNode *
 Parser::condExpr1()
 {
-    ParseNode *pn = orExpr1();
-    if (pn && tokenStream.isCurrentTokenType(TOK_HOOK)) {
-        ParseNode *pn1 = pn;
-        pn = TernaryNode::create(PNK_HOOK, tc);
-        if (!pn)
-            return NULL;
-
-        /*
-         * Always accept the 'in' operator in the middle clause of a ternary,
-         * where it's unambiguous, even if we might be parsing the init of a
-         * for statement.
-         */
-        uintN oldflags = tc->flags;
-        tc->flags &= ~TCF_IN_FOR_INIT;
-        ParseNode *pn2 = assignExpr();
-        tc->flags = oldflags | (tc->flags & TCF_FUN_FLAGS);
-
-        if (!pn2)
-            return NULL;
-        MUST_MATCH_TOKEN(TOK_COLON, JSMSG_COLON_IN_COND);
-        ParseNode *pn3 = assignExpr();
-        if (!pn3)
-            return NULL;
-        pn->pn_pos.begin = pn1->pn_pos.begin;
-        pn->pn_pos.end = pn3->pn_pos.end;
-        pn->pn_kid1 = pn1;
-        pn->pn_kid2 = pn2;
-        pn->pn_kid3 = pn3;
-        tokenStream.getToken();     /* need to read one token past the end */
-    }
-    return pn;
+    ParseNode *condition = orExpr1();
+    if (!condition || !tokenStream.isCurrentTokenType(TOK_HOOK))
+        return condition;
+
+    /*
+     * Always accept the 'in' operator in the middle clause of a ternary,
+     * where it's unambiguous, even if we might be parsing the init of a
+     * for statement.
+     */
+    uintN oldflags = tc->flags;
+    tc->flags &= ~TCF_IN_FOR_INIT;
+    ParseNode *thenExpr = assignExpr();
+    tc->flags = oldflags | (tc->flags & TCF_FUN_FLAGS);
+    if (!thenExpr)
+        return NULL;
+
+    MUST_MATCH_TOKEN(TOK_COLON, JSMSG_COLON_IN_COND);
+
+    ParseNode *elseExpr = assignExpr();
+    if (!elseExpr)
+        return NULL;
+
+    tokenStream.getToken(); /* read one token past the end */
+    return new_<ConditionalExpression>(condition, thenExpr, elseExpr);
 }
 
 bool
 Parser::setAssignmentLhsOps(ParseNode *pn, JSOp op)
 {
     switch (pn->getKind()) {
       case PNK_NAME:
         if (!CheckStrictAssignment(context, tc, pn))
@@ -6358,23 +6350,20 @@ Parser::xmlElementContent(ParseNode *pn)
             pn->pn_xflags |= PNX_CANTFOLD;
         } else if (tt == TOK_XMLSTAGO) {
             pn2 = xmlElementOrList(JS_FALSE);
             if (!pn2)
                 return false;
             pn2->pn_xflags &= ~PNX_XMLROOT;
             pn->pn_xflags |= pn2->pn_xflags;
         } else if (tt == TOK_XMLPI) {
-            pn2 = NullaryNode::create(PNK_XMLPI, tc);
+            const Token &tok = tokenStream.currentToken();
+            pn2 = new_<XMLProcessingInstruction>(tok.xmlPITarget(), tok.xmlPIData(), tok.pos);
             if (!pn2)
                 return false;
-            const Token &tok = tokenStream.currentToken();
-            pn2->setOp(tok.t_op);
-            pn2->pn_pitarget = tok.xmlPITarget();
-            pn2->pn_pidata = tok.xmlPIData();
         } else {
             JS_ASSERT(tt == TOK_XMLCDATA || tt == TOK_XMLCOMMENT);
             pn2 = atomNode(tt == TOK_XMLCDATA ? PNK_XMLCDATA : PNK_XMLCOMMENT,
                            tokenStream.currentToken().t_op);
             if (!pn2)
                 return false;
         }
         pn->pn_pos.end = pn2->pn_pos.end;
@@ -6571,26 +6560,16 @@ Parser::parseXMLText(JSObject *chain, bo
     }
     tokenStream.setXMLOnlyMode(false);
 
     return pn;
 }
 
 #endif /* JS_HAS_XMLSUPPORT */
 
-static ParseNode *
-PrimaryExprNode(ParseNodeKind kind, JSOp op, TreeContext *tc)
-{
-    ParseNode *pn = NullaryNode::create(kind, tc);
-    if (!pn)
-        return NULL;
-    pn->setOp(op);
-    return pn;
-}
-
 ParseNode *
 Parser::primaryExpr(TokenKind tt, JSBool afterDot)
 {
     JS_ASSERT(tokenStream.isCurrentTokenType(tt));
 
     ParseNode *pn, *pn2, *pn3;
     JSOp op;
 
@@ -6949,50 +6928,55 @@ Parser::primaryExpr(TokenKind tt, JSBool
       case TOK_LET:
         pn = letBlock(LetExpresion);
         if (!pn)
             return NULL;
         break;
 #endif
 
 #if JS_HAS_SHARP_VARS
-      case TOK_DEFSHARP:
-        pn = UnaryNode::create(PNK_DEFSHARP, tc);
+      case TOK_DEFSHARP: {
+        if (!tc->ensureSharpSlots())
+            return NULL;
+        const Token &tok = tokenStream.currentToken();
+        TokenPtr begin = tok.pos.begin;
+        uint16_t number = tok.sharpNumber();
+
+        tt = tokenStream.getToken(TSF_OPERAND);
+        ParseNode *expr = primaryExpr(tt, false);
+        if (!expr)
+            return NULL;
+        if (expr->isKind(PNK_USESHARP) ||
+            expr->isKind(PNK_DEFSHARP) ||
+            expr->isKind(PNK_STRING) ||
+            expr->isKind(PNK_NUMBER) ||
+            expr->isKind(PNK_TRUE) ||
+            expr->isKind(PNK_FALSE) ||
+            expr->isKind(PNK_NULL) ||
+            expr->isKind(PNK_THIS))
+        {
+            reportErrorNumber(expr, JSREPORT_ERROR, JSMSG_BAD_SHARP_VAR_DEF);
+            return NULL;
+        }
+        pn = new_<DefSharpExpression>(number, expr, begin, tokenStream.currentToken().pos.end);
         if (!pn)
             return NULL;
-        pn->pn_num = tokenStream.currentToken().sharpNumber();
-        tt = tokenStream.getToken(TSF_OPERAND);
-        pn->pn_kid = primaryExpr(tt, JS_FALSE);
-        if (!pn->pn_kid)
-            return NULL;
-        if (pn->pn_kid->isKind(PNK_USESHARP) ||
-            pn->pn_kid->isKind(PNK_DEFSHARP) ||
-            pn->pn_kid->isKind(PNK_STRING) ||
-            pn->pn_kid->isKind(PNK_NUMBER) ||
-            pn->pn_kid->isKind(PNK_TRUE) ||
-            pn->pn_kid->isKind(PNK_FALSE) ||
-            pn->pn_kid->isKind(PNK_NULL) ||
-            pn->pn_kid->isKind(PNK_THIS))
-        {
-            reportErrorNumber(pn->pn_kid, JSREPORT_ERROR, JSMSG_BAD_SHARP_VAR_DEF);
-            return NULL;
-        }
+        break;
+      }
+
+      case TOK_USESHARP: {
         if (!tc->ensureSharpSlots())
             return NULL;
-        break;
-
-      case TOK_USESHARP:
         /* Check for forward/dangling references at runtime, to allow eval. */
-        pn = NullaryNode::create(PNK_USESHARP, tc);
+        const Token &tok = tokenStream.currentToken();
+        pn = new_<UseSharpExpression>(tok.sharpNumber(), tok.pos);
         if (!pn)
             return NULL;
-        if (!tc->ensureSharpSlots())
-            return NULL;
-        pn->pn_num = tokenStream.currentToken().sharpNumber();
         break;
+      }
 #endif /* JS_HAS_SHARP_VARS */
 
       case TOK_LP:
       {
         JSBool genexp;
 
         pn = parenExpr(&genexp);
         if (!pn)
@@ -7048,24 +7032,24 @@ Parser::primaryExpr(TokenKind tt, JSBool
 #endif
       case TOK_STRING:
         pn = atomNode(PNK_STRING, JSOP_STRING);
         if (!pn)
             return NULL;
         break;
 
 #if JS_HAS_XML_SUPPORT
-      case TOK_XMLPI:
+      case TOK_XMLPI: {
         JS_ASSERT(!tc->inStrictMode());
-        pn = NullaryNode::create(PNK_XMLPI, tc);
+        const Token &tok = tokenStream.currentToken();
+        pn = new_<XMLProcessingInstruction>(tok.xmlPITarget(), tok.xmlPIData(), tok.pos);
         if (!pn)
             return NULL;
-        pn->pn_pitarget = tokenStream.currentToken().xmlPITarget();
-        pn->pn_pidata = tokenStream.currentToken().xmlPIData();
         break;
+      }
 #endif
 
       case TOK_NAME:
         pn = NameNode::create(PNK_NAME, tokenStream.currentToken().name(), tc);
         if (!pn)
             return NULL;
         JS_ASSERT(tokenStream.currentToken().t_op == JSOP_NAME);
         pn->setOp(JSOP_NAME);
@@ -7218,23 +7202,23 @@ Parser::primaryExpr(TokenKind tt, JSBool
         pn = NullaryNode::create(PNK_NUMBER, tc);
         if (!pn)
             return NULL;
         pn->setOp(JSOP_DOUBLE);
         pn->pn_dval = tokenStream.currentToken().number();
         break;
 
       case TOK_TRUE:
-        return PrimaryExprNode(PNK_TRUE, JSOP_TRUE, tc);
+        return new_<BooleanLiteral>(true, tokenStream.currentToken().pos);
       case TOK_FALSE:
-        return PrimaryExprNode(PNK_FALSE, JSOP_FALSE, tc);
+        return new_<BooleanLiteral>(false, tokenStream.currentToken().pos);
       case TOK_THIS:
-        return PrimaryExprNode(PNK_THIS, JSOP_THIS, tc);
+        return new_<ThisLiteral>(tokenStream.currentToken().pos);
       case TOK_NULL:
-        return PrimaryExprNode(PNK_NULL, JSOP_NULL, tc);
+        return new_<NullLiteral>(tokenStream.currentToken().pos);
 
       case TOK_ERROR:
         /* The scanner or one of its subroutines reported the error. */
         return NULL;
 
       default:
         reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_SYNTAX_ERROR);
         return NULL;
--- a/js/src/frontend/TokenStream.h
+++ b/js/src/frontend/TokenStream.h
@@ -325,18 +325,18 @@ struct Token {
                 PropertyName *name;     /* non-numeric atom */
                 JSAtom       *atom;     /* potentially-numeric atom */
             } n;
         } s;
 
       private:
         friend struct Token;
         struct {                        /* pair for <?target data?> XML PI */
-            JSAtom       *data;         /* auxiliary atom table entry */
-            PropertyName *target;       /* main atom table entry */
+            PropertyName *target;       /* non-empty */
+            JSAtom       *data;         /* maybe empty, never null */
         } xmlpi;
         uint16_t        sharpNumber;    /* sharp variable number: #1# or #1= */
         jsdouble        number;         /* floating point number */
         RegExpFlag      reflags;        /* regexp flags, use tokenbuf to access
                                            regexp chars */
     } u;
 
     /* Mutators */
@@ -354,16 +354,19 @@ struct Token {
 
     void setAtom(JSOp op, JSAtom *atom) {
         JS_ASSERT(op == JSOP_STRING || op == JSOP_XMLCOMMENT || JSOP_XMLCDATA);
         u.s.op = op;
         u.s.n.atom = atom;
     }
 
     void setProcessingInstruction(PropertyName *target, JSAtom *data) {
+        JS_ASSERT(target);
+        JS_ASSERT(data);
+        JS_ASSERT(!target->empty());
         u.xmlpi.target = target;
         u.xmlpi.data = data;
     }
 
     void setRegExpFlags(js::RegExpFlag flags) {
         JS_ASSERT((flags & AllFlags) == flags);
         u.reflags = flags;
     }
--- a/js/src/gc/Barrier.h
+++ b/js/src/gc/Barrier.h
@@ -421,27 +421,31 @@ class HeapId
  * this, we mark these empty shapes black whenever they get read out.
  */
 template<class T>
 class ReadBarriered
 {
     T *value;
 
   public:
+    ReadBarriered() : value(NULL) {}
     ReadBarriered(T *value) : value(value) {}
 
     T *get() const {
         if (!value)
             return NULL;
         T::readBarrier(value);
         return value;
     }
 
     operator T*() const { return get(); }
 
+    T &operator*() const { return *get(); }
+    T *operator->() const { return get(); }
+
     T *unsafeGet() { return value; }
 
     void set(T *v) { value = v; }
 
     operator bool() { return !!value; }
 
     template<class U>
     operator MarkablePtr<U>() const { return MarkablePtr<U>(value); }
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -400,33 +400,20 @@ DoGetElement(JSContext *cx, JSObject *ob
     *hole = !present;
     if (*hole)
         vp->setUndefined();
 
     return true;
 }
 
 template<typename IndexType>
-static void
-AssertGreaterThanZero(IndexType index)
-{
-    JS_ASSERT(index >= 0);
-}
-
-template<>
-void
-AssertGreaterThanZero(jsuint index)
-{
-}
-
-template<typename IndexType>
 static JSBool
 GetElement(JSContext *cx, JSObject *obj, IndexType index, JSBool *hole, Value *vp)
 {
-    AssertGreaterThanZero(index);
+    JS_ASSERT(index >= 0);
     if (obj->isDenseArray() && index < obj->getDenseArrayInitializedLength() &&
         !(*vp = obj->getDenseArrayElement(uint32_t(index))).isMagic(JS_ARRAY_HOLE)) {
         *hole = JS_FALSE;
         return JS_TRUE;
     }
     if (obj->isArguments()) {
         if (obj->asArguments()->getElement(uint32_t(index), vp)) {
             *hole = JS_FALSE;
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -99,17 +99,16 @@ using namespace js::gc;
 namespace js {
 
 ThreadData::ThreadData(JSRuntime *rt)
   : rt(rt),
     interruptFlags(0),
 #ifdef JS_THREADSAFE
     requestDepth(0),
 #endif
-    waiveGCQuota(false),
     tempLifoAlloc(TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
     execAlloc(NULL),
     bumpAlloc(NULL),
     repCache(NULL),
     dtoaState(NULL),
     nativeStackBase(GetNativeStackBase()),
     pendingProxyOperation(NULL),
     interpreterFrames(NULL)
@@ -147,27 +146,32 @@ ThreadData::sizeOfExcludingThis(JSMalloc
      * below have been seen by DMD to be worth measuring.  More stuff may be
      * added later.
      */
 
     /*
      * The computedSize is 0 because sizeof(DtoaState) isn't available here and
      * it's not worth making it available.
      */
-    *normal = mallocSizeOf(dtoaState, /* sizeof(DtoaState) */0);
+    if (normal)
+        *normal = mallocSizeOf(dtoaState, /* sizeof(DtoaState) */0);
 
-    *temporary = tempLifoAlloc.sizeOfExcludingThis(mallocSizeOf);
+    if (temporary)
+        *temporary = tempLifoAlloc.sizeOfExcludingThis(mallocSizeOf);
 
-    size_t method = 0, regexp = 0, unused = 0;
-    if (execAlloc)
-        execAlloc->sizeOfCode(&method, &regexp, &unused);
-    JS_ASSERT(method == 0);     /* this execAlloc is only used for regexp code */
-    *regexpCode = regexp + unused;
+    if (regexpCode) {
+        size_t method = 0, regexp = 0, unused = 0;
+        if (execAlloc)
+            execAlloc->sizeOfCode(&method, &regexp, &unused);
+        JS_ASSERT(method == 0);     /* this execAlloc is only used for regexp code */
+        *regexpCode = regexp + unused;
+    }
 
-    *stackCommitted = stackSpace.sizeOfCommitted();
+    if (stackCommitted)
+        *stackCommitted = stackSpace.sizeOfCommitted();
 }
 #endif
 
 void
 ThreadData::triggerOperationCallback(JSRuntime *rt)
 {
     JS_ASSERT(rt == this->rt);
 
@@ -248,17 +252,18 @@ js_GetCurrentScript(JSContext *cx)
 
 #ifdef JS_THREADSAFE
 
 void
 JSThread::sizeOfIncludingThis(JSMallocSizeOfFun mallocSizeOf, size_t *normal, size_t *temporary,
                               size_t *regexpCode, size_t *stackCommitted)
 {
     data.sizeOfExcludingThis(mallocSizeOf, normal, temporary, regexpCode, stackCommitted);
-    *normal += mallocSizeOf(this, sizeof(JSThread));
+    if (normal)
+        *normal += mallocSizeOf(this, sizeof(JSThread));
 }
 
 JSThread *
 js_CurrentThreadAndLockGC(JSRuntime *rt)
 {
     void *id = js_CurrentThreadId();
     JS_LOCK_GC(rt);
 
@@ -1588,23 +1593,23 @@ JSRuntime::onTooMuchMalloc()
 
 JS_FRIEND_API(void *)
 JSRuntime::onOutOfMemory(void *p, size_t nbytes, JSContext *cx)
 {
     /*
      * Retry when we are done with the background sweeping and have stopped
      * all the allocations and released the empty GC chunks.
      */
+    ShrinkGCBuffers(this);
+#ifdef JS_THREADSAFE
     {
-#ifdef JS_THREADSAFE
         AutoLockGC lock(this);
         gcHelperThread.waitBackgroundSweepOrAllocEnd();
+    }
 #endif
-        gcChunkPool.expire(this, true);
-    }
     if (!p)
         p = OffTheBooks::malloc_(nbytes);
     else if (p == reinterpret_cast<void *>(1))
         p = OffTheBooks::calloc_(nbytes);
     else
       p = OffTheBooks::realloc_(p, nbytes);
     if (p)
         return p;
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -135,22 +135,16 @@ struct ThreadData {
 #ifdef JS_THREADSAFE
     /* The request depth for this thread. */
     unsigned            requestDepth;
 #endif
 
     /* Keeper of the contiguous stack used by all contexts in this thread. */
     StackSpace          stackSpace;
 
-    /*
-     * Flag indicating that we are waiving any soft limits on the GC heap
-     * because we want allocations to be infallible (except when we hit OOM).
-     */
-    bool                waiveGCQuota;
-
     /* Temporary arena pool used while compiling and decompiling. */
     static const size_t TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE = 1 << 12;
     LifoAlloc           tempLifoAlloc;
 
   private:
     /*
      * Both of these allocators are used for regular expression code which is shared at the
      * thread-data level.
--- a/js/src/jsfriendapi.cpp
+++ b/js/src/jsfriendapi.cpp
@@ -120,22 +120,28 @@ JS_FRIEND_API(JSObject *)
 JS_NewObjectWithUniqueType(JSContext *cx, JSClass *clasp, JSObject *proto, JSObject *parent)
 {
     JSObject *obj = JS_NewObject(cx, clasp, proto, parent);
     if (!obj || !obj->setSingletonType(cx))
         return NULL;
     return obj;
 }
 
-JS_PUBLIC_API(void)
+JS_FRIEND_API(void)
 JS_ShrinkingGC(JSContext *cx)
 {
     js_GC(cx, NULL, GC_SHRINK, gcstats::PUBLIC_API);
 }
 
+JS_FRIEND_API(void)
+JS_ShrinkGCBuffers(JSRuntime *rt)
+{
+    ShrinkGCBuffers(rt);
+}
+
 JS_FRIEND_API(JSPrincipals *)
 JS_GetCompartmentPrincipals(JSCompartment *compartment)
 {
     return compartment->principals;
 }
 
 JS_FRIEND_API(JSBool)
 JS_WrapPropertyDescriptor(JSContext *cx, js::PropertyDescriptor *desc)
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -68,16 +68,19 @@ extern JS_FRIEND_API(JSObject *)
 JS_NewObjectWithUniqueType(JSContext *cx, JSClass *clasp, JSObject *proto, JSObject *parent);
 
 extern JS_FRIEND_API(uint32_t)
 JS_ObjectCountDynamicSlots(JSObject *obj);
 
 extern JS_FRIEND_API(void)
 JS_ShrinkingGC(JSContext *cx);
 
+extern JS_FRIEND_API(void)
+JS_ShrinkGCBuffers(JSRuntime *rt);
+
 extern JS_FRIEND_API(size_t)
 JS_GetE4XObjectsCreated(JSContext *cx);
 
 extern JS_FRIEND_API(size_t)
 JS_SetProtoCalled(JSContext *cx);
 
 extern JS_FRIEND_API(size_t)
 JS_GetCustomIteratorCount(JSContext *cx);
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -512,16 +512,32 @@ ChunkPool::expire(JSRuntime *rt, bool re
             ++chunk->info.age;
             chunkp = &chunk->info.next;
         }
     }
     JS_ASSERT_IF(releaseAll, !emptyCount);
     return freeList;
 }
 
+static void
+FreeChunkList(Chunk *chunkListHead)
+{
+    while (Chunk *chunk = chunkListHead) {
+        JS_ASSERT(!chunk->info.numArenasFreeCommitted);
+        chunkListHead = chunk->info.next;
+        FreeChunk(chunk);
+    }
+}
+
+void
+ChunkPool::expireAndFree(JSRuntime *rt, bool releaseAll)
+{
+    FreeChunkList(expire(rt, releaseAll));
+}
+
 JS_FRIEND_API(int64_t)
 ChunkPool::countCleanDecommittedArenas(JSRuntime *rt)
 {
     JS_ASSERT(this == &rt->gcChunkPool);
 
     int64_t numDecommitted = 0;
     Chunk *chunk = emptyChunkListHead;
     while (chunk) {
@@ -548,26 +564,16 @@ Chunk::allocate(JSRuntime *rt)
 /* static */ inline void
 Chunk::release(JSRuntime *rt, Chunk *chunk)
 {
     JS_ASSERT(chunk);
     chunk->prepareToBeFreed(rt);
     FreeChunk(chunk);
 }
 
-static void
-FreeChunkList(Chunk *chunkListHead)
-{
-    while (Chunk *chunk = chunkListHead) {
-        JS_ASSERT(!chunk->info.numArenasFreeCommitted);
-        chunkListHead = chunk->info.next;
-        FreeChunk(chunk);
-    }
-}
-
 inline void
 Chunk::prepareToBeFreed(JSRuntime *rt)
 {
     JS_ASSERT(rt->gcNumArenasFreeCommitted >= info.numArenasFreeCommitted);
     rt->gcNumArenasFreeCommitted -= info.numArenasFreeCommitted;
     rt->gcStats.count(gcstats::STAT_DESTROY_CHUNK);
 
 #ifdef DEBUG
@@ -918,21 +924,22 @@ InFreeList(ArenaHeader *aheader, uintptr
          * The last possible empty span is an the end of the arena. Here
          * span->end < thing < thingsEnd and so we must have more spans.
          */
         span = span->nextSpan();
     }
 }
 
 /*
- * Returns CGCT_VALID and mark it if the w can be a  live GC thing and sets
- * thingKind accordingly. Otherwise returns the reason for rejection.
+ * Tests whether w is a (possibly dead) GC thing. Returns CGCT_VALID and
+ * details about the thing if so. On failure, returns the reason for rejection.
  */
 inline ConservativeGCTest
-MarkIfGCThingWord(JSTracer *trc, jsuword w)
+IsAddressableGCThing(JSRuntime *rt, jsuword w,
+                     gc::AllocKind *thingKindPtr, ArenaHeader **arenaHeader, void **thing)
 {
     /*
      * We assume that the compiler never uses sub-word alignment to store
      * pointers and does not tag pointers on its own. Additionally, the value
      * representation for all values and the jsid representation for GC-things
      * do not touch the low two bits. Thus any word with the low two bits set
      * is not a valid GC-thing.
      */
@@ -948,17 +955,17 @@ MarkIfGCThingWord(JSTracer *trc, jsuword
 #if JS_BITS_PER_WORD == 32
     jsuword addr = w & JSID_PAYLOAD_MASK;
 #elif JS_BITS_PER_WORD == 64
     jsuword addr = w & JSID_PAYLOAD_MASK & JSVAL_PAYLOAD_MASK;
 #endif
 
     Chunk *chunk = Chunk::fromAddress(addr);
 
-    if (!trc->runtime->gcChunkSet.has(chunk))
+    if (!rt->gcChunkSet.has(chunk))
         return CGCT_NOTCHUNK;
 
     /*
      * We query for pointers outside the arena array after checking for an
      * allocated chunk. Such pointers are rare and we want to reject them
      * after doing more likely rejections.
      */
     if (!Chunk::withinArenasRange(addr))
@@ -969,57 +976,79 @@ MarkIfGCThingWord(JSTracer *trc, jsuword
     if (chunk->decommittedArenas.get(arenaOffset))
         return CGCT_FREEARENA;
 
     ArenaHeader *aheader = &chunk->arenas[arenaOffset].aheader;
 
     if (!aheader->allocated())
         return CGCT_FREEARENA;
 
-    JSCompartment *curComp = trc->runtime->gcCurrentCompartment;
+    JSCompartment *curComp = rt->gcCurrentCompartment;
     if (curComp && curComp != aheader->compartment)
         return CGCT_OTHERCOMPARTMENT;
 
     AllocKind thingKind = aheader->getAllocKind();
     uintptr_t offset = addr & ArenaMask;
     uintptr_t minOffset = Arena::firstThingOffset(thingKind);
     if (offset < minOffset)
         return CGCT_NOTARENA;
 
     /* addr can point inside the thing so we must align the address. */
     uintptr_t shift = (offset - minOffset) % Arena::thingSize(thingKind);
     addr -= shift;
 
+    if (thing)
+        *thing = reinterpret_cast<void *>(addr);
+    if (arenaHeader)
+        *arenaHeader = aheader;
+    if (thingKindPtr)
+        *thingKindPtr = thingKind;
+    return CGCT_VALID;
+}
+
+/*
+ * Returns CGCT_VALID and mark it if the w can be a  live GC thing and sets
+ * thingKind accordingly. Otherwise returns the reason for rejection.
+ */
+inline ConservativeGCTest
+MarkIfGCThingWord(JSTracer *trc, jsuword w)
+{
+    void *thing;
+    ArenaHeader *aheader;
+    AllocKind thingKind;
+    ConservativeGCTest status = IsAddressableGCThing(trc->runtime, w, &thingKind, &aheader, &thing);
+    if (status != CGCT_VALID)
+        return status;
+
     /*
      * Check if the thing is free. We must use the list of free spans as at
      * this point we no longer have the mark bits from the previous GC run and
      * we must account for newly allocated things.
      */
-    if (InFreeList(aheader, addr))
+    if (InFreeList(aheader, uintptr_t(thing)))
         return CGCT_NOTLIVE;
 
-    void *thing = reinterpret_cast<void *>(addr);
-
 #ifdef DEBUG
     const char pattern[] = "machine_stack %lx";
-    char nameBuf[sizeof(pattern) - 3 + sizeof(addr) * 2];
-    JS_snprintf(nameBuf, sizeof(nameBuf), "machine_stack %lx", (unsigned long) addr);
+    char nameBuf[sizeof(pattern) - 3 + sizeof(thing) * 2];
+    JS_snprintf(nameBuf, sizeof(nameBuf), "machine_stack %lx", (unsigned long) thing);
     JS_SET_TRACING_NAME(trc, nameBuf);
 #endif
     MarkKind(trc, thing, MapAllocToTraceKind(thingKind));
 
 #ifdef JS_DUMP_CONSERVATIVE_GC_ROOTS
     if (IS_GC_MARKING_TRACER(trc)) {
         GCMarker *marker = static_cast<GCMarker *>(trc);
         if (marker->conservativeDumpFileName)
             marker->conservativeRoots.append(thing);
-        if (shift)
+        if (jsuword(thing) != w)
             marker->conservativeStats.unaligned++;
     }
 #endif
+
     return CGCT_VALID;
 }
 
 static void
 MarkWordConservatively(JSTracer *trc, jsuword w)
 {
     /*
      * The conservative scanner may access words that valgrind considers as
@@ -1149,16 +1178,22 @@ RecordNativeStackTopForGC(JSContext *cx)
     if (cx->thread()->data.requestDepth == ctd->requestThreshold)
         return;
 #endif
     ctd->recordStackTop();
 }
 
 } /* namespace js */
 
+bool
+js_IsAddressableGCThing(JSRuntime *rt, jsuword w, gc::AllocKind *thingKind, void **thing)
+{
+    return js::IsAddressableGCThing(rt, w, thingKind, NULL, thing) == CGCT_VALID;
+}
+
 #ifdef DEBUG
 static void
 CheckLeakedRoots(JSRuntime *rt);
 #endif
 
 void
 js_FinishGC(JSRuntime *rt)
 {
@@ -1177,17 +1212,17 @@ js_FinishGC(JSRuntime *rt)
 #ifdef JS_THREADSAFE
     rt->gcHelperThread.finish();
 #endif
 
     /*
      * Finish the pool after the background thread stops in case it was doing
      * the background sweeping.
      */
-    FreeChunkList(rt->gcChunkPool.expire(rt, true));
+    rt->gcChunkPool.expireAndFree(rt, true);
 
 #ifdef DEBUG
     if (!rt->gcRootsHash.empty())
         CheckLeakedRoots(rt);
 #endif
     rt->gcRootsHash.clear();
     rt->gcLocksHash.clear();
 }
@@ -1337,17 +1372,17 @@ JSRuntime::setGCLastBytes(size_t lastByt
     size_t base = gckind == GC_SHRINK ? lastBytes : Max(lastBytes, GC_ALLOCATION_THRESHOLD);
     float trigger = float(base) * GC_HEAP_GROWTH_FACTOR;
     gcTriggerBytes = size_t(Min(float(gcMaxBytes), trigger));
 }
 
 void
 JSRuntime::reduceGCTriggerBytes(uint32_t amount) {
     JS_ASSERT(amount > 0);
-    JS_ASSERT(gcTriggerBytes >= amount);
+    JS_ASSERT(gcTriggerBytes - amount >= 0);
     if (gcTriggerBytes - amount < GC_ALLOCATION_THRESHOLD * GC_HEAP_GROWTH_FACTOR)
         return;
     gcTriggerBytes -= amount;
 }
 
 void
 JSCompartment::setGCLastBytes(size_t lastBytes, JSGCInvocationKind gckind)
 {
@@ -1356,17 +1391,17 @@ JSCompartment::setGCLastBytes(size_t las
     size_t base = gckind == GC_SHRINK ? lastBytes : Max(lastBytes, GC_ALLOCATION_THRESHOLD);
     float trigger = float(base) * GC_HEAP_GROWTH_FACTOR;
     gcTriggerBytes = size_t(Min(float(rt->gcMaxBytes), trigger));
 }
 
 void
 JSCompartment::reduceGCTriggerBytes(uint32_t amount) {
     JS_ASSERT(amount > 0);
-    JS_ASSERT(gcTriggerBytes >= amount);
+    JS_ASSERT(gcTriggerBytes - amount >= 0);
     if (gcTriggerBytes - amount < GC_ALLOCATION_THRESHOLD * GC_HEAP_GROWTH_FACTOR)
         return;
     gcTriggerBytes -= amount;
 }
 
 namespace js {
 namespace gc {
 
@@ -1636,34 +1671,28 @@ RunLastDitchGC(JSContext *cx)
 #ifdef JS_THREADSAFE
     if (rt->gcBytes >= rt->gcMaxBytes) {
         AutoLockGC lock(rt);
         cx->runtime->gcHelperThread.waitBackgroundSweepEnd();
     }
 #endif
 }
 
-inline bool
-IsGCAllowed(JSContext *cx)
-{
-    return !JS_THREAD_DATA(cx)->waiveGCQuota;
-}
-
 /* static */ void *
 ArenaLists::refillFreeList(JSContext *cx, AllocKind thingKind)
 {
     JS_ASSERT(cx->compartment->arenas.freeLists[thingKind].isEmpty());
 
     JSCompartment *comp = cx->compartment;
     JSRuntime *rt = comp->rt;
     JS_ASSERT(!rt->gcRunning);
 
     bool runGC = !!rt->gcIsNeeded;
     for (;;) {
-        if (JS_UNLIKELY(runGC) && IsGCAllowed(cx)) {
+        if (JS_UNLIKELY(runGC)) {
             RunLastDitchGC(cx);
 
             /* Report OOM of the GC failed to free enough memory. */
             if (rt->gcBytes > rt->gcMaxBytes)
                 break;
 
             /*
              * The JSGC_END callback can legitimately allocate new GC
@@ -1674,24 +1703,21 @@ ArenaLists::refillFreeList(JSContext *cx
             if (void *thing = comp->arenas.allocateFromFreeList(thingKind, thingSize))
                 return thing;
         }
         void *thing = comp->arenas.allocateFromArena(comp, thingKind);
         if (JS_LIKELY(!!thing))
             return thing;
 
         /*
-         * We failed to allocate. Run the GC if we can unless we have done it
-         * already. Otherwise report OOM but first schedule a new GC soon.
+         * We failed to allocate. Run the GC if we haven't done it already.
+         * Otherwise report OOM.
          */
-        if (runGC || !IsGCAllowed(cx)) {
-            AutoLockGC lock(rt);
-            TriggerGC(rt, gcstats::REFILL);
+        if (runGC)
             break;
-        }
         runGC = true;
     }
 
     js_ReportOutOfMemory(cx);
     return NULL;
 }
 
 } /* namespace gc */
@@ -2392,36 +2418,61 @@ GCHelperThread::threadLoop()
             state = IDLE;
             PR_NotifyAllCondVar(done);
             break;
         }
     }
 }
 
 bool
-GCHelperThread::prepareForBackgroundSweep(JSContext *cx)
+GCHelperThread::prepareForBackgroundSweep()
 {
-    JS_ASSERT(cx->runtime == rt);
     JS_ASSERT(state == IDLE);
     size_t maxArenaLists = MAX_BACKGROUND_FINALIZE_KINDS * rt->compartments.length();
-    if (!finalizeVector.reserve(maxArenaLists))
-        return false;
-    context = cx;
-    return true;
+    return finalizeVector.reserve(maxArenaLists);
+}
+
+/* Must be called with the GC lock taken. */
+void
+GCHelperThread::startBackgroundSweep(JSContext *cx, bool shouldShrink)
+{
+    /* The caller takes the GC lock. */
+    JS_ASSERT(state == IDLE);
+    JS_ASSERT(cx);
+    JS_ASSERT(!finalizationContext);
+    finalizationContext = cx;
+    shrinkFlag = shouldShrink;
+    state = SWEEPING;
+    PR_NotifyCondVar(wakeup);
 }
 
 /* Must be called with the GC lock taken. */
-inline void
-GCHelperThread::startBackgroundSweep(bool shouldShrink)
+void
+GCHelperThread::startBackgroundShrink()
 {
-    /* The caller takes the GC lock. */
-    JS_ASSERT(state == IDLE);
-    shrinkFlag = shouldShrink;
-    state = SWEEPING;
-    PR_NotifyCondVar(wakeup);
+    switch (state) {
+      case IDLE:
+        JS_ASSERT(!finalizationContext);
+        shrinkFlag = true;
+        state = SWEEPING;
+        PR_NotifyCondVar(wakeup);
+        break;
+      case SWEEPING:
+        shrinkFlag = true;
+        break;
+      case ALLOCATING:
+      case CANCEL_ALLOCATION:
+        /*
+         * If we have started background allocation there is nothing to
+         * shrink.
+         */
+        break;
+      case SHUTDOWN:
+        JS_NOT_REACHED("No shrink on shutdown");
+    }
 }
 
 /* Must be called with the GC lock taken. */
 void
 GCHelperThread::waitBackgroundSweepEnd()
 {
     while (state == SWEEPING)
         PR_WaitCondVar(done, PR_INTERVAL_NO_TIMEOUT);
@@ -2465,46 +2516,54 @@ GCHelperThread::replenishAndFreeLater(vo
     } while (false);
     Foreground::free_(ptr);
 }
 
 /* Must be called with the GC lock taken. */
 void
 GCHelperThread::doSweep()
 {
-    JS_ASSERT(context);
-
-    {
+    if (JSContext *cx = finalizationContext) {
+        finalizationContext = NULL;
         AutoUnlockGC unlock(rt);
 
         /*
          * We must finalize in the insert order, see comments in
          * finalizeObjects.
          */
         for (ArenaHeader **i = finalizeVector.begin(); i != finalizeVector.end(); ++i)
-            ArenaLists::backgroundFinalize(context, *i);
+            ArenaLists::backgroundFinalize(cx, *i);
         finalizeVector.resize(0);
 
-        context = NULL;
-
         if (freeCursor) {
             void **array = freeCursorEnd - FREE_ARRAY_LENGTH;
             freeElementsAndArray(array, freeCursor);
             freeCursor = freeCursorEnd = NULL;
         } else {
             JS_ASSERT(!freeCursorEnd);
         }
         for (void ***iter = freeVector.begin(); iter != freeVector.end(); ++iter) {
             void **array = *iter;
             freeElementsAndArray(array, array + FREE_ARRAY_LENGTH);
         }
         freeVector.resize(0);
     }
 
-    ExpireChunksAndArenas(rt, shouldShrink());
+    bool shrinking = shrinkFlag;
+    ExpireChunksAndArenas(rt, shrinking);
+
+    /*
+     * The main thread may have called ShrinkGCBuffers while
+     * ExpireChunksAndArenas(rt, false) was running, so we recheck the flag
+     * afterwards.
+     */
+    if (!shrinking && shrinkFlag) {
+        shrinkFlag = false;
+        ExpireChunksAndArenas(rt, true);
+    }
 }
 
 #endif /* JS_THREADSAFE */
 
 } /* namespace js */
 
 static bool
 ReleaseObservedTypes(JSContext *cx)
@@ -2977,33 +3036,31 @@ GCCycle(JSContext *cx, JSCompartment *co
      * As we about to purge caches and clear the mark bits we must wait for
      * any background finalization to finish. We must also wait for the
      * background allocation to finish so we can avoid taking the GC lock
      * when manipulating the chunks during the GC.
      */
     JS_ASSERT(!cx->gcBackgroundFree);
     rt->gcHelperThread.waitBackgroundSweepOrAllocEnd();
     if (gckind != GC_LAST_CONTEXT && rt->state != JSRTS_LANDING) {
-        if (rt->gcHelperThread.prepareForBackgroundSweep(cx))
+        if (rt->gcHelperThread.prepareForBackgroundSweep())
             cx->gcBackgroundFree = &rt->gcHelperThread;
     }
 #endif
 
     MarkAndSweep(cx, gckind);
 
     if (!comp)
         js_PurgeThreads_PostGlobalSweep(cx);
 
 #ifdef JS_THREADSAFE
-    if (gckind != GC_LAST_CONTEXT && rt->state != JSRTS_LANDING) {
+    if (cx->gcBackgroundFree) {
         JS_ASSERT(cx->gcBackgroundFree == &rt->gcHelperThread);
         cx->gcBackgroundFree = NULL;
-        rt->gcHelperThread.startBackgroundSweep(gckind == GC_SHRINK);
-    } else {
-        JS_ASSERT(!cx->gcBackgroundFree);
+        rt->gcHelperThread.startBackgroundSweep(cx, gckind == GC_SHRINK);
     }
 #endif
 
     rt->gcMarkAndSweep = false;
     rt->setGCLastBytes(rt->gcBytes, gckind);
     rt->gcCurrentCompartment = NULL;
 
     for (CompartmentsIter c(rt); !c.done(); c.next())
@@ -3071,16 +3128,28 @@ js_GC(JSContext *cx, JSCompartment *comp
 
     rt->gcNextFullGCTime = PRMJ_Now() + GC_IDLE_FULL_SPAN;
 
     rt->gcChunkAllocationSinceLastGC = false;
 }
 
 namespace js {
 
+void
+ShrinkGCBuffers(JSRuntime *rt)
+{
+    AutoLockGC lock(rt);
+    JS_ASSERT(!rt->gcRunning);
+#ifndef JS_THREADSAFE
+    ExpireChunksAndArenas(rt, true);
+#else
+    rt->gcHelperThread.startBackgroundShrink();
+#endif
+}
+
 class AutoCopyFreeListToArenas {
     JSRuntime *rt;
 
   public:
     AutoCopyFreeListToArenas(JSRuntime *rt)
       : rt(rt) {
         for (CompartmentsIter c(rt); !c.done(); c.next())
             c->arenas.copyFreeListsToArenas();
@@ -3150,16 +3219,38 @@ struct IterateCellCallbackOp
     IterateCellCallbackOp(JSContext *cx, void *data, IterateCellCallback callback,
                           JSGCTraceKind traceKind, size_t thingSize)
         : cx(cx), data(data), callback(callback), traceKind(traceKind), thingSize(thingSize)
     {}
     void operator()(Cell *cell) { (*callback)(cx, data, cell, traceKind, thingSize); }
 };
 
 void
+IterateCompartments(JSContext *cx, void *data,
+                    IterateCompartmentCallback compartmentCallback)
+{
+    CHECK_REQUEST(cx);
+
+    JSRuntime *rt = cx->runtime;
+    JS_ASSERT(!rt->gcRunning);
+
+    AutoLockGC lock(rt);
+    AutoGCSession gcsession(cx);
+#ifdef JS_THREADSAFE
+    rt->gcHelperThread.waitBackgroundSweepEnd();
+#endif
+    AutoUnlockGC unlock(rt);
+
+    AutoCopyFreeListToArenas copy(rt);
+    for (CompartmentsIter c(rt); !c.done(); c.next()) {
+        (*compartmentCallback)(cx, data, c);
+    }
+}
+
+void
 IterateCompartmentsArenasCells(JSContext *cx, void *data,
                                IterateCompartmentCallback compartmentCallback,
                                IterateArenaCallback arenaCallback,
                                IterateCellCallback cellCallback)
 {
     CHECK_REQUEST(cx);
 
     JSRuntime *rt = cx->runtime;
@@ -3274,29 +3365,27 @@ NewCompartment(JSContext *cx, JSPrincipa
     Foreground::delete_(compartment);
     return NULL;
 }
 
 void
 RunDebugGC(JSContext *cx)
 {
 #ifdef JS_GC_ZEAL
-    if (IsGCAllowed(cx)) {
-        JSRuntime *rt = cx->runtime;
-
-        /*
-         * If rt->gcDebugCompartmentGC is true, only GC the current
-         * compartment. But don't GC the atoms compartment.
-         */
-        rt->gcTriggerCompartment = rt->gcDebugCompartmentGC ? cx->compartment : NULL;
-        if (rt->gcTriggerCompartment == rt->atomsCompartment)
-            rt->gcTriggerCompartment = NULL;
-
-        RunLastDitchGC(cx);
-    }
+    JSRuntime *rt = cx->runtime;
+
+    /*
+     * If rt->gcDebugCompartmentGC is true, only GC the current
+     * compartment. But don't GC the atoms compartment.
+     */
+    rt->gcTriggerCompartment = rt->gcDebugCompartmentGC ? cx->compartment : NULL;
+    if (rt->gcTriggerCompartment == rt->atomsCompartment)
+        rt->gcTriggerCompartment = NULL;
+
+    RunLastDitchGC(cx);
 #endif
 }
 
 #ifdef JS_GC_ZEAL
 
 /*
  * Write barrier verification
  *
--- a/js/src/jsgc.h
+++ b/js/src/jsgc.h
@@ -803,16 +803,19 @@ class ChunkPool {
     inline void put(Chunk *chunk);
 
     /*
      * Return the list of chunks that can be released outside the GC lock.
      * Must be called either during the GC or with the GC lock taken.
      */
     Chunk *expire(JSRuntime *rt, bool releaseAll);
 
+    /* Must be called with the GC lock taken. */
+    void expireAndFree(JSRuntime *rt, bool releaseAll);
+
     /* Must be called either during the GC or with the GC lock taken. */
     JS_FRIEND_API(int64_t) countCleanDecommittedArenas(JSRuntime *rt);
 };
 
 inline uintptr_t
 Cell::address() const
 {
     uintptr_t addr = uintptr_t(this);
@@ -1355,16 +1358,19 @@ extern bool
 IsAboutToBeFinalized(JSContext *cx, const js::Value &value);
 
 extern JS_FRIEND_API(bool)
 js_GCThingIsMarked(void *thing, uintN color);
 
 extern void
 js_TraceStackFrame(JSTracer *trc, js::StackFrame *fp);
 
+extern bool
+js_IsAddressableGCThing(JSRuntime *rt, jsuword w, js::gc::AllocKind *thingKind, void **thing);
+
 namespace js {
 
 extern JS_REQUIRES_STACK void
 MarkRuntime(JSTracer *trc);
 
 extern void
 TraceRuntime(JSTracer *trc);
 
@@ -1377,16 +1383,19 @@ TriggerGC(JSRuntime *rt, js::gcstats::Re
 
 /* Must be called with GC lock taken. */
 extern void
 TriggerCompartmentGC(JSCompartment *comp, js::gcstats::Reason reason);
 
 extern void
 MaybeGC(JSContext *cx);
 
+extern void
+ShrinkGCBuffers(JSRuntime *rt);
+
 } /* namespace js */
 
 /*
  * Kinds of js_GC invocation.
  */
 typedef enum JSGCInvocationKind {
     /* Normal invocation. */
     GC_NORMAL           = 0,
@@ -1448,17 +1457,17 @@ class GCHelperThread {
     static const size_t FREE_ARRAY_LENGTH = FREE_ARRAY_SIZE / sizeof(void *);
 
     JSRuntime         *const rt;
     PRThread          *thread;
     PRCondVar         *wakeup;
     PRCondVar         *done;
     volatile State    state;
 
-    JSContext         *context;
+    JSContext         *finalizationContext;
     bool              shrinkFlag;
 
     Vector<void **, 16, js::SystemAllocPolicy> freeVector;
     void            **freeCursor;
     void            **freeCursorEnd;
 
     Vector<js::gc::ArenaHeader *, 64, js::SystemAllocPolicy> finalizeVector;
 
@@ -1484,26 +1493,31 @@ class GCHelperThread {
 
   public:
     GCHelperThread(JSRuntime *rt)
       : rt(rt),
         thread(NULL),
         wakeup(NULL),
         done(NULL),
         state(IDLE),
+        finalizationContext(NULL),
+        shrinkFlag(false),
         freeCursor(NULL),
         freeCursorEnd(NULL),
         backgroundAllocation(true)
     { }
 
     bool init();
     void finish();
 
     /* Must be called with the GC lock taken. */
-    inline void startBackgroundSweep(bool shouldShrink);
+    void startBackgroundSweep(JSContext *cx, bool shouldShrink);
+
+    /* Must be called with the GC lock taken. */
+    void startBackgroundShrink();
 
     /* Must be called with the GC lock taken. */
     void waitBackgroundSweepEnd();
 
     /* Must be called with the GC lock taken. */
     void waitBackgroundSweepOrAllocEnd();
 
     /* Must be called with the GC lock taken. */
@@ -1538,17 +1552,17 @@ class GCHelperThread {
         JS_ASSERT(!sweeping());
         if (freeCursor != freeCursorEnd)
             *freeCursor++ = ptr;
         else
             replenishAndFreeLater(ptr);
     }
 
     /* Must be called with the GC lock taken. */
-    bool prepareForBackgroundSweep(JSContext *cx);
+    bool prepareForBackgroundSweep();
 };
 
 #endif /* JS_THREADSAFE */
 
 struct GCChunkHasher {
     typedef gc::Chunk *Lookup;
 
     /*
@@ -1768,16 +1782,22 @@ MarkStackRangeConservatively(JSTracer *t
 typedef void (*IterateCompartmentCallback)(JSContext *cx, void *data, JSCompartment *compartment);
 typedef void (*IterateChunkCallback)(JSContext *cx, void *data, gc::Chunk *chunk);
 typedef void (*IterateArenaCallback)(JSContext *cx, void *data, gc::Arena *arena,
                                      JSGCTraceKind traceKind, size_t thingSize);
 typedef void (*IterateCellCallback)(JSContext *cx, void *data, void *thing,
                                     JSGCTraceKind traceKind, size_t thingSize);
 
 /*
+ * This function calls |compartmentCallback| on every compartment.
+ */
+extern JS_FRIEND_API(void)
+IterateCompartments(JSContext *cx, void *data,
+                    IterateCompartmentCallback compartmentCallback);
+/*
  * This function calls |compartmentCallback| on every compartment,
  * |arenaCallback| on every in-use arena, and |cellCallback| on every in-use
  * cell in the GC heap.
  */
 extern JS_FRIEND_API(void)
 IterateCompartmentsArenasCells(JSContext *cx, void *data,
                                IterateCompartmentCallback compartmentCallback,
                                IterateArenaCallback arenaCallback,
--- a/js/src/jsinfer.cpp
+++ b/js/src/jsinfer.cpp
@@ -2612,17 +2612,17 @@ struct types::ObjectTableKey
             shape = shape->previous();
         }
         return true;
     }
 };
 
 struct types::ObjectTableEntry
 {
-    TypeObject *object;
+    ReadBarriered<TypeObject> object;
     Type *types;
 };
 
 void
 TypeCompartment::fixObjectType(JSContext *cx, JSObject *obj)
 {
     AutoEnterTypeInference enter(cx);
 
@@ -5832,19 +5832,16 @@ JSObject::getNewType(JSContext *cx, JSFu
          * This case is rare, but can happen if, for example, two scripted
          * functions have the same value for their 'prototype' property, or if
          * Object.create is called with a prototype object that is also the
          * 'prototype' property of some scripted function.
          */
         if (type->newScript && type->newScript->fun != fun)
             type->clearNewScript(cx);
 
-        if (cx->compartment->needsBarrier())
-            TypeObject::readBarrier(type);
-
         return type;
     }
 
     bool markUnknown = lastProperty()->hasObjectFlag(BaseShape::NEW_TYPE_UNKNOWN);
 
     TypeObject *type = cx->compartment->types.newTypeObject(cx, NULL,
                                                             JSProto_Object, this, markUnknown);
     if (!type)
@@ -5900,19 +5897,16 @@ JSCompartment::getLazyType(JSContext *cx
     if (!table.initialized() && !table.init())
         return NULL;
 
     TypeObjectSet::AddPtr p = table.lookupForAdd(proto);
     if (p) {
         TypeObject *type = *p;
         JS_ASSERT(type->lazy());
 
-        if (cx->compartment->needsBarrier())
-            TypeObject::readBarrier(type);
-
         return type;
     }
 
     TypeObject *type = cx->compartment->types.newTypeObject(cx, NULL,
                                                             JSProto_Object, proto, false);
     if (!type)
         return NULL;
 
--- a/js/src/jsinfer.h
+++ b/js/src/jsinfer.h
@@ -893,17 +893,17 @@ struct TypeObject : gc::Cell
  */
 struct TypeObjectEntry
 {
     typedef JSObject *Lookup;
 
     static inline HashNumber hash(JSObject *base);
     static inline bool match(TypeObject *key, JSObject *lookup);
 };
-typedef HashSet<TypeObject *, TypeObjectEntry, SystemAllocPolicy> TypeObjectSet;
+typedef HashSet<ReadBarriered<TypeObject>, TypeObjectEntry, SystemAllocPolicy> TypeObjectSet;
 
 /*
  * Call to mark a script's arguments as having been created, recompile any
  * dependencies and walk the stack if necessary to fix any lazy arguments.
  */
 extern void
 MarkArgumentsCreated(JSContext *cx, JSScript *script);
 
@@ -1115,24 +1115,24 @@ class TypeScript
     static inline void SetArgument(JSContext *cx, JSScript *script, unsigned arg, const js::Value &value);
 
     static void Sweep(JSContext *cx, JSScript *script);
     inline void trace(JSTracer *trc);
     void destroy();
 };
 
 struct ArrayTableKey;
-typedef HashMap<ArrayTableKey,TypeObject*,ArrayTableKey,SystemAllocPolicy> ArrayTypeTable;
+typedef HashMap<ArrayTableKey,ReadBarriered<TypeObject>,ArrayTableKey,SystemAllocPolicy> ArrayTypeTable;
 
 struct ObjectTableKey;
 struct ObjectTableEntry;
 typedef HashMap<ObjectTableKey,ObjectTableEntry,ObjectTableKey,SystemAllocPolicy> ObjectTypeTable;
 
 struct AllocationSiteKey;
-typedef HashMap<AllocationSiteKey,TypeObject*,AllocationSiteKey,SystemAllocPolicy> AllocationSiteTable;
+typedef HashMap<AllocationSiteKey,ReadBarriered<TypeObject>,AllocationSiteKey,SystemAllocPolicy> AllocationSiteTable;
 
 /* Type information for a compartment. */
 struct TypeCompartment
 {
     /* Whether type inference is enabled in this compartment. */
     bool inferenceEnabled;
 
     /* Number of scripts in this compartment. */
--- a/js/src/jsinferinlines.h
+++ b/js/src/jsinferinlines.h
@@ -1274,19 +1274,18 @@ TypeObject::writeBarrierPost(TypeObject 
 {
 }
 
 inline void
 TypeObject::readBarrier(TypeObject *type)
 {
 #ifdef JSGC_INCREMENTAL
     JSCompartment *comp = type->compartment();
-    JS_ASSERT(comp->needsBarrier());
-
-    MarkTypeObjectUnbarriered(comp->barrierTracer(), type, "read barrier");
+    if (comp->needsBarrier())
+        MarkTypeObjectUnbarriered(comp->barrierTracer(), type, "read barrier");
 #endif
 }
 
 inline void
 TypeNewScript::writeBarrierPre(TypeNewScript *newScript)
 {
 #ifdef JSGC_INCREMENTAL
     if (!newScript)
--- a/js/src/jsprobes.cpp
+++ b/js/src/jsprobes.cpp
@@ -50,16 +50,18 @@
 #include "jsdbgapi.h"
 #include "jsfun.h"
 #include "jsinterp.h"
 #include "jsobj.h"
 #include "jsprobes.h"
 #include "jsscript.h"
 #include "jsstr.h"
 
+#include "methodjit/Compiler.h"
+
 #include "jsobjinlines.h"
 
 #define TYPEOF(cx,v)    (JSVAL_IS_NULL(v) ? JSTYPE_NULL : JS_TypeOfValue(cx,v))
 
 using namespace js;
 
 const char Probes::nullName[] = "(null)";
 const char Probes::anonymousName[] = "(anonymous)";
@@ -110,25 +112,124 @@ Probes::JITGranularityRequested()
         if (request > want)
             want = request;
     }
 
     return want;
 }
 
 #ifdef JS_METHODJIT
+/*
+ * Flatten the tree of inlined frames into a series of native code regions, one
+ * for each contiguous section of native code that belongs to a single
+ * ActiveFrame. (Note that some of these regions may be zero-length, for
+ * example if two ActiveFrames end at the same place.)
+ */
+typedef mjit::Compiler::ActiveFrame ActiveFrame;
+
+bool
+Probes::JITWatcher::CollectNativeRegions(RegionVector &regions,
+                                         JSRuntime *rt,
+                                         mjit::JITScript *jit,
+                                         mjit::JSActiveFrame *outerFrame,
+                                         mjit::JSActiveFrame **inlineFrames)
+{
+    regions.resize(jit->nInlineFrames * 2 + 2);
+
+    mjit::JSActiveFrame **stack =
+        rt->array_new<mjit::JSActiveFrame*>(jit->nInlineFrames+2);
+    if (!stack)
+        return false;
+    uint32_t depth = 0;
+    uint32_t ip = 0;
+
+    stack[depth++] = NULL;
+    stack[depth++] = outerFrame;
+    regions[0].frame = outerFrame;
+    regions[0].script = outerFrame->script;
+    regions[0].pc = outerFrame->script->code;
+    regions[0].enter = true;
+    ip++;
+
+    for (uint32_t i = 0; i <= jit->nInlineFrames; i++) {
+        mjit::JSActiveFrame *frame = (i < jit->nInlineFrames) ? inlineFrames[i] : outerFrame;
+
+        // Not a down frame; pop the current frame, then pop until we reach
+        // this frame's parent, recording subframe ends as we go
+        while (stack[depth-1] != frame->parent) {
+            depth--;
+            JS_ASSERT(depth > 0);
+            // Pop up from regions[ip-1].frame to top of the stack: start a
+            // region in the destination frame and close off the source
+            // (origin) frame at the end of its script
+            mjit::JSActiveFrame *src = regions[ip-1].frame;
+            mjit::JSActiveFrame *dst = stack[depth-1];
+            JS_ASSERT_IF(!dst, i == jit->nInlineFrames);
+            regions[ip].frame = dst;
+            regions[ip].script = dst ? dst->script : NULL;
+            regions[ip].pc = src->parentPC + 1;
+            regions[ip-1].endpc = src->script->code + src->script->length;
+            regions[ip].enter = false;
+            ip++;
+        }
+
+        if (i < jit->nInlineFrames) {
+            // Push a frame (enter an inlined function). Start a region at the
+            // beginning of the new frame's script, and end the previous region
+            // at parentPC.
+            stack[depth++] = frame;
+
+            regions[ip].frame = frame;
+            regions[ip].script = frame->script;
+            regions[ip].pc = frame->script->code;
+            regions[ip-1].endpc = frame->parentPC;
+            regions[ip].enter = true;
+            ip++;
+        }
+    }
+
+    // Final region is always zero-length and not particularly useful
+    ip--;
+    regions.popBack();
+
+    mjit::JSActiveFrame *prev = NULL;
+    for (NativeRegion *iter = regions.begin(); iter != regions.end(); ++iter) {
+        mjit::JSActiveFrame *frame = iter->frame;
+        if (iter->enter) {
+            // Pushing down a frame, so region starts at the beginning of the
+            // (destination) frame
+            iter->mainOffset = frame->mainCodeStart;
+            iter->stubOffset = frame->stubCodeStart;
+        } else {
+            // Popping up a level, so region starts at the end of the (source) frame
+            iter->mainOffset = prev->mainCodeEnd;
+            iter->stubOffset = prev->stubCodeEnd;
+        }
+        prev = frame;
+    }
+
+    JS_ASSERT(ip == 2 * jit->nInlineFrames + 1);
+    rt->array_delete(stack);
+
+    // All of the stub code comes immediately after the main code
+    for (NativeRegion *iter = regions.begin(); iter != regions.end(); ++iter)
+        iter->stubOffset += outerFrame->mainCodeEnd;
+
+    return true;
+}
+
 void
 Probes::registerMJITCode(JSContext *cx, js::mjit::JITScript *jscr,
-                         JSScript *script, JSFunction *fun,
-                         js::mjit::Compiler_ActiveFrame **inlineFrames,
+                         js::mjit::JSActiveFrame *outerFrame,
+                         js::mjit::JSActiveFrame **inlineFrames,
                          void *mainCodeAddress, size_t mainCodeSize,
                          void *stubCodeAddress, size_t stubCodeSize)
 {
     for (JITWatcher **p = jitWatchers.begin(); p != jitWatchers.end(); ++p)
-        (*p)->registerMJITCode(cx, jscr, script, fun,
+        (*p)->registerMJITCode(cx, jscr, outerFrame,
                                inlineFrames,
                                mainCodeAddress, mainCodeSize,
                                stubCodeAddress, stubCodeSize);
 }
 
 void
 Probes::discardMJITCode(JSContext *cx, mjit::JITScript *jscr, JSScript *script, void* address)
 {
--- a/js/src/jsprobes.h
+++ b/js/src/jsprobes.h
@@ -47,17 +47,17 @@
 #include "jsprvtd.h"
 #include "jsscript.h"
 #include "jsobj.h"
 
 namespace js {
 
 namespace mjit {
 struct NativeAddressInfo;
-struct Compiler_ActiveFrame;
+struct JSActiveFrame;
 }
 
 namespace Probes {
 
 /*
  * Static probes
  *
  * The probe points defined in this file are scattered around the SpiderMonkey
@@ -225,22 +225,41 @@ enum JITReportGranularity {
 
 /*
  * Observer class for JIT code allocation/deallocation. Currently, this only
  * handles the method JIT, and does not get notifications when JIT code is
  * changed (patched) with no new allocation.
  */
 class JITWatcher {
 public:
+    struct NativeRegion {
+        mjit::JSActiveFrame *frame;
+        JSScript *script;
+        size_t inlinedOffset;
+        jsbytecode *pc;
+        jsbytecode *endpc;
+        uintptr_t mainOffset;
+        uintptr_t stubOffset;
+        bool enter;
+    };
+
+    typedef Vector<NativeRegion, 0, RuntimeAllocPolicy> RegionVector;
+
     virtual JITReportGranularity granularityRequested() = 0;
 
 #ifdef JS_METHODJIT
+    static bool CollectNativeRegions(RegionVector &regions,
+                                     JSRuntime *rt,
+                                     mjit::JITScript *jit,
+                                     mjit::JSActiveFrame *outerFrame,
+                                     mjit::JSActiveFrame **inlineFrames);
+
     virtual void registerMJITCode(JSContext *cx, js::mjit::JITScript *jscr,
-                                  JSScript *script, JSFunction *fun,
-                                  mjit::Compiler_ActiveFrame** inlineFrames,
+                                  mjit::JSActiveFrame *outerFrame,
+                                  mjit::JSActiveFrame **inlineFrames,
                                   void *mainCodeAddress, size_t mainCodeSize,
                                   void *stubCodeAddress, size_t stubCodeSize) = 0;
 
     virtual void discardMJITCode(JSContext *cx, mjit::JITScript *jscr, JSScript *script,
                                  void* address) = 0;
 
     virtual void registerICCode(JSContext *cx,
                                 js::mjit::JITScript *jscr, JSScript *script, jsbytecode* pc,
@@ -277,18 +296,18 @@ JITReportGranularity
 JITGranularityRequested();
 
 #ifdef JS_METHODJIT
 /*
  * New method JIT code has been created
  */
 void
 registerMJITCode(JSContext *cx, js::mjit::JITScript *jscr,
-                 JSScript *script, JSFunction *fun,
-                 mjit::Compiler_ActiveFrame** inlineFrames,
+                 mjit::JSActiveFrame *outerFrame,
+                 mjit::JSActiveFrame **inlineFrames,
                  void *mainCodeAddress, size_t mainCodeSize,
                  void *stubCodeAddress, size_t stubCodeSize);
 
 /*
  * Method JIT code is about to be discarded
  */
 void
 discardMJITCode(JSContext *cx, mjit::JITScript *jscr, JSScript *script, void* address);
--- a/js/src/jsreflect.cpp
+++ b/js/src/jsreflect.cpp
@@ -614,18 +614,16 @@ class NodeBuilder
     bool xmlName(NodeVector &elts, TokenPos *pos, Value *dst);
 
     bool xmlAttribute(Value text, TokenPos *pos, Value *dst);
 
     bool xmlCdata(Value text, TokenPos *pos, Value *dst);
 
     bool xmlComment(Value text, TokenPos *pos, Value *dst);
 
-    bool xmlPI(Value target, TokenPos *pos, Value *dst);
-
     bool xmlPI(Value target, Value content, TokenPos *pos, Value *dst);
 };
 
 bool
 NodeBuilder::newNode(ASTType type, TokenPos *pos, JSObject **dst)
 {
     JS_ASSERT(type > AST_ERROR && type < AST_LIMIT);
 
@@ -1566,22 +1564,16 @@ NodeBuilder::xmlComment(Value text, Toke
     Value cb = callbacks[AST_XMLCOMMENT];
     if (!cb.isNull())
         return callback(cb, text, pos, dst);
 
     return newNode(AST_XMLCOMMENT, pos, "contents", text, dst);
 }
 
 bool
-NodeBuilder::xmlPI(Value target, TokenPos *pos, Value *dst)
-{
-    return xmlPI(target, NullValue(), pos, dst);
-}
-
-bool
 NodeBuilder::xmlPI(Value target, Value contents, TokenPos *pos, Value *dst)
 {
     Value cb = callbacks[AST_XMLPI];
     if (!cb.isNull())
         return callback(cb, target, contents, pos, dst);
 
     return newNode(AST_XMLPI, pos,
                    "target", target,
@@ -2405,17 +2397,17 @@ ASTSerializer::expression(ParseNode *pn,
 
       case PNK_COMMA:
       {
         NodeVector exprs(cx);
         return expressions(pn, exprs) &&
                builder.sequenceExpression(exprs, &pn->pn_pos, dst);
       }
 
-      case PNK_HOOK:
+      case PNK_CONDITIONAL:
       {
         Value test, cons, alt;
 
         return expression(pn->pn_kid1, &test) &&
                expression(pn->pn_kid2, &cons) &&
                expression(pn->pn_kid3, &alt) &&
                builder.conditionalExpression(test, cons, alt, &pn->pn_pos, dst);
       }
@@ -2625,23 +2617,26 @@ ASTSerializer::expression(ParseNode *pn,
       {
         Value arg;
         return optExpression(pn->pn_kid, &arg) &&
                builder.yieldExpression(arg, &pn->pn_pos, dst);
       }
 
       case PNK_DEFSHARP:
       {
+        DefSharpExpression &defsharp = pn->asDefSharpExpression();
         Value expr;
-        return expression(pn->pn_kid, &expr) &&
-               builder.graphExpression(pn->pn_num, expr, &pn->pn_pos, dst);
+        return expression(&defsharp.expression(), &expr) &&
+               builder.graphExpression(defsharp.number(), expr, &defsharp.pn_pos, dst);
       }
 
-      case PNK_USESHARP:
-        return builder.graphIndexExpression(pn->pn_num, &pn->pn_pos, dst);
+      case PNK_USESHARP: {
+        UseSharpExpression &expr = pn->asUseSharpExpression();
+        return builder.graphIndexExpression(expr.number(), &expr.pn_pos, dst);
+      }
 
       case PNK_ARRAYCOMP:
         /* NB: it's no longer the case that pn_count could be 2. */
         LOCAL_ASSERT(pn->pn_count == 1);
         LOCAL_ASSERT(pn->pn_head->isKind(PNK_LEXICALSCOPE));
 
         return comprehension(pn->pn_head->pn_expr, dst);
 
@@ -2790,24 +2785,23 @@ ASTSerializer::xml(ParseNode *pn, Value 
         return builder.xmlAttribute(atomContents(pn->pn_atom), &pn->pn_pos, dst);
 
       case PNK_XMLCDATA:
         return builder.xmlCdata(atomContents(pn->pn_atom), &pn->pn_pos, dst);
 
       case PNK_XMLCOMMENT:
         return builder.xmlComment(atomContents(pn->pn_atom), &pn->pn_pos, dst);
 
-      case PNK_XMLPI:
-        if (!pn->pn_pidata)
-            return builder.xmlPI(atomContents(pn->pn_pitarget), &pn->pn_pos, dst);
-        else
-            return builder.xmlPI(atomContents(pn->pn_pitarget),
-                                 atomContents(pn->pn_pidata),
-                                 &pn->pn_pos,
-                                 dst);
+      case PNK_XMLPI: {
+        XMLProcessingInstruction &pi = pn->asXMLProcessingInstruction();
+        return builder.xmlPI(atomContents(pi.target()),
+                             atomContents(pi.data()),
+                             &pi.pn_pos,
+                             dst);
+      }
 #endif
 
       default:
         LOCAL_NOT_REACHED("unexpected XML node type");
     }
 }
 
 bool
--- a/js/src/jsscope.cpp
+++ b/js/src/jsscope.cpp
@@ -1270,24 +1270,18 @@ BaseShape::getUnowned(JSContext *cx, con
 {
     BaseShapeSet &table = cx->compartment->baseShapes;
 
     if (!table.initialized() && !table.init())
         return NULL;
 
     BaseShapeSet::AddPtr p = table.lookupForAdd(&base);
 
-    if (p) {
-        UnownedBaseShape *base = *p;
-
-        if (cx->compartment->needsBarrier())
-            BaseShape::readBarrier(base);
-
-        return base;
-    }
+    if (p)
+        return *p;
 
     BaseShape *nbase_ = js_NewGCBaseShape(cx);
     if (!nbase_)
         return NULL;
     new (nbase_) BaseShape(base);
 
     UnownedBaseShape *nbase = static_cast<UnownedBaseShape *>(nbase_);
 
@@ -1377,24 +1371,18 @@ EmptyShape::getInitialShape(JSContext *c
     if (!table.initialized() && !table.init())
         return NULL;
 
     size_t nfixed = GetGCKindSlots(kind, clasp);
     InitialShapeEntry::Lookup lookup(clasp, proto, parent, nfixed, objectFlags);
 
     InitialShapeSet::AddPtr p = table.lookupForAdd(lookup);
 
-    if (p) {
-        Shape *shape = p->shape;
-
-        if (cx->compartment->needsBarrier())
-            Shape::readBarrier(shape);
-
-        return shape;
-    }
+    if (p)
+        return p->shape;
 
     BaseShape base(clasp, parent, objectFlags);
     UnownedBaseShape *nbase = BaseShape::getUnowned(cx, base);
     if (!nbase)
         return NULL;
 
     Shape *shape = JS_PROPERTY_TREE(cx).newShape(cx);
     if (!shape)
--- a/js/src/jsscope.h
+++ b/js/src/jsscope.h
@@ -488,17 +488,17 @@ BaseShape::baseUnowned()
 /* Entries for the per-compartment baseShapes set of unowned base shapes. */
 struct BaseShapeEntry
 {
     typedef const BaseShape *Lookup;
 
     static inline HashNumber hash(const BaseShape *base);
     static inline bool match(UnownedBaseShape *key, const BaseShape *lookup);
 };
-typedef HashSet<UnownedBaseShape *, BaseShapeEntry, SystemAllocPolicy> BaseShapeSet;
+typedef HashSet<ReadBarriered<UnownedBaseShape>, BaseShapeEntry, SystemAllocPolicy> BaseShapeSet;
 
 struct Shape : public js::gc::Cell
 {
     friend struct ::JSObject;
     friend struct ::JSFunction;
     friend class js::PropertyTree;
     friend class js::Bindings;
     friend bool IsShapeAboutToBeFinalized(JSContext *cx, const js::Shape *shape);
@@ -970,17 +970,17 @@ struct EmptyShape : public js::Shape
  */
 struct InitialShapeEntry
 {
     /*
      * Initial shape to give to the object. This is an empty shape, except for
      * certain classes (e.g. String, RegExp) which may add certain baked-in
      * properties.
      */
-    js::Shape *shape;
+    ReadBarriered<Shape> shape;
 
     /*
      * Matching prototype for the entry. The shape of an object determines its
      * prototype, but the prototype cannot be determined from the shape itself.
      */
     JSObject *proto;
 
     /* State used to determine a match on an initial shape. */
--- a/js/src/jsscopeinlines.h
+++ b/js/src/jsscopeinlines.h
@@ -357,19 +357,18 @@ Shape::writeBarrierPost(const js::Shape 
 {
 }
 
 inline void
 Shape::readBarrier(const Shape *shape)
 {
 #ifdef JSGC_INCREMENTAL
     JSCompartment *comp = shape->compartment();
-    JS_ASSERT(comp->needsBarrier());
-
-    MarkShapeUnbarriered(comp->barrierTracer(), shape, "read barrier");
+    if (comp->needsBarrier())
+        MarkShapeUnbarriered(comp->barrierTracer(), shape, "read barrier");
 #endif
 }
 
 inline void
 BaseShape::writeBarrierPre(BaseShape *base)
 {
 #ifdef JSGC_INCREMENTAL
     if (!base)
@@ -386,17 +385,16 @@ BaseShape::writeBarrierPost(BaseShape *s
 {
 }
 
 inline void
 BaseShape::readBarrier(BaseShape *base)
 {
 #ifdef JSGC_INCREMENTAL
     JSCompartment *comp = base->compartment();
-    JS_ASSERT(comp->needsBarrier());
-
-    MarkBaseShapeUnbarriered(comp->barrierTracer(), base, "read barrier");
+    if (comp->needsBarrier())
+        MarkBaseShapeUnbarriered(comp->barrierTracer(), base, "read barrier");
 #endif
 }
 
 } /* namespace js */
 
 #endif /* jsscopeinlines_h___ */
--- a/js/src/jstypedarray.cpp
+++ b/js/src/jstypedarray.cpp
@@ -1682,17 +1682,19 @@ class TypedArrayTemplate
 
     static void copyIndexToValue(JSContext *cx, JSObject *tarray, uint32_t index, Value *vp);
 
     static JSObject *
     createSubarray(JSContext *cx, JSObject *tarray, uint32_t begin, uint32_t end)
     {
         JS_ASSERT(tarray);
 
+        JS_ASSERT(0 <= begin);
         JS_ASSERT(begin <= getLength(tarray));
+        JS_ASSERT(0 <= end);
         JS_ASSERT(end <= getLength(tarray));
 
         JSObject *bufobj = getBuffer(tarray);
         JS_ASSERT(bufobj);
 
         JS_ASSERT(begin <= end);
         uint32_t length = end - begin;
 
--- a/js/src/jsxml.cpp
+++ b/js/src/jsxml.cpp
@@ -1616,34 +1616,35 @@ ParseNodeToXML(Parser *parser, ParseNode
       case PNK_XMLPI:
         str = pn->pn_atom;
         qn = NULL;
         if (pn->isKind(PNK_XMLCOMMENT)) {
             if (flags & XSF_IGNORE_COMMENTS)
                 goto skip_child;
             xml_class = JSXML_CLASS_COMMENT;
         } else if (pn->isKind(PNK_XMLPI)) {
+            XMLProcessingInstruction &pi = pn->asXMLProcessingInstruction();
             if (IS_XML(str)) {
                 Value v = StringValue(str);
                 JSAutoByteString bytes;
                 if (js_ValueToPrintable(cx, v, &bytes)) {
-                    ReportCompileErrorNumber(cx, &parser->tokenStream, pn,
+                    ReportCompileErrorNumber(cx, &parser->tokenStream, &pi,
                                              JSREPORT_ERROR, JSMSG_RESERVED_ID, bytes.ptr());
                 }
                 goto fail;
             }
 
             if (flags & XSF_IGNORE_PROCESSING_INSTRUCTIONS)
                 goto skip_child;
 
-            qn = ParseNodeToQName(parser, pn, inScopeNSes, JS_FALSE);
+            qn = ParseNodeToQName(parser, &pi, inScopeNSes, JS_FALSE);
             if (!qn)
                 goto fail;
 
-            str = pn->pn_pidata ? pn->pn_pidata : cx->runtime->emptyString;
+            str = pi.data();
             xml_class = JSXML_CLASS_PROCESSING_INSTRUCTION;
         } else {
             /* CDATA section content, or element text. */
             xml_class = JSXML_CLASS_TEXT;
         }
 
         xml = js_NewXML(cx, xml_class);
         if (!xml)
--- a/js/src/methodjit/Compiler.cpp
+++ b/js/src/methodjit/Compiler.cpp
@@ -481,18 +481,20 @@ mjit::Compiler::pushActiveFrame(JSScript
 
     return Compile_Okay;
 }
 
 void
 mjit::Compiler::popActiveFrame()
 {
     JS_ASSERT(a->parent);
+    a->mainCodeEnd = masm.size();
+    a->stubCodeEnd = stubcc.size();
     this->PC = a->parentPC;
-    this->a = a->parent;
+    this->a = (ActiveFrame *) a->parent;
     this->script = a->script;
     this->analysis = this->script->analysis();
 
     frame.popActiveFrame();
 }
 
 #define CHECK_STATUS(expr)                                           \
     JS_BEGIN_MACRO                                                   \
@@ -546,19 +548,24 @@ mjit::Compiler::performCompilation(JITSc
 
     JaegerSpew(JSpew_Scripts, "successfully compiled (code \"%p\") (size \"%u\")\n",
                (*jitp)->code.m_code.executableAddress(), unsigned((*jitp)->code.m_size));
     return Compile_Okay;
 }
 
 #undef CHECK_STATUS
 
+mjit::JSActiveFrame::JSActiveFrame()
+    : parent(NULL), parentPC(NULL), script(NULL), inlineIndex(UINT32_MAX)
+{
+}
+
 mjit::Compiler::ActiveFrame::ActiveFrame(JSContext *cx)
-    : parent(NULL), parentPC(NULL), script(NULL), jumpMap(NULL),
-      inlineIndex(UINT32_MAX), varTypes(NULL), needReturnValue(false),
+    : jumpMap(NULL),
+      varTypes(NULL), needReturnValue(false),
       syncReturnValue(false), returnValueDouble(false), returnSet(false),
       returnEntry(NULL), returnJumps(NULL), exitState(NULL)
 {}
 
 mjit::Compiler::ActiveFrame::~ActiveFrame()
 {
     js::Foreground::free_(jumpMap);
     if (varTypes)
@@ -917,16 +924,19 @@ mjit::Compiler::finishThisUp(JITScript *
     if (cx->runtime->gcNumber != gcNumber)
         return Compile_Retry;
 
     if (overflowICSpace) {
         JaegerSpew(JSpew_Scripts, "dumped a constant pool while generating an IC\n");
         return Compile_Abort;
     }
 
+    a->mainCodeEnd = masm.size();
+    a->stubCodeEnd = stubcc.size();
+
     for (size_t i = 0; i < branchPatches.length(); i++) {
         Label label = labelOf(branchPatches[i].pc, branchPatches[i].inlineIndex);
         branchPatches[i].jump.linkTo(label, &masm);
     }
 
 #ifdef JS_CPU_ARM
     masm.forceFlushConstantPool();
     stubcc.masm.forceFlushConstantPool();
@@ -1380,18 +1390,19 @@ mjit::Compiler::finishThisUp(JITScript *
 
     /* Patch all outgoing calls. */
     masm.finalize(fullCode, inlineDoubles);
     stubcc.masm.finalize(stubCode, oolDoubles);
 
     JSC::ExecutableAllocator::makeExecutable(result, masm.size() + stubcc.size());
     JSC::ExecutableAllocator::cacheFlush(result, masm.size() + stubcc.size());
 
-    Probes::registerMJITCode(cx, jit, script, script->function() ? script->function() : NULL,
-                             (mjit::Compiler_ActiveFrame**) inlineFrames.begin(),
+    Probes::registerMJITCode(cx, jit,
+                             a,
+                             (JSActiveFrame**) inlineFrames.begin(),
                              result, masm.size(),
                              result + masm.size(), stubcc.size());
 
     *jitp = jit;
 
     return Compile_Okay;
 }
 
--- a/js/src/methodjit/Compiler.h
+++ b/js/src/methodjit/Compiler.h
@@ -57,16 +57,37 @@ namespace mjit {
  * redirecting the return address in InvariantFailure.
  */
 struct InvariantCodePatch {
     bool hasPatch;
     JSC::MacroAssembler::DataLabelPtr codePatch;
     InvariantCodePatch() : hasPatch(false) {}
 };
 
+struct JSActiveFrame {
+    JSActiveFrame *parent;
+    jsbytecode *parentPC;
+    JSScript *script;
+
+    /*
+     * Index into inlineFrames or OUTER_FRAME, matches this frame's index in
+     * the cross script SSA.
+     */
+    uint32_t inlineIndex;
+
+    /* JIT code generation tracking state */
+    size_t mainCodeStart;
+    size_t stubCodeStart;
+    size_t mainCodeEnd;
+    size_t stubCodeEnd;
+    size_t inlinePCOffset;
+
+    JSActiveFrame();
+};
+
 class Compiler : public BaseCompiler
 {
     friend class StubCompiler;
 
     struct BranchPatch {
         BranchPatch(const Jump &j, jsbytecode *pc, uint32_t inlineIndex)
           : jump(j), pc(pc), inlineIndex(inlineIndex)
         { }
@@ -350,36 +371,22 @@ class Compiler : public BaseCompiler
     FrameState frame;
 
     /*
      * State for the current stack frame, and links to its parents going up to
      * the outermost script.
      */
 
 public:
-    struct ActiveFrame {
-        ActiveFrame *parent;
-        jsbytecode *parentPC;
-        JSScript *script;
+    struct ActiveFrame : public JSActiveFrame {
         Label *jumpMap;
 
-        /*
-         * Index into inlineFrames or OUTER_FRAME, matches this frame's index
-         * in the cross script SSA.
-         */
-        uint32_t inlineIndex;
-
         /* Current types for non-escaping vars in the script. */
         VarType *varTypes;
 
-        /* JIT code generation tracking state */
-        size_t mainCodeStart;
-        size_t stubCodeStart;
-        size_t inlinePCOffset;
-
         /* State for managing return from inlined frames. */
         bool needReturnValue;          /* Return value will be used. */
         bool syncReturnValue;          /* Return value should be fully synced. */
         bool returnValueDouble;        /* Return value should be a double. */
         bool returnSet;                /* Whether returnRegister is valid. */
         AnyRegisterID returnRegister;  /* Register holding return value. */
         const FrameEntry *returnEntry; /* Entry copied by return value. */
         Vector<Jump, 4, CompilerAllocPolicy> *returnJumps;
@@ -465,17 +472,17 @@ private:
     bool inlining() { return inlining_; }
     bool constructing() { return isConstructing; }
 
     jsbytecode *outerPC() {
         if (a == outer)
             return PC;
         ActiveFrame *scan = a;
         while (scan && scan->parent != outer)
-            scan = scan->parent;
+            scan = static_cast<ActiveFrame *>(scan->parent);
         return scan->parentPC;
     }
 
     jsbytecode *inlinePC() { return PC; }
     uint32_t inlineIndex() { return a->inlineIndex; }
 
     Assembler &getAssembler(bool ool) { return ool ? stubcc.masm : masm; }
 
@@ -486,17 +493,17 @@ private:
         return callSites[index].inlinepc;
     }
 
     bool activeFrameHasMultipleExits() {
         ActiveFrame *na = a;
         while (na->parent) {
             if (na->exitState)
                 return true;
-            na = na->parent;
+            na = static_cast<ActiveFrame *>(na->parent);
         }
         return false;
     }
 
   private:
     CompileStatus performCompilation(JITScript **jitp);
     CompileStatus generatePrologue();
     CompileStatus generateMethod();
--- a/js/src/methodjit/FastBuiltins.cpp
+++ b/js/src/methodjit/FastBuiltins.cpp
@@ -904,16 +904,17 @@ mjit::Compiler::inlineNativeFunction(uin
             type == JSVAL_TYPE_INT32) {
             return compileRound(arg, Floor);
         }
         if (native == js_math_round && argType == JSVAL_TYPE_DOUBLE &&
             type == JSVAL_TYPE_INT32) {
             return compileRound(arg, Round);
         }
         if (native == js_math_sqrt && type == JSVAL_TYPE_DOUBLE &&
+             masm.supportsFloatingPointSqrt() &&
             (argType == JSVAL_TYPE_INT32 || argType == JSVAL_TYPE_DOUBLE)) {
             return compileMathSqrt(arg);
         }
         if (native == js_str_charCodeAt && argType == JSVAL_TYPE_INT32 &&
             thisType == JSVAL_TYPE_STRING && type == JSVAL_TYPE_INT32) {
             return compileGetChar(thisValue, arg, GetCharCode);
         }
         if (native == js_str_charAt && argType == JSVAL_TYPE_INT32 &&
@@ -944,16 +945,17 @@ mjit::Compiler::inlineNativeFunction(uin
     } else if (argc == 2) {
         FrameEntry *arg1 = frame.peek(-2);
         FrameEntry *arg2 = frame.peek(-1);
 
         JSValueType arg1Type = arg1->isTypeKnown() ? arg1->getKnownType() : JSVAL_TYPE_UNKNOWN;
         JSValueType arg2Type = arg2->isTypeKnown() ? arg2->getKnownType() : JSVAL_TYPE_UNKNOWN;
 
         if (native == js_math_pow && type == JSVAL_TYPE_DOUBLE &&
+             masm.supportsFloatingPointSqrt() &&
             (arg1Type == JSVAL_TYPE_DOUBLE || arg1Type == JSVAL_TYPE_INT32) &&
             arg2Type == JSVAL_TYPE_DOUBLE && arg2->isConstant())
         {
             Value arg2Value = arg2->getValue();
             if (arg2Value.toDouble() == -0.5 || arg2Value.toDouble() == 0.5)
                 return compileMathPowSimple(arg1, arg2);
         }
         if ((native == js_math_min || native == js_math_max)) {
--- a/js/src/methodjit/MethodJIT.cpp
+++ b/js/src/methodjit/MethodJIT.cpp
@@ -135,37 +135,33 @@ StackFrame::methodjitStaticAsserts()
  *    stub call ABI.
  */
 
 #ifdef JS_METHODJIT_PROFILE_STUBS
 static const size_t STUB_CALLS_FOR_OP_COUNT = 255;
 static uint32_t StubCallsForOp[STUB_CALLS_FOR_OP_COUNT];
 #endif
 
+// Called from JaegerTrampoline only
 extern "C" void JS_FASTCALL
 PushActiveVMFrame(VMFrame &f)
 {
+    f.oldregs = &f.cx->stack.regs();
+    f.cx->stack.repointRegs(&f.regs);
     f.entryfp->script()->compartment()->jaegerCompartment()->pushActiveFrame(&f);
     f.entryfp->setNativeReturnAddress(JS_FUNC_TO_DATA_PTR(void*, JaegerTrampolineReturn));
     f.regs.clearInlined();
 }
 
+// Called from JaegerTrampolineReturn, JaegerThrowpoline, JaegerInterpoline
 extern "C" void JS_FASTCALL
 PopActiveVMFrame(VMFrame &f)
 {
     f.entryfp->script()->compartment()->jaegerCompartment()->popActiveFrame();
-}
-
-extern "C" void JS_FASTCALL
-SetVMFrameRegs(VMFrame &f)
-{
-    f.oldregs = &f.cx->stack.regs();
-
-    /* Restored on exit from EnterMethodJIT. */
-    f.cx->stack.repointRegs(&f.regs);
+    f.cx->stack.repointRegs(f.oldregs);
 }
 
 #if defined(__APPLE__) || (defined(XP_WIN) && !defined(JS_CPU_X64)) || defined(XP_OS2)
 # define SYMBOL_STRING(name) "_" #name
 #else
 # define SYMBOL_STRING(name) #name
 #endif
 
@@ -323,18 +319,16 @@ SYMBOL_STRING(JaegerTrampoline) ":"     
     "subq  $0x28, %rsp"                  "\n"
 
     /* This is actually part of the VMFrame. */
     "pushq %r8"                          "\n"
 
     /* Set cx->regs and set the active frame. Save rdx and align frame in one. */
     "pushq %rdx"                         "\n"
     "movq  %rsp, %rdi"                   "\n"
-    "call " SYMBOL_STRING_VMFRAME(SetVMFrameRegs) "\n"
-    "movq  %rsp, %rdi"                   "\n"
     "call " SYMBOL_STRING_VMFRAME(PushActiveVMFrame) "\n"
 
     /* Jump into the JIT'd code. */
     "jmp *0(%rsp)"                      "\n"
     CFI(".cfi_endproc"                  "\n")
 );
 
 asm (
@@ -509,18 +503,16 @@ SYMBOL_STRING(JaegerTrampoline) ":"     
     "pushl %ebx"                         "\n"   /* entryfp */
     "pushl 20(%ebp)"                     "\n"   /* stackLimit */
     "pushl 8(%ebp)"                      "\n"   /* cx */
     "pushl %ebx"                         "\n"   /* fp */
     "subl $0x1C, %esp"                   "\n"
 
     /* Jump into the JIT'd code. */
     "movl  %esp, %ecx"                   "\n"
-    "call " SYMBOL_STRING_VMFRAME(SetVMFrameRegs) "\n"
-    "movl  %esp, %ecx"                   "\n"
     "call " SYMBOL_STRING_VMFRAME(PushActiveVMFrame) "\n"
 
     "movl 28(%esp), %ebp"                "\n"   /* load fp for JIT code */
     "jmp *88(%esp)"                      "\n"
     CFI(".cfi_endproc"                   "\n")
 );
 
 asm (
@@ -726,18 +718,16 @@ SYMBOL_STRING(JaegerTrampoline) ":"     
 "   sub     sp, sp, #(4*7)"                     "\n"
 
     /* Preserve 'code' (r2) in an arbitrary callee-saved register. */
 "   mov     r4, r2"                             "\n"
     /* Preserve 'fp' (r1) in r10 (JSFrameReg). */
 "   mov     r10, r1"                            "\n"
 
 "   mov     r0, sp"                             "\n"
-"   blx  " SYMBOL_STRING_VMFRAME(SetVMFrameRegs)   "\n"
-"   mov     r0, sp"                             "\n"
 "   blx  " SYMBOL_STRING_VMFRAME(PushActiveVMFrame)"\n"
 
     /* Call the compiled JavaScript function. */
 "   bx     r4"                                  "\n"
 );
 
 asm (
 ".text\n"
@@ -872,18 +862,16 @@ extern "C" {
             push ebx;
             push [ebp + 20];
             push [ebp + 8];
             push ebx;
             sub  esp, 0x1C;
 
             /* Jump into into the JIT'd code. */
             mov  ecx, esp;
-            call SetVMFrameRegs;
-            mov  ecx, esp;
             call PushActiveVMFrame;
 
             mov ebp, [esp + 28];  /* load fp for JIT code */
             jmp dword ptr [esp + 88];
         }
     }
 
     __declspec(naked) void JaegerTrampolineReturn()
@@ -1050,33 +1038,29 @@ mjit::EnterMethodJIT(JSContext *cx, Stac
     JSScript *script = fp->script();
 
     JaegerSpew(JSpew_Prof, "%s jaeger script, line %d\n",
                script->filename, script->lineno);
     prof.start();
 #endif
 
     JS_ASSERT(cx->fp() == fp);
-    FrameRegs &oldRegs = cx->regs();
 
     JSBool ok;
     {
         AssertCompartmentUnchanged pcc(cx);
         JSAutoResolveFlags rf(cx, RESOLVE_INFER);
         ok = JaegerTrampoline(cx, fp, code, stackLimit);
     }
 
 #ifdef JS_METHODJIT_SPEW
     prof.stop();
     JaegerSpew(JSpew_Prof, "script run took %d ms\n", prof.time_ms());
 #endif
 
-    /* Undo repointRegs in SetVMFrameRegs. */
-    cx->stack.repointRegs(&oldRegs);
-
     JaegerStatus status = cx->compartment->jaegerCompartment()->lastUnfinished();
     if (status) {
         if (partial) {
             /*
              * Being called from the interpreter, which will resume execution
              * where the JIT left off.
              */
             return status;
--- a/js/src/methodjit/TrampolineMasmX64.asm
+++ b/js/src/methodjit/TrampolineMasmX64.asm
@@ -32,17 +32,16 @@
 ; 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 *****
 
 
 extern js_InternalThrow:PROC
-extern SetVMFrameRegs:PROC
 extern PushActiveVMFrame:PROC
 extern PopActiveVMFrame:PROC
 extern js_InternalInterpret:PROC
 
 .CODE
 
 ; JSBool JaegerTrampoline(JSContext *cx, StackFrame *fp, void *code,
 ;                         Value *stackLimit, void *safePoint);
@@ -90,18 +89,16 @@ JaegerTrampoline PROC FRAME
     ; This is actually part of the VMFrame.
     mov     r10, [rbp+8*5+8]
     push    r10
 
     ; Set cx->regs and set the active frame. Save r8 and align frame in one
     push    r8
     mov     rcx, rsp
     sub     rsp, 20h
-    call    SetVMFrameRegs
-    lea     rcx, [rsp+20h]
     call    PushActiveVMFrame
     add     rsp, 20h
 
     ; Jump into the JIT code.
     jmp     qword ptr [rsp]
 JaegerTrampoline ENDP
 
 ; void JaegerTrampolineReturn();
--- a/js/src/methodjit/TrampolineMingwX64.s
+++ b/js/src/methodjit/TrampolineMingwX64.s
@@ -32,17 +32,16 @@
 # 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 *****
 
 
 .extern js_InternalThrow
-.extern SetVMFrameRegs
 .extern PushActiveVMFrame
 .extern PopActiveVMFrame
 .extern js_InternalInterpret
 
 .text
 .intel_syntax noprefix
 
 # JSBool JaegerTrampoline(JSContext *cx, StackFrame *fp, void *code,
@@ -96,18 +95,16 @@ JaegerTrampoline:
     # This is actually part of the VMFrame.
     mov     r10, [rbp+8*5+8]
     push    r10
 
     # Set cx->regs and set the active frame. Save r8 and align frame in one
     push    r8
     mov     rcx, rsp
     sub     rsp, 0x20
-    call    SetVMFrameRegs
-    lea     rcx, [rsp+0x20]
     call    PushActiveVMFrame
     add     rsp, 0x20
 
     # Jump into the JIT code.
     jmp     qword ptr [rsp]
 
 # void JaegerTrampolineReturn()#
 .globl JaegerTrampolineReturn
--- a/js/src/methodjit/TrampolineSUNWX64.s
+++ b/js/src/methodjit/TrampolineSUNWX64.s
@@ -74,18 +74,16 @@ JaegerTrampoline:
     subq  $0x28, %rsp
 
     /* This is actually part of the VMFrame. */
     pushq %r8
 
     /* Set cx->regs and set the active frame. Save rdx and align frame in one. */
     pushq %rdx
     movq  %rsp, %rdi
-    call SetVMFrameRegs
-    movq  %rsp, %rdi
     call PushActiveVMFrame
 
     /* Jump into into the JIT'd code. */
     jmp *0(%rsp)
 .size   JaegerTrampoline, . - JaegerTrampoline
 
 / void JaegerTrampolineReturn()
 .global JaegerTrampolineReturn
--- a/js/src/methodjit/TrampolineSUNWX86.s
+++ b/js/src/methodjit/TrampolineSUNWX86.s
@@ -61,17 +61,16 @@ JaegerTrampoline:
     pushl 20(%ebp)                             /* stackLimit */
     pushl 8(%ebp)                              /* cx */
     pushl %ebx                                 /* fp */
     subl $0x1C, %esp
 
     /* Jump into the JIT'd code. */
     /* No fastcall for sunstudio. */
     pushl %esp
-    call SetVMFrameRegs
     call PushActiveVMFrame
     popl  %edx
 
     movl 28(%esp), %ebp                       /* load fp for JIT code */
     jmp  *88(%esp)
 .size   JaegerTrampoline, . - JaegerTrampoline
 
 / void JaegerTrampolineReturn()
--- a/js/src/methodjit/TrampolineSparc.s
+++ b/js/src/methodjit/TrampolineSparc.s
@@ -46,18 +46,16 @@
 JaegerTrampoline:
     save    %sp,-168,%sp
     st      %i1, [%fp - 36]        ! fp
     st      %i0, [%fp - 32]        ! cx
     st      %i3, [%fp - 28]        ! stackLimit
     st      %i1, [%fp - 24]        ! entryFp
     st      %i1, [%fp - 20]        ! entryncode
     st      %g0, [%fp - 16]        ! stubRejoin
-    call    SetVMFrameRegs
-    mov     %sp, %o0
     call    PushActiveVMFrame
     mov     %sp, %o0
     ld      [%fp - 36], %l0         ! fp
     jmp     %i2
     st      %i7, [%fp - 12]         ! return address
 .size   JaegerTrampoline, . - JaegerTrampoline
 
 ! void JaegerTrampolineReturn()
--- a/js/xpconnect/src/XPCJSRuntime.cpp
+++ b/js/xpconnect/src/XPCJSRuntime.cpp
@@ -1255,16 +1255,25 @@ CompartmentMemoryCallback(JSContext *cx,
     JS::SizeOfCompartmentTypeInferenceData(cx, compartment,
                                            &curr->typeInferenceMemory,
                                            JsMallocSizeOf);
     curr->shapesCompartmentTables =
         JS::SizeOfCompartmentShapeTable(compartment, JsMallocSizeOf);
 }
 
 void
+ExplicitNonHeapCompartmentCallback(JSContext *cx, void *data, JSCompartment *compartment)
+{
+    size_t *n = static_cast<size_t *>(data);
+#ifdef JS_METHODJIT
+    *n += JS::SizeOfCompartmentMjitCode(compartment);
+#endif
+}
+
+void
 ChunkCallback(JSContext *cx, void *vdata, js::gc::Chunk *chunk)
 {
     // Nb: This function is only called for dirty chunks, which is why we
     // increment gcHeapChunkDirtyDecommitted.
     IterateData *data = static_cast<IterateData *>(vdata);
     for (uint32 i = 0; i < js::gc::ArenasPerChunk; i++)
         if (chunk->decommittedArenas.get(i))
             data->gcHeapChunkDirtyDecommitted += js::gc::ArenaSize;
@@ -1536,18 +1545,19 @@ CompartmentStats::CompartmentStats(JSCon
             name.AssignLiteral("null-codebase");
         }
     } else {
         name.AssignLiteral("null-principal");
     }
 }
 
 JSBool
-CollectCompartmentStatsForRuntime(JSRuntime *rt, IterateData *data)
+CollectCompartmentStatsForRuntime(JSRuntime *rt, void *vdata)
 {
+    IterateData *data = (IterateData *)vdata;
     JSContext *cx = JS_NewContext(rt, 0);
     if (!cx) {
         NS_ERROR("couldn't create context for memory tracing");
         return false;
     }
 
     {
         JSAutoRequest ar(cx);
@@ -1683,16 +1693,70 @@ CollectCompartmentStatsForRuntime(JSRunt
                                     data->gcHeapChunkCleanDecommitted +
                                     data->gcHeapChunkDirtyDecommitted +
                                     data->gcHeapArenaUnused) * 10000 /
                                    data->gcHeapChunkTotal;
 
     return true;
 }
 
+JSBool
+GetExplicitNonHeapForRuntime(JSRuntime *rt, void *data)
+{
+    PRInt64 *amount = (PRInt64 *)data;
+
+    JSContext *cx = JS_NewContext(rt, 0);
+    if (!cx) {
+        NS_ERROR("couldn't create context for memory tracing");
+        return NS_ERROR_ABORT;
+    }
+
+    // explicit/<compartment>/gc-heap/*
+    *amount = PRInt64(JS_GetGCParameter(rt, JSGC_TOTAL_CHUNKS)) *
+              js::gc::ChunkSize;
+
+    {
+        JSAutoRequest ar(cx);
+
+        // explicit/<compartment>/mjit-code
+        size_t n = 0;
+        js::IterateCompartments(cx, &n, ExplicitNonHeapCompartmentCallback);
+        *amount += n;
+
+        {
+            #ifndef JS_THREADSAFE
+            #error "This code assumes JS_THREADSAFE is defined"
+            #endif
+
+            // Need the GC lock to call JS_ContextIteratorUnlocked() and to
+            // access rt->threads.
+            js::AutoLockGC lock(rt);
+
+            // explicit/runtime/threads/regexp-code
+            // explicit/runtime/threads/stack-committed
+            for (JSThread::Map::Range r = rt->threads.all(); !r.empty(); r.popFront()) {
+                JSThread *thread = r.front().value;
+                size_t regexpCode, stackCommitted;
+                thread->sizeOfIncludingThis(JsMallocSizeOf,
+                                            NULL,
+                                            NULL,
+                                            &regexpCode,
+                                            &stackCommitted);
+
+                *amount += regexpCode;
+                *amount += stackCommitted;
+            }
+        }
+    }
+
+    JS_DestroyContextNoGC(cx);
+
+    return true;
+}
+
 #define SLOP_BYTES_STRING \
     " The measurement includes slop bytes caused by the heap allocator rounding up request sizes."
 
 static PRInt64
 ReportCompartmentStats(const CompartmentStats &stats,
                        const nsACString &pathPrefix,
                        nsIMemoryMultiReporterCallback *callback,
                        nsISupports *closure)
@@ -1953,26 +2017,26 @@ ReportJSRuntimeStats(const IterateData &
                       callback, closure);
 
     ReportGCHeapBytes(pathPrefix +
                       NS_LITERAL_CSTRING("gc-heap-chunk-dirty-unused"),
                       &gcTotal, data.gcHeapChunkDirtyUnused,
                       "Memory on the garbage-collected JavaScript heap, within chunks with at "
                       "least one allocated GC thing, that could be holding useful data but "
                       "currently isn't.  Memory here is mutually exclusive with memory reported"
-                      "under gc-heap-decommitted.",
+                      "under 'explicit/js/gc-heap-decommitted'.",
                       callback, closure);
 
     ReportGCHeapBytes(pathPrefix +
                       NS_LITERAL_CSTRING("gc-heap-chunk-clean-unused"),
                       &gcTotal, data.gcHeapChunkCleanUnused,
                       "Memory on the garbage-collected JavaScript heap taken by completely empty "
                       "chunks, that soon will be released unless claimed for new allocations.  "
                       "Memory here is mutually exclusive with memory reported under "
-                      "gc-heap-decommitted.",
+                      "'explicit/js/gc-heap-decommitted'.",
                       callback, closure);
 
     ReportGCHeapBytes(pathPrefix +
                       NS_LITERAL_CSTRING("gc-heap-decommitted"),
                       &gcTotal,
                       data.gcHeapChunkCleanDecommitted + data.gcHeapChunkDirtyDecommitted,
                       "Memory in the address space of the garbage-collected JavaScript heap that "
                       "is currently returned to the OS.",
@@ -2100,16 +2164,27 @@ public:
         ReportMemoryBytes(NS_LITERAL_CSTRING("js-total-analysis-temporary"),
                           nsIMemoryReporter::KIND_OTHER, data.totalAnalysisTemp,
                           "Memory used transiently during type inference and compilation. "
                           "This is the sum of all compartments' 'analysis-temporary' numbers.",
                           callback, closure);
 
         return NS_OK;
     }
+
+    NS_IMETHOD
+    GetExplicitNonHeap(PRInt64 *n)
+    {
+        JSRuntime *rt = nsXPConnect::GetRuntimeInstance()->GetJSRuntime();
+
+        if (!GetExplicitNonHeapForRuntime(rt, n))
+            return NS_ERROR_FAILURE;
+
+        return NS_OK;
+    }
 };
 
 NS_IMPL_THREADSAFE_ISUPPORTS1(XPConnectJSCompartmentsMultiReporter
                               , nsIMemoryMultiReporter
                               )
 
 #ifdef MOZ_CRASHREPORTER
 static JSBool
--- a/js/xpconnect/src/xpcpublic.h
+++ b/js/xpconnect/src/xpcpublic.h
@@ -306,17 +306,19 @@ struct IterateData
     PRInt64 totalTypeInference;
     PRInt64 totalAnalysisTemp;
 
     nsTArray<CompartmentStats> compartmentStatsVector;
     CompartmentStats *currCompartmentStats;
 };
 
 JSBool
-CollectCompartmentStatsForRuntime(JSRuntime *rt, IterateData *data);
+CollectCompartmentStatsForRuntime(JSRuntime *rt, void *data);
+JSBool
+GetExplicitNonHeapForRuntime(JSRuntime *rt, void *data);
 
 void
 ReportJSRuntimeStats(const IterateData &data, const nsACString &pathPrefix,
                      nsIMemoryMultiReporterCallback *callback,
                      nsISupports *closure);
 
 } // namespace memory
 } // namespace xpconnect
--- a/layout/base/nsDisplayList.cpp
+++ b/layout/base/nsDisplayList.cpp
@@ -455,17 +455,17 @@ GetDisplayPortBounds(nsDisplayListBuilde
   // Start TransformRectToBoundsInAncestor() calculations from aItem's frame
   // parent in this case.
   nsIFrame* frame = aItem->GetUnderlyingFrame();
   if (aItem->GetType() == nsDisplayItem::TYPE_TRANSFORM) {
     frame = nsLayoutUtils::GetCrossDocParentFrame(frame);
   }
 
   const nsRect* displayport = aBuilder->GetDisplayPort();
-  nsRect result = nsLayoutUtils::TransformRectToBoundsInAncestor(
+  nsRect result = nsLayoutUtils::TransformAncestorRectToFrame(
                     frame,
                     nsRect(0, 0, displayport->width, displayport->height),
                     aBuilder->ReferenceFrame());
   result.MoveBy(aBuilder->ToReferenceFrame(frame));
   return result;
 }
 
 bool
--- a/layout/base/nsDocumentViewer.cpp
+++ b/layout/base/nsDocumentViewer.cpp
@@ -189,16 +189,18 @@ static const char sPrintOptionsContractI
 
 //paint forcing
 #include "prenv.h"
 #include <stdio.h>
 
 //switch to page layout
 #include "nsGfxCIID.h"
 
+#include "nsObserverService.h"
+
 #include "mozilla/dom/Element.h"
 
 using namespace mozilla;
 
 #ifdef NS_DEBUG
 
 #undef NOISY_VIEWER
 #else
@@ -504,16 +506,28 @@ public:
   ~nsPrintEventDispatcher()
   {
     DocumentViewerImpl::DispatchAfterPrint(mTop);
   }
 
   nsCOMPtr<nsIDocument> mTop;
 };
 
+class nsDocumentShownDispatcher : public nsRunnable
+{
+public:
+  nsDocumentShownDispatcher(nsIDocument *aDocument)
+  : mDocument(aDocument) {}
+
+  NS_IMETHOD Run();
+
+private:
+  nsCOMPtr<nsIDocument> mDocument;
+};
+
 
 //------------------------------------------------------------------
 // DocumentViewerImpl
 //------------------------------------------------------------------
 // Class IDs
 static NS_DEFINE_CID(kViewManagerCID,       NS_VIEW_MANAGER_CID);
 
 //------------------------------------------------------------------
@@ -2034,16 +2048,20 @@ DocumentViewerImpl::Show(void)
     // shown...
 
     if (mPresShell) {
       nsCOMPtr<nsIPresShell> shellDeathGrip(mPresShell); // bug 378682
       mPresShell->UnsuppressPainting();
     }
   }
 
+  // Notify observers that a new page has been shown. (But not right now;
+  // running JS at this time is not safe.)
+  NS_DispatchToMainThread(new nsDocumentShownDispatcher(mDocument));
+
   return NS_OK;
 }
 
 NS_IMETHODIMP
 DocumentViewerImpl::Hide(void)
 {
   if (!mAttachedToParent && mWindow) {
     mWindow->Show(false);
@@ -4366,8 +4384,22 @@ DocumentViewerImpl::SetPrintPreviewPrese
     DestroyPresShell();
   }
 
   mWindow = nsnull;
   mViewManager = aViewManager;
   mPresContext = aPresContext;
   mPresShell = aPresShell;
 }
+
+// Fires the "document-shown" event so that interested parties (right now, the
+// mobile browser) are aware of it.
+NS_IMETHODIMP
+nsDocumentShownDispatcher::Run()
+{
+  nsCOMPtr<nsIObserverService> observerService =
+    mozilla::services::GetObserverService();
+  if (observerService) {
+    observerService->NotifyObservers(mDocument, "document-shown", NULL);
+  }
+  return NS_OK;
+}
+
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -970,17 +970,17 @@ nsLayoutUtils::GetEventCoordinatesRelati
   PRInt32 rootAPD = rootFrame->PresContext()->AppUnitsPerDevPixel();
   PRInt32 localAPD = aFrame->PresContext()->AppUnitsPerDevPixel();
   widgetToView = widgetToView.ConvertAppUnits(rootAPD, localAPD);
 
   /* If we encountered a transform, we can't do simple arithmetic to figure
    * out how to convert back to aFrame's coordinates and must use the CTM.
    */
   if (transformFound)
-    return InvertTransformsToRoot(aFrame, widgetToView);
+    return TransformRootPointToFrame(aFrame, widgetToView);
 
   /* Otherwise, all coordinate systems are translations of one another,
    * so we can just subtract out the different.
    */
   return widgetToView - aFrame->GetOffsetToCrossDoc(rootFrame);
 }
 
 nsIFrame*
@@ -1112,93 +1112,106 @@ nsLayoutUtils::MatrixTransformPoint(cons
                                     const gfx3DMatrix &aMatrix, float aFactor)
 {
   gfxPoint image = aMatrix.Transform(gfxPoint(NSAppUnitsToFloatPixels(aPoint.x, aFactor),
                                               NSAppUnitsToFloatPixels(aPoint.y, aFactor)));
   return nsPoint(NSFloatPixelsToAppUnits(float(image.x), aFactor),
                  NSFloatPixelsToAppUnits(float(image.y), aFactor));
 }
 
-static gfxPoint 
-InvertTransformsToAncestor(nsIFrame *aFrame,
-                           const gfxPoint &aPoint,
-                           nsIFrame *aStopAtAncestor = nsnull)
+static gfx3DMatrix
+GetTransformToAncestor(nsIFrame *aFrame, nsIFrame *aAncestor)
 {
-  NS_PRECONDITION(aFrame, "Why are you inverting transforms when there is no frame?");
-
-  /* To invert everything to the root, we'll get the CTM, invert it, and use it to transform
-   * the point.
-   */
-  nsIFrame *parent = nsnull;
-  gfx3DMatrix ctm = aFrame->GetTransformMatrix(&parent);
-  gfxPoint result = aPoint;
-  
-  if (parent && parent != aStopAtAncestor) {
-      result = InvertTransformsToAncestor(parent, aPoint, aStopAtAncestor);
-  }
-
-  result = ctm.Inverse().ProjectPoint(result);
-  return result;
+  nsIFrame* parent;
+  gfx3DMatrix ctm = aFrame->GetTransformMatrix(aAncestor, &parent);
+  while (parent && parent != aAncestor) {
+    ctm = ctm * parent->GetTransformMatrix(aAncestor, &parent);
+  }
+  return ctm;
+}
+
+static gfxPoint
+TransformGfxPointFromAncestor(nsIFrame *aFrame,
+                              const gfxPoint &aPoint,
+                              nsIFrame *aAncestor)
+{
+  gfx3DMatrix ctm = GetTransformToAncestor(aFrame, aAncestor);
+  return ctm.Inverse().ProjectPoint(aPoint);
 }
 
 static gfxRect
-InvertGfxRectToAncestor(nsIFrame *aFrame,
-                     const gfxRect &aRect,
-                     nsIFrame *aStopAtAncestor = nsnull)
+TransformGfxRectFromAncestor(nsIFrame *aFrame,
+                             const gfxRect &aRect,
+                             nsIFrame *aAncestor)
 {
-  NS_PRECONDITION(aFrame, "Why are you inverting transforms when there is no frame?");
-
-  /* To invert everything to the root, we'll get the CTM, invert it, and use it to transform
-   * the point.
-   */
-  nsIFrame *parent = nsnull;
-  gfx3DMatrix ctm = aFrame->GetTransformMatrix(&parent);
-  gfxRect result = aRect;
-  
-  if (parent && parent != aStopAtAncestor) {
-      result = InvertGfxRectToAncestor(parent, aRect, aStopAtAncestor);
-  }
-
-  result = ctm.Inverse().ProjectRectBounds(result);
-  return result;
+  gfx3DMatrix ctm = GetTransformToAncestor(aFrame, aAncestor);
+  return ctm.Inverse().ProjectRectBounds(aRect);
+}
+
+static gfxRect
+TransformGfxRectToAncestor(nsIFrame *aFrame,
+                           const gfxRect &aRect,
+                           nsIFrame *aAncestor)
+{
+  gfx3DMatrix ctm = GetTransformToAncestor(aFrame, aAncestor);
+  return ctm.ProjectRectBounds(aRect);
 }
 
 nsPoint
-nsLayoutUtils::InvertTransformsToRoot(nsIFrame *aFrame,
-                                      const nsPoint &aPoint)
+nsLayoutUtils::TransformRootPointToFrame(nsIFrame *aFrame,
+                                         const nsPoint &aPoint)
 {
     float factor = aFrame->PresContext()->AppUnitsPerDevPixel();
     gfxPoint result(NSAppUnitsToFloatPixels(aPoint.x, factor),
                     NSAppUnitsToFloatPixels(aPoint.y, factor));
     
-    result = InvertTransformsToAncestor(aFrame, result);
+    result = TransformGfxPointFromAncestor(aFrame, result, nsnull);
    
     return nsPoint(NSFloatPixelsToAppUnits(float(result.x), factor),
                    NSFloatPixelsToAppUnits(float(result.y), factor));
 }
 
 nsRect 
-nsLayoutUtils::TransformRectToBoundsInAncestor(nsIFrame* aFrame,
-                                               const nsRect &aRect,
-                                               nsIFrame* aStopAtAncestor)
+nsLayoutUtils::TransformAncestorRectToFrame(nsIFrame* aFrame,
+                                            const nsRect &aRect,
+                                            nsIFrame* aAncestor)
 {
     float factor = aFrame->PresContext()->AppUnitsPerDevPixel();
     gfxRect result(NSAppUnitsToFloatPixels(aRect.x, factor),
                    NSAppUnitsToFloatPixels(aRect.y, factor),
                    NSAppUnitsToFloatPixels(aRect.width, factor),
                    NSAppUnitsToFloatPixels(aRect.height, factor));
 
-    result = InvertGfxRectToAncestor(aFrame, result, aStopAtAncestor);
+    result = TransformGfxRectFromAncestor(aFrame, result, aAncestor);
 
     return nsRect(NSFloatPixelsToAppUnits(float(result.x), factor),
                   NSFloatPixelsToAppUnits(float(result.y), factor),
                   NSFloatPixelsToAppUnits(float(result.width), factor),
                   NSFloatPixelsToAppUnits(float(result.height), factor));
 }
 
+nsRect
+nsLayoutUtils::TransformFrameRectToAncestor(nsIFrame* aFrame,
+                                            const nsRect& aRect,
+                                            nsIFrame* aAncestor)
+{
+  float factor = aFrame->PresContext()->AppUnitsPerDevPixel();
+  gfxRect result(NSAppUnitsToFloatPixels(aRect.x, factor),
+                 NSAppUnitsToFloatPixels(aRect.y, factor),
+                 NSAppUnitsToFloatPixels(aRect.width, factor),
+                 NSAppUnitsToFloatPixels(aRect.height, factor));
+
+  result = TransformGfxRectToAncestor(aFrame, result, aAncestor);
+
+  return nsRect(NSFloatPixelsToAppUnits(float(result.x), factor),
+                NSFloatPixelsToAppUnits(float(result.y), factor),
+                NSFloatPixelsToAppUnits(float(result.width), factor),
+                NSFloatPixelsToAppUnits(float(result.height), factor));
+}
+
 static nsIntPoint GetWidgetOffset(nsIWidget* aWidget, nsIWidget*& aRootWidget) {
   nsIntPoint offset(0, 0);
   nsIWidget* parent = aWidget->GetParent();
   while (parent) {
     nsIntRect bounds;
     aWidget->GetBounds(bounds);
     offset += bounds.TopLeft();
     aWidget = parent;
@@ -1829,37 +1842,45 @@ nsLayoutUtils::GetAllInFlowBoxes(nsIFram
 {
   while (aFrame) {
     AddBoxesForFrame(aFrame, aCallback);
     aFrame = nsLayoutUtils::GetNextContinuationOrSpecialSibling(aFrame);
   }
 }
 
 struct BoxToBorderRect : public nsLayoutUtils::BoxCallback {
-  nsIFrame*                    mRelativeTo;
+  nsIFrame* mRelativeTo;
   nsLayoutUtils::RectCallback* mCallback;
-
-  BoxToBorderRect(nsIFrame* aRelativeTo, nsLayoutUtils::RectCallback* aCallback)
-    : mRelativeTo(aRelativeTo), mCallback(aCallback) {}
+  PRUint32 mFlags;
+
+  BoxToBorderRect(nsIFrame* aRelativeTo, nsLayoutUtils::RectCallback* aCallback,
+                  PRUint32 aFlags)
+    : mRelativeTo(aRelativeTo), mCallback(aCallback), mFlags(aFlags) {}
 
   virtual void AddBox(nsIFrame* aFrame) {
     nsRect r;
     nsIFrame* outer = nsSVGUtils::GetOuterSVGFrameAndCoveredRegion(aFrame, &r);
-    if (outer) {
-      mCallback->AddRect(r + outer->GetOffsetTo(mRelativeTo));
-    } else
-      mCallback->AddRect(nsRect(aFrame->GetOffsetTo(mRelativeTo), aFrame->GetSize()));
+    if (!outer) {
+      outer = aFrame;
+      r = nsRect(nsPoint(0, 0), aFrame->GetSize());
+    }
+    if (mFlags & nsLayoutUtils::RECTS_ACCOUNT_FOR_TRANSFORMS) {
+      r = nsLayoutUtils::TransformFrameRectToAncestor(outer, r, mRelativeTo);
+    } else {
+      r += outer->GetOffsetTo(mRelativeTo);
+    }
+    mCallback->AddRect(r);
   }
 };
 
 void
 nsLayoutUtils::GetAllInFlowRects(nsIFrame* aFrame, nsIFrame* aRelativeTo,
-                                 RectCallback* aCallback)
+                                 RectCallback* aCallback, PRUint32 aFlags)
 {
-  BoxToBorderRect converter(aRelativeTo, aCallback);
+  BoxToBorderRect converter(aRelativeTo, aCallback, aFlags);
   GetAllInFlowBoxes(aFrame, &converter);
 }
 
 nsLayoutUtils::RectAccumulator::RectAccumulator() : mSeenFirstRect(false) {}
 
 void nsLayoutUtils::RectAccumulator::AddRect(const nsRect& aRect) {
   mResultRect.UnionRect(mResultRect, aRect);
   if (!mSeenFirstRect) {
@@ -1875,29 +1896,24 @@ void nsLayoutUtils::RectListBuilder::Add
   nsRefPtr<nsClientRect> rect = new nsClientRect();
 
   rect->SetLayoutRect(aRect);
   mRectList->Append(rect);
 }
 
 nsIFrame* nsLayoutUtils::GetContainingBlockForClientRect(nsIFrame* aFrame)
 {
-  // get the nearest enclosing SVG foreign object frame or the root frame
-  while (aFrame->GetParent() &&
-         !aFrame->IsFrameOfType(nsIFrame::eSVGForeignObject)) {
-    aFrame = aFrame->GetParent();
-  }
-
-  return aFrame;
+  return aFrame->PresContext()->PresShell()->GetRootFrame();
 }
 
 nsRect
-nsLayoutUtils::GetAllInFlowRectsUnion(nsIFrame* aFrame, nsIFrame* aRelativeTo) {
+nsLayoutUtils::GetAllInFlowRectsUnion(nsIFrame* aFrame, nsIFrame* aRelativeTo,
+                                      PRUint32 aFlags) {
   RectAccumulator accumulator;
-  GetAllInFlowRects(aFrame, aRelativeTo, &accumulator);
+  GetAllInFlowRects(aFrame, aRelativeTo, &accumulator, aFlags);
   return accumulator.mResultRect.IsEmpty() ? accumulator.mFirstRect
           : accumulator.mResultRect;
 }
 
 nsRect
 nsLayoutUtils::GetTextShadowRectsUnion(const nsRect& aTextAndDecorationsRect,
                                        nsIFrame* aFrame,
                                        PRUint32 aFlags)
--- a/layout/base/nsLayoutUtils.h
+++ b/layout/base/nsLayoutUtils.h
@@ -510,33 +510,43 @@ public:
    * @param aIgnoreRootScrollFrame whether or not the display list builder
    * should ignore the root scroll frame.
    */
   static nsresult GetFramesForArea(nsIFrame* aFrame, const nsRect& aRect,
                                    nsTArray<nsIFrame*> &aOutFrames,
                                    bool aShouldIgnoreSuppression = false,
                                    bool aIgnoreRootScrollFrame = false);
 
-  
+  /**
+   * Transform aRect relative to aAncestor down to the coordinate system of
+   * aFrame. Computes the bounding-box of the true quadrilateral.
+   */
+  static nsRect TransformAncestorRectToFrame(nsIFrame* aFrame,
+                                             const nsRect& aRect,
+                                             nsIFrame* aAncestor);
 
-  static nsRect TransformRectToBoundsInAncestor(nsIFrame* aFrame,
-                                                const nsRect& aRect,
-                                                nsIFrame* aStopAtAncestor);
+  /**
+   * Transform aRect relative to aFrame up to the coordinate system of
+   * aAncestor. Computes the bounding-box of the true quadrilateral.
+   */
+  static nsRect TransformFrameRectToAncestor(nsIFrame* aFrame,
+                                             const nsRect& aRect,
+                                             nsIFrame* aAncestor);
 
   /**
    * Given a point in the global coordinate space, returns that point expressed
    * in the coordinate system of aFrame.  This effectively inverts all transforms
    * between this point and the root frame.
    *
    * @param aFrame The frame that acts as the coordinate space container.
    * @param aPoint The point, in the global space, to get in the frame-local space.
    * @return aPoint, expressed in aFrame's canonical coordinate space.
    */
-  static nsPoint InvertTransformsToRoot(nsIFrame* aFrame,
-                                        const nsPoint &aPt);
+  static nsPoint TransformRootPointToFrame(nsIFrame* aFrame,
+                                           const nsPoint &aPt);
 
   /**
    * Helper function that, given a rectangle and a matrix, returns the smallest
    * rectangle containing the image of the source rectangle.
    *
    * @param aBounds The rectangle to transform.
    * @param aMatrix The matrix to transform it with.
    * @param aFactor The number of app units per graphics unit.
@@ -689,18 +699,18 @@ public:
   static void GetAllInFlowBoxes(nsIFrame* aFrame, BoxCallback* aCallback);
 
   class RectCallback {
   public:
     virtual void AddRect(const nsRect& aRect) = 0;
   };
 
   struct RectAccumulator : public RectCallback {
-    nsRect       mResultRect;
-    nsRect       mFirstRect;
+    nsRect mResultRect;
+    nsRect mFirstRect;
     bool mSeenFirstRect;
 
     RectAccumulator();
 
     virtual void AddRect(const nsRect& aRect);
   };
 
   struct RectListBuilder : public RectCallback {
@@ -708,33 +718,43 @@ public:
     nsresult          mRV;
 
     RectListBuilder(nsClientRectList* aList);
      virtual void AddRect(const nsRect& aRect);
   };
 
   static nsIFrame* GetContainingBlockForClientRect(nsIFrame* aFrame);
 
+  enum {
+    RECTS_ACCOUNT_FOR_TRANSFORMS = 0x01
+  };
   /**
    * Collect all CSS border-boxes associated with aFrame and its
    * continuations, "drilling down" through outer table frames and
    * some anonymous blocks since they're not real CSS boxes.
    * The boxes are positioned relative to aRelativeTo (taking scrolling
    * into account) and passed to the callback in frame-tree order.
    * If aFrame is null, no boxes are returned.
    * For SVG frames, returns one rectangle, the bounding box.
+   * If aFlags includes RECTS_ACCOUNT_FOR_TRANSFORMS, then when converting
+   * the boxes into aRelativeTo coordinates, transforms (including CSS
+   * and SVG transforms) are taken into account.
    */
   static void GetAllInFlowRects(nsIFrame* aFrame, nsIFrame* aRelativeTo,
-                                RectCallback* aCallback);
+                                RectCallback* aCallback, PRUint32 aFlags = 0);
 
   /**
    * Computes the union of all rects returned by GetAllInFlowRects. If
    * the union is empty, returns the first rect.
+   * If aFlags includes RECTS_ACCOUNT_FOR_TRANSFORMS, then when converting
+   * the boxes into aRelativeTo coordinates, transforms (including CSS
+   * and SVG transforms) are taken into account.
    */
-  static nsRect GetAllInFlowRectsUnion(nsIFrame* aFrame, nsIFrame* aRelativeTo);
+  static nsRect GetAllInFlowRectsUnion(nsIFrame* aFrame, nsIFrame* aRelativeTo,
+                                       PRUint32 aFlags = 0);
 
   enum {
     EXCLUDE_BLUR_SHADOWS = 0x01
   };
   /**
    * Takes a text-shadow array from the style properties of a given nsIFrame and
    * computes the union of those shadows along with the given initial rect.
    * If there are no shadows, the initial rect is returned.
--- a/layout/base/nsPresShell.cpp
+++ b/layout/base/nsPresShell.cpp
@@ -717,16 +717,23 @@ PresShell::MemoryReporter::CollectReport
 
   aCb->Callback(EmptyCString(), kTextRunWordCachePath,
                 nsIMemoryReporter::KIND_HEAP, nsIMemoryReporter::UNITS_BYTES,
                 textRunWordCacheSize, kTextRunWordCacheDesc, aClosure);
 
   return NS_OK;
 }
 
+NS_IMETHODIMP
+PresShell::MemoryReporter::GetExplicitNonHeap(PRInt64 *aAmount) {
+  // This reporter doesn't do any KIND_NONHEAP measurements.
+  *aAmount = 0;
+  return NS_OK;
+}
+
 class nsAutoCauseReflowNotifier
 {
 public:
   nsAutoCauseReflowNotifier(PresShell* aShell)
     : mShell(aShell)
   {
     mShell->WillCauseReflow();
   }
--- a/layout/base/tests/test_bug677878.html
+++ b/layout/base/tests/test_bug677878.html
@@ -31,17 +31,19 @@ https://bugzilla.mozilla.org/show_bug.cg
 <pre id="test">
 <script type="application/javascript">
 SimpleTest.waitForExplicitFinish();
 runtests();
 
 function runtests() {
   function doClick() {
     document.getElementById("test2").addEventListener("mousedown", testFinish, true);
-    synthesizeMouseAtCenter(document.getElementById("test2"), { type: "mousedown" })
+    // Don't target the center because the center could actually be outside the
+    // viewport.
+    synthesizeMouse(document.getElementById("test2"), 10, 10, { type: "mousedown" })
   }
   setTimeout(doClick, 300);
 }
 
 function testFinish(event){
   ok(true, "We can still interact with the item after it is transformed");
   SimpleTest.finish();
 }
--- a/layout/forms/nsComboboxControlFrame.cpp
+++ b/layout/forms/nsComboboxControlFrame.cpp
@@ -545,18 +545,18 @@ nsComboboxControlFrame::ReflowDropdown(n
 
 nsPoint
 nsComboboxControlFrame::GetCSSTransformTranslation()
 {
   nsIFrame* frame = this;
   bool is3DTransform = false;
   gfxMatrix transform;
   while (frame) {
-    nsIFrame* parent = nsnull;
-    gfx3DMatrix ctm = frame->GetTransformMatrix(&parent);
+    nsIFrame* parent;
+    gfx3DMatrix ctm = frame->GetTransformMatrix(nsnull, &parent);
     gfxMatrix matrix;
     if (ctm.Is2D(&matrix)) {
       transform = transform * matrix;
     } else {
       is3DTransform = true;
       break;
     }
     frame = parent;
--- a/layout/generic/TextOverflow.cpp
+++ b/layout/generic/TextOverflow.cpp
@@ -526,18 +526,18 @@ TextOverflow::ExamineLineFrames(nsLineBo
       mRight.mWidth = mRight.mIntrinsicWidth;
       mRight.mActive = guessRight = rightOverflow;
       continue;
     }
     if (guessLeft == (mLeft.mActive && mLeft.IsNeeded()) &&
         guessRight == (mRight.mActive && mRight.IsNeeded())) {
       break;
     } else {
-      guessLeft = mLeft.IsNeeded();
-      guessRight = mRight.IsNeeded();
+      guessLeft = mLeft.mActive && mLeft.IsNeeded();
+      guessRight = mRight.mActive && mRight.IsNeeded();
       mLeft.Reset();
       mRight.Reset();
       aFramesToHide->Clear();
     }
     NS_ASSERTION(pass == 0, "2nd pass should never guess wrong");
   } while (++pass != 2);
   if (!leftOverflow || !mLeft.mActive) {
     mLeft.Reset();
new file mode 100644
--- /dev/null
+++ b/layout/generic/crashtests/700031.xhtml
@@ -0,0 +1,9 @@
+<html xmlns="http://www.w3.org/1999/xhtml"><body>
+
+<div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div>
+
+<math xmlns="http://www.w3.org/1998/Math/MathML"><mover>abcdef</mover></math>
+
+</div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div></div>
+
+</body></html>
--- a/layout/generic/crashtests/crashtests.list
+++ b/layout/generic/crashtests/crashtests.list
@@ -377,8 +377,10 @@ load text-overflow-bug671796.xhtml
 load 667025.html
 asserts-if(Android,8) load 673770.html
 load 679933-1.html
 load 682649-1.html
 load 683702-1.xhtml
 load 688996-1.html
 load 688996-2.html
 load 683712.html
+load text-overflow-bug713610.html
+load 700031.xhtml
new file mode 100644
--- /dev/null
+++ b/layout/generic/crashtests/text-overflow-bug713610.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<html>
+<body>
+<div style="text-overflow: ellipsis; padding-right: 4000px; overflow: scroll;"><span style="-moz-transform: translatex(-50px); border-right-style: dashed;"></span></div>
+</body>
+</html>
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -4536,29 +4536,31 @@ nsIFrame::InvalidateInternal(const nsRec
     InvalidateInternalAfterResize(r, 0, 0, aFlags);
     return;
   }
   
   InvalidateInternalAfterResize(aDamageRect, aX, aY, aFlags);
 }
 
 gfx3DMatrix
-nsIFrame::GetTransformMatrix(nsIFrame **aOutAncestor)
+nsIFrame::GetTransformMatrix(nsIFrame* aStopAtAncestor,
+                             nsIFrame** aOutAncestor)
 {
   NS_PRECONDITION(aOutAncestor, "Need a place to put the ancestor!");
 
   /* If we're transformed, we want to hand back the combination
    * transform/translate matrix that will apply our current transform, then
    * shift us to our parent.
    */
   if (IsTransformed()) {
     /* Compute the delta to the parent, which we need because we are converting
      * coordinates to our parent.
      */
-    NS_ASSERTION(nsLayoutUtils::GetCrossDocParentFrame(this), "Cannot transform the viewport frame!");
+    NS_ASSERTION(nsLayoutUtils::GetCrossDocParentFrame(this),
+	             "Cannot transform the viewport frame!");
     PRInt32 scaleFactor = PresContext()->AppUnitsPerDevPixel();
 
     gfx3DMatrix result =
       nsDisplayTransform::GetResultingTransformMatrix(this, nsPoint(0, 0),
                                                       scaleFactor, nsnull, aOutAncestor);
     nsPoint delta = GetOffsetToCrossDoc(*aOutAncestor);
     /* Combine the raw transform with a translation to our parent. */
     result *= gfx3DMatrix::Translation
@@ -4577,17 +4579,17 @@ nsIFrame::GetTransformMatrix(nsIFrame **
    * we have to check to see if we have a parent.  If not, we'll set the
    * outparam to null (indicating that there's nothing left) and will hand back
    * the identity matrix.
    */
   if (!*aOutAncestor)
     return gfx3DMatrix();
   
   /* Keep iterating while the frame can't possibly be transformed. */
-  while (!(*aOutAncestor)->IsTransformed()) {
+  while (!(*aOutAncestor)->IsTransformed() && *aOutAncestor != aStopAtAncestor) {
     /* If no parent, stop iterating.  Otherwise, update the ancestor. */
     nsIFrame* parent = nsLayoutUtils::GetCrossDocParentFrame(*aOutAncestor);
     if (!parent)
       break;
 
     *aOutAncestor = parent;
   }
 
--- a/layout/generic/nsHTMLReflowMetrics.h
+++ b/layout/generic/nsHTMLReflowMetrics.h
@@ -66,21 +66,21 @@ enum nsOverflowType { eVisualOverflow, e
   for (nsOverflowType var_ = nsOverflowType(0); var_ < 2;                     \
        var_ = nsOverflowType(var_ + 1))
 
 struct nsOverflowAreas {
 private:
   nsRect mRects[2];
 public:
   nsRect& Overflow(size_t aIndex) {
-    NS_ASSERTION(aIndex < 2, "index out of range");
+    NS_ASSERTION(0 <= aIndex && aIndex < 2, "index out of range");
     return mRects[aIndex];
   }
   const nsRect& Overflow(size_t aIndex) const {
-    NS_ASSERTION(aIndex < 2, "index out of range");
+    NS_ASSERTION(0 <= aIndex && aIndex < 2, "index out of range");
     return mRects[aIndex];
   }
 
   nsRect& VisualOverflow() { return mRects[eVisualOverflow]; }
   const nsRect& VisualOverflow() const { return mRects[eVisualOverflow]; }
 
   nsRect& ScrollableOverflow() { return mRects[eScrollableOverflow]; }
   const nsRect& ScrollableOverflow() const { return mRects[eScrollableOverflow]; }
--- a/layout/generic/nsIFrame.h
+++ b/layout/generic/nsIFrame.h
@@ -1927,27 +1927,30 @@ public:
   /**
    * Get the "type" of the frame. May return a NULL atom pointer
    *
    * @see nsGkAtoms
    */
   virtual nsIAtom* GetType() const = 0;
 
   /**
-   * Returns a transformation matrix that converts points in this frame's coordinate space
-   * to points in some ancestor frame's coordinate space.  The frame decides which ancestor
-   * it will use as a reference point.  If this frame has no ancestor, aOutAncestor will be
-   * set to null.
+   * Returns a transformation matrix that converts points in this frame's
+   * coordinate space to points in some ancestor frame's coordinate space.
+   * The frame decides which ancestor it will use as a reference point.
+   * If this frame has no ancestor, aOutAncestor will be set to null.
    *
-   * @param aOutAncestor [out] The ancestor frame the frame has chosen.  If this frame has no
-   *        ancestor, aOutAncestor will be nsnull.
-   * @return A gfxMatrix that converts points in this frame's coordinate space into
-   *         points in aOutAncestor's coordinate space.
+   * @param aStopAtAncestor don't look further than aStopAtAncestor. If null,
+   *   all ancestors (including across documents) will be traversed.
+   * @param aOutAncestor [out] The ancestor frame the frame has chosen.  If
+   *   this frame has no ancestor, *aOutAncestor will be set to null.
+   * @return A gfxMatrix that converts points in this frame's coordinate space
+   *   into points in aOutAncestor's coordinate space.
    */
-  virtual gfx3DMatrix GetTransformMatrix(nsIFrame **aOutAncestor);
+  virtual gfx3DMatrix GetTransformMatrix(nsIFrame* aStopAtAncestor,
+                                         nsIFrame **aOutAncestor);
 
   /**
    * Bit-flags to pass to IsFrameOfType()
    */
   enum {
     eMathML =                           1 << 0,
     eSVG =                              1 << 1,
     eSVGForeignObject =                 1 << 2,
--- a/layout/generic/nsTextFrameThebes.cpp
+++ b/layout/generic/nsTextFrameThebes.cpp
@@ -2332,18 +2332,20 @@ GetEndOfTrimmedText(const nsTextFragment
 }
 
 nsTextFrame::TrimmedOffsets
 nsTextFrame::GetTrimmedOffsets(const nsTextFragment* aFrag,
                                bool aTrimAfter)
 {
   NS_ASSERTION(mTextRun, "Need textrun here");
   // This should not be used during reflow. We need our TEXT_REFLOW_FLAGS
-  // to be set correctly.
-  NS_ASSERTION(!(GetStateBits() & NS_FRAME_FIRST_REFLOW),
+  // to be set correctly.  If our parent wasn't reflowed due to the frame
+  // tree being too deep then the return value doesn't matter.
+  NS_ASSERTION(!(GetStateBits() & NS_FRAME_FIRST_REFLOW) ||
+               (GetParent()->GetStateBits() & NS_FRAME_TOO_DEEP_IN_FRAME_TREE),
                "Can only call this on frames that have been reflowed");
   NS_ASSERTION(!(GetStateBits() & NS_FRAME_IN_REFLOW),
                "Can only call this on frames that are not being reflowed");
 
   TrimmedOffsets offsets = { GetContentOffset(), GetContentLength() };
   const nsStyleText* textStyle = GetStyleText();
   // Note that pre-line newlines should still allow us to trim spaces
   // for display
--- a/layout/svg/base/src/nsSVGForeignObjectFrame.cpp
+++ b/layout/svg/base/src/nsSVGForeignObjectFrame.cpp
@@ -135,17 +135,22 @@ nsSVGForeignObjectFrame::AttributeChange
   return NS_OK;
 }
 
 /* virtual */ void
 nsSVGForeignObjectFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext)
 {
   nsSVGForeignObjectFrameBase::DidSetStyleContext(aOldStyleContext);
 
-  UpdateGraphic();
+  // No need to invalidate before first reflow - that will happen elsewhere.
+  // Moreover we haven't been initialised properly yet so we may not have the
+  // right state bits.
+  if (!(GetStateBits() & NS_FRAME_FIRST_REFLOW)) {
+    UpdateGraphic();
+  }
 }
 
 NS_IMETHODIMP
 nsSVGForeignObjectFrame::Reflow(nsPresContext*           aPresContext,
                                 nsHTMLReflowMetrics&     aDesiredSize,
                                 const nsHTMLReflowState& aReflowState,
                                 nsReflowStatus&          aStatus)
 {
@@ -273,17 +278,18 @@ nsSVGForeignObjectFrame::PaintSVG(nsSVGR
                                           NS_RGBA(0,0,0,0), flags);
 
   gfx->Restore();
 
   return rv;
 }
 
 gfx3DMatrix
-nsSVGForeignObjectFrame::GetTransformMatrix(nsIFrame **aOutAncestor)
+nsSVGForeignObjectFrame::GetTransformMatrix(nsIFrame* aAncestor,
+                                            nsIFrame **aOutAncestor)
 {
   NS_PRECONDITION(aOutAncestor, "We need an ancestor to write to!");
 
   /* Set the ancestor to be the outer frame. */
   *aOutAncestor = nsSVGUtils::GetOuterSVGFrame(this);
   NS_ASSERTION(*aOutAncestor, "How did we end up without an outer frame?");
 
   /* Return the matrix back to the root, factoring in the x and y offsets. */
--- a/layout/svg/base/src/nsSVGForeignObjectFrame.h
+++ b/layout/svg/base/src/nsSVGForeignObjectFrame.h
@@ -88,17 +88,18 @@ public:
   virtual bool IsTransformed() const
   {
     return true;
   }
 
   /**
    * Foreign objects can return a transform matrix.
    */
-  virtual gfx3DMatrix GetTransformMatrix(nsIFrame **aOutAncestor);
+  virtual gfx3DMatrix GetTransformMatrix(nsIFrame* aAncestor,
+                                         nsIFrame **aOutAncestor);
 
   /**
    * Get the "type" of the frame
    *
    * @see nsGkAtoms::svgForeignObjectFrame
    */
   virtual nsIAtom* GetType() const;
 
new file mode 100644
--- /dev/null
+++ b/layout/svg/crashtests/713413-1.svg
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<svg xmlns="http://www.w3.org/2000/svg">
+
+<marker id="m"></marker>
+
+<script>
+window.addEventListener("load", function() {
+  document.getElementById("m").appendChild(document.createElementNS("http://www.w3.org/2000/svg", "foreignObject"));
+}, false);
+</script>
+
+</svg>
--- a/layout/svg/crashtests/crashtests.list
+++ b/layout/svg/crashtests/crashtests.list
@@ -116,8 +116,9 @@ load 657077-1.svg
 load 669025-1.svg
 load 669025-2.svg
 load 682411-1.svg
 load 692203-1.svg
 load 692203-2.svg
 load 693424-1.svg
 load 709920-1.svg
 load 709920-2.svg
+load 713413-1.svg
new file mode 100644
--- /dev/null
+++ b/layout/tables/crashtests/710098-1.html
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<html>
+<body onload="document.getElementById('b').setAttribute('colspan', '2');">
+<table rules="all"><tr><td rowspan="3"></td><td id="b" rowspan="2"></td></tr>
+</table>
+</body>
+</html>
--- a/layout/tables/crashtests/crashtests.list
+++ b/layout/tables/crashtests/crashtests.list
@@ -118,8 +118,9 @@ load 580481-1.xhtml
 asserts(0-3) load 595758-1.xhtml # Bug 453871
 load 595758-2.xhtml
 load 678447-1.html
 load 691824-1.xhtml
 load 695430-1.html
 load 707622-1.html
 load 705996-1.html
 load 705996-2.html
+load 710098-1.html
--- a/layout/tables/nsCellMap.cpp
+++ b/layout/tables/nsCellMap.cpp
@@ -2177,17 +2177,17 @@ void nsCellMap::ShrinkWithoutCell(nsTabl
                                   nsRect&           aDamageArea)
 {
   NS_ASSERTION(!!aMap.mBCInfo == mIsBC, "BC state mismatch");
   PRUint32 colX, rowX;
 
   // get the rowspan and colspan from the cell map since the content may have changed
   bool zeroColSpan;
   PRUint32 numCols = aMap.GetColCount();
-  PRInt32 rowSpan = GetRowSpan(aRowIndex, aColIndex, false);
+  PRInt32 rowSpan = GetRowSpan(aRowIndex, aColIndex, true);
   PRUint32 colSpan = GetEffectiveColSpan(aMap, aRowIndex, aColIndex, zeroColSpan);
   PRUint32 endRowIndex = aRowIndex + rowSpan - 1;
   PRUint32 endColIndex = aColIndex + colSpan - 1;
 
   if (aMap.mTableFrame.HasZeroColSpans()) {
     aMap.mTableFrame.SetNeedColSpanExpansion(true);
   }
 
--- a/mfbt/Assertions.h
+++ b/mfbt/Assertions.h
@@ -33,16 +33,18 @@
  * 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 ***** */
 
+/* Implementations of runtime and static assertion macros for C and C++. */
+
 #ifndef mozilla_Assertions_h_
 #define mozilla_Assertions_h_
 
 #include "mozilla/Types.h"
 
 /*
  * MOZ_STATIC_ASSERT may be used to assert a condition *at compile time*.  This
  * can be useful when you make certain assumptions about what must hold for
--- a/mfbt/RangedPtr.h
+++ b/mfbt/RangedPtr.h
@@ -33,16 +33,21 @@
  * 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 ***** */
 
+/*
+ * Implements a smart pointer asserted to remain within a range specified at
+ * construction.
+ */
+
 #ifndef mozilla_RangedPtr_h_
 #define mozilla_RangedPtr_h_
 
 #include "mozilla/Assertions.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/Util.h"
 
 namespace mozilla {
--- a/mfbt/RefPtr.h
+++ b/mfbt/RefPtr.h
@@ -33,26 +33,24 @@
  * 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 ***** */
 
+/* Helpers for defining and using refcounted objects. */
+
 #ifndef mozilla_RefPtr_h_
 #define mozilla_RefPtr_h_
 
 #include "mozilla/Assertions.h"
 #include "mozilla/Attributes.h"
 
-/**
- * Helpers for defining and using refcounted objects.
- */
-
 namespace mozilla {
 
 template<typename T> class RefCounted;
 template<typename T> class RefPtr;
 template<typename T> class TemporaryRef;
 template<typename T> class OutParamRef;
 template<typename T> OutParamRef<T> byRef(RefPtr<T>&);
 
--- a/mfbt/Util.h
+++ b/mfbt/Util.h
@@ -32,16 +32,21 @@
  * 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 ***** */
 
+/*
+ * Miscellaneous uncategorized functionality.  Please add new functionality to
+ * new headers, or to other appropriate existing headers, not here.
+ */
+
 #ifndef mozilla_Util_h_
 #define mozilla_Util_h_
 
 #include "mozilla/Assertions.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/Types.h"
 
 #ifdef __cplusplus
--- a/mobile/android/base/GeckoApp.java
+++ b/mobile/android/base/GeckoApp.java
@@ -610,18 +610,21 @@ abstract public class GeckoApp
                     return;
 
                 if (getLayerController().getLayerClient() != mSoftwareLayerClient)
                     return;
 
                 if (mLastUri == lastHistoryEntry.mUri &&
                     mLastTitle == lastHistoryEntry.mTitle)
                     return;
-   
-                mLastViewport = mSoftwareLayerClient.getGeckoViewportMetrics().toJSON();
+
+                ViewportMetrics viewportMetrics = mSoftwareLayerClient.getGeckoViewportMetrics();
+                if (viewportMetrics != null)
+                    mLastViewport = viewportMetrics.toJSON();
+
                 mLastUri = lastHistoryEntry.mUri;
                 mLastTitle = lastHistoryEntry.mTitle;
                 Bitmap bitmap = mSoftwareLayerClient.getBitmap();
                 if (bitmap != null) {
                     ByteArrayOutputStream bos = new ByteArrayOutputStream();
                     bitmap.compress(Bitmap.CompressFormat.PNG, 0, bos);
                     mLastScreen = bos.toByteArray();
 
--- a/mobile/android/base/GeckoEvent.java
+++ b/mobile/android/base/GeckoEvent.java
@@ -73,16 +73,17 @@ public class GeckoEvent {
     public static final int ACTIVITY_SHUTDOWN = 11;
     public static final int LOAD_URI = 12;
     public static final int SURFACE_CREATED = 13;
     public static final int SURFACE_DESTROYED = 14;
     public static final int GECKO_EVENT_SYNC = 15;
     public static final int ACTIVITY_START = 17;
     public static final int BROADCAST = 19;
     public static final int VIEWPORT = 20;
+    public static final int EXPOSE = 21;
 
     public static final int IME_COMPOSITION_END = 0;
     public static final int IME_COMPOSITION_BEGIN = 1;
     public static final int IME_SET_TEXT = 2;
     public static final int IME_GET_TEXT = 3;
     public static final int IME_DELETE_TEXT = 4;
     public static final int IME_SET_SELECTION = 5;
     public static final int IME_GET_SELECTION = 6;
--- a/mobile/android/base/ProfileMigrator.java
+++ b/mobile/android/base/ProfileMigrator.java
@@ -160,30 +160,25 @@ public class ProfileMigrator {
                 // inserted with places date
                 allowUpdate = true;
             } else {
                 long androidDate = androidHistory.get(url);
                 if (androidDate < date) {
                     // Places URL hit is newer than Android,
                     // allow it to be updated with places date
                     allowUpdate = true;
-                } else {
-                    Log.i(LOGTAG, "Android history is newer, not adding: " + url
-                          + " date: " + (new Date(date)).toString()
-                          + " android: " + (new Date(androidDate)).toString());
                 }
             }
 
             if (allowUpdate) {
                 BrowserDB.updateVisitedHistory(mCr, url);
                 BrowserDB.updateHistoryDate(mCr, url, date);
                 if (title != null) {
                     BrowserDB.updateHistoryTitle(mCr, url, title);
                 }
-                Log.i(LOGTAG, "Adding history: " + url);
             }
         }
 
         protected void migrateHistory(SQLiteDatabase db) {
             Map<String, Long> androidHistory = gatherAndroidHistory();
             final ArrayList<String> placesHistory = new ArrayList<String>();
 
             Cursor cursor = null;
@@ -198,18 +193,16 @@ public class ProfileMigrator {
                     cursor.getColumnIndexOrThrow(historyDate);
 
                 cursor.moveToFirst();
                 while (!cursor.isAfterLast()) {
                     String url = cursor.getString(urlCol);
                     String title = cursor.getString(titleCol);
                     // Convert from us (Places) to ms (Java, Android)
                     long date = cursor.getLong(dateCol) / (long)1000;
-                    Log.i(LOGTAG, "History: " + title + " URL: " + url
-                          + " time: " + (new Date(date)).toString());
                     addHistory(androidHistory, url, title, date);
                     placesHistory.add(url);
                     cursor.moveToNext();
                 }
 
                 cursor.close();
             } catch (SQLiteException e) {
                 if (cursor != null) {
@@ -226,17 +219,16 @@ public class ProfileMigrator {
                             GlobalHistory.getInstance().addToGeckoOnly(url);
                         }
                     }
              });
         }
 
         protected void addBookmark(String url, String title) {
             if (!BrowserDB.isBookmark(mCr, url)) {
-                Log.i(LOGTAG, "Adding bookmark: " + url);
                 if (title == null) {
                     title = url;
                 }
                 BrowserDB.addBookmark(mCr, title, url);
             }
         }
 
         protected void migrateBookmarks(SQLiteDatabase db) {
@@ -249,17 +241,16 @@ public class ProfileMigrator {
                         cursor.getColumnIndexOrThrow(bookmarkUrl);
                     final int titleCol =
                         cursor.getColumnIndexOrThrow(bookmarkTitle);
 
                     cursor.moveToFirst();
                     while (!cursor.isAfterLast()) {
                         String url = cursor.getString(urlCol);
                         String title = cursor.getString(titleCol);
-                        Log.i(LOGTAG, "Bookmark: " + title + " URL: " + url);
                         addBookmark(url, title);
                         cursor.moveToNext();
                     }
                 }
                 cursor.close();
             } catch (SQLiteException e) {
                 if (cursor != null) {
                     cursor.close();
@@ -267,22 +258,23 @@ public class ProfileMigrator {
                 Log.i(LOGTAG, "Failed to get bookmarks: " + e.getMessage());
                 return;
             }
         }
 
         protected void addFavicon(String url, String mime, byte[] data) {
             ByteArrayInputStream byteStream = new ByteArrayInputStream(data);
             BitmapDrawable image = (BitmapDrawable) Drawable.createFromStream(byteStream, "src");
-            try {
-                BrowserDB.updateFaviconForUrl(mCr, url, image);
-                Log.i(LOGTAG, "Favicon added: " + mime + " URL: " + url);
-            } catch (SQLiteException e) {
-                Log.i(LOGTAG, "Favicon failed: " + mime + " URL: " + url
-                      + " error:" + e.getMessage());
+            if (image != null) {
+                try {
+                    BrowserDB.updateFaviconForUrl(mCr, url, image);
+                } catch (SQLiteException e) {
+                    Log.i(LOGTAG, "Migrating favicon failed: " + mime + " URL: " + url
+                          + " error:" + e.getMessage());
+                }
             }
         }
 
         protected void migrateFavicons(SQLiteDatabase db) {
             Cursor cursor = null;
             try {
                 cursor = db.rawQuery(faviconQuery,
                                      new String[] {});
@@ -344,16 +336,18 @@ public class ProfileMigrator {
                 migrateHistory(db);
                 migrateFavicons(db);
                 db.close();
 
                 // Clean up
                 dbFile.delete();
                 dbFileWal.delete();
                 dbFileShm.delete();
+
+                Log.i(LOGTAG, "Profile migration finished");
             } catch (SQLiteException e) {
                 if (db != null) {
                     db.close();
                 }
                 Log.i(LOGTAG, "Error on places database:" + e.getMessage());
                 return;
             }
         }
--- a/mobile/android/base/gfx/GeckoSoftwareLayerClient.java
+++ b/mobile/android/base/gfx/GeckoSoftwareLayerClient.java
@@ -135,17 +135,18 @@ public class GeckoSoftwareLayerClient ex
     public void setLayerController(LayerController layerController) {
         super.setLayerController(layerController);
 
         layerController.setRoot(mTileLayer);
         if (mGeckoViewport != null) {
             layerController.setViewportMetrics(mGeckoViewport);
         }
 
-        GeckoAppShell.registerGeckoEventListener("Viewport:Update", this);
+        GeckoAppShell.registerGeckoEventListener("Viewport:Expose", this);
+        GeckoAppShell.registerGeckoEventListener("Viewport:UpdateAndDraw", this);
         GeckoAppShell.registerGeckoEventListener("Viewport:UpdateLater", this);
     }
 
     public void beginDrawing(int width, int height) {
         beginTransaction(mTileLayer);
 
         if (mBufferSize.width != width || mBufferSize.height != height) {
             mBufferSize = new IntSize(width, height);
@@ -335,26 +336,22 @@ public class GeckoSoftwareLayerClient ex
             mViewportSizeChanged = false;
             GeckoAppShell.viewSizeChanged();
         }
 
         mLastViewportChangeTime = System.currentTimeMillis();
     }
 
     public void handleMessage(String event, JSONObject message) {
-        if ("Viewport:Update".equals(event)) {
-            beginTransaction(mTileLayer);
-            try {
-                updateViewport(message.getString("viewport"), false);
-            } catch (JSONException e) {
-                Log.e(LOGTAG, "Unable to update viewport", e);
-            } finally {
-                endTransaction(mTileLayer);
-            }
+        if ("Viewport:Expose".equals(event)) {
+            GeckoAppShell.sendEventToGecko(new GeckoEvent(GeckoEvent.EXPOSE));
+        } else if ("Viewport:UpdateAndDraw".equals(event)) {
+            mUpdateViewportOnEndDraw = true;
+
+            // Redraw everything.
+            Rect rect = new Rect(0, 0, mBufferSize.width, mBufferSize.height);
+            GeckoAppShell.sendEventToGecko(new GeckoEvent(GeckoEvent.DRAW, rect));
         } else if ("Viewport:UpdateLater".equals(event)) {
-            if (!mTileLayer.inTransaction()) {
-                Log.e(LOGTAG, "Viewport:UpdateLater called while not in transaction. You should be using Viewport:Update instead!");
-            }
             mUpdateViewportOnEndDraw = true;
         }
     }
 }
 
--- a/mobile/android/base/ui/PanZoomController.java
+++ b/mobile/android/base/ui/PanZoomController.java
@@ -259,17 +259,20 @@ public class PanZoomController
             mX.velocity = mY.velocity = 0.0f;
             mState = PanZoomState.NOTHING;
             // fall through
         case ANIMATED_ZOOM:
             // the zoom that's in progress likely makes no sense any more (such as if
             // the screen orientation changed) so abort it and start a new one to
             // ensure the viewport doesn't contain out-of-bounds areas
         case NOTHING:
-            bounce();
+            // Don't do animations here; they're distracting and can cause flashes on page
+            // transitions.
+            mController.setViewportMetrics(getValidViewportMetrics());
+            mController.notifyLayerClientOfGeometryChange();
             break;
         }
     }
 
     /*
      * Panning/scrolling
      */
 
@@ -661,30 +664,30 @@ public class PanZoomController
                 mX.advanceFling();
             if (flingingY)
                 mY.advanceFling();
 
             /* If we're still flinging in any direction, update the origin. */
             if (flingingX || flingingY) {
                 mX.displace(); mY.displace();
                 updatePosition();
+
+                /*
+                 * If we're still flinging with an appreciable velocity, stop here. The threshold is
+                 * higher in the case of overscroll, so we bounce back eagerly when overscrolling but
+                 * coast smoothly to a stop when not.
+                 */
+                float excess = PointUtils.distance(new PointF(mX.getExcess(), mY.getExcess()));
+                PointF velocityVector = new PointF(mX.getRealVelocity(), mY.getRealVelocity());
+                float threshold = (excess >= 1.0f) ? STOPPED_THRESHOLD : FLING_STOPPED_THRESHOLD;
+                if (PointUtils.distance(velocityVector) >= threshold)
+                    return;
             }
 
             /*
-             * If we're still flinging with an appreciable velocity, stop here. The threshold is
-             * higher in the case of overscroll, so we bounce back eagerly when overscrolling but
-             * coast smoothly to a stop when not.
-             */
-            float excess = PointUtils.distance(new PointF(mX.getExcess(), mY.getExcess()));
-            PointF velocityVector = new PointF(mX.getRealVelocity(), mY.getRealVelocity());
-            float threshold = (excess >= 1.0f) ? STOPPED_THRESHOLD : FLING_STOPPED_THRESHOLD;
-            if (PointUtils.distance(velocityVector) >= threshold)
-                return;
-
-            /*
              * Perform a bounce-back animation if overscrolled, unless panning is being overridden
              * (which happens e.g. when the user is panning an iframe).
              */
             boolean overscrolledX = mX.getOverscroll() != Axis.Overscroll.NONE;
             boolean overscrolledY = mY.getOverscroll() != Axis.Overscroll.NONE;
             if (!mOverridePanning && (overscrolledX || overscrolledY)) {
                 bounce();
             } else {
--- a/mobile/android/chrome/content/browser.js
+++ b/mobile/android/chrome/content/browser.js
@@ -150,31 +150,41 @@ var Strings = {};
   ["browser",    "chrome://browser/locale/browser.properties"]
 ].forEach(function (aStringBundle) {
   let [name, bundle] = aStringBundle;
   XPCOMUtils.defineLazyGetter(Strings, name, function() {
     return Services.strings.createBundle(bundle);
   });
 });
 
+var MetadataProvider = {
+  getDrawMetadata: function getDrawMetadata() {
+    return BrowserApp.getDrawMetadata();
+  },
+
+  drawingAllowed: function drawingAllowed() {
+    return !BrowserApp.selectedTab.suppressDrawing;
+  }
+};
+
 var BrowserApp = {
   _tabs: [],
   _selectedTab: null,
 
   deck: null,
 
   startup: function startup() {
     window.QueryInterface(Ci.nsIDOMChromeWindow).browserDOMWindow = new nsBrowserAccess();
     dump("zerdatime " + Date.now() + " - browser chrome startup finished.");
 
     this.deck = document.getElementById("browsers");
     BrowserEventHandler.init();
     ViewportHandler.init();
 
-    getBridge().setDrawMetadataProvider(this.getDrawMetadata.bind(this));
+    getBridge().setDrawMetadataProvider(MetadataProvider);
 
     Services.obs.addObserver(this, "Tab:Add", false);
     Services.obs.addObserver(this, "Tab:Load", false);
     Services.obs.addObserver(this, "Tab:Select", false);
     Services.obs.addObserver(this, "Tab:Close", false);
     Services.obs.addObserver(this, "Session:Back", false);
     Services.obs.addObserver(this, "Session:Forward", false);
     Services.obs.addObserver(this, "Session:Reload", false);
@@ -185,16 +195,17 @@ var BrowserApp = {
     Services.obs.addObserver(this, "Preferences:Set", false);
     Services.obs.addObserver(this, "ScrollTo:FocusedInput", false);
     Services.obs.addObserver(this, "Sanitize:ClearAll", false);
     Services.obs.addObserver(this, "PanZoom:PanZoom", false);
     Services.obs.addObserver(this, "FullScreen:Exit", false);
     Services.obs.addObserver(this, "Viewport:Change", false);
     Services.obs.addObserver(this, "AgentMode:Change", false);
     Services.obs.addObserver(this, "SearchEngines:Get", false);
+    Services.obs.addObserver(this, "document-shown", false);
 
     function showFullScreenWarning() {
       NativeWindow.toast.show(Strings.browser.GetStringFromName("alertFullScreenToast"), "short");
     }
 
     window.addEventListener("fullscreen", function() {
       sendMessageToJava({
         gecko: {
@@ -422,17 +433,16 @@ var BrowserApp = {
       evt.initUIEvent("TabSelect", true, false, window, null);
       aTab.browser.dispatchEvent(evt);
 
       sendMessageToJava(message);
     }
   },
 
   quit: function quit() {
-      Cu.reportError("got quit quit message");
       window.QueryInterface(Ci.nsIDOMChromeWindow).minimize();
       window.close();
   },
 
   saveAsPDF: function saveAsPDF(aBrowser) {
     // Create the final destination file location
     let fileName = ContentAreaUtils.getDefaultFileName(aBrowser.contentTitle, aBrowser.documentURI, null, null);
     fileName = fileName.trim() + ".pdf";
@@ -634,18 +644,75 @@ var BrowserApp = {
   },
 
   scrollToFocusedInput: function(aBrowser) {
     let doc = aBrowser.contentDocument;
     if (!doc)
       return;
     let focused = doc.activeElement;
     if ((focused instanceof HTMLInputElement && focused.mozIsTextField(false)) || (focused instanceof HTMLTextAreaElement)) {
-      focused.scrollIntoView(false);
-      BrowserApp.getTabForBrowser(aBrowser).sendViewportUpdate();
+      let tab = BrowserApp.getTabForBrowser(aBrowser);
+      let win = aBrowser.contentWindow;
+
+      // tell gecko to scroll the field into view. this will scroll any nested scrollable elements
+      // as well as the browser's content window, and modify the scrollX and scrollY on the content window.
+      focused.scrollIntoView(true);
+
+      // update userScrollPos so that we don't send a duplicate viewport update by triggering
+      // our scroll listener
+      tab.userScrollPos.x = win.scrollX;
+      tab.userScrollPos.y = win.scrollY;
+
+      // note that:
+      // 1. because of the way we do zooming using a CSS transform, gecko does not take into
+      // account the effect of the zoom on the viewport size.
+      // 2. if the input element is near the bottom/right of the page (less than one viewport
+      // height/width away from the bottom/right), the scrollIntoView call will make gecko scroll to the
+      // bottom/right of the page in an attempt to align the input field with the top of the viewport.
+      // however, since gecko doesn't know about the zoom, what it thinks is the "bottom/right of
+      // the page" isn't actually the bottom/right of the page at the current zoom level, and we 
+      // need to adjust this further.
+      // 3. we can't actually adjust this by changing the window scroll position, as gecko already thinks
+      // we're at the bottom/right, so instead we do it by changing the viewportExcess on the tab and
+      // moving the browser element.
+
+      let visibleContentWidth = tab._viewport.width / tab._viewport.zoom;
+      let visibleContentHeight = tab._viewport.height / tab._viewport.zoom;
+      // get the rect that the focused element occupies relative to what gecko thinks the viewport is,
+      // and adjust it by viewportExcess to so that it is relative to what the user sees as the viewport.
+      let focusedRect = focused.getBoundingClientRect();
+      focusedRect = {
+        left: focusedRect.left - tab.viewportExcess.x,
+        right: focusedRect.right - tab.viewportExcess.x,
+        top: focusedRect.top - tab.viewportExcess.y,
+        bottom: focusedRect.bottom - tab.viewportExcess.y
+      };
+      let transformChanged = false;
+      if (focusedRect.right >= visibleContentWidth && focusedRect.left > 0) {
+        // the element is too far off the right side, so we need to scroll to the right more
+        tab.viewportExcess.x += Math.min(focusedRect.left, focusedRect.right - visibleContentWidth);
+        transformChanged = true;
+      } else if (focusedRect.left < 0) {
+        // the element is too far off the left side, so we need to scroll to the left more
+        tab.viewportExcess.x += focusedRect.left;
+        transformChanged = true;
+      }
+      if (focusedRect.bottom >= visibleContentHeight && focusedRect.top > 0) {
+        // the element is too far down, so we need to scroll down more
+        tab.viewportExcess.y += Math.min(focusedRect.top, focusedRect.bottom - visibleContentHeight);
+        transformChanged = true;
+      } else if (focusedRect.top < 0) {
+        // the element is too far up, so we need to scroll up more
+        tab.viewportExcess.y += focusedRect.top;
+        transformChanged = true;
+      }
+      if (transformChanged)
+        tab.updateTransform();
+      // finally, let java know where we ended up
+      tab.sendViewportUpdate();
     }
   },
 
   getDrawMetadata: function getDrawMetadata() {
     return JSON.stringify(this.selectedTab.viewport);
   },
 
   observe: function(aSubject, aTopic, aData) {
@@ -700,24 +767,38 @@ var BrowserApp = {
       Sanitizer.sanitize();
     } else if (aTopic == "FullScreen:Exit") {
       browser.contentDocument.mozCancelFullScreen();
     } else if (aTopic == "Viewport:Change") {
       this.selectedTab.viewport = JSON.parse(aData);
       ViewportHandler.onResize();
     } else if (aTopic == "SearchEngines:Get") {
       this.getSearchEngines();
+    } else if (aTopic == "document-shown") {
+      let tab = this.selectedTab;
+      if (tab.browser.contentDocument != aSubject) {
+        return;
+      }
+
+      ViewportHandler.resetMetadata(tab);
+
+      // Unsuppress drawing unless the page was being thawed from the bfcache (which is an atomic
+      // operation, so there is no drawing to suppress).
+      if (tab.suppressDrawing) {
+        tab.sendExposeEvent();
+        tab.suppressDrawing = false;
+      }
     }
   },
 
   get defaultBrowserWidth() {
     delete this.defaultBrowserWidth;
     let width = Services.prefs.getIntPref("browser.viewport.desktopWidth");
     return this.defaultBrowserWidth = width;
-   }
+  }
 };
 
 var NativeWindow = {
   init: function() {
     Services.obs.addObserver(this, "Menu:Clicked", false);
     Services.obs.addObserver(this, "Doorhanger:Reply", false);
     this.contextmenus.init();
   },
@@ -1179,16 +1260,22 @@ Tab.prototype = {
 
     this.browser.addEventListener("DOMContentLoaded", this, true);
     this.browser.addEventListener("DOMLinkAdded", this, true);
     this.browser.addEventListener("DOMTitleChanged", this, true);
     this.browser.addEventListener("scroll", this, true);
     this.browser.addEventListener("PluginClickToPlay", this, true);
     this.browser.addEventListener("pagehide", this, true);
 
+    let chromeEventHandler = this.browser.contentWindow.QueryInterface(Ci.nsIInterfaceRequestor)
+                                                       .getInterface(Ci.nsIWebNavigation)
+                                                       .QueryInterface(Ci.nsIDocShell)
+                                                       .chromeEventHandler;
+    chromeEventHandler.addEventListener("DOMWindowCreated", this, false);
+
     Services.obs.addObserver(this, "http-on-modify-request", false);
 
     if (!aParams.delayLoad) {
       let flags = "flags" in aParams ? aParams.flags : Ci.nsIWebNavigation.LOAD_FLAGS_NONE;
       let postData = ("postData" in aParams && aParams.postData) ? aParams.postData.value : null;
       let referrerURI = "referrerURI" in aParams ? aParams.referrerURI : null;
       let charset = "charset" in aParams ? aParams.charset : null;
 
@@ -1315,30 +1402,32 @@ Tab.prototype = {
       this.viewportExcess.y = excessY;
       transformChanged = true;
     }
     if (Math.abs(aViewport.zoom - this._viewport.zoom) >= 1e-6) {
       this._viewport.zoom = aViewport.zoom;
       transformChanged = true;
     }
 
+    if (transformChanged)
+      this.updateTransform();
+  },
+
+  updateTransform: function() {
     let hasZoom = (Math.abs(this._viewport.zoom - 1.0) >= 1e-6);
-
-    if (transformChanged) {
-      let x = this._viewport.offsetX + Math.round(-excessX * this._viewport.zoom);
-      let y = this._viewport.offsetY + Math.round(-excessY * this._viewport.zoom);
-
-      let transform =
-        "translate(" + x + "px, " +
-                       y + "px)";
-      if (hasZoom)
-        transform += " scale(" + this._viewport.zoom + ")";
-
-      this.browser.style.MozTransform = transform;
-    }
+    let x = this._viewport.offsetX + Math.round(-this.viewportExcess.x * this._viewport.zoom);
+    let y = this._viewport.offsetY + Math.round(-this.viewportExcess.y * this._viewport.zoom);
+
+    let transform =
+      "translate(" + x + "px, " +
+                     y + "px)";
+    if (hasZoom)
+      transform += " scale(" + this._viewport.zoom + ")";
+
+    this.browser.style.MozTransform = transform;
   },
 
   get viewport() {
     // Update the viewport to current dimensions
     this._viewport.x = (this.browser.contentWindow.scrollX +
                         this.viewportExcess.x) || 0;
     this._viewport.y = (this.browser.contentWindow.scrollY +
                         this.viewportExcess.y) || 0;
@@ -1388,18 +1477,17 @@ Tab.prototype = {
     this.sendViewportUpdate();
   },
 
   sendViewportUpdate: function() {
     if (BrowserApp.selectedTab != this)
       return;
     sendMessageToJava({
       gecko: {
-        type: "Viewport:Update",
-        viewport: JSON.stringify(this.viewport)
+        type: "Viewport:UpdateAndDraw"
       }
     });
   },
 
   handleEvent: function(aEvent) {
     switch (aEvent.type) {
       case "DOMContentLoaded": {
         let target = aEvent.originalTarget;
@@ -1532,16 +1620,28 @@ Tab.prototype = {
         // Check to make sure it's top-level pagehide
         if (aEvent.target.defaultView == this.browser.contentWindow) {
           // Reset plugin state when we leave the page
           this._pluginsToPlay = [];
           this._pluginOverlayShowing = false;
         }
         break;
       }
+
+      case "DOMWindowCreated": {
+        // Conveniently, this call to getBrowserForDocument() will return null if the document is
+        // not the top-level content document of the browser.
+        let browser = BrowserApp.getBrowserForDocument(aEvent.originalTarget);
+        if (!browser)
+          break;
+
+        let tab = BrowserApp.getTabForBrowser(browser);
+        tab.suppressDrawing = true;
+        break;
+      }
     }
   },
 
   onStateChange: function(aWebProgress, aRequest, aStateFlags, aStatus) {
     if (aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK) {
       // Filter optimization: Only really send NETWORK state changes to Java listener
       let browser = BrowserApp.getBrowserForWindow(aWebProgress.DOMWindow);
       let uri = "";
@@ -1791,16 +1891,26 @@ Tab.prototype = {
     let channelWindow = this.getWindowForRequest(channel);
     if (channelWindow == this.browser.contentWindow) {
       this.setHostFromURL(channel.URI.spec);
       if (this.agentMode == UA_MODE_DESKTOP)
         channel.setRequestHeader("User-Agent", "Mozilla/5.0 (X11; Linux x86_64; rv:7.0.1) Gecko/20100101 Firefox/7.0.1", false);
     }
   },
 
+  sendExposeEvent: function() {
+    // Now that the document is actually on the screen, send an expose event.
+    this._timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
+    sendMessageToJava({
+      gecko: {
+        type: "Viewport:Expose"
+      }
+    });
+  },
+
   QueryInterface: XPCOMUtils.generateQI([
     Ci.nsIWebProgressListener,
     Ci.nsISHistoryListener,
     Ci.nsIObserver,
     Ci.nsISupportsWeakReference
   ])
 };
 
--- a/mobile/android/chrome/tests/browser_scrollbar.js
+++ b/mobile/android/chrome/tests/browser_scrollbar.js
@@ -60,17 +60,19 @@ function checkScrollbarsPosition(aX) {
   let browser = getBrowser();
   let width = browser.getBoundingClientRect().width;
   let height = browser.getBoundingClientRect().height;
   EventUtils.synthesizeMouse(browser, width / 2, height / 4, { type: "mousedown" });
   EventUtils.synthesizeMouse(browser, width / 2, height * 3 / 4, { type: "mousemove" });
 
   let verticalRect = verticalScrollbar.getBoundingClientRect();
   let margin = parseInt(verticalScrollbar.getAttribute("end"));
-  let expectedPosition = window.innerWidth - aX - margin;
+  let matches = verticalScrollbar.style.MozTransform.match(/^translate\(([-0-9]+)px/);
+  let translateX = matches ? parseInt(matches[1]) : 0;
+  let expectedPosition = window.innerWidth - aX - margin + translateX;
   is(verticalRect.right, expectedPosition, "The vertical scrollbar should be position to " + expectedPosition + " (got " + verticalRect.right + ")");
 
   EventUtils.synthesizeMouse(browser, width / 2, height * 3 / 4, { type: "mouseup" });
 }
 
 gTests.push({
   desc: "Testing visibility of scrollbars",
 
--- a/mobile/xul/chrome/tests/browser_scrollbar.js
+++ b/mobile/xul/chrome/tests/browser_scrollbar.js
@@ -60,17 +60,19 @@ function checkScrollbarsPosition(aX) {
   let browser = getBrowser();
   let width = browser.getBoundingClientRect().width;
   let height = browser.getBoundingClientRect().height;
   EventUtils.synthesizeMouse(browser, width / 2, height / 4, { type: "mousedown" });
   EventUtils.synthesizeMouse(browser, width / 2, height * 3 / 4, { type: "mousemove" });
 
   let verticalRect = verticalScrollbar.getBoundingClientRect();
   let margin = parseInt(verticalScrollbar.getAttribute("end"));
-  let expectedPosition = window.innerWidth - aX - margin;
+  let matches = verticalScrollbar.style.MozTransform.match(/^translate\(([-0-9]+)px/);
+  let translateX = matches ? parseInt(matches[1]) : 0;
+  let expectedPosition = window.innerWidth - aX - margin + translateX;
   is(verticalRect.right, expectedPosition, "The vertical scrollbar should be position to " + expectedPosition + " (got " + verticalRect.right + ")");
 
   EventUtils.synthesizeMouse(browser, width / 2, height * 3 / 4, { type: "mouseup" });
 }
 
 gTests.push({
   desc: "Testing visibility of scrollbars",
 
--- a/testing/mochitest/tests/SimpleTest/SimpleTest.js
+++ b/testing/mochitest/tests/SimpleTest/SimpleTest.js
@@ -11,17 +11,21 @@
  * NOTE: Pay attention to cross-browser compatibility in this file. For
  * instance, do not use const or JS > 1.5 features which are not yet
  * implemented everywhere.
  *
 **/
 
 var SimpleTest = { };
 var parentRunner = null;
-var isPrimaryTestWindow = !!parent.TestRunner;
+
+// In normal test runs, the window that has a TestRunner in its parent is
+// the primary window.  In single test runs, if there is no parent and there
+// is no opener then it is the primary window.
+var isPrimaryTestWindow = !!parent.TestRunner || (parent == window && !opener);
 
 // Finds the TestRunner for this test run and the SpecialPowers object (in
 // case it is not defined) from a parent/opener window.
 //
 // Finding the SpecialPowers object is needed when we have ChromePowers in
 // harness.xul and we need SpecialPowers in the iframe, and also for tests
 // like test_focus.xul where we open a window which opens another window which
 // includes SimpleTest.js.
--- a/toolkit/components/aboutmemory/content/aboutMemory.js
+++ b/toolkit/components/aboutmemory/content/aboutMemory.js
@@ -231,16 +231,19 @@ Reporter.prototype = {
       this._amount += r._amount;
     } else if (this._amount === kUnknown && r._amount !== kUnknown) {
       this._amount = r._amount;
     }
     this._nMerged = this._nMerged ? this._nMerged + 1 : 2;
   },
 
   treeNameMatches: function(aTreeName) {
+    // Nb: the '/' must be present, because we have a KIND_OTHER reporter
+    // called "explicit" which is not part of the "explicit" tree.
+    aTreeName += "/";
     return this._path.slice(0, aTreeName.length) === aTreeName;
   }
 };
 
 function getReportersByProcess()
 {
   var mgr = Cc["@mozilla.org/memory-reporter-manager;1"].
       getService(Ci.nsIMemoryReporterManager);
--- a/toolkit/components/aboutmemory/tests/test_aboutmemory.xul
+++ b/toolkit/components/aboutmemory/tests/test_aboutmemory.xul
@@ -31,18 +31,20 @@
     realReporters.push(r);
   }
   e = mgr.enumerateMultiReporters();
   var realMultiReporters = [];
   var dummy = 0;
   while (e.hasMoreElements()) {
     var r = e.getNext().QueryInterface(Ci.nsIMemoryMultiReporter);
     // Call collectReports, even though we don't use its results, just to
-    // test that the multi-reporter doesn't crash or anything.
+    // test that the multi-reporter doesn't crash or anything.  And likewise
+    // for the |explicitNonHeap| field.
     r.collectReports(function(){}, null);
+    dummy += r.explicitNonHeap;
     mgr.unregisterMultiReporter(r);
     realMultiReporters.push(r);
   }
 
   // Setup various fake-but-deterministic reporters.
   const KB = 1024;
   const MB = KB * KB;
   const kUnknown = -1;
@@ -73,54 +75,57 @@
   var fakeReporters = [
     f("", "heap-allocated",     OTHER,   500 * MB),
     f("", "heap-unallocated",   OTHER,   100 * MB),
     f("", "explicit/a",         HEAP,    222 * MB),
     f("", "explicit/b/a",       HEAP,     85 * MB),
     f("", "explicit/b/b",       HEAP,     75 * MB),
     f("", "explicit/b/c/a",     HEAP,     70 * MB),
     f("", "explicit/b/c/b",     HEAP,      2 * MB), // omitted
-    f("", "explicit/c",         NONHEAP, 100 * MB),
-    f("", "explicit/c/d",       NONHEAP,  13 * MB), // subsumed by parent
     f("", "explicit/g",         HEAP,      1 * MB), // internal, dup: merge
     f("", "explicit/g/a",       HEAP,      6 * MB),
     f("", "explicit/g/b",       HEAP,      5 * MB),
     f("", "other1",             OTHER,   111 * MB),
     f2("", "other4",            OTHER,   COUNT_CUMULATIVE, 888)
   ];
   var fakeMultiReporters = [
      { collectReports: function(cbObj, closure) {
           function f(p, k, u, a) { cbObj.callback("", p, k, u, a, "(desc)", closure); }
+          f("explicit/c",       NONHEAP, BYTES, 100 * MB),
+          f("explicit/c/d",     NONHEAP, BYTES,  13 * MB), // subsumed by parent
           f("explicit/c/d",     NONHEAP, BYTES,  10 * MB), // dup, subsumed by parent
           f("explicit/cc",      NONHEAP, BYTES,  13 * MB);
           f("explicit/cc",      NONHEAP, BYTES,  10 * MB); // dup
           f("explicit/d",       NONHEAP, BYTES, 499 * KB); // omitted
           f("explicit/e",       NONHEAP, BYTES, 100 * KB); // omitted
           f("explicit/f/g/h/i", HEAP,    BYTES,  20 * MB);
-       }
+       },
+       explicitNonHeap: (100 + 13 + 10)*MB + (499 + 100)*KB
      },
      { collectReports: function(cbObj, closure) {
           function f(p, k, u, a) { cbObj.callback("", p, k, u, a, "(desc)", closure); }
           f("explicit/g",       HEAP,    BYTES,  14 * MB); // internal
           f("other3",           OTHER,   COUNT, 777);
           f("other2",           OTHER,   BYTES, 222 * MB);
           f("perc2",            OTHER,   PERCENTAGE, 10000);
           f("perc1",            OTHER,   PERCENTAGE, 4567);
-       }
+       },
+       explicitNonHeap: 0
      },
      { collectReports: function(cbObj, closure) {
           // The amounts are given in pages, so multiply here by 4kb.
           function f(p, a) { cbObj.callback("", p, NONHEAP, BYTES, a * 4 * KB, "(desc)", closure); }
           f("map/vsize/a",     24);
           f("map/swap/a",       1);
           f("map/swap/a",       2);
           f("map/vsize/a",      19);
           f("map/swap/b/c",     10);
           f("map/resident/a",   42);
-       }
+       },
+       explicitNonHeap: 0
      }
   ];
   for (var i = 0; i < fakeReporters.length; i++) {
     mgr.registerReporter(fakeReporters[i]);
   }
   for (var i = 0; i < fakeMultiReporters.length; i++) {
     mgr.registerMultiReporter(fakeMultiReporters[i]);
   }
--- a/toolkit/components/downloads/test/schema_migration/head_migration.js
+++ b/toolkit/components/downloads/test/schema_migration/head_migration.js
@@ -29,22 +29,28 @@
  * use your version of this file under the terms of the MPL, indicate your
  * decision by devaring the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not devare
  * 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 ***** */
 
+Components.utils.import("resource://gre/modules/Services.jsm");
+
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cr = Components.results;
 
 do_get_profile();
 
+do_register_cleanup(function() {
+  Services.obs.notifyObservers(null, "quit-application", null);
+});
+
 var dirSvc = Cc["@mozilla.org/file/directory_service;1"].
              getService(Ci.nsIProperties);
 
 function importDatabaseFile(aFName)
 {
   var file = do_get_file(aFName);
   var newFile = dirSvc.get("ProfD", Ci.nsIFile);
   file.copyTo(newFile, "downloads.sqlite");
--- a/toolkit/components/telemetry/TelemetryPing.js
+++ b/toolkit/components/telemetry/TelemetryPing.js
@@ -52,16 +52,17 @@ const PREF_ENABLED = "toolkit.telemetry.
 const TELEMETRY_INTERVAL = 60000;
 // Delay before intializing telemetry (ms)
 const TELEMETRY_DELAY = 60000;
 // about:memory values to turn into histograms
 const MEM_HISTOGRAMS = {
   "js-gc-heap": "MEMORY_JS_GC_HEAP",
   "js-compartments-system": "MEMORY_JS_COMPARTMENTS_SYSTEM",
   "js-compartments-user": "MEMORY_JS_COMPARTMENTS_USER",
+  "explicit": "MEMORY_EXPLICIT",
   "resident": "MEMORY_RESIDENT",
   "explicit/storage/sqlite": "MEMORY_STORAGE_SQLITE",
   "explicit/images/content/used/uncompressed":
     "MEMORY_IMAGES_CONTENT_USED_UNCOMPRESSED",
   "heap-allocated": "MEMORY_HEAP_ALLOCATED",
   "page-faults-hard": "PAGE_FAULTS_HARD",
   "low-memory-events-virtual": "LOW_MEMORY_EVENTS_VIRTUAL",
   "low-memory-events-physical": "LOW_MEMORY_EVENTS_PHYSICAL"
@@ -258,55 +259,53 @@ TelemetryPing.prototype = {
       // OK to skip memory reporters in xpcshell
       return;
     }
 
     let e = mgr.enumerateReporters();
     while (e.hasMoreElements()) {
       let mr = e.getNext().QueryInterface(Ci.nsIMemoryReporter);
       let id = MEM_HISTOGRAMS[mr.path];
-      if (!id || mr.amount == -1) {
+      if (!id) {
+        continue;
+      }
+      // mr.amount is expensive to read in some cases, so get it only once.
+      let amount = mr.amount;
+      if (amount == -1) {
         continue;
       }
 
       let val;
       if (mr.units == Ci.nsIMemoryReporter.UNITS_BYTES) {
-        val = Math.floor(mr.amount / 1024);
+        val = Math.floor(amount / 1024);
       }
       else if (mr.units == Ci.nsIMemoryReporter.UNITS_COUNT) {
-        val = mr.amount;
+        val = amount;
       }
       else if (mr.units == Ci.nsIMemoryReporter.UNITS_COUNT_CUMULATIVE) {
         // If the reporter gives us a cumulative count, we'll report the
         // difference in its value between now and our previous ping.
 
-        // Read mr.amount just once so our arithmetic is consistent.
-        let curVal = mr.amount;
         if (!(mr.path in this._prevValues)) {
           // If this is the first time we're reading this reporter, store its
           // current value but don't report it in the telemetry ping, so we
           // ignore the effect startup had on the reporter.
-          this._prevValues[mr.path] = curVal;
+          this._prevValues[mr.path] = amount;
           continue;
         }
 
-        val = curVal - this._prevValues[mr.path];
-        this._prevValues[mr.path] = curVal;
+        val = amount - this._prevValues[mr.path];
+        this._prevValues[mr.path] = amount;
       }
       else {
         NS_ASSERT(false, "Can't handle memory reporter with units " + mr.units);
         continue;
       }
       this.addValue(mr.path, id, val);
     }
-    // "explicit" is found differently.
-    let explicit = mgr.explicit;    // Get it only once, it's reasonably expensive
-    if (explicit != -1) {
-      this.addValue("explicit", "MEMORY_EXPLICIT", Math.floor(explicit / 1024));
-    }
   },
   
   /** 
    * Make a copy of sqlite histograms on startup
    */
   gatherStartupSqlite: function gatherStartupSqlite() {
     let hls = Telemetry.histogramSnapshots;
     let sqlite_re = /SQLITE/;
--- a/toolkit/content/InlineSpellChecker.jsm
+++ b/toolkit/content/InlineSpellChecker.jsm
@@ -33,19 +33,21 @@
  * 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 = [ "InlineSpellChecker" ];
 var gLanguageBundle;
 var gRegionBundle;
+const MAX_UNDO_STACK_DEPTH = 1;
 
 function InlineSpellChecker(aEditor) {
   this.init(aEditor);
+  this.mAddedWordStack = []; // We init this here to preserve it between init/uninit calls
 }
 
 InlineSpellChecker.prototype = {
   // Call this function to initialize for a given editor
   init: function(aEditor)
   {
     this.uninit();
     this.mEditor = aEditor;
@@ -279,15 +281,34 @@ InlineSpellChecker.prototype = {
   toggleEnabled: function()
   {
     this.mEditor.setSpellcheckUserOverride(!this.mInlineSpellChecker.enableRealTimeSpell);
   },
 
   // callback for adding the current misspelling to the user-defined dictionary
   addToDictionary: function()
   {
+    // Prevent the undo stack from growing over the max depth
+    if (this.mAddedWordStack.length == MAX_UNDO_STACK_DEPTH)
+      this.mAddedWordStack.shift();
+      
+    this.mAddedWordStack.push(this.mMisspelling);
     this.mInlineSpellChecker.addWordToDictionary(this.mMisspelling);
   },
+  // callback for removing the last added word to the dictionary LIFO fashion
+  undoAddToDictionary: function()
+  {
+    if (this.mAddedWordStack.length > 0)
+    {
+      var word = this.mAddedWordStack.pop();
+      this.mInlineSpellChecker.removeWordFromDictionary(word);
+    }
+  },
+  canUndo : function()
+  {
+    // Return true if we have words on the stack
+    return (this.mAddedWordStack.length > 0);
+  },
   ignoreWord: function()
   {
     this.mInlineSpellChecker.ignoreWord(this.mMisspelling);
   }
 };
--- a/toolkit/content/tests/chrome/Makefile.in
+++ b/toolkit/content/tests/chrome/Makefile.in
@@ -139,16 +139,17 @@ include $(topsrcdir)/config/rules.mk
 		test_statusbar.xul \
 		test_timepicker.xul \
 		test_tree.xul \
 		test_tree_view.xul \
 		test_tree_single.xul \
 		test_textbox_emptytext.xul \
 		test_textbox_number.xul \
 		test_textbox_search.xul \
+		test_textbox_dictionary.xul\
 		test_toolbar.xul \
 		xul_selectcontrol.js \
 		test_popupincontent.xul \
 		test_panelfrommenu.xul \
 		test_hiddenitems.xul \
 		test_hiddenpaging.xul \
 		test_popup_tree.xul \
 		test_popup_keys.xul \
new file mode 100644
--- /dev/null
+++ b/toolkit/content/tests/chrome/test_textbox_dictionary.xul
@@ -0,0 +1,85 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>
+<!--
+  XUL Widget Test for textbox with placeholder
+  -->
+<window title="Textbox Add and Undo Add Dictionary Test" width="500" height="600"
+        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+
+  <hbox>
+    <textbox id="t1"  value="Hellop" oncontextmenu="runContextMenuTest()" spellcheck="true"/>
+  </hbox>
+
+  <!-- test results are displayed in the html:body -->
+  <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/>
+
+  <!-- test code goes here -->
+  <script type="application/javascript"><![CDATA[
+
+SimpleTest.waitForExplicitFinish();
+
+var textbox;
+var testNum;
+
+function bringUpContextMenu(element)
+{
+  synthesizeMouseAtCenter(element, { type: "contextmenu", button: 2});
+}
+
+function leftClickElement(element)
+{
+  synthesizeMouseAtCenter(element, { button: 0 });
+}
+
+function startTests() 
+{     
+  textbox = document.getElementById("t1");
+  textbox.focus();
+  testNum = 0;
+  
+  SimpleTest.executeSoon( function() {  bringUpContextMenu(textbox); });
+}
+
+function runContextMenuTest()
+{
+  SimpleTest.executeSoon( function() {
+    // The textbox has its children in an hbox XUL element, so get that first
+    var hbox = document.getAnonymousNodes(textbox).item(0);
+    
+    var contextMenu = document.getAnonymousElementByAttribute(hbox, "anonid", "input-box-contextmenu");
+   
+    switch(testNum)
+    {
+      case 0: // "Add to Dictionary" button
+        var addToDict = contextMenu.querySelector("[anonid=spell-add-to-dictionary]");
+        is(!addToDict.hidden, true, "Is Add to Dictionary visible?");
+        
+        addToDict.doCommand();
+        
+        contextMenu.hidePopup();
+        testNum++;
+        
+        SimpleTest.executeSoon( function() {bringUpContextMenu(textbox); }); // Bring up the menu again to invoke the next test
+        break;
+        
+      case 1: // "Undo Add to Dictionary" button
+        var undoAddDict = contextMenu.querySelector("[anonid=spell-undo-add-to-dictionary]");
+        is(!undoAddDict.hidden, true, "Is Undo Add to Dictioanry visible?");
+        
+        undoAddDict.doCommand();
+        
+        contextMenu.hidePopup();
+        SimpleTest.finish();
+        break;
+    }
+  });
+}
+
+SimpleTest.waitForFocus(startTests);
+
+  ]]></script>
+
+</window>
--- a/toolkit/content/widgets/textbox.xml
+++ b/toolkit/content/widgets/textbox.xml
@@ -510,16 +510,18 @@
                      onpopupshowing="if (document.commandDispatcher.focusedElement != this.parentNode.firstChild)
                                        this.parentNode.firstChild.focus();
                                      this.parentNode._doPopupItemEnablingSpell(this);"
                      onpopuphiding="this.parentNode._doPopupItemDisabling(this);"
                      oncommand="var cmd = event.originalTarget.getAttribute('cmd'); if(cmd) { this.parentNode.doCommand(cmd); event.stopPropagation(); }">
         <xul:menuitem label="&spellNoSuggestions.label;" anonid="spell-no-suggestions" disabled="true"/>
         <xul:menuitem label="&spellAddToDictionary.label;" accesskey="&spellAddToDictionary.accesskey;" anonid="spell-add-to-dictionary"
                       oncommand="this.parentNode.parentNode.spellCheckerUI.addToDictionary();"/>
+        <xul:menuitem label="&spellUndoAddToDictionary.label;" accesskey="&spellUndoAddToDictionary.accesskey;" anonid="spell-undo-add-to-dictionary"
+                      oncommand="this.parentNode.parentNode.spellCheckerUI.undoAddToDictionary();"/>
         <xul:menuseparator anonid="spell-suggestions-separator"/>
         <xul:menuitem label="&undoCmd.label;" accesskey="&undoCmd.accesskey;" cmd="cmd_undo"/>
         <xul:menuseparator/>
         <xul:menuitem label="&cutCmd.label;" accesskey="&cutCmd.accesskey;" cmd="cmd_cut"/>
         <xul:menuitem label="&copyCmd.label;" accesskey="&copyCmd.accesskey;" cmd="cmd_copy"/>
         <xul:menuitem label="&pasteCmd.label;" accesskey="&pasteCmd.accesskey;" cmd="cmd_paste"/>
         <xul:menuitem label="&deleteCmd.label;" accesskey="&deleteCmd.accesskey;" cmd="cmd_delete"/>
         <xul:menuseparator/>
@@ -575,29 +577,31 @@
         <body>
           <![CDATA[
             var spellui = this.spellCheckerUI;
             if (!spellui || !spellui.canSpellCheck) {
               this._setMenuItemVisibility("spell-no-suggestions", false);
               this._setMenuItemVisibility("spell-check-enabled", false);
               this._setMenuItemVisibility("spell-check-separator", false);
               this._setMenuItemVisibility("spell-add-to-dictionary", false);
+              this._setMenuItemVisibility("spell-undo-add-to-dictionary", false);
               this._setMenuItemVisibility("spell-suggestions-separator", false);
               this._setMenuItemVisibility("spell-dictionaries", false);
               return;
             }
 
             spellui.initFromEvent(document.popupRangeParent,
                                   document.popupRangeOffset);
 
             var enabled = spellui.enabled;
             this._enabledCheckbox.setAttribute("checked", enabled);
 
             var overMisspelling = spellui.overMisspelling;
             this._setMenuItemVisibility("spell-add-to-dictionary", overMisspelling);
+            this._setMenuItemVisibility("spell-undo-add-to-dictionary", spellui.canUndo());
             this._setMenuItemVisibility("spell-suggestions-separator", overMisspelling);
 
             // suggestion list
             var numsug = spellui.addSuggestionsToMenu(popupNode, this._suggestionsSeparator, 5);
             this._setMenuItemVisibility("spell-no-suggestions", overMisspelling && numsug == 0);
 
             // dictionary list
             var numdicts = spellui.addDictionaryListToMenu(this._dictionariesMenu, null);
--- a/toolkit/locales/en-US/chrome/global/textcontext.dtd
+++ b/toolkit/locales/en-US/chrome/global/textcontext.dtd
@@ -8,13 +8,15 @@
 <!ENTITY undoCmd.accesskey "u">
 <!ENTITY selectAllCmd.label "Select All">
 <!ENTITY selectAllCmd.accesskey "a">
 <!ENTITY deleteCmd.label "Delete">
 <!ENTITY deleteCmd.accesskey "d">
 
 <!ENTITY spellAddToDictionary.label "Add to Dictionary">
 <!ENTITY spellAddToDictionary.accesskey "o">
+<!ENTITY spellUndoAddToDictionary.label "Undo Add To Dictionary">
+<!ENTITY spellUndoAddToDictionary.accesskey "n">
 <!ENTITY spellCheckEnable.label "Check Spelling">
 <!ENTITY spellCheckEnable.accesskey "S">
 <!ENTITY spellNoSuggestions.label "(No Spelling Suggestions)">
 <!ENTITY spellDictionaries.label "Languages">
 <!ENTITY spellDictionaries.accesskey "l">
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/downloads/tests/unit/head_downloads.js
@@ -0,0 +1,5 @@
+Components.utils.import("resource://gre/modules/Services.jsm");
+
+do_register_cleanup(function() {
+  Services.obs.notifyObservers(null, "quit-application", null);
+});
--- a/toolkit/mozapps/downloads/tests/unit/xpcshell.ini
+++ b/toolkit/mozapps/downloads/tests/unit/xpcshell.ini
@@ -1,10 +1,10 @@
 [DEFAULT]
-head = 
+head = head_downloads.js
 tail = 
 
 [test_DownloadLastDir.js]
 [test_DownloadLastDirWithCPS.js]
 [test_DownloadPaths.js]
 [test_DownloadUtils.js]
 [test_lowMinutes.js]
 [test_privatebrowsing_downloadLastDir.js]
--- a/tools/profiler/Makefile.in
+++ b/tools/profiler/Makefile.in
@@ -78,35 +78,38 @@ EXTRA_JS_MODULES = \
   Profiler.jsm \
   $(NULL)
 
 ifneq (,$(filter Android Linux,$(OS_TARGET)))
 
 DEFINES += -DMOZ_ENABLE_PROFILER_SPS
 
 CPPSRCS += \
+  shared-libraries-linux.cc \
   platform-linux.cc \
   TableTicker.cpp \
   $(NULL)
 endif
 ifeq ($(OS_TARGET),Darwin)
 
 DEFINES += -DMOZ_ENABLE_PROFILER_SPS
 
 CPPSRCS += \
+  shared-libraries-macos.cc \
   platform-macos.cc \
   TableTicker.cpp \
   $(NULL)
 endif
 
 ifeq ($(OS_TARGET),WINNT)
 
 DEFINES += -DMOZ_ENABLE_PROFILER_SPS
 
 CPPSRCS += \
+  shared-libraries-win32.cc \
   platform-win32.cc \
   TableTicker.cpp \
   $(NULL)
 endif
 
 
 
 include $(topsrcdir)/config/rules.mk
--- a/tools/profiler/sps/TableTicker.cpp
+++ b/tools/profiler/sps/TableTicker.cpp
@@ -38,18 +38,27 @@
 
 #include <string>
 #include <stdio.h>
 #include "sps_sampler.h"
 #include "platform.h"
 #include "nsXULAppAPI.h"
 #include "nsThreadUtils.h"
 #include "prenv.h"
+#include "shared-libraries.h"
+#include "mozilla/StringBuilder.h"
+
+// we eventually want to make this runtime switchable
+//#define USE_BACKTRACE
+#ifdef USE_BACKTRACE
+#include <execinfo.h>
+#endif
 
 using std::string;
+using namespace mozilla;
 
 #ifdef XP_WIN
 #include <windows.h>
 #define getpid GetCurrentProcessId
 #else
 #include <unistd.h>
 #endif
 
@@ -66,16 +75,17 @@ using std::string;
 #define MAXPATHLEN 1024
 #endif
 #endif
 
 #if _MSC_VER
 #define snprintf _snprintf
 #endif
 
+
 mozilla::tls::key pkey_stack;
 mozilla::tls::key pkey_ticker;
 // We need to track whether we've been initialized otherwise
 // we end up using pkey_stack without initializing it.
 // Because pkey_stack is totally opaque to us we can't reuse
 // it as the flag itself.
 bool stack_key_initialized;
 
@@ -108,37 +118,41 @@ public:
 
   ProfileEntry(char aTagName, float aTagFloat)
     : mTagFloat(aTagFloat)
     , mLeafAddress(0)
     , mTagName(aTagName)
   { }
 
   string TagToString(Profile *profile);
-  void WriteTag(Profile *profile, FILE* stream);
 
 private:
   union {
     const char* mTagData;
     float mTagFloat;
+    Address mTagAddress;
   };
   Address mLeafAddress;
   char mTagName;
 };
 
 #define PROFILE_MAX_ENTRY 100000
 class Profile
 {
 public:
   Profile(int aEntrySize)
     : mWritePos(0)
     , mReadPos(0)
     , mEntrySize(aEntrySize)
   {
     mEntries = new ProfileEntry[mEntrySize];
+    mNeedsSharedLibraryInfo = false;
+#if defined(ENABLE_SPS_LEAF_DATA) || defined(USE_BACKTRACE)
+    mNeedsSharedLibraryInfo = true;
+#endif
   }
 
   ~Profile()
   {
     delete[] mEntries;
   }
 
   void addTag(ProfileEntry aTag)
@@ -148,65 +162,62 @@ public:
     mWritePos = (mWritePos + 1) % mEntrySize;
     if (mWritePos == mReadPos) {
       // Keep one slot open
       mEntries[mReadPos] = ProfileEntry();
       mReadPos = (mReadPos + 1) % mEntrySize;
     }
   }
 
-  void ToString(string* profile)
+  void ToString(StringBuilder &profile)
   {
-    // Can't be called from signal because
-    // get_maps calls non reentrant functions.
-#ifdef ENABLE_SPS_LEAF_DATA
-    mMaps = getmaps(getpid());
-#endif
+    if (mNeedsSharedLibraryInfo) {
+      // Can't be called from signal because
+      // getting the shared library information can call non-reentrant functions.
+      mSharedLibraryInfo = SharedLibraryInfo::GetInfoForSelf();
+    }
 
-    *profile = "";
     int oldReadPos = mReadPos;
     while (mReadPos != mWritePos) {
-      *profile += mEntries[mReadPos].TagToString(this);
+      profile.Append(mEntries[mReadPos].TagToString(this).c_str());
       mReadPos = (mReadPos + 1) % mEntrySize;
     }
     mReadPos = oldReadPos;
   }
 
   void WriteProfile(FILE* stream)
   {
-    // Can't be called from signal because
-    // get_maps calls non reentrant functions.
-#ifdef ENABLE_SPS_LEAF_DATA
-    mMaps = getmaps(getpid());
-#endif
+    if (mNeedsSharedLibraryInfo) {
+      // Can't be called from signal because
+      // getting the shared library information can call non-reentrant functions.
+      mSharedLibraryInfo = SharedLibraryInfo::GetInfoForSelf();
+    }
 
     int oldReadPos = mReadPos;
     while (mReadPos != mWritePos) {
-      mEntries[mReadPos].WriteTag(this, stream);
+      string tag = mEntries[mReadPos].TagToString(this);
+      fwrite(tag.data(), 1, tag.length(), stream);
       mReadPos = (mReadPos + 1) % mEntrySize;
     }
     mReadPos = oldReadPos;
   }
 
-#ifdef ENABLE_SPS_LEAF_DATA
-  MapInfo& getMap()
+  SharedLibraryInfo& getSharedLibraryInfo()
   {
-    return mMaps;
+    return mSharedLibraryInfo;
   }
-#endif
 private:
   // Circular buffer 'Keep One Slot Open' implementation
   // for simplicity
   ProfileEntry *mEntries;
   int mWritePos; // points to the next entry we will write to
   int mReadPos;  // points to the next entry we will read to
   int mEntrySize;
-#ifdef ENABLE_SPS_LEAF_DATA
-  MapInfo mMaps;
-#endif
+  bool mNeedsSharedLibraryInfo;
+  SharedLibraryInfo mSharedLibraryInfo;
 };
 
 class SaveProfileTask;
 
 class TableTicker: public Sampler {
  public:
   explicit TableTicker(int aInterval, int aEntrySize, Stack *aStack)
     : Sampler(aInterval, true)
@@ -298,69 +309,124 @@ void TableTicker::HandleSaveRequest()
   mSaveRequested = false;
 
   // TODO: Use use the ipc/chromium Tasks here to support processes
   // without XPCOM.
   nsCOMPtr<nsIRunnable> runnable = new SaveProfileTask();
   NS_DispatchToMainThread(runnable);
 }
 
+#ifdef USE_BACKTRACE
+static
+void doBacktrace(Profile &aProfile)
+{
+  void *array[100];
+  int count = backtrace (array, 100);
+
+  bool isSignal = true;
+#ifndef __i386__
+  // the test doesn't work for 64bit
+  isSignal = false;
+#endif
+  for (int i = count-1; i >= 0; i--) {
+    if( isSignal ) {
+      if( (intptr_t)array[i] == -1 ) { // signal frames have addresses of -1?
+        isSignal = false;
+      }
+      continue;
+    }
+    aProfile.addTag(ProfileEntry('l', (const char*)array[i]));
+  }
+  aProfile.addTag(ProfileEntry('s', "XRE_Main", 0));
+}
+#endif
+
+static
+void doSampleStackTrace(Stack *aStack, Profile &aProfile, TickSample *sample)
+{
+  // Sample
+  // 's' tag denotes the start of a sample block
+  // followed by 0 or more 'c' tags.
+  for (int i = 0; i < aStack->mStackPointer; i++) {
+    if (i == 0) {
+      Address pc = 0;
+      if (sample) {
+        pc = sample->pc;
+      }
+      aProfile.addTag(ProfileEntry('s', aStack->mStack[i], pc));
+    } else {
+      aProfile.addTag(ProfileEntry('c', aStack->mStack[i]));
+    }
+  }
+}
 
 void TableTicker::Tick(TickSample* sample)
 {
   // Marker(s) come before the sample
   int i = 0;
   const char *marker = mStack->getMarker(i++);
   for (int i = 0; marker != NULL; i++) {
     mProfile.addTag(ProfileEntry('m', marker));
     marker = mStack->getMarker(i++);
   }
   mStack->mQueueClearMarker = true;
 
-  // Sample
-  // 's' tag denotes the start of a sample block
-  // followed by 0 or more 'c' tags.
-  for (int i = 0; i < mStack->mStackPointer; i++) {
-    if (i == 0) {
-      Address pc = 0;
-      if (sample) {
-        pc = sample->pc;
-      }
-      mProfile.addTag(ProfileEntry('s', mStack->mStack[i], pc));
-    } else {
-      mProfile.addTag(ProfileEntry('c', mStack->mStack[i]));
-    }
-  }
+#ifdef USE_BACKTRACE
+  doBacktrace(mProfile);
+#else
+  doSampleStackTrace(mStack, mProfile, sample);
+#endif
 
   if (!sLastTracerEvent.IsNull()) {
     TimeDuration delta = sample->timestamp - sLastTracerEvent;
     mProfile.addTag(ProfileEntry('r', delta.ToMilliseconds()));
   }
 }
 
 string ProfileEntry::TagToString(Profile *profile)
 {
   string tag = "";
   if (mTagName == 'r') {
     char buff[50];
     snprintf(buff, 50, "%-40f", mTagFloat);
     tag += string(1, mTagName) + string("-") + string(buff) + string("\n");
+  } else if (mTagName == 'l') {
+    bool found = false;
+    char tagBuff[1024];
+    SharedLibraryInfo& shlibInfo = profile->getSharedLibraryInfo();
+    Address pc = mTagAddress;
+    // TODO Use binary sort (STL)
+    for (size_t i = 0; i < shlibInfo.GetSize(); i++) {
+      SharedLibrary &e = shlibInfo.GetEntry(i);
+      if (pc > (Address)e.GetStart() && pc < (Address)e.GetEnd()) {
+        if (e.GetName()) {
+          found = true;
+          snprintf(tagBuff, 1024, "l-%s@%p\n", e.GetName(), pc - e.GetStart());
+          tag += string(tagBuff);
+          break;
+        }
+      }
+    }
+    if (!found) {
+      snprintf(tagBuff, 1024, "l-???@%p\n", pc);
+      tag += string(tagBuff);
+    }
   } else {
     tag += string(1, mTagName) + string("-") + string(mTagData) + string("\n");
   }
 
 #ifdef ENABLE_SPS_LEAF_DATA
   if (mLeafAddress) {
     bool found = false;
     char tagBuff[1024];
-    MapInfo& maps = profile->getMap();
+    SharedLibraryInfo& shlibInfo = profile->getSharedLibraryInfo();
     unsigned long pc = (unsigned long)mLeafAddress;
     // TODO Use binary sort (STL)
-    for (size_t i = 0; i < maps.GetSize(); i++) {
-      MapEntry &e = maps.GetEntry(i);
+    for (size_t i = 0; i < shlibInfo.GetSize(); i++) {
+      SharedLibrary &e = shlibInfo.GetEntry(i);
       if (pc > e.GetStart() && pc < e.GetEnd()) {
         if (e.GetName()) {
           found = true;
           snprintf(tagBuff, 1024, "l-%900s@%llu\n", e.GetName(), pc - e.GetStart());
           tag += string(tagBuff);
           break;
         }
       }
@@ -369,43 +435,16 @@ string ProfileEntry::TagToString(Profile
       snprintf(tagBuff, 1024, "l-???@%llu\n", pc);
       tag += string(tagBuff);
     }
   }
 #endif
   return tag;
 }
 
-void ProfileEntry::WriteTag(Profile *profile, FILE *stream)
-{
-  fprintf(stream, "%c-%s\n", mTagName, mTagData);
-
-#ifdef ENABLE_SPS_LEAF_DATA
-  if (mLeafAddress) {
-    bool found = false;
-    MapInfo& maps = profile->getMap();
-    unsigned long pc = (unsigned long)mLeafAddress;
-    // TODO Use binary sort (STL)
-    for (size_t i = 0; i < maps.GetSize(); i++) {
-      MapEntry &e = maps.GetEntry(i);
-      if (pc > e.GetStart() && pc < e.GetEnd()) {
-        if (e.GetName()) {
-          found = true;
-          fprintf(stream, "l-%s@%li\n", e.GetName(), pc - e.GetStart());
-          break;
-        }
-      }
-    }
-    if (!found) {
-      fprintf(stream, "l-???@%li\n", pc);
-    }
-  }
-#endif
-}
-
 #define PROFILE_DEFAULT_ENTRY 100000
 #define PROFILE_DEFAULT_INTERVAL 10
 
 void mozilla_sampler_init()
 {
   // TODO linux port: Use TLS with ifdefs
   if (!mozilla::tls::create(&pkey_stack) ||
       !mozilla::tls::create(&pkey_ticker)) {
@@ -449,21 +488,21 @@ void mozilla_sampler_save() {
 }
 
 char* mozilla_sampler_get_profile() {
   TableTicker *t = mozilla::tls::get<TableTicker>(pkey_ticker);
   if (!t) {
     return NULL;
   }
 
-  string profile;
-  t->GetProfile()->ToString(&profile);
+  StringBuilder profile;
+  t->GetProfile()->ToString(profile);
 
-  char *rtn = (char*)malloc( (strlen(profile.c_str())+1) * sizeof(char) );
-  strcpy(rtn, profile.c_str());
+  char *rtn = (char*)malloc( (profile.Length()+1) * sizeof(char) );
+  strcpy(rtn, profile.Buffer());
   return rtn;
 }
 
 // Values are only honored on the first start
 void mozilla_sampler_start(int aProfileEntries, int aInterval)
 {
   Stack *stack = mozilla::tls::get<Stack>(pkey_stack);
   if (!stack) {
--- a/tools/profiler/sps/platform-linux.cc
+++ b/tools/profiler/sps/platform-linux.cc
@@ -34,87 +34,27 @@
 #include <strings.h>    // index
 #include <errno.h>
 #include <stdarg.h>
 #include "platform.h"
 
 #include <string.h>
 #include <stdio.h>
 
-// Real time signals are not supported on android.
-// This behaves as a standard signal.
-#define SIGNAL_SAVE_PROFILE 42
-
-#define PATH_MAX_TOSTRING(x) #x
-#define PATH_MAX_STRING(x) PATH_MAX_TOSTRING(x)
+#define SIGNAL_SAVE_PROFILE SIGUSR2
 
 #if defined(__GLIBC__)
 // glibc doesn't implement gettid(2).
 #include <sys/syscall.h>
 pid_t gettid()
 {
   return (pid_t) syscall(SYS_gettid);
 }
 #endif
 
-#ifdef ENABLE_SPS_LEAF_DATA
-/* a crapy version of getline, because it's not included in bionic */
-static ssize_t getline(char **lineptr, size_t *n, FILE *stream)
-{
- char *ret;
- if (!*lineptr) {
-   *lineptr = (char*)malloc(4096);
- }
- ret = fgets(*lineptr, 4096, stream);
- if (!ret)
-   return 0;
- return strlen(*lineptr);
-}
-
-MapInfo getmaps(pid_t pid)
-{
- MapInfo info;
- char path[PATH_MAX];
- snprintf(path, PATH_MAX, "/proc/%d/maps", pid);
- FILE *maps = fopen(path, "r");
- char *line = NULL;
- int count = 0;
- size_t line_size = 0;
- while (maps && getline (&line, &line_size, maps) > 0) {
-   int ret;
-   //XXX: needs input sanitizing
-   unsigned long start;
-   unsigned long end;
-   char perm[6] = "";
-   unsigned long offset;
-   char name[PATH_MAX] = "";
-   ret = sscanf(line,
-                "%lx-%lx %6s %lx %*s %*x %" PATH_MAX_STRING(PATH_MAX) "s\n",
-                &start, &end, perm, &offset, name);
-   if (!strchr(perm, 'x')) {
-     // Ignore non executable entries
-     continue;
-   }
-   if (ret != 5 && ret != 4) {
-     LOG("Get maps line failed");
-     continue;
-   }
-   MapEntry entry(start, end, offset, name);
-   info.AddMapEntry(entry);
-   if (count > 10000) {
-     LOG("Get maps failed");
-     break;
-   }
-   count++;
- }
- free(line);
- return info;
-}
-#endif
-
 static Sampler* sActiveSampler = NULL;
 
 
 #if !defined(__GLIBC__) && (defined(__arm__) || defined(__thumb__))
 // Android runs a fairly new Linux kernel, so signal info is there,
 // but the C library doesn't have the structs defined.
 
 struct sigcontext {
@@ -188,36 +128,48 @@ static void ProfilerSignalHandler(int si
     UNIMPLEMENTED();
 #endif
     sample->timestamp = mozilla::TimeStamp::Now();
   }
 #endif
   sActiveSampler->Tick(sample);
 }
 
+#ifndef XP_MACOSX
 void tgkill(pid_t tgid, pid_t tid, int signalno) {
   syscall(SYS_tgkill, tgid, tid, signalno);
 }
+#endif
 
 class Sampler::PlatformData : public Malloced {
  public:
   explicit PlatformData(Sampler* sampler)
       : sampler_(sampler),
         signal_handler_installed_(false),
         vm_tgid_(getpid()),
+#ifndef XP_MACOSX
         vm_tid_(gettid()),
-        signal_sender_launched_(false) {
+#endif
+        signal_sender_launched_(false)
+#ifdef XP_MACOSX
+        , signal_receiver_(pthread_self())
+#endif
+  {
   }
 
   void SignalSender() {
     while (sampler_->IsActive()) {
       sampler_->HandleSaveRequest();
 
+#ifdef XP_MACOSX
+      pthread_kill(signal_receiver_, SIGPROF);
+#else
       // Glibc doesn't provide a wrapper for tgkill(2).
       tgkill(vm_tgid_, vm_tid_, SIGPROF);
+#endif
       // Convert ms to us and subtract 100 us to compensate delays
       // occuring during signal delivery.
 
       // TODO measure and confirm this.
       const useconds_t interval = sampler_->interval_ * 1000 - 100;
       //int result = usleep(interval);
       usleep(interval);
       // sometimes usleep is defined as returning void
@@ -235,16 +187,19 @@ class Sampler::PlatformData : public Mal
   Sampler* sampler_;
   bool signal_handler_installed_;
   struct sigaction old_sigprof_signal_handler_;
   struct sigaction old_sigsave_signal_handler_;
   pid_t vm_tgid_;
   pid_t vm_tid_;
   bool signal_sender_launched_;
   pthread_t signal_sender_thread_;
+#ifdef XP_MACOSX
+  pthread_t signal_receiver_;
+#endif
 };
 
 
 static void* SenderEntry(void* arg) {
   Sampler::PlatformData* data =
       reinterpret_cast<Sampler::PlatformData*>(arg);
   data->SignalSender();
   return 0;
--- a/tools/profiler/sps/platform.h
+++ b/tools/profiler/sps/platform.h
@@ -19,73 +19,16 @@
 #define ENABLE_SPS_LEAF_DATA
 #define LOG(text) __android_log_print(ANDROID_LOG_ERROR, "profiler", "%s", text);
 #else
 #define LOG(text) printf("Profiler: %s\n", text)
 #endif
 
 typedef uint8_t* Address;
 
-class MapEntry {
-public:
-  MapEntry(unsigned long aStart, unsigned long aEnd, unsigned long aOffset, char *aName)
-    : mStart(aStart)
-    , mEnd(aEnd)
-    , mOffset(aOffset)
-    , mName(strdup(aName))
-  {}
-
-  MapEntry(const MapEntry& aEntry)
-    : mStart(aEntry.mStart)
-    , mEnd(aEntry.mEnd)
-    , mOffset(aEntry.mOffset)
-    , mName(strdup(aEntry.mName))
-  {}
-
-  ~MapEntry()
-  {
-    free(mName);
-  }
-
-  unsigned long GetStart() { return mStart; }
-  unsigned long GetEnd() { return mEnd; }
-  char* GetName() { return mName; }
-
-private:
-  unsigned long mStart;
-  unsigned long mEnd;
-  unsigned long mOffset;
-  char *mName;
-};
-
-class MapInfo {
-public:
-  MapInfo() {}
-
-  void AddMapEntry(MapEntry entry)
-  {
-    mEntries.push_back(entry);
-  }
-
-  MapEntry& GetEntry(size_t i)
-  {
-    return mEntries[i];
-  }
-
-  size_t GetSize()
-  {
-    return mEntries.size();
-  }
-private:
-  std::vector<MapEntry> mEntries;
-};
-
-#ifdef ENABLE_SPS_LEAF_DATA
-struct MapInfo getmaps(pid_t pid);
-#endif
 // ----------------------------------------------------------------------------
 // Mutex
 //
 // Mutexes are used for serializing access to non-reentrant sections of code.
 // The implementations of mutex should allow for nested/recursive locking.
 
 class Mutex {
  public:
new file mode 100644
--- /dev/null
+++ b/tools/profiler/sps/shared-libraries-linux.cc
@@ -0,0 +1,65 @@
+#define PATH_MAX_TOSTRING(x) #x
+#define PATH_MAX_STRING(x) PATH_MAX_TOSTRING(x)
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <limits.h>
+#include <unistd.h>
+#include "platform.h"
+#include "shared-libraries.h"
+
+#ifndef __GLIBC__
+/* a crapy version of getline, because it's not included in bionic */
+static ssize_t getline(char **lineptr, size_t *n, FILE *stream)
+{
+ char *ret;
+ if (!*lineptr) {
+   *lineptr = (char*)malloc(4096);
+ }
+ ret = fgets(*lineptr, 4096, stream);
+ if (!ret)
+   return 0;
+ return strlen(*lineptr);
+}
+#endif
+
+SharedLibraryInfo SharedLibraryInfo::GetInfoForSelf()
+{
+  pid_t pid = getpid();
+  SharedLibraryInfo info;
+  char path[PATH_MAX];
+  snprintf(path, PATH_MAX, "/proc/%d/maps", pid);
+  FILE *maps = fopen(path, "r");
+  char *line = NULL;
+  int count = 0;
+  size_t line_size = 0;
+  while (maps && getline (&line, &line_size, maps) > 0) {
+    int ret;
+    //XXX: needs input sanitizing
+    unsigned long start;
+    unsigned long end;
+    char perm[6] = "";
+    unsigned long offset;
+    char name[PATH_MAX] = "";
+    ret = sscanf(line,
+                 "%lx-%lx %6s %lx %*s %*x %" PATH_MAX_STRING(PATH_MAX) "s\n",
+                 &start, &end, perm, &offset, name);
+    if (!strchr(perm, 'x')) {
+      // Ignore non executable entries
+      continue;
+    }
+    if (ret != 5 && ret != 4) {
+      LOG("Get maps line failed");
+      continue;
+    }
+    SharedLibrary shlib(start, end, offset, name);
+    info.AddSharedLibrary(shlib);
+    if (count > 10000) {
+      LOG("Get maps failed");
+      break;
+    }
+    count++;
+  }
+  free(line);
+  return info;
+}
new file mode 100644
--- /dev/null
+++ b/tools/profiler/sps/shared-libraries-macos.cc
@@ -0,0 +1,147 @@
+/* -*- 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 mozilla.org code.
+ *
+ * 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):
+ *   Benoit Girard <bgirard@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 ***** */
+
+#include <AvailabilityMacros.h>
+#include <mach-o/loader.h>
+#include <mach-o/dyld_images.h>
+#include <mach/task_info.h>
+#include <mach/task.h>
+#include <mach/mach_init.h>
+#include <mach/mach_traps.h>
+#include <string.h>
+#include <stdlib.h>
+#include <vector>
+
+#include "shared-libraries.h"
+
+#ifndef MAC_OS_X_VERSION_10_6
+#define MAC_OS_X_VERSION_10_6 1060
+#endif
+
+#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_6
+// borrowed from Breakpad
+// Fallback declarations for TASK_DYLD_INFO and friends, introduced in
+// <mach/task_info.h> in the Mac OS X 10.6 SDK.
+#define TASK_DYLD_INFO 17
+struct task_dyld_info {
+    mach_vm_address_t all_image_info_addr;
+    mach_vm_size_t all_image_info_size;
+  };
+typedef struct task_dyld_info task_dyld_info_data_t;
+typedef struct task_dyld_info *task_dyld_info_t;
+#define TASK_DYLD_INFO_COUNT (sizeof(task_dyld_info_data_t) / sizeof(natural_t))
+
+#endif
+
+// Architecture specific abstraction.
+#ifdef __i386__
+typedef mach_header platform_mach_header;
+typedef segment_command mach_segment_command_type;
+#define MACHO_MAGIC_NUMBER MH_MAGIC
+#define CMD_SEGMENT LC_SEGMENT
+#define seg_size uint32_t
+#else
+typedef mach_header_64 platform_mach_header;
+typedef segment_command_64 mach_segment_command_type;
+#define MACHO_MAGIC_NUMBER MH_MAGIC_64
+#define CMD_SEGMENT LC_SEGMENT_64
+#define seg_size uint64_t
+#endif
+
+static
+void addSharedLibrary(const platform_mach_header* header, char *name, SharedLibraryInfo &info) {
+  const struct load_command *cmd =
+    reinterpret_cast<const struct load_command *>(header + 1);
+
+  seg_size size;
+  // Find the cmd segment in the macho image. It will contain the offset we care about.
+  for (unsigned int i = 0; cmd && (i < header->ncmds); ++i) {
+    if (cmd->cmd == CMD_SEGMENT) {
+      const mach_segment_command_type *seg =
+        reinterpret_cast<const mach_segment_command_type *>(cmd);
+
+      if (!strcmp(seg->segname, "__TEXT")) {
+        size = seg->vmsize;
+        unsigned long long start = reinterpret_cast<unsigned long long>(header);
+        info.AddSharedLibrary(SharedLibrary(start, start+seg->vmsize, seg->vmsize, name));
+        return;
+      }
+    }
+
+    cmd = reinterpret_cast<const struct load_command *>
+      (reinterpret_cast<const char *>(cmd) + cmd->cmdsize);
+  }
+}
+
+// Use dyld to inspect the macho image information. We can build the SharedLibraryEntry structure
+// giving us roughtly the same info as /proc/PID/maps in Linux.
+SharedLibraryInfo SharedLibraryInfo::GetInfoForSelf()
+{
+  SharedLibraryInfo sharedLibraryInfo;
+
+  task_dyld_info_data_t task_dyld_info;
+  mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT;
+  if (task_info(mach_task_self (), TASK_DYLD_INFO, (task_info_t)&task_dyld_info,
+                &count) != KERN_SUCCESS) {
+    return sharedLibraryInfo;
+  }
+
+  struct dyld_all_image_infos* aii = (struct dyld_all_image_infos*)task_dyld_info.all_image_info_addr;
+  size_t infoCount = aii->infoArrayCount;
+
+  // Iterate through all dyld images (loaded libraries) to get their names
+  // and offests.
+  for (size_t i = 0; i < infoCount; ++i) {
+    const dyld_image_info *info = &aii->infoArray[i];
+
+    // If the magic number doesn't match then go no further
+    // since we're not pointing to where we think we are.
+    if (info->imageLoadAddress->magic != MACHO_MAGIC_NUMBER) {
+      continue;
+    }
+
+    const platform_mach_header* header =
+      reinterpret_cast<const platform_mach_header*>(info->imageLoadAddress);
+
+    // Add the entry for this image.
+    addSharedLibrary(header, (char*)info->imageFilePath, sharedLibraryInfo);
+
+  }
+  return sharedLibraryInfo;
+}
+
new file mode 100644
--- /dev/null
+++ b/tools/profiler/sps/shared-libraries-win32.cc
@@ -0,0 +1,60 @@
+/* -*- 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 mozilla.org code.
+ *
+ * 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):
+ *  Jeff Muizelaar <jmuizelaar@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 ***** */
+
+#include <windows.h>
+#include <tlhelp32.h>
+
+#include "shared-libraries.h"
+
+SharedLibraryInfo SharedLibraryInfo::GetInfoForSelf()
+{
+  SharedLibraryInfo sharedLibraryInfo;
+
+  /*
+  HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, GetCurrentProcessId());
+
+  MODULEENTRY32 module;
+  Module32First(snap, &module);
+  do {
+    // process module
+  } while (Module32Next(snap, &module));
+  */
+
+  return sharedLibraryInfo;
+}
+
new file mode 100644
--- /dev/null
+++ b/tools/profiler/sps/shared-libraries.h
@@ -0,0 +1,108 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim:set ts=4 sw=4 sts=4 et cindent: */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla code.
+ *
+ * The Initial Developer of the Original Code is the Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *  Jeff Muizelaar <jmuizelaar@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 ***** */
+
+#include <vector>
+#include <string.h>
+#include <stdlib.h>
+#include <mozilla/StdInt.h>
+
+class SharedLibrary {
+public:
+  SharedLibrary(unsigned long aStart, unsigned long aEnd, unsigned long aOffset, char *aName)
+    : mStart(aStart)
+    , mEnd(aEnd)
+    , mOffset(aOffset)
+    , mName(strdup(aName))
+  {}
+
+  SharedLibrary(const SharedLibrary& aEntry)
+    : mStart(aEntry.mStart)
+    , mEnd(aEntry.mEnd)
+    , mOffset(aEntry.mOffset)
+    , mName(strdup(aEntry.mName))
+  {}
+
+  SharedLibrary& operator=(const SharedLibrary& aEntry)
+  {
+    mStart = aEntry.mStart;
+    mEnd = aEntry.mEnd;
+    mOffset = aEntry.mOffset;
+    mName = strdup(aEntry.mName);
+    return *this;
+  }
+
+  ~SharedLibrary()
+  {
+    free(mName);
+  }
+
+  uintptr_t GetStart() { return mStart; }
+  uintptr_t GetEnd() { return mEnd; }
+  char* GetName() { return mName; }
+
+private:
+  explicit SharedLibrary() {}
+
+  uintptr_t mStart;
+  uintptr_t mEnd;
+  uintptr_t mOffset;
+  char *mName;
+};
+
+class SharedLibraryInfo {
+public:
+  static SharedLibraryInfo GetInfoForSelf();
+  SharedLibraryInfo() {}
+
+  void AddSharedLibrary(SharedLibrary entry)
+  {
+    mEntries.push_back(entry);
+  }
+
+  SharedLibrary& GetEntry(size_t i)
+  {
+    return mEntries[i];
+  }
+
+  size_t GetSize()
+  {
+    return mEntries.size();
+  }
+private:
+  std::vector<SharedLibrary> mEntries;
+};
--- a/widget/src/android/AndroidJavaWrappers.h
+++ b/widget/src/android/AndroidJavaWrappers.h
@@ -527,16 +527,17 @@ public:
         LOAD_URI = 12,
         SURFACE_CREATED = 13,
         SURFACE_DESTROYED = 14,
         GECKO_EVENT_SYNC = 15,
         FORCED_RESIZE = 16,
         ACTIVITY_START = 17,
         BROADCAST = 19,
         VIEWPORT = 20,
+        EXPOSE = 21,
         dummy_java_enum_list_end
     };
 
     enum {
         IME_COMPOSITION_END = 0,
         IME_COMPOSITION_BEGIN = 1,
         IME_SET_TEXT = 2,
         IME_GET_TEXT = 3,
--- a/widget/src/android/nsIAndroidBridge.idl
+++ b/widget/src/android/nsIAndroidBridge.idl
@@ -1,13 +1,19 @@
 #include "nsISupports.idl"
 
-[scriptable, function, uuid(9feed1e5-bb90-4663-b70a-e03cb27a9e8b)]
+[scriptable, uuid(c60bf6cf-5e31-4bf8-a1f0-a82c061d65a8)]
 interface nsIAndroidDrawMetadataProvider : nsISupports {
   AString getDrawMetadata();
+
+  /*
+   * Returns true if drawing should be allowed or false if it should be suppressed (during page
+   * transitions).
+   */
+  boolean drawingAllowed();
 };
 
 [scriptable, uuid(7dd8441a-4f38-49b2-bd90-da69d02a96cf)]
 interface nsIAndroidBridge : nsISupports
 {
   AString handleGeckoMessage(in AString message);
   void setDrawMetadataProvider(in nsIAndroidDrawMetadataProvider provider);
 };
--- a/widget/src/android/nsWindow.cpp
+++ b/widget/src/android/nsWindow.cpp
@@ -973,16 +973,17 @@ nsWindow::OnGlobalAndroidEvent(AndroidGe
 
         case AndroidGeckoEvent::KEY_EVENT:
             win->UserActivity();
             if (win->mFocus)
                 win->mFocus->OnKeyEvent(ae);
             break;
 
         case AndroidGeckoEvent::DRAW:
+        case AndroidGeckoEvent::EXPOSE:
             win->OnDraw(ae);
             break;
 
         case AndroidGeckoEvent::IME_EVENT:
             win->UserActivity();
             if (win->mFocus) {
                 win->mFocus->OnIMEEvent(ae);
             } else {
@@ -1163,20 +1164,42 @@ nsWindow::OnDraw(AndroidGeckoEvent *ae)
     }
 
     AndroidBridge::AutoLocalJNIFrame jniFrame;
 #ifdef MOZ_JAVA_COMPOSITOR
     // We haven't been given a window-size yet, so do nothing
     if (gAndroidBounds.width <= 0 || gAndroidBounds.height <= 0)
         return;
 
+    nsCOMPtr<nsIAndroidDrawMetadataProvider> metadataProvider =
+        AndroidBridge::Bridge()->GetDrawMetadataProvider();
+
+    /*
+     * If this is a DRAW event (not an EXPOSE event), check to see whether browser.js wants us to
+     * draw. This will be false during page transitions, in which case we immediately bail out.
+     */
+
+    bool shouldDraw = true;
+    if (metadataProvider && ae->Type() == AndroidGeckoEvent::DRAW) {
+        metadataProvider->DrawingAllowed(&shouldDraw);
+    }
+    if (!shouldDraw) {
+        return;
+    }
+
     AndroidGeckoSoftwareLayerClient &client =
         AndroidBridge::Bridge()->GetSoftwareLayerClient();
     client.BeginDrawing(gAndroidBounds.width, gAndroidBounds.height);
 
+    // Redraw the entire tile on an EXPOSE event. Otherwise (on a DRAW event), redraw only the
+    // portion specified by the event.
+    nsIntRect rect(0, 0, gAndroidBounds.width, gAndroidBounds.height);
+    if (ae->Type() == AndroidGeckoEvent::DRAW)
+        rect = ae->Rect();
+
     nsAutoString metadata;
     unsigned char *bits = NULL;
     if (sHasDirectTexture) {
       if ((sDirectTexture->Width() != gAndroidBounds.width ||
            sDirectTexture->Height() != gAndroidBounds.height) &&
           gAndroidBounds.width != 0 && gAndroidBounds.height != 0) {
         sDirectTexture->Reallocate(gAndroidBounds.width, gAndroidBounds.height);
       }
@@ -1199,33 +1222,30 @@ nsWindow::OnDraw(AndroidGeckoEvent *ae)
                                 gfxASurface::ImageFormatRGB16_565);
         if (targetSurface->CairoStatus()) {
             ALOG("### Failed to create a valid surface from the bitmap");
         } else {
             if (sHasDirectTexture) {
               // XXX: lock only the dirty rect above and pass it in here
               DrawTo(targetSurface);
             } else {
-              DrawTo(targetSurface, ae->Rect());
+              DrawTo(targetSurface, rect);
             }
 
-            {
-                nsCOMPtr<nsIAndroidDrawMetadataProvider> metadataProvider =
-                    AndroidBridge::Bridge()->GetDrawMetadataProvider();
-                if (metadataProvider)
-                    metadataProvider->GetDrawMetadata(metadata);
+            if (metadataProvider) {
+                metadataProvider->GetDrawMetadata(metadata);
             }
         }
         if (sHasDirectTexture) {
           sDirectTexture->Unlock();
         } else {
           client.UnlockBuffer();
         }
     }
-    client.EndDrawing(ae->Rect(), metadata);
+    client.EndDrawing(rect, metadata);
     return;
 #endif
 
     if (!sSurfaceExists) {
         return;
     }
 
     AndroidGeckoSurfaceView& sview(AndroidBridge::Bridge()->SurfaceView());
--- a/widget/src/gonk/nsAppShell.cpp
+++ b/widget/src/gonk/nsAppShell.cpp
@@ -35,32 +35,35 @@
  * 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 ***** */
 
 #define _GNU_SOURCE
 
 #include <dirent.h>
+#include <errno.h>
 #include <fcntl.h>
 #include <linux/input.h>
 #include <signal.h>
 #include <sys/epoll.h>
 #include <sys/ioctl.h>
-#include <sys/types.h>
 #include <sys/param.h>
 #include <sys/stat.h>
+#include <sys/types.h>
 #include <unistd.h>
 
+#include "nscore.h"
+#include "mozilla/FileUtils.h"
+#include "mozilla/Services.h"
 #include "nsAppShell.h"
 #include "nsGkAtoms.h"
 #include "nsGUIEvent.h"
+#include "nsIObserverService.h"
 #include "nsWindow.h"
-#include "nsIObserverService.h"
-#include "mozilla/Services.h"
 
 #include "android/log.h"
 
 #ifndef ABS_MT_TOUCH_MAJOR
 // Taken from include/linux/input.h
 // XXX update the bionic input.h so we don't have to do this!
 #define ABS_X			0x00
 #define ABS_Y			0x01
@@ -74,17 +77,25 @@
 #define ABS_MT_POSITION_Y       0x36    /* Center Y ellipse position */
 #define ABS_MT_TOOL_TYPE        0x37    /* Type of touching device */
 #define ABS_MT_BLOB_ID          0x38    /* Group a set of packets as a blob */
 #define ABS_MT_TRACKING_ID      0x39    /* Unique ID of initiated contact */
 #define ABS_MT_PRESSURE         0x3a    /* Pressure on contact area */
 #define SYN_MT_REPORT           2
 #endif
 
-#define LOG(args...)  __android_log_print(ANDROID_LOG_INFO, "Gonk" , ## args)
+#define LOG(args...)                                            \
+    __android_log_print(ANDROID_LOG_INFO, "Gonk" , ## args)
+#ifdef VERBOSE_LOG_ENABLED
+# define VERBOSE_LOG(args...)                           \
+    __android_log_print(ANDROID_LOG_INFO, "Gonk" , ## args)
+#else
+# define VERBOSE_LOG(args...)                   \
+    (void)0
+#endif
 
 using namespace mozilla;
 
 bool gDrawRequest = false;
 static nsAppShell *gAppShell = NULL;
 static int epollfd = 0;
 static int signalfds[2] = {0};
 
@@ -114,34 +125,33 @@ pipeHandler(int fd, FdHandler *data)
 
 static
 PRUint64 timevalToMS(const struct timeval &time)
 {
     return time.tv_sec * 1000 + time.tv_usec / 1000;
 }
 
 static void
-sendMouseEvent(PRUint32 msg, struct timeval *time, int x, int y)
+sendMouseEvent(PRUint32 msg, struct timeval& time, int x, int y)
 {
     nsMouseEvent event(true, msg, NULL,
                        nsMouseEvent::eReal, nsMouseEvent::eNormal);
 
     event.refPoint.x = x;
     event.refPoint.y = y;
-    event.time = timevalToMS(*time);
+    event.time = timevalToMS(time);
     event.isShift = false;
     event.isControl = false;
     event.isMeta = false;
     event.isAlt = false;
     event.button = nsMouseEvent::eLeftButton;
     if (msg != NS_MOUSE_MOVE)
         event.clickCount = 1;
 
     nsWindow::DispatchInputEvent(event);
-    //LOG("Dispatched type %d at %dx%d", msg, x, y);
 }
 
 static nsEventStatus
 sendKeyEventWithMsg(PRUint32 keyCode,
                     PRUint32 msg,
                     const timeval &time,
                     PRUint32 flags)
 {
@@ -168,66 +178,184 @@ static void
 sendSpecialKeyEvent(nsIAtom *command, const timeval &time)
 {
     nsCommandEvent event(true, nsGkAtoms::onAppCommand, command, NULL);
     event.time = timevalToMS(time);
     nsWindow::DispatchInputEvent(event);
 }
 
 static void
+maybeSendKeyEvent(int keyCode, bool pressed, const timeval& time)
+{
+    switch (keyCode) {
+    case KEY_BACK:
+        sendKeyEvent(NS_VK_ESCAPE, pressed, time);
+        break;
+    case KEY_MENU:
+        if (!pressed)
+            sendSpecialKeyEvent(nsGkAtoms::Menu, time);
+        break;
+    case KEY_SEARCH:
+        if (pressed)
+            sendSpecialKeyEvent(nsGkAtoms::Search, time);
+        break;
+    case KEY_HOME:
+        sendKeyEvent(NS_VK_HOME, pressed, time);
+        break;
+    case KEY_POWER:
+        sendKeyEvent(NS_VK_SLEEP, pressed, time);
+        break;
+    case KEY_VOLUMEUP:
+        if (pressed)
+            sendSpecialKeyEvent(nsGkAtoms::VolumeUp, time);
+        break;
+    case KEY_VOLUMEDOWN:
+        if (pressed)
+            sendSpecialKeyEvent(nsGkAtoms::VolumeDown, time);
+        break;
+    default:
+        VERBOSE_LOG("Got unknown key event code. type 0x%04x code 0x%04x value %d",
+                    keyCode, pressed);
+    }
+}
+
+static void
 maybeSendKeyEvent(const input_event& e)
 {
     if (e.type != EV_KEY) {
-        LOG("Got unknown key event type. type 0x%04x code 0x%04x value %d",
+        VERBOSE_LOG("Got unknown key event type. type 0x%04x code 0x%04x value %d",
             e.type, e.code, e.value);
         return;
     }
 
     if (e.value != 0 && e.value != 1) {
-        LOG("Got unknown key event value. type 0x%04x code 0x%04x value %d",
+        VERBOSE_LOG("Got unknown key event value. type 0x%04x code 0x%04x value %d",
             e.type, e.code, e.value);
         return;
     }
 
     bool pressed = e.value == 1;
-    switch (e.code) {
-    case KEY_BACK:
-        sendKeyEvent(NS_VK_ESCAPE, pressed, e.time);
-        break;
-    case KEY_MENU:
-        if (!pressed)
-            sendSpecialKeyEvent(nsGkAtoms::Menu, e.time);
-        break;
-    case KEY_SEARCH:
-        if (pressed)
-            sendSpecialKeyEvent(nsGkAtoms::Search, e.time);
-        break;
-    case KEY_HOME:
-        sendKeyEvent(NS_VK_HOME, pressed, e.time);
-        break;
-    case KEY_POWER:
-        sendKeyEvent(NS_VK_SLEEP, pressed, e.time);
-        break;
-    case KEY_VOLUMEUP:
-        if (pressed)
-            sendSpecialKeyEvent(nsGkAtoms::VolumeUp, e.time);
-        break;
-    case KEY_VOLUMEDOWN:
-        if (pressed)
-            sendSpecialKeyEvent(nsGkAtoms::VolumeDown, e.time);
-        break;
-    default:
-        LOG("Got unknown key event code. type 0x%04x code 0x%04x value %d",
-            e.type, e.code, e.value);
+    maybeSendKeyEvent(e.code, pressed, e.time);
+}
+
+static void
+configureVButtons(FdHandler& data)
+{
+    char vbuttonsPath[PATH_MAX];
+    snprintf(vbuttonsPath, sizeof(vbuttonsPath),
+             "/sys/board_properties/virtualkeys.%s",
+             data.name);
+    ScopedClose fd(open(vbuttonsPath, O_RDONLY));
+    if (0 > fd.mFd) {
+        LOG("No vbuttons for mt device %s", data.name);
+        return;
+    }
+
+    // This device has vbuttons.  Process the configuration.
+    char config[1024];
+    ssize_t nread;
+    do {
+        nread = read(fd.mFd, config, sizeof(config));
+    } while (-1 == nread && EINTR == errno);
+
+    if (0 > nread) {
+        LOG("Error reading virtualkey configuration");
+        return;
     }
+
+    config[nread] = '\0';
+
+    LOG("Device %s has vbutton config '%s'", data.name, config);
+
+    char* startStr = config;
+    for (size_t i = 0; i < FdHandler::kMaxVButtons; ++i) {
+        FdHandler::VButton& vbutton = data.vbuttons[i];
+        char* token;
+        char* state;
+                
+        // XXX not clear what "0x01" is ... maybe a version
+        // number?  See InputManager.java.
+        if (!(token = strtok_r(startStr, ":", &state)) ||
+            strcmp(token, "0x01")) {
+            LOG("  magic 0x01 tag missing");
+            break;
+        }
+        startStr = NULL;
+
+        if (!(token = strtok_r(NULL, ":", &state))) {
+            LOG("  failed to read keycode");
+            break;
+        }
+        vbutton.keyCode = atoi(token);
+
+        const char *centerX, *centerY, *width, *height;
+        if (!((centerX = strtok_r(NULL, ":", &state)) &&
+              (centerY = strtok_r(NULL, ":", &state)) &&
+              (width = strtok_r(NULL, ":", &state)) &&
+              (height = strtok_r(NULL, ":", &state)))) {
+            LOG("  failed to read bound params");
+            break;
+        }
+
+        // NB: these coordinates are in *screen* space, not input
+        // space.  That means the values in /sys/board_config make
+        // assumptions about how the raw input events are mapped
+        // ... le sigh.
+        nsIntRect rect;
+        rect.width = atoi(width);
+        rect.height = atoi(height);
+        rect.x = atoi(centerX) - rect.width / 2;
+        rect.y = atoi(centerY) - rect.height / 2;
+        vbutton.buttonRect = rect;
+
+        LOG("  configured vbutton code=%d at <x=%d,y=%d,w=%d,h=%d>",
+            vbutton.keyCode, rect.x, rect.y, rect.width, rect.height);
+    }
+}
+
+static bool
+calibrateMultitouchDevice(FdHandler& data)
+{
+    if (data.calibrated)
+        return true;
+    if (gScreenBounds.IsEmpty()) {
+        // The framebuffer hasn't been initialized yet.  We *could*
+        // force it to be initialized here, but that's another patch.
+        LOG("Deferring multitouch calibrate, fb not ready");
+        return false;
+    }
+
+    struct input_absinfo xInfo, yInfo;
+    if (0 > ioctl(data.fd, EVIOCGABS(ABS_MT_POSITION_X), &xInfo) ||
+        0 > ioctl(data.fd, EVIOCGABS(ABS_MT_POSITION_Y), &yInfo)) {
+        LOG("Couldn't get absinfo for multitouch axes");
+        return false;
+    }
+    LOG("Input coordinate bounds: xmin=%d, xmax=%d, ymin=%d, ymax=%d",
+        xInfo.minimum, xInfo.maximum, yInfo.minimum, yInfo.maximum);
+
+    data.inputMinX = xInfo.minimum;
+    data.inputMinY = yInfo.minimum;
+    data.inputToScreenScaleX =
+        float(gScreenBounds.width) / float(xInfo.maximum - xInfo.minimum);
+    data.inputToScreenScaleY =
+        float(gScreenBounds.height) / float(yInfo.maximum - yInfo.minimum);
+
+    configureVButtons(data);
+
+    data.calibrated = true;
+    return true;
 }
 
 static void
 multitouchHandler(int fd, FdHandler *data)
 {
+    if (!calibrateMultitouchDevice(*data))
+        return;
+
     // The Linux's input documentation (Documentation/input/input.txt)
     // says that we'll always read a multiple of sizeof(input_event) bytes here.
     input_event events[16];
     int event_count = read(fd, events, sizeof(events));
     if (event_count < 0) {
         LOG("Error reading in multitouchHandler");
         return;
     }
@@ -253,56 +381,87 @@ multitouchHandler(int fd, FdHandler *dat
             case ABS_MT_WIDTH_MINOR:
             case ABS_MT_ORIENTATION:
             case ABS_MT_TOOL_TYPE:
             case ABS_MT_BLOB_ID:
             case ABS_MT_TRACKING_ID:
             case ABS_MT_PRESSURE:
                 break;
             case ABS_MT_POSITION_X:
-                data->mtX = event->value;
+                data->mtX = data->inputXToScreenX(event->value);
                 break;
             case ABS_MT_POSITION_Y:
-                data->mtY = event->value;
+                data->mtY = data->inputYToScreenY(event->value);
                 break;
             default:
-                LOG("Got unknown event type 0x%04x with code 0x%04x and value %d",
-                    event->type, event->code, event->value);
+                VERBOSE_LOG("Got unknown mt event type 0x%04x with code 0x%04x and value %d",
+                            event->type, event->code, event->value);
+                break;
             }
         } else if (event->type == EV_SYN) {
             switch (event->code) {
             case SYN_MT_REPORT:
                 if (data->mtState == FdHandler::MT_COLLECT)
                     data->mtState = FdHandler::MT_IGNORE;
                 break;
             case SYN_REPORT:
-                if ((!data->mtMajor || data->mtState == FdHandler::MT_START)) {
-                    sendMouseEvent(NS_MOUSE_BUTTON_UP, &event->time,
-                                   data->mtX, data->mtY);
+                if (!data->mtMajor || data->mtState == FdHandler::MT_START) {
                     data->mtDown = false;
-                    //LOG("Up mouse event");
+                    if (data->keyCode) {
+                        maybeSendKeyEvent(data->keyCode, data->mtDown,
+                                          event->time);
+                        data->keyCode = 0;
+                    } else {
+                        sendMouseEvent(NS_MOUSE_BUTTON_UP, event->time,
+                                       data->mtX, data->mtY);
+                    }
                 } else if (!data->mtDown) {
-                    sendMouseEvent(NS_MOUSE_BUTTON_DOWN, &event->time,
-                                   data->mtX, data->mtY);
+                    int x = data->mtX, y = data->mtY;
+
+                    bool isKeyEvent = false;
+                    if (!gScreenBounds.Contains(x, y)) {
+                        // Off-screen mt down.  Should be a vbutton.
+                        for (size_t i = 0; i < FdHandler::kMaxVButtons; ++i) {
+                            const FdHandler::VButton& vbutton = data->vbuttons[i];
+                            if (vbutton.buttonRect.IsEmpty())
+                                break;
+
+                            if (vbutton.buttonRect.Contains(x, y)) {
+                                isKeyEvent = true;
+                                data->keyCode = vbutton.keyCode;
+                                break;
+                            }
+                        }
+                    }
                     data->mtDown = true;
-                    //LOG("Down mouse event");
-                } else {
-                    sendMouseEvent(NS_MOUSE_MOVE, &event->time,
+
+                    if (isKeyEvent) {
+                        maybeSendKeyEvent(data->keyCode, data->mtDown,
+                                          event->time);
+                    } else {
+                        sendMouseEvent(NS_MOUSE_BUTTON_DOWN, event->time,
+                                       data->mtX, data->mtY);
+                    }
+                } else if (!data->keyCode) {
+                    sendMouseEvent(NS_MOUSE_MOVE, event->time,
                                    data->mtX, data->mtY);
                     data->mtDown = true;
                 }
+
                 data->mtState = FdHandler::MT_START;
+
                 break;
             default:
-                LOG("Got unknown event type 0x%04x with code 0x%04x and value %d",
-                    event->type, event->code, event->value);
+                VERBOSE_LOG("Got unknown mt event type 0x%04x with code 0x%04x and value %d",
+                            event->type, event->code, event->value);
+
             }
         } else
-            LOG("Got unknown event type 0x%04x with code 0x%04x and value %d",
-                event->type, event->code, event->value);
+            VERBOSE_LOG("Got unknown mt event type 0x%04x with code 0x%04x and value %d",
+                        event->type, event->code, event->value);
     }
 }
 
 static void
 singleTouchHandler(int fd, FdHandler *data)
 {
     // The Linux's input documentation (Documentation/input/input.txt)
     // says that we'll always read a multiple of sizeof(input_event) bytes here.
@@ -322,42 +481,41 @@ singleTouchHandler(int fd, FdHandler *da
         if (event->type == EV_KEY) {
             switch (event->code) {
             case BTN_TOUCH:
                 data->mtDown = event->value;
                 break;
             default:
                 maybeSendKeyEvent(*event);
             }
-        }
-        else if (event->type == EV_ABS) {
+        } else if (event->type == EV_ABS) {
             switch (event->code) {
             case ABS_X:
                 data->mtX = event->value;
                 break;
             case ABS_Y:
                 data->mtY = event->value;
                 break;
             default:
                 LOG("Got unknown st abs event type 0x%04x with code 0x%04x and value %d",
                     event->type, event->code, event->value);
             }
         } else if (event->type == EV_SYN) {
             if (data->mtState == FdHandler::MT_START) {
                 MOZ_ASSERT(data->mtDown);
-                sendMouseEvent(NS_MOUSE_BUTTON_DOWN, &event->time,
+                sendMouseEvent(NS_MOUSE_BUTTON_DOWN, event->time,
                                data->mtX, data->mtY);
                 data->mtState = FdHandler::MT_COLLECT;
             } else if (data->mtDown) {
                 MOZ_ASSERT(data->mtDown);
-                sendMouseEvent(NS_MOUSE_MOVE, &event->time,
+                sendMouseEvent(NS_MOUSE_MOVE, event->time,
                                data->mtX, data->mtY);
             } else {
                 MOZ_ASSERT(!data->mtDown);
-                sendMouseEvent(NS_MOUSE_BUTTON_UP, &event->time,
+                sendMouseEvent(NS_MOUSE_BUTTON_UP, event->time,
                                    data->mtX, data->mtY);
                 data->mtDown = false;
                 data->mtState = FdHandler::MT_START;
             }
         }
     }
 }
 
@@ -394,41 +552,32 @@ nsAppShell::nsAppShell()
 nsAppShell::~nsAppShell()
 {
     gAppShell = NULL;
 }
 
 nsresult
 nsAppShell::Init()
 {
-    epoll_event event = {
-        EPOLLIN,
-        { 0 }
-    };
-
     nsresult rv = nsBaseAppShell::Init();
     NS_ENSURE_SUCCESS(rv, rv);
 
     epollfd = epoll_create(16);
     NS_ENSURE_TRUE(epollfd >= 0, NS_ERROR_UNEXPECTED);
 
     int ret = pipe2(signalfds, O_NONBLOCK);
     NS_ENSURE_FALSE(ret, NS_ERROR_UNEXPECTED);
 
-    FdHandler *handler = mHandlers.AppendElement();
-    handler->fd = signalfds[0];
-    handler->func = pipeHandler;
-    event.data.u32 = mHandlers.Length() - 1;
-    ret = epoll_ctl(epollfd, EPOLL_CTL_ADD, signalfds[0], &event);
-    NS_ENSURE_FALSE(ret, NS_ERROR_UNEXPECTED);
+    rv = AddFdHandler(signalfds[0], pipeHandler, "");
+    NS_ENSURE_SUCCESS(rv, rv);
 
     DIR *dir = opendir("/dev/input");
     NS_ENSURE_TRUE(dir, NS_ERROR_UNEXPECTED);
 
-#define BITSET(bit, flags) (flags[bit >> 3] & (1 << (bit & 0x7)))
+#define IS_BIT_SET(bit, flags) (flags[bit >> 3] & (1 << (bit & 0x7)))
 
     struct dirent *entry;
     while ((entry = readdir(dir))) {
         char entryName[64];
         char entryPath[MAXPATHLEN];
         if (snprintf(entryPath, sizeof(entryPath),
                      "/dev/input/%s", entry->d_name) < 0) {
             LOG("Couldn't generate path while enumerating input devices!");
@@ -439,44 +588,59 @@ nsAppShell::Init()
             LOG("Found device %s - %s", entry->d_name, entryName);
         else
             continue;
 
         FdHandlerCallback handlerFunc = NULL;
 
         char flags[(NS_MAX(ABS_MAX, KEY_MAX) + 1) / 8];
         if (ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(flags)), flags) >= 0 &&
-            BITSET(ABS_MT_POSITION_X, flags)) {
+            IS_BIT_SET(ABS_MT_POSITION_X, flags)) {
 
             LOG("Found multitouch input device");
             handlerFunc = multitouchHandler;
         } else if (ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(flags)), flags) >= 0 &&
-                   BITSET(ABS_X, flags)) {
+                   IS_BIT_SET(ABS_X, flags)) {
             LOG("Found single touch input device");
             handlerFunc = singleTouchHandler;
         } else if (ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(flags)), flags) >= 0) {
             LOG("Found key input device");
             handlerFunc = keyHandler;
         }
 
         // Register the handler, if we have one.
         if (!handlerFunc)
             continue;
 
-        handler = mHandlers.AppendElement();
-        handler->fd = fd;
-        handler->func = handlerFunc;
-        event.data.u32 = mHandlers.Length() - 1;
-        if (epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &event))
+        rv = AddFdHandler(fd, handlerFunc, entryName);
+        if (NS_FAILED(rv))
             LOG("Failed to add fd to epoll fd");
     }
 
     return rv;
 }
 
+nsresult
+nsAppShell::AddFdHandler(int fd, FdHandlerCallback handlerFunc,
+                         const char* deviceName)
+{
+    epoll_event event = {
+        EPOLLIN,
+        { 0 }
+    };
+
+    FdHandler *handler = mHandlers.AppendElement();
+    handler->fd = fd;
+    strncpy(handler->name, deviceName, sizeof(handler->name) - 1);
+    handler->func = handlerFunc;
+    event.data.u32 = mHandlers.Length() - 1;
+    return epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &event) ?
+           NS_ERROR_UNEXPECTED : NS_OK;
+}
+
 void
 nsAppShell::ScheduleNativeEventCallback()
 {
     mNativeCallbackRequest = true;
     NotifyEvent();
 }
 
 bool
--- a/widget/src/gonk/nsAppShell.h
+++ b/widget/src/gonk/nsAppShell.h
@@ -34,62 +34,111 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef nsAppShell_h
 #define nsAppShell_h
 
 #include "nsBaseAppShell.h"
+#include "nsRect.h"
 #include "nsTArray.h"
 
 namespace mozilla {
 bool ProcessNextEvent();
 void NotifyEvent();
 }
 
 extern bool gDrawRequest;
 
 class FdHandler;
 typedef void(*FdHandlerCallback)(int, FdHandler *);
 
 class FdHandler {
 public:
-    FdHandler() : mtState(MT_START), mtDown(false) { }
+    FdHandler()
+        : mtState(MT_START)
+        , keyCode(0)
+        , mtDown(false)
+        , calibrated(false)
+    {
+        memset(name, 0, sizeof(name));
+    }
 
     int fd;
+    char name[64];
     FdHandlerCallback func;
     enum mtStates {
         MT_START,
         MT_COLLECT,
         MT_IGNORE
     } mtState;
     int mtX, mtY;
     int mtMajor;
+    int keyCode;
     bool mtDown;
+    // FIXME/bug 712973: we should be using libui here instead of
+    // recreating all that logic ourselves.  Please don't extend the
+    // hacks here further than what's below.
+    bool calibrated;
+    // Multitouch events are delivered to us in "input space", which
+    // is a coordinate space defined by the multitouch device driver.
+    // The coordinate space has top-left at P_min = <inputMinX,
+    // inputMinY> when in normal-portrait orientation.  The input
+    // device and the screen might have different resolutions.  The
+    // resolution difference is Scale = <inputToScreenScaleX,
+    // inputToScreenScaleY>.  So going from input to screen space
+    // (when in normal portrait orientation) is an affine transform
+    // defined by
+    //
+    //   P_screen = Scale * (P_input - P_min)
+    //
+    int inputMinX, inputMinY;
+    float inputToScreenScaleX, inputToScreenScaleY;
+    // Some touch devices use virtual buttons instead of hardware
+    // buttons.  When the device uses vbuttons, we convert touch
+    // events into key events of type |keyCode| when the start of the
+    // touch is within |buttonRect|.  |buttonRect| must be disjoint
+    // from the screen rect.
+    static const size_t kMaxVButtons = 4;
+    struct VButton {
+        nsIntRect buttonRect;   // in screen space
+        int keyCode;
+    } vbuttons[kMaxVButtons];
 
     void run()
     {
         func(fd, this);
     }
+
+    int inputXToScreenX(int inputX) {
+        return inputToScreenScaleX * (inputX - inputMinX);
+    }
+    int inputYToScreenY(int inputY) {
+        return inputToScreenScaleY * (inputY - inputMinY);
+    }
 };
 
 class nsAppShell : public nsBaseAppShell {
 public:
     nsAppShell();
 
     nsresult Init();
     virtual bool ProcessNextNativeEvent(bool maywait);
 
     void NotifyNativeEvent();
 
 protected:
     virtual ~nsAppShell();
 
     virtual void ScheduleNativeEventCallback();
 
+private:
+    nsresult AddFdHandler(int fd, FdHandlerCallback handlerFunc,
+                          const char* deviceName);
+
     // This is somewhat racy but is perfectly safe given how the callback works
     bool mNativeCallbackRequest;
     nsTArray<FdHandler> mHandlers;
 };
 
 #endif /* nsAppShell_h */
 
--- a/widget/src/gonk/nsWindow.cpp
+++ b/widget/src/gonk/nsWindow.cpp
@@ -73,26 +73,26 @@ nsWindow::nsWindow()
 {
     if (!sGLContext && !sFramebufferOpen) {
         // We (apparently) don't have a way to tell if allocating the
         // fbs succeeded or failed.
         gNativeWindow = new android::FramebufferNativeWindow();
         sGLContext = GLContextProvider::CreateForWindow(this);
         // CreateForWindow sets up gScreenBounds
         if (!sGLContext) {
-            LOG("Failed to create GL context for fb, trying /dev/fb0");
+            LOG("Failed to create GL context for fb, trying /dev/graphics/fb0");
 
             // We can't delete gNativeWindow.
 
             nsIntSize screenSize;
             sFramebufferOpen = Framebuffer::Open(&screenSize);
             gScreenBounds = nsIntRect(nsIntPoint(0, 0), screenSize);
             if (!sFramebufferOpen) {
                 LOG("Failed to mmap fb(?!?), aborting ...");
-                NS_RUNTIMEABORT("Can't open GL context and can't fall back on /dev/fb0 ...");
+                NS_RUNTIMEABORT("Can't open GL context and can't fall back on /dev/graphics/fb0 ...");
             }
         }
     }
 }
 
 nsWindow::~nsWindow()
 {
 }
@@ -101,17 +101,18 @@ void
 nsWindow::DoDraw(void)
 {
     if (!gWindowToRedraw) {
         LOG("  no window to draw, bailing");
         return;
     }
 
     nsPaintEvent event(true, NS_PAINT, gWindowToRedraw);
-    event.region = gScreenBounds;
+    event.region = gWindowToRedraw->mDirtyRegion;
+    gWindowToRedraw->mDirtyRegion.SetEmpty();
 
     LayerManager* lm = gWindowToRedraw->GetLayerManager();
     if (LayerManager::LAYERS_OPENGL == lm->GetBackendType()) {
         static_cast<LayerManagerOGL*>(lm)->
             SetClippingRegion(nsIntRegion(gScreenBounds));
         gWindowToRedraw->mEventCallback(&event);
     } else if (LayerManager::LAYERS_BASIC == lm->GetBackendType()) {
         MOZ_ASSERT(sFramebufferOpen);
@@ -288,19 +289,25 @@ nsWindow::Invalidate(const nsIntRect &aR
     nsWindow *parent = mParent;
     while (parent && parent != sTopWindows[0])
         parent = parent->mParent;
     if (parent != sTopWindows[0]) {
         LOG("  parent isn't top window, bailing");
         return NS_OK;
     }
 
+    mDirtyRegion.Or(mDirtyRegion, aRect);
     gWindowToRedraw = this;
-    gDrawRequest = true;
-    mozilla::NotifyEvent();
+    if (aIsSynchronous) {
+        gDrawRequest = false;
+        DoDraw();
+    } else {
+        gDrawRequest = true;
+        mozilla::NotifyEvent();
+    }
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsWindow::Update()
 {
     Invalidate(gScreenBounds, false);
     return NS_OK;
--- a/widget/src/gonk/nsWindow.h
+++ b/widget/src/gonk/nsWindow.h
@@ -124,15 +124,15 @@ public:
 
     NS_IMETHOD_(void) SetInputContext(const InputContext& aContext,
                                       const InputContextAction& aAction);
     NS_IMETHOD_(InputContext) GetInputContext();
 
 protected:
     nsWindow* mParent;
     bool mVisible;
+    nsIntRegion mDirtyRegion;
+    InputContext mInputContext;
 
     void BringToTop();
-
-    InputContext mInputContext;
 };
 
 #endif /* nsWindow_h */
--- a/widget/src/windows/AudioSession.cpp
+++ b/widget/src/windows/AudioSession.cpp
@@ -46,16 +46,17 @@
 #include "nsIUUIDGenerator.h"
 #include "nsIXULAppInfo.h"
 
 //#include "AudioSession.h"
 #include "nsCOMPtr.h"
 #include "nsAutoPtr.h"
 #include "nsServiceManagerUtils.h"
 #include "nsString.h"
+#include "nsThreadUtils.h"
 #include "nsXULAppAPI.h"
 
 #include <objbase.h>
 
 namespace mozilla {
 namespace widget {
 
 /* 
@@ -79,16 +80,19 @@ public:
   STDMETHODIMP OnChannelVolumeChanged(DWORD aChannelCount,
                                       float aChannelVolumeArray[],
                                       DWORD aChangedChannel,
                                       LPCGUID aContext);
   STDMETHODIMP OnDisplayNameChanged(LPCWSTR aDisplayName, LPCGUID aContext);
   STDMETHODIMP OnGroupingParamChanged(LPCGUID aGroupingParam, LPCGUID aContext);
   STDMETHODIMP OnIconPathChanged(LPCWSTR aIconPath, LPCGUID aContext);
   STDMETHODIMP OnSessionDisconnected(AudioSessionDisconnectReason aReason);
+private:
+  nsresult OnSessionDisconnectedInternal();
+public:
   STDMETHODIMP OnSimpleVolumeChanged(float aVolume,
                                      BOOL aMute,
                                      LPCGUID aContext);
   STDMETHODIMP OnStateChanged(AudioSessionState aState);
 
   nsresult Start();
   nsresult Stop();
   void StopInternal();
@@ -427,23 +431,35 @@ AudioSession::OnIconPathChanged(LPCWSTR 
                                 LPCGUID aContext)
 {
   return S_OK; // NOOP
 }
 
 STDMETHODIMP
 AudioSession::OnSessionDisconnected(AudioSessionDisconnectReason aReason)
 {
+  // Run our code asynchronously.  Per MSDN we can't do anything interesting
+  // in this callback.
+  nsCOMPtr<nsIRunnable> runnable =
+    NS_NewRunnableMethod(this, &AudioSession::OnSessionDisconnectedInternal);
+  NS_DispatchToMainThread(runnable);
+  return S_OK;
+}
+
+nsresult
+AudioSession::OnSessionDisconnectedInternal()
+{
   if (!mAudioSessionControl)
-    return S_OK;
+    return NS_OK;
 
   mAudioSessionControl->UnregisterAudioSessionNotification(this);
   mAudioSessionControl = nsnull;
-  Start(); // If it fails there's not much we can do
-  return S_OK;
+
+  Start(); // If it fails there's not much we can do.
+  return NS_OK;
 }
 
 STDMETHODIMP
 AudioSession::OnSimpleVolumeChanged(float aVolume,
                                     BOOL aMute,
                                     LPCGUID aContext)
 {
   return S_OK; // NOOP
--- a/xpcom/base/MapsMemoryReporter.cpp
+++ b/xpcom/base/MapsMemoryReporter.cpp
@@ -147,16 +147,23 @@ public:
   MapsReporter();
 
   NS_DECL_ISUPPORTS
 
   NS_IMETHOD
   CollectReports(nsIMemoryMultiReporterCallback *aCallback,
                  nsISupports *aClosure);
 
+  NS_IMETHOD
+  GetExplicitNonHeap(PRInt64 *aAmount) {
+    // This reporter doesn't do any "explicit" measurements.
+    *aAmount = 0;
+    return NS_OK;
+  }
+
 private:
   // Search through /proc/self/maps for libxul.so, and set mLibxulDir to the
   // the directory containing libxul.
   nsresult FindLibxul();
 
   nsresult
   ParseMapping(FILE *aFile,
                nsIMemoryMultiReporterCallback *aCallback,
@@ -218,17 +225,17 @@ MapsReporter::CollectReports(nsIMemoryMu
   // 'map/vsize'; otherwise we're probably not reading smaps correctly.  If we
   // didn't create a node under 'map/swap', create one here so about:memory
   // knows to create an empty 'map/swap' tree.  See also bug 682735.
 
   NS_ASSERTION(categoriesSeen.mSeenVsize, "Didn't create a vsize node?");
   NS_ASSERTION(categoriesSeen.mSeenVsize, "Didn't create a resident node?");
   if (!categoriesSeen.mSeenSwap) {
     aCallback->Callback(NS_LITERAL_CSTRING(""),
-                        NS_LITERAL_CSTRING("map/swap"),
+                        NS_LITERAL_CSTRING("map/swap/total"),
                         nsIMemoryReporter::KIND_NONHEAP,
                         nsIMemoryReporter::UNITS_BYTES,
                         0,
                         NS_LITERAL_CSTRING("This process uses no swap space."),
                         aClosure);
   }
 
   return NS_OK;
--- a/xpcom/base/nsIMemoryReporter.idl
+++ b/xpcom/base/nsIMemoryReporter.idl
@@ -208,21 +208,33 @@ interface nsIMemoryMultiReporterCallback
  * a single traversal of a large data structure).
  *
  * The arguments to the callback deliberately match the fields in
  * nsIMemoryReporter, but note that seeing any of these arguments requires
  * calling collectReports which will trigger all relevant computation.
  * (Compare and contrast this with nsIMemoryReporter, which allows all
  * fields except |amount| to be accessed without triggering computation.)
  */
-[scriptable, uuid(eae277ad-b67d-4389-95f4-03fa11c09d06)]
+[scriptable, uuid(61d498d5-b460-4398-a8ea-7f75208534b4)]
 interface nsIMemoryMultiReporter : nsISupports
 {
   void collectReports(in nsIMemoryMultiReporterCallback callback,
                       in nsISupports closure);
+
+  /*
+   * Return the sum of all this multi-reporter's measurements that have a
+   * path that starts with "explicit" and are KIND_NONHEAP.
+   *
+   * This is a hack that's required to implement
+   * nsIMemoryReporterManager::explicit efficiently, which is important --
+   * multi-reporters can special-case this operation so it's much faster
+   * than getting all the reports, filtering out the unneeded ones, and
+   * summing the remainder.
+   */
+  readonly attribute PRInt64 explicitNonHeap;
 };
 
 [scriptable, uuid(84ba9c85-3372-4423-b7ab-74708b9269a6)]
 interface nsIMemoryReporterManager : nsISupports
 {
   /*
    * Return an enumerator of nsIMemoryReporters that are currently registered.
    */
--- a/xpcom/base/nsMemoryReporterManager.cpp
+++ b/xpcom/base/nsMemoryReporterManager.cpp
@@ -43,16 +43,29 @@
 #include "nsServiceManagerUtils.h"
 #include "nsMemoryReporterManager.h"
 #include "nsArrayEnumerator.h"
 #include "nsISimpleEnumerator.h"
 #include "mozilla/Telemetry.h"
 
 using namespace mozilla;
 
+static PRInt64 GetExplicit()
+{
+    nsCOMPtr<nsIMemoryReporterManager> mgr = do_GetService("@mozilla.org/memory-reporter-manager;1");
+    if (mgr == nsnull)
+        return (PRInt64)-1;
+
+    PRInt64 n;
+    nsresult rv = mgr->GetExplicit(&n);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    return n;
+}
+
 #if defined(MOZ_MEMORY)
 #  if defined(XP_WIN) || defined(SOLARIS) || defined(ANDROID) || defined(XP_MACOSX)
 #    define HAVE_JEMALLOC_STATS 1
 #    include "jemalloc.h"
 #  elif defined(XP_LINUX)
 #    define HAVE_JEMALLOC_STATS 1
 #    include "jemalloc_types.h"
 // jemalloc is directly linked into firefox-bin; libxul doesn't link
@@ -315,16 +328,25 @@ NS_MEMORY_REPORTER_IMPLEMENT(PageFaultsH
     "fault. When memory is plentiful, you should see very few hard page faults. "
     "But if the process tries to use more memory than your machine has "
     "available, you may see many thousands of hard page faults. Because "
     "accessing the disk is up to a million times slower than accessing RAM, "
     "the program may run very slowly when it is experiencing more than 100 or "
     "so hard page faults a second.")
 #endif
 
+NS_MEMORY_REPORTER_IMPLEMENT(Explicit,
+    "explicit",
+    KIND_OTHER,
+    UNITS_BYTES,
+    GetExplicit,
+    "This is the same measurement as the root of the 'explicit' tree.  "
+    "However, it is measured at a different time and so gives slightly "
+    "different results.")
+
 NS_MEMORY_REPORTER_IMPLEMENT(Resident,
     "resident",
     KIND_OTHER,
     UNITS_BYTES,
     GetResident,
     "Memory mapped by the process that is present in physical memory, "
     "also known as the resident set size (RSS).  This is the best single "
     "figure to use when considering the memory resources used by the process, "
@@ -528,16 +550,17 @@ nsMemoryReporterManager::Init()
     if (!jemalloc_stats)
         return NS_ERROR_FAILURE;
 #endif
 
 #define REGISTER(_x)  RegisterReporter(new NS_MEMORY_REPORTER_NAME(_x))
 
     REGISTER(HeapAllocated);
     REGISTER(HeapUnallocated);
+    REGISTER(Explicit);
     REGISTER(Resident);
 
 #if defined(XP_LINUX) || defined(XP_MACOSX) || defined(XP_WIN) || defined(SOLARIS)
     REGISTER(Vsize);
 #endif
 
 #if defined(XP_LINUX) || defined(XP_MACOSX) || defined(SOLARIS)
     REGISTER(PageFaultsSoft);
@@ -652,16 +675,17 @@ struct MemoryReport {
     ~MemoryReport() 
     {
         MOZ_COUNT_DTOR(MemoryReport);
     }
     const nsCString path;
     PRInt64 amount;
 };
 
+#ifdef DEBUG
 // This is just a wrapper for InfallibleTArray<MemoryReport> that implements
 // nsISupports, so it can be passed to nsIMemoryMultiReporter::CollectReports.
 class MemoryReportsWrapper : public nsISupports {
 public:
     NS_DECL_ISUPPORTS
     MemoryReportsWrapper(InfallibleTArray<MemoryReport> *r) : mReports(r) { }
     InfallibleTArray<MemoryReport> *mReports;
 };
@@ -674,61 +698,62 @@ public:
 
     NS_IMETHOD Callback(const nsACString &aProcess, const nsACString &aPath,
                         PRInt32 aKind, PRInt32 aUnits, PRInt64 aAmount,
                         const nsACString &aDescription,
                         nsISupports *aWrappedMRs)
     {
         if (aKind == nsIMemoryReporter::KIND_NONHEAP &&
             PromiseFlatCString(aPath).Find("explicit") == 0 &&
-            aAmount != PRInt64(-1)) {
-
+            aAmount != PRInt64(-1))
+        {
             MemoryReportsWrapper *wrappedMRs =
                 static_cast<MemoryReportsWrapper *>(aWrappedMRs);
             MemoryReport mr(aPath, aAmount);
             wrappedMRs->mReports->AppendElement(mr);
         }
         return NS_OK;
     }
 };
 NS_IMPL_ISUPPORTS1(
   MemoryReportCallback
 , nsIMemoryMultiReporterCallback
 )
+#endif
 
 // Is path1 a prefix, and thus a parent, of path2?  Eg. "a/b" is a parent of
 // "a/b/c", but "a/bb" is not.
 static bool
 isParent(const nsACString &path1, const nsACString &path2)
 {
     if (path1.Length() >= path2.Length())
         return false;
 
     const nsACString& subStr = Substring(path2, 0, path1.Length());
     return subStr.Equals(path1) && path2[path1.Length()] == '/';
 }
 
 NS_IMETHODIMP
 nsMemoryReporterManager::GetExplicit(PRInt64 *aExplicit)
 {
-    InfallibleTArray<MemoryReport> nonheap;
-    PRInt64 heapUsed = PRInt64(-1);
+    nsresult rv;
 
-    // Get "heap-allocated" and all the KIND_NONHEAP measurements from vanilla
-    // "explicit" reporters.
+    // Get "heap-allocated" and all the KIND_NONHEAP measurements from normal
+    // (i.e. non-multi) "explicit" reporters.
+    PRInt64 heapAllocated = PRInt64(-1);
+    InfallibleTArray<MemoryReport> explicitNonHeapNormalReports;
     nsCOMPtr<nsISimpleEnumerator> e;
     EnumerateReporters(getter_AddRefs(e));
-
     bool more;
     while (NS_SUCCEEDED(e->HasMoreElements(&more)) && more) {
         nsCOMPtr<nsIMemoryReporter> r;
         e->GetNext(getter_AddRefs(r));
 
         PRInt32 kind;
-        nsresult rv = r->GetKind(&kind);
+        rv = r->GetKind(&kind);
         NS_ENSURE_SUCCESS(rv, rv);
 
         nsCString path;
         rv = r->GetPath(path);
         NS_ENSURE_SUCCESS(rv, rv);
 
         // We're only interested in NONHEAP explicit reporters and
         // the 'heap-allocated' reporter.
@@ -738,66 +763,115 @@ nsMemoryReporterManager::GetExplicit(PRI
             PRInt64 amount;
             rv = r->GetAmount(&amount);
             NS_ENSURE_SUCCESS(rv, rv);
 
             // Just skip any NONHEAP reporters that fail, because
             // "heap-allocated" is the most important one.
             if (amount != PRInt64(-1)) {
                 MemoryReport mr(path, amount);
-                nonheap.AppendElement(mr);
+                explicitNonHeapNormalReports.AppendElement(mr);
             }
         } else if (path.Equals("heap-allocated")) {
-            rv = r->GetAmount(&heapUsed);
+            rv = r->GetAmount(&heapAllocated);
             NS_ENSURE_SUCCESS(rv, rv);
-            // If "heap-allocated" fails, we give up, because the result
-            // would be horribly inaccurate.
-            if (heapUsed == PRInt64(-1)) {
-                *aExplicit = PRInt64(-1);
-                return NS_OK;
-            }
         }
     }
 
-    // Get KIND_NONHEAP measurements from multi-reporters, too.
+    // If we don't have "heap-allocated", give up, because the result would be
+    // horribly inaccurate.
+    if (heapAllocated == PRInt64(-1)) {
+        *aExplicit = PRInt64(-1);
+        return NS_OK;
+    }
+
+    // Sum all the explicit, NONHEAP reports from normal reporters.
+    // Ignore (by zeroing its amount) any normal reporter that is a child of
+    // another normal reporter.  Eg. if we have "explicit/a" and
+    // "explicit/a/b", zero the latter.  This is quadratic in the number of
+    // explicit NONHEAP reporters, but there shouldn't be many.
+    //
+    // XXX: bug 700508 will remove the need for this
+    //
+    for (PRUint32 i = 0; i < explicitNonHeapNormalReports.Length(); i++) {
+        const nsCString &iPath = explicitNonHeapNormalReports[i].path;
+        for (PRUint32 j = i + 1; j < explicitNonHeapNormalReports.Length(); j++) {
+            const nsCString &jPath = explicitNonHeapNormalReports[j].path;
+            if (isParent(iPath, jPath)) {
+                explicitNonHeapNormalReports[j].amount = 0;
+            } else if (isParent(jPath, iPath)) {
+                explicitNonHeapNormalReports[i].amount = 0;
+            }
+        }
+    }
+    PRInt64 explicitNonHeapNormalSize = 0;
+    for (PRUint32 i = 0; i < explicitNonHeapNormalReports.Length(); i++) {
+        explicitNonHeapNormalSize += explicitNonHeapNormalReports[i].amount;
+    }
+
+    // For each multi-reporter we could call CollectReports and filter out the
+    // non-explicit, non-NONHEAP measurements.  But that's lots of wasted work,
+    // so we instead use GetExplicitNonHeap() which exists purely for this
+    // purpose.
+    //
+    // (Actually, in debug builds we also do it the slow way and compare the
+    // result to the result obtained from GetExplicitNonHeap().  This
+    // guarantees the two measurement paths are equivalent.  This is wise
+    // because it's easy for memory reporters to have bugs.)
+
     nsCOMPtr<nsISimpleEnumerator> e2;
     EnumerateMultiReporters(getter_AddRefs(e2));
-    nsRefPtr<MemoryReportsWrapper> wrappedMRs =
-        new MemoryReportsWrapper(&nonheap);
-
-    // This callback adds only NONHEAP explicit reporters.
-    nsRefPtr<MemoryReportCallback> cb = new MemoryReportCallback();
-
+    PRInt64 explicitNonHeapMultiSize = 0;
     while (NS_SUCCEEDED(e2->HasMoreElements(&more)) && more) {
       nsCOMPtr<nsIMemoryMultiReporter> r;
       e2->GetNext(getter_AddRefs(r));
+      PRInt64 n;
+      rv = r->GetExplicitNonHeap(&n);
+      NS_ENSURE_SUCCESS(rv, rv);
+      explicitNonHeapMultiSize += n;
+    }
+
+#ifdef DEBUG
+    InfallibleTArray<MemoryReport> explicitNonHeapMultiReports;
+    nsRefPtr<MemoryReportCallback> cb = new MemoryReportCallback();
+    nsRefPtr<MemoryReportsWrapper> wrappedMRs =
+        new MemoryReportsWrapper(&explicitNonHeapMultiReports);
+    nsCOMPtr<nsISimpleEnumerator> e3;
+    EnumerateMultiReporters(getter_AddRefs(e3));
+    while (NS_SUCCEEDED(e3->HasMoreElements(&more)) && more) {
+      nsCOMPtr<nsIMemoryMultiReporter> r;
+      e3->GetNext(getter_AddRefs(r));
       r->CollectReports(cb, wrappedMRs);
     }
 
-    // Ignore (by zeroing its amount) any reporter that is a child of another
-    // reporter.  Eg. if we have "explicit/a" and "explicit/a/b", zero the
-    // latter.  This is quadratic in the number of explicit NONHEAP reporters,
-    // but there shouldn't be many.
-    for (PRUint32 i = 0; i < nonheap.Length(); i++) {
-        const nsCString &iPath = nonheap[i].path;
-        for (PRUint32 j = i + 1; j < nonheap.Length(); j++) {
-            const nsCString &jPath = nonheap[j].path;
+    // Sum all the explicit, NONHEAP reports from multi-reporters.
+    // XXX: identical to the explicitNonHeapNormalReports case above;  bug
+    // 700508 will remove the need for this
+    for (PRUint32 i = 0; i < explicitNonHeapMultiReports.Length(); i++) {
+        const nsCString &iPath = explicitNonHeapMultiReports[i].path;
+        for (PRUint32 j = i + 1; j < explicitNonHeapMultiReports.Length(); j++) {
+            const nsCString &jPath = explicitNonHeapMultiReports[j].path;
             if (isParent(iPath, jPath)) {
-                nonheap[j].amount = 0;
+                explicitNonHeapMultiReports[j].amount = 0;
             } else if (isParent(jPath, iPath)) {
-                nonheap[i].amount = 0;
+                explicitNonHeapMultiReports[i].amount = 0;
             }
         }
     }
+    PRInt64 explicitNonHeapMultiSize2 = 0;
+    for (PRUint32 i = 0; i < explicitNonHeapMultiReports.Length(); i++) {
+        explicitNonHeapMultiSize2 += explicitNonHeapMultiReports[i].amount;
+    }
 
-    // Sum all the nonheap reporters and heapUsed.
-    *aExplicit = heapUsed;
-    for (PRUint32 i = 0; i < nonheap.Length(); i++) {
-        *aExplicit += nonheap[i].amount;
-    }
+    // Check the two measurements give the same result.
+    NS_ASSERTION(explicitNonHeapMultiSize == explicitNonHeapMultiSize2,
+                 "The two measurements of 'explicit' memory usage don't match");
+#endif
+
+    *aExplicit = heapAllocated + explicitNonHeapNormalSize + explicitNonHeapMultiSize;
 
     return NS_OK;
 }
 
 NS_IMPL_ISUPPORTS1(nsMemoryReporter, nsIMemoryReporter)
 
 nsMemoryReporter::nsMemoryReporter(nsACString& process,
                                    nsACString& path,
--- a/xpcom/ds/Makefile.in
+++ b/xpcom/ds/Makefile.in
@@ -85,16 +85,17 @@ else
 CPPSRCS += TimeStamp.cpp
 endif
 
 EXPORTS_NAMESPACES = mozilla
 
 EXPORTS_mozilla = \
   CharTokenizer.h \
   TimeStamp.h \
+  StringBuilder.h \
   $(NULL)
 
 EXPORTS		= \
 		nsAtomService.h \
 		nsCheapSets.h \
 		nsCppSharedAllocator.h \
 		nsCRT.h \
 		nsDoubleHashtable.h \
new file mode 100644
--- /dev/null
+++ b/xpcom/ds/StringBuilder.h
@@ -0,0 +1,116 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim:set ts=4 sw=4 sts=4 et cindent: */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla code.
+ *
+ * The Initial Developer of the Original Code is the Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *  Jeff Muizelaar <jmuizelaar@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 ***** */
+
+/* We would use std::max but MS makes it painful
+// windef.h defines min and max macros that we don't want
+// http://support.microsoft.com/kb/143208
+#ifdef _WIN32
+#define NOMINMAX
+#endif
+*/
+
+#include <stdlib.h>
+#include <string.h>
+#include "nsAlgorithm.h"
+
+/* This is a standard string builder like ones in Java
+   or C#. It uses a doubling allocation strategy
+   to grow when out of capacity.
+
+   This does not use nsTArray because nsTArray starts
+   growing by multiples of page size after it is the
+   size of one page. We want to keep doubling in size
+   so that we can continue to append at high speed even
+   for large strings.
+
+   Eventually, this should be templated for wide characters.
+
+ */
+
+namespace mozilla {
+
+class StringBuilder
+{
+public:
+    StringBuilder() {
+        mCapacity = 16;
+        mLength = 0;
+        mBuffer = static_cast<char*>(malloc(sizeof(char)*mCapacity));
+        mBuffer[0] = '\0';
+    }
+
+    void Append(const char *s) {
+        size_t newLength = strlen(s);
+
+        EnsureCapacity(mLength + newLength+1);
+
+        // copy the entire string including the null terminator
+        memcpy(&mBuffer[mLength], s, newLength+1);
+        mLength += newLength;
+    }
+
+    char *Buffer() {
+        return mBuffer;
+    }
+
+    size_t Length() {
+        return mLength;
+    }
+
+    size_t EnsureCapacity(size_t capacity) {
+        if (capacity > mCapacity) {
+            // make sure we at least double in size
+            mCapacity = NS_MAX(capacity, mCapacity*2);
+            mBuffer = static_cast<char*>(realloc(mBuffer, mCapacity));
+            mCapacity = moz_malloc_usable_size(mBuffer);
+        }
+        return mCapacity;
+    }
+
+    ~StringBuilder()
+    {
+        free(mBuffer);
+    }
+
+private:
+    char *mBuffer;
+    size_t mLength; // the length of the contained string not including the null terminator
+    size_t mCapacity; // the total size of mBuffer
+};
+
+}