Merge m-c to fx-team. a=merge
authorRyan VanderMeulen <ryanvm@gmail.com>
Thu, 26 May 2016 21:48:52 -0400
changeset 338291 e5f2ab6acef01c1c195364b7a23d654685634190
parent 338290 ef5addc3db7d60f63d9880da296f6eebbc160535 (current diff)
parent 338219 4d63dde701b47b8661ab7990f197b6b60e543839 (diff)
child 338292 f23bb662638c633dd1ff44696a9d7fc687401d9e
push id6249
push userjlund@mozilla.com
push dateMon, 01 Aug 2016 13:59:36 +0000
treeherdermozilla-beta@bad9d4f5bf7e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone49.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge m-c to fx-team. a=merge
autom4te.cache/output.0t
autom4te.cache/requests
autom4te.cache/traces.0t
dom/media/platforms/PlatformDecoderModule.cpp
js/src/jit-test/tests/jaeger/bug565202.js
deleted file mode 100644
deleted file mode 100644
deleted file mode 100644
--- a/browser/base/content/browser.css
+++ b/browser/base/content/browser.css
@@ -243,26 +243,16 @@ toolbar[customizing] > .overflow-button 
   -moz-appearance: -moz-window-button-box;
 }
 
 %ifdef XP_MACOSX
 #titlebar-fullscreen-button {
   -moz-appearance: -moz-mac-fullscreen-button;
 }
 
-/* Because these buttons don't move, they should always be aligned the same,
- * left and right were deprecated, so we have to do work to get it to mean that: */
-#titlebar-buttonbox-container:-moz-locale-dir(ltr) {
-  -moz-box-align: start;
-}
-
-#titlebar-buttonbox-container:-moz-locale-dir(rtl) {
-  -moz-box-align: end;
-}
-
 /* Fullscreen and caption buttons don't move with RTL on OS X so override the automatic ordering. */
 #titlebar-secondary-buttonbox:-moz-locale-dir(ltr),
 #titlebar-buttonbox-container:-moz-locale-dir(rtl),
 .titlebar-placeholder[type="fullscreen-button"]:-moz-locale-dir(ltr),
 .titlebar-placeholder[type="caption-buttons"]:-moz-locale-dir(rtl) {
   -moz-box-ordinal-group: 1000;
 }
 
--- a/browser/components/migration/ChromeProfileMigrator.js
+++ b/browser/components/migration/ChromeProfileMigrator.js
@@ -383,17 +383,18 @@ function GetCookiesResource(aProfileFold
                 chromeTimeToDate(row.getResultByName("expires_utc")) / 1000;
               Services.cookies.add(host_key,
                                    row.getResultByName("path"),
                                    row.getResultByName("name"),
                                    row.getResultByName("value"),
                                    row.getResultByName("secure"),
                                    row.getResultByName("httponly"),
                                    false,
-                                   parseInt(expiresUtc));
+                                   parseInt(expiresUtc),
+                                   {});
             } catch (e) {
               Cu.reportError(e);
             }
           }
         },
 
         handleError : function(aError) {
           Cu.reportError("Async statement execution returned with '" +
--- a/browser/components/migration/MSMigrationUtils.jsm
+++ b/browser/components/migration/MSMigrationUtils.jsm
@@ -641,17 +641,18 @@ Cookies.prototype = {
 
       Services.cookies.add(host,
                            path,
                            name,
                            value,
                            Number(flags) & 0x1, // secure
                            false, // httpOnly
                            false, // session
-                           expireTime);
+                           expireTime,
+                           {});
     }
   }
 };
 
 function getTypedURLs(registryKeyPath) {
   // The list of typed URLs is a sort of annotation stored in the registry.
   // The number of entries stored is not UI-configurable, but has changed
   // between different Windows versions. We just keep reading up to the first
--- a/browser/components/migration/tests/unit/test_Chrome_cookies.js
+++ b/browser/components/migration/tests/unit/test_Chrome_cookies.js
@@ -29,17 +29,17 @@ add_task(function* () {
   yield promiseMigration(migrator, MigrationUtils.resourceTypes.COOKIES, PROFILE);
 
   Assert.equal(Services.cookies.countCookiesFromHost(COOKIE.host), 1,
                "Migrated the expected number of unencrypted cookies");
   Assert.equal(Services.cookies.countCookiesFromHost("encryptedcookie.invalid"), 0,
                "Migrated the expected number of encrypted cookies");
 
   // Now check the cookie details.
-  let enumerator = Services.cookies.getCookiesFromHost(COOKIE.host);
+  let enumerator = Services.cookies.getCookiesFromHost(COOKIE.host, {});
   Assert.ok(enumerator.hasMoreElements(), "Cookies available");
   let foundCookie = enumerator.getNext().QueryInterface(Ci.nsICookie2);
 
   for (let prop of Object.keys(COOKIE)) {
     Assert.equal(foundCookie[prop], COOKIE[prop], "Check cookie " + prop);
   }
 
   // Cleanup.
--- a/browser/components/migration/tests/unit/test_IE_cookies.js
+++ b/browser/components/migration/tests/unit/test_IE_cookies.js
@@ -93,17 +93,17 @@ add_task(function* () {
 
   // Migrate cookies.
   yield promiseMigration(migrator, MigrationUtils.resourceTypes.COOKIES);
 
   Assert.equal(Services.cookies.countCookiesFromHost(COOKIE.host), 1,
                "Migrated the expected number of cookies");
 
   // Now check the cookie details.
-  let enumerator = Services.cookies.getCookiesFromHost(COOKIE.host);
+  let enumerator = Services.cookies.getCookiesFromHost(COOKIE.host, {});
   Assert.ok(enumerator.hasMoreElements());
   let foundCookie = enumerator.getNext().QueryInterface(Ci.nsICookie2);
 
   Assert.equal(foundCookie.name, COOKIE.name);
   Assert.equal(foundCookie.value, COOKIE.value);
   Assert.equal(foundCookie.host, "." + COOKIE.host);
   Assert.equal(foundCookie.expiry, Math.floor(COOKIE.expiry / 1000));
 });
--- a/browser/components/sessionstore/SessionCookies.jsm
+++ b/browser/components/sessionstore/SessionCookies.jsm
@@ -120,17 +120,17 @@ var SessionCookiesInternal = {
       let cookieObj = {
         host: cookie.host,
         path: cookie.path || "",
         name: cookie.name || ""
       };
       if (!Services.cookies.cookieExists(cookieObj)) {
         Services.cookies.add(cookie.host, cookie.path || "", cookie.name || "",
                              cookie.value, !!cookie.secure, !!cookie.httponly,
-                             /* isSession = */ true, expiry);
+                             /* isSession = */ true, expiry, cookie.originAttributes || {});
       }
     }
   },
 
   /**
    * Handles observers notifications that are sent whenever cookies are added,
    * changed, or removed. Ensures that the storage is updated accordingly.
    */
@@ -412,16 +412,20 @@ var CookieStore = {
     if (cookie.isHttpOnly) {
       jscookie.httponly = true;
     }
 
     if (cookie.expiry < MAX_EXPIRY) {
       jscookie.expiry = cookie.expiry;
     }
 
+    if (cookie.originAttributes) {
+      jscookie.originAttributes = cookie.originAttributes;
+    }
+
     this._ensureMap(cookie).set(cookie.name, jscookie);
   },
 
   /**
    * Removes a given cookie.
    *
    * @param cookie
    *        The nsICookie2 object to be removed from storage.
--- a/browser/components/sessionstore/test/browser.ini
+++ b/browser/components/sessionstore/test/browser.ini
@@ -101,16 +101,17 @@ skip-if = buildapp == 'mulet'
 [browser_page_title.js]
 [browser_pageStyle.js]
 [browser_pending_tabs.js]
 [browser_privatetabs.js]
 [browser_purge_shistory.js]
 skip-if = e10s # Bug 1271024
 [browser_replace_load.js]
 [browser_restore_redirect.js]
+[browser_restore_cookies_noOriginAttributes.js]
 [browser_scrollPositions.js]
 [browser_scrollPositionsReaderMode.js]
 [browser_sessionHistory.js]
 [browser_sessionStorage.js]
 [browser_swapDocShells.js]
 [browser_switch_remoteness.js]
 run-if = e10s
 [browser_upgrade_backup.js]
new file mode 100644
--- /dev/null
+++ b/browser/components/sessionstore/test/browser_restore_cookies_noOriginAttributes.js
@@ -0,0 +1,172 @@
+/*
+ * Bug 1267910 - The regression test case for session cookies.
+ */
+
+"use strict";
+
+const TEST_HOST = "www.example.com";
+const COOKIE =
+{
+  name: "test1",
+  value: "yes1",
+  path: "/browser/browser/components/sessionstore/test/"
+};
+const SESSION_DATA = `
+{
+  "version": ["sessionrestore", 1],
+  "windows": [{
+    "tabs": [{
+      "entries": [],
+      "lastAccessed": 1463893009797,
+      "hidden": false,
+      "attributes": {},
+      "image": null
+    }, {
+      "entries": [{
+        "url": "http://www.example.com/browser/browser/components/sessionstore/test/browser_1267910_page.html",
+        \"charset": "UTF-8",
+        "ID": 0,
+        "docshellID": 2,
+        "originalURI": "http://www.example.com/browser/browser/components/sessionstore/test/browser_1267910_page.html",
+        \"docIdentifier": 0,
+        "persist": true
+      }],
+      "lastAccessed": 1463893009321,
+      "hidden": false,
+      "attributes": {},
+      "userContextId": 0,
+      "index": 1,
+      "image": "http://www.example.com/favicon.ico"
+    }],
+    "selected": 1,
+    "_closedTabs": [],
+    "busy": false,
+    "width": 1024,
+    "height": 768,
+    "screenX": 4,
+    "screenY": 23,
+    "sizemode": "normal",
+    "cookies": [{
+      "host": "www.example.com",
+      "value": "yes1",
+      "path": "/browser/browser/components/sessionstore/test/",
+      "name": "test1"
+    }]
+  }],
+  "selectedWindow": 1,
+  "_closedWindows": [],
+  "session": {
+    "lastUpdate": 1463893009801,
+    "startTime": 1463893007134,
+    "recentCrashes": 0
+  },
+  "global": {}
+}`;
+const SESSION_DATA_OA = `
+{
+  "version": ["sessionrestore", 1],
+  "windows": [{
+    "tabs": [{
+      "entries": [],
+      "lastAccessed": 1463893009797,
+      "hidden": false,
+      "attributes": {},
+      "image": null
+    }, {
+      "entries": [{
+        "url": "http://www.example.com/browser/browser/components/sessionstore/test/browser_1267910_page.html",
+        \"charset": "UTF-8",
+        "ID": 0,
+        "docshellID": 2,
+        "originalURI": "http://www.example.com/browser/browser/components/sessionstore/test/browser_1267910_page.html",
+        \"docIdentifier": 0,
+        "persist": true
+      }],
+      "lastAccessed": 1463893009321,
+      "hidden": false,
+      "attributes": {},
+      "userContextId": 0,
+      "index": 1,
+      "image": "http://www.example.com/favicon.ico"
+    }],
+    "selected": 1,
+    "_closedTabs": [],
+    "busy": false,
+    "width": 1024,
+    "height": 768,
+    "screenX": 4,
+    "screenY": 23,
+    "sizemode": "normal",
+    "cookies": [{
+      "host": "www.example.com",
+      "value": "yes1",
+      "path": "/browser/browser/components/sessionstore/test/",
+      "name": "test1",
+      "originAttributes": {
+        "addonId": "",
+        "appId": 0,
+        "inIsolatedMozBrowser": false,
+        "signedPkg": "",
+        "userContextId": 0
+      }
+    }]
+  }],
+  "selectedWindow": 1,
+  "_closedWindows": [],
+  "session": {
+    "lastUpdate": 1463893009801,
+    "startTime": 1463893007134,
+    "recentCrashes": 0
+  },
+  "global": {}
+}`;
+
+add_task(function* run_test() {
+  // Wait until initialization is complete.
+  yield SessionStore.promiseInitialized;
+
+  // Clear cookies.
+  Services.cookies.removeAll();
+
+  // Open a new window.
+  let win = yield promiseNewWindowLoaded();
+
+  // Restore window with session cookies that have no originAttributes.
+  ss.setWindowState(win, SESSION_DATA, true);
+
+  let enumerator = Services.cookies.getCookiesFromHost(TEST_HOST, {});
+  let cookie;
+  let cookieCount = 0;
+  while (enumerator.hasMoreElements()) {
+    cookie = enumerator.getNext().QueryInterface(Ci.nsICookie);
+    cookieCount++;
+  }
+
+  // Check that the cookie is restored successfully.
+  is(cookieCount, 1, "expected one cookie");
+  is(cookie.name, COOKIE.name, "cookie name successfully restored");
+  is(cookie.value, COOKIE.value, "cookie value successfully restored");
+  is(cookie.path, COOKIE.path, "cookie path successfully restored");
+
+  // Clear cookies.
+  Services.cookies.removeAll();
+
+  // Restore window with session cookies that have originAttributes within.
+  ss.setWindowState(win, SESSION_DATA_OA, true);
+
+  enumerator = Services.cookies.getCookiesFromHost(TEST_HOST, {});
+  cookieCount = 0;
+  while (enumerator.hasMoreElements()) {
+    cookie = enumerator.getNext().QueryInterface(Ci.nsICookie);
+    cookieCount++;
+  }
+
+  // Check that the cookie is restored successfully.
+  is(cookieCount, 1, "expected one cookie");
+  is(cookie.name, COOKIE.name, "cookie name successfully restored");
+  is(cookie.value, COOKIE.value, "cookie value successfully restored");
+  is(cookie.path, COOKIE.path, "cookie path successfully restored");
+
+  // Close our window.
+  yield BrowserTestUtils.closeWindow(win);
+});
--- a/browser/extensions/pocket/content/pktApi.jsm
+++ b/browser/extensions/pocket/content/pktApi.jsm
@@ -156,17 +156,17 @@ var pktApi = (function() {
 
     /*
      *  All cookies from the Pocket domain
      *  The return format: { cookieName:cookieValue, cookieName:cookieValue, ... }
     */
     function getCookiesFromPocket() {
 
         var cookieManager = Cc["@mozilla.org/cookiemanager;1"].getService(Ci.nsICookieManager2);
-        var pocketCookies = cookieManager.getCookiesFromHost(pocketSiteHost);
+        var pocketCookies = cookieManager.getCookiesFromHost(pocketSiteHost, {});
         var cookies = {};
         while (pocketCookies.hasMoreElements()) {
             var cookie = pocketCookies.getNext().QueryInterface(Ci.nsICookie2);
             cookies[cookie.name] = cookie.value;
         }
         return cookies;
     }
 
--- a/browser/themes/shared/privatebrowsing/aboutPrivateBrowsing.css
+++ b/browser/themes/shared/privatebrowsing/aboutPrivateBrowsing.css
@@ -61,23 +61,27 @@ p {
   line-height: 1.5em;
 }
 
 .list-row {
   overflow: auto;
 }
 
 .list-row > ul > li {
-  float: inline-start;
+  float: left;
   width: 220px;
   line-height: 1.5em;
   margin-inline-start: 1em;
   margin-bottom: 0;
 }
 
+.list-row > ul > li:dir(rtl) {
+  float: right;
+}
+
 .title {
   background-image: url("chrome://browser/skin/privatebrowsing/private-browsing.svg");
   background-size: 64px;
   background-position: left, center;
   font-weight: lighter;
   line-height: 1.5em;
   min-height: 64px;
   margin-inline-start: 0;
@@ -156,32 +160,33 @@ a.button {
   box-shadow: 0 0 1px 1px hsla(0, 0%, 0%, .1),
               0 1px 0 hsla(0, 0%, 0%, .2);
   border-radius: 50%;
   background: white;
   transition: left .2s ease;
 }
 
 .toggle + .toggle-btn::before {
-  float: inline-start;
+  float: left;
   left: 9px;
   visibility: hidden;
   background-size: 16px;
   background-repeat: no-repeat;
   background-color: transparent;
   background-image: url("chrome://browser/skin/privatebrowsing/check.svg");
 }
 
 .toggle + .toggle-btn:dir(rtl)::after {
   left: auto;
   right: 0;
   transition-property: right;
 }
 
 .toggle + .toggle-btn:dir(rtl)::before {
+  float: right;
   left: auto;
   right: 9px;
 }
 
 .toggle:checked + .toggle-btn {
   background: #3fc455;
   border: 1px solid #269939;
 }
--- a/devtools/server/actors/storage.js
+++ b/devtools/server/actors/storage.js
@@ -460,18 +460,20 @@ StorageActors.createActor({
       isDomain: cookie.isDomain,
       isSecure: cookie.isSecure,
       isHttpOnly: cookie.isHttpOnly
     };
   },
 
   populateStoresForHost(host) {
     this.hostVsStores.set(host, new Map());
+    let doc = this.storageActor.document;
 
-    let cookies = this.getCookiesFromHost(host);
+    let cookies = this.getCookiesFromHost(host, doc.nodePrincipal
+                                                   .originAttributes);
 
     for (let cookie of cookies) {
       if (this.isCookieAtHost(cookie, host)) {
         this.hostVsStores.get(host).set(cookie.name, cookie);
       }
     }
   },
 
@@ -565,25 +567,32 @@ StorageActors.createActor({
 
   /**
    * Pass the editItem command from the content to the chrome process.
    *
    * @param {Object} data
    *        See editCookie() for format details.
    */
   editItem: Task.async(function* (data) {
+    let doc = this.storageActor.document;
+    data.originAttributes = doc.nodePrincipal
+                               .originAttributes;
     this.editCookie(data);
   }),
 
   removeItem: Task.async(function* (host, name) {
-    this.removeCookie(host, name);
+    let doc = this.storageActor.document;
+    this.removeCookie(host, name, doc.nodePrincipal
+                                     .originAttributes);
   }),
 
   removeAll: Task.async(function* (host, domain) {
-    this.removeAllCookies(host, domain);
+    let doc = this.storageActor.document;
+    this.removeAllCookies(host, domain, doc.nodePrincipal
+                                           .originAttributes);
   }),
 
   maybeSetupChildProcess() {
     cookieHelpers.onCookieChanged = this.onCookieChanged.bind(this);
 
     if (!DebuggerServer.isInChildProcess) {
       this.getCookiesFromHost =
         cookieHelpers.getCookiesFromHost.bind(cookieHelpers);
@@ -643,23 +652,23 @@ StorageActors.createActor({
       }
 
       return result;
     }
   },
 });
 
 var cookieHelpers = {
-  getCookiesFromHost(host) {
+  getCookiesFromHost(host, originAttributes) {
     // Local files have no host.
     if (host.startsWith("file:///")) {
       host = "";
     }
 
-    let cookies = Services.cookies.getCookiesFromHost(host);
+    let cookies = Services.cookies.getCookiesFromHost(host, originAttributes);
     let store = [];
 
     while (cookies.hasMoreElements()) {
       let cookie = cookies.getNext().QueryInterface(Ci.nsICookie2);
 
       store.push(cookie);
     }
 
@@ -693,17 +702,17 @@ var cookieHelpers = {
    */
   editCookie(data) {
     let {field, oldValue, newValue} = data;
     let origName = field === "name" ? oldValue : data.items.name;
     let origHost = field === "host" ? oldValue : data.items.host;
     let origPath = field === "path" ? oldValue : data.items.path;
     let cookie = null;
 
-    let enumerator = Services.cookies.getCookiesFromHost(origHost);
+    let enumerator = Services.cookies.getCookiesFromHost(origHost, data.originAttributes || {});
     while (enumerator.hasMoreElements()) {
       let nsiCookie = enumerator.getNext().QueryInterface(Ci.nsICookie2);
       if (nsiCookie.name === origName && nsiCookie.host === origHost) {
         cookie = {
           host: nsiCookie.host,
           path: nsiCookie.path,
           name: nsiCookie.name,
           value: nsiCookie.value,
@@ -764,56 +773,57 @@ var cookieHelpers = {
     Services.cookies.add(
       cookie.host,
       cookie.path,
       cookie.name,
       cookie.value,
       cookie.isSecure,
       cookie.isHttpOnly,
       cookie.isSession,
-      cookie.isSession ? MAX_COOKIE_EXPIRY : cookie.expires
+      cookie.isSession ? MAX_COOKIE_EXPIRY : cookie.expires,
+      cookie.originAttributes
     );
   },
 
   _removeCookies(host, opts = {}) {
     function hostMatches(cookieHost, matchHost) {
       if (cookieHost == null) {
         return matchHost == null;
       }
       if (cookieHost.startsWith(".")) {
         return ("." + matchHost).endsWith(cookieHost);
       }
       return cookieHost == host;
     }
 
-    let enumerator = Services.cookies.getCookiesFromHost(host);
+    let enumerator = Services.cookies.getCookiesFromHost(host, opts.originAttributes || {});
     while (enumerator.hasMoreElements()) {
       let cookie = enumerator.getNext().QueryInterface(Ci.nsICookie2);
       if (hostMatches(cookie.host, host) &&
           (!opts.name || cookie.name === opts.name) &&
           (!opts.domain || cookie.host === opts.domain)) {
         Services.cookies.remove(
           cookie.host,
           cookie.name,
           cookie.path,
           false,
           cookie.originAttributes
         );
       }
     }
   },
 
-  removeCookie(host, name) {
+  removeCookie(host, name, originAttributes) {
     if (name !== undefined) {
-      this._removeCookies(host, { name });
+      this._removeCookies(host, { name, originAttributes });
     }
   },
 
-  removeAllCookies(host, domain) {
-    this._removeCookies(host, { domain });
+  removeAllCookies(host, domain, originAttributes) {
+    this._removeCookies(host, { domain, originAttributes });
   },
 
   addCookieObservers() {
     Services.obs.addObserver(cookieHelpers, "cookie-changed", false);
     return null;
   },
 
   removeCookieObservers() {
@@ -856,38 +866,41 @@ var cookieHelpers = {
         break;
     }
   },
 
   handleChildRequest(msg) {
     switch (msg.json.method) {
       case "getCookiesFromHost": {
         let host = msg.data.args[0];
-        let cookies = cookieHelpers.getCookiesFromHost(host);
+        let originAttributes = msg.data.args[1];
+        let cookies = cookieHelpers.getCookiesFromHost(host, originAttributes);
         return JSON.stringify(cookies);
       }
       case "addCookieObservers": {
         return cookieHelpers.addCookieObservers();
       }
       case "removeCookieObservers": {
         return cookieHelpers.removeCookieObservers();
       }
       case "editCookie": {
         let rowdata = msg.data.args[0];
         return cookieHelpers.editCookie(rowdata);
       }
       case "removeCookie": {
         let host = msg.data.args[0];
         let name = msg.data.args[1];
-        return cookieHelpers.removeCookie(host, name);
+        let originAttributes = msg.data.args[2];
+        return cookieHelpers.removeCookie(host, name, originAttributes);
       }
       case "removeAllCookies": {
         let host = msg.data.args[0];
         let domain = msg.data.args[1];
-        return cookieHelpers.removeAllCookies(host, domain);
+        let originAttributes = msg.data.args[2];
+        return cookieHelpers.removeAllCookies(host, domain, originAttributes);
       }
       default:
         console.error("ERR_DIRECTOR_PARENT_UNKNOWN_METHOD", msg.json.method);
         throw new Error("ERR_DIRECTOR_PARENT_UNKNOWN_METHOD");
     }
   },
 };
 
--- a/devtools/shared/gcli/commands/cookie.js
+++ b/devtools/shared/gcli/commands/cookie.js
@@ -80,18 +80,21 @@ exports.items = [
     manual: l10n.lookup("cookieListManual"),
     returnType: "cookies",
     exec: function(args, context) {
       if (context.environment.target.isRemote) {
         throw new Error("The cookie gcli commands only work in a local tab, " +
                         "see bug 1221488");
       }
       let host = new URL(context.environment.target.url).host;
+      let contentWindow  = context.environment.window;
       host = sanitizeHost(host);
-      let enm = cookieMgr.getCookiesFromHost(host);
+      let enm = cookieMgr.getCookiesFromHost(host, contentWindow.document.
+                                                   nodePrincipal.
+                                                   originAttributes);
 
       let cookies = [];
       while (enm.hasMoreElements()) {
         let cookie = enm.getNext().QueryInterface(Ci.nsICookie);
         if (isCookieAtHost(cookie, host)) {
           cookies.push({
             host: cookie.host,
             name: cookie.name,
@@ -122,18 +125,21 @@ exports.items = [
       }
     ],
     exec: function(args, context) {
       if (context.environment.target.isRemote) {
         throw new Error("The cookie gcli commands only work in a local tab, " +
                         "see bug 1221488");
       }
       let host = new URL(context.environment.target.url).host;
+      let contentWindow  = context.environment.window;
       host = sanitizeHost(host);
-      let enm = cookieMgr.getCookiesFromHost(host);
+      let enm = cookieMgr.getCookiesFromHost(host, contentWindow.document.
+                                                   nodePrincipal.
+                                                   originAttributes);
 
       while (enm.hasMoreElements()) {
         let cookie = enm.getNext().QueryInterface(Ci.nsICookie);
         if (isCookieAtHost(cookie, host)) {
           if (cookie.name == args.name) {
             cookieMgr.remove(cookie.host, cookie.name, cookie.path,
                              false, cookie.originAttributes);
           }
@@ -265,20 +271,23 @@ exports.items = [
     exec: function(args, context) {
       if (context.environment.target.isRemote) {
         throw new Error("The cookie gcli commands only work in a local tab, " +
                         "see bug 1221488");
       }
       let host = new URL(context.environment.target.url).host;
       host = sanitizeHost(host);
       let time = Date.parse(args.expires) / 1000;
-
+      let contentWindow  = context.environment.window;
       cookieMgr.add(args.domain ? "." + args.domain : host,
                     args.path ? args.path : "/",
                     args.name,
                     args.value,
                     args.secure,
                     args.httpOnly,
                     args.session,
-                    time);
+                    time,
+                    contentWindow.document.
+                                  nodePrincipal.
+                                  originAttributes);
     }
   }
 ];
--- a/dom/animation/ComputedTimingFunction.cpp
+++ b/dom/animation/ComputedTimingFunction.cpp
@@ -25,18 +25,18 @@ ComputedTimingFunction::Init(const nsTim
 
 static inline double
 StepTiming(uint32_t aSteps,
            double aPortion,
            ComputedTimingFunction::BeforeFlag aBeforeFlag,
            nsTimingFunction::Type aType)
 {
   MOZ_ASSERT(0.0 <= aPortion && aPortion <= 1.0, "out of range");
-  MOZ_ASSERT(aType != nsTimingFunction::Type::StepStart ||
-             aType != nsTimingFunction::Type::StepEnd, "invalid type");
+  MOZ_ASSERT(aType == nsTimingFunction::Type::StepStart ||
+             aType == nsTimingFunction::Type::StepEnd, "invalid type");
 
   if (aPortion == 1.0) {
     return 1.0;
   }
 
   // Calculate current step using step-end behavior
   uint32_t step = uint32_t(aPortion * aSteps); // floor
 
--- a/dom/canvas/CanvasRenderingContext2D.cpp
+++ b/dom/canvas/CanvasRenderingContext2D.cpp
@@ -314,16 +314,21 @@ public:
     mFinalTarget = aFinalTarget;
     mPostFilterBounds = aPostFilterBounds;
     mOffset = aFilterSpaceToTargetOffset;
 
     nsIntRegion sourceGraphicNeededRegion;
     nsIntRegion fillPaintNeededRegion;
     nsIntRegion strokePaintNeededRegion;
 
+    if (aCtx->CurrentState().updateFilterOnWriteOnly) {
+      aCtx->UpdateFilter();
+      aCtx->CurrentState().updateFilterOnWriteOnly = false;
+    }
+
     FilterSupport::ComputeSourceNeededRegions(
       aCtx->CurrentState().filter, mPostFilterBounds,
       sourceGraphicNeededRegion, fillPaintNeededRegion, strokePaintNeededRegion);
 
     mSourceGraphicRect = sourceGraphicNeededRegion.GetBounds();
     mFillPaintRect = fillPaintNeededRegion.GetBounds();
     mStrokePaintRect = strokePaintNeededRegion.GetBounds();
 
@@ -400,16 +405,22 @@ public:
       mFinalTarget, mCtx->CurrentState().filter,
       gfx::Rect(mPostFilterBounds),
       snapshot, mSourceGraphicRect,
       fillPaint, mFillPaintRect,
       strokePaint, mStrokePaintRect,
       mCtx->CurrentState().filterAdditionalImages,
       mPostFilterBounds.TopLeft() - mOffset,
       DrawOptions(1.0f, mCompositionOp));
+
+    const gfx::FilterDescription& filter = mCtx->CurrentState().filter;
+    MOZ_ASSERT(!filter.mPrimitives.IsEmpty());
+    if (filter.mPrimitives.LastElement().IsTainted() && mCtx->mCanvasElement) {
+      mCtx->mCanvasElement->SetWriteOnly();
+    }
   }
 
   DrawTarget* DT()
   {
     return mTarget;
   }
 
 private:
@@ -2463,16 +2474,19 @@ CanvasRenderingContext2D::SetFilter(cons
     filterChain.SwapElements(CurrentState().filterChain);
     if (mCanvasElement) {
       CurrentState().filterChainObserver =
         new CanvasFilterChainObserver(CurrentState().filterChain,
                                       mCanvasElement, this);
       UpdateFilter();
     }
   }
+  if (mCanvasElement && !mCanvasElement->IsWriteOnly()) {
+    CurrentState().updateFilterOnWriteOnly = true;
+  }
 }
 
 class CanvasUserSpaceMetrics : public UserSpaceMetricsWithSize
 {
 public:
   CanvasUserSpaceMetrics(const gfx::IntSize& aSize, const nsFont& aFont,
                          nsIAtom* aFontLanguage, bool aExplicitLanguage,
                          nsPresContext* aPresContext)
--- a/dom/canvas/CanvasRenderingContext2D.h
+++ b/dom/canvas/CanvasRenderingContext2D.h
@@ -907,16 +907,17 @@ protected:
                      globalAlpha(1.0f),
                      shadowBlur(0.0),
                      dashOffset(0.0f),
                      op(mozilla::gfx::CompositionOp::OP_OVER),
                      fillRule(mozilla::gfx::FillRule::FILL_WINDING),
                      lineCap(mozilla::gfx::CapStyle::BUTT),
                      lineJoin(mozilla::gfx::JoinStyle::MITER_OR_BEVEL),
                      filterString(MOZ_UTF16("none")),
+                     updateFilterOnWriteOnly(false),
                      imageSmoothingEnabled(true),
                      fontExplicitLanguage(false)
     { }
 
     ContextState(const ContextState& aOther)
         : fontGroup(aOther.fontGroup),
           fontLanguage(aOther.fontLanguage),
           fontFont(aOther.fontFont),
@@ -939,16 +940,17 @@ protected:
           fillRule(aOther.fillRule),
           lineCap(aOther.lineCap),
           lineJoin(aOther.lineJoin),
           filterString(aOther.filterString),
           filterChain(aOther.filterChain),
           filterChainObserver(aOther.filterChainObserver),
           filter(aOther.filter),
           filterAdditionalImages(aOther.filterAdditionalImages),
+          updateFilterOnWriteOnly(aOther.updateFilterOnWriteOnly),
           imageSmoothingEnabled(aOther.imageSmoothingEnabled),
           fontExplicitLanguage(aOther.fontExplicitLanguage)
     { }
 
     void SetColorStyle(Style aWhichStyle, nscolor aColor)
     {
       colorStyles[aWhichStyle] = aColor;
       gradientStyles[aWhichStyle] = nullptr;
@@ -1016,16 +1018,17 @@ protected:
     mozilla::gfx::CapStyle lineCap;
     mozilla::gfx::JoinStyle lineJoin;
 
     nsString filterString;
     nsTArray<nsStyleFilter> filterChain;
     RefPtr<nsSVGFilterChainObserver> filterChainObserver;
     mozilla::gfx::FilterDescription filter;
     nsTArray<RefPtr<mozilla::gfx::SourceSurface>> filterAdditionalImages;
+    bool updateFilterOnWriteOnly;
 
     bool imageSmoothingEnabled;
     bool fontExplicitLanguage;
   };
 
   AutoTArray<ContextState, 3> mStyleStack;
 
   inline ContextState& CurrentState() {
--- a/dom/canvas/test/mochitest.ini
+++ b/dom/canvas/test/mochitest.ini
@@ -251,16 +251,24 @@ tags = imagebitmap
 [test_toDataURL_parameters.html]
 [test_windingRuleUndefined.html]
 [test_2d.fillText.gradient.html]
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # bug 1040965
 [test_2d_composite_canvaspattern_setTransform.html]
 [test_createPattern_broken.html]
 [test_setlinedash.html]
 [test_filter.html]
+skip-if = (e10s && debug && os == 'win')
+[test_filter_tainted.html]
+skip-if = (e10s && debug && os == 'win')
+[test_filter_tainted_source_graphics.html]
+skip-if = (e10s && debug && os == 'win')
+[test_filter_tainted_displacement_map.html]
+skip-if = (e10s && debug && os == 'win')
+[test_filter_tainted_displacement_map_source_graphics.html]
 [test_offscreencanvas_toblob.html]
 subsuite = gpu
 tags = offscreencanvas
 [test_offscreencanvas_toimagebitmap.html]
 subsuite = gpu
 tags = offscreencanvas
 [test_offscreencanvas_basic_webgl.html]
 subsuite = gpu
new file mode 100644
--- /dev/null
+++ b/dom/canvas/test/test_filter_image.html
@@ -0,0 +1,45 @@
+<!DOCTYPE HTML>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" href="/tests/SimpleTest/test.css">
+<body onload="runTest()">
+<svg style="display: block; width: 0; height: 0">
+  <defs>
+    <filter id="tainted">
+      <feImage xlink:href="http://example.com/tests/dom/canvas/test/crossorigin/image.png" />
+    </filter>
+  </defs>
+</svg>
+<script>
+
+function runTest() {
+
+  SpecialPowers.pushPrefEnv({ 'set': [['canvas.filters.enabled', true]] }, function () {
+  
+    var canvas = document.createElement('canvas');
+    var ctx = canvas.getContext('2d');
+  
+    ctx.filter = 'url(#tainted)';
+    ctx.rect(0, 0, 16, 16);
+    ctx.fill();
+  
+    var expected_error = 'SecurityError';
+  
+    var data;
+    try {
+      data = ctx.getImageData(0, 0, 16, 16);
+      actual_error = "";
+    } catch (e) {
+      actual_error = e.name;
+    }
+  
+    is(actual_error, expected_error, 'Canvas should have been tainted and throw a SecurityError');
+  
+    SimpleTest.finish();
+    
+  });
+
+}
+
+SimpleTest.waitForExplicitFinish();
+
+</script>
new file mode 100644
--- /dev/null
+++ b/dom/canvas/test/test_filter_tainted.html
@@ -0,0 +1,62 @@
+<!DOCTYPE HTML>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" href="/tests/SimpleTest/test.css">
+<body onload="runTest()" style="margin: 0; padding: 0">
+<svg style="display: block; width: 0; height: 0">
+  <defs>
+    <filter id="tainted">
+      <feImage xlink:href="http://example.com/tests/dom/canvas/test/image_red-16x16.png"/>
+    </filter>
+  </defs>
+</svg>
+<canvas id="c" width="16" height="16"></canvas>
+<script>
+
+function isPixel(ctx, x,y, r,g,b,a, pos, color, d) {
+  var pixel = ctx.getImageData(x, y, 1, 1);
+  var pr = pixel.data[0],
+      pg = pixel.data[1],
+      pb = pixel.data[2],
+      pa = pixel.data[3];
+  ok(r - d <= pr && pr <= r + d &&
+     g - d <= pg && pg <= g + d &&
+     b - d <= pb && pb <= b + d &&
+     a - d <= pa && pa <= a + d,
+     'pixel ' + pos + ' is ' + pr + ',' + pg + ',' + pb + ',' + pa + '; expected ' + color + ' +/- ' + d);
+}
+
+function runTest() {
+
+  SpecialPowers.pushPrefEnv({ 'set': [['canvas.filters.enabled', true]] }, function () {
+  
+    var canvas = document.getElementById('c');
+    var ctx = canvas.getContext('2d');
+  
+    ctx.filter = 'url(#tainted)';
+    ctx.rect(0, 0, 16, 16);
+    ctx.fill();
+
+    var canvas2 = document.createElement('canvas');
+    var ctx2 = SpecialPowers.wrap(canvas2.getContext('2d'));
+    ctx2.drawWindow(window, 0, 0, 16, 16, 'rgb(255,255,255)', 0);
+    isPixel(ctx2, 8,8, 255,0,0,255, '8,8', "255,0,0,255", 5);
+  
+    var expected_error = 'SecurityError';
+    var data;
+    try {
+      data = ctx.getImageData(0, 0, 16, 16);
+      actual_error = "";
+    } catch (e) {
+      actual_error = e.name;
+    }
+    is(actual_error, expected_error, 'canvas should have been tainted and throw a SecurityError');
+  
+    SimpleTest.finish();
+    
+  });
+
+}
+
+SimpleTest.waitForExplicitFinish();
+
+</script>
new file mode 100644
--- /dev/null
+++ b/dom/canvas/test/test_filter_tainted_displacement_map.html
@@ -0,0 +1,64 @@
+<!DOCTYPE HTML>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" href="/tests/SimpleTest/test.css">
+<body onload="runTest()" style="margin: 0; padding: 0">
+<svg style="display: block; width: 0; height: 0">
+  <defs>
+    <filter id="tainted">
+      <feImage xlink:href='image_red-16x16.png' result='img'/>
+      <feImage xlink:href='http://example.com/tests/dom/canvas/test/image_green-16x16.png' result='map'/>
+      <feDisplacementMap in="img" in2="map" scale="20"/>
+    </filter>
+  </defs>
+</svg>
+<canvas id="c" width="16" height="16"></canvas>
+<script>
+
+function isPixel(ctx, x,y, r,g,b,a, pos, color, d) {
+  var pixel = ctx.getImageData(x, y, 1, 1);
+  var pr = pixel.data[0],
+      pg = pixel.data[1],
+      pb = pixel.data[2],
+      pa = pixel.data[3];
+  ok(r - d <= pr && pr <= r + d &&
+     g - d <= pg && pg <= g + d &&
+     b - d <= pb && pb <= b + d &&
+     a - d <= pa && pa <= a + d,
+     'pixel ' + pos + ' is ' + pr + ',' + pg + ',' + pb + ',' + pa + '; expected ' + color + ' +/- ' + d);
+}
+
+function runTest() {
+
+  SpecialPowers.pushPrefEnv({ 'set': [['canvas.filters.enabled', true]] }, function () {
+  
+    var canvas = document.getElementById('c');
+    var ctx = canvas.getContext('2d');
+  
+    ctx.filter = 'url(#tainted)';
+    ctx.rect(0, 0, 16, 16);
+    ctx.fill();
+  
+    var canvas2 = document.createElement('canvas');
+    var ctx2 = SpecialPowers.wrap(canvas2.getContext('2d'));
+    ctx2.drawWindow(window, 0, 0, 16, 16, 'rgb(255,255,255)', 0);
+    isPixel(ctx2, 8,8, 255,0,0,255, '8,8', "255,0,0,255", 5);
+
+    var expected_error = 'SecurityError';
+    var data;
+    try {
+      data = ctx.getImageData(0, 0, 16, 16);
+      actual_error = "";
+    } catch (e) {
+      actual_error = e.name;
+    }
+    is(actual_error, expected_error, 'canvas should have been tainted and throw a SecurityError');
+  
+    SimpleTest.finish();
+    
+  });
+
+}
+
+SimpleTest.waitForExplicitFinish();
+
+</script>
new file mode 100644
--- /dev/null
+++ b/dom/canvas/test/test_filter_tainted_displacement_map_source_graphics.html
@@ -0,0 +1,73 @@
+<!DOCTYPE HTML>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" href="/tests/SimpleTest/test.css">
+<body onload="runTest()" style="margin: 0; padding: 0">
+<svg style="display: block; width: 0; height: 0">
+  <defs>
+    <filter id="tainted1">
+      <feImage xlink:href='image_red-16x16.png' result='img'/>
+      <feDisplacementMap in="img" in2="SourceGraphic" scale="20"/>
+    </filter>
+    <filter id="tainted2">
+      <feImage xlink:href='http://example.com/tests/dom/canvas/test/image_red-16x16.png' result='img'/>
+      <feDisplacementMap in="SourceGraphic" in2="img" scale="20"/>
+    </filter>
+  </defs>
+</svg>
+<canvas id="c" width="16" height="16"></canvas>
+<img id="i" src="http://example.com/tests/dom/canvas/test/image_green-16x16.png"/>
+<script>
+
+function isPixel(ctx, x,y, r,g,b,a, pos, color, d) {
+  var pixel = ctx.getImageData(x, y, 1, 1);
+  var pr = pixel.data[0],
+      pg = pixel.data[1],
+      pb = pixel.data[2],
+      pa = pixel.data[3];
+  ok(r - d <= pr && pr <= r + d &&
+     g - d <= pg && pg <= g + d &&
+     b - d <= pb && pb <= b + d &&
+     a - d <= pa && pa <= a + d,
+     'pixel ' + pos + ' is ' + pr + ',' + pg + ',' + pb + ',' + pa + '; expected ' + color + ' +/- ' + d);
+}
+
+function runTest() {
+
+  SpecialPowers.pushPrefEnv({ 'set': [['canvas.filters.enabled', true]] }, function () {
+  
+    var canvas = document.getElementById('c');
+    var ctx = canvas.getContext('2d');
+    var img = document.getElementById('i');
+    ctx.filter = 'url(#tainted1)';
+    ctx.drawImage(img, 0, 0);
+  
+    var canvas2 = document.createElement('canvas');
+    var ctx2 = SpecialPowers.wrap(canvas2.getContext('2d'));
+    ctx2.drawWindow(window, 0, 0, 16, 16, 'rgb(255, 255, 255)', 0);
+    isPixel(ctx2, 8,8, 255,0,0,255, '8,8', "255,0,0,255", 5);
+
+    ctx.filter = 'url(#tainted2)';
+    ctx.drawImage(img, 0, 0);
+
+    ctx2.drawWindow(window, 0, 0, 16, 16, 'rgb(255, 255, 255)', 0);
+    isPixel(ctx2, 8,8, 0,255,0,255, '8,8', "0,255,0,255", 5);
+
+    var expected_error = 'SecurityError';
+    var data;
+    try {
+      data = ctx.getImageData(0, 0, 16, 16);
+      actual_error = "";
+    } catch (e) {
+      actual_error = e.name;
+    }
+    is(actual_error, expected_error, 'canvas should have been tainted and throw a SecurityError');
+  
+    SimpleTest.finish();
+    
+  });
+
+}
+
+SimpleTest.waitForExplicitFinish();
+
+</script>
new file mode 100644
--- /dev/null
+++ b/dom/canvas/test/test_filter_tainted_source_graphics.html
@@ -0,0 +1,65 @@
+<!DOCTYPE HTML>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" href="/tests/SimpleTest/test.css">
+<body onload="runTest()" style="margin: 0; padding: 0">
+<svg style="display: block; width: 0; height: 0">
+  <defs>
+    <filter id="tainted">
+      <feColorMatrix in="SourceGraphic" type="matrix" values="0 0 0 0 0
+                                                              1 1 1 1 0
+                                                              0 0 0 0 0
+                                                              0 0 0 1 0"/>
+    </filter>                                                     
+  </defs>
+</svg>
+<canvas id="c" width="16" height="16"></canvas>
+<img id="i" src="http://example.com/tests/dom/canvas/test/image_red-16x16.png"/>
+<script>
+
+function isPixel(ctx, x,y, r,g,b,a, pos, color, d) {
+  var pixel = ctx.getImageData(x, y, 1, 1);
+  var pr = pixel.data[0],
+      pg = pixel.data[1],
+      pb = pixel.data[2],
+      pa = pixel.data[3];
+  ok(r - d <= pr && pr <= r + d &&
+     g - d <= pg && pg <= g + d &&
+     b - d <= pb && pb <= b + d &&
+     a - d <= pa && pa <= a + d,
+     'pixel ' + pos + ' is ' + pr + ',' + pg + ',' + pb + ',' + pa + '; expected ' + color + ' +/- ' + d);
+}
+
+function runTest() {
+
+  SpecialPowers.pushPrefEnv({ 'set': [['canvas.filters.enabled', true]] }, function () {
+  
+    var canvas = document.getElementById('c');
+    var ctx = canvas.getContext('2d');
+    var img = document.getElementById('i');
+    ctx.filter = 'url(#tainted)';
+    ctx.drawImage(img, 0, 0);
+
+    var canvas2 = document.createElement('canvas');
+    var ctx2 = SpecialPowers.wrap(canvas2.getContext('2d'));
+    ctx2.drawWindow(window, 0, 0, 16, 16, 'rgb(255,255,255)', 0);
+    isPixel(ctx2, 8,8, 0,255,0,255, '8,8', "0,255,0,255", 5);
+  
+    var expected_error = 'SecurityError';
+    var data;
+    try {
+      data = ctx.getImageData(0, 0, 16, 16);
+      actual_error = "";
+    } catch (e) {
+      actual_error = e.name;
+    }
+    is(actual_error, expected_error, 'canvas should have been tainted and throw a SecurityError');
+  
+    SimpleTest.finish();
+    
+  });
+
+}
+
+SimpleTest.waitForExplicitFinish();
+
+</script>
--- a/dom/manifest/ManifestProcessor.jsm
+++ b/dom/manifest/ManifestProcessor.jsm
@@ -141,34 +141,33 @@ this.ManifestProcessor = { // jshint ign
       const spec = {
         objectName: 'manifest',
         object: rawManifest,
         property: 'orientation',
         expectedType: 'string',
         trim: true
       };
       const value = extractor.extractValue(spec);
-      if (this.orientationTypes.has(value)) {
-        return value;
+      if (value && typeof value === "string" && this.orientationTypes.has(value.toLowerCase())) {
+        return value.toLowerCase();
       }
-      // The spec special-cases orientation to return the empty string.
-      return '';
+      return undefined;
     }
 
     function processDisplayMember() {
       const spec = {
         objectName: 'manifest',
         object: rawManifest,
         property: 'display',
         expectedType: 'string',
         trim: true
       };
       const value = extractor.extractValue(spec);
-      if (displayModes.has(value)) {
-        return value;
+      if (value && typeof value === "string" && displayModes.has(value.toLowerCase())) {
+        return value.toLowerCase();
       }
       return this.defaultDisplayMode;
     }
 
     function processScopeMember() {
       const spec = {
         objectName: 'manifest',
         object: rawManifest,
--- a/dom/manifest/test/test_ManifestProcessor_display.html
+++ b/dom/manifest/test/test_ManifestProcessor_display.html
@@ -20,40 +20,57 @@ typeTests.forEach((type) => {
   var expected = `Expect non - string display to default to "browser".`;
   data.jsonText = JSON.stringify({
     display: type
   });
   var result = processor.process(data);
   is(result.display, 'browser', expected);
 });
 
-/*Test valid modes*/
-var validModes = ['fullscreen', 'standalone', 'minimal-ui', 'browser']
+/*Test valid modes - case insensitive*/
+var validModes = [
+  'fullscreen',
+  'standalone',
+  'minimal-ui',
+  'browser',
+  'FullScreen',
+  'standAlone',
+  'minimal-UI',
+  'BROWSER',
+]
 validModes.forEach((mode) => {
-  var expected = `Expect display mode to be ${mode}.`;
+  var expected = `Expect display mode to be ${mode.toLowerCase()}.`;
   data.jsonText = JSON.stringify({
     display: mode
   });
   var result = processor.process(data);
-  is(result.display, mode, expected);
+  is(result.display, mode.toLowerCase(), expected);
 });
 
 //trim tests
 validModes.forEach((display) => {
   var expected = `Expect trimmed display mode to be returned.`;
   var expandedDisplay =  seperators + lineTerminators + display + lineTerminators + seperators;
   data.jsonText = JSON.stringify({
     display: expandedDisplay
   });
   var result = processor.process(data);
-  is(result.display, display, expected);
+  is(result.display, display.toLowerCase(), expected);
 });
 
 //Unknown modes
-var invalidModes = ['foo', `fooo${whiteSpace}`, '', 'fullscreen,standalone', 'standalone fullscreen', 'FULLSCreEN'];
+var invalidModes = [
+  'foo',
+  `fooo${whiteSpace}`,
+  '',
+  'fullscreen,standalone',
+  'standalone fullscreen',
+  'FULLSCreENS',
+];
+
 invalidModes.forEach((invalidMode) => {
   var expected = `Expect default display mode "browser" to be returned: '${invalidMode}'`;
   data.jsonText = JSON.stringify({
     display: invalidMode
   });
   var result = processor.process(data);
   is(result.display, 'browser', expected);
 });
--- a/dom/manifest/test/test_ManifestProcessor_orientation.html
+++ b/dom/manifest/test/test_ManifestProcessor_orientation.html
@@ -17,67 +17,70 @@ https://bugzilla.mozilla.org/show_bug.cg
 'use strict';
 
 typeTests.forEach((type) => {
   var expected = `Expect non-string orientation to be empty string : ${typeof type}.`;
   data.jsonText = JSON.stringify({
     orientation: type
   });
   var result = processor.process(data);
-  is(result.orientation, '', expected);
+  is(result.orientation, undefined, expected);
 });
 
-
 var validOrientations = [
   'any',
   'natural',
   'landscape',
   'portrait',
   'portrait-primary',
   'portrait-secondary',
   'landscape-primary',
-  'landscape-secondary'
+  'landscape-secondary',
+  'aNy',
+  'NaTuRal',
+  'LANDsCAPE',
+  'PORTRAIT',
+  'portrait-PRIMARY',
+  'portrait-SECONDARY',
+  'LANDSCAPE-primary',
+  'LANDSCAPE-secondary',
 ];
 
 validOrientations.forEach((orientation) => {
   var expected = `Expect orientation to be returned: ${orientation}.`;
-  data.jsonText = JSON.stringify({
-    orientation: orientation
-  });
+  data.jsonText = JSON.stringify({ orientation });
   var result = processor.process(data);
-  is(result.orientation, orientation, expected);
+  is(result.orientation, orientation.toLowerCase(), expected);
 });
 
 var invalidOrientations = [
   'all',
-  'ANY',
-  'NaTuRal',
+  'ANYMany',
+  'NaTuRalle',
   'portrait-primary portrait-secondary',
   'portrait-primary,portrait-secondary',
   'any-natural',
   'portrait-landscape',
   'primary-portrait',
   'secondary-portrait',
   'landscape-landscape',
   'secondary-primary'
-]
+];
 
 invalidOrientations.forEach((orientation) => {
   var expected = `Expect orientation to be empty string: ${orientation}.`;
-  data.jsonText = JSON.stringify({
-    orientation: orientation
-  });
+  data.jsonText = JSON.stringify({ orientation });
   var result = processor.process(data);
-  is(result.orientation, "", expected);
+  is(result.orientation, undefined, expected);
 });
 
 //Trim tests
 validOrientations.forEach((orientation) => {
   var expected = `Expect trimmed orientation to be returned.`;
   var expandedOrientation = `${seperators}${lineTerminators}${orientation}${lineTerminators}${seperators}`;
   data.jsonText = JSON.stringify({
     orientation: expandedOrientation
   });
   var result = processor.process(data);
-  is(result.orientation, orientation, expected);
+  is(result.orientation, orientation.toLowerCase(), expected);
 });
   </script>
 </head>
--- a/dom/media/MediaStreamGraph.h
+++ b/dom/media/MediaStreamGraph.h
@@ -515,16 +515,23 @@ struct TrackBound
  * the DOM wrappers must keep upstream MediaStreams alive as long as they
  * could be being used in the media graph.
  *
  * At any time, however, a set of MediaStream wrappers could be
  * collected via cycle collection. Destroy messages will be sent
  * for those objects in arbitrary order and the MediaStreamGraph has to be able
  * to handle this.
  */
+
+// GetCurrentTime is defined in winbase.h as zero argument macro forwarding to
+// GetTickCount() and conflicts with MediaStream::GetCurrentTime.
+#ifdef GetCurrentTime
+#undef GetCurrentTime
+#endif
+
 class MediaStream : public mozilla::LinkedListElement<MediaStream>
 {
 public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaStream)
 
   explicit MediaStream(DOMMediaStream* aWrapper);
 
 protected:
deleted file mode 100644
--- a/dom/media/platforms/PlatformDecoderModule.cpp
+++ /dev/null
@@ -1,12 +0,0 @@
-/* -*- 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 "PlatformDecoderModule.h"
-
-mozilla::LogModule* GetPDMLog() {
-  static mozilla::LazyLogModule log("PlatformDecoderModule");
-  return log;
-}
--- a/dom/media/platforms/PlatformDecoderModule.h
+++ b/dom/media/platforms/PlatformDecoderModule.h
@@ -25,16 +25,18 @@ namespace layers {
 class ImageContainer;
 } // namespace layers
 
 class MediaDataDecoder;
 class MediaDataDecoderCallback;
 class TaskQueue;
 class CDMProxy;
 
+static LazyLogModule sPDMLog("PlatformDecoderModule");
+
 // The PlatformDecoderModule interface is used by the MediaFormatReader to
 // abstract access to decoders provided by various
 // platforms.
 // Each platform (Windows, MacOSX, Linux, B2G etc) must implement a
 // PlatformDecoderModule to provide access to its decoders in order to get
 // decompressed H.264/AAC from the MediaFormatReader.
 //
 // Decoding is asynchronous, and should be performed on the task queue
--- a/dom/media/platforms/agnostic/OpusDecoder.cpp
+++ b/dom/media/platforms/agnostic/OpusDecoder.cpp
@@ -10,18 +10,17 @@
 #include "VorbisDecoder.h" // For VorbisLayout
 #include "mozilla/Endian.h"
 #include "mozilla/PodOperations.h"
 #include "mozilla/SyncRunnable.h"
 
 #include <stdint.h>
 #include <inttypes.h>  // For PRId64
 
-extern mozilla::LogModule* GetPDMLog();
-#define OPUS_DEBUG(arg, ...) MOZ_LOG(GetPDMLog(), mozilla::LogLevel::Debug, \
+#define OPUS_DEBUG(arg, ...) MOZ_LOG(sPDMLog, mozilla::LogLevel::Debug, \
     ("OpusDataDecoder(%p)::%s: " arg, this, __func__, ##__VA_ARGS__))
 
 namespace mozilla {
 
 OpusDataDecoder::OpusDataDecoder(const AudioInfo& aConfig,
                                  TaskQueue* aTaskQueue,
                                  MediaDataDecoderCallback* aCallback)
   : mInfo(aConfig)
--- a/dom/media/platforms/agnostic/VPXDecoder.cpp
+++ b/dom/media/platforms/agnostic/VPXDecoder.cpp
@@ -10,18 +10,17 @@
 #include "TimeUnits.h"
 #include "mozilla/PodOperations.h"
 #include "mozilla/SyncRunnable.h"
 #include "prsystem.h"
 
 #include <algorithm>
 
 #undef LOG
-extern mozilla::LogModule* GetPDMLog();
-#define LOG(arg, ...) MOZ_LOG(GetPDMLog(), mozilla::LogLevel::Debug, ("VPXDecoder(%p)::%s: " arg, this, __func__, ##__VA_ARGS__))
+#define LOG(arg, ...) MOZ_LOG(sPDMLog, mozilla::LogLevel::Debug, ("VPXDecoder(%p)::%s: " arg, this, __func__, ##__VA_ARGS__))
 
 namespace mozilla {
 
 using namespace gfx;
 using namespace layers;
 
 static int MimeTypeToCodec(const nsACString& aMimeType)
 {
--- a/dom/media/platforms/agnostic/VorbisDecoder.cpp
+++ b/dom/media/platforms/agnostic/VorbisDecoder.cpp
@@ -8,18 +8,17 @@
 #include "VorbisUtils.h"
 #include "XiphExtradata.h"
 
 #include "mozilla/PodOperations.h"
 #include "mozilla/SyncRunnable.h"
 #include "nsAutoPtr.h"
 
 #undef LOG
-extern mozilla::LogModule* GetPDMLog();
-#define LOG(type, msg) MOZ_LOG(GetPDMLog(), type, msg)
+#define LOG(type, msg) MOZ_LOG(sPDMLog, type, msg)
 
 namespace mozilla {
 
 ogg_packet InitVorbisPacket(const unsigned char* aData, size_t aLength,
                          bool aBOS, bool aEOS,
                          int64_t aGranulepos, int64_t aPacketNo)
 {
   ogg_packet packet;
--- a/dom/media/platforms/apple/AppleATDecoder.cpp
+++ b/dom/media/platforms/apple/AppleATDecoder.cpp
@@ -8,18 +8,17 @@
 #include "MP4Decoder.h"
 #include "mp4_demuxer/Adts.h"
 #include "MediaInfo.h"
 #include "AppleATDecoder.h"
 #include "mozilla/Logging.h"
 #include "mozilla/SyncRunnable.h"
 #include "mozilla/UniquePtr.h"
 
-extern mozilla::LogModule* GetPDMLog();
-#define LOG(...) MOZ_LOG(GetPDMLog(), mozilla::LogLevel::Debug, (__VA_ARGS__))
+#define LOG(...) MOZ_LOG(sPDMLog, mozilla::LogLevel::Debug, (__VA_ARGS__))
 #define FourCC2Str(n) ((char[5]){(char)(n >> 24), (char)(n >> 16), (char)(n >> 8), (char)(n), 0})
 
 namespace mozilla {
 
 AppleATDecoder::AppleATDecoder(const AudioInfo& aConfig,
                                TaskQueue* aTaskQueue,
                                MediaDataDecoderCallback* aCallback)
   : mConfig(aConfig)
--- a/dom/media/platforms/apple/AppleCMLinker.cpp
+++ b/dom/media/platforms/apple/AppleCMLinker.cpp
@@ -9,18 +9,17 @@
 #include "AppleCMLinker.h"
 #include "mozilla/ArrayUtils.h"
 #include "nsDebug.h"
 
 #ifndef MOZ_WIDGET_UIKIT
 #include "nsCocoaFeatures.h"
 #endif
 
-extern mozilla::LogModule* GetPDMLog();
-#define LOG(...) MOZ_LOG(GetPDMLog(), mozilla::LogLevel::Debug, (__VA_ARGS__))
+#define LOG(...) MOZ_LOG(sPDMLog, mozilla::LogLevel::Debug, (__VA_ARGS__))
 
 namespace mozilla {
 
 AppleCMLinker::LinkStatus
 AppleCMLinker::sLinkStatus = LinkStatus_INIT;
 
 void* AppleCMLinker::sLink = nullptr;
 CFStringRef AppleCMLinker::skPropExtensionAtoms = nullptr;
--- a/dom/media/platforms/apple/AppleVDADecoder.cpp
+++ b/dom/media/platforms/apple/AppleVDADecoder.cpp
@@ -23,18 +23,17 @@
 #include <algorithm>
 #include "gfxPlatform.h"
 
 #ifndef MOZ_WIDGET_UIKIT
 #include "nsCocoaFeatures.h"
 #include "MacIOSurfaceImage.h"
 #endif
 
-extern mozilla::LogModule* GetPDMLog();
-#define LOG(...) MOZ_LOG(GetPDMLog(), mozilla::LogLevel::Debug, (__VA_ARGS__))
+#define LOG(...) MOZ_LOG(sPDMLog, mozilla::LogLevel::Debug, (__VA_ARGS__))
 //#define LOG_MEDIA_SHA1
 
 namespace mozilla {
 
 static uint32_t ComputeMaxRefFrames(const MediaByteBuffer* aExtraData)
 {
   uint32_t maxRefFrames = 4;
   // Retrieve video dimensions from H264 SPS NAL.
--- a/dom/media/platforms/apple/AppleVDALinker.cpp
+++ b/dom/media/platforms/apple/AppleVDALinker.cpp
@@ -4,18 +4,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include <dlfcn.h>
 
 #include "AppleVDALinker.h"
 #include "nsDebug.h"
 
-extern mozilla::LogModule* GetPDMLog();
-#define LOG(...) MOZ_LOG(GetPDMLog(), mozilla::LogLevel::Debug, (__VA_ARGS__))
+#define LOG(...) MOZ_LOG(sPDMLog, mozilla::LogLevel::Debug, (__VA_ARGS__))
 
 namespace mozilla {
 
 AppleVDALinker::LinkStatus
 AppleVDALinker::sLinkStatus = LinkStatus_INIT;
 
 void* AppleVDALinker::sLink = nullptr;
 CFStringRef AppleVDALinker::skPropWidth = nullptr;
--- a/dom/media/platforms/apple/AppleVTDecoder.cpp
+++ b/dom/media/platforms/apple/AppleVTDecoder.cpp
@@ -15,18 +15,17 @@
 #include "MediaData.h"
 #include "mozilla/ArrayUtils.h"
 #include "nsAutoPtr.h"
 #include "nsThreadUtils.h"
 #include "mozilla/Logging.h"
 #include "VideoUtils.h"
 #include "gfxPlatform.h"
 
-extern mozilla::LogModule* GetPDMLog();
-#define LOG(...) MOZ_LOG(GetPDMLog(), mozilla::LogLevel::Debug, (__VA_ARGS__))
+#define LOG(...) MOZ_LOG(sPDMLog, mozilla::LogLevel::Debug, (__VA_ARGS__))
 
 namespace mozilla {
 
 AppleVTDecoder::AppleVTDecoder(const VideoInfo& aConfig,
                                TaskQueue* aTaskQueue,
                                MediaDataDecoderCallback* aCallback,
                                layers::ImageContainer* aImageContainer)
   : AppleVDADecoder(aConfig, aTaskQueue, aCallback, aImageContainer)
--- a/dom/media/platforms/apple/AppleVTLinker.cpp
+++ b/dom/media/platforms/apple/AppleVTLinker.cpp
@@ -5,18 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include <dlfcn.h>
 
 #include "AppleVTLinker.h"
 #include "mozilla/ArrayUtils.h"
 #include "nsDebug.h"
 
-extern mozilla::LogModule* GetPDMLog();
-#define LOG(...) MOZ_LOG(GetPDMLog(), mozilla::LogLevel::Debug, (__VA_ARGS__))
+#define LOG(...) MOZ_LOG(sPDMLog, mozilla::LogLevel::Debug, (__VA_ARGS__))
 
 namespace mozilla {
 
 AppleVTLinker::LinkStatus
 AppleVTLinker::sLinkStatus = LinkStatus_INIT;
 
 void* AppleVTLinker::sLink = nullptr;
 CFStringRef AppleVTLinker::skPropEnableHWAccel = nullptr;
--- a/dom/media/platforms/ffmpeg/FFmpegLog.h
+++ b/dom/media/platforms/ffmpeg/FFmpegLog.h
@@ -4,12 +4,11 @@
  * 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 __FFmpegLog_h__
 #define __FFmpegLog_h__
 
 #include "mozilla/Logging.h"
 
-extern mozilla::LogModule* GetPDMLog();
-#define FFMPEG_LOG(...) MOZ_LOG(GetPDMLog(), mozilla::LogLevel::Debug, (__VA_ARGS__))
+#define FFMPEG_LOG(...) MOZ_LOG(sPDMLog, mozilla::LogLevel::Debug, (__VA_ARGS__))
 
 #endif // __FFmpegLog_h__
--- a/dom/media/platforms/gonk/GonkAudioDecoderManager.cpp
+++ b/dom/media/platforms/gonk/GonkAudioDecoderManager.cpp
@@ -22,18 +22,17 @@
 #include "MediaInfo.h"
 
 #define CODECCONFIG_TIMEOUT_US 10000LL
 #define READ_OUTPUT_BUFFER_TIMEOUT_US  0LL
 
 #include <android/log.h>
 #define GADM_LOG(...) __android_log_print(ANDROID_LOG_DEBUG, "GonkAudioDecoderManager", __VA_ARGS__)
 
-extern mozilla::LogModule* GetPDMLog();
-#define LOG(...) MOZ_LOG(GetPDMLog(), mozilla::LogLevel::Debug, (__VA_ARGS__))
+#define LOG(...) MOZ_LOG(sPDMLog, mozilla::LogLevel::Debug, (__VA_ARGS__))
 
 using namespace android;
 typedef android::MediaCodecProxy MediaCodecProxy;
 
 namespace mozilla {
 
 GonkAudioDecoderManager::GonkAudioDecoderManager(const AudioInfo& aConfig)
   : mAudioChannels(aConfig.mChannels)
--- a/dom/media/platforms/gonk/GonkMediaDataDecoder.cpp
+++ b/dom/media/platforms/gonk/GonkMediaDataDecoder.cpp
@@ -15,18 +15,17 @@
 #define GMDD_LOG(...) __android_log_print(ANDROID_LOG_DEBUG, "GonkMediaDataDecoder", __VA_ARGS__)
 #define INPUT_TIMEOUT_US 0LL // Don't wait for buffer if none is available.
 #define MIN_QUEUED_SAMPLES 2
 
 #ifdef DEBUG
 #include <utils/AndroidThreads.h>
 #endif
 
-extern mozilla::LogModule* GetPDMLog();
-#define LOG(...) MOZ_LOG(GetPDMLog(), mozilla::LogLevel::Debug, (__VA_ARGS__))
+#define LOG(...) MOZ_LOG(sPDMLog, mozilla::LogLevel::Debug, (__VA_ARGS__))
 
 using namespace android;
 
 namespace mozilla {
 
 bool
 GonkDecoderManager::InitLoopers(MediaData::Type aType)
 {
--- a/dom/media/platforms/gonk/GonkVideoDecoderManager.cpp
+++ b/dom/media/platforms/gonk/GonkVideoDecoderManager.cpp
@@ -27,18 +27,17 @@
 #include <cutils/properties.h>
 
 #define CODECCONFIG_TIMEOUT_US 10000LL
 #define READ_OUTPUT_BUFFER_TIMEOUT_US  0LL
 
 #include <android/log.h>
 #define GVDM_LOG(...) __android_log_print(ANDROID_LOG_DEBUG, "GonkVideoDecoderManager", __VA_ARGS__)
 
-extern mozilla::LogModule* GetPDMLog();
-#define LOG(...) MOZ_LOG(GetPDMLog(), mozilla::LogLevel::Debug, (__VA_ARGS__))
+#define LOG(...) MOZ_LOG(sPDMLog, mozilla::LogLevel::Debug, (__VA_ARGS__))
 using namespace mozilla::layers;
 using namespace android;
 typedef android::MediaCodecProxy MediaCodecProxy;
 
 namespace mozilla {
 
 class GonkTextureClientAllocationHelper : public layers::ITextureClientAllocationHelper
 {
--- a/dom/media/platforms/moz.build
+++ b/dom/media/platforms/moz.build
@@ -19,17 +19,16 @@ EXPORTS += [
 UNIFIED_SOURCES += [
     'agnostic/AgnosticDecoderModule.cpp',
     'agnostic/BlankDecoderModule.cpp',
     'agnostic/OpusDecoder.cpp',
     'agnostic/VorbisDecoder.cpp',
     'agnostic/VPXDecoder.cpp',
     'agnostic/WAVDecoder.cpp',
     'PDMFactory.cpp',
-    'PlatformDecoderModule.cpp',
     'wrappers/FuzzingWrapper.cpp',
     'wrappers/H264Converter.cpp'
 ]
 
 DIRS += [
     'agnostic/gmp',
     'omx'
 ]
--- a/dom/media/platforms/omx/GonkOmxPlatformLayer.cpp
+++ b/dom/media/platforms/omx/GonkOmxPlatformLayer.cpp
@@ -16,23 +16,22 @@
 #include "mozilla/layers/TextureClient.h"
 #include "mozilla/layers/GrallocTextureClient.h"
 #include "mozilla/layers/ImageBridgeChild.h"
 
 #include "ImageContainer.h"
 #include "MediaInfo.h"
 #include "OmxDataDecoder.h"
 
-extern mozilla::LogModule* GetPDMLog();
 
 #ifdef LOG
 #undef LOG
 #endif
 
-#define LOG(arg, ...) MOZ_LOG(GetPDMLog(), mozilla::LogLevel::Debug, ("GonkOmxPlatformLayer(%p)::%s: " arg, this, __func__, ##__VA_ARGS__))
+#define LOG(arg, ...) MOZ_LOG(sPDMLog, mozilla::LogLevel::Debug, ("GonkOmxPlatformLayer(%p)::%s: " arg, this, __func__, ##__VA_ARGS__))
 
 #define CHECK_ERR(err)                    \
   if (err != OK)                       {  \
     LOG("error %d at %s", err, __func__); \
     return NS_ERROR_FAILURE;              \
   }                                       \
 
 // Android proprietary value.
--- a/dom/media/platforms/omx/OmxDataDecoder.cpp
+++ b/dom/media/platforms/omx/OmxDataDecoder.cpp
@@ -7,29 +7,28 @@
 #include "OmxDataDecoder.h"
 
 #include "OMX_Audio.h"
 #include "OMX_Component.h"
 #include "OMX_Types.h"
 
 #include "OmxPlatformLayer.h"
 
-extern mozilla::LogModule* GetPDMLog();
 
 #ifdef LOG
 #undef LOG
 #undef LOGL
 #endif
 
-#define LOG(arg, ...) MOZ_LOG(GetPDMLog(), mozilla::LogLevel::Debug, ("OmxDataDecoder(%p)::%s: " arg, this, __func__, ##__VA_ARGS__))
+#define LOG(arg, ...) MOZ_LOG(sPDMLog, mozilla::LogLevel::Debug, ("OmxDataDecoder(%p)::%s: " arg, this, __func__, ##__VA_ARGS__))
 
 #define LOGL(arg, ...)                                                     \
   {                                                                        \
     void* p = self;                                              \
-    MOZ_LOG(GetPDMLog(), mozilla::LogLevel::Debug,                         \
+    MOZ_LOG(sPDMLog, mozilla::LogLevel::Debug,                         \
             ("OmxDataDecoder(%p)::%s: " arg, p, __func__, ##__VA_ARGS__)); \
   }
 
 #define CHECK_OMX_ERR(err)     \
   if (err != OMX_ErrorNone) {  \
     NotifyError(err, __func__);\
     return;                    \
   }                            \
--- a/dom/media/platforms/omx/OmxPlatformLayer.cpp
+++ b/dom/media/platforms/omx/OmxPlatformLayer.cpp
@@ -8,23 +8,22 @@
 
 #include "OMX_VideoExt.h" // For VP8.
 
 #if defined(MOZ_WIDGET_GONK) && (ANDROID_VERSION == 20 || ANDROID_VERSION == 19)
 #define OMX_PLATFORM_GONK
 #include "GonkOmxPlatformLayer.h"
 #endif
 
-extern mozilla::LogModule* GetPDMLog();
 
 #ifdef LOG
 #undef LOG
 #endif
 
-#define LOG(arg, ...) MOZ_LOG(GetPDMLog(), mozilla::LogLevel::Debug, ("OmxPlatformLayer -- %s: " arg, __func__, ##__VA_ARGS__))
+#define LOG(arg, ...) MOZ_LOG(sPDMLog, mozilla::LogLevel::Debug, ("OmxPlatformLayer -- %s: " arg, __func__, ##__VA_ARGS__))
 
 #define RETURN_IF_ERR(err)     \
   if (err != OMX_ErrorNone) {  \
     LOG("error: 0x%08x", err); \
     return err;                \
   }                            \
 
 // Common OMX decoder configuration code.
--- a/dom/media/platforms/omx/OmxPromiseLayer.cpp
+++ b/dom/media/platforms/omx/OmxPromiseLayer.cpp
@@ -6,23 +6,22 @@
 
 #include "OmxPromiseLayer.h"
 
 #include "ImageContainer.h"
 
 #include "OmxDataDecoder.h"
 #include "OmxPlatformLayer.h"
 
-extern mozilla::LogModule* GetPDMLog();
 
 #ifdef LOG
 #undef LOG
 #endif
 
-#define LOG(arg, ...) MOZ_LOG(GetPDMLog(), mozilla::LogLevel::Debug, ("OmxPromiseLayer(%p)::%s: " arg, this, __func__, ##__VA_ARGS__))
+#define LOG(arg, ...) MOZ_LOG(sPDMLog, mozilla::LogLevel::Debug, ("OmxPromiseLayer(%p)::%s: " arg, this, __func__, ##__VA_ARGS__))
 
 namespace mozilla {
 
 OmxPromiseLayer::OmxPromiseLayer(TaskQueue* aTaskQueue,
                                  OmxDataDecoder* aDataDecoder,
                                  layers::ImageContainer* aImageContainer)
   : mTaskQueue(aTaskQueue)
 {
--- a/dom/media/platforms/wmf/MFTDecoder.cpp
+++ b/dom/media/platforms/wmf/MFTDecoder.cpp
@@ -4,18 +4,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "MFTDecoder.h"
 #include "nsThreadUtils.h"
 #include "WMFUtils.h"
 #include "mozilla/Logging.h"
 
-extern mozilla::LogModule* GetPDMLog();
-#define LOG(...) MOZ_LOG(GetPDMLog(), mozilla::LogLevel::Debug, (__VA_ARGS__))
+#define LOG(...) MOZ_LOG(sPDMLog, mozilla::LogLevel::Debug, (__VA_ARGS__))
 
 namespace mozilla {
 
 MFTDecoder::MFTDecoder()
   : mMFTProvidesOutputSamples(false)
   , mDiscontinuity(true)
 {
   memset(&mInputStreamInfo, 0, sizeof(MFT_INPUT_STREAM_INFO));
--- a/dom/media/platforms/wmf/WMFAudioMFTManager.cpp
+++ b/dom/media/platforms/wmf/WMFAudioMFTManager.cpp
@@ -8,18 +8,17 @@
 #include "MediaInfo.h"
 #include "VideoUtils.h"
 #include "WMFUtils.h"
 #include "nsTArray.h"
 #include "TimeUnits.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/Logging.h"
 
-extern mozilla::LogModule* GetPDMLog();
-#define LOG(...) MOZ_LOG(GetPDMLog(), mozilla::LogLevel::Debug, (__VA_ARGS__))
+#define LOG(...) MOZ_LOG(sPDMLog, mozilla::LogLevel::Debug, (__VA_ARGS__))
 
 namespace mozilla {
 
 static void
 AACAudioSpecificConfigToUserData(uint8_t aAACProfileLevelIndication,
                                  const uint8_t* aAudioSpecConfig,
                                  uint32_t aConfigLength,
                                  nsTArray<BYTE>& aOutUserData)
--- a/dom/media/platforms/wmf/WMFMediaDataDecoder.cpp
+++ b/dom/media/platforms/wmf/WMFMediaDataDecoder.cpp
@@ -8,18 +8,17 @@
 #include "VideoUtils.h"
 #include "WMFUtils.h"
 #include "nsTArray.h"
 #include "mozilla/Telemetry.h"
 
 #include "mozilla/Logging.h"
 #include "mozilla/SyncRunnable.h"
 
-extern mozilla::LogModule* GetPDMLog();
-#define LOG(...) MOZ_LOG(GetPDMLog(), mozilla::LogLevel::Debug, (__VA_ARGS__))
+#define LOG(...) MOZ_LOG(sPDMLog, mozilla::LogLevel::Debug, (__VA_ARGS__))
 
 namespace mozilla {
 
 WMFMediaDataDecoder::WMFMediaDataDecoder(MFTManager* aMFTManager,
                                          TaskQueue* aTaskQueue,
                                          MediaDataDecoderCallback* aCallback)
   : mTaskQueue(aTaskQueue)
   , mCallback(aCallback)
--- a/dom/media/platforms/wmf/WMFVideoMFTManager.cpp
+++ b/dom/media/platforms/wmf/WMFVideoMFTManager.cpp
@@ -25,18 +25,17 @@
 #include "gfxWindowsPlatform.h"
 #include "IMFYCbCrImage.h"
 #include "mozilla/WindowsVersion.h"
 #include "mozilla/Telemetry.h"
 #include "nsPrintfCString.h"
 #include "MediaTelemetryConstants.h"
 #include "GMPUtils.h" // For SplitAt. TODO: Move SplitAt to a central place.
 
-extern mozilla::LogModule* GetPDMLog();
-#define LOG(...) MOZ_LOG(GetPDMLog(), mozilla::LogLevel::Debug, (__VA_ARGS__))
+#define LOG(...) MOZ_LOG(sPDMLog, mozilla::LogLevel::Debug, (__VA_ARGS__))
 
 using mozilla::layers::Image;
 using mozilla::layers::IMFYCbCrImage;
 using mozilla::layers::LayerManager;
 using mozilla::layers::LayersBackend;
 
 #if MOZ_WINSDK_MAXVER < 0x0A000000
 // Windows 10+ SDK has VP80 and VP90 defines
--- a/dom/promise/PromiseDebugging.cpp
+++ b/dom/promise/PromiseDebugging.cpp
@@ -72,16 +72,113 @@ UnwrapPromise(JS::Handle<JSObject*> aPro
 {
   Promise* promise;
   if (NS_WARN_IF(NS_FAILED(UNWRAP_OBJECT(Promise, aPromise, promise)))) {
     aRv.ThrowTypeError<MSG_IS_NOT_PROMISE>(NS_LITERAL_STRING("Argument"));
     return nullptr;
   }
   return promise;
 }
+#endif // SPIDERMONKEY_PROMISE
+
+#ifdef SPIDERMONKEY_PROMISE
+/* static */ void
+PromiseDebugging::GetState(GlobalObject& aGlobal, JS::Handle<JSObject*> aPromise,
+                           PromiseDebuggingStateHolder& aState,
+                           ErrorResult& aRv)
+{
+  JSContext* cx = aGlobal.Context();
+  JS::Rooted<JSObject*> obj(cx, js::CheckedUnwrap(aPromise));
+  if (!obj || !JS::IsPromiseObject(obj)) {
+    aRv.ThrowTypeError<MSG_IS_NOT_PROMISE>(NS_LITERAL_STRING(
+        "Argument of PromiseDebugging.getState"));
+    return;
+  }
+  switch (JS::GetPromiseState(obj)) {
+  case JS::PromiseState::Pending:
+    aState.mState = PromiseDebuggingState::Pending;
+    break;
+  case JS::PromiseState::Fulfilled:
+    aState.mState = PromiseDebuggingState::Fulfilled;
+    aState.mValue = JS::GetPromiseResult(obj);
+    break;
+  case JS::PromiseState::Rejected:
+    aState.mState = PromiseDebuggingState::Rejected;
+    aState.mReason = JS::GetPromiseResult(obj);
+    break;
+  }
+}
+
+/* static */ void
+PromiseDebugging::GetPromiseID(GlobalObject& aGlobal,
+                               JS::Handle<JSObject*> aPromise,
+                               nsString& aID,
+                               ErrorResult& aRv)
+{
+  JSContext* cx = aGlobal.Context();
+  JS::Rooted<JSObject*> obj(cx, js::CheckedUnwrap(aPromise));
+  if (!obj || !JS::IsPromiseObject(obj)) {
+    aRv.ThrowTypeError<MSG_IS_NOT_PROMISE>(NS_LITERAL_STRING(
+        "Argument of PromiseDebugging.getState"));
+    return;
+  }
+  uint64_t promiseID = JS::GetPromiseID(obj);
+  aID = sIDPrefix;
+  aID.AppendInt(promiseID);
+}
+
+/* static */ void
+PromiseDebugging::GetAllocationStack(GlobalObject& aGlobal,
+                                     JS::Handle<JSObject*> aPromise,
+                                     JS::MutableHandle<JSObject*> aStack,
+                                     ErrorResult& aRv)
+{
+  JSContext* cx = aGlobal.Context();
+  JS::Rooted<JSObject*> obj(cx, js::CheckedUnwrap(aPromise));
+  if (!obj || !JS::IsPromiseObject(obj)) {
+    aRv.ThrowTypeError<MSG_IS_NOT_PROMISE>(NS_LITERAL_STRING(
+        "Argument of PromiseDebugging.getAllocationStack"));
+    return;
+  }
+  aStack.set(JS::GetPromiseAllocationSite(obj));
+}
+
+/* static */ void
+PromiseDebugging::GetRejectionStack(GlobalObject& aGlobal,
+                                    JS::Handle<JSObject*> aPromise,
+                                    JS::MutableHandle<JSObject*> aStack,
+                                    ErrorResult& aRv)
+{
+  JSContext* cx = aGlobal.Context();
+  JS::Rooted<JSObject*> obj(cx, js::CheckedUnwrap(aPromise));
+  if (!obj || !JS::IsPromiseObject(obj)) {
+    aRv.ThrowTypeError<MSG_IS_NOT_PROMISE>(NS_LITERAL_STRING(
+        "Argument of PromiseDebugging.getRejectionStack"));
+    return;
+  }
+  aStack.set(JS::GetPromiseResolutionSite(obj));
+}
+
+/* static */ void
+PromiseDebugging::GetFullfillmentStack(GlobalObject& aGlobal,
+                                       JS::Handle<JSObject*> aPromise,
+                                       JS::MutableHandle<JSObject*> aStack,
+                                       ErrorResult& aRv)
+{
+  JSContext* cx = aGlobal.Context();
+  JS::Rooted<JSObject*> obj(cx, js::CheckedUnwrap(aPromise));
+  if (!obj || !JS::IsPromiseObject(obj)) {
+    aRv.ThrowTypeError<MSG_IS_NOT_PROMISE>(NS_LITERAL_STRING(
+        "Argument of PromiseDebugging.getFulfillmentStack"));
+    return;
+  }
+  aStack.set(JS::GetPromiseResolutionSite(obj));
+}
+
+#else
 
 /* static */ void
 PromiseDebugging::GetState(GlobalObject&, JS::Handle<JSObject*> aPromise,
                            PromiseDebuggingStateHolder& aState,
                            ErrorResult& aRv)
 {
   Promise* promise = UnwrapPromise(aPromise, aRv);
   if (aRv.Failed()) {
--- a/dom/promise/PromiseDebugging.h
+++ b/dom/promise/PromiseDebugging.h
@@ -27,43 +27,44 @@ class UncaughtRejectionObserver;
 class FlushRejections;
 
 class PromiseDebugging
 {
 public:
   static void Init();
   static void Shutdown();
 
-#ifndef SPIDERMONKEY_PROMISE
   static void GetState(GlobalObject&, JS::Handle<JSObject*> aPromise,
                        PromiseDebuggingStateHolder& aState,
                        ErrorResult& aRv);
 
+  static void GetPromiseID(GlobalObject&, JS::Handle<JSObject*>, nsString&,
+                           ErrorResult&);
+
   static void GetAllocationStack(GlobalObject&, JS::Handle<JSObject*> aPromise,
                                  JS::MutableHandle<JSObject*> aStack,
                                  ErrorResult& aRv);
   static void GetRejectionStack(GlobalObject&, JS::Handle<JSObject*> aPromise,
                                 JS::MutableHandle<JSObject*> aStack,
                                 ErrorResult& aRv);
   static void GetFullfillmentStack(GlobalObject&,
                                    JS::Handle<JSObject*> aPromise,
                                    JS::MutableHandle<JSObject*> aStack,
                                    ErrorResult& aRv);
+
+#ifndef SPIDERMONKEY_PROMISE
   static void GetDependentPromises(GlobalObject&,
                                    JS::Handle<JSObject*> aPromise,
                                    nsTArray<RefPtr<Promise>>& aPromises,
                                    ErrorResult& aRv);
   static double GetPromiseLifetime(GlobalObject&,
                                    JS::Handle<JSObject*> aPromise,
                                    ErrorResult& aRv);
   static double GetTimeToSettle(GlobalObject&, JS::Handle<JSObject*> aPromise,
                                 ErrorResult& aRv);
-
-  static void GetPromiseID(GlobalObject&, JS::Handle<JSObject*>, nsString&,
-                           ErrorResult&);
 #endif // SPIDERMONKEY_PROMISE
 
   // Mechanism for watching uncaught instances of Promise.
   // XXXbz figure out the plan
   static void AddUncaughtRejectionObserver(GlobalObject&,
                                            UncaughtRejectionObserver& aObserver);
   static bool RemoveUncaughtRejectionObserver(GlobalObject&,
                                               UncaughtRejectionObserver& aObserver);
--- a/dom/webidl/Document.webidl
+++ b/dom/webidl/Document.webidl
@@ -217,17 +217,17 @@ partial interface Document {
   [ChromeOnly]
   readonly attribute unsigned long referrerPolicy;
 
 };
 
 // https://fullscreen.spec.whatwg.org/#api
 partial interface Document {
   // Note: Per spec the 'S' in these two is lowercase, but the "Moz"
-  // versions hve it uppercase.
+  // versions have it uppercase.
   [LenientSetter, Func="nsDocument::IsUnprefixedFullscreenEnabled"]
   readonly attribute boolean fullscreen;
   [BinaryName="fullscreen", Deprecated="PrefixedFullscreenAPI"]
   readonly attribute boolean mozFullScreen;
   [LenientSetter, Func="nsDocument::IsUnprefixedFullscreenEnabled"]
   readonly attribute boolean fullscreenEnabled;
   [BinaryName="fullscreenEnabled", Deprecated="PrefixedFullscreenAPI"]
   readonly attribute boolean mozFullScreenEnabled;
--- a/dom/webidl/PromiseDebugging.webidl
+++ b/dom/webidl/PromiseDebugging.webidl
@@ -47,33 +47,39 @@ callback interface UncaughtRejectionObse
    * @param p A Promise that was previously left in uncaught state is
    * now caught, i.e. it is not the last in its chain anymore.
    */
   void onConsumed(Promise<any> p);
 };
 
 [ChromeOnly, Exposed=(Window,System)]
 interface PromiseDebugging {
-#ifndef SPIDERMONKEY_PROMISE
   /**
    * The various functions on this interface all expect to take promises but
    * don't want the WebIDL behavior of assimilating random passed-in objects
    * into promises.  They also want to treat Promise subclass instances as
    * promises instead of wrapping them in a vanilla Promise, which is what the
    * IDL spec says to do.  So we list all our arguments as "object" instead of
    * "Promise" and check for them being a Promise internally.
    */
 
   /**
    * Get the current state of the given promise.
    */
   [Throws]
   static PromiseDebuggingStateHolder getState(object p);
 
   /**
+   * Return an identifier for a promise. This identifier is guaranteed
+   * to be unique to the current process.
+   */
+  [Throws]
+  static DOMString getPromiseID(object p);
+
+  /**
    * Return the stack to the promise's allocation point.  This can
    * return null if the promise was not created from script.
    */
   [Throws]
   static object? getAllocationStack(object p);
 
   /**
    * Return the stack to the promise's rejection point, if the
@@ -86,23 +92,17 @@ interface PromiseDebugging {
   /**
    * Return the stack to the promise's fulfillment point, if the
    * fulfillment happened from script.  This can return null if the
    * promise has not been fulfilled or was not fulfilled from script.
    */
   [Throws]
   static object? getFullfillmentStack(object p);
 
-  /**
-   * Return an identifier for a promise. This identifier is guaranteed
-   * to be unique to this instance of Firefox.
-   */
-  [Throws]
-  static DOMString getPromiseID(object p);
-
+#ifndef SPIDERMONKEY_PROMISE
   /**
    * Get the promises directly depending on a given promise.  These are:
    *
    * 1) Return values of then() calls on the promise
    * 2) Return values of Promise.all() if the given promise was passed in as one
    *    of the arguments.
    * 3) Return values of Promise.race() if the given promise was passed in as
    *    one of the arguments.
--- a/embedding/browser/nsDocShellTreeOwner.cpp
+++ b/embedding/browser/nsDocShellTreeOwner.cpp
@@ -1014,17 +1014,17 @@ nsDocShellTreeOwner::HandleEvent(nsIDOME
   }
 
   nsCOMPtr<nsIDroppedLinkHandler> handler =
     do_GetService("@mozilla.org/content/dropped-link-handler;1");
   if (handler) {
     nsAutoString eventType;
     aEvent->GetType(eventType);
     if (eventType.EqualsLiteral("dragover")) {
-      bool canDropLink;
+      bool canDropLink = false;
       handler->CanDropLink(dragEvent, false, &canDropLink);
       if (canDropLink) {
         aEvent->PreventDefault();
       }
     } else if (eventType.EqualsLiteral("drop")) {
       nsIWebNavigation* webnav = static_cast<nsIWebNavigation*>(mWebBrowser);
 
       nsAutoString link, name;
--- a/extensions/cookie/test/file_testloadflags_chromescript.js
+++ b/extensions/cookie/test/file_testloadflags_chromescript.js
@@ -81,17 +81,17 @@ addMessageListener("init", ({ domain }) 
              .getService(Ci.nsICookieManager2);
 
   info("we are going to remove these cookies");
 
   let count = getCookieCount(cs);
   info(count + " cookies");
 
   cs.removeAll();
-  cs.add(domain, "", "oh", "hai", false, false, true, Math.pow(2, 62));
+  cs.add(domain, "", "oh", "hai", false, false, true, Math.pow(2, 62), {});
   is(cs.countCookiesFromHost(domain), 1, "number of cookies for domain " + domain);
 
   gObs = new obs();
   sendAsyncMessage("init:return");
 });
 
 addMessageListener("getCookieCount", () => {
   let cs = Cc["@mozilla.org/cookiemanager;1"]
--- a/extensions/cookie/test/unit/test_bug526789.js
+++ b/extensions/cookie/test/unit/test_bug526789.js
@@ -8,17 +8,17 @@ function run_test() {
 
   cm.removeAll();
 
   // Allow all cookies.
   Services.prefs.setIntPref("network.cookie.cookieBehavior", 0);
 
   // test that variants of 'baz.com' get normalized appropriately, but that
   // malformed hosts are rejected
-  cm.add("baz.com", "/", "foo", "bar", false, false, true, expiry);
+  cm.add("baz.com", "/", "foo", "bar", false, false, true, expiry, {});
   do_check_eq(cm.countCookiesFromHost("baz.com"), 1);
   do_check_eq(cm.countCookiesFromHost("BAZ.com"), 1);
   do_check_eq(cm.countCookiesFromHost(".baz.com"), 1);
   do_check_eq(cm.countCookiesFromHost("baz.com."), 0);
   do_check_eq(cm.countCookiesFromHost(".baz.com."), 0);
   do_check_throws(function() {
     cm.countCookiesFromHost("baz.com..");
   }, Cr.NS_ERROR_ILLEGAL_VALUE);
@@ -29,50 +29,50 @@ function run_test() {
     cm.countCookiesFromHost("..baz.com");
   }, Cr.NS_ERROR_ILLEGAL_VALUE);
   cm.remove("BAZ.com.", "foo", "/", false, {});
   do_check_eq(cm.countCookiesFromHost("baz.com"), 1);
   cm.remove("baz.com", "foo", "/", false, {});
   do_check_eq(cm.countCookiesFromHost("baz.com"), 0);
 
   // Test that 'baz.com' and 'baz.com.' are treated differently
-  cm.add("baz.com.", "/", "foo", "bar", false, false, true, expiry);
+  cm.add("baz.com.", "/", "foo", "bar", false, false, true, expiry, {});
   do_check_eq(cm.countCookiesFromHost("baz.com"), 0);
   do_check_eq(cm.countCookiesFromHost("BAZ.com"), 0);
   do_check_eq(cm.countCookiesFromHost(".baz.com"), 0);
   do_check_eq(cm.countCookiesFromHost("baz.com."), 1);
   do_check_eq(cm.countCookiesFromHost(".baz.com."), 1);
   cm.remove("baz.com", "foo", "/", false, {});
   do_check_eq(cm.countCookiesFromHost("baz.com."), 1);
   cm.remove("baz.com.", "foo", "/", false, {});
   do_check_eq(cm.countCookiesFromHost("baz.com."), 0);
 
   // test that domain cookies are illegal for IP addresses, aliases such as
   // 'localhost', and eTLD's such as 'co.uk'
-  cm.add("192.168.0.1", "/", "foo", "bar", false, false, true, expiry);
+  cm.add("192.168.0.1", "/", "foo", "bar", false, false, true, expiry, {});
   do_check_eq(cm.countCookiesFromHost("192.168.0.1"), 1);
   do_check_eq(cm.countCookiesFromHost("192.168.0.1."), 0);
   do_check_throws(function() {
     cm.countCookiesFromHost(".192.168.0.1");
   }, Cr.NS_ERROR_ILLEGAL_VALUE);
   do_check_throws(function() {
     cm.countCookiesFromHost(".192.168.0.1.");
   }, Cr.NS_ERROR_ILLEGAL_VALUE);
 
-  cm.add("localhost", "/", "foo", "bar", false, false, true, expiry);
+  cm.add("localhost", "/", "foo", "bar", false, false, true, expiry, {});
   do_check_eq(cm.countCookiesFromHost("localhost"), 1);
   do_check_eq(cm.countCookiesFromHost("localhost."), 0);
   do_check_throws(function() {
     cm.countCookiesFromHost(".localhost");
   }, Cr.NS_ERROR_ILLEGAL_VALUE);
   do_check_throws(function() {
     cm.countCookiesFromHost(".localhost.");
   }, Cr.NS_ERROR_ILLEGAL_VALUE);
 
-  cm.add("co.uk", "/", "foo", "bar", false, false, true, expiry);
+  cm.add("co.uk", "/", "foo", "bar", false, false, true, expiry, {});
   do_check_eq(cm.countCookiesFromHost("co.uk"), 1);
   do_check_eq(cm.countCookiesFromHost("co.uk."), 0);
   do_check_throws(function() {
     cm.countCookiesFromHost(".co.uk");
   }, Cr.NS_ERROR_ILLEGAL_VALUE);
   do_check_throws(function() {
     cm.countCookiesFromHost(".co.uk.");
   }, Cr.NS_ERROR_ILLEGAL_VALUE);
@@ -100,36 +100,36 @@ function run_test() {
   do_check_eq(cm.countCookiesFromHost(""), 0);
   do_check_throws(function() {
     cm.countCookiesFromHost(".");
   }, Cr.NS_ERROR_ILLEGAL_VALUE);
   do_check_throws(function() {
     cm.countCookiesFromHost("..");
   }, Cr.NS_ERROR_ILLEGAL_VALUE);
 
-  var e = cm.getCookiesFromHost("");
+  var e = cm.getCookiesFromHost("", {});
   do_check_false(e.hasMoreElements());
   do_check_throws(function() {
-    cm.getCookiesFromHost(".");
+    cm.getCookiesFromHost(".", {});
   }, Cr.NS_ERROR_ILLEGAL_VALUE);
   do_check_throws(function() {
-    cm.getCookiesFromHost("..");
+    cm.getCookiesFromHost("..", {});
   }, Cr.NS_ERROR_ILLEGAL_VALUE);
 
-  e = cm.getCookiesFromHost("baz.com");
+  e = cm.getCookiesFromHost("baz.com", {});
   do_check_true(e.hasMoreElements());
   do_check_eq(e.getNext().QueryInterface(Ci.nsICookie2).name, "foo");
   do_check_false(e.hasMoreElements());
-  e = cm.getCookiesFromHost("");
+  e = cm.getCookiesFromHost("", {});
   do_check_false(e.hasMoreElements());
   do_check_throws(function() {
-    cm.getCookiesFromHost(".");
+    cm.getCookiesFromHost(".", {});
   }, Cr.NS_ERROR_ILLEGAL_VALUE);
   do_check_throws(function() {
-    cm.getCookiesFromHost("..");
+    cm.getCookiesFromHost("..", {});
   }, Cr.NS_ERROR_ILLEGAL_VALUE);
 
   cm.removeAll();
 
   // test that an empty file:// host works
   emptyuri = NetUtil.newURI("file:///");
   do_check_eq(emptyuri.asciiHost, "");
   do_check_eq(NetUtil.newURI("file://./").asciiHost, "");
@@ -146,36 +146,36 @@ function run_test() {
   do_check_eq(cs.getCookieString(emptyuri, null), "foo2=bar; foo3=bar");
 
   do_check_eq(cm.countCookiesFromHost("baz.com"), 0);
   do_check_eq(cm.countCookiesFromHost(""), 2);
   do_check_throws(function() {
     cm.countCookiesFromHost(".");
   }, Cr.NS_ERROR_ILLEGAL_VALUE);
 
-  e = cm.getCookiesFromHost("baz.com");
+  e = cm.getCookiesFromHost("baz.com", {});
   do_check_false(e.hasMoreElements());
-  e = cm.getCookiesFromHost("");
+  e = cm.getCookiesFromHost("", {});
   do_check_true(e.hasMoreElements());
   e.getNext();
   do_check_true(e.hasMoreElements());
   e.getNext();
   do_check_false(e.hasMoreElements());
   do_check_throws(function() {
-    cm.getCookiesFromHost(".");
+    cm.getCookiesFromHost(".", {});
   }, Cr.NS_ERROR_ILLEGAL_VALUE);
 
   cm.removeAll();
 
   // test that an empty host to add() or remove() works,
   // but a host of '.' doesn't
-  cm.add("", "/", "foo2", "bar", false, false, true, expiry);
+  cm.add("", "/", "foo2", "bar", false, false, true, expiry, {});
   do_check_eq(getCookieCount(), 1);
   do_check_throws(function() {
-    cm.add(".", "/", "foo3", "bar", false, false, true, expiry);
+    cm.add(".", "/", "foo3", "bar", false, false, true, expiry, {});
   }, Cr.NS_ERROR_ILLEGAL_VALUE);
   do_check_eq(getCookieCount(), 1);
 
   cm.remove("", "foo2", "/", false, {});
   do_check_eq(getCookieCount(), 0);
   do_check_throws(function() {
     cm.remove(".", "foo3", "/", false, {});
   }, Cr.NS_ERROR_ILLEGAL_VALUE);
@@ -211,23 +211,23 @@ function getCookieCount() {
 function testDomainCookie(uriString, domain) {
   var cs = Cc["@mozilla.org/cookieService;1"].getService(Ci.nsICookieService);
   var cm = Cc["@mozilla.org/cookiemanager;1"].getService(Ci.nsICookieManager2);
 
   cm.removeAll();
 
   var uri = NetUtil.newURI(uriString);
   cs.setCookieString(uri, null, "foo=bar; domain=" + domain, null);
-  var e = cm.getCookiesFromHost(domain);
+  var e = cm.getCookiesFromHost(domain, {});
   do_check_true(e.hasMoreElements());
   do_check_eq(e.getNext().QueryInterface(Ci.nsICookie2).host, domain);
   cm.removeAll();
 
   cs.setCookieString(uri, null, "foo=bar; domain=." + domain, null);
-  e = cm.getCookiesFromHost(domain);
+  e = cm.getCookiesFromHost(domain, {});
   do_check_true(e.hasMoreElements());
   do_check_eq(e.getNext().QueryInterface(Ci.nsICookie2).host, domain);
   cm.removeAll();
 }
 
 function testTrailingDotCookie(uriString, domain) {
   var cs = Cc["@mozilla.org/cookieService;1"].getService(Ci.nsICookieService);
   var cm = Cc["@mozilla.org/cookiemanager;1"].getService(Ci.nsICookieManager2);
--- a/extensions/cookie/test/unit/test_bug650522.js
+++ b/extensions/cookie/test/unit/test_bug650522.js
@@ -5,12 +5,12 @@ Components.utils.import("resource://gre/
 
 function run_test() {
   var cs = Cc["@mozilla.org/cookieService;1"].getService(Ci.nsICookieService);
   var cm = Cc["@mozilla.org/cookiemanager;1"].getService(Ci.nsICookieManager2);
   var expiry = (Date.now() + 1000) * 1000;
 
   // Test our handling of host names with a single character at the beginning
   // followed by a dot.
-  cm.add("e.mail.com", "/", "foo", "bar", false, false, true, expiry);
+  cm.add("e.mail.com", "/", "foo", "bar", false, false, true, expiry, {});
   do_check_eq(cm.countCookiesFromHost("e.mail.com"), 1);
   do_check_eq(cs.getCookieString(NetUtil.newURI("http://e.mail.com"), null), "foo=bar");
 }
--- a/extensions/cookie/test/unit/test_bug667087.js
+++ b/extensions/cookie/test/unit/test_bug667087.js
@@ -5,12 +5,12 @@ Components.utils.import("resource://gre/
 
 function run_test() {
   var cs = Cc["@mozilla.org/cookieService;1"].getService(Ci.nsICookieService);
   var cm = Cc["@mozilla.org/cookiemanager;1"].getService(Ci.nsICookieManager2);
   var expiry = (Date.now() + 1000) * 1000;
 
   // Test our handling of host names with a single character consisting only
   // of a single character
-  cm.add("a", "/", "foo", "bar", false, false, true, expiry);
+  cm.add("a", "/", "foo", "bar", false, false, true, expiry, {});
   do_check_eq(cm.countCookiesFromHost("a"), 1);
   do_check_eq(cs.getCookieString(NetUtil.newURI("http://a"), null), "foo=bar");
 }
--- a/extensions/cookie/test/unit/test_cookies_async_failure.js
+++ b/extensions/cookie/test/unit/test_cookies_async_failure.js
@@ -153,17 +153,17 @@ function run_test_1(generator)
   do_check_eq(do_count_cookies_in_db(db.db), 1);
 
   // Insert a row.
   db.insertCookie(cookie);
   db.close();
 
   // Attempt to insert a cookie with the same (name, host, path) triplet.
   Services.cookiemgr.add(cookie.host, cookie.path, cookie.name, "hallo",
-    cookie.isSecure, cookie.isHttpOnly, cookie.isSession, cookie.expiry);
+    cookie.isSecure, cookie.isHttpOnly, cookie.isSession, cookie.expiry, {});
 
   // Check that the cookie service accepted the new cookie.
   do_check_eq(Services.cookiemgr.countCookiesFromHost(cookie.host), 1);
 
   // Wait for the cookie service to rename the old database and rebuild.
   new _observer(sub_generator, "cookie-db-rebuilding");
   yield;
   do_execute_soon(function() { do_run_generator(sub_generator); });
@@ -184,17 +184,17 @@ function run_test_1(generator)
   let backupdb = Services.storage.openDatabase(do_get_backup_file(profile));
   do_check_eq(do_count_cookies_in_db(backupdb, "foo.com"), 1);
   backupdb.close();
 
   // Load the profile, and check that it contains the new cookie.
   do_load_profile();
 
   do_check_eq(Services.cookiemgr.countCookiesFromHost("foo.com"), 1);
-  let enumerator = Services.cookiemgr.getCookiesFromHost(cookie.host);
+  let enumerator = Services.cookiemgr.getCookiesFromHost(cookie.host, {});
   do_check_true(enumerator.hasMoreElements());
   let dbcookie = enumerator.getNext().QueryInterface(Ci.nsICookie2);
   do_check_eq(dbcookie.value, "hallo");
   do_check_false(enumerator.hasMoreElements());
 
   // Close the profile.
   do_close_profile(sub_generator);
   yield;
--- a/extensions/cookie/test/unit/test_cookies_profile_close.js
+++ b/extensions/cookie/test/unit/test_cookies_profile_close.js
@@ -49,17 +49,17 @@ function do_run_test() {
     Services.cookiemgr.removeAll();
   }, Cr.NS_ERROR_NOT_AVAILABLE);
 
   do_check_throws(function() {
     Services.cookiemgr.enumerator;
   }, Cr.NS_ERROR_NOT_AVAILABLE);
 
   do_check_throws(function() {
-    Services.cookiemgr.add("foo.com", "", "oh4", "hai", false, false, false, 0);
+    Services.cookiemgr.add("foo.com", "", "oh4", "hai", false, false, false, 0, {});
   }, Cr.NS_ERROR_NOT_AVAILABLE);
 
   do_check_throws(function() {
     Services.cookiemgr.remove("foo.com", "", "oh4", false, {});
   }, Cr.NS_ERROR_NOT_AVAILABLE);
 
   do_check_throws(function() {
     let file = profile.clone();
@@ -71,17 +71,17 @@ function do_run_test() {
     Services.cookiemgr.cookieExists(cookie);
   }, Cr.NS_ERROR_NOT_AVAILABLE);
 
   do_check_throws(function() {
     Services.cookies.countCookiesFromHost("foo.com");
   }, Cr.NS_ERROR_NOT_AVAILABLE);
 
   do_check_throws(function() {
-    Services.cookies.getCookiesFromHost("foo.com");
+    Services.cookies.getCookiesFromHost("foo.com", {});
   }, Cr.NS_ERROR_NOT_AVAILABLE);
 
   // Wait for the database to finish closing.
   new _observer(test_generator, "cookie-db-closed");
   yield;
 
   // Load the profile and check that the API is available.
   do_load_profile();
--- a/extensions/cookie/test/unit/test_domain_eviction.js
+++ b/extensions/cookie/test/unit/test_domain_eviction.js
@@ -63,41 +63,41 @@ function do_run_test()
     if (cookie.host == "horse.radish")
       do_throw("cookies not evicted by lastAccessed order");
   }
 
   // Test that expired cookies for a domain are evicted before live ones.
   let shortExpiry = Math.floor(Date.now() / 1000 + 2);
   setCookies("captchart.com", 49, futureExpiry);
   Services.cookiemgr.add("captchart.com", "", "test100", "eviction",
-    false, false, false, shortExpiry);
+    false, false, false, shortExpiry, {});
   do_timeout(2100, continue_test);
   yield;
 
   do_check_eq(countCookies("captchart.com", "captchart.com"), 50);
   Services.cookiemgr.add("captchart.com", "", "test200", "eviction",
-    false, false, false, futureExpiry);
+    false, false, false, futureExpiry, {});
   do_check_eq(countCookies("captchart.com", "captchart.com"), 50);
 
-  enumerator = Services.cookiemgr.getCookiesFromHost("captchart.com");
+  enumerator = Services.cookiemgr.getCookiesFromHost("captchart.com", {});
   while (enumerator.hasMoreElements()) {
     let cookie = enumerator.getNext().QueryInterface(Ci.nsICookie2);
     do_check_true(cookie.expiry == futureExpiry);
   }
 
   do_finish_generator_test(test_generator);
 }
 
 // set 'aNumber' cookies with host 'aHost', with distinct names.
 function
 setCookies(aHost, aNumber, aExpiry)
 {
   for (let i = 0; i < aNumber; ++i)
     Services.cookiemgr.add(aHost, "", "test" + i, "eviction",
-      false, false, false, aExpiry);
+      false, false, false, aExpiry, {});
 }
 
 // count how many cookies are within domain 'aBaseDomain', using three
 // independent interface methods on nsICookieManager2:
 // 1) 'enumerator', an enumerator of all cookies;
 // 2) 'countCookiesFromHost', which returns the number of cookies within the
 //    base domain of 'aHost',
 // 3) 'getCookiesFromHost', which returns an enumerator of 2).
@@ -118,17 +118,17 @@ countCookies(aBaseDomain, aHost)
   }
 
   // confirm the count using countCookiesFromHost and getCookiesFromHost.
   let result = cookies.length;
   do_check_eq(Services.cookiemgr.countCookiesFromHost(aBaseDomain),
     cookies.length);
   do_check_eq(Services.cookiemgr.countCookiesFromHost(aHost), cookies.length);
 
-  enumerator = Services.cookiemgr.getCookiesFromHost(aHost);
+  enumerator = Services.cookiemgr.getCookiesFromHost(aHost, {});
   while (enumerator.hasMoreElements()) {
     let cookie = enumerator.getNext().QueryInterface(Ci.nsICookie2);
 
     if (cookie.host.length >= aBaseDomain.length &&
         cookie.host.slice(cookie.host.length - aBaseDomain.length) == aBaseDomain) {
       let found = false;
       for (let i = 0; i < cookies.length; ++i) {
         if (cookies[i].host == cookie.host && cookies[i].name == cookie.name) {
--- a/extensions/cookie/test/unit/test_eviction.js
+++ b/extensions/cookie/test/unit/test_eviction.js
@@ -194,17 +194,17 @@ function do_run_test()
 function set_cookies(begin, end, expiry)
 {
   do_check_true(begin != end);
 
   let beginTime;
   for (let i = begin; i < end; ++i) {
     let host = "eviction." + i + ".tests";
     Services.cookiemgr.add(host, "", "test", "eviction", false, false, false,
-      expiry);
+      expiry, {});
 
     if (i == begin)
       beginTime = get_creationTime(i);
   }
 
   let endTime = get_creationTime(end - 1);
   do_check_true(begin == end - 1 || endTime > beginTime);
   if (endTime - beginTime > gPurgeAge * 1000000) {
@@ -214,17 +214,17 @@ function set_cookies(begin, end, expiry)
   }
 
   return true;
 }
 
 function get_creationTime(i)
 {
   let host = "eviction." + i + ".tests";
-  let enumerator = Services.cookiemgr.getCookiesFromHost(host);
+  let enumerator = Services.cookiemgr.getCookiesFromHost(host, {});
   do_check_true(enumerator.hasMoreElements());
   let cookie = enumerator.getNext().QueryInterface(Ci.nsICookie2);
   return cookie.creationTime;
 }
 
 // Test that 'aNumberToExpect' cookies remain after purging is complete, and
 // that the cookies that remain consist of the set expected given the number of
 // of older and newer cookies -- eviction should occur by order of lastAccessed
--- a/extensions/cookie/test/unit/test_schema_2_migration.js
+++ b/extensions/cookie/test/unit/test_schema_2_migration.js
@@ -83,17 +83,17 @@ function do_run_test() {
   do_check_eq(Services.cookiemgr.countCookiesFromHost("foo.com"), 20);
 
   // 2) All expired, unique cookies exist.
   do_check_eq(Services.cookiemgr.countCookiesFromHost("bar.com"), 20);
 
   // 3) Only one cookie remains, and it's the one with the highest expiration
   // time.
   do_check_eq(Services.cookiemgr.countCookiesFromHost("baz.com"), 1);
-  let enumerator = Services.cookiemgr.getCookiesFromHost("baz.com");
+  let enumerator = Services.cookiemgr.getCookiesFromHost("baz.com", {});
   let cookie = enumerator.getNext().QueryInterface(Ci.nsICookie2);
   do_check_eq(cookie.expiry, futureExpiry + 44);
 
   do_close_profile(test_generator);
   yield;
 
   // Open the database so we can execute some more schema 2 statements on it.
   schema2db = new CookieDatabaseConnection(do_get_cookie_file(profile), 2);
--- a/extensions/cookie/test/unit/test_schema_3_migration.js
+++ b/extensions/cookie/test/unit/test_schema_3_migration.js
@@ -83,17 +83,17 @@ function do_run_test() {
   do_check_eq(Services.cookiemgr.countCookiesFromHost("foo.com"), 20);
 
   // 2) All expired, unique cookies exist.
   do_check_eq(Services.cookiemgr.countCookiesFromHost("bar.com"), 20);
 
   // 3) Only one cookie remains, and it's the one with the highest expiration
   // time.
   do_check_eq(Services.cookiemgr.countCookiesFromHost("baz.com"), 1);
-  let enumerator = Services.cookiemgr.getCookiesFromHost("baz.com");
+  let enumerator = Services.cookiemgr.getCookiesFromHost("baz.com", {});
   let cookie = enumerator.getNext().QueryInterface(Ci.nsICookie2);
   do_check_eq(cookie.expiry, futureExpiry + 44);
 
   do_close_profile(test_generator);
   yield;
 
   // Open the database so we can execute some more schema 3 statements on it.
   schema3db = new CookieDatabaseConnection(do_get_cookie_file(profile), 3);
@@ -111,15 +111,15 @@ function do_run_test() {
   schema3db = null;
 
   // Load the database. The cookies added immediately prior will have a NULL
   // creationTime column.
   do_load_profile();
 
   // Test the expected set of cookies.
   do_check_eq(Services.cookiemgr.countCookiesFromHost("cat.com"), 20);
-  enumerator = Services.cookiemgr.getCookiesFromHost("cat.com");
+  enumerator = Services.cookiemgr.getCookiesFromHost("cat.com", {});
   cookie = enumerator.getNext().QueryInterface(Ci.nsICookie2);
   do_check_eq(cookie.creationTime, 0);
 
   finish_test();
 }
 
--- a/image/DownscalingFilter.h
+++ b/image/DownscalingFilter.h
@@ -67,27 +67,27 @@ struct DownscalingConfig
  * manually constructs an instance and attempts to actually use it. Callers
  * should avoid this by ensuring that they do not request downscaling in
  * non-Skia builds.
  */
 template <typename Next>
 class DownscalingFilter final : public SurfaceFilter
 {
 public:
-  uint8_t* AdvanceRow() override { MOZ_CRASH(); return nullptr; }
   Maybe<SurfaceInvalidRect> TakeInvalidRect() override { return Nothing(); }
 
   template <typename... Rest>
   nsresult Configure(const DownscalingConfig& aConfig, Rest... aRest)
   {
     return NS_ERROR_FAILURE;
   }
 
 protected:
   uint8_t* DoResetToFirstRow() override { MOZ_CRASH(); return nullptr; }
+  uint8_t* DoAdvanceRow() override { MOZ_CRASH(); return nullptr; }
 };
 
 #else
 
 /**
  * DownscalingFilter performs Lanczos downscaling, taking image input data at one size
  * and outputting it rescaled to a different size.
  *
@@ -210,17 +210,29 @@ public:
     if (invalidRect) {
       // Compute the input space invalid rect by scaling.
       invalidRect->mInputSpaceRect.ScaleRoundOut(mScale.width, mScale.height);
     }
 
     return invalidRect;
   }
 
-  uint8_t* AdvanceRow() override
+protected:
+  uint8_t* DoResetToFirstRow() override
+  {
+    mNext.ResetToFirstRow();
+
+    mInputRow = 0;
+    mOutputRow = 0;
+    mRowsInWindow = 0;
+
+    return GetRowPointer();
+  }
+
+  uint8_t* DoAdvanceRow() override
   {
     if (mInputRow >= mInputSize.height) {
       NS_WARNING("Advancing DownscalingFilter past the end of the input");
       return nullptr;
     }
 
     if (mOutputRow >= mNext.InputSize().height) {
       NS_WARNING("Advancing DownscalingFilter past the end of the output");
@@ -255,28 +267,16 @@ public:
     }
 
     mInputRow++;
 
     return mInputRow < mInputSize.height ? GetRowPointer()
                                          : nullptr;
   }
 
-protected:
-  uint8_t* DoResetToFirstRow() override
-  {
-    mNext.ResetToFirstRow();
-
-    mInputRow = 0;
-    mOutputRow = 0;
-    mRowsInWindow = 0;
-
-    return GetRowPointer();
-  }
-
 private:
   uint8_t* GetRowPointer() const { return mRowBuffer.get(); }
 
   static uint32_t PaddedWidthInBytes(uint32_t aLogicalWidth)
   {
     // Convert from width in BGRA/BGRX pixels to width in bytes, padding by 15
     // to handle overreads by the SIMD code inside Skia.
     return aLogicalWidth * sizeof(uint32_t) + 15;
--- a/image/SurfaceFilters.h
+++ b/image/SurfaceFilters.h
@@ -114,17 +114,27 @@ public:
     return sizeof(PixelType) == 1 && mNext.IsValidPalettedPipe();
   }
 
   Maybe<SurfaceInvalidRect> TakeInvalidRect() override
   {
     return mNext.TakeInvalidRect();
   }
 
-  uint8_t* AdvanceRow() override
+protected:
+  uint8_t* DoResetToFirstRow() override
+  {
+    mNext.ResetToFirstRow();
+    mPass = 0;
+    mInputRow = 0;
+    mOutputRow = InterlaceOffset(mPass);
+    return GetRowPointer(mOutputRow);
+  }
+
+  uint8_t* DoAdvanceRow() override
   {
     if (mPass >= 4) {
       return nullptr;  // We already finished all passes.
     }
     if (mInputRow >= InputSize().height) {
       return nullptr;  // We already got all the input rows we expect.
     }
 
@@ -142,19 +152,16 @@ public:
 
     // Determine which output row the next input row corresponds to.
     bool advancedPass = false;
     uint32_t stride = InterlaceStride(mPass);
     int32_t nextOutputRow = mOutputRow + stride;
     while (nextOutputRow >= InputSize().height) {
       // Copy any remaining rows from the buffer.
       if (!advancedPass) {
-        DuplicateRows(HaeberliOutputUntilRow(mPass, mProgressiveDisplay,
-                                             InputSize(), mOutputRow),
-                      InputSize().height);
         OutputRows(HaeberliOutputUntilRow(mPass, mProgressiveDisplay,
                                           InputSize(), mOutputRow),
                    InputSize().height);
       }
 
       // We finished the current pass; advance to the next one.
       mPass++;
       if (mPass >= 4) {
@@ -206,26 +213,16 @@ public:
     mOutputRow = nextOutputRow;
 
     // We'll actually write to the first Haeberli output row, then copy it until
     // we reach the last Haeberli output row. The assertions above make sure
     // this always includes mOutputRow.
     return GetRowPointer(nextHaeberliOutputRow);
   }
 
-protected:
-  uint8_t* DoResetToFirstRow() override
-  {
-    mNext.ResetToFirstRow();
-    mPass = 0;
-    mInputRow = 0;
-    mOutputRow = InterlaceOffset(mPass);;
-    return GetRowPointer(mOutputRow);
-  }
-
 private:
   static uint32_t InterlaceOffset(uint32_t aPass)
   {
     MOZ_ASSERT(aPass < 4, "Invalid pass");
     static const uint8_t offset[] = { 0, 4, 2, 1 };
     return offset[aPass];
   }
 
@@ -408,17 +405,63 @@ public:
     return NS_OK;
   }
 
   Maybe<SurfaceInvalidRect> TakeInvalidRect() override
   {
     return mNext.TakeInvalidRect();
   }
 
-  uint8_t* AdvanceRow() override
+protected:
+  uint8_t* DoResetToFirstRow() override
+  {
+    uint8_t* rowPtr = mNext.ResetToFirstRow();
+    if (rowPtr == nullptr) {
+      mRow = mFrameRect.YMost();
+      return nullptr;
+    }
+
+    mRow = mUnclampedFrameRect.y;
+
+    // Advance the next pipeline stage to the beginning of the frame rect,
+    // outputting blank rows.
+    if (mFrameRect.y > 0) {
+      int32_t rowsToWrite = mFrameRect.y;
+      mNext.template WriteRows<uint32_t>([&](uint32_t* aRow, uint32_t aLength)
+                                           -> Maybe<WriteState> {
+        memset(aRow, 0, aLength * sizeof(uint32_t));
+        rowsToWrite--;
+        return rowsToWrite > 0 ? Nothing()
+                               : Some(WriteState::NEED_MORE_DATA);
+      });
+    }
+
+    // We're at the beginning of the frame rect now, so return if we're either
+    // ready for input or we're already done.
+    rowPtr = mBuffer ? mBuffer.get() : mNext.CurrentRowPointer();
+    if (!mFrameRect.IsEmpty() || rowPtr == nullptr) {
+      // Note that the pointer we're returning is for the next row we're
+      // actually going to write to, but we may discard writes before that point
+      // if mRow < mFrameRect.y.
+      return AdjustRowPointer(rowPtr);
+    }
+
+    // We've finished the region specified by the frame rect, but the frame rect
+    // is empty, so we need to output the rest of the image immediately. Advance
+    // to the end of the next pipeline stage's buffer, outputting blank rows.
+    mNext.template WriteRows<uint32_t>([](uint32_t* aRow, uint32_t aLength) {
+      memset(aRow, 0, aLength * sizeof(uint32_t));
+      return Nothing();
+    });
+
+    mRow = mFrameRect.YMost();
+    return nullptr;  // We're done.
+  }
+
+  uint8_t* DoAdvanceRow() override
   {
     uint8_t* rowPtr = nullptr;
 
     const int32_t currentRow = mRow;
     mRow++;
 
     if (currentRow < mFrameRect.y) {
       // This row is outside of the frame rect, so just drop it on the floor.
@@ -464,69 +507,22 @@ public:
 
     // If there's still more data coming, just adjust the pointer and return.
     if (mRow < mFrameRect.YMost() || rowPtr == nullptr) {
       return AdjustRowPointer(rowPtr);
     }
 
     // We've finished the region specified by the frame rect. Advance to the end
     // of the next pipeline stage's buffer, outputting blank rows.
-    mNext.template WriteRows<uint32_t>([&](uint32_t* aRow, uint32_t aLength) {
-      memset(rowPtr, 0, aLength * sizeof(uint32_t));
-      return Nothing();
-    });
-
-    return nullptr;  // We're done.
-  }
-
-protected:
-  uint8_t* DoResetToFirstRow() override
-  {
-    uint8_t* rowPtr = mNext.ResetToFirstRow();
-    if (rowPtr == nullptr) {
-      mRow = InputSize().height;
-      return nullptr;
-    }
-
-    mRow = mUnclampedFrameRect.y;
-
-    // Advance the next pipeline stage to the beginning of the frame rect,
-    // outputting blank rows.
-    if (mFrameRect.y > 0) {
-      int32_t rowsToWrite = mFrameRect.y;
-      mNext.template WriteRows<uint32_t>([&](uint32_t* aRow, uint32_t aLength)
-                                           -> Maybe<WriteState> {
-        memset(aRow, 0, aLength * sizeof(uint32_t));
-        rowsToWrite--;
-        return rowsToWrite > 0 ? Nothing()
-                               : Some(WriteState::NEED_MORE_DATA);
-      });
-    }
-
-    // We're at the beginning of the frame rect now, so return if we're either
-    // ready for input or we're already done.
-    rowPtr = mBuffer ? mBuffer.get() : mNext.CurrentRowPointer();
-    if (!mFrameRect.IsEmpty() || rowPtr == nullptr) {
-      // Note that the pointer we're returning is for the next row we're
-      // actually going to write to, but we may discard writes before that point
-      // if mRow < mFrameRect.y.
-      return AdjustRowPointer(rowPtr);
-    }
-
-    // We've finished the region specified by the frame rect, but the frame rect
-    // is empty, so we need to output the rest of the image immediately. Advance
-    // to the end of the next pipeline stage's buffer, outputting blank rows.
-    int32_t rowsWritten = 0;
-    mNext.template WriteRows<uint32_t>([&](uint32_t* aRow, uint32_t aLength) {
-      rowsWritten++;
+    mNext.template WriteRows<uint32_t>([](uint32_t* aRow, uint32_t aLength) {
       memset(aRow, 0, aLength * sizeof(uint32_t));
       return Nothing();
     });
 
-    mRow = InputSize().height;
+    mRow = mFrameRect.YMost();
     return nullptr;  // We're done.
   }
 
 private:
   uint8_t* AdjustRowPointer(uint8_t* aNextRowPointer) const
   {
     if (mBuffer) {
       MOZ_ASSERT(aNextRowPointer == mBuffer.get());
--- a/image/SurfacePipe.cpp
+++ b/image/SurfacePipe.cpp
@@ -65,17 +65,17 @@ AbstractSurfaceSink::TakeInvalidRect()
 uint8_t*
 AbstractSurfaceSink::DoResetToFirstRow()
 {
   mRow = 0;
   return GetRowPointer();
 }
 
 uint8_t*
-AbstractSurfaceSink::AdvanceRow()
+AbstractSurfaceSink::DoAdvanceRow()
 {
   if (mRow >= uint32_t(InputSize().height)) {
     return nullptr;
   }
 
   // If we're vertically flipping the output, we need to flip the invalid rect. Since we're
   // dealing with an axis-aligned rect, only the y coordinate needs to change.
   int32_t invalidY = mFlipVertically
--- a/image/SurfacePipe.h
+++ b/image/SurfacePipe.h
@@ -114,16 +114,30 @@ public:
    */
   uint8_t* ResetToFirstRow()
   {
     mCol = 0;
     mRowPointer = DoResetToFirstRow();
     return mRowPointer;
   }
 
+  /**
+   * Called by WritePixels() and WriteRows() to advance this filter to the next
+   * row.
+   *
+   * @return a pointer to the buffer for the next row, or nullptr to indicate
+   *         that we've finished the entire surface.
+   */
+  uint8_t* AdvanceRow()
+  {
+    mCol = 0;
+    mRowPointer = DoAdvanceRow();
+    return mRowPointer;
+  }
+
   /// @return a pointer to the buffer for the current row.
   uint8_t* CurrentRowPointer() const { return mRowPointer; }
 
   /// @return true if we've finished writing to the surface.
   bool IsSurfaceFinished() const { return mRowPointer == nullptr; }
 
   /// @return the input size this filter expects.
   gfx::IntSize InputSize() const { return mInputSize; }
@@ -186,19 +200,17 @@ public:
             // Note that we don't need to record this anywhere, because this
             // indicates an error in aFunc, and there's nothing wrong with our
             // machinery. The caller can recover as needed and continue writing to
             // the row.
             return WriteState::FAILURE;
         }
       }
 
-      // We've finished the row.
-      mRowPointer = AdvanceRow();
-      mCol = 0;
+      AdvanceRow();  // We've finished the row.
     }
 
     // We've finished the entire surface.
     return WriteState::FINISHED;
   }
 
   /**
    * Write rows to the surface one at a time by repeatedly calling a lambda
@@ -239,18 +251,17 @@ public:
       return WriteState::FINISHED;  // Already done.
     }
 
     while (true) {
       PixelType* rowPtr = reinterpret_cast<PixelType*>(mRowPointer);
 
       Maybe<WriteState> result = aFunc(rowPtr, mInputSize.width);
       if (result != Some(WriteState::FAILURE)) {
-        mCol = 0;
-        mRowPointer = AdvanceRow();  // We've finished the row.
+        AdvanceRow();  // We've finished the row.
       }
 
       if (IsSurfaceFinished()) {
         break;
       }
 
       if (result == Some(WriteState::FINISHED)) {
         // Make sure that IsSurfaceFinished() returns true so the caller can't
@@ -271,41 +282,40 @@ public:
   //////////////////////////////////////////////////////////////////////////////
   // Methods Subclasses Should Override
   //////////////////////////////////////////////////////////////////////////////
 
   /// @return true if this SurfaceFilter can be used with paletted surfaces.
   virtual bool IsValidPalettedPipe() const { return false; }
 
   /**
-   * Called by WritePixels() and WriteRows() to advance this filter to the next
-   * row.
-   *
-   * @return a pointer to the buffer for the next row, or nullptr to indicate
-   *         that we've finished the entire surface.
-   */
-  virtual uint8_t* AdvanceRow() = 0;
-
-  /**
    * @return a SurfaceInvalidRect representing the region of the surface that
    *         has been written to since the last time TakeInvalidRect() was
    *         called, or Nothing() if the region is empty (i.e. nothing has been
    *         written).
    */
   virtual Maybe<SurfaceInvalidRect> TakeInvalidRect() = 0;
 
 protected:
 
   /**
    * Called by ResetToFirstRow() to actually perform the reset. It's legal to
    * throw away any previously written data at this point, as all rows must be
    * written to on every pass.
    */
   virtual uint8_t* DoResetToFirstRow() = 0;
 
+  /**
+   * Called by AdvanceRow() to actually advance this filter to the next row.
+   *
+   * @return a pointer to the buffer for the next row, or nullptr to indicate
+   *         that we've finished the entire surface.
+   */
+  virtual uint8_t* DoAdvanceRow() = 0;
+
 
   //////////////////////////////////////////////////////////////////////////////
   // Methods For Internal Use By Subclasses
   //////////////////////////////////////////////////////////////////////////////
 
   /**
    * Called by subclasses' Configure() methods to initialize the configuration
    * of this filter. After the filter is configured, calls ResetToFirstRow().
@@ -355,20 +365,20 @@ public:
   /// Returns the singleton instance of NullSurfaceSink.
   static NullSurfaceSink* Singleton();
 
   virtual ~NullSurfaceSink() { }
 
   nsresult Configure(const NullSurfaceConfig& aConfig);
 
   Maybe<SurfaceInvalidRect> TakeInvalidRect() override { return Nothing(); }
-  uint8_t* AdvanceRow() override { return nullptr; }
 
 protected:
   uint8_t* DoResetToFirstRow() override { return nullptr; }
+  uint8_t* DoAdvanceRow() override { return nullptr; }
 
 private:
   static UniquePtr<NullSurfaceSink> sSingleton;  /// The singleton instance.
 };
 
 
 /**
  * SurfacePipe is the public API that decoders should use to interact with a
@@ -473,20 +483,20 @@ public:
   AbstractSurfaceSink()
     : mImageData(nullptr)
     , mImageDataLength(0)
     , mRow(0)
     , mFlipVertically(false)
   { }
 
   Maybe<SurfaceInvalidRect> TakeInvalidRect() override final;
-  uint8_t* AdvanceRow() override final;
 
 protected:
   uint8_t* DoResetToFirstRow() override final;
+  uint8_t* DoAdvanceRow() override final;
   virtual uint8_t* GetRowPointer() const = 0;
 
   gfx::IntRect mInvalidRect;  /// The region of the surface that has been written
                               /// to since the last call to TakeInvalidRect().
   uint8_t*  mImageData;       /// A pointer to the beginning of the surface data.
   uint32_t  mImageDataLength; /// The length of the surface data.
   uint32_t  mRow;             /// The row to which we're writing. (0-indexed)
   bool      mFlipVertically;  /// If true, write the rows from top to bottom.
--- a/image/SurfacePipeFactory.h
+++ b/image/SurfacePipeFactory.h
@@ -110,44 +110,40 @@ public:
     SurfaceConfig surfaceConfig { aDecoder, aFrameNum, aOutputSize,
                                   aFormat, flipVertically };
 
     Maybe<SurfacePipe> pipe;
 
     if (downscale) {
       if (removeFrameRect) {
         if (deinterlace) {
-          pipe = MakePipe(aFrameRect.Size(), deinterlacingConfig,
-                          removeFrameRectConfig, downscalingConfig,
-                          surfaceConfig);
+          pipe = MakePipe(deinterlacingConfig, removeFrameRectConfig,
+                          downscalingConfig, surfaceConfig);
         } else {  // (deinterlace is false)
-          pipe = MakePipe(aFrameRect.Size(), removeFrameRectConfig,
-                          downscalingConfig, surfaceConfig);
+          pipe = MakePipe(removeFrameRectConfig, downscalingConfig, surfaceConfig);
         }
       } else {  // (removeFrameRect is false)
         if (deinterlace) {
-          pipe = MakePipe(aInputSize, deinterlacingConfig,
-                          downscalingConfig, surfaceConfig);
+          pipe = MakePipe(deinterlacingConfig, downscalingConfig, surfaceConfig);
         } else {  // (deinterlace is false)
-          pipe = MakePipe(aInputSize, downscalingConfig, surfaceConfig);
+          pipe = MakePipe(downscalingConfig, surfaceConfig);
         }
       }
     } else {  // (downscale is false)
       if (removeFrameRect) {
         if (deinterlace) {
-          pipe = MakePipe(aFrameRect.Size(), deinterlacingConfig,
-                          removeFrameRectConfig, surfaceConfig);
+          pipe = MakePipe(deinterlacingConfig, removeFrameRectConfig, surfaceConfig);
         } else {  // (deinterlace is false)
-          pipe = MakePipe(aFrameRect.Size(), removeFrameRectConfig, surfaceConfig);
+          pipe = MakePipe(removeFrameRectConfig, surfaceConfig);
         }
       } else {  // (removeFrameRect is false)
         if (deinterlace) {
-          pipe = MakePipe(aInputSize, deinterlacingConfig, surfaceConfig);
+          pipe = MakePipe(deinterlacingConfig, surfaceConfig);
         } else {  // (deinterlace is false)
-          pipe = MakePipe(aInputSize, surfaceConfig);
+          pipe = MakePipe(surfaceConfig);
         }
       }
     }
 
     return pipe;
   }
 
   /**
@@ -190,29 +186,28 @@ public:
     DeinterlacingConfig<uint8_t> deinterlacingConfig { progressiveDisplay };
     PalettedSurfaceConfig palettedSurfaceConfig { aDecoder, aFrameNum, aInputSize,
                                                   aFrameRect, aFormat, aPaletteDepth,
                                                   flipVertically };
 
     Maybe<SurfacePipe> pipe;
 
     if (deinterlace) {
-      pipe = MakePipe(aFrameRect.Size(), deinterlacingConfig,
-                      palettedSurfaceConfig);
+      pipe = MakePipe(deinterlacingConfig, palettedSurfaceConfig);
     } else {
-      pipe = MakePipe(aFrameRect.Size(), palettedSurfaceConfig);
+      pipe = MakePipe(palettedSurfaceConfig);
     }
 
     return pipe;
   }
 
 private:
   template <typename... Configs>
   static Maybe<SurfacePipe>
-  MakePipe(const nsIntSize& aInputSize, Configs... aConfigs)
+  MakePipe(Configs... aConfigs)
   {
     auto pipe = MakeUnique<typename detail::FilterPipeline<Configs...>::Type>();
     nsresult rv = pipe->Configure(aConfigs...);
     if (NS_FAILED(rv)) {
       return Nothing();
     }
 
     return Some(SurfacePipe { Move(pipe) } );
--- a/image/decoders/nsIconDecoder.cpp
+++ b/image/decoders/nsIconDecoder.cpp
@@ -71,21 +71,21 @@ nsIconDecoder::ReadHeader(const char* aD
 
   // If we're doing a metadata decode, we're done.
   if (IsMetadataDecode()) {
     return Transition::TerminateSuccess();
   }
 
   MOZ_ASSERT(!mImageData, "Already have a buffer allocated?");
   IntSize targetSize = mDownscaler ? mDownscaler->TargetSize() : GetSize();
-  IntRect targetFrameRect(IntPoint(0, 0), targetSize);
+  IntRect frameRect(IntPoint(0, 0), GetSize());
 
   Maybe<SurfacePipe> pipe =
     SurfacePipeFactory::CreateSurfacePipe(this, 0, GetSize(), targetSize,
-                                          targetFrameRect, SurfaceFormat::B8G8R8A8,
+                                          frameRect, SurfaceFormat::B8G8R8A8,
                                           SurfacePipeFlags());
   if (!pipe) {
     return Transition::TerminateFailure();
   }
 
   mPipe = Move(*pipe);
 
   MOZ_ASSERT(mImageData, "Should have a buffer now");
--- a/image/test/gtest/TestDeinterlacingFilter.cpp
+++ b/image/test/gtest/TestDeinterlacingFilter.cpp
@@ -190,28 +190,138 @@ TEST(ImageDeinterlacingFilter, PalettedW
 TEST(ImageDeinterlacingFilter, PalettedWriteRows)
 {
   WithPalettedDeinterlacingFilter(IntSize(100, 100),
                                   [](Decoder* aDecoder, SurfaceFilter* aFilter) {
     CheckPalettedWriteRows(aDecoder, aFilter);
   });
 }
 
+TEST(ImageDeinterlacingFilter, WritePixelsNonProgressiveOutput51_52)
+{
+  WithDeinterlacingFilter(IntSize(51, 52), /* aProgressiveDisplay = */ false,
+                          [](Decoder* aDecoder, SurfaceFilter* aFilter) {
+    // Fill the image. The output should be green for even rows and red for odd
+    // rows but we need to write the rows in the order that the deinterlacer
+    // expects them.
+    uint32_t count = 0;
+    auto result = aFilter->WritePixels<uint32_t>([&]() {
+      uint32_t row = count / 51;  // Integer division.
+      ++count;
+
+      // Note that we use a switch statement here, even though it's quite
+      // verbose, because it's useful to have the mappings between input and
+      // output rows available when debugging these tests.
+
+      switch (row) {
+        // First pass. Output rows are positioned at 8n + 0.
+        case 0:  // Output row 0.
+        case 1:  // Output row 8.
+        case 2:  // Output row 16.
+        case 3:  // Output row 24.
+        case 4:  // Output row 32.
+        case 5:  // Output row 40.
+        case 6:  // Output row 48.
+          return AsVariant(BGRAColor::Green().AsPixel());
+
+        // Second pass. Rows are positioned at 8n + 4.
+        case 7:   // Output row 4.
+        case 8:   // Output row 12.
+        case 9:   // Output row 20.
+        case 10:  // Output row 28.
+        case 11:  // Output row 36.
+        case 12:  // Output row 44.
+          return AsVariant(BGRAColor::Green().AsPixel());
+
+        // Third pass. Rows are positioned at 4n + 2.
+        case 13:  // Output row 2.
+        case 14:  // Output row 6.
+        case 15:  // Output row 10.
+        case 16:  // Output row 14.
+        case 17:  // Output row 18.
+        case 18:  // Output row 22.
+        case 19:  // Output row 26.
+        case 20:  // Output row 30.
+        case 21:  // Output row 34.
+        case 22:  // Output row 38.
+        case 23:  // Output row 42.
+        case 24:  // Output row 46.
+        case 25:  // Output row 50.
+          return AsVariant(BGRAColor::Green().AsPixel());
+
+        // Fourth pass. Rows are positioned at 2n + 1.
+        case 26:  // Output row 1.
+        case 27:  // Output row 3.
+        case 28:  // Output row 5.
+        case 29:  // Output row 7.
+        case 30:  // Output row 9.
+        case 31:  // Output row 11.
+        case 32:  // Output row 13.
+        case 33:  // Output row 15.
+        case 34:  // Output row 17.
+        case 35:  // Output row 19.
+        case 36:  // Output row 21.
+        case 37:  // Output row 23.
+        case 38:  // Output row 25.
+        case 39:  // Output row 27.
+        case 40:  // Output row 29.
+        case 41:  // Output row 31.
+        case 42:  // Output row 33.
+        case 43:  // Output row 35.
+        case 44:  // Output row 37.
+        case 45:  // Output row 39.
+        case 46:  // Output row 41.
+        case 47:  // Output row 43.
+        case 48:  // Output row 45.
+        case 49:  // Output row 47.
+        case 50:  // Output row 49.
+        case 51:  // Output row 51.
+          return AsVariant(BGRAColor::Red().AsPixel());
+
+        default:
+          MOZ_ASSERT_UNREACHABLE("Unexpected row");
+          return AsVariant(BGRAColor::Transparent().AsPixel());
+      }
+    });
+    EXPECT_EQ(WriteState::FINISHED, result);
+    EXPECT_EQ(51u * 52u, count);
+
+    AssertCorrectPipelineFinalState(aFilter,
+                                    IntRect(0, 0, 51, 52),
+                                    IntRect(0, 0, 51, 52));
+
+    // Check that the generated image is correct. As mentioned above, we expect
+    // even rows to be green and odd rows to be red.
+    RawAccessFrameRef currentFrame = aDecoder->GetCurrentFrameRef();
+    RefPtr<SourceSurface> surface = currentFrame->GetSurface();
+
+    for (uint32_t row = 0; row < 52; ++row) {
+      EXPECT_TRUE(RowsAreSolidColor(surface, row, 1,
+                                    row % 2 == 0 ? BGRAColor::Green()
+                                                 : BGRAColor::Red()));
+    }
+  });
+}
+
 TEST(ImageDeinterlacingFilter, WritePixelsOutput20_20)
 {
   WithDeinterlacingFilter(IntSize(20, 20), /* aProgressiveDisplay = */ true,
                           [](Decoder* aDecoder, SurfaceFilter* aFilter) {
     // Fill the image. The output should be green for even rows and red for odd
     // rows but we need to write the rows in the order that the deinterlacer
     // expects them.
     uint32_t count = 0;
     auto result = aFilter->WritePixels<uint32_t>([&]() {
       uint32_t row = count / 20;  // Integer division.
       ++count;
 
+      // Note that we use a switch statement here, even though it's quite
+      // verbose, because it's useful to have the mappings between input and
+      // output rows available when debugging these tests.
+
       switch (row) {
         // First pass. Output rows are positioned at 8n + 0.
         case 0:  // Output row 0.
         case 1:  // Output row 8.
         case 2:  // Output row 16.
           return AsVariant(BGRAColor::Green().AsPixel());
 
         // Second pass. Rows are positioned at 8n + 4.
@@ -271,16 +381,17 @@ TEST(ImageDeinterlacingFilter, WriteRows
                           [](Decoder* aDecoder, SurfaceFilter* aFilter) {
     // Fill the image. The output should be a repeating pattern of two green
     // rows followed by two red rows but we need to write the rows in the order
     // that the deinterlacer expects them.
     uint32_t count = 0;
     uint32_t row = 0;
     auto result = aFilter->WriteRows<uint32_t>([&](uint32_t* aRow, uint32_t aLength) {
       uint32_t color = 0;
+
       switch (row) {
         // First pass. Output rows are positioned at 8n + 0.
         case 0:  // Output row 0.
           color = BGRAColor::Green().AsPixel();
           break;
 
         // Second pass. Rows are positioned at 8n + 4.
         case 1:  // Output row 4.
--- a/image/test/gtest/TestSurfacePipeIntegration.cpp
+++ b/image/test/gtest/TestSurfacePipeIntegration.cpp
@@ -119,18 +119,30 @@ TEST(ImageSurfacePipeIntegration, Deinte
   WithFilterPipeline(decoder, test,
                      DeinterlacingConfig<uint32_t> { /* mProgressiveDisplay = */ true },
                      DownscalingConfig { IntSize(100, 100),
                                          SurfaceFormat::B8G8R8A8 },
                      SurfaceConfig { decoder, 0, IntSize(25, 25),
                                      SurfaceFormat::B8G8R8A8, false });
 }
 
-TEST(ImageSurfacePipeIntegration, RemoveFrameRectDownscaleWritePixels)
+TEST(ImageSurfacePipeIntegration, RemoveFrameRectBottomRightDownscaleWritePixels)
 {
+  // This test case uses a frame rect that extends beyond the borders of the
+  // image to the bottom and to the right. It looks roughly like this (with the
+  // box made of '#'s representing the frame rect):
+  //
+  // +------------+
+  // +            +
+  // +      +------------+
+  // +      +############+
+  // +------+############+
+  //        +############+
+  //        +------------+
+
   RefPtr<Decoder> decoder = CreateTrivialDecoder();
   ASSERT_TRUE(decoder != nullptr);
 
   // Note that aInputWriteRect is 100x50 because RemoveFrameRectFilter ignores
   // trailing rows that don't show up in the output. (Leading rows unfortunately
   // can't be ignored.) So the action of the pipeline is as follows:
   //
   // (1) RemoveFrameRectFilter reads a 100x50 region of the input.
@@ -163,17 +175,17 @@ TEST(ImageSurfacePipeIntegration, Remove
   WithFilterPipeline(decoder, test,
                      RemoveFrameRectConfig { IntRect(50, 50, 100, 100) },
                      DownscalingConfig { IntSize(100, 100),
                                          SurfaceFormat::B8G8R8A8 },
                      SurfaceConfig { decoder, 0, IntSize(20, 20),
                                      SurfaceFormat::B8G8R8A8, false });
 }
 
-TEST(ImageSurfacePipeIntegration, RemoveFrameRectDownscaleWriteRows)
+TEST(ImageSurfacePipeIntegration, RemoveFrameRectBottomRightDownscaleWriteRows)
 {
   RefPtr<Decoder> decoder = CreateTrivialDecoder();
   ASSERT_TRUE(decoder != nullptr);
 
   // See the WritePixels version of this test for a discussion of where the
   // numbers below come from.
 
   auto test = [](Decoder* aDecoder, SurfaceFilter* aFilter) {
@@ -188,16 +200,73 @@ TEST(ImageSurfacePipeIntegration, Remove
   WithFilterPipeline(decoder, test,
                      RemoveFrameRectConfig { IntRect(50, 50, 100, 100) },
                      DownscalingConfig { IntSize(100, 100),
                                          SurfaceFormat::B8G8R8A8 },
                      SurfaceConfig { decoder, 0, IntSize(20, 20),
                                      SurfaceFormat::B8G8R8A8, false });
 }
 
+TEST(ImageSurfacePipeIntegration, RemoveFrameRectTopLeftDownscaleWritePixels)
+{
+  // This test case uses a frame rect that extends beyond the borders of the
+  // image to the top and to the left. It looks roughly like this (with the
+  // box made of '#'s representing the frame rect):
+  //
+  // +------------+
+  // +############+
+  // +############+------+
+  // +############+      +
+  // +------------+      +
+  //        +            +
+  //        +------------+
+
+  RefPtr<Decoder> decoder = CreateTrivialDecoder();
+  ASSERT_TRUE(decoder != nullptr);
+
+  auto test = [](Decoder* aDecoder, SurfaceFilter* aFilter) {
+    CheckWritePixels(aDecoder, aFilter,
+                     /* aOutputRect = */ Some(IntRect(0, 0, 20, 20)),
+                     /* aInputRect = */ Some(IntRect(0, 0, 100, 100)),
+                     /* aInputWriteRect = */ Some(IntRect(0, 0, 100, 100)),
+                     /* aOutputWriteRect = */ Some(IntRect(0, 0, 10, 10)),
+                     /* aFuzz = */ 0x21);
+  };
+
+  WithFilterPipeline(decoder, test,
+                     RemoveFrameRectConfig { IntRect(-50, -50, 100, 100) },
+                     DownscalingConfig { IntSize(100, 100),
+                                         SurfaceFormat::B8G8R8A8 },
+                     SurfaceConfig { decoder, 0, IntSize(20, 20),
+                                     SurfaceFormat::B8G8R8A8, false });
+}
+
+TEST(ImageSurfacePipeIntegration, RemoveFrameRectTopLeftDownscaleWriteRows)
+{
+  RefPtr<Decoder> decoder = CreateTrivialDecoder();
+  ASSERT_TRUE(decoder != nullptr);
+
+  auto test = [](Decoder* aDecoder, SurfaceFilter* aFilter) {
+    CheckWriteRows(aDecoder, aFilter,
+                   /* aOutputRect = */ Some(IntRect(0, 0, 20, 20)),
+                   /* aInputRect = */ Some(IntRect(0, 0, 100, 100)),
+                   /* aInputWriteRect = */ Some(IntRect(0, 0, 100, 100)),
+                   /* aOutputWriteRect = */ Some(IntRect(0, 0, 10, 10)),
+                   /* aFuzz = */ 0x21);
+  };
+
+  WithFilterPipeline(decoder, test,
+                     RemoveFrameRectConfig { IntRect(-50, -50, 100, 100) },
+                     DownscalingConfig { IntSize(100, 100),
+                                         SurfaceFormat::B8G8R8A8 },
+                     SurfaceConfig { decoder, 0, IntSize(20, 20),
+                                     SurfaceFormat::B8G8R8A8, false });
+}
+
+
 TEST(ImageSurfacePipeIntegration, DeinterlaceRemoveFrameRectWritePixels)
 {
   RefPtr<Decoder> decoder = CreateTrivialDecoder();
   ASSERT_TRUE(decoder != nullptr);
 
   // Note that aInputRect is the full 100x100 size even though
   // RemoveFrameRectFilter is part of this pipeline, because deinterlacing
   // requires reading every row.
--- a/ipc/glue/GeckoChildProcessHost.cpp
+++ b/ipc/glue/GeckoChildProcessHost.cpp
@@ -487,32 +487,34 @@ GeckoChildProcessHost::DissociateActor()
     MessageLoop::current()->
       PostTask(NewRunnableFunction(DelayedDeleteSubprocess, this));
   }
 }
 
 int32_t GeckoChildProcessHost::mChildCounter = 0;
 
 void
-GeckoChildProcessHost::SetChildLogName(const char* varName, const char* origLogName)
+GeckoChildProcessHost::SetChildLogName(const char* varName, const char* origLogName,
+                                       nsACString &buffer)
 {
   // We currently have no portable way to launch child with environment
   // different than parent.  So temporarily change NSPR_LOG_FILE so child
   // inherits value we want it to have. (NSPR only looks at NSPR_LOG_FILE at
   // startup, so it's 'safe' to play with the parent's environment this way.)
-  nsAutoCString setChildLogName(varName);
-  setChildLogName.Append(origLogName);
+  buffer.Assign(varName);
+  buffer.Append(origLogName);
 
   // Append child-specific postfix to name
-  setChildLogName.AppendLiteral(".child-");
-  setChildLogName.AppendInt(mChildCounter);
+  buffer.AppendLiteral(".child-");
+  buffer.AppendInt(mChildCounter);
 
-  // Passing temporary to PR_SetEnv is ok here because env gets copied
-  // by exec, etc., to permanent storage in child when process launched.
-  PR_SetEnv(setChildLogName.get());
+  // Passing temporary to PR_SetEnv is ok here if we keep the temporary
+  // for the time we launch the sub-process.  It's copied to the new
+  // environment.
+  PR_SetEnv(buffer.BeginReading());
 }
 
 bool
 GeckoChildProcessHost::PerformAsyncLaunch(std::vector<std::string> aExtraOpts, base::ProcessArchitecture arch)
 {
   // If NSPR log files are not requested, we're done.
   const char* origNSPRLogName = PR_GetEnv("NSPR_LOG_FILE");
   const char* origMozLogName = PR_GetEnv("MOZ_LOG_FILE");
@@ -523,29 +525,34 @@ GeckoChildProcessHost::PerformAsyncLaunc
   ++mChildCounter;
 
   // remember original value so we can restore it.
   // - Note: this code is not called re-entrantly, nor are restoreOrig*LogName
   //   or mChildCounter touched by any other thread, so this is safe.
   static nsAutoCString restoreOrigNSPRLogName;
   static nsAutoCString restoreOrigMozLogName;
 
+  // Must keep these on the same stack where from we call PerformAsyncLaunchInternal
+  // so that PR_DuplicateEnvironment() still sees a valid memory.
+  nsAutoCString nsprLogName;
+  nsAutoCString mozLogName;
+
   if (origNSPRLogName) {
     if (restoreOrigNSPRLogName.IsEmpty()) {
       restoreOrigNSPRLogName.AssignLiteral("NSPR_LOG_FILE=");
       restoreOrigNSPRLogName.Append(origNSPRLogName);
     }
-    SetChildLogName("NSPR_LOG_FILE=", origNSPRLogName);
+    SetChildLogName("NSPR_LOG_FILE=", origNSPRLogName, nsprLogName);
   }
   if (origMozLogName) {
     if (restoreOrigMozLogName.IsEmpty()) {
       restoreOrigMozLogName.AssignLiteral("MOZ_LOG_FILE=");
       restoreOrigMozLogName.Append(origMozLogName);
     }
-    SetChildLogName("MOZ_LOG_FILE=", origMozLogName);
+    SetChildLogName("MOZ_LOG_FILE=", origMozLogName, mozLogName);
   }
 
   bool retval = PerformAsyncLaunchInternal(aExtraOpts, arch);
 
   // Revert to original value
   if (origNSPRLogName) {
     PR_SetEnv(restoreOrigNSPRLogName.get());
   }
--- a/ipc/glue/GeckoChildProcessHost.h
+++ b/ipc/glue/GeckoChildProcessHost.h
@@ -195,17 +195,20 @@ private:
   bool PerformAsyncLaunchInternal(std::vector<std::string>& aExtraOpts,
                                   base::ProcessArchitecture arch);
 
   bool RunPerformAsyncLaunch(StringVector aExtraOpts=StringVector(),
 			     base::ProcessArchitecture aArch=base::GetCurrentProcessArchitecture());
 
   static void GetPathToBinary(FilePath& exePath);
 
-  void SetChildLogName(const char* varName, const char* origLogName);
+  // The buffer is passed to preserve its lifetime until we are done
+  // with launching the sub-process.
+  void SetChildLogName(const char* varName, const char* origLogName,
+                       nsACString &buffer);
 
   // In between launching the subprocess and handing off its IPC
   // channel, there's a small window of time in which *we* might still
   // be the channel listener, and receive messages.  That's bad
   // because we have no idea what to do with those messages.  So queue
   // them here until we hand off the eventual listener.
   //
   // FIXME/cjones: this strongly indicates bad design.  Shame on us.
--- a/js/src/asmjs/WasmTextToBinary.cpp
+++ b/js/src/asmjs/WasmTextToBinary.cpp
@@ -1725,21 +1725,22 @@ ParseFloatLiteral(WasmParseContext& c, W
             c.ts.generateError(token, c.error);
             return false;
         }
         break;
       case WasmToken::DecNumber: {
         // Call into JS' strtod. Tokenization has already required that the
         // string is well-behaved.
         LifoAlloc::Mark mark = c.lifo.mark();
-        char* buffer = c.lifo.newArray<char>(end - begin + 1);
+        char* buffer = c.lifo.newArray<char>(end - cur + 1);
         if (!buffer)
             return false;
         for (ptrdiff_t i = 0; i < end - cur; ++i)
             buffer[i] = char(cur[i]);
+        buffer[end - cur] = '\0';
         char* strtod_end;
         int err;
         Float d = (Float)js_strtod_harder(c.dtoaState, buffer, &strtod_end, &err);
         if (err != 0 || strtod_end == buffer) {
             c.lifo.release(mark);
             c.ts.generateError(token, c.error);
             return false;
         }
--- a/js/src/builtin/Promise.cpp
+++ b/js/src/builtin/Promise.cpp
@@ -9,16 +9,17 @@
 
 #include "mozilla/Atomics.h"
 
 #include "jscntxt.h"
 
 #include "gc/Heap.h"
 #include "js/Date.h"
 #include "js/Debug.h"
+#include "vm/SelfHosting.h"
 
 #include "jsobjinlines.h"
 
 #include "vm/NativeObject-inl.h"
 
 using namespace js;
 
 static const JSFunctionSpec promise_methods[] = {
@@ -35,17 +36,49 @@ static const JSFunctionSpec promise_stat
     JS_FS_END
 };
 
 static const JSPropertySpec promise_static_properties[] = {
     JS_SELF_HOSTED_SYM_GET(species, "Promise_static_get_species", 0),
     JS_PS_END
 };
 
-// ES6, 25.4.3.1. steps 3-11.
+static Value
+Now()
+{
+    return JS::TimeValue(JS::TimeClip(static_cast<double>(PRMJ_Now()) / PRMJ_USEC_PER_MSEC));
+}
+
+static bool
+CreateResolvingFunctions(JSContext* cx, HandleValue promise,
+                         MutableHandleValue resolveVal,
+                         MutableHandleValue rejectVal)
+{
+    FixedInvokeArgs<1> args(cx);
+    args[0].set(promise);
+
+    RootedValue rval(cx);
+
+    if (!CallSelfHostedFunction(cx, cx->names().CreateResolvingFunctions, UndefinedHandleValue,
+                                args, &rval))
+    {
+        return false;
+    }
+
+    RootedArrayObject resolvingFunctions(cx, &args.rval().toObject().as<ArrayObject>());
+    resolveVal.set(resolvingFunctions->getDenseElement(0));
+    rejectVal.set(resolvingFunctions->getDenseElement(1));
+
+    MOZ_ASSERT(IsCallable(resolveVal));
+    MOZ_ASSERT(IsCallable(rejectVal));
+
+    return true;
+}
+
+// ES2016, February 12 draft, 25.4.3.1. steps 3-11.
 PromiseObject*
 PromiseObject::create(JSContext* cx, HandleObject executor, HandleObject proto /* = nullptr */)
 {
     MOZ_ASSERT(executor->isCallable());
 
     RootedObject usedProto(cx, proto);
     bool wrappedProto = false;
     // If the proto is wrapped, that means the current function is running
@@ -68,77 +101,62 @@ PromiseObject::create(JSContext* cx, Han
         // All state stored in a Promise's fixed slots must be created in the
         // same compartment, so we get all of that out of the way here.
         // (Except for the resolution functions, which are created below.)
         mozilla::Maybe<AutoCompartment> ac;
         if (wrappedProto)
             ac.emplace(cx, usedProto);
 
         promise = &NewObjectWithClassProto(cx, &class_, usedProto)->as<PromiseObject>();
-
-        // Step 4.
         if (!promise)
             return nullptr;
 
-        // Step 5.
+        // Step 4.
         promise->setFixedSlot(PROMISE_STATE_SLOT, Int32Value(PROMISE_STATE_PENDING));
 
-        // Step 6.
+        // Step 5.
         RootedArrayObject reactions(cx, NewDenseEmptyArray(cx));
         if (!reactions)
             return nullptr;
         promise->setFixedSlot(PROMISE_FULFILL_REACTIONS_SLOT, ObjectValue(*reactions));
 
-        // Step 7.
+        // Step 6.
         reactions = NewDenseEmptyArray(cx);
         if (!reactions)
             return nullptr;
         promise->setFixedSlot(PROMISE_REJECT_REACTIONS_SLOT, ObjectValue(*reactions));
 
+        // Step 7.
+        promise->setFixedSlot(PROMISE_IS_HANDLED_SLOT,
+                              Int32Value(PROMISE_IS_HANDLED_STATE_UNHANDLED));
+
+        // Store an allocation stack so we can later figure out what the
+        // control flow was for some unexpected results. Frightfully expensive,
+        // but oh well.
         RootedObject stack(cx);
-        if (!JS::CaptureCurrentStack(cx, &stack, 0))
-            return nullptr;
-        promise->setFixedSlot(PROMISE_ALLOCATION_SITE_SLOT, ObjectValue(*stack));
-        Value now = JS::TimeValue(JS::TimeClip(static_cast<double>(PRMJ_Now()) /
-                                               PRMJ_USEC_PER_MSEC));
-        promise->setFixedSlot(PROMISE_ALLOCATION_TIME_SLOT, now);
+        if (cx->runtime()->options().asyncStack() || cx->compartment()->isDebuggee()) {
+            if (!JS::CaptureCurrentStack(cx, &stack, 0))
+                return nullptr;
+        }
+        promise->setFixedSlot(PROMISE_ALLOCATION_SITE_SLOT, ObjectOrNullValue(stack));
+        promise->setFixedSlot(PROMISE_ALLOCATION_TIME_SLOT, Now());
     }
 
     RootedValue promiseVal(cx, ObjectValue(*promise));
     if (wrappedProto && !cx->compartment()->wrap(cx, &promiseVal))
         return nullptr;
 
     // Step 8.
     // The resolving functions are created in the compartment active when the
     // (maybe wrapped) Promise constructor was called. They contain checks and
     // can unwrap the Promise if required.
-    RootedValue resolvingFunctionsVal(cx);
-    if (!GlobalObject::getIntrinsicValue(cx, cx->global(), cx->names().CreateResolvingFunctions,
-                                         &resolvingFunctionsVal))
-    {
+    RootedValue resolveVal(cx);
+    RootedValue rejectVal(cx);
+    if (!CreateResolvingFunctions(cx, promiseVal, &resolveVal, &rejectVal))
         return nullptr;
-    }
-
-    RootedArrayObject resolvingFunctions(cx);
-    {
-        FixedInvokeArgs<1> args(cx);
-
-        args[0].set(promiseVal);
-
-        RootedValue rval(cx);
-        if (!Call(cx, resolvingFunctionsVal, UndefinedHandleValue, args, &rval))
-            return nullptr;
-
-        resolvingFunctions = &rval.toObject().as<ArrayObject>();
-    }
-
-    RootedValue resolveVal(cx, resolvingFunctions->getDenseElement(0));
-    MOZ_ASSERT(IsCallable(resolveVal));
-    RootedValue rejectVal(cx, resolvingFunctions->getDenseElement(1));
-    MOZ_ASSERT(IsCallable(rejectVal));
 
     // Need to wrap the resolution functions before storing them on the Promise.
     if (wrappedProto) {
         AutoCompartment ac(cx, promise);
         RootedValue wrappedResolveVal(cx, resolveVal);
         RootedValue wrappedRejectVal(cx, rejectVal);
         if (!cx->compartment()->wrap(cx, &wrappedResolveVal) ||
             !cx->compartment()->wrap(cx, &wrappedRejectVal))
@@ -176,36 +194,37 @@ PromiseObject::create(JSContext* cx, Han
 
         args[0].set(exceptionVal);
 
         // |rejectVal| is unused after this, so we can safely write to it.
         if (!Call(cx, rejectVal, UndefinedHandleValue, args, &rejectVal))
             return nullptr;
     }
 
+    // Let the Debugger know about this Promise.
     JS::dbg::onNewPromise(cx, promise);
 
     // Step 11.
     return promise;
 }
 
 namespace {
 // Generator used by PromiseObject::getID.
 mozilla::Atomic<uint64_t> gIDGenerator(0);
 } // namespace
 
-double
+uint64_t
 PromiseObject::getID()
 {
     Value idVal(getReservedSlot(PROMISE_ID_SLOT));
     if (idVal.isUndefined()) {
         idVal.setDouble(++gIDGenerator);
         setReservedSlot(PROMISE_ID_SLOT, idVal);
     }
-    return idVal.toNumber();
+    return uint64_t(idVal.toNumber());
 }
 
 /**
  * Returns all promises that directly depend on this one. That means those
  * created by calling `then` on this promise, or the promise returned by
  * `Promise.all(iterable)` or `Promise.race(iterable)`, with this promise
  * being a member of the passed-in `iterable`.
  *
@@ -386,16 +405,134 @@ PromiseObject::reject(JSContext* cx, Han
     FixedInvokeArgs<1> args(cx);
 
     args[0].set(rejectionValue);
 
     RootedValue dummy(cx);
     return Call(cx, funVal, UndefinedHandleValue, args, &dummy);
 }
 
+void PromiseObject::onSettled(JSContext* cx)
+{
+    Rooted<PromiseObject*> promise(cx, this);
+    RootedObject stack(cx);
+    if (cx->runtime()->options().asyncStack() || cx->compartment()->isDebuggee()) {
+        if (!JS::CaptureCurrentStack(cx, &stack, 0)) {
+            cx->clearPendingException();
+            return;
+        }
+    }
+    promise->setFixedSlot(PROMISE_RESOLUTION_SITE_SLOT, ObjectOrNullValue(stack));
+    promise->setFixedSlot(PROMISE_RESOLUTION_TIME_SLOT, Now());
+
+    if (promise->state() == JS::PromiseState::Rejected &&
+        promise->getFixedSlot(PROMISE_IS_HANDLED_SLOT).toInt32() !=
+            PROMISE_IS_HANDLED_STATE_HANDLED)
+    {
+        cx->runtime()->addUnhandledRejectedPromise(cx, promise);
+    }
+
+    JS::dbg::onPromiseSettled(cx, promise);
+}
+
+// ES6, 25.4.2.1.
+bool
+PromiseReactionJob(JSContext* cx, unsigned argc, Value* vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+    RootedFunction job(cx, &args.callee().as<JSFunction>());
+    RootedNativeObject jobArgs(cx, &job->getExtendedSlot(0).toObject().as<NativeObject>());
+
+    RootedValue argument(cx, jobArgs->getDenseElement(1));
+
+    // Step 1 (omitted).
+
+    // Steps 2-3.
+    RootedValue handlerVal(cx, jobArgs->getDenseElement(0));
+    RootedValue handlerResult(cx);
+    bool shouldReject = false;
+
+    // Steps 4-7.
+    if (handlerVal.isNumber()) {
+        int32_t handlerNum = int32_t(handlerVal.toNumber());
+        // Step 4.
+        if (handlerNum == PROMISE_HANDLER_IDENTITY) {
+            handlerResult = argument;
+        } else {
+            // Step 5.
+            MOZ_ASSERT(handlerNum == PROMISE_HANDLER_THROWER);
+            shouldReject = true;
+            handlerResult = argument;
+        }
+    } else {
+        // Step 6.
+        FixedInvokeArgs<1> args2(cx);
+        args2[0].set(argument);
+        if (!Call(cx, handlerVal, UndefinedHandleValue, args2, &handlerResult)) {
+            shouldReject = true;
+            // Not much we can do about uncatchable exceptions, so just bail
+            // for those.
+            if (!cx->isExceptionPending() || !GetAndClearException(cx, &handlerResult))
+                return false;
+        }
+    }
+
+    // Steps 7-9.
+    FixedInvokeArgs<1> args2(cx);
+    args2[0].set(handlerResult);
+    RootedValue calleeOrRval(cx);
+    if (shouldReject) {
+        calleeOrRval = jobArgs->getDenseElement(3);
+    } else {
+        calleeOrRval = jobArgs->getDenseElement(2);
+    }
+    bool result = Call(cx, calleeOrRval, UndefinedHandleValue, args2, &calleeOrRval);
+
+    args.rval().set(calleeOrRval);
+    return result;
+}
+
+// ES6, 25.4.2.2.
+bool
+PromiseResolveThenableJob(JSContext* cx, unsigned argc, Value* vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+    RootedFunction job(cx, &args.callee().as<JSFunction>());
+    RootedNativeObject jobArgs(cx, &job->getExtendedSlot(0).toObject().as<NativeObject>());
+
+    RootedValue promise(cx, jobArgs->getDenseElement(2));
+    RootedValue then(cx, jobArgs->getDenseElement(0));
+    RootedValue thenable(cx, jobArgs->getDenseElement(1));
+
+    // Step 1.
+    RootedValue resolveVal(cx);
+    RootedValue rejectVal(cx);
+    if (!CreateResolvingFunctions(cx, promise, &resolveVal, &rejectVal))
+        return false;
+
+    // Step 2.
+    FixedInvokeArgs<2> args2(cx);
+    args2[0].set(resolveVal);
+    args2[1].set(rejectVal);
+
+    RootedValue rval(cx);
+
+    // In difference to the usual pattern, we return immediately on success.
+    if (Call(cx, then, thenable, args2, &rval))
+        return true;
+
+    if (!GetAndClearException(cx, &rval))
+        return false;
+
+    FixedInvokeArgs<1> rejectArgs(cx);
+    rejectArgs[0].set(rval);
+
+    return Call(cx, rejectVal, UndefinedHandleValue, rejectArgs, &rval);
+}
+
 } // namespace js
 
 static JSObject*
 CreatePromisePrototype(JSContext* cx, JSProtoKey key)
 {
     return cx->global()->createBlankPrototype(cx, &PromiseObject::protoClass_);
 }
 
--- a/js/src/builtin/Promise.h
+++ b/js/src/builtin/Promise.h
@@ -14,17 +14,17 @@
 
 namespace js {
 
 class AutoSetNewObjectMetadata;
 
 class PromiseObject : public NativeObject
 {
   public:
-    static const unsigned RESERVED_SLOTS = 11;
+    static const unsigned RESERVED_SLOTS = 12;
     static const Class class_;
     static const Class protoClass_;
     static PromiseObject* create(JSContext* cx, HandleObject executor,
                                  HandleObject proto = nullptr);
 
     JS::PromiseState state() {
         int32_t state = getFixedSlot(PROMISE_STATE_SLOT).toInt32();
         MOZ_ASSERT(state >= 0 && state <= int32_t(JS::PromiseState::Rejected));
@@ -37,27 +37,47 @@ class PromiseObject : public NativeObjec
     Value reason() {
         MOZ_ASSERT(state() == JS::PromiseState::Rejected);
         return getFixedSlot(PROMISE_RESULT_SLOT);
     }
 
     MOZ_MUST_USE bool resolve(JSContext* cx, HandleValue resolutionValue);
     MOZ_MUST_USE bool reject(JSContext* cx, HandleValue rejectionValue);
 
+    void onSettled(JSContext* cx);
+
     double allocationTime() { return getFixedSlot(PROMISE_ALLOCATION_TIME_SLOT).toNumber(); }
     double resolutionTime() { return getFixedSlot(PROMISE_RESOLUTION_TIME_SLOT).toNumber(); }
-    JSObject* allocationSite() { return &getFixedSlot(PROMISE_ALLOCATION_SITE_SLOT).toObject(); }
-    JSObject* resolutionSite() { return &getFixedSlot(PROMISE_RESOLUTION_SITE_SLOT).toObject(); }
+    JSObject* allocationSite() {
+        return getFixedSlot(PROMISE_ALLOCATION_SITE_SLOT).toObjectOrNull();
+    }
+    JSObject* resolutionSite() {
+        return getFixedSlot(PROMISE_RESOLUTION_SITE_SLOT).toObjectOrNull();
+    }
     double lifetime() {
         double now = JS::TimeClip(static_cast<double>(PRMJ_Now()) / PRMJ_USEC_PER_MSEC).toDouble();
         return now - allocationTime();
     }
     double timeToResolution() {
         MOZ_ASSERT(state() != JS::PromiseState::Pending);
         return resolutionTime() - allocationTime();
     }
     MOZ_MUST_USE bool dependentPromises(JSContext* cx, MutableHandle<GCVector<Value>> values);
-    double getID();
+    uint64_t getID();
+    bool markedAsUncaught() {
+        return getFixedSlot(PROMISE_IS_HANDLED_SLOT).toInt32() != PROMISE_IS_HANDLED_STATE_HANDLED;
+    }
+    void markAsReported() {
+        MOZ_ASSERT(getFixedSlot(PROMISE_IS_HANDLED_SLOT).toInt32() ==
+                   PROMISE_IS_HANDLED_STATE_UNHANDLED);
+        setFixedSlot(PROMISE_IS_HANDLED_SLOT, Int32Value(PROMISE_IS_HANDLED_STATE_REPORTED));
+    }
 };
 
+// ES6, 25.4.2.1.
+bool PromiseReactionJob(JSContext* cx, unsigned argc, Value* vp);
+
+// ES6, 25.4.2.2.
+bool PromiseResolveThenableJob(JSContext* cx, unsigned argc, Value* vp);
+
 } // namespace js
 
 #endif /* builtin_Promise_h */
--- a/js/src/builtin/Promise.js
+++ b/js/src/builtin/Promise.js
@@ -119,16 +119,17 @@ function CreateResolvingFunctions(promis
 function FulfillPromise(promise, value) {
     return ResolvePromise(promise, value, PROMISE_FULFILL_REACTIONS_SLOT, PROMISE_STATE_FULFILLED);
 }
 function FulfillUnwrappedPromise(value) {
     return ResolvePromise(this, value, PROMISE_FULFILL_REACTIONS_SLOT, PROMISE_STATE_FULFILLED);
 }
 
 // Commoned-out implementation of 25.4.1.4. and 25.4.1.7.
+// ES2016 February 12 draft.
 function ResolvePromise(promise, valueOrReason, reactionsSlot, state) {
     // Step 1.
     assert(GetPromiseState(promise) === PROMISE_STATE_PENDING,
            "Can't resolve non-pending promise");
     assert(state >= PROMISE_STATE_PENDING && state <= PROMISE_STATE_REJECTED,
            `Invalid Promise state <${state}>`);
 
     // Step 2.
@@ -146,22 +147,21 @@ function ResolvePromise(promise, valueOr
     // Step 6.
     UnsafeSetReservedSlot(promise, PROMISE_STATE_SLOT, state);
 
     // Also null out the resolve/reject functions so they can be GC'd.
     UnsafeSetReservedSlot(promise, PROMISE_RESOLVE_FUNCTION_SLOT, null);
     UnsafeSetReservedSlot(promise, PROMISE_REJECT_FUNCTION_SLOT, null);
 
     // Now that everything else is done, do the things the debugger needs.
-    let site = _dbg_captureCurrentStack(0);
-    UnsafeSetReservedSlot(promise, PROMISE_RESOLUTION_SITE_SLOT, site);
-    UnsafeSetReservedSlot(promise, PROMISE_RESOLUTION_TIME_SLOT, std_Date_now());
+    // Step 7 of RejectPromise implemented in the debugger intrinsic.
     _dbg_onPromiseSettled(promise);
 
-    // Step 7.
+    // Step 7 of FulfillPromise.
+    // Step 8 of RejectPromise.
     return TriggerPromiseReactions(reactions, valueOrReason);
 }
 
 // Used to verify that an object is a PromiseCapability record.
 var PromiseCapabilityRecordProto = {__proto__: null};
 
 // ES6, 25.4.1.5.
 // Creates PromiseCapability records, see 25.4.1.1.
@@ -204,89 +204,49 @@ function NewPromiseCapability(C) {
         promise,
         resolve,
         reject
     };
 }
 
 // ES6, 25.4.1.6. is implemented as an intrinsic in SelfHosting.cpp.
 
-// ES6, 25.4.1.7.
+// ES2016, February 12 draft, 25.4.1.7.
 function RejectPromise(promise, reason) {
     return ResolvePromise(promise, reason, PROMISE_REJECT_REACTIONS_SLOT, PROMISE_STATE_REJECTED);
 }
 
 // ES6, 25.4.1.8.
 function TriggerPromiseReactions(reactions, argument) {
     // Step 1.
     for (var i = 0, len = reactions.length; i < len; i++)
         EnqueuePromiseReactionJob(reactions[i], argument);
     // Step 2 (implicit).
 }
 
+// ES2016, February 12 draft 25.4.1.9, implemented in SelfHosting.cpp.
+
 // ES6, 25.4.2.1.
 function EnqueuePromiseReactionJob(reaction, argument) {
-    _EnqueuePromiseJob(function PromiseReactionJob() {
-        // Step 1.
-        assert(IsPromiseReaction(reaction), "Invalid promise reaction record");
-
-        // Step 2.
-        let promiseCapability = reaction.capabilities;
-
-        // Step 3.
-        let handler = reaction.handler;
-        let handlerResult = argument;
-        let shouldReject = false;
-
-        // Steps 4-6.
-        if (handler === PROMISE_HANDLER_IDENTITY) {
-            // handlerResult = argument; (implicit)
-        } else if (handler === PROMISE_HANDLER_THROWER) {
-            // handlerResult = argument; (implicit)
-            shouldReject = true;
-        } else {
-            try {
-                handlerResult = callContentFunction(handler, undefined, argument);
-            } catch (e) {
-                handlerResult = e;
-                shouldReject = true;
-            }
-        }
-
-        // Step 7.
-        if (shouldReject) {
-            // Step 7.a.
-            callContentFunction(promiseCapability.reject, undefined, handlerResult);
-
-            // Step 7.b.
-            return;
-        }
-
-        // Steps 8-9.
-        return callContentFunction(promiseCapability.resolve, undefined, handlerResult);
-    });
+    let capabilities = reaction.capabilities;
+    _EnqueuePromiseReactionJob([reaction.handler,
+                                argument,
+                                capabilities.resolve,
+                                capabilities.reject
+                               ],
+                               capabilities.promise);
 }
 
 // ES6, 25.4.2.2.
 function EnqueuePromiseResolveThenableJob(promiseToResolve, thenable, then) {
-    _EnqueuePromiseJob(function PromiseResolveThenableJob() {
-        // Step 1.
-        let {0: resolve, 1: reject} = CreateResolvingFunctions(promiseToResolve);
-
-        // Steps 2-3.
-        try {
-            // Step 2.
-            callContentFunction(then, thenable, resolve, reject);
-        } catch (thenCallResult) {
-            // Steps 3.a-b.
-            callFunction(reject, undefined, thenCallResult);
-        }
-
-        // Step 4 (implicit, no need to return anything).
-    });
+    _EnqueuePromiseResolveThenableJob([then,
+                                       thenable,
+                                       promiseToResolve
+                                      ],
+                                      promiseToResolve);
 }
 
 // ES6, 25.4.3.1. (Implemented in C++).
 
 // ES7 2016-01-21 draft, 25.4.4.1.
 function Promise_static_all(iterable) {
     // Step 1.
     let C = this;
@@ -896,17 +856,17 @@ function UnwrappedPerformPromiseThen(ful
     function onRejected(argument) {
         return UnsafeCallWrappedFunction(rejectedHandler, undefined, argument);
     }
     return PerformPromiseThen(this, IsCallable(fulfilledHandler) ? onFulfilled : fulfilledHandler,
                               IsCallable(rejectedHandler) ? onRejected : rejectedHandler,
                               resultCapability);
 }
 
-// ES6, 25.4.5.3.1.
+// ES2016, March 1, 2016 draft, 25.4.5.3.1.
 function PerformPromiseThen(promise, onFulfilled, onRejected, resultCapability) {
     // Step 1.
     assert(IsPromise(promise), "Can't call PerformPromiseThen on non-Promise objects");
 
     // Step 2.
     assert(IsPromiseCapability(resultCapability), "Invalid promise capability record");
 
     // Step 3.
@@ -950,25 +910,38 @@ function PerformPromiseThen(promise, onF
         // Step 8.a.
         let value = UnsafeGetReservedSlot(promise, PROMISE_RESULT_SLOT);
 
         // Step 8.b.
         EnqueuePromiseReactionJob(fulfillReaction, value);
     }
 
     // Step 9.
-    else if (state === PROMISE_STATE_REJECTED) {
+    else {
         // Step 9.a.
+        assert(state === PROMISE_STATE_REJECTED, "Invalid Promise state " + state);
+
+        // Step 9.b.
         let reason = UnsafeGetReservedSlot(promise, PROMISE_RESULT_SLOT);
 
-        // Step 9.b.
+        // Step 9.c.
+        if (UnsafeGetInt32FromReservedSlot(promise, PROMISE_IS_HANDLED_SLOT) !==
+            PROMISE_IS_HANDLED_STATE_HANDLED)
+        {
+            HostPromiseRejectionTracker(promise, PROMISE_REJECTION_TRACKER_OPERATION_HANDLE);
+        }
+
+        // Step 9.d.
         EnqueuePromiseReactionJob(rejectReaction, reason);
     }
 
     // Step 10.
+    UnsafeSetReservedSlot(promise, PROMISE_IS_HANDLED_SLOT, PROMISE_IS_HANDLED_STATE_HANDLED);
+
+    // Step 11.
     return resultCapability.promise;
 }
 
 /// Utility functions below.
 function IsPromiseReaction(record) {
     return std_Reflect_getPrototypeOf(record) === PromiseReactionRecordProto;
 }
 
--- a/js/src/builtin/SelfHostingDefines.h
+++ b/js/src/builtin/SelfHostingDefines.h
@@ -63,24 +63,32 @@
 #define PROMISE_REJECT_REACTIONS_SLOT  3
 #define PROMISE_RESOLVE_FUNCTION_SLOT  4
 #define PROMISE_REJECT_FUNCTION_SLOT   5
 #define PROMISE_ALLOCATION_SITE_SLOT   6
 #define PROMISE_RESOLUTION_SITE_SLOT   7
 #define PROMISE_ALLOCATION_TIME_SLOT   8
 #define PROMISE_RESOLUTION_TIME_SLOT   9
 #define PROMISE_ID_SLOT               10
+#define PROMISE_IS_HANDLED_SLOT       11
 
 #define PROMISE_STATE_PENDING   0
 #define PROMISE_STATE_FULFILLED 1
 #define PROMISE_STATE_REJECTED  2
 
+#define PROMISE_IS_HANDLED_STATE_HANDLED   0
+#define PROMISE_IS_HANDLED_STATE_UNHANDLED 1
+#define PROMISE_IS_HANDLED_STATE_REPORTED  2
+
 #define PROMISE_HANDLER_IDENTITY 0
 #define PROMISE_HANDLER_THROWER  1
 
+#define PROMISE_REJECTION_TRACKER_OPERATION_REJECT false
+#define PROMISE_REJECTION_TRACKER_OPERATION_HANDLE true
+
 // NB: keep these in sync with the copy in jsfriendapi.h.
 #define JSITER_OWNONLY    0x8   /* iterate over obj's own properties only */
 #define JSITER_HIDDEN     0x10  /* also enumerate non-enumerable properties */
 #define JSITER_SYMBOLS    0x20  /* also include symbol property keys */
 #define JSITER_SYMBOLSONLY 0x40 /* exclude string property keys */
 
 #define TELEMETRY_DEFINE_GETTER_SETTER_THIS_NULL_UNDEFINED 25
 
--- a/js/src/gc/GCRuntime.h
+++ b/js/src/gc/GCRuntime.h
@@ -848,19 +848,20 @@ class GCRuntime
     void startVerifyPreBarriers();
     void endVerifyPreBarriers();
     void finishVerifier();
     bool isVerifyPreBarriersEnabled() const { return !!verifyPreData; }
 #else
     bool isVerifyPreBarriersEnabled() const { return false; }
 #endif
 
-    // Free certain LifoAlloc blocks from the background sweep thread.
+    // Free certain LifoAlloc blocks when it is safe to do so.
     void freeUnusedLifoBlocksAfterSweeping(LifoAlloc* lifo);
     void freeAllLifoBlocksAfterSweeping(LifoAlloc* lifo);
+    void freeAllLifoBlocksAfterMinorGC(LifoAlloc* lifo);
 
     // Public here for ReleaseArenaLists and FinalizeTypedArenas.
     void releaseArena(Arena* arena, const AutoLockGC& lock);
 
     void releaseHeldRelocatedArenas();
     void releaseHeldRelocatedArenasWithoutUnlocking(const AutoLockGC& lock);
 
     // Allocator
@@ -1164,19 +1165,25 @@ class GCRuntime
     /* Whether any black->gray edges were found during marking. */
     bool foundBlackGrayEdges;
 
     /* Singly linekd list of zones to be swept in the background. */
     ZoneList backgroundSweepZones;
 
     /*
      * Free LIFO blocks are transferred to this allocator before being freed on
-     * the background GC thread.
+     * the background GC thread after sweeping.
      */
-    LifoAlloc freeLifoAlloc;
+    LifoAlloc blocksToFreeAfterSweeping;
+
+    /*
+     * Free LIFO blocks are transferred to this allocator before being freed
+     * after minor GC.
+     */
+    LifoAlloc blocksToFreeAfterMinorGC;
 
     /* Index of current zone group (for stats). */
     unsigned zoneGroupIndex;
 
     /*
      * Incremental sweep state.
      */
     JS::Zone* zoneGroups;
--- a/js/src/gc/Zone.cpp
+++ b/js/src/gc/Zone.cpp
@@ -214,22 +214,16 @@ Zone::discardJitCode(FreeOp* fop)
 #endif
 
         /* Mark baseline scripts on the stack as active. */
         jit::MarkActiveBaselineScripts(this);
 
         /* Only mark OSI points if code is being discarded. */
         jit::InvalidateAll(fop, this);
 
-        /* The storebuffer may contain pointers into data owned by BaselineScript. */
-        JSRuntime* rt = runtimeFromMainThread();
-        if (!rt->isHeapCollecting())
-            rt->gc.evictNursery();
-        MOZ_ASSERT(rt->gc.nursery.isEmpty());
-
         for (ZoneCellIter i(this, AllocKind::SCRIPT); !i.done(); i.next()) {
             JSScript* script = i.get<JSScript>();
             jit::FinishInvalidation(fop, script);
 
             /*
              * Discard baseline script if it's not marked as active. Note that
              * this also resets the active flag.
              */
@@ -238,17 +232,25 @@ Zone::discardJitCode(FreeOp* fop)
             /*
              * Warm-up counter for scripts are reset on GC. After discarding code we
              * need to let it warm back up to get information such as which
              * opcodes are setting array holes or accessing getter properties.
              */
             script->resetWarmUpCounter();
         }
 
-        jitZone()->optimizedStubSpace()->free();
+        /*
+         * When scripts contains pointers to nursery things, the store buffer
+         * can contain entries that point into the optimized stub space. Since
+         * this method can be called outside the context of a GC, this situation
+         * could result in us trying to mark invalid store buffer entries.
+         *
+         * Defer freeing any allocated blocks until after the next minor GC.
+         */
+        jitZone()->optimizedStubSpace()->freeAllAfterMinorGC(fop->runtime());
     }
 }
 
 #ifdef JSGC_HASH_TABLE_CHECKS
 void
 JS::Zone::checkUniqueIdTableAfterMovingGC()
 {
     for (UniqueIdMap::Enum e(uniqueIds_); !e.empty(); e.popFront())
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/undefined-warning-bug1274499.js
@@ -0,0 +1,13 @@
+options("strict", "werror");
+
+var o = {};
+var failureCode = 0;
+
+try {
+    // Don't throw here.
+    if (o.a || o.b)
+        failureCode = 1;
+} catch (e) {
+    failureCode = 2
+}
+assertEq(failureCode, 0);
rename from js/src/jit-test/tests/jaeger/bug565202.js
rename to js/src/jit-test/tests/basic/undefined-warning-bug565202.js
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/inspect-wrapped-promise.js
@@ -0,0 +1,36 @@
+if (typeof Promise === "undefined")
+    quit(0);
+
+let g = newGlobal();
+let dbg = new Debugger();
+let gw = dbg.addDebuggee(g);
+
+g.promise = Promise.resolve(42);
+
+let promiseDO = gw.getOwnPropertyDescriptor('promise').value;
+
+assertEq(promiseDO.isPromise, true);
+
+let state = promiseDO.promiseState;
+assertEq(state.state, "fulfilled");
+assertEq(state.value, 42);
+assertEq("reason" in state, true);
+assertEq(state.reason, undefined);
+
+let allocationSite = promiseDO.promiseAllocationSite;
+// Depending on whether async stacks are activated, this can be null, which
+// has typeof null.
+assertEq(typeof allocationSite === "object", true);
+
+let resolutionSite = promiseDO.promiseResolutionSite;
+// Depending on whether async stacks are activated, this can be null, which
+// has typeof null.
+assertEq(typeof resolutionSite === "object", true);
+
+assertEq(promiseDO.promiseID, 1);
+
+assertEq(typeof promiseDO.promiseDependentPromises, "object");
+assertEq(promiseDO.promiseDependentPromises.length, 0);
+
+assertEq(typeof promiseDO.promiseLifetime, "number");
+assertEq(typeof promiseDO.promiseTimeToResolution, "number");
--- a/js/src/jit/BaselineCompiler.cpp
+++ b/js/src/jit/BaselineCompiler.cpp
@@ -203,17 +203,18 @@ BaselineCompiler::compile()
                             profilerExitFrameToggleOffset_.offset(),
                             traceLoggerEnterToggleOffset_.offset(),
                             traceLoggerExitToggleOffset_.offset(),
                             postDebugPrologueOffset_.offset(),
                             icEntries_.length(),
                             pcMappingIndexEntries.length(),
                             pcEntries.length(),
                             bytecodeTypeMapEntries,
-                            yieldOffsets_.length()));
+                            yieldOffsets_.length()),
+        JS::DeletePolicy<BaselineScript>(cx->runtime()));
     if (!baselineScript) {
         ReportOutOfMemory(cx);
         return Method_Error;
     }
 
     baselineScript->setMethod(code);
     baselineScript->setTemplateScope(templateScope);
 
--- a/js/src/jit/BaselineDebugModeOSR.cpp
+++ b/js/src/jit/BaselineDebugModeOSR.cpp
@@ -866,19 +866,16 @@ jit::RecompileOnStackBaselineScriptsForD
             if (!CollectInterpreterStackScripts(cx, obs, iter, entries))
                 return false;
         }
     }
 
     if (entries.empty())
         return true;
 
-    // Scripts can entrain nursery things. See note in js::ReleaseAllJITCode.
-    cx->runtime()->gc.evictNursery();
-
     // When the profiler is enabled, we need to have suppressed sampling,
     // since the basline jit scripts are in a state of flux.
     MOZ_ASSERT(!cx->runtime()->isProfilerSamplingEnabled());
 
     // Invalidate all scripts we are recompiling.
     if (Zone* zone = obs.singleZone()) {
         if (!InvalidateScriptsInZone(cx, zone, entries))
             return false;
--- a/js/src/jit/BaselineJIT.cpp
+++ b/js/src/jit/BaselineJIT.cpp
@@ -40,16 +40,22 @@ PCMappingSlotInfo::ToSlotLocation(const 
             return SlotInR0;
         MOZ_ASSERT(stackVal->reg() == R1);
         return SlotInR1;
     }
     MOZ_ASSERT(stackVal->kind() != StackValue::Stack);
     return SlotIgnore;
 }
 
+void
+ICStubSpace::freeAllAfterMinorGC(JSRuntime* rt)
+{
+    rt->gc.freeAllLifoBlocksAfterMinorGC(&allocator_);
+}
+
 BaselineScript::BaselineScript(uint32_t prologueOffset, uint32_t epilogueOffset,
                                uint32_t profilerEnterToggleOffset,
                                uint32_t profilerExitToggleOffset,
                                uint32_t traceLoggerEnterToggleOffset,
                                uint32_t traceLoggerExitToggleOffset,
                                uint32_t postDebugPrologueOffset)
   : method_(nullptr),
     templateScope_(nullptr),
@@ -477,33 +483,40 @@ void
 BaselineScript::Trace(JSTracer* trc, BaselineScript* script)
 {
     script->trace(trc);
 }
 
 void
 BaselineScript::Destroy(FreeOp* fop, BaselineScript* script)
 {
-    /*
-     * When the script contains pointers to nursery things, the store buffer
-     * will contain entries refering to the referenced things. Since we can
-     * destroy scripts outside the context of a GC, this situation can result
-     * in invalid store buffer entries. Assert that if we do destroy scripts
-     * outside of a GC that we at least emptied the nursery first.
-     */
-    MOZ_ASSERT(fop->runtime()->gc.nursery.isEmpty());
-
     MOZ_ASSERT(!script->hasPendingIonBuilder());
 
     script->unlinkDependentWasmModules(fop);
 
+    /*
+     * When the script contains pointers to nursery things, the store buffer can
+     * contain entries that point into the fallback stub space. Since we can
+     * destroy scripts outside the context of a GC, this situation could result
+     * in us trying to mark invalid store buffer entries.
+     *
+     * Defer freeing any allocated blocks until after the next minor GC.
+     */
+    script->fallbackStubSpace_.freeAllAfterMinorGC(fop->runtime());
+
     fop->delete_(script);
 }
 
 void
+JS::DeletePolicy<js::jit::BaselineScript>::operator()(const js::jit::BaselineScript* script)
+{
+    BaselineScript::Destroy(rt_->defaultFreeOp(), const_cast<BaselineScript*>(script));
+}
+
+void
 BaselineScript::clearDependentWasmModules()
 {
     // Remove any links from wasm::Modules that contain optimized import calls into
     // this BaselineScript.
     if (dependentWasmModules_) {
         for (DependentWasmModuleImport dep : *dependentWasmModules_)
             dep.module->deoptimizeImportExit(dep.importIndex);
         dependentWasmModules_->clear();
--- a/js/src/jit/BaselineJIT.h
+++ b/js/src/jit/BaselineJIT.h
@@ -240,16 +240,22 @@ struct BaselineScript
     // Do not call directly, use BaselineScript::New. This is public for cx->new_.
     BaselineScript(uint32_t prologueOffset, uint32_t epilogueOffset,
                    uint32_t profilerEnterToggleOffset,
                    uint32_t profilerExitToggleOffset,
                    uint32_t traceLoggerEnterToggleOffset,
                    uint32_t traceLoggerExitToggleOffset,
                    uint32_t postDebugPrologueOffset);
 
+    ~BaselineScript() {
+        // The contents of the fallback stub space are removed and freed
+        // separately after the next minor GC. See BaselineScript::Destroy.
+        MOZ_ASSERT(fallbackStubSpace_.isEmpty());
+    }
+
     static BaselineScript* New(JSScript* jsscript, uint32_t prologueOffset,
                                uint32_t epilogueOffset, uint32_t postDebugPrologueOffset,
                                uint32_t profilerEnterToggleOffset,
                                uint32_t profilerExitToggleOffset,
                                uint32_t traceLoggerEnterToggleOffset,
                                uint32_t traceLoggerExitToggleOffset,
                                size_t icEntries, size_t pcMappingIndexEntries,
                                size_t pcMappingSize,
@@ -594,9 +600,23 @@ void
 MarkActiveBaselineScripts(Zone* zone);
 
 MethodStatus
 BaselineCompile(JSContext* cx, JSScript* script, bool forceDebugInstrumentation = false);
 
 } // namespace jit
 } // namespace js
 
+namespace JS {
+
+template <>
+struct DeletePolicy<js::jit::BaselineScript>
+{
+    explicit DeletePolicy(JSRuntime* rt) : rt_(rt) {}
+    void operator()(const js::jit::BaselineScript* script);
+
+  private:
+    JSRuntime* rt_;
+};
+
+} // namespace JS
+
 #endif /* jit_BaselineJIT_h */
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -3465,30 +3465,18 @@ CodeGenerator::emitPostWriteBarrier(Regi
     AllocatableGeneralRegisterSet regs(GeneralRegisterSet::Volatile());
     regs.takeUnchecked(objreg);
     EmitPostWriteBarrier(masm, objreg, false, regs);
 }
 
 void
 CodeGenerator::visitOutOfLineCallPostWriteBarrier(OutOfLineCallPostWriteBarrier* ool)
 {
+    saveLiveVolatile(ool->lir());
     const LAllocation* obj = ool->object();
-
-    // Check whether the object is a global that we have already barriered
-    // before calling into the VM.
-    if (obj->isConstant()) {
-        JSObject* object = &obj->toConstant()->toObject();
-        if (object->is<GlobalObject>()) {
-            JSCompartment* comp = object->compartment();
-            AbsoluteAddress addr(&comp->globalWriteBarriered);
-            masm.branch32(Assembler::NotEqual, addr, Imm32(0), ool->rejoin());
-        }
-    }
-
-    saveLiveVolatile(ool->lir());
     emitPostWriteBarrier(obj);
     restoreLiveVolatile(ool->lir());
 
     masm.jump(ool->rejoin());
 }
 
 template <class LPostBarrierType>
 void
--- a/js/src/jit/ICStubSpace.h
+++ b/js/src/jit/ICStubSpace.h
@@ -30,35 +30,39 @@ class ICStubSpace
 
   public:
     inline void* alloc(size_t size) {
         return allocator_.alloc(size);
     }
 
     JS_DECLARE_NEW_METHODS(allocate, alloc, inline)
 
+    void freeAllAfterMinorGC(JSRuntime* rt);
+
+#ifdef DEBUG
+    bool isEmpty() const {
+        return allocator_.isEmpty();
+    }
+#endif
+
     size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
         return allocator_.sizeOfExcludingThis(mallocSizeOf);
     }
 };
 
 // Space for optimized stubs. Every JitCompartment has a single
 // OptimizedICStubSpace.
 struct OptimizedICStubSpace : public ICStubSpace
 {
     static const size_t STUB_DEFAULT_CHUNK_SIZE = 4096;
 
   public:
     OptimizedICStubSpace()
       : ICStubSpace(STUB_DEFAULT_CHUNK_SIZE)
     {}
-
-    void free() {
-        allocator_.freeAll();
-    }
 };
 
 // Space for fallback stubs. Every BaselineScript has a
 // FallbackICStubSpace.
 struct FallbackICStubSpace : public ICStubSpace
 {
     static const size_t STUB_DEFAULT_CHUNK_SIZE = 4096;
 
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -1215,20 +1215,37 @@ IonScript::Trace(JSTracer* trc, IonScrip
     if (script != ION_DISABLED_SCRIPT)
         script->trace(trc);
 }
 
 void
 IonScript::Destroy(FreeOp* fop, IonScript* script)
 {
     script->unlinkFromRuntime(fop);
+
+    /*
+     * When the script contains pointers to nursery things, the store buffer can
+     * contain entries that point into the fallback stub space. Since we can
+     * destroy scripts outside the context of a GC, this situation could result
+     * in us trying to mark invalid store buffer entries.
+     *
+     * Defer freeing any allocated blocks until after the next minor GC.
+     */
+    script->fallbackStubSpace_.freeAllAfterMinorGC(fop->runtime());
+
     fop->delete_(script);
 }
 
 void
+JS::DeletePolicy<js::jit::IonScript>::operator()(const js::jit::IonScript* script)
+{
+    IonScript::Destroy(rt_->defaultFreeOp(), const_cast<IonScript*>(script));
+}
+
+void
 IonScript::toggleBarriers(bool enabled, ReprotectCode reprotect)
 {
     method()->togglePreBarriers(enabled, reprotect);
 }
 
 void
 IonScript::purgeOptimizedStubs(Zone* zone)
 {
--- a/js/src/jit/IonCode.h
+++ b/js/src/jit/IonCode.h
@@ -326,16 +326,22 @@ struct IonScript
 
   private:
     void trace(JSTracer* trc);
 
   public:
     // Do not call directly, use IonScript::New. This is public for cx->new_.
     IonScript();
 
+    ~IonScript() {
+        // The contents of the fallback stub space are removed and freed
+        // separately after the next minor GC. See IonScript::Destroy.
+        MOZ_ASSERT(fallbackStubSpace_.isEmpty());
+    }
+
     static IonScript* New(JSContext* cx, RecompileInfo recompileInfo,
                           uint32_t frameSlots, uint32_t argumentSlots, uint32_t frameSize,
                           size_t snapshotsListSize, size_t snapshotsRVATableSize,
                           size_t recoversSize, size_t bailoutEntries,
                           size_t constants, size_t safepointIndexEntries,
                           size_t osiIndexEntries, size_t cacheEntries,
                           size_t runtimeSize, size_t safepointsSize,
                           size_t backedgeEntries, size_t sharedStubEntries,
@@ -793,11 +799,22 @@ struct Concrete<js::jit::JitCode> : Trac
   protected:
     explicit Concrete(js::jit::JitCode *ptr) : TracerConcrete<js::jit::JitCode>(ptr) { }
 
   public:
     static void construct(void *storage, js::jit::JitCode *ptr) { new (storage) Concrete(ptr); }
 };
 
 } // namespace ubi
+
+template <>
+struct DeletePolicy<js::jit::IonScript>
+{
+    explicit DeletePolicy(JSRuntime* rt) : rt_(rt) {}
+    void operator()(const js::jit::IonScript* script);
+
+  private:
+    JSRuntime* rt_;
+};
+
 } // namespace JS
 
 #endif /* jit_IonCode_h */
--- a/js/src/jit/VMFunctions.cpp
+++ b/js/src/jit/VMFunctions.cpp
@@ -627,17 +627,17 @@ PostWriteElementBarrier(JSRuntime* rt, J
 }
 
 void
 PostGlobalWriteBarrier(JSRuntime* rt, JSObject* obj)
 {
     MOZ_ASSERT(obj->is<GlobalObject>());
     if (!obj->compartment()->globalWriteBarriered) {
         PostWriteBarrier(rt, obj);
-        obj->compartment()->globalWriteBarriered = 1;
+        obj->compartment()->globalWriteBarriered = true;
     }
 }
 
 uint32_t
 GetIndexFromString(JSString* str)
 {
     // Masks the return value UINT32_MAX as failure to get the index.
     // I.e. it is impossible to distinguish between failing to get the index
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -4772,16 +4772,24 @@ JS_GetInterruptCallback(JSRuntime* rt)
 JS_PUBLIC_API(void)
 JS::SetEnqueuePromiseJobCallback(JSRuntime* rt, JSEnqueuePromiseJobCallback callback,
                                  void* data /* = nullptr */)
 {
     rt->enqueuePromiseJobCallback = callback;
     rt->enqueuePromiseJobCallbackData = data;
 }
 
+extern JS_PUBLIC_API(void)
+JS::SetPromiseRejectionTrackerCallback(JSRuntime* rt, JSPromiseRejectionTrackerCallback callback,
+                                       void* data /* = nullptr */)
+{
+    rt->promiseRejectionTrackerCallback = callback;
+    rt->promiseRejectionTrackerCallbackData = data;
+}
+
 JS_PUBLIC_API(JSObject*)
 JS::NewPromiseObject(JSContext* cx, HandleObject executor, HandleObject proto /* = nullptr */)
 {
     MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(cx->compartment()));
     MOZ_ASSERT(IsCallable(executor));
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
 
@@ -4813,17 +4821,17 @@ JS::GetPromisePrototype(JSContext* cx)
 
 JS_PUBLIC_API(JS::PromiseState)
 JS::GetPromiseState(JS::HandleObject obj)
 {
     JSObject* promise = CheckedUnwrap(obj);
     return promise->as<PromiseObject>().state();
 }
 
-JS_PUBLIC_API(double)
+JS_PUBLIC_API(uint64_t)
 JS::GetPromiseID(JS::HandleObject promise)
 {
     return promise->as<PromiseObject>().getID();
 }
 
 JS_PUBLIC_API(JS::Value)
 JS::GetPromiseResult(JS::HandleObject promiseObj)
 {
@@ -5050,27 +5058,25 @@ JS::AutoSetAsyncStackForNewCalls::~AutoS
 }
 
 /************************************************************************/
 JS_PUBLIC_API(JSString*)
 JS_NewStringCopyN(JSContext* cx, const char* s, size_t n)
 {
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
-    if (!n)
-        return cx->names().empty;
     return NewStringCopyN<CanGC>(cx, s, n);
 }
 
 JS_PUBLIC_API(JSString*)
 JS_NewStringCopyZ(JSContext* cx, const char* s)
 {
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
-    if (!s || !*s)
+    if (!s)
         return cx->runtime()->emptyString;
     return NewStringCopyZ<CanGC>(cx, s);
 }
 
 JS_PUBLIC_API(bool)
 JS_StringHasBeenPinned(JSContext* cx, JSString* str)
 {
     AssertHeapIsIdle(cx);
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -598,17 +598,30 @@ typedef void
 
 typedef void
 (* JSWeakPointerCompartmentCallback)(JSRuntime* rt, JSCompartment* comp, void* data);
 
 typedef bool
 (* JSInterruptCallback)(JSContext* cx);
 
 typedef bool
-(* JSEnqueuePromiseJobCallback)(JSContext* cx, JS::HandleObject job, void* data);
+(* JSEnqueuePromiseJobCallback)(JSContext* cx, JS::HandleObject job,
+                                JS::HandleObject allocationSite, void* data);
+
+enum class PromiseRejectionHandlingState {
+    Unhandled,
+    Handled
+};
+
+typedef void
+(* JSPromiseRejectionTrackerCallback)(JSContext* cx, JS::HandleObject promise,
+                                      PromiseRejectionHandlingState state, void* data);
+
+typedef void
+(* JSProcessPromiseCallback)(JSContext* cx, JS::HandleObject promise);
 
 typedef void
 (* JSErrorReporter)(JSContext* cx, const char* message, JSErrorReport* report);
 
 /**
  * Possible exception types. These types are part of a JSErrorFormatString
  * structure. They define which error to throw in case of a runtime error.
  * JSEXN_NONE marks an unthrowable error.
@@ -4397,24 +4410,34 @@ JS_RequestInterruptCallback(JSRuntime* r
 
 namespace JS {
 
 /**
  * Sets the callback that's invoked whenever a Promise job should be enqeued.
  *
  * SpiderMonkey doesn't schedule Promise resolution jobs itself; instead,
  * using this function the embedding can provide a callback to do that
- * scheduling. The provided `callback` is invoked with the promise job
- * and the `data` pointer passed here as arguments.
+ * scheduling. The provided `callback` is invoked with the promise job,
+ * the corresponding Promise's allocation stack, and the `data` pointer
+ * passed here as arguments.
  */
 extern JS_PUBLIC_API(void)
 SetEnqueuePromiseJobCallback(JSRuntime* rt, JSEnqueuePromiseJobCallback callback,
                              void* data = nullptr);
 
 /**
+ * Sets the callback that's invoked whenever a Promise is rejected without
+ * a rejection handler, and when a Promise that was previously rejected
+ * without a handler gets a handler attached.
+ */
+extern JS_PUBLIC_API(void)
+SetPromiseRejectionTrackerCallback(JSRuntime* rt, JSPromiseRejectionTrackerCallback callback,
+                                   void* data = nullptr);
+
+/**
  * Returns a new instance of the Promise builtin class in the current
  * compartment, with the right slot layout. If a `proto` is passed, that gets
  * set as the instance's [[Prototype]] instead of the original value of
  * `Promise.prototype`.
  */
 extern JS_PUBLIC_API(JSObject*)
 NewPromiseObject(JSContext* cx, JS::HandleObject executor, JS::HandleObject proto = nullptr);
 
@@ -4448,17 +4471,17 @@ enum class PromiseState {
  * Returns the given Promise's state as a JS::PromiseState enum value.
  */
 extern JS_PUBLIC_API(PromiseState)
 GetPromiseState(JS::HandleObject promise);
 
 /**
  * Returns the given Promise's process-unique ID.
  */
-JS_PUBLIC_API(double)
+JS_PUBLIC_API(uint64_t)
 GetPromiseID(JS::HandleObject promise);
 
 /**
  * Returns the given Promise's result: either the resolution value for
  * fulfilled promises, or the rejection reason for rejected ones.
  */
 extern JS_PUBLIC_API(JS::Value)
 GetPromiseResult(JS::HandleObject promise);
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -58,17 +58,17 @@ JSCompartment::JSCompartment(Zone* zone,
 #endif
     global_(nullptr),
     enterCompartmentDepth(0),
     performanceMonitoring(runtime_),
     data(nullptr),
     allocationMetadataBuilder(nullptr),
     lastAnimationTime(0),
     regExps(runtime_),
-    globalWriteBarriered(0),
+    globalWriteBarriered(false),
     detachedTypedObjects(0),
     objectMetadataState(ImmediateMetadata()),
     propertyTree(thisForCtor()),
     baseShapes(zone, BaseShapeSet()),
     initialShapes(zone, InitialShapeSet()),
     selfHostingScriptSource(nullptr),
     objectMetadataTable(nullptr),
     lazyArrayBuffers(nullptr),
@@ -675,17 +675,17 @@ JSCompartment::traceRoots(JSTracer* trc,
 
     if (nonSyntacticLexicalScopes_)
         nonSyntacticLexicalScopes_->trace(trc);
 }
 
 void
 JSCompartment::sweepAfterMinorGC()
 {
-    globalWriteBarriered = 0;
+    globalWriteBarriered = false;
 
     if (innerViews.needsSweepAfterMinorGC())
         innerViews.sweepAfterMinorGC();
 }
 
 void
 JSCompartment::sweepInnerViews()
 {
--- a/js/src/jscompartment.h
+++ b/js/src/jscompartment.h
@@ -422,20 +422,20 @@ struct JSCompartment
     int64_t                      lastAnimationTime;
 
     js::RegExpCompartment        regExps;
 
     /*
      * For generational GC, record whether a write barrier has added this
      * compartment's global to the store buffer since the last minor GC.
      *
-     * This is used to avoid calling into the VM every time a nursery object is
-     * written to a property of the global.
+     * This is used to avoid adding it to the store buffer on every write, which
+     * can quickly fill the buffer and also cause performance problems.
      */
-    uint32_t                     globalWriteBarriered;
+    bool                         globalWriteBarriered;
 
     // Non-zero if the storage underlying any typed object in this compartment
     // might be detached.
     int32_t                      detachedTypedObjects;
 
   private:
     friend class js::AutoSetNewObjectMetadata;
     js::NewObjectMetadataState objectMetadataState;
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -1032,17 +1032,18 @@ GCRuntime::GCRuntime(JSRuntime* rt) :
     isFull(false),
 #ifdef DEBUG
     disableStrictProxyCheckingCount(0),
 #endif
     incrementalState(gc::NO_INCREMENTAL),
     lastMarkSlice(false),
     sweepOnBackgroundThread(false),
     foundBlackGrayEdges(false),
-    freeLifoAlloc(JSRuntime::TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
+    blocksToFreeAfterSweeping(JSRuntime::TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
+    blocksToFreeAfterMinorGC(JSRuntime::TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
     zoneGroupIndex(0),
     zoneGroups(nullptr),
     currentZoneGroup(nullptr),
     sweepZone(nullptr),
     sweepKind(AllocKind::FIRST),
     abortSweepAfterCurrentGroup(false),
     arenasAllocatedDuringSweep(nullptr),
     startedCompacting(false),
@@ -2756,17 +2757,17 @@ GCRuntime::updatePointersToRelocatedCell
 
     // Sweep everything to fix up weak pointers
     WatchpointMap::sweepAll(rt);
     Debugger::sweepAll(rt->defaultFreeOp());
     jit::JitRuntime::SweepJitcodeGlobalTable(rt);
     rt->gc.sweepZoneAfterCompacting(zone);
 
     // Type inference may put more blocks here to free.
-    freeLifoAlloc.freeAll();
+    blocksToFreeAfterSweeping.freeAll();
 
     // Call callbacks to get the rest of the system to fixup other untraced pointers.
     callWeakPointerZoneGroupCallbacks();
     for (CompartmentsInZoneIter comp(zone); !comp.done(); comp.next())
         callWeakPointerCompartmentCallbacks(comp);
     if (rt->sweepZoneCallback)
         rt->sweepZoneCallback(zone);
 }
@@ -3437,17 +3438,17 @@ GCRuntime::assertBackgroundSweepingFinis
 #ifdef DEBUG
     MOZ_ASSERT(backgroundSweepZones.isEmpty());
     for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) {
         for (auto i : AllAllocKinds()) {
             MOZ_ASSERT(!zone->arenas.arenaListsToSweep[i]);
             MOZ_ASSERT(zone->arenas.doneBackgroundFinalize(i));
         }
     }
-    MOZ_ASSERT(freeLifoAlloc.computedSizeOfExcludingThis() == 0);
+    MOZ_ASSERT(blocksToFreeAfterSweeping.computedSizeOfExcludingThis() == 0);
 #endif
 }
 
 unsigned
 js::GetCPUCount()
 {
     static unsigned ncpus = 0;
     if (ncpus == 0) {
@@ -3598,25 +3599,32 @@ GCRuntime::queueZonesForBackgroundSweep(
     helperState.maybeStartBackgroundSweep(lock);
 }
 
 void
 GCRuntime::freeUnusedLifoBlocksAfterSweeping(LifoAlloc* lifo)
 {
     MOZ_ASSERT(rt->isHeapBusy());
     AutoLockGC lock(rt);
-    freeLifoAlloc.transferUnusedFrom(lifo);
+    blocksToFreeAfterSweeping.transferUnusedFrom(lifo);
 }
 
 void
 GCRuntime::freeAllLifoBlocksAfterSweeping(LifoAlloc* lifo)
 {
     MOZ_ASSERT(rt->isHeapBusy());
     AutoLockGC lock(rt);
-    freeLifoAlloc.transferFrom(lifo);
+    blocksToFreeAfterSweeping.transferFrom(lifo);
+}
+
+void
+GCRuntime::freeAllLifoBlocksAfterMinorGC(LifoAlloc* lifo)
+{
+    MOZ_ASSERT(CurrentThreadCanAccessRuntime(rt));
+    blocksToFreeAfterMinorGC.transferFrom(lifo);
 }
 
 void
 GCHelperState::maybeStartBackgroundSweep(const AutoLockGC& lock)
 {
     MOZ_ASSERT(CanUseExtraThreads());
 
     if (state() == IDLE)
@@ -3659,17 +3667,17 @@ GCHelperState::doSweep(AutoLockGC& lock)
 
     do {
         while (!rt->gc.backgroundSweepZones.isEmpty()) {
             AutoSetThreadIsSweeping threadIsSweeping;
 
             ZoneList zones;
             zones.transferFrom(rt->gc.backgroundSweepZones);
             LifoAlloc freeLifoAlloc(JSRuntime::TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE);
-            freeLifoAlloc.transferFrom(&rt->gc.freeLifoAlloc);
+            freeLifoAlloc.transferFrom(&rt->gc.blocksToFreeAfterSweeping);
 
             AutoUnlockGC unlock(lock);
             rt->gc.sweepBackgroundThings(zones, freeLifoAlloc, BackgroundThread);
         }
 
         bool shrinking = shrinkFlag;
         shrinkFlag = false;
         rt->gc.expireChunksAndArenas(shrinking, lock);
@@ -5388,17 +5396,17 @@ GCRuntime::endSweepingZoneGroup()
 
     /* Start background thread to sweep zones if required. */
     ZoneList zones;
     for (GCZoneGroupIter zone(rt); !zone.done(); zone.next())
         zones.append(zone);
     if (sweepOnBackgroundThread)
         queueZonesForBackgroundSweep(zones);
     else
-        sweepBackgroundThings(zones, freeLifoAlloc, MainThread);
+        sweepBackgroundThings(zones, blocksToFreeAfterSweeping, MainThread);
 
     /* Reset the list of arenas marked as being allocated during sweep phase. */
     while (Arena* arena = arenasAllocatedDuringSweep) {
         arenasAllocatedDuringSweep = arena->getNextAllocDuringSweep();
         arena->unsetAllocDuringSweep();
     }
 }
 
@@ -5897,17 +5905,17 @@ GCRuntime::resetIncrementalGC(const char
             ResetGrayList(c);
 
         for (GCZonesIter zone(rt); !zone.done(); zone.next()) {
             MOZ_ASSERT(zone->isGCMarking());
             zone->setNeedsIncrementalBarrier(false, Zone::UpdateJit);
             zone->setGCState(Zone::NoGC);
         }
 
-        freeLifoAlloc.freeAll();
+        blocksToFreeAfterSweeping.freeAll();
 
         incrementalState = NO_INCREMENTAL;
 
         MOZ_ASSERT(!marker.shouldCheckCompartments());
 
         break;
       }
 
@@ -6738,16 +6746,18 @@ GCRuntime::minorGCImpl(JS::gcreason::Rea
         return;
 
     minorGCTriggerReason = JS::gcreason::NO_REASON;
     TraceLoggerThread* logger = TraceLoggerForMainThread(rt);
     AutoTraceLog logMinorGC(logger, TraceLogger_MinorGC);
     nursery.collect(rt, reason, pretenureGroups);
     MOZ_ASSERT(nursery.isEmpty());
 
+    blocksToFreeAfterMinorGC.freeAll();
+
     AutoLockGC lock(rt);
     for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next())
         maybeAllocTriggerZoneGC(zone, lock);
 }
 
 // Alternate to the runtime-taking form that allows marking object groups as
 // needing pretenuring.
 void
@@ -7092,22 +7102,16 @@ void PreventGCDuringInteractiveDebug()
     TlsPerThreadData.get()->suppressGC++;
 }
 
 #endif
 
 void
 js::ReleaseAllJITCode(FreeOp* fop)
 {
-    /*
-     * Scripts can entrain nursery things, inserting references to the script
-     * into the store buffer. Clear the store buffer before discarding scripts.
-     */
-    fop->runtime()->gc.evictNursery();
-
     for (ZonesIter zone(fop->runtime(), SkipAtoms); !zone.done(); zone.next()) {
         zone->setPreservingCode(false);
         zone->discardJitCode(fop);
     }
 }
 
 void
 js::PurgeJITCaches(Zone* zone)
--- a/js/src/old-configure.in
+++ b/js/src/old-configure.in
@@ -836,17 +836,17 @@ dnl System overrides of the defaults for
 dnl ========================================================
 
 case "$target" in
 *-darwin*)
     MKSHLIB='$(CXX) $(CXXFLAGS) $(DSO_PIC_CFLAGS) $(DSO_LDOPTS) -o $@'
     MKCSHLIB='$(CC) $(CFLAGS) $(DSO_PIC_CFLAGS) $(DSO_LDOPTS) -o $@'
     MOZ_OPTIMIZE_FLAGS="-O3 -fno-stack-protector"
     CFLAGS="$CFLAGS -fno-common"
-    CXXFLAGS="$CXXFLAGS -fno-common"
+    CXXFLAGS="$CXXFLAGS -fno-common -stdlib=libc++"
     DLL_SUFFIX=".dylib"
     DSO_LDOPTS=''
     STRIP="$STRIP -x -S"
     LDFLAGS="$LDFLAGS -lobjc"
     # The ExceptionHandling framework is needed for Objective-C exception
     # logging code in nsObjCExceptions.h. Currently we only use that in debug
     # builds.
     _SAVE_LDFLAGS=$LDFLAGS
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -154,16 +154,17 @@ struct ShellRuntime
     bool isWorker;
     double timeoutInterval;
     Atomic<bool> serviceInterrupt;
     Atomic<bool> haveInterruptFunc;
     JS::PersistentRootedValue interruptFunc;
     bool lastWarningEnabled;
     JS::PersistentRootedValue lastWarning;
 #ifdef SPIDERMONKEY_PROMISE
+    JS::PersistentRootedValue promiseRejectionTrackerCallback;
     JS::PersistentRooted<JobQueue> jobQueue;
 #endif // SPIDERMONKEY_PROMISE
 
     /*
      * Watchdog thread state.
      */
     PRLock* watchdogLock;
     PRCondVar* watchdogWakeup;
@@ -306,16 +307,19 @@ extern JS_EXPORT_API(void)   add_history
 ShellRuntime::ShellRuntime(JSRuntime* rt)
   : isWorker(false),
     timeoutInterval(-1.0),
     serviceInterrupt(false),
     haveInterruptFunc(false),
     interruptFunc(rt, NullValue()),
     lastWarningEnabled(false),
     lastWarning(rt, NullValue()),
+#ifdef SPIDERMONKEY_PROMISE
+    promiseRejectionTrackerCallback(rt, NullValue()),
+#endif // SPIDERMONKEY_PROMISE
     watchdogLock(nullptr),
     watchdogWakeup(nullptr),
     watchdogThread(nullptr),
     watchdogHasTimeout(false),
     watchdogTimeout(0),
     sleepWakeup(nullptr),
     exitCode(0),
     quitting(false),
@@ -627,26 +631,29 @@ RunModule(JSContext* cx, const char* fil
     if (!JS_CallFunction(cx, loaderObj, importFun, args, &value)) {
         sr->exitCode = EXITCODE_RUNTIME_ERROR;
         return;
     }
 }
 
 #ifdef SPIDERMONKEY_PROMISE
 static bool
-ShellEnqueuePromiseJobCallback(JSContext* cx, JS::HandleObject job, void* data)
+ShellEnqueuePromiseJobCallback(JSContext* cx, JS::HandleObject job, JS::HandleObject allocationSite,
+                               void* data)
 {
     ShellRuntime* sr = GetShellRuntime(cx);
     MOZ_ASSERT(job);
     return sr->jobQueue.append(job);
 }
+#endif // SPIDERMONKEY_PROMISE
 
 static bool
 DrainJobQueue(JSContext* cx)
 {
+#ifdef SPIDERMONKEY_PROMISE
     ShellRuntime* sr = GetShellRuntime(cx);
     if (sr->quitting)
         return true;
     RootedObject job(cx);
     JS::HandleValueArray args(JS::HandleValueArray::empty());
     RootedValue rval(cx);
     // Execute jobs in a loop until we've reached the end of the queue.
     // Since executing a job can trigger enqueuing of additional jobs,
@@ -654,32 +661,74 @@ DrainJobQueue(JSContext* cx)
     for (size_t i = 0; i < sr->jobQueue.length(); i++) {
         job = sr->jobQueue[i];
         AutoCompartment ac(cx, job);
         if (!JS::Call(cx, UndefinedHandleValue, job, args, &rval))
             JS_ReportPendingException(cx);
         sr->jobQueue[i].set(nullptr);
     }
     sr->jobQueue.clear();
+#endif // SPIDERMONKEY_PROMISE
     return true;
 }
 
 static bool
 DrainJobQueue(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     if (!DrainJobQueue(cx))
         return false;
     args.rval().setUndefined();
     return true;
 }
+
+#ifdef SPIDERMONKEY_PROMISE
+static void
+ForwardingPromiseRejectionTrackerCallback(JSContext* cx, JS::HandleObject promise,
+                                          PromiseRejectionHandlingState state, void* data)
+{
+    RootedValue callback(cx, GetShellRuntime(cx)->promiseRejectionTrackerCallback);
+    if (callback.isNull()) {
+        return;
+    }
+
+    FixedInvokeArgs<2> args(cx);
+    args[0].setObject(*promise);
+    args[1].setInt32(static_cast<int32_t>(state));
+
+    RootedValue rval(cx);
+    if (!Call(cx, callback, UndefinedHandleValue, args, &rval))
+        JS_ClearPendingException(cx);
+}
 #endif // SPIDERMONKEY_PROMISE
 
 static bool
+SetPromiseRejectionTrackerCallback(JSContext* cx, unsigned argc, Value* vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+
+#ifdef SPIDERMONKEY_PROMISE
+    if (!IsCallable(args.get(0))) {
+        JS_ReportError(cx,
+                       "setPromiseRejectionTrackerCallback expects a function as its sole "
+                       "argument");
+        return false;
+    }
+
+    GetShellRuntime(cx)->promiseRejectionTrackerCallback = args[0];
+    JS::SetPromiseRejectionTrackerCallback(cx->runtime(),
+                                           ForwardingPromiseRejectionTrackerCallback);
+
+#endif // SPIDERMONKEY_PROMISE
+    args.rval().setUndefined();
+    return true;
+}
+
+static bool
 EvalAndPrint(JSContext* cx, const char* bytes, size_t length,
              int lineno, bool compileOnly)
 {
     // Eval.
     JS::CompileOptions options(cx);
     options.setIntroductionType("js shell interactive")
            .setUTF8(true)
            .setIsRunOnce(true)
@@ -766,19 +815,17 @@ ReadEvalPrintLoop(JSContext* cx, FILE* i
         if (JS::ForceLexicalInitialization(cx, globalLexical) && gErrFile->isOpen()) {
             fputs("Warning: According to the standard, after the above exception,\n"
                   "Warning: the global bindings should be permanently uninitialized.\n"
                   "Warning: We have non-standard-ly initialized them to `undefined`"
                   "for you.\nWarning: This nicety only happens in the JS shell.\n",
                   stderr);
         }
 
-#ifdef SPIDERMONKEY_PROMISE
         DrainJobQueue(cx);
-#endif // SPIDERMONKEY_PROMISE
 
     } while (!hitEOF && !sr->quitting);
 
     if (gOutFile->isOpen())
         fprintf(gOutFile->fp, "\n");
 }
 
 enum FileKind
@@ -921,17 +968,16 @@ CreateMappedArrayBuffer(JSContext* cx, u
     RootedObject obj(cx, JS_NewMappedArrayBufferWithContents(cx, size, contents));
     if (!obj)
         return false;
 
     args.rval().setObject(*obj);
     return true;
 }
 
-#ifdef SPIDERMONKEY_PROMISE
 static bool
 AddPromiseReactions(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     if (args.length() != 3) {
         JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr,
                              args.length() < 3 ? JSSMSG_NOT_ENOUGH_ARGS : JSSMSG_TOO_MANY_ARGS,
@@ -960,17 +1006,16 @@ AddPromiseReactions(JSContext* cx, unsig
     if (!onResolve || !onResolve->is<JSFunction>() || !onReject || !onReject->is<JSFunction>()) {
         JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr,
                              JSSMSG_INVALID_ARGS, "addPromiseReactions");
         return false;
     }
 
     return JS::AddPromiseReactions(cx, promise, onResolve, onReject);
 }
-#endif // SPIDERMONKEY_PROMISE
 
 static bool
 Options(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     JS::RuntimeOptions oldRuntimeOptions = JS::RuntimeOptionsRef(cx);
     for (unsigned i = 0; i < args.length(); i++) {
@@ -5540,21 +5585,19 @@ static const JSFunctionSpecWithHelp shel
 "         principals of ~0 subsumes all other principals. The absence of a\n"
 "         principal is treated as if its bits were 0xffff, for subsumption\n"
 "         purposes. If this property is omitted, supply no principal."),
 
     JS_FN_HELP("createMappedArrayBuffer", CreateMappedArrayBuffer, 1, 0,
 "createMappedArrayBuffer(filename, [offset, [size]])",
 "  Create an array buffer that mmaps the given file."),
 
-#ifdef SPIDERMONKEY_PROMISE
     JS_FN_HELP("addPromiseReactions", AddPromiseReactions, 3, 0,
 "addPromiseReactions(promise, onResolve, onReject)",
 "  Calls the JS::AddPromiseReactions JSAPI function with the given arguments."),
-#endif // SPIDERMONKEY_PROMISE
 
     JS_FN_HELP("getMaxArgs", GetMaxArgs, 0, 0,
 "getMaxArgs()",
 "  Return the maximum number of supported args for a call."),
 
     JS_FN_HELP("objectEmulatingUndefined", ObjectEmulatingUndefined, 0, 0,
 "objectEmulatingUndefined()",
 "  Return a new object obj for which typeof obj === \"undefined\", obj == null\n"
@@ -5619,22 +5662,25 @@ static const JSFunctionSpecWithHelp shel
 "{ eval }: Apply JS::Evaluate to |params.eval|.\n"
 "\n"
 "The return value is an array of strings, with one element for each\n"
 "JavaScript invocation that occurred as a result of the given\n"
 "operation. Each element is the name of the function invoked, or the\n"
 "string 'eval:FILENAME' if the code was invoked by 'eval' or something\n"
 "similar.\n"),
 
-#ifdef SPIDERMONKEY_PROMISE
     JS_FN_HELP("drainJobQueue", DrainJobQueue, 0, 0,
 "drainJobQueue()",
 "Take jobs from the shell's job queue in FIFO order and run them until the\n"
 "queue is empty.\n"),
-#endif // SPIDERMONKEY_PROMISE
+
+    JS_FN_HELP("setPromiseRejectionTrackerCallback", SetPromiseRejectionTrackerCallback, 1, 0,
+"setPromiseRejectionTrackerCallback()",
+"Sets the callback to be invoked whenever a Promise rejection is unhandled\n"
+"or a previously-unhandled rejection becomes handled."),
 
     JS_FS_HELP_END
 };
 
 static const JSFunctionSpecWithHelp fuzzing_unsafe_functions[] = {
     JS_FN_HELP("clone", Clone, 1, 0,
 "clone(fun[, scope])",
 "  Clone function object."),
@@ -6735,19 +6781,17 @@ ProcessArgs(JSContext* cx, OptionParser*
 
     /* The |script| argument is processed after all options. */
     if (const char* path = op->getStringArg("script")) {
         Process(cx, path, false);
         if (sr->exitCode)
             return sr->exitCode;
     }
 
-#ifdef SPIDERMONKEY_PROMISE
     DrainJobQueue(cx);
-#endif // SPIDERMONKEY_PROMISE
 
     if (op->getBoolOption('i'))
         Process(cx, nullptr, true);
 
     return sr->exitCode ? sr->exitCode : EXIT_SUCCESS;
 }
 
 static bool
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/Promise/promise-rejection-tracking.js
@@ -0,0 +1,36 @@
+// |reftest| skip-if(!xulRuntime.shell) -- needs setPromiseRejectionTrackerCallback
+
+if (!this.Promise) {
+    this.reportCompare && reportCompare(true,true);
+    quit(0);
+}
+
+const UNHANDLED = 0;
+const HANDLED   = 1;
+
+let rejections = new Map();
+function rejectionTracker(promise, state) {
+    rejections.set(promise, state);
+}
+setPromiseRejectionTrackerCallback(rejectionTracker);
+
+// Unhandled rejections are tracked.
+let reject;
+let p = new Promise((res_, rej_) => (reject = rej_));
+assertEq(rejections.has(p), false);
+reject('reason');
+assertEq(rejections.get(p), UNHANDLED);
+// Later handling updates the tracking.
+p.then(_=>_, _=>_);
+assertEq(rejections.get(p), HANDLED);
+
+rejections.clear();
+
+// Handled rejections aren't tracked at all.
+p = new Promise((res_, rej_) => (reject = rej_));
+assertEq(rejections.has(p), false);
+p.then(_=>_, _=>_);
+reject('reason');
+assertEq(rejections.has(p), false);
+
+this.reportCompare && reportCompare(true,true);
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -2262,19 +2262,16 @@ AppendAndInvalidateScript(JSContext* cx,
 
 static bool
 UpdateExecutionObservabilityOfScriptsInZone(JSContext* cx, Zone* zone,
                                             const Debugger::ExecutionObservableSet& obs,
                                             Debugger::IsObserving observing)
 {
     using namespace js::jit;
 
-    // See note in js::ReleaseAllJITCode.
-    cx->runtime()->gc.evictNursery();
-
     AutoSuppressProfilerSampling suppressProfilerSampling(cx);
 
     JSRuntime* rt = cx->runtime();
     FreeOp* fop = cx->runtime()->defaultFreeOp();
 
     Vector<JSScript*> scripts(cx);
 
     // Iterate through observable scripts, invalidating their Ion scripts and
@@ -7745,25 +7742,27 @@ DebuggerObject_checkThis(JSContext* cx, 
     if (!obj)                                                                  \
         return false;                                                          \
     Debugger* dbg = Debugger::fromChildJSObject(obj);                          \
     obj = (JSObject*) obj->as<NativeObject>().getPrivate();                    \
     MOZ_ASSERT(obj)
 
 #define THIS_DEBUGOBJECT_PROMISE(cx, argc, vp, fnname, args, obj)                   \
    THIS_DEBUGOBJECT_REFERENT(cx, argc, vp, fnname, args, obj);                      \
+   obj = CheckedUnwrap(obj);                                                        \
    if (!obj->is<PromiseObject>()) {                                                 \
        JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_NOT_EXPECTED_TYPE,  \
                             "Debugger", "Promise", obj->getClass()->name);          \
        return false;                                                                \
    }                                                                                \
    Rooted<PromiseObject*> promise(cx, &obj->as<PromiseObject>());
 
 #define THIS_DEBUGOBJECT_OWNER_PROMISE(cx, argc, vp, fnname, args, dbg, obj)        \
    THIS_DEBUGOBJECT_OWNER_REFERENT(cx, argc, vp, fnname, args, dbg, obj);           \
+   obj = CheckedUnwrap(obj);                                                        \
    if (!obj->is<PromiseObject>()) {                                                 \
        JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_NOT_EXPECTED_TYPE,  \
                             "Debugger", "Promise", obj->getClass()->name);          \
        return false;                                                                \
    }                                                                                \
    Rooted<PromiseObject*> promise(cx, &obj->as<PromiseObject>());
 
 static bool
@@ -8054,16 +8053,17 @@ null(CallArgs& args)
 }
 
 #ifdef SPIDERMONKEY_PROMISE
 static bool
 DebuggerObject_getIsPromise(JSContext* cx, unsigned argc, Value* vp)
 {
     THIS_DEBUGOBJECT_REFERENT(cx, argc, vp, "get isPromise", args, refobj);
 
+    refobj = CheckedUnwrap(refobj);
     args.rval().setBoolean(refobj->is<PromiseObject>());
     return true;
 }
 
 static bool
 DebuggerObject_getPromiseState(JSContext* cx, unsigned argc, Value* vp)
 {
     THIS_DEBUGOBJECT_OWNER_PROMISE(cx, argc, vp, "get promiseState", args, dbg, refobj);
@@ -8159,17 +8159,17 @@ DebuggerObject_getPromiseResolutionSite(
     return true;
 }
 
 static bool
 DebuggerObject_getPromiseID(JSContext* cx, unsigned argc, Value* vp)
 {
     THIS_DEBUGOBJECT_PROMISE(cx, argc, vp, "get promiseID", args, refobj);
 
-    args.rval().setNumber(promise->getID());
+    args.rval().setNumber(double(promise->getID()));
     return true;
 }
 
 static bool
 DebuggerObject_getPromiseDependentPromises(JSContext* cx, unsigned argc, Value* vp)
 {
     THIS_DEBUGOBJECT_OWNER_PROMISE(cx, argc, vp, "get promiseDependentPromises", args, dbg, refobj);
 
@@ -8798,16 +8798,19 @@ DebuggerObject::initClass(JSContext* cx,
 {
     Rooted<GlobalObject*> global(cx, &obj->as<GlobalObject>());
     RootedObject objProto(cx, global->getOrCreateObjectPrototype(cx));
 
     RootedNativeObject objectProto(cx, InitClass(cx, debugCtor, objProto, &class_,
                                                  DebuggerObject_construct, 0, properties_,
                                                  methods_, nullptr, nullptr));
 
+    if (!objectProto)
+        return nullptr;
+
 #ifdef SPIDERMONKEY_PROMISE
     if (!DefinePropertiesAndFunctions(cx, objectProto, promiseProperties_, nullptr))
         return nullptr;
 #endif // SPIDERMONKEY_PROMISE
 
     return objectProto;
 }
 
--- a/js/src/vm/Debugger.h
+++ b/js/src/vm/Debugger.h
@@ -205,20 +205,20 @@ class AutoSuppressDebuggeeNoExecuteCheck
 
     ~AutoSuppressDebuggeeNoExecuteChecks() {
         MOZ_ASSERT(!*stack_);
         *stack_ = prev_;
     }
 };
 
 /*
- * Env is the type of what ES5 calls "lexical environments" (runtime
- * activations of lexical scopes). This is currently just JSObject, and is
- * implemented by Call, Block, With, and DeclEnv objects, among others--but
- * environments and objects are really two different concepts.
+ * Env is the type of what ES5 calls "lexical environments" (runtime activations
+ * of lexical scopes). This is currently just JSObject, and is implemented by
+ * CallObject, ClonedBlockObject, DynamicWithObject, and DeclEnvObject, among
+ * others--but environments and objects are really two different concepts.
  */
 typedef JSObject Env;
 
 // Either a real JSScript or synthesized.
 //
 // If synthesized, the referent is one of the following:
 //
 //   1. A WasmModuleObject, denoting a synthesized toplevel wasm module
--- a/js/src/vm/NativeObject.cpp
+++ b/js/src/vm/NativeObject.cpp
@@ -1819,16 +1819,24 @@ js::NativeGetExistingProperty(JSContext*
  * access is "property-detecting" -- that is, if we shouldn't warn about it
  * even if no such property is found and strict warnings are enabled.
  */
 static bool
 Detecting(JSContext* cx, JSScript* script, jsbytecode* pc)
 {
     MOZ_ASSERT(script->containsPC(pc));
 
+    // Skip jump target opcodes.
+    while (pc < script->codeEnd() && BytecodeIsJumpTarget(JSOp(*pc)))
+        pc = GetNextPc(pc);
+
+    MOZ_ASSERT(script->containsPC(pc));
+    if (pc >= script->codeEnd())
+        return false;
+
     // General case: a branch or equality op follows the access.
     JSOp op = JSOp(*pc);
     if (CodeSpec[op].format & JOF_DETECTING)
         return true;
 
     jsbytecode* endpc = script->codeEnd();
 
     if (op == JSOP_NULL) {
--- a/js/src/vm/Runtime.cpp
+++ b/js/src/vm/Runtime.cpp
@@ -36,16 +36,17 @@
 #include "jsnativestack.h"
 #include "jsobj.h"
 #include "jsscript.h"
 #include "jswatchpoint.h"
 #include "jswin.h"
 #include "jswrapper.h"
 
 #include "asmjs/WasmSignalHandlers.h"
+#include "builtin/Promise.h"
 #include "jit/arm/Simulator-arm.h"
 #include "jit/arm64/vixl/Simulator-vixl.h"
 #include "jit/JitCompartment.h"
 #include "jit/mips32/Simulator-mips32.h"
 #include "jit/mips64/Simulator-mips64.h"
 #include "jit/PcScriptCache.h"
 #include "js/Date.h"
 #include "js/MemoryMetrics.h"
@@ -152,16 +153,18 @@ JSRuntime::JSRuntime(JSRuntime* parentRu
 #endif
     interrupt_(false),
     telemetryCallback(nullptr),
     handlingSegFault(false),
     handlingJitInterrupt_(false),
     interruptCallback(nullptr),
     enqueuePromiseJobCallback(nullptr),
     enqueuePromiseJobCallbackData(nullptr),
+    promiseRejectionTrackerCallback(nullptr),
+    promiseRejectionTrackerCallbackData(nullptr),
 #ifdef DEBUG
     exclusiveAccessOwner(nullptr),
     mainThreadHasExclusiveAccess(false),
 #endif
     numExclusiveThreads(0),
     numCompartments(0),
     localeCallbacks(nullptr),
     defaultLocale(nullptr),
@@ -769,23 +772,50 @@ FreeOp::~FreeOp()
     for (size_t i = 0; i < freeLaterList.length(); i++)
         free_(freeLaterList[i]);
 
     if (!jitPoisonRanges.empty())
         jit::ExecutableAllocator::poisonCode(runtime(), jitPoisonRanges);
 }
 
 bool
-JSRuntime::enqueuePromiseJob(JSContext* cx, HandleFunction job)
+JSRuntime::enqueuePromiseJob(JSContext* cx, HandleFunction job, HandleObject promise)
 {
     MOZ_ASSERT(cx->runtime()->enqueuePromiseJobCallback,
                "Must set a callback using JS_SetEnqeueuPromiseJobCallback before using Promises");
 
     void* data = cx->runtime()->enqueuePromiseJobCallbackData;
-    return cx->runtime()->enqueuePromiseJobCallback(cx, job, data);
+    RootedObject allocationSite(cx);
+    if (promise)
+        allocationSite = JS::GetPromiseAllocationSite(promise);
+    return cx->runtime()->enqueuePromiseJobCallback(cx, job, allocationSite, data);
+}
+
+void
+JSRuntime::addUnhandledRejectedPromise(JSContext* cx, js::HandleObject promise)
+{
+    MOZ_ASSERT(promise->is<PromiseObject>());
+    if (!cx->runtime()->promiseRejectionTrackerCallback)
+        return;
+
+    void* data = cx->runtime()->promiseRejectionTrackerCallbackData;
+    cx->runtime()->promiseRejectionTrackerCallback(cx, promise,
+                                                   PromiseRejectionHandlingState::Unhandled, data);
+}
+
+void
+JSRuntime::removeUnhandledRejectedPromise(JSContext* cx, js::HandleObject promise)
+{
+    MOZ_ASSERT(promise->is<PromiseObject>());
+    if (!cx->runtime()->promiseRejectionTrackerCallback)
+        return;
+
+    void* data = cx->runtime()->promiseRejectionTrackerCallbackData;
+    cx->runtime()->promiseRejectionTrackerCallback(cx, promise,
+                                                   PromiseRejectionHandlingState::Handled, data);
 }
 
 void
 JSRuntime::updateMallocCounter(size_t nbytes)
 {
     updateMallocCounter(nullptr, nbytes);
 }
 
--- a/js/src/vm/Runtime.h
+++ b/js/src/vm/Runtime.h
@@ -932,16 +932,19 @@ struct JSRuntime : public JS::shadow::Ru
         return handlingJitInterrupt_;
     }
 
     JSInterruptCallback interruptCallback;
 
     JSEnqueuePromiseJobCallback enqueuePromiseJobCallback;
     void* enqueuePromiseJobCallbackData;
 
+    JSPromiseRejectionTrackerCallback promiseRejectionTrackerCallback;
+    void* promiseRejectionTrackerCallbackData;
+
 #ifdef DEBUG
     void assertCanLock(js::RuntimeLock which);
 #else
     void assertCanLock(js::RuntimeLock which) {}
 #endif
 
   private:
     /*
@@ -1040,17 +1043,19 @@ struct JSRuntime : public JS::shadow::Ru
     }
     bool hasJitRuntime() const {
         return !!jitRuntime_;
     }
     js::InterpreterStack& interpreterStack() {
         return interpreterStack_;
     }
 
-    bool enqueuePromiseJob(JSContext* cx, js::HandleFunction job);
+    bool enqueuePromiseJob(JSContext* cx, js::HandleFunction job, js::HandleObject promise);
+    void addUnhandledRejectedPromise(JSContext* cx, js::HandleObject promise);
+    void removeUnhandledRejectedPromise(JSContext* cx, js::HandleObject promise);
 
     //-------------------------------------------------------------------------
     // Self-hosting support
     //-------------------------------------------------------------------------
 
     bool initSelfHosting(JSContext* cx);
     void finishSelfHosting();
     void markSelfHostingGlobal(JSTracer* trc);
--- a/js/src/vm/SelfHosting.cpp
+++ b/js/src/vm/SelfHosting.cpp
@@ -1780,30 +1780,98 @@ js::ReportIncompatibleSelfHostedMethod(J
     }
 
     MOZ_ASSERT_UNREACHABLE("How did we not find a useful self-hosted frame?");
     return false;
 }
 
 // ES6, 25.4.1.6.
 static bool
-intrinsic_EnqueuePromiseJob(JSContext* cx, unsigned argc, Value* vp)
+intrinsic_EnqueuePromiseReactionJob(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
-    MOZ_ASSERT(args.length() == 1);
-    MOZ_ASSERT(args[0].isObject());
-    MOZ_ASSERT(args[0].toObject().is<JSFunction>());
-
-    RootedFunction job(cx, &args[0].toObject().as<JSFunction>());
-    if (!cx->runtime()->enqueuePromiseJob(cx, job))
+    MOZ_ASSERT(args.length() == 2);
+    MOZ_ASSERT(args[0].toObject().as<NativeObject>().getDenseInitializedLength() == 4);
+
+    // When using JS::AddPromiseReactions, no actual promise is created, so we
+    // might not have one here.
+    RootedObject promise(cx);
+    if (args[1].isObject())
+        promise = UncheckedUnwrap(&args[1].toObject());
+
+#ifdef DEBUG
+    MOZ_ASSERT_IF(promise, promise->is<PromiseObject>());
+    RootedNativeObject jobArgs(cx, &args[0].toObject().as<NativeObject>());
+    MOZ_ASSERT((jobArgs->getDenseElement(0).isNumber() &&
+                (jobArgs->getDenseElement(0).toNumber() == PROMISE_HANDLER_IDENTITY ||
+                 jobArgs->getDenseElement(0).toNumber() == PROMISE_HANDLER_THROWER)) ||
+               jobArgs->getDenseElement(0).toObject().isCallable());
+    MOZ_ASSERT(jobArgs->getDenseElement(2).toObject().isCallable());
+    MOZ_ASSERT(jobArgs->getDenseElement(3).toObject().isCallable());
+#endif
+
+    RootedAtom funName(cx, cx->names().empty);
+    RootedFunction job(cx, NewNativeFunction(cx, PromiseReactionJob, 0, funName,
+                                             gc::AllocKind::FUNCTION_EXTENDED));
+    if (!job)
+        return false;
+
+    job->setExtendedSlot(0, args[0]);
+    if (!cx->runtime()->enqueuePromiseJob(cx, job, promise))
         return false;
     args.rval().setUndefined();
     return true;
 }
 
+// ES6, 25.4.1.6.
+static bool
+intrinsic_EnqueuePromiseResolveThenableJob(JSContext* cx, unsigned argc, Value* vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+
+#ifdef DEBUG
+    MOZ_ASSERT(args.length() == 2);
+    MOZ_ASSERT(UncheckedUnwrap(&args[1].toObject())->is<PromiseObject>());
+    RootedNativeObject jobArgs(cx, &args[0].toObject().as<NativeObject>());
+    MOZ_ASSERT(jobArgs->getDenseInitializedLength() == 3);
+    MOZ_ASSERT(jobArgs->getDenseElement(0).toObject().isCallable());
+    MOZ_ASSERT(jobArgs->getDenseElement(1).isObject());
+    MOZ_ASSERT(UncheckedUnwrap(&jobArgs->getDenseElement(2).toObject())->is<PromiseObject>());
+#endif
+
+    RootedAtom funName(cx, cx->names().empty);
+    RootedFunction job(cx, NewNativeFunction(cx, PromiseResolveThenableJob, 0, funName,
+                                             gc::AllocKind::FUNCTION_EXTENDED));
+    if (!job)
+        return false;
+
+    job->setExtendedSlot(0, args[0]);
+    RootedObject promise(cx, CheckedUnwrap(&args[1].toObject()));
+    if (!cx->runtime()->enqueuePromiseJob(cx, job, promise))
+        return false;
+    args.rval().setUndefined();
+    return true;
+}
+
+// ES2016, February 12 draft, 25.4.1.9.
+static bool
+intrinsic_HostPromiseRejectionTracker(JSContext* cx, unsigned argc, Value* vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+    MOZ_ASSERT(args.length() == 2);
+    MOZ_ASSERT(args[0].toObject().is<PromiseObject>());
+
+    Rooted<PromiseObject*> promise(cx, &args[0].toObject().as<PromiseObject>());
+    mozilla::DebugOnly<bool> isHandled = args[1].toBoolean();
+    MOZ_ASSERT(isHandled, "HostPromiseRejectionTracker intrinsic currently only marks as handled");
+    cx->runtime()->removeUnhandledRejectedPromise(cx, promise);
+    args.rval().setUndefined();
+    return true;
+}
+
 /**
  * Returns the default locale as a well-formed, but not necessarily canonicalized,
  * BCP-47 language tag.
  */
 static bool
 intrinsic_RuntimeDefaultLocale(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
@@ -2152,46 +2220,22 @@ intrinsic_ModuleNamespaceExports(JSConte
  * resulting state has been set on the promise, and it's up to the debugger
  * to act on this signal in whichever way it wants.
  */
 static bool
 intrinsic_onPromiseSettled(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     MOZ_ASSERT(args.length() == 1);
-    RootedObject promise(cx, &args[0].toObject());
-    JS::dbg::onPromiseSettled(cx, promise);
+    Rooted<PromiseObject*> promise(cx, &args[0].toObject().as<PromiseObject>());
+    promise->onSettled(cx);
     args.rval().setUndefined();
     return true;
 }
 
-/**
- * Intrinsic used to tell the debugger about settled promises.
- *
- * This is invoked both when resolving and rejecting promises, after the
- * resulting state has been set on the promise, and it's up to the debugger
- * to act on this signal in whichever way it wants.
- */
-static bool
-intrinsic_captureCurrentStack(JSContext* cx, unsigned argc, Value* vp)
-{
-    CallArgs args = CallArgsFromVp(argc, vp);
-    MOZ_ASSERT(args.length() < 2);
-    unsigned maxFrameCount = 0;
-    if (args.length() == 1)
-        maxFrameCount = args[0].toInt32();
-
-    RootedObject stack(cx);
-    if (!JS::CaptureCurrentStack(cx, &stack, maxFrameCount))
-        return false;
-
-    args.rval().setObject(*stack);
-    return true;
-}
-
 // The self-hosting global isn't initialized with the normal set of builtins.
 // Instead, individual C++-implemented functions that're required by
 // self-hosted code are defined as global functions. Accessing these
 // functions via a content compartment's builtins would be unsafe, because
 // content script might have changed the builtins' prototypes' members.
 // Installing the whole set of builtins in the self-hosting compartment, OTOH,
 // would be wasteful: it increases memory usage and initialization time for
 // self-hosting compartment.
@@ -2442,17 +2486,19 @@ static const JSFunctionSpec intrinsic_fu
           CallNonGenericSelfhostedMethod<Is<StarGeneratorObject>>, 2, 0),
 
     JS_FN("IsWeakSet", intrinsic_IsInstanceOfBuiltin<WeakSetObject>, 1,0),
     JS_FN("CallWeakSetMethodIfWrapped",
           CallNonGenericSelfhostedMethod<Is<WeakSetObject>>, 2, 0),
 
     JS_FN("IsPromise",                      intrinsic_IsInstanceOfBuiltin<PromiseObject>, 1,0),
     JS_FN("IsWrappedPromise",               intrinsic_IsWrappedPromiseObject,     1, 0),
-    JS_FN("_EnqueuePromiseJob",             intrinsic_EnqueuePromiseJob,          1, 0),
+    JS_FN("_EnqueuePromiseReactionJob",     intrinsic_EnqueuePromiseReactionJob,  2, 0),
+    JS_FN("_EnqueuePromiseResolveThenableJob", intrinsic_EnqueuePromiseResolveThenableJob, 2, 0),
+    JS_FN("HostPromiseRejectionTracker",    intrinsic_HostPromiseRejectionTracker,2, 0),
     JS_FN("_GetOriginalPromiseConstructor", intrinsic_OriginalPromiseConstructor, 0, 0),
     JS_FN("RejectUnwrappedPromise",         intrinsic_RejectUnwrappedPromise,     2, 0),
     JS_FN("CallPromiseMethodIfWrapped",
           CallNonGenericSelfhostedMethod<Is<PromiseObject>>,      2,0),
 
     // See builtin/TypedObject.h for descriptors of the typedobj functions.
     JS_FN("NewOpaqueTypedObject",           js::NewOpaqueTypedObject, 1, 0),
     JS_FN("NewDerivedTypedObject",          js::NewDerivedTypedObject, 3, 0),
@@ -2555,17 +2601,16 @@ static const JSFunctionSpec intrinsic_fu
     JS_FN("SetModuleEvaluated", intrinsic_SetModuleEvaluated, 1, 0),
     JS_FN("EvaluateModule", intrinsic_EvaluateModule, 1, 0),
     JS_FN("IsModuleNamespace", intrinsic_IsInstanceOfBuiltin<ModuleNamespaceObject>, 1, 0),
     JS_FN("NewModuleNamespace", intrinsic_NewModuleNamespace, 2, 0),
     JS_FN("AddModuleNamespaceBinding", intrinsic_AddModuleNamespaceBinding, 4, 0),
     JS_FN("ModuleNamespaceExports", intrinsic_ModuleNamespaceExports, 1, 0),
 
     JS_FN("_dbg_onPromiseSettled", intrinsic_onPromiseSettled, 1, 0),
-    JS_FN("_dbg_captureCurrentStack", intrinsic_captureCurrentStack, 1, 0),
 
     JS_FS_END
 };
 
 void
 js::FillSelfHostingCompileOptions(CompileOptions& options)
 {
     /*
--- a/js/src/vm/String.cpp
+++ b/js/src/vm/String.cpp
@@ -779,27 +779,29 @@ bool
 StaticStrings::init(JSContext* cx)
 {
     AutoLockForExclusiveAccess lock(cx);
     AutoCompartment ac(cx, cx->runtime()->atomsCompartment(lock));
 
     static_assert(UNIT_STATIC_LIMIT - 1 <= JSString::MAX_LATIN1_CHAR,
                   "Unit strings must fit in Latin1Char.");
 
+    using Latin1Range = mozilla::Range<const Latin1Char>;
+
     for (uint32_t i = 0; i < UNIT_STATIC_LIMIT; i++) {
         Latin1Char buffer[] = { Latin1Char(i), '\0' };
-        JSFlatString* s = NewStringCopyN<NoGC>(cx, buffer, 1);
+        JSFlatString* s = NewInlineString<NoGC>(cx, Latin1Range(buffer, 1));
         if (!s)
             return false;
         unitStaticTable[i] = s->morphAtomizedStringIntoPermanentAtom();
     }
 
     for (uint32_t i = 0; i < NUM_SMALL_CHARS * NUM_SMALL_CHARS; i++) {
         Latin1Char buffer[] = { FROM_SMALL_CHAR(i >> 6), FROM_SMALL_CHAR(i & 0x3F), '\0' };
-        JSFlatString* s = NewStringCopyN<NoGC>(cx, buffer, 2);
+        JSFlatString* s = NewInlineString<NoGC>(cx, Latin1Range(buffer, 2));
         if (!s)
             return false;
         length2StaticTable[i] = s->morphAtomizedStringIntoPermanentAtom();
     }
 
     for (uint32_t i = 0; i < INT_STATIC_LIMIT; i++) {
         if (i < 10) {
             intStaticTable[i] = unitStaticTable[i + '0'];
@@ -807,17 +809,17 @@ StaticStrings::init(JSContext* cx)
             size_t index = ((size_t)TO_SMALL_CHAR((i / 10) + '0') << 6) +
                 TO_SMALL_CHAR((i % 10) + '0');
             intStaticTable[i] = length2StaticTable[index];
         } else {
             Latin1Char buffer[] = { Latin1Char('0' + (i / 100)),
                                     Latin1Char('0' + ((i / 10) % 10)),
                                     Latin1Char('0' + (i % 10)),
                                     '\0' };
-            JSFlatString* s = NewStringCopyN<NoGC>(cx, buffer, 3);
+            JSFlatString* s = NewInlineString<NoGC>(cx, Latin1Range(buffer, 3));
             if (!s)
                 return false;
             intStaticTable[i] = s->morphAtomizedStringIntoPermanentAtom();
         }
     }
 
     return true;
 }
@@ -1085,20 +1087,41 @@ NewInlineStringDeflated(ExclusiveContext
     for (size_t i = 0; i < len; i++) {
         MOZ_ASSERT(chars[i] <= JSString::MAX_LATIN1_CHAR);
         storage[i] = Latin1Char(chars[i]);
     }
     storage[len] = '\0';
     return str;
 }
 
+template <typename CharT>
+static MOZ_ALWAYS_INLINE JSFlatString*
+TryEmptyOrStaticString(ExclusiveContext* cx, const CharT* chars, size_t n)
+{
+    // Measurements on popular websites indicate empty strings are pretty common
+    // and most strings with length 1 or 2 are in the StaticStrings table. For
+    // length 3 strings that's only about 1%, so we check n <= 2.
+    if (n <= 2) {
+        if (n == 0)
+            return cx->emptyString();
+
+        if (JSFlatString* str = cx->staticStrings().lookup(chars, n))
+            return str;
+    }
+
+    return nullptr;
+}
+
 template <AllowGC allowGC>
 static JSFlatString*
 NewStringDeflated(ExclusiveContext* cx, const char16_t* s, size_t n)
 {
+    if (JSFlatString* str = TryEmptyOrStaticString(cx, s, n))
+        return str;
+
     if (JSInlineString::lengthFits<Latin1Char>(n))
         return NewInlineStringDeflated<allowGC>(cx, mozilla::Range<const char16_t>(s, n));
 
     ScopedJSFreePtr<Latin1Char> news(cx->pod_malloc<Latin1Char>(n + 1));
     if (!news)
         return nullptr;
 
     for (size_t i = 0; i < n; i++) {
@@ -1121,24 +1144,21 @@ NewStringDeflated(ExclusiveContext* cx, 
 {
     MOZ_CRASH("Shouldn't be called for Latin1 chars");
 }
 
 template <AllowGC allowGC, typename CharT>
 JSFlatString*
 js::NewStringDontDeflate(ExclusiveContext* cx, CharT* chars, size_t length)
 {
-    if (length == 1) {
-        char16_t c = chars[0];
-        if (StaticStrings::hasUnit(c)) {
-            // Free |chars| because we're taking possession of it, but it's no
-            // longer needed because we use the static string instead.
-            js_free(chars);
-            return cx->staticStrings().getUnit(c);
-        }
+    if (JSFlatString* str = TryEmptyOrStaticString(cx, chars, length)) {
+        // Free |chars| because we're taking possession of it, but it's no
+        // longer needed because we use the static string instead.
+        js_free(chars);
+        return str;
     }
 
     if (JSInlineString::lengthFits<CharT>(length)) {
         JSInlineString* str =
             NewInlineString<allowGC>(cx, mozilla::Range<const CharT>(chars, length));
         if (!str)
             return nullptr;
 
@@ -1161,24 +1181,16 @@ js::NewStringDontDeflate<CanGC>(Exclusiv
 template JSFlatString*
 js::NewStringDontDeflate<NoGC>(ExclusiveContext* cx, Latin1Char* chars, size_t length);
 
 template <AllowGC allowGC, typename CharT>
 JSFlatString*
 js::NewString(ExclusiveContext* cx, CharT* chars, size_t length)
 {
     if (IsSame<CharT, char16_t>::value && CanStoreCharsAsLatin1(chars, length)) {
-        if (length == 1) {
-            char16_t c = chars[0];
-            if (StaticStrings::hasUnit(c)) {
-                js_free(chars);
-                return cx->staticStrings().getUnit(c);
-            }
-        }
-
         JSFlatString* s = NewStringDeflated<allowGC>(cx, chars, length);
         if (!s)
             return nullptr;
 
         // Free |chars| because we're taking possession of it but not using it.
         js_free(chars);
         return s;
     }
@@ -1199,16 +1211,19 @@ template JSFlatString*
 js::NewString<NoGC>(ExclusiveContext* cx, Latin1Char* chars, size_t length);
 
 namespace js {
 
 template <AllowGC allowGC, typename CharT>
 JSFlatString*
 NewStringCopyNDontDeflate(ExclusiveContext* cx, const CharT* s, size_t n)
 {
+    if (JSFlatString* str = TryEmptyOrStaticString(cx, s, n))
+        return str;
+
     if (JSInlineString::lengthFits<CharT>(n))
         return NewInlineString<allowGC>(cx, mozilla::Range<const CharT>(s, n));
 
     ScopedJSFreePtr<CharT> news(cx->pod_malloc<CharT>(n + 1));
     if (!news) {
         if (!allowGC)
             cx->recoverFromOutOfMemory();
         return nullptr;
--- a/layout/base/nsPresContext.cpp
+++ b/layout/base/nsPresContext.cpp
@@ -2027,16 +2027,22 @@ nsPresContext::UpdateIsChrome()
   mIsChrome = mContainer &&
               nsIDocShellTreeItem::typeChrome == mContainer->ItemType();
 }
 
 /* virtual */ bool
 nsPresContext::HasAuthorSpecifiedRules(const nsIFrame *aFrame,
                                        uint32_t ruleTypeMask) const
 {
+#ifdef MOZ_STYLO
+  if (!mShell || mShell->StyleSet()->IsServo()) {
+    NS_ERROR("stylo: nsPresContext::HasAuthorSpecifiedRules not implemented");
+    return true;
+  }
+#endif
   return
     nsRuleNode::HasAuthorSpecifiedRules(aFrame->StyleContext(),
                                         ruleTypeMask,
                                         UseDocumentColors());
 }
 
 gfxUserFontSet*
 nsPresContext::GetUserFontSet()
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -2789,16 +2789,17 @@ nsIFrame::BuildDisplayListForChild(nsDis
       savedOutOfFlowData->mContainingBlockScrollClip);
   } else if (GetStateBits() & NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO &&
              isPlaceholder) {
     // If we have nested out-of-flow frames and the outer one isn't visible
     // then we won't have stored clip data for it. We can just clear the clip
     // instead since we know we won't render anything, and the inner out-of-flow
     // frame will setup the correct clip for itself.
     clipState.Clear();
+    clipState.SetScrollClipForContainingBlockDescendants(nullptr);
   }
 
   // Setup clipping for the parent's overflow:-moz-hidden-unscrollable,
   // or overflow:hidden on elements that don't support scrolling (and therefore
   // don't create nsHTML/XULScrollFrame). This clipping needs to not clip
   // anything directly rendered by the parent, only the rendering of its
   // children.
   // Don't use overflowClip to restrict the dirty rect, since some of the
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/1275411-1-ref.html
@@ -0,0 +1,13 @@
+<html>
+<head>
+</head>
+<body>
+	<div style="overflow: hidden; width:50px;height:50px;">
+		<div style="position: fixed;">
+			<div style="position: fixed;">
+				<div style="width: 100px; height: 50px; background: red;"></div>
+			</div>
+		</div>
+	</div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/1275411-1.html
@@ -0,0 +1,13 @@
+<html>
+<head>
+</head>
+<body>
+	<div style="overflow: hidden;">
+		<div style="position: fixed;">
+			<div style="position: fixed;">
+				<div style="width: 100px; height: 50px; background: red;"></div>
+			</div>
+		</div>
+	</div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/w3c-css/submitted/masking/mask-composite-1c.html
@@ -0,0 +1,56 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta charset="utf-8">
+    <title>CSS Masking: mask-composite: compose svg mask</title>
+    <link rel="author" title="CJ Ku" href="mailto:cku@mozilla.com">
+    <link rel="author" title="Mozilla" href="https://www.mozilla.org">
+    <link rel="help" href="http://www.w3.org/TR/css-masking-1/#the-mask-composite">
+    <link rel="match" href="mask-composite-1-ref.html">
+    <meta name="assert" content="Test checks that vector-mask-image can be composed correctly by different mask-composite value.">
+    <svg height="0">
+      <mask id="rectMask" x="0" y="0" width="100" height="100" >
+        <rect x="0" y="50" width="100" height="50" style="stroke:none; fill: #ffffff"/>
+      </mask>
+    </svg>
+    <style type="text/css">
+      div {
+        background-color: blue;
+        position: absolute;
+        margin: 0px;
+        padding: 0px;
+        width: 100px;
+        height: 100px;
+        top:10px;
+        mask-image: url(#rectMask),
+                    url(#rectMask);
+      }
+
+      div.add {
+        left: 10px;
+        mask-composite: add;
+      }
+
+      div.substract {
+        left: 120px;
+        mask-composite: substract;
+      }
+
+      div.intersect {
+        left: 230px;
+        mask-composite: intersect;
+      }
+
+      div.exclude {
+        left: 340px;
+        mask-composite: exclude;
+      }
+    </style>
+  </head>
+  <body>
+    <div class="add"></div>
+    <div class="substract"></div>
+    <div class="intersect"></div>
+    <div class="exclude"></div>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/w3c-css/submitted/masking/mask-composite-2c.html
@@ -0,0 +1,59 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta charset="utf-8">
+    <title>CSS Masking: mask-composite: compose SVG mask</title>
+    <link rel="author" title="CJ Ku" href="mailto:cku@mozilla.com">
+    <link rel="author" title="Mozilla" href="https://www.mozilla.org">
+    <link rel="help" href="http://www.w3.org/TR/css-masking-1/#the-mask-composite">
+    <link rel="match" href="mask-composite-2-ref.html">
+    <meta name="assert" content="Test checks that vector-mask-image can be composed correctly by different mask-composite value.">
+    <svg height="0">
+      <mask id="rectMask1" x="0" y="0" width="100" height="100" >
+        <rect x="0" y="50" width="100" height="50" style="stroke:none; fill: #ffffff"/>
+      </mask>
+      <mask id="rectMask2" x="0" y="0" width="100" height="100" >
+        <rect x="0" y="0" width="100" height="50" style="stroke:none; fill: #ffffff"/>
+      </mask>
+    </svg>
+    <style type="text/css">
+      div {
+        background-color: blue;
+        position: absolute;
+        margin: 0px;
+        padding: 0px;
+        width: 100px;
+        height: 100px;
+        top:10px;
+        mask-image: url(#rectMask1),
+                    url(#rectMask2);
+      }
+
+      div.add {
+        left: 10px;
+        mask-composite: add;
+      }
+
+      div.substract {
+        left: 120px;
+        mask-composite: substract;
+      }
+
+      div.intersect {
+        left: 230px;
+        mask-composite: intersect;
+      }
+
+      div.exclude {
+        left: 340px;
+        mask-composite: exclude;
+      }
+    </style>
+  </head>
+  <body>
+    <div class="add"></div>
+    <div class="substract"></div>
+    <div class="intersect"></div>
+    <div class="exclude"></div>
+  </body>
+</html>
--- a/layout/reftests/w3c-css/submitted/masking/reftest.list
+++ b/layout/reftests/w3c-css/submitted/masking/reftest.list
@@ -1,17 +1,19 @@
 # All mask properties test cases are meant to be failed
 # until bug 1251161 is fixed, which means enabling mask shorthand.
 # To enable it in compile time, refer to bug 1243734
 
 # mask-composite test cases
 fails == mask-composite-1a.html mask-composite-1-ref.html
 fails == mask-composite-1b.html mask-composite-1-ref.html
+fails == mask-composite-1c.html mask-composite-1-ref.html
 fails == mask-composite-2a.html mask-composite-2-ref.html
 fails == mask-composite-2b.html mask-composite-2-ref.html
+fails == mask-composite-2c.html mask-composite-2-ref.html
 
 # mask-mode test cases
 fails == mask-mode-a.html mask-mode-ref.html
 fails == mask-mode-b.html mask-mode-ref.html
 fails == mask-type.html mask-type-ref.html
 
 # mask-image test cases
 fails == mask-image-1a.html mask-image-1-ref.html
--- a/layout/style/ServoBindings.cpp
+++ b/layout/style/ServoBindings.cpp
@@ -1,18 +1,20 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/ServoBindings.h"
 
+#include "nsAttrValueInlines.h"
 #include "nsCSSRuleProcessor.h"
 #include "nsContentUtils.h"
+#include "nsDOMTokenList.h"
 #include "nsIDOMNode.h"
 #include "nsIDocument.h"
 #include "nsINode.h"
 #include "nsIPrincipal.h"
 #include "nsNameSpaceManager.h"
 #include "nsString.h"
 #include "nsStyleStruct.h"
 #include "StyleStructContext.h"
@@ -148,16 +150,77 @@ Gecko_LocalName(RawGeckoElement* aElemen
 
 nsIAtom*
 Gecko_Namespace(RawGeckoElement* aElement)
 {
   int32_t id = aElement->NodeInfo()->NamespaceID();
   return nsContentUtils::NameSpaceManager()->NameSpaceURIAtom(id);
 }
 
+nsIAtom*
+Gecko_GetElementId(RawGeckoElement* aElement)
+{
+  const nsAttrValue* attr = aElement->GetParsedAttr(nsGkAtoms::id);
+  return attr ? attr->GetAtomValue() : nullptr;
+}
+
+uint32_t
+Gecko_ClassOrClassList(RawGeckoElement* aElement,
+                       nsIAtom** aClass, nsIAtom*** aClassList)
+{
+  const nsAttrValue* attr = aElement->GetParsedAttr(nsGkAtoms::_class);
+  if (!attr) {
+    return 0;
+  }
+
+  // For class values with only whitespace, Gecko just stores a string. For the
+  // purposes of the style system, there is no class in this case.
+  if (attr->Type() == nsAttrValue::eString) {
+    MOZ_ASSERT(nsContentUtils::TrimWhitespace<nsContentUtils::IsHTMLWhitespace>(
+                 attr->GetStringValue()).IsEmpty());
+    return 0;
+  }
+
+  // Single tokens are generally stored as an atom. Check that case.
+  if (attr->Type() == nsAttrValue::eAtom) {
+    *aClass = attr->GetAtomValue();
+    return 1;
+  }
+
+  // At this point we should have an atom array. It is likely, but not
+  // guaranteed, that we have two or more elements in the array.
+  MOZ_ASSERT(attr->Type() == nsAttrValue::eAtomArray);
+  nsTArray<nsCOMPtr<nsIAtom>>* atomArray = attr->GetAtomArrayValue();
+  uint32_t length = atomArray->Length();
+
+  // Special case: zero elements.
+  if (length == 0) {
+    return 0;
+  }
+
+  // Special case: one element.
+  if (length == 1) {
+    *aClass = atomArray->ElementAt(0);
+    return 1;
+  }
+
+  // General case: Two or more elements.
+  //
+  // Note: We could also expose this array as an array of nsCOMPtrs, since
+  // bindgen knows what those look like, and eliminate the reinterpret_cast.
+  // But it's not obvious that that would be preferable.
+  static_assert(sizeof(nsCOMPtr<nsIAtom>) == sizeof(nsIAtom*), "Bad simplification");
+  static_assert(alignof(nsCOMPtr<nsIAtom>) == alignof(nsIAtom*), "Bad simplification");
+
+  nsCOMPtr<nsIAtom>* elements = atomArray->Elements();
+  nsIAtom** rawElements = reinterpret_cast<nsIAtom**>(elements);
+  *aClassList = rawElements;
+  return atomArray->Length();
+}
+
 ServoNodeData*
 Gecko_GetNodeData(RawGeckoNode* aNode)
 {
   return aNode->GetServoNodeData();
 }
 
 void
 Gecko_SetNodeData(RawGeckoNode* aNode, ServoNodeData* aData)
@@ -261,20 +324,18 @@ Gecko_SetMozBinding(nsStyleDisplay* aDis
 
   nsString url;
   nsDependentCSubstring urlString(reinterpret_cast<const char*>(aURLString),
                                   aURLStringLength);
   AppendUTF8toUTF16(urlString, url);
   RefPtr<nsStringBuffer> urlBuffer = nsCSSValue::BufferFromString(url);
 
   aDisplay->mBinding =
-    new css::URLValue(urlBuffer,
-                      nsMainThreadPtrHandle<nsIURI>(aBaseURI),
-                      nsMainThreadPtrHandle<nsIURI>(aReferrer),
-                      nsMainThreadPtrHandle<nsIPrincipal>(aPrincipal));
+    new css::URLValue(urlBuffer, do_AddRef(aBaseURI),
+                      do_AddRef(aReferrer), do_AddRef(aPrincipal));
 }
 
 void
 Gecko_CopyMozBindingFrom(nsStyleDisplay* aDest, const nsStyleDisplay* aSrc)
 {
   aDest->mBinding = aSrc->mBinding;
 }
 
--- a/layout/style/ServoBindings.h
+++ b/layout/style/ServoBindings.h
@@ -78,16 +78,32 @@ uint8_t Gecko_ElementState(RawGeckoEleme
 bool Gecko_IsHTMLElementInHTMLDocument(RawGeckoElement* element);
 bool Gecko_IsLink(RawGeckoElement* element);
 bool Gecko_IsTextNode(RawGeckoNode* node);
 bool Gecko_IsVisitedLink(RawGeckoElement* element);
 bool Gecko_IsUnvisitedLink(RawGeckoElement* element);
 bool Gecko_IsRootElement(RawGeckoElement* element);
 nsIAtom* Gecko_LocalName(RawGeckoElement* element);
 nsIAtom* Gecko_Namespace(RawGeckoElement* element);
+nsIAtom* Gecko_GetElementId(RawGeckoElement* element);
+
+// Gets the class or class list (if any) of the Element.
+//
+// The calling convention here is rather hairy, and is optimized for getting
+// Servo the information it needs for hot calls.
+//
+// The return value indicates the number of classes. If zero, neither outparam
+// is valid. If one, the class_ outparam is filled with the atom of the class.
+// If two or more, the classList outparam is set to point to an array of atoms
+// representing the class list.
+//
+// The array is borrowed and the atoms are not addrefed. These values can be
+// invalidated by any DOM mutation. Use them in a tight scope.
+uint32_t Gecko_ClassOrClassList(RawGeckoElement* element,
+                                nsIAtom** class_, nsIAtom*** classList);
 
 // Node data.
 ServoNodeData* Gecko_GetNodeData(RawGeckoNode* node);
 void Gecko_SetNodeData(RawGeckoNode* node, ServoNodeData* data);
 void Servo_DropNodeData(ServoNodeData* data);
 
 // Atoms.
 nsIAtom* Gecko_Atomize(const char* aString, uint32_t aLength);
--- a/layout/style/nsCSSDataBlock.cpp
+++ b/layout/style/nsCSSDataBlock.cpp
@@ -55,16 +55,33 @@ ShouldIgnoreColors(nsRuleData *aRuleData
 static void
 TryToStartImageLoadOnValue(const nsCSSValue& aValue, nsIDocument* aDocument,
                            nsStyleContext* aContext, nsCSSProperty aProperty,
                            bool aForTokenStream)
 {
   MOZ_ASSERT(aDocument);
 
   if (aValue.GetUnit() == eCSSUnit_URL) {
+#ifdef MOZ_ENABLE_MASK_AS_SHORTHAND
+    // The 'mask-image' property accepts local reference URIs.
+    // For example,
+    //   mask-image: url(#mask_id); // refer to a SVG mask element, whose id is
+    //                              // "mask_id", in the current document.
+    // For such 'mask-image' values (pointing to an in-document element),
+    // there is no need to trigger image download.
+    if (aProperty == eCSSProperty_mask_image) {
+      nsIURI* docURI = aDocument->GetDocumentURI();
+      nsIURI* imageURI = aValue.GetURLValue();
+      bool isEqualExceptRef = false;
+      nsresult  rv = imageURI->EqualsExceptRef(docURI, &isEqualExceptRef);
+      if (NS_SUCCEEDED(rv) && isEqualExceptRef) {
+        return;
+      }
+    }
+#endif
     aValue.StartImageLoad(aDocument);
     if (aForTokenStream && aContext) {
       CSSVariableImageTable::Add(aContext, aProperty,
                                  aValue.GetImageStructValue());
     }
   }
   else if (aValue.GetUnit() == eCSSUnit_Image) {
     // If we already have a request, see if this document needs to clone it.
--- a/layout/style/nsRuleNode.cpp
+++ b/layout/style/nsRuleNode.cpp
@@ -1254,23 +1254,35 @@ static void SetStyleImage(nsStyleContext
     }
     case eCSSUnit_Element:
       aResult.SetElementId(aValue.GetStringBufferValue());
       break;
     case eCSSUnit_Initial:
     case eCSSUnit_Unset:
     case eCSSUnit_None:
       break;
+    case eCSSUnit_URL:
+    {
+#ifdef DEBUG
+      nsIDocument* currentDoc = aStyleContext->PresContext()->Document();
+      nsIURI* docURI = currentDoc->GetDocumentURI();
+      nsIURI* imageURI = aValue.GetURLValue();
+      bool isEqualExceptRef = false;
+      imageURI->EqualsExceptRef(docURI, &isEqualExceptRef);
+      // Either we have eCSSUnit_URL values for if-visited style contexts,
+      // which we can safely treat like 'none', or aValue refers to an
+      // in-document resource. Otherwise this is an unexpected unit.
+      NS_ASSERTION(aStyleContext->IsStyleIfVisited() || isEqualExceptRef,
+                   "unexpected unit; maybe nsCSSValue::Image::Image() failed?");
+#endif
+
+      break;
+    }
     default:
-      // We might have eCSSUnit_URL values for if-visited style
-      // contexts, which we can safely treat like 'none'.  Otherwise
-      // this is an unexpected unit.
-      NS_ASSERTION(aStyleContext->IsStyleIfVisited() &&
-                   aValue.GetUnit() == eCSSUnit_URL,
-                   "unexpected unit; maybe nsCSSValue::Image::Image() failed?");
+      MOZ_ASSERT_UNREACHABLE("Unexpected Unit type.");
       break;
   }
 }
 
 // flags for SetDiscrete - align values with SETCOORD_* constants
 // where possible
 
 #define SETDSC_NORMAL                 0x01   // N
@@ -6570,17 +6582,18 @@ struct BackgroundItemComputer<nsCSSValue
 template <>
 struct BackgroundItemComputer<nsCSSValueList, nsCOMPtr<nsIURI> >
 {
   static void ComputeValue(nsStyleContext* aStyleContext,
                            const nsCSSValueList* aSpecifiedValue,
                            nsCOMPtr<nsIURI>& aComputedValue,
                            RuleNodeCacheConditions& aConditions)
   {
-    if (eCSSUnit_Image == aSpecifiedValue->mValue.GetUnit()) {
+    if (eCSSUnit_Image == aSpecifiedValue->mValue.GetUnit() ||
+        eCSSUnit_URL == aSpecifiedValue->mValue.GetUnit()) {
       aComputedValue = aSpecifiedValue->mValue.GetURLValue();
     } else if (eCSSUnit_Null != aSpecifiedValue->mValue.GetUnit()) {
       aComputedValue = nullptr;
     }
   }
 };
 
 /* Helper function for ComputePositionValue.
--- a/layout/style/nsStyleStruct.h
+++ b/layout/style/nsStyleStruct.h
@@ -1376,17 +1376,17 @@ protected:
  * An object that allows sharing of arrays that store 'quotes' property
  * values.  This is particularly important for inheritance, where we want
  * to share the same 'quotes' value with a parent style context.
  */
 class nsStyleQuoteValues
 {
 public:
   typedef nsTArray<std::pair<nsString, nsString>> QuotePairArray;
-  NS_INLINE_DECL_REFCOUNTING(nsStyleQuoteValues);
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(nsStyleQuoteValues);
   QuotePairArray mQuotePairs;
 
 private:
   ~nsStyleQuoteValues() {}
 };
 
 struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleList
 {
@@ -1437,17 +1437,17 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsSt
   mozilla::CounterStyle* GetCounterStyle() const
   {
     return mCounterStyle.get();
   }
   void SetCounterStyle(mozilla::CounterStyle* aStyle)
   {
     // NB: This function is called off-main-thread during parallel restyle, but
     // only with builtin styles that use dummy refcounting.
-    MOZ_ASSERT(NS_IsMainThread() || aStyle->IsDependentStyle());
+    MOZ_ASSERT(NS_IsMainThread() || !aStyle->IsDependentStyle());
     mCounterStyle = aStyle;
   }
   void SetListStyleType(const nsSubstring& aType,
                         nsPresContext* aPresContext)
   {
     SetCounterStyle(aPresContext->CounterStyleManager()->BuildCounterStyle(aType));
   }
 
--- a/layout/svg/nsSVGFilterInstance.cpp
+++ b/layout/svg/nsSVGFilterInstance.cpp
@@ -5,16 +5,17 @@
 
 // Main header first:
 #include "nsSVGFilterInstance.h"
 
 // Keep others in (case-insensitive) order:
 #include "gfxPlatform.h"
 #include "gfxUtils.h"
 #include "nsISVGChildFrame.h"
+#include "mozilla/dom/HTMLCanvasElement.h"
 #include "mozilla/dom/SVGFilterElement.h"
 #include "nsReferencedElement.h"
 #include "nsSVGFilterFrame.h"
 #include "nsSVGUtils.h"
 #include "SVGContentUtils.h"
 #include "FilterSupport.h"
 #include "gfx2DGlue.h"
 
@@ -257,23 +258,23 @@ nsSVGFilterInstance::ComputeFilterPrimit
   // in the region.
   region.RoundOut();
   return RoundedToInt(region);
 }
 
 void
 nsSVGFilterInstance::GetInputsAreTainted(const nsTArray<FilterPrimitiveDescription>& aPrimitiveDescrs,
                                          const nsTArray<int32_t>& aInputIndices,
+                                         bool aFilterInputIsTainted,
                                          nsTArray<bool>& aOutInputsAreTainted)
 {
   for (uint32_t i = 0; i < aInputIndices.Length(); i++) {
     int32_t inputIndex = aInputIndices[i];
     if (inputIndex < 0) {
-      // SourceGraphic, SourceAlpha, FillPaint and StrokePaint are tainted.
-      aOutInputsAreTainted.AppendElement(true);
+      aOutInputsAreTainted.AppendElement(aFilterInputIsTainted);
     } else {
       aOutInputsAreTainted.AppendElement(aPrimitiveDescrs[inputIndex].IsTainted());
     }
   }
 }
 
 static int32_t
 GetLastResultIndex(const nsTArray<FilterPrimitiveDescription>& aPrimitiveDescrs)
@@ -354,16 +355,30 @@ nsSVGFilterInstance::GetSourceIndices(ns
         return NS_ERROR_FAILURE;
     }
 
     aSourceIndices.AppendElement(sourceIndex);
   }
   return NS_OK;
 }
 
+static bool
+IsFilterInputTainted(nsIContent* aElement)
+{
+  // When the filter is applied during canvas drawing, we might be allowed to
+  // read from the canvas.
+  if (HTMLCanvasElement* canvas =
+        HTMLCanvasElement::FromContentOrNull(aElement)) {
+    return canvas->IsWriteOnly();
+  }
+
+  // Always treat normal filtered elements as tainted.
+  return true;
+}
+
 nsresult
 nsSVGFilterInstance::BuildPrimitives(nsTArray<FilterPrimitiveDescription>& aPrimitiveDescrs,
                                      nsTArray<RefPtr<SourceSurface>>& aInputImages)
 {
   mSourceGraphicIndex = GetLastResultIndex(aPrimitiveDescrs);
 
   // Clip previous filter's output to this filter's filter region.
   if (mSourceGraphicIndex >= 0) {
@@ -384,32 +399,34 @@ nsSVGFilterInstance::BuildPrimitives(nsT
   }
 
   // Maps source image name to source index.
   nsDataHashtable<nsStringHashKey, int32_t> imageTable(8);
 
   // The principal that we check principals of any loaded images against.
   nsCOMPtr<nsIPrincipal> principal = mTargetContent->NodePrincipal();
 
+  bool filterInputIsTainted = IsFilterInputTainted(mTargetContent);
+
   for (uint32_t primitiveElementIndex = 0;
        primitiveElementIndex < primitives.Length();
        ++primitiveElementIndex) {
     nsSVGFE* filter = primitives[primitiveElementIndex];
 
     AutoTArray<int32_t,2> sourceIndices;
     nsresult rv = GetSourceIndices(filter, aPrimitiveDescrs, imageTable, sourceIndices);
     if (NS_FAILED(rv)) {
       return rv;
     }
 
     IntRect primitiveSubregion =
       ComputeFilterPrimitiveSubregion(filter, aPrimitiveDescrs, sourceIndices);
 
     nsTArray<bool> sourcesAreTainted;
-    GetInputsAreTainted(aPrimitiveDescrs, sourceIndices, sourcesAreTainted);
+    GetInputsAreTainted(aPrimitiveDescrs, sourceIndices, filterInputIsTainted, sourcesAreTainted);
 
     FilterPrimitiveDescription descr =
       filter->GetPrimitiveDescription(this, primitiveSubregion, sourcesAreTainted, aInputImages);
 
     descr.SetIsTainted(filter->OutputIsTainted(sourcesAreTainted, principal));
     descr.SetFilterSpaceBounds(mFilterSpaceBounds);
     descr.SetPrimitiveSubregion(primitiveSubregion.Intersect(descr.FilterSpaceBounds()));
 
--- a/layout/svg/nsSVGFilterInstance.h
+++ b/layout/svg/nsSVGFilterInstance.h
@@ -150,16 +150,17 @@ private:
                                           const nsTArray<int32_t>& aInputIndices);
 
   /**
    * Takes the input indices of a filter primitive and returns for each input
    * whether the input's output is tainted.
    */
   void GetInputsAreTainted(const nsTArray<FilterPrimitiveDescription>& aPrimitiveDescrs,
                            const nsTArray<int32_t>& aInputIndices,
+                           bool aFilterInputIsTainted,
                            nsTArray<bool>& aOutInputsAreTainted);
 
   /**
    * Scales a numeric filter primitive length in the X, Y or "XY" directions
    * into a length in filter space (no offset is applied).
    */
   float GetPrimitiveNumber(uint8_t aCtxType, float aValue) const;
 
--- a/layout/svg/nsSVGIntegrationUtils.cpp
+++ b/layout/svg/nsSVGIntegrationUtils.cpp
@@ -477,38 +477,41 @@ GenerateMaskSurface(const nsSVGIntegrati
     ctx.CurrentMatrix() * gfxMatrix::Translation(-maskSurfaceRect.TopLeft());
   maskContext->SetMatrix(maskSurfaceMatrix);
 
   // Multiple SVG masks interleave with image mask. Paint each layer onto maskDT
   // one at a time.
   for (int i = svgMaskFrames.Length() - 1; i >= 0 ; i--) {
     nsSVGMaskFrame *maskFrame = svgMaskFrames[i];
 
+    CompositionOp compositionOp = (i == int(svgMaskFrames.Length() - 1))
+      ? CompositionOp::OP_OVER
+      : nsCSSRendering::GetGFXCompositeMode(svgReset->mMask.mLayers[i].mComposite);
+
     // maskFrame != nullptr means we get a SVG mask.
     // maskFrame == nullptr means we get an image mask.
     if (maskFrame) {
       Matrix svgMaskMatrix;
       RefPtr<SourceSurface> svgMask =
         maskFrame->GetMaskForMaskedFrame(maskContext, aParams.frame,
                                          cssPxToDevPxMatrix, aOpacity,
                                          &svgMaskMatrix,
                                          svgReset->mMask.mLayers[i].mMaskMode);
       if (svgMask) {
         gfxContextMatrixAutoSaveRestore matRestore(maskContext);
 
         maskContext->Multiply(ThebesMatrix(svgMaskMatrix));
         Rect drawRect = IntRectToRect(IntRect(IntPoint(0, 0), svgMask->GetSize()));
-        maskDT->DrawSurface(svgMask, drawRect, drawRect);
+        maskDT->DrawSurface(svgMask, drawRect, drawRect, DrawSurfaceOptions(),
+                            DrawOptions(1.0f, compositionOp));
       }
     } else {
       gfxContextMatrixAutoSaveRestore matRestore(maskContext);
 
       maskContext->Multiply(gfxMatrix::Translation(-aOffest));
-      CompositionOp compositionOp =
-        nsCSSRendering::GetGFXCompositeMode(svgReset->mMask.mLayers[i].mComposite);
       nsRenderingContext rc(maskContext);
       nsCSSRendering::PaintBGParams  params =
         nsCSSRendering::PaintBGParams::ForSingleLayer(*aParams.frame->PresContext(),
                                                       rc, aParams.dirtyRect,
                                                       aParams.borderArea,
                                                       aParams.frame,
                                                       aParams.builder->GetBackgroundPaintFlags() |
                                                       nsCSSRendering::PAINTBG_MASK_IMAGE,
--- a/media/libnestegg/README_MOZILLA
+++ b/media/libnestegg/README_MOZILLA
@@ -1,8 +1,8 @@
 The source from this directory was copied from the nestegg
 git repository using the update.sh script.  The only changes
 made were those applied by update.sh and the addition of
 Makefile.in build files for the Mozilla build system.
 
 The nestegg git repository is: git://github.com/kinetiknz/nestegg.git
 
-The git commit ID used was d493c8a7abd05c6911cd546fd6b5c82b366f5203.
+The git commit ID used was 3bc788d4de8f11a1e6b625047f49b9d35dce824f.
--- a/media/libnestegg/include/nestegg.h
+++ b/media/libnestegg/include/nestegg.h
@@ -84,16 +84,23 @@ extern "C" {
 #define NESTEGG_SEEK_END 2 /**< Seek offset relative to end of stream. */
 
 #define NESTEGG_LOG_DEBUG    1     /**< Debug level log message. */
 #define NESTEGG_LOG_INFO     10    /**< Informational level log message. */
 #define NESTEGG_LOG_WARNING  100   /**< Warning level log message. */
 #define NESTEGG_LOG_ERROR    1000  /**< Error level log message. */
 #define NESTEGG_LOG_CRITICAL 10000 /**< Critical level log message. */
 
+#define NESTEGG_ENCODING_COMPRESSION 0 /**< Content encoding type is compression. */
+#define NESTEGG_ENCODING_ENCRYPTION  1 /**< Content encoding type is encryption. */
+
+#define NESTEGG_PACKET_HAS_SIGNAL_BYTE_FALSE       0 /**< Packet does not have signal byte */
+#define NESTEGG_PACKET_HAS_SIGNAL_BYTE_UNENCRYPTED 1 /**< Packet has signal byte and is unencrypted */
+#define NESTEGG_PACKET_HAS_SIGNAL_BYTE_ENCRYPTED   2 /**< Packet has signal byte and is encrypted */
+
 #define NESTEGG_PACKET_HAS_KEYFRAME_FALSE   0 /**< Packet contains only keyframes. */
 #define NESTEGG_PACKET_HAS_KEYFRAME_TRUE    1 /**< Packet does not contain any keyframes */
 #define NESTEGG_PACKET_HAS_KEYFRAME_UNKNOWN 2 /**< Packet may or may not contain keyframes */
 
 typedef struct nestegg nestegg;               /**< Opaque handle referencing the stream state. */
 typedef struct nestegg_packet nestegg_packet; /**< Opaque handle referencing a packet of data. */
 
 /** User supplied IO context. */
@@ -283,16 +290,38 @@ int nestegg_track_video_params(nestegg *
     @param context Stream context initialized by #nestegg_init.
     @param track   Zero based track number.
     @param params  Storage for the queried audio parameters.
     @retval  0 Success.
     @retval -1 Error. */
 int nestegg_track_audio_params(nestegg * context, unsigned int track,
                                nestegg_audio_params * params);
 
+/** Query the encoding status for @a track. If a track has multiple encodings
+    the first will be returned.
+    @param context Stream context initialized by #nestegg_init.
+    @param track   Zero based track number.
+    @retval #NESTEGG_ENCODING_COMPRESSION The track is compressed, but not encrypted.
+    @retval #NESTEGG_ENCODING_ENCRYPTION The track is encrypted and compressed.
+    @retval -1 Error. */
+int nestegg_track_encoding(nestegg * context, unsigned int track);
+
+/** Query the ContentEncKeyId for @a track. Will return an error if the track
+    in not encrypted, or is not recognized.
+    @param context                   Stream context initialized by #nestegg_init.
+    @param track                     Zero based track number.
+    @param content_enc_key_id        Storage for queried id. The content encryption key used.
+                                     Owned by nestegg and will be freed separately.
+    @param content_enc_key_id_length Length of the queried ContentEncKeyId in bytes.
+    @retval  0 Success.
+    @retval -1 Error. */
+int nestegg_track_content_enc_key_id(nestegg * context, unsigned int track,
+                                     unsigned char const ** content_enc_key_id,
+                                     size_t * content_enc_key_id_length);
+
 /** Query the default frame duration for @a track.  For a video track, this
     is typically the inverse of the video frame rate.
     @param context  Stream context initialized by #nestegg_init.
     @param track    Zero based track number.
     @param duration Storage for the default duration in nanoseconds.
     @retval  0 Success.
     @retval -1 Error. */
 int nestegg_track_default_duration(nestegg * context, unsigned int track,
@@ -382,16 +411,39 @@ int nestegg_packet_additional_data(neste
 /** Returns discard_padding for given packet
     @param packet  Packet initialized by #nestegg_read_packet.
     @param discard_padding pointer to store discard padding in.
     @retval  0 Success.
     @retval -1 Error. */
 int nestegg_packet_discard_padding(nestegg_packet * packet,
                                    int64_t * discard_padding);
 
+/** Query if a packet is encrypted.
+    @param packet Packet initialized by #nestegg_read_packet.
+    @retval  #NESTEGG_PACKET_HAS_SIGNAL_BYTE_FALSE No signal byte, encryption
+             information not read from packet.
+    @retval  #NESTEGG_PACKET_HAS_SIGNAL_BYTE_UNENCRYPTED Encrypted bit not
+             set, encryption information not read from packet.
+    @retval  #NESTEGG_PACKET_HAS_SIGNAL_BYTE_ENCRYPTED Encrypted bit set,
+             encryption infomation read from packet.
+    @retval -1 Error.*/
+int nestegg_packet_encryption(nestegg_packet * packet);
+
+/** Query the IV for an encrypted packet. Expects a packet from an encrypted
+    track, and will return error if given a packet that has no signal btye.
+    @param packet Packet initialized by #nestegg_read_packet.
+    @param iv     Storage for queried iv.
+    @param length Length of returned iv, may be 0.
+                  The data is owned by the #nestegg_packet packet.
+    @retval  0 Success.
+    @retval -1 Error.
+  */
+int nestegg_packet_iv(nestegg_packet * packet, unsigned char const ** iv,
+                      size_t * length);
+
 /** Returns reference_block given packet
     @param packet          Packet initialized by #nestegg_read_packet.
     @param reference_block pointer to store reference block in.
     @retval  0 Success.
     @retval -1 Error. */
 int nestegg_packet_reference_block(nestegg_packet * packet,
                                    int64_t * reference_block);
 
--- a/media/libnestegg/src/nestegg.c
+++ b/media/libnestegg/src/nestegg.c
@@ -7,148 +7,172 @@
 #include <assert.h>
 #include <stdlib.h>
 #include <string.h>
 
 #include "halloc.h"
 #include "nestegg/nestegg.h"
 
 /* EBML Elements */
-#define ID_EBML                 0x1a45dfa3
-#define ID_EBML_VERSION         0x4286
-#define ID_EBML_READ_VERSION    0x42f7
-#define ID_EBML_MAX_ID_LENGTH   0x42f2
-#define ID_EBML_MAX_SIZE_LENGTH 0x42f3
-#define ID_DOCTYPE              0x4282
-#define ID_DOCTYPE_VERSION      0x4287
-#define ID_DOCTYPE_READ_VERSION 0x4285
+#define ID_EBML                     0x1a45dfa3
+#define ID_EBML_VERSION             0x4286
+#define ID_EBML_READ_VERSION        0x42f7
+#define ID_EBML_MAX_ID_LENGTH       0x42f2
+#define ID_EBML_MAX_SIZE_LENGTH     0x42f3
+#define ID_DOCTYPE                  0x4282
+#define ID_DOCTYPE_VERSION          0x4287
+#define ID_DOCTYPE_READ_VERSION     0x4285
 
 /* Global Elements */
-#define ID_VOID                 0xec
-#define ID_CRC32                0xbf
+#define ID_VOID                     0xec
+#define ID_CRC32                    0xbf
 
 /* WebM Elements */
-#define ID_SEGMENT              0x18538067
+#define ID_SEGMENT                  0x18538067
 
 /* Seek Head Elements */
-#define ID_SEEK_HEAD            0x114d9b74
-#define ID_SEEK                 0x4dbb
-#define ID_SEEK_ID              0x53ab
-#define ID_SEEK_POSITION        0x53ac
+#define ID_SEEK_HEAD                0x114d9b74
+#define ID_SEEK                     0x4dbb
+#define ID_SEEK_ID                  0x53ab
+#define ID_SEEK_POSITION            0x53ac
 
 /* Info Elements */
-#define ID_INFO                 0x1549a966
-#define ID_TIMECODE_SCALE       0x2ad7b1
-#define ID_DURATION             0x4489
+#define ID_INFO                     0x1549a966
+#define ID_TIMECODE_SCALE           0x2ad7b1
+#define ID_DURATION                 0x4489
 
 /* Cluster Elements */
-#define ID_CLUSTER              0x1f43b675
-#define ID_TIMECODE             0xe7
-#define ID_BLOCK_GROUP          0xa0
-#define ID_SIMPLE_BLOCK         0xa3
+#define ID_CLUSTER                  0x1f43b675
+#define ID_TIMECODE                 0xe7
+#define ID_BLOCK_GROUP              0xa0
+#define ID_SIMPLE_BLOCK             0xa3
 
 /* BlockGroup Elements */
-#define ID_BLOCK                0xa1
-#define ID_BLOCK_ADDITIONS      0x75a1
-#define ID_BLOCK_DURATION       0x9b
-#define ID_REFERENCE_BLOCK      0xfb
-#define ID_DISCARD_PADDING      0x75a2
+#define ID_BLOCK                    0xa1
+#define ID_BLOCK_ADDITIONS          0x75a1
+#define ID_BLOCK_DURATION           0x9b
+#define ID_REFERENCE_BLOCK          0xfb
+#define ID_DISCARD_PADDING          0x75a2
 
 /* BlockAdditions Elements */
-#define ID_BLOCK_MORE           0xa6
+#define ID_BLOCK_MORE               0xa6
 
 /* BlockMore Elements */
-#define ID_BLOCK_ADD_ID         0xee
-#define ID_BLOCK_ADDITIONAL     0xa5
+#define ID_BLOCK_ADD_ID             0xee
+#define ID_BLOCK_ADDITIONAL         0xa5
 
 /* Tracks Elements */
-#define ID_TRACKS               0x1654ae6b
-#define ID_TRACK_ENTRY          0xae
-#define ID_TRACK_NUMBER         0xd7
-#define ID_TRACK_UID            0x73c5
-#define ID_TRACK_TYPE           0x83
-#define ID_FLAG_ENABLED         0xb9
-#define ID_FLAG_DEFAULT         0x88
-#define ID_FLAG_LACING          0x9c
-#define ID_TRACK_TIMECODE_SCALE 0x23314f
-#define ID_LANGUAGE             0x22b59c
-#define ID_CODEC_ID             0x86
-#define ID_CODEC_PRIVATE        0x63a2
-#define ID_CODEC_DELAY          0x56aa
-#define ID_SEEK_PREROLL         0x56bb
-#define ID_DEFAULT_DURATION     0x23e383
+#define ID_TRACKS                   0x1654ae6b
+#define ID_TRACK_ENTRY              0xae
+#define ID_TRACK_NUMBER             0xd7
+#define ID_TRACK_UID                0x73c5
+#define ID_TRACK_TYPE               0x83
+#define ID_FLAG_ENABLED             0xb9
+#define ID_FLAG_DEFAULT             0x88
+#define ID_FLAG_LACING              0x9c
+#define ID_TRACK_TIMECODE_SCALE     0x23314f
+#define ID_LANGUAGE                 0x22b59c
+#define ID_CODEC_ID                 0x86
+#define ID_CODEC_PRIVATE            0x63a2
+#define ID_CODEC_DELAY              0x56aa
+#define ID_SEEK_PREROLL             0x56bb
+#define ID_DEFAULT_DURATION         0x23e383
 
 /* Video Elements */
-#define ID_VIDEO                0xe0
-#define ID_STEREO_MODE          0x53b8
-#define ID_ALPHA_MODE           0x53c0
-#define ID_PIXEL_WIDTH          0xb0
-#define ID_PIXEL_HEIGHT         0xba
-#define ID_PIXEL_CROP_BOTTOM    0x54aa
-#define ID_PIXEL_CROP_TOP       0x54bb
-#define ID_PIXEL_CROP_LEFT      0x54cc
-#define ID_PIXEL_CROP_RIGHT     0x54dd
-#define ID_DISPLAY_WIDTH        0x54b0
-#define ID_DISPLAY_HEIGHT       0x54ba
+#define ID_VIDEO                    0xe0
+#define ID_STEREO_MODE              0x53b8
+#define ID_ALPHA_MODE               0x53c0
+#define ID_PIXEL_WIDTH              0xb0
+#define ID_PIXEL_HEIGHT             0xba
+#define ID_PIXEL_CROP_BOTTOM        0x54aa
+#define ID_PIXEL_CROP_TOP           0x54bb
+#define ID_PIXEL_CROP_LEFT          0x54cc
+#define ID_PIXEL_CROP_RIGHT         0x54dd
+#define ID_DISPLAY_WIDTH            0x54b0
+#define ID_DISPLAY_HEIGHT           0x54ba
 
 /* Audio Elements */
-#define ID_AUDIO                0xe1
-#define ID_SAMPLING_FREQUENCY   0xb5
-#define ID_CHANNELS             0x9f
-#define ID_BIT_DEPTH            0x6264
+#define ID_AUDIO                    0xe1
+#define ID_SAMPLING_FREQUENCY       0xb5
+#define ID_CHANNELS                 0x9f
+#define ID_BIT_DEPTH                0x6264
 
 /* Cues Elements */
-#define ID_CUES                 0x1c53bb6b
-#define ID_CUE_POINT            0xbb
-#define ID_CUE_TIME             0xb3
-#define ID_CUE_TRACK_POSITIONS  0xb7
-#define ID_CUE_TRACK            0xf7
-#define ID_CUE_CLUSTER_POSITION 0xf1
-#define ID_CUE_BLOCK_NUMBER     0x5378
+#define ID_CUES                     0x1c53bb6b
+#define ID_CUE_POINT                0xbb
+#define ID_CUE_TIME                 0xb3
+#define ID_CUE_TRACK_POSITIONS      0xb7
+#define ID_CUE_TRACK                0xf7
+#define ID_CUE_CLUSTER_POSITION     0xf1
+#define ID_CUE_BLOCK_NUMBER         0x5378
+
+/* Encoding Elements */
+#define ID_CONTENT_ENCODINGS        0x6d80
+#define ID_CONTENT_ENCODING         0x6240
+#define ID_CONTENT_ENCODING_TYPE    0x5033
+
+/* Encryption Elements */
+#define ID_CONTENT_ENCRYPTION       0x5035
+#define ID_CONTENT_ENC_ALGO         0x47e1
+#define ID_CONTENT_ENC_KEY_ID       0x47e2
+#define ID_CONTENT_ENC_AES_SETTINGS 0x47e7
+#define ID_AES_SETTINGS_CIPHER_MODE 0x47e8
 
 /* EBML Types */
 enum ebml_type_enum {
   TYPE_UNKNOWN,
   TYPE_MASTER,
   TYPE_UINT,
   TYPE_FLOAT,
   TYPE_STRING,
   TYPE_BINARY
 };
 
-#define LIMIT_STRING            (1 << 20)
-#define LIMIT_BINARY            (1 << 24)
-#define LIMIT_BLOCK             (1 << 30)
-#define LIMIT_FRAME             (1 << 28)
+#define LIMIT_STRING                (1 << 20)
+#define LIMIT_BINARY                (1 << 24)
+#define LIMIT_BLOCK                 (1 << 30)
+#define LIMIT_FRAME                 (1 << 28)
 
 /* Field Flags */
-#define DESC_FLAG_NONE          0
-#define DESC_FLAG_MULTI         (1 << 0)
-#define DESC_FLAG_SUSPEND       (1 << 1)
-#define DESC_FLAG_OFFSET        (1 << 2)
+#define DESC_FLAG_NONE              0
+#define DESC_FLAG_MULTI             (1 << 0)
+#define DESC_FLAG_SUSPEND           (1 << 1)
+#define DESC_FLAG_OFFSET            (1 << 2)
 
 /* Block Header Flags */
 #define SIMPLE_BLOCK_FLAGS_KEYFRAME (1 << 7)
 #define BLOCK_FLAGS_LACING          6
 
 /* Lacing Constants */
-#define LACING_NONE             0
-#define LACING_XIPH             1
-#define LACING_FIXED            2
-#define LACING_EBML             3
+#define LACING_NONE                 0
+#define LACING_XIPH                 1
+#define LACING_FIXED                2
+#define LACING_EBML                 3
 
 /* Track Types */
-#define TRACK_TYPE_VIDEO        1
-#define TRACK_TYPE_AUDIO        2
+#define TRACK_TYPE_VIDEO            1
+#define TRACK_TYPE_AUDIO            2
 
 /* Track IDs */
-#define TRACK_ID_VP8            "V_VP8"
-#define TRACK_ID_VP9            "V_VP9"
-#define TRACK_ID_VORBIS         "A_VORBIS"
-#define TRACK_ID_OPUS           "A_OPUS"
+#define TRACK_ID_VP8                "V_VP8"
+#define TRACK_ID_VP9                "V_VP9"
+#define TRACK_ID_VORBIS             "A_VORBIS"
+#define TRACK_ID_OPUS               "A_OPUS"
+
+/* Track Encryption */
+#define CONTENT_ENC_ALGO_AES        5
+#define AES_SETTINGS_CIPHER_CTR     1
+
+/* Packet Encryption */
+#define SIGNAL_BYTE_SIZE            1
+#define IV_SIZE                     8
+
+/* Signal Byte */
+#define PACKET_ENCRYPTED            1
+#define ENCRYPTED_BIT_MASK          (1 << 0)
 
 enum vint_mask {
   MASK_NONE,
   MASK_FIRST_BIT
 };
 
 struct ebml_binary {
   unsigned char * data;
@@ -218,32 +242,52 @@ struct video {
 };
 
 struct audio {
   struct ebml_type sampling_frequency;
   struct ebml_type channels;
   struct ebml_type bit_depth;
 };
 
+struct content_enc_aes_settings {
+  struct ebml_type aes_settings_cipher_mode;
+};
+
+struct content_encryption {
+  struct ebml_type content_enc_algo;
+  struct ebml_type content_enc_key_id;
+  struct ebml_list content_enc_aes_settings;
+};
+
+struct content_encoding {
+  struct ebml_type content_encoding_type;
+  struct ebml_list content_encryption;
+};
+
+struct content_encodings {
+  struct ebml_list content_encoding;
+};
+
 struct track_entry {
   struct ebml_type number;
   struct ebml_type uid;
   struct ebml_type type;
   struct ebml_type flag_enabled;
   struct ebml_type flag_default;
   struct ebml_type flag_lacing;
   struct ebml_type track_timecode_scale;
   struct ebml_type language;
   struct ebml_type codec_id;
   struct ebml_type codec_private;
   struct ebml_type codec_delay;
   struct ebml_type seek_preroll;
   struct ebml_type default_duration;
   struct video video;
   struct audio audio;
+  struct content_encodings content_encodings;
 };
 
 struct tracks {
   struct ebml_list track_entry;
 };
 
 struct cue_track_positions {
   struct ebml_type track;
@@ -280,19 +324,26 @@ struct list_node {
 
 struct saved_state {
   int64_t stream_offset;
   uint64_t last_id;
   uint64_t last_size;
   int last_valid;
 };
 
+struct frame_encryption {
+  unsigned char * iv;
+  size_t length;
+  uint8_t signal_byte;
+};
+
 struct frame {
   unsigned char * data;
   size_t length;
+  struct frame_encryption * frame_encryption;
   struct frame * next;
 };
 
 struct block_additional {
   unsigned int id;
   unsigned char * data;
   size_t length;
   struct block_additional * next;
@@ -404,32 +455,56 @@ static struct ebml_element_desc ne_video
 
 static struct ebml_element_desc ne_audio_elements[] = {
   E_FIELD(ID_SAMPLING_FREQUENCY, TYPE_FLOAT, struct audio, sampling_frequency),
   E_FIELD(ID_CHANNELS, TYPE_UINT, struct audio, channels),
   E_FIELD(ID_BIT_DEPTH, TYPE_UINT, struct audio, bit_depth),
   E_LAST
 };
 
+static struct ebml_element_desc ne_content_enc_aes_settings_elements[] = {
+  E_FIELD(ID_AES_SETTINGS_CIPHER_MODE, TYPE_UINT, struct content_enc_aes_settings, aes_settings_cipher_mode),
+  E_LAST
+};
+
+static struct ebml_element_desc ne_content_encryption_elements[] = {
+  E_FIELD(ID_CONTENT_ENC_ALGO, TYPE_UINT, struct content_encryption, content_enc_algo),
+  E_FIELD(ID_CONTENT_ENC_KEY_ID, TYPE_BINARY, struct content_encryption, content_enc_key_id),
+  E_MASTER(ID_CONTENT_ENC_AES_SETTINGS, TYPE_MASTER, struct content_encryption, content_enc_aes_settings),
+  E_LAST
+};
+
+static struct ebml_element_desc ne_content_encoding_elements[] = {
+  E_FIELD(ID_CONTENT_ENCODING_TYPE, TYPE_UINT, struct content_encoding, content_encoding_type),
+  E_MASTER(ID_CONTENT_ENCRYPTION, TYPE_MASTER, struct content_encoding, content_encryption),
+  E_LAST
+};
+
+static struct ebml_element_desc ne_content_encodings_elements[] = {
+  E_MASTER(ID_CONTENT_ENCODING, TYPE_MASTER, struct content_encodings, content_encoding),
+  E_LAST
+};
+
 static struct ebml_element_desc ne_track_entry_elements[] = {
   E_FIELD(ID_TRACK_NUMBER, TYPE_UINT, struct track_entry, number),
   E_FIELD(ID_TRACK_UID, TYPE_UINT, struct track_entry, uid),
   E_FIELD(ID_TRACK_TYPE, TYPE_UINT, struct track_entry, type),
   E_FIELD(ID_FLAG_ENABLED, TYPE_UINT, struct track_entry, flag_enabled),
   E_FIELD(ID_FLAG_DEFAULT, TYPE_UINT, struct track_entry, flag_default),
   E_FIELD(ID_FLAG_LACING, TYPE_UINT, struct track_entry, flag_lacing),
   E_FIELD(ID_TRACK_TIMECODE_SCALE, TYPE_FLOAT, struct track_entry, track_timecode_scale),
   E_FIELD(ID_LANGUAGE, TYPE_STRING, struct track_entry, language),
   E_FIELD(ID_CODEC_ID, TYPE_STRING, struct track_entry, codec_id),
   E_FIELD(ID_CODEC_PRIVATE, TYPE_BINARY, struct track_entry, codec_private),
   E_FIELD(ID_CODEC_DELAY, TYPE_UINT, struct track_entry, codec_delay),
   E_FIELD(ID_SEEK_PREROLL, TYPE_UINT, struct track_entry, seek_preroll),
   E_FIELD(ID_DEFAULT_DURATION, TYPE_UINT, struct track_entry, default_duration),
   E_SINGLE_MASTER(ID_VIDEO, TYPE_MASTER, struct track_entry, video),
   E_SINGLE_MASTER(ID_AUDIO, TYPE_MASTER, struct track_entry, audio),
+  E_SINGLE_MASTER(ID_CONTENT_ENCODINGS, TYPE_MASTER, struct track_entry, content_encodings),
   E_LAST
 };
 
 static struct ebml_element_desc ne_tracks_elements[] = {
   E_MASTER(ID_TRACK_ENTRY, TYPE_MASTER, struct tracks, track_entry),
   E_LAST
 };
 
@@ -1068,16 +1143,65 @@ ne_parse(nestegg * ctx, struct ebml_elem
   if (r != 1)
     while (ctx->ancestor)
       ne_ctx_pop(ctx);
 
   return r;
 }
 
 static int
+ne_read_block_encryption(nestegg * ctx, struct track_entry const * entry,
+                         uint64_t * encoding_type, uint64_t * encryption_algo,
+                         uint64_t * encryption_mode)
+{
+  struct content_encoding * encoding;
+  struct content_encryption * encryption;
+  struct content_enc_aes_settings * aes_settings;
+
+  *encoding_type = 0;
+  if (entry->content_encodings.content_encoding.head) {
+    encoding = entry->content_encodings.content_encoding.head->data;
+    if (ne_get_uint(encoding->content_encoding_type, encoding_type) != 0)
+      return -1;
+
+    if (*encoding_type == NESTEGG_ENCODING_ENCRYPTION) {
+      /* Metadata states content is encrypted */
+      if (!encoding->content_encryption.head)
+        return -1;
+
+      encryption = encoding->content_encryption.head->data;
+      if (ne_get_uint(encryption->content_enc_algo, encryption_algo) != 0) {
+        ctx->log(ctx, NESTEGG_LOG_ERROR, "No ContentEncAlgo element found");
+        return -1;
+      }
+
+      if (*encryption_algo != CONTENT_ENC_ALGO_AES) {
+        ctx->log(ctx, NESTEGG_LOG_ERROR, "Disallowed ContentEncAlgo used");
+        return -1;
+      }
+
+      if (!encryption->content_enc_aes_settings.head) {
+        ctx->log(ctx, NESTEGG_LOG_ERROR, "No ContentEncAESSettings element found");
+        return -1;
+      }
+
+      aes_settings = encryption->content_enc_aes_settings.head->data;
+      *encryption_mode = AES_SETTINGS_CIPHER_CTR;
+      ne_get_uint(aes_settings->aes_settings_cipher_mode, encryption_mode);
+
+      if (*encryption_mode != AES_SETTINGS_CIPHER_CTR) {
+        ctx->log(ctx, NESTEGG_LOG_ERROR, "Disallowed AESSettingsCipherMode used");
+        return -1;
+      }
+    }
+  }
+  return 1;
+}
+
+static int
 ne_read_xiph_lace_value(nestegg_io * io, uint64_t * value, size_t * consumed)
 {
   int r;
   uint64_t lace;
 
   r = ne_read_uint(io, &lace, 1);
   if (r != 1)
     return r;
@@ -1219,20 +1343,21 @@ static int
 ne_read_block(nestegg * ctx, uint64_t block_id, uint64_t block_size, nestegg_packet ** data)
 {
   int r;
   int64_t timecode, abs_timecode;
   nestegg_packet * pkt;
   struct frame * f, * last;
   struct track_entry * entry;
   double track_scale;
-  uint64_t track_number, length, frame_sizes[256], cluster_tc, flags, frames, tc_scale, total;
+  uint64_t track_number, length, frame_sizes[256], cluster_tc, flags, frames, tc_scale, total,
+           encoding_type, encryption_algo, encryption_mode;
   unsigned int i, lacing, track;
-  uint8_t keyframe = NESTEGG_PACKET_HAS_KEYFRAME_UNKNOWN;
-  size_t consumed = 0;
+  uint8_t signal_byte, keyframe = NESTEGG_PACKET_HAS_KEYFRAME_UNKNOWN;
+  size_t consumed = 0, data_size, encryption_size;
 
   *data = NULL;
 
   if (block_size > LIMIT_BLOCK)
     return -1;
 
   r = ne_read_vint(ctx->io, &track_number, &length);
   if (r != 1)
@@ -1319,16 +1444,26 @@ ne_read_block(nestegg * ctx, uint64_t bl
 
   if (ne_map_track_number_to_index(ctx, track_number, &track) != 0)
     return -1;
 
   entry = ne_find_track_entry(ctx, track);
   if (!entry)
     return -1;
 
+  r = ne_read_block_encryption(ctx, entry, &encoding_type, &encryption_algo, &encryption_mode);
+  if (r != 1)
+    return r;
+
+  /* Encryption does not support lacing */
+  if (lacing != LACING_NONE && encoding_type == NESTEGG_ENCODING_ENCRYPTION) {
+    ctx->log(ctx, NESTEGG_LOG_ERROR, "Encrypted blocks may not also be laced");
+    return -1;
+  }
+
   track_scale = 1.0;
 
   tc_scale = ne_get_timecode_scale(ctx);
 
   if (!ctx->read_cluster_timecode)
     return -1;
   cluster_tc = ctx->cluster_timecode;
 
@@ -1352,25 +1487,74 @@ ne_read_block(nestegg * ctx, uint64_t bl
       nestegg_free_packet(pkt);
       return -1;
     }
     f = ne_alloc(sizeof(*f));
     if (!f) {
       nestegg_free_packet(pkt);
       return -1;
     }
-    f->data = ne_alloc(frame_sizes[i]);
+    /* Parse encryption */
+    if (encoding_type == NESTEGG_ENCODING_ENCRYPTION) {
+      r = ne_io_read(ctx->io, &signal_byte, SIGNAL_BYTE_SIZE);
+      if (r != 1) {
+        free(f);
+        nestegg_free_packet(pkt);
+        return r;
+      }
+      f->frame_encryption = ne_alloc(sizeof(*f->frame_encryption));
+      if (!f->frame_encryption) {
+        free(f);
+        nestegg_free_packet(pkt);
+        return -1;
+      }
+      f->frame_encryption->signal_byte = signal_byte;
+      if ((signal_byte & ENCRYPTED_BIT_MASK) == PACKET_ENCRYPTED) {
+        f->frame_encryption->iv = ne_alloc(IV_SIZE);
+        if (!f->frame_encryption->iv) {
+          free(f->frame_encryption);
+          free(f);
+          nestegg_free_packet(pkt);
+          return -1;
+        }
+        r = ne_io_read(ctx->io, f->frame_encryption->iv, IV_SIZE);
+        if (r != 1) {
+          free(f->frame_encryption);
+          free(f);
+          nestegg_free_packet(pkt);
+          return r;
+        }
+        f->frame_encryption->length = IV_SIZE;
+        encryption_size = SIGNAL_BYTE_SIZE + IV_SIZE;
+      } else {
+        f->frame_encryption->iv = NULL;
+        f->frame_encryption->length = 0;
+        encryption_size = SIGNAL_BYTE_SIZE;
+      }
+    } else {
+      f->frame_encryption = NULL;
+      encryption_size = 0;
+    }
+    data_size = frame_sizes[i] - encryption_size;
+    /* Encryption parsed */
+    f->data = ne_alloc(data_size);
     if (!f->data) {
+      if (f->frame_encryption)
+        free(f->frame_encryption->iv);
+      free(f->frame_encryption);
       free(f);
       nestegg_free_packet(pkt);
       return -1;
     }
-    f->length = frame_sizes[i];
-    r = ne_io_read(ctx->io, f->data, frame_sizes[i]);
+    f->length = data_size;
+    r = ne_io_read(ctx->io, f->data, data_size);
     if (r != 1) {
+      if (f->frame_encryption)
+        free(f->frame_encryption->iv);
+      free(f->frame_encryption);
       free(f->data);
       free(f);
       nestegg_free_packet(pkt);
       return r;
     }
 
     if (!last)
       pkt->frame = f;
@@ -2297,16 +2481,118 @@ nestegg_track_audio_params(nestegg * ctx
   value = 0;
   ne_get_uint(entry->seek_preroll, &value);
   params->seek_preroll = value;
 
   return 0;
 }
 
 int
+nestegg_track_encoding(nestegg * ctx, unsigned int track)
+{
+  struct track_entry * entry;
+  struct content_encoding * encoding;
+  uint64_t encoding_value;
+
+  entry = ne_find_track_entry(ctx, track);
+  if (!entry) {
+    ctx->log(ctx, NESTEGG_LOG_ERROR, "No track entry found");
+    return -1;
+  }
+
+  if (!entry->content_encodings.content_encoding.head) {
+    /* Default encoding is compression */
+    return NESTEGG_ENCODING_COMPRESSION;
+  }
+
+  encoding = entry->content_encodings.content_encoding.head->data;
+
+  encoding_value = NESTEGG_ENCODING_COMPRESSION;
+  ne_get_uint(encoding->content_encoding_type, &encoding_value);
+  if (encoding_value != NESTEGG_ENCODING_COMPRESSION && encoding_value != NESTEGG_ENCODING_ENCRYPTION) {
+    ctx->log(ctx, NESTEGG_LOG_ERROR, "Invalid ContentEncoding element found");
+    return -1;
+  }
+
+  return encoding_value;
+}
+
+int
+nestegg_track_content_enc_key_id(nestegg * ctx, unsigned int track, unsigned char const ** content_enc_key_id,
+                                 size_t * content_enc_key_id_length)
+{
+  struct track_entry * entry;
+  struct content_encoding * encoding;
+  struct content_encryption * encryption;
+  struct content_enc_aes_settings * aes_settings;
+  struct nestegg_encryption_params;
+  uint64_t value;
+  struct ebml_binary enc_key_id;
+
+  entry = ne_find_track_entry(ctx, track);
+  if (!entry) {
+    ctx->log(ctx, NESTEGG_LOG_ERROR, "No track entry found");
+    return -1;
+  }
+
+  if (!entry->content_encodings.content_encoding.head) {
+    ctx->log(ctx, NESTEGG_LOG_ERROR, "No ContentEncoding element found");
+    return -1;
+  }
+
+  encoding = entry->content_encodings.content_encoding.head->data;
+
+  value = 0;
+  ne_get_uint(encoding->content_encoding_type, &value);
+  if (value != NESTEGG_ENCODING_ENCRYPTION) {
+    ctx->log(ctx, NESTEGG_LOG_ERROR, "Disallowed ContentEncodingType found");
+    return -1;
+  }
+
+  if (!encoding->content_encryption.head) {
+    ctx->log(ctx, NESTEGG_LOG_ERROR, "No ContentEncryption element found");
+    return -1;
+  }
+
+  encryption = encoding->content_encryption.head->data;
+
+  value = 0;
+  ne_get_uint(encryption->content_enc_algo, &value);
+
+  if (value != CONTENT_ENC_ALGO_AES) {
+    ctx->log(ctx, NESTEGG_LOG_ERROR, "Disallowed ContentEncAlgo found");
+    return -1;
+  }
+
+  if (!encryption->content_enc_aes_settings.head) {
+    ctx->log(ctx, NESTEGG_LOG_ERROR, "No ContentEncAesSettings element found");
+    return -1;
+  }
+
+  aes_settings = encryption->content_enc_aes_settings.head->data;
+  value = AES_SETTINGS_CIPHER_CTR;
+  ne_get_uint(aes_settings->aes_settings_cipher_mode, &value);
+
+  if (value != AES_SETTINGS_CIPHER_CTR) {
+    ctx->log(ctx, NESTEGG_LOG_ERROR, "Disallowed AESSettingCipherMode used");
+    return -1;
+  }
+
+  if (ne_get_binary(encryption->content_enc_key_id, &enc_key_id) != 0) {
+    ctx->log(ctx, NESTEGG_LOG_ERROR, "Could not retrieve track ContentEncKeyId");
+    return -1;
+  }
+
+  *content_enc_key_id = enc_key_id.data;
+  *content_enc_key_id_length = enc_key_id.length;
+
+  return 0;
+}
+
+int
 nestegg_track_default_duration(nestegg * ctx, unsigned int track,
                                uint64_t * duration)
 {
   struct track_entry * entry;
   uint64_t value;
 
   entry = ne_find_track_entry(ctx, track);
   if (!entry)
@@ -2471,16 +2757,20 @@ void
 nestegg_free_packet(nestegg_packet * pkt)
 {
   struct frame * frame;
   struct block_additional * block_additional;
 
   while (pkt->frame) {
     frame = pkt->frame;
     pkt->frame = frame->next;
+    if (frame->frame_encryption) {
+      free(frame->frame_encryption->iv);
+    }
+    free(frame->frame_encryption);
     free(frame->data);
     free(frame);
   }
 
   while (pkt->block_additional) {
     block_additional = pkt->block_additional;
     pkt->block_additional = block_additional->next;
     free(block_additional->data);
@@ -2592,16 +2882,60 @@ nestegg_packet_additional_data(nestegg_p
     }
     a = a->next;
   }
 
   return -1;
 }
 
 int
+nestegg_packet_encryption(nestegg_packet * pkt)
+{
+  struct frame * f = pkt->frame;
+  unsigned char encrypted_bit;
+
+  if (!f->frame_encryption)
+    return NESTEGG_PACKET_HAS_SIGNAL_BYTE_FALSE;
+
+  /* Should never have parsed blocks with both encryption and lacing */
+  assert(f->next == NULL);
+
+  encrypted_bit = f->frame_encryption->signal_byte & ENCRYPTED_BIT_MASK;
+
+  if (encrypted_bit != PACKET_ENCRYPTED)
+    return NESTEGG_PACKET_HAS_SIGNAL_BYTE_UNENCRYPTED;
+
+  return NESTEGG_PACKET_HAS_SIGNAL_BYTE_ENCRYPTED;
+}
+
+int
+nestegg_packet_iv(nestegg_packet * pkt, unsigned char const ** iv, size_t * length)
+{
+  struct frame * f = pkt->frame;
+  unsigned char encrypted_bit;
+
+  *iv = NULL;
+  *length = 0;
+  if (!f->frame_encryption)
+    return -1;
+
+  /* Should never have parsed blocks with both encryption and lacing */
+  assert(f->next == NULL);
+
+  encrypted_bit = f->frame_encryption->signal_byte & ENCRYPTED_BIT_MASK;
+
+  if (encrypted_bit != PACKET_ENCRYPTED)
+    return 0;
+
+  *iv = f->frame_encryption->iv;
+  *length = f->frame_encryption->length;
+  return 0;
+}
+
+int
 nestegg_has_cues(nestegg * ctx)
 {
   return ctx->segment.cues.cue_point.head ||
     ne_find_seek_for_id(ctx->segment.seek_head.head, ID_CUES);
 }
 
 int
 nestegg_sniff(unsigned char const * buffer, size_t length)
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -313,17 +313,17 @@ pref("media.mp4.enabled", true);
 // decoder works on all platforms.
 pref("media.use-blank-decoder", false);
 #ifdef MOZ_WMF
 pref("media.wmf.enabled", true);
 pref("media.wmf.decoder.thread-count", -1);
 pref("media.wmf.low-latency.enabled", false);
 pref("media.wmf.skip-blacklist", false);
 pref("media.windows-media-foundation.allow-d3d11-dxva", true);
-pref("media.wmf.disable-d3d11-for-dlls", "igd10iumd32.dll: 20.19.15.4444, 20.19.15.4424, 20.19.15.4409, 20.19.15.4390, 20.19.15.4380, 20.19.15.4360, 10.18.10.4358, 20.19.15.4331, 20.19.15.4312, 20.19.15.4300, 10.18.15.4281, 10.18.15.4279, 10.18.10.4276, 10.18.15.4268, 10.18.15.4256, 10.18.10.4252, 10.18.14.4112, 10.18.10.3431, 10.18.10.3412, 10.18.10.3355, 9.18.10.3234, 9.18.10.3071, 9.18.10.3055; igd10umd32.dll: 9.17.10.4229, 9.17.10.2857, 8.15.10.2274, 8.15.10.2272, 8.15.10.2246, 8.15.10.1840, 8.15.10.1808; igd10umd64.dll: 9.17.10.4229, 10.18.10.3496; isonyvideoprocessor.dll: 4.1.2247.8090, 4.1.2153.6200; tosqep.dll: 1.2.15.526, 1.1.12.201, 1.0.11.318, 1.0.11.215, 1.0.10.1224; tosqep64.dll: 1.1.12.201, 1.0.11.215; nvwgf2um.dll: 10.18.13.5891, 10.18.13.5887, 10.18.13.5582, 10.18.13.5382, 9.18.13.3165; atidxx32.dll: 8.17.10.671, 8.17.10.661, 8.17.10.648, 8.17.10.644, 8.17.10.625, 8.17.10.605, 8.17.10.539, 8.17.10.525, 8.17.10.519, 8.17.10.511, 8.17.10.511, 8.17.10.451; atidxx64.dll: 8.17.10.661, 8.17.10.644");
+pref("media.wmf.disable-d3d11-for-dlls", "igd10iumd32.dll: 20.19.15.4444, 20.19.15.4424, 20.19.15.4409, 20.19.15.4390, 20.19.15.4380, 20.19.15.4360, 10.18.10.4358, 20.19.15.4331, 20.19.15.4312, 20.19.15.4300, 10.18.15.4281, 10.18.15.4279, 10.18.10.4276, 10.18.15.4268, 10.18.15.4256, 10.18.10.4252, 10.18.15.4248, 10.18.14.4112, 10.18.10.3958, 10.18.10.3496, 10.18.10.3431, 10.18.10.3412, 10.18.10.3355, 9.18.10.3234, 9.18.10.3071, 9.18.10.3055, 9.18.10.3006; igd10umd32.dll: 9.17.10.4229, 9.17.10.3040, 9.17.10.2857, 8.15.10.2274, 8.15.10.2272, 8.15.10.2246, 8.15.10.1840, 8.15.10.1808; igd10umd64.dll: 9.17.10.4229, 10.18.10.3496; isonyvideoprocessor.dll: 4.1.2247.8090, 4.1.2153.6200; tosqep.dll: 1.2.15.526, 1.1.12.201, 1.0.11.318, 1.0.11.215, 1.0.10.1224; tosqep64.dll: 1.1.12.201, 1.0.11.215; nvwgf2um.dll: 10.18.13.6510, 10.18.13.5891, 10.18.13.5887, 10.18.13.5582, 10.18.13.5382, 9.18.13.4195, 9.18.13.3165; atidxx32.dll: 8.17.10.671, 8.17.10.661, 8.17.10.648, 8.17.10.644, 8.17.10.625, 8.17.10.605, 8.17.10.539, 8.17.10.531, 8.17.10.525, 8.17.10.519, 8.17.10.511, 8.17.10.511, 8.17.10.453, 8.17.10.451; atidxx64.dll: 8.17.10.661, 8.17.10.644");
 #endif
 #if defined(MOZ_FFMPEG)
 #if defined(XP_MACOSX)
 pref("media.ffmpeg.enabled", false);
 #else
 pref("media.ffmpeg.enabled", true);
 #endif
 #endif
@@ -379,16 +379,17 @@ pref("media.navigator.video.enabled", tr
 pref("media.navigator.load_adapt", true);
 pref("media.navigator.load_adapt.measure_interval",1000);
 pref("media.navigator.load_adapt.avg_seconds",3);
 pref("media.navigator.load_adapt.high_load","0.90");
 pref("media.navigator.load_adapt.low_load","0.40");
 pref("media.navigator.video.default_fps",30);
 pref("media.navigator.video.default_minfps",10);
 pref("media.navigator.video.use_remb", true);
+pref("media.navigator.video.use_tmmbr", true);
 
 
 pref("media.webrtc.debug.trace_mask", 0);
 pref("media.webrtc.debug.multi_log", false);
 pref("media.webrtc.debug.aec_log_dir", "");
 pref("media.webrtc.debug.log_file", "");
 pref("media.webrtc.debug.aec_dump_max_size", 4194304); // 4MB
 
@@ -2424,17 +2425,17 @@ pref("layout.css.convertFromNode.enabled
 pref("layout.css.convertFromNode.enabled", true);
 #endif
 
 // Is support for CSS "text-align: unsafe X" enabled?
 pref("layout.css.text-align-unsafe-value.enabled", false);
 
 // Is support for CSS "float: inline-{start,end}" and
 // "clear: inline-{start,end}" enabled?
-#if defined(MOZ_B2G) || defined(NIGHTLY_BUILD)
+#if defined(MOZ_B2G) || !defined(RELEASE_BUILD)
 pref("layout.css.float-logical-values.enabled", true);
 #else
 pref("layout.css.float-logical-values.enabled", false);
 #endif
 
 // Is support for the CSS4 image-orientation property enabled?
 pref("layout.css.image-orientation.enabled", true);
 
--- a/moz.configure
+++ b/moz.configure
@@ -101,32 +101,47 @@ include(memory_include)
 
 
 @depends('--help')
 @imports(_from='mozbuild.backend', _import='backends')
 def build_backends_choices(help):
     return tuple(backends)
 
 
-option('--enable-build-backend', nargs='+', choices=build_backends_choices,
-       help='Enable additional build backends')
+@deprecated_option('--enable-build-backend', nargs='+',
+                   choices=build_backends_choices)
+def build_backend(backends):
+    if backends:
+        return tuple('+%s' % b for b in backends)
 
-@depends('--enable-build-backend', '--enable-artifact-builds', target,
-         compile_environment)
-def build_backend(backends, artifact_builds, target, compile_environment):
+imply_option('--build-backends', build_backend)
+
+
+@depends('--enable-artifact-builds', '--disable-compile-environment', '--help')
+@imports('sys')
+def build_backend_defaults(artifact_builds, compile_environment, _):
     if artifact_builds:
         all_backends = ['FasterMake+RecursiveMake']
     else:
         all_backends = ['RecursiveMake', 'FasterMake']
-    if target.os == 'WINNT' and compile_environment:
+    # Normally, we'd use target.os == 'WINNT', but a dependency on target
+    # would require target to depend on --help, as well as host and shell,
+    # and this is not a can of worms we can open at the moment.
+    if sys.platform == 'win32' and compile_environment:
         all_backends.append('VisualStudio')
-    all_backends.extend(backends)
-    return unique_list(all_backends)
+    return tuple(all_backends)
+
+option('--build-backends', nargs='+', default=build_backend_defaults,
+       choices=build_backends_choices, help='Build backends to generate')
 
-set_config('BUILD_BACKENDS', build_backend)
+@depends('--build-backends')
+def build_backends(backends):
+    return backends
+
+set_config('BUILD_BACKENDS', build_backends)
 
 
 # Awk detection
 # ==============================================================
 awk = check_prog('AWK', ('gawk', 'mawk', 'nawk', 'awk'))
 
 # Until the AWK variable is not necessary in old-configure
 @depends(awk)
--- a/netwerk/cookie/nsCookieService.cpp
+++ b/netwerk/cookie/nsCookieService.cpp
@@ -2255,26 +2255,98 @@ nsCookieService::GetEnumerator(nsISimple
     for (nsCookieEntry::IndexType i = 0; i < cookies.Length(); ++i) {
       cookieList.AppendObject(cookies[i]);
     }
   }
 
   return NS_NewArrayEnumerator(aEnumerator, cookieList);
 }
 
+static nsresult
+InitializeOriginAttributes(NeckoOriginAttributes* aAttrs,
+                           JS::HandleValue aOriginAttributes,
+                           JSContext* aCx,
+                           uint8_t aArgc,
+                           const char16_t* aAPI,
+                           const char16_t* aInterfaceSuffix)
+{
+  MOZ_ASSERT(aAttrs);
+  MOZ_ASSERT(aCx);
+  MOZ_ASSERT(aAPI);
+  MOZ_ASSERT(aInterfaceSuffix);
+
+  if (aArgc == 0) {
+    const char16_t* params[] = {
+      aAPI,
+      aInterfaceSuffix
+    };
+
+    // This is supposed to be temporary and in 1 or 2 releases we want to
+    // have originAttributes param as mandatory. But for now, we don't want to
+    // break existing addons, so we write a console message to inform the addon
+    // developers about it.
+    nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
+                                    NS_LITERAL_CSTRING("Cookie Manager"),
+                                    nullptr,
+                                    nsContentUtils::eNECKO_PROPERTIES,
+                                    "nsICookieManagerAPIDeprecated",
+                                    params, ArrayLength(params));
+  } else if (aArgc == 1) {
+    if (!aOriginAttributes.isObject() ||
+        !aAttrs->Init(aCx, aOriginAttributes)) {
+      return NS_ERROR_INVALID_ARG;
+    }
+  }
+
+  return NS_OK;
+}
+
 NS_IMETHODIMP
 nsCookieService::Add(const nsACString &aHost,
                      const nsACString &aPath,
                      const nsACString &aName,
                      const nsACString &aValue,
                      bool              aIsSecure,
                      bool              aIsHttpOnly,
                      bool              aIsSession,
-                     int64_t           aExpiry)
+                     int64_t           aExpiry,
+                     JS::HandleValue   aOriginAttributes,
+                     JSContext*        aCx,
+                     uint8_t           aArgc)
 {
+  MOZ_ASSERT(aArgc == 0 || aArgc == 1);
+
+  NeckoOriginAttributes attrs;
+  nsresult rv = InitializeOriginAttributes(&attrs,
+                                           aOriginAttributes,
+                                           aCx,
+                                           aArgc,
+                                           MOZ_UTF16("nsICookieManager2.add()"),
+                                           MOZ_UTF16("2"));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return AddNative(aHost, aPath, aName, aValue, aIsSecure, aIsHttpOnly,
+                   aIsSession, aExpiry, &attrs);
+}
+
+NS_IMETHODIMP_(nsresult)
+nsCookieService::AddNative(const nsACString &aHost,
+                           const nsACString &aPath,
+                           const nsACString &aName,
+                           const nsACString &aValue,
+                           bool              aIsSecure,
+                           bool              aIsHttpOnly,
+                           bool              aIsSession,
+                           int64_t           aExpiry,
+                           NeckoOriginAttributes* aOriginAttributes)
+{
+  if (NS_WARN_IF(!aOriginAttributes)) {
+    return NS_ERROR_FAILURE;
+  }
+
   if (!mDBState) {
     NS_WARNING("No DBState! Profile already closed?");
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   // first, normalize the hostname, and fail if it contains illegal characters.
   nsAutoCString host(aHost);
   nsresult rv = NormalizeHost(host);
@@ -2282,17 +2354,17 @@ nsCookieService::Add(const nsACString &a
 
   // get the base domain for the host URI.
   // e.g. for "www.bbc.co.uk", this would be "bbc.co.uk".
   nsAutoCString baseDomain;
   rv = GetBaseDomainFromHost(host, baseDomain);
   NS_ENSURE_SUCCESS(rv, rv);
 
   int64_t currentTimeInUsec = PR_Now();
-  nsCookieKey key = DEFAULT_APP_KEY(baseDomain);
+  nsCookieKey key = nsCookieKey(baseDomain, *aOriginAttributes);
 
   RefPtr<nsCookie> cookie =
     nsCookie::Create(aName, aValue, host, aPath,
                      aExpiry,
                      currentTimeInUsec,
                      nsCookie::GenerateUniqueCreationTime(currentTimeInUsec),
                      aIsSession,
                      aIsSecure,
@@ -2365,34 +2437,26 @@ nsCookieService::Remove(const nsACString
                         const nsACString &aName,
                         const nsACString &aPath,
                         bool             aBlocked,
                         JS::HandleValue  aOriginAttributes,
                         JSContext*       aCx,
                         uint8_t          aArgc)
 {
   MOZ_ASSERT(aArgc == 0 || aArgc == 1);
-  if (aArgc == 0) {
-    // This is supposed to be temporary and in 1 or 2 releases we want to
-    // have originAttributes param as mandatory. But for now, we don't want to
-    // break existing addons, so we write a console message to inform the addon
-    // developers about it.
-    nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
-                                    NS_LITERAL_CSTRING("Cookie Manager"),
-                                    nullptr,
-                                    nsContentUtils::eNECKO_PROPERTIES,
-                                    "nsICookieManagerRemoveDeprecated");
-  }
 
   NeckoOriginAttributes attrs;
-  if (aArgc == 1 &&
-      (!aOriginAttributes.isObject() ||
-       !attrs.Init(aCx, aOriginAttributes))) {
-    return NS_ERROR_INVALID_ARG;
-  }
+  nsresult rv = InitializeOriginAttributes(&attrs,
+                                           aOriginAttributes,
+                                           aCx,
+                                           aArgc,
+                                           MOZ_UTF16("nsICookieManager.remove()"),
+                                           MOZ_UTF16(""));
+  NS_ENSURE_SUCCESS(rv, rv);
+
   return RemoveNative(aHost, aName, aPath, aBlocked, &attrs);
 }
 
 NS_IMETHODIMP_(nsresult)
 nsCookieService::RemoveNative(const nsACString &aHost,
                               const nsACString &aName,
                               const nsACString &aPath,
                               bool aBlocked,
@@ -4295,33 +4359,47 @@ nsCookieService::CountCookiesFromHost(co
   *aCountFromHost = entry ? entry->GetCookies().Length() : 0;
   return NS_OK;
 }
 
 // get an enumerator of cookies stored by a particular host. this is provided by the
 // nsICookieManager2 interface.
 NS_IMETHODIMP
 nsCookieService::GetCookiesFromHost(const nsACString     &aHost,
+                                    JS::HandleValue       aOriginAttributes,
+                                    JSContext*            aCx,
+                                    uint8_t               aArgc,
                                     nsISimpleEnumerator **aEnumerator)
 {
+  MOZ_ASSERT(aArgc == 0 || aArgc == 1);
+
   if (!mDBState) {
     NS_WARNING("No DBState! Profile already closed?");
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   // first, normalize the hostname, and fail if it contains illegal characters.
   nsAutoCString host(aHost);
   nsresult rv = NormalizeHost(host);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsAutoCString baseDomain;
   rv = GetBaseDomainFromHost(host, baseDomain);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  nsCookieKey key = DEFAULT_APP_KEY(baseDomain);
+  NeckoOriginAttributes attrs;
+  rv = InitializeOriginAttributes(&attrs,
+                                  aOriginAttributes,
+                                  aCx,
+                                  aArgc,
+                                  MOZ_UTF16("nsICookieManager2.getCookiesFromHost()"),
+                                  MOZ_UTF16("2"));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCookieKey key = nsCookieKey(baseDomain, attrs);
   EnsureReadDomain(key);
 
   nsCookieEntry *entry = mDBState->hostTable.GetEntry(key);
   if (!entry)
     return NS_NewEmptyEnumerator(aEnumerator);
 
   nsCOMArray<nsICookie> cookieList(mMaxCookiesPerHost);
   const nsCookieEntry::ArrayType &cookies = entry->GetCookies();
--- a/netwerk/cookie/nsICookieManager2.idl
+++ b/netwerk/cookie/nsICookieManager2.idl
@@ -41,25 +41,40 @@ interface nsICookieManager2 : nsICookieM
    *        modified by, an http connection.
    * @param aIsSession
    *        true if the cookie should exist for the current session only.
    *        see aExpiry.
    * @param aExpiry
    *        expiration date, in seconds since midnight (00:00:00), January 1,
    *        1970 UTC. note that expiry time will also be honored for session cookies;
    *        in this way, the more restrictive of the two will take effect.
+   * @param aOriginAttributes The originAttributes of this cookie. This
+   *                          attribute is optional to avoid breaking add-ons.
    */
+  [implicit_jscontext, optional_argc]
   void add(in AUTF8String aHost,
            in AUTF8String aPath,
            in ACString    aName,
            in ACString    aValue,
            in boolean     aIsSecure,
            in boolean     aIsHttpOnly,
            in boolean     aIsSession,
-           in int64_t     aExpiry);
+           in int64_t     aExpiry,
+           [optional] in jsval aOriginAttributes);
+
+  [notxpcom]
+  nsresult addNative(in AUTF8String aHost,
+                     in AUTF8String aPath,
+                     in ACString    aName,
+                     in ACString    aValue,
+                     in boolean     aIsSecure,
+                     in boolean     aIsHttpOnly,
+                     in boolean     aIsSession,
+                     in int64_t     aExpiry,
+                     in NeckoOriginAttributesPtr aOriginAttributes);
 
   /**
    * Find whether a given cookie already exists.
    *
    * @param aCookie
    *        the cookie to look for
    *
    * @return true if a cookie was found which matches the host, path, and name
@@ -87,22 +102,27 @@ interface nsICookieManager2 : nsICookieM
    * 'aHost'. Thus, for a host "weather.yahoo.com", the base domain would be
    * "yahoo.com", and any host or domain cookies for "yahoo.com" and its
    * subdomains would be returned.
    *
    * @param aHost
    *        the host string to search for, e.g. "google.com". this should consist
    *        of only the host portion of a URI. see @add for a description of
    *        acceptable host strings.
+   * @param aOriginAttributes The originAttributes of cookies that would be
+   *                          retrived. This attribute is optional to avoid
+   *                          breaking add-ons.
    *
    * @return an nsISimpleEnumerator of nsICookie2 objects.
    *
    * @see countCookiesFromHost
    */
-  nsISimpleEnumerator getCookiesFromHost(in AUTF8String aHost);
+  [implicit_jscontext, optional_argc]
+  nsISimpleEnumerator getCookiesFromHost(in AUTF8String aHost,
+                                         [optional] in jsval aOriginAttributes);
 
   /**
    * Import an old-style cookie file. Imported cookies will be added to the
    * existing database. If the database contains any cookies the same as those
    * being imported (i.e. domain, name, and path match), they will be replaced.
    *
    * @param aCookieFile the file to import, usually cookies.txt
    */
new file mode 100644
--- /dev/null
+++ b/netwerk/cookie/test/unit/test_bug1267910.js
@@ -0,0 +1,196 @@
+/*
+ * Bug 1267910 - Add test cases for the backward compatiability and originAttributes
+ *               of nsICookieManager2.
+ */
+
+var {utils: Cu, interfaces: Ci, classes: Cc} = Components;
+
+Cu.import("resource://gre/modules/Services.jsm");
+
+const BASE_URL = "http://example.org/";
+
+const COOKIE = {
+  host: BASE_URL,
+  path: "/",
+  name: "test1",
+  value: "yes",
+  isSecure: false,
+  isHttpOnly: false,
+  isSession: true,
+  expiry: 2145934800,
+};
+
+const COOKIE_OA_DEFAULT = {
+  host: BASE_URL,
+  path: "/",
+  name: "test0",
+  value: "yes0",
+  isSecure: false,
+  isHttpOnly: false,
+  isSession: true,
+  expiry: 2145934800,
+  originAttributes: {},
+};
+
+const COOKIE_OA_1 = {
+  host: BASE_URL,
+  path: "/",
+  name: "test1",
+  value: "yes1",
+  isSecure: false,
+  isHttpOnly: false,
+  isSession: true,
+  expiry: 2145934800,
+  originAttributes: {userContextId: 1},
+};
+
+function checkCookie(cookie, cookieObj) {
+  for (let prop of Object.keys(cookieObj)) {
+    if (prop === "originAttributes") {
+      ok(ChromeUtils.isOriginAttributesEqual(cookie[prop], cookieObj[prop]),
+        "Check cookie: " + prop);
+    } else {
+      equal(cookie[prop], cookieObj[prop], "Check cookie: " + prop);
+    }
+  }
+}
+
+function countCookies(enumerator) {
+  let cnt = 0;
+
+  while (enumerator.hasMoreElements()) {
+    cnt++;
+    enumerator.getNext();
+  }
+
+  return cnt;
+}
+
+function run_test() {
+  // Allow all cookies.
+  Services.prefs.setIntPref("network.cookie.cookieBehavior", 0);
+
+  // Enable user context id
+  Services.prefs.setBoolPref("privacy.userContext.enabled", true);
+
+  add_test(test_backward_compatiability);
+  add_test(test_originAttributes);
+
+
+  run_next_test();
+}
+
+/*
+ * Test for backward compatiablility that APIs works correctly without
+ * originAttributes.
+ */
+function test_backward_compatiability() {
+  // Clear cookies.
+  Services.cookies.removeAll();
+
+  // Call Add() to add a cookie without originAttributes
+  Services.cookies.add(COOKIE.host,
+                       COOKIE.path,
+                       COOKIE.name,
+                       COOKIE.value,
+                       COOKIE.isSecure,
+                       COOKIE.isHttpOnly,
+                       COOKIE.isSession,
+                       COOKIE.expiry);
+
+  // Call getCookiesFromHost() to get cookies without originAttributes
+  let enumerator = Services.cookies.getCookiesFromHost(BASE_URL);
+
+  ok(enumerator.hasMoreElements(), "Cookies available");
+  let foundCookie = enumerator.getNext().QueryInterface(Ci.nsICookie2);
+
+  checkCookie(foundCookie, COOKIE);
+
+  ok(!enumerator.hasMoreElements(), "We should get only one cookie");
+
+  run_next_test();
+}
+
+/*
+ * Test for originAttributes.
+ */
+function test_originAttributes() {
+  // Clear cookies.
+  Services.cookies.removeAll();
+
+  // Add a cookie for default originAttributes.
+  Services.cookies.add(COOKIE_OA_DEFAULT.host,
+                       COOKIE_OA_DEFAULT.path,
+                       COOKIE_OA_DEFAULT.name,
+                       COOKIE_OA_DEFAULT.value,
+                       COOKIE_OA_DEFAULT.isSecure,
+                       COOKIE_OA_DEFAULT.isHttpOnly,
+                       COOKIE_OA_DEFAULT.isSession,
+                       COOKIE_OA_DEFAULT.expiry,
+                       COOKIE_OA_DEFAULT.originAttributes);
+
+  // Get cookies for default originAttributes.
+  let enumerator = Services.cookies.getCookiesFromHost(BASE_URL, COOKIE_OA_DEFAULT.originAttributes);
+
+  // Check that do we get cookie correctly.
+  ok(enumerator.hasMoreElements(), "Cookies available");
+  let foundCookie = enumerator.getNext().QueryInterface(Ci.nsICookie2);
+  checkCookie(foundCookie, COOKIE_OA_DEFAULT);
+
+  // We should only get one cookie.
+  ok(!enumerator.hasMoreElements(), "We should get only one cookie");
+
+  // Get cookies for originAttributes with user context id 1.
+  enumerator = Services.cookies.getCookiesFromHost(BASE_URL, COOKIE_OA_1.originAttributes);
+
+  // Check that we will not get cookies if the originAttributes is different.
+  ok(!enumerator.hasMoreElements(), "No cookie should be here");
+
+  // Add a cookie for originAttributes with user context id 1.
+  Services.cookies.add(COOKIE_OA_1.host,
+                       COOKIE_OA_1.path,
+                       COOKIE_OA_1.name,
+                       COOKIE_OA_1.value,
+                       COOKIE_OA_1.isSecure,
+                       COOKIE_OA_1.isHttpOnly,
+                       COOKIE_OA_1.isSession,
+                       COOKIE_OA_1.expiry,
+                       COOKIE_OA_1.originAttributes);
+
+  // Get cookies for originAttributes with user context id 1.
+  enumerator = Services.cookies.getCookiesFromHost(BASE_URL, COOKIE_OA_1.originAttributes);
+
+  // Check that do we get cookie correctly.
+  ok(enumerator.hasMoreElements(), "Cookies available");
+  foundCookie = enumerator.getNext().QueryInterface(Ci.nsICookie2);
+  checkCookie(foundCookie, COOKIE_OA_1);
+
+  // We should only get one cookie.
+  ok(!enumerator.hasMoreElements(), "We should get only one cookie");
+
+  // Check that add a cookie will not affect cookies in different originAttributes.
+  enumerator = Services.cookies.getCookiesFromHost(BASE_URL, COOKIE_OA_DEFAULT.originAttributes);
+  equal(countCookies(enumerator), 1, "We should get only one cookie for default originAttributes");
+
+  // Remove a cookie for originAttributes with user context id 1.
+  Services.cookies.remove(COOKIE_OA_1.host, COOKIE_OA_1.name, COOKIE_OA_1.path,
+                          false, COOKIE_OA_1.originAttributes);
+
+  // Check that remove will not affect cookies in default originAttributes.
+  enumerator = Services.cookies.getCookiesFromHost(BASE_URL, COOKIE_OA_DEFAULT.originAttributes);
+  equal(countCookies(enumerator), 1, "Get one cookie for default originAttributes.");
+
+  // Check that should be no cookie for originAttributes with user context id 1.
+  enumerator = Services.cookies.getCookiesFromHost(BASE_URL, COOKIE_OA_1.originAttributes);
+  equal(countCookies(enumerator), 0, "No cookie shold be here");
+
+  // Remove a cookie for default originAttributes.
+  Services.cookies.remove(COOKIE_OA_DEFAULT.host, COOKIE_OA_DEFAULT.name, COOKIE_OA_DEFAULT.path,
+                          false, COOKIE_OA_DEFAULT.originAttributes);
+
+  // Check remove() works correctly for default originAttributes.
+  enumerator = Services.cookies.getCookiesFromHost(BASE_URL, COOKIE_OA_DEFAULT.originAttributes);
+  equal(countCookies(enumerator), 0, "No cookie shold be here");
+
+  run_next_test();
+}
--- a/netwerk/cookie/test/unit/xpcshell.ini
+++ b/netwerk/cookie/test/unit/xpcshell.ini
@@ -1,9 +1,10 @@
 [DEFAULT]
 head = 
 tail = 
 skip-if = toolkit == 'gonk'
 
 [test_bug643051.js]
 [test_bug1155169.js]
+[test_bug1267910.js]
 [test_parser_0001.js]
 [test_parser_0019.js]
--- a/netwerk/locales/en-US/necko.properties
+++ b/netwerk/locales/en-US/necko.properties
@@ -39,10 +39,11 @@ SuperfluousAuth=You are about to log in to the site “%1$S” with the username “%2$S”, but the website does not require authentication. This may be an attempt to trick you.\n\nIs “%1$S” the site you want to visit?
 AutomaticAuth=You are about to log in to the site “%1$S” with the username “%2$S”.
 
 TrackingUriBlocked=The resource at “%1$S” was blocked because tracking protection is enabled.
 
 # LOCALIZATION NOTE (APIDeprecationWarning):
 # %1$S is the deprected API; %2$S is the API function that should be used.
 APIDeprecationWarning=Warning: ‘%1$S’ deprecated, please use ‘%2$S’
 
-# LOCALIZATION NOTE (nsICookieManagerRemoveDeprecated): don't localize nsICookieManager.remove() and originAttributes.
-nsICookieManagerRemoveDeprecated=“nsICookieManager.remove()” is changed. Update your code and pass the correct originAttributes. Read more on MDN: https://developer.mozilla.org/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsICookieManager
+# LOCALIZATION NOTE (nsICookieManagerDeprecated): don't localize originAttributes.
+# %1$S is the deprecated API; %2$S is the interface suffix that the given deprecated API belongs to.
+nsICookieManagerAPIDeprecated=“%1$S” is changed. Update your code and pass the correct originAttributes. Read more on MDN: https://developer.mozilla.org/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsICookieManager%2$S
--- a/netwerk/test/TestCookie.cpp
+++ b/netwerk/test/TestCookie.cpp
@@ -592,43 +592,48 @@ main(int32_t argc, char *argv[])
 
       // *** nsICookieManager{2} interface tests
       sBuffer = PR_sprintf_append(sBuffer, "*** Beginning nsICookieManager{2} interface tests...\n");
       nsCOMPtr<nsICookieManager> cookieMgr = do_GetService(NS_COOKIEMANAGER_CONTRACTID, &rv0);
       if (NS_FAILED(rv0)) return -1;
       nsCOMPtr<nsICookieManager2> cookieMgr2 = do_QueryInterface(cookieMgr);
       if (!cookieMgr2) return -1;
 
+      mozilla::NeckoOriginAttributes attrs;
+
       // first, ensure a clean slate
       rv[0] = NS_SUCCEEDED(cookieMgr->RemoveAll());
       // add some cookies
-      rv[1] = NS_SUCCEEDED(cookieMgr2->Add(NS_LITERAL_CSTRING("cookiemgr.test"), // domain
+      rv[1] = NS_SUCCEEDED(cookieMgr2->AddNative(NS_LITERAL_CSTRING("cookiemgr.test"), // domain
                                            NS_LITERAL_CSTRING("/foo"),           // path
                                            NS_LITERAL_CSTRING("test1"),          // name
                                            NS_LITERAL_CSTRING("yes"),            // value
                                            false,                             // is secure
                                            false,                             // is httponly
                                            true,                              // is session
-                                           INT64_MAX));                          // expiry time
-      rv[2] = NS_SUCCEEDED(cookieMgr2->Add(NS_LITERAL_CSTRING("cookiemgr.test"), // domain
+                                           INT64_MAX,                            // expiry time
+                                           &attrs));                         // originAttributes
+      rv[2] = NS_SUCCEEDED(cookieMgr2->AddNative(NS_LITERAL_CSTRING("cookiemgr.test"), // domain
                                            NS_LITERAL_CSTRING("/foo"),           // path
                                            NS_LITERAL_CSTRING("test2"),          // name
                                            NS_LITERAL_CSTRING("yes"),            // value
                                            false,                             // is secure
                                            true,                              // is httponly
                                            true,                              // is session
-                                           PR_Now() / PR_USEC_PER_SEC + 2));     // expiry time
-      rv[3] = NS_SUCCEEDED(cookieMgr2->Add(NS_LITERAL_CSTRING("new.domain"),     // domain
+                                           PR_Now() / PR_USEC_PER_SEC + 2,       // expiry time
+                                           &attrs));                         // originAttributes
+      rv[3] = NS_SUCCEEDED(cookieMgr2->AddNative(NS_LITERAL_CSTRING("new.domain"),     // domain
                                            NS_LITERAL_CSTRING("/rabbit"),        // path
                                            NS_LITERAL_CSTRING("test3"),          // name
                                            NS_LITERAL_CSTRING("yes"),            // value
                                            false,                             // is secure
                                            false,                             // is httponly
                                            true,                              // is session
-                                           INT64_MAX));                          // expiry time
+                                           INT64_MAX,                            // expiry time
+                                           &attrs));                         // originAttributes
       // confirm using enumerator
       nsCOMPtr<nsISimpleEnumerator> enumerator;
       rv[4] = NS_SUCCEEDED(cookieMgr->GetEnumerator(getter_AddRefs(enumerator)));
       int32_t i = 0;
       bool more;
       nsCOMPtr<nsICookie2> expiredCookie, newDomainCookie;
       while (NS_SUCCEEDED(enumerator->HasMoreElements(&more)) && more) {
         nsCOMPtr<nsISupports> cookie;
@@ -654,33 +659,33 @@ main(int32_t argc, char *argv[])
       // check CountCookiesFromHost()
       uint32_t hostCookies = 0;
       rv[8] = NS_SUCCEEDED(cookieMgr2->CountCookiesFromHost(NS_LITERAL_CSTRING("cookiemgr.test"), &hostCookies)) &&
               hostCookies == 2;
       // check CookieExists() using the third cookie
       bool found;
       rv[9] = NS_SUCCEEDED(cookieMgr2->CookieExists(newDomainCookie, &found)) && found;
 
-      mozilla::NeckoOriginAttributes attrs;
 
       // remove the cookie, block it, and ensure it can't be added again
       rv[10] = NS_SUCCEEDED(cookieMgr->RemoveNative(NS_LITERAL_CSTRING("new.domain"), // domain
                                                     NS_LITERAL_CSTRING("test3"),      // name
                                                     NS_LITERAL_CSTRING("/rabbit"),    // path
                                                     true,                             // is blocked
                                                     &attrs));                         // originAttributes
       rv[11] = NS_SUCCEEDED(cookieMgr2->CookieExists(newDomainCookie, &found)) && !found;
-      rv[12] = NS_SUCCEEDED(cookieMgr2->Add(NS_LITERAL_CSTRING("new.domain"),     // domain
+      rv[12] = NS_SUCCEEDED(cookieMgr2->AddNative(NS_LITERAL_CSTRING("new.domain"),     // domain
                                             NS_LITERAL_CSTRING("/rabbit"),        // path
                                             NS_LITERAL_CSTRING("test3"),          // name
                                             NS_LITERAL_CSTRING("yes"),            // value
                                             false,                             // is secure
                                             false,                             // is httponly
                                             true,                              // is session
-                                            INT64_MIN));                          // expiry time
+                                            INT64_MIN,                            // expiry time
+                                            &attrs));                         // originAttributes
       rv[13] = NS_SUCCEEDED(cookieMgr2->CookieExists(newDomainCookie, &found)) && !found;
       // sleep four seconds, to make sure the second cookie has expired
       PR_Sleep(4 * PR_TicksPerSecond());
       // check that both CountCookiesFromHost() and CookieExists() count the
       // expired cookie
       rv[14] = NS_SUCCEEDED(cookieMgr2->CountCookiesFromHost(NS_LITERAL_CSTRING("cookiemgr.test"), &hostCookies)) &&
               hostCookies == 2;
       rv[15] = NS_SUCCEEDED(cookieMgr2->CookieExists(expiredCookie, &found)) && found;
--- a/netwerk/test/unit/test_bug411952.js
+++ b/netwerk/test/unit/test_bug411952.js
@@ -1,16 +1,16 @@
 function run_test() {
   try {
     var cm = Cc["@mozilla.org/cookiemanager;1"].
              getService(Ci.nsICookieManager2);
     do_check_neq(cm, null, "Retrieving the cookie manager failed");
 
     const time = (new Date("Jan 1, 2030")).getTime() / 1000;
-    cm.add("example.com", "/", "C", "V", false, true, false, time);
+    cm.add("example.com", "/", "C", "V", false, true, false, time, {});
     const now = Math.floor((new Date()).getTime() / 1000);
 
     var enumerator = cm.enumerator, found = false;
     while (enumerator.hasMoreElements()) {
       var cookie = enumerator.getNext().QueryInterface(Ci.nsICookie2);
       if (cookie.host == "example.com" &&
           cookie.path == "/" &&
           cookie.name == "C") {
--- a/old-configure.in
+++ b/old-configure.in
@@ -1181,16 +1181,17 @@ dnl ====================================
 dnl System overrides of the defaults for target
 dnl ========================================================
 
 case "$target" in
 *-darwin*)
     MKSHLIB='$(CXX) $(CXXFLAGS) $(DSO_PIC_CFLAGS) $(DSO_LDOPTS) -o $@'
     MKCSHLIB='$(CC) $(CFLAGS) $(DSO_PIC_CFLAGS) $(DSO_LDOPTS) -o $@'
     MOZ_OPTIMIZE_FLAGS="-O3"
+    CXXFLAGS="$CXXFLAGS -stdlib=libc++"
     DLL_SUFFIX=".dylib"
     DSO_LDOPTS=''
     STRIP_FLAGS="$STRIP_FLAGS -x -S"
     # Ensure that if we're targeting iOS an SDK was provided.
     AC_CACHE_CHECK(for iOS target,
                    ac_cv_ios_target,
                    [AC_TRY_COMPILE([#include <TargetConditionals.h>
 #if !(TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR)
--- a/python/mozbuild/mozbuild/artifacts.py
+++ b/python/mozbuild/mozbuild/artifacts.py
@@ -288,17 +288,17 @@ class MacArtifactJob(ArtifactJob):
                 'libnss3.dylib',
                 'libnssckbi.dylib',
                 'libnssdbm3.dylib',
                 'libplugin_child_interpose.dylib',
                 # 'libreplace_jemalloc.dylib',
                 # 'libreplace_malloc.dylib',
                 'libsoftokn3.dylib',
                 'plugin-container.app/Contents/MacOS/plugin-container',
-                'updater.app/Contents/MacOS/updater',
+                'updater.app/Contents/MacOS/org.mozilla.updater',
                 # 'xpcshell',
                 'XUL',
             ])
 
             # These get copied into dist/bin with the path, so "root/a/b/c" -> "dist/bin/a/b/c".
             paths_keep_path = ('Contents/Resources', [
                 'browser/components/libbrowsercomps.dylib',
                 'dependentlibs.list',
--- a/python/mozbuild/mozbuild/configure/options.py
+++ b/python/mozbuild/mozbuild/configure/options.py
@@ -338,22 +338,41 @@ class Option(object):
                     '*': '0 or more',
                     '+': '1 or more',
                 }.get(self.nargs, str(self.nargs)),
                 's' if (not isinstance(self.nargs, int) or
                         self.nargs != 1) else ''
             ))
 
         if len(values) and self.choices:
+            relative_result = None
             for val in values:
+                if self.nargs in ('+', '*'):
+                    if val.startswith(('+', '-')):
+                        if relative_result is None:
+                            relative_result = list(self.default)
+                        sign = val[0]
+                        val = val[1:]
+                        if sign == '+':
+                            if val not in relative_result:
+                                relative_result.append(val)
+                        else:
+                            try:
+                                relative_result.remove(val)
+                            except ValueError:
+                                pass
+
                 if val not in self.choices:
                     raise InvalidOptionError(
                         "'%s' is not one of %s"
                         % (val, ', '.join("'%s'" % c for c in self.choices)))
 
+            if relative_result is not None:
+                values = PositiveOptionValue(relative_result, origin=origin)
+
         return values
 
 
 class CommandLineHelper(object):
     '''Helper class to handle the various ways options can be given either
     on the command line of through the environment.
 
     For instance, an Option('--foo', env='FOO') can be passed as --foo on the
--- a/python/mozbuild/mozbuild/test/configure/test_options.py
+++ b/python/mozbuild/mozbuild/test/configure/test_options.py
@@ -233,16 +233,54 @@ class TestOption(unittest.TestCase):
         value = option.get_value('--with-option=b,a')
         self.assertTrue(value)
         self.assertEquals(PositiveOptionValue(('b', 'a')), value)
 
         # Test nargs inference from choices
         option = Option('--with-option', choices=('a', 'b'))
         self.assertEqual(option.nargs, 1)
 
+        # Test "relative" values
+        option = Option('--with-option', nargs='*', default=('b', 'c'),
+                        choices=('a', 'b', 'c', 'd'))
+
+        value = option.get_value('--with-option=+d')
+        self.assertEquals(PositiveOptionValue(('b', 'c', 'd')), value)
+
+        value = option.get_value('--with-option=-b')
+        self.assertEquals(PositiveOptionValue(('c',)), value)
+
+        value = option.get_value('--with-option=-b,+d')
+        self.assertEquals(PositiveOptionValue(('c','d')), value)
+
+        # Adding something that is in the default is fine
+        value = option.get_value('--with-option=+b')
+        self.assertEquals(PositiveOptionValue(('b', 'c')), value)
+
+        # Removing something that is not in the default is fine, as long as it
+        # is one of the choices
+        value = option.get_value('--with-option=-a')
+        self.assertEquals(PositiveOptionValue(('b', 'c')), value)
+
+        with self.assertRaises(InvalidOptionError) as e:
+            option.get_value('--with-option=-e')
+        self.assertEquals(e.exception.message,
+                          "'e' is not one of 'a', 'b', 'c', 'd'")
+
+        # Other "not a choice" errors.
+        with self.assertRaises(InvalidOptionError) as e:
+            option.get_value('--with-option=+e')
+        self.assertEquals(e.exception.message,
+                          "'e' is not one of 'a', 'b', 'c', 'd'")
+
+        with self.assertRaises(InvalidOptionError) as e:
+            option.get_value('--with-option=e')
+        self.assertEquals(e.exception.message,
+                          "'e' is not one of 'a', 'b', 'c', 'd'")
+
     def test_option_value_format(self):
         val = PositiveOptionValue()
         self.assertEquals('--with-value', val.format('--with-value'))
         self.assertEquals('--with-value', val.format('--without-value'))
         self.assertEquals('--enable-value', val.format('--enable-value'))
         self.assertEquals('--enable-value', val.format('--disable-value'))
         self.assertEquals('--value', val.format('--value'))
         self.assertEquals('VALUE=1', val.format('VALUE'))
--- a/security/sandbox/linux/moz.build
+++ b/security/sandbox/linux/moz.build
@@ -63,21 +63,25 @@ SOURCES += [
     '../chromium/sandbox/linux/seccomp-bpf/trap.cc',
     'broker/SandboxBrokerCommon.cpp',
     'LinuxCapabilities.cpp',
     'Sandbox.cpp',
     'SandboxBrokerClient.cpp',
     'SandboxChroot.cpp',
     'SandboxFilter.cpp',
     'SandboxFilterUtil.cpp',
-    'SandboxHooks.cpp',
     'SandboxLogging.cpp',
     'SandboxUtil.cpp',
 ]
 
+if CONFIG['MOZ_WIDGET_TOOLKIT'] != 'gonk':
+    SOURCES += [
+        'SandboxHooks.cpp',
+    ]
+
 # This copy of SafeSPrintf doesn't need to avoid the Chromium logging
 # dependency like the one in libxul does, but this way the behavior is
 # consistent.  See also the comment in SandboxLogging.h.
 SOURCES['../chromium/base/strings/safe_sprintf.cc'].flags += ['-DNDEBUG']
 
 # Keep clang from warning about intentional 'switch' fallthrough in icu_utf.cc:
 if CONFIG['CLANG_CXX']:
     SOURCES['../chromium/base/third_party/icu/icu_utf.cc'].flags += ['-Wno-implicit-fallthrough']
--- a/security/sandbox/mac/Sandbox.mm
+++ b/security/sandbox/mac/Sandbox.mm
@@ -17,127 +17,24 @@
 
 // XXX There are currently problems with the /usr/include/sandbox.h file on
 // some/all of the Macs in Mozilla's build system.  For the time being (until
 // this problem is resolved), we refer directly to what we need from it,
 // rather than including it here.
 extern "C" int sandbox_init(const char *profile, uint64_t flags, char **errorbuf);
 extern "C" void sandbox_free_error(char *errorbuf);
 
-#define MAC_OS_X_VERSION_10_0_HEX  0x00001000
-#define MAC_OS_X_VERSION_10_6_HEX  0x00001060
-#define MAC_OS_X_VERSION_10_7_HEX  0x00001070
-#define MAC_OS_X_VERSION_10_8_HEX  0x00001080
-#define MAC_OS_X_VERSION_10_9_HEX  0x00001090
-#define MAC_OS_X_VERSION_10_10_HEX 0x000010A0
-
-// Note about "major", "minor" and "bugfix" in the following code:
-//
-// The code decomposes an OS X version number into these components, and in
-// doing so follows Apple's terminology in Gestalt.h.  But this is very
-// misleading, because in other contexts Apple uses the "minor" component of
-// an OS X version number to indicate a "major" release (for example the "9"
-// in OS X 10.9.5), and the "bugfix" component to indicate a "minor" release
-// (for example the "5" in OS X 10.9.5).
-
-class OSXVersion {
-public:
-  static bool OnLionOrLater();
-  static int32_t OSXVersionMinor();
-
-private:
-  static void GetSystemVersion(int32_t& aMajor, int32_t& aMinor, int32_t& aBugFix);
-  static int32_t GetVersionNumber();
-  static int32_t mOSXVersion;
-};
-
-int32_t OSXVersion::mOSXVersion = -1;
-
-bool OSXVersion::OnLionOrLater()
-{
-  return (GetVersionNumber() >= MAC_OS_X_VERSION_10_7_HEX);
-}
-
-int32_t OSXVersion::OSXVersionMinor()
-{
-  return (GetVersionNumber() & 0xF0) >> 4;
-}
-
-void
-OSXVersion::GetSystemVersion(int32_t& aMajor, int32_t& aMinor, int32_t& aBugFix)
-{
-  SInt32 major = 0, minor = 0, bugfix = 0;
-
-  CFURLRef url =
-    CFURLCreateWithString(kCFAllocatorDefault,
-                          CFSTR("file:///System/Library/CoreServices/SystemVersion.plist"),
-                          NULL);
-  CFReadStreamRef stream =
-    CFReadStreamCreateWithFile(kCFAllocatorDefault, url);
-  CFReadStreamOpen(stream);
-  CFDictionaryRef sysVersionPlist = (CFDictionaryRef)
-    CFPropertyListCreateWithStream(kCFAllocatorDefault,
-                                   stream, 0, kCFPropertyListImmutable,
-                                   NULL, NULL);
-  CFReadStreamClose(stream);
-  CFRelease(stream);
-  CFRelease(url);
-
-  CFStringRef versionString = (CFStringRef)
-    CFDictionaryGetValue(sysVersionPlist, CFSTR("ProductVersion"));
-  CFArrayRef versions =
-    CFStringCreateArrayBySeparatingStrings(kCFAllocatorDefault,
-                                           versionString, CFSTR("."));
-  CFIndex count = CFArrayGetCount(versions);
-  if (count > 0) {
-    CFStringRef component = (CFStringRef) CFArrayGetValueAtIndex(versions, 0);
-    major = CFStringGetIntValue(component);
-    if (count > 1) {
-      component = (CFStringRef) CFArrayGetValueAtIndex(versions, 1);
-      minor = CFStringGetIntValue(component);
-      if (count > 2) {
-        component = (CFStringRef) CFArrayGetValueAtIndex(versions, 2);
-        bugfix = CFStringGetIntValue(component);
-      }
-    }
-  }
-  CFRelease(sysVersionPlist);
-  CFRelease(versions);
-
-  // If 'major' isn't what we expect, assume the oldest version of OS X we
-  // currently support (OS X 10.6).
-  if (major != 10) {
-    aMajor = 10; aMinor = 6; aBugFix = 0;
-  } else {
-    aMajor = major; aMinor = minor; aBugFix = bugfix;
-  }
-}
-
-int32_t
-OSXVersion::GetVersionNumber()
-{
-  if (mOSXVersion == -1) {
-    int32_t major, minor, bugfix;
-    GetSystemVersion(major, minor, bugfix);
-    mOSXVersion = MAC_OS_X_VERSION_10_0_HEX + (minor << 4) + bugfix;
-  }
-  return mOSXVersion;
-}
-
 namespace mozilla {
 
 static const char pluginSandboxRules[] =
   "(version 1)\n"
   "(deny default)\n"
   "(allow signal (target self))\n"
   "(allow sysctl-read)\n"
-  // Illegal syntax on OS X 10.6, needed on 10.7 and up.
-  "%s(allow iokit-open (iokit-user-client-class \"IOHIDParamUserClient\"))\n"
-  // Needed only on OS X 10.6
-  "%s(allow file-read-data (literal \"%s\"))\n"
+  "(allow iokit-open (iokit-user-client-class \"IOHIDParamUserClient\"))\n"
   "(allow mach-lookup\n"
   "    (global-name \"com.apple.cfprefsd.agent\")\n"
   "    (global-name \"com.apple.cfprefsd.daemon\")\n"
   "    (global-name \"com.apple.system.opendirectoryd.libinfo\")\n"
   "    (global-name \"com.apple.system.logger\")\n"
   "    (global-name \"com.apple.ls.boxd\"))\n"
   "(allow file-read*\n"
   "    (regex #\"^/etc$\")\n"
@@ -152,328 +49,316 @@ static const char pluginSandboxRules[] =
 
 static const char widevinePluginSandboxRulesAddend[] =
   "(allow mach-lookup (global-name \"com.apple.windowserver.active\"))\n";
 
 static const char contentSandboxRules[] =
   "(version 1)\n"
   "\n"
   "(define sandbox-level %d)\n"
-  "(define macosMinorVersion %d)\n"
   "(define appPath \"%s\")\n"
   "(define appBinaryPath \"%s\")\n"
   "(define appDir \"%s\")\n"
   "(define appTempDir \"%s\")\n"
   "(define home-path \"%s\")\n"
   "\n"
   "(import \"/System/Library/Sandbox/Profiles/system.sb\")\n"
   "\n"
-  "(if \n"
-  "  (or\n"
-  "    (< macosMinorVersion 9)\n"
-  "    (< sandbox-level 1))\n"
-  "  (allow default)\n"
-  "  (begin\n"
-  "    (deny default)\n"
-  "    (debug deny)\n"
+  "(begin\n"
+  "  (deny default)\n"
+  "  (debug deny)\n"
+  "\n"
+  "  (define resolving-literal literal)\n"
+  "  (define resolving-subpath subpath)\n"
+  "  (define resolving-regex regex)\n"
+  "\n"
+  "  (define container-path appPath)\n"
+  "  (define appdir-path appDir)\n"
+  "  (define var-folders-re \"^/private/var/folders/[^/][^/]\")\n"
+  "  (define var-folders2-re (string-append var-folders-re \"/[^/]+/[^/]\"))\n"
   "\n"
-  "    (define resolving-literal literal)\n"
-  "    (define resolving-subpath subpath)\n"
-  "    (define resolving-regex regex)\n"
+  "  (define (home-regex home-relative-regex)\n"
+  "    (resolving-regex (string-append \"^\" (regex-quote home-path) home-relative-regex)))\n"
+  "  (define (home-subpath home-relative-subpath)\n"
+  "    (resolving-subpath (string-append home-path home-relative-subpath)))\n"
+  "  (define (home-literal home-relative-literal)\n"
+  "    (resolving-literal (string-append home-path home-relative-literal)))\n"
   "\n"
-  "    (define container-path appPath)\n"
-  "    (define appdir-path appDir)\n"
-  "    (define var-folders-re \"^/private/var/folders/[^/][^/]\")\n"
-  "    (define var-folders2-re (string-append var-folders-re \"/[^/]+/[^/]\"))\n"
-  "\n"
-  "    (define (home-regex home-relative-regex)\n"
-  "      (resolving-regex (string-append \"^\" (regex-quote home-path) home-relative-regex)))\n"
-  "    (define (home-subpath home-relative-subpath)\n"
-  "      (resolving-subpath (string-append home-path home-relative-subpath)))\n"
-  "    (define (home-literal home-relative-literal)\n"
-  "      (resolving-literal (string-append home-path home-relative-literal)))\n"
+  "  (define (container-regex container-relative-regex)\n"
+  "    (resolving-regex (string-append \"^\" (regex-quote container-path) container-relative-regex)))\n"
+  "  (define (container-subpath container-relative-subpath)\n"
+  "    (resolving-subpath (string-append container-path container-relative-subpath)))\n"
+  "  (define (container-literal container-relative-literal)\n"
+  "    (resolving-literal (string-append container-path container-relative-literal)))\n"
   "\n"
-  "    (define (container-regex container-relative-regex)\n"
-  "      (resolving-regex (string-append \"^\" (regex-quote container-path) container-relative-regex)))\n"
-  "    (define (container-subpath container-relative-subpath)\n"
-  "      (resolving-subpath (string-append container-path container-relative-subpath)))\n"
-  "    (define (container-literal container-relative-literal)\n"
-  "      (resolving-literal (string-append container-path container-relative-literal)))\n"
+  "  (define (var-folders-regex var-folders-relative-regex)\n"
+  "    (resolving-regex (string-append var-folders-re var-folders-relative-regex)))\n"
+  "  (define (var-folders2-regex var-folders2-relative-regex)\n"
+  "    (resolving-regex (string-append var-folders2-re var-folders2-relative-regex)))\n"
   "\n"
-  "    (define (var-folders-regex var-folders-relative-regex)\n"
-  "      (resolving-regex (string-append var-folders-re var-folders-relative-regex)))\n"
-  "    (define (var-folders2-regex var-folders2-relative-regex)\n"
-  "      (resolving-regex (string-append var-folders2-re var-folders2-relative-regex)))\n"
+  "  (define (appdir-regex appdir-relative-regex)\n"
+  "    (resolving-regex (string-append \"^\" (regex-quote appdir-path) appdir-relative-regex)))\n"
+  "  (define (appdir-subpath appdir-relative-subpath)\n"
+  "    (resolving-subpath (string-append appdir-path appdir-relative-subpath)))\n"
+  "  (define (appdir-literal appdir-relative-literal)\n"
+  "    (resolving-literal (string-append appdir-path appdir-relative-literal)))\n"
   "\n"
-  "    (define (appdir-regex appdir-relative-regex)\n"
-  "      (resolving-regex (string-append \"^\" (regex-quote appdir-path) appdir-relative-regex)))\n"
-  "    (define (appdir-subpath appdir-relative-subpath)\n"
-  "      (resolving-subpath (string-append appdir-path appdir-relative-subpath)))\n"
-  "    (define (appdir-literal appdir-relative-literal)\n"
-  "      (resolving-literal (string-append appdir-path appdir-relative-literal)))\n"
+  "  (define (allow-shared-preferences-read domain)\n"
+  "        (begin\n"
+  "          (if (defined? `user-preference-read)\n"
+  "            (allow user-preference-read (preference-domain domain)))\n"
+  "          (allow file-read*\n"
+  "                 (home-literal (string-append \"/Library/Preferences/\" domain \".plist\"))\n"
+  "                 (home-regex (string-append \"/Library/Preferences/ByHost/\" (regex-quote domain) \"\\..*\\.plist$\")))\n"
+  "          ))\n"
   "\n"
-  "    (define (allow-shared-preferences-read domain)\n"
-  "          (begin\n"
-  "            (if (defined? `user-preference-read)\n"
-  "              (allow user-preference-read (preference-domain domain)))\n"
-  "            (allow file-read*\n"
-  "                   (home-literal (string-append \"/Library/Preferences/\" domain \".plist\"))\n"
-  "                   (home-regex (string-append \"/Library/Preferences/ByHost/\" (regex-quote domain) \"\\..*\\.plist$\")))\n"
-  "            ))\n"
+  "  (define (allow-shared-list domain)\n"
+  "    (allow file-read*\n"
+  "           (home-regex (string-append \"/Library/Preferences/\" (regex-quote domain)))))\n"
   "\n"
-  "    (define (allow-shared-list domain)\n"
-  "      (allow file-read*\n"
-  "             (home-regex (string-append \"/Library/Preferences/\" (regex-quote domain)))))\n"
+  "  (allow file-read-metadata)\n"
   "\n"
-  "    (allow file-read-metadata)\n"
-  "\n"
-  "    (allow ipc-posix-shm\n"
-  "        (ipc-posix-name-regex \"^/tmp/com.apple.csseed:\")\n"
-  "        (ipc-posix-name-regex \"^CFPBS:\")\n"
-  "        (ipc-posix-name-regex \"^AudioIO\"))\n"
+  "  (allow ipc-posix-shm\n"
+  "      (ipc-posix-name-regex \"^/tmp/com.apple.csseed:\")\n"
+  "      (ipc-posix-name-regex \"^CFPBS:\")\n"
+  "      (ipc-posix-name-regex \"^AudioIO\"))\n"
   "\n"
-  "    (allow file-read-metadata\n"
-  "        (literal \"/home\")\n"
-  "        (literal \"/net\")\n"
-  "        (regex \"^/private/tmp/KSInstallAction\\.\")\n"
-  "        (var-folders-regex \"/\")\n"
-  "        (home-subpath \"/Library\"))\n"
+  "  (allow file-read-metadata\n"
+  "      (literal \"/home\")\n"
+  "      (literal \"/net\")\n"
+  "      (regex \"^/private/tmp/KSInstallAction\\.\")\n"
+  "      (var-folders-regex \"/\")\n"
+  "      (home-subpath \"/Library\"))\n"
   "\n"
-  "    (allow signal (target self))\n"
-  "    (allow job-creation (literal \"/Library/CoreMediaIO/Plug-Ins/DAL\"))\n"
-  "    (allow iokit-set-properties (iokit-property \"IOAudioControlValue\"))\n"
+  "  (allow signal (target self))\n"
+  "  (allow job-creation (literal \"/Library/CoreMediaIO/Plug-Ins/DAL\"))\n"
+  "  (allow iokit-set-properties (iokit-property \"IOAudioControlValue\"))\n"
   "\n"
-  "    (allow mach-lookup\n"
-  "        (global-name \"com.apple.coreservices.launchservicesd\")\n"
-  "        (global-name \"com.apple.coreservices.appleevents\")\n"
-  "        (global-name \"com.apple.pasteboard.1\")\n"
-  "        (global-name \"com.apple.window_proxies\")\n"
-  "        (global-name \"com.apple.windowserver.active\")\n"
-  "        (global-name \"com.apple.audio.coreaudiod\")\n"
-  "        (global-name \"com.apple.audio.audiohald\")\n"
-  "        (global-name \"com.apple.PowerManagement.control\")\n"
-  "        (global-name \"com.apple.cmio.VDCAssistant\")\n"
-  "        (global-name \"com.apple.SystemConfiguration.configd\")\n"
-  "        (global-name \"com.apple.iconservices\")\n"
-  "        (global-name \"com.apple.cookied\")\n"
-  "        (global-name \"com.apple.printuitool.agent\")\n"
-  "        (global-name \"com.apple.printtool.agent\")\n"
-  "        (global-name \"com.apple.cache_delete\")\n"
-  "        (global-name \"com.apple.pluginkit.pkd\")\n"
-  "        (global-name \"com.apple.bird\")\n"
-  "        (global-name \"com.apple.ocspd\")\n"
-  "        (global-name \"com.apple.cmio.AppleCameraAssistant\")\n"
-  "        (global-name \"com.apple.DesktopServicesHelper\")\n"
-  "        (global-name \"com.apple.printtool.daemon\"))\n"
+  "  (allow mach-lookup\n"
+  "      (global-name \"com.apple.coreservices.launchservicesd\")\n"
+  "      (global-name \"com.apple.coreservices.appleevents\")\n"
+  "      (global-name \"com.apple.pasteboard.1\")\n"
+  "      (global-name \"com.apple.window_proxies\")\n"
+  "      (global-name \"com.apple.windowserver.active\")\n"
+  "      (global-name \"com.apple.audio.coreaudiod\")\n"
+  "      (global-name \"com.apple.audio.audiohald\")\n"
+  "      (global-name \"com.apple.PowerManagement.control\")\n"
+  "      (global-name \"com.apple.cmio.VDCAssistant\")\n"
+  "      (global-name \"com.apple.SystemConfiguration.configd\")\n"
+  "      (global-name \"com.apple.iconservices\")\n"
+  "      (global-name \"com.apple.cookied\")\n"
+  "      (global-name \"com.apple.printuitool.agent\")\n"
+  "      (global-name \"com.apple.printtool.agent\")\n"
+  "      (global-name \"com.apple.cache_delete\")\n"
+  "      (global-name \"com.apple.pluginkit.pkd\")\n"
+  "      (global-name \"com.apple.bird\")\n"
+  "      (global-name \"com.apple.ocspd\")\n"
+  "      (global-name \"com.apple.cmio.AppleCameraAssistant\")\n"
+  "      (global-name \"com.apple.DesktopServicesHelper\")\n"
+  "      (global-name \"com.apple.printtool.daemon\"))\n"
   "\n"
-  "    (allow iokit-open\n"
-  "        (iokit-user-client-class \"IOHIDParamUserClient\")\n"
-  "        (iokit-user-client-class \"IOAudioControlUserClient\")\n"
-  "        (iokit-user-client-class \"IOAudioEngineUserClient\")\n"
-  "        (iokit-user-client-class \"IGAccelDevice\")\n"
-  "        (iokit-user-client-class \"nvDevice\")\n"
-  "        (iokit-user-client-class \"nvSharedUserClient\")\n"
-  "        (iokit-user-client-class \"nvFermiGLContext\")\n"
-  "        (iokit-user-client-class \"IGAccelGLContext\")\n"
-  "        (iokit-user-client-class \"IGAccelSharedUserClient\")\n"
-  "        (iokit-user-client-class \"IGAccelVideoContextMain\")\n"
-  "        (iokit-user-client-class \"IGAccelVideoContextMedia\")\n"
-  "        (iokit-user-client-class \"IGAccelVideoContextVEBox\")\n"
-  "        (iokit-user-client-class \"RootDomainUserClient\")\n"
-  "        (iokit-user-client-class \"IOUSBDeviceUserClientV2\")\n"
-  "        (iokit-user-client-class \"IOUSBInterfaceUserClientV2\"))\n"
+  "  (allow iokit-open\n"
+  "      (iokit-user-client-class \"IOHIDParamUserClient\")\n"
+  "      (iokit-user-client-class \"IOAudioControlUserClient\")\n"
+  "      (iokit-user-client-class \"IOAudioEngineUserClient\")\n"
+  "      (iokit-user-client-class \"IGAccelDevice\")\n"
+  "      (iokit-user-client-class \"nvDevice\")\n"
+  "      (iokit-user-client-class \"nvSharedUserClient\")\n"
+  "      (iokit-user-client-class \"nvFermiGLContext\")\n"
+  "      (iokit-user-client-class \"IGAccelGLContext\")\n"
+  "      (iokit-user-client-class \"IGAccelSharedUserClient\")\n"
+  "      (iokit-user-client-class \"IGAccelVideoContextMain\")\n"
+  "      (iokit-user-client-class \"IGAccelVideoContextMedia\")\n"
+  "      (iokit-user-client-class \"IGAccelVideoContextVEBox\")\n"
+  "      (iokit-user-client-class \"RootDomainUserClient\")\n"
+  "      (iokit-user-client-class \"IOUSBDeviceUserClientV2\")\n"
+  "      (iokit-user-client-class \"IOUSBInterfaceUserClientV2\"))\n"
   "\n"
   "; depending on systems, the 1st, 2nd or both rules are necessary\n"
-  "    (allow-shared-preferences-read \"com.apple.HIToolbox\")\n"
-  "    (allow file-read-data (literal \"/Library/Preferences/com.apple.HIToolbox.plist\"))\n"
+  "  (allow-shared-preferences-read \"com.apple.HIToolbox\")\n"
+  "  (allow file-read-data (literal \"/Library/Preferences/com.apple.HIToolbox.plist\"))\n"
   "\n"
-  "    (allow-shared-preferences-read \"com.apple.ATS\")\n"
-  "    (allow file-read-data (literal \"/Library/Preferences/.GlobalPreferences.plist\"))\n"
+  "  (allow-shared-preferences-read \"com.apple.ATS\")\n"
+  "  (allow file-read-data (literal \"/Library/Preferences/.GlobalPreferences.plist\"))\n"
   "\n"
-  "    (allow file-read*\n"
-  "        (subpath \"/Library/Fonts\")\n"
-  "        (subpath \"/Library/Audio/Plug-Ins\")\n"
-  "        (subpath \"/Library/CoreMediaIO/Plug-Ins/DAL\")\n"
-  "        (subpath \"/Library/Spelling\")\n"
-  "        (subpath \"/private/etc/cups/ppd\")\n"
-  "        (subpath \"/private/var/run/cupsd\")\n"
-  "        (literal \"/\")\n"
-  "        (literal \"/private/tmp\")\n"
-  "        (literal \"/private/var/tmp\")\n"
+  "  (allow file-read*\n"
+  "      (subpath \"/Library/Fonts\")\n"
+  "      (subpath \"/Library/Audio/Plug-Ins\")\n"
+  "      (subpath \"/Library/CoreMediaIO/Plug-Ins/DAL\")\n"
+  "      (subpath \"/Library/Spelling\")\n"
+  "      (subpath \"/private/etc/cups/ppd\")\n"
+  "      (subpath \"/private/var/run/cupsd\")\n"
+  "      (literal \"/\")\n"
+  "      (literal \"/private/tmp\")\n"
+  "      (literal \"/private/var/tmp\")\n"
   "\n"
-  "        (home-literal \"/.CFUserTextEncoding\")\n"
-  "        (home-literal \"/Library/Preferences/com.apple.DownloadAssessment.plist\")\n"
-  "        (home-subpath \"/Library/Colors\")\n"
-  "        (home-subpath \"/Library/Fonts\")\n"
-  "        (home-subpath \"/Library/FontCollections\")\n"
-  "        (home-subpath \"/Library/Keyboard Layouts\")\n"
-  "        (home-subpath \"/Library/Input Methods\")\n"
-  "        (home-subpath \"/Library/PDF Services\")\n"
-  "        (home-subpath \"/Library/Spelling\")\n"
+  "      (home-literal \"/.CFUserTextEncoding\")\n"
+  "      (home-literal \"/Library/Preferences/com.apple.DownloadAssessment.plist\")\n"
+  "      (home-subpath \"/Library/Colors\")\n"
+  "      (home-subpath \"/Library/Fonts\")\n"
+  "      (home-subpath \"/Library/FontCollections\")\n"
+  "      (home-subpath \"/Library/Keyboard Layouts\")\n"
+  "      (home-subpath \"/Library/Input Methods\")\n"
+  "      (home-subpath \"/Library/PDF Services\")\n"
+  "      (home-subpath \"/Library/Spelling\")\n"
   "\n"
-  "        (subpath appdir-path)\n"
+  "      (subpath appdir-path)\n"
   "\n"
-  "        (literal appPath)\n"
-  "        (literal appBinaryPath))\n"
+  "      (literal appPath)\n"
+  "      (literal appBinaryPath))\n"
   "\n"
-  "    (allow-shared-list \"org.mozilla.plugincontainer\")\n"
+  "  (allow-shared-list \"org.mozilla.plugincontainer\")\n"
   "\n"
   "; the following 2 rules should be removed when microphone and camera access\n"
   "; are brokered through the content process\n"
-  "    (allow device-microphone)\n"
-  "    (allow device-camera)\n"
+  "  (allow device-microphone)\n"
+  "  (allow device-camera)\n"
   "\n"
-  "    (allow file* (var-folders2-regex \"/com\\.apple\\.IntlDataCache\\.le$\"))\n"
-  "    (allow file-read*\n"
-  "        (var-folders2-regex \"/com\\.apple\\.IconServices/\")\n"
-  "        (var-folders2-regex \"/[^/]+\\.mozrunner/extensions/[^/]+/chrome/[^/]+/content/[^/]+\\.j(s|ar)$\"))\n"
+  "  (allow file* (var-folders2-regex \"/com\\.apple\\.IntlDataCache\\.le$\"))\n"
+  "  (allow file-read*\n"
+  "      (var-folders2-regex \"/com\\.apple\\.IconServices/\")\n"
+  "      (var-folders2-regex \"/[^/]+\\.mozrunner/extensions/[^/]+/chrome/[^/]+/content/[^/]+\\.j(s|ar)$\"))\n"
   "\n"
-  "    (allow file-write* (var-folders2-regex \"/org\\.chromium\\.[a-zA-Z0-9]*$\"))\n"
-  "    (allow file-read*\n"
-  "        (home-regex \"/Library/Application Support/[^/]+/Extensions/[^/]/\")\n"
-  "        (resolving-regex \"/Library/Application Support/[^/]+/Extensions/[^/]/\")\n"
-  "        (home-regex \"/Library/Application Support/Firefox/Profiles/[^/]+/extensions/\")\n"
-  "        (home-regex \"/Library/Application Support/Firefox/Profiles/[^/]+/weave/\"))\n"
+  "  (allow file-write* (var-folders2-regex \"/org\\.chromium\\.[a-zA-Z0-9]*$\"))\n"
+  "  (allow file-read*\n"
+  "      (home-regex \"/Library/Application Support/[^/]+/Extensions/[^/]/\")\n"
+  "      (resolving-regex \"/Library/Application Support/[^/]+/Extensions/[^/]/\")\n"
+  "      (home-regex \"/Library/Application Support/Firefox/Profiles/[^/]+/extensions/\")\n"
+  "      (home-regex \"/Library/Application Support/Firefox/Profiles/[^/]+/weave/\"))\n"
   "\n"
   "; the following rules should be removed when printing and \n"
   "; opening a file from disk are brokered through the main process\n"
-  "    (if\n"
-  "      (< sandbox-level 2)\n"
-  "      (allow file*\n"
-  "          (require-not\n"
-  "              (home-subpath \"/Library\")))\n"
-  "      (allow file*\n"
-  "          (require-all\n"
-  "              (subpath home-path)\n"
-  "              (require-not\n"
-  "                  (home-subpath \"/Library\")))))\n"
+  "  (if\n"
+  "    (< sandbox-level 2)\n"
+  "    (allow file*\n"
+  "        (require-not\n"
+  "            (home-subpath \"/Library\")))\n"
+  "    (allow file*\n"
+  "        (require-all\n"
+  "            (subpath home-path)\n"
+  "            (require-not\n"
+  "                (home-subpath \"/Library\")))))\n"
   "\n"
   "; printing\n"
-  "    (allow authorization-right-obtain\n"
-  "           (right-name \"system.print.operator\")\n"
-  "           (right-name \"system.printingmanager\"))\n"
-  "    (allow mach-lookup\n"
-  "           (global-name \"com.apple.printuitool.agent\")\n"
-  "           (global-name \"com.apple.printtool.agent\")\n"
-  "           (global-name \"com.apple.printtool.daemon\")\n"
-  "           (global-name \"com.apple.sharingd\")\n"
-  "           (global-name \"com.apple.metadata.mds\")\n"
-  "           (global-name \"com.apple.mtmd.xpc\")\n"
-  "           (global-name \"com.apple.FSEvents\")\n"
-  "           (global-name \"com.apple.locum\")\n"
-  "           (global-name \"com.apple.ImageCaptureExtension2.presence\"))\n"
-  "    (allow file-read*\n"
-  "           (home-literal \"/.cups/lpoptions\")\n"
-  "           (home-literal \"/.cups/client.conf\")\n"
-  "           (literal \"/private/etc/cups/lpoptions\")\n"
-  "           (literal \"/private/etc/cups/client.conf\")\n"
-  "           (subpath \"/private/etc/cups/ppd\")\n"
-  "           (literal \"/private/var/run/cupsd\"))\n"
-  "    (allow-shared-preferences-read \"org.cups.PrintingPrefs\")\n"
-  "    (allow-shared-preferences-read \"com.apple.finder\")\n"
-  "    (allow-shared-preferences-read \"com.apple.LaunchServices\")\n"
-  "    (allow-shared-preferences-read \".GlobalPreferences\")\n"
-  "    (allow network-outbound\n"
-  "        (literal \"/private/var/run/cupsd\")\n"
-  "        (literal \"/private/var/run/mDNSResponder\"))\n"
+  "  (allow authorization-right-obtain\n"
+  "         (right-name \"system.print.operator\")\n"
+  "         (right-name \"system.printingmanager\"))\n"
+  "  (allow mach-lookup\n"
+  "         (global-name \"com.apple.printuitool.agent\")\n"
+  "         (global-name \"com.apple.printtool.agent\")\n"
+  "         (global-name \"com.apple.printtool.daemon\")\n"
+  "         (global-name \"com.apple.sharingd\")\n"
+  "         (global-name \"com.apple.metadata.mds\")\n"
+  "         (global-name \"com.apple.mtmd.xpc\")\n"
+  "         (global-name \"com.apple.FSEvents\")\n"
+  "         (global-name \"com.apple.locum\")\n"
+  "         (global-name \"com.apple.ImageCaptureExtension2.presence\"))\n"
+  "  (allow file-read*\n"
+  "         (home-literal \"/.cups/lpoptions\")\n"
+  "         (home-literal \"/.cups/client.conf\")\n"
+  "         (literal \"/private/etc/cups/lpoptions\")\n"
+  "         (literal \"/private/etc/cups/client.conf\")\n"
+  "         (subpath \"/private/etc/cups/ppd\")\n"
+  "         (literal \"/private/var/run/cupsd\"))\n"
+  "  (allow-shared-preferences-read \"org.cups.PrintingPrefs\")\n"
+  "  (allow-shared-preferences-read \"com.apple.finder\")\n"
+  "  (allow-shared-preferences-read \"com.apple.LaunchServices\")\n"
+  "  (allow-shared-preferences-read \".GlobalPreferences\")\n"
+  "  (allow network-outbound\n"
+  "      (literal \"/private/var/run/cupsd\")\n"
+  "      (literal \"/private/var/run/mDNSResponder\"))\n"
   "\n"
   "; print preview\n"
-  "    (if (> macosMinorVersion 9)\n"
-  "        (allow lsopen))\n"
-  "    (allow file-write* file-issue-extension (var-folders2-regex \"/\"))\n"
-  "    (allow file-read-xattr (literal \"/Applications/Preview.app\"))\n"
-  "    (allow mach-task-name)\n"
-  "    (allow mach-register)\n"
-  "    (allow file-read-data\n"
-  "        (regex \"^/Library/Printers/[^/]+/PDEs/[^/]+.plugin\")\n"
-  "        (subpath \"/Library/PDF Services\")\n"
-  "        (subpath \"/Applications/Preview.app\")\n"
-  "        (home-literal \"/Library/Preferences/com.apple.ServicesMenu.Services.plist\"))\n"
-  "    (allow mach-lookup\n"
-  "        (global-name \"com.apple.pbs.fetch_services\")\n"
-  "        (global-name \"com.apple.tsm.uiserver\")\n"
-  "        (global-name \"com.apple.ls.boxd\")\n"
-  "        (global-name \"com.apple.coreservices.quarantine-resolver\")\n"
-  "        (global-name-regex \"_OpenStep$\"))\n"
-  "    (allow appleevent-send\n"
-  "        (appleevent-destination \"com.apple.preview\")\n"
-  "        (appleevent-destination \"com.apple.imagecaptureextension2\"))\n"
+  "  (allow lsopen)\n"
+  "  (allow file-write* file-issue-extension (var-folders2-regex \"/\"))\n"
+  "  (allow file-read-xattr (literal \"/Applications/Preview.app\"))\n"
+  "  (allow mach-task-name)\n"
+  "  (allow mach-register)\n"
+  "  (allow file-read-data\n"
+  "      (regex \"^/Library/Printers/[^/]+/PDEs/[^/]+.plugin\")\n"
+  "      (subpath \"/Library/PDF Services\")\n"
+  "      (subpath \"/Applications/Preview.app\")\n"
+  "      (home-literal \"/Library/Preferences/com.apple.ServicesMenu.Services.plist\"))\n"
+  "  (allow mach-lookup\n"
+  "      (global-name \"com.apple.pbs.fetch_services\")\n"
+  "      (global-name \"com.apple.tsm.uiserver\")\n"
+  "      (global-name \"com.apple.ls.boxd\")\n"
+  "      (global-name \"com.apple.coreservices.quarantine-resolver\")\n"
+  "      (global-name-regex \"_OpenStep$\"))\n"
+  "  (allow appleevent-send\n"
+  "      (appleevent-destination \"com.apple.preview\")\n"
+  "      (appleevent-destination \"com.apple.imagecaptureextension2\"))\n"
   "\n"
   "; accelerated graphics\n"
-  "    (allow-shared-preferences-read \"com.apple.opengl\")\n"
-  "    (allow-shared-preferences-read \"com.nvidia.OpenGL\")\n"
-  "    (allow mach-lookup\n"
-  "        (global-name \"com.apple.cvmsServ\"))\n"
-  "    (allow iokit-open\n"
-  "        (iokit-connection \"IOAccelerator\")\n"
-  "        (iokit-user-client-class \"IOAccelerationUserClient\")\n"
-  "        (iokit-user-client-class \"IOSurfaceRootUserClient\")\n"
-  "        (iokit-user-client-class \"IOSurfaceSendRight\")\n"
-  "        (iokit-user-client-class \"IOFramebufferSharedUserClient\")\n"
-  "        (iokit-user-client-class \"AppleSNBFBUserClient\")\n"
-  "        (iokit-user-client-class \"AGPMClient\")\n"
-  "        (iokit-user-client-class \"AppleGraphicsControlClient\")\n"
-  "        (iokit-user-client-class \"AppleGraphicsPolicyClient\"))\n"
+  "  (allow-shared-preferences-read \"com.apple.opengl\")\n"
+  "  (allow-shared-preferences-read \"com.nvidia.OpenGL\")\n"
+  "  (allow mach-lookup\n"
+  "      (global-name \"com.apple.cvmsServ\"))\n"
+  "  (allow iokit-open\n"
+  "      (iokit-connection \"IOAccelerator\")\n"
+  "      (iokit-user-client-class \"IOAccelerationUserClient\")\n"
+  "      (iokit-user-client-class \"IOSurfaceRootUserClient\")\n"
+  "      (iokit-user-client-class \"IOSurfaceSendRight\")\n"
+  "      (iokit-user-client-class \"IOFramebufferSharedUserClient\")\n"
+  "      (iokit-user-client-class \"AppleSNBFBUserClient\")\n"
+  "      (iokit-user-client-class \"AGPMClient\")\n"
+  "      (iokit-user-client-class \"AppleGraphicsControlClient\")\n"
+  "      (iokit-user-client-class \"AppleGraphicsPolicyClient\"))\n"
   "\n"
   "; bug 1153809\n"
-  "    (allow iokit-open\n"
-  "        (iokit-user-client-class \"NVDVDContextTesla\")\n"
-  "        (iokit-user-client-class \"Gen6DVDContext\"))\n"
+  "  (allow iokit-open\n"
+  "      (iokit-user-client-class \"NVDVDContextTesla\")\n"
+  "      (iokit-user-client-class \"Gen6DVDContext\"))\n"
   "\n"
   "; bug 1190032\n"
-  "    (allow file*\n"
-  "        (home-regex \"/Library/Caches/TemporaryItems/plugtmp.*\"))\n"
+  "  (allow file*\n"
+  "      (home-regex \"/Library/Caches/TemporaryItems/plugtmp.*\"))\n"
   "\n"
   "; bug 1201935\n"
-  "    (allow file-read*\n"
-  "        (home-subpath \"/Library/Caches/TemporaryItems\"))\n"
+  "  (allow file-read*\n"
+  "      (home-subpath \"/Library/Caches/TemporaryItems\"))\n"
   "\n"
   "; bug 1237847\n"
-  "    (allow file-read*\n"
-  "        (subpath appTempDir))\n"
-  "    (allow file-write*\n"
-  "        (subpath appTempDir))\n"
-  "  )\n"
+  "  (allow file-read*\n"
+  "      (subpath appTempDir))\n"
+  "  (allow file-write*\n"
+  "      (subpath appTempDir))\n"
   ")\n";
 
 bool StartMacSandbox(MacSandboxInfo aInfo, std::string &aErrorMessage)
 {
   char *profile = NULL;
   if (aInfo.type == MacSandboxType_Plugin) {
-    if (OSXVersion::OnLionOrLater()) {
-      asprintf(&profile, pluginSandboxRules, "", ";",
-               aInfo.pluginInfo.pluginPath.c_str(),
-               aInfo.pluginInfo.pluginBinaryPath.c_str(),
-               aInfo.appPath.c_str(),
-               aInfo.appBinaryPath.c_str());
-    } else {
-      asprintf(&profile, pluginSandboxRules, ";", "",
-               aInfo.pluginInfo.pluginPath.c_str(),
-               aInfo.pluginInfo.pluginBinaryPath.c_str(),
-               aInfo.appPath.c_str(),
-               aInfo.appBinaryPath.c_str());
-    }
+    asprintf(&profile, pluginSandboxRules,
+             aInfo.pluginInfo.pluginBinaryPath.c_str(),
+             aInfo.appPath.c_str(),
+             aInfo.appBinaryPath.c_str());
 
     if (profile &&
       aInfo.pluginInfo.type == MacSandboxPluginType_GMPlugin_EME_Widevine) {
       char *widevineProfile = NULL;
       asprintf(&widevineProfile, "%s%s", profile,
         widevinePluginSandboxRulesAddend);
       free(profile);
       profile = widevineProfile;
     }
   }
   else if (aInfo.type == MacSandboxType_Content) {
-    asprintf(&profile, contentSandboxRules, aInfo.level,
-             OSXVersion::OSXVersionMinor(),
-             aInfo.appPath.c_str(),
-             aInfo.appBinaryPath.c_str(),
-             aInfo.appDir.c_str(),
-             aInfo.appTempDir.c_str(),
-             getenv("HOME"));
+    if (aInfo.level >= 1) {
+      asprintf(&profile, contentSandboxRules, aInfo.level,
+               aInfo.appPath.c_str(),
+               aInfo.appBinaryPath.c_str(),
+               aInfo.appDir.c_str(),
+               aInfo.appTempDir.c_str(),
+               getenv("HOME"));
+    } else {
+      fprintf(stderr,
+        "Content sandbox disabled due to sandbox level setting\n");
+      return (true);
+    }
   }
   else {
     char *msg = NULL;
     asprintf(&msg, "Unexpected sandbox type %u", aInfo.type);
     if (msg) {
       aErrorMessage.assign(msg);
       free(msg);
     }
--- a/taskcluster/docs/attributes.rst
+++ b/taskcluster/docs/attributes.rst
@@ -58,28 +58,33 @@ sometimes set to match the suite).  Exam
 ``mochitest-devtools-chrome-chunked`` or ``a11y``.
 
 unittest_try_name
 =================
 
 (deprecated) This is the name used to refer to a unit test via try syntax.  It
 may not match either of ``unittest_suite`` or ``unittest_flavor``.
 
+talos_try_name
+==============
+
+(deprecated) This is the name used to refer to a talos job via try syntax.
+
 test_chunk
 ==========
 
 This is the chunk number of a chunked test suite (talos or unittest).  Note
 that this is a string!
 
 legacy_kind
 ===========
 
 (deprecated) The kind of task as created by the legacy kind.  This is valid
-only for the ``legacy`` kind.  One of ``build``, ``unittest,`` ``post_build``,
-or ``job``.
+only for the ``legacy`` kind.  One of ``build``, ``unittest,``, ``talos``,
+``post_build``, or ``job``.
 
 job
 ===
 
 (deprecated) The name of the job (corresponding to a ``-j`` option or the name
 of a post-build job).  This is valid only for the ``legacy`` kind.
 
 post_build
--- a/taskcluster/taskgraph/test/test_try_option_syntax.py
+++ b/taskcluster/taskgraph/test/test_try_option_syntax.py
@@ -15,43 +15,56 @@ from mozunit import main
 empty_graph = TaskGraph({}, Graph(set(), set()))
 
 def unittest_task(n, tp):
     return (n, Task('test', n, {
         'unittest_try_name': n,
         'test_platform': tp,
     }))
 
+def talos_task(n, tp):
+    return (n, Task('test', n, {
+        'talos_try_name': n,
+        'test_platform': tp,
+    }))
+
 tasks = {k: v for k,v in [
     unittest_task('mochitest-browser-chrome', 'linux'),
     unittest_task('mochitest-browser-chrome-e10s', 'linux64'),
     unittest_task('mochitest-chrome', 'linux'),
     unittest_task('mochitest-webgl', 'linux'),
     unittest_task('crashtest-e10s', 'linux'),
     unittest_task('gtest', 'linux64'),
+    talos_task('dromaeojs', 'linux64'),
 ]}
+unittest_tasks = {k: v for k,v in tasks.iteritems()
+                  if 'unittest_try_name' in v.attributes}
+talos_tasks = {k: v for k,v in tasks.iteritems()
+               if 'talos_try_name' in v.attributes}
 graph_with_jobs = TaskGraph(tasks, Graph(set(tasks), set()))
 
 
 class TestTryOptionSyntax(unittest.TestCase):
 
     def test_empty_message(self):
         "Given an empty message, it should return an empty value"
         tos = TryOptionSyntax('', empty_graph)
         self.assertEqual(tos.build_types, [])
         self.assertEqual(tos.jobs, [])
         self.assertEqual(tos.unittests, [])
+        self.assertEqual(tos.talos, [])
         self.assertEqual(tos.platforms, [])
 
     def test_message_without_try(self):
         "Given a non-try message, it should return an empty value"
         tos = TryOptionSyntax('Bug 1234: frobnicte the foo', empty_graph)
         self.assertEqual(tos.build_types, [])
         self.assertEqual(tos.jobs, [])
         self.assertEqual(tos.unittests, [])
+        self.assertEqual(tos.talos, [])
         self.assertEqual(tos.platforms, [])
 
     def test_unknown_args(self):
         "unknown arguments are ignored"
         tos = TryOptionSyntax('try: --doubledash -z extra', empty_graph)
         # equilvant to "try:"..
         self.assertEqual(tos.build_types, [])
         self.assertEqual(tos.jobs, None)
@@ -132,33 +145,33 @@ class TestTryOptionSyntax(unittest.TestC
     def test_u_none(self):
         "-u none sets unittests=[]"
         tos = TryOptionSyntax('try: -u none', graph_with_jobs)
         self.assertEqual(sorted(tos.unittests), [])
 
     def test_u_all(self):
         "-u all sets unittests=[..whole list..]"
         tos = TryOptionSyntax('try: -u all', graph_with_jobs)
-        self.assertEqual(sorted(tos.unittests), sorted([{'test': t} for t in tasks]))
+        self.assertEqual(sorted(tos.unittests), sorted([{'test': t} for t in unittest_tasks]))
 
     def test_u_single(self):
         "-u mochitest-webgl sets unittests=[mochitest-webgl]"
         tos = TryOptionSyntax('try: -u mochitest-webgl', graph_with_jobs)
         self.assertEqual(sorted(tos.unittests), sorted([{'test': 'mochitest-webgl'}]))
 
     def test_u_alias(self):
         "-u mochitest-gl sets unittests=[mochitest-webgl]"
         tos = TryOptionSyntax('try: -u mochitest-gl', graph_with_jobs)
         self.assertEqual(sorted(tos.unittests), sorted([{'test': 'mochitest-webgl'}]))
 
     def test_u_multi_alias(self):
         "-u e10s sets unittests=[all e10s unittests]"
         tos = TryOptionSyntax('try: -u e10s', graph_with_jobs)
         self.assertEqual(sorted(tos.unittests), sorted([
-            {'test': t} for t in tasks if 'e10s' in t
+            {'test': t} for t in unittest_tasks if 'e10s' in t
         ]))
 
     def test_u_commas(self):
         "-u mochitest-webgl,gtest sets unittests=both"
         tos = TryOptionSyntax('try: -u mochitest-webgl,gtest', graph_with_jobs)
         self.assertEqual(sorted(tos.unittests), sorted([
             {'test': 'mochitest-webgl'},
             {'test': 'gtest'},
@@ -213,19 +226,36 @@ class TestTryOptionSyntax(unittest.TestC
             {'test': 'gtest', 'platforms': ['linux', 'win32'], 'only_chunks': set('1')},
         ]))
 
     def test_u_chunks_platform_alias(self):
         "-u e10s-1[linux] selects the first chunk of every e10s test on linux"
         tos = TryOptionSyntax('try: -u e10s-1[linux]', graph_with_jobs)
         self.assertEqual(sorted(tos.unittests), sorted([
             {'test': t, 'platforms': ['linux'], 'only_chunks': set('1')}
-            for t in tasks if 'e10s' in t
+            for t in unittest_tasks if 'e10s' in t
         ]))
 
+    def test_t_none(self):
+        "-t none sets talos=[]"
+        tos = TryOptionSyntax('try: -t none', graph_with_jobs)
+        self.assertEqual(sorted(tos.talos), [])
+
+    def test_t_all(self):
+        "-t all sets talos=[..whole list..]"
+        tos = TryOptionSyntax('try: -t all', graph_with_jobs)
+        self.assertEqual(sorted(tos.talos), sorted([{'test': t} for t in talos_tasks]))
+
+    def test_t_single(self):
+        "-t mochitest-webgl sets talos=[mochitest-webgl]"
+        tos = TryOptionSyntax('try: -t mochitest-webgl', graph_with_jobs)
+        self.assertEqual(sorted(tos.talos), sorted([{'test': 'mochitest-webgl'}]))
+
+    # -t shares an implementation with -u, so it's not tested heavily
+
     def test_trigger_tests(self):
         "--trigger-tests 10 sets trigger_tests"
         tos = TryOptionSyntax('try: --trigger-tests 10', empty_graph)
         self.assertEqual(tos.trigger_tests, 10)
 
     def test_interactive(self):
         "--interactive sets interactive"
         tos = TryOptionSyntax('try: --interactive', empty_graph)
--- a/taskcluster/taskgraph/try_option_syntax.py
+++ b/taskcluster/taskgraph/try_option_syntax.py
@@ -143,16 +143,17 @@ class TryOptionSyntax(object):
             'platforms': [..platform names..], # to limit to only certain platforms
             'only_chunks': set([..chunk numbers..]), # to limit only to certain chunks
         }
         """
         self.jobs = []
         self.build_types = []
         self.platforms = []
         self.unittests = []
+        self.talos = []
         self.trigger_tests = 0
         self.interactive = False
 
         # shlex used to ensure we split correctly when giving values to argparse.
         parts = shlex.split(self.escape_whitespace_in_brackets(message))
         try_idx = None
         for idx, part in enumerate(parts):
             if part == TRY_DELIMITER:
@@ -162,26 +163,28 @@ class TryOptionSyntax(object):
         if try_idx is None:
             return
 
         # Argument parser based on try flag flags
         parser = argparse.ArgumentParser()
         parser.add_argument('-b', '--build', dest='build_types')
         parser.add_argument('-p', '--platform', nargs='?', dest='platforms', const='all', default='all')
         parser.add_argument('-u', '--unittests', nargs='?', dest='unittests', const='all', default='all')
+        parser.add_argument('-t', '--talos', nargs='?', dest='talos', const='all', default='all')
         parser.add_argument('-i', '--interactive', dest='interactive', action='store_true', default=False)
         parser.add_argument('-j', '--job', dest='jobs', action='append')
         # In order to run test jobs multiple times
         parser.add_argument('--trigger-tests', dest='trigger_tests', type=int, default=1)
         args, _ = parser.parse_known_args(parts[try_idx:])
 
         self.jobs = self.parse_jobs(args.jobs)
         self.build_types = self.parse_build_types(args.build_types)
         self.platforms = self.parse_platforms(args.platforms)
-        self.unittests = self.parse_unittests(args.unittests, full_task_graph)
+        self.unittests = self.parse_test_option("unittest_try_name", args.unittests, full_task_graph)
+        self.talos = self.parse_test_option("talos_try_name", args.talos, full_task_graph)
         self.trigger_tests = args.trigger_tests
         self.interactive = args.interactive
 
     def parse_jobs(self, jobs_arg):
         if not jobs_arg or jobs_arg == ['all']:
             return None
         expanded = []
         for job in jobs_arg:
@@ -202,42 +205,44 @@ class TryOptionSyntax(object):
         results = []
         for build in platform_arg.split(','):
             results.append(build)
             if build in RIDEALONG_BUILDS:
                 results.extend(RIDEALONG_BUILDS[build])
 
         return results
 
-    def parse_unittests(self, unittest_arg, full_task_graph):
+    def parse_test_option(self, attr_name, test_arg, full_task_graph):
         '''
-        Parse a unittest (-u) option, in the context of a full task graph containing
-        available `unittest_try_name` attributes.  There are three cases:
 
-            - unittest_arg is == 'none' (meaning an empty list)
-            - unittest_arg is == 'all' (meaning use the list of jobs for that job type)
-            - unittest_arg is comma string which needs to be parsed
+        Parse a unittest (-u) or talos (-t) option, in the context of a full
+        task graph containing available `unittest_try_name` or `talos_try_name`
+        attributes.  There are three cases:
+
+            - test_arg is == 'none' (meaning an empty list)
+            - test_arg is == 'all' (meaning use the list of jobs for that job type)
+            - test_arg is comma string which needs to be parsed
         '''
 
         # Empty job list case...
-        if unittest_arg is None or unittest_arg == 'none':
+        if test_arg is None or test_arg == 'none':
             return []
 
         all_platforms = set(t.attributes['test_platform']
                             for t in full_task_graph.tasks.itervalues()
                             if 'test_platform' in t.attributes)
 
-        tests = self.parse_test_opts(unittest_arg, all_platforms)
+        tests = self.parse_test_opts(test_arg, all_platforms)
 
         if not tests:
             return []
 
-        all_tests = set(t.attributes['unittest_try_name']
+        all_tests = set(t.attributes[attr_name]
                         for t in full_task_graph.tasks.itervalues()
-                        if 'unittest_try_name' in t.attributes)
+                        if attr_name in t.attributes)
 
         # Special case where tests is 'all' and must be expanded
         if tests[0]['test'] == 'all':
             results = []
             all_entry = tests[0]
             for test in all_tests:
                 entry = {'test': test}
                 # If there are platform restrictions copy them across the list.
@@ -424,48 +429,54 @@ class TryOptionSyntax(object):
                 continue
 
             result += char
 
         return result
 
     def task_matches(self, attributes):
         attr = attributes.get
+
+        def match_test(try_spec, attr_name):
+            if attr('build_type') not in self.build_types:
+                return False
+            if self.platforms is not None:
+                if attr('build_platform') not in self.platforms:
+                    return False
+            if try_spec is not None:
+                # TODO: optimize this search a bit
+                for test in try_spec:
+                    if attr(attr_name) == test['test']:
+                        break
+                else:
+                    return False
+                if 'platforms' in test and attr('test_platform') not in test['platforms']:
+                    return False
+                if 'only_chunks' in test and attr('test_chunk') not in test['only_chunks']:
+                    return False
+                return True
+            return True
+
         if attr('kind') == 'legacy':
             if attr('legacy_kind') in ('build', 'post_build'):
                 if attr('build_type') not in self.build_types:
                     return False
                 if self.platforms is not None:
                     if attr('build_platform') not in self.platforms:
                         return False
                 return True
             elif attr('legacy_kind') == 'job':
                 if self.jobs is not None:
                     if attr('job') not in self.jobs:
                         return False
                 return True
             elif attr('legacy_kind') == 'unittest':
-                if attr('build_type') not in self.build_types:
-                    return False
-                if self.platforms is not None:
-                    if attr('build_platform') not in self.platforms:
-                        return False
-                if self.unittests is not None:
-                    # TODO: optimize this search a bit
-                    for ut in self.unittests:
-                        if attr('unittest_try_name') == ut['test']:
-                            break
-                    else:
-                        return False
-                    if 'platforms' in ut and attr('test_platform') not in ut['platforms']:
-                        return False
-                    if 'only_chunks' in ut and attr('test_chunk') not in ut['only_chunks']:
-                        return False
-                    return True
-                return True
+                return match_test(self.unittests, 'unittest_try_name')
+            elif attr('legacy_kind') == 'talos':
+                return match_test(self.talos, 'talos_try_name')
             return False
         else:
             # TODO: match other kinds
             return False
 
     def __str__(self):
         def none_for_all(list):
             if list is None:
--- a/testing/marionette/client/marionette_driver/marionette.py
+++ b/testing/marionette/client/marionette_driver/marionette.py
@@ -1671,20 +1671,21 @@ class Marionette(object):
             with the specified id.
         """
         body = {"value": target, "using": method}
         if id:
             body["element"] = id
         return self._send_message(
             "findElements", body, key="value" if self.protocol == 1 else None)
 
-
     def get_active_element(self):
-        el = self._send_message("getActiveElement", key="value")
-        return HTMLElement(self, el)
+        el_or_ref = self._send_message("getActiveElement", key="value")
+        if self.protocol < 3:
+            return HTMLElement(self, el_or_ref)
+        return el_or_ref
 
     def log(self, msg, level="INFO"):
         """Stores a timestamped log message in the Marionette server
         for later retrieval.
 
         :param msg: String with message to log.
         :param level: String with log level (e.g. "INFO" or "DEBUG").
             Defaults to "INFO".
--- a/testing/marionette/driver.js
+++ b/testing/marionette/driver.js
@@ -201,16 +201,17 @@ GeckoDriver.prototype.switchToGlobalMess
  * @param {string} name
  *     Suffix of the targetted message listener ({@code Marionette:<suffix>}).
  * @param {Object=} msg
  *     JSON serialisable object to send to the listener.
  * @param {number=} cmdId
  *     Command ID to ensure synchronisity.
  */
 GeckoDriver.prototype.sendAsync = function(name, msg, cmdId) {
+  logger.info(`sendAsync ${this.sessionId}`)
   let curRemoteFrame = this.curBrowser.frameManager.currentRemoteFrame;
   name = "Marionette:" + name;
 
   // TODO(ato): When proxy.AsyncMessageChannel
   // is used for all chrome <-> content communication
   // this can be removed.
   if (cmdId) {
     msg.command_id = cmdId;
@@ -305,16 +306,17 @@ GeckoDriver.prototype.addBrowser = funct
  * switch focus to the start frame when it registers.
  *
  * @param {nsIDOMWindow} win
  *     Window whose browser we need to access.
  * @param {boolean=false} isNewSession
  *     True if this is the first time we're talking to this browser.
  */
 GeckoDriver.prototype.startBrowser = function(win, isNewSession=false) {
+  logger.info(`startBrowser ${this.sessionId}`)
   this.mainFrame = win;
   this.curFrame = null;
   this.addBrowser(win);
   this.curBrowser.isNewSession = isNewSession;
   this.curBrowser.startSession(isNewSession, win, this.whenBrowserStarted.bind(this));
 };
 
 /**
@@ -463,16 +465,19 @@ GeckoDriver.prototype.listeningPromise =
       resolve();
     };
     this.mm.addMessageListener(li, cb);
   });
 };
 
 /** Create a new session. */
 GeckoDriver.prototype.newSession = function*(cmd, resp) {
+  if (this.sessionId) {
+    throw new SessionNotCreatedError("Maximum number of active sessions.")
+  }
   this.sessionId = cmd.parameters.sessionId ||
       cmd.parameters.session_id ||
       element.generateUUID();
 
   this.newSessionCommandId = cmd.id;
   this.setSessionCapabilities(cmd.parameters.capabilities);
   this.scriptTimeout = 10000;
 
@@ -2090,17 +2095,18 @@ GeckoDriver.prototype.addCookie = functi
     Services.cookies.add(
         cookie.domain,
         cookie.path,
         cookie.name,
         cookie.value,
         cookie.secure,
         cookie.httpOnly,
         cookie.session,
-        cookie.expiry);
+        cookie.expiry,
+        {}); // originAttributes
     return true;
   };
   this.mm.addMessageListener("Marionette:addCookie", cb);
   yield this.listener.addCookie(cmd.parameters.cookie);
 };
 
 /**
  * Get all the cookies for the current domain.
@@ -2648,17 +2654,17 @@ GeckoDriver.prototype.receiveMessage = f
       }
       break;
 
     case "Marionette:getVisibleCookies":
       let [currentPath, host] = message.json;
       let isForCurrentPath = path => currentPath.indexOf(path) != -1;
       let results = [];
 
-      let en = cookieManager.getCookiesFromHost(host);
+      let en = cookieManager.getCookiesFromHost(host, {});
       while (en.hasMoreElements()) {
         let cookie = en.getNext().QueryInterface(Ci.nsICookie2);
         // take the hostname and progressively shorten
         let hostname = host;
         do {
           if ((cookie.host == "." + hostname || cookie.host == hostname) &&
               isForCurrentPath(cookie.path)) {
             results.push({
--- a/testing/marionette/harness/marionette/tests/unit/test_findelement.py
+++ b/testing/marionette/harness/marionette/tests/unit/test_findelement.py
@@ -144,19 +144,18 @@ class TestElements(MarionetteTestCase):
 
     def test_css_selector_scope_doesnt_start_at_rootnode(self):
         el = self.marionette.find_element(By.ID, "mozLink")
         nav_el = self.marionette.find_element(By.ID, "testDiv")
         found_els = nav_el.find_elements(By.CSS_SELECTOR, "a")
         self.assertFalse(el.id in [found_el.id for found_el in found_els])
 
     def test_finding_active_element_returns_element(self):
-        fbody = self.marionette.find_element(By.TAG_NAME, 'body')
-        abody = self.marionette.get_active_element()
-        self.assertEqual(fbody, abody)
+        body = self.marionette.find_element(By.TAG_NAME, "body")
+        self.assertEqual(body, self.marionette.get_active_element())
 
     def test_unknown_selector(self):
         with self.assertRaises(InvalidSelectorException):
             self.marionette.find_element("foo", "bar")
 
     def test_element_id_is_valid_uuid(self):
         el = self.marionette.find_element(By.TAG_NAME, "body")
         uuid_regex = re.compile('^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$')
--- a/testing/marionette/harness/marionette/tests/unit/test_session.py
+++ b/testing/marionette/harness/marionette/tests/unit/test_session.py
@@ -1,13 +1,14 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 from marionette import MarionetteTestCase
+from marionette_driver.errors import SessionNotCreatedException
 
 class TestSession(MarionetteTestCase):
     def setUp(self):
         super(TestSession, self).setUp()
         self.marionette.delete_session()
 
     def test_new_session_returns_capabilities(self):
         # Sends newSession
@@ -25,19 +26,24 @@ class TestSession(MarionetteTestCase):
         # Optional capabilities we want Marionette to support
         self.assertIn("device", caps)
         self.assertIn("rotatable", caps)
         self.assertIn("takesScreenshot", caps)
         self.assertIn("version", caps)
 
     def test_we_can_get_the_session_id(self):
         # Sends newSession
-        caps = self.marionette.start_session()
+        self.marionette.start_session()
 
         self.assertTrue(self.marionette.session_id is not None)
         self.assertTrue(isinstance(self.marionette.session_id, unicode))
 
     def test_we_can_set_the_session_id(self):
         # Sends newSession
-        caps = self.marionette.start_session(session_id="ILoveCheese")
+        self.marionette.start_session(session_id="ILoveCheese")
 
         self.assertEqual(self.marionette.session_id, "ILoveCheese")
         self.assertTrue(isinstance(self.marionette.session_id, unicode))
+
+    def test_we_only_support_one_active_session_at_a_time(self):
+        self.marionette.start_session()
+        self.assertTrue(isinstance(self.marionette.session_id, unicode))
+        self.assertRaises(SessionNotCreatedException, self.marionette._send_message, "newSession", {})
--- a/testing/marionette/listener.js
+++ b/testing/marionette/listener.js
@@ -1061,29 +1061,20 @@ function* findElementsContent(strategy, 
   }
 
   let els = yield element.find(curContainer, strategy, selector, opts);
   let elRefs = seenEls.addAll(els);
   let webEls = elRefs.map(element.makeWebElement);
   return webEls;
 }
 
-/**
- * Find and return the active element on the page.
- *
- * @return {WebElement}
- *     Reference to web element.
- */
+/** Find and return the active element on the page. */
 function getActiveElement() {
   let el = curContainer.frame.document.activeElement;
-  let elRef = seenEls.add(el);
-  // TODO(ato): This incorrectly returns
-  // the element's associated UUID as a string
-  // instead of a web element.
-  return elRef;
+  return element.toJson(el, seenEls);
 }
 
 /**
  * Send click event to element.
  *
  * @param {WebElement} id
  *     Reference to the web element to click.
  */
--- a/testing/mozharness/configs/hazards/common.py
+++ b/testing/mozharness/configs/hazards/common.py
@@ -17,28 +17,27 @@ config = {
 
     # These paths are relative to the tooltool checkout location
     "sixgill": "sixgill/usr/libexec/sixgill",
     "sixgill_bin": "sixgill/usr/bin",
 
     "python": "python",
 
     "exes": {
-        'hgtool.py': '%(abs_tools_dir)s/buildfarm/utils/hgtool.py',
         'gittool.py': '%(abs_tools_dir)s/buildfarm/utils/gittool.py',
         'tooltool.py': '/tools/tooltool.py',
         "virtualenv": [PYTHON_DIR + "/bin/python", "/tools/misc-python/virtualenv.py"],
     },
 
     "force_clobber": True,
     'vcs_share_base': HG_SHARE_BASE_DIR,
 
     "repos": [{
         "repo": "https://hg.mozilla.org/build/tools",
-        "revision": "default",
+        "branch": "default",
         "dest": "tools"
     }],
 
     "upload_remote_baseuri": 'https://ftp-ssl.mozilla.org/',
     "default_blob_upload_servers": [
         "https://blobupload.elasticbeanstalk.com",
     ],
     "blob_uploader_auth_file": os.path.join(os.getcwd(), "oauth.txt"),
--- a/testing/mozharness/configs/partner_repacks/release_mozilla-release_android.py
+++ b/testing/mozharness/configs/partner_repacks/release_mozilla-release_android.py
@@ -10,17 +10,17 @@ KEY_ALIAS = "release"
 
 config = {
     "log_name": "partner_repack",
     "locales_file": "buildbot-configs/mozilla/l10n-changesets_mobile-release.json",
     "additional_locales": ['en-US'],
     "platforms": ["android"],
     "repos": [{
         "repo": "https://hg.mozilla.org/build/buildbot-configs",
-        "revision": "default",
+        "branch": "default",
     }],
     'vcs_share_base': HG_SHARE_BASE_DIR,
     "ftp_upload_base_dir": FTP_UPLOAD_BASE_DIR,
     "ftp_ssh_key": FTP_SSH_KEY,
     "ftp_user": FTP_USER,
     "ftp_server": FTP_SERVER,
     "installer_base_names": {
         "android": APK_BASE_NAME,
--- a/testing/mozharness/configs/partner_repacks/staging_release_mozilla-release_android.py
+++ b/testing/mozharness/configs/partner_repacks/staging_release_mozilla-release_android.py
@@ -15,17 +15,17 @@ KEY_ALIAS = "nightly"
 
 config = {
     "log_name": "partner_repack",
     "locales_file": "buildbot-configs/mozilla/l10n-changesets_mobile-release.json",
     "additional_locales": ['en-US'],
     "platforms": ["android"],
     "repos": [{
         "repo": "https://hg.mozilla.org/build/buildbot-configs",
-        "revision": "default",
+        "branch": "default",
     }],
     'vcs_share_base': HG_SHARE_BASE_DIR,
     "ftp_upload_base_dir": FTP_UPLOAD_BASE_DIR,
     "ftp_ssh_key": FTP_SSH_KEY,
     "ftp_user": FTP_USER,
     "ftp_server": FTP_SERVER,
     "installer_base_names": {
         "android": APK_BASE_NAME,
--- a/testing/mozharness/configs/releases/dev_postrelease_firefox_beta.py
+++ b/testing/mozharness/configs/releases/dev_postrelease_firefox_beta.py
@@ -1,16 +1,16 @@
 config = {
     # date is used for staging mozilla-beta
     "log_name": "bump_date",
     "version_files": [{"file": "browser/config/version_display.txt"}],
     "repo": {
         # date is used for staging mozilla-beta
         "repo": "https://hg.mozilla.org/projects/date",
-        "revision": "default",
+        "branch": "default",
         "dest": "date",
         "vcs": "hg",
     },
     # date is used for staging mozilla-beta
     "push_dest": "ssh://hg.mozilla.org/projects/date",
     "ignore_no_changes": True,
     "ssh_user": "ffxbld",
     "ssh_key": "~/.ssh/ffxbld_rsa",
--- a/testing/mozharness/configs/releases/dev_postrelease_firefox_release.py
+++ b/testing/mozharness/configs/releases/dev_postrelease_firefox_release.py
@@ -3,17 +3,17 @@ config = {
     "version_files": [
         {"file": "browser/config/version.txt"},
         {"file": "browser/config/version_display.txt"},
         {"file": "config/milestone.txt"},
     ],
     "repo": {
         # jamun is used for staging mozilla-release
         "repo": "https://hg.mozilla.org/projects/jamun",
-        "revision": "default",
+        "branch": "default",
         "dest": "jamun",
         "vcs": "hg",
     },
     "push_dest": "ssh://hg.mozilla.org/projects/jamun",
     "ignore_no_changes": True,
     "ssh_user": "ffxbld",
     "ssh_key": "~/.ssh/ffxbld_rsa",
 }
--- a/testing/mozharness/configs/releases/dev_updates_firefox_beta.py
+++ b/testing/mozharness/configs/releases/dev_updates_firefox_beta.py
@@ -1,15 +1,15 @@
 
 config = {
     "log_name": "bump_beta_dev",
     # TODO: use real repo
     "repo": {
         "repo": "https://hg.mozilla.org/users/raliiev_mozilla.com/tools",
-        "revision": "default",
+        "branch": "default",
         "dest": "tools",
         "vcs": "hg",
     },
     # TODO: use real repo
     "push_dest": "ssh://hg.mozilla.org/users/raliiev_mozilla.com/tools",
     # date repo used for staging beta
     "shipped-locales-url": "https://hg.mozilla.org/projects/date/raw-file/{revision}/browser/locales/shipped-locales",
     "ignore_no_changes": True,
--- a/testing/mozharness/configs/releases/dev_updates_firefox_release.py
+++ b/testing/mozharness/configs/releases/dev_updates_firefox_release.py
@@ -1,15 +1,15 @@
 
 config = {
     "log_name": "updates_release_dev",
     # TODO: use real repo
     "repo": {
         "repo": "https://hg.mozilla.org/users/raliiev_mozilla.com/tools",
-        "revision": "default",
+        "branch": "default",
         "dest": "tools",
         "vcs": "hg",
     },
     # TODO: use real repo
     "push_dest": "ssh://hg.mozilla.org/users/raliiev_mozilla.com/tools",
     # jamun  repo used for staging release
     "shipped-locales-url": "https://hg.mozilla.org/projects/jamun/raw-file/{revision}/browser/locales/shipped-locales",
     "ignore_no_changes": True,
--- a/testing/mozharness/configs/releases/postrelease_esr38.py
+++ b/testing/mozharness/configs/releases/postrelease_esr38.py
@@ -3,17 +3,17 @@ config = {
     "version_files": [
         {"file": "browser/config/version.txt"},
         # TODO: enable this for esr45
         # {"file": "browser/config/version_display.txt"},
         {"file": "config/milestone.txt"},
     ],
     "repo": {
         "repo": "https://hg.mozilla.org/releases/mozilla-esr38",
-        "revision": "default",
+        "branch": "default",
         "dest": "mozilla-esr38",
         "vcs": "hg",
     },
     "push_dest": "ssh://hg.mozilla.org/releases/mozilla-esr38",
     "ignore_no_changes": True,
     "ssh_user": "ffxbld",
     "ssh_key": "~/.ssh/ffxbld_rsa",
 }
--- a/testing/mozharness/configs/releases/postrelease_firefox_beta.py
+++ b/testing/mozharness/configs/releases/postrelease_firefox_beta.py
@@ -1,14 +1,14 @@
 config = {
     "log_name": "bump_beta",
     "version_files": [{"file": "browser/config/version_display.txt"}],
     "repo": {
         "repo": "https://hg.mozilla.org/releases/mozilla-beta",
-        "revision": "default",
+        "branch": "default",
         "dest": "mozilla-beta",
         "vcs": "hg",
     },
     "push_dest": "ssh://hg.mozilla.org/releases/mozilla-beta",
     "ignore_no_changes": True,
     "ssh_user": "ffxbld",
     "ssh_key": "~/.ssh/ffxbld_rsa",
 }