Merge m-c to inbound, a=merge CLOSED TREE
authorWes Kocher <wkocher@mozilla.com>
Mon, 12 Jun 2017 17:11:10 -0700
changeset 412313 3f431be7899b
parent 412312 ec779d81487d (current diff)
parent 412222 2a3a253806d1 (diff)
child 412314 1c26e57c1899
push id7566
push usermtabara@mozilla.com
push dateWed, 02 Aug 2017 08:25:16 +0000
treeherdermozilla-beta@86913f512c3c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone56.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge m-c to inbound, a=merge CLOSED TREE MozReview-Commit-ID: 5Agk5fuwDGU
servo/components/script/dom/bindings/codegen/parser/tests/test_mozmap.py
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1664,16 +1664,17 @@ pref("browser.crashReports.unsubmittedCh
 
 // Preferences for the form autofill system extension
 #ifdef NIGHTLY_BUILD
 pref("extensions.formautofill.experimental", true);
 #else
 pref("extensions.formautofill.experimental", false);
 #endif
 pref("extensions.formautofill.addresses.enabled", true);
+pref("extensions.formautofill.firstTimeUse", true);
 pref("extensions.formautofill.heuristics.enabled", true);
 pref("extensions.formautofill.loglevel", "Warn");
 
 // Whether or not to restore a session with lazy-browser tabs.
 pref("browser.sessionstore.restore_tabs_lazily", true);
 
 // Enable safebrowsing v4 tables (suffixed by "-proto") update.
 #ifdef NIGHTLY_BUILD
--- a/browser/extensions/formautofill/FormAutofillContent.jsm
+++ b/browser/extensions/formautofill/FormAutofillContent.jsm
@@ -319,19 +319,21 @@ var FormAutofillContent = {
     this.savedFieldNames =
       Services.cpmm.initialProcessData.autofillSavedFieldNames;
   },
 
   /**
    * Send the profile to parent for doorhanger and storage saving/updating.
    *
    * @param {Object} profile Submitted form's address/creditcard guid and record.
+   * @param {Object} domWin Current content window.
    */
-  _onFormSubmit(profile) {
-    Services.cpmm.sendAsyncMessage("FormAutofill:OnFormSubmit", profile);
+  _onFormSubmit(profile, domWin) {
+    let mm = this._messageManagerFromWindow(domWin);
+    mm.sendAsyncMessage("FormAutofill:OnFormSubmit", profile);
   },
 
   /**
    * Handle earlyformsubmit event and early return when:
    * 1. In private browsing mode.
    * 2. Could not map any autofill handler by form element.
    * 3. Number of filled fields is less than autofill threshold
    *
@@ -360,17 +362,17 @@ var FormAutofillContent = {
     }
 
     this._onFormSubmit({
       address: {
         guid: handler.filledProfileGUID,
         record: pendingAddress,
       },
       // creditCard: {}
-    });
+    }, domWin);
 
     return true;
   },
 
   receiveMessage({name, data}) {
     switch (name) {
       case "FormAutofill:enabledStatus": {
         if (data) {
@@ -502,12 +504,20 @@ var FormAutofillContent = {
     let selectedIndex = ProfileAutocomplete._getSelectedIndex(doc.ownerGlobal);
 
     if (selectedIndex === -1) {
       ProfileAutocomplete._clearProfilePreview();
     } else {
       ProfileAutocomplete._previewSelectedProfile(selectedIndex);
     }
   },
+
+  _messageManagerFromWindow(win) {
+    return win.QueryInterface(Ci.nsIInterfaceRequestor)
+              .getInterface(Ci.nsIWebNavigation)
+              .QueryInterface(Ci.nsIDocShell)
+              .QueryInterface(Ci.nsIInterfaceRequestor)
+              .getInterface(Ci.nsIContentFrameMessageManager);
+  },
 };
 
 
 FormAutofillContent.init();
new file mode 100644
--- /dev/null
+++ b/browser/extensions/formautofill/FormAutofillDoorhanger.jsm
@@ -0,0 +1,173 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * Implements doorhanger singleton that wraps up the PopupNotifications and handles
+ * the doorhager UI for formautofill related features.
+ */
+
+/* exported FormAutofillDoorhanger */
+
+"use strict";
+
+this.EXPORTED_SYMBOLS = ["FormAutofillDoorhanger"];
+
+const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
+
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://formautofill/FormAutofillUtils.jsm");
+
+this.log = null;
+FormAutofillUtils.defineLazyLogGetter(this, this.EXPORTED_SYMBOLS[0]);
+
+const BUNDLE_URI = "chrome://formautofill/locale/formautofill.properties";
+const GetStringFromName = Services.strings.createBundle(BUNDLE_URI).GetStringFromName;
+
+const CONTENT = {
+  firstTimeUse: {
+    notificationId: "autofill-address",
+    message: GetStringFromName("saveAddressMessage"),
+    anchor: {
+      id: "autofill-address-notification-icon",
+      URL: "chrome://formautofill/content/icon-address-save.svg",
+      tooltiptext: GetStringFromName("openAutofillMessagePanel"),
+    },
+    options: {
+      persistWhileVisible: true,
+      popupIconURL: "chrome://formautofill/content/icon-address-save.svg",
+    },
+  },
+};
+
+let FormAutofillDoorhanger = {
+  /**
+   * Generate the main action and secondary actions from content parameters and
+   * promise resolve.
+   *
+   * @private
+   * @param  {Object} mainActionParams
+   *         Parameters for main action.
+   * @param  {Array<Object>} secondaryActionParams
+   *         Array of the parameters for secondary actions.
+   * @param  {Function} resolve Should be called in action callback.
+   * @returns {Array<Object>}
+              Return the mainAction and secondary actions in an array for showing doorhanger
+   */
+  _createActions(mainActionParams, secondaryActionParams, resolve) {
+    if (!mainActionParams) {
+      return [null, null];
+    }
+
+    let {label, accessKey, callbackState} = mainActionParams;
+    let callback = resolve.bind(null, callbackState);
+    let mainAction = {label, accessKey, callback};
+
+    if (!secondaryActionParams) {
+      return [mainAction, null];
+    }
+
+    let secondaryActions = [];
+    for (let params of secondaryActionParams) {
+      let cb = resolve.bind(null, params.callbackState);
+      secondaryActions.push({
+        label: params.label,
+        accessKey: params.accessKey,
+        callback: cb,
+      });
+    }
+
+    return [mainAction, secondaryActions];
+  },
+  /**
+   * Append the link label element to the popupnotificationcontent.
+   * @param  {XULElement} browser
+   *         Target browser element for showing doorhanger.
+   * @param  {string} id
+   *         The ID of the doorhanger.
+   */
+  _appendPrivacyPanelLink(browser, id) {
+    let notificationId = id + "-notification";
+    let chromeDoc = browser.ownerDocument;
+    let notification = chromeDoc.getElementById(notificationId);
+
+    if (!notification.querySelector("popupnotificationcontent")) {
+      let notificationcontent = chromeDoc.createElement("popupnotificationcontent");
+      let privacyLinkElement = chromeDoc.createElement("label");
+      privacyLinkElement.className = "text-link";
+      privacyLinkElement.setAttribute("useoriginprincipal", true);
+      privacyLinkElement.setAttribute("href", "about:preferences#privacy");
+      privacyLinkElement.setAttribute("value", GetStringFromName("viewAutofillOptions"));
+      notificationcontent.appendChild(privacyLinkElement);
+      notification.append(notificationcontent);
+    }
+  },
+  /**
+   * Create an image element for notification anchor if it doesn't already exist.
+   * @param  {XULElement} browser
+   *         Target browser element for showing doorhanger.
+   * @param  {Object} anchor
+   *         Anchor options for setting the anchor element.
+   * @param  {string} anchor.id
+   *         ID of the anchor element.
+   * @param  {string} anchor.URL
+   *         Path of the icon asset.
+   * @param  {string} anchor.tooltiptext
+   *         Tooltip string for the anchor.
+   */
+  _setAnchor(browser, anchor) {
+    let chromeDoc = browser.ownerDocument;
+    let {id, URL, tooltiptext} = anchor;
+    let anchorEt = chromeDoc.getElementById(id);
+    if (!anchorEt) {
+      let notificationPopupBox =
+        chromeDoc.getElementById("notification-popup-box");
+      // Icon shown on URL bar
+      let anchorElement = chromeDoc.createElement("image");
+      anchorElement.id = id;
+      anchorElement.setAttribute("src", URL);
+      anchorElement.classList.add("notification-anchor-icon");
+      anchorElement.setAttribute("role", "button");
+      anchorElement.setAttribute("tooltiptext", tooltiptext);
+      anchorElement.style.setProperty("-moz-context-properties", "fill");
+      anchorElement.style.fill = "currentcolor";
+      notificationPopupBox.appendChild(anchorElement);
+    }
+  },
+  /**
+   * Show different types of doorhanger by leveraging PopupNotifications.
+   * @param  {XULElement} browser
+   *         Target browser element for showing doorhanger.
+   * @param  {string} type
+   *         The type of the doorhanger. There will have first time use/update/credit card.
+   * @returns {Promise}
+              Resolved with action type when action callback is triggered.
+   */
+  async show(browser, type) {
+    log.debug("show doorhanger with type:", type);
+    return new Promise((resolve) => {
+      let content = CONTENT[type];
+      let chromeWin = browser.ownerGlobal;
+      content.options.eventCallback = (topic) => {
+        log.debug("eventCallback:", topic);
+
+        switch (topic) {
+          // We can only append label element when notification box is shown
+          case "shown":
+            this._appendPrivacyPanelLink(browser, content.notificationId);
+            break;
+        }
+      };
+      this._setAnchor(browser, content.anchor);
+      chromeWin.PopupNotifications.show(
+        browser,
+        content.notificationId,
+        content.message,
+        content.anchor.id,
+        ...this._createActions(content.mainAction, content.secondaryActions, resolve),
+        content.options,
+      );
+    });
+  },
+};
--- a/browser/extensions/formautofill/FormAutofillParent.jsm
+++ b/browser/extensions/formautofill/FormAutofillParent.jsm
@@ -30,22 +30,23 @@
 "use strict";
 
 this.EXPORTED_SYMBOLS = ["FormAutofillParent"];
 
 const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-Cu.import("resource://gre/modules/Services.jsm");
 
 Cu.import("resource://formautofill/FormAutofillUtils.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "FormAutofillPreferences",
                                   "resource://formautofill/FormAutofillPreferences.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "FormAutofillDoorhanger",
+                                  "resource://formautofill/FormAutofillDoorhanger.jsm");
 
 this.log = null;
 FormAutofillUtils.defineLazyLogGetter(this, this.EXPORTED_SYMBOLS[0]);
 
 const ENABLED_PREF = "extensions.formautofill.addresses.enabled";
 
 function FormAutofillParent() {
   // Lazily load the storage JSM to avoid disk I/O until absolutely needed.
@@ -75,17 +76,17 @@ FormAutofillParent.prototype = {
    * Initializes ProfileStorage and registers the message handler.
    */
   async init() {
     Services.obs.addObserver(this, "advanced-pane-loaded");
     Services.ppmm.addMessageListener("FormAutofill:InitStorage", this);
     Services.ppmm.addMessageListener("FormAutofill:GetAddresses", this);
     Services.ppmm.addMessageListener("FormAutofill:SaveAddress", this);
     Services.ppmm.addMessageListener("FormAutofill:RemoveAddresses", this);
-    Services.ppmm.addMessageListener("FormAutofill:OnFormSubmit", this);
+    Services.mm.addMessageListener("FormAutofill:OnFormSubmit", this);
 
     // Observing the pref and storage changes
     Services.prefs.addObserver(ENABLED_PREF, this);
     Services.obs.addObserver(this, "formautofill-storage-changed");
   },
 
   observe(subject, topic, data) {
     log.debug("observe:", topic, "with data:", data);
@@ -271,13 +272,21 @@ FormAutofillParent.prototype = {
 
     if (address.guid) {
       if (!this.profileStorage.addresses.mergeIfPossible(address.guid, address.record)) {
         // TODO: Show update doorhanger(bug 1303513) and set probe(bug 990200)
         return;
       }
       this.profileStorage.addresses.notifyUsed(address.guid);
     } else {
-      // TODO: Add first time use probe(bug 990199) and doorhanger(bug 1303510)
-      // profileStorage.addresses.add(address.record);
+      if (!Services.prefs.getBoolPref("extensions.formautofill.firstTimeUse")) {
+        if (!this.profileStorage.addresses.mergeToStorage(address.record)) {
+          this.profileStorage.addresses.add(address.record);
+        }
+        return;
+      }
+
+      this.profileStorage.addresses.add(address.record);
+      Services.prefs.setBoolPref("extensions.formautofill.firstTimeUse", false);
+      FormAutofillDoorhanger.show(target, "firstTimeUse");
     }
   },
 };
new file mode 100644
--- /dev/null
+++ b/browser/extensions/formautofill/content/icon-address-save.svg
@@ -0,0 +1,6 @@
+<!-- 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/. -->
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
+  <path fill="#999899" d="M22 13.7H9.4c-.6 0-1.2.5-1.2 1.2 0 .6.5 1.2 1.2 1.2H22c.6 0 1.2-.5 1.2-1.2s-.6-1.2-1.2-1.2zM6.1 26.6V5.5c0-.8.7-1.5 1.5-1.5h16c.9 0 1.5.6 1.5 1.5V16h2V3.8c0-1-.7-1.8-1.8-1.8H5.9c-1 0-1.8.8-1.8 1.8v24.5c0 1 .8 1.7 1.8 1.7h9.3v-2H7.6c-.8 0-1.5-.6-1.5-1.4zm21.1-1.9h-2.5V20c0-.4-.3-.8-.8-.8h-3.1c-.4 0-.8.3-.8.8v4.6h-2.5c-.6 0-.8.4-.3.8l4.3 4.2c.2.2.5.3.8.3s.6-.1.8-.3l4.3-4.2c.6-.4.4-.7-.2-.7zm-11.3-5.6H9.4c-.6 0-1.2.5-1.2 1.2s.5 1.2 1.2 1.2h6.5c.6 0 1.2-.5 1.2-1.2s-.6-1.2-1.2-1.2zM22 7.8H9.4c-.6 0-1.2.5-1.2 1.2s.5 1.2 1.2 1.2H22c.6 0 1.2-.5 1.2-1.2s-.6-1.2-1.2-1.2z"/>
+</svg>
--- a/browser/extensions/formautofill/locale/en-US/formautofill.properties
+++ b/browser/extensions/formautofill/locale/en-US/formautofill.properties
@@ -1,7 +1,10 @@
 # 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/.
 
 preferenceGroupTitle = Form Autofill
 enableProfileAutofill = Enable Profile Autofill
 savedProfiles = Saved Profiles…
+saveAddressMessage = Firefox now saves your form data to help you fill out forms faster!
+viewAutofillOptions = View Form Autofill options…
+openAutofillMessagePanel = Open Form Autofill message panel
--- a/browser/extensions/formautofill/test/browser/browser.ini
+++ b/browser/extensions/formautofill/test/browser/browser.ini
@@ -1,11 +1,12 @@
 [DEFAULT]
 head = head.js
 
 support-files =
   ../fixtures/autocomplete_basic.html
 
 [browser_check_installed.js]
 [browser_editProfileDialog.js]
+[browser_first_time_use_doorhanger.js]
 [browser_privacyPreferences.js]
 [browser_manageProfilesDialog.js]
 [browser_submission_in_private_mode.js]
new file mode 100644
--- /dev/null
+++ b/browser/extensions/formautofill/test/browser/browser_first_time_use_doorhanger.js
@@ -0,0 +1,87 @@
+"use strict";
+
+const FORM_URL = "http://mochi.test:8888/browser/browser/extensions/formautofill/test/browser/autocomplete_basic.html";
+const FTU_PREF = "extensions.formautofill.firstTimeUse";
+const ENABLED_PREF = "extensions.formautofill.addresses.enabled";
+
+registerCleanupFunction(async function() {
+  let addresses = await getAddresses();
+  if (addresses.length) {
+    await removeAddresses(addresses.map(address => address.guid));
+  }
+});
+
+add_task(async function test_first_time_save() {
+  let addresses = await getAddresses();
+  is(addresses.length, 0, "No profile in storage");
+  await SpecialPowers.pushPrefEnv({
+    "set": [
+      [FTU_PREF, true],
+      [ENABLED_PREF, true],
+    ],
+  });
+
+  await BrowserTestUtils.withNewTab({gBrowser, url: FORM_URL},
+    async function(browser) {
+      let promiseShown = BrowserTestUtils.waitForEvent(PopupNotifications.panel,
+                                                       "popupshown");
+      let tabPromise = BrowserTestUtils.waitForNewTab(gBrowser, "about:preferences#privacy");
+      await ContentTask.spawn(browser, null, async function() {
+        let form = content.document.getElementById("form");
+        form.querySelector("#organization").focus();
+        form.querySelector("#organization").value = "Sesame Street";
+        form.querySelector("#street-address").value = "123 Sesame Street";
+        form.querySelector("#tel").value = "1-345-345-3456";
+
+        // Wait 500ms before submission to make sure the input value applied
+        setTimeout(() => {
+          form.querySelector("input[type=submit]").click();
+        }, 500);
+      });
+
+      await promiseShown;
+      let notificationElement = PopupNotifications.panel.firstChild;
+      // Open the panel via link
+      let link = notificationElement.querySelector("popupnotificationcontent label");
+      link.click();
+      let tab = await tabPromise;
+      ok(tab, "Privacy panel opened");
+      await BrowserTestUtils.removeTab(tab);
+    }
+  );
+
+  addresses = await getAddresses();
+  is(addresses.length, 1, "Profile saved");
+  let ftuPref = SpecialPowers.getBoolPref(FTU_PREF);
+  is(ftuPref, false, "First time use flag is false");
+});
+
+add_task(async function test_non_first_time_save() {
+  let addresses = await getAddresses();
+  let ftuPref = SpecialPowers.getBoolPref(FTU_PREF);
+  is(ftuPref, false, "First time use flag is false");
+  is(addresses.length, 1, "1 address in storage");
+
+  await BrowserTestUtils.withNewTab({gBrowser, url: FORM_URL},
+    async function(browser) {
+      await ContentTask.spawn(browser, null, async function() {
+        let form = content.document.getElementById("form");
+        form.querySelector("#organization").focus();
+        form.querySelector("#organization").value = "Mozilla";
+        form.querySelector("#street-address").value = "331 E. Evelyn Avenue";
+        form.querySelector("#tel").value = "1-650-903-0800";
+
+        // Wait 500ms before submission to make sure the input value applied
+        setTimeout(() => {
+          form.querySelector("input[type=submit]").click();
+        }, 500);
+      });
+
+      await new Promise(resolve => setTimeout(resolve, 1000));
+      is(PopupNotifications.panel.state, "closed", "Doorhanger is hidden");
+    }
+  );
+
+  addresses = await getAddresses();
+  is(addresses.length, 2, "Another address saved");
+});
--- a/browser/extensions/formautofill/test/mochitest/formautofill_common.js
+++ b/browser/extensions/formautofill/test/mochitest/formautofill_common.js
@@ -18,58 +18,89 @@ function setInput(selector, value) {
   //       notify us of the state of "identifyAutofillFields" for now. We should
   //       figure out a better way after the heuristics land.
   SimpleTest.requestFlakyTimeout("Guarantee asynchronous identifyAutofillFields is invoked");
   return new Promise(resolve => setTimeout(() => {
     resolve(input);
   }, 500));
 }
 
+function clickOnElement(selector) {
+  let element = document.querySelector(selector);
+
+  if (!element) {
+    throw new Error("Can not find the element");
+  }
+
+  SimpleTest.executeSoon(() => element.click());
+}
+
+async function onAddressChanged(type) {
+  return new Promise(resolve => {
+    formFillChromeScript.addMessageListener("formautofill-storage-changed", function onChanged(data) {
+      formFillChromeScript.removeMessageListener("formautofill-storage-changed", onChanged);
+      is(data.data, type, `Receive ${type} storage changed event`);
+      resolve();
+    });
+  });
+}
+
 function checkMenuEntries(expectedValues) {
   let actualValues = getMenuEntries();
 
   is(actualValues.length, expectedValues.length, " Checking length of expected menu");
   for (let i = 0; i < expectedValues.length; i++) {
     is(actualValues[i], expectedValues[i], " Checking menu entry #" + i);
   }
 }
 
-function addAddress(address) {
+async function addAddress(address) {
   return new Promise(resolve => {
     formFillChromeScript.sendAsyncMessage("FormAutofillTest:AddAddress", {address});
     formFillChromeScript.addMessageListener("FormAutofillTest:AddressAdded", function onAdded(data) {
       formFillChromeScript.removeMessageListener("FormAutofillTest:AddressAdded", onAdded);
 
       resolve();
     });
   });
 }
 
-function removeAddress(guid) {
+async function removeAddress(guid) {
   return new Promise(resolve => {
     formFillChromeScript.sendAsyncMessage("FormAutofillTest:RemoveAddress", {guid});
     formFillChromeScript.addMessageListener("FormAutofillTest:AddressRemoved", function onDeleted(data) {
       formFillChromeScript.removeMessageListener("FormAutofillTest:AddressRemoved", onDeleted);
 
       resolve();
     });
   });
 }
 
-function updateAddress(guid, address) {
+async function updateAddress(guid, address) {
   return new Promise(resolve => {
     formFillChromeScript.sendAsyncMessage("FormAutofillTest:UpdateAddress", {address, guid});
     formFillChromeScript.addMessageListener("FormAutofillTest:AddressUpdated", function onUpdated(data) {
       formFillChromeScript.removeMessageListener("FormAutofillTest:AddressUpdated", onUpdated);
 
       resolve();
     });
   });
 }
 
+async function checkAddresses(expectedAddresses) {
+  return new Promise(resolve => {
+    formFillChromeScript.sendAsyncMessage("FormAutofillTest:CheckAddresses", {expectedAddresses});
+    formFillChromeScript.addMessageListener("FormAutofillTest:areAddressesMatching", function onChecked(data) {
+      formFillChromeScript.removeMessageListener("FormAutofillTest:areAddressesMatching", onChecked);
+
+      resolve(data);
+    });
+  });
+}
+
 function formAutoFillCommonSetup() {
   let chromeURL = SimpleTest.getTestFileURL("formautofill_parent_utils.js");
   formFillChromeScript = SpecialPowers.loadChromeScript(chromeURL);
   formFillChromeScript.addMessageListener("onpopupshown", ({results}) => {
     gLastAutoCompleteResults = results;
     if (gPopupShownListener) {
       gPopupShownListener({results});
     }
--- a/browser/extensions/formautofill/test/mochitest/formautofill_parent_utils.js
+++ b/browser/extensions/formautofill/test/mochitest/formautofill_parent_utils.js
@@ -2,16 +2,17 @@
 /* global assert */
 /* eslint-env mozilla/frame-script */
 
 "use strict";
 
 const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
 
 Cu.import("resource://gre/modules/Services.jsm");
+let {profileStorage} = Cu.import("resource://formautofill/ProfileStorage.jsm", {});
 
 var ParentUtils = {
   cleanUpAddress() {
     Services.cpmm.addMessageListener("FormAutofill:Addresses", function getResult(result) {
       Services.cpmm.removeMessageListener("FormAutofill:Addresses", getResult);
 
       let addresses = result.data;
       Services.cpmm.sendAsyncMessage("FormAutofill:RemoveAddresses",
@@ -37,27 +38,66 @@ var ParentUtils = {
     assert.ok(topic === "formautofill-storage-changed");
     sendAsyncMessage("formautofill-storage-changed", {subject: null, topic, data});
   },
 
   cleanup() {
     Services.obs.removeObserver(this, "formautofill-storage-changed");
     this.cleanUpAddress();
   },
+
+  areAddressesMatching(addressA, addressB) {
+    for (let field of profileStorage.addresses.VALID_FIELDS) {
+      if (addressA[field] !== addressB[field]) {
+        return false;
+      }
+    }
+    return true;
+  },
+
+  checkAddresses({expectedAddresses}) {
+    Services.cpmm.addMessageListener("FormAutofill:Addresses", function getResult(result) {
+      Services.cpmm.removeMessageListener("FormAutofill:Addresses", getResult);
+      let addresses = result.data;
+      if (addresses.length !== expectedAddresses.length) {
+        sendAsyncMessage("FormAutofillTest:areAddressesMatching", false);
+        return;
+      }
+
+      for (let address of addresses) {
+        let matching = expectedAddresses.some((expectedAddress) => {
+          return ParentUtils.areAddressesMatching(address, expectedAddress);
+        });
+
+        if (!matching) {
+          sendAsyncMessage("FormAutofillTest:areAddressesMatching", false);
+          return;
+        }
+      }
+
+      sendAsyncMessage("FormAutofillTest:areAddressesMatching", true);
+    });
+
+    Services.cpmm.sendAsyncMessage("FormAutofill:GetAddresses", {searchString: ""});
+  },
 };
 
 Services.obs.addObserver(ParentUtils, "formautofill-storage-changed");
 
 addMessageListener("FormAutofillTest:AddAddress", (msg) => {
   ParentUtils.updateAddress("add", "FormAutofill:SaveAddress", msg, "FormAutofillTest:AddressAdded");
 });
 
 addMessageListener("FormAutofillTest:RemoveAddress", (msg) => {
   ParentUtils.updateAddress("remove", "FormAutofill:RemoveAddress", msg, "FormAutofillTest:AddressRemoved");
 });
 
 addMessageListener("FormAutofillTest:UpdateAddress", (msg) => {
   ParentUtils.updateAddress("update", "FormAutofill:SaveAddress", msg, "FormAutofillTest:AddressUpdated");
 });
 
+addMessageListener("FormAutofillTest:CheckAddresses", (msg) => {
+  ParentUtils.checkAddresses(msg);
+});
+
 addMessageListener("cleanup", () => {
   ParentUtils.cleanup();
 });
--- a/browser/extensions/formautofill/test/mochitest/mochitest.ini
+++ b/browser/extensions/formautofill/test/mochitest/mochitest.ini
@@ -3,8 +3,9 @@ support-files =
   ../../../../../toolkit/components/satchel/test/satchel_common.js
   ../../../../../toolkit/components/satchel/test/parent_utils.js
   formautofill_common.js
   formautofill_parent_utils.js
 
 [test_autofocus_form.html]
 [test_basic_autocomplete_form.html]
 [test_formautofill_preview_highlight.html]
+[test_on_address_submission.html]
new file mode 100644
--- /dev/null
+++ b/browser/extensions/formautofill/test/mochitest/test_on_address_submission.html
@@ -0,0 +1,66 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>Test autofill submit</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/SpawnTask.js"></script>
+  <script type="text/javascript" src="formautofill_common.js"></script>
+  <script type="text/javascript" src="satchel_common.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+Form autofill test: check if address is saved/updated correctly
+
+<script>
+/* import-globals-from ../../../../../testing/mochitest/tests/SimpleTest/SpawnTask.js */
+/* import-globals-from ../../../../../toolkit/components/satchel/test/satchel_common.js */
+/* import-globals-from formautofill_common.js */
+
+"use strict";
+
+let TEST_ADDRESSES = [{
+  organization: "Sesame Street",
+  "street-address": "123 Sesame Street.",
+  tel: "1-345-345-3456",
+}, {
+  organization: "Mozilla",
+  "street-address": "331 E. Evelyn Avenue",
+  tel: "1-650-903-0800",
+}];
+
+// Autofill the address from dropdown menu.
+add_task(async function check_storage_after_form_submitted() {
+  // We already verified the first time use case in browser test
+  await SpecialPowers.pushPrefEnv({
+    set: [["extensions.formautofill.firstTimeUse", false]],
+  });
+
+  for (let key in TEST_ADDRESSES[0]) {
+    await setInput("#" + key, TEST_ADDRESSES[0][key]);
+  }
+
+  clickOnElement("input[type=submit]");
+
+  let expectedAddresses = TEST_ADDRESSES.slice(0, 1);
+  await onAddressChanged("add");
+  let matching = await checkAddresses(expectedAddresses);
+  ok(matching, "Address saved as expected");
+});
+
+</script>
+
+<div>
+
+  <form onsubmit="return false">
+    <p>This is a basic form for submitting test.</p>
+    <p><label>organization: <input id="organization" name="organization" autocomplete="organization" type="text"></label></p>
+    <p><label>streetAddress: <input id="street-address" name="street-address" autocomplete="street-address" type="text"></label></p>
+    <p><label>tel: <input id="tel" name="tel" autocomplete="tel" type="text"></label></p>
+    <p><label>country: <input id="country" name="country" autocomplete="country" type="text"></label></p>
+    <p><input type="submit"></p>
+  </form>
+
+</div>
+</body>
+</html>
--- a/browser/extensions/formautofill/test/unit/test_onFormSubmitted.js
+++ b/browser/extensions/formautofill/test/unit/test_onFormSubmitted.js
@@ -103,17 +103,17 @@ TESTCASES.forEach(testcase => {
   add_task(async function check_profile_saving_is_called_correctly() {
     do_print("Starting testcase: " + testcase.description);
 
     let form = MOCK_DOC.getElementById("form1");
     for (let key in testcase.formValue) {
       let input = MOCK_DOC.getElementById(key);
       input.value = testcase.formValue[key];
     }
-    sinon.spy(FormAutofillContent, "_onFormSubmit");
+    sinon.stub(FormAutofillContent, "_onFormSubmit");
 
     FormAutofillContent.identifyAutofillFields(MOCK_DOC);
     FormAutofillContent.notify(form);
 
     do_check_eq(FormAutofillContent._onFormSubmit.called,
                 testcase.expectedResult.formSubmission);
     if (FormAutofillContent._onFormSubmit.called) {
       Assert.deepEqual(FormAutofillContent._onFormSubmit.args[0][0],
--- a/devtools/client/animationinspector/test/browser_animation_mutations_with_same_names.js
+++ b/devtools/client/animationinspector/test/browser_animation_mutations_with_same_names.js
@@ -6,28 +6,29 @@
 
 // Check that when animations are added later (through animation mutations) and
 // if these animations have the same names, then all of them are still being
 // displayed (which should be true as long as these animations apply to
 // different nodes).
 
 add_task(function* () {
   yield addTab(URL_ROOT + "doc_negative_animation.html");
-  let {controller, panel} = yield openAnimationInspector();
+  const {controller, panel} = yield openAnimationInspector();
+  const timeline = panel.animationsTimelineComponent;
 
-  info("Wait until all animations have been added " +
-       "(they're added with setTimeout)");
-  while (controller.animationPlayers.length < 3) {
-    yield controller.once(controller.PLAYERS_UPDATED_EVENT);
+  const areTracksReady = () => timeline.animations.every(a => timeline.tracksMap.has(a));
+
+  // We need to wait for all tracks to be ready, cause this is an async part of the init
+  // of the panel.
+  while (controller.animationPlayers.length < 3 || !areTracksReady()) {
     yield waitForAnimationTimelineRendering(panel);
   }
-
+  // Same for animation targets, they're retrieved asynchronously.
   yield waitForAllAnimationTargets(panel);
 
   is(panel.animationsTimelineComponent.animations.length, 3,
      "The timeline shows 3 animations too");
 
   // Reduce the known nodeFronts to a set to make them unique.
   let nodeFronts = new Set(panel.animationsTimelineComponent
                                 .targetNodes.map(n => n.previewer.nodeFront));
-  is(nodeFronts.size, 3,
-     "The animations are applied to 3 different node fronts");
+  is(nodeFronts.size, 3, "The animations are applied to 3 different node fronts");
 });
--- a/devtools/client/framework/source-map-url-service.js
+++ b/devtools/client/framework/source-map-url-service.js
@@ -6,29 +6,39 @@
 /**
  * A simple service to track source actors and keep a mapping between
  * original URLs and objects holding the source actor's ID (which is
  * used as a cookie by the devtools-source-map service) and the source
  * map URL.
  *
  * @param {object} target
  *        The object the toolbox is debugging.
+ * @param {object} threadClient
+ *        The toolbox's thread client
  * @param {SourceMapService} sourceMapService
  *        The devtools-source-map functions
  */
-function SourceMapURLService(target, sourceMapService) {
+function SourceMapURLService(target, threadClient, sourceMapService) {
   this._target = target;
   this._sourceMapService = sourceMapService;
   this._urls = new Map();
 
   this._onSourceUpdated = this._onSourceUpdated.bind(this);
   this.reset = this.reset.bind(this);
 
   target.on("source-updated", this._onSourceUpdated);
   target.on("will-navigate", this.reset);
+
+  // Start fetching the sources now.
+  this._loadingPromise = new Promise(resolve => {
+    threadClient.getSources(({sources}) => {
+      // Just ignore errors.
+      resolve(sources);
+    });
+  });
 }
 
 /**
  * Reset the service.  This flushes the internal cache.
  */
 SourceMapURLService.prototype.reset = function () {
   this._sourceMapService.clearSourceMaps();
   this._urls.clear();
@@ -70,16 +80,24 @@ SourceMapURLService.prototype._onSourceU
  * @param {number} line
  *        The line number to map.
  * @param {number} column
  *        The column number to map.
  * @return Promise
  *        A promise resolving either to the original location, or null.
  */
 SourceMapURLService.prototype.originalPositionFor = async function (url, line, column) {
+  // Ensure the sources are loaded before replying.
+  await this._loadingPromise;
+
+  // Maybe we were shut down while waiting.
+  if (!this._urls) {
+    return null;
+  }
+
   const urlInfo = this._urls.get(url);
   if (!urlInfo) {
     return null;
   }
   // Call getOriginalURLs to make sure the source map has been
   // fetched.  We don't actually need the result of this though.
   await this._sourceMapService.getOriginalURLs(urlInfo);
   const location = { sourceId: urlInfo.id, line, column, sourceUrl: url };
--- a/devtools/client/framework/test/browser.ini
+++ b/devtools/client/framework/test/browser.ini
@@ -8,23 +8,26 @@ support-files =
   browser_toolbox_sidebar_tool.xul
   browser_toolbox_window_title_changes_page.html
   browser_toolbox_window_title_frame_select_page.html
   code_binary_search.coffee
   code_binary_search.js
   code_binary_search.map
   code_binary_search_absolute.js
   code_binary_search_absolute.map
+  code_bundle_no_race.js
+  code_bundle_no_race.js.map
   code_bundle_reload_1.js
   code_bundle_reload_1.js.map
   code_bundle_reload_2.js
   code_bundle_reload_2.js.map
   code_inline_bundle.js
   code_inline_original.js
   code_math.js
+  code_no_race.js
   code_reload_1.js
   code_reload_2.js
   doc_empty-tab-01.html
   doc_reload.html
   head.js
   shared-head.js
   shared-redux-head.js
   helper_disable_cache.js
@@ -47,16 +50,17 @@ support-files =
 [browser_keybindings_01.js]
 [browser_keybindings_02.js]
 [browser_keybindings_03.js]
 [browser_menu_api.js]
 [browser_new_activation_workflow.js]
 [browser_source_map-01.js]
 [browser_source_map-absolute.js]
 [browser_source_map-inline.js]
+[browser_source_map-no-race.js]
 [browser_source_map-reload.js]
 [browser_target_from_url.js]
 [browser_target_events.js]
 [browser_target_remote.js]
 [browser_target_support.js]
 [browser_toolbox_custom_host.js]
 [browser_toolbox_dynamic_registration.js]
 [browser_toolbox_getpanelwhenready.js]
new file mode 100644
--- /dev/null
+++ b/devtools/client/framework/test/browser_source_map-no-race.js
@@ -0,0 +1,41 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Test that the source map service doesn't race against source
+// reporting.
+
+"use strict";
+
+const JS_URL = URL_ROOT + "code_bundle_no_race.js";
+
+const PAGE_URL = `data:text/html,
+<!doctype html>
+
+<html>
+  <head>
+    <meta charset="utf-8"/>
+    <title>Empty test page to test race case</title>
+  </head>
+
+  <body>
+    <script src="${JS_URL}"></script>
+  </body>
+
+</html>`;
+
+const ORIGINAL_URL = "webpack:///code_no_race.js";
+
+const GENERATED_LINE = 84;
+const ORIGINAL_LINE = 11;
+
+add_task(function* () {
+  // Start with the empty page, then navigate, so that we can properly
+  // listen for new sources arriving.
+  const toolbox = yield openNewTabAndToolbox(PAGE_URL, "webconsole");
+  const service = toolbox.sourceMapURLService;
+
+  info(`checking original location for ${JS_URL}:${GENERATED_LINE}`);
+  let newLoc = yield service.originalPositionFor(JS_URL, GENERATED_LINE);
+  is(newLoc.sourceUrl, ORIGINAL_URL, "check mapped URL");
+  is(newLoc.line, ORIGINAL_LINE, "check mapped line number");
+});
new file mode 100644
--- /dev/null
+++ b/devtools/client/framework/test/code_bundle_no_race.js
@@ -0,0 +1,95 @@
+/******/ (function(modules) { // webpackBootstrap
+/******/ 	// The module cache
+/******/ 	var installedModules = {};
+/******/
+/******/ 	// The require function
+/******/ 	function __webpack_require__(moduleId) {
+/******/
+/******/ 		// Check if module is in cache
+/******/ 		if(installedModules[moduleId]) {
+/******/ 			return installedModules[moduleId].exports;
+/******/ 		}
+/******/ 		// Create a new module (and put it into the cache)
+/******/ 		var module = installedModules[moduleId] = {
+/******/ 			i: moduleId,
+/******/ 			l: false,
+/******/ 			exports: {}
+/******/ 		};
+/******/
+/******/ 		// Execute the module function
+/******/ 		modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
+/******/
+/******/ 		// Flag the module as loaded
+/******/ 		module.l = true;
+/******/
+/******/ 		// Return the exports of the module
+/******/ 		return module.exports;
+/******/ 	}
+/******/
+/******/
+/******/ 	// expose the modules object (__webpack_modules__)
+/******/ 	__webpack_require__.m = modules;
+/******/
+/******/ 	// expose the module cache
+/******/ 	__webpack_require__.c = installedModules;
+/******/
+/******/ 	// identity function for calling harmony imports with the correct context
+/******/ 	__webpack_require__.i = function(value) { return value; };
+/******/
+/******/ 	// define getter function for harmony exports
+/******/ 	__webpack_require__.d = function(exports, name, getter) {
+/******/ 		if(!__webpack_require__.o(exports, name)) {
+/******/ 			Object.defineProperty(exports, name, {
+/******/ 				configurable: false,
+/******/ 				enumerable: true,
+/******/ 				get: getter
+/******/ 			});
+/******/ 		}
+/******/ 	};
+/******/
+/******/ 	// getDefaultExport function for compatibility with non-harmony modules
+/******/ 	__webpack_require__.n = function(module) {
+/******/ 		var getter = module && module.__esModule ?
+/******/ 			function getDefault() { return module['default']; } :
+/******/ 			function getModuleExports() { return module; };
+/******/ 		__webpack_require__.d(getter, 'a', getter);
+/******/ 		return getter;
+/******/ 	};
+/******/
+/******/ 	// Object.prototype.hasOwnProperty.call
+/******/ 	__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
+/******/
+/******/ 	// __webpack_public_path__
+/******/ 	__webpack_require__.p = "";
+/******/
+/******/ 	// Load entry module and return exports
+/******/ 	return __webpack_require__(__webpack_require__.s = 0);
+/******/ })
+/************************************************************************/
+/******/ ([
+/* 0 */
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Original source code for the inline source map test.
+// The generated file was made with
+//    webpack --devtool source-map code_no_race.js code_bundle_no_race.js
+
+
+
+function f() {
+  console.log("anything will do");
+}
+
+f();
+
+// Avoid script GC.
+window.f = f;
+
+
+/***/ })
+/******/ ]);
+//# sourceMappingURL=code_bundle_no_race.js.map
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/devtools/client/framework/test/code_bundle_no_race.js.map
@@ -0,0 +1,1 @@
+{"version":3,"sources":["webpack:///webpack/bootstrap bac8dffc0cc5eb13fa9d","webpack:///./code_no_race.js"],"names":[],"mappings":";AAAA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;;;AAGA;AACA;;AAEA;AACA;;AAEA;AACA,mDAA2C,cAAc;;AAEzD;AACA;AACA;AACA;AACA;AACA;AACA;AACA,aAAK;AACL;AACA;;AAEA;AACA;AACA;AACA,mCAA2B,0BAA0B,EAAE;AACvD,yCAAiC,eAAe;AAChD;AACA;AACA;;AAEA;AACA,8DAAsD,+DAA+D;;AAErH;AACA;;AAEA;AACA;;;;;;;;AChEA;AACA;;AAEA;AACA;AACA;;AAEA;;AAEA;AACA;AACA;;AAEA;;AAEA;AACA","file":"code_bundle_no_race.js","sourcesContent":[" \t// The module cache\n \tvar installedModules = {};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// identity function for calling harmony imports with the correct context\n \t__webpack_require__.i = function(value) { return value; };\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, {\n \t\t\t\tconfigurable: false,\n \t\t\t\tenumerable: true,\n \t\t\t\tget: getter\n \t\t\t});\n \t\t}\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"\";\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(__webpack_require__.s = 0);\n\n\n\n// WEBPACK FOOTER //\n// webpack/bootstrap bac8dffc0cc5eb13fa9d","/* Any copyright is dedicated to the Public Domain.\n http://creativecommons.org/publicdomain/zero/1.0/ */\n\n// Original source code for the inline source map test.\n// The generated file was made with\n//    webpack --devtool source-map code_no_race.js code_bundle_no_race.js\n\n\"use strict\";\n\nfunction f() {\n  console.log(\"anything will do\");\n}\n\nf();\n\n// Avoid script GC.\nwindow.f = f;\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./code_no_race.js\n// module id = 0\n// module chunks = 0"],"sourceRoot":""}
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/devtools/client/framework/test/code_no_race.js
@@ -0,0 +1,17 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Original source code for the inline source map test.
+// The generated file was made with
+//    webpack --devtool source-map code_no_race.js code_bundle_no_race.js
+
+"use strict";
+
+function f() {
+  console.log("anything will do");
+}
+
+f();
+
+// Avoid script GC.
+window.f = f;
--- a/devtools/client/framework/toolbox.js
+++ b/devtools/client/framework/toolbox.js
@@ -562,17 +562,18 @@ Toolbox.prototype = {
   get sourceMapURLService() {
     if (this._sourceMapURLService) {
       return this._sourceMapURLService;
     }
     let sourceMaps = this.sourceMapService;
     if (!sourceMaps) {
       return null;
     }
-    this._sourceMapURLService = new SourceMapURLService(this._target, sourceMaps);
+    this._sourceMapURLService = new SourceMapURLService(this._target, this.threadClient,
+                                                        sourceMaps);
     return this._sourceMapURLService;
   },
 
   // Return HostType id for telemetry
   _getTelemetryHostId: function () {
     switch (this.hostType) {
       case Toolbox.HostType.BOTTOM: return 0;
       case Toolbox.HostType.SIDE: return 1;
--- a/dom/base/nsHTMLContentSerializer.cpp
+++ b/dom/base/nsHTMLContentSerializer.cpp
@@ -20,17 +20,16 @@
 #include "nsUnicharUtils.h"
 #include "nsXPIDLString.h"
 #include "nsIServiceManager.h"
 #include "nsIDocumentEncoder.h"
 #include "nsGkAtoms.h"
 #include "nsIURI.h"
 #include "nsNetUtil.h"
 #include "nsEscape.h"
-#include "nsITextToSubURI.h"
 #include "nsCRT.h"
 #include "nsIParserService.h"
 #include "nsContentUtils.h"
 #include "nsLWBrkCIID.h"
 #include "nsIScriptElement.h"
 #include "nsAttrName.h"
 #include "nsIDocShell.h"
 #include "nsIEditor.h"
@@ -127,20 +126,16 @@ nsHTMLContentSerializer::SerializeHTMLAt
         if (uri) {
           nsAutoString absURI;
           rv = NS_MakeAbsoluteURI(absURI, valueStr, uri);
           if (NS_SUCCEEDED(rv)) {
             valueStr = absURI;
           }
         }
       }
-      // Need to escape URI.
-      nsAutoString tempURI(valueStr);
-      if (!isJS && NS_FAILED(EscapeURI(aContent, tempURI, valueStr)))
-        valueStr = tempURI;
     }
 
     if (mRewriteEncodingDeclaration && aTagName == nsGkAtoms::meta &&
         aNamespace == kNameSpaceID_XHTML && attrName == nsGkAtoms::content
         && namespaceID == kNameSpaceID_None) {
       // If we're serializing a <meta http-equiv="content-type">,
       // use the proper value, rather than what's in the document.
       nsAutoString header;
--- a/dom/base/nsXHTMLContentSerializer.cpp
+++ b/dom/base/nsXHTMLContentSerializer.cpp
@@ -20,17 +20,16 @@
 #include "nsUnicharUtils.h"
 #include "nsXPIDLString.h"
 #include "nsIServiceManager.h"
 #include "nsIDocumentEncoder.h"
 #include "nsGkAtoms.h"
 #include "nsIURI.h"
 #include "nsNetUtil.h"
 #include "nsEscape.h"
-#include "nsITextToSubURI.h"
 #include "nsCRT.h"
 #include "nsIParserService.h"
 #include "nsContentUtils.h"
 #include "nsLWBrkCIID.h"
 #include "nsIScriptElement.h"
 #include "nsStubMutationObserver.h"
 #include "nsAttrName.h"
 #include "nsComputedDOMStyle.h"
@@ -152,80 +151,16 @@ nsXHTMLContentSerializer::AppendText(nsI
     else {
       NS_ENSURE_TRUE(AppendToStringConvertLF(data, aStr), NS_ERROR_OUT_OF_MEMORY);
     }
   }
 
   return NS_OK;
 }
 
-nsresult
-nsXHTMLContentSerializer::EscapeURI(nsIContent* aContent, const nsAString& aURI, nsAString& aEscapedURI)
-{
-  // URL escape %xx cannot be used in JS.
-  // No escaping if the scheme is 'javascript'.
-  if (IsJavaScript(aContent, nsGkAtoms::href, kNameSpaceID_None, aURI)) {
-    aEscapedURI = aURI;
-    return NS_OK;
-  }
-
-  // nsITextToSubURI does charset convert plus uri escape
-  // This is needed to convert to a document charset which is needed to support existing browsers.
-  // But we eventually want to use UTF-8 instead of a document charset, then the code would be much simpler.
-  // See HTML 4.01 spec, "Appendix B.2.1 Non-ASCII characters in URI attribute values"
-  nsCOMPtr<nsITextToSubURI> textToSubURI;
-  nsAutoString uri(aURI); // in order to use FindCharInSet()
-  nsresult rv = NS_OK;
-
-  if (!mCharset.IsEmpty() && !IsASCII(uri)) {
-    textToSubURI = do_GetService(NS_ITEXTTOSUBURI_CONTRACTID, &rv);
-    NS_ENSURE_SUCCESS(rv, rv);
-  }
-
-  int32_t start = 0;
-  int32_t end;
-  nsAutoString part;
-  nsXPIDLCString escapedURI;
-  aEscapedURI.Truncate(0);
-
-  // Loop and escape parts by avoiding escaping reserved characters
-  // (and '%', '#', as well as '[' and ']' for IPv6 address literals).
-  while ((end = uri.FindCharInSet("%#;/?:@&=+$,[]", start)) != -1) {
-    part = Substring(aURI, start, (end-start));
-    if (textToSubURI && !IsASCII(part)) {
-      rv = textToSubURI->ConvertAndEscape(mCharset.get(), part.get(), getter_Copies(escapedURI));
-      NS_ENSURE_SUCCESS(rv, rv);
-    } else if (NS_WARN_IF(!NS_Escape(NS_ConvertUTF16toUTF8(part), escapedURI,
-                                     url_Path))) {
-      return NS_ERROR_OUT_OF_MEMORY;
-    }
-    AppendASCIItoUTF16(escapedURI, aEscapedURI);
-
-    // Append a reserved character without escaping.
-    part = Substring(aURI, end, 1);
-    aEscapedURI.Append(part);
-    start = end + 1;
-  }
-
-  if (start < (int32_t) aURI.Length()) {
-    // Escape the remaining part.
-    part = Substring(aURI, start, aURI.Length()-start);
-    if (textToSubURI) {
-      rv = textToSubURI->ConvertAndEscape(mCharset.get(), part.get(), getter_Copies(escapedURI));
-      NS_ENSURE_SUCCESS(rv, rv);
-    } else if (NS_WARN_IF(!NS_Escape(NS_ConvertUTF16toUTF8(part), escapedURI,
-                                     url_Path))) {
-      return NS_ERROR_OUT_OF_MEMORY;
-    }
-    AppendASCIItoUTF16(escapedURI, aEscapedURI);
-  }
-
-  return rv;
-}
-
 bool
 nsXHTMLContentSerializer::SerializeAttributes(nsIContent* aContent,
                                               nsIContent *aOriginalElement,
                                               nsAString& aTagPrefix,
                                               const nsAString& aTagNamespaceURI,
                                               nsIAtom* aTagName,
                                               nsAString& aStr,
                                               uint32_t aSkipAttr,
@@ -368,20 +303,16 @@ nsXHTMLContentSerializer::SerializeAttri
           if (uri) {
             nsAutoString absURI;
             rv = NS_MakeAbsoluteURI(absURI, valueStr, uri);
             if (NS_SUCCEEDED(rv)) {
               valueStr = absURI;
             }
           }
         }
-        // Need to escape URI.
-        nsAutoString tempURI(valueStr);
-        if (!isJS && NS_FAILED(EscapeURI(aContent, tempURI, valueStr)))
-          valueStr = tempURI;
       }
 
       if (mRewriteEncodingDeclaration && aTagName == nsGkAtoms::meta &&
           attrName == nsGkAtoms::content) {
         // If we're serializing a <meta http-equiv="content-type">,
         // use the proper value, rather than what's in the document.
         nsAutoString header;
         aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::httpEquiv, header);
--- a/dom/base/nsXHTMLContentSerializer.h
+++ b/dom/base/nsXHTMLContentSerializer.h
@@ -87,20 +87,16 @@ class nsXHTMLContentSerializer : public 
                                  nsAString& aStr);
   bool IsShorthandAttr(const nsIAtom* aAttrName,
                          const nsIAtom* aElementName);
 
   MOZ_MUST_USE
   virtual bool AppendAndTranslateEntities(const nsAString& aStr,
                                           nsAString& aOutputStr) override;
 
-  nsresult EscapeURI(nsIContent* aContent,
-                     const nsAString& aURI,
-                     nsAString& aEscapedURI);
-
 private:
   bool IsElementPreformatted(nsIContent* aNode);
 
 protected:
   nsCOMPtr<nsIEntityConverter> mEntityConverter;
 
   /*
    * isHTMLParser should be set to true by the HTML parser which inherits from
--- a/dom/canvas/test/test_bug232227.html
+++ b/dom/canvas/test/test_bug232227.html
@@ -62,30 +62,30 @@ function beginTest() {
 		[ "-moz-Field", 0xFF, 0xFF, 0xFF ],
 		[ "-moz-FieldText", 0x00, 0x00, 0x00 ],
 		[ "-moz-MenuHover", 0x33, 0x99, 0xFF ],
 		[ "-moz-MenuHoverText", 0x00, 0x00, 0x00 ],
 		[ "-moz-MenubarText", 0x00, 0x00, 0x00 ],
     [ "-moz-MenubarHoverText", 0x00, 0x00, 0x00 ],
 		[ "-moz-NativeHyperlinkText", 0x00, 0x66, 0xCC ],
 		[ "-moz-OddTreeRow", 0xFF, 0xFF, 0xFF ],
-		[ "-moz-html-CellHighlight", 0x33, 0x99, 0xFF ],
-		[ "-moz-html-CellHighlightText", 0xFF, 0xFF, 0xFF ],
 		[ "-moz-mac-chrome-active", 0xB2, 0xB2, 0xB2 ],
 		[ "-moz-mac-chrome-inactive", 0xE1, 0xE1, 0xE1 ],
 		[ "-moz-mac-focusring", 0x60, 0x9D, 0xD7 ],
 		[ "-moz-mac-menuselect", 0x38, 0x75, 0xD7 ],
 		[ "-moz-mac-menushadow", 0xA3, 0xA3, 0xA3 ],
 		[ "-moz-mac-menutextdisable", 0x88, 0x88, 0x88 ],
 		[ "-moz-mac-menutextselect", 0xFF, 0xFF, 0xFF ],
 		[ "-moz-mac-DisabledToolbarText", 0x3F, 0x3F, 0x3F ],
 		[ "-moz-mac-AlternatePrimaryHighlight", 0x3F, 0x3F, 0x3F ],
 		[ "-moz-mac-SecondaryHighlight", 0xD4, 0xD4, 0xD4 ],
 		[ "-moz-win-MediaText", 0xFF, 0xFF, 0xFF ],
 		[ "-moz-win-CommunicationsText", 0xFF, 0xFF, 0xFF ],
+		[ "-moz-html-CellHighlight", 0x33, 0x99, 0xFF ],
+		[ "-moz-html-CellHighlightText", 0xFF, 0xFF, 0xFF ],
 
 		// These five are configured via Tools -> Options -> Content -> Colors.
 		//"-moz-ActiveHyperlinkText",
 		//"-moz-HyperLinkText",
 		//"-moz-VisitedHyperlinkText",
 		//"-moz-default-background-color",
 		//"-moz-default-color",
 	];
--- a/dom/media/gmp/ChromiumCDMParent.cpp
+++ b/dom/media/gmp/ChromiumCDMParent.cpp
@@ -1,25 +1,28 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "ChromiumCDMParent.h"
-#include "mozilla/gmp/GMPTypes.h"
+
+#include "ChromiumCDMProxy.h"
+#include "content_decryption_module.h"
 #include "GMPContentChild.h"
 #include "GMPContentParent.h"
-#include "mozilla/Unused.h"
-#include "ChromiumCDMProxy.h"
+#include "GMPLog.h"
+#include "GMPUtils.h"
+#include "MediaPrefs.h"
 #include "mozilla/dom/MediaKeyMessageEventBinding.h"
+#include "mozilla/gmp/GMPTypes.h"
 #include "mozilla/Telemetry.h"
-#include "content_decryption_module.h"
-#include "GMPLog.h"
-#include "MediaPrefs.h"
-#include "GMPUtils.h"
+#include "mozilla/Unused.h"
+#include "mp4_demuxer/AnnexB.h"
+#include "mp4_demuxer/H264.h"
 
 namespace mozilla {
 namespace gmp {
 
 using namespace eme;
 
 ChromiumCDMParent::ChromiumCDMParent(GMPContentParent* aContentParent,
                                      uint32_t aPluginId)
@@ -726,26 +729,30 @@ ChromiumCDMParent::RecvDecodedData(const
   if (!v) {
     mDecodePromise.RejectIfExists(
       MediaResult(NS_ERROR_OUT_OF_MEMORY,
                   RESULT_DETAIL("Can't create VideoData")),
       __func__);
     return IPC_OK();
   }
 
-  mDecodePromise.ResolveIfExists({ Move(v) }, __func__);
+  ReorderAndReturnOutput(Move(v));
 
   return IPC_OK();
 }
 
 ipc::IPCResult
 ChromiumCDMParent::RecvDecodedShmem(const CDMVideoFrame& aFrame,
                                     ipc::Shmem&& aShmem)
 {
-  GMP_LOG("ChromiumCDMParent::RecvDecodedShmem(this=%p)", this);
+  GMP_LOG("ChromiumCDMParent::RecvDecodedShmem(this=%p) time=%" PRId64
+          " duration=%" PRId64,
+          this,
+          aFrame.mTimestamp(),
+          aFrame.mDuration());
 
   // On failure we need to deallocate the shmem we're to return to the
   // CDM. On success we return it to the CDM to be reused.
   auto autoDeallocateShmem =
     MakeScopeExit([&, this] { this->DeallocShmem(aShmem); });
 
   if (mIsShutdown || mDecodePromise.IsEmpty()) {
     return IPC_OK();
@@ -770,21 +777,36 @@ ChromiumCDMParent::RecvDecodedShmem(cons
       __func__);
     return IPC_OK();
   }
 
   // Don't need to deallocate the shmem since the CDM process is responsible
   // for it again.
   autoDeallocateShmem.release();
 
-  mDecodePromise.ResolveIfExists({ Move(v) }, __func__);
+  ReorderAndReturnOutput(Move(v));
 
   return IPC_OK();
 }
 
+void
+ChromiumCDMParent::ReorderAndReturnOutput(RefPtr<VideoData>&& aFrame)
+{
+  if (mMaxRefFrames == 0) {
+    mDecodePromise.ResolveIfExists({ Move(aFrame) }, __func__);
+    return;
+  }
+  mReorderQueue.Push(Move(aFrame));
+  MediaDataDecoder::DecodedData results;
+  while (mReorderQueue.Length() > mMaxRefFrames) {
+    results.AppendElement(mReorderQueue.Pop());
+  }
+  mDecodePromise.Resolve(Move(results), __func__);
+}
+
 already_AddRefed<VideoData>
 ChromiumCDMParent::CreateVideoFrame(const CDMVideoFrame& aFrame,
                                     Span<uint8_t> aData)
 {
   VideoData::YCbCrBuffer b;
   MOZ_ASSERT(aData.Length() > 0);
 
   b.mPlanes[0].mData = aData.Elements();
@@ -910,16 +932,23 @@ ChromiumCDMParent::InitializeVideoDecode
 
   if (!SendInitializeVideoDecoder(aConfig)) {
     return MediaDataDecoder::InitPromise::CreateAndReject(
       MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
                   RESULT_DETAIL("Failed to send init video decoder to CDM")),
       __func__);
   }
 
+  mMaxRefFrames =
+    (aConfig.mCodec() == cdm::VideoDecoderConfig::kCodecH264)
+      ? mp4_demuxer::AnnexB::HasSPS(aInfo.mExtraData)
+          ? mp4_demuxer::H264::ComputeMaxRefFrames(aInfo.mExtraData)
+          : 16
+      : 0;
+
   mVideoDecoderInitialized = true;
   mImageContainer = aImageContainer;
   mVideoInfo = aInfo;
   mVideoFrameBufferSize = bufferSize;
 
   return mInitVideoDecoderPromise.Ensure(__func__);
 }
 
@@ -982,34 +1011,38 @@ ChromiumCDMParent::DecryptAndDecodeFrame
 
   return mDecodePromise.Ensure(__func__);
 }
 
 RefPtr<MediaDataDecoder::FlushPromise>
 ChromiumCDMParent::FlushVideoDecoder()
 {
   if (mIsShutdown) {
+    MOZ_ASSERT(mReorderQueue.IsEmpty());
     return MediaDataDecoder::FlushPromise::CreateAndReject(
       MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
                   RESULT_DETAIL("ChromiumCDMParent is shutdown")),
       __func__);
   }
 
+  mReorderQueue.Clear();
+
   mDecodePromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
   if (!SendResetVideoDecoder()) {
     return MediaDataDecoder::FlushPromise::CreateAndReject(
       MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR, "Failed to send flush to CDM."),
       __func__);
   }
   return mFlushDecoderPromise.Ensure(__func__);
 }
 
 ipc::IPCResult
 ChromiumCDMParent::RecvResetVideoDecoderComplete()
 {
+  MOZ_ASSERT(mReorderQueue.IsEmpty());
   if (mIsShutdown) {
     MOZ_ASSERT(mFlushDecoderPromise.IsEmpty());
     return IPC_OK();
   }
   mFlushDecoderPromise.ResolveIfExists(true, __func__);
   return IPC_OK();
 }
 
@@ -1033,17 +1066,23 @@ ChromiumCDMParent::Drain()
 
 ipc::IPCResult
 ChromiumCDMParent::RecvDrainComplete()
 {
   if (mIsShutdown) {
     MOZ_ASSERT(mDecodePromise.IsEmpty());
     return IPC_OK();
   }
-  mDecodePromise.ResolveIfExists(MediaDataDecoder::DecodedData(), __func__);
+
+  MediaDataDecoder::DecodedData samples;
+  while (!mReorderQueue.IsEmpty()) {
+    samples.AppendElement(Move(mReorderQueue.Pop()));
+  }
+
+  mDecodePromise.ResolveIfExists(Move(samples), __func__);
   return IPC_OK();
 }
 RefPtr<ShutdownPromise>
 ChromiumCDMParent::ShutdownVideoDecoder()
 {
   if (mIsShutdown || !mVideoDecoderInitialized) {
     return ShutdownPromise::CreateAndResolve(true, __func__);
   }
@@ -1092,16 +1131,18 @@ ChromiumCDMParent::Shutdown()
     NS_DispatchToMainThread(task.forget());
   }
 
   // We may be called from a task holding the last reference to the proxy, so
   // let's clear our local weak pointer to ensure it will not be used afterward
   // (including from an already-queued task, e.g.: ActorDestroy).
   mProxy = nullptr;
 
+  mReorderQueue.Clear();
+
   for (RefPtr<DecryptJob>& decrypt : mDecrypts) {
     decrypt->PostResult(eme::AbortedErr);
   }
   mDecrypts.Clear();
 
   if (mVideoDecoderInitialized && !mActorDestroyed) {
     Unused << SendDeinitializeVideoDecoder();
     mVideoDecoderInitialized = false;
--- a/dom/media/gmp/ChromiumCDMParent.h
+++ b/dom/media/gmp/ChromiumCDMParent.h
@@ -11,16 +11,17 @@
 #include "GMPCrashHelperHolder.h"
 #include "GMPMessageUtils.h"
 #include "mozilla/gmp/PChromiumCDMParent.h"
 #include "mozilla/RefPtr.h"
 #include "nsDataHashtable.h"
 #include "PlatformDecoderModule.h"
 #include "ImageContainer.h"
 #include "mozilla/Span.h"
+#include "ReorderQueue.h"
 
 namespace mozilla {
 
 class MediaRawData;
 class ChromiumCDMProxy;
 
 namespace gmp {
 
@@ -123,16 +124,18 @@ protected:
                                  nsTArray<uint8_t>&& aData) override;
   ipc::IPCResult RecvDecodeFailed(const uint32_t& aStatus) override;
   ipc::IPCResult RecvShutdown() override;
   ipc::IPCResult RecvResetVideoDecoderComplete() override;
   ipc::IPCResult RecvDrainComplete() override;
   void ActorDestroy(ActorDestroyReason aWhy) override;
   bool SendBufferToCDM(uint32_t aSizeInBytes);
 
+  void ReorderAndReturnOutput(RefPtr<VideoData>&& aFrame);
+
   void RejectPromise(uint32_t aPromiseId,
                      nsresult aError,
                      const nsCString& aErrorMessage);
 
   void ResolvePromise(uint32_t aPromiseId);
 
   bool InitCDMInputBuffer(gmp::CDMInputBuffer& aBuffer, MediaRawData* aSample);
 
@@ -167,14 +170,23 @@ protected:
   // Maximum number of shmems to use to return decoded video frames.
   uint32_t mVideoShmemLimit;
   // High water mark for mVideoShmemsActive, reported via telemetry.
   uint32_t mMaxVideoShmemsActive = 0;
 
   bool mIsShutdown = false;
   bool mVideoDecoderInitialized = false;
   bool mActorDestroyed = false;
+
+  // The H.264 decoder in Widevine CDM versions 970 and later output in decode
+  // order rather than presentation order, so we reorder in presentation order
+  // before presenting. mMaxRefFrames is non-zero if we have an initialized
+  // decoder and we are decoding H.264. If so, it stores the maximum length of
+  // the reorder queue that we need. Note we may have multiple decoders for the
+  // life time of this object, but never more than one active at once.
+  uint32_t mMaxRefFrames = 0;
+  ReorderQueue mReorderQueue;
 };
 
 } // namespace gmp
 } // namespace mozilla
 
 #endif // ChromiumCDMParent_h_
--- a/dom/media/platforms/moz.build
+++ b/dom/media/platforms/moz.build
@@ -9,16 +9,17 @@ EXPORTS += [
     'agnostic/DummyMediaDataDecoder.h',
     'agnostic/OpusDecoder.h',
     'agnostic/TheoraDecoder.h',
     'agnostic/VorbisDecoder.h',
     'agnostic/VPXDecoder.h',
     'MediaTelemetryConstants.h',
     'PDMFactory.h',
     'PlatformDecoderModule.h',
+    'ReorderQueue.h',
     'SimpleMap.h',
     'wrappers/H264Converter.h',
     'wrappers/MediaDataDecoderProxy.h'
 
 ]
 
 UNIFIED_SOURCES += [
     'agnostic/AgnosticDecoderModule.cpp',
--- a/layout/base/nsPresContext.cpp
+++ b/layout/base/nsPresContext.cpp
@@ -95,16 +95,20 @@
 #include "mozilla/dom/URL.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::layers;
 
 uint8_t gNotifySubDocInvalidationData;
 
+// This preference was first introduced in Bug 232227, in order to prevent
+// system colors from being exposed to CSS or canvas.
+constexpr char kUseStandinsForNativeColors[] = "ui.use_standins_for_native_colors";
+
 /**
  * Layer UserData for ContainerLayers that want to be notified
  * of local invalidations of them and their descendant layers.
  * Pass a callback to ComputeDifferences to have these called.
  */
 class ContainerLayerPresContext : public LayerUserData {
 public:
   nsPresContext* mPresContext;
@@ -376,16 +380,19 @@ nsPresContext::Destroy()
                                   "layout.css.devPixelsPerPx",
                                   this);
   Preferences::UnregisterCallback(nsPresContext::PrefChangedCallback,
                                   "nglayout.debug.paint_flashing",
                                   this);
   Preferences::UnregisterCallback(nsPresContext::PrefChangedCallback,
                                   "nglayout.debug.paint_flashing_chrome",
                                   this);
+  Preferences::UnregisterCallback(nsPresContext::PrefChangedCallback,
+                                  kUseStandinsForNativeColors,
+                                  this);
 
   mRefreshDriver = nullptr;
 }
 
 nsPresContext::~nsPresContext()
 {
   NS_PRECONDITION(!mShell, "Presshell forgot to clear our mShell pointer");
   DetachShell();
@@ -463,21 +470,27 @@ nsPresContext::GetDocumentColorPreferenc
   // but in some reference tests, that is not the case.
   gfxPrefs::GetSingleton();
 
   int32_t useAccessibilityTheme = 0;
   bool usePrefColors = true;
   bool isChromeDocShell = false;
   static int32_t sDocumentColorsSetting;
   static bool sDocumentColorsSettingPrefCached = false;
+  static bool sUseStandinsForNativeColors = false;
   if (!sDocumentColorsSettingPrefCached) {
     sDocumentColorsSettingPrefCached = true;
     Preferences::AddIntVarCache(&sDocumentColorsSetting,
                                 "browser.display.document_color_use",
                                 0);
+
+    // The preference "ui.use_standins_for_native_colors" also affects
+    // default foreground and background colors.
+    Preferences::AddBoolVarCache(&sUseStandinsForNativeColors,
+                                 kUseStandinsForNativeColors);
   }
 
   nsIDocument* doc = mDocument->GetDisplayDocument();
   if (doc && doc->GetDocShell()) {
     isChromeDocShell = nsIDocShellTreeItem::typeChrome ==
                        doc->GetDocShell()->ItemType();
   } else {
     nsCOMPtr<nsIDocShellTreeItem> docShell(mContainer);
@@ -496,17 +509,24 @@ nsPresContext::GetDocumentColorPreferenc
       LookAndFeel::GetInt(LookAndFeel::eIntID_UseAccessibilityTheme, 0);
     usePrefColors = !useAccessibilityTheme;
   }
   if (usePrefColors) {
     usePrefColors =
       !Preferences::GetBool("browser.display.use_system_colors", false);
   }
 
-  if (usePrefColors) {
+  if (sUseStandinsForNativeColors) {
+    // Once the preference "ui.use_standins_for_native_colors" is enabled,
+    // use fixed color values instead of prefered colors and system colors.
+    mDefaultColor = LookAndFeel::GetColorUsingStandins(
+        LookAndFeel::eColorID_windowtext, NS_RGB(0x00, 0x00, 0x00));
+    mBackgroundColor = LookAndFeel::GetColorUsingStandins(
+        LookAndFeel::eColorID_window, NS_RGB(0xff, 0xff, 0xff));
+  } else if (usePrefColors) {
     nsAdoptingString colorStr =
       Preferences::GetString("browser.display.foreground_color");
 
     if (!colorStr.IsEmpty()) {
       mDefaultColor = MakeColorPref(colorStr);
     }
 
     colorStr = Preferences::GetString("browser.display.background_color");
@@ -919,16 +939,19 @@ nsPresContext::Init(nsDeviceContext* aDe
                                 "layout.css.devPixelsPerPx",
                                 this);
   Preferences::RegisterCallback(nsPresContext::PrefChangedCallback,
                                 "nglayout.debug.paint_flashing",
                                 this);
   Preferences::RegisterCallback(nsPresContext::PrefChangedCallback,
                                 "nglayout.debug.paint_flashing_chrome",
                                 this);
+  Preferences::RegisterCallback(nsPresContext::PrefChangedCallback,
+                                kUseStandinsForNativeColors,
+                                this);
 
   nsresult rv = mEventManager->Init();
   NS_ENSURE_SUCCESS(rv, rv);
 
   mEventManager->SetPresContext(this);
 
 #ifdef RESTYLE_LOGGING
   mRestyleLoggingEnabled = GeckoRestyleManager::RestyleLoggingInitiallyEnabled();
--- a/layout/style/ServoBindingList.h
+++ b/layout/style/ServoBindingList.h
@@ -144,19 +144,22 @@ SERVO_BINDING_FUNC(Servo_CssRules_GetCou
                    ServoCssRulesBorrowed rules, uint32_t index)
 SERVO_BINDING_FUNC(Servo_StyleRule_GetStyle, RawServoDeclarationBlockStrong,
                    RawServoStyleRuleBorrowed rule)
 SERVO_BINDING_FUNC(Servo_StyleRule_SetStyle, void,
                    RawServoStyleRuleBorrowed rule,
                    RawServoDeclarationBlockBorrowed declarations)
 SERVO_BINDING_FUNC(Servo_StyleRule_GetSelectorText, void,
                    RawServoStyleRuleBorrowed rule, nsAString* result)
-SERVO_BINDING_FUNC(Servo_StyleRule_GetSelectorTextFromIndex, void,
+SERVO_BINDING_FUNC(Servo_StyleRule_GetSelectorTextAtIndex, void,
                    RawServoStyleRuleBorrowed rule, uint32_t index,
                    nsAString* result)
+SERVO_BINDING_FUNC(Servo_StyleRule_GetSpecificityAtIndex, void,
+                   RawServoStyleRuleBorrowed rule, uint32_t index,
+                   uint64_t* specificity)
 SERVO_BINDING_FUNC(Servo_StyleRule_GetSelectorCount, void,
                    RawServoStyleRuleBorrowed rule, uint32_t* count)
 SERVO_BINDING_FUNC(Servo_ImportRule_GetHref, void,
                    RawServoImportRuleBorrowed rule, nsAString* result)
 SERVO_BINDING_FUNC(Servo_ImportRule_GetSheet, const RawServoStyleSheet*,
                    RawServoImportRuleBorrowed rule)
 SERVO_BINDING_FUNC(Servo_Keyframe_GetKeyText, void,
                    RawServoKeyframeBorrowed keyframe, nsAString* result)
--- a/layout/style/ServoStyleRule.cpp
+++ b/layout/style/ServoStyleRule.cpp
@@ -257,24 +257,24 @@ ServoStyleRule::GetSelectorCount()
   Servo_StyleRule_GetSelectorCount(mRawRule, &aCount);
 
   return aCount;
 }
 
 nsresult
 ServoStyleRule::GetSelectorText(uint32_t aSelectorIndex, nsAString& aText)
 {
-  Servo_StyleRule_GetSelectorTextFromIndex(mRawRule, aSelectorIndex, &aText);
+  Servo_StyleRule_GetSelectorTextAtIndex(mRawRule, aSelectorIndex, &aText);
   return NS_OK;
 }
 
 nsresult
 ServoStyleRule::GetSpecificity(uint32_t aSelectorIndex, uint64_t* aSpecificity)
 {
-  // TODO Bug 1370501
+  Servo_StyleRule_GetSpecificityAtIndex(mRawRule, aSelectorIndex, aSpecificity);
   return NS_OK;
 }
 
 nsresult
 ServoStyleRule::SelectorMatchesElement(Element* aElement,
                                        uint32_t aSelectorIndex,
                                        const nsAString& aPseudo,
                                        bool* aMatches)
--- a/media/libspeex_resampler/src/moz.build
+++ b/media/libspeex_resampler/src/moz.build
@@ -44,8 +44,14 @@ if CONFIG['CPU_ARCH'] == 'arm' and CONFI
     SOURCES += [
         'resample_neon.c'
     ]
     SOURCES['resample_neon.c'].flags += CONFIG['NEON_FLAGS']
 
 # Suppress warnings in third-party code.
 if CONFIG['GNU_CC']:
     CFLAGS += ['-Wno-sign-compare']
+
+if CONFIG['_MSC_VER'] and not CONFIG['CLANG_CL']:
+    CFLAGS += [
+        '-wd4018', # '<' : signed/unsigned mismatch
+        '-wd4101', # unreferenced local variable
+    ]
--- a/media/webrtc/signaling/gtest/jsep_session_unittest.cpp
+++ b/media/webrtc/signaling/gtest/jsep_session_unittest.cpp
@@ -57,67 +57,75 @@ private:
 
 uint64_t FakeUuidGenerator::ctr = 1000;
 
 class JsepSessionTest : public JsepSessionTestBase,
                         public ::testing::WithParamInterface<std::string>
 {
 public:
   JsepSessionTest()
-      : mSessionOff("Offerer", MakeUnique<FakeUuidGenerator>()),
-        mSessionAns("Answerer", MakeUnique<FakeUuidGenerator>())
+      : mSdpHelper(&mLastError)
   {
-    EXPECT_EQ(NS_OK, mSessionOff.Init());
-    EXPECT_EQ(NS_OK, mSessionAns.Init());
-
-    AddTransportData(&mSessionOff, &mOffererTransport);
-    AddTransportData(&mSessionAns, &mAnswererTransport);
+    mSessionOff = MakeUnique<JsepSessionImpl>("Offerer", MakeUnique<FakeUuidGenerator>());
+    mSessionAns = MakeUnique<JsepSessionImpl>("Answerer", MakeUnique<FakeUuidGenerator>());
+
+    EXPECT_EQ(NS_OK, mSessionOff->Init());
+    EXPECT_EQ(NS_OK, mSessionAns->Init());
+
+    mOffererTransport = MakeUnique<TransportData>();
+    mAnswererTransport = MakeUnique<TransportData>();
+
+    AddTransportData(*mSessionOff, *mOffererTransport);
+    AddTransportData(*mSessionAns, *mAnswererTransport);
+
+    mOffCandidates = MakeUnique<CandidateSet>();
+    mAnsCandidates = MakeUnique<CandidateSet>();
   }
-
 protected:
   struct TransportData {
     std::string mIceUfrag;
     std::string mIcePwd;
     std::map<std::string, std::vector<uint8_t> > mFingerprints;
   };
 
   void
-  AddDtlsFingerprint(const std::string& alg, JsepSessionImpl* session,
-                     TransportData* tdata)
+  AddDtlsFingerprint(const std::string& alg, JsepSessionImpl& session,
+                     TransportData& tdata)
   {
     std::vector<uint8_t> fp;
     fp.assign((alg == "sha-1") ? 20 : 32,
-              (session->GetName() == "Offerer") ? 0x4f : 0x41);
-    session->AddDtlsFingerprint(alg, fp);
-    tdata->mFingerprints[alg] = fp;
+              (session.GetName() == "Offerer") ? 0x4f : 0x41);
+    session.AddDtlsFingerprint(alg, fp);
+    tdata.mFingerprints[alg] = fp;
   }
 
   void
-  AddTransportData(JsepSessionImpl* session, TransportData* tdata)
+  AddTransportData(JsepSessionImpl& session, TransportData& tdata)
   {
     // Values here semi-borrowed from JSEP draft.
-    tdata->mIceUfrag = session->GetName() + "-ufrag";
-    tdata->mIcePwd = session->GetName() + "-1234567890";
-    session->SetIceCredentials(tdata->mIceUfrag, tdata->mIcePwd);
+    tdata.mIceUfrag = session.GetName() + "-ufrag";
+    tdata.mIcePwd = session.GetName() + "-1234567890";
+    session.SetIceCredentials(tdata.mIceUfrag, tdata.mIcePwd);
     AddDtlsFingerprint("sha-1", session, tdata);
     AddDtlsFingerprint("sha-256", session, tdata);
   }
 
   std::string
   CreateOffer(const Maybe<JsepOfferOptions>& options = Nothing())
   {
     JsepOfferOptions defaultOptions;
     const JsepOfferOptions& optionsRef = options ? *options : defaultOptions;
     std::string offer;
-    nsresult rv = mSessionOff.CreateOffer(optionsRef, &offer);
-    EXPECT_EQ(NS_OK, rv) << mSessionOff.GetLastError();
+    nsresult rv;
+    rv = mSessionOff->CreateOffer(optionsRef, &offer);
+    EXPECT_EQ(NS_OK, rv) << mSessionOff->GetLastError();
 
     std::cerr << "OFFER: " << offer << std::endl;
 
-    ValidateTransport(mOffererTransport, offer);
+    ValidateTransport(*mOffererTransport, offer);
 
     return offer;
   }
 
   void
   AddTracks(JsepSessionImpl& side)
   {
     // Add tracks.
@@ -279,22 +287,22 @@ protected:
       return *i;
     }
 
     return RefPtr<JsepTrack>(nullptr);
   }
 
   RefPtr<JsepTrack> GetTrackOff(size_t index,
                                 SdpMediaSection::MediaType type) {
-    return GetTrack(mSessionOff, type, index);
+    return GetTrack(*mSessionOff, type, index);
   }
 
   RefPtr<JsepTrack> GetTrackAns(size_t index,
                                 SdpMediaSection::MediaType type) {
-    return GetTrack(mSessionAns, type, index);
+    return GetTrack(*mSessionAns, type, index);
   }
 
   class ComparePairsByLevel {
     public:
       bool operator()(const JsepTrackPair& lhs,
                       const JsepTrackPair& rhs) const {
         return lhs.mLevel < rhs.mLevel;
       }
@@ -489,44 +497,44 @@ protected:
       }
     }
   }
 
   void
   EnsureNegotiationFailure(SdpMediaSection::MediaType type,
                            const std::string& codecName)
   {
-    for (auto i = mSessionOff.Codecs().begin(); i != mSessionOff.Codecs().end();
+    for (auto i = mSessionOff->Codecs().begin(); i != mSessionOff->Codecs().end();
          ++i) {
       auto* codec = *i;
       if (codec->mType == type && codec->mName != codecName) {
         codec->mEnabled = false;
       }
     }
 
-    for (auto i = mSessionAns.Codecs().begin(); i != mSessionAns.Codecs().end();
+    for (auto i = mSessionAns->Codecs().begin(); i != mSessionAns->Codecs().end();
          ++i) {
       auto* codec = *i;
       if (codec->mType == type && codec->mName == codecName) {
         codec->mEnabled = false;
       }
     }
   }
 
   std::string
   CreateAnswer()
   {
     JsepAnswerOptions options;
     std::string answer;
-    nsresult rv = mSessionAns.CreateAnswer(options, &answer);
+    nsresult rv = mSessionAns->CreateAnswer(options, &answer);
     EXPECT_EQ(NS_OK, rv);
 
     std::cerr << "ANSWER: " << answer << std::endl;
 
-    ValidateTransport(mAnswererTransport, answer);
+    ValidateTransport(*mAnswererTransport, answer);
 
     return answer;
   }
 
   static const uint32_t NO_CHECKS = 0;
   static const uint32_t CHECK_SUCCESS = 1;
   static const uint32_t CHECK_TRACKS = 1 << 2;
   static const uint32_t ALL_CHECKS = CHECK_SUCCESS | CHECK_TRACKS;
@@ -540,26 +548,26 @@ protected:
     std::string answer = CreateAnswer();
     SetLocalAnswer(answer, checkFlags);
     SetRemoteAnswer(answer, checkFlags);
   }
 
   void
   SetLocalOffer(const std::string& offer, uint32_t checkFlags = ALL_CHECKS)
   {
-    nsresult rv = mSessionOff.SetLocalDescription(kJsepSdpOffer, offer);
+    nsresult rv = mSessionOff->SetLocalDescription(kJsepSdpOffer, offer);
 
     if (checkFlags & CHECK_SUCCESS) {
       ASSERT_EQ(NS_OK, rv);
     }
 
     if (checkFlags & CHECK_TRACKS) {
       // Check that the transports exist.
-      ASSERT_EQ(types.size(), mSessionOff.GetTransports().size());
-      auto tracks = mSessionOff.GetLocalTracks();
+      ASSERT_EQ(types.size(), mSessionOff->GetTransports().size());
+      auto tracks = mSessionOff->GetLocalTracks();
       for (size_t i = 0; i < types.size(); ++i) {
         ASSERT_NE("", tracks[i]->GetStreamId());
         ASSERT_NE("", tracks[i]->GetTrackId());
         if (tracks[i]->GetMediaType() != SdpMediaSection::kApplication) {
           std::string msidAttr("a=msid:");
           msidAttr += tracks[i]->GetStreamId();
           msidAttr += " ";
           msidAttr += tracks[i]->GetTrackId();
@@ -573,24 +581,24 @@ protected:
           << "Data channel should not contain SSRC";
       }
     }
   }
 
   void
   SetRemoteOffer(const std::string& offer, uint32_t checkFlags = ALL_CHECKS)
   {
-    nsresult rv = mSessionAns.SetRemoteDescription(kJsepSdpOffer, offer);
+    nsresult rv = mSessionAns->SetRemoteDescription(kJsepSdpOffer, offer);
 
     if (checkFlags & CHECK_SUCCESS) {
       ASSERT_EQ(NS_OK, rv);
     }
 
     if (checkFlags & CHECK_TRACKS) {
-      auto tracks = mSessionAns.GetRemoteTracks();
+      auto tracks = mSessionAns->GetRemoteTracks();
       // Now verify that the right stuff is in the tracks.
       ASSERT_EQ(types.size(), tracks.size());
       for (size_t i = 0; i < tracks.size(); ++i) {
         ASSERT_EQ(types[i], tracks[i]->GetMediaType());
         ASSERT_NE("", tracks[i]->GetStreamId());
         ASSERT_NE("", tracks[i]->GetTrackId());
         if (tracks[i]->GetMediaType() != SdpMediaSection::kApplication) {
           std::string msidAttr("a=msid:");
@@ -602,24 +610,24 @@ protected:
         }
       }
     }
   }
 
   void
   SetLocalAnswer(const std::string& answer, uint32_t checkFlags = ALL_CHECKS)
   {
-    nsresult rv = mSessionAns.SetLocalDescription(kJsepSdpAnswer, answer);
+    nsresult rv = mSessionAns->SetLocalDescription(kJsepSdpAnswer, answer);
     if (checkFlags & CHECK_SUCCESS) {
       ASSERT_EQ(NS_OK, rv);
     }
 
     if (checkFlags & CHECK_TRACKS) {
       // Verify that the right stuff is in the tracks.
-      auto pairs = mSessionAns.GetNegotiatedTrackPairs();
+      auto pairs = mSessionAns->GetNegotiatedTrackPairs();
       ASSERT_EQ(types.size(), pairs.size());
       for (size_t i = 0; i < types.size(); ++i) {
         ASSERT_TRUE(pairs[i].mSending);
         ASSERT_EQ(types[i], pairs[i].mSending->GetMediaType());
         ASSERT_TRUE(pairs[i].mReceiving);
         ASSERT_EQ(types[i], pairs[i].mReceiving->GetMediaType());
         ASSERT_NE("", pairs[i].mSending->GetStreamId());
         ASSERT_NE("", pairs[i].mSending->GetTrackId());
@@ -639,30 +647,30 @@ protected:
       }
       if (types.size() == 1 &&
           pairs[0].mReceiving->GetMediaType() == SdpMediaSection::kApplication) {
         ASSERT_EQ(std::string::npos, answer.find("a=ssrc"))
           << "Data channel should not contain SSRC";
       }
     }
     std::cerr << "OFFER pairs:" << std::endl;
-    DumpTrackPairs(mSessionOff);
+    DumpTrackPairs(*mSessionOff);
   }
 
   void
   SetRemoteAnswer(const std::string& answer, uint32_t checkFlags = ALL_CHECKS)
   {
-    nsresult rv = mSessionOff.SetRemoteDescription(kJsepSdpAnswer, answer);
+    nsresult rv = mSessionOff->SetRemoteDescription(kJsepSdpAnswer, answer);
     if (checkFlags & CHECK_SUCCESS) {
       ASSERT_EQ(NS_OK, rv);
     }
 
     if (checkFlags & CHECK_TRACKS) {
       // Verify that the right stuff is in the tracks.
-      auto pairs = mSessionOff.GetNegotiatedTrackPairs();
+      auto pairs = mSessionOff->GetNegotiatedTrackPairs();
       ASSERT_EQ(types.size(), pairs.size());
       for (size_t i = 0; i < types.size(); ++i) {
         ASSERT_TRUE(pairs[i].mSending);
         ASSERT_EQ(types[i], pairs[i].mSending->GetMediaType());
         ASSERT_TRUE(pairs[i].mReceiving);
         ASSERT_EQ(types[i], pairs[i].mReceiving->GetMediaType());
         ASSERT_NE("", pairs[i].mSending->GetStreamId());
         ASSERT_NE("", pairs[i].mSending->GetTrackId());
@@ -677,17 +685,17 @@ protected:
           msidAttr += " ";
           msidAttr += pairs[i].mReceiving->GetTrackId();
           ASSERT_NE(std::string::npos, answer.find(msidAttr))
             << "Did not find " << msidAttr << " in answer";
         }
       }
     }
     std::cerr << "ANSWER pairs:" << std::endl;
-    DumpTrackPairs(mSessionAns);
+    DumpTrackPairs(*mSessionAns);
   }
 
   typedef enum {
     RTP = 1,
     RTCP = 2
   } ComponentType;
 
   class CandidateSet {
@@ -960,16 +968,30 @@ protected:
     UniquePtr<Sdp> parsed(Parse(*sdp));
     ASSERT_TRUE(parsed.get());
     ASSERT_LT(level, parsed->GetMediaSectionCount());
     SdpHelper::DisableMsection(parsed.get(), &parsed->GetMediaSection(level));
     (*sdp) = parsed->ToString();
   }
 
   void
+  CopyTransportAttributes(std::string* sdp, size_t src_level, size_t dst_level)
+  {
+    UniquePtr<Sdp> parsed(Parse(*sdp));
+    ASSERT_TRUE(parsed.get());
+    ASSERT_LT(src_level, parsed->GetMediaSectionCount());
+    ASSERT_LT(dst_level, parsed->GetMediaSectionCount());
+    nsresult rv = mSdpHelper.CopyTransportParams(2,
+                                   parsed->GetMediaSection(src_level),
+                                   &parsed->GetMediaSection(dst_level));
+    ASSERT_EQ(NS_OK, rv);
+    (*sdp) = parsed->ToString();
+  }
+
+  void
   ReplaceInSdp(std::string* sdp,
                const char* searchStr,
                const char* replaceStr) const
   {
     if (searchStr[0] == '\0') return;
     size_t pos;
     while ((pos = sdp->find(searchStr)) != std::string::npos) {
       sdp->replace(pos, strlen(searchStr), replaceStr);
@@ -1009,16 +1031,30 @@ protected:
       const SdpRtpmapAttributeList::Rtpmap* rtpmap(msection->FindRtpmap("19"));
       ASSERT_TRUE(rtpmap);
       ASSERT_EQ("19", rtpmap->pt);
       ASSERT_EQ("reserved", rtpmap->name);
     }
   }
 
   void
+  ValidateSetupAttribute(const JsepSessionImpl& side,
+                         const SdpSetupAttribute::Role expectedRole)
+  {
+    auto sdp = GetParsedLocalDescription(side);
+    for (size_t i = 0; sdp && i < sdp->GetMediaSectionCount(); ++i) {
+      if (sdp->GetMediaSection(i).GetAttributeList().HasAttribute(
+            SdpAttribute::kSetupAttribute)) {
+        auto role = sdp->GetMediaSection(i).GetAttributeList().GetSetup().mRole;
+        ASSERT_EQ(expectedRole, role);
+      }
+    }
+  }
+
+  void
   DumpTrack(const JsepTrack& track)
   {
     const JsepTrackNegotiatedDetails* details = track.GetNegotiatedDetails();
     std::cerr << "  type=" << track.GetMediaType() << std::endl;
     std::cerr << "  encodings=" << std::endl;
     for (size_t i = 0; i < details->GetEncodingCount(); ++i) {
       const JsepTrackEncoding& encoding = details->GetEncoding(i);
       std::cerr << "    id=" << encoding.mRid << std::endl;
@@ -1033,17 +1069,17 @@ protected:
         std::cerr << std::endl;
       }
     }
   }
 
   void
   DumpTrackPairs(const JsepSessionImpl& session)
   {
-    auto pairs = mSessionAns.GetNegotiatedTrackPairs();
+    auto pairs = mSessionAns->GetNegotiatedTrackPairs();
     for (auto i = pairs.begin(); i != pairs.end(); ++i) {
       std::cerr << "Track pair " << i->mLevel << std::endl;
       if (i->mSending) {
         std::cerr << "Sending-->" << std::endl;
         DumpTrack(*i->mSending);
       }
       if (i->mReceiving) {
         std::cerr << "Receiving-->" << std::endl;
@@ -1057,20 +1093,29 @@ protected:
   {
     SipccSdpParser parser;
     UniquePtr<Sdp> parsed = parser.Parse(sdp);
     EXPECT_TRUE(parsed.get()) << "Should have valid SDP" << std::endl
                               << "Errors were: " << GetParseErrors(parser);
     return parsed;
   }
 
-  JsepSessionImpl mSessionOff;
-  CandidateSet mOffCandidates;
-  JsepSessionImpl mSessionAns;
-  CandidateSet mAnsCandidates;
+  void
+  SwapOfferAnswerRoles()
+  {
+    mSessionOff.swap(mSessionAns);
+    mOffCandidates.swap(mAnsCandidates);
+    mOffererTransport.swap(mAnswererTransport);
+  }
+
+  UniquePtr<JsepSessionImpl> mSessionOff;
+  UniquePtr<CandidateSet> mOffCandidates;
+  UniquePtr<JsepSessionImpl> mSessionAns;
+  UniquePtr<CandidateSet> mAnsCandidates;
+
   std::vector<SdpMediaSection::MediaType> types;
   std::vector<std::pair<std::string, uint16_t>> mGatheredCandidates;
 
 private:
   void
   ValidateTransport(TransportData& source, const std::string& sdp_str)
   {
     UniquePtr<Sdp> sdp(Parse(sdp_str));
@@ -1088,400 +1133,502 @@ private:
       const SdpAttributeList& attrs = msection.GetAttributeList();
       bool bundle_only = attrs.HasAttribute(SdpAttribute::kBundleOnlyAttribute);
 
       // port 0 only means disabled when the bundle-only attribute is missing
       if (!bundle_only && msection.GetPort() == 0) {
         ValidateDisabledMSection(&msection);
         continue;
       }
-
-      ASSERT_EQ(source.mIceUfrag, attrs.GetIceUfrag());
-      ASSERT_EQ(source.mIcePwd, attrs.GetIcePwd());
-      const SdpFingerprintAttributeList& fps = attrs.GetFingerprint();
-      for (auto fp = fps.mFingerprints.begin(); fp != fps.mFingerprints.end();
-           ++fp) {
-        std::string alg_str = "None";
-
-        if (fp->hashFunc == SdpFingerprintAttributeList::kSha1) {
-          alg_str = "sha-1";
-        } else if (fp->hashFunc == SdpFingerprintAttributeList::kSha256) {
-          alg_str = "sha-256";
+      if (!mSdpHelper.IsBundleSlave(*sdp, i)) {
+        const SdpAttributeList& attrs = msection.GetAttributeList();
+
+        ASSERT_EQ(source.mIceUfrag, attrs.GetIceUfrag());
+        ASSERT_EQ(source.mIcePwd, attrs.GetIcePwd());
+        const SdpFingerprintAttributeList& fps = attrs.GetFingerprint();
+        for (auto fp = fps.mFingerprints.begin(); fp != fps.mFingerprints.end();
+             ++fp) {
+          std::string alg_str = "None";
+
+          if (fp->hashFunc == SdpFingerprintAttributeList::kSha1) {
+            alg_str = "sha-1";
+          } else if (fp->hashFunc == SdpFingerprintAttributeList::kSha256) {
+            alg_str = "sha-256";
+          }
+          ASSERT_EQ(source.mFingerprints[alg_str], fp->fingerprint);
         }
 
-        ASSERT_EQ(source.mFingerprints[alg_str], fp->fingerprint);
+        ASSERT_EQ(source.mFingerprints.size(), fps.mFingerprints.size());
       }
-      ASSERT_EQ(source.mFingerprints.size(), fps.mFingerprints.size());
     }
   }
 
-  TransportData mOffererTransport;
-  TransportData mAnswererTransport;
+  std::string mLastError;
+  SdpHelper mSdpHelper;
+
+  UniquePtr<TransportData> mOffererTransport;
+  UniquePtr<TransportData> mAnswererTransport;
 };
 
 TEST_F(JsepSessionTestBase, CreateDestroy) {}
 
 TEST_P(JsepSessionTest, CreateOffer)
 {
-  AddTracks(mSessionOff);
+  AddTracks(*mSessionOff);
   CreateOffer();
 }
 
 TEST_P(JsepSessionTest, CreateOfferSetLocal)
 {
-  AddTracks(mSessionOff);
+  AddTracks(*mSessionOff);
   std::string offer = CreateOffer();
   SetLocalOffer(offer);
 }
 
 TEST_P(JsepSessionTest, CreateOfferSetLocalSetRemote)
 {
-  AddTracks(mSessionOff);
+  AddTracks(*mSessionOff);
   std::string offer = CreateOffer();
   SetLocalOffer(offer);
   SetRemoteOffer(offer);
 }
 
 TEST_P(JsepSessionTest, CreateOfferSetLocalSetRemoteCreateAnswer)
 {
-  AddTracks(mSessionOff);
+  AddTracks(*mSessionOff);
   std::string offer = CreateOffer();
   SetLocalOffer(offer);
   SetRemoteOffer(offer);
-  AddTracks(mSessionAns);
+  AddTracks(*mSessionAns);
   std::string answer = CreateAnswer();
 }
 
 TEST_P(JsepSessionTest, CreateOfferSetLocalSetRemoteCreateAnswerSetLocal)
 {
-  AddTracks(mSessionOff);
+  AddTracks(*mSessionOff);
   std::string offer = CreateOffer();
   SetLocalOffer(offer);
   SetRemoteOffer(offer);
-  AddTracks(mSessionAns);
+  AddTracks(*mSessionAns);
   std::string answer = CreateAnswer();
   SetLocalAnswer(answer);
 }
 
 TEST_P(JsepSessionTest, FullCall)
 {
-  AddTracks(mSessionOff);
+  AddTracks(*mSessionOff);
   std::string offer = CreateOffer();
   SetLocalOffer(offer);
   SetRemoteOffer(offer);
-  AddTracks(mSessionAns);
+  AddTracks(*mSessionAns);
   std::string answer = CreateAnswer();
   SetLocalAnswer(answer);
   SetRemoteAnswer(answer);
 }
 
 TEST_P(JsepSessionTest, RenegotiationNoChange)
 {
-  AddTracks(mSessionOff);
+  AddTracks(*mSessionOff);
   std::string offer = CreateOffer();
   SetLocalOffer(offer);
   SetRemoteOffer(offer);
 
-  auto added = mSessionAns.GetRemoteTracksAdded();
-  auto removed = mSessionAns.GetRemoteTracksRemoved();
+  auto added = mSessionAns->GetRemoteTracksAdded();
+  auto removed = mSessionAns->GetRemoteTracksRemoved();
   ASSERT_EQ(types.size(), added.size());
   ASSERT_EQ(0U, removed.size());
 
-  AddTracks(mSessionAns);
+  AddTracks(*mSessionAns);
   std::string answer = CreateAnswer();
   SetLocalAnswer(answer);
   SetRemoteAnswer(answer);
 
-  added = mSessionOff.GetRemoteTracksAdded();
-  removed = mSessionOff.GetRemoteTracksRemoved();
+  added = mSessionOff->GetRemoteTracksAdded();
+  removed = mSessionOff->GetRemoteTracksRemoved();
   ASSERT_EQ(types.size(), added.size());
   ASSERT_EQ(0U, removed.size());
 
-  auto offererPairs = GetTrackPairsByLevel(mSessionOff);
-  auto answererPairs = GetTrackPairsByLevel(mSessionAns);
+  ValidateSetupAttribute(*mSessionOff, SdpSetupAttribute::kActpass);
+  ValidateSetupAttribute(*mSessionAns, SdpSetupAttribute::kActive);
+
+  auto offererPairs = GetTrackPairsByLevel(*mSessionOff);
+  auto answererPairs = GetTrackPairsByLevel(*mSessionAns);
 
   std::string reoffer = CreateOffer();
   SetLocalOffer(reoffer);
   SetRemoteOffer(reoffer);
 
-  added = mSessionAns.GetRemoteTracksAdded();
-  removed = mSessionAns.GetRemoteTracksRemoved();
+  added = mSessionAns->GetRemoteTracksAdded();
+  removed = mSessionAns->GetRemoteTracksRemoved();
   ASSERT_EQ(0U, added.size());
   ASSERT_EQ(0U, removed.size());
 
   std::string reanswer = CreateAnswer();
   SetLocalAnswer(reanswer);
   SetRemoteAnswer(reanswer);
 
-  added = mSessionOff.GetRemoteTracksAdded();
-  removed = mSessionOff.GetRemoteTracksRemoved();
+  added = mSessionOff->GetRemoteTracksAdded();
+  removed = mSessionOff->GetRemoteTracksRemoved();
   ASSERT_EQ(0U, added.size());
   ASSERT_EQ(0U, removed.size());
 
-  auto newOffererPairs = GetTrackPairsByLevel(mSessionOff);
-  auto newAnswererPairs = GetTrackPairsByLevel(mSessionAns);
+  ValidateSetupAttribute(*mSessionOff, SdpSetupAttribute::kActpass);
+  ValidateSetupAttribute(*mSessionAns, SdpSetupAttribute::kActive);
+
+  auto newOffererPairs = GetTrackPairsByLevel(*mSessionOff);
+  auto newAnswererPairs = GetTrackPairsByLevel(*mSessionAns);
 
   ASSERT_EQ(offererPairs.size(), newOffererPairs.size());
   for (size_t i = 0; i < offererPairs.size(); ++i) {
     ASSERT_TRUE(Equals(offererPairs[i], newOffererPairs[i]));
   }
 
   ASSERT_EQ(answererPairs.size(), newAnswererPairs.size());
   for (size_t i = 0; i < answererPairs.size(); ++i) {
     ASSERT_TRUE(Equals(answererPairs[i], newAnswererPairs[i]));
   }
 }
 
+// Disabled: See Bug 1329028
+TEST_P(JsepSessionTest, DISABLED_RenegotiationSwappedRolesNoChange)
+{
+  AddTracks(*mSessionOff);
+  std::string offer = CreateOffer();
+  SetLocalOffer(offer);
+  SetRemoteOffer(offer);
+
+  auto added = mSessionAns->GetRemoteTracksAdded();
+  auto removed = mSessionAns->GetRemoteTracksRemoved();
+  ASSERT_EQ(types.size(), added.size());
+  ASSERT_EQ(0U, removed.size());
+
+  AddTracks(*mSessionAns);
+  std::string answer = CreateAnswer();
+  SetLocalAnswer(answer);
+  SetRemoteAnswer(answer);
+
+  added = mSessionOff->GetRemoteTracksAdded();
+  removed = mSessionOff->GetRemoteTracksRemoved();
+  ASSERT_EQ(types.size(), added.size());
+  ASSERT_EQ(0U, removed.size());
+
+  ValidateSetupAttribute(*mSessionOff, SdpSetupAttribute::kActpass);
+  ValidateSetupAttribute(*mSessionAns, SdpSetupAttribute::kActive);
+
+  auto offererPairs = GetTrackPairsByLevel(*mSessionOff);
+  auto answererPairs = GetTrackPairsByLevel(*mSessionAns);
+
+  SwapOfferAnswerRoles();
+
+  std::string reoffer = CreateOffer();
+  SetLocalOffer(reoffer);
+  SetRemoteOffer(reoffer);
+
+  added = mSessionAns->GetRemoteTracksAdded();
+  removed = mSessionAns->GetRemoteTracksRemoved();
+  ASSERT_EQ(0U, added.size());
+  ASSERT_EQ(0U, removed.size());
+
+  std::string reanswer = CreateAnswer();
+  SetLocalAnswer(reanswer);
+  SetRemoteAnswer(reanswer);
+
+  added = mSessionOff->GetRemoteTracksAdded();
+  removed = mSessionOff->GetRemoteTracksRemoved();
+  ASSERT_EQ(0U, added.size());
+  ASSERT_EQ(0U, removed.size());
+
+  ValidateSetupAttribute(*mSessionOff, SdpSetupAttribute::kActpass);
+  ValidateSetupAttribute(*mSessionAns, SdpSetupAttribute::kPassive);
+
+  auto newOffererPairs = GetTrackPairsByLevel(*mSessionOff);
+  auto newAnswererPairs = GetTrackPairsByLevel(*mSessionAns);
+
+  ASSERT_EQ(offererPairs.size(), newAnswererPairs.size());
+  for (size_t i = 0; i < offererPairs.size(); ++i) {
+    ASSERT_TRUE(Equals(offererPairs[i], newAnswererPairs[i]));
+  }
+
+  ASSERT_EQ(answererPairs.size(), newOffererPairs.size());
+  for (size_t i = 0; i < answererPairs.size(); ++i) {
+    ASSERT_TRUE(Equals(answererPairs[i], newOffererPairs[i]));
+  }
+}
+
+
 TEST_P(JsepSessionTest, RenegotiationOffererAddsTrack)
 {
-  AddTracks(mSessionOff);
-  AddTracks(mSessionAns);
+  AddTracks(*mSessionOff);
+  AddTracks(*mSessionAns);
 
   OfferAnswer();
 
-  auto offererPairs = GetTrackPairsByLevel(mSessionOff);
-  auto answererPairs = GetTrackPairsByLevel(mSessionAns);
+  ValidateSetupAttribute(*mSessionOff, SdpSetupAttribute::kActpass);
+  ValidateSetupAttribute(*mSessionAns, SdpSetupAttribute::kActive);
+
+  auto offererPairs = GetTrackPairsByLevel(*mSessionOff);
+  auto answererPairs = GetTrackPairsByLevel(*mSessionAns);
 
   std::vector<SdpMediaSection::MediaType> extraTypes;
   extraTypes.push_back(SdpMediaSection::kAudio);
   extraTypes.push_back(SdpMediaSection::kVideo);
-  AddTracks(mSessionOff, extraTypes);
+  AddTracks(*mSessionOff, extraTypes);
   types.insert(types.end(), extraTypes.begin(), extraTypes.end());
 
   OfferAnswer(CHECK_SUCCESS);
 
-  auto added = mSessionAns.GetRemoteTracksAdded();
-  auto removed = mSessionAns.GetRemoteTracksRemoved();
+  ValidateSetupAttribute(*mSessionOff, SdpSetupAttribute::kActpass);
+  ValidateSetupAttribute(*mSessionAns, SdpSetupAttribute::kActive);
+
+  auto added = mSessionAns->GetRemoteTracksAdded();
+  auto removed = mSessionAns->GetRemoteTracksRemoved();
   ASSERT_EQ(2U, added.size());
   ASSERT_EQ(0U, removed.size());
   ASSERT_EQ(SdpMediaSection::kAudio, added[0]->GetMediaType());
   ASSERT_EQ(SdpMediaSection::kVideo, added[1]->GetMediaType());
 
-  added = mSessionOff.GetRemoteTracksAdded();
-  removed = mSessionOff.GetRemoteTracksRemoved();
+  added = mSessionOff->GetRemoteTracksAdded();
+  removed = mSessionOff->GetRemoteTracksRemoved();
   ASSERT_EQ(0U, added.size());
   ASSERT_EQ(0U, removed.size());
 
-  auto newOffererPairs = GetTrackPairsByLevel(mSessionOff);
-  auto newAnswererPairs = GetTrackPairsByLevel(mSessionAns);
+  auto newOffererPairs = GetTrackPairsByLevel(*mSessionOff);
+  auto newAnswererPairs = GetTrackPairsByLevel(*mSessionAns);
 
   ASSERT_EQ(offererPairs.size() + 2, newOffererPairs.size());
   for (size_t i = 0; i < offererPairs.size(); ++i) {
     ASSERT_TRUE(Equals(offererPairs[i], newOffererPairs[i]));
   }
 
   ASSERT_EQ(answererPairs.size() + 2, newAnswererPairs.size());
   for (size_t i = 0; i < answererPairs.size(); ++i) {
     ASSERT_TRUE(Equals(answererPairs[i], newAnswererPairs[i]));
   }
 }
 
 TEST_P(JsepSessionTest, RenegotiationAnswererAddsTrack)
 {
-  AddTracks(mSessionOff);
-  AddTracks(mSessionAns);
+  AddTracks(*mSessionOff);
+  AddTracks(*mSessionAns);
 
   OfferAnswer();
 
-  auto offererPairs = GetTrackPairsByLevel(mSessionOff);
-  auto answererPairs = GetTrackPairsByLevel(mSessionAns);
+  ValidateSetupAttribute(*mSessionOff, SdpSetupAttribute::kActpass);
+  ValidateSetupAttribute(*mSessionAns, SdpSetupAttribute::kActive);
+
+  auto offererPairs = GetTrackPairsByLevel(*mSessionOff);
+  auto answererPairs = GetTrackPairsByLevel(*mSessionAns);
 
   std::vector<SdpMediaSection::MediaType> extraTypes;
   extraTypes.push_back(SdpMediaSection::kAudio);
   extraTypes.push_back(SdpMediaSection::kVideo);
-  AddTracks(mSessionAns, extraTypes);
+  AddTracks(*mSessionAns, extraTypes);
   types.insert(types.end(), extraTypes.begin(), extraTypes.end());
 
   // We need to add a recvonly m-section to the offer for this to work
   JsepOfferOptions options;
   options.mOfferToReceiveAudio =
-    Some(GetTrackCount(mSessionOff, SdpMediaSection::kAudio) + 1);
+    Some(GetTrackCount(*mSessionOff, SdpMediaSection::kAudio) + 1);
   options.mOfferToReceiveVideo =
-    Some(GetTrackCount(mSessionOff, SdpMediaSection::kVideo) + 1);
+    Some(GetTrackCount(*mSessionOff, SdpMediaSection::kVideo) + 1);
 
   std::string offer = CreateOffer(Some(options));
   SetLocalOffer(offer, CHECK_SUCCESS);
   SetRemoteOffer(offer, CHECK_SUCCESS);
 
   std::string answer = CreateAnswer();
   SetLocalAnswer(answer, CHECK_SUCCESS);
   SetRemoteAnswer(answer, CHECK_SUCCESS);
 
-  auto added = mSessionAns.GetRemoteTracksAdded();
-  auto removed = mSessionAns.GetRemoteTracksRemoved();
+  ValidateSetupAttribute(*mSessionOff, SdpSetupAttribute::kActpass);
+  ValidateSetupAttribute(*mSessionAns, SdpSetupAttribute::kActive);
+
+  auto added = mSessionAns->GetRemoteTracksAdded();
+  auto removed = mSessionAns->GetRemoteTracksRemoved();
   ASSERT_EQ(0U, added.size());
   ASSERT_EQ(0U, removed.size());
 
-  added = mSessionOff.GetRemoteTracksAdded();
-  removed = mSessionOff.GetRemoteTracksRemoved();
+  added = mSessionOff->GetRemoteTracksAdded();
+  removed = mSessionOff->GetRemoteTracksRemoved();
   ASSERT_EQ(2U, added.size());
   ASSERT_EQ(0U, removed.size());
   ASSERT_EQ(SdpMediaSection::kAudio, added[0]->GetMediaType());
   ASSERT_EQ(SdpMediaSection::kVideo, added[1]->GetMediaType());
 
-  auto newOffererPairs = GetTrackPairsByLevel(mSessionOff);
-  auto newAnswererPairs = GetTrackPairsByLevel(mSessionAns);
+  auto newOffererPairs = GetTrackPairsByLevel(*mSessionOff);
+  auto newAnswererPairs = GetTrackPairsByLevel(*mSessionAns);
 
   ASSERT_EQ(offererPairs.size() + 2, newOffererPairs.size());
   for (size_t i = 0; i < offererPairs.size(); ++i) {
     ASSERT_TRUE(Equals(offererPairs[i], newOffererPairs[i]));
   }
 
   ASSERT_EQ(answererPairs.size() + 2, newAnswererPairs.size());
   for (size_t i = 0; i < answererPairs.size(); ++i) {
     ASSERT_TRUE(Equals(answererPairs[i], newAnswererPairs[i]));
   }
 }
 
 TEST_P(JsepSessionTest, RenegotiationBothAddTrack)
 {
-  AddTracks(mSessionOff);
-  AddTracks(mSessionAns);
+  AddTracks(*mSessionOff);
+  AddTracks(*mSessionAns);
 
   OfferAnswer();
 
-  auto offererPairs = GetTrackPairsByLevel(mSessionOff);
-  auto answererPairs = GetTrackPairsByLevel(mSessionAns);
+  ValidateSetupAttribute(*mSessionOff, SdpSetupAttribute::kActpass);
+  ValidateSetupAttribute(*mSessionAns, SdpSetupAttribute::kActive);
+
+  auto offererPairs = GetTrackPairsByLevel(*mSessionOff);
+  auto answererPairs = GetTrackPairsByLevel(*mSessionAns);
 
   std::vector<SdpMediaSection::MediaType> extraTypes;
   extraTypes.push_back(SdpMediaSection::kAudio);
   extraTypes.push_back(SdpMediaSection::kVideo);
-  AddTracks(mSessionAns, extraTypes);
-  AddTracks(mSessionOff, extraTypes);
+  AddTracks(*mSessionAns, extraTypes);
+  AddTracks(*mSessionOff, extraTypes);
   types.insert(types.end(), extraTypes.begin(), extraTypes.end());
 
   OfferAnswer(CHECK_SUCCESS);
 
-  auto added = mSessionAns.GetRemoteTracksAdded();
-  auto removed = mSessionAns.GetRemoteTracksRemoved();
+  ValidateSetupAttribute(*mSessionOff, SdpSetupAttribute::kActpass);
+  ValidateSetupAttribute(*mSessionAns, SdpSetupAttribute::kActive);
+
+  auto added = mSessionAns->GetRemoteTracksAdded();
+  auto removed = mSessionAns->GetRemoteTracksRemoved();
   ASSERT_EQ(2U, added.size());
   ASSERT_EQ(0U, removed.size());
   ASSERT_EQ(SdpMediaSection::kAudio, added[0]->GetMediaType());
   ASSERT_EQ(SdpMediaSection::kVideo, added[1]->GetMediaType());
 
-  added = mSessionOff.GetRemoteTracksAdded();
-  removed = mSessionOff.GetRemoteTracksRemoved();
+  added = mSessionOff->GetRemoteTracksAdded();
+  removed = mSessionOff->GetRemoteTracksRemoved();
   ASSERT_EQ(2U, added.size());
   ASSERT_EQ(0U, removed.size());
   ASSERT_EQ(SdpMediaSection::kAudio, added[0]->GetMediaType());
   ASSERT_EQ(SdpMediaSection::kVideo, added[1]->GetMediaType());
 
-  auto newOffererPairs = GetTrackPairsByLevel(mSessionOff);
-  auto newAnswererPairs = GetTrackPairsByLevel(mSessionAns);
+  auto newOffererPairs = GetTrackPairsByLevel(*mSessionOff);
+  auto newAnswererPairs = GetTrackPairsByLevel(*mSessionAns);
 
   ASSERT_EQ(offererPairs.size() + 2, newOffererPairs.size());
   for (size_t i = 0; i < offererPairs.size(); ++i) {
     ASSERT_TRUE(Equals(offererPairs[i], newOffererPairs[i]));
   }
 
   ASSERT_EQ(answererPairs.size() + 2, newAnswererPairs.size());
   for (size_t i = 0; i < answererPairs.size(); ++i) {
     ASSERT_TRUE(Equals(answererPairs[i], newAnswererPairs[i]));
   }
 }
 
 TEST_P(JsepSessionTest, RenegotiationBothAddTracksToExistingStream)
 {
-  AddTracks(mSessionOff);
-  AddTracks(mSessionAns);
+  AddTracks(*mSessionOff);
+  AddTracks(*mSessionAns);
   if (GetParam() == "datachannel") {
     return;
   }
 
   OfferAnswer();
 
-  auto oHasStream = HasMediaStream(mSessionOff.GetLocalTracks());
-  auto aHasStream = HasMediaStream(mSessionAns.GetLocalTracks());
-  ASSERT_EQ(oHasStream, GetLocalUniqueStreamIds(mSessionOff).size() > 0);
-  ASSERT_EQ(aHasStream, GetLocalUniqueStreamIds(mSessionAns).size() > 0);
-  ASSERT_EQ(aHasStream, GetRemoteUniqueStreamIds(mSessionOff).size()> 0);
-  ASSERT_EQ(oHasStream, GetRemoteUniqueStreamIds(mSessionAns).size() > 0);
-
-  auto firstOffId = GetFirstLocalStreamId(mSessionOff);
-  auto firstAnsId = GetFirstLocalStreamId(mSessionAns);
-
-  auto offererPairs = GetTrackPairsByLevel(mSessionOff);
-  auto answererPairs = GetTrackPairsByLevel(mSessionAns);
+  auto oHasStream = HasMediaStream(mSessionOff->GetLocalTracks());
+  auto aHasStream = HasMediaStream(mSessionAns->GetLocalTracks());
+  ASSERT_EQ(oHasStream, GetLocalUniqueStreamIds(*mSessionOff).size() > 0);
+  ASSERT_EQ(aHasStream, GetLocalUniqueStreamIds(*mSessionAns).size() > 0);
+  ASSERT_EQ(aHasStream, GetRemoteUniqueStreamIds(*mSessionOff).size()> 0);
+  ASSERT_EQ(oHasStream, GetRemoteUniqueStreamIds(*mSessionAns).size() > 0);
+
+  auto firstOffId = GetFirstLocalStreamId(*mSessionOff);
+  auto firstAnsId = GetFirstLocalStreamId(*mSessionAns);
+
+  auto offererPairs = GetTrackPairsByLevel(*mSessionOff);
+  auto answererPairs = GetTrackPairsByLevel(*mSessionAns);
 
   std::vector<SdpMediaSection::MediaType> extraTypes;
   extraTypes.push_back(SdpMediaSection::kAudio);
   extraTypes.push_back(SdpMediaSection::kVideo);
-  AddTracksToStream(mSessionOff, firstOffId, extraTypes);
-  AddTracksToStream(mSessionAns, firstAnsId, extraTypes);
+  AddTracksToStream(*mSessionOff, firstOffId, extraTypes);
+  AddTracksToStream(*mSessionAns, firstAnsId, extraTypes);
   types.insert(types.end(), extraTypes.begin(), extraTypes.end());
 
   OfferAnswer(CHECK_SUCCESS);
 
-  oHasStream = HasMediaStream(mSessionOff.GetLocalTracks());
-  aHasStream = HasMediaStream(mSessionAns.GetLocalTracks());
-
-  ASSERT_EQ(oHasStream, GetLocalUniqueStreamIds(mSessionOff).size() > 0);
-  ASSERT_EQ(aHasStream, GetLocalUniqueStreamIds(mSessionAns).size() > 0);
-  ASSERT_EQ(aHasStream, GetRemoteUniqueStreamIds(mSessionOff).size() > 0);
-  ASSERT_EQ(oHasStream, GetRemoteUniqueStreamIds(mSessionAns).size() > 0);
+  oHasStream = HasMediaStream(mSessionOff->GetLocalTracks());
+  aHasStream = HasMediaStream(mSessionAns->GetLocalTracks());
+
+  ASSERT_EQ(oHasStream, GetLocalUniqueStreamIds(*mSessionOff).size() > 0);
+  ASSERT_EQ(aHasStream, GetLocalUniqueStreamIds(*mSessionAns).size() > 0);
+  ASSERT_EQ(aHasStream, GetRemoteUniqueStreamIds(*mSessionOff).size() > 0);
+  ASSERT_EQ(oHasStream, GetRemoteUniqueStreamIds(*mSessionAns).size() > 0);
   if (oHasStream) {
     ASSERT_STREQ(firstOffId.c_str(),
-                 GetFirstLocalStreamId(mSessionOff).c_str());
+                 GetFirstLocalStreamId(*mSessionOff).c_str());
   }
   if (aHasStream) {
     ASSERT_STREQ(firstAnsId.c_str(),
-                 GetFirstLocalStreamId(mSessionAns).c_str());
+                 GetFirstLocalStreamId(*mSessionAns).c_str());
+
+  auto oHasStream = HasMediaStream(mSessionOff->GetLocalTracks());
+  auto aHasStream = HasMediaStream(mSessionAns->GetLocalTracks());
+  ASSERT_EQ(oHasStream, GetLocalUniqueStreamIds(*mSessionOff).size() > 0);
+  ASSERT_EQ(aHasStream, GetLocalUniqueStreamIds(*mSessionAns).size() > 0);
   }
 }
 
 TEST_P(JsepSessionTest, RenegotiationOffererRemovesTrack)
 {
-  AddTracks(mSessionOff);
-  AddTracks(mSessionAns);
+  AddTracks(*mSessionOff);
+  AddTracks(*mSessionAns);
   if (types.front() == SdpMediaSection::kApplication) {
     return;
   }
 
   OfferAnswer();
 
-  auto offererPairs = GetTrackPairsByLevel(mSessionOff);
-  auto answererPairs = GetTrackPairsByLevel(mSessionAns);
+  auto offererPairs = GetTrackPairsByLevel(*mSessionOff);
+  auto answererPairs = GetTrackPairsByLevel(*mSessionAns);
 
   RefPtr<JsepTrack> removedTrack = GetTrackOff(0, types.front());
   ASSERT_TRUE(removedTrack);
-  ASSERT_EQ(NS_OK, mSessionOff.RemoveTrack(removedTrack->GetStreamId(),
+  ASSERT_EQ(NS_OK, mSessionOff->RemoveTrack(removedTrack->GetStreamId(),
                                            removedTrack->GetTrackId()));
 
   OfferAnswer(CHECK_SUCCESS);
 
-  auto added = mSessionAns.GetRemoteTracksAdded();
-  auto removed = mSessionAns.GetRemoteTracksRemoved();
+  auto added = mSessionAns->GetRemoteTracksAdded();
+  auto removed = mSessionAns->GetRemoteTracksRemoved();
   ASSERT_EQ(0U, added.size());
   ASSERT_EQ(1U, removed.size());
 
   ASSERT_EQ(removedTrack->GetMediaType(), removed[0]->GetMediaType());
   ASSERT_EQ(removedTrack->GetStreamId(), removed[0]->GetStreamId());
   ASSERT_EQ(removedTrack->GetTrackId(), removed[0]->GetTrackId());
 
-  added = mSessionOff.GetRemoteTracksAdded();
-  removed = mSessionOff.GetRemoteTracksRemoved();
+  added = mSessionOff->GetRemoteTracksAdded();
+  removed = mSessionOff->GetRemoteTracksRemoved();
   ASSERT_EQ(0U, added.size());
   ASSERT_EQ(0U, removed.size());
 
   // First m-section should be recvonly
-  auto offer = GetParsedLocalDescription(mSessionOff);
+  auto offer = GetParsedLocalDescription(*mSessionOff);
   auto* msection = GetMsection(*offer, types.front(), 0);
   ASSERT_TRUE(msection);
   ASSERT_TRUE(msection->IsReceiving());
   ASSERT_FALSE(msection->IsSending());
 
   // First audio m-section should be sendonly
-  auto answer = GetParsedLocalDescription(mSessionAns);
+  auto answer = GetParsedLocalDescription(*mSessionAns);
   msection = GetMsection(*answer, types.front(), 0);
   ASSERT_TRUE(msection);
   ASSERT_FALSE(msection->IsReceiving());
   ASSERT_TRUE(msection->IsSending());
 
-  auto newOffererPairs = GetTrackPairsByLevel(mSessionOff);
-  auto newAnswererPairs = GetTrackPairsByLevel(mSessionAns);
+  auto newOffererPairs = GetTrackPairsByLevel(*mSessionOff);
+  auto newAnswererPairs = GetTrackPairsByLevel(*mSessionAns);
 
   // Will be the same size since we still have a track on one side.
   ASSERT_EQ(offererPairs.size(), newOffererPairs.size());
 
   // This should be the only difference.
   ASSERT_TRUE(offererPairs[0].mSending);
   ASSERT_FALSE(newOffererPairs[0].mSending);
 
@@ -1502,64 +1649,64 @@ TEST_P(JsepSessionTest, RenegotiationOff
   answererPairs[0].mReceiving = nullptr;
   for (size_t i = 0; i < answererPairs.size(); ++i) {
     ASSERT_TRUE(Equals(answererPairs[i], newAnswererPairs[i]));
   }
 }
 
 TEST_P(JsepSessionTest, RenegotiationAnswererRemovesTrack)
 {
-  AddTracks(mSessionOff);
-  AddTracks(mSessionAns);
+  AddTracks(*mSessionOff);
+  AddTracks(*mSessionAns);
   if (types.front() == SdpMediaSection::kApplication) {
     return;
   }
 
   OfferAnswer();
 
-  auto offererPairs = GetTrackPairsByLevel(mSessionOff);
-  auto answererPairs = GetTrackPairsByLevel(mSessionAns);
+  auto offererPairs = GetTrackPairsByLevel(*mSessionOff);
+  auto answererPairs = GetTrackPairsByLevel(*mSessionAns);
 
   RefPtr<JsepTrack> removedTrack = GetTrackAns(0, types.front());
   ASSERT_TRUE(removedTrack);
-  ASSERT_EQ(NS_OK, mSessionAns.RemoveTrack(removedTrack->GetStreamId(),
+  ASSERT_EQ(NS_OK, mSessionAns->RemoveTrack(removedTrack->GetStreamId(),
                                            removedTrack->GetTrackId()));
 
   OfferAnswer(CHECK_SUCCESS);
 
-  auto added = mSessionAns.GetRemoteTracksAdded();
-  auto removed = mSessionAns.GetRemoteTracksRemoved();
+  auto added = mSessionAns->GetRemoteTracksAdded();
+  auto removed = mSessionAns->GetRemoteTracksRemoved();
   ASSERT_EQ(0U, added.size());
   ASSERT_EQ(0U, removed.size());
 
-  added = mSessionOff.GetRemoteTracksAdded();
-  removed = mSessionOff.GetRemoteTracksRemoved();
+  added = mSessionOff->GetRemoteTracksAdded();
+  removed = mSessionOff->GetRemoteTracksRemoved();
   ASSERT_EQ(0U, added.size());
   ASSERT_EQ(1U, removed.size());
 
   ASSERT_EQ(removedTrack->GetMediaType(), removed[0]->GetMediaType());
   ASSERT_EQ(removedTrack->GetStreamId(), removed[0]->GetStreamId());
   ASSERT_EQ(removedTrack->GetTrackId(), removed[0]->GetTrackId());
 
   // First m-section should be sendrecv
-  auto offer = GetParsedLocalDescription(mSessionOff);
+  auto offer = GetParsedLocalDescription(*mSessionOff);
   auto* msection = GetMsection(*offer, types.front(), 0);
   ASSERT_TRUE(msection);
   ASSERT_TRUE(msection->IsReceiving());
   ASSERT_TRUE(msection->IsSending());
 
   // First audio m-section should be recvonly
-  auto answer = GetParsedLocalDescription(mSessionAns);
+  auto answer = GetParsedLocalDescription(*mSessionAns);
   msection = GetMsection(*answer, types.front(), 0);
   ASSERT_TRUE(msection);
   ASSERT_TRUE(msection->IsReceiving());
   ASSERT_FALSE(msection->IsSending());
 
-  auto newOffererPairs = GetTrackPairsByLevel(mSessionOff);
-  auto newAnswererPairs = GetTrackPairsByLevel(mSessionAns);
+  auto newOffererPairs = GetTrackPairsByLevel(*mSessionOff);
+  auto newAnswererPairs = GetTrackPairsByLevel(*mSessionAns);
 
   // Will be the same size since we still have a track on one side.
   ASSERT_EQ(offererPairs.size(), newOffererPairs.size());
 
   // This should be the only difference.
   ASSERT_TRUE(offererPairs[0].mReceiving);
   ASSERT_FALSE(newOffererPairs[0].mReceiving);
 
@@ -1580,74 +1727,74 @@ TEST_P(JsepSessionTest, RenegotiationAns
   answererPairs[0].mSending = nullptr;
   for (size_t i = 0; i < answererPairs.size(); ++i) {
     ASSERT_TRUE(Equals(answererPairs[i], newAnswererPairs[i]));
   }
 }
 
 TEST_P(JsepSessionTest, RenegotiationBothRemoveTrack)
 {
-  AddTracks(mSessionOff);
-  AddTracks(mSessionAns);
+  AddTracks(*mSessionOff);
+  AddTracks(*mSessionAns);
   if (types.front() == SdpMediaSection::kApplication) {
     return;
   }
 
   OfferAnswer();
 
-  auto offererPairs = GetTrackPairsByLevel(mSessionOff);
-  auto answererPairs = GetTrackPairsByLevel(mSessionAns);
+  auto offererPairs = GetTrackPairsByLevel(*mSessionOff);
+  auto answererPairs = GetTrackPairsByLevel(*mSessionAns);
 
   RefPtr<JsepTrack> removedTrackAnswer = GetTrackAns(0, types.front());
   ASSERT_TRUE(removedTrackAnswer);
-  ASSERT_EQ(NS_OK, mSessionAns.RemoveTrack(removedTrackAnswer->GetStreamId(),
+  ASSERT_EQ(NS_OK, mSessionAns->RemoveTrack(removedTrackAnswer->GetStreamId(),
                                            removedTrackAnswer->GetTrackId()));
 
   RefPtr<JsepTrack> removedTrackOffer = GetTrackOff(0, types.front());
   ASSERT_TRUE(removedTrackOffer);
-  ASSERT_EQ(NS_OK, mSessionOff.RemoveTrack(removedTrackOffer->GetStreamId(),
+  ASSERT_EQ(NS_OK, mSessionOff->RemoveTrack(removedTrackOffer->GetStreamId(),
                                            removedTrackOffer->GetTrackId()));
 
   OfferAnswer(CHECK_SUCCESS);
 
-  auto added = mSessionAns.GetRemoteTracksAdded();
-  auto removed = mSessionAns.GetRemoteTracksRemoved();
+  auto added = mSessionAns->GetRemoteTracksAdded();
+  auto removed = mSessionAns->GetRemoteTracksRemoved();
   ASSERT_EQ(0U, added.size());
   ASSERT_EQ(1U, removed.size());
 
   ASSERT_EQ(removedTrackOffer->GetMediaType(), removed[0]->GetMediaType());
   ASSERT_EQ(removedTrackOffer->GetStreamId(), removed[0]->GetStreamId());
   ASSERT_EQ(removedTrackOffer->GetTrackId(), removed[0]->GetTrackId());
 
-  added = mSessionOff.GetRemoteTracksAdded();
-  removed = mSessionOff.GetRemoteTracksRemoved();
+  added = mSessionOff->GetRemoteTracksAdded();
+  removed = mSessionOff->GetRemoteTracksRemoved();
   ASSERT_EQ(0U, added.size());
   ASSERT_EQ(1U, removed.size());
 
   ASSERT_EQ(removedTrackAnswer->GetMediaType(), removed[0]->GetMediaType());
   ASSERT_EQ(removedTrackAnswer->GetStreamId(), removed[0]->GetStreamId());
   ASSERT_EQ(removedTrackAnswer->GetTrackId(), removed[0]->GetTrackId());
 
   // First m-section should be recvonly
-  auto offer = GetParsedLocalDescription(mSessionOff);
+  auto offer = GetParsedLocalDescription(*mSessionOff);
   auto* msection = GetMsection(*offer, types.front(), 0);
   ASSERT_TRUE(msection);
   ASSERT_TRUE(msection->IsReceiving());
   ASSERT_FALSE(msection->IsSending());
 
   // First m-section should be inactive, and rejected
-  auto answer = GetParsedLocalDescription(mSessionAns);
+  auto answer = GetParsedLocalDescription(*mSessionAns);
   msection = GetMsection(*answer, types.front(), 0);
   ASSERT_TRUE(msection);
   ASSERT_FALSE(msection->IsReceiving());
   ASSERT_FALSE(msection->IsSending());
   ASSERT_FALSE(msection->GetPort());
 
-  auto newOffererPairs = GetTrackPairsByLevel(mSessionOff);
-  auto newAnswererPairs = GetTrackPairsByLevel(mSessionAns);
+  auto newOffererPairs = GetTrackPairsByLevel(*mSessionOff);
+  auto newAnswererPairs = GetTrackPairsByLevel(*mSessionAns);
 
   ASSERT_EQ(offererPairs.size(), newOffererPairs.size() + 1);
 
   for (size_t i = 0; i < newOffererPairs.size(); ++i) {
     JsepTrackPair oldPair(offererPairs[i + 1]);
     JsepTrackPair newPair(newOffererPairs[i]);
     ASSERT_EQ(oldPair.mLevel, newPair.mLevel);
     ASSERT_EQ(oldPair.mSending.get(), newPair.mSending.get());
@@ -1670,141 +1817,141 @@ TEST_P(JsepSessionTest, RenegotiationBot
     ASSERT_TRUE(newPair.BundleLevel());
     ASSERT_EQ(0U, oldPair.BundleLevel());
     ASSERT_EQ(1U, newPair.BundleLevel());
   }
 }
 
 TEST_P(JsepSessionTest, RenegotiationBothRemoveThenAddTrack)
 {
-  AddTracks(mSessionOff);
-  AddTracks(mSessionAns);
+  AddTracks(*mSessionOff);
+  AddTracks(*mSessionAns);
   if (types.front() == SdpMediaSection::kApplication) {
     return;
   }
 
   SdpMediaSection::MediaType removedType = types.front();
 
   OfferAnswer();
 
   RefPtr<JsepTrack> removedTrackAnswer = GetTrackAns(0, removedType);
   ASSERT_TRUE(removedTrackAnswer);
-  ASSERT_EQ(NS_OK, mSessionAns.RemoveTrack(removedTrackAnswer->GetStreamId(),
+  ASSERT_EQ(NS_OK, mSessionAns->RemoveTrack(removedTrackAnswer->GetStreamId(),
                                            removedTrackAnswer->GetTrackId()));
 
   RefPtr<JsepTrack> removedTrackOffer = GetTrackOff(0, removedType);
   ASSERT_TRUE(removedTrackOffer);
-  ASSERT_EQ(NS_OK, mSessionOff.RemoveTrack(removedTrackOffer->GetStreamId(),
+  ASSERT_EQ(NS_OK, mSessionOff->RemoveTrack(removedTrackOffer->GetStreamId(),
                                            removedTrackOffer->GetTrackId()));
 
   OfferAnswer(CHECK_SUCCESS);
 
-  auto offererPairs = GetTrackPairsByLevel(mSessionOff);
-  auto answererPairs = GetTrackPairsByLevel(mSessionAns);
+  auto offererPairs = GetTrackPairsByLevel(*mSessionOff);
+  auto answererPairs = GetTrackPairsByLevel(*mSessionAns);
 
   std::vector<SdpMediaSection::MediaType> extraTypes;
   extraTypes.push_back(removedType);
-  AddTracks(mSessionAns, extraTypes);
-  AddTracks(mSessionOff, extraTypes);
+  AddTracks(*mSessionAns, extraTypes);
+  AddTracks(*mSessionOff, extraTypes);
   types.insert(types.end(), extraTypes.begin(), extraTypes.end());
 
   OfferAnswer(CHECK_SUCCESS);
 
-  auto added = mSessionAns.GetRemoteTracksAdded();
-  auto removed = mSessionAns.GetRemoteTracksRemoved();
+  auto added = mSessionAns->GetRemoteTracksAdded();
+  auto removed = mSessionAns->GetRemoteTracksRemoved();
   ASSERT_EQ(1U, added.size());
   ASSERT_EQ(0U, removed.size());
   ASSERT_EQ(removedType, added[0]->GetMediaType());
 
-  added = mSessionOff.GetRemoteTracksAdded();
-  removed = mSessionOff.GetRemoteTracksRemoved();
+  added = mSessionOff->GetRemoteTracksAdded();
+  removed = mSessionOff->GetRemoteTracksRemoved();
   ASSERT_EQ(1U, added.size());
   ASSERT_EQ(0U, removed.size());
   ASSERT_EQ(removedType, added[0]->GetMediaType());
 
-  auto newOffererPairs = GetTrackPairsByLevel(mSessionOff);
-  auto newAnswererPairs = GetTrackPairsByLevel(mSessionAns);
+  auto newOffererPairs = GetTrackPairsByLevel(*mSessionOff);
+  auto newAnswererPairs = GetTrackPairsByLevel(*mSessionAns);
 
   ASSERT_EQ(offererPairs.size() + 1, newOffererPairs.size());
   ASSERT_EQ(answererPairs.size() + 1, newAnswererPairs.size());
 
   // Ensure that the m-section was re-used; no gaps
   for (size_t i = 0; i < newOffererPairs.size(); ++i) {
     ASSERT_EQ(i, newOffererPairs[i].mLevel);
   }
   for (size_t i = 0; i < newAnswererPairs.size(); ++i) {
     ASSERT_EQ(i, newAnswererPairs[i].mLevel);
   }
 }
 
 TEST_P(JsepSessionTest, RenegotiationBothRemoveTrackDifferentMsection)
 {
-  AddTracks(mSessionOff);
-  AddTracks(mSessionAns);
+  AddTracks(*mSessionOff);
+  AddTracks(*mSessionAns);
   if (types.front() == SdpMediaSection::kApplication) {
     return;
   }
 
   if (types.size() < 2 || types[0] != types[1]) {
     // For simplicity, just run in cases where we have two of the same type
     return;
   }
 
   OfferAnswer();
 
-  auto offererPairs = GetTrackPairsByLevel(mSessionOff);
-  auto answererPairs = GetTrackPairsByLevel(mSessionAns);
+  auto offererPairs = GetTrackPairsByLevel(*mSessionOff);
+  auto answererPairs = GetTrackPairsByLevel(*mSessionAns);
 
   RefPtr<JsepTrack> removedTrackAnswer = GetTrackAns(0, types.front());
   ASSERT_TRUE(removedTrackAnswer);
-  ASSERT_EQ(NS_OK, mSessionAns.RemoveTrack(removedTrackAnswer->GetStreamId(),
+  ASSERT_EQ(NS_OK, mSessionAns->RemoveTrack(removedTrackAnswer->GetStreamId(),
                                            removedTrackAnswer->GetTrackId()));
 
   // Second instance of the same type
   RefPtr<JsepTrack> removedTrackOffer = GetTrackOff(1, types.front());
   ASSERT_TRUE(removedTrackOffer);
-  ASSERT_EQ(NS_OK, mSessionOff.RemoveTrack(removedTrackOffer->GetStreamId(),
+  ASSERT_EQ(NS_OK, mSessionOff->RemoveTrack(removedTrackOffer->GetStreamId(),
                                            removedTrackOffer->GetTrackId()));
 
   OfferAnswer(CHECK_SUCCESS);
 
-  auto added = mSessionAns.GetRemoteTracksAdded();
-  auto removed = mSessionAns.GetRemoteTracksRemoved();
+  auto added = mSessionAns->GetRemoteTracksAdded();
+  auto removed = mSessionAns->GetRemoteTracksRemoved();
   ASSERT_EQ(0U, added.size());
   ASSERT_EQ(1U, removed.size());
 
   ASSERT_EQ(removedTrackOffer->GetMediaType(), removed[0]->GetMediaType());
   ASSERT_EQ(removedTrackOffer->GetStreamId(), removed[0]->GetStreamId());
   ASSERT_EQ(removedTrackOffer->GetTrackId(), removed[0]->GetTrackId());
 
-  added = mSessionOff.GetRemoteTracksAdded();
-  removed = mSessionOff.GetRemoteTracksRemoved();
+  added = mSessionOff->GetRemoteTracksAdded();
+  removed = mSessionOff->GetRemoteTracksRemoved();
   ASSERT_EQ(0U, added.size());
   ASSERT_EQ(1U, removed.size());
 
   ASSERT_EQ(removedTrackAnswer->GetMediaType(), removed[0]->GetMediaType());
   ASSERT_EQ(removedTrackAnswer->GetStreamId(), removed[0]->GetStreamId());
   ASSERT_EQ(removedTrackAnswer->GetTrackId(), removed[0]->GetTrackId());
 
   // Second m-section should be recvonly
-  auto offer = GetParsedLocalDescription(mSessionOff);
+  auto offer = GetParsedLocalDescription(*mSessionOff);
   auto* msection = GetMsection(*offer, types.front(), 1);
   ASSERT_TRUE(msection);
   ASSERT_TRUE(msection->IsReceiving());
   ASSERT_FALSE(msection->IsSending());
 
   // First m-section should be recvonly
-  auto answer = GetParsedLocalDescription(mSessionAns);
+  auto answer = GetParsedLocalDescription(*mSessionAns);
   msection = GetMsection(*answer, types.front(), 0);
   ASSERT_TRUE(msection);
   ASSERT_TRUE(msection->IsReceiving());
   ASSERT_FALSE(msection->IsSending());
 
-  auto newOffererPairs = GetTrackPairsByLevel(mSessionOff);
-  auto newAnswererPairs = GetTrackPairsByLevel(mSessionAns);
+  auto newOffererPairs = GetTrackPairsByLevel(*mSessionOff);
+  auto newAnswererPairs = GetTrackPairsByLevel(*mSessionAns);
 
   ASSERT_EQ(offererPairs.size(), newOffererPairs.size());
 
   // This should be the only difference.
   ASSERT_TRUE(offererPairs[0].mReceiving);
   ASSERT_FALSE(newOffererPairs[0].mReceiving);
 
   // Remove this difference, let loop below take care of the rest
@@ -1839,72 +1986,72 @@ TEST_P(JsepSessionTest, RenegotiationBot
 
   for (size_t i = 0; i < newAnswererPairs.size(); ++i) {
     ASSERT_TRUE(Equals(answererPairs[i], newAnswererPairs[i]));
   }
 }
 
 TEST_P(JsepSessionTest, RenegotiationOffererReplacesTrack)
 {
-  AddTracks(mSessionOff);
-  AddTracks(mSessionAns);
+  AddTracks(*mSessionOff);
+  AddTracks(*mSessionAns);
 
   if (types.front() == SdpMediaSection::kApplication) {
     return;
   }
 
   OfferAnswer();
 
-  auto offererPairs = GetTrackPairsByLevel(mSessionOff);
-  auto answererPairs = GetTrackPairsByLevel(mSessionAns);
+  auto offererPairs = GetTrackPairsByLevel(*mSessionOff);
+  auto answererPairs = GetTrackPairsByLevel(*mSessionAns);
 
   RefPtr<JsepTrack> removedTrack = GetTrackOff(0, types.front());
   ASSERT_TRUE(removedTrack);
-  ASSERT_EQ(NS_OK, mSessionOff.RemoveTrack(removedTrack->GetStreamId(),
+  ASSERT_EQ(NS_OK, mSessionOff->RemoveTrack(removedTrack->GetStreamId(),
                                            removedTrack->GetTrackId()));
   RefPtr<JsepTrack> addedTrack(
       new JsepTrack(types.front(), "newstream", "newtrack"));
-  ASSERT_EQ(NS_OK, mSessionOff.AddTrack(addedTrack));
+  ASSERT_EQ(NS_OK, mSessionOff->AddTrack(addedTrack));
 
   OfferAnswer(CHECK_SUCCESS);
 
-  auto added = mSessionAns.GetRemoteTracksAdded();
-  auto removed = mSessionAns.GetRemoteTracksRemoved();
+  auto added = mSessionAns->GetRemoteTracksAdded();
+  auto removed = mSessionAns->GetRemoteTracksRemoved();
   ASSERT_EQ(1U, added.size());
   ASSERT_EQ(1U, removed.size());
 
   ASSERT_EQ(removedTrack->GetMediaType(), removed[0]->GetMediaType());
   ASSERT_EQ(removedTrack->GetStreamId(), removed[0]->GetStreamId());
   ASSERT_EQ(removedTrack->GetTrackId(), removed[0]->GetTrackId());
 
   ASSERT_EQ(addedTrack->GetMediaType(), added[0]->GetMediaType());
   ASSERT_EQ(addedTrack->GetStreamId(), added[0]->GetStreamId());
   ASSERT_EQ(addedTrack->GetTrackId(), added[0]->GetTrackId());
 
-  added = mSessionOff.GetRemoteTracksAdded();
-  removed = mSessionOff.GetRemoteTracksRemoved();
+  added = mSessionOff->GetRemoteTracksAdded();
+  removed = mSessionOff->GetRemoteTracksRemoved();
   ASSERT_EQ(0U, added.size());
   ASSERT_EQ(0U, removed.size());
 
   // First audio m-section should be sendrecv
-  auto offer = GetParsedLocalDescription(mSessionOff);
+  auto offer = GetParsedLocalDescription(*mSessionOff);
   auto* msection = GetMsection(*offer, types.front(), 0);
   ASSERT_TRUE(msection);
   ASSERT_TRUE(msection->IsReceiving());
   ASSERT_TRUE(msection->IsSending());
 
   // First audio m-section should be sendrecv
-  auto answer = GetParsedLocalDescription(mSessionAns);
+  auto answer = GetParsedLocalDescription(*mSessionAns);
   msection = GetMsection(*answer, types.front(), 0);
   ASSERT_TRUE(msection);
   ASSERT_TRUE(msection->IsReceiving());
   ASSERT_TRUE(msection->IsSending());
 
-  auto newOffererPairs = GetTrackPairsByLevel(mSessionOff);
-  auto newAnswererPairs = GetTrackPairsByLevel(mSessionAns);
+  auto newOffererPairs = GetTrackPairsByLevel(*mSessionOff);
+  auto newAnswererPairs = GetTrackPairsByLevel(*mSessionAns);
 
   ASSERT_EQ(offererPairs.size(), newOffererPairs.size());
 
   ASSERT_NE(offererPairs[0].mSending->GetStreamId(),
             newOffererPairs[0].mSending->GetStreamId());
   ASSERT_NE(offererPairs[0].mSending->GetTrackId(),
             newOffererPairs[0].mSending->GetTrackId());
 
@@ -1925,69 +2072,69 @@ TEST_P(JsepSessionTest, RenegotiationOff
     ASSERT_TRUE(Equals(answererPairs[i], newAnswererPairs[i]));
   }
 }
 
 // Tests whether auto-assigned remote msids (ie; what happens when the other
 // side doesn't use msid attributes) are stable across renegotiation.
 TEST_P(JsepSessionTest, RenegotiationAutoAssignedMsidIsStable)
 {
-  AddTracks(mSessionOff);
+  AddTracks(*mSessionOff);
   std::string offer = CreateOffer();
   SetLocalOffer(offer);
   SetRemoteOffer(offer);
-  AddTracks(mSessionAns);
+  AddTracks(*mSessionAns);
   std::string answer = CreateAnswer();
   SetLocalAnswer(answer);
 
   DisableMsid(&answer);
 
   SetRemoteAnswer(answer, CHECK_SUCCESS);
 
-  auto offererPairs = GetTrackPairsByLevel(mSessionOff);
+  auto offererPairs = GetTrackPairsByLevel(*mSessionOff);
 
   // Make sure that DisableMsid actually worked, since it is kinda hacky
-  auto answererPairs = GetTrackPairsByLevel(mSessionAns);
+  auto answererPairs = GetTrackPairsByLevel(*mSessionAns);
   ASSERT_EQ(offererPairs.size(), answererPairs.size());
   for (size_t i = 0; i < offererPairs.size(); ++i) {
     ASSERT_TRUE(offererPairs[i].mReceiving);
     ASSERT_TRUE(answererPairs[i].mSending);
     // These should not match since we've monkeyed with the msid
     ASSERT_NE(offererPairs[i].mReceiving->GetStreamId(),
               answererPairs[i].mSending->GetStreamId());
     ASSERT_NE(offererPairs[i].mReceiving->GetTrackId(),
               answererPairs[i].mSending->GetTrackId());
   }
 
   offer = CreateOffer();
   SetLocalOffer(offer);
   SetRemoteOffer(offer);
-  AddTracks(mSessionAns);
+  AddTracks(*mSessionAns);
   answer = CreateAnswer();
   SetLocalAnswer(answer);
 
   DisableMsid(&answer);
 
   SetRemoteAnswer(answer, CHECK_SUCCESS);
 
-  auto newOffererPairs = mSessionOff.GetNegotiatedTrackPairs();
+  auto newOffererPairs = mSessionOff->GetNegotiatedTrackPairs();
 
   ASSERT_EQ(offererPairs.size(), newOffererPairs.size());
   for (size_t i = 0; i < offererPairs.size(); ++i) {
     ASSERT_TRUE(Equals(offererPairs[i], newOffererPairs[i]));
   }
 }
 
 TEST_P(JsepSessionTest, RenegotiationOffererDisablesTelephoneEvent)
 {
-  AddTracks(mSessionOff);
-  AddTracks(mSessionAns);
+  AddTracks(*mSessionOff);
+  AddTracks(*mSessionAns);
   OfferAnswer();
 
-  auto offererPairs = GetTrackPairsByLevel(mSessionOff);
+  auto offererPairs = GetTrackPairsByLevel(*mSessionOff);
 
   // check all the audio tracks to make sure they have 2 codecs (109 and 101),
   // and dtmf is enabled on all audio tracks
   for (size_t i = 0; i < offererPairs.size(); ++i) {
     std::vector<JsepTrack*> tracks;
     tracks.push_back(offererPairs[i].mSending.get());
     tracks.push_back(offererPairs[i].mReceiving.get());
     for (JsepTrack *track : tracks) {
@@ -2013,22 +2160,22 @@ TEST_P(JsepSessionTest, RenegotiationOff
   std::string offer = CreateOffer();
   ReplaceInSdp(&offer, " 109 101 ", " 109 ");
   ReplaceInSdp(&offer, "a=fmtp:101 0-15\r\n", "");
   ReplaceInSdp(&offer, "a=rtpmap:101 telephone-event/8000/1\r\n", "");
   std::cerr << "modified OFFER: " << offer << std::endl;
 
   SetLocalOffer(offer);
   SetRemoteOffer(offer);
-  AddTracks(mSessionAns);
+  AddTracks(*mSessionAns);
   std::string answer = CreateAnswer();
   SetLocalAnswer(answer);
   SetRemoteAnswer(answer);
 
-  auto newOffererPairs = GetTrackPairsByLevel(mSessionOff);
+  auto newOffererPairs = GetTrackPairsByLevel(*mSessionOff);
 
   // check all the audio tracks to make sure they have 1 codec (109),
   // and dtmf is disabled on all audio tracks
   for (size_t i = 0; i < newOffererPairs.size(); ++i) {
     std::vector<JsepTrack*> tracks;
     tracks.push_back(newOffererPairs[i].mSending.get());
     tracks.push_back(newOffererPairs[i].mReceiving.get());
     for (JsepTrack* track : tracks) {
@@ -2048,39 +2195,39 @@ TEST_P(JsepSessionTest, RenegotiationOff
     }
   }
 }
 
 // Tests behavior when the answerer does not use msid in the initial exchange,
 // but does on renegotiation.
 TEST_P(JsepSessionTest, RenegotiationAnswererEnablesMsid)
 {
-  AddTracks(mSessionOff);
+  AddTracks(*mSessionOff);
   std::string offer = CreateOffer();
   SetLocalOffer(offer);
   SetRemoteOffer(offer);
-  AddTracks(mSessionAns);
+  AddTracks(*mSessionAns);
   std::string answer = CreateAnswer();
   SetLocalAnswer(answer);
 
   DisableMsid(&answer);
 
   SetRemoteAnswer(answer, CHECK_SUCCESS);
 
-  auto offererPairs = GetTrackPairsByLevel(mSessionOff);
+  auto offererPairs = GetTrackPairsByLevel(*mSessionOff);
 
   offer = CreateOffer();
   SetLocalOffer(offer);
   SetRemoteOffer(offer);
-  AddTracks(mSessionAns);
+  AddTracks(*mSessionAns);
   answer = CreateAnswer();
   SetLocalAnswer(answer);
   SetRemoteAnswer(answer, CHECK_SUCCESS);
 
-  auto newOffererPairs = mSessionOff.GetNegotiatedTrackPairs();
+  auto newOffererPairs = mSessionOff->GetNegotiatedTrackPairs();
 
   ASSERT_EQ(offererPairs.size(), newOffererPairs.size());
   for (size_t i = 0; i < offererPairs.size(); ++i) {
     ASSERT_EQ(offererPairs[i].mReceiving->GetMediaType(),
               newOffererPairs[i].mReceiving->GetMediaType());
 
     ASSERT_EQ(offererPairs[i].mSending, newOffererPairs[i].mSending);
     ASSERT_TRUE(Equals(offererPairs[i].mRtpTransport,
@@ -2095,39 +2242,39 @@ TEST_P(JsepSessionTest, RenegotiationAns
       // This should be the only difference
       ASSERT_NE(offererPairs[i].mReceiving, newOffererPairs[i].mReceiving);
     }
   }
 }
 
 TEST_P(JsepSessionTest, RenegotiationAnswererDisablesMsid)
 {
-  AddTracks(mSessionOff);
+  AddTracks(*mSessionOff);
   std::string offer = CreateOffer();
   SetLocalOffer(offer);
   SetRemoteOffer(offer);
-  AddTracks(mSessionAns);
+  AddTracks(*mSessionAns);
   std::string answer = CreateAnswer();
   SetLocalAnswer(answer);
   SetRemoteAnswer(answer, CHECK_SUCCESS);
 
-  auto offererPairs = GetTrackPairsByLevel(mSessionOff);
+  auto offererPairs = GetTrackPairsByLevel(*mSessionOff);
 
   offer = CreateOffer();
   SetLocalOffer(offer);
   SetRemoteOffer(offer);
-  AddTracks(mSessionAns);
+  AddTracks(*mSessionAns);
   answer = CreateAnswer();
   SetLocalAnswer(answer);
 
   DisableMsid(&answer);
 
   SetRemoteAnswer(answer, CHECK_SUCCESS);
 
-  auto newOffererPairs = mSessionOff.GetNegotiatedTrackPairs();
+  auto newOffererPairs = mSessionOff->GetNegotiatedTrackPairs();
 
   ASSERT_EQ(offererPairs.size(), newOffererPairs.size());
   for (size_t i = 0; i < offererPairs.size(); ++i) {
     ASSERT_EQ(offererPairs[i].mReceiving->GetMediaType(),
               newOffererPairs[i].mReceiving->GetMediaType());
 
     ASSERT_EQ(offererPairs[i].mSending, newOffererPairs[i].mSending);
     ASSERT_TRUE(Equals(offererPairs[i].mRtpTransport,
@@ -2144,41 +2291,41 @@ TEST_P(JsepSessionTest, RenegotiationAns
     }
   }
 }
 
 // Tests behavior when offerer does not use bundle on the initial offer/answer,
 // but does on renegotiation.
 TEST_P(JsepSessionTest, RenegotiationOffererEnablesBundle)
 {
-  AddTracks(mSessionOff);
-  AddTracks(mSessionAns);
+  AddTracks(*mSessionOff);
+  AddTracks(*mSessionAns);
 
   if (types.size() < 2) {
     // No bundle will happen here.
     return;
   }
 
   std::string offer = CreateOffer();
 
   DisableBundle(&offer);
 
   SetLocalOffer(offer);
   SetRemoteOffer(offer);
   std::string answer = CreateAnswer();
   SetLocalAnswer(answer);
   SetRemoteAnswer(answer);
 
-  auto offererPairs = GetTrackPairsByLevel(mSessionOff);
-  auto answererPairs = GetTrackPairsByLevel(mSessionAns);
+  auto offererPairs = GetTrackPairsByLevel(*mSessionOff);
+  auto answererPairs = GetTrackPairsByLevel(*mSessionAns);
 
   OfferAnswer();
 
-  auto newOffererPairs = GetTrackPairsByLevel(mSessionOff);
-  auto newAnswererPairs = GetTrackPairsByLevel(mSessionAns);
+  auto newOffererPairs = GetTrackPairsByLevel(*mSessionOff);
+  auto newAnswererPairs = GetTrackPairsByLevel(*mSessionAns);
 
   ASSERT_EQ(newOffererPairs.size(), newAnswererPairs.size());
   ASSERT_EQ(offererPairs.size(), newOffererPairs.size());
   ASSERT_EQ(answererPairs.size(), newAnswererPairs.size());
 
   for (size_t i = 0; i < newOffererPairs.size(); ++i) {
     // No bundle initially
     ASSERT_FALSE(offererPairs[i].HasBundleLevel());
@@ -2209,40 +2356,40 @@ TEST_P(JsepSessionTest, RenegotiationOff
               newAnswererPairs[i].mRtpTransport.get());
     ASSERT_EQ(newAnswererPairs[0].mRtcpTransport.get(),
               newAnswererPairs[i].mRtcpTransport.get());
   }
 }
 
 TEST_P(JsepSessionTest, RenegotiationOffererDisablesBundleTransport)
 {
-  AddTracks(mSessionOff);
-  AddTracks(mSessionAns);
+  AddTracks(*mSessionOff);
+  AddTracks(*mSessionAns);
 
   if (types.size() < 2) {
     return;
   }
 
   OfferAnswer();
 
-  auto offererPairs = GetTrackPairsByLevel(mSessionOff);
-  auto answererPairs = GetTrackPairsByLevel(mSessionAns);
+  auto offererPairs = GetTrackPairsByLevel(*mSessionOff);
+  auto answererPairs = GetTrackPairsByLevel(*mSessionAns);
 
   std::string reoffer = CreateOffer();
 
   DisableMsection(&reoffer, 0);
 
   SetLocalOffer(reoffer, CHECK_SUCCESS);
   SetRemoteOffer(reoffer, CHECK_SUCCESS);
   std::string reanswer = CreateAnswer();
   SetLocalAnswer(reanswer, CHECK_SUCCESS);
   SetRemoteAnswer(reanswer, CHECK_SUCCESS);
 
-  auto newOffererPairs = GetTrackPairsByLevel(mSessionOff);
-  auto newAnswererPairs = GetTrackPairsByLevel(mSessionAns);
+  auto newOffererPairs = GetTrackPairsByLevel(*mSessionOff);
+  auto newAnswererPairs = GetTrackPairsByLevel(*mSessionAns);
 
   ASSERT_EQ(newOffererPairs.size(), newAnswererPairs.size());
   ASSERT_EQ(offererPairs.size(), newOffererPairs.size() + 1);
   ASSERT_EQ(answererPairs.size(), newAnswererPairs.size() + 1);
 
   for (size_t i = 0; i < newOffererPairs.size(); ++i) {
     ASSERT_TRUE(newOffererPairs[i].HasBundleLevel());
     ASSERT_TRUE(newAnswererPairs[i].HasBundleLevel());
@@ -2258,49 +2405,50 @@ TEST_P(JsepSessionTest, RenegotiationOff
               newAnswererPairs[i].mRtcpTransport.get());
   }
 
   ASSERT_NE(newOffererPairs[0].mRtpTransport.get(),
             offererPairs[0].mRtpTransport.get());
   ASSERT_NE(newAnswererPairs[0].mRtpTransport.get(),
             answererPairs[0].mRtpTransport.get());
 
-  ASSERT_LE(1U, mSessionOff.GetTransports().size());
-  ASSERT_LE(1U, mSessionAns.GetTransports().size());
-
-  ASSERT_EQ(0U, mSessionOff.GetTransports()[0]->mComponents);
-  ASSERT_EQ(0U, mSessionAns.GetTransports()[0]->mComponents);
+  ASSERT_LE(1U, mSessionOff->GetTransports().size());
+  ASSERT_LE(1U, mSessionAns->GetTransports().size());
+
+  ASSERT_EQ(0U, mSessionOff->GetTransports()[0]->mComponents);
+  ASSERT_EQ(0U, mSessionAns->GetTransports()[0]->mComponents);
 }
 
 TEST_P(JsepSessionTest, RenegotiationAnswererDisablesBundleTransport)
 {
-  AddTracks(mSessionOff);
-  AddTracks(mSessionAns);
+  AddTracks(*mSessionOff);
+  AddTracks(*mSessionAns);
 
   if (types.size() < 2) {
     return;
   }
 
   OfferAnswer();
 
-  auto offererPairs = GetTrackPairsByLevel(mSessionOff);
-  auto answererPairs = GetTrackPairsByLevel(mSessionAns);
+  auto offererPairs = GetTrackPairsByLevel(*mSessionOff);
+  auto answererPairs = GetTrackPairsByLevel(*mSessionAns);
 
   std::string reoffer = CreateOffer();
   SetLocalOffer(reoffer, CHECK_SUCCESS);
   SetRemoteOffer(reoffer, CHECK_SUCCESS);
   std::string reanswer = CreateAnswer();
 
+  CopyTransportAttributes(&reanswer, 0, 1);
   DisableMsection(&reanswer, 0);
 
   SetLocalAnswer(reanswer, CHECK_SUCCESS);
   SetRemoteAnswer(reanswer, CHECK_SUCCESS);
 
-  auto newOffererPairs = GetTrackPairsByLevel(mSessionOff);
-  auto newAnswererPairs = GetTrackPairsByLevel(mSessionAns);
+  auto newOffererPairs = GetTrackPairsByLevel(*mSessionOff);
+  auto newAnswererPairs = GetTrackPairsByLevel(*mSessionAns);
 
   ASSERT_EQ(newOffererPairs.size(), newAnswererPairs.size());
   ASSERT_EQ(offererPairs.size(), newOffererPairs.size() + 1);
   ASSERT_EQ(answererPairs.size(), newAnswererPairs.size() + 1);
 
   for (size_t i = 0; i < newOffererPairs.size(); ++i) {
     ASSERT_TRUE(newOffererPairs[i].HasBundleLevel());
     ASSERT_TRUE(newAnswererPairs[i].HasBundleLevel());
@@ -2322,317 +2470,317 @@ TEST_P(JsepSessionTest, RenegotiationAns
             answererPairs[0].mRtpTransport.get());
 }
 
 TEST_P(JsepSessionTest, ParseRejectsBadMediaFormat)
 {
   if (GetParam() == "datachannel") {
     return;
   }
-  AddTracks(mSessionOff);
+  AddTracks(*mSessionOff);
   std::string offer = CreateOffer();
   UniquePtr<Sdp> munge(Parse(offer));
   SdpMediaSection& mediaSection = munge->GetMediaSection(0);
   mediaSection.AddCodec("75", "DummyFormatVal", 8000, 1);
   std::string sdpString = munge->ToString();
-  nsresult rv = mSessionOff.SetLocalDescription(kJsepSdpOffer, sdpString);
+  nsresult rv = mSessionOff->SetLocalDescription(kJsepSdpOffer, sdpString);
   ASSERT_EQ(NS_ERROR_INVALID_ARG, rv);
 }
 
 TEST_P(JsepSessionTest, FullCallWithCandidates)
 {
-  AddTracks(mSessionOff);
+  AddTracks(*mSessionOff);
   std::string offer = CreateOffer();
   SetLocalOffer(offer);
-  mOffCandidates.Gather(mSessionOff, types);
-
-  UniquePtr<Sdp> localOffer(Parse(mSessionOff.GetLocalDescription()));
+  mOffCandidates->Gather(*mSessionOff, types);
+
+  UniquePtr<Sdp> localOffer(Parse(mSessionOff->GetLocalDescription()));
   for (size_t i = 0; i < localOffer->GetMediaSectionCount(); ++i) {
-    mOffCandidates.CheckRtpCandidates(
+    mOffCandidates->CheckRtpCandidates(
         true, localOffer->GetMediaSection(i), i,
         "Local offer after gathering should have RTP candidates.");
-    mOffCandidates.CheckDefaultRtpCandidate(
+    mOffCandidates->CheckDefaultRtpCandidate(
         true, localOffer->GetMediaSection(i), i,
         "Local offer after gathering should have a default RTP candidate.");
-    mOffCandidates.CheckRtcpCandidates(
+    mOffCandidates->CheckRtcpCandidates(
         types[i] != SdpMediaSection::kApplication,
         localOffer->GetMediaSection(i), i,
         "Local offer after gathering should have RTCP candidates "
         "(unless m=application)");
-    mOffCandidates.CheckDefaultRtcpCandidate(
+    mOffCandidates->CheckDefaultRtcpCandidate(
         types[i] != SdpMediaSection::kApplication,
         localOffer->GetMediaSection(i), i,
         "Local offer after gathering should have a default RTCP candidate "
         "(unless m=application)");
     CheckEndOfCandidates(true, localOffer->GetMediaSection(i),
         "Local offer after gathering should have an end-of-candidates.");
   }
 
   SetRemoteOffer(offer);
-  mOffCandidates.Trickle(mSessionAns);
-
-  UniquePtr<Sdp> remoteOffer(Parse(mSessionAns.GetRemoteDescription()));
+  mOffCandidates->Trickle(*mSessionAns);
+
+  UniquePtr<Sdp> remoteOffer(Parse(mSessionAns->GetRemoteDescription()));
   for (size_t i = 0; i < remoteOffer->GetMediaSectionCount(); ++i) {
-    mOffCandidates.CheckRtpCandidates(
+    mOffCandidates->CheckRtpCandidates(
         true, remoteOffer->GetMediaSection(i), i,
         "Remote offer after trickle should have RTP candidates.");
-    mOffCandidates.CheckDefaultRtpCandidate(
+    mOffCandidates->CheckDefaultRtpCandidate(
         false, remoteOffer->GetMediaSection(i), i,
         "Initial remote offer should not have a default RTP candidate.");
-    mOffCandidates.CheckRtcpCandidates(
+    mOffCandidates->CheckRtcpCandidates(
         types[i] != SdpMediaSection::kApplication,
         remoteOffer->GetMediaSection(i), i,
         "Remote offer after trickle should have RTCP candidates "
         "(unless m=application)");
-    mOffCandidates.CheckDefaultRtcpCandidate(
+    mOffCandidates->CheckDefaultRtcpCandidate(
         false, remoteOffer->GetMediaSection(i), i,
         "Initial remote offer should not have a default RTCP candidate.");
     CheckEndOfCandidates(false, remoteOffer->GetMediaSection(i),
         "Initial remote offer should not have an end-of-candidates.");
   }
 
-  AddTracks(mSessionAns);
+  AddTracks(*mSessionAns);
   std::string answer = CreateAnswer();
   SetLocalAnswer(answer);
   // This will gather candidates that mSessionAns knows it doesn't need.
   // They should not be present in the SDP.
-  mAnsCandidates.Gather(mSessionAns, types);
-
-  UniquePtr<Sdp> localAnswer(Parse(mSessionAns.GetLocalDescription()));
+  mAnsCandidates->Gather(*mSessionAns, types);
+
+  UniquePtr<Sdp> localAnswer(Parse(mSessionAns->GetLocalDescription()));
   for (size_t i = 0; i < localAnswer->GetMediaSectionCount(); ++i) {
-    mAnsCandidates.CheckRtpCandidates(
+    mAnsCandidates->CheckRtpCandidates(
         i == 0, localAnswer->GetMediaSection(i), i,
         "Local answer after gathering should have RTP candidates on level 0.");
-    mAnsCandidates.CheckDefaultRtpCandidate(
+    mAnsCandidates->CheckDefaultRtpCandidate(
         true, localAnswer->GetMediaSection(i), 0,
         "Local answer after gathering should have a default RTP candidate "
         "on all levels that matches transport level 0.");
-    mAnsCandidates.CheckRtcpCandidates(
+    mAnsCandidates->CheckRtcpCandidates(
         false, localAnswer->GetMediaSection(i), i,
         "Local answer after gathering should not have RTCP candidates "
         "(because we're answering with rtcp-mux)");
-    mAnsCandidates.CheckDefaultRtcpCandidate(
+    mAnsCandidates->CheckDefaultRtcpCandidate(
         false, localAnswer->GetMediaSection(i), i,
         "Local answer after gathering should not have a default RTCP candidate "
         "(because we're answering with rtcp-mux)");
     CheckEndOfCandidates(i == 0, localAnswer->GetMediaSection(i),
         "Local answer after gathering should have an end-of-candidates only for"
         " level 0.");
   }
 
   SetRemoteAnswer(answer);
-  mAnsCandidates.Trickle(mSessionOff);
-
-  UniquePtr<Sdp> remoteAnswer(Parse(mSessionOff.GetRemoteDescription()));
+  mAnsCandidates->Trickle(*mSessionOff);
+
+  UniquePtr<Sdp> remoteAnswer(Parse(mSessionOff->GetRemoteDescription()));
   for (size_t i = 0; i < remoteAnswer->GetMediaSectionCount(); ++i) {
-    mAnsCandidates.CheckRtpCandidates(
+    mAnsCandidates->CheckRtpCandidates(
         i == 0, remoteAnswer->GetMediaSection(i), i,
         "Remote answer after trickle should have RTP candidates on level 0.");
-    mAnsCandidates.CheckDefaultRtpCandidate(
+    mAnsCandidates->CheckDefaultRtpCandidate(
         false, remoteAnswer->GetMediaSection(i), i,
         "Remote answer after trickle should not have a default RTP candidate.");
-    mAnsCandidates.CheckRtcpCandidates(
+    mAnsCandidates->CheckRtcpCandidates(
         false, remoteAnswer->GetMediaSection(i), i,
         "Remote answer after trickle should not have RTCP candidates "
         "(because we're answering with rtcp-mux)");
-    mAnsCandidates.CheckDefaultRtcpCandidate(
+    mAnsCandidates->CheckDefaultRtcpCandidate(
         false, remoteAnswer->GetMediaSection(i), i,
         "Remote answer after trickle should not have a default RTCP "
         "candidate.");
     CheckEndOfCandidates(false, remoteAnswer->GetMediaSection(i),
         "Remote answer after trickle should not have an end-of-candidates.");
   }
 }
 
 TEST_P(JsepSessionTest, RenegotiationWithCandidates)
 {
-  AddTracks(mSessionOff);
+  AddTracks(*mSessionOff);
   std::string offer = CreateOffer();
   SetLocalOffer(offer);
-  mOffCandidates.Gather(mSessionOff, types);
+  mOffCandidates->Gather(*mSessionOff, types);
   SetRemoteOffer(offer);
-  mOffCandidates.Trickle(mSessionAns);
-  AddTracks(mSessionAns);
+  mOffCandidates->Trickle(*mSessionAns);
+  AddTracks(*mSessionAns);
   std::string answer = CreateAnswer();
   SetLocalAnswer(answer);
-  mAnsCandidates.Gather(mSessionAns, types);
+  mAnsCandidates->Gather(*mSessionAns, types);
   SetRemoteAnswer(answer);
-  mAnsCandidates.Trickle(mSessionOff);
+  mAnsCandidates->Trickle(*mSessionOff);
 
   offer = CreateOffer();
   SetLocalOffer(offer);
 
   UniquePtr<Sdp> parsedOffer(Parse(offer));
   for (size_t i = 0; i < parsedOffer->GetMediaSectionCount(); ++i) {
-    mOffCandidates.CheckRtpCandidates(
+    mOffCandidates->CheckRtpCandidates(
         i == 0, parsedOffer->GetMediaSection(i), i,
         "Local reoffer before gathering should have RTP candidates on level 0"
         " only.");
-    mOffCandidates.CheckDefaultRtpCandidate(
+    mOffCandidates->CheckDefaultRtpCandidate(
         i == 0, parsedOffer->GetMediaSection(i), 0,
         "Local reoffer before gathering should have a default RTP candidate "
         "on level 0 only.");
-    mOffCandidates.CheckRtcpCandidates(
+    mOffCandidates->CheckRtcpCandidates(
         false, parsedOffer->GetMediaSection(i), i,
         "Local reoffer before gathering should not have RTCP candidates.");
-    mOffCandidates.CheckDefaultRtcpCandidate(
+    mOffCandidates->CheckDefaultRtcpCandidate(
         false, parsedOffer->GetMediaSection(i), i,
         "Local reoffer before gathering should not have a default RTCP "
         "candidate.");
     CheckEndOfCandidates(false, parsedOffer->GetMediaSection(i),
         "Local reoffer before gathering should not have an end-of-candidates.");
   }
 
   // mSessionAns should generate a reoffer that is similar
   std::string otherOffer;
   JsepOfferOptions defaultOptions;
-  nsresult rv = mSessionAns.CreateOffer(defaultOptions, &otherOffer);
+  nsresult rv = mSessionAns->CreateOffer(defaultOptions, &otherOffer);
   ASSERT_EQ(NS_OK, rv);
   parsedOffer = Parse(otherOffer);
   for (size_t i = 0; i < parsedOffer->GetMediaSectionCount(); ++i) {
-    mAnsCandidates.CheckRtpCandidates(
+    mAnsCandidates->CheckRtpCandidates(
         i == 0, parsedOffer->GetMediaSection(i), i,
         "Local reoffer before gathering should have RTP candidates on level 0"
         " only. (previous answerer)");
-    mAnsCandidates.CheckDefaultRtpCandidate(
+    mAnsCandidates->CheckDefaultRtpCandidate(
         i == 0, parsedOffer->GetMediaSection(i), 0,
         "Local reoffer before gathering should have a default RTP candidate "
         "on level 0 only. (previous answerer)");
-    mAnsCandidates.CheckRtcpCandidates(
+    mAnsCandidates->CheckRtcpCandidates(
         false, parsedOffer->GetMediaSection(i), i,
         "Local reoffer before gathering should not have RTCP candidates."
         " (previous answerer)");
-    mAnsCandidates.CheckDefaultRtcpCandidate(
+    mAnsCandidates->CheckDefaultRtcpCandidate(
         false, parsedOffer->GetMediaSection(i), i,
         "Local reoffer before gathering should not have a default RTCP "
         "candidate. (previous answerer)");
     CheckEndOfCandidates(false, parsedOffer->GetMediaSection(i),
         "Local reoffer before gathering should not have an end-of-candidates. "
         "(previous answerer)");
   }
 
   // Ok, let's continue with the renegotiation
   SetRemoteOffer(offer);
 
   // PeerConnection will not re-gather for RTP, but it will for RTCP in case
   // the answerer decides to turn off rtcp-mux.
   if (types[0] != SdpMediaSection::kApplication) {
-    mOffCandidates.Gather(mSessionOff, 0, RTCP);
+    mOffCandidates->Gather(*mSessionOff, 0, RTCP);
   }
 
   // Since the remaining levels were bundled, PeerConnection will re-gather for
   // both RTP and RTCP, in case the answerer rejects bundle.
   for (size_t level = 1; level < types.size(); ++level) {
-    mOffCandidates.Gather(mSessionOff, level, RTP);
+    mOffCandidates->Gather(*mSessionOff, level, RTP);
     if (types[level] != SdpMediaSection::kApplication) {
-      mOffCandidates.Gather(mSessionOff, level, RTCP);
+      mOffCandidates->Gather(*mSessionOff, level, RTCP);
     }
   }
-  mOffCandidates.FinishGathering(mSessionOff);
-
-  mOffCandidates.Trickle(mSessionAns);
-
-  UniquePtr<Sdp> localOffer(Parse(mSessionOff.GetLocalDescription()));
+  mOffCandidates->FinishGathering(*mSessionOff);
+
+  mOffCandidates->Trickle(*mSessionAns);
+
+  UniquePtr<Sdp> localOffer(Parse(mSessionOff->GetLocalDescription()));
   for (size_t i = 0; i < localOffer->GetMediaSectionCount(); ++i) {
-    mOffCandidates.CheckRtpCandidates(
+    mOffCandidates->CheckRtpCandidates(
         true, localOffer->GetMediaSection(i), i,
         "Local reoffer after gathering should have RTP candidates.");
-    mOffCandidates.CheckDefaultRtpCandidate(
+    mOffCandidates->CheckDefaultRtpCandidate(
         true, localOffer->GetMediaSection(i), i,
         "Local reoffer after gathering should have a default RTP candidate.");
-    mOffCandidates.CheckRtcpCandidates(
+    mOffCandidates->CheckRtcpCandidates(
         types[i] != SdpMediaSection::kApplication,
         localOffer->GetMediaSection(i), i,
         "Local reoffer after gathering should have RTCP candidates "
         "(unless m=application)");
-    mOffCandidates.CheckDefaultRtcpCandidate(
+    mOffCandidates->CheckDefaultRtcpCandidate(
         types[i] != SdpMediaSection::kApplication,
         localOffer->GetMediaSection(i), i,
         "Local reoffer after gathering should have a default RTCP candidate "
         "(unless m=application)");
     CheckEndOfCandidates(true, localOffer->GetMediaSection(i),
         "Local reoffer after gathering should have an end-of-candidates.");
   }
 
-  UniquePtr<Sdp> remoteOffer(Parse(mSessionAns.GetRemoteDescription()));
+  UniquePtr<Sdp> remoteOffer(Parse(mSessionAns->GetRemoteDescription()));
   for (size_t i = 0; i < remoteOffer->GetMediaSectionCount(); ++i) {
-    mOffCandidates.CheckRtpCandidates(
+    mOffCandidates->CheckRtpCandidates(
         true, remoteOffer->GetMediaSection(i), i,
         "Remote reoffer after trickle should have RTP candidates.");
-    mOffCandidates.CheckDefaultRtpCandidate(
+    mOffCandidates->CheckDefaultRtpCandidate(
         i == 0, remoteOffer->GetMediaSection(i), i,
         "Remote reoffer should have a default RTP candidate on level 0 "
         "(because it was gathered last offer/answer).");
-    mOffCandidates.CheckRtcpCandidates(
+    mOffCandidates->CheckRtcpCandidates(
         types[i] != SdpMediaSection::kApplication,
         remoteOffer->GetMediaSection(i), i,
         "Remote reoffer after trickle should have RTCP candidates.");
-    mOffCandidates.CheckDefaultRtcpCandidate(
+    mOffCandidates->CheckDefaultRtcpCandidate(
         false, remoteOffer->GetMediaSection(i), i,
         "Remote reoffer should not have a default RTCP candidate.");
     CheckEndOfCandidates(false, remoteOffer->GetMediaSection(i),
         "Remote reoffer should not have an end-of-candidates.");
   }
 
   answer = CreateAnswer();
   SetLocalAnswer(answer);
   SetRemoteAnswer(answer);
   // No candidates should be gathered at the answerer, but default candidates
   // should be set.
-  mAnsCandidates.FinishGathering(mSessionAns);
-
-  UniquePtr<Sdp> localAnswer(Parse(mSessionAns.GetLocalDescription()));
+  mAnsCandidates->FinishGathering(*mSessionAns);
+
+  UniquePtr<Sdp> localAnswer(Parse(mSessionAns->GetLocalDescription()));
   for (size_t i = 0; i < localAnswer->GetMediaSectionCount(); ++i) {
-    mAnsCandidates.CheckRtpCandidates(
+    mAnsCandidates->CheckRtpCandidates(
         i == 0, localAnswer->GetMediaSection(i), i,
         "Local reanswer after gathering should have RTP candidates on level "
         "0.");
-    mAnsCandidates.CheckDefaultRtpCandidate(
+    mAnsCandidates->CheckDefaultRtpCandidate(
         true, localAnswer->GetMediaSection(i), 0,
         "Local reanswer after gathering should have a default RTP candidate "
         "on all levels that matches transport level 0.");
-    mAnsCandidates.CheckRtcpCandidates(
+    mAnsCandidates->CheckRtcpCandidates(
         false, localAnswer->GetMediaSection(i), i,
         "Local reanswer after gathering should not have RTCP candidates "
         "(because we're reanswering with rtcp-mux)");
-    mAnsCandidates.CheckDefaultRtcpCandidate(
+    mAnsCandidates->CheckDefaultRtcpCandidate(
         false, localAnswer->GetMediaSection(i), i,
         "Local reanswer after gathering should not have a default RTCP "
         "candidate (because we're reanswering with rtcp-mux)");
     CheckEndOfCandidates(i == 0, localAnswer->GetMediaSection(i),
         "Local reanswer after gathering should have an end-of-candidates only "
         "for level 0.");
   }
 
-  UniquePtr<Sdp> remoteAnswer(Parse(mSessionOff.GetRemoteDescription()));
+  UniquePtr<Sdp> remoteAnswer(Parse(mSessionOff->GetRemoteDescription()));
   for (size_t i = 0; i < localAnswer->GetMediaSectionCount(); ++i) {
-    mAnsCandidates.CheckRtpCandidates(
+    mAnsCandidates->CheckRtpCandidates(
         i == 0, remoteAnswer->GetMediaSection(i), i,
         "Remote reanswer after trickle should have RTP candidates on level 0.");
-    mAnsCandidates.CheckDefaultRtpCandidate(
+    mAnsCandidates->CheckDefaultRtpCandidate(
         i == 0, remoteAnswer->GetMediaSection(i), i,
         "Remote reanswer should have a default RTP candidate on level 0 "
         "(because it was gathered last offer/answer).");
-    mAnsCandidates.CheckRtcpCandidates(
+    mAnsCandidates->CheckRtcpCandidates(
         false, remoteAnswer->GetMediaSection(i), i,
         "Remote reanswer after trickle should not have RTCP candidates "
         "(because we're reanswering with rtcp-mux)");
-    mAnsCandidates.CheckDefaultRtcpCandidate(
+    mAnsCandidates->CheckDefaultRtcpCandidate(
         false, remoteAnswer->GetMediaSection(i), i,
         "Remote reanswer after trickle should not have a default RTCP "
         "candidate.");
     CheckEndOfCandidates(false, remoteAnswer->GetMediaSection(i),
         "Remote reanswer after trickle should not have an end-of-candidates.");
   }
 }
 
 TEST_P(JsepSessionTest, RenegotiationAnswererSendonly)
 {
-  AddTracks(mSessionOff);
-  AddTracks(mSessionAns);
+  AddTracks(*mSessionOff);
+  AddTracks(*mSessionAns);
   OfferAnswer();
 
   std::string offer = CreateOffer();
   SetLocalOffer(offer);
   SetRemoteOffer(offer);
   std::string answer = CreateAnswer();
   SetLocalAnswer(answer);
 
@@ -2643,29 +2791,29 @@ TEST_P(JsepSessionTest, RenegotiationAns
       msection.SetReceiving(false);
     }
   }
 
   answer = parsedAnswer->ToString();
 
   SetRemoteAnswer(answer);
 
-  for (const RefPtr<JsepTrack>& track : mSessionOff.GetLocalTracks()) {
+  for (const RefPtr<JsepTrack>& track : mSessionOff->GetLocalTracks()) {
     if (track->GetMediaType() != SdpMediaSection::kApplication) {
       ASSERT_FALSE(track->GetActive());
     }
   }
 
-  ASSERT_EQ(types.size(), mSessionOff.GetNegotiatedTrackPairs().size());
+  ASSERT_EQ(types.size(), mSessionOff->GetNegotiatedTrackPairs().size());
 }
 
 TEST_P(JsepSessionTest, RenegotiationAnswererInactive)
 {
-  AddTracks(mSessionOff);
-  AddTracks(mSessionAns);
+  AddTracks(*mSessionOff);
+  AddTracks(*mSessionAns);
   OfferAnswer();
 
   std::string offer = CreateOffer();
   SetLocalOffer(offer);
   SetRemoteOffer(offer);
   std::string answer = CreateAnswer();
   SetLocalAnswer(answer);
 
@@ -2677,23 +2825,23 @@ TEST_P(JsepSessionTest, RenegotiationAns
       msection.SetSending(false);
     }
   }
 
   answer = parsedAnswer->ToString();
 
   SetRemoteAnswer(answer, CHECK_SUCCESS); // Won't have answerer tracks
 
-  for (const RefPtr<JsepTrack>& track : mSessionOff.GetLocalTracks()) {
+  for (const RefPtr<JsepTrack>& track : mSessionOff->GetLocalTracks()) {
     if (track->GetMediaType() != SdpMediaSection::kApplication) {
       ASSERT_FALSE(track->GetActive());
     }
   }
 
-  ASSERT_EQ(types.size(), mSessionOff.GetNegotiatedTrackPairs().size());
+  ASSERT_EQ(types.size(), mSessionOff->GetNegotiatedTrackPairs().size());
 }
 
 
 INSTANTIATE_TEST_CASE_P(
     Variants,
     JsepSessionTest,
     ::testing::Values("audio",
                       "video",
@@ -2756,17 +2904,17 @@ TEST_F(JsepSessionTest, OfferAnswerRecvO
       SdpAttribute::kRtcpMuxAttribute));
   ASSERT_TRUE(parsedOffer->GetMediaSection(1).GetAttributeList().HasAttribute(
       SdpAttribute::kRtcpMuxAttribute));
   ASSERT_TRUE(parsedOffer->GetMediaSection(2).GetAttributeList().HasAttribute(
       SdpAttribute::kRtcpMuxAttribute));
 
   SetLocalOffer(offer, CHECK_SUCCESS);
 
-  AddTracks(mSessionAns, "audio,video");
+  AddTracks(*mSessionAns, "audio,video");
   SetRemoteOffer(offer, CHECK_SUCCESS);
 
   std::string answer = CreateAnswer();
   UniquePtr<Sdp> parsedAnswer(Parse(answer));
 
   ASSERT_EQ(3U, parsedAnswer->GetMediaSectionCount());
   ASSERT_EQ(SdpMediaSection::kAudio,
             parsedAnswer->GetMediaSection(0).GetMediaType());
@@ -2779,29 +2927,29 @@ TEST_F(JsepSessionTest, OfferAnswerRecvO
   ASSERT_EQ(SdpMediaSection::kVideo,
             parsedAnswer->GetMediaSection(2).GetMediaType());
   ASSERT_EQ(SdpDirectionAttribute::kInactive,
             parsedAnswer->GetMediaSection(2).GetAttributeList().GetDirection());
 
   SetLocalAnswer(answer, CHECK_SUCCESS);
   SetRemoteAnswer(answer, CHECK_SUCCESS);
 
-  std::vector<JsepTrackPair> trackPairs(mSessionOff.GetNegotiatedTrackPairs());
+  std::vector<JsepTrackPair> trackPairs(mSessionOff->GetNegotiatedTrackPairs());
   ASSERT_EQ(2U, trackPairs.size());
   for (auto pair : trackPairs) {
     auto ssrcs = parsedOffer->GetMediaSection(pair.mLevel).GetAttributeList()
                  .GetSsrc().mSsrcs;
     ASSERT_EQ(1U, ssrcs.size());
     ASSERT_EQ(pair.mRecvonlySsrc, ssrcs.front().ssrc);
   }
 }
 
 TEST_F(JsepSessionTest, OfferAnswerSendOnlyLines)
 {
-  AddTracks(mSessionOff, "audio,video,video");
+  AddTracks(*mSessionOff, "audio,video,video");
 
   JsepOfferOptions options;
   options.mOfferToReceiveAudio = Some(static_cast<size_t>(0U));
   options.mOfferToReceiveVideo = Some(static_cast<size_t>(1U));
   options.mDontOfferDataChannel = Some(true);
   std::string offer = CreateOffer(Some(options));
 
   UniquePtr<Sdp> outputSdp(Parse(offer));
@@ -2825,17 +2973,17 @@ TEST_F(JsepSessionTest, OfferAnswerSendO
       SdpAttribute::kRtcpMuxAttribute));
   ASSERT_TRUE(outputSdp->GetMediaSection(1).GetAttributeList().HasAttribute(
       SdpAttribute::kRtcpMuxAttribute));
   ASSERT_TRUE(outputSdp->GetMediaSection(2).GetAttributeList().HasAttribute(
       SdpAttribute::kRtcpMuxAttribute));
 
   SetLocalOffer(offer, CHECK_SUCCESS);
 
-  AddTracks(mSessionAns, "audio,video");
+  AddTracks(*mSessionAns, "audio,video");
   SetRemoteOffer(offer, CHECK_SUCCESS);
 
   std::string answer = CreateAnswer();
   outputSdp = Parse(answer);
 
   ASSERT_EQ(3U, outputSdp->GetMediaSectionCount());
   ASSERT_EQ(SdpMediaSection::kAudio,
             outputSdp->GetMediaSection(0).GetMediaType());
@@ -2853,66 +3001,66 @@ TEST_F(JsepSessionTest, OfferAnswerSendO
 
 TEST_F(JsepSessionTest, OfferToReceiveAudioNotUsed)
 {
   JsepOfferOptions options;
   options.mOfferToReceiveAudio = Some<size_t>(1);
 
   OfferAnswer(CHECK_SUCCESS, Some(options));
 
-  UniquePtr<Sdp> offer(Parse(mSessionOff.GetLocalDescription()));
+  UniquePtr<Sdp> offer(Parse(mSessionOff->GetLocalDescription()));
   ASSERT_TRUE(offer.get());
   ASSERT_EQ(1U, offer->GetMediaSectionCount());
   ASSERT_EQ(SdpMediaSection::kAudio,
             offer->GetMediaSection(0).GetMediaType());
   ASSERT_EQ(SdpDirectionAttribute::kRecvonly,
             offer->GetMediaSection(0).GetAttributeList().GetDirection());
 
-  UniquePtr<Sdp> answer(Parse(mSessionAns.GetLocalDescription()));
+  UniquePtr<Sdp> answer(Parse(mSessionAns->GetLocalDescription()));
   ASSERT_TRUE(answer.get());
   ASSERT_EQ(1U, answer->GetMediaSectionCount());
   ASSERT_EQ(SdpMediaSection::kAudio,
             answer->GetMediaSection(0).GetMediaType());
   ASSERT_EQ(SdpDirectionAttribute::kInactive,
             answer->GetMediaSection(0).GetAttributeList().GetDirection());
 }
 
 TEST_F(JsepSessionTest, OfferToReceiveVideoNotUsed)
 {
   JsepOfferOptions options;
   options.mOfferToReceiveVideo = Some<size_t>(1);
 
   OfferAnswer(CHECK_SUCCESS, Some(options));
 
-  UniquePtr<Sdp> offer(Parse(mSessionOff.GetLocalDescription()));
+  UniquePtr<Sdp> offer(Parse(mSessionOff->GetLocalDescription()));
   ASSERT_TRUE(offer.get());
   ASSERT_EQ(1U, offer->GetMediaSectionCount());
   ASSERT_EQ(SdpMediaSection::kVideo,
             offer->GetMediaSection(0).GetMediaType());
   ASSERT_EQ(SdpDirectionAttribute::kRecvonly,
             offer->GetMediaSection(0).GetAttributeList().GetDirection());
 
-  UniquePtr<Sdp> answer(Parse(mSessionAns.GetLocalDescription()));
+  UniquePtr<Sdp> answer(Parse(mSessionAns->GetLocalDescription()));
   ASSERT_TRUE(answer.get());
   ASSERT_EQ(1U, answer->GetMediaSectionCount());
   ASSERT_EQ(SdpMediaSection::kVideo,
             answer->GetMediaSection(0).GetMediaType());
   ASSERT_EQ(SdpDirectionAttribute::kInactive,
             answer->GetMediaSection(0).GetAttributeList().GetDirection());
 }
 
 TEST_F(JsepSessionTest, CreateOfferNoDatachannelDefault)
 {
   RefPtr<JsepTrack> msta(
       new JsepTrack(SdpMediaSection::kAudio, "offerer_stream", "a1"));
-  mSessionOff.AddTrack(msta);
+  mSessionOff->AddTrack(msta);
 
   RefPtr<JsepTrack> mstv1(
       new JsepTrack(SdpMediaSection::kVideo, "offerer_stream", "v1"));
-  mSessionOff.AddTrack(mstv1);
+  mSessionOff->AddTrack(mstv1);
 
   std::string offer = CreateOffer();
 
   UniquePtr<Sdp> outputSdp(Parse(offer));
   ASSERT_TRUE(!!outputSdp);
 
   ASSERT_EQ(2U, outputSdp->GetMediaSectionCount());
   ASSERT_EQ(SdpMediaSection::kAudio,
@@ -2923,20 +3071,20 @@ TEST_F(JsepSessionTest, CreateOfferNoDat
 
 TEST_F(JsepSessionTest, ValidateOfferedVideoCodecParams)
 {
   types.push_back(SdpMediaSection::kAudio);
   types.push_back(SdpMediaSection::kVideo);
 
   RefPtr<JsepTrack> msta(
       new JsepTrack(SdpMediaSection::kAudio, "offerer_stream", "a1"));
-  mSessionOff.AddTrack(msta);
+  mSessionOff->AddTrack(msta);
   RefPtr<JsepTrack> mstv1(
       new JsepTrack(SdpMediaSection::kVideo, "offerer_stream", "v2"));
-  mSessionOff.AddTrack(mstv1);
+  mSessionOff->AddTrack(mstv1);
 
   std::string offer = CreateOffer();
 
   UniquePtr<Sdp> outputSdp(Parse(offer));
   ASSERT_TRUE(!!outputSdp);
 
   ASSERT_EQ(2U, outputSdp->GetMediaSectionCount());
   auto& video_section = outputSdp->GetMediaSection(1);
@@ -3050,20 +3198,20 @@ TEST_F(JsepSessionTest, ValidateOfferedV
 
 TEST_F(JsepSessionTest, ValidateOfferedAudioCodecParams)
 {
   types.push_back(SdpMediaSection::kAudio);
   types.push_back(SdpMediaSection::kVideo);
 
   RefPtr<JsepTrack> msta(
       new JsepTrack(SdpMediaSection::kAudio, "offerer_stream", "a1"));
-  mSessionOff.AddTrack(msta);
+  mSessionOff->AddTrack(msta);
   RefPtr<JsepTrack> mstv1(
       new JsepTrack(SdpMediaSection::kVideo, "offerer_stream", "v2"));
-  mSessionOff.AddTrack(mstv1);
+  mSessionOff->AddTrack(mstv1);
 
   std::string offer = CreateOffer();
 
   UniquePtr<Sdp> outputSdp(Parse(offer));
   ASSERT_TRUE(!!outputSdp);
 
   ASSERT_EQ(2U, outputSdp->GetMediaSectionCount());
   auto& audio_section = outputSdp->GetMediaSection(0);
@@ -3132,37 +3280,37 @@ TEST_F(JsepSessionTest, ValidateOfferedA
 
 TEST_F(JsepSessionTest, ValidateNoFmtpLineForRedInOfferAndAnswer)
 {
   types.push_back(SdpMediaSection::kAudio);
   types.push_back(SdpMediaSection::kVideo);
 
   RefPtr<JsepTrack> msta(
       new JsepTrack(SdpMediaSection::kAudio, "offerer_stream", "a1"));
-  mSessionOff.AddTrack(msta);
+  mSessionOff->AddTrack(msta);
   RefPtr<JsepTrack> mstv1(
       new JsepTrack(SdpMediaSection::kVideo, "offerer_stream", "v1"));
-  mSessionOff.AddTrack(mstv1);
+  mSessionOff->AddTrack(mstv1);
 
   std::string offer = CreateOffer();
 
   // look for line with fmtp:122 and remove it
   size_t start = offer.find("a=fmtp:122");
   size_t end = offer.find("\r\n", start);
   offer.replace(start, end+2-start, "");
 
   SetLocalOffer(offer);
   SetRemoteOffer(offer);
 
   RefPtr<JsepTrack> msta_ans(
       new JsepTrack(SdpMediaSection::kAudio, "answerer_stream", "a1"));
-  mSessionAns.AddTrack(msta);
+  mSessionAns->AddTrack(msta);
   RefPtr<JsepTrack> mstv1_ans(
       new JsepTrack(SdpMediaSection::kVideo, "answerer_stream", "v1"));
-  mSessionAns.AddTrack(mstv1);
+  mSessionAns->AddTrack(mstv1);
 
   std::string answer = CreateAnswer();
   // because parsing will throw out the malformed fmtp, make sure it is not
   // in the answer sdp string
   ASSERT_EQ(std::string::npos, answer.find("a=fmtp:122"));
 
   UniquePtr<Sdp> outputSdp(Parse(answer));
   ASSERT_TRUE(!!outputSdp);
@@ -3199,30 +3347,30 @@ TEST_F(JsepSessionTest, ValidateNoFmtpLi
   ASSERT_EQ("126", fmtps[0].format);
   ASSERT_EQ("97", fmtps[1].format);
   ASSERT_EQ("120", fmtps[2].format);
   ASSERT_EQ("121", fmtps[3].format);
 
   SetLocalAnswer(answer);
   SetRemoteAnswer(answer);
 
-  auto offerPairs = mSessionOff.GetNegotiatedTrackPairs();
+  auto offerPairs = mSessionOff->GetNegotiatedTrackPairs();
   ASSERT_EQ(2U, offerPairs.size());
   ASSERT_TRUE(offerPairs[1].mSending);
   ASSERT_TRUE(offerPairs[1].mReceiving);
   ASSERT_TRUE(offerPairs[1].mSending->GetNegotiatedDetails());
   ASSERT_TRUE(offerPairs[1].mReceiving->GetNegotiatedDetails());
   ASSERT_EQ(6U,
       offerPairs[1].mSending->GetNegotiatedDetails()->GetEncoding(0)
       .GetCodecs().size());
   ASSERT_EQ(6U,
       offerPairs[1].mReceiving->GetNegotiatedDetails()->GetEncoding(0)
       .GetCodecs().size());
 
-  auto answerPairs = mSessionAns.GetNegotiatedTrackPairs();
+  auto answerPairs = mSessionAns->GetNegotiatedTrackPairs();
   ASSERT_EQ(2U, answerPairs.size());
   ASSERT_TRUE(answerPairs[1].mSending);
   ASSERT_TRUE(answerPairs[1].mReceiving);
   ASSERT_TRUE(answerPairs[1].mSending->GetNegotiatedDetails());
   ASSERT_TRUE(answerPairs[1].mReceiving->GetNegotiatedDetails());
   ASSERT_EQ(6U,
       answerPairs[1].mSending->GetNegotiatedDetails()->GetEncoding(0)
       .GetCodecs().size());
@@ -3232,18 +3380,18 @@ TEST_F(JsepSessionTest, ValidateNoFmtpLi
 }
 
 TEST_F(JsepSessionTest, ValidateAnsweredCodecParams)
 {
   // TODO(bug 1099351): Once fixed, we can allow red in this offer,
   // which will also cause multiple codecs in answer.  For now,
   // red/ulpfec for video are behind a pref to mitigate potential for
   // errors.
-  SetCodecEnabled(mSessionOff, "red", false);
-  for (auto i = mSessionAns.Codecs().begin(); i != mSessionAns.Codecs().end();
+  SetCodecEnabled(*mSessionOff, "red", false);
+  for (auto i = mSessionAns->Codecs().begin(); i != mSessionAns->Codecs().end();
        ++i) {
     auto* codec = *i;
     if (codec->mName == "H264") {
       JsepVideoCodecDescription* h264 =
           static_cast<JsepVideoCodecDescription*>(codec);
       h264->mProfileLevelId = 0x42a00d;
       // Switch up the pts
       if (h264->mDefaultPt == "126") {
@@ -3254,31 +3402,31 @@ TEST_F(JsepSessionTest, ValidateAnswered
     }
   }
 
   types.push_back(SdpMediaSection::kAudio);
   types.push_back(SdpMediaSection::kVideo);
 
   RefPtr<JsepTrack> msta(
       new JsepTrack(SdpMediaSection::kAudio, "offerer_stream", "a1"));
-  mSessionOff.AddTrack(msta);
+  mSessionOff->AddTrack(msta);
   RefPtr<JsepTrack> mstv1(
       new JsepTrack(SdpMediaSection::kVideo, "offerer_stream", "v1"));
-  mSessionOff.AddTrack(mstv1);
+  mSessionOff->AddTrack(mstv1);
 
   std::string offer = CreateOffer();
   SetLocalOffer(offer);
   SetRemoteOffer(offer);
 
   RefPtr<JsepTrack> msta_ans(
       new JsepTrack(SdpMediaSection::kAudio, "answerer_stream", "a1"));
-  mSessionAns.AddTrack(msta);
+  mSessionAns->AddTrack(msta);
   RefPtr<JsepTrack> mstv1_ans(
       new JsepTrack(SdpMediaSection::kVideo, "answerer_stream", "v1"));
-  mSessionAns.AddTrack(mstv1);
+  mSessionAns->AddTrack(mstv1);
 
   std::string answer = CreateAnswer();
 
   UniquePtr<Sdp> outputSdp(Parse(answer));
   ASSERT_TRUE(!!outputSdp);
 
   ASSERT_EQ(2U, outputSdp->GetMediaSectionCount());
   auto& video_section = outputSdp->GetMediaSection(1);
@@ -3330,30 +3478,30 @@ TEST_F(JsepSessionTest, ValidateAnswered
 
   ASSERT_EQ((uint32_t)12288, parsed_vp8_params.max_fs);
   ASSERT_EQ((uint32_t)60, parsed_vp8_params.max_fr);
 
 
   SetLocalAnswer(answer);
   SetRemoteAnswer(answer);
 
-  auto offerPairs = mSessionOff.GetNegotiatedTrackPairs();
+  auto offerPairs = mSessionOff->GetNegotiatedTrackPairs();
   ASSERT_EQ(2U, offerPairs.size());
   ASSERT_TRUE(offerPairs[1].mSending);
   ASSERT_TRUE(offerPairs[1].mReceiving);
   ASSERT_TRUE(offerPairs[1].mSending->GetNegotiatedDetails());
   ASSERT_TRUE(offerPairs[1].mReceiving->GetNegotiatedDetails());
   ASSERT_EQ(1U,
       offerPairs[1].mSending->GetNegotiatedDetails()->GetEncoding(0)
       .GetCodecs().size());
   ASSERT_EQ(1U,
       offerPairs[1].mReceiving->GetNegotiatedDetails()->GetEncoding(0)
       .GetCodecs().size());
 
-  auto answerPairs = mSessionAns.GetNegotiatedTrackPairs();
+  auto answerPairs = mSessionAns->GetNegotiatedTrackPairs();
   ASSERT_EQ(2U, answerPairs.size());
   ASSERT_TRUE(answerPairs[1].mSending);
   ASSERT_TRUE(answerPairs[1].mReceiving);
   ASSERT_TRUE(answerPairs[1].mSending->GetNegotiatedDetails());
   ASSERT_TRUE(answerPairs[1].mReceiving->GetNegotiatedDetails());
   ASSERT_EQ(1U,
       answerPairs[1].mSending->GetNegotiatedDetails()->GetEncoding(0)
       .GetCodecs().size());
@@ -3444,155 +3592,155 @@ ForceH264(JsepSession& session, uint32_t
     } else {
       codec->mEnabled = false;
     }
   }
 }
 
 TEST_F(JsepSessionTest, TestH264Negotiation)
 {
-  ForceH264(mSessionOff, 0x42e00b);
-  ForceH264(mSessionAns, 0x42e00d);
-
-  AddTracks(mSessionOff, "video");
-  AddTracks(mSessionAns, "video");
+  ForceH264(*mSessionOff, 0x42e00b);
+  ForceH264(*mSessionAns, 0x42e00d);
+
+  AddTracks(*mSessionOff, "video");
+  AddTracks(*mSessionAns, "video");
 
   std::string offer(CreateOffer());
   SetLocalOffer(offer, CHECK_SUCCESS);
 
   SetRemoteOffer(offer, CHECK_SUCCESS);
   std::string answer(CreateAnswer());
 
   SetRemoteAnswer(answer, CHECK_SUCCESS);
   SetLocalAnswer(answer, CHECK_SUCCESS);
 
   const JsepCodecDescription* offererSendCodec;
-  GetCodec(mSessionOff, 0, sdp::kSend, 0, 0, &offererSendCodec);
+  GetCodec(*mSessionOff, 0, sdp::kSend, 0, 0, &offererSendCodec);
   ASSERT_TRUE(offererSendCodec);
   ASSERT_EQ("H264", offererSendCodec->mName);
   const JsepVideoCodecDescription* offererVideoSendCodec(
       static_cast<const JsepVideoCodecDescription*>(offererSendCodec));
   ASSERT_EQ((uint32_t)0x42e00d, offererVideoSendCodec->mProfileLevelId);
 
   const JsepCodecDescription* offererRecvCodec;
-  GetCodec(mSessionOff, 0, sdp::kRecv, 0, 0, &offererRecvCodec);
+  GetCodec(*mSessionOff, 0, sdp::kRecv, 0, 0, &offererRecvCodec);
   ASSERT_EQ("H264", offererRecvCodec->mName);
   const JsepVideoCodecDescription* offererVideoRecvCodec(
       static_cast<const JsepVideoCodecDescription*>(offererRecvCodec));
   ASSERT_EQ((uint32_t)0x42e00b, offererVideoRecvCodec->mProfileLevelId);
 
   const JsepCodecDescription* answererSendCodec;
-  GetCodec(mSessionAns, 0, sdp::kSend, 0, 0, &answererSendCodec);
+  GetCodec(*mSessionAns, 0, sdp::kSend, 0, 0, &answererSendCodec);
   ASSERT_TRUE(answererSendCodec);
   ASSERT_EQ("H264", answererSendCodec->mName);
   const JsepVideoCodecDescription* answererVideoSendCodec(
       static_cast<const JsepVideoCodecDescription*>(answererSendCodec));
   ASSERT_EQ((uint32_t)0x42e00b, answererVideoSendCodec->mProfileLevelId);
 
   const JsepCodecDescription* answererRecvCodec;
-  GetCodec(mSessionAns, 0, sdp::kRecv, 0, 0, &answererRecvCodec);
+  GetCodec(*mSessionAns, 0, sdp::kRecv, 0, 0, &answererRecvCodec);
   ASSERT_EQ("H264", answererRecvCodec->mName);
   const JsepVideoCodecDescription* answererVideoRecvCodec(
       static_cast<const JsepVideoCodecDescription*>(answererRecvCodec));
   ASSERT_EQ((uint32_t)0x42e00d, answererVideoRecvCodec->mProfileLevelId);
 }
 
 TEST_F(JsepSessionTest, TestH264NegotiationFails)
 {
-  ForceH264(mSessionOff, 0x42000b);
-  ForceH264(mSessionAns, 0x42e00d);
-
-  AddTracks(mSessionOff, "video");
-  AddTracks(mSessionAns, "video");
+  ForceH264(*mSessionOff, 0x42000b);
+  ForceH264(*mSessionAns, 0x42e00d);
+
+  AddTracks(*mSessionOff, "video");
+  AddTracks(*mSessionAns, "video");
 
   std::string offer(CreateOffer());
   SetLocalOffer(offer, CHECK_SUCCESS);
 
   SetRemoteOffer(offer, CHECK_SUCCESS);
   std::string answer(CreateAnswer());
 
   SetRemoteAnswer(answer, CHECK_SUCCESS);
   SetLocalAnswer(answer, CHECK_SUCCESS);
 
-  ASSERT_EQ(0U, mSessionOff.GetNegotiatedTrackPairs().size());
-  ASSERT_EQ(0U, mSessionAns.GetNegotiatedTrackPairs().size());
+  ASSERT_EQ(0U, mSessionOff->GetNegotiatedTrackPairs().size());
+  ASSERT_EQ(0U, mSessionAns->GetNegotiatedTrackPairs().size());
 }
 
 TEST_F(JsepSessionTest, TestH264NegotiationOffererDefault)
 {
-  ForceH264(mSessionOff, 0x42000d);
-  ForceH264(mSessionAns, 0x42000d);
-
-  AddTracks(mSessionOff, "video");
-  AddTracks(mSessionAns, "video");
+  ForceH264(*mSessionOff, 0x42000d);
+  ForceH264(*mSessionAns, 0x42000d);
+
+  AddTracks(*mSessionOff, "video");
+  AddTracks(*mSessionAns, "video");
 
   std::string offer(CreateOffer());
   SetLocalOffer(offer, CHECK_SUCCESS);
 
   Replace("profile-level-id=42000d",
           "some-unknown-param=0",
           &offer);
 
   SetRemoteOffer(offer, CHECK_SUCCESS);
   std::string answer(CreateAnswer());
 
   SetRemoteAnswer(answer, CHECK_SUCCESS);
   SetLocalAnswer(answer, CHECK_SUCCESS);
 
   const JsepCodecDescription* answererSendCodec;
-  GetCodec(mSessionAns, 0, sdp::kSend, 0, 0, &answererSendCodec);
+  GetCodec(*mSessionAns, 0, sdp::kSend, 0, 0, &answererSendCodec);
   ASSERT_TRUE(answererSendCodec);
   ASSERT_EQ("H264", answererSendCodec->mName);
   const JsepVideoCodecDescription* answererVideoSendCodec(
       static_cast<const JsepVideoCodecDescription*>(answererSendCodec));
   ASSERT_EQ((uint32_t)0x420010, answererVideoSendCodec->mProfileLevelId);
 }
 
 TEST_F(JsepSessionTest, TestH264NegotiationOffererNoFmtp)
 {
-  ForceH264(mSessionOff, 0x42000d);
-  ForceH264(mSessionAns, 0x42001e);
-
-  AddTracks(mSessionOff, "video");
-  AddTracks(mSessionAns, "video");
+  ForceH264(*mSessionOff, 0x42000d);
+  ForceH264(*mSessionAns, 0x42001e);
+
+  AddTracks(*mSessionOff, "video");
+  AddTracks(*mSessionAns, "video");
 
   std::string offer(CreateOffer());
   SetLocalOffer(offer, CHECK_SUCCESS);
 
   Replace("a=fmtp", "a=oops", &offer);
 
   SetRemoteOffer(offer, CHECK_SUCCESS);
   std::string answer(CreateAnswer());
 
   SetRemoteAnswer(answer, CHECK_SUCCESS);
   SetLocalAnswer(answer, CHECK_SUCCESS);
 
   const JsepCodecDescription* answererSendCodec;
-  GetCodec(mSessionAns, 0, sdp::kSend, 0, 0, &answererSendCodec);
+  GetCodec(*mSessionAns, 0, sdp::kSend, 0, 0, &answererSendCodec);
   ASSERT_TRUE(answererSendCodec);
   ASSERT_EQ("H264", answererSendCodec->mName);
   const JsepVideoCodecDescription* answererVideoSendCodec(
       static_cast<const JsepVideoCodecDescription*>(answererSendCodec));
   ASSERT_EQ((uint32_t)0x420010, answererVideoSendCodec->mProfileLevelId);
 
   const JsepCodecDescription* answererRecvCodec;
-  GetCodec(mSessionAns, 0, sdp::kRecv, 0, 0, &answererRecvCodec);
+  GetCodec(*mSessionAns, 0, sdp::kRecv, 0, 0, &answererRecvCodec);
   ASSERT_EQ("H264", answererRecvCodec->mName);
   const JsepVideoCodecDescription* answererVideoRecvCodec(
       static_cast<const JsepVideoCodecDescription*>(answererRecvCodec));
   ASSERT_EQ((uint32_t)0x420010, answererVideoRecvCodec->mProfileLevelId);
 }
 
 TEST_F(JsepSessionTest, TestH264LevelAsymmetryDisallowedByOffererWithLowLevel)
 {
-  ForceH264(mSessionOff, 0x42e00b);
-  ForceH264(mSessionAns, 0x42e00d);
-
-  AddTracks(mSessionOff, "video");
-  AddTracks(mSessionAns, "video");
+  ForceH264(*mSessionOff, 0x42e00b);
+  ForceH264(*mSessionAns, 0x42e00d);
+
+  AddTracks(*mSessionOff, "video");
+  AddTracks(*mSessionAns, "video");
 
   std::string offer(CreateOffer());
   SetLocalOffer(offer, CHECK_SUCCESS);
 
   Replace("level-asymmetry-allowed=1",
           "level-asymmetry-allowed=0",
           &offer);
 
@@ -3601,38 +3749,38 @@ TEST_F(JsepSessionTest, TestH264LevelAsy
 
   SetRemoteAnswer(answer, CHECK_SUCCESS);
   SetLocalAnswer(answer, CHECK_SUCCESS);
 
   // Offerer doesn't know about the shenanigans we've pulled here, so will
   // behave normally, and we test the normal behavior elsewhere.
 
   const JsepCodecDescription* answererSendCodec;
-  GetCodec(mSessionAns, 0, sdp::kSend, 0, 0, &answererSendCodec);
+  GetCodec(*mSessionAns, 0, sdp::kSend, 0, 0, &answererSendCodec);
   ASSERT_TRUE(answererSendCodec);
   ASSERT_EQ("H264", answererSendCodec->mName);
   const JsepVideoCodecDescription* answererVideoSendCodec(
       static_cast<const JsepVideoCodecDescription*>(answererSendCodec));
   ASSERT_EQ((uint32_t)0x42e00b, answererVideoSendCodec->mProfileLevelId);
 
   const JsepCodecDescription* answererRecvCodec;
-  GetCodec(mSessionAns, 0, sdp::kRecv, 0, 0, &answererRecvCodec);
+  GetCodec(*mSessionAns, 0, sdp::kRecv, 0, 0, &answererRecvCodec);
   ASSERT_EQ("H264", answererRecvCodec->mName);
   const JsepVideoCodecDescription* answererVideoRecvCodec(
       static_cast<const JsepVideoCodecDescription*>(answererRecvCodec));
   ASSERT_EQ((uint32_t)0x42e00b, answererVideoRecvCodec->mProfileLevelId);
 }
 
 TEST_F(JsepSessionTest, TestH264LevelAsymmetryDisallowedByOffererWithHighLevel)
 {
-  ForceH264(mSessionOff, 0x42e00d);
-  ForceH264(mSessionAns, 0x42e00b);
-
-  AddTracks(mSessionOff, "video");
-  AddTracks(mSessionAns, "video");
+  ForceH264(*mSessionOff, 0x42e00d);
+  ForceH264(*mSessionAns, 0x42e00b);
+
+  AddTracks(*mSessionOff, "video");
+  AddTracks(*mSessionAns, "video");
 
   std::string offer(CreateOffer());
   SetLocalOffer(offer, CHECK_SUCCESS);
 
   Replace("level-asymmetry-allowed=1",
           "level-asymmetry-allowed=0",
           &offer);
 
@@ -3641,101 +3789,101 @@ TEST_F(JsepSessionTest, TestH264LevelAsy
 
   SetRemoteAnswer(answer, CHECK_SUCCESS);
   SetLocalAnswer(answer, CHECK_SUCCESS);
 
   // Offerer doesn't know about the shenanigans we've pulled here, so will
   // behave normally, and we test the normal behavior elsewhere.
 
   const JsepCodecDescription* answererSendCodec;
-  GetCodec(mSessionAns, 0, sdp::kSend, 0, 0, &answererSendCodec);
+  GetCodec(*mSessionAns, 0, sdp::kSend, 0, 0, &answererSendCodec);
   ASSERT_TRUE(answererSendCodec);
   ASSERT_EQ("H264", answererSendCodec->mName);
   const JsepVideoCodecDescription* answererVideoSendCodec(
       static_cast<const JsepVideoCodecDescription*>(answererSendCodec));
   ASSERT_EQ((uint32_t)0x42e00b, answererVideoSendCodec->mProfileLevelId);
 
   const JsepCodecDescription* answererRecvCodec;
-  GetCodec(mSessionAns, 0, sdp::kRecv, 0, 0, &answererRecvCodec);
+  GetCodec(*mSessionAns, 0, sdp::kRecv, 0, 0, &answererRecvCodec);
   ASSERT_EQ("H264", answererRecvCodec->mName);
   const JsepVideoCodecDescription* answererVideoRecvCodec(
       static_cast<const JsepVideoCodecDescription*>(answererRecvCodec));
   ASSERT_EQ((uint32_t)0x42e00b, answererVideoRecvCodec->mProfileLevelId);
 }
 
 TEST_F(JsepSessionTest, TestH264LevelAsymmetryDisallowedByAnswererWithLowLevel)
 {
-  ForceH264(mSessionOff, 0x42e00d);
-  ForceH264(mSessionAns, 0x42e00b);
-
-  AddTracks(mSessionOff, "video");
-  AddTracks(mSessionAns, "video");
+  ForceH264(*mSessionOff, 0x42e00d);
+  ForceH264(*mSessionAns, 0x42e00b);
+
+  AddTracks(*mSessionOff, "video");
+  AddTracks(*mSessionAns, "video");
 
   std::string offer(CreateOffer());
   SetLocalOffer(offer, CHECK_SUCCESS);
   SetRemoteOffer(offer, CHECK_SUCCESS);
   std::string answer(CreateAnswer());
 
   Replace("level-asymmetry-allowed=1",
           "level-asymmetry-allowed=0",
           &answer);
 
   SetRemoteAnswer(answer, CHECK_SUCCESS);
   SetLocalAnswer(answer, CHECK_SUCCESS);
 
   const JsepCodecDescription* offererSendCodec;
-  GetCodec(mSessionOff, 0, sdp::kSend, 0, 0, &offererSendCodec);
+  GetCodec(*mSessionOff, 0, sdp::kSend, 0, 0, &offererSendCodec);
   ASSERT_TRUE(offererSendCodec);
   ASSERT_EQ("H264", offererSendCodec->mName);
   const JsepVideoCodecDescription* offererVideoSendCodec(
       static_cast<const JsepVideoCodecDescription*>(offererSendCodec));
   ASSERT_EQ((uint32_t)0x42e00b, offererVideoSendCodec->mProfileLevelId);
 
   const JsepCodecDescription* offererRecvCodec;
-  GetCodec(mSessionOff, 0, sdp::kRecv, 0, 0, &offererRecvCodec);
+  GetCodec(*mSessionOff, 0, sdp::kRecv, 0, 0, &offererRecvCodec);
   ASSERT_EQ("H264", offererRecvCodec->mName);
   const JsepVideoCodecDescription* offererVideoRecvCodec(
       static_cast<const JsepVideoCodecDescription*>(offererRecvCodec));
   ASSERT_EQ((uint32_t)0x42e00b, offererVideoRecvCodec->mProfileLevelId);
 
   // Answerer doesn't know we've pulled these shenanigans, it should act as if
   // it did not set level-asymmetry-required, and we already check that
   // elsewhere
 }
 
 TEST_F(JsepSessionTest, TestH264LevelAsymmetryDisallowedByAnswererWithHighLevel)
 {
-  ForceH264(mSessionOff, 0x42e00b);
-  ForceH264(mSessionAns, 0x42e00d);
-
-  AddTracks(mSessionOff, "video");
-  AddTracks(mSessionAns, "video");
+  ForceH264(*mSessionOff, 0x42e00b);
+  ForceH264(*mSessionAns, 0x42e00d);
+
+  AddTracks(*mSessionOff, "video");
+  AddTracks(*mSessionAns, "video");
 
   std::string offer(CreateOffer());
   SetLocalOffer(offer, CHECK_SUCCESS);
   SetRemoteOffer(offer, CHECK_SUCCESS);
   std::string answer(CreateAnswer());
 
   Replace("level-asymmetry-allowed=1",
           "level-asymmetry-allowed=0",
           &answer);
 
   SetRemoteAnswer(answer, CHECK_SUCCESS);
   SetLocalAnswer(answer, CHECK_SUCCESS);
 
   const JsepCodecDescription* offererSendCodec;
-  GetCodec(mSessionOff, 0, sdp::kSend, 0, 0, &offererSendCodec);
+  GetCodec(*mSessionOff, 0, sdp::kSend, 0, 0, &offererSendCodec);
   ASSERT_TRUE(offererSendCodec);
   ASSERT_EQ("H264", offererSendCodec->mName);
   const JsepVideoCodecDescription* offererVideoSendCodec(
       static_cast<const JsepVideoCodecDescription*>(offererSendCodec));
   ASSERT_EQ((uint32_t)0x42e00b, offererVideoSendCodec->mProfileLevelId);
 
   const JsepCodecDescription* offererRecvCodec;
-  GetCodec(mSessionOff, 0, sdp::kRecv, 0, 0, &offererRecvCodec);
+  GetCodec(*mSessionOff, 0, sdp::kRecv, 0, 0, &offererRecvCodec);
   ASSERT_EQ("H264", offererRecvCodec->mName);
   const JsepVideoCodecDescription* offererVideoRecvCodec(
       static_cast<const JsepVideoCodecDescription*>(offererRecvCodec));
   ASSERT_EQ((uint32_t)0x42e00b, offererVideoRecvCodec->mProfileLevelId);
 
   // Answerer doesn't know we've pulled these shenanigans, it should act as if
   // it did not set level-asymmetry-required, and we already check that
   // elsewhere
@@ -3759,22 +3907,22 @@ TEST_P(JsepSessionTest, TestRejectMline)
     case SdpMediaSection::kApplication:
       // Sabotage datachannel
       EnsureNegotiationFailure(types.front(), "webrtc-datachannel");
       break;
     default:
       ASSERT_TRUE(false) << "Unknown media type";
   }
 
-  AddTracks(mSessionOff);
-  AddTracks(mSessionAns);
+  AddTracks(*mSessionOff);
+  AddTracks(*mSessionAns);
 
   std::string offer = CreateOffer();
-  mSessionOff.SetLocalDescription(kJsepSdpOffer, offer);
-  mSessionAns.SetRemoteDescription(kJsepSdpOffer, offer);
+  mSessionOff->SetLocalDescription(kJsepSdpOffer, offer);
+  mSessionAns->SetRemoteDescription(kJsepSdpOffer, offer);
 
   std::string answer = CreateAnswer();
 
   UniquePtr<Sdp> outputSdp(Parse(answer));
   ASSERT_TRUE(!!outputSdp);
 
   ASSERT_NE(0U, outputSdp->GetMediaSectionCount());
   SdpMediaSection* failed_section = nullptr;
@@ -3785,88 +3933,88 @@ TEST_P(JsepSessionTest, TestRejectMline)
     }
   }
 
   ASSERT_TRUE(failed_section) << "Failed type was entirely absent from SDP";
   auto& failed_attrs = failed_section->GetAttributeList();
   ASSERT_EQ(SdpDirectionAttribute::kInactive, failed_attrs.GetDirection());
   ASSERT_EQ(0U, failed_section->GetPort());
 
-  mSessionAns.SetLocalDescription(kJsepSdpAnswer, answer);
-  mSessionOff.SetRemoteDescription(kJsepSdpAnswer, answer);
+  mSessionAns->SetLocalDescription(kJsepSdpAnswer, answer);
+  mSessionOff->SetRemoteDescription(kJsepSdpAnswer, answer);
 
   size_t numRejected = std::count(types.begin(), types.end(), types.front());
   size_t numAccepted = types.size() - numRejected;
 
-  ASSERT_EQ(numAccepted, mSessionOff.GetNegotiatedTrackPairs().size());
-  ASSERT_EQ(numAccepted, mSessionAns.GetNegotiatedTrackPairs().size());
-
-  ASSERT_EQ(types.size(), mSessionOff.GetTransports().size());
-  ASSERT_EQ(types.size(), mSessionOff.GetLocalTracks().size());
-  ASSERT_EQ(numAccepted, mSessionOff.GetRemoteTracks().size());
-
-  ASSERT_EQ(types.size(), mSessionAns.GetTransports().size());
-  ASSERT_EQ(types.size(), mSessionAns.GetLocalTracks().size());
-  ASSERT_EQ(types.size(), mSessionAns.GetRemoteTracks().size());
+  ASSERT_EQ(numAccepted, mSessionOff->GetNegotiatedTrackPairs().size());
+  ASSERT_EQ(numAccepted, mSessionAns->GetNegotiatedTrackPairs().size());
+
+  ASSERT_EQ(types.size(), mSessionOff->GetTransports().size());
+  ASSERT_EQ(types.size(), mSessionOff->GetLocalTracks().size());
+  ASSERT_EQ(numAccepted, mSessionOff->GetRemoteTracks().size());
+
+  ASSERT_EQ(types.size(), mSessionAns->GetTransports().size());
+  ASSERT_EQ(types.size(), mSessionAns->GetLocalTracks().size());
+  ASSERT_EQ(types.size(), mSessionAns->GetRemoteTracks().size());
 }
 
 TEST_F(JsepSessionTest, CreateOfferNoMlines)
 {
   JsepOfferOptions options;
   std::string offer;
-  nsresult rv = mSessionOff.CreateOffer(options, &offer);
+  nsresult rv = mSessionOff->CreateOffer(options, &offer);
   ASSERT_NE(NS_OK, rv);
-  ASSERT_NE("", mSessionOff.GetLastError());
+  ASSERT_NE("", mSessionOff->GetLastError());
 }
 
 TEST_F(JsepSessionTest, TestIceLite)
 {
-  AddTracks(mSessionOff, "audio");
-  AddTracks(mSessionAns, "audio");
+  AddTracks(*mSessionOff, "audio");
+  AddTracks(*mSessionAns, "audio");
   std::string offer = CreateOffer();
   SetLocalOffer(offer, CHECK_SUCCESS);
 
   UniquePtr<Sdp> parsedOffer(Parse(offer));
   parsedOffer->GetAttributeList().SetAttribute(
       new SdpFlagAttribute(SdpAttribute::kIceLiteAttribute));
 
   std::ostringstream os;
   parsedOffer->Serialize(os);
   SetRemoteOffer(os.str(), CHECK_SUCCESS);
 
-  ASSERT_TRUE(mSessionAns.RemoteIsIceLite());
-  ASSERT_FALSE(mSessionOff.RemoteIsIceLite());
+  ASSERT_TRUE(mSessionAns->RemoteIsIceLite());
+  ASSERT_FALSE(mSessionOff->RemoteIsIceLite());
 }
 
 TEST_F(JsepSessionTest, TestIceOptions)
 {
-  AddTracks(mSessionOff, "audio");
-  AddTracks(mSessionAns, "audio");
+  AddTracks(*mSessionOff, "audio");
+  AddTracks(*mSessionAns, "audio");
   std::string offer = CreateOffer();
   SetLocalOffer(offer, CHECK_SUCCESS);
   SetRemoteOffer(offer, CHECK_SUCCESS);
   std::string answer = CreateAnswer();
   SetLocalAnswer(answer, CHECK_SUCCESS);
   SetRemoteAnswer(answer, CHECK_SUCCESS);
 
-  ASSERT_EQ(1U, mSessionOff.GetIceOptions().size());
-  ASSERT_EQ("trickle", mSessionOff.GetIceOptions()[0]);
-
-  ASSERT_EQ(1U, mSessionAns.GetIceOptions().size());
-  ASSERT_EQ("trickle", mSessionAns.GetIceOptions()[0]);
+  ASSERT_EQ(1U, mSessionOff->GetIceOptions().size());
+  ASSERT_EQ("trickle", mSessionOff->GetIceOptions()[0]);
+
+  ASSERT_EQ(1U, mSessionAns->GetIceOptions().size());
+  ASSERT_EQ("trickle", mSessionAns->GetIceOptions()[0]);
 }
 
 TEST_F(JsepSessionTest, TestExtmap)
 {
-  AddTracks(mSessionOff, "audio");
-  AddTracks(mSessionAns, "audio");
+  AddTracks(*mSessionOff, "audio");
+  AddTracks(*mSessionAns, "audio");
   // ssrc-audio-level will be extmap 1 for both
-  mSessionOff.AddAudioRtpExtension("foo"); // Default mapping of 2
-  mSessionOff.AddAudioRtpExtension("bar"); // Default mapping of 3
-  mSessionAns.AddAudioRtpExtension("bar"); // Default mapping of 2
+  mSessionOff->AddAudioRtpExtension("foo"); // Default mapping of 2
+  mSessionOff->AddAudioRtpExtension("bar"); // Default mapping of 3
+  mSessionAns->AddAudioRtpExtension("bar"); // Default mapping of 2
   std::string offer = CreateOffer();
   SetLocalOffer(offer, CHECK_SUCCESS);
   SetRemoteOffer(offer, CHECK_SUCCESS);
   std::string answer = CreateAnswer();
   SetLocalAnswer(answer, CHECK_SUCCESS);
   SetRemoteAnswer(answer, CHECK_SUCCESS);
 
   UniquePtr<Sdp> parsedOffer(Parse(offer));
@@ -3893,62 +4041,62 @@ TEST_F(JsepSessionTest, TestExtmap)
   ASSERT_EQ(1U, answerExtmap.size());
   // We ensure that the entry for "bar" matches what was in the offer
   ASSERT_EQ("bar", answerExtmap[0].extensionname);
   ASSERT_EQ(3U, answerExtmap[0].entry);
 }
 
 TEST_F(JsepSessionTest, TestRtcpFbStar)
 {
-  AddTracks(mSessionOff, "video");
-  AddTracks(mSessionAns, "video");
+  AddTracks(*mSessionOff, "video");
+  AddTracks(*mSessionAns, "video");
 
   std::string offer = CreateOffer();
 
   UniquePtr<Sdp> parsedOffer(Parse(offer));
   auto* rtcpfbs = new SdpRtcpFbAttributeList;
   rtcpfbs->PushEntry("*", SdpRtcpFbAttributeList::kNack);
   parsedOffer->GetMediaSection(0).GetAttributeList().SetAttribute(rtcpfbs);
   offer = parsedOffer->ToString();
 
   SetLocalOffer(offer, CHECK_SUCCESS);
   SetRemoteOffer(offer, CHECK_SUCCESS);
   std::string answer = CreateAnswer();
   SetLocalAnswer(answer, CHECK_SUCCESS);
   SetRemoteAnswer(answer, CHECK_SUCCESS);
 
-  ASSERT_EQ(1U, mSessionAns.GetRemoteTracks().size());
-  RefPtr<JsepTrack> track = mSessionAns.GetRemoteTracks()[0];
+  ASSERT_EQ(1U, mSessionAns->GetRemoteTracks().size());
+  RefPtr<JsepTrack> track = mSessionAns->GetRemoteTracks()[0];
   ASSERT_TRUE(track->GetNegotiatedDetails());
   auto* details = track->GetNegotiatedDetails();
   for (const JsepCodecDescription* codec :
        details->GetEncoding(0).GetCodecs()) {
     const JsepVideoCodecDescription* videoCodec =
       static_cast<const JsepVideoCodecDescription*>(codec);
     ASSERT_EQ(1U, videoCodec->mNackFbTypes.size());
     ASSERT_EQ("", videoCodec->mNackFbTypes[0]);
   }
 }
 
 TEST_F(JsepSessionTest, TestUniquePayloadTypes)
 {
   // The audio payload types will all appear more than once, but the video
   // payload types will be unique.
-  AddTracks(mSessionOff, "audio,audio,video");
-  AddTracks(mSessionAns, "audio,audio,video");
+  AddTracks(*mSessionOff, "audio,audio,video");
+  AddTracks(*mSessionAns, "audio,audio,video");
 
   std::string offer = CreateOffer();
   SetLocalOffer(offer, CHECK_SUCCESS);
   SetRemoteOffer(offer, CHECK_SUCCESS);
   std::string answer = CreateAnswer();
   SetLocalAnswer(answer, CHECK_SUCCESS);
   SetRemoteAnswer(answer, CHECK_SUCCESS);
 
-  auto offerPairs = mSessionOff.GetNegotiatedTrackPairs();
-  auto answerPairs = mSessionAns.GetNegotiatedTrackPairs();
+  auto offerPairs = mSessionOff->GetNegotiatedTrackPairs();
+  auto answerPairs = mSessionAns->GetNegotiatedTrackPairs();
   ASSERT_EQ(3U, offerPairs.size());
   ASSERT_EQ(3U, answerPairs.size());
 
   ASSERT_TRUE(offerPairs[0].mReceiving);
   ASSERT_TRUE(offerPairs[0].mReceiving->GetNegotiatedDetails());
   ASSERT_EQ(0U,
       offerPairs[0].mReceiving->GetNegotiatedDetails()->
       GetUniquePayloadTypes().size());
@@ -3982,25 +4130,25 @@ TEST_F(JsepSessionTest, TestUniquePayloa
   ASSERT_NE(0U,
       answerPairs[2].mReceiving->GetNegotiatedDetails()->
       GetUniquePayloadTypes().size());
 }
 
 TEST_F(JsepSessionTest, UnknownFingerprintAlgorithm)
 {
   types.push_back(SdpMediaSection::kAudio);
-  AddTracks(mSessionOff, "audio");
-  AddTracks(mSessionAns, "audio");
+  AddTracks(*mSessionOff, "audio");
+  AddTracks(*mSessionAns, "audio");
 
   std::string offer(CreateOffer());
   SetLocalOffer(offer);
   ReplaceAll("fingerprint:sha", "fingerprint:foo", &offer);
-  nsresult rv = mSessionAns.SetRemoteDescription(kJsepSdpOffer, offer);
+  nsresult rv = mSessionAns->SetRemoteDescription(kJsepSdpOffer, offer);
   ASSERT_NE(NS_OK, rv);
-  ASSERT_NE("", mSessionAns.GetLastError());
+  ASSERT_NE("", mSessionAns->GetLastError());
 }
 
 TEST(H264ProfileLevelIdTest, TestLevelComparisons)
 {
   ASSERT_LT(JsepVideoCodecDescription::GetSaneH264Level(0x421D0B), // 1b
             JsepVideoCodecDescription::GetSaneH264Level(0x420D0B)); // 1.1
   ASSERT_LT(JsepVideoCodecDescription::GetSaneH264Level(0x420D0A), // 1.0
             JsepVideoCodecDescription::GetSaneH264Level(0x421D0B)); // 1b
@@ -4037,195 +4185,195 @@ TEST(H264ProfileLevelIdTest, TestLevelSe
   JsepVideoCodecDescription::SetSaneH264Level(
       JsepVideoCodecDescription::GetSaneH264Level(0x64000B),
       &profileLevelId);
   ASSERT_EQ((uint32_t)0x6E100B, profileLevelId);
 }
 
 TEST_F(JsepSessionTest, StronglyPreferredCodec)
 {
-  for (JsepCodecDescription* codec : mSessionAns.Codecs()) {
+  for (JsepCodecDescription* codec : mSessionAns->Codecs()) {
     if (codec->mName == "H264") {
       codec->mStronglyPreferred = true;
     }
   }
 
   types.push_back(SdpMediaSection::kVideo);
-  AddTracks(mSessionOff, "video");
-  AddTracks(mSessionAns, "video");
+  AddTracks(*mSessionOff, "video");
+  AddTracks(*mSessionAns, "video");
 
   OfferAnswer();
 
   const JsepCodecDescription* codec;
-  GetCodec(mSessionAns, 0, sdp::kSend, 0, 0, &codec);
+  GetCodec(*mSessionAns, 0, sdp::kSend, 0, 0, &codec);
   ASSERT_TRUE(codec);
   ASSERT_EQ("H264", codec->mName);
-  GetCodec(mSessionAns, 0, sdp::kRecv, 0, 0, &codec);
+  GetCodec(*mSessionAns, 0, sdp::kRecv, 0, 0, &codec);
   ASSERT_TRUE(codec);
   ASSERT_EQ("H264", codec->mName);
 }
 
 TEST_F(JsepSessionTest, LowDynamicPayloadType)
 {
-  SetPayloadTypeNumber(mSessionOff, "opus", "12");
+  SetPayloadTypeNumber(*mSessionOff, "opus", "12");
   types.push_back(SdpMediaSection::kAudio);
-  AddTracks(mSessionOff, "audio");
-  AddTracks(mSessionAns, "audio");
+  AddTracks(*mSessionOff, "audio");
+  AddTracks(*mSessionAns, "audio");
 
   OfferAnswer();
   const JsepCodecDescription* codec;
-  GetCodec(mSessionAns, 0, sdp::kSend, 0, 0, &codec);
+  GetCodec(*mSessionAns, 0, sdp::kSend, 0, 0, &codec);
   ASSERT_TRUE(codec);
   ASSERT_EQ("opus", codec->mName);
   ASSERT_EQ("12", codec->mDefaultPt);
-  GetCodec(mSessionAns, 0, sdp::kRecv, 0, 0, &codec);
+  GetCodec(*mSessionAns, 0, sdp::kRecv, 0, 0, &codec);
   ASSERT_TRUE(codec);
   ASSERT_EQ("opus", codec->mName);
   ASSERT_EQ("12", codec->mDefaultPt);
 }
 
 TEST_F(JsepSessionTest, PayloadTypeClash)
 {
   // Disable this so mSessionOff doesn't have a duplicate
-  SetCodecEnabled(mSessionOff, "PCMU", false);
-  SetPayloadTypeNumber(mSessionOff, "opus", "0");
-  SetPayloadTypeNumber(mSessionAns, "PCMU", "0");
+  SetCodecEnabled(*mSessionOff, "PCMU", false);
+  SetPayloadTypeNumber(*mSessionOff, "opus", "0");
+  SetPayloadTypeNumber(*mSessionAns, "PCMU", "0");
   types.push_back(SdpMediaSection::kAudio);
-  AddTracks(mSessionOff, "audio");
-  AddTracks(mSessionAns, "audio");
+  AddTracks(*mSessionOff, "audio");
+  AddTracks(*mSessionAns, "audio");
 
   OfferAnswer();
   const JsepCodecDescription* codec;
-  GetCodec(mSessionAns, 0, sdp::kSend, 0, 0, &codec);
+  GetCodec(*mSessionAns, 0, sdp::kSend, 0, 0, &codec);
   ASSERT_TRUE(codec);
   ASSERT_EQ("opus", codec->mName);
   ASSERT_EQ("0", codec->mDefaultPt);
-  GetCodec(mSessionAns, 0, sdp::kRecv, 0, 0, &codec);
+  GetCodec(*mSessionAns, 0, sdp::kRecv, 0, 0, &codec);
   ASSERT_TRUE(codec);
   ASSERT_EQ("opus", codec->mName);
   ASSERT_EQ("0", codec->mDefaultPt);
 
   // Now, make sure that mSessionAns does not put a=rtpmap:0 PCMU in a reoffer,
   // since pt 0 is taken for opus (the answerer still supports PCMU, and will
   // reoffer it, but it should choose a new payload type for it)
   JsepOfferOptions options;
   std::string reoffer;
-  nsresult rv = mSessionAns.CreateOffer(options, &reoffer);
+  nsresult rv = mSessionAns->CreateOffer(options, &reoffer);
   ASSERT_EQ(NS_OK, rv);
   ASSERT_EQ(std::string::npos, reoffer.find("a=rtpmap:0 PCMU")) << reoffer;
 }
 
 TEST_P(JsepSessionTest, TestGlareRollback)
 {
-  AddTracks(mSessionOff);
-  AddTracks(mSessionAns);
+  AddTracks(*mSessionOff);
+  AddTracks(*mSessionAns);
   JsepOfferOptions options;
 
   std::string offer;
-  ASSERT_EQ(NS_OK, mSessionAns.CreateOffer(options, &offer));
+  ASSERT_EQ(NS_OK, mSessionAns->CreateOffer(options, &offer));
   ASSERT_EQ(NS_OK,
-            mSessionAns.SetLocalDescription(kJsepSdpOffer, offer));
-  ASSERT_EQ(kJsepStateHaveLocalOffer, mSessionAns.GetState());
-
-  ASSERT_EQ(NS_OK, mSessionOff.CreateOffer(options, &offer));
+            mSessionAns->SetLocalDescription(kJsepSdpOffer, offer));
+  ASSERT_EQ(kJsepStateHaveLocalOffer, mSessionAns->GetState());
+
+  ASSERT_EQ(NS_OK, mSessionOff->CreateOffer(options, &offer));
   ASSERT_EQ(NS_OK,
-            mSessionOff.SetLocalDescription(kJsepSdpOffer, offer));
-  ASSERT_EQ(kJsepStateHaveLocalOffer, mSessionOff.GetState());
+            mSessionOff->SetLocalDescription(kJsepSdpOffer, offer));
+  ASSERT_EQ(kJsepStateHaveLocalOffer, mSessionOff->GetState());
 
   ASSERT_EQ(NS_ERROR_UNEXPECTED,
-            mSessionAns.SetRemoteDescription(kJsepSdpOffer, offer));
+            mSessionAns->SetRemoteDescription(kJsepSdpOffer, offer));
   ASSERT_EQ(NS_OK,
-            mSessionAns.SetLocalDescription(kJsepSdpRollback, ""));
-  ASSERT_EQ(kJsepStateStable, mSessionAns.GetState());
+            mSessionAns->SetLocalDescription(kJsepSdpRollback, ""));
+  ASSERT_EQ(kJsepStateStable, mSessionAns->GetState());
 
   SetRemoteOffer(offer);
 
   std::string answer = CreateAnswer();
   SetLocalAnswer(answer);
   SetRemoteAnswer(answer);
 }
 
 TEST_P(JsepSessionTest, TestRejectOfferRollback)
 {
-  AddTracks(mSessionOff);
-  AddTracks(mSessionAns);
+  AddTracks(*mSessionOff);
+  AddTracks(*mSessionAns);
 
   std::string offer = CreateOffer();
   SetLocalOffer(offer);
   SetRemoteOffer(offer);
 
   ASSERT_EQ(NS_OK,
-            mSessionAns.SetRemoteDescription(kJsepSdpRollback, ""));
-  ASSERT_EQ(kJsepStateStable, mSessionAns.GetState());
-  ASSERT_EQ(types.size(), mSessionAns.GetRemoteTracksRemoved().size());
+            mSessionAns->SetRemoteDescription(kJsepSdpRollback, ""));
+  ASSERT_EQ(kJsepStateStable, mSessionAns->GetState());
+  ASSERT_EQ(types.size(), mSessionAns->GetRemoteTracksRemoved().size());
 
   ASSERT_EQ(NS_OK,
-            mSessionOff.SetLocalDescription(kJsepSdpRollback, ""));
-  ASSERT_EQ(kJsepStateStable, mSessionOff.GetState());
+            mSessionOff->SetLocalDescription(kJsepSdpRollback, ""));
+  ASSERT_EQ(kJsepStateStable, mSessionOff->GetState());
 
   OfferAnswer();
 }
 
 TEST_P(JsepSessionTest, TestInvalidRollback)
 {
-  AddTracks(mSessionOff);
-  AddTracks(mSessionAns);
+  AddTracks(*mSessionOff);
+  AddTracks(*mSessionAns);
 
   ASSERT_EQ(NS_ERROR_UNEXPECTED,
-            mSessionOff.SetLocalDescription(kJsepSdpRollback, ""));
+            mSessionOff->SetLocalDescription(kJsepSdpRollback, ""));
   ASSERT_EQ(NS_ERROR_UNEXPECTED,
-            mSessionOff.SetRemoteDescription(kJsepSdpRollback, ""));
+            mSessionOff->SetRemoteDescription(kJsepSdpRollback, ""));
 
   std::string offer = CreateOffer();
   ASSERT_EQ(NS_ERROR_UNEXPECTED,
-            mSessionOff.SetLocalDescription(kJsepSdpRollback, ""));
+            mSessionOff->SetLocalDescription(kJsepSdpRollback, ""));
   ASSERT_EQ(NS_ERROR_UNEXPECTED,
-            mSessionOff.SetRemoteDescription(kJsepSdpRollback, ""));
+            mSessionOff->SetRemoteDescription(kJsepSdpRollback, ""));
 
   SetLocalOffer(offer);
   ASSERT_EQ(NS_ERROR_UNEXPECTED,
-            mSessionOff.SetRemoteDescription(kJsepSdpRollback, ""));
+            mSessionOff->SetRemoteDescription(kJsepSdpRollback, ""));
 
   SetRemoteOffer(offer);
   ASSERT_EQ(NS_ERROR_UNEXPECTED,
-            mSessionAns.SetLocalDescription(kJsepSdpRollback, ""));
+            mSessionAns->SetLocalDescription(kJsepSdpRollback, ""));
 
   std::string answer = CreateAnswer();
   ASSERT_EQ(NS_ERROR_UNEXPECTED,
-            mSessionAns.SetLocalDescription(kJsepSdpRollback, ""));
+            mSessionAns->SetLocalDescription(kJsepSdpRollback, ""));
 
   SetLocalAnswer(answer);
   ASSERT_EQ(NS_ERROR_UNEXPECTED,
-            mSessionAns.SetLocalDescription(kJsepSdpRollback, ""));
+            mSessionAns->SetLocalDescription(kJsepSdpRollback, ""));
   ASSERT_EQ(NS_ERROR_UNEXPECTED,
-            mSessionAns.SetRemoteDescription(kJsepSdpRollback, ""));
+            mSessionAns->SetRemoteDescription(kJsepSdpRollback, ""));
 
   SetRemoteAnswer(answer);
   ASSERT_EQ(NS_ERROR_UNEXPECTED,
-            mSessionOff.SetLocalDescription(kJsepSdpRollback, ""));
+            mSessionOff->SetLocalDescription(kJsepSdpRollback, ""));
   ASSERT_EQ(NS_ERROR_UNEXPECTED,
-            mSessionOff.SetRemoteDescription(kJsepSdpRollback, ""));
+            mSessionOff->SetRemoteDescription(kJsepSdpRollback, ""));
 }
 
 size_t GetActiveTransportCount(const JsepSession& session)
 {
   auto transports = session.GetTransports();
   size_t activeTransportCount = 0;
   for (RefPtr<JsepTransport>& transport : transports) {
     activeTransportCount += transport->mComponents;
   }
   return activeTransportCount;
 }
 
 TEST_P(JsepSessionTest, TestBalancedBundle)
 {
-  AddTracks(mSessionOff);
-  AddTracks(mSessionAns);
-
-  mSessionOff.SetBundlePolicy(kBundleBalanced);
+  AddTracks(*mSessionOff);
+  AddTracks(*mSessionAns);
+
+  mSessionOff->SetBundlePolicy(kBundleBalanced);
 
   std::string offer = CreateOffer();
   SipccSdpParser parser;
   UniquePtr<Sdp> parsedOffer = parser.Parse(offer);
   ASSERT_TRUE(parsedOffer.get());
 
   std::map<SdpMediaSection::MediaType, SdpMediaSection*> firstByType;
 
@@ -4241,266 +4389,266 @@ TEST_P(JsepSessionTest, TestBalancedBund
   }
 
   SetLocalOffer(offer);
   SetRemoteOffer(offer);
   std::string answer = CreateAnswer();
   SetLocalAnswer(answer);
   SetRemoteAnswer(answer);
 
-  CheckPairs(mSessionOff, "Offerer pairs");
-  CheckPairs(mSessionAns, "Answerer pairs");
-  EXPECT_EQ(1U, GetActiveTransportCount(mSessionOff));
-  EXPECT_EQ(1U, GetActiveTransportCount(mSessionAns));
+  CheckPairs(*mSessionOff, "Offerer pairs");
+  CheckPairs(*mSessionAns, "Answerer pairs");
+  EXPECT_EQ(1U, GetActiveTransportCount(*mSessionOff));
+  EXPECT_EQ(1U, GetActiveTransportCount(*mSessionAns));
 }
 
 TEST_P(JsepSessionTest, TestMaxBundle)
 {
-  AddTracks(mSessionOff);
-  AddTracks(mSessionAns);
-
-  mSessionOff.SetBundlePolicy(kBundleMaxBundle);
+  AddTracks(*mSessionOff);
+  AddTracks(*mSessionAns);
+
+  mSessionOff->SetBundlePolicy(kBundleMaxBundle);
   OfferAnswer();
 
-  std::string offer = mSessionOff.GetLocalDescription();
+  std::string offer = mSessionOff->GetLocalDescription();
   SipccSdpParser parser;
   UniquePtr<Sdp> parsedOffer = parser.Parse(offer);
   ASSERT_TRUE(parsedOffer.get());
 
   ASSERT_FALSE(
       parsedOffer->GetMediaSection(0).GetAttributeList().HasAttribute(
         SdpAttribute::kBundleOnlyAttribute));
   ASSERT_NE(0U, parsedOffer->GetMediaSection(0).GetPort());
   for (size_t i = 1; i < parsedOffer->GetMediaSectionCount(); ++i) {
     ASSERT_TRUE(
         parsedOffer->GetMediaSection(i).GetAttributeList().HasAttribute(
           SdpAttribute::kBundleOnlyAttribute));
     ASSERT_EQ(0U, parsedOffer->GetMediaSection(i).GetPort());
   }
 
 
-  CheckPairs(mSessionOff, "Offerer pairs");
-  CheckPairs(mSessionAns, "Answerer pairs");
-  EXPECT_EQ(1U, GetActiveTransportCount(mSessionOff));
-  EXPECT_EQ(1U, GetActiveTransportCount(mSessionAns));
+  CheckPairs(*mSessionOff, "Offerer pairs");
+  CheckPairs(*mSessionAns, "Answerer pairs");
+  EXPECT_EQ(1U, GetActiveTransportCount(*mSessionOff));
+  EXPECT_EQ(1U, GetActiveTransportCount(*mSessionAns));
 }
 
 TEST_F(JsepSessionTest, TestNonDefaultProtocol)
 {
-  AddTracks(mSessionOff, "audio,video,datachannel");
-  AddTracks(mSessionAns, "audio,video,datachannel");
+  AddTracks(*mSessionOff, "audio,video,datachannel");
+  AddTracks(*mSessionAns, "audio,video,datachannel");
 
   std::string offer;
-  ASSERT_EQ(NS_OK, mSessionOff.CreateOffer(JsepOfferOptions(), &offer));
+  ASSERT_EQ(NS_OK, mSessionOff->CreateOffer(JsepOfferOptions(), &offer));
   offer.replace(offer.find("UDP/TLS/RTP/SAVPF"),
                 strlen("UDP/TLS/RTP/SAVPF"),
                 "RTP/SAVPF");
   offer.replace(offer.find("UDP/TLS/RTP/SAVPF"),
                 strlen("UDP/TLS/RTP/SAVPF"),
                 "RTP/SAVPF");
-  mSessionOff.SetLocalDescription(kJsepSdpOffer, offer);
-  mSessionAns.SetRemoteDescription(kJsepSdpOffer, offer);
+  mSessionOff->SetLocalDescription(kJsepSdpOffer, offer);
+  mSessionAns->SetRemoteDescription(kJsepSdpOffer, offer);
 
   std::string answer;
-  mSessionAns.CreateAnswer(JsepAnswerOptions(), &answer);
+  mSessionAns->CreateAnswer(JsepAnswerOptions(), &answer);
   UniquePtr<Sdp> parsedAnswer = Parse(answer);
   ASSERT_EQ(3U, parsedAnswer->GetMediaSectionCount());
   ASSERT_EQ(SdpMediaSection::kRtpSavpf,
             parsedAnswer->GetMediaSection(0).GetProtocol());
   ASSERT_EQ(SdpMediaSection::kRtpSavpf,
             parsedAnswer->GetMediaSection(1).GetProtocol());
 
-  mSessionAns.SetLocalDescription(kJsepSdpAnswer, answer);
-  mSessionOff.SetRemoteDescription(kJsepSdpAnswer, answer);
+  mSessionAns->SetLocalDescription(kJsepSdpAnswer, answer);
+  mSessionOff->SetRemoteDescription(kJsepSdpAnswer, answer);
 
   // Make sure reoffer uses the same protocol as before
-  mSessionOff.CreateOffer(JsepOfferOptions(), &offer);
+  mSessionOff->CreateOffer(JsepOfferOptions(), &offer);
   UniquePtr<Sdp> parsedOffer = Parse(offer);
   ASSERT_EQ(3U, parsedOffer->GetMediaSectionCount());
   ASSERT_EQ(SdpMediaSection::kRtpSavpf,
             parsedOffer->GetMediaSection(0).GetProtocol());
   ASSERT_EQ(SdpMediaSection::kRtpSavpf,
             parsedOffer->GetMediaSection(1).GetProtocol());
 
   // Make sure reoffer from other side uses the same protocol as before
-  mSessionAns.CreateOffer(JsepOfferOptions(), &offer);
+  mSessionAns->CreateOffer(JsepOfferOptions(), &offer);
   parsedOffer = Parse(offer);
   ASSERT_EQ(3U, parsedOffer->GetMediaSectionCount());
   ASSERT_EQ(SdpMediaSection::kRtpSavpf,
             parsedOffer->GetMediaSection(0).GetProtocol());
   ASSERT_EQ(SdpMediaSection::kRtpSavpf,
             parsedOffer->GetMediaSection(1).GetProtocol());
 }
 
 TEST_F(JsepSessionTest, CreateOfferNoVideoStreamRecvVideo)
 {
   types.push_back(SdpMediaSection::kAudio);
-  AddTracks(mSessionOff, "audio");
+  AddTracks(*mSessionOff, "audio");
 
   JsepOfferOptions options;
   options.mOfferToReceiveAudio = Some(static_cast<size_t>(1U));
   options.mOfferToReceiveVideo = Some(static_cast<size_t>(1U));
 
   CreateOffer(Some(options));
 }
 
 TEST_F(JsepSessionTest, CreateOfferNoAudioStreamRecvAudio)
 {
   types.push_back(SdpMediaSection::kVideo);
-  AddTracks(mSessionOff, "video");
+  AddTracks(*mSessionOff, "video");
 
   JsepOfferOptions options;
   options.mOfferToReceiveAudio = Some(static_cast<size_t>(1U));
   options.mOfferToReceiveVideo = Some(static_cast<size_t>(1U));
 
   CreateOffer(Some(options));
 }
 
 TEST_F(JsepSessionTest, CreateOfferNoVideoStream)
 {
   types.push_back(SdpMediaSection::kAudio);
-  AddTracks(mSessionOff, "audio");
+  AddTracks(*mSessionOff, "audio");
 
   JsepOfferOptions options;
   options.mOfferToReceiveAudio = Some(static_cast<size_t>(1U));
   options.mOfferToReceiveVideo = Some(static_cast<size_t>(0U));
 
   CreateOffer(Some(options));
 }
 
 TEST_F(JsepSessionTest, CreateOfferNoAudioStream)
 {
   types.push_back(SdpMediaSection::kVideo);
-  AddTracks(mSessionOff, "video");
+  AddTracks(*mSessionOff, "video");
 
   JsepOfferOptions options;
   options.mOfferToReceiveAudio = Some(static_cast<size_t>(0U));
   options.mOfferToReceiveVideo = Some(static_cast<size_t>(1U));
 
   CreateOffer(Some(options));
 }
 
 TEST_F(JsepSessionTest, CreateOfferDontReceiveAudio)
 {
   types.push_back(SdpMediaSection::kAudio);
   types.push_back(SdpMediaSection::kVideo);
-  AddTracks(mSessionOff, "audio,video");
+  AddTracks(*mSessionOff, "audio,video");
 
   JsepOfferOptions options;
   options.mOfferToReceiveAudio = Some(static_cast<size_t>(0U));
   options.mOfferToReceiveVideo = Some(static_cast<size_t>(1U));
 
   CreateOffer(Some(options));
 }
 
 TEST_F(JsepSessionTest, CreateOfferDontReceiveVideo)
 {
   types.push_back(SdpMediaSection::kAudio);
   types.push_back(SdpMediaSection::kVideo);
-  AddTracks(mSessionOff, "audio,video");
+  AddTracks(*mSessionOff, "audio,video");
 
   JsepOfferOptions options;
   options.mOfferToReceiveAudio = Some(static_cast<size_t>(1U));
   options.mOfferToReceiveVideo = Some(static_cast<size_t>(0U));
 
   CreateOffer(Some(options));
 }
 
 TEST_F(JsepSessionTest, CreateOfferRemoveAudioTrack)
 {
   types.push_back(SdpMediaSection::kAudio);
   types.push_back(SdpMediaSection::kVideo);
-  AddTracks(mSessionOff, "audio,video");
+  AddTracks(*mSessionOff, "audio,video");
 
   JsepOfferOptions options;
   options.mOfferToReceiveAudio = Some(static_cast<size_t>(1U));
   options.mOfferToReceiveVideo = Some(static_cast<size_t>(0U));
 
   RefPtr<JsepTrack> removedTrack = GetTrackOff(0, types.front());
   ASSERT_TRUE(removedTrack);
-  ASSERT_EQ(NS_OK, mSessionOff.RemoveTrack(removedTrack->GetStreamId(),
+  ASSERT_EQ(NS_OK, mSessionOff->RemoveTrack(removedTrack->GetStreamId(),
                                            removedTrack->GetTrackId()));
 
   CreateOffer(Some(options));
 }
 
 TEST_F(JsepSessionTest, CreateOfferDontReceiveAudioRemoveAudioTrack)
 {
   types.push_back(SdpMediaSection::kAudio);
   types.push_back(SdpMediaSection::kVideo);
-  AddTracks(mSessionOff, "audio,video");
+  AddTracks(*mSessionOff, "audio,video");
 
   JsepOfferOptions options;
   options.mOfferToReceiveAudio = Some(static_cast<size_t>(0U));
   options.mOfferToReceiveVideo = Some(static_cast<size_t>(1U));
 
   RefPtr<JsepTrack> removedTrack = GetTrackOff(0, types.front());
   ASSERT_TRUE(removedTrack);
-  ASSERT_EQ(NS_OK, mSessionOff.RemoveTrack(removedTrack->GetStreamId(),
+  ASSERT_EQ(NS_OK, mSessionOff->RemoveTrack(removedTrack->GetStreamId(),
                                            removedTrack->GetTrackId()));
 
   CreateOffer(Some(options));
 }
 
 TEST_F(JsepSessionTest, CreateOfferDontReceiveVideoRemoveVideoTrack)
 {
   types.push_back(SdpMediaSection::kAudio);
   types.push_back(SdpMediaSection::kVideo);
-  AddTracks(mSessionOff, "audio,video");
+  AddTracks(*mSessionOff, "audio,video");
 
   JsepOfferOptions options;
   options.mOfferToReceiveAudio = Some(static_cast<size_t>(1U));
   options.mOfferToReceiveVideo = Some(static_cast<size_t>(0U));
 
   RefPtr<JsepTrack> removedTrack = GetTrackOff(0, types.back());
   ASSERT_TRUE(removedTrack);
-  ASSERT_EQ(NS_OK, mSessionOff.RemoveTrack(removedTrack->GetStreamId(),
+  ASSERT_EQ(NS_OK, mSessionOff->RemoveTrack(removedTrack->GetStreamId(),
                                            removedTrack->GetTrackId()));
 
   CreateOffer(Some(options));
 }
 
 static const std::string strSampleCandidate =
   "a=candidate:1 1 UDP 2130706431 192.168.2.1 50005 typ host\r\n";
 
 static const unsigned short nSamplelevel = 2;
 
 TEST_F(JsepSessionTest, CreateOfferAddCandidate)
 {
   types.push_back(SdpMediaSection::kAudio);
-  AddTracks(mSessionOff, "audio");
+  AddTracks(*mSessionOff, "audio");
   std::string offer = CreateOffer();
   SetLocalOffer(offer);
 
   std::string mid;
   bool skipped;
   nsresult rv;
-  rv = mSessionOff.AddLocalIceCandidate(strSampleCandidate,
+  rv = mSessionOff->AddLocalIceCandidate(strSampleCandidate,
                                         nSamplelevel, &mid, &skipped);
   ASSERT_EQ(NS_OK, rv);
 }
 
 TEST_F(JsepSessionTest, AddIceCandidateEarly)
 {
   std::string mid;
   bool skipped;
   nsresult rv;
-  rv = mSessionOff.AddLocalIceCandidate(strSampleCandidate,
+  rv = mSessionOff->AddLocalIceCandidate(strSampleCandidate,
                                         nSamplelevel, &mid, &skipped);
 
   // This can't succeed without a local description
   ASSERT_NE(NS_OK, rv);
 }
 
 TEST_F(JsepSessionTest, OfferAnswerDontAddAudioStreamOnAnswerNoOptions)
 {
   types.push_back(SdpMediaSection::kAudio);
   types.push_back(SdpMediaSection::kVideo);
-  AddTracks(mSessionOff, "audio,video");
-  AddTracks(mSessionAns, "video");
+  AddTracks(*mSessionOff, "audio,video");
+  AddTracks(*mSessionAns, "video");
 
   JsepOfferOptions options;
   options.mOfferToReceiveAudio = Some(static_cast<size_t>(1U));
   options.mOfferToReceiveVideo = Some(static_cast<size_t>(1U));
 
   CreateOffer(Some(options));
   std::string offer = CreateOffer(Some(options));
   SetLocalOffer(offer);
@@ -4509,18 +4657,18 @@ TEST_F(JsepSessionTest, OfferAnswerDontA
   SetLocalAnswer(answer, CHECK_SUCCESS);
   SetRemoteAnswer(answer, CHECK_SUCCESS);
 }
 
 TEST_F(JsepSessionTest, OfferAnswerDontAddVideoStreamOnAnswerNoOptions)
 {
   types.push_back(SdpMediaSection::kAudio);
   types.push_back(SdpMediaSection::kVideo);
-  AddTracks(mSessionOff, "audio,video");
-  AddTracks(mSessionAns, "audio");
+  AddTracks(*mSessionOff, "audio,video");
+  AddTracks(*mSessionAns, "audio");
 
   JsepOfferOptions options;
   options.mOfferToReceiveAudio = Some(static_cast<size_t>(1U));
   options.mOfferToReceiveVideo = Some(static_cast<size_t>(1U));
 
   CreateOffer(Some(options));
   std::string offer = CreateOffer(Some(options));
   SetLocalOffer(offer);
@@ -4529,99 +4677,99 @@ TEST_F(JsepSessionTest, OfferAnswerDontA
   SetLocalAnswer(answer, CHECK_SUCCESS);
   SetRemoteAnswer(answer, CHECK_SUCCESS);
 }
 
 TEST_F(JsepSessionTest, OfferAnswerDontAddAudioVideoStreamsOnAnswerNoOptions)
 {
   types.push_back(SdpMediaSection::kAudio);
   types.push_back(SdpMediaSection::kVideo);
-  AddTracks(mSessionOff, "audio,video");
-  AddTracks(mSessionAns);
+  AddTracks(*mSessionOff, "audio,video");
+  AddTracks(*mSessionAns);
 
   JsepOfferOptions options;
   options.mOfferToReceiveAudio = Some(static_cast<size_t>(1U));
   options.mOfferToReceiveVideo = Some(static_cast<size_t>(1U));
 
   OfferAnswer();
 }
 
 TEST_F(JsepSessionTest, OfferAndAnswerWithExtraCodec)
 {
   types.push_back(SdpMediaSection::kAudio);
-  AddTracks(mSessionOff, "audio");
-  AddTracks(mSessionAns, "audio");
+  AddTracks(*mSessionOff, "audio");
+  AddTracks(*mSessionAns, "audio");
   std::string offer = CreateOffer();
   SetLocalOffer(offer);
   SetRemoteOffer(offer);
   std::string answer = CreateAnswer();
 
   UniquePtr<Sdp> munge = Parse(answer);
   SdpMediaSection& mediaSection = munge->GetMediaSection(0);
   mediaSection.AddCodec("8", "PCMA", 8000, 1);
   std::string sdpString = munge->ToString();
 
   SetLocalAnswer(sdpString);
   SetRemoteAnswer(answer);
 }
 
 TEST_F(JsepSessionTest, AddCandidateInHaveLocalOffer) {
   types.push_back(SdpMediaSection::kAudio);
-  AddTracks(mSessionOff, "audio");
+  AddTracks(*mSessionOff, "audio");
   std::string offer = CreateOffer();
   SetLocalOffer(offer);
 
   nsresult rv;
   std::string mid;
-  rv = mSessionOff.AddRemoteIceCandidate(strSampleCandidate,
+  rv = mSessionOff->AddRemoteIceCandidate(strSampleCandidate,
                                          mid, nSamplelevel);
   ASSERT_EQ(NS_ERROR_UNEXPECTED, rv);
 }
 
 TEST_F(JsepSessionTest, SetLocalWithoutCreateOffer) {
   types.push_back(SdpMediaSection::kAudio);
-  AddTracks(mSessionOff, "audio");
-  AddTracks(mSessionAns, "audio");
+  AddTracks(*mSessionOff, "audio");
+  AddTracks(*mSessionAns, "audio");
 
   std::string offer = CreateOffer();
-  nsresult rv = mSessionAns.SetLocalDescription(kJsepSdpOffer, offer);
+  nsresult rv = mSessionAns->SetLocalDescription(kJsepSdpOffer, offer);
   ASSERT_EQ(NS_ERROR_UNEXPECTED, rv);
 }
 
 TEST_F(JsepSessionTest, SetLocalWithoutCreateAnswer) {
   types.push_back(SdpMediaSection::kAudio);
-  AddTracks(mSessionOff, "audio");
-  AddTracks(mSessionAns, "audio");
+  AddTracks(*mSessionOff, "audio");
+  AddTracks(*mSessionAns, "audio");
 
   std::string offer = CreateOffer();
   SetRemoteOffer(offer);
-  nsresult rv = mSessionAns.SetLocalDescription(kJsepSdpAnswer, offer);
+  nsresult rv = mSessionAns->SetLocalDescription(kJsepSdpAnswer, offer);
   ASSERT_EQ(NS_ERROR_UNEXPECTED, rv);
 }
 
 // Test for Bug 843595
 TEST_F(JsepSessionTest, missingUfrag)
 {
   types.push_back(SdpMediaSection::kAudio);
-  AddTracks(mSessionOff, "audio");
-  AddTracks(mSessionAns, "audio");
+  AddTracks(*mSessionOff, "audio");
+  AddTracks(*mSessionAns, "audio");
   std::string offer = CreateOffer();
   std::string ufrag = "ice-ufrag";
   std::size_t pos = offer.find(ufrag);
   ASSERT_NE(pos, std::string::npos);
   offer.replace(pos, ufrag.length(), "ice-ufrog");
-  nsresult rv = mSessionAns.SetRemoteDescription(kJsepSdpOffer, offer);
+  nsresult rv = mSessionAns->SetRemoteDescription(kJsepSdpOffer, offer);
   ASSERT_EQ(NS_ERROR_INVALID_ARG, rv);
 }
 
 TEST_F(JsepSessionTest, AudioOnlyCalleeNoRtcpMux)
 {
   types.push_back(SdpMediaSection::kAudio);
-  AddTracks(mSessionOff, "audio");
-  AddTracks(mSessionAns, "audio");
+  AddTracks(*mSessionOff, "audio");
+  AddTracks(*mSessionAns, "audio");
   std::string offer = CreateOffer();
   std::string rtcp_mux = "a=rtcp-mux\r\n";
   std::size_t pos = offer.find(rtcp_mux);
   ASSERT_NE(pos, std::string::npos);
   offer.replace(pos, rtcp_mux.length(), "");
   SetLocalOffer(offer);
   SetRemoteOffer(offer);
   std::string answer = CreateAnswer();
@@ -4649,17 +4797,17 @@ TEST_F(JsepSessionTest, AudioOnlyG711Cal
     "a=candidate:0 1 udp 2130706432 148.147.200.251 9000 typ host\r\n"
     "a=candidate:0 2 udp 2130706432 148.147.200.251 9005 typ host\r\n"
     "a=ice-ufrag:cYuakxkEKH+RApYE\r\n"
     "a=ice-pwd:bwtpzLZD+3jbu8vQHvEa6Xuq\r\n"
     "a=setup:active\r\n"
     "a=sendrecv\r\n";
 
   types.push_back(SdpMediaSection::kAudio);
-  AddTracks(mSessionAns, "audio");
+  AddTracks(*mSessionAns, "audio");
   SetRemoteOffer(offer, CHECK_SUCCESS);
   std::string answer = CreateAnswer();
 
   // They didn't offer opus, so our answer shouldn't include it.
   ASSERT_EQ(answer.find(" opus/"), std::string::npos);
 
   // They also didn't offer video or application
   ASSERT_EQ(answer.find("video"), std::string::npos);
@@ -4671,92 +4819,92 @@ TEST_F(JsepSessionTest, AudioOnlyG711Cal
   // Double-check the directionality
   ASSERT_NE(answer.find("\r\na=sendrecv"), std::string::npos);
 
 }
 
 TEST_F(JsepSessionTest, AudioOnlyG722Only)
 {
   types.push_back(SdpMediaSection::kAudio);
-  AddTracks(mSessionOff, "audio");
-  AddTracks(mSessionAns, "audio");
+  AddTracks(*mSessionOff, "audio");
+  AddTracks(*mSessionAns, "audio");
   std::string offer = CreateOffer();
   SetLocalOffer(offer);
 
   std::string audio = "m=audio 9 UDP/TLS/RTP/SAVPF 109 9 0 8 101\r\n";
   std::size_t pos = offer.find(audio);
   ASSERT_NE(pos, std::string::npos);
   offer.replace(pos, audio.length(),
                 "m=audio 65375 UDP/TLS/RTP/SAVPF 9\r\n");
   SetRemoteOffer(offer);
 
   std::string answer = CreateAnswer();
   SetLocalAnswer(answer);
-  ASSERT_NE(mSessionAns.GetLocalDescription().find("UDP/TLS/RTP/SAVPF 9\r"),
+  ASSERT_NE(mSessionAns->GetLocalDescription().find("UDP/TLS/RTP/SAVPF 9\r"),
             std::string::npos);
-  ASSERT_NE(mSessionAns.GetLocalDescription().find("a=rtpmap:9 G722/8000"),
+  ASSERT_NE(mSessionAns->GetLocalDescription().find("a=rtpmap:9 G722/8000"),
             std::string::npos);
 }
 
 TEST_F(JsepSessionTest, AudioOnlyG722Rejected)
 {
   types.push_back(SdpMediaSection::kAudio);
-  AddTracks(mSessionOff, "audio");
-  AddTracks(mSessionAns, "audio");
+  AddTracks(*mSessionOff, "audio");
+  AddTracks(*mSessionAns, "audio");
   std::string offer = CreateOffer();
   SetLocalOffer(offer);
 
   std::string audio = "m=audio 9 UDP/TLS/RTP/SAVPF 109 9 0 8 101\r\n";
   std::size_t pos = offer.find(audio);
   ASSERT_NE(pos, std::string::npos);
   offer.replace(pos, audio.length(),
                 "m=audio 65375 UDP/TLS/RTP/SAVPF 0 8\r\n");
   SetRemoteOffer(offer);
 
   std::string answer = CreateAnswer();
   SetLocalAnswer(answer);
   SetRemoteAnswer(answer);
 
   // TODO(bug 814227): Use commented out code instead.
-  ASSERT_NE(mSessionAns.GetLocalDescription().find("UDP/TLS/RTP/SAVPF 0\r"),
+  ASSERT_NE(mSessionAns->GetLocalDescription().find("UDP/TLS/RTP/SAVPF 0\r"),
             std::string::npos);
-  // ASSERT_NE(mSessionAns.GetLocalDescription().find("UDP/TLS/RTP/SAVPF 0 8\r"), std::string::npos);
-  ASSERT_NE(mSessionAns.GetLocalDescription().find("a=rtpmap:0 PCMU/8000"), std::string::npos);
-  ASSERT_EQ(mSessionAns.GetLocalDescription().find("a=rtpmap:109 opus/48000/2"), std::string::npos);
-  ASSERT_EQ(mSessionAns.GetLocalDescription().find("a=rtpmap:9 G722/8000"), std::string::npos);
+  // ASSERT_NE(mSessionAns->GetLocalDescription().find("UDP/TLS/RTP/SAVPF 0 8\r"), std::string::npos);
+  ASSERT_NE(mSessionAns->GetLocalDescription().find("a=rtpmap:0 PCMU/8000"), std::string::npos);
+  ASSERT_EQ(mSessionAns->GetLocalDescription().find("a=rtpmap:109 opus/48000/2"), std::string::npos);
+  ASSERT_EQ(mSessionAns->GetLocalDescription().find("a=rtpmap:9 G722/8000"), std::string::npos);
 }
 
 // This test doesn't make sense for bundle
 TEST_F(JsepSessionTest, DISABLED_FullCallAudioNoMuxVideoMux)
 {
   types.push_back(SdpMediaSection::kAudio);
-  AddTracks(mSessionOff, "audio,video");
-  AddTracks(mSessionAns, "audio,video");
+  AddTracks(*mSessionOff, "audio,video");
+  AddTracks(*mSessionAns, "audio,video");
   std::string offer = CreateOffer();
   SetLocalOffer(offer);
   std::string rtcp_mux = "a=rtcp-mux\r\n";
   std::size_t pos = offer.find(rtcp_mux);
   ASSERT_NE(pos, std::string::npos);
   offer.replace(pos, rtcp_mux.length(), "");
   SetRemoteOffer(offer);
   std::string answer = CreateAnswer();
 
-  size_t match = mSessionAns.GetLocalDescription().find("\r\na=rtcp-mux");
+  size_t match = mSessionAns->GetLocalDescription().find("\r\na=rtcp-mux");
   ASSERT_NE(match, std::string::npos);
-  match = mSessionAns.GetLocalDescription().find("\r\na=rtcp-mux", match + 1);
+  match = mSessionAns->GetLocalDescription().find("\r\na=rtcp-mux", match + 1);
   ASSERT_EQ(match, std::string::npos);
 }
 
 // Disabled pending resolution of bug 818640.
 // Actually, this test is completely broken; you can't just call
 // SetRemote/CreateAnswer over and over again.
 TEST_F(JsepSessionTest, DISABLED_OfferAllDynamicTypes)
 {
   types.push_back(SdpMediaSection::kAudio);
-  AddTracks(mSessionAns, "audio");
+  AddTracks(*mSessionAns, "audio");
 
   std::string offer;
   for (int i = 96; i < 128; i++)
   {
     std::stringstream ss;
     ss << i;
     std::cout << "Trying dynamic pt = " << i << std::endl;
     offer =
@@ -4797,17 +4945,17 @@ TEST_F(JsepSessionTest, ipAddrAnyOffer)
     "c=IN IP4 0.0.0.0\r\n"
     "a=rtpmap:99 opus/48000/2\r\n"
     "a=ice-ufrag:cYuakxkEKH+RApYE\r\n"
     "a=ice-pwd:bwtpzLZD+3jbu8vQHvEa6Xuq\r\n"
     "a=setup:active\r\n"
     "a=sendrecv\r\n";
 
   types.push_back(SdpMediaSection::kAudio);
-  AddTracks(mSessionAns, "audio");
+  AddTracks(*mSessionAns, "audio");
   SetRemoteOffer(offer, CHECK_SUCCESS);
   std::string answer = CreateAnswer();
 
   ASSERT_NE(answer.find("a=sendrecv"), std::string::npos);
 }
 
 static void CreateSDPForBigOTests(std::string& offer, const std::string& number) {
   offer =
@@ -4833,59 +4981,59 @@ static void CreateSDPForBigOTests(std::s
 
 TEST_F(JsepSessionTest, BigOValues)
 {
   std::string offer;
 
   CreateSDPForBigOTests(offer, "12345678901234567");
 
   types.push_back(SdpMediaSection::kAudio);
-  AddTracks(mSessionAns, "audio");
+  AddTracks(*mSessionAns, "audio");
   SetRemoteOffer(offer, CHECK_SUCCESS);
 }
 
 TEST_F(JsepSessionTest, BigOValuesExtraChars)
 {
   std::string offer;
 
   CreateSDPForBigOTests(offer, "12345678901234567FOOBAR");
 
   types.push_back(SdpMediaSection::kAudio);
-  AddTracks(mSessionAns, "audio");
+  AddTracks(*mSessionAns, "audio");
   // The signaling state will remain "stable" because the unparsable
   // SDP leads to a failure in SetRemoteDescription.
   SetRemoteOffer(offer, NO_CHECKS);
-  ASSERT_EQ(kJsepStateStable, mSessionAns.GetState());
+  ASSERT_EQ(kJsepStateStable, mSessionAns->GetState());
 }
 
 TEST_F(JsepSessionTest, BigOValuesTooBig)
 {
   std::string offer;
 
   CreateSDPForBigOTests(offer, "18446744073709551615");
   types.push_back(SdpMediaSection::kAudio);
-  AddTracks(mSessionAns, "audio");
+  AddTracks(*mSessionAns, "audio");
 
   // The signaling state will remain "stable" because the unparsable
   // SDP leads to a failure in SetRemoteDescription.
   SetRemoteOffer(offer, NO_CHECKS);
-  ASSERT_EQ(kJsepStateStable, mSessionAns.GetState());
+  ASSERT_EQ(kJsepStateStable, mSessionAns->GetState());
 }
 
 
 TEST_F(JsepSessionTest, SetLocalAnswerInStable)
 {
   types.push_back(SdpMediaSection::kAudio);
-  AddTracks(mSessionOff, "audio");
+  AddTracks(*mSessionOff, "audio");
   std::string offer = CreateOffer();
 
   // The signaling state will remain "stable" because the
   // SetLocalDescription call fails.
   SetLocalAnswer(offer, NO_CHECKS);
-  ASSERT_EQ(kJsepStateStable, mSessionOff.GetState());
+  ASSERT_EQ(kJsepStateStable, mSessionOff->GetState());
 }
 
 TEST_F(JsepSessionTest, SetRemoteAnswerInStable)
 {
   const std::string answer =
     "v=0\r\n"
     "o=Mozilla-SIPUA 4949 0 IN IP4 10.86.255.143\r\n"
     "s=SIP Call\r\n"
@@ -4907,91 +5055,91 @@ TEST_F(JsepSessionTest, SetRemoteAnswerI
     "a=rtpmap:120 VP8/90000\r\n"
     "a=fmtp:97 profile-level-id=42E00C\r\n"
     "a=sendrecv\r\n"
     "a=candidate:1 1 UDP 2130706431 192.168.2.3 50007 typ host\r\n"
     "a=candidate:2 2 UDP 2130706431 192.168.2.4 50008 typ host\r\n";
 
   // The signaling state will remain "stable" because the
   // SetRemoteDescription call fails.
-  nsresult rv = mSessionOff.SetRemoteDescription(kJsepSdpAnswer, answer);
+  nsresult rv = mSessionOff->SetRemoteDescription(kJsepSdpAnswer, answer);
   ASSERT_NE(NS_OK, rv);
-  ASSERT_EQ(kJsepStateStable, mSessionOff.GetState());
+  ASSERT_EQ(kJsepStateStable, mSessionOff->GetState());
 }
 
 TEST_F(JsepSessionTest, SetLocalAnswerInHaveLocalOffer)
 {
   types.push_back(SdpMediaSection::kAudio);
-  AddTracks(mSessionOff, "audio");
+  AddTracks(*mSessionOff, "audio");
   std::string offer = CreateOffer();
 
   SetLocalOffer(offer);
-  ASSERT_EQ(kJsepStateHaveLocalOffer, mSessionOff.GetState());
+  ASSERT_EQ(kJsepStateHaveLocalOffer, mSessionOff->GetState());
 
   // The signaling state will remain "have-local-offer" because the
   // SetLocalDescription call fails.
-  nsresult rv = mSessionOff.SetLocalDescription(kJsepSdpAnswer, offer);
+  nsresult rv = mSessionOff->SetLocalDescription(kJsepSdpAnswer, offer);
   ASSERT_NE(NS_OK, rv);
-  ASSERT_EQ(kJsepStateHaveLocalOffer, mSessionOff.GetState());
+  ASSERT_EQ(kJsepStateHaveLocalOffer, mSessionOff->GetState());
 }
 
 TEST_F(JsepSessionTest, SetRemoteOfferInHaveLocalOffer)
 {
   types.push_back(SdpMediaSection::kAudio);
-  AddTracks(mSessionOff, "audio");
+  AddTracks(*mSessionOff, "audio");
   std::string offer = CreateOffer();
 
   SetLocalOffer(offer);
-  ASSERT_EQ(kJsepStateHaveLocalOffer, mSessionOff.GetState());
+  ASSERT_EQ(kJsepStateHaveLocalOffer, mSessionOff->GetState());
 
   // The signaling state will remain "have-local-offer" because the
   // SetRemoteDescription call fails.
-  nsresult rv = mSessionOff.SetRemoteDescription(kJsepSdpOffer, offer);
+  nsresult rv = mSessionOff->SetRemoteDescription(kJsepSdpOffer, offer);
   ASSERT_NE(NS_OK, rv);
-  ASSERT_EQ(kJsepStateHaveLocalOffer, mSessionOff.GetState());
+  ASSERT_EQ(kJsepStateHaveLocalOffer, mSessionOff->GetState());
 }
 
 TEST_F(JsepSessionTest, SetLocalOfferInHaveRemoteOffer)
 {
   types.push_back(SdpMediaSection::kAudio);
-  AddTracks(mSessionOff, "audio");
+  AddTracks(*mSessionOff, "audio");
   std::string offer = CreateOffer();
 
   SetRemoteOffer(offer);
-  ASSERT_EQ(kJsepStateHaveRemoteOffer, mSessionAns.GetState());
+  ASSERT_EQ(kJsepStateHaveRemoteOffer, mSessionAns->GetState());
 
   // The signaling state will remain "have-remote-offer" because the
   // SetLocalDescription call fails.
-  nsresult rv = mSessionAns.SetLocalDescription(kJsepSdpOffer, offer);
+  nsresult rv = mSessionAns->SetLocalDescription(kJsepSdpOffer, offer);
   ASSERT_NE(NS_OK, rv);
-  ASSERT_EQ(kJsepStateHaveRemoteOffer, mSessionAns.GetState());
+  ASSERT_EQ(kJsepStateHaveRemoteOffer, mSessionAns->GetState());
 }
 
 TEST_F(JsepSessionTest, SetRemoteAnswerInHaveRemoteOffer)
 {
   types.push_back(SdpMediaSection::kAudio);
-  AddTracks(mSessionOff, "audio");
+  AddTracks(*mSessionOff, "audio");
   std::string offer = CreateOffer();
 
   SetRemoteOffer(offer);
-  ASSERT_EQ(kJsepStateHaveRemoteOffer, mSessionAns.GetState());
+  ASSERT_EQ(kJsepStateHaveRemoteOffer, mSessionAns->GetState());
 
   // The signaling state will remain "have-remote-offer" because the
   // SetRemoteDescription call fails.
-  nsresult rv = mSessionAns.SetRemoteDescription(kJsepSdpAnswer, offer);
+  nsresult rv = mSessionAns->SetRemoteDescription(kJsepSdpAnswer, offer);
   ASSERT_NE(NS_OK, rv);
 
-  ASSERT_EQ(kJsepStateHaveRemoteOffer, mSessionAns.GetState());
+  ASSERT_EQ(kJsepStateHaveRemoteOffer, mSessionAns->GetState());
 }
 
 TEST_F(JsepSessionTest, RtcpFbInOffer)
 {
   types.push_back(SdpMediaSection::kAudio);
   types.push_back(SdpMediaSection::kVideo);
-  AddTracks(mSessionOff, "audio,video");
+  AddTracks(*mSessionOff, "audio,video");
   std::string offer = CreateOffer();
 
   std::map<std::string, bool> expected;
   expected["nack"] = false;
   expected["nack pli"] = false;
   expected["ccm fir"] = false;
 
   size_t prev = 0;
@@ -5022,205 +5170,261 @@ TEST_F(JsepSessionTest, RtcpFbInOffer)
   }
 }
 
 // In this test we will change the offer SDP's a=setup value
 // from actpass to passive. This will force the answer to do active.
 TEST_F(JsepSessionTest, AudioCallForceDtlsRoles)
 {
   types.push_back(SdpMediaSection::kAudio);
-  AddTracks(mSessionOff, "audio");
-  AddTracks(mSessionAns, "audio");
+  AddTracks(*mSessionOff, "audio");
+  AddTracks(*mSessionAns, "audio");
   std::string offer = CreateOffer();
 
   std::string actpass = "\r\na=setup:actpass";
   size_t match = offer.find(actpass);
   ASSERT_NE(match, std::string::npos);
   offer.replace(match, actpass.length(), "\r\na=setup:passive");
 
   SetLocalOffer(offer);
   SetRemoteOffer(offer);
-  ASSERT_EQ(kJsepStateHaveRemoteOffer, mSessionAns.GetState());
+  ASSERT_EQ(kJsepStateHaveRemoteOffer, mSessionAns->GetState());
   std::string answer = CreateAnswer();
   match = answer.find("\r\na=setup:active");
   ASSERT_NE(match, std::string::npos);
 
   SetLocalAnswer(answer);
   SetRemoteAnswer(answer);
-  ASSERT_EQ(kJsepStateStable, mSessionAns.GetState());
+  ASSERT_EQ(kJsepStateStable, mSessionAns->GetState());
 }
 
 // In this test we will change the offer SDP's a=setup value
 // from actpass to active. This will force the answer to do passive.
 TEST_F(JsepSessionTest, AudioCallReverseDtlsRoles)
 {
   types.push_back(SdpMediaSection::kAudio);
-  AddTracks(mSessionOff, "audio");
-  AddTracks(mSessionAns, "audio");
+  AddTracks(*mSessionOff, "audio");
+  AddTracks(*mSessionAns, "audio");
   std::string offer = CreateOffer();
 
   std::string actpass = "\r\na=setup:actpass";
   size_t match = offer.find(actpass);
   ASSERT_NE(match, std::string::npos);
   offer.replace(match, actpass.length(), "\r\na=setup:active");
 
   SetLocalOffer(offer);
   SetRemoteOffer(offer);
-  ASSERT_EQ(kJsepStateHaveRemoteOffer, mSessionAns.GetState());
+  ASSERT_EQ(kJsepStateHaveRemoteOffer, mSessionAns->GetState());
   std::string answer = CreateAnswer();
   match = answer.find("\r\na=setup:passive");
   ASSERT_NE(match, std::string::npos);
 
   SetLocalAnswer(answer);
   SetRemoteAnswer(answer);
-  ASSERT_EQ(kJsepStateStable, mSessionAns.GetState());
+  ASSERT_EQ(kJsepStateStable, mSessionAns->GetState());
 }
 
 // In this test we will change the answer SDP's a=setup value
 // from active to passive.  This will make both sides do
 // active and should not connect.
 TEST_F(JsepSessionTest, AudioCallMismatchDtlsRoles)
 {
   types.push_back(SdpMediaSection::kAudio);
-  AddTracks(mSessionOff, "audio");
-  AddTracks(mSessionAns, "audio");
+  AddTracks(*mSessionOff, "audio");
+  AddTracks(*mSessionAns, "audio");
   std::string offer = CreateOffer();
 
   std::string actpass = "\r\na=setup:actpass";
   size_t match = offer.find(actpass);
   ASSERT_NE(match, std::string::npos);
   SetLocalOffer(offer);
   SetRemoteOffer(offer);
-  ASSERT_EQ(kJsepStateHaveRemoteOffer, mSessionAns.GetState());
+  ASSERT_EQ(kJsepStateHaveRemoteOffer, mSessionAns->GetState());
   std::string answer = CreateAnswer();
   SetLocalAnswer(answer);
 
   std::string active = "\r\na=setup:active";
   match = answer.find(active);
   ASSERT_NE(match, std::string::npos);
   answer.replace(match, active.length(), "\r\na=setup:passive");
   SetRemoteAnswer(answer);
 
   // This is as good as it gets in a JSEP test (w/o starting DTLS)
   ASSERT_EQ(JsepDtlsTransport::kJsepDtlsClient,
-      mSessionOff.GetTransports()[0]->mDtls->GetRole());
+      mSessionOff->GetTransports()[0]->mDtls->GetRole());
   ASSERT_EQ(JsepDtlsTransport::kJsepDtlsClient,
-      mSessionAns.GetTransports()[0]->mDtls->GetRole());
+      mSessionAns->GetTransports()[0]->mDtls->GetRole());
 }
 
 // Verify that missing a=setup in offer gets rejected
 TEST_F(JsepSessionTest, AudioCallOffererNoSetup)
 {
   types.push_back(SdpMediaSection::kAudio);
-  AddTracks(mSessionOff, "audio");
-  AddTracks(mSessionAns, "audio");
+  AddTracks(*mSessionOff, "audio");
+  AddTracks(*mSessionAns, "audio");
   std::string offer = CreateOffer();
   SetLocalOffer(offer);
 
   std::string actpass = "\r\na=setup:actpass";
   size_t match = offer.find(actpass);
   ASSERT_NE(match, std::string::npos);
   offer.replace(match, actpass.length(), "");
 
   // The signaling state will remain "stable" because the unparsable
   // SDP leads to a failure in SetRemoteDescription.
   SetRemoteOffer(offer, NO_CHECKS);
-  ASSERT_EQ(kJsepStateStable, mSessionAns.GetState());
-  ASSERT_EQ(kJsepStateHaveLocalOffer, mSessionOff.GetState());
+  ASSERT_EQ(kJsepStateStable, mSessionAns->GetState());
+  ASSERT_EQ(kJsepStateHaveLocalOffer, mSessionOff->GetState());
 }
 
 // In this test we will change the answer SDP to remove the
 // a=setup line, which results in active being assumed.
 TEST_F(JsepSessionTest, AudioCallAnswerNoSetup)
 {
   types.push_back(SdpMediaSection::kAudio);
-  AddTracks(mSessionOff, "audio");
-  AddTracks(mSessionAns, "audio");
+  AddTracks(*mSessionOff, "audio");
+  AddTracks(*mSessionAns, "audio");
   std::string offer = CreateOffer();
   size_t match = offer.find("\r\na=setup:actpass");
   ASSERT_NE(match, std::string::npos);
 
   SetLocalOffer(offer);
   SetRemoteOffer(offer);
-  ASSERT_EQ(kJsepStateHaveRemoteOffer, mSessionAns.GetState());
+  ASSERT_EQ(kJsepStateHaveRemoteOffer, mSessionAns->GetState());
   std::string answer = CreateAnswer();
   SetLocalAnswer(answer);
 
   std::string active = "\r\na=setup:active";
   match = answer.find(active);
   ASSERT_NE(match, std::string::npos);
   answer.replace(match, active.length(), "");
   SetRemoteAnswer(answer);
-  ASSERT_EQ(kJsepStateStable, mSessionAns.GetState());
+  ASSERT_EQ(kJsepStateStable, mSessionAns->GetState());
 
   // This is as good as it gets in a JSEP test (w/o starting DTLS)
   ASSERT_EQ(JsepDtlsTransport::kJsepDtlsServer,
-      mSessionOff.GetTransports()[0]->mDtls->GetRole());
+      mSessionOff->GetTransports()[0]->mDtls->GetRole());
   ASSERT_EQ(JsepDtlsTransport::kJsepDtlsClient,
-      mSessionAns.GetTransports()[0]->mDtls->GetRole());
+      mSessionAns->GetTransports()[0]->mDtls->GetRole());
 }
 
 // Verify that 'holdconn' gets rejected
 TEST_F(JsepSessionTest, AudioCallDtlsRoleHoldconn)
 {
   types.push_back(SdpMediaSection::kAudio);
-  AddTracks(mSessionOff, "audio");
-  AddTracks(mSessionAns, "audio");
+  AddTracks(*mSessionOff, "audio");
+  AddTracks(*mSessionAns, "audio");
   std::string offer = CreateOffer();
   SetLocalOffer(offer);
 
   std::string actpass = "\r\na=setup:actpass";
   size_t match = offer.find(actpass);
   ASSERT_NE(match, std::string::npos);
   offer.replace(match, actpass.length(), "\r\na=setup:holdconn");
 
   // The signaling state will remain "stable" because the unparsable
   // SDP leads to a failure in SetRemoteDescription.
   SetRemoteOffer(offer, NO_CHECKS);
-  ASSERT_EQ(kJsepStateStable, mSessionAns.GetState());
-  ASSERT_EQ(kJsepStateHaveLocalOffer, mSessionOff.GetState());
+  ASSERT_EQ(kJsepStateStable, mSessionAns->GetState());
+  ASSERT_EQ(kJsepStateHaveLocalOffer, mSessionOff->GetState());
 }
 
 // Verify that 'actpass' in answer gets rejected
 TEST_F(JsepSessionTest, AudioCallAnswererUsesActpass)
 {
   types.push_back(SdpMediaSection::kAudio);
-  AddTracks(mSessionOff, "audio");
-  AddTracks(mSessionAns, "audio");
+  AddTracks(*mSessionOff, "audio");
+  AddTracks(*mSessionAns, "audio");
   std::string offer = CreateOffer();
   SetLocalOffer(offer);
   SetRemoteOffer(offer);
   std::string answer = CreateAnswer();
   SetLocalAnswer(answer);
 
   std::string active = "\r\na=setup:active";
   size_t match = answer.find(active);
   ASSERT_NE(match, std::string::npos);
   answer.replace(match, active.length(), "\r\na=setup:actpass");
 
   // The signaling state will remain "stable" because the unparsable
   // SDP leads to a failure in SetRemoteDescription.
   SetRemoteAnswer(answer, NO_CHECKS);
-  ASSERT_EQ(kJsepStateStable, mSessionAns.GetState());
-  ASSERT_EQ(kJsepStateHaveLocalOffer, mSessionOff.GetState());
+  ASSERT_EQ(kJsepStateStable, mSessionAns->GetState());
+  ASSERT_EQ(kJsepStateHaveLocalOffer, mSessionOff->GetState());
+}
+
+// Disabled: See Bug 1329028
+TEST_F(JsepSessionTest, DISABLED_AudioCallOffererAttemptsSetupRoleSwitch)
+{
+  types.push_back(SdpMediaSection::kAudio);
+  AddTracks(*mSessionOff, "audio");
+  AddTracks(*mSessionAns, "audio");
+
+  OfferAnswer();
+
+  ValidateSetupAttribute(*mSessionOff, SdpSetupAttribute::kActpass);
+  ValidateSetupAttribute(*mSessionAns, SdpSetupAttribute::kActive);
+
+  std::string reoffer = CreateOffer();
+  SetLocalOffer(reoffer);
+
+  std::string actpass = "\r\na=setup:actpass";
+  size_t match = reoffer.find(actpass);
+  ASSERT_NE(match, std::string::npos);
+  reoffer.replace(match, actpass.length(), "\r\na=setup:active");
+
+  // This is expected to fail.
+  SetRemoteOffer(reoffer, NO_CHECKS);
+  ASSERT_EQ(kJsepStateHaveLocalOffer, mSessionOff->GetState());
+  ASSERT_EQ(kJsepStateStable, mSessionAns->GetState());
+}
+
+// Disabled: See Bug 1329028
+TEST_F(JsepSessionTest, DISABLED_AudioCallAnswererAttemptsSetupRoleSwitch)
+{
+  types.push_back(SdpMediaSection::kAudio);
+  AddTracks(*mSessionOff, "audio");
+  AddTracks(*mSessionAns, "audio");
+
+  OfferAnswer();
+
+  ValidateSetupAttribute(*mSessionOff, SdpSetupAttribute::kActpass);
+  ValidateSetupAttribute(*mSessionAns, SdpSetupAttribute::kActive);
+
+  std::string reoffer = CreateOffer();
+  SetLocalOffer(reoffer);
+  SetRemoteOffer(reoffer);
+
+  std::string reanswer = CreateAnswer();
+  SetLocalAnswer(reanswer);
+
+  std::string actpass = "\r\na=setup:active";
+  size_t match = reanswer.find(actpass);
+  ASSERT_NE(match, std::string::npos);
+  reanswer.replace(match, actpass.length(), "\r\na=setup:passive");
+
+  // This is expected to fail.
+  SetRemoteAnswer(reanswer, NO_CHECKS);
+  ASSERT_EQ(kJsepStateHaveLocalOffer, mSessionOff->GetState());
+  ASSERT_EQ(kJsepStateStable, mSessionAns->GetState());
 }
 
 // Remove H.264 P1 and VP8 from offer, check answer negotiates H.264 P0
 TEST_F(JsepSessionTest, OfferWithOnlyH264P0)
 {
-  for (JsepCodecDescription* codec : mSessionOff.Codecs()) {
+  for (JsepCodecDescription* codec : mSessionOff->Codecs()) {
     if (codec->mName != "H264" || codec->mDefaultPt == "126") {
       codec->mEnabled = false;
     }
   }
 
   types.push_back(SdpMediaSection::kAudio);
   types.push_back(SdpMediaSection::kVideo);
-  AddTracks(mSessionOff, "audio,video");
-  AddTracks(mSessionAns, "audio,video");
+  AddTracks(*mSessionOff, "audio,video");
+  AddTracks(*mSessionAns, "audio,video");
   std::string offer = CreateOffer();
 
   ASSERT_EQ(offer.find("a=rtpmap:126 H264/90000"), std::string::npos);
   ASSERT_EQ(offer.find("a=rtpmap:120 VP8/90000"), std::string::npos);
 
   SetLocalOffer(offer);
   SetRemoteOffer(offer);
   std::string answer = CreateAnswer();
@@ -5240,23 +5444,23 @@ TEST_F(JsepSessionTest, OfferWithOnlyH26
 }
 
 // Test negotiating an answer which has only H.264 P1
 // Which means replace VP8 with H.264 P1 in answer
 TEST_F(JsepSessionTest, AnswerWithoutVP8)
 {
   types.push_back(SdpMediaSection::kAudio);
   types.push_back(SdpMediaSection::kVideo);
-  AddTracks(mSessionOff, "audio,video");
-  AddTracks(mSessionAns, "audio,video");
+  AddTracks(*mSessionOff, "audio,video");
+  AddTracks(*mSessionAns, "audio,video");
   std::string offer = CreateOffer();
   SetLocalOffer(offer);
   SetRemoteOffer(offer);
 
-  for (JsepCodecDescription* codec : mSessionOff.Codecs()) {
+  for (JsepCodecDescription* codec : mSessionOff->Codecs()) {
     if (codec->mName != "H264" || codec->mDefaultPt == "126") {
       codec->mEnabled = false;
     }
   }
 
   std::string answer = CreateAnswer();
 
   SetLocalAnswer(answer);
--- a/netwerk/cookie/nsCookieService.cpp
+++ b/netwerk/cookie/nsCookieService.cpp
@@ -1469,16 +1469,20 @@ nsCookieService::TryInitDB(bool aRecreat
 
         rv = CreateTable();
         NS_ENSURE_SUCCESS(rv, RESULT_RETRY);
       }
       break;
     }
   }
 
+  // make operations on the table asynchronous, for performance
+  mDefaultDBState->dbConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "PRAGMA synchronous = OFF"));
+
   // Use write-ahead-logging for performance. We cap the autocheckpoint limit at
   // 16 pages (around 500KB).
   mDefaultDBState->dbConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
     MOZ_STORAGE_UNIQUIFY_QUERY_STR "PRAGMA journal_mode = WAL"));
   mDefaultDBState->dbConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
     "PRAGMA wal_autocheckpoint = 16"));
 
   // cache frequently used statements (for insertion, deletion, and updating)
--- a/npm-shrinkwrap.json
+++ b/npm-shrinkwrap.json
@@ -1,835 +1,678 @@
 {
   "name": "mozillaeslintsetup",
+  "lockfileVersion": 1,
   "dependencies": {
     "acorn": {
-      "version": "5.0.3",
-      "from": "acorn@>=5.0.1 <6.0.0",
-      "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.0.3.tgz"
+      "version": "https://registry.npmjs.org/acorn/-/acorn-5.0.3.tgz",
+      "integrity": "sha1-xGDfCEkUY/AozLguqzcwvwEIez0="
     },
     "acorn-jsx": {
-      "version": "3.0.1",
-      "from": "acorn-jsx@>=3.0.0 <4.0.0",
-      "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-3.0.1.tgz",
+      "version": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-3.0.1.tgz",
+      "integrity": "sha1-r9+UiPsezvyDSPb7IvRk4ypYs2s=",
       "dependencies": {
         "acorn": {
-          "version": "3.3.0",
-          "from": "acorn@>=3.0.4 <4.0.0",
-          "resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz"
+          "version": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz",
+          "integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo="
         }
       }
     },
     "ajv": {
-      "version": "4.11.8",
-      "from": "ajv@>=4.7.0 <5.0.0",
-      "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz"
+      "version": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz",
+      "integrity": "sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY="
     },
     "ajv-keywords": {
-      "version": "1.5.1",
-      "from": "ajv-keywords@>=1.0.0 <2.0.0",
-      "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-1.5.1.tgz"
+      "version": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-1.5.1.tgz",
+      "integrity": "sha1-MU3QpLM2j609/NxU7eYXG4htrzw="
     },
     "ansi-escapes": {
-      "version": "1.4.0",
-      "from": "ansi-escapes@>=1.1.0 <2.0.0",
-      "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-1.4.0.tgz"
+      "version": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-1.4.0.tgz",
+      "integrity": "sha1-06ioOzGapneTZisT52HHkRQiMG4="
     },
     "ansi-regex": {
-      "version": "2.1.1",
-      "from": "ansi-regex@>=2.0.0 <3.0.0",
-      "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz"
+      "version": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
+      "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8="
     },
     "ansi-styles": {
-      "version": "2.2.1",
-      "from": "ansi-styles@>=2.2.1 <3.0.0",
-      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz"
+      "version": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
+      "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4="
     },
     "argparse": {
-      "version": "1.0.9",
-      "from": "argparse@>=1.0.7 <2.0.0",
-      "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.9.tgz"
+      "version": "https://registry.npmjs.org/argparse/-/argparse-1.0.9.tgz",
+      "integrity": "sha1-c9g7wmP4bpf4zE9rrhsOkKfSLIY="
     },
     "array-union": {
-      "version": "1.0.2",
-      "from": "array-union@>=1.0.1 <2.0.0",
-      "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz"
+      "version": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz",
+      "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk="
     },
     "array-uniq": {
-      "version": "1.0.3",
-      "from": "array-uniq@>=1.0.1 <2.0.0",
-      "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz"
+      "version": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz",
+      "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY="
     },
     "array.prototype.find": {
-      "version": "2.0.4",
-      "from": "array.prototype.find@>=2.0.1 <3.0.0",
-      "resolved": "https://registry.npmjs.org/array.prototype.find/-/array.prototype.find-2.0.4.tgz"
+      "version": "https://registry.npmjs.org/array.prototype.find/-/array.prototype.find-2.0.4.tgz",
+      "integrity": "sha1-VWpcU2LAhkgyPdrrnenRS8GGTJA="
     },
     "arrify": {
-      "version": "1.0.1",
-      "from": "arrify@>=1.0.0 <2.0.0",
-      "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz"
+      "version": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz",
+      "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0="
     },
     "babel-code-frame": {
-      "version": "6.22.0",
-      "from": "babel-code-frame@>=6.16.0 <7.0.0",
-      "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.22.0.tgz"
+      "version": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.22.0.tgz",
+      "integrity": "sha1-AnYgvuVnqIwyVhV05/0IAdMxGOQ="
     },
     "balanced-match": {
-      "version": "0.4.2",
-      "from": "balanced-match@>=0.4.1 <0.5.0",
-      "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz"
+      "version": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz",
+      "integrity": "sha1-yz8+PHMtwPAe5wtAPzAuYddwmDg="
     },
     "brace-expansion": {
-      "version": "1.1.7",
-      "from": "brace-expansion@>=1.1.7 <2.0.0",
-      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.7.tgz"
+      "version": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.7.tgz",
+      "integrity": "sha1-Pv/DxQ4ABTH7cg6v+A8K6O8jz1k="
     },
     "buffer-shims": {
-      "version": "1.0.0",
-      "from": "buffer-shims@>=1.0.0 <1.1.0",
-      "resolved": "https://registry.npmjs.org/buffer-shims/-/buffer-shims-1.0.0.tgz"
+      "version": "https://registry.npmjs.org/buffer-shims/-/buffer-shims-1.0.0.tgz",
+      "integrity": "sha1-mXjOMXOIxkmth5MCjDR37wRKi1E="
     },
     "caller-path": {
-      "version": "0.1.0",
-      "from": "caller-path@>=0.1.0 <0.2.0",
-      "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-0.1.0.tgz"
+      "version": "https://registry.npmjs.org/caller-path/-/caller-path-0.1.0.tgz",
+      "integrity": "sha1-lAhe9jWB7NPaqSREqP6U6CV3dR8="
     },
     "callsites": {
-      "version": "0.2.0",
-      "from": "callsites@>=0.2.0 <0.3.0",
-      "resolved": "https://registry.npmjs.org/callsites/-/callsites-0.2.0.tgz"
+      "version": "https://registry.npmjs.org/callsites/-/callsites-0.2.0.tgz",
+      "integrity": "sha1-r6uWJikQp/M8GaV3WCXGnzTjUMo="
     },
     "chalk": {
-      "version": "1.1.3",
-      "from": "chalk@>=1.1.3 <2.0.0",
-      "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz"
+      "version": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+      "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg="
     },
     "circular-json": {
-      "version": "0.3.1",
-      "from": "circular-json@>=0.3.1 <0.4.0",
-      "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.1.tgz"
+      "version": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.1.tgz",
+      "integrity": "sha1-vos2rvzN6LPKeqLWr8B6NyQsDS0="
     },
     "cli-cursor": {
-      "version": "1.0.2",
-      "from": "cli-cursor@>=1.0.1 <2.0.0",
-      "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-1.0.2.tgz"
+      "version": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-1.0.2.tgz",
+      "integrity": "sha1-ZNo/fValRBLll5S9Ytw1KV6PKYc="
     },
     "cli-width": {
-      "version": "2.1.0",
-      "from": "cli-width@>=2.0.0 <3.0.0",
-      "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.1.0.tgz"
+      "version": "https://registry.npmjs.org/cli-width/-/cli-width-2.1.0.tgz",
+      "integrity": "sha1-sjTKIJsp72b8UY2bmNWEewDt8Ao="
     },
     "co": {
-      "version": "4.6.0",
-      "from": "co@>=4.6.0 <5.0.0",
-      "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz"
+      "version": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
+      "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ="
     },
     "code-point-at": {
-      "version": "1.1.0",
-      "from": "code-point-at@>=1.0.0 <2.0.0",
-      "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz"
+      "version": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
+      "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c="
     },
     "concat-map": {
-      "version": "0.0.1",
-      "from": "concat-map@0.0.1",
-      "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz"
+      "version": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+      "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
     },
     "concat-stream": {
-      "version": "1.6.0",
-      "from": "concat-stream@>=1.5.2 <2.0.0",
-      "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.0.tgz"
+      "version": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.0.tgz",
+      "integrity": "sha1-CqxmL9Ur54lk1VMvaUeE5wEQrPc="
     },
     "core-util-is": {
-      "version": "1.0.2",
-      "from": "core-util-is@>=1.0.0 <1.1.0",
-      "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz"
+      "version": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
+      "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
     },
     "d": {
-      "version": "1.0.0",
-      "from": "d@>=1.0.0 <2.0.0",
-      "resolved": "https://registry.npmjs.org/d/-/d-1.0.0.tgz"
+      "version": "https://registry.npmjs.org/d/-/d-1.0.0.tgz",
+      "integrity": "sha1-dUu1v+VUUdpppYuU1F9MWwRi1Y8="
     },
     "debug": {
-      "version": "2.6.6",
-      "from": "debug@>=2.1.1 <3.0.0",
-      "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.6.tgz"
+      "version": "https://registry.npmjs.org/debug/-/debug-2.6.6.tgz",
+      "integrity": "sha1-qfpvvpykPPHnn3O3XAGJy7fW21o="
     },
     "deep-is": {
-      "version": "0.1.3",
-      "from": "deep-is@>=0.1.3 <0.2.0",
-      "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz"
+      "version": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz",
+      "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ="
     },
     "define-properties": {
-      "version": "1.1.2",
-      "from": "define-properties@>=1.1.2 <2.0.0",
-      "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.2.tgz"
+      "version": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.2.tgz",
+      "integrity": "sha1-g6c/L+pWmJj7c3GTyPhzyvbUXJQ="
     },
     "del": {
-      "version": "2.2.2",
-      "from": "del@>=2.0.2 <3.0.0",
-      "resolved": "https://registry.npmjs.org/del/-/del-2.2.2.tgz"
+      "version": "https://registry.npmjs.org/del/-/del-2.2.2.tgz",
+      "integrity": "sha1-wSyYHQZ4RshLyvhiz/kw2Qf/0ag="
     },
     "doctrine": {
-      "version": "2.0.0",
-      "from": "doctrine@>=2.0.0 <3.0.0",
-      "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.0.0.tgz"
+      "version": "https://registry.npmjs.org/doctrine/-/doctrine-2.0.0.tgz",
+      "integrity": "sha1-xz2NKQnSIpHhoAejlYBNqLZl/mM="
     },
     "dom-serializer": {
-      "version": "0.1.0",
-      "from": "dom-serializer@>=0.0.0 <1.0.0",
-      "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.0.tgz",
+      "version": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.0.tgz",
+      "integrity": "sha1-BzxpdUbOB4DOI75KKOKT5AvDDII=",
       "dependencies": {
         "domelementtype": {
-          "version": "1.1.3",
-          "from": "domelementtype@>=1.1.1 <1.2.0",
-          "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.1.3.tgz"
+          "version": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.1.3.tgz",
+          "integrity": "sha1-vSh3PiZCiBrsUVRJJCmcXNgiGFs="
         }
       }
     },
     "domelementtype": {
-      "version": "1.3.0",
-      "from": "domelementtype@>=1.3.0 <2.0.0",
-      "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.0.tgz"
+      "version": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.0.tgz",
+      "integrity": "sha1-sXrtguirWeUt2cGbF1bg/BhyBMI="
     },
     "domhandler": {
-      "version": "2.4.1",
-      "from": "domhandler@>=2.3.0 <3.0.0",
-      "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.1.tgz"
+      "version": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.1.tgz",
+      "integrity": "sha1-iS5HAAqZvlW783dP/qBWHYh5wlk="
     },
     "domutils": {
-      "version": "1.6.2",
-      "from": "domutils@>=1.5.1 <2.0.0",
-      "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.6.2.tgz"
+      "version": "https://registry.npmjs.org/domutils/-/domutils-1.6.2.tgz",
+      "integrity": "sha1-GVjMC0yUJuntNn+xyOhUiRsPo/8="
     },
     "entities": {
-      "version": "1.1.1",
-      "from": "entities@>=1.1.1 <2.0.0",
-      "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.1.tgz"
+      "version": "https://registry.npmjs.org/entities/-/entities-1.1.1.tgz",
+      "integrity": "sha1-blwtClYhtdra7O+AuQ7ftc13cvA="
     },
     "es-abstract": {
-      "version": "1.7.0",
-      "from": "es-abstract@>=1.7.0 <2.0.0",
-      "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.7.0.tgz"
+      "version": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.7.0.tgz",
+      "integrity": "sha1-363ndOAb/Nl/lhgCmMRJyGI/uUw="
     },
     "es-to-primitive": {
-      "version": "1.1.1",
-      "from": "es-to-primitive@>=1.1.1 <2.0.0",
-      "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.1.1.tgz"
+      "version": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.1.1.tgz",
+      "integrity": "sha1-RTVSSKiJeQNLZ5Lhm7gfK3l13Q0="
     },
     "es5-ext": {
-      "version": "0.10.15",
-      "from": "es5-ext@>=0.10.14 <0.11.0",
-      "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.15.tgz"
+      "version": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.15.tgz",
+      "integrity": "sha1-wzClk0we4hKEp8CBqG5f2TfJHqY="
     },
     "es6-iterator": {
-      "version": "2.0.1",
-      "from": "es6-iterator@>=2.0.1 <2.1.0",
-      "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.1.tgz"
+      "version": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.1.tgz",
+      "integrity": "sha1-jjGcnwRTv1ddN0lAplWSDlnKVRI="
     },
     "es6-map": {
-      "version": "0.1.5",
-      "from": "es6-map@>=0.1.3 <0.2.0",
-      "resolved": "https://registry.npmjs.org/es6-map/-/es6-map-0.1.5.tgz"
+      "version": "https://registry.npmjs.org/es6-map/-/es6-map-0.1.5.tgz",
+      "integrity": "sha1-kTbgUD3MBqMBaQ8LsU/042TpSfA="
     },
     "es6-set": {
-      "version": "0.1.5",
-      "from": "es6-set@>=0.1.5 <0.2.0",
-      "resolved": "https://registry.npmjs.org/es6-set/-/es6-set-0.1.5.tgz"
+      "version": "https://registry.npmjs.org/es6-set/-/es6-set-0.1.5.tgz",
+      "integrity": "sha1-0rPsXU2ADO2BjbU40ol02wpzzLE="
     },
     "es6-symbol": {
-      "version": "3.1.1",
-      "from": "es6-symbol@>=3.1.1 <3.2.0",
-      "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.1.tgz"
+      "version": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.1.tgz",
+      "integrity": "sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc="
     },
     "es6-weak-map": {
-      "version": "2.0.2",
-      "from": "es6-weak-map@>=2.0.1 <3.0.0",
-      "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.2.tgz"
+      "version": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.2.tgz",
+      "integrity": "sha1-XjqzIlH/0VOKH45f+hNXdy+S2W8="
     },
     "escape-string-regexp": {
-      "version": "1.0.5",
-      "from": "escape-string-regexp@>=1.0.2 <2.0.0",
-      "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz"
+      "version": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+      "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ="
     },
     "escope": {
-      "version": "3.6.0",
-      "from": "escope@>=3.6.0 <4.0.0",
-      "resolved": "https://registry.npmjs.org/escope/-/escope-3.6.0.tgz"
+      "version": "https://registry.npmjs.org/escope/-/escope-3.6.0.tgz",
+      "integrity": "sha1-4Bl16BJ4GhY6ba392AOY3GTIicM="
     },
     "eslint": {
-      "version": "3.19.0",
-      "from": "eslint@3.19.0",
-      "resolved": "https://registry.npmjs.org/eslint/-/eslint-3.19.0.tgz"
+      "version": "https://registry.npmjs.org/eslint/-/eslint-3.19.0.tgz",
+      "integrity": "sha1-yPxiAcf0DdCJQbh8CFdnOGpnmsw="
     },
     "eslint-plugin-html": {
-      "version": "2.0.3",
-      "from": "eslint-plugin-html@2.0.3",
-      "resolved": "https://registry.npmjs.org/eslint-plugin-html/-/eslint-plugin-html-2.0.3.tgz"
+      "version": "https://registry.npmjs.org/eslint-plugin-html/-/eslint-plugin-html-2.0.3.tgz",
+      "integrity": "sha1-fImIOrDIX6XSi2ZqFKTpBqqQuJc="
+    },
+    "eslint-plugin-mozilla": {
+      "version": "file:tools\\lint\\eslint\\eslint-plugin-mozilla"
     },
     "eslint-plugin-react": {
-      "version": "6.10.3",
-      "from": "eslint-plugin-react@6.10.3",
-      "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-6.10.3.tgz",
+      "version": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-6.10.3.tgz",
+      "integrity": "sha1-xUNb6wZ3ThLH2y9qut3L+QDNP3g=",
       "dependencies": {
         "doctrine": {
-          "version": "1.5.0",
-          "from": "doctrine@>=1.2.2 <2.0.0",
-          "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz"
+          "version": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz",
+          "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo="
         }
       }
     },
+    "eslint-plugin-spidermonkey-js": {
+      "version": "file:tools\\lint\\eslint\\eslint-plugin-spidermonkey-js"
+    },
     "espree": {
-      "version": "3.4.3",
-      "from": "espree@>=3.4.0 <4.0.0",
-      "resolved": "https://registry.npmjs.org/espree/-/espree-3.4.3.tgz"
+      "version": "https://registry.npmjs.org/espree/-/espree-3.4.3.tgz",
+      "integrity": "sha1-KRC1zNSc6JPC//+qtP2LOjG4I3Q="
     },
     "esprima": {
-      "version": "3.1.3",
-      "from": "esprima@>=3.1.1 <4.0.0",
-      "resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz"
+      "version": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz",
+      "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM="
     },
     "esquery": {
-      "version": "1.0.0",
-      "from": "esquery@>=1.0.0 <2.0.0",
-      "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.0.0.tgz"
+      "version": "https://registry.npmjs.org/esquery/-/esquery-1.0.0.tgz",
+      "integrity": "sha1-z7qLV9f7qT8XKYqKAGoEzaE9gPo="
     },
     "esrecurse": {
-      "version": "4.1.0",
-      "from": "esrecurse@>=4.1.0 <5.0.0",
-      "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.1.0.tgz",
+      "version": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.1.0.tgz",
+      "integrity": "sha1-RxO2U2rffyrE8yfVWed1a/9kgiA=",
       "dependencies": {
         "estraverse": {
-          "version": "4.1.1",
-          "from": "estraverse@>=4.1.0 <4.2.0",
-          "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.1.1.tgz"
+          "version": "https://registry.npmjs.org/estraverse/-/estraverse-4.1.1.tgz",
+          "integrity": "sha1-9srKcokzqFDvkGYdDheYK6RxEaI="
         }
       }
     },
     "estraverse": {
-      "version": "4.2.0",
-      "from": "estraverse@>=4.2.0 <5.0.0",
-      "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz"
+      "version": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz",
+      "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM="
     },
     "esutils": {
-      "version": "2.0.2",
-      "from": "esutils@>=2.0.2 <3.0.0",
-      "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz"
+      "version": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz",
+      "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs="
     },
     "event-emitter": {
-      "version": "0.3.5",
-      "from": "event-emitter@>=0.3.5 <0.4.0",
-      "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz"
+      "version": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz",
+      "integrity": "sha1-34xp7vFkeSPHFXuc6DhAYQsCzDk="
     },
     "exit-hook": {
-      "version": "1.1.1",
-      "from": "exit-hook@>=1.0.0 <2.0.0",
-      "resolved": "https://registry.npmjs.org/exit-hook/-/exit-hook-1.1.1.tgz"
+      "version": "https://registry.npmjs.org/exit-hook/-/exit-hook-1.1.1.tgz",
+      "integrity": "sha1-8FyiM7SMBdVP/wd2XfhQfpXAL/g="
     },
     "fast-levenshtein": {
-      "version": "2.0.6",
-      "from": "fast-levenshtein@>=2.0.4 <2.1.0",
-      "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz"
+      "version": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
+      "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc="
     },
     "figures": {
-      "version": "1.7.0",
-      "from": "figures@>=1.3.5 <2.0.0",
-      "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz"
+      "version": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz",
+      "integrity": "sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4="
     },
     "file-entry-cache": {
-      "version": "2.0.0",
-      "from": "file-entry-cache@>=2.0.0 <3.0.0",
-      "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-2.0.0.tgz"
+      "version": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-2.0.0.tgz",
+      "integrity": "sha1-w5KZDD5oR4PYOLjISkXYoEhFg2E="
     },
     "flat-cache": {
-      "version": "1.2.2",
-      "from": "flat-cache@>=1.2.1 <2.0.0",
-      "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-1.2.2.tgz"
+      "version": "https://registry.npmjs.org/flat-cache/-/flat-cache-1.2.2.tgz",
+      "integrity": "sha1-+oZxTnLCHbiGAXYezy9VXRq8a5Y="
     },
     "foreach": {
-      "version": "2.0.5",
-      "from": "foreach@>=2.0.5 <3.0.0",
-      "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz"
+      "version": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz",
+      "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k="
     },
     "fs.realpath": {
-      "version": "1.0.0",
-      "from": "fs.realpath@>=1.0.0 <2.0.0",
-      "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz"
+      "version": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+      "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
     },
     "function-bind": {
-      "version": "1.1.0",
-      "from": "function-bind@>=1.1.0 <2.0.0",
-      "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.0.tgz"
+      "version": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.0.tgz",
+      "integrity": "sha1-FhdnFMgBeY5Ojyz391KUZ7tKV3E="
     },
     "generate-function": {
-      "version": "2.0.0",
-      "from": "generate-function@>=2.0.0 <3.0.0",
-      "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.0.0.tgz"
+      "version": "https://registry.npmjs.org/generate-function/-/generate-function-2.0.0.tgz",
+      "integrity": "sha1-aFj+fAlpt9TpCTM3ZHrHn2DfvnQ="
     },
     "generate-object-property": {
-      "version": "1.2.0",
-      "from": "generate-object-property@>=1.1.0 <2.0.0",
-      "resolved": "https://registry.npmjs.org/generate-object-property/-/generate-object-property-1.2.0.tgz"
+      "version": "https://registry.npmjs.org/generate-object-property/-/generate-object-property-1.2.0.tgz",
+      "integrity": "sha1-nA4cQDCM6AT0eDYYuTf6iPmdUNA="
     },
     "glob": {
-      "version": "7.1.1",
-      "from": "glob@>=7.0.3 <8.0.0",
-      "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.1.tgz"
+      "version": "https://registry.npmjs.org/glob/-/glob-7.1.1.tgz",
+      "integrity": "sha1-gFIR3wT6rxxjo2ADBs31reULLsg="
     },
     "globals": {
-      "version": "9.17.0",
-      "from": "globals@>=9.14.0 <10.0.0",
-      "resolved": "https://registry.npmjs.org/globals/-/globals-9.17.0.tgz"
+      "version": "https://registry.npmjs.org/globals/-/globals-9.17.0.tgz",
+      "integrity": "sha1-DAymltm5u2lNLlRwvTd3fKrVAoY="
     },
     "globby": {
-      "version": "5.0.0",
-      "from": "globby@>=5.0.0 <6.0.0",
-      "resolved": "https://registry.npmjs.org/globby/-/globby-5.0.0.tgz"
+      "version": "https://registry.npmjs.org/globby/-/globby-5.0.0.tgz",
+      "integrity": "sha1-69hGZ8oNuzMLmbz8aOrCvFQ3Dg0="
     },
     "graceful-fs": {
-      "version": "4.1.11",
-      "from": "graceful-fs@>=4.1.2 <5.0.0",
-      "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz"
+      "version": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz",
+      "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg="
     },
     "has": {
-      "version": "1.0.1",
-      "from": "has@>=1.0.1 <2.0.0",
-      "resolved": "https://registry.npmjs.org/has/-/has-1.0.1.tgz"
+      "version": "https://registry.npmjs.org/has/-/has-1.0.1.tgz",
+      "integrity": "sha1-hGFzP1OLCDfJNh45qauelwTcLyg="
     },
     "has-ansi": {
-      "version": "2.0.0",
-      "from": "has-ansi@>=2.0.0 <3.0.0",
-      "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz"
+      "version": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
+      "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE="
     },
     "htmlparser2": {
-      "version": "3.9.2",
-      "from": "htmlparser2@>=3.8.2 <4.0.0",
-      "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.9.2.tgz"
+      "version": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.9.2.tgz",
+      "integrity": "sha1-G9+HrMoPP55T+k/M6w9LTLsAszg="
     },
     "ignore": {
-      "version": "3.3.0",
-      "from": "ignore@>=3.2.0 <4.0.0",
-      "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.0.tgz"
+      "version": "https://registry.npmjs.org/ignore/-/ignore-3.3.0.tgz",
+      "integrity": "sha1-OBLSLL6RJfLCtJFXVaG4q9dFoAE="
     },
     "imurmurhash": {
-      "version": "0.1.4",
-      "from": "imurmurhash@>=0.1.4 <0.2.0",
-      "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz"
+      "version": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
+      "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o="
     },
     "inflight": {
-      "version": "1.0.6",
-      "from": "inflight@>=1.0.4 <2.0.0",
-      "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz"
+      "version": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+      "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk="
     },
     "inherits": {
-      "version": "2.0.3",
-      "from": "inherits@>=2.0.3 <3.0.0",
-      "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz"
+      "version": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
+      "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
     },
     "ini-parser": {
-      "version": "0.0.2",
-      "from": "ini-parser@>=0.0.2 <0.0.3",
-      "resolved": "https://registry.npmjs.org/ini-parser/-/ini-parser-0.0.2.tgz"
+      "version": "https://registry.npmjs.org/ini-parser/-/ini-parser-0.0.2.tgz",
+      "integrity": "sha1-+kF4flZ3Y7P/Zdel2alO23QHh+8="
     },
     "inquirer": {
-      "version": "0.12.0",
-      "from": "inquirer@>=0.12.0 <0.13.0",
-      "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-0.12.0.tgz"
+      "version": "https://registry.npmjs.org/inquirer/-/inquirer-0.12.0.tgz",
+      "integrity": "sha1-HvK/1jUE3wvHV4X/+MLEHfEvB34="
     },
     "interpret": {
-      "version": "1.0.3",
-      "from": "interpret@>=1.0.0 <2.0.0",
-      "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.0.3.tgz"
+      "version": "https://registry.npmjs.org/interpret/-/interpret-1.0.3.tgz",
+      "integrity": "sha1-y8NcYu7uc/Gat7EKgBURQBr8D5A="
     },
     "is-callable": {
-      "version": "1.1.3",
-      "from": "is-callable@>=1.1.3 <2.0.0",
-      "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.3.tgz"
+      "version": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.3.tgz",
+      "integrity": "sha1-hut1OSgF3cM69xySoO7fdO52BLI="
     },
     "is-date-object": {
-      "version": "1.0.1",
-      "from": "is-date-object@>=1.0.1 <2.0.0",
-      "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz"
+      "version": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz",
+      "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY="
     },
     "is-fullwidth-code-point": {
-      "version": "1.0.0",
-      "from": "is-fullwidth-code-point@>=1.0.0 <2.0.0",
-      "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz"
+      "version": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
+      "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs="
     },
     "is-my-json-valid": {
-      "version": "2.16.0",
-      "from": "is-my-json-valid@>=2.10.0 <3.0.0",
-      "resolved": "https://registry.npmjs.org/is-my-json-valid/-/is-my-json-valid-2.16.0.tgz"
+      "version": "https://registry.npmjs.org/is-my-json-valid/-/is-my-json-valid-2.16.0.tgz",
+      "integrity": "sha1-8Hndm/2uZe4gOKrorLyGqxCeNpM="
     },
     "is-path-cwd": {
-      "version": "1.0.0",
-      "from": "is-path-cwd@>=1.0.0 <2.0.0",
-      "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz"
+      "version": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz",
+      "integrity": "sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0="
     },
     "is-path-in-cwd": {
-      "version": "1.0.0",
-      "from": "is-path-in-cwd@>=1.0.0 <2.0.0",
-      "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.0.tgz"
+      "version": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.0.tgz",
+      "integrity": "sha1-ZHdYK4IU1gI0YJRWcAO+ip6sBNw="
     },
     "is-path-inside": {
-      "version": "1.0.0",
-      "from": "is-path-inside@>=1.0.0 <2.0.0",
-      "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.0.tgz"
+      "version": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.0.tgz",
+      "integrity": "sha1-/AbloWg/vaE95mev9xe7wQpI838="
     },
     "is-property": {
-      "version": "1.0.2",
-      "from": "is-property@>=1.0.0 <2.0.0",
-      "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz"
+      "version": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz",
+      "integrity": "sha1-V/4cTkhHTt1lsJkR8msc1Ald2oQ="
     },
     "is-regex": {
-      "version": "1.0.4",
-      "from": "is-regex@>=1.0.3 <2.0.0",
-      "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz"
+      "version": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz",
+      "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE="
     },
     "is-resolvable": {
-      "version": "1.0.0",
-      "from": "is-resolvable@>=1.0.0 <2.0.0",
-      "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.0.0.tgz"
+      "version": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.0.0.tgz",
+      "integrity": "sha1-jfV8YeouPFAUCNEA+wE8+NbgzGI="
     },
     "is-symbol": {
-      "version": "1.0.1",
-      "from": "is-symbol@>=1.0.1 <2.0.0",
-      "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.1.tgz"
+      "version": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.1.tgz",
+      "integrity": "sha1-PMWfAAJRlLarLjjbrmaJJWtmBXI="
     },
     "isarray": {
-      "version": "1.0.0",
-      "from": "isarray@>=1.0.0 <1.1.0",
-      "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz"
+      "version": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+      "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
     },
     "js-tokens": {
-      "version": "3.0.1",
-      "from": "js-tokens@>=3.0.0 <4.0.0",
-      "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.1.tgz"
+      "version": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.1.tgz",
+      "integrity": "sha1-COnxMkhKLEWjCQfp3E1VZ7fxFNc="
     },
     "js-yaml": {
-      "version": "3.8.3",
-      "from": "js-yaml@>=3.5.1 <4.0.0",
-      "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.8.3.tgz"
+      "version": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.8.3.tgz",
+      "integrity": "sha1-M6BexIHIUMiHWSkWb+G+thxyh2Y="
     },
     "json-stable-stringify": {
-      "version": "1.0.1",
-      "from": "json-stable-stringify@>=1.0.0 <2.0.0",
-      "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz"
+      "version": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz",
+      "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8="
     },
     "jsonify": {
-      "version": "0.0.0",
-      "from": "jsonify@>=0.0.0 <0.1.0",
-      "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz"
+      "version": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz",
+      "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM="
     },
     "jsonpointer": {
-      "version": "4.0.1",
-      "from": "jsonpointer@>=4.0.0 <5.0.0",
-      "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-4.0.1.tgz"
+      "version": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-4.0.1.tgz",
+      "integrity": "sha1-T9kss04OnbPInIYi7PUfm5eMbLk="
     },
     "jsx-ast-utils": {
-      "version": "1.4.1",
-      "from": "jsx-ast-utils@>=1.3.4 <2.0.0",
-      "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-1.4.1.tgz"
+      "version": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-1.4.1.tgz",
+      "integrity": "sha1-OGchPo3Xm/Ho8jAMDPwe+xgsDfE="
     },
     "levn": {
-      "version": "0.3.0",
-      "from": "levn@>=0.3.0 <0.4.0",
-      "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz"
+      "version": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz",
+      "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4="
     },
     "lodash": {
-      "version": "4.17.4",
-      "from": "lodash@>=4.0.0 <5.0.0",
-      "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz"
+      "version": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz",
+      "integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4="
     },
     "minimatch": {
-      "version": "3.0.4",
-      "from": "minimatch@>=3.0.2 <4.0.0",
-      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz"
+      "version": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
+      "integrity": "sha1-UWbihkV/AzBgZL5Ul+jbsMPTIIM="
     },
     "minimist": {
-      "version": "0.0.8",
-      "from": "minimist@0.0.8",
-      "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz"
+      "version": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
+      "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0="
     },
     "mkdirp": {
-      "version": "0.5.1",
-      "from": "mkdirp@>=0.5.0 <0.6.0",
-      "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz"
+      "version": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
+      "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM="
     },
     "ms": {
-      "version": "0.7.3",
-      "from": "ms@0.7.3",
-      "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.3.tgz"
+      "version": "https://registry.npmjs.org/ms/-/ms-0.7.3.tgz",
+      "integrity": "sha1-cIFVpeROM/X9D8U+gdDUCpG+H/8="
     },
     "mute-stream": {
-      "version": "0.0.5",
-      "from": "mute-stream@0.0.5",
-      "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.5.tgz"
+      "version": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.5.tgz",
+      "integrity": "sha1-j7+rsKmKJT0xhDMfno3rc3L6xsA="
     },
     "natural-compare": {
-      "version": "1.4.0",
-      "from": "natural-compare@>=1.4.0 <2.0.0",
-      "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz"
+      "version": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
+      "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc="
     },
     "number-is-nan": {
-      "version": "1.0.1",
-      "from": "number-is-nan@>=1.0.0 <2.0.0",
-      "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz"
+      "version": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
+      "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0="
     },
     "object-assign": {
-      "version": "4.1.1",
-      "from": "object-assign@>=4.0.1 <5.0.0",
-      "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz"
+      "version": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+      "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM="
     },
     "object-keys": {
-      "version": "1.0.11",
-      "from": "object-keys@>=1.0.8 <2.0.0",
-      "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.11.tgz"
+      "version": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.11.tgz",
+      "integrity": "sha1-xUYBd4rVYPEULODgG8yotW0TQm0="
     },
     "object.assign": {
-      "version": "4.0.4",
-      "from": "object.assign@>=4.0.4 <5.0.0",
-      "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.0.4.tgz"
+      "version": "https://registry.npmjs.org/object.assign/-/object.assign-4.0.4.tgz",
+      "integrity": "sha1-scnMBE7xuf5jYG/BQau7MuFHMMw="
     },
     "once": {
-      "version": "1.4.0",
-      "from": "once@>=1.3.0 <2.0.0",
-      "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz"
+      "version": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+      "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E="
     },
     "onetime": {
-      "version": "1.1.0",
-      "from": "onetime@>=1.0.0 <2.0.0",
-      "resolved": "https://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz"
+      "version": "https://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz",
+      "integrity": "sha1-ofeDj4MUxRbwXs78vEzP4EtO14k="
     },
     "optionator": {
-      "version": "0.8.2",
-      "from": "optionator@>=0.8.2 <0.9.0",
-      "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz"
+      "version": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz",
+      "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q="
     },
     "os-homedir": {
-      "version": "1.0.2",
-      "from": "os-homedir@>=1.0.0 <2.0.0",
-      "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz"
+      "version": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz",
+      "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M="
     },
     "path-is-absolute": {
-      "version": "1.0.1",
-      "from": "path-is-absolute@>=1.0.0 <2.0.0",
-      "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz"
+      "version": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+      "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18="
     },
     "path-is-inside": {
-      "version": "1.0.2",
-      "from": "path-is-inside@>=1.0.1 <2.0.0",
-      "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz"
+      "version": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz",
+      "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM="
     },
     "path-parse": {
-      "version": "1.0.5",
-      "from": "path-parse@>=1.0.5 <2.0.0",
-      "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.5.tgz"
+      "version": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.5.tgz",
+      "integrity": "sha1-PBrfhx6pzWyUMbbqK9dKD/BVxME="
     },
     "pify": {
-      "version": "2.3.0",
-      "from": "pify@>=2.0.0 <3.0.0",
-      "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz"
+      "version": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+      "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw="
     },
     "pinkie": {
-      "version": "2.0.4",
-      "from": "pinkie@>=2.0.0 <3.0.0",
-      "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz"
+      "version": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz",
+      "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA="
     },
     "pinkie-promise": {
-      "version": "2.0.1",
-      "from": "pinkie-promise@>=2.0.0 <3.0.0",
-      "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz"
+      "version": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz",
+      "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o="
     },
     "pluralize": {
-      "version": "1.2.1",
-      "from": "pluralize@>=1.2.1 <2.0.0",
-      "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-1.2.1.tgz"
+      "version": "https://registry.npmjs.org/pluralize/-/pluralize-1.2.1.tgz",
+      "integrity": "sha1-0aIUg/0iu0HlihL6NCGCMUCJfEU="
     },
     "prelude-ls": {
-      "version": "1.1.2",
-      "from": "prelude-ls@>=1.1.2 <1.2.0",
-      "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz"
+      "version": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz",
+      "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ="
     },
     "process-nextick-args": {
-      "version": "1.0.7",
-      "from": "process-nextick-args@>=1.0.6 <1.1.0",
-      "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz"
+      "version": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz",
+      "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M="
     },
     "progress": {
-      "version": "1.1.8",
-      "from": "progress@>=1.1.8 <2.0.0",
-      "resolved": "https://registry.npmjs.org/progress/-/progress-1.1.8.tgz"
+      "version": "https://registry.npmjs.org/progress/-/progress-1.1.8.tgz",
+      "integrity": "sha1-4mDHj2Fhzdmw5WzD4Khd4Xx6V74="
     },
     "readable-stream": {
-      "version": "2.2.9",
-      "from": "readable-stream@>=2.2.2 <3.0.0",
-      "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.9.tgz"
+      "version": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.9.tgz",
+      "integrity": "sha1-z3jsb0ptHrQ9JkiMrJfwQudLf8g="
     },
     "readline2": {
-      "version": "1.0.1",
-      "from": "readline2@>=1.0.1 <2.0.0",
-      "resolved": "https://registry.npmjs.org/readline2/-/readline2-1.0.1.tgz"
+      "version": "https://registry.npmjs.org/readline2/-/readline2-1.0.1.tgz",
+      "integrity": "sha1-QQWWCP/BVHV7cV2ZidGZ/783LjU="
     },
     "rechoir": {
-      "version": "0.6.2",
-      "from": "rechoir@>=0.6.2 <0.7.0",
-      "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz"
+      "version": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz",
+      "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q="
     },
     "require-uncached": {
-      "version": "1.0.3",
-      "from": "require-uncached@>=1.0.2 <2.0.0",
-      "resolved": "https://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz"
+      "version": "https://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz",
+      "integrity": "sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM="
     },
     "resolve": {
-      "version": "1.3.3",
-      "from": "resolve@>=1.1.6 <2.0.0",
-      "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.3.3.tgz"
+      "version": "https://registry.npmjs.org/resolve/-/resolve-1.3.3.tgz",
+      "integrity": "sha1-ZVkHw0aahoDcLeOidaj91paR8OU="
     },
     "resolve-from": {
-      "version": "1.0.1",
-      "from": "resolve-from@>=1.0.0 <2.0.0",
-      "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-1.0.1.tgz"
+      "version": "https://registry.npmjs.org/resolve-from/-/resolve-from-1.0.1.tgz",
+      "integrity": "sha1-Jsv+k10a7uq7Kbw/5a6wHpPUQiY="
     },
     "restore-cursor": {
-      "version": "1.0.1",
-      "from": "restore-cursor@>=1.0.1 <2.0.0",
-      "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-1.0.1.tgz"
+      "version": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-1.0.1.tgz",
+      "integrity": "sha1-NGYfRohjJ/7SmRR5FSJS35LapUE="
     },
     "rimraf": {
-      "version": "2.6.1",
-      "from": "rimraf@>=2.2.8 <3.0.0",
-      "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.1.tgz"
+      "version": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.1.tgz",
+      "integrity": "sha1-wjOOxkPfeht/5cVPqG9XQopV8z0="
     },
     "run-async": {
-      "version": "0.1.0",
-      "from": "run-async@>=0.1.0 <0.2.0",
-      "resolved": "https://registry.npmjs.org/run-async/-/run-async-0.1.0.tgz"
+      "version": "https://registry.npmjs.org/run-async/-/run-async-0.1.0.tgz",
+      "integrity": "sha1-yK1KXhEGYeQCp9IbUw4AnyX444k="
     },
     "rx-lite": {
-      "version": "3.1.2",
-      "from": "rx-lite@>=3.1.2 <4.0.0",
-      "resolved": "https://registry.npmjs.org/rx-lite/-/rx-lite-3.1.2.tgz"
+      "version": "https://registry.npmjs.org/rx-lite/-/rx-lite-3.1.2.tgz",
+      "integrity": "sha1-Gc5QLKVyZl87ZHsQk5+X/RYV8QI="
     },
     "sax": {
-      "version": "1.2.2",
-      "from": "sax@>=1.2.2 <2.0.0",
-      "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.2.tgz"
+      "version": "https://registry.npmjs.org/sax/-/sax-1.2.2.tgz",
+      "integrity": "sha1-/YYxojvHgmvvXYcb24c3jJVkeCg="
     },
     "shelljs": {
-      "version": "0.7.7",
-      "from": "shelljs@>=0.7.5 <0.8.0",
-      "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.7.7.tgz"
+      "version": "https://registry.npmjs.org/shelljs/-/shelljs-0.7.7.tgz",
+      "integrity": "sha1-svXHfvlxSPS09uImguELuoZnz/E="
     },
     "slice-ansi": {
-      "version": "0.0.4",
-      "from": "slice-ansi@0.0.4",
-      "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-0.0.4.tgz"
+      "version": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-0.0.4.tgz",
+      "integrity": "sha1-7b+JA/ZvfOL46v1s7tZeJkyDGzU="
     },
     "sprintf-js": {
-      "version": "1.0.3",
-      "from": "sprintf-js@>=1.0.2 <1.1.0",
-      "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz"
+      "version": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
+      "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw="
     },
     "string_decoder": {
-      "version": "1.0.0",
-      "from": "string_decoder@>=1.0.0 <1.1.0",
-      "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.0.tgz"
+      "version": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.0.tgz",
+      "integrity": "sha1-8G9BFXtmTYYGn4S9vcmw2KsoFmc="
     },
     "string-width": {
-      "version": "1.0.2",
-      "from": "string-width@>=1.0.1 <2.0.0",
-      "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz"
+      "version": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
+      "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M="
     },
     "strip-ansi": {
-      "version": "3.0.1",
-      "from": "strip-ansi@>=3.0.0 <4.0.0",
-      "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz"
+      "version": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+      "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8="
     },
     "strip-bom": {
-      "version": "3.0.0",
-      "from": "strip-bom@>=3.0.0 <4.0.0",
-      "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz"
+      "version": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
+      "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM="
     },
     "strip-json-comments": {
-      "version": "2.0.1",
-      "from": "strip-json-comments@>=2.0.1 <2.1.0",
-      "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz"
+      "version": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
+      "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo="
     },
     "supports-color": {
-      "version": "2.0.0",
-      "from": "supports-color@>=2.0.0 <3.0.0",
-      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz"
+      "version": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
+      "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc="
     },
     "table": {
-      "version": "3.8.3",
-      "from": "table@>=3.7.8 <4.0.0",
-      "resolved": "https://registry.npmjs.org/table/-/table-3.8.3.tgz",
+      "version": "https://registry.npmjs.org/table/-/table-3.8.3.tgz",
+      "integrity": "sha1-K7xULw/amGGnVdOUf+/Ys/UThV8=",
       "dependencies": {
         "is-fullwidth-code-point": {
-          "version": "2.0.0",
-          "from": "is-fullwidth-code-point@>=2.0.0 <3.0.0",
-          "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz"
+          "version": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
+          "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8="
         },
         "string-width": {
-          "version": "2.0.0",
-          "from": "string-width@>=2.0.0 <3.0.0",
-          "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.0.0.tgz"
+          "version": "https://registry.npmjs.org/string-width/-/string-width-2.0.0.tgz",
+          "integrity": "sha1-Y1xUNsxypuDDh87KJ41OLuxSaH4="
         }
       }
     },
     "text-table": {
-      "version": "0.2.0",
-      "from": "text-table@>=0.2.0 <0.3.0",
-      "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz"
+      "version": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
+      "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ="
     },
     "through": {
-      "version": "2.3.8",
-      "from": "through@>=2.3.6 <3.0.0",
-      "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz"
+      "version": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
+      "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU="
     },
     "tryit": {
-      "version": "1.0.3",
-      "from": "tryit@>=1.0.1 <2.0.0",
-      "resolved": "https://registry.npmjs.org/tryit/-/tryit-1.0.3.tgz"
+      "version": "https://registry.npmjs.org/tryit/-/tryit-1.0.3.tgz",
+      "integrity": "sha1-OTvnMKlEb9Hq1tpZoBQwjzbCics="
     },
     "type-check": {
-      "version": "0.3.2",
-      "from": "type-check@>=0.3.2 <0.4.0",
-      "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz"
+      "version": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz",
+      "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I="
     },
     "typedarray": {
-      "version": "0.0.6",
-      "from": "typedarray@>=0.0.6 <0.0.7",
-      "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz"
+      "version": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
+      "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c="
     },
     "user-home": {
-      "version": "2.0.0",
-      "from": "user-home@>=2.0.0 <3.0.0",
-      "resolved": "https://registry.npmjs.org/user-home/-/user-home-2.0.0.tgz"
+      "version": "https://registry.npmjs.org/user-home/-/user-home-2.0.0.tgz",
+      "integrity": "sha1-nHC/2Babwdy/SGBODwS4tJzenp8="
     },
     "util-deprecate": {
-      "version": "1.0.2",
-      "from": "util-deprecate@>=1.0.1 <1.1.0",
-      "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz"
+      "version": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+      "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
     },
     "wordwrap": {
-      "version": "1.0.0",
-      "from": "wordwrap@>=1.0.0 <1.1.0",
-      "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz"
+      "version": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz",
+      "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus="
     },
     "wrappy": {
-      "version": "1.0.2",
-      "from": "wrappy@>=1.0.0 <2.0.0",
-      "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz"
+      "version": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+      "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
     },
     "write": {
-      "version": "0.2.1",
-      "from": "write@>=0.2.1 <0.3.0",
-      "resolved": "https://registry.npmjs.org/write/-/write-0.2.1.tgz"
+      "version": "https://registry.npmjs.org/write/-/write-0.2.1.tgz",
+      "integrity": "sha1-X8A4KOJkzqP+kUVUdvejxWbLB1c="
     },
     "xtend": {
-      "version": "4.0.1",
-      "from": "xtend@>=4.0.0 <5.0.0",
-      "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz"
+      "version": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz",
+      "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68="
     }
   }
 }
--- a/package.json
+++ b/package.json
@@ -1,16 +1,18 @@
 {
   "name": "mozillaeslintsetup",
   "description": "This package file is for setup of ESLint only for editor integration.",
   "repository": {},
   "license": "MPL-2.0",
   "dependencies": {
+    "escope": "^3.6.0",
     "eslint": "3.19.0",
     "eslint-plugin-html": "2.0.3",
+    "eslint-plugin-mozilla": "file:tools\\lint\\eslint\\eslint-plugin-mozilla",
     "eslint-plugin-react": "6.10.3",
-    "escope": "^3.6.0",
+    "eslint-plugin-spidermonkey-js": "file:tools\\lint\\eslint\\eslint-plugin-spidermonkey-js",
     "espree": "^3.4.0",
     "estraverse": "^4.2.0",
     "ini-parser": "^0.0.2",
     "sax": "^1.2.2"
   }
 }
--- a/parser/xml/test/unit/results.js
+++ b/parser/xml/test/unit/results.js
@@ -426,17 +426,17 @@ var vectors = [
     "sanitized": "<html><head></head><body><img></body></html>"
   },
   {
     "data": "<title onpropertychange=alert(1)></title><title title=></title>",
     "sanitized": "<html><head><title></title><title title=\"\"></title></head><body></body></html>"
   },
   {
     "data": "<!-- IE 5-8 standards mode -->\r\n<a href=http://foo.bar/#x=`y></a><img alt=\"`><img src=xx:x onerror=alert(1)></a>\">\r\n\r\n<!-- IE 5-9 standards mode -->\r\n<!a foo=x=`y><img alt=\"`><img src=xx:x onerror=alert(2)//\">\r\n<?a foo=x=`y><img alt=\"`><img src=xx:x onerror=alert(3)//\">",
-    "sanitized": "<html><head></head><body><a href=\"http://foo.bar/#x=%60y\"></a><img alt=\"`&gt;&lt;img src=xx:x onerror=alert(1)&gt;&lt;/a&gt;\">\n\n\n<img alt=\"`&gt;&lt;img src=xx:x onerror=alert(2)//\">\n<img alt=\"`&gt;&lt;img src=xx:x onerror=alert(3)//\"></body></html>"
+    "sanitized": "<html><head></head><body><a href=\"http://foo.bar/#x=`y\"></a><img alt=\"`&gt;&lt;img src=xx:x onerror=alert(1)&gt;&lt;/a&gt;\">\n\n\n<img alt=\"`&gt;&lt;img src=xx:x onerror=alert(2)//\">\n<img alt=\"`&gt;&lt;img src=xx:x onerror=alert(3)//\"></body></html>"
   },
   {
     "data": "<svg xmlns=\"http://www.w3.org/2000/svg\">\n<a id=\"x\"><rect fill=\"white\" width=\"1000\" height=\"1000\"/></a>\n<rect  fill=\"white\" style=\"clip-path:url(test3.svg#a);fill:url(#b);filter:url(#c);marker:url(#d);mask:url(#e);stroke:url(#f);\"/>\n</svg>",
     "sanitized": "<html><head></head><body>\n\n\n</body></html>"
   },
   {
     "data": "<svg xmlns=\"http://www.w3.org/2000/svg\">\r\n<path d=\"M0,0\" style=\"marker-start:url(test4.svg#a)\"/>\r\n</svg>",
     "sanitized": "<html><head></head><body>\n\n</body></html>"
--- a/servo/components/script/dom/bindings/codegen/CodegenRust.py
+++ b/servo/components/script/dom/bindings/codegen/CodegenRust.py
@@ -12,21 +12,22 @@ import os
 import re
 import string
 import textwrap
 import functools
 
 from WebIDL import (
     BuiltinTypes,
     IDLBuiltinType,
+    IDLInterfaceMember,
+    IDLNullableType,
     IDLNullValue,
-    IDLNullableType,
     IDLObject,
+    IDLPromiseType,
     IDLType,
-    IDLInterfaceMember,
     IDLUndefinedValue,
     IDLWrapperType,
 )
 
 from Configuration import (
     MakeNativeName,
     MemberIsUnforgeable,
     getModuleFromObject,
@@ -89,24 +90,24 @@ def stripTrailingWhitespace(text):
     tail = '\n' if text.endswith('\n') else ''
     lines = text.splitlines()
     for i in range(len(lines)):
         lines[i] = lines[i].rstrip()
     return '\n'.join(lines) + tail
 
 
 def innerContainerType(type):
-    assert type.isSequence() or type.isMozMap()
+    assert type.isSequence() or type.isRecord()
     return type.inner.inner if type.nullable() else type.inner
 
 
 def wrapInNativeContainerType(type, inner):
     if type.isSequence():
         containerType = "Vec"
-    elif type.isMozMap():
+    elif type.isRecord():
         containerType = "MozMap"
     else:
         raise TypeError("Unexpected container type %s", type)
 
     return CGWrapper(inner, pre=containerType + "<", post=">")
 
 
 builtinNames = {
@@ -692,17 +693,17 @@ def getJSToNativeConversionInfo(type, de
             templateBody += (
                 "} else {\n" +
                 CGIndenter(onFailureNotAnObject(failureCode)).define() +
                 "}")
         return templateBody
 
     assert not (isEnforceRange and isClamp)  # These are mutually exclusive
 
-    if type.isSequence() or type.isMozMap():
+    if type.isSequence() or type.isRecord():
         innerInfo = getJSToNativeConversionInfo(innerContainerType(type),
                                                 descriptorProvider,
                                                 isMember=isMember)
         declType = wrapInNativeContainerType(type, innerInfo.declType)
         config = getConversionConfigForType(type, isEnforceRange, isClamp, treatNullAs)
 
         if type.nullable():
             declType = CGWrapper(declType, pre="Option<", post=" >")
@@ -749,16 +750,66 @@ def getJSToNativeConversionInfo(type, de
                     CGDictionary.makeDictionaryName(dictionary.inner))
             else:
                 default = None
         else:
             default = handleDefaultNull("None")
 
         return handleOptional(templateBody, declType, default)
 
+    if type.isPromise():
+        assert not type.nullable()
+        # Per spec, what we're supposed to do is take the original
+        # Promise.resolve and call it with the original Promise as this
+        # value to make a Promise out of whatever value we actually have
+        # here.  The question is which global we should use.  There are
+        # a couple cases to consider:
+        #
+        # 1) Normal call to API with a Promise argument.  This is a case the
+        #    spec covers, and we should be using the current Realm's
+        #    Promise.  That means the current compartment.
+        # 2) Promise return value from a callback or callback interface.
+        #    This is in theory a case the spec covers but in practice it
+        #    really doesn't define behavior here because it doesn't define
+        #    what Realm we're in after the callback returns, which is when
+        #    the argument conversion happens.  We will use the current
+        #    compartment, which is the compartment of the callable (which
+        #    may itself be a cross-compartment wrapper itself), which makes
+        #    as much sense as anything else. In practice, such an API would
+        #    once again be providing a Promise to signal completion of an
+        #    operation, which would then not be exposed to anyone other than
+        #    our own implementation code.
+        templateBody = fill(
+            """
+            { // Scope for our JSAutoCompartment.
+
+                rooted!(in(cx) let globalObj = CurrentGlobalOrNull(cx));
+                let promiseGlobal = GlobalScope::from_object_maybe_wrapped(globalObj.handle().get());
+
+                rooted!(in(cx) let mut valueToResolve = $${val}.get());
+                if !JS_WrapValue(cx, valueToResolve.handle_mut()) {
+                $*{exceptionCode}
+                }
+                match Promise::Resolve(&promiseGlobal, cx, valueToResolve.handle()) {
+                    Ok(value) => value,
+                    Err(error) => {
+                    throw_dom_exception(cx, &promiseGlobal, error);
+                    $*{exceptionCode}
+                    }
+                }
+            }
+            """,
+            exceptionCode=exceptionCode)
+
+        if isArgument:
+            declType = CGGeneric("&Promise")
+        else:
+            declType = CGGeneric("Rc<Promise>")
+        return handleOptional(templateBody, declType, handleDefaultNull("None"))
+
     if type.isGeckoInterface():
         assert not isEnforceRange and not isClamp
 
         descriptor = descriptorProvider.getDescriptor(
             type.unroll().inner.identifier.name)
 
         if descriptor.interface.isCallback():
             name = descriptor.nativeType
@@ -775,89 +826,44 @@ def getJSToNativeConversionInfo(type, de
         conversionFunction = "root_from_handlevalue"
         descriptorType = descriptor.returnType
         if isMember == "Variadic":
             conversionFunction = "native_from_handlevalue"
             descriptorType = descriptor.nativeType
         elif isArgument:
             descriptorType = descriptor.argumentType
 
-        templateBody = ""
-        isPromise = descriptor.interface.identifier.name == "Promise"
-        if isPromise:
-            # Per spec, what we're supposed to do is take the original
-            # Promise.resolve and call it with the original Promise as this
-            # value to make a Promise out of whatever value we actually have
-            # here.  The question is which global we should use.  There are
-            # a couple cases to consider:
-            #
-            # 1) Normal call to API with a Promise argument.  This is a case the
-            #    spec covers, and we should be using the current Realm's
-            #    Promise.  That means the current compartment.
-            # 2) Promise return value from a callback or callback interface.
-            #    This is in theory a case the spec covers but in practice it
-            #    really doesn't define behavior here because it doesn't define
-            #    what Realm we're in after the callback returns, which is when
-            #    the argument conversion happens.  We will use the current
-            #    compartment, which is the compartment of the callable (which
-            #    may itself be a cross-compartment wrapper itself), which makes
-            #    as much sense as anything else. In practice, such an API would
-            #    once again be providing a Promise to signal completion of an
-            #    operation, which would then not be exposed to anyone other than
-            #    our own implementation code.
-            templateBody = fill(
-                """
-                { // Scope for our JSAutoCompartment.
-
-                  rooted!(in(cx) let globalObj = CurrentGlobalOrNull(cx));
-                  let promiseGlobal = GlobalScope::from_object_maybe_wrapped(globalObj.handle().get());
-
-                  rooted!(in(cx) let mut valueToResolve = $${val}.get());
-                  if !JS_WrapValue(cx, valueToResolve.handle_mut()) {
-                    $*{exceptionCode}
-                  }
-                  match Promise::Resolve(&promiseGlobal, cx, valueToResolve.handle()) {
-                      Ok(value) => value,
-                      Err(error) => {
-                        throw_dom_exception(cx, &promiseGlobal, error);
-                        $*{exceptionCode}
-                      }
-                  }
+        if descriptor.interface.isConsequential():
+            raise TypeError("Consequential interface %s being used as an "
+                            "argument" % descriptor.interface.identifier.name)
+
+        if failureCode is None:
+            substitutions = {
+                "sourceDescription": sourceDescription,
+                "interface": descriptor.interface.identifier.name,
+                "exceptionCode": exceptionCode,
+            }
+            unwrapFailureCode = string.Template(
+                'throw_type_error(cx, "${sourceDescription} does not '
+                'implement interface ${interface}.");\n'
+                '${exceptionCode}').substitute(substitutions)
+        else:
+            unwrapFailureCode = failureCode
+
+        templateBody = fill(
+            """
+            match ${function}($${val}) {
+                Ok(val) => val,
+                Err(()) => {
+                    $*{failureCode}
                 }
-                """,
-                exceptionCode=exceptionCode)
-        else:
-            if descriptor.interface.isConsequential():
-                raise TypeError("Consequential interface %s being used as an "
-                                "argument" % descriptor.interface.identifier.name)
-
-            if failureCode is None:
-                substitutions = {
-                    "sourceDescription": sourceDescription,
-                    "interface": descriptor.interface.identifier.name,
-                    "exceptionCode": exceptionCode,
-                }
-                unwrapFailureCode = string.Template(
-                    'throw_type_error(cx, "${sourceDescription} does not '
-                    'implement interface ${interface}.");\n'
-                    '${exceptionCode}').substitute(substitutions)
-            else:
-                unwrapFailureCode = failureCode
-
-            templateBody = fill(
-                """
-                match ${function}($${val}) {
-                    Ok(val) => val,
-                    Err(()) => {
-                        $*{failureCode}
-                    }
-                }
-                """,
-                failureCode=unwrapFailureCode + "\n",
-                function=conversionFunction)
+            }
+            """,
+            failureCode=unwrapFailureCode + "\n",
+            function=conversionFunction)
 
         declType = CGGeneric(descriptorType)
         if type.nullable():
             templateBody = "Some(%s)" % templateBody
             declType = CGWrapper(declType, pre="Option<", post=">")
 
         templateBody = wrapObjectTemplate(templateBody, "None",
                                           isDefinitelyObject, type, failureCode)
@@ -1318,17 +1324,17 @@ def typeNeedsCx(type, retVal=False):
         return any(typeNeedsCx(t) for t in type.unroll().flatMemberTypes)
     if retVal and type.isSpiderMonkeyInterface():
         return True
     return type.isAny() or type.isObject()
 
 
 # Returns a conversion behavior suitable for a type
 def getConversionConfigForType(type, isEnforceRange, isClamp, treatNullAs):
-    if type.isSequence() or type.isMozMap():
+    if type.isSequence() or type.isRecord():
         return getConversionConfigForType(innerContainerType(type), isEnforceRange, isClamp, treatNullAs)
     if type.isDOMString():
         assert not isEnforceRange and not isClamp
 
         treatAs = {
             "Default": "StringificationBehavior::Default",
             "EmptyString": "StringificationBehavior::Empty",
         }
@@ -1376,16 +1382,19 @@ def getRetvalDeclarationForType(returnTy
         if returnType.nullable():
             result = CGWrapper(result, pre="Option<", post=">")
         return result
     if returnType.isEnum():
         result = CGGeneric(returnType.unroll().inner.identifier.name)
         if returnType.nullable():
             result = CGWrapper(result, pre="Option<", post=">")
         return result
+    if returnType.isPromise():
+        assert not returnType.nullable()
+        return CGGeneric("Rc<Promise>")
     if returnType.isGeckoInterface():
         descriptor = descriptorProvider.getDescriptor(
             returnType.unroll().inner.identifier.name)
         result = CGGeneric(descriptor.returnType)
         if returnType.nullable():
             result = CGWrapper(result, pre="Option<", post=">")
         return result
     if returnType.isCallback():
@@ -1403,17 +1412,17 @@ def getRetvalDeclarationForType(returnTy
     # https://github.com/servo/servo/issues/6307
     if returnType.isAny():
         return CGGeneric("JSVal")
     if returnType.isObject() or returnType.isSpiderMonkeyInterface():
         result = CGGeneric("NonZero<*mut JSObject>")
         if returnType.nullable():
             result = CGWrapper(result, pre="Option<", post=">")
         return result
-    if returnType.isSequence() or returnType.isMozMap():
+    if returnType.isSequence() or returnType.isRecord():
         result = getRetvalDeclarationForType(innerContainerType(returnType), descriptorProvider)
         result = wrapInNativeContainerType(returnType, result)
         if returnType.nullable():
             result = CGWrapper(result, pre="Option<", post=">")
         return result
     if returnType.isDictionary():
         nullable = returnType.nullable()
         dictName = returnType.inner.name if nullable else returnType.name
@@ -1941,18 +1950,20 @@ class CGImports(CGWrapper):
                 name = getIdentifier(t).name
                 descriptor = descriptorProvider.getDescriptor(name)
                 if name != 'GlobalScope':
                     extras += [descriptor.path]
                 parentName = descriptor.getParentName()
                 if parentName:
                     descriptor = descriptorProvider.getDescriptor(parentName)
                     extras += [descriptor.path, descriptor.bindingPath]
-            elif t.isType() and t.isMozMap():
+            elif t.isType() and t.isRecord():
                 extras += ['dom::bindings::mozmap::MozMap']
+            elif isinstance(t, IDLPromiseType):
+                extras += ["dom::promise::Promise"]
             else:
                 if t.isEnum():
                     extras += [getModuleFromObject(t) + '::' + getIdentifier(t).name + 'Values']
                 extras += [getModuleFromObject(t) + '::' + getIdentifier(t).name]
 
         statements = []
         if len(ignored_warnings) > 0:
             statements.append('#![allow(%s)]' % ','.join(ignored_warnings))
@@ -3814,17 +3825,19 @@ class CGMemberJITInfo(CGThing):
         if t.nullable():
             # Sometimes it might return null, sometimes not
             return "JSVAL_TYPE_UNKNOWN"
         if t.isVoid():
             # No return, every time
             return "JSVAL_TYPE_UNDEFINED"
         if t.isSequence():
             return "JSVAL_TYPE_OBJECT"
-        if t.isMozMap():
+        if t.isRecord():
+            return "JSVAL_TYPE_OBJECT"
+        if t.isPromise():
             return "JSVAL_TYPE_OBJECT"
         if t.isGeckoInterface():
             return "JSVAL_TYPE_OBJECT"
         if t.isString():
             return "JSVAL_TYPE_STRING"
         if t.isEnum():
             return "JSVAL_TYPE_STRING"
         if t.isCallback():
@@ -4050,17 +4063,17 @@ def getUnionTypeTemplateVars(type, descr
         name = type.inner.identifier.name
         typeName = descriptorProvider.getDescriptor(name).returnType
     elif type.isEnum():
         name = type.inner.identifier.name
         typeName = name
     elif type.isDictionary():
         name = type.name
         typeName = name
-    elif type.isSequence() or type.isMozMap():
+    elif type.isSequence() or type.isRecord():
         name = type.name
         inner = getUnionTypeTemplateVars(innerContainerType(type), descriptorProvider)
         typeName = wrapInNativeContainerType(type, CGGeneric(inner["typeName"])).define()
     elif type.isByteString():
         name = type.name
         typeName = "ByteString"
     elif type.isDOMString():
         name = type.name
@@ -4203,17 +4216,17 @@ class CGUnionConversionStruct(CGThing):
         if len(objectMemberTypes) > 0:
             assert len(objectMemberTypes) == 1
             typeName = objectMemberTypes[0].name
             object = CGGeneric(get_match(typeName))
             names.append(typeName)
         else:
             object = None
 
-        mozMapMemberTypes = filter(lambda t: t.isMozMap(), memberTypes)
+        mozMapMemberTypes = filter(lambda t: t.isRecord(), memberTypes)
         if len(mozMapMemberTypes) > 0:
             assert len(mozMapMemberTypes) == 1
             typeName = mozMapMemberTypes[0].name
             mozMapObject = CGGeneric(get_match(typeName))
             names.append(typeName)
         else:
             mozMapObject = None
 
@@ -6865,16 +6878,18 @@ def camel_to_upper_snake(s):
 
 
 def process_arg(expr, arg):
     if arg.type.isGeckoInterface() and not arg.type.unroll().inner.isCallback():
         if arg.type.nullable() or arg.type.isSequence() or arg.optional:
             expr += ".r()"
         else:
             expr = "&" + expr
+    elif isinstance(arg.type, IDLPromiseType):
+        expr = "&" + expr
     return expr
 
 
 class GlobalGenRoots():
     """
     Roots for global codegen.
 
     To generate code, call the method associated with the target, and then
--- a/servo/components/script/dom/bindings/codegen/parser/WebIDL.py
+++ b/servo/components/script/dom/bindings/codegen/parser/WebIDL.py
@@ -300,18 +300,18 @@ class IDLScope(IDLObject):
                 "identifier '%s'.\n%s\n%s"
                 % (identifier.name,
                    originalObject.location, newObject.location), [])
 
         # We do the merging of overloads here as opposed to in IDLInterface
         # because we need to merge overloads of NamedConstructors and we need to
         # detect conflicts in those across interfaces. See also the comment in
         # IDLInterface.addExtendedAttributes for "NamedConstructor".
-        if (originalObject.tag == IDLInterfaceMember.Tags.Method and
-           newObject.tag == IDLInterfaceMember.Tags.Method):
+        if (isinstance(originalObject, IDLMethod) and
+            isinstance(newObject, IDLMethod)):
             return originalObject.addOverload(newObject)
 
         # Default to throwing, derived classes can override.
         conflictdesc = "\n\t%s at %s\n\t%s at %s" % (originalObject,
                                                      originalObject.location,
                                                      newObject,
                                                      newObject.location)
 
@@ -362,18 +362,16 @@ class IDLUnresolvedIdentifier(IDLObject)
         if name == "__noSuchMethod__":
             raise WebIDLError("__noSuchMethod__ is deprecated", [location])
 
         if name[:2] == "__" and name != "__content" and not allowDoubleUnderscore:
             raise WebIDLError("Identifiers beginning with __ are reserved",
                               [location])
         if name[0] == '_' and not allowDoubleUnderscore:
             name = name[1:]
-        # TODO: Bug 872377, Restore "toJSON" to below list.
-        # We sometimes need custom serialization, so allow toJSON for now.
         if (name in ["constructor", "toString"] and
             not allowForbidden):
             raise WebIDLError("Cannot use reserved identifier '%s'" % (name),
                               [location])
 
         self.name = name
 
     def __str__(self):
@@ -510,16 +508,19 @@ class IDLExposureMixins():
                 self.isExposedInSystemGlobals())
 
     def isExposedInAnyWorker(self):
         return len(self.getWorkerExposureSet()) > 0
 
     def isExposedInWorkerDebugger(self):
         return len(self.getWorkerDebuggerExposureSet()) > 0
 
+    def isExposedInAnyWorklet(self):
+        return len(self.getWorkletExposureSet()) > 0
+
     def isExposedInSystemGlobals(self):
         return 'BackstagePass' in self.exposureSet
 
     def isExposedInSomeButNotAllWorkers(self):
         """
         Returns true if the Exposed extended attribute for this interface
         exposes it in some worker globals but not others.  The return value does
         not depend on whether the interface is exposed in Window or System
@@ -529,16 +530,20 @@ class IDLExposureMixins():
             return False
         workerScopes = self.parentScope.globalNameMapping["Worker"]
         return len(workerScopes.difference(self.exposureSet)) > 0
 
     def getWorkerExposureSet(self):
         workerScopes = self._globalScope.globalNameMapping["Worker"]
         return workerScopes.intersection(self.exposureSet)
 
+    def getWorkletExposureSet(self):
+        workletScopes = self._globalScope.globalNameMapping["Worklet"]
+        return workletScopes.intersection(self.exposureSet)
+
     def getWorkerDebuggerExposureSet(self):
         workerDebuggerScopes = self._globalScope.globalNameMapping["WorkerDebugger"]
         return workerDebuggerScopes.intersection(self.exposureSet)
 
 
 class IDLExternalInterface(IDLObjectWithIdentifier, IDLExposureMixins):
     def __init__(self, location, parentScope, identifier):
         assert isinstance(identifier, IDLUnresolvedIdentifier)
@@ -563,28 +568,31 @@ class IDLExternalInterface(IDLObjectWith
 
     def isInterface(self):
         return True
 
     def isConsequential(self):
         return False
 
     def addExtendedAttributes(self, attrs):
-        assert len(attrs) == 0
+        if len(attrs) != 0:
+            raise WebIDLError("There are no extended attributes that are "
+                              "allowed on external interfaces",
+                              [attrs[0].location, self.location])
 
     def resolve(self, parentScope):
         pass
 
     def getJSImplementation(self):
         return None
 
     def isJSImplemented(self):
         return False
 
-    def isProbablyShortLivingObject(self):
+    def hasProbablyShortLivingWrapper(self):
         return False
 
     def isNavigatorProperty(self):
         return False
 
     def _getDependentObjects(self):
         return set()
 
@@ -1085,17 +1093,20 @@ class IDLInterfaceOrNamespace(IDLObjectW
                                                                 isAncestor)
                 isAncestor = True
                 testInterface = testInterface.parent
 
         # Ensure that there's at most one of each {named,indexed}
         # {getter,setter,creator,deleter}, at most one stringifier,
         # and at most one legacycaller.  Note that this last is not
         # quite per spec, but in practice no one overloads
-        # legacycallers.
+        # legacycallers.  Also note that in practice we disallow
+        # indexed deleters, but it simplifies some other code to
+        # treat deleter analogously to getter/setter/creator by
+        # prefixing it with "named".
         specialMembersSeen = {}
         for member in self.members:
             if not member.isMethod():
                 continue
 
             if member.isGetter():
                 memberType = "getters"
             elif member.isSetter():
@@ -1187,16 +1198,22 @@ class IDLInterfaceOrNamespace(IDLObjectW
         if self.getExtendedAttribute("Unforgeable") and self.hasChildInterfaces():
             locations = ([self.location] +
                          list(i.location for i in
                               self.interfacesBasedOnSelf if i.parent == self))
             raise WebIDLError("%s is an unforgeable ancestor interface" %
                               self.identifier.name,
                               locations)
 
+        ctor = self.ctor()
+        if ctor is not None:
+            ctor.validate()
+        for namedCtor in self.namedConstructors:
+            namedCtor.validate()
+
         indexedGetter = None
         hasLengthAttribute = False
         for member in self.members:
             member.validate()
 
             if self.isCallback() and member.getExtendedAttribute("Replaceable"):
                 raise WebIDLError("[Replaceable] used on an attribute on "
                                   "interface %s which is a callback interface" %
@@ -1303,36 +1320,41 @@ class IDLInterfaceOrNamespace(IDLObjectW
         # and pair iterators are only allowed on interfaces without indexed
         # getters.
         if self.isIterable():
             iterableDecl = self.maplikeOrSetlikeOrIterable
             if iterableDecl.isValueIterator():
                 if not indexedGetter:
                     raise WebIDLError("Interface with value iterator does not "
                                       "support indexed properties",
-                                      [self.location])
+                                      [self.location, iterableDecl.location])
 
                 if iterableDecl.valueType != indexedGetter.signatures()[0][0]:
                     raise WebIDLError("Iterable type does not match indexed "
                                       "getter type",
                                       [iterableDecl.location,
                                        indexedGetter.location])
 
                 if not hasLengthAttribute:
                     raise WebIDLError('Interface with value iterator does not '
                                       'have an integer-typed "length" attribute',
-                                      [self.location])
+                                      [self.location, iterableDecl.location])
             else:
                 assert iterableDecl.isPairIterator()
                 if indexedGetter:
                     raise WebIDLError("Interface with pair iterator supports "
                                       "indexed properties",
                                       [self.location, iterableDecl.location,
                                        indexedGetter.location])
 
+        if indexedGetter and not hasLengthAttribute:
+            raise WebIDLError('Interface with an indexed getter does not have '
+                              'an integer-typed "length" attribute',
+                              [self.location, indexedGetter.location])
+
     def isExternal(self):
         return False
 
     def setIsConsequentialInterfaceOf(self, other):
         self._consequential = True
         self.interfacesBasedOnSelf.add(other)
 
     def isConsequential(self):
@@ -1453,31 +1475,35 @@ class IDLInterfaceOrNamespace(IDLObjectW
         self.parent = parent
         # Put the new members at the beginning
         self.members = members + self.members
 
     def addPartialInterface(self, partial):
         assert self.identifier.name == partial.identifier.name
         self._partialInterfaces.append(partial)
 
+    def getPartialInterfaces(self):
+        # Don't let people mutate our guts.
+        return list(self._partialInterfaces)
+
     def getJSImplementation(self):
         classId = self.getExtendedAttribute("JSImplementation")
         if not classId:
             return classId
         assert isinstance(classId, list)
         assert len(classId) == 1
         return classId[0]
 
     def isJSImplemented(self):
         return bool(self.getJSImplementation())
 
-    def isProbablyShortLivingObject(self):
+    def hasProbablyShortLivingWrapper(self):
         current = self
         while current:
-            if current.getExtendedAttribute("ProbablyShortLivingObject"):
+            if current.getExtendedAttribute("ProbablyShortLivingWrapper"):
                 return True
             current = current.parent
         return False
 
     def isNavigatorProperty(self):
         naviProp = self.getExtendedAttribute("NavigatorProperty")
         if not naviProp:
             return False
@@ -1521,20 +1547,18 @@ class IDLInterfaceOrNamespace(IDLObjectW
         deps.update(self.implementedInterfaces)
         if self.parent:
             deps.add(self.parent)
         return deps
 
     def hasMembersInSlots(self):
         return self._ownMembersInSlots != 0
 
-    conditionExtendedAttributes = [ "Pref", "ChromeOnly", "Func", "AvailableIn",
-                                    "SecureContext",
-                                    "CheckAnyPermissions",
-                                    "CheckAllPermissions" ]
+    conditionExtendedAttributes = [ "Pref", "ChromeOnly", "Func",
+                                    "SecureContext" ]
     def isExposedConditionally(self, exclusions=[]):
         return any(((not a in exclusions) and self.getExtendedAttribute(a)) for a in self.conditionExtendedAttributes)
 
 class IDLInterface(IDLInterfaceOrNamespace):
     def __init__(self, location, parentScope, name, parent, members,
                  isKnownNonPartial):
         IDLInterfaceOrNamespace.__init__(self, location, parentScope, name,
                                          parent, members, isKnownNonPartial)
@@ -1561,61 +1585,67 @@ class IDLInterface(IDLInterfaceOrNamespa
                     raise WebIDLError("[NoInterfaceObject] must take no arguments",
                                       [attr.location])
 
                 if self.ctor():
                     raise WebIDLError("Constructor and NoInterfaceObject are incompatible",
                                       [self.location])
 
                 self._noInterfaceObject = True
-            elif identifier == "Constructor" or identifier == "NamedConstructor" or identifier == "ChromeConstructor":
+            elif identifier == "Constructor" or identifier == "NamedConstructor" or identifier == "ChromeConstructor" or identifier == "HTMLConstructor":
                 if identifier == "Constructor" and not self.hasInterfaceObject():
                     raise WebIDLError(str(identifier) + " and NoInterfaceObject are incompatible",
                                       [self.location])
 
                 if identifier == "NamedConstructor" and not attr.hasValue():
                     raise WebIDLError("NamedConstructor must either take an identifier or take a named argument list",
                                       [attr.location])
 
                 if identifier == "ChromeConstructor" and not self.hasInterfaceObject():
                     raise WebIDLError(str(identifier) + " and NoInterfaceObject are incompatible",
                                       [self.location])
 
+                if identifier == "HTMLConstructor":
+                    if not self.hasInterfaceObject():
+                        raise WebIDLError(str(identifier) + " and NoInterfaceObject are incompatible",
+                                          [self.location])
+
+                    if not attr.noArguments():
+                        raise WebIDLError(str(identifier) + " must take no arguments",
+                                          [attr.location])
+
                 args = attr.args() if attr.hasArgs() else []
 
-                if self.identifier.name == "Promise":
-                    promiseType = BuiltinTypes[IDLBuiltinType.Types.any]
-                else:
-                    promiseType = None
-                retType = IDLWrapperType(self.location, self, promiseType)
-
-                if identifier == "Constructor" or identifier == "ChromeConstructor":
+                retType = IDLWrapperType(self.location, self)
+
+                if identifier == "Constructor" or identifier == "ChromeConstructor" or identifier == "HTMLConstructor":
                     name = "constructor"
                     allowForbidden = True
                 else:
                     name = attr.value()
                     allowForbidden = False
 
                 methodIdentifier = IDLUnresolvedIdentifier(self.location, name,
                                                            allowForbidden=allowForbidden)
 
                 method = IDLMethod(self.location, methodIdentifier, retType,
-                                   args, static=True)
+                                   args, static=True,
+                                   htmlConstructor=(identifier == "HTMLConstructor"))
                 # Constructors are always NewObject and are always
                 # assumed to be able to throw (since there's no way to
                 # indicate otherwise) and never have any other
                 # extended attributes.
                 method.addExtendedAttributes(
                     [IDLExtendedAttribute(self.location, ("NewObject",)),
                      IDLExtendedAttribute(self.location, ("Throws",))])
                 if identifier == "ChromeConstructor":
                     method.addExtendedAttributes(
                         [IDLExtendedAttribute(self.location, ("ChromeOnly",))])
 
-                if identifier == "Constructor" or identifier == "ChromeConstructor":
+                if identifier == "Constructor" or identifier == "ChromeConstructor" or identifier == "HTMLConstructor":
                     method.resolve(self)
                 else:
                     # We need to detect conflicts for NamedConstructors across
                     # interfaces. We first call resolve on the parentScope,
                     # which will merge all NamedConstructors with the same
                     # identifier accross interfaces as overloads.
                     method.resolve(self.parentScope)
 
@@ -1687,17 +1717,17 @@ class IDLInterface(IDLInterfaceOrNamespa
                                           [member.location, attr.location])
                     member.addExtendedAttributes([attr])
             elif (identifier == "NeedResolve" or
                   identifier == "OverrideBuiltins" or
                   identifier == "ChromeOnly" or
                   identifier == "Unforgeable" or
                   identifier == "UnsafeInPrerendering" or
                   identifier == "LegacyEventInit" or
-                  identifier == "ProbablyShortLivingObject" or
+                  identifier == "ProbablyShortLivingWrapper" or
                   identifier == "LegacyUnenumerableNamedProperties" or
                   identifier == "NonOrdinaryGetPrototypeOf" or
                   identifier == "Abstract" or
                   identifier == "Inline"):
                 # Known extended attributes that do not take values
                 if not attr.noArguments():
                     raise WebIDLError("[%s] must take no arguments" % identifier,
                                       [attr.location])
@@ -1848,17 +1878,17 @@ class IDLDictionary(IDLObjectWithScope):
                     the memberType argument, to the dictionary being validated,
                     if the boolean value in the first element is True.
 
                     None, if the boolean value in the first element is False.
             """
 
             if (memberType.nullable() or
                 memberType.isSequence() or
-                memberType.isMozMap()):
+                memberType.isRecord()):
                 return typeContainsDictionary(memberType.inner, dictionary)
 
             if memberType.isDictionary():
                 if memberType.inner == dictionary:
                     return (True, [memberType.location])
 
                 (contains, locations) = dictionaryContainsDictionary(memberType.inner,
                                                                      dictionary)
@@ -1896,17 +1926,20 @@ class IDLDictionary(IDLObjectWithScope):
                                   [member.location])
             (contains, locations) = typeContainsDictionary(member.type, self)
             if contains:
                 raise WebIDLError("Dictionary %s has member with itself as type." %
                                   self.identifier.name,
                                   [member.location] + locations)
 
     def addExtendedAttributes(self, attrs):
-        assert len(attrs) == 0
+        if len(attrs) != 0:
+            raise WebIDLError("There are no extended attributes that are "
+                              "allowed on dictionaries",
+                              [attrs[0].location, self.location])
 
     def _getDependentObjects(self):
         deps = set(self.members)
         if (self.parent):
             deps.add(self.parent)
         return deps
 
 
@@ -1930,17 +1963,20 @@ class IDLEnum(IDLObjectWithIdentifier):
 
     def validate(self):
         pass
 
     def isEnum(self):
         return True
 
     def addExtendedAttributes(self, attrs):
-        assert len(attrs) == 0
+        if len(attrs) != 0:
+            raise WebIDLError("There are no extended attributes that are "
+                              "allowed on enums",
+                              [attrs[0].location, self.location])
 
     def _getDependentObjects(self):
         return set()
 
 
 class IDLType(IDLObject):
     Tags = enum(
         # The integer types
@@ -1969,17 +2005,18 @@ class IDLType(IDLObject):
         'void',
         # Funny stuff
         'interface',
         'dictionary',
         'enum',
         'callback',
         'union',
         'sequence',
-        'mozmap'
+        'record',
+        'promise',
         )
 
     def __init__(self, location, name):
         IDLObject.__init__(self, location)
         self.name = name
         self.builtin = False
 
     def __eq__(self, other):
@@ -2019,17 +2056,17 @@ class IDLType(IDLObject):
         return False
 
     def isVoid(self):
         return self.name == "Void"
 
     def isSequence(self):
         return False
 
-    def isMozMap(self):
+    def isRecord(self):
         return False
 
     def isArrayBuffer(self):
         return False
 
     def isArrayBufferView(self):
         return False
 
@@ -2102,17 +2139,21 @@ class IDLType(IDLObject):
         assert self.tag() == IDLType.Tags.callback
         return self.nullable() and self.inner.callback._treatNonCallableAsNull
 
     def treatNonObjectAsNull(self):
         assert self.tag() == IDLType.Tags.callback
         return self.nullable() and self.inner.callback._treatNonObjectAsNull
 
     def addExtendedAttributes(self, attrs):
-        assert len(attrs) == 0
+        if len(attrs) != 0:
+            raise WebIDLError("There are no extended attributes that are "
+                              "allowed on types, for now (but this is "
+                              "changing; see bug 1359269)",
+                              [attrs[0].location, self.location])
 
     def resolveType(self, parentScope):
         pass
 
     def unroll(self):
         return self
 
     def isDistinguishableFrom(self, other):
@@ -2123,19 +2164,18 @@ class IDLType(IDLObject):
         return True
 
 
 class IDLUnresolvedType(IDLType):
     """
         Unresolved types are interface types
     """
 
-    def __init__(self, location, name, promiseInnerType=None):
+    def __init__(self, location, name):
         IDLType.__init__(self, location, name)
-        self._promiseInnerType = promiseInnerType
 
     def isComplete(self):
         return False
 
     def complete(self, scope):
         obj = None
         try:
             obj = scope._lookupIdentifier(self.name)
@@ -2152,28 +2192,25 @@ class IDLUnresolvedType(IDLType):
             typedefType = IDLTypedefType(self.location, obj.innerType,
                                          obj.identifier)
             assert not typedefType.isComplete()
             return typedefType.complete(scope)
         elif obj.isCallback() and not obj.isInterface():
             assert self.name.name == obj.identifier.name
             return IDLCallbackType(obj.location, obj)
 
-        if self._promiseInnerType and not self._promiseInnerType.isComplete():
-            self._promiseInnerType = self._promiseInnerType.complete(scope)
-
         name = self.name.resolve(scope, None)
-        return IDLWrapperType(self.location, obj, self._promiseInnerType)
+        return IDLWrapperType(self.location, obj)
 
     def isDistinguishableFrom(self, other):
         raise TypeError("Can't tell whether an unresolved type is or is not "
                         "distinguishable from other things")
 
 
-class IDLParameterizedType(IDLType):
+class IDLParametrizedType(IDLType):
     def __init__(self, location, name, innerType):
         IDLType.__init__(self, location, name)
         self.builtin = False
         self.inner = innerType
 
     def includesRestrictedFloat(self):
         return self.inner.includesRestrictedFloat()
 
@@ -2186,25 +2223,25 @@ class IDLParameterizedType(IDLType):
 
     def unroll(self):
         return self.inner.unroll()
 
     def _getDependentObjects(self):
         return self.inner._getDependentObjects()
 
 
-class IDLNullableType(IDLParameterizedType):
+class IDLNullableType(IDLParametrizedType):
     def __init__(self, location, innerType):
         assert not innerType.isVoid()
         assert not innerType == BuiltinTypes[IDLBuiltinType.Types.any]
 
         name = innerType.name
         if innerType.isComplete():
             name += "OrNull"
-        IDLParameterizedType.__init__(self, location, name, innerType)
+        IDLParametrizedType.__init__(self, location, name, innerType)
 
     def __eq__(self, other):
         return isinstance(other, IDLNullableType) and self.inner == other.inner
 
     def __str__(self):
         return self.inner.__str__() + "OrNull"
 
     def nullable(self):
@@ -2244,18 +2281,18 @@ class IDLNullableType(IDLParameterizedTy
         return self.inner.isInteger()
 
     def isVoid(self):
         return False
 
     def isSequence(self):
         return self.inner.isSequence()
 
-    def isMozMap(self):
-        return self.inner.isMozMap()
+    def isRecord(self):
+        return self.inner.isRecord()
 
     def isArrayBuffer(self):
         return self.inner.isArrayBuffer()
 
     def isArrayBufferView(self):
         return self.inner.isArrayBufferView()
 
     def isSharedArrayBuffer(self):
@@ -2266,17 +2303,19 @@ class IDLNullableType(IDLParameterizedTy
 
     def isDictionary(self):
         return self.inner.isDictionary()
 
     def isInterface(self):
         return self.inner.isInterface()
 
     def isPromise(self):
-        return self.inner.isPromise()
+        # There is no such thing as a nullable Promise.
+        assert not self.inner.isPromise()
+        return False
 
     def isCallbackInterface(self):
         return self.inner.isCallbackInterface()
 
     def isNonCallbackInterface(self):
         return self.inner.isNonCallbackInterface()
 
     def isEnum(self):
@@ -2302,28 +2341,30 @@ class IDLNullableType(IDLParameterizedTy
                 raise WebIDLError("The inner type of a nullable type must not "
                                   "be a union type that itself has a nullable "
                                   "type as a member type", [self.location])
 
         self.name = self.inner.name + "OrNull"
         return self
 
     def isDistinguishableFrom(self, other):
-        if (other.nullable() or (other.isUnion() and other.hasNullableType) or
-            other.isDictionary()):
+        if (other.nullable() or
+            other.isDictionary() or
+            (other.isUnion() and
+             (other.hasNullableType or other.hasDictionaryType()))):
             # Can't tell which type null should become
             return False
         return self.inner.isDistinguishableFrom(other)
 
 
-class IDLSequenceType(IDLParameterizedType):
+class IDLSequenceType(IDLParametrizedType):
     def __init__(self, location, parameterType):
         assert not parameterType.isVoid()
 
-        IDLParameterizedType.__init__(self, location, parameterType.name, parameterType)
+        IDLParametrizedType.__init__(self, location, parameterType.name, parameterType)
         # Need to set self.name up front if our inner type is already complete,
         # since in that case our .complete() won't be called.
         if self.inner.isComplete():
             self.name = self.inner.name + "Sequence"
 
     def __eq__(self, other):
         return isinstance(other, IDLSequenceType) and self.inner == other.inner
 
@@ -2378,44 +2419,48 @@ class IDLSequenceType(IDLParameterizedTy
         if other.isPromise():
             return False
         if other.isUnion():
             # Just forward to the union; it'll deal
             return other.isDistinguishableFrom(self)
         return (other.isPrimitive() or other.isString() or other.isEnum() or
                 other.isDate() or other.isInterface() or
                 other.isDictionary() or
-                other.isCallback() or other.isMozMap())
-
-
-class IDLMozMapType(IDLParameterizedType):
-    def __init__(self, location, parameterType):
-        assert not parameterType.isVoid()
-
-        IDLParameterizedType.__init__(self, location, parameterType.name, parameterType)
+                other.isCallback() or other.isRecord())
+
+
+class IDLRecordType(IDLParametrizedType):
+    def __init__(self, location, keyType, valueType):
+        assert keyType.isString()
+        assert keyType.isComplete()
+        assert not valueType.isVoid()
+
+        IDLParametrizedType.__init__(self, location, valueType.name, valueType)
+        self.keyType = keyType
+
         # Need to set self.name up front if our inner type is already complete,
         # since in that case our .complete() won't be called.
         if self.inner.isComplete():
-            self.name = self.inner.name + "MozMap"
+            self.name = self.keyType.name + self.inner.name + "Record"
 
     def __eq__(self, other):
-        return isinstance(other, IDLMozMapType) and self.inner == other.inner
+        return isinstance(other, IDLRecordType) and self.inner == other.inner
 
     def __str__(self):
-        return self.inner.__str__() + "MozMap"
-
-    def isMozMap(self):
+        return self.keyType.__str__() + self.inner.__str__() + "Record"
+
+    def isRecord(self):
         return True
 
     def tag(self):
-        return IDLType.Tags.mozmap
+        return IDLType.Tags.record
 
     def complete(self, scope):
         self.inner = self.inner.complete(scope)
-        self.name = self.inner.name + "MozMap"
+        self.name = self.keyType.name + self.inner.name + "Record"
         return self
 
     def unroll(self):
         # We do not unroll our inner.  Just stop at ourselves.  That
         # lets us add headers for both ourselves and our inner as
         # needed.
         return self
 
@@ -2603,18 +2648,18 @@ class IDLTypedefType(IDLType):
         return self.inner.isUSVString()
 
     def isVoid(self):
         return self.inner.isVoid()
 
     def isSequence(self):
         return self.inner.isSequence()
 
-    def isMozMap(self):
-        return self.inner.isMozMap()
+    def isRecord(self):
+        return self.inner.isRecord()
 
     def isDictionary(self):
         return self.inner.isDictionary()
 
     def isArrayBuffer(self):
         return self.inner.isArrayBuffer()
 
     def isArrayBufferView(self):
@@ -2674,30 +2719,31 @@ class IDLTypedef(IDLObjectWithIdentifier
 
     def validate(self):
         pass
 
     def isTypedef(self):
         return True
 
     def addExtendedAttributes(self, attrs):
-        assert len(attrs) == 0
+        if len(attrs) != 0:
+            raise WebIDLError("There are no extended attributes that are "
+                              "allowed on typedefs",
+                              [attrs[0].location, self.location])
 
     def _getDependentObjects(self):
         return self.innerType._getDependentObjects()
 
 
 class IDLWrapperType(IDLType):
-    def __init__(self, location, inner, promiseInnerType=None):
+    def __init__(self, location, inner):
         IDLType.__init__(self, location, inner.identifier.name)
         self.inner = inner
         self._identifier = inner.identifier
         self.builtin = False
-        assert not promiseInnerType or inner.identifier.name == "Promise"
-        self._promiseInnerType = promiseInnerType
 
     def __eq__(self, other):
         return (isinstance(other, IDLWrapperType) and
                 self._identifier == other._identifier and
                 self.builtin == other.builtin)
 
     def __str__(self):
         return str(self.name) + " (Wrapper)"
@@ -2737,24 +2783,16 @@ class IDLWrapperType(IDLType):
         return self.isInterface() and self.inner.isCallback()
 
     def isNonCallbackInterface(self):
         return self.isInterface() and not self.inner.isCallback()
 
     def isEnum(self):
         return isinstance(self.inner, IDLEnum)
 
-    def isPromise(self):
-        return (isinstance(self.inner, IDLInterface) and
-                self.inner.identifier.name == "Promise")
-
-    def promiseInnerType(self):
-        assert self.isPromise()
-        return self._promiseInnerType
-
     def isSerializable(self):
         if self.isInterface():
             if self.inner.isExternal():
                 return False
             return any(m.isMethod() and m.isJsonifier() for m in self.inner.members)
         elif self.isEnum():
             return True
         elif self.isDictionary():
@@ -2776,28 +2814,26 @@ class IDLWrapperType(IDLType):
         elif self.isEnum():
             return IDLType.Tags.enum
         elif self.isDictionary():
             return IDLType.Tags.dictionary
         else:
             assert False
 
     def isDistinguishableFrom(self, other):
-        if self.isPromise():
-            return False
         if other.isPromise():
             return False
         if other.isUnion():
             # Just forward to the union; it'll deal
             return other.isDistinguishableFrom(self)
         assert self.isInterface() or self.isEnum() or self.isDictionary()
         if self.isEnum():
             return (other.isPrimitive() or other.isInterface() or other.isObject() or
                     other.isCallback() or other.isDictionary() or
-                    other.isSequence() or other.isMozMap() or other.isDate())
+                    other.isSequence() or other.isRecord() or other.isDate())
         if self.isDictionary() and other.nullable():
             return False
         if (other.isPrimitive() or other.isString() or other.isEnum() or
             other.isDate() or other.isSequence()):
             return True
         if self.isDictionary():
             return other.isNonCallbackInterface()
 
@@ -2809,35 +2845,31 @@ class IDLWrapperType(IDLType):
             assert self.isGeckoInterface() and other.isGeckoInterface()
             if self.inner.isExternal() or other.unroll().inner.isExternal():
                 return self != other
             return (len(self.inner.interfacesBasedOnSelf &
                         other.unroll().inner.interfacesBasedOnSelf) == 0 and
                     (self.isNonCallbackInterface() or
                      other.isNonCallbackInterface()))
         if (other.isDictionary() or other.isCallback() or
-            other.isMozMap()):
+            other.isRecord()):
             return self.isNonCallbackInterface()
 
         # Not much else |other| can be
         assert other.isObject()
         return False
 
     def isExposedInAllOf(self, exposureSet):
         if not self.isInterface():
             return True
         iface = self.inner
         if iface.isExternal():
             # Let's say true, though ideally we'd only do this when
             # exposureSet contains the primary global's name.
             return True
-        if (self.isPromise() and
-            # Check the internal type
-            not self.promiseInnerType().unroll().isExposedInAllOf(exposureSet)):
-            return False
         return iface.exposureSet.issuperset(exposureSet)
 
     def _getDependentObjects(self):
         # NB: The codegen for an interface type depends on
         #  a) That the identifier is in fact an interface (as opposed to
         #     a dictionary or something else).
         #  b) The native type of the interface.
         #  If we depend on the interface object we will also depend on
@@ -2855,16 +2887,55 @@ class IDLWrapperType(IDLType):
         # depend on it, because the member types of a dictionary
         # affect whether a method taking the dictionary as an argument
         # takes a JSContext* argument or not.
         if self.isDictionary():
             return set([self.inner])
         return set()
 
 
+class IDLPromiseType(IDLParametrizedType):
+    def __init__(self, location, innerType):
+        IDLParametrizedType.__init__(self, location, "Promise", innerType)
+
+    def __eq__(self, other):
+        return (isinstance(other, IDLPromiseType) and
+                self.promiseInnerType() == other.promiseInnerType())
+
+    def __str__(self):
+        return self.inner.__str__() + "Promise"
+
+    def isPromise(self):
+        return True
+
+    def promiseInnerType(self):
+        return self.inner
+
+    def tag(self):
+        return IDLType.Tags.promise
+
+    def complete(self, scope):
+        self.inner = self.promiseInnerType().complete(scope)
+        return self
+
+    def unroll(self):
+        # We do not unroll our inner.  Just stop at ourselves.  That
+        # lets us add headers for both ourselves and our inner as
+        # needed.
+        return self
+
+    def isDistinguishableFrom(self, other):
+        # Promises are not distinguishable from anything.
+        return False
+
+    def isExposedInAllOf(self, exposureSet):
+        # Check the internal type
+        return self.promiseInnerType().unroll().isExposedInAllOf(exposureSet)
+
+
 class IDLBuiltinType(IDLType):
 
     Types = enum(
         # The integer types
         'byte',
         'octet',
         'short',
         'unsigned_short',
@@ -3019,45 +3090,45 @@ class IDLBuiltinType(IDLType):
             return False
         if other.isUnion():
             # Just forward to the union; it'll deal
             return other.isDistinguishableFrom(self)
         if self.isBoolean():
             return (other.isNumeric() or other.isString() or other.isEnum() or
                     other.isInterface() or other.isObject() or
                     other.isCallback() or other.isDictionary() or
-                    other.isSequence() or other.isMozMap() or other.isDate())
+                    other.isSequence() or other.isRecord() or other.isDate())
         if self.isNumeric():
             return (other.isBoolean() or other.isString() or other.isEnum() or
                     other.isInterface() or other.isObject() or
                     other.isCallback() or other.isDictionary() or
-                    other.isSequence() or other.isMozMap() or other.isDate())
+                    other.isSequence() or other.isRecord() or other.isDate())
         if self.isString():
             return (other.isPrimitive() or other.isInterface() or
                     other.isObject() or
                     other.isCallback() or other.isDictionary() or
-                    other.isSequence() or other.isMozMap() or other.isDate())
+                    other.isSequence() or other.isRecord() or other.isDate())
         if self.isAny():
             # Can't tell "any" apart from anything
             return False
         if self.isObject():
             return other.isPrimitive() or other.isString() or other.isEnum()
         if self.isDate():
             return (other.isPrimitive() or other.isString() or other.isEnum() or
                     other.isInterface() or other.isCallback() or
                     other.isDictionary() or other.isSequence() or
-                    other.isMozMap())
+                    other.isRecord())
         if self.isVoid():
             return not other.isVoid()
         # Not much else we could be!
         assert self.isSpiderMonkeyInterface()
         # Like interfaces, but we know we're not a callback
         return (other.isPrimitive() or other.isString() or other.isEnum() or
                 other.isCallback() or other.isDictionary() or
-                other.isSequence() or other.isMozMap() or other.isDate() or
+                other.isSequence() or other.isRecord() or other.isDate() or
                 (other.isInterface() and (
                  # ArrayBuffer is distinguishable from everything
                  # that's not an ArrayBuffer or a callback interface
                  (self.isArrayBuffer() and not other.isArrayBuffer()) or
                  (self.isSharedArrayBuffer() and not other.isSharedArrayBuffer()) or
                  # ArrayBufferView is distinguishable from everything
                  # that's not an ArrayBufferView or typed array.
                  (self.isArrayBufferView() and not other.isArrayBufferView() and
@@ -3451,16 +3522,24 @@ class IDLInterfaceMember(IDLObjectWithId
                                   "That seems rather unlikely.",
                                   [self.location])
 
         if self.getExtendedAttribute("NewObject"):
             if self.dependsOn == "Nothing" or self.dependsOn == "DOMState":
                 raise WebIDLError("A [NewObject] method is not idempotent, "
                                   "so it has to depend on something other than DOM state.",
                                   [self.location])
+            if (self.getExtendedAttribute("Cached") or
+                self.getExtendedAttribute("StoreInSlot")):
+                raise WebIDLError("A [NewObject] attribute shouldnt be "
+                                  "[Cached] or [StoreInSlot], since the point "
+                                  "of those is to keep returning the same "
+                                  "thing across multiple calls, which is not "
+                                  "what [NewObject] does.",
+                                  [self.location])
 
     def _setDependsOn(self, dependsOn):
         if self.dependsOn != "Everything":
             raise WebIDLError("Trying to specify multiple different DependsOn, "
                               "Pure, or Constant extended attributes for "
                               "attribute", [self.location])
         if dependsOn not in IDLInterfaceMember.DependsOnValues:
             raise WebIDLError("Invalid [DependsOn=%s] on attribute" % dependsOn,
@@ -3522,18 +3601,20 @@ class IDLMaplikeOrSetlikeOrIterableBase(
             if (member.identifier.name in self.disallowedMemberNames and
                 not ((member.isMethod() and member.isMaplikeOrSetlikeOrIterableMethod()) or
                      (member.isAttr() and member.isMaplikeOrSetlikeAttr()))):
                 raise WebIDLError("Member '%s' conflicts "
                                   "with reserved %s name." %
                                   (member.identifier.name,
                                    self.maplikeOrSetlikeOrIterableType),
                                   [self.location, member.location])
-            # Check that there are no disallowed non-method members
-            if (isAncestor or (member.isAttr() or member.isConst()) and
+            # Check that there are no disallowed non-method members.
+            # Ancestor members are always disallowed here; own members
+            # are disallowed only if they're non-methods.
+            if ((isAncestor or member.isAttr() or member.isConst()) and
                 member.identifier.name in self.disallowedNonMethodNames):
                 raise WebIDLError("Member '%s' conflicts "
                                   "with reserved %s method." %
                                   (member.identifier.name,
                                    self.maplikeOrSetlikeOrIterableType),
                                   [self.location, member.location])
 
     def addMethod(self, name, members, allowExistingOperations, returnType, args=[],
@@ -3725,16 +3806,17 @@ class IDLMaplikeOrSetlike(IDLMaplikeOrSe
         """
         # Both maplike and setlike have a size attribute
         members.append(IDLAttribute(self.location,
                                     IDLUnresolvedIdentifier(BuiltinLocation("<auto-generated-identifier>"), "size"),
                                     BuiltinTypes[IDLBuiltinType.Types.unsigned_long],
                                     True,
                                     maplikeOrSetlike=self))
         self.reserved_ro_names = ["size"]
+        self.disallowedMemberNames.append("size")
 
         # object entries()
         self.addMethod("entries", members, False, BuiltinTypes[IDLBuiltinType.Types.object],
                        affectsNothing=True, isIteratorAlias=self.isMaplike())
         # object keys()
         self.addMethod("keys", members, False, BuiltinTypes[IDLBuiltinType.Types.object],
                        affectsNothing=True)
         # object values()
@@ -3815,16 +3897,19 @@ class IDLConst(IDLInterfaceMember):
     def __init__(self, location, identifier, type, value):
         IDLInterfaceMember.__init__(self, location, identifier,
                                     IDLInterfaceMember.Tags.Const)
 
         assert isinstance(type, IDLType)
         if type.isDictionary():
             raise WebIDLError("A constant cannot be of a dictionary type",
                               [self.location])
+        if type.isRecord():
+            raise WebIDLError("A constant cannot be of a record type",
+                              [self.location])
         self.type = type
         self.value = value
 
         if identifier.name == "prototype":
             raise WebIDLError("The identifier of a constant must not be 'prototype'",
                               [location])
 
     def __str__(self):
@@ -3926,52 +4011,57 @@ class IDLAttribute(IDLInterfaceMember):
             self.type = t
 
         if self.type.isDictionary() and not self.getExtendedAttribute("Cached"):
             raise WebIDLError("An attribute cannot be of a dictionary type",
                               [self.location])
         if self.type.isSequence() and not self.getExtendedAttribute("Cached"):
             raise WebIDLError("A non-cached attribute cannot be of a sequence "
                               "type", [self.location])
-        if self.type.isMozMap() and not self.getExtendedAttribute("Cached"):
-            raise WebIDLError("A non-cached attribute cannot be of a MozMap "
+        if self.type.isRecord() and not self.getExtendedAttribute("Cached"):
+            raise WebIDLError("A non-cached attribute cannot be of a record "
                               "type", [self.location])
         if self.type.isUnion():
             for f in self.type.unroll().flatMemberTypes:
                 if f.isDictionary():
                     raise WebIDLError("An attribute cannot be of a union "
                                       "type if one of its member types (or "
                                       "one of its member types's member "
                                       "types, and so on) is a dictionary "
                                       "type", [self.location, f.location])
                 if f.isSequence():
                     raise WebIDLError("An attribute cannot be of a union "
                                       "type if one of its member types (or "
                                       "one of its member types's member "
                                       "types, and so on) is a sequence "
                                       "type", [self.location, f.location])
-                if f.isMozMap():
+                if f.isRecord():
                     raise WebIDLError("An attribute cannot be of a union "
                                       "type if one of its member types (or "
                                       "one of its member types's member "
-                                      "types, and so on) is a MozMap "
+                                      "types, and so on) is a record "
                                       "type", [self.location, f.location])
         if not self.type.isInterface() and self.getExtendedAttribute("PutForwards"):
             raise WebIDLError("An attribute with [PutForwards] must have an "
                               "interface type as its type", [self.location])
 
-        if not self.type.isInterface() and self.getExtendedAttribute("SameObject"):
+        if (not self.type.isInterface() and
+            self.getExtendedAttribute("SameObject")):
             raise WebIDLError("An attribute with [SameObject] must have an "
                               "interface type as its type", [self.location])
 
+        if self.type.isPromise() and not self.readonly:
+            raise WebIDLError("Promise-returning attributes must be readonly",
+                              [self.location])
+
     def validate(self):
         def typeContainsChromeOnlyDictionaryMember(type):
             if (type.nullable() or
                 type.isSequence() or
-                type.isMozMap()):
+                type.isRecord()):
                 return typeContainsChromeOnlyDictionaryMember(type.inner)
 
             if type.isUnion():
                 for memberType in type.flatMemberTypes:
                     (contains, location) = typeContainsChromeOnlyDictionaryMember(memberType)
                     if contains:
                         return (True, location)
 
@@ -4007,37 +4097,46 @@ class IDLAttribute(IDLInterfaceMember):
             (contains, location) = typeContainsChromeOnlyDictionaryMember(self.type)
             if contains:
                 raise WebIDLError("[Cached] and [StoreInSlot] must not be used "
                                   "on an attribute whose type contains a "
                                   "[ChromeOnly] dictionary member",
                                   [self.location, location])
         if self.getExtendedAttribute("Frozen"):
             if (not self.type.isSequence() and not self.type.isDictionary() and
-                not self.type.isMozMap()):
+                not self.type.isRecord()):
                 raise WebIDLError("[Frozen] is only allowed on "
                                   "sequence-valued, dictionary-valued, and "
-                                  "MozMap-valued attributes",
+                                  "record-valued attributes",
                                   [self.location])
         if not self.type.unroll().isExposedInAllOf(self.exposureSet):
             raise WebIDLError("Attribute returns a type that is not exposed "
                               "everywhere where the attribute is exposed",
                               [self.location])
+        if self.getExtendedAttribute("CEReactions"):
+            if self.readonly:
+                raise WebIDLError("[CEReactions] is not allowed on "
+                                  "readonly attributes",
+                                  [self.location])
 
     def handleExtendedAttribute(self, attr):
         identifier = attr.identifier()
-        if identifier == "SetterThrows" and self.readonly:
+        if ((identifier == "SetterThrows" or identifier == "SetterCanOOM")
+            and self.readonly):
             raise WebIDLError("Readonly attributes must not be flagged as "
-                              "[SetterThrows]",
+                              "[%s]" % identifier,
                               [self.location])
-        elif (((identifier == "Throws" or identifier == "GetterThrows") and
+        elif (((identifier == "Throws" or identifier == "GetterThrows" or
+                identifier == "CanOOM" or identifier == "GetterCanOOM") and
                self.getExtendedAttribute("StoreInSlot")) or
               (identifier == "StoreInSlot" and
                (self.getExtendedAttribute("Throws") or
-                self.getExtendedAttribute("GetterThrows")))):
+                self.getExtendedAttribute("GetterThrows") or
+                self.getExtendedAttribute("CanOOM") or
+                self.getExtendedAttribute("GetterCanOOM")))):
             raise WebIDLError("Throwing things can't be [StoreInSlot]",
                               [attr.location])
         elif identifier == "LenientThis":
             if not attr.noArguments():
                 raise WebIDLError("[LenientThis] must take no arguments",
                                   [attr.location])
             if self.isStatic():
                 raise WebIDLError("[LenientThis] is only allowed on non-static "
@@ -4061,16 +4160,20 @@ class IDLAttribute(IDLInterfaceMember):
                               [attr.location, self.location])
         elif identifier == "Constant" and not self.readonly:
             raise WebIDLError("[Constant] only allowed on readonly attributes",
                               [attr.location, self.location])
         elif identifier == "PutForwards":
             if not self.readonly:
                 raise WebIDLError("[PutForwards] is only allowed on readonly "
                                   "attributes", [attr.location, self.location])
+            if self.type.isPromise():
+                raise WebIDLError("[PutForwards] is not allowed on "
+                                  "Promise-typed attributes",
+                                  [attr.location, self.location])
             if self.isStatic():
                 raise WebIDLError("[PutForwards] is only allowed on non-static "
                                   "attributes", [attr.location, self.location])
             if self.getExtendedAttribute("Replaceable") is not None:
                 raise WebIDLError("[PutForwards] and [Replaceable] can't both "
                                   "appear on the same attribute",
                                   [attr.location, self.location])
             if not attr.hasValue():
@@ -4078,30 +4181,38 @@ class IDLAttribute(IDLInterfaceMember):
                                   [attr.location, self.location])
         elif identifier == "Replaceable":
             if not attr.noArguments():
                 raise WebIDLError("[Replaceable] must take no arguments",
                                   [attr.location])
             if not self.readonly:
                 raise WebIDLError("[Replaceable] is only allowed on readonly "
                                   "attributes", [attr.location, self.location])
+            if self.type.isPromise():
+                raise WebIDLError("[Replaceable] is not allowed on "
+                                  "Promise-typed attributes",
+                                  [attr.location, self.location])
             if self.isStatic():
                 raise WebIDLError("[Replaceable] is only allowed on non-static "
                                   "attributes", [attr.location, self.location])
             if self.getExtendedAttribute("PutForwards") is not None:
                 raise WebIDLError("[PutForwards] and [Replaceable] can't both "
                                   "appear on the same attribute",
                                   [attr.location, self.location])
         elif identifier == "LenientSetter":
             if not attr.noArguments():
                 raise WebIDLError("[LenientSetter] must take no arguments",
                                   [attr.location])
             if not self.readonly:
                 raise WebIDLError("[LenientSetter] is only allowed on readonly "
                                   "attributes", [attr.location, self.location])
+            if self.type.isPromise():
+                raise WebIDLError("[LenientSetter] is not allowed on "
+                                  "Promise-typed attributes",
+                                  [attr.location, self.location])
             if self.isStatic():
                 raise WebIDLError("[LenientSetter] is only allowed on non-static "
                                   "attributes", [attr.location, self.location])
             if self.getExtendedAttribute("PutForwards") is not None:
                 raise WebIDLError("[LenientSetter] and [PutForwards] can't both "
                                   "appear on the same attribute",
                                   [attr.location, self.location])
             if self.getExtendedAttribute("Replaceable") is not None:
@@ -4186,27 +4297,37 @@ class IDLAttribute(IDLInterfaceMember):
         elif identifier == "Unscopable":
             if not attr.noArguments():
                 raise WebIDLError("[Unscopable] must take no arguments",
                                   [attr.location])
             if self.isStatic():
                 raise WebIDLError("[Unscopable] is only allowed on non-static "
                                   "attributes and operations",
                                   [attr.location, self.location])
+        elif identifier == "CEReactions":
+            if not attr.noArguments():
+                raise WebIDLError("[CEReactions] must take no arguments",
+                                  [attr.location])
         elif (identifier == "Pref" or
               identifier == "Deprecated" or
               identifier == "SetterThrows" or
               identifier == "Throws" or
               identifier == "GetterThrows" or
+              identifier == "SetterCanOOM" or
+              identifier == "CanOOM" or
+              identifier == "GetterCanOOM" or
               identifier == "ChromeOnly" or
               identifier == "Func" or
               identifier == "SecureContext" or
               identifier == "Frozen" or
               identifier == "NewObject" or
               identifier == "UnsafeInPrerendering" or
+              identifier == "NeedsSubjectPrincipal" or
+              identifier == "NeedsCallerType" or
+              identifier == "ReturnValueNeedsContainsHack" or
               identifier == "BinaryName"):
             # Known attributes that we don't need to do anything with here
             pass
         else:
             raise WebIDLError("Unknown extended attribute %s on attribute" % identifier,
                               [attr.location])
         IDLInterfaceMember.handleExtendedAttribute(self, attr)
 
@@ -4477,17 +4598,17 @@ class IDLMethod(IDLInterfaceMember, IDLS
         'Named',
         'Indexed'
     )
 
     def __init__(self, location, identifier, returnType, arguments,
                  static=False, getter=False, setter=False, creator=False,
                  deleter=False, specialType=NamedOrIndexed.Neither,
                  legacycaller=False, stringifier=False, jsonifier=False,
-                 maplikeOrSetlikeOrIterable=None):
+                 maplikeOrSetlikeOrIterable=None, htmlConstructor=False):
         # REVIEW: specialType is NamedOrIndexed -- wow, this is messed up.
         IDLInterfaceMember.__init__(self, location, identifier,
                                     IDLInterfaceMember.Tags.Method)
 
         self._hasOverloads = False
 
         assert isinstance(returnType, IDLType)
 
@@ -4507,16 +4628,20 @@ class IDLMethod(IDLInterfaceMember, IDLS
         assert isinstance(legacycaller, bool)
         self._legacycaller = legacycaller
         assert isinstance(stringifier, bool)
         self._stringifier = stringifier
         assert isinstance(jsonifier, bool)
         self._jsonifier = jsonifier
         assert maplikeOrSetlikeOrIterable is None or isinstance(maplikeOrSetlikeOrIterable, IDLMaplikeOrSetlikeOrIterableBase)
         self.maplikeOrSetlikeOrIterable = maplikeOrSetlikeOrIterable
+        assert isinstance(htmlConstructor, bool)
+        # The identifier of a HTMLConstructor must be 'constructor'.
+        assert not htmlConstructor or identifier.name == "constructor"
+        self._htmlConstructor = htmlConstructor
         self._specialType = specialType
         self._unforgeable = False
         self.dependsOn = "Everything"
         self.affects = "Everything"
         self.aliases = []
 
         if static and identifier.name == "prototype":
             raise WebIDLError("The identifier of a static operation must not be 'prototype'",
@@ -4607,16 +4732,19 @@ class IDLMethod(IDLInterfaceMember, IDLS
         return (self.isGetter() or
                 self.isSetter() or
                 self.isCreator() or
                 self.isDeleter() or
                 self.isLegacycaller() or
                 self.isStringifier() or
                 self.isJsonifier())
 
+    def isHTMLConstructor(self):
+        return self._htmlConstructor
+
     def hasOverloads(self):
         return self._hasOverloads
 
     def isIdentifierLess(self):
         """
         True if the method name started with __, and if the method is not a
         maplike/setlike method. Interfaces with maplike/setlike will generate
         methods starting with __ for chrome only backing object access in JS
@@ -4662,16 +4790,18 @@ class IDLMethod(IDLInterfaceMember, IDLS
         assert not self.isCreator()
         assert not method.isCreator()
         assert not self.isDeleter()
         assert not method.isDeleter()
         assert not self.isStringifier()
         assert not method.isStringifier()
         assert not self.isJsonifier()
         assert not method.isJsonifier()
+        assert not self.isHTMLConstructor()
+        assert not method.isHTMLConstructor()
 
         return self
 
     def signatures(self):
         return [(overload.returnType, overload.arguments) for overload in
                 self._overloads]
 
     def finish(self, scope):
@@ -4738,19 +4868,20 @@ class IDLMethod(IDLInterfaceMember, IDLS
                      argument.type.inner.canBeEmpty())or
                     (argument.type.isUnion() and
                      argument.type.unroll().hasPossiblyEmptyDictionaryType())):
                     # Optional dictionaries and unions containing optional
                     # dictionaries at the end of the list or followed by
                     # optional arguments must be optional.
                     if (not argument.optional and
                         all(arg.optional for arg in arguments[idx+1:])):
-                        raise WebIDLError("Dictionary argument or union "
-                                          "argument containing a dictionary "
-                                          "not followed by a required argument "
+                        raise WebIDLError("Dictionary argument without any "
+                                          "required fields or union argument "
+                                          "containing such dictionary not "
+                                          "followed by a required argument "
                                           "must be optional",
                                           [argument.location])
 
                     # An argument cannot be a Nullable Dictionary
                     if argument.type.nullable():
                         raise WebIDLError("An argument cannot be a nullable "
                                           "dictionary or nullable union "
                                           "containing a dictionary",
@@ -4826,23 +4957,22 @@ class IDLMethod(IDLInterfaceMember, IDLS
         # No valid distinguishing index.  Time to throw
         locations = self.locationsForArgCount(argc)
         raise WebIDLError("Signatures with %d arguments for method '%s' are not "
                           "distinguishable" % (argc, self.identifier.name),
                           locations)
 
     def handleExtendedAttribute(self, attr):
         identifier = attr.identifier()
-        if identifier == "GetterThrows":
+        if (identifier == "GetterThrows" or
+            identifier == "SetterThrows" or
+            identifier == "GetterCanOOM" or
+            identifier == "SetterCanOOM"):
             raise WebIDLError("Methods must not be flagged as "
-                              "[GetterThrows]",
-                              [attr.location, self.location])
-        elif identifier == "SetterThrows":
-            raise WebIDLError("Methods must not be flagged as "
-                              "[SetterThrows]",
+                              "[%s]" % identifier,
                               [attr.location, self.location])
         elif identifier == "Unforgeable":
             if self.isStatic():
                 raise WebIDLError("[Unforgeable] is only allowed on non-static "
                                   "methods", [attr.location, self.location])
             self._unforgeable = True
         elif identifier == "SameObject":
             raise WebIDLError("Methods must not be flagged as [SameObject]",
@@ -4904,25 +5034,37 @@ class IDLMethod(IDLInterfaceMember, IDLS
         elif identifier == "Unscopable":
             if not attr.noArguments():
                 raise WebIDLError("[Unscopable] must take no arguments",
                                   [attr.location])
             if self.isStatic():
                 raise WebIDLError("[Unscopable] is only allowed on non-static "
                                   "attributes and operations",
                                   [attr.location, self.location])
+        elif identifier == "CEReactions":
+            if not attr.noArguments():
+                raise WebIDLError("[CEReactions] must take no arguments",
+                                  [attr.location])
+
+            if self.isSpecial() and not self.isSetter() and not self.isDeleter():
+                raise WebIDLError("[CEReactions] is only allowed on operation, "
+                                  "attribute, setter, and deleter",
+                                  [attr.location, self.location])
         elif (identifier == "Throws" or
+              identifier == "CanOOM" or
               identifier == "NewObject" or
               identifier == "ChromeOnly" or
               identifier == "UnsafeInPrerendering" or
               identifier == "Pref" or
               identifier == "Deprecated" or
               identifier == "Func" or
               identifier == "SecureContext" or
               identifier == "BinaryName" or
+              identifier == "NeedsSubjectPrincipal" or
+              identifier == "NeedsCallerType" or
               identifier == "StaticClassOverride"):
             # Known attributes that we don't need to do anything with here
             pass
         else:
             raise WebIDLError("Unknown extended attribute %s on method" % identifier,
                               [attr.location])
         IDLInterfaceMember.handleExtendedAttribute(self, attr)
 
@@ -4975,17 +5117,20 @@ class IDLImplementsStatement(IDLObject):
         implementor.addImplementedInterface(implementee)
         self.implementor = implementor
         self.implementee = implementee
 
     def validate(self):
         pass
 
     def addExtendedAttributes(self, attrs):
-        assert len(attrs) == 0
+        if len(attrs) != 0:
+            raise WebIDLError("There are no extended attributes that are "
+                              "allowed on implements statements",
+                              [attrs[0].location, self.location])
 
 
 class IDLExtendedAttribute(IDLObject):
     """
     A class to represent IDL extended attributes so we can give them locations
     """
     def __init__(self, location, tuple):
         IDLObject.__init__(self, location)
@@ -5114,17 +5259,17 @@ class Tokenizer(object):
         "double": "DOUBLE",
         "float": "FLOAT",
         "long": "LONG",
         "object": "OBJECT",
         "octet": "OCTET",
         "Promise": "PROMISE",
         "required": "REQUIRED",
         "sequence": "SEQUENCE",
-        "MozMap": "MOZMAP",
+        "record": "RECORD",
         "short": "SHORT",
         "unsigned": "UNSIGNED",
         "void": "VOID",
         ":": "COLON",
         ";": "SEMICOLON",
         "{": "LBRACE",
         "}": "RBRACE",
         "(": "LPAREN",
@@ -5832,16 +5977,19 @@ class Parser(Tokenizer):
                 raise WebIDLError("%s has wrong number of arguments" %
                                   ("getter" if getter else "deleter"),
                                   [self.getLocation(p, 2)])
             argType = arguments[0].type
             if argType == BuiltinTypes[IDLBuiltinType.Types.domstring]:
                 specialType = IDLMethod.NamedOrIndexed.Named
             elif argType == BuiltinTypes[IDLBuiltinType.Types.unsigned_long]:
                 specialType = IDLMethod.NamedOrIndexed.Indexed
+                if deleter:
+                    raise WebIDLError("There is no such thing as an indexed deleter.",
+                                      [self.getLocation(p, 1)])
             else:
                 raise WebIDLError("%s has wrong argument type (must be DOMString or UnsignedLong)" %
                                   ("getter" if getter else "deleter"),
                                   [arguments[0].location])
             if arguments[0].optional or arguments[0].variadic:
                 raise WebIDLError("%s cannot have %s argument" %
                                   ("getter" if getter else "deleter",
                                    "optional" if arguments[0].optional else "variadic"),
@@ -6240,17 +6388,17 @@ class Parser(Tokenizer):
                   | INTERFACE
                   | LONG
                   | MODULE
                   | NULL
                   | OBJECT
                   | OCTET
                   | OPTIONAL
                   | SEQUENCE
-                  | MOZMAP
+                  | RECORD
                   | SETTER
                   | SHORT
                   | STATIC
                   | STRINGIFIER
                   | JSONIFIER
                   | TRUE
                   | TYPEDEF
                   | UNSIGNED
@@ -6319,59 +6467,62 @@ class Parser(Tokenizer):
     def p_UnionMemberTypesEmpty(self, p):
         """
             UnionMemberTypes :
         """
         p[0] = []
 
     def p_NonAnyType(self, p):
         """
-            NonAnyType : PrimitiveOrStringType Null
+            NonAnyType : PrimitiveType Null
                        | ARRAYBUFFER Null
                        | SHAREDARRAYBUFFER Null
                        | OBJECT Null
         """
         if p[1] == "object":
             type = BuiltinTypes[IDLBuiltinType.Types.object]
         elif p[1] == "ArrayBuffer":
             type = BuiltinTypes[IDLBuiltinType.Types.ArrayBuffer]
         elif p[1] == "SharedArrayBuffer":
             type = BuiltinTypes[IDLBuiltinType.Types.SharedArrayBuffer]
         else:
             type = BuiltinTypes[p[1]]
 
         p[0] = self.handleNullable(type, p[2])
 
+    def p_NonAnyTypeStringType(self, p):
+        """
+            NonAnyType : StringType Null
+        """
+        p[0] = self.handleNullable(p[1], p[2])
+
     def p_NonAnyTypeSequenceType(self, p):
         """
             NonAnyType : SEQUENCE LT Type GT Null
         """
         innerType = p[3]
         type = IDLSequenceType(self.getLocation(p, 1), innerType)
         p[0] = self.handleNullable(type, p[5])
 
-    # Note: Promise<void> is allowed, so we want to parametrize on
-    # ReturnType, not Type.  Also, we want this to end up picking up
-    # the Promise interface for now, hence the games with IDLUnresolvedType.
+    # Note: Promise<void> is allowed, so we want to parametrize on ReturnType,
+    # not Type.  Promise types can't be null, hence no "Null" in there.
     def p_NonAnyTypePromiseType(self, p):
         """
-            NonAnyType : PROMISE LT ReturnType GT Null
-        """
-        innerType = p[3]
-        promiseIdent = IDLUnresolvedIdentifier(self.getLocation(p, 1), "Promise")
-        type = IDLUnresolvedType(self.getLocation(p, 1), promiseIdent, p[3])
-        p[0] = self.handleNullable(type, p[5])
-
-    def p_NonAnyTypeMozMapType(self, p):
-        """
-            NonAnyType : MOZMAP LT Type GT Null
-        """
-        innerType = p[3]
-        type = IDLMozMapType(self.getLocation(p, 1), innerType)
-        p[0] = self.handleNullable(type, p[5])
+            NonAnyType : PROMISE LT ReturnType GT
+        """
+        p[0] = IDLPromiseType(self.getLocation(p, 1), p[3])
+
+    def p_NonAnyTypeRecordType(self, p):
+        """
+            NonAnyType : RECORD LT StringType COMMA Type GT Null
+        """
+        keyType = p[3]
+        valueType = p[5]
+        type = IDLRecordType(self.getLocation(p, 1), keyType, valueType)
+        p[0] = self.handleNullable(type, p[7])
 
     def p_NonAnyTypeScopedName(self, p):
         """
             NonAnyType : ScopedName Null
         """
         assert isinstance(p[1], IDLUnresolvedIdentifier)
 
         if p[1].name == "Promise":
@@ -6404,93 +6555,99 @@ class Parser(Tokenizer):
         """
             NonAnyType : DATE Null
         """
         p[0] = self.handleNullable(BuiltinTypes[IDLBuiltinType.Types.date],
                                    p[2])
 
     def p_ConstType(self, p):
         """
-            ConstType : PrimitiveOrStringType Null
+            ConstType : PrimitiveType Null
         """
         type = BuiltinTypes[p[1]]
         p[0] = self.handleNullable(type, p[2])
 
     def p_ConstTypeIdentifier(self, p):
         """
             ConstType : IDENTIFIER Null
         """
         identifier = IDLUnresolvedIdentifier(self.getLocation(p, 1), p[1])
 
         type = IDLUnresolvedType(self.getLocation(p, 1), identifier)
         p[0] = self.handleNullable(type, p[2])
 
-    def p_PrimitiveOrStringTypeUint(self, p):
-        """
-            PrimitiveOrStringType : UnsignedIntegerType
+    def p_PrimitiveTypeUint(self, p):
+        """
+            PrimitiveType : UnsignedIntegerType
         """
         p[0] = p[1]
 
-    def p_PrimitiveOrStringTypeBoolean(self, p):
-        """
-            PrimitiveOrStringType : BOOLEAN
+    def p_PrimitiveTypeBoolean(self, p):
+        """
+            PrimitiveType : BOOLEAN
         """
         p[0] = IDLBuiltinType.Types.boolean
 
-    def p_PrimitiveOrStringTypeByte(self, p):
-        """
-            PrimitiveOrStringType : BYTE
+    def p_PrimitiveTypeByte(self, p):
+        """
+            PrimitiveType : BYTE
         """
         p[0] = IDLBuiltinType.Types.byte
 
-    def p_PrimitiveOrStringTypeOctet(self, p):
-        """
-            PrimitiveOrStringType : OCTET
+    def p_PrimitiveTypeOctet(self, p):
+        """
+            PrimitiveType : OCTET
         """
         p[0] = IDLBuiltinType.Types.octet
 
-    def p_PrimitiveOrStringTypeFloat(self, p):
-        """
-            PrimitiveOrStringType : FLOAT
+    def p_PrimitiveTypeFloat(self, p):
+        """
+            PrimitiveType : FLOAT
         """
         p[0] = IDLBuiltinType.Types.float
 
-    def p_PrimitiveOrStringTypeUnrestictedFloat(self, p):
-        """
-            PrimitiveOrStringType : UNRESTRICTED FLOAT
+    def p_PrimitiveTypeUnrestictedFloat(self, p):
+        """
+            PrimitiveType : UNRESTRICTED FLOAT
         """
         p[0] = IDLBuiltinType.Types.unrestricted_float
 
-    def p_PrimitiveOrStringTypeDouble(self, p):
-        """
-            PrimitiveOrStringType : DOUBLE
+    def p_PrimitiveTypeDouble(self, p):
+        """
+            PrimitiveType : DOUBLE
         """
         p[0] = IDLBuiltinType.Types.double
 
-    def p_PrimitiveOrStringTypeUnrestictedDouble(self, p):
-        """
-            PrimitiveOrStringType : UNRESTRICTED DOUBLE
+    def p_PrimitiveTypeUnrestictedDouble(self, p):
+        """
+            PrimitiveType : UNRESTRICTED DOUBLE
         """
         p[0] = IDLBuiltinType.Types.unrestricted_double
 
-    def p_PrimitiveOrStringTypeDOMString(self, p):
-        """
-            PrimitiveOrStringType : DOMSTRING
+    def p_StringType(self, p):
+        """
+            StringType : BuiltinStringType
+        """
+        p[0] = BuiltinTypes[p[1]]
+
+    def p_BuiltinStringTypeDOMString(self, p):
+        """
+            BuiltinStringType : DOMSTRING
         """
         p[0] = IDLBuiltinType.Types.domstring
 
-    def p_PrimitiveOrStringTypeBytestring(self, p):
-        """
-            PrimitiveOrStringType : BYTESTRING
+    def p_BuiltinStringTypeBytestring(self, p):
+        """
+            BuiltinStringType : BYTESTRING
         """
         p[0] = IDLBuiltinType.Types.bytestring
 
-    def p_PrimitiveOrStringTypeUSVString(self, p):
-        """
-            PrimitiveOrStringType : USVSTRING
+    def p_BuiltinStringTypeUSVString(self, p):
+        """
+            BuiltinStringType : USVSTRING
         """
         p[0] = IDLBuiltinType.Types.usvstring
 
     def p_UnsignedIntegerTypeUnsigned(self, p):
         """
             UnsignedIntegerType : UNSIGNED IntegerType
         """
         # Adding one to a given signed integer type gets you the unsigned type:
new file mode 100644
--- /dev/null
+++ b/servo/components/script/dom/bindings/codegen/parser/tests/test_cereactions.py
@@ -0,0 +1,162 @@
+def WebIDLTest(parser, harness):
+    threw = False
+    try:
+        parser.parse("""
+            interface Foo {
+              [CEReactions(DOMString a)] void foo(boolean arg2);
+            };
+        """)
+
+        results = parser.finish()
+    except:
+        threw = True
+
+    harness.ok(threw, "Should have thrown for [CEReactions] with an argument")
+
+    parser = parser.reset()
+    threw = False
+    try:
+        parser.parse("""
+            interface Foo {
+              [CEReactions(DOMString b)] readonly attribute boolean bar;
+            };
+        """)
+
+        results = parser.finish()
+    except:
+        threw = True
+
+    harness.ok(threw, "Should have thrown for [CEReactions] with an argument")
+
+    parser = parser.reset()
+    threw = False
+    try:
+        parser.parse("""
+            interface Foo {
+              [CEReactions] attribute boolean bar;
+            };
+        """)
+
+        results = parser.finish()
+    except Exception, e:
+        harness.ok(False, "Shouldn't have thrown for [CEReactions] used on writable attribute. %s" % e)
+        threw = True
+
+    parser = parser.reset()
+    threw = False
+    try:
+        parser.parse("""
+            interface Foo {
+              [CEReactions] void foo(boolean arg2);
+            };
+        """)
+
+        results = parser.finish()
+    except Exception, e:
+        harness.ok(False, "Shouldn't have thrown for [CEReactions] used on regular operations. %s" % e)
+        threw = True
+
+    parser = parser.reset()
+    threw = False
+    try:
+        parser.parse("""
+            interface Foo {
+              [CEReactions] readonly attribute boolean A;
+            };
+        """)
+
+        results = parser.finish()
+    except:
+        threw = True
+
+    harness.ok(threw, "Should have thrown for [CEReactions] used on a readonly attribute")
+
+    parser = parser.reset()
+    threw = False
+    try:
+        parser.parse("""
+            [CEReactions]
+            interface Foo {
+            }
+        """)
+
+        results = parser.finish()
+    except:
+        threw = True
+
+    harness.ok(threw, "Should have thrown for [CEReactions] used on a interface")
+
+    parser = parser.reset()
+    threw = False
+    try:
+        parser.parse("""
+          interface Foo {
+            [CEReactions] getter any(DOMString name);
+          };
+        """)
+        results = parser.finish()
+    except:
+        threw = True
+
+    harness.ok(threw,
+               "Should have thrown for [CEReactions] used on a named getter")
+
+    parser = parser.reset()
+    threw = False
+    try:
+        parser.parse("""
+          interface Foo {
+            [CEReactions] creator boolean (DOMString name, boolean value);
+          };
+        """)
+        results = parser.finish()
+    except:
+        threw = True
+
+    harness.ok(threw,
+               "Should have thrown for [CEReactions] used on a named creator")
+
+    parser = parser.reset()
+    threw = False
+    try:
+        parser.parse("""
+          interface Foo {
+            [CEReactions] legacycaller double compute(double x);
+          };
+        """)
+        results = parser.finish()
+    except:
+        threw = True
+
+    harness.ok(threw,
+               "Should have thrown for [CEReactions] used on a legacycaller")
+
+    parser = parser.reset()
+    threw = False
+    try:
+        parser.parse("""
+          interface Foo {
+            [CEReactions] stringifier DOMString ();
+          };
+        """)
+        results = parser.finish()
+    except:
+        threw = True
+
+    harness.ok(threw,
+               "Should have thrown for [CEReactions] used on a stringifier")
+
+    parser = parser.reset()
+    threw = False
+    try:
+        parser.parse("""
+            interface Foo {
+              [CEReactions] jsonifier;
+            };
+        """)
+
+        results = parser.finish()
+    except:
+        threw = True
+
+    harness.ok(threw, "Should have thrown for [CEReactions] used on a jsonifier")
--- a/servo/components/script/dom/bindings/codegen/parser/tests/test_constructor.py
+++ b/servo/components/script/dom/bindings/codegen/parser/tests/test_constructor.py
@@ -8,32 +8,33 @@ def WebIDLTest(parser, harness):
         harness.check(argument.identifier.name, name, "Argument has the right name")
         harness.check(str(argument.type), type, "Argument has the right return type")
         harness.check(argument.optional, optional, "Argument has the right optional value")
         harness.check(argument.variadic, variadic, "Argument has the right variadic value")
 
     def checkMethod(method, QName, name, signatures,
                     static=True, getter=False, setter=False, creator=False,
                     deleter=False, legacycaller=False, stringifier=False,
-                    chromeOnly=False):
+                    chromeOnly=False, htmlConstructor=False):
         harness.ok(isinstance(method, WebIDL.IDLMethod),
                    "Should be an IDLMethod")
         harness.ok(method.isMethod(), "Method is a method")
         harness.ok(not method.isAttr(), "Method is not an attr")
         harness.ok(not method.isConst(), "Method is not a const")
         harness.check(method.identifier.QName(), QName, "Method has the right QName")
         harness.check(method.identifier.name, name, "Method has the right name")
         harness.check(method.isStatic(), static, "Method has the correct static value")
         harness.check(method.isGetter(), getter, "Method has the correct getter value")
         harness.check(method.isSetter(), setter, "Method has the correct setter value")
         harness.check(method.isCreator(), creator, "Method has the correct creator value")
         harness.check(method.isDeleter(), deleter, "Method has the correct deleter value")
         harness.check(method.isLegacycaller(), legacycaller, "Method has the correct legacycaller value")
         harness.check(method.isStringifier(), stringifier, "Method has the correct stringifier value")
         harness.check(method.getExtendedAttribute("ChromeOnly") is not None, chromeOnly, "Method has the correct value for ChromeOnly")
+        harness.check(method.isHTMLConstructor(), htmlConstructor, "Method has the correct htmlConstructor value")
         harness.check(len(method.signatures()), len(signatures), "Method has the correct number of signatures")
 
         sigpairs = zip(method.signatures(), signatures)
         for (gotSignature, expectedSignature) in sigpairs:
             (gotRetType, gotArgs) = gotSignature
             (expectedRetType, expectedArgs) = expectedSignature
 
             harness.check(str(gotRetType), expectedRetType,
@@ -89,21 +90,184 @@ def WebIDLTest(parser, harness):
     harness.ok(isinstance(results[0], WebIDL.IDLInterface),
                "Should be an IDLInterface")
 
     checkMethod(results[0].ctor(), "::TestChromeConstructor::constructor",
                 "constructor", [("TestChromeConstructor (Wrapper)", [])],
                 chromeOnly=True)
 
     parser = parser.reset()
+    parser.parse("""
+        [HTMLConstructor]
+        interface TestHTMLConstructor {
+        };
+    """)
+    results = parser.finish()
+    harness.check(len(results), 1, "Should be one production")
+    harness.ok(isinstance(results[0], WebIDL.IDLInterface),
+               "Should be an IDLInterface")
+
+    checkMethod(results[0].ctor(), "::TestHTMLConstructor::constructor",
+                "constructor", [("TestHTMLConstructor (Wrapper)", [])],
+                htmlConstructor=True)
+
+    parser = parser.reset()
     threw = False
     try:
         parser.parse("""
         [Constructor(),
          ChromeConstructor(DOMString a)]
         interface TestChromeConstructor {
         };
         """)
         results = parser.finish()
     except:
         threw = True
 
     harness.ok(threw, "Can't have both a Constructor and a ChromeConstructor")
+
+    # Test HTMLConstructor with argument
+    parser = parser.reset()
+    threw = False
+    try:
+        parser.parse("""
+            [HTMLConstructor(DOMString a)]
+            interface TestHTMLConstructorWithArgs {
+            };
+        """)
+        results = parser.finish()
+    except:
+        threw = True
+
+    harness.ok(threw, "HTMLConstructor should take no argument")
+
+    # Test HTMLConstructor on a callback interface
+    parser = parser.reset()
+    threw = False
+    try:
+        parser.parse("""
+            [HTMLConstructor]
+            callback interface TestHTMLConstructorOnCallbackInterface {
+            };
+        """)
+        results = parser.finish()
+    except:
+        threw = True
+
+    harness.ok(threw, "HTMLConstructor can't be used on a callback interface")
+
+    # Test HTMLConstructor and Constructor
+    parser = parser.reset()
+    threw = False
+    try:
+        parser.parse("""
+            [Constructor,
+             HTMLConstructor]
+            interface TestHTMLConstructorAndConstructor {
+            };
+        """)
+        results = parser.finish()
+    except:
+        threw = True
+
+    harness.ok(threw, "Can't have both a Constructor and a HTMLConstructor")
+
+    parser = parser.reset()
+    threw = False
+    try:
+        parser.parse("""
+            [HTMLConstructor,
+             Constructor]
+            interface TestHTMLConstructorAndConstructor {
+            };
+        """)
+        results = parser.finish()
+    except:
+        threw = True
+
+    harness.ok(threw, "Can't have both a HTMLConstructor and a Constructor")
+
+    parser = parser.reset()
+    threw = False
+    try:
+        parser.parse("""
+            [HTMLConstructor,
+             Constructor(DOMString a)]
+            interface TestHTMLConstructorAndConstructor {
+            };
+        """)
+    except:
+        threw = True
+
+    harness.ok(threw, "Can't have both a HTMLConstructor and a Constructor")
+
+    parser = parser.reset()
+    threw = False
+    try:
+        parser.parse("""
+            [Constructor(DOMString a),
+             HTMLConstructor]
+            interface