Merge last PGO-safe changeset from inbound to central
authorMarco Bonardo <mbonardo@mozilla.com>
Fri, 09 Mar 2012 13:33:59 +0100
changeset 88616 ead9016b4102e8bcadc7f02cb47f54e9a890e2c2
parent 88525 89d3250b701d293eba8f6bad3eed6802659155f3 (current diff)
parent 88615 9059fb810f28f82bfeba318d6b9533c008f760d7 (diff)
child 88636 bfb1b7520ce9714dd7d089fb266fc40f004db923
child 89309 c6dcca94622e4b66c69ef48a03c256b5fde85295
push id22208
push usermak77@bonardo.net
push dateFri, 09 Mar 2012 12:34:50 +0000
treeherdermozilla-central@ead9016b4102 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone13.0a1
first release with
nightly linux32
ead9016b4102 / 13.0a1 / 20120309062528 / files
nightly linux64
ead9016b4102 / 13.0a1 / 20120309062528 / files
nightly mac
ead9016b4102 / 13.0a1 / 20120309062528 / files
nightly win32
ead9016b4102 / 13.0a1 / 20120309062528 / files
nightly win64
ead9016b4102 / 13.0a1 / 20120309062528 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge last PGO-safe changeset from inbound to central
browser/themes/gnomestripe/fullscreen-video.css
browser/themes/gnomestripe/section_collapsed-rtl.png
browser/themes/gnomestripe/section_collapsed.png
browser/themes/gnomestripe/section_expanded.png
browser/themes/pinstripe/fullscreen-video.css
browser/themes/pinstripe/section_collapsed-rtl.png
browser/themes/pinstripe/section_collapsed.png
browser/themes/pinstripe/section_expanded.png
browser/themes/winstripe/fullscreen-video.css
browser/themes/winstripe/section_collapsed-rtl.png
browser/themes/winstripe/section_collapsed.png
browser/themes/winstripe/section_expanded.png
build/unix/check_debug_ranges.py
content/base/public/nsIHTMLToTextSink.h
--- a/aclocal.m4
+++ b/aclocal.m4
@@ -13,16 +13,17 @@ builtin(include, build/autoconf/altoptio
 builtin(include, build/autoconf/mozprog.m4)dnl
 builtin(include, build/autoconf/mozheader.m4)dnl
 builtin(include, build/autoconf/mozcommonheader.m4)dnl
 builtin(include, build/autoconf/acwinpaths.m4)dnl
 builtin(include, build/autoconf/lto.m4)dnl
 builtin(include, build/autoconf/gcc-pr49911.m4)dnl
 builtin(include, build/autoconf/frameptr.m4)dnl
 builtin(include, build/autoconf/compiler-opts.m4)dnl
+builtin(include, build/autoconf/expandlibs.m4)dnl
 
 MOZ_PROG_CHECKMSYS()
 
 # Read the user's .mozconfig script.  We can't do this in
 # configure.in: autoconf puts the argument parsing code above anything
 # expanded from configure.in, and we need to get the configure options
 # from .mozconfig in place before that argument parsing code.
 MOZ_READ_MOZCONFIG(.)
--- a/browser/base/content/nsContextMenu.js
+++ b/browser/base/content/nsContextMenu.js
@@ -845,17 +845,17 @@ nsContextMenu.prototype = {
   saveVideoFrameAsImage: function () {
     urlSecurityCheck(this.mediaURL, this.browser.contentPrincipal,
                      Ci.nsIScriptSecurityManager.DISALLOW_SCRIPT);
     let name = "";
     try {
       let uri = makeURI(this.mediaURL);
       let url = uri.QueryInterface(Ci.nsIURL);
       if (url.fileBaseName)
-        name = url.fileBaseName + ".jpg";
+        name = decodeURI(url.fileBaseName) + ".jpg";
     } catch (e) { }
     if (!name)
       name = "snapshot.jpg";
     var video = this.target;
     var canvas = document.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
     canvas.width = video.videoWidth;
     canvas.height = video.videoHeight;
     var ctxDraw = canvas.getContext("2d");
--- a/browser/components/certerror/content/aboutCertError.css
+++ b/browser/components/certerror/content/aboutCertError.css
@@ -42,25 +42,11 @@
 /* Logical CSS rules belong here, but presentation & theming rules
    should live in the CSS of the appropriate theme */
 
 #technicalContentText {
   overflow: auto;
   white-space: pre-wrap;
 }
 
-#technicalContent > h2, #expertContent > h2 {
-  cursor: pointer;
-  -moz-padding-start: 20px;
-  position: relative;
-  left: -20px;
-}
-
-body[dir="rtl"] #technicalContent > h2,
-body[dir="rtl"] #expertContent > h2 {
-  left: auto;
-  right: -20px;
-}
-
-div[collapsed] > p,
-div[collapsed] > div {
+.expander[collapsed] + * {
   display: none;
 }
--- a/browser/components/certerror/content/aboutCertError.xhtml
+++ b/browser/components/certerror/content/aboutCertError.xhtml
@@ -255,28 +255,28 @@
           <div id="whatShouldIDoContentText">
             <p>&certerror.whatShouldIDo.content;</p>
             <button id='getMeOutOfHereButton'>&certerror.getMeOutOfHere.label;</button>
           </div>
         </div>
         
         <!-- The following sections can be unhidden by default by setting the
              "browser.xul.error_pages.expert_bad_cert" pref to true -->
-        <div id="technicalContent" collapsed="true">
-          <h2 onclick="toggle('technicalContent');" id="technicalContentHeading">&certerror.technical.heading;</h2>
-          <p id="technicalContentText"/>
-        </div>
+        <h2 id="technicalContent" class="expander" collapsed="true">
+          <button onclick="toggle('technicalContent');">&certerror.technical.heading;</button>
+        </h2>
+        <p id="technicalContentText"/>
         
-        <div id="expertContent" collapsed="true">
-          <h2 onclick="toggle('expertContent');" id="expertContentHeading">&certerror.expert.heading;</h2>
-          <div>
-            <p>&certerror.expert.content;</p>
-            <p>&certerror.expert.contentPara2;</p>
-            <button id='exceptionDialogButton'>&certerror.addException.label;</button>
-          </div>
+        <h2 id="expertContent" class="expander" collapsed="true">
+          <button onclick="toggle('expertContent');">&certerror.expert.heading;</button>
+        </h2>
+        <div>
+          <p>&certerror.expert.content;</p>
+          <p>&certerror.expert.contentPara2;</p>
+          <button id='exceptionDialogButton'>&certerror.addException.label;</button>
         </div>
       </div>
     </div>
 
     <!--
     - Note: It is important to run the script this way, instead of using
     - an onload handler. This is because error pages are loaded as
     - LOAD_BACKGROUND, which means that onload handlers will not be executed.
--- a/browser/components/dirprovider/DirectoryProvider.cpp
+++ b/browser/components/dirprovider/DirectoryProvider.cpp
@@ -51,16 +51,17 @@
 #include "nsCategoryManagerUtils.h"
 #include "nsComponentManagerUtils.h"
 #include "nsCOMArray.h"
 #include "nsDirectoryServiceUtils.h"
 #include "mozilla/ModuleUtils.h"
 #include "nsServiceManagerUtils.h"
 #include "nsStringAPI.h"
 #include "nsXULAppAPI.h"
+#include "nsIPrefLocalizedString.h"
 
 namespace mozilla {
 namespace browser {
 
 NS_IMPL_ISUPPORTS2(DirectoryProvider,
                    nsIDirectoryServiceProvider,
                    nsIDirectoryServiceProvider2)
 
@@ -195,17 +196,28 @@ AppendDistroSearchDirs(nsIProperties* aD
     nsCOMPtr<nsIFile> localePlugins;
     rv = searchPlugins->Clone(getter_AddRefs(localePlugins));
     if (NS_FAILED(rv))
       return;
 
     localePlugins->AppendNative(NS_LITERAL_CSTRING("locale"));
 
     nsCString locale;
-    rv = prefs->GetCharPref("general.useragent.locale", getter_Copies(locale));
+    nsCOMPtr<nsIPrefLocalizedString> prefString;
+    rv = prefs->GetComplexValue("general.useragent.locale",
+                                NS_GET_IID(nsIPrefLocalizedString),
+                                getter_AddRefs(prefString));
+    if (NS_SUCCEEDED(rv)) {
+      nsAutoString wLocale;
+      prefString->GetData(getter_Copies(wLocale));
+      CopyUTF16toUTF8(wLocale, locale);
+    } else {
+      rv = prefs->GetCharPref("general.useragent.locale", getter_Copies(locale));
+    }
+
     if (NS_SUCCEEDED(rv)) {
 
       nsCOMPtr<nsIFile> curLocalePlugins;
       rv = localePlugins->Clone(getter_AddRefs(curLocalePlugins));
       if (NS_SUCCEEDED(rv)) {
 
         curLocalePlugins->AppendNative(locale);
         rv = curLocalePlugins->Exists(&exists);
--- a/browser/components/places/content/browserPlacesViews.js
+++ b/browser/components/places/content/browserPlacesViews.js
@@ -652,17 +652,19 @@ PlacesViewBase.prototype = {
         PlacesUtils.livemarks.getLivemark({ id: aPlacesNode.itemId },
           (function (aStatus, aLivemark) {
             if (Components.isSuccessCode(aStatus)) {
               let shouldInvalidate = !aPlacesNode._feedURI;
               aPlacesNode._feedURI = aLivemark.feedURI;
               aPlacesNode._siteURI = aLivemark.siteURI;
               if (aNewState == Ci.nsINavHistoryContainerResultNode.STATE_OPENED) {
                 aLivemark.registerForUpdates(aPlacesNode, this);
+                // Prioritize the current livemark.
                 aLivemark.reload();
+                PlacesUtils.livemarks.reloadLivemarks();
                 if (shouldInvalidate)
                   this.invalidateContainer(aPlacesNode);
               }
               else {
                 aLivemark.unregisterForUpdates(aPlacesNode);
               }
             }
           }).bind(this)
@@ -895,21 +897,16 @@ function PlacesToolbar(aPlace) {
   Services.telemetry.getHistogramById("FX_BOOKMARKS_TOOLBAR_INIT_MS")
                     .add(Date.now() - startTime);
 }
 
 PlacesToolbar.prototype = {
   __proto__: PlacesViewBase.prototype,
 
   _cbEvents: ["dragstart", "dragover", "dragexit", "dragend", "drop",
-#ifdef XP_UNIX
-#ifndef XP_MACOSX
-              "mousedown", "mouseup",
-#endif
-#endif
               "mousemove", "mouseover", "mouseout"],
 
   QueryInterface: function PT_QueryInterface(aIID) {
     if (aIID.equals(Ci.nsIDOMEventListener) ||
         aIID.equals(Ci.nsITimerCallback))
       return this;
 
     return PlacesViewBase.prototype.QueryInterface.apply(this, arguments);
@@ -1096,26 +1093,16 @@ PlacesToolbar.prototype = {
         this._onMouseOver(aEvent);
         break;
       case "mousemove":
         this._onMouseMove(aEvent);
         break;
       case "mouseout":
         this._onMouseOut(aEvent);
         break;
-#ifdef XP_UNIX
-#ifndef XP_MACOSX
-      case "mouseup":
-        this._onMouseUp(aEvent);
-        break;
-      case "mousedown":
-        this._onMouseDown(aEvent);
-        break;
-#endif
-#endif
       case "popupshowing":
         this._onPopupShowing(aEvent);
         break;
       case "popuphidden":
         this._onPopupHidden(aEvent);
         break;
       default:
         throw "Trying to handle unexpected event.";
@@ -1533,24 +1520,16 @@ PlacesToolbar.prototype = {
     let draggedElt = aEvent.target;
     if (draggedElt.parentNode != this._rootElt || !draggedElt._placesNode)
       return;
 
     if (draggedElt.localName == "toolbarbutton" &&
         draggedElt.getAttribute("type") == "menu") {
       // If the drag gesture on a container is toward down we open instead
       // of dragging.
-#ifdef XP_UNIX
-#ifndef XP_MACOSX
-      if (this._mouseDownTimer) {
-        this._mouseDownTimer.cancel();
-        this._mouseDownTimer = null;
-      }
-#endif
-#endif
       let translateY = this._cachedMouseMoveEvent.clientY - aEvent.clientY;
       let translateX = this._cachedMouseMoveEvent.clientX - aEvent.clientX;
       if ((translateY) >= Math.abs(translateX/2)) {
         // Don't start the drag.
         aEvent.preventDefault();
         // Open the menu.
         draggedElt.open = true;
         return;
@@ -1713,57 +1692,16 @@ PlacesToolbar.prototype = {
       // Clear the dragover attribute if present, if we are dragging into a
       // folder in the hierachy of current opened popup we don't clear
       // this attribute on clearOverFolder.  See Notify for closeTimer.
       if (parent.hasAttribute("dragover"))
         parent.removeAttribute("dragover");
     }
   },
 
-#ifdef XP_UNIX
-#ifndef XP_MACOSX
-  _onMouseDown: function PT__onMouseDown(aEvent) {
-    let target = aEvent.target;
-    if (aEvent.button == 0 &&
-        target.localName == "toolbarbutton" &&
-        target.getAttribute("type") == "menu") {
-      this._allowPopupShowing = false;
-      // On Linux we can open the popup only after a delay.
-      // Indeed as soon as the menupopup opens we are unable to start a
-      // drag aEvent.  See bug 500081 for details.
-      this._mouseDownTimer = Cc["@mozilla.org/timer;1"].
-                             createInstance(Ci.nsITimer);
-      let callback = {
-        _self: this,
-        _target: target,
-        notify: function(timer) {
-          this._target.open = true;
-          this._mouseDownTimer = null;
-        }
-      };
-
-      this._mouseDownTimer.initWithCallback(callback, 300,
-                                            Ci.nsITimer.TYPE_ONE_SHOT);
-    }
-  },
-
-  _onMouseUp: function PT__onMouseUp(aEvent) {
-    if (aEvent.button != 0)
-      return;
-
-    if (this._mouseDownTimer) {
-      // On a click (down/up), we should open the menu popup.
-      this._mouseDownTimer.cancel();
-      this._mouseDownTimer = null;
-      aEvent.target.open = true;
-    }
-  },
-#endif
-#endif
-
   _onMouseMove: function PT__onMouseMove(aEvent) {
     // Used in dragStart to prevent dragging folders when dragging down.
     this._cachedMouseMoveEvent = aEvent;
 
     if (this._openedMenuButton == null ||
         PlacesControllerDragHelper.getSession())
       return;
 
--- a/browser/components/places/content/editBookmarkOverlay.xul
+++ b/browser/components/places/content/editBookmarkOverlay.xul
@@ -85,29 +85,27 @@
         <row align="center" id="editBMPanel_feedLocationRow">
           <label value="&editBookmarkOverlay.feedLocation.label;"
                  class="editBMPanel_rowLabel"
                  accesskey="&editBookmarkOverlay.feedLocation.accesskey;"
                  control="editBMPanel_feedLocationField"
                  observes="paneElementsBroadcaster"/>
           <textbox id="editBMPanel_feedLocationField"
                    class="uri-element"
-                   onblur="gEditItemOverlay.onFeedLocationFieldBlur();"
                    observes="paneElementsBroadcaster"/>
         </row>
 
         <row align="center" id="editBMPanel_siteLocationRow">
           <label value="&editBookmarkOverlay.siteLocation.label;"
                  class="editBMPanel_rowLabel"
                  accesskey="&editBookmarkOverlay.siteLocation.accesskey;"
                  control="editBMPanel_siteLocationField"
                  observes="paneElementsBroadcaster"/>
           <textbox id="editBMPanel_siteLocationField"
                    class="uri-element"
-                   onblur="gEditItemOverlay.onSiteLocationFieldBlur();"
                    observes="paneElementsBroadcaster"/>
         </row>
 
         <row align="center" id="editBMPanel_folderRow">
           <label value="&editBookmarkOverlay.folder.label;"
                  class="editBMPanel_rowLabel"
                  control="editBMPanel_folderMenuList"
                  observes="paneElementsBroadcaster"/>
--- a/browser/components/places/content/treeView.js
+++ b/browser/components/places/content/treeView.js
@@ -891,17 +891,19 @@ PlacesTreeView.prototype = {
 
       PlacesUtils.livemarks.getLivemark({ id: aNode.itemId },
         (function (aStatus, aLivemark) {
           if (Components.isSuccessCode(aStatus)) {
             let shouldInvalidate = !aNode._feedURI;
             aNode._feedURI = aLivemark.feedURI;
             if (aNewState == Components.interfaces.nsINavHistoryContainerResultNode.STATE_OPENED) {
               aLivemark.registerForUpdates(aNode, this);
+              // Prioritize the current livemark.
               aLivemark.reload();
+              PlacesUtils.livemarks.reloadLivemarks();
               if (shouldInvalidate)
                 this.invalidateContainer(aNode);
             }
             else {
               aLivemark.unregisterForUpdates(aNode);
             }
           }
         }).bind(this)
--- a/browser/themes/gnomestripe/aboutCertError.css
+++ b/browser/themes/gnomestripe/aboutCertError.css
@@ -81,26 +81,29 @@ body[dir="rtl"] #errorPageContainer {
 #errorTitle {
   -moz-margin-start: 80px;
 }
 
 #errorLongContent {
   -moz-margin-start: 80px;
 }
 
-#technicalContent > h2, #expertContent > h2 {
-  background : url("chrome://browser/skin/section_expanded.png") left 0 no-repeat;
-}
-
-body[dir="rtl"] #technicalContent > h2,
-body[dir="rtl"] #expertContent > h2 {
-  background-position: right 0;
+.expander > button {
+  -moz-padding-start: 20px;
+  -moz-margin-start: -20px;
+  background: url("chrome://browser/skin/aboutCertError_sectionExpanded.png") left center no-repeat;
+  border: none;
+  font: inherit;
+  color: inherit;
+  cursor: pointer;
 }
 
-#technicalContent[collapsed] > h2,
-#expertContent[collapsed] > h2{
-  background-image: url("chrome://browser/skin/section_collapsed.png");
+body[dir="rtl"] .expander > button {
+  background-position: right center;
 }
 
-body[dir="rtl"] #technicalContent[collapsed] > h2,
-body[dir="rtl"] #expertContent[collapsed] > h2 {
-  background-image: url("chrome://browser/skin/section_collapsed-rtl.png");
+.expander[collapsed] > button {
+  background-image: url("chrome://browser/skin/aboutCertError_sectionCollapsed.png");
 }
+
+body[dir="rtl"] .expander[collapsed] > button {
+  background-image: url("chrome://browser/skin/aboutCertError_sectionCollapsed-rtl.png");
+}
rename from browser/themes/gnomestripe/section_collapsed-rtl.png
rename to browser/themes/gnomestripe/aboutCertError_sectionCollapsed-rtl.png
rename from browser/themes/gnomestripe/section_collapsed.png
rename to browser/themes/gnomestripe/aboutCertError_sectionCollapsed.png
rename from browser/themes/gnomestripe/section_expanded.png
rename to browser/themes/gnomestripe/aboutCertError_sectionExpanded.png
deleted file mode 100644
--- a/browser/themes/gnomestripe/fullscreen-video.css
+++ /dev/null
@@ -1,8 +0,0 @@
-#close {
-  position: absolute;
-  top: 0;
-  right: 0;
-  width: 32px;
-  height: 32px;
-  background: url(KUI-close.png) center center no-repeat;
-}
--- a/browser/themes/gnomestripe/jar.mn
+++ b/browser/themes/gnomestripe/jar.mn
@@ -1,40 +1,39 @@
 browser.jar:
 % skin browser classic/1.0 %skin/classic/browser/
 % override chrome://global/skin/icons/warning-16.png moz-icon://stock/gtk-dialog-warning?size=menu
   skin/classic/browser/sanitizeDialog.css             (sanitizeDialog.css)
 * skin/classic/browser/aboutPrivateBrowsing.css             (aboutPrivateBrowsing.css)
 * skin/classic/browser/aboutSessionRestore.css        (aboutSessionRestore.css)
   skin/classic/browser/aboutSessionRestore-window-icon.png
-  skin/classic/browser/aboutCertError.css             (aboutCertError.css)
+  skin/classic/browser/aboutCertError.css
+  skin/classic/browser/aboutCertError_sectionCollapsed.png
+  skin/classic/browser/aboutCertError_sectionCollapsed-rtl.png
+  skin/classic/browser/aboutCertError_sectionExpanded.png
 #ifdef MOZ_SERVICES_SYNC
   skin/classic/browser/aboutSyncTabs.css
 #endif
   skin/classic/browser/actionicon-tab.png
 * skin/classic/browser/browser.css                    (browser.css)
 * skin/classic/browser/engineManager.css              (engineManager.css)
-  skin/classic/browser/fullscreen-video.css
   skin/classic/browser/Geolocation-16.png
   skin/classic/browser/Geolocation-64.png
   skin/classic/browser/Go-arrow.png
   skin/classic/browser/identity.png
   skin/classic/browser/Info.png
   skin/classic/browser/KUI-close.png
   skin/classic/browser/monitor.png
   skin/classic/browser/monitor_16-10.png
 * skin/classic/browser/pageInfo.css
   skin/classic/browser/pageInfo.png
   skin/classic/browser/page-livemarks.png
   skin/classic/browser/Privacy-16.png
   skin/classic/browser/Privacy-48.png
   skin/classic/browser/searchbar.css                  (searchbar.css)
-  skin/classic/browser/section_collapsed.png
-  skin/classic/browser/section_collapsed-rtl.png
-  skin/classic/browser/section_expanded.png
   skin/classic/browser/Secure.png
   skin/classic/browser/Security-broken.png
   skin/classic/browser/setDesktopBackground.css
   skin/classic/browser/Toolbar.png
   skin/classic/browser/Toolbar-small.png
   skin/classic/browser/urlbar-arrow.png
   skin/classic/browser/feeds/feedIcon.png             (feeds/feedIcon.png)
   skin/classic/browser/feeds/feedIcon16.png           (feeds/feedIcon16.png)
--- a/browser/themes/pinstripe/aboutCertError.css
+++ b/browser/themes/pinstripe/aboutCertError.css
@@ -81,26 +81,29 @@ body[dir="rtl"] #errorPageContainer {
 #errorTitle {
   -moz-margin-start: 80px;
 }
 
 #errorLongContent {
   -moz-margin-start: 80px;
 }
 
-#technicalContent > h2, #expertContent > h2 {
-  background : url("chrome://browser/skin/section_expanded.png") left 0 no-repeat;
-}
-
-body[dir="rtl"] #technicalContent > h2,
-body[dir="rtl"] #expertContent > h2 {
-  background-position: right 0;
+.expander > button {
+  -moz-padding-start: 20px;
+  -moz-margin-start: -20px;
+  background: url("chrome://browser/skin/aboutCertError_sectionExpanded.png") left center no-repeat;
+  border: none;
+  font: inherit;
+  color: inherit;
+  cursor: pointer;
 }
 
-#technicalContent[collapsed] > h2,
-#expertContent[collapsed] > h2{
-  background-image: url("chrome://browser/skin/section_collapsed.png");
+body[dir="rtl"] .expander > button {
+  background-position: right center;
 }
 
-body[dir="rtl"] #technicalContent[collapsed] > h2,
-body[dir="rtl"] #expertContent[collapsed] > h2 {
-  background-image: url("chrome://browser/skin/section_collapsed-rtl.png");
+.expander[collapsed] > button {
+  background-image: url("chrome://browser/skin/aboutCertError_sectionCollapsed.png");
 }
+
+body[dir="rtl"] .expander[collapsed] > button {
+  background-image: url("chrome://browser/skin/aboutCertError_sectionCollapsed-rtl.png");
+}
rename from browser/themes/pinstripe/section_collapsed-rtl.png
rename to browser/themes/pinstripe/aboutCertError_sectionCollapsed-rtl.png
rename from browser/themes/pinstripe/section_collapsed.png
rename to browser/themes/pinstripe/aboutCertError_sectionCollapsed.png
rename from browser/themes/pinstripe/section_expanded.png
rename to browser/themes/pinstripe/aboutCertError_sectionExpanded.png
deleted file mode 100644
--- a/browser/themes/pinstripe/fullscreen-video.css
+++ /dev/null
@@ -1,8 +0,0 @@
-#close {
-  position: absolute;
-  top: 0;
-  left: 0;
-  width: 32px;
-  height: 32px;
-  background: url(KUI-close.png) center center no-repeat;
-}
--- a/browser/themes/pinstripe/jar.mn
+++ b/browser/themes/pinstripe/jar.mn
@@ -1,22 +1,24 @@
 browser.jar:
 % skin browser classic/1.0 %skin/classic/browser/
   skin/classic/browser/sanitizeDialog.css                   (sanitizeDialog.css)
 * skin/classic/browser/aboutPrivateBrowsing.css             (aboutPrivateBrowsing.css)
 * skin/classic/browser/aboutSessionRestore.css              (aboutSessionRestore.css)
   skin/classic/browser/aboutSessionRestore-window-icon.png
-  skin/classic/browser/aboutCertError.css                   (aboutCertError.css)
+  skin/classic/browser/aboutCertError.css
+  skin/classic/browser/aboutCertError_sectionCollapsed.png
+  skin/classic/browser/aboutCertError_sectionCollapsed-rtl.png
+  skin/classic/browser/aboutCertError_sectionExpanded.png
 #ifdef MOZ_SERVICES_SYNC
   skin/classic/browser/aboutSyncTabs.css
 #endif
   skin/classic/browser/actionicon-tab.png
 * skin/classic/browser/browser.css                          (browser.css)
 * skin/classic/browser/engineManager.css                    (engineManager.css)
-  skin/classic/browser/fullscreen-video.css
   skin/classic/browser/Geolocation-16.png
   skin/classic/browser/Geolocation-64.png
   skin/classic/browser/home.png
   skin/classic/browser/hud-style-check-box-checked.png
   skin/classic/browser/hud-style-check-box-empty.png
   skin/classic/browser/hud-style-dropmarker-double-arrows.png
   skin/classic/browser/hud-style-expander-closed.png
   skin/classic/browser/hud-style-expander-open.png
@@ -31,19 +33,16 @@ browser.jar:
   skin/classic/browser/page-livemarks.png
   skin/classic/browser/pageInfo.css
   skin/classic/browser/Privacy-16.png
   skin/classic/browser/Privacy-48.png
   skin/classic/browser/reload-stop-go.png
   skin/classic/browser/searchbar-dropmarker.png
   skin/classic/browser/searchbar.css
   skin/classic/browser/Search.png
-  skin/classic/browser/section_collapsed.png
-  skin/classic/browser/section_collapsed-rtl.png
-  skin/classic/browser/section_expanded.png
   skin/classic/browser/Secure-Glyph-White.png
   skin/classic/browser/keyhole-circle.png
   skin/classic/browser/Toolbar.png
   skin/classic/browser/toolbarbutton-dropmarker.png
   skin/classic/browser/urlbar-history-dropmarker.png
   skin/classic/browser/urlbar-arrow.png
   skin/classic/browser/urlbar-popup-blocked.png
   skin/classic/browser/feeds/subscribe.css                  (feeds/subscribe.css)
--- a/browser/themes/winstripe/aboutCertError.css
+++ b/browser/themes/winstripe/aboutCertError.css
@@ -81,26 +81,29 @@ body[dir="rtl"] #errorPageContainer {
 #errorTitle {
   -moz-margin-start: 80px;
 }
 
 #errorLongContent {
   -moz-margin-start: 80px;
 }
 
-#technicalContent > h2, #expertContent > h2 {
-  background : url("chrome://browser/skin/section_expanded.png") left center no-repeat;
+.expander > button {
+  -moz-padding-start: 20px;
+  -moz-margin-start: -20px;
+  background: url("chrome://browser/skin/aboutCertError_sectionExpanded.png") left center no-repeat;
+  border: none;
+  font: inherit;
+  color: inherit;
+  cursor: pointer;
 }
 
-body[dir="rtl"] #technicalContent > h2,
-body[dir="rtl"] #expertContent > h2 {
+body[dir="rtl"] .expander > button {
   background-position: right center;
 }
 
-#technicalContent[collapsed] > h2,
-#expertContent[collapsed] > h2{
-  background-image: url("chrome://browser/skin/section_collapsed.png");
+.expander[collapsed] > button {
+  background-image: url("chrome://browser/skin/aboutCertError_sectionCollapsed.png");
 }
 
-body[dir="rtl"] #technicalContent[collapsed] > h2,
-body[dir="rtl"] #expertContent[collapsed] > h2 {
-  background-image: url("chrome://browser/skin/section_collapsed-rtl.png");
+body[dir="rtl"] .expander[collapsed] > button {
+  background-image: url("chrome://browser/skin/aboutCertError_sectionCollapsed-rtl.png");
 }
rename from browser/themes/winstripe/section_collapsed-rtl.png
rename to browser/themes/winstripe/aboutCertError_sectionCollapsed-rtl.png
rename from browser/themes/winstripe/section_collapsed.png
rename to browser/themes/winstripe/aboutCertError_sectionCollapsed.png
rename from browser/themes/winstripe/section_expanded.png
rename to browser/themes/winstripe/aboutCertError_sectionExpanded.png
deleted file mode 100644
--- a/browser/themes/winstripe/fullscreen-video.css
+++ /dev/null
@@ -1,8 +0,0 @@
-#close {
-  position: absolute;
-  top: 0;
-  right: 0;
-  width: 32px;
-  height: 32px;
-  background: url(KUI-close.png) center center no-repeat;
-}
--- a/browser/themes/winstripe/jar.mn
+++ b/browser/themes/winstripe/jar.mn
@@ -2,26 +2,28 @@ browser.jar:
 % skin browser classic/1.0 %skin/classic/browser/ os=WINNT osversion<6
 % skin browser classic/1.0 %skin/classic/browser/ os!=WINNT
 # NOTE: If you add a new file here, you'll need to add it to the aero
 # section at the bottom of this file
         skin/classic/browser/sanitizeDialog.css                      (sanitizeDialog.css)
 *       skin/classic/browser/aboutPrivateBrowsing.css                (aboutPrivateBrowsing.css)
 *       skin/classic/browser/aboutSessionRestore.css                 (aboutSessionRestore.css)
         skin/classic/browser/aboutSessionRestore-window-icon.png     (preferences/application.png)
-        skin/classic/browser/aboutCertError.css                      (aboutCertError.css)
+        skin/classic/browser/aboutCertError.css
+        skin/classic/browser/aboutCertError_sectionCollapsed.png
+        skin/classic/browser/aboutCertError_sectionCollapsed-rtl.png
+        skin/classic/browser/aboutCertError_sectionExpanded.png
 #ifdef MOZ_SERVICES_SYNC
         skin/classic/browser/aboutSyncTabs.css
 #endif
         skin/classic/browser/actionicon-tab.png
         skin/classic/browser/appmenu-icons.png
         skin/classic/browser/appmenu-dropmarker.png
 *       skin/classic/browser/browser.css                             (browser.css)
 *       skin/classic/browser/engineManager.css                       (engineManager.css)
-        skin/classic/browser/fullscreen-video.css
         skin/classic/browser/Geolocation-16.png
         skin/classic/browser/Geolocation-64.png
         skin/classic/browser/Info.png                                (Info.png)
         skin/classic/browser/identity.png                            (identity.png)
         skin/classic/browser/keyhole-forward-mask.svg
         skin/classic/browser/KUI-background.png
         skin/classic/browser/KUI-close.png
         skin/classic/browser/pageInfo.css
@@ -33,19 +35,16 @@ browser.jar:
         skin/classic/browser/reload-stop-go.png
         skin/classic/browser/Secure24.png                            (Secure24.png)
         skin/classic/browser/Toolbar.png                             (Toolbar.png)
         skin/classic/browser/Toolbar-inverted.png
         skin/classic/browser/toolbarbutton-dropdown-arrow.png
         skin/classic/browser/toolbarbutton-dropdown-arrow-inverted.png
 *       skin/classic/browser/searchbar.css                           (searchbar.css)
         skin/classic/browser/searchbar-dropdown-arrow.png
-        skin/classic/browser/section_collapsed.png
-        skin/classic/browser/section_collapsed-rtl.png
-        skin/classic/browser/section_expanded.png
         skin/classic/browser/setDesktopBackground.css
         skin/classic/browser/menu-back.png                           (menu-back.png)
         skin/classic/browser/menu-forward.png                        (menu-forward.png)
         skin/classic/browser/monitor.png
         skin/classic/browser/monitor_16-10.png
         skin/classic/browser/urlbar-arrow.png
         skin/classic/browser/urlbar-popup-blocked.png
         skin/classic/browser/urlbar-history-dropmarker.png
@@ -172,26 +171,28 @@ browser.jar:
 
 #ifdef XP_WIN
 browser.jar:
 % skin browser classic/1.0 %skin/classic/aero/browser/ os=WINNT osversion>=6
         skin/classic/aero/browser/sanitizeDialog.css                       (sanitizeDialog.css)
 *       skin/classic/aero/browser/aboutPrivateBrowsing.css           (aboutPrivateBrowsing.css)
 *       skin/classic/aero/browser/aboutSessionRestore.css            (aboutSessionRestore.css)
         skin/classic/aero/browser/aboutSessionRestore-window-icon.png (aboutSessionRestore-window-icon-aero.png)
-        skin/classic/aero/browser/aboutCertError.css                 (aboutCertError.css)
+        skin/classic/aero/browser/aboutCertError.css
+        skin/classic/aero/browser/aboutCertError_sectionCollapsed.png
+        skin/classic/aero/browser/aboutCertError_sectionCollapsed-rtl.png
+        skin/classic/aero/browser/aboutCertError_sectionExpanded.png
 #ifdef MOZ_SERVICES_SYNC
         skin/classic/aero/browser/aboutSyncTabs.css
 #endif
         skin/classic/aero/browser/actionicon-tab.png                 (actionicon-tab.png)
         skin/classic/aero/browser/appmenu-dropmarker.png
         skin/classic/aero/browser/appmenu-icons.png
 *       skin/classic/aero/browser/browser.css                        (browser-aero.css)
 *       skin/classic/aero/browser/engineManager.css                  (engineManager.css)
-        skin/classic/aero/browser/fullscreen-video.css
         skin/classic/aero/browser/Geolocation-16.png
         skin/classic/aero/browser/Geolocation-64.png
         skin/classic/aero/browser/Info.png                           (Info-aero.png)
         skin/classic/aero/browser/identity.png                       (identity-aero.png)
         skin/classic/aero/browser/keyhole-forward-mask.svg
         skin/classic/aero/browser/KUI-background.png
         skin/classic/aero/browser/KUI-close.png
         skin/classic/aero/browser/pageInfo.css
@@ -203,19 +204,16 @@ browser.jar:
         skin/classic/aero/browser/reload-stop-go.png
         skin/classic/aero/browser/Secure24.png                       (Secure24-aero.png)
         skin/classic/aero/browser/Toolbar.png
         skin/classic/aero/browser/Toolbar-inverted.png
         skin/classic/aero/browser/toolbarbutton-dropdown-arrow.png
         skin/classic/aero/browser/toolbarbutton-dropdown-arrow-inverted.png
 *       skin/classic/aero/browser/searchbar.css                      (searchbar.css)
         skin/classic/aero/browser/searchbar-dropdown-arrow.png       (searchbar-dropdown-arrow-aero.png)
-        skin/classic/aero/browser/section_collapsed.png
-        skin/classic/aero/browser/section_collapsed-rtl.png
-        skin/classic/aero/browser/section_expanded.png
         skin/classic/aero/browser/setDesktopBackground.css
         skin/classic/aero/browser/menu-back.png                      (menu-back-aero.png)
         skin/classic/aero/browser/menu-forward.png                   (menu-forward-aero.png)
         skin/classic/aero/browser/monitor.png
         skin/classic/aero/browser/monitor_16-10.png
         skin/classic/aero/browser/urlbar-arrow.png
         skin/classic/aero/browser/urlbar-popup-blocked.png
         skin/classic/aero/browser/urlbar-history-dropmarker.png
rename from build/unix/check_debug_ranges.py
rename to build/autoconf/check_debug_ranges.py
--- a/build/autoconf/compiler-opts.m4
+++ b/build/autoconf/compiler-opts.m4
@@ -4,10 +4,81 @@ AC_DEFUN([MOZ_COMPILER_OPTS],
 [
 if test "$CLANG_CXX"; then
     ## We disable return-type-c-linkage because jsval is defined as a C++ type but is
     ## returned by C functions. This is possible because we use knowledge about the ABI
     ## to typedef it to a C type with the same layout when the headers are included
     ## from C.
     _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Wno-unknown-warning-option -Wno-return-type-c-linkage"
 fi
+
+if test "$GNU_CC"; then
+    CFLAGS="$CFLAGS -ffunction-sections -fdata-sections"
+    CXXFLAGS="$CXXFLAGS -ffunction-sections -fdata-sections"
+fi
+
+dnl ========================================================
+dnl = Identical Code Folding
+dnl ========================================================
+
+MOZ_ARG_DISABLE_BOOL(icf,
+[  --disable-icf          Disable Identical Code Folding],
+    MOZ_DISABLE_ICF=1,
+    MOZ_DISABLE_ICF= )
+
+if test "$GNU_CC" -a "$GCC_USE_GNU_LD" -a -z "$MOZ_DISABLE_ICF"; then
+    AC_CACHE_CHECK([whether the linker supports Identical Code Folding],
+        LD_SUPPORTS_ICF,
+        [echo 'int foo() {return 42;}' \
+              'int bar() {return 42;}' \
+              'int main() {return foo() - bar();}' > conftest.${ac_ext}
+        # If the linker supports ICF, foo and bar symbols will have
+        # the same address
+        if AC_TRY_COMMAND([${CC-cc} -o conftest${ac_exeext} $LDFLAGS -Wl,--icf=safe -ffunction-sections conftest.${ac_ext} $LIBS 1>&2]) &&
+           test -s conftest${ac_exeext} &&
+           objdump -t conftest${ac_exeext} | awk changequote(<<, >>)'{a[<<$>>6] = <<$>>1} END {if (a["foo"] && (a["foo"] != a["bar"])) { exit 1 }}'changequote([, ]); then
+            LD_SUPPORTS_ICF=yes
+        else
+            LD_SUPPORTS_ICF=no
+        fi
+        rm -rf conftest*])
+    if test "$LD_SUPPORTS_ICF" = yes; then
+        _SAVE_LDFLAGS="$LDFLAGS -Wl,--icf=safe"
+        LDFLAGS="$LDFLAGS -Wl,--icf=safe -Wl,--print-icf-sections"
+        AC_TRY_LINK([], [],
+                    [LD_PRINT_ICF_SECTIONS=-Wl,--print-icf-sections],
+                    [LD_PRINT_ICF_SECTIONS=])
+        AC_SUBST([LD_PRINT_ICF_SECTIONS])
+        LDFLAGS="$_SAVE_LDFLAGS"
+    fi
+fi
+
+dnl ========================================================
+dnl = Automatically remove dead symbols
+dnl ========================================================
+
+if test "$GNU_CC" -a "$GCC_USE_GNU_LD" -a -n "$MOZ_DEBUG_FLAGS"; then
+   dnl See bug 670659
+   AC_CACHE_CHECK([whether removing dead symbols breaks debugging],
+       GC_SECTIONS_BREAKS_DEBUG_RANGES,
+       [echo 'int foo() {return 42;}' \
+             'int bar() {return 1;}' \
+             'int main() {return foo();}' > conftest.${ac_ext}
+        if AC_TRY_COMMAND([${CC-cc} -o conftest.${ac_objext} $CFLAGS $MOZ_DEBUG_FLAGS -c conftest.${ac_ext} 1>&2]) &&
+           AC_TRY_COMMAND([${CC-cc} -o conftest${ac_exeext} $LDFLAGS $MOZ_DEBUG_FLAGS -Wl,--gc-sections conftest.${ac_objext} $LIBS 1>&2]) &&
+           test -s conftest${ac_exeext} -a -s conftest.${ac_objext}; then
+            if test "`$PYTHON "$_topsrcdir"/build/autoconf/check_debug_ranges.py conftest.${ac_objext} conftest.${ac_ext}`" = \
+                    "`$PYTHON "$_topsrcdir"/build/autoconf/check_debug_ranges.py conftest${ac_exeext} conftest.${ac_ext}`"; then
+                GC_SECTIONS_BREAKS_DEBUG_RANGES=no
+            else
+                GC_SECTIONS_BREAKS_DEBUG_RANGES=yes
+            fi
+        else
+             dnl We really don't expect to get here, but just in case
+             GC_SECTIONS_BREAKS_DEBUG_RANGES="no, but it's broken in some other way"
+        fi
+        rm -rf conftest*])
+    if test "$GC_SECTIONS_BREAKS_DEBUG_RANGES" = no; then
+        DSO_LDOPTS="$DSO_LDOPTS -Wl,--gc-sections"
+    fi
+fi
+
 ])
-
new file mode 100644
--- /dev/null
+++ b/build/autoconf/expandlibs.m4
@@ -0,0 +1,56 @@
+AC_DEFUN([MOZ_EXPAND_LIBS],
+[
+dnl ========================================================
+dnl =
+dnl = Check what kind of list files are supported by the
+dnl = linker
+dnl =
+dnl ========================================================
+
+AC_CACHE_CHECK(what kind of list files are supported by the linker,
+    EXPAND_LIBS_LIST_STYLE,
+    [echo "int main() {return 0;}" > conftest.${ac_ext}
+     if AC_TRY_COMMAND(${CC-cc} -o conftest.${OBJ_SUFFIX} -c $CFLAGS $CPPFLAGS conftest.${ac_ext} 1>&5) && test -s conftest.${OBJ_SUFFIX}; then
+         echo "INPUT(conftest.${OBJ_SUFFIX})" > conftest.list
+         if AC_TRY_COMMAND(${CC-cc} -o conftest${ac_exeext} $LDFLAGS conftest.list $LIBS 1>&5) && test -s conftest${ac_exeext}; then
+             EXPAND_LIBS_LIST_STYLE=linkerscript
+         else
+             echo "conftest.${OBJ_SUFFIX}" > conftest.list
+             if AC_TRY_COMMAND(${CC-cc} -o conftest${ac_exeext} $LDFLAGS @conftest.list $LIBS 1>&5) && test -s conftest${ac_exeext}; then
+                 EXPAND_LIBS_LIST_STYLE=list
+             else
+                 EXPAND_LIBS_LIST_STYLE=none
+             fi
+         fi
+     else
+         dnl We really don't expect to get here, but just in case
+         AC_ERROR([couldn't compile a simple C file])
+     fi
+     rm -rf conftest*])
+
+LIBS_DESC_SUFFIX=desc
+AC_SUBST(LIBS_DESC_SUFFIX)
+AC_SUBST(EXPAND_LIBS_LIST_STYLE)
+
+if test "$GCC_USE_GNU_LD"; then
+    AC_CACHE_CHECK(what kind of ordering can be done with the linker,
+        EXPAND_LIBS_ORDER_STYLE,
+        [> conftest.order
+         _SAVE_LDFLAGS="$LDFLAGS"
+         LDFLAGS="${LDFLAGS} -Wl,--section-ordering-file,conftest.order"
+         AC_TRY_LINK([], [],
+             EXPAND_LIBS_ORDER_STYLE=section-ordering-file,
+             EXPAND_LIBS_ORDER_STYLE=)
+         LDFLAGS="$_SAVE_LDFLAGS"
+         if test -z "$EXPAND_LIBS_ORDER_STYLE"; then
+             if AC_TRY_COMMAND(${CC-cc} ${DSO_LDOPTS} ${LDFLAGS} -o ${DLL_PREFIX}conftest${DLL_SUFFIX} -Wl,--verbose 2> /dev/null | sed -n '/^===/,/^===/p' | grep '\.text'); then
+                 EXPAND_LIBS_ORDER_STYLE=linkerscript
+             else
+                 EXPAND_LIBS_ORDER_STYLE=none
+             fi
+             rm -f ${DLL_PREFIX}conftest${DLL_SUFFIX}
+         fi])
+fi
+AC_SUBST(EXPAND_LIBS_ORDER_STYLE)
+
+])
--- a/config/config.mk
+++ b/config/config.mk
@@ -784,18 +784,22 @@ OPTIMIZE_JARS_CMD = $(PYTHON) $(call cor
 CREATE_PRECOMPLETE_CMD = $(PYTHON) $(call core_abspath,$(topsrcdir)/config/createprecomplete.py)
 
 EXPAND_LIBS = $(PYTHON) -I$(DEPTH)/config $(topsrcdir)/config/expandlibs.py
 EXPAND_LIBS_EXEC = $(PYTHON) $(topsrcdir)/config/pythonpath.py -I$(DEPTH)/config $(topsrcdir)/config/expandlibs_exec.py
 EXPAND_LIBS_GEN = $(PYTHON) $(topsrcdir)/config/pythonpath.py -I$(DEPTH)/config $(topsrcdir)/config/expandlibs_gen.py
 EXPAND_AR = $(EXPAND_LIBS_EXEC) --extract -- $(AR)
 EXPAND_CC = $(EXPAND_LIBS_EXEC) --uselist -- $(CC)
 EXPAND_CCC = $(EXPAND_LIBS_EXEC) --uselist -- $(CCC)
-EXPAND_LD = $(EXPAND_LIBS_EXEC) --uselist $(if $(REORDER),--reorder $(REORDER))-- $(LD)
-EXPAND_MKSHLIB = $(EXPAND_LIBS_EXEC) --uselist $(if $(REORDER),--reorder $(REORDER))-- $(MKSHLIB)
+EXPAND_LD = $(EXPAND_LIBS_EXEC) --uselist -- $(LD)
+EXPAND_MKSHLIB_ARGS = --uselist
+ifdef SYMBOL_ORDER
+EXPAND_MKSHLIB_ARGS += --symbol-order $(SYMBOL_ORDER)
+endif
+EXPAND_MKSHLIB = $(EXPAND_LIBS_EXEC) $(EXPAND_MKSHLIB_ARGS) -- $(MKSHLIB)
 
 ifdef STDCXX_COMPAT
 ifneq ($(OS_ARCH),Darwin)
 CHECK_STDCXX = objdump -p $(1) | grep -e 'GLIBCXX_3\.4\.\(9\|[1-9][0-9]\)' > /dev/null && echo "TEST-UNEXPECTED-FAIL | | We don't want these libstdc++ symbols to be used:" && objdump -T $(1) | grep -e 'GLIBCXX_3\.4\.\(9\|[1-9][0-9]\)' && exit 1 || exit 0
 endif
 endif
 
 # autoconf.mk sets OBJ_SUFFIX to an error to avoid use before including
--- a/config/expandlibs_config.py.in
+++ b/config/expandlibs_config.py.in
@@ -49,8 +49,10 @@ AR_EXTRACT = "@AR_EXTRACT@".replace('$(A
 DLL_PREFIX = "@DLL_PREFIX@"
 LIB_PREFIX = "@LIB_PREFIX@"
 OBJ_SUFFIX = normalize_suffix("@OBJ_SUFFIX@")
 LIB_SUFFIX = normalize_suffix("@LIB_SUFFIX@")
 DLL_SUFFIX = normalize_suffix("@DLL_SUFFIX@")
 IMPORT_LIB_SUFFIX = normalize_suffix("@IMPORT_LIB_SUFFIX@")
 LIBS_DESC_SUFFIX = normalize_suffix("@LIBS_DESC_SUFFIX@")
 EXPAND_LIBS_LIST_STYLE = "@EXPAND_LIBS_LIST_STYLE@"
+EXPAND_LIBS_ORDER_STYLE = "@EXPAND_LIBS_ORDER_STYLE@"
+LD_PRINT_ICF_SECTIONS = "@LD_PRINT_ICF_SECTIONS@"
--- a/config/expandlibs_exec.py
+++ b/config/expandlibs_exec.py
@@ -44,29 +44,41 @@ from static libraries (or use those list
 
 With the --uselist argument (useful for e.g. $(CC)), it replaces all object
 files with a list file. This can be used to avoid limitations in the length
 of a command line. The kind of list file format used depends on the
 EXPAND_LIBS_LIST_STYLE variable: 'list' for MSVC style lists (@file.list)
 or 'linkerscript' for GNU ld linker scripts.
 See https://bugzilla.mozilla.org/show_bug.cgi?id=584474#c59 for more details.
 
-With the --reorder argument, followed by a file name, it will reorder the
-object files from the command line according to the order given in the file.
-Implies --extract.
+With the --symbol-order argument, followed by a file name, it will add the
+relevant linker options to change the order in which the linker puts the
+symbols appear in the resulting binary. Only works for ELF targets.
 '''
 from __future__ import with_statement
 import sys
 import os
 from expandlibs import ExpandArgs, relativize, isObject
 import expandlibs_config as conf
 from optparse import OptionParser
 import subprocess
 import tempfile
 import shutil
+import subprocess
+import re
+
+# The are the insert points for a GNU ld linker script, assuming a more
+# or less "standard" default linker script. This is not a dict because
+# order is important.
+SECTION_INSERT_BEFORE = [
+  ('.text', '.fini'),
+  ('.rodata', '.rodata1'),
+  ('.data.rel.ro', '.dynamic'),
+  ('.data', '.data1'),
+]
 
 class ExpandArgsMore(ExpandArgs):
     ''' Meant to be used as 'with ExpandArgsMore(args) as ...: '''
     def __enter__(self):
         self.tmp = []
         return self
         
     def __exit__(self, type, value, tb):
@@ -114,61 +126,203 @@ class ExpandArgsMore(ExpandArgs):
         fd, tmp = tempfile.mkstemp(suffix=".list",dir=os.curdir)
         if conf.EXPAND_LIBS_LIST_STYLE == "linkerscript":
             content = ["INPUT(%s)\n" % obj for obj in objs]
             ref = tmp
         elif conf.EXPAND_LIBS_LIST_STYLE == "list":
             content = ["%s\n" % obj for obj in objs]
             ref = "@" + tmp
         else:
+            os.close(fd)
             os.remove(tmp)
             return
         self.tmp.append(tmp)
         f = os.fdopen(fd, "w")
         f.writelines(content)
         f.close()
         idx = self.index(objs[0])
         newlist = self[0:idx] + [ref] + [item for item in self[idx:] if item not in objs]
         self[0:] = newlist
 
-    def reorder(self, order_list):
-        '''Given a list of file names without OBJ_SUFFIX, rearrange self
-        so that the object file names it contains are ordered according to
-        that list.
-        '''
-        objs = [o for o in self if isObject(o)]
-        if not objs: return
-        idx = self.index(objs[0])
-        # Keep everything before the first object, then the ordered objects,
-        # then any other objects, then any non-objects after the first object
-        objnames = dict([(os.path.splitext(os.path.basename(o))[0], o) for o in objs])
-        self[0:] = self[0:idx] + [objnames[o] for o in order_list if o in objnames] + \
-                   [o for o in objs if os.path.splitext(os.path.basename(o))[0] not in order_list] + \
-                   [x for x in self[idx:] if not isObject(x)]
+    def _getFoldedSections(self):
+        '''Returns a dict about folded sections.
+        When section A and B are folded into section C, the dict contains:
+        { 'A': 'C',
+          'B': 'C',
+          'C': ['A', 'B'] }'''
+        if not conf.LD_PRINT_ICF_SECTIONS:
+            return {}
+
+        proc = subprocess.Popen(self + [conf.LD_PRINT_ICF_SECTIONS], stdout = subprocess.PIPE, stderr = subprocess.PIPE)
+        (stdout, stderr) = proc.communicate()
+        result = {}
+        # gold's --print-icf-sections output looks like the following:
+        # ld: ICF folding section '.section' in file 'file.o'into '.section' in file 'file.o'
+        # In terms of words, chances are this will change in the future,
+        # especially considering "into" is misplaced. Splitting on quotes
+        # seems safer.
+        for l in stderr.split('\n'):
+            quoted = l.split("'")
+            if len(quoted) > 5 and quoted[1] != quoted[5]:
+                result[quoted[1]] = quoted[5]
+                if quoted[5] in result:
+                    result[quoted[5]].append(quoted[1])
+                else:
+                    result[quoted[5]] = [quoted[1]]
+        return result
+
+    def _getOrderedSections(self, ordered_symbols):
+        '''Given an ordered list of symbols, returns the corresponding list
+        of sections following the order.'''
+        if not conf.EXPAND_LIBS_ORDER_STYLE in ['linkerscript', 'section-ordering-file']:
+            raise Exception('EXPAND_LIBS_ORDER_STYLE "%s" is not supported' % conf.EXPAND_LIBS_ORDER_STYLE)
+        finder = SectionFinder([arg for arg in self if isObject(arg) or os.path.splitext(arg)[1] == conf.LIB_SUFFIX])
+        folded = self._getFoldedSections()
+        sections = set()
+        ordered_sections = []
+        for symbol in ordered_symbols:
+            symbol_sections = finder.getSections(symbol)
+            all_symbol_sections = []
+            for section in symbol_sections:
+                if section in folded:
+                    if isinstance(folded[section], str):
+                        section = folded[section]
+                    all_symbol_sections.append(section)
+                    all_symbol_sections.extend(folded[section])
+                else:
+                    all_symbol_sections.append(section)
+            for section in all_symbol_sections:
+                if not section in sections:
+                    ordered_sections.append(section)
+                    sections.add(section)
+        return ordered_sections
+
+    def orderSymbols(self, order):
+        '''Given a file containing a list of symbols, adds the appropriate
+        argument to make the linker put the symbols in that order.'''
+        with open(order) as file:
+            sections = self._getOrderedSections([l.strip() for l in file.readlines() if l.strip()])
+        split_sections = {}
+        linked_sections = [s[0] for s in SECTION_INSERT_BEFORE]
+        for s in sections:
+            for linked_section in linked_sections:
+                if s.startswith(linked_section):
+                    if linked_section in split_sections:
+                        split_sections[linked_section].append(s)
+                    else:
+                        split_sections[linked_section] = [s]
+                    break
+        content = []
+        # Order is important
+        linked_sections = [s for s in linked_sections if s in split_sections]
 
+        if conf.EXPAND_LIBS_ORDER_STYLE == 'section-ordering-file':
+            option = '-Wl,--section-ordering-file,%s'
+            content = sections
+            for linked_section in linked_sections:
+                content.extend(split_sections[linked_section])
+                content.append('%s.*' % linked_section)
+                content.append(linked_section)
+
+        elif conf.EXPAND_LIBS_ORDER_STYLE == 'linkerscript':
+            option = '-Wl,-T,%s'
+            section_insert_before = dict(SECTION_INSERT_BEFORE)
+            for linked_section in linked_sections:
+                content.append('SECTIONS {')
+                content.append('  %s : {' % linked_section)
+                content.extend('    *(%s)' % s for s in split_sections[linked_section])
+                content.append('  }')
+                content.append('}')
+                content.append('INSERT BEFORE %s' % section_insert_before[linked_section])
+        else:
+            raise Exception('EXPAND_LIBS_ORDER_STYLE "%s" is not supported' % conf.EXPAND_LIBS_ORDER_STYLE)
+
+        fd, tmp = tempfile.mkstemp(dir=os.curdir)
+        f = os.fdopen(fd, "w")
+        f.write('\n'.join(content)+'\n')
+        f.close()
+        self.tmp.append(tmp)
+        self.append(option % tmp)
+
+class SectionFinder(object):
+    '''Instances of this class allow to map symbol names to sections in
+    object files.'''
+
+    def __init__(self, objs):
+        '''Creates an instance, given a list of object files.'''
+        if not conf.EXPAND_LIBS_ORDER_STYLE in ['linkerscript', 'section-ordering-file']:
+            raise Exception('EXPAND_LIBS_ORDER_STYLE "%s" is not supported' % conf.EXPAND_LIBS_ORDER_STYLE)
+        self.mapping = {}
+        for obj in objs:
+            if not isObject(obj) and os.path.splitext(obj)[1] != conf.LIB_SUFFIX:
+                raise Exception('%s is not an object nor a static library' % obj)
+            for symbol, section in SectionFinder._getSymbols(obj):
+                sym = SectionFinder._normalize(symbol)
+                if sym in self.mapping:
+                    if not section in self.mapping[sym]:
+                        self.mapping[sym].append(section)
+                else:
+                    self.mapping[sym] = [section]
+
+    def getSections(self, symbol):
+        '''Given a symbol, returns a list of sections containing it or the
+        corresponding thunks. When the given symbol is a thunk, returns the
+        list of sections containing its corresponding normal symbol and the
+        other thunks for that symbol.'''
+        sym = SectionFinder._normalize(symbol)
+        if sym in self.mapping:
+            return self.mapping[sym]
+        return []
+
+    @staticmethod
+    def _normalize(symbol):
+        '''For normal symbols, return the given symbol. For thunks, return
+        the corresponding normal symbol.'''
+        if re.match('^_ZThn[0-9]+_', symbol):
+            return re.sub('^_ZThn[0-9]+_', '_Z', symbol)
+        return symbol
+
+    @staticmethod
+    def _getSymbols(obj):
+        '''Returns a list of (symbol, section) contained in the given object
+        file.'''
+        proc = subprocess.Popen(['objdump', '-t', obj], stdout = subprocess.PIPE, stderr = subprocess.PIPE)
+        (stdout, stderr) = proc.communicate()
+        syms = []
+        for line in stdout.splitlines():
+            # Each line has the following format:
+            # <addr> [lgu!][w ][C ][W ][Ii ][dD ][FfO ] <section>\t<length> <symbol>
+            tmp = line.split(' ',1)
+            # This gives us ["<addr>", "[lgu!][w ][C ][W ][Ii ][dD ][FfO ] <section>\t<length> <symbol>"]
+            # We only need to consider cases where "<section>\t<length> <symbol>" is present,
+            # and where the [FfO] flag is either F (function) or O (object).
+            if len(tmp) > 1 and len(tmp[1]) > 6 and tmp[1][6] in ['O', 'F']:
+                tmp = tmp[1][8:].split()
+                # That gives us ["<section>","<length>", "<symbol>"]
+                syms.append((tmp[-1], tmp[0]))
+        return syms
 
 def main():
     parser = OptionParser()
     parser.add_option("--extract", action="store_true", dest="extract",
         help="when a library has no descriptor file, extract it first, when possible")
     parser.add_option("--uselist", action="store_true", dest="uselist",
         help="use a list file for objects when executing a command")
     parser.add_option("--verbose", action="store_true", dest="verbose",
         help="display executed command and temporary files content")
-    parser.add_option("--reorder", dest="reorder",
-        help="reorder the objects according to the given list", metavar="FILE")
+    parser.add_option("--symbol-order", dest="symbol_order", metavar="FILE",
+        help="use the given list of symbols to order symbols in the resulting binary when using with a linker")
 
     (options, args) = parser.parse_args()
 
     with ExpandArgsMore(args) as args:
-        if options.extract or options.reorder:
+        if options.extract:
             args.extract()
-        if options.reorder:
-            with open(options.reorder) as file:
-                args.reorder([l.strip() for l in file.readlines()])
+        if options.symbol_order:
+            args.orderSymbols(options.symbol_order)
         if options.uselist:
             args.makelist()
 
         if options.verbose:
             print >>sys.stderr, "Executing: " + " ".join(args)
             for tmp in [f for f in args.tmp if os.path.isfile(f)]:
                 print >>sys.stderr, tmp + ":"
                 with open(tmp) as file:
--- a/config/tests/unit-expandlibs.py
+++ b/config/tests/unit-expandlibs.py
@@ -33,17 +33,17 @@ config_unix = {
     'LIBS_DESC_SUFFIX': '.desc',
     'EXPAND_LIBS_LIST_STYLE': 'linkerscript',
 }
 
 config = sys.modules['expandlibs_config'] = imp.new_module('expandlibs_config')
 
 from expandlibs import LibDescriptor, ExpandArgs, relativize
 from expandlibs_gen import generate
-from expandlibs_exec import ExpandArgsMore
+from expandlibs_exec import ExpandArgsMore, SectionFinder
 
 def Lib(name):
     return config.LIB_PREFIX + name + config.LIB_SUFFIX
 
 def Obj(name):
     return name + config.OBJ_SUFFIX
 
 def Dll(name):
@@ -262,33 +262,106 @@ class TestExpandArgsMore(TestExpandInit)
 
             tmp = args.tmp
         # Check that all temporary files are properly removed
         self.assertEqual(True, all([not os.path.exists(f) for f in tmp]))
 
         # Restore subprocess.call
         subprocess.call = subprocess_call
 
-    def test_reorder(self):
-        '''Test object reordering'''
-        # We don't care about AR_EXTRACT testing, which is done in test_extract
-        config.AR_EXTRACT = ''
+class FakeProcess(object):
+    def __init__(self, out, err = ''):
+        self.out = out
+        self.err = err
+
+    def communicate(self):
+        return (self.out, self.err)
 
-        # ExpandArgsMore does the same as ExpandArgs
-        with ExpandArgsMore(['foo', '-bar'] + self.arg_files + [self.tmpfile('liby', Lib('y'))]) as args:
-            self.assertRelEqual(args, ['foo', '-bar'] + self.files + self.liby_files + self.libx_files) 
+OBJDUMPS = {
+'foo.o': '''
+00000000 g     F .text\t00000001 foo
+00000000 g     F .text._Z6foobarv\t00000001 _Z6foobarv
+00000000 g     F .text.hello\t00000001 hello
+00000000 g     F .text._ZThn4_6foobarv\t00000001 _ZThn4_6foobarv
+''',
+'bar.o': '''
+00000000 g     F .text.hi\t00000001 hi
+00000000 g     F .text.hot._Z6barbazv\t00000001 .hidden _Z6barbazv
+''',
+}
+
+PRINT_ICF = '''
+ld: ICF folding section '.text.hello' in file 'foo.o'into '.text.hi' in file 'bar.o'
+ld: ICF folding section '.foo' in file 'foo.o'into '.foo' in file 'bar.o'
+'''
+
+class SubprocessPopen(object):
+    def __init__(self, test):
+        self.test = test
+
+    def __call__(self, args, stdout = None, stderr = None):
+        self.test.assertEqual(stdout, subprocess.PIPE)
+        self.test.assertEqual(stderr, subprocess.PIPE)
+        if args[0] == 'objdump':
+            self.test.assertEqual(args[1], '-t')
+            self.test.assertTrue(args[2] in OBJDUMPS)
+            return FakeProcess(OBJDUMPS[args[2]])
+        else:
+            return FakeProcess('', PRINT_ICF)
 
-            # Use an order containing object files from libraries
-            order_files = [self.libx_files[1], self.libx_files[0], self.liby_files[2], self.files[1]]
-            order = [os.path.splitext(os.path.basename(f))[0] for f in order_files]
-            args.reorder(order[:2] + ['unknown'] + order[2:])
+class TestSectionFinder(unittest.TestCase):
+    def test_getSections(self):
+        '''Test SectionFinder'''
+        # Divert subprocess.Popen
+        subprocess_popen = subprocess.Popen
+        subprocess.Popen = SubprocessPopen(self)
+        config.EXPAND_LIBS_ORDER_STYLE = 'linkerscript'
+        config.OBJ_SUFFIX = '.o'
+        config.LIB_SUFFIX = '.a'
+        finder = SectionFinder(['foo.o', 'bar.o'])
+        self.assertEqual(finder.getSections('foobar'), [])
+        self.assertEqual(finder.getSections('_Z6barbazv'), ['.text.hot._Z6barbazv'])
+        self.assertEqual(finder.getSections('_Z6foobarv'), ['.text._Z6foobarv', '.text._ZThn4_6foobarv'])
+        self.assertEqual(finder.getSections('_ZThn4_6foobarv'), ['.text._Z6foobarv', '.text._ZThn4_6foobarv'])
+        subprocess.Popen = subprocess_popen
 
-            # self.files has objects at #1, #2, #4
-            self.assertRelEqual(args[:3], ['foo', '-bar'] + self.files[:1])
-            self.assertRelEqual(args[3:7], order_files)
-            self.assertRelEqual(args[7:9], [self.files[2], self.files[4]])
-            self.assertRelEqual(args[9:11], self.liby_files[:2])
-            self.assertRelEqual(args[11:12], [self.libx_files[2]])
-            self.assertRelEqual(args[12:14], [self.files[3], self.files[5]])
-            self.assertRelEqual(args[14:], [self.liby_files[3]])
+class TestSymbolOrder(unittest.TestCase):
+    def test_getOrderedSections(self):
+        '''Test ExpandMoreArgs' _getOrderedSections'''
+        # Divert subprocess.Popen
+        subprocess_popen = subprocess.Popen
+        subprocess.Popen = SubprocessPopen(self)
+        config.EXPAND_LIBS_ORDER_STYLE = 'linkerscript'
+        config.OBJ_SUFFIX = '.o'
+        config.LIB_SUFFIX = '.a'
+        config.LD_PRINT_ICF_SECTIONS = ''
+        args = ExpandArgsMore(['foo', '-bar', 'bar.o', 'foo.o'])
+        self.assertEqual(args._getOrderedSections(['_Z6foobarv', '_Z6barbazv']), ['.text._Z6foobarv', '.text._ZThn4_6foobarv', '.text.hot._Z6barbazv'])
+        self.assertEqual(args._getOrderedSections(['_ZThn4_6foobarv', '_Z6barbazv']), ['.text._Z6foobarv', '.text._ZThn4_6foobarv', '.text.hot._Z6barbazv'])
+        subprocess.Popen = subprocess_popen
+
+    def test_getFoldedSections(self):
+        '''Test ExpandMoreArgs' _getFoldedSections'''
+        # Divert subprocess.Popen
+        subprocess_popen = subprocess.Popen
+        subprocess.Popen = SubprocessPopen(self)
+        config.LD_PRINT_ICF_SECTIONS = '-Wl,--print-icf-sections'
+        args = ExpandArgsMore(['foo', '-bar', 'bar.o', 'foo.o'])
+        self.assertEqual(args._getFoldedSections(), {'.text.hello': '.text.hi', '.text.hi': ['.text.hello']})
+        subprocess.Popen = subprocess_popen
+
+    def test_getOrderedSectionsWithICF(self):
+        '''Test ExpandMoreArgs' _getOrderedSections, with ICF'''
+        # Divert subprocess.Popen
+        subprocess_popen = subprocess.Popen
+        subprocess.Popen = SubprocessPopen(self)
+        config.EXPAND_LIBS_ORDER_STYLE = 'linkerscript'
+        config.OBJ_SUFFIX = '.o'
+        config.LIB_SUFFIX = '.a'
+        config.LD_PRINT_ICF_SECTIONS = '-Wl,--print-icf-sections'
+        args = ExpandArgsMore(['foo', '-bar', 'bar.o', 'foo.o'])
+        self.assertEqual(args._getOrderedSections(['hello', '_Z6barbazv']), ['.text.hi', '.text.hello', '.text.hot._Z6barbazv'])
+        self.assertEqual(args._getOrderedSections(['_ZThn4_6foobarv', 'hi', '_Z6barbazv']), ['.text._Z6foobarv', '.text._ZThn4_6foobarv', '.text.hi', '.text.hello', '.text.hot._Z6barbazv'])
+        subprocess.Popen = subprocess_popen
+
 
 if __name__ == '__main__':
     unittest.main(testRunner=MozTestRunner())
--- a/configure.in
+++ b/configure.in
@@ -2519,16 +2519,18 @@ ia64*-hpux*)
     AC_DEFINE(NSCAP_DISABLE_DEBUG_PTR_TYPES)
     ;;
 
 *-android*|*-linuxandroid*)
     AC_DEFINE(NO_PW_GECOS)
     no_x=yes
     if test -n "$gonkdir"; then
         _PLATFORM_DEFAULT_TOOLKIT=cairo-gonk
+        MOZ_B2G_RIL=1
+        MOZ_B2G_BT=1
     else
         _PLATFORM_DEFAULT_TOOLKIT=cairo-android
         MOZ_LINKER=1
     fi
     TARGET_NSPR_MDCPUCFG='\"md/_linux.cfg\"'
 
     MOZ_GFX_OPTIMIZE_MOBILE=1
     MOZ_OPTIMIZE_FLAGS="-Os -freorder-blocks -fno-reorder-functions"
@@ -5033,17 +5035,16 @@ cairo-android)
 cairo-gonk)
     AC_DEFINE(MOZ_WIDGET_GONK)
     AC_DEFINE(MOZ_TOUCH)
     MOZ_WIDGET_TOOLKIT=gonk
     TK_CFLAGS='$(MOZ_CAIRO_CFLAGS)'
     TK_LIBS='$(MOZ_CAIRO_LIBS)'
     MOZ_WEBGL=1
     MOZ_PDF_PRINTING=1
-    MOZ_B2G_RIL=1
     MOZ_TOUCH=1
     ;;
 
 esac
 
 AC_SUBST(MOZ_OLD_LINKER)
 AC_SUBST(MOZ_PDF_PRINTING)
 if test "$MOZ_PDF_PRINTING"; then
@@ -7175,88 +7176,16 @@ MOZ_ARG_ENABLE_STRING(debug-symbols,
   MOZ_DEBUG_SYMBOLS=1)
 
 if test -n "$MOZ_DEBUG" -o -n "$MOZ_DEBUG_SYMBOLS"; then
     AC_DEFINE(MOZ_DEBUG_SYMBOLS)
     export MOZ_DEBUG_SYMBOLS
 fi
 
 dnl ========================================================
-dnl = Identical Code Folding
-dnl ========================================================
-
-MOZ_ARG_DISABLE_BOOL(icf,
-[  --disable-icf          Disable Identical Code Folding],
-    MOZ_DISABLE_ICF=1,
-    MOZ_DISABLE_ICF= )
-
-if test "$GNU_CC" -a "$GCC_USE_GNU_LD" -a -z "$MOZ_DISABLE_ICF"; then
-    AC_CACHE_CHECK([whether the linker supports Identical Code Folding],
-        LD_SUPPORTS_ICF,
-        [echo 'int foo() {return 42;}' \
-              'int bar() {return 42;}' \
-              'int main() {return foo() - bar();}' > conftest.${ac_ext}
-        # If the linker supports ICF, foo and bar symbols will have
-        # the same address
-        if AC_TRY_COMMAND([${CC-cc} -o conftest${ac_exeext} $LDFLAGS -Wl,--icf=safe -ffunction-sections conftest.${ac_ext} $LIBS 1>&2]) &&
-           test -s conftest${ac_exeext} &&
-           objdump -t conftest${ac_exeext} | awk '{a[[$6]] = $1} END {if (a[["foo"]] && (a[["foo"]] != a[["bar"]])) { exit 1 }}'; then
-            LD_SUPPORTS_ICF=yes
-        else
-            LD_SUPPORTS_ICF=no
-        fi
-        rm -rf conftest*])
-    if test "$LD_SUPPORTS_ICF" = yes; then
-        LDFLAGS="$LDFLAGS -Wl,--icf=safe"
-        CFLAGS="$CFLAGS -ffunction-sections"
-        CXXFLAGS="$CXXFLAGS -ffunction-sections"
-    fi
-fi
-
-dnl ========================================================
-dnl = Automatically remove dead symbols
-dnl ========================================================
-
-if test "$GNU_CC" -a "$GCC_USE_GNU_LD" -a -n "$MOZ_DEBUG_FLAGS"; then
-   dnl See bug 670659
-   AC_CACHE_CHECK([whether removing dead symbols breaks debugging],
-       GC_SECTIONS_BREAKS_DEBUG_RANGES,
-       [echo 'int foo() {return 42;}' \
-             'int bar() {return 1;}' \
-             'int main() {return foo();}' > conftest.${ac_ext}
-        if AC_TRY_COMMAND([${CC-cc} -o conftest.${ac_objext} $CFLAGS $MOZ_DEBUG_FLAGS -ffunction-sections -c conftest.${ac_ext} 1>&2]) &&
-           AC_TRY_COMMAND([${CC-cc} -o conftest${ac_exeext} $LDFLAGS $MOZ_DEBUG_FLAGS -Wl,--gc-sections conftest.${ac_objext} $LIBS 1>&2]) &&
-           test -s conftest${ac_exeext} -a -s conftest.${ac_objext}; then
-            if test "`$PYTHON "$_topsrcdir"/build/unix/check_debug_ranges.py conftest.${ac_objext} conftest.${ac_ext}`" = \
-                    "`$PYTHON "$_topsrcdir"/build/unix/check_debug_ranges.py conftest${ac_exeext} conftest.${ac_ext}`"; then
-                GC_SECTIONS_BREAKS_DEBUG_RANGES=no
-            else
-                GC_SECTIONS_BREAKS_DEBUG_RANGES=yes
-            fi
-        else
-             dnl We really don't expect to get here, but just in case
-             GC_SECTIONS_BREAKS_DEBUG_RANGES="no, but it's broken in some other way"
-        fi
-        rm -rf conftest*])
-    if test "$GC_SECTIONS_BREAKS_DEBUG_RANGES" = no; then
-        DSO_LDOPTS="$DSO_LDOPTS -Wl,--gc-sections"
-        case "$CFLAGS" in
-        *-ffunction-sections*)
-            CFLAGS="$CFLAGS -fdata-sections"
-            CXXFLAGS="$CXXFLAGS -fdata-sections"
-            ;;
-        *)
-            CFLAGS="$CFLAGS -ffunction-sections -fdata-sections"
-            CXXFLAGS="$CXXFLAGS -ffunction-sections -fdata-sections"
-            ;;
-        esac
-    fi
-fi
-
-dnl ========================================================
 dnl = Enable any treating of compile warnings as errors
 dnl ========================================================
 MOZ_ARG_ENABLE_BOOL(warnings-as-errors,
 [  --enable-warnings-as-errors
                           Enable treating of warnings as errors],
     MOZ_ENABLE_WARNINGS_AS_ERRORS=1,
     MOZ_ENABLE_WARNINGS_AS_ERRORS=)
 if test -z "$MOZ_ENABLE_WARNINGS_AS_ERRORS"; then
@@ -8067,47 +7996,17 @@ CXXFLAGS="$_SAVE_CXXFLAGS"
 if test "$ac_nscap_nonconst_opeq_bug" = "yes" ; then
     AC_DEFINE(NSCAP_DONT_PROVIDE_NONCONST_OPEQ)
 fi
 fi # ! SKIP_COMPILER_CHECKS
 
 AC_DEFINE(CPP_THROW_NEW, [throw()])
 AC_LANG_C
 
-dnl ========================================================
-dnl =
-dnl = Check what kind of list files are supported by the
-dnl = linker
-dnl =
-dnl ========================================================
-
-AC_CACHE_CHECK(what kind of list files are supported by the linker,
-    EXPAND_LIBS_LIST_STYLE,
-    [echo "int main() {return 0;}" > conftest.${ac_ext}
-     if AC_TRY_COMMAND(${CC-cc} -o conftest.${OBJ_SUFFIX} -c $CFLAGS $CPPFLAGS conftest.${ac_ext} 1>&2) && test -s conftest.${OBJ_SUFFIX}; then
-         echo "INPUT(conftest.${OBJ_SUFFIX})" > conftest.list
-         if AC_TRY_COMMAND(${CC-cc} -o conftest${ac_exeext} $LDFLAGS conftest.list $LIBS 1>&2) && test -s conftest${ac_exeext}; then
-             EXPAND_LIBS_LIST_STYLE=linkerscript
-         else
-             echo "conftest.${OBJ_SUFFIX}" > conftest.list
-             if AC_TRY_COMMAND(${CC-cc} -o conftest${ac_exeext} $LDFLAGS @conftest.list $LIBS 1>&2) && test -s conftest${ac_exeext}; then
-                 EXPAND_LIBS_LIST_STYLE=list
-             else
-                 EXPAND_LIBS_LIST_STYLE=none
-             fi
-         fi
-     else
-         dnl We really don't expect to get here, but just in case
-         AC_ERROR([couldn't compile a simple C file])
-     fi
-     rm -rf conftest*])
-
-LIBS_DESC_SUFFIX=desc
-AC_SUBST(LIBS_DESC_SUFFIX)
-AC_SUBST(EXPAND_LIBS_LIST_STYLE)
+MOZ_EXPAND_LIBS
 
 dnl ========================================================
 dnl =
 dnl = Build depencency options
 dnl =
 dnl ========================================================
 MOZ_ARG_HEADER(Build dependencies)
 
--- a/content/base/public/Makefile.in
+++ b/content/base/public/Makefile.in
@@ -60,17 +60,16 @@ nsIMutationObserver.h \
 nsIMutationObserver2.h \
 nsINameSpaceManager.h \
 nsINode.h \
 nsINodeInfo.h \
 nsINodeList.h \
 nsIScriptElement.h \
 nsIStyleSheetLinkingElement.h \
 nsIContentSerializer.h \
-nsIHTMLToTextSink.h \
 nsIXPathEvaluatorInternal.h \
 mozISanitizingSerializer.h \
 nsCaseTreatment.h \
 nsContentCID.h \
 nsCopySupport.h \
 nsContentCreatorFunctions.h \
 nsDOMFile.h \
 nsLineBreaker.h \
--- a/content/base/public/nsContentUtils.h
+++ b/content/base/public/nsContentUtils.h
@@ -437,16 +437,26 @@ public:
    * We define whitespace using the list in HTML5 and css3-selectors:
    * U+0009, U+000A, U+000C, U+000D, U+0020
    *
    * HTML 4.01 also lists U+200B (zero-width space).
    */
   static bool IsHTMLWhitespace(PRUnichar aChar);
 
   /**
+   * Is the HTML local name a block element?
+   */
+  static bool IsHTMLBlock(nsIAtom* aLocalName);
+
+  /**
+   * Is the HTML local name a void element?
+   */
+  static bool IsHTMLVoid(nsIAtom* aLocalName);
+
+  /**
    * Parse a margin string of format 'top, right, bottom, left' into
    * an nsIntMargin.
    *
    * @param aString the string to parse
    * @param aResult the resulting integer
    * @return whether the value could be parsed
    */
   static bool ParseIntMarginValue(const nsAString& aString, nsIntMargin& aResult);
@@ -833,21 +843,32 @@ public:
   static nsresult GetLocalizedString(PropertiesFile aFile,
                                      const char* aKey,
                                      nsXPIDLString& aResult);
 
   /**
    * Fill (with the parameters given) the localized string named |aKey| in
    * properties file |aFile|.
    */
+private:
   static nsresult FormatLocalizedString(PropertiesFile aFile,
                                         const char* aKey,
-                                        const PRUnichar **aParams,
+                                        const PRUnichar** aParams,
                                         PRUint32 aParamsLength,
                                         nsXPIDLString& aResult);
+  
+public:
+  template<PRUint32 N>
+  static nsresult FormatLocalizedString(PropertiesFile aFile,
+                                        const char* aKey,
+                                        const PRUnichar* (&aParams)[N],
+                                        nsXPIDLString& aResult)
+  {
+    return FormatLocalizedString(aFile, aKey, aParams, N, aResult);
+  }
 
   /**
    * Returns true if aDocument is a chrome document
    */
   static bool IsChromeDoc(nsIDocument *aDocument);
 
   /**
    * Returns true if aDocument is in a docshell whose parent is the same type
deleted file mode 100644
--- a/content/base/public/nsIHTMLToTextSink.h
+++ /dev/null
@@ -1,66 +0,0 @@
-/* -*- 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
- * Netscape Communications Corporation.
- * Portions created by the Initial Developer are Copyright (C) 1998
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either of the GNU General Public License Version 2 or later (the "GPL"),
- * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-#ifndef _nsIPlainTextSink_h__
-#define _nsIPlainTextSink_h__
-
-#include "nsISupports.h"
-#include "nsStringGlue.h"
-
-#define NS_PLAINTEXTSINK_CONTRACTID "@mozilla.org/layout/plaintextsink;1"
-
-/* starting interface:    nsIContentSerializer */
-#define NS_IHTMLTOTEXTSINK_IID_STR "b12b5643-07cb-401e-aabb-64b2dcd2717f"
-
-#define NS_IHTMLTOTEXTSINK_IID \
-  {0xb12b5643, 0x07cb, 0x401e, \
-    { 0xaa, 0xbb, 0x64, 0xb2, 0xdc, 0xd2, 0x71, 0x7f }}
-
-
-class nsIHTMLToTextSink : public nsISupports {
- public: 
-
-  NS_DECLARE_STATIC_IID_ACCESSOR(NS_IHTMLTOTEXTSINK_IID)
-
-  NS_IMETHOD Initialize(nsAString* aOutString,
-                        PRUint32 aFlags, PRUint32 aWrapCol) = 0;
-     // This function violates string ownership rules, see impl.
-};
-
-NS_DEFINE_STATIC_IID_ACCESSOR(nsIHTMLToTextSink, NS_IHTMLTOTEXTSINK_IID)
-
-#endif
--- a/content/base/src/nsContentUtils.cpp
+++ b/content/base/src/nsContentUtils.cpp
@@ -1187,16 +1187,82 @@ nsContentUtils::IsHTMLWhitespace(PRUnich
          aChar == PRUnichar(0x000A) ||
          aChar == PRUnichar(0x000C) ||
          aChar == PRUnichar(0x000D) ||
          aChar == PRUnichar(0x0020);
 }
 
 /* static */
 bool
+nsContentUtils::IsHTMLBlock(nsIAtom* aLocalName)
+{
+  return
+    (aLocalName == nsGkAtoms::address) ||
+    (aLocalName == nsGkAtoms::article) ||
+    (aLocalName == nsGkAtoms::aside) ||
+    (aLocalName == nsGkAtoms::blockquote) ||
+    (aLocalName == nsGkAtoms::center) ||
+    (aLocalName == nsGkAtoms::dir) ||
+    (aLocalName == nsGkAtoms::div) ||
+    (aLocalName == nsGkAtoms::dl) || // XXX why not dt and dd?
+    (aLocalName == nsGkAtoms::fieldset) ||
+    (aLocalName == nsGkAtoms::figure) || // XXX shouldn't figcaption be on this list
+    (aLocalName == nsGkAtoms::footer) ||
+    (aLocalName == nsGkAtoms::form) ||
+    (aLocalName == nsGkAtoms::h1) ||
+    (aLocalName == nsGkAtoms::h2) ||
+    (aLocalName == nsGkAtoms::h3) ||
+    (aLocalName == nsGkAtoms::h4) ||
+    (aLocalName == nsGkAtoms::h5) ||
+    (aLocalName == nsGkAtoms::h6) ||
+    (aLocalName == nsGkAtoms::header) ||
+    (aLocalName == nsGkAtoms::hgroup) ||
+    (aLocalName == nsGkAtoms::hr) ||
+    (aLocalName == nsGkAtoms::li) ||
+    (aLocalName == nsGkAtoms::listing) ||
+    (aLocalName == nsGkAtoms::menu) ||
+    (aLocalName == nsGkAtoms::multicol) || // XXX get rid of this one?
+    (aLocalName == nsGkAtoms::nav) ||
+    (aLocalName == nsGkAtoms::ol) ||
+    (aLocalName == nsGkAtoms::p) ||
+    (aLocalName == nsGkAtoms::pre) ||
+    (aLocalName == nsGkAtoms::section) ||
+    (aLocalName == nsGkAtoms::table) ||
+    (aLocalName == nsGkAtoms::ul) ||
+    (aLocalName == nsGkAtoms::xmp);
+}
+
+/* static */
+bool
+nsContentUtils::IsHTMLVoid(nsIAtom* aLocalName)
+{
+  return
+    (aLocalName == nsGkAtoms::area) ||
+    (aLocalName == nsGkAtoms::base) ||
+    (aLocalName == nsGkAtoms::basefont) ||
+    (aLocalName == nsGkAtoms::bgsound) ||
+    (aLocalName == nsGkAtoms::br) ||
+    (aLocalName == nsGkAtoms::col) ||
+    (aLocalName == nsGkAtoms::command) ||
+    (aLocalName == nsGkAtoms::embed) ||
+    (aLocalName == nsGkAtoms::frame) ||
+    (aLocalName == nsGkAtoms::hr) ||
+    (aLocalName == nsGkAtoms::img) ||
+    (aLocalName == nsGkAtoms::input) ||
+    (aLocalName == nsGkAtoms::keygen) ||
+    (aLocalName == nsGkAtoms::link) ||
+    (aLocalName == nsGkAtoms::meta) ||
+    (aLocalName == nsGkAtoms::param) ||
+    (aLocalName == nsGkAtoms::source) ||
+    (aLocalName == nsGkAtoms::track) ||
+    (aLocalName == nsGkAtoms::wbr);
+}
+
+/* static */
+bool
 nsContentUtils::ParseIntMarginValue(const nsAString& aString, nsIntMargin& result)
 {
   nsAutoString marginStr(aString);
   marginStr.CompressWhitespace(true, true);
   if (marginStr.IsEmpty()) {
     return false;
   }
 
--- a/content/base/src/nsGenericElement.cpp
+++ b/content/base/src/nsGenericElement.cpp
@@ -2731,16 +2731,19 @@ nsGenericElement::SetAttribute(const nsA
 }
 
 nsresult
 nsGenericElement::RemoveAttribute(const nsAString& aName)
 {
   const nsAttrName* name = InternalGetExistingAttrNameFromQName(aName);
 
   if (!name) {
+    // If there is no canonical nsAttrName for this attribute name, then the
+    // attribute does not exist and we can't get its namespace ID and
+    // local name below, so we return early.
     return NS_OK;
   }
 
   // Hold a strong reference here so that the atom or nodeinfo doesn't go
   // away during UnsetAttr. If it did UnsetAttr would be left with a
   // dangling pointer as argument without knowing it.
   nsAttrName tmp(*name);
 
@@ -2884,18 +2887,19 @@ nsresult
 nsGenericElement::RemoveAttributeNS(const nsAString& aNamespaceURI,
                                     const nsAString& aLocalName)
 {
   nsCOMPtr<nsIAtom> name = do_GetAtom(aLocalName);
   PRInt32 nsid =
     nsContentUtils::NameSpaceManager()->GetNameSpaceID(aNamespaceURI);
 
   if (nsid == kNameSpaceID_Unknown) {
-    // Unknown namespace means no attr...
-
+    // If the namespace ID is unknown, it means there can't possibly be an
+    // existing attribute. We would need a known namespace ID to pass into
+    // UnsetAttr, so we return early if we don't have one.
     return NS_OK;
   }
 
   UnsetAttr(nsid, name, true);
 
   return NS_OK;
 }
 
--- a/content/base/src/nsGkAtomList.h
+++ b/content/base/src/nsGkAtomList.h
@@ -137,16 +137,17 @@ GK_ATOM(basefont, "basefont")
 GK_ATOM(baseline, "baseline")
 GK_ATOM(bdi, "bdi")
 GK_ATOM(bdo, "bdo")
 GK_ATOM(before, "before")
 GK_ATOM(before_end, "before_end")
 GK_ATOM(before_start, "before_start")
 GK_ATOM(below, "below")
 GK_ATOM(bgcolor, "bgcolor")
+GK_ATOM(bgsound, "bgsound")
 GK_ATOM(big, "big")
 GK_ATOM(binding, "binding")
 GK_ATOM(bindings, "bindings")
 GK_ATOM(blankrow, "blankrow")
 GK_ATOM(block, "block")
 GK_ATOM(blockquote, "blockquote")
 GK_ATOM(blur, "blur")
 GK_ATOM(body, "body")
@@ -488,16 +489,17 @@ GK_ATOM(itemref, "itemref")
 GK_ATOM(itemscope, "itemscope")
 GK_ATOM(itemtype, "itemtype")
 GK_ATOM(kbd, "kbd")
 GK_ATOM(noautofocus, "noautofocus")
 GK_ATOM(keepcurrentinview, "keepcurrentinview")
 GK_ATOM(key, "key")
 GK_ATOM(keycode, "keycode")
 GK_ATOM(keydown, "keydown")
+GK_ATOM(keygen, "keygen")
 GK_ATOM(keypress, "keypress")
 GK_ATOM(keyset, "keyset")
 GK_ATOM(keytext, "keytext")
 GK_ATOM(keyup, "keyup")
 GK_ATOM(kind, "kind")
 GK_ATOM(label, "label")
 GK_ATOM(lang, "lang")
 GK_ATOM(language, "language")
--- a/content/base/src/nsGkAtoms.cpp
+++ b/content/base/src/nsGkAtoms.cpp
@@ -60,11 +60,11 @@ using namespace mozilla;
 static const nsStaticAtom GkAtoms_info[] = {
 #define GK_ATOM(name_, value_) NS_STATIC_ATOM(name_##_buffer, &nsGkAtoms::name_),
 #include "nsGkAtomList.h"
 #undef GK_ATOM
 };
 
 void nsGkAtoms::AddRefAtoms()
 {
-  NS_RegisterStaticAtoms(GkAtoms_info, ArrayLength(GkAtoms_info));
+  NS_RegisterStaticAtoms(GkAtoms_info);
 }
 
--- a/content/base/src/nsPlainTextSerializer.cpp
+++ b/content/base/src/nsPlainTextSerializer.cpp
@@ -48,17 +48,16 @@
 #include "nsIServiceManager.h"
 #include "nsGkAtoms.h"
 #include "nsINameSpaceManager.h"
 #include "nsTextFragment.h"
 #include "nsContentUtils.h"
 #include "nsReadableUtils.h"
 #include "nsUnicharUtils.h"
 #include "nsCRT.h"
-#include "nsIParserService.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/Preferences.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 #define PREF_STRUCTS "converter.html2txt.structs"
 #define PREF_HEADER_STRATEGY "converter.html2txt.header_strategy"
@@ -74,17 +73,17 @@ static const  PRInt32 kIndentIncrementHe
                                                 indent h(x+1) this many
                                                 columns more than h(x) */
 static const  PRInt32 kIndentSizeList = kTabSize;
                                // Indention of non-first lines of ul and ol
 static const  PRInt32 kIndentSizeDD = kTabSize;  // Indention of <dd>
 static const  PRUnichar  kNBSP = 160;
 static const  PRUnichar kSPACE = ' ';
 
-static PRInt32 HeaderLevel(eHTMLTags aTag);
+static PRInt32 HeaderLevel(nsIAtom* aTag);
 static PRInt32 GetUnicharWidth(PRUnichar ucs);
 static PRInt32 GetUnicharStringWidth(const PRUnichar* pwcs, PRInt32 n);
 
 // Someday may want to make this non-const:
 static const PRUint32 TagStackSize = 500;
 static const PRUint32 OLStackSize = 100;
 
 nsresult NS_NewPlainTextSerializer(nsIContentSerializer** aSerializer)
@@ -121,17 +120,19 @@ nsPlainTextSerializer::nsPlainTextSerial
 
   // Flow
   mEmptyLines = 1; // The start of the document is an "empty line" in itself,
   mInWhitespace = false;
   mPreFormatted = false;
   mStartedOutput = false;
 
   // initialize the tag stack to zero:
-  mTagStack = new nsHTMLTag[TagStackSize];
+  // The stack only ever contains pointers to static atoms, so they don't
+  // need refcounting.
+  mTagStack = new nsIAtom*[TagStackSize];
   mTagStackIndex = 0;
   mIgnoreAboveIndex = (PRUint32)kNotFound;
 
   // initialize the OL stack, where numbers for ordered lists are kept
   mOLStack = new PRInt32[OLStackSize];
   mOLStackIndex = 0;
 
   mULCount = 0;
@@ -139,44 +140,39 @@ nsPlainTextSerializer::nsPlainTextSerial
 
 nsPlainTextSerializer::~nsPlainTextSerializer()
 {
   delete[] mTagStack;
   delete[] mOLStack;
   NS_WARN_IF_FALSE(mHeadLevel == 0, "Wrong head level!");
 }
 
-NS_IMPL_ISUPPORTS4(nsPlainTextSerializer, 
-                   nsIContentSerializer,
-                   nsIContentSink,
-                   nsIHTMLContentSink,
-                   nsIHTMLToTextSink)
+NS_IMPL_ISUPPORTS1(nsPlainTextSerializer,
+                   nsIContentSerializer)
 
 
 NS_IMETHODIMP 
 nsPlainTextSerializer::Init(PRUint32 aFlags, PRUint32 aWrapColumn,
                             const char* aCharSet, bool aIsCopying,
                             bool aIsWholeDocument)
 {
 #ifdef DEBUG
   // Check if the major control flags are set correctly.
-  if(aFlags & nsIDocumentEncoder::OutputFormatFlowed) {
+  if (aFlags & nsIDocumentEncoder::OutputFormatFlowed) {
     NS_ASSERTION(aFlags & nsIDocumentEncoder::OutputFormatted,
                  "If you want format=flowed, you must combine it with "
                  "nsIDocumentEncoder::OutputFormatted");
   }
 
-  if(aFlags & nsIDocumentEncoder::OutputFormatted) {
+  if (aFlags & nsIDocumentEncoder::OutputFormatted) {
     NS_ASSERTION(!(aFlags & nsIDocumentEncoder::OutputPreformatted),
                  "Can't do formatted and preformatted output at the same time!");
   }
 #endif
 
-  NS_ENSURE_TRUE(nsContentUtils::GetParserService(), NS_ERROR_UNEXPECTED);
-
   mFlags = aFlags;
   mWrapColumn = aWrapColumn;
 
   // Only create a linebreaker if we will handle wrapping.
   if (MayWrap()) {
     mLineBreaker = nsContentUtils::LineBreaker();
   }
 
@@ -265,31 +261,16 @@ nsPlainTextSerializer::PopBool(nsTArray<
   PRUint32 size = aStack.Length();
   if (size > 0) {
     returnValue = aStack.ElementAt(size-1);
     aStack.RemoveElementAt(size-1);
   }
   return returnValue;
 }
 
-NS_IMETHODIMP
-nsPlainTextSerializer::Initialize(nsAString* aOutString,
-                                  PRUint32 aFlags, PRUint32 aWrapCol)
-{
-  nsresult rv = Init(aFlags, aWrapCol, nsnull, false, false);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  // XXX This is wrong. It violates XPCOM string ownership rules.
-  // We're only getting away with this because instances of this
-  // class are restricted to single function scope.
-  mOutputString = aOutString;
-
-  return NS_OK;
-}
-
 NS_IMETHODIMP 
 nsPlainTextSerializer::AppendText(nsIContent* aText,
                                   PRInt32 aStartOffset,
                                   PRInt32 aEndOffset, 
                                   nsAString& aStr)
 {
   if (mIgnoreAboveIndex != (PRUint32)kNotFound) {
     return NS_OK;
@@ -330,41 +311,36 @@ nsPlainTextSerializer::AppendText(nsICon
   mOutputString = &aStr;
 
   // We have to split the string across newlines
   // to match parser behavior
   PRInt32 start = 0;
   PRInt32 offset = textstr.FindCharInSet("\n\r");
   while (offset != kNotFound) {
 
-    if(offset>start) {
+    if (offset>start) {
       // Pass in the line
-      rv = DoAddLeaf(nsnull,
-                     eHTMLTag_text,
-                     Substring(textstr, start, offset-start));
-      if (NS_FAILED(rv)) break;
+      DoAddText(false,
+                Substring(textstr, start, offset-start));
     }
 
     // Pass in a newline
-    rv = DoAddLeaf(nsnull, eHTMLTag_newline, mLineBreak);
-    if (NS_FAILED(rv)) break;
+    DoAddText(true, mLineBreak);
     
     start = offset+1;
     offset = textstr.FindCharInSet("\n\r", start);
   }
 
   // Consume the last bit of the string if there's any left
-  if (NS_SUCCEEDED(rv) && start < length) {
+  if (start < length) {
     if (start) {
-      rv = DoAddLeaf(nsnull,
-                     eHTMLTag_text,
-                     Substring(textstr, start, length-start));
+      DoAddText(false, Substring(textstr, start, length - start));
     }
     else {
-      rv = DoAddLeaf(nsnull, eHTMLTag_text, textstr);
+      DoAddText(false, textstr);
     }
   }
   
   mOutputString = nsnull;
 
   return rv;
 }
 
@@ -382,63 +358,63 @@ nsPlainTextSerializer::AppendElementStar
                                           Element* aOriginalElement,
                                           nsAString& aStr)
 {
   NS_ENSURE_ARG(aElement);
 
   mElement = aElement;
 
   nsresult rv;
-  PRInt32 id = GetIdForContent(mElement);
+  nsIAtom* id = GetIdForContent(mElement);
 
-  bool isContainer = IsContainer(id);
+  bool isContainer = !nsContentUtils::IsHTMLVoid(id);
 
   mOutputString = &aStr;
 
   if (isContainer) {
-    rv = DoOpenContainer(nsnull, id);
+    rv = DoOpenContainer(id);
   }
   else {
-    rv = DoAddLeaf(nsnull, id, EmptyString());
+    rv = DoAddLeaf(id);
   }
 
   mElement = nsnull;
   mOutputString = nsnull;
 
-  if (id == eHTMLTag_head) {
+  if (id == nsGkAtoms::head) {
     ++mHeadLevel;
   }
 
   return rv;
 } 
  
 NS_IMETHODIMP 
 nsPlainTextSerializer::AppendElementEnd(Element* aElement,
                                         nsAString& aStr)
 {
   NS_ENSURE_ARG(aElement);
 
   mElement = aElement;
 
   nsresult rv;
-  PRInt32 id = GetIdForContent(mElement);
+  nsIAtom* id = GetIdForContent(mElement);
 
-  bool isContainer = IsContainer(id);
+  bool isContainer = !nsContentUtils::IsHTMLVoid(id);
 
   mOutputString = &aStr;
 
   rv = NS_OK;
   if (isContainer) {
     rv = DoCloseContainer(id);
   }
 
   mElement = nsnull;
   mOutputString = nsnull;
 
-  if (id == eHTMLTag_head) {
+  if (id == nsGkAtoms::head) {
     --mHeadLevel;
     NS_ASSERTION(mHeadLevel >= 0, "mHeadLevel < 0");
   }
 
   return rv;
 }
 
 NS_IMETHODIMP 
@@ -452,159 +428,77 @@ nsPlainTextSerializer::Flush(nsAString& 
 
 NS_IMETHODIMP
 nsPlainTextSerializer::AppendDocumentStart(nsIDocument *aDocument,
                                            nsAString& aStr)
 {
   return NS_OK;
 }
 
-NS_IMETHODIMP
-nsPlainTextSerializer::OpenContainer(const nsIParserNode& aNode)
-{
-  PRInt32 type = aNode.GetNodeType();
-
-  if (type == eHTMLTag_head) {
-    ++mHeadLevel;
-    return NS_OK;
-  }
-
-  return DoOpenContainer(&aNode, type);
-}
-
-NS_IMETHODIMP 
-nsPlainTextSerializer::CloseContainer(const nsHTMLTag aTag)
-{
-  if (aTag == eHTMLTag_head) {
-    --mHeadLevel;
-    NS_ASSERTION(mHeadLevel >= 0, "mHeadLevel < 0");
-    return NS_OK;
-  }
-
-  return DoCloseContainer(aTag);
-}
-
-NS_IMETHODIMP 
-nsPlainTextSerializer::AddLeaf(const nsIParserNode& aNode)
-{
-  if (mIgnoreAboveIndex != (PRUint32)kNotFound) {
-    return NS_OK;
-  }
-
-  eHTMLTags type = (eHTMLTags)aNode.GetNodeType();
-  const nsAString& text = aNode.GetText();
-
-  if ((type == eHTMLTag_text) ||
-      (type == eHTMLTag_whitespace) ||
-      (type == eHTMLTag_newline)) {
-    // Copy the text out, stripping out CRs
-    nsAutoString str;
-    PRUint32 length;
-    str.SetCapacity(text.Length());
-    nsReadingIterator<PRUnichar> srcStart, srcEnd;
-    length = nsContentUtils::CopyNewlineNormalizedUnicodeTo(text.BeginReading(srcStart), text.EndReading(srcEnd), str);
-    str.SetLength(length);
-    return DoAddLeaf(&aNode, type, str);
-  }
-  else {
-    return DoAddLeaf(&aNode, type, text);
-  }
-}
-
-NS_IMETHODIMP 
-nsPlainTextSerializer::OpenHead()
-{
-  ++mHeadLevel;
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsPlainTextSerializer::IsEnabled(PRInt32 aTag, bool* aReturn)
-{
-  nsHTMLTag theHTMLTag = nsHTMLTag(aTag);
-
-  if (theHTMLTag == eHTMLTag_script) {
-    *aReturn = !(mFlags & nsIDocumentEncoder::OutputNoScriptContent);
-  }
-  else if (theHTMLTag == eHTMLTag_frameset) {
-    *aReturn = !(mFlags & nsIDocumentEncoder::OutputNoFramesContent);
-  }
-  else {
-    *aReturn = false;
-  }
-
-  return NS_OK;
-}
-
-/**
- * aNode may be null when we're working with the DOM, but then mElement is
- * useable instead.
- */
 nsresult
-nsPlainTextSerializer::DoOpenContainer(const nsIParserNode* aNode, PRInt32 aTag)
+nsPlainTextSerializer::DoOpenContainer(nsIAtom* aTag)
 {
   if (mFlags & nsIDocumentEncoder::OutputRaw) {
     // Raw means raw.  Don't even think about doing anything fancy
     // here like indenting, adding line breaks or any other
     // characters such as list item bullets, quote characters
     // around <q>, etc.  I mean it!  Don't make me smack you!
 
     return NS_OK;
   }
 
-  eHTMLTags type = (eHTMLTags)aTag;
-
   if (mTagStackIndex < TagStackSize) {
-    mTagStack[mTagStackIndex++] = type;
+    mTagStack[mTagStackIndex++] = aTag;
   }
 
   if (mIgnoreAboveIndex != (PRUint32)kNotFound) {
     return NS_OK;
   }
 
   // Reset this so that <blockquote type=cite> doesn't affect the whitespace
   // above random <pre>s below it.
-  mHasWrittenCiteBlockquote = mHasWrittenCiteBlockquote && aTag == eHTMLTag_pre;
+  mHasWrittenCiteBlockquote = mHasWrittenCiteBlockquote &&
+                              aTag == nsGkAtoms::pre;
 
   bool isInCiteBlockquote = false;
 
   // XXX special-case <blockquote type=cite> so that we don't add additional
   // newlines before the text.
-  if (aTag == eHTMLTag_blockquote) {
+  if (aTag == nsGkAtoms::blockquote) {
     nsAutoString value;
-    nsresult rv = GetAttributeValue(aNode, nsGkAtoms::type, value);
+    nsresult rv = GetAttributeValue(nsGkAtoms::type, value);
     isInCiteBlockquote = NS_SUCCEEDED(rv) && value.EqualsIgnoreCase("cite");
   }
 
   if (mLineBreakDue && !isInCiteBlockquote)
     EnsureVerticalSpace(mFloatingLines);
 
   // Check if this tag's content that should not be output
-  if ((type == eHTMLTag_noscript &&
+  if ((aTag == nsGkAtoms::noscript &&
        !(mFlags & nsIDocumentEncoder::OutputNoScriptContent)) ||
-      ((type == eHTMLTag_iframe || type == eHTMLTag_noframes) &&
+      ((aTag == nsGkAtoms::iframe || aTag == nsGkAtoms::noframes) &&
        !(mFlags & nsIDocumentEncoder::OutputNoFramesContent))) {
     // Ignore everything that follows the current tag in 
     // question until a matching end tag is encountered.
     mIgnoreAboveIndex = mTagStackIndex - 1;
     return NS_OK;
   }
 
-  if (type == eHTMLTag_body) {
+  if (aTag == nsGkAtoms::body) {
     // Try to figure out here whether we have a
     // preformatted style attribute.
     //
     // Trigger on the presence of a "pre-wrap" in the
     // style attribute. That's a very simplistic way to do
     // it, but better than nothing.
     // Also set mWrapColumn to the value given there
     // (which arguably we should only do if told to do so).
     nsAutoString style;
     PRInt32 whitespace;
-    if(NS_SUCCEEDED(GetAttributeValue(aNode, nsGkAtoms::style, style)) &&
+    if (NS_SUCCEEDED(GetAttributeValue(nsGkAtoms::style, style)) &&
        (kNotFound != (whitespace = style.Find("white-space:")))) {
 
       if (kNotFound != style.Find("pre-wrap", true, whitespace)) {
 #ifdef DEBUG_preformatted
         printf("Set mPreFormatted based on style pre-wrap\n");
 #endif
         mPreFormatted = true;
         PRInt32 widthOffset = style.Find("width:");
@@ -647,32 +541,32 @@ nsPlainTextSerializer::DoOpenContainer(c
     return NS_OK;
   }
 
   // Keep this in sync with DoCloseContainer!
   if (!DoOutput()) {
     return NS_OK;
   }
 
-  if (type == eHTMLTag_p)
+  if (aTag == nsGkAtoms::p)
     EnsureVerticalSpace(1);
-  else if (type == eHTMLTag_pre) {
+  else if (aTag == nsGkAtoms::pre) {
     if (GetLastBool(mIsInCiteBlockquote))
       EnsureVerticalSpace(0);
     else if (mHasWrittenCiteBlockquote) {
       EnsureVerticalSpace(0);
       mHasWrittenCiteBlockquote = false;
     }
     else
       EnsureVerticalSpace(1);
   }
-  else if (type == eHTMLTag_tr) {
+  else if (aTag == nsGkAtoms::tr) {
     PushBool(mHasWrittenCellsForRow, false);
   }
-  else if (type == eHTMLTag_td || type == eHTMLTag_th) {
+  else if (aTag == nsGkAtoms::td || aTag == nsGkAtoms::th) {
     // We must make sure that the content of two table cells get a
     // space between them.
 
     // To make the separation between cells most obvious and
     // importable, we use a TAB.
     if (GetLastBool(mHasWrittenCellsForRow)) {
       // Bypass |Write| so that the TAB isn't compressed away.
       AddToLine(NS_LITERAL_STRING("\t").get(), 1);
@@ -682,49 +576,49 @@ nsPlainTextSerializer::DoOpenContainer(c
       // We don't always see a <tr> (nor a <table>) before the <td> if we're
       // copying part of a table
       PushBool(mHasWrittenCellsForRow, true); // will never be popped
     }
     else {
       SetLastBool(mHasWrittenCellsForRow, true);
     }
   }
-  else if (type == eHTMLTag_ul) {
+  else if (aTag == nsGkAtoms::ul) {
     // Indent here to support nested lists, which aren't included in li :-(
     EnsureVerticalSpace(mULCount + mOLStackIndex == 0 ? 1 : 0);
          // Must end the current line before we change indention
     mIndent += kIndentSizeList;
     mULCount++;
   }
-  else if (type == eHTMLTag_ol) {
+  else if (aTag == nsGkAtoms::ol) {
     EnsureVerticalSpace(mULCount + mOLStackIndex == 0 ? 1 : 0);
     if (mFlags & nsIDocumentEncoder::OutputFormatted) {
       // Must end the current line before we change indention
       if (mOLStackIndex < OLStackSize) {
         nsAutoString startAttr;
         PRInt32 startVal = 1;
-        if(NS_SUCCEEDED(GetAttributeValue(aNode, nsGkAtoms::start, startAttr))){
+        if (NS_SUCCEEDED(GetAttributeValue(nsGkAtoms::start, startAttr))) {
           PRInt32 rv = 0;
           startVal = startAttr.ToInteger(&rv);
           if (NS_FAILED(rv))
             startVal = 1;
         }
         mOLStack[mOLStackIndex++] = startVal;
       }
     } else {
       mOLStackIndex++;
     }
     mIndent += kIndentSizeList;  // see ul
   }
-  else if (type == eHTMLTag_li &&
+  else if (aTag == nsGkAtoms::li &&
            (mFlags & nsIDocumentEncoder::OutputFormatted)) {
     if (mTagStackIndex > 1 && IsInOL()) {
       if (mOLStackIndex > 0) {
         nsAutoString valueAttr;
-        if(NS_SUCCEEDED(GetAttributeValue(aNode, nsGkAtoms::value, valueAttr))){
+        if (NS_SUCCEEDED(GetAttributeValue(nsGkAtoms::value, valueAttr))) {
           PRInt32 rv = 0;
           PRInt32 valueAttrVal = valueAttr.ToInteger(&rv);
           if (NS_SUCCEEDED(rv))
             mOLStack[mOLStackIndex-1] = valueAttrVal;
         }
         // This is what nsBulletFrame does for OLs:
         mInIndentString.AppendInt(mOLStack[mOLStackIndex-1]++, 10);
       }
@@ -739,73 +633,72 @@ nsPlainTextSerializer::DoOpenContainer(c
       static char bulletCharArray[] = "*o+#";
       PRUint32 index = mULCount > 0 ? (mULCount - 1) : 3;
       char bulletChar = bulletCharArray[index % 4];
       mInIndentString.Append(PRUnichar(bulletChar));
     }
 
     mInIndentString.Append(PRUnichar(' '));
   }
-  else if (type == eHTMLTag_dl) {
+  else if (aTag == nsGkAtoms::dl) {
     EnsureVerticalSpace(1);
   }
-  else if (type == eHTMLTag_dt) {
+  else if (aTag == nsGkAtoms::dt) {
     EnsureVerticalSpace(0);
   }
-  else if (type == eHTMLTag_dd) {
+  else if (aTag == nsGkAtoms::dd) {
     EnsureVerticalSpace(0);
     mIndent += kIndentSizeDD;
   }
-  else if (type == eHTMLTag_span) {
+  else if (aTag == nsGkAtoms::span) {
     ++mSpanLevel;
   }
-  else if (type == eHTMLTag_blockquote) {
+  else if (aTag == nsGkAtoms::blockquote) {
     // Push
     PushBool(mIsInCiteBlockquote, isInCiteBlockquote);
     if (isInCiteBlockquote) {
       EnsureVerticalSpace(0);
       mCiteQuoteLevel++;
     }
     else {
       EnsureVerticalSpace(1);
       mIndent += kTabSize; // Check for some maximum value?
     }
   }
-  else if (type == eHTMLTag_q) {
+  else if (aTag == nsGkAtoms::q) {
     Write(NS_LITERAL_STRING("\""));
   }
 
   // Else make sure we'll separate block level tags,
   // even if we're about to leave, before doing any other formatting.
-  else if (IsBlockLevel(aTag)) {
+  else if (nsContentUtils::IsHTMLBlock(aTag)) {
     EnsureVerticalSpace(0);
   }
 
   //////////////////////////////////////////////////////////////
   if (!(mFlags & nsIDocumentEncoder::OutputFormatted)) {
     return NS_OK;
   }
   //////////////////////////////////////////////////////////////
   // The rest of this routine is formatted output stuff,
   // which we should skip if we're not formatted:
   //////////////////////////////////////////////////////////////
 
   // Push on stack
-  bool currentNodeIsConverted = IsCurrentNodeConverted(aNode);
-  PushBool(mCurrentNodeIsConverted, currentNodeIsConverted);
+  bool currentNodeIsConverted = IsCurrentNodeConverted();
 
-  if (type == eHTMLTag_h1 || type == eHTMLTag_h2 ||
-      type == eHTMLTag_h3 || type == eHTMLTag_h4 ||
-      type == eHTMLTag_h5 || type == eHTMLTag_h6)
+  if (aTag == nsGkAtoms::h1 || aTag == nsGkAtoms::h2 ||
+      aTag == nsGkAtoms::h3 || aTag == nsGkAtoms::h4 ||
+      aTag == nsGkAtoms::h5 || aTag == nsGkAtoms::h6)
   {
     EnsureVerticalSpace(2);
     if (mHeaderStrategy == 2) {  // numbered
       mIndent += kIndentSizeHeaders;
       // Caching
-      PRInt32 level = HeaderLevel(type);
+      PRInt32 level = HeaderLevel(aTag);
       // Increase counter for current level
       mHeaderCounter[level]++;
       // Reset all lower levels
       PRInt32 i;
 
       for (i = level + 1; i <= 6; i++) {
         mHeaderCounter[i] = 0;
       }
@@ -816,62 +709,62 @@ nsPlainTextSerializer::DoOpenContainer(c
         leadup.AppendInt(mHeaderCounter[i]);
         leadup.Append(PRUnichar('.'));
       }
       leadup.Append(PRUnichar(' '));
       Write(leadup);
     }
     else if (mHeaderStrategy == 1) { // indent increasingly
       mIndent += kIndentSizeHeaders;
-      for (PRInt32 i = HeaderLevel(type); i > 1; i--) {
+      for (PRInt32 i = HeaderLevel(aTag); i > 1; i--) {
            // for h(x), run x-1 times
         mIndent += kIndentIncrementHeaders;
       }
     }
   }
-  else if (type == eHTMLTag_a && !currentNodeIsConverted) {
+  else if (aTag == nsGkAtoms::a && !currentNodeIsConverted) {
     nsAutoString url;
-    if (NS_SUCCEEDED(GetAttributeValue(aNode, nsGkAtoms::href, url))
+    if (NS_SUCCEEDED(GetAttributeValue(nsGkAtoms::href, url))
         && !url.IsEmpty()) {
       mURL = url;
     }
   }
-  else if (type == eHTMLTag_sup && mStructs && !currentNodeIsConverted) {
+  else if (aTag == nsGkAtoms::sup && mStructs && !currentNodeIsConverted) {
     Write(NS_LITERAL_STRING("^"));
   }
-  else if (type == eHTMLTag_sub && mStructs && !currentNodeIsConverted) { 
+  else if (aTag == nsGkAtoms::sub && mStructs && !currentNodeIsConverted) {
     Write(NS_LITERAL_STRING("_"));
   }
-  else if (type == eHTMLTag_code && mStructs && !currentNodeIsConverted) {
+  else if (aTag == nsGkAtoms::code && mStructs && !currentNodeIsConverted) {
     Write(NS_LITERAL_STRING("|"));
   }
-  else if ((type == eHTMLTag_strong || type == eHTMLTag_b)
+  else if ((aTag == nsGkAtoms::strong || aTag == nsGkAtoms::b)
            && mStructs && !currentNodeIsConverted) {
     Write(NS_LITERAL_STRING("*"));
   }
-  else if ((type == eHTMLTag_em || type == eHTMLTag_i)
+  else if ((aTag == nsGkAtoms::em || aTag == nsGkAtoms::i)
            && mStructs && !currentNodeIsConverted) {
     Write(NS_LITERAL_STRING("/"));
   }
-  else if (type == eHTMLTag_u && mStructs && !currentNodeIsConverted) {
+  else if (aTag == nsGkAtoms::u && mStructs && !currentNodeIsConverted) {
     Write(NS_LITERAL_STRING("_"));
   }
 
   /* Container elements are always block elements, so we shouldn't
      output any whitespace immediately after the container tag even if
      there's extra whitespace there because the HTML is pretty-printed
      or something. To ensure that happens, tell the serializer we're
      already in whitespace so it won't output more. */
   mInWhitespace = true;
 
   return NS_OK;
 }
 
 nsresult
-nsPlainTextSerializer::DoCloseContainer(PRInt32 aTag)
+nsPlainTextSerializer::DoCloseContainer(nsIAtom* aTag)
 {
   if (mFlags & nsIDocumentEncoder::OutputRaw) {
     // Raw means raw.  Don't even think about doing anything fancy
     // here like indenting, adding line breaks or any other
     // characters such as list item bullets, quote characters
     // around <q>, etc.  I mean it!  Don't make me smack you!
 
     return NS_OK;
@@ -886,19 +779,18 @@ nsPlainTextSerializer::DoCloseContainer(
       // We're dealing with the close tag whose matching
       // open tag had set the mIgnoreAboveIndex value.
       // Reset mIgnoreAboveIndex before discarding this tag.
       mIgnoreAboveIndex = (PRUint32)kNotFound;
     }
     return NS_OK;
   }
 
-  eHTMLTags type = (eHTMLTags)aTag;
   // End current line if we're ending a block level tag
-  if((type == eHTMLTag_body) || (type == eHTMLTag_html)) {
+  if ((aTag == nsGkAtoms::body) || (aTag == nsGkAtoms::html)) {
     // We want the output to end with a new line,
     // but in preformatted areas like text fields,
     // we can't emit newlines that weren't there.
     // So add the newline only in the case of formatted output.
     if (mFlags & nsIDocumentEncoder::OutputFormatted) {
       EnsureVerticalSpace(0);
     }
     else {
@@ -909,71 +801,71 @@ nsPlainTextSerializer::DoCloseContainer(
     return NS_OK;
   }
 
   // Keep this in sync with DoOpenContainer!
   if (!DoOutput()) {
     return NS_OK;
   }
 
-  if (type == eHTMLTag_tr) {
+  if (aTag == nsGkAtoms::tr) {
     PopBool(mHasWrittenCellsForRow);
     // Should always end a line, but get no more whitespace
     if (mFloatingLines < 0)
       mFloatingLines = 0;
     mLineBreakDue = true;
   }
-  else if (((type == eHTMLTag_li) ||
-            (type == eHTMLTag_dt)) &&
+  else if (((aTag == nsGkAtoms::li) ||
+            (aTag == nsGkAtoms::dt)) &&
            (mFlags & nsIDocumentEncoder::OutputFormatted)) {
     // Items that should always end a line, but get no more whitespace
     if (mFloatingLines < 0)
       mFloatingLines = 0;
     mLineBreakDue = true;
   }
-  else if (type == eHTMLTag_pre) {
+  else if (aTag == nsGkAtoms::pre) {
     mFloatingLines = GetLastBool(mIsInCiteBlockquote) ? 0 : 1;
     mLineBreakDue = true;
   }
-  else if (type == eHTMLTag_ul) {
+  else if (aTag == nsGkAtoms::ul) {
     FlushLine();
     mIndent -= kIndentSizeList;
     if (--mULCount + mOLStackIndex == 0) {
       mFloatingLines = 1;
       mLineBreakDue = true;
     }
   }
-  else if (type == eHTMLTag_ol) {
+  else if (aTag == nsGkAtoms::ol) {
     FlushLine(); // Doing this after decreasing OLStackIndex would be wrong.
     mIndent -= kIndentSizeList;
     NS_ASSERTION(mOLStackIndex, "Wrong OLStack level!");
     mOLStackIndex--;
     if (mULCount + mOLStackIndex == 0) {
       mFloatingLines = 1;
       mLineBreakDue = true;
     }
   }  
-  else if (type == eHTMLTag_dl) {
+  else if (aTag == nsGkAtoms::dl) {
     mFloatingLines = 1;
     mLineBreakDue = true;
   }
-  else if (type == eHTMLTag_dd) {
+  else if (aTag == nsGkAtoms::dd) {
     FlushLine();
     mIndent -= kIndentSizeDD;
   }
-  else if (type == eHTMLTag_span) {
+  else if (aTag == nsGkAtoms::span) {
     NS_ASSERTION(mSpanLevel, "Span level will be negative!");
     --mSpanLevel;
   }
-  else if (type == eHTMLTag_div) {
+  else if (aTag == nsGkAtoms::div) {
     if (mFloatingLines < 0)
       mFloatingLines = 0;
     mLineBreakDue = true;
   }
-  else if (type == eHTMLTag_blockquote) {
+  else if (aTag == nsGkAtoms::blockquote) {
     FlushLine();    // Is this needed?
 
     // Pop
     bool isInCiteBlockquote = PopBool(mIsInCiteBlockquote);
 
     if (isInCiteBlockquote) {
       NS_ASSERTION(mCiteQuoteLevel, "CiteQuote level will be negative!");
       mCiteQuoteLevel--;
@@ -981,23 +873,21 @@ nsPlainTextSerializer::DoCloseContainer(
       mHasWrittenCiteBlockquote = true;
     }
     else {
       mIndent -= kTabSize;
       mFloatingLines = 1;
     }
     mLineBreakDue = true;
   }
-  else if (type == eHTMLTag_q) {
+  else if (aTag == nsGkAtoms::q) {
     Write(NS_LITERAL_STRING("\""));
   }
-  else if (IsBlockLevel(aTag)
-           && type != eHTMLTag_script
-           && type != eHTMLTag_doctypeDecl
-           && type != eHTMLTag_markupDecl) {
+  else if (nsContentUtils::IsHTMLBlock(aTag)
+           && aTag != nsGkAtoms::script) {
     // All other blocks get 1 vertical space after them
     // in formatted mode, otherwise 0.
     // This is hard. Sometimes 0 is a better number, but
     // how to know?
     if (mFlags & nsIDocumentEncoder::OutputFormatted)
       EnsureVerticalSpace(1);
     else {
       if (mFloatingLines < 0)
@@ -1011,211 +901,210 @@ nsPlainTextSerializer::DoCloseContainer(
     return NS_OK;
   }
   //////////////////////////////////////////////////////////////
   // The rest of this routine is formatted output stuff,
   // which we should skip if we're not formatted:
   //////////////////////////////////////////////////////////////
 
   // Pop the currentConverted stack
-  bool currentNodeIsConverted = PopBool(mCurrentNodeIsConverted);
+  bool currentNodeIsConverted = IsCurrentNodeConverted();
   
-  if (type == eHTMLTag_h1 || type == eHTMLTag_h2 ||
-      type == eHTMLTag_h3 || type == eHTMLTag_h4 ||
-      type == eHTMLTag_h5 || type == eHTMLTag_h6) {
+  if (aTag == nsGkAtoms::h1 || aTag == nsGkAtoms::h2 ||
+      aTag == nsGkAtoms::h3 || aTag == nsGkAtoms::h4 ||
+      aTag == nsGkAtoms::h5 || aTag == nsGkAtoms::h6) {
     
     if (mHeaderStrategy) {  /*numbered or indent increasingly*/ 
       mIndent -= kIndentSizeHeaders;
     }
     if (mHeaderStrategy == 1 /*indent increasingly*/ ) {
-      for (PRInt32 i = HeaderLevel(type); i > 1; i--) {
+      for (PRInt32 i = HeaderLevel(aTag); i > 1; i--) {
            // for h(x), run x-1 times
         mIndent -= kIndentIncrementHeaders;
       }
     }
     EnsureVerticalSpace(1);
   }
-  else if (type == eHTMLTag_a && !currentNodeIsConverted && !mURL.IsEmpty()) {
+  else if (aTag == nsGkAtoms::a && !currentNodeIsConverted && !mURL.IsEmpty()) {
     nsAutoString temp; 
     temp.AssignLiteral(" <");
     temp += mURL;
     temp.Append(PRUnichar('>'));
     Write(temp);
     mURL.Truncate();
   }
-  else if ((type == eHTMLTag_sup || type == eHTMLTag_sub) 
+  else if ((aTag == nsGkAtoms::sup || aTag == nsGkAtoms::sub)
            && mStructs && !currentNodeIsConverted) {
     Write(kSpace);
   }
-  else if (type == eHTMLTag_code && mStructs && !currentNodeIsConverted) {
+  else if (aTag == nsGkAtoms::code && mStructs && !currentNodeIsConverted) {
     Write(NS_LITERAL_STRING("|"));
   }
-  else if ((type == eHTMLTag_strong || type == eHTMLTag_b)
+  else if ((aTag == nsGkAtoms::strong || aTag == nsGkAtoms::b)
            && mStructs && !currentNodeIsConverted) {
     Write(NS_LITERAL_STRING("*"));
   }
-  else if ((type == eHTMLTag_em || type == eHTMLTag_i)
+  else if ((aTag == nsGkAtoms::em || aTag == nsGkAtoms::i)
            && mStructs && !currentNodeIsConverted) {
     Write(NS_LITERAL_STRING("/"));
   }
-  else if (type == eHTMLTag_u && mStructs && !currentNodeIsConverted) {
+  else if (aTag == nsGkAtoms::u && mStructs && !currentNodeIsConverted) {
     Write(NS_LITERAL_STRING("_"));
   }
 
   return NS_OK;
 }
 
-/**
- * aNode may be null when we're working with the DOM, but then mElement is
- * useable instead.
- */
+bool
+nsPlainTextSerializer::MustSuppressLeaf()
+{
+  if ((mTagStackIndex > 1 &&
+       mTagStack[mTagStackIndex-2] == nsGkAtoms::select) ||
+      (mTagStackIndex > 0 &&
+        mTagStack[mTagStackIndex-1] == nsGkAtoms::select)) {
+    // Don't output the contents of SELECT elements;
+    // Might be nice, eventually, to output just the selected element.
+    // Read more in bug 31994.
+    return true;
+  }
+
+  if (mTagStackIndex > 0 &&
+      (mTagStack[mTagStackIndex-1] == nsGkAtoms::script ||
+       mTagStack[mTagStackIndex-1] == nsGkAtoms::style)) {
+    // Don't output the contents of <script> or <style> tags;
+    return true;
+  }
+
+  return false;
+}
+
+void
+nsPlainTextSerializer::DoAddText(bool aIsLineBreak, const nsAString& aText)
+{
+  // If we don't want any output, just return
+  if (!DoOutput()) {
+    return;
+  }
+
+  if (!aIsLineBreak) {
+    // Make sure to reset this, since it's no longer true.
+    mHasWrittenCiteBlockquote = false;
+  }
+
+  if (mLineBreakDue)
+    EnsureVerticalSpace(mFloatingLines);
+
+  if (MustSuppressLeaf()) {
+    return;
+  }
+
+  if (aIsLineBreak) {
+    // The only times we want to pass along whitespace from the original
+    // html source are if we're forced into preformatted mode via flags,
+    // or if we're prettyprinting and we're inside a <pre>.
+    // Otherwise, either we're collapsing to minimal text, or we're
+    // prettyprinting to mimic the html format, and in neither case
+    // does the formatting of the html source help us.
+    if ((mFlags & nsIDocumentEncoder::OutputPreformatted) ||
+        (mPreFormatted && !mWrapColumn) ||
+        IsInPre()) {
+      EnsureVerticalSpace(mEmptyLines+1);
+    }
+    else if (!mInWhitespace) {
+      Write(kSpace);
+      mInWhitespace = true;
+    }
+    return;
+  }
+
+  /* Check, if we are in a link (symbolized with mURL containing the URL)
+     and the text is equal to the URL. In that case we don't want to output
+     the URL twice so we scrap the text in mURL. */
+  if (!mURL.IsEmpty() && mURL.Equals(aText)) {
+    mURL.Truncate();
+  }
+  Write(aText);
+}
+
 nsresult
-nsPlainTextSerializer::DoAddLeaf(const nsIParserNode *aNode, PRInt32 aTag, 
-                                 const nsAString& aText)
+nsPlainTextSerializer::DoAddLeaf(nsIAtom* aTag)
 {
   // If we don't want any output, just return
   if (!DoOutput()) {
     return NS_OK;
   }
 
-  if (aTag != eHTMLTag_whitespace && aTag != eHTMLTag_newline) {
-    // Make sure to reset this, since it's no longer true.
-    mHasWrittenCiteBlockquote = false;
-  }
-  
   if (mLineBreakDue)
     EnsureVerticalSpace(mFloatingLines);
 
-  eHTMLTags type = (eHTMLTags)aTag;
-  
-  if ((mTagStackIndex > 1 &&
-       mTagStack[mTagStackIndex-2] == eHTMLTag_select) ||
-      (mTagStackIndex > 0 &&
-        mTagStack[mTagStackIndex-1] == eHTMLTag_select)) {
-    // Don't output the contents of SELECT elements;
-    // Might be nice, eventually, to output just the selected element.
-    // Read more in bug 31994.
-    return NS_OK;
-  }
-  else if (mTagStackIndex > 0 &&
-           (mTagStack[mTagStackIndex-1] == eHTMLTag_script ||
-            mTagStack[mTagStackIndex-1] == eHTMLTag_style)) {
-    // Don't output the contents of <script> or <style> tags;
+  if (MustSuppressLeaf()) {
     return NS_OK;
   }
-  else if (type == eHTMLTag_text) {
-    /* Check, if we are in a link (symbolized with mURL containing the URL)
-       and the text is equal to the URL. In that case we don't want to output
-       the URL twice so we scrap the text in mURL. */
-    if (!mURL.IsEmpty() && mURL.Equals(aText)) {
-      mURL.Truncate();
-    }
-    Write(aText);
-  }
-  else if (type == eHTMLTag_entity) {
-    nsIParserService* parserService = nsContentUtils::GetParserService();
-    if (parserService) {
-      nsAutoString str(aText);
-      PRInt32 entity;
-      parserService->HTMLConvertEntityToUnicode(str, &entity);
-      if (entity == -1 && 
-          !str.IsEmpty() &&
-          str.First() == (PRUnichar) '#') {
-        PRInt32 err = 0;
-        entity = str.ToInteger(&err, kAutoDetect);  // NCR
-      }
-      nsAutoString temp;
-      temp.Append(PRUnichar(entity));
-      Write(temp);
-    }
-  }
-  else if (type == eHTMLTag_br) {
+
+  if (aTag == nsGkAtoms::br) {
     // Another egregious editor workaround, see bug 38194:
     // ignore the bogus br tags that the editor sticks here and there.
-    nsAutoString typeAttr;
-    if (NS_FAILED(GetAttributeValue(aNode, nsGkAtoms::type, typeAttr))
-        || !typeAttr.EqualsLiteral("_moz")) {
+    nsAutoString tagAttr;
+    if (NS_FAILED(GetAttributeValue(nsGkAtoms::type, tagAttr))
+        || !tagAttr.EqualsLiteral("_moz")) {
       EnsureVerticalSpace(mEmptyLines+1);
     }
   }
-  else if (type == eHTMLTag_whitespace || type == eHTMLTag_newline) {
-    // The only times we want to pass along whitespace from the original
-    // html source are if we're forced into preformatted mode via flags,
-    // or if we're prettyprinting and we're inside a <pre>.
-    // Otherwise, either we're collapsing to minimal text, or we're
-    // prettyprinting to mimic the html format, and in neither case
-    // does the formatting of the html source help us.
-    if (mFlags & nsIDocumentEncoder::OutputPreformatted ||
-        (mPreFormatted && !mWrapColumn) ||
-        IsInPre()) {
-      if (type == eHTMLTag_newline)
-        EnsureVerticalSpace(mEmptyLines+1);
-      else  
-        Write(aText);
-    }
-    else if(!mInWhitespace) {
-      Write(kSpace);
-      mInWhitespace = true;
-    }
-  }
-  else if (type == eHTMLTag_hr &&
+  else if (aTag == nsGkAtoms::hr &&
            (mFlags & nsIDocumentEncoder::OutputFormatted)) {
     EnsureVerticalSpace(0);
 
     // Make a line of dashes as wide as the wrap width
     // XXX honoring percentage would be nice
     nsAutoString line;
     PRUint32 width = (mWrapColumn > 0 ? mWrapColumn : 25);
     while (line.Length() < width) {
       line.Append(PRUnichar('-'));
     }
     Write(line);
 
     EnsureVerticalSpace(0);
   }
-  else if (type == eHTMLTag_img) {
+  else if (aTag == nsGkAtoms::img) {
     /* Output (in decreasing order of preference)
        alt, title or nothing */
     // See <http://www.w3.org/TR/REC-html40/struct/objects.html#edef-IMG>
     nsAutoString imageDescription;
-    if (NS_SUCCEEDED(GetAttributeValue(aNode,
-                                       nsGkAtoms::alt,
+    if (NS_SUCCEEDED(GetAttributeValue(nsGkAtoms::alt,
                                        imageDescription))) {
       // If the alt attribute has an empty value (|alt=""|), output nothing
     }
-    else if (NS_SUCCEEDED(GetAttributeValue(aNode,
-                                            nsGkAtoms::title,
+    else if (NS_SUCCEEDED(GetAttributeValue(nsGkAtoms::title,
                                             imageDescription))
              && !imageDescription.IsEmpty()) {
       imageDescription = NS_LITERAL_STRING(" [") +
                          imageDescription +
                          NS_LITERAL_STRING("] ");
     }
    
     Write(imageDescription);
   }
 
-
   return NS_OK;
 }
 
 /**
  * Adds as many newline as necessary to get |noOfRows| empty lines
  *
  * noOfRows = -1    :   Being in the middle of some line of text
  * noOfRows =  0    :   Being at the start of a line
  * noOfRows =  n>0  :   Having n empty lines before the current line.
  */
 void
 nsPlainTextSerializer::EnsureVerticalSpace(PRInt32 noOfRows)
 {
   // If we have something in the indent we probably want to output
   // it and it's not included in the count for empty lines so we don't
   // realize that we should start a new line.
-  if(noOfRows >= 0 && !mInIndentString.IsEmpty()) {
+  if (noOfRows >= 0 && !mInIndentString.IsEmpty()) {
     EndLine(false);
     mInWhitespace = true;
   }
 
   while(mEmptyLines < noOfRows) {
     EndLine(false);
     mInWhitespace = true;
   }
@@ -1229,18 +1118,18 @@ nsPlainTextSerializer::EnsureVerticalSpa
  * this function destroys the cache information.
  *
  * It will also write indentation and quotes if we believe us to be
  * at the start of the line.
  */
 void
 nsPlainTextSerializer::FlushLine()
 {
-  if(!mCurrentLine.IsEmpty()) {
-    if(mAtFirstColumn) {
+  if (!mCurrentLine.IsEmpty()) {
+    if (mAtFirstColumn) {
       OutputQuotesAndIndent(); // XXX: Should we always do this? Bug?
     }
 
     Output(mCurrentLine);
     mAtFirstColumn = mAtFirstColumn && mCurrentLine.IsEmpty();
     mCurrentLine.Truncate();
     mCurrentLineWidth = 0;
   }
@@ -1288,60 +1177,60 @@ nsPlainTextSerializer::AddToLine(const P
                                  PRInt32 aLineFragmentLength)
 {
   PRUint32 prefixwidth = (mCiteQuoteLevel > 0 ? mCiteQuoteLevel + 1:0)+mIndent;
   
   if (mLineBreakDue)
     EnsureVerticalSpace(mFloatingLines);
 
   PRInt32 linelength = mCurrentLine.Length();
-  if(0 == linelength) {
-    if(0 == aLineFragmentLength) {
+  if (0 == linelength) {
+    if (0 == aLineFragmentLength) {
       // Nothing at all. Are you kidding me?
       return;
     }
 
-    if(mFlags & nsIDocumentEncoder::OutputFormatFlowed) {
-      if(IsSpaceStuffable(aLineFragment)
+    if (mFlags & nsIDocumentEncoder::OutputFormatFlowed) {
+      if (IsSpaceStuffable(aLineFragment)
          && mCiteQuoteLevel == 0  // We space-stuff quoted lines anyway
          )
         {
           // Space stuffing a la RFC 2646 (format=flowed).
           mCurrentLine.Append(PRUnichar(' '));
           
-          if(MayWrap()) {
+          if (MayWrap()) {
             mCurrentLineWidth += GetUnicharWidth(' ');
 #ifdef DEBUG_wrapping
             NS_ASSERTION(GetUnicharStringWidth(mCurrentLine.get(),
                                                mCurrentLine.Length()) ==
                          (PRInt32)mCurrentLineWidth,
                          "mCurrentLineWidth and reality out of sync!");
 #endif
           }
         }
     }
     mEmptyLines=-1;
   }
     
   mCurrentLine.Append(aLineFragment, aLineFragmentLength);
-  if(MayWrap()) {
+  if (MayWrap()) {
     mCurrentLineWidth += GetUnicharStringWidth(aLineFragment,
                                                aLineFragmentLength);
 #ifdef DEBUG_wrapping
     NS_ASSERTION(GetUnicharstringWidth(mCurrentLine.get(),
                                        mCurrentLine.Length()) ==
                  (PRInt32)mCurrentLineWidth,
                  "mCurrentLineWidth and reality out of sync!");
 #endif
   }
 
   linelength = mCurrentLine.Length();
 
   //  Wrap?
-  if(MayWrap())
+  if (MayWrap())
   {
 #ifdef DEBUG_wrapping
     NS_ASSERTION(GetUnicharstringWidth(mCurrentLine.get(),
                                   mCurrentLine.Length()) ==
                  (PRInt32)mCurrentLineWidth,
                  "mCurrentLineWidth and reality out of sync!");
 #endif
     // Yes, wrap!
@@ -1397,35 +1286,35 @@ nsPlainTextSerializer::AddToLine(const P
           goodSpace=(prefixwidth>mWrapColumn)?1:mWrapColumn-prefixwidth;
           while (goodSpace < linelength &&
                  !nsCRT::IsAsciiSpace(mCurrentLine.CharAt(goodSpace))) {
             goodSpace++;
           }
         }
       }
       
-      if((goodSpace < linelength) && (goodSpace > 0)) {
+      if ((goodSpace < linelength) && (goodSpace > 0)) {
         // Found a place to break
 
         // -1 (trim a char at the break position)
         // only if the line break was a space.
         if (nsCRT::IsAsciiSpace(mCurrentLine.CharAt(goodSpace))) {
           mCurrentLine.Right(restOfLine, linelength-goodSpace-1);
         }
         else {
           mCurrentLine.Right(restOfLine, linelength-goodSpace);
         }
         // if breaker was U+0020, it has to consider for delsp=yes support
         bool breakBySpace = mCurrentLine.CharAt(goodSpace) == ' ';
         mCurrentLine.Truncate(goodSpace); 
         EndLine(true, breakBySpace);
         mCurrentLine.Truncate();
         // Space stuff new line?
-        if(mFlags & nsIDocumentEncoder::OutputFormatFlowed) {
-          if(!restOfLine.IsEmpty() && IsSpaceStuffable(restOfLine.get())
+        if (mFlags & nsIDocumentEncoder::OutputFormatFlowed) {
+          if (!restOfLine.IsEmpty() && IsSpaceStuffable(restOfLine.get())
               && mCiteQuoteLevel == 0  // We space-stuff quoted lines anyway
             )
           {
             // Space stuffing a la RFC 2646 (format=flowed).
             mCurrentLine.Append(PRUnichar(' '));
             //XXX doesn't seem to work correctly for ' '
           }
         }
@@ -1453,66 +1342,66 @@ nsPlainTextSerializer::AddToLine(const P
  * one specified. Strips ending spaces from the line if it isn't
  * preformatted.
  */
 void
 nsPlainTextSerializer::EndLine(bool aSoftlinebreak, bool aBreakBySpace)
 {
   PRUint32 currentlinelength = mCurrentLine.Length();
 
-  if(aSoftlinebreak && 0 == currentlinelength) {
+  if (aSoftlinebreak && 0 == currentlinelength) {
     // No meaning
     return;
   }
 
   /* In non-preformatted mode, remove spaces from the end of the line for
    * format=flowed compatibility. Don't do this for these special cases:
    * "-- ", the signature separator (RFC 2646) shouldn't be touched and
    * "- -- ", the OpenPGP dash-escaped signature separator in inline
    * signed messages according to the OpenPGP standard (RFC 2440).
    */  
-  if(!(mFlags & nsIDocumentEncoder::OutputPreformatted) &&
+  if (!(mFlags & nsIDocumentEncoder::OutputPreformatted) &&
      (aSoftlinebreak || 
      !(mCurrentLine.EqualsLiteral("-- ") || mCurrentLine.EqualsLiteral("- -- ")))) {
     // Remove spaces from the end of the line.
     while(currentlinelength > 0 &&
           mCurrentLine[currentlinelength-1] == ' ') {
       --currentlinelength;
     }
     mCurrentLine.SetLength(currentlinelength);
   }
   
-  if(aSoftlinebreak &&
+  if (aSoftlinebreak &&
      (mFlags & nsIDocumentEncoder::OutputFormatFlowed) &&
      (mIndent == 0)) {
     // Add the soft part of the soft linebreak (RFC 2646 4.1)
     // We only do this when there is no indentation since format=flowed
     // lines and indentation doesn't work well together.
 
     // If breaker character is ASCII space with RFC 3676 support (delsp=yes),
     // add twice space.
-    if (mFlags & nsIDocumentEncoder::OutputFormatDelSp && aBreakBySpace)
+    if ((mFlags & nsIDocumentEncoder::OutputFormatDelSp) && aBreakBySpace)
       mCurrentLine.Append(NS_LITERAL_STRING("  "));
     else
       mCurrentLine.Append(PRUnichar(' '));
   }
 
-  if(aSoftlinebreak) {
+  if (aSoftlinebreak) {
     mEmptyLines=0;
   } 
   else {
     // Hard break
-    if(!mCurrentLine.IsEmpty() || !mInIndentString.IsEmpty()) {
+    if (!mCurrentLine.IsEmpty() || !mInIndentString.IsEmpty()) {
       mEmptyLines=-1;
     }
 
     mEmptyLines++;
   }
 
-  if(mAtFirstColumn) {
+  if (mAtFirstColumn) {
     // If we don't have anything "real" to output we have to
     // make sure the indent doesn't end in a space since that
     // would trick a format=flowed-aware receiver.
     bool stripTrailingSpaces = mCurrentLine.IsEmpty();
     OutputQuotesAndIndent(stripTrailingSpaces);
   }
 
   mCurrentLine.Append(mLineBreak);
@@ -1562,32 +1451,32 @@ nsPlainTextSerializer::OutputQuotesAndIn
       ) {
     nsAutoString spaces;
     for (int i=0; i < indentwidth; ++i)
       spaces.Append(PRUnichar(' '));
     stringToOutput += spaces;
     mAtFirstColumn = false;
   }
   
-  if(!mInIndentString.IsEmpty()) {
+  if (!mInIndentString.IsEmpty()) {
     stringToOutput += mInIndentString;
     mAtFirstColumn = false;
     mInIndentString.Truncate();
   }
 
-  if(stripTrailingSpaces) {
+  if (stripTrailingSpaces) {
     PRInt32 lineLength = stringToOutput.Length();
     while(lineLength > 0 &&
           ' ' == stringToOutput[lineLength-1]) {
       --lineLength;
     }
     stringToOutput.SetLength(lineLength);
   }
 
-  if(!stringToOutput.IsEmpty()) {
+  if (!stringToOutput.IsEmpty()) {
     Output(stringToOutput);
   }
     
 }
 
 /**
  * Write a string. This is the highlevel function to use to get text output.
  * By using AddToLine, Output, EndLine and other functions it handles quotation,
@@ -1654,34 +1543,34 @@ nsPlainTextSerializer::Write(const nsASt
       // Find one of '\n' or '\r' using iterators since nsAString
       // doesn't have the old FindCharInSet function.
       nsAString::const_iterator iter;           str.BeginReading(iter);
       nsAString::const_iterator done_searching; str.EndReading(done_searching);
       iter.advance(bol); 
       PRInt32 new_newline = bol;
       newline = kNotFound;
       while(iter != done_searching) {
-        if('\n' == *iter || '\r' == *iter) {
+        if ('\n' == *iter || '\r' == *iter) {
           newline = new_newline;
           break;
         }
-        if(' ' != *iter)
+        if (' ' != *iter)
           spacesOnly = false;
         ++new_newline;
         ++iter;
       }
 
       // Done searching
       nsAutoString stringpart;
-      if(newline == kNotFound) {
+      if (newline == kNotFound) {
         // No new lines.
         stringpart.Assign(Substring(str, bol, totLen - bol));
-        if(!stringpart.IsEmpty()) {
+        if (!stringpart.IsEmpty()) {
           PRUnichar lastchar = stringpart[stringpart.Length()-1];
-          if((lastchar == '\t') || (lastchar == ' ') ||
+          if ((lastchar == '\t') || (lastchar == ' ') ||
              (lastchar == '\r') ||(lastchar == '\n')) {
             mInWhitespace = true;
           } 
           else {
             mInWhitespace = false;
           }
         }
         mEmptyLines=-1;
@@ -1691,17 +1580,17 @@ nsPlainTextSerializer::Write(const nsASt
       else {
         // There is a newline
         stringpart.Assign(Substring(str, bol, newline-bol));
         mInWhitespace = true;
         outputLineBreak = true;
         mEmptyLines=0;
         atFirstColumn = true;
         bol = newline+1;
-        if('\r' == *iter && bol < totLen && '\n' == *++iter) {
+        if ('\r' == *iter && bol < totLen && '\n' == *++iter) {
           // There was a CRLF in the input. This used to be illegal and
           // stripped by the parser. Apparently not anymore. Let's skip
           // over the LF.
           bol++;
         }
       }
 
       mCurrentLine.AssignLiteral("");
@@ -1710,17 +1599,17 @@ nsPlainTextSerializer::Write(const nsASt
             !stringpart.EqualsLiteral("-- ") &&
             !stringpart.EqualsLiteral("- -- "))
           stringpart.Trim(" ", false, true, true);
         if (IsSpaceStuffable(stringpart.get()) && stringpart[0] != '>')
           mCurrentLine.Append(PRUnichar(' '));
       }
       mCurrentLine.Append(stringpart);
 
-      if(outputQuotes) {
+      if (outputQuotes) {
         // Note: this call messes with mAtFirstColumn
         OutputQuotesAndIndent();
       }
 
       Output(mCurrentLine);
       if (outputLineBreak) {
         Output(mLineBreak);
       }
@@ -1750,17 +1639,17 @@ nsPlainTextSerializer::Write(const nsASt
     nsAutoString remaining;
     str.Right(remaining, totLen - bol);
     foo = ToNewCString(remaining);
     //    printf("Next line: bol = %d, newlinepos = %d, totLen = %d, string = '%s'\n",
     //           bol, nextpos, totLen, foo);
     nsMemory::Free(foo);
 #endif
 
-    if(nextpos == kNotFound) {
+    if (nextpos == kNotFound) {
       // The rest of the string
       offsetIntoBuffer = str.get() + bol;
       AddToLine(offsetIntoBuffer, totLen-bol);
       bol=totLen;
       mInWhitespace=false;
     } 
     else {
       // There's still whitespace left in the string
@@ -1777,29 +1666,29 @@ nsPlainTextSerializer::Write(const nsASt
       // If we're already in whitespace and not preformatted, just skip it:
       if (mInWhitespace && (nextpos == bol) && !mPreFormatted &&
           !(mFlags & nsIDocumentEncoder::OutputPreformatted)) {
         // Skip whitespace
         bol++;
         continue;
       }
 
-      if(nextpos == bol) {
+      if (nextpos == bol) {
         // Note that we are in whitespace.
         mInWhitespace = true;
         offsetIntoBuffer = str.get() + nextpos;
         AddToLine(offsetIntoBuffer, 1);
         bol++;
         continue;
       }
       
       mInWhitespace = true;
       
       offsetIntoBuffer = str.get() + bol;
-      if(mPreFormatted || (mFlags & nsIDocumentEncoder::OutputPreformatted)) {
+      if (mPreFormatted || (mFlags & nsIDocumentEncoder::OutputPreformatted)) {
         // Preserve the real whitespace character
         nextpos++;
         AddToLine(offsetIntoBuffer, nextpos-bol);
         bol = nextpos;
       } 
       else {
         // Replace the whitespace with a space
         AddToLine(offsetIntoBuffer, nextpos-bol);
@@ -1811,118 +1700,70 @@ nsPlainTextSerializer::Write(const nsASt
 }
 
 
 /**
  * Gets the value of an attribute in a string. If the function returns
  * NS_ERROR_NOT_AVAILABLE, there was none such attribute specified.
  */
 nsresult
-nsPlainTextSerializer::GetAttributeValue(const nsIParserNode* aNode,
-                                         nsIAtom* aName,
+nsPlainTextSerializer::GetAttributeValue(nsIAtom* aName,
                                          nsString& aValueRet)
 {
   if (mElement) {
     if (mElement->GetAttr(kNameSpaceID_None, aName, aValueRet)) {
       return NS_OK;
     }
   }
-  else if (aNode) {
-    nsDependentAtomString name(aName); 
-
-    PRInt32 count = aNode->GetAttributeCount();
-    for (PRInt32 i=0;i<count;i++) {
-      const nsAString& key = aNode->GetKeyAt(i);
-      if (key.Equals(name, nsCaseInsensitiveStringComparator())) {
-        aValueRet = aNode->GetValueAt(i);
-        return NS_OK;
-      }
-    }
-  }
 
   return NS_ERROR_NOT_AVAILABLE;
 }
 
 /**
  * Returns true, if the element was inserted by Moz' TXT->HTML converter.
  * In this case, we should ignore it.
  */
 bool 
-nsPlainTextSerializer::IsCurrentNodeConverted(const nsIParserNode* aNode)
+nsPlainTextSerializer::IsCurrentNodeConverted()
 {
   nsAutoString value;
-  nsresult rv = GetAttributeValue(aNode, nsGkAtoms::_class, value);
+  nsresult rv = GetAttributeValue(nsGkAtoms::_class, value);
   return (NS_SUCCEEDED(rv) &&
           (value.EqualsIgnoreCase("moz-txt", 7) ||
            value.EqualsIgnoreCase("\"moz-txt", 8)));
 }
 
 
 // static
-PRInt32
+nsIAtom*
 nsPlainTextSerializer::GetIdForContent(nsIContent* aContent)
 {
   if (!aContent->IsHTML()) {
-    return eHTMLTag_unknown;
+    return nsnull;
   }
 
-  nsIParserService* parserService = nsContentUtils::GetParserService();
-
-  return parserService ? parserService->HTMLAtomTagToId(aContent->Tag()) :
-                         eHTMLTag_unknown;
-}
-
-/**
- * Returns true if the id represents an element of block type.
- * Can be used to determine if a new paragraph should be started.
- */
-bool 
-nsPlainTextSerializer::IsBlockLevel(PRInt32 aId)
-{
-  bool isBlock = false;
-
-  nsIParserService* parserService = nsContentUtils::GetParserService();
-  if (parserService) {
-    parserService->IsBlock(aId, isBlock);
-  }
-
-  return isBlock;
-}
-
-/**
- * Returns true if the id represents a container.
- */
-bool 
-nsPlainTextSerializer::IsContainer(PRInt32 aId)
-{
-  bool isContainer = false;
-
-  nsIParserService* parserService = nsContentUtils::GetParserService();
-  if (parserService) {
-    parserService->IsContainer(aId, isContainer);
-  }
-
-  return isContainer;
+  nsIAtom* localName = aContent->Tag();
+  return localName->IsStaticAtom() ? localName : nsnull;
 }
 
 /**
  * Returns true if we currently are inside a <pre>. The check is done
  * by traversing the tag stack looking for <pre> until we hit a block
  * level tag which is assumed to override any <pre>:s below it in
  * the stack. To do this correctly to a 100% would require access
  * to style which we don't support in this converter.
  */  
 bool
 nsPlainTextSerializer::IsInPre()
 {
   PRInt32 i = mTagStackIndex;
   while(i > 0) {
-    if(mTagStack[i-1] == eHTMLTag_pre)
+    if (mTagStack[i - 1] == nsGkAtoms::pre)
       return true;
-    if(IsBlockLevel(mTagStack[i-1])) {
+    if (nsContentUtils::IsHTMLBlock(mTagStack[i - 1])) {
       // We assume that every other block overrides a <pre>
       return false;
     }
     --i;
   }
 
   // Not a <pre> in the whole stack
   return false;
@@ -1932,51 +1773,51 @@ nsPlainTextSerializer::IsInPre()
  * This method is required only to identify LI's inside OL.
  * Returns TRUE if we are inside an OL tag and FALSE otherwise.
  */
 bool
 nsPlainTextSerializer::IsInOL()
 {
   PRInt32 i = mTagStackIndex;
   while(--i >= 0) {
-    if(mTagStack[i] == eHTMLTag_ol)
+    if (mTagStack[i] == nsGkAtoms::ol)
       return true;
-    if (mTagStack[i] == eHTMLTag_ul) {
+    if (mTagStack[i] == nsGkAtoms::ul) {
       // If a UL is reached first, LI belongs the UL nested in OL.
       return false;
     }
   }
   // We may reach here for orphan LI's.
   return false;
 }
 
 /*
   @return 0 = no header, 1 = h1, ..., 6 = h6
 */
-PRInt32 HeaderLevel(eHTMLTags aTag)
+PRInt32 HeaderLevel(nsIAtom* aTag)
 {
-  PRInt32 result;
-  switch (aTag)
-  {
-    case eHTMLTag_h1:
-      result = 1; break;
-    case eHTMLTag_h2:
-      result = 2; break;
-    case eHTMLTag_h3:
-      result = 3; break;
-    case eHTMLTag_h4:
-      result = 4; break;
-    case eHTMLTag_h5:
-      result = 5; break;
-    case eHTMLTag_h6:
-      result = 6; break;
-    default:
-      result = 0; break;
+  if (aTag == nsGkAtoms::h1) {
+    return 1;
+  }
+  if (aTag == nsGkAtoms::h2) {
+    return 2;
+  }
+  if (aTag == nsGkAtoms::h3) {
+    return 3;
   }
-  return result;
+  if (aTag == nsGkAtoms::h4) {
+    return 4;
+  }
+  if (aTag == nsGkAtoms::h5) {
+    return 5;
+  }
+  if (aTag == nsGkAtoms::h6) {
+    return 6;
+  }
+  return 0;
 }
 
 
 /*
  * This is an implementation of GetUnicharWidth() and
  * GetUnicharStringWidth() as defined in
  * "The Single UNIX Specification, Version 2, The Open Group, 1997"
  * <http://www.UNIX-systems.org/online.html>
--- a/content/base/src/nsPlainTextSerializer.h
+++ b/content/base/src/nsPlainTextSerializer.h
@@ -40,36 +40,31 @@
  * nsIDocumentEncoder to convert a DOM into plaintext in a nice way
  * (eg for copy/paste as plaintext).
  */
 
 #ifndef nsPlainTextSerializer_h__
 #define nsPlainTextSerializer_h__
 
 #include "nsIContentSerializer.h"
-#include "nsIHTMLContentSink.h"
-#include "nsHTMLTags.h"
 #include "nsCOMPtr.h"
 #include "nsString.h"
 #include "nsILineBreaker.h"
 #include "nsIContent.h"
 #include "nsIAtom.h"
-#include "nsIHTMLToTextSink.h"
 #include "nsIDocumentEncoder.h"
 #include "nsTArray.h"
 
 namespace mozilla {
 namespace dom {
 class Element;
 } // namespace dom
 } // namespace mozilla
 
-class nsPlainTextSerializer : public nsIContentSerializer,
-                              public nsIHTMLContentSink,
-                              public nsIHTMLToTextSink
+class nsPlainTextSerializer : public nsIContentSerializer
 {
 public:
   nsPlainTextSerializer();
   virtual ~nsPlainTextSerializer();
 
   NS_DECL_ISUPPORTS
 
   // nsIContentSerializer
@@ -95,63 +90,39 @@ public:
                                 nsAString& aStr); 
   NS_IMETHOD AppendElementEnd(mozilla::dom::Element* aElement,
                               nsAString& aStr);
   NS_IMETHOD Flush(nsAString& aStr);
 
   NS_IMETHOD AppendDocumentStart(nsIDocument *aDocument,
                                  nsAString& aStr);
 
-  // nsIContentSink
-  NS_IMETHOD WillParse(void) { return NS_OK; }
-  NS_IMETHOD WillInterrupt(void) { return NS_OK; }
-  NS_IMETHOD WillResume(void) { return NS_OK; }
-  NS_IMETHOD SetParser(nsParserBase* aParser) { return NS_OK; }
-  NS_IMETHOD OpenContainer(const nsIParserNode& aNode);
-  NS_IMETHOD CloseContainer(const nsHTMLTag aTag);
-  NS_IMETHOD AddLeaf(const nsIParserNode& aNode);
-  virtual void FlushPendingNotifications(mozFlushType aType) { }
-  NS_IMETHOD SetDocumentCharset(nsACString& aCharset) { return NS_OK; }
-  virtual nsISupports *GetTarget() { return nsnull; }
-
-  // nsIHTMLContentSink
-  NS_IMETHOD OpenHead();
-  NS_IMETHOD IsEnabled(PRInt32 aTag, bool* aReturn);
-  NS_IMETHOD NotifyTagObservers(nsIParserNode* aNode) { return NS_OK; }
-
-  NS_IMETHOD BeginContext(PRInt32 aPosition) { return NS_OK; }
-  NS_IMETHOD EndContext(PRInt32 aPosition) { return NS_OK; }
-  NS_IMETHOD DidProcessTokens(void) { return NS_OK; }
-  NS_IMETHOD WillProcessAToken(void) { return NS_OK; }
-  NS_IMETHOD DidProcessAToken(void) { return NS_OK; }
-
-  // nsIHTMLToTextSink
-  NS_IMETHOD Initialize(nsAString* aOutString,
-                        PRUint32 aFlags, PRUint32 aWrapCol);
-
 protected:
-  nsresult GetAttributeValue(const nsIParserNode* node, nsIAtom* aName, nsString& aValueRet);
+  nsresult GetAttributeValue(nsIAtom* aName, nsString& aValueRet);
   void AddToLine(const PRUnichar* aStringToAdd, PRInt32 aLength);
   void EndLine(bool softlinebreak, bool aBreakBySpace = false);
   void EnsureVerticalSpace(PRInt32 noOfRows);
   void FlushLine();
   void OutputQuotesAndIndent(bool stripTrailingSpaces=false);
   void Output(nsString& aString);
   void Write(const nsAString& aString);
-  bool IsBlockLevel(PRInt32 aId);
-  bool IsContainer(PRInt32 aId);
   bool IsInPre();
   bool IsInOL();
-  bool IsCurrentNodeConverted(const nsIParserNode* aNode);
-  static PRInt32 GetIdForContent(nsIContent* aContent);
-  nsresult DoOpenContainer(const nsIParserNode* aNode, PRInt32 aTag);
-  nsresult DoCloseContainer(PRInt32 aTag);
-  nsresult DoAddLeaf(const nsIParserNode* aNode,
-                     PRInt32 aTag,
-                     const nsAString& aText);
+  bool IsCurrentNodeConverted();
+  bool MustSuppressLeaf();
+
+  /**
+   * Returns the local name of the element as an atom if the element is an
+   * HTML element and the atom is a static atom. Otherwise, nsnull is returned.
+   */
+  static nsIAtom* GetIdForContent(nsIContent* aContent);
+  nsresult DoOpenContainer(nsIAtom* aTag);
+  nsresult DoCloseContainer(nsIAtom* aTag);
+  nsresult DoAddLeaf(nsIAtom* aTag);
+  void DoAddText(bool aIsWhitespace, const nsAString& aText);
 
   // Inlined functions
   inline bool MayWrap()
   {
     return mWrapColumn &&
       ((mFlags & nsIDocumentEncoder::OutputFormatted) ||
        (mFlags & nsIDocumentEncoder::OutputWrap));
   }
@@ -235,24 +206,25 @@ protected:
                                           mHeaderCounter[1] for <h1> etc. */
 
   nsRefPtr<mozilla::dom::Element> mElement;
 
   // For handling table rows
   nsAutoTArray<bool, 8> mHasWrittenCellsForRow;
   
   // Values gotten in OpenContainer that is (also) needed in CloseContainer
-  nsAutoTArray<bool, 8> mCurrentNodeIsConverted;
   nsAutoTArray<bool, 8> mIsInCiteBlockquote;
 
   // The output data
   nsAString*            mOutputString;
 
-  // The tag stack: the stack of tags we're operating on, so we can nest:
-  nsHTMLTag       *mTagStack;
+  // The tag stack: the stack of tags we're operating on, so we can nest.
+  // The stack only ever points to static atoms, so they don't need to be
+  // refcounted.
+  nsIAtom**        mTagStack;
   PRUint32         mTagStackIndex;
 
   // Content in the stack above this index should be ignored:
   PRUint32          mIgnoreAboveIndex;
 
   // The stack for ordered lists
   PRInt32         *mOLStack;
   PRUint32         mOLStackIndex;
--- a/content/html/content/src/nsFormSubmission.cpp
+++ b/content/html/content/src/nsFormSubmission.cpp
@@ -264,17 +264,16 @@ HandleMailtoSubject(nsCString& aPath) {
     if (NS_FAILED(rv))
       return;
     const PRUnichar *formatStrings[] = { brandName.get() };
     nsXPIDLString subjectStr;
     rv = nsContentUtils::FormatLocalizedString(
                                            nsContentUtils::eFORMS_PROPERTIES,
                                            "DefaultFormSubject",
                                            formatStrings,
-                                           ArrayLength(formatStrings),
                                            subjectStr);
     if (NS_FAILED(rv))
       return;
     aPath.AppendLiteral("subject=");
     nsCString subjectStrEscaped;
     aPath.Append(NS_EscapeURL(NS_ConvertUTF16toUTF8(subjectStr), esc_Query,
                               subjectStrEscaped));
   }
--- a/content/html/content/src/nsHTMLInputElement.cpp
+++ b/content/html/content/src/nsHTMLInputElement.cpp
@@ -3825,17 +3825,17 @@ nsHTMLInputElement::GetValidationMessage
       GetTextLength(&textLength);
 
       strMaxLength.AppendInt(maxLength);
       strTextLength.AppendInt(textLength);
 
       const PRUnichar* params[] = { strMaxLength.get(), strTextLength.get() };
       rv = nsContentUtils::FormatLocalizedString(nsContentUtils::eDOM_PROPERTIES,
                                                  "FormValidationTextTooLong",
-                                                 params, 2, message);
+                                                 params, message);
       aValidationMessage = message;
       break;
     }
     case VALIDITY_STATE_VALUE_MISSING:
     {
       nsXPIDLString message;
       nsCAutoString key;
       switch (mType)
@@ -3884,17 +3884,17 @@ nsHTMLInputElement::GetValidationMessage
                                                 message);
       } else {
         if (title.Length() > nsIConstraintValidation::sContentSpecifiedMaxLengthMessage) {
           title.Truncate(nsIConstraintValidation::sContentSpecifiedMaxLengthMessage);
         }
         const PRUnichar* params[] = { title.get() };
         rv = nsContentUtils::FormatLocalizedString(nsContentUtils::eDOM_PROPERTIES,
                                                    "FormValidationPatternMismatchWithTitle",
-                                                   params, 1, message);
+                                                   params, message);
       }
       aValidationMessage = message;
       break;
     }
     default:
       rv = nsIConstraintValidation::GetValidationMessage(aValidationMessage, aType);
   }
 
--- a/content/html/content/src/nsHTMLTextAreaElement.cpp
+++ b/content/html/content/src/nsHTMLTextAreaElement.cpp
@@ -1418,17 +1418,17 @@ nsHTMLTextAreaElement::GetValidationMess
         GetTextLength(&textLength);
 
         strMaxLength.AppendInt(maxLength);
         strTextLength.AppendInt(textLength);
 
         const PRUnichar* params[] = { strMaxLength.get(), strTextLength.get() };
         rv = nsContentUtils::FormatLocalizedString(nsContentUtils::eDOM_PROPERTIES,
                                                    "FormValidationTextTooLong",
-                                                   params, 2, message);
+                                                   params, message);
         aValidationMessage = message;
       }
       break;
     case VALIDITY_STATE_VALUE_MISSING:
       {
         nsXPIDLString message;
         rv = nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES,
                                                 "FormValidationValueMissing",
--- a/content/svg/content/src/nsSVGElement.cpp
+++ b/content/svg/content/src/nsSVGElement.cpp
@@ -1461,20 +1461,24 @@ nsSVGElement::MaybeSerializeAttrBeforeRe
 {
   if (!aNotify ||
       !nsContentUtils::HasMutationListeners(this,
                                             NS_EVENT_BITS_MUTATION_ATTRMODIFIED,
                                             this)) {
     return;
   }
 
+  const nsAttrValue* attrValue = mAttrsAndChildren.GetAttr(aName);
+  if (!attrValue)
+    return;
+
   nsAutoString serializedValue;
-  mAttrsAndChildren.GetAttr(aName)->ToString(serializedValue);
-  nsAttrValue attrValue(serializedValue);
-  mAttrsAndChildren.SetAndTakeAttr(aName, attrValue);
+  attrValue->ToString(serializedValue);
+  nsAttrValue oldAttrValue(serializedValue);
+  mAttrsAndChildren.SetAndTakeAttr(aName, oldAttrValue);
 }
 
 /* static */
 nsIAtom* nsSVGElement::GetEventNameForAttr(nsIAtom* aAttr)
 {
   if (aAttr == nsGkAtoms::onload)
     return nsGkAtoms::onSVGLoad;
   if (aAttr == nsGkAtoms::onunload)
--- a/content/svg/content/test/test_SVGLengthList.xhtml
+++ b/content/svg/content/test/test_SVGLengthList.xhtml
@@ -56,16 +56,23 @@ function run_tests()
   eventChecker.expect("modify");
   lengths[0].valueAsString = "10";
   eventChecker.expect("");
   lengths[0].value = 10;
   lengths[0].valueInSpecifiedUnits = 10;
   lengths[0].valueAsString = "10";
   lengths[0].convertToSpecifiedUnits(SVGLength.SVG_LENGTHTYPE_NUMBER);
   lengths[0].newValueSpecifiedUnits(SVGLength.SVG_LENGTHTYPE_NUMBER, 10);
+  // -- Attribute removal
+  eventChecker.expect("remove");
+  text.removeAttribute("x");
+  // -- Non-existent attribute removal
+  eventChecker.expect("");
+  text.removeAttribute("x");
+  text.removeAttributeNS(null, "x");
   eventChecker.finish();
 
   SimpleTest.finish();
 }
 
 window.addEventListener("load", run_tests, false);
 
 ]]>
--- a/content/svg/content/test/test_SVGNumberList.xhtml
+++ b/content/svg/content/test/test_SVGNumberList.xhtml
@@ -45,16 +45,23 @@ function run_tests()
   eventChecker.expect("modify modify");
   numbers[0].value = 15;
   text.setAttribute("rotate", "17 20 30");
   // -- Redundant changes
   eventChecker.expect("");
   numbers[0].value = 17;
   numbers[1].value = 20;
   text.setAttribute("rotate", "17 20 30");
+  // -- Attribute removal
+  eventChecker.expect("remove");
+  text.removeAttribute("rotate");
+  // -- Non-existent attribute removal
+  eventChecker.expect("");
+  text.removeAttribute("rotate");
+  text.removeAttributeNS(null, "rotate");
   eventChecker.finish();
 
   SimpleTest.finish();
 }
 
 window.addEventListener("load", run_tests, false);
 
 ]]>
--- a/content/svg/content/test/test_SVGPathSegList.xhtml
+++ b/content/svg/content/test/test_SVGPathSegList.xhtml
@@ -100,16 +100,25 @@ function run_tests()
   list[0].y = 5;
   path.setAttribute("d", "M20,5 L12,34");
 
   // -- Redundant changes
   eventChecker.expect("");
   list[0].x = 20;
   list[1].y = 34;
   path.setAttribute("d", "M20,5 L12,34");
+
+  // -- Attribute removal
+  eventChecker.expect("remove");
+  path.removeAttribute("d");
+
+  // -- Non-existent attribute removal
+  eventChecker.expect("");
+  path.removeAttribute("d");
+  path.removeAttributeNS(null, "d");
   eventChecker.finish();
 
   SimpleTest.finish();
 }
 
 window.addEventListener("load", run_tests, false);
 
 ]]>
--- a/content/svg/content/test/test_SVGPointList.xhtml
+++ b/content/svg/content/test/test_SVGPointList.xhtml
@@ -45,16 +45,23 @@ function run_tests()
   eventChecker.expect("modify modify");
   points[0].x = 40;
   polyline.setAttribute("points", "30,375 150,380");
   // -- Redundant changes
   eventChecker.expect("");
   points[0].x = 30;
   points[1].y = 380;
   polyline.setAttribute("points", "30,375 150,380");
+  // -- Attribute removal
+  eventChecker.expect("remove");
+  polyline.removeAttribute("points");
+  // -- Non-existent attribute removal
+  eventChecker.expect("");
+  polyline.removeAttribute("points");
+  polyline.removeAttributeNS(null, "points");
   eventChecker.finish();
 
   SimpleTest.finish();
 }
 
 window.addEventListener("load", run_tests, false);
 
 ]]>
--- a/content/svg/content/test/test_SVGTransformList.xhtml
+++ b/content/svg/content/test/test_SVGTransformList.xhtml
@@ -419,16 +419,26 @@ function testMutationEvents(g)
   list[0].matrix.e = 5;
 
   // setAttribute interaction
   eventChecker.expect("modify");
   list[0].setMatrix(mx);
   eventChecker.expect("");
   g.setAttribute("transform", "matrix(1, 0, 0, 1, 0, 0)");
   list[0].setMatrix(mx);
+
+  // Attribute removal
+  eventChecker.expect("remove");
+  g.removeAttribute("transform");
+
+  // Non-existent attribute removal
+  eventChecker.expect("");
+  g.removeAttribute("transform");
+  g.removeAttributeNS(null, "transform");
+
   eventChecker.finish();
 }
 
 window.addEventListener("load", main, false);
 
 ]]>
 </script>
 </pre>
--- a/content/svg/content/test/test_SVGxxxList.xhtml
+++ b/content/svg/content/test/test_SVGxxxList.xhtml
@@ -830,16 +830,17 @@ function run_baseVal_API_tests()
          'an object of the wrong type.');
       eventChecker.ignoreEvents();
     }
 
     // Test removal and addition events
 
     eventChecker.expect("remove add");
     t.element.removeAttribute(t.attr_name);
+    t.element.removeAttributeNS(null, t.attr_name);
     res = t.baseVal.appendItem(item);
     eventChecker.finish();
   }
 }
 
 
 /**
  * This function tests the SVGXxxList API for the anim val list (see also the
--- a/content/svg/content/test/test_dataTypesModEvents.html
+++ b/content/svg/content/test/test_dataTypesModEvents.html
@@ -33,16 +33,17 @@ function runTests()
 
   // class attribute
  
   eventChecker.watchAttr(filter, "class");
   eventChecker.expect("add modify remove add");
   filter.setAttribute("class", "foo");
   filter.className.baseVal = "bar";
   filter.removeAttribute("class");
+  filter.removeAttributeNS(null, "class");
   filter.className.baseVal = "foo";
 
   eventChecker.expect("");
   filter.className.baseVal = "foo";
   filter.setAttribute("class", "foo");
 
   // length attribute
 
@@ -52,16 +53,17 @@ function runTests()
   marker.markerWidth.baseVal.value = 8;
   marker.markerWidth.baseVal.valueInSpecifiedUnits = 9;
   marker.markerWidth.baseVal.valueAsString = "10";
   marker.markerWidth.baseVal.convertToSpecifiedUnits(
     SVGLength.SVG_LENGTHTYPE_CM);
   marker.markerWidth.baseVal.newValueSpecifiedUnits(
     SVGLength.SVG_LENGTHTYPE_MM, 11);
   marker.removeAttribute("markerWidth");
+  marker.removeAttributeNS(null, "markerWidth");
   marker.markerWidth.baseVal.value = 8;
 
   eventChecker.expect("remove add modify");
   marker.removeAttribute("markerWidth");
   console.log(marker.getAttribute("markerWidth"));
   marker.markerWidth.baseVal.convertToSpecifiedUnits(
     SVGLength.SVG_LENGTHTYPE_NUMBER);
   console.log(marker.getAttribute("markerWidth"));
@@ -79,43 +81,46 @@ function runTests()
 
   // number attribute
 
   eventChecker.watchAttr(convolve, "divisor");
   eventChecker.expect("add modify remove add");
   convolve.setAttribute("divisor", "12.5");
   convolve.divisor.baseVal = 6;
   convolve.removeAttribute("divisor");
+  convolve.removeAttributeNS(null, "divisor");
   convolve.divisor.baseVal = 8;
 
   eventChecker.expect("");
   convolve.divisor.baseVal = 8;
   convolve.setAttribute("divisor", "8");
 
   // number-optional-number attribute
 
   eventChecker.watchAttr(blur, "stdDeviation");
   eventChecker.expect("add modify remove add");
   blur.setAttribute("stdDeviation", "5, 6");
   blur.stdDeviationX.baseVal = 8;
   blur.removeAttribute("stdDeviation");
+  blur.removeAttributeNS(null, "stdDeviation");
   blur.setAttribute("stdDeviation", "2, 3");
 
   eventChecker.expect("");
   blur.stdDeviationX.baseVal = 2;
   blur.stdDeviationY.baseVal = 3;
   blur.setAttribute("stdDeviation", "2, 3");
 
   // integer attribute
 
   eventChecker.watchAttr(convolve, "targetX");
   eventChecker.expect("add modify remove add");
   convolve.setAttribute("targetX", "12");
   convolve.targetX.baseVal = 6;
   convolve.removeAttribute("targetX");
+  convolve.removeAttributeNS(null, "targetX");
   convolve.targetX.baseVal = 8;
 
   // Check redundant change when comparing typed value to attribute value
   eventChecker.expect("");
   convolve.setAttribute("targetX", "8");
   // Check redundant change when comparing attribute value to typed value
   eventChecker.expect("remove add");
   convolve.removeAttribute("targetX");
@@ -124,16 +129,17 @@ function runTests()
 
   // integer-optional-integer attribute
 
   eventChecker.watchAttr(filter, "filterRes");
   eventChecker.expect("add modify remove add");
   filter.setAttribute("filterRes", "60, 70");
   filter.filterResX.baseVal = 50;
   filter.removeAttribute("filterRes");
+  filter.removeAttributeNS(null, "filterRes");
   filter.setAttribute("filterRes", "50, 60");
 
   eventChecker.expect("");
   filter.filterResX.baseVal = 50;
   filter.setAttribute("filterRes", "50, 60");
   filter.filterResY.baseVal = 60;
 
   // angle attribute
@@ -144,16 +150,17 @@ function runTests()
   marker.orientAngle.baseVal.value = 12;
   marker.orientAngle.baseVal.valueInSpecifiedUnits = 23;
   marker.orientAngle.baseVal.valueAsString = "34";
   marker.orientAngle.baseVal.newValueSpecifiedUnits(
     SVGAngle.SVG_ANGLETYPE_GRAD, 34);
   marker.orientAngle.baseVal.newValueSpecifiedUnits(
     SVGAngle.SVG_ANGLETYPE_GRAD, 45);
   marker.removeAttribute("orient");
+  marker.removeAttributeNS(null, "orient");
   marker.orientAngle.baseVal.value = 40;
 
   eventChecker.expect("");
   marker.orientAngle.baseVal.value = 40;
   marker.orientAngle.baseVal.valueInSpecifiedUnits = 40;
   marker.orientAngle.baseVal.valueAsString = "40";
   marker.orientAngle.baseVal.convertToSpecifiedUnits(
     SVGAngle.SVG_ANGLETYPE_UNSPECIFIED);
@@ -162,73 +169,78 @@ function runTests()
 
   // boolean attribute
 
   eventChecker.watchAttr(convolve, "preserveAlpha");
   eventChecker.expect("add modify remove add");
   convolve.setAttribute("preserveAlpha", "true");
   convolve.preserveAlpha.baseVal = false;
   convolve.removeAttribute("preserveAlpha");
+  convolve.removeAttributeNS(null, "preserveAlpha");
   convolve.preserveAlpha.baseVal = true;
 
   eventChecker.expect("");
   convolve.preserveAlpha.baseVal = true;
   convolve.setAttribute("preserveAlpha", "true");
 
   // enum attribute
 
   eventChecker.watchAttr(convolve, "edgeMode");
   eventChecker.expect("add modify remove add");
   convolve.setAttribute("edgeMode", "none");
   convolve.edgeMode.baseVal = SVGFEConvolveMatrixElement.SVG_EDGEMODE_WRAP;
   convolve.removeAttribute("edgeMode");
+  convolve.removeAttributeNS(null, "edgeMode");
   convolve.edgeMode.baseVal = SVGFEConvolveMatrixElement.SVG_EDGEMODE_NONE;
 
   eventChecker.expect("");
   convolve.edgeMode.baseVal = SVGFEConvolveMatrixElement.SVG_EDGEMODE_NONE;
   convolve.setAttribute("edgeMode", "none");
   convolve.edgeMode.baseVal = SVGFEConvolveMatrixElement.SVG_EDGEMODE_NONE;
 
   // string attribute
 
   eventChecker.watchAttr(convolve, "result");
   eventChecker.expect("add modify remove add");
   convolve.setAttribute("result", "bar");
   convolve.result.baseVal = "foo";
   convolve.removeAttribute("result");
+  convolve.removeAttributeNS(null, "result");
   convolve.result.baseVal = "bar";
 
   eventChecker.expect("");
   convolve.result.baseVal = "bar";
   convolve.setAttribute("result", "bar");
   convolve.result.baseVal = "bar";
 
   // preserveAspectRatio attribute
 
   eventChecker.watchAttr(marker, "preserveAspectRatio");
   eventChecker.expect("add modify remove add");
   marker.setAttribute("preserveAspectRatio", "xMaxYMid slice");
   marker.preserveAspectRatio.baseVal.align =
     SVGPreserveAspectRatio.SVG_PRESERVEASPECTRATIO_XMAXYMAX;
   marker.removeAttribute("preserveAspectRatio");
+  marker.removeAttributeNS(null, "preserveAspectRatio");
   marker.preserveAspectRatio.baseVal.align =
     SVGPreserveAspectRatio.SVG_PRESERVEASPECTRATIO_XMIDYMIN;
 
   eventChecker.expect("");
   marker.preserveAspectRatio.baseVal.meetOrSlice =
     SVGPreserveAspectRatio.SVG_MEETORSLICE_MEET;
   marker.setAttribute("preserveAspectRatio", "xMidYMin meet");
 
   // viewBox attribute
 
   eventChecker.watchAttr(marker, "viewBox");
   eventChecker.expect("add modify remove add");
   marker.setAttribute("viewBox", "1 2 3 4");
   marker.viewBox.baseVal.height = 5;
   marker.removeAttribute("viewBox");
+  marker.removeAttributeNS(null, "viewBox");
   marker.viewBox.baseVal.height = 4;
 
   eventChecker.ignoreEvents();
   marker.setAttribute("viewBox", "1 2 3 4");
   eventChecker.expect("");
   marker.viewBox.baseVal.height = 4;
   marker.viewBox.baseVal.x = 1;
   marker.setAttribute("viewBox", "1 2 3 4");
--- a/content/xbl/test/test_bug389322.xhtml
+++ b/content/xbl/test/test_bug389322.xhtml
@@ -108,17 +108,17 @@ function report(testName, success) {
     let (x=1) (x);
     var success = true;
   }
   catch (e) { success = false; }
   report("HTML script tags with explicit version", success)
 ]]></script>
 <script type="text/javascript"><![CDATA[
   try {
-    let (x=1) (x);
+    eval("let (x=1) (x)");
     var success = false;
   }
   catch (e) { success = true; }
   is(success, true, "JS 1.7 should not work in versionless HTML script tags");
 ]]></script>
 </pre>
 </body>
 </html>
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -1928,22 +1928,16 @@ nsGlobalWindow::SetNewDocument(nsIDocume
      happens, setting status isn't a big requirement, so don't. (Doesn't happen
      under normal circumstances, but bug 49615 describes a case.) */
 
   nsContentUtils::AddScriptRunner(
     NS_NewRunnableMethod(this, &nsGlobalWindow::ClearStatus));
 
   bool reUseInnerWindow = aForceReuseInnerWindow || wouldReuseInnerWindow;
 
-  // Remember the old document's principal.
-  nsIPrincipal *oldPrincipal = nsnull;
-  if (oldDoc) {
-    oldPrincipal = oldDoc->NodePrincipal();
-  }
-
   nsresult rv = NS_OK;
 
   // Set mDocument even if this is an outer window to avoid
   // having to *always* reach into the inner window to find the
   // document.
   mDocument = do_QueryInterface(aDocument);
   mDoc = aDocument;
 
@@ -1951,16 +1945,17 @@ nsGlobalWindow::SetNewDocument(nsIDocume
   mLastOpenedURI = aDocument->GetDocumentURI();
 #endif
 
   mContext->WillInitializeContext();
 
   nsGlobalWindow *currentInner = GetCurrentInnerWindowInternal();
 
   nsRefPtr<nsGlobalWindow> newInnerWindow;
+  bool createdInnerWindow = false;
 
   bool thisChrome = IsChromeWindow();
 
   bool isChrome = false;
 
   nsCxPusher cxPusher;
   if (!cxPusher.Push(cx)) {
     return NS_ERROR_FAILURE;
@@ -2029,16 +2024,17 @@ nsGlobalWindow::SetNewDocument(nsIDocume
       rv = mContext->CreateNativeGlobalForInner(sgo, isChrome,
                                                 aDocument->NodePrincipal(),
                                                 &newInnerWindow->mJSObject,
                                                 getter_AddRefs(holder));
       NS_ASSERTION(NS_SUCCEEDED(rv) && newInnerWindow->mJSObject && holder,
                    "Failed to get script global and holder");
 
       mCreatingInnerWindow = false;
+      createdInnerWindow = true;
       Thaw();
 
       NS_ENSURE_SUCCESS(rv, rv);
     }
 
     if (currentInner && currentInner->mJSObject) {
       if (oldDoc == aDocument) {
         // Move the navigator from the old inner window to the new one since
@@ -2109,16 +2105,29 @@ nsGlobalWindow::SetNewDocument(nsIDocume
             cx,
             newInnerWindow->mJSObject
           };
           priv->waiverWrapperMap->Enumerate(ReparentWaiverWrappers, &closure);
         }
       }
     }
 
+    // If we created a new inner window above, we need to do the last little bit
+    // of initialization now that the dust has settled.
+    if (createdInnerWindow) {
+      nsIXPConnect *xpc = nsContentUtils::XPConnect();
+      nsCOMPtr<nsIXPConnectWrappedNative> wrapper;
+      nsresult rv = xpc->GetWrappedNativeOfJSObject(cx, newInnerWindow->mJSObject,
+                                                    getter_AddRefs(wrapper));
+      NS_ENSURE_SUCCESS(rv, rv);
+      NS_ABORT_IF_FALSE(wrapper, "bad wrapper");
+      rv = wrapper->FinishInitForWrappedGlobal();
+      NS_ENSURE_SUCCESS(rv, rv);
+    }
+
     JSAutoEnterCompartment ac;
     if (!ac.enter(cx, mJSObject)) {
       NS_ERROR("unable to enter a compartment");
       return NS_ERROR_FAILURE;
     }
 
     // XXX Not sure if this is needed.
     if (aState) {
@@ -4688,17 +4697,17 @@ nsGlobalWindow::MakeScriptDialogTitle(ns
               nsCAutoString prepath;
               fixedURI->GetPrePath(prepath);
 
               NS_ConvertUTF8toUTF16 ucsPrePath(prepath);
               const PRUnichar *formatStrings[] = { ucsPrePath.get() };
               nsXPIDLString tempString;
               nsContentUtils::FormatLocalizedString(nsContentUtils::eCOMMON_DIALOG_PROPERTIES,
                                                     "ScriptDlgHeading",
-                                                    formatStrings, ArrayLength(formatStrings),
+                                                    formatStrings,
                                                     tempString);
               aOutTitle = tempString;
             }
           }
         }
       }
     }
     else { // failed to get subject principal
--- a/dom/base/nsJSEnvironment.cpp
+++ b/dom/base/nsJSEnvironment.cpp
@@ -142,31 +142,34 @@ static PRLogModuleInfo* gJSDiagnostics;
 // and doing the actual CC.
 #define NS_CC_DELAY                 6000 // ms
 
 #define NS_CC_SKIPPABLE_DELAY       400 // ms
 
 // Force a CC after this long if there's anything in the purple buffer.
 #define NS_CC_FORCED                (2 * 60 * PR_USEC_PER_SEC) // 2 min
 
+// Don't allow an incremental GC to lock out the CC for too long.
+#define NS_MAX_CC_LOCKEDOUT_TIME    (5 * PR_USEC_PER_SEC) // 5 seconds
+
 // Trigger a CC if the purple buffer exceeds this size when we check it.
 #define NS_CC_PURPLE_LIMIT          250
 
 #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 PRTime sLastCCEndTime;
 
-static bool sGCHasRun;
 static bool sCCLockedOut;
+static PRTime sCCLockedOutTime;
 
 static js::GCSliceCallback sPrevGCSliceCallback;
 
 // 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
 // we're waiting for a slow page to load. IOW, this count may be 0
@@ -807,17 +810,17 @@ nsJSContext::DOMOperationCallback(JSCont
   if (script) {
     const char *filename = ::JS_GetScriptFilename(cx, script);
     if (filename) {
       nsXPIDLString scriptLocation;
       NS_ConvertUTF8toUTF16 filenameUTF16(filename);
       const PRUnichar *formatParams[] = { filenameUTF16.get() };
       rv = nsContentUtils::FormatLocalizedString(nsContentUtils::eDOM_PROPERTIES,
                                                  "KillScriptLocation",
-                                                 formatParams, 1,
+                                                 formatParams,
                                                  scriptLocation);
 
       if (NS_SUCCEEDED(rv) && scriptLocation) {
         msg.AppendLiteral("\n\n");
         msg.Append(scriptLocation);
 
         JSStackFrame *fp, *iterator = nsnull;
         fp = ::JS_FrameIterator(cx, &iterator);
@@ -2309,29 +2312,21 @@ nsJSContext::CreateOuterObject(nsIScript
 
   return SetOuterObject(outer);
 }
 
 nsresult
 nsJSContext::SetOuterObject(JSObject* aOuterObject)
 {
   // Force our context's global object to be the outer.
+  // NB: JS_SetGlobalObject sets mContext->compartment.
   JS_SetGlobalObject(mContext, aOuterObject);
 
-  // NB: JS_SetGlobalObject sets mContext->compartment.
+  // Set up the prototype for the outer object.
   JSObject *inner = JS_GetParent(aOuterObject);
-
-  nsIXPConnect *xpc = nsContentUtils::XPConnect();
-  nsCOMPtr<nsIXPConnectWrappedNative> wrapper;
-  nsresult rv = xpc->GetWrappedNativeOfJSObject(mContext, inner,
-                                                getter_AddRefs(wrapper));
-  NS_ENSURE_SUCCESS(rv, rv);
-  NS_ABORT_IF_FALSE(wrapper, "bad wrapper");
-
-  wrapper->FinishInitForWrappedGlobal();
   JS_SetPrototype(mContext, aOuterObject, JS_GetPrototype(inner));
 
   return NS_OK;
 }
 
 nsresult
 nsJSContext::InitOuterWindow()
 {
@@ -3313,19 +3308,34 @@ TimerFireForgetSkippable(PRUint32 aSuspe
   sTotalForgetSkippableTime += delta;
   sRemovedPurples += (aSuspected - sPreviousSuspectedCount);
   ++sForgetSkippableBeforeCC;
 }
 
 static void
 CCTimerFired(nsITimer *aTimer, void *aClosure)
 {
-  if (sDidShutdown || sCCLockedOut) {
+  if (sDidShutdown) {
     return;
   }
+
+  if (sCCLockedOut) {
+    PRTime now = PR_Now();
+    if (sCCLockedOutTime == 0) {
+      sCCLockedOutTime = now;
+      return;
+    }
+    if (now - sCCLockedOutTime < NS_MAX_CC_LOCKEDOUT_TIME) {
+      return;
+    }
+
+    // Finish the current incremental GC
+    nsJSContext::GarbageCollectNow(js::gcreason::CC_FORCED, nsGCNormal);
+  }
+
   ++sCCTimerFireCount;
 
   // During early timer fires, we only run forgetSkippable. During the first
   // late timer fire, we decide if we are going to have a second and final
   // late timer fire, where we may run the CC.
   const PRUint32 numEarlyTimerFires = NS_CC_DELAY / NS_CC_SKIPPABLE_DELAY - 2;
   bool isLateTimerFire = sCCTimerFireCount > numEarlyTimerFires;
   PRUint32 suspected = nsCycleCollector_suspectedCount();
@@ -3481,16 +3491,18 @@ nsJSContext::KillShrinkGCBuffersTimer()
     NS_RELEASE(sShrinkGCBuffersTimer);
   }
 }
 
 //static
 void
 nsJSContext::KillCCTimer()
 {
+  sCCLockedOutTime = 0;
+
   if (sCCTimer) {
     sCCTimer->Cancel();
 
     NS_RELEASE(sCCTimer);
   }
 }
 
 void
@@ -3519,63 +3531,52 @@ DOMGCSliceCallback(JSRuntime *aRt, js::G
                                         double(delta) / PR_USEC_PER_SEC,
                                         aDesc.logMessage));
     nsCOMPtr<nsIConsoleService> cs = do_GetService(NS_CONSOLESERVICE_CONTRACTID);
     if (cs) {
       cs->LogStringMessage(msg.get());
     }
   }
 
-  // Prevent cycle collections during incremental GC.
+  // Prevent cycle collections and shrinking during incremental GC.
   if (aProgress == js::GC_CYCLE_BEGIN) {
     sCCLockedOut = true;
+    nsJSContext::KillShrinkGCBuffersTimer();
   } else if (aProgress == js::GC_CYCLE_END) {
     sCCLockedOut = false;
   }
 
   // The GC has more work to do, so schedule another GC slice.
   if (aProgress == js::GC_SLICE_END) {
     nsJSContext::KillGCTimer();
-    nsJSContext::KillCCTimer();
-
     nsJSContext::PokeGC(js::gcreason::INTER_SLICE_GC, NS_INTERSLICE_GC_DELAY);
   }
 
   if (aProgress == js::GC_CYCLE_END) {
+    // May need to kill the inter-slice GC timer
+    nsJSContext::KillGCTimer();
+
     sCCollectedWaitingForGC = 0;
     sCleanupSinceLastGC = false;
 
-    if (sGCTimer) {
-      // If we were waiting for a GC to happen, kill the timer.
-      nsJSContext::KillGCTimer();
-
+    if (aDesc.isCompartment) {
       // If this is a compartment GC, restart it. We still want
       // a full GC to happen. Compartment GCs usually happen as a
-      // result of last-ditch or MaybeGC. In both cases its
+      // result of last-ditch or MaybeGC. In both cases it is
       // probably a time of heavy activity and we want to delay
       // the full GC, but we do want it to happen eventually.
-      if (aDesc.isCompartment) {
-        nsJSContext::PokeGC(js::gcreason::POST_COMPARTMENT);
-
-        // We poked the GC, so we can kill any pending CC here.
-        nsJSContext::KillCCTimer();
-      }
-    } else {
-      // If this was a full GC, poke the CC to run soon.
-      if (!aDesc.isCompartment) {
-        sGCHasRun = true;
-        sNeedsFullCC = true;
-        nsJSContext::MaybePokeCC();
-      }
+      nsJSContext::PokeGC(js::gcreason::POST_COMPARTMENT);
     }
 
-    // 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) {
+    sNeedsFullCC = true;
+    nsJSContext::MaybePokeCC();
+
+    if (!aDesc.isCompartment) {
+      // Avoid shrinking during heavy activity, which is suggested by
+      // compartment GC.
       nsJSContext::PokeShrinkGCBuffers();
     }
   }
 
   if (sPrevGCSliceCallback)
     (*sPrevGCSliceCallback)(aRt, aProgress, aDesc);
 }
 
@@ -3664,18 +3665,18 @@ nsJSRuntime::ParseVersion(const nsString
 }
 
 //static
 void
 nsJSRuntime::Startup()
 {
   // initialize all our statics, so that we can restart XPCOM
   sGCTimer = sCCTimer = nsnull;
-  sGCHasRun = false;
   sCCLockedOut = false;
+  sCCLockedOutTime = 0;
   sLastCCEndTime = 0;
   sPendingLoadCount = 0;
   sLoadingInProgress = false;
   sCCollectedWaitingForGC = 0;
   sPostGCEventsToConsole = false;
   sNeedsFullCC = false;
   gNameSpaceManager = nsnull;
   sRuntimeService = nsnull;
--- a/dom/bluetooth/BluetoothAdapter.cpp
+++ b/dom/bluetooth/BluetoothAdapter.cpp
@@ -4,24 +4,70 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsDOMClassInfo.h"
 #include "nsDOMEvent.h"
 #include "nsThreadUtils.h"
 #include "nsXPCOMCIDInternal.h"
 #include "mozilla/LazyIdleThread.h"
-
+#include <dlfcn.h>
 #include "BluetoothAdapter.h"
 
-#if defined(MOZ_WIDGET_GONK)
-#include <bluedroid/bluetooth.h>
-#endif
+USING_BLUETOOTH_NAMESPACE
+
+static struct BluedroidFunctions {
+  bool initialized;
+  bool tried_initialization;
+
+  BluedroidFunctions() :
+    initialized(false),
+    tried_initialization(false)
+  {
+  }
+  
+  int (* bt_enable)();
+  int (* bt_disable)();
+  int (* bt_is_enabled)();
+} sBluedroidFunctions;
+
+static bool EnsureBluetoothInit() {
+  if (sBluedroidFunctions.tried_initialization)
+  {
+    return sBluedroidFunctions.initialized;
+  }
 
-USING_BLUETOOTH_NAMESPACE
+  sBluedroidFunctions.initialized = false;
+  sBluedroidFunctions.tried_initialization = true;
+  
+  void* handle = dlopen("libbluedroid.so", RTLD_LAZY);
+
+  if(!handle) {
+    NS_ERROR("Failed to open libbluedroid.so, bluetooth cannot run");
+    return false;
+  }
+
+  sBluedroidFunctions.bt_enable = (int (*)())dlsym(handle, "bt_enable");
+  if(sBluedroidFunctions.bt_enable == NULL) {
+    NS_ERROR("Failed to attach bt_enable function");
+    return false;
+  }
+  sBluedroidFunctions.bt_disable = (int (*)())dlsym(handle, "bt_disable");
+  if(sBluedroidFunctions.bt_disable == NULL) {
+    NS_ERROR("Failed to attach bt_disable function");
+    return false;
+  }
+  sBluedroidFunctions.bt_is_enabled = (int (*)())dlsym(handle, "bt_is_enabled");
+  if(sBluedroidFunctions.bt_is_enabled == NULL) {
+    NS_ERROR("Failed to attach bt_is_enabled function");
+    return false;
+  }
+  sBluedroidFunctions.initialized = true;
+  return true;
+}
 
 class ToggleBtResultTask : public nsRunnable
 {
   public:
     ToggleBtResultTask(nsRefPtr<BluetoothAdapter>& adapterPtr, bool result)
       : mResult(result)
     {
       MOZ_ASSERT(!NS_IsMainThread());
@@ -49,53 +95,55 @@ class ToggleBtResultTask : public nsRunn
   private:
     nsRefPtr<BluetoothAdapter> mAdapterPtr;
     bool mResult;
 };
 
 class ToggleBtTask : public nsRunnable
 {
   public:
-    ToggleBtTask(bool onOff, BluetoothAdapter* adapterPtr) 
+    ToggleBtTask(bool onOff, BluetoothAdapter* adapterPtr)
       : mOnOff(onOff),
         mAdapterPtr(adapterPtr) 
     {
       MOZ_ASSERT(NS_IsMainThread());
     }
 
     NS_IMETHOD Run() 
     {
       MOZ_ASSERT(!NS_IsMainThread());
 
+      if(!EnsureBluetoothInit()) {
+        NS_ERROR("Failed to load bluedroid library.\n");
+        return NS_ERROR_FAILURE;
+      }
+
       bool result;
 
       //Toggle BT here
-#if defined(MOZ_WIDGET_GONK)  
+
       if (mOnOff) {
-        result = bt_enable();
+        result = sBluedroidFunctions.bt_enable();
       } else {
-        result = bt_disable();
+        result = sBluedroidFunctions.bt_disable();
       }
-#else 
-      result = true;
-#endif
 
       // Create a result thread and pass it to Main Thread, 
       nsCOMPtr<nsIRunnable> resultRunnable = new ToggleBtResultTask(mAdapterPtr, result);
 
       if (NS_FAILED(NS_DispatchToMainThread(resultRunnable))) {
         NS_WARNING("Failed to dispatch to main thread!");
       }
 
       return NS_OK;
     }
 
   private:
+    bool mOnOff;
     nsRefPtr<BluetoothAdapter> mAdapterPtr;
-    bool mOnOff;
 };
 
 DOMCI_DATA(BluetoothAdapter, BluetoothAdapter)
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(BluetoothAdapter)
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(BluetoothAdapter, 
                                                   nsDOMEventTargetHelper)
@@ -118,28 +166,47 @@ NS_IMPL_RELEASE_INHERITED(BluetoothAdapt
 BluetoothAdapter::BluetoothAdapter() 
   : mPower(false)
 {
 }
 
 NS_IMETHODIMP
 BluetoothAdapter::GetPower(bool* aPower)
 {
+#ifdef MOZ_WIDGET_GONK
+  if(!EnsureBluetoothInit()) {
+    NS_ERROR("Failed to load bluedroid library.\n");
+    return NS_ERROR_FAILURE;
+  }
+  *aPower = sBluedroidFunctions.bt_is_enabled();
+#else
   *aPower = mPower;
-
+#endif
   return NS_OK;
 }
 
 NS_IMETHODIMP
 BluetoothAdapter::SetPower(bool aPower)
 {
+
+#ifdef MOZ_WIDGET_GONK
+  // Platform specific check for gonk until object is divided in
+  // different implementations per platform. Linux doesn't require
+  // bluetooth firmware loading, but code should work otherwise.
+  if(!EnsureBluetoothInit()) {
+    NS_ERROR("Failed to load bluedroid library.\n");
+    return NS_ERROR_FAILURE;
+  }
+#endif
   if (mPower != aPower) {
     mPower = aPower;
 
+#ifdef MOZ_WIDGET_GONK
     return ToggleBluetoothAsync();
+#endif
   }
 
   return NS_OK;
 }
 
 nsresult
 BluetoothAdapter::ToggleBluetoothAsync()
 {
--- a/dom/tests/mochitest/general/file_frameElementWrapping.html
+++ b/dom/tests/mochitest/general/file_frameElementWrapping.html
@@ -1,26 +1,32 @@
 <html>
     <script>
-        function check(elt, expectProxy, message) {
-            netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
-            var utils = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
-                              .getInterface(Components.interfaces.nsIDOMWindowUtils);
-            var result = ((utils.getClassName(elt) === 'Proxy') === expectProxy)
-                         ? "PASS"
-                         : "FAIL";
+        function check(elt, expectAccess, prop) {
+            var access = false;
+            try {
+                elt[prop];
+                access = true;
+            }
+            catch (e) {}
+            return access === expectAccess;
+        }
 
+        function sendMessage(success, sameOrigin, prop) {
+            var result = success ? 'PASS' : 'FAIL';
+            var message;
+            if (sameOrigin)
+                message = 'Can access |' + prop + '| if same origin';
+            else
+                message = 'Cannot access |' + prop + '| if not same origin';
             parent.postMessage(result + ',' + message, '*');
         }
 
-        try {
-            // true if same origin, throws otherwise
-            var sameOrigin = parent.location.href !== '';
-        } catch (e) {
-            sameOrigin = false;
+        var sameOrigin = location.host !== 'example.org';
+        var pass = check(frameElement, sameOrigin, 'src');
+        if (!pass) {
+            sendMessage(false, sameOrigin, 'src');
+        } else {
+            pass = check(parent.location, sameOrigin, 'href');
+            sendMessage(pass, sameOrigin, 'href');
         }
-
-        check(frameElement, !sameOrigin,
-              sameOrigin
-              ? 'no wrapper needed if same origin'
-              : 'wrapper needed if not same origin');
     </script>
 </html>
--- a/dom/tests/mochitest/general/test_frameElementWrapping.html
+++ b/dom/tests/mochitest/general/test_frameElementWrapping.html
@@ -1,24 +1,32 @@
 <!DOCTYPE HTML>
 <html>
 <head>
-  <title>Test for location object behaviors</title>
+  <title>Test for same-origin and cross-origin wrapping of frameElement</title>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 <p id="display"></p>
 <div id="content" style="display: none">
  
 </div>
 <iframe id="ifr" src="file_frameElementWrapping.html"></iframe>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
+//
+// This test has sort of morphed over time to become less and less useful.
+// In the past, we had special security policy for frameElement, but that's
+// more or less gone away with compartment/proxy wrapping. So we just go
+// through the motions to make sure that, indeed, frameElement is subject
+// to the same-origin policy.
+//
+
 SimpleTest.waitForExplicitFinish();
 
 var count = 0;
 
 function runTest(result, message) {
     ok(result === 'PASS', message);
 
     if (++count === 2)
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -2920,17 +2920,17 @@ WorkerPrivate::Dispatch(WorkerRunnable* 
       }
     }
 
     if (!aQueue->Push(event)) {
       return false;
     }
 
     if (aQueue == &mControlQueue && mJSContext) {
-      JS_TriggerOperationCallback(mJSContext);
+      JS_TriggerOperationCallback(JS_GetRuntime(mJSContext));
     }
 
     mCondVar.Notify();
   }
 
   event.forget();
   return true;
 }
--- a/editor/libeditor/html/nsEditProperty.cpp
+++ b/editor/libeditor/html/nsEditProperty.cpp
@@ -80,10 +80,10 @@ nsEditProperty::RegisterAtoms()
 {
   // inline tags
   static const nsStaticAtom property_atoms[] = {
 #define EDITOR_ATOM(name_, value_) NS_STATIC_ATOM(name_##_buffer, &name_),
 #include "nsEditPropertyAtomList.h"
 #undef EDITOR_ATOM
   };
   
-  NS_RegisterStaticAtoms(property_atoms, ArrayLength(property_atoms));
+  NS_RegisterStaticAtoms(property_atoms);
 }
--- a/editor/txtsvc/src/nsTextServicesDocument.cpp
+++ b/editor/txtsvc/src/nsTextServicesDocument.cpp
@@ -129,17 +129,17 @@ void
 nsTextServicesDocument::RegisterAtoms()
 {
   static const nsStaticAtom ts_atoms[] = {
 #define TS_ATOM(name_, value_) NS_STATIC_ATOM(name_##_buffer, &name_),
 #include "nsTSAtomList.h"
 #undef TS_ATOM
   };
 
-  NS_RegisterStaticAtoms(ts_atoms, ArrayLength(ts_atoms));
+  NS_RegisterStaticAtoms(ts_atoms);
 }
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsTextServicesDocument)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsTextServicesDocument)
 
 NS_INTERFACE_MAP_BEGIN(nsTextServicesDocument)
   NS_INTERFACE_MAP_ENTRY(nsITextServicesDocument)
   NS_INTERFACE_MAP_ENTRY(nsIEditActionListener)
--- a/embedding/components/windowwatcher/src/nsWindowWatcher.cpp
+++ b/embedding/components/windowwatcher/src/nsWindowWatcher.cpp
@@ -1535,18 +1535,27 @@ PRUint32 nsWindowWatcher::CalculateChrom
   chromeFlags |= WinHasOption(aFeatures, "extrachrome", 0, nsnull) ?
     nsIWebBrowserChrome::CHROME_EXTRA : 0;
   chromeFlags |= WinHasOption(aFeatures, "centerscreen", 0, nsnull) ?
     nsIWebBrowserChrome::CHROME_CENTER_SCREEN : 0;
   chromeFlags |= WinHasOption(aFeatures, "dependent", 0, nsnull) ?
     nsIWebBrowserChrome::CHROME_DEPENDENT : 0;
   chromeFlags |= WinHasOption(aFeatures, "modal", 0, nsnull) ?
     (nsIWebBrowserChrome::CHROME_MODAL | nsIWebBrowserChrome::CHROME_DEPENDENT) : 0;
-  chromeFlags |= WinHasOption(aFeatures, "dialog", 0, nsnull) ?
-    nsIWebBrowserChrome::CHROME_OPENAS_DIALOG : 0;
+
+  /* On mobile we want to ignore the dialog window feature, since the mobile UI
+     does not provide any affordance for dialog windows. This does not interfere
+     with dialog windows created through openDialog. */
+  bool disableDialogFeature = false;
+  nsCOMPtr<nsIPrefBranch> branch = do_QueryInterface(prefs);
+  branch->GetBoolPref("dom.disable_window_open_dialog_feature", &disableDialogFeature);
+  if (!disableDialogFeature) {
+    chromeFlags |= WinHasOption(aFeatures, "dialog", 0, nsnull) ?
+      nsIWebBrowserChrome::CHROME_OPENAS_DIALOG : 0;
+  }
 
   /* and dialogs need to have the last word. assume dialogs are dialogs,
      and opened as chrome, unless explicitly told otherwise. */
   if (aDialog) {
     if (!PL_strcasestr(aFeatures, "dialog"))
       chromeFlags |= nsIWebBrowserChrome::CHROME_OPENAS_DIALOG;
     if (!PL_strcasestr(aFeatures, "chrome"))
       chromeFlags |= nsIWebBrowserChrome::CHROME_OPENAS_CHROME;
--- a/gfx/gl/GLContextProviderEGL.cpp
+++ b/gfx/gl/GLContextProviderEGL.cpp
@@ -37,16 +37,19 @@
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "mozilla/Preferences.h"
 #include "mozilla/Util.h"
 
+#include "nsIScreen.h"
+#include "nsIScreenManager.h"
+
 #if defined(XP_UNIX)
 
 #ifdef MOZ_WIDGET_GTK2
 #include <gdk/gdkx.h>
 // we're using default display for now
 #define GET_NATIVE_WINDOW(aWidget) (EGLNativeWindowType)GDK_WINDOW_XID((GdkWindow *) aWidget->GetNativeData(NS_NATIVE_WINDOW))
 #elif defined(MOZ_WIDGET_QT)
 #include <QtOpenGL/QGLContext>
@@ -2185,72 +2188,59 @@ static const EGLint kEGLConfigAttribsRGB
     LOCAL_EGL_RENDERABLE_TYPE, LOCAL_EGL_OPENGL_ES2_BIT,
     LOCAL_EGL_RED_SIZE,        8,
     LOCAL_EGL_GREEN_SIZE,      8,
     LOCAL_EGL_BLUE_SIZE,       8,
     LOCAL_EGL_ALPHA_SIZE,      8,
     LOCAL_EGL_NONE
 };
 
-// This struct is used only by CreateConfig below, but ISO C++98 forbids
-// instantiating a template dependent on a locally-defined type.  Boo-urns!
-struct EGLAttribs {
-    gfxASurface::gfxImageFormat mFormat;
-    const EGLint* mAttribs;
-};
-
 // Return true if a suitable EGLConfig was found and pass it out
 // through aConfig.  Return false otherwise.
 //
 // NB: It's entirely legal for the returned EGLConfig to be valid yet
 // have the value null.
 static bool
 CreateConfig(EGLConfig* aConfig)
 {
-    EGLAttribs attribsToTry[] = {
-#ifdef MOZ_GFX_OPTIMIZE_MOBILE
-        // Prefer r5g6b5 for potential savings in memory bandwidth.
-        // This needs to be reevaluated for newer devices.
-        { gfxASurface::ImageFormatRGB16_565, kEGLConfigAttribsRGB16 },
-#endif
-        { gfxASurface::ImageFormatARGB32, kEGLConfigAttribsRGBA32 },
-    };
+    nsCOMPtr<nsIScreenManager> screenMgr = do_GetService("@mozilla.org/gfx/screenmanager;1");
+    nsCOMPtr<nsIScreen> screen;
+    screenMgr->GetPrimaryScreen(getter_AddRefs(screen));
+    PRInt32 depth = 24;
+    screen->GetColorDepth(&depth);
 
     EGLConfig configs[64];
-    for (unsigned i = 0; i < ArrayLength(attribsToTry); ++i) {
-        const EGLAttribs& attribs = attribsToTry[i];
-        EGLint ncfg = ArrayLength(configs);
-
-        if (!sEGLLibrary.fChooseConfig(EGL_DISPLAY(), attribs.mAttribs,
-                                       configs, ncfg, &ncfg) ||
-            ncfg < 1)
+    gfxASurface::gfxImageFormat format;
+    const EGLint* attribs = depth == 16 ? kEGLConfigAttribsRGB16 :
+                                          kEGLConfigAttribsRGBA32;
+    EGLint ncfg = ArrayLength(configs);
+
+    if (!sEGLLibrary.fChooseConfig(EGL_DISPLAY(), attribs,
+                                   configs, ncfg, &ncfg) ||
+        ncfg < 1) {
+        return false;
+    }
+
+    for (int j = 0; j < ncfg; ++j) {
+        EGLConfig config = configs[j];
+        EGLint r, g, b, a;
+
+        if (sEGLLibrary.fGetConfigAttrib(EGL_DISPLAY(), config,
+                                         LOCAL_EGL_RED_SIZE, &r) &&
+            sEGLLibrary.fGetConfigAttrib(EGL_DISPLAY(), config,
+                                         LOCAL_EGL_GREEN_SIZE, &g) &&
+            sEGLLibrary.fGetConfigAttrib(EGL_DISPLAY(), config,
+                                         LOCAL_EGL_BLUE_SIZE, &b) &&
+            sEGLLibrary.fGetConfigAttrib(EGL_DISPLAY(), config,
+                                         LOCAL_EGL_ALPHA_SIZE, &a) &&
+            ((depth == 16 && r == 5 && g == 6 && b == 5) ||
+             (depth == 24 && r == 8 && g == 8 && b == 8 && a == 8)))
         {
-            continue;
-        }
-
-        for (int j = 0; j < ncfg; ++j) {
-            EGLConfig config = configs[j];
-            EGLint r, g, b, a;
-
-            if (sEGLLibrary.fGetConfigAttrib(EGL_DISPLAY(), config,
-                                             LOCAL_EGL_RED_SIZE, &r) &&
-                sEGLLibrary.fGetConfigAttrib(EGL_DISPLAY(), config,
-                                             LOCAL_EGL_GREEN_SIZE, &g) &&
-                sEGLLibrary.fGetConfigAttrib(EGL_DISPLAY(), config,
-                                             LOCAL_EGL_BLUE_SIZE, &b) &&
-                sEGLLibrary.fGetConfigAttrib(EGL_DISPLAY(), config,
-                                             LOCAL_EGL_ALPHA_SIZE, &a) &&
-                ((gfxASurface::ImageFormatRGB16_565 == attribs.mFormat &&
-                  r == 5 && g == 6 && b == 5) ||
-                 (gfxASurface::ImageFormatARGB32 == attribs.mFormat &&
-                  r == 8 && g == 8 && b == 8 && a == 8)))
-            {
-                *aConfig = config;
-                return true;
-            }
+            *aConfig = config;
+            return true;
         }
     }
     return false;
 }
 
 static EGLSurface
 CreateSurfaceForWindow(nsIWidget *aWidget, EGLConfig config)
 {
--- a/gfx/thebes/gfxAtoms.cpp
+++ b/gfx/thebes/gfxAtoms.cpp
@@ -53,10 +53,10 @@ using namespace mozilla;
 static const nsStaticAtom atoms[] = {
 #define GFX_ATOM(name_, value_) NS_STATIC_ATOM(name_##_buffer, &gfxAtoms::name_),
 #include "gfxAtomList.h"
 #undef GFX_ATOM
 };
 
 void gfxAtoms::RegisterAtoms()
 {
-    NS_RegisterStaticAtoms(atoms, ArrayLength(atoms));
+    NS_RegisterStaticAtoms(atoms);
 }
--- a/gfx/thebes/gfxDWriteFontList.cpp
+++ b/gfx/thebes/gfxDWriteFontList.cpp
@@ -63,16 +63,20 @@ using namespace mozilla;
                                    PR_LOG_DEBUG)
 
 #define LOG_FONTINIT(args) PR_LOG(gfxPlatform::GetLog(eGfxLog_fontinit), \
                                PR_LOG_DEBUG, args)
 #define LOG_FONTINIT_ENABLED() PR_LOG_TEST( \
                                    gfxPlatform::GetLog(eGfxLog_fontinit), \
                                    PR_LOG_DEBUG)
 
+#define LOG_CMAPDATA_ENABLED() PR_LOG_TEST( \
+                                   gfxPlatform::GetLog(eGfxLog_cmapdata), \
+                                   PR_LOG_DEBUG)
+
 // font info loader constants
 
 // avoid doing this during startup even on slow machines but try to start
 // it soon enough so that system fallback doesn't happen first
 static const PRUint32 kDelayBeforeLoadingFonts = 120 * 1000; // 2 minutes after init
 static const PRUint32 kIntervalBetweenLoadingFonts = 2000;   // every 2 seconds until complete
 
 static __inline void
@@ -366,16 +370,26 @@ gfxDWriteFontEntry::ReadCMAP()
         if (GetFontTable(kCmapTag, buffer) != NS_OK)
             return NS_ERROR_FAILURE;
         PRUint8 *cmap = buffer.Elements();
 
         bool          unicodeFont = false, symbolFont = false;
         rv = gfxFontUtils::ReadCMAP(cmap, buffer.Length(),
                                     mCharacterMap, mUVSOffset,
                                     unicodeFont, symbolFont);
+#ifdef PR_LOGGING
+        LOG_FONTLIST(("(fontlist-cmap) name: %s, size: %d\n",
+                      NS_ConvertUTF16toUTF8(mName).get(), mCharacterMap.GetSize()));
+        if (LOG_CMAPDATA_ENABLED()) {
+            char prefix[256];
+            sprintf(prefix, "(cmapdata) name: %.220s",
+                    NS_ConvertUTF16toUTF8(mName).get());
+            mCharacterMap.Dump(prefix, eGfxLog_cmapdata);
+        }
+#endif
         mHasCmapTable = NS_SUCCEEDED(rv);
         return rv;
     }
 
     // loading using dwrite, don't use GetFontTable to avoid copy
     nsRefPtr<IDWriteFontFace> fontFace;
     rv = CreateFontFace(getter_AddRefs(fontFace));
 
@@ -406,16 +420,22 @@ gfxDWriteFontEntry::ReadCMAP()
                                     isUnicode,
                                     isSymbol);
     }
     fontFace->ReleaseFontTable(tableContext);
 
 #ifdef PR_LOGGING
     LOG_FONTLIST(("(fontlist-cmap) name: %s, size: %d\n",
                   NS_ConvertUTF16toUTF8(mName).get(), mCharacterMap.GetSize()));
+    if (LOG_CMAPDATA_ENABLED()) {
+        char prefix[256];
+        sprintf(prefix, "(cmapdata) name: %.220s",
+                NS_ConvertUTF16toUTF8(mName).get());
+        mCharacterMap.Dump(prefix, eGfxLog_cmapdata);
+    }
 #endif
 
     mHasCmapTable = NS_SUCCEEDED(rv);
     return rv;
 }
 
 gfxFont *
 gfxDWriteFontEntry::CreateFontInstance(const gfxFontStyle* aFontStyle,
@@ -1169,8 +1189,187 @@ gfxDWriteFontList::ResolveFontName(const
     }
 
     if (mNonExistingFonts.Contains(keyName)) {
         return false;
     }
 
     return gfxPlatformFontList::ResolveFontName(aFontName, aResolvedFontName);
 }
+
+static nsresult GetFamilyName(IDWriteFont *aFont, nsString& aFamilyName)
+{
+    HRESULT hr;
+    nsRefPtr<IDWriteFontFamily> family;
+
+    // clean out previous value
+    aFamilyName.Truncate();
+
+    hr = aFont->GetFontFamily(getter_AddRefs(family));
+    if (FAILED(hr)) {
+        return hr;
+    }
+
+    nsRefPtr<IDWriteLocalizedStrings> familyNames;
+
+    hr = family->GetFamilyNames(getter_AddRefs(familyNames));
+    if (FAILED(hr)) {
+        return hr;
+    }
+
+    UINT32 index = 0;
+    BOOL exists = false;
+
+    hr = familyNames->FindLocaleName(L"en-us", &index, &exists);
+    if (FAILED(hr)) {
+        return hr;
+    }
+
+    // If the specified locale doesn't exist, select the first on the list.
+    if (!exists) {
+        index = 0;
+    }
+
+    nsAutoTArray<WCHAR, 32> name;
+    UINT32 length;
+
+    hr = familyNames->GetStringLength(index, &length);
+    if (FAILED(hr)) {
+        return hr;
+    }
+
+    if (!name.SetLength(length + 1)) {
+        return NS_ERROR_FAILURE;
+    }
+    hr = familyNames->GetString(index, name.Elements(), length + 1);
+    if (FAILED(hr)) {
+        return hr;
+    }
+
+    aFamilyName.Assign(name.Elements());
+    return NS_OK;
+}
+
+// bug 705594 - the method below doesn't actually do any "drawing", it's only
+// used to invoke the DirectWrite layout engine to determine the fallback font
+// for a given character.
+
+IFACEMETHODIMP FontFallbackRenderer::DrawGlyphRun(
+    __maybenull void* clientDrawingContext,
+    FLOAT baselineOriginX,
+    FLOAT baselineOriginY,
+    DWRITE_MEASURING_MODE measuringMode,
+    __in DWRITE_GLYPH_RUN const* glyphRun,
+    __in DWRITE_GLYPH_RUN_DESCRIPTION const* glyphRunDescription,
+    __maybenull IUnknown* clientDrawingEffect
+    )
+{
+    if (!mSystemFonts) {
+        return E_FAIL;
+    }
+
+    HRESULT hr = S_OK;
+
+    nsRefPtr<IDWriteFont> font;
+    hr = mSystemFonts->GetFontFromFontFace(glyphRun->fontFace,
+                                           getter_AddRefs(font));
+    if (FAILED(hr)) {
+        return hr;
+    }
+
+    // copy the family name
+    hr = GetFamilyName(font, mFamilyName);
+    if (FAILED(hr)) {
+        return hr;
+    }
+
+    // Arial is used as the default fallback font
+    // so if it matches ==> no font found
+    if (mFamilyName.EqualsLiteral("Arial")) {
+        mFamilyName.Truncate();
+        return E_FAIL;
+    }
+    return hr;
+}
+
+gfxFontEntry*
+gfxDWriteFontList::GlobalFontFallback(const PRUint32 aCh,
+                                      PRInt32 aRunScript,
+                                      const gfxFontStyle* aMatchStyle,
+                                      PRUint32& aCmapCount)
+{
+    bool useCmaps = gfxPlatform::GetPlatform()->UseCmapsDuringSystemFallback();
+
+    if (useCmaps) {
+        return gfxPlatformFontList::GlobalFontFallback(aCh,
+                                                       aRunScript,
+                                                       aMatchStyle,
+                                                       aCmapCount);
+    }
+
+    HRESULT hr;
+
+    nsRefPtr<IDWriteFactory> dwFactory =
+        gfxWindowsPlatform::GetPlatform()->GetDWriteFactory();
+    if (!dwFactory) {
+        return nsnull;
+    }
+
+    // initialize fallback renderer
+    if (!mFallbackRenderer) {
+        mFallbackRenderer = new FontFallbackRenderer(dwFactory);
+    }
+
+    // initialize text format
+    if (!mFallbackFormat) {
+        hr = dwFactory->CreateTextFormat(L"Arial", NULL,
+                                         DWRITE_FONT_WEIGHT_REGULAR,
+                                         DWRITE_FONT_STYLE_NORMAL,
+                                         DWRITE_FONT_STRETCH_NORMAL,
+                                         72.0f, L"en-us",
+                                         getter_AddRefs(mFallbackFormat));
+        if (FAILED(hr)) {
+            return nsnull;
+        }
+    }
+
+    // set up string with fallback character
+    wchar_t str[16];
+    PRUint32 strLen;
+
+    if (IS_IN_BMP(aCh)) {
+        str[0] = static_cast<wchar_t> (aCh);
+        str[1] = 0;
+        strLen = 1;
+    } else {
+        str[0] = static_cast<wchar_t> (H_SURROGATE(aCh));
+        str[1] = static_cast<wchar_t> (L_SURROGATE(aCh));
+        str[2] = 0;
+        strLen = 2;
+    }
+
+    // set up layout
+    nsRefPtr<IDWriteTextLayout> fallbackLayout;
+
+    hr = dwFactory->CreateTextLayout(str, strLen, mFallbackFormat,
+                                     200.0f, 200.0f,
+                                     getter_AddRefs(fallbackLayout));
+    if (FAILED(hr)) {
+        return nsnull;
+    }
+
+    // call the draw method to invoke the DirectWrite layout functions
+    // which determine the fallback font
+    hr = fallbackLayout->Draw(NULL, mFallbackRenderer, 50.0f, 50.0f);
+    if (FAILED(hr)) {
+        return nsnull;
+    }
+
+    gfxFontEntry *fontEntry = nsnull;
+    bool needsBold;  // ignored in the system fallback case
+    fontEntry = FindFontForFamily(mFallbackRenderer->FallbackFamilyName(),
+                                  aMatchStyle, needsBold);
+    if (fontEntry && !fontEntry->TestCharacterMap(aCh)) {
+        fontEntry = nsnull;
+        Telemetry::Accumulate(Telemetry::BAD_FALLBACK_FONT, true);
+    }
+    return fontEntry;
+}
--- a/gfx/thebes/gfxDWriteFontList.h
+++ b/gfx/thebes/gfxDWriteFontList.h
@@ -199,16 +199,155 @@ protected:
     nsRefPtr<IDWriteFont> mFont;
     nsRefPtr<IDWriteFontFile> mFontFile;
     DWRITE_FONT_FACE_TYPE mFaceType;
 
     PRInt8 mIsCJK;
     bool mForceGDIClassic;
 };
 
+// custom text renderer used to determine the fallback font for a given char
+class FontFallbackRenderer : public IDWriteTextRenderer
+{
+public:
+    FontFallbackRenderer(IDWriteFactory *aFactory)
+        : mRefCount(0)
+    {
+        HRESULT hr = S_OK;
+
+        hr = aFactory->GetSystemFontCollection(getter_AddRefs(mSystemFonts));
+        NS_ASSERTION(SUCCEEDED(hr), "GetSystemFontCollection failed!");
+    }
+
+    ~FontFallbackRenderer()
+    {}
+
+    // IDWriteTextRenderer methods
+    IFACEMETHOD(DrawGlyphRun)(
+        __maybenull void* clientDrawingContext,
+        FLOAT baselineOriginX,
+        FLOAT baselineOriginY,
+        DWRITE_MEASURING_MODE measuringMode,
+        __in DWRITE_GLYPH_RUN const* glyphRun,
+        __in DWRITE_GLYPH_RUN_DESCRIPTION const* glyphRunDescription,
+        __maybenull IUnknown* clientDrawingEffect
+        );
+
+    IFACEMETHOD(DrawUnderline)(
+        __maybenull void* clientDrawingContext,
+        FLOAT baselineOriginX,
+        FLOAT baselineOriginY,
+        __in DWRITE_UNDERLINE const* underline,
+        __maybenull IUnknown* clientDrawingEffect
+        )
+    {
+        return E_NOTIMPL;
+    }
+
+
+    IFACEMETHOD(DrawStrikethrough)(
+        __maybenull void* clientDrawingContext,
+        FLOAT baselineOriginX,
+        FLOAT baselineOriginY,
+        __in DWRITE_STRIKETHROUGH const* strikethrough,
+        __maybenull IUnknown* clientDrawingEffect
+        )
+    {
+        return E_NOTIMPL;
+    }
+
+
+    IFACEMETHOD(DrawInlineObject)(
+        __maybenull void* clientDrawingContext,
+        FLOAT originX,
+        FLOAT originY,
+        IDWriteInlineObject* inlineObject,
+        BOOL isSideways,
+        BOOL isRightToLeft,
+        __maybenull IUnknown* clientDrawingEffect
+        )
+    {
+        return E_NOTIMPL;
+    }
+
+    // IDWritePixelSnapping methods
+
+    IFACEMETHOD(IsPixelSnappingDisabled)(
+        __maybenull void* clientDrawingContext,
+        __out BOOL* isDisabled
+        )
+    {
+        *isDisabled = FALSE;
+        return S_OK;
+    }
+
+    IFACEMETHOD(GetCurrentTransform)(
+        __maybenull void* clientDrawingContext,
+        __out DWRITE_MATRIX* transform
+        )
+    {
+        const DWRITE_MATRIX ident = {1.0, 0.0, 0.0, 1.0, 0.0, 0.0};
+        *transform = ident;
+        return S_OK;
+    }
+
+    IFACEMETHOD(GetPixelsPerDip)(
+        __maybenull void* clientDrawingContext,
+        __out FLOAT* pixelsPerDip
+        )
+    {
+        *pixelsPerDip = 1.0f;
+        return S_OK;
+    }
+
+    // IUnknown methods
+
+    IFACEMETHOD_(unsigned long, AddRef) ()
+    {
+        return InterlockedIncrement(&mRefCount);
+    }
+
+    IFACEMETHOD_(unsigned long,  Release) ()
+    {
+        unsigned long newCount = InterlockedDecrement(&mRefCount);
+        if (newCount == 0)
+        {
+            delete this;
+            return 0;
+        }
+
+        return newCount;
+    }
+
+    IFACEMETHOD(QueryInterface) (IID const& riid, void** ppvObject)
+    {
+        if (__uuidof(IDWriteTextRenderer) == riid) {
+            *ppvObject = this;
+        } else if (__uuidof(IDWritePixelSnapping) == riid) {
+            *ppvObject = this;
+        } else if (__uuidof(IUnknown) == riid) {
+            *ppvObject = this;
+        } else {
+            *ppvObject = NULL;
+            return E_FAIL;
+        }
+
+        this->AddRef();
+        return S_OK;
+    }
+
+    const nsString& FallbackFamilyName() { return mFamilyName; }
+
+protected:
+    unsigned long mRefCount;
+    nsRefPtr<IDWriteFontCollection> mSystemFonts;
+    nsString mFamilyName;
+};
+
+
 
 class gfxDWriteFontList : public gfxPlatformFontList {
 public:
     gfxDWriteFontList();
 
     static gfxDWriteFontList* PlatformFontList() {
         return static_cast<gfxDWriteFontList*>(sPlatformFontList);
     }
@@ -243,16 +382,24 @@ public:
 
 private:
     friend class gfxDWriteFontFamily;
 
     nsresult GetFontSubstitutes();
 
     void GetDirectWriteSubstitutes();
 
+    // search fonts system-wide for a given character, null otherwise
+    virtual gfxFontEntry* GlobalFontFallback(const PRUint32 aCh,
+                                             PRInt32 aRunScript,
+                                             const gfxFontStyle* aMatchStyle,
+                                             PRUint32& aCmapCount);
+
+    virtual bool UsesSystemFallback() { return true; }
+
     /**
      * Fonts listed in the registry as substitutes but for which no actual
      * font family is found.
      */
     nsTArray<nsString> mNonExistingFonts;
 
     typedef nsDataHashtable<nsStringHashKey, nsRefPtr<gfxFontFamily> > FontTable;
 
@@ -265,12 +412,15 @@ private:
     bool mInitialized;
     virtual nsresult DelayedInitFontList();
 
     gfxFloat mForceGDIClassicMaxFontSize;
 
     // whether to use GDI font table access routines
     bool mGDIFontTableAccess;
     nsRefPtr<IDWriteGdiInterop> mGDIInterop;
+
+    nsRefPtr<FontFallbackRenderer> mFallbackRenderer;
+    nsRefPtr<IDWriteTextFormat>    mFallbackFormat;
 };
 
 
 #endif /* GFX_DWRITEFONTLIST_H */
--- a/gfx/thebes/gfxFT2Fonts.cpp
+++ b/gfx/thebes/gfxFT2Fonts.cpp
@@ -392,21 +392,22 @@ gfxFT2FontGroup::WhichPrefFontSupportsCh
         nsRefPtr<gfxFont> f = static_cast<gfxFont*>(selectedFont.get());
         return f.forget();
     }
 
     return nsnull;
 }
 
 already_AddRefed<gfxFont>
-gfxFT2FontGroup::WhichSystemFontSupportsChar(PRUint32 aCh)
+gfxFT2FontGroup::WhichSystemFontSupportsChar(PRUint32 aCh, PRInt32 aRunScript)
 {
 #if defined(XP_WIN) || defined(ANDROID)
     FontEntry *fe = static_cast<FontEntry*>
-        (gfxPlatformFontList::PlatformFontList()->FindFontForChar(aCh, GetFontAt(0)));
+        (gfxPlatformFontList::PlatformFontList()->
+            SystemFindFontForChar(aCh, aRunScript, &mStyle));
     if (fe) {
         nsRefPtr<gfxFT2Font> f = gfxFT2Font::GetOrMakeFont(fe, &mStyle);
         nsRefPtr<gfxFont> font = f.get();
         return font.forget();
     }
 #else
     nsRefPtr<gfxFont> selectedFont;
     nsRefPtr<gfxFont> refFont = GetFontAt(0);
--- a/gfx/thebes/gfxFT2Fonts.h
+++ b/gfx/thebes/gfxFT2Fonts.h
@@ -136,17 +136,18 @@ protected: // new functions
                       nsTArray<nsRefPtr<gfxFontEntry> >& aFontEntryList);
     void GetCJKPrefFonts(nsTArray<nsRefPtr<gfxFontEntry> >& aFontEntryList);
     void FamilyListToArrayList(const nsString& aFamilies,
                                nsIAtom *aLangGroup,
                                nsTArray<nsRefPtr<gfxFontEntry> > *aFontEntryList);
     already_AddRefed<gfxFT2Font> WhichFontSupportsChar(const nsTArray<nsRefPtr<gfxFontEntry> >& aFontEntryList,
                                                        PRUint32 aCh);
     already_AddRefed<gfxFont> WhichPrefFontSupportsChar(PRUint32 aCh);
-    already_AddRefed<gfxFont> WhichSystemFontSupportsChar(PRUint32 aCh);
+    already_AddRefed<gfxFont>
+        WhichSystemFontSupportsChar(PRUint32 aCh, PRInt32 aRunScript);
 
     nsTArray<gfxTextRange> mRanges;
     nsString mString;
 };
 #endif // !ANDROID
 
 #endif /* GFX_FT2FONTS_H */
 
--- a/gfx/thebes/gfxFont.cpp
+++ b/gfx/thebes/gfxFont.cpp
@@ -685,102 +685,125 @@ gfxFontFamily::FindWeightsForStyle(gfxFo
 
 
 void gfxFontFamily::LocalizedName(nsAString& aLocalizedName)
 {
     // just return the primary name; subclasses should override
     aLocalizedName = mName;
 }
 
+// metric for how close a given font matches a style
+static PRInt32
+CalcStyleMatch(gfxFontEntry *aFontEntry, const gfxFontStyle *aStyle)
+{
+    PRInt32 rank = 0;
+    if (aStyle) {
+         // italics
+         bool wantItalic =
+             ((aStyle->style & (FONT_STYLE_ITALIC | FONT_STYLE_OBLIQUE)) != 0);
+         if (aFontEntry->IsItalic() == wantItalic) {
+             rank += 10;
+         }
+
+        // measure of closeness of weight to the desired value
+        rank += 9 - abs(aFontEntry->Weight() / 100 - aStyle->ComputeWeight());
+    } else {
+        // if no font to match, prefer non-bold, non-italic fonts
+        if (!aFontEntry->IsItalic()) {
+            rank += 3;
+        }
+        if (!aFontEntry->IsBold()) {
+            rank += 2;
+        }
+    }
+
+    return rank;
+}
+
+#define RANK_MATCHED_CMAP   20
 
 void
-gfxFontFamily::FindFontForChar(FontSearch *aMatchData)
+gfxFontFamily::FindFontForChar(GlobalFontMatch *aMatchData)
 {
-    if (!mHasStyles) {
-        FindStyleVariations();
-    }
-
-    if (!TestCharacterMap(aMatchData->mCh)) {
+    if (mCharacterMapInitialized && !TestCharacterMap(aMatchData->mCh)) {
         // none of the faces in the family support the required char,
         // so bail out immediately
         return;
     }
 
-    // iterate over fonts
-    PRUint32 numFonts = mAvailableFonts.Length();
-    for (PRUint32 i = 0; i < numFonts; i++) {
-        gfxFontEntry *fe = mAvailableFonts[i];
-
-        // skip certain fonts during system fallback
-        if (!fe || fe->SkipDuringSystemFallback())
-            continue;
-
+    bool needsBold;
+    gfxFontStyle normal;
+    gfxFontEntry *fe = FindFontForStyle(
+                  (aMatchData->mStyle == nsnull) ? *aMatchData->mStyle : normal,
+                  needsBold);
+
+    if (fe && !fe->SkipDuringSystemFallback()) {
         PRInt32 rank = 0;
 
         if (fe->TestCharacterMap(aMatchData->mCh)) {
-            rank += 20;
+            rank += RANK_MATCHED_CMAP;
             aMatchData->mCount++;
 #ifdef PR_LOGGING
             PRLogModuleInfo *log = gfxPlatform::GetLog(eGfxLog_textrun);
-        
+
             if (NS_UNLIKELY(log)) {
                 PRUint32 charRange = gfxFontUtils::CharRangeBit(aMatchData->mCh);
                 PRUint32 unicodeRange = FindCharUnicodeRange(aMatchData->mCh);
                 PRUint32 script = GetScriptCode(aMatchData->mCh);
                 PR_LOG(log, PR_LOG_DEBUG,\
                        ("(textrun-systemfallback-fonts) char: u+%6.6x "
                         "char-range: %d unicode-range: %d script: %d match: [%s]\n",
                         aMatchData->mCh,
                         charRange, unicodeRange, script,
                         NS_ConvertUTF16toUTF8(fe->Name()).get()));
             }
 #endif
         }
 
-        // if we didn't match any characters don't bother wasting more time with this face.
-        if (rank == 0)
-            continue;
-            
-        // omitting from original windows code -- family name, lang group, pitch
-        // not available in current FontEntry implementation
-
-        if (aMatchData->mFontToMatch) { 
-            const gfxFontStyle *style = aMatchData->mFontToMatch->GetStyle();
-
-            // matching italics takes precedence over weight
-            bool wantItalic =
-                ((style->style & (FONT_STYLE_ITALIC | FONT_STYLE_OBLIQUE)) != 0);
-            if (fe->IsItalic() == wantItalic) {
-                rank += 10;
-            }
-
-            // measure of closeness of weight to the desired value
-            rank += 9 - abs(fe->Weight() / 100 - style->ComputeWeight());
-        } else {
-            // if no font to match, prefer non-bold, non-italic fonts
-            if (!fe->IsItalic()) {
-                rank += 3;
-            }
-            if (!fe->IsBold()) {
-                rank += 2;
-            }
+        aMatchData->mCmapsTested++;
+        if (rank == 0) {
+            return;
         }
-        
+
+         // omitting from original windows code -- family name, lang group, pitch
+         // not available in current FontEntry implementation
+        rank += CalcStyleMatch(fe, aMatchData->mStyle);
+
         // xxx - add whether AAT font with morphing info for specific lang groups
-        
+
         if (rank > aMatchData->mMatchRank
             || (rank == aMatchData->mMatchRank &&
-                Compare(fe->Name(), aMatchData->mBestMatch->Name()) > 0)) 
+                Compare(fe->Name(), aMatchData->mBestMatch->Name()) > 0))
         {
             aMatchData->mBestMatch = fe;
             aMatchData->mMatchRank = rank;
         }
     }
 }
 
+void
+gfxFontFamily::SearchAllFontsForChar(GlobalFontMatch *aMatchData)
+{
+    PRUint32 i, numFonts = mAvailableFonts.Length();
+    for (i = 0; i < numFonts; i++) {
+        gfxFontEntry *fe = mAvailableFonts[i];
+        if (fe && fe->TestCharacterMap(aMatchData->mCh)) {
+            PRInt32 rank = RANK_MATCHED_CMAP;
+            rank += CalcStyleMatch(fe, aMatchData->mStyle);
+            if (rank > aMatchData->mMatchRank
+                || (rank == aMatchData->mMatchRank &&
+                    Compare(fe->Name(), aMatchData->mBestMatch->Name()) > 0))
+            {
+                aMatchData->mBestMatch = fe;
+                aMatchData->mMatchRank = rank;
+            }
+        }
+    }
+}
+
 // returns true if other names were found, false otherwise
 bool
 gfxFontFamily::ReadOtherFamilyNamesForFace(gfxPlatformFontList *aPlatformFontList,
                                            FallibleTArray<PRUint8>& aNameTable,
                                            bool useFullName)
 {
     const PRUint8 *nameData = aNameTable.Elements();
     PRUint32 dataLength = aNameTable.Length();
@@ -3148,17 +3171,17 @@ gfxFontGroup::InitTextRun(gfxContext *aC
         PRInt32 runScript = MOZ_SCRIPT_LATIN;
         while (scriptRuns.Next(runStart, runLimit, runScript)) {
 
 #ifdef PR_LOGGING
             if (NS_UNLIKELY(log)) {
                 nsCAutoString lang;
                 mStyle.language->ToUTF8String(lang);
                 PRUint32 runLen = runLimit - runStart;
-                PR_LOG(log, PR_LOG_DEBUG,\
+                PR_LOG(log, PR_LOG_WARNING,\
                        ("(%s) fontgroup: [%s] lang: %s script: %d len %d "
                         "weight: %d width: %d style: %s "
                         "TEXTRUN [%s] ENDTEXTRUN\n",
                         (mStyle.systemFont ? "textrunui" : "textrun"),
                         NS_ConvertUTF16toUTF8(mFamilies).get(),
                         lang.get(), runScript, runLen,
                         PRUint32(mStyle.weight), PRUint32(mStyle.stretch),
                         (mStyle.style & FONT_STYLE_ITALIC ? "italic" :
@@ -3354,21 +3377,22 @@ gfxFontGroup::FindFontForChar(PRUint32 a
 
     // 1. check fonts in the font group
     for (PRUint32 i = 0; i < FontListLength(); i++) {
         nsRefPtr<gfxFont> font = GetFontAt(i);
         if (font->HasCharacter(aCh)) {
             *aMatchType = gfxTextRange::kFontGroup;
             return font.forget();
         }
+
         // check other faces of the family
         gfxFontFamily *family = font->GetFontEntry()->Family();
         if (family && family->TestCharacterMap(aCh)) {
-            FontSearch matchData(aCh, font);
-            family->FindFontForChar(&matchData);
+            GlobalFontMatch matchData(aCh, aRunScript, &mStyle);
+            family->SearchAllFontsForChar(&matchData);
             gfxFontEntry *fe = matchData.mBestMatch;
             if (fe) {
                 bool needsBold =
                     font->GetStyle()->weight >= 600 && !fe->IsBold();
                 selectedFont =
                     fe->FindOrMakeFont(font->GetStyle(), needsBold);
                 if (selectedFont) {
                     return selectedFont.forget();
@@ -3390,29 +3414,34 @@ gfxFontGroup::FindFontForChar(PRUint32 a
     // 3. use fallback fonts
     // -- before searching for something else check the font used for the previous character
     if (!selectedFont && aPrevMatchedFont && aPrevMatchedFont->HasCharacter(aCh)) {
         *aMatchType = gfxTextRange::kSystemFallback;
         selectedFont = aPrevMatchedFont;
         return selectedFont.forget();
     }
 
+    // never fall back for characters from unknown scripts
+    if (aRunScript == HB_SCRIPT_UNKNOWN) {
+        return nsnull;
+    }
+
     // for known "space" characters, don't do a full system-fallback search;
     // we'll synthesize appropriate-width spaces instead of missing-glyph boxes
     if (GetGeneralCategory(aCh) ==
             HB_UNICODE_GENERAL_CATEGORY_SPACE_SEPARATOR &&
         GetFontAt(0)->SynthesizeSpaceWidth(aCh) >= 0.0)
     {
         return nsnull;
     }
 
     // -- otherwise look for other stuff
     if (!selectedFont) {
         *aMatchType = gfxTextRange::kSystemFallback;
-        selectedFont = WhichSystemFontSupportsChar(aCh);
+        selectedFont = WhichSystemFontSupportsChar(aCh, aRunScript);
         return selectedFont.forget();
     }
 
     return nsnull;
 }
 
 template<typename T>
 void gfxFontGroup::ComputeRanges(nsTArray<gfxTextRange>& aRanges,
@@ -3547,20 +3576,16 @@ struct PrefFontCallbackData {
     }
 };
 
 already_AddRefed<gfxFont>
 gfxFontGroup::WhichPrefFontSupportsChar(PRUint32 aCh)
 {
     gfxFont *font;
 
-    // FindCharUnicodeRange only supports BMP character points and there are no non-BMP fonts in prefs
-    if (aCh > 0xFFFF)
-        return nsnull;
-
     // get the pref font list if it hasn't been set up already
     PRUint32 unicodeRange = FindCharUnicodeRange(aCh);
     eFontPrefLang charLang = gfxPlatform::GetPlatform()->GetFontPrefLangFor(unicodeRange);
 
     // if the last pref font was the first family in the pref list, no need to recheck through a list of families
     if (mLastPrefFont && charLang == mLastPrefLang &&
         mLastPrefFirstFont && mLastPrefFont->HasCharacter(aCh)) {
         font = mLastPrefFont;
@@ -3622,22 +3647,24 @@ gfxFontGroup::WhichPrefFontSupportsChar(
 
         }
     }
 
     return nsnull;
 }
 
 already_AddRefed<gfxFont>
-gfxFontGroup::WhichSystemFontSupportsChar(PRUint32 aCh)
+gfxFontGroup::WhichSystemFontSupportsChar(PRUint32 aCh, PRInt32 aRunScript)
 {
     gfxFontEntry *fe = 
-        gfxPlatformFontList::PlatformFontList()->FindFontForChar(aCh, GetFontAt(0));
+        gfxPlatformFontList::PlatformFontList()->
+            SystemFindFontForChar(aCh, aRunScript, &mStyle);
     if (fe) {
-        nsRefPtr<gfxFont> font = fe->FindOrMakeFont(&mStyle, false); // ignore bolder considerations in system fallback case...
+        // ignore bolder considerations in system fallback case...
+        nsRefPtr<gfxFont> font = fe->FindOrMakeFont(&mStyle, false);
         return font.forget();
     }
 
     return nsnull;
 }
 
 /*static*/ void
 gfxFontGroup::Shutdown()
--- a/gfx/thebes/gfxFont.h
+++ b/gfx/thebes/gfxFont.h
@@ -482,26 +482,34 @@ private:
 
     nsTHashtable<FontTableHashEntry> mFontTableCache;
 
     gfxFontEntry(const gfxFontEntry&);
     gfxFontEntry& operator=(const gfxFontEntry&);
 };
 
 
-// used when picking fallback font
-struct FontSearch {
-    FontSearch(const PRUint32 aCharacter, gfxFont *aFont) :
-        mCh(aCharacter), mFontToMatch(aFont), mMatchRank(0), mCount(0) {
-    }
-    const PRUint32         mCh;
-    gfxFont*               mFontToMatch;
-    PRInt32                mMatchRank;
-    nsRefPtr<gfxFontEntry> mBestMatch;
-    PRUint32               mCount;
+// used when iterating over all fonts looking for a match for a given character
+struct GlobalFontMatch {
+    GlobalFontMatch(const PRUint32 aCharacter,
+                    PRInt32 aRunScript,
+                    const gfxFontStyle *aStyle) :
+        mCh(aCharacter), mRunScript(aRunScript), mStyle(aStyle),
+        mMatchRank(0), mCount(0), mCmapsTested(0)
+        {
+
+        }
+
+    const PRUint32         mCh;          // codepoint to be matched
+    PRInt32                mRunScript;   // Unicode script for the codepoint
+    const gfxFontStyle*    mStyle;       // style to match
+    PRInt32                mMatchRank;   // metric indicating closest match
+    nsRefPtr<gfxFontEntry> mBestMatch;   // current best match
+    PRUint32               mCount;       // number of fonts matched
+    PRUint32               mCmapsTested; // number of cmaps tested
 };
 
 class gfxFontFamily {
 public:
     NS_INLINE_DECL_REFCOUNTING(gfxFontFamily)
 
     gfxFontFamily(const nsAString& aName) :
         mName(aName),
@@ -552,19 +560,22 @@ public:
     // choose a specific face to match a style using CSS font matching
     // rules (weight matching occurs here).  may return a face that doesn't
     // precisely match (e.g. normal face when no italic face exists).
     // aNeedsSyntheticBold is set to true when synthetic bolding is
     // needed, false otherwise
     gfxFontEntry *FindFontForStyle(const gfxFontStyle& aFontStyle, 
                                    bool& aNeedsSyntheticBold);
 
-    // iterates over faces looking for a match with a given characters
+    // checks for a matching font within the family
     // used as part of the font fallback process
-    void FindFontForChar(FontSearch *aMatchData);
+    void FindFontForChar(GlobalFontMatch *aMatchData);
+
+    // checks all fonts for a matching font within the family
+    void SearchAllFontsForChar(GlobalFontMatch *aMatchData);
 
     // read in other family names, if any, and use functor to add each into cache
     virtual void ReadOtherFamilyNames(gfxPlatformFontList *aPlatformFontList);
 
     // set when other family names have been read in
     void SetOtherFamilyNamesInitialized() {
         mOtherFamilyNamesInitialized = true;
     }
@@ -577,33 +588,33 @@ public:
     // find faces belonging to this family (platform implementations override this;
     // should be made pure virtual once all subclasses have been updated)
     virtual void FindStyleVariations() { }
 
     // search for a specific face using the Postscript name
     gfxFontEntry* FindFont(const nsAString& aPostscriptName);
 
     // read in cmaps for all the faces
-    void ReadCMAP() {
+    void ReadAllCMAPs() {
         PRUint32 i, numFonts = mAvailableFonts.Length();
         for (i = 0; i < numFonts; i++) {
             gfxFontEntry *fe = mAvailableFonts[i];
             if (!fe) {
                 continue;
             }
             fe->ReadCMAP();
             mCharacterMap.Union(fe->mCharacterMap);
         }
         mCharacterMap.Compact();
         mCharacterMapInitialized = true;
     }
 
     bool TestCharacterMap(PRUint32 aCh) {
         if (!mCharacterMapInitialized) {
-            ReadCMAP();
+            ReadAllCMAPs();
         }
         return mCharacterMap.test(aCh);
     }
 
     void ResetCharacterMap() {
         mCharacterMap.reset();
         mCharacterMapInitialized = false;
     }
@@ -2970,17 +2981,18 @@ public:
     virtual already_AddRefed<gfxFont>
         FindFontForChar(PRUint32 ch, PRUint32 prevCh, PRInt32 aRunScript,
                         gfxFont *aPrevMatchedFont,
                         PRUint8 *aMatchType);
 
     // search through pref fonts for a character, return nsnull if no matching pref font
     virtual already_AddRefed<gfxFont> WhichPrefFontSupportsChar(PRUint32 aCh);
 
-    virtual already_AddRefed<gfxFont> WhichSystemFontSupportsChar(PRUint32 aCh);
+    virtual already_AddRefed<gfxFont>
+        WhichSystemFontSupportsChar(PRUint32 aCh, PRInt32 aRunScript);
 
     template<typename T>
     void ComputeRanges(nsTArray<gfxTextRange>& mRanges,
                        const T *aString, PRUint32 aLength,
                        PRInt32 aRunScript);
 
     gfxUserFontSet* GetUserFontSet();
 
--- a/gfx/thebes/gfxFontUtils.cpp
+++ b/gfx/thebes/gfxFontUtils.cpp
@@ -32,37 +32,50 @@
  * 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 ***** */
 
+#ifdef MOZ_LOGGING
+#define FORCE_PR_LOG /* Allow logging in the release build */
+#endif
+#include "prlog.h"
+
 #include "mozilla/Util.h"
 
 #include "gfxFontUtils.h"
 
 #include "nsServiceManagerUtils.h"
 
 #include "mozilla/Preferences.h"
 
 #include "nsIStreamBufferAccess.h"
 #include "nsIUUIDGenerator.h"
 #include "nsMemory.h"
 #include "nsICharsetConverterManager.h"
 
 #include "plbase64.h"
+#include "prlog.h"
 
 #include "woff.h"
 
 #ifdef XP_MACOSX
 #include <CoreFoundation/CoreFoundation.h>
 #endif
 
+#ifdef PR_LOGGING
+
+#define LOG(log, args) PR_LOG(gfxPlatform::GetLog(log), \
+                               PR_LOG_DEBUG, args)
+
+#endif // PR_LOGGING
+
 #define NO_RANGE_FOUND 126 // bit 126 in the font unicode ranges is required to be 0
 
 #define UNICODE_BMP_LIMIT 0x10000
 
 using namespace mozilla;
 
 /* Unicode subrange table
  *   from: http://msdn.microsoft.com/en-us/library/dd374090
@@ -264,16 +277,47 @@ typedef struct {
 typedef struct {
     AutoSwap_PRUint32 startCharCode;
     AutoSwap_PRUint32 endCharCode;
     AutoSwap_PRUint32 startGlyphId;
 } Format12Group;
 
 #pragma pack()
 
+#if PR_LOGGING
+void
+gfxSparseBitSet::Dump(const char* aPrefix, eGfxLog aWhichLog) const
+{
+    NS_ASSERTION(mBlocks.DebugGetHeader(), "mHdr is null, this is bad");
+    PRUint32 b, numBlocks = mBlocks.Length();
+
+    for (b = 0; b < numBlocks; b++) {
+        Block *block = mBlocks[b];
+        if (!block) continue;
+        char outStr[256];
+        int index = 0;
+        index += sprintf(&outStr[index], "%s u+%6.6x [", aPrefix, (b << BLOCK_INDEX_SHIFT));
+        for (int i = 0; i < 32; i += 4) {
+            for (int j = i; j < i + 4; j++) {
+                PRUint8 bits = block->mBits[j];
+                PRUint8 flip1 = ((bits & 0xaa) >> 1) | ((bits & 0x55) << 1);
+                PRUint8 flip2 = ((flip1 & 0xcc) >> 2) | ((flip1 & 0x33) << 2);
+                PRUint8 flipped = ((flip2 & 0xf0) >> 4) | ((flip2 & 0x0f) << 4);
+
+                index += sprintf(&outStr[index], "%2.2x", flipped);
+            }
+            if (i + 4 != 32) index += sprintf(&outStr[index], " ");
+        }
+        index += sprintf(&outStr[index], "]");
+        LOG(aWhichLog, ("%s", outStr));
+    }
+}
+#endif
+
+
 nsresult
 gfxFontUtils::ReadCMAPTableFormat12(const PRUint8 *aBuf, PRUint32 aLength,
                                     gfxSparseBitSet& aCharacterMap) 
 {
     // Ensure table is large enough that we can safely read the header
     NS_ENSURE_TRUE(aLength >= sizeof(Format12CmapHeader),
                     NS_ERROR_GFX_CMAP_MALFORMED);
 
--- a/gfx/thebes/gfxFontUtils.h
+++ b/gfx/thebes/gfxFontUtils.h
@@ -36,16 +36,17 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef GFX_FONT_UTILS_H
 #define GFX_FONT_UTILS_H
 
 #include "gfxTypes.h"
+#include "gfxPlatform.h"
 
 #include "prtypes.h"
 #include "nsAlgorithm.h"
 #include "prcpucfg.h"
 
 #include "nsDataHashtable.h"
 
 #include "nsITimer.h"
@@ -92,16 +93,21 @@ public:
         if (blockIndex >= mBlocks.Length())
             return false;
         Block *block = mBlocks[blockIndex];
         if (!block)
             return false;
         return ((block->mBits[(aIndex>>3) & (BLOCK_SIZE - 1)]) & (1 << (aIndex & 0x7))) != 0;
     }
 
+#if PR_LOGGING
+    // dump out contents of bitmap
+    void Dump(const char* aPrefix, eGfxLog aWhichLog) const;
+#endif
+
     bool TestRange(PRUint32 aStart, PRUint32 aEnd) {
         PRUint32 startBlock, endBlock, blockLen;
         
         // start point is beyond the end of the block array? return false immediately
         startBlock = aStart >> BLOCK_INDEX_SHIFT;
         blockLen = mBlocks.Length();
         if (startBlock >= blockLen) return false;
         
--- a/gfx/thebes/gfxGDIFontList.cpp
+++ b/gfx/thebes/gfxGDIFontList.cpp
@@ -78,16 +78,20 @@ using namespace mozilla;
 
 #ifdef PR_LOGGING
 #define LOG_FONTLIST(args) PR_LOG(gfxPlatform::GetLog(eGfxLog_fontlist), \
                                PR_LOG_DEBUG, args)
 #define LOG_FONTLIST_ENABLED() PR_LOG_TEST( \
                                    gfxPlatform::GetLog(eGfxLog_fontlist), \
                                    PR_LOG_DEBUG)
 
+#define LOG_CMAPDATA_ENABLED() PR_LOG_TEST( \
+                                   gfxPlatform::GetLog(eGfxLog_cmapdata), \
+                                   PR_LOG_DEBUG)
+
 #endif // PR_LOGGING
 
 // font info loader constants
 
 // avoid doing this during startup even on slow machines but try to start
 // it soon enough so that system fallback doesn't happen first
 static const PRUint32 kDelayBeforeLoadingFonts = 120 * 1000; // 2 minutes after init
 static const PRUint32 kIntervalBetweenLoadingFonts = 2000;   // every 2 seconds until complete
@@ -222,16 +226,22 @@ GDIFontEntry::ReadCMAP()
                                          mCharacterMap, mUVSOffset,
                                          unicodeFont, symbolFont);
     mSymbolFont = symbolFont;
     mHasCmapTable = NS_SUCCEEDED(rv);
 
 #ifdef PR_LOGGING
     LOG_FONTLIST(("(fontlist-cmap) name: %s, size: %d\n",
                   NS_ConvertUTF16toUTF8(mName).get(), mCharacterMap.GetSize()));
+    if (LOG_CMAPDATA_ENABLED()) {
+        char prefix[256];
+        sprintf(prefix, "(cmapdata) name: %.220s",
+                NS_ConvertUTF16toUTF8(mName).get());
+        mCharacterMap.Dump(prefix, eGfxLog_cmapdata);
+    }
 #endif
     return rv;
 }
 
 bool
 GDIFontEntry::IsSymbolFont()
 {
     // initialize cmap first
--- a/gfx/thebes/gfxMacPlatformFontList.h
+++ b/gfx/thebes/gfxMacPlatformFontList.h
@@ -161,32 +161,44 @@ public:
     static bool UseATSFontEntry() {
         return gfxPlatformMac::GetPlatform()->OSXVersion() < MAC_OS_X_VERSION_10_6_HEX;
     }
 
 private:
     friend class gfxPlatformMac;
 
     gfxMacPlatformFontList();
+    virtual ~gfxMacPlatformFontList();
 
     // initialize font lists
     virtual nsresult InitFontList();
 
     // special case font faces treated as font families (set via prefs)
     void InitSingleFaceList();
 
     gfxFontEntry* MakePlatformFontCG(const gfxProxyFontEntry *aProxyEntry,
                                      const PRUint8 *aFontData, PRUint32 aLength);
 
     gfxFontEntry* MakePlatformFontATS(const gfxProxyFontEntry *aProxyEntry,
                                       const PRUint8 *aFontData, PRUint32 aLength);
 
     static void ATSNotification(ATSFontNotificationInfoRef aInfo, void* aUserArg);
 
+    // search fonts system-wide for a given character, null otherwise
+    virtual gfxFontEntry* GlobalFontFallback(const PRUint32 aCh,
+                                             PRInt32 aRunScript,
+                                             const gfxFontStyle* aMatchStyle,
+                                             PRUint32& aCmapCount);
+
+    virtual bool UsesSystemFallback() { return true; }
+
     // keep track of ATS generation to prevent unneeded updates when loading downloaded fonts
     PRUint32 mATSGeneration;
 
     enum {
         kATSGenerationInitial = -1
     };
+
+    // default font for use with system-wide font fallback
+    CTFontRef mDefaultFont;
 };
 
 #endif /* gfxMacPlatformFontList_H_ */
--- a/gfx/thebes/gfxMacPlatformFontList.mm
+++ b/gfx/thebes/gfxMacPlatformFontList.mm
@@ -53,16 +53,17 @@
 #include "gfxUserFontSet.h"
 
 #include "nsServiceManagerUtils.h"
 #include "nsTArray.h"
 
 #include "nsDirectoryServiceUtils.h"
 #include "nsDirectoryServiceDefs.h"
 #include "nsISimpleEnumerator.h"
+#include "nsCharTraits.h"
 
 #include "mozilla/Telemetry.h"
 
 #include <unistd.h>
 #include <time.h>
 
 using namespace mozilla;
 
@@ -131,16 +132,19 @@ static NSString* GetNSStringForString(co
 
 #ifdef PR_LOGGING
 
 #define LOG_FONTLIST(args) PR_LOG(gfxPlatform::GetLog(eGfxLog_fontlist), \
                                PR_LOG_DEBUG, args)
 #define LOG_FONTLIST_ENABLED() PR_LOG_TEST( \
                                    gfxPlatform::GetLog(eGfxLog_fontlist), \
                                    PR_LOG_DEBUG)
+#define LOG_CMAPDATA_ENABLED() PR_LOG_TEST( \
+                                   gfxPlatform::GetLog(eGfxLog_cmapdata), \
+                                   PR_LOG_DEBUG)
 
 #endif // PR_LOGGING
 
 /* MacOSFontEntry - abstract superclass for ATS and CG font entries */
 #pragma mark-
 
 MacOSFontEntry::MacOSFontEntry(const nsAString& aPostscriptName,
                                PRInt32 aWeight,
@@ -262,16 +266,22 @@ MacOSFontEntry::ReadCMAP()
             }
         }
     }
 
 #ifdef PR_LOGGING
     LOG_FONTLIST(("(fontlist-cmap) name: %s, size: %d\n",
                   NS_ConvertUTF16toUTF8(mName).get(),
                   mCharacterMap.GetSize()));
+    if (LOG_CMAPDATA_ENABLED()) {
+        char prefix[256];
+        sprintf(prefix, "(cmapdata) name: %.220s",
+                NS_ConvertUTF16toUTF8(mName).get());
+        mCharacterMap.Dump(prefix, eGfxLog_cmapdata);
+    }
 #endif
 
     return rv;
 }
 
 gfxFont*
 MacOSFontEntry::CreateFontInstance(const gfxFontStyle *aFontStyle, bool aNeedsBold)
 {
@@ -690,31 +700,39 @@ gfxSingleFaceMacFontFamily::ReadOtherFam
     mOtherFamilyNamesInitialized = true;
 }
 
 
 /* gfxMacPlatformFontList */
 #pragma mark-
 
 gfxMacPlatformFontList::gfxMacPlatformFontList() :
-    gfxPlatformFontList(false), mATSGeneration(PRUint32(kATSGenerationInitial))
+    gfxPlatformFontList(false), mATSGeneration(PRUint32(kATSGenerationInitial)),
+    mDefaultFont(nsnull)
 {
     ::ATSFontNotificationSubscribe(ATSNotification,
                                    kATSFontNotifyOptionDefault,
                                    (void*)this, nsnull);
 
     // this should always be available (though we won't actually fail if it's missing,
     // we'll just end up doing a search and then caching the new result instead)
     mReplacementCharFallbackFamily = NS_LITERAL_STRING("Lucida Grande");
 
     // cache this in a static variable so that MacOSFontFamily objects
     // don't have to repeatedly look it up
     sFontManager = [NSFontManager sharedFontManager];
 }
 
+gfxMacPlatformFontList::~gfxMacPlatformFontList()
+{
+    if (mDefaultFont) {
+        ::CFRelease(mDefaultFont);
+    }
+}
+
 nsresult
 gfxMacPlatformFontList::InitFontList()
 {
     nsAutoreleasePool localPool;
 
     ATSGeneration currentGeneration = ::ATSGetGeneration();
 
     // need to ignore notifications after adding each font
@@ -862,16 +880,103 @@ gfxMacPlatformFontList::ATSNotification(
                                     void* aUserArg)
 {
     // xxx - should be carefully pruning the list of fonts, not rebuilding it from scratch
     gfxMacPlatformFontList *qfc = (gfxMacPlatformFontList*)aUserArg;
     qfc->UpdateFontList();
 }
 
 gfxFontEntry*
+gfxMacPlatformFontList::GlobalFontFallback(const PRUint32 aCh,
+                                           PRInt32 aRunScript,
+                                           const gfxFontStyle* aMatchStyle,
+                                           PRUint32& aCmapCount)
+{
+    bool useCmaps = gfxPlatform::GetPlatform()->UseCmapsDuringSystemFallback();
+
+    if (useCmaps) {
+        return gfxPlatformFontList::GlobalFontFallback(aCh,
+                                                       aRunScript,
+                                                       aMatchStyle,
+                                                       aCmapCount);
+    }
+
+    CFStringRef str;
+    UniChar ch[2];
+    CFIndex len = 1;
+
+    if (IS_IN_BMP(aCh)) {
+        ch[0] = aCh;
+        str = ::CFStringCreateWithCharactersNoCopy(kCFAllocatorDefault, ch, 1,
+                                                   kCFAllocatorNull);
+    } else {
+        ch[0] = H_SURROGATE(aCh);
+        ch[1] = L_SURROGATE(aCh);
+        str = ::CFStringCreateWithCharactersNoCopy(kCFAllocatorDefault, ch, 2,
+                                                   kCFAllocatorNull);
+        if (!str) {
+            return nsnull;
+        }
+        len = 2;
+    }
+
+    // use CoreText to find the fallback family
+
+    gfxFontEntry *fontEntry = nsnull;
+    CTFontRef fallback;
+    bool cantUseFallbackFont = false;
+
+    if (!mDefaultFont) {
+        mDefaultFont = ::CTFontCreateWithName(CFSTR("Lucida Grande"), 12.f,
+                                              NULL);
+    }
+
+    fallback = ::CTFontCreateForString(mDefaultFont, str,
+                                       ::CFRangeMake(0, len));
+
+    if (fallback) {
+        CFStringRef familyName = ::CTFontCopyFamilyName(fallback);
+        ::CFRelease(fallback);
+
+        if (familyName &&
+            ::CFStringCompare(familyName, CFSTR("LastResort"),
+                              kCFCompareCaseInsensitive) != kCFCompareEqualTo)
+        {
+            nsAutoTArray<UniChar, 1024> buffer;
+            CFIndex len = ::CFStringGetLength(familyName);
+            buffer.SetLength(len+1);
+            ::CFStringGetCharacters(familyName, ::CFRangeMake(0, len),
+                                    buffer.Elements());
+            buffer[len] = 0;
+            nsDependentString family(buffer.Elements(), len);
+
+            bool needsBold;  // ignored in the system fallback case
+
+            fontEntry = FindFontForFamily(family, aMatchStyle, needsBold);
+            if (fontEntry && !fontEntry->TestCharacterMap(aCh)) {
+                fontEntry = nsnull;
+                cantUseFallbackFont = true;
+            }
+        }
+
+        if (familyName) {
+            ::CFRelease(familyName);
+        }
+    }
+
+    if (cantUseFallbackFont) {
+        Telemetry::Accumulate(Telemetry::BAD_FALLBACK_FONT, cantUseFallbackFont);
+    }
+
+    ::CFRelease(str);
+
+    return fontEntry;
+}
+
+gfxFontEntry*
 gfxMacPlatformFontList::GetDefaultFont(const gfxFontStyle* aStyle, bool& aNeedsBold)
 {
     nsAutoreleasePool localPool;
 
     NSString *defaultFamily = [[NSFont userFontOfSize:aStyle->size] familyName];
     nsAutoString familyName;
 
     GetStringForNSString(defaultFamily, familyName);
--- a/gfx/thebes/gfxPlatform.cpp
+++ b/gfx/thebes/gfxPlatform.cpp
@@ -113,16 +113,17 @@ static void MigratePrefs();
 using namespace mozilla::gfx;
 
 // logs shared across gfx
 #ifdef PR_LOGGING
 static PRLogModuleInfo *sFontlistLog = nsnull;
 static PRLogModuleInfo *sFontInitLog = nsnull;
 static PRLogModuleInfo *sTextrunLog = nsnull;
 static PRLogModuleInfo *sTextrunuiLog = nsnull;
+static PRLogModuleInfo *sCmapDataLog = nsnull;
 #endif
 
 /* Class to listen for pref changes so that chrome code can dynamically
    force sRGB as an output profile. See Bug #452125. */
 class SRGBOverrideObserver : public nsIObserver,
                              public nsSupportsWeakReference
 {
 public:
@@ -144,16 +145,17 @@ SRGBOverrideObserver::Observe(nsISupport
     return NS_OK;
 }
 
 #define GFX_DOWNLOADABLE_FONTS_ENABLED "gfx.downloadable_fonts.enabled"
 #define GFX_DOWNLOADABLE_FONTS_SANITIZE "gfx.downloadable_fonts.sanitize"
 
 #define GFX_PREF_HARFBUZZ_SCRIPTS "gfx.font_rendering.harfbuzz.scripts"
 #define HARFBUZZ_SCRIPTS_DEFAULT  mozilla::unicode::SHAPING_DEFAULT
+#define GFX_PREF_FALLBACK_USE_CMAPS  "gfx.font_rendering.fallback.always_use_cmaps"
 
 #ifdef MOZ_GRAPHITE
 #define GFX_PREF_GRAPHITE_SHAPING "gfx.font_rendering.graphite.enabled"
 #endif
 
 #define BIDI_NUMERAL_PREF "bidi.numeral"
 
 static const char* kObservedPrefs[] = {
@@ -227,16 +229,18 @@ static const char *gPrefLangNames[] = {
 };
 
 gfxPlatform::gfxPlatform()
   : mAzureBackendCollector(this, &gfxPlatform::GetAzureBackendInfo)
 {
     mUseHarfBuzzScripts = UNINITIALIZED_VALUE;
     mAllowDownloadableFonts = UNINITIALIZED_VALUE;
     mDownloadableFontsSanitize = UNINITIALIZED_VALUE;
+    mFallbackUsesCmaps = UNINITIALIZED_VALUE;
+
 #ifdef MOZ_GRAPHITE
     mGraphiteShapingEnabled = UNINITIALIZED_VALUE;
 #endif
     mBidiNumeralOption = UNINITIALIZED_VALUE;
 
     if (Preferences::GetBool("gfx.canvas.azure.prefer-skia", false)) {
         mPreferredDrawTargetBackend = BACKEND_SKIA;
     } else {
@@ -263,16 +267,17 @@ gfxPlatform::Init()
 
     gfxAtoms::RegisterAtoms();
 
 #ifdef PR_LOGGING
     sFontlistLog = PR_NewLogModule("fontlist");;
     sFontInitLog = PR_NewLogModule("fontinit");;
     sTextrunLog = PR_NewLogModule("textrun");;
     sTextrunuiLog = PR_NewLogModule("textrunui");;
+    sCmapDataLog = PR_NewLogModule("cmapdata");;
 #endif
 
 
     /* Initialize the GfxInfo service.
      * Note: we can't call functions on GfxInfo that depend
      * on gPlatform until after it has been initialized
      * below. GfxInfo initialization annotates our
      * crash reports so we want to do it before
@@ -675,16 +680,27 @@ gfxPlatform::SanitizeDownloadedFonts()
     if (mDownloadableFontsSanitize == UNINITIALIZED_VALUE) {
         mDownloadableFontsSanitize =
             Preferences::GetBool(GFX_DOWNLOADABLE_FONTS_SANITIZE, true);
     }
 
     return mDownloadableFontsSanitize;
 }
 
+bool
+gfxPlatform::UseCmapsDuringSystemFallback()
+{
+    if (mFallbackUsesCmaps == UNINITIALIZED_VALUE) {
+        mFallbackUsesCmaps =
+            Preferences::GetBool(GFX_PREF_FALLBACK_USE_CMAPS, false);
+    }
+
+    return mFallbackUsesCmaps;
+}
+
 #ifdef MOZ_GRAPHITE
 bool
 gfxPlatform::UseGraphiteShaping()
 {
     if (mGraphiteShapingEnabled == UNINITIALIZED_VALUE) {
         mGraphiteShapingEnabled =
             Preferences::GetBool(GFX_PREF_GRAPHITE_SHAPING, false);
     }
@@ -1353,16 +1369,18 @@ gfxPlatform::GetBidiNumeralOption()
 void
 gfxPlatform::FontsPrefsChanged(const char *aPref)
 {
     NS_ASSERTION(aPref != nsnull, "null preference");
     if (!strcmp(GFX_DOWNLOADABLE_FONTS_ENABLED, aPref)) {
         mAllowDownloadableFonts = UNINITIALIZED_VALUE;
     } else if (!strcmp(GFX_DOWNLOADABLE_FONTS_SANITIZE, aPref)) {
         mDownloadableFontsSanitize = UNINITIALIZED_VALUE;
+    } else if (!strcmp(GFX_PREF_FALLBACK_USE_CMAPS, aPref)) {
+        mFallbackUsesCmaps = UNINITIALIZED_VALUE;
 #ifdef MOZ_GRAPHITE
     } else if (!strcmp(GFX_PREF_GRAPHITE_SHAPING, aPref)) {
         mGraphiteShapingEnabled = UNINITIALIZED_VALUE;
         gfxFontCache *fontCache = gfxFontCache::GetCache();
         if (fontCache) {
             fontCache->AgeAllGenerations();
             fontCache->FlushShapedWordCaches();
         }
@@ -1392,16 +1410,19 @@ gfxPlatform::GetLog(eGfxLog aWhichLog)
         return sFontInitLog;
         break;
     case eGfxLog_textrun:
         return sTextrunLog;
         break;
     case eGfxLog_textrunui:
         return sTextrunuiLog;
         break;
+    case eGfxLog_cmapdata:
+        return sCmapDataLog;
+        break;
     default:
         break;
     }
 
     return nsnull;
 #else
     return nsnull;
 #endif
--- a/gfx/thebes/gfxPlatform.h
+++ b/gfx/thebes/gfxPlatform.h
@@ -131,17 +131,19 @@ enum eCMSMode {
 enum eGfxLog {
     // all font enumerations, localized names, fullname/psnames, cmap loads
     eGfxLog_fontlist         = 0,
     // timing info on font initialization
     eGfxLog_fontinit         = 1,
     // dump text runs, font matching, system fallback for content
     eGfxLog_textrun          = 2,
     // dump text runs, font matching, system fallback for chrome
-    eGfxLog_textrunui        = 3
+    eGfxLog_textrunui        = 3,
+    // dump cmap coverage data as they are loaded
+    eGfxLog_cmapdata         = 4
 };
 
 // when searching through pref langs, max number of pref langs
 const PRUint32 kMaxLenPrefLangList = 32;
 
 #define UNINITIALIZED_VALUE  (-1)
 
 typedef gfxASurface::gfxImageFormat gfxImageFormat;
@@ -312,16 +314,21 @@ public:
      * True when hinting should be enabled.  This setting shouldn't
      * change per gecko process, while the process is live.  If so the
      * results are not defined.
      *
      * NB: this bit is only honored by the FT2 backend, currently.
      */
     virtual bool FontHintingEnabled() { return true; }
 
+    /**
+     * Whether to check all font cmaps during system font fallback
+     */
+    bool UseCmapsDuringSystemFallback();
+
 #ifdef MOZ_GRAPHITE
     /**
      * Whether to use the SIL Graphite rendering engine
      * (for fonts that include Graphite tables)
      */
     bool UseGraphiteShaping();
 #endif
 
@@ -364,16 +371,25 @@ public:
     static eFontPrefLang GetFontPrefLangFor(PRUint8 aUnicodeRange);
 
     // returns true if a pref lang is CJK
     static bool IsLangCJK(eFontPrefLang aLang);
     
     // helper method to add a pref lang to an array, if not already in array
     static void AppendPrefLang(eFontPrefLang aPrefLangs[], PRUint32& aLen, eFontPrefLang aAddLang);
 
+    // returns a list of commonly used fonts for a given character
+    // these are *possible* matches, no cmap-checking is done at this level
+    virtual void GetCommonFallbackFonts(const PRUint32 /*aCh*/,
+                                        PRInt32 /*aRunScript*/,
+                                        nsTArray<const char*>& /*aFontList*/)
+    {
+        // platform-specific override, by default do nothing
+    }
+
     // helper method to indicate if we want to use Azure content drawing
     static bool UseAzureContentDrawing();
     
     /**
      * Are we going to try color management?
      */
     static eCMSMode GetCMSMode();
 
@@ -449,16 +465,20 @@ protected:
     PRInt8  mAllowDownloadableFonts;
     PRInt8  mDownloadableFontsSanitize;
 #ifdef MOZ_GRAPHITE
     PRInt8  mGraphiteShapingEnabled;
 #endif
 
     PRInt8  mBidiNumeralOption;
 
+    // whether to always search font cmaps globally 
+    // when doing system font fallback
+    PRInt8  mFallbackUsesCmaps;
+
     // which scripts should be shaped with harfbuzz
     PRInt32 mUseHarfBuzzScripts;
 
     // The preferred draw target backend to use
     mozilla::gfx::BackendType mPreferredDrawTargetBackend;
 
 private:
     virtual qcms_profile* GetPlatformCMSOutputProfile();
--- a/gfx/thebes/gfxPlatformFontList.cpp
+++ b/gfx/thebes/gfxPlatformFontList.cpp
@@ -15,16 +15,17 @@
  * The Original Code is Mozilla Corporation code.
  *
  * The Initial Developer of the Original Code is Mozilla Foundation.
  * Portions created by the Initial Developer are Copyright (C) 2006-2009
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
  *   Jonathan Kew <jfkthame@gmail.com>
+ *   John Daggett <jdaggett@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
@@ -371,103 +372,163 @@ struct FontFamilyListData {
 
 void
 gfxPlatformFontList::GetFontFamilyList(nsTArray<nsRefPtr<gfxFontFamily> >& aFamilyArray)
 {
     FontFamilyListData data(aFamilyArray);
     mFontFamilies.Enumerate(FontFamilyListData::AppendFamily, &data);
 }
 
-gfxFontEntry*  
-gfxPlatformFontList::FindFontForChar(const PRUint32 aCh, gfxFont *aPrevFont)
-{
+gfxFontEntry*
+gfxPlatformFontList::SystemFindFontForChar(const PRUint32 aCh,
+                                           PRInt32 aRunScript,
+                                           const gfxFontStyle* aStyle)
+ {
+    gfxFontEntry* fontEntry = nsnull;
+
     // is codepoint with no matching font? return null immediately
     if (mCodepointsWithNoFonts.test(aCh)) {
         return nsnull;
     }
 
-    // TODO: optimize fallback e.g. by caching lists of fonts to try for a given
-    // unicode range or script
-
-    // try to short-circuit font fallback for U+FFFD, used to represent encoding errors:
-    // just use a platform-specific fallback system font that is guaranteed (or at least
-    // highly likely) to be around, or a cached family from last time U+FFFD was seen.
-    // this helps speed up pages with lots of encoding errors, binary-as-text, etc.
+    // try to short-circuit font fallback for U+FFFD, used to represent
+    // encoding errors: just use a platform-specific fallback system
+    // font that is guaranteed (or at least highly likely) to be around,
+    // or a cached family from last time U+FFFD was seen. this helps
+    // speed up pages with lots of encoding errors, binary-as-text, etc.
     if (aCh == 0xFFFD && mReplacementCharFallbackFamily.Length() > 0) {
-        gfxFontEntry* fontEntry = nsnull;
         bool needsBold;  // ignored in the system fallback case
 
-        if (aPrevFont) {
-            fontEntry = FindFontForFamily(mReplacementCharFallbackFamily, aPrevFont->GetStyle(), needsBold);
-        } else {
-            gfxFontStyle normalStyle;
-            fontEntry = FindFontForFamily(mReplacementCharFallbackFamily, &normalStyle, needsBold);
-        }
+        fontEntry = FindFontForFamily(mReplacementCharFallbackFamily,
+                                      aStyle, needsBold);
 
         if (fontEntry && fontEntry->TestCharacterMap(aCh))
             return fontEntry;
     }
 
-    static bool first = true;
     TimeStamp start = TimeStamp::Now();
 
-    FontSearch data(aCh, aPrevFont);
-
-    // iterate over all font families to find a font that support the character
-    mFontFamilies.Enumerate(gfxPlatformFontList::FindFontForCharProc, &data);
-
+    // search commonly available fonts
+    bool common = true;
+    fontEntry = CommonFontFallback(aCh, aRunScript, aStyle);
+ 
+    // if didn't find a font, do system-wide fallback (except for specials)
+    PRUint32 cmapCount = 0;
+    if (!fontEntry) {
+        common = false;
+        fontEntry = GlobalFontFallback(aCh, aRunScript, aStyle, cmapCount);
+    }
     TimeDuration elapsed = TimeStamp::Now() - start;
 
 #ifdef PR_LOGGING
     PRLogModuleInfo *log = gfxPlatform::GetLog(eGfxLog_textrun);
 
     if (NS_UNLIKELY(log)) {
         PRUint32 charRange = gfxFontUtils::CharRangeBit(aCh);
         PRUint32 unicodeRange = FindCharUnicodeRange(aCh);
         PRInt32 script = mozilla::unicode::GetScriptCode(aCh);
-        PR_LOG(log, PR_LOG_DEBUG,\
-               ("(textrun-systemfallback) char: u+%6.6x "
-                "char-range: %d unicode-range: %d script: %d match: [%s]"
-                " count: %d time: %dus\n",
-                aCh,
-                charRange, unicodeRange, script,
-                (data.mBestMatch ?
-                 NS_ConvertUTF16toUTF8(data.mBestMatch->Name()).get() :
-                 "<none>"),
-                data.mCount,
-                PRInt32(elapsed.ToMicroseconds())));
+        PR_LOG(log, PR_LOG_WARNING,\
+               ("(textrun-systemfallback-%s) char: u+%6.6x "
+                 "char-range: %d unicode-range: %d script: %d match: [%s]"
+                " time: %dus cmaps: %d\n",
+                (common ? "common" : "global"), aCh,
+                 charRange, unicodeRange, script,
+                (fontEntry ? NS_ConvertUTF16toUTF8(fontEntry->Name()).get() :
+                    "<none>"),
+                PRInt32(elapsed.ToMicroseconds()),
+                cmapCount));
     }
 #endif
 
     // no match? add to set of non-matching codepoints
-    if (!data.mBestMatch) {
-        mCodepointsWithNoFonts.set(aCh);
-    } else if (aCh == 0xFFFD) {
-        mReplacementCharFallbackFamily = data.mBestMatch->FamilyName();
-    }
-
+    if (!fontEntry) {
+         mCodepointsWithNoFonts.set(aCh);
+    } else if (aCh == 0xFFFD && fontEntry) {
+        mReplacementCharFallbackFamily = fontEntry->FamilyName();
+     }
+ 
+    // track system fallback time
+    static bool first = true;
     PRInt32 intElapsed = PRInt32(first ? elapsed.ToMilliseconds() :
                                          elapsed.ToMicroseconds());
     Telemetry::Accumulate((first ? Telemetry::SYSTEM_FONT_FALLBACK_FIRST :
                                    Telemetry::SYSTEM_FONT_FALLBACK),
                           intElapsed);
     first = false;
 
-    return data.mBestMatch;
+    // track the script for which fallback occurred (incremented one make it
+    // 1-based)
+    Telemetry::Accumulate(Telemetry::SYSTEM_FONT_FALLBACK_SCRIPT, aRunScript + 1);
+
+    return fontEntry;
 }
 
 PLDHashOperator PR_CALLBACK 
 gfxPlatformFontList::FindFontForCharProc(nsStringHashKey::KeyType aKey, nsRefPtr<gfxFontFamily>& aFamilyEntry,
      void *userArg)
 {
-    FontSearch *data = static_cast<FontSearch*>(userArg);
+    GlobalFontMatch *data = static_cast<GlobalFontMatch*>(userArg);
+ 
+     // evaluate all fonts in this family for a match
+     aFamilyEntry->FindFontForChar(data);
+
+     return PL_DHASH_NEXT;
+}
+
+#define NUM_FALLBACK_FONTS        8
+
+gfxFontEntry*
+gfxPlatformFontList::CommonFontFallback(const PRUint32 aCh,
+                                        PRInt32 aRunScript,
+                                        const gfxFontStyle* aMatchStyle)
+{
+    nsAutoTArray<const char*,NUM_FALLBACK_FONTS> defaultFallbacks;
+    PRUint32 i, numFallbacks;
+
+    gfxPlatform::GetPlatform()->GetCommonFallbackFonts(aCh, aRunScript,
+                                                       defaultFallbacks);
+    numFallbacks = defaultFallbacks.Length();
+    for (i = 0; i < numFallbacks; i++) {
+        nsAutoString familyName;
+        const char *fallbackFamily = defaultFallbacks[i];
 
-    // evaluate all fonts in this family for a match
-    aFamilyEntry->FindFontForChar(data);
-    return PL_DHASH_NEXT;
+        familyName.AppendASCII(fallbackFamily);
+        gfxFontFamily *fallback =
+                gfxPlatformFontList::PlatformFontList()->FindFamily(familyName);
+        if (!fallback)
+            continue;
+
+        gfxFontEntry *fontEntry;
+        bool needsBold;  // ignored in the system fallback case
+
+        // use first font in list that supports a given character
+        fontEntry = fallback->FindFontForStyle(*aMatchStyle, needsBold);
+        if (fontEntry && fontEntry->TestCharacterMap(aCh)) {
+            return fontEntry;
+        }
+    }
+
+    return nsnull;
+}
+
+gfxFontEntry*
+gfxPlatformFontList::GlobalFontFallback(const PRUint32 aCh,
+                                        PRInt32 aRunScript,
+                                        const gfxFontStyle* aMatchStyle,
+                                        PRUint32& aCmapCount)
+{
+    // otherwise, try to find it among local fonts
+    GlobalFontMatch data(aCh, aRunScript, aMatchStyle);
+
+    // iterate over all font families to find a font that support the character
+    mFontFamilies.Enumerate(gfxPlatformFontList::FindFontForCharProc, &data);
+
+    aCmapCount = data.mCmapsTested;
+
+    return data.mBestMatch;
 }
 
 #ifdef XP_WIN
 #include <windows.h>
 
 // crude hack for using when monitoring process
 static void LogRegistryEvent(const wchar_t *msg)
 {
@@ -604,37 +665,41 @@ gfxPlatformFontList::GetStandardFamilyNa
 void 
 gfxPlatformFontList::InitLoader()
 {
     GetFontFamilyList(mFontFamiliesToLoad);
     mStartIndex = 0;
     mNumFamilies = mFontFamiliesToLoad.Length();
 }
 
-bool 
+bool
 gfxPlatformFontList::RunLoader()
 {
     PRUint32 i, endIndex = (mStartIndex + mIncrement < mNumFamilies ? mStartIndex + mIncrement : mNumFamilies);
+    bool loadCmaps = !UsesSystemFallback() ||
+        gfxPlatform::GetPlatform()->UseCmapsDuringSystemFallback();
 
     // for each font family, load in various font info
     for (i = mStartIndex; i < endIndex; i++) {
         gfxFontFamily* familyEntry = mFontFamiliesToLoad[i];
 
         // find all faces that are members of this family
         familyEntry->FindStyleVariations();
         if (familyEntry->GetFontList().Length() == 0) {
             // failed to load any faces for this family, so discard it
             nsAutoString key;
             GenerateFontListKey(familyEntry->Name(), key);
             mFontFamilies.Remove(key);
             continue;
         }
 
-        // load the cmaps
-        familyEntry->ReadCMAP();
+        // load the cmaps if needed
+        if (loadCmaps) {
+            familyEntry->ReadAllCMAPs();
+        }
 
         // read in face names
         familyEntry->ReadFaceNames(this, mNeedFullnamePostscriptNames);
 
         // check whether the family can be considered "simple" for style matching
         familyEntry->CheckForSimpleFamily();
     }
 
--- a/gfx/thebes/gfxPlatformFontList.h
+++ b/gfx/thebes/gfxPlatformFontList.h
@@ -15,16 +15,17 @@
  * The Original Code is Mozilla Corporation code.
  *
  * The Initial Developer of the Original Code is Mozilla Foundation.
  * Portions created by the Initial Developer are Copyright (C) 2008-2009
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
  *   Jonathan Kew <jfkthame@gmail.com>
+ *   John Daggett <jdaggett@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
@@ -92,17 +93,20 @@ public:
                                    nsAString& aResolvedFontName);
 
     void UpdateFontList() { InitFontList(); }
 
     void ClearPrefFonts() { mPrefFonts.Clear(); }
 
     virtual void GetFontFamilyList(nsTArray<nsRefPtr<gfxFontFamily> >& aFamilyArray);
 
-    gfxFontEntry* FindFontForChar(const PRUint32 aCh, gfxFont *aPrevFont);
+    virtual gfxFontEntry*
+    SystemFindFontForChar(const PRUint32 aCh,
+                          PRInt32 aRunScript,
+                          const gfxFontStyle* aStyle);
 
     // TODO: make this virtual, for lazily adding to the font list
     virtual gfxFontFamily* FindFamily(const nsAString& aFamily);
 
     gfxFontEntry* FindFontForFamily(const nsAString& aFamily, const gfxFontStyle* aStyle, bool& aNeedsBold);
 
     bool GetPrefFontFamilyEntries(eFontPrefLang aLangGroup, nsTArray<nsRefPtr<gfxFontFamily> > *array);
     void SetPrefFontFamilyEntries(eFontPrefLang aLangGroup, nsTArray<nsRefPtr<gfxFontFamily> >& array);
@@ -141,16 +145,31 @@ protected:
     gfxPlatformFontList(bool aNeedFullnamePostscriptNames = true);
 
     static gfxPlatformFontList *sPlatformFontList;
 
     static PLDHashOperator FindFontForCharProc(nsStringHashKey::KeyType aKey,
                                                nsRefPtr<gfxFontFamily>& aFamilyEntry,
                                                void* userArg);
 
+    // returns default font for a given character, null otherwise
+    virtual gfxFontEntry* CommonFontFallback(const PRUint32 aCh,
+                                             PRInt32 aRunScript,
+                                             const gfxFontStyle* aMatchStyle);
+
+    // search fonts system-wide for a given character, null otherwise
+    virtual gfxFontEntry* GlobalFontFallback(const PRUint32 aCh,
+                                             PRInt32 aRunScript,
+                                             const gfxFontStyle* aMatchStyle,
+                                             PRUint32& aCmapCount);
+
+    // whether system-based font fallback is used or not
+    // if system fallback is used, no need to load all cmaps
+    virtual bool UsesSystemFallback() { return false; }
+
     // separate initialization for reading in name tables, since this is expensive
     void InitOtherFamilyNames();
 
     static PLDHashOperator InitOtherFamilyNamesProc(nsStringHashKey::KeyType aKey,
                                                     nsRefPtr<gfxFontFamily>& aFamilyEntry,
                                                     void* userArg);
 
     // read in all fullname/Postscript names for all font faces
--- a/gfx/thebes/gfxPlatformGtk.cpp
+++ b/gfx/thebes/gfxPlatformGtk.cpp
@@ -40,16 +40,17 @@
 #ifdef MOZ_PANGO
 #define PANGO_ENABLE_BACKEND
 #define PANGO_ENABLE_ENGINE
 #endif
 
 #include "gfxPlatformGtk.h"
 
 #include "nsUnicharUtils.h"
+#include "nsUnicodeProperties.h"
 #include "gfxFontconfigUtils.h"
 #ifdef MOZ_PANGO
 #include "gfxPangoFonts.h"
 #include "gfxContext.h"
 #include "gfxUserFontSet.h"
 #else
 #include <ft2build.h>
 #include FT_FREETYPE_H
@@ -77,16 +78,17 @@
 #include <fontconfig/fontconfig.h>
 
 #include "nsMathUtils.h"
 
 #define GDK_PIXMAP_SIZE_MAX 32767
 
 using namespace mozilla;
 using namespace mozilla::gfx;
+using namespace mozilla::unicode;
 
 gfxFontconfigUtils *gfxPlatformGtk::sFontconfigUtils = nsnull;
 
 #ifndef MOZ_PANGO
 typedef nsDataHashtable<nsStringHashKey, nsRefPtr<FontFamily> > FontTable;
 typedef nsDataHashtable<nsCStringHashKey, nsTArray<nsRefPtr<gfxFontEntry> > > PrefFontTable;
 static FontTable *gPlatformFonts = NULL;
 static FontTable *gPlatformFontAliases = NULL;
@@ -663,33 +665,34 @@ gfxPlatformGtk::FindFontEntry(const nsAS
     return ff->FindFontEntry(aFontStyle);
 }
 
 static PLDHashOperator
 FindFontForCharProc(nsStringHashKey::KeyType aKey,
                     nsRefPtr<FontFamily>& aFontFamily,
                     void* aUserArg)
 {
-    FontSearch *data = (FontSearch*)aUserArg;
+    GlobalFontMatch *data = (GlobalFontMatch*)aUserArg;
     aFontFamily->FindFontForChar(data);
     return PL_DHASH_NEXT;
 }
 
 already_AddRefed<gfxFont>
 gfxPlatformGtk::FindFontForChar(PRUint32 aCh, gfxFont *aFont)
 {
     if (!gPlatformFonts || !gCodepointsWithNoFonts)
         return nsnull;
 
     // is codepoint with no matching font? return null immediately
     if (gCodepointsWithNoFonts->test(aCh)) {
         return nsnull;
     }
 
-    FontSearch data(aCh, aFont);
+    GlobalFontMatch data(aCh, GetScriptCode(aCh),
+                         (aFont ? aFont->GetStyle() : nsnull));
 
     // find fonts that support the character
     gPlatformFonts->Enumerate(FindFontForCharProc, &data);
 
     if (data.mBestMatch) {
         nsRefPtr<gfxFT2Font> font =
             gfxFT2Font::GetOrMakeFont(static_cast<FontEntry*>(data.mBestMatch.get()),
                                       aFont->GetStyle()); 
--- a/gfx/thebes/gfxPlatformMac.cpp
+++ b/gfx/thebes/gfxPlatformMac.cpp
@@ -258,16 +258,118 @@ gfxPlatformMac::GetFontList(nsIAtom *aLa
 
 nsresult
 gfxPlatformMac::UpdateFontList()
 {
     gfxPlatformFontList::PlatformFontList()->UpdateFontList();
     return NS_OK;
 }
 
+static const char kFontArialUnicodeMS[] = "Arial Unicode MS";
+static const char kFontAppleBraille[] = "Apple Braille";
+static const char kFontAppleSymbols[] = "Apple Symbols";
+static const char kFontAppleMyungjo[] = "AppleMyungjo";
+static const char kFontGeneva[] = "Geneva";
+static const char kFontGeezaPro[] = "Geeza Pro";
+static const char kFontHiraginoKakuGothic[] = "Hiragino Kaku Gothic ProN";
+static const char kFontLucidaGrande[] = "Lucida Grande";
+static const char kFontMenlo[] = "Menlo";
+static const char kFontPlantagenetCherokee[] = "Plantagenet Cherokee";
+static const char kFontSTHeiti[] = "STHeiti";
+
+void
+gfxPlatformMac::GetCommonFallbackFonts(const PRUint32 aCh,
+                                       PRInt32 aRunScript,
+                                       nsTArray<const char*>& aFontList)
+{
+    aFontList.AppendElement(kFontLucidaGrande);
+
+    if (!IS_IN_BMP(aCh)) {
+        PRUint32 p = aCh >> 16;
+        if (p == 1) {
+            aFontList.AppendElement(kFontAppleSymbols);
+            aFontList.AppendElement(kFontGeneva);
+        }
+    } else {
+        PRUint32 b = (aCh >> 8) & 0xff;
+
+        switch (b) {
+        case 0x03:
+        case 0x05:
+            aFontList.AppendElement(kFontGeneva);
+            break;
+        case 0x07:
+            aFontList.AppendElement(kFontGeezaPro);
+            break;
+        case 0x10:
+            aFontList.AppendElement(kFontMenlo);
+            break;
+        case 0x13:  // Cherokee
+            aFontList.AppendElement(kFontPlantagenetCherokee);
+            break;
+        case 0x18:  // Mongolian
+            aFontList.AppendElement(kFontSTHeiti);
+            break;
+        case 0x1d:
+        case 0x1e:
+            aFontList.AppendElement(kFontGeneva);
+            break;
+        case 0x20:  // Symbol ranges
+        case 0x21:
+        case 0x22:
+        case 0x23:
+        case 0x24:
+        case 0x25:
+        case 0x26:
+        case 0x27:
+        case 0x29:
+        case 0x2a:
+        case 0x2b:
+        case 0x2e:
+            aFontList.AppendElement(kFontAppleSymbols);
+            aFontList.AppendElement(kFontMenlo);
+            aFontList.AppendElement(kFontGeneva);
+            aFontList.AppendElement(kFontHiraginoKakuGothic);
+            break;
+        case 0x2c:
+        case 0x2d:
+            aFontList.AppendElement(kFontGeneva);
+            break;
+        case 0x28:  // Braille
+            aFontList.AppendElement(kFontAppleBraille);
+            break;
+        case 0x4d:
+            aFontList.AppendElement(kFontAppleSymbols);
+            break;
+        case 0xa0:  // Yi
+        case 0xa1:
+        case 0xa2:
+        case 0xa3:
+        case 0xa4:
+            aFontList.AppendElement(kFontSTHeiti);
+            break;
+        case 0xa6:
+        case 0xa7:
+            aFontList.AppendElement(kFontGeneva);
+            aFontList.AppendElement(kFontAppleSymbols);
+            break;
+        case 0xfc:
+        case 0xff:
+            aFontList.AppendElement(kFontAppleSymbols);
+            break;
+        default:
+            break;
+        }
+    }
+
+    // Arial Unicode MS has lots of glyphs for obscure, use it as a last resort
+    aFontList.AppendElement(kFontArialUnicodeMS);
+}
+
+
 PRInt32 
 gfxPlatformMac::OSXVersion()
 {
     if (!mOSXVersion) {
         // minor version is not accurate, use gestaltSystemVersionMajor, gestaltSystemVersionMinor, gestaltSystemVersionBugFix for these
         OSErr err = ::Gestalt(gestaltSystemVersion, reinterpret_cast<SInt32*>(&mOSXVersion));
         if (err != noErr) {
             //This should probably be changed when our minimum version changes
--- a/gfx/thebes/gfxPlatformMac.h
+++ b/gfx/thebes/gfxPlatformMac.h
@@ -45,16 +45,17 @@
 #define MAC_OS_X_VERSION_10_4_HEX 0x00001040
 #define MAC_OS_X_VERSION_10_5_HEX 0x00001050
 #define MAC_OS_X_VERSION_10_6_HEX 0x00001060
 #define MAC_OS_X_VERSION_10_7_HEX 0x00001070
 
 #define MAC_OS_X_MAJOR_VERSION_MASK 0xFFFFFFF0U
 
 class gfxTextRun;
+class gfxFontFamily;
 class mozilla::gfx::DrawTarget;
 
 class THEBES_API gfxPlatformMac : public gfxPlatform {
 public:
     gfxPlatformMac();
     virtual ~gfxPlatformMac();
 
     static gfxPlatformMac *GetPlatform() {
@@ -93,16 +94,20 @@ public:
 
     bool IsFontFormatSupported(nsIURI *aFontURI, PRUint32 aFormatFlags);
 
     nsresult GetFontList(nsIAtom *aLangGroup,
                          const nsACString& aGenericFamily,
                          nsTArray<nsString>& aListOfFonts);
     nsresult UpdateFontList();
 
+    virtual void GetCommonFallbackFonts(const PRUint32 aCh,
+                                        PRInt32 aRunScript,
+                                        nsTArray<const char*>& aFontList);
+
     // Returns the OS X version as returned from Gestalt(gestaltSystemVersion, ...)
     // Ex: Mac OS X 10.4.x ==> 0x104x 
     PRInt32 OSXVersion();
 
     // lower threshold on font anti-aliasing
     PRUint32 GetAntiAliasingThreshold() { return mFontAntiAliasingThreshold; }
 
     virtual already_AddRefed<gfxASurface>
--- a/gfx/thebes/gfxQtPlatform.cpp
+++ b/gfx/thebes/gfxQtPlatform.cpp
@@ -45,16 +45,17 @@
 #include "gfxQtPlatform.h"
 
 #include "gfxFontconfigUtils.h"
 
 #include "cairo.h"
 
 #include "gfxImageSurface.h"
 #include "gfxQPainterSurface.h"
+#include "nsUnicodeProperties.h"
 
 #ifdef MOZ_PANGO
 #include "gfxPangoFonts.h"
 #include "gfxContext.h"
 #include "gfxUserFontSet.h"
 #else
 #include "gfxFT2Fonts.h"
 #endif
@@ -74,16 +75,17 @@
 #ifndef MOZ_PANGO
 #include <ft2build.h>
 #include FT_FREETYPE_H
 #endif
 
 #include "mozilla/Preferences.h"
 
 using namespace mozilla;
+using namespace mozilla::unicode;
 
 #define DEFAULT_RENDER_MODE RENDER_DIRECT
 
 static QPaintEngine::Type sDefaultQtPaintEngineType = QPaintEngine::Raster;
 gfxFontconfigUtils *gfxQtPlatform::sFontconfigUtils = nsnull;
 static cairo_user_data_key_t cairo_qt_pixmap_key;
 static void do_qt_pixmap_unref (void *data)
 {
@@ -521,33 +523,34 @@ gfxQtPlatform::FindFontEntry(const nsASt
     return ff->FindFontEntry(aFontStyle);
 }
 
 static PLDHashOperator
 FindFontForCharProc(nsStringHashKey::KeyType aKey,
                     nsRefPtr<FontFamily>& aFontFamily,
                     void* aUserArg)
 {
-    FontSearch *data = (FontSearch*)aUserArg;
+    GlobalFontMatch *data = (GlobalFontMatch*)aUserArg;
     aFontFamily->FindFontForChar(data);
     return PL_DHASH_NEXT;
 }
 
 already_AddRefed<gfxFont>
 gfxQtPlatform::FindFontForChar(PRUint32 aCh, gfxFont *aFont)
 {
     if (!gPlatformFonts || !gCodepointsWithNoFonts)
         return nsnull;
 
     // is codepoint with no matching font? return null immediately
     if (gCodepointsWithNoFonts->test(aCh)) {
         return nsnull;
     }
 
-    FontSearch data(aCh, aFont);
+    GlobalFontMatch data(aCh, GetScriptCode(aCh),
+                         (aFont ? aFont->GetStyle() : nsnull));
 
     // find fonts that support the character
     gPlatformFonts->Enumerate(FindFontForCharProc, &data);
 
     if (data.mBestMatch) {
         nsRefPtr<gfxFT2Font> font =
             gfxFT2Font::GetOrMakeFont(static_cast<FontEntry*>(data.mBestMatch.get()),
                                       aFont->GetStyle()); 
--- a/gfx/thebes/gfxUserFontSet.cpp
+++ b/gfx/thebes/gfxUserFontSet.cpp
@@ -397,43 +397,44 @@ StoreUserFontData(gfxFontEntry* aFontEnt
     userFontData->mFormat = src.mFormatFlags;
     userFontData->mRealName = aOriginalName;
     if (aMetadata) {
         userFontData->mMetadata.SwapElements(*aMetadata);
         userFontData->mMetaOrigLen = aMetaOrigLen;
     }
 }
 
+struct WOFFHeader {
+    AutoSwap_PRUint32 signature;
+    AutoSwap_PRUint32 flavor;
+    AutoSwap_PRUint32 length;
+    AutoSwap_PRUint16 numTables;
+    AutoSwap_PRUint16 reserved;
+    AutoSwap_PRUint32 totalSfntSize;
+    AutoSwap_PRUint16 majorVersion;
+    AutoSwap_PRUint16 minorVersion;
+    AutoSwap_PRUint32 metaOffset;
+    AutoSwap_PRUint32 metaCompLen;
+    AutoSwap_PRUint32 metaOrigLen;
+    AutoSwap_PRUint32 privOffset;
+    AutoSwap_PRUint32 privLen;
+};
+
 void
 gfxUserFontSet::CopyWOFFMetadata(const PRUint8* aFontData,
                                  PRUint32 aLength,
                                  nsTArray<PRUint8>* aMetadata,
                                  PRUint32* aMetaOrigLen)
 {
     // This function may be called with arbitrary, unvalidated "font" data
     // from @font-face, so it needs to be careful to bounds-check, etc.,
     // before trying to read anything.
     // This just saves a copy of the compressed data block; it does NOT check
     // that the block can be successfully decompressed, or that it contains
     // well-formed/valid XML metadata.
-    struct WOFFHeader {
-        AutoSwap_PRUint32 signature;
-        AutoSwap_PRUint32 flavor;
-        AutoSwap_PRUint32 length;
-        AutoSwap_PRUint16 numTables;
-        AutoSwap_PRUint16 reserved;
-        AutoSwap_PRUint32 totalSfntSize;
-        AutoSwap_PRUint16 majorVersion;
-        AutoSwap_PRUint16 minorVersion;
-        AutoSwap_PRUint32 metaOffset;
-        AutoSwap_PRUint32 metaCompLen;
-        AutoSwap_PRUint32 metaOrigLen;
-        AutoSwap_PRUint32 privOffset;
-        AutoSwap_PRUint32 privLen;
-    };
     if (aLength < sizeof(WOFFHeader)) {
         return;
     }
     const WOFFHeader* woff = reinterpret_cast<const WOFFHeader*>(aFontData);
     PRUint32 metaOffset = woff->metaOffset;
     PRUint32 metaCompLen = woff->metaCompLen;
     if (!metaOffset || !metaCompLen || !woff->metaOrigLen) {
         return;
--- a/gfx/thebes/gfxWindowsPlatform.cpp
+++ b/gfx/thebes/gfxWindowsPlatform.cpp
@@ -823,16 +823,185 @@ RemoveCharsetFromFontSubstitute(nsAStrin
 nsresult
 gfxWindowsPlatform::UpdateFontList()
 {
     gfxPlatformFontList::PlatformFontList()->UpdateFontList();
 
     return NS_OK;
 }
 
+static const char kFontArabicTypesetting[] = "Arabic Typesetting";
+static const char kFontArial[] = "Arial";
+static const char kFontArialUnicodeMS[] = "Arial Unicode MS";
+static const char kFontCambria[] = "Cambria";
+static const char kFontCambriaMath[] = "Cambria Math";
+static const char kFontEbrima[] = "Ebrima";
+static const char kFontEstrangeloEdessa[] = "Estrangelo Edessa";
+static const char kFontEuphemia[] = "Euphemia";
+static const char kFontGabriola[] = "Gabriola";
+static const char kFontKhmerUI[] = "Khmer UI";
+static const char kFontLaoUI[] = "Lao UI";
+static const char kFontMVBoli[] = "MV Boli";
+static const char kFontMalgunGothic[] = "Malgun Gothic";
+static const char kFontMicrosoftJhengHei[] = "Microsoft JhengHei";
+static const char kFontMicrosoftNewTaiLue[] = "Microsoft New Tai Lue";
+static const char kFontMicrosoftPhagsPa[] = "Microsoft PhagsPa";
+static const char kFontMicrosoftTaiLe[] = "Microsoft Tai Le";
+static const char kFontMicrosoftUighur[] = "Microsoft Uighur";
+static const char kFontMicrosoftYaHei[] = "Microsoft YaHei";
+static const char kFontMicrosoftYiBaiti[] = "Microsoft Yi Baiti";
+static const char kFontMeiryo[] = "Meiryo";
+static const char kFontMongolianBaiti[] = "Mongolian Baiti";
+static const char kFontNyala[] = "Nyala";
+static const char kFontPlantagenetCherokee[] = "Plantagenet Cherokee";
+static const char kFontSegoeUI[] = "Segoe UI";
+static const char kFontSegoeUISymbol[] = "Segoe UI Symbol";
+static const char kFontSylfaen[] = "Sylfaen";
+static const char kFontTraditionalArabic[] = "Traditional Arabic";
+
+void
+gfxWindowsPlatform::GetCommonFallbackFonts(const PRUint32 aCh,
+                                           PRInt32 aRunScript,
+                                           nsTArray<const char*>& aFontList)
+{
+    // Arial is used as the default fallback for system fallback
+    aFontList.AppendElement(kFontArial);
+
+    if (!IS_IN_BMP(aCh)) {
+        PRUint32 p = aCh >> 16;
+        if (p == 1) { // SMP plane
+            aFontList.AppendElement(kFontCambriaMath);
+            aFontList.AppendElement(kFontSegoeUISymbol);
+            aFontList.AppendElement(kFontEbrima);
+        }
+    } else {
+        PRUint32 b = (aCh >> 8) & 0xff;
+
+        switch (b) {
+        case 0x05:
+            aFontList.AppendElement(kFontEstrangeloEdessa);
+            aFontList.AppendElement(kFontCambria);
+            break;
+        case 0x06:
+            aFontList.AppendElement(kFontMicrosoftUighur);
+            break;
+        case 0x07:
+            aFontList.AppendElement(kFontEstrangeloEdessa);
+            aFontList.AppendElement(kFontMVBoli);
+            aFontList.AppendElement(kFontEbrima);
+            break;
+        case 0x0e:
+            aFontList.AppendElement(kFontLaoUI);
+            break;
+        case 0x12:
+        case 0x13:
+            aFontList.AppendElement(kFontNyala);
+            aFontList.AppendElement(kFontPlantagenetCherokee);
+            break;
+        case 0x14:
+        case 0x15:
+        case 0x16:
+            aFontList.AppendElement(kFontEuphemia);
+            aFontList.AppendElement(kFontSegoeUISymbol);
+            break;
+        case 0x17:
+            aFontList.AppendElement(kFontKhmerUI);
+            break;
+        case 0x18:  // Mongolian
+            aFontList.AppendElement(kFontMongolianBaiti);
+            break;
+        case 0x19:
+            aFontList.AppendElement(kFontMicrosoftTaiLe);
+            aFontList.AppendElement(kFontMicrosoftNewTaiLue);
+            aFontList.AppendElement(kFontKhmerUI);
+            break;
+            break;
+        case 0x20:  // Symbol ranges
+        case 0x21:
+        case 0x22:
+        case 0x23:
+        case 0x24:
+        case 0x25:
+        case 0x26:
+        case 0x27:
+        case 0x29:
+        case 0x2a:
+        case 0x2b:
+        case 0x2c:
+            aFontList.AppendElement(kFontSegoeUI);
+            aFontList.AppendElement(kFontSegoeUISymbol);
+            aFontList.AppendElement(kFontCambria);
+            aFontList.AppendElement(kFontCambriaMath);
+            aFontList.AppendElement(kFontMeiryo);
+            aFontList.AppendElement(kFontArial);
+            aFontList.AppendElement(kFontEbrima);
+            break;
+        case 0x2d:
+        case 0x2e:
+        case 0x2f:
+            aFontList.AppendElement(kFontEbrima);
+            aFontList.AppendElement(kFontNyala);
+            aFontList.AppendElement(kFontMeiryo);
+            break;
+        case 0x28:  // Braille
+            aFontList.AppendElement(kFontSegoeUISymbol);
+            break;
+        case 0x30:
+        case 0x31:
+            aFontList.AppendElement(kFontMicrosoftYaHei);
+            break;
+        case 0x32:
+            aFontList.AppendElement(kFontMalgunGothic);
+            break;
+        case 0x4d:
+            aFontList.AppendElement(kFontSegoeUISymbol);
+            break;
+        case 0xa0:  // Yi
+        case 0xa1:
+        case 0xa2:
+        case 0xa3:
+        case 0xa4:
+            aFontList.AppendElement(kFontMicrosoftYiBaiti);
+            break;
+        case 0xa5:
+        case 0xa6:
+        case 0xa7:
+            aFontList.AppendElement(kFontEbrima);
+            aFontList.AppendElement(kFontCambriaMath);
+            break;
+        case 0xa8:
+             aFontList.AppendElement(kFontMicrosoftPhagsPa);
+             break;
+        case 0xfb:
+            aFontList.AppendElement(kFontMicrosoftUighur);
+            aFontList.AppendElement(kFontGabriola);
+            aFontList.AppendElement(kFontSylfaen);
+            break;
+        case 0xfc:
+        case 0xfd:
+            aFontList.AppendElement(kFontTraditionalArabic);
+            aFontList.AppendElement(kFontArabicTypesetting);
+            break;
+        case 0xfe:
+            aFontList.AppendElement(kFontTraditionalArabic);
+            aFontList.AppendElement(kFontMicrosoftJhengHei);
+           break;
+       case 0xff:
+            aFontList.AppendElement(kFontMicrosoftJhengHei);
+            break;
+        default:
+            break;
+        }
+    }
+
+    // Arial Unicode MS has lots of glyphs for obscure characters,
+    // use it as a last resort
+    aFontList.AppendElement(kFontArialUnicodeMS);
+}
+
 struct ResolveData {
     ResolveData(gfxPlatform::FontResolverCallback aCallback,
                 gfxWindowsPlatform *aCaller, const nsAString *aFontName,
                 void *aClosure) :
         mFoundCount(0), mCallback(aCallback), mCaller(aCaller),
         mFontName(aFontName), mClosure(aClosure) {}
     PRUint32 mFoundCount;
     gfxPlatform::FontResolverCallback mCallback;
--- a/gfx/thebes/gfxWindowsPlatform.h
+++ b/gfx/thebes/gfxWindowsPlatform.h
@@ -174,16 +174,20 @@ public:
     HDC GetScreenDC() { return mScreenDC; }
 
     nsresult GetFontList(nsIAtom *aLangGroup,
                          const nsACString& aGenericFamily,
                          nsTArray<nsString>& aListOfFonts);
 
     nsresult UpdateFontList();
 
+    virtual void GetCommonFallbackFonts(const PRUint32 aCh,
+                                        PRInt32 aRunScript,
+                                        nsTArray<const char*>& aFontList);
+
     nsresult ResolveFontName(const nsAString& aFontName,
                              FontResolverCallback aCallback,
                              void *aClosure, bool& aAborted);
 
     nsresult GetStandardFamilyName(const nsAString& aFontName, nsAString& aFamilyName);
 
     gfxFontGroup *CreateFontGroup(const nsAString &aFamilies,
                                   const gfxFontStyle *aStyle,
--- a/gfx/thebes/nsUnicodeRange.cpp
+++ b/gfx/thebes/nsUnicodeRange.cpp
@@ -208,17 +208,17 @@ static nsIAtom **gUnicodeRangeToLangGrou
  * Misc - small form variants
  *  fe50 - fe6f
  * Misc - Specials
  *  fff0 - ffff
  *********************************************************************/
 
 
 
-#define NUM_OF_SUBTABLES      9
+#define NUM_OF_SUBTABLES      10
 #define SUBTABLE_SIZE         16
 
 static const PRUint8 gUnicodeSubrangeTable[NUM_OF_SUBTABLES][SUBTABLE_SIZE] = 
 { 
   { // table for X---
     kRangeTableBase+1,  //u0xxx
     kRangeTableBase+2,  //u1xxx
     kRangeTableBase+3,  //u2xxx
@@ -247,17 +247,17 @@ static const PRUint8 gUnicodeSubrangeTab
     kRangeTertiaryTable,     //u07xx
     kRangeUnassigned,        //u08xx
     kRangeTertiaryTable,     //u09xx
     kRangeTertiaryTable,     //u0axx
     kRangeTertiaryTable,     //u0bxx
     kRangeTertiaryTable,     //u0cxx
     kRangeTertiaryTable,     //u0dxx
     kRangeTertiaryTable,     //u0exx
-    kRangeTibetan,           //u0fxx
+    kRangeTibetan            //u0fxx
   },
   { //table for 1x--
     kRangeTertiaryTable,     //u10xx
     kRangeKorean,            //u11xx
     kRangeEthiopic,          //u12xx
     kRangeTertiaryTable,     //u13xx
     kRangeCanadian,          //u14xx
     kRangeCanadian,          //u15xx
@@ -265,17 +265,17 @@ static const PRUint8 gUnicodeSubrangeTab
     kRangeKhmer,             //u17xx
     kRangeMongolian,         //u18xx
     kRangeUnassigned,        //u19xx
     kRangeUnassigned,        //u1axx
     kRangeUnassigned,        //u1bxx
     kRangeUnassigned,        //u1cxx
     kRangeUnassigned,        //u1dxx
     kRangeSetLatin,          //u1exx
-    kRangeGreek,             //u1fxx
+    kRangeGreek              //u1fxx
   },
   { //table for 2x--
     kRangeSetLatin,          //u20xx
     kRangeSetLatin,          //u21xx
     kRangeMathOperators,     //u22xx
     kRangeMiscTechnical,     //u23xx
     kRangeControlOpticalEnclose, //u24xx
     kRangeBoxBlockGeometrics, //u25xx
@@ -283,17 +283,17 @@ static const PRUint8 gUnicodeSubrangeTab
     kRangeDingbats,          //u27xx
     kRangeBraillePattern,    //u28xx
     kRangeUnassigned,        //u29xx
     kRangeUnassigned,        //u2axx
     kRangeUnassigned,        //u2bxx
     kRangeUnassigned,        //u2cxx
     kRangeUnassigned,        //u2dxx
     kRangeSetCJK,            //u2exx
-    kRangeSetCJK,            //u2fxx
+    kRangeSetCJK             //u2fxx
   },
   {  //table for ax--
     kRangeYi,                //ua0xx
     kRangeYi,                //ua1xx
     kRangeYi,                //ua2xx
     kRangeYi,                //ua3xx
     kRangeYi,                //ua4xx
     kRangeUnassigned,        //ua5xx
@@ -301,17 +301,17 @@ static const PRUint8 gUnicodeSubrangeTab
     kRangeUnassigned,        //ua7xx
     kRangeUnassigned,        //ua8xx
     kRangeUnassigned,        //ua9xx
     kRangeUnassigned,        //uaaxx
     kRangeUnassigned,        //uabxx
     kRangeKorean,            //uacxx
     kRangeKorean,            //uadxx
     kRangeKorean,            //uaexx
-    kRangeKorean,            //uafxx
+    kRangeKorean             //uafxx
   },
   {  //table for dx--
     kRangeKorean,            //ud0xx
     kRangeKorean,            //ud1xx
     kRangeKorean,            //ud2xx
     kRangeKorean,            //ud3xx
     kRangeKorean,            //ud4xx
     kRangeKorean,            //ud5xx
@@ -319,38 +319,35 @@ static const PRUint8 gUnicodeSubrangeTab
     kRangeKorean,            //ud7xx
     kRangeSurrogate,         //ud8xx
     kRangeSurrogate,         //ud9xx
     kRangeSurrogate,         //udaxx
     kRangeSurrogate,         //udbxx
     kRangeSurrogate,         //udcxx
     kRangeSurrogate,         //uddxx
     kRangeSurrogate,         //udexx
-    kRangeSurrogate,         //udfxx
+    kRangeSurrogate          //udfxx
   },
   { // table for fx--
     kRangePrivate,           //uf0xx 
     kRangePrivate,           //uf1xx 
     kRangePrivate,           //uf2xx 
     kRangePrivate,           //uf3xx 
     kRangePrivate,           //uf4xx 
     kRangePrivate,           //uf5xx 
     kRangePrivate,           //uf6xx 
     kRangePrivate,           //uf7xx 
     kRangePrivate,           //uf8xx 
     kRangeSetCJK,            //uf9xx 
     kRangeSetCJK,            //ufaxx 
     kRangeArabic,            //ufbxx, includes alphabic presentation form
     kRangeArabic,            //ufcxx
     kRangeArabic,            //ufdxx
-    kRangeArabic,            //ufexx, includes Combining half marks, 
-                             //                CJK compatibility forms, 
-                             //                CJK compatibility forms, 
-                             //                small form variants
-    kRangeTableBase+8,       //uffxx, halfwidth and fullwidth forms, includes Specials
+    kRangeTableBase+8,       //ufexx
+    kRangeTableBase+9        //uffxx, halfwidth and fullwidth forms, includes Specials
   },
   { //table for 0x0500 - 0x05ff
     kRangeCyrillic,          //u050x
     kRangeCyrillic,          //u051x
     kRangeCyrillic,          //u052x
     kRangeArmenian,          //u053x
     kRangeArmenian,          //u054x
     kRangeArmenian,          //u055x
@@ -358,17 +355,35 @@ static const PRUint8 gUnicodeSubrangeTab
     kRangeArmenian,          //u057x
     kRangeArmenian,          //u058x
     kRangeHebrew,            //u059x
     kRangeHebrew,            //u05ax
     kRangeHebrew,            //u05bx
     kRangeHebrew,            //u05cx
     kRangeHebrew,            //u05dx
     kRangeHebrew,            //u05ex
-    kRangeHebrew,            //u05fx
+    kRangeHebrew             //u05fx
+  },
+  { //table for 0xfe00 - 0xfeff
+    kRangeSetCJK,            //ufe0x
+    kRangeSetCJK,            //ufe1x
+    kRangeSetCJK,            //ufe2x
+    kRangeSetCJK,            //ufe3x
+    kRangeSetCJK,            //ufe4x
+    kRangeSetCJK,            //ufe5x
+    kRangeSetCJK,            //ufe6x
+    kRangeArabic,            //ufe7x
+    kRangeArabic,            //ufe8x
+    kRangeArabic,            //ufe9x
+    kRangeArabic,            //ufeax
+    kRangeArabic,            //ufebx
+    kRangeArabic,            //ufecx
+    kRangeArabic,            //ufedx
+    kRangeArabic,            //ufeex
+    kRangeArabic             //ufefx
   },
   { //table for 0xff00 - 0xffff
     kRangeSetCJK,            //uff0x, fullwidth latin
     kRangeSetCJK,            //uff1x, fullwidth latin
     kRangeSetCJK,            //uff2x, fullwidth latin
     kRangeSetCJK,            //uff3x, fullwidth latin
     kRangeSetCJK,            //uff4x, fullwidth latin
     kRangeSetCJK,            //uff5x, fullwidth latin
@@ -420,39 +435,52 @@ static const PRUint8 gUnicodeTertiaryRan
     kRangeEthiopic,          //u128x  place holder(resolved in the 2ndary tab.)
     kRangeEthiopic,          //u130x  
     kRangeCherokee,          //u138x
     kRangeCanadian,          //u140x  place holder(resolved in the 2ndary tab.)
     kRangeCanadian,          //u148x  place holder(resolved in the 2ndary tab.)
     kRangeCanadian,          //u150x  place holder(resolved in the 2ndary tab.)
     kRangeCanadian,          //u158x  place holder(resolved in the 2ndary tab.)
     kRangeCanadian,          //u160x  
-    kRangeOghamRunic,        //u168x  this contains two scripts, Ogham & Runic
+    kRangeOghamRunic         //u168x  this contains two scripts, Ogham & Runic
 };
 
 // A two level index is almost enough for locating a range, with the 
 // exception of u03xx and u05xx. Since we don't really care about range for
 // combining diacritical marks in our font application, they are 
 // not discriminated further. But future adoption of this module for other use 
 // should be aware of this limitation. The implementation can be extended if 
 // there is such a need.
 // For Indic, Southeast Asian scripts and some other scripts between
 // U+0700 and U+16FF, it's extended to the third level.
-PRUint32 FindCharUnicodeRange(PRUnichar ch)
+PRUint32 FindCharUnicodeRange(PRUint32 ch)
 {
   PRUint32 range;
+  
+  // aggregate ranges for non-BMP codepoints
+  if (ch > 0xFFFF) {
+    PRUint32 p = (ch >> 16);
+    if (p == 1) {
+        return kRangeSMP;
+    } else if (p == 2) {
+        return kRangeSetCJK;
+    }
+    return kRangeHigherPlanes;
+  }
 
-  //search the first table
+  // lookup explicit range for BMP codepoints
+  // first general range
   range = gUnicodeSubrangeTable[0][ch >> 12];
   
+  // if general range is good enough, return that
   if (range < kRangeTableBase)
     // we try to get a specific range 
     return range;
 
-  // otherwise, we have one more table to look at
+  // otherwise, use subrange tables
   range = gUnicodeSubrangeTable[range - kRangeTableBase][(ch & 0x0f00) >> 8];
   if (range < kRangeTableBase)
     return range;
   if (range < kRangeTertiaryTable)
     return gUnicodeSubrangeTable[range - kRangeTableBase][(ch & 0x00f0) >> 4];
 
   // Yet another table to look at : U+0700 - U+16FF : 128 code point blocks
   return gUnicodeTertiaryRangeTable[(ch - 0x0700) >> 7];
--- a/gfx/thebes/nsUnicodeRange.h
+++ b/gfx/thebes/nsUnicodeRange.h
@@ -103,17 +103,21 @@ const PRUint8   kRangeControlOpticalEncl
 const PRUint8   kRangeBoxBlockGeometrics   = 46;
 const PRUint8   kRangeMiscSymbols          = 47;
 const PRUint8   kRangeDingbats             = 48;
 const PRUint8   kRangeBraillePattern       = 49;
 const PRUint8   kRangeYi                   = 50;
 const PRUint8   kRangeCombiningDiacriticalMarks = 51;
 const PRUint8   kRangeSpecials             = 52;
 
+// aggregate ranges for non-BMP codepoints (u+2xxxx are all CJK)
+const PRUint8   kRangeSMP                  = 53;  // u+1xxxx
+const PRUint8   kRangeHigherPlanes         = 54;  // u+3xxxx and above
+
 const PRUint8   kRangeTableBase   = 128;    //values over 127 are reserved for internal use only
 const PRUint8   kRangeTertiaryTable  = 145; // leave room for 16 subtable 
                                             // indices (kRangeTableBase + 1 ..
                                             // kRangeTableBase + 16)
 
 
 
-PRUint32 FindCharUnicodeRange(PRUnichar ch);
+PRUint32 FindCharUnicodeRange(PRUint32 ch);
 nsIAtom* LangGroupFromUnicodeRange(PRUint8 unicodeRange);
--- a/image/src/RasterImage.cpp
+++ b/image/src/RasterImage.cpp
@@ -2576,16 +2576,26 @@ RasterImage::Draw(gfxContext *aContext,
   if (mFrameDecodeFlags != DECODE_FLAGS_DEFAULT) {
     if (!CanForciblyDiscard())
       return NS_ERROR_NOT_AVAILABLE;
     ForceDiscard();
 
     mFrameDecodeFlags = DECODE_FLAGS_DEFAULT;
   }
 
+  // If this image is a candidate for discarding, reset its position in the
+  // discard tracker so we're less likely to discard it right away.
+  //
+  // (We don't normally draw unlocked images, so this conditition will usually
+  // be false.  But we will draw unlocked images if image locking is globally
+  // disabled via the content.image.allow_locking pref.)
+  if (DiscardingActive()) {
+    DiscardTracker::Reset(&mDiscardTrackerNode);
+  }
+
   // We use !mDecoded && mHasSourceData to mean discarded.
   if (!mDecoded && mHasSourceData) {
       mDrawStartTime = TimeStamp::Now();
 
       // We're drawing this image, so indicate that we should decode it as soon
       // as possible.
       DecodeWorker::Singleton()->MarkAsASAP(this);
   }
--- a/image/test/reftest/reftest.list
+++ b/image/test/reftest/reftest.list
@@ -1,10 +1,10 @@
 # Check for 24-bit color mode (test for bug 414720)
-== colordepth.html about:blank
+skip-if(Android) == colordepth.html about:blank
 
 # "PngSuite, the official set of PNG test images"
 # Images by Willem van Schaik
 #
 # http://www.schaik.com/pngsuite/pngsuite.html
 # http://www.libpng.org/pub/png/pngsuite.html
 include pngsuite-basic-n/reftest.list
 include pngsuite-basic-i/reftest.list
--- a/js/src/aclocal.m4
+++ b/js/src/aclocal.m4
@@ -10,10 +10,11 @@ builtin(include, build/autoconf/moznbyte
 builtin(include, build/autoconf/mozprog.m4)dnl
 builtin(include, build/autoconf/mozheader.m4)dnl
 builtin(include, build/autoconf/mozcommonheader.m4)dnl
 builtin(include, build/autoconf/acwinpaths.m4)dnl
 builtin(include, build/autoconf/lto.m4)dnl
 builtin(include, build/autoconf/gcc-pr49911.m4)dnl
 builtin(include, build/autoconf/frameptr.m4)dnl
 builtin(include, build/autoconf/compiler-opts.m4)dnl
+builtin(include, build/autoconf/expandlibs.m4)dnl
 
 MOZ_PROG_CHECKMSYS()
copy from build/unix/check_debug_ranges.py
copy to js/src/build/autoconf/check_debug_ranges.py
--- a/js/src/build/autoconf/compiler-opts.m4
+++ b/js/src/build/autoconf/compiler-opts.m4
@@ -4,10 +4,81 @@ AC_DEFUN([MOZ_COMPILER_OPTS],
 [
 if test "$CLANG_CXX"; then
     ## We disable return-type-c-linkage because jsval is defined as a C++ type but is
     ## returned by C functions. This is possible because we use knowledge about the ABI
     ## to typedef it to a C type with the same layout when the headers are included
     ## from C.
     _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Wno-unknown-warning-option -Wno-return-type-c-linkage"
 fi
+
+if test "$GNU_CC"; then
+    CFLAGS="$CFLAGS -ffunction-sections -fdata-sections"
+    CXXFLAGS="$CXXFLAGS -ffunction-sections -fdata-sections"
+fi
+
+dnl ========================================================
+dnl = Identical Code Folding
+dnl ========================================================
+
+MOZ_ARG_DISABLE_BOOL(icf,
+[  --disable-icf          Disable Identical Code Folding],
+    MOZ_DISABLE_ICF=1,
+    MOZ_DISABLE_ICF= )
+
+if test "$GNU_CC" -a "$GCC_USE_GNU_LD" -a -z "$MOZ_DISABLE_ICF"; then
+    AC_CACHE_CHECK([whether the linker supports Identical Code Folding],
+        LD_SUPPORTS_ICF,
+        [echo 'int foo() {return 42;}' \
+              'int bar() {return 42;}' \
+              'int main() {return foo() - bar();}' > conftest.${ac_ext}
+        # If the linker supports ICF, foo and bar symbols will have
+        # the same address
+        if AC_TRY_COMMAND([${CC-cc} -o conftest${ac_exeext} $LDFLAGS -Wl,--icf=safe -ffunction-sections conftest.${ac_ext} $LIBS 1>&2]) &&
+           test -s conftest${ac_exeext} &&
+           objdump -t conftest${ac_exeext} | awk changequote(<<, >>)'{a[<<$>>6] = <<$>>1} END {if (a["foo"] && (a["foo"] != a["bar"])) { exit 1 }}'changequote([, ]); then
+            LD_SUPPORTS_ICF=yes
+        else
+            LD_SUPPORTS_ICF=no
+        fi
+        rm -rf conftest*])
+    if test "$LD_SUPPORTS_ICF" = yes; then
+        _SAVE_LDFLAGS="$LDFLAGS -Wl,--icf=safe"
+        LDFLAGS="$LDFLAGS -Wl,--icf=safe -Wl,--print-icf-sections"
+        AC_TRY_LINK([], [],
+                    [LD_PRINT_ICF_SECTIONS=-Wl,--print-icf-sections],
+                    [LD_PRINT_ICF_SECTIONS=])
+        AC_SUBST([LD_PRINT_ICF_SECTIONS])
+        LDFLAGS="$_SAVE_LDFLAGS"
+    fi
+fi
+
+dnl ========================================================
+dnl = Automatically remove dead symbols
+dnl ========================================================
+
+if test "$GNU_CC" -a "$GCC_USE_GNU_LD" -a -n "$MOZ_DEBUG_FLAGS"; then
+   dnl See bug 670659
+   AC_CACHE_CHECK([whether removing dead symbols breaks debugging],
+       GC_SECTIONS_BREAKS_DEBUG_RANGES,
+       [echo 'int foo() {return 42;}' \
+             'int bar() {return 1;}' \
+             'int main() {return foo();}' > conftest.${ac_ext}
+        if AC_TRY_COMMAND([${CC-cc} -o conftest.${ac_objext} $CFLAGS $MOZ_DEBUG_FLAGS -c conftest.${ac_ext} 1>&2]) &&
+           AC_TRY_COMMAND([${CC-cc} -o conftest${ac_exeext} $LDFLAGS $MOZ_DEBUG_FLAGS -Wl,--gc-sections conftest.${ac_objext} $LIBS 1>&2]) &&
+           test -s conftest${ac_exeext} -a -s conftest.${ac_objext}; then
+            if test "`$PYTHON "$_topsrcdir"/build/autoconf/check_debug_ranges.py conftest.${ac_objext} conftest.${ac_ext}`" = \
+                    "`$PYTHON "$_topsrcdir"/build/autoconf/check_debug_ranges.py conftest${ac_exeext} conftest.${ac_ext}`"; then
+                GC_SECTIONS_BREAKS_DEBUG_RANGES=no
+            else
+                GC_SECTIONS_BREAKS_DEBUG_RANGES=yes
+            fi
+        else
+             dnl We really don't expect to get here, but just in case
+             GC_SECTIONS_BREAKS_DEBUG_RANGES="no, but it's broken in some other way"
+        fi
+        rm -rf conftest*])
+    if test "$GC_SECTIONS_BREAKS_DEBUG_RANGES" = no; then
+        DSO_LDOPTS="$DSO_LDOPTS -Wl,--gc-sections"
+    fi
+fi
+
 ])
-
new file mode 100644
--- /dev/null
+++ b/js/src/build/autoconf/expandlibs.m4
@@ -0,0 +1,56 @@
+AC_DEFUN([MOZ_EXPAND_LIBS],
+[
+dnl ========================================================
+dnl =
+dnl = Check what kind of list files are supported by the
+dnl = linker
+dnl =
+dnl ========================================================
+
+AC_CACHE_CHECK(what kind of list files are supported by the linker,
+    EXPAND_LIBS_LIST_STYLE,
+    [echo "int main() {return 0;}" > conftest.${ac_ext}
+     if AC_TRY_COMMAND(${CC-cc} -o conftest.${OBJ_SUFFIX} -c $CFLAGS $CPPFLAGS conftest.${ac_ext} 1>&5) && test -s conftest.${OBJ_SUFFIX}; then
+         echo "INPUT(conftest.${OBJ_SUFFIX})" > conftest.list
+         if AC_TRY_COMMAND(${CC-cc} -o conftest${ac_exeext} $LDFLAGS conftest.list $LIBS 1>&5) && test -s conftest${ac_exeext}; then
+             EXPAND_LIBS_LIST_STYLE=linkerscript
+         else
+             echo "conftest.${OBJ_SUFFIX}" > conftest.list
+             if AC_TRY_COMMAND(${CC-cc} -o conftest${ac_exeext} $LDFLAGS @conftest.list $LIBS 1>&5) && test -s conftest${ac_exeext}; then
+                 EXPAND_LIBS_LIST_STYLE=list
+             else
+                 EXPAND_LIBS_LIST_STYLE=none
+             fi
+         fi
+     else
+         dnl We really don't expect to get here, but just in case
+         AC_ERROR([couldn't compile a simple C file])
+     fi
+     rm -rf conftest*])
+
+LIBS_DESC_SUFFIX=desc
+AC_SUBST(LIBS_DESC_SUFFIX)
+AC_SUBST(EXPAND_LIBS_LIST_STYLE)
+
+if test "$GCC_USE_GNU_LD"; then
+    AC_CACHE_CHECK(what kind of ordering can be done with the linker,
+        EXPAND_LIBS_ORDER_STYLE,
+        [> conftest.order
+         _SAVE_LDFLAGS="$LDFLAGS"
+         LDFLAGS="${LDFLAGS} -Wl,--section-ordering-file,conftest.order"
+         AC_TRY_LINK([], [],
+             EXPAND_LIBS_ORDER_STYLE=section-ordering-file,
+             EXPAND_LIBS_ORDER_STYLE=)
+         LDFLAGS="$_SAVE_LDFLAGS"
+         if test -z "$EXPAND_LIBS_ORDER_STYLE"; then
+             if AC_TRY_COMMAND(${CC-cc} ${DSO_LDOPTS} ${LDFLAGS} -o ${DLL_PREFIX}conftest${DLL_SUFFIX} -Wl,--verbose 2> /dev/null | sed -n '/^===/,/^===/p' | grep '\.text'); then
+                 EXPAND_LIBS_ORDER_STYLE=linkerscript
+             else
+                 EXPAND_LIBS_ORDER_STYLE=none
+             fi
+             rm -f ${DLL_PREFIX}conftest${DLL_SUFFIX}
+         fi])
+fi
+AC_SUBST(EXPAND_LIBS_ORDER_STYLE)
+
+])
--- a/js/src/config/config.mk
+++ b/js/src/config/config.mk
@@ -784,18 +784,22 @@ OPTIMIZE_JARS_CMD = $(PYTHON) $(call cor
 CREATE_PRECOMPLETE_CMD = $(PYTHON) $(call core_abspath,$(topsrcdir)/config/createprecomplete.py)
 
 EXPAND_LIBS = $(PYTHON) -I$(DEPTH)/config $(topsrcdir)/config/expandlibs.py
 EXPAND_LIBS_EXEC = $(PYTHON) $(topsrcdir)/config/pythonpath.py -I$(DEPTH)/config $(topsrcdir)/config/expandlibs_exec.py
 EXPAND_LIBS_GEN = $(PYTHON) $(topsrcdir)/config/pythonpath.py -I$(DEPTH)/config $(topsrcdir)/config/expandlibs_gen.py
 EXPAND_AR = $(EXPAND_LIBS_EXEC) --extract -- $(AR)
 EXPAND_CC = $(EXPAND_LIBS_EXEC) --uselist -- $(CC)
 EXPAND_CCC = $(EXPAND_LIBS_EXEC) --uselist -- $(CCC)
-EXPAND_LD = $(EXPAND_LIBS_EXEC) --uselist $(if $(REORDER),--reorder $(REORDER))-- $(LD)
-EXPAND_MKSHLIB = $(EXPAND_LIBS_EXEC) --uselist $(if $(REORDER),--reorder $(REORDER))-- $(MKSHLIB)
+EXPAND_LD = $(EXPAND_LIBS_EXEC) --uselist -- $(LD)
+EXPAND_MKSHLIB_ARGS = --uselist
+ifdef SYMBOL_ORDER
+EXPAND_MKSHLIB_ARGS += --symbol-order $(SYMBOL_ORDER)
+endif
+EXPAND_MKSHLIB = $(EXPAND_LIBS_EXEC) $(EXPAND_MKSHLIB_ARGS) -- $(MKSHLIB)
 
 ifdef STDCXX_COMPAT
 ifneq ($(OS_ARCH),Darwin)
 CHECK_STDCXX = objdump -p $(1) | grep -e 'GLIBCXX_3\.4\.\(9\|[1-9][0-9]\)' > /dev/null && echo "TEST-UNEXPECTED-FAIL | | We don't want these libstdc++ symbols to be used:" && objdump -T $(1) | grep -e 'GLIBCXX_3\.4\.\(9\|[1-9][0-9]\)' && exit 1 || exit 0
 endif
 endif
 
 # autoconf.mk sets OBJ_SUFFIX to an error to avoid use before including
--- a/js/src/config/expandlibs_config.py.in
+++ b/js/src/config/expandlibs_config.py.in
@@ -49,8 +49,10 @@ AR_EXTRACT = "@AR_EXTRACT@".replace('$(A
 DLL_PREFIX = "@DLL_PREFIX@"
 LIB_PREFIX = "@LIB_PREFIX@"
 OBJ_SUFFIX = normalize_suffix("@OBJ_SUFFIX@")
 LIB_SUFFIX = normalize_suffix("@LIB_SUFFIX@")
 DLL_SUFFIX = normalize_suffix("@DLL_SUFFIX@")
 IMPORT_LIB_SUFFIX = normalize_suffix("@IMPORT_LIB_SUFFIX@")
 LIBS_DESC_SUFFIX = normalize_suffix("@LIBS_DESC_SUFFIX@")
 EXPAND_LIBS_LIST_STYLE = "@EXPAND_LIBS_LIST_STYLE@"
+EXPAND_LIBS_ORDER_STYLE = "@EXPAND_LIBS_ORDER_STYLE@"
+LD_PRINT_ICF_SECTIONS = "@LD_PRINT_ICF_SECTIONS@"
--- a/js/src/config/expandlibs_exec.py
+++ b/js/src/config/expandlibs_exec.py
@@ -44,29 +44,41 @@ from static libraries (or use those list
 
 With the --uselist argument (useful for e.g. $(CC)), it replaces all object
 files with a list file. This can be used to avoid limitations in the length
 of a command line. The kind of list file format used depends on the
 EXPAND_LIBS_LIST_STYLE variable: 'list' for MSVC style lists (@file.list)
 or 'linkerscript' for GNU ld linker scripts.
 See https://bugzilla.mozilla.org/show_bug.cgi?id=584474#c59 for more details.
 
-With the --reorder argument, followed by a file name, it will reorder the
-object files from the command line according to the order given in the file.
-Implies --extract.
+With the --symbol-order argument, followed by a file name, it will add the
+relevant linker options to change the order in which the linker puts the
+symbols appear in the resulting binary. Only works for ELF targets.
 '''
 from __future__ import with_statement
 import sys
 import os
 from expandlibs import ExpandArgs, relativize, isObject
 import expandlibs_config as conf
 from optparse import OptionParser
 import subprocess
 import tempfile
 import shutil
+import subprocess
+import re
+
+# The are the insert points for a GNU ld linker script, assuming a more
+# or less "standard" default linker script. This is not a dict because
+# order is important.
+SECTION_INSERT_BEFORE = [
+  ('.text', '.fini'),
+  ('.rodata', '.rodata1'),
+  ('.data.rel.ro', '.dynamic'),
+  ('.data', '.data1'),
+]
 
 class ExpandArgsMore(ExpandArgs):
     ''' Meant to be used as 'with ExpandArgsMore(args) as ...: '''
     def __enter__(self):
         self.tmp = []
         return self
         
     def __exit__(self, type, value, tb):
@@ -114,61 +126,203 @@ class ExpandArgsMore(ExpandArgs):
         fd, tmp = tempfile.mkstemp(suffix=".list",dir=os.curdir)
         if conf.EXPAND_LIBS_LIST_STYLE == "linkerscript":
             content = ["INPUT(%s)\n" % obj for obj in objs]
             ref = tmp
         elif conf.EXPAND_LIBS_LIST_STYLE == "list":
             content = ["%s\n" % obj for obj in objs]
             ref = "@" + tmp
         else:
+            os.close(fd)
             os.remove(tmp)
             return
         self.tmp.append(tmp)
         f = os.fdopen(fd, "w")
         f.writelines(content)
         f.close()
         idx = self.index(objs[0])
         newlist = self[0:idx] + [ref] + [item for item in self[idx:] if item not in objs]
         self[0:] = newlist
 
-    def reorder(self, order_list):
-        '''Given a list of file names without OBJ_SUFFIX, rearrange self
-        so that the object file names it contains are ordered according to
-        that list.
-        '''
-        objs = [o for o in self if isObject(o)]
-        if not objs: return
-        idx = self.index(objs[0])
-        # Keep everything before the first object, then the ordered objects,
-        # then any other objects, then any non-objects after the first object
-        objnames = dict([(os.path.splitext(os.path.basename(o))[0], o) for o in objs])
-        self[0:] = self[0:idx] + [objnames[o] for o in order_list if o in objnames] + \
-                   [o for o in objs if os.path.splitext(os.path.basename(o))[0] not in order_list] + \
-                   [x for x in self[idx:] if not isObject(x)]
+    def _getFoldedSections(self):
+        '''Returns a dict about folded sections.
+        When section A and B are folded into section C, the dict contains:
+        { 'A': 'C',
+          'B': 'C',
+          'C': ['A', 'B'] }'''
+        if not conf.LD_PRINT_ICF_SECTIONS:
+            return {}
+
+        proc = subprocess.Popen(self + [conf.LD_PRINT_ICF_SECTIONS], stdout = subprocess.PIPE, stderr = subprocess.PIPE)
+        (stdout, stderr) = proc.communicate()
+        result = {}
+        # gold's --print-icf-sections output looks like the following:
+        # ld: ICF folding section '.section' in file 'file.o'into '.section' in file 'file.o'
+        # In terms of words, chances are this will change in the future,
+        # especially considering "into" is misplaced. Splitting on quotes
+        # seems safer.
+        for l in stderr.split('\n'):
+            quoted = l.split("'")
+            if len(quoted) > 5 and quoted[1] != quoted[5]:
+                result[quoted[1]] = quoted[5]
+                if quoted[5] in result:
+                    result[quoted[5]].append(quoted[1])
+                else:
+                    result[quoted[5]] = [quoted[1]]
+        return result
+
+    def _getOrderedSections(self, ordered_symbols):
+        '''Given an ordered list of symbols, returns the corresponding list
+        of sections following the order.'''
+        if not conf.EXPAND_LIBS_ORDER_STYLE in ['linkerscript', 'section-ordering-file']:
+            raise Exception('EXPAND_LIBS_ORDER_STYLE "%s" is not supported' % conf.EXPAND_LIBS_ORDER_STYLE)
+        finder = SectionFinder([arg for arg in self if isObject(arg) or os.path.splitext(arg)[1] == conf.LIB_SUFFIX])
+        folded = self._getFoldedSections()
+        sections = set()
+        ordered_sections = []
+        for symbol in ordered_symbols:
+            symbol_sections = finder.getSections(symbol)
+            all_symbol_sections = []
+            for section in symbol_sections:
+                if section in folded:
+                    if isinstance(folded[section], str):
+                        section = folded[section]
+                    all_symbol_sections.append(section)
+                    all_symbol_sections.extend(folded[section])
+                else:
+                    all_symbol_sections.append(section)
+            for section in all_symbol_sections:
+                if not section in sections:
+                    ordered_sections.append(section)
+                    sections.add(section)
+        return ordered_sections
+
+    def orderSymbols(self, order):
+        '''Given a file containing a list of symbols, adds the appropriate
+        argument to make the linker put the symbols in that order.'''
+        with open(order) as file:
+            sections = self._getOrderedSections([l.strip() for l in file.readlines() if l.strip()])
+        split_sections = {}
+        linked_sections = [s[0] for s in SECTION_INSERT_BEFORE]
+        for s in sections:
+            for linked_section in linked_sections:
+                if s.startswith(linked_section):
+                    if linked_section in split_sections:
+                        split_sections[linked_section].append(s)
+                    else:
+                        split_sections[linked_section] = [s]
+                    break
+        content = []
+        # Order is important
+        linked_sections = [s for s in linked_sections if s in split_sections]
 
+        if conf.EXPAND_LIBS_ORDER_STYLE == 'section-ordering-file':
+            option = '-Wl,--section-ordering-file,%s'
+            content = sections
+            for linked_section in linked_sections:
+                content.extend(split_sections[linked_section])
+                content.append('%s.*' % linked_section)
+                content.append(linked_section)
+
+        elif conf.EXPAND_LIBS_ORDER_STYLE == 'linkerscript':
+            option = '-Wl,-T,%s'
+            section_insert_before = dict(SECTION_INSERT_BEFORE)
+            for linked_section in linked_sections:
+                content.append('SECTIONS {')
+                content.append('  %s : {' % linked_section)
+                content.extend('    *(%s)' % s for s in split_sections[linked_section])
+                content.append('  }')
+                content.append('}')
+                content.append('INSERT BEFORE %s' % section_insert_before[linked_section])
+        else:
+            raise Exception('EXPAND_LIBS_ORDER_STYLE "%s" is not supported' % conf.EXPAND_LIBS_ORDER_STYLE)
+
+        fd, tmp = tempfile.mkstemp(dir=os.curdir)
+        f = os.fdopen(fd, "w")
+        f.write('\n'.join(content)+'\n')
+        f.close()
+        self.tmp.append(tmp)
+        self.append(option % tmp)
+
+class SectionFinder(object):
+    '''Instances of this class allow to map symbol names to sections in
+    object files.'''
+
+    def __init__(self, objs):
+        '''Creates an instance, given a list of object files.'''
+        if not conf.EXPAND_LIBS_ORDER_STYLE in ['linkerscript', 'section-ordering-file']:
+            raise Exception('EXPAND_LIBS_ORDER_STYLE "%s" is not supported' % conf.EXPAND_LIBS_ORDER_STYLE)
+        self.mapping = {}
+        for obj in objs:
+            if not isObject(obj) and os.path.splitext(obj)[1] != conf.LIB_SUFFIX:
+                raise Exception('%s is not an object nor a static library' % obj)
+            for symbol, section in SectionFinder._getSymbols(obj):
+                sym = SectionFinder._normalize(symbol)
+                if sym in self.mapping:
+                    if not section in self.mapping[sym]:
+                        self.mapping[sym].append(section)
+                else:
+                    self.mapping[sym] = [section]
+
+    def getSections(self, symbol):
+        '''Given a symbol, returns a list of sections containing it or the
+        corresponding thunks. When the given symbol is a thunk, returns the
+        list of sections containing its corresponding normal symbol and the
+        other thunks for that symbol.'''
+        sym = SectionFinder._normalize(symbol)
+        if sym in self.mapping:
+            return self.mapping[sym]
+        return []
+
+    @staticmethod
+    def _normalize(symbol):
+        '''For normal symbols, return the given symbol. For thunks, return
+        the corresponding normal symbol.'''
+        if re.match('^_ZThn[0-9]+_', symbol):
+            return re.sub('^_ZThn[0-9]+_', '_Z', symbol)
+        return symbol
+
+    @staticmethod
+    def _getSymbols(obj):
+        '''Returns a list of (symbol, section) contained in the given object
+        file.'''
+        proc = subprocess.Popen(['objdump', '-t', obj], stdout = subprocess.PIPE, stderr = subprocess.PIPE)
+        (stdout, stderr) = proc.communicate()
+        syms = []
+        for line in stdout.splitlines():
+            # Each line has the following format:
+            # <addr> [lgu!][w ][C ][W ][Ii ][dD ][FfO ] <section>\t<length> <symbol>
+            tmp = line.split(' ',1)
+            # This gives us ["<addr>", "[lgu!][w ][C ][W ][Ii ][dD ][FfO ] <section>\t<length> <symbol>"]
+            # We only need to consider cases where "<section>\t<length> <symbol>" is present,
+            # and where the [FfO] flag is either F (function) or O (object).
+            if len(tmp) > 1 and len(tmp[1]) > 6 and tmp[1][6] in ['O', 'F']:
+                tmp = tmp[1][8:].split()
+                # That gives us ["<section>","<length>", "<symbol>"]
+                syms.append((tmp[-1], tmp[0]))
+        return syms
 
 def main():
     parser = OptionParser()
     parser.add_option("--extract", action="store_true", dest="extract",
         help="when a library has no descriptor file, extract it first, when possible")
     parser.add_option("--uselist", action="store_true", dest="uselist",
         help="use a list file for objects when executing a command")
     parser.add_option("--verbose", action="store_true", dest="verbose",
         help="display executed command and temporary files content")
-    parser.add_option("--reorder", dest="reorder",
-        help="reorder the objects according to the given list", metavar="FILE")
+    parser.add_option("--symbol-order", dest="symbol_order", metavar="FILE",
+        help="use the given list of symbols to order symbols in the resulting binary when using with a linker")
 
     (options, args) = parser.parse_args()
 
     with ExpandArgsMore(args) as args:
-        if options.extract or options.reorder:
+        if options.extract:
             args.extract()
-        if options.reorder:
-            with open(options.reorder) as file:
-                args.reorder([l.strip() for l in file.readlines()])
+        if options.symbol_order:
+            args.orderSymbols(options.symbol_order)
         if options.uselist:
             args.makelist()
 
         if options.verbose:
             print >>sys.stderr, "Executing: " + " ".join(args)
             for tmp in [f for f in args.tmp if os.path.isfile(f)]:
                 print >>sys.stderr, tmp + ":"
                 with open(tmp) as file:
--- a/js/src/configure.in
+++ b/js/src/configure.in
@@ -4824,47 +4824,17 @@ else
    _MOZ_RTTI_FLAGS=$_MOZ_RTTI_FLAGS_OFF
 fi
 
 AC_SUBST(_MOZ_RTTI_FLAGS_ON)
 
 AC_DEFINE(CPP_THROW_NEW, [throw()])
 AC_LANG_C
 
-dnl ========================================================
-dnl =
-dnl = Check what kind of list files are supported by the
-dnl = linker
-dnl =
-dnl ========================================================
-
-AC_CACHE_CHECK(what kind of list files are supported by the linker,
-    EXPAND_LIBS_LIST_STYLE,
-    [echo "int main() {return 0;}" > conftest.${ac_ext}
-     if AC_TRY_COMMAND(${CC-cc} -o conftest.${OBJ_SUFFIX} -c $CFLAGS $CPPFLAGS conftest.${ac_ext} 1>&2) && test -s conftest.${OBJ_SUFFIX}; then
-         echo "INPUT(conftest.${OBJ_SUFFIX})" > conftest.list
-         if AC_TRY_COMMAND(${CC-cc} -o conftest${ac_exeext} $LDFLAGS conftest.list $LIBS 1>&2) && test -s conftest${ac_exeext}; then
-             EXPAND_LIBS_LIST_STYLE=linkerscript
-         else
-             echo "conftest.${OBJ_SUFFIX}" > conftest.list
-             if AC_TRY_COMMAND(${CC-cc} -o conftest${ac_exeext} $LDFLAGS @conftest.list $LIBS 1>&2) && test -s conftest${ac_exeext}; then
-                 EXPAND_LIBS_LIST_STYLE=list
-             else
-                 EXPAND_LIBS_LIST_STYLE=none
-             fi
-         fi
-     else
-         dnl We really don't expect to get here, but just in case
-         AC_ERROR([couldn't compile a simple C file])
-     fi
-     rm -rf conftest*])
-
-LIBS_DESC_SUFFIX=desc
-AC_SUBST(LIBS_DESC_SUFFIX)
-AC_SUBST(EXPAND_LIBS_LIST_STYLE)
+MOZ_EXPAND_LIBS
 
 dnl ========================================================
 dnl =
 dnl = Build depencency options
 dnl =
 dnl ========================================================
 MOZ_ARG_HEADER(Build dependencies)
 
--- a/js/src/frontend/TokenStream.cpp
+++ b/js/src/frontend/TokenStream.cpp
@@ -1317,22 +1317,28 @@ TokenStream::checkForKeyword(const jscha
                 *ttp = kw->tokentype;
                 *topp = (JSOp) kw->op;
                 return true;
             }
             return ReportCompileErrorNumber(cx, this, NULL, JSREPORT_ERROR,
                                             JSMSG_RESERVED_ID, kw->chars);
         }
 
+        /* The let keyword is reserved on <1.7 */
+        if (kw->tokentype == TOK_LET) {
+            return ReportCompileErrorNumber(cx, this, NULL, JSREPORT_ERROR,
+                                            JSMSG_RESERVED_ID, kw->chars);
+        }
+
         /*
          * The keyword is not in this version. Treat it as an identifier,
-         * unless it is let or yield which we treat as TOK_STRICT_RESERVED by
-         * falling through to the code below (ES5 forbids them in strict mode).
+         * unless it is yield which we treat as TOK_STRICT_RESERVED by
+         * falling through to the code below (ES5 forbids it in strict mode).
          */
-        if (kw->tokentype != TOK_LET && kw->tokentype != TOK_YIELD)
+        if (kw->tokentype != TOK_YIELD)
             return true;
     }
 
     /* Strict reserved word. */
     if (isStrictMode())
         return ReportStrictModeError(cx, this, NULL, NULL, JSMSG_RESERVED_ID, kw->chars);
     return ReportCompileErrorNumber(cx, this, NULL, JSREPORT_STRICT | JSREPORT_WARNING,
                                     JSMSG_RESERVED_ID, kw->chars);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/bug734196.js
@@ -0,0 +1,4 @@
+var x = new ArrayBuffer(2);
+gczeal(4);
+actual = [].concat(x).toString();
+var count2 = countHeap();
--- a/js/src/jit-test/tests/basic/regress-bug720680.js
+++ b/js/src/jit-test/tests/basic/regress-bug720680.js
@@ -4,12 +4,12 @@ eval("\
 function TimeFromYear( y ) {}\
 addTestCase( -2208988800000 );\
 function addTestCase( t ) {\
   var start = TimeFromYear((addTestCase(addTestCase << t, 0)));\
     new TestCase( \
                   SECTION,\
                   '(new Date('+d+')).getUTCDay()',\
                   WeekDay((d)),\
-                  (new Date(let ({ stop } = 'properties.length' )('/ab[c\\\n]/'))).getUTCDay() \
+                  (new Date(foo ({ stop } = 'properties.length' )('/ab[c\\\n]/'))).getUTCDay() \
                 );\
 }\
 ");
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/jaeger/bug732423.js
@@ -0,0 +1,23 @@
+function testXML(x, y) {
+    for (var i = 0; i < 60; i++) {
+        x[y]();
+        x[y];
+    }
+}
+testXML(<x/>, "elements");
+
+var o = {
+    res: 0,
+    f: function() { this.res += 3; },
+    __noSuchMethod__: function() { this.res += 5; }
+};
+
+function testNoSuchMethod(x, y) {
+    for (var i = 0; i < 60; i++) {
+        x[y]();
+    }
+}
+
+testNoSuchMethod(o, "f");
+testNoSuchMethod(o, "g");
+assertEq(o.res, 480);
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -836,17 +836,17 @@ JSRuntime::init(uint32_t maxbytes)
     if (!(atomsCompartment = this->new_<JSCompartment>(this)) ||
         !atomsCompartment->init(NULL) ||
         !compartments.append(atomsCompartment)) {
         Foreground::delete_(atomsCompartment);
         return false;
     }
 
     atomsCompartment->isSystemCompartment = true;
-    atomsCompartment->setGCLastBytes(8192, GC_NORMAL);
+    atomsCompartment->setGCLastBytes(8192, 8192, GC_NORMAL);
 
     if (!js_InitAtomState(this))
         return false;
 
     if (!InitRuntimeNumberState(this))
         return false;
 
     dtoaState = js_NewDtoaState();
@@ -1009,18 +1009,16 @@ static void
 StartRequest(JSContext *cx)
 {
     JSRuntime *rt = cx->runtime;
     JS_ASSERT(rt->onOwnerThread());
 
     if (rt->requestDepth) {
         rt->requestDepth++;
     } else {
-        AutoLockGC lock(rt);
-
         /* Indicate that a request is running. */
         rt->requestDepth = 1;
 
         if (rt->activityCallback)
             rt->activityCallback(rt->activityCallbackArg, true);
     }
 }
 
@@ -1029,20 +1027,16 @@ StopRequest(JSContext *cx)
 {
     JSRuntime *rt = cx->runtime;
     JS_ASSERT(rt->onOwnerThread());
     JS_ASSERT(rt->requestDepth != 0);
     if (rt->requestDepth != 1) {
         rt->requestDepth--;
     } else {
         rt->conservativeGC.updateForRequestEnd(rt->suspendCount);
-
-        /* Lock before clearing to interlock with ClaimScope, in jslock.c. */
-        AutoLockGC lock(rt);
-
         rt->requestDepth = 0;
 
         if (rt->activityCallback)
             rt->activityCallback(rt->activityCallbackArg, false);
     }
 }
 #endif /* JS_THREADSAFE */
 
@@ -1302,24 +1296,22 @@ SetOptionsCommon(JSContext *cx, unsigned
     cx->setCompileOptions(newcopts);
     cx->updateJITEnabled();
     return oldopts;
 }
 
 JS_PUBLIC_API(uint32_t)
 JS_SetOptions(JSContext *cx, uint32_t options)
 {
-    AutoLockGC lock(cx->runtime);
     return SetOptionsCommon(cx, options);
 }
 
 JS_PUBLIC_API(uint32_t)
 JS_ToggleOptions(JSContext *cx, uint32_t options)
 {
-    AutoLockGC lock(cx->runtime);
     unsigned oldopts = cx->allOptions();
     unsigned newopts = oldopts ^ options;
     return SetOptionsCommon(cx, newopts);
 }
 
 JS_PUBLIC_API(void)
 JS_SetJitHardening(JSRuntime *rt, JSBool enabled)
 {
@@ -2198,16 +2190,28 @@ JS_ComputeThis(JSContext *cx, jsval *vp)
     AssertNoGC(cx);
     assertSameCompartment(cx, JSValueArray(vp, 2));
     CallReceiver call = CallReceiverFromVp(vp);
     if (!BoxNonStrictThis(cx, call))
         return JSVAL_NULL;
     return call.thisv();
 }
 
+JS_PUBLIC_API(void)
+JS_MallocInCompartment(JSCompartment *comp, size_t nbytes)
+{
+    comp->mallocInCompartment(nbytes);
+}
+
+JS_PUBLIC_API(void)
+JS_FreeInCompartment(JSCompartment *comp, size_t nbytes)
+{
+    comp->freeInCompartment(nbytes);
+}
+
 JS_PUBLIC_API(void *)
 JS_malloc(JSContext *cx, size_t nbytes)
 {
     AssertNoGC(cx);
     CHECK_REQUEST(cx);
     return cx->malloc_(nbytes);
 }
 
@@ -2879,17 +2883,16 @@ JS_IsAboutToBeFinalized(void *thing)
     return IsAboutToBeFinalized(t);
 }
 
 JS_PUBLIC_API(void)
 JS_SetGCParameter(JSRuntime *rt, JSGCParamKey key, uint32_t value)
 {
     switch (key) {
       case JSGC_MAX_BYTES: {
-        AutoLockGC lock(rt);
         JS_ASSERT(value >= rt->gcBytes);
         rt->gcMaxBytes = value;
         break;
       }
       case JSGC_MAX_MALLOC_BYTES:
         rt->setGCMaxMallocBytes(value);
         break;
       case JSGC_SLICE_TIME_BUDGET:
@@ -4263,17 +4266,17 @@ prop_iter_trace(JSTracer *trc, JSObject 
     if (obj->getSlot(JSSLOT_ITER_INDEX).toInt32() < 0) {
         /*
          * Native case: just mark the next property to visit. We don't need a
          * barrier here because the pointer is updated via setPrivate, which
          * always takes a barrier.
          */
         Shape *tmp = (Shape *)pdata;
         MarkShapeUnbarriered(trc, &tmp, "prop iter shape");
-        JS_ASSERT(tmp == pdata);
+        obj->setPrivateUnbarriered(tmp);
     } else {
         /* Non-native case: mark each id in the JSIdArray private. */
         JSIdArray *ida = (JSIdArray *) pdata;
         MarkIdRange(trc, ida->length, ida->vector, "prop iter");
     }
 }
 
 static Class prop_iter_class = {
@@ -5506,30 +5509,18 @@ JS_SetOperationCallback(JSContext *cx, J
 
 JS_PUBLIC_API(JSOperationCallback)
 JS_GetOperationCallback(JSContext *cx)
 {
     return cx->operationCallback;
 }
 
 JS_PUBLIC_API(void)
-JS_TriggerOperationCallback(JSContext *cx)
-{
-#ifdef JS_THREADSAFE
-    AutoLockGC lock(cx->runtime);
-#endif
-    cx->runtime->triggerOperationCallback();
-}
-
-JS_PUBLIC_API(void)
-JS_TriggerRuntimeOperationCallback(JSRuntime *rt)
-{
-#ifdef JS_THREADSAFE
-    AutoLockGC lock(rt);
-#endif
+JS_TriggerOperationCallback(JSRuntime *rt)
+{
     rt->triggerOperationCallback();
 }
 
 JS_PUBLIC_API(JSBool)
 JS_IsRunning(JSContext *cx)
 {
     StackFrame *fp = cx->maybefp();
     while (fp && fp->isDummyFrame())
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -2874,16 +2874,22 @@ JS_THIS(JSContext *cx, jsval *vp)
  * or vice versa.  Either use the provided this value with this macro, or
  * compute the boxed this value using those.
  *
  * N.B. constructors must not use JS_THIS_VALUE, as no 'this' object has been
  * created.
  */
 #define JS_THIS_VALUE(cx,vp)    ((vp)[1])
 
+extern JS_PUBLIC_API(void)
+JS_MallocInCompartment(JSCompartment *comp, size_t nbytes);
+
+extern JS_PUBLIC_API(void)
+JS_FreeInCompartment(JSCompartment *comp, size_t nbytes);
+
 extern JS_PUBLIC_API(void *)
 JS_malloc(JSContext *cx, size_t nbytes);
 
 extern JS_PUBLIC_API(void *)
 JS_realloc(JSContext *cx, void *p, size_t nbytes);
 
 extern JS_PUBLIC_API(void)
 JS_free(JSContext *cx, void *p);
@@ -4493,42 +4499,35 @@ Call(JSContext *cx, jsval thisv, JSObjec
 
 } /* namespace JS */
 
 JS_BEGIN_EXTERN_C
 #endif /* __cplusplus */
 
 /*
  * These functions allow setting an operation callback that will be called
- * from the thread the context is associated with some time after any thread
- * triggered the callback using JS_TriggerOperationCallback(cx).
+ * from the JS thread some time after any thread triggered the callback using
+ * JS_TriggerOperationCallback(rt).
  *
- * In a threadsafe build the engine internally triggers operation callbacks
- * under certain circumstances (i.e. GC and title transfer) to force the
- * context to yield its current request, which the engine always
- * automatically does immediately prior to calling the callback function.
- * The embedding should thus not rely on callbacks being triggered through
- * the external API only.
+ * To schedule the GC and for other activities the engine internally triggers
+ * operation callbacks. The embedding should thus not rely on callbacks being
+ * triggered through the external API only.
  *
  * Important note: Additional callbacks can occur inside the callback handler
  * if it re-enters the JS engine. The embedding must ensure that the callback
  * is disconnected before attempting such re-entry.
  */
-
 extern JS_PUBLIC_API(JSOperationCallback)
 JS_SetOperationCallback(JSContext *cx, JSOperationCallback callback);
 
 extern JS_PUBLIC_API(JSOperationCallback)
 JS_GetOperationCallback(JSContext *cx);
 
 extern JS_PUBLIC_API(void)
-JS_TriggerOperationCallback(JSContext *cx);
-
-extern JS_PUBLIC_API(void)
-JS_TriggerRuntimeOperationCallback(JSRuntime *rt);
+JS_TriggerOperationCallback(JSRuntime *rt);
 
 extern JS_PUBLIC_API(JSBool)
 JS_IsRunning(JSContext *cx);
 
 /*
  * Saving and restoring frame chains.
  *
  * These two functions are used to set aside cx's call stack while that stack
--- a/js/src/jsbool.cpp
+++ b/js/src/jsbool.cpp
@@ -181,23 +181,16 @@ js_InitBooleanClass(JSContext *cx, JSObj
 }
 
 JSString *
 js_BooleanToString(JSContext *cx, JSBool b)
 {
     return cx->runtime->atomState.booleanAtoms[b ? 1 : 0];
 }
 
-/* This function implements E-262-3 section 9.8, toString. */
-bool
-js::BooleanToStringBuffer(JSContext *cx, JSBool b, StringBuffer &sb)
-{
-    return b ? sb.append("true") : sb.append("false");
-}
-
 namespace js {
 
 bool
 BooleanGetPrimitiveValueSlow(JSContext *cx, JSObject &obj, Value *vp)
 {
     JS_ASSERT(ObjectClassIs(obj, ESClass_Boolean, cx));
     JS_ASSERT(obj.isProxy());
 
--- a/js/src/jsbool.h
+++ b/js/src/jsbool.h
@@ -49,19 +49,16 @@
 extern JSObject *
 js_InitBooleanClass(JSContext *cx, JSObject *obj);
 
 extern JSString *
 js_BooleanToString(JSContext *cx, JSBool b);
 
 namespace js {
 
-extern bool
-BooleanToStringBuffer(JSContext *cx, JSBool b, StringBuffer &sb);
-
 inline bool
 BooleanGetPrimitiveValue(JSContext *cx, JSObject &obj, Value *vp);
 
 } /* namespace js */
 
 extern JSBool
 js_ValueToBoolean(const js::Value &v);
 
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -113,17 +113,17 @@ JSRuntime::sizeOfExcludingThis(JSMallocS
 
     if (stackCommitted)
         *stackCommitted = stackSpace.sizeOfCommitted();
 
     if (gcMarkerSize)
         *gcMarkerSize = gcMarker.sizeOfExcludingThis(mallocSizeOf);
 }
 
-JS_FRIEND_API(void)
+void
 JSRuntime::triggerOperationCallback()
 {
     /*
      * Use JS_ATOMIC_SET in the hope that it ensures the write will become
      * immediately visible to other processors polling the flag.
      */
     JS_ATOMIC_SET(&interrupt, 1);
 }
@@ -245,59 +245,58 @@ js_DestroyContext(JSContext *cx, JSDestr
              * JSCONTEXT_DESTROY callback is not allowed to fail and must
              * return true.
              */
             DebugOnly<JSBool> callbackStatus = cxCallback(cx, JSCONTEXT_DESTROY);
             JS_ASSERT(callbackStatus);
         }
     }
 
-    JS_LOCK_GC(rt);
     JS_REMOVE_LINK(&cx->link);
     bool last = !rt->hasContexts();
-    if (last || mode == JSDCM_FORCE_GC || mode == JSDCM_MAYBE_GC) {
+    if (last) {
         JS_ASSERT(!rt->gcRunning);
 
 #ifdef JS_THREADSAFE
-        rt->gcHelperThread.waitBackgroundSweepEnd();
+        {
+            AutoLockGC lock(rt);
+            rt->gcHelperThread.waitBackgroundSweepEnd();
+        }
 #endif
-        JS_UNLOCK_GC(rt);
-
-        if (last) {
-            /*
-             * Dump remaining type inference results first. This printing
-             * depends on atoms still existing.
-             */
-            {
-                AutoLockGC lock(rt);
-                for (CompartmentsIter c(rt); !c.done(); c.next())
-                    c->types.print(cx, false);
-            }
-
-            /* Unpin all common atoms before final GC. */
-            js_FinishCommonAtoms(cx);
+        
+        /*
+         * Dump remaining type inference results first. This printing
+         * depends on atoms still existing.
+         */
+        for (CompartmentsIter c(rt); !c.done(); c.next())
+            c->types.print(cx, false);
 
-            /* Clear debugging state to remove GC roots. */
-            for (CompartmentsIter c(rt); !c.done(); c.next())
-                c->clearTraps(cx);
-            JS_ClearAllWatchPoints(cx);
-
-            GC(cx, NULL, GC_NORMAL, gcreason::LAST_CONTEXT);
+        /* Unpin all common atoms before final GC. */
+        js_FinishCommonAtoms(cx);
+        
+        /* Clear debugging state to remove GC roots. */
+        for (CompartmentsIter c(rt); !c.done(); c.next())
+            c->clearTraps(cx);
+        JS_ClearAllWatchPoints(cx);
+        
+        GC(cx, NULL, GC_NORMAL, gcreason::LAST_CONTEXT);
+    } else if (mode == JSDCM_FORCE_GC) {
+        JS_ASSERT(!rt->gcRunning);
+        GC(cx, NULL, GC_NORMAL, gcreason::DESTROY_CONTEXT);
+    } else if (mode == JSDCM_MAYBE_GC) {
+        JS_ASSERT(!rt->gcRunning);
+        JS_MaybeGC(cx);
+    }
 
-        } else if (mode == JSDCM_FORCE_GC) {
-            GC(cx, NULL, GC_NORMAL, gcreason::DESTROY_CONTEXT);
-        } else if (mode == JSDCM_MAYBE_GC) {
-            JS_MaybeGC(cx);
-        }
-        JS_LOCK_GC(rt);
+#ifdef JS_THREADSAFE
+    {
+        AutoLockGC lock(rt);
+        rt->gcHelperThread.waitBackgroundSweepEnd();
     }
-#ifdef JS_THREADSAFE
-    rt->gcHelperThread.waitBackgroundSweepEnd();
 #endif
-    JS_UNLOCK_GC(rt);
     Foreground::delete_(cx);
 }
 
 namespace js {
 
 bool
 AutoResolving::alreadyStartedSlow() const
 {
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -681,17 +681,17 @@ struct JSRuntime : js::RuntimeFriendFiel
      * to recove some memory or to report an error. Failures in malloc and
      * calloc are signaled by p == null and p == reinterpret_cast<void *>(1).
      * Other values of p mean a realloc failure.
      *
      * The function must be called outside the GC lock.
      */
     JS_FRIEND_API(void *) onOutOfMemory(void *p, size_t nbytes, JSContext *cx);
 
-    JS_FRIEND_API(void) triggerOperationCallback();
+    void triggerOperationCallback();
 
     void setJitHardening(bool enabled);
     bool getJitHardening() const {
         return jitHardening;
     }
 
     void sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf, size_t *normal, size_t *temporary,
                              size_t *regexpCode, size_t *stackCommitted, size_t *gcMarker);
@@ -1245,16 +1245,50 @@ class AutoXMLRooter : private AutoGCRoot
 #ifdef JS_THREADSAFE
 # define JS_LOCK_GC(rt)    PR_Lock((rt)->gcLock)
 # define JS_UNLOCK_GC(rt)  PR_Unlock((rt)->gcLock)
 #else
 # define JS_LOCK_GC(rt)
 # define JS_UNLOCK_GC(rt)
 #endif
 
+class AutoLockGC
+{
+  public:
+    explicit AutoLockGC(JSRuntime *rt = NULL
+                        MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
+      : runtime(rt)
+    {
+        MOZ_GUARD_OBJECT_NOTIFIER_INIT;
+        if (rt)
+            JS_LOCK_GC(rt);
+    }
+
+    ~AutoLockGC()
+    {
+        if (runtime)
+            JS_UNLOCK_GC(runtime);
+    }
+
+    bool locked() const {
+        return !!runtime;
+    }
+
+    void lock(JSRuntime *rt) {
+        JS_ASSERT(rt);
+        JS_ASSERT(!runtime);
+        runtime = rt;
+        JS_LOCK_GC(rt);
+    }
+
+  private:
+    JSRuntime *runtime;
+    MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
+};
+
 class AutoUnlockGC {
   private:
     JSRuntime *rt;
     JS_DECL_USE_GUARD_OBJECT_NOTIFIER
 
   public:
     explicit AutoUnlockGC(JSRuntime *rt
                           JS_GUARD_OBJECT_NOTIFIER_PARAM)
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -70,27 +70,29 @@ using namespace js;
 using namespace js::gc;
 
 JSCompartment::JSCompartment(JSRuntime *rt)
   : rt(rt),
     principals(NULL),
     needsBarrier_(false),
     gcBytes(0),
     gcTriggerBytes(0),
-    gcLastBytes(0),
     hold(false),
     typeLifoAlloc(TYPE_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
     data(NULL),
     active(false),
 #ifdef JS_METHODJIT
     jaegerCompartment_(NULL),
 #endif
     regExps(rt),
     propertyTree(thisForCtor()),
     emptyTypeObject(NULL),
+    gcMallocAndFreeBytes(0),
+    gcTriggerMallocAndFreeBytes(0),
+    gcMallocBytes(0),
     debugModeBits(rt->debugMode ? DebugFromC : 0),
     mathCache(NULL),
     watchpointMap(NULL)
 {
     PodArrayZero(evalCache);
     setGCMaxMallocBytes(rt->gcMaxMallocBytes * 0.9);
 }
 
--- a/js/src/jscompartment.h
+++ b/js/src/jscompartment.h
@@ -200,17 +200,16 @@ struct JSCompartment
 
     js::GCMarker *barrierTracer() {
         JS_ASSERT(needsBarrier_);
         return &rt->gcMarker;
     }
 
     size_t                       gcBytes;
     size_t                       gcTriggerBytes;
-    size_t                       gcLastBytes;
     size_t                       gcMaxMallocBytes;
 
     bool                         hold;
     bool                         isSystemCompartment;
 
     /*
      * Pool for analysis and intermediate type information in this compartment.
      * Cleared on every GC, unless the GC happens during analysis (indicated
@@ -262,24 +261,16 @@ struct JSCompartment
     size_t sizeOfShapeTable(JSMallocSizeOfFun mallocSizeOf);
     void sizeOfTypeInferenceData(JS::TypeInferenceSizes *stats, JSMallocSizeOfFun mallocSizeOf);
 
     /*
      * Shared scope property tree, and arena-pool for allocating its nodes.
      */
     js::PropertyTree             propertyTree;
 
-#ifdef DEBUG
-    /* Property metering. */
-    unsigned                     livePropTreeNodes;
-    unsigned                     totalPropTreeNodes;
-    unsigned                     propTreeKidsChunks;
-    unsigned                     liveDictModeNodes;
-#endif
-
     /* Set of all unowned base shapes in the compartment. */
     js::BaseShapeSet             baseShapes;
     void sweepBaseShapeTable(JSContext *cx);
 
     /* Set of initial shapes in the compartment. */
     js::InitialShapeSet          initialShapes;
     void sweepInitialShapeTable(JSContext *cx);
 
@@ -293,26 +284,36 @@ struct JSCompartment
     /* Get the default 'new' type for objects with a NULL prototype. */
     inline js::types::TypeObject *getEmptyType(JSContext *cx);
 
     js::types::TypeObject *getLazyType(JSContext *cx, JSObject *proto);
 
     /* Cache to speed up object creation. */
     js::NewObjectCache           newObjectCache;
 
+    /*
+     * Keeps track of the total number of malloc bytes connected to a
+     * compartment's GC things. This counter should be used in preference to
+     * gcMallocBytes. These counters affect collection in the same way as
+     * gcBytes and gcTriggerBytes.
+     */
+    size_t                       gcMallocAndFreeBytes;
+    size_t                       gcTriggerMallocAndFreeBytes;
+
   private:
+    /*
+     * Malloc counter to measure memory pressure for GC scheduling. It runs from
+     * gcMaxMallocBytes down to zero. This counter should be used only when it's
+     * not possible to know the size of a free.
+     */
+    ptrdiff_t                    gcMallocBytes;
+
     enum { DebugFromC = 1, DebugFromJS = 2 };
 
-    unsigned                        debugModeBits;  // see debugMode() below
-    
-    /*
-     * Malloc counter to measure memory pressure for GC scheduling. It runs
-     * from gcMaxMallocBytes down to zero.
-     */
-    volatile ptrdiff_t           gcMallocBytes;
+    unsigned                     debugModeBits;  // see debugMode() below
 
   public:
     js::NativeIterCache          nativeIterCache;
 
     typedef js::Maybe<js::ToSourceCache> LazyToSourceCache;
     LazyToSourceCache            toSourceCache;
 
     js::ScriptFilenameTable      scriptFilenameTable;
@@ -335,31 +336,40 @@ struct JSCompartment
     bool wrap(JSContext *cx, js::PropertyDescriptor *desc);
     bool wrap(JSContext *cx, js::AutoIdVector &props);
 
     void markTypes(JSTracer *trc);
     void discardJitCode(JSContext *cx);
     void sweep(JSContext *cx, bool releaseTypes);
     void purge();
 
-    void setGCLastBytes(size_t lastBytes, js::JSGCInvocationKind gckind);
+    void setGCLastBytes(size_t lastBytes, size_t lastMallocBytes, js::JSGCInvocationKind gckind);
     void reduceGCTriggerBytes(size_t amount);
-    
+
     void resetGCMallocBytes();
     void setGCMaxMallocBytes(size_t value);
     void updateMallocCounter(size_t nbytes) {
         ptrdiff_t oldCount = gcMallocBytes;
         ptrdiff_t newCount = oldCount - ptrdiff_t(nbytes);
         gcMallocBytes = newCount;
         if (JS_UNLIKELY(newCount <= 0 && oldCount > 0))
             onTooMuchMalloc();
     }
-    
+
     void onTooMuchMalloc();
 
+    void mallocInCompartment(size_t nbytes) {
+        gcMallocAndFreeBytes += nbytes;
+    }
+
+    void freeInCompartment(size_t nbytes) {
+        JS_ASSERT(gcMallocAndFreeBytes >= nbytes);
+        gcMallocAndFreeBytes -= nbytes;
+    }
+
     js::DtoaCache dtoaCache;
 
   private:
     js::MathCache                *mathCache;
 
     js::MathCache *allocMathCache(JSContext *cx);
 
     /*
--- a/js/src/jsfriendapi.cpp
+++ b/js/src/jsfriendapi.cpp
@@ -604,39 +604,16 @@ js::DumpHeapComplete(JSRuntime *rt, FILE
     dtrc.visited.finish();
     fflush(dtrc.output);
 }
 
 #endif
 
 namespace js {
 
-/* static */ void
-AutoLockGC::LockGC(JSRuntime *rt)
-{
-    JS_ASSERT(rt);
-    JS_LOCK_GC(rt);
-}
-
-/* static */ void
-AutoLockGC::UnlockGC(JSRuntime *rt)
-{
-    JS_ASSERT(rt);
-    JS_UNLOCK_GC(rt);
-}
-
-void
-AutoLockGC::lock(JSRuntime *rt)
-{
-    JS_ASSERT(rt);
-    JS_ASSERT(!runtime);
-    runtime = rt;
-    JS_LOCK_GC(rt);
-}
-
 JS_FRIEND_API(const JSStructuredCloneCallbacks *)
 GetContextStructuredCloneCallbacks(JSContext *cx)
 {
     return cx->runtime->structuredCloneCallbacks;
 }
 
 JS_FRIEND_API(JSVersion)
 VersionSetXML(JSVersion version, bool enable)
@@ -669,22 +646,16 @@ GetOwnerThread(const JSContext *cx)
 }
 
 JS_FRIEND_API(unsigned)
 GetContextOutstandingRequests(const JSContext *cx)
 {
     return cx->outstandingRequests;
 }
 
-JS_FRIEND_API(PRLock *)
-GetRuntimeGCLock(const JSRuntime *rt)
-{
-    return rt->gcLock;
-}
-
 AutoSkipConservativeScan::AutoSkipConservativeScan(JSContext *cx
                                                    MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
   : context(cx)
 {
     MOZ_GUARD_OBJECT_NOTIFIER_INIT;
 
     JSRuntime *rt = context->runtime;
     JS_ASSERT(rt->requestDepth >= 1);
@@ -721,22 +692,16 @@ SetActivityCallback(JSRuntime *rt, Activ
 }
 
 JS_FRIEND_API(bool)
 IsContextRunningJS(JSContext *cx)
 {
     return !cx->stack.empty();
 }
 
-JS_FRIEND_API(void)
-TriggerOperationCallback(JSRuntime *rt)
-{
-    rt->triggerOperationCallback();
-}
-
 JS_FRIEND_API(const CompartmentVector&)
 GetRuntimeCompartments(JSRuntime *rt)
 {
     return rt->compartments;
 }
 
 JS_FRIEND_API(size_t)
 SizeOfJSContext()
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -182,18 +182,16 @@ extern JS_FRIEND_API(bool)
 JS_DefineFunctionsWithHelp(JSContext *cx, JSObject *obj, const JSFunctionSpecWithHelp *fs);
 
 #endif
 
 JS_END_EXTERN_C
 
 #ifdef __cplusplus
 
-struct PRLock;
-
 namespace js {
 
 struct ContextFriendFields {
     JSRuntime *const    runtime;
 
     ContextFriendFields(JSRuntime *rt)
       : runtime(rt) { }
 
@@ -568,19 +566,16 @@ GetPCCountScriptContents(JSContext *cx, 
 
 #ifdef JS_THREADSAFE
 JS_FRIEND_API(void *)
 GetOwnerThread(const JSContext *cx);
 
 JS_FRIEND_API(unsigned)
 GetContextOutstandingRequests(const JSContext *cx);
 
-JS_FRIEND_API(PRLock *)
-GetRuntimeGCLock(const JSRuntime *rt);
-
 class JS_FRIEND_API(AutoSkipConservativeScan)
 {
   public:
     AutoSkipConservativeScan(JSContext *cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
     ~AutoSkipConservativeScan();
 
   private:
     JSContext *context;
@@ -595,72 +590,36 @@ JS_FRIEND_API(bool)
 HasUnrootedGlobal(const JSContext *cx);
 
 typedef void
 (* ActivityCallback)(void *arg, JSBool active);
 
 /*
  * Sets a callback that is run whenever the runtime goes idle - the
  * last active request ceases - and begins activity - when it was
- * idle and a request begins. Note: The callback is called under the
- * GC lock.
+ * idle and a request begins.
  */
 JS_FRIEND_API(void)
 SetActivityCallback(JSRuntime *rt, ActivityCallback cb, void *arg);
 
-class JS_FRIEND_API(AutoLockGC)
-{
-  public:
-    explicit AutoLockGC(JSRuntime *rt = NULL
-                        MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
-      : runtime(rt)
-    {
-        MOZ_GUARD_OBJECT_NOTIFIER_INIT;
-        if (rt)
-            LockGC(rt);
-    }
-
-    ~AutoLockGC()
-    {
-        if (runtime)
-            UnlockGC(runtime);
-    }
-
-    bool locked() const {
-        return !!runtime;
-    }
-    void lock(JSRuntime *rt);
-
-  private:
-    static void LockGC(JSRuntime *rt);
-    static void UnlockGC(JSRuntime *rt);
-
-    JSRuntime *runtime;
-    MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
-};
-
 extern JS_FRIEND_API(const JSStructuredCloneCallbacks *)
 GetContextStructuredCloneCallbacks(JSContext *cx);
 
 extern JS_FRIEND_API(JSVersion)
 VersionSetXML(JSVersion version, bool enable);
 
 extern JS_FRIEND_API(bool)
 CanCallContextDebugHandler(JSContext *cx);
 
 extern JS_FRIEND_API(JSTrapStatus)
 CallContextDebugHandler(JSContext *cx, JSScript *script, jsbytecode *bc, Value *rval);
 
 extern JS_FRIEND_API(bool)
 IsContextRunningJS(JSContext *cx);
 
-/* Must be called with GC lock taken. */
-extern JS_FRIEND_API(void)
-TriggerOperationCallback(JSRuntime *rt);
-
 class SystemAllocPolicy;
 typedef Vector<JSCompartment*, 0, SystemAllocPolicy> CompartmentVector;
 extern JS_FRIEND_API(const CompartmentVector&)
 GetRuntimeCompartments(JSRuntime *rt);
 
 extern JS_FRIEND_API(size_t)
 SizeOfJSContext();
 
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -1303,53 +1303,30 @@ js_AddGCThingRoot(JSContext *cx, void **
     if (!ok)
         JS_ReportOutOfMemory(cx);
     return ok;
 }
 
 JS_FRIEND_API(JSBool)
 js_AddRootRT(JSRuntime *rt, jsval *vp, const char *name)
 {
-    /*
-     * Due to the long-standing, but now removed, use of rt->gcLock across the
-     * bulk of js::GC, API users have come to depend on JS_AddRoot etc. locking
-     * properly with a racing GC, without calling JS_AddRoot from a request.
-     * We have to preserve API compatibility here, now that we avoid holding
-     * rt->gcLock across the mark phase (including the root hashtable mark).
-     */
-    AutoLockGC lock(rt);
-
     return !!rt->gcRootsHash.put((void *)vp,
                                  RootInfo(name, JS_GC_ROOT_VALUE_PTR));
 }
 
 JS_FRIEND_API(JSBool)
 js_AddGCThingRootRT(JSRuntime *rt, void **rp, const char *name)
 {
-    /*
-     * Due to the long-standing, but now removed, use of rt->gcLock across the
-     * bulk of js::GC, API users have come to depend on JS_AddRoot etc. locking
-     * properly with a racing GC, without calling JS_AddRoot from a request.
-     * We have to preserve API compatibility here, now that we avoid holding
-     * rt->gcLock across the mark phase (including the root hashtable mark).
-     */
-    AutoLockGC lock(rt);
-
     return !!rt->gcRootsHash.put((void *)rp,
                                  RootInfo(name, JS_GC_ROOT_GCTHING_PTR));
 }
 
 JS_FRIEND_API(JSBool)
 js_RemoveRoot(JSRuntime *rt, void *rp)
 {
-    /*
-     * Due to the JS_RemoveRootRT API, we may be called outside of a request.
-     * Same synchronization drill as above in js_AddRoot.
-     */
-    AutoLockGC lock(rt);
     rt->gcRootsHash.remove(rp);
     rt->gcPoke = JS_TRUE;
     return JS_TRUE;
 }
 
 typedef RootedValueMap::Range RootRange;
 typedef RootedValueMap::Entry RootEntry;
 typedef RootedValueMap::Enum RootEnum;
@@ -1399,41 +1376,45 @@ js_DumpNamedRoots(JSRuntime *rt,
     }
 }
 
 #endif /* DEBUG */
 
 uint32_t
 js_MapGCRoots(JSRuntime *rt, JSGCRootMapFun map, void *data)
 {
-    AutoLockGC lock(rt);
     int ct = 0;
     for (RootEnum e(rt->gcRootsHash); !e.empty(); e.popFront()) {
         RootEntry &entry = e.front();
 
         ct++;
         int mapflags = map(entry.key, entry.value.type, entry.value.name, data);
 
         if (mapflags & JS_MAP_GCROOT_REMOVE)
             e.removeFront();
         if (mapflags & JS_MAP_GCROOT_STOP)
             break;
     }
 
     return ct;
 }
 
-void
-JSCompartment::setGCLastBytes(size_t lastBytes, JSGCInvocationKind gckind)
+static size_t
+ComputeTriggerBytes(size_t lastBytes, size_t maxBytes, JSGCInvocationKind gckind)
 {
-    gcLastBytes = lastBytes;
-
     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));
+    return size_t(Min(float(maxBytes), trigger));
+}
+
+void
+JSCompartment::setGCLastBytes(size_t lastBytes, size_t lastMallocBytes, JSGCInvocationKind gckind)
+{
+    gcTriggerBytes = ComputeTriggerBytes(lastBytes, rt->gcMaxBytes, gckind);
+    gcTriggerMallocAndFreeBytes = ComputeTriggerBytes(lastMallocBytes, SIZE_MAX, gckind);
 }
 
 void
 JSCompartment::reduceGCTriggerBytes(size_t amount)
 {
     JS_ASSERT(amount > 0);
     JS_ASSERT(gcTriggerBytes - amount >= 0);
     if (gcTriggerBytes - amount < GC_ALLOCATION_THRESHOLD * GC_HEAP_GROWTH_FACTOR)
@@ -1785,35 +1766,31 @@ js_GetGCThingTraceKind(void *thing)
 }
 
 JSBool
 js_LockGCThingRT(JSRuntime *rt, void *thing)
 {
     if (!thing)
         return true;
 
-    AutoLockGC lock(rt);
     if (GCLocks::Ptr p = rt->gcLocksHash.lookupWithDefault(thing, 0)) {
         p->value++;
         return true;
     }
 
     return false;
 }
 
 void
 js_UnlockGCThingRT(JSRuntime *rt, void *thing)
 {
     if (!thing)
         return;
 
-    AutoLockGC lock(rt);
-    GCLocks::Ptr p = rt->gcLocksHash.lookup(thing);
-
-    if (p) {
+    if (GCLocks::Ptr p = rt->gcLocksHash.lookup(thing)) {
         rt->gcPoke = true;
         if (--p->value == 0)
             rt->gcLocksHash.remove(p);
     }
 }
 
 namespace js {
 
@@ -2407,16 +2384,17 @@ MarkRuntime(JSTracer *trc, bool useSaved
             gcmarker->startBufferingGrayRoots();
             (*op)(trc, rt->gcGrayRootsData);
             gcmarker->endBufferingGrayRoots();
         } else {
             (*op)(trc, rt->gcGrayRootsData);
         }
     }
 }
+
 void
 TriggerGC(JSRuntime *rt, gcreason::Reason reason)
 {
     JS_ASSERT(rt->onOwnerThread());
 
     if (rt->gcRunning || rt->gcIsNeeded)
         return;
 
@@ -2453,17 +2431,17 @@ TriggerCompartmentGC(JSCompartment *comp
 
     /*
      * Trigger the GC when it is safe to call an operation callback on any
      * thread.
      */
     rt->gcIsNeeded = true;
     rt->gcTriggerCompartment = comp;
     rt->gcTriggerReason = reason;
-    comp->rt->triggerOperationCallback();
+    rt->triggerOperationCallback();
 }
 
 void
 MaybeGC(JSContext *cx)
 {
     JSRuntime *rt = cx->runtime;
     JS_ASSERT(rt->onOwnerThread());
 
@@ -2482,16 +2460,21 @@ MaybeGC(JSContext *cx)
     if (comp->gcBytes > 8192 &&
         comp->gcBytes >= 3 * (comp->gcTriggerBytes / 4) &&
         rt->gcIncrementalState == NO_INCREMENTAL)
     {
         GCSlice(cx, NULL, GC_NORMAL, gcreason::MAYBEGC);
         return;
     }
 
+    if (comp->gcMallocAndFreeBytes >= comp->gcTriggerMallocAndFreeBytes) {
+        GCSlice(cx, comp, GC_NORMAL, gcreason::MAYBEGC);
+        return;
+    }
+
     /*
      * Access to the counters and, on 32 bit, setting gcNextFullGCTime below
      * is not atomic and a race condition could trigger or suppress the GC. We
      * tolerate this.
      */
     int64_t now = PRMJ_Now();
     if (rt->gcNextFullGCTime && rt->gcNextFullGCTime <= now) {
         if (rt->gcChunkAllocationSinceLastGC ||
@@ -3267,17 +3250,17 @@ SweepPhase(JSContext *cx, JSGCInvocation
 
     {
         gcstats::AutoPhase ap(rt->gcStats, gcstats::PHASE_XPCONNECT);
         if (rt->gcFinalizeCallback)
             rt->gcFinalizeCallback(cx, JSFINALIZE_END);
     }
 
     for (CompartmentsIter c(rt); !c.done(); c.next())
-        c->setGCLastBytes(c->gcBytes, gckind);
+        c->setGCLastBytes(c->gcBytes, c->gcMallocAndFreeBytes, gckind);
 }
 
 /* Perform mark-and-sweep GC. If comp is set, we perform a single-compartment GC. */
 static void
 MarkAndSweep(JSContext *cx, JSGCInvocationKind gckind)
 {
     JSRuntime *rt = cx->runtime;
 
@@ -3920,17 +3903,17 @@ NewCompartment(JSContext *cx, JSPrincipa
         // Any compartment with the trusted principals -- and there can be
         // multiple -- is a system compartment.
         compartment->isSystemCompartment = principals && rt->trustedPrincipals() == principals;
         if (principals) {
             compartment->principals = principals;
             JSPRINCIPALS_HOLD(cx, principals);
         }
 
-        compartment->setGCLastBytes(8192, GC_NORMAL);
+        compartment->setGCLastBytes(8192, 8192, GC_NORMAL);
 
         /*
          * Before reporting the OOM condition, |lock| needs to be cleaned up,
          * hence the scoping.
          */
         {
             AutoLockGC lock(rt);
 
@@ -4501,34 +4484,32 @@ ReleaseScriptPCCounters(JSContext *cx)
     cx->delete_(rt->scriptPCCounters);
     rt->scriptPCCounters = NULL;
 }
 
 JS_FRIEND_API(void)
 StartPCCountProfiling(JSContext *cx)
 {
     JSRuntime *rt = cx->runtime;
-    AutoLockGC lock(rt);
 
     if (rt->profilingScripts)
         return;
 
     if (rt->scriptPCCounters)
         ReleaseScriptPCCounters(cx);
 
     ReleaseAllJITCode(cx);
 
     rt->profilingScripts = true;
 }
 
 JS_FRIEND_API(void)
 StopPCCountProfiling(JSContext *cx)
 {
     JSRuntime *rt = cx->runtime;
-    AutoLockGC lock(rt);
 
     if (!rt->profilingScripts)
         return;
     JS_ASSERT(!rt->scriptPCCounters);
 
     ReleaseAllJITCode(cx);
 
     ScriptOpcodeCountsVector *vec = cx->new_<ScriptOpcodeCountsVector>(SystemAllocPolicy());
@@ -4551,17 +4532,16 @@ StopPCCountProfiling(JSContext *cx)
     rt->profilingScripts = false;
     rt->scriptPCCounters = vec;
 }
 
 JS_FRIEND_API(void)
 PurgePCCounts(JSContext *cx)
 {
     JSRuntime *rt = cx->runtime;
-    AutoLockGC lock(rt);
 
     if (!rt->scriptPCCounters)
         return;
     JS_ASSERT(!rt->profilingScripts);
 
     ReleaseScriptPCCounters(cx);
 }
 
@@ -4570,16 +4550,20 @@ PurgePCCounts(JSContext *cx)
 JS_PUBLIC_API(void)
 JS_IterateCompartments(JSRuntime *rt, void *data,
                        JSIterateCompartmentCallback compartmentCallback)
 {
     JS_ASSERT(!rt->gcRunning);
 
     AutoLockGC lock(rt);
     AutoHeapSession session(rt);
+#ifdef JS_THREADSAFE
+    rt->gcHelperThread.waitBackgroundSweepOrAllocEnd();
+#endif
+    AutoUnlockGC unlock(rt);
 
     for (CompartmentsIter c(rt); !c.done(); c.next())
         (*compartmentCallback)(rt, data, c);
 }
 
 #if JS_HAS_XML_SUPPORT
 extern size_t sE4XObjectsCreated;
 
--- a/js/src/jsinfer.cpp
+++ b/js/src/jsinfer.cpp
@@ -2174,27 +2174,16 @@ TypeCompartment::nukeTypes(JSContext *cx
      * inconsistent state.
      */
     JS_ASSERT(pendingNukeTypes);
     if (pendingRecompiles) {
         cx->free_(pendingRecompiles);
         pendingRecompiles = NULL;
     }
 
-    /*
-     * We may or may not be under the GC. In either case don't allocate, and
-     * acquire the GC lock so we can update inferenceEnabled for all contexts.
-     */
-
-#ifdef JS_THREADSAFE
-    AutoLockGC maybeLock;
-    if (!cx->runtime->gcRunning)
-        maybeLock.lock(cx->runtime);
-#endif
-
     inferenceEnabled = false;
 
     /* Update the cached inferenceEnabled bit in all contexts. */
     for (ContextIter acx(cx->runtime); !acx.done(); acx.next())
         acx->setCompartment(acx->compartment);
 
 #ifdef JS_METHODJIT
 
--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -2556,55 +2556,27 @@ BEGIN_CASE(JSOP_SETMETHOD)
         goto error;
 
     regs.sp[-2] = regs.sp[-1];
     regs.sp--;
 }
 END_CASE(JSOP_SETPROP)
 
 BEGIN_CASE(JSOP_GETELEM)
+BEGIN_CASE(JSOP_CALLELEM)
 {
     Value &lref = regs.sp[-2];
     Value &rref = regs.sp[-1];
-    if (!GetElementOperation(cx, lref, rref, &regs.sp[-2]))
+    if (!GetElementOperation(cx, op, lref, rref, &regs.sp[-2]))
         goto error;
     TypeScript::Monitor(cx, script, regs.pc, regs.sp[-2]);
     regs.sp--;
 }
 END_CASE(JSOP_GETELEM)
 
-BEGIN_CASE(JSOP_CALLELEM)
-{
-    /* Find the object on which to look for |this|'s properties. */
-    Value thisv = regs.sp[-2];
-    JSObject *thisObj = ValuePropertyBearer(cx, regs.fp(), thisv, -2);
-    if (!thisObj)
-        goto error;
-
-    /* Fetch index and convert it to id suitable for use with obj. */
-    jsid id;
-    FETCH_ELEMENT_ID(thisObj, -1, id);
-
-    /* Get the method. */
-    if (!js_GetMethod(cx, thisObj, id, JSGET_NO_METHOD_BARRIER, &regs.sp[-2]))
-        goto error;
-
-#if JS_HAS_NO_SUCH_METHOD
-    if (JS_UNLIKELY(regs.sp[-2].isPrimitive()) && thisv.isObject()) {
-        if (!OnUnknownMethod(cx, &thisv.toObject(), regs.sp[-1], regs.sp - 2))
-            goto error;
-    }
-#endif
-
-    regs.sp--;
-
-    TypeScript::Monitor(cx, script, regs.pc, regs.sp[-1]);
-}
-END_CASE(JSOP_CALLELEM)
-
 BEGIN_CASE(JSOP_SETELEM)
 {
     JSObject *obj;
     FETCH_OBJECT(cx, -3, obj);
     jsid id;
     FETCH_ELEMENT_ID(obj, -2, id);
     Value &value = regs.sp[-1];
     if (!SetObjectElementOperation(cx, obj, id, value, script->strictModeCode))
--- a/js/src/jsinterpinlines.h
+++ b/js/src/jsinterpinlines.h
@@ -697,18 +697,27 @@ ToIdOperation(JSContext *cx, const Value
         return false;
 
     if (!res->isInt32())
         types::TypeScript::MonitorUnknown(cx);
     return true;
 }
 
 static JS_ALWAYS_INLINE bool
-GetObjectElementOperation(JSContext *cx, JSObject *obj, const Value &rref, Value *res)
+GetObjectElementOperation(JSContext *cx, JSOp op, JSObject *obj, const Value &rref, Value *res)
 {
+#if JS_HAS_XML_SUPPORT
+    if (op == JSOP_CALLELEM && JS_UNLIKELY(obj->isXML())) {
+        jsid id;
+        if (!FetchElementId(cx, obj, rref, id, res))
+            return false;
+        return js_GetXMLMethod(cx, obj, id, res);
+    }
+#endif
+
     uint32_t index;
     if (IsDefinitelyIndex(rref, &index)) {
         do {
             if (obj->isDenseArray()) {
                 if (index < obj->getDenseArrayInitializedLength()) {
                     *res = obj->getDenseArrayElement(index);
                     if (!res->isMagic())
                         break;
@@ -748,18 +757,20 @@ GetObjectElementOperation(JSContext *cx,
         }
     }
 
     assertSameCompartment(cx, *res);
     return true;
 }
 
 static JS_ALWAYS_INLINE bool
-GetElementOperation(JSContext *cx, const Value &lref, const Value &rref, Value *res)
+GetElementOperation(JSContext *cx, JSOp op, const Value &lref, const Value &rref, Value *res)
 {
+    JS_ASSERT(op == JSOP_GETELEM || op == JSOP_CALLELEM);
+
     if (lref.isString() && rref.isInt32()) {
         JSString *str = lref.toString();
         int32_t i = rref.toInt32();
         if (size_t(i) < str->length()) {
             str = cx->runtime->staticStrings.getUnitStringForElement(cx, str, size_t(i));
             if (!str)
                 return false;
             res->setString(str);
@@ -771,20 +782,30 @@ GetElementOperation(JSContext *cx, const
         if (rref.isInt32() && size_t(rref.toInt32()) < cx->regs().fp()->numActualArgs()) {
             *res = cx->regs().fp()->canonicalActualArg(rref.toInt32());
             return true;
         }
         types::MarkArgumentsCreated(cx, cx->fp()->script());
         JS_ASSERT(!lref.isMagic(JS_LAZY_ARGUMENTS));
     }
 
+    bool isObject = lref.isObject();
     JSObject *obj = ValueToObject(cx, lref);
     if (!obj)
         return false;
-    return GetObjectElementOperation(cx, obj, rref, res);
+    if (!GetObjectElementOperation(cx, op, obj, rref, res))
+        return false;
+
+#if JS_HAS_NO_SUCH_METHOD
+    if (op == JSOP_CALLELEM && JS_UNLIKELY(res->isPrimitive()) && isObject) {
+        if (!OnUnknownMethod(cx, obj, rref, res))
+            return false;
+    }
+#endif
+    return true;
 }
 
 static JS_ALWAYS_INLINE bool
 SetObjectElementOperation(JSContext *cx, JSObject *obj, jsid id, const Value &value, bool strict)
 {
     types::TypeScript::MonitorAssign(cx, obj, id);
 
     do {
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -769,16 +769,17 @@ struct JSObject : public js::ObjectImpl
 
     inline js::GlobalObject &global() const;
 
     /* Private data accessors. */
 
     inline bool hasPrivate() const;
     inline void *getPrivate() const;
     inline void setPrivate(void *data);
+    inline void setPrivateUnbarriered(void *data);
     inline void initPrivate(void *data);
 
     /* Access private data for an object with a known number of fixed slots. */
     inline void *getPrivate(size_t nfixed) const;
 
     /* N.B. Infallible: NULL means 'no principal', not an error. */
     inline JSPrincipals *principals(JSContext *cx);
 
--- a/js/src/jsobjinlines.h
+++ b/js/src/jsobjinlines.h
@@ -117,16 +117,23 @@ JSObject::setPrivate(void *data)
     void **pprivate = &privateRef(numFixedSlots());
 
     privateWriteBarrierPre(pprivate);
     *pprivate = data;
     privateWriteBarrierPost(pprivate);
 }
 
 inline void
+JSObject::setPrivateUnbarriered(void *data)
+{
+    void **pprivate = &privateRef(numFixedSlots());
+    *pprivate = data;
+}
+
+inline void
 JSObject::initPrivate(void *data)
 {
     privateRef(numFixedSlots()) = data;
 }
 
 inline bool
 JSObject::enumerate(JSContext *cx, JSIterateOp iterop, js::Value *statep, jsid *idp)
 {
--- a/js/src/jsstr.cpp
+++ b/js/src/jsstr.cpp
@@ -3260,36 +3260,16 @@ js::ToStringSlow(JSContext *cx, const Va
     } else if (v.isNull()) {
         str = cx->runtime->atomState.nullAtom;
     } else {
         str = cx->runtime->atomState.typeAtoms[JSTYPE_VOID];
     }
     return str;
 }
 
-/* This function implements E-262-3 section 9.8, toString. */
-bool
-js::ValueToStringBufferSlow(JSContext *cx, const Value &arg, StringBuffer &sb)
-{
-    Value v = arg;
-    if (!ToPrimitive(cx, JSTYPE_STRING, &v))
-        return false;
-
-    if (v.isString())
-        return sb.append(v.toString());
-    if (v.isNumber())
-        return NumberValueToStringBuffer(cx, v, sb);
-    if (v.isBoolean())
-        return BooleanToStringBuffer(cx, v.toBoolean(), sb);
-    if (v.isNull())
-        return sb.append(cx->runtime->atomState.nullAtom);
-    JS_ASSERT(v.isUndefined());
-    return sb.append(cx->runtime->atomState.typeAtoms[JSTYPE_VOID]);
-}
-
 JS_FRIEND_API(JSString *)
 js_ValueToSource(JSContext *cx, const Value &v)
 {
     JS_CHECK_RECURSION(cx, return NULL);
 
     if (v.isUndefined())
         return cx->runtime->atomState.void0Atom;
     if (v.isString())
--- a/js/src/jstypedarray.cpp
+++ b/js/src/jstypedarray.cpp
@@ -327,17 +327,17 @@ ArrayBuffer::obj_trace(JSTracer *trc, JS
 {
     /*
      * If this object changes, it will get marked via the private data barrier,
      * so it's safe to leave it Unbarriered.
      */
     JSObject *delegate = static_cast<JSObject*>(obj->getPrivate());
     if (delegate) {
         MarkObjectUnbarriered(trc, &delegate, "arraybuffer.delegate");
-        obj->setPrivate(delegate);
+        obj->setPrivateUnbarriered(delegate);
     }
 }
 
 static JSProperty * const PROPERTY_FOUND = reinterpret_cast<JSProperty *>(1);
 
 JSBool
 ArrayBuffer::obj_lookupGeneric(JSContext *cx, JSObject *obj, jsid id,
                                JSObject **objp, JSProperty **propp)
--- a/js/src/methodjit/PolyIC.cpp
+++ b/js/src/methodjit/PolyIC.cpp
@@ -2655,16 +2655,26 @@ ic::GetElement(VMFrame &f, ic::GetElemen
     Value idval = f.regs.sp[-1];
 
     RecompilationMonitor monitor(cx);
 
     JSObject *obj = ValueToObject(cx, f.regs.sp[-2]);
     if (!obj)
         THROW();
 
+#if JS_HAS_XML_SUPPORT
+    // Some XML properties behave differently when accessed in a call vs. normal
+    // context, so we fall back to stubs::GetElem.
+    if (obj->isXML()) {
+        ic->disable(f, "XML object");
+        stubs::GetElem(f);
+        return;
+    }
+#endif
+
     jsid id;
     if (idval.isInt32() && INT_FITS_IN_JSID(idval.toInt32())) {
         id = INT_TO_JSID(idval.toInt32());
     } else {
         if (!js_InternNonIntElementId(cx, obj, idval, &id))
             THROW();
     }
 
@@ -2680,16 +2690,23 @@ ic::GetElement(VMFrame &f, ic::GetElemen
             // If the result can be cached, the value was already retrieved.
             JS_ASSERT(!f.regs.sp[-2].isMagic());
             return;
         }
     }
 
     if (!obj->getGeneric(cx, id, &f.regs.sp[-2]))
         THROW();
+
+#if JS_HAS_NO_SUCH_METHOD
+    if (*f.pc() == JSOP_CALLELEM && JS_UNLIKELY(f.regs.sp[-2].isPrimitive())) {
+        if (!OnUnknownMethod(cx, obj, idval, &f.regs.sp[-2]))
+            THROW();
+    }
+#endif
 }
 
 #define APPLY_STRICTNESS(f, s)                          \
     (FunctionTemplateConditional(s, f<true>, f<false>))
 
 LookupStatus
 SetElementIC::disable(VMFrame &f, const char *reason)
 {
--- a/js/src/methodjit/StubCalls.cpp
+++ b/js/src/methodjit/StubCalls.cpp
@@ -129,89 +129,22 @@ stubs::Name(VMFrame &f)
     if (!NameOperation(f.cx, f.pc(), &rval))
         THROW();
     f.regs.sp[0] = rval;
 }
 
 void JS_FASTCALL
 stubs::GetElem(VMFrame &f)
 {
-    JSContext *cx = f.cx;
-    FrameRegs &regs = f.regs;
-
-    Value &lref = regs.sp[-2];
-    Value &rref = regs.sp[-1];
-    Value &rval = regs.sp[-2];
-    if (lref.isString() && rref.isInt32()) {
-        JSString *str = lref.toString();
-        int32_t i = rref.toInt32();
-        if ((size_t)i < str->length()) {
-            str = f.cx->runtime->staticStrings.getUnitStringForElement(cx, str, (size_t)i);
-            if (!str)
-                THROW();
-            rval.setString(str);
-            return;
-        }
-    }
-
-    if (lref.isMagic(JS_LAZY_ARGUMENTS)) {
-        if (rref.isInt32() && size_t(rref.toInt32()) < regs.fp()->numActualArgs()) {
-            rval = regs.fp()->canonicalActualArg(rref.toInt32());
-            return;
-        }
-        MarkArgumentsCreated(cx, f.script());
-        JS_ASSERT(!lref.isMagic(JS_LAZY_ARGUMENTS));
-    }
-
-    bool isObject = lref.isObject();
-    JSObject *obj = ValueToObject(cx, lref);
-    if (!obj)
-        THROW();
+    Value &lref = f.regs.sp[-2];
+    Value &rref = f.regs.sp[-1];
+    Value &rval = f.regs.sp[-2];
 
-    uint32_t index;
-    if (IsDefinitelyIndex(rref, &index)) {
-        if (obj->isDenseArray()) {
-            if (index < obj->getDenseArrayInitializedLength()) {
-                rval = obj->getDenseArrayElement(index);
-                if (!rval.isMagic())
-                    return;
-            }
-        } else if (obj->isArguments()) {
-            if (obj->asArguments().getElement(index, &rval))
-                return;
-        }
-
-        if (!obj->getElement(cx, index, &rval))
-            THROW();
-    } else {
-        SpecialId special;
-        if (ValueIsSpecial(obj, &rref, &special, cx)) {
-            if (!obj->getSpecial(cx, obj, special, &rval))
-                THROW();
-        } else {
-            JSAtom *name;
-            if (!js_ValueToAtom(cx, rref, &name))
-                THROW();
-
-            if (name->isIndex(&index)) {
-                if (!obj->getElement(cx, index, &rval))
-                    THROW();
-            } else {
-                if (!obj->getProperty(cx, name->asPropertyName(), &rval))
-                    THROW();
-            }
-        }
-    }
-
-#if JS_HAS_NO_SUCH_METHOD
-    if (*f.pc() == JSOP_CALLELEM && JS_UNLIKELY(rval.isPrimitive()) && isObject) {
-        if (!OnUnknownMethod(cx, obj, rref, &rval))
-            THROW();
-    }
-#endif
+    if (!GetElementOperation(f.cx, JSOp(*f.pc()), lref, rref, &rval))
+        THROW();
 }
 
 template<JSBool strict>
 void JS_FASTCALL
 stubs::SetElem(VMFrame &f)
 {
     JSContext *cx = f.cx;
     FrameRegs &regs = f.regs;
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -3129,17 +3129,17 @@ CancelExecution(JSRuntime *rt)
 {
     gCanceled = true;
     if (gExitCode == 0)
         gExitCode = EXITCODE_TIMEOUT;
 #ifdef JS_THREADSAFE
     if (gWorkerThreadPool)
         js::workers::terminateAll(gWorkerThreadPool);
 #endif
-    JS_TriggerRuntimeOperationCallback(rt);
+    JS_TriggerOperationCallback(rt);
 
     static const char msg[] = "Script runs for too long, terminating.\n";
 #if defined(XP_UNIX) && !defined(JS_THREADSAFE)
     /* It is not safe to call fputs from signals. */
     /* Dummy assignment avoids GCC warning on "attribute warn_unused_result" */
     ssize_t dummy = write(2, msg, sizeof(msg) - 1);
     (void)dummy;
 #else
--- a/js/src/shell/jsworkers.cpp
+++ b/js/src/shell/jsworkers.cpp
@@ -822,17 +822,17 @@ class Worker MOZ_FINAL : public WorkerPa
             return false;
         return events.push(event);
     }
 
     void setTerminateFlag() {
         AutoLock hold(lock);
         terminateFlag = true;
         if (current)
-            JS_TriggerOperationCallback(context);
+            JS_TriggerOperationCallback(runtime);
     }
 
     void notifyTerminating() {
         setTerminateFlag();
         WorkerParent::notifyTerminating();
     }
 
     void processOneEvent();
--- a/js/src/tests/ecma_5/misc/future-reserved-words.js
+++ b/js/src/tests/ecma_5/misc/future-reserved-words.js
@@ -16,24 +16,24 @@ print(BUGNUMBER + ": " + summary);
 var futureReservedWords =
   [
    "class",
    // "const", // Mozilla extension enabled even for versionless code
    "enum",
    "export",
    "extends",
    "import",
+   "let",  // Reserved even for versionless code, contrary to ES5 - see bug 730139
    "super",
   ];
 
 var strictFutureReservedWords =
   [
    "implements",
    "interface",
-   "let", // enabled: this file doesn't execute as JS1.7
    "package",
    "private",
    "protected",
    "public",
    "static",
    "yield", // enabled: this file doesn't execute as JS1.7
   ];
 
--- a/js/src/tests/js1_5/LexicalConventions/jstests.list
+++ b/js/src/tests/js1_5/LexicalConventions/jstests.list
@@ -1,5 +1,6 @@
 url-prefix ../../jsreftest.html?test=js1_5/LexicalConventions/
+script let.js
 script lexical-001.js
 script regress-177314.js
 script regress-343675.js
 script regress-469940.js
new file mode 100644
--- /dev/null
+++ b/js/src/tests/js1_5/LexicalConventions/let.js
@@ -0,0 +1,14 @@
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/
+ */
+
+var actual = 'No error';
+try {
+    eval("var let = true");
+} catch (exc) {
+    actual = exc + '';
+}
+
+reportCompare('SyntaxError: let is a reserved identifier', actual, 'ok'); 
+
--- a/js/src/tests/js1_5/Regress/regress-351515.js
+++ b/js/src/tests/js1_5/Regress/regress-351515.js
@@ -42,27 +42,25 @@ var actual = 'No Error';
 var expect = 'No Error';
 
 
 //-----------------------------------------------------------------------------
 test();
 //-----------------------------------------------------------------------------
 
 yield = 1;
-let   = 1;
 
 function test()
 {
   enterFunc ('test');
   printBugNumber(BUGNUMBER);
   printStatus (summary);
  
-  function f(yield, let) { return yield+let; }
+  function f(yield) { return yield; }
 
   var yield = 1;
-  var let = 1;
 
   function yield() {}
 
   reportCompare(expect, actual, summary);
 
   exitFunc ('test');
 }
--- a/js/src/tests/js1_6/extensions/regress-352392.js
+++ b/js/src/tests/js1_6/extensions/regress-352392.js
@@ -47,17 +47,17 @@ test();
 //-----------------------------------------------------------------------------
 
 function test()
 {
   enterFunc ('test');
   printBugNumber(BUGNUMBER);
   printStatus (summary);
 
-  expect = 'SyntaxError: invalid for each loop';
+  expect = 'SyntaxError: let is a reserved identifier';
   try
   {
     var obj = { };
     Object.defineProperty(obj, "y", { get: Array.prototype.map, enumerable: true, configurable: true });
     eval('(function() { for each(let z in obj) { } })()');
   }
   catch(ex)
   {
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -424,17 +424,16 @@ Breakpoint::nextInSite()
 
 Debugger::Debugger(JSContext *cx, JSObject *dbg)
   : object(dbg), uncaughtExceptionHook(NULL), enabled(true),
     frames(cx), scripts(cx), objects(cx), environments(cx)
 {
     assertSameCompartment(cx, dbg);
 
     JSRuntime *rt = cx->runtime;
-    AutoLockGC lock(rt);
     JS_APPEND_LINK(&link, &rt->debuggerList);
     JS_INIT_CLIST(&breakpoints);
 }
 
 Debugger::~Debugger()
 {
     JS_ASSERT(debuggees.empty());
 
@@ -3210,17 +3209,17 @@ DebuggerObject_trace(JSTracer *trc, JSOb
 {
     if (!trc->runtime->gcCurrentCompartment) {
         /*
          * There is a barrier on private pointers, so the Unbarriered marking
          * is okay.
          */
         if (JSObject *referent = (JSObject *) obj->getPrivate()) {
             MarkObjectUnbarriered(trc, &referent, "Debugger.Object referent");
-            obj->setPrivate(referent);
+            obj->setPrivateUnbarriered(referent);
         }
     }
 }
 
 Class DebuggerObject_class = {
     "Object",
     JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS |
     JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUGOBJECT_COUNT),
@@ -3854,17 +3853,17 @@ DebuggerEnv_trace(JSTracer *trc, JSObjec
 {
     if (!trc->runtime->gcCurrentCompartment) {
         /*
          * There is a barrier on private pointers, so the Unbarriered marking
          * is okay.
          */
         if (Env *referent = (JSObject *) obj->getPrivate()) {
             MarkObjectUnbarriered(trc, &referent, "Debugger.Environment referent");
-            obj->setPrivate(referent);
+            obj->setPrivateUnbarriered(referent);
         }
     }
 }
 
 Class DebuggerEnv_class = {
     "Environment",
     JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS |
     JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUGENV_COUNT),
--- a/js/src/vm/StringBuffer-inl.h
+++ b/js/src/vm/StringBuffer-inl.h
@@ -62,23 +62,31 @@ StringBuffer::append(const jschar *begin
 inline bool
 StringBuffer::appendN(const jschar c, size_t n)
 {
     if (!checkLength(cb.length() + n))
         return false;
     return cb.appendN(c, n);
 }
 
+/* ES5 9.8 ToString, appending the result to the string buffer. */
 extern bool
 ValueToStringBufferSlow(JSContext *cx, const Value &v, StringBuffer &sb);
 
 inline bool
 ValueToStringBuffer(JSContext *cx, const Value &v, StringBuffer &sb)
 {
     if (v.isString())
         return sb.append(v.toString());
 
     return ValueToStringBufferSlow(cx, v, sb);
 }
 
+/* ES5 9.8 ToString for booleans, appending the result to the string buffer. */
+inline bool
+BooleanToStringBuffer(JSContext *cx, bool b, StringBuffer &sb)
+{
+    return b ? sb.append("true") : sb.append("false");
+}
+
 }  /* namespace js */
 
 #endif /* StringBuffer_inl_h__ */
--- a/js/src/vm/StringBuffer.cpp
+++ b/js/src/vm/StringBuffer.cpp
@@ -1,16 +1,18 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  *
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "vm/StringBuffer.h"
 
+#include "jsobjinlines.h"
+
 #include "vm/String-inl.h"
 #include "vm/StringBuffer-inl.h"
 
 using namespace js;
 
 jschar *
 StringBuffer::extractWellSized()
 {
@@ -40,18 +42,17 @@ StringBuffer::extractWellSized()
 JSFixedString *
 StringBuffer::finishString()
 {
     JSContext *cx = context();
     if (cb.empty())
         return cx->runtime->atomState.emptyAtom;
 
     size_t length = cb.length();
-    if (!checkLength(length))
-        return NULL;
+    JS_ASSERT(checkLength(length));
 
     JS_STATIC_ASSERT(JSShortString::MAX_SHORT_LENGTH < CharBuffer::InlineLength);
     if (JSShortString::lengthFits(length))
         return NewShortString(cx, cb.begin(), length);
 
     if (!cb.append('\0'))
         return NULL;
 
@@ -73,8 +74,27 @@ StringBuffer::finishAtom()
     size_t length = cb.length();
     if (length == 0)
         return cx->runtime->atomState.emptyAtom;
 
     JSAtom *atom = js_AtomizeChars(cx, cb.begin(), length);
     cb.clear();
     return atom;
 }
+
+bool
+js::ValueToStringBufferSlow(JSContext *cx, const Value &arg, StringBuffer &sb)
+{
+    Value v = arg;
+    if (!ToPrimitive(cx, JSTYPE_STRING, &v))
+        return false;
+
+    if (v.isString())
+        return sb.append(v.toString());
+    if (v.isNumber())
+        return NumberValueToStringBuffer(cx, v, sb);
+    if (v.isBoolean())
+        return BooleanToStringBuffer(cx, v.toBoolean(), sb);
+    if (v.isNull())
+        return sb.append(cx->runtime->atomState.nullAtom);
+    JS_ASSERT(v.isUndefined());
+    return sb.append(cx->runtime->atomState.typeAtoms[JSTYPE_VOID]);
+}
--- a/js/src/vm/StringBuffer.h
+++ b/js/src/vm/StringBuffer.h
@@ -21,110 +21,96 @@ namespace js {
  * string length.
  *
  * Any operation which would exceed the maximum string length causes an
  * exception report on the context and results in a failed return value.
  *
  * Well-sized extractions (which waste no more than 1/4 of their char
  * buffer space) are guaranteed for strings built by this interface.
  * See |extractWellSized|.
- *
- * Note: over-allocation is not checked for when using the infallible
- * |replaceRawBuffer|, so the implementation of |finishString| also must check
- * for over-allocation.
  */
 class StringBuffer
 {
     /* cb's buffer is taken by the new string so use ContextAllocPolicy. */
     typedef Vector<jschar, 32, ContextAllocPolicy> CharBuffer;
 
     CharBuffer cb;
 
-    static inline bool checkLength(JSContext *cx, size_t length);
     inline bool checkLength(size_t length);
     JSContext *context() const { return cb.allocPolicy().context(); }
     jschar *extractWellSized();
 
     StringBuffer(const StringBuffer &other) MOZ_DELETE;
     void operator=(const StringBuffer &other) MOZ_DELETE;
 
   public:
-    explicit inline StringBuffer(JSContext *cx);
+    explicit StringBuffer(JSContext *cx) : cb(cx) { }
+
     inline bool reserve(size_t len);
     inline bool resize(size_t len);
     inline bool append(const jschar c);
     inline bool append(const jschar *chars, size_t len);
     inline bool append(const jschar *begin, const jschar *end);
     inline bool append(JSString *str);
     inline bool append(JSLinearString *str);
     inline bool appendN(const jschar c, size_t n);
     inline bool appendInflated(const char *cstr, size_t len);
 
+    template <size_t ArrayLength>
+    bool append(const char (&array)[ArrayLength]) {
+        return cb.append(array, array + ArrayLength - 1); /* No trailing '\0'. */
+    }
+
     /* Infallible variants usable when the corresponding space is reserved. */
     void infallibleAppend(const jschar c) {
         cb.infallibleAppend(c);
     }
     void infallibleAppend(const jschar *chars, size_t len) {
         cb.infallibleAppend(chars, len);
     }
     void infallibleAppend(const jschar *begin, const jschar *end) {
         cb.infallibleAppend(begin, end);
     }
     void infallibleAppendN(const jschar c, size_t n) {
         cb.infallibleAppendN(c, n);
     }
 
-    JSAtom *atomize(unsigned flags = 0);
-    static JSAtom *atomize(JSContext *cx, const CharBuffer &cb, unsigned flags = 0);
-    static JSAtom *atomize(JSContext *cx, const jschar *begin, size_t length, unsigned flags = 0);
-
-    void replaceRawBuffer(jschar *chars, size_t len) { cb.replaceRawBuffer(chars, len); }
     jschar *begin() { return cb.begin(); }
     jschar *end() { return cb.end(); }
     const jschar *begin() const { return cb.begin(); }
     const jschar *end() const { return cb.end(); }
     bool empty() const { return cb.empty(); }
     inline size_t length() const;
 
     /*
      * Creates a string from the characters in this buffer, then (regardless
      * whether string creation succeeded or failed) empties the buffer.
      */
     JSFixedString *finishString();
 
     /* Identical to finishString() except that an atom is created. */
     JSAtom *finishAtom();
-
-    template <size_t ArrayLength>
-    bool append(const char (&array)[ArrayLength]) {
-        return cb.append(array, array + ArrayLength - 1); /* No trailing '\0'. */
-    }
 };
 
-inline
-StringBuffer::StringBuffer(JSContext *cx)
-  : cb(cx)
-{}
+inline bool
+StringBuffer::append(JSLinearString *str)
+{
+    JS::Anchor<JSString *> anch(str);
+    return cb.append(str->chars(), str->length());
+}
 
 inline bool
 StringBuffer::append(JSString *str)
 {
     JSLinearString *linear = str->ensureLinear(context());
     if (!linear)
         return false;
     return append(linear);
 }
 
-inline bool
-StringBuffer::append(JSLinearString *str)
-{
-    JS::Anchor<JSString *> anch(str);
-    return cb.append(str->chars(), str->length());
-}
-
 inline size_t
 StringBuffer::length() const
 {
     JS_ASSERT(cb.length() <= JSString::MAX_LENGTH);
     return cb.length();
 }
 
 inline bool
--- a/js/xpconnect/src/XPCJSRuntime.cpp
+++ b/js/xpconnect/src/XPCJSRuntime.cpp
@@ -921,51 +921,64 @@ XPCJSRuntime::FinalizeCallback(JSContext
                 xpc_NotifyAll(self->GetMapLock());
             }
 
             break;
         }
     }
 }
 
+class AutoLockWatchdog {
+    XPCJSRuntime* const mRuntime;
+
+  public:
+    AutoLockWatchdog(XPCJSRuntime* aRuntime)
+      : mRuntime(aRuntime) {
+        PR_Lock(mRuntime->mWatchdogLock);
+    }
+
+    ~AutoLockWatchdog() {
+        PR_Unlock(mRuntime->mWatchdogLock);
+    }
+};
+
 //static
 void
 XPCJSRuntime::WatchdogMain(void *arg)
 {
     XPCJSRuntime* self = static_cast<XPCJSRuntime*>(arg);
 
     // Lock lasts until we return
-    js::AutoLockGC lock(self->mJSRuntime);
+    AutoLockWatchdog lock(self);
 
     PRIntervalTime sleepInterval;
     while (self->mWatchdogThread) {
         // Sleep only 1 second if recently (or currently) active; otherwise, hibernate
         if (self->mLastActiveTime == -1 || PR_Now() - self->mLastActiveTime <= PRTime(2*PR_USEC_PER_SEC))
             sleepInterval = PR_TicksPerSecond();
         else {
             sleepInterval = PR_INTERVAL_NO_TIMEOUT;
             self->mWatchdogHibernating = true;
         }
-#ifdef DEBUG
-        PRStatus status =
-#endif
-            PR_WaitCondVar(self->mWatchdogWakeup, sleepInterval);
-        JS_ASSERT(status == PR_SUCCESS);
-        js::TriggerOperationCallback(self->mJSRuntime);
+        MOZ_ALWAYS_TRUE(PR_WaitCondVar(self->mWatchdogWakeup, sleepInterval) == PR_SUCCESS);
+        JS_TriggerOperationCallback(self->mJSRuntime);
     }
 
     /* Wake up the main thread waiting for the watchdog to terminate. */
     PR_NotifyCondVar(self->mWatchdogWakeup);
 }
 
 //static
 void
 XPCJSRuntime::ActivityCallback(void *arg, JSBool active)
 {
     XPCJSRuntime* self = static_cast<XPCJSRuntime*>(arg);
+
+    AutoLockWatchdog lock(self);
+    
     if (active) {
         self->mLastActiveTime = -1;
         if (self->mWatchdogHibernating) {
             self->mWatchdogHibernating = false;
             PR_NotifyCondVar(self->mWatchdogWakeup);
         }
     } else {
         self->mLastActiveTime = PR_Now();
@@ -1053,24 +1066,25 @@ XPCJSRuntime::GetJSCycleCollectionContex
 XPCJSRuntime::~XPCJSRuntime()
 {
     if (mWatchdogWakeup) {
         // If the watchdog thread is running, tell it to terminate waking it
         // up if necessary and wait until it signals that it finished. As we
         // must release the lock before calling PR_DestroyCondVar, we use an
         // extra block here.
         {
-            js::AutoLockGC lock(mJSRuntime);
+            AutoLockWatchdog lock(this);
             if (mWatchdogThread) {
                 mWatchdogThread = nsnull;
                 PR_NotifyCondVar(mWatchdogWakeup);
                 PR_WaitCondVar(mWatchdogWakeup, PR_INTERVAL_NO_TIMEOUT);
             }
         }
         PR_DestroyCondVar(mWatchdogWakeup);
+        PR_DestroyLock(mWatchdogLock);
         mWatchdogWakeup = nsnull;
     }
 
     if (mJSCycleCollectionContext)
         JS_DestroyContextNoGC(mJSCycleCollectionContext);
 
 #ifdef XPC_DUMP_AT_SHUTDOWN
     {
@@ -2024,16 +2038,17 @@ XPCJSRuntime::XPCJSRuntime(nsXPConnect* 
    mMapLock(XPCAutoLock::NewLock("XPCJSRuntime::mMapLock")),
    mThreadRunningGC(nsnull),
    mWrappedJSToReleaseArray(),
    mNativesToReleaseArray(),
    mDoingFinalization(false),
    mVariantRoots(nsnull),
    mWrappedJSRoots(nsnull),
    mObjectHolderRoots(nsnull),
+   mWatchdogLock(nsnull),
    mWatchdogWakeup(nsnull),
    mWatchdogThread(nsnull),
    mWatchdogHibernating(false),
    mLastActiveTime(-1)
 {
 #ifdef XPC_CHECK_WRAPPERS_AT_SHUTDOWN
     DEBUG_WrappedNativeHashtable =
         JS_NewDHashTable(JS_DHashGetStubOps(), nsnull,
@@ -2048,72 +2063,72 @@ XPCJSRuntime::XPCJSRuntime(nsXPConnect* 
 
     // these jsids filled in later when we have a JSContext to work with.
     mStrIDs[0] = JSID_VOID;
 
     mJSRuntime = JS_NewRuntime(32L * 1024L * 1024L); // pref ?
     if (!mJSRuntime)
         NS_RUNTIMEABORT("JS_NewRuntime failed.");
 
-    {
-        // Unconstrain the runtime's threshold on nominal heap size, to avoid
-        // triggering GC too often if operating continuously near an arbitrary
-        // finite threshold (0xffffffff is infinity for uint32_t parameters).
-        // This leaves the maximum-JS_malloc-bytes threshold still in effect
-        // to cause period, and we hope hygienic, last-ditch GCs from within
-        // the GC's allocator.
-        JS_SetGCParameter(mJSRuntime, JSGC_MAX_BYTES, 0xffffffff);
+    // Unconstrain the runtime's threshold on nominal heap size, to avoid
+    // triggering GC too often if operating continuously near an arbitrary
+    // finite threshold (0xffffffff is infinity for uint32_t parameters).
+    // This leaves the maximum-JS_malloc-bytes threshold still in effect
+    // to cause period, and we hope hygienic, last-ditch GCs from within
+    // the GC's allocator.
+    JS_SetGCParameter(mJSRuntime, JSGC_MAX_BYTES, 0xffffffff);
 #ifdef MOZ_ASAN
-        // ASan requires more stack space due to redzones
-        JS_SetNativeStackQuota(mJSRuntime, 2 * 128 * sizeof(size_t) * 1024);
+    // ASan requires more stack space due to redzones
+    JS_SetNativeStackQuota(mJSRuntime, 2 * 128 * sizeof(size_t) * 1024);
 #else  
-        JS_SetNativeStackQuota(mJSRuntime, 128 * sizeof(size_t) * 1024);
+    JS_SetNativeStackQuota(mJSRuntime, 128 * sizeof(size_t) * 1024);
 #endif
-        JS_SetContextCallback(mJSRuntime, ContextCallback);
-        JS_SetCompartmentCallback(mJSRuntime, CompartmentCallback);
-        JS_SetGCCallback(mJSRuntime, GCCallback);
-        JS_SetFinalizeCallback(mJSRuntime, FinalizeCallback);
-        JS_SetExtraGCRootsTracer(mJSRuntime, TraceBlackJS, this);
-        JS_SetGrayGCRootsTracer(mJSRuntime, TraceGrayJS, this);
-        JS_SetWrapObjectCallbacks(mJSRuntime,
-                                  xpc::WrapperFactory::Rewrap,
-                                  xpc::WrapperFactory::PrepareForWrapping);
-        js::SetPreserveWrapperCallback(mJSRuntime, PreserveWrapper);
-
+    JS_SetContextCallback(mJSRuntime, ContextCallback);
+    JS_SetCompartmentCallback(mJSRuntime, CompartmentCallback);
+    JS_SetGCCallback(mJSRuntime, GCCallback);
+    JS_SetFinalizeCallback(mJSRuntime, FinalizeCallback);
+    JS_SetExtraGCRootsTracer(mJSRuntime, TraceBlackJS, this);
+    JS_SetGrayGCRootsTracer(mJSRuntime, TraceGrayJS, this);
+    JS_SetWrapObjectCallbacks(mJSRuntime,
+                              xpc::W