Merge inbound to m-c.
authorRyan VanderMeulen <ryanvm@gmail.com>
Fri, 17 May 2013 21:41:13 -0400
changeset 143780 6e2789a70f6b773028722b89953ac3b519384ab1
parent 143743 6cc55ddd1ae15a8e337a3235817776e5250bcf09 (current diff)
parent 143779 3f1b8af8ff14404fe5bf4e6f5f393f832d8c7d75 (diff)
child 143781 8ca260fe91e31a2b19d5ed90079db3a4d71d1c82
child 143785 e4d4cbfcbfeacb36a41481302b9f6390795b9576
push id2697
push userbbajaj@mozilla.com
push dateMon, 05 Aug 2013 18:49:53 +0000
treeherdermozilla-beta@dfec938c7b63 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone24.0a1
first release with
nightly linux32
6e2789a70f6b / 24.0a1 / 20130518031114 / files
nightly linux64
6e2789a70f6b / 24.0a1 / 20130518031114 / files
nightly mac
6e2789a70f6b / 24.0a1 / 20130518031114 / files
nightly win32
6e2789a70f6b / 24.0a1 / 20130518031114 / files
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
Merge inbound to m-c.
js/src/jsnuminlines.h
layout/reftests/forms/input-file-width-clip-1.html
layout/reftests/forms/input-file-width-clip-ref.html
--- a/CLOBBER
+++ b/CLOBBER
@@ -12,9 +12,12 @@
 #          O               O
 #          |               |
 #          O <-- Clobber   O  <-- Clobber
 #
 # Note: The description below will be part of the error message shown to users.
 #
 # Modifying this file will now automatically clobber the buildbot machines \o/
 #
-Bug 852687 - changing an idl without clobbering resulted test failures
+Bug 848530 - Added a dependency file for moz.build traversal.
+
+Alternative to clobber is to run ./config.status from the objdir and to
+touch the CLOBBER file in the objdir.
--- a/Makefile.in
+++ b/Makefile.in
@@ -12,16 +12,18 @@ ifndef .PYMAKE
 ifeq (,$(MAKE_VERSION))
 $(error GNU Make is required)
 endif
 ifeq (,$(filter-out 3.78 3.79,$(MAKE_VERSION)))
 $(error GNU Make 3.80 or higher is required)
 endif
 endif
 
+export TOPLEVEL_BUILD := 1
+
 include $(DEPTH)/config/autoconf.mk
 
 default::
 
 ifdef COMPILE_ENVIRONMENT
 include $(topsrcdir)/$(MOZ_BUILD_APP)/build.mk
 endif
 
@@ -31,17 +33,17 @@ include $(topsrcdir)/config/config.mk
 GARBAGE_DIRS += dist _javagen _profile _tests staticlib
 DIST_GARBAGE = config.cache config.log config.status* config-defs.h \
    config/autoconf.mk \
    unallmakefiles mozilla-config.h \
    netwerk/necko-config.h xpcom/xpcom-config.h xpcom/xpcom-private.h \
    $(topsrcdir)/.mozconfig.mk $(topsrcdir)/.mozconfig.out
 
 ifndef MOZ_PROFILE_USE
-default alldep all:: CLOBBER $(topsrcdir)/configure config.status
+default alldep all:: CLOBBER $(topsrcdir)/configure config.status backend.RecursiveMakeBackend.built
 	$(RM) -r $(DIST)/sdk
 	$(RM) -r $(DIST)/include
 	$(RM) -r $(DIST)/private
 	$(RM) -r $(DIST)/public
 	$(RM) -r $(DIST)/bin
 	$(RM) -r _tests
 endif
 
@@ -66,16 +68,28 @@ config.status: $(topsrcdir)/configure
 	@exit 1
 
 # Build pseudo-external modules first when export is explicitly called
 export::
 	$(RM) -r $(DIST)/sdk
 	$(MAKE) -C config export
 	$(MAKE) tier_nspr
 
+backend.RecursiveMakeBackend.built:
+	@echo "Updating build backend because of moz.build changes."
+	@$(PYTHON) ./config.status
+
+ifdef .PYMAKE
+includedeps backend.RecursiveMakeBackend.built.pp
+else
+include backend.RecursiveMakeBackend.built.pp
+endif
+
+export MOZBUILD_BACKEND_CHECKED=1
+
 ifdef ENABLE_TESTS
 # Additional makefile targets to call automated test suites
 include $(topsrcdir)/testing/testsuite-targets.mk
 endif
 
 include $(topsrcdir)/config/rules.mk
 
 distclean::
--- a/build/automationutils.py
+++ b/build/automationutils.py
@@ -25,16 +25,21 @@ from urlparse import urlparse
 DEBUGGER_INFO = {
   # gdb requires that you supply the '--args' flag in order to pass arguments
   # after the executable name to the executable.
   "gdb": {
     "interactive": True,
     "args": "-q --args"
   },
 
+  "lldb": {
+    "interactive": True,
+    "args": "--"
+  },
+
   # valgrind doesn't explain much about leaks unless you set the
   # '--leak-check=full' flag.
   "valgrind": {
     "interactive": False,
     "args": "--leak-check=full"
   }
 }
 
new file mode 100644
--- /dev/null
+++ b/config/makefiles/nonrecursive.mk
@@ -0,0 +1,62 @@
+# -*- makefile -*-
+# vim:set ts=8 sw=8 sts=8 noet:
+#
+# 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/.
+
+# The purpose of this file is to pull in non-recursive targets when performing
+# a partial tree (not top-level) build. This will allow people to continue to
+# build individual directories while some of the targets may not be normally
+# defined in that make file.
+#
+# Non-recursive targets are attached to existing make targets. The
+# NONRECURSIVE_TARGETS variable lists the make targets that modified. For
+# each target in this list, the NONRECURSIVE_TARGET_<target> variable will
+# contain a list of partial variable names. We will then look in variables
+# named NONRECURSIVE_TARGETS_<target>_<fragment>_* for information describing
+# how to evaluate non-recursive make targets.
+#
+# Targets are defined by the following variables:
+#
+#   FILE - The make file to evaluate.
+#   TARGETS - Targets to evaluate in that make file.
+#
+# For example:
+#
+# NONRECURSIVE_TARGETS = export libs
+# NONRECURSIVE_TARGETS_export = headers
+# NONRECURSIVE_TARGETS_export_headers_FILE = /path/to/exports.mk
+# NONRECURSIVE_TARGETS_export_headers_TARGETS = $(DIST)/include/foo.h $(DIST)/include/bar.h
+# NONRECURSIVE_TARGETS_libs = cppsrcs
+# NONRECURSIVE_TARGETS_libs_cppsrcs_FILE = /path/to/compilation.mk
+# NONRECURSIVE_TARGETS_libs_cppsrcs_TARGETS = /path/to/foo.o /path/to/bar.o
+#
+# Will get turned into the following:
+#
+# exports::
+#     $(MAKE) -f /path/to/exports.mk $(DIST)/include/foo.h $(DIST)/include/bar.h
+#
+# libs::
+#     $(MAKE) -f /path/to/compilation.mk /path/to/foo.o /path/to/bar.o
+
+ifndef INCLUDED_NONRECURSIVE_MK
+
+define define_nonrecursive_target
+$(1)::
+	cd $$(DEPTH) && $$(MAKE) -f $(2) $(3)
+endef
+
+$(foreach target,$(NONRECURSIVE_TARGETS), \
+    $(foreach entry,$(NONRECURSIVE_TARGETS_$(target)), \
+        $(eval $(call define_nonrecursive_target, \
+            $(target), \
+            $(NONRECURSIVE_TARGETS_$(target)_$(entry)_FILE), \
+            $(NONRECURSIVE_TARGETS_$(target)_$(entry)_TARGETS) \
+        )) \
+    ) \
+)
+
+INCLUDED_NONRECURSIVE_MK := 1
+endif
+
--- a/config/rules.mk
+++ b/config/rules.mk
@@ -606,16 +606,27 @@ endif
 
 ifeq (,$(CROSS_COMPILE))
 HOST_OUTOPTION = $(OUTOPTION)
 else
 HOST_OUTOPTION = -o # eol
 endif
 ################################################################################
 
+# Regenerate the build backend if it is out of date. We only check this once
+# per traversal, hence the ifdef and the export. This rule needs to come before
+# other rules for the default target or else it may not run in time.
+ifndef MOZBUILD_BACKEND_CHECKED
+default::
+	$(MAKE) -C $(DEPTH) backend.RecursiveMakeBackend.built
+
+export MOZBUILD_BACKEND_CHECKED=1
+endif
+
+
 # SUBMAKEFILES: List of Makefiles for next level down.
 #   This is used to update or create the Makefiles before invoking them.
 SUBMAKEFILES += $(addsuffix /Makefile, $(DIRS) $(TOOL_DIRS) $(PARALLEL_DIRS))
 
 # The root makefile doesn't want to do a plain export/libs, because
 # of the tiers and because of libxul. Suppress the default rules in favor
 # of something else. Makefiles which use this var *must* provide a sensible
 # default rule before including rules.mk
@@ -1185,36 +1196,16 @@ endif
 	$(JAR) cf $@ -C $(_JAVA_DIR) .
 
 GARBAGE_DIRS += $(_JAVA_DIR)
 
 ###############################################################################
 # Update Files Managed by Build Backend
 ###############################################################################
 
-ifdef MOZBUILD_DERIVED
-
-# If this Makefile is derived from moz.build files, substitution for all .in
-# files is handled by SUBSTITUTE_FILES. This includes Makefile.in.
-ifneq ($(SUBSTITUTE_FILES),,)
-$(SUBSTITUTE_FILES): % : $(srcdir)/%.in $(DEPTH)/config/autoconf.mk
-	@$(PYTHON) $(DEPTH)/config.status -n --file=$@
-	@$(TOUCH) $@
-endif
-
-# Detect when the backend.mk needs rebuilt. This will cause a full scan and
-# rebuild. While relatively expensive, it should only occur once per recursion.
-ifneq ($(BACKEND_INPUT_FILES),,)
-backend.mk: $(BACKEND_INPUT_FILES)
-	@$(PYTHON) $(DEPTH)/config.status -n
-	@$(TOUCH) $@
-endif
-
-endif # MOZBUILD_DERIVED
-
 ifndef NO_MAKEFILE_RULE
 Makefile: Makefile.in
 	@$(PYTHON) $(DEPTH)/config.status -n --file=Makefile
 	@$(TOUCH) $@
 endif
 
 ifndef NO_SUBMAKEFILES_RULE
 ifdef SUBMAKEFILES
@@ -1723,16 +1714,21 @@ endef
     $(eval $(call preprocess_file_template,					\
                   $(file),							\
                   $(or $($(category)_PATH),$(CURDIR))/$(notdir $(file:.in=)),	\
                   $(or $($(category)_TARGET),libs),				\
                   $($(category)_FLAGS)))					\
    )										\
  )
 
+# Pull in non-recursive targets if this is a partial tree build.
+ifndef TOPLEVEL_BUILD
+include $(topsrcdir)/config/makefiles/nonrecursive.mk
+endif
+
 ################################################################################
 # Special gmake rules.
 ################################################################################
 
 
 #
 # Re-define the list of default suffixes, so gmake won't have to churn through
 # hundreds of built-in suffix rules for stuff we don't need.
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -9257,32 +9257,32 @@ nsDocument::CreateTouchList(nsIVariant* 
     uint16_t type;
     aPoints->GetDataType(&type);
     if (type == nsIDataType::VTYPE_INTERFACE ||
         type == nsIDataType::VTYPE_INTERFACE_IS) {
       nsCOMPtr<nsISupports> data;
       aPoints->GetAsISupports(getter_AddRefs(data));
       nsCOMPtr<nsIDOMTouch> point = do_QueryInterface(data);
       if (point) {
-        retval->Append(point);
+        retval->Append(static_cast<Touch*>(point.get()));
       }
     } else if (type == nsIDataType::VTYPE_ARRAY) {
       uint16_t valueType;
       nsIID iid;
       uint32_t valueCount;
       void* rawArray;
       aPoints->GetAsArray(&valueType, &iid, &valueCount, &rawArray);
       if (valueType == nsIDataType::VTYPE_INTERFACE ||
           valueType == nsIDataType::VTYPE_INTERFACE_IS) {
         nsISupports** values = static_cast<nsISupports**>(rawArray);
         for (uint32_t i = 0; i < valueCount; ++i) {
           nsCOMPtr<nsISupports> supports = dont_AddRef(values[i]);
           nsCOMPtr<nsIDOMTouch> point = do_QueryInterface(supports);
           if (point) {
-            retval->Append(point);
+            retval->Append(static_cast<Touch*>(point.get()));
           }
         }
       }
       nsMemory::Free(rawArray);
     }
   }
 
   *aRetVal = retval.forget().get();
--- a/content/base/test/test_mutationobservers.html
+++ b/content/base/test/test_mutationobservers.html
@@ -13,21 +13,16 @@ https://bugzilla.mozilla.org/show_bug.cg
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=641821">Mozilla Bug 641821</a>
 <p id="display"></p>
 <div id="content" style="display: none">
                                 
 </div>
 <pre id="test">
 <script type="application/javascript">
 
-if (!navigator.platform.startsWith("Win")) {
-  // for non-Windows
-  SimpleTest.expectAssertions(1);
-}
-
 /** Test for Bug 641821 **/
 
 var div = document.createElement("div");
 
 var M;
 if ("MozMutationObserver" in window) {
   M = window.MozMutationObserver;
 } else if ("WebKitMutationObserver" in window) {
@@ -569,22 +564,16 @@ function testExpandos() {
     then();
   });
   m2.expandoProperty = true;
   m2.observe(div, { attributes: true });
   m2 = null;
   if (SpecialPowers) {
     // Run GC several times to see if the expando property disappears.
 
-    // Also, garbage collecting the windows created in this test can
-    // cause assertions, so we must GC now to blame those assertions to
-    // this test.
-    // ("mArguments wasn't cleaned up properly!" in ~nsGlobalWindow,
-    // bug 600703)
-
     SpecialPowers.gc();
     SpecialPowers.gc();
     SpecialPowers.gc();
     SpecialPowers.gc();
   }
   div.setAttribute("foo", "bar2");
 }
 
--- a/content/events/src/Touch.cpp
+++ b/content/events/src/Touch.cpp
@@ -123,16 +123,34 @@ Touch::GetRotationAngle(float* aRotation
 
 NS_IMETHODIMP
 Touch::GetForce(float* aForce)
 {
   *aForce = Force();
   return NS_OK;
 }
 
+void
+Touch::InitializePoints(nsPresContext* aPresContext, nsEvent* aEvent)
+{
+  if (mPointsInitialized) {
+    return;
+  }
+  mClientPoint = nsDOMEvent::GetClientCoords(aPresContext,
+                                             aEvent,
+                                             mRefPoint,
+                                             mClientPoint);
+  mPagePoint = nsDOMEvent::GetPageCoords(aPresContext,
+                                         aEvent,
+                                         mRefPoint,
+                                         mClientPoint);
+  mScreenPoint = nsDOMEvent::GetScreenCoords(aPresContext, aEvent, mRefPoint);
+  mPointsInitialized = true;
+}
+
 bool
 Touch::Equals(nsIDOMTouch* aTouch)
 {
   float force;
   float orientation;
   int32_t radiusX, radiusY;
   aTouch->GetForce(&force);
   aTouch->GetRotationAngle(&orientation);
--- a/content/events/src/Touch.h
+++ b/content/events/src/Touch.h
@@ -71,40 +71,27 @@ public:
 
       mChanged = false;
       mMessage = 0;
       nsJSContext::LikelyShortLivingObjectCreated();
     }
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(Touch)
   NS_DECL_NSIDOMTOUCH
-  void InitializePoints(nsPresContext* aPresContext, nsEvent* aEvent)
-  {
-    if (mPointsInitialized) {
-      return;
-    }
-    mClientPoint = nsDOMEvent::GetClientCoords(aPresContext,
-                                               aEvent,
-                                               mRefPoint,
-                                               mClientPoint);
-    mPagePoint = nsDOMEvent::GetPageCoords(aPresContext,
-                                           aEvent,
-                                           mRefPoint,
-                                           mClientPoint);
-    mScreenPoint = nsDOMEvent::GetScreenCoords(aPresContext, aEvent, mRefPoint);
-    mPointsInitialized = true;
-  }
+
+  void InitializePoints(nsPresContext* aPresContext, nsEvent* aEvent);
+
   void SetTarget(mozilla::dom::EventTarget *aTarget)
   {
     mTarget = aTarget;
   }
   bool Equals(nsIDOMTouch* aTouch);
 
   virtual JSObject* WrapObject(JSContext* aCx,
-			       JS::Handle<JSObject*> aScope) MOZ_OVERRIDE;
+                               JS::Handle<JSObject*> aScope) MOZ_OVERRIDE;
   EventTarget* GetParentObject() { return mTarget; }
 
   // WebIDL
   int32_t Identifier() const { return mIdentifier; }
   EventTarget* Target() const;
   int32_t ScreenX() const { return mScreenPoint.x; }
   int32_t ScreenY() const { return mScreenPoint.y; }
   int32_t ClientX() const { return mClientPoint.x; }
--- a/content/html/document/test/test_bug391777.html
+++ b/content/html/document/test/test_bug391777.html
@@ -8,28 +8,18 @@ https://bugzilla.mozilla.org/show_bug.cg
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>        
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=391777">Mozilla Bug 391777</a>
 <p id="display"></p>
 <script class="testbody" type="text/javascript">
 
-if (!navigator.platform.startsWith("Win")) {
-  // not Windows
-  SimpleTest.expectAssertions(1);
-}
-
 /** Test for Bug 391777 **/
 var arg = {};
 arg.testVal = "foo";
 var result = window.showModalDialog("javascript:window.returnValue = window.dialogArguments.testVal; window.close(); 'This window should close on its own.';", arg);
 ok(true, "We should get here without user interaction");
 is(result, "foo", "Unexpected result from showModalDialog");
 
-// Garbage collecting the windows created in this test can cause
-// assertions, so GC now to blame those assertions to this test.
-// ("mArguments wasn't cleaned up properly!" in ~nsGlobalWindow,
-// bug 600703)
-SpecialPowers.gc();
 </script>
 </body>
 </html>
--- a/content/media/webspeech/recognition/test/test_nested_eventloop.html
+++ b/content/media/webspeech/recognition/test/test_nested_eventloop.html
@@ -23,30 +23,27 @@ https://bugzilla.mozilla.org/show_bug.cg
   /*
    * window.showModalDialog() can be used to spin the event loop, causing
    * queued SpeechEvents (such as those created by calls to start(), stop()
    * or abort()) to be processed immediately.
    * When this is done from inside DOM event handlers, it is possible to
    * cause reentrancy in our C++ code, which we should be able to withstand.
    */
 
-  // Garbage collecting the windows created in this test can
-  // cause assertions (Bug 600703).
-  if (!navigator.platform.startsWith("Win")) {
-    SimpleTest.expectAssertions(2);
-  }
-
   function abortAndSpinEventLoop(evt, sr) {
     sr.abort();
     window.showModalDialog("javascript:window.close()");
   }
 
   function doneFunc() {
     // Trigger gc now and wait some time to make sure this test gets the blame
     // for any assertions caused by showModalDialog
+    //
+    // NB - The assertions should be gone, but this looks too scary to touch
+    // during batch cleanup.
     var count = 0, GC_COUNT = 4;
 
     function triggerGCOrFinish() {
       SpecialPowers.gc();
       count++;
 
       if (count == GC_COUNT) {
         SimpleTest.finish();
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -11447,17 +11447,17 @@ nsDocShell::EnsureScriptEnvironment()
 
     nsCOMPtr<nsIWebBrowserChrome> browserChrome(do_GetInterface(mTreeOwner));
     NS_ENSURE_TRUE(browserChrome, NS_ERROR_NOT_AVAILABLE);
 
     uint32_t chromeFlags;
     browserChrome->GetChromeFlags(&chromeFlags);
 
     bool isModalContentWindow = (mItemType == typeContent) &&
-        (chromeFlags & nsIWebBrowserChrome::CHROME_MODAL);
+        (chromeFlags & nsIWebBrowserChrome::CHROME_MODAL_CONTENT_WINDOW);
     // There can be various other content docshells associated with the
     // top-level window, like sidebars. Make sure that we only create an
     // nsGlobalModalWindow for the primary content shell.
     if (isModalContentWindow) {
         nsCOMPtr<nsIDocShellTreeItem> primaryItem;
         nsresult rv = mTreeOwner->GetPrimaryContentShell(getter_AddRefs(primaryItem));
         NS_ENSURE_SUCCESS(rv, rv);
         isModalContentWindow = (primaryItem == this);
--- a/dom/base/Navigator.cpp
+++ b/dom/base/Navigator.cpp
@@ -32,16 +32,18 @@
 #include "mozilla/dom/MobileMessageManager.h"
 #include "nsISmsService.h"
 #include "mozilla/Hal.h"
 #include "nsIWebNavigation.h"
 #include "nsISiteSpecificUserAgent.h"
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/StaticPtr.h"
 #include "Connection.h"
+#include "nsDOMClassInfo.h"
+#include "nsDOMEvent.h"
 #ifdef MOZ_B2G_RIL
 #include "MobileConnection.h"
 #include "mozilla/dom/CellBroadcast.h"
 #include "mozilla/dom/Voicemail.h"
 #endif
 #include "nsIIdleObserver.h"
 #include "nsIPermissionManager.h"
 #include "nsNetUtil.h"
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -1000,17 +1000,16 @@ jsid nsDOMClassInfo::sLocation_id       
 jsid nsDOMClassInfo::sConstructor_id     = JSID_VOID;
 jsid nsDOMClassInfo::s_content_id        = JSID_VOID;
 jsid nsDOMClassInfo::sContent_id         = JSID_VOID;
 jsid nsDOMClassInfo::sMenubar_id         = JSID_VOID;
 jsid nsDOMClassInfo::sToolbar_id         = JSID_VOID;
 jsid nsDOMClassInfo::sLocationbar_id     = JSID_VOID;
 jsid nsDOMClassInfo::sPersonalbar_id     = JSID_VOID;
 jsid nsDOMClassInfo::sStatusbar_id       = JSID_VOID;
-jsid nsDOMClassInfo::sDialogArguments_id = JSID_VOID;
 jsid nsDOMClassInfo::sControllers_id     = JSID_VOID;
 jsid nsDOMClassInfo::sLength_id          = JSID_VOID;
 jsid nsDOMClassInfo::sScrollX_id         = JSID_VOID;
 jsid nsDOMClassInfo::sScrollY_id         = JSID_VOID;
 jsid nsDOMClassInfo::sScrollMaxX_id      = JSID_VOID;
 jsid nsDOMClassInfo::sScrollMaxY_id      = JSID_VOID;
 jsid nsDOMClassInfo::sItem_id            = JSID_VOID;
 jsid nsDOMClassInfo::sNamedItem_id       = JSID_VOID;
@@ -1260,17 +1259,16 @@ nsDOMClassInfo::DefineStaticJSVals(JSCon
   SET_JSID_TO_STRING(sConstructor_id,     cx, "constructor");
   SET_JSID_TO_STRING(s_content_id,        cx, "_content");
   SET_JSID_TO_STRING(sContent_id,         cx, "content");
   SET_JSID_TO_STRING(sMenubar_id,         cx, "menubar");
   SET_JSID_TO_STRING(sToolbar_id,         cx, "toolbar");
   SET_JSID_TO_STRING(sLocationbar_id,     cx, "locationbar");
   SET_JSID_TO_STRING(sPersonalbar_id,     cx, "personalbar");
   SET_JSID_TO_STRING(sStatusbar_id,       cx, "statusbar");
-  SET_JSID_TO_STRING(sDialogArguments_id, cx, "dialogArguments");
   SET_JSID_TO_STRING(sControllers_id,     cx, "controllers");
   SET_JSID_TO_STRING(sLength_id,          cx, "length");
   SET_JSID_TO_STRING(sScrollX_id,         cx, "scrollX");
   SET_JSID_TO_STRING(sScrollY_id,         cx, "scrollY");
   SET_JSID_TO_STRING(sScrollMaxX_id,      cx, "scrollMaxX");
   SET_JSID_TO_STRING(sScrollMaxY_id,      cx, "scrollMaxY");
   SET_JSID_TO_STRING(sItem_id,            cx, "item");
   SET_JSID_TO_STRING(sNamedItem_id,       cx, "namedItem");
@@ -2968,17 +2966,16 @@ nsDOMClassInfo::ShutDown()
   sConstructor_id     = JSID_VOID;
   s_content_id        = JSID_VOID;
   sContent_id         = JSID_VOID;
   sMenubar_id         = JSID_VOID;
   sToolbar_id         = JSID_VOID;
   sLocationbar_id     = JSID_VOID;
   sPersonalbar_id     = JSID_VOID;
   sStatusbar_id       = JSID_VOID;
-  sDialogArguments_id = JSID_VOID;
   sControllers_id     = JSID_VOID;
   sLength_id          = JSID_VOID;
   sScrollX_id         = JSID_VOID;
   sScrollY_id         = JSID_VOID;
   sScrollMaxX_id      = JSID_VOID;
   sScrollMaxY_id      = JSID_VOID;
   sItem_id            = JSID_VOID;
   sEnumerate_id       = JSID_VOID;
@@ -5077,33 +5074,16 @@ nsWindowSH::NewResolve(nsIXPConnectWrapp
                                      JSPROP_READONLY | JSPROP_ENUMERATE);
         if (!*_retval) {
           return NS_ERROR_UNEXPECTED;
         }
       }
 
       return NS_OK;
     }
-
-    if (sDialogArguments_id == id && win->IsModalContentWindow()) {
-      nsCOMPtr<nsIArray> args;
-      ((nsGlobalModalWindow *)win)->GetDialogArguments(getter_AddRefs(args));
-
-      nsIScriptContext *script_cx = win->GetContext();
-      if (script_cx) {
-        // Make nsJSContext::SetProperty()'s magic argument array
-        // handling happen.
-        rv = script_cx->SetProperty(obj, "dialogArguments", args);
-        NS_ENSURE_SUCCESS(rv, rv);
-
-        *objp = obj;
-      }
-
-      return NS_OK;
-    }
   }
 
   rv = nsDOMGenericSH::NewResolve(wrapper, cx, obj, id, flags, objp,
                                   _retval);
 
   if (NS_FAILED(rv) || *objp) {
     // Something went wrong, or the property got resolved. Return.
     return rv;
--- a/dom/base/nsDOMWindowUtils.cpp
+++ b/dom/base/nsDOMWindowUtils.cpp
@@ -851,21 +851,21 @@ nsDOMWindowUtils::SendTouchEvent(const n
 
   nsPresContext* presContext = GetPresContext();
   if (!presContext) {
     return NS_ERROR_FAILURE;
   }
   event.touches.SetCapacity(aCount);
   for (uint32_t i = 0; i < aCount; ++i) {
     nsIntPoint pt = ToWidgetPoint(aXs[i], aYs[i], offset, presContext);
-    nsCOMPtr<nsIDOMTouch> t(new Touch(aIdentifiers[i],
-                                      pt,
-                                      nsIntPoint(aRxs[i], aRys[i]),
-                                      aRotationAngles[i],
-                                      aForces[i]));
+    nsRefPtr<Touch> t = new Touch(aIdentifiers[i],
+                                  pt,
+                                  nsIntPoint(aRxs[i], aRys[i]),
+                                  aRotationAngles[i],
+                                  aForces[i]);
     event.touches.AppendElement(t);
   }
 
   nsEventStatus status;
   nsresult rv = widget->DispatchEvent(&event, status);
   *aPreventDefault = (status == nsEventStatus_eConsumeNoDefault);
   return rv;
 }
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -530,16 +530,26 @@ nsPIDOMWindow::nsPIDOMWindow(nsPIDOMWind
   mIsActive(false), mIsBackground(false),
   mInnerWindow(nullptr), mOuterWindow(aOuterWindow),
   // Make sure no actual window ends up with mWindowID == 0
   mWindowID(++gNextWindowID), mHasNotifiedGlobalCreated(false)
  {}
 
 nsPIDOMWindow::~nsPIDOMWindow() {}
 
+// DialogValueHolder CC goop.
+NS_IMPL_CYCLE_COLLECTION_1(DialogValueHolder, mValue)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DialogValueHolder)
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(DialogValueHolder)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(DialogValueHolder)
+
 //*****************************************************************************
 // nsOuterWindowProxy: Outer Window Proxy
 //*****************************************************************************
 
 class nsOuterWindowProxy : public js::Wrapper
 {
 public:
   nsOuterWindowProxy() : js::Wrapper(0) { }
@@ -1227,19 +1237,26 @@ nsGlobalWindow::~nsGlobalWindow()
     nsGlobalWindow *outer = GetOuterWindowInternal();
     if (outer) {
       outer->MaybeClearInnerWindow(this);
     }
   }
 
   mDoc = nullptr;
 
-  NS_ASSERTION(!mArguments, "mArguments wasn't cleaned up properly!");
-
-  CleanUp(true);
+  // Outer windows are always supposed to call CleanUp before letting themselves
+  // be destroyed. And while CleanUp generally seems to be intended to clean up
+  // outers, we've historically called it for both. Changing this would probably
+  // involve auditing all of the references that inners and outers can have, and
+  // separating the handling into CleanUp() and FreeInnerObjects.
+  if (IsInnerWindow()) {
+    CleanUp(true);
+  } else {
+    MOZ_ASSERT(mCleanedUp);
+  }
 
   nsCOMPtr<nsIDeviceSensors> ac = do_GetService(NS_DEVICE_SENSORS_CONTRACTID);
   if (ac)
     ac->RemoveWindowAsListener(this);
 
   nsLayoutStatics::Release();
 }
 
@@ -1390,18 +1407,17 @@ nsGlobalWindow::CleanUp(bool aIgnoreModa
     if (asChrome->mMessageManager) {
       static_cast<nsFrameMessageManager*>(
         asChrome->mMessageManager.get())->Disconnect();
     }
   }
 
   mInnerWindowHolder = nullptr;
   mArguments = nullptr;
-  mArgumentsLast = nullptr;
-  mArgumentsOrigin = nullptr;
+  mDialogArguments = nullptr;
 
   CleanupCachedXBLHandlers(this);
 
   if (mIdleTimer) {
     mIdleTimer->Cancel();
     mIdleTimer = nullptr;
   }
 
@@ -1619,17 +1635,17 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_
   if (!cb.WantAllTraces() && tmp->IsBlackForCC()) {
     return NS_SUCCESS_INTERRUPTED_TRAVERSE;
   }
 
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mContext)
 
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mControllers)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mArguments)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mArgumentsLast)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDialogArguments)
 
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPerformance)
 
 #ifdef MOZ_WEBSPEECH
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSpeechSynthesis)
 #endif
 
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mInnerWindowHolder)
@@ -1666,17 +1682,17 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsGlobalWindow)
   nsGlobalWindow::CleanupCachedXBLHandlers(tmp);
 
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mContext)
 
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mControllers)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mArguments)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mArgumentsLast)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mDialogArguments)
 
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mPerformance)
 
 #ifdef MOZ_WEBSPEECH
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mSpeechSynthesis)
 #endif
 
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mInnerWindowHolder)
@@ -2505,21 +2521,17 @@ nsGlobalWindow::SetNewDocument(nsIDocume
       // Initialize DOM classes etc on the inner window.
       JS::Rooted<JSObject*> obj(cx, newInnerWindow->mJSObject);
       rv = mContext->InitClasses(obj);
       NS_ENSURE_SUCCESS(rv, rv);
     }
 
     if (mArguments) {
       newInnerWindow->DefineArgumentsProperty(mArguments);
-      newInnerWindow->mArguments = mArguments;
-      newInnerWindow->mArgumentsOrigin = mArgumentsOrigin;
-
       mArguments = nullptr;
-      mArgumentsOrigin = nullptr;
     }
 
     // Give the new inner window our chrome event handler (since it
     // doesn't have one).
     newInnerWindow->mChromeEventHandler = mChromeEventHandler;
   }
 
   mContext->GC(JS::gcreason::SET_NEW_DOCUMENT);
