Merge mozilla-central to b2g-inbound
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Tue, 28 Jul 2015 16:44:03 +0200
changeset 286629 e7a1dd4f8533fbbe53bc5197b6b1a5115cc00210
parent 286628 7a4c3baef26dbac76097e8de1e8a9b2b587eda41 (current diff)
parent 286575 bc589dd18ad57ab24bd70070855c4c9568796cc5 (diff)
child 286630 a24c22fab6989e5877d39b1eddc6468d8801e4ca
push id5067
push userraliiev@mozilla.com
push dateMon, 21 Sep 2015 14:04:52 +0000
treeherdermozilla-beta@14221ffe5b2f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone42.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-central to b2g-inbound
dom/base/test/iframe_cloning_fileList.html
dom/base/test/script_cloning_fileList.js
dom/base/test/test_cloning_fileList.html
--- a/b2g/config/mozconfigs/linux32_gecko/debug
+++ b/b2g/config/mozconfigs/linux32_gecko/debug
@@ -1,16 +1,14 @@
 MOZ_AUTOMATION_L10N_CHECK=0
 MOZ_AUTOMATION_UPLOAD_SYMBOLS=0
 MOZ_AUTOMATION_UPDATE_PACKAGING=0
 . "$topsrcdir/b2g/config/mozconfigs/common"
 . "$topsrcdir/build/unix/mozconfig.linux32"
 
-ac_add_options --enable-default-toolkit=cairo-gtk2
-
 ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL}
 ac_add_options --enable-update-packaging
 ac_add_options --enable-signmar
 ac_add_options --enable-debug
 
 # This will overwrite the default of stripping everything and keep the symbol table.
 # This is useful for profiling and debugging and only increases the package size
 # by 2 MBs.
--- a/b2g/config/mozconfigs/linux32_gecko/nightly
+++ b/b2g/config/mozconfigs/linux32_gecko/nightly
@@ -1,17 +1,15 @@
 MOZ_AUTOMATION_L10N_CHECK=0
 MOZ_AUTOMATION_UPLOAD_SYMBOLS=0
 MOZ_AUTOMATION_UPDATE_PACKAGING=0
 MOZ_AUTOMATION_SDK=0
 . "$topsrcdir/b2g/config/mozconfigs/common"
 . "$topsrcdir/build/unix/mozconfig.linux32"
 
-ac_add_options --enable-default-toolkit=cairo-gtk2
-
 ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL}
 ac_add_options --enable-update-packaging
 ac_add_options --enable-signmar
 
 # This will overwrite the default of stripping everything and keep the symbol table.
 # This is useful for profiling and debugging and only increases the package size
 # by 2 MBs.
 STRIP_FLAGS="--strip-debug"
--- a/b2g/config/mozconfigs/linux64_gecko/debug
+++ b/b2g/config/mozconfigs/linux64_gecko/debug
@@ -1,16 +1,14 @@
 MOZ_AUTOMATION_L10N_CHECK=0
 MOZ_AUTOMATION_UPLOAD_SYMBOLS=0
 MOZ_AUTOMATION_UPDATE_PACKAGING=0
 . "$topsrcdir/b2g/config/mozconfigs/common"
 . "$topsrcdir/build/unix/mozconfig.linux"
 
-ac_add_options --enable-default-toolkit=cairo-gtk2
-
 ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL}
 ac_add_options --enable-update-packaging
 ac_add_options --enable-signmar
 ac_add_options --enable-debug
 
 # This will overwrite the default of stripping everything and keep the symbol table.
 # This is useful for profiling and debugging and only increases the package size
 # by 2 MBs.
--- a/b2g/config/mozconfigs/linux64_gecko/nightly
+++ b/b2g/config/mozconfigs/linux64_gecko/nightly
@@ -1,17 +1,15 @@
 MOZ_AUTOMATION_L10N_CHECK=0
 MOZ_AUTOMATION_UPLOAD_SYMBOLS=0
 MOZ_AUTOMATION_UPDATE_PACKAGING=0
 MOZ_AUTOMATION_SDK=0
 . "$topsrcdir/b2g/config/mozconfigs/common"
 . "$topsrcdir/build/unix/mozconfig.linux"
 
-ac_add_options --enable-default-toolkit=cairo-gtk2
-
 ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL}
 ac_add_options --enable-update-packaging
 ac_add_options --enable-signmar
 
 # This will overwrite the default of stripping everything and keep the symbol table.
 # This is useful for profiling and debugging and only increases the package size
 # by 2 MBs.
 STRIP_FLAGS="--strip-debug"
--- a/b2g/dev/config/mozconfigs/linux64/mulet
+++ b/b2g/dev/config/mozconfigs/linux64/mulet
@@ -1,11 +1,10 @@
 MOZ_AUTOMATION_L10N_CHECK=0
 MOZ_AUTOMATION_UPLOAD_SYMBOLS=0
 MOZ_AUTOMATION_UPDATE_PACKAGING=0
 MOZ_AUTOMATION_SDK=0
 . "$topsrcdir/browser/config/mozconfigs/linux64/nightly"
 
-ac_add_options --enable-default-toolkit=cairo-gtk2
 ac_add_options --enable-application=b2g/dev
 
 # Include Firefox OS fonts.
 MOZTTDIR=$topsrcdir/moz-tt
--- a/b2g/installer/package-manifest.in
+++ b/b2g/installer/package-manifest.in
@@ -107,17 +107,17 @@
 @RESPATH@/resources.arsc
 @RESPATH@/classes.dex
 @RESPATH@/res/drawable
 @RESPATH@/res/drawable-hdpi
 @RESPATH@/res/layout
 #endif
 #ifdef MOZ_GTK3
 @BINPATH@/@DLL_PREFIX@mozgtk@DLL_SUFFIX@
-@BINPATH@/@DLL_PREFIX@mozgtk2@DLL_SUFFIX@
+@BINPATH@/gtk2/@DLL_PREFIX@mozgtk@DLL_SUFFIX@
 #endif
 
 [browser]
 ; [Base Browser Files]
 #ifndef XP_UNIX
 @BINPATH@/@MOZ_APP_NAME@.exe
 #else
 @BINPATH@/@MOZ_APP_NAME@-bin
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -349,16 +349,20 @@ pref("browser.urlbar.suggest.history",  
 pref("browser.urlbar.suggest.bookmark",             true);
 pref("browser.urlbar.suggest.openpage",             true);
 #ifdef NIGHTLY_BUILD
 pref("browser.urlbar.suggest.searches",             true);
 #else
 pref("browser.urlbar.suggest.searches",             false);
 #endif
 
+// Limit the number of characters sent to the current search engine to fetch
+// suggestions.
+pref("browser.urlbar.maxCharsForSearchSuggestions", 20);
+
 // Restrictions to current suggestions can also be applied (intersection).
 // Typed suggestion works only if history is set to true.
 pref("browser.urlbar.suggest.history.onlyTyped",    false);
 
 pref("browser.urlbar.formatting.enabled", true);
 pref("browser.urlbar.trimURLs", true);
 
 pref("browser.altClickSave", false);
--- a/browser/base/content/test/general/browser.ini
+++ b/browser/base/content/test/general/browser.ini
@@ -129,16 +129,17 @@ skip-if = e10s # Bug 1093153 - no about:
 [browser_action_searchengine.js]
 [browser_action_searchengine_alias.js]
 [browser_addKeywordSearch.js]
 [browser_search_favicon.js]
 [browser_alltabslistener.js]
 [browser_autocomplete_a11y_label.js]
 skip-if = e10s # Bug 1101993 - times out for unknown reasons when run in the dir (works on its own)
 [browser_autocomplete_cursor.js]
+[browser_autocomplete_edit_completed.js]
 [browser_autocomplete_enter_race.js]
 [browser_autocomplete_no_title.js]
 [browser_autocomplete_autoselect.js]
 [browser_autocomplete_oldschool_wrap.js]
 [browser_autocomplete_tag_star_visibility.js]
 [browser_backButtonFitts.js]
 skip-if = os == "mac" # The Fitt's Law back button is not supported on OS X
 [browser_beforeunload_duplicate_dialogs.js]
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/general/browser_autocomplete_edit_completed.js
@@ -0,0 +1,53 @@
+add_task(function*() {
+  yield PlacesTestUtils.clearHistory();
+
+  yield PlacesTestUtils.addVisits([
+    { uri: makeURI("http://example.com/foo") },
+    { uri: makeURI("http://example.com/foo/bar") },
+  ]);
+
+  Services.prefs.setBoolPref("browser.urlbar.unifiedcomplete", true);
+  yield* do_test();
+  Services.prefs.setBoolPref("browser.urlbar.unifiedcomplete", false);
+  yield* do_test();
+});
+
+registerCleanupFunction(function* () {
+  Services.prefs.clearUserPref("browser.urlbar.unifiedcomplete");
+  yield PlacesTestUtils.clearHistory();
+});
+
+function* do_test() {
+  gBrowser.selectedTab = gBrowser.addTab("about:blank");
+  gURLBar.focus();
+
+  yield promiseAutocompleteResultPopup("http://example.com");
+
+  let popup = gURLBar.popup;
+  let list = popup.richlistbox;
+  let initialIndex = list.selectedIndex;
+
+  info("Key Down to select the next item.");
+  EventUtils.synthesizeKey("VK_DOWN", {});
+
+  let nextIndex = initialIndex + 1;
+  let nextValue = gURLBar.controller.getFinalCompleteValueAt(nextIndex);
+  is(list.selectedIndex, nextIndex, "The next item is selected.");
+  is(gURLBar.value, nextValue, "The selected URL is completed.");
+
+  info("Press backspace");
+  EventUtils.synthesizeKey("VK_BACK_SPACE", {});
+  yield promiseSearchComplete();
+
+  let editedValue = gURLBar.value;
+  is(list.selectedIndex, initialIndex, "The initial index is selected again.");
+  isnot(editedValue, nextValue, "The URL has changed.");
+
+  info("Press return to load edited URL.");
+  EventUtils.synthesizeKey("VK_RETURN", {});
+  yield Promise.all([
+    promisePopupHidden(gURLBar.popup),
+    waitForDocLoadAndStopIt("http://" + editedValue)]);
+
+  gBrowser.removeTab(gBrowser.selectedTab);
+}
--- a/browser/base/content/test/social/browser_social_multiworker.js
+++ b/browser/base/content/test/social/browser_social_multiworker.js
@@ -48,17 +48,18 @@ var tests = {
       port.postMessage({topic: "test-init"});
     }
 
     for (let p of Social.providers) {
       oneWorkerTest(p);
     }
 
     waitForCondition(function() messageReceived == Social.providers.length,
-                     next, "received messages from all workers");
+                     next, "received messages from all workers",
+                     /* increase timeout because shutting down a child process is slow */ 60);
   },
 
    testMultipleWorkerEnabling: function(next) {
      // test that all workers are enabled when we allow multiple workers
      for (let p of Social.providers) {
        ok(p.enabled, "provider enabled");
        let port = p.getWorkerPort();
        ok(port, "worker enabled");
--- a/browser/components/migration/ChromeProfileMigrator.js
+++ b/browser/components/migration/ChromeProfileMigrator.js
@@ -1,35 +1,40 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
  * vim: sw=2 ts=2 sts=2 et */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
-const Cc = Components.classes;
-const Ci = Components.interfaces;
-const Cu = Components.utils;
-const Cr = Components.results;
+const { classes: Cc, interfaces: Ci, results: Cr, utils: Cu } = Components;
 
 const FILE_INPUT_STREAM_CID = "@mozilla.org/network/file-input-stream;1";
 
 const S100NS_FROM1601TO1970 = 0x19DB1DED53E8000;
 const S100NS_PER_MS = 10;
 
+const AUTH_TYPE = {
+  SCHEME_HTML: 0,
+  SCHEME_BASIC: 1,
+  SCHEME_DIGEST: 2
+};
+
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/NetUtil.jsm");
 Cu.import("resource://gre/modules/FileUtils.jsm");
 Cu.import("resource://gre/modules/Task.jsm");
 Cu.import("resource:///modules/MigrationUtils.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
                                   "resource://gre/modules/PlacesUtils.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "OSCrypto",
+                                  "resource://gre/modules/OSCrypto.jsm");
 
 /**
  * Convert Chrome time format to Date object
  *
  * @param   aTime
  *          Chrome time 
  * @return  converted Date object
  * @note    Google Chrome uses FILETIME / 10 as time.
@@ -88,17 +93,21 @@ ChromeProfileMigrator.prototype = Object
 ChromeProfileMigrator.prototype.getResources =
   function Chrome_getResources(aProfile) {
     if (this._chromeUserDataFolder) {
       let profileFolder = this._chromeUserDataFolder.clone();
       profileFolder.append(aProfile.id);
       if (profileFolder.exists()) {
         let possibleResources = [GetBookmarksResource(profileFolder),
                                  GetHistoryResource(profileFolder),
-                                 GetCookiesResource(profileFolder)];
+                                 GetCookiesResource(profileFolder),
+#ifdef XP_WIN
+                                 GetWindowsPasswordsResource(profileFolder)
+#endif
+                                 ];
         return [r for each (r in possibleResources) if (r != null)];
       }
     }
     return [];
   };
 
 Object.defineProperty(ChromeProfileMigrator.prototype, "sourceProfiles", {
   get: function Chrome_sourceProfiles() {
@@ -348,13 +357,108 @@ function GetCookiesResource(aProfileFold
           aCallback(aReason == Ci.mozIStorageStatementCallback.REASON_FINISHED);
         },
       });
       stmt.finalize();
     }
   }
 }
 
+function GetWindowsPasswordsResource(aProfileFolder) {
+  let loginFile = aProfileFolder.clone();
+  loginFile.append("Login Data");
+  if (!loginFile.exists())
+    return null;
+
+  return {
+    type: MigrationUtils.resourceTypes.PASSWORDS,
+
+    migrate(aCallback) {
+      let dbConn = Services.storage.openUnsharedDatabase(loginFile);
+      let stmt = dbConn.createAsyncStatement(`
+        SELECT origin_url, action_url, username_element, username_value,
+        password_element, password_value, signon_realm, scheme, date_created,
+        times_used FROM logins WHERE blacklisted_by_user = 0`);
+      let crypto = new OSCrypto();
+
+      stmt.executeAsync({
+        _rowToLoginInfo(row) {
+          let loginInfo = {
+            username: row.getResultByName("username_value"),
+            password: crypto.decryptData(row.getResultByName("password_value")),
+            hostName: NetUtil.newURI(row.getResultByName("origin_url")).prePath,
+            submitURL: null,
+            httpRealm: null,
+            usernameElement: row.getResultByName("username_element"),
+            passwordElement: row.getResultByName("password_element"),
+            timeCreated: chromeTimeToDate(row.getResultByName("date_created") + 0).getTime(),
+            timesUsed: row.getResultByName("times_used") + 0,
+          };
+
+          switch (row.getResultByName("scheme")) {
+            case AUTH_TYPE.SCHEME_HTML:
+              loginInfo.submitURL = NetUtil.newURI(row.getResultByName("action_url")).prePath;
+              break;
+            case AUTH_TYPE.SCHEME_BASIC:
+            case AUTH_TYPE.SCHEME_DIGEST:
+              // signon_realm format is URIrealm, so we need remove URI
+              loginInfo.httpRealm = row.getResultByName("signon_realm")
+                                    .substring(loginInfo.hostName.length + 1);
+              break;
+            default:
+              throw new Error("Login data scheme type not supported: " +
+                              row.getResultByName("scheme"));
+          }
+
+          return loginInfo;
+        },
+
+        handleResult(aResults) {
+          for (let row = aResults.getNextRow(); row; row = aResults.getNextRow()) {
+            try {
+              let loginInfo = this._rowToLoginInfo(row);
+              let login = Cc["@mozilla.org/login-manager/loginInfo;1"].createInstance(Ci.nsILoginInfo);
+
+              login.init(loginInfo.hostName, loginInfo.submitURL, loginInfo.httpRealm,
+                         loginInfo.username, loginInfo.password, loginInfo.usernameElement,
+                         loginInfo.passwordElement);
+              login.QueryInterface(Ci.nsILoginMetaInfo);
+              login.timeCreated = loginInfo.timeCreated;
+              login.timeLastUsed = loginInfo.timeCreated;
+              login.timePasswordChanged = loginInfo.timeCreated;
+              login.timesUsed = loginInfo.timesUsed;
+
+              // Add the login only if there's not an existing entry
+              let logins = Services.logins.findLogins({}, login.hostname,
+                                                      login.formSubmitURL,
+                                                      login.httpRealm);
+
+              // Bug 1187190: Password changes should be propagated depending on timestamps.
+              if (!logins.some(l => login.matches(l, true))) {
+                Services.logins.addLogin(login);
+              }
+            } catch (e) {
+              Cu.reportError(e);
+            }
+          }
+        },
+
+        handleError(aError) {
+          Cu.reportError("Async statement execution returned with '" +
+                         aError.result + "', '" + aError.message + "'");
+        },
+
+        handleCompletion(aReason) {
+          dbConn.asyncClose();
+          aCallback(aReason == Ci.mozIStorageStatementCallback.REASON_FINISHED);
+          crypto.finalize();
+        },
+      });
+      stmt.finalize();
+    }
+  };
+}
+
 ChromeProfileMigrator.prototype.classDescription = "Chrome Profile Migrator";
 ChromeProfileMigrator.prototype.contractID = "@mozilla.org/profile/migrator;1?app=browser&type=chrome";
 ChromeProfileMigrator.prototype.classID = Components.ID("{4cec1de4-1671-4fc3-a53e-6c539dc77a26}");
 
 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([ChromeProfileMigrator]);
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..914149c710a8c748b97543c541aaedfee74aa1f6
GIT binary patch
literal 22528
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
V5Zr`7u?69O0s;a80)oF6{ts6yA$0%%
new file mode 100644
--- /dev/null
+++ b/browser/components/migration/tests/unit/test_Chrome_passwords.js
@@ -0,0 +1,213 @@
+Cu.import("resource://gre/modules/OSCrypto.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
+
+const PROFILE = {
+  id: "Default",
+  name: "Person 1",
+};
+
+const TEST_LOGINS = [
+  {
+    id: 1, // id of the row in the chrome login db
+    username: "username",
+    password: "password",
+    hostname: "https://c9.io",
+    formSubmitURL: "https://c9.io",
+    httpRealm: null,
+    usernameField: "inputEmail",
+    passwordField: "inputPassword",
+    timeCreated: 1437418416037,
+    timePasswordChanged: 1437418416037,
+    timesUsed: 1,
+  },
+  {
+    id: 2,
+    username: "username@gmail.com",
+    password: "password2",
+    hostname: "https://accounts.google.com",
+    formSubmitURL: "https://accounts.google.com",
+    httpRealm: null,
+    usernameField: "Email",
+    passwordField: "Passwd",
+    timeCreated: 1437418446598,
+    timePasswordChanged: 1437418446598,
+    timesUsed: 6,
+  },
+  {
+    id: 3,
+    username: "username",
+    password: "password3",
+    hostname: "https://www.facebook.com",
+    formSubmitURL: "https://www.facebook.com",
+    httpRealm: null,
+    usernameField: "email",
+    passwordField: "pass",
+    timeCreated: 1437418478851,
+    timePasswordChanged: 1437418478851,
+    timesUsed: 1,
+  },
+  {
+    id: 4,
+    username: "user",
+    password: "password",
+    hostname: "http://httpbin.org",
+    formSubmitURL: null,
+    httpRealm: "me@kennethreitz.com", // Digest auth.
+    usernameField: "",
+    passwordField: "",
+    timeCreated: 1437787462368,
+    timePasswordChanged: 1437787462368,
+    timesUsed: 1,
+  },
+  {
+    id: 5,
+    username: "buser",
+    password: "bpassword",
+    hostname: "http://httpbin.org",
+    formSubmitURL: null,
+    httpRealm: "Fake Realm", // Basic auth.
+    usernameField: "",
+    passwordField: "",
+    timeCreated: 1437787539233,
+    timePasswordChanged: 1437787539233,
+    timesUsed: 1,
+  },
+];
+
+let crypto = new OSCrypto();
+let dbConn;
+
+function promiseSetPassword(login) {
+  return new Promise((resolve, reject) => {
+    let stmt = dbConn.createAsyncStatement(`
+      UPDATE logins
+      SET password_value = :password_value
+      WHERE rowid = :rowid
+    `);
+    let passwordValue = crypto.encryptData(login.password);
+    stmt.bindBlobByName("password_value", passwordValue, passwordValue.length);
+    stmt.params.rowid = login.id;
+
+    stmt.executeAsync({
+      handleError(aError) {
+        reject("Error with the query: " + aError.message);
+      },
+
+      handleCompletion(aReason) {
+        if (aReason === Ci.mozIStorageStatementCallback.REASON_FINISHED){
+          resolve();
+        } else {
+          reject("Query has failed: " + aReason);
+        }
+      },
+    });
+    stmt.finalize();
+  });
+}
+
+function checkLoginsAreEqual(passwordManagerLogin, chromeLogin, id) {
+  passwordManagerLogin.QueryInterface(Ci.nsILoginMetaInfo);
+
+  Assert.equal(passwordManagerLogin.username, chromeLogin.username,
+               "The two logins ID " + id + " have the same username");
+  Assert.equal(passwordManagerLogin.password, chromeLogin.password,
+               "The two logins ID " + id + " have the same password");
+  Assert.equal(passwordManagerLogin.hostname, chromeLogin.hostname,
+               "The two logins ID " + id + " have the same hostname");
+  Assert.equal(passwordManagerLogin.formSubmitURL, chromeLogin.formSubmitURL,
+               "The two logins ID " + id + " have the same formSubmitURL");
+  Assert.equal(passwordManagerLogin.httpRealm, chromeLogin.httpRealm,
+               "The two logins ID " + id + " have the same httpRealm");
+  Assert.equal(passwordManagerLogin.usernameField, chromeLogin.usernameField,
+               "The two logins ID " + id + " have the same usernameElement");
+  Assert.equal(passwordManagerLogin.passwordField, chromeLogin.passwordField,
+               "The two logins ID " + id + " have the same passwordElement");
+  Assert.equal(passwordManagerLogin.timeCreated, chromeLogin.timeCreated,
+               "The two logins ID " + id + " have the same timeCreated");
+  Assert.equal(passwordManagerLogin.timePasswordChanged, chromeLogin.timePasswordChanged,
+               "The two logins ID " + id + " have the same timePasswordChanged");
+  Assert.equal(passwordManagerLogin.timesUsed, chromeLogin.timesUsed,
+               "The two logins ID " + id + " have the same timesUsed");
+}
+
+function generateDifferentLogin(login) {
+  let newLogin = Cc["@mozilla.org/login-manager/loginInfo;1"].createInstance(Ci.nsILoginInfo);
+
+  newLogin.init(login.hostname, login.formSubmitURL, null,
+                login.username, login.password + 1, login.usernameField + 1,
+                login.passwordField + 1);
+  newLogin.QueryInterface(Ci.nsILoginMetaInfo);
+  newLogin.timeCreated = login.timeCreated + 1;
+  newLogin.timePasswordChanged = login.timePasswordChanged + 1;
+  newLogin.timesUsed = login.timesUsed + 1;
+  return newLogin;
+}
+
+add_task(function* setup() {
+  let loginDataFile = do_get_file("AppData/Local/Google/Chrome/User Data/Default/Login Data");
+  dbConn = Services.storage.openUnsharedDatabase(loginDataFile);
+  registerFakePath("LocalAppData", do_get_file("AppData/Local/"));
+
+  do_register_cleanup(() => {
+    Services.logins.removeAllLogins();
+    dbConn.asyncClose();
+    crypto.finalize();
+  });
+});
+
+add_task(function* test_importIntoEmptyDB() {
+  for (let login of TEST_LOGINS) {
+    yield promiseSetPassword(login);
+  }
+
+  let migrator = MigrationUtils.getMigrator("chrome");
+  Assert.ok(migrator.sourceExists, "Sanity check the source exists");
+
+  let logins = Services.logins.getAllLogins({});
+  Assert.equal(logins.length, 0, "There are no logins initially");
+
+  // Migrate the logins.
+  yield promiseMigration(migrator, MigrationUtils.resourceTypes.PASSWORDS, PROFILE);
+
+  logins = Services.logins.getAllLogins({});
+  Assert.equal(logins.length, TEST_LOGINS.length, "Check login count after importing the data");
+
+  for (let i = 0; i < TEST_LOGINS.length; i++) {
+    checkLoginsAreEqual(logins[i], TEST_LOGINS[i], i + 1);
+  }
+});
+
+// Test that existing logins for the same primary key don't get overwritten
+add_task(function* test_importExistingLogins() {
+  let migrator = MigrationUtils.getMigrator("chrome");
+  Assert.ok(migrator.sourceExists, "Sanity check the source exists");
+
+  Services.logins.removeAllLogins();
+  let logins = Services.logins.getAllLogins({});
+  Assert.equal(logins.length, 0, "There are no logins after removing all of them");
+
+  let newLogins = [];
+
+  // Create 3 new logins that are different but where the key properties are still the same.
+  for (let i = 0; i < 3; i++) {
+    newLogins.push(generateDifferentLogin(TEST_LOGINS[i]));
+    Services.logins.addLogin(newLogins[i]);
+  }
+
+  logins = Services.logins.getAllLogins({});
+  Assert.equal(logins.length, newLogins.length, "Check login count after the insertion");
+
+  for (let i = 0; i < newLogins.length; i++) {
+    checkLoginsAreEqual(logins[i], newLogins[i], i + 1);
+  }
+  // Migrate the logins.
+  yield promiseMigration(migrator, MigrationUtils.resourceTypes.PASSWORDS, PROFILE);
+
+  logins = Services.logins.getAllLogins({});
+  Assert.equal(logins.length, TEST_LOGINS.length,
+               "Check there are still the same number of logins after re-importing the data");
+
+  for (let i = 0; i < newLogins.length; i++) {
+    checkLoginsAreEqual(logins[i], newLogins[i], i + 1);
+  }
+});
--- a/browser/components/migration/tests/unit/xpcshell.ini
+++ b/browser/components/migration/tests/unit/xpcshell.ini
@@ -1,17 +1,20 @@
 [DEFAULT]
 head = head_migration.js
 tail =
 firefox-appdir = browser
 skip-if = toolkit == 'android' || toolkit == 'gonk'
 support-files =
   Library/**
+  AppData/**
 
 [test_Chrome_cookies.js]
 skip-if = os != "mac" # Relies on ULibDir
+[test_Chrome_passwords.js]
+skip-if = os != "win"
 [test_fx_fhr.js]
 [test_IE_bookmarks.js]
 skip-if = os != "win"
 [test_IE_cookies.js]
 skip-if = os != "win"
 [test_Safari_bookmarks.js]
 skip-if = os != "mac"
--- a/browser/config/mozconfigs/linux32/debug-asan
+++ b/browser/config/mozconfigs/linux32/debug-asan
@@ -5,15 +5,18 @@ ac_add_options --with-google-oauth-api-k
 ac_add_options --enable-debug
 ac_add_options --enable-optimize="-O1"
 
 # ASan specific options on Linux
 ac_add_options --enable-valgrind
 
 . $topsrcdir/build/unix/mozconfig.asan
 
+export PKG_CONFIG_LIBDIR=/usr/lib/pkgconfig:/usr/share/pkgconfig
+. $topsrcdir/build/unix/mozconfig.gtk
+
 # Package js shell.
 export MOZ_PACKAGE_JSSHELL=1
 
 # Need this to prevent name conflicts with the normal nightly build packages
 export MOZ_PKG_SPECIAL=asan
 
 . "$topsrcdir/build/mozconfig.common.override"
--- a/browser/config/mozconfigs/linux32/nightly-asan
+++ b/browser/config/mozconfigs/linux32/nightly-asan
@@ -4,15 +4,18 @@ ac_add_options --with-google-oauth-api-k
 ac_add_options --disable-debug
 ac_add_options --enable-optimize="-O2 -g"
 
 # ASan specific options on Linux
 ac_add_options --enable-valgrind
 
 . $topsrcdir/build/unix/mozconfig.asan
 
+export PKG_CONFIG_LIBDIR=/usr/lib/pkgconfig:/usr/share/pkgconfig
+. $topsrcdir/build/unix/mozconfig.gtk
+
 # Package js shell.
 export MOZ_PACKAGE_JSSHELL=1
 
 # Need this to prevent name conflicts with the normal nightly build packages
 export MOZ_PKG_SPECIAL=asan
 
 . "$topsrcdir/build/mozconfig.common.override"
--- a/browser/config/mozconfigs/linux32/valgrind
+++ b/browser/config/mozconfigs/linux32/valgrind
@@ -1,11 +1,9 @@
 . $topsrcdir/browser/config/mozconfigs/linux32/nightly
 
-ac_add_options --enable-default-toolkit=cairo-gtk2
-
 ac_add_options --enable-valgrind
 ac_add_options --disable-jemalloc
 ac_add_options --disable-install-strip
 
 # Include the override mozconfig again (even though the above includes it)
 # since it's supposed to override everything.
 . "$topsrcdir/build/mozconfig.common.override"
--- a/browser/config/mozconfigs/linux64/debug-asan
+++ b/browser/config/mozconfigs/linux64/debug-asan
@@ -3,19 +3,20 @@ ac_add_options --with-google-oauth-api-k
 # Use at least -O1 for optimization to avoid stack space
 # exhaustions caused by Clang function inlining.
 ac_add_options --enable-debug
 ac_add_options --enable-optimize="-O1"
 
 # ASan specific options on Linux
 ac_add_options --enable-valgrind
 
-ac_add_options --enable-default-toolkit=cairo-gtk2
+. $topsrcdir/build/unix/mozconfig.asan
 
-. $topsrcdir/build/unix/mozconfig.asan
+export PKG_CONFIG_LIBDIR=/usr/lib64/pkgconfig:/usr/share/pkgconfig
+. $topsrcdir/build/unix/mozconfig.gtk
 
 # Package js shell.
 export MOZ_PACKAGE_JSSHELL=1
 
 # Need this to prevent name conflicts with the normal nightly build packages
 export MOZ_PKG_SPECIAL=asan
 
 . "$topsrcdir/build/mozconfig.common.override"
--- a/browser/config/mozconfigs/linux64/debug-static-analysis-clang
+++ b/browser/config/mozconfigs/linux64/debug-static-analysis-clang
@@ -2,21 +2,22 @@ MOZ_AUTOMATION_BUILD_SYMBOLS=0
 MOZ_AUTOMATION_PACKAGE_TESTS=0
 MOZ_AUTOMATION_L10N_CHECK=0
 
 . "$topsrcdir/build/mozconfig.common"
 
 ac_add_options --enable-debug
 ac_add_options --enable-dmd
 
-ac_add_options --enable-default-toolkit=cairo-gtk2
-
 # Use Clang as specified in manifest
 export CC="$topsrcdir/clang/bin/clang"
 export CXX="$topsrcdir/clang/bin/clang++"
 
 # Add the static checker
 ac_add_options --enable-clang-plugin
 
 # Avoid dependency on libstdc++ 4.7
 ac_add_options --enable-stdcxx-compat
 
+export PKG_CONFIG_LIBDIR=/usr/lib64/pkgconfig:/usr/share/pkgconfig
+. $topsrcdir/build/unix/mozconfig.gtk
+
 . "$topsrcdir/build/mozconfig.common.override"
--- a/browser/config/mozconfigs/linux64/hazards
+++ b/browser/config/mozconfigs/linux64/hazards
@@ -6,26 +6,27 @@
 # tooltool-installed gcc, and the analysis works by wrapping the gcc invocation
 # with a script that invokes the real gcc with -fplugin and its configuration
 # directives. Instead, duplicate the contents of that mozconfig here:
 
 . "$topsrcdir/build/mozconfig.common"
 ac_add_options --enable-elf-hack
 ac_add_options --enable-stdcxx-compat
 
-ac_add_options --enable-default-toolkit=cairo-gtk2
-
 # The objdir must be at a known location so its path can be stripped from the
 # filenames stored by the analysis
 mk_add_options MOZ_OBJDIR=obj-analyzed
 
 # The configuration options are chosen to compile the most code
 # (--enable-debug, --enable-tests) in the trickiest way possible
 # (--enable-optimize) to maximize the chance of seeing tricky static orderings.
 ac_add_options --enable-debug
 ac_add_options --enable-tests
 ac_add_options --enable-optimize
 
 CFLAGS="$CFLAGS -Wno-attributes"
 CPPFLAGS="$CPPFLAGS -Wno-attributes"
 CXXFLAGS="$CXXFLAGS -Wno-attributes"
 
+export PKG_CONFIG_LIBDIR=/usr/lib64/pkgconfig:/usr/share/pkgconfig
+. $topsrcdir/build/unix/mozconfig.gtk
+
 . "$topsrcdir/build/mozconfig.common.override"
--- a/browser/config/mozconfigs/linux64/nightly-asan
+++ b/browser/config/mozconfigs/linux64/nightly-asan
@@ -2,19 +2,20 @@ ac_add_options --with-google-oauth-api-k
 
 # We still need to build with debug symbols
 ac_add_options --disable-debug
 ac_add_options --enable-optimize="-O2 -gline-tables-only"
 
 # ASan specific options on Linux
 ac_add_options --enable-valgrind
 
-ac_add_options --enable-default-toolkit=cairo-gtk2
+. $topsrcdir/build/unix/mozconfig.asan
 
-. $topsrcdir/build/unix/mozconfig.asan
+export PKG_CONFIG_LIBDIR=/usr/lib64/pkgconfig:/usr/share/pkgconfig
+. $topsrcdir/build/unix/mozconfig.gtk
 
 # Package js shell.
 export MOZ_PACKAGE_JSSHELL=1
 
 # Need this to prevent name conflicts with the normal nightly build packages
 export MOZ_PKG_SPECIAL=asan
 
 . "$topsrcdir/build/mozconfig.common.override"
--- a/browser/config/mozconfigs/linux64/valgrind
+++ b/browser/config/mozconfigs/linux64/valgrind
@@ -1,13 +1,9 @@
 . $topsrcdir/browser/config/mozconfigs/linux64/nightly
 
-ac_add_options --enable-default-toolkit=cairo-gtk2
-
 ac_add_options --enable-valgrind
 ac_add_options --disable-jemalloc
 ac_add_options --disable-install-strip
 
-ac_add_options --enable-default-toolkit=cairo-gtk2
-
 # Include the override mozconfig again (even though the above includes it)
 # since it's supposed to override everything.
 . "$topsrcdir/build/mozconfig.common.override"
--- a/browser/devtools/styleinspector/rule-view.js
+++ b/browser/devtools/styleinspector/rule-view.js
@@ -2850,16 +2850,23 @@ TextPropertyEditor.prototype = {
    * Boolean indicating if the name or value is being currently edited.
    */
   get editing() {
     return !!(this.nameSpan.inplaceEditor || this.valueSpan.inplaceEditor ||
       this.ruleView.tooltips.isEditing) || this.popup.isOpen;
   },
 
   /**
+   * Get the rule to the current text property
+   */
+  get rule() {
+    return this.prop.rule;
+  },
+
+  /**
    * Create the property editor's DOM.
    */
   _create: function() {
     this.element = this.doc.createElementNS(HTML_NS, "li");
     this.element.classList.add("ruleview-property");
     this.element._textPropertyEditor = this;
 
     this.container = createChild(this.element, "div", {
@@ -2999,17 +3006,17 @@ TextPropertyEditor.prototype = {
   },
 
   /**
    * Get the path from which to resolve requests for this
    * rule's stylesheet.
    * @return {string} the stylesheet's href.
    */
   get sheetHref() {
-    let domRule = this.prop.rule.domRule;
+    let domRule = this.rule.domRule;
     if (domRule) {
       return domRule.href || domRule.nodeHref;
     }
   },
 
   /**
    * Get the URI from which to resolve relative requests for
    * this rule's stylesheet.
@@ -3063,24 +3070,24 @@ TextPropertyEditor.prototype = {
       this.element.classList.remove("ruleview-overridden");
     }
 
     let name = this.prop.name;
     this.nameSpan.textContent = name;
 
     // Combine the property's value and priority into one string for
     // the value.
-    let store = this.prop.rule.elementStyle.store;
-    let val = store.userProperties.getProperty(this.prop.rule.style, name,
+    let store = this.rule.elementStyle.store;
+    let val = store.userProperties.getProperty(this.rule.style, name,
                                                this.prop.value);
     if (this.prop.priority) {
       val += " !" + this.prop.priority;
     }
 
-    let propDirty = store.userProperties.contains(this.prop.rule.style, name);
+    let propDirty = store.userProperties.contains(this.rule.style, name);
 
     if (propDirty) {
       this.element.setAttribute("dirty", "");
     } else {
       this.element.removeAttribute("dirty");
     }
 
     const sharedSwatchClass = "ruleview-swatch ";
@@ -3151,17 +3158,16 @@ TextPropertyEditor.prototype = {
     // Populate the computed styles.
     this._updateComputed();
 
     // Update the rule property highlight.
     this.ruleView._updatePropertyHighlight(this);
   },
 
   _onStartEditing: function() {
-    this.element.classList.remove("ruleview-overridden");
     this._previewValue(this.prop.value);
   },
 
   /**
    * Populate the list of computed styles.
    */
   _updateComputed: function() {
     // Clear out existing viewers.
@@ -3289,33 +3295,48 @@ TextPropertyEditor.prototype = {
    * commits it.
    *
    * @param {string} aValue
    *        The value contained in the editor.
    * @param {boolean} aCommit
    *        True if the change should be applied.
    */
   _onNameDone: function(aValue, aCommit) {
-    if (aCommit && !this.ruleEditor.isEditing) {
-      // Unlike the value editor, if a name is empty the entire property
-      // should always be removed.
-      if (aValue.trim() === "") {
-        this.remove();
-      } else {
-        // Adding multiple rules inside of name field overwrites the current
-        // property with the first, then adds any more onto the property list.
-        let properties = parseDeclarations(aValue);
-
-        if (properties.length) {
-          this.prop.setName(properties[0].name);
-          if (properties.length > 1) {
-            this.prop.setValue(properties[0].value, properties[0].priority);
-            this.ruleEditor.addProperties(properties.slice(1), this.prop);
-          }
-        }
+    if ((!aCommit && this.ruleEditor.isEditing) ||
+        this.committed.name == aValue) {
+      // Disable the property if the property was originally disabled.
+      if (!this.prop.enabled) {
+        this.rule.setPropertyEnabled(this.prop, this.prop.enabled);
+      }
+
+      return;
+    }
+
+    // Unlike the value editor, if a name is empty the entire property
+    // should always be removed.
+    if (aValue.trim() === "") {
+      this.remove();
+      return;
+    }
+
+    // Adding multiple rules inside of name field overwrites the current
+    // property with the first, then adds any more onto the property list.
+    let properties = parseDeclarations(aValue);
+
+    if (properties.length) {
+      this.prop.setName(properties[0].name);
+      this.committed.name = this.prop.name;
+
+      if (!this.prop.enabled) {
+        this.prop.setEnabled(true);
+      }
+
+      if (properties.length > 1) {
+        this.prop.setValue(properties[0].value, properties[0].priority);
+        this.ruleEditor.addProperties(properties.slice(1), this.prop);
       }
     }
   },
 
   /**
    * Remove property from style and the editors from DOM.
    * Begin editing next available property.
    */
@@ -3337,45 +3358,48 @@ TextPropertyEditor.prototype = {
    * Called when a value editor closes.  If the user pressed escape,
    * revert to the value this property had before editing.
    *
    * @param {string} aValue
    *        The value contained in the editor.
    * @param {bool} aCommit
    *        True if the change should be applied.
    */
-  _onValueDone: function(aValue, aCommit) {
-    if (!aCommit && !this.ruleEditor.isEditing) {
+  _onValueDone: function(aValue="", aCommit) {
+    let parsedProperties = this._getValueAndExtraProperties(aValue);
+    let val = parseSingleValue(parsedProperties.firstValue);
+    let isValueUnchanged =
+      !parsedProperties.propertiesToAdd.length &&
+      this.committed.value == val.value &&
+      this.committed.priority == val.priority;
+
+    if ((!aCommit && !this.ruleEditor.isEditing) || isValueUnchanged) {
       // A new property should be removed when escape is pressed.
       if (this.removeOnRevert) {
         this.remove();
       } else {
-        // update the editor back to committed value
-        this.update();
-
-        // undo the preview in content style
-        this.ruleEditor.rule.previewPropertyValue(this.prop,
-          this.prop.value, this.prop.priority);
+        // Disable the property if the property was originally disabled.
+        this.rule.setPropertyEnabled(this.prop, this.prop.enabled);
       }
       return;
     }
 
-    let {propertiesToAdd, firstValue} =
-        this._getValueAndExtraProperties(aValue);
-
     // First, set this property value (common case, only modified a property)
-    let val = parseSingleValue(firstValue);
-
     this.prop.setValue(val.value, val.priority);
+
+    if (!this.prop.enabled) {
+      this.prop.setEnabled(true);
+    }
+
     this.removeOnRevert = false;
     this.committed.value = this.prop.value;
     this.committed.priority = this.prop.priority;
 
     // If needed, add any new properties after this.prop.
-    this.ruleEditor.addProperties(propertiesToAdd, this.prop);
+    this.ruleEditor.addProperties(parsedProperties.propertiesToAdd, this.prop);
 
     // If the name or value is not actively being edited, and the value is
     // empty, then remove the whole property.
     // A timeout is used here to accurately check the state, since the inplace
     // editor `done` and `destroy` events fire before the next editor
     // is focused.
     if (val.value.trim() === "") {
       setTimeout(() => {
@@ -3439,16 +3463,19 @@ TextPropertyEditor.prototype = {
    */
   _previewValue: function(aValue) {
     // Since function call is throttled, we need to make sure we are still
     // editing, and any selector modifications have been completed
     if (!this.editing || this.ruleEditor.isEditing) {
       return;
     }
 
+    this.element.classList.remove("ruleview-overridden");
+    this.enable.style.visibility = "hidden";
+
     let val = parseSingleValue(aValue);
     this.ruleEditor.rule.previewPropertyValue(this.prop, val.value,
                                               val.priority);
   },
 
   /**
    * Validate this property. Does it make sense for this value to be assigned
    * to this property name? This does not apply the property value
--- a/browser/devtools/styleinspector/test/browser.ini
+++ b/browser/devtools/styleinspector/test/browser.ini
@@ -87,16 +87,20 @@ skip-if = e10s # Bug 1039528: "inspect e
 [browser_ruleview_custom.js]
 [browser_ruleview_edit-property-commit.js]
 [browser_ruleview_edit-property-computed.js]
 [browser_ruleview_edit-property-increments.js]
 [browser_ruleview_edit-property-order.js]
 [browser_ruleview_edit-property_01.js]
 [browser_ruleview_edit-property_02.js]
 [browser_ruleview_edit-property_03.js]
+[browser_ruleview_edit-property_04.js]
+[browser_ruleview_edit-property_05.js]
+[browser_ruleview_edit-property_06.js]
+[browser_ruleview_edit-property_07.js]
 [browser_ruleview_edit-selector-commit.js]
 [browser_ruleview_edit-selector_01.js]
 [browser_ruleview_edit-selector_02.js]
 [browser_ruleview_edit-selector_03.js]
 [browser_ruleview_edit-selector_04.js]
 [browser_ruleview_edit-selector_05.js]
 [browser_ruleview_eyedropper.js]
 [browser_ruleview_filtereditor-appears-on-swatch-click.js]
new file mode 100644
--- /dev/null
+++ b/browser/devtools/styleinspector/test/browser_ruleview_edit-property_04.js
@@ -0,0 +1,88 @@
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests that a disabled property is previewed when the property name or value
+// editor is focused and the property remains disabled when the escaping out of
+// the property editor.
+
+let TEST_URI = [
+  "<style type='text/css'>",
+  "#testid {",
+  "  background-color: blue;",
+  "}",
+  "</style>",
+  "<div id='testid'>Styled Node</div>",
+].join("\n");
+
+add_task(function*() {
+  yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
+  let {inspector, view} = yield openRuleView();
+  yield selectNode("#testid", inspector);
+  yield testDisableProperty(inspector, view);
+});
+
+function* testDisableProperty(inspector, view) {
+  let ruleEditor = getRuleViewRuleEditor(view, 1);
+  let propEditor = ruleEditor.rule.textProps[0].editor;
+
+  info("Disabling a property");
+  propEditor.enable.click();
+  yield ruleEditor.rule._applyingModifications;
+
+  let newValue = yield executeInContent("Test:GetRulePropertyValue", {
+    styleSheetIndex: 0,
+    ruleIndex: 0,
+    name: "background-color"
+  });
+  is(newValue, "", "background-color should have been unset.");
+
+  yield testPreviewDisableProperty(view, ruleEditor, propEditor,
+    propEditor.nameSpan, "VK_ESCAPE");
+  yield testPreviewDisableProperty(view, ruleEditor, propEditor,
+    propEditor.valueSpan, "VK_ESCAPE");
+  yield testPreviewDisableProperty(view, ruleEditor, propEditor,
+    propEditor.valueSpan, "VK_TAB");
+  yield testPreviewDisableProperty(view, ruleEditor, propEditor,
+    propEditor.valueSpan, "VK_RETURN");
+}
+
+function* testPreviewDisableProperty(view, ruleEditor, propEditor,
+    editableField, commitKey) {
+  let editor = yield focusEditableField(view, editableField);
+  yield ruleEditor.rule._applyingModifications;
+
+  ok(!propEditor.element.classList.contains("ruleview-overridden"),
+    "property is not overridden.");
+  is(propEditor.enable.style.visibility, "hidden",
+    "property enable checkbox is hidden.");
+
+  let newValue = yield executeInContent("Test:GetRulePropertyValue", {
+    styleSheetIndex: 0,
+    ruleIndex: 0,
+    name: "background-color"
+  });
+  is(newValue, "blue", "background-color should have been previewed.");
+
+  let onBlur = once(editor.input, "blur");
+  EventUtils.synthesizeKey(commitKey, {}, view.styleWindow);
+  yield onBlur;
+  yield ruleEditor.rule._applyingModifications;
+
+  ok(!propEditor.prop.enabled, "property is disabled.");
+  ok(propEditor.element.classList.contains("ruleview-overridden"),
+    "property is overridden.");
+  is(propEditor.enable.style.visibility, "visible",
+    "property enable checkbox is visible.");
+  ok(!propEditor.enable.getAttribute("checked"),
+    "property enable checkbox is not checked.");
+
+  newValue = yield executeInContent("Test:GetRulePropertyValue", {
+    styleSheetIndex: 0,
+    ruleIndex: 0,
+    name: "background-color"
+  });
+  is(newValue, "", "background-color should have been unset.");
+}
new file mode 100644
--- /dev/null
+++ b/browser/devtools/styleinspector/test/browser_ruleview_edit-property_05.js
@@ -0,0 +1,86 @@
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests that a disabled property is re-enabled if the property name or value is
+// modified
+
+let TEST_URI = [
+  "<style type='text/css'>",
+  "#testid {",
+  "  background-color: blue;",
+  "}",
+  "</style>",
+  "<div id='testid'>Styled Node</div>",
+].join("\n");
+
+add_task(function*() {
+  yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
+  let {inspector, view} = yield openRuleView();
+  yield selectNode("#testid", inspector);
+  yield testEditingDisableProperty(inspector, view);
+});
+
+function* testEditingDisableProperty(inspector, view) {
+  let ruleEditor = getRuleViewRuleEditor(view, 1);
+  let propEditor = ruleEditor.rule.textProps[0].editor;
+
+  info("Disabling background-color property");
+  propEditor.enable.click();
+  yield ruleEditor.rule._applyingModifications;
+
+  let newValue = yield getRulePropertyValue("background-color");
+  is(newValue, "", "background-color should have been unset.");
+
+  yield focusEditableField(view, propEditor.nameSpan);
+
+  info("Entering a new property name, including : to commit and " +
+       "focus the value");
+  let onValueFocus = once(ruleEditor.element, "focus", true);
+  EventUtils.sendString("border-color:", view.styleWindow);
+  yield onValueFocus;
+  yield ruleEditor.rule._applyingModifications;
+
+  info("Escape editing the property value");
+  EventUtils.synthesizeKey("VK_ESCAPE", {}, view.styleWindow);
+  yield ruleEditor.rule._applyingModifications;
+
+  newValue = yield getRulePropertyValue("border-color");
+  is(newValue, "blue", "border-color should have been set.");
+
+  ok(propEditor.prop.enabled, "border-color property is enabled.");
+  ok(!propEditor.element.classList.contains("ruleview-overridden"),
+    "border-color is not overridden");
+
+  info("Disabling border-color property");
+  propEditor.enable.click();
+  yield ruleEditor.rule._applyingModifications;
+
+  newValue = yield getRulePropertyValue("border-color");
+  is(newValue, "", "border-color should have been unset.");
+
+  info("Enter a new property value for the border-color property");
+  let editor = yield focusEditableField(view, propEditor.valueSpan);
+  let onBlur = once(editor.input, "blur");
+  EventUtils.sendString("red;", view.styleWindow);
+  yield onBlur;
+  yield ruleEditor.rule._applyingModifications;
+
+  newValue = yield getRulePropertyValue("border-color");
+  is(newValue, "red", "new border-color should have been set.");
+
+  ok(propEditor.prop.enabled, "border-color property is enabled.");
+  ok(!propEditor.element.classList.contains("ruleview-overridden"),
+    "border-color is not overridden");
+}
+
+function* getRulePropertyValue(name) {
+  let propValue = yield executeInContent("Test:GetRulePropertyValue", {
+    styleSheetIndex: 0,
+    ruleIndex: 0,
+    name: name
+  });
+  return propValue;
+}
new file mode 100644
--- /dev/null
+++ b/browser/devtools/styleinspector/test/browser_ruleview_edit-property_06.js
@@ -0,0 +1,64 @@
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests that editing a property's priority is behaving correctly, and disabling
+// and editing the property will re-enable the property.
+
+let TEST_URI = [
+  "<style type='text/css'>",
+  "body {",
+  "  background-color: green !important;",
+  "}",
+  "body {",
+  "  background-color: red;",
+  "}",
+  "</style>",
+].join("\n");
+
+add_task(function*() {
+  yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
+  let {inspector, view} = yield openRuleView();
+  yield selectNode("body", inspector);
+  yield testEditPropertyPriorityAndDisable(inspector, view);
+});
+
+function* testEditPropertyPriorityAndDisable(inspector, view) {
+  let ruleEditor = getRuleViewRuleEditor(view, 1);
+  let propEditor = ruleEditor.rule.textProps[0].editor;
+
+  is((yield getComputedStyleProperty("body", null, "background-color")),
+    "rgb(0, 128, 0)", "green background color is set.");
+
+  let editor = yield focusEditableField(view, propEditor.valueSpan);
+  let onBlur = once(editor.input, "blur");
+  EventUtils.sendString("red !important;", view.styleWindow);
+  yield onBlur;
+  yield ruleEditor.rule._applyingModifications;
+
+  is(propEditor.valueSpan.textContent, "red !important",
+    "'red !important' property value is correctly set.");
+  is((yield getComputedStyleProperty("body", null, "background-color")),
+    "rgb(255, 0, 0)", "red background color is set.");
+
+  info("Disabling red background color property");
+  propEditor.enable.click();
+  yield ruleEditor.rule._applyingModifications;
+
+  is((yield getComputedStyleProperty("body", null, "background-color")),
+    "rgb(0, 128, 0)", "green background color is set.");
+
+  editor = yield focusEditableField(view, propEditor.valueSpan);
+  onBlur = once(editor.input, "blur");
+  EventUtils.sendString("red;", view.styleWindow);
+  yield onBlur;
+  yield ruleEditor.rule._applyingModifications;
+
+  is(propEditor.valueSpan.textContent, "red",
+    "'red' property value is correctly set.");
+  ok(propEditor.prop.enabled, "red background-color property is enabled.");
+  is((yield getComputedStyleProperty("body", null, "background-color")),
+    "rgb(0, 128, 0)", "green background color is set.");
+}
new file mode 100644
--- /dev/null
+++ b/browser/devtools/styleinspector/test/browser_ruleview_edit-property_07.js
@@ -0,0 +1,55 @@
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests that adding multiple values will enable the property even if the
+// property does not change, and that the extra values are added correctly.
+
+let TEST_URI = [
+  "<style type='text/css'>",
+  "#testid {",
+  "  background-color: red;",
+  "}",
+  "</style>",
+  "<div id='testid'>Styled Node</div>",
+].join("\n");
+
+add_task(function*() {
+  yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
+  let {inspector, view} = yield openRuleView();
+  yield selectNode("#testid", inspector);
+  yield testEditDisableProperty(inspector, view);
+});
+
+function* testEditDisableProperty(inspector, view) {
+  let ruleEditor = getRuleViewRuleEditor(view, 1);
+  let propEditor = ruleEditor.rule.textProps[0].editor;
+
+  info("Disabling red background color property");
+  propEditor.enable.click();
+  yield ruleEditor.rule._applyingModifications;
+
+  ok(!propEditor.prop.enabled, "red background-color property is disabled.");
+
+  let editor = yield focusEditableField(view, propEditor.valueSpan);
+  let onBlur = once(editor.input, "blur");
+  EventUtils.sendString("red; color: red;", view.styleWindow);
+  yield onBlur;
+  yield ruleEditor.rule._applyingModifications;
+
+  is(propEditor.valueSpan.textContent, "red",
+    "'red' property value is correctly set.");
+  ok(propEditor.prop.enabled, "red background-color property is enabled.");
+  is((yield getComputedStyleProperty("#testid", null, "background-color")),
+    "rgb(255, 0, 0)", "red background color is set.");
+
+  propEditor = ruleEditor.rule.textProps[1].editor;
+  is(propEditor.nameSpan.textContent, "color",
+    "new 'color' property name is correctly set.");
+  is(propEditor.valueSpan.textContent, "red",
+    "new 'red' property value is correctly set.");
+  is((yield getComputedStyleProperty("#testid", null, "color")),
+    "rgb(255, 0, 0)", "red color is set.");
+}
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -125,17 +125,17 @@
 #endif
 #ifdef MOZ_REPLACE_MALLOC
 #ifndef MOZ_JEMALLOC3
 @BINPATH@/@DLL_PREFIX@replace_jemalloc@DLL_SUFFIX@
 #endif
 #endif
 #ifdef MOZ_GTK3
 @BINPATH@/@DLL_PREFIX@mozgtk@DLL_SUFFIX@
-@BINPATH@/@DLL_PREFIX@mozgtk2@DLL_SUFFIX@
+@BINPATH@/gtk2/@DLL_PREFIX@mozgtk@DLL_SUFFIX@
 #endif
 
 [browser]
 ; [Base Browser Files]
 #ifndef XP_UNIX
 @BINPATH@/@MOZ_APP_NAME@.exe
 #else
 @BINPATH@/@MOZ_APP_NAME@-bin
--- a/browser/themes/windows/browser.css
+++ b/browser/themes/windows/browser.css
@@ -2023,16 +2023,24 @@ richlistitem[type~="action"][actiontype=
   .tab-background-end[visuallyselected=true]:-moz-locale-dir(rtl)::after {
     background-image: url(chrome://browser/skin/tabbrowser/tab-stroke-start@2x.png);
   }
 
   .tab-background-end[visuallyselected=true]:-moz-locale-dir(ltr)::after,
   .tab-background-start[visuallyselected=true]:-moz-locale-dir(rtl)::after {
     background-image: url(chrome://browser/skin/tabbrowser/tab-stroke-end@2x.png);
   }
+
+  .tab-throbber[busy] {
+    list-style-image: url("chrome://browser/skin/tabbrowser/connecting@2x.png");
+  }
+
+  .tab-throbber[progress] {
+    list-style-image: url("chrome://browser/skin/tabbrowser/loading@2x.png");
+  }
 }
 
 /* Remove border between tab strip and navigation toolbar on Windows 10+ */
 @media not all and (-moz-os-version: windows-xp) {
   @media not all and (-moz-os-version: windows-vista) {
     @media not all and (-moz-os-version: windows-win7) {
       @media not all and (-moz-os-version: windows-win8) {
         .tab-background-end[visuallyselected=true]::after,
--- a/browser/themes/windows/jar.mn
+++ b/browser/themes/windows/jar.mn
@@ -325,32 +325,40 @@ browser.jar:
         skin/classic/browser/tabbrowser/newtab@2x.png                (tabbrowser/newtab@2x.png)
         skin/classic/browser/tabbrowser/newtab-XPVista7.png          (tabbrowser/newtab-XPVista7.png)
         skin/classic/browser/tabbrowser/newtab-XPVista7@2x.png       (tabbrowser/newtab-XPVista7@2x.png)
         skin/classic/browser/tabbrowser/newtab-inverted.png          (tabbrowser/newtab-inverted.png)
         skin/classic/browser/tabbrowser/newtab-inverted@2x.png       (tabbrowser/newtab-inverted@2x.png)
         skin/classic/browser/tabbrowser/newtab-inverted-XPVista7.png (tabbrowser/newtab-inverted-XPVista7.png)
         skin/classic/browser/tabbrowser/newtab-inverted-XPVista7@2x.png (tabbrowser/newtab-inverted-XPVista7@2x.png)
         skin/classic/browser/tabbrowser/connecting.png               (tabbrowser/connecting.png)
+        skin/classic/browser/tabbrowser/connecting@2x.png            (tabbrowser/connecting@2x.png)
         skin/classic/browser/tabbrowser/crashed.svg                  (../shared/tabbrowser/crashed.svg)
         skin/classic/browser/tabbrowser/loading.png                  (tabbrowser/loading.png)
+        skin/classic/browser/tabbrowser/loading@2x.png               (tabbrowser/loading@2x.png)
         skin/classic/browser/tabbrowser/tab-active-middle.png        (tabbrowser/tab-active-middle.png)
         skin/classic/browser/tabbrowser/tab-active-middle@2x.png     (tabbrowser/tab-active-middle@2x.png)
         skin/classic/browser/tabbrowser/tab-arrow-left.png           (tabbrowser/tab-arrow-left.png)
         skin/classic/browser/tabbrowser/tab-arrow-left@2x.png        (tabbrowser/tab-arrow-left@2x.png)
         skin/classic/browser/tabbrowser/tab-arrow-left-XPVista7.png  (tabbrowser/tab-arrow-left-XPVista7.png)
         skin/classic/browser/tabbrowser/tab-arrow-left-XPVista7@2x.png  (tabbrowser/tab-arrow-left-XPVista7@2x.png)
         skin/classic/browser/tabbrowser/tab-arrow-left-inverted.png  (tabbrowser/tab-arrow-left-inverted.png)
         skin/classic/browser/tabbrowser/tab-arrow-left-inverted@2x.png  (tabbrowser/tab-arrow-left-inverted@2x.png)
         skin/classic/browser/tabbrowser/tab-background-start.png     (tabbrowser/tab-background-start.png)
         skin/classic/browser/tabbrowser/tab-background-start@2x.png  (tabbrowser/tab-background-start@2x.png)
         skin/classic/browser/tabbrowser/tab-background-middle.png    (tabbrowser/tab-background-middle.png)
         skin/classic/browser/tabbrowser/tab-background-middle@2x.png (tabbrowser/tab-background-middle@2x.png)
         skin/classic/browser/tabbrowser/tab-background-end.png       (tabbrowser/tab-background-end.png)
         skin/classic/browser/tabbrowser/tab-background-end@2x.png    (tabbrowser/tab-background-end@2x.png)
+        skin/classic/browser/tabbrowser/tab-background-start-preWin10.png     (tabbrowser/tab-background-start-preWin10.png)
+        skin/classic/browser/tabbrowser/tab-background-start-preWin10@2x.png  (tabbrowser/tab-background-start-preWin10@2x.png)
+        skin/classic/browser/tabbrowser/tab-background-middle-preWin10.png    (tabbrowser/tab-background-middle-preWin10.png)
+        skin/classic/browser/tabbrowser/tab-background-middle-preWin10@2x.png (tabbrowser/tab-background-middle-preWin10@2x.png)
+        skin/classic/browser/tabbrowser/tab-background-end-preWin10.png       (tabbrowser/tab-background-end-preWin10.png)
+        skin/classic/browser/tabbrowser/tab-background-end-preWin10@2x.png    (tabbrowser/tab-background-end-preWin10@2x.png)
         skin/classic/browser/tabbrowser/tab-overflow-indicator.png   (../shared/tabbrowser/tab-overflow-indicator.png)
         skin/classic/browser/tabbrowser/pendingpaint.png             (../shared/tabbrowser/pendingpaint.png)
 
 # NOTE: The following two files (tab-selected-end.svg, tab-selected-start.svg) get pre-processed in
 #       Makefile.in with a non-default marker of "%" and the result of that gets packaged.
         skin/classic/browser/tabbrowser/tab-selected-end.svg         (tab-selected-end.svg)
         skin/classic/browser/tabbrowser/tab-selected-start.svg       (tab-selected-start.svg)
 
@@ -699,8 +707,14 @@ browser.jar:
 % override chrome://browser/skin/preferences/checkbox.png             chrome://browser/skin/preferences/checkbox-aero.png               os=WINNT osversion=6.1
 % override chrome://browser/skin/preferences/checkbox.png             chrome://browser/skin/preferences/checkbox-xp.png                 os=WINNT osversion<6
 
 % override chrome://browser/skin/reload-stop-go.png                   chrome://browser/skin/reload-stop-go-preWin10.png                 os=WINNT osversion<=6.3
 % override chrome://browser/skin/reload-stop-go@2x.png                chrome://browser/skin/reload-stop-go-preWin10@2x.png              os=WINNT osversion<=6.3
 % override chrome://browser/skin/urlbar-history-dropmarker.png        chrome://browser/skin/urlbar-history-dropmarker-preWin10.png      os=WINNT osversion<=6.3
 % override chrome://browser/skin/urlbar-history-dropmarker@2x.png     chrome://browser/skin/urlbar-history-dropmarker-preWin10@2x.png   os=WINNT osversion<=6.3
 
+% override chrome://browser/skin/tabbrowser/tab-background-start.png     chrome://browser/skin/tabbrowser/tab-background-start-preWin10.png     os=WINNT osversion<=6.3
+% override chrome://browser/skin/tabbrowser/tab-background-start@2x.png  chrome://browser/skin/tabbrowser/tab-background-start-preWin10@2x.png  os=WINNT osversion<=6.3
+% override chrome://browser/skin/tabbrowser/tab-background-middle.png    chrome://browser/skin/tabbrowser/tab-background-middle-preWin10.png    os=WINNT osversion<=6.3
+% override chrome://browser/skin/tabbrowser/tab-background-middle@2x.png chrome://browser/skin/tabbrowser/tab-background-middle-preWin10@2x.png os=WINNT osversion<=6.3
+% override chrome://browser/skin/tabbrowser/tab-background-end.png       chrome://browser/skin/tabbrowser/tab-background-end-preWin10.png       os=WINNT osversion<=6.3
+% override chrome://browser/skin/tabbrowser/tab-background-end@2x.png    chrome://browser/skin/tabbrowser/tab-background-end-preWin10@2x.png    os=WINNT osversion<=6.3
copy from browser/themes/osx/tabbrowser/connecting@2x.png
copy to browser/themes/windows/tabbrowser/connecting@2x.png
index ba54836e9834f2c495bcfb05738b56481b2dbed0..b886c73eff9239196fbe3db01116c220f726469b
GIT binary patch
literal 12585
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
copy from browser/themes/osx/tabbrowser/loading@2x.png
copy to browser/themes/windows/tabbrowser/loading@2x.png
copy from browser/themes/windows/tabbrowser/tab-background-end.png
copy to browser/themes/windows/tabbrowser/tab-background-end-preWin10.png
copy from browser/themes/windows/tabbrowser/tab-background-end@2x.png
copy to browser/themes/windows/tabbrowser/tab-background-end-preWin10@2x.png
index fb353b17e70657d85e501a30ca6e2e6249e83a76..d68ea6da6a9834dfed10a88e73afa10ba1933099
GIT binary patch
literal 256
zc$@(M0ssDqP)<h;3K|Lk000e1NJLTq0015U0018d1ONa4T4lA20002TNkl<Zcmc)B
zB|?M&5I|wv9TEwTxLtsTL&OqJBjWC^SKwZ_jqIO?*~RkCZ|~(VA-wp8k7<YIaTwcQ
zbK}Hx`#)au6qFlrbiSc6NjKu?yaI)0tb9$ftXRisJJzAm1;%wrvSR(S&IRTP7Z@)j
z)r|ETH@M3M#yJ<5_U%}QLKoO+bb(>e1?EK;STAvbBONX<u1lgBD{pmyY4;(^iglir
zBr{e%A?a4E<9eh{5{)?aCJ#6bRC1SW!LhmDX>m$F^1lGYxa1-eg!DoH0000<MNUMn
GLSTZ+0&AlH
index eefb6ac47cd820af31556f1d8c6631ad36396baa..8ed84ab37969668dcee44c1f5915cde2a48e59a0
GIT binary patch
literal 400
zc$@){0dM|^P)<h;3K|Lk000e1NJLTq002Ay002G+1ONa4-951K00043Nkl<ZcmeIv
zwR#&t6vc5dHDM+<hR)2?nVH${+?X-UEN1P~<Y~e<_pa1VuD&hP>x9n#0e%CvzkmNn
z2In|RL4HX4v_`{b4V@_w{A=o|S}<$0x8$%DCusHCbpULoHuIMtu+UkaeHQ@0)~^??
zAz+D9g;oeGbdI|butj%4V4<^A1%ah7ox#9{+SPUlEOZM9rjYhQU}+cxb5wf~u=KqP
z1oK@CEPcBUf@vHC^Aiv(^f?sVje^%fFa<%dbQT2vr~|?IQ4p*)K`;eTu<_#*2+r4`
zZaf8o=?Da?ly)Lu>3bOnHhV!Z?So*>Xg>m$R0|5;0>Pa5ei8yp!zehYT^QKlZV*gC
z?Zd!^IS+y<M->oQ3VuHcfu-Bp4}qnd?*bUu;76Y?Ltvqks_%^p02fnFRFifB;9}-0
up5Su;*ovcSlSZkP&T5Z#JapLp(ftHT{;3T*{uNOG0000<MNUMnLSTY7xvBL4
copy from browser/themes/windows/tabbrowser/tab-background-middle.png
copy to browser/themes/windows/tabbrowser/tab-background-middle-preWin10.png
copy from browser/themes/windows/tabbrowser/tab-background-middle@2x.png
copy to browser/themes/windows/tabbrowser/tab-background-middle-preWin10@2x.png
index 51e066c2e9c6d71f5ce4846b1b030d2f111127ef..faaf7e38e5d9726af460735604497ee0e8eed164
GIT binary patch
literal 75
zc%17D@N?(olHy`uVBq!ia0vp^Y(Ol}!2%@nWJ)FgDIrf6$B+uf<O95RXI&hA6}nk6
X+&1Fr%E^cVDP{0<^>bP0l+XkK3Q7>X
index b26cb95de6ab3231f676a4c939a2bbf210557771..c9d245f4f4459fe4e1265354b87f47b1e3a3ce32
GIT binary patch
literal 86
zc%17D@N?(olHy`uVBq!ia0vp^JV0#6!2%?=o-;ZHq+~r^978G?lNYf6FcG$1(tBhD
d46>YLV0fy)D;sK|d=sdU!PC{xWt~$(699p}7_$HX
copy from browser/themes/windows/tabbrowser/tab-background-start.png
copy to browser/themes/windows/tabbrowser/tab-background-start-preWin10.png
copy from browser/themes/windows/tabbrowser/tab-background-start@2x.png
copy to browser/themes/windows/tabbrowser/tab-background-start-preWin10@2x.png
index cf0dc852ac52f8b6a1d9fdd0b6211494263aa60b..d1f0b5561034d68884c0c6e48f0697da4da86474
GIT binary patch
literal 257
zc$@(N0sj7pP)<h;3K|Lk000e1NJLTq0015U0018d1ONa4T4lA20002UNkl<ZcmdVR
zr9uE<5Jh3!9TEw5_a`9X5V3?uD(>zwufU_B|9^+gL)^N1H*rq>QvORHg?`A-VH2l%
zqpvZbt9GmY1ri<+S~eE-HDoccE7jeQ#KM-R`w$YlRS1c-1wvv=)omL8c0g#X%QUu3
z|43srKzKZw0P98qj0+mOQV5NMo&?y<B*1=~#;AbM*bURz(r`j!KLjDM<>HLQmZobE
z3R|`=LkMiCKVAL+5(~ejqax_mX&gmDA^t87DuaHVg2ew1(G=t{@qmLN00000NkvXX
Hu0mjfJUM9X
index bbfc77dd189d6885201c4d9e923836485cfd8ad1..e860275a6435b42e181b6de2e60a0be01b031e55
GIT binary patch
literal 417
zc$@*D0bc%zP)<h;3K|Lk000e1NJLTq002Ay002G+1ONa4-951K0004KNkl<ZcmeI#
zv!dNW6vg3VHL=+QU2JtRuC3}e*S4)#`)SV8#GJJc%1L?9|7S(Nd4O;BOtpP~VgDn9
zZK;#Ej~4XlG}iQqzJ+)S+Gy&esWbDC;^QIw{UDn4c@A;{r}vnS9N`FlpU`Ua2I3gb
z9LW8sLl~FzCB!+bH}oyUIjq-ntV0;b^$o;1jBVI}IESs@^gsx(xd<V^HimTyj0X?`
zY@_;w0{^%Up};y$fxjPwP+*jjV4bDFx&y+2Z?NE25}aD0z}N<%!0A&g_z?-#b`p#=
z3jAX;gaW5eli;5fBp8Df*apo@3Y<L*p}?tIBp7`Z*v9oW1vVEU9JrGL|JbarDKH*D
z2yillRSK*(AOzUPuttGXwGaY~qj*bze{9AY#5ru^7=<{8-zP8#aSngkt1}SCa57|W
z>kEie*apn`-0JNJ)*V`w+Xg*^rvo-%BiN2hcw{>CiDzIA{^$Ay{|l(iuOFwK00000
LNkvXXu0mjfTWh>f
--- a/build/autoconf/compiler-opts.m4
+++ b/build/autoconf/compiler-opts.m4
@@ -353,16 +353,30 @@ if test "$GNU_CC" -a -n "$MOZ_PIE"; then
                   [MOZ_PROGRAM_LDFLAGS="$MOZ_PROGRAM_LDFLAGS -pie"],
                   AC_MSG_RESULT([no])
                   AC_MSG_ERROR([--enable-pie requires PIE support from the linker.]))
     LDFLAGS=$_SAVE_LDFLAGS
 fi
 
 AC_SUBST(MOZ_PROGRAM_LDFLAGS)
 
+dnl ASan assumes no symbols are being interposed, and when that happens,
+dnl it's not happy with it. Unconveniently, since Firefox is exporting
+dnl libffi symbols and Gtk+3 pulls system libffi via libwayland-client,
+dnl system libffi interposes libffi symbols that ASan assumes are in
+dnl libxul, so it barfs about buffer overflows.
+dnl Using -Wl,-Bsymbolic ensures no exported symbol can be interposed.
+if test -n "$GCC_USE_GNU_LD"; then
+  case "$LDFLAGS" in
+  *-fsanitize=address*)
+    LDFLAGS="$LDFLAGS -Wl,-Bsymbolic"
+    ;;
+  esac
+fi
+
 ])
 
 dnl GCC and clang will fail if given an unknown warning option like -Wfoobar. 
 dnl But later versions won't fail if given an unknown negated warning option
 dnl like -Wno-foobar.  So when we are check for support of negated warning 
 dnl options, we actually test the positive form, but add the negated form to 
 dnl the flags variable.
 
--- a/build/clang-plugin/clang-plugin.cpp
+++ b/build/clang-plugin/clang-plugin.cpp
@@ -800,17 +800,21 @@ void CustomTypeAnnotation::dumpAnnotatio
     }
 
     T = Reason.Type;
     Reason = directAnnotationReason(T);
   }
 }
 
 bool CustomTypeAnnotation::hasLiteralAnnotation(QualType T) const {
+#if CLANG_VERSION_FULL >= 306
   if (const TagDecl *D = T->getAsTagDecl()) {
+#else
+  if (const CXXRecordDecl *D = T->getAsCXXRecordDecl()) {
+#endif
     return MozChecker::hasCustomAnnotation(D, Spelling);
   }
   return false;
 }
 
 CustomTypeAnnotation::AnnotationReason CustomTypeAnnotation::directAnnotationReason(QualType T) {
   if (hasLiteralAnnotation(T)) {
     AnnotationReason Reason = { T, RK_Direct, nullptr };
--- a/build/sanitizers/lsan_suppressions.txt
+++ b/build/sanitizers/lsan_suppressions.txt
@@ -36,16 +36,23 @@ leak:NR_reg_init
 # we get less stack frames for some reason.
 leak:nr_reg_local_init
 leak:r_log_register
 leak:nr_reg_set
 
 # Additional WebRTC leak suppressions added for Mochitest 3.
 leak:mozilla::TransportLayerDtls::Setup
 
+# Bug 1187424 - DesktopApplication does not free any of its string members.
+leak:webrtc::DesktopApplication::
+
+# Bug 1187518 - SCTP leaks in child process while running WebRTC tests.
+leak:recv_function_udp
+
+
 ###
 ### Many leaks only affect some test suites.  The suite annotations are not checked.
 ###
 
 # Bug 981195 - Small leak in the parser. m4
 leak:ObjectGroup::fixPlainObjectGroup
 
 # Bug 982111 - WebM is leaking. m1
@@ -80,16 +87,32 @@ leak:GlobalPrinters::InitializeGlobalPri
 leak:nsPSPrinterList::GetPrinterList
 
 # Bug 1028456 - More leaks with _PR_Getfd, in nsLocalFile::CopyToNative and do_create. bc1, bc3
 leak:_PR_Getfd
 
 # Bug 1028483 - The XML parser sometimes leaks an object. bc3
 leak:processInternalEntity
 
+# Bug 1187421 - With e10s, NSS does not always free the error stack. m1.
+leak:nss_ClearErrorStack
+
+# Bug 1090570 - IPC Transport lifetime is not always managed correctly.
+leak:mozilla::ipc::OpenDescriptor
+leak:IPC::Channel::ChannelImpl::OutputQueuePush
+leak:IPC::Channel::Channel
+leak:base::MessagePumpLibevent::WatchFileDescriptor
+
+# Bug 1122045 - Leaks in MessageLoop::MessageLoop()
+leak:MessageLoop::MessageLoop
+# This may not actually be related to MessageLoop.
+leak:base::WaitableEvent::TimedWait
+leak:MessageLoop::PostTask_Helper
+
+
 ###
 ### Leaks with system libraries in their stacks. These show up across a number of tests.
 ### Better symbols and disabling fast stackwalking may help diagnose these.
 ###
 
 leak:libcairo.so
 leak:libdl.so
 leak:libdricore.so
new file mode 100644
--- /dev/null
+++ b/build/unix/mozconfig.gtk
@@ -0,0 +1,48 @@
+# $topsrcdir/gtk3 comes from tooltool, when the tooltool manifest contains it.
+if [ -d "$topsrcdir/gtk3" ]; then
+  if [ -z "$PKG_CONFIG_LIBDIR" ]; then
+    echo PKG_CONFIG_LIBDIR must be set >&2
+    exit 1
+  fi
+  export PKG_CONFIG_SYSROOT_DIR="$topsrcdir/gtk3"
+  export PKG_CONFIG_PATH="$topsrcdir/gtk3/usr/local/lib/pkgconfig"
+  export PATH="$topsrcdir/gtk3/usr/local/bin:${PATH}"
+  # Ensure cairo, gdk-pixbuf, etc. are not taken from the system installed packages.
+  LDFLAGS="-L$topsrcdir/gtk3/usr/local/lib ${LDFLAGS}"
+  mk_add_options "export LD_LIBRARY_PATH=$topsrcdir/gtk3/usr/local/lib"
+  ac_add_options --enable-default-toolkit=cairo-gtk3
+
+  # Set things up to use Gtk+3 from the tooltool package
+  mk_add_options "export FONTCONFIG_PATH=$topsrcdir/gtk3/usr/local/etc/fonts"
+  mk_add_options "export PANGO_SYSCONFDIR=$topsrcdir/gtk3/usr/local/etc"
+  mk_add_options "export PANGO_LIBDIR=$topsrcdir/gtk3/usr/local/lib"
+  mk_add_options "export GDK_PIXBUF_MODULE_FILE=$topsrcdir/gtk3/usr/local/lib/gdk-pixbuf-2.0/2.10.0/loaders.cache"
+  mk_add_options "export GDK_PIXBUF_MODULEDIR=$topsrcdir/gtk3/usr/local/lib/gdk-pixbuf-2.0/2.10.0/loaders"
+  mk_add_options "export LD_LIBRARY_PATH=$topsrcdir/gtk3/usr/local/lib"
+
+  # pango expects absolute paths in pango.modules, and topsrcdir may vary...
+  LD_LIBRARY_PATH=$topsrcdir/gtk3/usr/local/lib \
+  PANGO_SYSCONFDIR=$topsrcdir/gtk3/usr/local/etc \
+  PANGO_LIBDIR=$topsrcdir/gtk3/usr/local/lib \
+  $topsrcdir/gtk3/usr/local/bin/pango-querymodules > $topsrcdir/gtk3/usr/local/etc/pango/pango.modules
+
+  # same with gdb-pixbuf and loaders.cache
+  LD_LIBRARY_PATH=$topsrcdir/gtk3/usr/local/lib \
+  GDK_PIXBUF_MODULE_FILE=$topsrcdir/gtk3/usr/local/lib/gdk-pixbuf-2.0/2.10.0/loaders.cache \
+  GDK_PIXBUF_MODULEDIR=$topsrcdir/gtk3/usr/local/lib/gdk-pixbuf-2.0/2.10.0/loaders \
+  $topsrcdir/gtk3/usr/local/bin/gdk-pixbuf-query-loaders > $topsrcdir/gtk3/usr/local/lib/gdk-pixbuf-2.0/2.10.0/loaders.cache
+
+  # mock build environment doesn't have fonts in /usr/share/fonts, but
+  # has some in /usr/share/X11/fonts. Add this directory to the
+  # fontconfig configuration without changing the gtk3 tooltool package.
+  cat << EOF > $topsrcdir/gtk3/usr/local/etc/fonts/local.conf
+<?xml version="1.0"?>
+<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
+<fontconfig>
+  <dir>/usr/share/X11/fonts</dir>
+</fontconfig>
+EOF
+
+else
+  ac_add_options --enable-default-toolkit=cairo-gtk2
+fi
--- a/build/unix/mozconfig.linux
+++ b/build/unix/mozconfig.linux
@@ -24,22 +24,14 @@ else
   CXX="/tools/gcc-4.7.3-0moz1/bin/g++"
 fi
 
 ac_add_options --enable-elf-hack
 
 # Avoid dependency on libstdc++ 4.7
 ac_add_options --enable-stdcxx-compat
 
-# $topsrcdir/gtk3 comes from tooltool, when the tooltool manifest contains it.
-if [ -d "$topsrcdir/gtk3" ]; then
-  # PKG_CONFIG_LIBDIR is appropriately overridden in mozconfig.linux32
-  export PKG_CONFIG_LIBDIR=/usr/lib64/pkgconfig:/usr/share/pkgconfig
-  export PKG_CONFIG_SYSROOT_DIR="$topsrcdir/gtk3"
-  export PKG_CONFIG_PATH="$topsrcdir/gtk3/usr/local/lib/pkgconfig"
-  export PATH="$topsrcdir/gtk3/usr/local/bin:${PATH}"
-  # Ensure cairo, gdk-pixbuf, etc. are not taken from the system installed packages.
-  LDFLAGS="-L$topsrcdir/gtk3/usr/local/lib"
-  mk_add_options "export LD_LIBRARY_PATH=$topsrcdir/gtk3/usr/local/lib"
-  ac_add_options --enable-default-toolkit=cairo-gtk3
-fi
+# PKG_CONFIG_LIBDIR is appropriately overridden in mozconfig.linux32
+export PKG_CONFIG_LIBDIR=/usr/lib64/pkgconfig:/usr/share/pkgconfig
 
 export SOCORRO_SYMBOL_UPLOAD_TOKEN_FILE=/builds/crash-stats-api.token
+
+. "$topsrcdir/build/unix/mozconfig.gtk"
--- a/build/unix/mozconfig.linux32
+++ b/build/unix/mozconfig.linux32
@@ -1,11 +1,12 @@
 . "$topsrcdir/build/unix/mozconfig.linux"
 
+export PKG_CONFIG_LIBDIR=/usr/lib/pkgconfig:/usr/share/pkgconfig
+
 if test `uname -m` = "x86_64"; then
   # -march=pentiumpro is what our 32-bit native toolchain defaults to
   CC="$CC -m32 -march=pentiumpro"
   CXX="$CXX -m32 -march=pentiumpro"
   ac_add_options --target=i686-pc-linux
   ac_add_options --host=i686-pc-linux
   ac_add_options --x-libraries=/usr/lib
-  export PKG_CONFIG_LIBDIR=/usr/lib/pkgconfig:/usr/share/pkgconfig
 fi
--- a/build/valgrind/mach_commands.py
+++ b/build/valgrind/mach_commands.py
@@ -87,16 +87,18 @@ class MachCommands(MachCommandBase):
             firefox_args = [httpd.get_url()]
 
             env = os.environ.copy()
             env['G_SLICE'] = 'always-malloc'
             env['MOZ_CC_RUN_DURING_SHUTDOWN'] = '1'
             env['MOZ_CRASHREPORTER_NO_REPORT'] = '1'
             env['XPCOM_DEBUG_BREAK'] = 'warn'
 
+            env.update(self.extra_environment_variables)
+
             outputHandler = OutputHandler()
             kp_kwargs = {'processOutputLine': [outputHandler]}
 
             valgrind = 'valgrind'
             if not os.path.exists(valgrind):
                 valgrind = findInPath(valgrind)
 
             valgrind_args = [
--- a/dom/base/FileList.cpp
+++ b/dom/base/FileList.cpp
@@ -16,40 +16,16 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMFileList)
   NS_INTERFACE_MAP_ENTRY(nsIDOMFileList)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(FileList)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(FileList)
 
-/* static */ already_AddRefed<FileList>
-FileList::Create(nsISupports* aParent, FileListClonedData* aData)
-{
-  MOZ_ASSERT(aData);
-
-  nsRefPtr<FileList> fileList = new FileList(aParent);
-
-  const nsTArray<nsRefPtr<BlobImpl>>& blobImpls = aData->BlobImpls();
-  for (uint32_t i = 0; i < blobImpls.Length(); ++i) {
-    const nsRefPtr<BlobImpl>& blobImpl = blobImpls[i];
-    MOZ_ASSERT(blobImpl);
-    MOZ_ASSERT(blobImpl->IsFile());
-
-    nsRefPtr<File> file = File::Create(aParent, blobImpl);
-    MOZ_ASSERT(file);
-
-    if (NS_WARN_IF(!fileList->Append(file))) {
-      return nullptr;
-    }
-  }
-
-  return fileList.forget();
-}
-
 JSObject*
 FileList::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return mozilla::dom::FileListBinding::Wrap(aCx, this, aGivenProto);
 }
 
 NS_IMETHODIMP
 FileList::GetLength(uint32_t* aLength)
@@ -62,24 +38,10 @@ FileList::GetLength(uint32_t* aLength)
 NS_IMETHODIMP
 FileList::Item(uint32_t aIndex, nsISupports** aFile)
 {
   nsCOMPtr<nsIDOMBlob> file = Item(aIndex);
   file.forget(aFile);
   return NS_OK;
 }
 
-already_AddRefed<FileListClonedData>
-FileList::CreateClonedData() const
-{
-  nsTArray<nsRefPtr<BlobImpl>> blobImpls;
-  for (uint32_t i = 0; i < mFiles.Length(); ++i) {
-    blobImpls.AppendElement(mFiles[i]->Impl());
-  }
-
-  nsRefPtr<FileListClonedData> data = new FileListClonedData(blobImpls);
-  return data.forget();
-}
-
-NS_IMPL_ISUPPORTS0(FileListClonedData)
-
 } // namespace dom
 } // namespace mozilla
--- a/dom/base/FileList.h
+++ b/dom/base/FileList.h
@@ -2,62 +2,40 @@
 /* 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/. */
 
 #ifndef mozilla_dom_FileList_h
 #define mozilla_dom_FileList_h
 
+#include "mozilla/dom/BindingDeclarations.h"
+#include "nsCycleCollectionParticipant.h"
 #include "nsIDOMFileList.h"
 #include "nsWrapperCache.h"
 
 namespace mozilla {
 namespace dom {
 
 class BlobImpls;
 class File;
 
-class FileListClonedData final : public nsISupports
-{
-public:
-  NS_DECL_THREADSAFE_ISUPPORTS
-
-  explicit FileListClonedData(const nsTArray<nsRefPtr<BlobImpl>>& aBlobImpls)
-    : mBlobImpls(aBlobImpls)
-  {}
-
-  const nsTArray<nsRefPtr<BlobImpl>>& BlobImpls() const
-  {
-    return mBlobImpls;
-  }
-
-private:
-  ~FileListClonedData()
-  {}
-
-  const nsTArray<nsRefPtr<BlobImpl>> mBlobImpls;
-};
-
 class FileList final : public nsIDOMFileList,
                        public nsWrapperCache
 {
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(FileList)
 
   NS_DECL_NSIDOMFILELIST
 
   explicit FileList(nsISupports* aParent)
     : mParent(aParent)
   {}
 
-  static already_AddRefed<FileList>
-  Create(nsISupports* aParent, FileListClonedData* aData);
-
   virtual JSObject* WrapObject(JSContext* aCx,
                                JS::Handle<JSObject*> aGivenProto) override;
 
   nsISupports* GetParentObject()
   {
     return mParent;
   }
 
@@ -109,19 +87,16 @@ public:
     return aFound ? mFiles.ElementAt(aIndex) : nullptr;
   }
 
   uint32_t Length()
   {
     return mFiles.Length();
   }
 
-  // Useful for cloning
-  already_AddRefed<FileListClonedData> CreateClonedData() const;
-
 private:
   ~FileList() {}
 
   nsTArray<nsRefPtr<File>> mFiles;
   nsCOMPtr<nsISupports> mParent;
 };
 
 } // namespace dom
--- a/dom/base/PostMessageEvent.cpp
+++ b/dom/base/PostMessageEvent.cpp
@@ -23,17 +23,18 @@
 namespace mozilla {
 namespace dom {
 
 PostMessageEvent::PostMessageEvent(nsGlobalWindow* aSource,
                                    const nsAString& aCallerOrigin,
                                    nsGlobalWindow* aTargetWindow,
                                    nsIPrincipal* aProvidedPrincipal,
                                    bool aTrustedCaller)
-: mSource(aSource),
+: StructuredCloneHelper(CloningSupported, TransferringSupported),
+  mSource(aSource),
   mCallerOrigin(aCallerOrigin),
   mTargetWindow(aTargetWindow),
   mProvidedPrincipal(aProvidedPrincipal),
   mTrustedCaller(aTrustedCaller)
 {
   MOZ_COUNT_CTOR(PostMessageEvent);
 }
 
--- a/dom/base/StructuredCloneHelper.cpp
+++ b/dom/base/StructuredCloneHelper.cpp
@@ -194,149 +194,208 @@ StructuredCloneHelperInternal::FreeTrans
                                                     void* aContent,
                                                     uint64_t aExtraData)
 {
   MOZ_CRASH("Nothing to free.");
 }
 
 // StructuredCloneHelper class
 
-StructuredCloneHelper::StructuredCloneHelper(uint32_t aFlags)
-  : mFlags(aFlags)
+StructuredCloneHelper::StructuredCloneHelper(CloningSupport aSupportsCloning,
+                                             TransferringSupport aSupportsTransferring)
+  : mSupportsCloning(aSupportsCloning == CloningSupported)
+  , mSupportsTransferring(aSupportsTransferring == TransferringSupported)
   , mParent(nullptr)
 {}
 
 StructuredCloneHelper::~StructuredCloneHelper()
 {
   Shutdown();
 }
 
 bool
 StructuredCloneHelper::Write(JSContext* aCx,
+                             JS::Handle<JS::Value> aValue)
+{
+  return Write(aCx, aValue, JS::UndefinedHandleValue);
+}
+
+bool
+StructuredCloneHelper::Write(JSContext* aCx,
                              JS::Handle<JS::Value> aValue,
                              JS::Handle<JS::Value> aTransfer)
 {
   bool ok = StructuredCloneHelperInternal::Write(aCx, aValue, aTransfer);
   mTransferringPort.Clear();
   return ok;
 }
 
 bool
 StructuredCloneHelper::Read(nsISupports* aParent,
                             JSContext* aCx,
                             JS::MutableHandle<JS::Value> aValue)
 {
   mozilla::AutoRestore<nsISupports*> guard(mParent);
   mParent = aParent;
 
-  return StructuredCloneHelperInternal::Read(aCx, aValue);
+  bool ok = StructuredCloneHelperInternal::Read(aCx, aValue);
+  mBlobImplArray.Clear();
+  return ok;
+}
+
+bool
+StructuredCloneHelper::ReadFromBuffer(nsISupports* aParent,
+                                      JSContext* aCx,
+                                      uint64_t* aBuffer,
+                                      size_t aBufferLength,
+                                      nsTArray<nsRefPtr<BlobImpl>>& aBlobImpls,
+                                      JS::MutableHandle<JS::Value> aValue)
+{
+  MOZ_ASSERT(!mBuffer, "ReadFromBuffer() must be called without a Write().");
+  MOZ_ASSERT(mBlobImplArray.IsEmpty());
+
+  MOZ_ASSERT(aBuffer);
+  MOZ_ASSERT_IF(!mSupportsCloning, aBlobImpls.IsEmpty());
+
+  mozilla::AutoRestore<nsISupports*> guard(mParent);
+  mParent = aParent;
+
+  mBlobImplArray.AppendElements(aBlobImpls);
+
+  bool ok = JS_ReadStructuredClone(aCx, aBuffer, aBufferLength,
+                                   JS_STRUCTURED_CLONE_VERSION, aValue,
+                                   &gCallbacks, this);
+
+  mBlobImplArray.Clear();
+  return ok;
 }
 
 JSObject*
 StructuredCloneHelper::ReadCallback(JSContext* aCx,
                                     JSStructuredCloneReader* aReader,
                                     uint32_t aTag,
                                     uint32_t aIndex)
 {
+  MOZ_ASSERT(mSupportsCloning);
+
   if (aTag == SCTAG_DOM_BLOB) {
-    MOZ_ASSERT(!(mFlags & eBlobNotSupported));
+    MOZ_ASSERT(aIndex < mBlobImplArray.Length());
+    nsRefPtr<BlobImpl> blobImpl =  mBlobImplArray[aIndex];
 
-    BlobImpl* blobImpl;
-    if (JS_ReadBytes(aReader, &blobImpl, sizeof(blobImpl))) {
-      MOZ_ASSERT(blobImpl);
+    // nsRefPtr<File> needs to go out of scope before toObjectOrNull() is
+    // called because the static analysis thinks dereferencing XPCOM objects
+    // can GC (because in some cases it can!), and a return statement with a
+    // JSObject* type means that JSObject* is on the stack as a raw pointer
+    // while destructors are running.
+    JS::Rooted<JS::Value> val(aCx);
+    {
+      nsRefPtr<Blob> blob = Blob::Create(mParent, blobImpl);
+      if (!ToJSValue(aCx, blob, &val)) {
+        return nullptr;
+      }
+    }
+
+    return &val.toObject();
+  }
 
-      // nsRefPtr<File> needs to go out of scope before toObjectOrNull() is
-      // called because the static analysis thinks dereferencing XPCOM objects
-      // can GC (because in some cases it can!), and a return statement with a
-      // JSObject* type means that JSObject* is on the stack as a raw pointer
-      // while destructors are running.
-      JS::Rooted<JS::Value> val(aCx);
-      {
-        nsRefPtr<Blob> blob = Blob::Create(mParent, blobImpl);
-        if (!ToJSValue(aCx, blob, &val)) {
+  if (aTag == SCTAG_DOM_FILELIST) {
+    JS::Rooted<JS::Value> val(aCx);
+    {
+      nsRefPtr<FileList> fileList = new FileList(mParent);
+
+      // |aIndex| is the number of BlobImpls to use from |offset|.
+      uint32_t tag, offset;
+      if (!JS_ReadUint32Pair(aReader, &tag, &offset)) {
+        return nullptr;
+      }
+      MOZ_ASSERT(tag == 0);
+
+      for (uint32_t i = 0; i < aIndex; ++i) {
+        uint32_t index = offset + i;
+        MOZ_ASSERT(index < mBlobImplArray.Length());
+
+        nsRefPtr<BlobImpl> blobImpl = mBlobImplArray[index];
+        MOZ_ASSERT(blobImpl->IsFile());
+
+        nsRefPtr<File> file = File::Create(mParent, blobImpl);
+        if (!fileList->Append(file)) {
           return nullptr;
         }
       }
 
-      return &val.toObject();
+      if (!ToJSValue(aCx, fileList, &val)) {
+        return nullptr;
+      }
     }
-  }
-
-  if (aTag == SCTAG_DOM_FILELIST) {
-    MOZ_ASSERT(!(mFlags & eFileListNotSupported));
-
-    FileListClonedData* fileListClonedData;
-    if (JS_ReadBytes(aReader, &fileListClonedData,
-                     sizeof(fileListClonedData))) {
-      MOZ_ASSERT(fileListClonedData);
 
-      // nsRefPtr<FileList> needs to go out of scope before toObjectOrNull() is
-      // called because the static analysis thinks dereferencing XPCOM objects
-      // can GC (because in some cases it can!), and a return statement with a
-      // JSObject* type means that JSObject* is on the stack as a raw pointer
-      // while destructors are running.
-      JS::Rooted<JS::Value> val(aCx);
-      {
-        nsRefPtr<FileList> fileList =
-          FileList::Create(mParent, fileListClonedData);
-        if (!fileList || !ToJSValue(aCx, fileList, &val)) {
-          return nullptr;
-        }
-      }
-
-      return &val.toObject();
-    }
+    return &val.toObject();
   }
 
   return NS_DOMReadStructuredClone(aCx, aReader, aTag, aIndex, nullptr);
 }
 
 bool
 StructuredCloneHelper::WriteCallback(JSContext* aCx,
                                      JSStructuredCloneWriter* aWriter,
                                      JS::Handle<JSObject*> aObj)
 {
+  if (!mSupportsCloning) {
+    return false;
+  }
+
   // See if this is a File/Blob object.
-  if (!(mFlags & eBlobNotSupported)) {
+  {
     Blob* blob = nullptr;
     if (NS_SUCCEEDED(UNWRAP_OBJECT(Blob, aObj, blob))) {
       BlobImpl* blobImpl = blob->Impl();
-      return JS_WriteUint32Pair(aWriter, SCTAG_DOM_BLOB, 0) &&
-             JS_WriteBytes(aWriter, &blobImpl, sizeof(blobImpl)) &&
-             StoreISupports(blobImpl);
+      if (JS_WriteUint32Pair(aWriter, SCTAG_DOM_BLOB,
+                             mBlobImplArray.Length())) {
+        mBlobImplArray.AppendElement(blobImpl);
+        return true;
+      }
+
+      return false;
     }
   }
 
-  if (!(mFlags & eFileListNotSupported)) {
+  {
     FileList* fileList = nullptr;
     if (NS_SUCCEEDED(UNWRAP_OBJECT(FileList, aObj, fileList))) {
-      nsRefPtr<FileListClonedData> fileListClonedData =
-        fileList->CreateClonedData();
-      MOZ_ASSERT(fileListClonedData);
-      FileListClonedData* ptr = fileListClonedData.get();
-      return JS_WriteUint32Pair(aWriter, SCTAG_DOM_FILELIST, 0) &&
-             JS_WriteBytes(aWriter, &ptr, sizeof(ptr)) &&
-             StoreISupports(fileListClonedData);
+      // A FileList is serialized writing the X number of elements and the offset
+      // from mBlobImplArray. The Read will take X elements from mBlobImplArray
+      // starting from the offset.
+      if (!JS_WriteUint32Pair(aWriter, SCTAG_DOM_FILELIST,
+                              fileList->Length()) ||
+          !JS_WriteUint32Pair(aWriter, 0,
+                              mBlobImplArray.Length())) {
+        return false;
+      }
+
+      for (uint32_t i = 0; i < fileList->Length(); ++i) {
+        mBlobImplArray.AppendElement(fileList->Item(i)->Impl());
+      }
+
+      return true;
     }
   }
 
   return NS_DOMWriteStructuredClone(aCx, aWriter, aObj, nullptr);
 }
 
 bool
 StructuredCloneHelper::ReadTransferCallback(JSContext* aCx,
                                             JSStructuredCloneReader* aReader,
                                             uint32_t aTag,
                                             void* aContent,
                                             uint64_t aExtraData,
                                             JS::MutableHandleObject aReturnObject)
 {
+  MOZ_ASSERT(mSupportsTransferring);
+
   if (aTag == SCTAG_DOM_MAP_MESSAGEPORT) {
-    MOZ_ASSERT(!(mFlags & eMessagePortNotSupported));
-
     // This can be null.
     nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(mParent);
 
     MOZ_ASSERT(aExtraData < mPortIdentifiers.Length());
     const MessagePortIdentifier& portIdentifier = mPortIdentifiers[aExtraData];
 
     // aExtraData is the index of this port identifier.
     ErrorResult rv;
@@ -365,17 +424,21 @@ StructuredCloneHelper::ReadTransferCallb
 bool
 StructuredCloneHelper::WriteTransferCallback(JSContext* aCx,
                                              JS::Handle<JSObject*> aObj,
                                              uint32_t* aTag,
                                              JS::TransferableOwnership* aOwnership,
                                              void** aContent,
                                              uint64_t* aExtraData)
 {
-  if (!(mFlags & eMessagePortNotSupported)) {
+  if (!mSupportsTransferring) {
+    return false;
+  }
+
+  {
     MessagePortBase* port = nullptr;
     nsresult rv = UNWRAP_OBJECT(MessagePort, aObj, port);
     if (NS_SUCCEEDED(rv)) {
       if (mTransferringPort.Contains(port)) {
         // No duplicates.
         return false;
       }
 
@@ -401,18 +464,19 @@ StructuredCloneHelper::WriteTransferCall
 }
 
 void
 StructuredCloneHelper::FreeTransferCallback(uint32_t aTag,
                                             JS::TransferableOwnership aOwnership,
                                             void* aContent,
                                             uint64_t aExtraData)
 {
+  MOZ_ASSERT(mSupportsTransferring);
+
   if (aTag == SCTAG_DOM_MAP_MESSAGEPORT) {
-    MOZ_ASSERT(!(mFlags & eMessagePortNotSupported));
     MOZ_ASSERT(!aContent);
     MOZ_ASSERT(aExtraData < mPortIdentifiers.Length());
     MessagePort::ForceClose(mPortIdentifiers[aExtraData]);
   }
 }
 
 } // dom namespace
 } // mozilla namespace
--- a/dom/base/StructuredCloneHelper.h
+++ b/dom/base/StructuredCloneHelper.h
@@ -71,61 +71,90 @@ public:
 
   bool Write(JSContext* aCx,
              JS::Handle<JS::Value> aValue,
              JS::Handle<JS::Value> aTransfer);
 
   bool Read(JSContext* aCx,
             JS::MutableHandle<JS::Value> aValue);
 
+  uint64_t* BufferData() const
+  {
+    MOZ_ASSERT(mBuffer, "Write() has never been called.");
+    return mBuffer->data();
+  }
+
+  size_t BufferSize() const
+  {
+    MOZ_ASSERT(mBuffer, "Write() has never been called.");
+    return mBuffer->nbytes();
+  }
+
 protected:
   nsAutoPtr<JSAutoStructuredCloneBuffer> mBuffer;
 
 #ifdef DEBUG
   bool mShutdownCalled;
 #endif
 };
 
+class BlobImpl;
 class MessagePortBase;
 class MessagePortIdentifier;
 
 class StructuredCloneHelper : public StructuredCloneHelperInternal
 {
 public:
-  enum StructuredCloneHelperFlags {
-    eAll = 0,
-
-    // Disable the cloning of blobs. If a blob is part of the cloning value,
-    // an exception will be thrown.
-    eBlobNotSupported = 1 << 0,
-
-    // Disable the cloning of FileLists. If a FileList is part of the cloning
-    // value, an exception will be thrown.
-    eFileListNotSupported = 1 << 1,
-
-    // MessagePort can just be transfered. Using this flag we do not support
-    // the transfering.
-    eMessagePortNotSupported = 1 << 2,
+  enum CloningSupport
+  {
+    CloningSupported,
+    CloningNotSupported
   };
 
-  // aFlags is a bitmap of StructuredCloneHelperFlags.
-  explicit StructuredCloneHelper(uint32_t aFlags = eAll);
+  enum TransferringSupport
+  {
+    TransferringSupported,
+    TransferringNotSupported
+  };
+
+  // If cloning is supported, this object will clone objects such as Blobs,
+  // FileList, ImageData, etc.
+  // If transferring is supported, we will transfer MessagePorts and in the
+  // future other transferrable objects.
+  explicit StructuredCloneHelper(CloningSupport aSupportsCloning,
+                                 TransferringSupport aSupportsTransferring);
   virtual ~StructuredCloneHelper();
 
   bool Write(JSContext* aCx,
+             JS::Handle<JS::Value> aValue);
+
+  bool Write(JSContext* aCx,
              JS::Handle<JS::Value> aValue,
              JS::Handle<JS::Value> aTransfer);
 
   bool Read(nsISupports* aParent,
             JSContext* aCx,
             JS::MutableHandle<JS::Value> aValue);
 
+  bool ReadFromBuffer(nsISupports* aParent,
+                      JSContext* aCx,
+                      uint64_t* aBuffer,
+                      size_t aBufferLength,
+                      nsTArray<nsRefPtr<BlobImpl>>& aBlobImpls,
+                      JS::MutableHandle<JS::Value> aValue);
+
+  const nsTArray<nsRefPtr<BlobImpl>>& ClonedBlobImpls() const
+  {
+    MOZ_ASSERT(mBuffer, "Write() has never been called.");
+    return mBlobImplArray;
+  }
+
   nsTArray<nsRefPtr<MessagePortBase>>& GetTransferredPorts()
   {
-    MOZ_ASSERT(!(mFlags & eMessagePortNotSupported));
+    MOZ_ASSERT(mSupportsTransferring);
     return mTransferredPorts;
   }
 
   // Custom Callbacks
 
   virtual JSObject* ReadCallback(JSContext* aCx,
                                  JSStructuredCloneReader* aReader,
                                  uint32_t aTag,
@@ -149,29 +178,22 @@ public:
                                      void** aContent,
                                      uint64_t* aExtraData) override;
 
   virtual void FreeTransferCallback(uint32_t aTag,
                                     JS::TransferableOwnership aOwnership,
                                     void* aContent,
                                     uint64_t aExtraData) override;
 private:
-  bool StoreISupports(nsISupports* aSupports)
-  {
-    MOZ_ASSERT(aSupports);
-    mSupportsArray.AppendElement(aSupports);
-    return true;
-  }
-
-  // This is our bitmap.
-  uint32_t mFlags;
+  bool mSupportsCloning;
+  bool mSupportsTransferring;
 
   // Useful for the structured clone algorithm:
 
-  nsTArray<nsCOMPtr<nsISupports>> mSupportsArray;
+  nsTArray<nsRefPtr<BlobImpl>> mBlobImplArray;
 
   // This raw pointer is set and unset into the ::Read(). It's always null
   // outside that method. For this reason it's a raw pointer.
   nsISupports* MOZ_NON_OWNING_REF mParent;
 
   // This hashtable contains the ports while doing write (transferring and
   // mapping transferred objects to the objects in the clone). It's an empty
   // array outside the 'Write()' method.
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -1245,31 +1245,21 @@ nsGlobalWindow::Init()
                "gEntropyCollector should have been initialized!");
 
   gDOMLeakPRLog = PR_NewLogModule("DOMLeak");
   NS_ASSERTION(gDOMLeakPRLog, "gDOMLeakPRLog should have been initialized!");
 
   sWindowsById = new WindowByIdTable();
 }
 
-static PLDHashOperator
-DisconnectEventTargetObjects(nsPtrHashKey<DOMEventTargetHelper>* aKey,
-                             void* aClosure)
-{
-  nsRefPtr<DOMEventTargetHelper> target = aKey->GetKey();
-  target->DisconnectFromOwner();
-  return PL_DHASH_NEXT;
-}
-
 nsGlobalWindow::~nsGlobalWindow()
 {
   AssertIsOnMainThread();
 
-  mEventTargetObjects.EnumerateEntries(DisconnectEventTargetObjects, nullptr);
-  mEventTargetObjects.Clear();
+  DisconnectEventTargetObjects();
 
   // We have to check if sWindowsById isn't null because ::Shutdown might have
   // been called.
   if (sWindowsById) {
     NS_ASSERTION(sWindowsById->Get(mWindowID),
                  "This window should be in the hash table");
     sWindowsById->Remove(mWindowID);
   }
@@ -1370,16 +1360,27 @@ nsGlobalWindow::AddEventTargetObject(DOM
 
 void
 nsGlobalWindow::RemoveEventTargetObject(DOMEventTargetHelper* aObject)
 {
   MOZ_ASSERT(IsInnerWindow());
   mEventTargetObjects.RemoveEntry(aObject);
 }
 
+void
+nsGlobalWindow::DisconnectEventTargetObjects()
+{
+  for (auto iter = mEventTargetObjects.ConstIter(); !iter.Done();
+       iter.Next()) {
+    nsRefPtr<DOMEventTargetHelper> target = iter.Get()->GetKey();
+    target->DisconnectFromOwner();
+  }
+  mEventTargetObjects.Clear();
+}
+
 // static
 void
 nsGlobalWindow::ShutDown()
 {
   AssertIsOnMainThread();
 
   if (gDumpFile && gDumpFile != stdout) {
     fclose(gDumpFile);
@@ -1429,18 +1430,17 @@ nsGlobalWindow::CleanUp()
 {
   // Guarantee idempotence.
   if (mCleanedUp)
     return;
   mCleanedUp = true;
 
   StartDying();
 
-  mEventTargetObjects.EnumerateEntries(DisconnectEventTargetObjects, nullptr);
-  mEventTargetObjects.Clear();
+  DisconnectEventTargetObjects();
 
   if (mObserver) {
     nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
     if (os) {
       os->RemoveObserver(mObserver, NS_IOSERVICE_OFFLINE_STATUS_TOPIC);
       os->RemoveObserver(mObserver, "dom-storage2-changed");
     }
 
@@ -13572,37 +13572,16 @@ nsGlobalWindow::EnableTimeChangeNotifica
 }
 
 void
 nsGlobalWindow::DisableTimeChangeNotifications()
 {
   mozilla::time::RemoveWindowListener(this);
 }
 
-static PLDHashOperator
-CollectSizeAndListenerCount(
-  nsPtrHashKey<DOMEventTargetHelper>* aEntry,
-  void *arg)
-{
-  nsWindowSizes* windowSizes = static_cast<nsWindowSizes*>(arg);
-
-  DOMEventTargetHelper* et = aEntry->GetKey();
-
-  if (nsCOMPtr<nsISizeOfEventTarget> iSizeOf = do_QueryObject(et)) {
-    windowSizes->mDOMEventTargetsSize +=
-      iSizeOf->SizeOfEventTargetIncludingThis(windowSizes->mMallocSizeOf);
-  }
-
-  if (EventListenerManager* elm = et->GetExistingListenerManager()) {
-    windowSizes->mDOMEventListenersCount += elm->ListenerCount();
-  }
-
-  return PL_DHASH_NEXT;
-}
-
 void
 nsGlobalWindow::AddSizeOfIncludingThis(nsWindowSizes* aWindowSizes) const
 {
   aWindowSizes->mDOMOtherSize += aWindowSizes->mMallocSizeOf(this);
 
   if (IsInnerWindow()) {
     EventListenerManager* elm = GetExistingListenerManager();
     if (elm) {
@@ -13626,20 +13605,29 @@ nsGlobalWindow::AddSizeOfIncludingThis(n
       mNavigator->SizeOfIncludingThis(aWindowSizes->mMallocSizeOf);
   }
 
   // The things pointed to by the entries will be measured below, so we
   // use nullptr for the callback here.
   aWindowSizes->mDOMEventTargetsSize +=
     mEventTargetObjects.SizeOfExcludingThis(nullptr,
                                             aWindowSizes->mMallocSizeOf);
-  aWindowSizes->mDOMEventTargetsCount +=
-    const_cast<nsTHashtable<nsPtrHashKey<DOMEventTargetHelper> >*>
-      (&mEventTargetObjects)->EnumerateEntries(CollectSizeAndListenerCount,
-                                               aWindowSizes);
+
+
+  for (auto iter = mEventTargetObjects.ConstIter(); !iter.Done(); iter.Next()) {
+    DOMEventTargetHelper* et = iter.Get()->GetKey();
+    if (nsCOMPtr<nsISizeOfEventTarget> iSizeOf = do_QueryObject(et)) {
+      aWindowSizes->mDOMEventTargetsSize +=
+        iSizeOf->SizeOfEventTargetIncludingThis(aWindowSizes->mMallocSizeOf);
+    }
+    if (EventListenerManager* elm = et->GetExistingListenerManager()) {
+      aWindowSizes->mDOMEventListenersCount += elm->ListenerCount();
+    }
+    ++aWindowSizes->mDOMEventTargetsCount;
+  }
 }
 
 
 #ifdef MOZ_GAMEPAD
 void
 nsGlobalWindow::AddGamepad(uint32_t aIndex, Gamepad* aGamepad)
 {
   MOZ_ASSERT(IsInnerWindow());
--- a/dom/base/nsGlobalWindow.h
+++ b/dom/base/nsGlobalWindow.h
@@ -1612,16 +1612,18 @@ protected:
   // being abused. This is used in the cases where we have no modifiable UI to
   // show, in that case we show a separate dialog to ask this question.
   bool ConfirmDialogIfNeeded();
 
 private:
   // Fire the JS engine's onNewGlobalObject hook.  Only used on inner windows.
   void FireOnNewGlobalObject();
 
+  void DisconnectEventTargetObjects();
+
 protected:
   // When adding new member variables, be careful not to create cycles
   // through JavaScript.  If there is any chance that a member variable
   // could own objects that are implemented in JavaScript, then those
   // objects will keep the global object (this object) alive.  To prevent
   // these cycles, ownership of such members must be released in
   // |CleanUp| and |DetachFromDocShell|.
 
--- a/dom/base/nsJSEnvironment.cpp
+++ b/dom/base/nsJSEnvironment.cpp
@@ -2488,36 +2488,48 @@ JSObject*
 NS_DOMReadStructuredClone(JSContext* cx,
                           JSStructuredCloneReader* reader,
                           uint32_t tag,
                           uint32_t data,
                           void* closure)
 {
   if (tag == SCTAG_DOM_IMAGEDATA) {
     return ReadStructuredCloneImageData(cx, reader);
-  } else if (tag == SCTAG_DOM_WEBCRYPTO_KEY) {
+  }
+
+  if (tag == SCTAG_DOM_WEBCRYPTO_KEY) {
+    if (!NS_IsMainThread()) {
+      return nullptr;
+    }
+
     nsIGlobalObject *global = xpc::NativeGlobal(JS::CurrentGlobalOrNull(cx));
     if (!global) {
       return nullptr;
     }
 
     // Prevent the return value from being trashed by a GC during ~nsRefPtr.
     JS::Rooted<JSObject*> result(cx);
     {
       nsRefPtr<CryptoKey> key = new CryptoKey(global);
       if (!key->ReadStructuredClone(reader)) {
         result = nullptr;
       } else {
         result = key->WrapObject(cx, nullptr);
       }
     }
     return result;
-  } else if (tag == SCTAG_DOM_NULL_PRINCIPAL ||
-             tag == SCTAG_DOM_SYSTEM_PRINCIPAL ||
-             tag == SCTAG_DOM_CONTENT_PRINCIPAL) {
+  }
+
+  if (tag == SCTAG_DOM_NULL_PRINCIPAL ||
+      tag == SCTAG_DOM_SYSTEM_PRINCIPAL ||
+      tag == SCTAG_DOM_CONTENT_PRINCIPAL) {
+    if (!NS_IsMainThread()) {
+      return nullptr;
+    }
+
     mozilla::ipc::PrincipalInfo info;
     if (tag == SCTAG_DOM_SYSTEM_PRINCIPAL) {
       info = mozilla::ipc::SystemPrincipalInfo();
     } else if (tag == SCTAG_DOM_NULL_PRINCIPAL) {
       info = mozilla::ipc::NullPrincipalInfo();
     } else {
       uint32_t appId = data;
 
@@ -2545,58 +2557,64 @@ NS_DOMReadStructuredClone(JSContext* cx,
     JS::RootedValue result(cx);
     rv = nsContentUtils::WrapNative(cx, principal, &NS_GET_IID(nsIPrincipal), &result);
     if (NS_FAILED(rv)) {
       xpc::Throw(cx, NS_ERROR_DOM_DATA_CLONE_ERR);
       return nullptr;
     }
 
     return result.toObjectOrNull();
-  } else if (tag == SCTAG_DOM_NFC_NDEF) {
+  }
+
 #ifdef MOZ_NFC
+  if (tag == SCTAG_DOM_NFC_NDEF) {
+    if (!NS_IsMainThread()) {
+      return nullptr;
+    }
+
     nsIGlobalObject *global = xpc::NativeGlobal(JS::CurrentGlobalOrNull(cx));
     if (!global) {
       return nullptr;
     }
 
     // Prevent the return value from being trashed by a GC during ~nsRefPtr.
     JS::Rooted<JSObject*> result(cx);
     {
       nsRefPtr<MozNDEFRecord> ndefRecord = new MozNDEFRecord(global);
       result = ndefRecord->ReadStructuredClone(cx, reader) ?
                ndefRecord->WrapObject(cx, nullptr) : nullptr;
     }
     return result;
-#else
-    return nullptr;
+  }
 #endif
-  }
-
+
+#ifdef MOZ_WEBRTC
   if (tag == SCTAG_DOM_RTC_CERTIFICATE) {
-#ifdef MOZ_WEBRTC
+    if (!NS_IsMainThread()) {
+      return nullptr;
+    }
+
     nsIGlobalObject *global = xpc::NativeGlobal(JS::CurrentGlobalOrNull(cx));
     if (!global) {
       return nullptr;
     }
 
     // Prevent the return value from being trashed by a GC during ~nsRefPtr.
     JS::Rooted<JSObject*> result(cx);
     {
       nsRefPtr<RTCCertificate> cert = new RTCCertificate(global);
       if (!cert->ReadStructuredClone(reader)) {
         result = nullptr;
       } else {
         result = cert->WrapObject(cx, nullptr);
       }
     }
     return result;
-#else
-    return nullptr;
+  }
 #endif
-  }
 
   // Don't know what this is. Bail.
   xpc::Throw(cx, NS_ERROR_DOM_DATA_CLONE_ERR);
   return nullptr;
 }
 
 bool
 NS_DOMWriteStructuredClone(JSContext* cx,
@@ -2608,30 +2626,32 @@ NS_DOMWriteStructuredClone(JSContext* cx
   ImageData* imageData;
   if (NS_SUCCEEDED(UNWRAP_OBJECT(ImageData, obj, imageData))) {
     return WriteStructuredCloneImageData(cx, writer, imageData);
   }
 
   // Handle Key cloning
   CryptoKey* key;
   if (NS_SUCCEEDED(UNWRAP_OBJECT(CryptoKey, obj, key))) {
+    MOZ_ASSERT(NS_IsMainThread(), "This object should not be exposed outside the main-thread.");
     return JS_WriteUint32Pair(writer, SCTAG_DOM_WEBCRYPTO_KEY, 0) &&
            key->WriteStructuredClone(writer);
   }
 
 #ifdef MOZ_WEBRTC
   // Handle WebRTC Certificate cloning
   RTCCertificate* cert;
   if (NS_SUCCEEDED(UNWRAP_OBJECT(RTCCertificate, obj, cert))) {
+    MOZ_ASSERT(NS_IsMainThread(), "This object should not be exposed outside the main-thread.");
     return JS_WriteUint32Pair(writer, SCTAG_DOM_RTC_CERTIFICATE, 0) &&
            cert->WriteStructuredClone(writer);
   }
 #endif
 
-  if (xpc::IsReflector(obj)) {
+  if (NS_IsMainThread() && xpc::IsReflector(obj)) {
     nsCOMPtr<nsISupports> base = xpc::UnwrapReflectorToISupports(obj);
     nsCOMPtr<nsIPrincipal> principal = do_QueryInterface(base);
     if (principal) {
       mozilla::ipc::PrincipalInfo info;
       if (NS_WARN_IF(NS_FAILED(PrincipalToPrincipalInfo(principal, &info)))) {
         xpc::Throw(cx, NS_ERROR_DOM_DATA_CLONE_ERR);
         return false;
       }
@@ -2649,16 +2669,17 @@ NS_DOMWriteStructuredClone(JSContext* cx
              JS_WriteUint32Pair(writer, cInfo.isInBrowserElement(), cInfo.spec().Length()) &&
              JS_WriteBytes(writer, cInfo.spec().get(), cInfo.spec().Length());
     }
   }
 
 #ifdef MOZ_NFC
   MozNDEFRecord* ndefRecord;
   if (NS_SUCCEEDED(UNWRAP_OBJECT(MozNDEFRecord, obj, ndefRecord))) {
+    MOZ_ASSERT(NS_IsMainThread(), "This object should not be exposed outside the main-thread.");
     return JS_WriteUint32Pair(writer, SCTAG_DOM_NFC_NDEF, 0) &&
            ndefRecord->WriteStructuredClone(cx, writer);
   }
 #endif // MOZ_NFC
 
   // Don't know what this is
   xpc::Throw(cx, NS_ERROR_DOM_DATA_CLONE_ERR);
   return false;
rename from dom/base/test/iframe_cloning_fileList.html
rename to dom/base/test/iframe_postMessages.html
--- a/dom/base/test/iframe_cloning_fileList.html
+++ b/dom/base/test/iframe_postMessages.html
@@ -1,10 +1,10 @@
 <!DOCTYPE HTML>
 <html>
 <body>
 <script>
 onmessage = function(e) {
-  parent.postMessage(e.data, '*');
+  parent.postMessage(e.data, '*', e.ports);
 }
 </script>
 </body>
 </html>
--- a/dom/base/test/mochitest.ini
+++ b/dom/base/test/mochitest.ini
@@ -237,16 +237,18 @@ support-files =
   referrerHelper.js
   test_performance_user_timing.js
   img_referrer_testserver.sjs
   file_audioLoop.html
   file_webaudioLoop.html
   file_webaudioLoop2.html
   referrer_helper.js
   referrer_testserver.sjs
+  script_postmessages_fileList.js
+  iframe_postMessages.html
 
 [test_anonymousContent_api.html]
 [test_anonymousContent_append_after_reflow.html]
 [test_anonymousContent_insert.html]
 [test_anonymousContent_manipulate_content.html]
 [test_appname_override.html]
 [test_async_setTimeout_stack.html]
 [test_async_setTimeout_stack_across_globals.html]
@@ -800,10 +802,9 @@ skip-if = buildapp == 'mulet' || buildap
 skip-if = buildapp == 'mulet' || buildapp == 'b2g'
 [test_integer_attr_with_leading_zero.html]
 [test_getAttribute_after_createAttribute.html]
 [test_script_loader_crossorigin_data_url.html]
 [test_file_negative_date.html]
 [test_nonascii_blob_url.html]
 [test_window_element_enumeration.html]
 [test_referrer_redirect.html]
-[test_cloning_fileList.html]
-support-files = script_cloning_fileList.js iframe_cloning_fileList.html
+[test_postMessages.html]
rename from dom/base/test/script_cloning_fileList.js
rename to dom/base/test/script_postmessages_fileList.js
deleted file mode 100644
--- a/dom/base/test/test_cloning_fileList.html
+++ /dev/null
@@ -1,85 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<head>
-  <title>Test for cloning FileList</title>
-  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
-</head>
-
-<body>
-<p id="display">
-  <input id="fileList" type="file"></input>
-</p>
-<pre id="test">
-<script class="testbody" type="text/javascript">
-
-var iframeScriptURL;
-
-var url = SimpleTest.getTestFileURL("script_cloning_fileList.js");
-var script = SpecialPowers.loadChromeScript(url);
-script.addMessageListener("file.opened", onOpened);
-
-function onOpened(message) {
-  var fileList = document.getElementById('fileList');
-  SpecialPowers.wrap(fileList).mozSetFileArray([message.file]);
-
-  // Just a simple test
-  var domFile = fileList.files[0];
-  is(domFile.name, "prefs.js", "fileName should be prefs.js");
-
-  var ifr = document.createElement('iframe');
-  ifr.src = iframeScriptURL;
-  ifr.onload = function() {
-    ifr.contentWindow.postMessage(fileList.files, "*");
-  }
-  document.body.appendChild(ifr);
-}
-
-function runTest() {
-  script.sendAsyncMessage("file.open");
-}
-
-onmessage = function(e) {
-  var fileList = document.getElementById('fileList');
-
-  ok(true, "Message received");
-  ok(e.data instanceof FileList, "The object is a FileList");
-  ok(e.data != fileList.files, "The object has been cloned!");
-  is(e.data.length, fileList.files.length, "The length matches");
-  is(e.data.length, 1, "1 element found!");
-  is(e.data[0].name, "prefs.js", "fileName should be prefs.js");
-
-  next();
-}
-
-var tests = [
-  function() {
-    // Same origin
-    iframeScriptURL = 'iframe_cloning_fileList.html';
-    runTest();
-  },
-
-  function() {
-    // Cross Origin
-    iframeScriptURL = 'http://example.com/tests/dom/base/test/iframe_cloning_fileList.html';
-    runTest();
-  }
-];
-
-function next() {
-  if (!tests.length) {
-    script.destroy();
-    SimpleTest.finish();
-    return;
-  }
-
-  var test = tests.shift();
-  test();
-}
-
-SimpleTest.waitForExplicitFinish();
-next();
-</script>
-</pre>
-</body>
-</html>
new file mode 100644
--- /dev/null
+++ b/dom/base/test/test_postMessages.html
@@ -0,0 +1,295 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test for postMessages cloning/transferring objects</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+
+<body>
+<input id="fileList" type="file"></input>
+<script type="application/javascript;version=1.7">
+
+function getType(a) {
+  if (a === null || a === undefined)
+    return 'null';
+
+  if (Array.isArray(a))
+    return 'array';
+
+  if (typeof a == 'object')
+    return 'object';
+
+  return 'primitive';
+}
+
+function compare(a, b) {
+  is (getType(a), getType(b), 'Type matches');
+
+  var type = getType(a);
+  if (type == 'array') {
+    is (a.length, b.length, 'Array.length matches');
+    for (var i = 0; i < a.length; ++i) {
+      compare(a[i], b[i]);
+    }
+
+    return;
+  }
+
+  if (type == 'object') {
+    ok (a !== b, 'They should not match');
+
+    var aProps = [];
+    for (var p in a) aProps.push(p);
+
+    var bProps = [];
+    for (var p in b) bProps.push(p);
+
+    is (aProps.length, bProps.length, 'Props match');
+    is (aProps.sort().toSource(), bProps.sort().toSource(), 'Props match - using toSource()');
+
+    for (var p in a) {
+      compare(a[p], b[p]);
+    }
+
+    return;
+  }
+
+  if (type != 'null') {
+    is (a.toSource(), b.toSource(), 'Matching using toSource()');
+  }
+}
+
+var clonableObjects = [
+  'hello world',
+  123,
+  null,
+  true,
+  new Date(),
+  [ 1, 'test', true, new Date() ],
+  { a: true, b:  null, c: new Date(), d: [ true, false, {} ] },
+  new Blob([123], { type: 'plain/text' }),
+  new ImageData(2, 2),
+];
+
+function create_fileList() {
+  var url = SimpleTest.getTestFileURL("script_postmessages_fileList.js");
+  var script = SpecialPowers.loadChromeScript(url);
+
+  function onOpened(message) {
+    var fileList = document.getElementById('fileList');
+    SpecialPowers.wrap(fileList).mozSetFileArray([message.file]);
+
+    // Just a simple test
+    var domFile = fileList.files[0];
+    is(domFile.name, "prefs.js", "fileName should be prefs.js");
+
+    clonableObjects.push(fileList.files);
+    script.destroy();
+    next();
+  }
+
+  script.addMessageListener("file.opened", onOpened);
+  script.sendAsyncMessage("file.open");
+}
+
+function runTests(obj) {
+  ok(('clonableObjects' in obj) &&
+     ('transferableObjects' in obj) &&
+     (obj.clonableObjects || obj.transferableObjects), "We must run some test!");
+
+  // cloning tests
+  new Promise(function(resolve, reject) {
+    if (!obj.clonableObjects) {
+      resolve();
+      return;
+    }
+
+    var clonableObjectsId = 0;
+    function runClonableTest() {
+      if (clonableObjectsId >= clonableObjects.length) {
+        resolve();
+        return;
+      }
+
+      var object = clonableObjects[clonableObjectsId++];
+      obj.send(object, []).then(function(received) {
+        compare(received.data, object);
+        runClonableTest();
+      });
+    }
+
+    runClonableTest();
+  })
+
+  // transfering tests
+  .then(function() {
+    if (!obj.transferableObjects) {
+      return;
+    }
+
+    // MessagePort
+    return new Promise(function(r, rr) {
+      var mc = new MessageChannel();
+      obj.send(42, [mc.port1]).then(function(received) {
+        ok(received.ports.length, 1, "MessagePort has been transferred");
+        mc.port2.postMessage("hello world");
+        received.ports[0].onmessage = function(e) {
+          ok(e.data, "hello world", "Ports are connected!");
+          r();
+        }
+      });
+    });
+  })
+
+  // done.
+  .then(function() {
+    obj.finished();
+  });
+}
+
+// PostMessage to the same window.
+function test_windowToWindow() {
+  info("Testing window to window");
+  var resolve;
+
+  onmessage = function(e) {
+    if (!resolve) {
+      ok(false, "Unexpected message!");
+      return;
+    }
+
+    let tmp = resolve;
+    resolve = null;
+    tmp({ data: e.data, ports: e.ports });
+  }
+
+  runTests({
+    clonableObjects: true,
+    transferableObjects: true,
+    send: function(what, ports) {
+      return new Promise(function(r, rr) {
+        resolve = r;
+        postMessage(what, '*', ports);
+      });
+    },
+
+    finished: function() {
+      onmessage = null;
+      next();
+    }
+  });
+}
+
+// PostMessage to iframe
+function test_windowToIframe() {
+  info("Testing window to iframe");
+  test_windowToIframeURL('iframe_postMessages.html');
+}
+
+// PostMessage to cross-origin iframe
+function test_windowToCrossOriginIframe() {
+  info("Testing window to cross-origin iframe");
+  test_windowToIframeURL('http://example.com/tests/dom/base/test/iframe_postMessages.html');
+}
+
+// iframe helper class
+function test_windowToIframeURL(url) {
+  var resolve;
+
+  onmessage = function(e) {
+    if (!resolve) {
+      ok(false, "Unexpected message!");
+      return;
+    }
+
+    let tmp = resolve;
+    resolve = null;
+    tmp({ data: e.data, ports: e.ports });
+  }
+
+  var ifr = document.createElement('iframe');
+  ifr.src = url;
+  ifr.onload = function() {
+    runTests({
+      clonableObjects: true,
+      transferableObjects: true,
+      send: function(what, ports) {
+        return new Promise(function(r, rr) {
+          resolve = r;
+          ifr.contentWindow.postMessage(what, '*', ports);
+        });
+      },
+
+      finished: function() {
+        document.body.removeChild(ifr);
+        onmessage = null;
+        next();
+      }
+    });
+  }
+  document.body.appendChild(ifr);
+}
+
+function test_broadcastChannel() {
+  info("Testing broadcastChannel");
+
+  var bc1 = new BroadcastChannel('postMessagesTest');
+  var bc2 = new BroadcastChannel('postMessagesTest');
+
+  var resolve;
+
+  bc2.onmessage = function(e) {
+    if (!resolve) {
+      ok(false, "Unexpected message!");
+      return;
+    }
+
+    let tmp = resolve;
+    resolve = null;
+    tmp({ data: e.data, ports: [] });
+  }
+
+  runTests({
+    clonableObjects: true,
+    transferableObjects: false,
+    send: function(what, ports) {
+      is(ports.length, 0, "No ports for this test!");
+      return new Promise(function(r, rr) {
+        resolve = r;
+        bc1.postMessage(what);
+      });
+    },
+
+    finished: function() {
+      onmessage = null;
+      next();
+    }
+  });
+}
+
+var tests = [
+  create_fileList,
+
+  test_windowToWindow,
+  test_windowToIframe,
+  test_windowToCrossOriginIframe,
+
+  test_broadcastChannel,
+];
+
+function next() {
+  if (!tests.length) {
+    SimpleTest.finish();
+    return;
+  }
+
+  var test = tests.shift();
+  test();
+}
+
+SimpleTest.waitForExplicitFinish();
+SpecialPowers.pushPrefEnv({"set": [["dom.messageChannel.enabled", true]]}, next);
+</script>
+</body>
+</html>
--- a/dom/broadcastchannel/BroadcastChannel.cpp
+++ b/dom/broadcastchannel/BroadcastChannel.cpp
@@ -3,17 +3,18 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "BroadcastChannel.h"
 #include "BroadcastChannelChild.h"
 #include "mozilla/dom/BroadcastChannelBinding.h"
 #include "mozilla/dom/Navigator.h"
-#include "mozilla/dom/StructuredCloneUtils.h"
+#include "mozilla/dom/File.h"
+#include "mozilla/dom/StructuredCloneHelper.h"
 #include "mozilla/ipc/BackgroundChild.h"
 #include "mozilla/ipc/BackgroundUtils.h"
 #include "mozilla/ipc/PBackgroundChild.h"
 #include "WorkerPrivate.h"
 #include "WorkerRunnable.h"
 
 #include "nsIDocument.h"
 #include "nsISupportsPrimitives.h"
@@ -25,30 +26,28 @@
 namespace mozilla {
 
 using namespace ipc;
 
 namespace dom {
 
 using namespace workers;
 
-class BroadcastChannelMessage final
+class BroadcastChannelMessage final : public StructuredCloneHelper
 {
 public:
   NS_INLINE_DECL_REFCOUNTING(BroadcastChannelMessage)
 
-  JSAutoStructuredCloneBuffer mBuffer;
-  StructuredCloneClosure mClosure;
-
   BroadcastChannelMessage()
-  { }
+    : StructuredCloneHelper(CloningSupported, TransferringNotSupported)
+  {}
 
 private:
   ~BroadcastChannelMessage()
-  { }
+  {}
 };
 
 namespace {
 
 nsIPrincipal*
 GetPrincipalFromWorkerPrivate(WorkerPrivate* aWorkerPrivate)
 {
   nsIPrincipal* principal = aWorkerPrivate->GetPrincipal();
@@ -161,23 +160,23 @@ public:
     MOZ_ASSERT(mActor);
     if (mActor->IsActorDestroyed()) {
       return NS_OK;
     }
 
     ClonedMessageData message;
 
     SerializedStructuredCloneBuffer& buffer = message.data();
-    buffer.data = mData->mBuffer.data();
-    buffer.dataLength = mData->mBuffer.nbytes();
+    buffer.data = mData->BufferData();
+    buffer.dataLength = mData->BufferSize();
 
     PBackgroundChild* backgroundManager = mActor->Manager();
     MOZ_ASSERT(backgroundManager);
 
-    const nsTArray<nsRefPtr<BlobImpl>>& blobImpls = mData->mClosure.mBlobImpls;
+    const nsTArray<nsRefPtr<BlobImpl>>& blobImpls = mData->ClonedBlobImpls();
 
     if (!blobImpls.IsEmpty()) {
       message.blobsChild().SetCapacity(blobImpls.Length());
 
       for (uint32_t i = 0, len = blobImpls.Length(); i < len; ++i) {
         PBlobChild* blobChild =
           BackgroundChild::GetOrCreateActorForBlobImpl(backgroundManager,
                                                        blobImpls[i]);
@@ -450,22 +449,22 @@ BroadcastChannel::PostMessage(JSContext*
 
 void
 BroadcastChannel::PostMessageInternal(JSContext* aCx,
                                       JS::Handle<JS::Value> aMessage,
                                       ErrorResult& aRv)
 {
   nsRefPtr<BroadcastChannelMessage> data = new BroadcastChannelMessage();
 
-  if (!WriteStructuredClone(aCx, aMessage, data->mBuffer, data->mClosure)) {
+  if (!data->Write(aCx, aMessage)) {
     aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
     return;
   }
 
-  const nsTArray<nsRefPtr<BlobImpl>>& blobImpls = data->mClosure.mBlobImpls;
+  const nsTArray<nsRefPtr<BlobImpl>>& blobImpls = data->ClonedBlobImpls();
   for (uint32_t i = 0, len = blobImpls.Length(); i < len; ++i) {
     if (!blobImpls[i]->MayBeClonedToOtherThreads()) {
       aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
       return;
     }
   }
 
   PostMessageData(data);
--- a/dom/broadcastchannel/BroadcastChannelChild.cpp
+++ b/dom/broadcastchannel/BroadcastChannelChild.cpp
@@ -6,17 +6,17 @@
 
 #include "BroadcastChannelChild.h"
 #include "BroadcastChannel.h"
 #include "jsapi.h"
 #include "mozilla/dom/ipc/BlobChild.h"
 #include "mozilla/dom/File.h"
 #include "mozilla/dom/MessageEvent.h"
 #include "mozilla/dom/MessageEventBinding.h"
-#include "mozilla/dom/StructuredCloneUtils.h"
+#include "mozilla/dom/StructuredCloneHelper.h"
 #include "mozilla/dom/WorkerPrivate.h"
 #include "mozilla/dom/WorkerScope.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/ipc/PBackgroundChild.h"
 #include "WorkerPrivate.h"
 
 namespace mozilla {
 
@@ -81,25 +81,25 @@ BroadcastChannelChild::RecvNotify(const 
   }
 
   if (!globalObject || !jsapi.Init(globalObject)) {
     NS_WARNING("Failed to initialize AutoJSAPI object.");
     return true;
   }
 
   JSContext* cx = jsapi.cx();
-
   const SerializedStructuredCloneBuffer& buffer = aData.data();
-  StructuredCloneData cloneData;
-  cloneData.mData = buffer.data;
-  cloneData.mDataLength = buffer.dataLength;
-  cloneData.mClosure.mBlobImpls.SwapElements(blobs);
+  StructuredCloneHelper cloneHelper(StructuredCloneHelper::CloningSupported,
+                                    StructuredCloneHelper::TransferringNotSupported);
 
   JS::Rooted<JS::Value> value(cx, JS::NullValue());
-  if (cloneData.mDataLength && !ReadStructuredClone(cx, cloneData, &value)) {
+  if (buffer.dataLength &&
+      !cloneHelper.ReadFromBuffer(mBC->GetParentObject(), cx,
+                                  buffer.data, buffer.dataLength, blobs,
+                                  &value)) {
     JS_ClearPendingException(cx);
     return false;
   }
 
   RootedDictionary<MessageEventInit> init(cx);
   init.mBubbles = false;
   init.mCancelable = false;
   init.mOrigin.Construct(mOrigin);
--- a/dom/canvas/test/webgl-mochitest/webgl-util.js
+++ b/dom/canvas/test/webgl-mochitest/webgl-util.js
@@ -69,22 +69,16 @@ WebGLUtil = (function() {
       var canvas = document.getElementById(canvasId);
 
       var gl = null;
       try {
         gl = canvas.getContext('webgl2');
       } catch(e) {}
 
       if (!gl) {
-        try {
-          gl = canvas.getContext('experimental-webgl2');
-        } catch(e) {}
-      }
-
-      if (!gl) {
         todo(false, 'WebGL2 is not supported');
         onFinished();
         return;
       }
 
       function errorFunc(str) {
         ok(false, 'Error: ' + str);
       }
--- a/dom/events/DataContainerEvent.cpp
+++ b/dom/events/DataContainerEvent.cpp
@@ -23,21 +23,21 @@ DataContainerEvent::DataContainerEvent(E
       doc->WarnOnceAbout(nsIDocument::eDataContainerEvent);
     }
   }
 }
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(DataContainerEvent)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(DataContainerEvent, Event)
-  tmp->mData.Clear();
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mData)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(DataContainerEvent, Event)
-  tmp->mData.EnumerateRead(TraverseEntry, &cb);
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mData)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_ADDREF_INHERITED(DataContainerEvent, Event)
 NS_IMPL_RELEASE_INHERITED(DataContainerEvent, Event)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(DataContainerEvent)
   NS_INTERFACE_MAP_ENTRY(nsIDOMDataContainerEvent)
 NS_INTERFACE_MAP_END_INHERITING(Event)
@@ -76,28 +76,16 @@ DataContainerEvent::SetData(JSContext* a
     nsContentUtils::XPConnect()->JSToVariant(aCx, aVal, getter_AddRefs(val));
   if (NS_FAILED(rv)) {
     aRv = rv;
     return;
   }
   aRv = SetData(aKey, val);
 }
 
-PLDHashOperator
-DataContainerEvent::TraverseEntry(const nsAString& aKey,
-                                  nsIVariant* aDataItem,
-                                  void* aUserArg)
-{
-  nsCycleCollectionTraversalCallback *cb =
-    static_cast<nsCycleCollectionTraversalCallback*>(aUserArg);
-  cb->NoteXPCOMChild(aDataItem);
-
-  return PL_DHASH_NEXT;
-}
-
 } // namespace dom
 } // namespace mozilla
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 nsresult
 NS_NewDOMDataContainerEvent(nsIDOMEvent** aInstancePtrResult,
--- a/dom/events/DataContainerEvent.h
+++ b/dom/events/DataContainerEvent.h
@@ -46,18 +46,15 @@ public:
 
   void SetData(JSContext* aCx, const nsAString& aKey,
                JS::Handle<JS::Value> aVal, ErrorResult& aRv);
 
 protected:
   ~DataContainerEvent() {}
 
 private:
-  static PLDHashOperator
-    TraverseEntry(const nsAString& aKey, nsIVariant* aDataItem, void* aUserArg);
-
   nsInterfaceHashtable<nsStringHashKey, nsIVariant> mData;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_DataContainerEvent_h_
--- a/dom/html/HTMLCanvasElement.cpp
+++ b/dom/html/HTMLCanvasElement.cpp
@@ -732,17 +732,17 @@ GetCanvasContextType(const nsAString& st
      *   canvas context types, they shall be treated as aliases.
      */
     *out_type = CanvasContextType::WebGL1;
     return true;
   }
 #endif
 
   if (WebGL2Context::IsSupported()) {
-    if (str.EqualsLiteral("experimental-webgl2")) {
+    if (str.EqualsLiteral("webgl2")) {
       *out_type = CanvasContextType::WebGL2;
       return true;
     }
   }
 
   return false;
 }
 
--- a/dom/html/HTMLFormControlsCollection.cpp
+++ b/dom/html/HTMLFormControlsCollection.cpp
@@ -384,38 +384,31 @@ HTMLFormControlsCollection::NamedGetter(
   }
   if (nsCOMPtr<RadioNodeList> nodelist = do_QueryInterface(item)) {
     aResult.SetValue().SetAsRadioNodeList() = nodelist;
     return;
   }
   MOZ_ASSERT_UNREACHABLE("Should only have Elements and NodeLists here.");
 }
 
-static PLDHashOperator
-CollectNames(const nsAString& aName,
-             nsISupports* /* unused */,
-             void* aClosure)
-{
-  static_cast<nsTArray<nsString>*>(aClosure)->AppendElement(aName);
-  return PL_DHASH_NEXT;
-}
-
 void
 HTMLFormControlsCollection::GetSupportedNames(unsigned aFlags,
                                               nsTArray<nsString>& aNames)
 {
   if (!(aFlags & JSITER_HIDDEN)) {
     return;
   }
 
   FlushPendingNotifications();
   // Just enumerate mNameLookupTable.  This won't guarantee order, but
   // that's OK, because the HTML5 spec doesn't define an order for
   // this enumeration.
-  mNameLookupTable.EnumerateRead(CollectNames, &aNames);
+  for (auto iter = mNameLookupTable.Iter(); !iter.Done(); iter.Next()) {
+    aNames.AppendElement(iter.Key());
+  }
 }
 
 /* virtual */ JSObject*
 HTMLFormControlsCollection::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return HTMLFormControlsCollectionBinding::Wrap(aCx, this, aGivenProto);
 }
 
--- a/dom/html/HTMLFormElement.cpp
+++ b/dom/html/HTMLFormElement.cpp
@@ -127,35 +127,24 @@ HTMLFormElement::~HTMLFormElement()
     mControls->DropFormReference();
   }
 
   Clear();
 }
 
 // nsISupports
 
-static PLDHashOperator
-ElementTraverser(const nsAString& key, HTMLInputElement* element,
-                 void* userArg)
-{
-  nsCycleCollectionTraversalCallback *cb =
-    static_cast<nsCycleCollectionTraversalCallback*>(userArg);
-
-  cb->NoteXPCOMChild(ToSupports(element));
-  return PL_DHASH_NEXT;
-}
-
 NS_IMPL_CYCLE_COLLECTION_CLASS(HTMLFormElement)
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(HTMLFormElement,
                                                   nsGenericHTMLElement)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mControls)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mImageNameLookupTable)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPastNameLookupTable)
-  tmp->mSelectedRadioButtons.EnumerateRead(ElementTraverser, &cb);
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSelectedRadioButtons)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(HTMLFormElement,
                                                 nsGenericHTMLElement)
   tmp->Clear();
   tmp->mExpandoAndGeneration.Unlink();
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -1701,68 +1701,42 @@ HTMLMediaElement::SetVolume(double aVolu
 
 NS_IMETHODIMP HTMLMediaElement::SetVolume(double aVolume)
 {
   ErrorResult rv;
   SetVolume(aVolume, rv);
   return rv.StealNSResult();
 }
 
-// Helper struct with arguments for our hash iterator.
-typedef struct MOZ_STACK_CLASS {
-  JSContext* cx;
-  JS::Handle<JSObject*> tags;
-  bool error;
-} MetadataIterCx;
-
-PLDHashOperator
-HTMLMediaElement::BuildObjectFromTags(nsCStringHashKey::KeyType aKey,
-                                      nsCString aValue,
-                                      void* aUserArg)
-{
-  MetadataIterCx* args = static_cast<MetadataIterCx*>(aUserArg);
-
-  nsString wideValue = NS_ConvertUTF8toUTF16(aValue);
-  JS::Rooted<JSString*> string(args->cx, JS_NewUCStringCopyZ(args->cx, wideValue.Data()));
-  if (!string) {
-    NS_WARNING("Failed to perform string copy");
-    args->error = true;
-    return PL_DHASH_STOP;
-  }
-  if (!JS_DefineProperty(args->cx, args->tags, aKey.Data(), string, JSPROP_ENUMERATE)) {
-    NS_WARNING("Failed to set metadata property");
-    args->error = true;
-    return PL_DHASH_STOP;
-  }
-
-  return PL_DHASH_NEXT;
-}
-
 void
 HTMLMediaElement::MozGetMetadata(JSContext* cx,
                                  JS::MutableHandle<JSObject*> aRetval,
                                  ErrorResult& aRv)
 {
   if (mReadyState < nsIDOMHTMLMediaElement::HAVE_METADATA) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
 
   JS::Rooted<JSObject*> tags(cx, JS_NewPlainObject(cx));
   if (!tags) {
     aRv.Throw(NS_ERROR_FAILURE);
     return;
   }
   if (mTags) {
-    MetadataIterCx iter = {cx, tags, false};
-    mTags->EnumerateRead(BuildObjectFromTags, static_cast<void*>(&iter));
-    if (iter.error) {
-      NS_WARNING("couldn't create metadata object!");
-      aRv.Throw(NS_ERROR_FAILURE);
-      return;
+    for (auto iter = mTags->ConstIter(); !iter.Done(); iter.Next()) {
+      nsString wideValue = NS_ConvertUTF8toUTF16(iter.UserData());
+      JS::Rooted<JSString*> string(cx,
+                                   JS_NewUCStringCopyZ(cx, wideValue.Data()));
+      if (!string || !JS_DefineProperty(cx, tags, iter.Key().Data(), string,
+                                        JSPROP_ENUMERATE)) {
+        NS_WARNING("couldn't create metadata object!");
+        aRv.Throw(NS_ERROR_FAILURE);
+        return;
+      }
     }
   }
 
   aRetval.set(tags);
 }
 
 NS_IMETHODIMP
 HTMLMediaElement::MozGetMetadata(JSContext* cx, JS::MutableHandle<JS::Value> aValue)
--- a/dom/html/HTMLPropertiesCollection.cpp
+++ b/dom/html/HTMLPropertiesCollection.cpp
@@ -68,34 +68,28 @@ NS_INTERFACE_TABLE_HEAD(HTMLPropertiesCo
                        nsIHTMLCollection,
                        nsIMutationObserver)
     NS_INTERFACE_TABLE_TO_MAP_SEGUE_CYCLE_COLLECTION(HTMLPropertiesCollection)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(HTMLPropertiesCollection)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(HTMLPropertiesCollection)
 
-
-static PLDHashOperator
-SetPropertyListDocument(const nsAString& aKey, PropertyNodeList* aEntry, void* aData)
-{
-  aEntry->SetDocument(static_cast<nsIDocument*>(aData));
-  return PL_DHASH_NEXT;
-}
-
 void
 HTMLPropertiesCollection::SetDocument(nsIDocument* aDocument) {
   if (mDoc) {
     mDoc->RemoveMutationObserver(this);
   }
   mDoc = aDocument;
   if (mDoc) {
     mDoc->AddMutationObserver(this);
   }
-  mNamedItemEntries.EnumerateRead(SetPropertyListDocument, aDocument);
+  for (auto iter = mNamedItemEntries.Iter(); !iter.Done(); iter.Next()) {
+    iter.UserData()->SetDocument(aDocument);
+  }
   mIsDirty = true;
 }
 
 JSObject*
 HTMLPropertiesCollection::WrapObject(JSContext* cx, JS::Handle<JSObject*> aGivenProto)
 {
   return HTMLPropertiesCollectionBinding::Wrap(cx, this, aGivenProto);
 }
@@ -186,35 +180,30 @@ HTMLPropertiesCollection::ContentRemoved
                                          nsIContent* aContainer,
                                          nsIContent* aChild,
                                          int32_t aIndexInContainer,
                                          nsIContent* aPreviousSibling)
 {
   mIsDirty = true;
 }
 
-static PLDHashOperator
-MarkDirty(const nsAString& aKey, PropertyNodeList* aEntry, void* aData)
-{
-  aEntry->SetDirty();
-  return PL_DHASH_NEXT;
-}
-
 void
 HTMLPropertiesCollection::EnsureFresh()
 {
   if (mDoc && !mIsDirty) {
     return;
   }
   mIsDirty = false;
 
   mProperties.Clear();
   mNames->Clear();
   // We don't clear NamedItemEntries because the PropertyNodeLists must be live.
-  mNamedItemEntries.EnumerateRead(MarkDirty, nullptr);
+  for (auto iter = mNamedItemEntries.Iter(); !iter.Done(); iter.Next()) {
+    iter.UserData()->SetDirty();
+  }
   if (!mRoot->HasAttr(kNameSpaceID_None, nsGkAtoms::itemscope)) {
     return;
   }
 
   CrawlProperties();
   TreeOrderComparator comparator;
   mProperties.Sort(comparator);
 
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -1904,17 +1904,17 @@ ContentChild::RecvSetConnectivity(const 
 void
 ContentChild::ActorDestroy(ActorDestroyReason why)
 {
     if (AbnormalShutdown == why) {
         NS_WARNING("shutting down early because of crash!");
         QuickExit();
     }
 
-#ifndef DEBUG
+#if !defined(DEBUG) && !defined(MOZ_ASAN)
     // In release builds, there's no point in the content process
     // going through the full XPCOM shutdown path, because it doesn't
     // keep persistent state.
     QuickExit();
 #endif
 
     if (sFirstIdleTask) {
         sFirstIdleTask->Cancel();
--- a/dom/media/AudioSegment.h
+++ b/dom/media/AudioSegment.h
@@ -143,17 +143,24 @@ struct AudioChunk {
           return false;
         }
       }
     }
 
     return true;
   }
 
-  int ChannelCount() const { return mChannelData.Length(); }
+  size_t ChannelCount() const { return mChannelData.Length(); }
+
+  float* ChannelFloatsForWrite(size_t aChannel)
+  {
+    MOZ_ASSERT(mBufferFormat == AUDIO_FORMAT_FLOAT32);
+    MOZ_ASSERT(!mBuffer->IsShared());
+    return static_cast<float*>(const_cast<void*>(mChannelData[aChannel]));
+  }
 
   bool IsMuted() const { return mVolume == 0.0f; }
 
   size_t SizeOfExcludingThisIfUnshared(MallocSizeOf aMallocSizeOf) const
   {
     return SizeOfExcludingThis(aMallocSizeOf, true);
   }
 
--- a/dom/media/AudioSink.cpp
+++ b/dom/media/AudioSink.cpp
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "AudioSink.h"
 #include "AudioStream.h"
 #include "MediaQueue.h"
 #include "VideoUtils.h"
 
 #include "mozilla/CheckedInt.h"
+#include "mozilla/DebugOnly.h"
 
 namespace mozilla {
 
 extern PRLogModuleInfo* gMediaDecoderLog;
 #define SINK_LOG(msg, ...) \
   MOZ_LOG(gMediaDecoderLog, LogLevel::Debug, ("AudioSink=%p " msg, this, ##__VA_ARGS__))
 #define SINK_LOG_V(msg, ...) \
   MOZ_LOG(gMediaDecoderLog, LogLevel::Verbose, ("AudioSink=%p " msg, this, ##__VA_ARGS__))
@@ -23,52 +24,90 @@ extern PRLogModuleInfo* gMediaDecoderLog
 static const int64_t AUDIO_FUZZ_FRAMES = 1;
 
 AudioSink::AudioSink(MediaQueue<AudioData>& aAudioQueue,
                      int64_t aStartTime,
                      const AudioInfo& aInfo,
                      dom::AudioChannel aChannel)
   : mAudioQueue(aAudioQueue)
   , mMonitor("AudioSink::mMonitor")
+  , mState(AUDIOSINK_STATE_INIT)
+  , mAudioLoopScheduled(false)
   , mStartTime(aStartTime)
   , mWritten(0)
   , mLastGoodPosition(0)
   , mInfo(aInfo)
   , mChannel(aChannel)
   , mVolume(1.0)
   , mPlaybackRate(1.0)
   , mPreservesPitch(false)
   , mStopAudioThread(false)
   , mSetVolume(false)
   , mSetPlaybackRate(false)
   , mSetPreservesPitch(false)
   , mPlaying(true)
 {
 }
 
+void
+AudioSink::SetState(State aState)
+{
+  AssertOnAudioThread();
+  mPendingState = Some(aState);
+}
+
+void
+AudioSink::DispatchTask(already_AddRefed<nsIRunnable>&& event)
+{
+  DebugOnly<nsresult> rv = mThread->Dispatch(Move(event), NS_DISPATCH_NORMAL);
+  // There isn't much we can do if Dispatch() fails.
+  // Just assert it to keep things simple.
+  MOZ_ASSERT(NS_SUCCEEDED(rv));
+}
+
+void
+AudioSink::ScheduleNextLoop()
+{
+  AssertOnAudioThread();
+  if (mAudioLoopScheduled) {
+    return;
+  }
+  mAudioLoopScheduled = true;
+  nsCOMPtr<nsIRunnable> r = NS_NewRunnableMethod(this, &AudioSink::AudioLoop);
+  DispatchTask(r.forget());
+}
+
+void
+AudioSink::ScheduleNextLoopCrossThread()
+{
+  AssertNotOnAudioThread();
+  nsRefPtr<AudioSink> self = this;
+  nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction([self] () {
+    // Do nothing if there is already a pending task waiting for its turn.
+    if (!self->mAudioLoopScheduled) {
+      self->AudioLoop();
+    }
+  });
+  DispatchTask(r.forget());
+}
+
 nsRefPtr<GenericPromise>
 AudioSink::Init()
 {
   nsRefPtr<GenericPromise> p = mEndPromise.Ensure(__func__);
   nsresult rv = NS_NewNamedThread("Media Audio",
                                   getter_AddRefs(mThread),
                                   nullptr,
                                   MEDIA_THREAD_STACK_SIZE);
   if (NS_FAILED(rv)) {
     mEndPromise.Reject(rv, __func__);
     return p;
   }
 
-  nsCOMPtr<nsIRunnable> event = NS_NewRunnableMethod(this, &AudioSink::AudioLoop);
-  rv =  mThread->Dispatch(event, NS_DISPATCH_NORMAL);
-  if (NS_FAILED(rv)) {
-    mEndPromise.Reject(rv, __func__);
-    return p;
-  }
-
+  ScheduleNextLoopCrossThread();
   return p;
 }
 
 int64_t
 AudioSink::GetPosition()
 {
   ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
 
@@ -94,26 +133,32 @@ AudioSink::HasUnplayedFrames()
 void
 AudioSink::Shutdown()
 {
   ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
   mStopAudioThread = true;
   if (mAudioStream) {
     mAudioStream->Cancel();
   }
-  GetReentrantMonitor().NotifyAll();
+  ScheduleNextLoopCrossThread();
 
   // Exit the monitor so audio loop can enter the monitor and finish its job.
   ReentrantMonitorAutoExit exit(GetReentrantMonitor());
   mThread->Shutdown();
   mThread = nullptr;
   if (mAudioStream) {
     mAudioStream->Shutdown();
     mAudioStream = nullptr;
   }
+
+  // Should've reached the final state after shutdown.
+  MOZ_ASSERT(mState == AUDIOSINK_STATE_SHUTDOWN ||
+             mState == AUDIOSINK_STATE_ERROR);
+  // Should have no pending state change.
+  MOZ_ASSERT(mPendingState.isNothing());
 }
 
 void
 AudioSink::SetVolume(double aVolume)
 {
   ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
   mVolume = aVolume;
   mSetVolume = true;
@@ -136,83 +181,23 @@ AudioSink::SetPreservesPitch(bool aPrese
   mSetPreservesPitch = true;
 }
 
 void
 AudioSink::SetPlaying(bool aPlaying)
 {
   ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
   mPlaying = aPlaying;
-  GetReentrantMonitor().NotifyAll();
+  ScheduleNextLoopCrossThread();
 }
 
 void
 AudioSink::NotifyData()
 {
-  ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
-  GetReentrantMonitor().NotifyAll();
-}
-
-void
-AudioSink::AudioLoop()
-{
-  AssertOnAudioThread();
-  SINK_LOG("AudioLoop started");
-
-  nsresult rv = InitializeAudioStream();
-  if (NS_FAILED(rv)) {
-    NS_WARNING("Initializing AudioStream failed.");
-    mEndPromise.Reject(rv, __func__);
-    return;
-  }
-
-  while (1) {
-    {
-      ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
-      WaitForAudioToPlay();
-      if (!IsPlaybackContinuing()) {
-        break;
-      }
-    }
-    // See if there's a gap in the audio. If there is, push silence into the
-    // audio hardware, so we can play across the gap.
-    // Calculate the timestamp of the next chunk of audio in numbers of
-    // samples.
-    NS_ASSERTION(AudioQueue().GetSize() > 0, "Should have data to play");
-    CheckedInt64 sampleTime = UsecsToFrames(AudioQueue().PeekFront()->mTime, mInfo.mRate);
-
-    // Calculate the number of frames that have been pushed onto the audio hardware.
-    CheckedInt64 playedFrames = UsecsToFrames(mStartTime, mInfo.mRate) +
-                                static_cast<int64_t>(mWritten);
-
-    CheckedInt64 missingFrames = sampleTime - playedFrames;
-    if (!missingFrames.isValid() || !sampleTime.isValid()) {
-      NS_WARNING("Int overflow adding in AudioLoop");
-      break;
-    }
-
-    if (missingFrames.value() > AUDIO_FUZZ_FRAMES) {
-      // The next audio chunk begins some time after the end of the last chunk
-      // we pushed to the audio hardware. We must push silence into the audio
-      // hardware so that the next audio chunk begins playback at the correct
-      // time.
-      missingFrames = std::min<int64_t>(UINT32_MAX, missingFrames.value());
-      mWritten += PlaySilence(static_cast<uint32_t>(missingFrames.value()));
-    } else {
-      mWritten += PlayFromAudioQueue();
-    }
-  }
-  ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
-  MOZ_ASSERT(mStopAudioThread || AudioQueue().AtEndOfStream());
-  if (!mStopAudioThread && mPlaying) {
-    Drain();
-  }
-  SINK_LOG("AudioLoop complete");
-  Cleanup();
-  SINK_LOG("AudioLoop exit");
+  ScheduleNextLoopCrossThread();
 }
 
 nsresult
 AudioSink::InitializeAudioStream()
 {
   // AudioStream initialization can block for extended periods in unusual
   // circumstances, so we take care to drop the decoder monitor while
   // initializing.
@@ -256,28 +241,29 @@ AudioSink::Cleanup()
 }
 
 bool
 AudioSink::ExpectMoreAudioData()
 {
   return AudioQueue().GetSize() == 0 && !AudioQueue().IsFinished();
 }
 
-void
-AudioSink::WaitForAudioToPlay()
+bool
+AudioSink::WaitingForAudioToPlay()
 {
-  // Wait while we're not playing, and we're not shutting down, or we're
+  // Return true if we're not playing, and we're not shutting down, or we're
   // playing and we've got no audio to play.
   AssertCurrentThreadInMonitor();
-  while (!mStopAudioThread && (!mPlaying || ExpectMoreAudioData())) {
+  if (!mStopAudioThread && (!mPlaying || ExpectMoreAudioData())) {
     if (!mPlaying && !mAudioStream->IsPaused()) {
       mAudioStream->Pause();
     }
-    GetReentrantMonitor().Wait();
+    return true;
   }
+  return false;
 }
 
 bool
 AudioSink::IsPlaybackContinuing()
 {
   AssertCurrentThreadInMonitor();
   if (mPlaying && mAudioStream->IsPaused()) {
     mAudioStream->Resume();
@@ -289,16 +275,129 @@ AudioSink::IsPlaybackContinuing()
     return false;
   }
 
   UpdateStreamSettings();
 
   return true;
 }
 
+void
+AudioSink::AudioLoop()
+{
+  AssertOnAudioThread();
+  mAudioLoopScheduled = false;
+
+  switch (mState) {
+    case AUDIOSINK_STATE_INIT: {
+      SINK_LOG("AudioLoop started");
+      nsresult rv = InitializeAudioStream();
+      if (NS_FAILED(rv)) {
+        NS_WARNING("Initializing AudioStream failed.");
+        mEndPromise.Reject(rv, __func__);
+        SetState(AUDIOSINK_STATE_ERROR);
+        break;
+      }
+      SetState(AUDIOSINK_STATE_PLAYING);
+      break;
+    }
+
+    case AUDIOSINK_STATE_PLAYING: {
+      {
+        ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
+        if (WaitingForAudioToPlay()) {
+          // NotifyData() will schedule next loop.
+          break;
+        }
+        if (!IsPlaybackContinuing()) {
+          SetState(AUDIOSINK_STATE_COMPLETE);
+          break;
+        }
+      }
+      if (!PlayAudio()) {
+        SetState(AUDIOSINK_STATE_COMPLETE);
+        break;
+      }
+      // Schedule next loop to play next sample.
+      ScheduleNextLoop();
+      break;
+    }
+
+    case AUDIOSINK_STATE_COMPLETE: {
+      FinishAudioLoop();
+      SetState(AUDIOSINK_STATE_SHUTDOWN);
+      break;
+    }
+
+    case AUDIOSINK_STATE_SHUTDOWN:
+      break;
+
+    case AUDIOSINK_STATE_ERROR:
+      break;
+  } // end of switch
+
+  // We want mState to stay stable during AudioLoop to keep things simple.
+  // Therefore, we only do state transition at the end of AudioLoop.
+  if (mPendingState.isSome()) {
+    MOZ_ASSERT(mState != mPendingState.ref());
+    SINK_LOG("change mState, %d -> %d", mState, mPendingState.ref());
+    mState = mPendingState.ref();
+    mPendingState.reset();
+    // Schedule next loop when state changes.
+    ScheduleNextLoop();
+  }
+}
+
+bool
+AudioSink::PlayAudio()
+{
+  // See if there's a gap in the audio. If there is, push silence into the
+  // audio hardware, so we can play across the gap.
+  // Calculate the timestamp of the next chunk of audio in numbers of
+  // samples.
+  NS_ASSERTION(AudioQueue().GetSize() > 0, "Should have data to play");
+  CheckedInt64 sampleTime = UsecsToFrames(AudioQueue().PeekFront()->mTime, mInfo.mRate);
+
+  // Calculate the number of frames that have been pushed onto the audio hardware.
+  CheckedInt64 playedFrames = UsecsToFrames(mStartTime, mInfo.mRate) +
+                              static_cast<int64_t>(mWritten);
+
+  CheckedInt64 missingFrames = sampleTime - playedFrames;
+  if (!missingFrames.isValid() || !sampleTime.isValid()) {
+    NS_WARNING("Int overflow adding in AudioLoop");
+    return false;
+  }
+
+  if (missingFrames.value() > AUDIO_FUZZ_FRAMES) {
+    // The next audio chunk begins some time after the end of the last chunk
+    // we pushed to the audio hardware. We must push silence into the audio
+    // hardware so that the next audio chunk begins playback at the correct
+    // time.
+    missingFrames = std::min<int64_t>(UINT32_MAX, missingFrames.value());
+    mWritten += PlaySilence(static_cast<uint32_t>(missingFrames.value()));
+  } else {
+    mWritten += PlayFromAudioQueue();
+  }
+
+  return true;
+}
+
+void
+AudioSink::FinishAudioLoop()
+{
+  ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
+  MOZ_ASSERT(mStopAudioThread || AudioQueue().AtEndOfStream());
+  if (!mStopAudioThread && mPlaying) {
+    Drain();
+  }
+  SINK_LOG("AudioLoop complete");
+  Cleanup();
+  SINK_LOG("AudioLoop exit");
+}
+
 uint32_t
 AudioSink::PlaySilence(uint32_t aFrames)
 {
   // Maximum number of bytes we'll allocate and write at once to the audio
   // hardware when the audio stream contains missing frames and we're
   // writing silence in order to fill the gap. We limit our silence-writes
   // to 32KB in order to avoid allocating an impossibly large chunk of
   // memory if we encounter a large chunk of silence.
@@ -406,9 +505,15 @@ AudioSink::GetEndTime() const
 }
 
 void
 AudioSink::AssertOnAudioThread()
 {
   MOZ_ASSERT(NS_GetCurrentThread() == mThread);
 }
 
+void
+AudioSink::AssertNotOnAudioThread()
+{
+  MOZ_ASSERT(NS_GetCurrentThread() != mThread);
+}
+
 } // namespace mozilla
--- a/dom/media/AudioSink.h
+++ b/dom/media/AudioSink.h
@@ -7,16 +7,17 @@
 #define AudioSink_h__
 
 #include "MediaInfo.h"
 #include "nsRefPtr.h"
 #include "nsISupportsImpl.h"
 
 #include "mozilla/dom/AudioChannelBinding.h"
 #include "mozilla/Atomics.h"
+#include "mozilla/Maybe.h"
 #include "mozilla/MozPromise.h"
 #include "mozilla/ReentrantMonitor.h"
 
 namespace mozilla {
 
 class AudioData;
 class AudioStream;
 template <class T> class MediaQueue;
@@ -52,40 +53,59 @@ public:
   void SetPreservesPitch(bool aPreservesPitch);
   void SetPlaying(bool aPlaying);
 
   // Wake up the audio loop if it is waiting for data to play or the audio
   // queue is finished.
   void NotifyData();
 
 private:
+  enum State {
+    AUDIOSINK_STATE_INIT,
+    AUDIOSINK_STATE_PLAYING,
+    AUDIOSINK_STATE_COMPLETE,
+    AUDIOSINK_STATE_SHUTDOWN,
+    AUDIOSINK_STATE_ERROR
+  };
+
   ~AudioSink() {}
 
+  void DispatchTask(already_AddRefed<nsIRunnable>&& event);
+  void SetState(State aState);
+  void ScheduleNextLoop();
+  void ScheduleNextLoopCrossThread();
+
   // The main loop for the audio thread. Sent to the thread as
   // an nsRunnableMethod. This continually does blocking writes to
   // to audio stream to play audio data.
   void AudioLoop();
 
   // Allocate and initialize mAudioStream.  Returns NS_OK on success.
   nsresult InitializeAudioStream();
 
   void Drain();
 
   void Cleanup();
 
   bool ExpectMoreAudioData();
 
-  // Wait on the decoder monitor until playback is ready or the sink is told to shut down.
-  void WaitForAudioToPlay();
+  // Return true if playback is not ready and the sink is not told to shut down.
+  bool WaitingForAudioToPlay();
 
   // Check if the sink has been told to shut down, resuming mAudioStream if
   // not.  Returns true if processing should continue, false if AudioLoop
   // should shutdown.
   bool IsPlaybackContinuing();
 
+  // Write audio samples or silence to the audio hardware.
+  // Return false if any error. Called on the audio thread.
+  bool PlayAudio();
+
+  void FinishAudioLoop();
+
   // Write aFrames of audio frames of silence to the audio hardware. Returns
   // the number of frames actually written. The write size is capped at
   // SILENCE_BYTES_CHUNK (32kB), so must be called in a loop to write the
   // desired number of frames. This ensures that the playback position
   // advances smoothly, and guarantees that we don't try to allocate an
   // impossibly large chunk of memory in order to play back silence. Called
   // on the audio thread.
   uint32_t PlaySilence(uint32_t aFrames);
@@ -109,20 +129,26 @@ private:
     return mMonitor;
   }
 
   void AssertCurrentThreadInMonitor() const {
     GetReentrantMonitor().AssertCurrentThreadIn();
   }
 
   void AssertOnAudioThread();
+  void AssertNotOnAudioThread();
 
   MediaQueue<AudioData>& mAudioQueue;
   mutable ReentrantMonitor mMonitor;
 
+  // There members are accessed on the audio thread only.
+  State mState;
+  Maybe<State> mPendingState;
+  bool mAudioLoopScheduled;
+
   // Thread for pushing audio onto the audio hardware.
   // The "audio push thread".
   nsCOMPtr<nsIThread> mThread;
 
   // The audio stream resource. Used on the state machine, and audio threads.
   // This is created and destroyed on the audio thread, while holding the
   // decoder monitor, so if this is used off the audio thread, you must
   // first acquire the decoder monitor and check that it is non-null.
--- a/dom/media/MediaFormatReader.cpp
+++ b/dom/media/MediaFormatReader.cpp
@@ -499,21 +499,25 @@ MediaFormatReader::GetDecoderData(TrackT
   }
   return mVideo;
 }
 
 void
 MediaFormatReader::DisableHardwareAcceleration()
 {
   MOZ_ASSERT(OnTaskQueue());
-  if (HasVideo() && mSharedDecoderManager) {
-    mSharedDecoderManager->DisableHardwareAcceleration();
-
-    if (!mSharedDecoderManager->Recreate(mInfo.mVideo)) {
-      mVideo.mError = true;
+  if (HasVideo()) {
+    mPlatform->DisableHardwareAcceleration();
+    Flush(TrackInfo::kVideoTrack);
+    mVideo.mDecoder->Shutdown();
+    mVideo.mDecoder = nullptr;
+    if (!EnsureDecodersSetup()) {
+      LOG("Unable to re-create decoder, aborting");
+      NotifyError(TrackInfo::kVideoTrack);
+      return;
     }
     ScheduleUpdate(TrackInfo::kVideoTrack);
   }
 }
 
 bool
 MediaFormatReader::ShouldSkip(bool aSkipToNextKeyframe, media::TimeUnit aTimeThreshold)
 {
--- a/dom/media/mediasource/TrackBuffersManager.cpp
+++ b/dom/media/mediasource/TrackBuffersManager.cpp
@@ -1137,31 +1137,31 @@ TrackBuffersManager::CompleteCodedFrameP
   ProcessFrames(mVideoTracks.mQueuedSamples, mVideoTracks);
   mVideoTracks.mQueuedSamples.Clear();
 
 #if defined(DEBUG)
   if (HasVideo()) {
     const auto& track = mVideoTracks.mBuffers.LastElement();
     MOZ_ASSERT(track.IsEmpty() || track[0]->mKeyframe);
     for (uint32_t i = 1; i < track.Length(); i++) {
-      MOZ_ASSERT((track[i-1]->mTrackInfo->GetID() == track[i]->mTrackInfo->GetID() && track[i-1]->mTimecode < track[i]->mTimecode) ||
+      MOZ_ASSERT((track[i-1]->mTrackInfo->GetID() == track[i]->mTrackInfo->GetID() && track[i-1]->mTimecode <= track[i]->mTimecode) ||
                  track[i]->mKeyframe);
     }
   }
 #endif
 
   ProcessFrames(mAudioTracks.mQueuedSamples, mAudioTracks);
   mAudioTracks.mQueuedSamples.Clear();
 
 #if defined(DEBUG)
   if (HasAudio()) {
     const auto& track = mAudioTracks.mBuffers.LastElement();
     MOZ_ASSERT(track.IsEmpty() || track[0]->mKeyframe);
     for (uint32_t i = 1; i < track.Length(); i++) {
-      MOZ_ASSERT((track[i-1]->mTrackInfo->GetID() == track[i]->mTrackInfo->GetID() && track[i-1]->mTimecode < track[i]->mTimecode) ||
+      MOZ_ASSERT((track[i-1]->mTrackInfo->GetID() == track[i]->mTrackInfo->GetID() && track[i-1]->mTimecode <= track[i]->mTimecode) ||
                  track[i]->mKeyframe);
     }
   }
 #endif
 
   UpdateBufferedRanges();
 
   // Update our reported total size.
--- a/dom/media/mediasource/test/mochitest.ini
+++ b/dom/media/mediasource/test/mochitest.ini
@@ -76,16 +76,18 @@ skip-if = ((os == "win" && os_version ==
 [test_SeekableBeforeEndOfStream.html]
 skip-if = true # bug 1182946
 [test_SeekableBeforeEndOfStream_mp4.html]
 skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+
 [test_SeekableBeforeEndOfStreamSplit.html]
 skip-if = true # bug 1182946
 [test_SeekableBeforeEndOfStreamSplit_mp4.html]
 skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+
+[test_SeekNoData_mp4.html]
+skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+
 [test_SeekTwice_mp4.html]
 skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+
 [test_SetModeThrows.html]
 skip-if = true # bug 1182946
 [test_SplitAppendDelay.html]
 skip-if = true # bug 1182946
 [test_SplitAppendDelay_mp4.html]
 skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+
new file mode 100644
--- /dev/null
+++ b/dom/media/mediasource/test/test_SeekNoData_mp4.html
@@ -0,0 +1,55 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>MSE: basic functionality</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="mediasource.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+SimpleTest.waitForExplicitFinish();
+
+// Avoid making trouble for people who fix rounding bugs.
+function fuzzyEquals(a, b) {
+  return Math.abs(a - b) < 0.01;
+}
+
+runWithMSE(function(ms, el) {
+  el.controls = true;
+  once(ms, 'sourceopen').then(function() {
+    ok(true, "Receive a sourceopen event");
+    var audiosb = ms.addSourceBuffer("audio/mp4");
+    var videosb = ms.addSourceBuffer("video/mp4");
+    fetchAndLoad(audiosb, 'bipbop/bipbop_audio', ['init'], '.mp4')
+    .then(fetchAndLoad.bind(null, videosb, 'bipbop/bipbop_video', ['init'], '.mp4'))
+    .then(once.bind(null, el, 'loadedmetadata'))
+    .then(function() {
+      var p = once(el, 'seeking');
+      el.play();
+      el.currentTime = 5;
+      return p;
+    })
+    .then(function() {
+       ok(true, "Got seeking event");
+       var promises = [];
+       promises.push(once(el, 'seeked'));
+       promises.push(fetchAndLoad(audiosb, 'bipbop/bipbop_audio', range(5, 9), '.m4s'));
+       promises.push(fetchAndLoad(videosb, 'bipbop/bipbop_video', range(6, 10), '.m4s'));
+       return Promise.all(promises);
+    })
+    .then(function() {
+      ok(true, "Got seeked event");
+      ok(el.currentTime >= 5, "Time >= 5");
+      once(el, 'ended').then(SimpleTest.finish.bind(SimpleTest));
+      ms.endOfStream();
+    });
+  });
+});
+
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/media/test/crashtests/1012609.html
@@ -0,0 +1,9 @@
+<script>
+try{var r0=new AudioContext();}catch(e){}
+try{var r32=r0.createOscillator();}catch(e){}
+try{var r58=r0.createPeriodicWave(new Float32Array(1997),new Float32Array(1997));}catch(e){}
+try{r32.start(0);}catch(e){}
+try{r32.setPeriodicWave(r58);}catch(e){}
+try{r32.frequency.value=-1;}catch(e){}
+</script>
+
new file mode 100644
--- /dev/null
+++ b/dom/media/test/crashtests/1020205.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<script>
+var context = new window.AudioContext();
+var source = context.createBufferSource();
+source.connect(context.destination);
+source.playbackRate.value = 0.01;
+source.buffer = context.createBuffer(2, 32, context.sampleRate);
+source.start(0);
+
+setTimeout(
+    function() {
+        source.buffer = context.createBuffer(1, 10, context.sampleRate);
+        source.playbackRate.value = 1;
+        source.onended =
+            function() {
+                document.documentElement.removeAttribute("class");
+            };
+    }, 0);
+
+</script>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/media/test/crashtests/1041466.html
@@ -0,0 +1,21 @@
+<html>
+<script>
+try{var Context1= new (window.webkitAudioContext || window.AudioContext)()}catch(e){}
+try{var Delay0=Context1.createDelay();}catch(e){}
+try{var ScriptProcessor0=Context1.createScriptProcessor(512,26,7);}catch(e){}
+try{var ChannelSplitter0=Context1.createChannelSplitter(91);}catch(e){}
+try{var Gain1=Context1.createGain();}catch(e){}
+try{var WaveShaper0=Context1.createWaveShaper();}catch(e){}
+try{var Analyser1=Context1.createAnalyser();}catch(e){}
+try{Gain1.connect(Delay0);}catch(e){}
+try{Analyser1.connect(BiquadFilter0);}catch(e){}
+try{Gain1.connect(Context1.destination);}catch(e){}
+try{WaveShaper0.connect(ScriptProcessor0);}catch(e){}
+try{ChannelSplitter0.connect(BiquadFilter1);}catch(e){}
+try{Delay0.connect(Gain1);}catch(e){}
+try{ScriptProcessor0.connect(Context1.destination);}catch(e){}
+try{WaveShaper0.connect(Gain1);}catch(e){}
+try{WaveShaper0.connect(ChannelSplitter0);}catch(e){}
+try{ScriptProcessor0.connect(WaveShaper0);}catch(e){}
+</script>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/media/test/crashtests/1045650.html
@@ -0,0 +1,18 @@
+<html><body><script>
+
+var r0=new AudioContext();
+
+var splitter=r0.createChannelSplitter();
+var delay=r0.createDelay();
+var scriptp=r0.createScriptProcessor();
+var cmerger=r0.createChannelMerger();
+var gain=r0.createGain();
+
+splitter.connect(delay,2);
+delay.connect(scriptp);
+scriptp.connect(cmerger);
+cmerger.connect(splitter);
+gain.connect(gain);
+gain.connect(cmerger);
+
+</script></body></html>
new file mode 100644
--- /dev/null
+++ b/dom/media/test/crashtests/analyser-channels-1.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<script>
+  var context = new window.OfflineAudioContext(1, 256, 48000);
+  var analyser = context.createAnalyser();
+  analyser.channelCount = 2;
+  analyser.channelCountMode = "explicit";
+  analyser.fftSize = 32;
+  var source = context.createOscillator();
+  source.connect(analyser);
+  source.start(0);
+  context.startRendering().
+    then(function() {
+      document.documentElement.removeAttribute("class");
+    });
+</script>
--- a/dom/media/test/crashtests/crashtests.list
+++ b/dom/media/test/crashtests/crashtests.list
@@ -62,18 +62,23 @@ load 925619-2.html
 load 926619.html
 load 933151.html
 load 933156.html
 load 944851.html
 load 952756.html
 load 966636.html
 load 986901.html
 load 990794.html
+load 1012609.html
 load 1015662.html
+load 1020205.html
+load 1041466.html
+load 1045650.html
 skip-if(Android||B2G) test-pref(media.navigator.permission.disabled,true) load 1028458.html # bug 1048863
+load analyser-channels-1.html
 load buffer-source-ended-1.html
 load doppler-1.html
 HTTP load media-element-source-seek-1.html
 load offline-buffer-source-ended-1.html
 load oscillator-ended-1.html
 load oscillator-ended-2.html
 load 1080986.html
 load 1158427.html
--- a/dom/media/webaudio/AnalyserNode.cpp
+++ b/dom/media/webaudio/AnalyserNode.cpp
@@ -7,16 +7,24 @@
 #include "mozilla/dom/AnalyserNode.h"
 #include "mozilla/dom/AnalyserNodeBinding.h"
 #include "AudioNodeEngine.h"
 #include "AudioNodeStream.h"
 #include "mozilla/Mutex.h"
 #include "mozilla/PodOperations.h"
 
 namespace mozilla {
+
+static const uint32_t MAX_FFT_SIZE = 32768;
+static const size_t CHUNK_COUNT = MAX_FFT_SIZE >> WEBAUDIO_BLOCK_SIZE_BITS;
+static_assert(MAX_FFT_SIZE == CHUNK_COUNT * WEBAUDIO_BLOCK_SIZE,
+              "MAX_FFT_SIZE must be a multiple of WEBAUDIO_BLOCK_SIZE");
+static_assert((CHUNK_COUNT & (CHUNK_COUNT - 1)) == 0,
+              "CHUNK_COUNT must be power of 2 for remainder behavior");
+
 namespace dom {
 
 NS_IMPL_ISUPPORTS_INHERITED0(AnalyserNode, AudioNode)
 
 class AnalyserNodeEngine final : public AudioNodeEngine
 {
   class TransferBuffer final : public nsRunnable
   {
@@ -52,30 +60,17 @@ public:
 
   virtual void ProcessBlock(AudioNodeStream* aStream,
                             const AudioChunk& aInput,
                             AudioChunk* aOutput,
                             bool* aFinished) override
   {
     *aOutput = aInput;
 
-    // If the input is silent, we sill need to send a silent buffer
-    if (aOutput->IsNull()) {
-      AllocateAudioBlock(1, aOutput);
-      float* samples =
-        static_cast<float*>(const_cast<void*>(aOutput->mChannelData[0]));
-      PodZero(samples, WEBAUDIO_BLOCK_SIZE);
-    }
-    uint32_t channelCount = aOutput->mChannelData.Length();
-    for (uint32_t channel = 0; channel < channelCount; ++channel) {
-      float* samples =
-        static_cast<float*>(const_cast<void*>(aOutput->mChannelData[channel]));
-      AudioBlockInPlaceScale(samples, aOutput->mVolume);
-    }
-    nsRefPtr<TransferBuffer> transfer = new TransferBuffer(aStream, *aOutput);
+    nsRefPtr<TransferBuffer> transfer = new TransferBuffer(aStream, aInput);
     NS_DispatchToMainThread(transfer);
   }
 
   virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override
   {
     return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
   }
 };
@@ -84,29 +79,34 @@ AnalyserNode::AnalyserNode(AudioContext*
   : AudioNode(aContext,
               1,
               ChannelCountMode::Max,
               ChannelInterpretation::Speakers)
   , mAnalysisBlock(2048)
   , mMinDecibels(-100.)
   , mMaxDecibels(-30.)
   , mSmoothingTimeConstant(.8)
-  , mWriteIndex(0)
 {
   mStream = aContext->Graph()->CreateAudioNodeStream(new AnalyserNodeEngine(this),
                                                      MediaStreamGraph::INTERNAL_STREAM);
+
+  // Enough chunks must be recorded to handle the case of fftSize being
+  // increased to maximum immediately before getFloatTimeDomainData() is
+  // called, for example.
+  (void)mChunks.SetLength(CHUNK_COUNT, fallible);
+
   AllocateBuffer();
 }
 
 size_t
 AnalyserNode::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
 {
   size_t amount = AudioNode::SizeOfExcludingThis(aMallocSizeOf);
   amount += mAnalysisBlock.SizeOfExcludingThis(aMallocSizeOf);
-  amount += mBuffer.SizeOfExcludingThis(aMallocSizeOf);
+  amount += mChunks.SizeOfExcludingThis(aMallocSizeOf);
   amount += mOutputBuffer.SizeOfExcludingThis(aMallocSizeOf);
   return amount;
 }
 
 size_t
 AnalyserNode::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
 {
   return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
@@ -118,17 +118,17 @@ AnalyserNode::WrapObject(JSContext* aCx,
   return AnalyserNodeBinding::Wrap(aCx, this, aGivenProto);
 }
 
 void
 AnalyserNode::SetFftSize(uint32_t aValue, ErrorResult& aRv)
 {
   // Disallow values that are not a power of 2 and outside the [32,32768] range
   if (aValue < 32 ||
-      aValue > 32768 ||
+      aValue > MAX_FFT_SIZE ||
       (aValue & (aValue - 1)) != 0) {
     aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
     return;
   }
   if (FftSize() != aValue) {
     mAnalysisBlock.SetFFTSize(aValue);
     AllocateBuffer();
   }
@@ -207,62 +207,61 @@ AnalyserNode::GetByteFrequencyData(const
 }
 
 void
 AnalyserNode::GetFloatTimeDomainData(const Float32Array& aArray)
 {
   aArray.ComputeLengthAndData();
 
   float* buffer = aArray.Data();
-  size_t length = std::min(size_t(aArray.Length()), mBuffer.Length());
+  size_t length = std::min(aArray.Length(), FftSize());
 
-  for (size_t i = 0; i < length; ++i) {
-    buffer[i] = mBuffer[(i + mWriteIndex) % mBuffer.Length()];;
-  }
+  GetTimeDomainData(buffer, length);
 }
 
 void
 AnalyserNode::GetByteTimeDomainData(const Uint8Array& aArray)
 {
   aArray.ComputeLengthAndData();
 
-  unsigned char* buffer = aArray.Data();
-  size_t length = std::min(size_t(aArray.Length()), mBuffer.Length());
+  size_t length = std::min(aArray.Length(), FftSize());
 
+  AlignedTArray<float> tmpBuffer;
+  if (!tmpBuffer.SetLength(length, fallible)) {
+    return;
+  }
+
+  GetTimeDomainData(tmpBuffer.Elements(), length);
+
+  unsigned char* buffer = aArray.Data();
   for (size_t i = 0; i < length; ++i) {
-    const float value = mBuffer[(i + mWriteIndex) % mBuffer.Length()];
+    const float value = tmpBuffer[i];
     // scale the value to the range of [0, UCHAR_MAX]
     const float scaled = std::max(0.0f, std::min(float(UCHAR_MAX),
                                                  128.0f * (value + 1.0f)));
     buffer[i] = static_cast<unsigned char>(scaled);
   }
 }
 
 bool
 AnalyserNode::FFTAnalysis()
 {
-  float* inputBuffer;
   AlignedTArray<float> tmpBuffer;
-  if (mWriteIndex == 0) {
-    inputBuffer = mBuffer.Elements();
-  } else {
-    if (!tmpBuffer.SetLength(FftSize(), fallible)) {
-      return false;
-    }
-    inputBuffer = tmpBuffer.Elements();
-    memcpy(inputBuffer, mBuffer.Elements() + mWriteIndex, sizeof(float) * (FftSize() - mWriteIndex));
-    memcpy(inputBuffer + FftSize() - mWriteIndex, mBuffer.Elements(), sizeof(float) * mWriteIndex);
+  size_t fftSize = FftSize();
+  if (!tmpBuffer.SetLength(fftSize, fallible)) {
+    return false;
   }
 
-  ApplyBlackmanWindow(inputBuffer, FftSize());
-
+  float* inputBuffer = tmpBuffer.Elements();
+  GetTimeDomainData(inputBuffer, fftSize);
+  ApplyBlackmanWindow(inputBuffer, fftSize);
   mAnalysisBlock.PerformFFT(inputBuffer);
 
   // Normalize so than an input sine wave at 0dBfs registers as 0dBfs (undo FFT scaling factor).
-  const double magnitudeScale = 1.0 / FftSize();
+  const double magnitudeScale = 1.0 / fftSize;
 
   for (uint32_t i = 0; i < mOutputBuffer.Length(); ++i) {
     double scalarMagnitude = NS_hypot(mAnalysisBlock.RealData(i),
                                       mAnalysisBlock.ImagData(i)) *
                              magnitudeScale;
     mOutputBuffer[i] = mSmoothingTimeConstant * mOutputBuffer[i] +
                        (1.0 - mSmoothingTimeConstant) * scalarMagnitude;
   }
@@ -284,57 +283,77 @@ AnalyserNode::ApplyBlackmanWindow(float*
     aBuffer[i] *= window;
   }
 }
 
 bool
 AnalyserNode::AllocateBuffer()
 {
   bool result = true;
-  if (mBuffer.Length() != FftSize()) {
-    if (!mBuffer.SetLength(FftSize(), fallible)) {
-      return false;
-    }
-    memset(mBuffer.Elements(), 0, sizeof(float) * FftSize());
-    mWriteIndex = 0;
-
+  if (mOutputBuffer.Length() != FrequencyBinCount()) {
     if (!mOutputBuffer.SetLength(FrequencyBinCount(), fallible)) {
       return false;
     }
     memset(mOutputBuffer.Elements(), 0, sizeof(float) * FrequencyBinCount());
   }
   return result;
 }
 
 void
 AnalyserNode::AppendChunk(const AudioChunk& aChunk)
 {
-  const uint32_t bufferSize = mBuffer.Length();
-  const uint32_t channelCount = aChunk.mChannelData.Length();
-  uint32_t chunkDuration = aChunk.mDuration;
-  MOZ_ASSERT((bufferSize & (bufferSize - 1)) == 0); // Must be a power of two!
-  MOZ_ASSERT(channelCount > 0);
-  MOZ_ASSERT(chunkDuration == WEBAUDIO_BLOCK_SIZE);
+  if (mChunks.Length() == 0) {
+    return;
+  }
+
+  ++mCurrentChunk;
+  mChunks[mCurrentChunk & (CHUNK_COUNT - 1)] = aChunk;
+}
 
-  if (chunkDuration > bufferSize) {
-    // Copy a maximum bufferSize samples.
-    chunkDuration = bufferSize;
+// Reads into aData the oldest aLength samples of the fftSize most recent
+// samples.
+void
+AnalyserNode::GetTimeDomainData(float* aData, size_t aLength)
+{
+  size_t fftSize = FftSize();
+  MOZ_ASSERT(aLength <= fftSize);
+
+  if (mChunks.Length() == 0) {
+    PodZero(aData, aLength);
+    return;
   }
 
-  PodCopy(mBuffer.Elements() + mWriteIndex, static_cast<const float*>(aChunk.mChannelData[0]), chunkDuration);
-  for (uint32_t i = 1; i < channelCount; ++i) {
-    AudioBlockAddChannelWithScale(static_cast<const float*>(aChunk.mChannelData[i]), 1.0f,
-                                  mBuffer.Elements() + mWriteIndex);
-  }
-  if (channelCount > 1) {
-    AudioBlockInPlaceScale(mBuffer.Elements() + mWriteIndex,
-                           1.0f / aChunk.mChannelData.Length());
-  }
-  mWriteIndex += chunkDuration;
-  MOZ_ASSERT(mWriteIndex <= bufferSize);
-  if (mWriteIndex >= bufferSize) {
-    mWriteIndex = 0;
+  size_t readChunk =
+    mCurrentChunk - ((fftSize - 1) >> WEBAUDIO_BLOCK_SIZE_BITS);
+  size_t readIndex = (0 - fftSize) & (WEBAUDIO_BLOCK_SIZE - 1);
+  MOZ_ASSERT(readIndex == 0 || readIndex + fftSize == WEBAUDIO_BLOCK_SIZE);
+
+  for (size_t writeIndex = 0; writeIndex < aLength; ) {
+    const AudioChunk& chunk = mChunks[readChunk & (CHUNK_COUNT - 1)];
+    const size_t channelCount = chunk.mChannelData.Length();
+    size_t copyLength =
+      std::min<size_t>(aLength - writeIndex, WEBAUDIO_BLOCK_SIZE);
+    float* dataOut = &aData[writeIndex];
+
+    if (channelCount == 0) {
+      PodZero(dataOut, copyLength);
+    } else {
+      float scale = chunk.mVolume / channelCount;
+      { // channel 0
+        auto channelData =
+          static_cast<const float*>(chunk.mChannelData[0]) + readIndex;
+        AudioBufferCopyWithScale(channelData, scale, dataOut, copyLength);
+      }
+      for (uint32_t i = 1; i < channelCount; ++i) {
+        auto channelData =
+          static_cast<const float*>(chunk.mChannelData[i]) + readIndex;
+        AudioBufferAddWithScale(channelData, scale, dataOut, copyLength);
+      }
+    }
+
+    readChunk++;
+    writeIndex += copyLength;
   }
 }
 
 } // namespace dom
 } // namespace mozilla
 
--- a/dom/media/webaudio/AnalyserNode.h
+++ b/dom/media/webaudio/AnalyserNode.h
@@ -66,24 +66,25 @@ protected:
   ~AnalyserNode() {}
 
 private:
   friend class AnalyserNodeEngine;
   void AppendChunk(const AudioChunk& aChunk);
   bool AllocateBuffer();
   bool FFTAnalysis();
   void ApplyBlackmanWindow(float* aBuffer, uint32_t aSize);
+  void GetTimeDomainData(float* aData, size_t aLength);
 
 private:
   FFTBlock mAnalysisBlock;
+  nsTArray<AudioChunk> mChunks;
   double mMinDecibels;
   double mMaxDecibels;
   double mSmoothingTimeConstant;
-  uint32_t mWriteIndex;
-  AlignedTArray<float> mBuffer;
+  size_t mCurrentChunk = 0;
   AlignedTArray<float> mOutputBuffer;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif
 
--- a/dom/media/webaudio/AudioBufferSourceNode.cpp
+++ b/dom/media/webaudio/AudioBufferSourceNode.cpp
@@ -202,17 +202,17 @@ public:
 
   // Copy aNumberOfFrames frames from the source buffer at offset aSourceOffset
   // and put it at offset aBufferOffset in the destination buffer.
   void CopyFromInputBuffer(AudioChunk* aOutput,
                            uint32_t aChannels,
                            uintptr_t aOffsetWithinBlock,
                            uint32_t aNumberOfFrames) {
     for (uint32_t i = 0; i < aChannels; ++i) {
-      float* baseChannelData = static_cast<float*>(const_cast<void*>(aOutput->mChannelData[i]));
+      float* baseChannelData = aOutput->ChannelFloatsForWrite(i);
       memcpy(baseChannelData + aOffsetWithinBlock,
              mBuffer->GetData(i) + mBufferPosition,
              aNumberOfFrames * sizeof(float));
     }
   }
 
   // Resamples input data to an output buffer, according to |mBufferSampleRate| and
   // the playbackRate/detune.
@@ -263,18 +263,17 @@ public:
       inputLimit = std::min(inputLimit, availableInInputBuffer);
 
       for (uint32_t i = 0; true; ) {
         uint32_t inSamples = inputLimit;
         const float* inputData = mBuffer->GetData(i) + mBufferPosition;
 
         uint32_t outSamples = availableInOutputBuffer;
         float* outputData =
-          static_cast<float*>(const_cast<void*>(aOutput->mChannelData[i])) +
-          *aOffsetWithinBlock;
+          aOutput->ChannelFloatsForWrite(i) + *aOffsetWithinBlock;
 
         WebAudioUtils::SpeexResamplerProcess(resampler, i,
                                              inputData, &inSamples,
                                              outputData, &outSamples);
         if (++i == aChannels) {
           mBufferPosition += inSamples;
           MOZ_ASSERT(mBufferPosition <= mBufferEnd || mLoop);
           *aOffsetWithinBlock += outSamples;
@@ -289,18 +288,17 @@ public:
           return;
         }
       }
     } else {
       for (uint32_t i = 0; true; ) {
         uint32_t inSamples = mRemainingResamplerTail;
         uint32_t outSamples = availableInOutputBuffer;
         float* outputData =
-          static_cast<float*>(const_cast<void*>(aOutput->mChannelData[i])) +
-          *aOffsetWithinBlock;
+          aOutput->ChannelFloatsForWrite(i) + *aOffsetWithinBlock;
 
         // AudioDataValue* for aIn selects the function that does not try to
         // copy and format-convert input data.
         WebAudioUtils::SpeexResamplerProcess(resampler, i,
                          static_cast<AudioDataValue*>(nullptr), &inSamples,
                          outputData, &outSamples);
         if (++i == aChannels) {
           mRemainingResamplerTail -= inSamples;
--- a/dom/media/webaudio/AudioNodeEngine.cpp
+++ b/dom/media/webaudio/AudioNodeEngine.cpp
@@ -10,16 +10,25 @@
 #include "AudioNodeEngineNEON.h"
 #endif
 
 namespace mozilla {
 
 void
 AllocateAudioBlock(uint32_t aChannelCount, AudioChunk* aChunk)
 {
+  if (aChunk->mBuffer && !aChunk->mBuffer->IsShared() &&
+      aChunk->ChannelCount() == aChannelCount) {
+    MOZ_ASSERT(aChunk->mBufferFormat == AUDIO_FORMAT_FLOAT32);
+    MOZ_ASSERT(aChunk->mDuration == WEBAUDIO_BLOCK_SIZE);
+    // No need to allocate again.
+    aChunk->mVolume = 1.0f;
+    return;
+  }
+
   CheckedInt<size_t> size = WEBAUDIO_BLOCK_SIZE;
   size *= aChannelCount;
   size *= sizeof(float);
   if (!size.isValid()) {
     MOZ_CRASH();
   }
   // XXX for SIMD purposes we should do something here to make sure the
   // channel buffers are 16-byte aligned.
@@ -37,19 +46,19 @@ AllocateAudioBlock(uint32_t aChannelCoun
 
 void
 WriteZeroesToAudioBlock(AudioChunk* aChunk, uint32_t aStart, uint32_t aLength)
 {
   MOZ_ASSERT(aStart + aLength <= WEBAUDIO_BLOCK_SIZE);
   MOZ_ASSERT(!aChunk->IsNull(), "You should pass a non-null chunk");
   if (aLength == 0)
     return;
+
   for (uint32_t i = 0; i < aChunk->mChannelData.Length(); ++i) {
-    memset(static_cast<float*>(const_cast<void*>(aChunk->mChannelData[i])) + aStart,
-           0, aLength*sizeof(float));
+    PodZero(aChunk->ChannelFloatsForWrite(i) + aStart, aLength);
   }
 }
 
 void AudioBufferCopyWithScale(const float* aInput,
                               float aScale,
                               float* aOutput,
                               uint32_t aSize)
 {
--- a/dom/media/webaudio/AudioNodeEngine.h
+++ b/dom/media/webaudio/AudioNodeEngine.h
@@ -109,18 +109,18 @@ public:
     return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
   }
 
 private:
   nsAutoTArray<Storage, 2> mContents;
 };
 
 /**
- * Allocates an AudioChunk with fresh buffers of WEBAUDIO_BLOCK_SIZE float samples.
- * AudioChunk::mChannelData's entries can be cast to float* for writing.
+ * Allocates, if necessary, aChannelCount buffers of WEBAUDIO_BLOCK_SIZE float
+ * samples for writing to an AudioChunk.
  */
 void AllocateAudioBlock(uint32_t aChannelCount, AudioChunk* aChunk);
 
 /**
  * aChunk must have been allocated by AllocateAudioBlock.
  */
 void WriteZeroesToAudioBlock(AudioChunk* aChunk, uint32_t aStart, uint32_t aLength);
 
--- a/dom/media/webaudio/AudioNodeExternalInputStream.cpp
+++ b/dom/media/webaudio/AudioNodeExternalInputStream.cpp
@@ -44,18 +44,17 @@ CopyChunkToBlock(const AudioChunk& aInpu
       // We only need to upmix here because aBlock's channel count has been
       // chosen to be a superset of the channel count of every chunk.
       AudioChannelsUpMix(&channels, blockChannels, nullptr);
     }
   }
 
   uint32_t duration = aInput.GetDuration();
   for (uint32_t c = 0; c < blockChannels; ++c) {
-    float* outputData =
-      static_cast<float*>(const_cast<void*>(aBlock->mChannelData[c])) + aOffsetInBlock;
+    float* outputData = aBlock->ChannelFloatsForWrite(c) + aOffsetInBlock;
     if (channels[c]) {
       switch (aInput.mBufferFormat) {
       case AUDIO_FORMAT_FLOAT32:
         ConvertAudioSamplesWithScale(
             static_cast<const float*>(channels[c]), outputData, duration,
             aInput.mVolume);
         break;
       case AUDIO_FORMAT_S16:
--- a/dom/media/webaudio/AudioNodeStream.cpp
+++ b/dom/media/webaudio/AudioNodeStream.cpp
@@ -406,17 +406,17 @@ AudioNodeStream::AccumulateInputChunk(ui
                                       AudioChunk* aBlock,
                                       nsTArray<float>* aDownmixBuffer)
 {
   nsAutoTArray<const void*,GUESS_AUDIO_CHANNELS> channels;
   UpMixDownMixChunk(&aChunk, aBlock->mChannelData.Length(), channels, *aDownmixBuffer);
 
   for (uint32_t c = 0; c < channels.Length(); ++c) {
     const float* inputData = static_cast<const float*>(channels[c]);
-    float* outputData = static_cast<float*>(const_cast<void*>(aBlock->mChannelData[c]));
+    float* outputData = aBlock->ChannelFloatsForWrite(c);
     if (inputData) {
       if (aInputIndex == 0) {
         AudioBlockCopyChannelWithScale(inputData, aChunk.mVolume, outputData);
       } else {
         AudioBlockAddChannelWithScale(inputData, aChunk.mVolume, outputData);
       }
     } else {
       if (aInputIndex == 0) {
--- a/dom/media/webaudio/BiquadFilterNode.cpp
+++ b/dom/media/webaudio/BiquadFilterNode.cpp
@@ -201,17 +201,17 @@ public:
         if (aInput.mVolume != 1.0) {
           AudioBlockCopyChannelWithScale(input, aInput.mVolume, inputBuffer);
           input = inputBuffer;
         }
       }
       SetParamsOnBiquad(mBiquads[i], aStream->SampleRate(), mType, freq, q, gain, detune);
 
       mBiquads[i].process(input,
-                          static_cast<float*>(const_cast<void*>(aOutput->mChannelData[i])),
+                          aOutput->ChannelFloatsForWrite(i),
                           aInput.GetDuration());
     }
   }
 
   virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const override
   {
     // Not owned:
     // - mSource - probably not owned
--- a/dom/media/webaudio/ChannelMergerNode.cpp
+++ b/dom/media/webaudio/ChannelMergerNode.cpp
@@ -45,17 +45,17 @@ public:
     // Append each channel in each input to the output
     size_t channelIndex = 0;
     for (uint16_t i = 0; true; ++i) {
       MOZ_ASSERT(i < InputCount());
       for (size_t j = 0; j < aInput[i].mChannelData.Length(); ++j) {
         AudioBlockCopyChannelWithScale(
             static_cast<const float*>(aInput[i].mChannelData[j]),
             aInput[i].mVolume,
-            static_cast<float*>(const_cast<void*>(aOutput[0].mChannelData[channelIndex])));
+            aOutput[0].ChannelFloatsForWrite(channelIndex));
         ++channelIndex;
         if (channelIndex >= channelCount) {
           return;
         }
       }
     }
   }
 
--- a/dom/media/webaudio/ChannelSplitterNode.cpp
+++ b/dom/media/webaudio/ChannelSplitterNode.cpp
@@ -33,17 +33,17 @@ public:
     aOutput.SetLength(OutputCount());
     for (uint16_t i = 0; i < OutputCount(); ++i) {
       if (i < aInput[0].mChannelData.Length()) {
         // Split out existing channels
         AllocateAudioBlock(1, &aOutput[i]);
         AudioBlockCopyChannelWithScale(
             static_cast<const float*>(aInput[0].mChannelData[i]),
             aInput[0].mVolume,
-            static_cast<float*>(const_cast<void*>(aOutput[i].mChannelData[0])));
+            aOutput[i].ChannelFloatsForWrite(0));
       } else {
         // Pad with silent channels if needed
         aOutput[i].SetNull(WEBAUDIO_BLOCK_SIZE);
       }
     }
   }
 
   virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override
--- a/dom/media/webaudio/ConvolverNode.cpp
+++ b/dom/media/webaudio/ConvolverNode.cpp
@@ -130,17 +130,17 @@ public:
       }
     } else {
       if (aInput.mVolume != 1.0f) {
         // Pre-multiply the input's volume
         uint32_t numChannels = aInput.mChannelData.Length();
         AllocateAudioBlock(numChannels, &input);
         for (uint32_t i = 0; i < numChannels; ++i) {
           const float* src = static_cast<const float*>(aInput.mChannelData[i]);
-          float* dest = static_cast<float*>(const_cast<void*>(input.mChannelData[i]));
+          float* dest = input.ChannelFloatsForWrite(i);
           AudioBlockCopyChannelWithScale(src, aInput.mVolume, dest);
         }
       }
 
       if (mLeftOverData <= 0) {
         nsRefPtr<PlayingRefChanged> refchanged =
           new PlayingRefChanged(aStream, PlayingRefChanged::ADDREF);
         aStream->Graph()->
--- a/dom/media/webaudio/DelayBuffer.cpp
+++ b/dom/media/webaudio/DelayBuffer.cpp
@@ -92,49 +92,46 @@ DelayBuffer::Read(const double aPerFrame
   }
 
   // Remember currentDelayFrames for the next ProcessBlock call
   mCurrentDelay = aPerFrameDelays[WEBAUDIO_BLOCK_SIZE - 1];
 }
 
 void
 DelayBuffer::ReadChannel(const double aPerFrameDelays[WEBAUDIO_BLOCK_SIZE],
-                         const AudioChunk* aOutputChunk, uint32_t aChannel,
+                         AudioChunk* aOutputChunk, uint32_t aChannel,
                          ChannelInterpretation aChannelInterpretation)
 {
   if (!mChunks.Length()) {
-    float* outputChannel = static_cast<float*>
-      (const_cast<void*>(aOutputChunk->mChannelData[aChannel]));
+    float* outputChannel = aOutputChunk->ChannelFloatsForWrite(aChannel);
     PodZero(outputChannel, WEBAUDIO_BLOCK_SIZE);
     return;
   }
 
   ReadChannels(aPerFrameDelays, aOutputChunk,
                aChannel, 1, aChannelInterpretation);
 }
 
 void
 DelayBuffer::ReadChannels(const double aPerFrameDelays[WEBAUDIO_BLOCK_SIZE],
-                          const AudioChunk* aOutputChunk,
+                          AudioChunk* aOutputChunk,
                           uint32_t aFirstChannel, uint32_t aNumChannelsToRead,
                           ChannelInterpretation aChannelInterpretation)
 {
   uint32_t totalChannelCount = aOutputChunk->mChannelData.Length();
   uint32_t readChannelsEnd = aFirstChannel + aNumChannelsToRead;
   MOZ_ASSERT(readChannelsEnd <= totalChannelCount);
 
   if (mUpmixChannels.Length() != totalChannelCount) {
     mLastReadChunk = -1; // invalidate cache
   }
 
-  float* const* outputChannels = reinterpret_cast<float* const*>
-    (const_cast<void* const*>(aOutputChunk->mChannelData.Elements()));
   for (uint32_t channel = aFirstChannel;
        channel < readChannelsEnd; ++channel) {
-    PodZero(outputChannels[channel], WEBAUDIO_BLOCK_SIZE);
+    PodZero(aOutputChunk->ChannelFloatsForWrite(channel), WEBAUDIO_BLOCK_SIZE);
   }
 
   for (unsigned i = 0; i < WEBAUDIO_BLOCK_SIZE; ++i) {
     double currentDelay = aPerFrameDelays[i];
     MOZ_ASSERT(currentDelay >= 0.0);
     MOZ_ASSERT(currentDelay <= (mChunks.Length() - 1) * WEBAUDIO_BLOCK_SIZE);
 
     // Interpolate two input frames in case the read position does not match
@@ -153,17 +150,17 @@ DelayBuffer::ReadChannels(const double a
       // chunks specially.
       if (!mChunks[readChunk].IsNull()) {
         int readOffset = OffsetForPosition(positions[tick]);
         UpdateUpmixChannels(readChunk, totalChannelCount,
                             aChannelInterpretation);
         double multiplier = interpolationFactor * mChunks[readChunk].mVolume;
         for (uint32_t channel = aFirstChannel;
              channel < readChannelsEnd; ++channel) {
-          outputChannels[channel][i] += multiplier *
+          aOutputChunk->ChannelFloatsForWrite(channel)[i] += multiplier *
             static_cast<const float*>(mUpmixChannels[channel])[readOffset];
         }
       }
 
       interpolationFactor = 1.0 - interpolationFactor;
     }
   }
 }
--- a/dom/media/webaudio/DelayBuffer.h
+++ b/dom/media/webaudio/DelayBuffer.h
@@ -51,17 +51,17 @@ public:
   void Read(double aDelayTicks, AudioChunk* aOutputChunk,
             ChannelInterpretation aChannelInterpretation);
 
   // Read into one of the channels of aOutputChunk, given an array of
   // delays in ticks.  This is useful when delays are different on different
   // channels.  aOutputChunk must have already been allocated with at least as
   // many channels as were in any of the blocks passed to Write().
   void ReadChannel(const double aPerFrameDelays[WEBAUDIO_BLOCK_SIZE],
-                   const AudioChunk* aOutputChunk, uint32_t aChannel,
+                   AudioChunk* aOutputChunk, uint32_t aChannel,
                    ChannelInterpretation aChannelInterpretation);
 
   // Advance the buffer pointer
   void NextBlock()
   {
     mCurrentChunk = (mCurrentChunk + 1) % mChunks.Length();
 #ifdef DEBUG
     MOZ_ASSERT(mHaveWrittenBlock);
@@ -75,17 +75,17 @@ public:
   };
 
   int MaxDelayTicks() const { return mMaxDelayTicks; }
 
   size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const;
 
 private:
   void ReadChannels(const double aPerFrameDelays[WEBAUDIO_BLOCK_SIZE],
-                    const AudioChunk* aOutputChunk,
+                    AudioChunk* aOutputChunk,
                     uint32_t aFirstChannel, uint32_t aNumChannelsToRead,
                     ChannelInterpretation aChannelInterpretation);
   bool EnsureBuffer();
   int PositionForDelay(int aDelay);
   int ChunkForPosition(int aPosition);
   int OffsetForPosition(int aPosition);
   int ChunkForDelay(int aDelay);
   void UpdateUpmixChannels(int aNewReadChunk, uint32_t channelCount,
--- a/dom/media/webaudio/GainNode.cpp
+++ b/dom/media/webaudio/GainNode.cpp
@@ -90,18 +90,17 @@ public:
 
       for (size_t counter = 0; counter < WEBAUDIO_BLOCK_SIZE; ++counter) {
         computedGain[counter] *= aInput.mVolume;
       }
 
       // Apply the gain to the output buffer
       for (size_t channel = 0; channel < aOutput->mChannelData.Length(); ++channel) {
         const float* inputBuffer = static_cast<const float*> (aInput.mChannelData[channel]);
-        float* buffer = static_cast<float*> (const_cast<void*>
-                          (aOutput->mChannelData[channel]));
+        float* buffer = aOutput->ChannelFloatsForWrite(channel);
         AudioBlockCopyChannelWithScale(inputBuffer, computedGain, buffer);
       }
     }
   }
 
   virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const override
   {
     // Not owned:
--- a/dom/media/webaudio/OscillatorNode.cpp
+++ b/dom/media/webaudio/OscillatorNode.cpp
@@ -303,18 +303,17 @@ public:
     }
     if (ticks + WEBAUDIO_BLOCK_SIZE <= mStart) {
       // We're not playing yet.
       ComputeSilence(aOutput);
       return;
     }
 
     AllocateAudioBlock(1, aOutput);
-    float* output = static_cast<float*>(
-        const_cast<void*>(aOutput->mChannelData[0]));
+    float* output = aOutput->ChannelFloatsForWrite(0);
 
     uint32_t start, end;
     FillBounds(output, ticks, start, end);
 
     // Synthesize the correct waveform.
     switch(mType) {
       case OscillatorType::Sine:
         ComputeSine(output, ticks, start, end);
--- a/dom/media/webaudio/PanningUtils.h
+++ b/dom/media/webaudio/PanningUtils.h
@@ -13,37 +13,37 @@
 namespace mozilla {
 namespace dom {
 
 template<typename T>
 void
 GainMonoToStereo(const AudioChunk& aInput, AudioChunk* aOutput,
                  T aGainL, T aGainR)
 {
-  float* outputL = static_cast<float*>(const_cast<void*>(aOutput->mChannelData[0]));
-  float* outputR = static_cast<float*>(const_cast<void*>(aOutput->mChannelData[1]));
-  const float* input = static_cast<float*>(const_cast<void*>(aInput.mChannelData[0]));
+  float* outputL = aOutput->ChannelFloatsForWrite(0);
+  float* outputR = aOutput->ChannelFloatsForWrite(1);
+  const float* input = static_cast<const float*>(aInput.mChannelData[0]);
 
   MOZ_ASSERT(aInput.ChannelCount() == 1);
   MOZ_ASSERT(aOutput->ChannelCount() == 2);
 
   AudioBlockPanMonoToStereo(input, aGainL, aGainR, outputL, outputR);
 }
 
 // T can be float or an array of float, and  U can be bool or an array of bool,
 // depending if the value of the parameters are constant for this block.
 template<typename T, typename U>
 void
 GainStereoToStereo(const AudioChunk& aInput, AudioChunk* aOutput,
                    T aGainL, T aGainR, U aOnLeft)
 {
-  float* outputL = static_cast<float*>(const_cast<void*>(aOutput->mChannelData[0]));
-  float* outputR = static_cast<float*>(const_cast<void*>(aOutput->mChannelData[1]));
-  const float* inputL = static_cast<float*>(const_cast<void*>(aInput.mChannelData[0]));
-  const float* inputR = static_cast<float*>(const_cast<void*>(aInput.mChannelData[1]));
+  float* outputL = aOutput->ChannelFloatsForWrite(0);
+  float* outputR = aOutput->ChannelFloatsForWrite(1);
+  const float* inputL = static_cast<const float*>(aInput.mChannelData[0]);
+  const float* inputR = static_cast<const float*>(aInput.mChannelData[1]);
 
   MOZ_ASSERT(aInput.ChannelCount() == 2);
   MOZ_ASSERT(aOutput->ChannelCount() == 2);
 
   AudioBlockPanStereoToStereo(inputL, inputR, aGainL, aGainR, aOnLeft, outputL, outputR);
 }
 
 // T can be float or an array of float, and  U can be bool or an array of bool,
--- a/dom/media/webaudio/StereoPannerNode.cpp
+++ b/dom/media/webaudio/StereoPannerNode.cpp
@@ -81,33 +81,33 @@ public:
 
     aLeftGain  = cos(0.5 * M_PI * aPanning);
     aRightGain = sin(0.5 * M_PI * aPanning);
   }
 
   void SetToSilentStereoBlock(AudioChunk* aChunk)
   {
     for (uint32_t channel = 0; channel < 2; channel++) {
-      float* samples = static_cast<float*>(const_cast<void*>(aChunk->mChannelData[channel]));
+      float* samples = aChunk->ChannelFloatsForWrite(channel);
       for (uint32_t i = 0; i < WEBAUDIO_BLOCK_SIZE; i++) {
         samples[i] = 0.f;
       }
     }
   }
 
   void UpmixToStereoIfNeeded(const AudioChunk& aInput, AudioChunk* aOutput)
   {
     if (aInput.ChannelCount() == 2) {
       *aOutput = aInput;
     } else {
       MOZ_ASSERT(aInput.ChannelCount() == 1);
       AllocateAudioBlock(2, aOutput);
       const float* input = static_cast<const float*>(aInput.mChannelData[0]);
       for (uint32_t channel = 0; channel < 2; channel++) {
-        float* output = static_cast<float*>(const_cast<void*>(aOutput->mChannelData[channel]));
+        float* output = aOutput->ChannelFloatsForWrite(channel);
         PodCopy(output, input, WEBAUDIO_BLOCK_SIZE);
       }
     }
   }
 
   virtual void ProcessBlock(AudioNodeStream* aStream,
                             const AudioChunk& aInput,
                             AudioChunk* aOutput,
--- a/dom/media/webaudio/WaveShaperNode.cpp
+++ b/dom/media/webaudio/WaveShaperNode.cpp
@@ -227,17 +227,17 @@ public:
       return;
     }
 
     AllocateAudioBlock(channelCount, aOutput);
     for (uint32_t i = 0; i < channelCount; ++i) {
       float* scaledSample = (float *)(aInput.mChannelData[i]);
       AudioBlockInPlaceScale(scaledSample, aInput.mVolume);
       const float* inputBuffer = static_cast<const float*>(scaledSample);
-      float* outputBuffer = const_cast<float*> (static_cast<const float*>(aOutput->mChannelData[i]));
+      float* outputBuffer = aOutput->ChannelFloatsForWrite(i);
       float* sampleBuffer;
 
       switch (mType) {
       case OverSampleType::None:
         mResampler.Reset(channelCount, aStream->SampleRate(), OverSampleType::None);
         ProcessCurve<1>(inputBuffer, outputBuffer);
         break;
       case OverSampleType::_2x:
--- a/dom/media/webaudio/test/mochitest.ini
+++ b/dom/media/webaudio/test/mochitest.ini
@@ -26,16 +26,17 @@ support-files =
   ting-48k-1ch.wav
   ting-48k-2ch.wav
   webaudio.js
 
 [test_analyserNode.html]
 [test_analyserScale.html]
 [test_analyserNodeOutput.html]
 [test_analyserNodePassThrough.html]
+[test_analyserNodeWithGain.html]
 [test_AudioBuffer.html]
 [test_audioBufferSourceNode.html]
 [test_audioBufferSourceNodeEnded.html]
 [test_audioBufferSourceNodeLazyLoopParam.html]
 [test_audioBufferSourceNodeLoop.html]
 [test_audioBufferSourceNodeLoopStartEnd.html]
 [test_audioBufferSourceNodeLoopStartEndSame.html]
 [test_audioBufferSourceNodeNeutered.html]
new file mode 100644
--- /dev/null
+++ b/dom/media/webaudio/test/test_analyserNodeWithGain.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<title>Test effect of AnalyserNode on GainNode output</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+async_test(function(t) {
+  // fftSize <= 128 so that the time domain data is full of input after
+  // notification that the first block has been processed.
+  const fftSize = 32;
+
+  var context = new AudioContext();
+
+  var analyser1 = context.createAnalyser();
+  analyser1.fftSize = fftSize;
+  var analyser2 = context.createAnalyser();
+  analyser2.fftSize = fftSize;
+
+  var gain = context.createGain();
+  gain.gain.value = 2.0;
+  gain.connect(analyser1);
+  gain.connect(analyser2);
+
+  // Create a DC input to make getFloatTimeDomainData() output consistent at
+  // any time.
+  var buffer = context.createBuffer(1, 1, context.sampleRate);
+  buffer.getChannelData(0)[0] = 1.0 / gain.gain.value;
+  var source = context.createBufferSource();
+  source.buffer = buffer;
+  source.loop = true;
+  source.connect(gain);
+  source.start();
+
+  // Waiting for an ended event ensures that the AnalyserNode has received the
+  // signal.
+  var timer = context.createBufferSource();
+  timer.buffer = buffer;
+  timer.onended = t.step_func_done(function() {
+    var data = new Float32Array(1);
+    analyser1.getFloatTimeDomainData(data);
+    assert_equals(data[0], 1.0, "analyser1 time domain data");
+    analyser2.getFloatTimeDomainData(data);
+    assert_equals(data[0], 1.0, "analyser2 time domain data");
+  });
+
+  timer.start()
+});
+</script>
--- a/dom/security/moz.build
+++ b/dom/security/moz.build
@@ -31,8 +31,12 @@ UNIFIED_SOURCES += [
 
 FAIL_ON_WARNINGS = True
 
 FINAL_LIBRARY = 'xul'
 LOCAL_INCLUDES += [
     '/caps',
     '/netwerk/base',
 ]
+
+if CONFIG['GNU_CC']:
+    CFLAGS += ['-Wshadow']
+    CXXFLAGS += ['-Wshadow']
--- a/dom/workers/DataStore.cpp
+++ b/dom/workers/DataStore.cpp
@@ -11,16 +11,17 @@
 #include "mozilla/dom/DataStoreCursor.h"
 #include "mozilla/dom/DataStoreChangeEvent.h"
 #include "mozilla/dom/DataStoreBinding.h"
 #include "mozilla/dom/DataStoreImplBinding.h"
 
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/PromiseWorkerProxy.h"
 #include "mozilla/dom/ScriptSettings.h"
+#include "mozilla/dom/StructuredCloneHelper.h"
 #include "mozilla/ErrorResult.h"
 
 #include "WorkerPrivate.h"
 #include "WorkerRunnable.h"
 #include "WorkerScope.h"
 
 BEGIN_WORKERS_NAMESPACE
 
@@ -202,41 +203,42 @@ protected:
     nsRefPtr<Promise> promise = mBackingStore->Get(mId, mRv);
     promise->AppendNativeHandler(mPromiseWorkerProxy);
     return true;
   }
 };
 
 // A DataStoreRunnable to run DataStore::Put(...) on the main thread.
 class DataStorePutRunnable final : public DataStoreProxyRunnable
+                                 , public StructuredCloneHelper
 {
-  JSAutoStructuredCloneBuffer mObjBuffer;
   const StringOrUnsignedLong& mId;
   const nsString mRevisionId;
   ErrorResult& mRv;
 
 public:
   DataStorePutRunnable(WorkerPrivate* aWorkerPrivate,
                        const nsMainThreadPtrHandle<DataStore>& aBackingStore,
                        Promise* aWorkerPromise,
                        JSContext* aCx,
                        JS::Handle<JS::Value> aObj,
                        const StringOrUnsignedLong& aId,
                        const nsAString& aRevisionId,
                        ErrorResult& aRv)
     : DataStoreProxyRunnable(aWorkerPrivate, aBackingStore, aWorkerPromise)
+    , StructuredCloneHelper(CloningNotSupported, TransferringNotSupported)
     , mId(aId)
     , mRevisionId(aRevisionId)
     , mRv(aRv)
   {
     MOZ_ASSERT(aWorkerPrivate);
     aWorkerPrivate->AssertIsOnWorkerThread();
 
     // This needs to be structured cloned while it's still on the worker thread.
-    if (!mObjBuffer.write(aCx, aObj)) {
+    if (!Write(aCx, aObj)) {
       JS_ClearPendingException(aCx);
       mRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
     }
   }
 
 protected:
   virtual bool
   MainThreadRun() override
@@ -247,17 +249,17 @@ protected:
     AutoJSAPI jsapi;
     if (NS_WARN_IF(!jsapi.Init(mBackingStore->GetParentObject()))) {
       mRv.Throw(NS_ERROR_UNEXPECTED);
       return true;
     }
     JSContext* cx = jsapi.cx();
 
     JS::Rooted<JS::Value> value(cx);
-    if (!mObjBuffer.read(cx, &value)) {
+    if (!Read(mBackingStore->GetParentObject(), cx, &value)) {
       JS_ClearPendingException(cx);
       mRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
       return true;
     }
 
     nsRefPtr<Promise> promise = mBackingStore->Put(cx,
                                                    value,
                                                    mId,
@@ -265,41 +267,42 @@ protected:
                                                    mRv);
     promise->AppendNativeHandler(mPromiseWorkerProxy);
     return true;
   }
 };
 
 // A DataStoreRunnable to run DataStore::Add(...) on the main thread.
 class DataStoreAddRunnable final : public DataStoreProxyRunnable
+                                 , public StructuredCloneHelper
 {
-  JSAutoStructuredCloneBuffer mObjBuffer;
   const Optional<StringOrUnsignedLong>& mId;
   const nsString mRevisionId;
   ErrorResult& mRv;
 
 public:
   DataStoreAddRunnable(WorkerPrivate* aWorkerPrivate,
                        const nsMainThreadPtrHandle<DataStore>& aBackingStore,
                        Promise* aWorkerPromise,
                        JSContext* aCx,
                        JS::Handle<JS::Value> aObj,
                        const Optional<StringOrUnsignedLong>& aId,
                        const nsAString& aRevisionId,
                        ErrorResult& aRv)
     : DataStoreProxyRunnable(aWorkerPrivate, aBackingStore, aWorkerPromise)
+    , StructuredCloneHelper(CloningNotSupported, TransferringNotSupported)
     , mId(aId)
     , mRevisionId(aRevisionId)
     , mRv(aRv)
   {
     MOZ_ASSERT(aWorkerPrivate);
     aWorkerPrivate->AssertIsOnWorkerThread();
 
     // This needs to be structured cloned while it's still on the worker thread.
-    if (!mObjBuffer.write(aCx, aObj)) {
+    if (!Write(aCx, aObj)) {
       JS_ClearPendingException(aCx);
       mRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
     }
   }
 
 protected:
   virtual bool
   MainThreadRun() override
@@ -310,17 +313,17 @@ protected:
     AutoJSAPI jsapi;
     if (NS_WARN_IF(!jsapi.Init(mBackingStore->GetParentObject()))) {
       mRv.Throw(NS_ERROR_UNEXPECTED);
       return true;
     }
     JSContext* cx = jsapi.cx();
 
     JS::Rooted<JS::Value> value(cx);
-    if (!mObjBuffer.read(cx, &value)) {
+    if (!Read(mBackingStore->GetParentObject(), cx, &value)) {
       JS_ClearPendingException(cx);
       mRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
       return true;
     }
 
     nsRefPtr<Promise> promise = mBackingStore->Add(cx,
                                                    value,
                                                    mId,
--- a/dom/xbl/nsXBLDocumentInfo.cpp
+++ b/dom/xbl/nsXBLDocumentInfo.cpp
@@ -34,71 +34,48 @@
 
 using namespace mozilla;
 using namespace mozilla::scache;
 using namespace mozilla::dom;
 
 static const char kXBLCachePrefix[] = "xblcache";
 
 /* Implementation file */
-
-static PLDHashOperator
-TraverseProtos(const nsACString &aKey, nsXBLPrototypeBinding *aProto, void* aClosure)
-{
-  nsCycleCollectionTraversalCallback *cb =
-    static_cast<nsCycleCollectionTraversalCallback*>(aClosure);
-  aProto->Traverse(*cb);
-  return PL_DHASH_NEXT;
-}
-
-static PLDHashOperator
-UnlinkProto(const nsACString &aKey, nsXBLPrototypeBinding *aProto, void* aClosure)
-{
-  aProto->Unlink();
-  return PL_DHASH_NEXT;
-}
-
-struct ProtoTracer
-{
-  const TraceCallbacks &mCallbacks;
-  void *mClosure;
-};
-
-static PLDHashOperator
-TraceProtos(const nsACString &aKey, nsXBLPrototypeBinding *aProto, void* aClosure)
-{
-  ProtoTracer* closure = static_cast<ProtoTracer*>(aClosure);
-  aProto->Trace(closure->mCallbacks, closure->mClosure);
-  return PL_DHASH_NEXT;
-}
-
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsXBLDocumentInfo)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsXBLDocumentInfo)
   if (tmp->mBindingTable) {
-    tmp->mBindingTable->EnumerateRead(UnlinkProto, nullptr);
+    for (auto iter = tmp->mBindingTable->ConstIter();
+         !iter.Done(); iter.Next()) {
+      iter.UserData()->Unlink();
+    }
   }
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocument)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsXBLDocumentInfo)
   if (tmp->mDocument &&
       nsCCUncollectableMarker::InGeneration(cb, tmp->mDocument->GetMarkedCCGeneration())) {
     NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
     return NS_SUCCESS_INTERRUPTED_TRAVERSE;
   }
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocument)
   if (tmp->mBindingTable) {
-    tmp->mBindingTable->EnumerateRead(TraverseProtos, &cb);
+    for (auto iter = tmp->mBindingTable->ConstIter();
+         !iter.Done(); iter.Next()) {
+      iter.UserData()->Traverse(cb);
+    }
   }
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsXBLDocumentInfo)
   if (tmp->mBindingTable) {
-    ProtoTracer closure = { aCallbacks, aClosure };
-    tmp->mBindingTable->EnumerateRead(TraceProtos, &closure);
+    for (auto iter = tmp->mBindingTable->ConstIter();
+         !iter.Done(); iter.Next()) {
+      iter.UserData()->Trace(aCallbacks, aClosure);
+    }
   }
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
 static void
 UnmarkXBLJSObject(JS::GCCellPtr aPtr, const char* aName, void* aClosure)
 {
   JS::ExposeObjectToActiveJS(aPtr.toObject());
 }
--- a/gfx/layers/ImageContainer.cpp
+++ b/gfx/layers/ImageContainer.cpp
@@ -634,30 +634,27 @@ CairoImage::GetTextureClient(Compositabl
     textureClient = aClient->CreateTextureClientForDrawing(surface->GetFormat(),
                                                            surface->GetSize(),
                                                            gfx::BackendType::NONE,
                                                            TextureFlags::DEFAULT);
   }
   if (!textureClient) {
     return nullptr;
   }
-  MOZ_ASSERT(textureClient->CanExposeDrawTarget());
+
   if (!textureClient->Lock(OpenMode::OPEN_WRITE_ONLY)) {
     return nullptr;
   }
 
-  TextureClientAutoUnlock autoUnolck(textureClient);
-  {
-    // We must not keep a reference to the DrawTarget after it has been unlocked.
-    DrawTarget* dt = textureClient->BorrowDrawTarget();
-    if (!dt) {
-      return nullptr;
-    }
-    dt->CopySurface(surface, IntRect(IntPoint(), surface->GetSize()), IntPoint());
-  }
+  TextureClientAutoUnlock autoUnlock(textureClient);
+
+  RefPtr<DataSourceSurface> dataSurf = surface->GetDataSurface();
+  textureClient->UpdateFromSurface(dataSurf);
+
+  textureClient->SyncWithObject(forwarder->GetSyncObject());
 
   mTextureClients.Put(forwarder->GetSerial(), textureClient);
   return textureClient;
 }
 
 PImageContainerChild*
 ImageContainer::GetPImageContainerChild()
 {
--- a/gfx/layers/TextureDIB.cpp
+++ b/gfx/layers/TextureDIB.cpp
@@ -51,16 +51,35 @@ TextureClientDIB::BorrowDrawTarget()
   if (!mDrawTarget) {
     mDrawTarget =
       gfxPlatform::GetPlatform()->CreateDrawTargetForSurface(mSurface, mSize);
   }
 
   return mDrawTarget;
 }
 
+void
+TextureClientDIB::UpdateFromSurface(gfx::DataSourceSurface* aSurface)
+{
+  MOZ_ASSERT(mIsLocked && IsAllocated());
+
+  nsRefPtr<gfxImageSurface> imgSurf = mSurface->GetAsImageSurface();
+
+  DataSourceSurface::MappedSurface sourceMap;
+  aSurface->Map(DataSourceSurface::READ, &sourceMap);
+
+  for (int y = 0; y < aSurface->GetSize().height; y++) {
+    memcpy(imgSurf->Data() + imgSurf->Stride() * y,
+           sourceMap.mData + sourceMap.mStride * y,
+           aSurface->GetSize().width * BytesPerPixel(aSurface->GetFormat()));
+  }
+
+  aSurface->Unmap();
+}
+
 TextureClientMemoryDIB::TextureClientMemoryDIB(ISurfaceAllocator* aAllocator,
                                    gfx::SurfaceFormat aFormat,
                                    TextureFlags aFlags)
   : TextureClientDIB(aAllocator, aFormat, aFlags)
 {
   MOZ_COUNT_CTOR(TextureClientMemoryDIB);
 }
 
--- a/gfx/layers/TextureDIB.h
+++ b/gfx/layers/TextureDIB.h
@@ -29,16 +29,18 @@ public:
   virtual gfx::IntSize GetSize() const override { return mSize; }
 
   virtual gfx::SurfaceFormat GetFormat() const override { return mFormat; }
 
   virtual bool CanExposeDrawTarget() const override { return true; }
 
   virtual gfx::DrawTarget* BorrowDrawTarget() override;
 
+  virtual void UpdateFromSurface(gfx::DataSourceSurface* aSurface) override;
+
   virtual bool HasInternalBuffer() const override { return true; }
 
 protected:
   TextureClientDIB(ISurfaceAllocator* aAllocator, gfx::SurfaceFormat aFormat, TextureFlags aFlags)
     : TextureClient(aAllocator, aFlags)
     , mFormat(aFormat)
     , mIsLocked(false)
   { }
--- a/gfx/layers/apz/util/APZCCallbackHelper.cpp
+++ b/gfx/layers/apz/util/APZCCallbackHelper.cpp
@@ -2,16 +2,17 @@
 /* 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 "APZCCallbackHelper.h"
 
 #include "ContentHelper.h"
 #include "gfxPlatform.h" // For gfxPlatform::UseTiling
+#include "gfxPrefs.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/TabParent.h"
 #include "mozilla/layers/LayerTransactionChild.h"
 #include "mozilla/layers/ShadowLayers.h"
 #include "mozilla/TouchEvents.h"
 #include "nsIScrollableFrame.h"
 #include "nsLayoutUtils.h"
 #include "nsIInterfaceRequestorUtils.h"
@@ -189,43 +190,50 @@ APZCCallbackHelper::UpdateRootFrame(Fram
 
   nsCOMPtr<nsIPresShell> shell = GetPresShell(content);
   if (!shell || aMetrics.GetPresShellId() != shell->GetPresShellId()) {
     return;
   }
 
   MOZ_ASSERT(aMetrics.GetUseDisplayPortMargins());
 
-  float presShellResolution = nsLayoutUtils::GetResolution(shell);
+  if (gfxPrefs::APZAllowZooming()) {
+    // If zooming is disabled then we don't really want to let APZ fiddle
+    // with these things. In theory setting the resolution here should be a
+    // no-op, but setting the SPCSPS is bad because it can cause a stale value
+    // to be returned by window.innerWidth/innerHeight (see bug 1187792).
+
+    float presShellResolution = nsLayoutUtils::GetResolution(shell);
 
-  // If the pres shell resolution has changed on the content side side
-  // the time this repaint request was fired, consider this request out of date
-  // and drop it; setting a zoom based on the out-of-date resolution can have
-  // the effect of getting us stuck with the stale resolution.
-  if (presShellResolution != aMetrics.GetPresShellResolution()) {
-    return;
-  }
+    // If the pres shell resolution has changed on the content side side
+    // the time this repaint request was fired, consider this request out of date
+    // and drop it; setting a zoom based on the out-of-date resolution can have
+    // the effect of getting us stuck with the stale resolution.
+    if (presShellResolution != aMetrics.GetPresShellResolution()) {
+      return;
+    }
 
-  // Set the scroll port size, which determines the scroll range. For example if
-  // a 500-pixel document is shown in a 100-pixel frame, the scroll port length would
-  // be 100, and gecko would limit the maximum scroll offset to 400 (so as to prevent
-  // overscroll). Note that if the content here was zoomed to 2x, the document would
-  // be 1000 pixels long but the frame would still be 100 pixels, and so the maximum
-  // scroll range would be 900. Therefore this calculation depends on the zoom applied
-  // to the content relative to the container.
-  // Note that this needs to happen before scrolling the frame (in UpdateFrameCommon),
-  // otherwise the scroll position may get clamped incorrectly.
-  CSSSize scrollPort = aMetrics.CalculateCompositedSizeInCssPixels();
-  nsLayoutUtils::SetScrollPositionClampingScrollPortSize(shell, scrollPort);
+    // Set the scroll port size, which determines the scroll range. For example if
+    // a 500-pixel document is shown in a 100-pixel frame, the scroll port length would
+    // be 100, and gecko would limit the maximum scroll offset to 400 (so as to prevent
+    // overscroll). Note that if the content here was zoomed to 2x, the document would
+    // be 1000 pixels long but the frame would still be 100 pixels, and so the maximum
+    // scroll range would be 900. Therefore this calculation depends on the zoom applied
+    // to the content relative to the container.
+    // Note that this needs to happen before scrolling the frame (in UpdateFrameCommon),
+    // otherwise the scroll position may get clamped incorrectly.
+    CSSSize scrollPort = aMetrics.CalculateCompositedSizeInCssPixels();
+    nsLayoutUtils::SetScrollPositionClampingScrollPortSize(shell, scrollPort);
 
-  // The pres shell resolution is updated by the the async zoom since the
-  // last paint.
-  presShellResolution = aMetrics.GetPresShellResolution()
-                      * aMetrics.GetAsyncZoom().scale;
-  nsLayoutUtils::SetResolutionAndScaleTo(shell, presShellResolution);
+    // The pres shell resolution is updated by the the async zoom since the
+    // last paint.
+    presShellResolution = aMetrics.GetPresShellResolution()
+                        * aMetrics.GetAsyncZoom().scale;
+    nsLayoutUtils::SetResolutionAndScaleTo(shell, presShellResolution);
+  }
 
   // Do this as late as possible since scrolling can flush layout. It also
   // adjusts the display port margins, so do it before we set those.
   ScrollFrame(content, aMetrics);
 
   SetDisplayPortMargins(shell, content, aMetrics);
 }
 
--- a/gfx/layers/basic/TextureClientX11.cpp
+++ b/gfx/layers/basic/TextureClientX11.cpp
@@ -147,8 +147,23 @@ TextureClientX11::BorrowDrawTarget()
 
   if (!mDrawTarget) {
     IntSize size = mSurface->GetSize();
     mDrawTarget = Factory::CreateDrawTargetForCairoSurface(mSurface->CairoSurface(), size);
   }
 
   return mDrawTarget;
 }
+
+void
+TextureClientX11::UpdateFromSurface(gfx::DataSourceSurface* aSurface)
+{
+  MOZ_ASSERT(CanExposeDrawTarget());
+
+  DrawTarget* dt = BorrowDrawTarget();
+
+  if (!dt) {
+    gfxCriticalError() << "Failed to borrow drawtarget for TextureClientX11::UpdateFromSurface";
+    return;
+  }
+
+  dt->CopySurface(aSurface, IntRect(IntPoint(), aSurface->GetSize()), IntPoint());
+}
\ No newline at end of file
--- a/gfx/layers/basic/TextureClientX11.h
+++ b/gfx/layers/basic/TextureClientX11.h
@@ -38,16 +38,18 @@ class TextureClientX11 : public TextureC
   virtual bool IsLocked() const override { return mLocked; }
 
   virtual bool AllocateForSurface(gfx::IntSize aSize, TextureAllocationFlags flags) override;
 
   virtual bool CanExposeDrawTarget() const override { return true; }
 
   virtual gfx::DrawTarget* BorrowDrawTarget() override;
 
+  virtual void UpdateFromSurface(gfx::DataSourceSurface* aSurface) override;
+
   virtual gfx::SurfaceFormat GetFormat() const override { return mFormat; }
 
   virtual bool HasInternalBuffer() const override { return false; }
 
   virtual already_AddRefed<TextureClient>
   CreateSimilar(TextureFlags aFlags = TextureFlags::DEFAULT,
                 TextureAllocationFlags aAllocFlags = ALLOC_DEFAULT) const override;
 
--- a/gfx/layers/client/TextureClient.cpp
+++ b/gfx/layers/client/TextureClient.cpp
@@ -348,25 +348,27 @@ TextureClient::CreateForDrawing(ISurface
       aSize.width <= maxTextureSize &&
       aSize.height <= maxTextureSize) {
     texture = new TextureClientD3D11(aAllocator, aFormat, aTextureFlags);
   }
   if (parentBackend == LayersBackend::LAYERS_D3D9 &&
       aMoz2DBackend == gfx::BackendType::CAIRO &&
       aAllocator->IsSameProcess() &&
       aSize.width <= maxTextureSize &&
-      aSize.height <= maxTextureSize) {
+      aSize.height <= maxTextureSize &&
+      NS_IsMainThread()) {
     if (gfxWindowsPlatform::GetPlatform()->GetD3D9Device()) {
       texture = new TextureClientD3D9(aAllocator, aFormat, aTextureFlags);
     }
   }
 
   if (!texture && aFormat == SurfaceFormat::B8G8R8X8 &&
       aAllocator->IsSameProcess() &&
-      aMoz2DBackend == gfx::BackendType::CAIRO) {
+      aMoz2DBackend == gfx::BackendType::CAIRO &&
+      NS_IsMainThread()) {
     if (aAllocator->IsSameProcess()) {
       texture = new TextureClientMemoryDIB(aAllocator, aFormat, aTextureFlags);
     } else {
       texture = new TextureClientShmemDIB(aAllocator, aFormat, aTextureFlags);
     }
   }
 #endif
 
@@ -796,16 +798,17 @@ BufferTextureClient::AllocateForSurface(
   mSize = aSize;
   return true;
 }
 
 gfx::DrawTarget*
 BufferTextureClient::BorrowDrawTarget()
 {
   MOZ_ASSERT(IsValid());
+  MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(mLocked, "BorrowDrawTarget should be called on locked textures only");
   if (!mLocked) {
     return nullptr;
   }
 
   if (mDrawTarget) {
     mDrawTarget->SetTransform(Matrix());
     return mDrawTarget;
@@ -821,16 +824,43 @@ BufferTextureClient::BorrowDrawTarget()
     return mDrawTarget;
   }
 
   mDrawTarget = serializer.GetAsDrawTarget(BackendType::CAIRO);
 
   return mDrawTarget;
 }
 
+void
+BufferTextureClient::UpdateFromSurface(gfx::DataSourceSurface* aSurface)
+{
+  ImageDataSerializer serializer(GetBuffer(), GetBufferSize());
+
+  RefPtr<DataSourceSurface> surface = serializer.GetAsSurface();
+
+  if (surface->GetSize() != aSurface->GetSize() || surface->GetFormat() != aSurface->GetFormat()) {
+    gfxCriticalError() << "Attempt to update texture client from a surface with a different size or format! This: " << surface->GetSize() << " " << surface->GetFormat() << " Other: " << aSurface->GetSize() << " " << aSurface->GetFormat();
+    return;
+  }
+
+  DataSourceSurface::MappedSurface sourceMap;
+  DataSourceSurface::MappedSurface destMap;
+  aSurface->Map(DataSourceSurface::READ, &sourceMap);
+  surface->Map(DataSourceSurface::WRITE, &destMap);
+
+  for (int y = 0; y < aSurface->GetSize().height; y++) {
+    memcpy(destMap.mData + destMap.mStride * y,
+           sourceMap.mData + sourceMap.mStride * y,
+           aSurface->GetSize().width * BytesPerPixel(aSurface->GetFormat()));
+  }
+
+  aSurface->Unmap();
+  surface->Unmap();
+}
+
 bool
 BufferTextureClient::Lock(OpenMode aMode)
 {
   MOZ_ASSERT(!mLocked, "The TextureClient is already Locked!");
   mOpenMode = aMode;
   mLocked = IsValid() && IsAllocated();;
   return mLocked;
 }
--- a/gfx/layers/client/TextureClient.h
+++ b/gfx/layers/client/TextureClient.h
@@ -238,16 +238,17 @@ public:
   virtual void Unlock() {}
 
   virtual bool IsLocked() const = 0;
 
   virtual bool CanExposeDrawTarget() const { return false; }
 
   /**
    * Returns a DrawTarget to draw into the TextureClient.
+   * This function should never be called when not on the main thread!
    *
    * This must never be called on a TextureClient that is not sucessfully locked.
    * When called several times within one Lock/Unlock pair, this method should
    * return the same DrawTarget.
    * The DrawTarget is automatically flushed by the TextureClient when the latter
    * is unlocked, and the DrawTarget that will be returned within the next
    * lock/unlock pair may or may not be the same object.
    * Do not keep references to the DrawTarget outside of the lock/unlock pair.
@@ -263,16 +264,22 @@ public:
    *   DrawTarget* dt = texture->BorrowDrawTarget();
    *   // use the draw target ...
    * }
    * texture->Unlock();
    *
    */
   virtual gfx::DrawTarget* BorrowDrawTarget() { return nullptr; }
 
+  /**
+   * This function can be used to update the contents of the TextureClient
+   * off the main thread.
+   */
+  virtual void UpdateFromSurface(gfx::DataSourceSurface* aSurface) { MOZ_CRASH(); }
+
   // TextureClients that can expose a DrawTarget should override this method.
   virtual gfx::SurfaceFormat GetFormat() const
   {
     return gfx::SurfaceFormat::UNKNOWN;
   }
 
   /**
    * This method is strictly for debugging. It causes locking and
@@ -581,16 +588,18 @@ public:
   virtual bool IsLocked() const override { return mLocked; }
 
   uint8_t* GetLockedData() const;
 
   virtual bool CanExposeDrawTarget() const override { return true; }
 
   virtual gfx::DrawTarget* BorrowDrawTarget() override;
 
+  virtual void UpdateFromSurface(gfx::DataSourceSurface* aSurface) override;
+
   virtual bool AllocateForSurface(gfx::IntSize aSize,
                                   TextureAllocationFlags aFlags = ALLOC_DEFAULT) override;
 
   // TextureClientYCbCr
 
   virtual TextureClientYCbCr* AsTextureClientYCbCr() override { return this; }
 
   virtual bool UpdateYCbCr(const PlanarYCbCrData& aData) override;
--- a/gfx/layers/d3d11/TextureD3D11.cpp
+++ b/gfx/layers/d3d11/TextureD3D11.cpp
@@ -239,17 +239,18 @@ TextureClientD3D11::CreateSimilar(Textur
   }
 
   return tex.forget();
 }
 
 void
 TextureClientD3D11::SyncWithObject(SyncObject* aSyncObject)
 {
-  if (!aSyncObject) {
+  if (!aSyncObject || !NS_IsMainThread()) {
+    // When off the main thread we sync using a keyed mutex per texture.
     return;
   }
 
   MOZ_ASSERT(aSyncObject->GetSyncType() == SyncObject::SyncType::D3D11);
 
   SyncObjectD3D11* sync = static_cast<SyncObjectD3D11*>(aSyncObject);
 
   if (mTexture) {
@@ -273,43 +274,45 @@ TextureClientD3D11::Lock(OpenMode aMode)
   } else {
     MOZ_ASSERT(!mTexture);
     mIsLocked = LockD3DTexture(mTexture10.get());
   }
   if (!mIsLocked) {
     return false;
   }
 
-  // Make sure that successful write-lock means we will have a DrawTarget to
-  // write into.
-  if (aMode & OpenMode::OPEN_WRITE) {
-    mDrawTarget = BorrowDrawTarget();
-    if (!mDrawTarget) {
-      Unlock();
-      return false;
-    }
-  }
-
-  if (mNeedsClear) {
-    mDrawTarget = BorrowDrawTarget();
-    if (!mDrawTarget) {
+  if (NS_IsMainThread()) {
+    // Make sure that successful write-lock means we will have a DrawTarget to
+    // write into.
+    if (aMode & OpenMode::OPEN_WRITE) {
+      mDrawTarget = BorrowDrawTarget();
+      if (!mDrawTarget) {
         Unlock();
         return false;
+      }
     }
-    mDrawTarget->ClearRect(Rect(0, 0, GetSize().width, GetSize().height));
-    mNeedsClear = false;
-  }
-  if (mNeedsClearWhite) {
-    mDrawTarget = BorrowDrawTarget();
-    if (!mDrawTarget) {
+
+    if (mNeedsClear) {
+      mDrawTarget = BorrowDrawTarget();
+      if (!mDrawTarget) {
         Unlock();
         return false;
+      }
+      mDrawTarget->ClearRect(Rect(0, 0, GetSize().width, GetSize().height));
+      mNeedsClear = false;
     }
-    mDrawTarget->FillRect(Rect(0, 0, GetSize().width, GetSize().height), ColorPattern(Color(1.0, 1.0, 1.0, 1.0)));
-    mNeedsClearWhite = false;
+    if (mNeedsClearWhite) {
+      mDrawTarget = BorrowDrawTarget();
+      if (!mDrawTarget) {
+        Unlock();
+        return false;
+      }
+      mDrawTarget->FillRect(Rect(0, 0, GetSize().width, GetSize().height), ColorPattern(Color(1.0, 1.0, 1.0, 1.0)));
+      mNeedsClearWhite = false;
+    }
   }
 
   return true;
 }
 
 void
 TextureClientD3D11::Unlock()
 {
@@ -322,17 +325,17 @@ TextureClientD3D11::Unlock()
     // see the comment on TextureClient::BorrowDrawTarget.
     // This DrawTarget is internal to the TextureClient and is only exposed to the
     // outside world between Lock() and Unlock(). This assertion checks that no outside
     // reference remains by the time Unlock() is called.
     MOZ_ASSERT(mDrawTarget->refCount() == 1);
     mDrawTarget->Flush();
   }
 
-  if (mReadbackSink && mTexture10) {
+  if (NS_IsMainThread() && mReadbackSink && mTexture10) {
     ID3D10Device* device = gfxWindowsPlatform::GetPlatform()->GetD3D10Device();
 
     D3D10_TEXTURE2D_DESC desc;
     mTexture10->GetDesc(&desc);
     desc.BindFlags = 0;
     desc.Usage = D3D10_USAGE_STAGING;
     desc.CPUAccessFlags = D3D10_CPU_ACCESS_READ;
     desc.MiscFlags = 0;
@@ -358,16 +361,17 @@ TextureClientD3D11::Unlock()
   }
   mIsLocked = false;
 }
 
 DrawTarget*
 TextureClientD3D11::BorrowDrawTarget()
 {
   MOZ_ASSERT(mIsLocked, "Calling TextureClient::BorrowDrawTarget without locking :(");
+  MOZ_ASSERT(NS_IsMainThread());
 
   if (!mIsLocked || (!mTexture && !mTexture10)) {
     gfxCriticalError() << "Attempted to borrow a DrawTarget without locking the texture.";
     return nullptr;
   }
 
   if (mDrawTarget) {
     return mDrawTarget;
@@ -382,16 +386,63 @@ TextureClientD3D11::BorrowDrawTarget()
     mDrawTarget = Factory::CreateDrawTargetForD3D10Texture(mTexture10, mFormat);
   }
   if (!mDrawTarget) {
       gfxWarning() << "Invalid draw target for borrowing";
   }
   return mDrawTarget;
 }
 
+void
+TextureClientD3D11::UpdateFromSurface(gfx::DataSourceSurface* aSurface)
+{
+  DataSourceSurface::MappedSurface sourceMap;
+  aSurface->Map(DataSourceSurface::READ, &sourceMap);
+
+  if (mDrawTarget) {
+    // Ensure unflushed work from our outstanding drawtarget won't override this
+    // update later.
+    mDrawTarget->Flush();
+  }
+
+  if (mSize != aSurface->GetSize() || mFormat != aSurface->GetFormat()) {
+    gfxCriticalError() << "Attempt to update texture client from a surface with a different size or format!";
+    return;
+  }
+
+  if (mTexture) {
+    RefPtr<ID3D11Device> device;
+    mTexture->GetDevice(byRef(device));
+    RefPtr<ID3D11DeviceContext> ctx;
+    device->GetImmediateContext(byRef(ctx));
+
+    D3D11_BOX box;
+    box.front = 0;
+    box.back = 1;
+    box.top = box.left = 0;
+    box.right = aSurface->GetSize().width;
+    box.bottom = aSurface->GetSize().height;
+
+    ctx->UpdateSubresource(mTexture, 0, &box, sourceMap.mData, sourceMap.mStride, 0);
+  } else {
+    RefPtr<ID3D10Device> device;
+    mTexture10->GetDevice(byRef(device));
+
+    D3D10_BOX box;
+    box.front = 0;
+    box.back = 1;
+    box.top = box.left = 0;
+    box.right = aSurface->GetSize().width;
+    box.bottom = aSurface->GetSize().height;
+
+    device->UpdateSubresource(mTexture10, 0, &box, sourceMap.mData, sourceMap.mStride, 0);
+  }
+  aSurface->Unmap();
+}
+
 static const GUID sD3D11TextureUsage =
 { 0xd89275b0, 0x6c7d, 0x4038, { 0xb5, 0xfa, 0x4d, 0x87, 0x16, 0xd5, 0xcc, 0x4e } };
 
 /* This class gets its lifetime tied to a D3D texture
  * and increments memory usage on construction and decrements
  * on destruction */
 class TextureMemoryMeasurer : public IUnknown
 {
@@ -442,28 +493,36 @@ TextureClientD3D11::AllocateForSurface(g
   HRESULT hr;
 
   if (mFormat == SurfaceFormat::A8) {
     // Currently TextureClientD3D11 does not support A8 surfaces. Fallback.
     return false;
   }
 
   gfxWindowsPlatform* windowsPlatform = gfxWindowsPlatform::GetPlatform();
-  ID3D11Device* d3d11device = windowsPlatform->GetD3D11ContentDevice();
-  bool haveD3d11Backend = windowsPlatform->GetContentBackend() == BackendType::DIRECT2D1_1;
+  ID3D11Device* d3d11device = windowsPlatform->GetD3D11DeviceForCurrentThread();
+
+  // When we're not on the main thread we're not going to be using Direct2D
+  // to access the contents of this texture client so we will always use D3D11.
+  bool haveD3d11Backend = windowsPlatform->GetContentBackend() == BackendType::DIRECT2D1_1 || !NS_IsMainThread();
 
   if (haveD3d11Backend) {
     MOZ_ASSERT(d3d11device != nullptr);
 
     CD3D11_TEXTURE2D_DESC newDesc(mFormat == SurfaceFormat::A8 ? DXGI_FORMAT_R8_UNORM : DXGI_FORMAT_B8G8R8A8_UNORM,
                                   aSize.width, aSize.height, 1, 1,
                                   D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE);
 
     newDesc.MiscFlags = D3D11_RESOURCE_MISC_SHARED;
 
+    if (!NS_IsMainThread()) {
+      // On the main thread we use the syncobject to handle synchronization.
+      newDesc.MiscFlags = D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX;
+    }
+
     hr = d3d11device->CreateTexture2D(&newDesc, nullptr, byRef(mTexture));
     if (FAILED(hr)) {
       gfxCriticalError(CriticalLog::DefaultOptions(Factory::ReasonableSurfaceSize(aSize))) << "[D3D11] 2 CreateTexture2D failure " << aSize << " Code: " << gfx::hexa(hr);
       return false;
     }
     mTexture->SetPrivateDataInterface(sD3D11TextureUsage,
                                       new TextureMemoryMeasurer(newDesc.Width * newDesc.Height *
                                                                 (mFormat == SurfaceFormat::A8 ?
--- a/gfx/layers/d3d11/TextureD3D11.h
+++ b/gfx/layers/d3d11/TextureD3D11.h
@@ -59,16 +59,18 @@ public:
   virtual gfx::IntSize GetSize() const override { return mSize; }
 
   virtual gfx::SurfaceFormat GetFormat() const override { return mFormat; }
 
   virtual bool CanExposeDrawTarget() const override { return true; }
 
   virtual gfx::DrawTarget* BorrowDrawTarget() override;
 
+  virtual void UpdateFromSurface(gfx::DataSourceSurface* aSurface) override;
+
   virtual bool AllocateForSurface(gfx::IntSize aSize,
                                   TextureAllocationFlags aFlags = ALLOC_DEFAULT) override;
 
   virtual already_AddRefed<TextureClient>
   CreateSimilar(TextureFlags aFlags = TextureFlags::DEFAULT,
                 TextureAllocationFlags aAllocFlags = ALLOC_DEFAULT) const override;
 
   virtual void SyncWithObject(SyncObject* aSyncObject) override;
--- a/gfx/layers/d3d9/TextureD3D9.cpp
+++ b/gfx/layers/d3d9/TextureD3D9.cpp
@@ -493,45 +493,16 @@ TextureClientD3D9::Lock(OpenMode aMode)
     if (FAILED(hr)) {
       NS_WARNING("Failed to get texture surface level.");
       return false;
     }
   }
 
   mIsLocked = true;
 
-  // Make sure that successful write-lock means we will have a DrawTarget to
-  // write into.
-  if (aMode & OpenMode::OPEN_WRITE) {
-    mDrawTarget = BorrowDrawTarget();
-    if (!mDrawTarget) {
-      Unlock();
-      return false;
-    }
-  }
-
-  if (mNeedsClear) {
-    mDrawTarget = BorrowDrawTarget();
-    if (!mDrawTarget) {
-      Unlock();
-      return false;
-    }
-    mDrawTarget->ClearRect(Rect(0, 0, GetSize().width, GetSize().height));
-    mNeedsClear = false;
-  }
-  if (mNeedsClearWhite) {
-    mDrawTarget = BorrowDrawTarget();
-    if (!mDrawTarget) {
-      Unlock();
-      return false;
-    }
-    mDrawTarget->FillRect(Rect(0, 0, GetSize().width, GetSize().height), ColorPattern(Color(1.0, 1.0, 1.0, 1.0)));
-    mNeedsClearWhite = false;
-  }
-
   return true;
 }
 
 void
 TextureClientD3D9::Unlock()
 {
   MOZ_ASSERT(mIsLocked, "Unlocked called while the texture is not locked!");
   if (!mIsLocked) {
@@ -596,19 +567,61 @@ TextureClientD3D9::BorrowDrawTarget()
       return nullptr;
     }
     mDrawTarget =
      gfxPlatform::GetPlatform()->CreateDrawTargetForData((uint8_t*)rect.pBits, mSize,
                                                          rect.Pitch, mFormat);
     mLockRect = true;
   }
 
+  if (mNeedsClear) {
+    mDrawTarget->ClearRect(Rect(0, 0, GetSize().width, GetSize().height));
+    mNeedsClear = false;
+  }
+  if (mNeedsClearWhite) {
+    mDrawTarget->FillRect(Rect(0, 0, GetSize().width, GetSize().height), ColorPattern(Color(1.0, 1.0, 1.0, 1.0)));
+    mNeedsClearWhite = false;
+  }
+
   return mDrawTarget;
 }
 
+void
+TextureClientD3D9::UpdateFromSurface(gfx::DataSourceSurface* aSurface)
+{
+  MOZ_ASSERT(mIsLocked && mD3D9Surface);
+
+  // gfxWindowsSurface don't support transparency so we can't use the d3d9
+  // windows surface optimization.
+  // Instead we have to use a gfxImageSurface and fallback for font drawing.
+  D3DLOCKED_RECT rect;
+  HRESULT hr = mD3D9Surface->LockRect(&rect, nullptr, 0);
+  if (FAILED(hr) || !rect.pBits) {
+    gfxCriticalError() << "Failed to lock rect borrowing the target in D3D9 " << hexa(hr);
+    return;
+  }
+
+  if (mSize != aSurface->GetSize() || mFormat != aSurface->GetFormat()) {
+    gfxCriticalError() << "Attempt to update texture client from a surface with a different size or format! This: " << mSize << " " << mFormat << " Other: " << aSurface->GetSize() << " " << aSurface->GetFormat();
+    return;
+  }
+
+  DataSourceSurface::MappedSurface sourceMap;
+  aSurface->Map(DataSourceSurface::READ, &sourceMap);
+
+  for (int y = 0; y < aSurface->GetSize().height; y++) {
+    memcpy((uint8_t*)rect.pBits + rect.Pitch * y,
+           sourceMap.mData + sourceMap.mStride * y,
+           aSurface->GetSize().width * BytesPerPixel(aSurface->GetFormat()));
+  }
+
+  aSurface->Unmap();
+  mD3D9Surface->UnlockRect();
+}
+
 bool
 TextureClientD3D9::AllocateForSurface(gfx::IntSize aSize, TextureAllocationFlags aFlags)
 {
   MOZ_ASSERT(!IsAllocated());
   mSize = aSize;
   _D3DFORMAT format = SurfaceFormatToD3D9Format(mFormat);
 
   DeviceManagerD3D9* deviceManager = gfxWindowsPlatform::GetPlatform()->GetD3D9DeviceManager();
--- a/gfx/layers/d3d9/TextureD3D9.h
+++ b/gfx/layers/d3d9/TextureD3D9.h
@@ -197,16 +197,18 @@ public:
   virtual gfx::IntSize GetSize() const { return mSize; }
 
   virtual gfx::SurfaceFormat GetFormat() const { return mFormat; }
 
   virtual bool CanExposeDrawTarget() const override { return true; }
 
   virtual gfx::DrawTarget* BorrowDrawTarget() override;
 
+  virtual void UpdateFromSurface(gfx::DataSourceSurface* aSurface) override;
+
   virtual bool AllocateForSurface(gfx::IntSize aSize,
                                   TextureAllocationFlags aFlags = ALLOC_DEFAULT) override;
 
   virtual bool HasInternalBuffer() const override { return true; }
 
   virtual already_AddRefed<TextureClient>
   CreateSimilar(TextureFlags aFlags = TextureFlags::DEFAULT,
                 TextureAllocationFlags aAllocFlags = ALLOC_DEFAULT) const override;
--- a/gfx/layers/opengl/GrallocTextureClient.cpp
+++ b/gfx/layers/opengl/GrallocTextureClient.cpp
@@ -209,16 +209,50 @@ GrallocTextureClientOGL::BorrowDrawTarge
   long byteStride = pixelStride * BytesPerPixel(format);
   mDrawTarget = gfxPlatform::GetPlatform()->CreateDrawTargetForData(GetBuffer(),
                                                                     mSize,
                                                                     byteStride,
                                                                     mFormat);
   return mDrawTarget;
 }
 
+void
+GrallocTextureClientOGL::UpdateFromSurface(gfx::DataSourceSurface* aSurface)
+{
+  MOZ_ASSERT(IsValid());
+  MOZ_ASSERT(mMappedBuffer, "Calling TextureClient::BorrowDrawTarget without locking :(");
+
+  if (!IsValid() || !IsAllocated() || !mMappedBuffer) {
+    return;
+  }
+
+  gfx::SurfaceFormat format = SurfaceFormatForPixelFormat(mGraphicBuffer->getPixelFormat());
+  if (mSize != aSurface->GetSize() || mFormat != aSurface->GetFormat()) {
+    gfxCriticalError() << "Attempt to update texture client from a surface with a different size or format! This: " << mSize << " " << format << " Other: " << aSurface->GetSize() << " " << aSurface->GetFormat();
+    return;
+  }
+
+  long pixelStride = mGraphicBuffer->getStride();
+  long byteStride = pixelStride * BytesPerPixel(format);
+
+  DataSourceSurface::MappedSurface sourceMap;
+
+  aSurface->Map(DataSourceSurface::READ, &sourceMap);
+
+  uint8_t* buffer = GetBuffer();
+
+  for (int y = 0; y < aSurface->GetSize().height; y++) {
+    memcpy(buffer + byteStride * y,
+           sourceMap.mData + sourceMap.mStride * y,
+           aSurface->GetSize().width * BytesPerPixel(aSurface->GetFormat()));
+  }
+
+  aSurface->Unmap();
+}
+
 bool
 GrallocTextureClientOGL::AllocateForSurface(gfx::IntSize aSize,
                                             TextureAllocationFlags)
 {
   MOZ_ASSERT(IsValid());
 
   uint32_t format;
   uint32_t usage = android::GraphicBuffer::USAGE_SW_READ_OFTEN |
--- a/gfx/layers/opengl/GrallocTextureClient.h
+++ b/gfx/layers/opengl/GrallocTextureClient.h
@@ -77,16 +77,18 @@ public:
   {
     return mGraphicBuffer->getPixelFormat();
   }
 
   virtual uint8_t* GetBuffer() const override;
 
   virtual gfx::DrawTarget* BorrowDrawTarget() override;
 
+  virtual void UpdateFromSurface(gfx::DataSourceSurface* aSurface) override;
+
   virtual bool AllocateForSurface(gfx::IntSize aSize,
                                   TextureAllocationFlags aFlags = ALLOC_DEFAULT) override;
 
   virtual bool AllocateForYCbCr(gfx::IntSize aYSize,
                                 gfx::IntSize aCbCrSize,
                                 StereoMode aStereoMode) override;
 
   bool AllocateForGLRendering(gfx::IntSize aSize);
--- a/gfx/thebes/gfxWindowsPlatform.cpp
+++ b/gfx/thebes/gfxWindowsPlatform.cpp
@@ -552,16 +552,17 @@ gfxWindowsPlatform::UpdateRenderMode()
       mD3D11Device = nullptr;
       mD3D11ContentDevice = nullptr;
       mAdapter = nullptr;
       mDeviceResetReason = DeviceResetReason::OK;
       mHasDeviceReset = false;
 
       imgLoader::Singleton()->ClearCache(true);
       imgLoader::Singleton()->ClearCache(false);
+      gfxAlphaBoxBlur::ShutdownBlurCache();
       Factory::SetDirect3D11Device(nullptr);
 
       didReset = true;
     }
 
     mRenderMode = RENDER_GDI;
     mUseDirectWrite = gfxPrefs::DirectWriteFontRenderingEnabled();
 
@@ -1583,16 +1584,25 @@ gfxWindowsPlatform::GetD3D11ImageBridgeD
     return mD3D11ImageBridgeDevice;
   }
 
   InitD3D11Devices();
 
   return mD3D11ImageBridgeDevice;
 }
 
+ID3D11Device*
+gfxWindowsPlatform::GetD3D11DeviceForCurrentThread()
+{
+  if (NS_IsMainThread()) {
+    return GetD3D11ContentDevice();
+  } else {
+    return GetD3D11ImageBridgeDevice();
+  }
+}
 
 ReadbackManagerD3D11*
 gfxWindowsPlatform::GetReadbackManager()
 {
   if (!mD3D11ReadbackManager) {
     mD3D11ReadbackManager = new ReadbackManagerD3D11();
   }
 
@@ -1895,25 +1905,17 @@ bool DoesD3D11TextureSharingWorkInternal
     return false;
   }
 
   return true;
 }
 
 bool DoesD3D11TextureSharingWork(ID3D11Device *device)
 {
-  static bool checked;
-  static bool result;
-
-  if (checked)
-    return result;
-  checked = true;
-
-  result = DoesD3D11TextureSharingWorkInternal(device, DXGI_FORMAT_B8G8R8A8_UNORM, D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE);
-  return result;
+  return DoesD3D11TextureSharingWorkInternal(device, DXGI_FORMAT_B8G8R8A8_UNORM, D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE);
 }
 
 bool DoesD3D11AlphaTextureSharingWork(ID3D11Device *device)
 {
   return DoesD3D11TextureSharingWorkInternal(device, DXGI_FORMAT_R8_UNORM, D3D11_BIND_SHADER_RESOURCE);
 }
 
 auto
--- a/gfx/thebes/gfxWindowsPlatform.h
+++ b/gfx/thebes/gfxWindowsPlatform.h
@@ -239,16 +239,17 @@ public:
     inline bool DWriteEnabled() { return false; }
 #endif
     void OnDeviceManagerDestroy(mozilla::layers::DeviceManagerD3D9* aDeviceManager);
     mozilla::layers::DeviceManagerD3D9* GetD3D9DeviceManager();
     IDirect3DDevice9* GetD3D9Device();
     ID3D10Device1 *GetD3D10Device() { return mD3D10Device; }
     ID3D11Device *GetD3D11Device();
     ID3D11Device *GetD3D11ContentDevice();
+    ID3D11Device* GetD3D11DeviceForCurrentThread();
     // Device to be used on the ImageBridge thread
     ID3D11Device *GetD3D11ImageBridgeDevice();
 
     // Create a D3D11 device to be used for DXVA decoding.
     already_AddRefed<ID3D11Device> CreateD3D11DecoderDevice();
 
     mozilla::layers::ReadbackManagerD3D11* GetReadbackManager();
 
--- a/ipc/glue/GeckoChildProcessHost.cpp
+++ b/ipc/glue/GeckoChildProcessHost.cpp
@@ -120,17 +120,18 @@ GeckoChildProcessHost::~GeckoChildProces
 
   MOZ_COUNT_DTOR(GeckoChildProcessHost);
 
   if (mChildProcessHandle > 0) {
 #if defined(MOZ_WIDGET_COCOA)
     SharedMemoryBasic::CleanupForPid(mChildProcessHandle);
 #endif
     ProcessWatcher::EnsureProcessTerminated(mChildProcessHandle
-#if defined(NS_BUILD_REFCNT_LOGGING)
+#if defined(NS_BUILD_REFCNT_LOGGING) || defined(MOZ_ASAN)
+    // If we're doing leak logging, shutdown can be slow.
                                             , false // don't "force"
 #endif
     );
   }
 
 #if defined(MOZ_WIDGET_COCOA)
   if (mChildTask != MACH_PORT_NULL)
     mach_port_deallocate(mach_task_self(), mChildTask);
@@ -595,42 +596,29 @@ GeckoChildProcessHost::PerformAsyncLaunc
     MOZ_ASSERT(gGREBinPath);
     nsCString path;
     NS_CopyUnicodeToNative(nsDependentString(gGREBinPath), path);
 # if defined(OS_LINUX) || defined(OS_BSD)
 #  if defined(MOZ_WIDGET_ANDROID)
     path += "/lib";
 #  endif  // MOZ_WIDGET_ANDROID
     const char *ld_library_path = PR_GetEnv("LD_LIBRARY_PATH");
-    nsCString new_ld_lib_path;
-    if (ld_library_path && *ld_library_path) {
-      new_ld_lib_path.Assign(path.get());
-      new_ld_lib_path.Append(':');
-      new_ld_lib_path.Append(ld_library_path);
-      newEnvVars["LD_LIBRARY_PATH"] = new_ld_lib_path.get();
-    } else {
-      newEnvVars["LD_LIBRARY_PATH"] = path.get();
-    }
+    nsCString new_ld_lib_path(path.get());
 
 #  if (MOZ_WIDGET_GTK == 3)
     if (mProcessType == GeckoProcessType_Plugin) {
-      const char *ld_preload = PR_GetEnv("LD_PRELOAD");
-      nsCString new_ld_preload;
-
-      new_ld_preload.Assign(path.get());
-      new_ld_preload.AppendLiteral("/" DLL_PREFIX "mozgtk2" DLL_SUFFIX);
-
-      if (ld_preload && *ld_preload) {
-        new_ld_preload.AppendLiteral(":");
-        new_ld_preload.Append(ld_preload);
-      }
-      newEnvVars["LD_PRELOAD"] = new_ld_preload.get();
+      new_ld_lib_path.Append("/gtk2:");
+      new_ld_lib_path.Append(path.get());
     }
-#  endif // MOZ_WIDGET_GTK
-
+#endif
+    if (ld_library_path && *ld_library_path) {
+      new_ld_lib_path.Append(':');
+      new_ld_lib_path.Append(ld_library_path);
+    }
+    newEnvVars["LD_LIBRARY_PATH"] = new_ld_lib_path.get();
 
 # elif OS_MACOSX
     newEnvVars["DYLD_LIBRARY_PATH"] = path.get();
     // XXX DYLD_INSERT_LIBRARIES should only be set when launching a plugin
     //     process, and has no effect on other subprocesses (the hooks in
     //     libplugin_child_interpose.dylib become noops).  But currently it
     //     gets set when launching any kind of subprocess.
     //
--- a/ipc/ipdl/ipdl/lower.py
+++ b/ipc/ipdl/ipdl/lower.py
@@ -2052,16 +2052,22 @@ def _generateCxxStruct(sd):
 
     # If this is an empty struct (no fields), then the default ctor
     # and "create-with-fields" ctors are equivalent.  So don't bother
     # with the default ctor.
     if len(sd.fields):
         # Struct()
         defctor = ConstructorDefn(ConstructorDecl(sd.name))
         defctor.addstmt(StmtExpr(callinit))
+        defctor.memberinits = []
+        for f in sd.fields:
+          # Only generate default values for primitives.
+          if not (f.ipdltype.isCxx() and f.ipdltype.isAtom()):
+            continue
+          defctor.memberinits.append(ExprMemberInit(f.memberVar()))
         struct.addstmts([ defctor, Whitespace.NL ])
 
     # Struct(const field1& _f1, ...)
     valctor = ConstructorDefn(ConstructorDecl(sd.name,
                                               params=fieldsAsParamList(),
                                               force_inline=1))
     valctor.addstmts([
         StmtExpr(callinit),
--- a/js/src/builtin/ReflectParse.cpp
+++ b/js/src/builtin/ReflectParse.cpp
@@ -42,17 +42,17 @@ enum ASTType {
 };
 
 enum AssignmentOperator {
     AOP_ERR = -1,
 
     /* assign */
     AOP_ASSIGN = 0,
     /* operator-assign */
-    AOP_PLUS, AOP_MINUS, AOP_STAR, AOP_DIV, AOP_MOD,
+    AOP_PLUS, AOP_MINUS, AOP_STAR, AOP_DIV, AOP_MOD, AOP_POW,
     /* shift-assign */
     AOP_LSH, AOP_RSH, AOP_URSH,
     /* binary */
     AOP_BITOR, AOP_BITXOR, AOP_BITAND,
 
     AOP_LIMIT
 };
 
@@ -61,17 +61,17 @@ enum BinaryOperator {
 
     /* eq */
     BINOP_EQ = 0, BINOP_NE, BINOP_STRICTEQ, BINOP_STRICTNE,
     /* rel */
     BINOP_LT, BINOP_LE, BINOP_GT, BINOP_GE,
     /* shift */
     BINOP_LSH, BINOP_RSH, BINOP_URSH,
     /* arithmetic */
-    BINOP_ADD, BINOP_SUB, BINOP_STAR, BINOP_DIV, BINOP_MOD,
+    BINOP_ADD, BINOP_SUB, BINOP_STAR, BINOP_DIV, BINOP_MOD, BINOP_POW,
     /* binary */
     BINOP_BITOR, BINOP_BITXOR, BINOP_BITAND,
     /* misc */
     BINOP_IN, BINOP_INSTANCEOF,
 
     BINOP_LIMIT
 };
 
@@ -108,16 +108,17 @@ enum PropKind {
 
 static const char* const aopNames[] = {
     "=",    /* AOP_ASSIGN */
     "+=",   /* AOP_PLUS */
     "-=",   /* AOP_MINUS */
     "*=",   /* AOP_STAR */
     "/=",   /* AOP_DIV */
     "%=",   /* AOP_MOD */
+    "**=",  /* AOP_POW */
     "<<=",  /* AOP_LSH */
     ">>=",  /* AOP_RSH */
     ">>>=", /* AOP_URSH */
     "|=",   /* AOP_BITOR */
     "^=",   /* AOP_BITXOR */
     "&="    /* AOP_BITAND */
 };
 
@@ -133,16 +134,17 @@ static const char* const binopNames[] = 
     "<<",         /* BINOP_LSH */
     ">>",         /* BINOP_RSH */
     ">>>",        /* BINOP_URSH */
     "+",          /* BINOP_PLUS */
     "-",          /* BINOP_MINUS */
     "*",          /* BINOP_STAR */
     "/",          /* BINOP_DIV */
     "%",          /* BINOP_MOD */
+    "**",         /* BINOP_POW */
     "|",          /* BINOP_BITOR */
     "^",          /* BINOP_BITXOR */
     "&",          /* BINOP_BITAND */
     "in",         /* BINOP_IN */
     "instanceof", /* BINOP_INSTANCEOF */
 };
 
 static const char* const unopNames[] = {
@@ -1853,16 +1855,17 @@ class ASTSerializer
 
     BinaryOperator binop(ParseNodeKind kind, JSOp op);
     UnaryOperator unop(ParseNodeKind kind, JSOp op);
     AssignmentOperator aop(JSOp op);
 
     bool statements(ParseNode* pn, NodeVector& elts);
     bool expressions(ParseNode* pn, NodeVector& elts);
     bool leftAssociate(ParseNode* pn, MutableHandleValue dst);
+    bool rightAssociate(ParseNode* pn, MutableHandleValue dst);
     bool functionArgs(ParseNode* pn, ParseNode* pnargs, ParseNode* pnbody,
                       NodeVector& args, NodeVector& defaults, MutableHandleValue rest);
 
     bool sourceElement(ParseNode* pn, MutableHandleValue dst);
 
     bool declaration(ParseNode* pn, MutableHandleValue dst);
     bool variableDeclaration(ParseNode* pn, bool lexical, MutableHandleValue dst);
     bool variableDeclarator(ParseNode* pn, MutableHandleValue dst);
@@ -1969,16 +1972,18 @@ ASTSerializer::aop(JSOp op)
       case JSOP_SUB:
         return AOP_MINUS;
       case JSOP_MUL:
         return AOP_STAR;
       case JSOP_DIV:
         return AOP_DIV;
       case JSOP_MOD:
         return AOP_MOD;
+      case JSOP_POW:
+        return AOP_POW;
       case JSOP_LSH:
         return AOP_LSH;
       case JSOP_RSH:
         return AOP_RSH;
       case JSOP_URSH:
         return AOP_URSH;
       case JSOP_BITOR:
         return AOP_BITOR;
@@ -2047,16 +2052,18 @@ ASTSerializer::binop(ParseNodeKind kind,
       case PNK_SUB:
         return BINOP_SUB;
       case PNK_STAR:
         return BINOP_STAR;
       case PNK_DIV:
         return BINOP_DIV;
       case PNK_MOD:
         return BINOP_MOD;
+      case PNK_POW:
+        return BINOP_POW;
       case PNK_BITOR:
         return BINOP_BITOR;
       case PNK_BITXOR:
         return BINOP_BITXOR;
       case PNK_BITAND:
         return BINOP_BITAND;
       case PNK_IN:
         return BINOP_IN;
@@ -2782,16 +2789,59 @@ ASTSerializer::leftAssociate(ParseNode* 
         }
     }
 
     dst.set(left);
     return true;
 }
 
 bool
+ASTSerializer::rightAssociate(ParseNode* pn, MutableHandleValue dst)
+{
+    MOZ_ASSERT(pn->isArity(PN_LIST));
+    MOZ_ASSERT(pn->pn_count >= 1);
+
+    // First, we need to reverse the list, so that we can traverse it in the right order.
+    // It's OK to destructively reverse the list, because there are no other consumers.
+
+    ParseNode* head = pn->pn_head;
+    ParseNode* prev = nullptr;
+    ParseNode* current = head;
+    ParseNode* next;
+    while (current != nullptr) {
+        next = current->pn_next;
+        current->pn_next = prev;
+        prev = current;
+        current = next;
+    }
+
+    head = prev;
+
+    RootedValue right(cx);
+    if (!expression(head, &right))
+        return false;
+    for (ParseNode* next = head->pn_next; next; next = next->pn_next) {
+        RootedValue left(cx);
+        if (!expression(next, &left))
+            return false;
+
+        TokenPos subpos(pn->pn_pos.begin, next->pn_pos.end);
+
+        BinaryOperator op = binop(pn->getKind(), pn->getOp());
+        LOCAL_ASSERT(op > BINOP_ERR && op < BINOP_LIMIT);
+
+        if (!builder.binaryExpression(op, left, right, &subpos, &right))
+            return false;
+    }
+
+    dst.set(right);
+    return true;
+}
+
+bool
 ASTSerializer::comprehensionBlock(ParseNode* pn, MutableHandleValue dst)
 {
     LOCAL_ASSERT(pn->isArity(PN_BINARY));
 
     ParseNode* in = pn->pn_left;
 
     LOCAL_ASSERT(in && (in->isKind(PNK_FORIN) || in->isKind(PNK_FOROF)));
 
@@ -2969,16 +3019,17 @@ ASTSerializer::expression(ParseNode* pn,
       case PNK_BITXORASSIGN:
       case PNK_BITANDASSIGN:
       case PNK_LSHASSIGN:
       case PNK_RSHASSIGN:
       case PNK_URSHASSIGN:
       case PNK_MULASSIGN:
       case PNK_DIVASSIGN:
       case PNK_MODASSIGN:
+      case PNK_POWASSIGN:
       {
         MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_left->pn_pos));
         MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_right->pn_pos));
 
         AssignmentOperator op = aop(pn->getOp());
         LOCAL_ASSERT(op > AOP_ERR && op < AOP_LIMIT);
 
         RootedValue lhs(cx), rhs(cx);
@@ -3005,16 +3056,19 @@ ASTSerializer::expression(ParseNode* pn,
       case PNK_MOD:
       case PNK_BITOR:
       case PNK_BITXOR:
       case PNK_BITAND:
       case PNK_IN:
       case PNK_INSTANCEOF:
         return leftAssociate(pn, dst);
 
+      case PNK_POW:
+	return rightAssociate(pn, dst);
+
       case PNK_DELETENAME:
       case PNK_DELETEPROP:
       case PNK_DELETESUPERPROP:
       case PNK_DELETEELEM:
       case PNK_DELETESUPERELEM:
       case PNK_DELETEEXPR:
       case PNK_TYPEOFNAME:
       case PNK_TYPEOFEXPR:
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -2034,16 +2034,17 @@ BytecodeEmitter::checkSideEffects(ParseN
       case PNK_BITXORASSIGN:
       case PNK_BITANDASSIGN:
       case PNK_LSHASSIGN:
       case PNK_RSHASSIGN:
       case PNK_URSHASSIGN:
       case PNK_MULASSIGN:
       case PNK_DIVASSIGN:
       case PNK_MODASSIGN:
+      case PNK_POWASSIGN:
         MOZ_ASSERT(pn->isArity(PN_BINARY));
         *answer = true;
         return true;
 
       case PNK_STATEMENTLIST:
       case PNK_CATCHLIST:
       // Strict equality operations and logical operators are well-behaved and
       // perform no conversions.
@@ -2086,16 +2087,17 @@ BytecodeEmitter::checkSideEffects(ParseN
       case PNK_LSH:
       case PNK_RSH:
       case PNK_URSH:
       case PNK_ADD:
       case PNK_SUB:
       case PNK_STAR:
       case PNK_DIV:
       case PNK_MOD:
+      case PNK_POW:
         MOZ_ASSERT(pn->isArity(PN_LIST));
         MOZ_ASSERT(pn->pn_count >= 2);
         *answer = true;
         return true;
 
       case PNK_DEFAULT:
       case PNK_COLON:
       case PNK_CASE:
@@ -7725,16 +7727,17 @@ BytecodeEmitter::emitTree(ParseNode* pn)
       case PNK_BITXORASSIGN:
       case PNK_BITANDASSIGN:
       case PNK_LSHASSIGN:
       case PNK_RSHASSIGN:
       case PNK_URSHASSIGN:
       case PNK_MULASSIGN:
       case PNK_DIVASSIGN:
       case PNK_MODASSIGN:
+      case PNK_POWASSIGN:
         if (!emitAssignment(pn->pn_left, pn->getOp(), pn->pn_right))
             return false;
         break;
 
       case PNK_CONDITIONAL:
         ok = emitConditionalExpression(pn->as<ConditionalExpression>());
         break;
 
@@ -7774,16 +7777,30 @@ BytecodeEmitter::emitTree(ParseNode* pn)
             if (!emitTree(subexpr))
                 return false;
             if (!emit1(op))
                 return false;
         }
         break;
       }
 
+      case PNK_POW: {
+        MOZ_ASSERT(pn->isArity(PN_LIST));
+        /* Right-associative operator chain. */
+        for (ParseNode* subexpr = pn->pn_head; subexpr; subexpr = subexpr->pn_next) {
+            if (!emitTree(subexpr))
+                return false;
+        }
+        for (uint32_t i = 0; i < pn->pn_count - 1; i++) {
+            if (!emit1(JSOP_POW))
+                return false;
+        }
+        break;
+      }
+
       case PNK_TYPEOFNAME:
         ok = emitTypeof(pn, JSOP_TYPEOF);
         break;
 
       case PNK_TYPEOFEXPR:
         ok = emitTypeof(pn, JSOP_TYPEOFEXPR);
         break;
 
--- a/js/src/frontend/FoldConstants.cpp
+++ b/js/src/frontend/FoldConstants.cpp
@@ -358,28 +358,30 @@ ContainsHoistedDeclaration(ExclusiveCont
       case PNK_LSH:
       case PNK_RSH:
       case PNK_URSH:
       case PNK_ADD:
       case PNK_SUB:
       case PNK_STAR:
       case PNK_DIV:
       case PNK_MOD:
+      case PNK_POW:
       case PNK_ASSIGN:
       case PNK_ADDASSIGN:
       case PNK_SUBASSIGN:
       case PNK_BITORASSIGN:
       case PNK_BITXORASSIGN:
       case PNK_BITANDASSIGN:
       case PNK_LSHASSIGN:
       case PNK_RSHASSIGN:
       case PNK_URSHASSIGN:
       case PNK_MULASSIGN:
       case PNK_DIVASSIGN:
       case PNK_MODASSIGN:
+      case PNK_POWASSIGN:
       case PNK_COMMA:
       case PNK_ARRAY:
       case PNK_OBJECT:
       case PNK_DOT:
       case PNK_ELEM:
       case PNK_CALL:
       case PNK_NAME:
       case PNK_TEMPLATE_STRING:
@@ -525,16 +527,20 @@ FoldBinaryNumeric(ExclusiveContext* cx, 
       case JSOP_MOD:
         if (d2 == 0) {
             d = GenericNaN();
         } else {
             d = js_fmod(d, d2);
         }
         break;
 
+      case JSOP_POW:
+        d = ecmaPow(d, d2);
+        break;
+
       default:;
     }
 
     /* Take care to allow pn1 or pn2 to alias pn. */
     pn->setKind(PNK_NUMBER);
     pn->setOp(JSOP_DOUBLE);
     pn->setArity(PN_NULLARY);
     pn->pn_dval = d;
@@ -599,126 +605,125 @@ enum class SyntacticContext : int {
 
 static SyntacticContext
 condIf(const ParseNode* pn, ParseNodeKind kind)
 {
     return pn->isKind(kind) ? SyntacticContext::Condition : SyntacticContext::Other;
 }
 
 static bool
-Fold(ExclusiveContext* cx, ParseNode** pnp,
-     FullParseHandler& handler, const ReadOnlyCompileOptions& options,
-     bool inGenexpLambda, SyntacticContext sc);
+Fold(ExclusiveContext* cx, ParseNode** pnp, Parser<FullParseHandler>& parser, bool inGenexpLambda,
+     SyntacticContext sc);
 
 static bool
-FoldTypeOfExpr(ExclusiveContext* cx, ParseNode* node, FullParseHandler& handler,
-               const ReadOnlyCompileOptions& options, bool inGenexpLambda)
+FoldTypeOfExpr(ExclusiveContext* cx, ParseNode* node, Parser<FullParseHandler>& parser,
+               bool inGenexpLambda)
 {
     MOZ_ASSERT(node->isKind(PNK_TYPEOFEXPR));
     MOZ_ASSERT(node->isArity(PN_UNARY));
 
     ParseNode*& expr = node->pn_kid;
-    if (!Fold(cx, &expr, handler, options, inGenexpLambda, SyntacticContext::Other))
+    if (!Fold(cx, &expr, parser, inGenexpLambda, SyntacticContext::Other))
         return false;
 
     // Constant-fold the entire |typeof| if given a constant with known type.
     RootedPropertyName result(cx);
     if (expr->isKind(PNK_STRING) || expr->isKind(PNK_TEMPLATE_STRING))
         result = cx->names().string;
     else if (expr->isKind(PNK_NUMBER))
         result = cx->names().number;
     else if (expr->isKind(PNK_NULL))
         result = cx->names().object;
     else if (expr->isKind(PNK_TRUE) || expr->isKind(PNK_FALSE))
         result = cx->names().boolean;
     else if (expr->isKind(PNK_FUNCTION))
         result = cx->names().function;
 
     if (result) {
-        handler.prepareNodeForMutation(node);
+        parser.prepareNodeForMutation(node);
 
         node->setKind(PNK_STRING);
         node->setArity(PN_NULLARY);
         node->setOp(JSOP_NOP);
         node->pn_atom = result;
     }
 
     return true;
 }
 
 static bool
-FoldVoid(ExclusiveContext* cx, ParseNode* node, FullParseHandler& handler,
-         const ReadOnlyCompileOptions& options, bool inGenexpLambda, SyntacticContext sc)
+FoldVoid(ExclusiveContext* cx, ParseNode* node, Parser<FullParseHandler>& parser,
+         bool inGenexpLambda, SyntacticContext sc)
 {
     MOZ_ASSERT(node->isKind(PNK_VOID));
     MOZ_ASSERT(node->isArity(PN_UNARY));
 
     ParseNode*& expr = node->pn_kid;
-    if (!Fold(cx, &expr, handler, options, inGenexpLambda, SyntacticContext::Other))
+    if (!Fold(cx, &expr, parser, inGenexpLambda, SyntacticContext::Other))
         return false;
 
     if (sc == SyntacticContext::Condition) {
         if (expr->isKind(PNK_TRUE) ||
             expr->isKind(PNK_FALSE) ||
             expr->isKind(PNK_STRING) ||
             expr->isKind(PNK_TEMPLATE_STRING) ||
             expr->isKind(PNK_NUMBER) ||
             expr->isKind(PNK_NULL) ||
             expr->isKind(PNK_FUNCTION))
         {
-            handler.prepareNodeForMutation(node);
+            parser.prepareNodeForMutation(node);
             node->setKind(PNK_FALSE);
             node->setArity(PN_NULLARY);
             node->setOp(JSOP_FALSE);
         }
     }
 
     return true;
 }
 
 static bool
-FoldDeleteExpr(ExclusiveContext* cx, ParseNode* node, FullParseHandler& handler,
-               const ReadOnlyCompileOptions& options, bool inGenexpLambda)
+FoldDeleteExpr(ExclusiveContext* cx, ParseNode* node, Parser<FullParseHandler>& parser,
+               bool inGenexpLambda)
 {
     MOZ_ASSERT(node->isKind(PNK_DELETEEXPR));
     MOZ_ASSERT(node->isArity(PN_UNARY));
 
     ParseNode*& expr = node->pn_kid;
-    if (!Fold(cx, &expr, handler, options, inGenexpLambda, SyntacticContext::Other))
+    if (!Fold(cx, &expr, parser, inGenexpLambda, SyntacticContext::Other))
         return false;
 
     // Expression deletion evaluates the expression, then evaluates to
     // true.  For trivial expressions, eliminate the expression evaluation.
     if (expr->isKind(PNK_TRUE) ||
         expr->isKind(PNK_FALSE) ||
         expr->isKind(PNK_STRING) ||
         expr->isKind(PNK_TEMPLATE_STRING) ||
         expr->isKind(PNK_NUMBER) ||
         expr->isKind(PNK_NULL) ||
         expr->isKind(PNK_FUNCTION))
     {
-        handler.prepareNodeForMutation(node);
+        parser.prepareNodeForMutation(node);
         node->setKind(PNK_TRUE);
         node->setArity(PN_NULLARY);
         node->setOp(JSOP_TRUE);
     }
 
     return true;
 }
 
 static bool
-FoldDeleteElement(ExclusiveContext* cx, ParseNode* node, FullParseHandler& handler,
-                  const ReadOnlyCompileOptions& options, bool inGenexpLambda)
+FoldDeleteElement(ExclusiveContext* cx, ParseNode* node, Parser<FullParseHandler>& parser,
+                  bool inGenexpLambda)
 {
     MOZ_ASSERT(node->isKind(PNK_DELETEELEM) || node->isKind(PNK_DELETESUPERELEM));
     MOZ_ASSERT(node->isArity(PN_UNARY));
     MOZ_ASSERT(node->pn_kid->isKind(PNK_ELEM) || node->pn_kid->isKind(PNK_SUPERELEM));
 
     ParseNode*& expr = node->pn_kid;
-    if (!Fold(cx, &expr, handler, options, inGenexpLambda, SyntacticContext::Other))
+    if (!Fold(cx, &expr, parser, inGenexpLambda, SyntacticContext::Other))
         return false;
 
     // If we're deleting an element, but constant-folding converted our
     // element reference into a dotted property access, we must *also*
     // morph the node's kind.
     //
     // In principle this also applies to |super["foo"] -> super.foo|,
     // but we don't constant-fold |super["foo"]| yet.
@@ -727,41 +732,131 @@ FoldDeleteElement(ExclusiveContext* cx, 
         if (expr->isKind(PNK_DOT))
             node->setKind(PNK_DELETEPROP);
     }
 
     return true;
 }
 
 static bool
-FoldDeleteProperty(ExclusiveContext* cx, ParseNode* node, FullParseHandler& handler,
-                   const ReadOnlyCompileOptions& options, bool inGenexpLambda)
+FoldDeleteProperty(ExclusiveContext* cx, ParseNode* node, Parser<FullParseHandler>& parser,
+                   bool inGenexpLambda)
 {
     MOZ_ASSERT(node->isKind(PNK_DELETEPROP) || node->isKind(PNK_DELETESUPERPROP));
     MOZ_ASSERT(node->isArity(PN_UNARY));
     MOZ_ASSERT(node->pn_kid->isKind(PNK_DOT) || node->pn_kid->isKind(PNK_SUPERPROP));
 
     ParseNode*& expr = node->pn_kid;
 #ifdef DEBUG
     ParseNodeKind oldKind = expr->getKind();
 #endif
 
-    if (!Fold(cx, &expr, handler, options, inGenexpLambda, SyntacticContext::Other))
+    if (!Fold(cx, &expr, parser, inGenexpLambda, SyntacticContext::Other))
         return false;
 
     MOZ_ASSERT(expr->isKind(oldKind),
                "kind should have remained invariant under folding");
 
     return true;
 }
 
+static bool
+FoldNot(ExclusiveContext* cx, ParseNode* node, Parser<FullParseHandler>& parser,
+        bool inGenexpLambda)
+{
+    MOZ_ASSERT(node->isKind(PNK_NOT));
+    MOZ_ASSERT(node->isArity(PN_UNARY));
+
+    ParseNode*& expr = node->pn_kid;
+    if (!Fold(cx, &expr, parser, inGenexpLambda, SyntacticContext::Condition))
+        return false;
+
+    if (expr->isKind(PNK_NUMBER)) {
+        double d = expr->pn_dval;
+
+        parser.prepareNodeForMutation(node);
+        if (d == 0 || IsNaN(d)) {
+            node->setKind(PNK_TRUE);
+            node->setOp(JSOP_TRUE);
+        } else {
+            node->setKind(PNK_FALSE);
+            node->setOp(JSOP_FALSE);
+        }
+        node->setArity(PN_NULLARY);
+    } else if (expr->isKind(PNK_TRUE) || expr->isKind(PNK_FALSE)) {
+        bool newval = !expr->isKind(PNK_TRUE);
+
+        parser.prepareNodeForMutation(node);
+        node->setKind(newval ? PNK_TRUE : PNK_FALSE);
+        node->setArity(PN_NULLARY);
+        node->setOp(newval ? JSOP_TRUE : JSOP_FALSE);
+    }
+
+    return true;
+}
+
+static bool
+FoldUnaryArithmetic(ExclusiveContext* cx, ParseNode* node, Parser<FullParseHandler>& parser,
+                    bool inGenexpLambda)
+{
+    MOZ_ASSERT(node->isKind(PNK_BITNOT) || node->isKind(PNK_POS) || node->isKind(PNK_NEG),
+               "need a different method for this node kind");
+    MOZ_ASSERT(node->isArity(PN_UNARY));
+
+    ParseNode*& expr = node->pn_kid;
+    if (!Fold(cx, &expr, parser, inGenexpLambda, SyntacticContext::Other))
+        return false;
+
+    if (expr->isKind(PNK_NUMBER) || expr->isKind(PNK_TRUE) || expr->isKind(PNK_FALSE)) {
+        double d = expr->isKind(PNK_NUMBER)
+                   ? expr->pn_dval
+                   : double(expr->isKind(PNK_TRUE));
+
+        if (node->isKind(PNK_BITNOT))
+            d = ~ToInt32(d);
+        else if (node->isKind(PNK_NEG))
+            d = -d;
+        else
+            MOZ_ASSERT(node->isKind(PNK_POS)); // nothing to do
+
+        parser.prepareNodeForMutation(node);
+        node->setKind(PNK_NUMBER);
+        node->setOp(JSOP_DOUBLE);
+        node->setArity(PN_NULLARY);
+        node->pn_dval = d;
+    }
+
+    return true;
+}
+
+static bool
+FoldIncrementDecrement(ExclusiveContext* cx, ParseNode* node, Parser<FullParseHandler>& parser,
+                       bool inGenexpLambda)
+{
+    MOZ_ASSERT(node->isKind(PNK_PREINCREMENT) ||
+               node->isKind(PNK_POSTINCREMENT) ||
+               node->isKind(PNK_PREDECREMENT) ||
+               node->isKind(PNK_POSTDECREMENT));
+    MOZ_ASSERT(node->isArity(PN_UNARY));
+
+    ParseNode*& target = node->pn_kid;
+    MOZ_ASSERT(parser.isValidSimpleAssignmentTarget(target, Parser<FullParseHandler>::PermitAssignmentToFunctionCalls));
+
+    if (!Fold(cx, &target, parser, inGenexpLambda, SyntacticContext::Other))
+        return false;
+
+    MOZ_ASSERT(parser.isValidSimpleAssignmentTarget(target, Parser<FullParseHandler>::PermitAssignmentToFunctionCalls));
+
+    return true;
+}
+
+
 bool
-Fold(ExclusiveContext* cx, ParseNode** pnp,
-     FullParseHandler& handler, const ReadOnlyCompileOptions& options,
-     bool inGenexpLambda, SyntacticContext sc)
+Fold(ExclusiveContext* cx, ParseNode** pnp, Parser<FullParseHandler>& parser, bool inGenexpLambda,
+     SyntacticContext sc)
 {
     JS_CHECK_RECURSION(cx, return false);
 
     ParseNode** restartNode = nullptr;
     SyntacticContext restartContext;
 
     bool mightHaveHoistedDeclarations = true;
 
@@ -804,47 +899,53 @@ Fold(ExclusiveContext* cx, ParseNode** p
 
       case PNK_TYPEOFNAME:
         MOZ_ASSERT(pn->isArity(PN_UNARY));
         MOZ_ASSERT(pn->pn_kid->isKind(PNK_NAME));
         MOZ_ASSERT(!pn->pn_kid->maybeExpr());
         return true;
 
       case PNK_TYPEOFEXPR:
-        return FoldTypeOfExpr(cx, pn, handler, options, inGenexpLambda);
+        return FoldTypeOfExpr(cx, pn, parser, inGenexpLambda);
 
       case PNK_VOID:
-        return FoldVoid(cx, pn, handler, options, inGenexpLambda, sc);
+        return FoldVoid(cx, pn, parser, inGenexpLambda, sc);
 
       case PNK_DELETENAME: {
         MOZ_ASSERT(pn->isArity(PN_UNARY));
         MOZ_ASSERT(pn->pn_kid->isKind(PNK_NAME));
         return true;
       }
 
       case PNK_DELETEEXPR:
-        return FoldDeleteExpr(cx, pn, handler, options, inGenexpLambda);
+        return FoldDeleteExpr(cx, pn, parser, inGenexpLambda);
 
       case PNK_DELETEELEM:
       case PNK_DELETESUPERELEM:
-        return FoldDeleteElement(cx, pn, handler, options, inGenexpLambda);
+        return FoldDeleteElement(cx, pn, parser, inGenexpLambda);
 
       case PNK_DELETEPROP:
       case PNK_DELETESUPERPROP:
-        return FoldDeleteProperty(cx, pn, handler, options, inGenexpLambda);
+        return FoldDeleteProperty(cx, pn, parser, inGenexpLambda);
 
       case PNK_NOT:
+        return FoldNot(cx, pn, parser, inGenexpLambda);
+
       case PNK_BITNOT:
-      case PNK_THROW:
       case PNK_POS:
       case PNK_NEG:
+        return FoldUnaryArithmetic(cx, pn, parser, inGenexpLambda);
+
       case PNK_PREINCREMENT:
       case PNK_POSTINCREMENT:
       case PNK_PREDECREMENT:
       case PNK_POSTDECREMENT:
+        return FoldIncrementDecrement(cx, pn, parser, inGenexpLambda);
+
+      case PNK_THROW:
       case PNK_COMPUTED_NAME:
       case PNK_ARRAYPUSH:
       case PNK_SPREAD:
       case PNK_MUTATEPROTO:
       case PNK_EXPORT:
       case PNK_SEMI:
       case PNK_ASSIGN:
       case PNK_ADDASSIGN:
@@ -853,16 +954,17 @@ Fold(ExclusiveContext* cx, ParseNode** p
       case PNK_BITXORASSIGN:
       case PNK_BITANDASSIGN:
       case PNK_LSHASSIGN:
       case PNK_RSHASSIGN:
       case PNK_URSHASSIGN:
       case PNK_MULASSIGN:
       case PNK_DIVASSIGN:
       case PNK_MODASSIGN:
+      case PNK_POWASSIGN:
       case PNK_ELEM:
       case PNK_SUPERELEM:
       case PNK_COLON:
       case PNK_CASE:
       case PNK_SHORTHAND:
       case PNK_DOWHILE:
       case PNK_WHILE:
       case PNK_SWITCH:
@@ -903,16 +1005,17 @@ Fold(ExclusiveContext* cx, ParseNode** p
       case PNK_LSH:
       case PNK_RSH:
       case PNK_URSH:
       case PNK_ADD:
       case PNK_SUB:
       case PNK_STAR:
       case PNK_DIV:
       case PNK_MOD:
+      case PNK_POW:
       case PNK_COMMA:
       case PNK_NEW:
       case PNK_CALL:
       case PNK_GENEXP:
       case PNK_ARRAY:
       case PNK_STATEMENTLIST:
       case PNK_ARGSBODY:
       case PNK_ARRAYCOMP:
@@ -946,130 +1049,129 @@ Fold(ExclusiveContext* cx, ParseNode** p
     switch (pn->getArity()) {
       case PN_CODE:
         if (pn->isKind(PNK_FUNCTION) && pn->pn_funbox->useAsmOrInsideUseAsm())
             return true;
 
         // Note: pn_body is nullptr for functions which are being lazily parsed.
         MOZ_ASSERT(pn->getKind() == PNK_FUNCTION);
         if (pn->pn_body) {
-            if (!Fold(cx, &pn->pn_body, handler, options, pn->pn_funbox->inGenexpLambda,
+            if (!Fold(cx, &pn->pn_body, parser, pn->pn_funbox->inGenexpLambda,
                       SyntacticContext::Other))
+            {
                 return false;
+            }
         }
         break;
 
       case PN_LIST:
       {
         // Propagate Condition context through logical connectives.
         SyntacticContext kidsc = SyntacticContext::Other;
         if (pn->isKind(PNK_OR) || pn->isKind(PNK_AND))
             kidsc = sc;
 
         // Don't fold a parenthesized call expression. See bug 537673.
         ParseNode** listp = &pn->pn_head;
         if ((pn->isKind(PNK_CALL) || pn->isKind(PNK_TAGGED_TEMPLATE)) && (*listp)->isInParens())
             listp = &(*listp)->pn_next;
 
         for (; *listp; listp = &(*listp)->pn_next) {
-            if (!Fold(cx, listp, handler, options, inGenexpLambda, kidsc))
+            if (!Fold(cx, listp, parser, inGenexpLambda, kidsc))
                 return false;
         }
 
         // If the last node in the list was replaced, pn_tail points into the wrong node.
         pn->pn_tail = listp;
 
         // Save the list head in pn1 for later use.
         pn1 = pn->pn_head;
         pn2 = nullptr;
         break;
       }
 
       case PN_TERNARY:
         /* Any kid may be null (e.g. for (;;)). */
         if (pn->pn_kid1) {
-            if (!Fold(cx, &pn->pn_kid1, handler, options, inGenexpLambda, condIf(pn, PNK_IF)))
+            if (!Fold(cx, &pn->pn_kid1, parser, inGenexpLambda, condIf(pn, PNK_IF)))
                 return false;
         }
         pn1 = pn->pn_kid1;
 
         if (pn->pn_kid2) {
-            if (!Fold(cx, &pn->pn_kid2, handler, options, inGenexpLambda, condIf(pn, PNK_FORHEAD)))
+            if (!Fold(cx, &pn->pn_kid2, parser, inGenexpLambda, condIf(pn, PNK_FORHEAD)))
                 return false;
             if (pn->isKind(PNK_FORHEAD) && pn->pn_kid2->isKind(PNK_TRUE)) {
-                handler.freeTree(pn->pn_kid2);
+                parser.freeTree(pn->pn_kid2);
                 pn->pn_kid2 = nullptr;
             }
         }
         pn2 = pn->pn_kid2;
 
         if (pn->pn_kid3) {
             if (pn->isKind(PNK_IF) || pn->isKind(PNK_CONDITIONAL)) {
                 restartNode = &pn->pn_kid3;
                 restartContext = SyntacticContext::Other;
             } else {
-                if (!Fold(cx, &pn->pn_kid3, handler, options, inGenexpLambda, SyntacticContext::Other))
+                if (!Fold(cx, &pn->pn_kid3, parser, inGenexpLambda, SyntacticContext::Other))
                     return false;
             }
         }
         pn3 = pn->pn_kid3;
         break;
 
       case PN_BINARY:
       case PN_BINARY_OBJ:
         if (pn->isKind(PNK_OR) || pn->isKind(PNK_AND)) {
             // Propagate Condition context through logical connectives.
             SyntacticContext kidsc = SyntacticContext::Other;
             if (sc == SyntacticContext::Condition)
                 kidsc = sc;
-            if (!Fold(cx, &pn->pn_left, handler, options, inGenexpLambda, kidsc))
+            if (!Fold(cx, &pn->pn_left, parser, inGenexpLambda, kidsc))
                 return false;
-            if (!Fold(cx, &pn->pn_right, handler, options, inGenexpLambda, kidsc))
+            if (!Fold(cx, &pn->pn_right, parser, inGenexpLambda, kidsc))
                 return false;
         } else {
             /* First kid may be null (for default case in switch). */
             if (pn->pn_left) {
-                if (!Fold(cx, &pn->pn_left, handler, options, inGenexpLambda, condIf(pn, PNK_WHILE)))
+                if (!Fold(cx, &pn->pn_left, parser, inGenexpLambda, condIf(pn, PNK_WHILE)))
                     return false;
             }
             /* Second kid may be null (for return in non-generator). */
             if (pn->pn_right) {
-                if (!Fold(cx, &pn->pn_right, handler, options, inGenexpLambda, condIf(pn, PNK_DOWHILE)))
+                if (!Fold(cx, &pn->pn_right, parser, inGenexpLambda, condIf(pn, PNK_DOWHILE)))
                     return false;
             }
         }
         pn1 = pn->pn_left;
         pn2 = pn->pn_right;
         break;
 
       case PN_UNARY:
         MOZ_ASSERT(!IsDeleteKind(pn->getKind()),
                    "should have been handled above");
         if (pn->pn_kid) {
-            SyntacticContext kidsc = pn->isKind(PNK_NOT)
-                                     ? SyntacticContext::Condition
-                                     : SyntacticContext::Other;
-            if (!Fold(cx, &pn->pn_kid, handler, options, inGenexpLambda, kidsc))
+            if (!Fold(cx, &pn->pn_kid, parser, inGenexpLambda, SyntacticContext::Other))
                 return false;
         }
         pn1 = pn->pn_kid;
         break;
 
       case PN_NAME:
         /*
          * Skip pn1 down along a chain of dotted member expressions to avoid
          * excessive recursion.  Our only goal here is to fold constants (if
          * any) in the primary expression operand to the left of the first
          * dot in the chain.
          */
         if (!pn->isUsed()) {
             ParseNode** lhsp = &pn->pn_expr;
             while (*lhsp && (*lhsp)->isArity(PN_NAME) && !(*lhsp)->isUsed())
                 lhsp = &(*lhsp)->pn_expr;
-            if (*lhsp && !Fold(cx, lhsp, handler, options, inGenexpLambda, SyntacticContext::Other))
+            if (*lhsp && !Fold(cx, lhsp, parser, inGenexpLambda, SyntacticContext::Other))
                 return false;
             pn1 = *lhsp;
         }
         break;
 
       case PN_NULLARY:
         break;
     }
@@ -1140,25 +1242,25 @@ Fold(ExclusiveContext* cx, ParseNode** p
         if (!pn2 || (pn->isKind(PNK_SEMI) && !pn->pn_kid)) {
             /*
              * False condition and no else, or an empty then-statement was
              * moved up over pn.  Either way, make pn an empty block (not an
              * empty statement, which does not decompile, even when labeled).
              * NB: pn must be a PNK_IF as PNK_CONDITIONAL can never have a null
              * kid or an empty statement for a child.
              */
-            handler.prepareNodeForMutation(pn);
+            parser.prepareNodeForMutation(pn);
             pn->setKind(PNK_STATEMENTLIST);
             pn->setArity(PN_LIST);
             pn->makeEmpty();
         }
         if (pn3 && pn3 != pn2) {
             if (restartNode && *restartNode == pn3)
                 restartNode = nullptr;
-            handler.freeTree(pn3);
+            parser.freeTree(pn3);
         }
         break;
 
       case PNK_OR:
       case PNK_AND:
         if (sc == SyntacticContext::Condition) {
             ParseNode** listp = &pn->pn_head;
             MOZ_ASSERT(*listp == pn1);
@@ -1167,27 +1269,27 @@ Fold(ExclusiveContext* cx, ParseNode** p
                 Truthiness t = Boolish(pn1);
                 if (t == Unknown) {
                     listp = &pn1->pn_next;
                     continue;
                 }
                 if ((t == Truthy) == pn->isKind(PNK_OR)) {
                     for (pn2 = pn1->pn_next; pn2; pn2 = pn3) {
                         pn3 = pn2->pn_next;
-                        handler.freeTree(pn2);
+                        parser.freeTree(pn2);
                         --pn->pn_count;
                     }
                     pn1->pn_next = nullptr;
                     break;
                 }
                 MOZ_ASSERT((t == Truthy) == pn->isKind(PNK_AND));
                 if (pn->pn_count == 1)
                     break;
                 *listp = pn1->pn_next;
-                handler.freeTree(pn1);
+                parser.freeTree(pn1);
                 --pn->pn_count;
             } while ((pn1 = *listp) != nullptr);
 
             // We may have to replace a one-element list with its element.
             pn1 = pn->pn_head;
             if (pn->pn_count == 1) {
                 ReplaceNode(pnp, pn1);
                 pn = pn1;
@@ -1209,17 +1311,17 @@ Fold(ExclusiveContext* cx, ParseNode** p
         pn2 = pn1->pn_next;
         if (pn1->isKind(PNK_NUMBER)) {
             // Fold addition of numeric literals: (1 + 2 + x === 3 + x).
             // Note that we can only do this the front of the list:
             // (x + 1 + 2 !== x + 3) when x is a string.
             while (pn2 && pn2->isKind(PNK_NUMBER)) {
                 pn1->pn_dval += pn2->pn_dval;
                 pn1->pn_next = pn2->pn_next;
-                handler.freeTree(pn2);
+                parser.freeTree(pn2);
                 pn2 = pn1->pn_next;
                 pn->pn_count--;
                 folded = true;
             }
         }
 
         // Now search for adjacent pairs of literals to fold for string
         // concatenation.
@@ -1253,17 +1355,17 @@ Fold(ExclusiveContext* cx, ParseNode** p
                     return false;
                 if (!foldedStr)
                     foldedStr = pn1->pn_atom;
                 RootedString right(cx, pn2->pn_atom);
                 foldedStr = ConcatStrings<CanGC>(cx, foldedStr, right);
                 if (!foldedStr)
                     return false;
                 pn1->pn_next = pn2->pn_next;
-                handler.freeTree(pn2);
+                parser.freeTree(pn2);
                 pn2 = pn1->pn_next;
                 pn->pn_count--;
                 folded = true;
             } else {
                 if (foldedStr) {
                     // Convert the rope of folded strings into an Atom.
                     pn1->pn_atom = AtomizeString(cx, foldedStr);
                     if (!pn1->pn_atom)
@@ -1299,27 +1401,34 @@ Fold(ExclusiveContext* cx, ParseNode** p
 
       case PNK_SUB:
       case PNK_STAR:
       case PNK_LSH:
       case PNK_RSH:
       case PNK_URSH:
       case PNK_DIV:
       case PNK_MOD:
+      case PNK_POW:
         MOZ_ASSERT(pn->getArity() == PN_LIST);
         MOZ_ASSERT(pn->pn_count >= 2);
         for (pn2 = pn1; pn2; pn2 = pn2->pn_next) {
             if (!FoldType(cx, pn2, PNK_NUMBER))
                 return false;
         }
         for (pn2 = pn1; pn2; pn2 = pn2->pn_next) {
             /* XXX fold only if all operands convert to number */
             if (!pn2->isKind(PNK_NUMBER))
                 break;
         }
+
+        // No constant-folding for (2**3**5), because (**) is right-
+        // associative. We would have to reverse the list. It's not worth it.
+        if (pn->getKind() == PNK_POW && pn->pn_count > 2)
+            break;
+
         if (!pn2) {
             JSOp op = pn->getOp();
 
             pn2 = pn1->pn_next;
             pn3 = pn2->pn_next;
             if (!FoldBinaryNumeric(cx, op, pn1, pn2, pn))
                 return false;
             while ((pn2 = pn3) != nullptr) {
@@ -1328,73 +1437,21 @@ Fold(ExclusiveContext* cx, ParseNode** p
                     return false;
             }
         }
         break;
 
       case PNK_TYPEOFNAME:
       case PNK_TYPEOFEXPR:
       case PNK_VOID:
-        MOZ_CRASH("should have been fully handled above");
-
       case PNK_NOT:
       case PNK_BITNOT:
       case PNK_POS:
       case PNK_NEG:
-        if (pn1->isKind(PNK_NUMBER)) {
-            double d;
-
-            /* Operate on one numeric constant. */
-            d = pn1->pn_dval;
-            switch (pn->getKind()) {
-              case PNK_BITNOT:
-                d = ~ToInt32(d);
-                break;
-
-              case PNK_NEG:
-                d = -d;
-                break;
-
-              case PNK_POS:
-                break;
-
-              case PNK_NOT:
-                if (d == 0 || IsNaN(d)) {
-                    pn->setKind(PNK_TRUE);
-                    pn->setOp(JSOP_TRUE);
-                } else {
-                    pn->setKind(PNK_FALSE);
-                    pn->setOp(JSOP_FALSE);
-                }
-                pn->setArity(PN_NULLARY);
-                /* FALL THROUGH */
-
-              default:
-                /* Return early to dodge the common PNK_NUMBER code. */
-                goto restart;
-            }
-            pn->setKind(PNK_NUMBER);
-            pn->setOp(JSOP_DOUBLE);
-            pn->setArity(PN_NULLARY);
-            pn->pn_dval = d;
-            handler.freeTree(pn1);
-        } else if (pn1->isKind(PNK_TRUE) || pn1->isKind(PNK_FALSE)) {
-            if (pn->isKind(PNK_NOT)) {
-                ReplaceNode(pnp, pn1);
-                pn = pn1;
-                if (pn->isKind(PNK_TRUE)) {
-                    pn->setKind(PNK_FALSE);
-                    pn->setOp(JSOP_FALSE);
-                } else {
-                    pn->setKind(PNK_TRUE);
-                    pn->setOp(JSOP_TRUE);
-                }
-            }
-        }
-        break;
+        MOZ_CRASH("should have been fully handled above");
 
       case PNK_ELEM: {
         // An indexed expression, pn1[pn2]. A few cases can be improved.
         PropertyName* name = nullptr;
         if (pn2->isKind(PNK_STRING)) {
             JSAtom* atom = pn2->pn_atom;
             uint32_t index;
 
@@ -1420,33 +1477,33 @@ Fold(ExclusiveContext* cx, ParseNode** p
             }
         }
 
         if (name && NameToId(name) == IdToTypeId(NameToId(name))) {
             // Optimization 3: We have pn1["foo"] where foo is not an index.
             // Convert to a property access (like pn1.foo) which we optimize
             // better downstream. Don't bother with this for names which TI
             // considers to be indexes, to simplify downstream analysis.
-            ParseNode* expr = handler.newPropertyAccess(pn->pn_left, name, pn->pn_pos.end);
+            ParseNode* expr = parser.handler.newPropertyAccess(pn->pn_left, name, pn->pn_pos.end);
             if (!expr)
                 return false;
             expr->setInParens(pn->isInParens());
             ReplaceNode(pnp, expr);
 
             // Supposing we're replacing |obj["prop"]| with |obj.prop|, we now
             // can free the |"prop"| node and |obj["prop"]| nodes -- but not
             // the |obj| node now a sub-node of |expr|.  Mutate |pn| into a
             // node with |"prop"| as its child so that our |pn| doesn't have a
             // necessarily-weird structure (say, by nulling out |pn->pn_left|
             // only) that would fail AST sanity assertions performed by
-            // |handler.freeTree(pn)|.
+            // |parser.freeTree(pn)|.
             pn->setKind(PNK_TYPEOFEXPR);
             pn->setArity(PN_UNARY);
             pn->pn_kid = pn2;
-            handler.freeTree(pn);
+            parser.freeTree(pn);
 
             pn = expr;
         }
         break;
       }
 
       default:;
     }
@@ -1456,17 +1513,17 @@ Fold(ExclusiveContext* cx, ParseNode** p
         Truthiness t = Boolish(pn);
         if (t != Unknown) {
             /*
              * We can turn function nodes into constant nodes here, but mutating function
              * nodes is tricky --- in particular, mutating a function node that appears on
              * a method list corrupts the method list. However, methods are M's in
              * statements of the form 'this.foo = M;', which we never fold, so we're okay.
              */
-            handler.prepareNodeForMutation(pn);
+            parser.prepareNodeForMutation(pn);
             if (t == Truthy) {
                 pn->setKind(PNK_TRUE);
                 pn->setOp(JSOP_TRUE);
             } else {
                 pn->setKind(PNK_FALSE);
                 pn->setOp(JSOP_FALSE);
             }
             pn->setArity(PN_NULLARY);
@@ -1481,10 +1538,10 @@ frontend::FoldConstants(ExclusiveContext
 {
     // Don't fold constants if the code has requested "use asm" as
     // constant-folding will misrepresent the source text for the purpose
     // of type checking. (Also guard against entering a function containing
     // "use asm", see PN_FUNC case below.)
     if (parser->pc->useAsmOrInsideUseAsm())
         return true;
 
-    return Fold(cx, pnp, parser->handler, parser->options(), false, SyntacticContext::Other);
+    return Fold(cx, pnp, *parser, false, SyntacticContext::Other);
 }
--- a/js/src/frontend/NameFunctions.cpp
+++ b/js/src/frontend/NameFunctions.cpp
@@ -434,16 +434,17 @@ class NameResolver
           case PNK_BITXORASSIGN:
           case PNK_BITANDASSIGN:
           case PNK_LSHASSIGN:
           case PNK_RSHASSIGN:
           case PNK_URSHASSIGN:
           case PNK_MULASSIGN:
           case PNK_DIVASSIGN:
           case PNK_MODASSIGN:
+          case PNK_POWASSIGN:
           case PNK_ELEM:
           case PNK_COLON:
           case PNK_CASE:
           case PNK_SHORTHAND:
           case PNK_DOWHILE:
           case PNK_WHILE:
           case PNK_SWITCH:
           case PNK_LETBLOCK:
@@ -654,16 +655,17 @@ class NameResolver
           case PNK_LSH:
           case PNK_RSH:
           case PNK_URSH:
           case PNK_ADD:
           case PNK_SUB:
           case PNK_STAR:
           case PNK_DIV:
           case PNK_MOD:
+          case PNK_POW:
           case PNK_COMMA:
           case PNK_NEW:
           case PNK_CALL:
           case PNK_GENEXP:
           case PNK_ARRAY:
           case PNK_STATEMENTLIST:
           case PNK_ARGSBODY:
           // Initializers for individual variables, and computed property names
--- a/js/src/frontend/ParseNode.cpp
+++ b/js/src/frontend/ParseNode.cpp
@@ -267,16 +267,17 @@ PushNodeChildren(ParseNode* pn, NodeStac
       case PNK_BITXORASSIGN:
       case PNK_BITANDASSIGN:
       case PNK_LSHASSIGN:
       case PNK_RSHASSIGN:
       case PNK_URSHASSIGN:
       case PNK_MULASSIGN:
       case PNK_DIVASSIGN:
       case PNK_MODASSIGN:
+      case PNK_POWASSIGN:
       // ...and a few others.
       case PNK_ELEM:
       case PNK_IMPORT_SPEC:
       case PNK_EXPORT_SPEC:
       case PNK_COLON:
       case PNK_CASE:
       case PNK_SHORTHAND:
       case PNK_DOWHILE:
@@ -473,16 +474,17 @@ PushNodeChildren(ParseNode* pn, NodeStac
       case PNK_LSH:
       case PNK_RSH:
       case PNK_URSH:
       case PNK_ADD:
       case PNK_SUB:
       case PNK_STAR:
       case PNK_DIV:
       case PNK_MOD:
+      case PNK_POW:
       case PNK_COMMA:
       case PNK_NEW:
       case PNK_CALL:
       case PNK_GENEXP:
       case PNK_ARRAY:
       case PNK_OBJECT:
       case PNK_TEMPLATE_STRING_LIST:
       case PNK_TAGGED_TEMPLATE:
@@ -606,17 +608,28 @@ ParseNode::appendOrCreateList(ParseNodeK
     // binary tree of lists exactly as ECMAScript would by skipping the
     // following optimization.
     if (!pc->useAsmOrInsideUseAsm()) {
         // Left-associative trees of a given operator (e.g. |a + b + c|) are
         // binary trees in the spec: (+ (+ a b) c) in Lisp terms.  Recursively
         // processing such a tree, exactly implemented that way, would blow the
         // the stack.  We use a list node that uses O(1) stack to represent
         // such operations: (+ a b c).
-        if (left->isKind(kind) && left->isOp(op) && (js_CodeSpec[op].format & JOF_LEFTASSOC)) {
+        //
+        // (**) is right-associative; per spec |a ** b ** c| parses as
+        // (** a (** b c)). But we treat this the same way, creating a list
+        // node: (** a b c). All consumers must understand that this must be
+        // processed with a right fold, whereas the list (+ a b c) must be
+        // processed with a left fold because (+) is left-associative.
+        //
+        if (left->isKind(kind) &&
+            left->isOp(op) &&
+            (js_CodeSpec[op].format & JOF_LEFTASSOC ||
+             (kind == PNK_POW && !left->pn_parens)))
+        {
             ListNode* list = &left->as<ListNode>();
 
             list->append(right);
             list->pn_pos.end = right->pn_pos.end;
 
             return list;
         }
     }
--- a/js/src/frontend/ParseNode.h
+++ b/js/src/frontend/ParseNode.h
@@ -192,31 +192,33 @@ class UpvarCookie
     F(LSH) \
     F(RSH) \
     F(URSH) \
     F(ADD) \
     F(SUB) \
     F(STAR) \
     F(DIV) \
     F(MOD) \
+    F(POW) \
     \
     /* Assignment operators (= += -= etc.). */ \
     /* ParseNode::isAssignment assumes all these are consecutive. */ \
     F(ASSIGN) \
     F(ADDASSIGN) \
     F(SUBASSIGN) \
     F(BITORASSIGN) \
     F(BITXORASSIGN) \
     F(BITANDASSIGN) \
     F(LSHASSIGN) \
     F(RSHASSIGN) \
     F(URSHASSIGN) \
     F(MULASSIGN) \
     F(DIVASSIGN) \
-    F(MODASSIGN)
+    F(MODASSIGN) \
+    F(POWASSIGN)
 
 /*
  * Parsing builds a tree of nodes that directs code generation.  This tree is
  * not a concrete syntax tree in all respects (for example, || and && are left
  * associative, but (A && B && C) translates into the right-associated tree
  * <A && <B && C>> so that code generation can emit a left-associative branch
  * around <B && C> when A is false).  Nodes are labeled by kind, with a
  * secondary JSOp label when needed.
@@ -225,19 +227,19 @@ class UpvarCookie
  */
 enum ParseNodeKind
 {
 #define EMIT_ENUM(name) PNK_##name,
     FOR_EACH_PARSE_NODE_KIND(EMIT_ENUM)
 #undef EMIT_ENUM
     PNK_LIMIT, /* domain size */
     PNK_BINOP_FIRST = PNK_OR,
-    PNK_BINOP_LAST = PNK_MOD,
+    PNK_BINOP_LAST = PNK_POW,
     PNK_ASSIGNMENT_START = PNK_ASSIGN,
-    PNK_ASSIGNMENT_LAST = PNK_MODASSIGN
+    PNK_ASSIGNMENT_LAST = PNK_POWASSIGN
 };
 
 inline bool
 IsDeleteKind(ParseNodeKind kind)
 {
     return PNK_DELETENAME <= kind && kind <= PNK_DELETEEXPR;
 }
 
@@ -364,41 +366,44 @@ IsDeleteKind(ParseNodeKind kind)
  * PNK_BITORASSIGN,
  * PNK_BITXORASSIGN,
  * PNK_BITANDASSIGN,
  * PNK_LSHASSIGN,
  * PNK_RSHASSIGN,
  * PNK_URSHASSIGN,
  * PNK_MULASSIGN,
  * PNK_DIVASSIGN,
- * PNK_MODASSIGN
+ * PNK_MODASSIGN,
+ * PNK_POWASSIGN
  * PNK_CONDITIONAL ternary  (cond ? trueExpr : falseExpr)
  *                          pn_kid1: cond, pn_kid2: then, pn_kid3: else
- * PNK_OR       binary      pn_left: first in || chain, pn_right: rest of chain
- * PNK_AND      binary      pn_left: first in && chain, pn_right: rest of chain
- * PNK_BITOR    binary      pn_left: left-assoc | expr, pn_right: ^ expr
- * PNK_BITXOR   binary      pn_left: left-assoc ^ expr, pn_right: & expr
- * PNK_BITAND   binary      pn_left: left-assoc & expr, pn_right: EQ expr
- *
- * PNK_EQ,      binary      pn_left: left-assoc EQ expr, pn_right: REL expr
+ * PNK_OR,      list        pn_head; list of pn_count subexpressions
+ * PNK_AND,                 All of these operators are left-associative except (**).
+ * PNK_BITOR,
+ * PNK_BITXOR,
+ * PNK_BITAND,
+ * PNK_EQ,
  * PNK_NE,
  * PNK_STRICTEQ,
- * PNK_STRICTNE
- * PNK_LT,      binary      pn_left: left-assoc REL expr, pn_right: SH expr
+ * PNK_STRICTNE,
+ * PNK_LT,
  * PNK_LE,
  * PNK_GT,
- * PNK_GE
- * PNK_LSH,     binary      pn_left: left-assoc SH expr, pn_right: ADD expr
+ * PNK_GE,
+ * PNK_LSH,
  * PNK_RSH,
- * PNK_URSH
- * PNK_ADD,     binary      pn_left: left-assoc ADD expr, pn_right: MUL expr
- * PNK_SUB
- * PNK_STAR,    binary      pn_left: left-assoc MUL expr, pn_right: UNARY expr
- * PNK_DIV,                 pn_op: JSOP_MUL, JSOP_DIV, JSOP_MOD
- * PNK_MOD
+ * PNK_URSH,
+ * PNK_ADD,
+ * PNK_SUB,
+ * PNK_STAR,
+ * PNK_DIV,
+ * PNK_MOD,
+ * PNK_POW                  (**) is right-associative, but forms a list
+ *                          nonetheless. Special hacks everywhere.
+ *
  * PNK_POS,     unary       pn_kid: UNARY expr
  * PNK_NEG
  * PNK_VOID,    unary       pn_kid: UNARY expr
  * PNK_NOT,
  * PNK_BITNOT
  * PNK_TYPEOFNAME, unary    pn_kid: UNARY expr
  * PNK_TYPEOFEXPR
  * PNK_PREINCREMENT, unary  pn_kid: MEMBER expr
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -6346,17 +6346,18 @@ static const JSOp ParseNodeKindToJSOp[] 
     JSOP_IN,
     JSOP_LSH,
     JSOP_RSH,
     JSOP_URSH,
     JSOP_ADD,
     JSOP_SUB,
     JSOP_MUL,
     JSOP_DIV,
-    JSOP_MOD
+    JSOP_MOD,
+    JSOP_POW
 };
 
 static inline JSOp
 BinaryOpParseNodeKindToJSOp(ParseNodeKind pnk)
 {
     MOZ_ASSERT(pnk >= PNK_BINOP_FIRST);
     MOZ_ASSERT(pnk <= PNK_BINOP_LAST);
     return ParseNodeKindToJSOp[pnk - PNK_BINOP_FIRST];
@@ -6387,20 +6388,21 @@ static const int PrecedenceTable[] = {
     7, /* PNK_IN */
     8, /* PNK_LSH */
     8, /* PNK_RSH */
     8, /* PNK_URSH */
     9, /* PNK_ADD */
     9, /* PNK_SUB */
     10, /* PNK_STAR */
     10, /* PNK_DIV */
-    10  /* PNK_MOD */
+    10, /* PNK_MOD */
+    11  /* PNK_POW */
 };
 
-static const int PRECEDENCE_CLASSES = 10;
+static const int PRECEDENCE_CLASSES = 11;
 
 static int
 Precedence(ParseNodeKind pnk) {
     // Everything binds tighter than PNK_LIMIT, because we want to reduce all
     // nodes to a single node when we reach a token that is not another binary
     // operator.
     if (pnk == PNK_LIMIT)
         return 0;
@@ -6410,18 +6412,18 @@ Precedence(ParseNodeKind pnk) {
     return PrecedenceTable[pnk - PNK_BINOP_FIRST];
 }
 
 template <typename ParseHandler>
 MOZ_ALWAYS_INLINE typename ParseHandler::Node
 Parser<ParseHandler>::orExpr1(InHandling inHandling, YieldHandling yieldHandling,
                               InvokedPrediction invoked)
 {
-    // Shift-reduce parser for the left-associative binary operator part of
-    // the JS syntax.
+    // Shift-reduce parser for the binary operator part of the JS expression
+    // syntax.
 
     // Conceptually there's just one stack, a stack of pairs (lhs, op).
     // It's implemented using two separate arrays, though.
     Node nodeStack[PRECEDENCE_CLASSES];
     ParseNodeKind kindStack[PRECEDENCE_CLASSES];
     int depth = 0;
 
     Node pn;
@@ -6443,20 +6445,19 @@ Parser<ParseHandler>::orExpr1(InHandling
             tok = TOK_EOF;
             pnk = PNK_LIMIT;
         }
 
         // If pnk has precedence less than or equal to another operator on the
         // stack, reduce. This combines nodes on the stack until we form the
         // actual lhs of pnk.
         //
-        // The >= in this condition works because all the operators in question
-        // are left-associative; if any were not, the case where two operators
-        // have equal precedence would need to be handled specially, and the
-        // stack would need to be a Vector.
+        // The >= in this condition works because it is appendOrCreateList's
+        // job to decide if the operator in question is left- or
+        // right-associative, and build the corresponding tree.
         while (depth > 0 && Precedence(kindStack[depth - 1]) >= Precedence(pnk)) {
             depth--;
             ParseNodeKind combiningPnk = kindStack[depth];
             JSOp combiningOp = BinaryOpParseNodeKindToJSOp(combiningPnk);
             pn = handler.appendOrCreateList(combiningPnk, nodeStack[depth], pn, pc, combiningOp);
             if (!pn)
                 return pn;
         }
@@ -6608,16 +6609,17 @@ Parser<ParseHandler>::assignExpr(InHandl
       case TOK_BITXORASSIGN: kind = PNK_BITXORASSIGN; op = JSOP_BITXOR; break;
       case TOK_BITANDASSIGN: kind = PNK_BITANDASSIGN; op = JSOP_BITAND; break;
       case TOK_LSHASSIGN:    kind = PNK_LSHASSIGN;    op = JSOP_LSH;    break;
       case TOK_RSHASSIGN:    kind = PNK_RSHASSIGN;    op = JSOP_RSH;    break;
       case TOK_URSHASSIGN:   kind = PNK_URSHASSIGN;   op = JSOP_URSH;   break;
       case TOK_MULASSIGN:    kind = PNK_MULASSIGN;    op = JSOP_MUL;    break;
       case TOK_DIVASSIGN:    kind = PNK_DIVASSIGN;    op = JSOP_DIV;    break;
       case TOK_MODASSIGN:    kind = PNK_MODASSIGN;    op = JSOP_MOD;    break;
+      case TOK_POWASSIGN:    kind = PNK_POWASSIGN;    op = JSOP_POW;    break;
 
       case TOK_ARROW: {
         // A line terminator between ArrowParameters and the => should trigger a SyntaxError.
         tokenStream.ungetToken();
         TokenKind next;
         if (!tokenStream.peekTokenSameLine(&next) || next != TOK_ARROW) {
             report(ParseError, false, null(), JSMSG_UNEXPECTED_TOKEN,
                    "expression", TokenKindToDesc(TOK_ARROW));
--- a/js/src/frontend/Parser.h
+++ b/js/src/frontend/Parser.h
@@ -384,16 +384,19 @@ class Parser : private JS::AutoGCRooter,
 
     typedef typename ParseHandler::Node Node;
     typedef typename ParseHandler::DefinitionNode DefinitionNode;
 
   public:
     /* State specific to the kind of parse being performed. */
     ParseHandler handler;
 
+    void prepareNodeForMutation(Node node) { handler.prepareNodeForMutation(node); }
+    void freeTree(Node node) { handler.freeTree(node); }
+
   private:
     bool reportHelper(ParseReportKind kind, bool strict, uint32_t offset,
                       unsigned errorNumber, va_list args);
   public:
     bool report(ParseReportKind kind, bool strict, Node pn, unsigned errorNumber, ...);
     bool reportNoOffset(ParseReportKind kind, bool strict, unsigned errorNumber, ...);
     bool reportWithOffset(ParseReportKind kind, bool strict, uint32_t offset, unsigned errorNumber,
                           ...);
@@ -678,24 +681,26 @@ class Parser : private JS::AutoGCRooter,
                                  bool* pbodyProcessed);
     bool finishFunctionDefinition(Node pn, FunctionBox* funbox, Node body);
     bool addFreeVariablesFromLazyFunction(JSFunction* fun, ParseContext<ParseHandler>* pc);
 
     bool isValidForStatementLHS(Node pn1, JSVersion version, bool forDecl, bool forEach,
                                 ParseNodeKind headKind);
     bool checkForHeadConstInitializers(Node pn1);
 
+  public:
     enum FunctionCallBehavior {
         PermitAssignmentToFunctionCalls,
         ForbidAssignmentToFunctionCalls
     };
 
     bool isValidSimpleAssignmentTarget(Node node,
                                        FunctionCallBehavior behavior = ForbidAssignmentToFunctionCalls);
 
+  private:
     bool reportIfArgumentsEvalTarget(Node nameNode);
     bool reportIfNotValidSimpleAssignmentTarget(Node target, AssignmentFlavor flavor);
 
     bool checkAndMarkAsIncOperand(Node kid, AssignmentFlavor flavor);
 
     bool checkStrictAssignment(Node lhs);
 
     bool checkStrictBinding(PropertyName* name, Node pn);
--- a/js/src/frontend/SyntaxParseHandler.h
+++ b/js/src/frontend/SyntaxParseHandler.h
@@ -168,16 +168,19 @@ class SyntaxParseHandler
                        TokenStream& tokenStream, Parser<SyntaxParseHandler>* syntaxParser,
                        LazyScript* lazyOuterFunction)
       : lastAtom(nullptr),
         tokenStream(tokenStream)
     {}
 
     static Node null() { return NodeFailure; }
 
+    void prepareNodeForMutation(Node node) {}
+    void freeTree(Node node) {}
+
     void trace(JSTracer* trc) {}
 
     Node newName(PropertyName* name, uint32_t blockid, const TokenPos& pos, ExclusiveContext* cx) {
         lastAtom = name;
         if (name == cx->names().arguments)
             return NodeUnparenthesizedArgumentsName;
         if (name == cx->names().eval)
             return NodeUnparenthesizedEvalName;
--- a/js/src/frontend/TokenKind.h
+++ b/js/src/frontend/TokenKind.h
@@ -117,17 +117,17 @@
     /* reserved keywords in strict mode */ \
     macro(STRICT_RESERVED, "reserved keyword") \
     \
     /* \
      * The following token types occupy contiguous ranges to enable easy \
      * range-testing. \
      */ \
     /* \
-     * Binary operators tokens, TOK_OR thru TOK_MOD. These must be in the same \
+     * Binary operators tokens, TOK_OR thru TOK_POW. These must be in the same \
      * order as F(OR) and friends in FOR_EACH_PARSE_NODE_KIND in ParseNode.h. \
      */ \
     macro(OR,           "'||'")   /* logical or */ \
     range(BINOP_FIRST, OR) \
     macro(AND,          "'&&'")   /* logical and */ \
     macro(BITOR,        "'|'")    /* bitwise-or */ \
     macro(BITXOR,       "'^'")    /* bitwise-xor */ \
     macro(BITAND,       "'&'")    /* bitwise-and */ \
@@ -158,17 +158,18 @@
     macro(URSH,         "'>>>'") \
     range(SHIFTOP_LAST, URSH) \
     \
     macro(ADD,          "'+'") \
     macro(SUB,          "'-'") \
     macro(MUL,          "'*'") \
     macro(DIV,          "'/'") \
     macro(MOD,          "'%'") \
-    range(BINOP_LAST, MOD) \
+    macro(POW,          "'**'") \
+    range(BINOP_LAST, POW) \
     \
     /* Unary operation tokens. */ \
     macro(TYPEOF,       "keyword 'typeof'") \
     macro(VOID,         "keyword 'void'") \
     macro(NOT,          "'!'") \
     macro(BITNOT,       "'~'") \
     \
     macro(ARROW,        "'=>'")   /* function arrow */ \
@@ -182,17 +183,18 @@
     macro(BITXORASSIGN, "'^='") \
     macro(BITANDASSIGN, "'&='") \
     macro(LSHASSIGN,    "'<<='") \
     macro(RSHASSIGN,    "'>>='") \
     macro(URSHASSIGN,   "'>>>='") \
     macro(MULASSIGN,    "'*='") \
     macro(DIVASSIGN,    "'/='") \
     macro(MODASSIGN,    "'%='") \
-    range(ASSIGNMENT_LAST, MODASSIGN)
+    macro(POWASSIGN,    "'**='") \
+    range(ASSIGNMENT_LAST, POWASSIGN)
 
 #define TOKEN_KIND_RANGE_EMIT_NONE(name, value)
 #define FOR_EACH_TOKEN_KIND(macro) \
     FOR_EACH_TOKEN_KIND_WITH_RANGE(macro, TOKEN_KIND_RANGE_EMIT_NONE)
 
 namespace js {
 namespace frontend {
 
--- a/js/src/frontend/TokenStream.cpp
+++ b/js/src/frontend/TokenStream.cpp
@@ -1504,17 +1504,20 @@ TokenStream::getTokenInternal(TokenKind*
             else
                 tp->type = matchChar('=') ? TOK_RSHASSIGN : TOK_RSH;
         } else {
             tp->type = matchChar('=') ? TOK_GE : TOK_GT;
         }
         goto out;
 
       case '*':
-        tp->type = matchChar('=') ? TOK_MULASSIGN : TOK_MUL;
+        if (matchChar('*'))
+            tp->type = matchChar('=') ? TOK_POWASSIGN : TOK_POW;
+        else
+            tp->type = matchChar('=') ? TOK_MULASSIGN : TOK_MUL;
         goto out;
 
       case '/':
         // Look for a single-line comment.
         if (matchChar('/')) {
             c = peekChar();
             if (c == '@' || c == '#') {
                 bool shouldWarn = getChar() == '@';
--- a/js/src/jit/BaselineCompiler.cpp
+++ b/js/src/jit/BaselineCompiler.cpp
@@ -1594,16 +1594,22 @@ BaselineCompiler::emit_JSOP_DIV()
 
 bool
 BaselineCompiler::emit_JSOP_MOD()
 {
     return emitBinaryArith();
 }
 
 bool
+BaselineCompiler::emit_JSOP_POW()
+{
+    return emitBinaryArith();
+}
+
+bool
 BaselineCompiler::emitBinaryArith()
 {
     // Keep top JSStack value in R0 and R2
     frame.popRegsAndSync(2);
 
     // Call IC
     ICBinaryArith_Fallback::Compiler stubCompiler(cx);
     if (!emitOpIC(stubCompiler.getStub(&stubSpace_)))
--- a/js/src/jit/BaselineCompiler.h
+++ b/js/src/jit/BaselineCompiler.h
@@ -75,16 +75,17 @@ namespace jit {
     _(JSOP_LSH)                \
     _(JSOP_RSH)                \
     _(JSOP_URSH)               \
     _(JSOP_ADD)                \
     _(JSOP_SUB)                \
     _(JSOP_MUL)                \
     _(JSOP_DIV)                \
     _(JSOP_MOD)                \
+    _(JSOP_POW)                \
     _(JSOP_LT)                 \
     _(JSOP_LE)                 \
     _(JSOP_GT)                 \
     _(JSOP_GE)                 \
     _(JSOP_EQ)                 \
     _(JSOP_NE)                 \
     _(JSOP_STRICTEQ)           \
     _(JSOP_STRICTNE)           \
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -1900,16 +1900,20 @@ DoBinaryArithFallback(JSContext* cx, Bas
       case JSOP_DIV:
         if (!DivValues(cx, &lhsCopy, &rhsCopy, ret))
             return false;
         break;
       case JSOP_MOD:
         if (!ModValues(cx, &lhsCopy, &rhsCopy, ret))
             return false;
         break;
+      case JSOP_POW:
+        if (!math_pow_handle(cx, lhsCopy, rhsCopy, ret))
+            return false;
+        break;
       case JSOP_BITOR: {
         int32_t result;
         if (!BitOr(cx, lhs, rhs, &result))
             return false;
         ret.setInt32(result);
         break;
       }
       case JSOP_BITXOR: {
@@ -2033,17 +2037,17 @@ DoBinaryArithFallback(JSContext* cx, Bas
             stub->addNewStub(doubleStub);
             return true;
           }
           default:
             break;
         }
     }
 
-    if (lhs.isInt32() && rhs.isInt32()) {
+    if (lhs.isInt32() && rhs.isInt32() && op != JSOP_POW) {
         bool allowDouble = ret.isDouble();
         if (allowDouble)
             stub->unlinkStubsWithKind(cx, ICStub::BinaryArith_Int32);
         JitSpew(JitSpew_BaselineIC, "  Generating %s(Int32, Int32%s) stub", js_CodeName[op],
                 allowDouble ? " => Double" : "");
         ICBinaryArith_Int32::Compiler compilerInt32(cx, op, allowDouble);
         ICStub* int32Stub = compilerInt32.getStub(compilerInt32.getStubSpace(script));
         if (!int32Stub)
@@ -10019,17 +10023,17 @@ ICCallStubCompiler::pushSpreadCallArgume
                                             Register argcReg, bool isJitCall,
                                             bool isConstructing)
 {
     // Pull the array off the stack before aligning.
     Register startReg = regs.takeAny();
     masm.unboxObject(Address(masm.getStackPointer(),
                              (isConstructing * sizeof(Value)) + STUB_FRAME_SIZE), startReg);
     masm.loadPtr(Address(startReg, NativeObject::offsetOfElements()), startReg);
-    
+
     // Align the stack such that the JitFrameLayout is aligned on the
     // JitStackAlignment.
     if (isJitCall) {
         Register alignReg = argcReg;
         if (isConstructing) {
             alignReg = regs.takeAny();
             masm.movePtr(argcReg, alignReg);
             masm.addPtr(Imm32(1), alignReg);
@@ -10269,28 +10273,28 @@ ICCall_Fallback::Compiler::generateStubC
 
     if (MOZ_UNLIKELY(isSpread_)) {
         // Push a stub frame so that we can perform a non-tail call.
         enterStubFrame(masm, R1.scratchReg());
 
         // Use BaselineFrameReg instead of BaselineStackReg, because
         // BaselineFrameReg and BaselineStackReg hold the same value just after
         // calling enterStubFrame.
-        
+
         // newTarget
         if (isConstructing_)
             masm.pushValue(Address(BaselineFrameReg, STUB_FRAME_SIZE));
-        
+
         // array
         uint32_t valueOffset = isConstructing_;
         masm.pushValue(Address(BaselineFrameReg, valueOffset++ * sizeof(Value) + STUB_FRAME_SIZE));
-        
+
         // this
         masm.pushValue(Address(BaselineFrameReg, valueOffset++ * sizeof(Value) + STUB_FRAME_SIZE));
-        
+
         // callee
         masm.pushValue(Address(BaselineFrameReg, valueOffset++ * sizeof(Value) + STUB_FRAME_SIZE));
 
         masm.push(masm.getStackPointer());
         masm.push(ICStubReg);
 
         pushFramePtr(masm, R0.scratchReg());
 
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -1652,16 +1652,19 @@ IonBuilder::inspectOpcode(JSOp op)
 
       case JSOP_ADD:
       case JSOP_SUB:
       case JSOP_MUL:
       case JSOP_DIV:
       case JSOP_MOD:
         return jsop_binary(op);
 
+      case JSOP_POW:
+        return jsop_pow();
+
       case JSOP_POS:
         return jsop_pos();
 
       case JSOP_NEG:
         return jsop_neg();
 
       case JSOP_AND:
       case JSOP_OR:
@@ -4593,16 +4596,35 @@ IonBuilder::jsop_binary(JSOp op, MDefini
     current->push(ins);
 
     if (ins->isEffectful())
         return resumeAfter(ins);
     return maybeInsertResume();
 }
 
 bool
+IonBuilder::jsop_pow()
+{
+    MDefinition* exponent = current->pop();
+    MDefinition* base = current->pop();
+
+    if (inlineMathPowHelper(base, exponent, MIRType_Double) == InliningStatus_Inlined) {
+        base->setImplicitlyUsedUnchecked();
+        exponent->setImplicitlyUsedUnchecked();
+        return true;
+    }
+
+    // For now, use MIRType_Double, as a safe cover-all. See bug 1188079.
+    MPow* pow = MPow::New(alloc(), base, exponent, MIRType_Double);
+    current->add(pow);
+    current->push(pow);
+    return true;
+}
+
+bool
 IonBuilder::jsop_binary(JSOp op)
 {
     MDefinition* right = current->pop();
     MDefinition* left = current->pop();
 
     return jsop_binary(op, left, right);
 }
 
--- a/js/src/jit/IonBuilder.h
+++ b/js/src/jit/IonBuilder.h
@@ -619,16 +619,17 @@ class IonBuilder
     bool tryFoldInstanceOf(MDefinition* lhs, JSObject* protoObject);
     bool hasOnProtoChain(TypeSet::ObjectKey* key, JSObject* protoObject, bool* hasOnProto);
 
     bool jsop_add(MDefinition* left, MDefinition* right);
     bool jsop_bitnot();
     bool jsop_bitop(JSOp op);
     bool jsop_binary(JSOp op);
     bool jsop_binary(JSOp op, MDefinition* left, MDefinition* right);
+    bool jsop_pow();
     bool jsop_pos();
     bool jsop_neg();
     bool jsop_setarg(uint32_t arg);
     bool jsop_defvar(uint32_t index);
     bool jsop_deffun(uint32_t index);
     bool jsop_notearg();
     bool jsop_checklexical();
     bool jsop_checkaliasedlet(ScopeCoordinate sc);
@@ -751,16 +752,17 @@ class IonBuilder
     InliningStatus inlineMathCeil(CallInfo& callInfo);
     InliningStatus inlineMathClz32(CallInfo& callInfo);
     InliningStatus inlineMathRound(CallInfo& callInfo);
     InliningStatus inlineMathSqrt(CallInfo& callInfo);
     InliningStatus inlineMathAtan2(CallInfo& callInfo);
     InliningStatus inlineMathHypot(CallInfo& callInfo);
     InliningStatus inlineMathMinMax(CallInfo& callInfo, bool max);
     InliningStatus inlineMathPow(CallInfo& callInfo);
+    InliningStatus inlineMathPowHelper(MDefinition* lhs, MDefinition* rhs, MIRType outputType);
     InliningStatus inlineMathRandom(CallInfo& callInfo);
     InliningStatus inlineMathImul(CallInfo& callInfo);
     InliningStatus inlineMathFRound(CallInfo& callInfo);
     InliningStatus inlineMathFunction(CallInfo& callInfo, MMathFunction::Function function);
 
     // String natives.
     InliningStatus inlineStringObject(CallInfo& callInfo);
     InliningStatus inlineConstantStringSplit(CallInfo& callInfo);
--- a/js/src/jit/MCallOptimize.cpp
+++ b/js/src/jit/MCallOptimize.cpp
@@ -1342,46 +1342,36 @@ IonBuilder::inlineMathHypot(CallInfo& ca
         return InliningStatus_NotInlined;
 
     current->add(hypot);
     current->push(hypot);
     return InliningStatus_Inlined;
 }
 
 IonBuilder::InliningStatus
-IonBuilder::inlineMathPow(CallInfo& callInfo)
+IonBuilder::inlineMathPowHelper(MDefinition* lhs, MDefinition* rhs, MIRType outputType)
 {
-    if (callInfo.argc() != 2 || callInfo.constructing()) {
-        trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
-        return InliningStatus_NotInlined;
-    }
-
     // Typechecking.
-    MIRType baseType = callInfo.getArg(0)->type();
-    MIRType powerType = callInfo.getArg(1)->type();
-    MIRType outputType = getInlineReturnType();
+    MIRType baseType = lhs->type();
+    MIRType powerType = rhs->type();
 
     if (outputType != MIRType_Int32 && outputType != MIRType_Double)
         return InliningStatus_NotInlined;
     if (!IsNumberType(baseType))
         return InliningStatus_NotInlined;
     if (!IsNumberType(powerType))
         return InliningStatus_NotInlined;
 
-    callInfo.setImplicitlyUsedUnchecked();
-
-    MDefinition* base = callInfo.getArg(0);
-    MDefinition* power = callInfo.getArg(1);
+    MDefinition* base = lhs;
+    MDefinition* power = rhs;
     MDefinition* output = nullptr;
 
     // Optimize some constant powers.
-    if (callInfo.getArg(1)->isConstantValue() &&
-        callInfo.getArg(1)->constantValue().isNumber())
-    {
-        double pow = callInfo.getArg(1)->constantValue().toNumber();
+    if (rhs->isConstantValue() && rhs->constantValue().isNumber()) {
+        double pow = rhs->constantValue().toNumber();
 
         // Math.pow(x, 0.5) is a sqrt with edge-case detection.
         if (pow == 0.5) {
             MPowHalf* half = MPowHalf::New(alloc(), base);
             current->add(half);
             output = half;
         }
 
@@ -1447,16 +1437,33 @@ IonBuilder::inlineMathPow(CallInfo& call
         output = toDouble;
     }
 
     current->push(output);
     return InliningStatus_Inlined;
 }
 
 IonBuilder::InliningStatus
+IonBuilder::inlineMathPow(CallInfo& callInfo)
+{
+    if (callInfo.argc() != 2 || callInfo.constructing()) {
+        trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
+        return InliningStatus_NotInlined;
+    }
+
+    IonBuilder::InliningStatus status =
+        inlineMathPowHelper(callInfo.getArg(0), callInfo.getArg(1), getInlineReturnType());
+
+    if (status == IonBuilder::InliningStatus_Inlined)
+        callInfo.setImplicitlyUsedUnchecked();
+
+    return status;
+}
+
+IonBuilder::InliningStatus
 IonBuilder::inlineMathRandom(CallInfo& callInfo)
 {
     if (callInfo.constructing()) {
         trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
         return InliningStatus_NotInlined;
     }
 
     if (getInlineReturnType() != MIRType_Double)
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_7/Math/Pow.js
@@ -0,0 +1,104 @@
+// |reftest| skip-if(!xulRuntime.shell)
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/
+ */
+
+var BUGNUMBER = 1135708;
+var summary = "Implement the exponentiation operator";
+
+print(BUGNUMBER + ": " + summary);
+
+// Constant folding
+assertEq(2 ** 2 ** 3, 256);
+assertEq(1 ** 1 ** 4, 1);
+
+// No folding
+var two = 2;
+var three = 3;
+var four = 4;
+assertEq(two ** two ** three, 256);
+assertEq(1 ** 1 ** four, 1);
+
+// Operator precedence
+assertEq(2 ** 3 / 2 ** 3, 1);
+assertEq(2 ** 3 * 2 ** 3, 64);
+assertEq(2 ** 3 + 2 ** 3, 16);
+
+// With parentheses
+assertEq((2 ** 3) ** 2, 64);
+assertEq(2 ** (3 ** 2), 512);
+
+// Assignment operator
+var x = 2;
+assertEq(x **= 2 ** 3, 256);
+assertEq(x, 256);
+
+// Loop to test baseline and ION
+for (var i=0; i<10000; i++) {
+    assertEq((2 ** 3) ** 2, 64);
+    assertEq(2 ** (3 ** 2), 512);
+    var x = 2;
+    assertEq(x **= 2 ** 3, 256);
+    assertEq(x, 256);
+}
+
+// Comments should not be confused with exp operator
+var a, c, e;
+a = c = e = 2;
+assertEq(a**/**b**/c/**/**/**d**/e, 16);
+
+// Two stars separated should not parse as exp operator
+assertThrows(function() { return Reflect.parse("2 * * 3"); }, SyntaxError);
+
+// Check if error propagation works
+var thrower = {
+    get value() {
+        throw new Error();
+    }
+};
+
+assertThrowsInstanceOf(function() { return thrower.value ** 2; }, Error);
+assertThrowsInstanceOf(function() { return 2 ** thrower.value; }, Error);
+assertThrowsInstanceOf(function() { return 2 ** thrower.value ** 2; }, Error);
+
+var convertibleToPrimitive = {
+    valueOf: function() {
+        throw new Error("oops");
+    }
+};
+
+assertThrowsInstanceOf(function() { return convertibleToPrimitive ** 3; }, Error);
+assertThrowsInstanceOf(function() { return 3 ** convertibleToPrimitive; }, Error);
+
+assertEq(NaN ** 2, NaN);
+assertEq(2 ** NaN, NaN);
+assertEq(2 ** "3", 8);
+assertEq("2" ** 3, 8);
+
+// Reflect.parse generates a correct parse tree for simplest case
+var parseTree = Reflect.parse("a ** b");
+assertEq(parseTree.body[0].type, "ExpressionStatement");
+assertEq(parseTree.body[0].expression.operator, "**");
+assertEq(parseTree.body[0].expression.left.name, "a");
+assertEq(parseTree.body[0].expression.right.name, "b");
+
+// Reflect.parse generates a tree following the right-associativity rule
+var parseTree = Reflect.parse("a ** b ** c");
+assertEq(parseTree.body[0].type, "ExpressionStatement");
+assertEq(parseTree.body[0].expression.left.name, "a");
+assertEq(parseTree.body[0].expression.right.operator, "**");
+assertEq(parseTree.body[0].expression.right.left.name, "b");
+assertEq(parseTree.body[0].expression.right.right.name, "c");
+
+
+function assertTrue(v) {
+    assertEq(v, true);
+}
+
+function assertFalse(v) {
+    assertEq(v, false);
+}
+
+if (typeof reportCompare === "function")
+    reportCompare(true, true);
new file mode 100644
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -2047,17 +2047,16 @@ CASE(EnableInterruptsPseudoOpcode)
     SANITY_CHECKS();
     DISPATCH_TO(op);
 }
 
 /* Various 1-byte no-ops. */
 CASE(JSOP_NOP)
 CASE(JSOP_UNUSED2)
 CASE(JSOP_BACKPATCH)
-CASE(JSOP_UNUSED150)
 CASE(JSOP_UNUSED161)
 CASE(JSOP_UNUSED162)
 CASE(JSOP_UNUSED163)
 CASE(JSOP_UNUSED164)
 CASE(JSOP_UNUSED165)
 CASE(JSOP_UNUSED166)
 CASE(JSOP_UNUSED167)
 CASE(JSOP_UNUSED168)
@@ -2650,16 +2649,27 @@ CASE(JSOP_MOD)
     ReservedRooted<Value> rval(&rootValue1, REGS.sp[-1]);
     MutableHandleValue res = REGS.stackHandleAt(-2);
     if (!ModOperation(cx, lval, rval, res))
         goto error;
     REGS.sp--;
 }
 END_CASE(JSOP_MOD)
 
+CASE(JSOP_POW)
+{
+    ReservedRooted<Value> lval(&rootValue0, REGS.sp[-2]);
+    ReservedRooted<Value> rval(&rootValue1, REGS.sp[-1]);
+    MutableHandleValue res = REGS.stackHandleAt(-2);
+    if (!math_pow_handle(cx, lval, rval, res))
+        goto error;
+    REGS.sp--;
+}
+END_CASE(JSOP_POW)
+
 CASE(JSOP_NOT)
 {
     bool cond = ToBoolean(REGS.stackHandleAt(-1));
     REGS.sp--;
     PUSH_BOOLEAN(!cond);
 }
 END_CASE(JSOP_NOT)
 
--- a/js/src/vm/Opcodes.h
+++ b/js/src/vm/Opcodes.h
@@ -1528,17 +1528,25 @@ 1234567890123456789012345678901234567890
      * Placeholder opcode used during bytecode generation. This never
      * appears in a finished script. FIXME: bug 473671.
      *   Category: Statements
      *   Type: Jumps
      *   Operands: int32_t offset
      *   Stack: =>
      */ \
     macro(JSOP_BACKPATCH,     149,"backpatch", NULL,      5,  0,  0,  JOF_JUMP) \
-    macro(JSOP_UNUSED150,     150,"unused150", NULL,      1,  0,  0,  JOF_BYTE) \
+    /*
+     * Pops the top two values 'lval' and 'rval' from the stack, then pushes
+     * the result of 'Math.pow(lval, rval)'.
+     *   Category: Operators
+     *   Type: Arithmetic Operators
+     *   Operands:
+     *   Stack: lval, rval => (lval ** rval)
+     */ \
+    macro(JSOP_POW,           150, "pow",     "**",       1,  2,  1, JOF_BYTE|JOF_ARITH) \
     \
     /*
      * Pops the top of stack value as 'v', sets pending exception as 'v',
      * to trigger rethrow.
      *
      * This opcode is used in conditional catch clauses.
      *   Category: Statements
      *   Type: Exception Handling
--- a/js/src/vm/Xdr.h
+++ b/js/src/vm/Xdr.h
@@ -24,17 +24,17 @@ namespace js {
  * versions.  If deserialization fails, the data should be invalidated if
  * possible.
  *
  * When you change this, run make_opcode_doc.py and copy the new output into
  * this wiki page:
  *
  *  https://developer.mozilla.org/en-US/docs/SpiderMonkey/Internals/Bytecode
  */
-static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 298;
+static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 299;
 static const uint32_t XDR_BYTECODE_VERSION =
     uint32_t(0xb973c0de - XDR_BYTECODE_VERSION_SUBTRAHEND);
 
 static_assert(JSErr_Limit == 407,
               "GREETINGS, POTENTIAL SUBTRAHEND INCREMENTER! If you added or "
               "removed MSG_DEFs from js.msg, you should increment "
               "XDR_BYTECODE_VERSION_SUBTRAHEND and update this assertion's "
               "expected JSErr_Limit value.");
--- a/layout/base/nsPresShell.cpp
+++ b/layout/base/nsPresShell.cpp
@@ -981,17 +981,17 @@ PresShell::Init(nsIDocument* aDocument,
 
   mTouchManager.Init(this, mDocument);
 
   if (mPresContext->IsRootContentDocument()) {
     mZoomConstraintsClient = new ZoomConstraintsClient();
     mZoomConstraintsClient->Init(this, mDocument);
 #ifndef MOZ_WIDGET_ANDROID
     // Fennec will need some work to use this code; see bug 1180267.
-    if (gfxPrefs::MetaViewportEnabled()) {
+    if (gfxPrefs::MetaViewportEnabled() || gfxPrefs::APZAllowZooming()) {
       mMobileViewportManager = new MobileViewportManager(this, mDocument);
     }
 #endif
   }
 }
 
 enum TextPerfLogType {
   eLog_reflow,
--- a/layout/generic/nsBlockReflowContext.cpp
+++ b/layout/generic/nsBlockReflowContext.cpp
@@ -159,17 +159,18 @@ nsBlockReflowContext::ComputeCollapsedBS
           {
             LogicalSize availSpace =
               outerReflowState->ComputedSize(kid->GetWritingMode());
             nsHTMLReflowState innerReflowState(prescontext,
                                                *outerReflowState, kid,
                                                availSpace);
             // Record that we're being optimistic by assuming the kid
             // has no clearance
-            if (kid->StyleDisplay()->mBreakType != NS_STYLE_CLEAR_NONE) {
+            if (kid->StyleDisplay()->mBreakType != NS_STYLE_CLEAR_NONE ||
+                !nsBlockFrame::BlockCanIntersectFloats(kid)) {
               *aMayNeedRetry = true;
             }
             if (ComputeCollapsedBStartMargin(innerReflowState, aMargin,
                                              aClearanceFrame, aMayNeedRetry,
                                              &isEmpty)) {
               line->MarkDirty();
               dirtiedLine = true;
             }
new file mode 100644
--- /dev/null
+++ b/layout/reftests/floats/478834-1-ref.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <title>Bug 478834 Testcase</title>
+  <style>
+  html { overflow: hidden }
+  </style>
+</head>
+<body>
+<div style="background:teal; width: 500px">
+  <div style="background:yellow; width: 100px; height: 50px;"></div>
+  <div style="margin-right: 150px">
+    <table width="100%" style="background: fuchsia">
+      <tr><td>
+        x x x x x x x x x x x x x x x x x x x x x x
+        x x x x x x x x x x x x x x x x x x x x x x
+        x x x x x x x x x x x x x x x x x x x x x x
+        x x x x x x x x x x x x x x x x x x x x x x
+        x x x x x x x x x x x x x x x x x x x x x x
+        x x x x x x x x x x x x x x x x x x x x x x
+      </td></tr>
+    </table>
+  </div>
+</div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/floats/478834-1.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <title>Bug 478834 Testcase</title>
+  <style>
+  html { overflow: hidden }
+  </style>
+</head>
+<body>
+<div style="background:teal; width: 500px">
+  <div style="background:yellow; float:left; width: 100px; height: 50px;"></div>
+  <div style="margin-right: 150px">
+    <table width="100%" style="background: fuchsia">
+      <tr><td>
+        x x x x x x x x x x x x x x x x x x x x x x
+        x x x x x x x x x x x x x x x x x x x x x x
+        x x x x x x x x x x x x x x x x x x x x x x
+        x x x x x x x x x x x x x x x x x x x x x x
+        x x x x x x x x x x x x x x x x x x x x x x
+        x x x x x x x x x x x x x x x x x x x x x x
+      </td></tr>
+    </table>
+  </div>
+</div>
+</body>
+</html>
--- a/layout/reftests/floats/reftest.list
+++ b/layout/reftests/floats/reftest.list
@@ -11,16 +11,17 @@ fuzzy-if(gtkWidget,1,10) == float-outsid
 == zero-height-float-base.html zero-height-float-ref.html
 fails == zero-height-float.html zero-height-float-ref.html # bug 81710
 fails == 345369-1.html 345369-1-ref.html
 fails == 345369-2.html 345369-2-ref.html
 == 345369-3.html 345369-3-ref.html
 == 345369-4.html 345369-4-ref.html
 == 345369-5.html 345369-5-ref.html
 == 429974-1.html 429974-1-ref.html
+== 478834-1.html 478834-1-ref.html
 == 546048-1.html 546048-1-ref.html
 == 775350-1.html 775350-1-ref.html
 == 1114329.html 1114329-ref.html
 == float-in-rtl-1a.html float-in-rtl-1-ref.html
 == float-in-rtl-1b.html float-in-rtl-1-ref.html
 == float-in-rtl-1c.html float-in-rtl-1-ref.html
 == float-in-rtl-1d.html float-in-rtl-1-ref.html
 == float-in-rtl-2a.html float-in-rtl-2-ref.html
--- a/layout/reftests/w3c-css/submitted/css21/replaced-sizing/replaced-elements-all-auto-ref.html
+++ b/layout/reftests/w3c-css/submitted/css21/replaced-sizing/replaced-elements-all-auto-ref.html
@@ -1,16 +1,13 @@
 <!DOCTYPE html>
 <html lang="en-US">
 <head>
   <title>CSS Reference: CSS 2.1 replaced element sizing</title>
   <link rel="author" title="L. David Baron" href="http://dbaron.org/">
-  <link rel="help" href="http://www.w3.org/TR/CSS21/visudet.html#inline-replaced-width">
-  <link rel="help" href="http://www.w3.org/TR/CSS21/visudet.html#inline-replaced-height">
-  <link rel="help" href="http://www.w3.org/TR/CSS21/visudet.html#min-max-widths">
   <meta name="flags" content="should"> <!-- undefinedness in 10.3.2 -->
   <style type="text/css">
 
   div { width: 200px }
 
   span {
     display: inline-block;
     width: 50px;
--- a/layout/reftests/w3c-css/submitted/css21/replaced-sizing/replaced-elements-height-20-ref.html
+++ b/layout/reftests/w3c-css/submitted/css21/replaced-sizing/replaced-elements-height-20-ref.html
@@ -1,16 +1,13 @@
 <!DOCTYPE html>
 <html lang="en-US">
 <head>
   <title>CSS Reference: CSS 2.1 replaced element sizing</title>
   <link rel="author" title="L. David Baron" href="http://dbaron.org/">
-  <link rel="help" href="http://www.w3.org/TR/CSS21/visudet.html#inline-replaced-width">
-  <link rel="help" href="http://www.w3.org/TR/CSS21/visudet.html#inline-replaced-height">
-  <link rel="help" href="http://www.w3.org/TR/CSS21/visudet.html#min-max-widths">
   <meta name="flags" content="should"> <!-- undefinedness in 10.3.2 -->
   <style type="text/css">
 
   div { width: 200px }
 
   span {
     display: inline-block;
     width: 40px;
--- a/layout/reftests/w3c-css/submitted/css21/replaced-sizing/replaced-elements-min-height-40-ref.html
+++ b/layout/reftests/w3c-css/submitted/css21/replaced-sizing/replaced-elements-min-height-40-ref.html
@@ -1,16 +1,13 @@
 <!DOCTYPE html>
 <html lang="en-US">
 <head>
   <title>CSS Reference: CSS 2.1 replaced element sizing</title>
   <link rel="author" title="L. David Baron" href="http://dbaron.org/">
-  <link rel="help" href="http://www.w3.org/TR/CSS21/visudet.html#inline-replaced-width">
-  <link rel="help" href="http://www.w3.org/TR/CSS21/visudet.html#inline-replaced-height">
-  <link rel="help" href="http://www.w3.org/TR/CSS21/visudet.html#min-max-widths">
   <meta name="flags" content="should"> <!-- undefinedness in 10.3.2 -->
   <style type="text/css">
 
   div { width: 200px }
 
   span {
     display: inline-block;
     width: 80px;
--- a/layout/reftests/w3c-css/submitted/css21/replaced-sizing/replaced-elements-min-width-80-ref.html
+++ b/layout/reftests/w3c-css/submitted/css21/replaced-sizing/replaced-elements-min-width-80-ref.html
@@ -1,16 +1,13 @@
 <!DOCTYPE html>
 <html lang="en-US">
 <head>
   <title>CSS Reference: CSS 2.1 replaced element sizing</title>
   <link rel="author" title="L. David Baron" href="http://dbaron.org/">
-  <link rel="help" href="http://www.w3.org/TR/CSS21/visudet.html#inline-replaced-width">
-  <link rel="help" href="http://www.w3.org/TR/CSS21/visudet.html#inline-replaced-height">
-  <link rel="help" href="http://www.w3.org/TR/CSS21/visudet.html#min-max-widths">
   <meta name="flags" content="should"> <!-- undefinedness in 10.3.2 -->
   <style type="text/css">
 
   div { width: 200px }
 
   span {
     display: inline-block;
     width: 80px;
--- a/layout/reftests/w3c-css/submitted/css21/replaced-sizing/replaced-elements-width-40-ref.html
+++ b/layout/reftests/w3c-css/submitted/css21/replaced-sizing/replaced-elements-width-40-ref.html
@@ -1,16 +1,13 @@
 <!DOCTYPE html>
 <html lang="en-US">
 <head>
   <title>CSS Reference: CSS 2.1 replaced element sizing</title>
   <link rel="author" title="L. David Baron" href="http://dbaron.org/">
-  <link rel="help" href="http://www.w3.org/TR/CSS21/visudet.html#inline-replaced-width">
-  <link rel="help" href="http://www.w3.org/TR/CSS21/visudet.html#inline-replaced-height">
-  <link rel="help" href="http://www.w3.org/TR/CSS21/visudet.html#min-max-widths">
   <meta name="flags" content="should"> <!-- undefinedness in 10.3.2 -->
   <style type="text/css">
 
   div { width: 200px }
 
   span {
     display: inline-block;
     width: 40px;
--- a/media/libstagefright/binding/MoofParser.cpp
+++ b/media/libstagefright/binding/MoofParser.cpp
@@ -453,21 +453,16 @@ Moof::ParseTrun(Box& aBox, Tfhd& aTfhd, 
   }
 
   BoxReader reader(aBox);
   if (!reader->CanReadType<uint32_t>()) {
     LOG(Moof, "Incomplete Box (missing flags)");
     return false;
   }
   uint32_t flags = reader->ReadU32();
-  if ((flags & 0x404) == 0x404) {
-    // Can't use these flags together
-    reader->DiscardRemaining();
-    return true;
-  }
   uint8_t version = flags >> 24;
 
   if (!reader->CanReadType<uint32_t>()) {
     LOG(Moof, "Incomplete Box (missing sampleCount)");
     return false;
   }
   uint32_t sampleCount = reader->ReadU32();
   if (sampleCount == 0) {
@@ -485,35 +480,34 @@ Moof::ParseTrun(Box& aBox, Tfhd& aTfhd, 
   }
   if (reader->Remaining() < need) {
     LOG(Moof, "Incomplete Box (have:%lld need:%lld)",
         reader->Remaining(), need);
     return false;
   }
 
   uint64_t offset = aTfhd.mBaseDataOffset + (flags & 1 ? reader->ReadU32() : 0);
-  bool hasFirstSampleFlags = flags & 4;
-  uint32_t firstSampleFlags = hasFirstSampleFlags ? reader->ReadU32() : 0;
+  uint32_t firstSampleFlags =
+    flags & 4 ? reader->ReadU32() : aTfhd.mDefaultSampleFlags;
   uint64_t decodeTime = *aDecodeTime;
   nsTArray<Interval<Microseconds>> timeRanges;
 
   if (!mIndex.SetCapacity(sampleCount, fallible)) {
     LOG(Moof, "Out of Memory");
     return false;
   }
 
   for (size_t i = 0; i < sampleCount; i++) {
     uint32_t sampleDuration =
       flags & 0x100 ? reader->ReadU32() : aTfhd.mDefaultSampleDuration;
     uint32_t sampleSize =
       flags & 0x200 ? reader->ReadU32() : aTfhd.mDefaultSampleSize;
     uint32_t sampleFlags =
-      flags & 0x400 ? reader->ReadU32() : hasFirstSampleFlags && i == 0
-                                            ? firstSampleFlags
-                                            : aTfhd.mDefaultSampleFlags;
+      flags & 0x400 ? reader->ReadU32()
+                    : i ? aTfhd.mDefaultSampleFlags : firstSampleFlags;
     int32_t ctsOffset = 0;
     if (flags & 0x800) {
       ctsOffset = reader->Read32();
     }
 
     Sample sample;
     sample.mByteRange = MediaByteRange(offset, offset + sampleSize);
     offset += sampleSize;
--- a/media/libstagefright/frameworks/av/media/libstagefright/ESDS.cpp
+++ b/media/libstagefright/frameworks/av/media/libstagefright/ESDS.cpp
@@ -133,33 +133,43 @@ status_t ESDS::parseESDescriptor(size_t 
     unsigned URL_Flag = mData[offset] & 0x40;
     unsigned OCRstreamFlag = mData[offset] & 0x20;
 
     ++offset;
     --size;
 
     if (streamDependenceFlag) {
         offset += 2;
+        if (size <= 2) {
+            return ERROR_MALFORMED;
+        }
         size -= 2;
     }
 
     if (URL_Flag) {
         if (offset >= size) {
             return ERROR_MALFORMED;
         }
         unsigned URLlength = mData[offset];
         offset += URLlength + 1;
+        if (size <= URLlength + 1) {
+            return ERROR_MALFORMED;
+        }
         size -= URLlength + 1;
     }
 
     if (OCRstreamFlag) {
         offset += 2;
+        if (size <= 2) {
+            return ERROR_MALFORMED;
+        }
         size -= 2;
 
         if ((offset >= size || mData[offset] != kTag_DecoderConfigDescriptor)
+                && offset >= 2
                 && offset - 2 < size
                 && mData[offset - 2] == kTag_DecoderConfigDescriptor) {
             // Content found "in the wild" had OCRstreamFlag set but was
             // missing OCR_ES_Id, the decoder config descriptor immediately
             // followed instead.
             offset -= 2;
             size += 2;
 
--- a/media/webrtc/trunk/webrtc/modules/video_capture/android/java/src/org/webrtc/videoengine/VideoCaptureAndroid.java
+++ b/media/webrtc/trunk/webrtc/modules/video_capture/android/java/src/org/webrtc/videoengine/VideoCaptureAndroid.java
@@ -51,17 +51,17 @@ public class VideoCaptureAndroid impleme
   private static SurfaceHolder localPreview;
   // Only non-null while capturing, accessed exclusively from synchronized methods.
   Camera camera;
   private Camera.CameraInfo info;
   private CameraThread cameraThread;
   private Handler cameraThreadHandler;
   private Context context;
   private final int id;
-  private final long native_capturer;  // |VideoCaptureAndroid*| in C++.
+  private volatile long native_capturer;  // |VideoCaptureAndroid*| in C++.
   private SurfaceTexture cameraSurfaceTexture;
   private int[] cameraGlTextures = null;
 
   // Arbitrary queue depth.  Higher number means more memory allocated & held,
   // lower number means more sensitivity to processing time in the client (and
   // potentially stalling the capturer if it runs out of buffers to write to).
   private final int numCaptureBuffers = 3;
 
@@ -368,16 +368,25 @@ public class VideoCaptureAndroid impleme
       throw new RuntimeException(e);
     }
     cameraThreadHandler = null;
     cameraThread = null;
     Log.d(TAG, "stopCapture done");
     return status;
   }
 
+  @WebRTCJNITarget
+  private void unlinkCapturer() {
+    // stopCapture might fail. That might leave the callbacks dangling, so make
+    // sure those don't call into dead code.
+    // Note that onPreviewCameraFrame isn't synchronized, so there's no point in
+    // synchronizing us either. ProvideCameraFrame has to do the null check.
+    native_capturer = 0;
+  }
+
   private void stopCaptureOnCameraThread(
       Exchanger<Boolean> result) {
     if (camera == null) {
       if (mResumeCapture == true) {
         // We already got onPause, but now the native code wants us to stop.
         // Do not resume capturing when resuming the app.
         mResumeCapture = false;
         return;
--- a/media/webrtc/trunk/webrtc/modules/video_capture/android/video_capture_android.cc
+++ b/media/webrtc/trunk/webrtc/modules/video_capture/android/video_capture_android.cc
@@ -36,16 +36,18 @@ jobject JNICALL GetContext(JNIEnv* env, 
 void JNICALL ProvideCameraFrame(
     JNIEnv* env,
     jobject,
     jbyteArray javaCameraFrame,
     jint length,
     jint rotation,
     jlong timeStamp,
     jlong context) {
+  if (!context)
+    return;
   webrtc::videocapturemodule::VideoCaptureAndroid* captureModule =
       reinterpret_cast<webrtc::videocapturemodule::VideoCaptureAndroid*>(
           context);
   jbyte* cameraFrame = env->GetByteArrayElements(javaCameraFrame, NULL);
   captureModule->OnIncomingFrame(
       reinterpret_cast<uint8_t*>(cameraFrame), length, rotation, 0);
   env->ReleaseByteArrayElements(javaCameraFrame, cameraFrame, JNI_ABORT);
 }
@@ -162,17 +164,24 @@ int32_t VideoCaptureAndroid::Init(const 
   return 0;
 }
 
 VideoCaptureAndroid::~VideoCaptureAndroid() {
   // Ensure Java camera is released even if our caller didn't explicitly Stop.
   if (_captureStarted)
     StopCapture();
   AttachThreadScoped ats(g_jvm);
-  ats.env()->DeleteGlobalRef(_jCapturer);
+  JNIEnv* env = ats.env();
+
+  // Avoid callbacks into ourself even if the above stopCapture fails.
+  jmethodID j_unlink =
+    env->GetMethodID(g_java_capturer_class, "unlinkCapturer", "()V");
+  env->CallVoidMethod(_jCapturer, j_unlink);
+
+  env->DeleteGlobalRef(_jCapturer);
 }
 
 int32_t VideoCaptureAndroid::StartCapture(
     const VideoCaptureCapability& capability) {
   CriticalSectionScoped cs(&_apiCs);
   AttachThreadScoped ats(g_jvm);
   JNIEnv* env = ats.env();
 
@@ -211,16 +220,17 @@ int32_t VideoCaptureAndroid::StopCapture
 
   memset(&_requestedCapability, 0, sizeof(_requestedCapability));
   memset(&_captureCapability, 0, sizeof(_captureCapability));
   _captureStarted = false;
   // Exit critical section to avoid blocking camera thread inside
   // onIncomingFrame() call.
   _apiCs.Leave();
 
+  // try to stop the capturer.
   jmethodID j_stop =
       env->GetMethodID(g_java_capturer_class, "stopCapture", "()Z");
   return env->CallBooleanMethod(_jCapturer, j_stop) ? 0 : -1;
 }
 
 bool VideoCaptureAndroid::CaptureStarted() {
   CriticalSectionScoped cs(&_apiCs);
   return _captureStarted;
--- a/mobile/android/chrome/content/aboutLogins.js
+++ b/mobile/android/chrome/content/aboutLogins.js
@@ -2,16 +2,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/. */
 
 let Ci = Components.interfaces, Cc = Components.classes, Cu = Components.utils;
 
 Cu.import("resource://gre/modules/Messaging.jsm");
 Cu.import("resource://gre/modules/Services.jsm")
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/TelemetryStopwatch.jsm");
 
 XPCOMUtils.defineLazyGetter(window, "gChromeWin", function()
   window.QueryInterface(Ci.nsIInterfaceRequestor)
     .getInterface(Ci.nsIWebNavigation)
     .QueryInterface(Ci.nsIDocShellTreeItem)
     .rootTreeItem
     .QueryInterface(Ci.nsIInterfaceRequestor)
     .getInterface(Ci.nsIDOMWindow)
@@ -48,17 +49,19 @@ let Logins = {
     let contentBody = document.getElementById("content-body");
     let emptyBody = document.getElementById("empty-body");
     let filterIcon = document.getElementById("filter-button");
 
     this._toggleListBody(true);
     emptyBody.classList.add("hidden");
 
     try {
+      TelemetryStopwatch.start("PWMGR_ABOUT_LOGINS_GET_ALL_LOGINS_MS");
       logins = Services.logins.getAllLogins();
+      TelemetryStopwatch.finish("PWMGR_ABOUT_LOGINS_GET_ALL_LOGINS_MS");
     } catch(e) {
       // Master password was not entered
       debug("Master password permissions error: " + e);
       logins = [];
     }
     this._toggleListBody(false);
 
     if (!logins.length) {
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -2456,17 +2456,17 @@ pref("plugin.java.mime", "application/x-
 
 // How long in minutes we will allow a plugin to work after the user has chosen
 // to allow it "now"
 pref("plugin.sessionPermissionNow.intervalInMinutes", 60);
 // How long in days we will allow a plugin to work after the user has chosen
 // to allow it persistently.
 pref("plugin.persistentPermissionAlways.intervalInDays", 90);
 
-#ifndef DEBUG
+#if !defined(DEBUG) && !defined(MOZ_ASAN)
 // How long a plugin is allowed to process a synchronous IPC message
 // before we consider it "hung".
 pref("dom.ipc.plugins.timeoutSecs", 45);
 // How long a plugin process will wait for a response from the parent
 // to a synchronous request before terminating itself. After this
 // point the child assumes the parent is hung. Currently disabled.
 pref("dom.ipc.plugins.parentTimeoutSecs", 0);
 // How long a plugin in e10s is allowed to process a synchronous IPC
@@ -2482,17 +2482,17 @@ pref("dom.ipc.plugins.hangUITimeoutSecs"
 // Minimum time that the plugin hang UI will be displayed
 pref("dom.ipc.plugins.hangUIMinDisplaySecs", 10);
 #endif
 // How long a content process can take before closing its IPC channel
 // after shutdown is initiated.  If the process exceeds the timeout,
 // we fear the worst and kill it.
 pref("dom.ipc.tabs.shutdownTimeoutSecs", 5);
 #else
-// No timeout in DEBUG builds
+// No timeout in DEBUG or ASan builds
 pref("dom.ipc.plugins.timeoutSecs", 0);
 pref("dom.ipc.plugins.contentTimeoutSecs", 0);
 pref("dom.ipc.plugins.processLaunchTimeoutSecs", 0);
 pref("dom.ipc.plugins.parentTimeoutSecs", 0);
 #ifdef XP_WIN
 pref("dom.ipc.plugins.hangUITimeoutSecs", 0);
 pref("dom.ipc.plugins.hangUIMinDisplaySecs", 0);
 #endif
@@ -4725,21 +4725,21 @@ pref("dom.inter-app-communication-api.en
 // Disable mapped array buffer by default.
 pref("dom.mapped_arraybuffer.enabled", false);
 
 // The tables used for Safebrowsing phishing and malware checks.
 pref("urlclassifier.malwareTable", "goog-malware-shavar,goog-unwanted-shavar,test-malware-simple,test-unwanted-simple");
 pref("urlclassifier.phishTable", "goog-phish-shavar,test-phish-simple");
 pref("urlclassifier.downloadBlockTable", "");
 pref("urlclassifier.downloadAllowTable", "");
-pref("urlclassifier.disallow_completions", "test-malware-simple,test-phish-simple,test-unwanted-simple,goog-downloadwhite-digest256,mozpub-track-digest256");
+pref("urlclassifier.disallow_completions", "test-malware-simple,test-phish-simple,test-unwanted-simple,test-track-simple,goog-downloadwhite-digest256,mozpub-track-digest256");
 
 // The table and update/gethash URLs for Safebrowsing phishing and malware
 // checks.
-pref("urlclassifier.trackingTable", "mozpub-track-digest256");
+pref("urlclassifier.trackingTable", "test-track-simple,mozpub-track-digest256");
 pref("browser.trackingprotection.updateURL", "https://tracking.services.mozilla.com/downloads?client=SAFEBROWSING_ID&appver=%VERSION%&pver=2.2");
 pref("browser.trackingprotection.gethashURL", "https://tracking.services.mozilla.com/gethash?client=SAFEBROWSING_ID&appver=%VERSION%&pver=2.2");
 
 // Turn off Spatial navigation by default.
 pref("snav.enabled", false);
 
 // Original caret implementation on collapsed selection.
 pref("touchcaret.enabled", false);
--- a/netwerk/protocol/res/moz.build
+++ b/netwerk/protocol/res/moz.build
@@ -6,17 +6,17 @@
 
 XPIDL_SOURCES += [
     'nsIResProtocolHandler.idl',
     'nsISubstitutingProtocolHandler.idl',
 ]
 
 XPIDL_MODULE = 'necko_res'
 
-SOURCES += [
+UNIFIED_SOURCES += [
     'ExtensionProtocolHandler.cpp',
     'nsResProtocolHandler.cpp',
     'SubstitutingProtocolHandler.cpp',
 ]
 
 FAIL_ON_WARNINGS = True
 
 include('/ipc/chromium/chromium-config.mozbuild')
--- a/python/mozbuild/mozbuild/base.py
+++ b/python/mozbuild/mozbuild/base.py
@@ -20,16 +20,17 @@ from mozfile.mozfile import rmtree
 
 from .backend.configenvironment import ConfigEnvironment
 from .controller.clobber import Clobberer
 from .mozconfig import (
     MozconfigFindException,
     MozconfigLoadException,
     MozconfigLoader,
 )
+from .util import memoized_property
 from .virtualenv import VirtualenvManager
 
 
 _config_guess_output = []
 
 
 def ancestors(path):
     """Emit the parent directories of a path."""
@@ -285,16 +286,33 @@ class MozbuildObject(ProcessExecutionMix
     @property
     def includedir(self):
         return os.path.join(self.topobjdir, 'dist', 'include')
 
     @property
     def statedir(self):
         return os.path.join(self.topobjdir, '.mozbuild')
 
+    @memoized_property
+    def extra_environment_variables(self):
+        '''Some extra environment variables are stored in .mozconfig.mk.
+        This functions extracts and returns them.'''
+        from pymake.process import ClineSplitter
+        mozconfig_mk = os.path.join(self.topobjdir, '.mozconfig.mk')
+        env = {}
+        with open(mozconfig_mk) as fh:
+            for line in fh:
+                if line.startswith('export '):
+                    exports = ClineSplitter(line, self.topobjdir)[1:]
+                    for e in exports:
+                        if '=' in e:
+                            key, value = e.split('=')
+                            env[key] = value
+        return env
+
     def is_clobber_needed(self):
         if not os.path.exists(self.topobjdir):
             return False
         return Clobberer(self.topsrcdir, self.topobjdir).clobber_needed()
 
     def have_winrm(self):
         # `winrm -h` should print 'winrm version ...' and exit 1
         p = subprocess.Popen(['winrm.exe', '-h'],
--- a/testing/mozharness/mozharness/mozilla/testing/talos.py
+++ b/testing/mozharness/mozharness/mozilla/testing/talos.py
@@ -36,38 +36,48 @@ TalosErrorList = PythonErrorList + [
      'level': CRITICAL,
      'explanation': r"""Most likely the browser failed to launch, or the test was otherwise unsuccessful in even starting."""},
 ]
 
 # TODO: check for running processes on script invocation
 
 class TalosOutputParser(OutputParser):
     minidump_regex = re.compile(r'''talosError: "error executing: '(\S+) (\S+) (\S+)'"''')
-    minidump_output = None
     worst_tbpl_status = TBPL_SUCCESS
 
+    def __init__(self, **kwargs):
+        super(TalosOutputParser, self).__init__(**kwargs)
+        self.minidump_output = None
+        self.found_talosdata = False
+
+    def update_worst_log_and_tbpl_levels(self, log_level, tbpl_level):
+        self.worst_log_level = self.worst_level(log_level,
+                                                self.worst_log_level)
+        self.worst_tbpl_status = self.worst_level(
+            tbpl_level, self.worst_tbpl_status,
+            levels=TBPL_WORST_LEVEL_TUPLE
+        )
+
     def parse_single_line(self, line):
         """ In Talos land, every line that starts with RETURN: needs to be
         printed with a TinderboxPrint:"""
         if line.startswith("RETURN:"):
             line.replace("RETURN:", "TinderboxPrint:")
         m = self.minidump_regex.search(line)
         if m:
             self.minidump_output = (m.group(1), m.group(2), m.group(3))
 
+        if line.startswith('INFO : TALOSDATA: '):
+            self.found_talosdata = True
+
         # now let's check if buildbot should retry
         harness_retry_re = TinderBoxPrintRe['harness_error']['retry_regex']
         if harness_retry_re.search(line):
             self.critical(' %s' % line)
-            self.worst_log_level = self.worst_level(CRITICAL,
-                                                    self.worst_log_level)
-            self.worst_tbpl_status = self.worst_level(
-                TBPL_RETRY, self.worst_tbpl_status,
-                levels=TBPL_WORST_LEVEL_TUPLE
-            )
+            self.update_worst_log_and_tbpl_levels(CRITICAL, TBPL_RETRY)
             return  # skip base parse_single_line
         super(TalosOutputParser, self).parse_single_line(line)
 
 
 talos_config_options = [
     [["-a", "--tests"],
      {'action': 'extend',
       "dest": "tests",
@@ -589,28 +599,27 @@ class Talos(TestingMixin, MercurialScrip
         self.return_code = self.run_command(command, cwd=self.workdir,
                                             output_timeout=output_timeout,
                                             output_parser=parser,
                                             env=env)
         if parser.minidump_output:
             self.info("Looking at the minidump files for debugging purposes...")
             for item in parser.minidump_output:
                 self.run_command(["ls", "-l", item])
+        if not parser.found_talosdata:
+            self.critical("No talos data in output!")
+            parser.update_worst_log_and_tbpl_levels(WARNING, TBPL_WARNING)
+
         if self.return_code not in [0]:
             # update the worst log level and tbpl status
             log_level = ERROR
             tbpl_level = TBPL_FAILURE
             if self.return_code == 1:
                 log_level = WARNING
                 tbpl_level = TBPL_WARNING
             if self.return_code == 4:
                 log_level = WARNING
                 tbpl_level = TBPL_RETRY
 
-            parser.worst_log_level = parser.worst_level(
-                log_level, parser.worst_log_level
-            )
-            parser.worst_tbpl_status = parser.worst_level(
-                tbpl_level, parser.worst_tbpl_status,
-                levels=TBPL_WORST_LEVEL_TUPLE
-            )
+            parser.update_worst_log_and_tbpl_levels(log_level, tbpl_level)
+
         self.buildbot_status(parser.worst_tbpl_status,
                              level=parser.worst_log_level)
--- a/toolkit/components/autocomplete/nsAutoCompleteController.cpp
+++ b/toolkit/components/autocomplete/nsAutoCompleteController.cpp
@@ -1339,17 +1339,19 @@ nsAutoCompleteController::EnterMatch(boo
         // If completeselectedindex is true, and EnterMatch was not invoked by
         // mouse-clicking a match (for example the user pressed Enter),
         // don't fill in the value as it will have already been filled in as
         // needed, unless the selected match has a final complete value that
         // differs from the user-facing value.
         GetResultValueAt(mCompletedSelectionIndex, true, finalValue);
         nsAutoString inputValue;
         input->GetTextValue(inputValue);
-        if (!finalValue.Equals(inputValue)) {
+        nsAutoString completedValue;
+        GetResultValueAt(mCompletedSelectionIndex, false, completedValue);
+        if (completedValue.Equals(inputValue) && !finalValue.Equals(inputValue)) {
           value = finalValue;
         }
         // Note that if the user opens the popup, mouses over entries without
         // ever selecting one with the keyboard, and then hits enter, none of
         // the above cases will be hitt, since mouseover doesn't activate
         // completeselectedindex and thus mCompletedSelectionIndex would be
         // -1.
       }
new file mode 100644
--- /dev/null
+++ b/toolkit/components/passwordmgr/OSCrypto.jsm
@@ -0,0 +1,22 @@
+/* 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/. */
+
+/**
+ * Common front for various implementations of OSCrypto
+ */
+
+"use strict";
+
+Components.utils.import("resource://gre/modules/AppConstants.jsm");
+Components.utils.import("resource://gre/modules/Services.jsm");
+
+this.EXPORTED_SYMBOLS = ["OSCrypto"];
+
+let OSCrypto = {};
+
+if (AppConstants.platform == "win") {
+  Services.scriptloader.loadSubScript("resource://gre/modules/OSCrypto_win.js", this);
+} else {
+  throw new Error("OSCrypto.jsm isn't supported on this platform");
+}
new file mode 100644
--- /dev/null
+++ b/toolkit/components/passwordmgr/OSCrypto_win.js
@@ -0,0 +1,143 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+let { classes: Cc, interfaces: Ci, results: Cr, utils: Cu } = Components;
+
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+
+XPCOMUtils.defineLazyModuleGetter(this, "ctypes", "resource://gre/modules/ctypes.jsm");
+
+function OSCrypto() {
+  this._structs = {};
+  this._functions = new Map();
+  this._libs = new Map();
+  this._structs.DATA_BLOB = new ctypes.StructType("DATA_BLOB",
+                                                  [
+                                                    {cbData: ctypes.uint32_t},
+                                                    {pbData: ctypes.uint8_t.ptr}
+                                                  ]);
+
+  try {
+
+    this._libs.set("crypt32", ctypes.open("Crypt32"));
+    this._libs.set("kernel32", ctypes.open("Kernel32"));
+
+    this._functions.set("CryptProtectData",
+                        this._libs.get("crypt32").declare("CryptProtectData",
+                                                          ctypes.winapi_abi,
+                                                          ctypes.uint32_t,
+                                                          this._structs.DATA_BLOB.ptr,
+                                                          ctypes.voidptr_t,
+                                                          ctypes.voidptr_t,
+                                                          ctypes.voidptr_t,
+                                                          ctypes.voidptr_t,
+                                                          ctypes.uint32_t,
+                                                          this._structs.DATA_BLOB.ptr));
+
+    this._functions.set("CryptUnprotectData",
+                        this._libs.get("crypt32").declare("CryptUnprotectData",
+                                                          ctypes.winapi_abi,
+                                                          ctypes.uint32_t,
+                                                          this._structs.DATA_BLOB.ptr,
+                                                          ctypes.voidptr_t,
+                                                          ctypes.voidptr_t,
+                                                          ctypes.voidptr_t,
+                                                          ctypes.voidptr_t,
+                                                          ctypes.uint32_t,
+                                                          this._structs.DATA_BLOB.ptr));
+    this._functions.set("LocalFree",
+                        this._libs.get("kernel32").declare("LocalFree",
+                                                           ctypes.winapi_abi,
+                                                           ctypes.uint32_t,
+                                                           ctypes.voidptr_t));
+  } catch (ex) {
+    Cu.reportError(ex);
+    this.finalize();
+    throw ex;
+  }
+}
+OSCrypto.prototype = {
+  /**
+   * Decrypt an array of numbers using the windows CryptUnprotectData API.
+   * @param {number[]} array - the encrypted array that needs to be decrypted.
+   * @returns {string} the decryption of the array.
+   */
+  decryptData(array) {
+    let decryptedData = "";
+    let encryptedData = ctypes.uint8_t.array(array.length)(array);
+    let inData = new this._structs.DATA_BLOB(encryptedData.length, encryptedData);
+    let outData = new this._structs.DATA_BLOB();
+    let status = this._functions.get("CryptUnprotectData")(inData.address(), null,
+                                                           null, null, null, 0,
+                                                           outData.address());
+    if (status === 0) {
+      throw new Error("decryptData failed: " + status);
+    }
+
+    // convert byte array to JS string.
+    let len = outData.cbData;
+    let decrypted = ctypes.cast(outData.pbData,
+                                ctypes.uint8_t.array(len).ptr).contents;
+    for (let i = 0; i < decrypted.length; i++) {
+      decryptedData += String.fromCharCode(decrypted[i]);
+    }
+
+    this._functions.get("LocalFree")(outData.pbData);
+    return decryptedData;
+  },
+
+  /**
+   * Encrypt a string using the windows CryptProtectData API.
+   * @param {string} string - the string that is going to be encrypted.
+   * @returns {number[]} the encrypted string encoded as an array of numbers.
+   */
+  encryptData(string) {
+    let encryptedData = [];
+    let decryptedData = ctypes.uint8_t.array(string.length)();
+
+    for (let i = 0; i < string.length; i++) {
+      decryptedData[i] = string.charCodeAt(i);
+    }
+
+    let inData = new this._structs.DATA_BLOB(string.length, decryptedData);
+    let outData = new this._structs.DATA_BLOB();
+    let status = this._functions.get("CryptProtectData")(inData.address(), null,
+                                                         null, null, null, 0,
+                                                         outData.address());
+    if (status === 0) {
+      throw new Error("encryptData failed: " + status);
+    }
+
+    // convert byte array to JS string.
+    let len = outData.cbData;
+    let encrypted = ctypes.cast(outData.pbData,
+                                ctypes.uint8_t.array(len).ptr).contents;
+
+    for (let i = 0; i < len; i++) {
+      encryptedData.push(encrypted[i]);
+    }
+
+    this._functions.get("LocalFree")(outData.pbData);
+    return encryptedData;
+  },
+
+  /**
+   * Must be invoked once after last use of any of the provided helpers.
+   */
+  finalize() {
+    this._structs = {};
+    this._functions.clear();
+    for (let lib of this._libs.values()) {
+      try {
+        lib.close();
+      } catch (ex) {
+        Cu.reportError(ex);
+      }
+    }
+    this._libs.clear();
+  },
+};
--- a/toolkit/components/passwordmgr/moz.build
+++ b/toolkit/components/passwordmgr/moz.build
@@ -36,31 +36,37 @@ EXTRA_PP_COMPONENTS += [
 ]
 
 EXTRA_PP_JS_MODULES += [
     'LoginManagerParent.jsm',
 ]
 
 EXTRA_JS_MODULES += [
     'InsecurePasswordUtils.jsm',
-    'LoginDoorhangers.jsm',
     'LoginHelper.jsm',
     'LoginManagerContent.jsm',
     'LoginRecipes.jsm',
+    'OSCrypto.jsm',
 ]
 
 if CONFIG['OS_TARGET'] == 'Android':
     EXTRA_COMPONENTS += [
         'storage-mozStorage.js',
     ]
 else:
     EXTRA_COMPONENTS += [
         'storage-json.js',
     ]
     EXTRA_JS_MODULES += [
+        'LoginDoorhangers.jsm',
         'LoginImport.jsm',
         'LoginStore.jsm',
     ]
 
+if CONFIG['OS_TARGET'] == 'WINNT':
+    EXTRA_JS_MODULES += [
+        'OSCrypto_win.js',
+    ]
+
 JAR_MANIFESTS += ['jar.mn']
 
 with Files('**'):
     BUG_COMPONENT = ('Toolkit', 'Password Manager')
--- a/toolkit/components/places/PlacesSearchAutocompleteProvider.jsm
+++ b/toolkit/components/places/PlacesSearchAutocompleteProvider.jsm
@@ -128,17 +128,19 @@ const SearchAutocompleteProviderInternal
 
 function SearchSuggestionControllerWrapper(engine, searchToken,
                                            inPrivateContext, maxResults) {
   this._controller = new SearchSuggestionController();
   this._controller.maxLocalResults = 0;
   this._controller.maxRemoteResults = maxResults;
   let promise = this._controller.fetch(searchToken, inPrivateContext, engine);
   this._suggestions = [];
+  this._success = false;
   this._promise = promise.then(results => {
+    this._success = true;
     this._suggestions = (results ? results.remote : null) || [];
   }).catch(err => {
     // fetch() rejects its promise if there's a pending request.
   });
 }
 
 SearchSuggestionControllerWrapper.prototype = {
 
@@ -160,16 +162,24 @@ SearchSuggestionControllerWrapper.protot
    */
   consume() {
     return !this._suggestions.length ? [null, null] :
            [SearchAutocompleteProviderInternal.defaultMatch,
             this._suggestions.shift()];
   },
 
   /**
+   * Returns the number of fetched suggestions, or -1 if the fetching was
+   * incomplete or failed.
+   */
+  get resultsCount() {
+    return this._success ? this._suggestions.length : -1;
+  },
+
+  /**
    * Stops the fetch.
    */
   stop() {
     this._controller.stop();
   },
 };
 
 let gInitializationPromise = null;
--- a/toolkit/components/places/UnifiedComplete.js
+++ b/toolkit/components/places/UnifiedComplete.js
@@ -33,16 +33,18 @@ const PREF_MATCH_TITLE =            [ "m
 const PREF_MATCH_URL =              [ "match.url",              "@" ];
 
 const PREF_SUGGEST_HISTORY =        [ "suggest.history",        true ];
 const PREF_SUGGEST_BOOKMARK =       [ "suggest.bookmark",       true ];
 const PREF_SUGGEST_OPENPAGE =       [ "suggest.openpage",       true ];
 const PREF_SUGGEST_HISTORY_ONLYTYPED = [ "suggest.history.onlyTyped", false ];
 const PREF_SUGGEST_SEARCHES =       [ "suggest.searches",       true ];
 
+const PREF_MAX_CHARS_FOR_SUGGEST =  [ "maxCharsForSearchSuggestions", 20];
+
 // Match type constants.
 // These indicate what type of search function we should be using.
 const MATCH_ANYWHERE = Ci.mozIPlacesAutoComplete.MATCH_ANYWHERE;
 const MATCH_BOUNDARY_ANYWHERE = Ci.mozIPlacesAutoComplete.MATCH_BOUNDARY_ANYWHERE;
 const MATCH_BOUNDARY = Ci.mozIPlacesAutoComplete.MATCH_BOUNDARY;
 const MATCH_BEGINNING = Ci.mozIPlacesAutoComplete.MATCH_BEGINNING;
 const MATCH_BEGINNING_CASE_SENSITIVE = Ci.mozIPlacesAutoComplete.MATCH_BEGINNING_CASE_SENSITIVE;
 
@@ -411,16 +413,17 @@ XPCOMUtils.defineLazyGetter(this, "Prefs
     store.restrictSearchesToken = prefs.get(...PREF_RESTRICT_SEARCHES);
     store.matchTitleToken = prefs.get(...PREF_MATCH_TITLE);
     store.matchURLToken = prefs.get(...PREF_MATCH_URL);
     store.suggestHistory = prefs.get(...PREF_SUGGEST_HISTORY);
     store.suggestBookmark = prefs.get(...PREF_SUGGEST_BOOKMARK);
     store.suggestOpenpage = prefs.get(...PREF_SUGGEST_OPENPAGE);
     store.suggestTyped = prefs.get(...PREF_SUGGEST_HISTORY_ONLYTYPED);
     store.suggestSearches = prefs.get(...PREF_SUGGEST_SEARCHES);
+    store.maxCharsForSearchSuggestions = prefs.get(...PREF_MAX_CHARS_FOR_SUGGEST);
 
     // If history is not set, onlyTyped value should be ignored.
     if (!store.suggestHistory) {
       store.suggestTyped = false;
     }
     store.defaultBehavior = types.concat("Typed").reduce((memo, type) => {
       let prefValue = store["suggest" + type];
       return memo | (prefValue &&
@@ -593,19 +596,21 @@ function makeActionURL(action, params) {
  *          possibly in permanent private-browsing mode.  The search
  *          should exclude privacy-sensitive results as appropriate.
  * @param autocompleteListener
  *        An nsIAutoCompleteObserver.
  * @param resultListener
  *        An nsIAutoCompleteSimpleResultListener.
  * @param autocompleteSearch
  *        An nsIAutoCompleteSearch.
+ * @param prohibitSearchSuggestions
+ *        Whether search suggestions are allowed for this search.
  */
 function Search(searchString, searchParam, autocompleteListener,
-                resultListener, autocompleteSearch) {
+                resultListener, autocompleteSearch, prohibitSearchSuggestions) {
   // We want to store the original string for case sensitive searches.
   this._originalSearchString = searchString;
   this._trimmedOriginalSearchString = searchString.trim();
   this._searchString = fixupSearchText(this._trimmedOriginalSearchString.toLowerCase());
 
   this._matchBehavior = Prefs.matchBehavior;
   // Set the default behavior for this search.
   this._behavior = this._searchString ? Prefs.defaultBehavior
@@ -627,16 +632,18 @@ function Search(searchString, searchPara
   // host, but the path must be matched in a case sensitive way.
   let pathIndex =
     this._trimmedOriginalSearchString.indexOf("/", this._strippedPrefix.length);
   this._autofillUrlSearchString = fixupSearchText(
     this._trimmedOriginalSearchString.slice(0, pathIndex).toLowerCase() +
     this._trimmedOriginalSearchString.slice(pathIndex)
   );
 
+  this._prohibitSearchSuggestions = prohibitSearchSuggestions;
+
   this._listener = autocompleteListener;
   this._autocompleteSearch = autocompleteSearch;
 
   // Create a new result to add eventual matches.  Note we need a result
   // regardless having matches.
   let result = Cc["@mozilla.org/autocomplete/simple-result;1"]
                  .createInstance(Ci.nsIAutoCompleteSimpleResult);
   result.setSearchString(searchString);
@@ -904,28 +911,39 @@ Search.prototype = {
       }
     }
 
     // Ensure to fill any remaining space.
     yield Promise.all(this._remoteMatchesPromises);
   }),
 
   *_matchSearchSuggestions() {
-    if (!this.hasBehavior("searches") || this._inPrivateWindow) {
+    // Limit the string sent for search suggestions to a maximum length.
+    let searchString = this._searchTokens.join(" ")
+                           .substr(0, Prefs.maxCharsForSearchSuggestions);
+    // Avoid fetching suggestions if they are not required, private browsing
+    // mode is enabled, or the search string may expose sensitive information.
+    if (!this.hasBehavior("searches") || this._inPrivateWindow ||
+        this._prohibitSearchSuggestionsFor(searchString)) {
       return;
     }
 
     this._searchSuggestionController =
       PlacesSearchAutocompleteProvider.getSuggestionController(
-        this._searchTokens.join(" "),
+        searchString,
         this._inPrivateWindow,
         Prefs.maxRichResults
       );
     let promise = this._searchSuggestionController.fetchCompletePromise
       .then(() => {
+        if (this._searchSuggestionController.resultsCount >= 0 &&
+            this._searchSuggestionController.resultsCount < 2) {
+          // The original string is used to properly compare with the next search.
+          this._lastLowResultsSearchSuggestion = this._originalSearchString;
+        }
         while (this.pending && this._remoteMatchesCount < Prefs.maxRichResults) {
           let [match, suggestion] = this._searchSuggestionController.consume();
           if (!suggestion)
             break;
           // Don't include the restrict token, if present.
           let searchString = this._searchTokens.join(" ");
           this._addSearchEngineMatch(match, searchString, suggestion);
         }
@@ -935,16 +953,38 @@ Search.prototype = {
       // We're done if we're restricting to search suggestions.
       yield promise;
       this.stop();
     } else {
       this._remoteMatchesPromises.push(promise);
     }
   },
 
+  _prohibitSearchSuggestionsFor(searchString) {
+    if (this._prohibitSearchSuggestions)
+      return true;
+
+    // Suggestions for a single letter are unlikely to be useful.
+    if (searchString.length < 2)
+      return true;
+
+    let tokens = searchString.split(/\s/);
+
+    // The first token may be a whitelisted host.
+    if (REGEXP_SINGLEWORD_HOST.test(tokens[0]) &&
+        Services.uriFixup.isDomainWhitelisted(tokens[0], -1))
+      return true;
+
+    // Disallow fetching search suggestions for strings looking like URLs, to
+    // avoid disclosing information about networks or passwords.
+    return tokens.some(token => {
+      return ["/", "@", ":", "."].some(c => token.includes(c));
+    });
+  },
+
   _matchKnownUrl: function* (conn) {
     // Hosts have no "/" in them.
     let lastSlashIndex = this._searchString.lastIndexOf("/");
     // Search only URLs if there's a slash in the search string...
     if (lastSlashIndex != -1) {
       // ...but not if it's exactly at the end of the search string.
       if (lastSlashIndex < this._searchString.length - 1) {
         // We don't want to execute this query right away because it needs to
@@ -1696,18 +1736,25 @@ UnifiedComplete.prototype = {
     // Stop the search in case the controller has not taken care of it.
     if (this._currentSearch) {
       this.stopSearch();
     }
 
     // Note: We don't use previousResult to make sure ordering of results are
     //       consistent.  See bug 412730 for more details.
 
+    // If the previous search didn't fetch enough search suggestions, it's
+    // unlikely a longer text would do.
+    let prohibitSearchSuggestions =
+      this._lastLowResultsSearchSuggestion &&
+      searchString.length > this._lastLowResultsSearchSuggestion.length &&
+      searchString.startsWith(this._lastLowResultsSearchSuggestion);
+
     this._currentSearch = new Search(searchString, searchParam, listener,
-                                     this, this);
+                                     this, this, prohibitSearchSuggestions);
 
     // If we are not enabled, we need to return now.  Notice we need an empty
     // result regardless, so we still create the Search object.
     if (!Prefs.enabled) {
       this.finishSearch(true);
       return;
     }
 
@@ -1740,16 +1787,17 @@ UnifiedComplete.prototype = {
    *        Indicates if we should notify the AutoComplete listener about our
    *        results or not.
    */
   finishSearch: function (notify=false) {
     TelemetryStopwatch.cancel(TELEMETRY_1ST_RESULT, this);
     TelemetryStopwatch.cancel(TELEMETRY_6_FIRST_RESULTS, this);
     // Clear state now to avoid race conditions, see below.
     let search = this._currentSearch;
+    this._lastLowResultsSearchSuggestion = search._lastLowResultsSearchSuggestion;
     delete this._currentSearch;
 
     if (!notify)
       return;
 
     // There is a possible race condition here.
     // When a search completes it calls finishSearch that notifies results
     // here.  When the controller gets the last result it fires
--- a/toolkit/components/places/tests/unifiedcomplete/test_searchSuggestions.js
+++ b/toolkit/components/places/tests/unifiedcomplete/test_searchSuggestions.js
@@ -409,8 +409,76 @@ add_task(function* mixup_frecency() {
         title: "low frecency 1" },
       { uri: NetUtil.newURI("http://example.com/lo0"),
         title: "low frecency 0" },
     ],
   });
 
   yield cleanUpSuggestions();
 });
+
+add_task(function* prohibit_suggestions() {
+  Services.prefs.setBoolPref(SUGGEST_PREF, true);
+
+  yield check_autocomplete({
+    search: "localhost",
+    matches: [
+      {
+        uri: makeActionURI(("searchengine"), {
+          engineName: ENGINE_NAME,
+          input: "localhost foo",
+          searchQuery: "localhost",
+          searchSuggestion: "localhost foo",
+        }),
+        title: ENGINE_NAME,
+        style: ["action", "searchengine"],
+        icon: "",
+      },
+      {
+        uri: makeActionURI(("searchengine"), {
+          engineName: ENGINE_NAME,
+          input: "localhost bar",
+          searchQuery: "localhost",
+          searchSuggestion: "localhost bar",
+        }),
+        title: ENGINE_NAME,
+        style: ["action", "searchengine"],
+        icon: "",
+      },
+    ],
+  });
+  Services.prefs.setBoolPref("browser.fixup.domainwhitelist.localhost", true);
+  do_register_cleanup(() => {
+    Services.prefs.clearUserPref("browser.fixup.domainwhitelist.localhost");
+  });
+  yield check_autocomplete({
+    search: "localhost",
+    matches: [],
+  });
+
+  yield check_autocomplete({
+    search: "1.2.3.4",
+    matches: [],
+  });
+  yield check_autocomplete({
+    search: "[2001::1]:30",
+    matches: [],
+  });
+  yield check_autocomplete({
+    search: "user:pass@test",
+    matches: [],
+  });
+  yield check_autocomplete({
+    search: "test/test",
+    matches: [],
+  });
+  yield check_autocomplete({
+    search: "data:text/plain,Content",
+    matches: [],
+  });
+
+  yield check_autocomplete({
+    search: "a",
+    matches: [],
+  });
+
+  yield cleanUpSuggestions();
+});
--- a/toolkit/components/telemetry/Histograms.json
+++ b/toolkit/components/telemetry/Histograms.json
@@ -8218,16 +8218,24 @@
     "alert_emails": ["firefox-dev@mozilla.org", "gavin@mozilla.com"],
     "expires_in_version": "50",
     "kind": "exponential",
     "high": "30000",
     "n_buckets": 20,
     "extended_statistics_ok": true,
     "description": "Sanitize: Time it takes to sanitize the open windows list (ms)"
   },
+  "PWMGR_ABOUT_LOGINS_GET_ALL_LOGINS_MS": {
+    "expires_in_version": "55",
+    "kind": "exponential",
+    "high": 60000,
+    "n_buckets": 30,
+    "extended_statistics_ok": true,
+    "description": "How long getAllLogins() on about:logins takes for mobile users"
+  },
   "PWMGR_BLOCKLIST_NUM_SITES": {
     "expires_in_version": "never",
     "kind": "exponential",
     "high": 100,
     "n_buckets" : 10,
     "extended_statistics_ok": true,
     "description": "The number of sites for which the user has explicitly rejected saving logins"
   },
--- a/toolkit/components/telemetry/TelemetryEnvironment.jsm
+++ b/toolkit/components/telemetry/TelemetryEnvironment.jsm
@@ -121,16 +121,17 @@ const DEFAULT_ENVIRONMENT_PREFS = new Ma
   ["layers.acceleration.disabled", TelemetryEnvironment.RECORD_PREF_VALUE],
   ["layers.acceleration.force-enabled", TelemetryEnvironment.RECORD_PREF_VALUE],
   ["layers.async-pan-zoom.enabled", TelemetryEnvironment.RECORD_PREF_VALUE],
   ["layers.async-video-oop.enabled", TelemetryEnvironment.RECORD_PREF_VALUE],
   ["layers.async-video.enabled", TelemetryEnvironment.RECORD_PREF_VALUE],
   ["layers.componentalpha.enabled", TelemetryEnvironment.RECORD_PREF_VALUE],
   ["layers.d3d11.disable-warp", TelemetryEnvironment.RECORD_PREF_VALUE],
   ["layers.d3d11.force-warp", TelemetryEnvironment.RECORD_PREF_VALUE],
+  ["layers.offmainthreadcomposition.enabled", TelemetryEnvironment.RECORD_PREF_VALUE],
   ["layers.prefer-d3d9", TelemetryEnvironment.RECORD_PREF_VALUE],
   ["layers.prefer-opengl", TelemetryEnvironment.RECORD_PREF_VALUE],
   ["layout.css.devPixelsPerPx", TelemetryEnvironment.RECORD_PREF_VALUE],
   ["network.proxy.autoconfig_url", TelemetryEnvironment.RECORD_PREF_STATE],
   ["network.proxy.http", TelemetryEnvironment.RECORD_PREF_STATE],
   ["network.proxy.ssl", TelemetryEnvironment.RECORD_PREF_STATE],
   ["pdfjs.disabled", TelemetryEnvironment.RECORD_PREF_VALUE],
   ["places.history.enabled", TelemetryEnvironment.RECORD_PREF_VALUE],
--- a/toolkit/components/telemetry/docs/environment.rst
+++ b/toolkit/components/telemetry/docs/environment.rst
@@ -134,17 +134,18 @@ Structure::
               ...
             ],
             // Note: currently only added on Desktop. On Linux, only a single
             // monitor is returned representing the entire virtual screen.
             monitors: [
               {
                 screenWidth: <number>,  // screen width in pixels
                 screenHeight: <number>, // screen height in pixels
-                refreshRate: <number>,  // refresh rate in hertz (present on Windows only)
+                refreshRate: <number>,  // refresh rate in hertz (present on Windows only).
+                                        //  (values <= 1 indicate an unknown value)
                 pseudoDisplay: <bool>,  // networked screen (present on Windows only)
                 scale: <number>,        // backing scale factor (present on Mac only)
               },
               ...
             ],
             features: {
               compositor: <string>,     // Layers backend for compositing (eg "d3d11", "none", "opengl")
 
@@ -156,16 +157,18 @@ Structure::
               //   "disabled"    - User explicitly disabled this default feature.
               //   "failed"      - This feature was attempted but failed to initialize.
               //   "available"   - User has this feature available.
               "d3d11" { // This feature is Windows-only.
                 status: <string>,
                 warp: <bool>,           // Software rendering (WARP) mode was chosen.
                 textureSharing: <bool>  // Whether or not texture sharing works.
                 version: <number>,      // The D3D11 device feature level.
+                blacklisted: <bool>,    // Whether D3D11 is blacklisted; use to see whether WARP
+                                        // was blacklist induced or driver-failure induced.
               },
               "d2d" { // This feature is Windows-only.
                 status: <string>,
                 version: <string>,      // Either "1.0" or "1.1".
               },
             },
           },
       },
--- a/toolkit/components/url-classifier/SafeBrowsing.jsm
+++ b/toolkit/components/url-classifier/SafeBrowsing.jsm
@@ -209,41 +209,51 @@ this.SafeBrowsing = {
 
 
   addMozEntries: function() {
     // Add test entries to the DB.
     // XXX bug 779008 - this could be done by DB itself?
     const phishURL    = "itisatrap.org/firefox/its-a-trap.html";
     const malwareURL  = "itisatrap.org/firefox/its-an-attack.html";
     const unwantedURL = "itisatrap.org/firefox/unwanted.html";
+    const trackerURLs  = [
+      "trackertest.org/",
+      "itisatracker.org/",
+    ];
 
     let update = "n:1000\ni:test-malware-simple\nad:1\n" +
                  "a:1:32:" + malwareURL.length + "\n" +
                  malwareURL;
     update += "n:1000\ni:test-phish-simple\nad:1\n" +
               "a:1:32:" + phishURL.length + "\n" +
               phishURL;
     update += "n:1000\ni:test-unwanted-simple\nad:1\n" +
               "a:1:32:" + unwantedURL.length + "\n" +
               unwantedURL;
+    trackerURLs.forEach((trackerURL, i) => {
+      update += "n:1000\ni:test-track-simple\nad:1\n" +
+                "a:" + (i + 1) + ":32:" + trackerURL.length + "\n" +
+                trackerURL;
+    });
     log("addMozEntries:", update);
 
     let db = Cc["@mozilla.org/url-classifier/dbservice;1"].
              getService(Ci.nsIUrlClassifierDBService);
 
     // nsIUrlClassifierUpdateObserver
     let dummyListener = {
       updateUrlRequested: function() { },
       streamFinished:     function() { },
       updateError:        function() { },
       updateSuccess:      function() { }
     };
 
     try {
-      db.beginUpdate(dummyListener, "test-malware-simple,test-phish-simple,test-unwanted-simple", "");
+      let tables = "test-malware-simple,test-phish-simple,test-unwanted-simple,test-track-simple";
+      db.beginUpdate(dummyListener, tables, "");
       db.beginStream("", "");
       db.updateStream(update);
       db.finishStream();
       db.finishUpdate();
     } catch(ex) {
       // beginUpdate will throw harmlessly if there's an existing update in progress, ignore failures.
       log("addMozEntries failed!", ex);
     }
--- a/toolkit/components/url-classifier/tests/unit/head_urlclassifier.js
+++ b/toolkit/components/url-classifier/tests/unit/head_urlclassifier.js
@@ -49,27 +49,30 @@ function delFile(name) {
 }
 
 function cleanUp() {
   delFile("urlclassifier3.sqlite");
   delFile("safebrowsing/classifier.hashkey");
   delFile("safebrowsing/test-phish-simple.sbstore");
   delFile("safebrowsing/test-malware-simple.sbstore");
   delFile("safebrowsing/test-unwanted-simple.sbstore");
+  delFile("safebrowsing/test-track-simple.sbstore");
   delFile("safebrowsing/test-phish-simple.cache");
   delFile("safebrowsing/test-malware-simple.cache");
   delFile("safebrowsing/test-unwanted-simple.cache");
+  delFile("safebrowsing/test-track-simple.cache");
   delFile("safebrowsing/test-phish-simple.pset");
   delFile("safebrowsing/test-malware-simple.pset");
   delFile("safebrowsing/test-unwanted-simple.pset");
+  delFile("safebrowsing/test-track-simple.pset");
   delFile("testLarge.pset");
   delFile("testNoDelta.pset");
 }
 
-var allTables = "test-phish-simple,test-malware-simple,test-unwanted-simple";
+var allTables = "test-phish-simple,test-malware-simple,test-unwanted-simple,test-track-simple";
 
 var dbservice = Cc["@mozilla.org/url-classifier/dbservice;1"].getService(Ci.nsIUrlClassifierDBService);
 var streamUpdater = Cc["@mozilla.org/url-classifier/streamupdater;1"]
                     .getService(Ci.nsIUrlClassifierStreamUpdater);
 
 
 /*
  * Builds an update from an object that looks like:
@@ -139,18 +142,17 @@ function doSimpleUpdate(updateText, succ
     },
 
     updateUrlRequested: function(url) { },
     streamFinished: function(status) { },
     updateError: function(errorCode) { failure(errorCode); },
     updateSuccess: function(requestedTimeout) { success(requestedTimeout); }
   };
 
-  dbservice.beginUpdate(listener,
-                        "test-phish-simple,test-malware-simple,test-unwanted-simple");
+  dbservice.beginUpdate(listener, allTables);
   dbservice.beginStream("", "");
   dbservice.updateStream(updateText);
   dbservice.finishStream();
   dbservice.finishUpdate();
 }
 
 /**
  * Simulates a failed database update.
@@ -182,17 +184,17 @@ function doErrorUpdate(tables, success, 
  */
 function doStreamUpdate(updateText, success, failure, downloadFailure) {
   var dataUpdate = "data:," + encodeURIComponent(updateText);
 
   if (!downloadFailure) {
     downloadFailure = failure;
   }
 
-  streamUpdater.downloadUpdates("test-phish-simple,test-malware-simple,test-unwanted-simple", "",
+  streamUpdater.downloadUpdates(allTables, "",
                                 dataUpdate, success, failure, downloadFailure);
 }
 
 var gAssertions = {
 
 tableData : function(expectedTables, cb)
 {
   dbservice.getTables(function(tables) {
--- a/tools/mercurial/hgsetup/wizard.py
+++ b/tools/mercurial/hgsetup/wizard.py
@@ -208,16 +208,30 @@ Would you like to activate bundleclone
 
 FILE_PERMISSIONS_WARNING = '''
 Your hgrc file is currently readable by others.