merge mozilla-inbound to mozilla-central a=merge
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Mon, 27 Oct 2014 15:50:43 +0100
changeset 212438 d65d20dc0ac238dac2379286d8d1ddcd8170ac6b
parent 212398 cf1a9a16429bd6bea71a0aa3550a1fbf76a0d65a (current diff)
parent 212437 1eb9bb6ee090cc7b23e41058baf2572235e9f9a7 (diff)
child 212449 20408ad61ce5a530d7e563da564efb9bb6b8658f
push id27710
push usercbook@mozilla.com
push dateMon, 27 Oct 2014 14:52:01 +0000
treeherdermozilla-central@d65d20dc0ac2 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone36.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
merge mozilla-inbound to mozilla-central a=merge
dom/html/HTMLMediaElement.cpp
dom/html/HTMLMediaElement.h
memory/replace/dmd/test/script-show-all-block-sizes-expected.txt
memory/replace/dmd/test/script-show-all-block-sizes.json
xpcom/reflect/xptcall/md/unix/xptcinvoke_amd64_openbsd.cpp
xpcom/reflect/xptcall/md/unix/xptcstubs_amd64_openbsd.cpp
--- a/b2g/components/OopCommandLine.js
+++ b/b2g/components/OopCommandLine.js
@@ -34,14 +34,14 @@ oopCommandlineHandler.prototype = {
             } catch (e) { }
 
         }
         if (cmdLine.state == Ci.nsICommandLine.STATE_REMOTE_AUTO) {
             cmdLine.preventDefault = true;
         }
     },
 
-    helpInfo: "  -oop         Use out-of-process model in B2G\n",
+    helpInfo: "  --oop        Use out-of-process model in B2G\n",
     classID: Components.ID("{e30b0e13-2d12-4cb0-bc4c-4e617a1bf76e}"),
     QueryInterface: XPCOMUtils.generateQI([Ci.nsICommandLineHandler]),
 };
 
 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([oopCommandlineHandler]);
--- a/b2g/gaia/run-b2g.c
+++ b/b2g/gaia/run-b2g.c
@@ -30,18 +30,20 @@ int main(int argc, char* argv[], char* e
     full_profile_path = (char*) malloc(strlen(cwd) + strlen(GAIA_PATH) + 2);
     if (!full_profile_path) {
         error(NOMEM);
         return -2;
     }
     sprintf(full_path, "%s/%s", cwd, B2G_NAME);
     sprintf(full_profile_path, "%s/%s", cwd, GAIA_PATH);
     free(cwd);
-    printf("Running: %s -profile %s\n", full_path, full_profile_path);
+    printf("Running: %s --profile %s\n", full_path, full_profile_path);
     fflush(stdout);
     fflush(stderr);
+    // XXX: yes, the printf above says --profile and this execle uses -profile.
+    // Bug 1088430 will change the execle to use --profile.
     execle(full_path, full_path, "-profile", full_profile_path, NULL, envp);
     error("unable to start");
     perror(argv[0]);
     free(full_path);
     free(full_profile_path);
     return -1;
 }
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -4254,17 +4254,17 @@ nsBrowserAccess.prototype = {
     return browser;
   },
 
   openURI: function (aURI, aOpener, aWhere, aContext) {
     var newWindow = null;
     var isExternal = (aContext == Ci.nsIBrowserDOMWindow.OPEN_EXTERNAL);
 
     if (isExternal && aURI && aURI.schemeIs("chrome")) {
-      dump("use -chrome command-line option to load external chrome urls\n");
+      dump("use --chrome command-line option to load external chrome urls\n");
       return null;
     }
 
     if (aWhere == Ci.nsIBrowserDOMWindow.OPEN_DEFAULTWINDOW) {
       if (isExternal &&
           gPrefService.prefHasUserValue("browser.link.open_newwindow.override.external"))
         aWhere = gPrefService.getIntPref("browser.link.open_newwindow.override.external");
       else
--- a/browser/base/content/test/general/browser_bug520538.js
+++ b/browser/base/content/test/general/browser_bug520538.js
@@ -1,15 +1,15 @@
 function test() {
   var tabCount = gBrowser.tabs.length;
   gBrowser.selectedBrowser.focus();
   browserDOMWindow.openURI(makeURI("about:blank"),
                            null,
                            Ci.nsIBrowserDOMWindow.OPEN_NEWTAB,
                            Ci.nsIBrowserDOMWindow.OPEN_EXTERNAL);
   is(gBrowser.tabs.length, tabCount + 1,
-     "'-new-tab about:blank' opens a new tab");
+     "'--new-tab about:blank' opens a new tab");
   is(gBrowser.selectedTab, gBrowser.tabs[tabCount],
-     "'-new-tab about:blank' selects the new tab");
+     "'--new-tab about:blank' selects the new tab");
   is(document.activeElement, gURLBar.inputField,
-     "'-new-tab about:blank' focuses the location bar");
+     "'--new-tab about:blank' focuses the location bar");
   gBrowser.removeCurrentTab();
 }
--- a/browser/base/content/test/general/browser_save_private_link_perwindowpb.js
+++ b/browser/base/content/test/general/browser_save_private_link_perwindowpb.js
@@ -87,17 +87,17 @@ function test() {
       // Select "Save Image As" option from context menu
       var saveVideoCommand = aWindow.document.getElementById("context-saveimage");
       saveVideoCommand.doCommand();
 
       event.target.hidePopup();
     }
 
     aWindow.gBrowser.addEventListener("pageshow", function pageShown(event) {
-      // If data: -url PAC file isn't loaded soon enough, we may get about:privatebrowsing loaded
+      // If data: --url PAC file isn't loaded soon enough, we may get about:privatebrowsing loaded
       if (event.target.location == "about:blank" ||
           event.target.location == "about:privatebrowsing") {
         aWindow.gBrowser.selectedBrowser.loadURI(testURI);
         return;
       }
       aWindow.gBrowser.removeEventListener("pageshow", pageShown);
 
       waitForFocus(function () {
--- a/browser/components/nsBrowserContentHandler.js
+++ b/browser/components/nsBrowserContentHandler.js
@@ -43,17 +43,17 @@ const NS_ERROR_ABORT = Components.result
 const URI_INHERITS_SECURITY_CONTEXT = Components.interfaces.nsIHttpProtocolHandler
                                         .URI_INHERITS_SECURITY_CONTEXT;
 
 function shouldLoadURI(aURI) {
   if (aURI && !aURI.schemeIs("chrome"))
     return true;
 
   dump("*** Preventing external load of chrome: URI into browser window\n");
-  dump("    Use -chrome <uri> instead\n");
+  dump("    Use --chrome <uri> instead\n");
   return false;
 }
 
 function resolveURIInternal(aCmdLine, aArgument) {
   var uri = aCmdLine.resolveURI(aArgument);
   var urifixup = Components.classes["@mozilla.org/docshell/urifixup;1"]
                            .getService(nsIURIFixup);
 
@@ -451,26 +451,26 @@ nsBrowserContentHandler.prototype = {
 
         searchParam = param.substr(2);
         doSearch(searchParam, cmdLine);
       }
     }
 #endif
   },
 
-  helpInfo : "  -browser           Open a browser window.\n" +
-             "  -new-window  <url> Open <url> in a new window.\n" +
-             "  -new-tab     <url> Open <url> in a new tab.\n" +
-             "  -private-window <url> Open <url> in a new private window.\n" +
+  helpInfo : "  --browser          Open a browser window.\n" +
+             "  --new-window <url> Open <url> in a new window.\n" +
+             "  --new-tab <url>    Open <url> in a new tab.\n" +
+             "  --private-window <url> Open <url> in a new private window.\n" +
 #ifdef XP_WIN
-             "  -preferences       Open Options dialog.\n" +
+             "  --preferences      Open Options dialog.\n" +
 #else
-             "  -preferences       Open Preferences dialog.\n" +
+             "  --preferences      Open Preferences dialog.\n" +
 #endif
-             "  -search     <term> Search <term> with your default search engine.\n",
+             "  --search <term>    Search <term> with your default search engine.\n",
 
   /* nsIBrowserHandler */
 
   get defaultArgs() {
     var prefb = Components.classes["@mozilla.org/preferences-service;1"]
                           .getService(nsIPrefBranch);
 
     if (!gFirstWindow) {
--- a/browser/components/shell/nsSetDefaultBrowser.js
+++ b/browser/components/shell/nsSetDefaultBrowser.js
@@ -1,14 +1,14 @@
 /* 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/. */
 
 /* 
- * -setDefaultBrowser commandline handler
+ * --setDefaultBrowser commandline handler
  * Makes the current executable the "default browser".
  */
 
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
 
 function nsSetDefaultBrowser() {}
@@ -17,15 +17,15 @@ nsSetDefaultBrowser.prototype = {
   handle: function nsSetDefault_handle(aCmdline) {
     if (aCmdline.handleFlag("setDefaultBrowser", false)) {
       var shell = Cc["@mozilla.org/browser/shell-service;1"].
                   getService(Ci.nsIShellService);
       shell.setDefaultBrowser(true, true);
     }
   },
 
-  helpInfo: "  -setDefaultBrowser Set this app as the default browser.\n",
+  helpInfo: "  --setDefaultBrowser Set this app as the default browser.\n",
 
   classID: Components.ID("{F57899D0-4E2C-4ac6-9E29-50C736103B0C}"),
   QueryInterface: XPCOMUtils.generateQI([Ci.nsICommandLineHandler]),
 };
 
 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([nsSetDefaultBrowser]);
--- a/browser/devtools/devtools-clhandler.js
+++ b/browser/devtools/devtools-clhandler.js
@@ -62,16 +62,16 @@ devtoolsCommandlineHandler.prototype = {
       dump(errorMsg + "\n");
     }
 
     if (cmdLine.state == Ci.nsICommandLine.STATE_REMOTE_AUTO) {
       cmdLine.preventDefault = true;
     }
   },
 
-  helpInfo : "  -jsconsole         Open the Browser Console.\n" +
-             "  -jsdebugger        Open the Browser Toolbox.\n",
+  helpInfo : "  --jsconsole        Open the Browser Console.\n" +
+             "  --jsdebugger       Open the Browser Toolbox.\n",
 
   classID: Components.ID("{9e9a9283-0ce9-4e4a-8f1c-ba129a032c32}"),
   QueryInterface: XPCOMUtils.generateQI([Ci.nsICommandLineHandler]),
 };
 
 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([devtoolsCommandlineHandler]);
--- a/browser/devtools/webide/components/webideCli.js
+++ b/browser/devtools/webide/components/webideCli.js
@@ -5,33 +5,33 @@
 const Ci = Components.interfaces;
 const Cu = Components.utils;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "Services", "resource://gre/modules/Services.jsm");
 
 /**
- * Handles -webide command line option.
+ * Handles --webide command line option.
  */
 
 function webideCli() { }
 
 webideCli.prototype = {
   handle: function(cmdLine) {
     let param;
 
     if (!cmdLine.handleFlag("webide", false)) {
       return;
     }
 
-    // If -webide is used remotely, we don't want to open
+    // If --webide is used remotely, we don't want to open
     // a new tab.
     //
-    // If -webide is used for a new Firefox instance, we
+    // If --webide is used for a new Firefox instance, we
     // want to open webide only.
     cmdLine.preventDefault = true;
 
     let win = Services.wm.getMostRecentWindow("devtools:webide");
     if (win) {
       win.focus();
     } else {
       win = Services.ww.openWindow(null,
--- a/browser/metro/components/BrowserCLH.js
+++ b/browser/metro/components/BrowserCLH.js
@@ -132,20 +132,20 @@ BrowserCLH.prototype = {
   handle: function fs_handle(aCmdLine) {
 #ifdef DEBUG
     for (var idx = 0; idx <  aCmdLine.length; idx++) {
       dump(aCmdLine.getArgument(idx) + "\n");
     }
 #endif
     // Instantiate the search service so the search engine cache is created now
     // instead when the application is running. The install process will register
-    // this component by using the -silent command line flag, thereby creating
+    // this component by using the --silent command line flag, thereby creating
     // the cache during install, not runtime.
     // NOTE: This code assumes this CLH is run before the nsDefaultCLH, which
-    // consumes the "-silent" flag.
+    // consumes the "--silent" flag.
     if (aCmdLine.findFlag("silent", false) > -1) {
       let searchService = Services.search;
       let autoComplete = Cc["@mozilla.org/autocomplete/search;1?name=history"].
                          getService(Ci.nsIAutoCompleteSearch);
       return;
     }
 
     // Handle chrome windows loaded via commandline
--- a/browser/metro/shell/commandexecutehandler/CommandExecuteHandler.cpp
+++ b/browser/metro/shell/commandexecutehandler/CommandExecuteHandler.cpp
@@ -611,17 +611,17 @@ void LaunchDesktopBrowserWithParams(CStr
     // Fallback to the module path if it failed to get the default browser.
     GetDefaultBrowserPath(aBrowserPath);
     params += "-url ";
     params += "\"";
     params += aTarget;
     params += "\"";
   }
 
-  // Tack on any extra parameters we received (for example -profilemanager)
+  // Tack on any extra parameters we received (for example --profilemanager)
   if (!aParameters.IsEmpty()) {
     params += " ";
     params += aParameters;
   }
 
   Log(L"Desktop Launch: verb:'%s' exe:'%s' params:'%s'", aVerb, aBrowserPath, params);
 
   // Relaunch in Desktop mode uses a special URL to trick Windows into
--- a/configure.in
+++ b/configure.in
@@ -4000,17 +4000,17 @@ else
   # "mobile" no longer exists.
   if test "$MOZ_BUILD_APP" = "mobile" ; then
     AC_MSG_RESULT([none])
     AC_MSG_ERROR([--enable-application=mobile is no longer supported.])
   fi
   # Support comm-central.
   if test -n "$EXTERNAL_SOURCE_DIR" ; then
     MOZ_BUILD_APP="$EXTERNAL_SOURCE_DIR/$MOZ_BUILD_APP"
-    MOZ_BUILD_APP=`${PYTHON} -c "import os.path; print(os.path.relpath(\"${MOZ_BUILD_APP}\", \"${srcdir}\"))"`
+    MOZ_BUILD_APP=`${PYTHON} -c "import mozpack.path as mozpath; print(mozpath.relpath(\"${MOZ_BUILD_APP}\", \"${srcdir}\"))"`
   fi
   # We have a valid application only if it has a build.mk file in its top
   # directory.
   if test ! -f "${srcdir}/${MOZ_BUILD_APP}/build.mk" ; then
     AC_MSG_RESULT([none])
     AC_MSG_ERROR([--enable-application value not recognized (${MOZ_BUILD_APP}/build.mk does not exist).])
   else
     AC_MSG_RESULT([$MOZ_BUILD_APP])
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -9637,17 +9637,17 @@ nsDocShell::InternalLoad(nsIURI * aURI,
         }
     }
 
     // Before going any further vet loads initiated by external programs.
     if (aLoadType == LOAD_NORMAL_EXTERNAL) {
         // Disallow external chrome: loads targetted at content windows
         bool isChrome = false;
         if (NS_SUCCEEDED(aURI->SchemeIs("chrome", &isChrome)) && isChrome) {
-            NS_WARNING("blocked external chrome: url -- use '-chrome' option");
+            NS_WARNING("blocked external chrome: url -- use '--chrome' option");
             return NS_ERROR_FAILURE;
         }
 
         // clear the decks to prevent context bleed-through (bug 298255)
         rv = CreateAboutBlankContentViewer(nullptr, nullptr);
         if (NS_FAILED(rv))
             return NS_ERROR_FAILURE;
 
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -4136,21 +4136,16 @@ nsContentUtils::CreateContextualFragment
   nsAutoString uriStr, nameStr;
   nsCOMPtr<nsIContent> content = do_QueryInterface(aContextNode);
   // just in case we have a text node
   if (content && !content->IsElement())
     content = content->GetParent();
 
   while (content && content->IsElement()) {
     nsString& tagName = *tagStack.AppendElement();
-    if (!&tagName) {
-      aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
-      return nullptr;
-    }
-
     tagName = content->NodeInfo()->QualifiedName();
 
     // see if we need to add xmlns declarations
     uint32_t count = content->GetAttrCount();
     bool setDefaultNamespace = false;
     if (count > 0) {
       uint32_t index;
 
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -9350,31 +9350,40 @@ nsGlobalWindow::UpdateCommands(const nsA
                                                             anAction));
     }
   }
 
   if (gSelectionCaretPrefEnabled && mDoc && anAction.EqualsLiteral("selectionchange")) {
     SelectionChangeEventInit init;
     init.mBubbles = true;
     if (aSel) {
-      nsCOMPtr<nsIDOMRange> range;
-      nsresult rv = aSel->GetRangeAt(0, getter_AddRefs(range));
-      if (NS_SUCCEEDED(rv) && range) {
-        nsRefPtr<nsRange> nsrange = static_cast<nsRange*>(range.get());
-        init.mBoundingClientRect = nsrange->GetBoundingClientRect(true, false);
-        range->ToString(init.mSelectedText);
-
-        for (uint32_t reasonType = 0;
-             reasonType < static_cast<uint32_t>(SelectionChangeReason::EndGuard_);
-             ++reasonType) {
-          SelectionChangeReason strongReasonType =
-            static_cast<SelectionChangeReason>(reasonType);
-          if (CheckReason(aReason, strongReasonType)) {
-            init.mReasons.AppendElement(strongReasonType);
-          }
+      Selection* selection = static_cast<Selection*>(aSel);
+      int32_t rangeCount = selection->GetRangeCount();
+      nsLayoutUtils::RectAccumulator accumulator;
+      for (int32_t idx = 0; idx < rangeCount; ++idx) {
+        nsRange* range = selection->GetRangeAt(idx);
+        nsRange::CollectClientRects(&accumulator, range,
+                                    range->GetStartParent(), range->StartOffset(),
+                                    range->GetEndParent(), range->EndOffset(),
+                                    true, false);
+      }
+      nsRect rect = accumulator.mResultRect.IsEmpty() ? accumulator.mFirstRect :
+        accumulator.mResultRect;
+      nsRefPtr<DOMRect> domRect = new DOMRect(ToSupports(this));
+      domRect->SetLayoutRect(rect);
+      init.mBoundingClientRect = domRect;
+
+      selection->Stringify(init.mSelectedText);
+      for (uint32_t reasonType = 0;
+           reasonType < static_cast<uint32_t>(SelectionChangeReason::EndGuard_);
+           ++reasonType) {
+        SelectionChangeReason strongReasonType =
+          static_cast<SelectionChangeReason>(reasonType);
+        if (CheckReason(aReason, strongReasonType)) {
+          init.mReasons.AppendElement(strongReasonType);
         }
       }
 
       nsRefPtr<SelectionChangeEvent> event =
         SelectionChangeEvent::Constructor(mDoc, NS_LITERAL_STRING("mozselectionchange"), init);
 
       event->SetTrusted(true);
       event->GetInternalNSEvent()->mFlags.mOnlyChromeDispatch = true;
--- a/dom/base/test/test_bug811701.html
+++ b/dom/base/test/test_bug811701.html
@@ -21,28 +21,28 @@ https://bugzilla.mozilla.org/show_bug.cg
   <script type="application/javascript">
 
   /** Test for Bug 811701 **/
   var math = document.querySelector("math");
   is(math.innerHTML, "<mtext>test</mtext>", "<math> should have innerHTML");
   is(math.outerHTML, "<math><mtext>test</mtext></math>",
      "<math> should have innerHTML");
   math.innerHTML = "<mo>+</mo>";
-  is(math.firstChild.namespaceURI, "http://www.w3.org/1999/xhtml",
+  is(math.firstChild.namespaceURI, "http://www.w3.org/1998/Math/MathML",
      "Should have the right namespace after setting innerHTML on <math>");
 
   var polygon = document.querySelector("polygon");
   is(polygon.parentNode.innerHTML,
      '<polygon points="0,0 100,100 200,300"></polygon>',
      "<svg> should have innerHTML");
   is(polygon.parentNode.outerHTML,
      '<svg><polygon points="0,0 100,100 200,300"></polygon></svg>',
      "<svg> should have outerHTML");
   is(polygon.outerHTML, '<polygon points="0,0 100,100 200,300"></polygon>',
      "<polygon> should have outerHTML");
 
   var svg = document.querySelector("svg");
   svg.innerHTML = "<rect/>";
-  is(svg.firstChild.namespaceURI, "http://www.w3.org/1999/xhtml",
-     "Should have the right namespace after setting innerHTML on <math>");
+  is(svg.firstChild.namespaceURI, "http://www.w3.org/2000/svg",
+     "Should have the right namespace after setting innerHTML on <svg>");
   </script>
 </body>
 </html>
old mode 100755
new mode 100644
old mode 100755
new mode 100644
--- a/dom/html/nsHTMLContentSink.cpp
+++ b/dom/html/nsHTMLContentSink.cpp
@@ -168,20 +168,16 @@ protected:
   // Boolean indicating whether we've seen a <head> tag that might have had
   // attributes once already.
   bool mHaveSeenHead;
 
   // Boolean indicating whether we've notified insertion of our root content
   // yet.  We want to make sure to only do this once.
   bool mNotifiedRootInsertion;
 
-  uint8_t mScriptEnabled : 1;
-  uint8_t mFramesEnabled : 1;
-  uint8_t unused : 6;  // bits available if someone needs one
-
   mozilla::dom::NodeInfo* mNodeInfoCache[NS_HTML_TAG_MAX + 1];
 
   nsresult FlushTags();
 
   // Routines for tags that require special handling
   nsresult CloseHTML();
   nsresult OpenBody();
   nsresult CloseBody();
@@ -719,35 +715,16 @@ NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION
     NS_INTERFACE_TABLE_ENTRY(HTMLContentSink, nsIContentSink)
     NS_INTERFACE_TABLE_ENTRY(HTMLContentSink, nsIHTMLContentSink)
   NS_INTERFACE_TABLE_END
 NS_INTERFACE_TABLE_TAIL_INHERITING(nsContentSink)
 
 NS_IMPL_ADDREF_INHERITED(HTMLContentSink, nsContentSink)
 NS_IMPL_RELEASE_INHERITED(HTMLContentSink, nsContentSink)
 
-static bool
-IsScriptEnabled(nsIDocument *aDoc, nsIDocShell *aContainer)
-{
-  NS_ENSURE_TRUE(aDoc && aContainer, true);
-
-  nsCOMPtr<nsIScriptGlobalObject> globalObject =
-    do_QueryInterface(aDoc->GetInnerWindow());
-
-  // Getting context is tricky if the document hasn't had its
-  // GlobalObject set yet
-  if (!globalObject) {
-    globalObject = aContainer->GetScriptGlobalObject();
-  }
-
-  NS_ENSURE_TRUE(globalObject && globalObject->GetGlobalJSObject(), true);
-  return nsContentUtils::GetSecurityManager()->
-           ScriptAllowed(globalObject->GetGlobalJSObject());
-}
-
 nsresult
 HTMLContentSink::Init(nsIDocument* aDoc,
                       nsIURI* aURI,
                       nsISupports* aContainer,
                       nsIChannel* aChannel)
 {
   NS_ENSURE_TRUE(aContainer, NS_ERROR_NULL_POINTER);
   
@@ -757,31 +734,16 @@ HTMLContentSink::Init(nsIDocument* aDoc,
   }
 
   aDoc->AddObserver(this);
   mIsDocumentObserver = true;
   mHTMLDocument = do_QueryInterface(aDoc);
 
   NS_ASSERTION(mDocShell, "oops no docshell!");
 
-  // Find out if subframes are enabled
-  if (mDocShell) {
-    bool subFramesEnabled = true;
-    mDocShell->GetAllowSubframes(&subFramesEnabled);
-    if (subFramesEnabled) {
-      mFramesEnabled = true;
-    }
-  }
-
-  // Find out if scripts are enabled, if not, show <noscript> content
-  if (IsScriptEnabled(aDoc, mDocShell)) {
-    mScriptEnabled = true;
-  }
-
-
   // Changed from 8192 to greatly improve page loading performance on
   // large pages.  See bugzilla bug 77540.
   mMaxTextRun = Preferences::GetInt("content.maxtextrun", 8191);
 
   nsRefPtr<mozilla::dom::NodeInfo> nodeInfo;
   nodeInfo = mNodeInfoManager->GetNodeInfo(nsGkAtoms::html, nullptr,
                                            kNameSpaceID_XHTML,
                                            nsIDOMNode::ELEMENT_NODE);
--- a/dom/media/MediaDecoderStateMachine.cpp
+++ b/dom/media/MediaDecoderStateMachine.cpp
@@ -208,17 +208,19 @@ MediaDecoderStateMachine::MediaDecoderSt
   mStopAudioThread(true),
   mQuickBuffering(false),
   mMinimizePreroll(false),
   mDecodeThreadWaiting(false),
   mDropAudioUntilNextDiscontinuity(false),
   mDropVideoUntilNextDiscontinuity(false),
   mDecodeToSeekTarget(false),
   mCurrentTimeBeforeSeek(0),
-  mLastFrameStatus(MediaDecoderOwner::NEXT_FRAME_UNINITIALIZED)
+  mLastFrameStatus(MediaDecoderOwner::NEXT_FRAME_UNINITIALIZED),
+  mDecodingFrozenAtStateMetadata(false),
+  mDecodingFrozenAtStateDecoding(false)
 {
   MOZ_COUNT_CTOR(MediaDecoderStateMachine);
   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
 
   mAmpleVideoFrames =
     std::max<uint32_t>(Preferences::GetUint("media.video-queue.default-size", 10), 3);
 
   mBufferingWait = mScheduler->IsRealTime() ? 0 : BUFFERING_WAIT_S;
@@ -1351,18 +1353,19 @@ void MediaDecoderStateMachine::SetDorman
 
   DECODER_LOG("SetDormant=%d", aDormant);
 
   if (aDormant) {
     ScheduleStateMachine();
     SetState(DECODER_STATE_DORMANT);
     mDecoder->GetReentrantMonitor().NotifyAll();
   } else if ((aDormant != true) && (mState == DECODER_STATE_DORMANT)) {
+    mDecodingFrozenAtStateMetadata = true;
+    mDecodingFrozenAtStateDecoding = true;
     ScheduleStateMachine();
-    mStartTime = 0;
     mCurrentFrameTime = 0;
     SetState(DECODER_STATE_DECODING_NONE);
     mDecoder->GetReentrantMonitor().NotifyAll();
   }
 }
 
 void MediaDecoderStateMachine::Shutdown()
 {
@@ -1449,16 +1452,20 @@ void MediaDecoderStateMachine::Play()
   // we are currently buffering. In other cases, we'll start playing anyway
   // when the state machine notices the decoder's state change to PLAYING.
   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
   if (mState == DECODER_STATE_BUFFERING) {
     DECODER_LOG("Changed state from BUFFERING to DECODING");
     SetState(DECODER_STATE_DECODING);
     mDecodeStartTime = TimeStamp::Now();
   }
+  if (mDecodingFrozenAtStateDecoding) {
+    mDecodingFrozenAtStateDecoding = false;
+    DispatchDecodeTasksIfNeeded();
+  }
   // Once we start playing, we don't want to minimize our prerolling, as we
   // assume the user is likely to want to keep playing in future.
   mMinimizePreroll = false;
   ScheduleStateMachine();
 }
 
 void MediaDecoderStateMachine::ResetPlayback()
 {
@@ -1510,16 +1517,18 @@ void MediaDecoderStateMachine::NotifyDat
   }
 }
 
 void MediaDecoderStateMachine::Seek(const SeekTarget& aTarget)
 {
   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
 
+  mDecodingFrozenAtStateDecoding = false;
+
   if (mState == DECODER_STATE_SHUTDOWN) {
     return;
   }
 
   // We need to be able to seek both at a transport level and at a media level
   // to seek.
   if (!mDecoder->IsMediaSeekable()) {
     DECODER_WARN("Seek() function should not be called on a non-seekable state machine");
@@ -1617,16 +1626,21 @@ MediaDecoderStateMachine::DispatchDecode
   AssertCurrentThreadInMonitor();
 
   if (mState != DECODER_STATE_DECODING &&
       mState != DECODER_STATE_BUFFERING &&
       mState != DECODER_STATE_SEEKING) {
     return;
   }
 
+  if (mState == DECODER_STATE_DECODING && mDecodingFrozenAtStateDecoding) {
+    DECODER_LOG("DispatchDecodeTasksIfNeeded return due to "
+                "mFreezeDecodingAtStateDecoding");
+    return;
+  }
   // NeedToDecodeAudio() can go from false to true while we hold the
   // monitor, but it can't go from true to false. This can happen because
   // NeedToDecodeAudio() takes into account the amount of decoded audio
   // that's been written to the AudioStream but not played yet. So if we
   // were calling NeedToDecodeAudio() twice and we thread-context switch
   // between the calls, audio can play, which can affect the return value
   // of NeedToDecodeAudio() giving inconsistent results. So we cache the
   // value returned by NeedToDecodeAudio(), and make decisions
@@ -1967,16 +1981,20 @@ nsresult MediaDecoderStateMachine::Decod
       NS_NewRunnableMethod(this, &MediaDecoderStateMachine::DispatchVideoDecodeTaskIfNeeded));
     VideoQueue().AddPopListener(decodeTask, mDecodeTaskQueue);
   }
 
   if (mScheduler->IsRealTime()) {
     SetStartTime(0);
     res = FinishDecodeMetadata();
     NS_ENSURE_SUCCESS(res, res);
+  } else if (mDecodingFrozenAtStateMetadata) {
+    SetStartTime(mStartTime);
+    res = FinishDecodeMetadata();
+    NS_ENSURE_SUCCESS(res, res);
   } else {
     if (HasAudio()) {
       ReentrantMonitorAutoExit unlock(mDecoder->GetReentrantMonitor());
       mReader->RequestAudioData();
     }
     if (HasVideo()) {
       ReentrantMonitorAutoExit unlock(mDecoder->GetReentrantMonitor());
       mReader->RequestVideoData(false, 0);
@@ -1992,17 +2010,17 @@ MediaDecoderStateMachine::FinishDecodeMe
   AssertCurrentThreadInMonitor();
   NS_ASSERTION(OnDecodeThread(), "Should be on decode thread.");
   DECODER_LOG("FinishDecodeMetadata");
 
   if (mState == DECODER_STATE_SHUTDOWN) {
     return NS_ERROR_FAILURE;
   }
 
-  if (!mScheduler->IsRealTime()) {
+  if (!mScheduler->IsRealTime() && !mDecodingFrozenAtStateMetadata) {
 
     const VideoData* v = VideoQueue().PeekFront();
     const AudioData* a = AudioQueue().PeekFront();
 
     int64_t startTime = std::min<int64_t>(a ? a->mTime : INT64_MAX,
                                           v ? v->mTime : INT64_MAX);
     if (startTime == INT64_MAX) {
       startTime = 0;
@@ -2024,16 +2042,18 @@ MediaDecoderStateMachine::FinishDecodeMe
   MOZ_ASSERT(!(mDecoder->IsMediaSeekable() && mDecoder->IsTransportSeekable()) ||
                GetDuration() != -1,
              "Seekable media should have duration");
   DECODER_LOG("Media goes from %lld to %lld (duration %lld) "
               "transportSeekable=%d, mediaSeekable=%d",
               mStartTime, mEndTime, GetDuration(),
               mDecoder->IsTransportSeekable(), mDecoder->IsMediaSeekable());
 
+  mDecodingFrozenAtStateMetadata = false;
+
   if (HasAudio() && !HasVideo()) {
     // We're playing audio only. We don't need to worry about slow video
     // decodes causing audio underruns, so don't buffer so much audio in
     // order to reduce memory usage.
     mAmpleAudioThresholdUsecs /= NO_VIDEO_AMPLE_AUDIO_DIVISOR;
     mLowAudioThresholdUsecs /= NO_VIDEO_AMPLE_AUDIO_DIVISOR;
   }
 
--- a/dom/media/MediaDecoderStateMachine.h
+++ b/dom/media/MediaDecoderStateMachine.h
@@ -916,12 +916,23 @@ protected:
 
   // Stores presentation info required for playback. The decoder monitor
   // must be held when accessing this.
   MediaInfo mInfo;
 
   mozilla::MediaMetadataManager mMetadataManager;
 
   MediaDecoderOwner::NextFrameStatus mLastFrameStatus;
+
+  // True if we are back from DECODER_STATE_DORMANT state, and we can skip
+  // SetStartTime because the mStartTime already set before. Also we don't need
+  // to decode any audio/video since the MediaDecoder will trigger a seek
+  // operation soon.
+  // mDecodingFrozenAtStateMetadata: turn on/off at
+  //                                 SetDormant/FinishDecodeMetadata.
+  // mDecodingFrozenAtStateDecoding: turn on/off at
+  //                                 SetDormant/Seek,Play.
+  bool mDecodingFrozenAtStateMetadata;
+  bool mDecodingFrozenAtStateDecoding;
 };
 
 } // namespace mozilla;
 #endif
--- a/dom/media/omx/RtspMediaCodecReader.cpp
+++ b/dom/media/omx/RtspMediaCodecReader.cpp
@@ -96,21 +96,9 @@ RtspMediaCodecReader::ReadMetadata(Media
   nsresult rv = MediaCodecReader::ReadMetadata(aInfo, aTags);
   if (rv == NS_OK && !IsWaitingMediaResources()) {
     EnsureActive();
   }
 
   return rv;
 }
 
-// Called on Binder thread.
-void
-RtspMediaCodecReader::codecReserved(Track& aTrack)
-{
-  // TODO: fix me, we need a SeekTime(0) here because the
-  // MediaDecoderStateMachine will update the mStartTime after ReadMetadata.
-  MediaCodecReader::codecReserved(aTrack);
-  if (aTrack.mCodec != nullptr) {
-    mRtspResource->SeekTime(0);
-  }
-}
-
 } // namespace mozilla
--- a/dom/media/omx/RtspMediaCodecReader.h
+++ b/dom/media/omx/RtspMediaCodecReader.h
@@ -60,18 +60,16 @@ public:
                                 int64_t aTimeThreshold) MOZ_OVERRIDE;
 
   // Disptach a DecodeAudioDataTask to decode audio data.
   virtual void RequestAudioData() MOZ_OVERRIDE;
 
   virtual nsresult ReadMetadata(MediaInfo* aInfo,
                                 MetadataTags** aTags) MOZ_OVERRIDE;
 
-  virtual void codecReserved(Track& aTrack) MOZ_OVERRIDE;
-
 private:
   // A pointer to RtspMediaResource for calling the Rtsp specific function.
   // The lifetime of mRtspResource is controlled by MediaDecoder. MediaDecoder
   // holds the MediaDecoderStateMachine and RtspMediaResource.
   // And MediaDecoderStateMachine holds this RtspMediaCodecReader.
   RtspMediaResource* mRtspResource;
 };
 
--- a/dom/svg/SVGRectElement.cpp
+++ b/dom/svg/SVGRectElement.cpp
@@ -2,16 +2,18 @@
 /* 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 "mozilla/dom/SVGRectElement.h"
 #include "nsGkAtoms.h"
 #include "mozilla/dom/SVGRectElementBinding.h"
 #include "mozilla/gfx/2D.h"
+#include "mozilla/gfx/Matrix.h"
+#include "mozilla/gfx/Rect.h"
 #include "mozilla/gfx/PathHelpers.h"
 #include <algorithm>
 
 NS_IMPL_NS_NEW_NAMESPACED_SVG_ELEMENT(Rect)
 
 using namespace mozilla::gfx;
 
 namespace mozilla {
@@ -103,16 +105,46 @@ SVGRectElement::GetLengthInfo()
 {
   return LengthAttributesInfo(mLengthAttributes, sLengthInfo,
                               ArrayLength(sLengthInfo));
 }
 
 //----------------------------------------------------------------------
 // nsSVGPathGeometryElement methods
 
+bool
+SVGRectElement::GetGeometryBounds(Rect* aBounds, Float aStrokeWidth,
+                                  const Matrix& aTransform)
+{
+  Rect r;
+  Float rx, ry;
+  GetAnimatedLengthValues(&r.x, &r.y, &r.width, &r.height, &rx, &ry, nullptr);
+
+  if (r.IsEmpty()) {
+    // Rendering of the element disabled
+    r.SetEmpty(); // make sure width/height are actually zero
+    *aBounds = r;
+    return true;
+  }
+
+  rx = std::max(rx, 0.0f);
+  ry = std::max(ry, 0.0f);
+
+  if (rx != 0 || ry != 0) {
+    return false;
+  }
+
+  if (aStrokeWidth > 0.f) {
+    r.Inflate(aStrokeWidth / 2.f);
+  }
+
+  *aBounds = aTransform.TransformBounds(r);
+  return true;
+}
+
 void
 SVGRectElement::GetAsSimplePath(SimplePath* aSimplePath)
 {
   float x, y, width, height, rx, ry;
   GetAnimatedLengthValues(&x, &y, &width, &height, &rx, &ry, nullptr);
 
   if (width <= 0 || height <= 0) {
     aSimplePath->Reset();
--- a/dom/svg/SVGRectElement.h
+++ b/dom/svg/SVGRectElement.h
@@ -25,16 +25,18 @@ protected:
   friend nsresult (::NS_NewSVGRectElement(nsIContent **aResult,
                                           already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo));
 
 public:
   // nsSVGSVGElement methods:
   virtual bool HasValidDimensions() const MOZ_OVERRIDE;
 
   // nsSVGPathGeometryElement methods:
+  virtual bool GetGeometryBounds(Rect* aBounds, Float aStrokeWidth,
+                                 const Matrix& aTransform) MOZ_OVERRIDE;
   virtual void GetAsSimplePath(SimplePath* aSimplePath) MOZ_OVERRIDE;
   virtual TemporaryRef<Path> BuildPath(PathBuilder* aBuilder = nullptr) MOZ_OVERRIDE;
 
   virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const MOZ_OVERRIDE;
 
   // WebIDL
   already_AddRefed<SVGAnimatedLength> X();
   already_AddRefed<SVGAnimatedLength> Y();
--- a/dom/svg/nsSVGPathGeometryElement.h
+++ b/dom/svg/nsSVGPathGeometryElement.h
@@ -29,16 +29,17 @@ struct nsSVGMark {
 typedef mozilla::dom::SVGGraphicsElement nsSVGPathGeometryElementBase;
 
 class nsSVGPathGeometryElement : public nsSVGPathGeometryElementBase
 {
 protected:
   typedef mozilla::gfx::DrawTarget DrawTarget;
   typedef mozilla::gfx::FillRule FillRule;
   typedef mozilla::gfx::Float Float;
+  typedef mozilla::gfx::Matrix Matrix;
   typedef mozilla::gfx::Path Path;
   typedef mozilla::gfx::Point Point;
   typedef mozilla::gfx::PathBuilder PathBuilder;
   typedef mozilla::gfx::Rect Rect;
 
 public:
   explicit nsSVGPathGeometryElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo);
 
@@ -64,16 +65,21 @@ public:
    * This could be moved up to a more general class so it can be used for non-leaf
    * elements, but that would require care and for now there's no need.
    */
   bool GeometryDependsOnCoordCtx();
 
   virtual bool IsMarkable();
   virtual void GetMarkPoints(nsTArray<nsSVGMark> *aMarks);
 
+  virtual bool GetGeometryBounds(Rect* aBounds, Float aStrokeWidth,
+                                 const Matrix& aTransform) {
+    return false;
+  }
+
   /**
    * For use with GetAsSimplePath.
    */
   class SimplePath
   {
   public:
     SimplePath()
       : mType(NONE)
--- a/dom/workers/RuntimeService.cpp
+++ b/dom/workers/RuntimeService.cpp
@@ -169,16 +169,19 @@ const uint32_t kNoIndex = uint32_t(-1);
 const JS::ContextOptions kRequiredContextOptions =
   JS::ContextOptions().setDontReportUncaught(true);
 
 uint32_t gMaxWorkersPerDomain = MAX_WORKERS_PER_DOMAIN;
 
 // Does not hold an owning reference.
 RuntimeService* gRuntimeService = nullptr;
 
+// Only true during the call to Init.
+bool gRuntimeServiceDuringInit = false;
+
 #ifdef ENABLE_TESTS
 bool gTestPBackground = false;
 #endif // ENABLE_TESTS
 
 enum {
   ID_Worker = 0,
   ID_ChromeWorker,
   ID_Event,
@@ -459,127 +462,132 @@ LoadJSGCMemoryOptions(const char* aPrefN
   // be no just a "mem." pref here.
   if (!rts) {
     NS_ASSERTION(memPrefName.EqualsLiteral(PREF_MEM_OPTIONS_PREFIX), "Huh?!");
   }
 #endif
 
   // If we're running in Init() then do this for every pref we care about.
   // Otherwise we just want to update the parameter that changed.
-  for (uint32_t index = rts ? JSSettings::kGCSettingsArraySize - 1 : 0;
+  for (uint32_t index = !gRuntimeServiceDuringInit
+                          ? JSSettings::kGCSettingsArraySize - 1 : 0;
        index < JSSettings::kGCSettingsArraySize;
        index++) {
     LiteralRebindingCString matchName;
 
     matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX "max");
-    if (memPrefName == matchName || (!rts && index == 0)) {
+    if (memPrefName == matchName || (gRuntimeServiceDuringInit && index == 0)) {
       int32_t prefValue = GetWorkerPref(matchName, -1);
       uint32_t value = (prefValue <= 0 || prefValue >= 0x1000) ?
                        uint32_t(-1) :
                        uint32_t(prefValue) * 1024 * 1024;
       UpdatOtherJSGCMemoryOption(rts, JSGC_MAX_BYTES, value);
       continue;
     }
 
     matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX "high_water_mark");
-    if (memPrefName == matchName || (!rts && index == 1)) {
+    if (memPrefName == matchName || (gRuntimeServiceDuringInit && index == 1)) {
       int32_t prefValue = GetWorkerPref(matchName, 128);
       UpdatOtherJSGCMemoryOption(rts, JSGC_MAX_MALLOC_BYTES,
                                  uint32_t(prefValue) * 1024 * 1024);
       continue;
     }
 
     matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX
                             "gc_high_frequency_time_limit_ms");
-    if (memPrefName == matchName || (!rts && index == 2)) {
+    if (memPrefName == matchName || (gRuntimeServiceDuringInit && index == 2)) {
       UpdateCommonJSGCMemoryOption(rts, matchName,
                                    JSGC_HIGH_FREQUENCY_TIME_LIMIT);
       continue;
     }
 
     matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX
                             "gc_low_frequency_heap_growth");
-    if (memPrefName == matchName || (!rts && index == 3)) {
+    if (memPrefName == matchName || (gRuntimeServiceDuringInit && index == 3)) {
       UpdateCommonJSGCMemoryOption(rts, matchName,
                                    JSGC_LOW_FREQUENCY_HEAP_GROWTH);
       continue;
     }
 
     matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX
                             "gc_high_frequency_heap_growth_min");
-    if (memPrefName == matchName || (!rts && index == 4)) {
+    if (memPrefName == matchName || (gRuntimeServiceDuringInit && index == 4)) {
       UpdateCommonJSGCMemoryOption(rts, matchName,
                                    JSGC_HIGH_FREQUENCY_HEAP_GROWTH_MIN);
       continue;
     }
 
     matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX
                             "gc_high_frequency_heap_growth_max");
-    if (memPrefName == matchName || (!rts && index == 5)) {
+    if (memPrefName == matchName || (gRuntimeServiceDuringInit && index == 5)) {
       UpdateCommonJSGCMemoryOption(rts, matchName,
                                    JSGC_HIGH_FREQUENCY_HEAP_GROWTH_MAX);
       continue;
     }
 
     matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX
                             "gc_high_frequency_low_limit_mb");
-    if (memPrefName == matchName || (!rts && index == 6)) {
+    if (memPrefName == matchName || (gRuntimeServiceDuringInit && index == 6)) {
       UpdateCommonJSGCMemoryOption(rts, matchName,
                                    JSGC_HIGH_FREQUENCY_LOW_LIMIT);
       continue;
     }
 
     matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX
                             "gc_high_frequency_high_limit_mb");
-    if (memPrefName == matchName || (!rts && index == 7)) {
+    if (memPrefName == matchName || (gRuntimeServiceDuringInit && index == 7)) {
       UpdateCommonJSGCMemoryOption(rts, matchName,
                                    JSGC_HIGH_FREQUENCY_HIGH_LIMIT);
       continue;
     }
 
     matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX
                             "gc_allocation_threshold_mb");
-    if (memPrefName == matchName || (!rts && index == 8)) {
+    if (memPrefName == matchName || (gRuntimeServiceDuringInit && index == 8)) {
       UpdateCommonJSGCMemoryOption(rts, matchName, JSGC_ALLOCATION_THRESHOLD);
       continue;
     }
 
     matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX "gc_incremental_slice_ms");
-    if (memPrefName == matchName || (!rts && index == 9)) {
+    if (memPrefName == matchName || (gRuntimeServiceDuringInit && index == 9)) {
       int32_t prefValue = GetWorkerPref(matchName, -1);
       uint32_t value =
         (prefValue <= 0 || prefValue >= 100000) ? 0 : uint32_t(prefValue);
       UpdatOtherJSGCMemoryOption(rts, JSGC_SLICE_TIME_BUDGET, value);
       continue;
     }
 
     matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX "gc_dynamic_heap_growth");
-    if (memPrefName == matchName || (!rts && index == 10)) {
+    if (memPrefName == matchName ||
+        (gRuntimeServiceDuringInit && index == 10)) {
       bool prefValue = GetWorkerPref(matchName, false);
       UpdatOtherJSGCMemoryOption(rts, JSGC_DYNAMIC_HEAP_GROWTH,
                                  prefValue ? 0 : 1);
       continue;
     }
 
     matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX "gc_dynamic_mark_slice");
-    if (memPrefName == matchName || (!rts && index == 11)) {
+    if (memPrefName == matchName ||
+        (gRuntimeServiceDuringInit && index == 11)) {
       bool prefValue = GetWorkerPref(matchName, false);
       UpdatOtherJSGCMemoryOption(rts, JSGC_DYNAMIC_MARK_SLICE,
                                  prefValue ? 0 : 1);
       continue;
     }
 
     matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX "gc_min_empty_chunk_count");
-    if (memPrefName == matchName || (!rts && index == 12)) {
+    if (memPrefName == matchName ||
+        (gRuntimeServiceDuringInit && index == 12)) {
       UpdateCommonJSGCMemoryOption(rts, matchName, JSGC_MIN_EMPTY_CHUNK_COUNT);
       continue;
     }
 
     matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX "gc_max_empty_chunk_count");
-    if (memPrefName == matchName || (!rts && index == 13)) {
+    if (memPrefName == matchName ||
+        (gRuntimeServiceDuringInit && index == 13)) {
       UpdateCommonJSGCMemoryOption(rts, matchName, JSGC_MAX_EMPTY_CHUNK_COUNT);
       continue;
     }
 
 #ifdef DEBUG
     nsAutoCString message("Workers don't support the 'mem.");
     message.Append(memPrefName);
     message.AppendLiteral("' preference!");
@@ -1760,16 +1768,19 @@ RuntimeService::Init()
                                  false))) {
     NS_WARNING("Failed to register for memory pressure notifications!");
   }
 
   if (NS_FAILED(obs->AddObserver(this, NS_IOSERVICE_OFFLINE_STATUS_TOPIC, false))) {
     NS_WARNING("Failed to register for offline notification event!");
   }
 
+  MOZ_ASSERT(!gRuntimeServiceDuringInit, "This should be false!");
+  gRuntimeServiceDuringInit = true;
+
   if (NS_FAILED(Preferences::RegisterCallback(
                                  LoadJSGCMemoryOptions,
                                  PREF_JS_OPTIONS_PREFIX PREF_MEM_OPTIONS_PREFIX,
                                  nullptr)) ||
       NS_FAILED(Preferences::RegisterCallbackAndCall(
                             LoadJSGCMemoryOptions,
                             PREF_WORKERS_OPTIONS_PREFIX PREF_MEM_OPTIONS_PREFIX,
                             nullptr)) ||
@@ -1817,16 +1828,19 @@ RuntimeService::Init()
                                                  nullptr)) ||
       NS_FAILED(Preferences::RegisterCallbackAndCall(
                                                  JSVersionChanged,
                                                  PREF_WORKERS_LATEST_JS_VERSION,
                                                  nullptr))) {
     NS_WARNING("Failed to register pref callbacks!");
   }
 
+  MOZ_ASSERT(gRuntimeServiceDuringInit, "Should be true!");
+  gRuntimeServiceDuringInit = false;
+
   // We assume atomic 32bit reads/writes. If this assumption doesn't hold on
   // some wacky platform then the worst that could happen is that the close
   // handler will run for a slightly different amount of time.
   if (NS_FAILED(Preferences::AddIntVarCache(
                                    &sDefaultJSSettings.content.maxScriptRuntime,
                                    PREF_MAX_SCRIPT_RUN_TIME_CONTENT,
                                    MAX_SCRIPT_RUN_TIME_SEC)) ||
       NS_FAILED(Preferences::AddIntVarCache(
--- a/gfx/layers/apz/src/APZCTreeManager.cpp
+++ b/gfx/layers/apz/src/APZCTreeManager.cpp
@@ -257,17 +257,17 @@ APZCTreeManager::PrepareAPZCForLayer(con
   // that is supposed to scroll together is split into multiple layers because of
   // e.g. non-scrolling content interleaved in z-index order.
   ScrollableLayerGuid guid(aLayersId, aMetrics);
   auto insertResult = aState.mApzcMap.insert(std::make_pair(guid, static_cast<AsyncPanZoomController*>(nullptr)));
   if (!insertResult.second) {
     apzc = insertResult.first->second;
     PrintAPZCInfo(aLayer, apzc);
   }
-  APZCTM_LOG("Found APZC %p for layer %p with identifiers %lld %lld\n", apzc, aLayer.GetLayer(), guid.mLayersId, guid.mScrollId);
+  APZCTM_LOG("Found APZC %p for layer %p with identifiers %" PRId64 " %" PRId64 "\n", apzc, aLayer.GetLayer(), guid.mLayersId, guid.mScrollId);
 
   // If we haven't encountered a layer already with the same metrics, then we need to
   // do the full reuse-or-make-an-APZC algorithm, which is contained inside the block
   // below.
   if (apzc == nullptr) {
     apzc = aLayer.GetApzc();
 
     // If the content represented by the scrollable layer has changed (which may
@@ -310,17 +310,17 @@ APZCTreeManager::PrepareAPZCForLayer(con
       // so that it doesn't continue pointing to APZCs that should no longer
       // be in the tree. These pointers will get reset properly as we continue
       // building the tree. Also remove it from the set of APZCs that are going
       // to be destroyed, because it's going to remain active.
       aState.mApzcsToDestroy.RemoveElement(apzc);
       apzc->SetPrevSibling(nullptr);
       apzc->SetLastChild(nullptr);
     }
-    APZCTM_LOG("Using APZC %p for layer %p with identifiers %lld %lld\n", apzc, aLayer.GetLayer(), aLayersId, aMetrics.GetScrollId());
+    APZCTM_LOG("Using APZC %p for layer %p with identifiers %" PRId64 " %" PRId64 "\n", apzc, aLayer.GetLayer(), aLayersId, aMetrics.GetScrollId());
 
     apzc->NotifyLayersUpdated(aMetrics,
         aState.mIsFirstPaint && (aLayersId == aState.mOriginatingLayersId));
 
     nsIntRegion unobscured = ComputeTouchSensitiveRegion(state->mController, aMetrics, aObscured);
     apzc->SetLayerHitTestData(unobscured, aAncestorTransform);
     APZCTM_LOG("Setting region %s as visible region for APZC %p\n",
         Stringify(unobscured).c_str(), apzc);
--- a/gfx/layers/apz/src/InputQueue.cpp
+++ b/gfx/layers/apz/src/InputQueue.cpp
@@ -100,17 +100,17 @@ InputQueue::ReceiveInputEvent(const nsRe
   block->AddEvent(aEvent.AsMultiTouchInput());
   return result;
 }
 
 uint64_t
 InputQueue::InjectNewTouchBlock(AsyncPanZoomController* aTarget)
 {
   TouchBlockState* block = StartNewTouchBlock(aTarget, true);
-  INPQ_LOG("%p injecting new touch block with id %llu and target %p\n",
+  INPQ_LOG("%p injecting new touch block with id %" PRIu64 " and target %p\n",
     this, block->GetBlockId(), aTarget);
   ScheduleContentResponseTimeout(aTarget, block->GetBlockId());
   return block->GetBlockId();
 }
 
 TouchBlockState*
 InputQueue::StartNewTouchBlock(const nsRefPtr<AsyncPanZoomController>& aTarget, bool aCopyAllowedTouchBehaviorFromCurrent)
 {
@@ -157,51 +157,51 @@ InputQueue::ScheduleContentResponseTimeo
     NewRunnableMethod(this, &InputQueue::ContentResponseTimeout, aInputBlockId),
     gfxPrefs::APZContentResponseTimeout());
 }
 
 void
 InputQueue::ContentResponseTimeout(const uint64_t& aInputBlockId) {
   AsyncPanZoomController::AssertOnControllerThread();
 
-  INPQ_LOG("got a content response timeout; block=%llu\n", aInputBlockId);
+  INPQ_LOG("got a content response timeout; block=%" PRIu64 "\n", aInputBlockId);
   bool success = false;
   for (size_t i = 0; i < mTouchBlockQueue.Length(); i++) {
     if (mTouchBlockQueue[i]->GetBlockId() == aInputBlockId) {
       success = mTouchBlockQueue[i]->TimeoutContentResponse();
       break;
     }
   }
   if (success) {
     ProcessPendingInputBlocks();
   }
 }
 
 void
 InputQueue::ContentReceivedTouch(uint64_t aInputBlockId, bool aPreventDefault) {
   AsyncPanZoomController::AssertOnControllerThread();
 
-  INPQ_LOG("got a content response; block=%llu\n", aInputBlockId);
+  INPQ_LOG("got a content response; block=%" PRIu64 "\n", aInputBlockId);
   bool success = false;
   for (size_t i = 0; i < mTouchBlockQueue.Length(); i++) {
     if (mTouchBlockQueue[i]->GetBlockId() == aInputBlockId) {
       success = mTouchBlockQueue[i]->SetContentResponse(aPreventDefault);
       break;
     }
   }
   if (success) {
     ProcessPendingInputBlocks();
   }
 }
 
 void
 InputQueue::SetAllowedTouchBehavior(uint64_t aInputBlockId, const nsTArray<TouchBehaviorFlags>& aBehaviors) {
   AsyncPanZoomController::AssertOnControllerThread();
 
-  INPQ_LOG("got allowed touch behaviours; block=%llu\n", aInputBlockId);
+  INPQ_LOG("got allowed touch behaviours; block=%" PRIu64 "\n", aInputBlockId);
   bool success = false;
   for (size_t i = 0; i < mTouchBlockQueue.Length(); i++) {
     if (mTouchBlockQueue[i]->GetBlockId() == aInputBlockId) {
       success = mTouchBlockQueue[i]->SetAllowedTouchBehaviors(aBehaviors);
       break;
     }
   }
   if (success) {
--- a/gfx/tests/gtest/TestAsyncPanZoomController.cpp
+++ b/gfx/tests/gtest/TestAsyncPanZoomController.cpp
@@ -1516,17 +1516,16 @@ TEST_F(APZCGestureDetectorTester, Double
   MakeApzcZoomable();
 
   EXPECT_CALL(*mcc, HandleSingleTap(CSSPoint(10, 10), 0, apzc->GetGuid())).Times(0);
   EXPECT_CALL(*mcc, HandleDoubleTap(CSSPoint(10, 10), 0, apzc->GetGuid())).Times(0);
 
   int time = 0;
   uint64_t blockIds[2];
   ApzcDoubleTapAndCheckStatus(apzc, 10, 10, time, &blockIds);
-printf_stderr("blockids %llu %llu\n", blockIds[0], blockIds[1]);
 
   // responses to the two touchstarts
   apzc->ContentReceivedTouch(blockIds[0], true);
   apzc->ContentReceivedTouch(blockIds[1], true);
 
   while (mcc->RunThroughDelayedTasks());
 
   apzc->AssertStateIsReset();
--- a/gfx/thebes/gfxContext.cpp
+++ b/gfx/thebes/gfxContext.cpp
@@ -320,32 +320,16 @@ gfxContext::CurveTo(const gfxPoint& pt1,
 void
 gfxContext::QuadraticCurveTo(const gfxPoint& pt1, const gfxPoint& pt2)
 {
   EnsurePathBuilder();
   mPathBuilder->QuadraticBezierTo(ToPoint(pt1), ToPoint(pt2));
 }
 
 void
-gfxContext::Arc(const gfxPoint& center, gfxFloat radius,
-                gfxFloat angle1, gfxFloat angle2)
-{
-  EnsurePathBuilder();
-  mPathBuilder->Arc(ToPoint(center), Float(radius), Float(angle1), Float(angle2));
-}
-
-void
-gfxContext::NegativeArc(const gfxPoint& center, gfxFloat radius,
-                        gfxFloat angle1, gfxFloat angle2)
-{
-  EnsurePathBuilder();
-  mPathBuilder->Arc(ToPoint(center), Float(radius), Float(angle2), Float(angle1));
-}
-
-void
 gfxContext::Line(const gfxPoint& start, const gfxPoint& end)
 {
   EnsurePathBuilder();
   mPathBuilder->MoveTo(ToPoint(start));
   mPathBuilder->LineTo(ToPoint(end));
 }
 
 // XXX snapToPixels is only valid when snapping for filled
--- a/gfx/thebes/gfxContext.h
+++ b/gfx/thebes/gfxContext.h
@@ -130,17 +130,17 @@ public:
     void ClosePath();
 
     /**
      * Returns the current path.
      */
     mozilla::TemporaryRef<Path> GetPath();
 
     /**
-     * Appends the given path to the current path.
+     * Sets the given path as the current path.
      */
     void SetPath(Path* path);
 
     /**
      * Moves the pen to a new point without drawing a line.
      */
     void MoveTo(const gfxPoint& pt);
 
@@ -167,37 +167,16 @@ public:
      */
     void CurveTo(const gfxPoint& pt1, const gfxPoint& pt2, const gfxPoint& pt3);
 
     /**
      * Draws a quadratic Bézier curve with control points pt1, pt2 and pt3.
      */
     void QuadraticCurveTo(const gfxPoint& pt1, const gfxPoint& pt2);
 
-    /**
-     * Draws a clockwise arc (i.e. a circle segment).
-     * @param center The center of the circle
-     * @param radius The radius of the circle
-     * @param angle1 Starting angle for the segment
-     * @param angle2 Ending angle
-     */
-    void Arc(const gfxPoint& center, gfxFloat radius,
-             gfxFloat angle1, gfxFloat angle2);
-
-    /**
-     * Draws a counter-clockwise arc (i.e. a circle segment).
-     * @param center The center of the circle
-     * @param radius The radius of the circle
-     * @param angle1 Starting angle for the segment
-     * @param angle2 Ending angle
-     */
-
-    void NegativeArc(const gfxPoint& center, gfxFloat radius,
-                     gfxFloat angle1, gfxFloat angle2);
-
     // path helpers
     /**
      * Draws a line from start to end.
      */
     void Line(const gfxPoint& start, const gfxPoint& end); // XXX snapToPixels option?
 
     /**
      * Draws the rectangle given by rect.
--- a/gfx/thebes/gfxWindowsPlatform.cpp
+++ b/gfx/thebes/gfxWindowsPlatform.cpp
@@ -1567,16 +1567,26 @@ bool DoesD3D11DeviceSupportResourceShari
   return true;
 }
 
 void
 gfxWindowsPlatform::InitD3D11Devices()
 {
   mD3D11DeviceInitialized = true;
 
+  nsCOMPtr<nsIGfxInfo> gfxInfo = do_GetService("@mozilla.org/gfx/info;1");
+  if (gfxInfo) {
+    int32_t status;
+    if (NS_SUCCEEDED(gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_DIRECT3D_11_LAYERS, &status))) {
+      if (status != nsIGfxInfo::FEATURE_STATUS_OK) {
+        return;
+      }
+    }
+  }
+
   nsModuleHandle d3d11Module(LoadLibrarySystem32(L"d3d11.dll"));
   decltype(D3D11CreateDevice)* d3d11CreateDevice = (decltype(D3D11CreateDevice)*)
     GetProcAddress(d3d11Module, "D3D11CreateDevice");
 
   if (!d3d11CreateDevice) {
     return;
   }
 
--- a/intl/locale/PluralForm.jsm
+++ b/intl/locale/PluralForm.jsm
@@ -130,17 +130,17 @@ this.PluralForm = {
       let words = aWords ? aWords.split(/;/) : [""];
 
       // Explicitly check bounds to avoid strict warnings
       let ret = index < words.length ? words[index] : undefined;
 
       // Check for array out of bounds or empty strings
       if ((ret == undefined) || (ret == "")) {
         // Report the caller to help figure out who is causing badness
-        let caller = PluralForm.get.caller ? PluralForm.get.caller.name : "top";
+        let caller = Components.stack.caller ? Components.stack.caller.name : "top";
 
         // Display a message in the error console
         log(["Index #", index, " of '", aWords, "' for value ", aNum,
             " is invalid -- plural rule #", aRuleNum, "; called by ", caller]);
 
         // Default to the first entry (which might be empty, but not undefined)
         ret = words[0];
       }
new file mode 100644
--- /dev/null
+++ b/intl/locale/tests/unit/test_bug1086527.js
@@ -0,0 +1,20 @@
+/* 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/. */
+
+/**
+ * This unit test makes sure that PluralForm.get can be called from strict mode
+ */
+
+Components.utils.import("resource://gre/modules/PluralForm.jsm");
+
+delete PluralForm.numForms;
+delete PluralForm.get;
+[PluralForm.get, PluralForm.numForms] = PluralForm.makeGetter(9);
+
+function run_test() {
+  "use strict";
+
+  do_check_eq(3, PluralForm.numForms());
+  do_check_eq("one", PluralForm.get(5, 'one;many'));
+}
--- a/intl/locale/tests/unit/xpcshell.ini
+++ b/intl/locale/tests/unit/xpcshell.ini
@@ -8,11 +8,12 @@ run-if = toolkit == "windows" || toolkit
 
 [test_bug371611.js]
 [test_bug374040.js]
 skip-if = toolkit == "windows" || toolkit == "cocoa"
 
 [test_collation_mac_icu.js]
 run-if = toolkit == "cocoa"
 
+[test_bug1086527.js]
 [test_pluralForm.js]
 [test_pluralForm_english.js]
 [test_pluralForm_makeGetter.js]
--- a/js/src/builtin/SIMD.cpp
+++ b/js/src/builtin/SIMD.cpp
@@ -461,17 +461,17 @@ js::CreateSimd(JSContext *cx, typename V
 
 template JSObject *js::CreateSimd<Float32x4>(JSContext *cx, Float32x4::Elem *data);
 template JSObject *js::CreateSimd<Int32x4>(JSContext *cx, Int32x4::Elem *data);
 
 namespace js {
 // Unary SIMD operators
 template<typename T>
 struct Abs {
-    static inline T apply(T x) { return x < 0 ? -1 * x : x; }
+    static inline T apply(T x) { return mozilla::Abs(x); }
 };
 template<typename T>
 struct Neg {
     static inline T apply(T x) { return -1 * x; }
 };
 template<typename T>
 struct Not {
     static inline T apply(T x) { return ~x; }
@@ -707,24 +707,24 @@ static bool
 Swizzle(JSContext *cx, unsigned argc, Value *vp)
 {
     typedef typename V::Elem Elem;
 
     CallArgs args = CallArgsFromVp(argc, vp);
     if (args.length() != (V::lanes + 1) || !IsVectorObject<V>(args[0]))
         return ErrorBadArgs(cx);
 
-    int32_t lanes[V::lanes];
+    uint32_t lanes[V::lanes];
     for (unsigned i = 0; i < V::lanes; i++) {
         int32_t lane = -1;
         if (!ToInt32(cx, args[i + 1], &lane))
             return false;
-        if (lane < 0 || lane >= V::lanes)
+        if (lane < 0 || uint32_t(lane) >= V::lanes)
             return ErrorBadArgs(cx);
-        lanes[i] = lane;
+        lanes[i] = uint32_t(lane);
     }
 
     Elem *val = TypedObjectMemory<Elem *>(args[0]);
 
     Elem result[V::lanes];
     for (unsigned i = 0; i < V::lanes; i++)
         result[i] = val[lanes[i]];
 
@@ -736,24 +736,24 @@ static bool
 Shuffle(JSContext *cx, unsigned argc, Value *vp)
 {
     typedef typename V::Elem Elem;
 
     CallArgs args = CallArgsFromVp(argc, vp);
     if (args.length() != (V::lanes + 2) || !IsVectorObject<V>(args[0]) || !IsVectorObject<V>(args[1]))
         return ErrorBadArgs(cx);
 
-    int32_t lanes[V::lanes];
+    uint32_t lanes[V::lanes];
     for (unsigned i = 0; i < V::lanes; i++) {
         int32_t lane = -1;
         if (!ToInt32(cx, args[i + 2], &lane))
             return false;
-        if (lane < 0 || lane >= (2 * V::lanes))
+        if (lane < 0 || uint32_t(lane) >= (2 * V::lanes))
             return ErrorBadArgs(cx);
-        lanes[i] = lane;
+        lanes[i] = uint32_t(lane);
     }
 
     Elem *lhs = TypedObjectMemory<Elem *>(args[0]);
     Elem *rhs = TypedObjectMemory<Elem *>(args[1]);
 
     Elem result[V::lanes];
     for (unsigned i = 0; i < V::lanes; i++) {
         Elem *selectedInput = lanes[i] < V::lanes ? lhs : rhs;
--- a/js/src/tests/ecma_6/TypedObject/simd/float32x4abs.js
+++ b/js/src/tests/ecma_6/TypedObject/simd/float32x4abs.js
@@ -3,22 +3,34 @@ var BUGNUMBER = 946042;
 var float32x4 = SIMD.float32x4;
 var int32x4 = SIMD.int32x4;
 
 var summary = 'float32x4 abs';
 
 function test() {
   print(BUGNUMBER + ": " + summary);
 
-  // FIXME -- Bug 948379: Amend to check for correctness of border cases.
-
   var a = float32x4(-1, 2, -3, 4);
   var c = SIMD.float32x4.abs(a);
   assertEq(c.x, 1);
   assertEq(c.y, 2);
   assertEq(c.z, 3);
   assertEq(c.w, 4);
 
+  var d = float32x4(-1.63, 2.46, -3.17, 4.94);
+  var f = SIMD.float32x4.abs(d);
+  assertEq(f.x, Math.fround(1.63));
+  assertEq(f.y, Math.fround(2.46));
+  assertEq(f.z, Math.fround(3.17));
+  assertEq(f.w, Math.fround(4.94));
+
+  var g = float32x4(NaN, -0, Infinity, -Infinity);
+  var i = SIMD.float32x4.abs(g);
+  assertEq(i.x, NaN);
+  assertEq(i.y, 0);
+  assertEq(i.z, Infinity);
+  assertEq(i.w, Infinity);
+
   if (typeof reportCompare === "function")
     reportCompare(true, true);
 }
 
 test();
--- a/js/src/tests/ecma_6/TypedObject/simd/float32x4add.js
+++ b/js/src/tests/ecma_6/TypedObject/simd/float32x4add.js
@@ -1,25 +1,43 @@
 // |reftest| skip-if(!this.hasOwnProperty("SIMD"))
 var BUGNUMBER = 946042;
 var float32x4 = SIMD.float32x4;
 var int32x4 = SIMD.int32x4;
 
 var summary = 'float32x4 add';
 
+function addf(a, b) {
+  return Math.fround(Math.fround(a) + Math.fround(b));
+}
+
 function test() {
   print(BUGNUMBER + ": " + summary);
 
-  // FIXME -- Bug 948379: Amend to check for correctness of border cases.
-
   var a = float32x4(1, 2, 3, 4);
   var b = float32x4(10, 20, 30, 40);
   var c = SIMD.float32x4.add(a, b);
   assertEq(c.x, 11);
   assertEq(c.y, 22);
   assertEq(c.z, 33);
   assertEq(c.w, 44);
 
+  var d = float32x4(1.57, 2.27, 3.57, 4.19);
+  var e = float32x4(10.31, 20.49, 30.41, 40.72);
+  var f = SIMD.float32x4.add(d, e);
+  assertEq(f.x, addf(1.57, 10.31));
+  assertEq(f.y, addf(2.27, 20.49));
+  assertEq(f.z, addf(3.57, 30.41));
+  assertEq(f.w, addf(4.19, 40.72));
+
+  var g = float32x4(NaN, -0, Infinity, -Infinity);
+  var h = float32x4(0, -0, -Infinity, -Infinity);
+  var i = SIMD.float32x4.add(g, h);
+  assertEq(i.x, NaN);
+  assertEq(i.y, -0);
+  assertEq(i.z, NaN);
+  assertEq(i.w, -Infinity);
+
   if (typeof reportCompare === "function")
     reportCompare(true, true);
 }
 
 test();
--- a/js/src/tests/ecma_6/TypedObject/simd/float32x4and.js
+++ b/js/src/tests/ecma_6/TypedObject/simd/float32x4and.js
@@ -14,24 +14,38 @@ var andf = (function() {
         i[2] = i[0] & i[1];
         return f[2];
     }
 })();
 
 function test() {
   print(BUGNUMBER + ": " + summary);
 
-  // FIXME -- Bug 948379: Amend to check for correctness of border cases.
-
   var a = float32x4(1, 2, 3, 4);
   var b = float32x4(10, 20, 30, 40);
   var c = SIMD.float32x4.and(a, b);
   assertEq(c.x, andf(1, 10));
   assertEq(c.y, andf(2, 20));
   assertEq(c.z, andf(3, 30));
   assertEq(c.w, andf(4, 40));
 
+  var d = float32x4(1.51, 2.98, 3.65, 4.34);
+  var e = float32x4(10.29, 20.12, 30.79, 40.41);
+  var f = SIMD.float32x4.and(d, e);
+  assertEq(f.x, andf(1.51, 10.29));
+  assertEq(f.y, andf(2.98, 20.12));
+  assertEq(f.z, andf(3.65, 30.79));
+  assertEq(f.w, andf(4.34, 40.41));
+
+  var g = float32x4(NaN, -0, Infinity, -Infinity);
+  var h = float32x4(NaN, -0, -Infinity, Infinity);
+  var i = SIMD.float32x4.and(g, h);
+  assertEq(i.x, NaN);
+  assertEq(i.y, -0);
+  assertEq(i.z, Infinity);
+  assertEq(i.w, Infinity);
+
   if (typeof reportCompare === "function")
     reportCompare(true, true);
 }
 
 test();
 
--- a/js/src/tests/ecma_6/TypedObject/simd/float32x4clamp.js
+++ b/js/src/tests/ecma_6/TypedObject/simd/float32x4clamp.js
@@ -3,23 +3,43 @@ var BUGNUMBER = 946042;
 var float32x4 = SIMD.float32x4;
 var int32x4 = SIMD.int32x4;
 
 var summary = 'float32x4 clamp';
 
 function test() {
   print(BUGNUMBER + ": " + summary);
 
-  var a = float32x4(-13.37, 10.46, 31.79, 0.54);
-  var lower = float32x4(2.1, 1.1, 50.13, 0.0);
-  var upper = float32x4(2.56, 5.55, 55.93, 1.1);
+  // FIXME -- Bug 1068028: Amend to check for correctness of NaN border cases once the semantics are defined.
+
+  var a = float32x4(-20, 10, 30, 0.5);
+  var lower = float32x4(2, 1, 50, 0);
+  var upper = float32x4(2.5, 5, 55, 1);
   var c = SIMD.float32x4.clamp(a, lower, upper);
-  assertEq(c.x, Math.fround(2.1));
-  assertEq(c.y, Math.fround(5.55));
-  assertEq(c.z, Math.fround(50.13));
-  assertEq(c.w, Math.fround(0.54));
+  assertEq(c.x, 2);
+  assertEq(c.y, 5);
+  assertEq(c.z, 50);
+  assertEq(c.w, 0.5);
+
+  var d = float32x4(-13.37, 10.46, 31.79, 0.54);
+  var lower1 = float32x4(2.1, 1.1, 50.13, 0.0);
+  var upper1 = float32x4(2.56, 5.55, 55.93, 1.1);
+  var f = SIMD.float32x4.clamp(d, lower1, upper1);
+  assertEq(f.x, Math.fround(2.1));
+  assertEq(f.y, Math.fround(5.55));
+  assertEq(f.z, Math.fround(50.13));
+  assertEq(f.w, Math.fround(0.54));
+
+  var g = float32x4(4, -Infinity, 10, -10);
+  var lower2 = float32x4(5, -Infinity, -Infinity, -Infinity);
+  var upper2 = float32x4(Infinity, 5, Infinity, Infinity);
+  var i = SIMD.float32x4.clamp(g, lower2, upper2);
+  assertEq(i.x, 5);
+  assertEq(i.y, -Infinity);
+  assertEq(i.z, 10);
+  assertEq(i.w, -10);
 
   if (typeof reportCompare === "function")
     reportCompare(true, true);
 }
 
 test();
 
--- a/js/src/tests/ecma_6/TypedObject/simd/float32x4div.js
+++ b/js/src/tests/ecma_6/TypedObject/simd/float32x4div.js
@@ -1,26 +1,43 @@
 // |reftest| skip-if(!this.hasOwnProperty("SIMD"))
 var BUGNUMBER = 946042;
 var float32x4 = SIMD.float32x4;
 var int32x4 = SIMD.int32x4;
 
 var summary = 'float32x4 div';
 
+function divf(a, b) {
+    return Math.fround(Math.fround(a) / Math.fround(b));
+}
+
 function test() {
   print(BUGNUMBER + ": " + summary);
 
-  // FIXME -- Bug 948379: Amend to check for correctness of border cases.
-
   var a = float32x4(1, 2, 3, 4);
   var b = float32x4(10, 20, 30, 40);
-  var c = SIMD.float32x4.div(b,a);
+  var c = SIMD.float32x4.div(b, a);
   assertEq(c.x, 10);
   assertEq(c.y, 10);
   assertEq(c.z, 10);
   assertEq(c.w, 10);
 
+  var d = float32x4(1.26, 2.03, 3.17, 4.59);
+  var e = float32x4(11.025, 17.3768, 29.1957, 46.4049);
+  var f = SIMD.float32x4.div(e, d);
+  assertEq(f.x, divf(11.025, 1.26));
+  assertEq(f.y, divf(17.3768, 2.03));
+  assertEq(f.z, divf(29.1957, 3.17));
+  assertEq(f.w, divf(46.4049, 4.59));
+
+  var g = float32x4(0, -0, Infinity, -Infinity);
+  var h = float32x4(1, 1, -Infinity, Infinity);
+  var i = SIMD.float32x4.div(h, g);
+  assertEq(i.x, Infinity);
+  assertEq(i.y, -Infinity);
+  assertEq(i.z, NaN);
+  assertEq(i.w, NaN);
+
   if (typeof reportCompare === "function")
     reportCompare(true, true);
 }
 
 test();
-
--- a/js/src/tests/ecma_6/TypedObject/simd/float32x4equal.js
+++ b/js/src/tests/ecma_6/TypedObject/simd/float32x4equal.js
@@ -3,24 +3,32 @@ var BUGNUMBER = 946042;
 var float32x4 = SIMD.float32x4;
 var int32x4 = SIMD.int32x4;
 
 var summary = 'float32x4 equal';
 
 function test() {
   print(BUGNUMBER + ": " + summary);
 
-  // FIXME -- Bug 948379: Amend to check for correctness of border cases.
+  // FIXME -- Bug 1081697: Amend to check for correctness of NaN/-0/Infinity/-Infinity border cases.
 
   var a = float32x4(1, 20, 30, 40);
   var b = float32x4(10, 20, 30, 4);
   var c = SIMD.float32x4.equal(a, b);
   assertEq(c.x, 0);
   assertEq(c.y, -1);
   assertEq(c.z, -1);
   assertEq(c.w, 0);
 
+  var d = float32x4(1.89, 20.51, 30.46, 40.12);
+  var e = float32x4(10.89, 20.51, Math.fround(30.46), 4.12);
+  var f = SIMD.float32x4.equal(d, e);
+  assertEq(c.x, 0);
+  assertEq(c.y, -1);
+  assertEq(c.z, -1);
+  assertEq(c.w, 0);
+
   if (typeof reportCompare === "function")
     reportCompare(true, true);
 }
 
 test();
 
--- a/js/src/tests/ecma_6/TypedObject/simd/float32x4fromint32x4.js
+++ b/js/src/tests/ecma_6/TypedObject/simd/float32x4fromint32x4.js
@@ -3,23 +3,33 @@ var BUGNUMBER = 946042;
 var float32x4 = SIMD.float32x4;
 var int32x4 = SIMD.int32x4;
 
 var summary = 'float32x4 fromInt32x4';
 
 function test() {
   print(BUGNUMBER + ": " + summary);
 
-  // FIXME -- Bug 948379: Amend to check for correctness of border cases.
+  var INT32_MAX = Math.pow(2, 31) - 1;
+  var INT32_MIN = -Math.pow(2, 31);
 
   var a = int32x4(1, 2, 3, 4);
   var c = SIMD.float32x4.fromInt32x4(a);
   assertEq(c.x, 1);
   assertEq(c.y, 2);
   assertEq(c.z, 3);
   assertEq(c.w, 4);
 
+  var value1 = Math.pow(2, 30) - 1;
+  var value2 = -Math.pow(2, 30);
+  var d = int32x4(INT32_MIN, INT32_MAX, value1, value2);
+  var f = float32x4.fromInt32x4(d);
+  assertEq(f.x, Math.fround(INT32_MIN));
+  assertEq(f.y, Math.fround(INT32_MAX));
+  assertEq(f.z, Math.fround(value1));
+  assertEq(f.w, Math.fround(value2));
+
   if (typeof reportCompare === "function")
     reportCompare(true, true);
 }
 
 test();
 
--- a/js/src/tests/ecma_6/TypedObject/simd/float32x4fromint32x4bits.js
+++ b/js/src/tests/ecma_6/TypedObject/simd/float32x4fromint32x4bits.js
@@ -3,23 +3,31 @@ var BUGNUMBER = 946042;
 var float32x4 = SIMD.float32x4;
 var int32x4 = SIMD.int32x4;
 
 var summary = 'float32x4 fromInt32x4Bits';
 
 function test() {
   print(BUGNUMBER + ": " + summary);
 
-  // FIXME -- Bug 948379: Amend to check for correctness of border cases.
+  var INT32_MAX = Math.pow(2, 31) - 1;
+  var INT32_MIN = -Math.pow(2, 31);
 
   var a = int32x4(100, 200, 300, 400);
   var c = SIMD.float32x4.fromInt32x4Bits(a);
   assertEq(c.x, 1.401298464324817e-43);
   assertEq(c.y, 2.802596928649634e-43);
   assertEq(c.z, 4.203895392974451e-43);
   assertEq(c.w, 5.605193857299268e-43);
 
+  var d = int32x4(INT32_MIN, INT32_MAX, 0, 0);
+  var f = float32x4.fromInt32x4Bits(d);
+  assertEq(f.x, -0);
+  assertEq(f.y, NaN);
+  assertEq(f.z, 0);
+  assertEq(f.w, 0);
+
   if (typeof reportCompare === "function")
     reportCompare(true, true);
 }
 
 test();
 
--- a/js/src/tests/ecma_6/TypedObject/simd/float32x4greaterthan.js
+++ b/js/src/tests/ecma_6/TypedObject/simd/float32x4greaterthan.js
@@ -3,24 +3,33 @@ var BUGNUMBER = 946042;
 var float32x4 = SIMD.float32x4;
 var int32x4 = SIMD.int32x4;
 
 var summary = 'float32x4 greaterThan';
 
 function test() {
   print(BUGNUMBER + ": " + summary);
 
-  // FIXME -- Bug 948379: Amend to check for correctness of border cases.
+  // FIXME -- Bug 1081697: Amend to check for correctness of -0/Infinity/-Infinity border cases.
+  // FIXME -- Bug 1068028: Amend to check for correctness of NaN border cases once the semantics are defined.
 
   var a = float32x4(1, 20, 3, 40);
   var b = float32x4(10, 2, 30, 4);
-  var c = SIMD.float32x4.greaterThan(b,a);
+  var c = SIMD.float32x4.greaterThan(b, a);
   assertEq(c.x, -1);
   assertEq(c.y, 0);
   assertEq(c.z, -1);
   assertEq(c.w, 0);
 
+  var d = float32x4(10.8399, 20.37, 3.07, 4.6802);
+  var e = float32x4(10.8401, 20.367, 3.1, 4.6801);
+  var f = float32x4.greaterThan(e, d);
+  assertEq(f.x, -1);
+  assertEq(f.y, 0);
+  assertEq(f.z, -1);
+  assertEq(f.w, 0);
+
   if (typeof reportCompare === "function")
     reportCompare(true, true);
 }
 
 test();
 
--- a/js/src/tests/ecma_6/TypedObject/simd/float32x4greaterthanorequal.js
+++ b/js/src/tests/ecma_6/TypedObject/simd/float32x4greaterthanorequal.js
@@ -3,24 +3,33 @@ var BUGNUMBER = 946042;
 var float32x4 = SIMD.float32x4;
 var int32x4 = SIMD.int32x4;
 
 var summary = 'float32x4 greaterThanOrEqual';
 
 function test() {
   print(BUGNUMBER + ": " + summary);
 
-  // FIXME -- Bug 948379: Amend to check for correctness of border cases.
+  // FIXME -- Bug 1081697: Amend to check for correctness of -0/Infinity/-Infinity border cases.
+  // FIXME -- Bug 1068028: Amend to check for correctness of NaN border cases once the semantics are defined.
 
   var a = float32x4(1, 20, 30, 40);
   var b = float32x4(10, 20, 30, 4);
-  var c = SIMD.float32x4.greaterThanOrEqual(b,a);
+  var c = SIMD.float32x4.greaterThanOrEqual(b, a);
   assertEq(c.x, -1);
   assertEq(c.y, -1);
   assertEq(c.z, -1);
   assertEq(c.w, 0);
 
+  var d = float32x4(10.029, 20.87, 30.56, 4.7);
+  var e = float32x4(10.03, 20.87, 30.56, 4.698);
+  var f = float32x4.greaterThanOrEqual(e, d);
+  assertEq(f.x, -1);
+  assertEq(f.y, -1);
+  assertEq(f.z, -1);
+  assertEq(f.w, 0);
+
   if (typeof reportCompare === "function")
     reportCompare(true, true);
 }
 
 test();
 
--- a/js/src/tests/ecma_6/TypedObject/simd/float32x4lessthan.js
+++ b/js/src/tests/ecma_6/TypedObject/simd/float32x4lessthan.js
@@ -3,24 +3,33 @@ var BUGNUMBER = 946042;
 var float32x4 = SIMD.float32x4;
 var int32x4 = SIMD.int32x4;
 
 var summary = 'float32x4 lessThan';
 
 function test() {
   print(BUGNUMBER + ": " + summary);
 
-  // FIXME -- Bug 948379: Amend to check for correctness of border cases.
+  // FIXME -- Bug 1081697: Amend to check for correctness of -0/Infinity/-Infinity border cases.
+  // FIXME -- Bug 1068028: Amend to check for correctness of NaN border cases once the semantics are defined.
 
   var a = float32x4(1, 20, 3, 40);
   var b = float32x4(10, 2, 30, 4);
   var c = SIMD.float32x4.lessThan(a, b);
   assertEq(c.x, -1);
   assertEq(c.y, 0);
   assertEq(c.z, -1);
   assertEq(c.w, 0);
 
+  var d = float32x4(1.5399, 20.001, 30.045, 4.74);
+  var e = float32x4(1.54, 19.999, 30.05, 4.72);
+  var f = float32x4.lessThan(a, b);
+  assertEq(f.x, -1);
+  assertEq(f.y, 0);
+  assertEq(f.z, -1);
+  assertEq(f.w, 0);
+
   if (typeof reportCompare === "function")
     reportCompare(true, true);
 }
 
 test();
 
--- a/js/src/tests/ecma_6/TypedObject/simd/float32x4lessthanorequal.js
+++ b/js/src/tests/ecma_6/TypedObject/simd/float32x4lessthanorequal.js
@@ -3,24 +3,33 @@ var BUGNUMBER = 946042;
 var float32x4 = SIMD.float32x4;
 var int32x4 = SIMD.int32x4;
 
 var summary = 'float32x4 lessThanOrEqual';
 
 function test() {
   print(BUGNUMBER + ": " + summary);
 
-  // FIXME -- Bug 948379: Amend to check for correctness of border cases.
+  // FIXME -- Bug 1081697: Amend to check for correctness of -0/Infinity/-Infinity border cases.
+  // FIXME -- Bug 1068028: Amend to check for correctness of NaN border cases once the semantics are defined.
 
   var a = float32x4(1, 20, 30, 40);
   var b = float32x4(10, 20, 30, 4);
   var c = SIMD.float32x4.lessThanOrEqual(a, b);
   assertEq(c.x, -1);
   assertEq(c.y, -1);
   assertEq(c.z, -1);
   assertEq(c.w, 0);
 
+  var d = float32x4(9.999, 20.78, 30.14, 40.1235);
+  var e = float32x4(10, 20.78, 30.14, 40.123);
+  var f = float32x4.lessThanOrEqual(d, e);
+  assertEq(f.x, -1);
+  assertEq(f.y, -1);
+  assertEq(f.z, -1);
+  assertEq(f.w, 0);
+
   if (typeof reportCompare === "function")
     reportCompare(true, true);
 }
 
 test();
 
--- a/js/src/tests/ecma_6/TypedObject/simd/float32x4max.js
+++ b/js/src/tests/ecma_6/TypedObject/simd/float32x4max.js
@@ -3,24 +3,33 @@ var BUGNUMBER = 946042;
 var float32x4 = SIMD.float32x4;
 var int32x4 = SIMD.int32x4;
 
 var summary = 'float32x4 max';
 
 function test() {
   print(BUGNUMBER + ": " + summary);
 
-  // FIXME -- Bug 948379: Amend to check for correctness of border cases.
+  // FIXME -- Bug 1081697: Amend to check for correctness of -0/Infinity/-Infinity border cases.
+  // FIXME -- Bug 1068028: Amend to check for correctness of NaN border cases once the semantics are defined.
 
   var a = float32x4(1, 20, 30, 4);
   var b = float32x4(10, 2, 3, 40);
   var c = SIMD.float32x4.max(a, b);
   assertEq(c.x, 10);
   assertEq(c.y, 20);
   assertEq(c.z, 30);
   assertEq(c.w, 40);
 
+  var d = float32x4(9.999, 2.1234, 30.4443, 4);
+  var e = float32x4(10, 2.1233, 30.4444, 4.0001);
+  var f = float32x4.max(d, e);
+  assertEq(f.x, 10);
+  assertEq(f.y, Math.fround(2.1234));
+  assertEq(f.z, Math.fround(30.4444));
+  assertEq(f.w, Math.fround(4.0001));
+
   if (typeof reportCompare === "function")
     reportCompare(true, true);
 }
 
 test();
 
--- a/js/src/tests/ecma_6/TypedObject/simd/float32x4min.js
+++ b/js/src/tests/ecma_6/TypedObject/simd/float32x4min.js
@@ -3,24 +3,33 @@ var BUGNUMBER = 946042;
 var float32x4 = SIMD.float32x4;
 var int32x4 = SIMD.int32x4;
 
 var summary = 'float32x4 min';
 
 function test() {
   print(BUGNUMBER + ": " + summary);
 
-  // FIXME -- Bug 948379: Amend to check for correctness of border cases.
+  // FIXME -- Bug 1081697: Amend to check for correctness of -0/Infinity/-Infinity border cases.
+  // FIXME -- Bug 1068028: Amend to check for correctness of NaN border cases once the semantics are defined.
 
   var a = float32x4(1, 20, 3, 40);
   var b = float32x4(10, 2, 30, 4);
   var c = SIMD.float32x4.min(a, b);
   assertEq(c.x, 1);
   assertEq(c.y, 2);
   assertEq(c.z, 3);
   assertEq(c.w, 4);
 
+  var d = float32x4(1.4321, 20.5567, 30.8999, 4.0002);
+  var e = float32x4(1.432, 20.5568, 30.8998, 4.0001);
+  var f = float32x4.min(d, e);
+  assertEq(f.x, Math.fround(1.432));
+  assertEq(f.y, Math.fround(20.5567));
+  assertEq(f.z, Math.fround(30.8998));
+  assertEq(f.w, Math.fround(4.0001));
+
   if (typeof reportCompare === "function")
     reportCompare(true, true);
 }
 
 test();
 
--- a/js/src/tests/ecma_6/TypedObject/simd/float32x4mul.js
+++ b/js/src/tests/ecma_6/TypedObject/simd/float32x4mul.js
@@ -1,26 +1,43 @@
 // |reftest| skip-if(!this.hasOwnProperty("SIMD"))
 var BUGNUMBER = 946042;
 var float32x4 = SIMD.float32x4;
 var int32x4 = SIMD.int32x4;
 
-var summary = 'float32x4 add';
+var summary = 'float32x4 mul';
+
+function mulf(a, b) {
+    return Math.fround(Math.fround(a) * Math.fround(b));
+}
 
 function test() {
   print(BUGNUMBER + ": " + summary);
 
-  // FIXME -- Bug 948379: Amend to check for correctness of border cases.
-
   var a = float32x4(1, 2, 3, 4);
   var b = float32x4(10, 20, 30, 40);
   var c = SIMD.float32x4.mul(a, b);
   assertEq(c.x, 10);
   assertEq(c.y, 40);
   assertEq(c.z, 90);
   assertEq(c.w, 160);
 
+  var d = float32x4(1.66, 2.57, 3.73, 4.12);
+  var e = float32x4(10.67, 20.68, 30.02, 40.58);
+  var f = SIMD.float32x4.mul(d, e);
+  assertEq(f.x, mulf(1.66, 10.67));
+  assertEq(f.y, mulf(2.57, 20.68));
+  assertEq(f.z, mulf(3.73, 30.02));
+  assertEq(f.w, mulf(4.12, 40.58));
+
+  var g = float32x4(NaN, -0, Infinity, -Infinity);
+  var h = float32x4(NaN, -0, -Infinity, 0);
+  var i = SIMD.float32x4.mul(g, h);
+  assertEq(i.x, NaN);
+  assertEq(i.y, 0);
+  assertEq(i.z, -Infinity);
+  assertEq(i.w, NaN);
+
   if (typeof reportCompare === "function")
     reportCompare(true, true);
 }
 
 test();
-
--- a/js/src/tests/ecma_6/TypedObject/simd/float32x4neg.js
+++ b/js/src/tests/ecma_6/TypedObject/simd/float32x4neg.js
@@ -3,23 +3,35 @@ var BUGNUMBER = 946042;
 var float32x4 = SIMD.float32x4;
 var int32x4 = SIMD.int32x4;
 
 var summary = 'float32x4 neg';
 
 function test() {
   print(BUGNUMBER + ": " + summary);
 
-  // FIXME -- Bug 948379: Amend to check for correctness of border cases.
-
   var a = float32x4(1, 2, 3, 4);
   var c = SIMD.float32x4.neg(a);
   assertEq(c.x, -1);
   assertEq(c.y, -2);
   assertEq(c.z, -3);
   assertEq(c.w, -4);
 
+  var d = float32x4(0.999, -0.001, 3.78, 4.05);
+  var f = SIMD.float32x4.neg(d);
+  assertEq(f.x, Math.fround(-0.999));
+  assertEq(f.y, Math.fround(0.001));
+  assertEq(f.z, Math.fround(-3.78));
+  assertEq(f.w, Math.fround(-4.05));
+
+  var g = float32x4(NaN, -0, Infinity, -Infinity);
+  var i = SIMD.float32x4.neg(g);
+  assertEq(i.x, NaN);
+  assertEq(i.y, 0);
+  assertEq(i.z, -Infinity);
+  assertEq(i.w, Infinity);
+
   if (typeof reportCompare === "function")
     reportCompare(true, true);
 }
 
 test();
 
--- a/js/src/tests/ecma_6/TypedObject/simd/float32x4not.js
+++ b/js/src/tests/ecma_6/TypedObject/simd/float32x4not.js
@@ -13,23 +13,35 @@ var notf = (function() {
         i[0] = ~i[0];
         return f[0];
     }
 })();
 
 function test() {
   print(BUGNUMBER + ": " + summary);
 
-  // FIXME -- Bug 948379: Amend to check for correctness of border cases.
-
   var a = float32x4(2, 13, -37, 4.2);
   var c = SIMD.float32x4.not(a);
   assertEq(c.x, notf(2));
   assertEq(c.y, notf(13));
   assertEq(c.z, notf(-37));
   assertEq(c.w, notf(4.2));
 
+  var d = float32x4(2.897, 13.245, -37.781, 5.28);
+  var f = SIMD.float32x4.not(d);
+  assertEq(f.x, notf(2.897));
+  assertEq(f.y, notf(13.245));
+  assertEq(f.z, notf(-37.781));
+  assertEq(f.w, notf(5.28));
+
+  var g = float32x4(NaN, -0, Infinity, -Infinity);
+  var i = SIMD.float32x4.not(g);
+  assertEq(i.x, notf(NaN));
+  assertEq(i.y, notf(-0));
+  assertEq(i.z, notf(Infinity));
+  assertEq(i.w, notf(-Infinity));
+
   if (typeof reportCompare === "function")
     reportCompare(true, true);
 }
 
 test();
 
--- a/js/src/tests/ecma_6/TypedObject/simd/float32x4notequal.js
+++ b/js/src/tests/ecma_6/TypedObject/simd/float32x4notequal.js
@@ -3,24 +3,32 @@ var BUGNUMBER = 946042;
 var float32x4 = SIMD.float32x4;
 var int32x4 = SIMD.int32x4;
 
 var summary = 'float32x4 notEqual';
 
 function test() {
   print(BUGNUMBER + ": " + summary);
 
-  // FIXME -- Bug 948379: Amend to check for correctness of border cases.
+  // FIXME -- Bug 1081697: Amend to check for correctness of NaN/-0/Infinity/-Infinity border cases.
 
   var a = float32x4(1, 20, 30, 40);
   var b = float32x4(10, 20, 30, 4);
   var c = SIMD.float32x4.notEqual(a, b);
   assertEq(c.x, -1);
   assertEq(c.y, 0);
   assertEq(c.z, 0);
   assertEq(c.w, -1);
 
+  var d = float32x4(9.98, 20.65, 30.14, 4.235);
+  var e = float32x4(9.99, 20.65, Math.fround(30.14), 4.23);
+  var f = SIMD.float32x4.notEqual(d, e);
+  assertEq(f.x, -1);
+  assertEq(f.y, 0);
+  assertEq(f.z, 0);
+  assertEq(f.w, -1);
+
   if (typeof reportCompare === "function")
     reportCompare(true, true);
 }
 
 test();
 
--- a/js/src/tests/ecma_6/TypedObject/simd/float32x4or.js
+++ b/js/src/tests/ecma_6/TypedObject/simd/float32x4or.js
@@ -14,24 +14,38 @@ var orf = (function() {
         i[2] = i[0] | i[1];
         return f[2];
     }
 })();
 
 function test() {
   print(BUGNUMBER + ": " + summary);
 
-  // FIXME -- Bug 948379: Amend to check for correctness of border cases.
-
   var a = float32x4(1, 2, 3, 4);
   var b = float32x4(10, 20, 30, 40);
   var c = SIMD.float32x4.or(a, b);
   assertEq(c.x, orf(1, 10));
   assertEq(c.y, orf(2, 20));
   assertEq(c.z, orf(3, 30));
   assertEq(c.w, orf(4, 40));
 
+  var d = float32x4(1.12, 2.39, 3.83, 4.57);
+  var e = float32x4(10.76, 20.41, 30.96, 40.23);
+  var f = SIMD.float32x4.or(d, e);
+  assertEq(f.x, orf(1.12, 10.76));
+  assertEq(f.y, orf(2.39, 20.41));
+  assertEq(f.z, orf(3.83, 30.96));
+  assertEq(f.w, orf(4.57, 40.23));
+
+  var g = float32x4(NaN, -0, Infinity, -Infinity);
+  var h = float32x4(5, 5, -Infinity, Infinity);
+  var i = SIMD.float32x4.or(g, h);
+  assertEq(i.x, orf(NaN, 5));
+  assertEq(i.y, orf(-0, 5));
+  assertEq(i.z, orf(Infinity, -Infinity));
+  assertEq(i.w, orf(-Infinity, Infinity));
+
   if (typeof reportCompare === "function")
     reportCompare(true, true);
 }
 
 test();
 
--- a/js/src/tests/ecma_6/TypedObject/simd/float32x4reciprocal.js
+++ b/js/src/tests/ecma_6/TypedObject/simd/float32x4reciprocal.js
@@ -1,25 +1,41 @@
 // |reftest| skip-if(!this.hasOwnProperty("SIMD"))
 var BUGNUMBER = 946042;
 var float32x4 = SIMD.float32x4;
 var int32x4 = SIMD.int32x4;
 
 var summary = 'float32x4 reciprocal';
 
+function reciprocalf(a) {
+  return Math.fround(1 / Math.fround(a));
+}
+
 function test() {
   print(BUGNUMBER + ": " + summary);
 
-  // FIXME -- Bug 948379: Amend to check for correctness of border cases.
-
   var a = float32x4(1, 0.5, 0.25, 0.125);
   var c = SIMD.float32x4.reciprocal(a);
   assertEq(c.x, 1);
   assertEq(c.y, 2);
   assertEq(c.z, 4);
   assertEq(c.w, 8);
 
+  var d = float32x4(1.6, 0.8, 0.4, 0.2);
+  var f = SIMD.float32x4.reciprocal(d);
+  assertEq(f.x, reciprocalf(1.6));
+  assertEq(f.y, reciprocalf(0.8));
+  assertEq(f.z, reciprocalf(0.4));
+  assertEq(f.w, reciprocalf(0.2));
+
+  var g = float32x4(NaN, -0, Infinity, -Infinity);
+  var i = SIMD.float32x4.reciprocal(g);
+  assertEq(i.x, NaN);
+  assertEq(i.y, -Infinity);
+  assertEq(i.z, 0);
+  assertEq(i.w, -0);
+
   if (typeof reportCompare === "function")
     reportCompare(true, true);
 }
 
 test();
 
--- a/js/src/tests/ecma_6/TypedObject/simd/float32x4reciprocalsqrt.js
+++ b/js/src/tests/ecma_6/TypedObject/simd/float32x4reciprocalsqrt.js
@@ -1,25 +1,41 @@
 // |reftest| skip-if(!this.hasOwnProperty("SIMD"))
 var BUGNUMBER = 946042;
 var float32x4 = SIMD.float32x4;
 var int32x4 = SIMD.int32x4;
 
 var summary = 'float32x4 reciprocalSqrt';
 
+function reciprocalsqrtf(a) {
+    return Math.fround(Math.sqrt(1 / Math.fround(a)));
+}
+
 function test() {
   print(BUGNUMBER + ": " + summary);
 
-  // FIXME -- Bug 948379: Amend to check for correctness of border cases.
-
   var a = float32x4(1, 1, 0.25, 0.25);
   var c = SIMD.float32x4.reciprocalSqrt(a);
   assertEq(c.x, 1);
   assertEq(c.y, 1);
   assertEq(c.z, 2);
   assertEq(c.w, 2);
 
+  var d = float32x4(25, 16, 6.25, 1.5625);
+  var f = SIMD.float32x4.reciprocalSqrt(d);
+  assertEq(f.x, reciprocalsqrtf(25));
+  assertEq(f.y, reciprocalsqrtf(16));
+  assertEq(f.z, reciprocalsqrtf(6.25));
+  assertEq(f.w, reciprocalsqrtf(1.5625));
+
+  var g = float32x4(NaN, -0, Infinity, -Infinity);
+  var i = SIMD.float32x4.reciprocalSqrt(g);
+  assertEq(i.x, NaN);
+  assertEq(i.y, -Infinity);
+  assertEq(i.z, 0);
+  assertEq(i.w, NaN);
+
   if (typeof reportCompare === "function")
     reportCompare(true, true);
 }
 
 test();
 
--- a/js/src/tests/ecma_6/TypedObject/simd/float32x4scale.js
+++ b/js/src/tests/ecma_6/TypedObject/simd/float32x4scale.js
@@ -1,25 +1,41 @@
 // |reftest| skip-if(!this.hasOwnProperty("SIMD"))
 var BUGNUMBER = 946042;
 var float32x4 = SIMD.float32x4;
 var int32x4 = SIMD.int32x4;
 
 var summary = 'float32x4 scale';
 
+function mulf(a, b) {
+  return Math.fround(Math.fround(a) * Math.fround(b));
+}
+
 function test() {
   print(BUGNUMBER + ": " + summary);
 
-  // FIXME -- Bug 948379: Amend to check for correctness of border cases.
-
   var a = float32x4(1, 2, 3, 4);
   var c = SIMD.float32x4.scale(a, 2);
   assertEq(c.x, 2);
   assertEq(c.y, 4);
   assertEq(c.z, 6);
   assertEq(c.w, 8);
 
+  var d = float32x4(1.34, 2.76, 3.21, 4.09);
+  var f = float32x4.scale(d, 2.54);
+  assertEq(f.x, mulf(1.34, 2.54));
+  assertEq(f.y, mulf(2.76, 2.54));
+  assertEq(f.z, mulf(3.21, 2.54));
+  assertEq(f.w, mulf(4.09, 2.54));
+
+  var g = float32x4(NaN, -0, Infinity, -Infinity);
+  var i = float32x4.scale(g, 2.54);
+  assertEq(i.x, NaN);
+  assertEq(i.y, -0);
+  assertEq(i.z, Infinity);
+  assertEq(i.w, -Infinity);
+
   if (typeof reportCompare === "function")
     reportCompare(true, true);
 }
 
 test();
 
--- a/js/src/tests/ecma_6/TypedObject/simd/float32x4sqrt.js
+++ b/js/src/tests/ecma_6/TypedObject/simd/float32x4sqrt.js
@@ -1,25 +1,41 @@
 // |reftest| skip-if(!this.hasOwnProperty("SIMD"))
 var BUGNUMBER = 946042;
 var float32x4 = SIMD.float32x4;
 var int32x4 = SIMD.int32x4;
 
 var summary = 'float32x4 sqrt';
 
+function sqrtf(a) {
+  return Math.fround(Math.sqrt(Math.fround(a)));
+}
+
 function test() {
   print(BUGNUMBER + ": " + summary);
 
-  // FIXME -- Bug 948379: Amend to check for correctness of border cases.
-
   var a = float32x4(1, 4, 9, 16);
   var c = SIMD.float32x4.sqrt(a);
   assertEq(c.x, 1);
   assertEq(c.y, 2);
   assertEq(c.z, 3);
   assertEq(c.w, 4);
 
+  var d = float32x4(2.7225, 7.3441, 9.4249, -1);
+  var f = SIMD.float32x4.sqrt(d);
+  assertEq(f.x, sqrtf(2.7225));
+  assertEq(f.y, sqrtf(7.3441));
+  assertEq(f.z, sqrtf(9.4249));
+  assertEq(f.w, NaN);
+
+  var g = float32x4(NaN, -0, Infinity, -Infinity);
+  var i = SIMD.float32x4.sqrt(g);
+  assertEq(i.x, NaN);
+  assertEq(i.y, -0);
+  assertEq(i.z, Infinity);
+  assertEq(i.w, NaN);
+
   if (typeof reportCompare === "function")
     reportCompare(true, true);
 }
 
 test();
 
--- a/js/src/tests/ecma_6/TypedObject/simd/float32x4sub.js
+++ b/js/src/tests/ecma_6/TypedObject/simd/float32x4sub.js
@@ -1,26 +1,44 @@
 // |reftest| skip-if(!this.hasOwnProperty("SIMD"))
 var BUGNUMBER = 946042;
 var float32x4 = SIMD.float32x4;
 var int32x4 = SIMD.int32x4;
 
 var summary = 'float32x4 sub';
 
+function subf(a, b) {
+    return Math.fround(Math.fround(a) - Math.fround(b));
+}
+
 function test() {
   print(BUGNUMBER + ": " + summary);
 
-  // FIXME -- Bug 948379: Amend to check for correctness of border cases.
-
   var a = float32x4(1, 2, 3, 4);
   var b = float32x4(10, 20, 30, 40);
-  var c = SIMD.float32x4.sub(b,a);
+  var c = SIMD.float32x4.sub(b, a);
   assertEq(c.x, 9);
   assertEq(c.y, 18);
   assertEq(c.z, 27);
   assertEq(c.w, 36);
 
+  var d = float32x4(1.34, 2.95, 3.17, 4.29);
+  var e = float32x4(10.18, 20.43, 30.63, 40.38);
+  var f = SIMD.float32x4.sub(e, d);
+  assertEq(f.x, subf(10.18, 1.34));
+  assertEq(f.y, subf(20.43, 2.95));
+  assertEq(f.z, subf(30.63, 3.17));
+  assertEq(f.w, subf(40.38, 4.29));
+
+  var g = float32x4(NaN, -0, -Infinity, -Infinity);
+  var h = float32x4(NaN, -0, Infinity, -Infinity);
+  var i = SIMD.float32x4.sub(h, g);
+  assertEq(i.x, NaN);
+  assertEq(i.y, 0);
+  assertEq(i.z, Infinity);
+  assertEq(i.w, NaN);
+
   if (typeof reportCompare === "function")
     reportCompare(true, true);
 }
 
 test();
 
--- a/js/src/tests/ecma_6/TypedObject/simd/float32x4with.js
+++ b/js/src/tests/ecma_6/TypedObject/simd/float32x4with.js
@@ -3,26 +3,89 @@ var BUGNUMBER = 946042;
 var float32x4 = SIMD.float32x4;
 var int32x4 = SIMD.int32x4;
 
 var summary = 'float32x4 with';
 
 function test() {
   print(BUGNUMBER + ": " + summary);
 
-  // FIXME -- Bug 948379: Amend to check for correctness of border cases.
-
   var a = float32x4(1, 2, 3, 4);
   var x = SIMD.float32x4.withX(a, 5);
   var y = SIMD.float32x4.withY(a, 5);
   var z = SIMD.float32x4.withZ(a, 5);
   var w = SIMD.float32x4.withW(a, 5);
   assertEq(x.x, 5);
+  assertEq(x.y, 2);
+  assertEq(x.z, 3);
+  assertEq(x.w, 4);
+
+  assertEq(y.x, 1);
   assertEq(y.y, 5);
+  assertEq(y.z, 3);
+  assertEq(y.w, 4);
+
+  assertEq(z.x, 1);
+  assertEq(z.y, 2);
   assertEq(z.z, 5);
+  assertEq(z.w, 4);
+
+  assertEq(w.x, 1);
+  assertEq(w.y, 2);
+  assertEq(w.z, 3);
   assertEq(w.w, 5);
 
+  var b = float32x4(1.87, 2.08, 3.84, 4.17);
+  var x1 = SIMD.float32x4.withX(b, 5.38);
+  var y1 = SIMD.float32x4.withY(b, 5.19);
+  var z1 = SIMD.float32x4.withZ(b, 5.11);
+  var w1 = SIMD.float32x4.withW(b, 5.07);
+  assertEq(x1.x, Math.fround(5.38));
+  assertEq(x1.y, Math.fround(2.08));
+  assertEq(x1.z, Math.fround(3.84));
+  assertEq(x1.w, Math.fround(4.17));
+
+  assertEq(y1.x, Math.fround(1.87));
+  assertEq(y1.y, Math.fround(5.19));
+  assertEq(y1.z, Math.fround(3.84));
+  assertEq(y1.w, Math.fround(4.17));
+
+  assertEq(z1.x, Math.fround(1.87));
+  assertEq(z1.y, Math.fround(2.08));
+  assertEq(z1.z, Math.fround(5.11));
+  assertEq(z1.w, Math.fround(4.17));
+
+  assertEq(w1.x, Math.fround(1.87));
+  assertEq(w1.y, Math.fround(2.08));
+  assertEq(w1.z, Math.fround(3.84));
+  assertEq(w1.w, Math.fround(5.07));
+
+  var c = float32x4(NaN, -0, Infinity, -Infinity);
+  var x2 = SIMD.float32x4.withX(c, 0);
+  var y2 = SIMD.float32x4.withY(c, 0);
+  var z2 = SIMD.float32x4.withZ(c, 0);
+  var w2 = SIMD.float32x4.withW(c, 0);
+  assertEq(x2.x, 0);
+  assertEq(x2.y, -0);
+  assertEq(x2.z, Infinity);
+  assertEq(x2.w, -Infinity);
+
+  assertEq(y2.x, NaN);
+  assertEq(y2.y, 0);
+  assertEq(y2.z, Infinity);
+  assertEq(y2.w, -Infinity);
+
+  assertEq(z2.x, NaN);
+  assertEq(z2.y, -0);
+  assertEq(z2.z, 0);
+  assertEq(z2.w, -Infinity);
+
+  assertEq(w2.x, NaN);
+  assertEq(w2.y, -0);
+  assertEq(w2.z, Infinity);
+  assertEq(w2.w, 0);
+
   if (typeof reportCompare === "function")
     reportCompare(true, true);
 }
 
 test();
 
--- a/js/src/tests/ecma_6/TypedObject/simd/float32x4xor.js
+++ b/js/src/tests/ecma_6/TypedObject/simd/float32x4xor.js
@@ -14,24 +14,38 @@ var xorf = (function() {
         i[2] = i[0] ^ i[1];
         return f[2];
     }
 })();
 
 function test() {
   print(BUGNUMBER + ": " + summary);
 
-  // FIXME -- Bug 948379: Amend to check for correctness of border cases.
-
   var a = float32x4(1, 2, 3, 4);
   var b = float32x4(10, 20, 30, 40);
   var c = SIMD.float32x4.xor(a, b);
   assertEq(c.x, xorf(1, 10));
   assertEq(c.y, xorf(2, 20));
   assertEq(c.z, xorf(3, 30));
   assertEq(c.w, xorf(4, 40));
 
+  var d = float32x4(1.07, 2.62, 3.79, 4.15);
+  var e = float32x4(10.38, 20.47, 30.44, 40.16);
+  var f = SIMD.float32x4.xor(d, e);
+  assertEq(f.x, xorf(1.07, 10.38));
+  assertEq(f.y, xorf(2.62, 20.47));
+  assertEq(f.z, xorf(3.79, 30.44));
+  assertEq(f.w, xorf(4.15, 40.16));
+
+  var g = float32x4(NaN, -0, Infinity, -Infinity);
+  var h = float32x4(-0, Infinity, -Infinity, NaN);
+  var i = SIMD.float32x4.xor(g, h);
+  assertEq(i.x, xorf(NaN, -0));
+  assertEq(i.y, xorf(-0, Infinity));
+  assertEq(i.z, xorf(Infinity, -Infinity));
+  assertEq(i.w, xorf(-Infinity, NaN));
+
   if (typeof reportCompare === "function")
     reportCompare(true, true);
 }
 
 test();
 
--- a/js/xpconnect/src/XPCJSRuntime.cpp
+++ b/js/xpconnect/src/XPCJSRuntime.cpp
@@ -3208,25 +3208,21 @@ XPCJSRuntime::XPCJSRuntime(nsXPConnect* 
     // default (1MB on 32-bit, 2MB on 64-bit). ASAN stack frame measurements
     // were not taken at the time of this writing, so we hazard a guess that
     // ASAN builds have roughly thrice the stack overhead as normal builds.
     // On normal builds, the largest stack frame size we might encounter is
     // 8.2k, so let's use a buffer of 8.2 * 3 * 10 = 246k.
     const size_t kStackQuota =  2 * kDefaultStackQuota;
     const size_t kTrustedScriptBuffer = 246 * 1024;
 #elif defined(XP_WIN)
-    // 1MB is the default stack size on Windows, so use 900k. And since 32-bit
-    // Windows stack frames are 3.4k each, let's use a buffer of 48k and double
-    // that for 64-bit.
-    //
-    // Note - Frames on Win32 PGO builds seem to have grown recently, and 48k
-    // stacks seem about 20-30% too small - so we bump it to 64k.
+    // 1MB is the default stack size on Windows, so use 900k.
+    // Windows PGO stack frames have unfortunately gotten pretty large lately. :-(
     const size_t kStackQuota = 900 * 1024;
-    const size_t kTrustedScriptBuffer = (sizeof(size_t) == 8) ? 96 * 1024
-                                                              : 64 * 1024;
+    const size_t kTrustedScriptBuffer = (sizeof(size_t) == 8) ? 120 * 1024
+                                                              : 80 * 1024;
     // The following two configurations are linux-only. Given the numbers above,
     // we use 50k and 100k trusted buffers on 32-bit and 64-bit respectively.
 #elif defined(DEBUG)
     // Bug 803182: account for the 4x difference in the size of js::Interpret
     // between optimized and debug builds.
     // XXXbholley - Then why do we only account for 2x of difference?
     const size_t kStackQuota = 2 * kDefaultStackQuota;
     const size_t kTrustedScriptBuffer = sizeof(size_t) * 12800;
--- a/layout/base/SelectionCarets.cpp
+++ b/layout/base/SelectionCarets.cpp
@@ -379,49 +379,52 @@ SelectionCarets::UpdateSelectionCarets()
   }
 
   nsRefPtr<dom::Selection> selection = GetSelection();
   if (!selection) {
     SetVisibility(false);
     return;
   }
 
-  if (selection->GetRangeCount() <= 0) {
+  if (selection->IsCollapsed()) {
     SetVisibility(false);
     return;
   }
 
-  nsRefPtr<nsRange> range = selection->GetRangeAt(0);
-  if (range->Collapsed()) {
-    SetVisibility(false);
-    return;
-  }
+  int32_t rangeCount = selection->GetRangeCount();
+  nsRefPtr<nsRange> firstRange = selection->GetRangeAt(0);
+  nsRefPtr<nsRange> lastRange = selection->GetRangeAt(rangeCount - 1);
 
-  nsLayoutUtils::FirstAndLastRectCollector collector;
-  nsRange::CollectClientRects(&collector, range,
-                              range->GetStartParent(), range->StartOffset(),
-                              range->GetEndParent(), range->EndOffset(), true, true);
+  nsLayoutUtils::FirstAndLastRectCollector collectorStart;
+  nsRange::CollectClientRects(&collectorStart, firstRange,
+                              firstRange->GetStartParent(), firstRange->StartOffset(),
+                              firstRange->GetEndParent(), firstRange->EndOffset(), true, true);
+
+  nsLayoutUtils::FirstAndLastRectCollector collectorEnd;
+  nsRange::CollectClientRects(&collectorEnd, lastRange,
+                              lastRange->GetStartParent(), lastRange->StartOffset(),
+                              lastRange->GetEndParent(), lastRange->EndOffset(), true, true);
 
   nsIFrame* canvasFrame = mPresShell->GetCanvasFrame();
   nsIFrame* rootFrame = mPresShell->GetRootFrame();
 
   if (!canvasFrame || !rootFrame) {
     SetVisibility(false);
     return;
   }
 
   // Check start and end frame is rtl or ltr text
   nsRefPtr<nsFrameSelection> fs = GetFrameSelection();
   int32_t startOffset;
   nsIFrame* startFrame = FindFirstNodeWithFrame(mPresShell->GetDocument(),
-                                                range, fs, false, startOffset);
+                                                firstRange, fs, false, startOffset);
 
   int32_t endOffset;
   nsIFrame* endFrame = FindFirstNodeWithFrame(mPresShell->GetDocument(),
-                                              range, fs, true, endOffset);
+                                              lastRange, fs, true, endOffset);
 
   if (!startFrame || !endFrame) {
     SetVisibility(false);
     return;
   }
 
   // If frame isn't editable and we don't support non-editable fields, bail
   // out.
@@ -437,46 +440,46 @@ SelectionCarets::UpdateSelectionCarets()
     return;
   }
 
   bool startFrameIsRTL = IsRightToLeft(startFrame);
   bool endFrameIsRTL = IsRightToLeft(endFrame);
 
   // If start frame is LTR, then place start caret in first rect's leftmost
   // otherwise put it to first rect's rightmost.
-  ReduceRectToVerticalEdge(collector.mFirstRect, startFrameIsRTL);
+  ReduceRectToVerticalEdge(collectorStart.mFirstRect, startFrameIsRTL);
 
   // Contrary to start frame, if end frame is LTR, put end caret to last
   // rect's rightmost position, otherwise, put it to last rect's leftmost.
-  ReduceRectToVerticalEdge(collector.mLastRect, !endFrameIsRTL);
+  ReduceRectToVerticalEdge(collectorEnd.mLastRect, !endFrameIsRTL);
 
   nsAutoTArray<nsIFrame*, 16> hitFramesInFirstRect;
   nsLayoutUtils::GetFramesForArea(rootFrame,
-    collector.mFirstRect,
+    collectorStart.mFirstRect,
     hitFramesInFirstRect,
     nsLayoutUtils::IGNORE_PAINT_SUPPRESSION |
       nsLayoutUtils::IGNORE_CROSS_DOC |
       nsLayoutUtils::IGNORE_ROOT_SCROLL_FRAME);
 
   nsAutoTArray<nsIFrame*, 16> hitFramesInLastRect;
   nsLayoutUtils::GetFramesForArea(rootFrame,
-    collector.mLastRect,
+    collectorEnd.mLastRect,
     hitFramesInLastRect,
     nsLayoutUtils::IGNORE_PAINT_SUPPRESSION |
       nsLayoutUtils::IGNORE_CROSS_DOC |
       nsLayoutUtils::IGNORE_ROOT_SCROLL_FRAME);
 
   SetStartFrameVisibility(hitFramesInFirstRect.Contains(startFrame));
   SetEndFrameVisibility(hitFramesInLastRect.Contains(endFrame));
 
-  nsLayoutUtils::TransformRect(rootFrame, canvasFrame, collector.mFirstRect);
-  nsLayoutUtils::TransformRect(rootFrame, canvasFrame, collector.mLastRect);
+  nsLayoutUtils::TransformRect(rootFrame, canvasFrame, collectorStart.mFirstRect);
+  nsLayoutUtils::TransformRect(rootFrame, canvasFrame, collectorEnd.mLastRect);
 
-  SetStartFramePos(collector.mFirstRect.BottomLeft());
-  SetEndFramePos(collector.mLastRect.BottomRight());
+  SetStartFramePos(collectorStart.mFirstRect.BottomLeft());
+  SetEndFramePos(collectorEnd.mLastRect.BottomRight());
   SetVisibility(true);
 
   // If range select only one character, append tilt class name to it.
   bool isTilt = false;
   if (startFrame && endFrame) {
     // In this case <textarea>abc</textarea> and we select 'c' character,
     // EndContent would be HTMLDivElement and mResultContent which get by
     // calling startFrame->PeekOffset() with selecting next cluster would be
@@ -684,21 +687,23 @@ SelectionCarets::DragSelection(const nsP
 
   nsFrame::ContentOffsets offsets =
     newFrame->GetContentOffsetsFromPoint(newPoint);
   if (!offsets.content) {
     return nsEventStatus_eConsumeNoDefault;
   }
 
   nsRefPtr<dom::Selection> selection = GetSelection();
-  if (selection->GetRangeCount() <= 0) {
+  int32_t rangeCount = selection->GetRangeCount();
+  if (rangeCount <= 0) {
     return nsEventStatus_eConsumeNoDefault;
   }
 
-  nsRefPtr<nsRange> range = selection->GetRangeAt(0);
+  nsRefPtr<nsRange> range = mDragMode == START_FRAME ?
+    selection->GetRangeAt(0) : selection->GetRangeAt(rangeCount - 1);
   if (!CompareRangeWithContentOffset(range, fs, offsets, mDragMode)) {
     return nsEventStatus_eConsumeNoDefault;
   }
 
   nsIFrame* anchorFrame;
   selection->GetPrimaryFrameForAnchorNode(&anchorFrame);
   if (!anchorFrame) {
     return nsEventStatus_eConsumeNoDefault;
@@ -732,30 +737,32 @@ SelectionCarets::GetCaretYCenterPosition
 {
   nsIFrame* canvasFrame = mPresShell->GetCanvasFrame();
 
   if (!canvasFrame) {
     return 0;
   }
 
   nsRefPtr<dom::Selection> selection = GetSelection();
-  if (selection->GetRangeCount() <= 0) {
+  int32_t rangeCount = selection->GetRangeCount();
+  if (rangeCount <= 0) {
     return 0;
   }
 
-  nsRefPtr<nsRange> range = selection->GetRangeAt(0);
   nsRefPtr<nsFrameSelection> fs = GetFrameSelection();
 
   MOZ_ASSERT(mDragMode != NONE);
   nsCOMPtr<nsIContent> node;
   uint32_t nodeOffset;
   if (mDragMode == START_FRAME) {
+    nsRefPtr<nsRange> range = selection->GetRangeAt(0);
     node = do_QueryInterface(range->GetStartParent());
     nodeOffset = range->StartOffset();
   } else {
+    nsRefPtr<nsRange> range = selection->GetRangeAt(rangeCount - 1);
     node = do_QueryInterface(range->GetEndParent());
     nodeOffset = range->EndOffset();
   }
 
   int32_t offset;
   CaretAssociationHint hint =
     mDragMode == START_FRAME ? CARET_ASSOCIATE_AFTER : CARET_ASSOCIATE_BEFORE;
   nsIFrame* theFrame =
@@ -880,22 +887,16 @@ SelectionCarets::GetFrameSelection()
   }
 }
 
 nsresult
 SelectionCarets::NotifySelectionChanged(nsIDOMDocument* aDoc,
                                        nsISelection* aSel,
                                        int16_t aReason)
 {
-  bool isCollapsed;
-  aSel->GetIsCollapsed(&isCollapsed);
-  if (isCollapsed) {
-    SetVisibility(false);
-    return NS_OK;
-  }
   if (!aReason || (aReason & (nsISelectionListener::DRAG_REASON |
                                nsISelectionListener::KEYPRESS_REASON |
                                nsISelectionListener::MOUSEDOWN_REASON))) {
     SetVisibility(false);
   } else {
     UpdateSelectionCarets();
   }
   return NS_OK;
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -4676,41 +4676,52 @@ static int32_t FindSafeLength(const char
 }
 
 static int32_t GetMaxChunkLength(nsFontMetrics& aFontMetrics)
 {
   return std::min(aFontMetrics.GetMaxStringLength(), MAX_GFX_TEXT_BUF_SIZE);
 }
 
 nscoord
-nsLayoutUtils::AppUnitWidthOfString(const nsString& aString,
-                                    nsFontMetrics& aFontMetrics,
-                                    nsRenderingContext& aContext)
-{
-  return AppUnitWidthOfString(aString.get(), aString.Length(),
-                              aFontMetrics, aContext);
-}
-
-nscoord
 nsLayoutUtils::AppUnitWidthOfString(const char16_t *aString,
                                     uint32_t aLength,
                                     nsFontMetrics& aFontMetrics,
                                     nsRenderingContext& aContext)
 {
   uint32_t maxChunkLength = GetMaxChunkLength(aFontMetrics);
   nscoord width = 0;
   while (aLength > 0) {
     int32_t len = FindSafeLength(aString, aLength, maxChunkLength);
     width += aFontMetrics.GetWidth(aString, len, &aContext);
     aLength -= len;
     aString += len;
   }
   return width;
 }
 
+nscoord
+nsLayoutUtils::AppUnitWidthOfStringBidi(const char16_t* aString,
+                                        uint32_t aLength,
+                                        const nsIFrame* aFrame,
+                                        nsFontMetrics& aFontMetrics,
+                                        nsRenderingContext& aContext)
+{
+  nsPresContext* presContext = aFrame->PresContext();
+  if (presContext->BidiEnabled()) {
+    nsBidiLevel level =
+      nsBidiPresUtils::BidiLevelFromStyle(aFrame->StyleContext());
+    return nsBidiPresUtils::MeasureTextWidth(aString, aLength, level,
+                                             presContext, aContext,
+                                             aFontMetrics);
+  }
+  aFontMetrics.SetTextRunRTL(false);
+  return nsLayoutUtils::AppUnitWidthOfString(aString, aLength, aFontMetrics,
+                                             aContext);
+}
+
 nsBoundingMetrics
 nsLayoutUtils::AppUnitBoundsOfString(const char16_t* aString,
                                      uint32_t aLength,
                                      nsFontMetrics& aFontMetrics,
                                      nsRenderingContext& aContext)
 {
   uint32_t maxChunkLength = GetMaxChunkLength(aFontMetrics);
   int32_t len = FindSafeLength(aString, aLength, maxChunkLength);
@@ -4794,36 +4805,16 @@ nsLayoutUtils::DrawUniDirString(const ch
     if (!isRTL) {
       x += width;
     }
     aLength -= len;
     aString += len;
   }
 }
 
-nscoord
-nsLayoutUtils::GetStringWidth(const nsIFrame*      aFrame,
-                              nsRenderingContext* aContext,
-                              nsFontMetrics&      aFontMetrics,
-                              const char16_t*     aString,
-                              int32_t              aLength)
-{
-  nsPresContext* presContext = aFrame->PresContext();
-  if (presContext->BidiEnabled()) {
-    nsBidiLevel level =
-      nsBidiPresUtils::BidiLevelFromStyle(aFrame->StyleContext());
-    return nsBidiPresUtils::MeasureTextWidth(aString, aLength,
-                                             level, presContext, *aContext,
-                                             aFontMetrics);
-  }
-  aFontMetrics.SetTextRunRTL(false);
-  return nsLayoutUtils::AppUnitWidthOfString(aString, aLength, aFontMetrics,
-                                             *aContext);
-}
-
 /* static */ void
 nsLayoutUtils::PaintTextShadow(const nsIFrame* aFrame,
                                nsRenderingContext* aContext,
                                const nsRect& aTextRect,
                                const nsRect& aDirtyRect,
                                const nscolor& aForegroundColor,
                                TextShadowCallback aCallback,
                                void* aCallbackData)
--- a/layout/base/nsLayoutUtils.h
+++ b/layout/base/nsLayoutUtils.h
@@ -1321,21 +1321,38 @@ public:
 
   static nscoord AppUnitWidthOfString(char16_t aC,
                                       nsFontMetrics& aFontMetrics,
                                       nsRenderingContext& aContext) {
     return AppUnitWidthOfString(&aC, 1, aFontMetrics, aContext);
   }
   static nscoord AppUnitWidthOfString(const nsString& aString,
                                       nsFontMetrics& aFontMetrics,
-                                      nsRenderingContext& aContext);
+                                      nsRenderingContext& aContext) {
+    return nsLayoutUtils::AppUnitWidthOfString(aString.get(), aString.Length(),
+                                               aFontMetrics, aContext);
+  }
   static nscoord AppUnitWidthOfString(const char16_t *aString,
                                       uint32_t aLength,
                                       nsFontMetrics& aFontMetrics,
                                       nsRenderingContext& aContext);
+  static nscoord AppUnitWidthOfStringBidi(const nsString& aString,
+                                          const nsIFrame* aFrame,
+                                          nsFontMetrics& aFontMetrics,
+                                          nsRenderingContext& aContext) {
+    return nsLayoutUtils::AppUnitWidthOfStringBidi(aString.get(),
+                                                   aString.Length(), aFrame,
+                                                   aFontMetrics, aContext);
+  }
+  static nscoord AppUnitWidthOfStringBidi(const char16_t* aString,
+                                          uint32_t aLength,
+                                          const nsIFrame* aFrame,
+                                          nsFontMetrics& aFontMetrics,
+                                          nsRenderingContext& aContext);
+
   static nsBoundingMetrics AppUnitBoundsOfString(const char16_t* aString,
                                                  uint32_t aLength,
                                                  nsFontMetrics& aFontMetrics,
                                                  nsRenderingContext& aContext);
 
   static void DrawString(const nsIFrame*       aFrame,
                          nsFontMetrics&        aFontMetrics,
                          nsRenderingContext*   aContext,
@@ -1348,22 +1365,16 @@ public:
    * Supports only LTR or RTL. Bidi (mixed direction) is not supported.
    */
   static void DrawUniDirString(const char16_t* aString,
                                uint32_t aLength,
                                nsPoint aPoint,
                                nsFontMetrics& aFontMetrics,
                                nsRenderingContext& aContext);
 
-  static nscoord GetStringWidth(const nsIFrame*      aFrame,
-                                nsRenderingContext* aContext,
-                                nsFontMetrics&      aFontMetrics,
-                                const char16_t*     aString,
-                                int32_t              aLength);
-
   /**
    * Helper function for drawing text-shadow. The callback's job
    * is to draw whatever needs to be blurred onto the given context.
    */
   typedef void (* TextShadowCallback)(nsRenderingContext* aCtx,
                                       nsPoint aShadowOffset,
                                       const nscolor& aShadowColor,
                                       void* aData);
--- a/layout/base/tests/marionette/manifest.ini
+++ b/layout/base/tests/marionette/manifest.ini
@@ -8,8 +8,9 @@ browser = true
 ; true if the test is compatible with b2g, otherwise false
 b2g = true
 
 ; true if the test should be skipped
 skip = false
 
 [test_touchcaret.py]
 [test_selectioncarets.py]
+[test_selectioncarets_multiplerange.py]
new file mode 100644
--- /dev/null
+++ b/layout/base/tests/marionette/test_selectioncarets_multiplerange.py
@@ -0,0 +1,108 @@
+# -*- coding: utf-8 -*-
+# 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/.
+
+from by import By
+from marionette import Actions
+from marionette_test import MarionetteTestCase
+from selection import SelectionManager
+
+
+class SelectionCaretsMultipleRangeTest(MarionetteTestCase):
+    _long_press_time = 1        # 1 second
+
+    def setUp(self):
+        # Code to execute before a tests are run.
+        MarionetteTestCase.setUp(self)
+        self.actions = Actions(self.marionette)
+
+    def openTestHtml(self, enabled=True):
+        # Open html for testing and enable selectioncaret and
+        # non-editable support
+        self.marionette.execute_script(
+            'SpecialPowers.setBoolPref("selectioncaret.enabled", %s);' %
+            ('true' if enabled else 'false'))
+        self.marionette.execute_script(
+            'SpecialPowers.setBoolPref("selectioncaret.noneditable", %s);' %
+            ('true' if enabled else 'false'))
+
+        test_html = self.marionette.absolute_url('test_selectioncarets_multiplerange.html')
+        self.marionette.navigate(test_html)
+
+        self._body = self.marionette.find_element(By.ID, 'bd')
+        self._sel3 = self.marionette.find_element(By.ID, 'sel3')
+        self._sel4 = self.marionette.find_element(By.ID, 'sel4')
+        self._sel6 = self.marionette.find_element(By.ID, 'sel6')
+        self._nonsel1 = self.marionette.find_element(By.ID, 'nonsel1')
+
+    def _long_press_without_contextmenu(self, el, x, y):
+        return self.actions.press(el, x, y).move_by_offset(0, 0).\
+            wait(self._long_press_time).release()
+
+    def _long_press_to_select_word(self, el, wordOrdinal):
+        sel = SelectionManager(el)
+        original_content = sel.content
+        words = original_content.split()
+        self.assertTrue(wordOrdinal < len(words),
+            'Expect at least %d words in the content.' % wordOrdinal)
+
+        # Calc offset
+        offset = 0
+        for i in range(wordOrdinal):
+            offset += (len(words[i]) + 1)
+
+        # Move caret inside the word.
+        el.tap()
+        sel.move_caret_to_front()
+        sel.move_caret_by_offset(offset)
+        x, y = sel.caret_location()
+
+        # Long press the caret position. Selection carets should appear, and the
+        # word will be selected. On Windows, those spaces after the word
+        # will also be selected.
+        self._long_press_without_contextmenu(el, x, y).perform()
+
+    def _to_unix_line_ending(self, s):
+        """Changes all Windows/Mac line endings in s to UNIX line endings."""
+
+        return s.replace('\r\n', '\n').replace('\r', '\n')
+
+    def test_long_press_to_select_non_selectable_word(self):
+        '''Testing long press on non selectable field.
+        We should not select anything when long press on non selectable fields.'''
+
+        self.openTestHtml(enabled=True)
+        halfY = self._nonsel1.size['height'] / 2
+        self._long_press_without_contextmenu(self._nonsel1, 0, halfY).perform()
+        sel = SelectionManager(self._nonsel1)
+        range_count = sel.range_count()
+        self.assertEqual(range_count, 0)
+
+    def test_drag_caret_over_non_selectable_field(self):
+        '''Testing drag caret over non selectable field.
+        So that the selected content should exclude non selectable field and
+        end selection caret should appear in last range's position.'''
+        self.openTestHtml(enabled=True)
+
+        # Select target element and get target caret location
+        self._long_press_to_select_word(self._sel4, 3)
+        sel = SelectionManager(self._body)
+        (_, _), (end_caret_x, end_caret_y) = sel.selection_carets_location()
+
+        self._long_press_to_select_word(self._sel6, 0)
+        (_, _), (end_caret2_x, end_caret2_y) = sel.selection_carets_location()
+
+        # Select start element
+        self._long_press_to_select_word(self._sel3, 3)
+
+        # Drag end caret to target location
+        (caret1_x, caret1_y), (caret2_x, caret2_y) = sel.selection_carets_location()
+        self.actions.flick(self._body, caret2_x, caret2_y, end_caret_x, end_caret_y).perform()
+        self.assertEqual(self._to_unix_line_ending(sel.selected_content.strip()),
+            'this 3\nuser can select this')
+
+        (caret1_x, caret1_y), (caret2_x, caret2_y) = sel.selection_carets_location()
+        self.actions.flick(self._body, caret2_x, caret2_y, end_caret2_x, end_caret2_y).perform()
+        self.assertEqual(self._to_unix_line_ending(sel.selected_content.strip()),
+            'this 3\nuser can select this 4\nuser can select this 5\nuser')
--- a/layout/generic/TextOverflow.cpp
+++ b/layout/generic/TextOverflow.cpp
@@ -758,18 +758,17 @@ TextOverflow::Marker::SetupString(nsIFra
       mWidth = 0;
     }
   } else {
     nsRefPtr<nsRenderingContext> rc =
       aFrame->PresContext()->PresShell()->CreateReferenceRenderingContext();
     nsRefPtr<nsFontMetrics> fm;
     nsLayoutUtils::GetFontMetricsForFrame(aFrame, getter_AddRefs(fm),
       nsLayoutUtils::FontSizeInflationFor(aFrame));
-    mWidth = nsLayoutUtils::GetStringWidth(aFrame, rc, *fm,
-                                           mStyle->mString.get(),
-                                           mStyle->mString.Length());
+    mWidth = nsLayoutUtils::AppUnitWidthOfStringBidi(mStyle->mString, aFrame,
+                                                     *fm, *rc);
   }
   mIntrinsicISize = mWidth;
   mInitialized = true;
 }
 
 }  // namespace css
 }  // namespace mozilla
--- a/layout/generic/nsBulletFrame.cpp
+++ b/layout/generic/nsBulletFrame.cpp
@@ -597,18 +597,18 @@ nsBulletFrame::GetDesiredSize(nsPresCont
       }
       AppendSpacingToPadding(fm);
       break;
 
     default:
       GetListItemText(text);
       finalSize.BSize(wm) = fm->MaxHeight();
       finalSize.ISize(wm) =
-        nsLayoutUtils::GetStringWidth(this, aRenderingContext, *fm,
-                                      text.get(), text.Length());
+        nsLayoutUtils::AppUnitWidthOfStringBidi(text, this, *fm,
+                                                *aRenderingContext);
       aMetrics.SetBlockStartAscent(fm->MaxAscent());
       break;
   }
   aMetrics.SetSize(wm, finalSize);
 }
 
 void
 nsBulletFrame::Reflow(nsPresContext* aPresContext,
--- a/layout/generic/nsImageFrame.cpp
+++ b/layout/generic/nsImageFrame.cpp
@@ -1030,17 +1030,18 @@ nsImageFrame::MeasureString(const char16
         len = i;  // don't include the space when measuring
         trailingSpace = true;
         break;
       }
     }
   
     // Measure this chunk of text, and see if it fits
     nscoord width =
-      nsLayoutUtils::GetStringWidth(this, &aContext, aFontMetrics, aString, len);
+      nsLayoutUtils::AppUnitWidthOfStringBidi(aString, len, this, aFontMetrics,
+                                              aContext);
     bool    fits = (totalWidth + width) <= aMaxWidth;
 
     // If it fits on the line, or it's the first word we've processed then
     // include it
     if (fits || (0 == totalWidth)) {
       // New piece fits
       totalWidth += width;
 
--- a/layout/generic/nsPageFrame.cpp
+++ b/layout/generic/nsPageFrame.cpp
@@ -238,20 +238,19 @@ nsPageFrame::ProcessSpecialCodes(const n
 
 //------------------------------------------------------------------------------
 nscoord nsPageFrame::GetXPosition(nsRenderingContext& aRenderingContext,
                                   nsFontMetrics&       aFontMetrics,
                                   const nsRect&        aRect, 
                                   int32_t              aJust,
                                   const nsString&      aStr)
 {
-  nscoord width = nsLayoutUtils::GetStringWidth(this, &aRenderingContext,
-                                                aFontMetrics,
-                                                aStr.get(), aStr.Length());
-
+  nscoord width = nsLayoutUtils::AppUnitWidthOfStringBidi(aStr, this,
+                                                          aFontMetrics,
+                                                          aRenderingContext);
   nscoord x = aRect.x;
   switch (aJust) {
     case nsIPrintSettings::kJustLeft:
       x += mPD->mEdgePaperMargin.left;
       break;
 
     case nsIPrintSettings::kJustCenter:
       x += (aRect.width - width) / 2;
--- a/layout/generic/nsSubDocumentFrame.cpp
+++ b/layout/generic/nsSubDocumentFrame.cpp
@@ -95,20 +95,18 @@ static void
 EndSwapDocShellsForViews(nsView* aView);
 
 void
 nsSubDocumentFrame::Init(nsIContent*       aContent,
                          nsContainerFrame* aParent,
                          nsIFrame*         aPrevInFlow)
 {
   // determine if we are a <frame> or <iframe>
-  if (aContent) {
-    nsCOMPtr<nsIDOMHTMLFrameElement> frameElem = do_QueryInterface(aContent);
-    mIsInline = frameElem ? false : true;
-  }
+  nsCOMPtr<nsIDOMHTMLFrameElement> frameElem = do_QueryInterface(aContent);
+  mIsInline = frameElem ? false : true;
 
   nsLeafFrame::Init(aContent, aParent, aPrevInFlow);
 
   // We are going to create an inner view.  If we need a view for the
   // OuterFrame but we wait for the normal view creation path in
   // nsCSSFrameConstructor, then we will lose because the inner view's
   // parent will already have been set to some outer view (e.g., the
   // canvas) when it really needs to have this frame's view as its
new file mode 100644
--- /dev/null
+++ b/layout/reftests/mathml/mfenced-12-ref.html
@@ -0,0 +1,40 @@
+<!doctype html>
+<html class="reftest-wait">
+
+<math style="font-size:25px; position: absolute; top: 10px; left:10px;">
+  <mrow id="outer">
+    <mo>(</mo>
+    <mrow>
+      <mn id="a" style="visibility:hidden;">a</mn>
+      <mo>&amp;</mo>
+      <mn id="c" style="visibility:hidden;">c</mn>
+      </mrow>
+    <mo>)</mo>
+  </mrow>
+</math>
+
+<!-- Implementation kludge.  <mfenced> renders the position of the ampersand in
+     a slightly different position compared to <mo>+<mrow>.
+     In this test we are only concerned about the size of the fences "(" and
+     ")", so the ampersand gets redacted. -->
+<div id="div" style="position: absolute; background:black; top: 0px;
+                     height: 120px;"></div>
+
+<script>
+  function doTest()
+  {
+    a = document.getElementById("a");
+    c = document.getElementById("c");
+    div = document.getElementById("div");
+    outer = document.getElementById("outer");
+
+    left = a.getBoundingClientRect().left;  // div's left
+    div.style.left = left + 'px';
+    div.style.width = (c.getBoundingClientRect().right - left ) + 'px';
+
+    document.documentElement.removeAttribute("class");
+   }
+   window.addEventListener("MozReftestInvalidate", doTest, false);
+</script>
+
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/mathml/mfenced-12.html
@@ -0,0 +1,35 @@
+<!doctype html>
+<html class="reftest-wait">
+
+<math style="font-size:25px; position: absolute; top: 10px; left:10px;">
+  <mfenced id="outer" separators="&amp;">
+      <mn id="a" style="visibility:hidden;">a</mn>
+      <mn id="c" style="visibility:hidden;">c</mn>
+  </mfenced>
+</math>
+
+<!-- Implementation kludge.  <mfenced> renders the position of the ampersand in
+     a slightly different position compared to <mo>+<mrow>.
+     In this test we are only concerned about the size of the fences "(" and
+     ")", so the ampersand gets redacted. -->
+<div id="div" style="position: absolute; background:black; top: 0px;
+                     height: 120px;"></div>
+
+<script>
+  function doTest()
+  {
+    a = document.getElementById("a");
+    c = document.getElementById("c");
+    div = document.getElementById("div");
+    outer = document.getElementById("outer");
+
+    left = a.getBoundingClientRect().left;  // div's left
+    div.style.left = left + 'px';
+    div.style.width = (c.getBoundingClientRect().right - left ) + 'px';
+
+    document.documentElement.removeAttribute("class");
+   }
+   window.addEventListener("MozReftestInvalidate", doTest, false);
+</script>
+
+</html>
--- a/layout/reftests/mathml/reftest.list
+++ b/layout/reftests/mathml/reftest.list
@@ -33,16 +33,17 @@ skip-if(B2G&&browserIsRemote) random-if(
 == mfenced-5c.xhtml mfenced-5-ref.xhtml
 == mfenced-5d.xhtml mfenced-5-ref.xhtml
 == mfenced-6.html mfenced-6-ref.html
 == mfenced-7.html mfenced-7-ref.html
 != mfenced-8.html mfenced-8-ref.html
 == mfenced-9.html mfenced-9-ref.html
 fails-if(/^Windows\x20NT\x205\.1/.test(http.oscpu)) == mfenced-10.html mfenced-10-ref.html # Windows versions without Cambria Math, see bug 670592
 fails-if(winWidget&&d2d) == mfenced-11.html mfenced-11-ref.html
+== mfenced-12.html mfenced-12-ref.html
 == mi-mathvariant-1.xhtml mi-mathvariant-1-ref.xhtml
 == mi-mathvariant-2.xhtml mi-mathvariant-2-ref.xhtml
 != mi-mathvariant-3.html mi-mathvariant-3-ref.html
 != non-spacing-accent-1.xhtml non-spacing-accent-1-ref.xhtml
 == overbar-width-1.xhtml overbar-width-1-ref.xhtml
 skip-if(B2G) == quotes-1.xhtml quotes-1-ref.xhtml
 != stretchy-underbar-1.xhtml stretchy-underbar-1-ref.xhtml
 != stretchy-munderover-1a.html stretchy-munderover-1-ref.html
--- a/layout/style/nsStyleStruct.h
+++ b/layout/style/nsStyleStruct.h
@@ -3035,16 +3035,20 @@ struct nsStyleSVGReset {
     return NS_CombineHint(nsChangeHint_NeedReflow,
                           nsChangeHint_ClearAncestorIntrinsics);
   }
 
   bool HasFilters() const {
     return mFilters.Length() > 0;
   }
 
+  bool HasNonScalingStroke() const {
+    return mVectorEffect == NS_STYLE_VECTOR_EFFECT_NON_SCALING_STROKE;
+  }
+
   nsStyleClipPath mClipPath;          // [reset]
   nsTArray<nsStyleFilter> mFilters;   // [reset]
   nsCOMPtr<nsIURI> mMask;             // [reset]
   nscolor          mStopColor;        // [reset]
   nscolor          mFloodColor;       // [reset]
   nscolor          mLightingColor;    // [reset]
 
   float            mStopOpacity;      // [reset]
--- a/layout/svg/nsSVGPathGeometryFrame.cpp
+++ b/layout/svg/nsSVGPathGeometryFrame.cpp
@@ -458,123 +458,142 @@ nsSVGPathGeometryFrame::GetBBoxContribut
   if (aToBBoxUserspace.IsSingular()) {
     // XXX ReportToConsole
     return bbox;
   }
 
   nsSVGPathGeometryElement* element =
     static_cast<nsSVGPathGeometryElement*>(mContent);
 
-  RefPtr<DrawTarget> tmpDT;
-#ifdef XP_WIN
-  // Unfortunately D2D backed DrawTarget produces bounds with rounding errors
-  // when whole number results are expected, even in the case of trivial
-  // calculations. To avoid that and meet the expectations of web content we
-  // have to use a CAIRO DrawTarget. The most efficient way to do that is to
-  // wrap the cached cairo_surface_t from ScreenReferenceSurface():
-  nsRefPtr<gfxASurface> refSurf =
-    gfxPlatform::GetPlatform()->ScreenReferenceSurface();
-  tmpDT = gfxPlatform::GetPlatform()->
-    CreateDrawTargetForSurface(refSurf, IntSize(1, 1));
-#else
-  tmpDT = gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget();
-#endif
+  bool getFill = (aFlags & nsSVGUtils::eBBoxIncludeFillGeometry) ||
+                 ((aFlags & nsSVGUtils::eBBoxIncludeFill) &&
+                  StyleSVG()->mFill.mType != eStyleSVGPaintType_None);
+
+  bool getStroke = (aFlags & nsSVGUtils::eBBoxIncludeStrokeGeometry) ||
+                   ((aFlags & nsSVGUtils::eBBoxIncludeStroke) &&
+                    nsSVGUtils::HasStroke(this));
 
-  FillRule fillRule = nsSVGUtils::ToFillRule(StyleSVG()->mFillRule);
-  RefPtr<Path> pathInUserSpace = element->GetOrBuildPath(*tmpDT, fillRule);
-  if (!pathInUserSpace) {
-    return bbox;
-  }
-  RefPtr<Path> pathInBBoxSpace;
-  if (aToBBoxUserspace.IsIdentity()) {
-    pathInBBoxSpace = pathInUserSpace;
-  } else {
-    RefPtr<PathBuilder> builder =
-      pathInUserSpace->TransformedCopyToBuilder(aToBBoxUserspace, fillRule);
-    pathInBBoxSpace = builder->Finish();
-    if (!pathInBBoxSpace) {
-      return bbox;
+  bool gotSimpleBounds = false;
+  if (!StyleSVGReset()->HasNonScalingStroke()) {
+    Float strokeWidth = getStroke ? nsSVGUtils::GetStrokeWidth(this) : 0.f;
+    Rect simpleBounds;
+    gotSimpleBounds = element->GetGeometryBounds(&simpleBounds, strokeWidth,
+                                                 aToBBoxUserspace);
+    if (gotSimpleBounds) {
+      bbox = simpleBounds;
     }
   }
 
-  // Be careful when replacing the following logic to get the fill and stroke
-  // extents independently (instead of computing the stroke extents from the
-  // path extents). You may think that you can just use the stroke extents if
-  // there is both a fill and a stroke. In reality it's necessary to calculate
-  // both the fill and stroke extents, and take the union of the two. There are
-  // two reasons for this:
-  //
-  // # Due to stroke dashing, in certain cases the fill extents could actually
-  //   extend outside the stroke extents.
-  // # If the stroke is very thin, cairo won't paint any stroke, and so the
-  //   stroke bounds that it will return will be empty.
+  if (!gotSimpleBounds) {
+    // Get the bounds using a Moz2D Path object (more expensive):
+    RefPtr<DrawTarget> tmpDT;
+#ifdef XP_WIN
+    // Unfortunately D2D backed DrawTarget produces bounds with rounding errors
+    // when whole number results are expected, even in the case of trivial
+    // calculations. To avoid that and meet the expectations of web content we
+    // have to use a CAIRO DrawTarget. The most efficient way to do that is to
+    // wrap the cached cairo_surface_t from ScreenReferenceSurface():
+    nsRefPtr<gfxASurface> refSurf =
+      gfxPlatform::GetPlatform()->ScreenReferenceSurface();
+    tmpDT = gfxPlatform::GetPlatform()->
+      CreateDrawTargetForSurface(refSurf, IntSize(1, 1));
+#else
+    tmpDT = gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget();
+#endif
 
-  Rect pathBBoxExtents = pathInBBoxSpace->GetBounds();
-  if (!pathBBoxExtents.IsFinite()) {
-    // This can happen in the case that we only have a move-to command in the
-    // path commands, in which case we know nothing gets rendered.
-    return bbox;
-  }
+    FillRule fillRule = nsSVGUtils::ToFillRule(StyleSVG()->mFillRule);
+    RefPtr<Path> pathInUserSpace = element->GetOrBuildPath(*tmpDT, fillRule);
+    if (!pathInUserSpace) {
+      return bbox;
+    }
+    RefPtr<Path> pathInBBoxSpace;
+    if (aToBBoxUserspace.IsIdentity()) {
+      pathInBBoxSpace = pathInUserSpace;
+    } else {
+      RefPtr<PathBuilder> builder =
+        pathInUserSpace->TransformedCopyToBuilder(aToBBoxUserspace, fillRule);
+      pathInBBoxSpace = builder->Finish();
+      if (!pathInBBoxSpace) {
+        return bbox;
+      }
+    }
 
-  // Account for fill:
-  if ((aFlags & nsSVGUtils::eBBoxIncludeFillGeometry) ||
-      ((aFlags & nsSVGUtils::eBBoxIncludeFill) &&
-       StyleSVG()->mFill.mType != eStyleSVGPaintType_None)) {
-    bbox = pathBBoxExtents;
-  }
+    // Be careful when replacing the following logic to get the fill and stroke
+    // extents independently (instead of computing the stroke extents from the
+    // path extents). You may think that you can just use the stroke extents if
+    // there is both a fill and a stroke. In reality it's necessary to
+    // calculate both the fill and stroke extents, and take the union of the
+    // two. There are two reasons for this:
+    //
+    // # Due to stroke dashing, in certain cases the fill extents could
+    //   actually extend outside the stroke extents.
+    // # If the stroke is very thin, cairo won't paint any stroke, and so the
+    //   stroke bounds that it will return will be empty.
 
-  // Account for stroke:
-  if ((aFlags & nsSVGUtils::eBBoxIncludeStrokeGeometry) ||
-      ((aFlags & nsSVGUtils::eBBoxIncludeStroke) &&
-       nsSVGUtils::HasStroke(this))) {
+    Rect pathBBoxExtents = pathInBBoxSpace->GetBounds();
+    if (!pathBBoxExtents.IsFinite()) {
+      // This can happen in the case that we only have a move-to command in the
+      // path commands, in which case we know nothing gets rendered.
+      return bbox;
+    }
+
+    // Account for fill:
+    if (getFill) {
+      bbox = pathBBoxExtents;
+    }
+
+    // Account for stroke:
+    if (getStroke) {
 #if 0
-    // This disabled code is how we would calculate the stroke bounds using
-    // Moz2D Path::GetStrokedBounds(). Unfortunately at the time of writing it
-    // there are two problems that prevent us from using it.
-    //
-    // First, it seems that some of the Moz2D backends are really dumb. Not
-    // only do some GetStrokeOptions() implementations sometimes significantly
-    // overestimate the stroke bounds, but if an argument is passed for the
-    // aTransform parameter then they just return bounds-of-transformed-bounds.
-    // These two things combined can lead the bounds to be unacceptably
-    // oversized, leading to massive over-invalidation.
-    //
-    // Second, the way we account for non-scaling-stroke by transforming the
-    // path using the transform to the outer-<svg> element is not compatible
-    // with the way that nsSVGPathGeometryFrame::Reflow() inserts a scale into
-    // aToBBoxUserspace and then scales the bounds that we return.
-    SVGContentUtils::AutoStrokeOptions strokeOptions;
-    SVGContentUtils::GetStrokeOptions(&strokeOptions, element, StyleContext(),
-                                      nullptr, SVGContentUtils::eIgnoreStrokeDashing);
-    Rect strokeBBoxExtents;
-    gfxMatrix userToOuterSVG;
-    if (nsSVGUtils::GetNonScalingStrokeTransform(this, &userToOuterSVG)) {
-      Matrix outerSVGToUser = ToMatrix(userToOuterSVG);
-      outerSVGToUser.Invert();
-      Matrix outerSVGToBBox = aToBBoxUserspace * outerSVGToUser;
-      RefPtr<PathBuilder> builder =
-        pathInUserSpace->TransformedCopyToBuilder(ToMatrix(userToOuterSVG));
-      RefPtr<Path> pathInOuterSVGSpace = builder->Finish();
-      strokeBBoxExtents =
-        pathInOuterSVGSpace->GetStrokedBounds(strokeOptions, outerSVGToBBox);
-    } else {
-      strokeBBoxExtents =
-        pathInUserSpace->GetStrokedBounds(strokeOptions, aToBBoxUserspace);
-    }
-    MOZ_ASSERT(strokeBBoxExtents.IsFinite(), "bbox is about to go bad");
-    bbox.UnionEdges(strokeBBoxExtents);
+      // This disabled code is how we would calculate the stroke bounds using
+      // Moz2D Path::GetStrokedBounds(). Unfortunately at the time of writing
+      // it there are two problems that prevent us from using it.
+      //
+      // First, it seems that some of the Moz2D backends are really dumb. Not
+      // only do some GetStrokeOptions() implementations sometimes
+      // significantly overestimate the stroke bounds, but if an argument is
+      // passed for the aTransform parameter then they just return bounds-of-
+      // transformed-bounds.  These two things combined can lead the bounds to
+      // be unacceptably oversized, leading to massive over-invalidation.
+      //
+      // Second, the way we account for non-scaling-stroke by transforming the
+      // path using the transform to the outer-<svg> element is not compatible
+      // with the way that nsSVGPathGeometryFrame::Reflow() inserts a scale
+      // into aToBBoxUserspace and then scales the bounds that we return.
+      SVGContentUtils::AutoStrokeOptions strokeOptions;
+      SVGContentUtils::GetStrokeOptions(&strokeOptions, element,
+                                        StyleContext(), nullptr,
+                                        SVGContentUtils::eIgnoreStrokeDashing);
+      Rect strokeBBoxExtents;
+      gfxMatrix userToOuterSVG;
+      if (nsSVGUtils::GetNonScalingStrokeTransform(this, &userToOuterSVG)) {
+        Matrix outerSVGToUser = ToMatrix(userToOuterSVG);
+        outerSVGToUser.Invert();
+        Matrix outerSVGToBBox = aToBBoxUserspace * outerSVGToUser;
+        RefPtr<PathBuilder> builder =
+          pathInUserSpace->TransformedCopyToBuilder(ToMatrix(userToOuterSVG));
+        RefPtr<Path> pathInOuterSVGSpace = builder->Finish();
+        strokeBBoxExtents =
+          pathInOuterSVGSpace->GetStrokedBounds(strokeOptions, outerSVGToBBox);
+      } else {
+        strokeBBoxExtents =
+          pathInUserSpace->GetStrokedBounds(strokeOptions, aToBBoxUserspace);
+      }
+      MOZ_ASSERT(strokeBBoxExtents.IsFinite(), "bbox is about to go bad");
+      bbox.UnionEdges(strokeBBoxExtents);
 #else
     // For now we just use nsSVGUtils::PathExtentsToMaxStrokeExtents:
-    gfxRect strokeBBoxExtents =
-      nsSVGUtils::PathExtentsToMaxStrokeExtents(ThebesRect(pathBBoxExtents),
-                                                this,
-                                                ThebesMatrix(aToBBoxUserspace));
-    MOZ_ASSERT(ToRect(strokeBBoxExtents).IsFinite(), "bbox is about to go bad");
-    bbox.UnionEdges(strokeBBoxExtents);
+      gfxRect strokeBBoxExtents =
+        nsSVGUtils::PathExtentsToMaxStrokeExtents(ThebesRect(pathBBoxExtents),
+                                                  this,
+                                                  ThebesMatrix(aToBBoxUserspace));
+      MOZ_ASSERT(ToRect(strokeBBoxExtents).IsFinite(), "bbox is about to go bad");
+      bbox.UnionEdges(strokeBBoxExtents);
 #endif
+    }
   }
 
   // Account for markers:
   if ((aFlags & nsSVGUtils::eBBoxIncludeMarkers) != 0 &&
       static_cast<nsSVGPathGeometryElement*>(mContent)->IsMarkable()) {
 
     float strokeWidth = nsSVGUtils::GetStrokeWidth(this);
     MarkerProperties properties = GetMarkerProperties(this);
--- a/layout/tools/recording/recording-cmdline.js
+++ b/layout/tools/recording/recording-cmdline.js
@@ -62,13 +62,13 @@ RecordingCmdLineHandler.prototype =
 
         var wwatch = Components.classes["@mozilla.org/embedcomp/window-watcher;1"]
                                .getService(nsIWindowWatcher);
         wwatch.openWindow(null, "chrome://recording/content/recording.xul", "_blank",
                           "chrome,dialog=no,all", args);
         cmdLine.preventDefault = true;
     },
 
-    helpInfo : "  -recording <file>  Record drawing for a given URL.\n" +
-               "  -recording-output <file> Specify destination file for a drawing recording.\n"
+    helpInfo : "  --recording <file> Record drawing for a given URL.\n" +
+               "  --recording-output <file> Specify destination file for a drawing recording.\n"
 };
 
 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([RecordingCmdLineHandler]);
--- a/layout/tools/reftest/reftest-cmdline.js
+++ b/layout/tools/reftest/reftest-cmdline.js
@@ -100,12 +100,12 @@ RefTestCmdLineHandler.prototype =
         dummy.focus();
         loadReftests();
       }
     }
 
     cmdLine.preventDefault = true;
   },
 
-  helpInfo : "  -reftest <file>    Run layout acceptance tests on given manifest.\n"
+  helpInfo : "  --reftest <file>   Run layout acceptance tests on given manifest.\n"
 };
 
 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([RefTestCmdLineHandler]);
--- a/layout/xul/nsListBoxBodyFrame.cpp
+++ b/layout/xul/nsListBoxBodyFrame.cpp
@@ -722,18 +722,18 @@ nsListBoxBodyFrame::ComputeIntrinsicISiz
             }
           }
 
           nsRefPtr<nsFontMetrics> fm;
           nsLayoutUtils::GetFontMetricsForStyleContext(styleContext,
                                                        getter_AddRefs(fm));
 
           nscoord textWidth =
-            nsLayoutUtils::GetStringWidth(this, rendContext, *fm,
-                                          value.get(), value.Length());
+            nsLayoutUtils::AppUnitWidthOfStringBidi(value, this, *fm,
+                                                    *rendContext);
           textWidth += width;
 
           if (textWidth > largestWidth) 
             largestWidth = textWidth;
         }
       }
     }
   }
--- a/layout/xul/nsTextBoxFrame.cpp
+++ b/layout/xul/nsTextBoxFrame.cpp
@@ -608,20 +608,19 @@ nsTextBoxFrame::CalculateTitleForWidth(n
         mCroppedTitle.Truncate();
         return 0;
     }
 
     nsRefPtr<nsFontMetrics> fm;
     nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm));
 
     // see if the text will completely fit in the width given
-    nscoord titleWidth = nsLayoutUtils::GetStringWidth(this, &aRenderingContext,
-                                                       *fm,
-                                                       mTitle.get(), mTitle.Length());
-
+    nscoord titleWidth =
+      nsLayoutUtils::AppUnitWidthOfStringBidi(mTitle, this, *fm,
+                                              aRenderingContext);
     if (titleWidth <= aWidth) {
         mCroppedTitle = mTitle;
         if (HasRTLChars(mTitle)) {
             mState |= NS_FRAME_IS_BIDI;
         }
         return titleWidth;  // fits, done.
     }
 
@@ -708,18 +707,18 @@ nsTextBoxFrame::CalculateTitleForWidth(n
             mTitle.Right(copy, length-1-i);
             mCroppedTitle += copy;
         }
         break;
 
         case CropCenter:
         {
             nscoord stringWidth =
-                nsLayoutUtils::GetStringWidth(this, &aRenderingContext, *fm,
-                                              mTitle.get(), mTitle.Length());
+                nsLayoutUtils::AppUnitWidthOfStringBidi(mTitle, this, *fm,
+                                                        aRenderingContext);
             if (stringWidth <= aWidth) {
                 // the entire string will fit in the maximum width
                 mCroppedTitle.Insert(mTitle, 0);
                 break;
             }
 
             // determine how much of the string will fit in the max width
             nscoord charWidth = 0;
@@ -766,18 +765,18 @@ nsTextBoxFrame::CalculateTitleForWidth(n
                 rightPos--;
             }
 
             mCroppedTitle = leftString + kEllipsis + rightString;
         }
         break;
     }
 
-    return nsLayoutUtils::GetStringWidth(this, &aRenderingContext, *fm,
-                                         mCroppedTitle.get(), mCroppedTitle.Length());
+    return nsLayoutUtils::AppUnitWidthOfStringBidi(mCroppedTitle, this, *fm,
+                                                   aRenderingContext);
 }
 
 #define OLD_ELLIPSIS NS_LITERAL_STRING("...")
 
 // the following block is to append the accesskey to mTitle if there is an accesskey
 // but the mTitle doesn't have the character
 void
 nsTextBoxFrame::UpdateAccessTitle()
@@ -995,18 +994,18 @@ nsTextBoxFrame::GetTextSize(nsPresContex
                             nsRenderingContext& aRenderingContext,
                             const nsString& aString,
                             nsSize& aSize, nscoord& aAscent)
 {
     nsRefPtr<nsFontMetrics> fontMet;
     nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fontMet));
     aSize.height = fontMet->MaxHeight();
     aSize.width =
-      nsLayoutUtils::GetStringWidth(this, &aRenderingContext, *fontMet,
-                                    aString.get(), aString.Length());
+      nsLayoutUtils::AppUnitWidthOfStringBidi(aString, this, *fontMet,
+                                              aRenderingContext);
     aAscent = fontMet->MaxAscent();
 }
 
 void
 nsTextBoxFrame::CalcTextSize(nsBoxLayoutState& aBoxLayoutState)
 {
     if (mNeedsRecalc)
     {
--- a/layout/xul/tree/nsTreeBodyFrame.cpp
+++ b/layout/xul/tree/nsTreeBodyFrame.cpp
@@ -1314,19 +1314,19 @@ void
 nsTreeBodyFrame::AdjustForCellText(nsAutoString& aText,
                                    int32_t aRowIndex,  nsTreeColumn* aColumn,
                                    nsRenderingContext& aRenderingContext,
                                    nsFontMetrics& aFontMetrics,
                                    nsRect& aTextRect)
 {
   NS_PRECONDITION(aColumn && aColumn->GetFrame(), "invalid column passed");
 
-  nscoord width =
-    nsLayoutUtils::GetStringWidth(this, &aRenderingContext, aFontMetrics,
-                                  aText.get(), aText.Length());
+  nscoord width = nsLayoutUtils::AppUnitWidthOfStringBidi(aText, this,
+                                                          aFontMetrics,
+                                                          aRenderingContext);
   nscoord maxWidth = aTextRect.width;
 
   if (aColumn->Overflow()) {
     DebugOnly<nsresult> rv;
     nsTreeColumn* nextColumn = aColumn->GetNext();
     while (nextColumn && width > maxWidth) {
       while (nextColumn) {
         nscoord width;
@@ -1454,19 +1454,18 @@ nsTreeBodyFrame::AdjustForCellText(nsAut
           aText = leftStr;
           aText.Append(kEllipsis);
           aText += rightStr;
         }
         break;
       }
     }
 
-    width = nsLayoutUtils::GetStringWidth(this, &aRenderingContext,
-                                          aFontMetrics, aText.get(),
-                                          aText.Length());
+    width = nsLayoutUtils::AppUnitWidthOfStringBidi(aText, this, aFontMetrics,
+                                                    aRenderingContext);
   }
 
   switch (aColumn->GetTextAlignment()) {
     case NS_STYLE_TEXT_ALIGN_RIGHT: {
       aTextRect.x += aTextRect.width - width;
     }
     break;
     case NS_STYLE_TEXT_ALIGN_CENTER: {
@@ -1743,19 +1742,18 @@ nsTreeBodyFrame::GetCellWidth(int32_t aR
 
   // Get the borders and padding for the text.
   GetBorderPadding(textContext, bp);
 
   nsRefPtr<nsFontMetrics> fm;
   nsLayoutUtils::GetFontMetricsForStyleContext(textContext,
                                                getter_AddRefs(fm));
   // Get the width of the text itself
-  nscoord width =
-    nsLayoutUtils::GetStringWidth(this, aRenderingContext, *fm,
-                                  cellText.get(), cellText.Length());
+  nscoord width = nsLayoutUtils::AppUnitWidthOfStringBidi(cellText, this, *fm,
+                                                          *aRenderingContext);
   nscoord totalTextWidth = width + bp.left + bp.right;
   aDesiredSize += totalTextWidth;
   return NS_OK;
 }
 
 nsresult
 nsTreeBodyFrame::IsCellCropped(int32_t aRow, nsITreeColumn* aCol, bool *_retval)
 {  
--- a/memory/replace/dmd/DMD.cpp
+++ b/memory/replace/dmd/DMD.cpp
@@ -222,28 +222,16 @@ StatusMsg(const char* aFmt, ...)
 /* static */ void
 InfallibleAllocPolicy::ExitOnFailure(const void* aP)
 {
   if (!aP) {
     MOZ_CRASH("DMD out of memory; aborting");
   }
 }
 
-class FpWriteFunc : public JSONWriteFunc
-{
-public:
-  explicit FpWriteFunc(FILE* aFp) : mFp(aFp) {}
-  ~FpWriteFunc() { fclose(mFp); }
-
-  void Write(const char* aStr) { fputs(aStr, mFp); }
-
-private:
-  FILE* mFp;
-};
-
 static double
 Percent(size_t part, size_t whole)
 {
   return (whole == 0) ? 0 : 100 * (double)part / whole;
 }
 
 // Commifies the number.
 static char*
@@ -284,46 +272,38 @@ class Options
     const T mDefault;
     const T mMax;
     T       mActual;
     NumOption(T aDefault, T aMax)
       : mDefault(aDefault), mMax(aMax), mActual(aDefault)
     {}
   };
 
-  enum Mode {
-    Normal,   // run normally
-    Test      // do some basic correctness tests
-  };
-
   char* mDMDEnvVar;   // a saved copy, for later printing
 
   NumOption<size_t>   mSampleBelowSize;
   NumOption<uint32_t> mMaxFrames;
   bool mShowDumpStats;
-  Mode mMode;
 
   void BadArg(const char* aArg);
   static const char* ValueIfMatch(const char* aArg, const char* aOptionName);
   static bool GetLong(const char* aArg, const char* aOptionName,
                       long aMin, long aMax, long* aValue);
   static bool GetBool(const char* aArg, const char* aOptionName, bool* aValue);
 
 public:
   explicit Options(const char* aDMDEnvVar);
 
   const char* DMDEnvVar() const { return mDMDEnvVar; }
 
   size_t SampleBelowSize() const { return mSampleBelowSize.mActual; }
   size_t MaxFrames()       const { return mMaxFrames.mActual; }
   size_t ShowDumpStats()   const { return mShowDumpStats; }
 
-  void SetSampleBelowSize(size_t aN) { mSampleBelowSize.mActual = aN; }
-
-  bool IsTestMode()   const { return mMode == Test; }
+  void SetSampleBelowSize(size_t aSize) { mSampleBelowSize.mActual = aSize; }
 };
 
 static Options *gOptions;
 
 //---------------------------------------------------------------------------
 // The global lock
 //---------------------------------------------------------------------------
 
@@ -333,63 +313,39 @@ static Options *gOptions;
 
 class MutexBase
 {
   CRITICAL_SECTION mCS;
 
   DISALLOW_COPY_AND_ASSIGN(MutexBase);
 
 public:
-  MutexBase()
-  {
-    InitializeCriticalSection(&mCS);
-  }
-
-  ~MutexBase()
-  {
-    DeleteCriticalSection(&mCS);
-  }
+  MutexBase()  { InitializeCriticalSection(&mCS); }
+  ~MutexBase() { DeleteCriticalSection(&mCS); }
 
-  void Lock()
-  {
-    EnterCriticalSection(&mCS);
-  }
-
-  void Unlock()
-  {
-    LeaveCriticalSection(&mCS);
-  }
+  void Lock()   { EnterCriticalSection(&mCS); }
+  void Unlock() { LeaveCriticalSection(&mCS); }
 };
 
 #else
 
 #include <pthread.h>
 #include <sys/types.h>
 
 class MutexBase
 {
   pthread_mutex_t mMutex;
 
   DISALLOW_COPY_AND_ASSIGN(MutexBase);
 
 public:
-  MutexBase()
-  {
-    pthread_mutex_init(&mMutex, nullptr);
-  }
+  MutexBase() { pthread_mutex_init(&mMutex, nullptr); }
 
-  void Lock()
-  {
-    pthread_mutex_lock(&mMutex);
-  }
-
-  void Unlock()
-  {
-    pthread_mutex_unlock(&mMutex);
-  }
+  void Lock()   { pthread_mutex_lock(&mMutex); }
+  void Unlock() { pthread_mutex_unlock(&mMutex); }
 };
 
 #endif
 
 class Mutex : private MutexBase
 {
   bool mIsLocked;
 
@@ -409,54 +365,39 @@ public:
 
   void Unlock()
   {
     MOZ_ASSERT(mIsLocked);
     mIsLocked = false;
     MutexBase::Unlock();
   }
 
-  bool IsLocked()
-  {
-    return mIsLocked;
-  }
+  bool IsLocked() { return mIsLocked; }
 };
 
 // This lock must be held while manipulating global state, such as
 // gStackTraceTable, gBlockTable, etc.
 static Mutex* gStateLock = nullptr;
 
 class AutoLockState
 {
   DISALLOW_COPY_AND_ASSIGN(AutoLockState);
 
 public:
-  AutoLockState()
-  {
-    gStateLock->Lock();
-  }
-  ~AutoLockState()
-  {
-    gStateLock->Unlock();
-  }
+  AutoLockState()  { gStateLock->Lock(); }
+  ~AutoLockState() { gStateLock->Unlock(); }
 };
 
 class AutoUnlockState
 {
   DISALLOW_COPY_AND_ASSIGN(AutoUnlockState);
 
 public:
-  AutoUnlockState()
-  {
-    gStateLock->Unlock();
-  }
-  ~AutoUnlockState()
-  {
-    gStateLock->Lock();
-  }
+  AutoUnlockState()  { gStateLock->Unlock(); }
+  ~AutoUnlockState() { gStateLock->Lock(); }
 };
 
 //---------------------------------------------------------------------------
 // Thread-local storage and blocking of intercepts
 //---------------------------------------------------------------------------
 
 #ifdef XP_WIN
 
@@ -509,20 +450,17 @@ public:
   }
 
   bool UnblockIntercepts()
   {
     MOZ_ASSERT(mBlockIntercepts);
     return mBlockIntercepts = false;
   }
 
-  bool InterceptsAreBlocked() const
-  {
-    return mBlockIntercepts;
-  }
+  bool InterceptsAreBlocked() const { return mBlockIntercepts; }
 };
 
 /* static */ Thread*
 Thread::Fetch()
 {
   Thread* t = static_cast<Thread*>(DMD_GET_TLS_DATA(gTlsIndex));
 
   if (MOZ_UNLIKELY(!t)) {
@@ -558,20 +496,17 @@ public:
 
 //---------------------------------------------------------------------------
 // Location service
 //---------------------------------------------------------------------------
 
 class StringTable
 {
 public:
-  StringTable()
-  {
-    (void)mSet.init(64);
-  }
+  StringTable() { (void)mSet.init(64); }
 
   const char*
   Intern(const char* aString)
   {
     StringHashSet::AddPtr p = mSet.lookupForAdd(aString);
     if (p) {
       return *p;
     }
@@ -613,23 +548,21 @@ private:
   typedef js::HashSet<const char*, StringHasher, InfallibleAllocPolicy> StringHashSet;
 
   StringHashSet mSet;
 };
 
 class StringAlloc
 {
 public:
-  static char*
-  copy(const char* aString)
+  static char* copy(const char* aString)
   {
     return InfallibleAllocPolicy::strdup_(aString);
   }
-  static void
-  free(char* aString)
+  static void free(char* aString)
   {
     InfallibleAllocPolicy::free_(aString);
   }
 };
 
 struct DescribeCodeAddressLock
 {
   static void Unlock() { gStateLock->Unlock(); }
@@ -1273,18 +1206,17 @@ Options::GetBool(const char* aArg, const
 //   of 4096, for example, then our alloc counter would only take on even
 //   values, because jemalloc always rounds up requests sizes.  In contrast, a
 //   prime size will explore all possible values of the alloc counter.
 //
 Options::Options(const char* aDMDEnvVar)
   : mDMDEnvVar(InfallibleAllocPolicy::strdup_(aDMDEnvVar)),
     mSampleBelowSize(4093, 100 * 100 * 1000),
     mMaxFrames(StackTrace::MaxFrames, StackTrace::MaxFrames),
-    mShowDumpStats(false),
-    mMode(Normal)
+    mShowDumpStats(false)
 {
   char* e = mDMDEnvVar;
   if (strcmp(e, "1") != 0) {
     bool isEnd = false;
     while (!isEnd) {
       // Consume leading whitespace.
       while (isspace(*e)) {
         e++;
@@ -1309,21 +1241,16 @@ Options::Options(const char* aDMDEnvVar)
         mSampleBelowSize.mActual = myLong;
 
       } else if (GetLong(arg, "--max-frames", 1, mMaxFrames.mMax, &myLong)) {
         mMaxFrames.mActual = myLong;
 
       } else if (GetBool(arg, "--show-dump-stats", &myBool)) {
         mShowDumpStats = myBool;
 
-      } else if (strcmp(arg, "--mode=normal") == 0) {
-        mMode = Options::Normal;
-      } else if (strcmp(arg, "--mode=test")   == 0) {
-        mMode = Options::Test;
-
       } else if (strcmp(arg, "") == 0) {
         // This can only happen if there is trailing whitespace.  Ignore.
         MOZ_ASSERT(isEnd);
 
       } else {
         BadArg(arg);
       }
 
@@ -1349,48 +1276,32 @@ Options::BadArg(const char* aArg)
   StatusMsg("  --sample-below=<1..%d> Sample blocks smaller than this [%d]\n",
             int(mSampleBelowSize.mMax),
             int(mSampleBelowSize.mDefault));
   StatusMsg("                               (prime numbers are recommended)\n");
   StatusMsg("  --max-frames=<1..%d>         Max. depth of stack traces [%d]\n",
             int(mMaxFrames.mMax),
             int(mMaxFrames.mDefault));
   StatusMsg("  --show-dump-stats=<yes|no>   Show stats about dumps? [no]\n");
-  StatusMsg("  --mode=<normal|test>         Mode of operation [normal]\n");
   StatusMsg("\n");
   exit(1);
 }
 
 //---------------------------------------------------------------------------
 // DMD start-up
 //---------------------------------------------------------------------------
 
 #ifdef XP_MACOSX
 static void
 NopStackWalkCallback(uint32_t aFrameNumber, void* aPc, void* aSp,
                      void* aClosure)
 {
 }
 #endif
 
-// Note that fopen() can allocate.
-static FILE*
-OpenOutputFile(const char* aFilename)
-{
-  FILE* fp = fopen(aFilename, "w");
-  if (!fp) {
-    StatusMsg("can't create %s file: %s\n", aFilename, strerror(errno));
-    exit(1);
-  }
-  return fp;
-}
-
-static void RunTestMode(UniquePtr<FpWriteFunc> aF1, UniquePtr<FpWriteFunc> aF2,
-                        UniquePtr<FpWriteFunc> aF3, UniquePtr<FpWriteFunc> aF4);
-
 // WARNING: this function runs *very* early -- before all static initializers
 // have run.  For this reason, non-scalar globals such as gStateLock and
 // gStackTraceTable are allocated dynamically (so we can guarantee their
 // construction in this function) rather than statically.
 static void
 Init(const malloc_table_t* aMallocTable)
 {
   MOZ_ASSERT(!gIsDMDRunning);
@@ -1434,41 +1345,17 @@ Init(const malloc_table_t* aMallocTable)
 
     gStackTraceTable = InfallibleAllocPolicy::new_<StackTraceTable>();
     gStackTraceTable->init(8192);
 
     gBlockTable = InfallibleAllocPolicy::new_<BlockTable>();
     gBlockTable->init(8192);
   }
 
-  if (gOptions->IsTestMode()) {
-    // Do all necessary allocations before setting gIsDMDRunning so those
-    // allocations don't show up in our results.  Once gIsDMDRunning is set we
-    // are intercepting malloc et al. in earnest.
-    //
-    // These files are written to $CWD. It would probably be better to write
-    // them to "TmpD" using the directory service, but that would require
-    // linking DMD with XPCOM.
-    auto f1 = MakeUnique<FpWriteFunc>(OpenOutputFile("full-empty.json"));
-    auto f2 = MakeUnique<FpWriteFunc>(OpenOutputFile("full-unsampled1.json"));
-    auto f3 = MakeUnique<FpWriteFunc>(OpenOutputFile("full-unsampled2.json"));
-    auto f4 = MakeUnique<FpWriteFunc>(OpenOutputFile("full-sampled.json"));
-    gIsDMDRunning = true;
-
-    StatusMsg("running test mode...\n");
-    RunTestMode(Move(f1), Move(f2), Move(f3), Move(f4));
-    StatusMsg("finished test mode; DMD is now disabled again\n");
-
-    // Continue running so that the xpcshell test can complete, but DMD no
-    // longer needs to be running.
-    gIsDMDRunning = false;
-
-  } else {
-    gIsDMDRunning = true;
-  }
+  gIsDMDRunning = true;
 }
 
 //---------------------------------------------------------------------------
 // DMD reporting and unreporting
 //---------------------------------------------------------------------------
 
 static void
 ReportHelper(const void* aPtr, bool aReportedOnAlloc)
@@ -1845,266 +1732,23 @@ AnalyzeReports(JSONWriter& aWriter)
   AnalyzeReportsImpl(aWriter);
   ClearReports();
 }
 
 //---------------------------------------------------------------------------
 // Testing
 //---------------------------------------------------------------------------
 
-// This function checks that heap blocks that have the same stack trace but
-// different (or no) reporters get aggregated separately.
-void Foo(int aSeven)
+MOZ_EXPORT void
+SetSampleBelowSize(size_t aSize)
 {
-  char* a[6];
-  for (int i = 0; i < aSeven - 1; i++) {
-    a[i] = (char*) replace_malloc(128 - 16*i);
-  }
-
-  for (int i = 0; i < aSeven - 5; i++) {
-    Report(a[i]);                   // reported
-  }
-  Report(a[2]);                     // reported
-  Report(a[3]);                     // reported
-  // a[4], a[5] unreported
-}
-
-// This stops otherwise-unused variables from being optimized away.
-static void
-UseItOrLoseIt(void* aPtr, int aSeven)
-{
-  char buf[64];
-  int n = sprintf(buf, "%p\n", aPtr);
-  if (n == 20 + aSeven) {
-    fprintf(stderr, "well, that is surprising");
-  }
+  gOptions->SetSampleBelowSize(aSize);
 }
 
-// The output from this function feeds into DMD's xpcshell test.
-static void
-RunTestMode(UniquePtr<FpWriteFunc> aF1, UniquePtr<FpWriteFunc> aF2,
-            UniquePtr<FpWriteFunc> aF3, UniquePtr<FpWriteFunc> aF4)
+MOZ_EXPORT void
+ClearBlocks()
 {
-  // This test relies on the compiler not doing various optimizations, such as
-  // eliding unused replace_malloc() calls or unrolling loops with fixed
-  // iteration counts. So we want a constant value that the compiler can't
-  // determine statically, and we use that in various ways to prevent the above
-  // optimizations from happening.
-  //
-  // This code always sets |seven| to the value 7. It works because we know
-  // that "--mode=test" must be within the DMD environment variable if we reach
-  // here, but the compiler almost certainly does not.
-  //
-  char* env = getenv("DMD");
-  char* p1 = strstr(env, "--mode=t");
-  char* p2 = strstr(p1, "test");
-  int seven = p2 - p1;
-
-  // The first part of this test requires sampling to be disabled.
-  gOptions->SetSampleBelowSize(1);
-
-  //---------
-
-  // AnalyzeReports 1.  Zero for everything.
-  JSONWriter writer1(Move(aF1));
-  AnalyzeReports(writer1);
-
-  //---------
-
-  // AnalyzeReports 2: 1 freed, 9 out of 10 unreported.
-  // AnalyzeReports 3: still present and unreported.
-  int i;
-  char* a = nullptr;
-  for (i = 0; i < seven + 3; i++) {
-      a = (char*) replace_malloc(100);
-      UseItOrLoseIt(a, seven);
-  }
-  replace_free(a);
-
-  // Note: 8 bytes is the smallest requested size that gives consistent
-  // behaviour across all platforms with jemalloc.
-  // AnalyzeReports 2: reported.
-  // AnalyzeReports 3: thrice-reported.
-  char* a2 = (char*) replace_malloc(8);
-  Report(a2);
-
-  // AnalyzeReports 2: reported.
-  // AnalyzeReports 3: reportedness carries over, due to ReportOnAlloc.
-  char* b = (char*) replace_malloc(10);
-  ReportOnAlloc(b);
-
-  // ReportOnAlloc, then freed.
-  // AnalyzeReports 2: freed, irrelevant.
-  // AnalyzeReports 3: freed, irrelevant.
-  char* b2 = (char*) replace_malloc(1);
-  ReportOnAlloc(b2);
-  replace_free(b2);
-
-  // AnalyzeReports 2: reported 4 times.
-  // AnalyzeReports 3: freed, irrelevant.
-  char* c = (char*) replace_calloc(10, 3);
-  Report(c);
-  for (int i = 0; i < seven - 4; i++) {
-    Report(c);
-  }
-
-  // AnalyzeReports 2: ignored.
-  // AnalyzeReports 3: irrelevant.
-  Report((void*)(intptr_t)i);
-
-  // jemalloc rounds this up to 8192.
-  // AnalyzeReports 2: reported.
-  // AnalyzeReports 3: freed.
-  char* e = (char*) replace_malloc(4096);
-  e = (char*) replace_realloc(e, 4097);
-  Report(e);
-
-  // First realloc is like malloc;  second realloc is shrinking.
-  // AnalyzeReports 2: reported.
-  // AnalyzeReports 3: re-reported.
-  char* e2 = (char*) replace_realloc(nullptr, 1024);
-  e2 = (char*) replace_realloc(e2, 512);
-  Report(e2);
-
-  // First realloc is like malloc;  second realloc creates a min-sized block.
-  // XXX: on Windows, second realloc frees the block.
-  // AnalyzeReports 2: reported.
-  // AnalyzeReports 3: freed, irrelevant.
-  char* e3 = (char*) replace_realloc(nullptr, 1023);
-//e3 = (char*) replace_realloc(e3, 0);
-  MOZ_ASSERT(e3);
-  Report(e3);
-
-  // AnalyzeReports 2: freed, irrelevant.
-  // AnalyzeReports 3: freed, irrelevant.
-  char* f = (char*) replace_malloc(64);
-  replace_free(f);
-
-  // AnalyzeReports 2: ignored.
-  // AnalyzeReports 3: irrelevant.
-  Report((void*)(intptr_t)0x0);
-
-  // AnalyzeReports 2: mixture of reported and unreported.
-  // AnalyzeReports 3: all unreported.
-  Foo(seven);
-  Foo(seven);
-
-  // AnalyzeReports 2: twice-reported.
-  // AnalyzeReports 3: twice-reported.
-  char* g1 = (char*) replace_malloc(77);
-  ReportOnAlloc(g1);
-  ReportOnAlloc(g1);
-
-  // AnalyzeReports 2: twice-reported.
-  // AnalyzeReports 3: once-reported.
-  char* g2 = (char*) replace_malloc(78);
-  Report(g2);
-  ReportOnAlloc(g2);
-
-  // AnalyzeReports 2: twice-reported.
-  // AnalyzeReports 3: once-reported.
-  char* g3 = (char*) replace_malloc(79);
-  ReportOnAlloc(g3);
-  Report(g3);
-
-  // All the odd-ball ones.
-  // AnalyzeReports 2: all unreported.
-  // AnalyzeReports 3: all freed, irrelevant.
-  // XXX: no memalign on Mac
-//void* x = memalign(64, 65);           // rounds up to 128
-//UseItOrLoseIt(x, seven);
-  // XXX: posix_memalign doesn't work on B2G
-//void* y;
-//posix_memalign(&y, 128, 129);         // rounds up to 256
-//UseItOrLoseIt(y, seven);
-  // XXX: valloc doesn't work on Windows.
-//void* z = valloc(1);                  // rounds up to 4096
-//UseItOrLoseIt(z, seven);
-//aligned_alloc(64, 256);               // XXX: C11 only
-
-  // AnalyzeReports 2.
-  JSONWriter writer2(Move(aF2));
-  AnalyzeReports(writer2);
-
-  //---------
-
-  Report(a2);
-  Report(a2);
-  replace_free(c);
-  replace_free(e);
-  Report(e2);
-  replace_free(e3);
-//replace_free(x);
-//replace_free(y);
-//replace_free(z);
-
-  // AnalyzeReports 3.
-  JSONWriter writer3(Move(aF3));
-  AnalyzeReports(writer3);
-
-  //---------
-
-  // Clear all knowledge of existing blocks to give us a clean slate.
   gBlockTable->clear();
-
-  gOptions->SetSampleBelowSize(128);
-
-  char* s;
-
-  // This equals the sample size, and so is reported exactly.  It should be
-  // listed before records of the same size that are sampled.
-  s = (char*) replace_malloc(128);
-  UseItOrLoseIt(s, seven);
-
-  // This exceeds the sample size, and so is reported exactly.
-  s = (char*) replace_malloc(144);
-  UseItOrLoseIt(s, seven);
-
-  // These together constitute exactly one sample.
-  for (int i = 0; i < seven + 9; i++) {
-    s = (char*) replace_malloc(8);
-    UseItOrLoseIt(s, seven);
-  }
-  MOZ_ASSERT(gSmallBlockActualSizeCounter == 0);
-
-  // These fall 8 bytes short of a full sample.
-  for (int i = 0; i < seven + 8; i++) {
-    s = (char*) replace_malloc(8);
-    UseItOrLoseIt(s, seven);
-  }
-  MOZ_ASSERT(gSmallBlockActualSizeCounter == 120);
-
-  // This exceeds the sample size, and so is recorded exactly.
-  s = (char*) replace_malloc(256);
-  UseItOrLoseIt(s, seven);
-  MOZ_ASSERT(gSmallBlockActualSizeCounter == 120);
-
-  // This gets more than to a full sample from the |i < 15| loop above.
-  s = (char*) replace_malloc(96);
-  UseItOrLoseIt(s, seven);
-  MOZ_ASSERT(gSmallBlockActualSizeCounter == 88);
-
-  // This gets to another full sample.
-  for (int i = 0; i < seven - 2; i++) {
-    s = (char*) replace_malloc(8);
-    UseItOrLoseIt(s, seven);
-  }
-  MOZ_ASSERT(gSmallBlockActualSizeCounter == 0);
-
-  // This allocates 16, 32, ..., 128 bytes, which results in a heap block
-  // record that contains a mix of sample and non-sampled blocks, and so should
-  // be printed with '~' signs.
-  for (int i = 1; i <= seven + 1; i++) {
-    s = (char*) replace_malloc(i * 16);
-    UseItOrLoseIt(s, seven);
-  }
-  MOZ_ASSERT(gSmallBlockActualSizeCounter == 64);
-
-  // At the end we're 64 bytes into the current sample so we report ~1,424
-  // bytes of allocation overall, which is 64 less than the real value 1,488.
-
-  // AnalyzeReports 4.
-  JSONWriter writer4(Move(aF4));
-  AnalyzeReports(writer4);
+  gSmallBlockActualSizeCounter = 0;
 }
 
 }   // namespace dmd
 }   // namespace mozilla
--- a/memory/replace/dmd/DMD.h
+++ b/memory/replace/dmd/DMD.h
@@ -142,12 +142,20 @@ SizeOf(Sizes* aSizes);
 // Prints a status message prefixed with "DMD[<pid>]". Use sparingly.
 MOZ_EXPORT void
 StatusMsg(const char* aFmt, ...);
 
 // Indicates whether or not DMD is running.
 MOZ_EXPORT bool
 IsRunning();
 
+// Sets the sample-below size. Only used for testing purposes.
+MOZ_EXPORT void
+SetSampleBelowSize(size_t aSize);
+
+// Clears all records of live allocations. Only used for testing purposes.
+MOZ_EXPORT void
+ClearBlocks();
+
 } // namespace mozilla
 } // namespace dmd
 
 #endif /* DMD_h___ */
--- a/memory/replace/dmd/dmd.py
+++ b/memory/replace/dmd/dmd.py
@@ -1,9 +1,9 @@
-#! /usr/bin/python
+#! /usr/bin/env python
 #
 # 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/.
 
 '''This script analyzes a JSON file emitted by DMD.'''
 
 from __future__ import print_function, division
@@ -53,43 +53,105 @@ allocatorFns = [
     'pod_realloc',
     # This one necessary to fully filter some sequences of allocation functions
     # that happen in practice. Note that ??? entries that follow non-allocation
     # functions won't be stripped, as explained above.
     '???',
 ]
 
 class Record(object):
+    '''A record is an aggregation of heap blocks that have identical stack
+    traces. It can also be used to represent the difference between two
+    records.'''
+
     def __init__(self):
         self.numBlocks = 0
         self.reqSize = 0
         self.slopSize = 0
         self.usableSize = 0
         self.isSampled = False
+        self.allocatedAtDesc = None
+        self.reportedAtDescs = []
         self.usableSizes = collections.defaultdict(int)
 
+    def isZero(self, args):
+        return self.numBlocks == 0 and \
+               self.reqSize == 0 and \
+               self.slopSize == 0 and \
+               self.usableSize == 0 and \
+               len(self.usableSizes) == 0
+
+    def negate(self):
+        self.numBlocks = -self.numBlocks
+        self.reqSize = -self.reqSize
+        self.slopSize = -self.slopSize
+        self.usableSize = -self.usableSize
+
+        negatedUsableSizes = collections.defaultdict(int)
+        for (usableSize, isSampled), count in self.usableSizes.items():
+            negatedUsableSizes[(-usableSize, isSampled)] = count
+        self.usableSizes = negatedUsableSizes
+
+    def subtract(self, r):
+        # We should only be calling this on records with matching stack traces.
+        # Check this.
+        assert self.allocatedAtDesc == r.allocatedAtDesc
+        assert self.reportedAtDescs == r.reportedAtDescs
+
+        self.numBlocks -= r.numBlocks
+        self.reqSize -= r.reqSize
+        self.slopSize -= r.slopSize
+        self.usableSize -= r.usableSize
+        self.isSampled = self.isSampled or r.isSampled
+
+        usableSizes1 = self.usableSizes
+        usableSizes2 = r.usableSizes
+        usableSizes3 = collections.defaultdict(int)
+        for usableSize, isSampled in usableSizes1:
+            counts1 = usableSizes1[usableSize, isSampled]
+            if (usableSize, isSampled) in usableSizes2:
+                counts2 = usableSizes2[usableSize, isSampled]
+                del usableSizes2[usableSize, isSampled]
+                counts3 = counts1 - counts2
+                if counts3 != 0:
+                    if counts3 < 0:
+                        usableSize = -usableSize
+                        counts3 = -counts3
+                    usableSizes3[usableSize, isSampled] = counts3
+            else:
+                usableSizes3[usableSize, isSampled] = counts1
+
+        for usableSize, isSampled in usableSizes2:
+            usableSizes3[-usableSize, isSampled] = \
+                usableSizes2[usableSize, isSampled]
+
+        self.usableSizes = usableSizes3
+
     @staticmethod
     def cmpByIsSampled(r1, r2):
         # Treat sampled as smaller than non-sampled.
         return cmp(r2.isSampled, r1.isSampled)
 
     @staticmethod
     def cmpByUsableSize(r1, r2):
         # Sort by usable size, then req size, then by isSampled.
-        return cmp(r1.usableSize, r2.usableSize) or Record.cmpByReqSize(r1, r2)
+        return cmp(abs(r1.usableSize), abs(r2.usableSize)) or \
+               Record.cmpByReqSize(r1, r2)
 
     @staticmethod
     def cmpByReqSize(r1, r2):
         # Sort by req size, then by isSampled.
-        return cmp(r1.reqSize, r2.reqSize) or Record.cmpByIsSampled(r1, r2)
+        return cmp(abs(r1.reqSize), abs(r2.reqSize)) or \
+               Record.cmpByIsSampled(r1, r2)
 
     @staticmethod
     def cmpBySlopSize(r1, r2):
         # Sort by slop size, then by isSampled.
-        return cmp(r1.slopSize, r2.slopSize) or Record.cmpByIsSampled(r1, r2)
+        return cmp(abs(r1.slopSize), abs(r2.slopSize)) or \
+               Record.cmpByIsSampled(r1, r2)
 
 
 sortByChoices = {
     'usable': Record.cmpByUsableSize,   # the default
     'req':    Record.cmpByReqSize,
     'slop':   Record.cmpBySlopSize,
 }
 
@@ -100,17 +162,19 @@ def parseCommandLine():
         value = int(string)
         if value < 1 or value > 24:
             msg = '{:s} is not in the range 1..24'.format(string)
             raise argparse.ArgumentTypeError(msg)
         return value
 
     description = '''
 Analyze heap data produced by DMD.
-If no files are specified, read from stdin; input can be gzipped.
+If one file is specified, analyze it; if two files are specified, analyze the
+difference.
+Input files can be gzipped.
 Write to stdout unless -o/--output is specified.
 Stack traces are fixed to show function names, filenames and line numbers
 unless --no-fix-stacks is specified; stack fixing modifies the original file
 and may take some time. If specified, the BREAKPAD_SYMBOLS_PATH environment
 variable is used to find breakpad symbols for stack fixing.
 '''
     p = argparse.ArgumentParser(description=description)
 
@@ -126,26 +190,27 @@ variable is used to find breakpad symbol
 
     p.add_argument('-s', '--sort-by', choices=sortByChoices.keys(),
                    default=sortByChoices.keys()[0],
                    help='sort the records by a particular metric')
 
     p.add_argument('-a', '--ignore-alloc-fns', action='store_true',
                    help='ignore allocation functions at the start of traces')
 
-    p.add_argument('-b', '--show-all-block-sizes', action='store_true',
-                   help='show individual block sizes for each record')
-
     p.add_argument('--no-fix-stacks', action='store_true',
                    help='do not fix stacks')
 
     p.add_argument('--filter-stacks-for-testing', action='store_true',
                    help='filter stack traces; only useful for testing purposes')
 
-    p.add_argument('input_file')
+    p.add_argument('input_file',
+                   help='a file produced by DMD')
+
+    p.add_argument('input_file2', nargs='?',
+                   help='a file produced by DMD; if present, it is diff\'d with input_file')
 
     return p.parse_args(sys.argv[1:])
 
 
 # Fix stacks if necessary: first write the output to a tempfile, then replace
 # the original file with it.
 def fixStackTraces(inputFilename, isZipped, opener):
     # This append() call is needed to make the import statements work when this
@@ -190,28 +255,26 @@ def fixStackTraces(inputFilename, isZipp
             for line in inputFile:
                 tmpFile.write(fix(line))
 
         tmpFile.close()
 
         shutil.move(tmpFilename, inputFilename)
 
 
-def main():
-    args = parseCommandLine()
-
+def getDigestFromFile(args, inputFile):
     # Handle gzipped input if necessary.
-    isZipped = args.input_file.endswith('.gz')
+    isZipped = inputFile.endswith('.gz')
     opener = gzip.open if isZipped else open
 
     # Fix stack traces unless otherwise instructed.
     if not args.no_fix_stacks:
-        fixStackTraces(args.input_file, isZipped, opener)
+        fixStackTraces(inputFile, isZipped, opener)
 
-    with opener(args.input_file, 'rb') as f:
+    with opener(inputFile, 'rb') as f:
         j = json.load(f)
 
     if j['version'] != outputVersion:
         raise Exception("'version' property isn't '{:d}'".format(outputVersion))
 
     # Extract the main parts of the JSON object.
     invocation = j['invocation']
     dmdEnvVar = invocation['dmdEnvVar']
@@ -240,16 +303,41 @@ def main():
             if numSkippedFrames > 0:
                 traceTable[traceKey] = frameKeys[numSkippedFrames:]
 
     # Trim the number of frames.
     for traceKey, frameKeys in traceTable.items():
         if len(frameKeys) > args.max_frames:
             traceTable[traceKey] = frameKeys[:args.max_frames]
 
+    def buildTraceDescription(traceTable, frameTable, traceKey):
+        frameKeys = traceTable[traceKey]
+        fmt = '    #{:02d}{:}'
+
+        if args.filter_stacks_for_testing:
+            # When running SmokeDMD.cpp, every stack trace should contain at
+            # least one frame that contains 'DMD.cpp', from either |DMD.cpp| or
+            # |SmokeDMD.cpp|. (Or 'dmd.cpp' on Windows.) If we see such a
+            # frame, we replace the entire stack trace with a single,
+            # predictable frame. There is too much variation in the stack
+            # traces across different machines and platforms to do more precise
+            # matching, but this level of matching will result in failure if
+            # stack fixing fails completely.
+            for frameKey in frameKeys:
+                frameDesc = frameTable[frameKey]
+                if 'DMD.cpp' in frameDesc or 'dmd.cpp' in frameDesc:
+                    return [fmt.format(1, ': ... DMD.cpp ...')]
+
+        # The frame number is always '#00' (see DMD.h for why), so we have to
+        # replace that with the correct frame number.
+        desc = []
+        for n, frameKey in enumerate(traceTable[traceKey], start=1):
+            desc.append(fmt.format(n, frameTable[frameKey][3:]))
+        return desc
+
     # Aggregate blocks into records. All sufficiently similar blocks go into a
     # single record.
 
     if args.ignore_reports:
         liveRecords = collections.defaultdict(Record)
     else:
         unreportedRecords    = collections.defaultdict(Record)
         onceReportedRecords  = collections.defaultdict(Record)
@@ -259,34 +347,43 @@ def main():
     heapBlocks = 0
 
     for block in blockList:
         # For each block we compute a |recordKey|, and all blocks with the same
         # |recordKey| are aggregated into a single record. The |recordKey| is
         # derived from the block's 'alloc' and 'reps' (if present) stack
         # traces.
         #
-        # Each stack trace has a key in the JSON file. But we don't use that
-        # key to construct |recordKey|; instead we use the frame keys.
-        # This is because the stack trimming done for --max-frames can cause
-        # stack traces with distinct trace keys to end up with the same frame
-        # keys, and these should be considered equivalent. E.g. if we have
-        # distinct traces T1:[A,B,C] and T2:[A,B,D] and we trim the final frame
-        # of each they should be considered equivalent.
-        allocatedAt = block['alloc']
+        # We use frame descriptions (e.g. "#00: foo (X.cpp:99)") when comparing
+        # traces for equality. We can't use trace keys or frame keys because
+        # they're not comparable across different DMD runs (which is relevant
+        # when doing diffs).
+        #
+        # Using frame descriptions also fits in with the stack trimming done
+        # for --max-frames, which requires that stack traces with common
+        # beginnings but different endings to be considered equivalent. E.g. if
+        # we have distinct traces T1:[A:D1,B:D2,C:D3] and T2:[X:D1,Y:D2,Z:D4]
+        # and we trim the final frame of each they should be considered
+        # equivalent because the untrimmed frame descriptions (D1 and D2)
+        # match.
+        def makeRecordKeyPart(traceKey):
+            return str(map(lambda frameKey: frameTable[frameKey],
+                           traceTable[traceKey]))
+
+        allocatedAtTraceKey = block['alloc']
         if args.ignore_reports:
-            recordKey = str(traceTable[allocatedAt])
+            recordKey = makeRecordKeyPart(allocatedAtTraceKey)
             records = liveRecords
         else:
-            recordKey = str(traceTable[allocatedAt])
+            recordKey = makeRecordKeyPart(allocatedAtTraceKey)
             if 'reps' in block:
-                reportedAts = block['reps']
-                for reportedAt in reportedAts:
-                    recordKey += str(traceTable[reportedAt])
-                if len(reportedAts) == 1:
+                reportedAtTraceKeys = block['reps']
+                for reportedAtTraceKey in reportedAtTraceKeys:
+                    recordKey += makeRecordKeyPart(reportedAtTraceKey)
+                if len(reportedAtTraceKeys) == 1:
                     records = onceReportedRecords
                 else:
                     records = twiceReportedRecords
             else:
                 records = unreportedRecords
 
         record = records[recordKey]
 
@@ -307,25 +404,102 @@ def main():
         heapUsableSize += usableSize
         heapBlocks += 1
 
         record.numBlocks  += 1
         record.reqSize    += reqSize
         record.slopSize   += slopSize
         record.usableSize += usableSize
         record.isSampled   = record.isSampled or isSampled
-        record.allocatedAt = block['alloc']
+        if record.allocatedAtDesc == None:
+            record.allocatedAtDesc = \
+                buildTraceDescription(traceTable, frameTable,
+                                      allocatedAtTraceKey)
+
         if args.ignore_reports:
             pass
         else:
-            if 'reps' in block:
-                record.reportedAts = block['reps']
+            if 'reps' in block and record.reportedAtDescs == []:
+                f = lambda k: buildTraceDescription(traceTable, frameTable, k)
+                record.reportedAtDescs = map(f, reportedAtTraceKeys)
         record.usableSizes[(usableSize, isSampled)] += 1
 
-    # Print records.
+    # All the processed data for a single DMD file is called a "digest".
+    digest = {}
+    digest['dmdEnvVar'] = dmdEnvVar
+    digest['sampleBelowSize'] = sampleBelowSize
+    digest['heapUsableSize'] = heapUsableSize
+    digest['heapBlocks'] = heapBlocks
+    digest['heapIsSampled'] = heapIsSampled
+    if args.ignore_reports:
+        digest['liveRecords'] = liveRecords
+    else:
+        digest['unreportedRecords'] = unreportedRecords
+        digest['onceReportedRecords'] = onceReportedRecords
+        digest['twiceReportedRecords'] = twiceReportedRecords
+    return digest
+
+
+def diffRecords(args, records1, records2):
+    records3 = {}
+
+    # Process records1.
+    for k in records1:
+        r1 = records1[k]
+        if k in records2:
+            # This record is present in both records1 and records2.
+            r2 = records2[k]
+            del records2[k]
+            r2.subtract(r1)
+            if not r2.isZero(args):
+                records3[k] = r2
+        else:
+            # This record is present only in records1.
+            r1.negate()
+            records3[k] = r1
+
+    for k in records2:
+        # This record is present only in records2.
+        records3[k] = records2[k]
+
+    return records3
+
+
+def diffDigests(args, d1, d2):
+    d3 = {}
+    d3['dmdEnvVar'] = (d1['dmdEnvVar'], d2['dmdEnvVar'])
+    d3['sampleBelowSize'] = (d1['sampleBelowSize'], d2['sampleBelowSize'])
+    d3['heapUsableSize'] = d2['heapUsableSize'] - d1['heapUsableSize']
+    d3['heapBlocks']     = d2['heapBlocks']     - d1['heapBlocks']
+    d3['heapIsSampled']  = d2['heapIsSampled'] or d1['heapIsSampled']
+    if args.ignore_reports:
+        d3['liveRecords'] = diffRecords(args, d1['liveRecords'],
+                                              d2['liveRecords'])
+    else:
+        d3['unreportedRecords']    = diffRecords(args, d1['unreportedRecords'],
+                                                       d2['unreportedRecords'])
+        d3['onceReportedRecords']  = diffRecords(args, d1['onceReportedRecords'],
+                                                       d2['onceReportedRecords'])
+        d3['twiceReportedRecords'] = diffRecords(args, d1['twiceReportedRecords'],
+                                                       d2['twiceReportedRecords'])
+    return d3
+
+
+def printDigest(args, digest):
+    dmdEnvVar       = digest['dmdEnvVar']
+    sampleBelowSize = digest['sampleBelowSize']
+    heapUsableSize  = digest['heapUsableSize']
+    heapIsSampled   = digest['heapIsSampled']
+    heapBlocks      = digest['heapBlocks']
+    if args.ignore_reports:
+        liveRecords = digest['liveRecords']
+    else:
+        unreportedRecords    = digest['unreportedRecords']
+        onceReportedRecords  = digest['onceReportedRecords']
+        twiceReportedRecords = digest['twiceReportedRecords']
 
     separator = '#' + '-' * 65 + '\n'
 
     def number(n, isSampled):
         '''Format a number, with comma as a separator and a '~' prefix if it's
         sampled.'''
         return '{:}{:,d}'.format('~' if isSampled else '', n)
 
@@ -334,37 +508,19 @@ def main():
 
     def plural(n):
         return '' if n == 1 else 's'
 
     # Prints to stdout, or to file if -o/--output was specified.
     def out(*arguments, **kwargs):
         print(*arguments, file=args.output, **kwargs)
 
-    def printStack(traceTable, frameTable, traceKey):
-        frameKeys = traceTable[traceKey]
-        fmt = '    #{:02d}{:}'
-
-        if args.filter_stacks_for_testing:
-            # If any frame has "DMD.cpp" or "replace_malloc.c" in its
-            # description -- as should be the case for every stack trace when
-            # running DMD in test mode -- we replace the entire trace with a
-            # single, predictable frame. There is too much variation in the
-            # stack traces across different machines and platforms to do more
-            # specific matching.
-            for frameKey in frameKeys:
-                frameDesc = frameTable[frameKey]
-                if 'DMD.cpp' in frameDesc or 'replace_malloc.c' in frameDesc:
-                    out(fmt.format(1, ': ... DMD.cpp ...'))
-                    return
-
-        # The frame number is always '#00' (see DMD.h for why), so we have to
-        # replace that with the correct frame number.
-        for n, frameKey in enumerate(traceTable[traceKey], start=1):
-            out(fmt.format(n, frameTable[frameKey][3:]))
+    def printStack(traceDesc):
+        for frameDesc in traceDesc:
+            out(frameDesc)
 
     def printRecords(recordKind, records, heapUsableSize):
         RecordKind = recordKind.capitalize()
         out(separator)
         numRecords = len(records)
         cmpRecords = sortByChoices[args.sort_by]
         sortedRecords = sorted(records.values(), cmp=cmpRecords, reverse=True)
         kindBlocks = 0
@@ -395,78 +551,97 @@ def main():
             out(RecordKind + ' {')
             out('  {:} block{:} in heap block record {:,d} of {:,d}'.
                 format(number(record.numBlocks, isSampled),
                        plural(record.numBlocks), i, numRecords))
             out('  {:} bytes ({:} requested / {:} slop)'.
                 format(number(record.usableSize, isSampled),
                        number(record.reqSize, isSampled),
                        number(record.slopSize, isSampled)))
+
+            abscmp = lambda ((usableSize1, _1a), _1b), \
+                            ((usableSize2, _2a), _2b): \
+                            cmp(abs(usableSize1), abs(usableSize2))
+            usableSizes = sorted(record.usableSizes.items(), cmp=abscmp,
+                                 reverse=True)
+
+            hasSingleBlock = len(usableSizes) == 1 and usableSizes[0][1] == 1
+
+            if not hasSingleBlock:
+                out('  Individual block sizes: ', end='')
+                if len(usableSizes) == 0:
+                    out('(no change)', end='')
+                else:
+                    isFirst = True
+                    for (usableSize, isSampled), count in usableSizes:
+                        if not isFirst:
+                            out('; ', end='')
+                        out('{:}'.format(number(usableSize, isSampled)), end='')
+                        if count > 1:
+                            out(' x {:,d}'.format(count), end='')
+                        isFirst = False
+                out()
+
             out('  {:4.2f}% of the heap ({:4.2f}% cumulative)'.
                 format(perc(record.usableSize, heapUsableSize),
                        perc(kindCumulativeUsableSize, heapUsableSize)))
             if args.ignore_reports:
                 pass
             else:
                 out('  {:4.2f}% of {:} ({:4.2f}% cumulative)'.
                     format(perc(record.usableSize, kindUsableSize),
                            recordKind,
                            perc(kindCumulativeUsableSize, kindUsableSize)))
-
-            if args.show_all_block_sizes:
-                usableSizes = sorted(record.usableSizes.items(), reverse=True)
-
-                out('  Individual block sizes: ', end='')
-                isFirst = True
-                for (usableSize, isSampled), count in usableSizes:
-                    if not isFirst:
-                        out('; ', end='')
-                    out('{:}'.format(number(usableSize, isSampled)), end='')
-                    if count > 1:
-                        out(' x {:,d}'.format(count), end='')
-                    isFirst = False
-                out()
-
             out('  Allocated at {')
-            printStack(traceTable, frameTable, record.allocatedAt)
+            printStack(record.allocatedAtDesc)
             out('  }')
             if args.ignore_reports:
                 pass
             else:
-                if hasattr(record, 'reportedAts'):
-                    for n, reportedAt in enumerate(record.reportedAts):
-                        again = 'again ' if n > 0 else ''
-                        out('  Reported {:}at {{'.format(again))
-                        printStack(traceTable, frameTable, reportedAt)
-                        out('  }')
+                for n, reportedAtDesc in enumerate(record.reportedAtDescs):
+                    again = 'again ' if n > 0 else ''
+                    out('  Reported {:}at {{'.format(again))
+                    printStack(reportedAtDesc)
+                    out('  }')
             out('}\n')
 
         return (kindUsableSize, kindBlocks)
 
 
-    # Print header.
-    out(separator)
-    out('Invocation {')
-    out('  $DMD = \'' + dmdEnvVar + '\'')
-    out('  Sample-below size = ' + str(sampleBelowSize))
-    out('}\n')
+    def printInvocation(n, dmdEnvVar, sampleBelowSize):
+        out('Invocation{:} {{'.format(n))
+        out('  $DMD = \'' + dmdEnvVar + '\'')
+        out('  Sample-below size = ' + str(sampleBelowSize))
+        out('}\n')
+
+    # Print command line. Strip dirs so the output is deterministic, which is
+    # needed for testing.
+    out(separator, end='')
+    out('# ' + ' '.join(map(os.path.basename, sys.argv)) + '\n')
+
+    # Print invocation(s).
+    if type(dmdEnvVar) is not tuple:
+        printInvocation('', dmdEnvVar, sampleBelowSize)
+    else:
+        printInvocation(' 1', dmdEnvVar[0], sampleBelowSize[0])
+        printInvocation(' 2', dmdEnvVar[1], sampleBelowSize[1])
 
     # Print records.
     if args.ignore_reports:
         liveUsableSize, liveBlocks = \
             printRecords('live', liveRecords, heapUsableSize)
     else:
         twiceReportedUsableSize, twiceReportedBlocks = \
             printRecords('twice-reported', twiceReportedRecords, heapUsableSize)
 
         unreportedUsableSize, unreportedBlocks = \
-            printRecords('unreported',     unreportedRecords, heapUsableSize)
+            printRecords('unreported', unreportedRecords, heapUsableSize)
 
         onceReportedUsableSize, onceReportedBlocks = \
-            printRecords('once-reported',  onceReportedRecords, heapUsableSize)
+            printRecords('once-reported', onceReportedRecords, heapUsableSize)
 
     # Print summary.
     out(separator)
     out('Summary {')
     if args.ignore_reports:
         out('  Total: {:} bytes in {:} blocks'.
             format(number(liveUsableSize, heapIsSampled),
                    number(liveBlocks, heapIsSampled)))
@@ -494,10 +669,20 @@ def main():
             format('Twice-reported:',
                    number(twiceReportedUsableSize, heapIsSampled),
                    perc(twiceReportedUsableSize, heapUsableSize),
                    number(twiceReportedBlocks, heapIsSampled),
                    perc(twiceReportedBlocks, heapBlocks)))
     out('}\n')
 
 
+def main():
+    args = parseCommandLine()
+    digest = getDigestFromFile(args, args.input_file)
+    if args.input_file2:
+        digest2 = getDigestFromFile(args, args.input_file2)
+        digest = diffDigests(args, digest, digest2)
+    printDigest(args, digest)
+
+
 if __name__ == '__main__':
     main()
+
--- a/memory/replace/dmd/moz.build
+++ b/memory/replace/dmd/moz.build
@@ -28,12 +28,10 @@ if CONFIG['MOZ_OPTIMIZE']:
 
 DISABLE_STL_WRAPPING = True
 
 if CONFIG['OS_ARCH'] == 'WINNT':
     OS_LIBS += [
         'dbghelp',
     ]
 
-XPCSHELL_TESTS_MANIFESTS += [
-    'test/xpcshell.ini',
-]
+TEST_DIRS += ['test']
 
new file mode 100644
--- /dev/null
+++ b/memory/replace/dmd/test/SmokeDMD.cpp
@@ -0,0 +1,335 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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/. */
+
+// This program is used by the DMD xpcshell test. It is run under DMD and
+// produces some output. The xpcshell test then post-processes and checks this
+// output.
+//
+// Note that this file does not have "Test" or "test" in its name, because that
+// will cause the build system to not record breakpad symbols for it, which
+// will stop the post-processing (which includes stack fixing) from working
+// correctly.
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "mozilla/Assertions.h"
+#include "mozilla/JSONWriter.h"
+#include "mozilla/UniquePtr.h"
+#include "DMD.h"
+
+using mozilla::JSONWriter;
+using mozilla::MakeUnique;
+using namespace mozilla::dmd;
+
+class FpWriteFunc : public mozilla::JSONWriteFunc
+{
+public:
+  explicit FpWriteFunc(const char* aFilename)
+  {
+    mFp = fopen(aFilename, "w");
+    if (!mFp) {
+      fprintf(stderr, "SmokeDMD: can't create %s file: %s\n",
+              aFilename, strerror(errno));
+      exit(1);
+    }
+  }
+
+  ~FpWriteFunc() { fclose(mFp); }
+
+  void Write(const char* aStr) { fputs(aStr, mFp); }
+
+private:
+  FILE* mFp;
+};
+
+// This stops otherwise-unused variables from being optimized away.
+static void
+UseItOrLoseIt(void* aPtr, int aSeven)
+{
+  char buf[64];
+  int n = sprintf(buf, "%p\n", aPtr);
+  if (n == 20 + aSeven) {
+    fprintf(stderr, "well, that is surprising");
+  }
+}
+
+// This function checks that heap blocks that have the same stack trace but
+// different (or no) reporters get aggregated separately.
+void Foo(int aSeven)
+{
+  char* a[6];
+  for (int i = 0; i < aSeven - 1; i++) {
+    a[i] = (char*) malloc(128 - 16*i);
+  }
+
+  // Oddly, some versions of clang will cause identical stack traces to be
+  // generated for adjacent calls to Report(), which breaks the test. Inserting
+  // the UseItOrLoseIt() calls in between is enough to prevent this.
+
+  Report(a[2]);                     // reported
+
+  UseItOrLoseIt(a[2], aSeven);
+
+  for (int i = 0; i < aSeven - 5; i++) {
+    Report(a[i]);                   // reported
+  }
+
+  UseItOrLoseIt(a[2], aSeven);
+
+  Report(a[3]);                     // reported
+
+  // a[4], a[5] unreported
+}
+
+void
+RunTests()
+{
+  // These files are written to $CWD.
+  auto f1 = MakeUnique<FpWriteFunc>("full-empty.json");
+  auto f2 = MakeUnique<FpWriteFunc>("full-unsampled1.json");
+  auto f3 = MakeUnique<FpWriteFunc>("full-unsampled2.json");
+  auto f4 = MakeUnique<FpWriteFunc>("full-sampled.json");
+
+  // This test relies on the compiler not doing various optimizations, such as
+  // eliding unused malloc() calls or unrolling loops with fixed iteration
+  // counts. So we compile it with -O0 (or equivalent), which probably prevents
+  // that. We also use the following variable for various loop iteration
+  // counts, just in case compilers might unroll very small loops even with
+  // -O0.
+  int seven = 7;
+
+  // Make sure that DMD is actually running; it is initialized on the first
+  // allocation.
+  int *x = (int*)malloc(100);
+  UseItOrLoseIt(x, seven);
+  MOZ_RELEASE_ASSERT(IsRunning());
+
+  // The first part of this test requires sampling to be disabled.
+  SetSampleBelowSize(1);
+
+  // The file manipulations above may have done some heap allocations.
+  // Clear all knowledge of existing blocks to give us a clean slate.
+  ClearBlocks();
+
+  //---------
+
+  // AnalyzeReports 1.  Zero for everything.
+  JSONWriter writer1(Move(f1));
+  AnalyzeReports(writer1);
+
+  //---------
+
+  // AnalyzeReports 2: 1 freed, 9 out of 10 unreported.
+  // AnalyzeReports 3: still present and unreported.
+  int i;
+  char* a = nullptr;
+  for (i = 0; i < seven + 3; i++) {
+      a = (char*) malloc(100);
+      UseItOrLoseIt(a, seven);
+  }
+  free(a);
+
+  // Note: 8 bytes is the smallest requested size that gives consistent
+  // behaviour across all platforms with jemalloc.
+  // AnalyzeReports 2: reported.
+  // AnalyzeReports 3: thrice-reported.
+  char* a2 = (char*) malloc(8);
+  Report(a2);
+
+  // AnalyzeReports 2: reported.
+  // AnalyzeReports 3: reportedness carries over, due to ReportOnAlloc.
+  char* b = (char*) malloc(10);
+  ReportOnAlloc(b);
+
+  // ReportOnAlloc, then freed.
+  // AnalyzeReports 2: freed, irrelevant.
+  // AnalyzeReports 3: freed, irrelevant.
+  char* b2 = (char*) malloc(1);
+  ReportOnAlloc(b2);
+  free(b2);
+
+  // AnalyzeReports 2: reported 4 times.
+  // AnalyzeReports 3: freed, irrelevant.
+  char* c = (char*) calloc(10, 3);
+  Report(c);
+  for (int i = 0; i < seven - 4; i++) {
+    Report(c);
+  }
+
+  // AnalyzeReports 2: ignored.
+  // AnalyzeReports 3: irrelevant.
+  Report((void*)(intptr_t)i);
+
+  // jemalloc rounds this up to 8192.
+  // AnalyzeReports 2: reported.
+  // AnalyzeReports 3: freed.
+  char* e = (char*) malloc(4096);
+  e = (char*) realloc(e, 4097);
+  Report(e);
+
+  // First realloc is like malloc;  second realloc is shrinking.
+  // AnalyzeReports 2: reported.
+  // AnalyzeReports 3: re-reported.
+  char* e2 = (char*) realloc(nullptr, 1024);
+  e2 = (char*) realloc(e2, 512);
+  Report(e2);
+
+  // First realloc is like malloc;  second realloc creates a min-sized block.
+  // XXX: on Windows, second realloc frees the block.
+  // AnalyzeReports 2: reported.
+  // AnalyzeReports 3: freed, irrelevant.
+  char* e3 = (char*) realloc(nullptr, 1023);
+//e3 = (char*) realloc(e3, 0);
+  MOZ_ASSERT(e3);
+  Report(e3);
+
+  // AnalyzeReports 2: freed, irrelevant.
+  // AnalyzeReports 3: freed, irrelevant.
+  char* f = (char*) malloc(64);
+  free(f);
+
+  // AnalyzeReports 2: ignored.
+  // AnalyzeReports 3: irrelevant.
+  Report((void*)(intptr_t)0x0);
+
+  // AnalyzeReports 2: mixture of reported and unreported.
+  // AnalyzeReports 3: all unreported.
+  Foo(seven);
+
+  // AnalyzeReports 2: twice-reported.
+  // AnalyzeReports 3: twice-reported.
+  char* g1 = (char*) malloc(77);
+  ReportOnAlloc(g1);
+  ReportOnAlloc(g1);
+
+  // AnalyzeReports 2: mixture of reported and unreported.
+  // AnalyzeReports 3: all unreported.
+  // Nb: this Foo() call is deliberately not adjacent to the previous one. See
+  // the comment about adjacent calls in Foo() for more details.
+  Foo(seven);
+
+  // AnalyzeReports 2: twice-reported.
+  // AnalyzeReports 3: once-reported.
+  char* g2 = (char*) malloc(78);
+  Report(g2);
+  ReportOnAlloc(g2);
+
+  // AnalyzeReports 2: twice-reported.
+  // AnalyzeReports 3: once-reported.
+  char* g3 = (char*) malloc(79);
+  ReportOnAlloc(g3);
+  Report(g3);
+
+  // All the odd-ball ones.
+  // AnalyzeReports 2: all unreported.
+  // AnalyzeReports 3: all freed, irrelevant.
+  // XXX: no memalign on Mac
+//void* w = memalign(64, 65);           // rounds up to 128
+//UseItOrLoseIt(w, seven);
+
+  // XXX: posix_memalign doesn't work on B2G
+//void* x;
+//posix_memalign(&y, 128, 129);         // rounds up to 256
+//UseItOrLoseIt(x, seven);
+
+  // XXX: valloc doesn't work on Windows.
+//void* y = valloc(1);                  // rounds up to 4096
+//UseItOrLoseIt(y, seven);
+
+  // XXX: C11 only
+//void* z = aligned_alloc(64, 256);
+//UseItOrLoseIt(z, seven);
+
+  // AnalyzeReports 2.
+  JSONWriter writer2(Move(f2));
+  AnalyzeReports(writer2);
+
+  //---------
+
+  Report(a2);
+  Report(a2);
+  free(c);
+  free(e);
+  Report(e2);
+  free(e3);
+//free(w);
+//free(x);
+//free(y);
+//free(z);
+
+  // AnalyzeReports 3.
+  JSONWriter writer3(Move(f3));
+  AnalyzeReports(writer3);
+
+  //---------
+
+  // The first part of this test requires sampling to be disabled.
+  SetSampleBelowSize(128);
+
+  // Clear all knowledge of existing blocks to give us a clean slate.
+  ClearBlocks();
+
+  char* s;
+
+  // This equals the sample size, and so is reported exactly.  It should be
+  // listed before records of the same size that are sampled.
+  s = (char*) malloc(128);
+  UseItOrLoseIt(s, seven);
+
+  // This exceeds the sample size, and so is reported exactly.
+  s = (char*) malloc(144);
+  UseItOrLoseIt(s, seven);
+
+  // These together constitute exactly one sample.
+  for (int i = 0; i < seven + 9; i++) {
+    s = (char*) malloc(8);
+    UseItOrLoseIt(s, seven);
+  }
+
+  // These fall 8 bytes short of a full sample.
+  for (int i = 0; i < seven + 8; i++) {
+    s = (char*) malloc(8);
+    UseItOrLoseIt(s, seven);
+  }
+
+  // This exceeds the sample size, and so is recorded exactly.
+  s = (char*) malloc(256);
+  UseItOrLoseIt(s, seven);
+
+  // This gets more than to a full sample from the |i < seven + 8| loop above.
+  s = (char*) malloc(96);
+  UseItOrLoseIt(s, seven);
+
+  // This gets to another full sample.
+  for (int i = 0; i < seven - 2; i++) {
+    s = (char*) malloc(8);
+    UseItOrLoseIt(s, seven);
+  }
+
+  // This allocates 16, 32, ..., 128 bytes, which results in a heap block
+  // record that contains a mix of sample and non-sampled blocks, and so should
+  // be printed with '~' signs.
+  for (int i = 1; i <= seven + 1; i++) {
+    s = (char*) malloc(i * 16);
+    UseItOrLoseIt(s, seven);
+  }
+
+  // At the end we're 64 bytes into the current sample so we report ~1,424
+  // bytes of allocation overall, which is 64 less than the real value 1,488.
+
+  // AnalyzeReports 4.
+  JSONWriter writer4(Move(f4));
+  AnalyzeReports(writer4);
+}
+
+int main()
+{
+  RunTests();
+
+  return 0;
+}
--- a/memory/replace/dmd/test/full-heap-empty-expected.txt
+++ b/memory/replace/dmd/test/full-heap-empty-expected.txt
@@ -1,12 +1,13 @@
 #-----------------------------------------------------------------
+# dmd.py --filter-stacks-for-testing -o full-heap-empty-actual.txt --ignore-reports full-empty.json
 
 Invocation {
-  $DMD = '--mode=test'
+  $DMD = '1'
   Sample-below size = 1
 }
 
 #-----------------------------------------------------------------
 
 # no live heap blocks
 
 #-----------------------------------------------------------------
--- a/memory/replace/dmd/test/full-heap-sampled-expected.txt
+++ b/memory/replace/dmd/test/full-heap-sampled-expected.txt
@@ -1,20 +1,22 @@
 #-----------------------------------------------------------------
+# dmd.py --filter-stacks-for-testing -o full-heap-sampled-actual.txt --ignore-reports full-sampled.json
 
 Invocation {
-  $DMD = '--mode=test'
+  $DMD = '1'
   Sample-below size = 128
 }
 
 #-----------------------------------------------------------------
 
 Live {
   ~4 blocks in heap block record 1 of 7
   ~512 bytes (~512 requested / ~0 slop)
+  Individual block sizes: ~128 x 3; 128
   35.96% of the heap (35.96% cumulative)
   Allocated at {
     #01: ... DMD.cpp ...
   }
 }
 
 Live {
   1 block in heap block record 2 of 7
--- a/memory/replace/dmd/test/full-heap-unsampled1-expected.txt
+++ b/memory/replace/dmd/test/full-heap-unsampled1-expected.txt
@@ -1,12 +1,13 @@
 #-----------------------------------------------------------------
+# dmd.py --filter-stacks-for-testing -o full-heap-unsampled1-actual.txt --ignore-reports full-unsampled1.json
 
 Invocation {
-  $DMD = '--mode=test'
+  $DMD = '1'
   Sample-below size = 1
 }
 
 #-----------------------------------------------------------------
 
 Live {
   1 block in heap block record 1 of 12
   8,192 bytes (4,097 requested / 4,095 slop)
@@ -23,34 +24,37 @@ Live {
   Allocated at {
     #01: ... DMD.cpp ...
   }
 }
 
 Live {
   9 blocks in heap block record 3 of 12
   1,008 bytes (900 requested / 108 slop)
+  Individual block sizes: 112 x 9
   8.34% of the heap (84.58% cumulative)
   Allocated at {
     #01: ... DMD.cpp ...
   }
 }
 
 Live {
   6 blocks in heap block record 4 of 12
   528 bytes (528 requested / 0 slop)
+  Individual block sizes: 128; 112; 96; 80; 64; 48
   4.37% of the heap (88.95% cumulative)
   Allocated at {
     #01: ... DMD.cpp ...
   }
 }
 
 Live {
   6 blocks in heap block record 5 of 12
   528 bytes (528 requested / 0 slop)
+  Individual block sizes: 128; 112; 96; 80; 64; 48
   4.37% of the heap (93.32% cumulative)
   Allocated at {
     #01: ... DMD.cpp ...
   }
 }
 
 Live {
   1 block in heap block record 6 of 12
--- a/memory/replace/dmd/test/full-heap-unsampled2-expected.txt
+++ b/memory/replace/dmd/test/full-heap-unsampled2-expected.txt
@@ -1,38 +1,42 @@
 #-----------------------------------------------------------------
+# dmd.py --filter-stacks-for-testing -o full-heap-unsampled2-actual.txt --ignore-reports full-unsampled2.json
 
 Invocation {
-  $DMD = '--mode=test'
+  $DMD = '1'
   Sample-below size = 1
 }
 
 #-----------------------------------------------------------------
 
 Live {
   9 blocks in heap block record 1 of 9
   1,008 bytes (900 requested / 108 slop)
+  Individual block sizes: 112 x 9
   35.49% of the heap (35.49% cumulative)
   Allocated at {
     #01: ... DMD.cpp ...
   }
 }
 
 Live {
   6 blocks in heap block record 2 of 9
   528 bytes (528 requested / 0 slop)
+  Individual block sizes: 128; 112; 96; 80; 64; 48
   18.59% of the heap (54.08% cumulative)
   Allocated at {
     #01: ... DMD.cpp ...
   }
 }
 
 Live {
   6 blocks in heap block record 3 of 9
   528 bytes (528 requested / 0 slop)
+  Individual block sizes: 128; 112; 96; 80; 64; 48
   18.59% of the heap (72.68% cumulative)
   Allocated at {
     #01: ... DMD.cpp ...
   }
 }
 
 Live {
   1 block in heap block record 4 of 9
--- a/memory/replace/dmd/test/full-reports-empty-expected.txt
+++ b/memory/replace/dmd/test/full-reports-empty-expected.txt
@@ -1,12 +1,13 @@
 #-----------------------------------------------------------------
+# dmd.py --filter-stacks-for-testing -o full-reports-empty-actual.txt full-empty.json
 
 Invocation {
-  $DMD = '--mode=test'
+  $DMD = '1'
   Sample-below size = 1
 }
 
 #-----------------------------------------------------------------
 
 # no twice-reported heap blocks
 
 #-----------------------------------------------------------------
--- a/memory/replace/dmd/test/full-reports-sampled-expected.txt
+++ b/memory/replace/dmd/test/full-reports-sampled-expected.txt
@@ -1,24 +1,26 @@
 #-----------------------------------------------------------------
+# dmd.py --filter-stacks-for-testing -o full-reports-sampled-actual.txt full-sampled.json
 
 Invocation {
-  $DMD = '--mode=test'
+  $DMD = '1'
   Sample-below size = 128
 }
 
 #-----------------------------------------------------------------
 
 # no twice-reported heap blocks
 
 #-----------------------------------------------------------------
 
 Unreported {
   ~4 blocks in heap block record 1 of 7
   ~512 bytes (~512 requested / ~0 slop)
+  Individual block sizes: ~128 x 3; 128
   35.96% of the heap (35.96% cumulative)
   35.96% of unreported (35.96% cumulative)
   Allocated at {
     #01: ... DMD.cpp ...
   }
 }
 
 Unreported {
--- a/memory/replace/dmd/test/full-reports-unsampled1-expected.txt
+++ b/memory/replace/dmd/test/full-reports-unsampled1-expected.txt
@@ -1,12 +1,13 @@
 #-----------------------------------------------------------------
+# dmd.py --filter-stacks-for-testing -o full-reports-unsampled1-actual.txt full-unsampled1.json
 
 Invocation {
-  $DMD = '--mode=test'
+  $DMD = '1'
   Sample-below size = 1
 }
 
 #-----------------------------------------------------------------
 
 Twice-reported {
   1 block in heap block record 1 of 4
   80 bytes (79 requested / 1 slop)
@@ -71,36 +72,39 @@ Twice-reported {
   }
 }
 
 #-----------------------------------------------------------------
 
 Unreported {
   9 blocks in heap block record 1 of 3
   1,008 bytes (900 requested / 108 slop)
+  Individual block sizes: 112 x 9
   8.34% of the heap (8.34% cumulative)
   81.82% of unreported (81.82% cumulative)
   Allocated at {
     #01: ... DMD.cpp ...
   }
 }
 
 Unreported {
   2 blocks in heap block record 2 of 3
   112 bytes (112 requested / 0 slop)
+  Individual block sizes: 64; 48
   0.93% of the heap (9.27% cumulative)
   9.09% of unreported (90.91% cumulative)
   Allocated at {
     #01: ... DMD.cpp ...
   }
 }
 
 Unreported {
   2 blocks in heap block record 3 of 3
   112 bytes (112 requested / 0 slop)
+  Individual block sizes: 64; 48
   0.93% of the heap (10.19% cumulative)
   9.09% of unreported (100.00% cumulative)
   Allocated at {
     #01: ... DMD.cpp ...
   }
 }
 
 #-----------------------------------------------------------------
@@ -142,29 +146,31 @@ Once-reported {
   Reported at {
     #01: ... DMD.cpp ...
   }
 }
 
 Once-reported {
   2 blocks in heap block record 4 of 11
   240 bytes (240 requested / 0 slop)
+  Individual block sizes: 128; 112
   1.99% of the heap (82.46% cumulative)
   2.27% of once-reported (94.18% cumulative)
   Allocated at {
     #01: ... DMD.cpp ...
   }
   Reported at {
     #01: ... DMD.cpp ...
   }
 }
 
 Once-reported {
   2 blocks in heap block record 5 of 11
   240 bytes (240 requested / 0 slop)
+  Individual block sizes: 128; 112
   1.99% of the heap (84.45% cumulative)
   2.27% of once-reported (96.45% cumulative)
   Allocated at {
     #01: ... DMD.cpp ...
   }
   Reported at {
     #01: ... DMD.cpp ...
   }
--- a/memory/replace/dmd/test/full-reports-unsampled2-expected.txt
+++ b/memory/replace/dmd/test/full-reports-unsampled2-expected.txt
@@ -1,12 +1,13 @@
 #-----------------------------------------------------------------
+# dmd.py --filter-stacks-for-testing -o full-reports-unsampled2-actual.txt full-unsampled2.json
 
 Invocation {
-  $DMD = '--mode=test'
+  $DMD = '1'
   Sample-below size = 1
 }
 
 #-----------------------------------------------------------------
 
 Twice-reported {
   1 block in heap block record 1 of 2
   80 bytes (77 requested / 3 slop)
@@ -39,36 +40,39 @@ Twice-reported {
   }
 }
 
 #-----------------------------------------------------------------
 
 Unreported {
   9 blocks in heap block record 1 of 3
   1,008 bytes (900 requested / 108 slop)
+  Individual block sizes: 112 x 9
   35.49% of the heap (35.49% cumulative)
   48.84% of unreported (48.84% cumulative)
   Allocated at {
     #01: ... DMD.cpp ...
   }
 }
 
 Unreported {
   6 blocks in heap block record 2 of 3
   528 bytes (528 requested / 0 slop)
+  Individual block sizes: 128; 112; 96; 80; 64; 48
   18.59% of the heap (54.08% cumulative)
   25.58% of unreported (74.42% cumulative)
   Allocated at {
     #01: ... DMD.cpp ...
   }
 }
 
 Unreported {
   6 blocks in heap block record 3 of 3
   528 bytes (528 requested / 0 slop)
+  Individual block sizes: 128; 112; 96; 80; 64; 48
   18.59% of the heap (72.68% cumulative)
   25.58% of unreported (100.00% cumulative)
   Allocated at {
     #01: ... DMD.cpp ...
   }
 }
 
 #-----------------------------------------------------------------
new file mode 100644
--- /dev/null
+++ b/memory/replace/dmd/test/moz.build
@@ -0,0 +1,26 @@
+# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+SimplePrograms([
+    'SmokeDMD',
+])
+
+# See the comment at the top of SmokeDMD.cpp:RunTests().
+if CONFIG['OS_ARCH'] == 'WINNT':
+    CXXFLAGS += ['-Og-']
+else:
+    CXXFLAGS += ['-O0']
+
+DEFINES['MOZ_NO_MOZALLOC'] = True
+
+DISABLE_STL_WRAPPING = True
+
+USE_LIBS += ['dmd']
+
+XPCSHELL_TESTS_MANIFESTS += [
+    'xpcshell.ini',
+]
+
new file mode 100644
--- /dev/null
+++ b/memory/replace/dmd/test/script-diff-basic-expected.txt
@@ -0,0 +1,127 @@
+#-----------------------------------------------------------------
+# dmd.py --filter-stacks-for-testing -o script-diff-basic-actual.txt script-diff1.json script-diff2.json
+
+Invocation 1 {
+  $DMD = '--sample-below=127'
+  Sample-below size = 127
+}
+
+Invocation 2 {
+  $DMD = '--sample-below=63'
+  Sample-below size = 63
+}
+
+#-----------------------------------------------------------------
+
+Twice-reported {
+  ~-1 blocks in heap block record 1 of 1
+  ~-1,088 bytes (~-1,064 requested / ~-24 slop)
+  Individual block sizes: -1,024; ~-127; ~63
+  15.46% of the heap (15.46% cumulative)
+  100.00% of twice-reported (100.00% cumulative)
+  Allocated at {
+    #01: F (F.cpp:99)
+  }
+  Reported at {
+    #01: R1 (R1.cpp:99)
+  }
+  Reported again at {
+    #01: R2 (R2.cpp:99)
+  }
+}
+
+#-----------------------------------------------------------------
+
+Unreported {
+  4 blocks in heap block record 1 of 5
+  16,384 bytes (16,384 requested / 0 slop)
+  Individual block sizes: 4,096 x 4
+  -232.76% of the heap (-232.76% cumulative)
+  371.01% of unreported (371.01% cumulative)
+  Allocated at {
+    #01: E (E.cpp:99)
+  }
+}
+
+Unreported {
+  ~7 blocks in heap block record 2 of 5
+  ~-11,968 bytes (~-12,016 requested / ~48 slop)
+  Individual block sizes: -15,360; 2,048; 512 x 2; 128; ~-127; 64 x 4; ~63
+  170.02% of the heap (-62.74% cumulative)
+  -271.01% of unreported (100.00% cumulative)
+  Allocated at {
+    #01: F (F.cpp:99)
+  }
+}
+
+Unreported {
+  0 blocks in heap block record 3 of 5
+  0 bytes (-384 requested / 384 slop)
+  Individual block sizes: (no change)
+  -0.00% of the heap (-62.74% cumulative)
+  0.00% of unreported (100.00% cumulative)
+  Allocated at {
+    #01: C (C.cpp:99)
+  }
+}
+
+Unreported {
+  -2 blocks in heap block record 4 of 5
+  0 bytes (0 requested / 0 slop)
+  Individual block sizes: 8,192 x 2; -4,096 x 4
+  -0.00% of the heap (-62.74% cumulative)
+  0.00% of unreported (100.00% cumulative)
+  Allocated at {
+    #01: B (B.cpp:99)
+  }
+}
+
+Unreported {
+  0 blocks in heap block record 5 of 5
+  0 bytes (0 requested / 0 slop)
+  Individual block sizes: 20,480; -16,384; -8,192; 4,096
+  -0.00% of the heap (-62.74% cumulative)
+  0.00% of unreported (100.00% cumulative)
+  Allocated at {
+    #01: G (G.cpp:99)
+  }
+}
+
+#-----------------------------------------------------------------
+
+Once-reported {
+  -3 blocks in heap block record 1 of 2
+  -10,240 bytes (-10,192 requested / -48 slop)
+  Individual block sizes: -4,096 x 2; -2,048
+  145.48% of the heap (145.48% cumulative)
+  98.77% of once-reported (98.77% cumulative)
+  Allocated at {
+    #01: D (D.cpp:99)
+  }
+  Reported at {
+    #01: R1 (R1.cpp:99)
+  }
+}
+
+Once-reported {
+  ~-1 blocks in heap block record 2 of 2
+  ~-127 bytes (~-151 requested / ~24 slop)
+  1.80% of the heap (147.28% cumulative)
+  1.23% of once-reported (100.00% cumulative)
+  Allocated at {
+    #01: F (F.cpp:99)
+  }
+  Reported at {
+    #01: R1 (R1.cpp:99)
+  }
+}
+
+#-----------------------------------------------------------------
+
+Summary {
+  Total:               ~-7,039 bytes (100.00%) in      ~4 blocks (100.00%)
+  Unreported:           ~4,416 bytes (-62.74%) in      ~9 blocks (225.00%)
+  Once-reported:      ~-10,367 bytes (147.28%) in     ~-4 blocks (-100.00%)
+  Twice-reported:      ~-1,088 bytes ( 15.46%) in     ~-1 blocks (-25.00%)
+}
+
new file mode 100644
--- /dev/null
+++ b/memory/replace/dmd/test/script-diff-options-expected.txt
@@ -0,0 +1,81 @@
+#-----------------------------------------------------------------
+# dmd.py --filter-stacks-for-testing -o script-diff-options-actual.txt --ignore-reports script-diff1.json script-diff2.json
+
+Invocation 1 {
+  $DMD = '--sample-below=127'
+  Sample-below size = 127
+}
+
+Invocation 2 {
+  $DMD = '--sample-below=63'
+  Sample-below size = 63
+}
+
+#-----------------------------------------------------------------
+
+Live {
+  4 blocks in heap block record 1 of 6
+  16,384 bytes (16,384 requested / 0 slop)
+  Individual block sizes: 4,096 x 4
+  -232.76% of the heap (-232.76% cumulative)
+  Allocated at {
+    #01: E (E.cpp:99)
+  }
+}
+
+Live {
+  ~5 blocks in heap block record 2 of 6
+  ~-13,183 bytes (~-13,231 requested / ~48 slop)
+  Individual block sizes: -15,360; 2,048; -1,024; 512 x 2; 128; ~-127 x 3; 64 x 4; ~63 x 2
+  187.29% of the heap (-45.48% cumulative)
+  Allocated at {
+    #01: F (F.cpp:99)
+  }
+}
+
+Live {
+  -3 blocks in heap block record 3 of 6
+  -10,240 bytes (-10,192 requested / -48 slop)
+  Individual block sizes: -4,096 x 2; -2,048
+  145.48% of the heap (100.00% cumulative)
+  Allocated at {
+    #01: D (D.cpp:99)
+  }
+}
+
+Live {
+  0 blocks in heap block record 4 of 6
+  0 bytes (-384 requested / 384 slop)
+  Individual block sizes: (no change)
+  -0.00% of the heap (100.00% cumulative)
+  Allocated at {
+    #01: C (C.cpp:99)
+  }
+}
+
+Live {
+  0 blocks in heap block record 5 of 6
+  0 bytes (0 requested / 0 slop)
+  Individual block sizes: 20,480; -16,384; -8,192; 4,096
+  -0.00% of the heap (100.00% cumulative)
+  Allocated at {
+    #01: G (G.cpp:99)
+  }
+}
+
+Live {
+  -2 blocks in heap block record 6 of 6
+  0 bytes (0 requested / 0 slop)
+  Individual block sizes: 8,192 x 2; -4,096 x 4
+  -0.00% of the heap (100.00% cumulative)
+  Allocated at {
+    #01: B (B.cpp:99)
+  }
+}
+
+#-----------------------------------------------------------------
+
+Summary {
+  Total: ~-7,039 bytes in ~4 blocks
+}
+
new file mode 100644
--- /dev/null
+++ b/memory/replace/dmd/test/script-diff1.json
@@ -0,0 +1,62 @@
+{
+ "version": 1,
+ "invocation": {
+  "dmdEnvVar": "--sample-below=127",
+  "sampleBelowSize": 127
+ },
+ "blockList": [
+  {"req": 4096, "alloc": "A"},
+  {"req": 4096, "alloc": "A"},
+  {"req": 4096, "alloc": "A"},
+  {"req": 4096, "alloc": "A"},
+
+  {"req": 4096, "alloc": "B"},
+  {"req": 4096, "alloc": "B"},
+  {"req": 4096, "alloc": "B"},
+  {"req": 4096, "alloc": "B"},
+
+  {"req": 4096, "alloc": "C"},
+  {"req": 4096, "alloc": "C"},
+  {"req": 4096, "alloc": "C"},
+  {"req": 4096, "alloc": "C"},
+
+  {"req": 4096,             "alloc": "D", "reps": ["R1"]},
+  {"req": 4096,             "alloc": "D", "reps": ["R1"]},
+  {"req": 2000, "slop": 48, "alloc": "D", "reps": ["R1"]},
+
+  {"req": 15360,            "alloc": "F"},
+  {"req": 512,              "alloc": "F"},
+  {"req": 512,              "alloc": "F"},
+  {                         "alloc": "F"},
+  {"req": 1024,             "alloc": "F", "reps": ["R1"]},
+  {                         "alloc": "F", "reps": ["R1"]},
+  {"req": 1000, "slop": 24, "alloc": "F", "reps": ["R1", "R2"]},
+  {                         "alloc": "F", "reps": ["R1", "R2"]},
+
+  {"req": 4096,            "alloc": "G"},
+  {"req": 8192,            "alloc": "G"},
+  {"req": 16384,           "alloc": "G"}
+ ],
+ "traceTable": {
+  "A": ["AA"],
+  "B": ["BB"],
+  "C": ["CC"],
+  "D": ["DD"],
+  "E": ["EE"],
+  "F": ["FF"],
+  "G": ["GG"],
+  "R1": ["RR1"],
+  "R2": ["RR2"]
+ },
+ "frameTable": {
+  "AA": "#00: A (A.cpp:99)",
+  "BB": "#00: B (B.cpp:99)",
+  "CC": "#00: C (C.cpp:99)",
+  "DD": "#00: D (D.cpp:99)",
+  "EE": "#00: E (E.cpp:99)",
+  "FF": "#00: F (F.cpp:99)",
+  "GG": "#00: G (G.cpp:99)",
+  "RR1": "#00: R1 (R1.cpp:99)",
+  "RR2": "#00: R2 (R2.cpp:99)"
+ }
+}
new file mode 100644
--- /dev/null
+++ b/memory/replace/dmd/test/script-diff2.json
@@ -0,0 +1,66 @@
+{
+ "version": 1,
+ "invocation": {
+  "dmdEnvVar": "--sample-below=63",
+  "sampleBelowSize": 63
+ },
+ "blockList": [
+  {"req": 4096, "alloc": "A"},
+  {"req": 4096, "alloc": "A"},
+  {"req": 4096, "alloc": "A"},
+  {"req": 4096, "alloc": "A"},
+
+  {"req": 8192, "alloc": "B"},
+  {"req": 8192, "alloc": "B"},
+
+  {"req": 4000, "slop": 96, "alloc": "C"},
+  {"req": 4000, "slop": 96, "alloc": "C"},
+  {"req": 4000, "slop": 96, "alloc": "C"},
+  {"req": 4000, "slop": 96, "alloc": "C"},
+
+  {"req": 4096, "alloc": "E"},
+  {"req": 4096, "alloc": "E"},
+  {"req": 4096, "alloc": "E"},
+  {"req": 4096, "alloc": "E"},
+
+  {"req": 2000, "slop": 48, "alloc": "F"},
+  {"req": 1000, "slop": 24, "alloc": "F", "reps": ["R1"]},
+  {"req": 512,              "alloc": "F"},
+  {"req": 512,              "alloc": "F"},
+  {"req": 512,              "alloc": "F"},
+  {"req": 512,              "alloc": "F"},
+  {"req": 128,              "alloc": "F"},
+  {                         "alloc": "F", "reps": ["R1", "R2"]},
+  {"req": 64,               "alloc": "F"},
+  {"req": 64,               "alloc": "F"},
+  {"req": 64,               "alloc": "F"},
+  {"req": 64,               "alloc": "F"},
+  {                         "alloc": "F"},
+
+  {"req": 4096,            "alloc": "G"},
+  {"req": 4096,            "alloc": "G"},
+  {"req": 20480,           "alloc": "G"}
+ ],
+ "traceTable": {
+  "A": ["AA"],
+  "B": ["BB"],
+  "C": ["CC"],
+  "D": ["DD"],
+  "E": ["EE"],
+  "F": ["FF"],
+  "G": ["GG"],
+  "R1": ["RR1"],
+  "R2": ["RR2"]
+ },
+ "frameTable": {
+  "AA": "#00: A (A.cpp:99)",
+  "BB": "#00: B (B.cpp:99)",
+  "CC": "#00: C (C.cpp:99)",
+  "DD": "#00: D (D.cpp:99)",
+  "EE": "#00: E (E.cpp:99)",
+  "FF": "#00: F (F.cpp:99)",
+  "GG": "#00: G (G.cpp:99)",
+  "RR1": "#00: R1 (R1.cpp:99)",
+  "RR2": "#00: R2 (R2.cpp:99)"
+ }
+}
--- a/memory/replace/dmd/test/script-ignore-alloc-fns-expected.txt
+++ b/memory/replace/dmd/test/script-ignore-alloc-fns-expected.txt
@@ -1,9 +1,10 @@
 #-----------------------------------------------------------------
+# dmd.py --filter-stacks-for-testing -o script-ignore-alloc-fns-actual.txt --ignore-reports --ignore-alloc-fns script-ignore-alloc-fns.json
 
 Invocation {
   $DMD = '1'
   Sample-below size = 2500
 }
 
 #-----------------------------------------------------------------
 
--- a/memory/replace/dmd/test/script-max-frames-1-expected.txt
+++ b/memory/replace/dmd/test/script-max-frames-1-expected.txt
@@ -1,20 +1,22 @@
 #-----------------------------------------------------------------
+# dmd.py --filter-stacks-for-testing -o script-max-frames-1-actual.txt --ignore-reports --max-frames=1 script-max-frames.json
 
 Invocation {
   $DMD = '1'
   Sample-below size = 1
 }
 
 #-----------------------------------------------------------------
 
 Live {
   4 blocks in heap block record 1 of 1
   4,416 bytes (4,404 requested / 12 slop)
+  Individual block sizes: 4,096; 128; 112; 80
   100.00% of the heap (100.00% cumulative)
   Allocated at {
     #01: E (E.cpp:99)
   }
 }
 
 #-----------------------------------------------------------------
 
--- a/memory/replace/dmd/test/script-max-frames-3-expected.txt
+++ b/memory/replace/dmd/test/script-max-frames-3-expected.txt
@@ -1,20 +1,22 @@
 #-----------------------------------------------------------------
+# dmd.py --filter-stacks-for-testing -o script-max-frames-3-actual.txt --ignore-reports --max-frames=3 --no-fix-stacks script-max-frames.json
 
 Invocation {
   $DMD = '1'
   Sample-below size = 1
 }
 
 #-----------------------------------------------------------------
 
 Live {
   2 blocks in heap block record 1 of 3
   4,224 bytes (4,224 requested / 0 slop)
+  Individual block sizes: 4,096; 128
   95.65% of the heap (95.65% cumulative)
   Allocated at {
     #01: E (E.cpp:99)
     #02: F (F.cpp:99)
     #03: G (G.cpp:99)
   }
 }
 
--- a/memory/replace/dmd/test/script-max-frames-8-expected.txt
+++ b/memory/replace/dmd/test/script-max-frames-8-expected.txt
@@ -1,9 +1,10 @@
 #-----------------------------------------------------------------
+# dmd.py --filter-stacks-for-testing -o script-max-frames-8-actual.txt --ignore-reports --max-frames=8 script-max-frames.json
 
 Invocation {
   $DMD = '1'
   Sample-below size = 1
 }
 
 #-----------------------------------------------------------------
 
deleted file mode 100644
--- a/memory/replace/dmd/test/script-show-all-block-sizes-expected.txt
+++ /dev/null
@@ -1,25 +0,0 @@
-#-----------------------------------------------------------------
-
-Invocation {
-  $DMD = '1'
-  Sample-below size = 4093
-}
-
-#-----------------------------------------------------------------
-
-Live {
-  ~15 blocks in heap block record 1 of 1
-  ~1,343,470 bytes (~1,342,313 requested / ~1,157 slop)
-  100.00% of the heap (100.00% cumulative)
-  Individual block sizes: 1,048,576; 65,536 x 3; 40,960; 8,192 x 4; ~4,093 x 6
-  Allocated at {
-    #01: A (A.cpp:99)
-  }
-}
-
-#-----------------------------------------------------------------
-
-Summary {
-  Total: ~1,343,470 bytes in ~15 blocks
-}
-
deleted file mode 100644
--- a/memory/replace/dmd/test/script-show-all-block-sizes.json
+++ /dev/null
@@ -1,35 +0,0 @@
-{
- "version": 1,
- "invocation": {
-  "dmdEnvVar": "1",
-  "sampleBelowSize": 4093
- },
- "blockList": [
-  {"req": 1048576, "alloc": "A"},
-
-  {"req": 65536,            "alloc": "A"},
-  {"req": 65535, "slop": 1, "alloc": "A"},
-  {"req": 65534, "slop": 2, "alloc": "A"},
-
-  {"req": 40000, "slop": 960, "alloc": "A"},
-
-  {"req": 8192,              "alloc": "A"},
-  {"req": 8192,              "alloc": "A"},
-  {"req": 8190, "slop":   2, "alloc": "A"},
-  {"req": 8000, "slop": 192, "alloc": "A"},
-
-  {"alloc": "A"},
-  {"alloc": "A"},
-  {"alloc": "A"},
-  {"alloc": "A"},
-  {"alloc": "A"},
-  {"alloc": "A"}
- ],
- "traceTable": {
-  "A": ["AA"]
- },
- "frameTable": {
-  "AA": "#00: A (A.cpp:99)"
- }
-}
-
--- a/memory/replace/dmd/test/script-sort-by-req-expected.txt
+++ b/memory/replace/dmd/test/script-sort-by-req-expected.txt
@@ -1,38 +1,42 @@
 #-----------------------------------------------------------------
+# dmd.py --filter-stacks-for-testing -o script-sort-by-req-actual.txt --ignore-reports --sort-by=req --no-fix-stacks script-sort-by.json.gz
 
 Invocation {
   $DMD = '1'
   Sample-below size = 1
 }
 
 #-----------------------------------------------------------------
 
 Live {
   5 blocks in heap block record 1 of 3
   16,392 bytes (16,392 requested / 0 slop)
+  Individual block sizes: 4,096 x 4; 8
   33.33% of the heap (33.33% cumulative)
   Allocated at {
     #01: A (A.cpp:99)
   }
 }
 
 Live {
   5 blocks in heap block record 2 of 3
   16,400 bytes (12,016 requested / 4,384 slop)
+  Individual block sizes: 4,096 x 4; 16
   33.35% of the heap (66.68% cumulative)
   Allocated at {
     #01: B (B.cpp:99)
   }
 }
 
 Live {
   4 blocks in heap block record 3 of 3
   16,384 bytes (8,196 requested / 8,188 slop)
+  Individual block sizes: 4,096 x 4
   33.32% of the heap (100.00% cumulative)
   Allocated at {
     #01: C (C.cpp:99)
   }
 }
 
 #-----------------------------------------------------------------
 
--- a/memory/replace/dmd/test/script-sort-by-slop-expected.txt
+++ b/memory/replace/dmd/test/script-sort-by-slop-expected.txt
@@ -1,38 +1,42 @@
 #-----------------------------------------------------------------
+# dmd.py --filter-stacks-for-testing -o script-sort-by-slop-actual.txt --ignore-reports --sort-by=slop script-sort-by.json.gz
 
 Invocation {
   $DMD = '1'
   Sample-below size = 1
 }
 
 #-----------------------------------------------------------------
 
 Live {
   4 blocks in heap block record 1 of 3
   16,384 bytes (8,196 requested / 8,188 slop)
+  Individual block sizes: 4,096 x 4
   33.32% of the heap (33.32% cumulative)
   Allocated at {
     #01: C (C.cpp:99)
   }
 }
 
 Live {
   5 blocks in heap block record 2 of 3
   16,400 bytes (12,016 requested / 4,384 slop)
+  Individual block sizes: 4,096 x 4; 16
   33.35% of the heap (66.67% cumulative)
   Allocated at {
     #01: B (B.cpp:99)
   }
 }
 
 Live {
   5 blocks in heap block record 3 of 3
   16,392 bytes (16,392 requested / 0 slop)
+  Individual block sizes: 4,096 x 4; 8
   33.33% of the heap (100.00% cumulative)
   Allocated at {
     #01: A (A.cpp:99)
   }
 }
 
 #-----------------------------------------------------------------
 
--- a/memory/replace/dmd/test/script-sort-by-usable-expected.txt
+++ b/memory/replace/dmd/test/script-sort-by-usable-expected.txt
@@ -1,38 +1,42 @@
 #-----------------------------------------------------------------
+# dmd.py --filter-stacks-for-testing -o script-sort-by-usable-actual.txt --ignore-reports --sort-by=usable script-sort-by.json.gz
 
 Invocation {
   $DMD = '1'
   Sample-below size = 1
 }
 
 #-----------------------------------------------------------------
 
 Live {
   5 blocks in heap block record 1 of 3
   16,400 bytes (12,016 requested / 4,384 slop)
+  Individual block sizes: 4,096 x 4; 16
   33.35% of the heap (33.35% cumulative)
   Allocated at {
     #01: B (B.cpp:99)
   }
 }
 
 Live {
   5 blocks in heap block record 2 of 3
   16,392 bytes (16,392 requested / 0 slop)
+  Individual block sizes: 4,096 x 4; 8
   33.33% of the heap (66.68% cumulative)
   Allocated at {
     #01: A (A.cpp:99)
   }
 }
 
 Live {
   4 blocks in heap block record 3 of 3
   16,384 bytes (8,196 requested / 8,188 slop)
+  Individual block sizes: 4,096 x 4
   33.32% of the heap (100.00% cumulative)
   Allocated at {
     #01: C (C.cpp:99)
   }
 }
 
 #-----------------------------------------------------------------
 
--- a/memory/replace/dmd/test/test_dmd.js
+++ b/memory/replace/dmd/test/test_dmd.js
@@ -10,113 +10,174 @@ const {classes: Cc, interfaces: Ci, util
 
 Cu.import("resource://gre/modules/FileUtils.jsm");
 
 // The xpcshell test harness sets PYTHON so we can read it here.
 let gEnv = Cc["@mozilla.org/process/environment;1"]
              .getService(Ci.nsIEnvironment);
 let gPythonName = gEnv.get("PYTHON");
 
-// If we're testing locally, the script is in "CurProcD". Otherwise, it is in
-// another location that we have to find.
-let gDmdScriptFile = FileUtils.getFile("CurProcD", ["dmd.py"]);
-if (!gDmdScriptFile.exists()) {
-  gDmdScriptFile = FileUtils.getFile("CurWorkD", []);
-  while (gDmdScriptFile.path.contains("xpcshell")) {
-    gDmdScriptFile = gDmdScriptFile.parent;
+// If we're testing locally, the executable file is in "CurProcD". Otherwise,
+// it is in another location that we have to find.
+function getExecutable(aFilename) {
+  let file = FileUtils.getFile("CurProcD", [aFilename]);
+  if (!file.exists()) {
+    file = FileUtils.getFile("CurWorkD", []);
+    while (file.path.contains("xpcshell")) {
+      file = file.parent;
+    }
+    file.append("bin");
+    file.append(aFilename);
   }
-  gDmdScriptFile.append("bin");
-  gDmdScriptFile.append("dmd.py");
+  return file;
 }
 
-function test(aJsonFile, aPrefix, aOptions) {
+let gIsWindows = Cc["@mozilla.org/xre/app-info;1"]
+                 .getService(Ci.nsIXULRuntime).OS === "WINNT";
+let gDmdTestFile = getExecutable("SmokeDMD" + (gIsWindows ? ".exe" : ""));
+
+let gDmdScriptFile = getExecutable("dmd.py");
+
+function readFile(aFile) {
+  var fstream = Cc["@mozilla.org/network/file-input-stream;1"]
+                  .createInstance(Ci.nsIFileInputStream);
+  var cstream = Cc["@mozilla.org/intl/converter-input-stream;1"]
+                  .createInstance(Ci.nsIConverterInputStream);
+  fstream.init(aFile, -1, 0, 0);
+  cstream.init(fstream, "UTF-8", 0, 0);
+
+  var data = "";
+  let (str = {}) {
+    let read = 0;
+    do {
+      // Read as much as we can and put it in str.value.
+      read = cstream.readString(0xffffffff, str);
+      data += str.value;
+    } while (read != 0);
+  }
+  cstream.close();                // this closes fstream
+  return data.replace(/\r/g, ""); // normalize line endings
+}
+
+function runProcess(aExeFile, aArgs) {
+  let process = Cc["@mozilla.org/process/util;1"]
+                  .createInstance(Components.interfaces.nsIProcess);
+  process.init(aExeFile);
+  process.run(/* blocking = */true, aArgs, aArgs.length);
+  return process.exitValue;
+}
+
+function test(aPrefix, aArgs) {
   // DMD writes the JSON files to CurWorkD, so we do likewise here with
   // |actualFile| for consistency. It is removed once we've finished.
   let expectedFile = FileUtils.getFile("CurWorkD", [aPrefix + "-expected.txt"]);
   let actualFile   = FileUtils.getFile("CurWorkD", [aPrefix + "-actual.txt"]);
 
   // Run dmd.py on the JSON file, producing |actualFile|.
 
-  let pythonFile = new FileUtils.File(gPythonName);
-  let pythonProcess = Cc["@mozilla.org/process/util;1"]
-                        .createInstance(Components.interfaces.nsIProcess);
-  pythonProcess.init(pythonFile);
-
   let args = [
     gDmdScriptFile.path,
     "--filter-stacks-for-testing",
     "-o", actualFile.path
-  ];
-  args = args.concat(aOptions);
-  args.push(aJsonFile.path);
+  ].concat(aArgs);
+
+  runProcess(new FileUtils.File(gPythonName), args);
 
-  pythonProcess.run(/* blocking = */true, args, args.length);
+  // Compare |expectedFile| with |actualFile|. We produce nice diffs with
+  // /usr/bin/diff on systems that have it (Mac and Linux). Otherwise (Windows)
+  // we do a string compare of the file contents and then print them both if
+  // they don't match.
 
-  // Compare |expectedFile| with |actualFile|. Difference are printed to
-  // stdout.
+  let success;
+  try {
+    let rv = runProcess(new FileUtils.File("/usr/bin/diff"),
+                        ["-u", expectedFile.path, actualFile.path]);
+    success = rv == 0;
 
-  let diffFile = new FileUtils.File("/usr/bin/diff");
-  let diffProcess = Cc["@mozilla.org/process/util;1"]
-                      .createInstance(Components.interfaces.nsIProcess);
-  // XXX: this doesn't work on Windows (bug 1076446).
-  diffProcess.init(diffFile);
+  } catch (e) {
+    let expectedData = readFile(expectedFile);
+    let actualData   = readFile(actualFile);
+    success = expectedData === actualData;
+    if (!success) {
+      expectedData = expectedData.split("\n");
+      actualData = actualData.split("\n");
+      for (let i = 0; i < expectedData.length; i++) {
+        print("EXPECTED:" + expectedData[i]);
+      }
+      for (let i = 0; i < actualData.length; i++) {
+        print("  ACTUAL:" + actualData[i]);
+      }
+    }
+  }
 
-  args = ["-u", expectedFile.path, actualFile.path];
-  diffProcess.run(/* blocking = */true, args, args.length);
-  let success = diffProcess.exitValue == 0;
   ok(success, aPrefix);
 
   actualFile.remove(true);
 }
 
 function run_test() {
-  let jsonFile;
+  let jsonFile, jsonFile2;
 
   // These tests do full end-to-end testing of DMD, i.e. both the C++ code that
   // generates the JSON output, and the script that post-processes that output.
-  // The test relies on DMD's test mode executing beforehand, in order to
-  // produce the relevant JSON files.
   //
   // Run these synchronously, because test() updates the full*.json files
   // in-place (to fix stacks) when it runs dmd.py, and that's not safe to do
   // asynchronously.
+
+  gEnv.set("DMD", "1");
+  gEnv.set(gEnv.get("DMD_PRELOAD_VAR"), gEnv.get("DMD_PRELOAD_VALUE"));
+
+  runProcess(gDmdTestFile, []);
+
   let fullTestNames = ["empty", "unsampled1", "unsampled2", "sampled"];
   for (let i = 0; i < fullTestNames.length; i++) {
       let name = fullTestNames[i];
       jsonFile = FileUtils.getFile("CurWorkD", ["full-" + name + ".json"]);
-      test(jsonFile, "full-heap-" + name, ["--ignore-reports"])
-      test(jsonFile, "full-reports-" + name, [])
+      test("full-heap-" + name, ["--ignore-reports", jsonFile.path])
+      test("full-reports-" + name, [jsonFile.path])
       jsonFile.remove(true);
   }
 
   // These tests only test the post-processing script. They use hand-written
   // JSON files as input. Ideally the JSON files would contain comments
   // explaining how they work, but JSON doesn't allow comments, so I've put
   // explanations here.
 
   // This just tests that stack traces of various lengths are truncated
   // appropriately. The number of records in the output is different for each
   // of the tested values.
   jsonFile = FileUtils.getFile("CurWorkD", ["script-max-frames.json"]);
-  test(jsonFile, "script-max-frames-8", ["-r", "--max-frames=8"]);
-  test(jsonFile, "script-max-frames-3", ["-r", "--max-frames=3",
-                                         "--no-fix-stacks"]);
-  test(jsonFile, "script-max-frames-1", ["-r", "--max-frames=1"]);
+  test("script-max-frames-8",
+       ["--ignore-reports", "--max-frames=8", jsonFile.path]);
+  test("script-max-frames-3",
+       ["--ignore-reports", "--max-frames=3", "--no-fix-stacks",
+        jsonFile.path]);
+  test("script-max-frames-1",
+       ["--ignore-reports", "--max-frames=1", jsonFile.path]);
 
-  // This test has three records that are shown in a different order for each
+  // This file has three records that are shown in a different order for each
   // of the different sort values. It also tests the handling of gzipped JSON
   // files.
   jsonFile = FileUtils.getFile("CurWorkD", ["script-sort-by.json.gz"]);
-  test(jsonFile, "script-sort-by-usable", ["-r", "--sort-by=usable"]);
-  test(jsonFile, "script-sort-by-req",    ["-r", "--sort-by=req",
-                                           "--no-fix-stacks"]);
-  test(jsonFile, "script-sort-by-slop",   ["-r", "--sort-by=slop"]);
+  test("script-sort-by-usable",
+       ["--ignore-reports", "--sort-by=usable", jsonFile.path]);
+  test("script-sort-by-req",
+       ["--ignore-reports", "--sort-by=req", "--no-fix-stacks", jsonFile.path]);
+  test("script-sort-by-slop",
+       ["--ignore-reports", "--sort-by=slop", jsonFile.path]);
 
-  // This test has several real stack traces taken from Firefox execution, each
+  // This file has several real stack traces taken from Firefox execution, each
   // of which tests a different allocator function (or functions).
   jsonFile = FileUtils.getFile("CurWorkD", ["script-ignore-alloc-fns.json"]);
-  test(jsonFile, "script-ignore-alloc-fns", ["-r", "--ignore-alloc-fns"]);
+  test("script-ignore-alloc-fns",
+       ["--ignore-reports", "--ignore-alloc-fns", jsonFile.path]);
 
-  // This test has numerous allocations of different sizes, some repeated, some
-  // sampled, that all end up in the same record.
-  jsonFile = FileUtils.getFile("CurWorkD", ["script-show-all-block-sizes.json"]);
-  test(jsonFile, "script-show-all-block-sizes", ["-r", "--show-all-block-sizes"]);
+  // This tests diffs. The first invocation has no options, the second has
+  // several.
+  jsonFile  = FileUtils.getFile("CurWorkD", ["script-diff1.json"]);
+  jsonFile2 = FileUtils.getFile("CurWorkD", ["script-diff2.json"]);
+  test("script-diff-basic",
+       [jsonFile.path, jsonFile2.path]);
+  test("script-diff-options",
+       ["--ignore-reports", jsonFile.path, jsonFile2.path]);
 }
+
--- a/memory/replace/dmd/test/xpcshell.ini
+++ b/memory/replace/dmd/test/xpcshell.ini
@@ -13,16 +13,19 @@ support-files =
   script-max-frames-3-expected.txt
   script-max-frames-1-expected.txt
   script-sort-by.json.gz
   script-sort-by-usable-expected.txt
   script-sort-by-req-expected.txt
   script-sort-by-slop-expected.txt
   script-ignore-alloc-fns.json
   script-ignore-alloc-fns-expected.txt
-  script-show-all-block-sizes.json
-  script-show-all-block-sizes-expected.txt
+  script-diff1.json
+  script-diff2.json
+  script-diff-basic-expected.txt
+  script-diff-options-expected.txt
 
 # Bug 1077230 explains why this test is disabled on Mac 10.6.
-# Bug 1076446 is open for getting this test working on on Windows.
+# Bug 1076446 comment 20 explains why this test is only enabled on Windows 5.1
+# (WinXP) and 6.1 (Win7), but not 6.2 (Win8).
 [test_dmd.js]
 dmd = true
-run-if = os == 'linux' || os == 'mac' && os_version != '10.6'
+run-if = os == 'linux' || os == 'mac' && os_version != '10.6' || os == 'win' && (os_version == '5.1' || os_version == '6.1')
--- a/netwerk/dns/effective_tld_names.dat
+++ b/netwerk/dns/effective_tld_names.dat
@@ -5251,72 +5251,66 @@ web.pk
 gov.pk
 gob.pk
 gok.pk
 gon.pk
 gop.pk
 gos.pk
 info.pk
 
-// pl : http://www.dns.pl/english/
+// pl http://www.dns.pl/english/index.html
+// confirmed on 26.09.2014 from Bogna Tchórzewska <partner@dns.pl>
 pl
-// NASK functional domains (nask.pl / dns.pl) : http://www.dns.pl/english/dns-funk.html
+com.pl
+net.pl
+org.pl
+info.pl
+waw.pl
+gov.pl
+// pl functional domains (http://www.dns.pl/english/index.html)
 aid.pl
 agro.pl
 atm.pl
 auto.pl
 biz.pl
-com.pl
 edu.pl
 gmina.pl
 gsm.pl
-info.pl
 mail.pl
 miasta.pl
 media.pl
 mil.pl
-net.pl
 nieruchomosci.pl
 nom.pl
-org.pl
 pc.pl
 powiat.pl
 priv.pl
 realestate.pl
 rel.pl
 sex.pl
 shop.pl
 sklep.pl
 sos.pl
 szkola.pl
 targi.pl
 tm.pl
 tourism.pl
 travel.pl
 turystyka.pl
-// ICM functional domains (icm.edu.pl)
-6bone.pl
-art.pl
-mbone.pl
 // Government domains (administred by ippt.gov.pl)
-gov.pl
 uw.gov.pl
 um.gov.pl
 ug.gov.pl
 upow.gov.pl
 starostwo.gov.pl
 so.gov.pl
 sr.gov.pl
 po.gov.pl
 pa.gov.pl
-// other functional domains
-ngo.pl
-irc.pl
-usenet.pl
-// NASK geographical domains : http://www.dns.pl/english/dns-regiony.html
+// pl regional domains (http://www.dns.pl/english/index.html)
 augustow.pl
 babia-gora.pl
 bedzin.pl
 beskidy.pl
 bialowieza.pl
 bialystok.pl
 bielawa.pl
 bieszczady.pl
@@ -5392,17 +5386,16 @@ pruszkow.pl
 przeworsk.pl
 pulawy.pl
 radom.pl
 rawa-maz.pl
 rybnik.pl
 rzeszow.pl
 sanok.pl
 sejny.pl
-siedlce.pl
 slask.pl
 slupsk.pl
 sosnowiec.pl
 stalowa-wola.pl
 skoczow.pl
 starachowice.pl
 stargard.pl
 suwalki.pl
@@ -5414,41 +5407,28 @@ szczytno.pl
 tarnobrzeg.pl
 tgory.pl
 turek.pl
 tychy.pl
 ustka.pl
 walbrzych.pl
 warmia.pl
 warszawa.pl
-waw.pl
 wegrow.pl
 wielun.pl
 wlocl.pl
 wloclawek.pl
 wodzislaw.pl
 wolomin.pl
 wroclaw.pl
 zachpomor.pl
 zagan.pl
 zarow.pl
 zgora.pl
 zgorzelec.pl
-// TASK geographical domains (www.task.gda.pl/uslugi/dns)
-gda.pl
-gdansk.pl
-gdynia.pl
-med.pl
-sopot.pl
-// other geographical domains
-gliwice.pl
-krakow.pl
-poznan.pl
-wroc.pl
-zakopane.pl
 
 // pm : http://www.afnic.fr/medias/documents/AFNIC-naming-policy2012.pdf
 pm
 
 // pn : http://www.government.pn/PnRegistry/policies.htm
 pn
 gov.pn
 co.pn
@@ -8698,30 +8678,32 @@ githubusercontent.com
 
 // GlobeHosting, Inc.
 // Submitted by Zoltan Egresi <egresi@globehosting.com> 2013-07-12
 ro.com
 
 // Google, Inc.
 // Submitted by Eduardo Vela <evn@google.com> 2012-10-24
 appspot.com
+blogspot.ae
 blogspot.be
 blogspot.bj
 blogspot.ca
 blogspot.cf
 blogspot.ch
 blogspot.co.at
 blogspot.co.il
 blogspot.co.nz
 blogspot.co.uk
 blogspot.com
 blogspot.com.ar
 blogspot.com.au
 blogspot.com.br
 blogspot.com.es
+blogspot.com.tr
 blogspot.cv
 blogspot.cz
 blogspot.de
 blogspot.dk
 blogspot.fi
 blogspot.fr
 blogspot.gr
 blogspot.hk
@@ -8733,16 +8715,17 @@ blogspot.jp
 blogspot.kr
 blogspot.mr
 blogspot.mx
 blogspot.nl
 blogspot.no
 blogspot.pt
 blogspot.re
 blogspot.ro
+blogspot.ru
 blogspot.se
 blogspot.sg
 blogspot.sk
 blogspot.td
 blogspot.tw
 codespot.com
 googleapis.com
 googlecode.com
@@ -8785,28 +8768,43 @@ nid.io
 // Opera Software, A.S.A.
 // Submitted by Yngve Pettersen <yngve@opera.com> 2009-11-26
 operaunite.com
 
 // OutSystems
 // Submitted by Duarte Santos <domain-admin@outsystemscloud.com> 2014-03-11
 outsystemscloud.com
 
+// .pl domains (grandfathered)
+art.pl
+gliwice.pl
+krakow.pl
+poznan.pl
+wroc.pl
+zakopane.pl
+
 // Red Hat, Inc. OpenShift : https://openshift.redhat.com/
 // Submitted by Tim Kramer <tkramer@rhcloud.com> 2012-10-24
 rhcloud.com
 
 // GDS : https://www.gov.uk/service-manual/operations/operating-servicegovuk-subdomains
 // Submitted by David Illsley <david.illsley@digital.cabinet-office.gov.uk> 2014-08-28
 service.gov.uk
 
 // priv.at : http://www.nic.priv.at/
 // Submitted by registry <lendl@nic.at> 2008-06-09
 priv.at
 
+// TASK geographical domains (www.task.gda.pl/uslugi/dns)
+gda.pl
+gdansk.pl
+gdynia.pl
+med.pl
+sopot.pl
+
 // Yola : https://www.yola.com/
 // Submitted by Stefano Rivera <stefano@yola.com> 2014-07-09
 yolasite.com
 
 // ZaNiC : http://www.za.net/
 // Submitted by registry <hostmaster@nic.za.net> 2009-10-03
 za.net
 za.org
--- a/parser/html/javasrc/TreeBuilder.java
+++ b/parser/html/javasrc/TreeBuilder.java
@@ -606,42 +606,114 @@ public abstract class TreeBuilder<T> imp
         framesetOk = true;
         if (fragment) {
             T elt;
             if (contextNode != null) {
                 elt = contextNode;
             } else {
                 elt = createHtmlElementSetAsRoot(tokenizer.emptyAttributes());
             }
-            StackNode<T> node = new StackNode<T>(ElementName.HTML, elt
-            // [NOCPP[
-                    , errorHandler == null ? null : new TaintableLocatorImpl(tokenizer)
-            // ]NOCPP]
-            );
-            currentPtr++;
-            stack[currentPtr] = node;
-            if ("template" == contextName) {
-                pushTemplateMode(IN_TEMPLATE);
-            }
-            resetTheInsertionMode();
-            formPointer = getFormPointerForContext(contextNode);
-            if ("title" == contextName || "textarea" == contextName) {
-                tokenizer.setStateAndEndTagExpectation(Tokenizer.RCDATA, contextName);
-            } else if ("style" == contextName || "xmp" == contextName
-                    || "iframe" == contextName || "noembed" == contextName
-                    || "noframes" == contextName
-                    || (scriptingEnabled && "noscript" == contextName)) {
-                tokenizer.setStateAndEndTagExpectation(Tokenizer.RAWTEXT, contextName);
-            } else if ("plaintext" == contextName) {
-                tokenizer.setStateAndEndTagExpectation(Tokenizer.PLAINTEXT, contextName);
-            } else if ("script" == contextName) {
-                tokenizer.setStateAndEndTagExpectation(Tokenizer.SCRIPT_DATA,
+            // When the context node is not in the HTML namespace, contrary
+            // to the spec, the first node on the stack is not set to "html"
+            // in the HTML namespace. Instead, it is set to a node that has
+            // the characteristics of the appropriate "adjusted current node".
+            // This way, there is no need to perform "adjusted current node"
+            // checks during tree construction. Instead, it's sufficient to
+            // just look at the current node. However, this also means that it
+            // is not safe to treat "html" in the HTML namespace as a sentinel
+            // that ends stack popping. Instead, stack popping loops that are
+            // meant not to pop the first element on the stack need to check
+            // for currentPos becoming zero.
+            if (contextNamespace == "http://www.w3.org/2000/svg") {
+                ElementName elementName = ElementName.SVG;
+                if ("title" == contextName || "desc" == contextName
+                        || "foreignObject" == contextName) {
+                    // These elements are all alike and we don't care about
+                    // the exact name.
+                    elementName = ElementName.FOREIGNOBJECT;
+                }
+                // This is the SVG variant of the StackNode constructor.
+                StackNode<T> node = new StackNode<T>(elementName,
+                        elementName.camelCaseName, elt
+                        // [NOCPP[
+                        , errorHandler == null ? null
+                                : new TaintableLocatorImpl(tokenizer)
+                // ]NOCPP]
+                );
+                currentPtr++;
+                stack[currentPtr] = node;
+                tokenizer.setStateAndEndTagExpectation(Tokenizer.DATA,
                         contextName);
-            } else {
-                tokenizer.setStateAndEndTagExpectation(Tokenizer.DATA, contextName);
+                // The frameset-ok flag is set even though <frameset> never
+                // ends up being allowed as HTML frameset in the fragment case.
+                mode = FRAMESET_OK;
+            } else if (contextNamespace == "http://www.w3.org/1998/Math/MathML") {
+                ElementName elementName = ElementName.MATH;
+                if ("mi" == contextName || "mo" == contextName
+                        || "mn" == contextName || "ms" == contextName
+                        || "mtext" == contextName) {
+                    // These elements are all alike and we don't care about
+                    // the exact name.
+                    elementName = ElementName.MTEXT;
+                } else if ("annotation-xml" == contextName) {
+                    elementName = ElementName.ANNOTATION_XML;
+                    // Blink does not check the encoding attribute of the
+                    // annotation-xml element innerHTML is being set on.
+                    // Let's do the same at least until
+                    // https://www.w3.org/Bugs/Public/show_bug.cgi?id=26783
+                    // is resolved.
+                }
+                // This is the MathML variant of the StackNode constructor.
+                StackNode<T> node = new StackNode<T>(elementName, elt,
+                        elementName.name, false
+                        // [NOCPP[
+                        , errorHandler == null ? null
+                                : new TaintableLocatorImpl(tokenizer)
+                // ]NOCPP]
+                );
+                currentPtr++;
+                stack[currentPtr] = node;
+                tokenizer.setStateAndEndTagExpectation(Tokenizer.DATA,
+                        contextName);
+                // The frameset-ok flag is set even though <frameset> never
+                // ends up being allowed as HTML frameset in the fragment case.
+                mode = FRAMESET_OK;
+            } else { // html
+                StackNode<T> node = new StackNode<T>(ElementName.HTML, elt
+                // [NOCPP[
+                        , errorHandler == null ? null
+                                : new TaintableLocatorImpl(tokenizer)
+                // ]NOCPP]
+                );
+                currentPtr++;
+                stack[currentPtr] = node;
+                if ("template" == contextName) {
+                    pushTemplateMode(IN_TEMPLATE);
+                }
+                resetTheInsertionMode();
+                formPointer = getFormPointerForContext(contextNode);
+                if ("title" == contextName || "textarea" == contextName) {
+                    tokenizer.setStateAndEndTagExpectation(Tokenizer.RCDATA,
+                            contextName);
+                } else if ("style" == contextName || "xmp" == contextName
+                        || "iframe" == contextName || "noembed" == contextName
+                        || "noframes" == contextName
+                        || (scriptingEnabled && "noscript" == contextName)) {
+                    tokenizer.setStateAndEndTagExpectation(Tokenizer.RAWTEXT,
+                            contextName);
+                } else if ("plaintext" == contextName) {
+                    tokenizer.setStateAndEndTagExpectation(Tokenizer.PLAINTEXT,
+                            contextName);
+                } else if ("script" == contextName) {
+                    tokenizer.setStateAndEndTagExpectation(
+                            Tokenizer.SCRIPT_DATA, contextName);
+                } else {
+                    tokenizer.setStateAndEndTagExpectation(Tokenizer.DATA,
+                            contextName);
+                }
             }
             contextName = null;
             contextNode = null;
         } else {
             mode = INITIAL;
             // If we are viewing XML source, put a foreign element permanently
             // on the stack so that cdataSectionAllowed() returns true.
             // CPPONLY: if (tokenizer.isViewingXmlSource()) {
@@ -1449,17 +1521,18 @@ public abstract class TreeBuilder<T> imp
                 case IN_SELECT_IN_TABLE:
                 case IN_SELECT:
                 case IN_COLUMN_GROUP:
                 case FRAMESET_OK:
                 case IN_CAPTION:
                 case IN_CELL:
                 case IN_BODY:
                     // [NOCPP[
-                    openelementloop: for (int i = currentPtr; i >= 0; i--) {
+                    // i > 0 to stop in time in the foreign fragment case.
+                    openelementloop: for (int i = currentPtr; i > 0; i--) {
                         int group = stack[i].getGroup();
                         switch (group) {
                             case DD_OR_DT:
                             case LI:
                             case P:
                             case TBODY_OR_THEAD_OR_TFOOT:
                             case TD_OR_TH:
                             case BODY:
@@ -1616,30 +1689,27 @@ public abstract class TreeBuilder<T> imp
                         case HEAD:
                         case HR:
                         case LI:
                         case META:
                         case NOBR:
                         case P:
                         case PRE_OR_LISTING:
                         case TABLE:
-                            errHtmlStartTagInForeignContext(name);
-                            while (!isSpecialParentInForeign(stack[currentPtr])) {
-                                pop();
-                            }
-                            continue starttagloop;
                         case FONT:
-                            if (attributes.contains(AttributeName.COLOR)
-                                    || attributes.contains(AttributeName.FACE)
-                                    || attributes.contains(AttributeName.SIZE)) {
+                            // re-check FONT to deal with the special case
+                            if (!(group == FONT && !(attributes.contains(AttributeName.COLOR)
+                                    || attributes.contains(AttributeName.FACE) || attributes.contains(AttributeName.SIZE)))) {
                                 errHtmlStartTagInForeignContext(name);
-                                while (!isSpecialParentInForeign(stack[currentPtr])) {
-                                    pop();
-                                }
-                                continue starttagloop;
+                                if (!fragment) {
+                                    while (!isSpecialParentInForeign(stack[currentPtr])) {
+                                        pop();
+                                    }
+                                    continue starttagloop;
+                                } // else fall thru
                             }
                             // else fall thru
                         default:
                             if ("http://www.w3.org/2000/svg" == currNs) {
                                 attributes.adjustForSvg();
                                 if (selfClosing) {
                                     appendVoidElementToCurrentMayFosterSVG(
                                             elementName, attributes);
@@ -3303,20 +3373,28 @@ public abstract class TreeBuilder<T> imp
         flushCharacters();
         needToDropLF = false;
         int eltPos;
         int group = elementName.getGroup();
         @Local String name = elementName.name;
         endtagloop: for (;;) {
             if (isInForeign()) {
                 if (stack[currentPtr].name != name) {
-                    errEndTagDidNotMatchCurrentOpenElement(name, stack[currentPtr].popName);
+                    if (currentPtr == 0) {
+                        errStrayEndTag(name);
+                    } else {
+                        errEndTagDidNotMatchCurrentOpenElement(name, stack[currentPtr].popName);
+                    }
                 }
                 eltPos = currentPtr;
                 for (;;) {
+                    if (eltPos == 0) {
+                        assert fragment: "We can get this close to the root of the stack in foreign content only in the fragment case.";
+                        break endtagloop;
+                    }
                     if (stack[eltPos].name == name) {
                         while (currentPtr >= eltPos) {
                             pop();
                         }
                         break endtagloop;
                     }
                     if (stack[--eltPos].ns == "http://www.w3.org/1999/xhtml") {
                         break;
@@ -3644,17 +3722,19 @@ public abstract class TreeBuilder<T> imp
                             }
                         case P:
                             eltPos = findLastInButtonScope("p");
                             if (eltPos == TreeBuilder.NOT_FOUND_ON_STACK) {
                                 errNoElementToCloseButEndTagSeen("p");
                                 // XXX Can the 'in foreign' case happen anymore?
                                 if (isInForeign()) {
                                     errHtmlStartTagInForeignContext(name);
-                                    while (stack[currentPtr].ns != "http://www.w3.org/1999/xhtml") {
+                                    // Check for currentPtr for the fragment
+                                    // case.
+                                    while (currentPtr >= 0 && stack[currentPtr].ns != "http://www.w3.org/1999/xhtml") {
                                         pop();
                                     }
                                 }
                                 appendVoidElementToCurrentMayFoster(
                                         elementName,
                                         HtmlAttributes.EMPTY_ATTRIBUTES);
                                 break endtagloop;
                             }
@@ -3725,18 +3805,21 @@ public abstract class TreeBuilder<T> imp
                                     pop();
                                 }
                                 clearTheListOfActiveFormattingElementsUpToTheLastMarker();
                             }
                             break endtagloop;
                         case BR:
                             errEndTagBr();
                             if (isInForeign()) {
+                                // XXX can this happen anymore?
                                 errHtmlStartTagInForeignContext(name);
-                                while (stack[currentPtr].ns != "http://www.w3.org/1999/xhtml") {
+                                // Check for currentPtr for the fragment
+                                // case.
+                                while (currentPtr >= 0 && stack[currentPtr].ns != "http://www.w3.org/1999/xhtml") {
                                     pop();
                                 }
                             }
                             reconstructTheActiveFormattingElements();
                             appendVoidElementToCurrentMayFoster(
                                     elementName,
                                     HtmlAttributes.EMPTY_ATTRIBUTES);
                             break endtagloop;
--- a/parser/html/nsHtml5AtomList.h
+++ b/parser/html/nsHtml5AtomList.h
@@ -16,18 +16,26 @@
  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 
  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
  * DEALINGS IN THE SOFTWARE.
  */
 
 HTML5_ATOM(emptystring, "")
+HTML5_ATOM(title, "title")
+HTML5_ATOM(desc, "desc")
+HTML5_ATOM(foreignObject, "foreignObject")
+HTML5_ATOM(mi, "mi")
+HTML5_ATOM(mo, "mo")
+HTML5_ATOM(mn, "mn")
+HTML5_ATOM(ms, "ms")
+HTML5_ATOM(mtext, "mtext")
+HTML5_ATOM(annotation_xml, "annotation-xml")
 HTML5_ATOM(template_, "template")
-HTML5_ATOM(title, "title")
 HTML5_ATOM(textarea, "textarea")
 HTML5_ATOM(style, "style")
 HTML5_ATOM(xmp, "xmp")
 HTML5_ATOM(iframe, "iframe")
 HTML5_ATOM(noembed, "noembed")
 HTML5_ATOM(noframes, "noframes")
 HTML5_ATOM(noscript, "noscript")
 HTML5_ATOM(plaintext, "plaintext")
@@ -723,20 +731,16 @@ HTML5_ATOM(h3, "h3")
 HTML5_ATOM(h4, "h4")
 HTML5_ATOM(h5, "h5")
 HTML5_ATOM(h6, "h6")
 HTML5_ATOM(gt, "gt")
 HTML5_ATOM(hr, "hr")
 HTML5_ATOM(li, "li")
 HTML5_ATOM(ln, "ln")
 HTML5_ATOM(lt, "lt")
-HTML5_ATOM(mi, "mi")
-HTML5_ATOM(mn, "mn")
-HTML5_ATOM(mo, "mo")
-HTML5_ATOM(ms, "ms")
 HTML5_ATOM(or_, "or")
 HTML5_ATOM(pi, "pi")
 HTML5_ATOM(rb, "rb")
 HTML5_ATOM(rp, "rp")
 HTML5_ATOM(tt, "tt")
 HTML5_ATOM(and_, "and")
 HTML5_ATOM(arg, "arg")
 HTML5_ATOM(abs, "abs")
@@ -780,17 +784,16 @@ HTML5_ATOM(wbr, "wbr")
 HTML5_ATOM(xor_, "xor")
 HTML5_ATOM(area, "area")
 HTML5_ATOM(bvar, "bvar")
 HTML5_ATOM(card, "card")
 HTML5_ATOM(csch, "csch")
 HTML5_ATOM(cosh, "cosh")
 HTML5_ATOM(coth, "coth")
 HTML5_ATOM(curl, "curl")
-HTML5_ATOM(desc, "desc")
 HTML5_ATOM(diff, "diff")
 HTML5_ATOM(defs, "defs")
 HTML5_ATOM(font, "font")
 HTML5_ATOM(grad, "grad")
 HTML5_ATOM(line, "line")
 HTML5_ATOM(meta, "meta")
 HTML5_ATOM(msub, "msub")
 HTML5_ATOM(math, "math")
@@ -831,17 +834,16 @@ HTML5_ATOM(ident, "ident")
 HTML5_ATOM(limit, "limit")
 HTML5_ATOM(mfrac, "mfrac")
 HTML5_ATOM(mpath, "mpath")
 HTML5_ATOM(meter, "meter")
 HTML5_ATOM(mover, "mover")
 HTML5_ATOM(minus, "minus")
 HTML5_ATOM(mroot, "mroot")
 HTML5_ATOM(msqrt, "msqrt")
-HTML5_ATOM(mtext, "mtext")
 HTML5_ATOM(notin, "notin")
 HTML5_ATOM(piece, "piece")
 HTML5_ATOM(param, "param")
 HTML5_ATOM(power, "power")
 HTML5_ATOM(reals, "reals")
 HTML5_ATOM(small_, "small")
 HTML5_ATOM(track, "track")
 HTML5_ATOM(tspan, "tspan")
@@ -1030,24 +1032,22 @@ HTML5_ATOM(feDropShadow, "feDropShadow")
 HTML5_ATOM(femorphology, "femorphology")
 HTML5_ATOM(feMorphology, "feMorphology")
 HTML5_ATOM(outerproduct, "outerproduct")
 HTML5_ATOM(animatemotion, "animatemotion")
 HTML5_ATOM(animateMotion, "animateMotion")
 HTML5_ATOM(font_face_src, "font-face-src")
 HTML5_ATOM(font_face_uri, "font-face-uri")
 HTML5_ATOM(foreignobject, "foreignobject")
-HTML5_ATOM(foreignObject, "foreignObject")
 HTML5_ATOM(fecolormatrix, "fecolormatrix")
 HTML5_ATOM(feColorMatrix, "feColorMatrix")
 HTML5_ATOM(missing_glyph, "missing-glyph")
 HTML5_ATOM(mmultiscripts, "mmultiscripts")
 HTML5_ATOM(scalarproduct, "scalarproduct")
 HTML5_ATOM(vectorproduct, "vectorproduct")
-HTML5_ATOM(annotation_xml, "annotation-xml")
 HTML5_ATOM(definition_src, "definition-src")
 HTML5_ATOM(font_face_name, "font-face-name")
 HTML5_ATOM(fegaussianblur, "fegaussianblur")
 HTML5_ATOM(feGaussianBlur, "feGaussianBlur")
 HTML5_ATOM(fedistantlight, "fedistantlight")
 HTML5_ATOM(feDistantLight, "feDistantLight")
 HTML5_ATOM(lineargradient, "lineargradient")
 HTML5_ATOM(linearGradient, "linearGradient")
--- a/parser/html/nsHtml5TreeBuilder.cpp
+++ b/parser/html/nsHtml5TreeBuilder.cpp
@@ -92,34 +92,58 @@ nsHtml5TreeBuilder::startTokenization(ns
   framesetOk = true;
   if (fragment) {
     nsIContentHandle* elt;
     if (contextNode) {
       elt = contextNode;
     } else {
       elt = createHtmlElementSetAsRoot(tokenizer->emptyAttributes());
     }
-    nsHtml5StackNode* node = new nsHtml5StackNode(nsHtml5ElementName::ELT_HTML, elt);
-    currentPtr++;
-    stack[currentPtr] = node;
-    if (nsHtml5Atoms::template_ == contextName) {
-      pushTemplateMode(NS_HTML5TREE_BUILDER_IN_TEMPLATE);
-    }
-    resetTheInsertionMode();
-    formPointer = getFormPointerForContext(contextNode);
-    if (nsHtml5Atoms::title == contextName || nsHtml5Atoms::textarea == contextName) {
-      tokenizer->setStateAndEndTagExpectation(NS_HTML5TOKENIZER_RCDATA, contextName);
-    } else if (nsHtml5Atoms::style == contextName || nsHtml5Atoms::xmp == contextName || nsHtml5Atoms::iframe == contextName || nsHtml5Atoms::noembed == contextName || nsHtml5Atoms::noframes == contextName || (scriptingEnabled && nsHtml5Atoms::noscript == contextName)) {
-      tokenizer->setStateAndEndTagExpectation(NS_HTML5TOKENIZER_RAWTEXT, contextName);
-    } else if (nsHtml5Atoms::plaintext == contextName) {
-      tokenizer->setStateAndEndTagExpectation(NS_HTML5TOKENIZER_PLAINTEXT, contextName);
-    } else if (nsHtml5Atoms::script == contextName) {
-      tokenizer->setStateAndEndTagExpectation(NS_HTML5TOKENIZER_SCRIPT_DATA, contextName);
+    if (contextNamespace == kNameSpaceID_SVG) {
+      nsHtml5ElementName* elementName = nsHtml5ElementName::ELT_SVG;
+      if (nsHtml5Atoms::title == contextName || nsHtml5Atoms::desc == contextName || nsHtml5Atoms::foreignObject == contextName) {
+        elementName = nsHtml5ElementName::ELT_FOREIGNOBJECT;
+      }
+      nsHtml5StackNode* node = new nsHtml5StackNode(elementName, elementName->camelCaseName, elt);
+      currentPtr++;
+      stack[currentPtr] = node;
+      tokenizer->setStateAndEndTagExpectation(NS_HTML5TOKENIZER_DATA, contextName);
+      mode = NS_HTML5TREE_BUILDER_FRAMESET_OK;
+    } else if (contextNamespace == kNameSpaceID_MathML) {
+      nsHtml5ElementName* elementName = nsHtml5ElementName::ELT_MATH;
+      if (nsHtml5Atoms::mi == contextName || nsHtml5Atoms::mo == contextName || nsHtml5Atoms::mn == contextName || nsHtml5Atoms::ms == contextName || nsHtml5Atoms::mtext == contextName) {
+        elementName = nsHtml5ElementName::ELT_MTEXT;
+      } else if (nsHtml5Atoms::annotation_xml == contextName) {
+        elementName = nsHtml5ElementName::ELT_ANNOTATION_XML;
+      }
+      nsHtml5StackNode* node = new nsHtml5StackNode(elementName, elt, elementName->name, false);
+      currentPtr++;
+      stack[currentPtr] = node;
+      tokenizer->setStateAndEndTagExpectation(NS_HTML5TOKENIZER_DATA, contextName);
+      mode = NS_HTML5TREE_BUILDER_FRAMESET_OK;
     } else {
-      tokenizer->setStateAndEndTagExpectation(NS_HTML5TOKENIZER_DATA, contextName);
+      nsHtml5StackNode* node = new nsHtml5StackNode(nsHtml5ElementName::ELT_HTML, elt);
+      currentPtr++;
+      stack[currentPtr] = node;
+      if (nsHtml5Atoms::template_ == contextName) {
+        pushTemplateMode(NS_HTML5TREE_BUILDER_IN_TEMPLATE);
+      }
+      resetTheInsertionMode();
+      formPointer = getFormPointerForContext(contextNode);
+      if (nsHtml5Atoms::title == contextName || nsHtml5Atoms::textarea == contextName) {
+        tokenizer->setStateAndEndTagExpectation(NS_HTML5TOKENIZER_RCDATA, contextName);
+      } else if (nsHtml5Atoms::style == contextName || nsHtml5Atoms::xmp == contextName || nsHtml5Atoms::iframe == contextName || nsHtml5Atoms::noembed == contextName || nsHtml5Atoms::noframes == contextName || (scriptingEnabled && nsHtml5Atoms::noscript == contextName)) {
+        tokenizer->setStateAndEndTagExpectation(NS_HTML5TOKENIZER_RAWTEXT, contextName);
+      } else if (nsHtml5Atoms::plaintext == contextName) {
+        tokenizer->setStateAndEndTagExpectation(NS_HTML5TOKENIZER_PLAINTEXT, contextName);
+      } else if (nsHtml5Atoms::script == contextName) {
+        tokenizer->setStateAndEndTagExpectation(NS_HTML5TOKENIZER_SCRIPT_DATA, contextName);
+      } else {
+        tokenizer->setStateAndEndTagExpectation(NS_HTML5TOKENIZER_DATA, contextName);
+      }
     }
     contextName = nullptr;
     contextNode = nullptr;
   } else {
     mode = NS_HTML5TREE_BUILDER_INITIAL;
     if (tokenizer->isViewingXmlSource()) {
       nsIContentHandle* elt = createElement(kNameSpaceID_SVG, nsHtml5Atoms::svg, tokenizer->emptyAttributes());
       nsHtml5StackNode* node = new nsHtml5StackNode(nsHtml5ElementName::ELT_SVG, nsHtml5Atoms::svg, elt);
@@ -601,30 +625,26 @@ nsHtml5TreeBuilder::startTag(nsHtml5Elem
           case NS_HTML5TREE_BUILDER_H1_OR_H2_OR_H3_OR_H4_OR_H5_OR_H6:
           case NS_HTML5TREE_BUILDER_HEAD:
           case NS_HTML5TREE_BUILDER_HR:
           case NS_HTML5TREE_BUILDER_LI:
           case NS_HTML5TREE_BUILDER_META:
           case NS_HTML5TREE_BUILDER_NOBR:
           case NS_HTML5TREE_BUILDER_P:
           case NS_HTML5TREE_BUILDER_PRE_OR_LISTING:
-          case NS_HTML5TREE_BUILDER_TABLE: {
-            errHtmlStartTagInForeignContext(name);
-            while (!isSpecialParentInForeign(stack[currentPtr])) {
-              pop();
-            }
-            NS_HTML5_CONTINUE(starttagloop);
-          }
+          case NS_HTML5TREE_BUILDER_TABLE:
           case NS_HTML5TREE_BUILDER_FONT: {
-            if (attributes->contains(nsHtml5AttributeName::ATTR_COLOR) || attributes->contains(nsHtml5AttributeName::ATTR_FACE) || attributes->contains(nsHtml5AttributeName::ATTR_SIZE)) {
+            if (!(group == NS_HTML5TREE_BUILDER_FONT && !(attributes->contains(nsHtml5AttributeName::ATTR_COLOR) || attributes->contains(nsHtml5AttributeName::ATTR_FACE) || attributes->contains(nsHtml5AttributeName::ATTR_SIZE)))) {
               errHtmlStartTagInForeignContext(name);
-              while (!isSpecialParentInForeign(stack[currentPtr])) {
-                pop();
+              if (!fragment) {
+                while (!isSpecialParentInForeign(stack[currentPtr])) {
+                  pop();
+                }
+                NS_HTML5_CONTINUE(starttagloop);
               }
-              NS_HTML5_CONTINUE(starttagloop);
             }
           }
           default: {
             if (kNameSpaceID_SVG == currNs) {
               attributes->adjustForSvg();
               if (selfClosing) {
                 appendVoidElementToCurrentMayFosterSVG(elementName, attributes);
                 selfClosing = false;
@@ -2197,20 +2217,28 @@ nsHtml5TreeBuilder::endTag(nsHtml5Elemen
   flushCharacters();
   needToDropLF = false;
   int32_t eltPos;
   int32_t group = elementName->getGroup();
   nsIAtom* name = elementName->name;
   for (; ; ) {
     if (isInForeign()) {
       if (stack[currentPtr]->name != name) {
-        errEndTagDidNotMatchCurrentOpenElement(name, stack[currentPtr]->popName);
+        if (!currentPtr) {
+          errStrayEndTag(name);
+        } else {
+          errEndTagDidNotMatchCurrentOpenElement(name, stack[currentPtr]->popName);
+        }
       }
       eltPos = currentPtr;
       for (; ; ) {
+        if (!eltPos) {
+          MOZ_ASSERT(fragment, "We can get this close to the root of the stack in foreign content only in the fragment case.");
+          NS_HTML5_BREAK(endtagloop);
+        }
         if (stack[eltPos]->name == name) {
           while (currentPtr >= eltPos) {
             pop();
           }
           NS_HTML5_BREAK(endtagloop);
         }
         if (stack[--eltPos]->ns == kNameSpaceID_XHTML) {
           break;
@@ -2569,17 +2597,17 @@ nsHtml5TreeBuilder::endTag(nsHtml5Elemen
             }
           }
           case NS_HTML5TREE_BUILDER_P: {
             eltPos = findLastInButtonScope(nsHtml5Atoms::p);
             if (eltPos == NS_HTML5TREE_BUILDER_NOT_FOUND_ON_STACK) {
               errNoElementToCloseButEndTagSeen(nsHtml5Atoms::p);
               if (isInForeign()) {
                 errHtmlStartTagInForeignContext(name);
-                while (stack[currentPtr]->ns != kNameSpaceID_XHTML) {
+                while (currentPtr >= 0 && stack[currentPtr]->ns != kNameSpaceID_XHTML) {
                   pop();
                 }
               }
               appendVoidElementToCurrentMayFoster(elementName, nsHtml5HtmlAttributes::EMPTY_ATTRIBUTES);
               NS_HTML5_BREAK(endtagloop);
             }
             generateImpliedEndTagsExceptFor(nsHtml5Atoms::p);
             MOZ_ASSERT(eltPos != NS_HTML5TREE_BUILDER_NOT_FOUND_ON_STACK);
@@ -2652,17 +2680,17 @@ nsHtml5TreeBuilder::endTag(nsHtml5Elemen
               clearTheListOfActiveFormattingElementsUpToTheLastMarker();
             }
             NS_HTML5_BREAK(endtagloop);
           }
           case NS_HTML5TREE_BUILDER_BR: {
             errEndTagBr();
             if (isInForeign()) {
               errHtmlStartTagInForeignContext(name);
-              while (stack[currentPtr]->ns != kNameSpaceID_XHTML) {
+              while (currentPtr >= 0 && stack[currentPtr]->ns != kNameSpaceID_XHTML) {
                 pop();
               }
             }
             reconstructTheActiveFormattingElements();
             appendVoidElementToCurrentMayFoster(elementName, nsHtml5HtmlAttributes::EMPTY_ATTRIBUTES);
             NS_HTML5_BREAK(endtagloop);
           }
           case NS_HTML5TREE_BUILDER_TEMPLATE: {
--- a/parser/htmlparser/tests/mochitest/html5lib_tree_construction/README.md
+++ b/parser/htmlparser/tests/mochitest/html5lib_tree_construction/README.md
@@ -16,24 +16,37 @@ Where [TEST] is the following format:
 Each test must begin with a string "\#data" followed by a newline (LF).
 All subsequent lines until a line that says "\#errors" are the test data
 and must be passed to the system being tested unchanged, except with the
 final newline (on the last line) removed.
 
 Then there must be a line that says "\#errors". It must be followed by
 one line per parse error that a conformant checker would return. It
 doesn't matter what those lines are, although they can't be
-"\#document-fragment", "\#document", or empty, the only thing that
-matters is that there be the right number of parse errors.
+"\#document-fragment", "\#document", "\#script-off", "\#script-on", or
+empty, the only thing that matters is that there be the right number
+of parse errors.
 
 Then there \*may\* be a line that says "\#document-fragment", which must
 be followed by a newline (LF), followed by a string of characters that
-indicates the context element, followed by a newline (LF). If this line
-is present the "\#data" must be parsed using the HTML fragment parsing
-algorithm with the context element as context.
+indicates the context element, followed by a newline (LF). If the string 
+of characters starts with "svg ", the context element is in the SVG
+namespace and the substring after "svg " is the local name. If the
+string of characters starts with "math ", the context element is in the
+MathML namespace and the substring after "math " is the local name.
+Otherwise, the context element is in the HTML namespace and the string
+is the local name. If this line is present the "\#data" must be parsed
+using the HTML fragment parsing algorithm with the context element as
+context.
+
+Then there \*may\* be a line that says "\#script-off" or
+"\#script-in". If a line that says "\#script-off" is present, the
+parser must set the scripting flag to disabled. If a line that says
+"\#script-on" is present, it must set it to enabled. Otherwise, the
+test should be run in both modes.
 
 Then there must be a line that says "\#document", which must be followed
 by a dump of the tree of the parsed DOM. Each node must be represented
 by a single line. Each line must start with "| ", followed by two spaces
 per parent node that the node has before the root document node.
 
 -   Element nodes must be represented by a "`<`" then the *tag name
     string* "`>`", and all the attributes must be given, sorted
@@ -48,16 +61,18 @@ per parent node that the node has before
 -   DOCTYPEs must be "`<!DOCTYPE `" then the name then if either of the
     system id or public id is non-empty a space, public id in
     double-quotes, another space an the system id in double-quotes, and
     then in any case "`>`".
 -   Processing instructions must be "`<?`", then the target, then a
     space, then the data and then "`>`". (The HTML parser cannot emit
     processing instructions, but scripts can, and the WebVTT to DOM
     rules can emit them.)
+-   Template contents are represented by the string "content" with the
+    children below it.
 
 The *tag name string* is the local name prefixed by a namespace
 designator. For the HTML namespace, the namespace designator is the
 empty string, i.e. there's no prefix. For the SVG namespace, the
 namespace designator is "svg ". For the MathML namespace, the namespace
 designator is "math ".
 
 The *attribute name string* is the local name prefixed by a namespace
new file mode 100644
--- /dev/null
+++ b/parser/htmlparser/tests/mochitest/html5lib_tree_construction/foreign-fragment.dat
@@ -0,0 +1,550 @@
+#data
+<nobr>X
+#errors
+6: HTML start tag “nobr” in a foreign namespace context.
+7: End of file seen and there were open elements.
+6: Unclosed element “nobr”.
+#document-fragment
+svg path
+#document
+| <svg nobr>
+|   "X"
+
+#data
+<font color></font>X
+#errors
+12: HTML start tag “font” in a foreign namespace context.
+#document-fragment
+svg path
+#document
+| <svg font>
+|   color=""
+| "X"
+
+#data
+<font></font>X
+#errors
+#document-fragment
+svg path
+#document
+| <svg font>
+| "X"
+
+#data
+<g></path>X
+#errors
+10: End tag “path” did not match the name of the current open element (“g”).
+11: End of file seen and there were open elements.
+3: Unclosed element “g”.
+#document-fragment
+svg path
+#document
+| <svg g>
+|   "X"
+
+#data
+</path>X
+#errors
+5: Stray end tag “path”.
+#document-fragment
+svg path
+#document
+| "X"
+
+#data
+</foreignObject>X
+#errors
+5: Stray end tag “foreignobject”.
+#document-fragment
+svg foreignObject
+#document
+| "X"
+
+#data
+</desc>X
+#errors
+5: Stray end tag “desc”.
+#document-fragment
+svg desc
+#document
+| "X"
+
+#data
+</title>X
+#errors
+5: Stray end tag “title”.
+#document-fragment
+svg title
+#document
+| "X"
+
+#data
+</svg>X
+#errors
+5: Stray end tag “svg”.
+#document-fragment
+svg svg
+#document
+| "X"
+
+#data
+</mfenced>X
+#errors
+5: Stray end tag “mfenced”.
+#document-fragment
+math mfenced
+#document
+| "X"
+
+#data
+</malignmark>X
+#errors
+5: Stray end tag “malignmark”.
+#document-fragment
+math malignmark
+#document
+| "X"
+
+#data
+</math>X
+#errors
+5: Stray end tag “math”.
+#document-fragment
+math math
+#document
+| "X"
+
+#data
+</annotation-xml>X
+#errors
+5: Stray end tag “annotation-xml”.
+#document-fragment
+math annotation-xml
+#document
+| "X"
+
+#data
+</mtext>X
+#errors
+5: Stray end tag “mtext”.
+#document-fragment
+math mtext
+#document
+| "X"
+
+#data
+</mi>X
+#errors
+5: Stray end tag “mi”.
+#document-fragment
+math mi
+#document
+| "X"
+
+#data
+</mo>X
+#errors
+5: Stray end tag “mo”.
+#document-fragment
+math mo
+#document
+| "X"
+
+#data
+</mn>X
+#errors
+5: Stray end tag “mn”.
+#document-fragment
+math mn
+#document
+| "X"
+
+#data
+</ms>X
+#errors
+5: Stray end tag “ms”.
+#document-fragment
+math ms
+#document
+| "X"
+
+#data
+<b></b><mglyph/><i></i><malignmark/><u></u><ms/>X
+#errors
+51: Self-closing syntax (“/>”) used on a non-void HTML element. Ignoring the slash and treating as a start tag.
+52: End of file seen and there were open elements.
+51: Unclosed element “ms”.
+#document-fragment
+math ms
+#document
+| <b>
+| <math mglyph>
+| <i>
+| <math malignmark>
+| <u>
+| <ms>
+|   "X"
+
+#data
+<malignmark></malignmark>
+#errors
+#document-fragment
+math ms
+#document
+| <math malignmark>
+
+#data
+<div></div>
+#errors
+#document-fragment
+math ms
+#document
+| <div>
+
+#data
+<figure></figure>
+#errors
+#document-fragment
+math ms
+#document
+| <figure>
+
+#data
+<b></b><mglyph/><i></i><malignmark/><u></u><mn/>X
+#errors
+51: Self-closing syntax (“/>”) used on a non-void HTML element. Ignoring the slash and treating as a start tag.
+52: End of file seen and there were open elements.
+51: Unclosed element “mn”.
+#document-fragment
+math mn
+#document
+| <b>
+| <math mglyph>
+| <i>
+| <math malignmark>
+| <u>
+| <mn>
+|   "X"
+
+#data
+<malignmark></malignmark>
+#errors
+#document-fragment
+math mn
+#document
+| <math malignmark>
+
+#data
+<div></div>
+#errors
+#document-fragment
+math mn
+#document
+| <div>
+
+#data
+<figure></figure>
+#errors
+#document-fragment
+math mn
+#document
+| <figure>
+
+#data
+<b></b><mglyph/><i></i><malignmark/><u></u><mo/>X
+#errors
+51: Self-closing syntax (“/>”) used on a non-void HTML element. Ignoring the slash and treating as a start tag.
+52: End of file seen and there were open elements.
+51: Unclosed element “mo”.
+#document-fragment
+math mo
+#document
+| <b>
+| <math mglyph>
+| <i>
+| <math malignmark>
+| <u>
+| <mo>
+|   "X"
+
+#data
+<malignmark></malignmark>
+#errors
+#document-fragment
+math mo
+#document
+| <math malignmark>
+
+#data
+<div></div>
+#errors
+#document-fragment
+math mo
+#document
+| <div>
+
+#data
+<figure></figure>
+#errors
+#document-fragment
+math mo
+#document
+| <figure>
+
+#data
+<b></b><mglyph/><i></i><malignmark/><u></u><mi/>X
+#errors
+51: Self-closing syntax (“/>”) used on a non-void HTML element. Ignoring the slash and treating as a start tag.
+52: End of file seen and there were open elements.
+51: Unclosed element “mi”.
+#document-fragment
+math mi
+#document
+| <b>
+| <math mglyph>
+| <i>
+| <math malignmark>
+| <u>
+| <mi>
+|   "X"
+
+#data
+<malignmark></malignmark>
+#errors
+#document-fragment
+math mi
+#document
+| <math malignmark>
+
+#data
+<div></div>
+#errors
+#document-fragment
+math mi
+#document
+| <div>
+
+#data
+<figure></figure>
+#errors
+#document-fragment
+math mi
+#document
+| <figure>
+
+#data
+<b></b><mglyph/><i></i><malignmark/><u></u><mtext/>X
+#errors
+51: Self-closing syntax (“/>”) used on a non-void HTML element. Ignoring the slash and treating as a start tag.
+52: End of file seen and there were open elements.
+51: Unclosed element “mtext”.
+#document-fragment
+math mtext
+#document
+| <b>
+| <math mglyph>
+| <i>
+| <math malignmark>
+| <u>
+| <mtext>
+|   "X"
+
+#data
+<malignmark></malignmark>
+#errors
+#document-fragment
+math mtext
+#document
+| <math malignmark>
+
+#data
+<div></div>
+#errors
+#document-fragment
+math mtext
+#document
+| <div>
+
+#data
+<figure></figure>
+#errors
+#document-fragment
+math mtext
+#document
+| <figure>
+
+#data
+<div></div>
+#errors
+5: HTML start tag “div” in a foreign namespace context.
+#document-fragment
+math annotation-xml
+#document
+| <math div>
+
+#data
+<figure></figure>
+#errors
+#document-fragment
+math annotation-xml
+#document
+| <math figure>
+
+#data
+<div></div>
+#errors
+5: HTML start tag “div” in a foreign namespace context.
+#document-fragment
+math math
+#document
+| <math div>
+
+#data
+<figure></figure>
+#errors
+#document-fragment
+math math
+#document
+| <math figure>
+
+#data
+<div></div>
+#errors
+#document-fragment
+svg foreignObject
+#document
+| <div>
+
+#data
+<figure></figure>
+#errors
+#document-fragment
+svg foreignObject
+#document
+| <figure>
+
+#data
+<div></div>
+#errors
+#document-fragment
+svg title
+#document
+| <div>
+
+#data
+<figure></figure>
+#errors
+#document-fragment
+svg title
+#document
+| <figure>
+
+#data
+<figure></figure>
+#errors
+#document-fragment
+svg desc
+#document
+| <figure>
+
+#data
+<div><h1>X</h1></div>
+#errors
+5: HTML start tag “div” in a foreign namespace context.
+9: HTML start tag “h1” in a foreign namespace context.
+#document-fragment
+svg svg
+#document
+| <svg div>
+|   <svg h1>
+|     "X"
+
+#data
+<div></div>
+#errors
+5: HTML start tag “div” in a foreign namespace context.
+#document-fragment
+svg svg
+#document
+| <svg div>
+
+#data
+<div></div>
+#errors
+#document-fragment
+svg desc
+#document
+| <div>
+
+#data
+<figure></figure>
+#errors
+#document-fragment
+svg desc
+#document
+| <figure>
+
+#data
+<plaintext><foo>
+#errors
+16: End of file seen and there were open elements.
+11: Unclosed element “plaintext”.
+#document-fragment
+svg desc
+#document
+| <plaintext>
+|   "<foo>"
+
+#data
+<frameset>X
+#errors
+6: Stray start tag “frameset”.
+#document-fragment
+svg desc
+#document
+| "X"
+
+#data
+<head>X
+#errors
+6: Stray start tag “head”.
+#document-fragment
+svg desc
+#document
+| "X"
+
+#data
+<body>X
+#errors
+6: Stray start tag “body”.
+#document-fragment
+svg desc
+#document
+| "X"
+
+#data
+<html>X
+#errors
+6: Stray start tag “html”.
+#document-fragment
+svg desc
+#document
+| "X"
+
+#data
+<html class="foo">X
+#errors
+6: Stray start tag “html”.
+#document-fragment
+svg desc
+#document
+| "X"
+
+#data
+<body class="foo">X
+#errors
+6: Stray start tag “body”.
+#document-fragment
+svg desc
+#document
+| "X"
--- a/parser/htmlparser/tests/mochitest/mochitest.ini
+++ b/parser/htmlparser/tests/mochitest/mochitest.ini
@@ -45,16 +45,17 @@ support-files =
   dir_bug534293/file_bug534293.sjs
   html5lib_tree_construction/adoption01.dat
   html5lib_tree_construction/adoption02.dat
   html5lib_tree_construction/comments01.dat
   html5lib_tree_construction/doctype01.dat
   html5lib_tree_construction/domjs-unsafe.dat
   html5lib_tree_construction/entities01.dat
   html5lib_tree_construction/entities02.dat
+  html5lib_tree_construction/foreign-fragment.dat
   html5lib_tree_construction/html5test-com.dat
   html5lib_tree_construction/inbody01.dat
   html5lib_tree_construction/isindex.dat
   html5lib_tree_construction/pending-spec-changes.dat
   html5lib_tree_construction/pending-spec-changes-plain-text-unsafe.dat
   html5lib_tree_construction/plain-text-unsafe.dat
   html5lib_tree_construction/scriptdata01.dat
   html5lib_tree_construction/tables01.dat
--- a/parser/htmlparser/tests/mochitest/parser_web_testrunner.js
+++ b/parser/htmlparser/tests/mochitest/parser_web_testrunner.js
@@ -71,18 +71,27 @@ function makeTestChecker(input, expected
 }
 
 function makeFragmentTestChecker(input, 
                                  expected, 
                                  errors, 
                                  fragment, 
                                  testframe) {
   return function () {
-    var context = document.createElementNS("http://www.w3.org/1999/xhtml",
-                                           fragment);
+    var context;
+    if (fragment.startsWith("svg ")) {
+      context = document.createElementNS("http://www.w3.org/2000/svg",
+                                         fragment.substring(4));
+    } else if (fragment.startsWith("math ")) {
+      context = document.createElementNS("http://www.w3.org/1998/Math/MathML",
+                                         fragment.substring(5));
+    } else {
+      context = document.createElementNS("http://www.w3.org/1999/xhtml",
+                                         fragment);
+    }
     context.innerHTML = input;
     var domAsString = fragmentToTestOutput(context);
     is(domAsString, expected, "HTML5 expected success. " + new Date());
     if (domAsString != expected) {
       writeErrorSummary(input, expected, domAsString, false);
     }
     nextTest(testframe);
   } 
--- a/parser/htmlparser/tests/mochitest/test_html5_tree_construction_part2.html
+++ b/parser/htmlparser/tests/mochitest/test_html5_tree_construction_part2.html
@@ -34,16 +34,17 @@ https://bugzilla.mozilla.org/show_bug.cg
                          "tests7.dat",
                          "tests8.dat",
                          "tests9.dat",
                          "tests_innerHTML_1.dat",
                          "tricky01.dat",
                          "webkit01.dat",
                          "webkit02.dat",
                          "main-element.dat",
+                         "foreign-fragment.dat",
                          "ruby.dat"];
   </script>
   <script type="application/javascript;version=1.7"
           src="parser_web_testrunner.js"></script>
 </head>
 <body>
 <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=366936"
    target="_blank">Mozilla Bug 366936</a>
--- a/python/mozbuild/mozbuild/mach_commands.py
+++ b/python/mozbuild/mozbuild/mach_commands.py
@@ -789,24 +789,24 @@ class Install(MachCommandBase):
 class RunProgram(MachCommandBase):
     """Run the compiled program."""
 
     prog_group = 'the compiled program'
 
     @Command('run', category='post-build',
         description='Run the compiled program, possibly under a debugger or DMD.')
     @CommandArgument('params', nargs='...', group=prog_group,
-        help='Command-line arguments to be passed through to the program. Not specifying a -profile or -P option will result in a temporary profile being used.')
+        help='Command-line arguments to be passed through to the program. Not specifying a --profile or -P option will result in a temporary profile being used.')
     @CommandArgumentGroup(prog_group)
-    @CommandArgument('-remote', '-r', action='store_true', group=prog_group,
-        help='Do not pass the -no-remote argument by default.')
-    @CommandArgument('-background', '-b', action='store_true', group=prog_group,
-        help='Do not pass the -foreground argument by default on Mac.')
-    @CommandArgument('-noprofile', '-n', action='store_true', group=prog_group,
-        help='Do not pass the -profile argument by default.')
+    @CommandArgument('--remote', '-r', action='store_true', group=prog_group,
+        help='Do not pass the --no-remote argument by default.')
+    @CommandArgument('--background', '-b', action='store_true', group=prog_group,
+        help='Do not pass the --foreground argument by default on Mac.')
+    @CommandArgument('--noprofile', '-n', action='store_true', group=prog_group,
+        help='Do not pass the --profile argument by default.')
 
     @CommandArgumentGroup('debugging')
     @CommandArgument('--debug', action='store_true', group='debugging',
         help='Enable the debugger. Not specifying a --debugger option will result in the default debugger being used. The following arguments have no effect without this.')
     @CommandArgument('--debugger', default=None, type=str, group='debugging',
         help='Name of debugger to use.')
     @CommandArgument('--debugparams', default=None, metavar='params', type=str,
         group='debugging',
@@ -823,21 +823,19 @@ class RunProgram(MachCommandBase):
     @CommandArgument('--dmd', action='store_true', group='DMD',
         help='Enable DMD. The following arguments have no effect without this.')
     @CommandArgument('--sample-below', default=None, type=str, group='DMD',
         help='Sample blocks smaller than this. Use 1 for no sampling. The default is 4093.')
     @CommandArgument('--max-frames', default=None, type=str, group='DMD',
         help='The maximum depth of stack traces. The default and maximum is 24.')
     @CommandArgument('--show-dump-stats', action='store_true', group='DMD',
         help='Show stats when doing dumps.')
-    @CommandArgument('--mode', choices=['normal', 'test'], group='DMD',
-        help='Mode of operation. The default is normal.')
     def run(self, params, remote, background, noprofile, debug, debugger,
         debugparams, slowscript, dmd, sample_below, max_frames,
-        show_dump_stats, mode):
+        show_dump_stats):
 
         try:
             binpath = self.get_binary_path('app')
         except Exception as e:
             print("It looks like your program isn't built.",
                 "You can run |mach build| to build it.")
             print(e)
             return 1
@@ -848,17 +846,19 @@ class RunProgram(MachCommandBase):
             args.extend(params)
 
         if not remote:
             args.append('-no-remote')
 
         if not background and sys.platform == 'darwin':
             args.append('-foreground')
 
-        if '-profile' not in params and '-P' not in params and not noprofile:
+        no_profile_option_given = \
+            all(p not in params for p in ['-profile', '--profile', '-P'])
+        if no_profile_option_given and not noprofile:
             path = os.path.join(self.topobjdir, 'tmp', 'scratch_user')
             if not os.path.isdir(path):
                 os.makedirs(path)
             args.append('-profile')
             args.append(path)
 
         extra_env = {}
 
@@ -897,18 +897,16 @@ class RunProgram(MachCommandBase):
             dmd_params = []
 
             if sample_below:
                 dmd_params.append('--sample-below=' + sample_below)
             if max_frames:
                 dmd_params.append('--max-frames=' + max_frames)
             if show_dump_stats:
                 dmd_params.append('--show-dump-stats=yes')
-            if mode:
-                dmd_params.append('--mode=' + mode)
 
             if dmd_params:
                 dmd_env_var = " ".join(dmd_params)
             else:
                 dmd_env_var = "1"
 
             bin_dir = os.path.dirname(binpath)
             lib_name = self.substs['DLL_PREFIX'] + 'dmd' + self.substs['DLL_SUFFIX']
--- a/services/sync/tps/extensions/tps/components/tps-cmdline.js
+++ b/services/sync/tps/extensions/tps/components/tps-cmdline.js
@@ -67,19 +67,19 @@ TPSCmdLineHandler.prototype = {
     Components.utils.import("resource://tps/tps.jsm");
     Components.utils.import("resource://tps/quit.js", TPS);
     let uri = cmdLine.resolveURI(uristr).spec;
     TPS.RunTestPhase(uri, phase, logfile, options);
 
     //cmdLine.preventDefault = true;
   },
 
-  helpInfo : "  -tps <file>               Run TPS tests with the given test file.\n" +
-             "  -tpsphase <phase>         Run the specified phase in the TPS test.\n" +
-             "  -tpslogfile <file>        Logfile for TPS output.\n" +
+  helpInfo : "  --tps <file>              Run TPS tests with the given test file.\n" +
+             "  --tpsphase <phase>        Run the specified phase in the TPS test.\n" +
+             "  --tpslogfile <file>       Logfile for TPS output.\n" +
              "  --ignore-unused-engines   Don't load engines not used in tests.\n",
 };
 
 
 var TPSCmdLineFactory = {
   createInstance : function(outer, iid) {
     if (outer != null) {
       throw new Error(Components.results.NS_ERROR_NO_AGGREGATION);
--- a/testing/marionette/client/marionette/selection.py
+++ b/testing/marionette/client/marionette/selection.py
@@ -68,40 +68,48 @@ class SelectionManager(object):
             cmd = '''var len = arguments[0].value.length;
                   arguments[0].setSelectionRange(len, len);'''
         else:
             cmd = '''var sel = window.getSelection();
                   sel.collapse(arguments[0].lastChild, arguments[0].lastChild.length);'''
 
         self.element.marionette.execute_script(cmd, script_args=[self.element])
 
-    def selection_rect_list(self):
-        '''Return the selection's DOMRectList object.
+    def selection_rect_list(self, idx):
+        '''Return the selection's DOMRectList object for the range at given idx.
 
-        If the element is either <input> or <textarea>, return the selection's
-        DOMRectList within the element. Otherwise, return the DOMRectList of the
-        current selection.
+        If the element is either <input> or <textarea>, return the DOMRectList of
+        the range at given idx of the selection within the element. Otherwise,
+        return the DOMRectList of the of the range at given idx of current selection.
 
         '''
         cmd = self.js_selection_cmd() +\
-            '''return sel.getRangeAt(0).getClientRects();'''
+            '''return sel.getRangeAt(%d).getClientRects();''' % idx
+        return self.element.marionette.execute_script(cmd, script_args=[self.element])
+
+    def range_count(self):
+        '''Get selection's range count'''
+        cmd = self.js_selection_cmd() +\
+            '''return sel.rangeCount;'''
         return self.element.marionette.execute_script(cmd, script_args=[self.element])
 
     def _selection_location_helper(self, location_type):
         '''Return the start and end location of the selection in the element.
 
         Return a tuple containing two pairs of (x, y) coordinates of the start
         and end locations in the element. The coordinates are relative to the
         top left-hand corner of the element. Both ltr and rtl directions are
         considered.
 
         '''
-        rect_list = self.selection_rect_list()
-        list_length = rect_list['length']
-        first_rect, last_rect = rect_list['0'], rect_list[str(list_length - 1)]
+        range_count = self.range_count();
+        first_rect_list = self.selection_rect_list(0)
+        last_rect_list = self.selection_rect_list(range_count - 1)
+        last_list_length = last_rect_list['length']
+        first_rect, last_rect = first_rect_list['0'], last_rect_list[str(last_list_length - 1)]
         origin_x, origin_y = self.element.location['x'], self.element.location['y']
 
         if self.element.get_attribute('dir') == 'rtl':  # such as Arabic
             start_pos, end_pos = 'right', 'left'
         else:
             start_pos, end_pos = 'left', 'right'
 
         # Calculate y offset according to different needs.
new file mode 100644
--- /dev/null
+++ b/testing/marionette/client/marionette/www/test_selectioncarets_multiplerange.html
@@ -0,0 +1,19 @@
+<html>
+<style>
+h4 {
+  -moz-user-select: none;
+}
+</style>
+<body id=bd>
+<h3 id=sel1>user can select this 1</h3>
+<h3 id=sel2>user can select this 2</h3>
+<h3 id=sel3>user can select this 3</h3>
+<h4 id=nonsel1>user cannot select this 1</h4>
+<h4 id=nonsel2>user cannot select this 2</h4>
+<h3 id=sel4>user can select this 4</h3>
+<h3 id=sel5>user can select this 5</h3>
+<h4 id=nonsel3>user cannot select this 3</h4>
+<h3 id=sel6>user can select this 6</h3>
+<h3 id=sel7>user can select this 7</h3>
+</body>
+</html>
--- a/testing/mochitest/Makefile.in
+++ b/testing/mochitest/Makefile.in
@@ -58,17 +58,20 @@ endif
 ifeq (windows,$(MOZ_WIDGET_TOOLKIT))
 TEST_HARNESS_BINS += screenshot$(BIN_SUFFIX)
 ifdef MOZ_METRO
 TEST_HARNESS_BINS += metrotestharness$(BIN_SUFFIX)
 endif
 endif
 
 ifdef MOZ_DMD
-TEST_HARNESS_BINS += dmd.py
+TEST_HARNESS_BINS += \
+  dmd.py \
+  SmokeDMD$(BIN_SUFFIX) \
+  $(NULL)
 endif
 
 # Components / typelibs that don't get packaged with
 # the build, but that we need for the test harness.
 TEST_HARNESS_COMPONENTS := \
   test_necko.xpt \
   $(NULL)
 
--- a/testing/xpcshell/runxpcshelltests.py
+++ b/testing/xpcshell/runxpcshelltests.py
@@ -615,20 +615,20 @@ class XPCShellTestThread(Thread):
                 # self.xrePath is <prefix>/Contents/Resources.
                 # We need <prefix>/Contents/MacOS/libdmd.dylib.
                 contents_dir = os.path.dirname(self.xrePath)
                 libdmd = os.path.join(contents_dir, 'MacOS', 'libdmd.dylib')
             elif sys.platform == 'win32':
                 preloadEnvVar = 'MOZ_REPLACE_MALLOC_LIB'
                 libdmd = os.path.join(self.xrePath, 'dmd.dll')
 
-            self.env['DMD'] = '--mode=test'
             self.env['PYTHON'] = sys.executable
             self.env['BREAKPAD_SYMBOLS_PATH'] = self.symbolsPath
-            self.env[preloadEnvVar] = libdmd
+            self.env['DMD_PRELOAD_VAR'] = preloadEnvVar
+            self.env['DMD_PRELOAD_VALUE'] = libdmd
 
         testTimeoutInterval = HARNESS_TIMEOUT
         # Allow a test to request a multiple of the timeout if it is expected to take long
         if 'requesttimeoutfactor' in self.test_object:
             testTimeoutInterval *= int(self.test_object['requesttimeoutfactor'])
 
         testTimer = None
         if not self.interactive and not self.debuggerInfo:
--- a/toolkit/components/commandlines/nsICommandLine.idl
+++ b/toolkit/components/commandlines/nsICommandLine.idl
@@ -11,17 +11,17 @@ interface nsIDOMWindow;
 /**
  * Represents the command line used to invoke a XUL application. This may be the
  * original command-line of this instance, or a command line remoted from another
  * instance of the application.
  *
  * DEFINITIONS:
  * "arguments" are any values found on the command line.
  * "flags" are switches. In normalized form they are preceded by a single dash.
- * Some flags may take "parameters", e.g. "-url <param>" or "-install-xpi <param>"
+ * Some flags may take "parameters", e.g. "--url <param>".
  */
 
 [scriptable, uuid(bc3173bd-aa46-46a0-9d25-d9867a9659b6)]
 interface nsICommandLine : nsISupports
 {
   /**
    * Number of arguments in the command line. The application name is not
    * part of the command line.
--- a/toolkit/components/commandlines/nsICommandLineHandler.idl
+++ b/toolkit/components/commandlines/nsICommandLineHandler.idl
@@ -38,16 +38,16 @@ interface nsICommandLineHandler : nsISup
    *
    * @throw NS_ERROR_ABORT to immediately cease command-line handling
    *        (if this is STATE_INITIAL_LAUNCH, quits the app).
    *        All other exceptions are silently ignored.
    */
   void handle(in nsICommandLine aCommandLine);
 
   /**
-   * When the app is launched with the -help argument, this attribute
+   * When the app is launched with the --help argument, this attribute
    * is retrieved and displayed to the user (on stdout). The text should
    * have embedded newlines which wrap at 76 columns, and should include
    * a newline at the end. By convention, the right column which contains flag
    * descriptions begins at the 24th character.
    */
   readonly attribute AUTF8String helpInfo;
 };
--- a/toolkit/components/console/jsconsole-clhandler.js
+++ b/toolkit/components/console/jsconsole-clhandler.js
@@ -26,15 +26,15 @@ jsConsoleHandler.prototype = {
     } else {
       console.focus(); // the Error console was already open
     }
 
     if (cmdLine.state == Ci.nsICommandLine.STATE_REMOTE_AUTO)
       cmdLine.preventDefault = true;
   },
 
-  helpInfo : "  -jsconsole         Open the Error console.\n",
+  helpInfo : "  --jsconsole        Open the Error console.\n",
 
   classID: Components.ID("{2cd0c310-e127-44d0-88fc-4435c9ab4d4b}"),
   QueryInterface: XPCOMUtils.generateQI([Ci.nsICommandLineHandler]),
 };
 
 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([jsConsoleHandler]);
--- a/toolkit/mozapps/installer/packager.mk
+++ b/toolkit/mozapps/installer/packager.mk
@@ -591,18 +591,21 @@ NO_PKG_FILES += \
 	content_unit_tests \
 	necko_unit_tests \
 	*.dSYM \
 	$(NULL)
 
 # If a manifest has not been supplied, the following
 # files should be excluded from the package too
 ifndef MOZ_PKG_MANIFEST
-NO_PKG_FILES += \
-	ssltunnel*
+NO_PKG_FILES += ssltunnel*
+endif
+
+ifdef MOZ_DMD
+NO_PKG_FILES += SmokeDMD
 endif
 
 # browser/locales/Makefile uses this makefile for its variable defs, but
 # doesn't want the libs:: rule.
 ifndef PACKAGER_NO_LIBS
 libs:: make-package
 endif
 
--- a/toolkit/mozapps/update/updater/updater.cpp
+++ b/toolkit/mozapps/update/updater/updater.cpp
@@ -2180,17 +2180,17 @@ UpdateThreadFunc(void *param)
     // updater application again in order to apply the update without
     // staging.
     // The MOZ_NO_REPLACE_FALLBACK environment variable is used to
     // bypass this fallback, and is used in the updater tests.
     // The only special thing which we should do here is to remove the
     // staged directory as it won't be useful any more.
     ensure_remove_recursive(gWorkingDirPath);
     WriteStatusFile(sUsingService ? "pending-service" : "pending");
-    putenv(const_cast<char*>("MOZ_PROCESS_UPDATES=")); // We need to use -process-updates again in the tests
+    putenv(const_cast<char*>("MOZ_PROCESS_UPDATES=")); // We need to use --process-updates again in the tests
     reportRealResults = false; // pretend success
   }
 
   if (reportRealResults) {
     if (rv) {
       LOG(("failed: %d", rv));
     }
     else {
--- a/toolkit/xre/nsAppRunner.cpp
+++ b/toolkit/xre/nsAppRunner.cpp
@@ -486,17 +486,17 @@ CheckArg(const char* aArg, bool aCheckOS
 
     ++curarg;
   }
 
   if (aCheckOSInt && ar == ARG_FOUND) {
     ArgResult arOSInt = CheckArg("osint");
     if (arOSInt == ARG_FOUND) {
       ar = ARG_BAD;
-      PR_fprintf(PR_STDERR, "Error: argument -osint is invalid\n");
+      PR_fprintf(PR_STDERR, "Error: argument --osint is invalid\n");
     }
   }
 
   return ar;
 }
 
 #if defined(XP_WIN)
 /**
@@ -1508,32 +1508,32 @@ DumpHelp()
          "  --display=DISPLAY  X display to use\n"
          "  --sync             Make X calls synchronous\n");
 #endif
 #ifdef XP_UNIX
   printf("  --g-fatal-warnings Make all warnings fatal\n"
          "\n%s options\n", gAppData->name);
 #endif
 
-  printf("  -h or -help        Print this message.\n"
-         "  -v or -version     Print %s version.\n"
+  printf("  -h or --help       Print this message.\n"
+         "  -v or --version    Print %s version.\n"
          "  -P <profile>       Start with <profile>.\n"
-         "  -profile <path>    Start with profile at <path>.\n"
-         "  -migration         Start with migration wizard.\n"
-         "  -ProfileManager    Start with ProfileManager.\n"
-         "  -no-remote         Do not accept or send remote commands; implies -new-instance.\n"
-         "  -new-instance      Open new instance, not a new window in running instance.\n"
-         "  -UILocale <locale> Start with <locale> resources as UI Locale.\n"
-         "  -safe-mode         Disables extensions and themes for this session.\n", gAppData->name);
+         "  --profile <path>   Start with profile at <path>.\n"
+         "  --migration        Start with migration wizard.\n"
+         "  --ProfileManager   Start with ProfileManager.\n"
+         "  --no-remote        Do not accept or send remote commands; implies --new-instance.\n"
+         "  --new-instance     Open new instance, not a new window in running instance.\n"
+         "  --UILocale <locale> Start with <locale> resources as UI Locale.\n"
+         "  --safe-mode        Disables extensions and themes for this session.\n", gAppData->name);
 
 #if defined(XP_WIN)
-  printf("  -console           Start %s with a debugging console.\n", gAppData->name);
+  printf("  --console          Start %s with a debugging console.\n", gAppData->name);
 #endif
 
-  // this works, but only after the components have registered.  so if you drop in a new command line handler, -help
+  // this works, but only after the components have registered.  so if you drop in a new command line handler, --help
   // won't not until the second run.
   // out of the bug, because we ship a component.reg file, it works correctly.
   DumpArbitraryHelp();
 }
 
 #if defined(DEBUG) && defined(XP_WIN)
 #ifdef DEBUG_warren
 #define _CRTDBG_MAP_ALLOC
@@ -2087,19 +2087,19 @@ SetCurrentProfileAsDefault(nsIToolkitPro
   return rv;
 }
 
 static bool gDoMigration = false;
 static bool gDoProfileReset = false;
 
 // Pick a profile. We need to end up with a profile lock.
 //
-// 1) check for -profile <path>
+// 1) check for --profile <path>
 // 2) check for -P <name>
-// 3) check for -ProfileManager
+// 3) check for --ProfileManager
 // 4) use the default profile, if there is one
 // 5) if there are *no* profiles, set up profile-migration
 // 6) display the profile-manager UI
 static nsresult
 SelectProfile(nsIProfileLock* *aResult, nsIToolkitProfileService* aProfileSvc, nsINativeAppSupport* aNative,
               bool* aStartOffline, nsACString* aProfileName)
 {
   StartupTimeline::Record(StartupTimeline::SELECT_PROFILE);
@@ -2107,41 +2107,41 @@ SelectProfile(nsIProfileLock* *aResult, 
   nsresult rv;
   ArgResult ar;
   const char* arg;
   *aResult = nullptr;
   *aStartOffline = false;
 
   ar = CheckArg("offline", true);
   if (ar == ARG_BAD) {
-    PR_fprintf(PR_STDERR, "Error: argument -offline is invalid when argument -osint is specified\n");
+    PR_fprintf(PR_STDERR, "Error: argument --offline is invalid when argument --osint is specified\n");
     return NS_ERROR_FAILURE;
   }
 
   if (ar || EnvHasValue("XRE_START_OFFLINE"))
     *aStartOffline = true;
 
   if (EnvHasValue("MOZ_RESET_PROFILE_RESTART")) {
     gDoProfileReset = true;
     gDoMigration = true;
     SaveToEnv("MOZ_RESET_PROFILE_RESTART=");
   }
 
   // reset-profile and migration args need to be checked before any profiles are chosen below.
   ar = CheckArg("reset-profile", true);
   if (ar == ARG_BAD) {
-    PR_fprintf(PR_STDERR, "Error: argument -reset-profile is invalid when argument -osint is specified\n");
+    PR_fprintf(PR_STDERR, "Error: argument --reset-profile is invalid when argument --osint is specified\n");
     return NS_ERROR_FAILURE;
   } else if (ar == ARG_FOUND) {
     gDoProfileReset = true;
   }
 
   ar = CheckArg("migration", true);
   if (ar == ARG_BAD) {
-    PR_fprintf(PR_STDERR, "Error: argument -migration is invalid when argument -osint is specified\n");
+    PR_fprintf(PR_STDERR, "Error: argument --migration is invalid when argument --osint is specified\n");
     return NS_ERROR_FAILURE;
   } else if (ar == ARG_FOUND) {
     gDoMigration = true;
   }
 
   nsCOMPtr<nsIFile> lf = GetFileFromEnv("XRE_PROFILE_PATH");
   if (lf) {
     nsCOMPtr<nsIFile> localDir =
@@ -2192,17 +2192,17 @@ SelectProfile(nsIProfileLock* *aResult, 
       }
     }
 
     return NS_LockProfilePath(lf, localDir, nullptr, aResult);
   }
 
   ar = CheckArg("profile", true, &arg);
   if (ar == ARG_BAD) {
-    PR_fprintf(PR_STDERR, "Error: argument -profile requires a path\n");
+    PR_fprintf(PR_STDERR, "Error: argument --profile requires a path\n");
     return NS_ERROR_FAILURE;
   }
   if (ar) {
     if (gDoProfileReset) {
       NS_WARNING("Profile reset is only supported for the default profile.");
       gDoProfileReset = false;
     }
 
@@ -2226,33 +2226,33 @@ SelectProfile(nsIProfileLock* *aResult, 
     if (NS_SUCCEEDED(rv))
       return rv;
 
     return ProfileLockedDialog(lf, lf, unlocker, aNative, aResult);
   }
 
   ar = CheckArg("createprofile", true, &arg);
   if (ar == ARG_BAD) {
-    PR_fprintf(PR_STDERR, "Error: argument -createprofile requires a profile name\n");
+    PR_fprintf(PR_STDERR, "Error: argument --createprofile requires a profile name\n");
     return NS_ERROR_FAILURE;
   }
   if (ar) {
     nsCOMPtr<nsIToolkitProfile> profile;
 
     const char* delim = strchr(arg, ' ');
     if (delim) {
       nsCOMPtr<nsIFile> lf;
       rv = NS_NewNativeLocalFile(nsDependentCString(delim + 1),
                                    true, getter_AddRefs(lf));
       if (NS_FAILED(rv)) {
         PR_fprintf(PR_STDERR, "Error: profile path not valid.\n");
         return rv;
       }
-      
-      // As with -profile, assume that the given path will be used for the
+
+      // As with --profile, assume that the given path will be used for the
       // main profile directory.
       rv = aProfileSvc->CreateProfile(lf, nsDependentCSubstring(arg, delim),
                                      getter_AddRefs(profile));
     } else {
       rv = aProfileSvc->CreateProfile(nullptr, nsDependentCString(arg),
                                      getter_AddRefs(profile));
     }
     // Some pathological arguments can make it this far
@@ -2283,28 +2283,28 @@ SelectProfile(nsIProfileLock* *aResult, 
   uint32_t count;
   rv = aProfileSvc->GetProfileCount(&count);
   NS_ENSURE_SUCCESS(rv, rv);
 
   ar = CheckArg("p", false, &arg);
   if (ar == ARG_BAD) {
     ar = CheckArg("osint");
     if (ar == ARG_FOUND) {
-      PR_fprintf(PR_STDERR, "Error: argument -p is invalid when argument -osint is specified\n");
+      PR_fprintf(PR_STDERR, "Error: argument -p is invalid when argument --osint is specified\n");
       return NS_ERROR_FAILURE;
     }
 
     if (CanShowProfileManager()) {
       return ShowProfileManager(aProfileSvc, aNative);
     }
   }
   if (ar) {
     ar = CheckArg("osint");
     if (ar == ARG_FOUND) {
-      PR_fprintf(PR_STDERR, "Error: argument -p is invalid when argument -osint is specified\n");
+      PR_fprintf(PR_STDERR, "Error: argument -p is invalid when argument --osint is specified\n");
       return NS_ERROR_FAILURE;
     }
     nsCOMPtr<nsIToolkitProfile> profile;
     rv = aProfileSvc->GetProfileByName(nsDependentCString(arg),
                                       getter_AddRefs(profile));
     if (NS_SUCCEEDED(rv)) {
       if (gDoProfileReset) {
         NS_WARNING("Profile reset is only supported for the default profile.");
@@ -2324,17 +2324,17 @@ SelectProfile(nsIProfileLock* *aResult, 
 
     if (CanShowProfileManager()) {
       return ShowProfileManager(aProfileSvc, aNative);
     }
   }
 
   ar = CheckArg("profilemanager", true);
   if (ar == ARG_BAD) {
-    PR_fprintf(PR_STDERR, "Error: argument -profilemanager is invalid when argument -osint is specified\n");
+    PR_fprintf(PR_STDERR, "Error: argument --profilemanager is invalid when argument --osint is specified\n");
     return NS_ERROR_FAILURE;
   } else if (ar == ARG_FOUND && CanShowProfileManager()) {
     return ShowProfileManager(aProfileSvc, aNative);
   }
 
   if (!count) {
     gDoMigration = true;
     gDoProfileReset = false;
@@ -3005,17 +3005,17 @@ XREMain::XRE_mainInit(bool* aExitFlag)
   // initialized.  This works after gnome 2.24.2.
   SaveToEnv("NO_AT_BRIDGE=1");
 #endif
 
   // Check for application.ini overrides
   const char* override = nullptr;
   ar = CheckArg("override", true, &override);
   if (ar == ARG_BAD) {
-    Output(true, "Incorrect number of arguments passed to -override");
+    Output(true, "Incorrect number of arguments passed to --override");
     return 1;
   }
   else if (ar == ARG_FOUND) {
     nsCOMPtr<nsIFile> overrideLF;
     rv = XRE_GetFileFromPath(override, getter_AddRefs(overrideLF));
     if (NS_FAILED(rv)) {
       Output(true, "Error: unrecognized override.ini path.\n");
       return 1;
@@ -3215,17 +3215,17 @@ XREMain::XRE_mainInit(bool* aExitFlag)
   if (EnvHasValue("MOZ_SAFE_MODE_RESTART")) {
     gSafeMode = true;
     // unset the env variable
     SaveToEnv("MOZ_SAFE_MODE_RESTART=");
   }
 
   ar = CheckArg("safe-mode", true);
   if (ar == ARG_BAD) {
-    PR_fprintf(PR_STDERR, "Error: argument -safe-mode is invalid when argument -osint is specified\n");
+    PR_fprintf(PR_STDERR, "Error: argument --safe-mode is invalid when argument --osint is specified\n");
     return 1;
   } else if (ar == ARG_FOUND) {
     gSafeMode = true;
   }
 
 #ifdef XP_WIN
   // If the shift key is pressed and the ctrl and / or alt keys are not pressed
   // during startup start in safe mode. GetKeyState returns a short and the high
@@ -3238,36 +3238,36 @@ XREMain::XRE_mainInit(bool* aExitFlag)
   }
 #endif
 
 #ifdef XP_MACOSX
   if (GetCurrentEventKeyModifiers() & optionKey)
     gSafeMode = true;
 #endif
 
-  // Handle -no-remote and -new-instance command line arguments. Setup
+  // Handle --no-remote and --new-instance command line arguments. Setup
   // the environment to better accommodate other components and various
   // restart scenarios.
   ar = CheckArg("no-remote", true);
   if (ar == ARG_BAD) {
-    PR_fprintf(PR_STDERR, "Error: argument -no-remote is invalid when argument -osint is specified\n");
+    PR_fprintf(PR_STDERR, "Error: argument --no-remote is invalid when argument --osint is specified\n");
     return 1;
   } else if (ar == ARG_FOUND) {
     SaveToEnv("MOZ_NO_REMOTE=1");
   }
 
   ar = CheckArg("new-instance", true);
   if (ar == ARG_BAD) {
-    PR_fprintf(PR_STDERR, "Error: argument -new-instance is invalid when argument -osint is specified\n");
+    PR_fprintf(PR_STDERR, "Error: argument --new-instance is invalid when argument --osint is specified\n");
     return 1;
   } else if (ar == ARG_FOUND) {
     SaveToEnv("MOZ_NEW_INSTANCE=1");
   }
 
-  // Handle -help and -version command line arguments.
+  // Handle --help and --version command line arguments.
   // They should return quickly, so we deal with them here.
   if (CheckArg("h") || CheckArg("help") || CheckArg("?")) {
     DumpHelp();
     *aExitFlag = true;
     return 0;
   }
 
   if (CheckArg("v") || CheckArg("version")) {
@@ -3278,20 +3278,20 @@ XREMain::XRE_mainInit(bool* aExitFlag)
     
 #ifdef NS_TRACE_MALLOC
   gArgc = NS_TraceMallocStartupArgs(gArgc, gArgv);
 #endif
 
   rv = XRE_InitCommandLine(gArgc, gArgv);
   NS_ENSURE_SUCCESS(rv, 1);
 
-  // Check for -register, which registers chrome and then exits immediately.
+  // Check for --register, which registers chrome and then exits immediately.
   ar = CheckArg("register", true);
   if (ar == ARG_BAD) {
-    PR_fprintf(PR_STDERR, "Error: argument -register is invalid when argument -osint is specified\n");
+    PR_fprintf(PR_STDERR, "Error: argument --register is invalid when argument --osint is specified\n");
     return 1;
   } else if (ar == ARG_FOUND) {
     ScopedXPCOMStartup xpcom;
     rv = xpcom.Initialize();
     NS_ENSURE_SUCCESS(rv, 1);
     {
       nsCOMPtr<nsIChromeRegistry> chromeReg =
         mozilla::services::GetChromeRegistryService();
@@ -3532,17 +3532,17 @@ XREMain::XRE_mainStartup(bool* aExitFlag
     if (!display_name) {
       PR_fprintf(PR_STDERR, "Error: no display specified\n");
       return 1;
     }
   }
 #endif /* MOZ_WIDGET_GTK */
 
 #ifdef MOZ_ENABLE_XREMOTE
-  // handle -remote now that xpcom is fired up
+  // handle --remote now that xpcom is fired up
   bool newInstance;
   {
     char *e = PR_GetEnv("MOZ_NO_REMOTE");
     mDisableRemote = (e && *e);
     if (mDisableRemote) {
       newInstance = true;
     } else {
       e = PR_GetEnv("MOZ_NEW_INSTANCE");
@@ -4449,41 +4449,41 @@ XRE_InitCommandLine(int aArgc, char* aAr
   for (int i = 0; i < aArgc; ++i)
       free(canonArgs[i]);
   delete[] canonArgs;
 #endif
 
   const char *path = nullptr;
   ArgResult ar = CheckArg("greomni", false, &path);
   if (ar == ARG_BAD) {
-    PR_fprintf(PR_STDERR, "Error: argument -greomni requires a path argument\n");
+    PR_fprintf(PR_STDERR, "Error: argument --greomni requires a path argument\n");
     return NS_ERROR_FAILURE;
   }
 
   if (!path)
     return rv;
 
   nsCOMPtr<nsIFile> greOmni;
   rv = XRE_GetFileFromPath(path, getter_AddRefs(greOmni));
   if (NS_FAILED(rv)) {
-    PR_fprintf(PR_STDERR, "Error: argument -greomni requires a valid path\n");
+    PR_fprintf(PR_STDERR, "Error: argument --greomni requires a valid path\n");
     return rv;
   }
 
   ar = CheckArg("appomni", false, &path);
   if (ar == ARG_BAD) {
-    PR_fprintf(PR_STDERR, "Error: argument -appomni requires a path argument\n");
+    PR_fprintf(PR_STDERR, "Error: argument --appomni requires a path argument\n");
     return NS_ERROR_FAILURE;
   }
 
   nsCOMPtr<nsIFile> appOmni;
   if (path) {
       rv = XRE_GetFileFromPath(path, getter_AddRefs(appOmni));
       if (NS_FAILED(rv)) {
-        PR_fprintf(PR_STDERR, "Error: argument -appomni requires a valid path\n");
+        PR_fprintf(PR_STDERR, "Error: argument --appomni requires a valid path\n");
         return rv;
       }
   }
 
   mozilla::Omnijar::Init(greOmni, appOmni);
   return rv;
 }
 
--- a/toolkit/xre/nsNativeAppSupportWin.cpp
+++ b/toolkit/xre/nsNativeAppSupportWin.cpp
@@ -246,22 +246,22 @@ private:
  *    WWW_OpenURL topic and the params as specified in the ifexec registry key
  *    for the verb (e.g. open).
  *
  * Application DDE Sequence:
  * 1. If the application is running a DDE request is received with the
  *    WWW_OpenURL topic and the params as specified in the default value of the
  *    ddeexec registry key (e.g. "%1",,0,0,,,, where '%1' is the url to open)
  *    for the verb (e.g. open).
- * 2. If the application is not running it is launched with the -requestPending
- *    and the -url argument.
- * 2.1  If the application does not need to restart and the -requestPending
+ * 2. If the application is not running it is launched with the --requestPending
+ *    and the --url argument.
+ * 2.1  If the application does not need to restart and the --requestPending
  *      argument is present the accompanying url will not be used. Instead the
  *      application will wait for the DDE message to open the url.
- * 2.2  If the application needs to restart the -requestPending argument is
+ * 2.2  If the application needs to restart the --requestPending argument is
  *      removed from the arguments used to restart the application and the url
  *      will be handled normally.
  *
  * Note: Due to a bug in IE the ifexec key should not be used (see bug 355650).
  */
 
 class nsNativeAppSupportWin : public nsNativeAppSupportBase,
                               public nsIObserver
@@ -273,17 +273,17 @@ public:
     // Overrides of base implementation.
     NS_IMETHOD Start( bool *aResult );
     NS_IMETHOD Stop( bool *aResult );
     NS_IMETHOD Quit();
     NS_IMETHOD Enable();
     // The "old" Start method (renamed).
     NS_IMETHOD StartDDE();
     // Utility function to handle a Win32-specific command line
-    // option: "-console", which dynamically creates a Windows
+    // option: "--console", which dynamically creates a Windows
     // console.
     void CheckConsole();
 
 private:
     ~nsNativeAppSupportWin() {}
     static void HandleCommandLine(const char* aCmdLineString, nsIFile* aWorkingDir, uint32_t aState);
     static HDDEDATA CALLBACK HandleDDENotification( UINT     uType,
                                                     UINT     uFmt,
@@ -329,18 +329,18 @@ NS_INTERFACE_MAP_BEGIN(nsNativeAppSuppor
 NS_INTERFACE_MAP_END_INHERITING(nsNativeAppSupportBase)
 
 NS_IMPL_ADDREF_INHERITED(nsNativeAppSupportWin, nsNativeAppSupportBase)
 NS_IMPL_RELEASE_INHERITED(nsNativeAppSupportWin, nsNativeAppSupportBase)
 
 void
 nsNativeAppSupportWin::CheckConsole() {
     for ( int i = 1; i < gArgc; i++ ) {
-        if ( strcmp( "-console", gArgv[i] ) == 0
-             ||
+        if ( strcmp( "-console", gArgv[i] ) == 0 ||
+             strcmp( "--console", gArgv[i] ) == 0 ||
              strcmp( "/console", gArgv[i] ) == 0 ) {
             // Users wants to make sure we have a console.
             // Try to allocate one.
             BOOL rc = ::AllocConsole();
             if ( rc ) {
                 // Console allocated.  Fix it up so that output works in
                 // all cases.  See http://support.microsoft.com/support/kb/articles/q105/3/05.asp.
 
--- a/webapprt/gtk/webapprt.cpp
+++ b/webapprt/gtk/webapprt.cpp
@@ -322,20 +322,20 @@ int main(int argc, char *argv[])
     ErrorDialog("Couldn't read current executable path");
     return 255;
   }
   char curExeDir[MAXPATHLEN];
   GetDirFromPath(curExeDir, curExePath);
 
   bool removeApp = false;
   for (int i = 1; i < argc; i++) {
-    if (!strcmp(argv[i], "-profile")) {
+    if (!strcmp(argv[i], "-profile") || !strcmp(argv[i], "--profile")) {
       isProfileOverridden = true;
     }
-    else if (!strcmp(argv[i], "-remove")) {
+    else if (!strcmp(argv[i], "-remove") || !strcmp(argv[i], "--remove")) {
       removeApp = true;
     }
   }
 
   char firefoxDir[MAXPATHLEN];
 
   // Check if Firefox is in the same directory as the webapp runtime.
   // This is the case for webapprt chrome and content tests.
--- a/webapprt/mac/webapprt.mm
+++ b/webapprt/mac/webapprt.mm
@@ -254,17 +254,17 @@ main(int argc, char **argv)
         nsXREAppData *webShellAppData;
         if (NS_FAILED(XRE_CreateAppData(rtINI, &webShellAppData))) {
           NSLog(@"Couldn't read WebappRT application.ini: %s", rtINIPath);
           @throw MakeException(@"Error", @"Unable to parse base INI file.");
         }
 
         NSString *profile = [args objectForKey:@"profile"];
         if (profile) {
-          NSLog(@"Profile specified with -profile: %@", profile);
+          NSLog(@"Profile specified with --profile: %@", profile);
         }
         else {
           nsINIParser parser;
           if (NS_FAILED(parser.Init(appEnv))) {
             NSLog(@"%s was not found\n", appEnv);
             @throw MakeException(@"Error", @"Unable to parse environment files for application startup");
           }
           char profile[MAXPATHLEN];
--- a/webapprt/win/webapprt.cpp
+++ b/webapprt/win/webapprt.cpp
@@ -439,19 +439,21 @@ main(int argc, char* argv[])
                                buffer,
                                MAXPATHLEN,
                                nullptr,
                                nullptr)) {
     Output("Application directory could not be processed.");
     return 255;
   }
 
-  // Check if the runtime was executed with the "-profile" argument
+  // Check if the runtime was executed with the "--profile" argument
   for (int i = 1; i < argc; i++) {
-    if (!strcmp(argv[i], "-profile")) {
+    if (!strcmp(argv[i], "-profile") ||
+        !strcmp(argv[i], "--profile") ||
+        !strcmp(argv[i], "/profile")) {
       isProfileOverridden = true;
       break;
     }
   }
 
   // First attempt at loading Firefox binaries:
   //   Check if the webapprt is in the same directory as the Firefox binary.
   //   This is the case during WebappRT chrome and content tests.
--- a/widget/gtk/nsNativeThemeGTK.cpp
+++ b/widget/gtk/nsNativeThemeGTK.cpp
@@ -29,16 +29,17 @@
 #include <gtk/gtk.h>
 
 #include "gfxContext.h"
 #include "gfxPlatformGtk.h"
 #include "gfxGdkNativeRenderer.h"
 #include <algorithm>
 
 using namespace mozilla;
+using namespace mozilla::gfx;
 
 NS_IMPL_ISUPPORTS_INHERITED(nsNativeThemeGTK, nsNativeTheme, nsITheme,
                                                              nsIObserver)
 
 static int gLastGdkError;
 
 nsNativeThemeGTK::nsNativeThemeGTK()
 {
@@ -875,20 +876,20 @@ nsNativeThemeGTK::DrawWidgetBackground(n
   }
 
   // GtkStyles (used by the widget drawing backend) are created for a
   // particular colormap/visual.
   GdkColormap* colormap = moz_gtk_widget_get_colormap();
 
   renderer.Draw(ctx, drawingRect.Size(), rendererFlags, colormap);
 #else 
-  cairo_t *ctx =
-    (cairo_t*)aDrawTarget->GetNativeSurface(NativeSurfaceType::CAIRO_CONTEXT); 
-  MOZ_ASSERT(ctx);
-  moz_gtk_widget_paint(gtkWidgetType, ctx, &gdk_rect, 
+  cairo_t *cairo_ctx =
+    (cairo_t*)aDrawTarget.GetNativeSurface(NativeSurfaceType::CAIRO_CONTEXT); 
+  MOZ_ASSERT(cairo_ctx);
+  moz_gtk_widget_paint(gtkWidgetType, cairo_ctx, &gdk_rect, 
                        &state, flags, direction);
 #endif
 
   if (!safeState) {
     gdk_flush();
     gLastGdkError = gdk_error_trap_pop ();
 
     if (gLastGdkError) {
--- a/widget/windows/winrt/MetroContracts.cpp
+++ b/widget/windows/winrt/MetroContracts.cpp
@@ -92,23 +92,26 @@ FrameworkView::LaunchActivated(ComPtr<IL
 {
   if (!aArgs)
     return;
   HString data;
   AssertHRESULT(aArgs->get_Arguments(data.GetAddressOf()));
   if (WindowsIsStringEmpty(data.Get()))
     return;
 
-  // If we're being launched from a secondary tile then we have a 2nd command line param of -url
+  // If we're being launched from a secondary tile then we have a 2nd command line param of --url
   // and a third of the secondary tile.  We want it in sActivationURI so that browser.js will
   // load it in without showing the start UI.
   int argc;
   unsigned int length;
   LPWSTR* argv = CommandLineToArgvW(data.GetRawBuffer(&length), &argc);
-  if (aStartup && argc == 2 && !wcsicmp(argv[0], L"-url")) {
+  if (aStartup && argc == 2 &&
+      (!wcsicmp(argv[0], L"-url") ||
+       !wcsicmp(argv[0], L"--url") ||
+       !wcsicmp(argv[0], L"/url"))) {
     WindowsCreateString(argv[1], wcslen(argv[1]), &sActivationURI);
   } else {
     // Some other command line or this is not a startup.
     // If it is startup we process it later when XPCOM is initialilzed.
     mActivationCommandLine = data.GetRawBuffer(&length);
     if (!aStartup) {
       ProcessLaunchArguments();
     }
--- a/xpcom/reflect/xptcall/md/unix/moz.build
+++ b/xpcom/reflect/xptcall/md/unix/moz.build
@@ -14,24 +14,24 @@ if CONFIG['OS_ARCH'] == 'Darwin':
             'xptcinvoke_asm_ppc_rhapsody.s',
         ]
         GENERATED_SOURCES += [
             'xptcstubs_asm_ppc_darwin.s',
         ]
     if '86' in CONFIG['OS_TEST'] and CONFIG['OS_TEST'] != 'x86_64':
         DEFINES['MOZ_NEED_LEADING_UNDERSCORE'] = True
 
-if CONFIG['OS_ARCH'] in ('NetBSD', 'OpenBSD', 'GNU'):
+if CONFIG['OS_ARCH'] in ('NetBSD', 'GNU'):
     if CONFIG['CPU_ARCH'] == 'x86':
         SOURCES += [
             'xptcinvoke_gcc_x86_unix.cpp',
             'xptcstubs_gcc_x86_unix.cpp'
         ]
 
-if CONFIG['OS_ARCH'] in ('Linux', 'FreeBSD') or \
+if CONFIG['OS_ARCH'] in ('Linux', 'FreeBSD', 'OpenBSD') or \
    CONFIG['OS_ARCH'].startswith('GNU_'):
     if CONFIG['OS_TEST'] == 'x86_64':
         SOURCES += [
             'xptcinvoke_x86_64_unix.cpp',
             'xptcstubs_x86_64_linux.cpp',
         ]
     elif CONFIG['OS_TEST'].find('86') != -1:
         SOURCES += [
@@ -43,22 +43,16 @@ if CONFIG['OS_ARCH'] in ('Linux', 'FreeB
     if CONFIG['OS_TEST'].find('ia64') != -1:
         SOURCES += [
             'xptcinvoke_asm_ipf64.s',
             'xptcinvoke_ipf64.cpp',
             'xptcstubs_asm_ipf64.s',
             'xptcstubs_ipf64.cpp'
         ]
 
-if CONFIG['OS_ARCH'] == 'OpenBSD' and CONFIG['OS_TEST'] == 'x86_64':
-    SOURCES += [
-        'xptcinvoke_amd64_openbsd.cpp',
-        'xptcstubs_amd64_openbsd.cpp'
-    ]
-
 if CONFIG['OS_ARCH'] == 'SunOS':
     if CONFIG['OS_TEST'] == 'x86_64':
         if CONFIG['GNU_CC']:
             SOURCES += [
                 'xptcinvoke_x86_64_unix.cpp',
                 'xptcstubs_x86_64_linux.cpp'
             ]
         else:
deleted file mode 100644
--- a/xpcom/reflect/xptcall/md/unix/xptcinvoke_amd64_openbsd.cpp
+++ /dev/null
@@ -1,178 +0,0 @@
-/* -*- 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/. */
-
-// Platform specific code to invoke XPCOM methods on native objects
-
-#include "xptcprivate.h"
-
-// 6 integral parameters are passed in registers
-const uint32_t GPR_COUNT = 6;
-
-// 8 floating point parameters are passed in SSE registers
-const uint32_t FPR_COUNT = 8;
-
-// Remember that these 'words' are 64-bit long
-static inline void
-invoke_count_words(uint32_t paramCount, nsXPTCVariant * s,
-                   uint32_t & nr_gpr, uint32_t & nr_fpr, uint32_t & nr_stack)
-{
-    nr_gpr = 1; // skip one GP register for 'that'
-    nr_fpr = 0;
-    nr_stack = 0;
-
-    /* Compute number of eightbytes of class MEMORY.  */
-    for (uint32_t i = 0; i < paramCount; i++, s++) {
-        if (!s->IsPtrData()
-            && (s->type == nsXPTType::T_FLOAT || s->type == nsXPTType::T_DOUBLE)) {
-            if (nr_fpr < FPR_COUNT)
-                nr_fpr++;
-            else
-                nr_stack++;
-        }
-        else {
-            if (nr_gpr < GPR_COUNT)
-                nr_gpr++;
-            else
-                nr_stack++;
-        }
-    }
-}
-
-static void
-invoke_copy_to_stack(uint64_t * d, uint32_t paramCount, nsXPTCVariant * s,
-                     uint64_t * gpregs, double * fpregs)
-{
-    uint32_t nr_gpr = 1; // skip one GP register for 'that'
-    uint32_t nr_fpr = 0;
-    uint64_t value;
-
-    for (uint32_t i = 0; i < paramCount; i++, s++) {
-        if (s->IsPtrData())
-            value = (uint64_t) s->ptr;
-        else {
-            switch (s->type) {
-            case nsXPTType::T_FLOAT:                                break;
-            case nsXPTType::T_DOUBLE:                               break;
-            case nsXPTType::T_I8:     value = s->val.i8;            break;
-            case nsXPTType::T_I16:    value = s->val.i16;           break;
-            case nsXPTType::T_I32:    value = s->val.i32;           break;
-            case nsXPTType::T_I64:    value = s->val.i64;           break;
-            case nsXPTType::T_U8:     value = s->val.u8;            break;
-            case nsXPTType::T_U16:    value = s->val.u16;           break;
-            case nsXPTType::T_U32:    value = s->val.u32;           break;
-            case nsXPTType::T_U64:    value = s->val.u64;           break;
-            case nsXPTType::T_BOOL:   value = s->val.b;             break;
-            case nsXPTType::T_CHAR:   value = s->val.c;             break;
-            case nsXPTType::T_WCHAR:  value = s->val.wc;            break;
-            default:                  value = (uint64_t) s->val.p;  break;
-            }
-        }
-
-        if (!s->IsPtrData() && s->type == nsXPTType::T_DOUBLE) {
-            if (nr_fpr < FPR_COUNT)
-                fpregs[nr_fpr++] = s->val.d;
-            else {
-                *((double *)d) = s->val.d;
-                d++;
-            }
-        }
-        else if (!s->IsPtrData() && s->type == nsXPTType::T_FLOAT) {
-            if (nr_fpr < FPR_COUNT)
-                // The value in %xmm register is already prepared to
-                // be retrieved as a float. Therefore, we pass the
-                // value verbatim, as a double without conversion.
-                fpregs[nr_fpr++] = s->val.d;
-            else {
-                *((float *)d) = s->val.f;
-                d++;
-            }
-        }
-        else {
-            if (nr_gpr < GPR_COUNT)
-                gpregs[nr_gpr++] = value;
-            else
-                *d++ = value;
-        }
-    }
-}
-
-EXPORT_XPCOM_API(nsresult)
-NS_InvokeByIndex(nsISupports * that, uint32_t methodIndex,
-                 uint32_t paramCount, nsXPTCVariant * params)
-{
-    uint32_t nr_gpr, nr_fpr, nr_stack;
-    invoke_count_words(paramCount, params, nr_gpr, nr_fpr, nr_stack);
-    
-    // Stack, if used, must be 16-bytes aligned
-    if (nr_stack)
-        nr_stack = (nr_stack + 1) & ~1;
-
-    // Load parameters to stack, if necessary
-    uint64_t *stack = (uint64_t *) __builtin_alloca(nr_stack * 8);
-    uint64_t gpregs[GPR_COUNT];
-    double fpregs[FPR_COUNT];
-    invoke_copy_to_stack(stack, paramCount, params, gpregs, fpregs);
-
-    // Load FPR registers from fpregs[]
-    register double d0 asm("xmm0");
-    register double d1 asm("xmm1");
-    register double d2 asm("xmm2");
-    register double d3 asm("xmm3");
-    register double d4 asm("xmm4");
-    register double d5 asm("xmm5");
-    register double d6 asm("xmm6");
-    register double d7 asm("xmm7");
-
-    switch (nr_fpr) {
-#define ARG_FPR(N) \
-    case N+1: d##N = fpregs[N];
-        ARG_FPR(7);
-        ARG_FPR(6);
-        ARG_FPR(5);
-        ARG_FPR(4);
-        ARG_FPR(3);
-        ARG_FPR(2);
-        ARG_FPR(1);
-        ARG_FPR(0);
-    case 0:;
-#undef ARG_FPR
-    }
-    
-    // Load GPR registers from gpregs[]
-    register uint64_t a0 asm("rdi");
-    register uint64_t a1 asm("rsi");
-    register uint64_t a2 asm("rdx");
-    register uint64_t a3 asm("rcx");
-    register uint64_t a4 asm("r8");
-    register uint64_t a5 asm("r9");
-    
-    switch (nr_gpr) {
-#define ARG_GPR(N) \
-    case N+1: a##N = gpregs[N];
-        ARG_GPR(5);
-        ARG_GPR(4);
-        ARG_GPR(3);
-        ARG_GPR(2);
-        ARG_GPR(1);
-    case 1: a0 = (uint64_t) that;
-    case 0:;
-#undef ARG_GPR
-    }
-
-    // Ensure that assignments to SSE registers won't be optimized away
-    asm("" ::
-        "x" (d0), "x" (d1), "x" (d2), "x" (d3),
-        "x" (d4), "x" (d5), "x" (d6), "x" (d7));
-    
-    // Get pointer to method
-    uint64_t methodAddress = *((uint64_t *)that);
-    methodAddress += 8 * methodIndex;
-    methodAddress = *((uint64_t *)methodAddress);
-    
-    typedef nsresult (*Method)(uint64_t, uint64_t, uint64_t, uint64_t, uint64_t, uint64_t);
-    nsresult result = ((Method)methodAddress)(a0, a1, a2, a3, a4, a5);
-    return result;
-}
deleted file mode 100644
--- a/xpcom/reflect/xptcall/md/unix/xptcstubs_amd64_openbsd.cpp
+++ /dev/null
@@ -1,201 +0,0 @@
-/* -*- 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/. */
-
-// Implement shared vtbl methods.
-
-#include "xptcprivate.h"
-#include "xptiprivate.h"
-
-// The Linux/x86-64 ABI passes the first 6 integer parameters and the
-// first 8 floating point parameters in registers (rdi, rsi, rdx, rcx,
-// r8, r9 and xmm0-xmm7), no stack space is allocated for these by the
-// caller.  The rest of the parameters are passed in the callers stack
-// area.
-
-const uint32_t PARAM_BUFFER_COUNT   = 16;
-const uint32_t GPR_COUNT            = 6;
-const uint32_t FPR_COUNT            = 8;
-
-// PrepareAndDispatch() is called by SharedStub() and calls the actual method.
-//
-// - 'args[]' contains the arguments passed on stack
-// - 'gpregs[]' contains the arguments passed in integer registers
-// - 'fpregs[]' contains the arguments passed in floating point registers
-// 
-// The parameters are mapped into an array of type 'nsXPTCMiniVariant'
-// and then the method gets called.
-
-extern "C" nsresult ATTRIBUTE_USED
-PrepareAndDispatch(nsXPTCStubBase * self, uint32_t methodIndex,
-                   uint64_t * args, uint64_t * gpregs, double *fpregs)
-{
-    nsXPTCMiniVariant paramBuffer[PARAM_BUFFER_COUNT];
-    nsXPTCMiniVariant* dispatchParams = nullptr;
-    const nsXPTMethodInfo* info;
-    uint32_t paramCount;
-    uint32_t i;
-    nsresult result = NS_ERROR_FAILURE;
-
-    NS_ASSERTION(self,"no self");
-
-    self->mEntry->GetMethodInfo(uint16_t(methodIndex), &info);
-    NS_ASSERTION(info,"no method info");
-    if (!info)
-        return NS_ERROR_UNEXPECTED;
-
-    paramCount = info->GetParamCount();
-
-    // setup variant array pointer
-    if (paramCount > PARAM_BUFFER_COUNT)
-        dispatchParams = new nsXPTCMiniVariant[paramCount];
-    else
-        dispatchParams = paramBuffer;
-
-    NS_ASSERTION(dispatchParams,"no place for params");
-    if (!dispatchParams)
-        return NS_ERROR_OUT_OF_MEMORY;
-
-    uint64_t* ap = args;
-    uint32_t nr_gpr = 1;    // skip one GPR register for 'that'
-    uint32_t nr_fpr = 0;
-    uint64_t value;
-
-    for (i = 0; i < paramCount; i++) {
-        const nsXPTParamInfo& param = info->GetParam(i);
-        const nsXPTType& type = param.GetType();
-        nsXPTCMiniVariant* dp = &dispatchParams[i];
-	
-        if (!param.IsOut() && type == nsXPTType::T_DOUBLE) {
-            if (nr_fpr < FPR_COUNT)
-                dp->val.d = fpregs[nr_fpr++];
-            else
-                dp->val.d = *(double*) ap++;
-            continue;
-        }
-        else if (!param.IsOut() && type == nsXPTType::T_FLOAT) {
-            if (nr_fpr < FPR_COUNT)
-                // The value in %xmm register is already prepared to
-                // be retrieved as a float. Therefore, we pass the
-                // value verbatim, as a double without conversion.
-                dp->val.d = *(double*) ap++;
-            else
-                dp->val.f = *(float*) ap++;
-            continue;
-        }
-        else {
-            if (nr_gpr < GPR_COUNT)
-                value = gpregs[nr_gpr++];
-            else
-                value = *ap++;
-        }
-
-        if (param.IsOut() || !type.IsArithmetic()) {
-            dp->val.p = (void*) value;
-            continue;
-        }
-
-        switch (type) {
-        case nsXPTType::T_I8:      dp->val.i8  = (int8_t)   value; break;
-        case nsXPTType::T_I16:     dp->val.i16 = (int16_t)  value; break;
-        case nsXPTType::T_I32:     dp->val.i32 = (int32_t)  value; break;
-        case nsXPTType::T_I64:     dp->val.i64 = (int64_t)  value; break;
-        case nsXPTType::T_U8:      dp->val.u8  = (uint8_t)  value; break;
-        case nsXPTType::T_U16:     dp->val.u16 = (uint16_t) value; break;
-        case nsXPTType::T_U32:     dp->val.u32 = (uint32_t) value; break;
-        case nsXPTType::T_U64:     dp->val.u64 = (uint64_t) value; break;
-        case nsXPTType::T_BOOL:    dp->val.b   = (bool)   value; break;
-        case nsXPTType::T_CHAR:    dp->val.c   = (char)     value; break;
-        case nsXPTType::T_WCHAR:   dp->val.wc  = (wchar_t)  value; break;
-
-        default:
-            NS_ERROR("bad type");
-            break;
-        }
-    }
-
-    result = self->mOuter->CallMethod((uint16_t) methodIndex, info, dispatchParams);
-
-    if (dispatchParams != paramBuffer)
-        delete [] dispatchParams;
-
-    return result;
-}
-
-// Linux/x86-64 uses gcc >= 3.1
-#define STUB_ENTRY(n) \
-asm(".section	\".text\"\n\t" \
-    ".align	2\n\t" \
-    ".if	" #n " < 10\n\t" \
-    ".globl	_ZN14nsXPTCStubBase5Stub" #n "Ev\n\t" \
-    ".hidden	_ZN14nsXPTCStubBase5Stub" #n "Ev\n\t" \
-    ".type	_ZN14nsXPTCStubBase5Stub" #n "Ev,@function\n" \
-    "_ZN14nsXPTCStubBase5Stub" #n "Ev:\n\t" \
-    ".elseif	" #n " < 100\n\t" \
-    ".globl	_ZN14nsXPTCStubBase6Stub" #n "Ev\n\t" \
-    ".hidden	_ZN14nsXPTCStubBase6Stub" #n "Ev\n\t" \
-    ".type	_ZN14nsXPTCStubBase6Stub" #n "Ev,@function\n" \
-    "_ZN14nsXPTCStubBase6Stub" #n "Ev:\n\t" \
-    ".elseif    " #n " < 1000\n\t" \
-    ".globl     _ZN14nsXPTCStubBase7Stub" #n "Ev\n\t" \
-    ".hidden    _ZN14nsXPTCStubBase7Stub" #n "Ev\n\t" \
-    ".type      _ZN14nsXPTCStubBase7Stub" #n "Ev,@function\n" \
-    "_ZN14nsXPTCStubBase7Stub" #n "Ev:\n\t" \
-    ".else\n\t" \
-    ".err	\"stub number " #n " >= 1000 not yet supported\"\n\t" \
-    ".endif\n\t" \
-    "movl	$" #n ", %eax\n\t" \
-    "jmp	SharedStub\n\t" \
-    ".if	" #n " < 10\n\t" \
-    ".size	_ZN14nsXPTCStubBase5Stub" #n "Ev,.-_ZN14nsXPTCStubBase5Stub" #n "Ev\n\t" \
-    ".elseif	" #n " < 100\n\t" \
-    ".size	_ZN14nsXPTCStubBase6Stub" #n "Ev,.-_ZN14nsXPTCStubBase6Stub" #n "Ev\n\t" \
-    ".else\n\t" \
-    ".size	_ZN14nsXPTCStubBase7Stub" #n "Ev,.-_ZN14nsXPTCStubBase7Stub" #n "Ev\n\t" \
-    ".endif");
-
-// static nsresult SharedStub(uint32_t methodIndex)
-asm(".section   \".text\"\n\t"
-    ".align     2\n\t"
-    ".type      SharedStub,@function\n\t"
-    "SharedStub:\n\t"
-    // make room for gpregs (48), fpregs (64)
-    "pushq      %rbp\n\t"
-    "movq       %rsp,%rbp\n\t"
-    "subq       $112,%rsp\n\t"
-    // save GP registers
-    "movq       %rdi,-112(%rbp)\n\t"
-    "movq       %rsi,-104(%rbp)\n\t"
-    "movq       %rdx, -96(%rbp)\n\t"
-    "movq       %rcx, -88(%rbp)\n\t"
-    "movq       %r8 , -80(%rbp)\n\t"
-    "movq       %r9 , -72(%rbp)\n\t"
-    "leaq       -112(%rbp),%rcx\n\t"
-    // save FP registers
-    "movsd      %xmm0,-64(%rbp)\n\t"
-    "movsd      %xmm1,-56(%rbp)\n\t"
-    "movsd      %xmm2,-48(%rbp)\n\t"
-    "movsd      %xmm3,-40(%rbp)\n\t"
-    "movsd      %xmm4,-32(%rbp)\n\t"
-    "movsd      %xmm5,-24(%rbp)\n\t"
-    "movsd      %xmm6,-16(%rbp)\n\t"
-    "movsd      %xmm7, -8(%rbp)\n\t"
-    "leaq       -64(%rbp),%r8\n\t"
-    // rdi has the 'self' pointer already
-    "movl       %eax,%esi\n\t"
-    "leaq       16(%rbp),%rdx\n\t"
-    "call       PrepareAndDispatch@plt\n\t"
-    "leave\n\t"
-    "ret\n\t"
-    ".size      SharedStub,.-SharedStub");
-
-#define SENTINEL_ENTRY(n) \
-nsresult nsXPTCStubBase::Sentinel##n() \
-{ \
-    NS_ERROR("nsXPTCStubBase::Sentinel called"); \
-    return NS_ERROR_NOT_IMPLEMENTED; \
-}
-
-#include "xptcstubsdef.inc"
--- a/xpfe/test/winopen.js
+++ b/xpfe/test/winopen.js
@@ -31,20 +31,20 @@ const options = [ [ "phase1", "PHASE_ONE
                   [ "phase3", "PHASE_THREE", false ],
                   [ "overlap", "OVERLAP_COUNT", false ],
                   [ "cycles", "CYCLES", false ],
                   [ "chrome", "KID_CHROME", true ],
                   [ "close", "AUTOCLOSE", false ] ];
 
 // Note: You can attach search options to the url for this file to control
 // any of the options in the array above.  E.g., specifying
-// mozilla -chrome "file:///D|/mozilla/xpfe/test/winopen.xul?phase1=16&close=0"
+// mozilla --chrome "file:///D|/mozilla/xpfe/test/winopen.xul?phase1=16&close=0"
 // will run this script with PHASE_ONE=16 and AUTOCLOSE=0.
 //
-// On Win32, you must enclose the -chrome option in quotes in order pass funny Win32 shell
+// On Win32, you must enclose the --chrome option in quotes in order pass funny Win32 shell
 // characters such as '&' or '|'!
 
 var opts = window.location.search.substring(1).split( '&' );
 for ( opt in opts ) {
     for ( var i in options ) {
         if ( opts[opt].indexOf( options[i][0]+"=" ) == 0 ) {
             var newVal = opts[opt].split( '=' )[ 1 ];
             // wrap with quotes, if required.