@@ -2729,24 +2741,16 @@ nsGlobalWindow::DetachFromDocShell()
     mDoc = nullptr;
     mFocusedNode = nullptr;
   }
 
   ClearControllers();
 
   mChromeEventHandler = nullptr; // force release now
 
-  if (mArguments) { 
-    // We got no new document after someone called
-    // SetArguments(), drop our reference to the arguments.
-    mArguments = nullptr;
-    mArgumentsLast = nullptr;
-    mArgumentsOrigin = nullptr;
-  }
-
   if (mContext) {
     mContext->GC(JS::gcreason::SET_DOC_SHELL);
     mContext = nullptr;
   }
 
   mDocShell = nullptr; // Weak Reference
 
   NS_ASSERTION(!mNavigator, "Non-null mNavigator in outer window!");
@@ -3138,62 +3142,60 @@ nsGlobalWindow::SetScriptsEnabled(bool a
     // Scripts are enabled (again?) on this context, run timeouts that
     // fired on this context while scripts were disabled.
     void (nsGlobalWindow::*run)() = &nsGlobalWindow::RunTimeout;
     NS_DispatchToCurrentThread(NS_NewRunnableMethod(this, run));
   }
 }
 
 nsresult
-nsGlobalWindow::SetArguments(nsIArray *aArguments, nsIPrincipal *aOrigin)
-{
-  FORWARD_TO_OUTER(SetArguments, (aArguments, aOrigin),
+nsGlobalWindow::SetArguments(nsIArray *aArguments)
+{
+  FORWARD_TO_OUTER(SetArguments, (aArguments),
                    NS_ERROR_NOT_INITIALIZED);
-
-  // Hold on to the arguments so that we can re-set them once the next
-  // document is loaded.
-  mArguments = aArguments;
-  mArgumentsOrigin = aOrigin;
-
+  nsresult rv;
+
+  // Historically, we've used the same machinery to handle openDialog arguments
+  // (exposed via window.arguments) and showModalDialog arguments (exposed via
+  // window.dialogArguments), even though the former is XUL-only and uses an XPCOM
+  // array while the latter is web-exposed and uses an arbitrary JS value.
+  // Moreover, per-spec |dialogArguments| is a property of the browsing context
+  // (outer), whereas |arguments| lives on the inner.
+  //
+  // We've now mostly separated them, but the difference is still opaque to
+  // nsWindowWatcher (the caller of SetArguments in this little back-and-forth
+  // embedding waltz we do here).
+  //
+  // So we need to demultiplex the two cases here.
   nsGlobalWindow *currentInner = GetCurrentInnerWindowInternal();
-
-  if (!mIsModalContentWindow) {
-    mArgumentsLast = aArguments;
-  } else if (currentInner) {
-    // SetArguments() is being called on a modal content window that
-    // already has an inner window. This can happen when loading
-    // javascript: URIs as modal content dialogs. In this case, we'll
-    // set up the dialog window, both inner and outer, before we call
-    // SetArguments() on the window, so to deal with that, make sure
-    // here that the arguments are propagated to the inner window.
-
-    currentInner->mArguments = aArguments;
-    currentInner->mArgumentsOrigin = aOrigin;
-  }
-
-  return currentInner ?
-    currentInner->DefineArgumentsProperty(aArguments) : NS_OK;
+  if (mIsModalContentWindow) {
+    // nsWindowWatcher blindly converts the original nsISupports into an array
+    // of length 1. We need to recover it, and then cast it back to the concrete
+    // object we know it to be.
+    nsCOMPtr<nsISupports> supports = do_QueryElementAt(aArguments, 0, &rv);
+    NS_ENSURE_SUCCESS(rv, rv);
+    mDialogArguments = static_cast<DialogValueHolder*>(supports.get());
+  } else {
+    mArguments = aArguments;
+    rv = currentInner->DefineArgumentsProperty(aArguments);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  return NS_OK;
 }
 
 nsresult
 nsGlobalWindow::DefineArgumentsProperty(nsIArray *aArguments)
 {
+  MOZ_ASSERT(!mIsModalContentWindow); // Handled separately.
   nsIScriptContext *ctx = GetOuterWindowInternal()->mContext;
   NS_ENSURE_TRUE(aArguments && ctx, NS_ERROR_NOT_INITIALIZED);
   AutoPushJSContext cx(ctx->GetNativeContext());
   NS_ENSURE_TRUE(cx, NS_ERROR_NOT_INITIALIZED);
 
-  if (mIsModalContentWindow) {
-    // Modal content windows don't have an "arguments" property, they
-    // have a "dialogArguments" property which is handled
-    // separately. See nsWindowSH::NewResolve().
-
-    return NS_OK;
-  }
-
   JS::Rooted<JSObject*> obj(cx, mJSObject);
   return GetContextInternal()->SetProperty(obj, "arguments", aArguments);
 }
 
 //*****************************************************************************
 // nsGlobalWindow::nsIScriptObjectPrincipal
 //*****************************************************************************
 
@@ -7631,28 +7633,38 @@ ConvertDialogOptions(const nsAString& aO
       break;
     }
 
     iter++;
   }
 }
 
 NS_IMETHODIMP
-nsGlobalWindow::ShowModalDialog(const nsAString& aURI, nsIVariant *aArgs,
-                                const nsAString& aOptions,
+nsGlobalWindow::ShowModalDialog(const nsAString& aURI, nsIVariant *aArgs_,
+                                const nsAString& aOptions, uint8_t aArgc,
                                 nsIVariant **aRetVal)
 {
-  FORWARD_TO_OUTER(ShowModalDialog, (aURI, aArgs, aOptions, aRetVal),
+  FORWARD_TO_OUTER(ShowModalDialog, (aURI, aArgs_, aOptions, aArgc, aRetVal),
                    NS_ERROR_NOT_INITIALIZED);
 
   *aRetVal = nullptr;
 
   if (Preferences::GetBool("dom.disable_window_showModalDialog", false))
     return NS_ERROR_NOT_AVAILABLE;
 
+  // Per-spec the |arguments| parameter is supposed to pass through unmodified.
+  // However, XPConnect default-initializes variants to null, rather than
+  // undefined. Fix this up here.
+  nsCOMPtr<nsIVariant> aArgs = aArgs_;
+  if (aArgc < 1) {
+    aArgs = CreateVoidVariant();
+  }
+  nsRefPtr<DialogValueHolder> argHolder =
+    new DialogValueHolder(nsContentUtils::GetSubjectPrincipal(), aArgs);
+
   // Before bringing up the window/dialog, unsuppress painting and flush
   // pending reflows.
   EnsureReflowFlushAndPaint();
 
   bool needToPromptForAbuse;
   if (DialogsAreBlocked(&needToPromptForAbuse)) {
     return NS_ERROR_NOT_AVAILABLE;
   }
@@ -7672,73 +7684,35 @@ nsGlobalWindow::ShowModalDialog(const ns
   uint32_t oldMicroTaskLevel = nsContentUtils::MicroTaskLevel();
   nsContentUtils::SetMicroTaskLevel(0);
   nsresult rv = OpenInternal(aURI, EmptyString(), options,
                              false,          // aDialog
                              true,           // aContentModal
                              true,           // aCalledNoScript
                              true,           // aDoJSFixups
                              true,           // aNavigate
-                             nullptr, aArgs, // args
-                             GetPrincipal(),    // aCalleePrincipal
+                             nullptr, argHolder, // args
+                             GetPrincipal(),     // aCalleePrincipal
                              nullptr,            // aJSCallerContext
                              getter_AddRefs(dlgWin));
   nsContentUtils::SetMicroTaskLevel(oldMicroTaskLevel);
   LeaveModalState(callerWin);
-
   NS_ENSURE_SUCCESS(rv, rv);
-  
-  if (dlgWin) {
-    nsCOMPtr<nsIPrincipal> subjectPrincipal;
-    rv = nsContentUtils::GetSecurityManager()->
-      GetSubjectPrincipal(getter_AddRefs(subjectPrincipal));
-    if (NS_FAILED(rv)) {
-      return rv;
-    }
-
-    bool canAccess = true;
-
-    if (subjectPrincipal) {
-      nsCOMPtr<nsIScriptObjectPrincipal> objPrincipal =
-        do_QueryInterface(dlgWin);
-      nsCOMPtr<nsIPrincipal> dialogPrincipal;
-
-      if (objPrincipal) {
-        dialogPrincipal = objPrincipal->GetPrincipal();
-
-        rv = subjectPrincipal->Subsumes(dialogPrincipal, &canAccess);
-        NS_ENSURE_SUCCESS(rv, rv);
-      } else {
-        // Uh, not sure what kind of dialog this is. Prevent access to
-        // be on the safe side...
-
-        canAccess = false;
-      }
-    }
-
-    nsCOMPtr<nsPIDOMWindow> win(do_QueryInterface(dlgWin));
-
-    if (canAccess) {
-      nsPIDOMWindow *inner = win->GetCurrentInnerWindow();
-
-      nsCOMPtr<nsIDOMModalContentWindow> dlgInner(do_QueryInterface(inner));
-
-      if (dlgInner) {
-        dlgInner->GetReturnValue(aRetVal);
-      }
-    }
-
-    nsRefPtr<nsGlobalWindow> winInternal =
-      static_cast<nsGlobalWindow*>(win.get());
-    if (winInternal->mCallCleanUpAfterModalDialogCloses) {
-      winInternal->mCallCleanUpAfterModalDialogCloses = false;
-      winInternal->CleanUp(true);
-    }
-  }
-  
+
+  nsCOMPtr<nsIDOMModalContentWindow> dialog = do_QueryInterface(dlgWin);
+  if (dialog) {
+    rv = dialog->GetReturnValue(aRetVal);
+    MOZ_ASSERT(NS_SUCCEEDED(rv));
+    nsGlobalModalWindow *win = static_cast<nsGlobalModalWindow*>(dialog.get());
+    if (win->mCallCleanUpAfterModalDialogCloses) {
+      win->mCallCleanUpAfterModalDialogCloses = false;
+      win->CleanUp(true);
+    }
+  }
+
   return NS_OK;
 }
 
 class CommandDispatcher : public nsRunnable
 {
 public:
   CommandDispatcher(nsIDOMXULCommandDispatcher* aDispatcher,
                     const nsAString& aAction)
@@ -11617,66 +11591,48 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_
   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(ModalContentWindow)
 NS_INTERFACE_MAP_END_INHERITING(nsGlobalWindow)
 
 NS_IMPL_ADDREF_INHERITED(nsGlobalModalWindow, nsGlobalWindow)
 NS_IMPL_RELEASE_INHERITED(nsGlobalModalWindow, nsGlobalWindow)
 
 
 NS_IMETHODIMP
-nsGlobalModalWindow::GetDialogArguments(nsIArray **aArguments)
-{
-  FORWARD_TO_INNER_MODAL_CONTENT_WINDOW(GetDialogArguments, (aArguments),
+nsGlobalModalWindow::GetDialogArguments(nsIVariant **aArguments)
+{
+  FORWARD_TO_OUTER_MODAL_CONTENT_WINDOW(GetDialogArguments, (aArguments),
                                         NS_ERROR_NOT_INITIALIZED);
 
-  bool subsumes = false;
-  nsIPrincipal *self = GetPrincipal();
-  if (self && NS_SUCCEEDED(self->Subsumes(mArgumentsOrigin, &subsumes)) &&
-      subsumes) {
-    NS_IF_ADDREF(*aArguments = mArguments);
-  } else {
-    *aArguments = nullptr;
-  }
-
-  return NS_OK;
+  // This does an internal origin check, and returns undefined if the subject
+  // does not subsumes the origin of the arguments.
+  return mDialogArguments->Get(nsContentUtils::GetSubjectPrincipal(), aArguments);
 }
 
 NS_IMETHODIMP
 nsGlobalModalWindow::GetReturnValue(nsIVariant **aRetVal)
 {
   FORWARD_TO_OUTER_MODAL_CONTENT_WINDOW(GetReturnValue, (aRetVal), NS_OK);
 
-  NS_IF_ADDREF(*aRetVal = mReturnValue);
-
-  return NS_OK;
+  nsCOMPtr<nsIVariant> result;
+  if (!mReturnValue) {
+    nsCOMPtr<nsIVariant> variant = CreateVoidVariant();
+    variant.forget(aRetVal);
+    return NS_OK;
+  }
+  return mReturnValue->Get(nsContentUtils::GetSubjectPrincipal(), aRetVal);
 }
 
 NS_IMETHODIMP
 nsGlobalModalWindow::SetReturnValue(nsIVariant *aRetVal)
 {
   FORWARD_TO_OUTER_MODAL_CONTENT_WINDOW(SetReturnValue, (aRetVal), NS_OK);
 
-  mReturnValue = aRetVal;
-
-  return NS_OK;
-}
-
-nsresult
-nsGlobalModalWindow::SetNewDocument(nsIDocument *aDocument,
-                                    nsISupports *aState,
-                                    bool aForceReuseInnerWindow)
-{
-  MOZ_ASSERT(aDocument);
-
-  // If we're loading a new document into a modal dialog, clear the
-  // return value that was set, if any, by the current document.
-  mReturnValue = nullptr;
-
-  return nsGlobalWindow::SetNewDocument(aDocument, aState,
-                                        aForceReuseInnerWindow);
+  mReturnValue = new DialogValueHolder(nsContentUtils::GetSubjectPrincipal(),
+                                       aRetVal);
+  return NS_OK;
 }
 
 void
 nsGlobalWindow::SetHasAudioAvailableEventListeners()
 {
   if (mDoc) {
     mDoc->NotifyAudioAvailableListener();
   }
--- a/dom/base/nsGlobalWindow.h
+++ b/dom/base/nsGlobalWindow.h
@@ -229,16 +229,63 @@ struct IdleObserverHolder
   }
 
   ~IdleObserverHolder()
   {
     MOZ_COUNT_DTOR(IdleObserverHolder);
   }
 };
 
+static inline already_AddRefed<nsIVariant>
+CreateVoidVariant()
+{
+  nsCOMPtr<nsIWritableVariant> writable =
+    do_CreateInstance(NS_VARIANT_CONTRACTID);
+  writable->SetAsVoid();
+  return writable.forget();
+}
+
+// Helper class to manage modal dialog arguments and all their quirks.
+//
+// Given our clunky embedding APIs, modal dialog arguments need to be passed
+// as an nsISupports parameter to WindowWatcher, get stuck inside an array of
+// length 1, and then passed back to the newly-created dialog.
+//
+// However, we need to track both the caller-passed value as well as the
+// caller's, so that we can do an origin check (even for primitives) when the
+// value is accessed. This class encapsulates that magic.
+//
+// We also use the same machinery for |returnValue|, which needs similar origin
+// checks.
+class DialogValueHolder : public nsISupports
+{
+public:
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTION_CLASS(DialogValueHolder)
+
+  DialogValueHolder(nsIPrincipal* aSubject, nsIVariant* aValue)
+    : mOrigin(aSubject)
+    , mValue(aValue) {}
+  nsresult Get(nsIPrincipal* aSubject, nsIVariant** aResult)
+  {
+    nsCOMPtr<nsIVariant> result;
+    if (aSubject->Subsumes(mOrigin)) {
+      result = mValue;
+    } else {
+      result = CreateVoidVariant();
+    }
+    result.forget(aResult);
+    return NS_OK;
+  }
+  virtual ~DialogValueHolder() {}
+private:
+  nsCOMPtr<nsIPrincipal> mOrigin;
+  nsCOMPtr<nsIVariant> mValue;
+};
+
 //*****************************************************************************
 // nsGlobalWindow: Global Object for Scripting
 //*****************************************************************************
 // Beware that all scriptable interfaces implemented by
 // nsGlobalWindow will be reachable from JS, if you make this class
 // implement new interfaces you better know what you're
 // doing. Security wise this is very sensitive code. --
 // jst@netscape.com
@@ -566,17 +613,17 @@ public:
   virtual void EnableTimeChangeNotifications();
   virtual void DisableTimeChangeNotifications();
 
 #ifdef MOZ_B2G
   virtual void EnableNetworkEvent(uint32_t aType);
   virtual void DisableNetworkEvent(uint32_t aType);
 #endif // MOZ_B2G
 
-  virtual nsresult SetArguments(nsIArray *aArguments, nsIPrincipal *aOrigin);
+  virtual nsresult SetArguments(nsIArray *aArguments);
 
   static bool DOMWindowDumpEnabled();
 
   void MaybeForgiveSpamCount();
   bool IsClosedOrClosing() {
     return (mIsClosed ||
             mInClose ||
             mHavePendingClose ||
@@ -1108,19 +1155,23 @@ protected:
   bool                   mNotifiedIDDestroyed : 1;
   // whether scripts may close the window,
   // even if "dom.allow_scripts_to_close_windows" is false.
   bool                   mAllowScriptsToClose : 1;
 
   nsCOMPtr<nsIScriptContext>    mContext;
   nsWeakPtr                     mOpener;
   nsCOMPtr<nsIControllers>      mControllers;
+
+  // For |window.arguments|, via |openDialog|.
   nsCOMPtr<nsIArray>            mArguments;
-  nsCOMPtr<nsIArray>            mArgumentsLast;
-  nsCOMPtr<nsIPrincipal>        mArgumentsOrigin;
+
+  // For |window.dialogArguments|, via |showModalDialog|.
+  nsRefPtr<DialogValueHolder> mDialogArguments;
+
   nsRefPtr<Navigator>           mNavigator;
   nsRefPtr<nsScreen>            mScreen;
   nsRefPtr<nsDOMWindowList>     mFrames;
   nsRefPtr<nsBarProp>           mMenubar;
   nsRefPtr<nsBarProp>           mToolbar;
   nsRefPtr<nsBarProp>           mLocationbar;
   nsRefPtr<nsBarProp>           mPersonalbar;
   nsRefPtr<nsBarProp>           mStatusbar;
@@ -1276,22 +1327,19 @@ public:
     mIsModalContentWindow = true;
   }
 
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_NSIDOMMODALCONTENTWINDOW
 
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(nsGlobalModalWindow, nsGlobalWindow)
 
-  virtual NS_HIDDEN_(nsresult) SetNewDocument(nsIDocument *aDocument,
-                                              nsISupports *aState,
-                                              bool aForceReuseInnerWindow);
-
 protected:
-  nsCOMPtr<nsIVariant> mReturnValue;
+  // For use by outer windows only.
+  nsRefPtr<DialogValueHolder> mReturnValue;
 };
 
 /* factory function */
 inline already_AddRefed<nsGlobalWindow>
 NS_NewScriptGlobalObject(bool aIsChrome, bool aIsModalContentWindow)
 {
   nsRefPtr<nsGlobalWindow> global;
 
--- a/dom/base/nsJSEnvironment.cpp
+++ b/dom/base/nsJSEnvironment.cpp
@@ -1672,37 +1672,27 @@ nsJSContext::SetProperty(JS::Handle<JSOb
 
   Maybe<nsRootedJSValueArray> tempStorage;
 
   JS::Rooted<JSObject*> global(mContext, GetNativeGlobal());
   nsresult rv =
     ConvertSupportsTojsvals(aArgs, global, &argc, &argv, tempStorage);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  JS::Value vargs;
-
   // got the arguments, now attach them.
 
-  // window.dialogArguments is supposed to be an array if a JS array
-  // was passed to showModalDialog(), deal with that here.
-  if (strcmp(aPropName, "dialogArguments") == 0 && argc <= 1) {
-    vargs = argc ? argv[0] : JSVAL_VOID;
-  } else {
-    for (uint32_t i = 0; i < argc; ++i) {
-      if (!JS_WrapValue(mContext, &argv[i])) {
-        return NS_ERROR_FAILURE;
-      }
+  for (uint32_t i = 0; i < argc; ++i) {
+    if (!JS_WrapValue(mContext, &argv[i])) {
+      return NS_ERROR_FAILURE;
     }
-
-    JSObject *args = ::JS_NewArrayObject(mContext, argc, argv);
-    vargs = OBJECT_TO_JSVAL(args);
   }
 
-  // Make sure to use JS_DefineProperty here so that we can override
-  // readonly XPConnect properties here as well (read dialogArguments).
+  JSObject *args = ::JS_NewArrayObject(mContext, argc, argv);
+  JS::Value vargs = OBJECT_TO_JSVAL(args);
+
   return JS_DefineProperty(mContext, aTarget, aPropName, vargs, NULL, NULL, 0)
     ? NS_OK
     : NS_ERROR_FAILURE;
 }
 
 nsresult
 nsJSContext::ConvertSupportsTojsvals(nsISupports *aArgs,
                                      JS::Handle<JSObject*> aScope,
@@ -2539,82 +2529,16 @@ nsJSContext::ShrinkGCBuffersNow()
 {
   PROFILER_LABEL("GC", "ShrinkGCBuffersNow");
 
   KillShrinkGCBuffersTimer();
 
   JS::ShrinkGCBuffers(nsJSRuntime::sRuntime);
 }
 
-// Return true if there exists a JSContext with a default global whose current
-// inner is gray. The intent is to look for JS Object windows. We don't merge
-// system compartments, so we don't use them to trigger merging CCs.
-static bool
-AnyGrayCurrentContentInnerWindows()
-{
-  if (!nsJSRuntime::sRuntime) {
-    return false;
-  }
-  JSContext *iter = nullptr;
-  JSContext *cx;
-  while ((cx = JS_ContextIterator(nsJSRuntime::sRuntime, &iter))) {
-    // Skip anything without an nsIScriptContext, as well as any scx whose
-    // NativeGlobal() is not an outer window (this happens with XUL Prototype
-    // compilation scopes, for example, which we're not interested in).
-    nsIScriptContext *scx = GetScriptContextFromJSContext(cx);
-    JS::RootedObject global(cx, scx ? scx->GetNativeGlobal() : nullptr);
-    if (!global || !js::GetObjectParent(global)) {
-      continue;
-    }
-    // Grab the inner from the outer.
-    global = JS_ObjectToInnerObject(cx, global);
-    MOZ_ASSERT(!js::GetObjectParent(global));
-    if (JS::GCThingIsMarkedGray(global) &&
-        !js::IsSystemCompartment(js::GetObjectCompartment(global))) {
-      return true;
-    }
-  }
-  return false;
-}
-
-static bool
-DoMergingCC(bool aForced)
-{
-  // Don't merge too many times in a row, and do at least a minimum
-  // number of unmerged CCs in a row.
-  static const int32_t kMinConsecutiveUnmerged = 3;
-  static const int32_t kMaxConsecutiveMerged = 3;
-
-  static int32_t sUnmergedNeeded = 0;
-  static int32_t sMergedInARow = 0;
-
-  MOZ_ASSERT(0 <= sUnmergedNeeded && sUnmergedNeeded <= kMinConsecutiveUnmerged);
-  MOZ_ASSERT(0 <= sMergedInARow && sMergedInARow <= kMaxConsecutiveMerged);
-
-  if (sMergedInARow == kMaxConsecutiveMerged) {
-    MOZ_ASSERT(sUnmergedNeeded == 0);
-    sUnmergedNeeded = kMinConsecutiveUnmerged;
-  }
-
-  if (sUnmergedNeeded > 0) {
-    sUnmergedNeeded--;
-    sMergedInARow = 0;
-    return false;
-  }
-
-  if (!aForced && AnyGrayCurrentContentInnerWindows()) {
-    sMergedInARow++;
-    return true;
-  } else {
-    sMergedInARow = 0;
-    return false;
-  }
-
-}
-
 static void
 FinishAnyIncrementalGC()
 {
   if (sCCLockedOut) {
     // We're in the middle of an incremental GC, so finish it.
     JS::PrepareForIncrementalGC(nsJSRuntime::sRuntime);
     JS::FinishIncrementalGC(nsJSRuntime::sRuntime, JS::gcreason::CC_FORCED);
   }
@@ -2647,17 +2571,17 @@ TimeBetween(PRTime start, PRTime end)
   MOZ_ASSERT(end >= start);
   return (uint32_t)(end - start) / PR_USEC_PER_MSEC;
 }
 
 //static
 void
 nsJSContext::CycleCollectNow(nsICycleCollectorListener *aListener,
                              int32_t aExtraForgetSkippableCalls,
-                             bool aForced)
+                             bool aManuallyTriggered)
 {
   if (!NS_IsMainThread()) {
     return;
   }
 
   PROFILER_LABEL("CC", "CycleCollectNow");
 
   PRTime start = PR_Now();
@@ -2686,19 +2610,18 @@ nsJSContext::CycleCollectNow(nsICycleCol
     FireForgetSkippable(nsCycleCollector_suspectedCount(), false);
     ranSyncForgetSkippable = true;
   }
 
   PRTime endSkippableTime = PR_Now();
   uint32_t skippableDuration = TimeBetween(endGCTime, endSkippableTime);
 
   // Prepare to actually run the CC.
-  bool mergingCC = DoMergingCC(aForced);
   nsCycleCollectorResults ccResults;
-  nsCycleCollector_collect(mergingCC, &ccResults, aListener);
+  nsCycleCollector_collect(aManuallyTriggered, &ccResults, aListener);
   sCCollectedWaitingForGC += ccResults.mFreedRefCounted + ccResults.mFreedGCed;
 
   // If we collected a substantial amount of cycles, poke the GC since more objects
   // might be unreachable now.
   if (sCCollectedWaitingForGC > 250 ||
       sLikelyShortLivingObjectsNeedingGC > 2500) {
     PokeGC(JS::gcreason::CC_WAITING);
   }
@@ -2723,17 +2646,17 @@ nsJSContext::CycleCollectNow(nsICycleCol
   PRTime delta = GetCollectionTimeDelta();
 
   uint32_t cleanups = sForgetSkippableBeforeCC ? sForgetSkippableBeforeCC : 1;
   uint32_t minForgetSkippableTime = (sMinForgetSkippableTime == UINT32_MAX)
     ? 0 : sMinForgetSkippableTime;
 
   if (sPostGCEventsToConsole) {
     nsCString mergeMsg;
-    if (mergingCC) {
+    if (ccResults.mMergedZones) {
       mergeMsg.AssignLiteral(" merged");
     }
 
     nsCString gcMsg;
     if (ccResults.mForcedGC) {
       gcMsg.AssignLiteral(", forced a GC");
     }
 
--- a/dom/base/nsJSEnvironment.h
+++ b/dom/base/nsJSEnvironment.h
@@ -124,17 +124,17 @@ public:
                                 IsCompartment aCompartment = NonCompartmentGC,
                                 IsShrinking aShrinking = NonShrinkingGC,
                                 int64_t aSliceMillis = 0);
   static void ShrinkGCBuffersNow();
   // If aExtraForgetSkippableCalls is -1, forgetSkippable won't be
   // called even if the previous collection was GC.
   static void CycleCollectNow(nsICycleCollectorListener *aListener = nullptr,
                               int32_t aExtraForgetSkippableCalls = 0,
-                              bool aForced = true);
+                              bool aManuallyTriggered = true);
 
   static void PokeGC(JS::gcreason::Reason aReason, int aDelay = 0);
   static void KillGCTimer();
 
   static void PokeShrinkGCBuffers();
   static void KillShrinkGCBuffersTimer();
 
   static void MaybePokeCC();
--- a/dom/base/nsPIDOMWindow.h
+++ b/dom/base/nsPIDOMWindow.h
@@ -53,18 +53,18 @@ class nsPIWindowRoot;
 
 namespace mozilla {
 namespace dom {
 class AudioContext;
 }
 }
 
 #define NS_PIDOMWINDOW_IID \
-{ 0x81fe131f, 0x57c9, 0x4992, \
-  { 0xa7, 0xad, 0x82, 0x67, 0x3f, 0xc4, 0xe2, 0x53 } }
+{ 0x7202842a, 0x0e24, 0x46dc, \
+  { 0xb2, 0x25, 0xd2, 0x9d, 0x28, 0xda, 0x87, 0xd8 } }
 
 class nsPIDOMWindow : public nsIDOMWindowInternal
 {
 public:
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_PIDOMWINDOW_IID)
 
   virtual nsPIDOMWindow* GetPrivateRoot() = 0;
 
@@ -598,21 +598,24 @@ public:
   /**
    * Tell this window that there is an observer for gamepad input
    */
   virtual void SetHasGamepadEventListener(bool aHasGamepad = true) = 0;
 
   /**
    * Set a arguments for this window. This will be set on the window
    * right away (if there's an existing document) and it will also be
-   * installed on the window when the next document is loaded. Each
-   * language impl is responsible for converting to an array of args
-   * as appropriate for that language.
+   * installed on the window when the next document is loaded.
+   *
+   * This function serves double-duty for passing both |arguments| and
+   * |dialogArguments| back from nsWindowWatcher to nsGlobalWindow. For the
+   * latter, the array is an array of length 0 whose only element is a
+   * DialogArgumentsHolder representing the JS value passed to showModalDialog.
    */
-  virtual nsresult SetArguments(nsIArray *aArguments, nsIPrincipal *aOrigin) = 0;
+  virtual nsresult SetArguments(nsIArray *aArguments) = 0;
 
   /**
    * NOTE! This function *will* be called on multiple threads so the
    * implementation must not do any AddRef/Release or other actions that will
    * mutate internal state.
    */
   virtual uint32_t GetSerial() = 0;
 
--- a/dom/bindings/BindingUtils.h
+++ b/dom/bindings/BindingUtils.h
@@ -1717,17 +1717,17 @@ public:
   }
 
   void SetSequence(InfallibleTArray<T>* aSequence)
   {
     mInfallibleArray = aSequence;
     mSequenceType = eInfallibleArray;
   }
 
-  void SetSequence(Nullable<nsTArray<T>>* aSequence)
+  void SetSequence(Nullable<nsTArray<T> >* aSequence)
   {
     mNullableArray = aSequence;
     mSequenceType = eNullableArray;
   }
 
  private:
   enum SequenceType {
     eNone,
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -1414,16 +1414,31 @@ class PropertyDefiner:
 def overloadLength(arguments):
     i = len(arguments)
     while i > 0 and arguments[i - 1].optional:
         i -= 1
     return i
 def methodLength(method):
     signatures = method.signatures()
     return min(overloadLength(arguments) for (retType, arguments) in signatures)
+def requiresQueryInterfaceMethod(descriptor):
+    # Make sure to not stick QueryInterface on abstract interfaces that
+    # have hasXPConnectImpls (like EventTarget).  So only put it on
+    # interfaces that are concrete and all of whose ancestors are abstract.
+    def allAncestorsAbstract(iface):
+        if not iface.parent:
+            return True
+        desc = descriptor.getDescriptor(iface.parent.identifier.name)
+        if desc.concrete:
+            return False
+        return allAncestorsAbstract(iface.parent)
+    return (descriptor.nativeOwnership == 'nsisupports' and
+            descriptor.interface.hasInterfacePrototypeObject() and
+            descriptor.concrete and
+            allAncestorsAbstract(descriptor.interface))
 
 class MethodDefiner(PropertyDefiner):
     """
     A class for defining methods on a prototype object.
     """
     def __init__(self, descriptor, name, static):
         PropertyDefiner.__init__(self, descriptor, name)
 
@@ -1455,31 +1470,17 @@ class MethodDefiner(PropertyDefiner):
         if any(m.isGetter() and m.isIndexed() for m in methods):
             self.regular.append({"name": 'iterator',
                                  "methodInfo": False,
                                  "nativeName": "JS_ArrayIterator",
                                  "length": 0,
                                  "flags": "JSPROP_ENUMERATE",
                                  "condition": MemberCondition(None, None) })
 
-        # Make sure to not stick QueryInterface on abstract interfaces that
-        # have hasXPConnectImpls (like EventTarget).  So only put it on
-        # interfaces that are concrete and all of whose ancestors are abstract.
-        def allAncestorsAbstract(iface):
-            if not iface.parent:
-                return True
-            desc = descriptor.getDescriptor(iface.parent.identifier.name)
-            if desc.concrete:
-                return False
-            return allAncestorsAbstract(iface.parent)
-        if (not static and
-            descriptor.nativeOwnership == 'nsisupports' and
-            descriptor.interface.hasInterfacePrototypeObject() and
-            descriptor.concrete and
-            allAncestorsAbstract(descriptor.interface)):
+        if not static and requiresQueryInterfaceMethod(descriptor):
             self.regular.append({"name": 'QueryInterface',
                                  "methodInfo": False,
                                  "length": 1,
                                  "flags": "0",
                                  "condition":
                                      MemberCondition(None,
                                                      "nsINode::IsChromeOrXBL") })
 
@@ -4370,17 +4371,17 @@ def wrapTypeIntoCurrentCompartment(type,
             myDict = myDict.parent
         return CGList(memberWraps, "\n") if len(memberWraps) != 0 else None
 
     if type.isUnion():
         raise TypeError("Can't handle wrapping of unions in constructor "
                         "arguments yet")
 
     if (type.isString() or type.isPrimitive() or type.isEnum() or
-        type.isGeckoInterface() or type.isCallback()):
+        type.isGeckoInterface() or type.isCallback() or type.isDate()):
         # All of these don't need wrapping
         return None
 
     raise TypeError("Unknown type; we don't know how to wrap it in constructor "
                     "arguments: %s" % type)
 
 def wrapArgIntoCurrentCompartment(arg, value, isMember=True):
     """
@@ -8063,16 +8064,24 @@ class CGBindingRoot(CGThing):
         descriptors = config.getDescriptors(webIDLFile=webIDLFile,
                                             hasInterfaceOrInterfacePrototypeObject=True,
                                             skipGen=False)
         def descriptorRequiresPreferences(desc):
             iface = desc.interface
             return any(m.getExtendedAttribute("Pref") for m in iface.members + [iface]);
         requiresPreferences = any(descriptorRequiresPreferences(d) for d in descriptors)
         hasOwnedDescriptors = any(d.nativeOwnership == 'owned' for d in descriptors)
+        def descriptorRequiresContentUtils(desc):
+            return ((desc.concrete and not desc.proxy and
+                     not desc.workers and desc.wrapperCache) or
+                    desc.interface.hasInterfaceObject())
+        requiresContentUtils = any(descriptorRequiresContentUtils(d) for d in descriptors)
+        def descriptorHasChromeOnlyMembers(desc):
+            return any(isChromeOnly(a) for a in desc.interface.members)
+        hasChromeOnlyMembers = any(descriptorHasChromeOnlyMembers(d) for d in descriptors)
         hasWorkerStuff = len(config.getDescriptors(webIDLFile=webIDLFile,
                                                    workers=True)) != 0
         mainDictionaries = config.getDictionaries(webIDLFile=webIDLFile,
                                                   workers=False)
         workerDictionaries = config.getDictionaries(webIDLFile=webIDLFile,
                                                     workers=True)
         mainCallbacks = config.getCallbacks(webIDLFile=webIDLFile,
                                             workers=False)
@@ -8168,26 +8177,27 @@ class CGBindingRoot(CGThing):
                           'mozilla/ErrorResult.h',
                           'mozilla/dom/DOMJSClass.h',
                           'mozilla/dom/DOMJSProxyHandler.h'],
                          ['mozilla/dom/BindingUtils.h',
                           'mozilla/dom/Nullable.h',
                           'PrimitiveConversions.h',
                           'XPCQuickStubs.h',
                           'XPCWrapper.h',
+                          'WrapperFactory.h',
                           'nsDOMQS.h',
-                          'AccessCheck.h',
-                          'nsContentUtils.h',
                           # Have to include nsDOMQS.h to get fast arg unwrapping
                           # for old-binding things with castability.
                           'nsDOMQS.h'
                           ] + (['WorkerPrivate.h',
                                 'nsThreadUtils.h'] if hasWorkerStuff else [])
                             + (['mozilla/Preferences.h'] if requiresPreferences else [])
-                            + (['mozilla/dom/NonRefcountedDOMObject.h'] if hasOwnedDescriptors else []),
+                            + (['mozilla/dom/NonRefcountedDOMObject.h'] if hasOwnedDescriptors else [])
+                            + (['nsContentUtils.h'] if requiresContentUtils else [])
+                            + (['AccessCheck.h'] if hasChromeOnlyMembers else []),
                          curr,
                          config,
                          jsImplemented)
 
         # Add include guards.
         curr = CGIncludeGuard(prefix, curr)
 
         # Add the auto-generated comment.
--- a/dom/bindings/test/TestBindingHeader.h
+++ b/dom/bindings/test/TestBindingHeader.h
@@ -116,16 +116,20 @@ public:
     Constructor(const GlobalObject&, uint32_t, const Nullable<bool>&,
                 ErrorResult&);
   static
   already_AddRefed<TestInterface>
     Constructor(const GlobalObject&, TestInterface*, ErrorResult&);
   static
   already_AddRefed<TestInterface>
     Constructor(const GlobalObject&, uint32_t, IndirectlyImplementedInterface&, ErrorResult&);
+
+  static
+  already_AddRefed<TestInterface>
+    Constructor(const GlobalObject&, Date&, ErrorResult&);
   /*  static
   already_AddRefed<TestInterface>
     Constructor(const GlobalObject&, uint32_t, uint32_t,
                 const TestInterfaceOrOnlyForUseInConstructor&, ErrorResult&);
   */
 
   static
   already_AddRefed<TestInterface> Test(const GlobalObject&, ErrorResult&);
--- a/dom/bindings/test/TestCodeGen.webidl
+++ b/dom/bindings/test/TestCodeGen.webidl
@@ -96,16 +96,17 @@ TestInterface implements ImplementedInte
 interface OnlyForUseInConstructor {
 };
 
 [Constructor,
  Constructor(DOMString str),
  Constructor(unsigned long num, boolean? boolArg),
  Constructor(TestInterface? iface),
  Constructor(long arg1, IndirectlyImplementedInterface iface),
+ Constructor(Date arg1),
  // Constructor(long arg1, long arg2, (TestInterface or OnlyForUseInConstructor) arg3),
  NamedConstructor=Test,
  NamedConstructor=Test(DOMString str),
  NamedConstructor=Test2(DictForConstructor dict, any any1, object obj1,
                         object? obj2, sequence<Dict> seq, optional any any2,
                         optional object obj3, optional object? obj4)
  ]
 interface TestInterface {
--- a/dom/interfaces/base/nsIDOMModalContentWindow.idl
+++ b/dom/interfaces/base/nsIDOMModalContentWindow.idl
@@ -3,23 +3,24 @@
  * 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 "nsISupports.idl"
 
 interface nsIVariant;
 interface nsIArray;
 
-[scriptable, uuid(51aebd45-b979-4ec6-9d11-3a3fd3d5d59e)]
+[scriptable, uuid(3f4cb2d0-5f7e-44a9-9f4f-370945f8db08)]
 interface nsIDOMModalContentWindow : nsISupports
 {
   /**
-   * Readonly attribute containing an array of arguments that was
-   * passed to the code that opened this modal content window.
+   * Readonly attribute containing an arbitrary JS value passed by the
+   * code that opened the modal content window. A security check is
+   * performed at access time, per spec.
    */
-  readonly attribute nsIArray            dialogArguments;
+  readonly attribute nsIVariant            dialogArguments;
 
   /**
    * The return value that will be returned to the function that
    * opened the modal content window.
    */
   attribute nsIVariant                   returnValue;
 };
--- a/dom/interfaces/base/nsIDOMWindow.idl
+++ b/dom/interfaces/base/nsIDOMWindow.idl
@@ -20,17 +20,17 @@ interface nsIVariant;
  * The nsIDOMWindow interface is the primary interface for a DOM
  * window object. It represents a single window object that may
  * contain child windows if the document in the window contains a
  * HTML frameset document or if the document contains iframe elements.
  *
  * @see <http://www.whatwg.org/html/#window>
  */
 
-[scriptable, uuid(b4cf7214-5385-4f72-aa83-f8d1ff17252b)]
+[scriptable, uuid(e0f33b20-72ef-415b-9ff2-8bf176f581f8)]
 interface nsIDOMWindow : nsISupports
 {
   // the current browsing context
   readonly attribute nsIDOMWindow                       window;
 
   /* [replaceable] self */
   readonly attribute nsIDOMWindow                       self;
 
@@ -189,16 +189,17 @@ interface nsIDOMWindow : nsISupports
   boolean                   confirm([optional] in DOMString text);
 
   // prompt() should return a null string if cancel is pressed
   DOMString                 prompt([optional] in DOMString aMessage,
                                    [optional] in DOMString aInitial);
 
   void                      print();
 
+  [optional_argc]
   nsIVariant                showModalDialog(in DOMString aURI,
                                             [optional] in nsIVariant aArgs,
                                             [optional] in DOMString aOptions);
 
 
   // cross-document messaging
   /**
    * Implements a safe message-passing system which can cross same-origin
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -21,16 +21,17 @@
 #include "AudioChannelService.h"
 #include "CrashReporterParent.h"
 #include "IHistory.h"
 #include "IDBFactory.h"
 #include "IndexedDBParent.h"
 #include "IndexedDatabaseManager.h"
 #include "mozIApplication.h"
 #include "mozilla/ClearOnShutdown.h"
+#include "mozilla/dom/Element.h"
 #include "mozilla/dom/ExternalHelperAppParent.h"
 #include "mozilla/dom/PMemoryReportRequestParent.h"
 #include "mozilla/dom/power/PowerManagerService.h"
 #include "mozilla/dom/DOMStorageIPC.h"
 #include "mozilla/dom/bluetooth/PBluetoothParent.h"
 #include "mozilla/dom/devicestorage/DeviceStorageRequestParent.h"
 #include "SmsParent.h"
 #include "mozilla/Hal.h"
--- a/dom/ipc/ProcessPriorityManager.cpp
+++ b/dom/ipc/ProcessPriorityManager.cpp
@@ -12,16 +12,17 @@
 #include "mozilla/Preferences.h"
 #include "mozilla/Services.h"
 #include "mozilla/unused.h"
 #include "AudioChannelService.h"
 #include "prlog.h"
 #include "nsPrintfCString.h"
 #include "nsWeakPtr.h"
 #include "nsXULAppAPI.h"
+#include "nsIFrameLoader.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsITimer.h"
 #include "nsIObserver.h"
 #include "nsIObserverService.h"
 #include "nsIDocument.h"
 #include "nsIDOMEventListener.h"
 #include "nsIDOMWindow.h"
 #include "nsIDOMEvent.h"
--- a/dom/media/MediaManager.cpp
+++ b/dom/media/MediaManager.cpp
@@ -7,16 +7,17 @@
 #include "MediaStreamGraph.h"
 #include "nsIDOMFile.h"
 #include "nsIEventTarget.h"
 #include "nsIUUIDGenerator.h"
 #include "nsIScriptGlobalObject.h"
 #include "nsIPopupWindowManager.h"
 #include "nsISupportsArray.h"
 #include "nsIDocShell.h"
+#include "nsIDocument.h"
 
 // For PR_snprintf
 #include "prprf.h"
 
 #include "nsJSUtils.h"
 #include "nsDOMFile.h"
 #include "nsGlobalWindow.h"
 
--- a/dom/mobilemessage/src/ipc/SmsParent.cpp
+++ b/dom/mobilemessage/src/ipc/SmsParent.cpp
@@ -16,16 +16,17 @@
 #include "MmsMessage.h"
 #include "nsIMobileMessageDatabaseService.h"
 #include "SmsFilter.h"
 #include "SmsSegmentInfo.h"
 #include "MobileMessageThread.h"
 #include "nsIDOMFile.h"
 #include "mozilla/dom/ipc/Blob.h"
 #include "mozilla/dom/ContentParent.h"
+#include "nsContentUtils.h"
 #include "nsTArrayHelpers.h"
 
 namespace mozilla {
 namespace dom {
 namespace mobilemessage {
 
 static JSObject*
 MmsAttachmentDataToJSObject(JSContext* aContext,
--- a/dom/plugins/ipc/PluginIdentifierParent.cpp
+++ b/dom/plugins/ipc/PluginIdentifierParent.cpp
@@ -1,18 +1,19 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  * vim: sw=2 ts=2 et :
  * 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 "PluginIdentifierParent.h"
 
+#include "nsContentUtils.h"
+#include "nsNPAPIPlugin.h"
 #include "nsServiceManagerUtils.h"
-#include "nsNPAPIPlugin.h"
 #include "PluginScriptableObjectUtils.h"
 #include "mozilla/unused.h"
 
 using namespace mozilla::plugins::parent;
 
 namespace mozilla {
 namespace plugins {
 
--- a/dom/power/WakeLock.cpp
+++ b/dom/power/WakeLock.cpp
@@ -3,17 +3,19 @@
  * 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 "WakeLock.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/Hal.h"
 #include "mozilla/HalWakeLock.h"
 #include "nsDOMClassInfoID.h"
+#include "nsDOMEvent.h"
 #include "nsError.h"
+#include "nsIDocument.h"
 #include "nsIDOMWindow.h"
 #include "nsIDOMEvent.h"
 #include "nsPIDOMWindow.h"
 #include "PowerManager.h"
 
 DOMCI_DATA(MozWakeLock, mozilla::dom::power::WakeLock)
 
 using namespace mozilla::hal;
--- a/dom/tests/mochitest/bugs/file_bug504862.html
+++ b/dom/tests/mochitest/bugs/file_bug504862.html
@@ -3,18 +3,18 @@
 <script>
 window.returnValue = 3;
 
 if (location.toString().match(/^http:\/\/mochi.test:8888/)) {
   // Test that we got the right arguments.
   opener.is(window.dialogArguments, "my args",
             "dialog did not get the right arguments.");
 
-  // Load a different url, and test that it doesn't see the arguments.
-  window.location="data:text/html,<html><body onload=\"opener.is(window.dialogArguments, null, 'subsequent dialog document did not get the right arguments.'); close();\">';";
+  // Load a different url, and test that it sees the arguments (since it's same origin).
+  window.location="data:text/html,<html><body onload=\"opener.is(window.dialogArguments, 'my args', 'subsequent dialog document did not get the right arguments.'); close();\">';";
 } else {
   // Post a message containing our arguments to the opener to test
   // that this cross origing dialog does *not* see the passed in
   // arguments.
   opener.postMessage("args: " + window.dialogArguments,
                      "http://mochi.test:8888");
 
   close();
--- a/dom/tests/mochitest/bugs/test_bug291653.html
+++ b/dom/tests/mochitest/bugs/test_bug291653.html
@@ -12,41 +12,30 @@ https://bugzilla.mozilla.org/show_bug.cg
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=291653">Mozilla Bug 291653</a>
 <p id="display"></p>
 <div id="content" style="display: none">
   
 </div>
 <pre id="test">
 <script type="application/javascript">
 
-if (!navigator.platform.startsWith("Win")) {
-  SimpleTest.expectAssertions(1);
-}
-
 /** Test for Bug 291653 **/
 SimpleTest.waitForExplicitFinish();
 
 var secondListenerDidRun = false;
 
 var w = window.open("file_bug291653.html", "foo", "width=300,height=300");
 
 function closeTest() {
   w.setTimeout("close()", 0);
   setTimeout("finish()", 500);
 }
 
 function finish() {
   ok(!secondListenerDidRun, "Shouldn't have run second listener!");
-
-  // Garbage collecting the windows created in this test can cause
-  // assertions, so GC now to blame those assertions to this test.
-  // ("mArguments wasn't cleaned up properly!" in ~nsGlobalWindow,
-  // bug 600703)
-  SpecialPowers.gc();
-
   SimpleTest.finish();
 }
 
 function end() {
   setTimeout("closeTest()", 500);
 }
 
 
--- a/dom/tests/mochitest/bugs/test_bug406375.html
+++ b/dom/tests/mochitest/bugs/test_bug406375.html
@@ -13,35 +13,27 @@ https://bugzilla.mozilla.org/show_bug.cg
 <p id="display"></p>
 <div id="content" style="display: none">
   
 </div>
 <pre id="test">
 <script type="application/javascript">
 
 if (navigator.platform.startsWith("Mac")) {
-  SimpleTest.expectAssertions(3);
-} else if (navigator.platform.startsWith("Linux")) {
-  SimpleTest.expectAssertions(1);
+  SimpleTest.expectAssertions(2);
 }
 
 /** Test for Bug 406375 **/
 
 
 SimpleTest.waitForExplicitFinish();
 
 function runTest() {
   window.showModalDialog("file_bug406375.html");
   ok(true, "This test should not hang");
 
-  // Garbage collecting the windows created in this test can cause
-  // assertions, so GC now to blame those assertions to this test.
-  // ("mArguments wasn't cleaned up properly!" in ~nsGlobalWindow,
-  // bug 600703)
-  SpecialPowers.gc();
-
   SimpleTest.finish();
 }
 
 </script>
 </pre>
 </body>
 </html>
--- a/dom/tests/mochitest/bugs/test_bug414291.html
+++ b/dom/tests/mochitest/bugs/test_bug414291.html
@@ -7,37 +7,27 @@ https://bugzilla.mozilla.org/show_bug.cg
   <title>Test for Bug 414291</title>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=414291">Mozilla Bug 414291</a>
 <script class="testbody" type="text/javascript">
 
-if (!navigator.platform.startsWith("Win")) {
-  SimpleTest.expectAssertions(1);
-}
-
 /** Test for Bug 414291 **/
 
 var result1 = 0;
 var result2 = 0;
 var result3 = 0;
 
 window.open("data:text/html,<html><body onload='close(); opener.result1 = 1;'>", "w1");
 is(result1, 0, "window either opened as modal or loaded synchronously.");
 
 window.open("data:text/html,<html><body onload='close(); opener.result2 = 2;'>", "w2", "modal=yes");
 is(result2, 0, "window either opened as modal or data loaded synchronously.");
 
 result3 = window.showModalDialog("data:text/html,<html><body onload='close(); returnValue = 3;'>");
 is(result3, 3, "window didn't open as modal.");
 
-// Garbage collecting the windows created in this test can cause
-// assertions, so GC now to blame those assertions to this test.
-// ("mArguments wasn't cleaned up properly!" in ~nsGlobalWindow,
-// bug 600703)
-SpecialPowers.gc();
-
 </script>
 </pre>
 </body>
 </html>
--- a/dom/tests/mochitest/bugs/test_bug437361.html
+++ b/dom/tests/mochitest/bugs/test_bug437361.html
@@ -5,27 +5,23 @@ https://bugzilla.mozilla.org/show_bug.cg
 -->
 <head>
   <title>Test for Bug 437361</title>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
   
   <script class="testbody" type="text/javascript">
 
-  if (!navigator.platform.startsWith("Win")) {
-    SimpleTest.expectAssertions(1);
-  }
-
   /** Test for Bug 437361 **/
 
   function testModalDialogBlockedCleanly() {
     is(true, SpecialPowers.getBoolPref("dom.disable_open_during_load"), "mozprefs sanity check");
     var rv = window.showModalDialog( // should be blocked without exception
       "data:text/html,<html><body onload='close(); returnValue = 1;' /></html>");
-    is(rv, null, "Modal dialog opened unexpectedly.");
+    is(rv, undefined, "Modal dialog opened unexpectedly.");
   }
   
   function testModalDialogAllowed() {
     is(false, SpecialPowers.getBoolPref("dom.disable_open_during_load"), "mozprefs sanity check");
     var rv = window.showModalDialog( // should not be blocked this time
       "data:text/html,<html><body onload='close(); returnValue = 1;' /></html>");
     is(rv, 1, "Problem with modal dialog returnValue.");
   }
@@ -53,22 +49,16 @@ https://bugzilla.mozilla.org/show_bug.cg
        "Blocked showModalDialog caused an exception.");
        
   test(false, false, testModalDialogAllowed,
        "showModalDialog was blocked even though dom.disable_open_during_load was false.");
 
   test(false, true, testOtherExceptionsNotTrapped,
        "Incorrectly suppressed insecure showModalDialog exception.");
 
-  // Garbage collecting the windows created in this test can cause
-  // assertions, so GC now to blame those assertions to this test.
-  // ("mArguments wasn't cleaned up properly!" in ~nsGlobalWindow,
-  // bug 600703)
-  SpecialPowers.gc();
-
   </script>
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=437361">Mozilla Bug 437361</a>
 <p id="display"></p>
 <div id="content" style="display: none"> 
 </div>
 <pre id="test">
--- a/dom/tests/mochitest/bugs/test_bug479143.html
+++ b/dom/tests/mochitest/bugs/test_bug479143.html
@@ -11,20 +11,16 @@ https://bugzilla.mozilla.org/show_bug.cg
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=479143">Mozilla Bug 479143</a>
 <p id="display"></p>
 <div id="content" style="display: none"></div>
 
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
-  if (!navigator.platform.startsWith("Win")) {
-    SimpleTest.expectAssertions(1);
-  }
-
   SimpleTest.waitForExplicitFinish();
 
   setTimeout(function() {
     var interval = setInterval(function() { var i = 0; i++; }, 10);
 
     var xhr = new XMLHttpRequest();
     xhr.open("GET", "test_bug479143.html", false);
     xhr.send(null);
@@ -32,21 +28,15 @@ https://bugzilla.mozilla.org/show_bug.cg
     window.showModalDialog("javascript:" +
                            "setTimeout(function() { window.close(); }, 1000);",
                            null);
 
     clearInterval(interval);
 
     ok(true, "did not crash");
 
-    // Garbage collecting the windows created in this test can cause
-    // assertions, so GC now to blame those assertions to this test.
-    // ("mArguments wasn't cleaned up properly!" in ~nsGlobalWindow,
-    // bug 600703)
-    SpecialPowers.gc();
-
     SimpleTest.finish();
   }, 0);
 
 </script>
 </pre>
 </body>
 </html>
--- a/dom/tests/mochitest/bugs/test_bug504862.html
+++ b/dom/tests/mochitest/bugs/test_bug504862.html
@@ -7,42 +7,35 @@ https://bugzilla.mozilla.org/show_bug.cg
   <title>Test for Bug 504862</title>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body onload="runTest()">
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=504862">Mozilla Bug 504862</a>
 <script class="testbody" type="text/javascript">
 
-if (!navigator.platform.startsWith("Win")) {
-  SimpleTest.expectAssertions(4);
-}
-
 /** Test for Bug 504862 **/
 SimpleTest.waitForExplicitFinish();
 function onMsgRcv(event)
 {
   is(event.data, "args: undefined", "Unexpected cross origin dialog arguments.");
 }
 
 function runTest() {
   window.addEventListener("message", onMsgRcv, false);
 
   var result = window.showModalDialog("file_bug504862.html", "my args");
-  is(result, null, "window sees previous dialog documents return value.");
+  // NB: We used to clear returnValue on each navigation, but now we do a
+  // security check on access, so we can safely make returnValue live on
+  // the browsing context, per spec.
+  is(result, 3, "window sees previous dialog documents return value.");
 
   result = window.showModalDialog("http://test1.example.com/tests/dom/tests/mochitest/bugs/file_bug504862.html", "my args");
 
   is(result, null, "Able to see return value from cross origin dialog.");
 
-  // Garbage collecting the windows created in this test can cause
-  // assertions, so GC now to blame those assertions to this test.
-  // ("mArguments wasn't cleaned up properly!" in ~nsGlobalWindow,
-  // bug 600703)
-  SpecialPowers.gc();
-
   SimpleTest.finish();
 }
 
 </script>
 </pre>
 </body>
 </html>
--- a/dom/tests/mochitest/bugs/test_bug61098.html
+++ b/dom/tests/mochitest/bugs/test_bug61098.html
@@ -16,22 +16,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 </p>
 <div id="content" style="display: none"> 
 </div>
 <pre id="test">
 </pre>
 <script class="testbody" type="text/javascript">
 /** Test for Bug 61098 **/
 
-if (!navigator.platform.startsWith("Win")) {
-  SimpleTest.expectAssertions(9);
-} else {
-  SimpleTest.expectAssertions(8);
-}
-
+SimpleTest.expectAssertions(8);
 SimpleTest.waitForExplicitFinish();
 
 var mockPromptServiceRegisterer, mockPromptFactoryRegisterer;
 
 var promptState;
 
 function registerMockPromptService()
 {
@@ -346,20 +341,14 @@ function runtests()
 
   w.close();
 
   resetDialogLoopBlocking();
 
   mockPromptFactoryRegisterer.unregister();
   mockPromptServiceRegisterer.unregister();
 
-  // Garbage collecting the windows created in this test can cause
-  // assertions, so GC now to blame those assertions to this test.
-  // ("mArguments wasn't cleaned up properly!" in ~nsGlobalWindow,
-  // bug 600703)
-  SpecialPowers.gc();
-
   SimpleTest.finish();
 }
 
 </script>
 </body>
 </html>
--- a/dom/tests/mochitest/general/file_showModalDialog.html
+++ b/dom/tests/mochitest/general/file_showModalDialog.html
@@ -1,19 +1,35 @@
 <!DOCTYPE html>
 <html>
 <head>
 <script>
-  ok = window.opener.ok;
-  is = window.opener.is;
-  SpecialPowers = window.opener.SpecialPowers;
   function go() {
+    is(SpecialPowers.wrap(window).location.toString(), location.toString(), "sanity");
     is(SpecialPowers.Cu.getClassName(window, /* aUnwrap = */ true), "ModalContentWindow", "We are modal");
     var iwin = document.getElementById('ifr').contentWindow;
     is(SpecialPowers.Cu.getClassName(iwin, /* aUnwrap = */ true), "Window", "Descendant frames should not be modal");
-    window.close();
+
+    if (location.origin != "http://mochi.test:8888") {
+      is(window.dialogArguments, undefined,
+        "dialogArguments should be undefined cross-origin: " + location.origin);
+    }
+
+    window.returnValue = "rv: " + window.dialogArguments;
+
+    // Allow for testing navigations in series.
+    if (location.search == "") {
+      window.close();
+    } else {
+      var origins = location.search.split('?')[1].split(',');
+      var newsearch = '?' + origins.splice(1).join(',');
+      var newurl = location.toString().replace(location.origin, origins[0])
+                                      .replace(location.search, newsearch);
+      location = newurl;
+    }
+
   }
 </script>
 </head>
-<body onload="go();">
+<body onload="opener.postMessage('dosetup', '*');">
 <iframe id="ifr"></iframe>
 </body>
 </html>
--- a/dom/tests/mochitest/general/test_showModalDialog.html
+++ b/dom/tests/mochitest/general/test_showModalDialog.html
@@ -5,23 +5,50 @@ https://bugzilla.mozilla.org/show_bug.cg
 -->
 <head>
   <meta charset="utf-8">
   <title>Test for Bug 862918</title>
   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
   <script type="application/javascript">
 
-  // This will be fixed in bug 860941.
-  SimpleTest.expectAssertions(0, 1);
+  /** Test for window.showModalDialog. **/
+
+  // The modal window needs to touch Cu, which means that it needs
+  // SpecialPowers. But given the semantics of modal windows, we have to
+  // do some funny stuff with postMessage to set this up.
+  window.onmessage = function(evt) {
+    is(evt.data, 'dosetup', "message from modal window is correct");
+    var win = SpecialPowers.wrap(evt.source);
+    win.wrappedJSObject.SpecialPowers = SpecialPowers;
+    win.wrappedJSObject.is = is;
+    win.wrappedJSObject.ok = ok;
+    win.wrappedJSObject.go();
+  };
 
-  /** Test for window.showModalDialog. **/
-  window.showModalDialog('file_showModalDialog.html');
+  var someObj = { foo: 42, bar: "hi"};
+  var xurl = location.toString()
+                     .replace('mochi.test:8888', 'example.org')
+                     .replace('test_showModal', 'file_showModal');
+  if (xurl.indexOf('?') != -1)
+    xurl = xurl.substring(0, xurl.indexOf('?'));
+  is(showModalDialog('file_showModalDialog.html'), "rv: undefined");
+  is(showModalDialog(xurl), undefined);
+  is(showModalDialog('file_showModalDialog.html', 3), "rv: 3");
+  is(showModalDialog(xurl, 3), undefined);
+  is(showModalDialog('file_showModalDialog.html', someObj), "rv: " + someObj);
+  is(showModalDialog(xurl, someObj), undefined);
 
-  // Blame mArguments assertion on this test.
+  // Test sequential navigations.
+  is(showModalDialog('file_showModalDialog.html?http://mochi.test:8888', 4),
+                     'rv: 4');
+  is(showModalDialog('file_showModalDialog.html?http://example.com', 4), undefined);
+  is(showModalDialog('file_showModalDialog.html?http://example.com,http://mochi.test:8888', 4), 'rv: 4');
+
+  // This test used to assert after gc. Make sure it doesn't.
   SpecialPowers.gc();
 
   </script>
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=862918">Mozilla Bug 862918</a>
 <p id="display"></p>
 <div id="content" style="display: none">
--- a/dom/time/TimeChangeObserver.cpp
+++ b/dom/time/TimeChangeObserver.cpp
@@ -6,16 +6,17 @@
 #include "TimeChangeObserver.h"
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/Services.h"
 #include "mozilla/StaticPtr.h"
 #include "nsPIDOMWindow.h"
 #include "nsDOMEvent.h"
 #include "nsContentUtils.h"
 #include "nsIObserverService.h"
+#include "nsIDocument.h"
 
 using namespace mozilla;
 using namespace mozilla::hal;
 using namespace mozilla::services;
 
 StaticAutoPtr<nsSystemTimeChangeObserver> sObserver;
 
 nsSystemTimeChangeObserver* nsSystemTimeChangeObserver::GetInstance()
--- a/embedding/browser/webBrowser/nsIWebBrowserChrome.idl
+++ b/embedding/browser/webBrowser/nsIWebBrowserChrome.idl
@@ -72,16 +72,19 @@ interface nsIWebBrowserChrome : nsISuppo
     //
     // CHROME_PRIVATE_LIFETIME causes the docshell to affect private-browsing
     // session lifetime.  This flag is currently respected only for remote
     // docshells.
     const unsigned long CHROME_PRIVATE_WINDOW         = 0x00010000;
     const unsigned long CHROME_NON_PRIVATE_WINDOW     = 0x00020000;
     const unsigned long CHROME_PRIVATE_LIFETIME       = 0x00040000;
 
+    // Whether this was opened by nsGlobalWindow::ShowModalDialog.
+    const unsigned long CHROME_MODAL_CONTENT_WINDOW   = 0x00080000;
+
     // Prevents new window animations on Mac OS X Lion.  Ignored on other
     // platforms.
     const unsigned long CHROME_MAC_SUPPRESS_ANIMATION = 0x01000000;
 
     const unsigned long CHROME_WINDOW_RAISED          = 0x02000000;
     const unsigned long CHROME_WINDOW_LOWERED         = 0x04000000;
     const unsigned long CHROME_CENTER_SCREEN          = 0x08000000;
 
--- a/embedding/components/windowwatcher/src/nsWindowWatcher.cpp
+++ b/embedding/components/windowwatcher/src/nsWindowWatcher.cpp
@@ -499,39 +499,32 @@ nsWindowWatcher::OpenWindowInternal(nsID
 
   // If we're not called through our JS version of the API, and we got
   // our internal modal option, treat the window we're opening as a
   // modal content window (and set the modal chrome flag).
   if (!aCalledFromJS && argv &&
       WinHasOption(features.get(), "-moz-internal-modal", 0, nullptr)) {
     windowIsModalContentDialog = true;
 
+    // CHROME_MODAL gets inherited by dependent windows, which affects various
+    // platform-specific window state (especially on OSX). So we need some way
+    // to determine that this window was actually opened by nsGlobalWindow::
+    // ShowModalDialog(), and that somebody is actually going to be watching
+    // for return values and all that.
+    chromeFlags |= nsIWebBrowserChrome::CHROME_MODAL_CONTENT_WINDOW;
     chromeFlags |= nsIWebBrowserChrome::CHROME_MODAL;
   }
 
   SizeSpec sizeSpec;
   CalcSizeSpec(features.get(), sizeSpec);
 
   nsCOMPtr<nsIScriptSecurityManager>
     sm(do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID));
 
-  NS_ENSURE_TRUE(sm, NS_ERROR_FAILURE);
-
-  // Remember who's calling us. This code used to assume a null
-  // subject principal if it failed to get the principal, but that's
-  // just not safe, so bail on errors here.
-  nsCOMPtr<nsIPrincipal> callerPrincipal;
-  rv = sm->GetSubjectPrincipal(getter_AddRefs(callerPrincipal));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  bool isCallerChrome = true;
-  if (callerPrincipal) {
-    rv = sm->IsSystemPrincipal(callerPrincipal, &isCallerChrome);
-    NS_ENSURE_SUCCESS(rv, rv);
-  }
+  bool isCallerChrome = nsContentUtils::IsCallerChrome();
 
   JSContext *cx = GetJSContextFromWindow(aParent);
 
   bool windowTypeIsChrome = chromeFlags & nsIWebBrowserChrome::CHROME_OPENAS_CHROME;
   if (isCallerChrome && !hasChromeParent && !windowTypeIsChrome && cx) {
     // open() is called from chrome on a non-chrome window, push the context of the
     // callee onto the context stack to prevent the caller's priveleges from leaking
     // into code that runs while opening the new window.
@@ -735,17 +728,17 @@ nsWindowWatcher::OpenWindowInternal(nsID
     }
   }
 
   if ((aDialog || windowIsModalContentDialog) && argv) {
     // Set the args on the new window.
     nsCOMPtr<nsPIDOMWindow> piwin(do_QueryInterface(*_retval));
     NS_ENSURE_TRUE(piwin, NS_ERROR_UNEXPECTED);
 
-    rv = piwin->SetArguments(argv, callerPrincipal);
+    rv = piwin->SetArguments(argv);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   /* allow a window that we found by name to keep its name (important for cases
      like _self where the given name is different (and invalid)).  Also, _blank
      is not a window name. */
   if (windowNeedsName) {
     if (nameSpecified && !name.LowerCaseEqualsLiteral("_blank")) {
--- a/extensions/cookie/nsPermissionManager.cpp
+++ b/extensions/cookie/nsPermissionManager.cpp
@@ -25,16 +25,18 @@
 #include "mozilla/Attributes.h"
 #include "nsXULAppAPI.h"
 #include "nsIPrincipal.h"
 #include "nsContentUtils.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsIAppsService.h"
 #include "mozIApplication.h"
 #include "nsIEffectiveTLDService.h"
+#include "nsPIDOMWindow.h"
+#include "nsIDocument.h"
 
 static nsPermissionManager *gPermissionManager = nullptr;
 
 using mozilla::dom::ContentParent;
 using mozilla::dom::ContentChild;
 using mozilla::unused; // ha!
 
 static bool
--- a/hal/Hal.cpp
+++ b/hal/Hal.cpp
@@ -6,17 +6,19 @@
 
 #include "Hal.h"
 #include "HalImpl.h"
 #include "HalSandbox.h"
 #include "mozilla/Util.h"
 #include "nsThreadUtils.h"
 #include "nsXULAppAPI.h"
 #include "mozilla/Observer.h"
+#include "nsIDocument.h"
 #include "nsIDOMDocument.h"
+#include "nsPIDOMWindow.h"
 #include "nsIDOMWindow.h"
 #include "mozilla/Services.h"
 #include "nsIWebNavigation.h"
 #include "nsITabChild.h"
 #include "nsIDocShell.h"
 #include "mozilla/StaticPtr.h"
 #include "mozilla/ClearOnShutdown.h"
 #include "WindowIdentifier.h"
--- a/js/src/Makefile.in
+++ b/js/src/Makefile.in
@@ -3,16 +3,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/.
 
 DEPTH		= @DEPTH@
 topsrcdir	= @top_srcdir@
 srcdir		= @srcdir@
 
+TOPLEVEL_BUILD := 1
+
 run_for_side_effects := $(shell echo "MAKE: $(MAKE)")
 include $(DEPTH)/config/autoconf.mk
 
 ifdef JS_STANDALONE
 LIBRARY_NAME	= mozjs-@MOZJS_MAJOR_VERSION@.@MOZJS_MINOR_VERSION@@MOZJS_ALPHA@
 else
 LIBRARY_NAME	= mozjs
 endif
new file mode 100644
--- /dev/null
+++ b/js/src/config/makefiles/nonrecursive.mk
@@ -0,0 +1,62 @@
+# -*- makefile -*-
+# vim:set ts=8 sw=8 sts=8 noet:
+#
+# 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/.
+
+# The purpose of this file is to pull in non-recursive targets when performing
+# a partial tree (not top-level) build. This will allow people to continue to
+# build individual directories while some of the targets may not be normally
+# defined in that make file.
+#
+# Non-recursive targets are attached to existing make targets. The
+# NONRECURSIVE_TARGETS variable lists the make targets that modified. For
+# each target in this list, the NONRECURSIVE_TARGET_<target> variable will
+# contain a list of partial variable names. We will then look in variables
+# named NONRECURSIVE_TARGETS_<target>_<fragment>_* for information describing
+# how to evaluate non-recursive make targets.
+#
+# Targets are defined by the following variables:
+#
+#   FILE - The make file to evaluate.
+#   TARGETS - Targets to evaluate in that make file.
+#
+# For example:
+#
+# NONRECURSIVE_TARGETS = export libs
+# NONRECURSIVE_TARGETS_export = headers
+# NONRECURSIVE_TARGETS_export_headers_FILE = /path/to/exports.mk
+# NONRECURSIVE_TARGETS_export_headers_TARGETS = $(DIST)/include/foo.h $(DIST)/include/bar.h
+# NONRECURSIVE_TARGETS_libs = cppsrcs
+# NONRECURSIVE_TARGETS_libs_cppsrcs_FILE = /path/to/compilation.mk
+# NONRECURSIVE_TARGETS_libs_cppsrcs_TARGETS = /path/to/foo.o /path/to/bar.o
+#
+# Will get turned into the following:
+#
+# exports::
+#     $(MAKE) -f /path/to/exports.mk $(DIST)/include/foo.h $(DIST)/include/bar.h
+#
+# libs::
+#     $(MAKE) -f /path/to/compilation.mk /path/to/foo.o /path/to/bar.o
+
+ifndef INCLUDED_NONRECURSIVE_MK
+
+define define_nonrecursive_target
+$(1)::
+	cd $$(DEPTH) && $$(MAKE) -f $(2) $(3)
+endef
+
+$(foreach target,$(NONRECURSIVE_TARGETS), \
+    $(foreach entry,$(NONRECURSIVE_TARGETS_$(target)), \
+        $(eval $(call define_nonrecursive_target, \
+            $(target), \
+            $(NONRECURSIVE_TARGETS_$(target)_$(entry)_FILE), \
+            $(NONRECURSIVE_TARGETS_$(target)_$(entry)_TARGETS) \
+        )) \
+    ) \
+)
+
+INCLUDED_NONRECURSIVE_MK := 1
+endif
+
--- a/js/src/config/rules.mk
+++ b/js/src/config/rules.mk
@@ -606,16 +606,27 @@ endif
 
 ifeq (,$(CROSS_COMPILE))
 HOST_OUTOPTION = $(OUTOPTION)
 else
 HOST_OUTOPTION = -o # eol
 endif
 ################################################################################
 
+# Regenerate the build backend if it is out of date. We only check this once
+# per traversal, hence the ifdef and the export. This rule needs to come before
+# other rules for the default target or else it may not run in time.
+ifndef MOZBUILD_BACKEND_CHECKED
+default::
+	$(MAKE) -C $(DEPTH) backend.RecursiveMakeBackend.built
+
+export MOZBUILD_BACKEND_CHECKED=1
+endif
+
+
 # SUBMAKEFILES: List of Makefiles for next level down.
 #   This is used to update or create the Makefiles before invoking them.
 SUBMAKEFILES += $(addsuffix /Makefile, $(DIRS) $(TOOL_DIRS) $(PARALLEL_DIRS))
 
 # The root makefile doesn't want to do a plain export/libs, because
 # of the tiers and because of libxul. Suppress the default rules in favor
 # of something else. Makefiles which use this var *must* provide a sensible
 # default rule before including rules.mk
@@ -1185,36 +1196,16 @@ endif
 	$(JAR) cf $@ -C $(_JAVA_DIR) .
 
 GARBAGE_DIRS += $(_JAVA_DIR)
 
 ###############################################################################
 # Update Files Managed by Build Backend
 ###############################################################################
 
-ifdef MOZBUILD_DERIVED
-
-# If this Makefile is derived from moz.build files, substitution for all .in
-# files is handled by SUBSTITUTE_FILES. This includes Makefile.in.
-ifneq ($(SUBSTITUTE_FILES),,)
-$(SUBSTITUTE_FILES): % : $(srcdir)/%.in $(DEPTH)/config/autoconf.mk
-	@$(PYTHON) $(DEPTH)/config.status -n --file=$@
-	@$(TOUCH) $@
-endif
-
-# Detect when the backend.mk needs rebuilt. This will cause a full scan and
-# rebuild. While relatively expensive, it should only occur once per recursion.
-ifneq ($(BACKEND_INPUT_FILES),,)
-backend.mk: $(BACKEND_INPUT_FILES)
-	@$(PYTHON) $(DEPTH)/config.status -n
-	@$(TOUCH) $@
-endif
-
-endif # MOZBUILD_DERIVED
-
 ifndef NO_MAKEFILE_RULE
 Makefile: Makefile.in
 	@$(PYTHON) $(DEPTH)/config.status -n --file=Makefile
 	@$(TOUCH) $@
 endif
 
 ifndef NO_SUBMAKEFILES_RULE
 ifdef SUBMAKEFILES
@@ -1723,16 +1714,21 @@ endef
     $(eval $(call preprocess_file_template,					\
                   $(file),							\
                   $(or $($(category)_PATH),$(CURDIR))/$(notdir $(file:.in=)),	\
                   $(or $($(category)_TARGET),libs),				\
                   $($(category)_FLAGS)))					\
    )										\
  )
 
+# Pull in non-recursive targets if this is a partial tree build.
+ifndef TOPLEVEL_BUILD
+include $(topsrcdir)/config/makefiles/nonrecursive.mk
+endif
+
 ################################################################################
 # Special gmake rules.
 ################################################################################
 
 
 #
 # Re-define the list of default suffixes, so gmake won't have to churn through
 # hundreds of built-in suffix rules for stuff we don't need.
--- a/js/src/jit-test/tests/ion/doubleArrays.js
+++ b/js/src/jit-test/tests/ion/doubleArrays.js
@@ -7,29 +7,16 @@ function testPushConvert() {
     x.push(i);
   var res = 0;
   for (var i = 0; i < x.length; i++)
     res += x[i];
   assertEq(res, 60);
 }
 testPushConvert();
 
-var UnsafeSetElement = getSelfHostedValue("UnsafeSetElement");
-function testUnsafeSetConvert() {
-  var x = [0.5, 1.5, 2.1];
-  for (var i = 0; i < 2000; i++)
-    // try to ensure we JIT and hence inline
-    UnsafeSetElement(x, 1, i);
-  var res = 0;
-  for (var i = 0; i < x.length; i++)
-    res += x[i];
-  assertEq(res, 2001.6);
-}
-testUnsafeSetConvert();
-
 function testArrayInitializer() {
   var x = [.5,1.5,2.5,3];
   var res = 0;
   for (var i = 0; i < x.length; i++)
     res += x[i];
   assertEq(res, 7.5);
 }
 for (var i = 0; i < 5; i++)
--- a/js/src/jsnum.cpp
+++ b/js/src/jsnum.cpp
@@ -38,17 +38,16 @@
 #include "jsobj.h"
 #include "jsstr.h"
 
 #include "vm/GlobalObject.h"
 #include "vm/NumericConversions.h"
 #include "vm/StringBuffer.h"
 
 #include "jsatominlines.h"
-#include "jsnuminlines.h"
 #include "jsobjinlines.h"
 
 #include "vm/NumberObject-inl.h"
 #include "vm/String-inl.h"
 
 using namespace js;
 using namespace js::types;
 
@@ -1326,16 +1325,78 @@ js::NumberValueToStringBuffer(JSContext 
      * Inflate to jschar string.  The input C-string characters are < 127, so
      * even if jschars are UTF-8, all chars should map to one jschar.
      */
     size_t cstrlen = strlen(cstr);
     JS_ASSERT(!cbuf.dbuf && cstrlen < cbuf.sbufSize);
     return sb.appendInflated(cstr, cstrlen);
 }
 
+static bool
+StringToNumber(JSContext *cx, JSString *str, double *result)
+{
+    size_t length = str->length();
+    const jschar *chars = str->getChars(NULL);
+    if (!chars)
+        return false;
+
+    if (length == 1) {
+        jschar c = chars[0];
+        if ('0' <= c && c <= '9') {
+            *result = c - '0';
+            return true;
+        }
+        if (unicode::IsSpace(c)) {
+            *result = 0.0;
+            return true;
+        }
+        *result = js_NaN;
+        return true;
+    }
+
+    const jschar *end = chars + length;
+    const jschar *bp = SkipSpace(chars, end);
+
+    /* ECMA doesn't allow signed hex numbers (bug 273467). */
+    if (end - bp >= 2 && bp[0] == '0' && (bp[1] == 'x' || bp[1] == 'X')) {
+        /*
+         * It's probably a hex number.  Accept if there's at least one hex
+         * digit after the 0x, and if no non-whitespace characters follow all
+         * the hex digits.
+         */
+        const jschar *endptr;
+        double d;
+        if (!GetPrefixInteger(cx, bp + 2, end, 16, &endptr, &d) ||
+            endptr == bp + 2 ||
+            SkipSpace(endptr, end) != end)
+        {
+            *result = js_NaN;
+            return true;
+        }
+        *result = d;
+        return true;
+    }
+
+    /*
+     * Note that ECMA doesn't treat a string beginning with a '0' as
+     * an octal number here. This works because all such numbers will
+     * be interpreted as decimal by js_strtod.  Also, any hex numbers
+     * that have made it here (which can only be negative ones) will
+     * be treated as 0 without consuming the 'x' by js_strtod.
+     */
+    const jschar *ep;
+    double d;
+    if (!js_strtod(cx, bp, end, &ep, &d) || SkipSpace(ep, end) != end) {
+        *result = js_NaN;
+        return true;
+    }
+    *result = d;
+    return true;
+}
+
 #if defined(_MSC_VER)
 # pragma optimize("g", off)
 #endif
 JS_PUBLIC_API(bool)
 js::ToNumberSlow(JSContext *cx, Value v, double *out)
 {
 #ifdef DEBUG
     /*
@@ -1360,17 +1421,17 @@ js::ToNumberSlow(JSContext *cx, Value v,
     goto skip_int_double;
     for (;;) {
         if (v.isNumber()) {
             *out = v.toNumber();
             return true;
         }
       skip_int_double:
         if (v.isString())
-            return StringToNumberType<double>(cx, v.toString(), out);
+            return StringToNumber(cx, v.toString(), out);
         if (v.isBoolean()) {
             if (v.toBoolean()) {
                 *out = 1.0;
                 return true;
             }
             *out = 0.0;
             return true;
         }
deleted file mode 100644
--- a/js/src/jsnuminlines.h
+++ /dev/null
@@ -1,88 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
- * vim: set ts=8 sts=4 et sw=4 tw=99:
- * 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/. */
-
-#ifndef jsnuminlines_h___
-#define jsnuminlines_h___
-
-#include "vm/NumericConversions.h"
-#include "vm/Unicode.h"
-
-#include "jsstrinlines.h"
-
-
-namespace js {
-
-template<typename T> struct NumberTraits { };
-template<> struct NumberTraits<int32_t> {
-  static JS_ALWAYS_INLINE int32_t NaN() { return 0; }
-  static JS_ALWAYS_INLINE int32_t toSelfType(int32_t i) { return i; }
-  static JS_ALWAYS_INLINE int32_t toSelfType(double d) { return ToUint32(d); }
-};
-template<> struct NumberTraits<double> {
-  static JS_ALWAYS_INLINE double NaN() { return js_NaN; }
-  static JS_ALWAYS_INLINE double toSelfType(int32_t i) { return i; }
-  static JS_ALWAYS_INLINE double toSelfType(double d) { return d; }
-};
-
-template<typename T>
-static JS_ALWAYS_INLINE bool
-StringToNumberType(JSContext *cx, JSString *str, T *result)
-{
-    size_t length = str->length();
-    const jschar *chars = str->getChars(NULL);
-    if (!chars)
-        return false;
-
-    if (length == 1) {
-        jschar c = chars[0];
-        if ('0' <= c && c <= '9') {
-            *result = NumberTraits<T>::toSelfType(T(c - '0'));
-            return true;
-        }
-        if (unicode::IsSpace(c)) {
-            *result = NumberTraits<T>::toSelfType(T(0));
-            return true;
-        }
-        *result = NumberTraits<T>::NaN();
-        return true;
-    }
-
-    const jschar *end = chars + length;
-    const jschar *bp = SkipSpace(chars, end);
-
-    /* ECMA doesn't allow signed hex numbers (bug 273467). */
-    if (end - bp >= 2 && bp[0] == '0' && (bp[1] == 'x' || bp[1] == 'X')) {
-        /* Looks like a hex number. */
-        const jschar *endptr;
-        double d;
-        if (!GetPrefixInteger(cx, bp + 2, end, 16, &endptr, &d) || SkipSpace(endptr, end) != end) {
-            *result = NumberTraits<T>::NaN();
-            return true;
-        }
-        *result = NumberTraits<T>::toSelfType(d);
-        return true;
-    }
-
-    /*
-     * Note that ECMA doesn't treat a string beginning with a '0' as
-     * an octal number here. This works because all such numbers will
-     * be interpreted as decimal by js_strtod.  Also, any hex numbers
-     * that have made it here (which can only be negative ones) will
-     * be treated as 0 without consuming the 'x' by js_strtod.
-     */
-    const jschar *ep;
-    double d;
-    if (!js_strtod(cx, bp, end, &ep, &d) || SkipSpace(ep, end) != end) {
-        *result = NumberTraits<T>::NaN();
-        return true;
-    }
-    *result = NumberTraits<T>::toSelfType(d);
-    return true;
-}
-
-} // namespace js
-
-#endif /* jsnuminlines_h___ */
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -3875,23 +3875,28 @@ static JSFunctionSpecWithHelp shell_func
 "getMaxArgs()",
 "  Return the maximum number of supported args for a call."),
 
     JS_FN_HELP("objectEmulatingUndefined", ObjectEmulatingUndefined, 0, 0,
 "objectEmulatingUndefined()",
 "  Return a new object obj for which typeof obj === \"undefined\", obj == null\n"
 "  and obj == undefined (and vice versa for !=), and ToBoolean(obj) === false.\n"),
 
+    JS_FS_HELP_END
+};
+
+static JSFunctionSpecWithHelp self_hosting_functions[] = {
     JS_FN_HELP("getSelfHostedValue", GetSelfHostedValue, 1, 0,
 "getSelfHostedValue()",
 "  Get a self-hosted value by its name. Note that these values don't get \n"
 "  cached, so repeatedly getting the same value creates multiple distinct clones."),
 
     JS_FS_HELP_END
 };
+
 #ifdef MOZ_PROFILING
 # define PROFILING_FUNCTION_COUNT 5
 # ifdef MOZ_CALLGRIND
 #  define CALLGRIND_FUNCTION_COUNT 3
 # else
 #  define CALLGRIND_FUNCTION_COUNT 0
 # endif
 # ifdef MOZ_VTUNE
@@ -4814,22 +4819,29 @@ NewGlobalObject(JSContext *cx, JSObject 
 #endif
         if (!JS_InitReflect(cx, glob))
             return NULL;
         if (!JS_DefineDebuggerObject(cx, glob))
             return NULL;
         if (!JS::RegisterPerfMeasurement(cx, glob))
             return NULL;
         if (!JS_DefineFunctionsWithHelp(cx, glob, shell_functions) ||
-            !JS_DefineProfilingFunctions(cx, glob)) {
+            !JS_DefineProfilingFunctions(cx, glob))
+        {
             return NULL;
         }
         if (!js::DefineTestingFunctions(cx, glob))
             return NULL;
 
+        if (getenv("MOZ_SELFHOSTEDJS") &&
+            !JS_DefineFunctionsWithHelp(cx, glob, self_hosting_functions))
+        {
+            return NULL;
+        }
+
         RootedObject it(cx, JS_DefineObject(cx, glob, "it", &its_class, NULL, 0));
         if (!it)
             return NULL;
         if (!JS_DefineProperties(cx, it, its_props))
             return NULL;
 
         if (!JS_DefineProperty(cx, glob, "custom", UndefinedValue(), its_getter,
                                its_setter, 0))
@@ -4837,17 +4849,19 @@ NewGlobalObject(JSContext *cx, JSObject 
         if (!JS_DefineProperty(cx, glob, "customRdOnly", UndefinedValue(), its_getter,
                                its_setter, JSPROP_READONLY))
             return NULL;
 
         if (!JS_DefineProperty(cx, glob, "customNative", UndefinedValue(),
                                (JSPropertyOp)its_get_customNative,
                                (JSStrictPropertyOp)its_set_customNative,
                                JSPROP_NATIVE_ACCESSORS))
+        {
             return NULL;
+        }
 
         /* Initialize FakeDOMObject. */
         static js::DOMCallbacks DOMcallbacks = {
             InstanceClassHasProtoAtDepth
         };
         SetDOMCallbacks(cx->runtime, &DOMcallbacks);
 
         RootedObject domProto(cx, JS_InitClass(cx, glob, NULL, &dom_class, dom_constructor, 0,
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_5/Global/delete-global-NaN-property.js
@@ -0,0 +1,32 @@
+// Any copyright is dedicated to the Public Domain.
+// http://creativecommons.org/licenses/publicdomain/
+"use strict"
+
+//-----------------------------------------------------------------------------
+var BUGNUMBER = 649570;
+var summary = "|delete window.NaN| should throw a TypeError";
+
+print(BUGNUMBER + ": " + summary);
+
+/**************
+ * BEGIN TEST *
+ **************/
+
+var g = this, v = false;
+try
+{
+  delete this.NaN;
+  throw new Error("no exception thrown");
+}
+catch (e)
+{
+  assertEq(e instanceof TypeError, true,
+           "Expected a TypeError, got: " + e);
+}
+
+/******************************************************************************/
+
+if (typeof reportCompare === "function")
+  reportCompare(true, true);
+
+print("Tests complete");
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_5/Number/tonumber-string-hex.js
@@ -0,0 +1,38 @@
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommonn.org/licenses/publicdomain/
+ */
+
+var BUGNUMBER = 872853;
+var summary = 'Various tests of ToNumber(string), particularly +"0x" being NaN';
+
+print(BUGNUMBER + ": " + summary);
+
+/**************
+ * BEGIN TEST *
+ **************/
+
+assertEq(+"0x", NaN);
+assertEq(+"\t0x", NaN);
+assertEq(+"0x\n", NaN);
+assertEq(+"\n0x\t", NaN);
+assertEq(+"0x0", 0);
+assertEq(+"0xa", 10);
+assertEq(+"0xff", 255);
+assertEq(+"-0x", NaN);
+assertEq(+"-0xa", NaN);
+assertEq(+"-0xff", NaN);
+assertEq(+"0xInfinity", NaN);
+assertEq(+"+Infinity", Infinity);
+assertEq(+"-Infinity", -Infinity);
+assertEq(+"\t+Infinity", Infinity);
+assertEq(+"-Infinity\n", -Infinity);
+assertEq(+"+ Infinity", NaN);
+assertEq(+"- Infinity", NaN);
+
+/******************************************************************************/
+
+if (typeof reportCompare === "function")
+  reportCompare(true, true);
+
+print("Tests complete");
--- a/js/xpconnect/src/XPCConvert.cpp
+++ b/js/xpconnect/src/XPCConvert.cpp
@@ -12,16 +12,17 @@
 #include "xpcprivate.h"
 #include "nsString.h"
 #include "nsIAtom.h"
 #include "XPCWrapper.h"
 #include "nsJSPrincipals.h"
 #include "nsWrapperCache.h"
 #include "AccessCheck.h"
 #include "nsJSUtils.h"
+#include "WrapperFactory.h"
 
 #include "nsWrapperCacheInlines.h"
 
 #include "jsapi.h"
 #include "jsfriendapi.h"
 
 #include "mozilla/dom/BindingUtils.h"
 #include "mozilla/dom/PrimitiveConversions.h"
--- a/js/xpconnect/src/nsXPConnect.cpp
+++ b/js/xpconnect/src/nsXPConnect.cpp
@@ -17,16 +17,17 @@
 #include "nsBaseHashtable.h"
 #include "nsHashKeys.h"
 #include "jsfriendapi.h"
 #include "dom_quickstubs.h"
 #include "nsNullPrincipal.h"
 #include "nsIURI.h"
 #include "nsJSEnvironment.h"
 #include "nsThreadUtils.h"
+#include "nsDOMJSUtils.h"
 
 #include "XrayWrapper.h"
 #include "WrapperFactory.h"
 #include "AccessCheck.h"
 
 #ifdef MOZ_JSDEBUGGER
 #include "jsdIDebuggerService.h"
 #endif
@@ -530,16 +531,46 @@ nsXPConnect::NotifyLeaveCycleCollectionT
 
 void
 nsXPConnect::NotifyEnterMainThread()
 {
     NS_ABORT_IF_FALSE(NS_IsMainThread(), "Off main thread");
     JS_SetRuntimeThread(mRuntime->GetJSRuntime());
 }
 
+/*
+ * Return true if there exists a JSContext with a default global whose current
+ * inner is gray. The intent is to look for JS Object windows. We don't merge
+ * system compartments, so we don't use them to trigger merging CCs.
+ */
+bool
+nsXPConnect::UsefulToMergeZones()
+{
+    JSContext *iter = nullptr;
+    JSContext *cx;
+    while ((cx = JS_ContextIterator(GetRuntime()->GetJSRuntime(), &iter))) {
+        // Skip anything without an nsIScriptContext, as well as any scx whose
+        // NativeGlobal() is not an outer window (this happens with XUL Prototype
+        // compilation scopes, for example, which we're not interested in).
+        nsIScriptContext *scx = GetScriptContextFromJSContext(cx);
+        JS::RootedObject global(cx, scx ? scx->GetNativeGlobal() : nullptr);
+        if (!global || !js::GetObjectParent(global)) {
+            continue;
+        }
+        // Grab the inner from the outer.
+        global = JS_ObjectToInnerObject(cx, global);
+        MOZ_ASSERT(!js::GetObjectParent(global));
+        if (JS::GCThingIsMarkedGray(global) &&
+            !js::IsSystemCompartment(js::GetObjectCompartment(global))) {
+            return true;
+        }
+    }
+    return false;
+}
+
 class nsXPConnectParticipant: public nsCycleCollectionParticipant
 {
 public:
     static NS_METHOD RootImpl(void *n)
     {
         return NS_OK;
     }
     static NS_METHOD UnlinkImpl(void *n)
--- a/js/xpconnect/src/xpcprivate.h
+++ b/js/xpconnect/src/xpcprivate.h
@@ -519,16 +519,17 @@ public:
 
     // nsCycleCollectionJSRuntime
     virtual bool NotifyLeaveMainThread();
     virtual void NotifyEnterCycleCollectionThread();
     virtual void NotifyLeaveCycleCollectionThread();
     virtual void NotifyEnterMainThread();
     virtual nsresult BeginCycleCollection(nsCycleCollectionTraversalCallback &cb);
     virtual nsCycleCollectionParticipant *GetParticipant();
+    virtual bool UsefulToMergeZones();
     virtual void FixWeakMappingGrayBits();
     virtual bool NeedCollect();
     virtual void Collect(uint32_t reason);
 
     // This returns the singleton nsCycleCollectionParticipant for JSContexts.
     static nsCycleCollectionParticipant *JSContextParticipant();
 
     virtual nsIPrincipal* GetPrincipal(JSObject* obj,
new file mode 100644
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_bug872772.js
@@ -0,0 +1,41 @@
+const Cu = Components.utils;
+function run_test() {
+
+  // Make a content sandbox with an Xrayable object.
+  var contentSB = new Cu.Sandbox('http://www.google.com',
+                                 {wantXHRConstructor: true, wantComponents: true});
+
+  // Make an XHR in the content sandbox.
+  Cu.evalInSandbox('xhr = new XMLHttpRequest();', contentSB);
+
+  // Make sure that waivers can be set as Xray expandos.
+  var xhr = contentSB.xhr;
+  do_check_true(Cu.isXrayWrapper(xhr));
+  xhr.unwaivedExpando = xhr;
+  do_check_true(Cu.isXrayWrapper(xhr.unwaivedExpando));
+  var waived = xhr.wrappedJSObject;
+  do_check_true(!Cu.isXrayWrapper(waived));
+  xhr.waivedExpando = waived;
+  do_check_true(!Cu.isXrayWrapper(xhr.waivedExpando));
+
+  // Try the same thing for getters/setters, even though that's kind of
+  // contrived.
+  Cu.evalInSandbox('function f() {}', contentSB);
+  var f = contentSB.f;
+  var fWaiver = Cu.waiveXrays(f);
+  do_check_true(f != fWaiver);
+  do_check_true(Cu.unwaiveXrays(fWaiver) === f);
+  Object.defineProperty(xhr, 'waivedAccessors', {get: fWaiver, set: fWaiver});
+  var desc = Object.getOwnPropertyDescriptor(xhr, 'waivedAccessors');
+  do_check_true(desc.get === fWaiver);
+  do_check_true(desc.set === fWaiver);
+
+  // Make sure we correctly handle same-compartment security wrappers.
+  var unwaivedC = contentSB.Components;
+  do_check_true(Cu.isXrayWrapper(unwaivedC));
+  var waivedC = unwaivedC.wrappedJSObject;
+  do_check_true(waivedC && unwaivedC && (waivedC != unwaivedC));
+  xhr.waivedC = waivedC;
+  do_check_true(xhr.waivedC === waivedC);
+  do_check_true(Cu.unwaiveXrays(xhr.waivedC) === unwaivedC);
+}
--- a/js/xpconnect/tests/unit/xpcshell.ini
+++ b/js/xpconnect/tests/unit/xpcshell.ini
@@ -17,16 +17,17 @@ tail =
 [test_bug809652.js]
 [test_bug813901.js]
 [test_bug845201.js]
 [test_bug845862.js]
 [test_bug849730.js]
 [test_bug851895.js]
 [test_bug854558.js]
 [test_bug868675.js]
+[test_bug872772.js]
 [test_bug_442086.js]
 [test_file.js]
 [test_blob.js]
 [test_import.js]
 [test_import_fail.js]
 [test_js_weak_references.js]
 [test_reflect_parse.js]
 [test_localeCompare.js]
--- a/js/xpconnect/wrappers/AccessCheck.h
+++ b/js/xpconnect/wrappers/AccessCheck.h
@@ -5,17 +5,16 @@
  * 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/. */
 
 #ifndef __AccessCheck_h__
 #define __AccessCheck_h__
 
 #include "jsapi.h"
 #include "jswrapper.h"
-#include "WrapperFactory.h"
 
 class nsIPrincipal;
 
 namespace xpc {
 
 class AccessCheck {
   public:
     static bool subsumes(JSCompartment *a, JSCompartment *b);
--- a/js/xpconnect/wrappers/WrapperFactory.cpp
+++ b/js/xpconnect/wrappers/WrapperFactory.cpp
@@ -6,16 +6,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "WaiveXrayWrapper.h"
 #include "FilteringWrapper.h"
 #include "XrayWrapper.h"
 #include "AccessCheck.h"
 #include "XPCWrapper.h"
 #include "ChromeObjectWrapper.h"
+#include "WrapperFactory.h"
 
 #include "xpcprivate.h"
 #include "XPCMaps.h"
 #include "mozilla/dom/BindingUtils.h"
 #include "jsfriendapi.h"
 #include "mozilla/Likely.h"
 #include "nsContentUtils.h"
 
--- a/js/xpconnect/wrappers/XrayWrapper.cpp
+++ b/js/xpconnect/wrappers/XrayWrapper.cpp
@@ -26,16 +26,19 @@
 #include "nsGlobalWindow.h"
 
 using namespace mozilla::dom;
 using namespace JS;
 using namespace mozilla;
 
 using js::PropertyDescriptor;
 using js::Wrapper;
+using js::IsCrossCompartmentWrapper;
+using js::UncheckedUnwrap;
+using js::CheckedUnwrap;
 
 namespace xpc {
 
 static const uint32_t JSSLOT_RESOLVING = 0;
 
 static XPCWrappedNative *GetWrappedNative(JSObject *obj);
 
 namespace XrayUtils {
@@ -1634,16 +1637,73 @@ XrayWrapper<Base, Traits>::getOwnPropert
 
     if (!Traits::singleton.resolveOwnProperty(cx, *this, wrapper, holder, id, desc, flags))
         return false;
     if (desc->obj)
         desc->obj = wrapper;
     return true;
 }
 
+// Consider what happens when chrome does |xray.expando = xray.wrappedJSObject|.
+//
+// Since the expando comes from the target compartment, wrapping it back into
+// the target compartment to define it on the expando object ends up stripping
+// off the Xray waiver that gives |xray| and |xray.wrappedJSObject| different
+// identities. This is generally the right thing to do when wrapping across
+// compartments, but is incorrect in the special case of the Xray expando
+// object. Manually re-apply Xrays if necessary.
+//
+// NB: In order to satisfy the invariants of WaiveXray, we need to pass
+// in an object sans security wrapper, which means we need to strip off any
+// potential same-compartment security wrapper that may have been applied
+// to the content object. This is ok, because the the expando object is only
+// ever accessed by code across the compartment boundary.
+static bool
+RecreateLostWaivers(JSContext *cx, PropertyDescriptor *orig,
+                    MutableHandle<PropertyDescriptor> wrapped)
+{
+    // Compute whether the original objects were waived, and implicitly, whether
+    // they were objects at all.
+    bool valueWasWaived =
+        orig->value.isObject() &&
+        WrapperFactory::HasWaiveXrayFlag(&orig->value.toObject());
+    bool getterWasWaived =
+        (orig->attrs & JSPROP_GETTER) &&
+        WrapperFactory::HasWaiveXrayFlag(JS_FUNC_TO_DATA_PTR(JSObject*, orig->getter));
+    bool setterWasWaived =
+        (orig->attrs & JSPROP_SETTER) &&
+        WrapperFactory::HasWaiveXrayFlag(JS_FUNC_TO_DATA_PTR(JSObject*, orig->setter));
+
+    // Recreate waivers. Note that for value, we need an extra UncheckedUnwrap
+    // to handle same-compartment security wrappers (see above). This should
+    // never happen for getters/setters.
+
+    RootedObject rewaived(cx);
+    if (valueWasWaived && !IsCrossCompartmentWrapper(&wrapped.value().toObject())) {
+        rewaived = &wrapped.value().toObject();
+        rewaived = WrapperFactory::WaiveXray(cx, UncheckedUnwrap(rewaived));
+        NS_ENSURE_TRUE(rewaived, false);
+        wrapped.value().set(ObjectValue(*rewaived));
+    }
+    if (getterWasWaived && !IsCrossCompartmentWrapper(wrapped.getterObject())) {
+        MOZ_ASSERT(CheckedUnwrap(wrapped.getterObject()));
+        rewaived = WrapperFactory::WaiveXray(cx, wrapped.getterObject());
+        NS_ENSURE_TRUE(rewaived, false);
+        wrapped.setGetterObject(rewaived);
+    }
+    if (setterWasWaived && !IsCrossCompartmentWrapper(wrapped.setterObject())) {
+        MOZ_ASSERT(CheckedUnwrap(wrapped.setterObject()));
+        rewaived = WrapperFactory::WaiveXray(cx, wrapped.setterObject());
+        NS_ENSURE_TRUE(rewaived, false);
+        wrapped.setSetterObject(rewaived);
+    }
+
+    return true;
+}
+
 template <typename Base, typename Traits>
 bool
 XrayWrapper<Base, Traits>::defineProperty(JSContext *cx, HandleObject wrapper,
                                           HandleId id, PropertyDescriptor *desc)
 {
     assertEnteredPolicy(cx, wrapper, id);
 
     // NB: We still need JSRESOLVE_ASSIGNING here for the time being, because it
@@ -1672,16 +1732,20 @@ XrayWrapper<Base, Traits>::definePropert
     if (!expandoObject)
         return false;
 
     // Wrap the property descriptor for the target compartment.
     Rooted<PropertyDescriptor> wrappedDesc(cx, *desc);
     if (!JS_WrapPropertyDescriptor(cx, wrappedDesc.address()))
         return false;
 
+    // Fix up Xray waivers.
+    if (!RecreateLostWaivers(cx, desc, &wrappedDesc))
+        return false;
+
     return JS_DefinePropertyById(cx, expandoObject, id, wrappedDesc.value(),
                                  wrappedDesc.getter(), wrappedDesc.setter(),
                                  wrappedDesc.get().attrs);
 }
 
 template <typename Base, typename Traits>
 bool
 XrayWrapper<Base, Traits>::getOwnPropertyNames(JSContext *cx, HandleObject wrapper,
--- a/layout/base/nsPresShell.h
+++ b/layout/base/nsPresShell.h
@@ -347,17 +347,16 @@ protected:
 
   void WillCauseReflow() {
     nsContentUtils::AddScriptBlocker();
     ++mChangeNestCount;
   }
   nsresult DidCauseReflow();
   friend class nsAutoCauseReflowNotifier;
 
-  bool TouchesAreEqual(nsIDOMTouch *aTouch1, nsIDOMTouch *aTouch2);
   void DispatchTouchEvent(nsEvent *aEvent,
                           nsEventStatus* aStatus,
                           nsPresShellEventCB* aEventCB,
                           bool aTouchIsNew);
 
   void     WillDoReflow();
 
   /**
--- a/layout/forms/nsFileControlFrame.cpp
+++ b/layout/forms/nsFileControlFrame.cpp
@@ -338,52 +338,17 @@ nsFileControlFrame::SetFormProperty(nsIA
   return NS_OK;
 }
 
 void
 nsFileControlFrame::BuildDisplayList(nsDisplayListBuilder*   aBuilder,
                                      const nsRect&           aDirtyRect,
                                      const nsDisplayListSet& aLists)
 {
-  // box-shadow
-  if (StyleBorder()->mBoxShadow) {
-    aLists.BorderBackground()->AppendNewToTop(new (aBuilder)
-      nsDisplayBoxShadowOuter(aBuilder, this));
-  }
-
-  // Clip height only
-  nsRect clipRect(aBuilder->ToReferenceFrame(this), GetSize());
-  clipRect.width = GetVisualOverflowRect().XMost();
-
-  nsDisplayListCollection tempList;
-  {
-    DisplayListClipState::AutoSaveRestore clipState(aBuilder);
-    clipState.ClipContainingBlockDescendants(clipRect, nullptr);
-
-    // Our background is inherited to the text input, and we don't really want to
-    // paint it or out padding and borders (which we never have anyway, per
-    // styles in forms.css) -- doing it just makes us look ugly in some cases and
-    // has no effect in others.
-    nsBlockFrame::BuildDisplayList(aBuilder, aDirtyRect, tempList);
-  }
-
-  tempList.BorderBackground()->DeleteAll();
-
-  tempList.MoveTo(aLists);
-
-  // Disabled file controls don't pass mouse events to their children, so we
-  // put an invisible item in the display list above the children
-  // just to catch events
-  nsEventStates eventStates = mContent->AsElement()->State();
-  if (eventStates.HasState(NS_EVENT_STATE_DISABLED) && IsVisibleForPainting(aBuilder)) {
-    aLists.Content()->AppendNewToTop(
-      new (aBuilder) nsDisplayEventReceiver(aBuilder, this));
-  }
-
-  DisplaySelectionOverlay(aBuilder, aLists.Content());
+  BuildDisplayListForInline(aBuilder, aDirtyRect, aLists);
 }
 
 #ifdef ACCESSIBILITY
 a11y::AccType
 nsFileControlFrame::AccessibleType()
 {
   return a11y::eHTMLFileInputType;
 }
--- a/layout/reftests/forms/input/file/input-file-background-ref.xul
+++ b/layout/reftests/forms/input/file/input-file-background-ref.xul
@@ -1,19 +1,16 @@
 <?xml version="1.0"?>
 <?xml-stylesheet href="style.css" type="text/css"?>
 <window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
         xmlns:html="http://www.w3.org/1999/xhtml">
+  <html:style>
+    window {
+      background-color: blue;
+    }
+  </html:style>
+
   <vbox>
-    <html:style>
-      window {
-        background-color: blue;
-      }
-    </html:style>
     <html:div class='file'>
       <html:button>Browse&#8230;</html:button><label value="No file selected."/>
     </html:div>
-    <html:br/>
-    <html:div class='file'>
-      <html:button>Browse&#8230;</html:button><label value="No files selected."/>
-    </html:div>
   </vbox>
 </window>
--- a/layout/reftests/forms/input/file/input-file-background.html
+++ b/layout/reftests/forms/input/file/input-file-background.html
@@ -2,12 +2,10 @@
 <html>
   <style>
     body {
       background-color: blue;
     }
   </style>
   <body>
     <input type='file'>
-    <br>
-    <input type='file' multiple>
   </body>
 </html>
--- a/layout/reftests/forms/input/file/input-file-rtl-ref.xul
+++ b/layout/reftests/forms/input/file/input-file-rtl-ref.xul
@@ -1,17 +1,17 @@
 <?xml version="1.0"?>
 <?xml-stylesheet href="style.css" type="text/css"?>
 <window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
         xmlns:html="http://www.w3.org/1999/xhtml">
   <vbox>
+    <html:style>
+      vbox {
+       margin-right: 8px;
+      }
+    </html:style>
     <html:div dir='rtl'>
       <html:div class='file' dir='rtl'>
         <html:button>Browse&#8230;</html:button><label value="No file selected."/>
       </html:div>
     </html:div>
-    <html:div dir='rtl'>
-      <html:div class='file' dir='rtl'>
-        <html:button>Browse&#8230;</html:button><label value="No files selected."/>
-      </html:div>
-    </html:div>
   </vbox>
 </window>
--- a/layout/reftests/forms/input/file/input-file-rtl.html
+++ b/layout/reftests/forms/input/file/input-file-rtl.html
@@ -1,11 +1,8 @@
 <!DOCTYPE html>
 <html>
   <body>
     <div dir='rtl'>
       <input type='file'>
     </div>
-    <div dir='rtl'>
-      <input type='file' multiple>
-    </div>
   </body>
 </html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/forms/input/file/input-file-style-ref.xul
@@ -0,0 +1,25 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="style.css" type="text/css"?>
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+        xmlns:html="http://www.w3.org/1999/xhtml">
+  <hbox>
+    <html:style>
+      .file {
+        background-color: blue;
+        border: 1px red solid;
+        width: 400px;
+        padding: 2px;
+        margin: 5px;
+        display: inline-block;
+      }
+
+      .file > label {
+        /* color only applies to the label */
+        color: white;
+      }
+    </html:style>
+    <html:div class='file'>
+      <html:button>Browse&#8230;</html:button><label value="No file selected."/>
+    </html:div>
+  </hbox>
+</window>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/forms/input/file/input-file-style.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html>
+  <style>
+    input {
+      background-color: blue;
+      border: 1px red solid;
+      width: 400px;
+      color: white;
+      padding: 2px;
+      margin: 5px;
+    }
+  </style>
+  <body>
+    <input type='file'>
+  </body>
+</html>
rename from layout/reftests/forms/input-file-width-clip-ref.html
rename to layout/reftests/forms/input/file/input-file-width-clip-ref.html
rename from layout/reftests/forms/input-file-width-clip-1.html
rename to layout/reftests/forms/input/file/input-file-width-clip.html
--- a/layout/reftests/forms/input/file/reftest.list
+++ b/layout/reftests/forms/input/file/reftest.list
@@ -1,4 +1,7 @@
-fails-if(B2G) fuzzy-if(OSX==10.6,8,128) == input-file-simple.html input-file-simple-ref.xul # b2g failure is bug 855352
-fails-if(B2G) fuzzy-if(OSX==10.6,8,114) == input-file-rtl.html input-file-rtl-ref.xul # b2g failure is bug 855352
-fails-if(B2G) fuzzy-if(OSX==10.6,8,128) == input-file-size.html input-file-simple-ref.xul # b2g failure is bug 855352
-fails-if(B2G) fuzzy-if(OSX==10.6,8,128) == input-file-background.html input-file-background-ref.xul # b2g failure is bug 855352
+# B2G failures: bug 855352.
+fails-if(B2G) fuzzy-if(OSX==10.6,8,128) == input-file-simple.html input-file-simple-ref.xul
+fails-if(B2G) fuzzy-if(OSX==10.6,8,64) == input-file-rtl.html input-file-rtl-ref.xul
+fails-if(B2G) fuzzy-if(OSX==10.6,8,128) == input-file-size.html input-file-simple-ref.xul
+fails-if(B2G) fuzzy-if(OSX==10.6,8,64) == input-file-background.html input-file-background-ref.xul
+fails-if(B2G) == input-file-style.html input-file-style-ref.xul
+!= input-file-width-clip.html input-file-width-clip-ref.html
--- a/layout/reftests/forms/input/file/style.css
+++ b/layout/reftests/forms/input/file/style.css
@@ -1,45 +1,46 @@
-vbox {
-  margin: 8px;
+vbox, hbox {
+  margin-top: 8px;
+  margin-left: 8px;
+  display: block;
 }
 
 .file {
-  display: inline;
-
   /* Copy of input properties that apply of forms.css below this */
   color: -moz-FieldText;
   font: -moz-field;
   text-rendering: optimizeLegibility;
   line-height: normal !important;
   text-align: start;
   text-transform: none;
   word-spacing: normal;
   letter-spacing: normal;
   text-indent: 0;
   -moz-user-select: text;
   text-shadow: none;
 
   /* Copy of the type=file part of forms.css below this */
-  -moz-appearance: none;
+  display: inline; /* this one isn't really a copy... */
   white-space: nowrap;
-  cursor: default;
-  -moz-binding: none;
+  overflow:hidden;
 
+  -moz-appearance: none;
+  -moz-binding: none;
+  cursor: default;
   border: none;
-  background-color: none;
-
-  padding: 0 !important;
+  background-color: transparent;
+  padding: 0;
 }
 
 .file > label {
   display: inline-block;
 
   /* Copy from forms.css below this */
-  width: 12em;
+  min-width: 12em;
   -moz-padding-start: 5px;
 
   color: inherit;
   font-size: inherit;
   letter-spacing: inherit;
 
   direction: ltr !important;
 }
--- a/layout/reftests/forms/reftest.list
+++ b/layout/reftests/forms/reftest.list
@@ -1,13 +1,12 @@
 
 == checkbox-label-dynamic.html checkbox-label-dynamic-ref.html
 skip-if(B2G) fails-if(Android) == checkbox-radio-stretched.html checkbox-radio-stretched-ref.html # test for bug 464589
 
-skip-if(B2G) == input-file-width-clip-1.html input-file-width-clip-ref.html # test for bug 409587
 == input-hidden-border.html about:blank
 == input-text-bounds-1.html input-text-bounds-1-ref.html
 == input-text-size-1.html input-text-size-1-ref.html
 skip-if(B2G) fails-if(Android) == input-text-size-2.html input-text-size-2-ref.html
 HTTP(..) == input-text-baseline-1.html input-text-baseline-1-ref.html
 HTTP(..) == input-text-centering-1.xul input-text-centering-1-ref.xul
 skip-if(B2G) fails-if(Android) HTTP(..) == text-control-baseline-1.html text-control-baseline-1-ref.html
 == input-text-dynamic-height-1.xul input-text-dynamic-height-1-ref.xul
--- a/layout/style/forms.css
+++ b/layout/style/forms.css
@@ -406,30 +406,32 @@ input[type="image"]:disabled {
 
 input[type="image"]:-moz-focusring {
   /* Don't specify the outline-color, we should always use initial value. */
   outline: 1px dotted;
 }
 
 /* file selector */
 input[type="file"] {
-  -moz-appearance: none;
+  display: inline-block;
   white-space: nowrap;
+  overflow:hidden;
+
+  /* Revert rules which apply on all inputs. */
+  -moz-appearance: none;
+  -moz-binding: none;
   cursor: default;
-  -moz-binding: none;
 
   border: none;
   background-color: transparent;
-
-  /* TODO: check why. */
-  padding: 0 !important;
+  padding: 0;
 }
 
 input[type="file"] > xul|label {
-  width: 12em;
+  min-width: 12em;
   -moz-padding-start: 5px;
 
   color: inherit;
   font-size: inherit;
   letter-spacing: inherit;
 
   /*
    * Force the text to have LTR directionality. Otherwise filenames containing
new file mode 100644
--- /dev/null
+++ b/mfbt/Atomics.h
@@ -0,0 +1,895 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+/*
+ * Implements (almost always) lock-free atomic operations. The operations here
+ * are a subset of that which can be found in C++11's <atomic> header, with a
+ * different API to enforce consistent memory ordering constraints.
+ *
+ * Anyone caught using |volatile| for inter-thread memory safety needs to be
+ * sent a copy of this header and the C++11 standard.
+ */
+
+#ifndef mozilla_Atomics_h_
+#define mozilla_Atomics_h_
+
+#include "mozilla/Assertions.h"
+#include "mozilla/TypeTraits.h"
+
+#include <stdint.h>
+
+/*
+ * Our minimum deployment target on clang/OS X is OS X 10.6, whose SDK
+ * does not have <atomic>.  So be sure to check for <atomic> support
+ * along with C++0x support.
+ */
+#if defined(__clang__)
+#  if (__cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__)) && \
+      __has_include(<atomic>)
+#    define MOZ_HAVE_CXX11_ATOMICS
+#  endif
+/*
+ * Android uses a different C++ standard library that does not provide
+ * support for <atomic>
+ */
+#elif defined(__GNUC__) && !defined(__ANDROID__)
+#  include "mozilla/Compiler.h"
+#  if (defined(__GXX_EXPERIMENTAL_CXX0X__) || __cplusplus >= 201103L) && \
+      MOZ_GCC_VERSION_AT_LEAST(4, 5, 0)
+#    define MOZ_HAVE_CXX11_ATOMICS
+#  endif
+#elif defined(_MSC_VER) && _MSC_VER >= 1700
+#  define MOZ_HAVE_CXX11_ATOMICS
+#endif
+
+namespace mozilla {
+
+/**
+ * An enum of memory ordering possibilities for atomics.
+ *
+ * Memory ordering is the observable state of distinct values in memory.
+ * (It's a separate concept from atomicity, which concerns whether an
+ * operation can ever be observed in an intermediate state.  Don't
+ * conflate the two!)  Given a sequence of operations in source code on
+ * memory, it is *not* always the case that, at all times and on all
+ * cores, those operations will appear to have occurred in that exact
+ * sequence.  First, the compiler might reorder that sequence, if it
+ * thinks another ordering will be more efficient.  Second, the CPU may
+ * not expose so consistent a view of memory.  CPUs will often perform
+ * their own instruction reordering, above and beyond that performed by
+ * the compiler.  And each core has its own memory caches, and accesses
+ * (reads and writes both) to "memory" may only resolve to out-of-date
+ * cache entries -- not to the "most recently" performed operation in
+ * some global sense.  Any access to a value that may be used by
+ * multiple threads, potentially across multiple cores, must therefore
+ * have a memory ordering imposed on it, for all code on all
+ * threads/cores to have a sufficiently coherent worldview.
+ *
+ * http://gcc.gnu.org/wiki/Atomic/GCCMM/AtomicSync and
+ * http://en.cppreference.com/w/cpp/atomic/memory_order go into more
+ * detail on all this, including examples of how each mode works.
+ *
+ * Note that for simplicity and practicality, not all of the modes in
+ * C++11 are supported.  The missing C++11 modes are either subsumed by
+ * the modes we provide below, or not relevant for the CPUs we support
+ * in Gecko.  These three modes are confusing enough as it is!
+ */
+enum MemoryOrdering {
+  /*
+   * Relaxed ordering is the simplest memory ordering: none at all.
+   * When the result of a write is observed, nothing may be inferred
+   * about other memory.  Writes ostensibly performed "before" on the
+   * writing thread may not yet be visible.  Writes performed "after" on
+   * the writing thread may already be visible, if the compiler or CPU
+   * reordered them.  (The latter can happen if reads and/or writes get
+   * held up in per-processor caches.)  Relaxed ordering means
+   * operations can always use cached values (as long as the actual
+   * updates to atomic values actually occur, correctly, eventually), so
+   * it's usually the fastest sort of atomic access.  For this reason,
+   * *it's also the most dangerous kind of access*.
+   *
+   * Relaxed ordering is good for things like process-wide statistics
+   * counters that don't need to be consistent with anything else, so
+   * long as updates themselves are atomic.  (And so long as any
+   * observations of that value can tolerate being out-of-date -- if you
+   * need some sort of up-to-date value, you need some sort of other
+   * synchronizing operation.)  It's *not* good for locks, mutexes,
+   * reference counts, etc. that mediate access to other memory, or must
+   * be observably consistent with other memory.
+   *
+   * x86 architectures don't take advantage of the optimization
+   * opportunities that relaxed ordering permits.  Thus it's possible
+   * that using relaxed ordering will "work" on x86 but fail elsewhere
+   * (ARM, say, which *does* implement non-sequentially-consistent
+   * relaxed ordering semantics).  Be extra-careful using relaxed
+   * ordering if you can't easily test non-x86 architectures!
+   */
+  Relaxed,
+  /*
+   * When an atomic value is updated with ReleaseAcquire ordering, and
+   * that new value is observed with ReleaseAcquire ordering, prior
+   * writes (atomic or not) are also observable.  What ReleaseAcquire
+   * *doesn't* give you is any observable ordering guarantees for
+   * ReleaseAcquire-ordered operations on different objects.  For
+   * example, if there are two cores that each perform ReleaseAcquire
+   * operations on separate objects, each core may or may not observe
+   * the operations made by the other core.  The only way the cores can
+   * be synchronized with ReleaseAcquire is if they both
+   * ReleaseAcquire-access the same object.  This implies that you can't
+   * necessarily describe some global total ordering of ReleaseAcquire
+   * operations.
+   *
+   * ReleaseAcquire ordering is good for (as the name implies) atomic
+   * operations on values controlling ownership of things: reference
+   * counts, mutexes, and the like.  However, if you are thinking about
+   * using these to implement your own locks or mutexes, you should take
+   * a good, hard look at actual lock or mutex primitives first.
+   */
+  ReleaseAcquire,
+  /*
+   * When an atomic value is updated with SequentiallyConsistent
+   * ordering, all writes observable when the update is observed, just
+   * as with ReleaseAcquire ordering.  But, furthermore, a global total
+   * ordering of SequentiallyConsistent operations *can* be described.
+   * For example, if two cores perform SequentiallyConsistent operations
+   * on separate objects, one core will observably perform its update
+   * (and all previous operations will have completed), then the other
+   * core will observably perform its update (and all previous
+   * operations will have completed).  (Although those previous
+   * operations aren't themselves ordered -- they could be intermixed,
+   * or ordered if they occur on atomic values with ordering
+   * requirements.)  SequentiallyConsistent is the *simplest and safest*
+   * ordering of atomic operations -- it's always as if one operation
+   * happens, then another, then another, in some order -- and every
+   * core observes updates to happen in that single order.  Because it
+   * has the most synchronization requirements, operations ordered this
+   * way also tend to be slowest.
+   *
+   * SequentiallyConsistent ordering can be desirable when multiple
+   * threads observe objects, and they all have to agree on the
+   * observable order of changes to them.  People expect
+   * SequentiallyConsistent ordering, even if they shouldn't, when
+   * writing code, atomic or otherwise.  SequentiallyConsistent is also
+   * the ordering of choice when designing lockless data structures.  If
+   * you don't know what order to use, use this one.
+   */
+  SequentiallyConsistent,
+};
+
+} // namespace mozilla
+
+// Build up the underlying intrinsics.
+#ifdef MOZ_HAVE_CXX11_ATOMICS
+
+#  include <atomic>
+
+namespace mozilla {
+namespace detail {
+
+template<MemoryOrdering Order> struct AtomicOrderConstraints;
+
+template<>
+struct AtomicOrderConstraints<Relaxed>
+{
+    static const std::memory_order AtomicRMWOrder = std::memory_order_relaxed;
+    static const std::memory_order LoadOrder = std::memory_order_relaxed;
+    static const std::memory_order StoreOrder = std::memory_order_relaxed;
+};
+
+template<>
+struct AtomicOrderConstraints<ReleaseAcquire>
+{
+    static const std::memory_order AtomicRMWOrder = std::memory_order_acq_rel;
+    static const std::memory_order LoadOrder = std::memory_order_acquire;
+    static const std::memory_order StoreOrder = std::memory_order_release;
+};
+
+template<>
+struct AtomicOrderConstraints<SequentiallyConsistent>
+{
+    static const std::memory_order AtomicRMWOrder = std::memory_order_seq_cst;
+    static const std::memory_order LoadOrder = std::memory_order_seq_cst;
+    static const std::memory_order StoreOrder = std::memory_order_seq_cst;
+};
+
+template<typename T, MemoryOrdering Order>
+struct IntrinsicBase
+{
+    typedef std::atomic<T> ValueType;
+    typedef AtomicOrderConstraints<Order> OrderedOp;
+};
+
+template<typename T, MemoryOrdering Order>
+struct IntrinsicMemoryOps : public IntrinsicBase<T, Order>
+{
+    typedef IntrinsicBase<T, Order> Base;
+    static T load(const typename Base::ValueType& ptr) {
+      return ptr.load(Base::OrderedOp::LoadOrder);
+    }
+    static void store(typename Base::ValueType& ptr, T val) {
+      ptr.store(val, Base::OrderedOp::StoreOrder);
+    }
+    static T exchange(typename Base::ValueType& ptr, T val) {
+      return ptr.exchange(val, Base::OrderedOp::AtomicRMWOrder);
+    }
+};
+
+template<typename T, MemoryOrdering Order>
+struct IntrinsicAddSub : public IntrinsicBase<T, Order>
+{
+    typedef IntrinsicBase<T, Order> Base;
+    static T add(typename Base::ValueType& ptr, T val) {
+      return ptr.fetch_add(val, Base::OrderedOp::AtomicRMWOrder);
+    }
+    static T sub(typename Base::ValueType& ptr, T val) {
+      return ptr.fetch_sub(val, Base::OrderedOp::AtomicRMWOrder);
+    }
+};
+
+template<typename T, MemoryOrdering Order>
+struct IntrinsicAddSub<T*, Order> : public IntrinsicBase<T*, Order>
+{
+    typedef IntrinsicBase<T*, Order> Base;
+    static T* add(typename Base::ValueType& ptr, ptrdiff_t val) {
+      return ptr.fetch_add(fixupAddend(val), Base::OrderedOp::AtomicRMWOrder);
+    }
+    static T* sub(typename Base::ValueType& ptr, ptrdiff_t val) {
+      return ptr.fetch_sub(fixupAddend(val), Base::OrderedOp::AtomicRMWOrder);
+    }
+  private:
+    /*
+     * GCC 4.6's <atomic> header has a bug where adding X to an
+     * atomic<T*> is not the same as adding X to a T*.  Hence the need
+     * for this function to provide the correct addend.
+     */
+    static ptrdiff_t fixupAddend(ptrdiff_t val) {
+#if defined(__clang__) || defined(_MSC_VER)
+      return val;
+#elif defined(__GNUC__) && MOZ_GCC_VERSION_AT_LEAST(4, 6, 0) && \
+      !MOZ_GCC_VERSION_AT_LEAST(4, 7, 0)
+      return val * sizeof(T);
+#else
+      return val;
+#endif
+    }
+};
+
+template<typename T, MemoryOrdering Order>
+struct IntrinsicIncDec : public IntrinsicAddSub<T, Order>
+{
+    typedef IntrinsicBase<T, Order> Base;
+    static T inc(typename Base::ValueType& ptr) {
+      return IntrinsicAddSub<T, Order>::add(ptr, 1);
+    }
+    static T dec(typename Base::ValueType& ptr) {
+      return IntrinsicAddSub<T, Order>::sub(ptr, 1);
+    }
+};
+
+template<typename T, MemoryOrdering Order>
+struct AtomicIntrinsics : public IntrinsicMemoryOps<T, Order>,
+                          public IntrinsicIncDec<T, Order>
+{
+    typedef IntrinsicBase<T, Order> Base;
+    static T or_(typename Base::ValueType& ptr, T val) {
+      return ptr.fetch_or(val, Base::OrderedOp::AtomicRMWOrder);
+    }
+    static T xor_(typename Base::ValueType& ptr, T val) {
+      return ptr.fetch_xor(val, Base::OrderedOp::AtomicRMWOrder);
+    }
+    static T and_(typename Base::ValueType& ptr, T val) {
+      return ptr.fetch_and(val, Base::OrderedOp::AtomicRMWOrder);
+    }
+};
+
+template<typename T, MemoryOrdering Order>
+struct AtomicIntrinsics<T*, Order>
+  : public IntrinsicMemoryOps<T*, Order>, public IntrinsicIncDec<T*, Order>
+{
+};
+
+} // namespace detail
+} // namespace mozilla
+
+#elif defined(__GNUC__)
+
+namespace mozilla {
+namespace detail {
+
+/*
+ * The __sync_* family of intrinsics is documented here:
+ *
+ * http://gcc.gnu.org/onlinedocs/gcc-4.6.4/gcc/Atomic-Builtins.html
+ *
+ * While these intrinsics are deprecated in favor of the newer __atomic_*
+ * family of intrincs:
+ *
+ * http://gcc.gnu.org/onlinedocs/gcc-4.7.3/gcc/_005f_005fatomic-Builtins.html
+ *
+ * any GCC version that supports the __atomic_* intrinsics will also support
+ * the <atomic> header and so will be handled above.  We provide a version of
+ * atomics using the __sync_* intrinsics to support older versions of GCC.
+ *
+ * All __sync_* intrinsics that we use below act as full memory
+ * barriers, for both compiler and hardware reordering, with one notable
+ * exception: __sync_lock_test_and_set.  This intrinsic is not a full
+ * barrier, but only an acquire barrier.  In practice, this has turned
+ * out to not matter very much, and will become less important as newer
+ * compilers are used.
+ */
+
+template<MemoryOrdering Order> struct Barrier;
+
+/*
+ * Some processors (in particular, x86) don't require quite so many calls to
+ * __sync_sychronize as our specializations of Barrier produce.  If
+ * performance turns out to be an issue, defining these specializations
+ * on a per-processor basis would be a good first tuning step.
+ */
+
+template<>
+struct Barrier<Relaxed>
+{
+    static void beforeLoad() {}
+    static void afterLoad() {}
+    static void beforeStore() {}
+    static void afterStore() {}
+};
+
+template<>
+struct Barrier<ReleaseAcquire>
+{
+    static void beforeLoad() {}
+    static void afterLoad() { __sync_synchronize(); }
+    static void beforeStore() { __sync_synchronize(); }
+    static void afterStore() {}
+};
+
+template<>
+struct Barrier<SequentiallyConsistent>
+{
+    static void beforeLoad() { __sync_synchronize(); }
+    static void afterLoad() { __sync_synchronize(); }
+    static void beforeStore() { __sync_synchronize(); }
+    static void afterStore() { __sync_synchronize(); }
+};
+
+template<typename T, MemoryOrdering Order>
+struct IntrinsicMemoryOps
+{
+    static T load(const T& ptr) {
+      Barrier<Order>::beforeLoad();
+      T val = ptr;
+      Barrier<Order>::afterLoad();
+      return val;
+    }
+    static void store(T& ptr, T val) {
+      Barrier<Order>::beforeStore();
+      ptr = val;
+      Barrier<Order>::afterStore();
+    }
+    static T exchange(T& ptr, T val) {
+      return __sync_lock_test_and_set(&ptr, val);
+    }
+};
+
+template<typename T>
+struct IntrinsicAddSub
+{
+    typedef T ValueType;
+    static T add(T& ptr, T val) {
+      return __sync_fetch_and_add(&ptr, val);
+    }
+    static T sub(T& ptr, T val) {
+      return __sync_fetch_and_sub(&ptr, val);
+    }
+};
+
+template<typename T>
+struct IntrinsicAddSub<T*>
+{
+    typedef T* ValueType;
+    /*
+     * The reinterpret_casts are needed so that
+     * __sync_fetch_and_{add,sub} will properly type-check.
+     *
+     * Also, these functions do not provide standard semantics for
+     * pointer types, so we need to adjust the addend.
+     */
+    static ValueType add(ValueType& ptr, ptrdiff_t val) {
+      ValueType amount = reinterpret_cast<ValueType>(val * sizeof(T));
+      return __sync_fetch_and_add(&ptr, amount);
+    }
+    static ValueType sub(ValueType& ptr, ptrdiff_t val) {
+      ValueType amount = reinterpret_cast<ValueType>(val * sizeof(T));
+      return __sync_fetch_and_sub(&ptr, amount);
+    }
+};
+
+template<typename T>
+struct IntrinsicIncDec : public IntrinsicAddSub<T>
+{
+    static T inc(T& ptr) { return IntrinsicAddSub<T>::add(ptr, 1); }
+    static T dec(T& ptr) { return IntrinsicAddSub<T>::sub(ptr, 1); }
+};
+
+template<typename T, MemoryOrdering Order>
+struct AtomicIntrinsics : public IntrinsicMemoryOps<T, Order>,
+                          public IntrinsicIncDec<T>
+{
+    static T or_(T& ptr, T val) {
+      return __sync_fetch_and_or(&ptr, val);
+    }
+    static T xor_(T& ptr, T val) {
+      return __sync_fetch_and_xor(&ptr, val);
+    }
+    static T and_(T& ptr, T val) {
+      return __sync_fetch_and_and(&ptr, val);
+    }
+};
+
+template<typename T, MemoryOrdering Order>
+struct AtomicIntrinsics<T*, Order> : public IntrinsicMemoryOps<T*, Order>,
+                                     public IntrinsicIncDec<T*>
+{
+};
+
+} // namespace detail
+} // namespace mozilla
+
+#elif defined(_MSC_VER)
+
+/*
+ * Windows comes with a full complement of atomic operations.
+ * Unfortunately, most of those aren't available for Windows XP (even if
+ * the compiler supports intrinsics for them), which is the oldest
+ * version of Windows we support.  Therefore, we only provide operations
+ * on 32-bit datatypes for 32-bit Windows versions; for 64-bit Windows
+ * versions, we support 64-bit datatypes as well.
+ *
+ * To avoid namespace pollution issues, we declare whatever functions we
+ * need ourselves.
+ */
+
+extern "C" {
+long __cdecl _InterlockedExchangeAdd(long volatile* dst, long value);
+long __cdecl _InterlockedOr(long volatile* dst, long value);
+long __cdecl _InterlockedXor(long volatile* dst, long value);
+long __cdecl _InterlockedAnd(long volatile* dst, long value);
+long __cdecl _InterlockedExchange(long volatile *dst, long value);
+}
+
+#  pragma intrinsic(_InterlockedExchangeAdd)
+#  pragma intrinsic(_InterlockedOr)
+#  pragma intrinsic(_InterlockedXor)
+#  pragma intrinsic(_InterlockedAnd)
+#  pragma intrinsic(_InterlockedExchange)
+
+namespace mozilla {
+namespace detail {
+
+#  if !defined(_M_IX86) && !defined(_M_X64)
+     /*
+      * The implementations below are optimized for x86ish systems.  You
+      * will have to modify them if you are porting to Windows on a
+      * different architecture.
+      */
+#    error "Unknown CPU type"
+#  endif
+
+/*
+ * This template should define seven functions and |Type|, the datatype upon
+ * which the functions operate.  These five functions perform the obvious
+ * operation on the value contained in |*ptr| combined with |val| and return
+ * the value previously stored in |*ptr|
+ *
+ * static Type add(Type* ptr, Type val);
+ * static Type sub(Type* ptr, Type val);
+ * static Type or_(Type* ptr, Type val);
+ * static Type xor_(Type* ptr, Type val);
+ * static Type and_(Type* ptr, Type val);
+ *
+ * This function atomically stores |val| into |*ptr| and must provide a
+ * full memory fence after the store to prevent compiler and hardware
+ * instruction reordering.  It should also act as a compiler barrier
+ * to prevent reads and writes from moving to after the store.
+ *
+ * static void store(Type* ptr, Type val);
+ *
+ * This function atomically stores |val| into |*ptr| and returns the
+ * previous contents of *ptr;
+ *
+ * static Type exchange(Type* ptr, Type val);
+ */
+template<size_t DataSize> struct PrimitiveIntrinsics;
+
+template<>
+struct PrimitiveIntrinsics<4>
+{
+    typedef long Type;
+
+    static Type add(Type* ptr, Type val) {
+      return _InterlockedExchangeAdd(ptr, val);
+    }
+    static Type sub(Type* ptr, Type val) {
+      /*
+       * _InterlockedExchangeSubtract isn't available before Windows 7,
+       * and we must support Windows XP.
+       */
+      return _InterlockedExchangeAdd(ptr, -val);
+    }
+    static Type or_(Type* ptr, Type val) {
+      return _InterlockedOr(ptr, val);
+    }
+    static Type xor_(Type* ptr, Type val) {
+      return _InterlockedXor(ptr, val);
+    }
+    static Type and_(Type* ptr, Type val) {
+      return _InterlockedAnd(ptr, val);
+    }
+    static void store(Type* ptr, Type val) {
+      _InterlockedExchange(ptr, val);
+    }
+    static Type exchange(Type* ptr, Type val) {
+      return _InterlockedExchange(ptr, val);
+    }
+};
+
+#  if defined(_M_X64)
+
+extern "C" {
+long long __cdecl _InterlockedExchangeAdd64(long long volatile* dst,
+                                            long long value);
+long long __cdecl _InterlockedOr64(long long volatile* dst,
+                                   long long value);
+long long __cdecl _InterlockedXor64(long long volatile* dst,
+                                    long long value);
+long long __cdecl _InterlockedAnd64(long long volatile* dst,
+                                    long long value);
+long long __cdecl _InterlockedExchange64(long long volatile* dst,
+                                         long long value);
+}
+
+#    pragma intrinsic(_InterlockedExchangeAdd64)
+#    pragma intrinsic(_InterlockedOr64)
+#    pragma intrinsic(_InterlockedXor64)
+#    pragma intrinsic(_InterlockedAnd64)
+#    pragma intrinsic(_InterlockedExchange64)
+
+template <>
+struct PrimitiveIntrinsics<8>
+{
+    typedef __int64 Type;
+
+    static Type add(Type* ptr, Type val) {
+      return _InterlockedExchangeAdd64(ptr, val);
+    }
+    static Type sub(Type* ptr, Type val) {
+      /*
+       * There is no _InterlockedExchangeSubtract64.
+       */
+      return _InterlockedExchangeAdd64(ptr, -val);
+    }
+    static Type or_(Type* ptr, Type val) {
+      return _InterlockedOr64(ptr, val);
+    }
+    static Type xor_(Type* ptr, Type val) {
+      return _InterlockedXor64(ptr, val);
+    }
+    static Type and_(Type* ptr, Type val) {
+      return _InterlockedAnd64(ptr, val);
+    }
+    static void store(Type* ptr, Type val) {
+      _InterlockedExchange64(ptr, val);
+    }
+    static Type exchange(Type* ptr, Type val) {
+      return _InterlockedExchange64(ptr, val);
+    }
+};
+
+#  endif
+
+extern "C" { void _ReadWriteBarrier(); }
+
+#  pragma intrinsic(_ReadWriteBarrier)
+
+template<MemoryOrdering Order> struct Barrier;
+
+/*
+ * We do not provide an afterStore method in Barrier, as Relaxed and
+ * ReleaseAcquire orderings do not require one, and the required barrier
+ * for SequentiallyConsistent is handled by PrimitiveIntrinsics.
+ */
+
+template<>
+struct Barrier<Relaxed>
+{
+    static void beforeLoad() {}
+    static void afterLoad() {}
+    static void beforeStore() {}
+};
+
+template<>
+struct Barrier<ReleaseAcquire>
+{
+    static void beforeLoad() {}
+    static void afterLoad() { _ReadWriteBarrier(); }
+    static void beforeStore() { _ReadWriteBarrier(); }
+};
+
+template<>
+struct Barrier<SequentiallyConsistent>
+{
+    static void beforeLoad() { _ReadWriteBarrier(); }
+    static void afterLoad() { _ReadWriteBarrier(); }
+    static void beforeStore() { _ReadWriteBarrier(); }
+};
+
+template<typename PrimType, typename T>
+struct CastHelper
+{
+  static PrimType toPrimType(T val) { return static_cast<PrimType>(val); }
+  static T fromPrimType(PrimType val) { return static_cast<T>(val); }
+};
+
+template<typename PrimType, typename T>
+struct CastHelper<PrimType, T*>
+{
+  static PrimType toPrimType(T* val) { return reinterpret_cast<PrimType>(val); }
+  static T* fromPrimType(PrimType val) { return reinterpret_cast<T*>(val); }
+};
+
+template<typename T>
+struct IntrinsicBase
+{
+    typedef T ValueType;
+    typedef PrimitiveIntrinsics<sizeof(T)> Primitives;
+    typedef typename Primitives::Type PrimType;
+    MOZ_STATIC_ASSERT(sizeof(PrimType) == sizeof(T),
+                      "Selection of PrimitiveIntrinsics was wrong");
+    typedef CastHelper<PrimType, T> Cast;
+};
+
+template<typename T, MemoryOrdering Order>
+struct IntrinsicMemoryOps : public IntrinsicBase<T>
+{
+    static ValueType load(const ValueType& ptr) {
+      Barrier<Order>::beforeLoad();
+      ValueType val = ptr;
+      Barrier<Order>::afterLoad();
+      return val;
+    }
+    static void store(ValueType& ptr, ValueType val) {
+      // For SequentiallyConsistent, Primitives::store() will generate the
+      // proper memory fence.  Everything else just needs a barrier before
+      // the store.
+      if (Order == SequentiallyConsistent) {
+        Primitives::store(reinterpret_cast<PrimType*>(&ptr),
+                          Cast::toPrimType(val));
+      } else {
+        Barrier<Order>::beforeStore();
+        ptr = val;
+      }
+    }
+    static ValueType exchange(ValueType& ptr, ValueType val) {
+      PrimType oldval =
+        Primitives::exchange(reinterpret_cast<PrimType*>(&ptr),
+                             Cast::toPrimType(val));
+      return Cast::fromPrimType(oldval);
+    }
+};
+
+template<typename T>
+struct IntrinsicApplyHelper : public IntrinsicBase<T>
+{
+    typedef PrimType (*BinaryOp)(PrimType*, PrimType);
+    typedef PrimType (*UnaryOp)(PrimType*);
+
+    static ValueType applyBinaryFunction(BinaryOp op, ValueType& ptr,
+                                         ValueType val) {
+      PrimType* primTypePtr = reinterpret_cast<PrimType*>(&ptr);
+      PrimType primTypeVal = Cast::toPrimType(val);
+      return Cast::fromPrimType(op(primTypePtr, primTypeVal));
+    }
+
+    static ValueType applyUnaryFunction(UnaryOp op, ValueType& ptr) {
+      PrimType* primTypePtr = reinterpret_cast<PrimType*>(&ptr);
+      return Cast::fromPrimType(op(primTypePtr));
+    }
+};
+
+template<typename T>
+struct IntrinsicAddSub : public IntrinsicApplyHelper<T>
+{
+    static ValueType add(ValueType& ptr, ValueType val) {
+      return applyBinaryFunction(&Primitives::add, ptr, val);
+    }
+    static ValueType sub(ValueType& ptr, ValueType val) {
+      return applyBinaryFunction(&Primitives::sub, ptr, val);
+    }
+};
+
+template<typename T>
+struct IntrinsicAddSub<T*> : public IntrinsicApplyHelper<T*>
+{
+    static ValueType add(ValueType& ptr, ptrdiff_t amount) {
+      return applyBinaryFunction(&Primitives::add, ptr,
+                                 (ValueType)(amount * sizeof(ValueType)));
+    }
+    static ValueType sub(ValueType& ptr, ptrdiff_t amount) {
+      return applyBinaryFunction(&Primitives::sub, ptr,
+                                 (ValueType)(amount * sizeof(ValueType)));
+    }
+};
+
+template<typename T>
+struct IntrinsicIncDec : public IntrinsicAddSub<T>
+{
+    static ValueType inc(ValueType& ptr) { return add(ptr, 1); }
+    static ValueType dec(ValueType& ptr) { return sub(ptr, 1); }
+};
+
+template<typename T, MemoryOrdering Order>
+struct AtomicIntrinsics : public IntrinsicMemoryOps<T, Order>,
+                          public IntrinsicIncDec<T>
+{
+    static ValueType or_(ValueType& ptr, T val) {
+      return applyBinaryFunction(&Primitives::or_, ptr, val);
+    }
+    static ValueType xor_(ValueType& ptr, T val) {
+      return applyBinaryFunction(&Primitives::xor_, ptr, val);
+    }
+    static ValueType and_(ValueType& ptr, T val) {
+      return applyBinaryFunction(&Primitives::and_, ptr, val);
+    }
+};
+
+template<typename T, MemoryOrdering Order>
+struct AtomicIntrinsics<T*, Order> : public IntrinsicMemoryOps<T*, Order>,
+                                     public IntrinsicIncDec<T*>
+{
+};
+
+} // namespace detail
+} // namespace mozilla
+
+#else
+# error "Atomic compiler intrinsics are not supported on your platform"
+#endif
+
+namespace mozilla {
+
+namespace detail {
+
+template<typename T, MemoryOrdering Order>
+class AtomicBase
+{
+  protected:
+    typedef typename detail::AtomicIntrinsics<T, Order> Intrinsics;
+    typename Intrinsics::ValueType mValue;
+
+  public:
+    AtomicBase() : mValue() {}
+    AtomicBase(T aInit) { Intrinsics::store(mValue, aInit); }
+
+    T operator++(int) { return Intrinsics::inc(mValue); }
+    T operator--(int) { return Intrinsics::dec(mValue); }
+    T operator++() { return Intrinsics::inc(mValue) + 1; }
+    T operator--() { return Intrinsics::dec(mValue) - 1; }
+
+    operator T() const { return Intrinsics::load(mValue); }
+
+    /**
+     * Performs an atomic swap operation.  aValue is stored and the previous
+     * value of this variable is returned.
+     */
+    T exchange(T aValue) {
+      return Intrinsics::exchange(mValue, aValue);
+    }
+
+  private:
+    template<MemoryOrdering AnyOrder>
+    AtomicBase(const AtomicBase<T, AnyOrder>& aCopy) MOZ_DELETE;
+};
+
+} // namespace detail
+
+/**
+ * A wrapper for a type that enforces that all memory accesses are atomic.
+ *
+ * In general, where a variable |T foo| exists, |Atomic<T> foo| can be
+ * used in its place.  In addition to atomic store and load operations,
+ * compound assignment and increment/decrement operators are implemented
+ * which perform the corresponding read-modify-write operation
+ * atomically.  Finally, an atomic swap method is provided.
+ *
+ * Atomic accesses are sequentially consistent by default.  You should
+ * use the default unless you are tall enough to ride the
+ * memory-ordering roller coaster (if you're not sure, you aren't) and
+ * you have a compelling reason to do otherwise.
+ *
+ * There is one exception to the case of atomic memory accesses: providing an
+ * initial value of the atomic value is not guaranteed to be atomic.  This is a
+ * deliberate design choice that enables static atomic variables to be declared
+ * without introducing extra static constructors.
+ */
+template<typename T, MemoryOrdering Order = SequentiallyConsistent>
+class Atomic : public detail::AtomicBase<T, Order>
+{
+    // We only support 32-bit types on 32-bit Windows, which constrains our
+    // implementation elsewhere.  But we support pointer-sized types everywhere.
+    MOZ_STATIC_ASSERT(sizeof(T) == 4 || (sizeof(uintptr_t) == 8 && sizeof(T) == 8),
+                      "mozilla/Atomics.h only supports 32-bit and pointer-sized types");
+    // Regardless of the OS, we only support integral types here.
+    MOZ_STATIC_ASSERT(IsIntegral<T>::value, "can only have integral atomic variables");
+
+    typedef typename detail::AtomicBase<T, Order> Base;
+
+  public:
+    Atomic() : detail::AtomicBase<T, Order>() {}
+    Atomic(T aInit) : detail::AtomicBase<T, Order>(aInit) {}
+
+    T operator+=(T delta) { return Base::Intrinsics::add(Base::mValue, delta) + delta; }
+    T operator-=(T delta) { return Base::Intrinsics::sub(Base::mValue, delta) - delta; }
+    T operator|=(T val) { return Base::Intrinsics::or_(Base::mValue, val) | val; }
+    T operator^=(T val) { return Base::Intrinsics::xor_(Base::mValue, val) ^ val; }
+    T operator&=(T val) { return Base::Intrinsics::and_(Base::mValue, val) & val; }
+
+    T operator=(T aValue) {
+      Base::Intrinsics::store(Base::mValue, aValue);
+      return aValue;
+    }
+
+  private:
+    Atomic(Atomic<T, Order>& aOther) MOZ_DELETE;
+};
+
+/**
+ * A partial specialization of Atomic for pointer variables.
+ *
+ * Like Atomic<T>, Atomic<T*> is equivalent in most respects to a regular T*
+ * variable.  An atomic compare-and-swap primitive for pointer variables is
+ * provided, as are atomic increment and decement operators.  Also provided
+ * are the compound assignment operators for addition and subtraction.
+ * Atomic swap (via exchange()) is included as well.
+ *
+ * Atomic accesses are sequentially consistent by default.  You should
+ * use the default unless you are tall enough to ride the
+ * memory-ordering roller coaster (if you're not sure, you aren't) and
+ * you have a compelling reason to do otherwise.
+ *
+ * There is one exception to the case of atomic memory accesses: providing an
+ * initial value of the atomic value is not guaranteed to be atomic. This is a
+ * deliberate design choice that enables static atomic variables to be declared
+ * without introducing extra static constructors.
+ */
+template<typename T, MemoryOrdering Order>
+class Atomic<T*, Order> : public detail::AtomicBase<T*, Order>
+{
+    typedef typename detail::AtomicBase<T*, Order> Base;
+
+  public:
+    Atomic() : detail::AtomicBase<T*, Order>() {}
+    Atomic(T* aInit) : detail::AtomicBase<T*, Order>(aInit) {}
+
+    T* operator +=(ptrdiff_t delta) {
+      return Base::Intrinsics::add(Base::mValue, delta) + delta;
+    }
+    T* operator -=(ptrdiff_t delta) {
+      return Base::Intrinsics::sub(Base::mValue, delta) - delta;
+    }
+
+    T* operator=(T* aValue) {
+      Base::Intrinsics::store(Base::mValue, aValue);
+      return aValue;
+    }
+
+  private:
+    Atomic(Atomic<T*, Order>& aOther) MOZ_DELETE;
+};
+
+} // namespace mozilla
+
+#endif /* mozilla_Atomics_h_ */
--- a/mfbt/exported_headers.mk
+++ b/mfbt/exported_headers.mk
@@ -5,16 +5,17 @@
 # This file defines the headers exported by mfbt.  It is included by mfbt
 # itself and by the JS engine, which, when built standalone, must install
 # mfbt's exported headers itself.
 
 EXPORTS_NAMESPACES += mozilla
 
 EXPORTS_mozilla += \
   Assertions.h \
+  Atomics.h \
   Attributes.h \
   BloomFilter.h \
   Casting.h \
   Char16.h \
   CheckedInt.h \
   Compiler.h \
   Constants.h \
   DebugOnly.h \
--- a/mfbt/tests/Makefile.in
+++ b/mfbt/tests/Makefile.in
@@ -7,16 +7,17 @@ topsrcdir = @top_srcdir@
 srcdir = @srcdir@
 VPATH = @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 STL_FLAGS =
 
 CPP_UNIT_TESTS = \
+  TestAtomics.cpp \
   TestBloomFilter.cpp \
   TestCasting.cpp \
   TestCheckedInt.cpp \
   TestEndian.cpp \
   TestEnumSet.cpp \
   TestSHA1.cpp \
   TestTypeTraits.cpp \
   TestWeakPtr.cpp \
new file mode 100644
--- /dev/null
+++ b/mfbt/tests/TestAtomics.cpp
@@ -0,0 +1,140 @@
+/* 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/Assertions.h"
+#include "mozilla/Atomics.h"
+#include "mozilla/DebugOnly.h"
+
+#include <stdint.h>
+
+using mozilla::Atomic;
+using mozilla::DebugOnly;
+using mozilla::MemoryOrdering;
+using mozilla::Relaxed;
+using mozilla::ReleaseAcquire;
+using mozilla::SequentiallyConsistent;
+
+template <typename T, MemoryOrdering Order>
+static void
+TestTypeWithOrdering()
+{
+  Atomic<T, Order> atomic(5);
+  MOZ_ASSERT(atomic == 5, "Atomic variable did not initialize");
+
+  // Test atomic increment
+  MOZ_ASSERT(++atomic == T(6), "Atomic increment did not work");
+  MOZ_ASSERT(atomic++ == T(6), "Atomic post-increment did not work");
+  MOZ_ASSERT(atomic == T(7), "Atomic post-increment did not work");
+
+  // Test atomic decrement
+  MOZ_ASSERT(--atomic == 6, "Atomic decrement did not work");
+  MOZ_ASSERT(atomic-- == 6, "Atomic post-decrement did not work");
+  MOZ_ASSERT(atomic == 5, "Atomic post-decrement did not work");
+
+  // Test other arithmetic.
+  DebugOnly<T> result;
+  result = (atomic += T(5));
+  MOZ_ASSERT(atomic == T(10), "Atomic += did not work");
+  MOZ_ASSERT(result == T(10), "Atomic += returned the wrong value");
+  result = (atomic -= T(3));
+  MOZ_ASSERT(atomic == T(7), "Atomic -= did not work");
+  MOZ_ASSERT(result == T(7), "Atomic -= returned the wrong value");
+
+  // Test assignment
+  result = (atomic = T(5));
+  MOZ_ASSERT(atomic == T(5), "Atomic assignment failed");
+  MOZ_ASSERT(result == T(5), "Atomic assignment returned the wrong value");
+
+  // Test logical operations.
+  result = (atomic ^= T(2));
+  MOZ_ASSERT(atomic == T(7), "Atomic ^= did not work");
+  MOZ_ASSERT(result == T(7), "Atomic ^= returned the wrong value");
+  result = (atomic ^= T(4));
+  MOZ_ASSERT(atomic == T(3), "Atomic ^= did not work");
+  MOZ_ASSERT(result == T(3), "Atomic ^= returned the wrong value");
+  result = (atomic |= T(8));
+  MOZ_ASSERT(atomic == T(11), "Atomic |= did not work");
+  MOZ_ASSERT(result == T(11), "Atomic |= returned the wrong value");
+  result = (atomic |= T(8));
+  MOZ_ASSERT(atomic == T(11), "Atomic |= did not work");
+  MOZ_ASSERT(result == T(11), "Atomic |= returned the wrong value");
+  result = (atomic &= T(12));
+  MOZ_ASSERT(atomic == T(8), "Atomic &= did not work");
+  MOZ_ASSERT(result == T(8), "Atomic &= returned the wrong value");
+
+  // Test exchange.
+  atomic = T(30);
+  result = atomic.exchange(42);
+  MOZ_ASSERT(atomic == T(42), "Atomic exchange did not work");
+  MOZ_ASSERT(result == T(30), "Atomic exchange returned the wrong value");
+}
+
+template<typename T, MemoryOrdering Order>
+static void
+TestPointerWithOrdering()
+{
+  T array1[10];
+  Atomic<T*, Order> atomic(array1);
+  MOZ_ASSERT(atomic == array1, "Atomic variable did not initialize");
+
+  // Test atomic increment
+  MOZ_ASSERT(++atomic == array1 + 1, "Atomic increment did not work");
+  MOZ_ASSERT(atomic++ == array1 + 1, "Atomic post-increment did not work");
+  MOZ_ASSERT(atomic == array1 + 2, "Atomic post-increment did not work");
+
+  // Test atomic decrement
+  MOZ_ASSERT(--atomic == array1 + 1, "Atomic decrement did not work");
+  MOZ_ASSERT(atomic-- == array1 + 1, "Atomic post-decrement did not work");
+  MOZ_ASSERT(atomic == array1, "Atomic post-decrement did not work");
+
+  // Test other arithmetic operations
+  DebugOnly<T*> result;
+  result = (atomic += 2);
+  MOZ_ASSERT(atomic == array1 + 2, "Atomic += did not work");
+  MOZ_ASSERT(result == array1 + 2, "Atomic += returned the wrong value");
+  result = (atomic -= 1);
+  MOZ_ASSERT(atomic == array1 + 1, "Atomic -= did not work");
+  MOZ_ASSERT(result == array1 + 1, "Atomic -= returned the wrong value");
+
+  // Test stores
+  result = (atomic = array1);
+  MOZ_ASSERT(atomic == array1, "Atomic assignment did not work");
+  MOZ_ASSERT(result == array1, "Atomic assignment returned the wrong value");
+
+  // Test exchange
+  atomic = array1 + 2;
+  result = atomic.exchange(array1);
+  MOZ_ASSERT(atomic == array1, "Atomic exchange did not work");
+  MOZ_ASSERT(result == array1 + 2, "Atomic exchange returned the wrong value");
+}
+
+template <typename T>
+static void
+TestType()
+{
+  TestTypeWithOrdering<T, SequentiallyConsistent>();
+  TestTypeWithOrdering<T, ReleaseAcquire>();
+  TestTypeWithOrdering<T, Relaxed>();
+}
+
+template<typename T>
+static void
+TestPointer()
+{
+  TestPointerWithOrdering<T, SequentiallyConsistent>();
+  TestPointerWithOrdering<T, ReleaseAcquire>();
+  TestPointerWithOrdering<T, Relaxed>();
+}
+
+int main()
+{
+  TestType<uint32_t>();
+  TestType<int32_t>();
+  TestType<intptr_t>();
+  TestType<uintptr_t>();
+  TestPointer<int>();
+  TestPointer<float>();
+  TestPointer<uint16_t*>();
+  TestPointer<uint32_t*>();
+}
--- a/mobile/android/base/resources/xml/preferences_main.xml
+++ b/mobile/android/base/resources/xml/preferences_main.xml
@@ -99,19 +99,21 @@
                             android:defaultValue="true"
                             android:persistent="false" />
 
         <CheckBoxPreference android:key="android.not_a_preference.privacy.announcements.enabled"
                             android:title="@string/pref_show_product_announcements"
                             android:defaultValue="true"
                             android:persistent="true" />
 
-        <ListPreference android:key="android.not_a_preference.privacy.titlebar"
+        <ListPreference android:key="browser.chrome.titlebarMode"
                         android:title="@string/pref_titlebar_mode"
-                        android:persistent="true" />
+                        android:entries="@array/pref_titlebar_mode_entries"
+                        android:entryValues="@array/pref_titlebar_mode_values"
+                        android:persistent="false" />
 
         <PreferenceScreen android:key="android.not_a_preference.datareporting.preferences"
                           android:title="@string/pref_category_datareporting"
                           android:fragment="org.mozilla.gecko.GeckoPreferenceFragment" >
 
             <extra android:name="resource"
                    android:value="preferences_datareporting" />
 
--- a/mobile/android/base/resources/xml/preferences_nonfragment.xml.in
+++ b/mobile/android/base/resources/xml/preferences_nonfragment.xml.in
@@ -103,19 +103,21 @@
                             android:defaultValue="true"
                             android:persistent="false" />
 
         <CheckBoxPreference android:key="android.not_a_preference.privacy.announcements.enabled"
                             android:title="@string/pref_show_product_announcements"
                             android:defaultValue="true"
                             android:persistent="true" />
 
-        <ListPreference android:key="android.not_a_preference.privacy.titlebar"
+        <ListPreference android:key="browser.chrome.titlebarMode"
                         android:title="@string/pref_titlebar_mode"
-                        android:persistent="true" />
+                        android:entries="@array/pref_titlebar_mode_entries"
+                        android:entryValues="@array/pref_titlebar_mode_values"
+                        android:persistent="false" />
 
         <PreferenceScreen android:key="android.not_a_preference.datareporting.preferences"
                           android:title="@string/pref_category_datareporting" >
 
             <intent android:action="android.intent.action.VIEW"
                     android:targetPackage="@ANDROID_PACKAGE_NAME@"
                     android:targetClass="org.mozilla.gecko.GeckoPreferences" >
 
--- a/mobile/android/base/widget/TopSitesView.java
+++ b/mobile/android/base/widget/TopSitesView.java
@@ -378,29 +378,27 @@ public class TopSitesView extends GridVi
 
         public TopSitesViewHolder(View v) {
             titleView = (TextView) v.findViewById(R.id.title);
             thumbnailView = (ImageView) v.findViewById(R.id.thumbnail);
             pinnedView = (ImageView) v.findViewById(R.id.pinned);
         }
 
         public void setTitle(String title) {
-            Log.i(LOGTAG, "setTitle " + title + " from " + mTitle);
             if (mTitle != null && mTitle.equals(title))
                 return;
             mTitle = title;
             updateTitleView();
         }
 
         public String getTitle() {
             return (!TextUtils.isEmpty(mTitle) ? mTitle : mUrl);
         }
 
         public void setUrl(String url) {
-            Log.i(LOGTAG, "setUrl " + url + " from " + mUrl);
             if (mUrl != null && mUrl.equals(url)) {
                 return;
             }
             mUrl = url;
             updateTitleView();
         }
 
         public String getUrl() {
@@ -470,17 +468,16 @@ public class TopSitesView extends GridVi
                 convertView = LayoutInflater.from(mContext).inflate(R.layout.abouthome_topsite_item, null);
 
                 viewHolder = new TopSitesViewHolder(convertView);
                 convertView.setTag(viewHolder);
             } else {
                 viewHolder = (TopSitesViewHolder) convertView.getTag();
             }
 
-            Log.i(LOGTAG, "Build");
             viewHolder.setTitle(title);
             viewHolder.setUrl(url);
             viewHolder.setPinned(pinned);
 
             // Force the view to fit inside this slot in the grid
             convertView.setLayoutParams(new AbsListView.LayoutParams(getColumnWidth(),
                         Math.round(getColumnWidth()*ThumbnailHelper.THUMBNAIL_ASPECT_RATIO)));
 
@@ -633,17 +630,16 @@ public class TopSitesView extends GridVi
                 // a special URI until we can get a valid URL for this bookmark.
                 if (data.getBooleanExtra(AwesomeBar.USER_ENTERED_KEY, false)) {
                     // Store what the user typed as the bookmark's title.
                     title = url;
                     url = encodeUserEnteredUrl(url);
                 }
 
                 clearThumbnailsWithUrl(url);
-                Log.i(LOGTAG, "Edit done: " + url + " " + title);
 
                 holder.setUrl(url);
                 holder.setTitle(title);
                 holder.setPinned(true);
 
                 // update the database on a background thread
                 (new UiAsyncTask<Void, Void, Bitmap>(ThreadUtils.getBackgroundHandler()) {
                     @Override
--- a/python/mozbuild/mozbuild/backend/base.py
+++ b/python/mozbuild/mozbuild/backend/base.py
@@ -99,16 +99,20 @@ class BuildBackend(LoggingMixin):
     def __init__(self, environment):
         assert isinstance(environment, ConfigEnvironment)
 
         self.populate_logger()
 
         self.environment = environment
         self.summary = BackendConsumeSummary()
 
+        # Files whose modification should cause a new read and backend
+        # generation.
+        self.backend_input_files = set()
+
         self._environments = {}
         self._environments[environment.topobjdir] = environment
 
         self._init()
 
     def _init():
         """Hook point for child classes to perform actions during __init__.
 
@@ -148,16 +152,19 @@ class BuildBackend(LoggingMixin):
         backend_time = 0.0
 
         for obj in objs:
             self.summary.object_count += 1
             obj_start = time.time()
             self.consume_object(obj)
             backend_time += time.time() - obj_start
 
+            if isinstance(obj, SandboxDerived):
+                self.backend_input_files |= obj.sandbox_all_paths
+
             if isinstance(obj, ReaderSummary):
                 self.summary.mozbuild_count = obj.total_file_count
                 self.summary.mozbuild_execution_time = obj.total_execution_time
 
         # Write out a file indicating when this backend was last generated.
         age_file = os.path.join(self.environment.topobjdir,
             'backend.%s.built' % self.__class__.__name__)
         with open(age_file, 'a'):
--- a/python/mozbuild/mozbuild/backend/recursivemake.py
+++ b/python/mozbuild/mozbuild/backend/recursivemake.py
@@ -53,94 +53,46 @@ class BackendMakeFile(object):
     tree is scanned as opposed to individual files), if we were to blindly
     write backend.mk files, the net effect of updating a single mozbuild file
     in the tree is all backend.mk files have new mtimes. This would in turn
     invalidate all make targets across the whole tree! This would effectively
     undermine incremental builds as any mozbuild change would cause the entire
     tree to rebuild!
 
     The solution is to not update the mtimes of backend.mk files unless they
-    actually change. We use FileAvoidWrite to accomplish this. However, this
-    puts us in a somewhat complicated position when it comes to tree recursion.
-    As you are recursing the tree, the first time you come across a backend.mk
-    that is out of date, a full tree build will be incurred. In typical make
-    build systems, we would touch the out-of-date target (backend.mk) to ensure
-    its mtime is newer than all its dependencies - even if the contents did
-    not change. However, we can't rely on just this approach. During recursion,
-    the first trigger of backend generation will cause only that backend.mk to
-    update. If there is another backend.mk that is also out of date according
-    to mtime but whose contents were not changed, when we recurse to that
-    directory, make will trigger another full backend generation! This would
-    be completely redundant and would slow down builds! This is not acceptable.
-
-    We work around this problem by having backend generation update the mtime
-    of backend.mk if they are older than their inputs - even if the file
-    contents did not change. This is essentially a middle ground between
-    always updating backend.mk and only updating the backend.mk that was out
-    of date during recursion.
+    actually change. We use FileAvoidWrite to accomplish this.
     """
 
     def __init__(self, srcdir, objdir, environment):
         self.srcdir = srcdir
         self.objdir = objdir
         self.environment = environment
         self.path = os.path.join(objdir, 'backend.mk')
 
-        # Filenames that influenced the content of this file.
-        self.inputs = set()
-
         self.fh = FileAvoidWrite(self.path)
         self.fh.write('# THIS FILE WAS AUTOMATICALLY GENERATED. DO NOT EDIT.\n')
         self.fh.write('\n')
         self.fh.write('MOZBUILD_DERIVED := 1\n')
 
-        # SUBSTITUTE_FILES handles Makefile.in -> Makefile conversion. This
-        # also doubles to handle the case where there is no Makefile.in.
+        # The global rule to incur backend generation generates Makefiles.
         self.fh.write('NO_MAKEFILE_RULE := 1\n')
 
         # We can't blindly have a SUBMAKEFILES rule because some of the
         # Makefile may not have a corresponding Makefile.in. For the case
         # where a new directory is added, the mozbuild file referencing that
         # new directory will need updated. This will cause a full backend
         # scan and build, installing the new Makefile.
         self.fh.write('NO_SUBMAKEFILES_RULE := 1\n')
 
 
     def write(self, buf):
         self.fh.write(buf)
 
     def close(self):
-        if self.inputs:
-            l = ' '.join(sorted(self.inputs))
-            self.fh.write('BACKEND_INPUT_FILES += %s\n' % l)
-
-        result = self.fh.close()
-
-        if not self.inputs:
-            return result
-
-        # Update mtime iff any of its input files are newer. See class notes
-        # for why we do this.
-        existing_mtime = os.path.getmtime(self.path)
-
-        def mtime(path):
-            try:
-                return os.path.getmtime(path)
-            except OSError as e:
-                if e.errno == errno.ENOENT:
-                    return 0
-
-                raise
-
-        input_mtime = max(mtime(path) for path in self.inputs)
-
-        if input_mtime > existing_mtime:
-            os.utime(self.path, None)
-
-        return result
+        return self.fh.close()
 
 
 class RecursiveMakeBackend(BuildBackend):
     """Backend that integrates with the existing recursive make build system.
 
     This backend facilitates the transition from Makefile.in to moz.build
     files.
 
@@ -165,16 +117,19 @@ class RecursiveMakeBackend(BuildBackend)
             return '{:d} total backend files. {:d} created; {:d} updated; {:d} unchanged'.format(
                 summary.managed_count, summary.created_count,
                 summary.updated_count, summary.unchanged_count)
 
         # This is a little kludgy and could be improved with a better API.
         self.summary.backend_detailed_summary = types.MethodType(detailed,
             self.summary)
 
+        self.backend_input_files.add(os.path.join(self.environment.topobjdir,
+            'config', 'autoconf.mk'))
+
     def _update_from_avoid_write(self, result):
         existed, updated = result
 
         if not existed:
             self.summary.created_count += 1
         elif updated:
             self.summary.updated_count += 1
         else:
@@ -184,29 +139,22 @@ class RecursiveMakeBackend(BuildBackend)
         """Write out build files necessary to build with recursive make."""
 
         if not isinstance(obj, SandboxDerived):
             return
 
         backend_file = self._backend_files.get(obj.srcdir,
             BackendMakeFile(obj.srcdir, obj.objdir, self.get_environment(obj)))
 
-        # Define the paths that will trigger a backend rebuild. We always
-        # add autoconf.mk because that is proxy for CONFIG. We can't use
-        # config.status because there is no make target for that!
-        autoconf_path = os.path.join(obj.topobjdir, 'config', 'autoconf.mk')
-        backend_file.inputs.add(autoconf_path)
-        backend_file.inputs |= obj.sandbox_all_paths
-
         if isinstance(obj, DirectoryTraversal):
             self._process_directory_traversal(obj, backend_file)
         elif isinstance(obj, ConfigFileSubstitution):
-            backend_file.write('SUBSTITUTE_FILES += %s\n' % obj.relpath)
             self._update_from_avoid_write(
                 backend_file.environment.create_config_file(obj.output_path))
+            self.backend_input_files.add(obj.input_path)
             self.summary.managed_count += 1
         elif isinstance(obj, VariablePassthru):
             # Sorted so output is consistent and we don't bump mtimes.
             for k, v in sorted(obj.variables.items()):
                 if isinstance(v, list):
                     for item in v:
                         backend_file.write('%s += %s\n' % (k, item))
 
@@ -242,17 +190,22 @@ class RecursiveMakeBackend(BuildBackend)
             if os.path.exists(makefile_in):
                 self.log(logging.DEBUG, 'substitute_makefile',
                     {'path': makefile}, 'Substituting makefile: {path}')
 
                 self._update_from_avoid_write(
                     bf.environment.create_config_file(makefile))
                 self.summary.managed_count += 1
 
-                bf.write('SUBSTITUTE_FILES += Makefile\n')
+                # Adding the Makefile.in here has the desired side-effect that
+                # if the Makefile.in disappears, this will force moz.build
+                # traversal. This means that when we remove empty Makefile.in
+                # files, the old file will get replaced with the autogenerated
+                # one automatically.
+                self.backend_input_files.add(makefile_in)
             else:
                 self.log(logging.DEBUG, 'stub_makefile',
                     {'path': makefile}, 'Creating stub Makefile: {path}')
 
                 params = {
                     'topsrc': bf.environment.get_top_srcdir(makefile),
                     'src': bf.environment.get_file_srcdir(makefile),
                     'depth': bf.environment.get_depth(makefile),
@@ -262,16 +215,29 @@ class RecursiveMakeBackend(BuildBackend)
                 aw = FileAvoidWrite(makefile)
                 aw.write(STUB_MAKEFILE.format(**params))
                 self._update_from_avoid_write(aw.close())
                 self.summary.managed_count += 1
 
             self._update_from_avoid_write(bf.close())
             self.summary.managed_count += 1
 
+        # Write out a dependency file used to determine whether a config.status
+        # re-run is needed.
+        backend_built_path = os.path.join(self.environment.topobjdir,
+            'backend.%s.built' % self.__class__.__name__)
+        backend_deps = FileAvoidWrite('%s.pp' % backend_built_path)
+        inputs = sorted(self.backend_input_files)
+        backend_deps.write('%s: %s\n' % (backend_built_path, ' '.join(inputs)))
+        for path in inputs:
+            backend_deps.write('%s:\n' % path)
+
+        self._update_from_avoid_write(backend_deps.close())
+        self.summary.managed_count += 1
+
     def _process_directory_traversal(self, obj, backend_file):
         """Process a data.DirectoryTraversal instance."""
         fh = backend_file.fh
 
         for tier, dirs in obj.tier_dirs.iteritems():
             fh.write('TIERS += %s\n' % tier)
 
             if dirs:
--- a/python/mozbuild/mozbuild/test/backend/test_recursivemake.py
+++ b/python/mozbuild/mozbuild/test/backend/test_recursivemake.py
@@ -18,16 +18,18 @@ from mozbuild.test.backend.common import
 
 
 class TestRecursiveMakeBackend(BackendTester):
     def test_basic(self):
         """Ensure the RecursiveMakeBackend works without error."""
         env = self._consume('stub0', RecursiveMakeBackend)
         self.assertTrue(os.path.exists(os.path.join(env.topobjdir,
             'backend.RecursiveMakeBackend.built')))
+        self.assertTrue(os.path.exists(os.path.join(env.topobjdir,
+            'backend.RecursiveMakeBackend.built.pp')))
 
     def test_output_files(self):
         """Ensure proper files are generated."""
         env = self._consume('stub0', RecursiveMakeBackend)
 
         expected = ['', 'dir1', 'dir2']
 
         for d in expected:
@@ -68,25 +70,24 @@ class TestRecursiveMakeBackend(BackendTe
         self.assertTrue(lines[0].startswith('# THIS FILE WAS AUTOMATICALLY'))
 
     def test_backend_mk(self):
         """Ensure backend.mk file is written out properly."""
         env = self._consume('stub0', RecursiveMakeBackend)
 
         p = os.path.join(env.topobjdir, 'backend.mk')
 
-        lines = [l.strip() for l in open(p, 'rt').readlines()[2:-1]]
+        lines = [l.strip() for l in open(p, 'rt').readlines()[2:]]
         self.assertEqual(lines, [
             'MOZBUILD_DERIVED := 1',
             'NO_MAKEFILE_RULE := 1',
             'NO_SUBMAKEFILES_RULE := 1',
             'DIRS := dir1',
             'PARALLEL_DIRS := dir2',
             'TEST_DIRS := dir3',
-            'SUBSTITUTE_FILES += Makefile',
         ])
 
     def test_mtime_no_change(self):
         """Ensure mtime is not updated if file content does not change."""
 
         env = self._consume('stub0', RecursiveMakeBackend)
 
         makefile_path = os.path.join(env.topobjdir, 'Makefile')
@@ -102,26 +103,25 @@ class TestRecursiveMakeBackend(BackendTe
         self.assertEqual(os.path.getmtime(makefile_path), makefile_mtime)
         self.assertEqual(os.path.getmtime(backend_path), backend_mtime)
 
     def test_external_make_dirs(self):
         """Ensure we have make recursion into external make directories."""
         env = self._consume('external_make_dirs', RecursiveMakeBackend)
 
         backend_path = os.path.join(env.topobjdir, 'backend.mk')
-        lines = [l.strip() for l in open(backend_path, 'rt').readlines()[2:-1]]
+        lines = [l.strip() for l in open(backend_path, 'rt').readlines()[2:]]
         self.assertEqual(lines, [
             'MOZBUILD_DERIVED := 1',
             'NO_MAKEFILE_RULE := 1',
             'NO_SUBMAKEFILES_RULE := 1',
             'DIRS := dir',
             'PARALLEL_DIRS := p_dir',
             'DIRS += external',
             'PARALLEL_DIRS += p_external',
-            'SUBSTITUTE_FILES += Makefile',
         ])
 
     def test_substitute_config_files(self):
         """Ensure substituted config files are produced."""
         env = self._consume('substitute_config_files', RecursiveMakeBackend)
 
         p = os.path.join(env.topobjdir, 'foo')
         self.assertTrue(os.path.exists(p))
@@ -130,17 +130,17 @@ class TestRecursiveMakeBackend(BackendTe
             'TEST = foo',
         ])
 
     def test_variable_passthru(self):
         """Ensure variable passthru is written out correctly."""
         env = self._consume('variable_passthru', RecursiveMakeBackend)
 
         backend_path = os.path.join(env.topobjdir, 'backend.mk')
-        lines = [l.strip() for l in open(backend_path, 'rt').readlines()[2:-1]]
+        lines = [l.strip() for l in open(backend_path, 'rt').readlines()[2:]]
 
         expected = {
             'ASFILES': [
                 'ASFILES += bar.s',
                 'ASFILES += foo.asm',
             ],
             'XPIDL_FLAGS': [
                 'XPIDL_FLAGS += -Idir1',
@@ -162,17 +162,17 @@ class TestRecursiveMakeBackend(BackendTe
             found = [str for str in lines if str.startswith(var)]
             self.assertEqual(found, val)
 
     def test_exports(self):
         """Ensure EXPORTS is written out correctly."""
         env = self._consume('exports', RecursiveMakeBackend)
 
         backend_path = os.path.join(env.topobjdir, 'backend.mk')
-        lines = [l.strip() for l in open(backend_path, 'rt').readlines()[2:-1]]
+        lines = [l.strip() for l in open(backend_path, 'rt').readlines()[2:]]
 
         self.assertEqual(lines, [
             'MOZBUILD_DERIVED := 1',
             'NO_MAKEFILE_RULE := 1',
             'NO_SUBMAKEFILES_RULE := 1',
             'EXPORTS += foo.h',
             'EXPORTS_NAMESPACES += mozilla',
             'EXPORTS_mozilla += mozilla1.h mozilla2.h',
@@ -184,17 +184,17 @@ class TestRecursiveMakeBackend(BackendTe
             'EXPORTS_nspr/private += pprio.h',
         ])
 
     def test_xpcshell_manifests(self):
         """Ensure XPCSHELL_TESTS_MANIFESTS is written out correctly."""
         env = self._consume('xpcshell_manifests', RecursiveMakeBackend)
 
         backend_path = os.path.join(env.topobjdir, 'backend.mk')
-        lines = [l.strip() for l in open(backend_path, 'rt').readlines()[2:-1]]
+        lines = [l.strip() for l in open(backend_path, 'rt').readlines()[2:]]
 
         # Avoid positional parameter and async related breakage
         var = 'XPCSHELL_TESTS'
         xpclines = sorted([val for val in lines if val.startswith(var)])
 
         # Assignment[aa], append[cc], conditional[valid]
         expected = ('aa', 'bb', 'cc', 'dd', 'valid_val')
         self.assertEqual(xpclines, ["XPCSHELL_TESTS += %s" % val for val in expected])
--- a/testing/marionette/client/marionette/geckoinstance.py
+++ b/testing/marionette/client/marionette/geckoinstance.py
@@ -26,23 +26,27 @@ class GeckoInstance(object):
         profile_path = self.profile
         profile_args = {"preferences": self.required_prefs}
         if not profile_path:
             runner_class = Runner
             profile_args["restore"] = False
         else:
             runner_class = CloneRunner
             profile_args["path_from"] = profile_path
+
+        self.gecko_log = os.path.abspath('gecko.log')
+        if os.access(self.gecko_log, os.F_OK):
+            os.remove(self.gecko_log)
         self.runner = runner_class.create(
             binary=self.bin,
             profile_args=profile_args,
             cmdargs=['-no-remote'],
             kp_kwargs={
                 'processOutputLine': [NullOutput()],
-                'logfile': os.path.abspath('gecko.log')})
+                'logfile': self.gecko_log})
         self.runner.start()
 
     def close(self):
         self.runner.stop()
         self.runner.cleanup()
 
 
 class B2GDesktopInstance(GeckoInstance):
--- a/testing/marionette/marionette-actors.js
+++ b/testing/marionette/marionette-actors.js
@@ -1079,19 +1079,19 @@ MarionetteDriverActor.prototype = {
   },
 
   /**
    * Gets the page source of the content document
    */
   getPageSource: function MDA_getPageSource(){
     this.command_id = this.getCommandId();
     if (this.context == "chrome"){
-      var curWindow = this.getCurrentWindow();
-      var XMLSerializer = curWindow.XMLSerializer; 
-      var pageSource = new XMLSerializer().serializeToString(curWindow.document);
+      let curWindow = this.getCurrentWindow();
+      let XMLSerializer = curWindow.XMLSerializer; 
+      let pageSource = new XMLSerializer().serializeToString(curWindow.document);
       this.sendResponse(pageSource, this.command_id);
     }
     else {
       this.sendAsync("getPageSource", {}, this.command_id);
     }
   },
 
   /**
@@ -1187,30 +1187,30 @@ MarionetteDriverActor.prototype = {
    *        'value' if element is not set, then this
    *                holds either the id, name or index 
    *                of the frame to switch to
    */
   switchToFrame: function MDA_switchToFrame(aRequest) {
     let command_id = this.command_id = this.getCommandId();
     this.logRequest("switchToFrame", aRequest);
     let checkTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
+    let curWindow = this.getCurrentWindow();
     let checkLoad = function() { 
       let errorRegex = /about:.+(error)|(blocked)\?/;
       if (curWindow.document.readyState == "complete") { 
         this.sendOk(command_id);
         return;
       } 
       else if (curWindow.document.readyState == "interactive" && errorRegex.exec(curWindow.document.baseURI)) {
         this.sendError("Error loading page", 13, null, command_id);
         return;
       }
       
       checkTimer.initWithCallback(checkLoad.bind(this), 100, Ci.nsITimer.TYPE_ONE_SHOT);
     }
-    let curWindow = this.getCurrentWindow();
     if (this.context == "chrome") {
       let foundFrame = null;
       if ((aRequest.value == null) && (aRequest.element == null)) {
         this.curFrame = null;
         if (aRequest.focus) {
           this.mainFrame.focus();
         }
         checkTimer.initWithCallback(checkLoad.bind(this), 100, Ci.nsITimer.TYPE_ONE_SHOT);
@@ -2162,19 +2162,19 @@ MarionetteDriverActor.prototype = {
         try {
           browserType = message.target.getAttribute("type");
         } catch (ex) {
           // browserType remains undefined.
         }
         let reg = {};
         if (!browserType || browserType != "content") {
           reg.id = this.curBrowser.register(this.generateFrameId(message.json.value),
-                                         message.json.href); 
+                                            listenerWindow);
         }
-        this.curBrowser.elementManager.seenItems[reg.id] = Cu.getWeakReference(listenerWindow); //add to seenItems
+        this.curBrowser.elementManager.seenItems[reg.id] = Cu.getWeakReference(listenerWindow);
         reg.importedScripts = this.importedScripts.path;
         if (nullPrevious && (this.curBrowser.curFrameId != null)) {
           if (!this.sendAsync("newSession",
                               { B2G: (appName == "B2G") },
                               this.newSessionCommandId)) {
             return;
           }
           if (this.curBrowser.newSession) {
@@ -2294,17 +2294,17 @@ BrowserObj.prototype = {
    * @param boolean newTab
    *        If true, create new tab
    */
   startSession: function BO_startSession(newTab, win, callback) {
     if (appName != "Firefox") {
       callback(win, newTab);
     }
     else if (newTab) {
-      this.addTab(this.startPage);
+      this.tab = this.addTab(this.startPage);
       //if we have a new tab, make it the selected tab
       this.browser.selectedTab = this.tab;
       let newTabBrowser = this.browser.getBrowserForTab(this.tab);
       // wait for tab to be loaded
       newTabBrowser.addEventListener("load", function onLoad() {
         newTabBrowser.removeEventListener("load", onLoad, true);
         callback(win, newTab);
       }, true);
@@ -2330,17 +2330,17 @@ BrowserObj.prototype = {
 
   /**
    * Opens a tab with given uri
    *
    * @param string uri
    *      URI to open
    */
   addTab: function BO_addTab(uri) {
-    this.tab = this.browser.addTab(uri, true);
+    return this.browser.addTab(uri, true);
   },
 
   /**
    * Loads content listeners if we don't already have them
    *
    * @param string script
    *        path of script to load
    * @param nsIDOMWindow frame
@@ -2353,23 +2353,28 @@ BrowserObj.prototype = {
 
   /**
    * Registers a new frame, and sets its current frame id to this frame
    * if it is not already assigned, and if a) we already have a session 
    * or b) we're starting a new session and it is the right start frame.
    *
    * @param string uid
    *        frame uid
-   * @param string href
-   *        frame's href 
+   * @param object frameWindow
+   *        the DOMWindow object of the frame that's being registered
    */
-  register: function BO_register(uid, href) {
+  register: function BO_register(uid, frameWindow) {
     if (this.curFrameId == null) {
-      if ((!this.newSession) || (this.newSession && 
-          ((appName != "Firefox") || href.indexOf(this.startPage) > -1))) {
+      // If we're setting up a new session on Firefox, we only process the
+      // registration for this frame if it belongs to the tab we've just
+      // created.
+      if ((!this.newSession) ||
+          (this.newSession &&
+            ((appName != "Firefox") ||
+             frameWindow == this.browser.getBrowserForTab(this.tab).contentWindow))) {
         this.curFrameId = uid;
         this.mainContentId = uid;
       }
     }
     this.knownFrames.push(uid); //used to delete sessions
     return uid;
   },
 }
--- a/toolkit/components/osfile/osfile_shared_front.jsm
+++ b/toolkit/components/osfile/osfile_shared_front.jsm
@@ -337,16 +337,20 @@ AbstractFile.read = function read(path, 
  * device is disconnected or removed before the buffer is flushed, the
  * file may be corrupted.
  *
  * @return {number} The number of bytes actually written.
  */
 AbstractFile.writeAtomic =
      function writeAtomic(path, buffer, options = noOptions) {
 
+  // Verify that path is defined and of the correct type
+  if (typeof path != "string" || path == "") {
+    throw new TypeError("File path should be a (non-empty) string");
+  }
   let noOverwrite = options.noOverwrite;
   if (noOverwrite && OS.File.exists(path)) {
     throw OS.File.Error.exists("writeAtomic");
   }
 
   if (typeof buffer == "string") {
     // Normalize buffer to a C buffer by encoding it
     let encoding = options.encoding || "utf-8";
--- a/toolkit/components/osfile/tests/mochi/main_test_osfile_async.js
+++ b/toolkit/components/osfile/tests/mochi/main_test_osfile_async.js
@@ -474,16 +474,37 @@ let test_read_write_all = maketest("read
         test.is(view1[i], array2[i], "Offset " + i + " is correct (without flush)");
       }
       test.ok(true, "Compared re-read of partial write (without flush)");
     }
 
     // Cleanup.
     OS.File.remove(pathDest);
 
+    // Check that writeAtomic fails when destination path is undefined
+    try {
+      let path = undefined;
+      let options = {tmpPath: tmpPath};
+      yield OS.File.writeAtomic(path, contents, options);
+      test.fail("With file path undefined, writeAtomic should have failed");
+    } catch (err) {
+      test.ok(err.message == "TypeError: File path should be a (non-empty) string",
+              "With file path undefined, writeAtomic correctly failed");
+    }
+
+    // Check that writeAtomic fails when destination path is an empty string
+    try {
+      let path = "";
+      let options = {tmpPath: tmpPath};
+      yield OS.File.writeAtomic(path, contents, options);
+      test.fail("With file path an empty string, writeAtomic should have failed");
+    } catch (err) {
+      test.ok(err.message == "TypeError: File path should be a (non-empty) string",
+              "With file path an empty string, writeAtomic correctly failed");
+    }
   });
 });
 
 /**
  * Test file.{getPosition, setPosition}
  */
 let test_position = maketest("position", function position(test) {
   return Task.spawn(function() {
--- a/toolkit/components/osfile/tests/mochi/worker_test_osfile_front.js
+++ b/toolkit/components/osfile/tests/mochi/worker_test_osfile_front.js
@@ -333,16 +333,38 @@ function test_readall_writeall_file()
   try {
     OS.File.writeAtomic(tmp_file_name, readResult.buffer,
       {bytes: readResult.length});
   } catch (x) {
     exn = x;
   }
   ok(!!exn && exn instanceof TypeError, "writeAtomic fails if tmpPath is not provided");
 
+  // Check that writeAtomic fails when destination path is undefined
+  exn = null;
+  try {
+    let path = undefined;
+    let options = {tmpPath: tmp_file_name};
+    OS.File.writeAtomic(path, readResult.buffer, options);
+  } catch (x) {
+    exn = x;
+  }
+  ok(!!exn && exn instanceof TypeError, "writeAtomic fails if path is undefined");
+
+  // Check that writeAtomic fails when destination path is an empty string
+  exn = null;
+  try {
+    let path = "";
+    let options = {tmpPath: tmp_file_name};
+    OS.File.writeAtomic(path, readResult.buffer, options);
+  } catch (x) {
+    exn = x;
+  }
+  ok(!!exn && exn instanceof TypeError, "writeAtomic fails if path is an empty string");
+
   // Cleanup.
   OS.File.remove(tmp_file_name);
 }
 
 /**
  * Test that copying a file using |copy| works.
  */
 function test_copy_existing_file()
--- a/widget/android/AndroidJavaWrappers.cpp
+++ b/widget/android/AndroidJavaWrappers.cpp
@@ -2,18 +2,21 @@
  * 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 "AndroidJavaWrappers.h"
 #include "AndroidBridge.h"
 #include "nsIAndroidBridge.h"
 #include "nsIDOMKeyEvent.h"
+#include "nsIWidget.h"
+#include "nsGUIEvent.h"
 
 using namespace mozilla;
+using namespace mozilla::dom;
 
 jclass AndroidGeckoEvent::jGeckoEventClass = 0;
 jfieldID AndroidGeckoEvent::jActionField = 0;
 jfieldID AndroidGeckoEvent::jTypeField = 0;
 jfieldID AndroidGeckoEvent::jAckNeededField = 0;
 jfieldID AndroidGeckoEvent::jTimeField = 0;
 jfieldID AndroidGeckoEvent::jPoints = 0;
 jfieldID AndroidGeckoEvent::jPointIndicies = 0;
@@ -731,21 +734,21 @@ AndroidGeckoEvent::MakeTouchEvent(nsIWid
     event.InitBasicModifiers(IsCtrlPressed(),
                              IsAltPressed(),
                              IsShiftPressed(),
                              IsMetaPressed());
 
     const nsIntPoint& offset = widget->WidgetToScreenOffset();
     event.touches.SetCapacity(endIndex - startIndex);
     for (int i = startIndex; i < endIndex; i++) {
-        nsCOMPtr<nsIDOMTouch> t(new dom::Touch(PointIndicies()[i],
-                                               Points()[i] - offset,
-                                               PointRadii()[i],
-                                               Orientations()[i],
-                                               Pressures()[i]));
+        nsRefPtr<Touch> t = new Touch(PointIndicies()[i],
+                                      Points()[i] - offset,
+                                      PointRadii()[i],
+                                      Orientations()[i],
+                                      Pressures()[i]);
         event.touches.AppendElement(t);
     }
 
     return event;
 }
 
 MultiTouchInput
 AndroidGeckoEvent::MakeMultiTouchInput(nsIWidget* widget)
--- a/widget/android/AndroidJavaWrappers.h
+++ b/widget/android/AndroidJavaWrappers.h
@@ -26,16 +26,17 @@
 #define ALOG(args...)  __android_log_print(ANDROID_LOG_INFO, "Gecko" , ## args)
 #else
 #define ALOG(args...) ((void)0)
 #endif
 #endif
 
 class nsIAndroidDisplayport;
 class nsIAndroidViewport;
+class nsIWidget;
 
 namespace mozilla {
 
 class AndroidGeckoLayerClient;
 class AutoLocalJNIFrame;
 
 void InitAndroidJavaWrappers(JNIEnv *jEnv);
 
--- a/widget/windows/nsWindow.cpp
+++ b/widget/windows/nsWindow.cpp
@@ -6328,17 +6328,17 @@ bool nsWindow::OnTouch(WPARAM wParam, LP
         continue;
       }
 
       // Setup the touch point we'll append to the touch event array
       nsPointWin touchPoint;
       touchPoint.x = TOUCH_COORD_TO_PIXEL(pInputs[i].x);
       touchPoint.y = TOUCH_COORD_TO_PIXEL(pInputs[i].y);
       touchPoint.ScreenToClient(mWnd);
-      nsCOMPtr<nsIDOMTouch> touch =
+      nsRefPtr<Touch> touch =
         new Touch(pInputs[i].dwID,
                   touchPoint,
                   /* radius, if known */
                   pInputs[i].dwFlags & TOUCHINPUTMASKF_CONTACTAREA ?
                     nsIntPoint(
                       TOUCH_COORD_TO_PIXEL(pInputs[i].cxContact) / 2,
                       TOUCH_COORD_TO_PIXEL(pInputs[i].cyContact) / 2) :
                     nsIntPoint(1,1),
--- a/widget/windows/winrt/MetroInput.cpp
+++ b/widget/windows/winrt/MetroInput.cpp
@@ -126,35 +126,35 @@ namespace {
                   == aDeviceType) {
       aMozInputSource = nsIDOMMouseEvent::MOZ_SOURCE_PEN;
     }
   }
 
   /**
    * This function is for use with mTouches.Enumerate.  It will
    * append each element it encounters to the {@link nsTArray}
-   * of {@link nsIDOMTouch}es passed in through the third (void*)
+   * of {@link mozilla::dom::Touch}es passed in through the third (void*)
    * parameter.
    *
    * NOTE: This function will set the `mChanged` member of each
    * element it encounters to `false`, since this function is only
    * used to populate a touchlist that is about to be dispatched
    * in a gecko touch event.
    *
    * @param aKey the key of the current element being enumerated
    * @param aData the value of the current element being enumerated
    * @param aTouchList the {@link nsTArray} to append to
    */
   PLDHashOperator
   AppendToTouchList(const unsigned int& aKey,
-                    nsCOMPtr<nsIDOMTouch>& aData,
+                    nsRefPtr<Touch>& aData,
                     void *aTouchList)
   {
-    nsTArray<nsCOMPtr<nsIDOMTouch> > *touches =
-              static_cast<nsTArray<nsCOMPtr<nsIDOMTouch> > *>(aTouchList);
+    nsTArray<nsRefPtr<Touch> > *touches =
+              static_cast<nsTArray<nsRefPtr<Touch> > *>(aTouchList);
     touches->AppendElement(aData);
     aData->mChanged = false;
     return PL_DHASH_NEXT;
   }
 
   // In response to keyboard events, we create an `NPEvent` and point the
   // `pluginEvent` member of our `nsInputEvent` to it.  We must set the
   // `wParam` and `lParam` members of our `NPEvent` according to what
@@ -800,17 +800,17 @@ MetroInput::OnPointerPressed(UI::Core::I
     mGestureRecognizer->ProcessDownEvent(currentPoint.Get());
     return S_OK;
   }
 
   // This is touch input.
   // Create the new touch point and add it to our event.
   uint32_t pointerId;
   currentPoint->get_PointerId(&pointerId);
-  nsCOMPtr<nsIDOMTouch> touch = CreateDOMTouch(currentPoint.Get());
+  nsRefPtr<Touch> touch = CreateDOMTouch(currentPoint.Get());
   touch->mChanged = true;
   mTouches.Put(pointerId, touch);
   mTouchEvent.message = NS_TOUCH_START;
 
   // If this is the first touchstart of a touch session,
   // dispatch it now so we can see if preventDefault gets called on it.
   if (mTouches.Count() == 1) {
     nsEventStatus status;
@@ -859,17 +859,17 @@ MetroInput::OnPointerReleased(UI::Core::
     mGestureRecognizer->ProcessUpEvent(currentPoint.Get());
     return S_OK;
   }
 
   // This is touch input.
   // Get the touch associated with this touch point.
   uint32_t pointerId;
   currentPoint->get_PointerId(&pointerId);
-  nsCOMPtr<nsIDOMTouch> touch = mTouches.Get(pointerId);
+  nsRefPtr<Touch> touch = mTouches.Get(pointerId);
 
   // We are about to dispatch a touchend.  Before we do that, we should make
   // sure that we don't have a touchmove or touchstart sitting around for this
   // point.
   if (touch->mChanged) {
     DispatchPendingTouchEvent();
   }
   mTouches.Remove(pointerId);
@@ -928,17 +928,17 @@ MetroInput::OnPointerMoved(UI::Core::ICo
     mGestureRecognizer->ProcessMoveEvents(pointerPoints.Get());
     return S_OK;
   }
 
   // This is touch input.
   // Get the touch associated with this touch point.
   uint32_t pointerId;
   currentPoint->get_PointerId(&pointerId);
-  nsCOMPtr<nsIDOMTouch> touch = mTouches.Get(pointerId);
+  nsRefPtr<Touch> touch = mTouches.Get(pointerId);
 
   // Some old drivers cause us to receive a PointerMoved event for a touchId
   // after we've already received a PointerReleased event for that touchId.
   // To work around those busted drivers, we simply ignore TouchMoved events
   // for touchIds that we are not currently tracking.  See bug 819223.
   if (!touch) {
     return S_OK;
   }
--- a/widget/windows/winrt/MetroInput.h
+++ b/widget/windows/winrt/MetroInput.h
@@ -15,21 +15,26 @@
 // System headers (alphabetical)
 #include <EventToken.h>     // EventRegistrationToken
 #include <stdint.h>         // uint32_t
 #include <wrl\client.h>     // Microsoft::WRL::ComPtr class
 #include <wrl\implements.h> // Microsoft::WRL::InspectableClass macro
 
 // Moz forward declarations
 class MetroWidget;
-class nsIDOMTouch;
 enum nsEventStatus;
 class nsGUIEvent;
 struct nsIntPoint;
 
+namespace mozilla {
+namespace dom {
+class Touch;
+}
+}
+
 // Windows forward declarations
 namespace ABI {
   namespace Windows {
     namespace Devices {
       namespace Input {
         enum PointerDeviceType;
       }
     };
@@ -230,18 +235,18 @@ private:
   // the updated touchpoint info and record the fact that the touchpoint
   // has changed.  If ever we try to update a touchpoint has already
   // changed, we dispatch a touch event containing all the changed touches.
   nsEventStatus mTouchEventStatus;
   nsTouchEvent mTouchEvent;
   void DispatchPendingTouchEvent();
   void DispatchPendingTouchEvent(nsEventStatus& status);
   nsBaseHashtable<nsUint32HashKey,
-                  nsCOMPtr<nsIDOMTouch>,
-                  nsCOMPtr<nsIDOMTouch> > mTouches;
+                  nsRefPtr<mozilla::dom::Touch>,
+                  nsRefPtr<mozilla::dom::Touch> > mTouches;
 
   // When a key press is received, we convert the Windows virtual key
   // into a gecko virtual key to send in a gecko event.
   //
   // Source:
   // http://msdn.microsoft.com
   //       /en-us/library/windows/apps/windows.system.virtualkey.aspx
   static uint32_t sVirtualKeyMap[255];
--- a/widget/xpwidgets/InputData.cpp
+++ b/widget/xpwidgets/InputData.cpp
@@ -3,16 +3,17 @@
  * 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 "InputData.h"
 
 #include "nsGUIEvent.h"
 #include "mozilla/dom/Touch.h"
 #include "nsDebug.h"
+#include "nsThreadUtils.h"
 
 namespace mozilla {
 
 using namespace dom;
 
 MultiTouchInput::MultiTouchInput(const nsTouchEvent& aTouchEvent)
   : InputData(MULTITOUCH_INPUT, aTouchEvent.time)
 {
--- a/xpcom/base/nsCycleCollector.cpp
+++ b/xpcom/base/nsCycleCollector.cpp
@@ -153,17 +153,16 @@
 using namespace mozilla;
 
 //#define COLLECT_TIME_DEBUG
 
 // Enable assertions that are useful for diagnosing errors in graph construction.
 //#define DEBUG_CC_GRAPH
 
 #define DEFAULT_SHUTDOWN_COLLECTIONS 5
-#define SHUTDOWN_COLLECTIONS(params) DEFAULT_SHUTDOWN_COLLECTIONS
 
 #if defined(XP_WIN)
 // Defined in nsThreadManager.cpp.
 extern DWORD gTLSThreadIDIndex;
 #elif defined(NS_TLS)
 // Defined in nsThreadManager.cpp.
 extern NS_TLS mozilla::threads::ID gTLSThreadID;
 #else
@@ -912,60 +911,66 @@ nsPurpleBuffer::SelectPointers(GCGraphBu
 
     NS_ASSERTION(mCount == 0, "AddPurpleRoot failed");
     if (mCount == 0) {
         FreeBlocks();
         InitBlocks();
     }
 }
 
+enum ccType {
+    ScheduledCC, /* Automatically triggered, based on time or the purple buffer. */
+    ManualCC,    /* Explicitly triggered. */
+    ShutdownCC   /* Shutdown CC, used for finding leaks. */
+};
+
 class nsCycleCollector;
 
 class nsCycleCollectorRunner : public nsRunnable
 {
     nsCycleCollector *mCollector;
     CCThreadingModel mModel;
     nsICycleCollectorListener *mListener;
     nsCOMPtr<nsIThread> mThread;
     Mutex mLock;
     CondVar mRequest;
     CondVar mReply;
     bool mRunning;
     bool mShutdown;
     bool mCollected;
-    bool mMergeZones;
+    ccType mCCType;
 
 public:
     nsCycleCollectorRunner(nsCycleCollector *collector,
                            CCThreadingModel aModel)
         : mCollector(collector),
           mModel(aModel),
           mListener(nullptr),
           mLock("cycle collector lock"),
           mRequest(mLock, "cycle collector request condvar"),
           mReply(mLock, "cycle collector reply condvar"),
           mRunning(false),
           mShutdown(false),
           mCollected(false),
-          mMergeZones(false)
+          mCCType(ScheduledCC)
     {
         MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
     }
 
     NS_IMETHOD Run();
 
     nsresult Init()
     {
         if (mModel == CCSingleThread)
             return NS_OK;
 
         return NS_NewThread(getter_AddRefs(mThread), this);
     }
 
-    void Collect(bool aMergeZones,
+    void Collect(ccType aCCType,
                  nsCycleCollectorResults *aResults,
                  nsICycleCollectorListener *aListener);
 
     void Shutdown()
     {
         MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
 
         if (!mThread)
@@ -1024,16 +1029,19 @@ private:
 
     CC_BeforeUnlinkCallback mBeforeUnlinkCB;
     CC_ForgetSkippableCallback mForgetSkippableCB;
 
     nsCOMPtr<nsIMemoryMultiReporter> mReporter;
 
     nsPurpleBuffer mPurpleBuf;
 
+    uint32_t mUnmergedNeeded;
+    uint32_t mMergedInARow;
+
 public:
     void RegisterJSRuntime(nsCycleCollectionJSRuntime *aJSRuntime);
     void ForgetJSRuntime();
 
     inline nsCycleCollectionJSRuntime*
     JSRuntime() const
     {
         return mJSRuntime;
@@ -1065,34 +1073,32 @@ public:
     nsresult Init();
     void ShutdownThreads();
 
     nsPurpleBufferEntry* Suspect(void *n, nsCycleCollectionParticipant *cp);
 
     void CheckThreadSafety();
 
 private:
-    void MainThreadCollect(bool aMergeZones,
-                           nsCycleCollectorResults *aResults,
-                           uint32_t aTryCollections,
-                           nsICycleCollectorListener *aListener);
+    void ShutdownCollect(nsICycleCollectorListener *aListener);
 
 public:
-    void Collect(bool aMergeZones,
+    void Collect(ccType aCCType,
                  nsCycleCollectorResults *aResults,
                  nsICycleCollectorListener *aListener);
 
     // Prepare for and cleanup after one or more collection(s).
     bool PrepareForCollection(nsCycleCollectorResults *aResults,
                               nsTArray<PtrInfo*> *aWhiteNodes);
     void FixGrayBits(bool aForceGC);
+    bool ShouldMergeZones(ccType aCCType);
     void CleanupAfterCollection();
 
     // Start and finish an individual collection.
-    bool BeginCollection(bool aMergeZones, nsICycleCollectorListener *aListener);
+    bool BeginCollection(ccType aCCType, nsICycleCollectorListener *aListener);
     bool FinishCollection(nsICycleCollectorListener *aListener);
 
     uint32_t SuspectedCount();
     void Shutdown();
 
     void ClearGraph()
     {
         mGraph.mNodes.Clear();
@@ -1139,27 +1145,27 @@ nsCycleCollectorRunner::Run()
         mRequest.Wait();
 
         if (!mRunning) {
             mReply.Notify();
             return NS_OK;
         }
 
         mCollector->JSRuntime()->NotifyEnterCycleCollectionThread();
-        mCollected = mCollector->BeginCollection(mMergeZones, mListener);
+        mCollected = mCollector->BeginCollection(mCCType, mListener);
         mCollector->JSRuntime()->NotifyLeaveCycleCollectionThread();
 
         mReply.Notify();
     }
 
     return NS_OK;
 }
 
 void
-nsCycleCollectorRunner::Collect(bool aMergeZones,
+nsCycleCollectorRunner::Collect(ccType aCCType,
                                 nsCycleCollectorResults *aResults,
                                 nsICycleCollectorListener *aListener)
 {
     MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
 
     // On a WantAllTraces CC, force a synchronous global GC to prevent
     // hijinks from ForgetSkippable and compartmental GCs.
     bool wantAllTraces = false;
@@ -1176,25 +1182,25 @@ nsCycleCollectorRunner::Collect(bool aMe
     nsAutoTArray<PtrInfo*, 4000> whiteNodes;
     if (!mCollector->PrepareForCollection(aResults, &whiteNodes))
         return;
 
     MOZ_ASSERT(!mListener, "Should have cleared this already!");
     if (aListener && NS_FAILED(aListener->Begin()))
         aListener = nullptr;
     mListener = aListener;
-    mMergeZones = aMergeZones;
+    mCCType = aCCType;
 
     if (mModel == CCWithTraverseThread &&
         mCollector->JSRuntime()->NotifyLeaveMainThread()) {
         mRequest.Notify();
         mReply.Wait();
         mCollector->JSRuntime()->NotifyEnterMainThread();
     } else {
-        mCollected = mCollector->BeginCollection(aMergeZones, mListener);
+        mCollected = mCollector->BeginCollection(aCCType, mListener);
     }
 
     mListener = nullptr;
 
     if (mCollected) {
         mCollector->FinishCollection(aListener);
         mCollector->CleanupAfterCollection();
     }
@@ -2516,17 +2522,19 @@ nsCycleCollector::nsCycleCollector(CCThr
     mThread(PR_GetCurrentThread()),
     mWhiteNodes(nullptr),
     mWhiteNodeCount(0),
     mVisitedRefCounted(0),
     mVisitedGCed(0),
     mBeforeUnlinkCB(nullptr),
     mForgetSkippableCB(nullptr),
     mReporter(nullptr),
-    mPurpleBuf(mParams)
+    mPurpleBuf(mParams),
+    mUnmergedNeeded(0),
+    mMergedInARow(0)
 {
     nsRefPtr<nsCycleCollectorRunner> runner =
         new nsCycleCollectorRunner(this, aModel);
     runner.forget(&mRunner);
 }
 
 nsCycleCollector::~nsCycleCollector()
 {
@@ -2732,62 +2740,96 @@ nsCycleCollector::CleanupAfterCollection
     }
     Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR, interval);
     Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_VISITED_REF_COUNTED, mVisitedRefCounted);
     Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_VISITED_GCED, mVisitedGCed);
     Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_COLLECTED, mWhiteNodeCount);
 }
 
 void
-nsCycleCollector::MainThreadCollect(bool aMergeZones,
-                                    nsCycleCollectorResults *aResults,
-                                    uint32_t aTryCollections,
-                                    nsICycleCollectorListener *aListener)
+nsCycleCollector::ShutdownCollect(nsICycleCollectorListener *aListener)
 {
     nsAutoTArray<PtrInfo*, 4000> whiteNodes;
 
-    if (!PrepareForCollection(aResults, &whiteNodes))
+    if (!PrepareForCollection(nullptr, &whiteNodes))
         return;
 
-    uint32_t totalCollections = 0;
-    while (aTryCollections > totalCollections) {
+    for (uint32_t i = 0; i < DEFAULT_SHUTDOWN_COLLECTIONS; ++i) {
         // Synchronous cycle collection. Always force a JS GC beforehand.
         FixGrayBits(true);
         if (aListener && NS_FAILED(aListener->Begin()))
             aListener = nullptr;
-        if (!(BeginCollection(aMergeZones, aListener) &&
+        if (!(BeginCollection(ShutdownCC, aListener) &&
               FinishCollection(aListener)))
             break;
-
-        ++totalCollections;
     }
 
     CleanupAfterCollection();
 }
 
 void
-nsCycleCollector::Collect(bool aMergeZones,
+nsCycleCollector::Collect(ccType aCCType,
                           nsCycleCollectorResults *aResults,
                           nsICycleCollectorListener *aListener)
 {
-    mRunner->Collect(aMergeZones, aResults, aListener);
+    mRunner->Collect(aCCType, aResults, aListener);
+}
+
+// Don't merge too many times in a row, and do at least a minimum
+// number of unmerged CCs in a row.
+static const uint32_t kMinConsecutiveUnmerged = 3;
+static const uint32_t kMaxConsecutiveMerged = 3;
+
+bool
+nsCycleCollector::ShouldMergeZones(ccType aCCType)
+{
+    if (!mJSRuntime) {
+        return false;
+    }
+
+    MOZ_ASSERT(mUnmergedNeeded <= kMinConsecutiveUnmerged);
+    MOZ_ASSERT(mMergedInARow <= kMaxConsecutiveMerged);
+
+    if (mMergedInARow == kMaxConsecutiveMerged) {
+        MOZ_ASSERT(mUnmergedNeeded == 0);
+        mUnmergedNeeded = kMinConsecutiveUnmerged;
+    }
+
+    if (mUnmergedNeeded > 0) {
+        mUnmergedNeeded--;
+        mMergedInARow = 0;
+        return false;
+    }
+
+    if (aCCType == ScheduledCC && mJSRuntime->UsefulToMergeZones()) {
+        mMergedInARow++;
+        return true;
+    } else {
+        mMergedInARow = 0;
+        return false;
+    }
 }
 
 bool
-nsCycleCollector::BeginCollection(bool aMergeZones,
+nsCycleCollector::BeginCollection(ccType aCCType,
                                   nsICycleCollectorListener *aListener)
 {
     // aListener should be Begin()'d before this
     TimeLog timeLog;
 
     if (mParams.mDoNothing)
         return false;
 
+    bool mergeZones = ShouldMergeZones(aCCType);
+    if (mResults) {
+        mResults->mMergedZones = mergeZones;
+    }
+
     GCGraphBuilder builder(this, mGraph, mJSRuntime, aListener,
-                           aMergeZones);
+                           mergeZones);
     if (!builder.Initialized())
         return false;
 
     if (mJSRuntime) {
         mJSRuntime->BeginCycleCollection(builder);
         timeLog.Checkpoint("mJSRuntime->BeginCycleCollection()");
     }
 
@@ -2861,18 +2903,17 @@ nsCycleCollector::Shutdown()
     {
         nsCOMPtr<nsCycleCollectorLogger> listener;
         if (mParams.mLogAll || mParams.mLogShutdown) {
             listener = new nsCycleCollectorLogger();
             if (mParams.mAllTracesAtShutdown) {
                 listener->SetAllTraces();
             }
         }
-        MainThreadCollect(false, nullptr,  SHUTDOWN_COLLECTIONS(mParams),
-                          listener);
+        ShutdownCollect(listener);
     }
 
     mParams.mDoNothing = true;
 }
 
 void
 nsCycleCollector::SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf,
                                       size_t *aObjectSize,
@@ -3024,33 +3065,33 @@ nsCycleCollector_forgetSkippable(bool aR
 
     PROFILER_LABEL("CC", "nsCycleCollector_forgetSkippable");
     TimeLog timeLog;
     collector->ForgetSkippable(aRemoveChildlessNodes);
     timeLog.Checkpoint("ForgetSkippable()");
 }
 
 void
-nsCycleCollector_collect(bool aMergeZones,
+nsCycleCollector_collect(bool aManuallyTriggered,
                          nsCycleCollectorResults *aResults,
                          nsICycleCollectorListener *aListener)
 {
     nsCycleCollector *collector = sCollector.get();
 
     if (!collector) {
         MOZ_CRASH();
     }
 
     PROFILER_LABEL("CC", "nsCycleCollector_collect");
     nsCOMPtr<nsICycleCollectorListener> listener(aListener);
     if (!aListener && collector->mParams.mLogAll) {
         listener = new nsCycleCollectorLogger();
     }
 
-    collector->Collect(aMergeZones, aResults, listener);
+    collector->Collect(aManuallyTriggered ? ManualCC : ScheduledCC, aResults, listener);
 }
 
 void
 nsCycleCollector_shutdownThreads()
 {
     nsCycleCollector *collector = sCollector.get();
 
     MOZ_ASSERT(collector);
--- a/xpcom/base/nsCycleCollector.h
+++ b/xpcom/base/nsCycleCollector.h
@@ -10,19 +10,21 @@ class nsCycleCollectionJSRuntime;
 class nsICycleCollectorListener;
 class nsISupports;
 
 // Contains various stats about the cycle collection.
 class nsCycleCollectorResults
 {
 public:
     nsCycleCollectorResults() :
-        mForcedGC(false), mVisitedRefCounted(0), mVisitedGCed(0),
+        mForcedGC(false), mMergedZones(false),
+        mVisitedRefCounted(0), mVisitedGCed(0),
         mFreedRefCounted(0), mFreedGCed(0) {}
     bool mForcedGC;
+    bool mMergedZones;
     uint32_t mVisitedRefCounted;
     uint32_t mVisitedGCed;
     uint32_t mFreedRefCounted;
     uint32_t mFreedGCed;
 };
 
 bool nsCycleCollector_init();
 
@@ -36,17 +38,17 @@ nsresult nsCycleCollector_startup(CCThre
 typedef void (*CC_BeforeUnlinkCallback)(void);
 void nsCycleCollector_setBeforeUnlinkCallback(CC_BeforeUnlinkCallback aCB);
 
 typedef void (*CC_ForgetSkippableCallback)(void);
 void nsCycleCollector_setForgetSkippableCallback(CC_ForgetSkippableCallback aCB);
 
 void nsCycleCollector_forgetSkippable(bool aRemoveChildlessNodes = false);
 
-void nsCycleCollector_collect(bool aMergeCompartments,
+void nsCycleCollector_collect(bool aManuallyTriggered,
                               nsCycleCollectorResults *aResults,
                               nsICycleCollectorListener *aListener);
 uint32_t nsCycleCollector_suspectedCount();
 void nsCycleCollector_shutdownThreads();
 void nsCycleCollector_shutdown();
 
 // Helpers for interacting with JS
 void nsCycleCollector_registerJSRuntime(nsCycleCollectionJSRuntime *aRt);
--- a/xpcom/glue/nsCycleCollectionJSRuntime.h
+++ b/xpcom/glue/nsCycleCollectionJSRuntime.h
@@ -27,16 +27,21 @@ struct nsCycleCollectionJSRuntime
   virtual void NotifyEnterMainThread() = 0;
 
   /**
    * Unmark gray any weak map values, as needed.
    */
   virtual void FixWeakMappingGrayBits() = 0;
 
   /**
+   * Return true if merging content zones may greatly reduce the size of the CC graph.
+   */
+  virtual bool UsefulToMergeZones() = 0;
+
+  /**
    * Should we force a JavaScript GC before a CC?
    */
   virtual bool NeedCollect() = 0;
 
   /**
    * Runs the JavaScript GC. |reason| is a gcreason::Reason from jsfriendapi.h.
    */
   virtual void Collect(uint32_t aReason) = 0;