merge mozilla-inbound to mozilla-central a=merge
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Mon, 17 Oct 2016 11:19:02 +0200
changeset 425967 94b0fddf96b43942bdd851a3275042909ea37e09
parent 425902 0191c2f3a6e8c0a241bc440cb61a8f15d061bc38 (current diff)
parent 425929 9707f0972299ceb207d7f4ad64f720cba38f0a14 (diff)
child 425968 bd8e68d8e3026afc426e5cbc7ef17e886fcbb342
child 425979 765a09a063466f1219f8905e74fa5e517234ef95
child 425980 4f16390894a7035dad4771afc94bd27bc108a304
child 425981 07871a6b42bb79a727a014ddfb258a8f6d3648cc
child 425982 aa8638e9de6a9fa2ed435f2911eaa159b0c32d7c
child 425986 f1520194d36bd3e8bf9f4c7e35a6dc6fe184d6df
child 425987 a5ef6bd645aef65785b9cc20d1ff8c1e786d58c9
child 425999 1db6d733d04865446d5a299a8f5f5f3dde85ded9
child 426000 bad8a0206cdbceda441134e470411da39ffae8c5
child 426002 acddae7bce4d11ed00b82836a989f252769e7898
child 426008 b18d7b9343ed89dc08be0a22f4fa072b7c8c38a1
child 426017 b42a9759a65948f42bb69a2b0e67ce9492881eaa
child 426028 8040f04ac33bd2de5ecc24578bdad6df105f4ed3
child 426031 9398368334be3138ea37bb7f715d292784b58156
child 426033 c0bb4a1e4404fb28edeaff26d7b86419004955e3
child 426034 5672f1ac86079c2fa0b4e6037da255f7e630ed6e
child 426036 e6e9655e9ff6a9d73b4d2b215a31855071d7d707
child 426051 78bd3dbc3ddee84be7d9b79fde3614eec50d39a9
child 426053 aebbe671eb788c3876c0f3be9dd01b4c297c3c54
child 426084 fcbda6d0afa62931d49a94ec2f490271b528ac5e
child 426085 811fe85b6452df5c1c1aa1cc162b5cc363470034
child 426136 e4ef6fa03aa8689479827c4c7ea6dd5d26675c01
child 426443 78c2bb397c56df3069b4ec627a6a74fea84d35a9
child 426453 1cd2d4e5662563eb79d6e439237603b1b9d9f3fd
child 426474 f70b371688e4a230be645c5270b2c5e5509b2d7f
child 426479 5697b44478ac17cd5f7bc2635ef87b09929d8d2d
child 426485 8ed74e4ee22401e787b9b1249356a7e4e40ce775
child 426487 0d1b4355699a533cc0f014b793759a01c2ac839a
child 426488 c7fad873d155d1433811a78d9f74c056d0dbf059
child 426509 9430dbc93a838e281dcf784b68db03fb812ceb50
child 426524 31332c2e575483de15a95211fff35b2133441e3e
child 426530 4c3c4c82d73ec88e4477ae39753853f7cf662aac
child 426532 a3e960ac1f7534f88d4d99925c6a5e3c45af9a51
child 426564 ac85cb1c10f353a8e44d011095bfd25d7ba4e255
child 426940 d36aefb091809da08f81b66c896c3e7958f8a9ac
child 428044 ca9ec75514b42605b05c38b18880408843ca8183
child 428296 51066668c6c4419f2d04f4bf10fa712a79863994
child 428297 0b34ad0b2af6b9b4d782ad7e9b4cca0d6d5cc9e5
push id32552
push userbmo:bpostelnicu@mozilla.com
push dateMon, 17 Oct 2016 11:57:53 +0000
reviewersmerge
milestone52.0a1
merge mozilla-inbound to mozilla-central a=merge
addon-sdk/source/lib/sdk/system/child_process/LICENSE
addon-sdk/source/lib/sdk/system/child_process/README.md
addon-sdk/source/lib/sdk/system/child_process/subprocess_worker_unix.js
addon-sdk/source/lib/sdk/system/child_process/subprocess_worker_win.js
dom/base/CustomElementsRegistry.cpp
dom/base/CustomElementsRegistry.h
dom/base/nsDocument.cpp
dom/ipc/TabChild.cpp
dom/media/MediaManager.cpp
dom/webidl/CustomElementsRegistry.webidl
dom/webidl/moz.build
layout/base/nsDisplayList.cpp
layout/base/nsDisplayList.h
layout/build/nsLayoutStatics.cpp
modules/libpref/init/all.js
old-configure.in
security/nss/cmd/ectest/Makefile
security/nss/cmd/ectest/ectest.c
security/nss/cmd/ectest/manifest.mn
security/nss/cmd/ectest/testvecs.h
testing/web-platform/meta/content-security-policy/blink-contrib-2/scripthash-unicode-normalization.sub.html.ini
testing/web-platform/tests/2dcontext/fill-and-stroke-styles/2d.fillStyle.parse.invalid.rgb-4.html
testing/web-platform/tests/2dcontext/fill-and-stroke-styles/2d.fillStyle.parse.invalid.rgb-5.html
testing/web-platform/tests/2dcontext/fill-and-stroke-styles/2d.fillStyle.parse.invalid.rgb-6.html
testing/web-platform/tests/2dcontext/fill-and-stroke-styles/2d.fillStyle.parse.invalid.rgb-7.html
testing/web-platform/tests/2dcontext/fill-and-stroke-styles/2d.fillStyle.parse.invalid.rgba-6.html
testing/web-platform/tests/2dcontext/fill-and-stroke-styles/2d.fillStyle.parse.invalid.rgba-7.html
--- a/CLOBBER
+++ b/CLOBBER
@@ -17,9 +17,9 @@
 #
 # Modifying this file will now automatically clobber the buildbot machines \o/
 #
 
 # Are you updating CLOBBER because you think it's needed for your WebIDL
 # changes to stick? As of bug 928195, this shouldn't be necessary! Please
 # don't change CLOBBER for WebIDL changes any more.
 
-Bug 1261019 - Clobber on OS X to fix bustage caused by removed files
+Bug 1306438 and bug 1304815 - Rust update and related changes require clobber
--- a/addon-sdk/moz.build
+++ b/addon-sdk/moz.build
@@ -475,18 +475,16 @@ EXTRA_JS_MODULES.commonjs.sdk.system += 
     'source/lib/sdk/system/runtime.js',
     'source/lib/sdk/system/unload.js',
     'source/lib/sdk/system/xul-app.js',
     'source/lib/sdk/system/xul-app.jsm',
 ]
 
 EXTRA_JS_MODULES.commonjs.sdk.system.child_process += [
     'source/lib/sdk/system/child_process/subprocess.js',
-    'source/lib/sdk/system/child_process/subprocess_worker_unix.js',
-    'source/lib/sdk/system/child_process/subprocess_worker_win.js',
 ]
 
 EXTRA_JS_MODULES.commonjs.sdk.tab += [
     'source/lib/sdk/tab/events.js',
 ]
 
 EXTRA_JS_MODULES.commonjs.sdk.ui.button.view += [
     'source/lib/sdk/ui/button/view/events.js',
--- a/addon-sdk/source/app-extension/bootstrap.js
+++ b/addon-sdk/source/app-extension/bootstrap.js
@@ -293,18 +293,21 @@ function loadSandbox(uri) {
       CC: bind(CC, Components), components: Components,
       ChromeWorker: ChromeWorker });
   };
   scriptLoader.loadSubScript(uri, sandbox, 'UTF-8');
   return sandbox;
 }
 
 function unloadSandbox(sandbox) {
-  if ("nukeSandbox" in Cu)
-    Cu.nukeSandbox(sandbox);
+  if ("nukeSandbox" in Cu) {
+    try {
+      Cu.nukeSandbox(sandbox);
+    } catch (e) {}
+  }
 }
 
 function setTimeout(callback, delay) {
   let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
   timer.initWithCallback({ notify: callback }, delay,
                          Ci.nsITimer.TYPE_ONE_SHOT);
   return timer;
 }
@@ -341,16 +344,17 @@ function nukeModules() {
   }
   // Direct links to sandboxes should be removed too
   for (let key in loader.sandboxes) {
     let sandbox = loader.sandboxes[key];
     delete loader.sandboxes[key];
     // Bug 775067: From FF17 we can kill all CCW from a given sandbox
     unloadSandbox(sandbox);
   }
+  unloadSandbox(loader.sharedGlobalSandbox);
   loader = null;
 
   // both `toolkit/loader` and `system/xul-app` are loaded as JSM's via
   // `cuddlefish.js`, and needs to be unloaded to avoid memory leaks, when
   // the addon is unload.
 
   unloadSandbox(cuddlefishSandbox.loaderSandbox);
 
--- a/addon-sdk/source/lib/sdk/addon/bootstrap.js
+++ b/addon-sdk/source/lib/sdk/addon/bootstrap.js
@@ -152,21 +152,31 @@ Bootstrap.prototype = {
     return new Promise(resolve => {
       const { loader } = this;
       if (loader) {
         this.loader = null;
         unload(loader, reason);
 
         setTimeout(() => {
           for (let uri of Object.keys(loader.sandboxes)) {
-            Cu.nukeSandbox(loader.sandboxes[uri]);
+            try {
+              Cu.nukeSandbox(loader.sandboxes[uri]);
+            } catch (e) {
+              // This will throw for shared sandboxes.
+            }
             delete loader.sandboxes[uri];
             delete loader.modules[uri];
           }
 
+          try {
+            Cu.nukeSandbox(loader.sharedGlobalSandbox);
+          } catch (e) {
+            Cu.reportError(e);
+          }
+
           resolve();
         }, 1000);
       }
       else {
         resolve();
       }
     });
   }
--- a/addon-sdk/source/lib/sdk/system/child_process.js
+++ b/addon-sdk/source/lib/sdk/system/child_process.js
@@ -56,17 +56,20 @@ var Child = Class({
             child.stdin.off('data', pumpStdin);
             child.stdin.off('end', closeStdin);
             stream.close();
           });
           function pumpStdin (data) {
             stream.write(data);
           }
         },
-        done: function (result) {
+        done: function (result, error) {
+          if (error)
+            return handleError(error);
+
           // Only emit if child is not killed; otherwise,
           // the `kill` method will handle this
           if (!child.killed) {
             child.exitCode = result.exitCode;
             child.signalCode = null;
 
             // If process exits with < 0, there was an error
             if (child.exitCode < 0) {
@@ -165,17 +168,17 @@ function exec (cmd, ...args) {
     callback = args[0];
   else {
     merge(options, args[0]);
     callback = args[1];
   }
 
   if (isWindows) {
     file = 'C:\\Windows\\System32\\cmd.exe';
-    cmdArgs = ['/s', '/c', (cmd || '').split(' ')];
+    cmdArgs = ['/S/C', cmd || ''];
   }
   else {
     file = '/bin/sh';
     cmdArgs = ['-c', cmd];
   }
 
   // Undocumented option from node being able to specify shell
   if (options && options.shell)
deleted file mode 100644
--- a/addon-sdk/source/lib/sdk/system/child_process/LICENSE
+++ /dev/null
@@ -1,36 +0,0 @@
-<!-- ***** BEGIN LICENSE BLOCK *****
-   - Version: MPL 1.1/GPL 2.0/LGPL 2.1
-   -
-   - The contents of this file are subject to the Mozilla Public License Version
-   - 1.1 (the "License"); you may not use this file except in compliance with
-   - the License. You may obtain a copy of the License at
-   - http://www.mozilla.org/MPL/
-   -
-   - Software distributed under the License is distributed on an "AS IS" basis,
-   - WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
-   - for the specific language governing rights and limitations under the
-   - License.
-   -
-   - The Original Code is subprocess.jsm.
-   -
-   - The Initial Developer of this code is Jan Gerber.
-   - Portions created by Jan Gerber <j@mailb.org>
-   - are Copyright (C) 2011 Jan Gerber.
-   - All Rights Reserved.
-   -
-   -
-   - Contributor(s):
-   -
-   - Alternatively, the contents of this file may be used under the terms of
-   - either the GNU General Public License Version 2 or later (the "GPL"), or
-   - the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
-   - in which case the provisions of the GPL or the LGPL are applicable instead
-   - of those above. If you wish to allow use of your version of this file only
-   - under the terms of either the GPL or the LGPL, and not to allow others to
-   - use your version of this file under the terms of the MPL, indicate your
-   - decision by deleting the provisions above and replace them with the notice
-   - and other provisions required by the LGPL or the GPL. If you do not delete
-   - the provisions above, a recipient may use your version of this file under
-   - the terms of any one of the MPL, the GPL or the LGPL.
-   -
-   - ***** END LICENSE BLOCK ***** -->
deleted file mode 100644
--- a/addon-sdk/source/lib/sdk/system/child_process/README.md
+++ /dev/null
@@ -1,129 +0,0 @@
-# subprocess
-
-
-Created by [Jan Gerber](j@mailb.org), with contributions from [Patrick Brunschwig](patrick@enigmail.net), subprocess.jsm is used as the underlying library, supporting the Addon-SDK's implementation of Node's `child-process` API.
-
-`subprocess.jsm` is originally from [http://hg.mozilla.org/ipccode/](http://hg.mozilla.org/ipccode/).
-
-How to Use subprocess.jsm in your Add-on
-----------------------------------------
-
-1. copy subprocess.jsm and subprocess_worker_*.js into the modules/ directory
-   of your add-on.
-
-2. add this line to chrome.manifest:
- resource EXTENSION modules/
-
-3. import it where needed:
- Components.utils.import("resource://EXTENSION/subprocess.jsm");
-
-This object allows to start a process, and read/write data to/from it
-using stdin/stdout/stderr streams.
-
-Usage example:
-
-  var p = subprocess.call({
-    command: '/bin/foo',
-    arguments: ['-v', 'foo'],
-    environment: [ "XYZ=abc", "MYVAR=def" ],
-    charset: 'UTF-8',
-    workdir: '/home/foo',
-    //stdin: "some value to write to stdin\nfoobar",
-    stdin: function(stdin) {
-      stdin.write("some value to write to stdin\nfoobar");
-      stdin.close();
-    },
-    stdout: function(data) {
-      dump("got data on stdout:" + data + "\n");
-    },
-    stderr: function(data) {
-      dump("got data on stderr:" + data + "\n");
-    },
-    done: function(result) {
-      dump("process terminated with " + result.exitCode + "\n");
-    },
-    mergeStderr: false
-  });
-
-  p.wait(); // wait for the subprocess to terminate,
-            // this will block the main thread,
-            // only do if you can wait that long
-
-
-Description of subprocess.call(...) Parameters
-----------------------------------------------
-Apart from <command>, all arguments are optional.
-
- command: either a |nsIFile| object pointing to an executable file or a
-              String containing the platform-dependent path to an executable
-              file.
-
- arguments: optional string array containing the arguments to the command.
-
- environment: optional string array containing environment variables to pass
-              to the command. The array elements must have the form
-              "VAR=data". Please note that if environment is defined, it
-              replaces any existing environment variables for the subprocess.
-
- charset: Output is decoded with given charset and a string is returned.
-              If charset is undefined, "UTF-8" is used as default.
-              To get binary data, set this to null and the returned string
-              is not decoded in any way.
-
- workdir: Optional; either a |nsIFile| object or string containing the
-              platform-dependent path to a directory to become the current
-              working directory of the subprocess.
-
- stdin: Optional input data for the process to be passed on standard
-              input. stdin can either be a string or a function.
-              A |string| gets written to stdin and stdin gets closed;
-              A |function| gets passed an object with write and close function.
-              Please note that the write() function will return almost immediately;
-              data is always written asynchronously on a separate thread.
-
- stdout: An optional function that can receive output data from the
-              process. The stdout-function is called asynchronously; it can be
-              called mutliple times during the execution of a process.
-              At a minimum at each occurance of \n or \r.
-              Please note that null-characters might need to be escaped
-              with something like 'data.replace(/\0/g, "\\0");'.
-
- stderr: An optional function that can receive stderr data from the
-              process. The stderr-function is called asynchronously; it can be
-              called mutliple times during the execution of a process. Please
-              note that null-characters might need to be escaped with
-              something like 'data.replace(/\0/g, "\\0");'.
-              (on windows it only gets called once right now)
-
- done: Optional function that is called when the process has terminated.
-              The exit code from the process available via result.exitCode. If
-              stdout is not defined, then the output from stdout is available
-              via result.stdout. stderr data is in result.stderr
-
- mergeStderr: Optional boolean value. If true, stderr is merged with stdout;
-              no data will be provided to stderr.
-
-
-Description of object returned by subprocess.call(...)
-------------------------------------------------------
-The object returned by subprocess.call offers a few methods that can be
-executed:
-
- wait(): waits for the subprocess to terminate. It is not required to use
-              wait; done will be called in any case when the subprocess terminated.
-
- kill(): kill the subprocess. Any open pipes will be closed and
-              done will be called.
-
-
-Other methods exported by subprocess
-------------------------------------
-The following functions help debugging and provide logging facilities.
-
- registerDebugHandler(functionRef): register a handler that is called to get
-                                      debugging information
- registerLogHandler(functionRef): register a handler that is called to get error
-                                      messages
-
- example:
-    subprocess.registerLogHandler( function(s) { dump(s); } );
--- a/addon-sdk/source/lib/sdk/system/child_process/subprocess.js
+++ b/addon-sdk/source/lib/sdk/system/child_process/subprocess.js
@@ -1,1687 +1,186 @@
-// -*- coding: utf-8; indent-tabs-mode: nil -*-
-// vim: et:ts=4:sw=4:sts=4:ft=javascript
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public
- * License Version 1.1 (the "MPL"); you may not use this file
- * except in compliance with the MPL. You may obtain a copy of
- * the MPL at http://www.mozilla.org/MPL/
- *
- * Software distributed under the MPL is distributed on an "AS
- * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
- * implied. See the MPL for the specific language governing
- * rights and limitations under the MPL.
- *
- * The Original Code is subprocess.jsm.
- *
- * The Initial Developer of this code is Jan Gerber.
- * Portions created by Jan Gerber <j@mailb.org>
- * are Copyright (C) 2011 Jan Gerber.
- * All Rights Reserved.
- *
- * Contributor(s):
- * Patrick Brunschwig <patrick@enigmail.net>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- * ***** END LICENSE BLOCK ***** */
-
-/*
- * This object allows to start a process, and read/write data to/from it
- * using stdin/stdout/stderr streams.
- * Usage example:
- *
- *  var p = subprocess.call({
- *    command:     '/bin/foo',
- *    arguments:   ['-v', 'foo'],
- *    environment: [ "XYZ=abc", "MYVAR=def" ],
- *    charset: 'UTF-8',
- *    workdir: '/home/foo',
- *    //stdin: "some value to write to stdin\nfoobar",
- *    stdin: function(stdin) {
- *      stdin.write("some value to write to stdin\nfoobar");
- *      stdin.close();
- *    },
- *    stdout: function(data) {
- *      dump("got data on stdout:" + data + "\n");
- *    },
- *    stderr: function(data) {
- *      dump("got data on stderr:" + data + "\n");
- *    },
- *    done: function(result) {
- *      dump("process terminated with " + result.exitCode + "\n");
- *    },
- *    mergeStderr: false
- *  });
- *  p.wait(); // wait for the subprocess to terminate
- *            // this will block the main thread,
- *            // only do if you can wait that long
- *
- *
- * Description of parameters:
- * --------------------------
- * Apart from <command>, all arguments are optional.
- *
- * command:     either a |nsIFile| object pointing to an executable file or a
- *              String containing the platform-dependent path to an executable
- *              file.
- *
- * arguments:   optional string array containing the arguments to the command.
- *
- * environment: optional string array containing environment variables to pass
- *              to the command. The array elements must have the form
- *              "VAR=data". Please note that if environment is defined, it
- *              replaces any existing environment variables for the subprocess.
- *
- * charset:     Output is decoded with given charset and a string is returned.
- *              If charset is undefined, "UTF-8" is used as default.
- *              To get binary data, set this to null and the returned string
- *              is not decoded in any way.
- *
- * workdir:     optional; String containing the platform-dependent path to a
- *              directory to become the current working directory of the subprocess.
- *
- * stdin:       optional input data for the process to be passed on standard
- *              input. stdin can either be a string or a function.
- *              A |string| gets written to stdin and stdin gets closed;
- *              A |function| gets passed an object with write and close function.
- *              Please note that the write() function will return almost immediately;
- *              data is always written asynchronously on a separate thread.
- *
- * stdout:      an optional function that can receive output data from the
- *              process. The stdout-function is called asynchronously; it can be
- *              called mutliple times during the execution of a process.
- *              At a minimum at each occurance of \n or \r.
- *              Please note that null-characters might need to be escaped
- *              with something like 'data.replace(/\0/g, "\\0");'.
- *
- * stderr:      an optional function that can receive stderr data from the
- *              process. The stderr-function is called asynchronously; it can be
- *              called mutliple times during the execution of a process. Please
- *              note that null-characters might need to be escaped with
- *              something like 'data.replace(/\0/g, "\\0");'.
- *              (on windows it only gets called once right now)
- *
- * done:        optional function that is called when the process has terminated.
- *              The exit code from the process available via result.exitCode. If
- *              stdout is not defined, then the output from stdout is available
- *              via result.stdout. stderr data is in result.stderr
- *
- * mergeStderr: optional boolean value. If true, stderr is merged with stdout;
- *              no data will be provided to stderr.
- *
- *
- * Description of object returned by subprocess.call(...)
- * ------------------------------------------------------
- * The object returned by subprocess.call offers a few methods that can be
- * executed:
- *
- * wait():         waits for the subprocess to terminate. It is not required to use
- *                 wait; done will be called in any case when the subprocess terminated.
- *
- * kill(hardKill): kill the subprocess. Any open pipes will be closed and
- *                 done will be called.
- *                 hardKill [ignored on Windows]:
- *                  - false: signal the process terminate (SIGTERM)
- *                  - true:  kill the process (SIGKILL)
- *
- *
- * Other methods in subprocess
- * ---------------------------
- *
- * registerDebugHandler(functionRef):   register a handler that is called to get
- *                                      debugging information
- * registerLogHandler(functionRef):     register a handler that is called to get error
- *                                      messages
- *
- * example:
- *    subprocess.registerLogHandler( function(s) { dump(s); } );
- */
+/* 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/. */
 
 'use strict';
 
-const { Cc, Ci, Cu, ChromeWorker } = require("chrome");
+const { Ci, Cu } = require("chrome");
 
-Cu.import("resource://gre/modules/ctypes.jsm");
-
-const NS_LOCAL_FILE = "@mozilla.org/file/local;1";
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/Subprocess.jsm");
+Cu.import("resource://gre/modules/Task.jsm");
 
 const Runtime = require("sdk/system/runtime");
 const Environment = require("sdk/system/environment").env;
 const DEFAULT_ENVIRONMENT = [];
 if (Runtime.OS == "Linux" && "DISPLAY" in Environment) {
   DEFAULT_ENVIRONMENT.push("DISPLAY=" + Environment.DISPLAY);
 }
 
-/*
-Fake require statements to ensure worker scripts are packaged:
-require("./subprocess_worker_win.js");
-require("./subprocess_worker_unix.js");
-*/
-const URL_PREFIX = module.uri.replace(/subprocess\.js/, "");
-const WORKER_URL_WIN = URL_PREFIX + "subprocess_worker_win.js";
-const WORKER_URL_UNIX = URL_PREFIX + "subprocess_worker_unix.js";
-
-//Windows API definitions
-if (ctypes.size_t.size == 8) {
-    var WinABI = ctypes.default_abi;
-} else {
-    var WinABI = ctypes.winapi_abi;
-}
-const WORD = ctypes.uint16_t;
-const DWORD = ctypes.uint32_t;
-const LPDWORD = DWORD.ptr;
-
-const UINT = ctypes.unsigned_int;
-const BOOL = ctypes.bool;
-const HANDLE = ctypes.size_t;
-const HWND = HANDLE;
-const HMODULE = HANDLE;
-const WPARAM = ctypes.size_t;
-const LPARAM = ctypes.size_t;
-const LRESULT = ctypes.size_t;
-const ULONG_PTR = ctypes.uintptr_t;
-const PVOID = ctypes.voidptr_t;
-const LPVOID = PVOID;
-const LPCTSTR = ctypes.char16_t.ptr;
-const LPCWSTR = ctypes.char16_t.ptr;
-const LPTSTR = ctypes.char16_t.ptr;
-const LPSTR = ctypes.char.ptr;
-const LPCSTR = ctypes.char.ptr;
-const LPBYTE = ctypes.char.ptr;
-
-const CREATE_NEW_CONSOLE = 0x00000010;
-const CREATE_NO_WINDOW = 0x08000000;
-const CREATE_UNICODE_ENVIRONMENT = 0x00000400;
-const STARTF_USESHOWWINDOW = 0x00000001;
-const STARTF_USESTDHANDLES = 0x00000100;
-const SW_HIDE = 0;
-const DUPLICATE_SAME_ACCESS = 0x00000002;
-const STILL_ACTIVE = 259;
-const INFINITE = DWORD(0xFFFFFFFF);
-const WAIT_TIMEOUT = 0x00000102;
-
-/*
-typedef struct _SECURITY_ATTRIBUTES {
- DWORD  nLength;
- LPVOID lpSecurityDescriptor;
- BOOL   bInheritHandle;
-} SECURITY_ATTRIBUTES, *PSECURITY_ATTRIBUTES, *LPSECURITY_ATTRIBUTES;
-*/
-const SECURITY_ATTRIBUTES = new ctypes.StructType("SECURITY_ATTRIBUTES", [
-    {"nLength": DWORD},
-    {"lpSecurityDescriptor": LPVOID},
-    {"bInheritHandle": BOOL},
-]);
+function awaitPromise(promise) {
+  let value;
+  let resolved = null;
+  promise.then(val => {
+    resolved = true;
+    value = val;
+  }, val => {
+    resolved = false;
+    value = val;
+  });
 
-/*
-typedef struct _STARTUPINFO {
-  DWORD  cb;
-  LPTSTR lpReserved;
-  LPTSTR lpDesktop;
-  LPTSTR lpTitle;
-  DWORD  dwX;
-  DWORD  dwY;
-  DWORD  dwXSize;
-  DWORD  dwYSize;
-  DWORD  dwXCountChars;
-  DWORD  dwYCountChars;
-  DWORD  dwFillAttribute;
-  DWORD  dwFlags;
-  WORD   wShowWindow;
-  WORD   cbReserved2;
-  LPBYTE lpReserved2;
-  HANDLE hStdInput;
-  HANDLE hStdOutput;
-  HANDLE hStdError;
-} STARTUPINFO, *LPSTARTUPINFO;
-*/
-const STARTUPINFO = new ctypes.StructType("STARTUPINFO", [
-    {"cb": DWORD},
-    {"lpReserved": LPTSTR},
-    {"lpDesktop": LPTSTR},
-    {"lpTitle": LPTSTR},
-    {"dwX": DWORD},
-    {"dwY": DWORD},
-    {"dwXSize": DWORD},
-    {"dwYSize": DWORD},
-    {"dwXCountChars": DWORD},
-    {"dwYCountChars": DWORD},
-    {"dwFillAttribute": DWORD},
-    {"dwFlags": DWORD},
-    {"wShowWindow": WORD},
-    {"cbReserved2": WORD},
-    {"lpReserved2": LPBYTE},
-    {"hStdInput": HANDLE},
-    {"hStdOutput": HANDLE},
-    {"hStdError": HANDLE},
-]);
+  while (resolved === null)
+    Services.tm.mainThread.processNextEvent(true);
 
-/*
-typedef struct _PROCESS_INFORMATION {
-  HANDLE hProcess;
-  HANDLE hThread;
-  DWORD  dwProcessId;
-  DWORD  dwThreadId;
-} PROCESS_INFORMATION, *LPPROCESS_INFORMATION;
-*/
-const PROCESS_INFORMATION = new ctypes.StructType("PROCESS_INFORMATION", [
-    {"hProcess": HANDLE},
-    {"hThread": HANDLE},
-    {"dwProcessId": DWORD},
-    {"dwThreadId": DWORD},
-]);
-
-/*
-typedef struct _OVERLAPPED {
-  ULONG_PTR Internal;
-  ULONG_PTR InternalHigh;
-  union {
-    struct {
-      DWORD Offset;
-      DWORD OffsetHigh;
-    };
-    PVOID  Pointer;
-  };
-  HANDLE    hEvent;
-} OVERLAPPED, *LPOVERLAPPED;
-*/
-const OVERLAPPED = new ctypes.StructType("OVERLAPPED");
-
-//UNIX definitions
-const pid_t = ctypes.int32_t;
-const WNOHANG = 1;
-const F_GETFD = 1;
-const F_SETFL = 4;
-
-const LIBNAME       = 0;
-const O_NONBLOCK    = 1;
-const RLIM_T        = 2;
-const RLIMIT_NOFILE = 3;
-
-function getPlatformValue(valueType) {
-
-    if (! gXulRuntime)
-        gXulRuntime = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime);
-
-    const platformDefaults = {
-        // Windows API:
-        'winnt':   [ 'kernel32.dll' ],
-
-        // Unix API:
-        //            library name   O_NONBLOCK RLIM_T                RLIMIT_NOFILE
-        'darwin':  [ 'libc.dylib',   0x04     , ctypes.uint64_t     , 8 ],
-        'linux':   [ 'libc.so.6',    2024     , ctypes.unsigned_long, 7 ],
-        'freebsd': [ 'libc.so.7',    0x04     , ctypes.int64_t      , 8 ],
-        'openbsd': [ 'libc.so.61.0', 0x04     , ctypes.int64_t      , 8 ],
-        'sunos':   [ 'libc.so',      0x80     , ctypes.unsigned_long, 5 ]
-    }
-
-    return platformDefaults[gXulRuntime.OS.toLowerCase()][valueType];
+  if (resolved === true)
+    return value;
+  throw value;
 }
 
-
-var gDebugFunc = null,
-    gLogFunc = null,
-    gXulRuntime = null;
+let readAllData = Task.async(function* (pipe, read, callback) {
+  let string;
+  while (string = yield read(pipe))
+    callback(string);
+});
 
-function LogError(s) {
-    if (gLogFunc)
-        gLogFunc(s);
-    else
-        dump(s);
-}
-
-function debugLog(s) {
-    if (gDebugFunc)
-        gDebugFunc(s);
-}
-
-function setTimeout(callback, timeout) {
-    var timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
-    timer.initWithCallback(callback, timeout, Ci.nsITimer.TYPE_ONE_SHOT);
+let write = (pipe, data) => {
+  let buffer = new Uint8Array(Array.from(data, c => c.charCodeAt(0)));
+  return pipe.write(data);
 };
 
-function getBytes(data) {
-  var string = '';
-  data.forEach(function(x) { string += String.fromCharCode(x < 0 ? x + 256 : x) });
-  return string;
-}
-
-function readString(data, length, charset) {
-    var string = '', bytes = [];
-    for(var i = 0;i < length; i++) {
-        if(data[i] == 0 && charset !== null) // stop on NULL character for non-binary data
-           break
-        bytes.push(data[i]);
-    }
-    if (!bytes || bytes.length == 0)
-        return string;
-    if(charset === null) {
-        return bytes;
-    }
-    return convertBytes(bytes, charset);
-}
+var subprocess = {
+  call: function(options) {
+    var result;
 
-function convertBytes(bytes, charset) {
-    var string = '';
-    charset = charset || 'UTF-8';
-    var unicodeConv = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
-                        .getService(Ci.nsIScriptableUnicodeConverter);
-    try {
-        unicodeConv.charset = charset;
-        string = unicodeConv.convertFromByteArray(bytes, bytes.length);
-    } catch (ex) {
-        LogError("String conversion failed: "+ex.toString()+"\n")
-        string = '';
-    }
-    string += unicodeConv.Finish();
-    return string;
-}
-
-
-// temporary solution for removal of nsILocalFile
-function getLocalFileApi() {
-  if ("nsILocalFile" in Ci) {
-    return Ci.nsILocalFile;
-  }
-  else
-    return Ci.nsIFile;
-}
+    let procPromise = Task.spawn(function*() {
+      let opts = {};
 
-function getCommandStr(command) {
-    let commandStr = null;
-    if (typeof(command) == "string") {
-        let file = Cc[NS_LOCAL_FILE].createInstance(getLocalFileApi());
-        file.initWithPath(command);
-        if (! (file.isExecutable() && file.isFile()))
-            throw("File '"+command+"' is not an executable file");
-        commandStr = command;
-    }
-    else {
-        if (! (command.isExecutable() && command.isFile()))
-            throw("File '"+command.path+"' is not an executable file");
-        commandStr = command.path;
-    }
-
-    return commandStr;
-}
+      if (options.mergeStderr) {
+        opts.stderr = "stdout"
+      } else if (options.stderr) {
+        opts.stderr = "pipe";
+      }
 
-function getWorkDir(workdir) {
-    let workdirStr = null;
-    if (typeof(workdir) == "string") {
-        let file = Cc[NS_LOCAL_FILE].createInstance(getLocalFileApi());
-        file.initWithPath(workdir);
-        if (! (file.isDirectory()))
-            throw("Directory '"+workdir+"' does not exist");
-        workdirStr = workdir;
-    }
-    else if (workdir) {
-        if (! workdir.isDirectory())
-            throw("Directory '"+workdir.path+"' does not exist");
-        workdirStr = workdir.path;
-    }
-    return workdirStr;
-}
-
+      if (options.command instanceof Ci.nsIFile) {
+        opts.command = options.command.path;
+      } else {
+        opts.command = yield Subprocess.pathSearch(options.command);
+      }
 
-var subprocess = {
-    call: function(options) {
-        options.mergeStderr = options.mergeStderr || false;
-        options.workdir = options.workdir ||  null;
-        options.environment = options.environment || DEFAULT_ENVIRONMENT;
-        if (options.arguments) {
-            var args = options.arguments;
-            options.arguments = [];
-            args.forEach(function(argument) {
-                options.arguments.push(argument);
-            });
-        } else {
-            options.arguments = [];
-        }
+      if (options.workdir) {
+        opts.workdir = options.workdir;
+      }
 
-        options.libc = getPlatformValue(LIBNAME);
-
-        if (gXulRuntime.OS.substring(0, 3) == "WIN") {
-            return subprocess_win32(options);
-        } else {
-            return subprocess_unix(options);
-        }
-
-    },
-    registerDebugHandler: function(func) {
-        gDebugFunc = func;
-    },
-    registerLogHandler: function(func) {
-        gLogFunc = func;
-    }
-};
-
+      opts.arguments = options.arguments || [];
 
 
-function subprocess_win32(options) {
-    var kernel32dll = ctypes.open(options.libc),
-        hChildProcess,
-        active = true,
-        done = false,
-        exitCode = -1,
-        child = {},
-        stdinWorker = null,
-        stdoutWorker = null,
-        stderrWorker = null,
-        pendingWriteCount = 0,
-        readers = options.mergeStderr ? 1 : 2,
-        stdinOpenState = 2,
-        error = '',
-        output = '';
-
-    // stdin pipe states
-    const OPEN = 2;
-    const CLOSEABLE = 1;
-    const CLOSED = 0;
-
-    //api declarations
-    /*
-    BOOL WINAPI CloseHandle(
-      __in  HANDLE hObject
-    );
-    */
-    var CloseHandle = kernel32dll.declare("CloseHandle",
-                                            WinABI,
-                                            BOOL,
-                                            HANDLE
-    );
-
-    /*
-    BOOL WINAPI CreateProcess(
-      __in_opt     LPCTSTR lpApplicationName,
-      __inout_opt  LPTSTR lpCommandLine,
-      __in_opt     LPSECURITY_ATTRIBUTES lpProcessAttributes,
-      __in_opt     LPSECURITY_ATTRIBUTES lpThreadAttributes,
-      __in         BOOL bInheritHandles,
-      __in         DWORD dwCreationFlags,
-      __in_opt     LPVOID lpEnvironment,
-      __in_opt     LPCTSTR lpCurrentDirectory,
-      __in         LPSTARTUPINFO lpStartupInfo,
-      __out        LPPROCESS_INFORMATION lpProcessInformation
-    );
-     */
-    var CreateProcessW = kernel32dll.declare("CreateProcessW",
-                                            WinABI,
-                                            BOOL,
-                                            LPCTSTR,
-                                            LPTSTR,
-                                            SECURITY_ATTRIBUTES.ptr,
-                                            SECURITY_ATTRIBUTES.ptr,
-                                            BOOL,
-                                            DWORD,
-                                            LPVOID,
-                                            LPCTSTR,
-                                            STARTUPINFO.ptr,
-                                            PROCESS_INFORMATION.ptr
-                                         );
+      // Set up environment
 
-//     /*
-//     BOOL WINAPI ReadFile(
-//       __in         HANDLE hFile,
-//       __out        LPVOID ReadFileBuffer,
-//       __in         DWORD nNumberOfBytesToRead,
-//       __out_opt    LPDWORD lpNumberOfBytesRead,
-//       __inout_opt  LPOVERLAPPED lpOverlapped
-//     );
-//     */
-//     var ReadFileBufferSize = 1024,
-//         ReadFileBuffer = ctypes.char.array(ReadFileBufferSize),
-//         ReadFile = kernel32dll.declare("ReadFile",
-//                                         WinABI,
-//                                         BOOL,
-//                                         HANDLE,
-//                                         ReadFileBuffer,
-//                                         DWORD,
-//                                         LPDWORD,
-//                                         OVERLAPPED.ptr
-//     );
-//
-//     /*
-//     BOOL WINAPI PeekNamedPipe(
-//       __in       HANDLE hNamedPipe,
-//       __out_opt  LPVOID lpBuffer,
-//       __in       DWORD nBufferSize,
-//       __out_opt  LPDWORD lpBytesRead,
-//       __out_opt  LPDWORD lpTotalBytesAvail,
-//       __out_opt  LPDWORD lpBytesLeftThisMessage
-//     );
-//     */
-//     var PeekNamedPipe = kernel32dll.declare("PeekNamedPipe",
-//                                         WinABI,
-//                                         BOOL,
-//                                         HANDLE,
-//                                         ReadFileBuffer,
-//                                         DWORD,
-//                                         LPDWORD,
-//                                         LPDWORD,
-//                                         LPDWORD
-//     );
-//
-//     /*
-//     BOOL WINAPI WriteFile(
-//       __in         HANDLE hFile,
-//       __in         LPCVOID lpBuffer,
-//       __in         DWORD nNumberOfBytesToWrite,
-//       __out_opt    LPDWORD lpNumberOfBytesWritten,
-//       __inout_opt  LPOVERLAPPED lpOverlapped
-//     );
-//     */
-//     var WriteFile = kernel32dll.declare("WriteFile",
-//                                         WinABI,
-//                                         BOOL,
-//                                         HANDLE,
-//                                         ctypes.char.ptr,
-//                                         DWORD,
-//                                         LPDWORD,
-//                                         OVERLAPPED.ptr
-//     );
+      let envVars = options.environment || DEFAULT_ENVIRONMENT;
+      if (envVars.length) {
+        let environment = {};
+        for (let val of envVars) {
+          let idx = val.indexOf("=");
+          if (idx >= 0)
+            environment[val.slice(0, idx)] = val.slice(idx + 1);
+        }
 
-    /*
-    BOOL WINAPI CreatePipe(
-      __out     PHANDLE hReadPipe,
-      __out     PHANDLE hWritePipe,
-      __in_opt  LPSECURITY_ATTRIBUTES lpPipeAttributes,
-      __in      DWORD nSize
-    );
-    */
-    var CreatePipe = kernel32dll.declare("CreatePipe",
-                                        WinABI,
-                                        BOOL,
-                                        HANDLE.ptr,
-                                        HANDLE.ptr,
-                                        SECURITY_ATTRIBUTES.ptr,
-                                        DWORD
-    );
-
-    /*
-    HANDLE WINAPI GetCurrentProcess(void);
-    */
-    var GetCurrentProcess = kernel32dll.declare("GetCurrentProcess",
-                                        WinABI,
-                                        HANDLE
-    );
-
-    /*
-    DWORD WINAPI GetLastError(void);
-    */
-    var GetLastError = kernel32dll.declare("GetLastError",
-                                        WinABI,
-                                        DWORD
-    );
-
-    /*
-    BOOL WINAPI DuplicateHandle(
-      __in   HANDLE hSourceProcessHandle,
-      __in   HANDLE hSourceHandle,
-      __in   HANDLE hTargetProcessHandle,
-      __out  LPHANDLE lpTargetHandle,
-      __in   DWORD dwDesiredAccess,
-      __in   BOOL bInheritHandle,
-      __in   DWORD dwOptions
-    );
-    */
-    var DuplicateHandle = kernel32dll.declare("DuplicateHandle",
-                                        WinABI,
-                                        BOOL,
-                                        HANDLE,
-                                        HANDLE,
-                                        HANDLE,
-                                        HANDLE.ptr,
-                                        DWORD,
-                                        BOOL,
-                                        DWORD
-    );
+        opts.environment = environment;
+      }
 
 
-    /*
-    BOOL WINAPI GetExitCodeProcess(
-      __in   HANDLE hProcess,
-      __out  LPDWORD lpExitCode
-    );
-    */
-    var GetExitCodeProcess = kernel32dll.declare("GetExitCodeProcess",
-                                        WinABI,
-                                        BOOL,
-                                        HANDLE,
-                                        LPDWORD
-    );
-
-    /*
-    DWORD WINAPI WaitForSingleObject(
-      __in  HANDLE hHandle,
-      __in  DWORD dwMilliseconds
-    );
-    */
-    var WaitForSingleObject = kernel32dll.declare("WaitForSingleObject",
-                                        WinABI,
-                                        DWORD,
-                                        HANDLE,
-                                        DWORD
-    );
-
-    /*
-    BOOL WINAPI TerminateProcess(
-      __in  HANDLE hProcess,
-      __in  UINT uExitCode
-    );
-    */
-    var TerminateProcess = kernel32dll.declare("TerminateProcess",
-                                        WinABI,
-                                        BOOL,
-                                        HANDLE,
-                                        UINT
-    );
-
-    //functions
-    function popen(command, workdir, args, environment, child) {
-        //escape arguments
-        args.unshift(command);
-        for (var i = 0; i < args.length; i++) {
-          if (typeof args[i] != "string") { args[i] = args[i].toString(); }
-          /* quote arguments with spaces */
-          if (args[i].match(/\s/)) {
-            args[i] = "\"" + args[i] + "\"";
-          }
-          /* If backslash is followed by a quote, double it */
-          args[i] = args[i].replace(/\\\"/g, "\\\\\"");
-        }
-        command = args.join(' ');
-
-        environment = environment || [];
-        if(environment.length) {
-            //An environment block consists of
-            //a null-terminated block of null-terminated strings.
-            //Using CREATE_UNICODE_ENVIRONMENT so needs to be char16_t
-            environment = ctypes.char16_t.array()(environment.join('\0') + '\0');
-        } else {
-            environment = null;
-        }
-
-        var hOutputReadTmp = new HANDLE(),
-            hOutputRead = new HANDLE(),
-            hOutputWrite = new HANDLE();
-
-        var hErrorRead = new HANDLE(),
-            hErrorReadTmp = new HANDLE(),
-            hErrorWrite = new HANDLE();
-
-        var hInputRead = new HANDLE(),
-            hInputWriteTmp = new HANDLE(),
-            hInputWrite = new HANDLE();
-
-        // Set up the security attributes struct.
-        var sa = new SECURITY_ATTRIBUTES();
-        sa.nLength = SECURITY_ATTRIBUTES.size;
-        sa.lpSecurityDescriptor = null;
-        sa.bInheritHandle = true;
-
-        // Create output pipe.
-
-        if(!CreatePipe(hOutputReadTmp.address(), hOutputWrite.address(), sa.address(), 0))
-            LogError('CreatePipe hOutputReadTmp failed');
-
-        if(options.mergeStderr) {
-          // Create a duplicate of the output write handle for the std error
-          // write handle. This is necessary in case the child application
-          // closes one of its std output handles.
-          if (!DuplicateHandle(GetCurrentProcess(), hOutputWrite,
-                               GetCurrentProcess(), hErrorWrite.address(), 0,
-                               true, DUPLICATE_SAME_ACCESS))
-             LogError("DuplicateHandle hOutputWrite failed");
-        } else {
-            // Create error pipe.
-            if(!CreatePipe(hErrorReadTmp.address(), hErrorWrite.address(), sa.address(), 0))
-                LogError('CreatePipe hErrorReadTmp failed');
-        }
-
-        // Create input pipe.
-        if (!CreatePipe(hInputRead.address(),hInputWriteTmp.address(),sa.address(), 0))
-            LogError("CreatePipe hInputRead failed");
-
-        // Create new output/error read handle and the input write handles. Set
-        // the Properties to FALSE. Otherwise, the child inherits the
-        // properties and, as a result, non-closeable handles to the pipes
-        // are created.
-        if (!DuplicateHandle(GetCurrentProcess(), hOutputReadTmp,
-                             GetCurrentProcess(),
-                             hOutputRead.address(), // Address of new handle.
-                             0, false, // Make it uninheritable.
-                             DUPLICATE_SAME_ACCESS))
-             LogError("DupliateHandle hOutputReadTmp failed");
+      let proc = yield Subprocess.call(opts);
 
-        if(!options.mergeStderr) {
-            if (!DuplicateHandle(GetCurrentProcess(), hErrorReadTmp,
-                             GetCurrentProcess(),
-                             hErrorRead.address(), // Address of new handle.
-                             0, false, // Make it uninheritable.
-                             DUPLICATE_SAME_ACCESS))
-             LogError("DupliateHandle hErrorReadTmp failed");
-        }
-        if (!DuplicateHandle(GetCurrentProcess(), hInputWriteTmp,
-                             GetCurrentProcess(),
-                             hInputWrite.address(), // Address of new handle.
-                             0, false, // Make it uninheritable.
-                             DUPLICATE_SAME_ACCESS))
-          LogError("DupliateHandle hInputWriteTmp failed");
-
-        // Close inheritable copies of the handles.
-        if (!CloseHandle(hOutputReadTmp)) LogError("CloseHandle hOutputReadTmp failed");
-        if(!options.mergeStderr)
-            if (!CloseHandle(hErrorReadTmp)) LogError("CloseHandle hErrorReadTmp failed");
-        if (!CloseHandle(hInputWriteTmp)) LogError("CloseHandle failed");
-
-        var pi = new PROCESS_INFORMATION();
-        var si = new STARTUPINFO();
-
-        si.cb = STARTUPINFO.size;
-        si.dwFlags = STARTF_USESTDHANDLES;
-        si.hStdInput  = hInputRead;
-        si.hStdOutput = hOutputWrite;
-        si.hStdError  = hErrorWrite;
-
-        // Launch the process
-        if(!CreateProcessW(null,            // executable name
-                           command,         // command buffer
-                           null,            // process security attribute
-                           null,            // thread security attribute
-                           true,            // inherits system handles
-                           CREATE_UNICODE_ENVIRONMENT|CREATE_NO_WINDOW, // process flags
-                           environment,     // envrionment block
-                           workdir,          // set as current directory
-                           si.address(),    // (in) startup information
-                           pi.address()     // (out) process information
-        ))
-            throw("Fatal - Could not launch subprocess '"+command+"'");
-
-        // Close any unnecessary handles.
-        if (!CloseHandle(pi.hThread))
-            LogError("CloseHandle pi.hThread failed");
-
-        // Close pipe handles (do not continue to modify the parent).
-        // You need to make sure that no handles to the write end of the
-        // output pipe are maintained in this process or else the pipe will
-        // not close when the child process exits and the ReadFile will hang.
-        if (!CloseHandle(hInputRead)) LogError("CloseHandle hInputRead failed");
-        if (!CloseHandle(hOutputWrite)) LogError("CloseHandle hOutputWrite failed");
-        if (!CloseHandle(hErrorWrite)) LogError("CloseHandle hErrorWrite failed");
-
-        //return values
-        child.stdin = hInputWrite;
-        child.stdout = hOutputRead;
-        child.stderr = options.mergeStderr ? undefined : hErrorRead;
-        child.process = pi.hProcess;
-        return pi.hProcess;
-    }
-
-    /*
-     * createStdinWriter ()
-     *
-     * Create a ChromeWorker object for writing data to the subprocess' stdin
-     * pipe. The ChromeWorker object lives on a separate thread; this avoids
-     * internal deadlocks.
-     */
-    function createStdinWriter() {
-        debugLog("Creating new stdin worker\n");
-        stdinWorker = new ChromeWorker(WORKER_URL_WIN);
-        stdinWorker.onmessage = function(event) {
-            switch(event.data) {
-            case "WriteOK":
-                pendingWriteCount--;
-                debugLog("got OK from stdinWorker - remaining count: "+pendingWriteCount+"\n");
-                break;
-            case "ClosedOK":
-                stdinOpenState = CLOSED;
-                debugLog("Stdin pipe closed\n");
-                break;
-            default:
-                debugLog("got msg from stdinWorker: "+event.data+"\n");
-            }
-        }
-        stdinWorker.onerror = function(error) {
-            pendingWriteCount--;
-            LogError("got error from stdinWorker: "+error.message+"\n");
-        }
-
-        stdinWorker.postMessage({msg: "init", libc: options.libc});
-    }
-
-    /*
-     * writeStdin()
-     * @data: String containing the data to write
-     *
-     * Write data to the subprocess' stdin (equals to sending a request to the
-     * ChromeWorker object to write the data).
-     */
-    function writeStdin(data) {
-        ++pendingWriteCount;
-        debugLog("sending "+data.length+" bytes to stdinWorker\n");
-        var pipePtr = parseInt(ctypes.cast(child.stdin.address(), ctypes.uintptr_t).value);
-
-        stdinWorker.postMessage({
-                msg: 'write',
-                pipe: pipePtr,
-                data: data
-            });
-    }
-
-    /*
-     * closeStdinHandle()
-     *
-     * Close the stdin pipe, either directly or by requesting the ChromeWorker to
-     * close the pipe. The ChromeWorker will only close the pipe after the last write
-     * request process is done.
-     */
-
-    function closeStdinHandle() {
-        debugLog("trying to close stdin\n");
-        if (stdinOpenState != OPEN) return;
-        stdinOpenState = CLOSEABLE;
-
-        if (stdinWorker) {
-            debugLog("sending close stdin to worker\n");
-            var pipePtr = parseInt(ctypes.cast(child.stdin.address(), ctypes.uintptr_t).value);
-            stdinWorker.postMessage({
-                msg: 'close',
-                pipe: pipePtr
-            });
-        }
-        else {
-            stdinOpenState = CLOSED;
-            debugLog("Closing Stdin\n");
-            CloseHandle(child.stdin) || LogError("CloseHandle hInputWrite failed");
-        }
-    }
+      Object.defineProperty(result, "pid", {
+        value: proc.pid,
+        enumerable: true,
+        configurable: true,
+      });
 
 
-    /*
-     * createReader(pipe, name)
-     *
-     * @pipe: handle to the pipe
-     * @name: String containing the pipe name (stdout or stderr)
-     *
-     * Create a ChromeWorker object for reading data asynchronously from
-     * the pipe (i.e. on a separate thread), and passing the result back to
-     * the caller.
-     */
-    function createReader(pipe, name, callbackFunc) {
-        var worker = new ChromeWorker(WORKER_URL_WIN);
-        worker.onmessage = function(event) {
-            switch(event.data.msg) {
-            case "data":
-                debugLog("got "+event.data.count+" bytes from "+name+"\n");
-                var data = '';
-                if (options.charset === null) {
-                    data = getBytes(event.data.data);
-                }
-                else {
-                    try {
-                        data = convertBytes(event.data.data, options.charset);
-                    }
-                    catch(ex) {
-                        console.warn("error decoding output: " + ex);
-                        data = getBytes(event.data.data);
-                    }
-                }
+      let promises = [];
 
-                callbackFunc(data);
-                break;
-            case "done":
-                debugLog("Pipe "+name+" closed\n");
-                --readers;
-                if (readers == 0) cleanup();
-                break;
-            default:
-                debugLog("Got msg from "+name+": "+event.data.data+"\n");
-            }
-        }
+      // Set up IO handlers.
 
-        worker.onerror = function(errorMsg) {
-            LogError("Got error from "+name+": "+errorMsg.message);
-        }
-
-        var pipePtr = parseInt(ctypes.cast(pipe.address(), ctypes.uintptr_t).value);
-
-        worker.postMessage({
-                msg: 'read',
-                pipe: pipePtr,
-                libc: options.libc,
-                charset: options.charset === null ? "null" : options.charset,
-                name: name
-            });
-
-        return worker;
-    }
-
-    /*
-     * readPipes()
-     *
-     * Open the pipes for reading from stdout and stderr
-     */
-    function readPipes() {
-        stdoutWorker = createReader(child.stdout, "stdout", function (data) {
-            if(options.stdout) {
-                setTimeout(function() {
-                    options.stdout(data);
-                }, 0);
-            } else {
-                output += data;
-            }
-        });
-
-
-        if (!options.mergeStderr) stderrWorker = createReader(child.stderr, "stderr", function (data) {
-            if(options.stderr) {
-                setTimeout(function() {
-                    options.stderr(data);
-                }, 0);
-            } else {
-                error += data;
-            }
-        });
-    }
+      let read = pipe => pipe.readString();
+      if (options.charset === null) {
+        read = pipe => {
+          return pipe.read().then(buffer => {
+            return String.fromCharCode(...buffer);
+          });
+        };
+      }
 
-    /*
-     * cleanup()
-     *
-     * close stdin if needed, get the exit code from the subprocess and invoke
-     * the caller's done() function.
-     *
-     * Note: because stdout() and stderr() are called using setTimeout, we need to
-     * do the same here in order to guarantee the message sequence.
-     */
-    function cleanup() {
-        debugLog("Cleanup called\n");
-        if(active) {
-            active = false;
-
-            closeStdinHandle(); // should only be required in case of errors
-
-            var exit = new DWORD();
-            GetExitCodeProcess(child.process, exit.address());
-            exitCode = exit.value;
-
-            if (stdinWorker)
-                stdinWorker.postMessage({msg: 'stop'})
+      if (options.stdout)
+        promises.push(readAllData(proc.stdout, read, options.stdout));
 
-            setTimeout(function _done() {
-                if (options.done) {
-                    try {
-                        options.done({
-                            exitCode: exitCode,
-                            stdout: output,
-                            stderr: error,
-                        });
-                    }
-                    catch (ex) {
-                        // prevent from blocking if options.done() throws an error
-                        done = true;
-                        throw ex;
-                    }
-                }
-                done = true;
-            }, 0);
-            kernel32dll.close();
-        }
-    }
-
-    var cmdStr = getCommandStr(options.command);
-    var workDir = getWorkDir(options.workdir);
+      if (options.stderr && proc.stderr)
+        promises.push(readAllData(proc.stderr, read, options.stderr));
 
-    //main
-    hChildProcess = popen(cmdStr, workDir, options.arguments, options.environment, child);
-
-    readPipes();
-
-    if (options.stdin) {
-       createStdinWriter();
+      // Process stdin
 
-        if(typeof(options.stdin) == 'function') {
-            try {
-                options.stdin({
-                    write: function(data) {
-                        writeStdin(data);
-                    },
-                    close: function() {
-                        closeStdinHandle();
-                    }
-                });
-            }
-            catch (ex) {
-                // prevent from failing if options.stdin() throws an exception
-                closeStdinHandle();
-                throw ex;
-            }
-        } else {
-            writeStdin(options.stdin);
-            closeStdinHandle();
-        }
-    }
-    else
-        closeStdinHandle();
-
-    return {
-        kill: function(hardKill) {
-            // hardKill is currently ignored on Windows
-            var r = !!TerminateProcess(child.process, 255);
-            cleanup(-1);
-            return r;
-        },
-        wait: function() {
-            // wait for async operations to complete
-            var thread = Cc['@mozilla.org/thread-manager;1'].getService(Ci.nsIThreadManager).currentThread;
-            while (!done) thread.processNextEvent(true);
-
-            return exitCode;
-        }
-    }
-}
+      if (typeof options.stdin === "string") {
+        write(proc.stdin, options.stdin);
+        proc.stdin.close();
+      }
 
 
-function subprocess_unix(options) {
-    // stdin pipe states
-    const OPEN = 2;
-    const CLOSEABLE = 1;
-    const CLOSED = 0;
-
-    var libc = ctypes.open(options.libc),
-        active = true,
-        done = false,
-        exitCode = -1,
-        workerExitCode = 0,
-        child = {},
-        pid = -1,
-        stdinWorker = null,
-        stdoutWorker = null,
-        stderrWorker = null,
-        pendingWriteCount = 0,
-        readers = options.mergeStderr ? 1 : 2,
-        stdinOpenState = OPEN,
-        error = '',
-        output = '';
-
-    //api declarations
+      // Handle process completion
 
-    //pid_t fork(void);
-    var fork = libc.declare("fork",
-                         ctypes.default_abi,
-                         pid_t
-    );
-
-    //NULL terminated array of strings, argv[0] will be command >> + 2
-    var argv = ctypes.char.ptr.array(options.arguments.length + 2);
-    var envp = ctypes.char.ptr.array(options.environment.length + 1);
-
-    // posix_spawn_file_actions_t is a complex struct that may be different on
-    // each platform. We do not care about its attributes, we don't need to
-    // get access to them, but we do need to allocate the right amount
-    // of memory for it.
-    // At 2013/10/28, its size was 80 on linux, but better be safe (and larger),
-    // than crash when posix_spawn_file_actions_init fill `action` with zeros.
-    // Use `gcc sizeof_fileaction.c && ./a.out` to check that size.
-    var posix_spawn_file_actions_t = ctypes.uint8_t.array(100);
-
-    //int posix_spawn(pid_t *restrict pid, const char *restrict path,
-    //   const posix_spawn_file_actions_t *file_actions,
-    //   const posix_spawnattr_t *restrict attrp,
-    //   char *const argv[restrict], char *const envp[restrict]);
-    var posix_spawn = libc.declare("posix_spawn",
-                         ctypes.default_abi,
-                         ctypes.int,
-                         pid_t.ptr,
-                         ctypes.char.ptr,
-                         posix_spawn_file_actions_t.ptr,
-                         ctypes.voidptr_t,
-                         argv,
-                         envp
-    );
+      if (options.done)
+        Promise.all(promises)
+          .then(() => proc.wait())
+          .then(options.done);
 
-    //int posix_spawn_file_actions_init(posix_spawn_file_actions_t *file_actions);
-    var posix_spawn_file_actions_init = libc.declare("posix_spawn_file_actions_init",
-                         ctypes.default_abi,
-                         ctypes.int,
-                         posix_spawn_file_actions_t.ptr
-    );
-
-    //int posix_spawn_file_actions_destroy(posix_spawn_file_actions_t *file_actions);
-    var posix_spawn_file_actions_destroy = libc.declare("posix_spawn_file_actions_destroy",
-                         ctypes.default_abi,
-                         ctypes.int,
-                         posix_spawn_file_actions_t.ptr
-    );
-
-    // int posix_spawn_file_actions_adddup2(posix_spawn_file_actions_t *
-    //                                      file_actions, int fildes, int newfildes);
-    var posix_spawn_file_actions_adddup2 = libc.declare("posix_spawn_file_actions_adddup2",
-                         ctypes.default_abi,
-                         ctypes.int,
-                         posix_spawn_file_actions_t.ptr,
-                         ctypes.int,
-                         ctypes.int
-    );
+      return proc;
+    });
 
-    // int posix_spawn_file_actions_addclose(posix_spawn_file_actions_t *
-    //                                       file_actions, int fildes);
-    var posix_spawn_file_actions_addclose = libc.declare("posix_spawn_file_actions_addclose",
-                         ctypes.default_abi,
-                         ctypes.int,
-                         posix_spawn_file_actions_t.ptr,
-                         ctypes.int
-    );
-
-    //int pipe(int pipefd[2]);
-    var pipefd = ctypes.int.array(2);
-    var pipe = libc.declare("pipe",
-                         ctypes.default_abi,
-                         ctypes.int,
-                         pipefd
-    );
-
-    //int close(int fd);
-    var close = libc.declare("close",
-                          ctypes.default_abi,
-                          ctypes.int,
-                          ctypes.int
-    );
-
-    //pid_t waitpid(pid_t pid, int *status, int options);
-    var waitpid = libc.declare("waitpid",
-                          ctypes.default_abi,
-                          pid_t,
-                          pid_t,
-                          ctypes.int.ptr,
-                          ctypes.int
-    );
-
-    //int kill(pid_t pid, int sig);
-    var kill = libc.declare("kill",
-                          ctypes.default_abi,
-                          ctypes.int,
-                          pid_t,
-                          ctypes.int
-    );
+    procPromise.catch(e => {
+      if (options.done)
+        options.done({exitCode: -1}, e);
+      else
+        Cu.reportError(e instanceof Error ? e : e.message || e);
+    });
 
-    //int read(int fd, void *buf, size_t count);
-    var bufferSize = 1024;
-    var buffer = ctypes.char.array(bufferSize);
-    var read = libc.declare("read",
-                          ctypes.default_abi,
-                          ctypes.int,
-                          ctypes.int,
-                          buffer,
-                          ctypes.int
-    );
-
-    //ssize_t write(int fd, const void *buf, size_t count);
-    var write = libc.declare("write",
-                          ctypes.default_abi,
-                          ctypes.int,
-                          ctypes.int,
-                          ctypes.char.ptr,
-                          ctypes.int
-    );
-
-    //int chdir(const char *path);
-    var chdir = libc.declare("chdir",
-                          ctypes.default_abi,
-                          ctypes.int,
-                          ctypes.char.ptr
-    );
-
-    //int fcntl(int fd, int cmd, ... /* arg */ );
-    var fcntl = libc.declare("fcntl",
-                          ctypes.default_abi,
-                          ctypes.int,
-                          ctypes.int,
-                          ctypes.int,
-                          ctypes.int
-    );
-
-    function popen(command, workdir, args, environment, child) {
-        var _in,
-            _out,
-            _err,
-            pid,
-            rc;
-        _in = new pipefd();
-        _out = new pipefd();
-        if(!options.mergeStderr)
-            _err = new pipefd();
-
-        var _args = argv();
-        args.unshift(command);
-        for(var i=0;i<args.length;i++) {
-            _args[i] = ctypes.char.array()(args[i]);
-        }
-        var _envp = envp();
-        for(var i=0;i<environment.length;i++) {
-            _envp[i] = ctypes.char.array()(environment[i]);
-            // LogError(_envp);
-        }
+    if (typeof options.stdin === "function") {
+      // Unfortunately, some callers (child_process.js) depend on this
+      // being called synchronously.
+      options.stdin({
+        write(val) {
+          procPromise.then(proc => {
+            write(proc.stdin, val);
+          });
+        },
 
-        rc = pipe(_in);
-        if (rc < 0) {
-            return -1;
-        }
-        rc = pipe(_out);
-        fcntl(_out[0], F_SETFL, getPlatformValue(O_NONBLOCK));
-        if (rc < 0) {
-            close(_in[0]);
-            close(_in[1]);
-            return -1
-        }
-        if(!options.mergeStderr) {
-            rc = pipe(_err);
-            fcntl(_err[0], F_SETFL, getPlatformValue(O_NONBLOCK));
-            if (rc < 0) {
-                close(_in[0]);
-                close(_in[1]);
-                close(_out[0]);
-                close(_out[1]);
-                return -1
-            }
-        }
-
-        let STDIN_FILENO = 0;
-        let STDOUT_FILENO = 1;
-        let STDERR_FILENO = 2;
-
-        let action = posix_spawn_file_actions_t();
-        posix_spawn_file_actions_init(action.address());
-
-        posix_spawn_file_actions_adddup2(action.address(), _in[0], STDIN_FILENO);
-        posix_spawn_file_actions_addclose(action.address(), _in[1]);
-        posix_spawn_file_actions_addclose(action.address(), _in[0]);
-
-        posix_spawn_file_actions_adddup2(action.address(), _out[1], STDOUT_FILENO);
-        posix_spawn_file_actions_addclose(action.address(), _out[1]);
-        posix_spawn_file_actions_addclose(action.address(), _out[0]);
-
-        if (!options.mergeStderr) {
-          posix_spawn_file_actions_adddup2(action.address(), _err[1], STDERR_FILENO);
-          posix_spawn_file_actions_addclose(action.address(), _err[1]);
-          posix_spawn_file_actions_addclose(action.address(), _err[0]);
-        }
-
-        // posix_spawn doesn't support setting a custom workdir for the child,
-        // so change the cwd in the parent process before launching the child process.
-        if (workdir) {
-          if (chdir(workdir) < 0) {
-            throw new Error("Unable to change workdir before launching child process");
-          }
-        }
-
-        closeOtherFds(action, _in[1], _out[0], options.mergeStderr ? undefined : _err[0]);
-
-        let id = pid_t(0);
-        let rv = posix_spawn(id.address(), command, action.address(), null, _args, _envp);
-        posix_spawn_file_actions_destroy(action.address());
-        if (rv != 0) {
-          // we should not really end up here
-          if(!options.mergeStderr) {
-            close(_err[0]);
-            close(_err[1]);
-          }
-          close(_out[0]);
-          close(_out[1]);
-          close(_in[0]);
-          close(_in[1]);
-          throw new Error("Fatal - failed to create subprocess '"+command+"'");
-        }
-        pid = id.value;
-
-        close(_in[0]);
-        close(_out[1]);
-        if (!options.mergeStderr)
-          close(_err[1]);
-        child.stdin  = _in[1];
-        child.stdout = _out[0];
-        child.stderr = options.mergeStderr ? undefined : _err[0];
-        child.pid = pid;
-
-        return pid;
+        close() {
+          procPromise.then(proc => {
+            proc.stdin.close();
+          });
+        },
+      });
     }
 
-
-    // close any file descriptors that are not required for the process
-    function closeOtherFds(action, fdIn, fdOut, fdErr) {
-        // Unfortunately on mac, any fd registered in posix_spawn_file_actions_addclose
-        // that can't be closed correctly will make posix_spawn fail...
-        // Even if we ensure registering only still opened fds.
-        if (gXulRuntime.OS == "Darwin")
-            return;
-
-        var maxFD = 256; // arbitrary max
-
-
-        var rlim_t = getPlatformValue(RLIM_T);
-
-        const RLIMITS = new ctypes.StructType("RLIMITS", [
-            {"rlim_cur": rlim_t},
-            {"rlim_max": rlim_t}
-        ]);
-
-        try {
-            var getrlimit = libc.declare("getrlimit",
-                                  ctypes.default_abi,
-                                  ctypes.int,
-                                  ctypes.int,
-                                  RLIMITS.ptr
-            );
-
-            var rl = new RLIMITS();
-            if (getrlimit(getPlatformValue(RLIMIT_NOFILE), rl.address()) == 0) {
-                maxFD = rl.rlim_cur;
-            }
-            debugLog("getlimit: maxFD="+maxFD+"\n");
-
-        }
-        catch(ex) {
-            debugLog("getrlimit: no such function on this OS\n");
-            debugLog(ex.toString());
-        }
-
-        // close any file descriptors
-        // fd's 0-2 are already closed
-        for (var i = 3; i < maxFD; i++) {
-            if (i != fdIn && i != fdOut && i != fdErr && fcntl(i, F_GETFD, -1) >= 0) {
-                posix_spawn_file_actions_addclose(action.address(), i);
-            }
-        }
-    }
-
-    /*
-     * createStdinWriter ()
-     *
-     * Create a ChromeWorker object for writing data to the subprocess' stdin
-     * pipe. The ChromeWorker object lives on a separate thread; this avoids
-     * internal deadlocks.
-     */
-    function createStdinWriter() {
-        debugLog("Creating new stdin worker\n");
-        stdinWorker = new ChromeWorker(WORKER_URL_UNIX);
-        stdinWorker.onmessage = function(event) {
-            switch (event.data.msg) {
-            case "info":
-                switch(event.data.data) {
-                case "WriteOK":
-                    pendingWriteCount--;
-                    debugLog("got OK from stdinWorker - remaining count: "+pendingWriteCount+"\n");
-                    break;
-                case "ClosedOK":
-                    stdinOpenState = CLOSED;
-                    debugLog("Stdin pipe closed\n");
-                    break;
-                default:
-                    debugLog("got msg from stdinWorker: "+event.data.data+"\n");
-                }
-                break;
-            case "debug":
-                debugLog("stdinWorker: "+event.data.data+"\n");
-                break;
-            case "error":
-                LogError("got error from stdinWorker: "+event.data.data+"\n");
-                pendingWriteCount = 0;
-                stdinOpenState = CLOSED;
-            }
-        }
-        stdinWorker.onerror = function(error) {
-            pendingWriteCount = 0;
-            closeStdinHandle();
-            LogError("got error from stdinWorker: "+error.message+"\n");
-        }
-        stdinWorker.postMessage({msg: "init", libc: options.libc});
-    }
-
-    /*
-     * writeStdin()
-     * @data: String containing the data to write
-     *
-     * Write data to the subprocess' stdin (equals to sending a request to the
-     * ChromeWorker object to write the data).
-     */
-    function writeStdin(data) {
-        if (stdinOpenState == CLOSED) return; // do not write to closed pipes
-
-        ++pendingWriteCount;
-        debugLog("sending "+data.length+" bytes to stdinWorker\n");
-        var pipe = parseInt(child.stdin);
-
-        stdinWorker.postMessage({
-            msg: 'write',
-            pipe: pipe,
-            data: data
-        });
-    }
-
-
-    /*
-     * closeStdinHandle()
-     *
-     * Close the stdin pipe, either directly or by requesting the ChromeWorker to
-     * close the pipe. The ChromeWorker will only close the pipe after the last write
-     * request process is done.
-     */
-
-    function closeStdinHandle() {
-        debugLog("trying to close stdin\n");
-        if (stdinOpenState != OPEN) return;
-        stdinOpenState = CLOSEABLE;
-
-        if (stdinWorker) {
-            debugLog("sending close stdin to worker\n");
-            var pipePtr = parseInt(child.stdin);
-
-            stdinWorker.postMessage({
-                msg: 'close',
-                pipe: pipePtr
-            });
-        }
-        else {
-            stdinOpenState = CLOSED;
-            debugLog("Closing Stdin\n");
-            close(child.stdin) && LogError("CloseHandle stdin failed");
-        }
-    }
-
+    result = {
+      get pid() {
+        return awaitPromise(procPromise.then(proc => {
+          return proc.pid;
+        }));
+      },
 
-    /*
-     * createReader(pipe, name)
-     *
-     * @pipe: handle to the pipe
-     * @name: String containing the pipe name (stdout or stderr)
-     * @callbackFunc: function to be called with the read data
-     *
-     * Create a ChromeWorker object for reading data asynchronously from
-     * the pipe (i.e. on a separate thread), and passing the result back to
-     * the caller.
-     *
-     */
-    function createReader(pipe, name, callbackFunc) {
-        var worker = new ChromeWorker(WORKER_URL_UNIX);
-        worker.onmessage = function(event) {
-            switch(event.data.msg) {
-            case "data":
-                debugLog("got "+event.data.count+" bytes from "+name+"\n");
-                var data = '';
-                if (options.charset === null) {
-                    data = getBytes(event.data.data);
-                }
-                else {
-                    try {
-                        data = convertBytes(event.data.data, options.charset);
-                    }
-                    catch(ex) {
-                        console.warn("error decoding output: " + ex);
-                        data = getBytes(event.data.data);
-                    }
-                }
-
-                callbackFunc(data);
-                break;
-            case "done":
-                debugLog("Pipe "+name+" closed\n");
-                if (event.data.data > 0) workerExitCode = event.data.data;
-                --readers;
-                if (readers == 0) cleanup();
-                break;
-            default:
-                debugLog("Got msg from "+name+": "+event.data.data+"\n");
-            }
-        }
-        worker.onerror = function(error) {
-            LogError("Got error from "+name+": "+error.message);
-        }
-
-        worker.postMessage({
-                msg: 'read',
-                pipe: pipe,
-                pid: pid,
-                libc: options.libc,
-                charset: options.charset === null ? "null" : options.charset,
-                name: name
-            });
-
-        return worker;
-    }
-
-    /*
-     * readPipes()
-     *
-     * Open the pipes for reading from stdout and stderr
-     */
-    function readPipes() {
-
-        stdoutWorker = createReader(child.stdout, "stdout", function (data) {
-            if(options.stdout) {
-                setTimeout(function() {
-                    options.stdout(data);
-                }, 0);
-            } else {
-                output += data;
-            }
-        });
-
-        if (!options.mergeStderr) stderrWorker = createReader(child.stderr, "stderr", function (data) {
-            if(options.stderr) {
-                setTimeout(function() {
-                    options.stderr(data);
-                 }, 0);
-            } else {
-                error += data;
-            }
-        });
-    }
+      wait() {
+        return awaitPromise(procPromise.then(proc => {
+          return proc.wait().then(({exitCode}) => exitCode);
+        }));
+      },
 
-    function cleanup() {
-        debugLog("Cleanup called\n");
-        if(active) {
-            active = false;
-
-            closeStdinHandle(); // should only be required in case of errors
-
-            var result, status = ctypes.int();
-            result = waitpid(child.pid, status.address(), 0);
-            if (result > 0)
-                exitCode = status.value
-            else
-                if (workerExitCode >= 0)
-                    exitCode = workerExitCode
-                else
-                    exitCode = status.value;
-
-            if (stdinWorker)
-                stdinWorker.postMessage({msg: 'stop'})
-
-            setTimeout(function _done() {
-                if (options.done) {
-                    try {
-                        options.done({
-                            exitCode: exitCode,
-                            stdout: output,
-                            stderr: error,
-                        });
-                    }
-                    catch(ex) {
-                        // prevent from blocking if options.done() throws an error
-                        done = true;
-                        throw ex;
-                    }
-
-                }
-                done = true;
-            }, 0);
-
-            libc.close();
-        }
-    }
-
-    //main
-
-    var cmdStr = getCommandStr(options.command);
-    var workDir = getWorkDir(options.workdir);
+      kill(hard = false) {
+        procPromise.then(proc => {
+          proc.kill(hard ? 0 : undefined);
+        });
+      },
+    };
 
-    child = {};
-    pid = popen(cmdStr, workDir, options.arguments, options.environment, child);
-
-    debugLog("subprocess started; got PID "+pid+"\n");
-
-    readPipes();
-
-    if (options.stdin) {
-        createStdinWriter();
-        if(typeof(options.stdin) == 'function') {
-            try {
-                options.stdin({
-                    write: function(data) {
-                        writeStdin(data);
-                    },
-                    close: function() {
-                        closeStdinHandle();
-                    }
-                });
-            }
-            catch(ex) {
-                // prevent from failing if options.stdin() throws an exception
-                closeStdinHandle();
-                throw ex;
-            }
-        } else {
-            writeStdin(options.stdin);
-            closeStdinHandle();
-        }
-    }
-
-    return {
-        wait: function() {
-            // wait for async operations to complete
-            var thread = Cc['@mozilla.org/thread-manager;1'].getService(Ci.nsIThreadManager).currentThread;
-            while (! done) thread.processNextEvent(true)
-            return exitCode;
-        },
-        kill: function(hardKill) {
-            var rv = kill(pid, (hardKill ? 9: 15));
-            cleanup(-1);
-            return rv;
-        },
-        pid: pid
-    }
-}
-
+    return result;
+  },
+};
 
 module.exports = subprocess;
deleted file mode 100644
--- a/addon-sdk/source/lib/sdk/system/child_process/subprocess_worker_unix.js
+++ /dev/null
@@ -1,278 +0,0 @@
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public
- * License Version 1.1 (the "MPL"); you may not use this file
- * except in compliance with the MPL. You may obtain a copy of
- * the MPL at http://www.mozilla.org/MPL/
- *
- * Software distributed under the MPL is distributed on an "AS
- * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
- * implied. See the MPL for the specific language governing
- * rights and limitations under the MPL.
- *
- * The Original Code is subprocess.jsm.
- *
- * The Initial Developer of this code is Patrick Brunschwig.
- * Portions created by Patrick Brunschwig <patrick@enigmail.net>
- * are Copyright (C) 2011 Patrick Brunschwig.
- * All Rights Reserved.
- *
- * Contributor(s):
- * Jan Gerber <j@mailb.org>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- * ***** END LICENSE BLOCK ***** */
-
-/*
- * ChromeWorker Object subprocess.jsm on Unix-like systems (Linux, Mac OS X, ...)
- * to process stdin/stdout/stderr on separate threads.
- *
- */
-
-// Being a ChromeWorker object, implicitly uses the following:
-// Cu.import("resource://gre/modules/ctypes.jsm");
-
-'use strict';
-
-const BufferSize = 1024;
-
-var libc = null;
-var libcFunc = {};
-
-
-/*
-    struct pollfd {
-         int    fd;       // file descriptor
-         short  events;   // events to look for
-         short  revents;  // events returned
-     };
-*/
-
-var pollfd = new ctypes.StructType("pollfd",
-                        [   {'fd': ctypes.int},
-                            {'events': ctypes.short},
-                            {'revents': ctypes.short}
-                        ]);
-
-var WriteBuffer = ctypes.uint8_t.array(BufferSize);
-var ReadBuffer = ctypes.char.array(BufferSize);
-
-
-const POLLIN     = 0x0001;
-const POLLOUT    = 0x0004;
-
-const POLLERR    = 0x0008;         // some poll error occurred
-const POLLHUP    = 0x0010;         // file descriptor was "hung up"
-const POLLNVAL   = 0x0020;         // requested events "invalid"
-
-const WNOHANG    = 0x01;
-
-const pid_t = ctypes.int32_t;
-
-const INDEFINITE = -1;
-const NOWAIT     = 0;
-const WAITTIME   = 200  // wait time for poll() in ms
-
-function initLibc(libName) {
-    postMessage({msg: "debug", data: "initialising library with "+ libName});
-
-    libc = ctypes.open(libName);
-
-    libcFunc.pollFds = pollfd.array(1);
-
-    // int poll(struct pollfd fds[], nfds_t nfds, int timeout);
-    libcFunc.poll = libc.declare("poll",
-                                  ctypes.default_abi,
-                                  ctypes.int,
-                                  libcFunc.pollFds,
-                                  ctypes.unsigned_int,
-                                  ctypes.int);
-
-    //ssize_t write(int fd, const void *buf, size_t count);
-    // NOTE: buf is declared as array of unsigned int8 instead of char to avoid
-    // implicit charset conversion
-    libcFunc.write = libc.declare("write",
-                                  ctypes.default_abi,
-                                  ctypes.int,
-                                  ctypes.int,
-                                  WriteBuffer,
-                                  ctypes.int);
-
-    //int read(int fd, void *buf, size_t count);
-    libcFunc.read = libc.declare("read",
-                                  ctypes.default_abi,
-                                  ctypes.int,
-                                  ctypes.int,
-                                  ReadBuffer,
-                                  ctypes.int);
-
-    //int pipe(int pipefd[2]);
-    libcFunc.pipefd = ctypes.int.array(2);
-
-    //int close(int fd);
-    libcFunc.close = libc.declare("close",
-                                  ctypes.default_abi,
-                                  ctypes.int,
-                                  ctypes.int);
-
-    //pid_t waitpid(pid_t pid, int *status, int options);
-    libcFunc.waitpid = libc.declare("waitpid",
-                                  ctypes.default_abi,
-                                  pid_t,
-                                  pid_t,
-                                  ctypes.int.ptr,
-                                  ctypes.int);
-}
-
-function closePipe(pipe) {
-    libcFunc.close(pipe);
-}
-
-function writePipe(pipe, data) {
-
-    postMessage({msg: "debug", data: "trying to write to "+pipe});
-
-    let numChunks = Math.floor(data.length / BufferSize);
-    let pData = new WriteBuffer();
-
-    for (var chunk = 0; chunk <= numChunks; chunk ++) {
-        let numBytes = chunk < numChunks ? BufferSize : data.length - chunk * BufferSize;
-        for (var i=0; i < numBytes; i++) {
-            pData[i] = data.charCodeAt(chunk * BufferSize + i) % 256;
-        }
-
-        let bytesWritten = libcFunc.write(pipe, pData, numBytes);
-        if (bytesWritten != numBytes) {
-            closePipe(pipe);
-            postMessage({ msg: "error", data: "error: wrote "+bytesWritten+" instead of "+numBytes+" bytes"});
-            close();
-        }
-    }
-    postMessage({msg: "info", data: "wrote "+data.length+" bytes of data"});
-}
-
-
-function readString(data, length, charset) {
-    var string = '', bytes = [];
-    for(var i = 0;i < length; i++) {
-        if(data[i] == 0 && charset != "null") // stop on NULL character for non-binary data
-           break;
-
-        bytes.push(data[i]);
-    }
-
-    return bytes;
-}
-
-function readPipe(pipe, charset, pid) {
-    var p = new libcFunc.pollFds;
-    p[0].fd = pipe;
-    p[0].events = POLLIN | POLLERR | POLLHUP;
-    p[0].revents = 0;
-    var pollTimeout = WAITTIME;
-    var exitCode = -1;
-    var readCount = 0;
-    var result, status = ctypes.int();
-    result = 0;
-
-
-    const i=0;
-    while (true) {
-        if (result == 0) {
-            result = libcFunc.waitpid(pid, status.address(), WNOHANG);
-            if (result > 0) {
-                pollTimeout = NOWAIT;
-                exitCode = parseInt(status.value);
-                postMessage({msg: "debug", data: "waitpid signaled subprocess stop, exitcode="+status.value });
-            }
-        }
-        var r = libcFunc.poll(p, 1, pollTimeout);
-        if (r > 0) {
-            if (p[i].revents & POLLIN) {
-                postMessage({msg: "debug", data: "reading next chunk"});
-                readCount = readPolledFd(p[i].fd, charset);
-                if (readCount == 0) break;
-            }
-
-            if (p[i].revents & POLLHUP) {
-                postMessage({msg: "debug", data: "poll returned HUP"});
-                break;
-            }
-            else if (p[i].revents & POLLERR) {
-                postMessage({msg: "error", data: "poll returned error"});
-                break;
-            }
-            else if (p[i].revents != POLLIN) {
-                postMessage({msg: "error", data: "poll returned "+p[i]});
-                break;
-            }
-        }
-        else
-            if (pollTimeout == 0 || r < 0) break;
-    }
-
-    // continue reading until the buffer is empty
-    while (readCount > 0) {
-      readCount = readPolledFd(pipe, charset);
-    }
-
-    libcFunc.close(pipe);
-    postMessage({msg: "done", data: exitCode });
-    libc.close();
-    close();
-}
-
-function readPolledFd(pipe, charset) {
-    var line = new ReadBuffer();
-    var r = libcFunc.read(pipe, line, BufferSize);
-
-    if (r > 0) {
-        var c = readString(line, r, charset);
-        postMessage({msg: "data", data: c, count: c.length});
-    }
-    return r;
-}
-
-onmessage = function (event) {
-    switch (event.data.msg) {
-    case "init":
-        initLibc(event.data.libc);
-        break;
-    case "read":
-        initLibc(event.data.libc);
-        readPipe(event.data.pipe, event.data.charset, event.data.pid);
-        break;
-    case "write":
-        // data contents:
-        //   msg: 'write'
-        //   data: the data (string) to write
-        //   pipe: ptr to pipe
-        writePipe(event.data.pipe, event.data.data);
-        postMessage({msg: "info", data: "WriteOK"});
-        break;
-    case "close":
-        postMessage({msg: "debug", data: "closing stdin\n"});
-
-        closePipe(event.data.pipe);
-        postMessage({msg: "info", data: "ClosedOK"});
-        break;
-    case "stop":
-        libc.close(); // do not use libc after this point
-        close();
-        break;
-    default:
-        throw("error: Unknown command"+event.data.msg+"\n");
-    }
-    return;
-};
deleted file mode 100644
--- a/addon-sdk/source/lib/sdk/system/child_process/subprocess_worker_win.js
+++ /dev/null
@@ -1,237 +0,0 @@
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public
- * License Version 1.1 (the "MPL"); you may not use this file
- * except in compliance with the MPL. You may obtain a copy of
- * the MPL at http://www.mozilla.org/MPL/
- *
- * Software distributed under the MPL is distributed on an "AS
- * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
- * implied. See the MPL for the specific language governing
- * rights and limitations under the MPL.
- *
- * The Original Code is subprocess.jsm.
- *
- * The Initial Developer of this code is Patrick Brunschwig.
- * Portions created by Patrick Brunschwig <patrick@enigmail.net>
- * are Copyright (C) 2011 Patrick Brunschwig.
- * All Rights Reserved.
- *
- * Contributor(s):
- * Jan Gerber <j@mailb.org>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- * ***** END LICENSE BLOCK ***** */
-
-/*
- * ChromeWorker Object subprocess.jsm on Windows to process stdin/stdout/stderr
- * on separate threads.
- *
- */
-
-// Being a ChromeWorker object, implicitly uses the following:
-// Cu.import("resource://gre/modules/ctypes.jsm");
-
-'use strict';
-
-const BufferSize = 1024;
-
-const BOOL = ctypes.bool;
-const HANDLE = ctypes.size_t;
-const DWORD = ctypes.uint32_t;
-const LPDWORD = DWORD.ptr;
-const PVOID = ctypes.voidptr_t;
-const LPVOID = PVOID;
-
-/*
-typedef struct _OVERLAPPED {
-  ULONG_PTR Internal;
-  ULONG_PTR InternalHigh;
-  union {
-    struct {
-      DWORD Offset;
-      DWORD OffsetHigh;
-    };
-    PVOID  Pointer;
-  };
-  HANDLE    hEvent;
-} OVERLAPPED, *LPOVERLAPPED;
-*/
-const OVERLAPPED = new ctypes.StructType("OVERLAPPED");
-
-var ReadFileBuffer = ctypes.char.array(BufferSize);
-var WriteFileBuffer = ctypes.uint8_t.array(BufferSize);
-
-var kernel32dll = null;
-var libFunc = {};
-
-function initLib(libName) {
-    if (ctypes.size_t.size == 8) {
-        var WinABI = ctypes.default_abi;
-    } else {
-        var WinABI = ctypes.winapi_abi;
-    }
-
-    kernel32dll = ctypes.open(libName);
-
-    /*
-    BOOL WINAPI WriteFile(
-      __in         HANDLE hFile,
-      __in         LPCVOID lpBuffer,
-      __in         DWORD nNumberOfBytesToWrite,
-      __out_opt    LPDWORD lpNumberOfBytesWritten,
-      __inout_opt  LPOVERLAPPED lpOverlapped
-    );
-
-    NOTE: lpBuffer is declared as array of unsigned int8 instead of char to avoid
-           implicit charset conversion
-    */
-    libFunc.WriteFile = kernel32dll.declare("WriteFile",
-                                        WinABI,
-                                        BOOL,
-                                        HANDLE,
-                                        WriteFileBuffer,
-                                        DWORD,
-                                        LPDWORD,
-                                        OVERLAPPED.ptr
-    );
-
-    /*
-    BOOL WINAPI ReadFile(
-      __in         HANDLE hFile,
-      __out        LPVOID ReadFileBuffer,
-      __in         DWORD nNumberOfBytesToRead,
-      __out_opt    LPDWORD lpNumberOfBytesRead,
-      __inout_opt  LPOVERLAPPED lpOverlapped
-    );
-    */
-    libFunc.ReadFile = kernel32dll.declare("ReadFile",
-                                        WinABI,
-                                        BOOL,
-                                        HANDLE,
-                                        ReadFileBuffer,
-                                        DWORD,
-                                        LPDWORD,
-                                        OVERLAPPED.ptr
-    );
-
-    /*
-    BOOL WINAPI CloseHandle(
-      __in  HANDLE hObject
-    );
-    */
-    libFunc.CloseHandle = kernel32dll.declare("CloseHandle",
-                                            WinABI,
-                                            BOOL,
-                                            HANDLE
-    );
-}
-
-
-function writePipe(pipe, data) {
-    var bytesWritten = DWORD(0);
-
-    var pData = new WriteFileBuffer();
-
-    var numChunks = Math.floor(data.length / BufferSize);
-    for (var chunk = 0; chunk <= numChunks; chunk ++) {
-        var numBytes = chunk < numChunks ? BufferSize : data.length - chunk * BufferSize;
-        for (var i=0; i < numBytes; i++) {
-            pData[i] = data.charCodeAt(chunk * BufferSize + i) % 256;
-        }
-
-      var r = libFunc.WriteFile(pipe, pData, numBytes, bytesWritten.address(), null);
-      if (bytesWritten.value != numBytes)
-          throw("error: wrote "+bytesWritten.value+" instead of "+numBytes+" bytes");
-    }
-    postMessage("wrote "+data.length+" bytes of data");
-}
-
-function readString(data, length, charset) {
-    var string = '', bytes = [];
-    for(var i = 0;i < length; i++) {
-        if(data[i] == 0 && charset != "null") // stop on NULL character for non-binary data
-           break;
-
-        bytes.push(data[i]);
-    }
-
-    return bytes;
-}
-
-function readPipe(pipe, charset) {
-    while (true) {
-        var bytesRead = DWORD(0);
-        var line = new ReadFileBuffer();
-        var r = libFunc.ReadFile(pipe, line, BufferSize, bytesRead.address(), null);
-
-        if (!r) {
-            // stop if we get an error (such as EOF reached)
-            postMessage({msg: "info", data: "ReadFile failed"});
-            break;
-        }
-
-        if (bytesRead.value > 0) {
-            var c = readString(line, bytesRead.value, charset);
-            postMessage({msg: "data", data: c, count: c.length});
-        }
-        else {
-            break;
-        }
-    }
-    libFunc.CloseHandle(pipe);
-    postMessage({msg: "done"});
-    kernel32dll.close();
-    close();
-}
-
-onmessage = function (event) {
-    let pipePtr;
-    switch (event.data.msg) {
-    case "init":
-        initLib(event.data.libc);
-        break;
-    case "write":
-        // data contents:
-        //   msg: 'write'
-        //   data: the data (string) to write
-        //   pipe: ptr to pipe
-        pipePtr = HANDLE.ptr(event.data.pipe);
-        writePipe(pipePtr.contents, event.data.data);
-        postMessage("WriteOK");
-        break;
-    case "read":
-        initLib(event.data.libc);
-        pipePtr = HANDLE.ptr(event.data.pipe);
-        readPipe(pipePtr.contents, event.data.charset);
-        break;
-    case "close":
-        pipePtr = HANDLE.ptr(event.data.pipe);
-        postMessage("closing stdin\n");
-
-        if (libFunc.CloseHandle(pipePtr.contents)) {
-            postMessage("ClosedOK");
-        }
-        else
-            postMessage("Could not close stdin handle");
-        break;
-    case "stop":
-        kernel32dll.close();
-        close();
-        break;
-    default:
-        throw("error: Unknown command"+event.data.msg+"\n");
-    }
-    return;
-};
--- a/addon-sdk/source/lib/sdk/system/globals.js
+++ b/addon-sdk/source/lib/sdk/system/globals.js
@@ -30,10 +30,17 @@ Object.defineProperty(exports, 'define',
   // variables remain accessible.
   configurable: true,
   get: function() {
     let sandbox = this;
     return function define(factory) {
       factory = Array.slice(arguments).pop();
       factory.call(sandbox, sandbox.require, sandbox.exports, sandbox.module);
     }
-  }
+  },
+  set: function(value) {
+    Object.defineProperty(this, 'define', {
+      configurable: true,
+      enumerable: true,
+      value,
+    });
+  },
 });
--- a/addon-sdk/source/lib/toolkit/loader.js
+++ b/addon-sdk/source/lib/toolkit/loader.js
@@ -31,31 +31,65 @@ const { loadSubScript } = Cc['@mozilla.o
 const { notifyObservers } = Cc['@mozilla.org/observer-service;1'].
                         getService(Ci.nsIObserverService);
 const { XPCOMUtils } = Cu.import("resource://gre/modules/XPCOMUtils.jsm", {});
 const { NetUtil } = Cu.import("resource://gre/modules/NetUtil.jsm", {});
 const { join: pathJoin, normalize, dirname } = Cu.import("resource://gre/modules/osfile/ospath_unix.jsm");
 
 XPCOMUtils.defineLazyGetter(this, "XulApp", () => {
   let xulappURI = module.uri.replace("toolkit/loader.js",
-                                       "sdk/system/xul-app.jsm");
+                                     "sdk/system/xul-app.jsm");
   return Cu.import(xulappURI, {});
 });
 
 // Define some shortcuts.
 const bind = Function.call.bind(Function.bind);
 const getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
-const define = Object.defineProperties;
 const prototypeOf = Object.getPrototypeOf;
-const create = Object.create;
-const keys = Object.keys;
 const getOwnIdentifiers = x => [...Object.getOwnPropertyNames(x),
                                 ...Object.getOwnPropertySymbols(x)];
 
-const NODE_MODULES = ["assert", "buffer_ieee754", "buffer", "child_process", "cluster", "console", "constants", "crypto", "_debugger", "dgram", "dns", "domain", "events", "freelist", "fs", "http", "https", "_linklist", "module", "net", "os", "path", "punycode", "querystring", "readline", "repl", "stream", "string_decoder", "sys", "timers", "tls", "tty", "url", "util", "vm", "zlib"];
+const NODE_MODULES = new Set([
+  "assert",
+  "buffer_ieee754",
+  "buffer",
+  "child_process",
+  "cluster",
+  "console",
+  "constants",
+  "crypto",
+  "_debugger",
+  "dgram",
+  "dns",
+  "domain",
+  "events",
+  "freelist",
+  "fs",
+  "http",
+  "https",
+  "_linklist",
+  "module",
+  "net",
+  "os",
+  "path",
+  "punycode",
+  "querystring",
+  "readline",
+  "repl",
+  "stream",
+  "string_decoder",
+  "sys",
+  "timers",
+  "tls",
+  "tty",
+  "url",
+  "util",
+  "vm",
+  "zlib",
+]);
 
 const COMPONENT_ERROR = '`Components` is not available in this context.\n' +
   'Functionality provided by Components may be available in an SDK\n' +
   'module: https://developer.mozilla.org/en-US/Add-ons/SDK \n\n' +
   'However, if you still need to import Components, you may use the\n' +
   '`chrome` module\'s properties for shortcuts to Component properties:\n\n' +
   'Shortcuts: \n' +
   '    Cc = Components' + '.classes \n' +
@@ -117,17 +151,17 @@ function iced(f) {
 // useful during loader bootstrap when other util modules can't be used &
 // thats only case where this export should be used.
 const override = iced(function override(target, source) {
   let properties = descriptor(target)
   let extension = descriptor(source || {})
   getOwnIdentifiers(extension).forEach(function(name) {
     properties[name] = extension[name];
   });
-  return define({}, properties);
+  return Object.defineProperties({}, properties);
 });
 Loader.override = override;
 
 function sourceURI(uri) { return String(uri).split(" -> ").pop(); }
 Loader.sourceURI = iced(sourceURI);
 
 function isntLoaderFrame(frame) { return frame.fileName !== module.uri }
 
@@ -188,17 +222,17 @@ function readURI(uri) {
   });
 
   stream.close();
 
   return data;
 }
 
 // Combines all arguments into a resolved, normalized path
-function join (...paths) {
+function join(...paths) {
   let joined = pathJoin(...paths);
   let resolved = normalize(joined);
 
   // OS.File `normalize` strips out any additional slashes breaking URIs like
   // `resource://`, `resource:///`, `chrome://` or `file:///`, so we work
   // around this putting back the slashes originally given, for such schemes.
   let re = /^(resource|file|chrome)(\:\/{1,3})([^\/])/;
   let matches = joined.match(re);
@@ -296,31 +330,32 @@ const load = iced(function load(loader, 
     get Components() {
       // Expose `Components` property to throw error on usage with
       // additional information
       throw new ReferenceError(COMPONENT_ERROR);
     }
   });
 
   let sandbox;
-  if (loader.sharedGlobalSandbox &&
+  if ((loader.useSharedGlobalSandbox || isSystemURI(module.uri)) &&
       loader.sharedGlobalBlocklist.indexOf(module.id) == -1) {
     // Create a new object in this sandbox, that will be used as
     // the scope object for this particular module
     sandbox = new loader.sharedGlobalSandbox.Object();
     // Inject all expected globals in the scope object
     getOwnIdentifiers(globals).forEach(function(name) {
       descriptors[name] = getOwnPropertyDescriptor(globals, name)
+      descriptors[name].configurable = true;
     });
-    define(sandbox, descriptors);
+    Object.defineProperties(sandbox, descriptors);
   }
   else {
     sandbox = Sandbox({
       name: module.uri,
-      prototype: create(globals, descriptors),
+      prototype: Object.create(globals, descriptors),
       wantXrays: false,
       wantGlobalProperties: module.id == "sdk/indexed-db" ? ["indexedDB"] : [],
       invisibleToDebugger: loader.invisibleToDebugger,
       metadata: {
         addonID: loader.id,
         URI: module.uri
       }
     });
@@ -354,26 +389,26 @@ const load = iced(function load(loader, 
     // it does.
     else if (frames[frames.length - 1].fileName !== file) {
       frames.push({ fileName: file, lineNumber: lineNumber, name: "" });
     }
 
     let prototype = typeof(error) === "object" ? error.constructor.prototype :
                     Error.prototype;
 
-    throw create(prototype, {
+    throw Object.create(prototype, {
       message: { value: message, writable: true, configurable: true },
       fileName: { value: fileName, writable: true, configurable: true },
       lineNumber: { value: lineNumber, writable: true, configurable: true },
       stack: { value: serializeStack(frames), writable: true, configurable: true },
       toString: { value: () => toString, writable: true, configurable: true },
     });
   }
 
-  if(loadModuleHook) {
+  if (loadModuleHook) {
     module = loadModuleHook(module, require);
   }
 
   if (loader.checkCompatibility) {
     let err = XulApp.incompatibility(module);
     if (err) {
       throw err;
     }
@@ -382,96 +417,159 @@ const load = iced(function load(loader, 
   if (module.exports && typeof(module.exports) === 'object')
     freeze(module.exports);
 
   return module;
 });
 Loader.load = load;
 
 // Utility function to normalize module `uri`s so they have `.js` extension.
-function normalizeExt (uri) {
+function normalizeExt(uri) {
   return isJSURI(uri) ? uri :
          isJSONURI(uri) ? uri :
          isJSMURI(uri) ? uri :
          uri + '.js';
 }
 
 // Strips `rootURI` from `string` -- used to remove absolute resourceURI
 // from a relative path
-function stripBase (rootURI, string) {
+function stripBase(rootURI, string) {
   return string.replace(rootURI, './');
 }
 
 // Utility function to join paths. In common case `base` is a
 // `requirer.uri` but in some cases it may be `baseURI`. In order to
 // avoid complexity we require `baseURI` with a trailing `/`.
 const resolve = iced(function resolve(id, base) {
-  if (!isRelative(id)) return id;
-  let basePaths = base.split('/');
-  // Pop the last element in the `base`, because it is from a
-  // relative file
-  // '../mod.js' from '/path/to/file.js' should resolve to '/path/mod.js'
-  basePaths.pop();
-  if (!basePaths.length)
+  if (!isRelative(id))
+    return id;
+
+  let baseDir = dirname(base);
+  if (!baseDir)
     return normalize(id);
-  let resolved = join(basePaths.join('/'), id);
+
+  let resolved = join(baseDir, id);
 
   // Joining and normalizing removes the './' from relative files.
   // We need to ensure the resolution still has the root
   if (isRelative(base))
     resolved = './' + resolved;
 
   return resolved;
 });
 Loader.resolve = resolve;
 
+// Attempts to load `path` and then `path.js`
+// Returns `path` with valid file, or `undefined` otherwise
+function resolveAsFile(path) {
+  let found;
+
+  // As per node's loader spec,
+  // we first should try and load 'path' (with no extension)
+  // before trying 'path.js'. We will not support this feature
+  // due to performance, but may add it if necessary for adoption.
+  try {
+    // Append '.js' to path name unless it's another support filetype
+    path = normalizeExt(path);
+    readURI(path);
+    found = path;
+  } catch (e) {}
+
+  return found;
+}
+
+// Attempts to load `path/package.json`'s `main` entry,
+// followed by `path/index.js`, or `undefined` otherwise
+function resolveAsDirectory(path) {
+  try {
+    // If `path/package.json` exists, parse the `main` entry
+    // and attempt to load that
+    let main = getManifestMain(JSON.parse(readURI(path + '/package.json')));
+    if (main != null) {
+      let tmpPath = join(path, main);
+      let found = resolveAsFile(tmpPath);
+      if (found)
+        return found
+    }
+  } catch (e) {}
+
+  try {
+    let tmpPath = path + '/index.js';
+    readURI(tmpPath);
+    return tmpPath;
+  } catch (e) {}
+
+  return null;
+}
+
+function resolveRelative(rootURI, modulesDir, id) {
+  let fullId = join(rootURI, modulesDir, id);
+  let resolvedPath;
+
+  if ((resolvedPath = resolveAsFile(fullId)))
+    return stripBase(rootURI, resolvedPath);
+
+  if ((resolvedPath = resolveAsDirectory(fullId)))
+    return stripBase(rootURI, resolvedPath);
+
+  return null;
+}
+
+// From `resolve` module
+// https://github.com/substack/node-resolve/blob/master/lib/node-modules-paths.js
+function* getNodeModulePaths(start) {
+  // Configurable in node -- do we need this to be configurable?
+  let moduleDir = 'node_modules';
+
+  let parts = start.split('/');
+  while (parts.length) {
+    let leaf = parts.pop();
+    if (leaf !== moduleDir)
+      yield join(...parts, leaf, moduleDir);
+  }
+
+  yield moduleDir;
+}
+
 // Node-style module lookup
 // Takes an id and path and attempts to load a file using node's resolving
 // algorithm.
 // `id` should already be resolved relatively at this point.
 // http://nodejs.org/api/modules.html#modules_all_together
 const nodeResolve = iced(function nodeResolve(id, requirer, { rootURI }) {
   // Resolve again
   id = Loader.resolve(id, requirer);
 
   // If this is already an absolute URI then there is no resolution to do
   if (isAbsoluteURI(id))
-    return void 0;
+    return null;
 
   // we assume that extensions are correct, i.e., a directory doesnt't have '.js'
   // and a js file isn't named 'file.json.js'
-  let fullId = join(rootURI, id);
   let resolvedPath;
 
-  if ((resolvedPath = loadAsFile(fullId)))
-    return stripBase(rootURI, resolvedPath);
-
-  if ((resolvedPath = loadAsDirectory(fullId)))
-    return stripBase(rootURI, resolvedPath);
+  if ((resolvedPath = resolveRelative(rootURI, "", id)))
+    return resolvedPath;
 
   // If the requirer is an absolute URI then the node module resolution below
   // won't work correctly as we prefix everything with rootURI
   if (isAbsoluteURI(requirer))
-    return void 0;
+    return null;
 
   // If manifest has dependencies, attempt to look up node modules
   // in the `dependencies` list
-  let dirs = getNodeModulePaths(dirname(requirer)).map(dir => join(rootURI, dir, id));
-  for (let i = 0; i < dirs.length; i++) {
-    if ((resolvedPath = loadAsFile(dirs[i])))
-      return stripBase(rootURI, resolvedPath);
-
-    if ((resolvedPath = loadAsDirectory(dirs[i])))
-      return stripBase(rootURI, resolvedPath);
+  for (let modulesDir of getNodeModulePaths(dirname(requirer))) {
+    if ((resolvedPath = resolveRelative(rootURI, modulesDir, id)))
+      return resolvedPath;
   }
 
   // We would not find lookup for things like `sdk/tabs`, as that's part of
   // the alias mapping. If during `generateMap`, the runtime lookup resolves
   // with `resolveURI` -- if during runtime, then `resolve` will throw.
-  return void 0;
+  return null;
 });
 
 // String (`${rootURI}:${requirer}:${id}`) -> resolvedPath
 Loader.nodeResolverCache = new Map();
 
 const nodeResolveWithCache = iced(function cacheNodeResolutions(id, requirer, { rootURI }) {
   // Compute the cache key based on current arguments.
   let cacheKey = `${rootURI || ""}:${requirer}:${id}`;
@@ -483,141 +581,65 @@ const nodeResolveWithCache = iced(functi
 
   // Resolve and cache if it is not in the cache yet.
   let result = nodeResolve(id, requirer, { rootURI });
   Loader.nodeResolverCache.set(cacheKey, result);
   return result;
 });
 Loader.nodeResolve = nodeResolveWithCache;
 
-// Attempts to load `path` and then `path.js`
-// Returns `path` with valid file, or `undefined` otherwise
-function loadAsFile (path) {
-  let found;
-
-  // As per node's loader spec,
-  // we first should try and load 'path' (with no extension)
-  // before trying 'path.js'. We will not support this feature
-  // due to performance, but may add it if necessary for adoption.
-  try {
-    // Append '.js' to path name unless it's another support filetype
-    path = normalizeExt(path);
-    readURI(path);
-    found = path;
-  } catch (e) {}
-
-  return found;
-}
-
-// Attempts to load `path/package.json`'s `main` entry,
-// followed by `path/index.js`, or `undefined` otherwise
-function loadAsDirectory (path) {
-  try {
-    // If `path/package.json` exists, parse the `main` entry
-    // and attempt to load that
-    let main = getManifestMain(JSON.parse(readURI(path + '/package.json')));
-    if (main != null) {
-      let tmpPath = join(path, main);
-      let found = loadAsFile(tmpPath);
-      if (found)
-        return found
-    }
-    try {
-      let tmpPath = path + '/index.js';
-      readURI(tmpPath);
-      return tmpPath;
-    } catch (e) {}
-  } catch (e) {
-    try {
-      let tmpPath = path + '/index.js';
-      readURI(tmpPath);
-      return tmpPath;
-    } catch (e) {}
-  }
-  return void 0;
-}
-
-// From `resolve` module
-// https://github.com/substack/node-resolve/blob/master/lib/node-modules-paths.js
-function getNodeModulePaths (start) {
-  // Configurable in node -- do we need this to be configurable?
-  let moduleDir = 'node_modules';
-
-  let parts = start.split('/');
-  let dirs = [];
-  for (let i = parts.length - 1; i >= 0; i--) {
-    if (parts[i] === moduleDir) continue;
-    let dir = join(parts.slice(0, i + 1).join('/'), moduleDir);
-    dirs.push(dir);
-  }
-  dirs.push(moduleDir);
-  return dirs;
-}
-
-
-function addTrailingSlash (path) {
-  return !path ? null : !path.endsWith('/') ? path + '/' : path;
-}
-
-// Utility function to determine of module id `name` is a built in
-// module in node (fs, path, etc.);
-function isNodeModule (name) {
-  return !!~NODE_MODULES.indexOf(name);
-}
-
-// Make mapping array that is sorted from longest path to shortest path
-// to allow overlays. Used by `resolveURI`, returns an array
-function sortPaths (paths) {
-  return keys(paths).
-    sort((a, b) => (b.length - a.length)).
-    map((path) => [ path, paths[path] ]);
+function addTrailingSlash(path) {
+  return path.replace(/\/*$/, "/");
 }
 
 const resolveURI = iced(function resolveURI(id, mapping) {
-  let count = mapping.length, index = 0;
-
   // Do not resolve if already a resource URI
-  if (isAbsoluteURI(id)) return normalizeExt(id);
+  if (isAbsoluteURI(id))
+    return normalizeExt(id);
 
-  while (index < count) {
-    let [ path, uri ] = mapping[index++];
-
+  for (let [path, uri] of mapping) {
     // Strip off any trailing slashes to make comparisons simpler
-    let stripped = path.endsWith('/') ? path.slice(0, -1) : path;
+    let stripped = path.replace(/\/+$/, "");
 
     // We only want to match path segments explicitly. Examples:
     // * "foo/bar" matches for "foo/bar"
     // * "foo/bar" matches for "foo/bar/baz"
     // * "foo/bar" does not match for "foo/bar-1"
     // * "foo/bar/" does not match for "foo/bar"
     // * "foo/bar/" matches for "foo/bar/baz"
     //
     // Check for an empty path, an exact match, or a substring match
     // with the next character being a forward slash.
-    if(stripped === "" ||
-       (id.indexOf(stripped) === 0 &&
-        (id.length === path.length || id[stripped.length] === '/'))) {
+    if(stripped === "" || id === stripped || id.startsWith(stripped + "/")) {
       return normalizeExt(id.replace(path, uri));
     }
   }
-  return void 0; // otherwise we raise a warning, see bug 910304
+  return null;
 });
 Loader.resolveURI = resolveURI;
 
 // Creates version of `require` that will be exposed to the given `module`
 // in the context of the given `loader`. Each module gets own limited copy
 // of `require` that is allowed to load only a modules that are associated
 // with it during link time.
 const Require = iced(function Require(loader, requirer) {
   let {
     modules, mapping, resolve: loaderResolve, load,
     manifest, rootURI, isNative, requireMap,
     requireHook
   } = loader;
 
+  if (isSystemURI(requirer.uri)) {
+    // Built-in modules don't require the expensive module resolution
+    // algorithm used by SDK add-ons, so give them the more efficient standard
+    // resolve instead.
+    isNative = false;
+    loaderResolve = Loader.resolve;
+  }
+
   function require(id) {
     if (!id) // Throw if `id` is not passed.
       throw Error('You must provide a module name when calling require() from '
                   + requirer.id, requirer.uri);
 
     if (requireHook) {
       return requireHook(id, _require);
     }
@@ -699,41 +721,38 @@ const Require = iced(function Require(lo
       // immediately resolve the node-style mapping.
       // TODO: write more tests for this use case
       if (requireMap && requireMap[requirer.id])
         requirement = requireMap[requirer.id][id];
 
       let { overrides } = manifest.jetpack;
       for (let key in overrides) {
         // ignore any overrides using relative keys
-        if (/^[\.\/]/.test(key)) {
+        if (/^[.\/]/.test(key)) {
           continue;
         }
 
         // If the override is for x -> y,
         // then using require("x/lib/z") to get reqire("y/lib/z")
         // should also work
-        if (id == key || (id.substr(0, key.length + 1) == (key + "/"))) {
+        if (id == key || id.startsWith(key + "/")) {
           id = overrides[key] + id.substr(key.length);
-          id = id.replace(/^[\.\/]+/, "./");
-          if (id.substr(0, 2) == "./") {
-            id = "" + id.substr(2);
-          }
+          id = id.replace(/^[.\/]+/, "");
         }
       }
 
       // For native modules, we want to check if it's a module specified
       // in 'modules', like `chrome`, or `@loader` -- if it exists,
       // just set the uri to skip resolution
       if (!requirement && modules[id])
         uri = requirement = id;
 
       // If no requireMap was provided, or resolution not found in
       // the requireMap, and not a npm dependency, attempt a runtime lookup
-      if (!requirement && !isNodeModule(id)) {
+      if (!requirement && !NODE_MODULES.has(id)) {
         // If `isNative` defined, this is using the new, native-style
         // loader, not cuddlefish, so lets resolve using node's algorithm
         // and get back a path that needs to be resolved via paths mapping
         // in `resolveURI`
         requirement = loaderResolve(id, requirer.id, {
           manifest: manifest,
           rootURI: rootURI
         });
@@ -742,19 +761,25 @@ const Require = iced(function Require(lo
       // If not found in the map, not a node module, and wasn't able to be
       // looked up, it's something
       // found in the paths most likely, like `sdk/tabs`, which should
       // be resolved relatively if needed using traditional resolve
       if (!requirement) {
         requirement = isRelative(id) ? Loader.resolve(id, requirer.id) : id;
       }
     }
-    else {
+    else if (modules[id]) {
+      uri = requirement = id;
+    }
+    else if (requirer) {
       // Resolve `id` to its requirer if it's relative.
-      requirement = requirer ? loaderResolve(id, requirer.id) : id;
+      requirement = loaderResolve(id, requirer.id);
+    }
+    else {
+      requirement = id;
     }
 
     // Resolves `uri` of module using loaders resolve function.
     uri = uri || resolveURI(requirement, mapping);
 
     // Throw if `uri` can not be resolved.
     if (!uri) {
       throw Error('Module: Can not resolve "' + id + '" module required by ' +
@@ -785,19 +810,19 @@ const main = iced(function main(loader, 
   let module = loader.main = loader.modules[uri] = Module(id, uri);
   return loader.load(loader, module).exports;
 });
 Loader.main = main;
 
 // Makes module object that is made available to CommonJS modules when they
 // are evaluated, along with `exports` and `require`.
 const Module = iced(function Module(id, uri) {
-  return create(null, {
+  return Object.create(null, {
     id: { enumerable: true, value: id },
-    exports: { enumerable: true, writable: true, value: create(null),
+    exports: { enumerable: true, writable: true, value: Object.create(null),
                configurable: true },
     uri: { value: uri }
   });
 });
 Loader.Module = Module;
 
 // Takes `loader`, and unload `reason` string and notifies all observers that
 // they should cleanup after them-self.
@@ -873,34 +898,38 @@ function Loader(options) {
     manifest.jetpack.overrides = {};
   }
 
   // We create an identity object that will be dispatched on an unload
   // event as subject. This way unload listeners will be able to assert
   // which loader is unloaded. Please note that we intentionally don't
   // use `loader` as subject to prevent a loader access leakage through
   // observer notifications.
-  let destructor = freeze(create(null));
+  let destructor = freeze(Object.create(null));
 
-  let mapping = sortPaths(paths);
+  // Make mapping array that is sorted from longest path to shortest path.
+  let mapping = Object.keys(paths)
+                      .sort((a, b) => b.length - a.length)
+                      .map(path => [path, paths[path]]);
 
   // Define pseudo modules.
   modules = override({
     '@loader/unload': destructor,
     '@loader/options': options,
     'chrome': { Cc: Cc, Ci: Ci, Cu: Cu, Cr: Cr, Cm: Cm,
                 CC: bind(CC, Components), components: Components,
                 // `ChromeWorker` has to be inject in loader global scope.
                 // It is done by bootstrap.js:loadSandbox for the SDK.
                 ChromeWorker: ChromeWorker
     }
   }, modules);
 
   const builtinModuleExports = modules;
-  modules = keys(modules).reduce(function(result, id) {
+  modules = {};
+  for (let id of Object.keys(builtinModuleExports)) {
     // We resolve `uri` from `id` since modules are cached by `uri`.
     let uri = resolveURI(id, mapping);
     // In native loader, the mapping will not contain values for
     // pseudomodules -- store them as their ID rather than the URI
     if (isNative && !uri)
       uri = id;
     let module = Module(id, uri);
 
@@ -908,49 +937,46 @@ function Loader(options) {
     // allow them to be loaded lazily.
     Object.defineProperty(module, "exports", {
       enumerable: true,
       get: function() {
         return builtinModuleExports[id];
       }
     });
 
-    result[uri] = freeze(module);
-    return result;
-  }, {});
+    modules[uri] = freeze(module);
+  }
 
-  let sharedGlobalSandbox;
-  if (sharedGlobal) {
-    // Create the unique sandbox we will be using for all modules,
-    // so that we prevent creating a new comportment per module.
-    // The side effect is that all modules will share the same
-    // global objects.
-    sharedGlobalSandbox = Sandbox({
-      name: "Addon-SDK",
-      wantXrays: false,
-      wantGlobalProperties: [],
-      invisibleToDebugger: options.invisibleToDebugger || false,
-      metadata: {
-        addonID: options.id,
-        URI: "Addon-SDK"
-      },
-      prototype: options.sandboxPrototype || {}
-    });
-  }
+  // Create the unique sandbox we will be using for all modules,
+  // so that we prevent creating a new comportment per module.
+  // The side effect is that all modules will share the same
+  // global objects.
+  let sharedGlobalSandbox = Sandbox({
+    name: "Addon-SDK",
+    wantXrays: false,
+    wantGlobalProperties: [],
+    invisibleToDebugger: options.invisibleToDebugger || false,
+    metadata: {
+      addonID: options.id,
+      URI: "Addon-SDK"
+    },
+    prototype: options.sandboxPrototype || {}
+  });
 
   // Loader object is just a representation of a environment
   // state. We freeze it and mark make it's properties non-enumerable
   // as they are pure implementation detail that no one should rely upon.
   let returnObj = {
     destructor: { enumerable: false, value: destructor },
     globals: { enumerable: false, value: globals },
     mapping: { enumerable: false, value: mapping },
     // Map of module objects indexed by module URIs.
     modules: { enumerable: false, value: modules },
     metadata: { enumerable: false, value: metadata },
+    useSharedGlobalSandbox: { enumerable: false, value: !!sharedGlobal },
     sharedGlobalSandbox: { enumerable: false, value: sharedGlobalSandbox },
     sharedGlobalBlocklist: { enumerable: false, value: sharedGlobalBlocklist },
     sharedGlobalBlacklist: { enumerable: false, value: sharedGlobalBlocklist },
     // Map of module sandboxes indexed by module URIs.
     sandboxes: { enumerable: false, value: {} },
     resolve: { enumerable: false, value: resolve },
     // ID of the addon, if provided.
     id: { enumerable: false, value: options.id },
@@ -976,156 +1002,31 @@ function Loader(options) {
 
   if (isNative) {
     returnObj.isNative = { enumerable: false, value: true };
     returnObj.manifest = { enumerable: false, value: manifest };
     returnObj.requireMap = { enumerable: false, value: requireMap };
     returnObj.rootURI = { enumerable: false, value: addTrailingSlash(rootURI) };
   }
 
-  return freeze(create(null, returnObj));
+  return freeze(Object.create(null, returnObj));
 };
 Loader.Loader = Loader;
 
-var isJSONURI = uri => uri.substr(-5) === '.json';
-var isJSMURI = uri => uri.substr(-4) === '.jsm';
-var isJSURI = uri => uri.substr(-3) === '.js';
-var isAbsoluteURI = uri => uri.indexOf("resource://") >= 0 ||
-                           uri.indexOf("chrome://") >= 0 ||
-                           uri.indexOf("file://") >= 0
-var isRelative = id => id[0] === '.'
-
-const generateMap = iced(function generateMap(options, callback) {
-  let { rootURI, resolve, paths } = override({
-    paths: {},
-    resolve: Loader.nodeResolve
-  }, options);
-
-  rootURI = addTrailingSlash(rootURI);
+var isSystemURI = uri => /^resource:\/\/(gre|devtools|testing-common)\//.test(uri);
 
-  let manifest;
-  let manifestURI = join(rootURI, 'package.json');
-
-  if (rootURI)
-    manifest = JSON.parse(readURI(manifestURI));
-  else
-    throw new Error('No `rootURI` given to generate map');
-
-  let main = getManifestMain(manifest);
-
-  findAllModuleIncludes(main, {
-    resolve: resolve,
-    manifest: manifest,
-    rootURI: rootURI
-  }, {}, callback);
-
-});
-Loader.generateMap = generateMap;
+var isJSONURI = uri => uri.endsWith('.json');
+var isJSMURI = uri => uri.endsWith('.jsm');
+var isJSURI = uri => uri.endsWith('.js');
+var isAbsoluteURI = uri => uri.startsWith("resource://") ||
+                           uri.startsWith("chrome://") ||
+                           uri.startsWith("file://");
+var isRelative = id => id.startsWith(".");
 
 // Default `main` entry to './index.js' and ensure is relative,
 // since node allows 'lib/index.js' without relative `./`
-function getManifestMain (manifest) {
+function getManifestMain(manifest) {
   let main = manifest.main || './index.js';
   return isRelative(main) ? main : './' + main;
 }
 
-function findAllModuleIncludes (uri, options, results, callback) {
-  let { resolve, manifest, rootURI } = options;
-  results = results || {};
-
-  // Abort if JSON or JSM
-  if (isJSONURI(uri) || isJSMURI(uri)) {
-    callback(results);
-    return;
-  }
-
-  findModuleIncludes(join(rootURI, uri), modules => {
-    // If no modules are included in the file, just call callback immediately
-    if (!modules.length) {
-      callback(results);
-      return;
-    }
-
-    results[uri] = modules.reduce((agg, mod) => {
-      let resolved = resolve(mod, uri, { manifest: manifest, rootURI: rootURI });
-
-      // If resolution found, store the resolution; otherwise,
-      // skip storing it as runtime lookup will handle this
-      if (!resolved)
-        return agg;
-      agg[mod] = resolved;
-      return agg;
-    }, {});
-
-    let includes = keys(results[uri]);
-    let count = 0;
-    let subcallback = () => { if (++count >= includes.length) callback(results) };
-    includes.map(id => {
-      let moduleURI = results[uri][id];
-      if (!results[moduleURI])
-        findAllModuleIncludes(moduleURI, options, results, subcallback);
-      else
-        subcallback();
-    });
-  });
-}
-
-// From Substack's detector
-// https://github.com/substack/node-detective
-//
-// Given a resource URI or source, return an array of strings passed into
-// the require statements from the source
-function findModuleIncludes (uri, callback) {
-  let src = isAbsoluteURI(uri) ? readURI(uri) : uri;
-  let modules = [];
-
-  walk(src, function (node) {
-    if (isRequire(node))
-      modules.push(node.arguments[0].value);
-  });
-
-  callback(modules);
-}
-
-function walk (src, callback) {
-  // Import Reflect.jsm from here to prevent loading it until someone uses it
-  let { Reflect } = Cu.import("resource://gre/modules/reflect.jsm", {});
-  let nodes = Reflect.parse(src);
-  traverse(nodes, callback);
-}
-
-function traverse (node, cb) {
-  if (Array.isArray(node)) {
-    node.map(x => {
-      if (x != null) {
-        x.parent = node;
-        traverse(x, cb);
-      }
-    });
-  }
-  else if (node && typeof node === 'object') {
-    cb(node);
-    keys(node).map(key => {
-      if (key === 'parent' || !node[key]) return;
-      if (typeof node[key] === "object")
-        node[key].parent = node;
-      traverse(node[key], cb);
-    });
-  }
-}
-
-// From Substack's detector
-// https://github.com/substack/node-detective
-// Check an AST node to see if its a require statement.
-// A modification added to only evaluate to true if it actually
-// has a value being passed in as an argument
-function isRequire (node) {
-  var c = node.callee;
-  return c
-    && node.type === 'CallExpression'
-    && c.type === 'Identifier'
-    && c.name === 'require'
-    && node.arguments.length
-   && node.arguments[0].type === 'Literal';
-}
-
 module.exports = iced(Loader);
 });
--- a/addon-sdk/source/test/addons/child_process/index.js
+++ b/addon-sdk/source/test/addons/child_process/index.js
@@ -17,17 +17,17 @@ const app = require("sdk/system/xul-app"
 
 // Once Bug 903018 is resolved, just move the application testing to
 // module.metadata.engines
 if (app.is("Firefox")) {
   exports["test child_process in an addon"] = (assert, done) => {
     exec(isWindows ? "DIR /A-D" : "ls -al", {
       cwd: PROFILE_DIR
     }, (err, stdout, stderr) => {
-      assert.ok(!err, "no errors");
+      assert.equal(err, null, "no errors");
       assert.equal(stderr, "", "stderr is empty");
       assert.ok(/extensions\.ini/.test(stdout), "stdout output of `ls -al` finds files");
 
       if (isWindows)
         assert.ok(!/<DIR>/.test(stdout), "passing args works");
       else
         assert.ok(/d(r[-|w][-|x]){3}/.test(stdout), "passing args works");
       done();
--- a/addon-sdk/source/test/test-child_process.js
+++ b/addon-sdk/source/test/test-child_process.js
@@ -116,18 +116,18 @@ exports.testExecFileCallbackSuccess = fu
       assert.equal(stdout.trim(), '--myargs -j -s'.trim(), 'passes in correct arguments');
       done();
     });
   }).then(null, assert.fail);
 };
 
 exports.testExecFileCallbackError = function (assert, done) {
   execFile('not-real-command', { cwd: PROFILE_DIR }, function (err, stdout, stderr) {
-    assert.ok(/NS_ERROR_FILE_UNRECOGNIZED_PATH/.test(err.message),
-      'error contains error message');
+    assert.ok(/Executable not found/.test(err.message),
+      `error '${err.message}' contains error message`);
     assert.ok(err.lineNumber >= 0, 'error contains lineNumber');
     assert.ok(/resource:\/\//.test(err.fileName), 'error contains fileName');
     assert.equal(stdout, '', 'stdout is empty');
     assert.equal(stderr, '', 'stdout is empty');
     done();
   });
 };
 
--- a/addon-sdk/source/test/test-content-events.js
+++ b/addon-sdk/source/test/test-content-events.js
@@ -61,22 +61,17 @@ exports["test dead object errors"] = fun
   let cleanup = () => system.off("console-api-log-event", onMessage);
   let fail = (reason) => {
     cleanup();
     assert.fail(reason);
   }
 
   loader.unload();
 
-  // in order to get a dead object error on this module, we need to nuke
-  // the relative sandbox; unload the loader is not enough
-  let url = Object.keys(loader.sandboxes).
-    find(url => url.endsWith("/sdk/content/events.js"));
-
-  nuke(loader.sandboxes[url]);
+  nuke(loader.sharedGlobalSandbox);
 
   system.on("console-api-log-event", onMessage, true);
 
   openBrowserWindow().
     then(closeWindow).
     then(() => assert.pass("checking dead object errors")).
     then(cleanup).
     then(done, fail);
--- a/addon-sdk/source/test/test-loader.js
+++ b/addon-sdk/source/test/test-loader.js
@@ -1,15 +1,15 @@
 /* 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/. */
 'use strict';
 
 var {
-  Loader, main, unload, parseStack, generateMap, resolve, join,
+  Loader, main, unload, parseStack, resolve, join,
   Require, Module
 } = require('toolkit/loader');
 var { readURI } = require('sdk/net/url');
 
 var root = module.uri.substr(0, module.uri.lastIndexOf('/'));
 
 const app = require('sdk/system/xul-app');
 
--- a/addon-sdk/source/test/test-native-loader.js
+++ b/addon-sdk/source/test/test-native-loader.js
@@ -1,15 +1,15 @@
 /* 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/. */
 'use strict';
 
 var {
-  Loader, main, unload, parseStack, generateMap, resolve, nodeResolve
+  Loader, main, unload, parseStack, resolve, nodeResolve
 } = require('toolkit/loader');
 var { readURI } = require('sdk/net/url');
 var { all } = require('sdk/core/promise');
 var testOptions = require('@test/options');
 
 var root = module.uri.substr(0, module.uri.lastIndexOf('/'))
 // The following adds Debugger constructor to the global namespace.
 const { Cu } = require('chrome');
@@ -65,29 +65,16 @@ exports['test nodeResolve'] = function (
 
 /*
 // TODO not working in current env
 exports['test bundle'] = function (assert, done) {
   loadAddon('/native-addons/native-addon-test/')
 };
 */
 
-exports['test generateMap()'] = function (assert, done) {
-  getJSON('/fixtures/native-addon-test/expectedmap.json').then(expected => {
-    generateMap({
-      rootURI: root + '/fixtures/native-addon-test/'
-    }, map => {
-      assert.deepEqual(map, expected, 'generateMap returns expected mappings');
-      assert.equal(map['./index.js']['./dir/a'], './dir/a.js',
-        'sanity check on correct mappings');
-      done();
-    });
-  }).then(null, (reason) => console.error(reason));
-};
-
 exports['test JSM loading'] = function (assert, done) {
   getJSON('/fixtures/jsm-package/package.json').then(manifest => {
     let rootURI = root + '/fixtures/jsm-package/';
     let loader = Loader({
       paths: makePaths(rootURI),
       rootURI: rootURI,
       manifest: manifest,
       isNative: true
--- a/b2g/dev/config/tooltool-manifests/linux64/hazard.manifest
+++ b/b2g/dev/config/tooltool-manifests/linux64/hazard.manifest
@@ -19,19 +19,19 @@
 "algorithm" : "sha512",
 "filename" : "gtk3.tar.xz",
 "setup" : "setup.sh",
 "unpack" : true,
 "digest" : "3915f8ec396c56a8a92e6f9695b70f09ce9d1582359d1258e37e3fd43a143bc974410e4cfc27f500e095f34a8956206e0ebf799b7287f0f38def0d5e34ed71c9",
 "size" : 12072532
 },
 {
-"version": "rustc 1.11.0 (9b21dcd6a 2016-08-15) repack",
-"size": 131489924,
-"digest": "59f7463a0da38f324daa4ffc2678d78afb4fe0df13248c1d215bcb996ec05e8521155563cde9a8b719a9b98c5feeaf97cc9e8d52c9b95f6b44728870d908d5b6",
+"version": "rustc 1.12.0 (3191fbae9 2016-09-23) repack",
+"size": 102403884,
+"digest": "a7c1512d955d3030bcc1ebddfbf512f7b11b66e31634726deab78d0403fc0ceadd603d32b08c1a5025d3e9ee4ff48ddcf5eaba33468bb2161cfb9fb1a557affa",
 "algorithm": "sha512",
 "filename": "rustc.tar.xz",
 "unpack": true
 },
 {
 "algorithm" : "sha512",
 "filename" : "sccache.tar.bz2",
 "unpack" : true,
--- a/b2g/dev/config/tooltool-manifests/linux64/releng.manifest
+++ b/b2g/dev/config/tooltool-manifests/linux64/releng.manifest
@@ -11,19 +11,19 @@
 "size": 12072532,
 "digest": "3915f8ec396c56a8a92e6f9695b70f09ce9d1582359d1258e37e3fd43a143bc974410e4cfc27f500e095f34a8956206e0ebf799b7287f0f38def0d5e34ed71c9",
 "algorithm": "sha512",
 "filename": "gtk3.tar.xz",
 "setup": "setup.sh",
 "unpack": true
 },
 {
-"version": "rustc 1.11.0 (9b21dcd6a 2016-08-15) repack",
-"size": 131489924,
-"digest": "59f7463a0da38f324daa4ffc2678d78afb4fe0df13248c1d215bcb996ec05e8521155563cde9a8b719a9b98c5feeaf97cc9e8d52c9b95f6b44728870d908d5b6",
+"version": "rustc 1.12.0 (3191fbae9 2016-09-23) repack",
+"size": 102403884,
+"digest": "a7c1512d955d3030bcc1ebddfbf512f7b11b66e31634726deab78d0403fc0ceadd603d32b08c1a5025d3e9ee4ff48ddcf5eaba33468bb2161cfb9fb1a557affa",
 "algorithm": "sha512",
 "filename": "rustc.tar.xz",
 "unpack": true
 },
 {
 "version": "cargo 0.13.0-nightly (e713e7f 2016-08-31)",
 "size": 3245716,
 "digest": "d5bb0d88ce7bb1b5a316d7a8ca6341672f5ee8008fa7754511bf53fabd54c0770e95397232896d6087547891f1143f6968d8b1e106e39800b43defeb0025c7c0",
--- a/browser/config/tooltool-manifests/linux32/releng.manifest
+++ b/browser/config/tooltool-manifests/linux32/releng.manifest
@@ -11,19 +11,19 @@
 "size": 11189216,
 "digest": "18bc52b0599b1308b667e282abb45f47597bfc98a5140cfcab8da71dacf89dd76d0dee22a04ce26fe7ad1f04e2d6596991f9e5b01fd2aaaab5542965f596b0e6",
 "algorithm": "sha512",
 "filename": "gtk3.tar.xz",
 "setup": "setup.sh",
 "unpack": true
 },
 {
-"version": "gecko rustc 1.11.0 (9b21dcd6a 2016-08-15) x86_64+i586",
-"size": 99378568,
-"digest": "ea5ae0a37ab8c583ef3f9a97c45baf0644feed95f1e6191a4456fd42bbd45b218fe4bc528747a63af55ce67c4b6155bd50f312746628b30e41c421f4d54e5417",
+"version": "rustc 1.12.0 (3191fbae9 2016-09-23) repack x86_64+i586",
+"size": 102403884,
+"digest": "a7c1512d955d3030bcc1ebddfbf512f7b11b66e31634726deab78d0403fc0ceadd603d32b08c1a5025d3e9ee4ff48ddcf5eaba33468bb2161cfb9fb1a557affa",
 "algorithm": "sha512",
 "filename": "rustc.tar.xz",
 "unpack": true
 },
 {
 "version": "cargo 0.13.0-nightly (e713e7f 2016-08-31)",
 "size": 3245716,
 "digest": "d5bb0d88ce7bb1b5a316d7a8ca6341672f5ee8008fa7754511bf53fabd54c0770e95397232896d6087547891f1143f6968d8b1e106e39800b43defeb0025c7c0",
--- a/browser/config/tooltool-manifests/linux64/hazard.manifest
+++ b/browser/config/tooltool-manifests/linux64/hazard.manifest
@@ -19,19 +19,19 @@
 "digest" : "3915f8ec396c56a8a92e6f9695b70f09ce9d1582359d1258e37e3fd43a143bc974410e4cfc27f500e095f34a8956206e0ebf799b7287f0f38def0d5e34ed71c9",
 "unpack" : true,
 "setup" : "setup.sh",
 "algorithm" : "sha512",
 "filename" : "gtk3.tar.xz",
 "size" : 12072532
 },
 {
-"version": "rustc 1.11.0 (9b21dcd6a 2016-08-15) repack",
-"size": 131489924,
-"digest": "59f7463a0da38f324daa4ffc2678d78afb4fe0df13248c1d215bcb996ec05e8521155563cde9a8b719a9b98c5feeaf97cc9e8d52c9b95f6b44728870d908d5b6",
+"version": "rustc 1.12.0 (3191fbae9 2016-09-23) repack",
+"size": 102403884,
+"digest": "a7c1512d955d3030bcc1ebddfbf512f7b11b66e31634726deab78d0403fc0ceadd603d32b08c1a5025d3e9ee4ff48ddcf5eaba33468bb2161cfb9fb1a557affa",
 "algorithm": "sha512",
 "filename": "rustc.tar.xz",
 "unpack": true
 },
 {
 "filename" : "sccache.tar.bz2",
 "algorithm" : "sha512",
 "digest" : "0b71a936edf5bd70cf274aaa5d7abc8f77fe8e7b5593a208f805cc9436fac646b9c4f0b43c2b10de63ff3da671497d35536077ecbc72dba7f8159a38b580f831",
--- a/browser/config/tooltool-manifests/linux64/releng.manifest
+++ b/browser/config/tooltool-manifests/linux64/releng.manifest
@@ -11,19 +11,19 @@
 "size": 12072532,
 "digest": "3915f8ec396c56a8a92e6f9695b70f09ce9d1582359d1258e37e3fd43a143bc974410e4cfc27f500e095f34a8956206e0ebf799b7287f0f38def0d5e34ed71c9",
 "algorithm": "sha512",
 "filename": "gtk3.tar.xz",
 "setup": "setup.sh",
 "unpack": true
 },
 {
-"version": "rustc 1.11.0 (9b21dcd6a 2016-08-15) repack",
-"size": 131489924,
-"digest": "59f7463a0da38f324daa4ffc2678d78afb4fe0df13248c1d215bcb996ec05e8521155563cde9a8b719a9b98c5feeaf97cc9e8d52c9b95f6b44728870d908d5b6",
+"version": "rustc 1.12.0 (3191fbae9 2016-09-23) repack",
+"size": 102403884,
+"digest": "a7c1512d955d3030bcc1ebddfbf512f7b11b66e31634726deab78d0403fc0ceadd603d32b08c1a5025d3e9ee4ff48ddcf5eaba33468bb2161cfb9fb1a557affa",
 "algorithm": "sha512",
 "filename": "rustc.tar.xz",
 "unpack": true
 },
 {
 "version": "cargo 0.13.0-nightly (e713e7f 2016-08-31)",
 "size": 3245716,
 "digest": "d5bb0d88ce7bb1b5a316d7a8ca6341672f5ee8008fa7754511bf53fabd54c0770e95397232896d6087547891f1143f6968d8b1e106e39800b43defeb0025c7c0",
--- a/browser/config/tooltool-manifests/macosx64/cross-releng.manifest
+++ b/browser/config/tooltool-manifests/macosx64/cross-releng.manifest
@@ -50,16 +50,16 @@
 "size": 188880, 
 "visibility": "public", 
 "digest": "1ffddd43efb03aed897ee42035d9d8d758a8d66ab6c867599ef755e1a586768fc22011ce03698af61454920b00fe8bed08c9a681e7bd324d7f8f78c026c83943", 
 "algorithm": "sha512", 
 "unpack": true,
 "filename": "genisoimage.tar.xz"
 },
 {
-"version": "rustc 1.11.0 (9b21dcd6a 2016-08-15) repack",
-"size": 171059204,
-"digest": "7554ac993f55818827c80dab90135209e57db70c7c9131bef4309aff3b8d7452c4c0de663df7e8c46bd5702455c36292ade6c7a8007e567c4588c7f91aa88b57",
+"version": "rustc 1.12.0 (3191fbae9 2016-09-23) repack",
+"size": 179757256,
+"digest": "26bbd6a34beab36620634f7eafe63281e3398ae4673b3a4d49e1da4eae0467bc6efc2471ef842682db527ad137e702b74c68f278025c60c8bb69d244cff1e6b6",
 "algorithm": "sha512",
 "filename": "rustc.tar.xz",
 "unpack": true
 }
 ]
--- a/browser/config/tooltool-manifests/macosx64/releng.manifest
+++ b/browser/config/tooltool-manifests/macosx64/releng.manifest
@@ -3,19 +3,19 @@
 "version": "clang 3.8.0",
 "size": 133060926,
 "digest": "aff5ad3ac2d41db19d1ba0df5f97b189a7d7e1b6af8c56e22c2b0cced84d75fa98394ded6a4ba5713652e6684a0a46f47aeccf87991f9e849bf8d7d82e564f6f",
 "algorithm": "sha512",
 "filename": "clang.tar.bz2",
 "unpack": true
 },
 {
-"version": "rustc 1.11.0 (9b21dcd6a 2016-08-15) repack",
-"size": 146060042,
-"digest": "c7c5556af0dea1f97a737e4634496d407a5e0f7d14a7013746ad41ef188bab03be60cea59ed63d733dcb03bf11b05d8bf637dc0261f15cd5b0ab46d1199243cf",
+"version": "rustc 1.12.0 (3191fbae9 2016-09-23) repack",
+"size": 152905062,
+"digest": "1fb64d68ad41e5ca444a5a91f752efec154957d22bdf078adbc7b6a1cdbeefbadbc618de96cc46540a33849d43ac95a520a463d4f852e1a5a1f636d7079d969f",
 "algorithm": "sha512",
 "filename": "rustc.tar.bz2",
 "unpack": true
 },
 {
 "version": "cargo 0.13.0-nightly (e713e7f 2016-08-31)",
 "size": 2715131,
 "digest": "f037d2bbbeccb2c95519e083d6d9eecb5cb06a510e849b5721d6933a6c2428203b93ed3d20d3f20329f4d4eee17177d762f051b1ae79fee97d93b84611f3df66",
--- a/browser/config/tooltool-manifests/win32/clang.manifest
+++ b/browser/config/tooltool-manifests/win32/clang.manifest
@@ -1,19 +1,19 @@
 [
 {
 "size": 266240,
 "digest": "bb345b0e700ffab4d09436981f14b5de84da55a3f18a7f09ebc4364a4488acdeab8d46f447b12ac70f2da1444a68b8ce8b8675f0dae2ccf845e966d1df0f0869",
 "algorithm": "sha512",
 "filename": "mozmake.exe"
 },
 {
-"version": "rustc 1.11.0 (9b21dcd6a 2016-08-15) repack",
-"size": 86199150,
-"digest": "fec209dc85a098817c892655fbfda2bd6961199b1c28422994a50daddcb219608673b87dde30b3380555400cf4484863a12d431a6a25ef01cb9b1b32bef48f8b",
+"version": "rustc 1.12.0 (3191fbae9 2016-09-23) repack",
+"size": 89434100,
+"digest": "4da0efd2c36c77f29846f328d9f3c095a7b7e0dfd94f76b3159d441aae02b25007a475a979fb5bf313cf8fecb22fec81684871809effcb6b514419bc3854f398",
 "algorithm": "sha512",
 "filename": "rustc.tar.bz2",
 "unpack": true
 },
 {
 "size": 167175,
 "digest": "0b71a936edf5bd70cf274aaa5d7abc8f77fe8e7b5593a208f805cc9436fac646b9c4f0b43c2b10de63ff3da671497d35536077ecbc72dba7f8159a38b580f831",
 "algorithm": "sha512",
--- a/browser/config/tooltool-manifests/win32/releng.manifest
+++ b/browser/config/tooltool-manifests/win32/releng.manifest
@@ -1,19 +1,19 @@
 [
 {
 "size": 266240,
 "digest": "bb345b0e700ffab4d09436981f14b5de84da55a3f18a7f09ebc4364a4488acdeab8d46f447b12ac70f2da1444a68b8ce8b8675f0dae2ccf845e966d1df0f0869",
 "algorithm": "sha512",
 "filename": "mozmake.exe"
 },
 {
-"version": "rustc 1.11.0 (9b21dcd6a 2016-08-15) repack",
-"size": 86199150,
-"digest": "fec209dc85a098817c892655fbfda2bd6961199b1c28422994a50daddcb219608673b87dde30b3380555400cf4484863a12d431a6a25ef01cb9b1b32bef48f8b",
+"version": "rustc 1.12.0 (3191fbae9 2016-09-23) repack",
+"size": 89434100,
+"digest": "4da0efd2c36c77f29846f328d9f3c095a7b7e0dfd94f76b3159d441aae02b25007a475a979fb5bf313cf8fecb22fec81684871809effcb6b514419bc3854f398",
 "algorithm": "sha512",
 "filename": "rustc.tar.bz2",
 "unpack": true
 },
 {
 "version": "cargo 0.13.0-nightly (e713e7f 2016-08-31)",
 "size": 2402000,
 "digest": "56f12f7ac437742ed717ce0ccfb0b4134160948e45d73016e48d9033567e5b01a171ac95dd7965eb007702c31da73274b5913281655f461f611ddeee37181ecc",
--- a/browser/config/tooltool-manifests/win64/clang.manifest
+++ b/browser/config/tooltool-manifests/win64/clang.manifest
@@ -1,19 +1,19 @@
 [
 {
 "size": 266240,
 "digest": "bb345b0e700ffab4d09436981f14b5de84da55a3f18a7f09ebc4364a4488acdeab8d46f447b12ac70f2da1444a68b8ce8b8675f0dae2ccf845e966d1df0f0869",
 "algorithm": "sha512",
 "filename": "mozmake.exe"
 },
 {
-"version": "rustc 1.11.0 (9b21dcd6a 2016-08-15) repack",
-"size": 91329933,
-"digest": "db97f0186db432c57698e287798940abb5946c8903f990b087ea977fb938e83f2f9ca1bf90377bc575563af3144d429cc897a36750a1978a288a42b132c3d25d",
+"version": "rustc 1.12.0 (3191fbae9 2016-09-23) repack",
+"size": 94812923,
+"digest": "f8ff01a44caf38711c352e49c06e8ef6bbac7836bed1050bb043f89ba70f70a11c88001f453baec0cbc56a013efb0fd6b16d612923d07e29b5d8d4512dbaab07",
 "algorithm": "sha512",
 "visibility": "public",
 "filename": "rustc.tar.bz2",
 "unpack": true
 },
 {
 "size": 167175,
 "digest": "0b71a936edf5bd70cf274aaa5d7abc8f77fe8e7b5593a208f805cc9436fac646b9c4f0b43c2b10de63ff3da671497d35536077ecbc72dba7f8159a38b580f831",
--- a/browser/config/tooltool-manifests/win64/releng.manifest
+++ b/browser/config/tooltool-manifests/win64/releng.manifest
@@ -1,19 +1,19 @@
 [
 {
 "size": 266240,
 "digest": "bb345b0e700ffab4d09436981f14b5de84da55a3f18a7f09ebc4364a4488acdeab8d46f447b12ac70f2da1444a68b8ce8b8675f0dae2ccf845e966d1df0f0869",
 "algorithm": "sha512",
 "filename": "mozmake.exe"
 },
 {
-"version": "rustc 1.11.0 (9b21dcd6a 2016-08-15) repack",
-"size": 91329933,
-"digest": "db97f0186db432c57698e287798940abb5946c8903f990b087ea977fb938e83f2f9ca1bf90377bc575563af3144d429cc897a36750a1978a288a42b132c3d25d",
+"version": "rustc 1.12.0 (3191fbae9 2016-09-23) repack",
+"size": 94812923,
+"digest": "f8ff01a44caf38711c352e49c06e8ef6bbac7836bed1050bb043f89ba70f70a11c88001f453baec0cbc56a013efb0fd6b16d612923d07e29b5d8d4512dbaab07",
 "algorithm": "sha512",
 "visibility": "public",
 "filename": "rustc.tar.bz2",
 "unpack": true
 },
 {
 "version": "cargo 0.13.0-nightly (e713e7f 2016-08-31)",
 "size": 2677831,
--- a/browser/modules/ContentWebRTC.jsm
+++ b/browser/modules/ContentWebRTC.jsm
@@ -68,20 +68,20 @@ this.ContentWebRTC = {
         break;
       }
       case "webrtc:Allow": {
         let callID = aMessage.data.callID;
         let contentWindow = Services.wm.getOuterWindowWithId(aMessage.data.windowID);
         let devices = contentWindow.pendingGetUserMediaRequests.get(callID);
         forgetGUMRequest(contentWindow, callID);
 
-        let allowedDevices = Cc["@mozilla.org/supports-array;1"]
-                               .createInstance(Ci.nsISupportsArray);
+        let allowedDevices = Cc["@mozilla.org/array;1"]
+                               .createInstance(Ci.nsIMutableArray);
         for (let deviceIndex of aMessage.data.devices)
-           allowedDevices.AppendElement(devices[deviceIndex]);
+           allowedDevices.appendElement(devices[deviceIndex], /*weak =*/ false);
 
         Services.obs.notifyObservers(allowedDevices, "getUserMedia:response:allow", callID);
         break;
       }
       case "webrtc:Deny":
         denyGUMRequest(aMessage.data);
         break;
       case "webrtc:StopSharing":
@@ -256,18 +256,18 @@ function forgetPendingListsEventually(aC
     return;
   }
   aContentWindow.pendingGetUserMediaRequests = null;
   aContentWindow.pendingPeerConnectionRequests = null;
   aContentWindow.removeEventListener("unload", ContentWebRTC);
 }
 
 function updateIndicators() {
-  let contentWindowSupportsArray = MediaManagerService.activeMediaCaptureWindows;
-  let count = contentWindowSupportsArray.Count();
+  let contentWindowArray = MediaManagerService.activeMediaCaptureWindows;
+  let count = contentWindowArray.length;
 
   let state = {
     showGlobalIndicator: count > 0,
     showCameraIndicator: false,
     showMicrophoneIndicator: false,
     showScreenSharingIndicator: ""
   };
 
@@ -275,17 +275,17 @@ function updateIndicators() {
                .getService(Ci.nsIMessageSender);
   cpmm.sendAsyncMessage("webrtc:UpdatingIndicators");
 
   // If several iframes in the same page use media streams, it's possible to
   // have the same top level window several times. We use a Set to avoid
   // sending duplicate notifications.
   let contentWindows = new Set();
   for (let i = 0; i < count; ++i) {
-    contentWindows.add(contentWindowSupportsArray.GetElementAt(i).top);
+    contentWindows.add(contentWindowArray.queryElementAt(i, Ci.nsISupports).top);
   }
 
   for (let contentWindow of contentWindows) {
     let tabState = getTabStateForContentWindow(contentWindow);
     if (tabState.camera)
       state.showCameraIndicator = true;
     if (tabState.microphone)
       state.showMicrophoneIndicator = true;
--- a/browser/themes/osx/jar.mn
+++ b/browser/themes/osx/jar.mn
@@ -82,17 +82,17 @@ browser.jar:
 * skin/classic/browser/newtab/newTab.css                    (newtab/newTab.css)
   skin/classic/browser/setDesktopBackground.css
   skin/classic/browser/monitor.png
   skin/classic/browser/monitor_16-10.png
   skin/classic/browser/places/allBookmarks.png              (places/allBookmarks.png)
   skin/classic/browser/places/autocomplete-star.png         (places/autocomplete-star.png)
   skin/classic/browser/places/autocomplete-star@2x.png      (places/autocomplete-star@2x.png)
 * skin/classic/browser/places/places.css                    (places/places.css)
-* skin/classic/browser/places/organizer.css                 (places/organizer.css)
+  skin/classic/browser/places/organizer.css                 (places/organizer.css)
   skin/classic/browser/places/query.png                     (places/query.png)
   skin/classic/browser/places/query@2x.png                  (places/query@2x.png)
   skin/classic/browser/places/bookmarksMenu.png             (places/bookmarksMenu.png)
   skin/classic/browser/places/bookmarksToolbar.png          (places/bookmarksToolbar.png)
   skin/classic/browser/places/bookmarksToolbar@2x.png       (places/bookmarksToolbar@2x.png)
   skin/classic/browser/places/bookmarks-notification-finish.png  (places/bookmarks-notification-finish.png)
   skin/classic/browser/places/bookmarks-notification-finish@2x.png  (places/bookmarks-notification-finish@2x.png)
   skin/classic/browser/places/bookmarksToolbar-menuPanel.png    (places/bookmarksToolbar-menuPanel.png)
--- a/browser/themes/osx/places/organizer.css
+++ b/browser/themes/osx/places/organizer.css
@@ -1,50 +1,32 @@
 /* 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 ../shared.inc
-
 /* Places Organizer Sidebars */
 
 #placesList > treechildren::-moz-tree-row {
   background-color: transparent;
   border-color: transparent;
   padding-bottom: 1px;
   height: 24px;
 }
 
 #placesList > treechildren::-moz-tree-cell-text {
   font-size: 12px;
   margin-inline-end: 6px;
 }
 
 #placesList > treechildren::-moz-tree-row(selected) {  
-  background: @sidebarItemBackground@;
-  border-top: @sidebarItemBorderTop@;
-  border-bottom: @sidebarItemBorderBottom@;
+  -moz-appearance: -moz-mac-source-list-selection;
 }
 
 #placesList > treechildren::-moz-tree-row(selected,focus) {  
-  background: @sidebarItemFocusedBackground@;
-  border-top: @sidebarItemFocusedBorderTop@;
-  border-bottom: @sidebarItemFocusedBorderBottom@;
-}
-
-#placesList:-moz-system-metric(mac-graphite-theme) > treechildren::-moz-tree-row(selected) {
-  background: @sidebarItemGraphiteBackground@;
-  border-top: @sidebarItemGraphiteBorderTop@;
-  border-bottom: @sidebarItemGraphiteBorderBottom@;
-}
-
-#placesList:-moz-system-metric(mac-graphite-theme) > treechildren::-moz-tree-row(selected,focus) {
-  background: @sidebarItemGraphiteFocusedBackground@;
-  border-top: @sidebarItemGraphiteFocusedBorderTop@;
-  border-bottom: @sidebarItemGraphiteFocusedBorderBottom@;
+  -moz-appearance: -moz-mac-active-source-list-selection;
 }
 
 #placesList > treechildren::-moz-tree-row(History),
 #placesList > treechildren::-moz-tree-row(history)  {
   background-color: blue;
 }
 
 #placesList > treechildren::-moz-tree-cell(separator) {
@@ -75,40 +57,16 @@
   list-style-image: url("chrome://global/skin/tree/arrow-disclosure.svg#arrow-disclosure-expanded");
 }
 
 #placesList > treechildren::-moz-tree-twisty(open, selected) {
   list-style-image: url("chrome://global/skin/tree/arrow-disclosure.svg#arrow-disclosure-expanded-inverted");
 }
 
 @media (-moz-mac-yosemite-theme) {
-  #placesList > treechildren::-moz-tree-row(selected) {
-    background: @sidebarItemBackgroundYosemite@;
-    border-top: none;
-    border-bottom: none;
-  }
-
-  #placesList > treechildren::-moz-tree-row(selected,focus) {
-    background: @sidebarItemFocusedBackgroundYosemite@;
-    border-top: none;
-    border-bottom: none;
-  }
-
-  #placesList > treechildren:-moz-system-metric(mac-graphite-theme)::-moz-tree-row(selected) {
-    background: @sidebarItemGraphiteBackgroundYosemite@;
-    border-top: none;
-    border-bottom: none;
-  }
-
-  #placesList > treechildren:-moz-system-metric(mac-graphite-theme)::-moz-tree-row(selected,focus) {
-    background: @sidebarItemGraphiteFocusedBackgroundYosemite@;
-    border-top: none;
-    border-bottom: none;
-  }
-
   #placesList > treechildren::-moz-tree-cell-text(selected) {
     color: -moz-dialogtext;
     font-weight: 500;
   }
 
   #placesList > treechildren::-moz-tree-cell-text(selected, focus) {
     color: #fff;
   }
--- a/browser/themes/osx/places/places.css
+++ b/browser/themes/osx/places/places.css
@@ -12,57 +12,45 @@
 #tabs-panel {
   -moz-appearance: none !important;
   background-color: transparent !important;
   border-top: none !important;
 }
 
 .sidebar-placesTree,
 .sidebar-placesTreechildren::-moz-tree-row {
-  background-color: transparent;
-  border-color: transparent;
   padding-bottom: 1px;
-  -moz-appearance: none;
   margin: 0;
   height: 24px;
-  border: none;
   font-size: 12px;
 }
 
+.sidebar-placesTree {
+  -moz-appearance: -moz-mac-source-list;
+}
+
+.sidebar-placesTreechildren {
+  border-top: 1px solid #bebebe;
+}
+
 .sidebar-placesTreechildren::-moz-tree-separator {
   border-top: 1px solid #505d6d;
   margin: 0 10px;
 }
 
-.sidebar-placesTree {
-  border-top: 1px solid #bebebe;
+.sidebar-placesTreechildren::-moz-tree-row {
+  background-color: transparent;
 }
 
 .sidebar-placesTreechildren::-moz-tree-row(selected) {
-  background: @sidebarItemBackground@;
-  border-top: @sidebarItemBorderTop@;
-  border-bottom: @sidebarItemBorderBottom@;
+  -moz-appearance: -moz-mac-source-list-selection;
 }
 
 .sidebar-placesTreechildren::-moz-tree-row(selected,focus) {
-  background: @sidebarItemFocusedBackground@;
-  border-top: @sidebarItemFocusedBorderTop@;
-  border-bottom: @sidebarItemFocusedBorderBottom@;
-}
-
-.sidebar-placesTreechildren:-moz-system-metric(mac-graphite-theme)::-moz-tree-row(selected) {
-  background: @sidebarItemGraphiteBackground@;
-  border-top: @sidebarItemGraphiteBorderTop@;
-  border-bottom: @sidebarItemGraphiteBorderBottom@;
-}
-
-.sidebar-placesTreechildren:-moz-system-metric(mac-graphite-theme)::-moz-tree-row(selected,focus) {
-  background: @sidebarItemGraphiteFocusedBackground@;
-  border-top: @sidebarItemGraphiteFocusedBorderTop@;
-  border-bottom: @sidebarItemGraphiteFocusedBorderBottom@;
+  -moz-appearance: -moz-mac-active-source-list-selection;
 }
 
 .sidebar-placesTreechildren::-moz-tree-cell-text {
   margin-inline-end: 6px;
 }
 
 .sidebar-placesTreechildren::-moz-tree-cell-text(selected) {
   color: #fff;
@@ -87,40 +75,16 @@
   list-style-image: url("chrome://global/skin/tree/arrow-disclosure.svg#arrow-disclosure-expanded");
 }
 
 .sidebar-placesTreechildren::-moz-tree-twisty(open, selected) {
   list-style-image: url("chrome://global/skin/tree/arrow-disclosure.svg#arrow-disclosure-expanded-inverted");
 }
 
 @media (-moz-mac-yosemite-theme) {
-  .sidebar-placesTreechildren::-moz-tree-row(selected) {
-    background: @sidebarItemBackgroundYosemite@;
-    border-top: none;
-    border-bottom: none;
-  }
-
-  .sidebar-placesTreechildren::-moz-tree-row(selected,focus) {
-    background: @sidebarItemFocusedBackgroundYosemite@;
-    border-top: none;
-    border-bottom: none;
-  }
-
-  .sidebar-placesTreechildren:-moz-system-metric(mac-graphite-theme)::-moz-tree-row(selected) {
-    background: @sidebarItemGraphiteBackgroundYosemite@;
-    border-top: none;
-    border-bottom: none;
-  }
-
-  .sidebar-placesTreechildren:-moz-system-metric(mac-graphite-theme)::-moz-tree-row(selected,focus) {
-    background: @sidebarItemGraphiteFocusedBackgroundYosemite@;
-    border-top: none;
-    border-bottom: none;
-  }
-
   .sidebar-placesTreechildren::-moz-tree-cell-text(selected) {
     color: -moz-dialogtext;
     font-weight: 500;
   }
 
   .sidebar-placesTreechildren::-moz-tree-cell-text(selected, focus) {
     color: #fff;
   }
--- a/browser/themes/osx/syncedtabs/sidebar.css
+++ b/browser/themes/osx/syncedtabs/sidebar.css
@@ -1,18 +1,18 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-%include ../shared.inc
 %include ../../shared/syncedtabs/sidebar.inc.css
 
 /* These styles are intended to mimic XUL trees and the XUL search box. */
 
-html {
+.content-container {
+  -moz-appearance: -moz-mac-source-list;
 }
 
 .item {
   color: -moz-DialogText;
 }
 
 .item-title-container {
   box-sizing: border-box;
@@ -22,25 +22,21 @@ html {
 }
 
 .item.selected > .item-title-container {
   color: HighlightText;
   font-weight: bold;
 }
 
 .item.selected > .item-title-container {
-  background: @sidebarItemBackground@;
-  border-top: @sidebarItemBorderTop@;
-  border-bottom: @sidebarItemBorderBottom@;
+  -moz-appearance: -moz-mac-source-list-selection;
 }
 
 .item.selected:focus > .item-title-container {
-  background: @sidebarItemFocusedBackground@;
-  border-top: @sidebarItemFocusedBorderTop@;
-  border-bottom: @sidebarItemFocusedBorderBottom@;
+  -moz-appearance: -moz-mac-active-source-list-selection;
 }
 
 .item.client .item-twisty-container {
   min-width: 16px;
   height: 16px;
   background-image: url("chrome://global/skin/tree/arrow-disclosure.svg#arrow-disclosure-expanded");
 }
 
@@ -53,58 +49,25 @@ html {
 }
 
 .item.client.selected.closed:focus .item-twisty-container {
   background-image: url("chrome://global/skin/tree/arrow-disclosure.svg#arrow-disclosure-collapsed-inverted");
 }
 
 @media (-moz-mac-yosemite-theme) {
   .item.selected > .item-title-container {
-    font-weight: 500;
-  }
-
-  .item.selected > .item-title-container {
-    background-image: none;
-    background-color: @sidebarItemBackgroundYosemite@;
-    border-top: none;
-    border-bottom: none;
-  }
-
-  .item.selected:focus > .item-title-container {
-    background-image: none;
-    background-color: @sidebarItemFocusedBackgroundYosemite@;
-    border-top: none;
-    border-bottom: none;
-  }
-
-  .item.selected:-moz-system-metric(mac-graphite-theme) > .item-title-container {
-    background-image: none;
-    background-color: @sidebarItemGraphiteBackgroundYosemite@;
-    border-top: none;
-    border-bottom: none;
-  }
-
-  .item.selected:focus:-moz-system-metric(mac-graphite-theme) > .item-title-container {
-    background-image: none;
-    background-color: @sidebarItemGraphiteFocusedBackgroundYosemite@;
-    border-top: none;
-    border-bottom: none;
-  }
-
-  .item.selected > .item-title-container {
     color: -moz-dialogtext;
     font-weight: 500;
   }
 
   .item.selected:focus > .item-title-container {
     color: #fff;
   }
 }
 
-
 .sidebar-search-container {
   border-bottom: 1px solid #bdbdbd;
 }
 
 .search-box {
   -moz-appearance: searchfield;
   padding: 1px;
   font-size: 12px;
--- a/config/expandlibs_gen.py
+++ b/config/expandlibs_gen.py
@@ -16,21 +16,16 @@ def generate(args):
     desc = LibDescriptor()
     for arg in args:
         if isObject(arg):
             if os.path.exists(arg):
                 desc['OBJS'].append(os.path.abspath(arg))
             else:
                 raise Exception("File not found: %s" % arg)
         elif os.path.splitext(arg)[1] == conf.LIB_SUFFIX:
-            # We want to skip static libraries with the name foo-rs-prelink
-            # as they are individually linked for every final library, and
-            # thus should not be included in the descriptor file
-            if '-rs-prelink' in os.path.basename(arg):
-                continue
             if os.path.exists(arg) or os.path.exists(arg + conf.LIBS_DESC_SUFFIX):
                 desc['LIBS'].append(os.path.abspath(arg))
             else:
                 raise Exception("File not found: %s" % arg)
     return desc
 
 if __name__ == '__main__':
     parser = OptionParser()
--- a/config/rules.mk
+++ b/config/rules.mk
@@ -791,29 +791,29 @@ endif
 endif
 endif
 
 # On Darwin (Mac OS X), dwarf2 debugging uses debug info left in .o files,
 # so instead of deleting .o files after repacking them into a dylib, we make
 # symlinks back to the originals. The symlinks are a no-op for stabs debugging,
 # so no need to conditionalize on OS version or debugging format.
 
-$(SHARED_LIBRARY): $(OBJS) $(RESFILE) $(STATIC_LIBS_DEPS) $(EXTRA_DEPS) $(GLOBAL_DEPS)
+$(SHARED_LIBRARY): $(OBJS) $(RESFILE) $(RUST_STATIC_LIB_FOR_SHARED_LIB) $(STATIC_LIBS_DEPS) $(EXTRA_DEPS) $(GLOBAL_DEPS)
 	$(REPORT_BUILD)
 ifndef INCREMENTAL_LINKER
 	$(RM) $@
 endif
 ifdef DTRACE_LIB_DEPENDENT
 ifndef XP_MACOSX
 	dtrace -x nolibs -G -C -s $(MOZILLA_DTRACE_SRC) -o  $(DTRACE_PROBE_OBJ) $(shell $(EXPAND_LIBS) $(MOZILLA_PROBE_LIBS))
 endif
-	$(EXPAND_MKSHLIB) $(SHLIB_LDSTARTFILE) $(OBJS) $(SUB_SHLOBJS) $(DTRACE_PROBE_OBJ) $(MOZILLA_PROBE_LIBS) $(RESFILE) $(LDFLAGS) $(WRAP_LDFLAGS) $(STATIC_LIBS) $(SHARED_LIBS) $(EXTRA_DSO_LDOPTS) $(MOZ_GLUE_LDFLAGS) $(EXTRA_LIBS) $(OS_LIBS) $(SHLIB_LDENDFILE)
+	$(EXPAND_MKSHLIB) $(SHLIB_LDSTARTFILE) $(OBJS) $(SUB_SHLOBJS) $(DTRACE_PROBE_OBJ) $(MOZILLA_PROBE_LIBS) $(RESFILE) $(LDFLAGS) $(WRAP_LDFLAGS) $(STATIC_LIBS) $(RUST_STATIC_LIB_FOR_SHARED_LIB) $(SHARED_LIBS) $(EXTRA_DSO_LDOPTS) $(MOZ_GLUE_LDFLAGS) $(EXTRA_LIBS) $(OS_LIBS) $(SHLIB_LDENDFILE)
 	@$(RM) $(DTRACE_PROBE_OBJ)
 else # ! DTRACE_LIB_DEPENDENT
-	$(EXPAND_MKSHLIB) $(SHLIB_LDSTARTFILE) $(OBJS) $(SUB_SHLOBJS) $(RESFILE) $(LDFLAGS) $(WRAP_LDFLAGS) $(STATIC_LIBS) $(SHARED_LIBS) $(EXTRA_DSO_LDOPTS) $(MOZ_GLUE_LDFLAGS) $(EXTRA_LIBS) $(OS_LIBS) $(SHLIB_LDENDFILE)
+	$(EXPAND_MKSHLIB) $(SHLIB_LDSTARTFILE) $(OBJS) $(SUB_SHLOBJS) $(RESFILE) $(LDFLAGS) $(WRAP_LDFLAGS) $(STATIC_LIBS) $(RUST_STATIC_LIB_FOR_SHARED_LIB) $(SHARED_LIBS) $(EXTRA_DSO_LDOPTS) $(MOZ_GLUE_LDFLAGS) $(EXTRA_LIBS) $(OS_LIBS) $(SHLIB_LDENDFILE)
 endif # DTRACE_LIB_DEPENDENT
 	$(call CHECK_BINARY,$@)
 
 ifeq (_WINNT,$(GNU_CC)_$(OS_ARCH))
 ifdef MSMANIFEST_TOOL
 ifdef EMBED_MANIFEST_AT
 	@if test -f $@.manifest; then \
 		$(MT) -NOLOGO -MANIFEST $@.manifest -OUTPUTRESOURCE:$@\;$(EMBED_MANIFEST_AT); \
@@ -893,17 +893,17 @@ ifdef ASFILES
 # The AS_DASH_C_FLAG is needed cause not all assemblers (Solaris) accept
 # a '-c' flag.
 $(ASOBJS):
 	$(REPORT_BUILD_VERBOSE)
 	$(AS) $(ASOUTOPTION)$@ $(ASFLAGS) $($(notdir $<)_FLAGS) $(AS_DASH_C_FLAG) $(_VPATH_SRCS)
 endif
 
 ifdef MOZ_RUST
-ifdef CARGO_FILE
+ifdef RUST_LIBRARY_FILE
 
 ifdef MOZ_DEBUG
 cargo_build_flags =
 else
 cargo_build_flags = --release
 endif
 ifdef MOZ_CARGO_SUPPORTS_FROZEN
 cargo_build_flags += --frozen
@@ -922,40 +922,16 @@ cargo_build_flags += --verbose
 # XXX: We're passing `-C debuginfo=1` to rustc to work around an llvm-dsymutil
 # crash (bug 1301751). This should be temporary until we upgrade to Rust 1.12.
 force-cargo-build:
 	$(REPORT_BUILD)
 	env CARGO_TARGET_DIR=. RUSTC=$(RUSTC) RUSTFLAGS='-C debuginfo=1' $(CARGO) build $(cargo_build_flags) --
 
 $(RUST_LIBRARY_FILE): force-cargo-build
 endif # CARGO_FILE
-
-ifdef RUST_PRELINK
-# Make target for building a prelinked rust library. This merges rust .rlibs
-# together into a single .a file which is used within the FINAL_LIBRARY.
-#
-# RUST_PRELINK_FLAGS, RUST_PRELINK_SRC, and RUST_PRELINK_DEPS are set in
-# recursivemake.py, and together tell rustc how to find the libraries to link
-# together, but we compute the optimization flags below
-
-RUST_PRELINK_FLAGS += -g
-RUST_PRELINK_FLAGS += -C panic=abort
-
-ifdef MOZ_DEBUG
-RUST_PRELINK_FLAGS += -C opt-level=1
-RUST_PRELINK_FLAGS += -C debug-assertions
-else
-RUST_PRELINK_FLAGS += -C opt-level=2
-RUST_PRELINK_FLAGS += -C lto
-endif
-
-$(RUST_PRELINK): $(RUST_PRELINK_DEPS) $(RUST_PRELINK_SRC)
-	$(REPORT_BUILD)
-	$(RUSTC) -o $@ --crate-type staticlib --target $(RUST_TARGET) $(RUST_PRELINK_FLAGS) $(RUST_PRELINK_SRC)
-endif # RUST_PRELINK
 endif # MOZ_RUST
 
 $(SOBJS):
 	$(REPORT_BUILD)
 	$(AS) -o $@ $(DEFINES) $(ASFLAGS) $($(notdir $<)_FLAGS) $(LOCAL_INCLUDES) -c $<
 
 $(CPPOBJS):
 	$(REPORT_BUILD_VERBOSE)
--- a/docshell/base/LoadContext.cpp
+++ b/docshell/base/LoadContext.cpp
@@ -94,25 +94,16 @@ NS_IMETHODIMP
 LoadContext::GetNestedFrameId(uint64_t* aId)
 {
   NS_ENSURE_ARG(aId);
   *aId = mNestedFrameId;
   return NS_OK;
 }
 
 NS_IMETHODIMP
-LoadContext::IsAppOfType(uint32_t, bool*)
-{
-  MOZ_ASSERT(mIsNotNull);
-
-  // don't expect we need this in parent (Thunderbird/SeaMonkey specific?)
-  return NS_ERROR_UNEXPECTED;
-}
-
-NS_IMETHODIMP
 LoadContext::GetIsContent(bool* aIsContent)
 {
   MOZ_ASSERT(mIsNotNull);
 
   NS_ENSURE_ARG_POINTER(aIsContent);
 
   *aIsContent = mIsContent;
   return NS_OK;
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -9733,16 +9733,30 @@ nsDocShell::InternalLoad(nsIURI* aURI,
                          nsISHEntry* aSHEntry,
                          bool aFirstParty,
                          const nsAString& aSrcdoc,
                          nsIDocShell* aSourceDocShell,
                          nsIURI* aBaseURI,
                          nsIDocShell** aDocShell,
                          nsIRequest** aRequest)
 {
+  // In most cases both principals (aTriggeringPrincipal and aPrincipalToInherit)
+  // are both null or both non-null. For the exceptional cases let's make sure that:
+  // * if aTriggeringPrincipal is null then either aPrincipalToInherit is null or
+  //   it's a NullPrincipal
+  // * if aPrincipalToInherit is null then either aTriggeringPrincipal is null or
+  //   it's a NullPrincipal or INTERNAL_LOAD_FLAGS_INHERIT_PRINCIPAL is set.
+  MOZ_ASSERT(aTriggeringPrincipal ||
+             (!aPrincipalToInherit ||
+              aPrincipalToInherit->GetIsNullPrincipal()));
+  MOZ_ASSERT(aPrincipalToInherit ||
+             (!aTriggeringPrincipal ||
+              aTriggeringPrincipal->GetIsNullPrincipal() ||
+              (aFlags & INTERNAL_LOAD_FLAGS_INHERIT_PRINCIPAL)));
+
   nsresult rv = NS_OK;
   mOriginalUriString.Truncate();
 
   if (gDocShellLeakLog && MOZ_LOG_TEST(gDocShellLeakLog, LogLevel::Debug)) {
     PR_LogPrint("DOCSHELL %p InternalLoad %s\n",
                 this, aURI ? aURI->GetSpecOrDefault().get() : "");
   }
   // Initialize aDocShell/aRequest
@@ -9924,22 +9938,36 @@ nsDocShell::InternalLoad(nsIURI* aURI,
   // done by someone from chrome manually messing with our nsIWebNavigation
   // or by C++ setting document.location) don't get a funky principal.  If
   // callers want something interesting to happen with the about:blank
   // principal in this case, they should pass aPrincipalToInherit in.
   //
   {
     bool inherits;
     // One more twist: Don't inherit the principal for external loads.
-    if (aLoadType != LOAD_NORMAL_EXTERNAL && !principalToInherit &&
-        (aFlags & INTERNAL_LOAD_FLAGS_INHERIT_PRINCIPAL) &&
+    if (!principalToInherit && 
         NS_SUCCEEDED(nsContentUtils::URIInheritsSecurityContext(aURI,
                                                                 &inherits)) &&
         inherits) {
-      principalToInherit = GetInheritedPrincipal(true);
+      if (aLoadType != LOAD_NORMAL_EXTERNAL && 
+          (aFlags & INTERNAL_LOAD_FLAGS_INHERIT_PRINCIPAL)) {
+        principalToInherit = GetInheritedPrincipal(true);
+      }
+
+      // In case we don't have a principalToInherit and the TriggeringPrincipal
+      // either already is a SystemPrincipal or would fall back to become
+      // a SystemPrincipal within the loadInfo then we should explicitly set
+      // the principalToInherit to a freshly created NullPrincipal.
+      if (!principalToInherit && 
+          (nsContentUtils::IsSystemPrincipal(aTriggeringPrincipal) ||
+           (!aTriggeringPrincipal && !aReferrer))) {
+        // We're going to default to inheriting our system triggering principal, 
+        // more or less by accident.  This doesn't seem like a good idea.
+        principalToInherit = nsNullPrincipal::CreateWithInheritedAttributes(this);
+      }
     }
   }
 
   // Don't allow loads that would inherit our security context
   // if this document came from an unsafe channel.
   {
     bool willInherit;
     // This condition needs to match the one in
@@ -12295,17 +12323,17 @@ nsDocShell::AddToSessionHistory(nsIURI* 
           } else {
             // get the OriginAttributes
             NeckoOriginAttributes nAttrs;
             loadInfo->GetOriginAttributes(&nAttrs);
             PrincipalOriginAttributes pAttrs;
             pAttrs.InheritFromNecko(nAttrs);
             principalToInherit = nsNullPrincipal::Create(pAttrs);
           }
-        } else if (loadInfo->GetForceInheritPrincipal()) {
+        } else {
           principalToInherit = loadInfo->PrincipalToInherit();
         }
       }
     }
   }
 
   // Title is set in nsDocShell::SetTitle()
   entry->Create(aURI,                // uri
@@ -13552,34 +13580,16 @@ nsDocShell::GetTopFrameElement(nsIDOMEle
 NS_IMETHODIMP
 nsDocShell::GetNestedFrameId(uint64_t* aId)
 {
   *aId = 0;
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsDocShell::IsAppOfType(uint32_t aAppType, bool* aIsOfType)
-{
-  RefPtr<nsDocShell> shell = this;
-  while (shell) {
-    uint32_t type;
-    shell->GetAppType(&type);
-    if (type == aAppType) {
-      *aIsOfType = true;
-      return NS_OK;
-    }
-    shell = shell->GetParentDocshell();
-  }
-
-  *aIsOfType = false;
-  return NS_OK;
-}
-
-NS_IMETHODIMP
 nsDocShell::IsTrackingProtectionOn(bool* aIsTrackingProtectionOn)
 {
   if (Preferences::GetBool("privacy.trackingprotection.enabled", false)) {
     *aIsTrackingProtectionOn = true;
   } else if (UsePrivateBrowsing() &&
              Preferences::GetBool("privacy.trackingprotection.pbmode.enabled", false)) {
     *aIsTrackingProtectionOn = true;
   } else {
--- a/docshell/base/nsDocShell.h
+++ b/docshell/base/nsDocShell.h
@@ -219,17 +219,16 @@ public:
     nsDocShellInfoLoadType aDocShellLoadType);
 
   // Don't use NS_DECL_NSILOADCONTEXT because some of nsILoadContext's methods
   // are shared with nsIDocShell (appID, etc.) and can't be declared twice.
   NS_IMETHOD GetAssociatedWindow(mozIDOMWindowProxy**) override;
   NS_IMETHOD GetTopWindow(mozIDOMWindowProxy**) override;
   NS_IMETHOD GetTopFrameElement(nsIDOMElement**) override;
   NS_IMETHOD GetNestedFrameId(uint64_t*) override;
-  NS_IMETHOD IsAppOfType(uint32_t, bool*) override;
   NS_IMETHOD GetIsContent(bool*) override;
   NS_IMETHOD GetUsePrivateBrowsing(bool*) override;
   NS_IMETHOD SetUsePrivateBrowsing(bool) override;
   NS_IMETHOD SetPrivateBrowsing(bool) override;
   NS_IMETHOD GetUseRemoteTabs(bool*) override;
   NS_IMETHOD SetRemoteTabs(bool) override;
   NS_IMETHOD GetOriginAttributes(JS::MutableHandle<JS::Value>) override;
   NS_IMETHOD IsTrackingProtectionOn(bool*) override;
@@ -291,19 +290,20 @@ private:
   // It is necessary to allow adding a timeline marker wherever a docshell
   // instance is available. This operation happens frequently and needs to
   // be very fast, so instead of using a Map or having to search for some
   // docshell-specific markers storage, a pointer to an `ObservedDocShell` is
   // is stored on docshells directly.
   friend void mozilla::TimelineConsumers::AddConsumer(nsDocShell*);
   friend void mozilla::TimelineConsumers::RemoveConsumer(nsDocShell*);
   friend void mozilla::TimelineConsumers::AddMarkerForDocShell(
-    nsDocShell*, const char*, MarkerTracingType);
+    nsDocShell*, const char*, MarkerTracingType, MarkerStackRequest);
   friend void mozilla::TimelineConsumers::AddMarkerForDocShell(
-    nsDocShell*, const char*, const TimeStamp&, MarkerTracingType);
+    nsDocShell*, const char*, const TimeStamp&, MarkerTracingType,
+    MarkerStackRequest);
   friend void mozilla::TimelineConsumers::AddMarkerForDocShell(
     nsDocShell*, UniquePtr<AbstractTimelineMarker>&&);
   friend void mozilla::TimelineConsumers::PopMarkers(nsDocShell*,
     JSContext*, nsTArray<dom::ProfileTimelineMarker>&);
 
 public:
   // Tell the favicon service that aNewURI has the same favicon as aOldURI.
   static void CopyFavicon(nsIURI* aOldURI,
--- a/docshell/base/nsIDocShell.idl
+++ b/docshell/base/nsIDocShell.idl
@@ -127,18 +127,45 @@ interface nsIDocShell : nsIDocShellTreeI
    * @param aOriginalURI         - The URI to set as the originalURI on the channel
    *                               that does the load. If null, aURI will be set as
    *                               the originalURI.
    * @param aLoadReplace         - If set LOAD_REPLACE flag will be set on the
    *                               channel. aOriginalURI is null, this argument is
    *                               ignored.
    * @param aReferrer            - Referring URI
    * @param aReferrerPolicy      - Referrer policy
-   * @param aTriggeringPrincipal - Principal that initiated that load
-   * @param aPrincipalToInherit  - Principal to be inherited for that load
+   * @param aTriggeringPrincipal - Principal that initiated that load. If passing
+   *                               null for this argument, then internally a
+   *                               principal is created from aReferrer. If
+   *                               aReferrer is also null, then the
+   *                               triggeringPrincipal defaults to the
+   *                               SystemPrincipal. Please note that this is the
+   *                               principal that is used for security checks. If
+   *                               the argument aURI is provided by the web, then
+   *                               please pass an explicit triggeringPrincipal to
+   *                               avoid the fallback to SystemPrincipal and
+   *                               hence a potential security risk.
+   *                               If aTriggeringPrincipal is null then either
+   *                               aPrincipalToInherit is null or it's
+   *                               a NullPrincipal.
+   * @param aPrincipalToInherit  - Principal to be inherited for that load. If
+   *                               passing null for this argument, then internally
+   *                               the triggeringPrincipal is also used for the
+   *                               principalToInherit. There are cases where those
+   *                               two principals need to be different though.
+   *                               E.g. the system might initiate a load for
+   *                               'about:blank', hence SystemPrincipal is passed
+   *                               for aTriggeringPrincipal. But the principal to
+   *                               be inherited for that load should be a
+   *                               NullPrincipal and not the SystemPrincipal.
+   *                               In that case, please pass a non null
+   *                               principalToInherit.
+   *                               If aPrincipalToInherit is null then either
+   *                               aTriggeringPrincipal is null or
+   *                               INTERNAL_LOAD_FLAGS_INHERIT_PRINCIPAL is set.
    * @param aFlags               - Any of the load flags defined within above.
    * @param aStopActiveDoc       - Flag indicating whether loading the current
    *                               document should be stopped.
    * @param aWindowTarget        - Window target for the load.
    * @param aTypeHint            - A hint as to the content-type of the resulting
    *                               data.  May be null or empty if no hint.
    * @param aFileName            - Non-null when the link should be downloaded as
                                    the given filename.
--- a/docshell/base/nsILoadContext.idl
+++ b/docshell/base/nsILoadContext.idl
@@ -56,27 +56,16 @@ interface nsILoadContext : nsISupports
    * If this LoadContext corresponds to a nested remote iframe, we don't have
    * access to the topFrameElement.  Instead, we must use this id to send
    * messages. A return value of 0 signifies that this load context is not for
    * a nested frame.
    */
   readonly attribute unsigned long long nestedFrameId;
 
   /**
-   * Check whether the load is happening in a particular type of application.
-   *
-   * @param an application type.  For now, the constants to be passed here are
-   *        the nsIDocShell APP_TYPE_* constants.
-   *
-   * @return whether there is some ancestor of the associatedWindow that is of
-   *         the given app type.
-   */
-  boolean isAppOfType(in unsigned long appType);
-
-  /**
    * True if the load context is content (as opposed to chrome).  This is
    * determined based on the type of window the load is performed in, NOT based
    * on any URIs that might be around.
    */
   readonly attribute boolean isContent;
 
   /*
    * Attribute that determines if private browsing should be used.
--- a/docshell/base/timeline/TimelineConsumers.cpp
+++ b/docshell/base/timeline/TimelineConsumers.cpp
@@ -168,63 +168,67 @@ TimelineConsumers::IsEmpty()
 {
   StaticMutexAutoLock lock(sMutex); // for `mActiveConsumers`.
   return mActiveConsumers == 0;
 }
 
 void
 TimelineConsumers::AddMarkerForDocShell(nsDocShell* aDocShell,
                                         const char* aName,
-                                        MarkerTracingType aTracingType)
+                                        MarkerTracingType aTracingType,
+                                        MarkerStackRequest aStackRequest)
 {
   MOZ_ASSERT(NS_IsMainThread());
   if (HasConsumer(aDocShell)) {
-    aDocShell->mObserved->AddMarker(Move(MakeUnique<TimelineMarker>(aName, aTracingType)));
+    aDocShell->mObserved->AddMarker(Move(MakeUnique<TimelineMarker>(aName, aTracingType, aStackRequest)));
   }
 }
 
 void
 TimelineConsumers::AddMarkerForDocShell(nsDocShell* aDocShell,
                                         const char* aName,
                                         const TimeStamp& aTime,
-                                        MarkerTracingType aTracingType)
+                                        MarkerTracingType aTracingType,
+                                        MarkerStackRequest aStackRequest)
 {
   MOZ_ASSERT(NS_IsMainThread());
   if (HasConsumer(aDocShell)) {
-    aDocShell->mObserved->AddMarker(Move(MakeUnique<TimelineMarker>(aName, aTime, aTracingType)));
+    aDocShell->mObserved->AddMarker(Move(MakeUnique<TimelineMarker>(aName, aTime, aTracingType, aStackRequest)));
   }
 }
 
 void
 TimelineConsumers::AddMarkerForDocShell(nsDocShell* aDocShell,
                                         UniquePtr<AbstractTimelineMarker>&& aMarker)
 {
   MOZ_ASSERT(NS_IsMainThread());
   if (HasConsumer(aDocShell)) {
     aDocShell->mObserved->AddMarker(Move(aMarker));
   }
 }
 
 void
 TimelineConsumers::AddMarkerForDocShell(nsIDocShell* aDocShell,
                                         const char* aName,
-                                        MarkerTracingType aTracingType)
+                                        MarkerTracingType aTracingType,
+                                        MarkerStackRequest aStackRequest)
 {
   MOZ_ASSERT(NS_IsMainThread());
-  AddMarkerForDocShell(static_cast<nsDocShell*>(aDocShell), aName, aTracingType);
+  AddMarkerForDocShell(static_cast<nsDocShell*>(aDocShell), aName, aTracingType, aStackRequest);
 }
 
 void
 TimelineConsumers::AddMarkerForDocShell(nsIDocShell* aDocShell,
                                         const char* aName,
                                         const TimeStamp& aTime,
-                                        MarkerTracingType aTracingType)
+                                        MarkerTracingType aTracingType,
+                                        MarkerStackRequest aStackRequest)
 {
   MOZ_ASSERT(NS_IsMainThread());
-  AddMarkerForDocShell(static_cast<nsDocShell*>(aDocShell), aName, aTime, aTracingType);
+  AddMarkerForDocShell(static_cast<nsDocShell*>(aDocShell), aName, aTime, aTracingType, aStackRequest);
 }
 
 void
 TimelineConsumers::AddMarkerForDocShell(nsIDocShell* aDocShell,
                                         UniquePtr<AbstractTimelineMarker>&& aMarker)
 {
   MOZ_ASSERT(NS_IsMainThread());
   AddMarkerForDocShell(static_cast<nsDocShell*>(aDocShell), Move(aMarker));
--- a/docshell/base/timeline/TimelineConsumers.h
+++ b/docshell/base/timeline/TimelineConsumers.h
@@ -66,29 +66,33 @@ public:
   // created unless that docshell is specifically being currently observed.
   // See nsIDocShell::recordProfileTimelineMarkers
 
   // These methods create a basic TimelineMarker from a name and some metadata,
   // relevant for a specific docshell.
   // Main thread only.
   void AddMarkerForDocShell(nsDocShell* aDocShell,
                             const char* aName,
-                            MarkerTracingType aTracingType);
+                            MarkerTracingType aTracingType,
+                            MarkerStackRequest aStackRequest = MarkerStackRequest::STACK);
   void AddMarkerForDocShell(nsIDocShell* aDocShell,
                             const char* aName,
-                            MarkerTracingType aTracingType);
+                            MarkerTracingType aTracingType,
+                            MarkerStackRequest aStackRequest = MarkerStackRequest::STACK);
 
   void AddMarkerForDocShell(nsDocShell* aDocShell,
                             const char* aName,
                             const TimeStamp& aTime,
-                            MarkerTracingType aTracingType);
+                            MarkerTracingType aTracingType,
+                            MarkerStackRequest aStackRequest = MarkerStackRequest::STACK);
   void AddMarkerForDocShell(nsIDocShell* aDocShell,
                             const char* aName,
                             const TimeStamp& aTime,
-                            MarkerTracingType aTracingType);
+                            MarkerTracingType aTracingType,
+                            MarkerStackRequest aStackRequest = MarkerStackRequest::STACK);
 
   // These methods register and receive ownership of an already created marker,
   // relevant for a specific docshell.
   // Main thread only.
   void AddMarkerForDocShell(nsDocShell* aDocShell,
                             UniquePtr<AbstractTimelineMarker>&& aMarker);
   void AddMarkerForDocShell(nsIDocShell* aDocShell,
                             UniquePtr<AbstractTimelineMarker>&& aMarker);
--- a/dom/apps/AppsUtils.jsm
+++ b/dom/apps/AppsUtils.jsm
@@ -152,20 +152,16 @@ this.AppsUtils = {
        isInIsolatedMozBrowserElement: aInIsolatedMozBrowser,
        originAttributes: {
          appId: aAppId,
          inIsolatedMozBrowser: aInIsolatedMozBrowser
        },
        usePrivateBrowsing: false,
        isContent: false,
 
-       isAppOfType: function(appType) {
-         throw Cr.NS_ERROR_NOT_IMPLEMENTED;
-       },
-
        QueryInterface: XPCOMUtils.generateQI([Ci.nsILoadContext,
                                               Ci.nsIInterfaceRequestor,
                                               Ci.nsISupports]),
        getInterface: function(iid) {
          if (iid.equals(Ci.nsILoadContext))
            return this;
          throw Cr.NS_ERROR_NO_INTERFACE;
        }
rename from dom/base/CustomElementsRegistry.cpp
rename to dom/base/CustomElementRegistry.cpp
--- a/dom/base/CustomElementsRegistry.cpp
+++ b/dom/base/CustomElementRegistry.cpp
@@ -1,17 +1,17 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#include "mozilla/dom/CustomElementsRegistry.h"
+#include "mozilla/dom/CustomElementRegistry.h"
 
-#include "mozilla/dom/CustomElementsRegistryBinding.h"
+#include "mozilla/dom/CustomElementRegistryBinding.h"
 #include "mozilla/dom/HTMLElementBinding.h"
 #include "mozilla/dom/WebComponentsBinding.h"
 #include "nsIParserService.h"
 #include "jsapi.h"
 
 namespace mozilla {
 namespace dom {
 
@@ -94,26 +94,26 @@ CustomElementData::RunCallbackQueue()
     mCallbackQueue[mCurrentCallback]->Call();
   }
 
   mCallbackQueue.Clear();
   mCurrentCallback = -1;
 }
 
 // Only needed for refcounted objects.
-NS_IMPL_CYCLE_COLLECTION_CLASS(CustomElementsRegistry)
+NS_IMPL_CYCLE_COLLECTION_CLASS(CustomElementRegistry)
 
-NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(CustomElementsRegistry)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(CustomElementRegistry)
   tmp->mCustomDefinitions.Clear();
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mWhenDefinedPromiseMap)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindow)
   NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(CustomElementsRegistry)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(CustomElementRegistry)
   for (auto iter = tmp->mCustomDefinitions.Iter(); !iter.Done(); iter.Next()) {
     nsAutoPtr<LifecycleCallbacks>& callbacks = iter.UserData()->mCallbacks;
 
     if (callbacks->mAttributeChangedCallback.WasPassed()) {
       NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb,
         "mCustomDefinitions->mCallbacks->mAttributeChangedCallback");
       cb.NoteXPCOMChild(callbacks->mAttributeChangedCallback.Value());
     }
@@ -136,70 +136,70 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(
       cb.NoteXPCOMChild(callbacks->mDetachedCallback.Value());
     }
   }
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWhenDefinedPromiseMap)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
-NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(CustomElementsRegistry)
+NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(CustomElementRegistry)
   for (auto iter = tmp->mCustomDefinitions.Iter(); !iter.Done(); iter.Next()) {
     aCallbacks.Trace(&iter.UserData()->mConstructor,
                      "mCustomDefinitions constructor",
                      aClosure);
     aCallbacks.Trace(&iter.UserData()->mPrototype,
                      "mCustomDefinitions prototype",
                      aClosure);
   }
   NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
-NS_IMPL_CYCLE_COLLECTING_ADDREF(CustomElementsRegistry)
-NS_IMPL_CYCLE_COLLECTING_RELEASE(CustomElementsRegistry)
+NS_IMPL_CYCLE_COLLECTING_ADDREF(CustomElementRegistry)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(CustomElementRegistry)
 
-NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CustomElementsRegistry)
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CustomElementRegistry)
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
 /* static */ bool
-CustomElementsRegistry::IsCustomElementsEnabled(JSContext* aCx, JSObject* aObject)
+CustomElementRegistry::IsCustomElementEnabled(JSContext* aCx, JSObject* aObject)
 {
   JS::Rooted<JSObject*> obj(aCx, aObject);
   if (Preferences::GetBool("dom.webcomponents.customelements.enabled") ||
       Preferences::GetBool("dom.webcomponents.enabled")) {
     return true;
   }
 
   return false;
 }
 
-/* static */ already_AddRefed<CustomElementsRegistry>
-CustomElementsRegistry::Create(nsPIDOMWindowInner* aWindow)
+/* static */ already_AddRefed<CustomElementRegistry>
+CustomElementRegistry::Create(nsPIDOMWindowInner* aWindow)
 {
   MOZ_ASSERT(aWindow);
   MOZ_ASSERT(aWindow->IsInnerWindow());
 
   if (!aWindow->GetDocShell()) {
     return nullptr;
   }
 
   if (!Preferences::GetBool("dom.webcomponents.customelements.enabled") &&
       !Preferences::GetBool("dom.webcomponents.enabled")) {
     return nullptr;
   }
 
-  RefPtr<CustomElementsRegistry> customElementsRegistry =
-    new CustomElementsRegistry(aWindow);
-  return customElementsRegistry.forget();
+  RefPtr<CustomElementRegistry> customElementRegistry =
+    new CustomElementRegistry(aWindow);
+  return customElementRegistry.forget();
 }
 
 /* static */ void
-CustomElementsRegistry::ProcessTopElementQueue()
+CustomElementRegistry::ProcessTopElementQueue()
 {
   MOZ_ASSERT(nsContentUtils::IsSafeToRunScript());
 
   nsTArray<RefPtr<CustomElementData>>& stack = *sProcessingStack;
   uint32_t firstQueue = stack.LastIndexOf((CustomElementData*) nullptr);
 
   for (uint32_t i = firstQueue + 1; i < stack.Length(); ++i) {
     // Callback queue may have already been processed in an earlier
@@ -217,59 +217,59 @@ CustomElementsRegistry::ProcessTopElemen
     stack.SetLength(firstQueue);
   } else {
     // Don't pop sentinel for base element queue.
     stack.SetLength(1);
   }
 }
 
 /* static */ void
-CustomElementsRegistry::XPCOMShutdown()
+CustomElementRegistry::XPCOMShutdown()
 {
   sProcessingStack.reset();
 }
 
 /* static */ Maybe<nsTArray<RefPtr<CustomElementData>>>
-CustomElementsRegistry::sProcessingStack;
+CustomElementRegistry::sProcessingStack;
 
-CustomElementsRegistry::CustomElementsRegistry(nsPIDOMWindowInner* aWindow)
+CustomElementRegistry::CustomElementRegistry(nsPIDOMWindowInner* aWindow)
  : mWindow(aWindow)
  , mIsCustomDefinitionRunning(false)
 {
   mozilla::HoldJSObjects(this);
 
   if (!sProcessingStack) {
     sProcessingStack.emplace();
     // Add the base queue sentinel to the processing stack.
     sProcessingStack->AppendElement((CustomElementData*) nullptr);
   }
 }
 
-CustomElementsRegistry::~CustomElementsRegistry()
+CustomElementRegistry::~CustomElementRegistry()
 {
   mozilla::DropJSObjects(this);
 }
 
 CustomElementDefinition*
-CustomElementsRegistry::LookupCustomElementDefinition(const nsAString& aLocalName,
-                                                      const nsAString* aIs) const
+CustomElementRegistry::LookupCustomElementDefinition(const nsAString& aLocalName,
+                                                     const nsAString* aIs) const
 {
   nsCOMPtr<nsIAtom> localNameAtom = NS_Atomize(aLocalName);
   nsCOMPtr<nsIAtom> typeAtom = aIs ? NS_Atomize(*aIs) : localNameAtom;
 
   CustomElementDefinition* data = mCustomDefinitions.Get(typeAtom);
   if (data && data->mLocalName == localNameAtom) {
     return data;
   }
 
   return nullptr;
 }
 
 void
-CustomElementsRegistry::RegisterUnresolvedElement(Element* aElement, nsIAtom* aTypeName)
+CustomElementRegistry::RegisterUnresolvedElement(Element* aElement, nsIAtom* aTypeName)
 {
   mozilla::dom::NodeInfo* info = aElement->NodeInfo();
 
   // Candidate may be a custom element through extension,
   // in which case the custom element type name will not
   // match the element tag name. e.g. <button is="x-button">.
   nsCOMPtr<nsIAtom> typeName = aTypeName;
   if (!typeName) {
@@ -284,18 +284,18 @@ CustomElementsRegistry::RegisterUnresolv
   nsWeakPtr* elem = unresolved->AppendElement();
   *elem = do_GetWeakReference(aElement);
   aElement->AddStates(NS_EVENT_STATE_UNRESOLVED);
 
   return;
 }
 
 void
-CustomElementsRegistry::SetupCustomElement(Element* aElement,
-                                           const nsAString* aTypeExtension)
+CustomElementRegistry::SetupCustomElement(Element* aElement,
+                                          const nsAString* aTypeExtension)
 {
   nsCOMPtr<nsIAtom> tagAtom = aElement->NodeInfo()->NameAtom();
   nsCOMPtr<nsIAtom> typeAtom = aTypeExtension ?
     NS_Atomize(*aTypeExtension) : tagAtom;
 
   if (aTypeExtension && !aElement->HasAttr(kNameSpaceID_None, nsGkAtoms::is)) {
     // Custom element setup in the parser happens after the "is"
     // attribute is added.
@@ -321,20 +321,20 @@ CustomElementsRegistry::SetupCustomEleme
   }
 
   // Enqueuing the created callback will set the CustomElementData on the
   // element, causing prototype swizzling to occur in Element::WrapObject.
   EnqueueLifecycleCallback(nsIDocument::eCreated, aElement, nullptr, data);
 }
 
 void
-CustomElementsRegistry::EnqueueLifecycleCallback(nsIDocument::ElementCallbackType aType,
-                                                 Element* aCustomElement,
-                                                 LifecycleCallbackArgs* aArgs,
-                                                 CustomElementDefinition* aDefinition)
+CustomElementRegistry::EnqueueLifecycleCallback(nsIDocument::ElementCallbackType aType,
+                                                Element* aCustomElement,
+                                                LifecycleCallbackArgs* aArgs,
+                                                CustomElementDefinition* aDefinition)
 {
   CustomElementData* elementData = aCustomElement->GetCustomElementData();
 
   // Let DEFINITION be ELEMENT's definition
   CustomElementDefinition* definition = aDefinition;
   if (!definition) {
     mozilla::dom::NodeInfo* info = aCustomElement->NodeInfo();
 
@@ -438,38 +438,38 @@ CustomElementsRegistry::EnqueueLifecycle
     // Add a script runner to pop and process the element queue at
     // the top of the processing stack.
     if (shouldPushElementQueue) {
       // Lifecycle callbacks enqueued by user agent implementation
       // should be invoked prior to returning control back to script.
       // Create a script runner to process the top of the processing
       // stack as soon as it is safe to run script.
       nsCOMPtr<nsIRunnable> runnable =
-        NS_NewRunnableFunction(&CustomElementsRegistry::ProcessTopElementQueue);
+        NS_NewRunnableFunction(&CustomElementRegistry::ProcessTopElementQueue);
       nsContentUtils::AddScriptRunner(runnable);
     }
   }
 }
 
 void
-CustomElementsRegistry::GetCustomPrototype(nsIAtom* aAtom,
-                                           JS::MutableHandle<JSObject*> aPrototype)
+CustomElementRegistry::GetCustomPrototype(nsIAtom* aAtom,
+                                          JS::MutableHandle<JSObject*> aPrototype)
 {
   mozilla::dom::CustomElementDefinition* definition = mCustomDefinitions.Get(aAtom);
   if (definition) {
     aPrototype.set(definition->mPrototype);
   } else {
     aPrototype.set(nullptr);
   }
 }
 
 void
-CustomElementsRegistry::UpgradeCandidates(JSContext* aCx,
-                                          nsIAtom* aKey,
-                                          CustomElementDefinition* aDefinition)
+CustomElementRegistry::UpgradeCandidates(JSContext* aCx,
+                                         nsIAtom* aKey,
+                                         CustomElementDefinition* aDefinition)
 {
   nsAutoPtr<nsTArray<nsWeakPtr>> candidates;
   mCandidatesMap.RemoveAndForget(aKey, candidates);
   if (candidates) {
     for (size_t i = 0; i < candidates->Length(); ++i) {
       nsCOMPtr<Element> elem = do_QueryReferent(candidates->ElementAt(i));
       if (!elem) {
         continue;
@@ -504,22 +504,22 @@ CustomElementsRegistry::UpgradeCandidate
 
       nsContentUtils::EnqueueLifecycleCallback(
         elem->OwnerDoc(), nsIDocument::eCreated, elem, nullptr, aDefinition);
     }
   }
 }
 
 JSObject*
-CustomElementsRegistry::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
+CustomElementRegistry::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
-  return CustomElementsRegistryBinding::Wrap(aCx, this, aGivenProto);
+  return CustomElementRegistryBinding::Wrap(aCx, this, aGivenProto);
 }
 
-nsISupports* CustomElementsRegistry::GetParentObject() const
+nsISupports* CustomElementRegistry::GetParentObject() const
 {
   return mWindow;
 }
 
 static const char* kLifeCycleCallbackNames[] = {
   "connectedCallback",
   "disconnectedCallback",
   "adoptedCallback",
@@ -553,20 +553,20 @@ CheckLifeCycleCallbacks(JSContext* aCx,
         return;
       }
     }
   }
 }
 
 // https://html.spec.whatwg.org/multipage/scripting.html#element-definition
 void
-CustomElementsRegistry::Define(const nsAString& aName,
-                               Function& aFunctionConstructor,
-                               const ElementDefinitionOptions& aOptions,
-                               ErrorResult& aRv)
+CustomElementRegistry::Define(const nsAString& aName,
+                              Function& aFunctionConstructor,
+                              const ElementDefinitionOptions& aOptions,
+                              ErrorResult& aRv)
 {
   aRv.MightThrowJSException();
 
   AutoJSAPI jsapi;
   if (NS_WARN_IF(!jsapi.Init(mWindow))) {
     aRv.Throw(NS_ERROR_FAILURE);
     return;
   }
@@ -584,41 +584,41 @@ CustomElementsRegistry::Define(const nsA
   if (!constructorUnwrapped) {
     // If the caller's compartment does not have permission to access the
     // unwrapped constructor then throw.
     aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
     return;
   }
 
   if (!JS::IsConstructor(constructorUnwrapped)) {
-    aRv.ThrowTypeError<MSG_NOT_CONSTRUCTOR>(NS_LITERAL_STRING("Argument 2 of CustomElementsRegistry.define"));
+    aRv.ThrowTypeError<MSG_NOT_CONSTRUCTOR>(NS_LITERAL_STRING("Argument 2 of CustomElementRegistry.define"));
     return;
   }
 
   /**
    * 2. If name is not a valid custom element name, then throw a "SyntaxError"
    *    DOMException and abort these steps.
    */
   nsCOMPtr<nsIAtom> nameAtom(NS_Atomize(aName));
   if (!nsContentUtils::IsCustomElementName(nameAtom)) {
     aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
     return;
   }
 
   /**
-   * 3. If this CustomElementsRegistry contains an entry with name name, then
+   * 3. If this CustomElementRegistry contains an entry with name name, then
    *    throw a "NotSupportedError" DOMException and abort these steps.
    */
   if (mCustomDefinitions.Get(nameAtom)) {
     aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
     return;
   }
 
   /**
-   * 4. If this CustomElementsRegistry contains an entry with constructor constructor,
+   * 4. If this CustomElementRegistry contains an entry with constructor constructor,
    *    then throw a "NotSupportedError" DOMException and abort these steps.
    */
   // TODO: Step 3 of HTMLConstructor also needs a way to look up definition by
   // using constructor. So I plans to figure out a solution to support both of
   // them in bug 1274159.
 
   /**
    * 5. Let localName be name.
@@ -767,60 +767,60 @@ CustomElementsRegistry::Define(const nsA
     new CustomElementDefinition(nameAtom,
                                 localNameAtom,
                                 constructor,
                                 constructorPrototype,
                                 callbacks,
                                 0 /* TODO dependent on HTML imports. Bug 877072 */);
 
   /**
-   * 12. Add definition to this CustomElementsRegistry.
+   * 12. Add definition to this CustomElementRegistry.
    */
   mCustomDefinitions.Put(nameAtom, definition);
 
   /**
    * 13. 14. 15. Upgrade candidates
    */
   // TODO: Bug 1299363 - Implement custom element v1 upgrade algorithm
   UpgradeCandidates(cx, nameAtom, definition);
 
   /**
-   * 16. If this CustomElementsRegistry's when-defined promise map contains an
+   * 16. If this CustomElementRegistry's when-defined promise map contains an
    *     entry with key name:
    *     1. Let promise be the value of that entry.
    *     2. Resolve promise with undefined.
-   *     3. Delete the entry with key name from this CustomElementsRegistry's
+   *     3. Delete the entry with key name from this CustomElementRegistry's
    *        when-defined promise map.
    */
   RefPtr<Promise> promise;
   mWhenDefinedPromiseMap.Remove(nameAtom, getter_AddRefs(promise));
   if (promise) {
     promise->MaybeResolveWithUndefined();
   }
 
 }
 
 void
-CustomElementsRegistry::Get(JSContext* aCx, const nsAString& aName,
-                            JS::MutableHandle<JS::Value> aRetVal)
+CustomElementRegistry::Get(JSContext* aCx, const nsAString& aName,
+                           JS::MutableHandle<JS::Value> aRetVal)
 {
   nsCOMPtr<nsIAtom> nameAtom(NS_Atomize(aName));
   CustomElementDefinition* data = mCustomDefinitions.Get(nameAtom);
 
   if (!data) {
     aRetVal.setUndefined();
     return;
   }
 
   aRetVal.setObject(*data->mConstructor);
   return;
 }
 
 already_AddRefed<Promise>
-CustomElementsRegistry::WhenDefined(const nsAString& aName, ErrorResult& aRv)
+CustomElementRegistry::WhenDefined(const nsAString& aName, ErrorResult& aRv)
 {
   nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(mWindow);
   RefPtr<Promise> promise = Promise::Create(global, aRv);
 
   if (aRv.Failed()) {
     return nullptr;
   }
 
rename from dom/base/CustomElementsRegistry.h
rename to dom/base/CustomElementRegistry.h
--- a/dom/base/CustomElementsRegistry.h
+++ b/dom/base/CustomElementRegistry.h
@@ -1,16 +1,16 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* 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 mozilla_dom_CustomElementsRegistry_h
-#define mozilla_dom_CustomElementsRegistry_h
+#ifndef mozilla_dom_CustomElementRegistry_h
+#define mozilla_dom_CustomElementRegistry_h
 
 #include "js/TypeDecls.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/ErrorResult.h"
 #include "mozilla/dom/BindingDeclarations.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsWrapperCache.h"
 #include "mozilla/dom/FunctionBinding.h"
@@ -124,29 +124,29 @@ struct CustomElementDefinition
 
   // A construction stack.
   // TODO: Bug 1287348 - Implement construction stack for upgrading an element
 
   // The document custom element order.
   uint32_t mDocOrder;
 };
 
-class CustomElementsRegistry final : public nsISupports,
-                                     public nsWrapperCache
+class CustomElementRegistry final : public nsISupports,
+                                    public nsWrapperCache
 {
   // Allow nsDocument to access mCustomDefinitions and mCandidatesMap.
   friend class ::nsDocument;
 
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
-  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(CustomElementsRegistry)
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(CustomElementRegistry)
 
 public:
-  static bool IsCustomElementsEnabled(JSContext* aCx, JSObject* aObject);
-  static already_AddRefed<CustomElementsRegistry> Create(nsPIDOMWindowInner* aWindow);
+  static bool IsCustomElementEnabled(JSContext* aCx, JSObject* aObject);
+  static already_AddRefed<CustomElementRegistry> Create(nsPIDOMWindowInner* aWindow);
   static void ProcessTopElementQueue();
 
   static void XPCOMShutdown();
 
   /**
    * Looking up a custom element definition.
    * https://html.spec.whatwg.org/#look-up-a-custom-element-definition
    */
@@ -164,18 +164,18 @@ public:
                                 Element* aCustomElement,
                                 LifecycleCallbackArgs* aArgs,
                                 CustomElementDefinition* aDefinition);
 
   void GetCustomPrototype(nsIAtom* aAtom,
                           JS::MutableHandle<JSObject*> aPrototype);
 
 private:
-  explicit CustomElementsRegistry(nsPIDOMWindowInner* aWindow);
-  ~CustomElementsRegistry();
+  explicit CustomElementRegistry(nsPIDOMWindowInner* aWindow);
+  ~CustomElementRegistry();
 
   /**
    * Registers an unresolved custom element that is a candidate for
    * upgrade when the definition is registered via registerElement.
    * |aTypeName| is the name of the custom element type, if it is not
    * provided, then element name is used. |aTypeName| should be provided
    * when registering a custom element that extends an existing
    * element. e.g. <button is="x-button">.
@@ -216,30 +216,30 @@ private:
   static mozilla::Maybe<nsTArray<RefPtr<CustomElementData>>> sProcessingStack;
 
   // It is used to prevent reentrant invocations of element definition.
   bool mIsCustomDefinitionRunning;
 
 private:
   class MOZ_RAII AutoSetRunningFlag final {
     public:
-      explicit AutoSetRunningFlag(CustomElementsRegistry* aRegistry)
+      explicit AutoSetRunningFlag(CustomElementRegistry* aRegistry)
         : mRegistry(aRegistry)
       {
         MOZ_ASSERT(!mRegistry->mIsCustomDefinitionRunning,
                    "IsCustomDefinitionRunning flag should be initially false");
         mRegistry->mIsCustomDefinitionRunning = true;
       }
 
       ~AutoSetRunningFlag() {
         mRegistry->mIsCustomDefinitionRunning = false;
       }
 
     private:
-      CustomElementsRegistry* mRegistry;
+      CustomElementRegistry* mRegistry;
   };
 
 public:
   nsISupports* GetParentObject() const;
 
   virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
   void Define(const nsAString& aName, Function& aFunctionConstructor,
@@ -250,9 +250,9 @@ public:
 
   already_AddRefed<Promise> WhenDefined(const nsAString& aName, ErrorResult& aRv);
 };
 
 } // namespace dom
 } // namespace mozilla
 
 
-#endif // mozilla_dom_CustomElementsRegistry_h
+#endif // mozilla_dom_CustomElementRegistry_h
--- a/dom/base/Element.cpp
+++ b/dom/base/Element.cpp
@@ -2808,19 +2808,19 @@ Element::DescribeAttribute(uint32_t inde
 {
   // name
   mAttrsAndChildren.AttrNameAt(index)->GetQualifiedName(aOutDescription);
 
   // value
   aOutDescription.AppendLiteral("=\"");
   nsAutoString value;
   mAttrsAndChildren.AttrAt(index)->ToString(value);
-  for (int i = value.Length(); i >= 0; --i) {
-    if (value[i] == char16_t('"'))
-      value.Insert(char16_t('\\'), uint32_t(i));
+  for (uint32_t i = value.Length(); i > 0; --i) {
+    if (value[i - 1] == char16_t('"'))
+      value.Insert(char16_t('\\'), i - 1);
   }
   aOutDescription.Append(value);
   aOutDescription.Append('"');
 }
 
 #ifdef DEBUG
 void
 Element::ListAttributes(FILE* out) const
@@ -3718,25 +3718,27 @@ Element::InsertAdjacent(const nsAString&
 {
   if (aWhere.LowerCaseEqualsLiteral("beforebegin")) {
     nsCOMPtr<nsINode> parent = GetParentNode();
     if (!parent) {
       return nullptr;
     }
     parent->InsertBefore(*aNode, this, aError);
   } else if (aWhere.LowerCaseEqualsLiteral("afterbegin")) {
-    static_cast<nsINode*>(this)->InsertBefore(*aNode, GetFirstChild(), aError);
+    nsCOMPtr<nsINode> refNode = GetFirstChild();
+    static_cast<nsINode*>(this)->InsertBefore(*aNode, refNode, aError);
   } else if (aWhere.LowerCaseEqualsLiteral("beforeend")) {
     static_cast<nsINode*>(this)->AppendChild(*aNode, aError);
   } else if (aWhere.LowerCaseEqualsLiteral("afterend")) {
     nsCOMPtr<nsINode> parent = GetParentNode();
     if (!parent) {
       return nullptr;
     }
-    parent->InsertBefore(*aNode, GetNextSibling(), aError);
+    nsCOMPtr<nsINode> refNode = GetNextSibling();
+    parent->InsertBefore(*aNode, refNode, aError);
   } else {
     aError.Throw(NS_ERROR_DOM_SYNTAX_ERR);
     return nullptr;
   }
 
   return aError.Failed() ? nullptr : aNode;
 }
 
--- a/dom/base/Element.h
+++ b/dom/base/Element.h
@@ -131,17 +131,17 @@ class EventChainPostVisitor;
 class EventChainPreVisitor;
 class EventChainVisitor;
 class EventListenerManager;
 class EventStateManager;
 
 namespace dom {
 
 class Animation;
-class CustomElementsRegistry;
+class CustomElementRegistry;
 class Link;
 class UndoManager;
 class DOMRect;
 class DOMRectList;
 class DestinationInsertionPointList;
 class Grid;
 
 // IID for the dom::Element interface
@@ -422,17 +422,17 @@ protected:
 private:
   // Need to allow the ESM, nsGlobalWindow, and the focus manager to
   // set our state
   friend class mozilla::EventStateManager;
   friend class ::nsGlobalWindow;
   friend class ::nsFocusManager;
 
   // Allow CusomtElementRegistry to call AddStates.
-  friend class CustomElementsRegistry;
+  friend class CustomElementRegistry;
 
   // Also need to allow Link to call UpdateLinkState.
   friend class Link;
 
   void NotifyStateChange(EventStates aStates);
 
   void NotifyStyleStateChange(EventStates aStates);
 
@@ -743,27 +743,25 @@ public:
   }
   void ReleasePointerCapture(int32_t aPointerId, ErrorResult& aError)
   {
     bool activeState = false;
     if (!nsIPresShell::GetPointerInfo(aPointerId, activeState)) {
       aError.Throw(NS_ERROR_DOM_INVALID_POINTER_ERR);
       return;
     }
-    nsIPresShell::PointerCaptureInfo* pointerCaptureInfo = nullptr;
-    if (nsIPresShell::gPointerCaptureList->Get(aPointerId, &pointerCaptureInfo) &&
-        pointerCaptureInfo && pointerCaptureInfo->mPendingContent == this) {
+    if (HasPointerCapture(aPointerId)) {
       nsIPresShell::ReleasePointerCapturingContent(aPointerId);
     }
   }
   bool HasPointerCapture(long aPointerId)
   {
-    nsIPresShell::PointerCaptureInfo* pointerCaptureInfo = nullptr;
-    if (nsIPresShell::gPointerCaptureList->Get(aPointerId, &pointerCaptureInfo) &&
-        pointerCaptureInfo && pointerCaptureInfo->mPendingContent == this) {
+    nsIPresShell::PointerCaptureInfo* pointerCaptureInfo =
+      nsIPresShell::GetPointerCaptureInfo(aPointerId);
+    if (pointerCaptureInfo && pointerCaptureInfo->mPendingContent == this) {
       return true;
     }
     return false;
   }
   void SetCapture(bool aRetargetToElement)
   {
     // If there is already an active capture, ignore this request. This would
     // occur if a splitter, frame resizer, etc had already captured and we don't
--- a/dom/base/moz.build
+++ b/dom/base/moz.build
@@ -152,17 +152,17 @@ EXPORTS.mozilla.dom += [
     'BarProps.h',
     'BlobSet.h',
     'BodyUtil.h',
     'BorrowedAttrInfo.h',
     'ChildIterator.h',
     'ChromeNodeList.h',
     'ChromeUtils.h',
     'Comment.h',
-    'CustomElementsRegistry.h',
+    'CustomElementRegistry.h',
     'DirectionalityUtils.h',
     'DocumentFragment.h',
     'DocumentType.h',
     'DOMCursor.h',
     'DOMError.h',
     'DOMException.h',
     'DOMImplementation.h',
     'DOMMatrix.h',
@@ -217,17 +217,17 @@ UNIFIED_SOURCES += [
     'BlobSet.cpp',
     'BodyUtil.cpp',
     'BorrowedAttrInfo.cpp',
     'ChildIterator.cpp',
     'ChromeNodeList.cpp',
     'ChromeUtils.cpp',
     'Comment.cpp',
     'Crypto.cpp',
-    'CustomElementsRegistry.cpp',
+    'CustomElementRegistry.cpp',
     'DirectionalityUtils.cpp',
     'DocumentFragment.cpp',
     'DocumentType.cpp',
     'DOMCursor.cpp',
     'DOMError.cpp',
     'DOMException.cpp',
     'DOMImplementation.cpp',
     'DOMMatrix.cpp',
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -33,17 +33,17 @@
 #include "mozilla/Attributes.h"
 #include "mozilla/AutoRestore.h"
 #include "mozilla/AutoTimelineMarker.h"
 #include "mozilla/Base64.h"
 #include "mozilla/CheckedInt.h"
 #include "mozilla/DebugOnly.h"
 #include "mozilla/LoadInfo.h"
 #include "mozilla/dom/ContentChild.h"
-#include "mozilla/dom/CustomElementsRegistry.h"
+#include "mozilla/dom/CustomElementRegistry.h"
 #include "mozilla/dom/DocumentFragment.h"
 #include "mozilla/dom/DOMTypes.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/HTMLMediaElement.h"
 #include "mozilla/dom/HTMLTemplateElement.h"
 #include "mozilla/dom/HTMLContentElement.h"
 #include "mozilla/dom/HTMLShadowElement.h"
 #include "mozilla/dom/ipc/BlobChild.h"
@@ -62,16 +62,17 @@
 #include "mozilla/gfx/DataSurfaceHelpers.h"
 #include "mozilla/IMEStateManager.h"
 #include "mozilla/InternalMutationEvent.h"
 #include "mozilla/Likely.h"
 #include "mozilla/MouseEvents.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/dom/Selection.h"
 #include "mozilla/TextEvents.h"
+#include "nsArrayUtils.h"
 #include "nsAString.h"
 #include "nsAttrName.h"
 #include "nsAttrValue.h"
 #include "nsAttrValueInlines.h"
 #include "nsBindingManager.h"
 #include "nsCaret.h"
 #include "nsCCUncollectableMarker.h"
 #include "nsCharSeparatedTokenizer.h"
@@ -7688,21 +7689,21 @@ nsContentUtils::TransferableToIPCTransfe
                                               IPCDataTransfer* aIPCDataTransfer,
                                               bool aInSyncMessage,
                                               mozilla::dom::nsIContentChild* aChild,
                                               mozilla::dom::nsIContentParent* aParent)
 {
   MOZ_ASSERT((aChild && !aParent) || (!aChild && aParent));
 
   if (aTransferable) {
-    nsCOMPtr<nsISupportsArray> flavorList;
+    nsCOMPtr<nsIArray> flavorList;
     aTransferable->FlavorsTransferableCanExport(getter_AddRefs(flavorList));
     if (flavorList) {
       uint32_t flavorCount = 0;
-      flavorList->Count(&flavorCount);
+      flavorList->GetLength(&flavorCount);
       for (uint32_t j = 0; j < flavorCount; ++j) {
         nsCOMPtr<nsISupportsCString> flavor = do_QueryElementAt(flavorList, j);
         if (!flavor) {
           continue;
         }
 
         nsAutoCString flavorStr;
         flavor->GetData(flavorStr);
@@ -9532,17 +9533,17 @@ nsContentUtils::LookupCustomElementDefin
     return nullptr;
   }
 
   nsCOMPtr<nsPIDOMWindowInner> window(doc->GetInnerWindow());
   if (!window) {
     return nullptr;
   }
 
-  RefPtr<CustomElementsRegistry> registry(window->CustomElements());
+  RefPtr<CustomElementRegistry> registry(window->CustomElements());
   if (!registry) {
     return nullptr;
   }
 
   return registry->LookupCustomElementDefinition(aLocalName, aIs);
 }
 
 /* static */ void
@@ -9565,17 +9566,17 @@ nsContentUtils::SetupCustomElement(Eleme
     return;
   }
 
   nsCOMPtr<nsPIDOMWindowInner> window(doc->GetInnerWindow());
   if (!window) {
     return;
   }
 
-  RefPtr<CustomElementsRegistry> registry(window->CustomElements());
+  RefPtr<CustomElementRegistry> registry(window->CustomElements());
   if (!registry) {
     return;
   }
 
   return registry->SetupCustomElement(aElement, aTypeExtension);
 }
 
 /* static */ void
@@ -9594,17 +9595,17 @@ nsContentUtils::EnqueueLifecycleCallback
     return;
   }
 
   nsCOMPtr<nsPIDOMWindowInner> window(doc->GetInnerWindow());
   if (!window) {
     return;
   }
 
-  RefPtr<CustomElementsRegistry> registry(window->CustomElements());
+  RefPtr<CustomElementRegistry> registry(window->CustomElements());
   if (!registry) {
     return;
   }
 
   registry->EnqueueLifecycleCallback(aType, aCustomElement, aArgs, aDefinition);
 }
 
 /* static */ void
@@ -9623,15 +9624,15 @@ nsContentUtils::GetCustomPrototype(nsIDo
     return;
   }
 
   nsCOMPtr<nsPIDOMWindowInner> window(doc->GetInnerWindow());
   if (!window) {
     return;
   }
 
-  RefPtr<CustomElementsRegistry> registry(window->CustomElements());
+  RefPtr<CustomElementRegistry> registry(window->CustomElements());
   if (!registry) {
     return;
   }
 
   return registry->GetCustomPrototype(aAtom, aPrototype);
 }
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -214,18 +214,18 @@
 #include "mozilla/dom/HTMLInputElement.h"
 #include "mozilla/dom/ImageTracker.h"
 #include "mozilla/dom/MediaQueryList.h"
 #include "mozilla/dom/NodeFilterBinding.h"
 #include "mozilla/OwningNonNull.h"
 #include "mozilla/dom/TabChild.h"
 #include "mozilla/dom/UndoManager.h"
 #include "mozilla/dom/WebComponentsBinding.h"
-#include "mozilla/dom/CustomElementsRegistryBinding.h"
-#include "mozilla/dom/CustomElementsRegistry.h"
+#include "mozilla/dom/CustomElementRegistryBinding.h"
+#include "mozilla/dom/CustomElementRegistry.h"
 #include "nsFrame.h"
 #include "nsDOMCaretPosition.h"
 #include "nsIDOMHTMLTextAreaElement.h"
 #include "nsViewportInfo.h"
 #include "mozilla/StaticPtr.h"
 #include "nsITextControlElement.h"
 #include "nsIDOMNSEditableElement.h"
 #include "nsIEditor.h"
@@ -5369,34 +5369,34 @@ bool IsLowercaseASCII(const nsAString& a
     char16_t c = aValue[i];
     if (!(0x0061 <= (c) && ((c) <= 0x007a))) {
       return false;
     }
   }
   return true;
 }
 
-already_AddRefed<mozilla::dom::CustomElementsRegistry>
-nsDocument::GetCustomElementsRegistry()
+already_AddRefed<mozilla::dom::CustomElementRegistry>
+nsDocument::GetCustomElementRegistry()
 {
   nsAutoString contentType;
   GetContentType(contentType);
   if (!IsHTMLDocument() &&
       !contentType.EqualsLiteral("application/xhtml+xml")) {
     return nullptr;
   }
 
   nsCOMPtr<nsPIDOMWindowInner> window(
     do_QueryInterface(mScriptGlobalObject ? mScriptGlobalObject
                                           : GetScopeObject()));
   if (!window) {
     return nullptr;
   }
 
-  RefPtr<CustomElementsRegistry> registry = window->CustomElements();
+  RefPtr<CustomElementRegistry> registry = window->CustomElements();
   if (!registry) {
     return nullptr;
   }
 
   return registry.forget();
 }
 
 already_AddRefed<Element>
@@ -5685,17 +5685,17 @@ nsDocument::CustomElementConstructor(JSC
   // Function name is the type of the custom element.
   JSString* jsFunName =
     JS_GetFunctionId(JS_ValueToFunction(aCx, args.calleev()));
   nsAutoJSString elemName;
   if (!elemName.init(aCx, jsFunName)) {
     return true;
   }
 
-  RefPtr<mozilla::dom::CustomElementsRegistry> registry = window->CustomElements();
+  RefPtr<mozilla::dom::CustomElementRegistry> registry = window->CustomElements();
   if (!registry) {
     return true;
   }
 
   nsCOMPtr<nsIAtom> typeAtom(NS_Atomize(elemName));
   CustomElementDefinition* definition = registry->mCustomDefinitions.Get(typeAtom);
   if (!definition) {
     return true;
@@ -5753,17 +5753,17 @@ nsDocument::IsWebComponentsEnabled(JSCon
 }
 
 void
 nsDocument::RegisterElement(JSContext* aCx, const nsAString& aType,
                             const ElementRegistrationOptions& aOptions,
                             JS::MutableHandle<JSObject*> aRetval,
                             ErrorResult& rv)
 {
-  RefPtr<CustomElementsRegistry> registry(GetCustomElementsRegistry());
+  RefPtr<CustomElementRegistry> registry(GetCustomElementRegistry());
   if (!registry) {
     rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
     return;
   }
 
   // Unconditionally convert TYPE to lowercase.
   nsAutoString lcType;
   nsContentUtils::ASCIIToLower(aType, lcType);
@@ -8807,17 +8807,20 @@ nsDocument::OnPageHide(bool aPersisted,
     nsIPrincipal* principal = GetPrincipal();
     os->NotifyObservers(static_cast<nsIDocument*>(this),
                         nsContentUtils::IsSystemPrincipal(principal) ?
                           "chrome-page-hidden" :
                           "content-page-hidden",
                         nullptr);
   }
 
-  DispatchPageTransition(target, NS_LITERAL_STRING("pagehide"), aPersisted);
+  {
+    PageUnloadingEventTimeStamp timeStamp(this);
+    DispatchPageTransition(target, NS_LITERAL_STRING("pagehide"), aPersisted);
+  }
 
   mVisible = false;
 
   UpdateVisibilityState();
 
   EnumerateExternalResources(NotifyPageHide, &aPersisted);
   EnumerateActivityObservers(NotifyActivityChanged, nullptr);
 
--- a/dom/base/nsDocument.h
+++ b/dom/base/nsDocument.h
@@ -63,17 +63,17 @@
 #include "mozilla/dom/StyleSheetList.h"
 #include "nsDataHashtable.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/Attributes.h"
 #include "nsIDOMXPathEvaluator.h"
 #include "jsfriendapi.h"
 #include "ImportManager.h"
 #include "mozilla/LinkedList.h"
-#include "CustomElementsRegistry.h"
+#include "CustomElementRegistry.h"
 
 #define XML_DECLARATION_BITS_DECLARATION_EXISTS   (1 << 0)
 #define XML_DECLARATION_BITS_ENCODING_EXISTS      (1 << 1)
 #define XML_DECLARATION_BITS_STANDALONE_EXISTS    (1 << 2)
 #define XML_DECLARATION_BITS_STANDALONE_YES       (1 << 3)
 
 
 class nsDOMStyleSheetSetList;
@@ -1359,18 +1359,18 @@ private:
    */
   const nsString* CheckCustomElementName(
     const mozilla::dom::ElementCreationOptions& aOptions,
     const nsAString& aLocalName,
     uint32_t aNamespaceID,
     ErrorResult& rv);
 
 public:
-  virtual already_AddRefed<mozilla::dom::CustomElementsRegistry>
-    GetCustomElementsRegistry() override;
+  virtual already_AddRefed<mozilla::dom::CustomElementRegistry>
+    GetCustomElementRegistry() override;
 
   static bool IsWebComponentsEnabled(JSContext* aCx, JSObject* aObject);
 
   RefPtr<mozilla::EventListenerManager> mListenerManager;
   RefPtr<mozilla::dom::StyleSheetList> mDOMStyleSheets;
   RefPtr<nsDOMStyleSheetSetList> mStyleSheetSetList;
   RefPtr<nsScriptLoader> mScriptLoader;
   nsDocHeaderData* mHeaderData;
--- a/dom/base/nsDocumentEncoder.cpp
+++ b/dom/base/nsDocumentEncoder.cpp
@@ -44,19 +44,17 @@
 #include "nsUnicharUtils.h"
 #include "nsReadableUtils.h"
 #include "nsTArray.h"
 #include "nsIFrame.h"
 #include "nsStringBuffer.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/ShadowRoot.h"
 #include "mozilla/dom/EncodingUtils.h"
-#include "nsContainerFrame.h"
-#include "nsBlockFrame.h"
-#include "nsComputedDOMStyle.h"
+#include "nsLayoutUtils.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 nsresult NS_NewDomSelection(nsISelection **aDomSelection);
 
 enum nsRangeIterationDirection {
   kDirectionOut = -1,
@@ -319,95 +317,16 @@ nsDocumentEncoder::GetMimeType(nsAString
 
 
 bool
 nsDocumentEncoder::IncludeInContext(nsINode *aNode)
 {
   return false;
 }
 
-static
-bool
-LineHasNonEmptyContentWorker(nsIFrame* aFrame)
-{
-  // Look for non-empty frames, but ignore inline and br frames.
-  // For inline frames, descend into the children, if any.
-  if (aFrame->GetType() == nsGkAtoms::inlineFrame) {
-    for (nsIFrame* child : aFrame->PrincipalChildList()) {
-      if (LineHasNonEmptyContentWorker(child)) {
-        return true;
-      }
-    }
-  } else {
-    if (aFrame->GetType() != nsGkAtoms::brFrame &&
-        !aFrame->IsEmpty()) {
-      return true;
-    }
-  }
-  return false;
-}
-
-static
-bool
-LineHasNonEmptyContent(nsLineBox* aLine)
-{
-  int32_t count = aLine->GetChildCount();
-  for (nsIFrame* frame = aLine->mFirstChild; count > 0;
-       --count, frame = frame->GetNextSibling()) {
-    if (LineHasNonEmptyContentWorker(frame)) {
-      return true;
-    }
-  }
-  return false;
-}
-
-static
-bool
-IsInvisibleBreak(nsINode *aNode)
-{
-  if (!aNode->IsElement() || !aNode->IsEditable()) {
-    return false;
-  }
-  nsIFrame* frame = aNode->AsElement()->GetPrimaryFrame();
-  if (!frame || frame->GetType() != nsGkAtoms::brFrame) {
-    return false;
-  }
-
-  nsContainerFrame* f = frame->GetParent();
-  while (f && f->IsFrameOfType(nsBox::eLineParticipant)) {
-    f = f->GetParent();
-  }
-  nsBlockFrame* blockAncestor = do_QueryFrame(f);
-  if (!blockAncestor) {
-    // The container frame doesn't support line breaking.
-    return false;
-  }
-
-  bool valid = false;
-  nsBlockInFlowLineIterator iter(blockAncestor, frame, &valid);
-  if (!valid) {
-    return false;
-  }
-
-  bool lineNonEmpty = LineHasNonEmptyContent(iter.GetLine());
-
-  while (iter.Next()) {
-    auto currentLine = iter.GetLine();
-    // Completely skip empty lines.
-    if (!currentLine->IsEmpty()) {
-      // If we come across an inline line, the BR has caused a visible line break.
-      if (currentLine->IsInline()) {
-        return false;
-      }
-    }
-  }
-
-  return lineNonEmpty;
-}
-
 nsresult
 nsDocumentEncoder::SerializeNodeStart(nsINode* aNode,
                                       int32_t aStartOffset,
                                       int32_t aEndOffset,
                                       nsAString& aStr,
                                       nsINode* aOriginalNode)
 {
   if (!IsVisibleNode(aNode))
@@ -432,17 +351,17 @@ nsDocumentEncoder::SerializeNodeStart(ns
   // Either there was no fixed-up node,
   // or the caller did fixup themselves and aNode is already fixed
   if (!node)
     node = aNode;
 
   if (node->IsElement()) {
     if ((mFlags & (nsIDocumentEncoder::OutputPreformatted |
                    nsIDocumentEncoder::OutputDropInvisibleBreak)) &&
-        IsInvisibleBreak(node)) {
+        nsLayoutUtils::IsInvisibleBreak(node)) {
       return NS_OK;
     }
     Element* originalElement =
       aOriginalNode && aOriginalNode->IsElement() ?
         aOriginalNode->AsElement() : nullptr;
     mSerializer->AppendElementStart(node->AsElement(),
                                     originalElement, aStr);
     return NS_OK;
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -3794,22 +3794,22 @@ nsGlobalWindow::GetHistory(ErrorResult& 
 
   if (!mHistory) {
     mHistory = new nsHistory(AsInner());
   }
 
   return mHistory;
 }
 
-CustomElementsRegistry*
+CustomElementRegistry*
 nsGlobalWindow::CustomElements()
 {
   MOZ_RELEASE_ASSERT(IsInnerWindow());
   if (!mCustomElements) {
-      mCustomElements = CustomElementsRegistry::Create(AsInner());
+      mCustomElements = CustomElementRegistry::Create(AsInner());
   }
 
   return mCustomElements;
 }
 
 Performance*
 nsPIDOMWindowInner::GetPerformance()
 {
--- a/dom/base/nsGlobalWindow.h
+++ b/dom/base/nsGlobalWindow.h
@@ -100,17 +100,17 @@ class nsWindowSizes;
 
 namespace mozilla {
 class DOMEventTargetHelper;
 namespace dom {
 class BarProp;
 struct ChannelPixelLayout;
 class Console;
 class Crypto;
-class CustomElementsRegistry;
+class CustomElementRegistry;
 class External;
 class Function;
 class Gamepad;
 enum class ImageBitmapFormat : uint32_t;
 class Location;
 class MediaQueryList;
 class MozSelfSupport;
 class Navigator;
@@ -807,17 +807,16 @@ public:
 
   // Inner windows only.
   // Enable/disable updates for VR
   void EnableVRUpdates();
   void DisableVRUpdates();
 
   // Update the VR displays for this window
   bool UpdateVRDisplays(nsTArray<RefPtr<mozilla::dom::VRDisplay>>& aDisplays);
-  
   // Inner windows only.
   // Called to inform that the set of active VR displays has changed.
   void NotifyActiveVRDisplaysChanged();
 
 #define EVENT(name_, id_, type_, struct_)                                     \
   mozilla::dom::EventHandlerNonNull* GetOn##name_()                           \
   {                                                                           \
     mozilla::EventListenerManager* elm = GetExistingListenerManager();        \
@@ -882,17 +881,17 @@ public:
   }
   void GetNameOuter(nsAString& aName);
   void GetName(nsAString& aName, mozilla::ErrorResult& aError);
   void SetNameOuter(const nsAString& aName, mozilla::ErrorResult& aError);
   void SetName(const nsAString& aName, mozilla::ErrorResult& aError);
   mozilla::dom::Location* GetLocation(mozilla::ErrorResult& aError);
   nsIDOMLocation* GetLocation() override;
   nsHistory* GetHistory(mozilla::ErrorResult& aError);
-  mozilla::dom::CustomElementsRegistry* CustomElements() override;
+  mozilla::dom::CustomElementRegistry* CustomElements() override;
   mozilla::dom::BarProp* GetLocationbar(mozilla::ErrorResult& aError);
   mozilla::dom::BarProp* GetMenubar(mozilla::ErrorResult& aError);
   mozilla::dom::BarProp* GetPersonalbar(mozilla::ErrorResult& aError);
   mozilla::dom::BarProp* GetScrollbars(mozilla::ErrorResult& aError);
   mozilla::dom::BarProp* GetStatusbar(mozilla::ErrorResult& aError);
   mozilla::dom::BarProp* GetToolbar(mozilla::ErrorResult& aError);
   void GetStatusOuter(nsAString& aStatus);
   void GetStatus(nsAString& aStatus, mozilla::ErrorResult& aError);
@@ -1884,17 +1883,17 @@ protected:
   // If mTimeoutInsertionPoint is non-null, insertions should happen after it.
   // This is a dummy timeout at the moment; if that ever changes, the logic in
   // ResetTimersForNonBackgroundWindow needs to change.
   nsTimeout*                    mTimeoutInsertionPoint;
   uint32_t                      mTimeoutPublicIdCounter;
   uint32_t                      mTimeoutFiringDepth;
   RefPtr<mozilla::dom::Location> mLocation;
   RefPtr<nsHistory>           mHistory;
-  RefPtr<mozilla::dom::CustomElementsRegistry> mCustomElements;
+  RefPtr<mozilla::dom::CustomElementRegistry> mCustomElements;
 
   // These member variables are used on both inner and the outer windows.
   nsCOMPtr<nsIPrincipal> mDocumentPrincipal;
 
   typedef nsTArray<RefPtr<mozilla::dom::StorageEvent>> nsDOMStorageEventArray;
   nsDOMStorageEventArray mPendingStorageEvents;
 
   uint32_t mTimeoutsSuspendDepth;
--- a/dom/base/nsHostObjectProtocolHandler.cpp
+++ b/dom/base/nsHostObjectProtocolHandler.cpp
@@ -28,16 +28,17 @@
 
 #define RELEASING_TIMER 1000
 
 using mozilla::DOMMediaStream;
 using mozilla::dom::BlobImpl;
 using mozilla::dom::MediaSource;
 using mozilla::ErrorResult;
 using mozilla::net::LoadInfo;
+using mozilla::Move;
 
 // -----------------------------------------------------------------------
 // Hash table
 struct DataInfo
 {
   enum ObjectType {
     eBlobImpl,
     eMediaStream,
@@ -610,17 +611,17 @@ nsHostObjectProtocolHandler::RemoveDataE
     return;
   }
 
   if (aBroadcastToOtherProcesses && info->mObjectType == DataInfo::eBlobImpl) {
     mozilla::BroadcastBlobURLUnregistration(aUri, info);
   }
 
   if (!info->mURIs.IsEmpty()) {
-    ReleasingTimerHolder::Create(Move(info->mURIs));
+    mozilla::ReleasingTimerHolder::Create(Move(info->mURIs));
   }
 
   gDataTable->Remove(aUri);
   if (gDataTable->Count() == 0) {
     delete gDataTable;
     gDataTable = nullptr;
   }
 }
--- a/dom/base/nsIDocument.h
+++ b/dom/base/nsIDocument.h
@@ -32,16 +32,17 @@
 #include "nsExpirationTracker.h"
 #include "nsClassHashtable.h"
 #include "prclist.h"
 #include "mozilla/UniquePtr.h"
 #include "mozilla/CORSMode.h"
 #include "mozilla/LinkedList.h"
 #include "mozilla/StyleBackendType.h"
 #include "mozilla/StyleSheet.h"
+#include "mozilla/TimeStamp.h"
 #include <bitset>                        // for member
 
 #ifdef MOZILLA_INTERNAL_API
 #include "mozilla/dom/DocumentBinding.h"
 #else
 namespace mozilla {
 namespace dom {
 class ElementCreationOptionsOrString;
@@ -211,16 +212,43 @@ public:
 
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_IDOCUMENT_IID)
   NS_DECL_AND_IMPL_ZEROING_OPERATOR_NEW
 
 #ifdef MOZILLA_INTERNAL_API
   nsIDocument();
 #endif
 
+  // This helper class must be set when we dispatch beforeunload and unload
+  // events in order to avoid unterminate sync XHRs.
+  class MOZ_RAII PageUnloadingEventTimeStamp
+  {
+    nsCOMPtr<nsIDocument> mDocument;
+    bool mSet;
+
+  public:
+    explicit PageUnloadingEventTimeStamp(nsIDocument* aDocument)
+      : mDocument(aDocument)
+      , mSet(false)
+    {
+      MOZ_ASSERT(aDocument);
+      if (mDocument->mPageUnloadingEventTimeStamp.IsNull()) {
+        mDocument->SetPageUnloadingEventTimeStamp();
+        mSet = true;
+      }
+    }
+
+    ~PageUnloadingEventTimeStamp()
+    {
+      if (mSet) {
+        mDocument->CleanUnloadEventsTimeStamp();
+      }
+    }
+  };
+
   /**
    * Let the document know that we're starting to load data into it.
    * @param aCommand The parser command. Must not be null.
    *                 XXXbz It's odd to have that here.
    * @param aChannel The channel the data will come from. The channel must be
    *                 able to report its Content-Type.
    * @param aLoadGroup The loadgroup this document should use from now on.
    *                   Note that the document might not be the only thing using
@@ -931,19 +959,50 @@ public:
   nsTArray<RefPtr<mozilla::dom::AnonymousContent>>& GetAnonymousContents() {
     return mAnonymousContents;
   }
 
   static nsresult GenerateDocumentId(nsAString& aId);
   nsresult GetOrCreateId(nsAString& aId);
   void SetId(const nsAString& aId);
 
+  mozilla::TimeStamp GetPageUnloadingEventTimeStamp() const
+  {
+    if (!mParentDocument) {
+      return mPageUnloadingEventTimeStamp;
+    }
+
+    mozilla::TimeStamp parentTimeStamp(mParentDocument->GetPageUnloadingEventTimeStamp());
+    if (parentTimeStamp.IsNull()) {
+      return mPageUnloadingEventTimeStamp;
+    }
+
+    if (!mPageUnloadingEventTimeStamp ||
+        parentTimeStamp < mPageUnloadingEventTimeStamp) {
+      return parentTimeStamp;
+    }
+
+    return mPageUnloadingEventTimeStamp;
+  }
+
 protected:
   virtual Element *GetRootElementInternal() const = 0;
 
+  void SetPageUnloadingEventTimeStamp()
+  {
+    MOZ_ASSERT(!mPageUnloadingEventTimeStamp);
+    mPageUnloadingEventTimeStamp = mozilla::TimeStamp::NowLoRes();
+  }
+
+  void CleanUnloadEventsTimeStamp()
+  {
+    MOZ_ASSERT(mPageUnloadingEventTimeStamp);
+    mPageUnloadingEventTimeStamp = mozilla::TimeStamp();
+  }
+
 private:
   class SelectorCacheKey
   {
     public:
       explicit SelectorCacheKey(const nsAString& aString) : mKey(aString)
       {
         MOZ_COUNT_CTOR(SelectorCacheKey);
       }
@@ -2510,18 +2569,18 @@ public:
 
   nsIDocument* GetTopLevelContentDocument();
 
   virtual void
     RegisterElement(JSContext* aCx, const nsAString& aName,
                     const mozilla::dom::ElementRegistrationOptions& aOptions,
                     JS::MutableHandle<JSObject*> aRetval,
                     mozilla::ErrorResult& rv) = 0;
-  virtual already_AddRefed<mozilla::dom::CustomElementsRegistry>
-    GetCustomElementsRegistry() = 0;
+  virtual already_AddRefed<mozilla::dom::CustomElementRegistry>
+    GetCustomElementRegistry() = 0;
 
   already_AddRefed<nsContentList>
   GetElementsByTagName(const nsAString& aTagName)
   {
     return NS_GetContentList(this, kNameSpaceID_Unknown, aTagName);
   }
   already_AddRefed<nsContentList>
     GetElementsByTagNameNS(const nsAString& aNamespaceURI,
@@ -3237,16 +3296,18 @@ protected:
   // Flags for use counters used by any child documents of this document.
   std::bitset<mozilla::eUseCounter_Count> mChildDocumentUseCounters;
   // Flags for whether we've notified our top-level "page" of a use counter
   // for this child document.
   std::bitset<mozilla::eUseCounter_Count> mNotifiedPageForUseCounter;
 
   // Whether the user has interacted with the document or not:
   bool mUserHasInteracted;
+
+  mozilla::TimeStamp mPageUnloadingEventTimeStamp;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsIDocument, NS_IDOCUMENT_IID)
 
 /**
  * mozAutoSubtreeModified batches DOM mutations so that a DOMSubtreeModified
  * event is dispatched, if necessary, when the outermost mozAutoSubtreeModified
  * object is deleted.
--- a/dom/base/nsImageLoadingContent.cpp
+++ b/dom/base/nsImageLoadingContent.cpp
@@ -741,20 +741,27 @@ nsImageLoadingContent::LoadImage(const n
 {
   // First, get a document (needed for security checks and the like)
   nsIDocument* doc = GetOurOwnerDoc();
   if (!doc) {
     // No reason to bother, I think...
     return NS_OK;
   }
 
+  // Pending load/error events need to be canceled in some situations. This
+  // is not documented in the spec, but can cause site compat problems if not
+  // done. See bug 1309461 and https://github.com/whatwg/html/issues/1872.
+  CancelPendingEvent();
+
   if (aNewURI.IsEmpty()) {
     // Cancel image requests and then fire only error event per spec.
     CancelImageRequests(aNotify);
-    FireEvent(NS_LITERAL_STRING("error"));
+    // Mark error event as cancelable only for src="" case, since only this
+    // error causes site compat problem (bug 1308069) for now.
+    FireEvent(NS_LITERAL_STRING("error"), true);
     return NS_OK;
   }
 
   // Fire loadstart event
   FireEvent(NS_LITERAL_STRING("loadstart"));
 
   // Parse the URI string to get image URI
   nsCOMPtr<nsIURI> imageURI;
@@ -776,16 +783,21 @@ nsresult
 nsImageLoadingContent::LoadImage(nsIURI* aNewURI,
                                  bool aForce,
                                  bool aNotify,
                                  ImageLoadType aImageLoadType,
                                  bool aLoadStart,
                                  nsIDocument* aDocument,
                                  nsLoadFlags aLoadFlags)
 {
+  // Pending load/error events need to be canceled in some situations. This
+  // is not documented in the spec, but can cause site compat problems if not
+  // done. See bug 1309461 and https://github.com/whatwg/html/issues/1872.
+  CancelPendingEvent();
+
   // Fire loadstart event if required
   if (aLoadStart) {
     FireEvent(NS_LITERAL_STRING("loadstart"));
   }
 
   if (!mLoadingEnabled) {
     // XXX Why fire an error here? seems like the callers to SetLoadingEnabled
     // don't want/need it.
@@ -1138,36 +1150,57 @@ nsImageLoadingContent::StringToURI(const
   return NS_NewURI(aURI,
                    aSpec,
                    charset.IsEmpty() ? nullptr : charset.get(),
                    baseURL,
                    nsContentUtils::GetIOService());
 }
 
 nsresult
-nsImageLoadingContent::FireEvent(const nsAString& aEventType)
+nsImageLoadingContent::FireEvent(const nsAString& aEventType, bool aIsCancelable)
 {
   if (nsContentUtils::DocumentInactiveForImageLoads(GetOurOwnerDoc())) {
     // Don't bother to fire any events, especially error events.
     return NS_OK;
   }
 
   // We have to fire the event asynchronously so that we won't go into infinite
   // loops in cases when onLoad handlers reset the src and the new src is in
   // cache.
 
   nsCOMPtr<nsINode> thisNode = do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
 
   RefPtr<AsyncEventDispatcher> loadBlockingAsyncDispatcher =
     new LoadBlockingAsyncEventDispatcher(thisNode, aEventType, false, false);
   loadBlockingAsyncDispatcher->PostDOMEvent();
 
+  if (aIsCancelable) {
+    mPendingEvent = loadBlockingAsyncDispatcher;
+  }
+
   return NS_OK;
 }
 
+void
+nsImageLoadingContent::AsyncEventRunning(AsyncEventDispatcher* aEvent)
+{
+  if (mPendingEvent == aEvent) {
+    mPendingEvent = nullptr;
+  }
+}
+
+void
+nsImageLoadingContent::CancelPendingEvent()
+{
+  if (mPendingEvent) {
+    mPendingEvent->Cancel();
+    mPendingEvent = nullptr;
+  }
+}
+
 RefPtr<imgRequestProxy>&
 nsImageLoadingContent::PrepareNextRequest(ImageLoadType aImageLoadType)
 {
   nsImageFrame* frame = do_QueryFrame(GetOurPrimaryFrame());
   if (frame) {
     // Detect JavaScript-based animations created by changing the |src|
     // attribute on a timer.
     TimeStamp now = TimeStamp::Now();
--- a/dom/base/nsImageLoadingContent.h
+++ b/dom/base/nsImageLoadingContent.h
@@ -27,16 +27,20 @@
 #include "mozilla/net/ReferrerPolicy.h"
 
 class nsIURI;
 class nsIDocument;
 class nsPresContext;
 class nsIContent;
 class imgRequestProxy;
 
+namespace mozilla {
+class AsyncEventDispatcher;
+} // namespace mozilla
+
 #ifdef LoadImage
 // Undefine LoadImage to prevent naming conflict with Windows.
 #undef LoadImage
 #endif
 
 class nsImageLoadingContent : public nsIImageLoadingContent,
                               public imgIOnloadBlocker
 {
@@ -208,16 +212,18 @@ protected:
 
   nsresult OnLoadComplete(imgIRequest* aRequest, nsresult aStatus);
   void OnUnlockedDraw();
   nsresult OnImageIsAnimated(imgIRequest *aRequest);
 
   // The nsContentPolicyType we would use for this ImageLoadType
   static nsContentPolicyType PolicyTypeForLoad(ImageLoadType aImageLoadType);
 
+  void AsyncEventRunning(mozilla::AsyncEventDispatcher* aEvent);
+
 private:
   /**
    * Struct used to manage the image observers.
    */
   struct ImageObserver {
     explicit ImageObserver(imgINotificationObserver* aObserver);
     ~ImageObserver();
 
@@ -255,18 +261,26 @@ private:
    */
   void UpdateImageState(bool aNotify);
 
   /**
    * Method to fire an event once we know what's going on with the image load.
    *
    * @param aEventType "loadstart", "loadend", "load", or "error" depending on
    *                   how things went
+   * @param aIsCancelable true if event is cancelable.
    */
-  nsresult FireEvent(const nsAString& aEventType);
+  nsresult FireEvent(const nsAString& aEventType, bool aIsCancelable = false);
+
+  /**
+   * Method to cancel and null-out pending event if they exist.
+   */
+  void CancelPendingEvent();
+
+  RefPtr<mozilla::AsyncEventDispatcher> mPendingEvent;
 
 protected:
   /**
    * Method to create an nsIURI object from the given string (will
    * handle getting the right charset, base, etc).  You MUST pass in a
    * non-null document to this function.
    *
    * @param aSpec the string spec (from an HTML attribute, eg)
--- a/dom/base/nsPIDOMWindow.h
+++ b/dom/base/nsPIDOMWindow.h
@@ -40,17 +40,17 @@ struct nsTimeout;
 typedef uint32_t SuspendTypes;
 
 namespace mozilla {
 namespace dom {
 class AudioContext;
 class Element;
 class Performance;
 class ServiceWorkerRegistration;
-class CustomElementsRegistry;
+class CustomElementRegistry;
 } // namespace dom
 } // namespace mozilla
 
 // Popup control state enum. The values in this enum must go from most
 // permissive to least permissive so that it's safe to push state in
 // all situations. Pushing popup state onto the stack never makes the
 // current popup state less permissive (see
 // nsGlobalWindow::PushPopupControlState()).
@@ -90,17 +90,17 @@ class nsPIDOMWindow : public T
 {
 public:
   nsPIDOMWindowInner* AsInner();
   const nsPIDOMWindowInner* AsInner() const;
   nsPIDOMWindowOuter* AsOuter();
   const nsPIDOMWindowOuter* AsOuter() const;
 
   virtual nsPIDOMWindowOuter* GetPrivateRoot() = 0;
-  virtual mozilla::dom::CustomElementsRegistry* CustomElements() = 0;
+  virtual mozilla::dom::CustomElementRegistry* CustomElements() = 0;
   // Outer windows only.
   virtual void ActivateOrDeactivate(bool aActivate) = 0;
 
   // this is called GetTopWindowRoot to avoid conflicts with nsIDOMWindow::GetWindowRoot
   /**
    * |top| gets the root of the window hierarchy.
    *
    * This function does not cross chrome-content boundaries, so if this
--- a/dom/base/nsTreeSanitizer.cpp
+++ b/dom/base/nsTreeSanitizer.cpp
@@ -1414,17 +1414,18 @@ nsTreeSanitizer::SanitizeChildren(nsINod
       }
       if (MustFlatten(ns, localName)) {
         RemoveAllAttributes(node);
         nsCOMPtr<nsIContent> next = node->GetNextNode(aRoot);
         nsCOMPtr<nsIContent> parent = node->GetParent();
         nsCOMPtr<nsIContent> child; // Must keep the child alive during move
         ErrorResult rv;
         while ((child = node->GetFirstChild())) {
-          parent->InsertBefore(*child, node, rv);
+          nsCOMPtr<nsINode> refNode = node;
+          parent->InsertBefore(*child, refNode, rv);
           if (rv.Failed()) {
             break;
           }
         }
         node->RemoveFromParent();
         node = next;
         continue;
       }
--- a/dom/base/test/mochitest.ini
+++ b/dom/base/test/mochitest.ini
@@ -626,16 +626,17 @@ skip-if = buildapp == 'b2g'
 [test_bug1250148.html]
 [test_bug1259588.html]
 [test_bug1263696.html]
 [test_bug1268962.html]
 [test_bug1274806.html]
 [test_bug1281963.html]
 [test_bug1295852.html]
 [test_bug1307730.html]
+[test_bug1308069.html]
 [test_caretPositionFromPoint.html]
 [test_change_policy.html]
 skip-if = buildapp == 'b2g' #no ssl support
 [test_classList.html]
 [test_clearTimeoutIntervalNoArg.html]
 [test_constructor-assignment.html]
 [test_constructor.html]
 [test_copyimage.html]
new file mode 100644
--- /dev/null
+++ b/dom/base/test/test_bug1308069.html
@@ -0,0 +1,87 @@
+<!DOCTYPE html>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1308069
+-->
+<head>
+<title>Bug 1308069</title>
+<script 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=1308069">Mozilla Bug 1308069</a>
+<script class="testbody" type="text/javascript">
+
+function testClearPendingErrorEvent() {
+  return new Promise(function(aResolve, aReject) {
+    var hasErrorEvent = false;
+    var imgTarget = new Image();
+
+    var imgForChangingTargetSrc = new Image();
+    // Queue an error event for changing imgTarget's src.
+    imgForChangingTargetSrc.src = '';
+    imgForChangingTargetSrc.onerror = function() {
+      // This clears imgTarget's pending error event.
+      imgTarget.src = 'data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="96" height="96"><path d="M10,10L32,90L90,32z" fill="lightgreen"/></svg>';
+
+      // Queue an error event for checking and resolving promise.
+      var imgForCheckingAndResolvingPromise = new Image();
+      imgForCheckingAndResolvingPromise.src = '';
+      imgForCheckingAndResolvingPromise.onerror = function() {
+        ok(!hasErrorEvent,
+           'Should not receive an error event since the pending error event ' +
+           'should be cleared before it fired');
+        aResolve();
+      };
+    };
+
+    // Setting src to empty string queues an error event.
+    imgTarget.src = '';
+    imgTarget.onerror = function() {
+      hasErrorEvent = true;
+    };
+  });
+}
+
+function testReplacePendingErrorEvent() {
+  return new Promise(function(aResolve) {
+    var numOfErrorEvent = 0;
+    var imgTarget = new Image();
+
+    var imgForChangingTargetSrc = new Image();
+    // Queue an error event for changing imgTarget's src.
+    imgForChangingTargetSrc.src = '';
+    imgForChangingTargetSrc.onerror = function() {
+      // This clears pending error event and fires a new one.
+      imgTarget.src = '';
+
+      // Queue an error event for checking and resolving promise.
+      var imgForCheckingAndResolvingPromise = new Image();
+      imgForCheckingAndResolvingPromise.src = '';
+      imgForCheckingAndResolvingPromise.onerror = function() {
+        is(numOfErrorEvent, 1,
+           'Should only receive one error event since the first pending error ' +
+           'event should be cleared before it fired');
+        aResolve();
+      };
+    };
+
+    // Setting src to empty string queues an error event.
+    imgTarget.src = '';
+    imgTarget.onerror = function() {
+      numOfErrorEvent++;
+    };
+  });
+}
+
+SimpleTest.waitForExplicitFinish();
+
+Promise.resolve()
+.then(() => testClearPendingErrorEvent())
+.then(() => testReplacePendingErrorEvent())
+.catch((err) => ok(false, "promise rejected: " + err))
+.then(() => SimpleTest.finish());
+
+</script>
+</body>
+</html>
--- a/dom/bindings/BindingUtils.h
+++ b/dom/bindings/BindingUtils.h
@@ -1276,56 +1276,50 @@ FindEnumStringIndexImpl(const CharT* cha
       return i;
     }
   }
 
   return -1;
 }
 
 template<bool InvalidValueFatal>
-inline int
+inline bool
 FindEnumStringIndex(JSContext* cx, JS::Handle<JS::Value> v, const EnumEntry* values,
-                    const char* type, const char* sourceDescription, bool* ok)
+                    const char* type, const char* sourceDescription, int* index)
 {
   // JS_StringEqualsAscii is slow as molasses, so don't use it here.
   JS::RootedString str(cx, JS::ToString(cx, v));
   if (!str) {
-    *ok = false;
-    return 0;
+    return false;
   }
 
   {
-    int index;
     size_t length;
     JS::AutoCheckCannotGC nogc;
     if (js::StringHasLatin1Chars(str)) {
       const JS::Latin1Char* chars = JS_GetLatin1StringCharsAndLength(cx, nogc, str,
                                                                      &length);
       if (!chars) {
-        *ok = false;
-        return 0;
+        return false;
       }
-      index = FindEnumStringIndexImpl(chars, length, values);
+      *index = FindEnumStringIndexImpl(chars, length, values);
     } else {
       const char16_t* chars = JS_GetTwoByteStringCharsAndLength(cx, nogc, str,
                                                                 &length);
       if (!chars) {
-        *ok = false;
-        return 0;
+        return false;
       }
-      index = FindEnumStringIndexImpl(chars, length, values);
+      *index = FindEnumStringIndexImpl(chars, length, values);
     }
-    if (index >= 0) {
-      *ok = true;
-      return index;
+    if (*index >= 0) {
+      return true;
     }
   }
 
-  *ok = EnumValueNotFound<InvalidValueFatal>(cx, str, type, sourceDescription);
-  return -1;
+  return EnumValueNotFound<InvalidValueFatal>(cx, str, type, sourceDescription);
 }
 
 inline nsWrapperCache*
 GetWrapperCache(const ParentObject& aParentObject)
 {
   return aParentObject.mWrapperCache;
 }
 
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -5592,19 +5592,18 @@ def getJSToNativeConversionInfo(type, de
                 if (index < 0) {
                   return true;
                 }
                 """)
 
         template = fill(
             """
             {
-              bool ok;
-              int index = FindEnumStringIndex<${invalidEnumValueFatal}>(cx, $${val}, ${values}, "${enumtype}", "${sourceDescription}", &ok);
-              if (!ok) {
+              int index;
+              if (!FindEnumStringIndex<${invalidEnumValueFatal}>(cx, $${val}, ${values}, "${enumtype}", "${sourceDescription}", &index)) {
                 $*{exceptionCode}
               }
               $*{handleInvalidEnumValueCode}
               ${enumLoc} = static_cast<${enumtype}>(index);
             }
             """,
             enumtype=enumName,
             values=enumName + "Values::" + ENUM_ENTRY_VARIABLE_NAME,
--- a/dom/browser-element/mochitest/mochitest-oop.ini
+++ b/dom/browser-element/mochitest/mochitest-oop.ini
@@ -1,14 +1,12 @@
 [DEFAULT]
 # Both the "inproc" and "oop" versions of OpenMixedProcess open remote frames,
 # so we don't run that test on platforms which don't support OOP tests.
-# OOP tests don't work on native-fennec (bug 774939).
-# Bug 960345 - Disabled on OSX debug for frequent crashes.
-skip-if = os == "android" || (toolkit == "cocoa" && debug) || buildapp == 'mulet' || (buildapp == 'b2g' && (toolkit != 'gonk' || debug)) || e10s
+skip-if = os == "android" || e10s
 support-files =
   browserElement_OpenMixedProcess.js
   file_browserElement_ExecuteScript.html
   file_browserElement_OpenMixedProcess.html
   browserElement_ExecuteScript.js
   browserElement_Find.js
   browserElement_OpenTab.js
 
--- a/dom/canvas/test/test_canvas.html
+++ b/dom/canvas/test/test_canvas.html
@@ -4740,17 +4740,17 @@ isPixel(ctx, 50,25, 0,255,0,255, 0);
 
 function test_2d_fillStyle_parse_invalid_hsl_5() {
 
 var canvas = document.getElementById('c172');
 var ctx = canvas.getContext('2d');
 
 
 ctx.fillStyle = '#0f0';
-try { ctx.fillStyle = 'hsl(0, 100%, 100%, 1)'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+try { ctx.fillStyle = 'hsl(0, 100%, 100%,)'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
 ctx.fillRect(0, 0, 100, 50);
 isPixel(ctx, 50,25, 0,255,0,255, 0);
 
 
 }
 </script>
 
 <!-- [[[ test_2d.fillStyle.parse.invalid.hsla-1.html ]]] -->
@@ -4869,17 +4869,17 @@ isPixel(ctx, 50,25, 0,255,0,255, 0);
 
 function test_2d_fillStyle_parse_invalid_rgb_1() {
 
 var canvas = document.getElementById('c175');
 var ctx = canvas.getContext('2d');
 
 
 ctx.fillStyle = '#0f0';
-try { ctx.fillStyle = 'rgb(255.0, 0, 0)'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+try { ctx.fillStyle = 'rgb(255.0, 0%, 0)'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
 ctx.fillRect(0, 0, 100, 50);
 isPixel(ctx, 50,25, 0,255,0,255, 0);
 
 
 }
 </script>
 
 <!-- [[[ test_2d.fillStyle.parse.invalid.rgb-2.html ]]] -->
@@ -4891,17 +4891,17 @@ isPixel(ctx, 50,25, 0,255,0,255, 0);
 
 function test_2d_fillStyle_parse_invalid_rgb_2() {
 
 var canvas = document.getElementById('c176');
 var ctx = canvas.getContext('2d');
 
 
 ctx.fillStyle = '#0f0';
-try { ctx.fillStyle = 'rgb(255, 0.0, 0)'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+try { ctx.fillStyle = 'rgb(255%, 0.0, 0)'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
 ctx.fillRect(0, 0, 100, 50);
 isPixel(ctx, 50,25, 0,255,0,255, 0);
 
 
 }
 </script>
 
 <!-- [[[ test_2d.fillStyle.parse.invalid.rgb-3.html ]]] -->
@@ -4957,17 +4957,17 @@ isPixel(ctx, 50,25, 0,255,0,255, 0);
 
 function test_2d_fillStyle_parse_invalid_rgb_5() {
 
 var canvas = document.getElementById('c179');
 var ctx = canvas.getContext('2d');
 
 
 ctx.fillStyle = '#0f0';
-try { ctx.fillStyle = 'rgb(255 0 0)'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+try { ctx.fillStyle = 'rgb(255, 0 0)'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
 ctx.fillRect(0, 0, 100, 50);
 isPixel(ctx, 50,25, 0,255,0,255, 0);
 
 
 }
 </script>
 
 <!-- [[[ test_2d.fillStyle.parse.invalid.rgb-6.html ]]] -->
@@ -5001,17 +5001,17 @@ isPixel(ctx, 50,25, 0,255,0,255, 0);
 
 function test_2d_fillStyle_parse_invalid_rgb_7() {
 
 var canvas = document.getElementById('c181');
 var ctx = canvas.getContext('2d');
 
 
 ctx.fillStyle = '#0f0';
-try { ctx.fillStyle = 'rgb(255, 0, 0, 1)'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+try { ctx.fillStyle = 'rgb(255, 0, 0, 1,)'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
 ctx.fillRect(0, 0, 100, 50);
 isPixel(ctx, 50,25, 0,255,0,255, 0);
 
 
 }
 </script>
 
 <!-- [[[ test_2d.fillStyle.parse.invalid.rgba-1.html ]]] -->
@@ -5023,17 +5023,17 @@ isPixel(ctx, 50,25, 0,255,0,255, 0);
 
 function test_2d_fillStyle_parse_invalid_rgba_1() {
 
 var canvas = document.getElementById('c182');
 var ctx = canvas.getContext('2d');
 
 
 ctx.fillStyle = '#0f0';
-try { ctx.fillStyle = 'rgba(255, 0, 0)'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+try { ctx.fillStyle = 'rgba(255, 0, 0,)'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
 ctx.fillRect(0, 0, 100, 50);
 isPixel(ctx, 50,25, 0,255,0,255, 0);
 
 
 }
 </script>
 
 <!-- [[[ test_2d.fillStyle.parse.invalid.rgba-2.html ]]] -->
@@ -5045,17 +5045,17 @@ isPixel(ctx, 50,25, 0,255,0,255, 0);
 
 function test_2d_fillStyle_parse_invalid_rgba_2() {
 
 var canvas = document.getElementById('c183');
 var ctx = canvas.getContext('2d');
 
 
 ctx.fillStyle = '#0f0';
-try { ctx.fillStyle = 'rgba(255.0, 0, 0, 1)'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+try { ctx.fillStyle = 'rgba(255.0, 0, 0, 1,)'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
 ctx.fillRect(0, 0, 100, 50);
 isPixel(ctx, 50,25, 0,255,0,255, 0);
 
 
 }
 </script>
 
 <!-- [[[ test_2d.fillStyle.parse.invalid.rgba-3.html ]]] -->
@@ -5089,17 +5089,17 @@ isPixel(ctx, 50,25, 0,255,0,255, 0);
 
 function test_2d_fillStyle_parse_invalid_rgba_4() {
 
 var canvas = document.getElementById('c185');
 var ctx = canvas.getContext('2d');
 
 
 ctx.fillStyle = '#0f0';
-try { ctx.fillStyle = 'rgba(255, 0, 0, 100%)'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
+try { ctx.fillStyle = 'rgba(255, 0, 0, 100.%)'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
 ctx.fillRect(0, 0, 100, 50);
 isPixel(ctx, 50,25, 0,255,0,255, 0);
 
 
 }
 </script>
 
 <!-- [[[ test_2d.fillStyle.parse.invalid.rgba-5.html ]]] -->
--- a/dom/events/AsyncEventDispatcher.cpp
+++ b/dom/events/AsyncEventDispatcher.cpp
@@ -34,16 +34,17 @@ AsyncEventDispatcher::AsyncEventDispatch
 }
 
 NS_IMETHODIMP
 AsyncEventDispatcher::Run()
 {
   if (mCanceled) {
     return NS_OK;
   }
+  mTarget->AsyncEventRunning(this);
   RefPtr<Event> event = mEvent ? mEvent->InternalDOMEvent() : nullptr;
   if (!event) {
     event = NS_NewDOMEvent(mTarget, nullptr, nullptr);
     event->InitEvent(mEventType, mBubbles, false);
     event->SetTrusted(true);
   }
   if (mOnlyChromeDispatch) {
     MOZ_ASSERT(event->IsTrusted());
--- a/dom/events/EventTarget.h
+++ b/dom/events/EventTarget.h
@@ -11,16 +11,17 @@
 #include "nsWrapperCache.h"
 #include "nsIAtom.h"
 
 class nsPIDOMWindowOuter;
 class nsIGlobalObject;
 
 namespace mozilla {
 
+class AsyncEventDispatcher;
 class ErrorResult;
 class EventListenerManager;
 
 namespace dom {
 
 class AddEventListenerOptionsOrBoolean;
 class Event;
 class EventListener;
@@ -86,16 +87,19 @@ public:
   virtual EventListenerManager* GetOrCreateListenerManager() = 0;
 
   /**
    * Get the event listener manager, returning null if it does not already
    * exist.
    */
   virtual EventListenerManager* GetExistingListenerManager() const = 0;
 
+  // Called from AsyncEventDispatcher to notify it is running.
+  virtual void AsyncEventRunning(AsyncEventDispatcher* aEvent) {}
+
   virtual bool IsApzAware() const;
 
 protected:
   EventHandlerNonNull* GetEventHandler(nsIAtom* aType,
                                        const nsAString& aTypeString);
   void SetEventHandler(nsIAtom* aType, const nsAString& aTypeString,
                        EventHandlerNonNull* aHandler);
 };
--- a/dom/html/HTMLImageElement.cpp
+++ b/dom/html/HTMLImageElement.cpp
@@ -161,16 +161,22 @@ NS_IMPL_INT_ATTR(HTMLImageElement, Vspac
 
 bool
 HTMLImageElement::IsInteractiveHTMLContent(bool aIgnoreTabindex) const
 {
   return HasAttr(kNameSpaceID_None, nsGkAtoms::usemap) ||
           nsGenericHTMLElement::IsInteractiveHTMLContent(aIgnoreTabindex);
 }
 
+void
+HTMLImageElement::AsyncEventRunning(AsyncEventDispatcher* aEvent)
+{
+  nsImageLoadingContent::AsyncEventRunning(aEvent);
+}
+
 nsresult
 HTMLImageElement::GetCurrentSrc(nsAString& aValue)
 {
   nsCOMPtr<nsIURI> currentURI;
   GetCurrentURI(getter_AddRefs(currentURI));
   if (currentURI) {
     nsAutoCString spec;
     currentURI->GetSpec(spec);
--- a/dom/html/HTMLImageElement.h
+++ b/dom/html/HTMLImageElement.h
@@ -44,16 +44,19 @@ public:
   // nsISupports
   NS_DECL_ISUPPORTS_INHERITED
 
   virtual bool Draggable() const override;
 
   // Element
   virtual bool IsInteractiveHTMLContent(bool aIgnoreTabindex) const override;
 
+  // EventTarget
+  virtual void AsyncEventRunning(AsyncEventDispatcher* aEvent) override;
+
   // nsIDOMHTMLImageElement
   NS_DECL_NSIDOMHTMLIMAGEELEMENT
 
   NS_IMPL_FROMCONTENT_HTML_WITH_TAG(HTMLImageElement, img)
 
   // override from nsImageLoadingContent
   CORSMode GetCORSMode() override;
 
--- a/dom/html/HTMLInputElement.cpp
+++ b/dom/html/HTMLInputElement.cpp
@@ -3666,16 +3666,22 @@ HTMLInputElement::IsNodeApzAwareInternal
 
 bool
 HTMLInputElement::IsInteractiveHTMLContent(bool aIgnoreTabindex) const
 {
   return mType != NS_FORM_INPUT_HIDDEN ||
          nsGenericHTMLFormElementWithState::IsInteractiveHTMLContent(aIgnoreTabindex);
 }
 
+void
+HTMLInputElement::AsyncEventRunning(AsyncEventDispatcher* aEvent)
+{
+  nsImageLoadingContent::AsyncEventRunning(aEvent);
+}
+
 NS_IMETHODIMP
 HTMLInputElement::Select()
 {
   if (mType == NS_FORM_INPUT_NUMBER) {
     nsNumberControlFrame* numberControlFrame =
       do_QueryFrame(GetPrimaryFrame());
     if (numberControlFrame) {
       return numberControlFrame->HandleSelectCall();
--- a/dom/html/HTMLInputElement.h
+++ b/dom/html/HTMLInputElement.h
@@ -137,16 +137,19 @@ public:
   // nsINode
 #if !defined(ANDROID) && !defined(XP_MACOSX)
   virtual bool IsNodeApzAwareInternal() const override;
 #endif
 
   // Element
   virtual bool IsInteractiveHTMLContent(bool aIgnoreTabindex) const override;
 
+  // EventTarget
+  virtual void AsyncEventRunning(AsyncEventDispatcher* aEvent) override;
+
   // nsIDOMHTMLInputElement
   NS_DECL_NSIDOMHTMLINPUTELEMENT
 
   // nsIPhonetic
   NS_DECL_NSIPHONETIC
 
   // nsIDOMNSEditableElement
   NS_IMETHOD GetEditor(nsIEditor** aEditor) override
--- a/dom/html/HTMLObjectElement.cpp
+++ b/dom/html/HTMLObjectElement.cpp
@@ -54,16 +54,22 @@ HTMLObjectElement::~HTMLObjectElement()
 
 bool
 HTMLObjectElement::IsInteractiveHTMLContent(bool aIgnoreTabindex) const
 {
   return HasAttr(kNameSpaceID_None, nsGkAtoms::usemap) ||
          nsGenericHTMLFormElement::IsInteractiveHTMLContent(aIgnoreTabindex);
 }
 
+void
+HTMLObjectElement::AsyncEventRunning(AsyncEventDispatcher* aEvent)
+{
+  nsImageLoadingContent::AsyncEventRunning(aEvent);
+}
+
 bool
 HTMLObjectElement::IsDoneAddingChildren()
 {
   return mIsDoneAddingChildren;
 }
 
 void
 HTMLObjectElement::DoneAddingChildren(bool aHaveNotified)
--- a/dom/html/HTMLObjectElement.h
+++ b/dom/html/HTMLObjectElement.h
@@ -43,16 +43,19 @@ public:
   static void HandlePluginInstantiated(Element* aElement);
   // Weak pointer. Null if last action was blur.
   static Element* sLastFocused;
 #endif
 
   // Element
   virtual bool IsInteractiveHTMLContent(bool aIgnoreTabindex) const override;
 
+  // EventTarget
+  virtual void AsyncEventRunning(AsyncEventDispatcher* aEvent) override;
+
   // nsIDOMHTMLObjectElement
   NS_DECL_NSIDOMHTMLOBJECTELEMENT
 
   virtual nsresult BindToTree(nsIDocument *aDocument, nsIContent *aParent,
                               nsIContent *aBindingParent,
                               bool aCompileEventHandlers) override;
   virtual void UnbindFromTree(bool aDeep = true,
                               bool aNullParent = true) override;
--- a/dom/html/HTMLSelectElement.cpp
+++ b/dom/html/HTMLSelectElement.cpp
@@ -582,17 +582,18 @@ HTMLSelectElement::Add(nsGenericHTMLElem
     // NOT_FOUND_ERR: Raised if before is not a descendant of the SELECT
     // element.
     aError.Throw(NS_ERROR_DOM_NOT_FOUND_ERR);
     return;
   }
 
   // If the before parameter is not null, we are equivalent to the
   // insertBefore method on the parent of before.
-  parent->InsertBefore(aElement, aBefore, aError);
+  nsCOMPtr<nsINode> refNode = aBefore;
+  parent->InsertBefore(aElement, refNode, aError);
 }
 
 NS_IMETHODIMP
 HTMLSelectElement::Add(nsIDOMHTMLElement* aElement,
                        nsIVariant* aBefore)
 {
   uint16_t dataType;
   nsresult rv = aBefore->GetDataType(&dataType);
--- a/dom/html/HTMLSharedObjectElement.cpp
+++ b/dom/html/HTMLSharedObjectElement.cpp
@@ -103,16 +103,22 @@ NS_IMETHODIMP
 HTMLSharedObjectElement::PostHandleEvent(EventChainPostVisitor& aVisitor)
 {
   HTMLObjectElement::HandleFocusBlurPlugin(this, aVisitor.mEvent);
   return NS_OK;
 }
 
 #endif // #ifdef XP_MACOSX
 
+void
+HTMLSharedObjectElement::AsyncEventRunning(AsyncEventDispatcher* aEvent)
+{
+  nsImageLoadingContent::AsyncEventRunning(aEvent);
+}
+
 nsresult
 HTMLSharedObjectElement::BindToTree(nsIDocument *aDocument,
                                     nsIContent *aParent,
                                     nsIContent *aBindingParent,
                                     bool aCompileEventHandlers)
 {
   nsresult rv = nsGenericHTMLElement::BindToTree(aDocument, aParent,
                                                  aBindingParent,
--- a/dom/html/HTMLSharedObjectElement.h
+++ b/dom/html/HTMLSharedObjectElement.h
@@ -38,16 +38,19 @@ public:
 #endif
 
   // nsIDOMHTMLAppletElement
   NS_DECL_NSIDOMHTMLAPPLETELEMENT
 
   // Can't use macro for nsIDOMHTMLEmbedElement because it has conflicts with
   // NS_DECL_NSIDOMHTMLAPPLETELEMENT.
 
+  // EventTarget
+  virtual void AsyncEventRunning(AsyncEventDispatcher* aEvent) override;
+
   // nsIDOMHTMLEmbedElement
   NS_IMETHOD GetSrc(nsAString &aSrc) override;
   NS_IMETHOD SetSrc(const nsAString &aSrc) override;
   NS_IMETHOD GetType(nsAString &aType) override;
   NS_IMETHOD SetType(const nsAString &aType) override;
 
   virtual nsresult BindToTree(nsIDocument *aDocument, nsIContent *aParent,
                               nsIContent *aBindingParent,
--- a/dom/html/HTMLTableElement.cpp
+++ b/dom/html/HTMLTableElement.cpp
@@ -407,17 +407,18 @@ HTMLTableElement::CreateTHead()
                                 getter_AddRefs(nodeInfo));
 
     head = NS_NewHTMLTableSectionElement(nodeInfo.forget());
     if (!head) {
       return nullptr;
     }
 
     ErrorResult rv;
-    nsINode::InsertBefore(*head, nsINode::GetFirstChild(), rv);
+    nsCOMPtr<nsINode> refNode = nsINode::GetFirstChild();
+    nsINode::InsertBefore(*head, refNode, rv);
   }
   return head.forget();
 }
 
 void
 HTMLTableElement::DeleteTHead()
 {
   HTMLTableSectionElement* tHead = GetTHead();
@@ -498,17 +499,17 @@ HTMLTableElement::CreateTBody()
                                                kNameSpaceID_XHTML,
                                                nsIDOMNode::ELEMENT_NODE);
   MOZ_ASSERT(nodeInfo);
 
   RefPtr<nsGenericHTMLElement> newBody =
     NS_NewHTMLTableSectionElement(nodeInfo.forget());
   MOZ_ASSERT(newBody);
 
-  nsIContent* referenceNode = nullptr;
+  nsCOMPtr<nsIContent> referenceNode = nullptr;
   for (nsIContent* child = nsINode::GetLastChild();
        child;
        child = child->GetPreviousSibling()) {
     if (child->IsHTMLElement(nsGkAtoms::tbody)) {
       referenceNode = child->GetNextSibling();
       break;
     }
   }
@@ -610,17 +611,18 @@ HTMLTableElement::InsertRow(int32_t aInd
       nsContentUtils::NameChanged(mNodeInfo, nsGkAtoms::tr,
                                   getter_AddRefs(nodeInfo));
 
       newRow = NS_NewHTMLTableRowElement(nodeInfo.forget());
       if (newRow) {
         HTMLTableSectionElement* section =
           static_cast<HTMLTableSectionElement*>(rowGroup.get());
         nsIHTMLCollection* rows = section->Rows();
-        rowGroup->InsertBefore(*newRow, rows->Item(0), aError);
+        nsCOMPtr<nsINode> refNode = rows->Item(0);
+        rowGroup->InsertBefore(*newRow, refNode, aError);
       }
     }
   }
 
   return newRow.forget();
 }
 
 void
--- a/dom/html/HTMLTableElement.h
+++ b/dom/html/HTMLTableElement.h
@@ -54,17 +54,18 @@ public:
   {
     if (aTHead && !aTHead->IsHTMLElement(nsGkAtoms::thead)) {
       aError.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR);
       return;
     }
 
     DeleteTHead();
     if (aTHead) {
-      nsINode::InsertBefore(*aTHead, nsINode::GetFirstChild(), aError);
+      nsCOMPtr<nsINode> refNode = nsINode::GetFirstChild();
+      nsINode::InsertBefore(*aTHead, refNode, aError);
     }
   }
   already_AddRefed<nsGenericHTMLElement> CreateTHead();
 
   void DeleteTHead();
 
   HTMLTableSectionElement* GetTFoot() const
   {
--- a/dom/html/HTMLTableSectionElement.cpp
+++ b/dom/html/HTMLTableSectionElement.cpp
@@ -86,17 +86,18 @@ HTMLTableSectionElement::InsertRow(int32
   RefPtr<nsGenericHTMLElement> rowContent =
     NS_NewHTMLTableRowElement(nodeInfo.forget());
   if (!rowContent) {
     aError.Throw(NS_ERROR_OUT_OF_MEMORY);
     return nullptr;
   }
 
   if (doInsert) {
-    nsINode::InsertBefore(*rowContent, rows->Item(aIndex), aError);
+    nsCOMPtr<nsINode> refNode = rows->Item(aIndex);
+    nsINode::InsertBefore(*rowContent, refNode, aError);
   } else {
     nsINode::AppendChild(*rowContent, aError);
   }
   return rowContent.forget();
 }
 
 void
 HTMLTableSectionElement::DeleteRow(int32_t aValue, ErrorResult& aError)
--- a/dom/html/UndoManager.cpp
+++ b/dom/html/UndoManager.cpp
@@ -438,17 +438,18 @@ UndoContentInsert::RedoTransaction()
   }
 
   // Check to see if next sibling has same parent.
   if (mNextNode && mNextNode->GetParentNode() != mContent) {
     return NS_OK;
   }
 
   IgnoredErrorResult error;
-  mContent->InsertBefore(*mChild, mNextNode, error);
+  nsCOMPtr<nsIContent> refNode = mNextNode;
+  mContent->InsertBefore(*mChild, refNode, error);
   return NS_OK;
 }
 
 nsresult
 UndoContentInsert::UndoTransaction()
 {
   if (!mChild) {
     return NS_ERROR_UNEXPECTED;
@@ -532,17 +533,18 @@ UndoContentRemove::UndoTransaction()
   }
 
   // Make sure next sibling is still under same parent.
   if (mNextNode && mNextNode->GetParentNode() != mContent) {
     return NS_OK;
   }
 
   IgnoredErrorResult error;
-  mContent->InsertBefore(*mChild, mNextNode, error);
+  nsCOMPtr<nsIContent> refNode = mNextNode;
+  mContent->InsertBefore(*mChild, refNode, error);
   return NS_OK;
 }
 
 nsresult
 UndoContentRemove::RedoTransaction()
 {
   if (!mChild) {
     return NS_ERROR_UNEXPECTED;
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -3093,20 +3093,26 @@ TabChild::DidRequestComposite(const Time
   if (!docShellComPtr) {
     return;
   }
 
   nsDocShell* docShell = static_cast<nsDocShell*>(docShellComPtr.get());
   RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
 
   if (timelines && timelines->HasConsumer(docShell)) {
+    // Since we're assuming that it's impossible for content JS to directly
+    // trigger a synchronous paint, we can avoid capturing a stack trace here,
+    // which means we won't run into JS engine reentrancy issues like bug
+    // 1310014.
     timelines->AddMarkerForDocShell(docShell,
-      "CompositeForwardTransaction", aCompositeReqStart, MarkerTracingType::START);
+      "CompositeForwardTransaction", aCompositeReqStart,
+      MarkerTracingType::START, MarkerStackRequest::NO_STACK);
     timelines->AddMarkerForDocShell(docShell,
-      "CompositeForwardTransaction", aCompositeReqEnd, MarkerTracingType::END);
+      "CompositeForwardTransaction", aCompositeReqEnd,
+      MarkerTracingType::END, MarkerStackRequest::NO_STACK);
   }
 }
 
 void
 TabChild::ClearCachedResources()
 {
   MOZ_ASSERT(mPuppetWidget);
   MOZ_ASSERT(mPuppetWidget->GetLayerManager());
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -3184,17 +3184,16 @@ public:
   NS_IMETHOD GetTopWindow(mozIDOMWindowProxy**) NO_IMPL
   NS_IMETHOD GetTopFrameElement(nsIDOMElement** aElement) override
   {
     nsCOMPtr<nsIDOMElement> elem = do_QueryInterface(mElement);
     elem.forget(aElement);
     return NS_OK;
   }
   NS_IMETHOD GetNestedFrameId(uint64_t*) NO_IMPL
-  NS_IMETHOD IsAppOfType(uint32_t, bool*) NO_IMPL
   NS_IMETHOD GetIsContent(bool*) NO_IMPL
   NS_IMETHOD GetUsePrivateBrowsing(bool*) NO_IMPL
   NS_IMETHOD SetUsePrivateBrowsing(bool) NO_IMPL
   NS_IMETHOD SetPrivateBrowsing(bool) NO_IMPL
   NS_IMETHOD GetIsInIsolatedMozBrowserElement(bool*) NO_IMPL
   NS_IMETHOD GetAppId(uint32_t*) NO_IMPL
   NS_IMETHOD GetOriginAttributes(JS::MutableHandleValue) NO_IMPL
   NS_IMETHOD GetUseRemoteTabs(bool*) NO_IMPL
--- a/dom/locales/en-US/chrome/layout/css.properties
+++ b/dom/locales/en-US/chrome/layout/css.properties
@@ -93,16 +93,18 @@ PEPseudoClassNoArg=Missing argument in pseudo-class ‘%1$S’.
 PEPseudoClassNotUserAction=Expected end of selector or a user action pseudo-class after pseudo-element but found pseudo-class ‘%1$S’.
 PESelectorEOF=selector
 PEBadDeclBlockStart=Expected ‘{’ to begin declaration block but found ‘%1$S’.
 PEColorEOF=color
 PEColorNotColor=Expected color but found ‘%1$S’.
 PEColorComponentEOF=color component
 PEExpectedPercent=Expected a percentage but found ‘%1$S’.
 PEExpectedInt=Expected an integer but found ‘%1$S’.
+PEExpectedNumberOrAngle=Expected a number or an angle but found ‘%1$S’.
+PEExpectedNumberOrPercent=Expected a number or a percentage but found ‘%1$S’.
 PEColorBadRGBContents=Expected number or percentage in rgb() but found ‘%1$S’.
 PEColorComponentBadTerm=Expected ‘%2$S’ but found ‘%1$S’.
 PEColorHueEOF=hue
 PEExpectedComma=Expected ‘,’ but found ‘%1$S’.
 PEColorSaturationEOF=saturation
 PEColorLightnessEOF=lightness
 PEColorOpacityEOF=opacity in color value
 PEExpectedNumber=Expected a number but found ‘%1$S’.
--- a/dom/manifest/test/test_ManifestProcessor_background_color.html
+++ b/dom/manifest/test/test_ManifestProcessor_background_color.html
@@ -25,53 +25,77 @@ typeTests.forEach(type => {
   is(result.background_color, undefined, `Expect non-string background_color to be undefined: ${typeof type}.`);
 });
 
 var validThemeColors = [
   'maroon',
   '#f00',
   '#ff0000',
   'rgb(255,0,0)',
+  'rgb(255,0,0,1)',
+  'rgb(255,0,0,1.0)',
+  'rgb(255,0,0,100%)',
+  'rgb(255 0 0)',
+  'rgb(255 0 0 / 1)',
+  'rgb(255 0 0 / 1.0)',
+  'rgb(255 0 0 / 100%)',
   'rgb(100%, 0%, 0%)',
-  'rgb(255,0,0)',
+  'rgb(100%, 0%, 0%, 1)',
+  'rgb(100%, 0%, 0%, 1.0)',
+  'rgb(100%, 0%, 0%, 100%)',
+  'rgb(100% 0% 0%)',
+  'rgb(100% 0% 0% / 1)',
+  'rgb(100%, 0%, 0%, 1.0)',
+  'rgb(100%, 0%, 0%, 100%)',
   'rgb(300,0,0)',
+  'rgb(300 0 0)',
   'rgb(255,-10,0)',
   'rgb(110%, 0%, 0%)',
-  'rgb(255,0,0)',
+  'rgba(255,0,0)',
   'rgba(255,0,0,1)',
-  'rgb(100%,0%,0%)',
+  'rgba(255 0 0 / 1)',
   'rgba(100%,0%,0%,1)',
   'rgba(0,0,255,0.5)',
   'rgba(100%, 50%, 0%, 0.1)',
   'hsl(120, 100%, 50%)',
-  'hsla(120, 100%, 50%, 1)',
+  'hsl(120 100% 50%)',
+  'hsl(120, 100%, 50%, 1.0)',
+  'hsl(120 100% 50% / 1.0)',
+  'hsla(120, 100%, 50%)',
+  'hsla(120 100% 50%)',
+  'hsla(120, 100%, 50%, 1.0)',
+  'hsla(120 100% 50% / 1.0)',
+  'hsl(120deg, 100%, 50%)',
+  'hsl(133.33333333grad, 100%, 50%)',
+  'hsl(2.0943951024rad, 100%, 50%)',
+  'hsl(0.3333333333turn, 100%, 50%)',
 ];
 
 validThemeColors.forEach(background_color => {
   data.jsonText = JSON.stringify({
     background_color: background_color
   });
   var result = processor.process(data);
 
   is(result.background_color, background_color, `Expect background_color to be returned: ${background_color}.`);
 });
 
 var invalidThemeColors = [
   'marooon',
   'f000000',
   '#ff00000',
-  'rgb(255 0 0)',
   'rgb(100, 0%, 0%)',
   'rgb(255,0)',
-  'rgb(300 0 0)',
   'rbg(255,-10,0)',
   'rgb(110, 0%, 0%)',
   '(255,0,0) }',
   'rgba(255)',
   ' rgb(100%,0%,0%) }',
+  'hsl(120, 100%, 50)',
+  'hsl(120, 100%, 50.0)',
   'hsl 120, 100%, 50%',
   'hsla{120, 100%, 50%, 1}',
 ]
 
 invalidThemeColors.forEach(background_color => {
   data.jsonText = JSON.stringify({
     background_color: background_color
   });
--- a/dom/manifest/test/test_ManifestProcessor_theme_color.html
+++ b/dom/manifest/test/test_ManifestProcessor_theme_color.html
@@ -25,53 +25,77 @@ typeTests.forEach(type => {
   is(result.theme_color, undefined, `Expect non-string theme_color to be undefined: ${typeof type}.`);
 });
 
 var validThemeColors = [
   'maroon',
   '#f00',
   '#ff0000',
   'rgb(255,0,0)',
+  'rgb(255,0,0,1)',
+  'rgb(255,0,0,1.0)',
+  'rgb(255,0,0,100%)',
+  'rgb(255 0 0)',
+  'rgb(255 0 0 / 1)',
+  'rgb(255 0 0 / 1.0)',
+  'rgb(255 0 0 / 100%)',
   'rgb(100%, 0%, 0%)',
-  'rgb(255,0,0)',
+  'rgb(100%, 0%, 0%, 1)',
+  'rgb(100%, 0%, 0%, 1.0)',
+  'rgb(100%, 0%, 0%, 100%)',
+  'rgb(100% 0% 0%)',
+  'rgb(100% 0% 0% / 1)',
+  'rgb(100%, 0%, 0%, 1.0)',
+  'rgb(100%, 0%, 0%, 100%)',
   'rgb(300,0,0)',
+  'rgb(300 0 0)',
   'rgb(255,-10,0)',
   'rgb(110%, 0%, 0%)',
-  'rgb(255,0,0)',
+  'rgba(255,0,0)',
   'rgba(255,0,0,1)',
-  'rgb(100%,0%,0%)',
+  'rgba(255 0 0 / 1)',
   'rgba(100%,0%,0%,1)',
   'rgba(0,0,255,0.5)',
   'rgba(100%, 50%, 0%, 0.1)',
   'hsl(120, 100%, 50%)',
-  'hsla(120, 100%, 50%, 1)',
+  'hsl(120 100% 50%)',
+  'hsl(120, 100%, 50%, 1.0)',
+  'hsl(120 100% 50% / 1.0)',
+  'hsla(120, 100%, 50%)',
+  'hsla(120 100% 50%)',
+  'hsla(120, 100%, 50%, 1.0)',
+  'hsla(120 100% 50% / 1.0)',
+  'hsl(120deg, 100%, 50%)',
+  'hsl(133.33333333grad, 100%, 50%)',
+  'hsl(2.0943951024rad, 100%, 50%)',
+  'hsl(0.3333333333turn, 100%, 50%)',
 ];
 
 validThemeColors.forEach(theme_color => {
   data.jsonText = JSON.stringify({
     theme_color: theme_color
   });
   var result = processor.process(data);
 
   is(result.theme_color, theme_color, `Expect theme_color to be returned: ${theme_color}.`);
 });
 
 var invalidThemeColors = [
   'marooon',
   'f000000',
   '#ff00000',
-  'rgb(255 0 0)',
   'rgb(100, 0%, 0%)',
   'rgb(255,0)',
-  'rgb(300 0 0)',
   'rbg(255,-10,0)',
   'rgb(110, 0%, 0%)',
   '(255,0,0) }',
   'rgba(255)',
   ' rgb(100%,0%,0%) }',
+  'hsl(120, 100%, 50)',
+  'hsl(120, 100%, 50.0)',
   'hsl 120, 100%, 50%',
   'hsla{120, 100%, 50%, 1}',
 ]
 
 invalidThemeColors.forEach(theme_color => {
   data.jsonText = JSON.stringify({
     theme_color: theme_color
   });
--- a/dom/media/GraphDriver.cpp
+++ b/dom/media/GraphDriver.cpp
@@ -580,16 +580,42 @@ AudioCallbackDriver::AudioCallbackDriver
   STREAM_LOG(LogLevel::Debug, ("AudioCallbackDriver ctor for graph %p", aGraphImpl));
 }
 
 AudioCallbackDriver::~AudioCallbackDriver()
 {
   MOZ_ASSERT(mPromisesForOperation.IsEmpty());
 }
 
+bool IsMacbookOrMacbookAir()
+{
+#ifdef XP_MACOSX
+  size_t len = 0;
+  sysctlbyname("hw.model", NULL, &len, NULL, 0);
+  if (len) {
+    UniquePtr<char[]> model(new char[len]);
+    // This string can be
+    // MacBook%d,%d for a normal MacBook
+    // MacBookPro%d,%d for a MacBook Pro
+    // MacBookAir%d,%d for a Macbook Air
+    sysctlbyname("hw.model", model.get(), &len, NULL, 0);
+    char* substring = strstr(model.get(), "MacBook");
+    if (substring) {
+      const size_t offset = strlen("MacBook");
+      if (strncmp(model.get() + offset, "Air", len - offset) ||
+          isdigit(model[offset + 1])) {
+        return true;
+      }
+    }
+    return false;
+  }
+#endif
+  return false;
+}
+
 void
 AudioCallbackDriver::Init()
 {
   cubeb* cubebContext = CubebUtils::GetCubebContext();
   if (!cubebContext) {
     NS_WARNING("Could not get cubeb context.");
     if (!mFromFallback) {
       CubebUtils::ReportCubebStreamInitFailure(true);
@@ -633,16 +659,23 @@ AudioCallbackDriver::Init()
     latency_frames = latencyPref.value();
   } else {
     if (cubeb_get_min_latency(cubebContext, output, &latency_frames) != CUBEB_OK) {
       NS_WARNING("Could not get minimal latency from cubeb.");
       return;
     }
   }
 
+  // Macbook and MacBook air don't have enough CPU to run very low latency
+  // MediaStreamGraphs, cap the minimal latency to 512 frames int this case.
+  if (IsMacbookOrMacbookAir()) {
+    latency_frames = std::max((uint32_t) 512, latency_frames);
+  }
+
+
   input = output;
   input.channels = mInputChannels; // change to support optional stereo capture
 
   cubeb_stream* stream = nullptr;
   CubebUtils::AudioDeviceID input_id = nullptr, output_id = nullptr;
   // We have to translate the deviceID values to cubeb devid's since those can be
   // freed whenever enumerate is called.
   {
--- a/dom/media/MediaManager.cpp
+++ b/dom/media/MediaManager.cpp
@@ -5,27 +5,27 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "MediaManager.h"
 
 #include "MediaStreamGraph.h"
 #include "mozilla/dom/MediaStreamTrack.h"
 #include "GetUserMediaRequest.h"
 #include "MediaStreamListener.h"
+#include "nsArray.h"
 #include "nsContentUtils.h"
 #include "nsHashPropertyBag.h"
 #ifdef MOZ_WIDGET_GONK
 #include "nsIAudioManager.h"
 #endif
 #include "nsIEventTarget.h"
 #include "nsIUUIDGenerator.h"
 #include "nsIScriptGlobalObject.h"
 #include "nsIPermissionManager.h"
 #include "nsIPopupWindowManager.h"
-#include "nsISupportsArray.h"
 #include "nsIDocShell.h"
 #include "nsIDocument.h"
 #include "nsISupportsPrimitives.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsIIDNService.h"
 #include "nsNetCID.h"
 #include "nsNetUtil.h"
 #include "nsPrincipal.h"
@@ -2395,24 +2395,20 @@ if (privileged) {
       }
       if (!(*devices)->Length()) {
         RefPtr<MediaStreamError> error =
             new MediaStreamError(window, NS_LITERAL_STRING("NotFoundError"));
         onFailure->OnError(error);
         return;
       }
 
-      nsCOMPtr<nsISupportsArray> devicesCopy; // before we give up devices below
+      nsCOMPtr<nsIMutableArray> devicesCopy = nsArray::Create(); // before we give up devices below
       if (!askPermission) {
-        nsresult rv = NS_NewISupportsArray(getter_AddRefs(devicesCopy));
-        if (NS_WARN_IF(NS_FAILED(rv))) {
-          return;
-        }
         for (auto& device : **devices) {
-          rv = devicesCopy->AppendElement(device);
+          nsresult rv = devicesCopy->AppendElement(device, /*weak =*/ false);
           if (NS_WARN_IF(NS_FAILED(rv))) {
             return;
           }
         }
       }
 
       // Pass callbacks and MediaStreamListener along to GetUserMediaTask.
       RefPtr<GetUserMediaTask> task (new GetUserMediaTask(c, onSuccess.forget(),
@@ -3020,25 +3016,25 @@ MediaManager::Observe(nsISupports* aSubj
     mActiveCallbacks.Remove(key, getter_AddRefs(task));
     if (!task) {
       return NS_OK;
     }
 
     if (aSubject) {
       // A particular device or devices were chosen by the user.
       // NOTE: does not allow setting a device to null; assumes nullptr
-      nsCOMPtr<nsISupportsArray> array(do_QueryInterface(aSubject));
+      nsCOMPtr<nsIArray> array(do_QueryInterface(aSubject));
       MOZ_ASSERT(array);
       uint32_t len = 0;
-      array->Count(&len);
+      array->GetLength(&len);
       bool videoFound = false, audioFound = false;
       for (uint32_t i = 0; i < len; i++) {
-        nsCOMPtr<nsISupports> supports;
-        array->GetElementAt(i,getter_AddRefs(supports));
-        nsCOMPtr<nsIMediaDevice> device(do_QueryInterface(supports));
+        nsCOMPtr<nsIMediaDevice> device;
+        array->QueryElementAt(i, NS_GET_IID(nsIMediaDevice),
+                              getter_AddRefs(device));
         MOZ_ASSERT(device); // shouldn't be returning anything else...
         if (device) {
           nsString type;
           device->GetType(type);
           if (type.EqualsLiteral("video")) {
             if (!videoFound) {
               task->SetVideoDevice(static_cast<VideoDevice*>(device.get()));
               videoFound = true;
@@ -3122,24 +3118,21 @@ MediaManager::Observe(nsISupports* aSubj
     return NS_OK;
   }
 #endif
 
   return NS_OK;
 }
 
 nsresult
-MediaManager::GetActiveMediaCaptureWindows(nsISupportsArray** aArray)
+MediaManager::GetActiveMediaCaptureWindows(nsIArray** aArray)
 {
   MOZ_ASSERT(aArray);
-  nsISupportsArray* array;
-  nsresult rv = NS_NewISupportsArray(&array); // AddRefs
-  if (NS_FAILED(rv)) {
-    return rv;
-  }
+
+  nsCOMPtr<nsIMutableArray> array = nsArray::Create();
 
   for (auto iter = mActiveWindows.Iter(); !iter.Done(); iter.Next()) {
     const uint64_t& id = iter.Key();
     StreamListeners* listeners = iter.UserData();
 
     nsPIDOMWindowInner* window =
       nsGlobalWindow::GetInnerWindowWithId(id)->AsInner();
     MOZ_ASSERT(window);
@@ -3160,21 +3153,21 @@ MediaManager::GetActiveMediaCaptureWindo
             listener->CapturingScreen() || listener->CapturingWindow() ||
             listener->CapturingApplication()) {
           capturing = true;
           break;
         }
       }
     }
     if (capturing) {
-      array->AppendElement(window);
+      array->AppendElement(window, /*weak =*/ false);
     }
   }
 
-  *aArray = array;
+  array.forget(aArray);
   return NS_OK;
 }
 
 // XXX flags might be better...
 struct CaptureWindowStateData {
   bool *mVideo;
   bool *mAudio;
   bool *mScreenShare;
@@ -3323,43 +3316,43 @@ MediaManager::IterateWindowListeners(nsP
     }
   }
 }
 
 
 void
 MediaManager::StopMediaStreams()
 {
-  nsCOMPtr<nsISupportsArray> array;
+  nsCOMPtr<nsIArray> array;
   GetActiveMediaCaptureWindows(getter_AddRefs(array));
   uint32_t len;
-  array->Count(&len);
+  array->GetLength(&len);
   for (uint32_t i = 0; i < len; i++) {
-    nsCOMPtr<nsISupports> window;
-    array->GetElementAt(i, getter_AddRefs(window));
-    nsCOMPtr<nsPIDOMWindowInner> win(do_QueryInterface(window));
+    nsCOMPtr<nsPIDOMWindowInner> win;
+    array->QueryElementAt(i, NS_GET_IID(nsPIDOMWindowInner),
+                          getter_AddRefs(win));
     if (win) {
       OnNavigation(win->WindowID());
     }
   }
 }
 
 bool
 MediaManager::IsActivelyCapturingOrHasAPermission(uint64_t aWindowId)
 {
   // Does page currently have a gUM stream active?
 
-  nsCOMPtr<nsISupportsArray> array;
+  nsCOMPtr<nsIArray> array;
   GetActiveMediaCaptureWindows(getter_AddRefs(array));
   uint32_t len;
-  array->Count(&len);
+  array->GetLength(&len);
   for (uint32_t i = 0; i < len; i++) {
-    nsCOMPtr<nsISupports> window;
-    array->GetElementAt(i, getter_AddRefs(window));
-    nsCOMPtr<nsPIDOMWindowInner> win(do_QueryInterface(window));
+    nsCOMPtr<nsPIDOMWindowInner> win;
+    array->QueryElementAt(i, NS_GET_IID(nsPIDOMWindowInner),
+                          getter_AddRefs(win));
     if (win && win->WindowID() == aWindowId) {
       return true;
     }
   }
 
   // Or are persistent permissions (audio or video) granted?
 
   auto* window = nsGlobalWindow::GetInnerWindowWithId(aWindowId);
--- a/dom/media/MediaPermissionGonk.cpp
+++ b/dom/media/MediaPermissionGonk.cpp
@@ -1,21 +1,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 "MediaManager.h"
 #include "MediaPermissionGonk.h"
 
+#include "nsArray.h"
 #include "nsCOMPtr.h"
 #include "nsIContentPermissionPrompt.h"
 #include "nsIDocument.h"
 #include "nsIDOMNavigatorUserMedia.h"
 #include "nsIStringEnumerator.h"
-#include "nsISupportsArray.h"
 #include "nsJSUtils.h"
 #include "nsQueryObject.h"
 #include "nsPIDOMWindow.h"
 #include "nsTArray.h"
 #include "GetUserMediaRequest.h"
 #include "mozilla/dom/PBrowserChild.h"
 #include "mozilla/dom/MediaStreamTrackBinding.h"
 #include "mozilla/dom/MediaStreamError.h"
@@ -62,22 +62,20 @@ FindDeviceByName(nsTArray<nsCOMPtr<nsIMe
   return nullptr;
 }
 
 // Helper function for notifying permission granted
 static nsresult
 NotifyPermissionAllow(const nsAString &aCallID, nsTArray<nsCOMPtr<nsIMediaDevice> > &aDevices)
 {
   nsresult rv;
-  nsCOMPtr<nsISupportsArray> array;
-  rv = NS_NewISupportsArray(getter_AddRefs(array));
-  NS_ENSURE_SUCCESS(rv, rv);
+  nsCOMPtr<nsIMutableArray> array = nsArray::Create();
 
   for (uint32_t i = 0; i < aDevices.Length(); ++i) {
-    rv = array->AppendElement(aDevices.ElementAt(i));
+    rv = array->AppendElement(aDevices.ElementAt(i), /*weak =*/ false);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
   NS_ENSURE_TRUE(obs, NS_ERROR_FAILURE);
 
   return obs->NotifyObservers(array, "getUserMedia:response:allow",
                               aCallID.BeginReading());
--- a/dom/media/eme/MediaKeySystemAccessManager.cpp
+++ b/dom/media/eme/MediaKeySystemAccessManager.cpp
@@ -60,21 +60,16 @@ MediaKeySystemAccessManager::~MediaKeySy
   Shutdown();
 }
 
 void
 MediaKeySystemAccessManager::Request(DetailedPromise* aPromise,
                                      const nsAString& aKeySystem,
                                      const Sequence<MediaKeySystemConfiguration>& aConfigs)
 {
-  if (aKeySystem.IsEmpty() || aConfigs.IsEmpty()) {
-    aPromise->MaybeReject(NS_ERROR_DOM_INVALID_ACCESS_ERR,
-                          NS_LITERAL_CSTRING("Invalid keysystem type or invalid options sequence"));
-    return;
-  }
   Request(aPromise, aKeySystem, aConfigs, RequestType::Initial);
 }
 
 void
 MediaKeySystemAccessManager::Request(DetailedPromise* aPromise,
                                      const nsAString& aKeySystem,
                                      const Sequence<MediaKeySystemConfiguration>& aConfigs,
                                      RequestType aType)
--- a/dom/media/nsIMediaManager.idl
+++ b/dom/media/nsIMediaManager.idl
@@ -1,27 +1,27 @@
 /* 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 "nsISupports.idl"
 
-interface nsISupportsArray;
+interface nsIArray;
 interface nsIDOMWindow;
 
 %{C++
 #define NS_MEDIAMANAGERSERVICE_CID {0xabc622ea, 0x9655, 0x4123, {0x80, 0xd9, 0x22, 0x62, 0x1b, 0xdd, 0x54, 0x65}}
 #define MEDIAMANAGERSERVICE_CONTRACTID "@mozilla.org/mediaManagerService;1"
 %}
 
 [scriptable, builtinclass, uuid(24b23e01-33fd-401f-ba25-6e52658750b0)]
 interface nsIMediaManagerService : nsISupports
 {
   /* return a array of inner windows that have active captures */
-  readonly attribute nsISupportsArray activeMediaCaptureWindows;
+  readonly attribute nsIArray activeMediaCaptureWindows;
 
   /* Get the capture state for the given window and all descendant windows (iframes, etc) */
   void mediaCaptureWindowState(in nsIDOMWindow aWindow, out boolean aVideo, out boolean aAudio,
                                [optional] out boolean aScreenShare, [optional] out boolean aWindowShare,
                                [optional] out boolean aAppShare, [optional] out boolean aBrowserShare);
 
   /* Clear per-orgin list of persistent DeviceIds stored for enumerateDevices
      sinceTime is milliseconds since 1 January 1970 00:00:00 UTC. 0 = clear all */
--- a/dom/notification/DesktopNotification.cpp
+++ b/dom/notification/DesktopNotification.cpp
@@ -126,17 +126,18 @@ DesktopNotification::PostDesktopNotifica
   nsresult rv = alert->Init(uniqueName, mIconURL, mTitle,
                             mDescription,
                             true,
                             uniqueName,
                             NS_LITERAL_STRING("auto"),
                             EmptyString(),
                             EmptyString(),
                             principal,
-                            inPrivateBrowsing);
+                            inPrivateBrowsing,
+                            false /* requireInteraction */);
   NS_ENSURE_SUCCESS(rv, rv);
   return alerts->ShowAlert(alert, mObserver);
 }
 
 DesktopNotification::DesktopNotification(const nsAString & title,
                                          const nsAString & description,
                                          const nsAString & iconURL,
                                          nsPIDOMWindowInner* aWindow,
--- a/dom/notification/Notification.cpp
+++ b/dom/notification/Notification.cpp
@@ -923,16 +923,31 @@ NotificationTask::Run()
   } else {
     MOZ_CRASH("Invalid action");
   }
 
   MOZ_ASSERT(!mNotificationRef);
   return NS_OK;
 }
 
+bool
+Notification::RequireInteractionEnabled(JSContext* aCx, JSObject* aOjb)
+{
+  if (NS_IsMainThread()) {
+    return Preferences::GetBool("dom.webnotifications.requireinteraction.enabled", false);
+  }
+
+  WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(aCx);
+  if (!workerPrivate) {
+    return false;
+  }
+
+  return workerPrivate->DOMWorkerNotificationRIEnabled();
+}
+
 // static
 bool
 Notification::PrefEnabled(JSContext* aCx, JSObject* aObj)
 {
   if (NS_IsMainThread()) {
     return Preferences::GetBool("dom.webnotifications.enabled", false);
   }
 
@@ -954,21 +969,23 @@ Notification::IsGetEnabled(JSContext* aC
 {
   return NS_IsMainThread();
 }
 
 Notification::Notification(nsIGlobalObject* aGlobal, const nsAString& aID,
                            const nsAString& aTitle, const nsAString& aBody,
                            NotificationDirection aDir, const nsAString& aLang,
                            const nsAString& aTag, const nsAString& aIconUrl,
+                           bool aRequireInteraction,
                            const NotificationBehavior& aBehavior)
   : DOMEventTargetHelper(),
     mWorkerPrivate(nullptr), mObserver(nullptr),
     mID(aID), mTitle(aTitle), mBody(aBody), mDir(aDir), mLang(aLang),
-    mTag(aTag), mIconUrl(aIconUrl), mBehavior(aBehavior), mData(JS::NullValue()),
+    mTag(aTag), mIconUrl(aIconUrl), mRequireInteraction(aRequireInteraction),
+    mBehavior(aBehavior), mData(JS::NullValue()),
     mIsClosed(false), mIsStored(false), mTaskCount(0)
 {
   if (NS_IsMainThread()) {
     // We can only call this on the main thread because
     // Event::SetEventType() called down the call chain when dispatching events
     // using DOMEventTargetHelper::DispatchTrustedEvent() will assume the event
     // is a main thread event if it has a valid owner. It will then attempt to
     // fetch the atom for the event name which asserts main thread only.
@@ -1181,16 +1198,17 @@ Notification::CreateInternal(nsIGlobalOb
   }
 
   RefPtr<Notification> notification = new Notification(aGlobal, id, aTitle,
                                                          aOptions.mBody,
                                                          aOptions.mDir,
                                                          aOptions.mLang,
                                                          aOptions.mTag,
                                                          aOptions.mIcon,
+                                                         aOptions.mRequireInteraction,
                                                          aOptions.mMozbehavior);
   rv = notification->Init();
   NS_ENSURE_SUCCESS(rv, nullptr);
   return notification.forget();
 }
 
 Notification::~Notification()
 {
@@ -1813,30 +1831,36 @@ Notification::ShowInternal()
 #endif
 
   // In the case of IPC, the parent process uses the cookie to map to
   // nsIObserver. Thus the cookie must be unique to differentiate observers.
   nsString uniqueCookie = NS_LITERAL_STRING("notification:");
   uniqueCookie.AppendInt(sCount++);
   bool inPrivateBrowsing = IsInPrivateBrowsing();
 
+  bool requireInteraction = mRequireInteraction;
+  if (!Preferences::GetBool("dom.webnotifications.requireinteraction.enabled", false)) {
+    requireInteraction = false;
+  }
+
   nsAutoString alertName;
   GetAlertName(alertName);
   nsCOMPtr<nsIAlertNotification> alert =
     do_CreateInstance(ALERT_NOTIFICATION_CONTRACTID);
   NS_ENSURE_TRUE_VOID(alert);
   nsIPrincipal* principal = GetPrincipal();
   rv = alert->Init(alertName, iconUrl, mTitle, mBody,
                    true,
                    uniqueCookie,
                    DirectionToString(mDir),
                    mLang,
                    mDataAsBase64,
                    GetPrincipal(),
-                   inPrivateBrowsing);
+                   inPrivateBrowsing,
+                   requireInteraction);
   NS_ENSURE_SUCCESS_VOID(rv);
 
   if (isPersistent) {
     nsAutoString persistentData;
 
     JSONWriter w(MakeUnique<StringWriteFunc>(persistentData));
     w.Start();
 
@@ -2356,16 +2380,22 @@ Notification::GetOrigin(nsIPrincipal* aP
       do_GetService("@mozilla.org/AppsService;1", &rv);
     NS_ENSURE_SUCCESS(rv, rv);
     appsService->GetManifestURLByLocalId(appId, aOrigin);
   }
 
   return NS_OK;
 }
 
+bool
+Notification::RequireInteraction() const
+{
+  return mRequireInteraction;
+}
+
 void
 Notification::GetData(JSContext* aCx,
                       JS::MutableHandle<JS::Value> aRetval)
 {
   if (mData.isNull() && !mDataAsBase64.IsEmpty()) {
     nsresult rv;
     RefPtr<nsStructuredCloneContainer> container =
       new nsStructuredCloneContainer();
@@ -2687,17 +2717,17 @@ Notification::ShowPersistentNotification
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
   // We check permission here rather than pass the Promise to NotificationTask
   // which leads to uglier code.
   NotificationPermission permission = GetPermission(aGlobal, aRv);
 
-  // "If permission for notification’s origin is not "granted", reject promise with a TypeError exception, and terminate these substeps."
+  // "If permission for notification's origin is not "granted", reject promise with a TypeError exception, and terminate these substeps."
   if (NS_WARN_IF(aRv.Failed()) || permission == NotificationPermission::Denied) {
     ErrorResult result;
     result.ThrowTypeError<MSG_NOTIFICATION_PERMISSION_DENIED>();
     p->MaybeReject(result);
     return p.forget();
   }
 
   // "Otherwise, resolve promise with undefined."
--- a/dom/notification/Notification.h
+++ b/dom/notification/Notification.h
@@ -150,16 +150,17 @@ public:
   IMPL_EVENT_HANDLER(show)
   IMPL_EVENT_HANDLER(error)
   IMPL_EVENT_HANDLER(close)
 
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(Notification, DOMEventTargetHelper)
   NS_DECL_NSIOBSERVER
 
+  static bool RequireInteractionEnabled(JSContext* aCx, JSObject* aObj);
   static bool PrefEnabled(JSContext* aCx, JSObject* aObj);
   // Returns if Notification.get() is allowed for the current global.
   static bool IsGetEnabled(JSContext* aCx, JSObject* aObj);
 
   static already_AddRefed<Notification> Constructor(const GlobalObject& aGlobal,
                                                     const nsAString& aTitle,
                                                     const NotificationOptions& aOption,
                                                     ErrorResult& aRv);
@@ -275,16 +276,18 @@ public:
 
   nsPIDOMWindowInner* GetParentObject()
   {
     return GetOwner();
   }
 
   virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
+  bool RequireInteraction() const;
+
   void GetData(JSContext* aCx, JS::MutableHandle<JS::Value> aRetval);
 
   void InitFromJSVal(JSContext* aCx, JS::Handle<JS::Value> aData, ErrorResult& aRv);
 
   void InitFromBase64(const nsAString& aData, ErrorResult& aRv);
 
   void AssertIsOnTargetThread() const
   {
@@ -324,16 +327,17 @@ public:
 
   static nsresult RemovePermission(nsIPrincipal* aPrincipal);
   static nsresult OpenSettings(nsIPrincipal* aPrincipal);
 protected:
   Notification(nsIGlobalObject* aGlobal, const nsAString& aID,
                const nsAString& aTitle, const nsAString& aBody,
                NotificationDirection aDir, const nsAString& aLang,
                const nsAString& aTag, const nsAString& aIconUrl,
+               bool aRequireNotification,
                const NotificationBehavior& aBehavior);
 
   static already_AddRefed<Notification> CreateInternal(nsIGlobalObject* aGlobal,
                                                        const nsAString& aID,
                                                        const nsAString& aTitle,
                                                        const NotificationOptions& aOptions);
 
   nsresult Init();
@@ -392,16 +396,17 @@ protected:
 
   const nsString mID;
   const nsString mTitle;
   const nsString mBody;
   const NotificationDirection mDir;
   const nsString mLang;
   const nsString mTag;
   const nsString mIconUrl;
+  const bool mRequireInteraction;
   nsString mDataAsBase64;
   const NotificationBehavior mBehavior;
 
   // It's null until GetData is first called
   JS::Heap<JS::Value> mData;
 
   nsString mAlertName;
   nsString mScope;
--- a/dom/presentation/PresentationReceiver.h
+++ b/dom/presentation/PresentationReceiver.h
@@ -38,17 +38,17 @@ public:
   // WebIDL (public APIs)
   already_AddRefed<Promise> GetConnectionList(ErrorResult& aRv);
 
 private:
   explicit PresentationReceiver(nsPIDOMWindowInner* aWindow);
 
   virtual ~PresentationReceiver();
 
-  bool Init();
+  MOZ_IS_CLASS_INIT bool Init();
 
   void Shutdown();
 
   void CreateConnectionList();
 
   // Store the inner window ID for |UnregisterRespondingListener| call in
   // |Shutdown| since the inner window may not exist at that moment.
   uint64_t mWindowId;
--- a/dom/security/nsCSPContext.cpp
+++ b/dom/security/nsCSPContext.cpp
@@ -960,17 +960,18 @@ nsCSPContext::SendReports(nsISupports* a
     // there's no loadgroup, AsyncOpen will fail on process-split necko (since
     // the channel cannot query the iTabChild).
     rv = reportChannel->SetLoadGroup(mCallingChannelLoadGroup);
     NS_ENSURE_SUCCESS(rv, rv);
 
     // wire in the string input stream to send the report
     nsCOMPtr<nsIStringInputStream> sis(do_CreateInstance(NS_STRINGINPUTSTREAM_CONTRACTID));
     NS_ASSERTION(sis, "nsIStringInputStream is needed but not available to send CSP violation reports");
-    rv = sis->SetData(NS_ConvertUTF16toUTF8(csp_report).get(), csp_report.Length());
+    nsAutoCString utf8CSPReport = NS_ConvertUTF16toUTF8(csp_report);
+    rv = sis->SetData(utf8CSPReport.get(), utf8CSPReport.Length());
     NS_ENSURE_SUCCESS(rv, rv);
 
     nsCOMPtr<nsIUploadChannel> uploadChannel(do_QueryInterface(reportChannel));
     if (!uploadChannel) {
       // It's possible the URI provided can't be uploaded to, in which case
       // we skip this one. We'll already have warned about a non-HTTP URI earlier.
       continue;
     }
--- a/dom/security/test/unit/test_csp_reports.js
+++ b/dom/security/test/unit/test_csp_reports.js
@@ -116,31 +116,36 @@ function run_test() {
                                        "", // aContent
                                        0); // aLineNumber
 
         // this is not a report only policy, so it better block inline scripts
         do_check_false(inlineOK);
       });
 
   // test that eval violations cause a report.
-  makeTest(1, {"blocked-uri": "self"}, false,
+  makeTest(1, {"blocked-uri": "self",
+               // JSON script-sample is UTF8 encoded
+               "script-sample" : "\xc2\xa3\xc2\xa5\xc2\xb5\xe5\x8c\x97\xf0\xa0\x9d\xb9"}, false,
       function(csp) {
         let evalOK = true, oReportViolation = {'value': false};
         evalOK = csp.getAllowsEval(oReportViolation);
 
         // this is not a report only policy, so it better block eval
         do_check_false(evalOK);
         // ... and cause reports to go out
         do_check_true(oReportViolation.value);
 
         if (oReportViolation.value) {
           // force the logging, since the getter doesn't.
           csp.logViolationDetails(Ci.nsIContentSecurityPolicy.VIOLATION_TYPE_EVAL,
                                   selfuri.asciiSpec,
-                                  "script sample",
+                                  // sending UTF-16 script sample to make sure
+                                  // csp report in JSON is not cut-off, please
+                                  // note that JSON is UTF8 encoded.
+                                  "\u00a3\u00a5\u00b5\u5317\ud841\udf79",
                                   1);
         }
       });
 
   makeTest(2, {"blocked-uri": "http://blocked.test"}, false,
       function(csp) {
         // shouldLoad creates and sends out the report here.
         csp.shouldLoad(Ci.nsIContentPolicy.TYPE_SCRIPT,
--- a/dom/svg/SVGFEImageElement.cpp
+++ b/dom/svg/SVGFEImageElement.cpp
@@ -90,16 +90,25 @@ SVGFEImageElement::LoadSVGImage(bool aFo
       return NS_OK;
     }
   }
 
   return LoadImage(href, aForce, aNotify, eImageLoadType_Normal);
 }
 
 //----------------------------------------------------------------------
+// EventTarget methods:
+
+void
+SVGFEImageElement::AsyncEventRunning(AsyncEventDispatcher* aEvent)
+{
+  nsImageLoadingContent::AsyncEventRunning(aEvent);
+}
+
+//----------------------------------------------------------------------
 // nsIContent methods:
 
 NS_IMETHODIMP_(bool)
 SVGFEImageElement::IsAttributeMapped(const nsIAtom* name) const
 {
   static const MappedAttributeEntry* const map[] = {
     sGraphicsMap
   };
--- a/dom/svg/SVGFEImageElement.h
+++ b/dom/svg/SVGFEImageElement.h
@@ -33,16 +33,19 @@ protected:
   virtual JSObject* WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) override;
 
 public:
   virtual bool SubregionIsUnionOfRegions() override { return false; }
 
   // interfaces:
   NS_DECL_ISUPPORTS_INHERITED
 
+  // EventTarget
+  virtual void AsyncEventRunning(AsyncEventDispatcher* aEvent) override;
+
   virtual FilterPrimitiveDescription
     GetPrimitiveDescription(nsSVGFilterInstance* aInstance,
                             const IntRect& aFilterSubregion,
                             const nsTArray<bool>& aInputsAreTainted,
                             nsTArray<RefPtr<SourceSurface>>& aInputImages) override;
   virtual bool AttributeAffectsRendering(
           int32_t aNameSpaceID, nsIAtom* aAttribute) const override;
   virtual nsSVGString& GetResultImageName() override { return mStringAttributes[RESULT]; }
--- a/dom/svg/SVGImageElement.cpp
+++ b/dom/svg/SVGImageElement.cpp
@@ -132,16 +132,25 @@ SVGImageElement::LoadSVGImage(bool aForc
 
   if (baseURI && !href.IsEmpty())
     NS_MakeAbsoluteURI(href, href, baseURI);
 
   return LoadImage(href, aForce, aNotify, eImageLoadType_Normal);
 }
 
 //----------------------------------------------------------------------
+// EventTarget methods:
+
+void
+SVGImageElement::AsyncEventRunning(AsyncEventDispatcher* aEvent)
+{
+  nsImageLoadingContent::AsyncEventRunning(aEvent);
+}
+
+//----------------------------------------------------------------------
 // nsIContent methods:
 
 nsresult
 SVGImageElement::AfterSetAttr(int32_t aNamespaceID, nsIAtom* aName,
                               const nsAttrValue* aValue, bool aNotify)
 {
   if (aName == nsGkAtoms::href &&
       (aNamespaceID == kNameSpaceID_None ||
--- a/dom/svg/SVGImageElement.h
+++ b/dom/svg/SVGImageElement.h
@@ -36,16 +36,19 @@ protected:
   friend nsresult (::NS_NewSVGImageElement(nsIContent **aResult,
                                            already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo));
 
 public:
   // interfaces:
 
   NS_DECL_ISUPPORTS_INHERITED
 
+  // EventTarget
+  virtual void AsyncEventRunning(AsyncEventDispatcher* aEvent) override;
+
   // nsIContent interface
   virtual nsresult AfterSetAttr(int32_t aNamespaceID, nsIAtom* aName,
                                 const nsAttrValue* aValue, bool aNotify) override;
   virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent,
                               nsIContent* aBindingParent,
                               bool aCompileEventHandlers) override;
   virtual void UnbindFromTree(bool aDeep, bool aNullParent) override;
 
--- a/dom/telephony/TelephonyCall.h
+++ b/dom/telephony/TelephonyCall.h
@@ -17,32 +17,32 @@
 
 class nsPIDOMWindowInner;
 
 namespace mozilla {
 namespace dom {
 
 class TelephonyCall final : public DOMEventTargetHelper
 {
-  RefPtr<Telephony> mTelephony;
-  RefPtr<TelephonyCallGroup> mGroup;
+  MOZ_INIT_OUTSIDE_CTOR RefPtr<Telephony> mTelephony;
+  MOZ_INIT_OUTSIDE_CTOR RefPtr<TelephonyCallGroup> mGroup;
 
-  RefPtr<TelephonyCallId> mId;
+  MOZ_INIT_OUTSIDE_CTOR RefPtr<TelephonyCallId> mId;
   RefPtr<TelephonyCallId> mSecondId;
 
-  uint32_t mServiceId;
-  TelephonyCallState mState;
-  bool mEmergency;
-  RefPtr<DOMError> mError;
+  MOZ_INIT_OUTSIDE_CTOR uint32_t mServiceId;
+  MOZ_INIT_OUTSIDE_CTOR TelephonyCallState mState;
+  MOZ_INIT_OUTSIDE_CTOR bool mEmergency;
+  MOZ_INIT_OUTSIDE_CTOR RefPtr<DOMError> mError;
   Nullable<TelephonyCallDisconnectedReason> mDisconnectedReason;
 
-  bool mSwitchable;
-  bool mMergeable;
+  MOZ_INIT_OUTSIDE_CTOR bool mSwitchable;
+  MOZ_INIT_OUTSIDE_CTOR bool mMergeable;
 
-  uint32_t mCallIndex;
+  MOZ_INIT_OUTSIDE_CTOR uint32_t mCallIndex;
   bool mLive;
 
 public:
   NS_DECL_ISUPPORTS_INHERITED
   NS_REALLY_FORWARD_NSIDOMEVENTTARGET(DOMEventTargetHelper)
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(TelephonyCall,
                                            DOMEventTargetHelper)
   friend class Telephony;
--- a/dom/telephony/TelephonyCallGroup.h
+++ b/dom/telephony/TelephonyCallGroup.h
@@ -17,17 +17,17 @@ namespace dom {
 class TelephonyCallGroup final : public DOMEventTargetHelper
 {
   RefPtr<Telephony> mTelephony;
 
   nsTArray<RefPtr<TelephonyCall> > mCalls;
 
   RefPtr<CallsList> mCallsList;
 
-  TelephonyCallGroupState mState;
+  MOZ_INIT_OUTSIDE_CTOR TelephonyCallGroupState mState;
 
 public:
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(TelephonyCallGroup,
                                            DOMEventTargetHelper)
 
   friend class Telephony;
 
--- a/dom/tests/mochitest/general/test_interfaces.html
+++ b/dom/tests/mochitest/general/test_interfaces.html
@@ -264,17 +264,17 @@ var interfaceNamesInGlobalScope =
     "CSSSupportsRule",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "CSSTransition", release: false},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "CSSValue",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "CSSValueList",
 // IMPORTANT: Do not change this list without review from a DOM peer!
-    "CustomElementsRegistry",
+    "CustomElementRegistry",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "CustomEvent",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "DataChannel",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "DataTransfer",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "DataTransferItem",
--- a/dom/vr/VRDisplay.cpp
+++ b/dom/vr/VRDisplay.cpp
@@ -499,30 +499,51 @@ VRDisplay::Capabilities()
 }
 
 VRStageParameters*
 VRDisplay::GetStageParameters()
 {
   return mStageParameters;
 }
 
+void
+VRDisplay::UpdateFrameInfo()
+{
+  /**
+   * The WebVR 1.1 spec Requires that VRDisplay.getPose and VRDisplay.getFrameData
+   * must return the same values until the next VRDisplay.submitFrame.
+   *
+   * mFrameInfo is marked dirty at the end of the frame or start of a new
+   * composition and lazily created here in order to receive mid-frame
+   * pose-prediction updates while still ensuring conformance to the WebVR spec
+   * requirements.
+   *
+   * If we are not presenting WebVR content, the frame will never end and we should
+   * return the latest frame data always.
+   */
+  if (mFrameInfo.IsDirty() || !mPresentation) {
+    gfx::VRHMDSensorState state = mClient->GetSensorState();
+    const gfx::VRDisplayInfo& info = mClient->GetDisplayInfo();
+    mFrameInfo.Update(info, state, mDepthNear, mDepthFar);
+  }
+}
+
 bool
 VRDisplay::GetFrameData(VRFrameData& aFrameData)
 {
-  gfx::VRHMDSensorState state = mClient->GetSensorState();
-  const gfx::VRDisplayInfo& info = mClient->GetDisplayInfo();
-  aFrameData.Update(info, state, mDepthNear, mDepthFar);
+  UpdateFrameInfo();
+  aFrameData.Update(mFrameInfo);
   return true;
 }
 
 already_AddRefed<VRPose>
 VRDisplay::GetPose()
 {
-  gfx::VRHMDSensorState state = mClient->GetSensorState();
-  RefPtr<VRPose> obj = new VRPose(GetParentObject(), state);
+  UpdateFrameInfo();
+  RefPtr<VRPose> obj = new VRPose(GetParentObject(), mFrameInfo.mVRState);
 
   return obj.forget();
 }
 
 void
 VRDisplay::ResetPose()
 {
   mClient->ZeroSensor();
@@ -544,16 +565,17 @@ VRDisplay::RequestPresent(const nsTArray
   NS_ENSURE_TRUE(obs, nullptr);
 
   if (mClient->GetIsPresenting()) {
     // Only one presentation allowed per VRDisplay
     // on a first-come-first-serve basis.
     promise->MaybeRejectWithUndefined();
   } else {
     mPresentation = mClient->BeginPresentation(aLayers);
+    mFrameInfo.Clear();
 
     nsresult rv = obs->AddObserver(this, "inner-window-destroyed", false);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       mPresentation = nullptr;
       promise->MaybeRejectWithUndefined();
     } else {
       promise->MaybeResolve(JS::UndefinedHandleValue);
     }
@@ -623,25 +645,22 @@ VRDisplay::GetLayers(nsTArray<VRLayer>& 
   if (mPresentation) {
     mPresentation->GetDOMLayers(result);
   } else {
     result = nsTArray<VRLayer>();
   }
 }
 
 void
-VRDisplay::SubmitFrame(const Optional<NonNull<VRPose>>& aPose)
+VRDisplay::SubmitFrame()
 {
   if (mPresentation) {
-    if (aPose.WasPassed()) {
-      mPresentation->SubmitFrame(aPose.Value().FrameID());
-    } else {
-      mPresentation->SubmitFrame(0);
-    }
+    mPresentation->SubmitFrame();
   }
+  mFrameInfo.Clear();
 }
 
 int32_t
 VRDisplay::RequestAnimationFrame(FrameRequestCallback& aCallback,
 ErrorResult& aError)
 {
   gfx::VRManagerChild* vm = gfx::VRManagerChild::Get();
 
@@ -760,65 +779,74 @@ VRFrameData::LazyCreateMatrix(JS::Heap<J
     JS::ExposeObjectToActiveJS(aArray);
   }
   aRetval.set(aArray);
 }
 
 double
 VRFrameData::Timestamp() const
 {
-  return mVRState.timestamp * 1000.0f; // Converting from seconds to milliseconds
+  // Converting from seconds to milliseconds
+  return mFrameInfo.mVRState.timestamp * 1000.0f;
 }
 
 void
 VRFrameData::GetLeftProjectionMatrix(JSContext* aCx,
                                      JS::MutableHandle<JSObject*> aRetval,
                                      ErrorResult& aRv)
 {
-  LazyCreateMatrix(mLeftProjectionMatrix, mLeftProjection, aCx, aRetval, aRv);
+  LazyCreateMatrix(mLeftProjectionMatrix, mFrameInfo.mLeftProjection, aCx,
+                   aRetval, aRv);
 }
 
 void
 VRFrameData::GetLeftViewMatrix(JSContext* aCx,
                                JS::MutableHandle<JSObject*> aRetval,
                                ErrorResult& aRv)
 {
-  LazyCreateMatrix(mLeftViewMatrix, mLeftView, aCx, aRetval, aRv);
+  LazyCreateMatrix(mLeftViewMatrix, mFrameInfo.mLeftView, aCx, aRetval, aRv);
 }
 
 void
 VRFrameData::GetRightProjectionMatrix(JSContext* aCx,
                                       JS::MutableHandle<JSObject*> aRetval,
                                       ErrorResult& aRv)
 {
-  LazyCreateMatrix(mRightProjectionMatrix, mRightProjection, aCx, aRetval, aRv);
+  LazyCreateMatrix(mRightProjectionMatrix, mFrameInfo.mRightProjection, aCx,
+                   aRetval, aRv);
 }
 
 void
 VRFrameData::GetRightViewMatrix(JSContext* aCx,
                                 JS::MutableHandle<JSObject*> aRetval,
                                 ErrorResult& aRv)
 {
-  LazyCreateMatrix(mRightViewMatrix, mRightView, aCx, aRetval, aRv);
+  LazyCreateMatrix(mRightViewMatrix, mFrameInfo.mRightView, aCx, aRetval, aRv);
 }
 
 void
-VRFrameData::Update(const gfx::VRDisplayInfo& aInfo,
-                    const gfx::VRHMDSensorState& aState,
-                    float aDepthNear,
-                    float aDepthFar)
+VRFrameData::Update(const VRFrameInfo& aFrameInfo)
 {
-  mVRState = aState;
+  mFrameInfo = aFrameInfo;
 
   mLeftProjectionMatrix = nullptr;
   mLeftViewMatrix = nullptr;
   mRightProjectionMatrix = nullptr;
   mRightViewMatrix = nullptr;
 
-  mPose = new VRPose(GetParentObject(), aState);
+  mPose = new VRPose(GetParentObject(), mFrameInfo.mVRState);
+}
+
+void
+VRFrameInfo::Update(const gfx::VRDisplayInfo& aInfo,
+                    const gfx::VRHMDSensorState& aState,
+                    float aDepthNear,
+                    float aDepthFar)
+{
+  mVRState = aState;
 
   gfx::Quaternion qt;
   if (mVRState.flags & gfx::VRDisplayCapabilityFlags::Cap_Orientation) {
     qt.x = mVRState.orientation[0];
     qt.y = mVRState.orientation[1];
     qt.z = mVRState.orientation[2];
     qt.w = mVRState.orientation[3];
   }
@@ -843,13 +871,29 @@ VRFrameData::Update(const gfx::VRDisplay
   if (fabs(aDepthFar - aDepthNear) < kEpsilon) {
     aDepthFar = aDepthNear + kEpsilon;
   }
 
   const gfx::VRFieldOfView leftFOV = aInfo.mEyeFOV[gfx::VRDisplayInfo::Eye_Left];
   mLeftProjection = leftFOV.ConstructProjectionMatrix(aDepthNear, aDepthFar, true);
   const gfx::VRFieldOfView rightFOV = aInfo.mEyeFOV[gfx::VRDisplayInfo::Eye_Right];
   mRightProjection = rightFOV.ConstructProjectionMatrix(aDepthNear, aDepthFar, true);
+}
 
+VRFrameInfo::VRFrameInfo()
+{
+  mVRState.Clear();
+}
+
+bool
+VRFrameInfo::IsDirty()
+{
+  return mVRState.timestamp == 0;
+}
+
+void
+VRFrameInfo::Clear()
+{
+  mVRState.Clear();
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/vr/VRDisplay.h
+++ b/dom/vr/VRDisplay.h
@@ -140,30 +140,47 @@ protected:
   JS::Heap<JSObject*> mLinearVelocity;
   JS::Heap<JSObject*> mLinearAcceleration;
   JS::Heap<JSObject*> mOrientation;
   JS::Heap<JSObject*> mAngularVelocity;
   JS::Heap<JSObject*> mAngularAcceleration;
 
 };
 
+struct VRFrameInfo
+{
+  VRFrameInfo();
+
+  void Update(const gfx::VRDisplayInfo& aInfo,
+              const gfx::VRHMDSensorState& aState,
+              float aDepthNear,
+              float aDepthFar);
+
+  void Clear();
+  bool IsDirty();
+
+  gfx::VRHMDSensorState mVRState;
+  gfx::Matrix4x4 mLeftProjection;
+  gfx::Matrix4x4 mLeftView;
+  gfx::Matrix4x4 mRightProjection;
+  gfx::Matrix4x4 mRightView;
+
+};
+
 class VRFrameData final : public nsWrapperCache
 {
 public:
   NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(VRFrameData)
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(VRFrameData)
 
   explicit VRFrameData(nsISupports* aParent);
   static already_AddRefed<VRFrameData> Constructor(const GlobalObject& aGlobal,
                                                    ErrorResult& aRv);
 
-  void Update(const gfx::VRDisplayInfo& aInfo,
-              const gfx::VRHMDSensorState& aState,
-              float aDepthNear,
-              float aDepthFar);
+  void Update(const VRFrameInfo& aFrameInfo);
 
   // WebIDL Members
   double Timestamp() const;
   void GetLeftProjectionMatrix(JSContext* aCx,
                                JS::MutableHandle<JSObject*> aRetval,
                                ErrorResult& aRv);
   void GetLeftViewMatrix(JSContext* aCx,
                          JS::MutableHandle<JSObject*> aRetval,
@@ -180,28 +197,23 @@ public:
   // WebIDL Boilerplate
   nsISupports* GetParentObject() const { return mParent; }
   virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
 protected:
   ~VRFrameData();
   nsCOMPtr<nsISupports> mParent;
 
-  gfx::VRHMDSensorState mVRState;
+  VRFrameInfo mFrameInfo;
   RefPtr<VRPose> mPose;
   JS::Heap<JSObject*> mLeftProjectionMatrix;
   JS::Heap<JSObject*> mLeftViewMatrix;
   JS::Heap<JSObject*> mRightProjectionMatrix;
   JS::Heap<JSObject*> mRightViewMatrix;
 
-  gfx::Matrix4x4 mLeftProjection;
-  gfx::Matrix4x4 mLeftView;
-  gfx::Matrix4x4 mRightProjection;
-  gfx::Matrix4x4 mRightView;
-
   void LazyCreateMatrix(JS::Heap<JSObject*>& aArray, gfx::Matrix4x4& aMat,
                         JSContext* aCx, JS::MutableHandle<JSObject*> aRetval,
                         ErrorResult& aRv);
 };
 
 class VRStageParameters final : public nsWrapperCache
 {
 public:
@@ -315,39 +327,49 @@ public:
     // XXX When we start sending depth buffers to VRLayer's we will want
     // to communicate this with the VRDisplayHost
     mDepthFar = aDepthFar;
   }
 
   already_AddRefed<Promise> RequestPresent(const nsTArray<VRLayer>& aLayers, ErrorResult& aRv);
   already_AddRefed<Promise> ExitPresent(ErrorResult& aRv);
   void GetLayers(nsTArray<VRLayer>& result);
-  void SubmitFrame(const Optional<NonNull<VRPose>>& aPose);
+  void SubmitFrame();
 
   int32_t RequestAnimationFrame(mozilla::dom::FrameRequestCallback& aCallback,
                                 mozilla::ErrorResult& aError);
   void CancelAnimationFrame(int32_t aHandle, mozilla::ErrorResult& aError);
 
 protected:
   VRDisplay(nsPIDOMWindowInner* aWindow, gfx::VRDisplayClient* aClient);
   virtual ~VRDisplay();
   virtual void LastRelease() override;
 
   void ExitPresentInternal();
+  void UpdateFrameInfo();
 
   RefPtr<gfx::VRDisplayClient> mClient;
 
   uint32_t mDisplayId;
   nsString mDisplayName;
 
   RefPtr<VRDisplayCapabilities> mCapabilities;
   RefPtr<VRStageParameters> mStageParameters;
 
   double mDepthNear;
   double mDepthFar;
 
   RefPtr<gfx::VRDisplayPresentation> mPresentation;
+
+  /**
+  * The WebVR 1.1 spec Requires that VRDisplay.getPose and VRDisplay.getFrameData
+  * must return the same values until the next VRDisplay.submitFrame.
+  * mFrameInfo is updated only on the first call to either function within one
+  * frame.  Subsequent calls before the next SubmitFrame or ExitPresent call
+  * will use these cached values.
+  */
+  VRFrameInfo mFrameInfo;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif
--- a/dom/webidl/CheckerboardReportService.webidl
+++ b/dom/webidl/CheckerboardReportService.webidl
@@ -40,9 +40,19 @@ interface CheckerboardReportService {
    * Gets the state of the apz.record_checkerboarding pref.
    */
   boolean isRecordingEnabled();
 
   /**
    * Sets the state of the apz.record_checkerboarding pref.
    */
   void setRecordingEnabled(boolean aEnabled);
+
+  /**
+   * Flush any in-progress checkerboard reports. Since this happens
+   * asynchronously, the caller may register an observer with the observer
+   * service to be notified when this operation is complete. The observer should
+   * listen for the topic "APZ:FlushActiveCheckerboard:Done". Upon receiving
+   * this notification, the caller may call getReports() to obtain the flushed
+   * reports, along with any other reports that are available.
+   */
+  void flushActiveReports();
 };
rename from dom/webidl/CustomElementsRegistry.webidl
rename to dom/webidl/CustomElementRegistry.webidl
--- a/dom/webidl/CustomElementsRegistry.webidl
+++ b/dom/webidl/CustomElementRegistry.webidl
@@ -1,15 +1,15 @@
 /* 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/. */
 
 // https://html.spec.whatwg.org/#dom-window-customelements
-[Func="CustomElementsRegistry::IsCustomElementsEnabled"]
-interface CustomElementsRegistry {
+[Func="CustomElementRegistry::IsCustomElementEnabled"]
+interface CustomElementRegistry {
   [Throws]
   void define(DOMString name, Function functionConstructor,
               optional ElementDefinitionOptions options);
   any get(DOMString name);
   [Throws]
   Promise<void> whenDefined(DOMString name);
 };
 
--- a/dom/webidl/Document.webidl
+++ b/dom/webidl/Document.webidl
@@ -266,17 +266,17 @@ partial interface Document {
   // Event handlers
   attribute EventHandler onpointerlockchange;
   attribute EventHandler onpointerlockerror;
 };
 
 //http://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/custom/index.html#dfn-document-register
 partial interface Document {
     // this is deprecated from CustomElements v0
-    [Throws, Func="CustomElementsRegistry::IsCustomElementsEnabled"]
+    [Throws, Func="CustomElementRegistry::IsCustomElementEnabled"]
     object registerElement(DOMString name, optional ElementRegistrationOptions options);
 };
 
 // http://dvcs.w3.org/hg/webperf/raw-file/tip/specs/PageVisibility/Overview.html#sec-document-interface
 partial interface Document {
   readonly attribute boolean hidden;
   readonly attribute VisibilityState visibilityState;
 };
--- a/dom/webidl/Notification.webidl
+++ b/dom/webidl/Notification.webidl
@@ -46,28 +46,32 @@ interface Notification : EventTarget {
   readonly attribute DOMString? body;
 
   [Constant]
   readonly attribute DOMString? tag;
 
   [Pure]
   readonly attribute DOMString? icon;
 
+  [Constant, Func="mozilla::dom::Notification::RequireInteractionEnabled"]
+  readonly attribute boolean requireInteraction;
+
   [Constant]
   readonly attribute any data;
 
   void close();
 };
 
 dictionary NotificationOptions {
   NotificationDirection dir = "auto";
   DOMString lang = "";
   DOMString body = "";
   DOMString tag = "";
   DOMString icon = "";
+  boolean requireInteraction = false;
   any data = null;
   NotificationBehavior mozbehavior = null;
 };
 
 dictionary GetNotificationOptions {
   DOMString tag = "";
 };
 
--- a/dom/webidl/SVGSVGElement.webidl
+++ b/dom/webidl/SVGSVGElement.webidl
@@ -71,10 +71,10 @@ interface SVGSVGElement : SVGGraphicsEle
   SVGTransform createSVGTransform();
   [NewObject]
   SVGTransform createSVGTransformFromMatrix(SVGMatrix matrix);
   [UseCounter]
   Element? getElementById(DOMString elementId);
 };
 
 SVGSVGElement implements SVGFitToViewBox;
-SVGSVGElement implements SVGZoomAndPan;
+SVGSVGElement implements SVGZoomAndPanValues;
 
--- a/dom/webidl/SVGViewElement.webidl
+++ b/dom/webidl/SVGViewElement.webidl
@@ -10,10 +10,10 @@
  * liability, trademark and document use rules apply.
  */
 
 interface SVGViewElement : SVGElement {
   readonly attribute SVGStringList viewTarget;
 };
 
 SVGViewElement implements SVGFitToViewBox;
-SVGViewElement implements SVGZoomAndPan;
+SVGViewElement implements SVGZoomAndPanValues;
 
--- a/dom/webidl/SVGZoomAndPan.webidl
+++ b/dom/webidl/SVGZoomAndPan.webidl
@@ -6,18 +6,11 @@
  * The origin of this IDL file is
  * http://www.w3.org/TR/SVG2/
  *
  * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
  * liability, trademark and document use rules apply.
  */
 
 interface SVGZoomAndPan {
-
-  // Zoom and Pan Types
-  const unsigned short SVG_ZOOMANDPAN_UNKNOWN = 0;
-  const unsigned short SVG_ZOOMANDPAN_DISABLE = 1;
-  const unsigned short SVG_ZOOMANDPAN_MAGNIFY = 2;
-
-  [SetterThrows]
-  attribute unsigned short zoomAndPan;
 };
 
+SVGZoomAndPan implements SVGZoomAndPanValues;
copy from dom/webidl/SVGZoomAndPan.webidl
copy to dom/webidl/SVGZoomAndPanValues.webidl
--- a/dom/webidl/SVGZoomAndPan.webidl
+++ b/dom/webidl/SVGZoomAndPanValues.webidl
@@ -5,17 +5,18 @@
  *
  * The origin of this IDL file is
  * http://www.w3.org/TR/SVG2/
  *
  * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
  * liability, trademark and document use rules apply.
  */
 
-interface SVGZoomAndPan {
+[NoInterfaceObject]
+interface SVGZoomAndPanValues {
 
   // Zoom and Pan Types
   const unsigned short SVG_ZOOMANDPAN_UNKNOWN = 0;
   const unsigned short SVG_ZOOMANDPAN_DISABLE = 1;
   const unsigned short SVG_ZOOMANDPAN_MAGNIFY = 2;
 
   [SetterThrows]
   attribute unsigned short zoomAndPan;
--- a/dom/webidl/VRDisplay.webidl
+++ b/dom/webidl/VRDisplay.webidl
@@ -276,10 +276,10 @@ interface VRDisplay : EventTarget {
   sequence<VRLayer> getLayers();
 
   /**
    * The VRLayer provided to the VRDisplay will be captured and presented
    * in the HMD. Calling this function has the same effect on the source
    * canvas as any other operation that uses its source image, and canvases
    * created without preserveDrawingBuffer set to true will be cleared.
    */
-  void submitFrame(optional VRPose pose);
+  void submitFrame();
 };
--- a/dom/webidl/Window.webidl
+++ b/dom/webidl/Window.webidl
@@ -31,18 +31,18 @@ typedef any Transferable;
    CrossOriginReadable] readonly attribute Window window;
   [Replaceable, Constant, StoreInSlot,
    CrossOriginReadable] readonly attribute Window self;
   [Unforgeable, StoreInSlot, Pure] readonly attribute Document? document;
   [Throws] attribute DOMString name;
   [PutForwards=href, Unforgeable, Throws,
    CrossOriginReadable, CrossOriginWritable] readonly attribute Location? location;
   [Throws] readonly attribute History history;
-  [Func="CustomElementsRegistry::IsCustomElementsEnabled"]
-  readonly attribute CustomElementsRegistry customElements;
+  [Func="CustomElementRegistry::IsCustomElementEnabled"]
+  readonly attribute CustomElementRegistry customElements;
   [Replaceable, Throws] readonly attribute BarProp locationbar;
   [Replaceable, Throws] readonly attribute BarProp menubar;
   [Replaceable, Throws] readonly attribute BarProp personalbar;
   [Replaceable, Throws] readonly attribute BarProp scrollbars;
   [Replaceable, Throws] readonly attribute BarProp statusbar;
   [Replaceable, Throws] readonly attribute BarProp toolbar;
   [Throws] attribute DOMString status;
   [Throws, CrossOriginCallable, UnsafeInPrerendering] void close();
--- a/dom/webidl/moz.build
+++ b/dom/webidl/moz.build
@@ -97,17 +97,17 @@ WEBIDL_FILES = [
     'CSSPrimitiveValue.webidl',
     'CSSPseudoElement.webidl',
     'CSSRuleList.webidl',
     'CSSStyleDeclaration.webidl',
     'CSSStyleSheet.webidl',
     'CSSTransition.webidl',
     'CSSValue.webidl',
     'CSSValueList.webidl',
-    'CustomElementsRegistry.webidl',
+    'CustomElementRegistry.webidl',
     'DataContainerEvent.webidl',
     'DataTransfer.webidl',
     'DataTransferItem.webidl',
     'DataTransferItemList.webidl',
     'DecoderDoctorNotification.webidl',
     'DedicatedWorkerGlobalScope.webidl',
     'DelayNode.webidl',
     'DesktopNotification.webidl',
@@ -539,16 +539,17 @@ WEBIDL_FILES = [
     'SVGTransform.webidl',
     'SVGTransformList.webidl',
     'SVGTSpanElement.webidl',
     'SVGUnitTypes.webidl',
     'SVGURIReference.webidl',
     'SVGUseElement.webidl',
     'SVGViewElement.webidl',
     'SVGZoomAndPan.webidl',
+    'SVGZoomAndPanValues.webidl',
     'SVGZoomEvent.webidl',
     'SystemUpdate.webidl',
     'TCPServerSocket.webidl',
     'TCPServerSocketEvent.webidl',
     'TCPSocket.webidl',
     'TCPSocketErrorEvent.webidl',
     'TCPSocketEvent.webidl',
     'Telephony.webidl',
--- a/dom/workers/ServiceWorkerWindowClient.cpp
+++ b/dom/workers/ServiceWorkerWindowClient.cpp
@@ -323,17 +323,17 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
 NS_INTERFACE_MAP_END
 
 class ClientNavigateRunnable final : public Runnable
 {
   uint64_t mWindowId;
   nsString mUrl;
   nsCString mBaseUrl;
   RefPtr<PromiseWorkerProxy> mPromiseProxy;
-  WorkerPrivate* mWorkerPrivate;
+  MOZ_INIT_OUTSIDE_CTOR WorkerPrivate* mWorkerPrivate;
 
 public:
   ClientNavigateRunnable(uint64_t aWindowId, const nsAString& aUrl,
                          PromiseWorkerProxy* aPromiseProxy)
     : mWindowId(aWindowId)
     , mUrl(aUrl)
     , mPromiseProxy(aPromiseProxy)
   {
--- a/dom/workers/WorkerPrefs.h
+++ b/dom/workers/WorkerPrefs.h
@@ -25,16 +25,17 @@
 WORKER_SIMPLE_PREF("browser.dom.window.dump.enabled", DumpEnabled, DUMP)
 #endif
 WORKER_SIMPLE_PREF("canvas.imagebitmap_extensions.enabled", ImageBitmapExtensionsEnabled, IMAGEBITMAP_EXTENSIONS_ENABLED)
 WORKER_SIMPLE_PREF("dom.caches.enabled", DOMCachesEnabled, DOM_CACHES)
 WORKER_SIMPLE_PREF("dom.caches.testing.enabled", DOMCachesTestingEnabled, DOM_CACHES_TESTING)
 WORKER_SIMPLE_PREF("dom.performance.enable_user_timing_logging", PerformanceLoggingEnabled, PERFORMANCE_LOGGING_ENABLED)
 WORKER_SIMPLE_PREF("dom.webnotifications.enabled", DOMWorkerNotificationEnabled, DOM_WORKERNOTIFICATION)
 WORKER_SIMPLE_PREF("dom.webnotifications.serviceworker.enabled", DOMServiceWorkerNotificationEnabled, DOM_SERVICEWORKERNOTIFICATION)
+WORKER_SIMPLE_PREF("dom.webnotifications.requireinteraction.enabled", DOMWorkerNotificationRIEnabled, DOM_WORKERNOTIFICATIONRI)
 WORKER_SIMPLE_PREF("dom.serviceWorkers.enabled", ServiceWorkersEnabled, SERVICEWORKERS_ENABLED)
 WORKER_SIMPLE_PREF("dom.serviceWorkers.testing.enabled", ServiceWorkersTestingEnabled, SERVICEWORKERS_TESTING_ENABLED)
 WORKER_SIMPLE_PREF("dom.serviceWorkers.openWindow.enabled", OpenWindowEnabled, OPEN_WINDOW_ENABLED)
 WORKER_SIMPLE_PREF("dom.push.enabled", PushEnabled, PUSH_ENABLED)
 WORKER_SIMPLE_PREF("dom.requestcontext.enabled", RequestContextEnabled, REQUESTCONTEXT_ENABLED)
 WORKER_SIMPLE_PREF("gfx.offscreencanvas.enabled", OffscreenCanvasEnabled, OFFSCREENCANVAS_ENABLED)
 WORKER_SIMPLE_PREF("dom.webkitBlink.dirPicker.enabled", WebkitBlinkDirectoryPickerEnabled, DOM_WEBKITBLINK_DIRPICKER_WEBKITBLINK)
 WORKER_PREF("dom.workers.latestJSVersion", JSVersionChanged)
--- a/dom/xhr/XMLHttpRequestMainThread.cpp
+++ b/dom/xhr/XMLHttpRequestMainThread.cpp
@@ -120,16 +120,17 @@ namespace {
   const nsString kLiteralString_DOMContentLoaded = NS_LITERAL_STRING("DOMContentLoaded");
 }
 
 // CIDs
 #define NS_BADCERTHANDLER_CONTRACTID \
   "@mozilla.org/content/xmlhttprequest-bad-cert-handler;1"
 
 #define NS_PROGRESS_EVENT_INTERVAL 50
+#define MAX_SYNC_TIMEOUT_WHEN_UNLOADING 10000 /* 10 secs */
 
 NS_IMPL_ISUPPORTS(nsXHRParseEndListener, nsIDOMEventListener)
 
 class nsResumeTimeoutsEvent : public Runnable
 {
 public:
   explicit nsResumeTimeoutsEvent(nsPIDOMWindowInner* aWindow) : mWindow(aWindow) {}
 
@@ -2927,25 +2928,38 @@ XMLHttpRequestMainThread::SendInternal(c
           topWindow->SuspendTimeouts(1, false);
           resumeTimeoutRunnable = new nsResumeTimeoutsEvent(topInner);
         }
       }
     }
 
     StopProgressEventTimer();
 
-    {
+    SyncTimeoutType syncTimeoutType = MaybeStartSyncTimeoutTimer();
+    if (syncTimeoutType == eErrorOrExpired) {
+      Abort();
+      rv = NS_ERROR_DOM_NETWORK_ERR;
+    }
+
+    if (NS_SUCCEEDED(rv)) {
       nsAutoSyncOperation sync(suspendedDoc);
       nsIThread *thread = NS_GetCurrentThread();
       while (mFlagSyncLooping) {
         if (!NS_ProcessNextEvent(thread)) {
           rv = NS_ERROR_UNEXPECTED;
           break;
         }
       }
+
+      // Time expired... We should throw.
+      if (syncTimeoutType == eTimerStarted && !mSyncTimeoutTimer) {
+        rv = NS_ERROR_DOM_NETWORK_ERR;
+      }
+
+      CancelSyncTimeoutTimer();
     }
 
     if (suspendedDoc) {
       suspendedDoc->UnsuppressEventHandlingAndFireEvents(nsIDocument::eEvents,
                                                          true);
     }
 
     if (resumeTimeoutRunnable) {
@@ -3507,16 +3521,21 @@ XMLHttpRequestMainThread::Notify(nsITime
     return NS_OK;
   }
 
   if (mTimeoutTimer == aTimer) {
     HandleTimeoutCallback();
     return NS_OK;
   }
 
+  if (mSyncTimeoutTimer == aTimer) {
+    HandleSyncTimeoutTimer();
+    return NS_OK;
+  }
+
   // Just in case some JS user wants to QI to nsITimerCallback and play with us...
   NS_WARNING("Unexpected timer!");
   return NS_ERROR_INVALID_POINTER;
 }
 
 void
 XMLHttpRequestMainThread::HandleProgressTimerCallback()
 {
@@ -3559,16 +3578,62 @@ XMLHttpRequestMainThread::StartProgressE
   if (mProgressNotifier) {
     mProgressTimerIsActive = true;
     mProgressNotifier->Cancel();
     mProgressNotifier->InitWithCallback(this, NS_PROGRESS_EVENT_INTERVAL,
                                         nsITimer::TYPE_ONE_SHOT);
   }
 }
 
+XMLHttpRequestMainThread::SyncTimeoutType
+XMLHttpRequestMainThread::MaybeStartSyncTimeoutTimer()
+{
+  MOZ_ASSERT(mFlagSynchronous);
+
+  nsIDocument* doc = GetDocumentIfCurrent();
+  if (!doc || !doc->GetPageUnloadingEventTimeStamp()) {
+    return eNoTimerNeeded;
+  }
+
+  // If we are in a beforeunload or a unload event, we must force a timeout.
+  TimeDuration diff = (TimeStamp::NowLoRes() - doc->GetPageUnloadingEventTimeStamp());
+  if (diff.ToMilliseconds() > MAX_SYNC_TIMEOUT_WHEN_UNLOADING) {
+    return eErrorOrExpired;
+  }
+
+  mSyncTimeoutTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
+  if (!mSyncTimeoutTimer) {
+    return eErrorOrExpired;
+  }
+
+  uint32_t timeout = MAX_SYNC_TIMEOUT_WHEN_UNLOADING - diff.ToMilliseconds();
+  nsresult rv = mSyncTimeoutTimer->InitWithCallback(this, timeout,
+                                                    nsITimer::TYPE_ONE_SHOT);
+  return NS_FAILED(rv) ? eErrorOrExpired : eTimerStarted;
+}
+
+void
+XMLHttpRequestMainThread::HandleSyncTimeoutTimer()
+{
+  MOZ_ASSERT(mSyncTimeoutTimer);
+  MOZ_ASSERT(mFlagSyncLooping);
+
+  CancelSyncTimeoutTimer();
+  Abort();
+}
+
+void
+XMLHttpRequestMainThread::CancelSyncTimeoutTimer()
+{
+  if (mSyncTimeoutTimer) {
+    mSyncTimeoutTimer->Cancel();
+    mSyncTimeoutTimer = nullptr;
+  }
+}
+
 already_AddRefed<nsXMLHttpRequestXPCOMifier>
 XMLHttpRequestMainThread::EnsureXPCOMifier()
 {
   if (!mXPCOMifier) {
     mXPCOMifier = new nsXMLHttpRequestXPCOMifier(this);
   }
   RefPtr<nsXMLHttpRequestXPCOMifier> newRef(mXPCOMifier);
   return newRef.forget();
--- a/dom/xhr/XMLHttpRequestMainThread.h
+++ b/dom/xhr/XMLHttpRequestMainThread.h
@@ -705,16 +705,28 @@ protected:
 
   // Timeout support
   PRTime mRequestSentTime;
   uint32_t mTimeoutMilliseconds;
   nsCOMPtr<nsITimer> mTimeoutTimer;
   void StartTimeoutTimer();
   void HandleTimeoutCallback();
 
+  nsCOMPtr<nsITimer> mSyncTimeoutTimer;
+
+  enum SyncTimeoutType {
+    eErrorOrExpired,
+    eTimerStarted,
+    eNoTimerNeeded
+  };
+
+  SyncTimeoutType MaybeStartSyncTimeoutTimer();
+  void HandleSyncTimeoutTimer();
+  void CancelSyncTimeoutTimer();
+
   bool mErrorLoad;
   bool mErrorParsingXML;
   bool mWaitingForOnStopRequest;
   bool mProgressTimerIsActive;
   bool mIsHtml;
   bool mWarnAboutMultipartHtml;
   bool mWarnAboutSyncHtml;
   int64_t mLoadTotal; // -1 if not known.
new file mode 100644
new file mode 100644
--- /dev/null
+++ b/dom/xhr/tests/iframe_sync_xhr_unload.html
@@ -0,0 +1,18 @@
+<!DOCTYPE HTML>
+<html>
+<body>
+  <script type="application/javascript">
+
+function o() {
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", "sync_xhr_unload.sjs", false);
+ try { xhr.send(); } catch(e) {}
+}
+
+window.addEventListener("beforeunload", o, false);
+window.addEventListener("unload", o, false)
+
+  </script>
+</body>
+</html>
+
--- a/dom/xhr/tests/mochitest.ini
+++ b/dom/xhr/tests/mochitest.ini
@@ -55,16 +55,19 @@ support-files =
   relativeLoad_worker2.js
   responseIdentical.sjs
   subdir/relativeLoad_sub_worker.js
   subdir/relativeLoad_sub_worker2.js
   subdir/relativeLoad_sub_import.js
   common_temporaryFileBlob.js
   worker_temporaryFileBlob.js
   worker_bug1300552.js
+  sync_xhr_unload.sjs
+  iframe_sync_xhr_unload.html
+  empty.html
 
 [test_xhr_overridemimetype_throws_on_invalid_state.html]
 skip-if = buildapp == 'b2g' # Requires webgl support
 [test_html_in_xhr.html]
 [test_sync_xhr_timer.xhtml]
 skip-if = toolkit == 'android'
 [test_xhr_abort_after_load.html]
 skip-if = toolkit == 'android'
@@ -102,8 +105,9 @@ skip-if = buildapp == 'b2g'
 [test_worker_xhr_responseURL.html]
 [test_worker_xhr_system.html]
 [test_worker_xhr_timeout.html]
 skip-if = (os == "win") || (os == "mac") || toolkit == 'android' #bug 798220
 [test_relativeLoad.html]
 skip-if = buildapp == 'b2g' # b2g(Failed to load script: relativeLoad_import.js) b2g-debug(Failed to load script: relativeLoad_import.js) b2g-desktop(Failed to load script: relativeLoad_import.js)
 [test_temporaryFileBlob.html]
 [test_bug1300552.html]
+[test_sync_xhr_unload.html]
new file mode 100644
--- /dev/null
+++ b/dom/xhr/tests/sync_xhr_unload.sjs
@@ -0,0 +1,15 @@
+var timer = null;
+
+function handleRequest(request, response)
+{
+  response.processAsync();
+  timer = Components.classes["@mozilla.org/timer;1"]
+                    .createInstance(Components.interfaces.nsITimer);
+  timer.initWithCallback(function()
+  {
+    response.setStatusLine(null, 200, "OK");
+    response.setHeader("Content-Type", "text/plain", false);
+    response.write("hello");
+    response.finish();
+  }, 30000 /* milliseconds */, Components.interfaces.nsITimer.TYPE_ONE_SHOT);
+}
new file mode 100644
--- /dev/null
+++ b/dom/xhr/tests/test_sync_xhr_unload.html
@@ -0,0 +1,36 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 1307122</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="common_temporaryFileBlob.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+  <script type="application/javascript">
+
+SimpleTest.waitForExplicitFinish();
+
+info("Creating the iframe...");
+var ifr = document.createElement('iframe');
+
+ifr.addEventListener("load", function ifr_load1() {
+  info("Iframe loaded");
+
+  ifr.removeEventListener("load", ifr_load1);
+  ifr.src = "empty.html";
+
+  ifr.addEventListener("load", function ifr_load2() {
+    ok(true, "Test passed");
+    SimpleTest.finish();
+  });
+
+});
+
+ifr.src = "iframe_sync_xhr_unload.html";
+document.body.appendChild(ifr);
+
+  </script>
+</body>
+</html>
--- a/dom/xml/XMLDocument.cpp
+++ b/dom/xml/XMLDocument.cpp
@@ -286,20 +286,27 @@ XMLDocument::Load(const nsAString& aUrl,
                                     NS_LITERAL_CSTRING("DOM"),
                                     callingDoc,
                                     nsContentUtils::eDOM_PROPERTIES,
                                     "XMLDocumentLoadPrincipalMismatch");
     aRv.Throw(NS_ERROR_UNEXPECTED);
     return false;
   }
 
+  // Reporting a warning on ourselves is rather pointless, because we probably
+  // have no window id (and hence the warning won't show up in any web console)
+  // and probably aren't considered a "content document" because we're not
+  // loaded in a docshell, so won't accumulate telemetry for use counters.  Try
+  // warning on our entry document, if any, since that should have things like
+  // window ids and associated docshells.
+  nsIDocument* docForWarning = callingDoc ? callingDoc.get() : this;
   if (nsContentUtils::IsCallerChrome()) {
-    WarnOnceAbout(nsIDocument::eChromeUseOfDOM3LoadMethod);
+    docForWarning->WarnOnceAbout(nsIDocument::eChromeUseOfDOM3LoadMethod);
   } else {
-    WarnOnceAbout(nsIDocument::eUseOfDOM3LoadMethod);
+    docForWarning->WarnOnceAbout(nsIDocument::eUseOfDOM3LoadMethod);
   } 
 
   nsIURI *baseURI = mDocumentURI;
   nsAutoCString charset;
 
   if (callingDoc) {
     baseURI = callingDoc->GetDocBaseURI();
     charset = callingDoc->GetDocumentCharacterSet();
--- a/editor/libeditor/CreateElementTransaction.cpp
+++ b/editor/libeditor/CreateElementTransaction.cpp
@@ -79,17 +79,18 @@ CreateElementTransaction::DoTransaction(
   }
 
   mOffsetInParent = std::min(mOffsetInParent,
                              static_cast<int32_t>(mParent->GetChildCount()));
 
   // Note, it's ok for mRefNode to be null. That means append
   mRefNode = mParent->GetChildAt(mOffsetInParent);
 
-  mParent->InsertBefore(*mNewNode, mRefNode, rv);
+  nsCOMPtr<nsIContent> refNode = mRefNode;
+  mParent->InsertBefore(*mNewNode, refNode, rv);
   NS_ENSURE_TRUE(!rv.Failed(), rv.StealNSResult());
 
   // Only set selection to insertion point if editor gives permission
   if (!mEditorBase->GetShouldTxnSetSelection()) {
     // Do nothing - DOM range gravity will adjust selection
     return NS_OK;
   }
 
@@ -119,17 +120,18 @@ CreateElementTransaction::RedoTransactio
   MOZ_ASSERT(mEditorBase && mParent);
 
   // First, reset mNewNode so it has no attributes or content
   // XXX We never actually did this, we only cleared mNewNode's contents if it
   // was a CharacterData node (which it's not, it's an Element)
 
   // Now, reinsert mNewNode
   ErrorResult rv;
-  mParent->InsertBefore(*mNewNode, mRefNode, rv);
+  nsCOMPtr<nsIContent> refNode = mRefNode;
+  mParent->InsertBefore(*mNewNode, refNode, rv);
   return rv.StealNSResult();
 }
 
 NS_IMETHODIMP
 CreateElementTransaction::GetTxnDescription(nsAString& aString)
 {
   aString.AssignLiteral("CreateElementTransaction: ");
   aString += nsDependentAtomString(mTag);
--- a/editor/libeditor/DeleteNodeTransaction.cpp
+++ b/editor/libeditor/DeleteNodeTransaction.cpp
@@ -83,17 +83,18 @@ DeleteNodeTransaction::UndoTransaction()
     // this is a legal state, the txn is a no-op
     return NS_OK;
   }
   if (!mNode) {
     return NS_ERROR_NULL_POINTER;
   }
 
   ErrorResult error;
-  mParent->InsertBefore(*mNode, mRefNode, error);
+  nsCOMPtr<nsIContent> refNode = mRefNode;
+  mParent->InsertBefore(*mNode, refNode, error);
   return error.StealNSResult();
 }
 
 NS_IMETHODIMP
 DeleteNodeTransaction::RedoTransaction()
 {
   if (!mParent) {
     // this is a legal state, the txn is a no-op
--- a/editor/libeditor/EditorBase.cpp
+++ b/editor/libeditor/EditorBase.cpp
@@ -2701,17 +2701,18 @@ EditorBase::SplitNodeImpl(nsIContent& aE
       savedRanges.AppendElement(range);
     }
   }
 
   nsCOMPtr<nsINode> parent = aExistingRightNode.GetParentNode();
   NS_ENSURE_TRUE(parent, NS_ERROR_NULL_POINTER);
 
   ErrorResult rv;
-  parent->InsertBefore(aNewLeftNode, &aExistingRightNode, rv);
+  nsCOMPtr<nsINode> refNode = &aExistingRightNode;
+  parent->InsertBefore(aNewLeftNode, refNode, rv);
   NS_ENSURE_TRUE(!rv.Failed(), rv.StealNSResult());
 
   // Split the children between the two nodes.  At this point,
   // aExistingRightNode has all the children.  Move all the children whose
   // index is < aOffset to aNewLeftNode.
   if (aOffset < 0) {
     // This means move no children
     return NS_OK;
--- a/editor/libeditor/JoinNodeTransaction.cpp
+++ b/editor/libeditor/JoinNodeTransaction.cpp
@@ -89,17 +89,18 @@ JoinNodeTransaction::UndoTransaction()
         return NS_ERROR_NULL_POINTER;
       }
       nsCOMPtr<nsIContent> nextSibling = child->GetNextSibling();
       mLeftNode->AppendChild(*child, rv);
       child = nextSibling;
     }
   }
   // Second, re-insert the left node into the tree
-  mParent->InsertBefore(*mLeftNode, mRightNode, rv);
+  nsCOMPtr<nsINode> refNode = mRightNode;
+  mParent->InsertBefore(*mLeftNode, refNode, rv);
   return rv.StealNSResult();
 }
 
 NS_IMETHODIMP
 JoinNodeTransaction::GetTxnDescription(nsAString& aString)
 {
   aString.AssignLiteral("JoinNodeTransaction");
   return NS_OK;
--- a/editor/libeditor/SplitNodeTransaction.cpp
+++ b/editor/libeditor/SplitNodeTransaction.cpp
@@ -101,17 +101,18 @@ SplitNodeTransaction::RedoTransaction()
       mExistingRightNode->RemoveChild(*child, rv);
       if (!rv.Failed()) {
         mNewLeftNode->AppendChild(*child, rv);
       }
       child = nextSibling;
     }
   }
   // Second, re-insert the left node into the tree
-  mParent->InsertBefore(*mNewLeftNode, mExistingRightNode, rv);
+  nsCOMPtr<nsIContent> refNode = mExistingRightNode;
+  mParent->InsertBefore(*mNewLeftNode, refNode, rv);
   return rv.StealNSResult();
 }
 
 
 NS_IMETHODIMP
 SplitNodeTransaction::GetTxnDescription(nsAString& aString)
 {
   aString.AssignLiteral("SplitNodeTransaction");
--- a/gfx/2d/2D.h
+++ b/gfx/2d/2D.h
@@ -1431,19 +1431,16 @@ public:
                           DataSourceSurface* aDest);
 
 
   static already_AddRefed<DrawEventRecorder>
     CreateEventRecorderForFile(const char *aFilename);
 
   static void SetGlobalEventRecorder(DrawEventRecorder *aRecorder);
 
-  // This is a little hacky at the moment, but we want to have this data. Bug 1068613.
-  static void SetLogForwarder(LogForwarder* aLogFwd);
-
   static uint32_t GetMaxSurfaceSize(BackendType aType);
 
   static LogForwarder* GetLogForwarder() { return sConfig ? sConfig->mLogForwarder : nullptr; }
 
 private:
   static Config* sConfig;
 public:
 
--- a/gfx/2d/DrawTargetCairo.cpp
+++ b/gfx/2d/DrawTargetCairo.cpp
@@ -1839,36 +1839,45 @@ DrawTargetCairo::CreateSimilarDrawTarget
   if (cairo_surface_status(cairo_get_group_target(mContext))) {
     RefPtr<DrawTargetCairo> target = new DrawTargetCairo();
     if (target->Init(aSize, aFormat)) {
       return target.forget();
     }
   }
 
   cairo_surface_t* similar;
+  switch (cairo_surface_get_type(mSurface)) {
 #ifdef CAIRO_HAS_WIN32_SURFACE
-  if (cairo_surface_get_type(mSurface) == CAIRO_SURFACE_TYPE_WIN32) {
-    similar = cairo_win32_surface_create_with_dib(GfxFormatToCairoFormat(aFormat),
-                                                  aSize.width, aSize.height);
-  } else
+    case CAIRO_SURFACE_TYPE_WIN32:
+      similar = cairo_win32_surface_create_with_dib(
+        GfxFormatToCairoFormat(aFormat), aSize.width, aSize.height);
+      break;
 #endif
-  {
-    similar = cairo_surface_create_similar(mSurface,
-                                           GfxFormatToCairoContent(aFormat),
-                                           aSize.width, aSize.height);
+#ifdef CAIRO_HAS_QUARTZ_SURFACE
+    case CAIRO_SURFACE_TYPE_QUARTZ:
+      similar = cairo_quartz_surface_create_cg_layer(
+        mSurface, GfxFormatToCairoContent(aFormat), aSize.width, aSize.height);
+      break;
+#endif
+    default:
+      similar = cairo_surface_create_similar(mSurface,
+                                             GfxFormatToCairoContent(aFormat),
+                                             aSize.width, aSize.height);
+      break;
   }
 
   if (!cairo_surface_status(similar)) {
     RefPtr<DrawTargetCairo> target = new DrawTargetCairo();
     if (target->InitAlreadyReferenced(similar, aSize)) {
       return target.forget();
     }
   }
 
   gfxCriticalError(CriticalLog::DefaultOptions(Factory::ReasonableSurfaceSize(aSize))) << "Failed to create similar cairo surface! Size: " << aSize << " Status: " << cairo_surface_status(similar) << cairo_surface_status(cairo_get_group_target(mContext)) << " format " << (int)aFormat;
+  cairo_surface_destroy(similar);
 
   return nullptr;
 }
 
 bool
 DrawTargetCairo::InitAlreadyReferenced(cairo_surface_t* aSurface, const IntSize& aSize, SurfaceFormat* aFormat)
 {
   if (cairo_surface_status(aSurface)) {
--- a/gfx/2d/Factory.cpp
+++ b/gfx/2d/Factory.cpp
@@ -186,16 +186,17 @@ Factory::Init(const Config& aConfig)
     sConfig->mMaxTextureSize = kMinSizePref;
   }
 }
 
 void
 Factory::ShutDown()
 {
   if (sConfig) {
+    delete sConfig->mLogForwarder;
     delete sConfig;
     sConfig = nullptr;
   }
 }
 
 bool
 Factory::HasSSE2()
 {
@@ -977,23 +978,16 @@ Factory::CreateEventRecorderForFile(cons
 void
 Factory::SetGlobalEventRecorder(DrawEventRecorder *aRecorder)
 {
   mRecorder = aRecorder;
 }
 
 // static
 void
-Factory::SetLogForwarder(LogForwarder* aLogFwd) {
-  sConfig->mLogForwarder = aLogFwd;
-}
-
-
-// static
-void
 CriticalLogger::OutputMessage(const std::string &aString,
                               int aLevel, bool aNoNewline)
 {
   if (Factory::GetLogForwarder()) {
     Factory::GetLogForwarder()->Log(aString);
   }
 
   BasicLogger::OutputMessage(aString, aLevel, aNoNewline);
--- a/gfx/ipc/GPUChild.cpp
+++ b/gfx/ipc/GPUChild.cpp
@@ -118,16 +118,27 @@ bool
 GPUChild::RecvInitCrashReporter(Shmem&& aShmem)
 {
 #ifdef MOZ_CRASHREPORTER
   mCrashReporter = MakeUnique<ipc::CrashReporterHost>(GeckoProcessType_GPU, aShmem);
 #endif
   return true;
 }
 
+bool
+GPUChild::RecvNotifyUiObservers(const nsCString& aTopic)
+{
+  nsCOMPtr<nsIObserverService> obsSvc = mozilla::services::GetObserverService();
+  MOZ_ASSERT(obsSvc);
+  if (obsSvc) {
+    obsSvc->NotifyObservers(nullptr, aTopic.get(), nullptr);
+  }
+  return true;
+}
+
 void
 GPUChild::ActorDestroy(ActorDestroyReason aWhy)
 {
   if (aWhy == AbnormalShutdown) {
 #ifdef MOZ_CRASHREPORTER
     if (mCrashReporter) {
       mCrashReporter->GenerateCrashReport(OtherPid());
       mCrashReporter = nullptr;
--- a/gfx/ipc/GPUChild.h
+++ b/gfx/ipc/GPUChild.h
@@ -35,16 +35,17 @@ public:
   void OnVarChanged(const GfxVarUpdate& aVar) override;
 
   // PGPUChild overrides.
   bool RecvInitComplete(const GPUDeviceData& aData) override;
   bool RecvReportCheckerboard(const uint32_t& aSeverity, const nsCString& aLog) override;
   bool RecvInitCrashReporter(Shmem&& shmem) override;
   void ActorDestroy(ActorDestroyReason aWhy) override;
   bool RecvGraphicsError(const nsCString& aError) override;
+  bool RecvNotifyUiObservers(const nsCString& aTopic) override;
 
   static void Destroy(UniquePtr<GPUChild>&& aChild);
 
 private:
   GPUProcessHost* mHost;
   UniquePtr<ipc::CrashReporterHost> mCrashReporter;
   bool mGPUReady;
 };
--- a/gfx/ipc/GPUParent.cpp
+++ b/gfx/ipc/GPUParent.cpp
@@ -7,16 +7,17 @@
 #include "WMF.h"
 #endif
 #include "GPUParent.h"
 #include "gfxConfig.h"
 #include "gfxPlatform.h"
 #include "gfxPrefs.h"
 #include "GPUProcessHost.h"
 #include "mozilla/Assertions.h"
+#include "mozilla/gfx/2D.h"
 #include "mozilla/gfx/gfxVars.h"
 #include "mozilla/ipc/CrashReporterClient.h"
 #include "mozilla/ipc/ProcessChild.h"
 #include "mozilla/layers/APZThreadUtils.h"
 #include "mozilla/layers/APZCTreeManager.h"
 #include "mozilla/layers/CompositorBridgeParent.h"
 #include "mozilla/dom/VideoDecoderManagerParent.h"
 #include "mozilla/layers/CompositorThread.h"
@@ -300,16 +301,27 @@ GPUParent::RecvDeallocateLayerTreeId(con
 
 bool
 GPUParent::RecvAddLayerTreeIdMapping(const uint64_t& aLayersId, const ProcessId& aOwnerId)
 {
   LayerTreeOwnerTracker::Get()->Map(aLayersId, aOwnerId);
   return true;
 }
 
+bool
+GPUParent::RecvNotifyGpuObservers(const nsCString& aTopic)
+{
+  nsCOMPtr<nsIObserverService> obsSvc = mozilla::services::GetObserverService();
+  MOZ_ASSERT(obsSvc);
+  if (obsSvc) {
+    obsSvc->NotifyObservers(nullptr, aTopic.get(), nullptr);
+  }
+  return true;
+}
+
 void
 GPUParent::ActorDestroy(ActorDestroyReason aWhy)
 {
   if (AbnormalShutdown == aWhy) {
     NS_WARNING("Shutting down GPU process early due to a crash!");
     ProcessChild::QuickExit();
   }
 
@@ -323,16 +335,17 @@ GPUParent::ActorDestroy(ActorDestroyReas
   ProcessChild::QuickExit();
 #endif
 
   if (mVsyncBridge) {
     mVsyncBridge->Shutdown();
     mVsyncBridge = nullptr;
   }
   CompositorThreadHolder::Shutdown();
+  Factory::ShutDown();
 #if defined(XP_WIN)
   DeviceManagerDx::Shutdown();
   DeviceManagerD3D9::Shutdown();
 #endif
   LayerTreeOwnerTracker::Shutdown();
   gfxVars::Shutdown();
   gfxConfig::Shutdown();
   gfxPrefs::DestroySingleton();
--- a/gfx/ipc/GPUParent.h
+++ b/gfx/ipc/GPUParent.h
@@ -42,16 +42,17 @@ public:
     const IntSize& aSurfaceSize) override;
   bool RecvNewContentCompositorBridge(Endpoint<PCompositorBridgeParent>&& aEndpoint) override;
   bool RecvNewContentImageBridge(Endpoint<PImageBridgeParent>&& aEndpoint) override;
   bool RecvNewContentVRManager(Endpoint<PVRManagerParent>&& aEndpoint) override;
   bool RecvNewContentVideoDecoderManager(Endpoint<PVideoDecoderManagerParent>&& aEndpoint) override;
   bool RecvDeallocateLayerTreeId(const uint64_t& aLayersId) override;
   bool RecvGetDeviceStatus(GPUDeviceData* aOutStatus) override;
   bool RecvAddLayerTreeIdMapping(const uint64_t& aLayersId, const ProcessId& aOwnerId) override;
+  bool RecvNotifyGpuObservers(const nsCString& aTopic) override;
 
   void ActorDestroy(ActorDestroyReason aWhy) override;
 
 private:
   RefPtr<VsyncBridgeParent> mVsyncBridge;
 };
 
 } // namespace gfx
--- a/gfx/ipc/GPUProcessHost.cpp
+++ b/gfx/ipc/GPUProcessHost.cpp
@@ -201,18 +201,16 @@ void
 GPUProcessHost::KillHard(const char* aReason)
 {
   ProcessHandle handle = GetChildProcessHandle();
   if (!base::KillProcess(handle, base::PROCESS_END_KILLED_BY_USER, false)) {
     NS_WARNING("failed to kill subprocess!");
   }
 
   SetAlreadyDead();
-  XRE_GetIOMessageLoop()->PostTask(
-    NewRunnableFunction(&ProcessWatcher::EnsureProcessTerminated, handle, /*force=*/true));
 }
 
 uint64_t
 GPUProcessHost::GetProcessToken() const
 {
   return mProcessToken;
 }
 
--- a/gfx/ipc/GPUProcessManager.cpp
+++ b/gfx/ipc/GPUProcessManager.cpp
@@ -735,10 +735,21 @@ GPUProcessManager::AddListener(GPUProces
 }
 
 void
 GPUProcessManager::RemoveListener(GPUProcessListener* aListener)
 {
   mListeners.RemoveElement(aListener);
 }
 
+bool
+GPUProcessManager::NotifyGpuObservers(const char* aTopic)
+{
+  if (!mGPUChild) {
+    return false;
+  }
+  nsCString topic(aTopic);
+  mGPUChild->SendNotifyGpuObservers(topic);
+  return true;
+}
+
 } // namespace gfx
 } // namespace mozilla
--- a/gfx/ipc/GPUProcessManager.h
+++ b/gfx/ipc/GPUProcessManager.h
@@ -123,16 +123,20 @@ public:
 
   // Notify the GPUProcessManager that a top-level PGPU protocol has been
   // terminated. This may be called from any thread.
   void NotifyRemoteActorDestroyed(const uint64_t& aProcessToken);
 
   void AddListener(GPUProcessListener* aListener);
   void RemoveListener(GPUProcessListener* aListener);
 
+  // Send a message to the GPU process observer service to broadcast. Returns
+  // true if the message was sent, false if not.
+  bool NotifyGpuObservers(const char* aTopic);
+
   // Returns access to the PGPU protocol if a GPU process is present.
   GPUChild* GetGPUChild() {
     return mGPUChild;
   }
 
 private:
   // Called from our xpcom-shutdown observer.
   void OnXPCOMShutdown();
--- a/gfx/ipc/PGPU.ipdl
+++ b/gfx/ipc/PGPU.ipdl
@@ -63,24 +63,32 @@ parent:
 
   // Called to notify the GPU process of who owns a layersId.
   sync AddLayerTreeIdMapping(uint64_t layersId, ProcessId ownerId);
 
   // Request the current DeviceStatus from the GPU process. This blocks until
   // one is available (i.e., Init has completed).
   sync GetDeviceStatus() returns (GPUDeviceData status);
 
+  // Have a message be broadcasted to the GPU process by the GPU process
+  // observer service.
+  async NotifyGpuObservers(nsCString aTopic);
+
 child:
   // Sent when the GPU process has initialized devices. This occurs once, after
   // Init().
   async InitComplete(GPUDeviceData data);
 
   // Sent when APZ detects checkerboarding and apz checkerboard reporting is enabled.
   async ReportCheckerboard(uint32_t severity, nsCString log);
 
   // Graphics errors, analogous to PContent::GraphicsError
   async GraphicsError(nsCString aError);
 
   async InitCrashReporter(Shmem shmem);
+
+  // Have a message be broadcasted to the UI process by the UI process
+  // observer service.
+  async NotifyUiObservers(nsCString aTopic);
 };
 
 } // namespace gfx
 } // namespace mozilla
--- a/gfx/layers/apz/src/APZCTreeManager.cpp
+++ b/gfx/layers/apz/src/APZCTreeManager.cpp
@@ -9,16 +9,17 @@
 #include "Compositor.h"                 // for Compositor
 #include "DragTracker.h"                // for DragTracker
 #include "gfxPrefs.h"                   // for gfxPrefs
 #include "HitTestingTreeNode.h"         // for HitTestingTreeNode
 #include "InputBlockState.h"            // for InputBlockState
 #include "InputData.h"                  // for InputData, etc
 #include "Layers.h"                     // for Layer, etc
 #include "mozilla/dom/Touch.h"          // for Touch
+#include "mozilla/gfx/GPUParent.h"      // for GPUParent
 #include "mozilla/gfx/Logging.h"        // for gfx::TreeLog
 #include "mozilla/gfx/Point.h"          // for Point
 #include "mozilla/layers/APZThreadUtils.h"  // for AssertOnCompositorThread, etc
 #include "mozilla/layers/AsyncCompositionManager.h" // for ViewTransform
 #include "mozilla/layers/AsyncDragMetrics.h" // for AsyncDragMetrics
 #include "mozilla/layers/CompositorBridgeParent.h" // for CompositorBridgeParent, etc
 #include "mozilla/layers/LayerMetricsWrapper.h"
 #include "mozilla/MouseEvents.h"
@@ -79,31 +80,102 @@ struct APZCTreeManager::TreeBuildingStat
   nsTArray<RefPtr<HitTestingTreeNode>> mNodesToDestroy;
 
   // This map is populated as we place APZCs into the new tree. Its purpose is
   // to facilitate re-using the same APZC for different layers that scroll
   // together (and thus have the same ScrollableLayerGuid).
   std::map<ScrollableLayerGuid, AsyncPanZoomController*> mApzcMap;
 };
 
+class APZCTreeManager::CheckerboardFlushObserver : public nsIObserver {
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIOBSERVER
+
+  explicit CheckerboardFlushObserver(APZCTreeManager* aTreeManager)
+    : mTreeManager(aTreeManager)
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+    nsCOMPtr<nsIObserverService> obsSvc = mozilla::services::GetObserverService();
+    MOZ_ASSERT(obsSvc);
+    if (obsSvc) {
+      obsSvc->AddObserver(this, "APZ:FlushActiveCheckerboard", false);
+    }
+  }
+
+  void Unregister()
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+    nsCOMPtr<nsIObserverService> obsSvc = mozilla::services::GetObserverService();
+    if (obsSvc) {
+      obsSvc->RemoveObserver(this, "APZ:FlushActiveCheckerboard");
+    }
+    mTreeManager = nullptr;
+  }
+
+protected:
+  virtual ~CheckerboardFlushObserver() {}
+
+private:
+  RefPtr<APZCTreeManager> mTreeManager;
+};
+
+NS_IMPL_ISUPPORTS(APZCTreeManager::CheckerboardFlushObserver, nsIObserver)
+
+NS_IMETHODIMP
+APZCTreeManager::CheckerboardFlushObserver::Observe(nsISupports* aSubject,
+                                                    const char* aTopic,
+                                                    const char16_t*)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(mTreeManager.get());
+
+  MutexAutoLock lock(mTreeManager->mTreeLock);
+  if (mTreeManager->mRootNode) {
+    ForEachNode<ReverseIterator>(mTreeManager->mRootNode.get(),
+        [](HitTestingTreeNode* aNode)
+        {
+          if (aNode->IsPrimaryHolder()) {
+            MOZ_ASSERT(aNode->GetApzc());
+            aNode->GetApzc()->FlushActiveCheckerboardReport();
+          }
+        });
+  }
+  if (XRE_IsGPUProcess()) {
+    if (gfx::GPUParent* gpu = gfx::GPUParent::GetSingleton()) {
+      nsCString topic("APZ:FlushActiveCheckerboard:Done");
+      Unused << gpu->SendNotifyUiObservers(topic);
+    }
+  } else {
+    MOZ_ASSERT(XRE_IsParentProcess());
+    nsCOMPtr<nsIObserverService> obsSvc = mozilla::services::GetObserverService();
+    if (obsSvc) {
+      obsSvc->NotifyObservers(nullptr, "APZ:FlushActiveCheckerboard:Done", nullptr);
+    }
+  }
+  return NS_OK;
+}
+
+
 /*static*/ const ScreenMargin
 APZCTreeManager::CalculatePendingDisplayPort(
   const FrameMetrics& aFrameMetrics,
   const ParentLayerPoint& aVelocity)
 {
   return AsyncPanZoomController::CalculatePendingDisplayPort(
     aFrameMetrics, aVelocity);
 }
 
 APZCTreeManager::APZCTreeManager()
     : mInputQueue(new InputQueue()),
       mTreeLock("APZCTreeLock"),
       mHitResultForInputBlock(HitNothing),
       mRetainedTouchIdentifier(-1),
-      mApzcTreeLog("apzctree")
+      mApzcTreeLog("apzctree"),
+      mFlushObserver(new CheckerboardFlushObserver(this))
 {
   AsyncPanZoomController::InitializeGlobalState();
   mApzcTreeLog.ConditionOnPrefFunction(gfxPrefs::APZPrintTree);
 }
 
 APZCTreeManager::~APZCTreeManager()
 {
 }
@@ -1270,16 +1342,22 @@ APZCTreeManager::ClearTree()
       {
         nodesToDestroy.AppendElement(aNode);
       });
 
   for (size_t i = 0; i < nodesToDestroy.Length(); i++) {
     nodesToDestroy[i]->Destroy();
   }
   mRootNode = nullptr;
+
+  RefPtr<APZCTreeManager> self(this);
+  NS_DispatchToMainThread(NS_NewRunnableFunction([self] {
+    self->mFlushObserver->Unregister();
+    self->mFlushObserver = nullptr;
+  }));
 }
 
 RefPtr<HitTestingTreeNode>
 APZCTreeManager::GetRootNode() const
 {
   MutexAutoLock lock(mTreeLock);
   return mRootNode;
 }
--- a/gfx/layers/apz/src/APZCTreeManager.h
+++ b/gfx/layers/apz/src/APZCTreeManager.h
@@ -237,20 +237,20 @@ public:
    * documentation on AsyncPanZoomController::AdjustScrollForSurfaceShift for
    * some more details. This is only currently needed due to surface shifts
    * caused by the dynamic toolbar on Android.
    */
   void AdjustScrollForSurfaceShift(const ScreenPoint& aShift) override;
 
   /**
    * Calls Destroy() on all APZC instances attached to the tree, and resets the
-   * tree back to empty. This function may be called multiple times during the
-   * lifetime of this APZCTreeManager, but it must always be called at least once
-   * when this APZCTreeManager is no longer needed. Failing to call this function
-   * may prevent objects from being freed properly.
+   * tree back to empty. This function must be called exactly once during the
+   * lifetime of this APZCTreeManager, when this APZCTreeManager is no longer
+   * needed. Failing to call this function may prevent objects from being freed
+   * properly.
    */
   void ClearTree();
 
   /**
    * Tests if a screen point intersect an apz in the tree.
    */
   bool HitTestAPZC(const ScreenIntPoint& aPoint);
 
@@ -513,15 +513,19 @@ private:
   int32_t mRetainedTouchIdentifier;
   /* Tracks the number of touch points we are tracking that are currently on
    * the screen. */
   TouchCounter mTouchCounter;
   /* For logging the APZC tree for debugging (enabled by the apz.printtree
    * pref). */
   gfx::TreeLog mApzcTreeLog;
 
+  class CheckerboardFlushObserver;
+  friend class CheckerboardFlushObserver;
+  RefPtr<CheckerboardFlushObserver> mFlushObserver;
+
   static float sDPI;
 };
 
 } // namespace layers
 } // namespace mozilla
 
 #endif // mozilla_layers_PanZoomController_h
--- a/gfx/layers/apz/src/AsyncPanZoomController.cpp
+++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp
@@ -3236,39 +3236,55 @@ AsyncPanZoomController::ReportCheckerboa
   MutexAutoLock lock(mCheckerboardEventLock);
   if (!mCheckerboardEvent && (recordTrace || forTelemetry)) {
     mCheckerboardEvent = MakeUnique<CheckerboardEvent>(recordTrace);
   }
   mPotentialCheckerboardTracker.InTransform(IsTransformingState(mState));
   if (magnitude) {
     mPotentialCheckerboardTracker.CheckerboardSeen();
   }
-  if (mCheckerboardEvent && mCheckerboardEvent->RecordFrameInfo(magnitude)) {
+  UpdateCheckerboardEvent(lock, magnitude);
+}
+
+void
+AsyncPanZoomController::UpdateCheckerboardEvent(const MutexAutoLock& aProofOfLock,
+                                                uint32_t aMagnitude)
+{
+  if (mCheckerboardEvent && mCheckerboardEvent->RecordFrameInfo(aMagnitude)) {
     // This checkerboard event is done. Report some metrics to telemetry.
     mozilla::Telemetry::Accumulate(mozilla::Telemetry::CHECKERBOARD_SEVERITY,
       mCheckerboardEvent->GetSeverity());
     mozilla::Telemetry::Accumulate(mozilla::Telemetry::CHECKERBOARD_PEAK,
       mCheckerboardEvent->GetPeak());
     mozilla::Telemetry::Accumulate(mozilla::Telemetry::CHECKERBOARD_DURATION,
       (uint32_t)mCheckerboardEvent->GetDuration().ToMilliseconds());
 
     mPotentialCheckerboardTracker.CheckerboardDone();
 
-    if (recordTrace) {
+    if (gfxPrefs::APZRecordCheckerboarding()) {
       // if the pref is enabled, also send it to the storage class. it may be
       // chosen for public display on about:checkerboard, the hall of fame for
       // checkerboard events.
       uint32_t severity = mCheckerboardEvent->GetSeverity();
       std::string log = mCheckerboardEvent->GetLog();
       CheckerboardEventStorage::Report(severity, log);
     }
     mCheckerboardEvent = nullptr;
   }
 }
 
+void
+AsyncPanZoomController::FlushActiveCheckerboardReport()
+{
+  MutexAutoLock lock(mCheckerboardEventLock);
+  // Pretend like we got a frame with 0 pixels checkerboarded. This will
+  // terminate the checkerboard event and flush it out
+  UpdateCheckerboardEvent(lock, 0);
+}
+
 bool AsyncPanZoomController::IsCurrentlyCheckerboarding() const {
   ReentrantMonitorAutoEnter lock(mMonitor);
 
   if (!gfxPrefs::APZAllowCheckerboarding() || mScrollMetadata.IsApzForceDisabled()) {
     return false;
   }
 
   CSSPoint currentScrollOffset = mFrameMetrics.GetScrollOffset() + mTestAsyncScrollOffset;
--- a/gfx/layers/apz/src/AsyncPanZoomController.h
+++ b/gfx/layers/apz/src/AsyncPanZoomController.h
@@ -230,16 +230,25 @@ public:
   uint32_t GetCheckerboardMagnitude() const;
 
   /**
    * Report the number of CSSPixel-milliseconds of checkerboard to telemetry.
    */
   void ReportCheckerboard(const TimeStamp& aSampleTime);
 
   /**
+   * Flush any active checkerboard report that's in progress. This basically
+   * pretends like any in-progress checkerboard event has terminated, and pushes
+   * out the report to the checkerboard reporting service and telemetry. If the
+   * checkerboard event has not really finished, it will start a new event
+   * on the next composite.
+   */
+  void FlushActiveCheckerboardReport();
+
+  /**
    * Returns whether or not the APZC is currently in a state of checkerboarding.
    * This is a simple computation based on the last-painted content and whether
    * the async transform has pushed it so far that it doesn't fully contain the
    * composition bounds.
    */
   bool IsCurrentlyCheckerboarding() const;
 
   /**
@@ -1162,16 +1171,20 @@ private:
   bool mAsyncTransformAppliedToContent;
 
 
   /* ===================================================================
    * The functions and members in this section are used for checkerboard
    * recording.
    */
 private:
+  // Helper function to update the in-progress checkerboard event, if any.
+  void UpdateCheckerboardEvent(const MutexAutoLock& aProofOfLock,
+                               uint32_t aMagnitude);
+
   // Mutex protecting mCheckerboardEvent
   Mutex mCheckerboardEventLock;
   // This is created when this APZC instance is first included as part of a
   // composite. If a checkerboard event takes place, this is destroyed at the
   // end of the event, and a new one is created on the next composite.
   UniquePtr<CheckerboardEvent> mCheckerboardEvent;
   // This is used to track the total amount of time that we could reasonably
   // be checkerboarding. Combined with other info, this allows us to meaningfully
--- a/gfx/layers/apz/test/gtest/APZCBasicTester.h
+++ b/gfx/layers/apz/test/gtest/APZCBasicTester.h
@@ -43,16 +43,17 @@ protected:
         metrics.GetScrollableRect().TopLeft(),
         metrics.GetScrollableRect().Size() - metrics.CalculateCompositedSizeInCssPixels());
   }
 
   virtual void TearDown()
   {
     while (mcc->RunThroughDelayedTasks());
     apzc->Destroy();
+    tm->ClearTree();
   }
 
   void MakeApzcWaitForMainThread()
   {
     apzc->SetWaitForMainThread();
   }
 
   void MakeApzcZoomable()
--- a/gfx/layers/apz/util/CheckerboardReportService.cpp
+++ b/gfx/layers/apz/util/CheckerboardReportService.cpp
@@ -8,16 +8,17 @@
 #include "gfxPrefs.h" // for gfxPrefs
 #include "jsapi.h" // for JS_Now
 #include "MainThreadUtils.h" // for NS_IsMainThread
 #include "mozilla/Assertions.h" // for MOZ_ASSERT
 #include "mozilla/ClearOnShutdown.h" // for ClearOnShutdown
 #include "mozilla/Unused.h"
 #include "mozilla/dom/CheckerboardReportServiceBinding.h" // for dom::CheckerboardReports
 #include "mozilla/gfx/GPUParent.h"
+#include "mozilla/gfx/GPUProcessManager.h"
 #include "nsContentUtils.h" // for nsContentUtils
 #include "nsXULAppAPI.h"
 
 namespace mozilla {
 namespace layers {
 
 /*static*/ StaticRefPtr<CheckerboardEventStorage> CheckerboardEventStorage::sInstance;
 
@@ -202,10 +203,26 @@ CheckerboardReportService::IsRecordingEn
 }
 
 void
 CheckerboardReportService::SetRecordingEnabled(bool aEnabled)
 {
   gfxPrefs::SetAPZRecordCheckerboarding(aEnabled);
 }
 
+void
+CheckerboardReportService::FlushActiveReports()
+{
+  MOZ_ASSERT(XRE_IsParentProcess());
+  gfx::GPUProcessManager* gpu = gfx::GPUProcessManager::Get();
+  if (gpu && gpu->NotifyGpuObservers("APZ:FlushActiveCheckerboard")) {
+    return;
+  }
+
+  nsCOMPtr<nsIObserverService> obsSvc = mozilla::services::GetObserverService();
+  MOZ_ASSERT(obsSvc);
+  if (obsSvc) {
+    obsSvc->NotifyObservers(nullptr, "APZ:FlushActiveCheckerboard", nullptr);
+  }
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/gfx/layers/apz/util/CheckerboardReportService.h
+++ b/gfx/layers/apz/util/CheckerboardReportService.h
@@ -125,16 +125,17 @@ public:
 
 public:
   /*
    * The methods exposed via the webidl.
    */
   void GetReports(nsTArray<dom::CheckerboardReport>& aOutReports);
   bool IsRecordingEnabled() const;
   void SetRecordingEnabled(bool aEnabled);
+  void FlushActiveReports();
 
 private:
   virtual ~CheckerboardReportService() {}
 
   nsCOMPtr<nsISupports> mParent;
 };
 
 } // namespace dom
--- a/gfx/src/nsThemeConstants.h
+++ b/gfx/src/nsThemeConstants.h
@@ -287,8 +287,10 @@
 
 #define NS_THEME_MAC_VIBRANCY_LIGHT                        243
 #define NS_THEME_MAC_VIBRANCY_DARK                         244
 #define NS_THEME_MAC_DISCLOSURE_BUTTON_OPEN                245
 #define NS_THEME_MAC_DISCLOSURE_BUTTON_CLOSED              246
 
 #define NS_THEME_GTK_INFO_BAR                              247
 #define NS_THEME_MAC_SOURCE_LIST                           248
+#define NS_THEME_MAC_SOURCE_LIST_SELECTION                 249
+#define NS_THEME_MAC_ACTIVE_SOURCE_LIST_SELECTION          250
--- a/gfx/thebes/gfxPlatform.cpp
+++ b/gfx/thebes/gfxPlatform.cpp
@@ -885,22 +885,16 @@ gfxPlatform::Shutdown()
     // WebGL on Optimus.
     GLContextProviderEGL::Shutdown();
 #endif
 
     if (XRE_IsParentProcess()) {
       GPUProcessManager::Shutdown();
     }
 
-    // This is a bit iffy - we're assuming that we were the ones that set the
-    // log forwarder in the Factory, so that it's our responsibility to
-    // delete it.
-    delete mozilla::gfx::Factory::GetLogForwarder();
-    mozilla::gfx::Factory::SetLogForwarder(nullptr);
-
     gfx::Factory::ShutDown();
 
     delete gGfxPlatformPrefsLock;
 
     gfxVars::Shutdown();
     gfxPrefs::DestroySingleton();
     gfxFont::DestroySingletons();
 
--- a/gfx/vr/VRDisplayHost.cpp
+++ b/gfx/vr/VRDisplayHost.cpp
@@ -67,16 +67,22 @@ VRDisplayHost::RemoveLayer(VRLayerParent
 
 #if defined(XP_WIN)
 
 void
 VRDisplayHost::SubmitFrame(VRLayerParent* aLayer, const int32_t& aInputFrameID,
   PTextureParent* aTexture, const gfx::Rect& aLeftEyeRect,
   const gfx::Rect& aRightEyeRect)
 {
+  // aInputFrameID is no longer controlled by content with the WebVR 1.1 API
+  // update; however, we will later use this code to enable asynchronous
+  // submission of multiple layers to be composited.  This will enable
+  // us to build browser UX that remains responsive even when content does
+  // not consistently submit frames.
+
   int32_t inputFrameID = aInputFrameID;
   if (inputFrameID == 0) {
     inputFrameID = mInputFrameID;
   }
   if (inputFrameID < 0) {
     // Sanity check to prevent invalid memory access on builds with assertions
     // disabled.
     inputFrameID = 0;
--- a/gfx/vr/VRDisplayPresentation.cpp
+++ b/gfx/vr/VRDisplayPresentation.cpp
@@ -98,15 +98,15 @@ VRDisplayPresentation::GetDOMLayers(nsTA
 }
 
 VRDisplayPresentation::~VRDisplayPresentation()
 {
   DestroyLayers();
   mDisplayClient->PresentationDestroyed();
 }
 
-void VRDisplayPresentation::SubmitFrame(int32_t aInputFrameID)
+void VRDisplayPresentation::SubmitFrame()
 {
   for (VRLayerChild *layer : mLayers) {
-    layer->SubmitFrame(aInputFrameID);
+    layer->SubmitFrame();
     break; // Currently only one layer supported, submit only the first
   }
 }
--- a/gfx/vr/VRDisplayPresentation.h
+++ b/gfx/vr/VRDisplayPresentation.h
@@ -15,17 +15,17 @@ class VRDisplayClient;
 class VRLayerChild;
 
 class VRDisplayPresentation final