Merge inbound to mozilla-central a=merge
authorCoroiu Cristina <ccoroiu@mozilla.com>
Thu, 29 Nov 2018 06:27:40 +0200
changeset 507857 5972866ac7daab2749b0fc47378169e210367a7f
parent 507817 c35dea45f131cc498de25030f91f6178751aec71 (current diff)
parent 507856 c99a9a65ebedc8c294452f57ee08c49f3c1b5b3d (diff)
child 507878 d8361a515653d512a52024aa05a4d86e68e065a3
child 507889 1a669c064a396860699bf8a194b70198a095858f
push id1905
push userffxbld-merge
push dateMon, 21 Jan 2019 12:33:13 +0000
treeherdermozilla-release@c2fca1944d8c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone65.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 inbound to mozilla-central a=merge
.clang-format-ignore
devtools/client/debugger/new/images/next.svg
js/src/tests/test262-intl-relativetimeformat.js
mobile/android/geckoview/CHANGELOG.md
mobile/android/geckoview/api.txt
mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoSession.java
--- a/.clang-format-ignore
+++ b/.clang-format-ignore
@@ -39,16 +39,17 @@ widget/tests/.*
 xpcom/glue/tests/.*
 xpcom/tests/.*
 
 # Generated by protobuf
 .*/.*.pb.h
 .*/.*.pb.cc
 
 # Autogenerated file
+gfx/gl/GLConsts.h
 gfx/webrender_bindings/webrender_ffi_generated.h
 intl/unicharutil/util/nsUnicodePropertyData.cpp
 intl/unicharutil/util/nsUnicodeScriptCodes.h
 media/mp4parse-rust/mp4parse.h
 
 # The XPTCall stubs files have some inline assembly macros
 # that get reformatted badly. See bug 1510781.
 xpcom/reflect/xptcall/md/win32/.*
--- a/browser/components/aboutconfig/content/aboutconfig.css
+++ b/browser/components/aboutconfig/content/aboutconfig.css
@@ -42,8 +42,13 @@
 #prefs > tr > td {
   padding: 4px;
   width: 50%;
 }
 
 .cell-value {
   word-break: break-all;
 }
+
+td.cell-value > form > input[type="text"] {
+  width: 100%;
+  box-sizing: border-box;
+}
--- a/browser/components/aboutconfig/content/aboutconfig.js
+++ b/browser/components/aboutconfig/content/aboutconfig.js
@@ -2,16 +2,22 @@
  * 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/. */
 
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 ChromeUtils.import("resource://gre/modules/Preferences.jsm");
 
 let gDefaultBranch = Services.prefs.getDefaultBranch("");
 let gPrefArray;
+let gPrefRowInEdit;
+let gPrefInEdit;
+
+function getPrefName(prefRow) {
+  return prefRow.getAttribute("aria-label");
+}
 
 function onLoad() {
   gPrefArray = Services.prefs.getChildList("").map(function(name) {
     let hasUserValue = Services.prefs.prefHasUserValue(name);
     let pref = {
       name,
       value: Preferences.get(name),
       hasUserValue,
@@ -28,52 +34,88 @@ function onLoad() {
       pref.value = "";
     }
     return pref;
   });
 
   gPrefArray.sort((a, b) => a.name > b.name);
 
   document.getElementById("search").addEventListener("keypress", function(e) {
-    if (e.code == "Enter") {
+    if (e.key == "Enter") {
       filterPrefs();
     }
   });
 
   document.getElementById("prefs").addEventListener("click", (event) => {
     if (event.target.localName != "button") {
       return;
     }
     let prefRow = event.target.closest("tr");
-    let prefName = prefRow.getAttribute("aria-label");
+    let prefName = getPrefName(prefRow);
+    let pref = gPrefArray.find(p => p.name == prefName);
     let button = event.target.closest("button");
     if (button.classList.contains("button-reset")) {
       // Reset pref and update gPrefArray.
       Services.prefs.clearUserPref(prefName);
-      let pref = gPrefArray.find(p => p.name == prefName);
       pref.value = Preferences.get(prefName);
       pref.hasUserValue = false;
       // Update UI.
       prefRow.textContent = "";
       prefRow.classList.remove("has-user-value");
       prefRow.appendChild(getPrefRow(pref));
+      prefRow.querySelector("td.cell-edit").firstChild.focus();
+    } else if (button.classList.contains("add-true")) {
+      addNewPref(prefRow.firstChild.innerHTML, true);
+    } else if (button.classList.contains("add-false")) {
+      addNewPref(prefRow.firstChild.innerHTML, false);
+    } else if (button.classList.contains("add-Number") ||
+               button.classList.contains("add-String")) {
+      addNewPref(prefRow.firstChild.innerHTML,
+                 button.classList.contains("add-Number") ? 0 : "");
+      prefRow = [...document.getElementById("prefs").getElementsByTagName("tr")]
+                .find(row => row.querySelector("td").textContent == prefName);
+      startEditingPref(prefRow, gPrefArray.find(p => p.name == prefName));
+      prefRow.querySelector("td.cell-value").firstChild.firstChild.focus();
+    } else if (button.classList.contains("button-toggle")) {
+      // Toggle the pref and update gPrefArray.
+      Services.prefs.setBoolPref(prefName, !pref.value);
+      pref.value = !pref.value;
+      pref.hasUserValue = Services.prefs.prefHasUserValue(pref.name);
+      // Update UI.
+      prefRow.textContent = "";
+      if (pref.hasUserValue) {
+        prefRow.classList.add("has-user-value");
+      } else {
+        prefRow.classList.remove("has-user-value");
+      }
+      prefRow.appendChild(getPrefRow(pref));
+      prefRow.querySelector("td.cell-edit").firstChild.focus();
+    } else if (button.classList.contains("button-edit")) {
+      startEditingPref(prefRow, pref);
+      prefRow.querySelector("td.cell-value").firstChild.firstChild.focus();
+    } else if (button.classList.contains("button-save")) {
+      endEditingPref(prefRow);
+      prefRow.querySelector("td.cell-edit").firstChild.focus();
     } else {
       Services.prefs.clearUserPref(prefName);
-      gPrefArray.splice(gPrefArray.findIndex(pref => pref.name == prefName), 1);
+      gPrefArray.splice(gPrefArray.findIndex(p => p.name == prefName), 1);
       prefRow.remove();
     }
   });
 
-  document.getElementById("prefs").appendChild(createPrefsFragment(gPrefArray));
+  filterPrefs();
 }
 
 function filterPrefs() {
   let substring = document.getElementById("search").value.trim();
+  document.getElementById("prefs").textContent = "";
+  if (substring && !gPrefArray.some(pref => pref.name == substring)) {
+    document.getElementById("prefs").appendChild(createNewPrefFragment(substring));
+  }
   let fragment = createPrefsFragment(gPrefArray.filter(pref => pref.name.includes(substring)));
-  document.getElementById("prefs").textContent = "";
   document.getElementById("prefs").appendChild(fragment);
 }
 
 function createPrefsFragment(prefArray) {
   let fragment = document.createDocumentFragment();
   for (let pref of prefArray) {
     let row = document.createElement("tr");
     if (pref.hasUserValue) {
@@ -82,55 +124,181 @@ function createPrefsFragment(prefArray) 
     row.setAttribute("aria-label", pref.name);
 
     row.appendChild(getPrefRow(pref));
     fragment.appendChild(row);
   }
   return fragment;
 }
 
+function createNewPrefFragment(name) {
+  let fragment = document.createDocumentFragment();
+  let row = document.createElement("tr");
+  row.classList.add("has-user-value");
+  row.setAttribute("aria-label", name);
+  let nameCell = document.createElement("td");
+  nameCell.append(name);
+  row.appendChild(nameCell);
+
+  let valueCell = document.createElement("td");
+  valueCell.classList.add("cell-value");
+  let guideText = document.createElement("span");
+  document.l10n.setAttributes(guideText, "about-config-pref-add");
+  valueCell.appendChild(guideText);
+  for (let item of ["true", "false", "Number", "String"]) {
+    let optionBtn = document.createElement("button");
+    optionBtn.textContent = item;
+    optionBtn.classList.add("add-" + item);
+    valueCell.appendChild(optionBtn);
+  }
+  row.appendChild(valueCell);
+
+  let editCell = document.createElement("td");
+  row.appendChild(editCell);
+
+  let buttonCell = document.createElement("td");
+  row.appendChild(buttonCell);
+
+  fragment.appendChild(row);
+  return fragment;
+}
+
 function getPrefRow(pref) {
   let rowFragment = document.createDocumentFragment();
   let nameCell = document.createElement("td");
   // Add <wbr> behind dots to prevent line breaking in random mid-word places.
   let parts = pref.name.split(".");
   for (let i = 0; i < parts.length - 1; i++) {
     nameCell.append(parts[i] + ".", document.createElement("wbr"));
   }
   nameCell.append(parts[parts.length - 1]);
   rowFragment.appendChild(nameCell);
 
   let valueCell = document.createElement("td");
   valueCell.classList.add("cell-value");
   valueCell.textContent = pref.value;
   rowFragment.appendChild(valueCell);
 
+  let editCell = document.createElement("td");
+  editCell.className = "cell-edit";
+  let button = document.createElement("button");
+  if (Services.prefs.getPrefType(pref.name) == Services.prefs.PREF_BOOL) {
+    document.l10n.setAttributes(button, "about-config-pref-toggle");
+    button.className = "button-toggle";
+  } else {
+    document.l10n.setAttributes(button, "about-config-pref-edit");
+    button.className = "button-edit";
+  }
+  editCell.appendChild(button);
+  rowFragment.appendChild(editCell);
+
   let buttonCell = document.createElement("td");
   if (pref.hasUserValue) {
-    let button = document.createElement("button");
+    let resetButton = document.createElement("button");
     if (!pref.hasDefaultValue) {
-      document.l10n.setAttributes(button, "about-config-pref-delete");
+      document.l10n.setAttributes(resetButton, "about-config-pref-delete");
     } else {
-      document.l10n.setAttributes(button, "about-config-pref-reset");
-      button.className = "button-reset";
+      document.l10n.setAttributes(resetButton, "about-config-pref-reset");
+      resetButton.className = "button-reset";
     }
-    buttonCell.appendChild(button);
+    buttonCell.appendChild(resetButton);
   }
   rowFragment.appendChild(buttonCell);
   return rowFragment;
 }
 
+function startEditingPref(row, arrayEntry) {
+  if (gPrefRowInEdit != undefined) {
+    // Abort editing-process first.
+    gPrefRowInEdit.textContent = "";
+    gPrefRowInEdit.appendChild(getPrefRow(gPrefInEdit));
+  }
+  gPrefRowInEdit = row;
+
+  let name = getPrefName(row);
+  gPrefInEdit = arrayEntry;
+
+  let valueCell = row.querySelector("td.cell-value");
+  let oldValue = valueCell.textContent;
+  valueCell.textContent = "";
+  // The form is needed for the invalid-tooltip to appear.
+  let form = document.createElement("form");
+  form.id = "form-" + name;
+  let inputField = document.createElement("input");
+  inputField.type = "text";
+  inputField.value = oldValue;
+  if (Services.prefs.getPrefType(name) == Services.prefs.PREF_INT) {
+    inputField.setAttribute("pattern", "-?[0-9]*");
+    document.l10n.setAttributes(inputField, "about-config-pref-input-number");
+  } else {
+    document.l10n.setAttributes(inputField, "about-config-pref-input-string");
+  }
+  inputField.placeholder = oldValue;
+  form.appendChild(inputField);
+  valueCell.appendChild(form);
+
+  let buttonCell = row.querySelector("td.cell-edit");
+  buttonCell.childNodes[0].remove();
+  let button = document.createElement("button");
+  button.classList.add("primary", "button-save");
+  document.l10n.setAttributes(button, "about-config-pref-save");
+  button.setAttribute("form", form.id);
+  buttonCell.appendChild(button);
+}
+
+function endEditingPref(row) {
+  let name = gPrefInEdit.name;
+  let input = row.querySelector("td.cell-value").firstChild.firstChild;
+  let newValue = input.value;
+
+  if (Services.prefs.getPrefType(name) == Services.prefs.PREF_INT) {
+    let numberValue = parseInt(newValue);
+    if (!/^-?[0-9]*$/.test(newValue) || isNaN(numberValue)) {
+      input.setCustomValidity(input.title);
+      return;
+    }
+    newValue = numberValue;
+    Services.prefs.setIntPref(name, newValue);
+  } else {
+    Services.prefs.setStringPref(name, newValue);
+  }
+
+  // Update gPrefArray.
+  gPrefInEdit.value = newValue;
+  gPrefInEdit.hasUserValue = Services.prefs.prefHasUserValue(name);
+  // Update UI.
+  row.textContent = "";
+  if (gPrefInEdit.hasUserValue) {
+    row.classList.add("has-user-value");
+  } else {
+    row.classList.remove("has-user-value");
+  }
+  row.appendChild(getPrefRow(gPrefInEdit));
+}
+
 function prefHasDefaultValue(name) {
   try {
     switch (Services.prefs.getPrefType(name)) {
       case Ci.nsIPrefBranch.PREF_STRING:
         gDefaultBranch.getStringPref(name);
         return true;
       case Ci.nsIPrefBranch.PREF_INT:
         gDefaultBranch.getIntPref(name);
         return true;
       case Ci.nsIPrefBranch.PREF_BOOL:
         gDefaultBranch.getBoolPref(name);
         return true;
     }
   } catch (ex) {}
   return false;
 }
+
+function addNewPref(name, value) {
+  Preferences.set(name, value);
+  gPrefArray.push({
+    name,
+    value,
+    hasUserValue: true,
+    hasDefaultValue: false,
+  });
+  gPrefArray.sort((a, b) => a.name > b.name);
+  filterPrefs();
+}
--- a/browser/components/aboutconfig/content/aboutconfig.notftl
+++ b/browser/components/aboutconfig/content/aboutconfig.notftl
@@ -2,10 +2,18 @@
 # 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/.
 
 about-config-title = about:config
 
 about-config-search =
     .placeholder = Search
 
+about-config-pref-add = Add as:
+about-config-pref-toggle = Toggle
+about-config-pref-edit = Edit
+about-config-pref-input-number =
+    .title = Please enter an integer value
+about-config-pref-input-string =
+    .title = Please enter the value
+about-config-pref-save = Save
 about-config-pref-reset = Reset
 about-config-pref-delete = Delete
--- a/browser/components/aboutconfig/test/browser/browser_edit.js
+++ b/browser/components/aboutconfig/test/browser/browser_edit.js
@@ -1,13 +1,67 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
+ChromeUtils.import("resource://gre/modules/Preferences.jsm", this);
+
 const PAGE_URL = "chrome://browser/content/aboutconfig/aboutconfig.html";
 
+add_task(async function setup() {
+  await SpecialPowers.pushPrefEnv({
+    set: [
+      ["test.aboutconfig.modify.boolean", true],
+      ["test.aboutconfig.modify.number", 1337],
+      ["test.aboutconfig.modify.string", "the answer to the life the universe and everything"],
+    ],
+  });
+
+  registerCleanupFunction(() => {
+    Services.prefs.clearUserPref("accessibility.typeaheadfind.autostart");
+    Services.prefs.clearUserPref("accessibility.typeaheadfind.soundURL");
+    Services.prefs.clearUserPref("accessibility.typeaheadfind.casesensitive");
+  });
+});
+
+add_task(async function test_add_user_pref() {
+  await BrowserTestUtils.withNewTab({
+    gBrowser,
+    url: PAGE_URL,
+  }, async browser => {
+    await ContentTask.spawn(browser, null, () => {
+      Assert.ok(!Services.prefs.getChildList("").find(pref => pref == "testPref"));
+      let search = content.document.getElementById("search");
+      search.value = "testPref";
+      search.focus();
+    });
+
+    for (let [buttonSelector, expectedValue] of [
+      [".add-true", true],
+      [".add-false", false],
+      [".add-Number", 0],
+      [".add-String", ""],
+    ]) {
+      EventUtils.sendKey("return");
+      await ContentTask.spawn(browser, [buttonSelector, expectedValue],
+                                      // eslint-disable-next-line no-shadow
+                                      ([buttonSelector, expectedValue]) => {
+        ChromeUtils.import("resource://gre/modules/Preferences.jsm");
+
+        content.document.querySelector("#prefs button" + buttonSelector).click();
+        Assert.ok(Services.prefs.getChildList("").find(pref => pref == "testPref"));
+        Assert.ok(Preferences.get("testPref") === expectedValue);
+        content.document.querySelector("#prefs button[data-l10n-id='about-config-pref-delete']").click();
+        let search = content.document.getElementById("search");
+        search.value = "testPref";
+        search.focus();
+      });
+    }
+  });
+});
+
 add_task(async function test_delete_user_pref() {
   Services.prefs.setBoolPref("userAddedPref", true);
   await BrowserTestUtils.withNewTab({
     gBrowser,
     url: PAGE_URL,
   }, async browser => {
     await ContentTask.spawn(browser, null, () => {
       let list = [...content.document.getElementById("prefs")
@@ -60,17 +114,17 @@ add_task(async function test_reset_user_
       let list = [...content.document.getElementById("prefs")
         .getElementsByTagName("tr")];
       let row = getRow(testPref);
       row.lastChild.lastChild.click();
       // Check new layout and reset.
       list = [...content.document.getElementById("prefs")
         .getElementsByTagName("tr")];
       Assert.ok(!row.classList.contains("has-user-value"));
-      Assert.equal(row.childNodes[2].childNodes.length, 0);
+      Assert.equal(row.lastChild.childNodes.length, 0);
       Assert.ok(!Services.prefs.prefHasUserValue(testPref));
       Assert.equal(getValue(testPref), "" + Preferences.get(testPref));
 
       // Search for nothing to test gPrefArray
       let search = content.document.getElementById("search");
       search.focus();
     });
 
@@ -83,13 +137,91 @@ add_task(async function test_reset_user_
         return getRow(name).querySelector("td.cell-value").textContent;
       }
       let testPref = "browser.autofocus";
       // Check new layout and reset.
       let list = [...content.document.getElementById("prefs")
         .getElementsByTagName("tr")];
       let row = getRow(testPref);
       Assert.ok(!row.classList.contains("has-user-value"));
-      Assert.equal(row.childNodes[2].childNodes.length, 0);
+      Assert.equal(row.lastChild.childNodes.length, 0);
       Assert.equal(getValue(testPref), "" + Preferences.get(testPref));
     });
   });
 });
+
+add_task(async function test_modify() {
+  await BrowserTestUtils.withNewTab({
+    gBrowser,
+    url: PAGE_URL,
+  }, browser => {
+    return ContentTask.spawn(browser, null, () => {
+      ChromeUtils.import("resource://gre/modules/Preferences.jsm");
+
+      function getRow(name, list) {
+        return list.find(row => row.querySelector("td").textContent == name);
+      }
+      function getValue(name, list) {
+        return getRow(name, list).querySelector("td.cell-value").textContent;
+      }
+
+      // Test toggle for boolean prefs.
+      let list = [...content.document.getElementById("prefs")
+        .getElementsByTagName("tr")];
+      for (let nameOfBoolPref of [
+        "test.aboutconfig.modify.boolean",
+        "accessibility.typeaheadfind.autostart",
+      ]) {
+        let row = getRow(nameOfBoolPref, list);
+        // Do this a two times to reset the pref.
+        for (let i = 0; i < 2; i++) {
+          row.querySelector("td.cell-edit").firstChild.click();
+          // Check new layout and saving in backend.
+          Assert.equal(getValue(nameOfBoolPref, list),
+            "" + Preferences.get(nameOfBoolPref));
+          let prefHasUserValue = Services.prefs.prefHasUserValue(nameOfBoolPref);
+          Assert.equal(row.classList.contains("has-user-value"), prefHasUserValue);
+          Assert.equal(row.lastChild.childNodes.length > 0, prefHasUserValue);
+        }
+      }
+
+      // Test abort of edit by starting with string and continuing with editing Int pref.
+      let row = getRow("test.aboutconfig.modify.string", list);
+      row.querySelector("td.cell-edit").firstChild.click();
+      row.querySelector("td.cell-value").firstChild.firstChild.value = "test";
+      let intRow = getRow("test.aboutconfig.modify.number", list);
+      intRow.querySelector("td.cell-edit").firstChild.click();
+      Assert.equal(intRow.querySelector("td.cell-value").firstChild.firstChild.value,
+        Preferences.get("test.aboutconfig.modify.number"));
+      Assert.equal(getValue("test.aboutconfig.modify.string", list),
+        "" + Preferences.get("test.aboutconfig.modify.string"));
+      Assert.equal(row.querySelector("td.cell-value").textContent,
+        Preferences.get("test.aboutconfig.modify.string"));
+
+      // Test regex check for Int pref.
+      intRow.querySelector("td.cell-value").firstChild.firstChild.value += "a";
+      intRow.querySelector("td.cell-edit").firstChild.click();
+      Assert.ok(!intRow.querySelector("td.cell-value").firstChild.firstChild.checkValidity());
+
+      // Test correct saving and DOM-update.
+      for (let prefName of [
+        "test.aboutconfig.modify.string",
+        "test.aboutconfig.modify.number",
+        "accessibility.typeaheadfind.soundURL",
+        "accessibility.typeaheadfind.casesensitive",
+      ]) {
+        row = getRow(prefName, list);
+        // Activate edit and check displaying.
+        row.querySelector("td.cell-edit").firstChild.click();
+        Assert.equal(row.querySelector("td.cell-value").firstChild.firstChild.value,
+          Preferences.get(prefName));
+        row.querySelector("td.cell-value").firstChild.firstChild.value = "42";
+        // Save and check saving.
+        row.querySelector("td.cell-edit").firstChild.click();
+        Assert.equal(getValue(prefName, list),
+          "" + Preferences.get(prefName));
+        let prefHasUserValue = Services.prefs.prefHasUserValue(prefName);
+        Assert.equal(row.lastChild.childNodes.length > 0, prefHasUserValue);
+        Assert.equal(row.classList.contains("has-user-value"), prefHasUserValue);
+      }
+    });
+  });
+});
--- a/browser/components/aboutconfig/test/browser/browser_search.js
+++ b/browser/components/aboutconfig/test/browser/browser_search.js
@@ -32,18 +32,21 @@ add_task(async function test_search() {
       search.value = "button   ";
       search.focus();
     });
 
     EventUtils.sendKey("return");
     await ContentTask.spawn(browser, prefArray, aPrefArray => {
       let filteredPrefArray =
           aPrefArray.filter(pref => pref.includes("button"));
+      // Adding +1 to the list since button does not match an exact
+      // preference name then a row is added for the user to add a
+      // new button preference if desired
       Assert.equal(content.document.getElementById("prefs").childElementCount,
-                   filteredPrefArray.length);
+                   filteredPrefArray.length + 1);
 
       // Test empty search returns all preferences.
       let search = content.document.getElementById("search");
       search.value = "";
       search.focus();
     });
 
     EventUtils.sendKey("return");
@@ -54,18 +57,19 @@ add_task(async function test_search() {
       // Test invalid search returns no preferences.
       let search = content.document.getElementById("search");
       search.value = "aJunkValueasdf";
       search.focus();
     });
 
     EventUtils.sendKey("return");
     await ContentTask.spawn(browser, prefArray, aPrefArray => {
+      // Expecting 1 row to be returned since it offers the ability to add
       Assert.equal(content.document.getElementById("prefs").childElementCount,
-                   0);
+                   1);
 
       // Test added preferences search returns 2 preferences.
       let search = content.document.getElementById("search");
       search.value = "test.aboutconfig.a";
       search.focus();
     });
 
     EventUtils.sendKey("return");
--- a/config/make-stl-wrappers.py
+++ b/config/make-stl-wrappers.py
@@ -1,34 +1,17 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 from __future__ import print_function
 import os
 import string
 from mozbuild.util import FileAvoidWrite
-
-
-def find_in_path(file, searchpath):
-    for dir in searchpath.split(os.pathsep):
-        f = os.path.join(dir, file)
-        if os.path.exists(f):
-            return f
-    return ''
-
+from system_header_util import header_path
 
-def header_path(header, compiler):
-    if compiler == 'gcc':
-        # we use include_next on gcc
-        return header
-    elif compiler == 'msvc':
-        return find_in_path(header, os.environ.get('INCLUDE', ''))
-    else:
-        # hope someone notices this ...
-        raise NotImplementedError(compiler)
 
 # The 'unused' arg is the output file from the file_generate action. We actually
 # generate all the files in header_list
 
 
 def gen_wrappers(unused, outdir, compiler, template_file, *header_list):
     template = open(template_file, 'r').read()
 
new file mode 100644
--- /dev/null
+++ b/config/make-windows-h-wrapper.py
@@ -0,0 +1,86 @@
+# 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/.
+
+import re
+import textwrap
+import string
+from system_header_util import header_path
+
+comment_re = re.compile(r'//[^\n]*\n|/\*.*\*/', re.S)
+decl_re = re.compile(r'''^(.+)\s+        # type
+                         (\w+)\s*        # name
+                         (?:\((.*)\))?$  # optional param tys
+                         ''', re.X | re.S)
+
+
+def read_decls(filename):
+    """Parse & yield C-style decls from an input file"""
+    with open(filename, 'r') as fd:
+        # Strip comments from the source text.
+        text = comment_re.sub('', fd.read())
+
+        # Parse individual declarations.
+        raw_decls = [d.strip() for d in text.split(';') if d.strip()]
+        for raw in raw_decls:
+            match = decl_re.match(raw)
+            if match is None:
+                raise "Invalid decl: %s" % raw
+
+            ty, name, params = match.groups()
+            if params is not None:
+                params = [a.strip() for a in params.split(',') if a.strip()]
+            yield ty, name, params
+
+
+def generate(fd, consts_path, unicodes_path, template_path, compiler):
+    # Parse the template
+    with open(template_path, 'r') as template_fd:
+        template = string.Template(template_fd.read())
+
+    decls = ''
+
+    # Each constant should be saved to a temporary, and then re-assigned to a
+    # constant with the correct name, allowing the value to be determined by
+    # the actual definition.
+    for ty, name, args in read_decls(consts_path):
+        assert args is None, "parameters in const decl!"
+
+        decls += textwrap.dedent("""
+            #ifdef {name}
+            constexpr {ty} _tmp_{name} = {name};
+            #undef {name}
+            constexpr {ty} {name} = _tmp_{name};
+            #endif
+            """.format(ty=ty, name=name))
+
+    # Each unicode declaration defines a static inline function with the
+    # correct types which calls the 'A' or 'W'-suffixed versions of the
+    # function. Full types are required here to ensure that '0' to 'nullptr'
+    # coersions are preserved.
+    for ty, name, args in read_decls(unicodes_path):
+        assert args is not None, "argument list required for unicode decl"
+
+        # Parameter & argument string list
+        params = ', '.join('%s a%d' % (ty, i) for i, ty in enumerate(args))
+        args = ', '.join('a%d' % i for i in range(len(args)))
+
+        decls += textwrap.dedent("""
+            #ifdef {name}
+            #undef {name}
+            static inline {ty} WINAPI
+            {name}({params})
+            {{
+            #ifdef UNICODE
+              return {name}W({args});
+            #else
+              return {name}A({args});
+            #endif
+            }}
+            #endif
+            """.format(ty=ty, name=name, params=params, args=args))
+
+    path = header_path('windows.h', compiler)
+
+    # Write out the resulting file
+    fd.write(template.substitute(header_path=path, decls=decls))
--- a/config/moz.build
+++ b/config/moz.build
@@ -58,16 +58,29 @@ if CONFIG['WRAP_STL_INCLUDES']:
         # the files in gen_wrappers()
         outputs = tuple(['stl.sentinel'] + ['%s/%s' % (output_dir, h) for h in stl_headers])
         GENERATED_FILES += [outputs]
         stl = GENERATED_FILES[outputs]
         stl.script = 'make-stl-wrappers.py:gen_wrappers'
         stl.flags = [output_dir, stl_compiler, template_file]
         stl.flags.extend(stl_headers)
 
+    # Wrap <windows.h> to make it easier to use correctly
+    # NOTE: If we aren't wrapping STL includes, we're building part of the browser
+    # which won't need this wrapper, such as L10N. Just don't try to generate the
+    # wrapper in that case.
+    if CONFIG['OS_ARCH'] == 'WINNT':
+        GENERATED_FILES += ['../dist/stl_wrappers/windows.h']
+        windows_h = GENERATED_FILES['../dist/stl_wrappers/windows.h']
+        windows_h.script = 'make-windows-h-wrapper.py:generate'
+        windows_h.inputs = ['windows-h-constant.decls.h',
+                            'windows-h-unicode.decls.h',
+                            'windows-h-wrapper.template.h']
+        windows_h.flags = [stl_compiler]
+
 if CONFIG['WRAP_SYSTEM_INCLUDES']:
     include('system-headers.mozbuild')
     output_dir = '../dist/system_wrappers'
     outputs = tuple(['system-header.sentinel'] + ['%s/%s' % (output_dir, h) for h in stl_headers + system_headers])
     GENERATED_FILES += [outputs]
     system = GENERATED_FILES[outputs]
     system.script = 'make-system-wrappers.py:gen_wrappers'
     system.flags = [output_dir]
new file mode 100644
--- /dev/null
+++ b/config/system_header_util.py
@@ -0,0 +1,20 @@
+import os
+
+
+def find_in_path(file, searchpath):
+    for dir in searchpath.split(os.pathsep):
+        f = os.path.join(dir, file)
+        if os.path.exists(f):
+            return f
+    return ''
+
+
+def header_path(header, compiler):
+    if compiler == 'gcc':
+        # we use include_next on gcc
+        return header
+    elif compiler == 'msvc':
+        return find_in_path(header, os.environ.get('INCLUDE', ''))
+    else:
+        # hope someone notices this ...
+        raise NotImplementedError(compiler)
new file mode 100644
--- /dev/null
+++ b/config/windows-h-constant.decls.h
@@ -0,0 +1,57 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * This file contains a series of C-style declarations for constants defined in
+ * windows.h using #define. Adding a new constant should be a simple as adding
+ * its name (and optionally type) to this file.
+ *
+ * This file is processed by generate-windows-h-wrapper.py to generate a wrapper
+ * for the header which removes the defines usually implementing these constants.
+ *
+ * Wrappers defined in this file will be declared as `constexpr` values,
+ * and will have their value derived from the windows.h define.
+ *
+ * NOTE: This is *NOT* a real C header, but rather an input to the avove script.
+ * Only basic declarations in the form found here are allowed.
+ */
+
+// XXX(nika): There are a lot of these (>30k)!
+// This is just a set of ones I saw in a quick scan which looked problematic.
+
+auto CREATE_NEW;
+auto CREATE_ALWAYS;
+auto OPEN_EXISTING;
+auto OPEN_ALWAYS;
+auto TRUNCATE_EXISTING;
+auto INVALID_FILE_SIZE;
+auto INVALID_SET_FILE_POINTER;
+auto INVALID_FILE_ATTRIBUTES;
+
+auto ANSI_NULL;
+auto UNICODE_NULL;
+
+auto MINCHAR;
+auto MAXCHAR;
+auto MINSHORT;
+auto MAXSHORT;
+auto MINLONG;
+auto MAXLONG;
+auto MAXBYTE;
+auto MAXWORD;
+auto MAXDWORD;
+
+auto DELETE;
+auto READ_CONTROL;
+auto WRITE_DAC;
+auto WRITE_OWNER;
+auto SYNCHRONIZE;
+
+auto MAXIMUM_ALLOWED;
+auto GENERIC_READ;
+auto GENERIC_WRITE;
+auto GENERIC_EXECUTE;
+auto GENERIC_ALL;
new file mode 100644
--- /dev/null
+++ b/config/windows-h-unicode.decls.h
@@ -0,0 +1,1052 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * This file contains a series of C-style function prototypes for A/W-suffixed
+ * Win32 APIs defined by windows.h.
+ *
+ * This file is processed by generate-windows-h-wrapper.py to generate a wrapper
+ * for the header which removes the defines usually implementing these aliases.
+ *
+ * Wrappers defined in this file will have the 'stdcall' calling convention,
+ * will be defined as 'inline', and will only be defined if the corresponding
+ * #define directive has not been #undef-ed.
+ *
+ * NOTE: This is *NOT* a real C header, but rather an input to the avove script.
+ * Only basic declarations in the form found here are allowed.
+ */
+
+LPTSTR GetCommandLine();
+
+BOOL FreeEnvironmentStrings(LPTCH);
+
+DWORD GetEnvironmentVariable(LPCTSTR, LPTSTR, DWORD);
+
+BOOL SetEnvironmentVariable(LPCTSTR, LPCTSTR);
+
+DWORD ExpandEnvironmentStrings(LPCTSTR, LPTSTR, DWORD);
+
+BOOL SetCurrentDirectory(LPCTSTR);
+
+DWORD GetCurrentDirectory(DWORD, LPTSTR);
+
+DWORD SearchPath(LPCTSTR, LPCTSTR, LPCTSTR, DWORD, LPTSTR, LPTSTR*);
+
+BOOL NeedCurrentDirectoryForExePath(LPCTSTR);
+
+BOOL CreateDirectory(LPCTSTR, LPSECURITY_ATTRIBUTES);
+
+HANDLE CreateFile(LPCTSTR, DWORD, DWORD, LPSECURITY_ATTRIBUTES, DWORD, DWORD, HANDLE);
+
+BOOL DeleteFile(LPCTSTR);
+
+HANDLE FindFirstChangeNotification(LPCTSTR, BOOL, DWORD);
+
+HANDLE FindFirstFile(LPCTSTR, LPWIN32_FIND_DATA);
+
+HANDLE FindFirstFileEx(LPCTSTR, FINDEX_INFO_LEVELS, LPVOID, FINDEX_SEARCH_OPS, LPVOID, DWORD);
+
+BOOL FindNextFile(HANDLE, LPWIN32_FIND_DATA);
+
+BOOL GetDiskFreeSpace(LPCTSTR, LPDWORD, LPDWORD, LPDWORD, LPDWORD);
+
+BOOL GetDiskFreeSpaceEx(LPCTSTR, PULARGE_INTEGER, PULARGE_INTEGER, PULARGE_INTEGER);
+
+UINT GetDriveType(LPCTSTR);
+
+DWORD GetFileAttributes(LPCTSTR);
+
+BOOL GetFileAttributesEx(LPCTSTR, GET_FILEEX_INFO_LEVELS, LPVOID);
+
+DWORD GetFinalPathNameByHandle(HANDLE, LPTSTR, DWORD, DWORD);
+
+DWORD GetFullPathName(LPCTSTR, DWORD, LPTSTR, LPTSTR*);
+
+DWORD GetLongPathName(LPCTSTR, LPTSTR, DWORD);
+
+BOOL RemoveDirectory(LPCTSTR);
+
+BOOL SetFileAttributes(LPCTSTR, DWORD);
+
+DWORD GetCompressedFileSize(LPCTSTR, LPDWORD);
+
+DWORD GetTempPath(DWORD, LPTSTR);
+
+BOOL GetVolumeInformation(LPCTSTR, LPTSTR, DWORD, LPDWORD, LPDWORD, LPDWORD, LPTSTR, DWORD);
+
+UINT GetTempFileName(LPCTSTR, LPCTSTR, UINT, LPTSTR);
+
+void OutputDebugString(LPCTSTR);
+
+void FatalAppExit(UINT, LPCTSTR);
+
+HANDLE CreateMutex(LPSECURITY_ATTRIBUTES, BOOL, LPCTSTR);
+
+HANDLE CreateEvent(LPSECURITY_ATTRIBUTES, BOOL, BOOL, LPCTSTR);
+
+HANDLE OpenEvent(DWORD, BOOL, LPCTSTR);
+
+HANDLE CreateMutexEx(LPSECURITY_ATTRIBUTES, LPCTSTR, DWORD, DWORD);
+
+HANDLE CreateEventEx(LPSECURITY_ATTRIBUTES, LPCTSTR, DWORD, DWORD);
+
+BOOL CreateProcess(LPCTSTR, LPTSTR, LPSECURITY_ATTRIBUTES, LPSECURITY_ATTRIBUTES, BOOL, DWORD, LPVOID, LPCTSTR, LPSTARTUPINFO, LPPROCESS_INFORMATION);
+
+BOOL CreateProcessAsUser(HANDLE, LPCTSTR, LPTSTR, LPSECURITY_ATTRIBUTES, LPSECURITY_ATTRIBUTES, BOOL, DWORD, LPVOID, LPCTSTR, LPSTARTUPINFO, LPPROCESS_INFORMATION);
+
+UINT GetSystemDirectory(LPTSTR, UINT);
+
+UINT GetWindowsDirectory(LPTSTR, UINT);
+
+UINT GetSystemWindowsDirectory(LPTSTR, UINT);
+
+BOOL GetComputerNameEx(COMPUTER_NAME_FORMAT, LPTSTR, LPDWORD);
+
+BOOL GetVersionEx(LPOSVERSIONINFO);
+
+BOOL SetComputerName(LPCTSTR);
+
+BOOL SetComputerNameEx(COMPUTER_NAME_FORMAT, LPCTSTR);
+
+BOOL LoadEnclaveImage(LPVOID, LPCTSTR);
+
+UINT GetSystemWow64Directory(LPTSTR, UINT);
+
+DWORD GetModuleFileName(HMODULE, LPTSTR, DWORD);
+
+HMODULE GetModuleHandle(LPCTSTR);
+
+BOOL GetModuleHandleEx(DWORD, LPCTSTR, HMODULE*);
+
+HMODULE LoadLibraryEx(LPCTSTR, HANDLE, DWORD);
+
+int LoadString(HINSTANCE, UINT, LPTSTR, int);
+
+BOOL EnumResourceLanguagesEx(HMODULE, LPCTSTR, LPCTSTR, ENUMRESLANGPROC, LONG_PTR, DWORD, LANGID);
+
+BOOL EnumResourceNamesEx(HMODULE, LPCTSTR, ENUMRESNAMEPROC, LONG_PTR, DWORD, LANGID);
+
+BOOL EnumResourceTypesEx(HMODULE, ENUMRESTYPEPROC, LONG_PTR, DWORD, LANGID);
+
+HMODULE LoadLibrary(LPCTSTR);
+
+BOOL GetBinaryType(LPCTSTR, LPDWORD);
+
+DWORD GetShortPathName(LPCTSTR, LPTSTR, DWORD);
+
+DWORD GetLongPathNameTransacted(LPCTSTR, LPTSTR, DWORD, HANDLE);
+
+BOOL SetEnvironmentStrings(LPTCH);
+
+BOOL SetFileShortName(HANDLE, LPCTSTR);
+
+DWORD FormatMessage(DWORD, LPCVOID, DWORD, DWORD, LPTSTR, DWORD, va_list *);
+
+HANDLE CreateMailslot(LPCTSTR, DWORD, DWORD, LPSECURITY_ATTRIBUTES);
+
+BOOL EncryptFile(LPCTSTR);
+
+BOOL DecryptFile(LPCTSTR, DWORD);
+
+BOOL FileEncryptionStatus(LPCTSTR, LPDWORD);
+
+DWORD OpenEncryptedFileRaw(LPCTSTR, ULONG, PVOID *);
+
+HANDLE OpenMutex(DWORD, BOOL, LPCTSTR);
+
+HANDLE CreateSemaphore(LPSECURITY_ATTRIBUTES, LONG, LONG, LPCTSTR);
+
+HANDLE OpenSemaphore(DWORD, BOOL, LPCTSTR);
+
+HANDLE CreateWaitableTimer(LPSECURITY_ATTRIBUTES, BOOL, LPCTSTR);
+
+HANDLE OpenWaitableTimer(DWORD, BOOL, LPCTSTR);
+
+HANDLE CreateSemaphoreEx(LPSECURITY_ATTRIBUTES, LONG, LONG, LPCTSTR, DWORD, DWORD);
+
+HANDLE CreateWaitableTimerEx(LPSECURITY_ATTRIBUTES, LPCTSTR, DWORD, DWORD);
+
+HANDLE CreateFileMapping(HANDLE, LPSECURITY_ATTRIBUTES, DWORD, DWORD, DWORD, LPCTSTR);
+
+HANDLE CreateFileMappingNuma(HANDLE, LPSECURITY_ATTRIBUTES, DWORD, DWORD, DWORD, LPCTSTR, DWORD);
+
+HANDLE OpenFileMapping(DWORD, BOOL, LPCTSTR);
+
+DWORD GetLogicalDriveStrings(DWORD, LPTSTR);
+
+void GetStartupInfo(LPSTARTUPINFO);
+
+DWORD GetFirmwareEnvironmentVariable(LPCTSTR, LPCTSTR, PVOID, DWORD);
+
+BOOL SetFirmwareEnvironmentVariable(LPCTSTR, LPCTSTR, PVOID, DWORD);
+
+HRSRC FindResource(HMODULE, LPCTSTR, LPCTSTR);
+
+HRSRC FindResourceEx(HMODULE, LPCTSTR, LPCTSTR, WORD);
+
+BOOL EnumResourceTypes(HMODULE, ENUMRESTYPEPROC, LONG_PTR);
+
+BOOL EnumResourceNames(HMODULE, LPCTSTR, ENUMRESNAMEPROC, LONG_PTR);
+
+BOOL EnumResourceLanguages(HMODULE, LPCTSTR, LPCTSTR, ENUMRESLANGPROC, LONG_PTR);
+
+HANDLE BeginUpdateResource(LPCTSTR, BOOL);
+
+BOOL UpdateResource(HANDLE, LPCTSTR, LPCTSTR, WORD, LPVOID, DWORD);
+
+BOOL EndUpdateResource(HANDLE, BOOL);
+
+ATOM GlobalAddAtom(LPCTSTR);
+
+ATOM GlobalAddAtomEx(LPCTSTR, DWORD);
+
+ATOM GlobalFindAtom(LPCTSTR);
+
+UINT GlobalGetAtomName(ATOM, LPTSTR, int);
+
+ATOM AddAtom(LPCTSTR);
+
+ATOM FindAtom(LPCTSTR);
+
+UINT GetAtomName(ATOM, LPTSTR, int);
+
+UINT GetProfileInt(LPCTSTR, LPCTSTR, INT);
+
+DWORD GetProfileString(LPCTSTR, LPCTSTR, LPCTSTR, LPTSTR, DWORD);
+
+BOOL WriteProfileString(LPCTSTR, LPCTSTR, LPCTSTR);
+
+DWORD GetProfileSection(LPCTSTR, LPTSTR, DWORD);
+
+BOOL WriteProfileSection(LPCTSTR, LPCTSTR);
+
+UINT GetPrivateProfileInt(LPCTSTR, LPCTSTR, INT, LPCTSTR);
+
+DWORD GetPrivateProfileString(LPCTSTR, LPCTSTR, LPCTSTR, LPTSTR, DWORD, LPCTSTR);
+
+BOOL WritePrivateProfileString(LPCTSTR, LPCTSTR, LPCTSTR, LPCTSTR);
+
+DWORD GetPrivateProfileSection(LPCTSTR, LPTSTR, DWORD, LPCTSTR);
+
+BOOL WritePrivateProfileSection(LPCTSTR, LPCTSTR, LPCTSTR);
+
+DWORD GetPrivateProfileSectionNames(LPTSTR, DWORD, LPCTSTR);
+
+BOOL GetPrivateProfileStruct(LPCTSTR, LPCTSTR, LPVOID, UINT, LPCTSTR);
+
+BOOL WritePrivateProfileStruct(LPCTSTR, LPCTSTR, LPVOID, UINT, LPCTSTR);
+
+BOOL SetDllDirectory(LPCTSTR);
+
+DWORD GetDllDirectory(DWORD, LPTSTR);
+
+BOOL CreateDirectoryEx(LPCTSTR, LPCTSTR, LPSECURITY_ATTRIBUTES);
+
+BOOL CreateDirectoryTransacted(LPCTSTR, LPCTSTR, LPSECURITY_ATTRIBUTES, HANDLE);
+
+BOOL RemoveDirectoryTransacted(LPCTSTR, HANDLE);
+
+DWORD GetFullPathNameTransacted(LPCTSTR, DWORD, LPTSTR, LPTSTR *, HANDLE);
+
+BOOL DefineDosDevice(DWORD, LPCTSTR, LPCTSTR);
+
+DWORD QueryDosDevice(LPCTSTR, LPTSTR, DWORD);
+
+HANDLE CreateFileTransacted(LPCTSTR, DWORD, DWORD, LPSECURITY_ATTRIBUTES, DWORD, DWORD, HANDLE, HANDLE, PUSHORT, PVOID);
+
+BOOL SetFileAttributesTransacted(LPCTSTR, DWORD, HANDLE);
+
+BOOL GetFileAttributesTransacted(LPCTSTR, GET_FILEEX_INFO_LEVELS, LPVOID, HANDLE);
+
+DWORD GetCompressedFileSizeTransacted(LPCTSTR, LPDWORD, HANDLE);
+
+BOOL DeleteFileTransacted(LPCTSTR, HANDLE);
+
+BOOL CheckNameLegalDOS8Dot3(LPCTSTR, LPSTR, DWORD, PBOOL, PBOOL);
+
+HANDLE FindFirstFileTransacted(LPCTSTR, FINDEX_INFO_LEVELS, LPVOID, FINDEX_SEARCH_OPS, LPVOID, DWORD, HANDLE);
+
+BOOL CopyFile(LPCTSTR, LPCTSTR, BOOL);
+
+BOOL CopyFileEx(LPCTSTR, LPCTSTR, LPPROGRESS_ROUTINE, LPVOID, LPBOOL, DWORD);
+
+BOOL CopyFileTransacted(LPCTSTR, LPCTSTR, LPPROGRESS_ROUTINE, LPVOID, LPBOOL, DWORD, HANDLE);
+
+BOOL MoveFile(LPCTSTR, LPCTSTR);
+
+BOOL MoveFileEx(LPCTSTR, LPCTSTR, DWORD);
+
+BOOL MoveFileWithProgress(LPCTSTR, LPCTSTR, LPPROGRESS_ROUTINE, LPVOID, DWORD);
+
+BOOL MoveFileTransacted(LPCTSTR, LPCTSTR, LPPROGRESS_ROUTINE, LPVOID, DWORD, HANDLE);
+
+BOOL ReplaceFile(LPCTSTR, LPCTSTR, LPCTSTR, DWORD, LPVOID, LPVOID);
+
+BOOL CreateHardLink(LPCTSTR, LPCTSTR, LPSECURITY_ATTRIBUTES);
+
+BOOL CreateHardLinkTransacted(LPCTSTR, LPCTSTR, LPSECURITY_ATTRIBUTES, HANDLE);
+
+HANDLE CreateNamedPipe(LPCTSTR, DWORD, DWORD, DWORD, DWORD, DWORD, DWORD, LPSECURITY_ATTRIBUTES);
+
+BOOL GetNamedPipeHandleState(HANDLE, LPDWORD, LPDWORD, LPDWORD, LPDWORD, LPTSTR, DWORD);
+
+BOOL CallNamedPipe(LPCTSTR, LPVOID, DWORD, LPVOID, DWORD, LPDWORD, DWORD);
+
+BOOL WaitNamedPipe(LPCTSTR, DWORD);
+
+BOOL GetNamedPipeClientComputerName(HANDLE, LPTSTR, ULONG);
+
+BOOL SetVolumeLabel(LPCTSTR, LPCTSTR);
+
+BOOL ClearEventLog(HANDLE, LPCTSTR);
+
+BOOL BackupEventLog(HANDLE, LPCTSTR);
+
+HANDLE OpenEventLog(LPCTSTR, LPCTSTR);
+
+HANDLE RegisterEventSource(LPCTSTR, LPCTSTR);
+
+HANDLE OpenBackupEventLog(LPCTSTR, LPCTSTR);
+
+BOOL ReadEventLog(HANDLE, DWORD, DWORD, LPVOID, DWORD, DWORD *, DWORD *);
+
+BOOL ReportEvent(HANDLE, WORD, WORD, DWORD, PSID, WORD, DWORD, LPCTSTR *, LPVOID);
+
+BOOL AccessCheckAndAuditAlarm(LPCTSTR, LPVOID, LPTSTR, LPTSTR, PSECURITY_DESCRIPTOR, DWORD, PGENERIC_MAPPING, BOOL, LPDWORD, LPBOOL, LPBOOL);
+
+BOOL AccessCheckByTypeAndAuditAlarm(LPCTSTR, LPVOID, LPCTSTR, LPCTSTR, PSECURITY_DESCRIPTOR, PSID, DWORD, AUDIT_EVENT_TYPE, DWORD, POBJECT_TYPE_LIST, DWORD, PGENERIC_MAPPING, BOOL, LPDWORD, LPBOOL, LPBOOL);
+
+BOOL AccessCheckByTypeResultListAndAuditAlarm(LPCTSTR, LPVOID, LPCTSTR, LPCTSTR, PSECURITY_DESCRIPTOR, PSID, DWORD, AUDIT_EVENT_TYPE, DWORD, POBJECT_TYPE_LIST, DWORD, PGENERIC_MAPPING, BOOL, LPDWORD, LPDWORD, LPBOOL);
+
+BOOL AccessCheckByTypeResultListAndAuditAlarmByHandle(LPCTSTR, LPVOID, HANDLE, LPCTSTR, LPCTSTR, PSECURITY_DESCRIPTOR, PSID, DWORD, AUDIT_EVENT_TYPE, DWORD, POBJECT_TYPE_LIST, DWORD, PGENERIC_MAPPING, BOOL, LPDWORD, LPDWORD, LPBOOL);
+
+BOOL ObjectOpenAuditAlarm(LPCTSTR, LPVOID, LPTSTR, LPTSTR, PSECURITY_DESCRIPTOR, HANDLE, DWORD, DWORD, PPRIVILEGE_SET, BOOL, BOOL, LPBOOL);
+
+BOOL ObjectPrivilegeAuditAlarm(LPCTSTR, LPVOID, HANDLE, DWORD, PPRIVILEGE_SET, BOOL);
+
+BOOL ObjectCloseAuditAlarm(LPCTSTR, LPVOID, BOOL);
+
+BOOL ObjectDeleteAuditAlarm(LPCTSTR, LPVOID, BOOL);
+
+BOOL PrivilegedServiceAuditAlarm(LPCTSTR, LPCTSTR, HANDLE, PPRIVILEGE_SET, BOOL);
+
+BOOL SetFileSecurity(LPCTSTR, SECURITY_INFORMATION, PSECURITY_DESCRIPTOR);
+
+BOOL GetFileSecurity(LPCTSTR, SECURITY_INFORMATION, PSECURITY_DESCRIPTOR, DWORD, LPDWORD);
+
+BOOL IsBadStringPtr(LPCTSTR, UINT_PTR);
+
+BOOL LookupAccountSid(LPCTSTR, PSID, LPTSTR, LPDWORD, LPTSTR, LPDWORD, PSID_NAME_USE);
+
+BOOL LookupAccountName(LPCTSTR, LPCTSTR, PSID, LPDWORD, LPTSTR, LPDWORD, PSID_NAME_USE);
+
+BOOL LookupAccountNameLocal(LPCTSTR, PSID, LPDWORD, LPTSTR, LPDWORD, PSID_NAME_USE);
+
+BOOL LookupAccountSidLocal(PSID, LPTSTR, LPDWORD, LPTSTR, LPDWORD, PSID_NAME_USE);
+
+BOOL LookupPrivilegeValue(LPCTSTR, LPCTSTR, PLUID);
+
+BOOL LookupPrivilegeName(LPCTSTR, PLUID, LPTSTR, LPDWORD);
+
+BOOL LookupPrivilegeDisplayName(LPCTSTR, LPCTSTR, LPTSTR, LPDWORD, LPDWORD);
+
+BOOL BuildCommDCB(LPCTSTR, LPDCB);
+
+BOOL BuildCommDCBAndTimeouts(LPCTSTR, LPDCB, LPCOMMTIMEOUTS);
+
+BOOL CommConfigDialog(LPCTSTR, HWND, LPCOMMCONFIG);
+
+BOOL GetDefaultCommConfig(LPCTSTR, LPCOMMCONFIG, LPDWORD);
+
+BOOL SetDefaultCommConfig(LPCTSTR, LPCOMMCONFIG, DWORD);
+
+BOOL GetComputerName(LPTSTR, LPDWORD);
+
+BOOL DnsHostnameToComputerName(LPCTSTR, LPTSTR, LPDWORD);
+
+BOOL GetUserName(LPTSTR, LPDWORD);
+
+BOOL LogonUser(LPCTSTR, LPCTSTR, LPCTSTR, DWORD, DWORD, PHANDLE);
+
+BOOL LogonUserEx(LPCTSTR, LPCTSTR, LPCTSTR, DWORD, DWORD, PHANDLE, PSID *, PVOID *, LPDWORD, PQUOTA_LIMITS);
+
+HANDLE CreatePrivateNamespace(LPSECURITY_ATTRIBUTES, LPVOID, LPCTSTR);
+
+HANDLE OpenPrivateNamespace(LPVOID, LPCTSTR);
+
+HANDLE CreateBoundaryDescriptor(LPCTSTR, ULONG);
+
+BOOL GetCurrentHwProfile(LPHW_PROFILE_INFO);
+
+BOOL VerifyVersionInfo(LPOSVERSIONINFOEX, DWORD, DWORDLONG);
+
+HANDLE CreateJobObject(LPSECURITY_ATTRIBUTES, LPCTSTR);
+
+HANDLE OpenJobObject(DWORD, BOOL, LPCTSTR);
+
+HANDLE FindFirstVolume(LPTSTR, DWORD);
+
+BOOL FindNextVolume(HANDLE, LPTSTR, DWORD);
+
+HANDLE FindFirstVolumeMountPoint(LPCTSTR, LPTSTR, DWORD);
+
+BOOL FindNextVolumeMountPoint(HANDLE, LPTSTR, DWORD);
+
+BOOL SetVolumeMountPoint(LPCTSTR, LPCTSTR);
+
+BOOL DeleteVolumeMountPoint(LPCTSTR);
+
+BOOL GetVolumeNameForVolumeMountPoint(LPCTSTR, LPTSTR, DWORD);
+
+BOOL GetVolumePathName(LPCTSTR, LPTSTR, DWORD);
+
+BOOL GetVolumePathNamesForVolumeName(LPCTSTR, LPTCH, DWORD, PDWORD);
+
+HANDLE CreateActCtx(PCACTCTX);
+
+BOOL FindActCtxSectionString(DWORD, const GUID *, ULONG, LPCTSTR, PACTCTX_SECTION_KEYED_DATA);
+
+BOOLEAN CreateSymbolicLink(LPCTSTR, LPCTSTR, DWORD);
+
+BOOLEAN CreateSymbolicLinkTransacted(LPCTSTR, LPCTSTR, DWORD, HANDLE);
+
+int AddFontResource(LPCTSTR);
+
+HMETAFILE CopyMetaFile(HMETAFILE, LPCTSTR);
+
+HDC CreateDC(LPCTSTR, LPCTSTR, LPCTSTR, const DEVMODE *);
+
+HFONT CreateFontIndirect(const LOGFONT *);
+
+HFONT CreateFont(int, int, int, int, int, DWORD, DWORD, DWORD, DWORD, DWORD, DWORD, DWORD, DWORD, LPCTSTR);
+
+HDC CreateIC(LPCTSTR, LPCTSTR, LPCTSTR, const DEVMODE *);
+
+HDC CreateMetaFile(LPCTSTR);
+
+BOOL CreateScalableFontResource(DWORD, LPCTSTR, LPCTSTR, LPCTSTR);
+
+int DeviceCapabilities(LPCTSTR, LPCTSTR, WORD, LPTSTR, const DEVMODE *);
+
+int EnumFontFamiliesEx(HDC, LPLOGFONT, FONTENUMPROC, LPARAM, DWORD);
+
+int EnumFontFamilies(HDC, LPCTSTR, FONTENUMPROC, LPARAM);
+
+int EnumFonts(HDC, LPCTSTR, FONTENUMPROC, LPARAM);
+
+BOOL GetCharWidth(HDC, UINT, UINT, LPINT);
+
+BOOL GetCharWidth32(HDC, UINT, UINT, LPINT);
+
+BOOL GetCharWidthFloat(HDC, UINT, UINT, PFLOAT);
+
+BOOL GetCharABCWidths(HDC, UINT, UINT, LPABC);
+
+BOOL GetCharABCWidthsFloat(HDC, UINT, UINT, LPABCFLOAT);
+
+DWORD GetGlyphOutline(HDC, UINT, UINT, LPGLYPHMETRICS, DWORD, LPVOID, const MAT2 *);
+
+HMETAFILE GetMetaFile(LPCTSTR);
+
+UINT GetOutlineTextMetrics(HDC, UINT, LPOUTLINETEXTMETRIC);
+
+BOOL GetTextExtentPoint(HDC, LPCTSTR, int, LPSIZE);
+
+BOOL GetTextExtentPoint32(HDC, LPCTSTR, int, LPSIZE);
+
+BOOL GetTextExtentExPoint(HDC, LPCTSTR, int, int, LPINT, LPINT, LPSIZE);
+
+DWORD GetCharacterPlacement(HDC, LPCTSTR, int, int, LPGCP_RESULTS, DWORD);
+
+DWORD GetGlyphIndices(HDC, LPCTSTR, int, LPWORD, DWORD);
+
+int AddFontResourceEx(LPCTSTR, DWORD, PVOID);
+
+BOOL RemoveFontResourceEx(LPCTSTR, DWORD, PVOID);
+
+HFONT CreateFontIndirectEx(const ENUMLOGFONTEXDV *);
+
+HDC ResetDC(HDC, const DEVMODE *);
+
+BOOL RemoveFontResource(LPCTSTR);
+
+HENHMETAFILE CopyEnhMetaFile(HENHMETAFILE, LPCTSTR);
+
+HDC CreateEnhMetaFile(HDC, LPCTSTR, const RECT *, LPCTSTR);
+
+HENHMETAFILE GetEnhMetaFile(LPCTSTR);
+
+UINT GetEnhMetaFileDescription(HENHMETAFILE, UINT, LPTSTR);
+
+BOOL GetTextMetrics(HDC, LPTEXTMETRIC);
+
+int StartDoc(HDC, const DOCINFO *);
+
+int GetObject(HANDLE, int, LPVOID);
+
+BOOL TextOut(HDC, int, int, LPCTSTR, int);
+
+BOOL ExtTextOut(HDC, int, int, UINT, const RECT *, LPCTSTR, UINT, const INT *);
+
+BOOL PolyTextOut(HDC, const POLYTEXT *, int);
+
+int GetTextFace(HDC, int, LPTSTR);
+
+DWORD GetKerningPairs(HDC, DWORD, LPKERNINGPAIR);
+
+BOOL GetLogColorSpace(HCOLORSPACE, LPLOGCOLORSPACE, DWORD);
+
+HCOLORSPACE CreateColorSpace(LPLOGCOLORSPACE);
+
+BOOL GetICMProfile(HDC, LPDWORD, LPTSTR);
+
+BOOL SetICMProfile(HDC, LPTSTR);
+
+int EnumICMProfiles(HDC, ICMENUMPROC, LPARAM);
+
+BOOL UpdateICMRegKey(DWORD, LPTSTR, LPTSTR, UINT);
+
+HKL LoadKeyboardLayout(LPCTSTR, UINT);
+
+BOOL GetKeyboardLayoutName(LPTSTR);
+
+HDESK CreateDesktop(LPCTSTR, LPCTSTR, DEVMODE*, DWORD, ACCESS_MASK, LPSECURITY_ATTRIBUTES);
+
+HDESK CreateDesktopEx(LPCTSTR, LPCTSTR, DEVMODE*, DWORD, ACCESS_MASK, LPSECURITY_ATTRIBUTES, ULONG, PVOID);
+
+HDESK OpenDesktop(LPCTSTR, DWORD, BOOL, ACCESS_MASK);
+
+BOOL EnumDesktops(HWINSTA, DESKTOPENUMPROC, LPARAM);
+
+HWINSTA CreateWindowStation(LPCTSTR, DWORD, ACCESS_MASK, LPSECURITY_ATTRIBUTES);
+
+HWINSTA OpenWindowStation(LPCTSTR, BOOL, ACCESS_MASK);
+
+BOOL EnumWindowStations(WINSTAENUMPROC, LPARAM);
+
+BOOL GetUserObjectInformation(HANDLE, int, PVOID, DWORD, LPDWORD);
+
+BOOL SetUserObjectInformation(HANDLE, int, PVOID, DWORD);
+
+UINT RegisterWindowMessage(LPCTSTR);
+
+BOOL GetMessage(LPMSG, HWND, UINT, UINT);
+
+LRESULT DispatchMessage(const MSG *);
+
+BOOL PeekMessage(LPMSG, HWND, UINT, UINT, UINT);
+
+LRESULT SendMessage(HWND, UINT, WPARAM, LPARAM);
+
+LRESULT SendMessageTimeout(HWND, UINT, WPARAM, LPARAM, UINT, UINT, PDWORD_PTR);
+
+BOOL SendNotifyMessage(HWND, UINT, WPARAM, LPARAM);
+
+BOOL SendMessageCallback(HWND, UINT, WPARAM, LPARAM, SENDASYNCPROC, ULONG_PTR);
+
+long BroadcastSystemMessageEx(DWORD, LPDWORD, UINT, WPARAM, LPARAM, PBSMINFO);
+
+long BroadcastSystemMessage(DWORD, LPDWORD, UINT, WPARAM, LPARAM);
+
+HDEVNOTIFY RegisterDeviceNotification(HANDLE, LPVOID, DWORD);
+
+BOOL PostMessage(HWND, UINT, WPARAM, LPARAM);
+
+BOOL PostThreadMessage(DWORD, UINT, WPARAM, LPARAM);
+
+BOOL PostAppMessage(DWORD, UINT, WPARAM, LPARAM);
+
+LRESULT DefWindowProc(HWND, UINT, WPARAM, LPARAM);
+
+LRESULT CallWindowProc(WNDPROC, HWND, UINT, WPARAM, LPARAM);
+
+ATOM RegisterClass(const WNDCLASS *);
+
+BOOL UnregisterClass(LPCTSTR, HINSTANCE);
+
+BOOL GetClassInfo(HINSTANCE, LPCTSTR, LPWNDCLASS);
+
+ATOM RegisterClassEx(const WNDCLASSEX *);
+
+BOOL GetClassInfoEx(HINSTANCE, LPCTSTR, LPWNDCLASSEX);
+
+HWND CreateWindowEx(DWORD, LPCTSTR, LPCTSTR, DWORD, int, int, int, int, HWND, HMENU, HINSTANCE, LPVOID);
+
+HWND CreateWindow(LPCTSTR, LPCTSTR, DWORD, int, int, int, int, HWND, HMENU, HINSTANCE, LPVOID);
+
+HWND CreateDialogParam(HINSTANCE, LPCTSTR, HWND, DLGPROC, LPARAM);
+
+HWND CreateDialogIndirectParam(HINSTANCE, LPCDLGTEMPLATE, HWND, DLGPROC, LPARAM);
+
+HWND CreateDialog(HINSTANCE, LPCTSTR, HWND, DLGPROC);
+
+HWND CreateDialogIndirect(HINSTANCE, LPCDLGTEMPLATE, HWND, DLGPROC);
+
+INT_PTR DialogBoxParam(HINSTANCE, LPCTSTR, HWND, DLGPROC, LPARAM);
+
+INT_PTR DialogBoxIndirectParam(HINSTANCE, LPCDLGTEMPLATE, HWND, DLGPROC, LPARAM);
+
+INT_PTR DialogBox(HINSTANCE, LPCTSTR, HWND, DLGPROC);
+
+INT_PTR DialogBoxIndirect(HINSTANCE, LPCDLGTEMPLATE, HWND, DLGPROC);
+
+BOOL SetDlgItemText(HWND, int, LPCTSTR);
+
+UINT GetDlgItemText(HWND, int, LPTSTR, int);
+
+LRESULT SendDlgItemMessage(HWND, int, UINT, WPARAM, LPARAM);
+
+LRESULT DefDlgProc(HWND, UINT, WPARAM, LPARAM);
+
+BOOL CallMsgFilter(LPMSG, int);
+
+UINT RegisterClipboardFormat(LPCTSTR);
+
+int GetClipboardFormatName(UINT, LPTSTR, int);
+
+BOOL CharToOem(LPCTSTR, LPSTR);
+
+BOOL OemToChar(LPCSTR, LPTSTR);
+
+BOOL CharToOemBuff(LPCTSTR, LPSTR, DWORD);
+
+BOOL OemToCharBuff(LPCSTR, LPTSTR, DWORD);
+
+LPTSTR CharUpper(LPTSTR);
+
+DWORD CharUpperBuff(LPTSTR, DWORD);
+
+LPTSTR CharLower(LPTSTR);
+
+DWORD CharLowerBuff(LPTSTR, DWORD);
+
+LPTSTR CharNext(LPCTSTR);
+
+LPTSTR CharPrev(LPCTSTR, LPCTSTR);
+
+BOOL IsCharAlpha(CHAR);
+
+BOOL IsCharAlphaNumeric(CHAR);
+
+BOOL IsCharUpper(CHAR);
+
+BOOL IsCharLower(CHAR);
+
+int GetKeyNameText(LONG, LPTSTR, int);
+
+SHORT VkKeyScan(CHAR);
+
+SHORT VkKeyScanEx(CHAR, HKL);
+
+UINT MapVirtualKey(UINT, UINT);
+
+UINT MapVirtualKeyEx(UINT, UINT, HKL);
+
+HACCEL LoadAccelerators(HINSTANCE, LPCTSTR);
+
+HACCEL CreateAcceleratorTable(LPACCEL, int);
+
+int CopyAcceleratorTable(HACCEL, LPACCEL, int);
+
+int TranslateAccelerator(HWND, HACCEL, LPMSG);
+
+HMENU LoadMenu(HINSTANCE, LPCTSTR);
+
+HMENU LoadMenuIndirect(const MENUTEMPLATE *);
+
+BOOL ChangeMenu(HMENU, UINT, LPCTSTR, UINT, UINT);
+
+int GetMenuString(HMENU, UINT, LPTSTR, int, UINT);
+
+BOOL InsertMenu(HMENU, UINT, UINT, UINT_PTR, LPCTSTR);
+
+BOOL AppendMenu(HMENU, UINT, UINT_PTR, LPCTSTR);
+
+BOOL ModifyMenu(HMENU, UINT, UINT, UINT_PTR, LPCTSTR);
+
+BOOL InsertMenuItem(HMENU, UINT, BOOL, LPCMENUITEMINFO);
+
+BOOL GetMenuItemInfo(HMENU, UINT, BOOL, LPMENUITEMINFO);
+
+BOOL SetMenuItemInfo(HMENU, UINT, BOOL, LPCMENUITEMINFO);
+
+int DrawText(HDC, LPCTSTR, int, LPRECT, UINT);
+
+int DrawTextEx(HDC, LPTSTR, int, LPRECT, UINT, LPDRAWTEXTPARAMS);
+
+BOOL GrayString(HDC, HBRUSH, GRAYSTRINGPROC, LPARAM, int, int, int, int, int);
+
+BOOL DrawState(HDC, HBRUSH, DRAWSTATEPROC, LPARAM, WPARAM, int, int, int, int, UINT);
+
+LONG TabbedTextOut(HDC, int, int, LPCTSTR, int, int, const INT *, int);
+
+DWORD GetTabbedTextExtent(HDC, LPCTSTR, int, int, const INT *);
+
+BOOL SetProp(HWND, LPCTSTR, HANDLE);
+
+HANDLE GetProp(HWND, LPCTSTR);
+
+HANDLE RemoveProp(HWND, LPCTSTR);
+
+int EnumPropsEx(HWND, PROPENUMPROCEX, LPARAM);
+
+int EnumProps(HWND, PROPENUMPROC);
+
+BOOL SetWindowText(HWND, LPCTSTR);
+
+int GetWindowText(HWND, LPTSTR, int);
+
+int GetWindowTextLength(HWND);
+
+int MessageBox(HWND, LPCTSTR, LPCTSTR, UINT);
+
+int MessageBoxEx(HWND, LPCTSTR, LPCTSTR, UINT, WORD);
+
+int MessageBoxIndirect(const MSGBOXPARAMS *);
+
+LONG GetWindowLong(HWND, int);
+
+LONG SetWindowLong(HWND, int, LONG);
+
+LONG_PTR GetWindowLongPtr(HWND, int);
+
+LONG_PTR SetWindowLongPtr(HWND, int, LONG_PTR);
+
+DWORD GetClassLong(HWND, int);
+
+DWORD SetClassLong(HWND, int, LONG);
+
+ULONG_PTR GetClassLongPtr(HWND, int);
+
+ULONG_PTR SetClassLongPtr(HWND, int, LONG_PTR);
+
+HWND FindWindow(LPCTSTR, LPCTSTR);
+
+HWND FindWindowEx(HWND, HWND, LPCTSTR, LPCTSTR);
+
+int GetClassName(HWND, LPTSTR, int);
+
+HHOOK SetWindowsHook(int, HOOKPROC);
+
+HHOOK SetWindowsHookEx(int, HOOKPROC, HINSTANCE, DWORD);
+
+HBITMAP LoadBitmap(HINSTANCE, LPCTSTR);
+
+HCURSOR LoadCursor(HINSTANCE, LPCTSTR);
+
+HCURSOR LoadCursorFromFile(LPCTSTR);
+
+HICON LoadIcon(HINSTANCE, LPCTSTR);
+
+UINT PrivateExtractIcons(LPCTSTR, int, int, int, HICON *, UINT *, UINT, UINT);
+
+HANDLE LoadImage(HINSTANCE, LPCTSTR, UINT, int, int, UINT);
+
+BOOL GetIconInfoEx(HICON, PICONINFOEX);
+
+BOOL IsDialogMessage(HWND, LPMSG);
+
+int DlgDirList(HWND, LPTSTR, int, int, UINT);
+
+BOOL DlgDirSelectEx(HWND, LPTSTR, int, int);
+
+int DlgDirListComboBox(HWND, LPTSTR, int, int, UINT);
+
+BOOL DlgDirSelectComboBoxEx(HWND, LPTSTR, int, int);
+
+LRESULT DefFrameProc(HWND, HWND, UINT, WPARAM, LPARAM);
+
+LRESULT DefMDIChildProc(HWND, UINT, WPARAM, LPARAM);
+
+HWND CreateMDIWindow(LPCTSTR, LPCTSTR, DWORD, int, int, int, int, HWND, HINSTANCE, LPARAM);
+
+BOOL WinHelp(HWND, LPCTSTR, UINT, ULONG_PTR);
+
+LONG ChangeDisplaySettings(DEVMODE*, DWORD);
+
+LONG ChangeDisplaySettingsEx(LPCTSTR, DEVMODE*, HWND, DWORD, LPVOID);
+
+BOOL EnumDisplaySettings(LPCTSTR, DWORD, DEVMODE*);
+
+BOOL EnumDisplaySettingsEx(LPCTSTR, DWORD, DEVMODE*, DWORD);
+
+BOOL EnumDisplayDevices(LPCTSTR, DWORD, PDISPLAY_DEVICE, DWORD);
+
+BOOL SystemParametersInfo(UINT, UINT, PVOID, UINT);
+
+BOOL GetMonitorInfo(HMONITOR, LPMONITORINFO);
+
+UINT GetWindowModuleFileName(HWND, LPTSTR, UINT);
+
+UINT RealGetWindowClass(HWND, LPTSTR, UINT);
+
+BOOL GetAltTabInfo(HWND, int, PALTTABINFO, LPTSTR, UINT);
+
+UINT GetRawInputDeviceInfo(HANDLE, UINT, LPVOID, PUINT);
+
+int GetDateFormat(LCID, DWORD, const SYSTEMTIME*, LPCTSTR, LPTSTR, int);
+
+int GetTimeFormat(LCID, DWORD, const SYSTEMTIME*, LPCTSTR, LPTSTR, int);
+
+BOOL GetCPInfoEx(UINT, DWORD, LPCPINFOEX);
+
+int CompareString(LCID, DWORD, PCNZTCH, int, PCNZTCH, int);
+
+int GetLocaleInfo(LCID, LCTYPE, LPTSTR, int);
+
+BOOL SetLocaleInfo(LCID, LCTYPE, LPCTSTR);
+
+int GetCalendarInfo(LCID, CALID, CALTYPE, LPTSTR, int, LPDWORD);
+
+BOOL SetCalendarInfo(LCID, CALID, CALTYPE, LPCTSTR);
+
+int GetNumberFormat(LCID, DWORD, LPCTSTR, const NUMBERFMT *, LPTSTR, int);
+
+int GetCurrencyFormat(LCID, DWORD, LPCTSTR, const CURRENCYFMT *, LPTSTR, int);
+
+BOOL EnumCalendarInfo(CALINFO_ENUMPROC, LCID, CALID, CALTYPE);
+
+BOOL EnumCalendarInfoEx(CALINFO_ENUMPROCEX, LCID, CALID, CALTYPE);
+
+BOOL EnumTimeFormats(TIMEFMT_ENUMPROC, LCID, DWORD);
+
+BOOL EnumDateFormats(DATEFMT_ENUMPROC, LCID, DWORD);
+
+BOOL EnumDateFormatsEx(DATEFMT_ENUMPROCEX, LCID, DWORD);
+
+int GetGeoInfo(GEOID, GEOTYPE, LPTSTR, int, LANGID);
+
+BOOL GetStringTypeEx(LCID, DWORD, LPCTSTR, int, LPWORD);
+
+int FoldString(DWORD, LPCTSTR, int, LPTSTR, int);
+
+BOOL EnumSystemLocales(LOCALE_ENUMPROC, DWORD);
+
+BOOL EnumSystemLanguageGroups(LANGUAGEGROUP_ENUMPROC, DWORD, LONG_PTR);
+
+BOOL EnumLanguageGroupLocales(LANGGROUPLOCALE_ENUMPROC, LGRPID, DWORD, LONG_PTR);
+
+BOOL EnumUILanguages(UILANGUAGE_ENUMPROC, DWORD, LONG_PTR);
+
+BOOL EnumSystemCodePages(CODEPAGE_ENUMPROC, DWORD);
+
+BOOL ReadConsoleInput(HANDLE, PINPUT_RECORD, DWORD, LPDWORD);
+
+BOOL PeekConsoleInput(HANDLE, PINPUT_RECORD, DWORD, LPDWORD);
+
+BOOL ReadConsole(HANDLE, LPVOID, DWORD, LPDWORD, PCONSOLE_READCONSOLE_CONTROL);
+
+BOOL WriteConsole(HANDLE, const void*, DWORD, LPDWORD, LPVOID);
+
+BOOL FillConsoleOutputCharacter(HANDLE, CHAR, DWORD, COORD, LPDWORD);
+
+BOOL WriteConsoleOutputCharacter(HANDLE, LPCTSTR, DWORD, COORD, LPDWORD);
+
+BOOL ReadConsoleOutputCharacter(HANDLE, LPTSTR, DWORD, COORD, LPDWORD);
+
+BOOL WriteConsoleInput(HANDLE, const INPUT_RECORD*, DWORD, LPDWORD);
+
+BOOL ScrollConsoleScreenBuffer(HANDLE, const SMALL_RECT*, const SMALL_RECT*, COORD, const CHAR_INFO*);
+
+BOOL WriteConsoleOutput(HANDLE, const CHAR_INFO*, COORD, COORD, PSMALL_RECT);
+
+BOOL ReadConsoleOutput(HANDLE, PCHAR_INFO, COORD, COORD, PSMALL_RECT);
+
+DWORD GetConsoleTitle(LPTSTR, DWORD);
+
+DWORD GetConsoleOriginalTitle(LPTSTR, DWORD);
+
+BOOL SetConsoleTitle(LPCTSTR);
+
+BOOL AddConsoleAlias(LPTSTR, LPTSTR, LPTSTR);
+
+DWORD GetConsoleAlias(LPTSTR, LPTSTR, DWORD, LPTSTR);
+
+DWORD GetConsoleAliasesLength(LPTSTR);
+
+DWORD GetConsoleAliasExesLength();
+
+DWORD GetConsoleAliases(LPTSTR, DWORD, LPTSTR);
+
+DWORD GetConsoleAliasExes(LPTSTR, DWORD);
+
+void ExpungeConsoleCommandHistory(LPTSTR);
+
+BOOL SetConsoleNumberOfCommands(DWORD, LPTSTR);
+
+DWORD GetConsoleCommandHistoryLength(LPTSTR);
+
+DWORD GetConsoleCommandHistory(LPTSTR, DWORD, LPTSTR);
+
+DWORD VerFindFile(DWORD, LPTSTR, LPTSTR, LPTSTR, LPTSTR, PUINT, LPTSTR, PUINT);
+
+DWORD VerInstallFile(DWORD, LPTSTR, LPTSTR, LPTSTR, LPTSTR, LPTSTR, LPTSTR, PUINT);
+
+DWORD GetFileVersionInfoSize(LPCTSTR, LPDWORD);
+
+BOOL GetFileVersionInfo(LPCTSTR, DWORD, DWORD, LPVOID);
+
+DWORD GetFileVersionInfoSizeEx(DWORD, LPCTSTR, LPDWORD);
+
+BOOL GetFileVersionInfoEx(DWORD, LPCTSTR, DWORD, DWORD, LPVOID);
+
+DWORD VerLanguageName(DWORD, LPTSTR, DWORD);
+
+BOOL VerQueryValue(LPCVOID, LPCTSTR, LPVOID *, PUINT);
+
+LSTATUS RegConnectRegistry(LPCTSTR, HKEY, PHKEY);
+
+LSTATUS RegConnectRegistryEx(LPCTSTR, HKEY, ULONG, PHKEY);
+
+LSTATUS RegCreateKey(HKEY, LPCTSTR, PHKEY);
+
+LSTATUS RegCreateKeyEx(HKEY, LPCTSTR, DWORD, LPTSTR, DWORD, REGSAM, const LPSECURITY_ATTRIBUTES, PHKEY, LPDWORD);
+
+LSTATUS RegCreateKeyTransacted(HKEY, LPCTSTR, DWORD, LPTSTR, DWORD, REGSAM, const LPSECURITY_ATTRIBUTES, PHKEY, LPDWORD, HANDLE, PVOID);
+
+LSTATUS RegDeleteKey(HKEY, LPCTSTR);
+
+LSTATUS RegDeleteKeyEx(HKEY, LPCTSTR, REGSAM, DWORD);
+
+LSTATUS RegDeleteKeyTransacted(HKEY, LPCTSTR, REGSAM, DWORD, HANDLE, PVOID);
+
+LSTATUS RegDeleteValue(HKEY, LPCTSTR);
+
+LSTATUS RegEnumKey(HKEY, DWORD, LPTSTR, DWORD);
+
+LSTATUS RegEnumKeyEx(HKEY, DWORD, LPTSTR, LPDWORD, LPDWORD, LPTSTR, LPDWORD, PFILETIME);
+
+LSTATUS RegEnumValue(HKEY, DWORD, LPTSTR, LPDWORD, LPDWORD, LPDWORD, LPBYTE, LPDWORD);
+
+LSTATUS RegLoadKey(HKEY, LPCTSTR, LPCTSTR);
+
+LSTATUS RegOpenKey(HKEY, LPCTSTR, PHKEY);
+
+LSTATUS RegOpenKeyEx(HKEY, LPCTSTR, DWORD, REGSAM, PHKEY);
+
+LSTATUS RegOpenKeyTransacted(HKEY, LPCTSTR, DWORD, REGSAM, PHKEY, HANDLE, PVOID);
+
+LSTATUS RegQueryInfoKey(HKEY, LPTSTR, LPDWORD, LPDWORD, LPDWORD, LPDWORD, LPDWORD, LPDWORD, LPDWORD, LPDWORD, LPDWORD, PFILETIME);
+
+LSTATUS RegQueryValue(HKEY, LPCTSTR, LPTSTR, PLONG);
+
+LSTATUS RegQueryMultipleValues(HKEY, PVALENT, DWORD, LPTSTR, LPDWORD);
+
+LSTATUS RegQueryValueEx(HKEY, LPCTSTR, LPDWORD, LPDWORD, LPBYTE, LPDWORD);
+
+LSTATUS RegReplaceKey(HKEY, LPCTSTR, LPCTSTR, LPCTSTR);
+
+LSTATUS RegRestoreKey(HKEY, LPCTSTR, DWORD);
+
+LSTATUS RegSaveKey(HKEY, LPCTSTR, const LPSECURITY_ATTRIBUTES);
+
+LSTATUS RegSetValue(HKEY, LPCTSTR, DWORD, LPCTSTR, DWORD);
+
+LSTATUS RegSetValueEx(HKEY, LPCTSTR, DWORD, DWORD, const BYTE*, DWORD);
+
+LSTATUS RegUnLoadKey(HKEY, LPCTSTR);
+
+LSTATUS RegDeleteKeyValue(HKEY, LPCTSTR, LPCTSTR);
+
+LSTATUS RegSetKeyValue(HKEY, LPCTSTR, LPCTSTR, DWORD, LPCVOID, DWORD);
+
+LSTATUS RegDeleteTree(HKEY, LPCTSTR);
+
+LSTATUS RegCopyTree(HKEY, LPCTSTR, HKEY);
+
+LSTATUS RegGetValue(HKEY, LPCTSTR, LPCTSTR, DWORD, LPDWORD, PVOID, LPDWORD);
+
+LSTATUS RegLoadMUIString(HKEY, LPCTSTR, LPTSTR, DWORD, LPDWORD, DWORD, LPCTSTR);
+
+LSTATUS RegLoadAppKey(LPCTSTR, PHKEY, REGSAM, DWORD, DWORD);
+
+BOOL InitiateSystemShutdown(LPTSTR, LPTSTR, DWORD, BOOL, BOOL);
+
+BOOL AbortSystemShutdown(LPTSTR);
+
+BOOL InitiateSystemShutdownEx(LPTSTR, LPTSTR, DWORD, BOOL, BOOL, DWORD);
+
+DWORD InitiateShutdown(LPTSTR, LPTSTR, DWORD, DWORD, DWORD);
+
+LSTATUS RegSaveKeyEx(HKEY, LPCTSTR, const LPSECURITY_ATTRIBUTES, DWORD);
+
+DWORD MultinetGetConnectionPerformance(LPNETRESOURCE, LPNETCONNECTINFOSTRUCT);
+
+BOOL ChangeServiceConfig(SC_HANDLE, DWORD, DWORD, DWORD, LPCTSTR, LPCTSTR, LPDWORD, LPCTSTR, LPCTSTR, LPCTSTR, LPCTSTR);
+
+BOOL ChangeServiceConfig2(SC_HANDLE, DWORD, LPVOID);
+
+SC_HANDLE CreateService(SC_HANDLE, LPCTSTR, LPCTSTR, DWORD, DWORD, DWORD, DWORD, LPCTSTR, LPCTSTR, LPDWORD, LPCTSTR, LPCTSTR, LPCTSTR);
+
+BOOL EnumDependentServices(SC_HANDLE, DWORD, LPENUM_SERVICE_STATUS, DWORD, LPDWORD, LPDWORD);
+
+BOOL EnumServicesStatus(SC_HANDLE, DWORD, DWORD, LPENUM_SERVICE_STATUS, DWORD, LPDWORD, LPDWORD, LPDWORD);
+
+BOOL EnumServicesStatusEx(SC_HANDLE, SC_ENUM_TYPE, DWORD, DWORD, LPBYTE, DWORD, LPDWORD, LPDWORD, LPDWORD, LPCTSTR);
+
+BOOL GetServiceKeyName(SC_HANDLE, LPCTSTR, LPTSTR, LPDWORD);
+
+BOOL GetServiceDisplayName(SC_HANDLE, LPCTSTR, LPTSTR, LPDWORD);
+
+SC_HANDLE OpenSCManager(LPCTSTR, LPCTSTR, DWORD);
+
+SC_HANDLE OpenService(SC_HANDLE, LPCTSTR, DWORD);
+
+BOOL QueryServiceConfig(SC_HANDLE, LPQUERY_SERVICE_CONFIG, DWORD, LPDWORD);
+
+BOOL QueryServiceConfig2(SC_HANDLE, DWORD, LPBYTE, DWORD, LPDWORD);
+
+BOOL QueryServiceLockStatus(SC_HANDLE, LPQUERY_SERVICE_LOCK_STATUS, DWORD, LPDWORD);
+
+SERVICE_STATUS_HANDLE RegisterServiceCtrlHandler(LPCTSTR, LPHANDLER_FUNCTION);
+
+SERVICE_STATUS_HANDLE RegisterServiceCtrlHandlerEx(LPCTSTR, LPHANDLER_FUNCTION_EX, LPVOID);
+
+BOOL StartServiceCtrlDispatcher(const SERVICE_TABLE_ENTRY *);
+
+BOOL StartService(SC_HANDLE, DWORD, LPCTSTR *);
+
+DWORD NotifyServiceStatusChange(SC_HANDLE, DWORD, PSERVICE_NOTIFY);
+
+BOOL ControlServiceEx(SC_HANDLE, DWORD, DWORD, PVOID);
+
+HKL ImmInstallIME(LPCTSTR, LPCTSTR);
+
+UINT ImmGetDescription(HKL, LPTSTR, UINT);
+
+UINT ImmGetIMEFileName(HKL, LPTSTR, UINT);
+
+LONG ImmGetCompositionString(HIMC, DWORD, LPVOID, DWORD);
+
+BOOL ImmSetCompositionString(HIMC, DWORD, LPVOID, DWORD, LPVOID, DWORD);
+
+DWORD ImmGetCandidateListCount(HIMC, LPDWORD);
+
+DWORD ImmGetCandidateList(HIMC, DWORD, LPCANDIDATELIST, DWORD);
+
+DWORD ImmGetGuideLine(HIMC, DWORD, LPTSTR, DWORD);
+
+BOOL ImmGetCompositionFont(HIMC, LPLOGFONT);
+
+BOOL ImmSetCompositionFont(HIMC, LPLOGFONT);
+
+BOOL ImmConfigureIME(HKL, HWND, DWORD, LPVOID);
+
+LRESULT ImmEscape(HKL, HIMC, UINT, LPVOID);
+
+DWORD ImmGetConversionList(HKL, HIMC, LPCTSTR, LPCANDIDATELIST, DWORD, UINT);
+
+BOOL ImmIsUIMessage(HWND, UINT, WPARAM, LPARAM);
+
+BOOL ImmRegisterWord(HKL, LPCTSTR, DWORD, LPCTSTR);
+
+BOOL ImmUnregisterWord(HKL, LPCTSTR, DWORD, LPCTSTR);
+
+UINT ImmGetRegisterWordStyle(HKL, UINT, LPSTYLEBUF);
+
+UINT ImmEnumRegisterWord(HKL, REGISTERWORDENUMPROC, LPCTSTR, DWORD, LPCTSTR, LPVOID);
+
+DWORD ImmGetImeMenuItems(HIMC, DWORD, DWORD, LPIMEMENUITEMINFO, LPIMEMENUITEMINFO, DWORD);
new file mode 100644
--- /dev/null
+++ b/config/windows-h-wrapper.template.h
@@ -0,0 +1,55 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_windows_h
+#define mozilla_windows_h
+
+// Include the "real" windows.h header. On clang/gcc, this can be done with the
+// `include_next` feature, however MSVC requires a direct include path.
+//
+// Also turn off deprecation warnings, as we may be wrapping deprecated fns.
+
+#if defined(__GNUC__) || defined(__clang__)
+#  pragma GCC system_header
+#  include_next <windows.h>
+
+#  pragma GCC diagnostic push
+#  pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+#else
+#  include <${header_path}>
+
+#  pragma warning(push)
+#  pragma warning(disable: 4996 4995)
+#endif // defined(__GNUC__) || defined(__clang__)
+
+// Check if the header should be disabled
+#if defined(MOZ_DISABLE_WINDOWS_WRAPPER)
+#define MOZ_WINDOWS_WRAPPER_DISABLED_REASON "explicitly disabled"
+
+#elif !defined(__cplusplus)
+#define MOZ_WINDOWS_WRAPPER_DISABLED_REASON "non-C++ source file"
+
+#elif !defined(__GNUC__) && !defined(__clang__) && !defined(_DLL)
+#define MOZ_WINDOWS_WRAPPER_DISABLED_REASON "non-dynamic RTL"
+
+#else
+// We're allowed to wrap in the current context. Define `MOZ_WRAPPED_WINDOWS_H`
+// to note that fact, and perform the wrapping.
+#define MOZ_WRAPPED_WINDOWS_H
+extern "C++" {
+
+${decls}
+
+} // extern "C++"
+#endif // enabled
+
+#if defined(__GNUC__) || defined(__clang__)
+#  pragma GCC diagnostic pop
+#else
+#  pragma warning(pop)
+#endif // defined(__GNUC__) || defined(__clang__)
+
+#endif // !defined(mozilla_windows_h)
--- a/devtools/client/debugger/new/README.mozilla
+++ b/devtools/client/debugger/new/README.mozilla
@@ -1,13 +1,13 @@
 This is the debugger.html project output.
 See https://github.com/devtools-html/debugger.html
 
-Version 104
+Version 105
 
-Comparison: https://github.com/devtools-html/debugger.html/compare/release-103...release-104
+Comparison: https://github.com/devtools-html/debugger.html/compare/release-104...release-105
 
 Packages:
 - babel-plugin-transform-es2015-modules-commonjs @6.26.2
 - babel-preset-react @6.24.1
 - react @16.4.1
 - react-dom @16.4.1
 - webpack @3.12.0
--- a/devtools/client/debugger/new/dist/debugger.css
+++ b/devtools/client/debugger/new/dist/debugger.css
@@ -272,16 +272,32 @@ button.jump-definition {
   vertical-align: middle;
 }
 
 .jump-definition:hover {
   background-color: var(--theme-highlight-blue);
 }
 
 /******************************************************************************/
+/* Invoke getter button */
+
+button.invoke-getter {
+  mask: url("resource://devtools/client/shared/components/reps/images/input.svg") no-repeat;
+  display: inline-block;
+  background-color: var(--comment-node-color);
+  height: 12px;
+  vertical-align:bottom;
+  border:none;
+}
+
+.invoke-getter:hover {
+  background-color: var(--theme-highlight-blue);
+}
+
+/******************************************************************************/
 /* "more…" ellipsis */
 .more-ellipsis {
   color: var(--comment-node-color);
 }
 /* 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/. */
 
@@ -332,44 +348,49 @@ button.jump-definition {
 .tree-node[data-expandable="false"][aria-level="1"] {
   padding-inline-start: 15px
 }
 
 .tree .tree-node[data-expandable="true"] {
   cursor: default;
 }
 
-.tree-node img.arrow {
-  mask: url("resource://devtools/client/debugger/new/images/arrow.svg") no-repeat;
-  mask-size: 100%;
+.tree-node button.arrow {
+  background:url("resource://devtools/client/debugger/new/images/arrow.svg") no-repeat;
+  background-size:contain;
+  background-position:center center;
   width: 9px;
   height: 9px;
+  border:0;
+  padding:0;
   margin-inline-start: 1px;
   margin-inline-end: 4px;
-  background-color: var(--theme-splitter-color, #9B9B9B);
   transform: rotate(-90deg);
+  transform-origin: center center;
   transition: transform 0.125s ease;
   align-self: center;
-}
-
-html[dir="rtl"] .tree-node img.arrow {
+  -moz-context-properties: fill;
+  fill: var(--theme-splitter-color, #9B9B9B);
+}
+
+html[dir="rtl"] .tree-node button.arrow {
   transform: rotate(90deg);
 }
 
-.tree-node img.arrow.expanded.expanded {
+.tree-node button.arrow.expanded.expanded {
   transform: rotate(0deg);
  }
 
 .tree .tree-node.focused {
   color: white;
   background-color: var(--theme-selection-background, #0a84ff);
 }
 
-.tree-node.focused img.arrow {
-  background-color: currentColor;
+.tree-node.focused button.arrow {
+  fill: currentColor;
 }
 /* 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/. */
 
 .tree.object-inspector .node.object-node {
   display: inline-block;
 }
@@ -399,17 +420,17 @@ html[dir="rtl"] .tree-node img.arrow {
   content: "\2632   ";
   font-size: 1.1em;
 }
 
 .object-inspector .object-delimiter {
   color: var(--theme-comment);
 }
 
-.object-inspector .tree-node img.arrow {
+.object-inspector .tree-node .arrow {
   display: inline-block;
   vertical-align: middle;
 }
 
 /* Focused styles */
 .tree.object-inspector .tree-node.focused * {
   color: inherit;
 }
@@ -2660,45 +2681,33 @@ menuseparator {
   color: var(--theme-comment);
 }
 /* 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/>. */
 
 .call-site {
   position: relative;
-  border-bottom: 2px solid lightgrey;
+  display: inline;
   cursor: pointer;
 }
 
 .call-site::before {
   content: "";
   mask: url("resource://devtools/client/debugger/new/images/column-marker.svg") no-repeat 100% 100%;
   mask-size: contain;
   display: inline-block;
   background-color: var(--blue-55);
   opacity: 0.5;
   width: 9px;
   height: 12px;
 }
 
-.call-site-bp {
-  position: relative;
-  border-bottom: 2px solid #aed3ef;
-  cursor: pointer;
-}
-
-.call-site-bp::before {
-  content: "";
-  mask: url("resource://devtools/client/debugger/new/images/column-marker.svg") no-repeat 100% 100%;
-  mask-size: contain;
-  display: inline-block;
-  background-color: var(--blue-55);
-  width: 9px;
-  height: 12px;
+.call-site.active::before {
+  opacity: 1;
 }
 
 .theme-dark .call-site {
   border-bottom: none;
 }
 
 .theme-dark .call-site-bp {
   border-bottom: none;
@@ -2945,17 +2954,18 @@ html[dir="rtl"] .editor-mount {
 .editor-wrapper .CodeMirror-line {
   font-size: 11px;
 }
 
 .theme-dark .editor-wrapper .CodeMirror-line .cm-comment {
   color: var(--theme-comment);
 }
 
-.debug-expression {
+.debug-expression,
+.new-debug-line .call-site {
   background-color: var(--debug-expression-background);
 }
 
 debug-expression-error {
   background-color: var(--debug-expression-error-background);
 }
 
 .new-debug-line .CodeMirror-line {
@@ -3607,17 +3617,17 @@ html[dir="rtl"] .breakpoints-list .break
 }
 
 .annotation-logo {
   display: inline-block;
   width: 12px;
   margin-inline-start: 4px;
 }
 
-:root.theme-dark .annotation-logo svg path {
+:root.theme-dark .annotation-logo:not(.angular) svg path {
   fill: var(--theme-highlight-blue);
 }
 /* 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/>. */
 
 .event-listeners {
   list-style: none;
--- a/devtools/client/debugger/new/dist/parser-worker.js
+++ b/devtools/client/debugger/new/dist/parser-worker.js
@@ -1010,16 +1010,17 @@ exports.getObjectExpressionValue = getOb
 exports.getCode = getCode;
 exports.getVariableNames = getVariableNames;
 exports.getComments = getComments;
 exports.getSpecifiers = getSpecifiers;
 exports.isVariable = isVariable;
 exports.isComputedExpression = isComputedExpression;
 exports.getMemberExpression = getMemberExpression;
 exports.getVariables = getVariables;
+exports.getPatternIdentifiers = getPatternIdentifiers;
 exports.isTopLevel = isTopLevel;
 
 var _types = __webpack_require__(2268);
 
 var t = _interopRequireWildcard(_types);
 
 var _generator = __webpack_require__(2365);
 
@@ -1159,32 +1160,63 @@ function getVariables(dec) {
     return [];
   }
 
   if (t.isArrayPattern(dec.id)) {
     if (!dec.id.elements) {
       return [];
     }
 
-    // NOTE: it's possible that an element is empty
+    // NOTE: it's possible that an element is empty or has several variables
     // e.g. const [, a] = arr
+    // e.g. const [{a, b }] = 2
     return dec.id.elements.filter(element => element).map(element => {
       return {
-        name: t.isAssignmentPattern(element) ? element.left.name : element.name || element.argument.name,
+        name: t.isAssignmentPattern(element) ? element.left.name : element.name || element.argument && element.argument.name,
         location: element.loc
       };
-    });
+    }).filter(({ name }) => name);
   }
 
   return [{
     name: dec.id.name,
     location: dec.loc
   }];
 }
 
+function getPatternIdentifiers(pattern) {
+  let items = [];
+  if (t.isObjectPattern(pattern)) {
+    items = pattern.properties.map(({ value }) => value);
+  }
+
+  if (t.isArrayPattern(pattern)) {
+    items = pattern.elements;
+  }
+
+  return getIdentifiers(items);
+}
+
+function getIdentifiers(items) {
+  let ids = [];
+  items.forEach(function (item) {
+    if (t.isObjectPattern(item) || t.isArrayPattern(item)) {
+      ids = ids.concat(getPatternIdentifiers(item));
+    } else if (t.isIdentifier(item)) {
+      const { start, end } = item.loc;
+      ids.push({
+        name: item.name,
+        expression: item.name,
+        location: { start, end }
+      });
+    }
+  });
+  return ids;
+}
+
 // Top Level checks the number of "body" nodes in the ancestor chain
 // if the node is top-level, then it shoul only have one body.
 function isTopLevel(ancestors) {
   return ancestors.filter(ancestor => ancestor.key == "body").length == 1;
 }
 
 /***/ }),
 
@@ -1445,17 +1477,17 @@ function extractSymbol(path, symbols) {
     const { start, end } = path.node.loc;
     return symbols.identifiers.push({
       name: path.node.value,
       expression: (0, _helpers.getObjectExpressionValue)(path.parent),
       location: { start, end }
     });
   }
 
-  if (t.isIdentifier(path) && !t.isGenericTypeAnnotation(path.parent)) {
+  if (t.isIdentifier(path) && !t.isGenericTypeAnnotation(path.parent) && !t.isObjectProperty(path.parent) && !t.isArrayPattern(path.parent)) {
     let { start, end } = path.node.loc;
 
     // We want to include function params, but exclude the function name
     if (t.isClassMethod(path.parent) && !path.inList) {
       return;
     }
 
     if (t.isProperty(path.parentPath) && !(0, _helpers.isObjectShorthand)(path.parent)) {
@@ -1485,35 +1517,21 @@ function extractSymbol(path, symbols) {
       location: { start, end },
       expression: "this"
     });
   }
 
   if (t.isVariableDeclarator(path)) {
     const nodeId = path.node.id;
 
-    if (t.isArrayPattern(nodeId)) {
-      return;
-    }
-
-    const properties = nodeId.properties && t.objectPattern(nodeId.properties) ? nodeId.properties : [{
-      value: { name: nodeId.name },
-      loc: path.node.loc
-    }];
-
-    properties.forEach(function (property) {
-      const { start, end } = property.loc;
-      symbols.identifiers.push({
-        name: property.value.name,
-        expression: property.value.name,
-        location: { start, end }
-      });
-    });
-  }
-}
+    const ids = (0, _helpers.getPatternIdentifiers)(nodeId);
+    symbols.identifiers = [...symbols.identifiers, ...ids];
+  }
+}
+
 /* eslint-enable complexity */
 
 function extractSymbols(sourceId) {
   const symbols = {
     functions: [],
     variables: [],
     callExpressions: [],
     memberExpressions: [],
--- a/devtools/client/debugger/new/dist/vendors.css
+++ b/devtools/client/debugger/new/dist/vendors.css
@@ -49,44 +49,49 @@
 .tree-node[data-expandable="false"][aria-level="1"] {
   padding-inline-start: 15px
 }
 
 .tree .tree-node[data-expandable="true"] {
   cursor: default;
 }
 
-.tree-node img.arrow {
-  mask: url("resource://devtools/client/debugger/new/images/arrow.svg") no-repeat;
-  mask-size: 100%;
+.tree-node button.arrow {
+  background:url("resource://devtools/client/debugger/new/images/arrow.svg") no-repeat;
+  background-size:contain;
+  background-position:center center;
   width: 9px;
   height: 9px;
+  border:0;
+  padding:0;
   margin-inline-start: 1px;
   margin-inline-end: 4px;
-  background-color: var(--theme-splitter-color, #9B9B9B);
   transform: rotate(-90deg);
+  transform-origin: center center;
   transition: transform 0.125s ease;
   align-self: center;
+  -moz-context-properties: fill;
+  fill: var(--theme-splitter-color, #9B9B9B);
 }
 
-html[dir="rtl"] .tree-node img.arrow {
+html[dir="rtl"] .tree-node button.arrow {
   transform: rotate(90deg);
 }
 
-.tree-node img.arrow.expanded.expanded {
+.tree-node button.arrow.expanded.expanded {
   transform: rotate(0deg);
  }
 
 .tree .tree-node.focused {
   color: white;
   background-color: var(--theme-selection-background, #0a84ff);
 }
 
-.tree-node.focused img.arrow {
-  background-color: currentColor;
+.tree-node.focused button.arrow {
+  fill: currentColor;
 }
 /* vim:set ts=2 sw=2 sts=2 et: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 .split-box {
   display: flex;
--- a/devtools/client/debugger/new/dist/vendors.js
+++ b/devtools/client/debugger/new/dist/vendors.js
@@ -4617,17 +4617,17 @@ module.exports = "<!-- This Source Code 
 
 module.exports = "<!-- 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 16 16\"><path class=\"st0\" d=\"M9 9.3l3.6 3.6\"></path><ellipse fill=\"transparent\" cx=\"5.9\" cy=\"6.2\" rx=\"4.5\" ry=\"4.5\"></ellipse></svg>"
 
 /***/ }),
 
 /***/ 358:
 /***/ (function(module, exports) {
 
-module.exports = "<!-- 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 viewBox=\"0 0 16 16\" xmlns=\"http://www.w3.org/2000/svg\"><g fill-rule=\"evenodd\"><path d=\"M5 12.503l.052-9a.5.5 0 0 0-1-.006l-.052 9a.5.5 0 0 0 1 .006zM12 12.497l-.05-9A.488.488 0 0 0 11.474 3a.488.488 0 0 0-.473.503l.05 9a.488.488 0 0 0 .477.497.488.488 0 0 0 .473-.503z\"></path></g></svg>"
+module.exports = "<!-- 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 version=\"1.1\" xmlns:svg=\"http://www.w3.org/2000/svg\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" viewBox=\"0 0 16 16\"><path d=\"M5,13.5C5,13.8,4.7,14,4.5,14C4.2,14,4,13.8,4,13.5V2.6c0-0.3,0.2-0.5,0.5-0.5C4.7,2.1,5,2.3,5,2.6V13.5z\"></path><path d=\"M11.9,13.5c0,0.3-0.2,0.5-0.5,0.5s-0.5-0.2-0.5-0.5V2.6c0-0.3,0.2-0.5,0.5-0.5s0.5,0.2,0.5,0.5V13.5z\"></path></svg>"
 
 /***/ }),
 
 /***/ 359:
 /***/ (function(module, exports) {
 
 module.exports = "<!-- 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 viewBox=\"0 0 16 16\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M10.483 13.995H5.517l-3.512-3.512V5.516l3.512-3.512h4.966l3.512 3.512v4.967l-3.512 3.512zm4.37-9.042l-3.807-3.805A.503.503 0 0 0 10.691 1H5.309a.503.503 0 0 0-.356.148L1.147 4.953A.502.502 0 0 0 1 5.308v5.383c0 .134.053.262.147.356l3.806 3.806a.503.503 0 0 0 .356.147h5.382a.503.503 0 0 0 .355-.147l3.806-3.806A.502.502 0 0 0 15 10.69V5.308a.502.502 0 0 0-.147-.355z\"></path><path d=\"M10 10.5a.5.5 0 1 0 1 0v-5a.5.5 0 1 0-1 0v5zM5 10.5a.5.5 0 1 0 1 0v-5a.5.5 0 0 0-1 0v5z\"></path></svg>"
 
@@ -4673,17 +4673,17 @@ module.exports = "<!-- This Source Code 
 
 module.exports = "<!-- 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\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" viewBox=\"0 0 20 16\" stroke=\"none\" fillrule=\"evenodd\"><rect x=\"3\" y=\"10\" width=\"3\" height=\"3\" rx=\"1\"></rect><rect x=\"12\" y=\"3\" width=\"2\" height=\"9\" rx=\"1\"></rect><rect transform=\"translate(13.000000, 7.500000) rotate(60.000000) translate(-13.000000, -7.500000) \" x=\"12\" y=\"3\" width=\"2\" height=\"9\" rx=\"1\"></rect><rect transform=\"translate(13.000000, 7.500000) rotate(-60.000000) translate(-13.000000, -7.500000) \" x=\"12\" y=\"3\" width=\"2\" height=\"9\" rx=\"1\"></rect></svg>"
 
 /***/ }),
 
 /***/ 363:
 /***/ (function(module, exports) {
 
-module.exports = "<!-- 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\" xmlns:svg=\"http://www.w3.org/2000/svg\"><path fill=\"black\" id=\"svg_1\" fill-rule=\"evenodd\" d=\"m4.55195,12.97461l7.4,-5l-7.4,-5l0,10zm-0.925,0l0,-10c0,-0.785 0.8,-1.264 1.415,-0.848l7.4,5c0.58,0.392 0.58,1.304 0,1.696l-7.4,5c-0.615,0.416 -1.415,-0.063 -1.415,-0.848z\"></path></svg>"
+module.exports = "<!-- 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\" xmlns:svg=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 16 16\"><path fill=\"black\" id=\"svg_1\" fill-rule=\"evenodd\" d=\"m4.55195,12.97461l7.4,-5l-7.4,-5l0,10zm-0.925,0l0,-10c0,-0.785 0.8,-1.264 1.415,-0.848l7.4,5c0.58,0.392 0.58,1.304 0,1.696l-7.4,5c-0.615,0.416 -1.415,-0.063 -1.415,-0.848z\"></path></svg>"
 
 /***/ }),
 
 /***/ 3631:
 /***/ (function(module, exports) {
 
 module.exports = "<!-- 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 28 28\"><path fill=\"context-fill\" d=\"M15 11h-1V5a2 2 0 0 0-2-2H4a2 2 0 0 0-2 2v6H1a1 1 0 0 0 0 2h14a1 1 0 1 0 0-2z\"></path></svg>"
 
@@ -6007,17 +6007,17 @@ class ArrowExpander extends Component {
 
   render() {
     const { expanded } = this.props;
 
     const classNames = ["arrow"];
     if (expanded) {
       classNames.push("expanded");
     }
-    return _reactDomFactories2.default.img({
+    return _reactDomFactories2.default.button({
       className: classNames.join(" ")
     });
   }
 }
 
 const treeIndent = _reactDomFactories2.default.span({ className: "tree-indent" }, "\u200B");
 
 class TreeNode extends Component {
--- a/devtools/client/debugger/new/images/moz.build
+++ b/devtools/client/debugger/new/images/moz.build
@@ -16,17 +16,16 @@ DevToolsModules(
     'command-chevron.svg',
     'disable-pausing.svg',
     'domain.svg',
     'extension.svg',
     'file.svg',
     'folder.svg',
     'help.svg',
     'javascript.svg',
-    'next.svg',
     'pause.svg',
     'prettyPrint.svg',
     'react.svg',
     'resume.svg',
     'stepIn.svg',
     'stepOut.svg',
     'stepOver.svg',
     'tab.svg',
deleted file mode 100644
--- a/devtools/client/debugger/new/images/next.svg
+++ /dev/null
@@ -1,8 +0,0 @@
-<!-- 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 version="1.1" xmlns:svg="http://www.w3.org/2000/svg"
-     xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 16 16">
-<path d="M12.4,2.1c-0.3,0-0.5,0.2-0.5,0.5v4.8c0,0-0.1-0.1-0.1-0.1l-7.4-5C3.8,1.8,3,2.2,3,3v10c0,0.8,0.8,1.3,1.4,0.8l7.4-5
-    c0.1,0,0.1-0.1,0.1-0.1v4.8c0,0.3,0.2,0.5,0.5,0.5s0.5-0.2,0.5-0.5v-11C12.9,2.3,12.7,2.1,12.4,2.1z M3.9,13V3l7.4,5L3.9,13z"/>
-</svg>
\ No newline at end of file
--- a/devtools/client/debugger/new/src/actions/ui.js
+++ b/devtools/client/debugger/new/src/actions/ui.js
@@ -9,17 +9,17 @@ import {
   getPaneCollapse,
   getQuickOpenEnabled,
   getSource,
   getFileSearchQuery,
   getProjectDirectoryRoot
 } from "../selectors";
 import { selectSource } from "../actions/sources/select";
 import type { ThunkArgs, panelPositionType } from "./types";
-import { getEditor } from "../utils/editor";
+import { getEditor, getLocationsInViewport } from "../utils/editor";
 import { searchContents } from "./file-search";
 
 import type {
   ActiveSearchType,
   OrientationType,
   SelectedPrimaryPaneTabType
 } from "../reducers/ui";
 
@@ -196,11 +196,18 @@ export function setProjectDirectoryRoot(
 
     dispatch({
       type: "SET_PROJECT_DIRECTORY_ROOT",
       url: newRoot
     });
   };
 }
 
+export function updateViewport() {
+  return {
+    type: "SET_VIEWPORT",
+    viewport: getLocationsInViewport(getEditor())
+  };
+}
+
 export function setOrientation(orientation: OrientationType) {
   return { type: "SET_ORIENTATION", orientation };
 }
new file mode 100644
--- /dev/null
+++ b/devtools/client/debugger/new/src/components/Editor/ColumnBreakpoint.js
@@ -0,0 +1,80 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
+
+// @flow
+import { PureComponent } from "react";
+import classnames from "classnames";
+import { getDocument } from "../../utils/editor";
+
+// eslint-disable-next-line max-len
+import type { ColumnBreakpoint as ColumnBreakpointType } from "../../selectors/visibleColumnBreakpoints";
+
+type Bookmark = {
+  clear: Function
+};
+
+type Props = {
+  callSite: Object,
+  editor: Object,
+  source: Object,
+  enabled: boolean,
+  toggleBreakpoint: (number, number) => void,
+  columnBreakpoint: ColumnBreakpointType
+};
+
+const breakpointImg = document.createElement("div");
+function makeBookmark(isActive, { onClick }) {
+  const bp = breakpointImg.cloneNode(true);
+  bp.className = classnames("call-site", {
+    active: isActive
+  });
+  bp.onclick = onClick;
+  return bp;
+}
+
+export default class CallSite extends PureComponent<Props> {
+  addCallSite: Function;
+  bookmark: ?Bookmark;
+
+  addCallSite = (nextProps: ?Props) => {
+    const { columnBreakpoint, source } = nextProps || this.props;
+    const sourceId = source.id;
+    const { line, column } = columnBreakpoint.location;
+    const widget = makeBookmark(columnBreakpoint.enabled, {
+      onClick: this.toggleBreakpoint
+    });
+    const doc = getDocument(sourceId);
+    this.bookmark = doc.setBookmark({ line: line - 1, ch: column }, { widget });
+  };
+
+  clearCallSite = () => {
+    if (this.bookmark) {
+      this.bookmark.clear();
+      this.bookmark = null;
+    }
+  };
+
+  toggleBreakpoint = () => {
+    const { columnBreakpoint, toggleBreakpoint } = this.props;
+    const { line, column } = columnBreakpoint.location;
+    toggleBreakpoint(line, column);
+  };
+
+  componentDidMount() {
+    this.addCallSite();
+  }
+
+  componentWillUnmount() {
+    this.clearCallSite();
+  }
+
+  componentDidUpdate() {
+    this.clearCallSite();
+    this.addCallSite();
+  }
+
+  render() {
+    return null;
+  }
+}
new file mode 100644
--- /dev/null
+++ b/devtools/client/debugger/new/src/components/Editor/ColumnBreakpoints.js
@@ -0,0 +1,67 @@
+/* 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/>. */
+
+import React, { Component } from "react";
+import { connect } from "react-redux";
+
+import ColumnBreakpoint from "./ColumnBreakpoint";
+import "./ColumnBreakpoints.css";
+
+import { getSelectedSource, visibleColumnBreakpoints } from "../../selectors";
+import { makeLocationId } from "../../utils/breakpoint";
+import actions from "../../actions";
+
+import type { Source } from "../../types";
+// eslint-disable-next-line max-len
+import type { ColumnBreakpoint as ColumnBreakpointType } from "../../selectors/visibleColumnBreakpoints";
+
+class ColumnBreakpoints extends Component {
+  props: {
+    editor: Object,
+    selectedSource: Source,
+    columnBreakpoints: ColumnBreakpointType[]
+  };
+
+  render() {
+    const {
+      editor,
+      columnBreakpoints,
+      selectedSource,
+      toggleBreakpoint
+    } = this.props;
+
+    if (!selectedSource || selectedSource.isBlackBoxed) {
+      return null;
+    }
+
+    let breakpoints;
+    editor.codeMirror.operation(() => {
+      breakpoints = columnBreakpoints.map(breakpoint => (
+        <ColumnBreakpoint
+          key={makeLocationId(breakpoint.location)}
+          columnBreakpoint={breakpoint}
+          editor={editor}
+          source={selectedSource}
+          toggleBreakpoint={toggleBreakpoint}
+        />
+      ));
+    });
+    return <div>{breakpoints}</div>;
+  }
+}
+
+const mapStateToProps = state => {
+  return {
+    selectedSource: getSelectedSource(state),
+    columnBreakpoints: visibleColumnBreakpoints(state)
+  };
+};
+
+const { toggleBreakpoint } = actions;
+const mapDispatchToProps = { toggleBreakpoint };
+
+export default connect(
+  mapStateToProps,
+  mapDispatchToProps
+)(ColumnBreakpoints);
--- a/devtools/client/debugger/new/src/components/Editor/Tabs.js
+++ b/devtools/client/debugger/new/src/components/Editor/Tabs.js
@@ -71,22 +71,26 @@ class Tabs extends PureComponent<Props, 
     if (!(prevProps === this.props)) {
       this.updateHiddenTabs();
     }
   }
 
   componentDidMount() {
     window.requestIdleCallback(this.updateHiddenTabs);
     window.addEventListener("resize", this.onResize);
-    window.document.querySelector(".editor-pane").addEventListener("resizeend", this.onResize);
+    window.document
+      .querySelector(".editor-pane")
+      .addEventListener("resizeend", this.onResize);
   }
 
   componentWillUnmount() {
     window.removeEventListener("resize", this.onResize);
-    window.document.querySelector(".editor-pane").removeEventListener("resizeend", this.onResize);
+    window.document
+      .querySelector(".editor-pane")
+      .removeEventListener("resizeend", this.onResize);
   }
 
   /*
    * Updates the hiddenSourceTabs state, by
    * finding the source tabs which are wrapped and are not on the top row.
    */
   updateHiddenTabs = () => {
     if (!this.refs.sourceTabs) {
--- a/devtools/client/debugger/new/src/components/Editor/index.js
+++ b/devtools/client/debugger/new/src/components/Editor/index.js
@@ -4,16 +4,18 @@
 
 // @flow
 
 import PropTypes from "prop-types";
 import React, { PureComponent } from "react";
 import ReactDOM from "react-dom";
 import { connect } from "react-redux";
 import classnames from "classnames";
+import { debounce } from "lodash";
+
 import { isLoaded } from "../../utils/source";
 import { isFirefox } from "devtools-environment";
 import { features } from "../../utils/prefs";
 import { getIndentation } from "../../utils/indentation";
 
 import {
   getActiveSearch,
   getSelectedLocation,
@@ -25,17 +27,17 @@ import {
 // Redux actions
 import actions from "../../actions";
 
 import Footer from "./Footer";
 import SearchBar from "./SearchBar";
 import HighlightLines from "./HighlightLines";
 import Preview from "./Preview";
 import Breakpoints from "./Breakpoints";
-import CallSites from "./CallSites";
+import ColumnBreakpoints from "./ColumnBreakpoints";
 import DebugLine from "./DebugLine";
 import HighlightLine from "./HighlightLine";
 import EmptyLines from "./EmptyLines";
 import GutterMenu from "./GutterMenu";
 import EditorMenu from "./EditorMenu";
 import ConditionalPanel from "./ConditionalPanel";
 
 import {
@@ -86,17 +88,18 @@ export type Props = {
   openConditionalPanel: (?number) => void,
   closeConditionalPanel: void => void,
   setContextMenu: (string, any) => void,
   continueToHere: (?number) => void,
   toggleBreakpoint: (?number) => void,
   toggleBreakpointsAtLine: (?number) => void,
   addOrToggleDisabledBreakpoint: (?number) => void,
   jumpToMappedLocation: any => void,
-  traverseResults: (boolean, Object) => void
+  traverseResults: (boolean, Object) => void,
+  updateViewport: void => void
 };
 
 type State = {
   editor: SourceEditor
 };
 
 class Editor extends PureComponent<Props, State> {
   $editorWrapper: ?HTMLDivElement;
@@ -152,16 +155,17 @@ class Editor extends PureComponent<Props
 
     codeMirror.on("gutterClick", this.onGutterClick);
 
     // Set code editor wrapper to be focusable
     codeMirrorWrapper.tabIndex = 0;
     codeMirrorWrapper.addEventListener("keydown", e => this.onKeyDown(e));
     codeMirrorWrapper.addEventListener("click", e => this.onClick(e));
     codeMirrorWrapper.addEventListener("mouseover", onMouseOver(codeMirror));
+    codeMirror.on("scroll", this.onEditorScroll);
 
     const toggleFoldMarkerVisibility = e => {
       if (node instanceof HTMLElement) {
         node
           .querySelectorAll(".CodeMirror-guttermarker-subtle")
           .forEach(elem => {
             elem.classList.toggle("visible");
           });
@@ -272,16 +276,18 @@ class Editor extends PureComponent<Props
 
   onToggleConditionalPanel = (key, e: KeyboardEvent) => {
     e.stopPropagation();
     e.preventDefault();
     const line = this.getCurrentLine();
     this.toggleConditionalPanel(line);
   };
 
+  onEditorScroll = debounce(this.props.updateViewport, 200);
+
   onKeyDown(e: KeyboardEvent) {
     const { codeMirror } = this.state.editor;
     const { key, target } = e;
     const codeWrapper = codeMirror.getWrapperElement();
     const textArea = codeWrapper.querySelector("textArea");
 
     if (key === "Escape" && target == textArea) {
       e.stopPropagation();
@@ -543,17 +549,19 @@ class Editor extends PureComponent<Props
         <EmptyLines editor={editor} />
         <Breakpoints editor={editor} />
         <Preview editor={editor} editorRef={this.$editorWrapper} />;
         <Footer horizontal={horizontal} />
         <HighlightLines editor={editor} />
         <EditorMenu editor={editor} />
         <GutterMenu editor={editor} />
         <ConditionalPanel editor={editor} />
-        {features.columnBreakpoints ? <CallSites editor={editor} /> : null}
+        {features.columnBreakpoints ? (
+          <ColumnBreakpoints editor={editor} />
+        ) : null}
       </div>
     );
   }
 
   renderSearchBar() {
     const { editor } = this.state;
 
     if (!editor) {
@@ -602,11 +610,12 @@ export default connect(
     openConditionalPanel: actions.openConditionalPanel,
     closeConditionalPanel: actions.closeConditionalPanel,
     setContextMenu: actions.setContextMenu,
     continueToHere: actions.continueToHere,
     toggleBreakpoint: actions.toggleBreakpoint,
     toggleBreakpointsAtLine: actions.toggleBreakpointsAtLine,
     addOrToggleDisabledBreakpoint: actions.addOrToggleDisabledBreakpoint,
     jumpToMappedLocation: actions.jumpToMappedLocation,
-    traverseResults: actions.traverseResults
+    traverseResults: actions.traverseResults,
+    updateViewport: actions.updateViewport
   }
 )(Editor);
--- a/devtools/client/debugger/new/src/components/Editor/moz.build
+++ b/devtools/client/debugger/new/src/components/Editor/moz.build
@@ -5,18 +5,18 @@
 
 DIRS += [
     'Preview',
 ]
 
 DebuggerModules(
     'Breakpoint.js',
     'Breakpoints.js',
-    'CallSite.js',
-    'CallSites.js',
+    'ColumnBreakpoint.js',
+    'ColumnBreakpoints.js',
     'ConditionalPanel.js',
     'DebugLine.js',
     'EditorMenu.js',
     'EmptyLines.js',
     'Footer.js',
     'GutterMenu.js',
     'HighlightLine.js',
     'HighlightLines.js',
--- a/devtools/client/debugger/new/src/components/SecondaryPanes/Breakpoints/index.js
+++ b/devtools/client/debugger/new/src/components/SecondaryPanes/Breakpoints/index.js
@@ -10,17 +10,17 @@ import { connect } from "react-redux";
 
 import ExceptionOption from "./ExceptionOption";
 
 import Breakpoint from "./Breakpoint";
 import BreakpointHeading from "./BreakpointHeading";
 
 import actions from "../../../actions";
 import { getDisplayPath } from "../../../utils/source";
-import { makeLocationId } from "../../../utils/breakpoint";
+import { makeLocationId, sortBreakpoints } from "../../../utils/breakpoint";
 
 import { getSelectedSource, getBreakpointSources } from "../../../selectors";
 
 import type { Source } from "../../../types";
 import type { BreakpointSources } from "../../../selectors/breakpointSources";
 
 import "./Breakpoints.css";
 
@@ -74,24 +74,26 @@ class Breakpoints extends Component<Prop
     const { breakpointSources } = this.props;
     const sources = [
       ...breakpointSources.map(({ source, breakpoints }) => source)
     ];
 
     return [
       ...breakpointSources.map(({ source, breakpoints, i }) => {
         const path = getDisplayPath(source, sources);
+        const sortedBreakpoints = sortBreakpoints(breakpoints);
+
         return [
           <BreakpointHeading
             source={source}
             sources={sources}
             path={path}
             key={source.url}
           />,
-          ...breakpoints.map(breakpoint => (
+          ...sortedBreakpoints.map(breakpoint => (
             <Breakpoint
               breakpoint={breakpoint}
               source={source}
               key={makeLocationId(breakpoint.selectedLocation)}
             />
           ))
         ];
       })
--- a/devtools/client/debugger/new/src/reducers/ui.js
+++ b/devtools/client/debugger/new/src/reducers/ui.js
@@ -7,36 +7,39 @@
 /**
  * UI reducer
  * @module reducers/ui
  */
 
 import makeRecord from "../utils/makeRecord";
 import { prefs } from "../utils/prefs";
 
-import type { Source } from "../types";
+import type { Source, Range } from "../types";
 
 import type { Action, panelPositionType } from "../actions/types";
 import type { Record } from "../utils/makeRecord";
 
 export type ActiveSearchType = "project" | "file";
 
 export type OrientationType = "horizontal" | "vertical";
 
 export type SelectedPrimaryPaneTabType = "sources" | "outline";
 
+type Viewport = Range;
+
 export type UIState = {
   selectedPrimaryPaneTab: SelectedPrimaryPaneTabType,
   activeSearch: ?ActiveSearchType,
   contextMenu: any,
   shownSource: ?Source,
   startPanelCollapsed: boolean,
   endPanelCollapsed: boolean,
   frameworkGroupingOn: boolean,
   orientation: OrientationType,
+  viewport: ?Viewport,
   highlightedLineRange?: {
     start?: number,
     end?: number,
     sourceId?: number
   },
   conditionalPanelLine: null | number
 };
 
@@ -46,17 +49,18 @@ export const createUIState = makeRecord(
     activeSearch: null,
     contextMenu: {},
     shownSource: null,
     startPanelCollapsed: prefs.startPanelCollapsed,
     endPanelCollapsed: prefs.endPanelCollapsed,
     frameworkGroupingOn: prefs.frameworkGroupingOn,
     highlightedLineRange: undefined,
     conditionalPanelLine: null,
-    orientation: "horizontal"
+    orientation: "horizontal",
+    viewport: null
   }: UIState)
 );
 
 function update(
   state: Record<UIState> = createUIState(),
   action: Action
 ): Record<UIState> {
   switch (action.type) {
@@ -116,16 +120,20 @@ function update(
 
     case "CLOSE_PROJECT_SEARCH": {
       if (state.get("activeSearch") === "project") {
         return state.set("activeSearch", null);
       }
       return state;
     }
 
+    case "SET_VIEWPORT": {
+      return state.set("viewport", action.viewport);
+    }
+
     case "NAVIGATE": {
       return state.set("activeSearch", null).set("highlightedLineRange", {});
     }
 
     default: {
       return state;
     }
   }
@@ -175,9 +183,13 @@ export function getHighlightedLineRange(
 export function getConditionalPanelLine(state: OuterState): null | number {
   return state.ui.get("conditionalPanelLine");
 }
 
 export function getOrientation(state: OuterState): boolean {
   return state.ui.get("orientation");
 }
 
+export function getViewport(state: OuterState) {
+  return state.ui.get("viewport");
+}
+
 export default update;
--- a/devtools/client/debugger/new/src/selectors/index.js
+++ b/devtools/client/debugger/new/src/selectors/index.js
@@ -30,16 +30,18 @@ export {
 } from "./breakpointAtLocation";
 export { getVisibleBreakpoints } from "./visibleBreakpoints";
 export { inComponent } from "./inComponent";
 export { isSelectedFrameVisible } from "./isSelectedFrameVisible";
 export { getCallStackFrames } from "./getCallStackFrames";
 export { getVisibleSelectedFrame } from "./visibleSelectedFrame";
 export { getBreakpointSources } from "./breakpointSources";
 export { getXHRBreakpoints, shouldPauseOnAnyXHR } from "./breakpoints";
+export { visibleColumnBreakpoints } from "./visibleColumnBreakpoints";
+export { getVisiblePausePoints } from "./visiblePausePoints";
 
 import { objectInspector } from "devtools-reps";
 
 const { reducer } = objectInspector;
 
 Object.keys(reducer).forEach(function(key) {
   if (key === "default" || key === "__esModule") {
     return;
--- a/devtools/client/debugger/new/src/selectors/moz.build
+++ b/devtools/client/debugger/new/src/selectors/moz.build
@@ -11,10 +11,12 @@ DebuggerModules(
     'breakpointAtLocation.js',
     'breakpoints.js',
     'breakpointSources.js',
     'getCallStackFrames.js',
     'inComponent.js',
     'index.js',
     'isSelectedFrameVisible.js',
     'visibleBreakpoints.js',
+    'visibleColumnBreakpoints.js',
+    'visiblePausePoints.js',
     'visibleSelectedFrame.js',
 )
new file mode 100644
--- /dev/null
+++ b/devtools/client/debugger/new/src/selectors/visibleColumnBreakpoints.js
@@ -0,0 +1,76 @@
+/* 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/>. */
+
+import { groupBy, hasIn } from "lodash";
+import { createSelector } from "reselect";
+
+import { getViewport } from "../selectors";
+import { getVisibleBreakpoints } from "./visibleBreakpoints";
+import { getVisiblePausePoints } from "./visiblePausePoints";
+
+import type { Location } from "../types";
+
+export type ColumnBreakpoint = {|
+  +location: Location,
+  +enabled: boolean
+|};
+
+function contains(location, range) {
+  return (
+    location.line >= range.start.line &&
+    location.line <= range.end.line &&
+    location.column >= range.start.column &&
+    location.column <= range.end.column
+  );
+}
+
+function groupBreakpoints(breakpoints) {
+  const map = groupBy(breakpoints, ({ location }) => location.line);
+  for (const line in map) {
+    map[line] = groupBy(map[line], ({ location }) => location.column);
+  }
+
+  return map;
+}
+
+function isEnabled(location, breakpointMap) {
+  const { line, column } = location;
+  return hasIn(breakpointMap, [line, column]);
+}
+
+export function formatColumnBreakpoints(columnBreakpoints) {
+  console.log(
+    "Column Breakpoints\n\n",
+    columnBreakpoints
+      .map(
+        ({ location, enabled }) =>
+          `(${location.line}, ${location.column}) ${enabled}`
+      )
+      .join("\n")
+  );
+}
+
+export function getColumnBreakpoints(pausePoints, breakpoints, viewport) {
+  if (!pausePoints) {
+    return [];
+  }
+
+  const breakpointMap = groupBreakpoints(breakpoints);
+  const columnBreakpoints = pausePoints
+    .filter(({ types }) => types.break)
+    .filter(({ location }) => breakpointMap[location.line])
+    .filter(({ location }) => viewport && contains(location, viewport));
+
+  return columnBreakpoints.map(({ location }) => ({
+    location,
+    enabled: isEnabled(location, breakpointMap)
+  }));
+}
+
+export const visibleColumnBreakpoints = createSelector(
+  getVisiblePausePoints,
+  getVisibleBreakpoints,
+  getViewport,
+  getColumnBreakpoints
+);
new file mode 100644
--- /dev/null
+++ b/devtools/client/debugger/new/src/selectors/visiblePausePoints.js
@@ -0,0 +1,17 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
+
+import { getSelectedSource } from "../reducers/sources";
+import { getPausePoints } from "../reducers/ast";
+import { convertToList } from "../utils/pause/pausePoints";
+
+export function getVisiblePausePoints(state) {
+  const source = getSelectedSource(state);
+  if (!source) {
+    return null;
+  }
+
+  const pausePoints = getPausePoints(state, source.id);
+  return convertToList(pausePoints);
+}
--- a/devtools/client/debugger/new/src/utils/breakpoint/index.js
+++ b/devtools/client/debugger/new/src/utils/breakpoint/index.js
@@ -1,20 +1,23 @@
 /* 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/>. */
 
 // @flow
 
+import { sortBy } from "lodash";
+
 import { getBreakpoint } from "../../selectors";
 import assert from "../assert";
 import { features } from "../prefs";
 
 export { getASTLocation, findScopeByName } from "./astBreakpointLocation";
 
+import type { FormattedBreakpoint } from "../../selectors/breakpointSources";
 import type {
   Location,
   PendingLocation,
   Breakpoint,
   PendingBreakpoint
 } from "../../types";
 
 import type { State } from "../../reducers/types";
@@ -174,8 +177,25 @@ export function createPendingBreakpoint(
   return {
     condition: bp.condition,
     disabled: bp.disabled,
     location: pendingLocation,
     astLocation: bp.astLocation,
     generatedLocation: pendingGeneratedLocation
   };
 }
+
+export function sortBreakpoints(breakpoints: FormattedBreakpoint[]) {
+  breakpoints = breakpoints.map(bp => ({
+    ...bp,
+    selectedLocation: {
+      ...bp.selectedLocation,
+      // If a breakpoint has an undefined column, we must provide a 0 value
+      // or the breakpoint will display after all explicit column numbers
+      column: bp.selectedLocation.column || 0
+    }
+  }));
+
+  return sortBy(breakpoints, [
+    "selectedLocation.line",
+    "selectedLocation.column"
+  ]);
+}
--- a/devtools/client/debugger/new/src/utils/dbg.js
+++ b/devtools/client/debugger/new/src/utils/dbg.js
@@ -49,16 +49,24 @@ function getCM() {
 }
 
 function _formatPausePoints(dbg: Object, url: string) {
   const source = dbg.helpers.findSource(url);
   const pausePoints = dbg.selectors.getPausePoints(source);
   console.log(formatPausePoints(source.text, pausePoints));
 }
 
+function _formatColumnBreapoints(dbg: Object) {
+  console.log(
+    dbg.selectors.formatColumnBreakpoints(
+      dbg.selectors.visibleColumnBreakpoints()
+    )
+  );
+}
+
 export function setupHelper(obj: Object) {
   const selectors = bindSelectors(obj);
   const dbg: Object = {
     ...obj,
     selectors,
     prefs,
     asyncStore,
     features,
@@ -67,17 +75,18 @@ export function setupHelper(obj: Object)
     helpers: {
       findSource: url => findSource(dbg, url),
       findSources: url => findSources(dbg, url),
       evaluate: (expression, cbk) => evaluate(dbg, expression, cbk),
       sendPacketToThread: (packet, cbk) => sendPacketToThread(dbg, packet, cbk),
       sendPacket: (packet, cbk) => sendPacket(dbg, packet, cbk)
     },
     formatters: {
-      pausePoints: url => _formatPausePoints(dbg, url)
+      pausePoints: url => _formatPausePoints(dbg, url),
+      visibleColumnBreakpoints: () => _formatColumnBreapoints(dbg)
     },
     _telemetry: {
       events: {}
     }
   };
 
   window.dbg = dbg;
 
--- a/devtools/client/debugger/new/src/utils/editor/index.js
+++ b/devtools/client/debugger/new/src/utils/editor/index.js
@@ -153,16 +153,44 @@ function isVisible(codeMirror: any, top:
     top,
     scrollTop,
     scrollTop + scrollArea.clientHeight - fontHeight
   );
 
   return inXView && inYView;
 }
 
+export function getLocationsInViewport(_editor: any) {
+  // Get scroll position
+  const charWidth = _editor.codeMirror.defaultCharWidth();
+  const scrollArea = _editor.codeMirror.getScrollInfo();
+  const { scrollLeft } = _editor.codeMirror.doc;
+  const rect = _editor.codeMirror.getWrapperElement().getBoundingClientRect();
+  const topVisibleLine = _editor.codeMirror.lineAtHeight(rect.top, "window");
+  const bottomVisibleLine = _editor.codeMirror.lineAtHeight(
+    rect.bottom,
+    "window"
+  );
+
+  const leftColumn = Math.floor(scrollLeft > 0 ? scrollLeft / charWidth : 0);
+  const rightPosition = scrollLeft + (scrollArea.clientWidth - 30);
+  const rightCharacter = Math.floor(rightPosition / charWidth);
+
+  return {
+    start: {
+      line: topVisibleLine,
+      column: leftColumn
+    },
+    end: {
+      line: bottomVisibleLine,
+      column: rightCharacter
+    }
+  };
+}
+
 export function markText(_editor: any, className, { start, end }: EditorRange) {
   return _editor.codeMirror.markText(
     { ch: start.column, line: start.line },
     { ch: end.column, line: end.line },
     { className }
   );
 }
 
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg-breakpoints-actions.js
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-breakpoints-actions.js
@@ -28,43 +28,51 @@ add_task(async function() {
   await selectSource(dbg, "simple1");
   await waitForSelectedSource(dbg, "simple1");
 
   await addBreakpoint(dbg, "simple1", 1);
   await addBreakpoint(dbg, "simple1", 4);
   await addBreakpoint(dbg, "simple1", 5);
   await addBreakpoint(dbg, "simple1", 6);
 
-  openFirstBreakpointContextMenu(dbg)
+  openFirstBreakpointContextMenu(dbg);
   // select "Disable Others"
+  let dispatched = waitForDispatch(dbg, "DISABLE_BREAKPOINT", 3);
   selectMenuItem(dbg, 7);
   await waitForState(dbg, state =>
     dbg.selectors.getBreakpointsList(state)
       .every(bp => (bp.location.line !== 1) === bp.disabled)
   );
+  await dispatched;
   ok("breakpoint at 1 is the only enabled breakpoint");
 
-  openFirstBreakpointContextMenu(dbg)
+  openFirstBreakpointContextMenu(dbg);
   // select "Disable All"
+  dispatched = waitForDispatch(dbg, "DISABLE_ALL_BREAKPOINTS");
   selectMenuItem(dbg, 9);
   await waitForState(dbg, state =>
     dbg.selectors.getBreakpointsList(state).every(bp => bp.disabled)
   );
-  ok("all breakpoints are disabled")
+  await dispatched;
+  ok("all breakpoints are disabled");
 
-  openFirstBreakpointContextMenu(dbg)
+  openFirstBreakpointContextMenu(dbg);
   // select "Enable Others"
+  dispatched = waitForDispatch(dbg, "ENABLE_BREAKPOINT", 3);
   selectMenuItem(dbg, 3);
   await waitForState(dbg, state =>
     dbg.selectors.getBreakpointsList(state)
       .every(bp => (bp.location.line === 1) === bp.disabled)
   );
+  await dispatched;
   ok("all breakpoints except line 1 are enabled");
 
-  openFirstBreakpointContextMenu(dbg)
+  openFirstBreakpointContextMenu(dbg);
   // select "Remove Others"
+  dispatched = waitForDispatch(dbg, "REMOVE_BREAKPOINT", 3);
   selectMenuItem(dbg, 6);
   await waitForState(dbg, state =>
     dbg.selectors.getBreakpointsList(state).length === 1 &&
     dbg.selectors.getBreakpointsList(state)[0].location.line === 1
   );
+  await dispatched;
   ok("remaining breakpoint should be on line 1");
 });
--- a/devtools/client/debugger/new/test/mochitest/helpers.js
+++ b/devtools/client/debugger/new/test/mochitest/helpers.js
@@ -336,17 +336,17 @@ function assertDebugLine(dbg, line) {
     "There is only one line"
   );
 
   ok(isVisibleInEditor(dbg, debugLine), "debug line is visible");
 
   const markedSpans = lineInfo.handle.markedSpans;
   if (markedSpans && markedSpans.length > 0) {
     const classMatch = markedSpans.filter(
-      span => span.marker.className.includes("debug-expression")
+      span => span.marker.className && span.marker.className.includes("debug-expression")
     ).length > 0;
 
     ok(
       classMatch,
       "expression is highlighted as paused"
     );
   }
 }
--- a/devtools/client/inspector/flexbox/components/FlexItem.js
+++ b/devtools/client/inspector/flexbox/components/FlexItem.js
@@ -17,36 +17,39 @@ const Types = require("../types");
 
 class FlexItem extends PureComponent {
   static get propTypes() {
     return {
       flexItem: PropTypes.shape(Types.flexItem).isRequired,
       index: PropTypes.number.isRequired,
       onHideBoxModelHighlighter: PropTypes.func.isRequired,
       onShowBoxModelHighlighterForNode: PropTypes.func.isRequired,
+      scrollToTop: PropTypes.func.isRequired,
       setSelectedNode: PropTypes.func.isRequired,
     };
   }
 
   render() {
     const {
       flexItem,
       index,
       onHideBoxModelHighlighter,
       onShowBoxModelHighlighterForNode,
+      scrollToTop,
       setSelectedNode,
     } = this.props;
     const { nodeFront } = flexItem;
 
     return (
       dom.button(
         {
           className: "devtools-button devtools-monospace",
           onClick: e => {
             e.stopPropagation();
+            scrollToTop();
             setSelectedNode(nodeFront);
             onHideBoxModelHighlighter();
           },
           onMouseOut: () => onHideBoxModelHighlighter(),
           onMouseOver: () => onShowBoxModelHighlighterForNode(nodeFront),
         },
         dom.span({ className: "flex-item-index" }, index),
         Rep({
--- a/devtools/client/inspector/flexbox/components/FlexItemList.js
+++ b/devtools/client/inspector/flexbox/components/FlexItemList.js
@@ -14,38 +14,41 @@ const FlexItem = createFactory(require((
 const Types = require("../types");
 
 class FlexItemList extends PureComponent {
   static get propTypes() {
     return {
       flexItems: PropTypes.arrayOf(PropTypes.shape(Types.flexItem)).isRequired,
       onHideBoxModelHighlighter: PropTypes.func.isRequired,
       onShowBoxModelHighlighterForNode: PropTypes.func.isRequired,
+      scrollToTop: PropTypes.func.isRequired,
       setSelectedNode: PropTypes.func.isRequired,
     };
   }
 
   render() {
     const {
       flexItems,
       onHideBoxModelHighlighter,
       onShowBoxModelHighlighterForNode,
+      scrollToTop,
       setSelectedNode,
     } = this.props;
 
     return (
       dom.div(
         { className: "flex-item-list" },
         dom.div({ className: "flex-item-list-header" }, getStr("flexbox.flexItems")),
         flexItems.map((flexItem, index) => FlexItem({
           key: flexItem.actorID,
           flexItem,
           index: index + 1,
           onHideBoxModelHighlighter,
           onShowBoxModelHighlighterForNode,
+          scrollToTop,
           setSelectedNode,
         }))
       )
     );
   }
 }
 
 module.exports = FlexItemList;
--- a/devtools/client/inspector/flexbox/components/Flexbox.js
+++ b/devtools/client/inspector/flexbox/components/Flexbox.js
@@ -34,32 +34,35 @@ class Flexbox extends PureComponent {
     return {
       flexbox: PropTypes.shape(Types.flexbox).isRequired,
       flexContainer: PropTypes.shape(Types.flexContainer).isRequired,
       getSwatchColorPickerTooltip: PropTypes.func.isRequired,
       onHideBoxModelHighlighter: PropTypes.func.isRequired,
       onSetFlexboxOverlayColor: PropTypes.func.isRequired,
       onShowBoxModelHighlighterForNode: PropTypes.func.isRequired,
       onToggleFlexboxHighlighter: PropTypes.func.isRequired,
+      scrollToTop: PropTypes.func.isRequired,
       setSelectedNode: PropTypes.func.isRequired,
     };
   }
 
   renderFlexItemList() {
     const {
       onHideBoxModelHighlighter,
       onShowBoxModelHighlighterForNode,
+      scrollToTop,
       setSelectedNode,
     } = this.props;
     const { flexItems } = this.props.flexContainer;
 
     return FlexItemList({
       flexItems,
       onHideBoxModelHighlighter,
       onShowBoxModelHighlighterForNode,
+      scrollToTop,
       setSelectedNode,
     });
   }
 
   renderFlexItemSizing() {
     const {
       color,
     } = this.props.flexbox;
--- a/devtools/client/inspector/layout/components/LayoutApp.js
+++ b/devtools/client/inspector/layout/components/LayoutApp.js
@@ -1,16 +1,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const Services = require("Services");
-const { createFactory, PureComponent } = require("devtools/client/shared/vendor/react");
+const { createFactory, createRef, PureComponent } = require("devtools/client/shared/vendor/react");
 const dom = require("devtools/client/shared/vendor/react-dom-factories");
 const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
 const { connect } = require("devtools/client/shared/vendor/react-redux");
 const {
   getSelectorFromGrip,
   translateNodeFrontToGrip,
 } = require("devtools/client/inspector/shared/utils");
 const { LocalizationHelper } = require("devtools/shared/l10n");
@@ -56,29 +56,43 @@ class LayoutApp extends PureComponent {
       onToggleShowGridAreas: PropTypes.func.isRequired,
       onToggleShowGridLineNumbers: PropTypes.func.isRequired,
       onToggleShowInfiniteLines: PropTypes.func.isRequired,
       setSelectedNode: PropTypes.func.isRequired,
       showBoxModelProperties: PropTypes.bool.isRequired,
     };
   }
 
+  constructor(props) {
+    super(props);
+    this.containerRef = createRef();
+
+    this.scrollToTop = this.scrollToTop.bind(this);
+  }
+
   getFlexboxHeader(flexContainer) {
     if (!flexContainer.actorID) {
       // No flex container or flex item selected.
       return LAYOUT_L10N.getStr("flexbox.header");
     } else if (!flexContainer.flexItemShown) {
       // No flex item selected.
       return LAYOUT_L10N.getStr("flexbox.flexContainer");
     }
 
     const grip = translateNodeFrontToGrip(flexContainer.nodeFront);
     return LAYOUT_L10N.getFormatStr("flexbox.flexItemOf", getSelectorFromGrip(grip));
   }
 
+  /**
+   * Scrolls to top of the layout container.
+   */
+  scrollToTop() {
+    this.containerRef.current.scrollTop = 0;
+  }
+
   render() {
     const items = [
       {
         component: Grid,
         componentProps: this.props,
         header: LAYOUT_L10N.getStr("layout.header"),
         opened: Services.prefs.getBoolPref(GRID_OPENED_PREF),
         onToggled: () => {
@@ -101,16 +115,17 @@ class LayoutApp extends PureComponent {
     if (Services.prefs.getBoolPref(FLEXBOX_ENABLED_PREF)) {
       // Since the flexbox panel is hidden behind a pref. We insert the flexbox container
       // to the first index of the accordion item list.
       items.splice(0, 0, {
         component: Flexbox,
         componentProps: {
           ...this.props,
           flexContainer: this.props.flexbox.flexContainer,
+          scrollToTop: this.scrollToTop,
         },
         header: this.getFlexboxHeader(this.props.flexbox.flexContainer),
         opened: Services.prefs.getBoolPref(FLEXBOX_OPENED_PREF),
         onToggled: () => {
           const opened =  Services.prefs.getBoolPref(FLEXBOX_OPENED_PREF);
           Services.prefs.setBoolPref(FLEXBOX_OPENED_PREF, !opened);
         },
       });
@@ -122,28 +137,29 @@ class LayoutApp extends PureComponent {
           this.props.flexbox.flexItemContainer.actorID) {
         // Insert the parent flex container to the first index of the accordion item
         // list.
         items.splice(0, 0, {
           component: Flexbox,
           componentProps: {
             ...this.props,
             flexContainer: this.props.flexbox.flexItemContainer,
+            scrollToTop: this.scrollToTop,
           },
           header: this.getFlexboxHeader(this.props.flexbox.flexItemContainer),
           opened: Services.prefs.getBoolPref(FLEXBOX_OPENED_PREF),
           onToggled: () => {
             const opened =  Services.prefs.getBoolPref(FLEXBOX_OPENED_PREF);
             Services.prefs.setBoolPref(FLEXBOX_OPENED_PREF, !opened);
           },
         });
       }
     }
 
     return (
-      dom.div({ className: "layout-container" },
+      dom.div({ className: "layout-container", ref: this.containerRef },
         Accordion({ items })
       )
     );
   }
 }
 
 module.exports = connect(state => state)(LayoutApp);
new file mode 100644
--- /dev/null
+++ b/devtools/client/shared/components/reps/images/input.svg
@@ -0,0 +1,7 @@
+<!-- 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 12 12" fill="context-fill #0b0b0b">
+  <path d="M11.04 5.46L7.29 1.71a.75.75 0 0 0-1.06 1.06L9.45 6 6.23 9.21a.75.75 0 1 0 1.06 1.06l3.75-3.75c.3-.3.3-.77 0-1.06z"/>
+  <path d="M6.04 5.46L2.29 1.71a.75.75 0 0 0-1.06 1.06L4.45 6 1.23 9.21a.75.75 0 1 0 1.06 1.06l3.75-3.75c.3-.3.3-.77 0-1.06z"/>
+</svg>
\ No newline at end of file
--- a/devtools/client/shared/components/reps/images/moz.build
+++ b/devtools/client/shared/components/reps/images/moz.build
@@ -1,10 +1,11 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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/.
 
 DevToolsModules(
+    'input.svg',
     'jump-definition.svg',
     'open-inspector.svg',
 )
--- a/devtools/client/shared/components/reps/reps.css
+++ b/devtools/client/shared/components/reps/reps.css
@@ -272,16 +272,32 @@ button.jump-definition {
   vertical-align: middle;
 }
 
 .jump-definition:hover {
   background-color: var(--theme-highlight-blue);
 }
 
 /******************************************************************************/
+/* Invoke getter button */
+
+button.invoke-getter {
+  mask: url("resource://devtools/client/shared/components/reps/images/input.svg") no-repeat;
+  display: inline-block;
+  background-color: var(--comment-node-color);
+  height: 12px;
+  vertical-align:bottom;
+  border:none;
+}
+
+.invoke-getter:hover {
+  background-color: var(--theme-highlight-blue);
+}
+
+/******************************************************************************/
 /* "more…" ellipsis */
 .more-ellipsis {
   color: var(--comment-node-color);
 }
 /* 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/. */
 
@@ -332,44 +348,49 @@ button.jump-definition {
 .tree-node[data-expandable="false"][aria-level="1"] {
   padding-inline-start: 15px
 }
 
 .tree .tree-node[data-expandable="true"] {
   cursor: default;
 }
 
-.tree-node img.arrow {
-  mask: url("resource://devtools/client/debugger/new/images/arrow.svg") no-repeat;
-  mask-size: 100%;
+.tree-node button.arrow {
+  background:url("resource://devtools/client/debugger/new/images/arrow.svg") no-repeat;
+  background-size:contain;
+  background-position:center center;
   width: 9px;
   height: 9px;
+  border:0;
+  padding:0;
   margin-inline-start: 1px;
   margin-inline-end: 4px;
-  background-color: var(--theme-splitter-color, #9B9B9B);
   transform: rotate(-90deg);
+  transform-origin: center center;
   transition: transform 0.125s ease;
   align-self: center;
+  -moz-context-properties: fill;
+  fill: var(--theme-splitter-color, #9B9B9B);
 }
 
-html[dir="rtl"] .tree-node img.arrow {
+html[dir="rtl"] .tree-node button.arrow {
   transform: rotate(90deg);
 }
 
-.tree-node img.arrow.expanded.expanded {
+.tree-node button.arrow.expanded.expanded {
   transform: rotate(0deg);
  }
 
 .tree .tree-node.focused {
   color: white;
   background-color: var(--theme-selection-background, #0a84ff);
 }
 
-.tree-node.focused img.arrow {
-  background-color: currentColor;
+.tree-node.focused button.arrow {
+  fill: currentColor;
 }
 /* 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/. */
 
 .tree.object-inspector .node.object-node {
   display: inline-block;
 }
@@ -399,17 +420,17 @@ html[dir="rtl"] .tree-node img.arrow {
   content: "\2632   ";
   font-size: 1.1em;
 }
 
 .object-inspector .object-delimiter {
   color: var(--theme-comment);
 }
 
-.object-inspector .tree-node img.arrow {
+.object-inspector .tree-node .arrow {
   display: inline-block;
   vertical-align: middle;
 }
 
 /* Focused styles */
 .tree.object-inspector .tree-node.focused * {
   color: inherit;
 }
--- a/devtools/client/shared/components/reps/reps.js
+++ b/devtools/client/shared/components/reps/reps.js
@@ -3766,17 +3766,17 @@ class ArrowExpander extends Component {
 
   render() {
     const { expanded } = this.props;
 
     const classNames = ["arrow"];
     if (expanded) {
       classNames.push("expanded");
     }
-    return _reactDomFactories2.default.img({
+    return _reactDomFactories2.default.button({
       className: classNames.join(" ")
     });
   }
 }
 
 const treeIndent = _reactDomFactories2.default.span({ className: "tree-indent" }, "\u200B");
 
 class TreeNode extends Component {
@@ -4988,16 +4988,18 @@ module.exports = {
 /***/ }),
 
 /***/ 3680:
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
+var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
+
 /* 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/>. */
 
 // Dependencies
 const dom = __webpack_require__(3643);
 const PropTypes = __webpack_require__(3642);
 const { wrapRender } = __webpack_require__(3644);
@@ -5009,28 +5011,50 @@ const { span } = dom;
  * properties enclosed in curly brackets.
  */
 Accessor.propTypes = {
   object: PropTypes.object.isRequired,
   mode: PropTypes.oneOf(Object.values(MODE))
 };
 
 function Accessor(props) {
-  const { object } = props;
+  const { object, evaluation, onInvokeGetterButtonClick } = props;
+
+  if (evaluation) {
+    const { Rep, Grip } = __webpack_require__(3647);
+    return span({
+      className: "objectBox objectBox-accessor objectTitle"
+    }, Rep(_extends({}, props, {
+      object: evaluation.getterValue,
+      mode: props.mode || MODE.TINY,
+      defaultRep: Grip
+    })));
+  }
+
+  if (hasGetter(object) && onInvokeGetterButtonClick) {
+    return dom.button({
+      className: "invoke-getter",
+      title: "Invoke getter",
+      onClick: event => {
+        onInvokeGetterButtonClick();
+        event.stopPropagation();
+      }
+    });
+  }
 
   const accessors = [];
   if (hasGetter(object)) {
     accessors.push("Getter");
   }
+
   if (hasSetter(object)) {
     accessors.push("Setter");
   }
-  const title = accessors.join(" & ");
-
-  return span({ className: "objectBox objectBox-accessor objectTitle" }, title);
+
+  return span({ className: "objectBox objectBox-accessor objectTitle" }, accessors.join(" & "));
 }
 
 function hasGetter(object) {
   return object && object.get && object.get.type !== "undefined";
 }
 
 function hasSetter(object) {
   return object && object.set && object.set.type !== "undefined";
--- a/devtools/client/shared/vendor/WASMPARSER_UPGRADING
+++ b/devtools/client/shared/vendor/WASMPARSER_UPGRADING
@@ -1,14 +1,14 @@
 # wasmparser version
 
-Current vesion is: 0.6.1
+Current vesion is: 0.6.2
 
 # Upgrade process
 
 1. Pull latest release from npm and extract WasmDis.js and WasmParser.js, e.g.
 
 ```
-curl https://registry.npmjs.org/wasmparser/-/wasmparser-0.6.1.tgz | tar -x --strip-components 2 package/dist/{WasmDis,WasmParser}.js
+curl https://registry.npmjs.org/wasmparser/-/wasmparser-0.6.2.tgz | tar -x --strip-components 2 package/dist/{WasmDis,WasmParser}.js
 ```
 
 2. Remove reference to source maps (last line)
 
--- a/devtools/client/shared/vendor/WasmDis.js
+++ b/devtools/client/shared/vendor/WasmDis.js
@@ -626,17 +626,17 @@ var WasmDisassembler = /** @class */ (fu
                 break;
             case 63 /* current_memory */:
             case 64 /* grow_memory */:
                 break;
             case 65 /* i32_const */:
                 this.appendBuffer(" " + operator.literal.toString());
                 break;
             case 66 /* i64_const */:
-                this.appendBuffer(" " + operator.literal.toDouble());
+                this.appendBuffer(" " + operator.literal.toString());
                 break;
             case 67 /* f32_const */:
                 this.appendBuffer(" " + formatFloat32(operator.literal));
                 break;
             case 68 /* f64_const */:
                 this.appendBuffer(" " + formatFloat64(operator.literal));
                 break;
         }
--- a/devtools/client/shared/vendor/WasmParser.js
+++ b/devtools/client/shared/vendor/WasmParser.js
@@ -409,16 +409,46 @@ var Int64 = /** @class */ (function () {
         }
         else {
             sum = 0;
             for (var i = 0; i < 8; i++, power *= 256)
                 sum += power * this._data[i];
         }
         return sum;
     };
+    Int64.prototype.toString = function () {
+        var low = (this._data[0] | (this._data[1] << 8) | (this._data[2] << 16) | (this._data[3] << 24)) >>> 0;
+        var high = (this._data[4] | (this._data[5] << 8) | (this._data[6] << 16) | (this._data[7] << 24)) >>> 0;
+        if (low === 0 && high === 0) {
+            return '0';
+        }
+        var sign = false;
+        if (high >> 31) {
+            high = 4294967296 - high;
+            if (low > 0) {
+                high--;
+                low = 4294967296 - low;
+            }
+            sign = true;
+        }
+        var buf = [];
+        while (high > 0) {
+            var t = ((high % 10) * 4294967296) + low;
+            high = Math.floor(high / 10);
+            buf.unshift((t % 10).toString());
+            low = Math.floor(t / 10);
+        }
+        while (low > 0) {
+            buf.unshift((low % 10).toString());
+            low = Math.floor(low / 10);
+        }
+        if (sign)
+            buf.unshift('-');
+        return buf.join('');
+    };
     Object.defineProperty(Int64.prototype, "data", {
         get: function () {
             return this._data;
         },
         enumerable: true,
         configurable: true
     });
     return Int64;
--- a/devtools/client/themes/layout.css
+++ b/devtools/client/themes/layout.css
@@ -139,16 +139,19 @@ html[dir="rtl"] .flex-header-button-prev
   text-overflow: ellipsis;
   overflow: hidden;
   white-space: nowrap;
 }
 
 .flex-header-content.flex-item-shown {
   justify-content: center;
   padding: 0;
+  text-overflow: ellipsis;
+  overflow: hidden;
+  white-space: nowrap;
 }
 
 .flex-header-container-properties {
   display: flex;
 }
 
 .flex-header-container-properties .inspector-badge:first-child {
   margin-inline-start: 0;
@@ -224,32 +227,18 @@ html[dir="rtl"] .flex-item-list .devtool
   vertical-align: middle;
 }
 
 /**
  * Flex Item Selector
  */
 
 #flex-item-selector {
-  background-position: right 4px center;
   cursor: pointer;
-  padding-left: 0;
-  vertical-align: middle;
-  width: 140px;
-}
-
-#flex-item-selector .objectBox-node {
-  display: inline-block;
   font-size: 12px;
-  overflow: hidden;
-  padding-top: 0.15em;
-  text-align: left;
-  text-overflow: ellipsis;
-  white-space: nowrap;
-  width: 85%;
 }
 
 /**
  * Flex Item Sizing Outline
  */
 
 .flex-outline-container {
   display: flex;
--- a/devtools/client/themes/toolbars.css
+++ b/devtools/client/themes/toolbars.css
@@ -211,16 +211,17 @@ splitter.devtools-horizontal-splitter,
   background-color: var(--theme-splitter-color);
 }
 
 /* Dropdown Menu Button */
 .devtools-dropdown-button {
   background-image: url("chrome://devtools/skin/images/select-arrow.svg") !important;
   background-position: right center !important;
   background-repeat: no-repeat !important;
+  text-overflow: ellipsis;
   overflow: hidden;
   padding-inline-start: 2px !important;
   padding-inline-end: 10px !important;
   fill: var(--theme-toolbar-photon-icon-color);
   -moz-context-properties: fill;
 }
 
 .devtools-dropdown-button:not(:hover) {
--- a/docshell/base/nsDefaultURIFixup.cpp
+++ b/docshell/base/nsDefaultURIFixup.cpp
@@ -74,22 +74,19 @@ nsDefaultURIFixup::CreateExposableURI(ns
   if (isWyciwyg) {
     nsresult rv = nsContentUtils::RemoveWyciwygScheme(aURI, getter_AddRefs(uri));
     NS_ENSURE_SUCCESS(rv, rv);
   } else {
     // No need to clone the URI as NS_MutateURI does that for us.
     uri = aURI;
   }
 
-  // hide user:pass unless overridden by pref
-  if (Preferences::GetBool("browser.fixup.hide_user_pass", true)) {
-    Unused << NS_MutateURI(uri)
-                .SetUserPass(EmptyCString())
-                .Finalize(uri);
-  }
+  Unused << NS_MutateURI(uri)
+              .SetUserPass(EmptyCString())
+              .Finalize(uri);
 
   uri.forget(aReturn);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDefaultURIFixup::CreateFixupURI(const nsACString& aStringURI,
                                   uint32_t aFixupFlags,
--- a/dom/base/moz.build
+++ b/dom/base/moz.build
@@ -419,17 +419,17 @@ SOURCES += [
     # Several conflicts with other bindings.
     'DOMIntersectionObserver.cpp',
     # Because of OS X headers.
     'nsContentUtils.cpp',
     # this file doesn't like windows.h
     'nsDOMWindowUtils.cpp',
     # Conflicts with windows.h's definition of SendMessage.
     'nsFrameMessageManager.cpp',
-    # These files have a #error "Never include windows.h in this file!"
+    # These files have a #error "Never include unwrapped windows.h in this file!"
     'nsGlobalWindowInner.cpp',
     'nsGlobalWindowOuter.cpp',
     # Conflicts with windows.h's definition of LoadImage.
     'nsImageLoadingContent.cpp',
     # Because of OS X headers.
     'nsObjectLoadingContent.cpp',
     # nsPluginArray.cpp includes npapi.h indirectly, and that includes a lot of system headers
     'nsPluginArray.cpp',
--- a/dom/base/nsGlobalWindowInner.cpp
+++ b/dom/base/nsGlobalWindowInner.cpp
@@ -7811,18 +7811,19 @@ nsGlobalWindowInner::FireOnNewGlobalObje
 {
   // AutoEntryScript required to invoke debugger hook, which is a
   // Gecko-specific concept at present.
   AutoEntryScript aes(this, "nsGlobalWindowInner report new global");
   JS::Rooted<JSObject*> global(aes.cx(), GetWrapper());
   JS_FireOnNewGlobalObject(aes.cx(), global);
 }
 
-#ifdef _WINDOWS_
-#error "Never include windows.h in this file!"
+#if defined(_WINDOWS_) && !defined(MOZ_WRAPPED_WINDOWS_H)
+#pragma message("wrapper failure reason: " MOZ_WINDOWS_WRAPPER_DISABLED_REASON)
+#error "Never include unwrapped windows.h in this file!"
 #endif
 
 already_AddRefed<Promise>
 nsGlobalWindowInner::CreateImageBitmap(JSContext* aCx,
                                        const ImageBitmapSource& aImage,
                                        ErrorResult& aRv)
 {
   return ImageBitmap::Create(this, aImage, Nothing(), aRv);
--- a/dom/base/nsGlobalWindowOuter.cpp
+++ b/dom/base/nsGlobalWindowOuter.cpp
@@ -7728,18 +7728,19 @@ nsGlobalWindowOuter::ReportLargeAllocSta
 
   nsContentUtils::ReportToConsole(errorFlags,
                                   NS_LITERAL_CSTRING("DOM"),
                                   mDoc,
                                   nsContentUtils::eDOM_PROPERTIES,
                                   message);
 }
 
-#ifdef _WINDOWS_
-#error "Never include windows.h in this file!"
+#if defined(_WINDOWS_) && !defined(MOZ_WRAPPED_WINDOWS_H)
+#pragma message("wrapper failure reason: " MOZ_WINDOWS_WRAPPER_DISABLED_REASON)
+#error "Never include unwrapped windows.h in this file!"
 #endif
 
 // Helper called by methods that move/resize the window,
 // to ensure the presContext (if any) is aware of resolution
 // change that may happen in multi-monitor configuration.
 void
 nsGlobalWindowOuter::CheckForDPIChange()
 {
--- a/dom/base/nsHTMLContentSerializer.cpp
+++ b/dom/base/nsHTMLContentSerializer.cpp
@@ -444,62 +444,43 @@ static const char* const kEntityStrings[
   /* 0 */ nullptr,
   /* 1 */ "&quot;",
   /* 2 */ "&amp;",
   /* 3 */ "&lt;",
   /* 4 */ "&gt;",
   /* 5 */ "&nbsp;"
 };
 
-uint32_t FindNextBasicEntity(const nsAString& aStr,
-                             const uint32_t aLen,
-                             uint32_t aIndex,
-                             const uint8_t* aEntityTable,
-                             const char** aEntity)
-{
-  for (; aIndex < aLen; ++aIndex) {
-    // for each character in this chunk, check if it
-    // needs to be replaced
-    char16_t val = aStr[aIndex];
-    if (val <= kValNBSP && aEntityTable[val]) {
-      *aEntity = kEntityStrings[aEntityTable[val]];
-      return aIndex;
-    }
-  }
-  return aIndex;
-}
-
 bool
 nsHTMLContentSerializer::AppendAndTranslateEntities(const nsAString& aStr,
-                                                     nsAString& aOutputStr)
+                                                    nsAString& aOutputStr)
 {
   if (mBodyOnly && !mInBody) {
     return true;
   }
 
   if (mDisableEntityEncoding) {
     return aOutputStr.Append(aStr, mozilla::fallible);
   }
 
   if (mFlags & (nsIDocumentEncoder::OutputEncodeBasicEntities)) {
-    const uint8_t* entityTable = mInAttribute ? kAttrEntities : kEntities;
-    uint32_t start = 0;
-    const uint32_t len = aStr.Length();
-    for (uint32_t i = 0; i < len; ++i) {
-      const char* entity = nullptr;
-      i = FindNextBasicEntity(aStr, len, i, entityTable, &entity);
-      uint32_t normalTextLen = i - start;
-      if (normalTextLen) {
-        NS_ENSURE_TRUE(aOutputStr.Append(Substring(aStr, start, normalTextLen),
-                                         mozilla::fallible), false);
-      }
-      if (entity) {
-        NS_ENSURE_TRUE(aOutputStr.AppendASCII(entity, mozilla::fallible), false);
-        start = i + 1;
-      }
+    // Per the API documentation, encode &nbsp;, &amp;, &lt;, &gt;, and &quot;
+    if (mInAttribute) {
+      return nsXMLContentSerializer::AppendAndTranslateEntities<kValNBSP>(
+        aStr, aOutputStr, kAttrEntities, kEntityStrings);
     }
-    return true;
-  } else {
-    NS_ENSURE_TRUE(nsXMLContentSerializer::AppendAndTranslateEntities(aStr, aOutputStr), false);
+
+    return nsXMLContentSerializer::AppendAndTranslateEntities<kValNBSP>(
+      aStr, aOutputStr, kEntities, kEntityStrings);
   }
 
-  return true;
+  // We don't want to call into our superclass 2-arg version of
+  // AppendAndTranslateEntities, because it wants to encode more characters
+  // than we do.  Use our tables, but avoid encoding &nbsp; by passing in a
+  // smaller max index.  This will only encode &amp;, &lt;, &gt;, and &quot;.
+  if (mInAttribute) {
+    return nsXMLContentSerializer::AppendAndTranslateEntities<kGTVal>(
+      aStr, aOutputStr, kAttrEntities, kEntityStrings);
+  }
+
+  return nsXMLContentSerializer::AppendAndTranslateEntities<kGTVal>(
+    aStr, aOutputStr, kEntities, kEntityStrings);
 }
--- a/dom/base/nsHTMLContentSerializer.h
+++ b/dom/base/nsHTMLContentSerializer.h
@@ -42,15 +42,14 @@ class nsHTMLContentSerializer final : pu
                                        const nsAString& aTagNamespaceURI,
                                        nsAtom* aTagName,
                                        int32_t aNamespace,
                                        nsAString& aStr);
 
   MOZ_MUST_USE
   virtual bool AppendAndTranslateEntities(const nsAString& aStr,
                                           nsAString& aOutputStr) override;
-
 };
 
 nsresult
 NS_NewHTMLContentSerializer(nsIContentSerializer** aSerializer);
 
 #endif
--- a/dom/base/nsXMLContentSerializer.cpp
+++ b/dom/base/nsXMLContentSerializer.cpp
@@ -1170,18 +1170,16 @@ nsXMLContentSerializer::AppendToString(c
   if (mBodyOnly && !mInBody) {
     return true;
   }
   mColPos += aStr.Length();
   return aOutputStr.Append(aStr, mozilla::fallible);
 }
 
 
-static const uint16_t kGTVal = 62;
-
 #define _ 0
 
 // This table indexes into kEntityStrings[].
 static const uint8_t kEntities[] = {
   // clang-format off
   _, _, _, _, _, _, _, _, _, _,
   _, _, _, _, _, _, _, _, _, _,
   _, _, _, _, _, _, _, _, _, _,
@@ -1217,41 +1215,55 @@ static const char* const kEntityStrings[
   /* 6 */ "&#xA;",
   /* 7 */ "&#xD;",
 };
 
 bool
 nsXMLContentSerializer::AppendAndTranslateEntities(const nsAString& aStr,
                                                    nsAString& aOutputStr)
 {
+  if (mInAttribute) {
+    return AppendAndTranslateEntities<kGTVal>(aStr, aOutputStr, kAttrEntities,
+                                              kEntityStrings);
+  }
+
+  return AppendAndTranslateEntities<kGTVal>(aStr, aOutputStr, kEntities, kEntityStrings);
+}
+
+/* static */
+bool
+nsXMLContentSerializer::AppendAndTranslateEntities(const nsAString& aStr,
+                                                   nsAString& aOutputStr,
+                                                   const uint8_t aEntityTable[],
+                                                   uint16_t aMaxTableIndex,
+                                                   const char* const aStringTable[])
+{
   nsReadingIterator<char16_t> done_reading;
   aStr.EndReading(done_reading);
 
   // for each chunk of |aString|...
   uint32_t advanceLength = 0;
   nsReadingIterator<char16_t> iter;
 
-  const uint8_t* entityTable = mInAttribute ? kAttrEntities : kEntities;
-
   for (aStr.BeginReading(iter);
        iter != done_reading;
        iter.advance(int32_t(advanceLength))) {
     uint32_t fragmentLength = done_reading - iter;
     const char16_t* c = iter.get();
     const char16_t* fragmentStart = c;
     const char16_t* fragmentEnd = c + fragmentLength;
     const char* entityText = nullptr;
 
     advanceLength = 0;
     // for each character in this chunk, check if it
     // needs to be replaced
     for (; c < fragmentEnd; c++, advanceLength++) {
       char16_t val = *c;
-      if ((val <= kGTVal) && entityTable[val]) {
-        entityText = kEntityStrings[entityTable[val]];
+      if ((val <= aMaxTableIndex) && aEntityTable[val]) {
+        entityText = aStringTable[aEntityTable[val]];
         break;
       }
     }
 
     NS_ENSURE_TRUE(aOutputStr.Append(fragmentStart, advanceLength, mozilla::fallible), false);
     if (entityText) {
       NS_ENSURE_TRUE(AppendASCIItoUTF16(mozilla::MakeStringSpan(entityText),
                                         aOutputStr,
--- a/dom/base/nsXMLContentSerializer.h
+++ b/dom/base/nsXMLContentSerializer.h
@@ -164,16 +164,65 @@ class nsXMLContentSerializer : public ns
    * Appends a string by translating entities
    * It doesn't increment the column position
    */
   MOZ_MUST_USE
   virtual bool AppendAndTranslateEntities(const nsAString& aStr,
                                           nsAString& aOutputStr);
 
   /**
+   * Helper for virtual AppendAndTranslateEntities that does the actualy work.
+   *
+   * Do not call this directly.  Call it via the template helper below.
+   */
+private:
+  MOZ_MUST_USE
+  static bool AppendAndTranslateEntities(const nsAString& aStr,
+                                         nsAString& aOutputStr,
+                                         const uint8_t aEntityTable[],
+                                         uint16_t aMaxTableIndex,
+                                         const char* const aStringTable[]);
+
+protected:
+  /**
+   * Helper for calling AppendAndTranslateEntities in a way that guarantees we
+   * don't mess up our aEntityTable sizing.  This is a bit more complicated than
+   * it could be, becaue sometimes we don't want to use all of aEntityTable, so
+   * we have to allow passing the amount to use independently.  But we can
+   * statically ensure it's not too big.
+   *
+   * The first integer template argument, which callers need to specify
+   * explicitly, is the index of the last entry in aEntityTable that should be
+   * considered for encoding as an entity reference.  The second integer
+   * argument will be deduced from the actual table passed in.
+   *
+   * aEntityTable contains as values indices into aStringTable.  Those represent
+   * the strings that should be used to replace the characters that are used to
+   * index into aEntityTable.  aStringTable[0] should be nullptr, and characters
+   * that do not need replacement should map to 0 in aEntityTable.
+   */
+  template<uint16_t LargestIndex, uint16_t TableLength>
+  MOZ_MUST_USE
+  bool AppendAndTranslateEntities(const nsAString& aStr,
+                                  nsAString& aOutputStr,
+                                  const uint8_t (&aEntityTable)[TableLength],
+                                  const char* const aStringTable[])
+  {
+    static_assert(LargestIndex < TableLength,
+                  "Largest allowed index must be smaller than table length");
+    return AppendAndTranslateEntities(aStr, aOutputStr, aEntityTable,
+                                      LargestIndex, aStringTable);
+  }
+
+  /**
+   * Max index that can be used with some of our entity tables.
+   */
+  static const uint16_t kGTVal = 62;
+
+  /**
    * retrieve the text content of the node and append it to the given string
    * It doesn't increment the column position
    */
   nsresult AppendTextData(nsIContent* aNode,
                           int32_t aStartOffset,
                           int32_t aEndOffset,
                           nsAString& aStr,
                           bool aTranslateEntities);
new file mode 100644
--- /dev/null
+++ b/dom/base/test/unit/test_serializers_entities.js
@@ -0,0 +1,85 @@
+const encoders = {
+    xml: (doc) => {
+	let enc = Cu.createDocumentEncoder("text/xml");
+	enc.init(doc, "text/xml", Ci.nsIDocumentEncoder.OutputLFLineBreak);
+	return enc;
+    },
+    html: (doc) => {
+	let enc = Cu.createDocumentEncoder("text/html");
+	enc.init(doc, "text/html", Ci.nsIDocumentEncoder.OutputLFLineBreak);
+	return enc;
+    },
+    htmlBasic: (doc) => {
+	let enc = Cu.createDocumentEncoder("text/html");
+	enc.init(doc, "text/html",
+		 Ci.nsIDocumentEncoder.OutputEncodeBasicEntities |
+		 Ci.nsIDocumentEncoder.OutputLFLineBreak);
+	return enc;
+    },
+    xhtml: (doc) => {
+	let enc = Cu.createDocumentEncoder("application/xhtml+xml");
+	enc.init(doc, "application/xhtml+xml",
+		 Ci.nsIDocumentEncoder.OutputLFLineBreak);
+	return enc;
+    },
+};
+
+// Which characters should we encode as entities?  It depends on the serializer.
+const encodeAll = { html: true, htmlBasic: true, xhtml: true, xml: true };
+const encodeHTMLBasic = { html: false, htmlBasic: true, xhtml: false, xml: false };
+const encodeXML = { html: false, htmlBasic: false, xhtml: true, xml: true };
+const encodeNone = { html: false, htmlBasic: false, xhtml: false, xml: false };
+const encodingInfoMap = new Map([
+    // Basic sanity chars '<', '>', '"', '&' get encoded in all cases.
+    [ '<',  encodeAll ],
+    [ '>',  encodeAll ],
+    [ '&',  encodeAll ],
+    // nbsp is only encoded with the HTML encoder when encoding basic entities.
+    [ '\xA0', encodeHTMLBasic ],
+]);
+
+const encodingMap = new Map([
+    [ '<', '&lt;' ],
+    [ '>', '&gt;' ],
+    [ '&', '&amp;' ],
+    // nbsp is only encoded with the HTML encoder when encoding basic entities.
+    [ '\xA0', '&nbsp;' ],
+]);
+
+function encodingInfoForChar(c) {
+    var info = encodingInfoMap.get(c);
+    if (info) {
+	return info;
+    }
+    return encodeNone;
+}
+
+function encodingForChar(c, type) {
+    var info = encodingInfoForChar(c);
+    if (!info[type]) {
+	return c;
+    }
+    return encodingMap.get(c);
+}
+
+const doc = new DOMParser().parseFromString("<root></root>", "text/xml")
+const root = doc.documentElement;
+for (let i = 0; i < 255; ++i) {
+    let el = doc.createElement("span");
+    el.textContent = String.fromCharCode(i);
+    root.appendChild(el);
+}
+for (let type of ["xml", "xhtml", "htmlBasic", "html"]) {
+    let str = encoders[type](doc).encodeToString();
+    const prefix = "<root><span>";
+    const suffix = "</span></root>";
+    Assert.ok(str.startsWith(prefix), `${type} serialization starts correctly`);
+    Assert.ok(str.endsWith(suffix), `${type} serialization ends correctly`);
+    str = str.substring(prefix.length, str.length - suffix.length);
+    let encodings = str.split("</span><span>");
+    for (let i = 0; i < 255; ++i) {
+	let c = String.fromCharCode(i);
+	Assert.equal(encodingForChar(c, type), encodings[i],
+		     `${type} encoding of char ${i} is correct`);
+    }
+}
new file mode 100644
--- /dev/null
+++ b/dom/base/test/unit/test_serializers_entities_in_attr.js
@@ -0,0 +1,94 @@
+const encoders = {
+    xml: (doc) => {
+	let enc = Cu.createDocumentEncoder("text/xml");
+	enc.init(doc, "text/xml", Ci.nsIDocumentEncoder.OutputLFLineBreak);
+	return enc;
+    },
+    html: (doc) => {
+	let enc = Cu.createDocumentEncoder("text/html");
+	enc.init(doc, "text/html", Ci.nsIDocumentEncoder.OutputLFLineBreak);
+	return enc;
+    },
+    htmlBasic: (doc) => {
+	let enc = Cu.createDocumentEncoder("text/html");
+	enc.init(doc, "text/html",
+		 Ci.nsIDocumentEncoder.OutputEncodeBasicEntities |
+		 Ci.nsIDocumentEncoder.OutputLFLineBreak);
+	return enc;
+    },
+    xhtml: (doc) => {
+	let enc = Cu.createDocumentEncoder("application/xhtml+xml");
+	enc.init(doc, "application/xhtml+xml",
+		 Ci.nsIDocumentEncoder.OutputLFLineBreak);
+	return enc;
+    },
+};
+
+// Which characters should we encode as entities?  It depends on the serializer.
+const encodeAll = { html: true, htmlBasic: true, xhtml: true, xml: true };
+const encodeHTMLBasic = { html: false, htmlBasic: true, xhtml: false, xml: false };
+const encodeXML = { html: false, htmlBasic: false, xhtml: true, xml: true };
+const encodeNone = { html: false, htmlBasic: false, xhtml: false, xml: false };
+const encodingInfoMap = new Map([
+    // Basic sanity chars '<', '>', '"', '&' get encoded in all cases.
+    [ '<',  encodeAll ],
+    [ '>',  encodeAll ],
+    [ '"',  encodeAll ],
+    [ '&',  encodeAll ],
+    // nbsp is only encoded with the HTML encoder when encoding basic entities.
+    [ '\xA0', encodeHTMLBasic ],
+    // Whitespace bits are only encoded in XML.
+    [ '\n', encodeXML ],
+    [ '\r', encodeXML ],
+    [ '\t', encodeXML ],
+]);
+
+const encodingMap = new Map([
+    [ '<', '&lt;' ],
+    [ '>', '&gt;' ],
+    [ '"', '&quot;' ],
+    [ '&', '&amp;' ],
+    [ '\xA0', '&nbsp;' ],
+    [ '\n', '&#xA;' ],
+    [ '\r', '&#xD;' ],
+    [ '\t', '&#9;' ],
+]);
+
+function encodingInfoForChar(c) {
+    var info = encodingInfoMap.get(c);
+    if (info) {
+	return info;
+    }
+    return encodeNone;
+}
+
+function encodingForChar(c, type) {
+    var info = encodingInfoForChar(c);
+    if (!info[type]) {
+	return c;
+    }
+    return encodingMap.get(c);
+}
+
+const doc = new DOMParser().parseFromString("<root></root>", "text/xml")
+const root = doc.documentElement;
+for (let i = 0; i < 255; ++i) {
+    let el = doc.createElement("span");
+    el.setAttribute("x", String.fromCharCode(i));
+    el.textContent = " ";
+    root.appendChild(el);
+}
+for (let type of ["xml", "xhtml", "htmlBasic", "html"]) {
+    let str = encoders[type](doc).encodeToString();
+    const prefix = '<root><span x="';
+    const suffix = '"> </span></root>';
+    Assert.ok(str.startsWith(prefix), `${type} serialization starts correctly`);
+    Assert.ok(str.endsWith(suffix), `${type} serialization ends correctly`);
+    str = str.substring(prefix.length, str.length - suffix.length);
+    let encodings = str.split('"> </span><span x="');
+    for (let i = 0; i < 255; ++i) {
+	let c = String.fromCharCode(i);
+	Assert.equal(encodingForChar(c, type), encodings[i],
+		     `${type} encoding of char ${i} is correct`);
+    }
+}
--- a/dom/base/test/unit/xpcshell.ini
+++ b/dom/base/test/unit/xpcshell.ini
@@ -33,16 +33,18 @@ skip-if = os == 'mac'
 [test_isequalnode.js]
 head = head_xml.js
 [test_nodelist.js]
 head = head_xml.js
 [test_normalize.js]
 head = head_xml.js
 [test_range.js]
 head = head_xml.js
+[test_serializers_entities.js]
+[test_serializers_entities_in_attr.js]
 [test_structuredcloneholder.js]
 [test_thirdpartyutil.js]
 [test_treewalker.js]
 head = head_xml.js
 [test_xhr_document.js]
 [test_xhr_standalone.js]
 [test_xhr_origin_attributes.js]
 [test_xml_parser.js]
--- a/dom/interfaces/payments/nsIPaymentActionResponse.idl
+++ b/dom/interfaces/payments/nsIPaymentActionResponse.idl
@@ -287,16 +287,87 @@ interface nsIPaymentCompleteActionRespon
             in uint32_t aCompleteStatus);
 
   /**
    *  Check if the complete action is succeeded.
    */
   bool isCompleted();
 };
 
+[builtinclass, scriptable, uuid(2035e0a9-c9ab-4c9f-b8e9-28b2ed61548c)]
+interface nsIMethodChangeDetails : nsISupports
+{
+  /**
+   *  The consts for representing the method change details data type.
+   *  GENERAL_DETAILS is the general purpose details data type. Except basic
+   *  card details, all details should belong to this type.
+   *  BASICCARD_DETAILS is a special details data type for basic card change
+   *  details.
+   */
+  const uint32_t GENERAL_DETAILS = 0;
+  const uint32_t BASICCARD_DETAILS = 1;
+
+  /**
+   *  The method change details data type.
+   *  Using the above defined consts(GENERAL_DETAILS or BASICCARD_DETAILS).
+   */
+  readonly attribute uint32_t type;
+
+  /**
+   *  The initial method.
+   *  @param aType - the method change details data type.
+   */
+  void init(in uint32_t aType);
+};
+
+/**
+ * The general purpose method change details.
+ */
+[builtinclass, scriptable, uuid(e031267e-bec8-4f3c-b0b1-396b77ca260c)]
+interface nsIGeneralChangeDetails : nsIMethodChangeDetails
+{
+  /**
+   *  The stringified change details.
+   */
+  readonly attribute AString details;
+
+  /**
+   *  The initial method for nsIGeneralChangeDetails.
+   *  @param aData - the javascript object of the content.
+   */
+  [implicit_jscontext]
+  void initData(in jsval aDetails);
+};
+
+/**
+ *  The basic card change details.
+ *  Since PaymentAddress is an no constructor interface type, UI code can not
+ *  easy create PaymentAddress by calling new PaymentAddress().
+ *  Unfortunately, BasicCardResponse has a PaymentAddress attribute, billingAddress
+ *  , it means UI can not create BsaicCardChangeDetails by calling the init() with a
+ *  given JSObject directly, because PaymentAddress creation in JS code is hard.
+ *  To let UI code can create BasicCardResponse easier, nsIBasicCardResponse is
+ *  provided for UI by passing the raw data of BasicCardResponse,
+ */
+[builtinclass, scriptable, uuid(5296f79e-15ea-40c3-8196-19cfa64d328c)]
+interface nsIBasicCardChangeDetails : nsIMethodChangeDetails
+{
+  /**
+   *  The billing address.
+   */
+  readonly attribute nsIPaymentAddress billingAddress;
+
+  /**
+   *  The initial method for nsIBasicCardChangeDetails.
+   *  @param aBillingAddreess  - the billing address.
+   */
+  void initData(in nsIPaymentAddress billingAddress);
+};
+
+
 %{C++
 #define NS_GENERAL_RESPONSE_DATA_CID \
   { 0xb986773e, 0x2b30, 0x4ed2, { 0xb8, 0xfe, 0x6a, 0x96, 0x63, 0x1c, 0x80, 0x00 } }
 #define NS_GENERAL_RESPONSE_DATA_CONTRACT_ID \
   "@mozilla.org/dom/payments/general-response-data;1"
 
 #define NS_BASICCARD_RESPONSE_DATA_CID \
   { 0x0d55a5e6, 0xd185, 0x44f0, { 0xb9, 0x92, 0xa8, 0xe1, 0x32, 0x1e, 0x4b, 0xce } }
@@ -317,9 +388,19 @@ interface nsIPaymentCompleteActionRespon
   { 0x8c72bcdb, 0x0c37, 0x4786, { 0xa9, 0xe5, 0x51, 0x0a, 0xfa, 0x2f, 0x8e, 0xde } }
 #define NS_PAYMENT_ABORT_ACTION_RESPONSE_CONTRACT_ID \
   "@mozilla.org/dom/payments/payment-abort-action-response;1"
 
 #define NS_PAYMENT_COMPLETE_ACTION_RESPONSE_CID \
   { 0x62c01e69, 0x9ca4, 0x4060, { 0x99, 0xe4, 0xb9, 0x5f, 0x62, 0x8c, 0x8e, 0x6d } }
 #define NS_PAYMENT_COMPLETE_ACTION_RESPONSE_CONTRACT_ID \
   "@mozilla.org/dom/payments/payment-complete-action-response;1"
+
+#define NS_GENERAL_CHANGE_DETAILS_CID \
+  { 0xe031267e, 0xbec8, 0x4f3c, { 0xb0, 0xb1, 0x39, 0x6b, 0x77, 0xca, 0x26, 0x0c } }
+#define NS_GENERAL_CHANGE_DETAILS_CONTRACT_ID \
+  "@mozilla.org/dom/payments/general-change-details;1"
+
+#define NS_BASICCARD_CHANGE_DETAILS_CID \
+  { 0x5296f79e, 0x15ea, 0x40c3, { 0x81, 0x96, 0x19, 0xcf, 0xa6, 0x4d, 0x32, 0x8c } }
+#define NS_BASICCARD_CHANGE_DETAILS_CONTRACT_ID \
+  "@mozilla.org/dom/payments/basiccard-change-details;1"
 %}
--- a/dom/interfaces/payments/nsIPaymentRequest.idl
+++ b/dom/interfaces/payments/nsIPaymentRequest.idl
@@ -70,16 +70,17 @@ interface nsIPaymentDetails : nsISupport
 
 [scriptable, builtinclass, uuid(d53f9f20-138e-47cc-9fd5-db16a3f6d301)]
 interface nsIPaymentOptions : nsISupports
 {
   readonly attribute boolean requestPayerName;
   readonly attribute boolean requestPayerEmail;
   readonly attribute boolean requestPayerPhone;
   readonly attribute boolean requestShipping;
+  readonly attribute boolean requestBillingAddress;
   readonly attribute AString shippingType;
 };
 
 [scriptable, builtinclass, uuid(2fa36783-d684-4487-b7a8-9def6ae3128f)]
 interface nsIPaymentRequest : nsISupports
 {
   readonly attribute uint64_t topOuterWindowId;
   readonly attribute nsIPrincipal topLevelPrincipal;
--- a/dom/interfaces/payments/nsIPaymentRequestService.idl
+++ b/dom/interfaces/payments/nsIPaymentRequestService.idl
@@ -62,16 +62,27 @@ interface nsIPaymentRequestService : nsI
    *  @param aPayerPhone - the changed payer's phone.
    */
   void changePayerDetail(in AString requestId,
                          in AString aPayerName,
                          in AString aPayerEmail,
                          in AString aPayerPhone);
 
   /**
+   *  Inform the merchant the payment method has changed.
+   *  @param requestId - the request identifier of the payment request.
+   *  @param aMethodName - the changed payment method's name.
+   *  @param aMethodDetails - the changed payment method's details.
+   */
+  void changePaymentMethod(in AString requestId,
+                           in AString aMethodName,
+                           in nsIMethodChangeDetails aMethodDetails);
+
+
+  /**
    *  Following APIs are for testing or platform code only. UI implementation
    *  should not use them.
    */
   /**
    *  Clean up the all managed payment requests.
    *  This API is for testing only.
    */
   void cleanup();
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -268,114 +268,123 @@ using mozilla::loader::PScriptCacheChild
 
 namespace mozilla {
 
 namespace dom {
 
 // IPC sender for remote GC/CC logging.
 class CycleCollectWithLogsChild final
   : public PCycleCollectWithLogsChild
-  , public nsICycleCollectorLogSink
 {
 public:
-  NS_DECL_ISUPPORTS
-
-  CycleCollectWithLogsChild(const FileDescriptor& aGCLog,
-                            const FileDescriptor& aCCLog)
-  {
-    mGCLog = FileDescriptorToFILE(aGCLog, "w");
-    mCCLog = FileDescriptorToFILE(aCCLog, "w");
-  }
-
-  NS_IMETHOD Open(FILE** aGCLog, FILE** aCCLog) override
-  {
-    if (NS_WARN_IF(!mGCLog) || NS_WARN_IF(!mCCLog)) {
-      return NS_ERROR_FAILURE;
-    }
-    *aGCLog = mGCLog;
-    *aCCLog = mCCLog;
-    return NS_OK;
-  }
-
-  NS_IMETHOD CloseGCLog() override
-  {
-    MOZ_ASSERT(mGCLog);
-    fclose(mGCLog);
-    mGCLog = nullptr;
-    SendCloseGCLog();
-    return NS_OK;
-  }
-
-  NS_IMETHOD CloseCCLog() override
+  NS_INLINE_DECL_REFCOUNTING(CycleCollectWithLogsChild)
+
+  class Sink final : public nsICycleCollectorLogSink
   {
-    MOZ_ASSERT(mCCLog);
-    fclose(mCCLog);
-    mCCLog = nullptr;
-    SendCloseCCLog();
-    return NS_OK;
-  }
-
-  NS_IMETHOD GetFilenameIdentifier(nsAString& aIdentifier) override
-  {
-    return UnimplementedProperty();
-  }
-
-  NS_IMETHOD SetFilenameIdentifier(const nsAString& aIdentifier) override
-  {
-    return UnimplementedProperty();
-  }
-
-  NS_IMETHOD GetProcessIdentifier(int32_t *aIdentifier) override
-  {
-    return UnimplementedProperty();
-  }
-
-  NS_IMETHOD SetProcessIdentifier(int32_t aIdentifier) override
-  {
-    return UnimplementedProperty();
-  }
-
-  NS_IMETHOD GetGcLog(nsIFile** aPath) override
-  {
-    return UnimplementedProperty();
-  }
-
-  NS_IMETHOD GetCcLog(nsIFile** aPath) override
-  {
-    return UnimplementedProperty();
-  }
+    NS_DECL_ISUPPORTS
+
+    Sink(CycleCollectWithLogsChild* aActor,
+         const FileDescriptor& aGCLog,
+         const FileDescriptor& aCCLog)
+    {
+      mActor = aActor;
+      mGCLog = FileDescriptorToFILE(aGCLog, "w");
+      mCCLog = FileDescriptorToFILE(aCCLog, "w");
+    }
+
+    NS_IMETHOD Open(FILE** aGCLog, FILE** aCCLog) override
+    {
+      if (NS_WARN_IF(!mGCLog) || NS_WARN_IF(!mCCLog)) {
+        return NS_ERROR_FAILURE;
+      }
+      *aGCLog = mGCLog;
+      *aCCLog = mCCLog;
+      return NS_OK;
+    }
+
+    NS_IMETHOD CloseGCLog() override
+    {
+      MOZ_ASSERT(mGCLog);
+      fclose(mGCLog);
+      mGCLog = nullptr;
+      mActor->SendCloseGCLog();
+      return NS_OK;
+    }
+
+    NS_IMETHOD CloseCCLog() override
+    {
+      MOZ_ASSERT(mCCLog);
+      fclose(mCCLog);
+      mCCLog = nullptr;
+      mActor->SendCloseCCLog();
+      return NS_OK;
+    }
+
+    NS_IMETHOD GetFilenameIdentifier(nsAString& aIdentifier) override
+    {
+      return UnimplementedProperty();
+    }
+
+    NS_IMETHOD SetFilenameIdentifier(const nsAString& aIdentifier) override
+    {
+      return UnimplementedProperty();
+    }
+
+    NS_IMETHOD GetProcessIdentifier(int32_t *aIdentifier) override
+    {
+      return UnimplementedProperty();
+    }
+
+    NS_IMETHOD SetProcessIdentifier(int32_t aIdentifier) override
+    {
+      return UnimplementedProperty();
+    }
+
+    NS_IMETHOD GetGcLog(nsIFile** aPath) override
+    {
+      return UnimplementedProperty();
+    }
+
+    NS_IMETHOD GetCcLog(nsIFile** aPath) override
+    {
+      return UnimplementedProperty();
+    }
+
+  private:
+    ~Sink()
+    {
+      if (mGCLog) {
+        fclose(mGCLog);
+        mGCLog = nullptr;
+      }
+      if (mCCLog) {
+        fclose(mCCLog);
+        mCCLog = nullptr;
+      }
+      // The XPCOM refcount drives the IPC lifecycle;
+      Unused << mActor->Send__delete__(mActor);
+    }
+
+    nsresult UnimplementedProperty()
+    {
+      MOZ_ASSERT(false, "This object is a remote GC/CC logger;"
+                        " this property isn't meaningful.");
+      return NS_ERROR_UNEXPECTED;
+    }
+
+    RefPtr<CycleCollectWithLogsChild> mActor;
+    FILE* mGCLog;
+    FILE* mCCLog;
+  };
 
 private:
-  ~CycleCollectWithLogsChild() override
-  {
-    if (mGCLog) {
-      fclose(mGCLog);
-      mGCLog = nullptr;
-    }
-    if (mCCLog) {
-      fclose(mCCLog);
-      mCCLog = nullptr;
-    }
-    // The XPCOM refcount drives the IPC lifecycle; see also
-    // DeallocPCycleCollectWithLogsChild.
-    Unused << Send__delete__(this);
-  }
-
-  nsresult UnimplementedProperty()
-  {
-    MOZ_ASSERT(false, "This object is a remote GC/CC logger;"
-                      " this property isn't meaningful.");
-    return NS_ERROR_UNEXPECTED;
-  }
-
-  FILE* mGCLog;
-  FILE* mCCLog;
+  ~CycleCollectWithLogsChild() {}
 };
 
-NS_IMPL_ISUPPORTS(CycleCollectWithLogsChild, nsICycleCollectorLogSink);
+NS_IMPL_ISUPPORTS(CycleCollectWithLogsChild::Sink, nsICycleCollectorLogSink);
 
 class AlertObserver
 {
 public:
 
   AlertObserver(nsIObserver *aObserver, const nsString& aData)
     : mObserver(aObserver)
     , mData(aData)
@@ -1316,44 +1325,42 @@ ContentChild::RecvRequestMemoryReport(co
   return IPC_OK();
 }
 
 PCycleCollectWithLogsChild*
 ContentChild::AllocPCycleCollectWithLogsChild(const bool& aDumpAllTraces,
                                               const FileDescriptor& aGCLog,
                                               const FileDescriptor& aCCLog)
 {
-  auto* actor = new CycleCollectWithLogsChild(aGCLog, aCCLog);
-  // Return actor with refcount 0, which is safe because it has a non-XPCOM type.
-  return actor;
+  return do_AddRef(new CycleCollectWithLogsChild()).take();
 }
 
 mozilla::ipc::IPCResult
 ContentChild::RecvPCycleCollectWithLogsConstructor(PCycleCollectWithLogsChild* aActor,
                                                    const bool& aDumpAllTraces,
                                                    const FileDescriptor& aGCLog,
                                                    const FileDescriptor& aCCLog)
 {
-  // Take a reference here, where the XPCOM type is regained.
-  RefPtr<CycleCollectWithLogsChild> sink = static_cast<CycleCollectWithLogsChild*>(aActor);
+  // The sink's destructor is called when the last reference goes away, which
+  // will cause the actor to be closed down.
+  auto* actor = static_cast<CycleCollectWithLogsChild*>(aActor);
+  RefPtr<CycleCollectWithLogsChild::Sink> sink =
+    new CycleCollectWithLogsChild::Sink(actor, aGCLog, aCCLog);
+
+  // Invoke the dumper, which will take a reference to the sink.
   nsCOMPtr<nsIMemoryInfoDumper> dumper = do_GetService("@mozilla.org/memory-info-dumper;1");
-
   dumper->DumpGCAndCCLogsToSink(aDumpAllTraces, sink);
-
-  // The actor's destructor is called when the last reference goes away...
   return IPC_OK();
 }
 
 bool
-ContentChild::DeallocPCycleCollectWithLogsChild(PCycleCollectWithLogsChild* /* aActor */)
+ContentChild::DeallocPCycleCollectWithLogsChild(PCycleCollectWithLogsChild* aActor)
 {
-  // ...so when we get here, there's nothing for us to do.
-  //
-  // Also, we're already in ~CycleCollectWithLogsChild (q.v.) at
-  // this point, so we shouldn't touch the actor in any case.
+  RefPtr<CycleCollectWithLogsChild> actor =
+    dont_AddRef(static_cast<CycleCollectWithLogsChild*>(aActor));
   return true;
 }
 
 mozilla::ipc::IPCResult
 ContentChild::RecvInitContentBridgeChild(Endpoint<PContentBridgeChild>&& aEndpoint)
 {
   ContentBridgeChild::Create(std::move(aEndpoint));
   return IPC_OK();
--- a/dom/payments/PaymentActionResponse.cpp
+++ b/dom/payments/PaymentActionResponse.cpp
@@ -384,10 +384,93 @@ PaymentCompleteActionResponse::GetComple
 nsresult
 PaymentCompleteActionResponse::IsCompleted(bool* aIsCompleted)
 {
   NS_ENSURE_ARG_POINTER(aIsCompleted);
   *aIsCompleted = (mCompleteStatus == nsIPaymentActionResponse::COMPLETE_SUCCEEDED);
   return NS_OK;
 }
 
+/* PaymentChangeDetails */
+
+NS_IMPL_ISUPPORTS(MethodChangeDetails, nsIMethodChangeDetails)
+
+NS_IMETHODIMP
+MethodChangeDetails::GetType(uint32_t* aType)
+{
+  NS_ENSURE_ARG_POINTER(aType);
+  *aType = mType;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+MethodChangeDetails::Init(const uint32_t aType)
+{
+  if (aType != nsIMethodChangeDetails::GENERAL_DETAILS &&
+      aType != nsIMethodChangeDetails::BASICCARD_DETAILS) {
+    return NS_ERROR_FAILURE;
+  }
+  mType = aType;
+  return NS_OK;
+}
+
+/* GeneralMethodChangeDetails */
+
+NS_IMPL_ISUPPORTS_INHERITED(GeneralMethodChangeDetails,
+                            MethodChangeDetails,
+                            nsIGeneralChangeDetails)
+
+GeneralMethodChangeDetails::GeneralMethodChangeDetails()
+  : mDetails(NS_LITERAL_STRING("{}"))
+{
+  Init(nsIMethodChangeDetails::GENERAL_DETAILS);
+}
+
+NS_IMETHODIMP
+GeneralMethodChangeDetails::GetDetails(nsAString& aDetails)
+{
+  aDetails = mDetails;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+GeneralMethodChangeDetails::InitData(JS::HandleValue aDetails, JSContext* aCx)
+{
+  if (aDetails.isNullOrUndefined()) {
+    return NS_ERROR_FAILURE;
+  }
+  nsresult rv = SerializeFromJSVal(aCx, aDetails, mDetails);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+  return NS_OK;
+}
+
+/* BasicCardMethodChangeDetails */
+
+NS_IMPL_ISUPPORTS_INHERITED(BasicCardMethodChangeDetails,
+                            MethodChangeDetails,
+                            nsIBasicCardChangeDetails)
+
+BasicCardMethodChangeDetails::BasicCardMethodChangeDetails()
+{
+  Init(nsIMethodChangeDetails::BASICCARD_DETAILS);
+}
+
+NS_IMETHODIMP
+BasicCardMethodChangeDetails::GetBillingAddress(nsIPaymentAddress** aBillingAddress)
+{
+  NS_ENSURE_ARG_POINTER(aBillingAddress);
+  nsCOMPtr<nsIPaymentAddress> address;
+  address = mBillingAddress;
+  address.forget(aBillingAddress);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+BasicCardMethodChangeDetails::InitData(nsIPaymentAddress* aBillingAddress)
+{
+  mBillingAddress = aBillingAddress;
+  return NS_OK;
+}
+
 } // end of namespace dom
 } // end of namespace mozilla
--- a/dom/payments/PaymentActionResponse.h
+++ b/dom/payments/PaymentActionResponse.h
@@ -144,12 +144,58 @@ public:
   PaymentCompleteActionResponse();
 
 private:
   ~PaymentCompleteActionResponse() = default;
 
   uint32_t mCompleteStatus;
 };
 
+class MethodChangeDetails : public nsIMethodChangeDetails
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIMETHODCHANGEDETAILS
+
+  MethodChangeDetails() = default;
+
+protected:
+  virtual ~MethodChangeDetails() = default;
+
+  uint32_t mType;
+};
+
+class GeneralMethodChangeDetails final : public MethodChangeDetails
+                                       , public nsIGeneralChangeDetails
+{
+public:
+  NS_DECL_ISUPPORTS_INHERITED
+  NS_FORWARD_NSIMETHODCHANGEDETAILS(MethodChangeDetails::)
+  NS_DECL_NSIGENERALCHANGEDETAILS
+
+  GeneralMethodChangeDetails();
+
+private:
+  ~GeneralMethodChangeDetails() = default;
+
+  nsString mDetails;
+};
+
+class BasicCardMethodChangeDetails final : public MethodChangeDetails
+                                         , public nsIBasicCardChangeDetails
+{
+public:
+  NS_DECL_ISUPPORTS_INHERITED
+  NS_FORWARD_NSIMETHODCHANGEDETAILS(MethodChangeDetails::)
+  NS_DECL_NSIBASICCARDCHANGEDETAILS
+
+  BasicCardMethodChangeDetails();
+
+private:
+  ~BasicCardMethodChangeDetails() = default;
+
+  nsCOMPtr<nsIPaymentAddress> mBillingAddress;
+};
+
 } // end of dom
 } // end of namespace mozilla
 
 #endif
--- a/dom/payments/PaymentMethodChangeEvent.cpp
+++ b/dom/payments/PaymentMethodChangeEvent.cpp
@@ -1,17 +1,18 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+#include "BasicCardPayment.h"
 #include "mozilla/dom/PaymentMethodChangeEvent.h"
 #include "mozilla/dom/PaymentRequestUpdateEvent.h"
-#include "mozilla/HoldDropJSObjects.h"
+#include "PaymentRequestUtils.h"
 
 namespace mozilla {
 namespace dom {
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(PaymentMethodChangeEvent)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(PaymentMethodChangeEvent,
                                                 PaymentRequestUpdateEvent)
   tmp->mMethodDetails = nullptr;
@@ -32,65 +33,146 @@ NS_IMPL_RELEASE_INHERITED(PaymentMethodC
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PaymentMethodChangeEvent)
 NS_INTERFACE_MAP_END_INHERITING(PaymentRequestUpdateEvent)
 
 already_AddRefed<PaymentMethodChangeEvent>
 PaymentMethodChangeEvent::Constructor(
   mozilla::dom::EventTarget* aOwner,
   const nsAString& aType,
-  const PaymentMethodChangeEventInit& aEventInitDict)
+  const PaymentRequestUpdateEventInit& aEventInitDict,
+  const nsAString& aMethodName,
+  const ChangeDetails& aMethodDetails)
 {
   RefPtr<PaymentMethodChangeEvent> e = new PaymentMethodChangeEvent(aOwner);
   bool trusted = e->Init(aOwner);
   e->InitEvent(aType, aEventInitDict.mBubbles, aEventInitDict.mCancelable);
   e->SetTrusted(trusted);
   e->SetComposed(aEventInitDict.mComposed);
-  e->init(aEventInitDict);
+  e->SetMethodName(aMethodName);
+  e->SetMethodDetails(aMethodDetails);
   return e.forget();
 }
 
 already_AddRefed<PaymentMethodChangeEvent>
 PaymentMethodChangeEvent::Constructor(
   const GlobalObject& aGlobal,
   const nsAString& aType,
   const PaymentMethodChangeEventInit& aEventInitDict,
   ErrorResult& aRv)
 {
   nsCOMPtr<mozilla::dom::EventTarget> owner =
     do_QueryInterface(aGlobal.GetAsSupports());
-  return Constructor(owner, aType, aEventInitDict);
+  RefPtr<PaymentMethodChangeEvent> e = new PaymentMethodChangeEvent(owner);
+  bool trusted = e->Init(owner);
+  e->InitEvent(aType, aEventInitDict.mBubbles, aEventInitDict.mCancelable);
+  e->SetTrusted(trusted);
+  e->SetComposed(aEventInitDict.mComposed);
+  e->init(aEventInitDict);
+  return e.forget();
 }
 
 PaymentMethodChangeEvent::PaymentMethodChangeEvent(EventTarget* aOwner)
   : PaymentRequestUpdateEvent(aOwner)
 {
   MOZ_ASSERT(aOwner);
   mozilla::HoldJSObjects(this);
 }
 
 void
-PaymentMethodChangeEvent::init(
-  const PaymentMethodChangeEventInit& aEventInitDict)
+PaymentMethodChangeEvent::init(const PaymentMethodChangeEventInit& aEventInitDict)
 {
   mMethodName.Assign(aEventInitDict.mMethodName);
   mMethodDetails = aEventInitDict.mMethodDetails;
 }
 
 void
 PaymentMethodChangeEvent::GetMethodName(nsAString& aMethodName)
 {
   aMethodName.Assign(mMethodName);
 }
 
 void
-PaymentMethodChangeEvent::GetMethodDetails(JSContext* cx,
-                                           JS::MutableHandle<JSObject*> retval)
+PaymentMethodChangeEvent::SetMethodName(const nsAString& aMethodName)
+{
+  mMethodName = aMethodName;
+}
+
+void
+PaymentMethodChangeEvent::GetMethodDetails(JSContext* aCx,
+                                           JS::MutableHandle<JSObject*> aRetVal)
 {
-  retval.set(mMethodDetails.get());
+  MOZ_ASSERT(aCx);
+
+  if (mMethodDetails) {
+    aRetVal.set(mMethodDetails.get());
+    return;
+  }
+
+  RefPtr<BasicCardService> service = BasicCardService::GetService();
+  MOZ_ASSERT(service);
+  aRetVal.set(nullptr);
+  switch(mInternalDetails.type()) {
+    case ChangeDetails::GeneralMethodDetails: {
+      const GeneralDetails& rawDetails = mInternalDetails.generalDetails();
+      DeserializeToJSObject(rawDetails.details, aCx, aRetVal);
+      break;
+    }
+    case ChangeDetails::BasicCardMethodDetails: {
+      const BasicCardDetails& rawDetails = mInternalDetails.basicCardDetails();
+      BasicCardChangeDetails basicCardDetails;
+      PaymentOptions options;
+      mRequest->GetOptions(options);
+      if (options.mRequestBillingAddress) {
+        if (!rawDetails.billingAddress.country.IsEmpty() ||
+            !rawDetails.billingAddress.addressLine.IsEmpty() ||
+            !rawDetails.billingAddress.region.IsEmpty() ||
+            !rawDetails.billingAddress.regionCode.IsEmpty() ||
+            !rawDetails.billingAddress.city.IsEmpty() ||
+            !rawDetails.billingAddress.dependentLocality.IsEmpty() ||
+            !rawDetails.billingAddress.postalCode.IsEmpty() ||
+            !rawDetails.billingAddress.sortingCode.IsEmpty() ||
+            !rawDetails.billingAddress.organization.IsEmpty() ||
+            !rawDetails.billingAddress.recipient.IsEmpty() ||
+            !rawDetails.billingAddress.phone.IsEmpty()) {
+          nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(GetParentObject());
+          basicCardDetails.mBillingAddress.Construct();
+          basicCardDetails.mBillingAddress.Value() =
+            new PaymentAddress(window,
+                               rawDetails.billingAddress.country,
+                               rawDetails.billingAddress.addressLine,
+                               rawDetails.billingAddress.region,
+                               rawDetails.billingAddress.regionCode,
+                               rawDetails.billingAddress.city,
+                               rawDetails.billingAddress.dependentLocality,
+                               rawDetails.billingAddress.postalCode,
+                               rawDetails.billingAddress.sortingCode,
+                               rawDetails.billingAddress.organization,
+                               rawDetails.billingAddress.recipient,
+                               rawDetails.billingAddress.phone);
+        }
+      }
+      MOZ_ASSERT(aCx);
+      JS::RootedValue value(aCx);
+      if (NS_WARN_IF(!basicCardDetails.ToObjectInternal(aCx, &value))) {
+        return;
+      }
+      aRetVal.set(&value.toObject());
+      break;
+    }
+    default: {
+      break;
+    }
+  }
+}
+
+void
+PaymentMethodChangeEvent::SetMethodDetails(const ChangeDetails& aMethodDetails)
+{
+  mInternalDetails = aMethodDetails;
 }
 
 PaymentMethodChangeEvent::~PaymentMethodChangeEvent()
 {
   mozilla::DropJSObjects(this);
 }
 
 JSObject*
--- a/dom/payments/PaymentMethodChangeEvent.h
+++ b/dom/payments/PaymentMethodChangeEvent.h
@@ -27,34 +27,39 @@ public:
 
   virtual JSObject* WrapObjectInternal(
     JSContext* aCx,
     JS::Handle<JSObject*> aGivenProto) override;
 
   static already_AddRefed<PaymentMethodChangeEvent> Constructor(
     EventTarget* aOwner,
     const nsAString& aType,
-    const PaymentMethodChangeEventInit& aEventInitDict);
+    const PaymentRequestUpdateEventInit& aEventInitDict,
+    const nsAString& aMethodName,
+    const ChangeDetails& aMethodDetails);
 
   // Called by WebIDL constructor
   static already_AddRefed<PaymentMethodChangeEvent> Constructor(
     const GlobalObject& aGlobal,
     const nsAString& aType,
     const PaymentMethodChangeEventInit& aEventInitDict,
     ErrorResult& aRv);
 
   void GetMethodName(nsAString& aMethodName);
+  void SetMethodName(const nsAString& aMethodName);
 
   void GetMethodDetails(JSContext* cx, JS::MutableHandle<JSObject*> retval);
+  void SetMethodDetails(const ChangeDetails& aMethodDetails);
 
 protected:
   void init(const PaymentMethodChangeEventInit& aEventInitDict);
   ~PaymentMethodChangeEvent();
 
 private:
   JS::Heap<JSObject*> mMethodDetails;
+  ChangeDetails mInternalDetails;
   nsString mMethodName;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_PaymentMethodChangeEvent_h
--- a/dom/payments/PaymentRequest.cpp
+++ b/dom/payments/PaymentRequest.cpp
@@ -1037,16 +1037,40 @@ PaymentRequest::DispatchMerchantValidati
   }
   event->SetTrusted(true);
   event->SetRequest(this);
 
   DispatchEvent(*event, rv);
   return rv.StealNSResult();
 }
 
+nsresult
+PaymentRequest::DispatchPaymentMethodChangeEvent(const nsAString& aMethodName,
+                                                 const ChangeDetails& aMethodDetails)
+{
+  MOZ_ASSERT(ReadyForUpdate());
+
+  PaymentRequestUpdateEventInit init;
+  init.mBubbles = false;
+  init.mCancelable = false;
+
+  RefPtr<PaymentMethodChangeEvent> event =
+    PaymentMethodChangeEvent::Constructor(this,
+                                          NS_LITERAL_STRING("paymentmethodchange"),
+                                          init,
+                                          aMethodName,
+                                          aMethodDetails);
+  event->SetTrusted(true);
+  event->SetRequest(this);
+
+  ErrorResult rv;
+  DispatchEvent(*event, rv);
+  return rv.StealNSResult();
+}
+
 already_AddRefed<PaymentAddress>
 PaymentRequest::GetShippingAddress() const
 {
   RefPtr<PaymentAddress> address = mShippingAddress;
   return address.forget();
 }
 
 nsresult
@@ -1093,16 +1117,23 @@ nsresult
 PaymentRequest::UpdateShippingOption(const nsAString& aShippingOption)
 {
   mShippingOption = aShippingOption;
 
   // Fire shippingaddresschange event
   return DispatchUpdateEvent(NS_LITERAL_STRING("shippingoptionchange"));
 }
 
+nsresult
+PaymentRequest::UpdatePaymentMethod(const nsAString& aMethodName,
+                                    const ChangeDetails& aMethodDetails)
+{
+  return DispatchPaymentMethodChangeEvent(aMethodName, aMethodDetails);
+}
+
 void
 PaymentRequest::SetShippingType(const Nullable<PaymentShippingType>& aShippingType)
 {
   mShippingType = aShippingType;
 }
 
 Nullable<PaymentShippingType>
 PaymentRequest::GetShippingType() const
--- a/dom/payments/PaymentRequest.h
+++ b/dom/payments/PaymentRequest.h
@@ -14,22 +14,93 @@
 #include "mozilla/ErrorResult.h"
 #include "nsIDocumentActivity.h"
 #include "nsWrapperCache.h"
 #include "PaymentRequestUpdateEvent.h"
 
 namespace mozilla {
 namespace dom {
 
-class EventHandlerNonNull;
 class PaymentAddress;
 class PaymentRequestChild;
 class PaymentResponse;
 class ResponseData;
 
+class GeneralDetails final
+{
+public:
+  GeneralDetails() = default;
+  ~GeneralDetails() = default;
+  nsString details;
+};
+
+class BasicCardDetails final
+{
+public:
+  struct Address {
+    nsString country;
+    nsTArray<nsString> addressLine;
+    nsString region;
+    nsString regionCode;
+    nsString city;
+    nsString dependentLocality;
+    nsString postalCode;
+    nsString sortingCode;
+    nsString organization;
+    nsString recipient;
+    nsString phone;
+  };
+  BasicCardDetails() = default;
+  ~BasicCardDetails() = default;
+
+  Address billingAddress;
+};
+
+class ChangeDetails final
+{
+public:
+  enum Type {
+    Unknown = 0,
+    GeneralMethodDetails = 1,
+    BasicCardMethodDetails
+  };
+  ChangeDetails()
+    : mType(ChangeDetails::Unknown)
+  {}
+  explicit ChangeDetails(const GeneralDetails& aGeneralDetails)
+    : mType(GeneralMethodDetails)
+    , mGeneralDetails(aGeneralDetails)
+  {}
+  explicit ChangeDetails(const BasicCardDetails& aBasicCardDetails)
+    : mType(BasicCardMethodDetails)
+    , mBasicCardDetails(aBasicCardDetails)
+  {}
+  ChangeDetails& operator = (const GeneralDetails& aGeneralDetails) {
+    mType = GeneralMethodDetails;
+    mGeneralDetails = aGeneralDetails;
+    mBasicCardDetails = BasicCardDetails();
+    return *this;
+  }
+  ChangeDetails& operator = (const BasicCardDetails& aBasicCardDetails) {
+    mType = BasicCardMethodDetails;
+    mGeneralDetails = GeneralDetails();
+    mBasicCardDetails = aBasicCardDetails;
+    return *this;
+  }
+  ~ChangeDetails() = default;
+
+  const Type& type() const { return mType; }
+  const GeneralDetails& generalDetails() const { return mGeneralDetails; }
+  const BasicCardDetails& basicCardDetails() const { return mBasicCardDetails;}
+private:
+  Type mType;
+  GeneralDetails mGeneralDetails;
+  BasicCardDetails mBasicCardDetails;
+};
+
 class PaymentRequest final
   : public DOMEventTargetHelper
   , public PromiseNativeHandler
   , public nsIDocumentActivity
 {
 public:
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(PaymentRequest,
@@ -92,17 +163,17 @@ public:
 
   already_AddRefed<Promise> CanMakePayment(ErrorResult& aRv);
   void RespondCanMakePayment(bool aResult);
 
   already_AddRefed<Promise> Show(
     const Optional<OwningNonNull<Promise>>& detailsPromise,
     ErrorResult& aRv);
   void RespondShowPayment(const nsAString& aMethodName,
-                          const ResponseData& aDetails,
+                          const ResponseData& aData,
                           const nsAString& aPayerName,
                           const nsAString& aPayerEmail,
                           const nsAString& aPayerPhone,
                           nsresult aRv);
   void RejectShowPayment(nsresult aRejectReason);
   void RespondComplete();
 
   already_AddRefed<Promise> Abort(ErrorResult& aRv);
@@ -146,16 +217,19 @@ public:
                          const PaymentDetailsUpdate& aDetails);
   void AbortUpdate(nsresult aRv);
 
   void SetShippingType(const Nullable<PaymentShippingType>& aShippingType);
   Nullable<PaymentShippingType> GetShippingType() const;
 
   inline void ShippingWasRequested() { mRequestShipping = true; }
 
+  nsresult UpdatePaymentMethod(const nsAString& aMethodName,
+                               const ChangeDetails& aMethodDetails);
+
   void ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override;
   void RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override;
 
   IMPL_EVENT_HANDLER(merchantvalidation);
   IMPL_EVENT_HANDLER(shippingaddresschange);
   IMPL_EVENT_HANDLER(shippingoptionchange);
   IMPL_EVENT_HANDLER(paymentmethodchange);
 
@@ -171,16 +245,19 @@ protected:
 
   void RegisterActivityObserver();
   void UnregisterActivityObserver();
 
   nsresult DispatchUpdateEvent(const nsAString& aType);
 
   nsresult DispatchMerchantValidationEvent(const nsAString& aType);
 
+  nsresult DispatchPaymentMethodChangeEvent(const nsAString& aMethodName,
+                                            const ChangeDetails& aMethodDatils);
+
   PaymentRequest(nsPIDOMWindowInner* aWindow, const nsAString& aInternalId);
 
   // Id for internal identification
   nsString mInternalId;
   // Id for communicating with merchant side
   // mId is initialized to details.id if it exists
   // otherwise, mId has the same value as mInternalId.
   nsString mId;
--- a/dom/payments/PaymentRequestData.cpp
+++ b/dom/payments/PaymentRequestData.cpp
@@ -600,36 +600,39 @@ PaymentDetails::UpdateErrors(const nsASt
 
 NS_IMPL_ISUPPORTS(PaymentOptions,
                   nsIPaymentOptions)
 
 PaymentOptions::PaymentOptions(const bool aRequestPayerName,
                                const bool aRequestPayerEmail,
                                const bool aRequestPayerPhone,
                                const bool aRequestShipping,
+                               const bool aRequestBillingAddress,
                                const nsAString& aShippingType)
   : mRequestPayerName(aRequestPayerName)
   , mRequestPayerEmail(aRequestPayerEmail)
   , mRequestPayerPhone(aRequestPayerPhone)
   , mRequestShipping(aRequestShipping)
+  , mRequestBillingAddress(aRequestBillingAddress)
   , mShippingType(aShippingType)
 {
 }
 
 nsresult
 PaymentOptions::Create(const IPCPaymentOptions& aIPCOptions,
                        nsIPaymentOptions** aOptions)
 {
   NS_ENSURE_ARG_POINTER(aOptions);
 
   nsCOMPtr<nsIPaymentOptions> options =
     new PaymentOptions(aIPCOptions.requestPayerName(),
                        aIPCOptions.requestPayerEmail(),
                        aIPCOptions.requestPayerPhone(),
                        aIPCOptions.requestShipping(),
+                       aIPCOptions.requestBillingAddress(),
                        aIPCOptions.shippingType());
   options.forget(aOptions);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 PaymentOptions::GetRequestPayerName(bool* aRequestPayerName)
 {
@@ -658,16 +661,24 @@ NS_IMETHODIMP
 PaymentOptions::GetRequestShipping(bool* aRequestShipping)
 {
   NS_ENSURE_ARG_POINTER(aRequestShipping);
   *aRequestShipping = mRequestShipping;
   return NS_OK;
 }
 
 NS_IMETHODIMP
+PaymentOptions::GetRequestBillingAddress(bool* aRequestBillingAddress)
+{
+  NS_ENSURE_ARG_POINTER(aRequestBillingAddress);
+  *aRequestBillingAddress = mRequestBillingAddress;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 PaymentOptions::GetShippingType(nsAString& aShippingType)
 {
   aShippingType = mShippingType;
   return NS_OK;
 }
 
 /* PaymentReqeust */
 
--- a/dom/payments/PaymentRequestData.h
+++ b/dom/payments/PaymentRequestData.h
@@ -171,23 +171,25 @@ public:
   static nsresult Create(const IPCPaymentOptions& aIPCOptions,
                          nsIPaymentOptions** aOptions);
 
 private:
   PaymentOptions(const bool aRequestPayerName,
                  const bool aRequestPayerEmail,
                  const bool aRequestPayerPhone,
                  const bool aRequestShipping,
+                 const bool aRequestBillingAddress,
                  const nsAString& aShippintType);
   ~PaymentOptions() = default;
 
   bool mRequestPayerName;
   bool mRequestPayerEmail;
   bool mRequestPayerPhone;
   bool mRequestShipping;
+  bool mRequestBillingAddress;
   nsString mShippingType;
 };
 
 class PaymentRequest final : public nsIPaymentRequest
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIPAYMENTREQUEST
--- a/dom/payments/PaymentRequestManager.cpp
+++ b/dom/payments/PaymentRequestManager.cpp
@@ -250,16 +250,17 @@ ConvertOptions(const PaymentOptions& aOp
   if (shippingTypeIndex < ArrayLength(PaymentShippingTypeValues::strings)) {
     shippingType.AssignASCII(
       PaymentShippingTypeValues::strings[shippingTypeIndex].value);
   }
   aIPCOption = IPCPaymentOptions(aOptions.mRequestPayerName,
                                  aOptions.mRequestPayerEmail,
                                  aOptions.mRequestPayerPhone,
                                  aOptions.mRequestShipping,
+                                 aOptions.mRequestBillingAddress,
                                  shippingType);
 }
 
 void
 ConvertResponseData(const IPCPaymentResponseData& aIPCData,
                     ResponseData& aData)
 {
   switch (aIPCData.type()) {
@@ -293,16 +294,53 @@ ConvertResponseData(const IPCPaymentResp
       aData = bData;
       break;
     }
     default: {
       break;
     }
   }
 }
+
+void
+ConvertMethodChangeDetails(const IPCMethodChangeDetails& aIPCDetails,
+                           ChangeDetails& aDetails)
+{
+  switch (aIPCDetails.type()) {
+    case IPCMethodChangeDetails::TIPCGeneralChangeDetails : {
+      const IPCGeneralChangeDetails& details = aIPCDetails;
+      GeneralDetails gDetails;
+      gDetails.details = details.details();
+      aDetails = gDetails;
+      break;
+    }
+    case IPCMethodChangeDetails::TIPCBasicCardChangeDetails: {
+      const IPCBasicCardChangeDetails& details = aIPCDetails;
+      BasicCardDetails bDetails;
+      bDetails.billingAddress.country = details.billingAddress().country();
+      bDetails.billingAddress.addressLine = details.billingAddress().addressLine();
+      bDetails.billingAddress.region = details.billingAddress().region();
+      bDetails.billingAddress.regionCode = details.billingAddress().regionCode();
+      bDetails.billingAddress.city = details.billingAddress().city();
+      bDetails.billingAddress.dependentLocality =
+        details.billingAddress().dependentLocality();
+      bDetails.billingAddress.postalCode = details.billingAddress().postalCode();
+      bDetails.billingAddress.sortingCode = details.billingAddress().sortingCode();
+      bDetails.billingAddress.organization = details.billingAddress().organization();
+      bDetails.billingAddress.recipient = details.billingAddress().recipient();
+      bDetails.billingAddress.phone = details.billingAddress().phone();
+      aDetails = bDetails;
+      break;
+    }
+    default: {
+      break;
+    }
+  }
+
+}
 } // end of namespace
 
 /* PaymentRequestManager */
 
 StaticRefPtr<PaymentRequestManager> gPaymentManager;
 const char kSupportedRegionsPref[] = "dom.payments.request.supportedRegions";
 
 void
@@ -761,10 +799,21 @@ PaymentRequestManager::ChangePayerDetail
   RefPtr<PaymentResponse> response = aRequest->GetResponse();
   // ignoring the case call changePayerDetail during show().
   if (!response) {
     return NS_OK;
   }
   return response->UpdatePayerDetail(aPayerName, aPayerEmail, aPayerPhone);
 }
 
+nsresult
+PaymentRequestManager::ChangePaymentMethod(PaymentRequest* aRequest,
+                                           const nsAString& aMethodName,
+                                           const IPCMethodChangeDetails& aMethodDetails)
+{
+  NS_ENSURE_ARG_POINTER(aRequest);
+  ChangeDetails methodDetails;
+  ConvertMethodChangeDetails(aMethodDetails, methodDetails);
+  return aRequest->UpdatePaymentMethod(aMethodName, methodDetails);
+}
+
 } // end of namespace dom
 } // end of namespace mozilla
--- a/dom/payments/PaymentRequestManager.h
+++ b/dom/payments/PaymentRequestManager.h
@@ -62,21 +62,23 @@ public:
                         const PaymentValidationErrors& aErrors);
 
   nsresult RespondPayment(PaymentRequest* aRequest,
                           const IPCPaymentActionResponse& aResponse);
   nsresult ChangeShippingAddress(PaymentRequest* aRequest,
                                  const IPCPaymentAddress& aAddress);
   nsresult ChangeShippingOption(PaymentRequest* aRequest,
                                 const nsAString& aOption);
-
   nsresult ChangePayerDetail(PaymentRequest* aRequest,
                              const nsAString& aPayerName,
                              const nsAString& aPayerEmail,
                              const nsAString& aPayerPhone);
+  nsresult ChangePaymentMethod(PaymentRequest* aRequest,
+                               const nsAString& aMethodName,
+                               const IPCMethodChangeDetails& aMethodDetails);
 
   bool IsRegionSupported(const nsAString& region) const;
 
   // Called to ensure that we don't "leak" aRequest if we shut down while it had
   // an active request to the parent.
   void RequestIPCOver(PaymentRequest* aRequest);
 
 private:
--- a/dom/payments/PaymentRequestModule.cpp
+++ b/dom/payments/PaymentRequestModule.cpp
@@ -10,69 +10,81 @@
 #include "PaymentRequestService.h"
 
 using mozilla::dom::GeneralResponseData;
 using mozilla::dom::BasicCardResponseData;
 using mozilla::dom::PaymentCanMakeActionResponse;
 using mozilla::dom::PaymentAbortActionResponse;
 using mozilla::dom::PaymentShowActionResponse;
 using mozilla::dom::PaymentCompleteActionResponse;
+using mozilla::dom::GeneralMethodChangeDetails;
+using mozilla::dom::BasicCardMethodChangeDetails;
 using mozilla::dom::payments::PaymentAddress;
 using mozilla::dom::PaymentRequestService;
 
 NS_GENERIC_FACTORY_CONSTRUCTOR(GeneralResponseData)
 NS_GENERIC_FACTORY_CONSTRUCTOR(BasicCardResponseData)
 NS_GENERIC_FACTORY_CONSTRUCTOR(PaymentCanMakeActionResponse)
 NS_GENERIC_FACTORY_CONSTRUCTOR(PaymentAbortActionResponse)
 NS_GENERIC_FACTORY_CONSTRUCTOR(PaymentShowActionResponse)
 NS_GENERIC_FACTORY_CONSTRUCTOR(PaymentCompleteActionResponse)
+NS_GENERIC_FACTORY_CONSTRUCTOR(GeneralMethodChangeDetails)
+NS_GENERIC_FACTORY_CONSTRUCTOR(BasicCardMethodChangeDetails)
 NS_GENERIC_FACTORY_CONSTRUCTOR(PaymentAddress)
 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(PaymentRequestService,
                                          PaymentRequestService::GetSingleton)
 
 NS_DEFINE_NAMED_CID(NS_GENERAL_RESPONSE_DATA_CID);
 NS_DEFINE_NAMED_CID(NS_BASICCARD_RESPONSE_DATA_CID);
 NS_DEFINE_NAMED_CID(NS_PAYMENT_CANMAKE_ACTION_RESPONSE_CID);
 NS_DEFINE_NAMED_CID(NS_PAYMENT_ABORT_ACTION_RESPONSE_CID);
 NS_DEFINE_NAMED_CID(NS_PAYMENT_SHOW_ACTION_RESPONSE_CID);
 NS_DEFINE_NAMED_CID(NS_PAYMENT_COMPLETE_ACTION_RESPONSE_CID);
+NS_DEFINE_NAMED_CID(NS_GENERAL_CHANGE_DETAILS_CID);
+NS_DEFINE_NAMED_CID(NS_BASICCARD_CHANGE_DETAILS_CID);
 NS_DEFINE_NAMED_CID(NS_PAYMENT_ADDRESS_CID);
 NS_DEFINE_NAMED_CID(NS_PAYMENT_REQUEST_SERVICE_CID);
 
 static const mozilla::Module::CIDEntry kPaymentRequestCIDs[] = {
   { &kNS_GENERAL_RESPONSE_DATA_CID, false, nullptr, GeneralResponseDataConstructor},
   { &kNS_BASICCARD_RESPONSE_DATA_CID, false, nullptr, BasicCardResponseDataConstructor},
   { &kNS_PAYMENT_CANMAKE_ACTION_RESPONSE_CID, false, nullptr, PaymentCanMakeActionResponseConstructor},
   { &kNS_PAYMENT_ABORT_ACTION_RESPONSE_CID, false, nullptr, PaymentAbortActionResponseConstructor},
   { &kNS_PAYMENT_SHOW_ACTION_RESPONSE_CID, false, nullptr, PaymentShowActionResponseConstructor},
   { &kNS_PAYMENT_COMPLETE_ACTION_RESPONSE_CID, false, nullptr, PaymentCompleteActionResponseConstructor},
+  { &kNS_GENERAL_CHANGE_DETAILS_CID, false, nullptr, GeneralMethodChangeDetailsConstructor},
+  { &kNS_BASICCARD_CHANGE_DETAILS_CID, false, nullptr, BasicCardMethodChangeDetailsConstructor},
   { &kNS_PAYMENT_ADDRESS_CID, false, nullptr, PaymentAddressConstructor},
   { &kNS_PAYMENT_REQUEST_SERVICE_CID, true, nullptr, PaymentRequestServiceConstructor },
   { nullptr }
 };
 
 static const mozilla::Module::ContractIDEntry kPaymentRequestContracts[] = {
   { NS_GENERAL_RESPONSE_DATA_CONTRACT_ID, &kNS_GENERAL_RESPONSE_DATA_CID },
   { NS_BASICCARD_RESPONSE_DATA_CONTRACT_ID, &kNS_BASICCARD_RESPONSE_DATA_CID },
   { NS_PAYMENT_CANMAKE_ACTION_RESPONSE_CONTRACT_ID, &kNS_PAYMENT_CANMAKE_ACTION_RESPONSE_CID },
   { NS_PAYMENT_ABORT_ACTION_RESPONSE_CONTRACT_ID, &kNS_PAYMENT_ABORT_ACTION_RESPONSE_CID },
   { NS_PAYMENT_SHOW_ACTION_RESPONSE_CONTRACT_ID, &kNS_PAYMENT_SHOW_ACTION_RESPONSE_CID },
   { NS_PAYMENT_COMPLETE_ACTION_RESPONSE_CONTRACT_ID, &kNS_PAYMENT_COMPLETE_ACTION_RESPONSE_CID },
+  { NS_GENERAL_CHANGE_DETAILS_CONTRACT_ID, &kNS_GENERAL_CHANGE_DETAILS_CID },
+  { NS_BASICCARD_CHANGE_DETAILS_CONTRACT_ID, &kNS_BASICCARD_CHANGE_DETAILS_CID },
   { NS_PAYMENT_ADDRESS_CONTRACT_ID, &kNS_PAYMENT_ADDRESS_CID },
   { NS_PAYMENT_REQUEST_SERVICE_CONTRACT_ID, &kNS_PAYMENT_REQUEST_SERVICE_CID },
   { nullptr }
 };
 
 static const mozilla::Module::CategoryEntry kPaymentRequestCategories[] = {
   { "payment-request", "GeneralResponseData", NS_GENERAL_RESPONSE_DATA_CONTRACT_ID },
   { "payment-request", "BasicCardResponseData", NS_BASICCARD_RESPONSE_DATA_CONTRACT_ID },
   { "payment-request", "PaymentCanMakeActionResponse", NS_PAYMENT_CANMAKE_ACTION_RESPONSE_CONTRACT_ID },
   { "payment-request", "PaymentAbortActionResponse", NS_PAYMENT_ABORT_ACTION_RESPONSE_CONTRACT_ID },
   { "payment-request", "PaymentShowActionResponse", NS_PAYMENT_SHOW_ACTION_RESPONSE_CONTRACT_ID },
   { "payment-request", "PaymentCompleteActionResponse", NS_PAYMENT_COMPLETE_ACTION_RESPONSE_CONTRACT_ID },
+  { "payment-request", "GeneralMethodChangeDetails", NS_GENERAL_CHANGE_DETAILS_CONTRACT_ID },
+  { "payment-request", "BasicCardMethodChangeDetails", NS_BASICCARD_CHANGE_DETAILS_CONTRACT_ID },
   { "payment-request", "PaymentAddress", NS_PAYMENT_ADDRESS_CONTRACT_ID },
   { "payment-request", "PaymentRequestService", NS_PAYMENT_REQUEST_SERVICE_CONTRACT_ID },
   { nullptr }
 };
 
 static const mozilla::Module kPaymentRequestModule = {
   mozilla::Module::kVersion,
   kPaymentRequestCIDs,
--- a/dom/payments/PaymentRequestService.cpp
+++ b/dom/payments/PaymentRequestService.cpp
@@ -1,19 +1,20 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+#include "BasicCardPayment.h"
 #include "mozilla/ClearOnShutdown.h"
+#include "mozilla/dom/BasicCardPaymentBinding.h"
 #include "mozilla/dom/PaymentRequestParent.h"
+#include "nsSimpleEnumerator.h"
 #include "PaymentRequestService.h"
-#include "BasicCardPayment.h"
-#include "nsSimpleEnumerator.h"
 
 namespace mozilla {
 namespace dom {
 
 StaticRefPtr<PaymentRequestService> gPaymentService;
 
 namespace {
 
@@ -498,16 +499,67 @@ PaymentRequestService::ChangeShippingOpt
   }
   rv = request->GetIPC()->ChangeShippingOption(aRequestId, aOption);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
   return NS_OK;
 }
 
+NS_IMETHODIMP
+PaymentRequestService::ChangePayerDetail(const nsAString& aRequestId,
+                                         const nsAString& aPayerName,
+                                         const nsAString& aPayerEmail,
+                                         const nsAString& aPayerPhone)
+{
+  RefPtr<payments::PaymentRequest> request;
+  nsresult rv = GetPaymentRequestById(aRequestId, getter_AddRefs(request));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+  MOZ_ASSERT(request);
+  if (!request->GetIPC()) {
+    return NS_ERROR_FAILURE;
+  }
+  rv = request->GetIPC()->ChangePayerDetail(
+    aRequestId, aPayerName, aPayerEmail, aPayerPhone);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+PaymentRequestService::ChangePaymentMethod(const nsAString& aRequestId,
+                                           const nsAString& aMethodName,
+                                           nsIMethodChangeDetails* aMethodDetails)
+{
+  RefPtr<payments::PaymentRequest> request;
+  nsresult rv = GetPaymentRequestById(aRequestId, getter_AddRefs(request));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+  if (!request) {
+    return NS_ERROR_FAILURE;
+  }
+  if (request->GetState() != payments::PaymentRequest::eInteractive) {
+    return NS_ERROR_FAILURE;
+  }
+  if (!request->GetIPC()) {
+    return NS_ERROR_FAILURE;
+  }
+  rv = request->GetIPC()->ChangePaymentMethod(aRequestId,
+                                              aMethodName,
+                                              aMethodDetails);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+  return NS_OK;
+}
+
 bool
 PaymentRequestService::CanMakePayment(const nsAString& aRequestId)
 {
   /*
    *  TODO: Check third party payment app support by traversing all
    *        registered third party payment apps.
    */
   return IsBasicCardPayment(aRequestId);
@@ -582,33 +634,10 @@ PaymentRequestService::IsBasicCardPaymen
     NS_ENSURE_SUCCESS(rv, false);
     if (service->IsBasicCardPayment(supportedMethods)) {
       return true;
     }
   }
   return false;
 }
 
-NS_IMETHODIMP
-PaymentRequestService::ChangePayerDetail(const nsAString& aRequestId,
-                                         const nsAString& aPayerName,
-                                         const nsAString& aPayerEmail,
-                                         const nsAString& aPayerPhone)
-{
-  RefPtr<payments::PaymentRequest> request;
-  nsresult rv = GetPaymentRequestById(aRequestId, getter_AddRefs(request));
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-  MOZ_ASSERT(request);
-  if (!request->GetIPC()) {
-    return NS_ERROR_FAILURE;
-  }
-  rv = request->GetIPC()->ChangePayerDetail(
-    aRequestId, aPayerName, aPayerEmail, aPayerPhone);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-  return NS_OK;
-}
-
 } // end of namespace dom
 } // end of namespace mozilla
--- a/dom/payments/PaymentRequestUpdateEvent.h
+++ b/dom/payments/PaymentRequestUpdateEvent.h
@@ -49,18 +49,16 @@ public:
               ErrorResult& aRv);
 
   void UpdateWith(Promise& aPromise, ErrorResult& aRv);
 
   void SetRequest(PaymentRequest* aRequest);
 
 protected:
   ~PaymentRequestUpdateEvent();
-
-private:
   // Indicating whether an updateWith()-initiated update is currently in progress.
   bool mWaitForUpdate;
   RefPtr<PaymentRequest> mRequest;
 };
 
 } // namespace dom
 } // namespace mozilla
 
--- a/dom/payments/PaymentResponse.h
+++ b/dom/payments/PaymentResponse.h
@@ -80,17 +80,17 @@ public:
     return *this;
   }
   ResponseData& operator = (const BasicCardData& aBasicCardData) {
     mType = BasicCardResponse;
     mGeneralData = GeneralData();
     mBasicCardData = aBasicCardData;
     return *this;
   }
-  virtual ~ResponseData() = default;
+  ~ResponseData() = default;
 
   const Type& type() const { return mType; }
   const GeneralData& generalData() const { return mGeneralData; }
   const BasicCardData& basicCardData() const { return mBasicCardData;}
 private:
   Type mType;
   GeneralData mGeneralData;
   BasicCardData mBasicCardData;
--- a/dom/payments/ipc/PPaymentRequest.ipdl
+++ b/dom/payments/ipc/PPaymentRequest.ipdl
@@ -60,16 +60,17 @@ struct IPCPaymentDetails
 };
 
 struct IPCPaymentOptions
 {
   bool requestPayerName;
   bool requestPayerEmail;
   bool requestPayerPhone;
   bool requestShipping;
+  bool requestBillingAddress;
   nsString shippingType;
 };
 
 struct IPCPaymentCreateActionRequest
 {
   uint64_t topOuterWindowId;
   nsString requestId;
   Principal topLevelPrincipal;
@@ -202,16 +203,32 @@ struct IPCPaymentCompleteActionResponse
 union IPCPaymentActionResponse
 {
   IPCPaymentCanMakeActionResponse;
   IPCPaymentShowActionResponse;
   IPCPaymentAbortActionResponse;
   IPCPaymentCompleteActionResponse;
 };
 
+struct IPCGeneralChangeDetails
+{
+  nsString details;
+};
+
+struct IPCBasicCardChangeDetails
+{
+  IPCPaymentAddress billingAddress;
+};
+
+union IPCMethodChangeDetails
+{
+  IPCGeneralChangeDetails;
+  IPCBasicCardChangeDetails;
+};
+
 sync protocol PPaymentRequest
 {
   manager PBrowser;
 
 parent:
   async __delete__();
 
   async RequestPayment(IPCPaymentActionRequest aAction);
@@ -221,12 +238,15 @@ child:
   async ChangeShippingAddress(nsString aRequestId,
                               IPCPaymentAddress aAddress);
   async ChangeShippingOption(nsString aRequestId,
                              nsString aOption);
   async ChangePayerDetail(nsString aRequestId,
                           nsString aPayerName,
                           nsString aPayerEmail,
                           nsString aPayerPhone);
+  async ChangePaymentMethod(nsString aRequestId,
+                            nsString aMethodName,
+                            IPCMethodChangeDetails aMethodDetails);
 };
 
 } // end of namespace dom
 } // end of namespace mozilla
--- a/dom/payments/ipc/PaymentRequestChild.cpp
+++ b/dom/payments/ipc/PaymentRequestChild.cpp
@@ -96,16 +96,34 @@ PaymentRequestChild::RecvChangePayerDeta
   RefPtr<PaymentRequest> request(mRequest);
   nsresult rv = manager->ChangePayerDetail(request, aPayerName, aPayerEmail, aPayerPhone);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return IPC_FAIL_NO_REASON(this);
   }
   return IPC_OK();
 }
 
+mozilla::ipc::IPCResult
+PaymentRequestChild::RecvChangePaymentMethod(const nsString& aRequestId,
+                                             const nsString& aMethodName,
+                                             const IPCMethodChangeDetails& aMethodDetails)
+{
+  if (!mRequest) {
+    return IPC_FAIL_NO_REASON(this);
+  }
+  RefPtr<PaymentRequestManager> manager = PaymentRequestManager::GetSingleton();
+  MOZ_ASSERT(manager);
+  RefPtr<PaymentRequest> request(mRequest);
+  nsresult rv = manager->ChangePaymentMethod(request, aMethodName, aMethodDetails);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return IPC_FAIL_NO_REASON(this);
+  }
+  return IPC_OK();
+}
+
 void
 PaymentRequestChild::ActorDestroy(ActorDestroyReason aWhy)
 {
   if (mRequest) {
     DetachFromRequest(true);
   }
 }
 
--- a/dom/payments/ipc/PaymentRequestChild.h
+++ b/dom/payments/ipc/PaymentRequestChild.h
@@ -36,16 +36,21 @@ protected:
                            const nsString& aOption) override;
 
   mozilla::ipc::IPCResult
   RecvChangePayerDetail(const nsString& aRequestId,
                         const nsString& aPayerName,
                         const nsString& aPayerEmail,
                         const nsString& aPayerPhone) override;
 
+  mozilla::ipc::IPCResult
+  RecvChangePaymentMethod(const nsString& aRequestId,
+                          const nsString& aMethodName,
+                          const IPCMethodChangeDetails& aMethodDetails) override;
+
   void ActorDestroy(ActorDestroyReason aWhy) override;
 
 private:
   ~PaymentRequestChild() = default;
 
   void DetachFromRequest(bool aCanBeInManager);
 
   PaymentRequest* MOZ_NON_OWNING_REF mRequest;
--- a/dom/payments/ipc/PaymentRequestParent.cpp
+++ b/dom/payments/ipc/PaymentRequestParent.cpp
@@ -272,16 +272,77 @@ PaymentRequestParent::ChangePayerDetail(
     return NS_ERROR_FAILURE;
   }
   if (!SendChangePayerDetail(requestId, payerName, payerEmail, payerPhone)) {
     return NS_ERROR_FAILURE;
   }
   return NS_OK;
 }
 
+nsresult
+PaymentRequestParent::ChangePaymentMethod(const nsAString& aRequestId,
+                                          const nsAString& aMethodName,
+                                          nsIMethodChangeDetails* aMethodDetails)
+{
+  nsAutoString requestId(aRequestId);
+  nsAutoString methodName(aMethodName);
+  nsCOMPtr<nsIMethodChangeDetails> methodDetails(aMethodDetails);
+  if (!NS_IsMainThread()) {
+    RefPtr<PaymentRequestParent> self = this;
+    nsCOMPtr<nsIRunnable> r =
+      NS_NewRunnableFunction("dom::PaymentRequestParent::ChangePaymentMethod",
+                             [self, requestId, methodName, methodDetails] ()
+    {
+      self->ChangePaymentMethod(requestId, methodName, methodDetails);
+    });
+    return NS_DispatchToMainThread(r);
+  }
+  if (!mActorAlive) {
+    return NS_ERROR_FAILURE;
+  }
+
+  // Convert nsIMethodChangeDetails to IPCMethodChangeDetails
+  // aMethodChangeDetails can be null
+  IPCMethodChangeDetails ipcChangeDetails;
+  if (aMethodDetails) {
+    uint32_t dataType;
+    NS_ENSURE_SUCCESS(aMethodDetails->GetType(&dataType), NS_ERROR_FAILURE);
+    switch(dataType) {
+      case nsIMethodChangeDetails::GENERAL_DETAILS: {
+        nsCOMPtr<nsIGeneralChangeDetails> details = do_QueryInterface(methodDetails);
+        MOZ_ASSERT(details);
+        IPCGeneralChangeDetails ipcGeneralDetails;
+        NS_ENSURE_SUCCESS(details->GetDetails(ipcGeneralDetails.details()), NS_ERROR_FAILURE);
+        ipcChangeDetails = ipcGeneralDetails;
+        break;
+      }
+      case nsIMethodChangeDetails::BASICCARD_DETAILS: {
+        nsCOMPtr<nsIBasicCardChangeDetails> details = do_QueryInterface(methodDetails);
+        MOZ_ASSERT(details);
+        IPCBasicCardChangeDetails ipcBasicCardDetails;
+        nsCOMPtr<nsIPaymentAddress> address;
+        NS_ENSURE_SUCCESS(details->GetBillingAddress(getter_AddRefs(address)),
+                          NS_ERROR_FAILURE);
+        IPCPaymentAddress ipcAddress;
+        NS_ENSURE_SUCCESS(SerializeAddress(ipcAddress, address), NS_ERROR_FAILURE);
+        ipcBasicCardDetails.billingAddress() = ipcAddress;
+        ipcChangeDetails = ipcBasicCardDetails;
+        break;
+      }
+      default: {
+        return NS_ERROR_FAILURE;
+      }
+    }
+  }
+  if (!SendChangePaymentMethod(requestId, methodName, ipcChangeDetails)) {
+    return NS_ERROR_FAILURE;
+  }
+  return NS_OK;
+}
+
 mozilla::ipc::IPCResult
 PaymentRequestParent::Recv__delete__()
 {
   mActorAlive = false;
   return IPC_OK();
 }
 
 void
--- a/dom/payments/ipc/PaymentRequestParent.h
+++ b/dom/payments/ipc/PaymentRequestParent.h
@@ -24,16 +24,19 @@ public:
   nsresult ChangeShippingAddress(const nsAString& aRequestId,
                                  nsIPaymentAddress* aAddress);
   nsresult ChangeShippingOption(const nsAString& aRequestId,
                                 const nsAString& aOption);
   nsresult ChangePayerDetail(const nsAString& aRequestId,
                              const nsAString& aPayerName,
                              const nsAString& aPayerEmail,
                              const nsAString& aPayerPhone);
+  nsresult ChangePaymentMethod(const nsAString& aRequestId,
+                               const nsAString& aMethodName,
+                               nsIMethodChangeDetails* aMethodDetails);
 
 protected:
   mozilla::ipc::IPCResult
   RecvRequestPayment(const IPCPaymentActionRequest& aRequest) override;
 
   mozilla::ipc::IPCResult Recv__delete__() override;
 
   void ActorDestroy(ActorDestroyReason aWhy) override;
--- a/dom/payments/test/BasiccardChromeScript.js
+++ b/dom/payments/test/BasiccardChromeScript.js
@@ -3,17 +3,17 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 "use strict";
 
 const { XPCOMUtils } = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 
 const paymentSrv = Cc["@mozilla.org/dom/payments/payment-request-service;1"].getService(Ci.nsIPaymentRequestService);
 
 function emitTestFail(message) {
-  sendAsyncMessage("test-fail", message);
+  sendAsyncMessage("test-fail", `${DummyUIService.testName}: ${message}`);
 }
 
 const billingAddress = Cc["@mozilla.org/dom/payments/payment-address;1"].
                            createInstance(Ci.nsIPaymentAddress);
 const addressLine = Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray);
 const address = Cc["@mozilla.org/supports-string;1"].createInstance(Ci.nsISupportsString);
 address.data = "Easton Ave";
 addressLine.appendElement(address);
@@ -45,16 +45,19 @@ specialAddress.init("USA",              
                      "123456",           // sorting code
                      "",                 // organization
                      "Bill A. Pacheco",  // recipient
                      "+14344413879"); // phone
 
 const basiccardResponseData = Cc["@mozilla.org/dom/payments/basiccard-response-data;1"].
                                  createInstance(Ci.nsIBasicCardResponseData);
 
+const basiccardChangeDetails = Cc["@mozilla.org/dom/payments/basiccard-change-details;1"].
+                                  createInstance(Ci.nsIBasicCardChangeDetails);
+
 const showResponse = Cc["@mozilla.org/dom/payments/payment-show-action-response;1"].
                         createInstance(Ci.nsIPaymentShowActionResponse);
 
 function abortPaymentResponse(requestId) {
   let abortResponse = Cc["@mozilla.org/dom/payments/payment-abort-action-response;1"].
                          createInstance(Ci.nsIPaymentAbortActionResponse);
   abortResponse.init(requestId, Ci.nsIPaymentActionResponse.ABORT_SUCCEEDED);
   paymentSrv.respondPayment(abortResponse.QueryInterface(Ci.nsIPaymentActionResponse));
@@ -62,18 +65,79 @@ function abortPaymentResponse(requestId)
 
 function completePaymentResponse(requestId) {
   let completeResponse = Cc["@mozilla.org/dom/payments/payment-complete-action-response;1"].
                             createInstance(Ci.nsIPaymentCompleteActionResponse);
   completeResponse.init(requestId, Ci.nsIPaymentActionResponse.COMPLETE_SUCCEEDED);
   paymentSrv.respondPayment(completeResponse.QueryInterface(Ci.nsIPaymentActionResponse));
 }
 
-const detailedResponseUI = {
-  showPayment: function(requestId) {
+function showRequest(requestId) {
+  if (DummyUIService.showAction === "payment-method-change") {
+    basiccardChangeDetails.initData(billingAddress);
+    try {
+      paymentSrv.changePaymentMethod(requestId, "basic-card", basiccardChangeDetails.QueryInterface(Ci.nsIMethodChangeDetails));
+    } catch (error) {
+      emitTestFail(`Unexpected error (${error.name}) when calling PaymentRequestService::changePaymentMethod`);
+    }
+    return;
+  }
+  if (DummyUIService.showAction === "detailBasicCardResponse") {
+    try {
+      basiccardResponseData.initData("Bill A. Pacheco",  // cardholderName
+                                     "4916855166538720", // cardNumber
+                                     "01",               // expiryMonth
+                                     "2024",             // expiryYear
+                                     "180",              // cardSecurityCode
+                                     billingAddress);   // billingAddress
+    } catch (e) {
+      emitTestFail("Fail to initialize basic card response data.");
+    }
+  }
+  if (DummyUIService.showAction === "simpleBasicCardResponse") {
+    try {
+      basiccardResponseData.initData("",                 // cardholderName
+                                     "4916855166538720", // cardNumber
+                                     "",                 // expiryMonth
+                                     "",                 // expiryYear
+                                     "",                 // cardSecurityCode
+                                     null);              // billingAddress
+    } catch (e) {
+      emitTestFail("Fail to initialize basic card response data.");
+    }
+  }
+  if (DummyUIService.showAction === "specialAddressResponse") {
+    try {
+      basiccardResponseData.initData("Bill A. Pacheco",  // cardholderName
+                                     "4916855166538720", // cardNumber
+                                     "01",               // expiryMonth
+                                     "2024",             // expiryYear
+                                     "180",              // cardSecurityCode
+                                     specialAddress);    // billingAddress
+    } catch (e) {
+      emitTestFail("Fail to initialize basic card response data.");
+    }
+  }
+  showResponse.init(requestId,
+                    Ci.nsIPaymentActionResponse.PAYMENT_ACCEPTED,
+                    "basic-card",         // payment method
+                    basiccardResponseData,// payment method data
+                    "Bill A. Pacheco",    // payer name
+                    "",                   // payer email
+                    "");                  // payer phone
+  paymentSrv.respondPayment(showResponse.QueryInterface(Ci.nsIPaymentActionResponse));
+}
+
+const DummyUIService = {
+  testName: "",
+  showAction: "",
+  showPayment: showRequest,
+  abortPayment: abortPaymentResponse,
+  completePayment: completePaymentResponse,
+  updatePayment: (requestId) => {
     try {
       basiccardResponseData.initData("Bill A. Pacheco",  // cardholderName
                                      "4916855166538720", // cardNumber
                                      "01",               // expiryMonth
                                      "2024",             // expiryYear
                                      "180",              // cardSecurityCode
                                      billingAddress);   // billingAddress
     } catch (e) {
@@ -83,98 +147,47 @@ const detailedResponseUI = {
                       Ci.nsIPaymentActionResponse.PAYMENT_ACCEPTED,
                       "basic-card",         // payment method
                       basiccardResponseData,// payment method data
                       "Bill A. Pacheco",    // payer name
                       "",                   // payer email
                       "");                  // payer phone
     paymentSrv.respondPayment(showResponse.QueryInterface(Ci.nsIPaymentActionResponse));
   },
-  abortPayment: abortPaymentResponse,
-  completePayment: completePaymentResponse,
-  updatePayment: function(requestId) {
-  },
-  closePayment: function (requestId) {
-  },
-  QueryInterface: ChromeUtils.generateQI([Ci.nsIPaymentUIService]),
-};
-
-const simpleResponseUI = {
-  showPayment: function(requestId) {
-    try {
-      basiccardResponseData.initData("",                 // cardholderName
-                                     "4916855166538720", // cardNumber
-                                     "",                 // expiryMonth
-                                     "",                 // expiryYear
-                                     "",                 // cardSecurityCode
-                                     null);              // billingAddress
-    } catch (e) {
-      emitTestFail("Fail to initialize basic card response data.");
-    }
-    showResponse.init(requestId,
-                      Ci.nsIPaymentActionResponse.PAYMENT_ACCEPTED,
-                      "basic-card",         // payment method
-                      basiccardResponseData,// payment method data
-                      "Bill A. Pacheco",    // payer name
-                      "",                   // payer email
-                      "");                  // payer phone
-    paymentSrv.respondPayment(showResponse.QueryInterface(Ci.nsIPaymentActionResponse));
-  },
-  abortPayment: abortPaymentResponse,
-  completePayment: completePaymentResponse,
-  updatePayment: function(requestId) {
-  },
-  closePayment: function(requestId) {
-  },
+  closePayment: (requestId) => {},
   QueryInterface: ChromeUtils.generateQI([Ci.nsIPaymentUIService]),
 };
 
-const specialAddressUI = {
-  showPayment: function(requestId) {
-    try {
-      basiccardResponseData.initData("Bill A. Pacheco",  // cardholderName
-                                     "4916855166538720", // cardNumber
-                                     "01",               // expiryMonth
-                                     "2024",             // expiryYear
-                                     "180",              // cardSecurityCode
-                                     specialAddress);    // billingAddress
-    } catch (e) {
-      emitTestFail("Fail to initialize basic card response data.");
-    }
-    showResponse.init(requestId,
-                      Ci.nsIPaymentActionResponse.PAYMENT_ACCEPTED,
-                      "basic-card",         // payment method
-                      basiccardResponseData,// payment method data
-                      "Bill A. Pacheco",    // payer name
-                      "",                   // payer email
-                      "");                  // payer phone
-    paymentSrv.respondPayment(showResponse.QueryInterface(Ci.nsIPaymentActionResponse));
-  },
-  abortPayment: abortPaymentResponse,
-  completePayment: completePaymentResponse,
-  updatePayment: function(requestId) {
-  },
-  closePayment: function (requestId) {
-  },
-  QueryInterface: ChromeUtils.generateQI([Ci.nsIPaymentUIService]),
-};
+paymentSrv.setTestingUIService(DummyUIService.QueryInterface(Ci.nsIPaymentUIService));
 
-addMessageListener("set-detailed-ui-service", function() {
-  paymentSrv.setTestingUIService(detailedResponseUI.QueryInterface(Ci.nsIPaymentUIService));
+addMessageListener("set-detailed-ui-service", function(testName) {
+  DummyUIService.testName = testName;
+  DummyUIService.showAction = "detailBasicCardResponse";
+  sendAsyncMessage("set-detailed-ui-service-complete");
+});
+
+addMessageListener("set-simple-ui-service", function(testName) {
+  DummyUIService.testName = testName;
+  DummyUIService.showAction = "simpleBasicCardResponse";
+  sendAsyncMessage("set-simple-ui-service-complete");
 });
 
-addMessageListener("set-simple-ui-service", function() {
-  paymentSrv.setTestingUIService(simpleResponseUI.QueryInterface(Ci.nsIPaymentUIService));
+addMessageListener("set-special-address-ui-service", function(testName) {
+  DummyUIService.testName = testName;
+  DummyUIService.showAction = "specialAddressResponse";
+  sendAsyncMessage("set-special-address-ui-service-complete");
 });
 
-addMessageListener("set-special-address-ui-service", function() {
-  paymentSrv.setTestingUIService(specialAddressUI.QueryInterface(Ci.nsIPaymentUIService));
+addMessageListener("method-change-to-basic-card", function(testName) {
+  DummyUIService.testName = testName;
+  DummyUIService.showAction = "payment-method-change";
+  sendAsyncMessage("method-change-to-basic-card-complete");
 });
 
-addMessageListener("error-response-test", function() {
+addMessageListener("error-response-test", function(testName) {
   // test empty cardNumber
   try {
     basiccardResponseData.initData("", "", "", "", "", null);
     emitTestFail("BasicCardResponse should not be initialized with empty cardNumber.");
   } catch (e) {
     if (e.name != "NS_ERROR_FAILURE") {
       emitTestFail("Empty cardNumber expected 'NS_ERROR_FAILURE', but got " + e.name + ".");
     }
@@ -244,15 +257,15 @@ addMessageListener("error-response-test"
                       "",                   // payer email
                       "");                  // payer phone
     emitTestFail("nsIPaymentShowActionResponse should not be initialized with basic-card method and nsIGeneralResponseData.");
   } catch (e) {
     if (e.name != "NS_ERROR_FAILURE") {
       emitTestFail("ShowResponse init expected 'NS_ERROR_FAILURE', but got " + e.name + ".");
     }
   }
-  sendAsyncMessage("error-response-complete");
+  sendAsyncMessage("error-response-test-complete");
 });
 
 addMessageListener("teardown", function() {
   paymentSrv.setTestingUIService(null);
   sendAsyncMessage("teardown-complete");
 });
--- a/dom/payments/test/test_basiccard.html
+++ b/dom/payments/test/test_basiccard.html
@@ -14,17 +14,31 @@ https://bugzilla.mozilla.org/show_bug.cg
   SimpleTest.waitForExplicitFinish();
 
   var gUrl = SimpleTest.getTestFileURL('BasiccardChromeScript.js');
   var gScript = SpecialPowers.loadChromeScript(gUrl);
 
   function testFailHandler(message) {
     ok(false, message);
   }
+  function testPassHandler(message) {
+    ok(true, message);
+  }
   gScript.addMessageListener("test-fail", testFailHandler);
+  gScript.addMessageListener("test-pass", testPassHandler);
+
+  async function requestChromeAction(action, params) {
+    await new Promise(resolve => {
+      gScript.addMessageListener(`${action}-complete`, function completeListener() {
+        gScript.removeMessageListener(`${action}-complete`, completeListener);
+        resolve();
+      });
+      gScript.sendAsyncMessage(action, params);
+    });
+  }
 
   const errorNetworksMethods = [{
     supportedMethods: "basic-card",
     data: {
       supportedNetworks: ["myNetwork"],
     },
   }];
 
@@ -76,210 +90,304 @@ https://bugzilla.mozilla.org/show_bug.cg
           currency: "USD",
           value: "30.00"
         },
         selected: false,
       },
     ],
   };
 
+  const updateDetails = {
+    total: {
+      label: "Total",
+      amount: {
+        currency: "USD",
+        value: "1.00"
+      }
+    },
+    shippingOptions: [
+      {
+        id: "NormalShipping",
+        label: "NormalShipping",
+        amount: {
+          currency: "USD",
+          value: "10.00"
+        },
+        selected: true,
+      },
+      {
+        id: "FastShipping",
+        label: "FastShipping",
+        amount: {
+          currency: "USD",
+          value: "30.00"
+        },
+        selected: false,
+      },
+    ],
+    error: "",
+  };
+
   const defaultOptions = {
     requestPayerName: true,
     requestPayerEmail: false,
     requestPayerPhone: false,
     requestShipping: true,
     shippingType: "shipping"
   };
 
-  function testBasicCardRequestWithErrorNetworks() {
-    return new Promise((resolve, reject) => {
+  async function testBasicCardRequestWithErrorNetworks() {
+    const testName = "testBasicCardRequestWithErrorNetworks";
+    return new Promise(async (resolve) => {
       try {
-        const payRequest = new PaymentRequest(errorNetworksMethods, defaultDetails, defaultOptions);
-        ok(false, "Expected 'TypeError', but got success construction.");
-        resolve();
+        const request = new PaymentRequest(errorNetworksMethods, defaultDetails, defaultOptions);
+        ok(false, `${testName}: Expected 'TypeError', but got success construction.`);
       } catch (e) {
-        is(e.name, "TypeError", "Expected TypeError, but got " + e.name);
-        resolve();
+        is(e.name, "TypeError", `${testName}: Expected TypeError, but got ${e.name}`);
       }
+      resolve();
     });
   }
 
-  function testBasicCardRequestWithUnconvertableData() {
-    return new Promise((resolve, reject) => {
+  async function testBasicCardRequestWithUnconvertableData() {
+    const testName = "testBasicCardRequestWithUnconvertableData";
+    return new Promise(async (resolve) => {
       try {
-        const payRequest = new PaymentRequest(unconvertableDataMethods, defaultDetails, defaultOptions);
-        ok(false, "Expected 'TypeError', but got success construction.");
-        resolve();
+        const request = new PaymentRequest(unconvertableDataMethods, defaultDetails, defaultOptions);
+        ok(false, `${testName}: Expected 'TypeError', but got success construction.`);
       } catch (e) {
-        is(e.name, "TypeError", "Expected TypeError, but got " + e.name);
-        resolve();
+        is(e.name, "TypeError", `${testName}: Expected TypeError, but got ${e.name}`);
       }
+      resolve();
+    });
+  }
+
+  async function testBasicCardRequestWithNullData() {
+    const testName = "testBasicCardRequestWithNullData";
+    return new Promise(async (resolve) => {
+      try {
+        const request = new PaymentRequest(nullDataMethods, defaultDetails, defaultOptions);
+        ok(request, `${testName}: PaymentRequest should be constructed with null data BasicCardRequest.`);
+      } catch (e) {
+        ok(false, `${testName}: Unexpected error: ${e.name}`);
+      }
+      resolve();
     });
   }
 
-  function testBasicCardRequestWithNullData() {
-    return new Promise((resolve, reject) => {
+  async function testBasicCardRequestWithEmptyData() {
+    const testName = "testBasicCardRequestWithEmptyData";
+    return new Promise(async (resolve) => {
       try {
-        const payRequest = new PaymentRequest(nullDataMethods, defaultDetails, defaultOptions);
-        ok(payRequest, "PaymentRequest should be constructed with null data BasicCardRequest.");
-        resolve();
+        const request = new PaymentRequest(emptyDataMethods, defaultDetails, defaultOptions);
+        ok(request, `${testName}: PaymentRequest should be constructed with empty data BasicCardRequest.`);
       } catch (e) {
-        ok(false, "Unexpected error: " + e.name);
-        resolve();
+        ok(false, `${testName}: Unexpected error: ${e.name}`);
       }
+      resolve();
     });
   }
 
-  function testBasicCardRequestWithEmptyData() {
-    return new Promise((resolve, reject) => {
+  async function testCanMakePaymentWithBasicCardRequest() {
+    const testName = "testCanMakePaymentWithBasicCardRequest";
+    return new Promise(async (resolve) => {
+      const request = new PaymentRequest(defaultMethods, defaultDetails, defaultOptions);
       try {
-        const payRequest = new PaymentRequest(emptyDataMethods, defaultDetails, defaultOptions);
-        ok(payRequest, "PaymentRequest should be constructed with empty data BasicCardRequest.");
-        resolve();
+        const result = await request.canMakePayment();
+        ok(result, `${testName}: canMakePayment() should be resolved with true.`);
       } catch (e) {
-        ok(false, "Unexpected error: " + e.name);
-        resolve();
+        ok(false, `${testName}: Unexpected error: ${e.name}`);
       }
+      resolve();
     });
   }
 
-  function testCanMakePaymentWithBasicCardRequest() {
-    return new Promise((resolve, reject) => {
-      const payRequest = new PaymentRequest(defaultMethods, defaultDetails, defaultOptions);
-      payRequest.canMakePayment().then( result => {
-        ok(result, "Should be resolved with true, but got false.");
-        resolve();
-      }).catch (e => {
-        ok(false, "Unexpected error: " + e.name);
-        resolve();
-      });
+  async function testBasicCardSimpleResponse() {
+    const testName = "testBasicCardSimpleResponse";
+    await requestChromeAction("set-simple-ui-service", testName);
+    return new Promise(async (resolve) => {
+      const request = new PaymentRequest(defaultMethods, defaultDetails, defaultOptions);
+      const handler = SpecialPowers.getDOMWindowUtils(window).setHandlingUserInput(true);
+      try {
+        const response = await request.show();
+        ok(response.details, `${testName}: basiccard response should exists.`);
+        ok(!response.details.cardholderName, `${testName}: response.details.cardholderName should not exist.`);
+        is(response.details.cardNumber, "4916855166538720",
+           `${testName}: response.details.cardNumber should be '4916855166538720'.`);
+        ok(!response.details.expiryMonth, `${testName}: response.details.expiryMonth should not exist.`);
+        ok(!response.details.expiryYear, `${testName}: response.details.expiryYear should be '2024'.`);
+        ok(!response.details.cardSecurityCode, `${testName}: response.details.cardSecurityCode should not exist.`);
+        ok(!response.details.billingAddress, `${testName}: response.details.billingAddress should not exist.`);
+        await response.complete("success");
+      } catch (e) {
+        ok(false, `${testName}: Unexpected error: ${e.name}`);
+      }
+      await handler.destruct();
+      resolve();
     });
   }
 
-  function testBasicCardSimpleResponse() {
-    const handler = SpecialPowers.getDOMWindowUtils(window).setHandlingUserInput(true);
+  async function testBasicCardDetailedResponse() {
+    const testName = "testBasicCardDetailedResponse";
+    await requestChromeAction("set-detailed-ui-service", testName);
+    return new Promise(async (resolve) => {
+      const request = new PaymentRequest(defaultMethods, defaultDetails, defaultOptions);
+      const handler = SpecialPowers.getDOMWindowUtils(window).setHandlingUserInput(true);
+      try {
+        const response = await request.show();
+        ok(response.details, `${testName}: basiccard response should exists.`);
+        ok(response.details.cardholderName, `${testName}: response.details.cardholderName should not exist.`);
+        is(response.details.cardNumber, "4916855166538720",
+           `${testName}: response.details.cardNumber should be '4916855166538720'.`);
+        ok(response.details.expiryMonth, `${testName}: response.details.expiryMonth should not exist.`);
+        ok(response.details.expiryYear, `${testName}: response.details.expiryYear should be '2024'.`);
+        ok(response.details.cardSecurityCode, `${testName}: response.details.cardSecurityCode should not exist.`);
+        ok(response.details.billingAddress, `${testName}: response.details.billingAddress should not exist.`);
+        const billingAddress = response.details.billingAddress;
+        is(billingAddress.country, "USA", `${testName}: country should be 'USA'.`);
+        is(billingAddress.addressLine.length, 1, `${testName}: addressLine.length should be 1.`);
+        is(billingAddress.addressLine[0], "Easton Ave", `${testName}: addressLine[0] should be 'Easton Ave'.`);
+        is(billingAddress.region, "CA", `${testName}: region should be 'CA'.`);
+        is(billingAddress.regionCode, "CA", `${testName}: regionCode should be 'CA'.`);
+        is(billingAddress.city, "San Bruno", `${testName}: city should be 'San Bruno'.`);
+        is(billingAddress.dependentLocality, "", `${testName}: dependentLocality should be empty.`);
+        is(billingAddress.postalCode, "94066", `${testName}: postalCode should be '94066'.`);
+        is(billingAddress.sortingCode, "123456", `${testName}: sortingCode should be '123456'.`);
+        is(billingAddress.organization, "", `${testName}: organization should be empty.`);
+        is(billingAddress.recipient, "Bill A. Pacheco", `${testName}: recipient should be 'Bill A. Pacheco'.`);
+        is(billingAddress.phone, "+14344413879", `${testName}: phone should be '+14344413879'.`);
+        await response.complete("success");
+      } catch (e) {
+        ok(false, `${testName}: Unexpected error: ${e.name}`);
+      }
+      await handler.destruct();
+      resolve();
+    });
+  }
 
-    gScript.sendAsyncMessage("set-simple-ui-service");
-    return new Promise((resolve, reject) => {
-      const payRequest = new PaymentRequest(defaultMethods, defaultDetails, defaultOptions);
-      payRequest.show().then(response => {
-        ok(response.details, "basiccard response should exists.");
-        ok(!response.details.cardholderName, "response.details.cardholderName should not exist.");
-        is(response.details.cardNumber, "4916855166538720", "response.details.cardNumber should be '4916855166538720'.");
-        ok(!response.details.expiryMonth, "response.details.expiryMonth should not exist.");
-        ok(!response.details.expiryYear, "response.details.expiryYear should be '2024'.");
-        ok(!response.details.cardSecurityCode, "response.details.cardSecurityCode should not exist.");
-        ok(!response.details.billingAddress, "response.details.billingAddress should not exist.");
-        response.complete("success").then(() =>{
-          resolve();
-        }).catch(e => {
-          ok(false, "Unexpected error: " + e.name);
-          resolve();
-        });
-      }).catch( e => {
-        ok(false, "Unexpected error: " + e.name);
-        resolve();
-      }).finally(handler.destruct);
+  async function testSpecialAddressResponse() {
+    const testName = "testSpecialAddressResponse";
+    await requestChromeAction("set-special-address-ui-service", testName);
+    return new Promise(async (resolve) => {
+      const request = new PaymentRequest(defaultMethods, defaultDetails, defaultOptions);
+      const handler = SpecialPowers.getDOMWindowUtils(window).setHandlingUserInput(true);
+      try {
+        const response = await request.show();
+        ok(response.details, `${testName}: BasiccardResponse should exist.`);
+        ok(response.details.billingAddress,
+           `${testName}: BasiccardResponse.billingAddress should exist.`);
+        is(response.details.billingAddress.addressLine[0], ":$%@&*",
+           `${testName}: AddressLine should be ':$%@&*'`);
+        await response.complete("success");
+      } catch (e) {
+        ok(false, `${testName}: Unexpected error: ${e.name}`);
+      }
+      await handler.destruct();
+      resolve();
     });
   }
 
-  function testBasicCardDetailedResponse() {
-    const handler = SpecialPowers.getDOMWindowUtils(window).setHandlingUserInput(true);
-
-    gScript.sendAsyncMessage("set-detailed-ui-service");
-    return new Promise((resolve, reject) => {
-      const payRequest = new PaymentRequest(defaultMethods, defaultDetails, defaultOptions);
-      payRequest.show().then(response => {
-        ok(response.details, "basiccard response should exists.");
-        is(response.details.cardholderName, "Bill A. Pacheco", "response.details.cardholderName should be 'Bill A. Pacheco'.");
-        is(response.details.cardNumber, "4916855166538720", "response.details.cardNumber should be '4916855166538720'.");
-        is(response.details.expiryMonth, "01", "response.details.expiryMonth should be '01'.");
-        is(response.details.expiryYear, "2024", "response.details.expiryYear should be '2024'.");
-        is(response.details.cardSecurityCode, "180", "response.details.cardSecurityCode should be '180'.");
-        const billingAddress = response.details.billingAddress;
-        is(billingAddress.country, "USA", "country should be 'USA'.");
-        is(billingAddress.addressLine.length, 1, "addressLine.length should be 1.");
-        is(billingAddress.addressLine[0], "Easton Ave", "addressLine[0] should be 'Easton Ave'.");
-        is(billingAddress.region, "CA", "region should be 'CA'.");
-        is(billingAddress.regionCode, "CA", "regionCode should be 'CA'.");
-        is(billingAddress.city, "San Bruno", "city should be 'San Bruno'.");
-        is(billingAddress.dependentLocality, "", "dependentLocality should be empty.");
-        is(billingAddress.postalCode, "94066", "postalCode should be '94066'.");
-        is(billingAddress.sortingCode, "123456", "sortingCode should be '123456'.");
-        is(billingAddress.organization, "", "organization should be empty." );
-        is(billingAddress.recipient, "Bill A. Pacheco", "recipient should be 'Bill A. Pacheco'.");
-        is(billingAddress.phone, "+14344413879", "phone should be '+14344413879'.");
-        response.complete("success").then(() =>{
-          resolve();
-        }).catch(e => {
-          ok(false, "Unexpected error: " + e.name);
-          resolve();
-        });
-      }).catch( e => {
-        ok(false, "Unexpected error: " + e.name);
-        resolve();
-      }).finally(handler.destruct);
+  async function testMethodChangeWithoutRequestBillingAddress() {
+    const testName = "testMethodChangeWithoutRequestBillingAddress";
+    await requestChromeAction("method-change-to-basic-card", testName);
+    return new Promise(async (resolve) => {
+      const request = new PaymentRequest(defaultMethods, defaultDetails, defaultOptions);
+      request.addEventListener("paymentmethodchange", async (event) => {
+        is(event.methodName, "basic-card", `${testName}: PaymentMethodChangeEvent.methodName should be 'basic-card'.`)
+        ok(event.methodDetails, `PaymentMethodChangeEvent.methodDetails should exist.`);
+        ok(!event.methodDetails.billingAddress, `PaymentMethodChangeEvent.methodDetails.billingAddres should not exist.`);
+        event.updateWith(updateDetails);
+      });
+      const handler = SpecialPowers.getDOMWindowUtils(window).setHandlingUserInput(true);
+      try {
+        const response = await request.show();
+        await response.complete("success");
+      } catch (error) {
+        ok(false, `${testName}: Unexpected error: ${error.name}`);
+      }
+      await handler.destruct();
+      resolve();
     });
   }
 
-  function testSpecialAddressResponse() {
-    const handler = SpecialPowers.getDOMWindowUtils(window).setHandlingUserInput(true);
-    gScript.sendAsyncMessage("set-special-address-ui-service");
-    return new Promise((resolve, reject) => {
-      const payRequest = new PaymentRequest(defaultMethods, defaultDetails, defaultOptions);
-      payRequest.show().then(response => {
-        ok(response.details, "BasiccardResponse should exist.");
-        ok(response.details.billingAddress,
-           "BasiccardResponse.billingAddress should exist.");
-        is(response.details.billingAddress.addressLine[0], ":$%@&*",
-           "AddressLine should be ':$%@&*'");
-        response.complete("success").then(()=>{
-          resolve();
-        });
-      }).finally(handler.destruct);
+  async function testMethodChangeWithRequestBillingAddress() {
+    const testName = "testMethodChangeWithRequestBillingAddress";
+    await requestChromeAction("method-change-to-basic-card", testName);
+    return new Promise(async (resolve) => {
+      const options = {
+        requestPayerName: true,
+        requestBillingAddress: true,
+        requestShipping: true,
+        shippingType: "shipping",
+      };
+      const request = new PaymentRequest(defaultMethods, defaultDetails, options);
+      request.addEventListener("paymentmethodchange", async (event) => {
+        is(event.methodName, "basic-card", `${testName}: PaymentMethodChangeEvent.methodName should be 'basic-card'.`)
+        ok(event.methodDetails, `PaymentMethodChangeEvent.methodDetails should exist.`);
+        const billingAddress = event.methodDetails.billingAddress;
+        is(billingAddress.country, "USA", `${testName}: country should be 'USA'.`);
+        is(billingAddress.addressLine.length, 1, `${testName}: addressLine.length should be 1.`);
+        is(billingAddress.addressLine[0], "Easton Ave", `${testName}: addressLine[0] should be 'Easton Ave'.`);
+        is(billingAddress.region, "CA", `${testName}: region should be 'CA'.`);
+        is(billingAddress.regionCode, "CA", `${testName}: regionCode should be 'CA'.`);
+        is(billingAddress.city, "San Bruno", `${testName}: city should be 'San Bruno'.`);
+        is(billingAddress.dependentLocality, "", `${testName}: dependentLocality should be empty.`);
+        is(billingAddress.postalCode, "94066", `${testName}: postalCode should be '94066'.`);
+        is(billingAddress.sortingCode, "123456", `${testName}: sortingCode should be '123456'.`);
+        is(billingAddress.organization, "", `${testName}: organization should be empty.`);
+        is(billingAddress.recipient, "Bill A. Pacheco", `${testName}: recipient should be 'Bill A. Pacheco'.`);
+        is(billingAddress.phone, "+14344413879", `${testName}: phone should be '+14344413879'.`);
+        event.updateWith(updateDetails);
+      });
+      const handler = SpecialPowers.getDOMWindowUtils(window).setHandlingUserInput(true);
+      try {
+        const response = await request.show();
+        await response.complete("success");
+      } catch (error) {
+        ok(false, `${testName}: Unexpected error: ${error.name}`);
+      }
+      await handler.destruct();
+      resolve();
     });
   }
 
-  function testBasicCardErrorResponse() {
-    return new Promise((resolve, reject) => {
-      gScript.addMessageListener("error-response-complete",
-                                 function errorResponseCompleteHandler() {
-        gScript.removeMessageListener("error-response-complete",
-                                      errorResponseCompleteHandler);
-        resolve();
-      });
-      gScript.sendAsyncMessage("error-response-test");
-    });
+
+  async function testBasicCardErrorResponse() {
+    const testName = "testBasicCardErrorResponse";
+    return requestChromeAction("error-response-test", testName);
   }
 
-  function teardown() {
+  async function teardown() {
     gScript.addMessageListener("teardown-complete", function teardownCompleteHandler() {
       gScript.removeMessageListener("teardown-complete", teardownCompleteHandler);
       gScript.removeMessageListener("test-fail", testFailHandler)
       gScript.destroy();
       SimpleTest.finish();
     });
     gScript.sendAsyncMessage("teardown");
   }
 
-  function runTests() {
-    testBasicCardRequestWithErrorNetworks()
-    .then(testBasicCardRequestWithUnconvertableData)
-    .then(testBasicCardRequestWithNullData)
-    .then(testBasicCardRequestWithEmptyData)
-    .then(testCanMakePaymentWithBasicCardRequest)
-    .then(testBasicCardSimpleResponse)
-    .then(testBasicCardDetailedResponse)
-    .then(testSpecialAddressResponse)
-    .then(testBasicCardErrorResponse)
-    .then(teardown)
-    .catch( e => {
-      ok(false, "Unexpected error: " + e.name);
+  async function runTests() {
+    try {
+      await testBasicCardRequestWithErrorNetworks();
+      await testBasicCardRequestWithUnconvertableData();
+      await testBasicCardRequestWithNullData();
+      await testBasicCardRequestWithEmptyData();
+      await testCanMakePaymentWithBasicCardRequest();
+      await testBasicCardSimpleResponse();
+      await testBasicCardDetailedResponse();
+      await testSpecialAddressResponse();
+      await testBasicCardErrorResponse();
+      await testMethodChangeWithoutRequestBillingAddress();
+      await testMethodChangeWithRequestBillingAddress()
+      await teardown();
+    } catch (e) {
+      ok(false, `test_basiccard.html: Unexpected error: ${e.name}`);
       SimpleTest.finish();
-    });
+    };
   }
 
   window.addEventListener('load', function() {
     SpecialPowers.pushPrefEnv({
       'set': [
         ['dom.payments.request.enabled', true],
       ]
     }, runTests);
--- a/dom/webidl/BasicCardPayment.webidl
+++ b/dom/webidl/BasicCardPayment.webidl
@@ -15,16 +15,20 @@ dictionary BasicCardResponse {
            DOMString cardholderName;
   required DOMString cardNumber;
            DOMString expiryMonth;
            DOMString expiryYear;
            DOMString cardSecurityCode;
            PaymentAddress? billingAddress;
 };
 
+dictionary BasicCardChangeDetails {
+  PaymentAddress? billingAddress;
+};
+
 dictionary BasicCardErrors {
   DOMString cardNumber;
   DOMString cardholderName;
   DOMString cardSecurityCode;
   DOMString expiryMonth;
   DOMString expiryYear;
   AddressErrors billingAddress;
 };
--- a/dom/webidl/PaymentRequest.webidl
+++ b/dom/webidl/PaymentRequest.webidl
@@ -93,16 +93,17 @@ enum PaymentShippingType {
   "pickup"
 };
 
 dictionary PaymentOptions {
   boolean             requestPayerName = false;
   boolean             requestPayerEmail = false;
   boolean             requestPayerPhone = false;
   boolean             requestShipping = false;
+  boolean             requestBillingAddress = false;
   PaymentShippingType shippingType = "shipping";
 };
 
 [Constructor(sequence<PaymentMethodData> methodData, PaymentDetailsInit details,
              optional PaymentOptions options),
  SecureContext,
  Func="mozilla::dom::PaymentRequest::PrefEnabled"]
 interface PaymentRequest : EventTarget {
--- a/gfx/cairo/cairo/src/moz.build
+++ b/gfx/cairo/cairo/src/moz.build
@@ -29,16 +29,17 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] not in (
         'cairo-pdf-surface.c',
         'cairo-truetype-subset.c',
         # cairo-type1-subset.c should be here, but it's only supported on freetype platforms
         'cairo-type1-fallback.c',
         'cairo-type3-glyph-surface.c',
     ]
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
+    DEFINES['MOZ_DISABLE_WINDOWS_WRAPPER'] = True
     EXPORTS.cairo += [
         'cairo-win32.h',
     ]
     SOURCES += [
         'cairo-dwrite-font.cpp',
     ]
     if CONFIG['MOZ_ENABLE_D2D_SURFACE']:
         SOURCES += [
--- a/gfx/gl/GLConsts.h
+++ b/gfx/gl/GLConsts.h
@@ -1,17 +1,15 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef GLCONSTS_H_
 #define GLCONSTS_H_
 
-// clang-format off
-
 /**
  * GENERATED FILE, DO NOT MODIFY DIRECTLY.
  * This is a file generated directly from the official OpenGL registry
  * xml available http://www.opengl.org/registry/#specfiles.
  *
  * To generate this file, see tutorial in 'GLParseRegistryXML.py'.
  */
 
@@ -6996,11 +6994,9 @@
 #define LOCAL_WGL_VIDEO_OUT_COLOR_NV                         0x20C3
 #define LOCAL_WGL_VIDEO_OUT_DEPTH_NV                         0x20C5
 #define LOCAL_WGL_VIDEO_OUT_FIELD_1                          0x20C9
 #define LOCAL_WGL_VIDEO_OUT_FIELD_2                          0x20CA
 #define LOCAL_WGL_VIDEO_OUT_FRAME                            0x20C8
 #define LOCAL_WGL_VIDEO_OUT_STACKED_FIELDS_1_2               0x20CB
 #define LOCAL_WGL_VIDEO_OUT_STACKED_FIELDS_2_1               0x20CC
 
-// clang-format on
-
 #endif // GLCONSTS_H_
--- a/gfx/gl/GLContext.cpp
+++ b/gfx/gl/GLContext.cpp
@@ -1501,17 +1501,17 @@ GLContext::LoadMoreSymbols(const char* p
             { (PRFuncPtr*) &mSymbols.fFinishFence,  { "FinishFenceNV",  nullptr } },
             { (PRFuncPtr*) &mSymbols.fIsFence,      { "IsFenceNV",      nullptr } },
             { (PRFuncPtr*) &mSymbols.fGetFenceiv,   { "GetFenceivNV",   nullptr } },
             END_SYMBOLS
         };
         fnLoadForExt(symbols, NV_fence);
     }
 
-// clang-format off
+// clang-format on
 
     if (IsExtensionSupported(NV_texture_barrier)) {
         const SymLoadStruct symbols[] = {
             { (PRFuncPtr*) &mSymbols.fTextureBarrier, { "TextureBarrierNV", nullptr } },
             END_SYMBOLS
         };
         fnLoadForExt(symbols, NV_texture_barrier);
     }
--- a/gfx/gl/GLParseRegistryXML.py
+++ b/gfx/gl/GLParseRegistryXML.py
@@ -50,18 +50,16 @@ class GLConstHeader:
         self.write([
             '/* This Source Code Form is subject to the terms of the Mozilla Public',
             ' * License, v. 2.0. If a copy of the MPL was not distributed with this',
             ' * file, You can obtain one at http://mozilla.org/MPL/2.0/. */',
             '',
             '#ifndef GLCONSTS_H_',
             '#define GLCONSTS_H_',
             '',
-            '// clang-format off',
-            '',
             '/**',
             ' * GENERATED FILE, DO NOT MODIFY DIRECTLY.',
             ' * This is a file generated directly from the official OpenGL registry',
             ' * xml available http://www.opengl.org/registry/#specfiles.',
             ' *',
             ' * To generate this file, see tutorial in \'GLParseRegistryXML.py\'.',
             ' */',
             ''
@@ -86,18 +84,16 @@ class GLConstHeader:
 
     def formatLibEnd(self, lib):
         # lib would be 'GL', 'EGL', 'GLX' or 'WGL'
         self.write(2)
 
     def formatFileEnd(self):
         self.write([
             '',
-            '// clang-format on',
-            '',
             '#endif // GLCONSTS_H_'
         ])
 
 
 ################################################################################
 # underground code
 
 def getScriptDir():
--- a/gfx/thebes/gfxDWriteFonts.cpp
+++ b/gfx/thebes/gfxDWriteFonts.cpp
@@ -256,17 +256,17 @@ gfxDWriteFont::ComputeMetrics(AntialiasO
         }
     }
 
     mMetrics->internalLeading = std::max(mMetrics->maxHeight - mMetrics->emHeight, 0.0);
     mMetrics->externalLeading = ceil(fontMetrics.lineGap * mFUnitsConvFactor);
 
     UINT32 ucs = L' ';
     UINT16 glyph;
-    HRESULT hr = mFontFace->GetGlyphIndicesW(&ucs, 1, &glyph);
+    HRESULT hr = mFontFace->GetGlyphIndices(&ucs, 1, &glyph);
     if (FAILED(hr)) {
         mMetrics->spaceWidth = 0;
     } else {
         mSpaceGlyph = glyph;
         mMetrics->spaceWidth = MeasureGlyphWidth(glyph);
     }
 
     // try to get aveCharWidth from the OS/2 table, fall back to measuring 'x'
@@ -286,27 +286,27 @@ gfxDWriteFont::ComputeMetrics(AntialiasO
                 mMetrics->aveCharWidth =
                     int16_t(os2->xAvgCharWidth) * mFUnitsConvFactor;
             }
         }
     }
 
     if (mMetrics->aveCharWidth < 1) {
         ucs = L'x';
-        if (SUCCEEDED(mFontFace->GetGlyphIndicesW(&ucs, 1, &glyph))) {
+        if (SUCCEEDED(mFontFace->GetGlyphIndices(&ucs, 1, &glyph))) {
             mMetrics->aveCharWidth = MeasureGlyphWidth(glyph);
         }
         if (mMetrics->aveCharWidth < 1) {
             // Let's just assume the X is square.
             mMetrics->aveCharWidth = fontMetrics.xHeight * mFUnitsConvFactor;
         }
     }
 
     ucs = L'0';
-    if (SUCCEEDED(mFontFace->GetGlyphIndicesW(&ucs, 1, &glyph))) {
+    if (SUCCEEDED(mFontFace->GetGlyphIndices(&ucs, 1, &glyph))) {
         mMetrics->zeroOrAveCharWidth = MeasureGlyphWidth(glyph);
     }
     if (mMetrics->zeroOrAveCharWidth < 1) {
         mMetrics->zeroOrAveCharWidth = mMetrics->aveCharWidth;
     }
 
     mMetrics->underlineOffset =
         fontMetrics.underlinePosition * mFUnitsConvFactor;
--- a/gfx/vr/service/openvr/moz.build
+++ b/gfx/vr/service/openvr/moz.build
@@ -3,16 +3,19 @@
 # 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/.
 
 FINAL_LIBRARY = 'xul'
 
 DEFINES['VR_API_PUBLIC'] = True
 
+# Windows.h wrappers conflict with internal methods in openvr
+DEFINES['MOZ_DISABLE_WINDOWS_WRAPPER'] = True
+
 if CONFIG['OS_ARCH'] == 'WINNT':
     if CONFIG['HAVE_64BIT_BUILD']:
         DEFINES['WIN64'] = True
     else:
         DEFINES['WIN32'] = True
 
 if CONFIG['OS_ARCH'] == 'Darwin':
     DEFINES['POSIX'] = True
--- a/image/SurfaceFilters.h
+++ b/image/SurfaceFilters.h
@@ -410,19 +410,19 @@ public:
 
     // Clamp mFrameRect to the output size.
     gfx::IntRect outputRect(0, 0, outputSize.width, outputSize.height);
     mFrameRect = mFrameRect.Intersect(outputRect);
     bool fullFrame = outputRect.IsEqualEdges(mFrameRect);
 
     // If there's no intersection, |mFrameRect| will be an empty rect positioned
     // at the maximum of |inputRect|'s and |aFrameRect|'s coordinates, which is
-    // not what we want. Force it to (0, 0) in that case.
+    // not what we want. Force it to (0, 0) sized 0 x 0 in that case.
     if (mFrameRect.IsEmpty()) {
-      mFrameRect.MoveTo(0, 0);
+      mFrameRect.SetRect(0, 0, 0, 0);
     }
 
     BlendMethod blendMethod = currentFrame->GetBlendMethod();
     switch (blendMethod) {
       default:
         blendMethod = BlendMethod::SOURCE;
         MOZ_FALLTHROUGH_ASSERT("Unexpected blend method!");
       case BlendMethod::SOURCE:
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..05b141ba5f575095aef08a04abb92fa63edeff59
GIT binary patch
literal 92
zc${<hbhEHbWMp7uXkcLY4+e_=x&2&2f}I@$T#fV$m>C%u7!-f9FmN&aXV3w0K#Ccd
Um|Pe^!i-!%o(=;-*asmD084)nsQ>@~
--- a/image/test/crashtests/crashtests.list
+++ b/image/test/crashtests/crashtests.list
@@ -50,8 +50,11 @@ load invalid_ico_width.ico
 
 # Bug 525326 - Test image sizes of 65535x65535 which is larger than we allow)
 load invalid-size.gif
 load invalid-size-second-frame.gif
 
 load multiple-png-hassize.ico # Bug 863958 - This icon's size is such that it leads to multiple writes to the PNG decoder after we've gotten our size.
 asserts(0-2) load ownerdiscard.html # Bug 1323672, bug 807211
 load truncated-second-frame.png # Bug 863975
+
+# Bug 1509998 - Ensure that we handle empty frame rects in animated images.
+load 1509998.gif
--- a/js/src/builtin/intl/IntlObject.cpp
+++ b/js/src/builtin/intl/IntlObject.cpp
@@ -15,16 +15,17 @@
 #include "jsapi.h"
 
 #include "builtin/intl/Collator.h"
 #include "builtin/intl/CommonFunctions.h"
 #include "builtin/intl/DateTimeFormat.h"
 #include "builtin/intl/ICUStubs.h"
 #include "builtin/intl/NumberFormat.h"
 #include "builtin/intl/PluralRules.h"
+#include "builtin/intl/RelativeTimeFormat.h"
 #include "builtin/intl/ScopedICUObject.h"
 #include "js/CharacterEncoding.h"
 #include "js/Class.h"
 #include "js/StableStringChars.h"
 #include "vm/GlobalObject.h"
 #include "vm/JSContext.h"
 #include "vm/JSObject.h"
 #include "vm/StringType.h"
@@ -567,16 +568,20 @@ GlobalObject::initIntlObject(JSContext* 
     numberFormatProto = CreateNumberFormatPrototype(cx, intl, global, &numberFormat);
     if (!numberFormatProto) {
         return false;
     }
     RootedObject pluralRulesProto(cx, CreatePluralRulesPrototype(cx, intl, global));
     if (!pluralRulesProto) {
         return false;
     }
+    RootedObject relativeTimeFmtProto(cx, CreateRelativeTimeFormatPrototype(cx, intl, global));
+    if (!relativeTimeFmtProto) {
+        return false;
+    }
 
     // The |Intl| object is fully set up now, so define the global property.
     RootedValue intlValue(cx, ObjectValue(*intl));
     if (!DefineDataProperty(cx, global, cx->names().Intl, intlValue, JSPROP_RESOLVING)) {
         return false;
     }
 
     // Now that the |Intl| object is successfully added, we can OOM-safely fill
@@ -589,16 +594,17 @@ GlobalObject::initIntlObject(JSContext* 
     // |getPrototype(JSProto_*)|, but that has global-object-property-related
     // baggage we don't need or want, so we use one-off reserved slots.
     global->setReservedSlot(COLLATOR_PROTO, ObjectValue(*collatorProto));
     global->setReservedSlot(DATE_TIME_FORMAT, ObjectValue(*dateTimeFormat));
     global->setReservedSlot(DATE_TIME_FORMAT_PROTO, ObjectValue(*dateTimeFormatProto));
     global->setReservedSlot(NUMBER_FORMAT, ObjectValue(*numberFormat));
     global->setReservedSlot(NUMBER_FORMAT_PROTO, ObjectValue(*numberFormatProto));
     global->setReservedSlot(PLURAL_RULES_PROTO, ObjectValue(*pluralRulesProto));
+    global->setReservedSlot(RELATIVE_TIME_FORMAT_PROTO, ObjectValue(*relativeTimeFmtProto));
 
     // Also cache |Intl| to implement spec language that conditions behavior
     // based on values being equal to "the standard built-in |Intl| object".
     // Use |setConstructor| to correspond with |JSProto_Intl|.
     //
     // XXX We should possibly do a one-off reserved slot like above.
     global->setConstructor(JSProto_Intl, ObjectValue(*intl));
     return true;
--- a/js/src/builtin/intl/RelativeTimeFormat.cpp
+++ b/js/src/builtin/intl/RelativeTimeFormat.cpp
@@ -170,48 +170,16 @@ js::CreateRelativeTimeFormatPrototype(JS
     RootedValue ctorValue(cx, ObjectValue(*ctor));
     if (!DefineDataProperty(cx, Intl, cx->names().RelativeTimeFormat, ctorValue, 0)) {
         return nullptr;
     }
 
     return proto;
 }
 
-/* static */ bool
-js::GlobalObject::addRelativeTimeFormatConstructor(JSContext* cx, HandleObject intl)
-{
-    Handle<GlobalObject*> global = cx->global();
-
-    {
-        const HeapSlot& slot = global->getReservedSlotRef(RELATIVE_TIME_FORMAT_PROTO);
-        if (!slot.isUndefined()) {
-            MOZ_ASSERT(slot.isObject());
-            JS_ReportErrorASCII(cx,
-                                "the RelativeTimeFormat constructor can't be added "
-                                "multiple times in the same global");
-            return false;
-        }
-    }
-
-    JSObject* relativeTimeFormatProto = CreateRelativeTimeFormatPrototype(cx, intl, global);
-    if (!relativeTimeFormatProto) {
-        return false;
-    }
-
-    global->setReservedSlot(RELATIVE_TIME_FORMAT_PROTO, ObjectValue(*relativeTimeFormatProto));
-    return true;
-}
-
-bool
-js::AddRelativeTimeFormatConstructor(JSContext* cx, JS::Handle<JSObject*> intl)
-{
-    return GlobalObject::addRelativeTimeFormatConstructor(cx, intl);
-}
-
-
 bool
 js::intl_RelativeTimeFormat_availableLocales(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     MOZ_ASSERT(args.length() == 0);
 
     RootedValue result(cx);
     // We're going to use ULocale availableLocales as per ICU recommendation:
--- a/js/src/builtin/intl/RelativeTimeFormat.js
+++ b/js/src/builtin/intl/RelativeTimeFormat.js
@@ -33,32 +33,38 @@ function relativeTimeFormatLocaleData() 
  */
 function resolveRelativeTimeFormatInternals(lazyRelativeTimeFormatData) {
     assert(IsObject(lazyRelativeTimeFormatData), "lazy data not an object?");
 
     var internalProps = std_Object_create(null);
 
     var RelativeTimeFormat = relativeTimeFormatInternalProperties;
 
-    // Step 10.
+    // Steps 7-8.
     const r = ResolveLocale(callFunction(RelativeTimeFormat.availableLocales, RelativeTimeFormat),
                             lazyRelativeTimeFormatData.requestedLocales,
                             lazyRelativeTimeFormatData.opt,
                             RelativeTimeFormat.relevantExtensionKeys,
                             RelativeTimeFormat.localeData);
 
-    // Step 11.
+    // Steps 9-10.
     internalProps.locale = r.locale;
 
-    // Step 14.
+    // Step 11.
+    assert(r.locale === r.dataLocale,
+           "resolved locale matches the resolved data-locale when no extension-keys are present");
+
+    // Step 13.
     internalProps.style = lazyRelativeTimeFormatData.style;
 
-    // Step 16.
+    // Step 15.
     internalProps.numeric = lazyRelativeTimeFormatData.numeric;
 
+    // Steps 16-20 (Not relevant in our implementation).
+
     return internalProps;
 }
 
 /**
  * Returns an object containing the RelativeTimeFormat internal properties of |obj|,
  * or throws a TypeError if |obj| isn't RelativeTimeFormat-initialized.
  */
 function getRelativeTimeFormatInternals(obj, methodName) {
@@ -96,51 +102,51 @@ function InitializeRelativeTimeFormat(re
 
     // Lazy RelativeTimeFormat data has the following structure:
     //
     //   {
     //     requestedLocales: List of locales,
     //     style: "long" / "short" / "narrow",
     //     numeric: "always" / "auto",
     //
-    //     opt: // opt object computer in InitializeRelativeTimeFormat
+    //     opt: // opt object computed in InitializeRelativeTimeFormat
     //       {
     //         localeMatcher: "lookup" / "best fit",
     //       }
     //   }
     //
     // Note that lazy data is only installed as a final step of initialization,
     // so every RelativeTimeFormat lazy data object has *all* these properties, never a
     // subset of them.
     const lazyRelativeTimeFormatData = std_Object_create(null);
 
-    // Step 3.
+    // Step 1.
     let requestedLocales = CanonicalizeLocaleList(locales);
     lazyRelativeTimeFormatData.requestedLocales = requestedLocales;
 
-    // Steps 4-5.
+    // Steps 2-3.
     if (options === undefined)
         options = std_Object_create(null);
     else
         options = ToObject(options);
 
-    // Step 6.
+    // Step 4.
     let opt = new Record();
 
-    // Steps 7-8.
+    // Steps 5-6.
     let matcher = GetOption(options, "localeMatcher", "string", ["lookup", "best fit"], "best fit");
     opt.localeMatcher = matcher;
 
     lazyRelativeTimeFormatData.opt = opt;
 
-    // Steps 13-14.
+    // Steps 12-13.
     const style = GetOption(options, "style", "string", ["long", "short", "narrow"], "long");
     lazyRelativeTimeFormatData.style = style;
 
-    // Steps 15-16.
+    // Steps 14-15.
     const numeric = GetOption(options, "numeric", "string", ["always", "auto"], "always");
     lazyRelativeTimeFormatData.numeric = numeric;
 
     initializeIntlObject(relativeTimeFormat, "RelativeTimeFormat", lazyRelativeTimeFormatData);
 }
 
 /**
  * Returns the subset of the given locale list for which this locale list has a
@@ -223,25 +229,31 @@ function Intl_RelativeTimeFormat_format(
 }
 
 /**
  * Returns the resolved options for a RelativeTimeFormat object.
  *
  * Spec: ECMAScript 402 API, RelativeTimeFormat, 1.4.4.
  */
 function Intl_RelativeTimeFormat_resolvedOptions() {
-    var relativeTimeFormat;
-    // Check "this RelativeTimeFormat object" per introduction of section 1.4.
-    if (!IsObject(this) || (relativeTimeFormat = GuardToRelativeTimeFormat(this)) === null) {
+    // Step 1.
+    var relativeTimeFormat = this;
+
+    // Steps 2-3.
+    if (!IsObject(relativeTimeFormat) ||
+        (relativeTimeFormat = GuardToRelativeTimeFormat(relativeTimeFormat)) === null)
+    {
         return callFunction(CallRelativeTimeFormatMethodIfWrapped, this,
                             "Intl_RelativeTimeFormat_resolvedOptions");
     }
 
     var internals = getRelativeTimeFormatInternals(relativeTimeFormat, "resolvedOptions");
 
+    // Steps 4-5.
     var result = {
         locale: internals.locale,
         style: internals.style,
         numeric: internals.numeric,
     };
 
+    // Step 6.
     return result;
 }
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -2926,22 +2926,16 @@ ToWindowIfWindowProxy(JSObject* obj);
 // if passed bad input. But the current behavior is entirely under-specified
 // and emphatically not shippable on the web, and it *must* be fixed before
 // this functionality can be exposed in the real world. (There are also some
 // questions about whether the format exposed here is the *right* one to
 // standardize, that will also need to be resolved to ship this.)
 extern bool
 AddMozDateTimeFormatConstructor(JSContext* cx, JS::Handle<JSObject*> intl);
 
-// Create and add the Intl.RelativeTimeFormat constructor function to the provided
-// object.  This function throws if called more than once per realm/global
-// object.
-extern bool
-AddRelativeTimeFormatConstructor(JSContext* cx, JS::Handle<JSObject*> intl);
-
 class MOZ_STACK_CLASS JS_FRIEND_API AutoAssertNoContentJS
 {
   public:
     explicit AutoAssertNoContentJS(JSContext* cx);
     ~AutoAssertNoContentJS();
 
   private:
     JSContext* context_;
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -1242,20 +1242,16 @@ AddIntlExtras(JSContext* cx, unsigned ar
     if (!JS_DefineFunctions(cx, intl, funcs)) {
         return false;
     }
 
     if (!js::AddMozDateTimeFormatConstructor(cx, intl)) {
         return false;
     }
 
-    if (!js::AddRelativeTimeFormatConstructor(cx, intl)) {
-        return false;
-    }
-
     args.rval().setUndefined();
     return true;
 }
 #endif // ENABLE_INTL_API
 
 static MOZ_MUST_USE bool
 EvalUtf8AndPrint(JSContext* cx, const char* bytes, size_t length,
                  int lineno, bool compileOnly)
--- a/js/src/tests/non262/Intl/RelativeTimeFormat/construct-newtarget.js
+++ b/js/src/tests/non262/Intl/RelativeTimeFormat/construct-newtarget.js
@@ -1,16 +1,14 @@
-// |reftest| skip-if(!this.hasOwnProperty("Intl")||!this.hasOwnProperty("addIntlExtras"))
+// |reftest| skip-if(!this.hasOwnProperty("Intl"))
 
 /* 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/. */
 
-addIntlExtras(Intl);
-
 var obj = new Intl.RelativeTimeFormat();
 
 // Test that new RTF produces an object with the right prototype.
 assertEq(Object.getPrototypeOf(obj), Intl.RelativeTimeFormat.prototype);
 
 // Test subclassing %Intl.RelativeTimeFormat% works correctly.
 class MyRelativeTimeFormat extends Intl.RelativeTimeFormat {}
 
--- a/js/src/tests/non262/Intl/RelativeTimeFormat/cross-compartment.js
+++ b/js/src/tests/non262/Intl/RelativeTimeFormat/cross-compartment.js
@@ -1,14 +1,11 @@
-// |reftest| skip-if(!this.hasOwnProperty("Intl")||!this.hasOwnProperty("addIntlExtras"))
-
-addIntlExtras(Intl);
+// |reftest| skip-if(!this.hasOwnProperty("Intl"))
 
 var otherGlobal = newGlobal();
-otherGlobal.addIntlExtras(otherGlobal.Intl);
 
 var relativeTimeFormat = new Intl.RelativeTimeFormat();
 var ccwRelativeTimeFormat = new otherGlobal.Intl.RelativeTimeFormat();
 
 // Test Intl.RelativeTimeFormat.prototype.format with a CCW object.
 var Intl_RelativeTimeFormat_format = Intl.RelativeTimeFormat.prototype.format;
 
 assertEq(Intl_RelativeTimeFormat_format.call(ccwRelativeTimeFormat, 0, "hour"),
--- a/js/src/tests/non262/Intl/RelativeTimeFormat/format.js
+++ b/js/src/tests/non262/Intl/RelativeTimeFormat/format.js
@@ -1,19 +1,17 @@
-// |reftest| skip-if(!this.hasOwnProperty('Intl')||!this.hasOwnProperty('addIntlExtras'))
+// |reftest| skip-if(!this.hasOwnProperty('Intl'))
 /* 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/. */
 
 // Tests the format function with a diverse set of locales and options.
 
 var rtf;
 
-addIntlExtras(Intl);
-
 {
   // Numeric format
   rtf = new Intl.RelativeTimeFormat("en-US");
   assertEq(rtf.format(0, "second"), "in 0 seconds");
   assertEq(rtf.format(-0, "second"), "0 seconds ago");
   assertEq(rtf.format(-1, "second"), "1 second ago");
   assertEq(rtf.format(1, "second"), "in 1 second");
 
--- a/js/src/tests/non262/Intl/RelativeTimeFormat/relativetimeformat.js
+++ b/js/src/tests/non262/Intl/RelativeTimeFormat/relativetimeformat.js
@@ -1,17 +1,15 @@
-// |reftest| skip-if(!this.hasOwnProperty("Intl")||!this.hasOwnProperty('addIntlExtras'))
+// |reftest| skip-if(!this.hasOwnProperty("Intl"))
 /* 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/. */
 
 // Tests the format function with a diverse set of locales and options.
 
 var rtf;
 
-addIntlExtras(Intl);
-
 rtf = new Intl.RelativeTimeFormat("en-us");
 assertEq(rtf.resolvedOptions().locale, "en-US");
 assertEq(rtf.resolvedOptions().style, "long");
 assertEq(rtf.resolvedOptions().numeric, "always");
 
 reportCompare(0, 0, 'ok');
--- a/js/src/tests/non262/Intl/RelativeTimeFormat/supportedLocalesOf.js
+++ b/js/src/tests/non262/Intl/RelativeTimeFormat/supportedLocalesOf.js
@@ -1,9 +1,9 @@
-// |reftest| skip-if(!this.hasOwnProperty('Intl')||!this.hasOwnProperty('addIntlExtras')||xulRuntime.shell)
+// |reftest| skip-if(!this.hasOwnProperty('Intl')||xulRuntime.shell)
 // -- test in browser only that ICU has locale data for all Mozilla languages
 
 /* 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/. */
 
 // This array contains the locales that ICU supports in
 // number formatting whose languages Mozilla localizes Firefox into.
@@ -361,15 +361,13 @@ var locales = [
     "zh-Hans-MO",
     "zh-Hans-SG",
     "zh-Hant",
     "zh-Hant-HK",
     "zh-Hant-MO",
     "zh-Hant-TW",
 ];
 
-addIntlExtras(Intl);
-
 const result = Intl.RelativeTimeFormat.supportedLocalesOf(locales);
 
 assertEqArray(locales, result);
 
 reportCompare(0, 0, 'ok');
deleted file mode 100644
--- a/js/src/tests/test262-intl-relativetimeformat.js
+++ /dev/null
@@ -1,14 +0,0 @@
-// 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/.
-
-// Call the shell helper to add Intl.RelativeTimeFormat to the Intl object.
-if (typeof addIntlExtras === "function") {
-    let intlExtras = {};
-    addIntlExtras(intlExtras);
-
-    Object.defineProperty(Intl, "RelativeTimeFormat", {
-        value: intlExtras.RelativeTimeFormat,
-        writable: true, enumerable: false, configurable: true
-    });
-}
--- a/js/src/tests/test262-update.py
+++ b/js/src/tests/test262-update.py
@@ -368,21 +368,16 @@ def process_test262(test262Dir, test262O
     explicitIncludes[os.path.join("built-ins", "Atomics")] = ["testAtomics.js",
                                                               "testTypedArray.js"]
     explicitIncludes[os.path.join("built-ins", "DataView")] = ["byteConversionValues.js"]
     explicitIncludes[os.path.join("built-ins", "Promise")] = ["promiseHelper.js"]
     explicitIncludes[os.path.join("built-ins", "TypedArray")] = ["byteConversionValues.js",
                                                                  "detachArrayBuffer.js", "nans.js"]
     explicitIncludes[os.path.join("built-ins", "TypedArrays")] = ["detachArrayBuffer.js"]
 
-    # Intl.RelativeTimeFormat isn't yet enabled by default.
-    localIncludesMap[os.path.join("intl402", "RelativeTimeFormat")] = [
-        "test262-intl-relativetimeformat.js"
-    ]
-
     # Process all test directories recursively.
     for (dirPath, dirNames, fileNames) in os.walk(testDir):
         relPath = os.path.relpath(dirPath, testDir)
         if relPath == ".":
             continue
 
         # Skip creating a "prs" directory if it already exists
         if relPath not in ("prs", "local") and not os.path.exists(os.path.join(test262OutDir,
--- a/js/src/tests/test262/intl402/RelativeTimeFormat/shell.js
+++ b/js/src/tests/test262/intl402/RelativeTimeFormat/shell.js
@@ -1,15 +0,0 @@
-// file: test262-intl-relativetimeformat.js
-// 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/.
-
-// Call the shell helper to add Intl.RelativeTimeFormat to the Intl object.
-if (typeof addIntlExtras === "function") {
-    let intlExtras = {};
-    addIntlExtras(intlExtras);
-
-    Object.defineProperty(Intl, "RelativeTimeFormat", {
-        value: intlExtras.RelativeTimeFormat,
-        writable: true, enumerable: false, configurable: true
-    });
-}
--- a/js/src/vm/GlobalObject.h
+++ b/js/src/vm/GlobalObject.h
@@ -796,19 +796,16 @@ class GlobalObject : public NativeObject
 
     // Implemented in builtin/MapObject.cpp.
     static bool initMapIteratorProto(JSContext* cx, Handle<GlobalObject*> global);
     static bool initSetIteratorProto(JSContext* cx, Handle<GlobalObject*> global);
 
     // Implemented in builtin/intl/IntlObject.cpp.
     static bool initIntlObject(JSContext* cx, Handle<GlobalObject*> global);
 
-    // Implemented in builtin/intl/RelativeTimeFormat.cpp.
-    static bool addRelativeTimeFormatConstructor(JSContext* cx, HandleObject intl);
-
     // Implemented in builtin/ModuleObject.cpp
     static bool initModuleProto(JSContext* cx, Handle<GlobalObject*> global);
     static bool initImportEntryProto(JSContext* cx, Handle<GlobalObject*> global);
     static bool initExportEntryProto(JSContext* cx, Handle<GlobalObject*> global);
     static bool initRequestedModuleProto(JSContext* cx, Handle<GlobalObject*> global);
 
     // Implemented in builtin/TypedObject.cpp
     static bool initTypedObjectModule(JSContext* cx, Handle<GlobalObject*> global);
--- a/js/xpconnect/src/XPCWrappedJS.cpp
+++ b/js/xpconnect/src/XPCWrappedJS.cpp
@@ -66,16 +66,20 @@ nsXPCWrappedJS::CanSkip()
         return true;
     }
 
     // If this wrapper holds a gray object, need to trace it.
     JSObject* obj = GetJSObjectPreserveColor();
     if (obj && JS::ObjectIsMarkedGray(obj)) {
         return false;
     }
+    JSObject* global = GetJSObjectGlobalPreserveColor();
+    if (global && JS::ObjectIsMarkedGray(global)) {
+        return false;
+    }
 
     // For non-root wrappers, check if the root wrapper will be
     // added to the CC graph.
     if (!IsRootWrapper()) {
         // mRoot points to null after unlinking.
         NS_ENSURE_TRUE(mRoot, false);
         return mRoot->CanSkip();
     }
@@ -125,16 +129,18 @@ NS_CYCLE_COLLECTION_CLASSNAME(nsXPCWrapp
     // not subject to finalization alive.
     NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "self");
     cb.NoteXPCOMChild(s);
 
     if (tmp->IsValid()) {
         MOZ_ASSERT(refcnt > 1);
         NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mJSObj");
         cb.NoteJSChild(JS::GCCellPtr(tmp->GetJSObjectPreserveColor()));
+        NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mJSObjGlobal");
+        cb.NoteJSChild(JS::GCCellPtr(tmp->GetJSObjectGlobalPreserveColor()));
     }
 
     if (tmp->IsRootWrapper()) {
         NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "aggregated native");
         cb.NoteXPCOMChild(tmp->GetAggregatedNativeObject());
     } else {
         NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "root");
         cb.NoteXPCOMChild(ToSupports(tmp->GetRootWrapper()));
--- a/js/xpconnect/src/xpcprivate.h
+++ b/js/xpconnect/src/xpcprivate.h
@@ -1849,16 +1849,20 @@ public:
      * object returned is not guaranteed to be kept alive past the next CC.
      *
      * This should only be called if you are certain that the return value won't
      * be passed into a JS API function and that it won't be stored without
      * being rooted (or otherwise signaling the stored value to the CC).
      */
     JSObject* GetJSObjectPreserveColor() const { return mJSObj.unbarrieredGet(); }
 
+    JSObject* GetJSObjectGlobalPreserveColor() const {
+        return mJSObjGlobal.unbarrieredGet();
+    }
+
     // Returns true if the wrapper chain contains references to multiple
     // compartments. If the wrapper chain contains references to multiple
     // compartments, then it must be registered on the XPCJSContext. Otherwise,
     // it should be registered in the CompartmentPrivate for the compartment of
     // the root's JS object. This will only return correct results when called
     // on the root wrapper and will assert if not called on a root wrapper.
     bool IsMultiCompartment() const;
 
--- a/layout/forms/nsComboboxControlFrame.cpp
+++ b/layout/forms/nsComboboxControlFrame.cpp
@@ -1055,34 +1055,39 @@ nsComboboxControlFrame::HandleRedisplayT
   // Redirect frame insertions during this method (see GetContentInsertionFrame())
   // so that any reframing that the frame constructor forces upon us is inserted
   // into the correct parent (mDisplayFrame). See bug 282607.
   MOZ_ASSERT(!mInRedisplayText, "Nested RedisplayText");
   mInRedisplayText = true;
   mRedisplayTextEvent.Forget();
 
   ActuallyDisplayText(true);
+  if (!weakThis.IsAlive()) {
+    return;
+  }
+
   // XXXbz This should perhaps be eResize.  Check.
   PresShell()->FrameNeedsReflow(mDisplayFrame,
                                                nsIPresShell::eStyleChange,
                                                NS_FRAME_IS_DIRTY);
 
   mInRedisplayText = false;
 }
 
 void
 nsComboboxControlFrame::ActuallyDisplayText(bool aNotify)
 {
+  RefPtr<nsTextNode> displayContent = mDisplayContent;
   if (mDisplayedOptionTextOrPreview.IsEmpty()) {
     // Have to use a non-breaking space for line-block-size calculations
     // to be right
     static const char16_t space = 0xA0;
-    mDisplayContent->SetText(&space, 1, aNotify);
+    displayContent->SetText(&space, 1, aNotify);
   } else {
-    mDisplayContent->SetText(mDisplayedOptionTextOrPreview, aNotify);
+    displayContent->SetText(mDisplayedOptionTextOrPreview, aNotify);
   }
 }
 
 int32_t
 nsComboboxControlFrame::GetIndexOfDisplayArea()
 {
   return mDisplayedIndex;
 }
new file mode 100644
--- /dev/null
+++ b/mobile/android/config/mozconfigs/android-aarch64/debug-artifact
@@ -0,0 +1,15 @@
+. "$topsrcdir/build/mozconfig.artifact.automation"
+
+NO_CACHE=1
+NO_NDK=1
+
+. "$topsrcdir/mobile/android/config/mozconfigs/common"
+
+# Global options
+ac_add_options --enable-debug
+
+. "$topsrcdir/mobile/android/config/mozconfigs/android-aarch64/nightly"
+
+. "$topsrcdir/build/mozconfig.artifact"
+
+. "$topsrcdir/mobile/android/config/mozconfigs/common.override"
new file mode 100644
--- /dev/null
+++ b/mobile/android/config/mozconfigs/android-aarch64/nightly-artifact
@@ -0,0 +1,12 @@
+. "$topsrcdir/build/mozconfig.artifact.automation"
+
+NO_CACHE=1
+NO_NDK=1
+
+. "$topsrcdir/mobile/android/config/mozconfigs/common"
+
+. "$topsrcdir/mobile/android/config/mozconfigs/android-aarch64/nightly"
+
+. "$topsrcdir/build/mozconfig.artifact"
+
+. "$topsrcdir/mobile/android/config/mozconfigs/common.override"
new file mode 100644
--- /dev/null
+++ b/mobile/android/config/mozconfigs/android-x86_64/debug-artifact
@@ -0,0 +1,15 @@
+. "$topsrcdir/build/mozconfig.artifact.automation"
+
+NO_CACHE=1
+NO_NDK=1
+
+. "$topsrcdir/mobile/android/config/mozconfigs/common"
+
+# Global options
+ac_add_options --enable-debug
+
+. "$topsrcdir/mobile/android/config/mozconfigs/android-x86_64/nightly"
+
+. "$topsrcdir/build/mozconfig.artifact"
+
+. "$topsrcdir/mobile/android/config/mozconfigs/common.override"
--- a/mobile/android/geckoview/CHANGELOG.md
+++ b/mobile/android/geckoview/CHANGELOG.md
@@ -19,10 +19,12 @@
 - Changed `LoadRequest.isUserTriggered` to `isRedirect`.
 - Added `GeckoSession.LOAD_FLAGS_BYPASS_CLASSIFIER` to bypass the URI
   classifier.
 - Added a `protected` empty constructor to all field-only classes so that apps
   can mock these classes in tests.
 - Added `ContentDelegate.ContextElement` to extend the information passed to
   `ContentDelegate#onContextMenu`. Extended information includes the element's
   title and alt attributes.
+- Changed `ContentDelegate.ContextElement` TYPE_ constants to public access.
+  Changed `ContentDelegate.ContextElement` to non-final class.
 
-[api-version]: 8cfd04a09e7a242b3da22ccdd55c88a2aca2ba6d
+[api-version]: 01a6677fc7645b27d9824a2ebd3a6ab54c5e192a
--- a/mobile/android/geckoview/api.txt
+++ b/mobile/android/geckoview/api.txt
@@ -326,18 +326,22 @@ package org.mozilla.geckoview {
     method public void onCrash(org.mozilla.geckoview.GeckoSession);
     method public void onExternalResponse(org.mozilla.geckoview.GeckoSession, org.mozilla.geckoview.GeckoSession.WebResponseInfo);
     method public void onFirstComposite(org.mozilla.geckoview.GeckoSession);
     method public void onFocusRequest(org.mozilla.geckoview.GeckoSession);
     method public void onFullScreen(org.mozilla.geckoview.GeckoSession, boolean);
     method public void onTitleChange(org.mozilla.geckoview.GeckoSession, java.lang.String);
   }
 
-  public static final class GeckoSession.ContentDelegate.ContextElement {
+  public static class GeckoSession.ContentDelegate.ContextElement {
     ctor protected ContextElement(java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String);
+    field public static final int TYPE_AUDIO = 3;
+    field public static final int TYPE_IMAGE = 1;
+    field public static final int TYPE_NONE = 0;
+    field public static final int TYPE_VIDEO = 2;
     field public final java.lang.String altText;
     field public final java.lang.String linkUri;
     field public final java.lang.String srcUri;
     field public final java.lang.String title;
     field public final int type;
   }
 
   public static final class GeckoSession.FinderResult {
--- a/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/AccessibilityTest.kt
+++ b/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/AccessibilityTest.kt
@@ -29,19 +29,19 @@ import android.view.ViewGroup
 import android.widget.EditText
 
 import android.widget.FrameLayout
 
 import org.hamcrest.Matchers.*
 import org.junit.Test
 import org.junit.Before
 import org.junit.After
+import org.junit.Ignore
 import org.junit.runner.RunWith
 import org.mozilla.geckoview.test.rule.GeckoSessionTestRule.Setting
-import org.mozilla.geckoview.test.rule.GeckoSessionTestRule.ReuseSession
 
 const val DISPLAY_WIDTH = 480
 const val DISPLAY_HEIGHT = 640
 
 @RunWith(AndroidJUnit4::class)
 @MediumTest
 @WithDisplay(width = DISPLAY_WIDTH, height = DISPLAY_HEIGHT)
 @WithDevToolsAPI
@@ -496,17 +496,17 @@ class AccessibilityTest : BaseSessionTes
 
     private fun screenContainsNode(nodeId: Int): Boolean {
         var node = createNodeInfo(nodeId)
         var nodeBounds = Rect()
         node.getBoundsInScreen(nodeBounds)
         return screenRect.contains(nodeBounds)
     }
 
-    @ReuseSession(false)
+    @Ignore // Bug 1506276 - We need to reliably wait for APZC here, and it's not trivial.
     @Test fun testScroll() {
         var nodeId = View.NO_ID
         sessionRule.session.loadString(
                 """<body style="margin: 0;">
                         <div style="height: 100vh;"></div>
                         <button>Hello</button>
                         <p style="margin: 0;">Lorem ipsum dolor sit amet, consectetur adipiscing elit,
                             sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>
--- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoSession.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoSession.java
@@ -2579,24 +2579,23 @@ public class GeckoSession implements Par
          * @param session The GeckoSession that initiated the callback.
          * @param fullScreen True if the page is in full screen mode.
          */
         void onFullScreen(GeckoSession session, boolean fullScreen);
 
         /**
          * Element details for onContextMenu callbacks.
          */
-        public static final class ContextElement {
+        public static class ContextElement {
             @IntDef({TYPE_NONE, TYPE_IMAGE, TYPE_VIDEO, TYPE_AUDIO})
             /* package */ @interface Type {}
-            static final int TYPE_NONE = 0;
-            static final int TYPE_IMAGE = 1;
-            static final int TYPE_VIDEO = 2;
-            static final int TYPE_AUDIO = 3;
-
+            public static final int TYPE_NONE = 0;
+            public static final int TYPE_IMAGE = 1;
+            public static final int TYPE_VIDEO = 2;
+            public static final int TYPE_AUDIO = 3;
 
             /**
              * The link URI (href) of the element.
              */
             public final @Nullable String linkUri;
 
             /**
              * The title text of the element.
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -1164,17 +1164,16 @@ pref("layout.framevisibility.numscrollpo
 // 1 and higher - slider thickness multiple
 pref("slider.snapMultiplier", 0);
 
 // URI fixup prefs
 pref("browser.fixup.alternate.enabled", true);
 pref("browser.fixup.alternate.prefix", "www.");
 pref("browser.fixup.alternate.suffix", ".com");
 pref("browser.fixup.dns_first_for_single_words", false);
-pref("browser.fixup.hide_user_pass", true);
 
 // Print header customization
 // Use the following codes:
 // &T - Title
 // &U - Document URL
 // &D - Date/Time
 // &P - Page Number
 // &PT - Page Number "of" Page total
--- a/netwerk/ipc/NeckoChild.cpp
+++ b/netwerk/ipc/NeckoChild.cpp
@@ -69,17 +69,19 @@ void NeckoChild::InitNeckoChild()
       return;
     }
     gNeckoChild = cpc->SendPNeckoConstructor();
     NS_ASSERTION(gNeckoChild, "PNecko Protocol init failed!");
   }
 }
 
 PHttpChannelChild*
-NeckoChild::AllocPHttpChannelChild()
+NeckoChild::AllocPHttpChannelChild(const PBrowserOrId& browser,
+                                   const SerializedLoadContext& loadContext,
+                                   const HttpChannelCreationArgs& aOpenArgs)
 {
   // We don't allocate here: instead we always use IPDL constructor that takes
   // an existing HttpChildChannel
   MOZ_ASSERT_UNREACHABLE("AllocPHttpChannelChild should not be called on "
                          "child");
   return nullptr;
 }
 
--- a/netwerk/ipc/NeckoChild.h
+++ b/netwerk/ipc/NeckoChild.h
@@ -20,17 +20,19 @@ class NeckoChild :
 {
 public:
   NeckoChild() = default;
   virtual ~NeckoChild();
 
   static void InitNeckoChild();
 
 protected:
-  virtual PHttpChannelChild* AllocPHttpChannelChild() override;
+  virtual PHttpChannelChild*
+    AllocPHttpChannelChild(const PBrowserOrId&, const SerializedLoadContext&,
+                           const HttpChannelCreationArgs& aOpenArgs) override;
   virtual bool DeallocPHttpChannelChild(PHttpChannelChild*) override;
 
   virtual PStunAddrsRequestChild* AllocPStunAddrsRequestChild() override;
   virtual bool
     DeallocPStunAddrsRequestChild(PStunAddrsRequestChild* aActor) override;
 
   virtual PWebrtcProxyChannelChild* AllocPWebrtcProxyChannelChild(
     const PBrowserOrId& browser) override;
--- a/netwerk/ipc/NeckoParent.cpp
+++ b/netwerk/ipc/NeckoParent.cpp
@@ -87,31 +87,29 @@ NeckoParent::NeckoParent()
   static bool registeredBool = false;
   if (!registeredBool) {
     Preferences::AddBoolVarCache(&NeckoCommonInternal::gSecurityDisabled,
                                  "network.disable.ipc.security");
     registeredBool = true;
   }
 }
 
-/* static */ PBOverrideStatus
-NeckoParent::PBOverrideStatusFromLoadContext(
-  const SerializedLoadContext& aSerialized)
+static PBOverrideStatus
+PBOverrideStatusFromLoadContext(const SerializedLoadContext& aSerialized)
 {
   if (!aSerialized.IsNotNull() && aSerialized.IsPrivateBitValid()) {
     return (aSerialized.mOriginAttributes.mPrivateBrowsingId > 0) ?
       kPBOverride_Private :
       kPBOverride_NotPrivate;
   }
   return kPBOverride_Unset;
 }
 
-/* static */ already_AddRefed<nsIPrincipal>
-NeckoParent::GetRequestingPrincipal(
-  const OptionalLoadInfoArgs& aOptionalLoadInfoArgs)
+static already_AddRefed<nsIPrincipal>
+GetRequestingPrincipal(const OptionalLoadInfoArgs& aOptionalLoadInfoArgs)
 {
   if (aOptionalLoadInfoArgs.type() != OptionalLoadInfoArgs::TLoadInfoArgs) {
     return nullptr;
   }
 
   const LoadInfoArgs& loadInfoArgs = aOptionalLoadInfoArgs.get_LoadInfoArgs();
   const OptionalPrincipalInfo& optionalPrincipalInfo =
     loadInfoArgs.requestingPrincipalInfo();
@@ -121,29 +119,29 @@ NeckoParent::GetRequestingPrincipal(
   }
 
   const PrincipalInfo& principalInfo =
     optionalPrincipalInfo.get_PrincipalInfo();
 
   return PrincipalInfoToPrincipal(principalInfo);
 }
 
-/* static */ already_AddRefed<nsIPrincipal>
-NeckoParent::GetRequestingPrincipal(const HttpChannelCreationArgs& aArgs)
+static already_AddRefed<nsIPrincipal>
+GetRequestingPrincipal(const HttpChannelCreationArgs& aArgs)
 {
   if (aArgs.type() != HttpChannelCreationArgs::THttpChannelOpenArgs) {
     return nullptr;
   }
 
   const HttpChannelOpenArgs& args = aArgs.get_HttpChannelOpenArgs();
   return GetRequestingPrincipal(args.loadInfo());
 }
 
-/* static */ already_AddRefed<nsIPrincipal>
-NeckoParent::GetRequestingPrincipal(const FTPChannelCreationArgs& aArgs)
+static already_AddRefed<nsIPrincipal>
+GetRequestingPrincipal(const FTPChannelCreationArgs& aArgs)
 {
   if (aArgs.type() != FTPChannelCreationArgs::TFTPChannelOpenArgs) {
     return nullptr;
   }
 
   const FTPChannelOpenArgs& args = aArgs.get_FTPChannelOpenArgs();
   return GetRequestingPrincipal(args.loadInfo());
 }
@@ -154,17 +152,17 @@ NeckoParent::GetRequestingPrincipal(cons
 static MOZ_COLD
 void CrashWithReason(const char * reason)
 {
 #ifndef RELEASE_OR_BETA
   MOZ_CRASH_UNSAFE_OOL(reason);
 #endif
 }
 
-/* static */ const char*
+const char*
 NeckoParent::GetValidatedOriginAttributes(const SerializedLoadContext& aSerialized,
                                           PContentParent* aContent,
                                           nsIPrincipal* aRequestingPrincipal,
                                           OriginAttributes& aAttrs)
 {
   if (!UsingNeckoIPCSecurity()) {
     if (!aSerialized.IsNotNull()) {
       // If serialized is null, we cannot validate anything. We have to assume
@@ -230,17 +228,17 @@ NeckoParent::GetValidatedOriginAttribute
   // Leak the buffer on the heap to make sure that it lives long enough, as
   // MOZ_CRASH_ANNOTATE expects the pointer passed to it to live to the end of
   // the program.
   char * error = strdup(errorString.BeginReading());
   CrashWithReason(error);
   return "App does not have permission";
 }
 
-/* static */ const char *
+const char *
 NeckoParent::CreateChannelLoadContext(const PBrowserOrId& aBrowser,
                                       PContentParent* aContent,
                                       const SerializedLoadContext& aSerialized,
                                       nsIPrincipal* aRequestingPrincipal,
                                       nsCOMPtr<nsILoadContext> &aResult)
 {
   OriginAttributes attrs;
   const char* error = GetValidatedOriginAttributes(aSerialized, aContent,
@@ -281,31 +279,61 @@ NeckoParent::CreateChannelLoadContext(co
 void
 NeckoParent::ActorDestroy(ActorDestroyReason aWhy)
 {
   // Nothing needed here. Called right before destructor since this is a
   // non-refcounted class.
 }
 
 PHttpChannelParent*
-NeckoParent::AllocPHttpChannelParent()
+NeckoParent::AllocPHttpChannelParent(const PBrowserOrId& aBrowser,
+                                     const SerializedLoadContext& aSerialized,
+                                     const HttpChannelCreationArgs& aOpenArgs)
 {
-  HttpChannelParent* p = new HttpChannelParent();
+  nsCOMPtr<nsIPrincipal> requestingPrincipal =
+    GetRequestingPrincipal(aOpenArgs);
+
+  nsCOMPtr<nsILoadContext> loadContext;
+  const char *error = CreateChannelLoadContext(aBrowser, Manager(),
+                                               aSerialized, requestingPrincipal,
+                                               loadContext);
+  if (error) {
+    printf_stderr("NeckoParent::AllocPHttpChannelParent: "
+                  "FATAL error: %s: KILLING CHILD PROCESS\n",
+                  error);
+    return nullptr;
+  }
+  PBOverrideStatus overrideStatus = PBOverrideStatusFromLoadContext(aSerialized);
+  HttpChannelParent *p = new HttpChannelParent(aBrowser, loadContext, overrideStatus);
   p->AddRef();
   return p;
 }
 
 bool
 NeckoParent::DeallocPHttpChannelParent(PHttpChannelParent* channel)
 {
   HttpChannelParent *p = static_cast<HttpChannelParent *>(channel);
   p->Release();
   return true;
 }
 
+mozilla::ipc::IPCResult
+NeckoParent::RecvPHttpChannelConstructor(
+                      PHttpChannelParent* aActor,
+                      const PBrowserOrId& aBrowser,
+                      const SerializedLoadContext& aSerialized,
+                      const HttpChannelCreationArgs& aOpenArgs)
+{
+  HttpChannelParent* p = static_cast<HttpChannelParent*>(aActor);
+  if (!p->Init(aOpenArgs)) {
+    return IPC_FAIL_NO_REASON(this);
+  }
+  return IPC_OK();
+}
+
 PStunAddrsRequestParent*
 NeckoParent::AllocPStunAddrsRequestParent()
 {
 #ifdef MOZ_WEBRTC
   StunAddrsRequestParent* p = new StunAddrsRequestParent();
   p->AddRef();
   return p;
 #else
--- a/netwerk/ipc/NeckoParent.h
+++ b/netwerk/ipc/NeckoParent.h
@@ -28,25 +28,16 @@ enum PBOverrideStatus {
 // Header file contents
 class NeckoParent
   : public PNeckoParent
 {
 public:
   NeckoParent();
   virtual ~NeckoParent() = default;
 
-  static PBOverrideStatus PBOverrideStatusFromLoadContext(
-    const SerializedLoadContext& aSerialized);
-  static already_AddRefed<nsIPrincipal> GetRequestingPrincipal(
-    const OptionalLoadInfoArgs& aOptionalLoadInfoArgs);
-  static already_AddRefed<nsIPrincipal> GetRequestingPrincipal(
-    const FTPChannelCreationArgs& aArgs);
-  static already_AddRefed<nsIPrincipal> GetRequestingPrincipal(
-    const HttpChannelCreationArgs& aArgs);
-
   MOZ_MUST_USE
   static const char *
   GetValidatedOriginAttributes(const SerializedLoadContext& aSerialized,
                                PContentParent* aBrowser,
                                nsIPrincipal* aRequestingPrincipal,
                                mozilla::OriginAttributes& aAttrs);
 
   /*
@@ -97,17 +88,25 @@ public:
                                nsIAuthInformation* aInfo, nsICancelable**) override;
 
   protected:
     PNeckoParent* mNeckoParent;
     TabId mNestedFrameId;
   };
 
 protected:
-  virtual PHttpChannelParent* AllocPHttpChannelParent() override;
+  virtual PHttpChannelParent*
+    AllocPHttpChannelParent(const PBrowserOrId&, const SerializedLoadContext&,
+                            const HttpChannelCreationArgs& aOpenArgs) override;
+  virtual mozilla::ipc::IPCResult
+    RecvPHttpChannelConstructor(
+      PHttpChannelParent* aActor,
+      const PBrowserOrId& aBrowser,
+      const SerializedLoadContext& aSerialized,
+      const HttpChannelCreationArgs& aOpenArgs) override;
   virtual bool DeallocPHttpChannelParent(PHttpChannelParent*) override;
 
   virtual PStunAddrsRequestParent* AllocPStunAddrsRequestParent() override;
   virtual bool
     DeallocPStunAddrsRequestParent(PStunAddrsRequestParent* aActor) override;
 
   virtual PWebrtcProxyChannelParent* AllocPWebrtcProxyChannelParent(
     const PBrowserOrId& aBrowser) override;
--- a/netwerk/ipc/PNecko.ipdl
+++ b/netwerk/ipc/PNecko.ipdl
@@ -67,17 +67,19 @@ nested(upto inside_cpow) sync protocol P
   manages PStunAddrsRequest;
   manages PTrackingDummyChannel;
   manages PWebrtcProxyChannel;
 
 parent:
   async __delete__();
 
   nested(inside_cpow) async PCookieService();
-  async PHttpChannel();
+  async PHttpChannel(PBrowserOrId browser,
+                     SerializedLoadContext loadContext,
+                     HttpChannelCreationArgs args);
   async PWyciwygChannel();
   async PFTPChannel(PBrowserOrId browser, SerializedLoadContext loadContext,
                     FTPChannelCreationArgs args);
 
   async PWebSocket(PBrowserOrId browser, SerializedLoadContext loadContext,
                    uint32_t aSerialID);
   async PTCPServerSocket(uint16_t localPort, uint16_t backlog, bool useArrayBuffers);
   async PUDPSocket(Principal principal, nsCString filter);
--- a/netwerk/protocol/http/HttpBaseChannel.cpp
+++ b/netwerk/protocol/http/HttpBaseChannel.cpp
@@ -63,16 +63,17 @@
 #include "mozilla/Move.h"
 #include "mozilla/net/PartiallySeekableInputStream.h"
 #include "mozilla/InputStreamLengthHelper.h"
 #include "nsIHttpHeaderVisitor.h"
 #include "nsIMIMEInputStream.h"
 #include "nsIXULRuntime.h"
 #include "nsICacheInfoChannel.h"
 #include "nsIDOMWindowUtils.h"
+#include "nsIURIFixup.h"
 #include "nsHttpChannel.h"
 #include "nsRedirectHistoryEntry.h"
 #include "nsServerTiming.h"
 
 #include <algorithm>
 #include "HttpBaseChannel.h"
 
 namespace mozilla {
@@ -1900,20 +1901,28 @@ HttpBaseChannel::SetReferrerWithPolicy(n
     rv = NS_GetURIWithoutRef(mURI, getter_AddRefs(mURIclone));
     if (NS_FAILED(rv)) return rv;
     clone = mURIclone;
     currentHost = referrerHost;
   }
 
   // strip away any userpass; we don't want to be giving out passwords ;-)
   // This is required by Referrer Policy stripping algorithm.
-  rv = NS_MutateURI(clone)
-         .SetUserPass(EmptyCString())
-         .Finalize(clone);
-  if (NS_FAILED(rv)) return rv;
+  nsCOMPtr<nsIURIFixup> urifixup = services::GetURIFixup();
+  if (NS_WARN_IF(!urifixup)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  nsCOMPtr<nsIURI> exposableURI;
+  rv = urifixup->CreateExposableURI(clone, getter_AddRefs(exposableURI));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  clone = exposableURI;
 
   // 0: full URI
   // 1: scheme+host+port+path
   // 2: scheme+host+port
   int userReferrerTrimmingPolicy = gHttpHandler->ReferrerTrimmingPolicy();
   int userReferrerXOriginTrimmingPolicy =
     gHttpHandler->ReferrerXOriginTrimmingPolicy();
 
--- a/netwerk/protocol/http/HttpChannelChild.cpp
+++ b/netwerk/protocol/http/HttpChannelChild.cpp
@@ -188,17 +188,16 @@ HttpChannelChild::HttpChannelChild()
   , mShouldInterceptSubsequentRedirect(false)
   , mRedirectingForSubsequentSynthesizedResponse(false)
   , mPostRedirectChannelShouldIntercept(false)
   , mPostRedirectChannelShouldUpgrade(false)
   , mShouldParentIntercept(false)
   , mSuspendParentAfterSynthesizeResponse(false)
   , mCacheNeedToReportBytesReadInitialized(false)
   , mNeedToReportBytesRead(true)
-  , mSentAsyncOpen(false)
 {
   LOG(("Creating HttpChannelChild @%p\n", this));
 
   mChannelCreationTime = PR_Now();
   mChannelCreationTimestamp = TimeStamp::Now();
   mLastStatusReported = mChannelCreationTimestamp; // in case we enable the profiler after Init()
   mAsyncOpenTime = TimeStamp::Now();
   mEventQ = new ChannelEventQueue(static_cast<nsIHttpChannel*>(this));
@@ -262,17 +261,17 @@ NS_IMETHODIMP_(MozExternalRefCountType) 
   nsrefcnt count = --mRefCnt;
   MOZ_ASSERT(int32_t(count) >= 0, "dup release");
   NS_LOG_RELEASE(this, count, "HttpChannelChild");
 
   // Normally we Send_delete in OnStopRequest, but when we need to retain the
   // remote channel for security info IPDL itself holds 1 reference, so we
   // Send_delete when refCnt==1.  But if !mIPCOpen, then there's nobody to send
   // to, so we fall through.
-  if ((mKeptAlive || !mSentAsyncOpen) && count == 1 && mIPCOpen) {
+  if (mKeptAlive && count == 1 && mIPCOpen) {
     mKeptAlive = false;
     // We send a message to the parent, which calls SendDelete, and then the
     // child calling Send__delete__() to finally drop the refcount to 0.
     TrySendDeletingChannel();
     return 1;
   }
 
   if (count == 0) {
@@ -1647,18 +1646,17 @@ HttpChannelChild::RecvFinishInterceptedR
 void
 HttpChannelChild::DeleteSelf()
 {
   Send__delete__(this);
 }
 
 void HttpChannelChild::FinishInterceptedRedirect()
 {
-  nsresult rv = InitIPCChannel(); // reinitializes IPC channel
-  MOZ_ASSERT(NS_SUCCEEDED(rv));
+  nsresult rv;
   if (mLoadInfo && mLoadInfo->GetEnforceSecurity()) {
     MOZ_ASSERT(!mInterceptedRedirectContext, "the context should be null!");
     rv = AsyncOpen2(mInterceptedRedirectListener);
   } else {
     rv = AsyncOpen(mInterceptedRedirectListener, mInterceptedRedirectContext);
   }
   mInterceptedRedirectListener = nullptr;
   mInterceptedRedirectContext = nullptr;
@@ -2205,23 +2203,34 @@ HttpChannelChild::ConnectParent(uint32_t
 
   ContentChild* cc = static_cast<ContentChild*>(gNeckoChild->Manager());
   if (cc->IsShuttingDown()) {
     return NS_ERROR_FAILURE;
   }
 
   HttpBaseChannel::SetDocshellUserAgentOverride();
 
+  // The socket transport in the chrome process now holds a logical ref to us
+  // until OnStopRequest, or we do a redirect, or we hit an IPDL error.
+  AddIPDLReference();
+
+  // This must happen before the constructor message is sent. Otherwise messages
+  // from the parent could arrive quickly and be delivered to the wrong event
+  // target.
+  SetEventTarget();
+
   HttpChannelConnectArgs connectArgs(registrarId, mShouldParentIntercept);
   PBrowserOrId browser = static_cast<ContentChild*>(gNeckoChild->Manager())
                          ->GetBrowserOrId(tabChild);
-  if (!SendAsyncOpen(browser, IPC::SerializedLoadContext(this), connectArgs)) {
+  if (!gNeckoChild->
+        SendPHttpChannelConstructor(this, browser,
+                                    IPC::SerializedLoadContext(this),
+                                    connectArgs)) {
     return NS_ERROR_FAILURE;
   }
-  mSentAsyncOpen = true;
 
   {
     MutexAutoLock lock(mBgChildMutex);
 
     MOZ_ASSERT(!mBgChild);
     MOZ_ASSERT(!mBgInitFailCallback);
 
     mBgInitFailCallback = NewRunnableMethod<nsresult>(
@@ -2718,52 +2727,16 @@ HttpChannelChild::AsyncOpen2(nsIStreamLi
   nsresult rv = nsContentSecurityManager::doContentSecurityCheck(this, listener);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     ReleaseListeners();
     return rv;
   }
   return AsyncOpen(listener, nullptr);
 }
 
-NS_IMETHODIMP
-HttpChannelChild::SetLoadInfo(nsILoadInfo* aLoadInfo)
-{
-  LOG(("HttpChannelChild::SetLoadInfo [this=%p, aLoadInfo=%p]",
-       this, aLoadInfo));
-  MOZ_ALWAYS_SUCCEEDS(HttpBaseChannel::SetLoadInfo(aLoadInfo));
-
-  // IPC channel already open
-  if (NS_WARN_IF(mIPCOpen)) {
-    return NS_OK;
-  }
-
-  return InitIPCChannel();
-}
-
-nsresult
-HttpChannelChild::InitIPCChannel()
-{
-  MOZ_ASSERT(!mIPCOpen);
-
-  // This must happen before the constructor message is sent. Otherwise messages
-  // from the parent could arrive quickly and be delivered to the wrong event
-  // target.
-  SetEventTarget();
-
-  LOG(("  Calling SendPHttpChannelConstructor"));
-  if (!gNeckoChild->SendPHttpChannelConstructor(this)) {
-    return NS_ERROR_FAILURE;
-  }
-
-  // The socket transport in the chrome process now holds a logical ref to us
-  // until OnStopRequest, or we do a redirect, or we hit an IPDL error.
-  AddIPDLReference();
-  return NS_OK;
-}
-
 // Assigns an nsIEventTarget to our IPDL actor so that IPC messages are sent to
 // the correct DocGroup/TabGroup.
 void
 HttpChannelChild::SetEventTarget()
 {
   nsCOMPtr<nsILoadInfo> loadInfo;
   GetLoadInfo(getter_AddRefs(loadInfo));
 
@@ -2967,22 +2940,31 @@ HttpChannelChild::ContinueAsyncOpen()
   openArgs.dispatchFetchEventEnd()    = mDispatchFetchEventEnd;
   openArgs.handleFetchEventStart()    = mHandleFetchEventStart;
   openArgs.handleFetchEventEnd()      = mHandleFetchEventEnd;
 
   openArgs.forceMainDocumentChannel() = mForceMainDocumentChannel;
 
   openArgs.navigationStartTimeStamp() = navigationStartTimeStamp;
 
-  LOG(("HttpChannelChild::ContinueAsyncOpen - SendAsyncOpen [this=%p]", this));
+  // This must happen before the constructor message is sent. Otherwise messages
+  // from the parent could arrive quickly and be delivered to the wrong event
+  // target.
+  SetEventTarget();
+
+  // The socket transport in the chrome process now holds a logical ref to us
+  // until OnStopRequest, or we do a redirect, or we hit an IPDL error.
+  AddIPDLReference();
+
   PBrowserOrId browser = cc->GetBrowserOrId(tabChild);
-  if (!SendAsyncOpen(browser, IPC::SerializedLoadContext(this), openArgs)) {
+  if (!gNeckoChild->SendPHttpChannelConstructor(this, browser,
+                                                IPC::SerializedLoadContext(this),
+                                                openArgs)) {
     return NS_ERROR_FAILURE;
   }
-  mSentAsyncOpen = true;
 
   {
     MutexAutoLock lock(mBgChildMutex);
 
     MOZ_RELEASE_ASSERT(gSocketTransportService);
 
     // Service worker might use the same HttpChannelChild to do async open
     // twice. Need to disconnect with previous background channel before
@@ -3749,24 +3731,16 @@ HttpChannelChild::ResetInterception()
     mLoadFlags |= LOAD_BYPASS_SERVICE_WORKER;
   }
 
   // If the channel has already been aborted or canceled, just stop.
   if (NS_FAILED(mStatus)) {
     return;
   }
 
-  if (!mIPCOpen) {
-    // The IPC channel was closed. See RecvFinishInterceptedRedirect.
-    // We now want to reuse it, so we call InitIPCChannel again.
-    DebugOnly<nsresult> rv = InitIPCChannel();
-    MOZ_ASSERT(NS_SUCCEEDED(rv));
-  }
-  MOZ_ASSERT(mLoadInfo && mIPCOpen);
-
   // Continue with the original cross-process request
   nsresult rv = ContinueAsyncOpen();
   if (NS_WARN_IF(NS_FAILED(rv))) {
     Unused << Cancel(rv);
   }
 }
 
 void
--- a/netwerk/protocol/http/HttpChannelChild.h
+++ b/netwerk/protocol/http/HttpChannelChild.h
@@ -86,17 +86,16 @@ public:
   // nsIRequest
   NS_IMETHOD Cancel(nsresult status) override;
   NS_IMETHOD Suspend() override;
   NS_IMETHOD Resume() override;
   // nsIChannel
   NS_IMETHOD GetSecurityInfo(nsISupports **aSecurityInfo) override;
   NS_IMETHOD AsyncOpen(nsIStreamListener *listener, nsISupports *aContext) override;
   NS_IMETHOD AsyncOpen2(nsIStreamListener *aListener) override;
-  NS_IMETHOD SetLoadInfo(nsILoadInfo* aLoadInfo) override;
 
   // HttpBaseChannel::nsIHttpChannel
   NS_IMETHOD SetReferrerWithPolicy(nsIURI *referrer, uint32_t referrerPolicy) override;
   NS_IMETHOD SetRequestHeader(const nsACString& aHeader,
                               const nsACString& aValue,
                               bool aMerge) override;
   NS_IMETHOD SetEmptyRequestHeader(const nsACString& aHeader) override;
   NS_IMETHOD RedirectTo(nsIURI *newURI) override;
@@ -202,17 +201,16 @@ protected:
   };
 
   // Get event target for processing network events.
   already_AddRefed<nsIEventTarget> GetNeckoTarget() override;
 
   virtual mozilla::ipc::IPCResult RecvLogBlockedCORSRequest(const nsString& aMessage, const nsCString& aCategory) override;
   NS_IMETHOD LogBlockedCORSRequest(const nsAString & aMessage, const nsACString& aCategory) override;
 
-  nsresult InitIPCChannel();
 private:
   nsresult
   AsyncCallImpl(void (HttpChannelChild::*funcPtr)(),
                 nsRunnableMethod<HttpChannelChild> **retval);
 
   class OverrideRunnable : public Runnable {
   public:
     OverrideRunnable(HttpChannelChild* aChannel,
@@ -429,32 +427,21 @@ private:
   uint8_t mSuspendParentAfterSynthesizeResponse : 1;
 
   // Set if we get the result and cache |mNeedToReportBytesRead|
   uint8_t mCacheNeedToReportBytesReadInitialized : 1;
 
   // True if we need to tell the parent the size of unreported received data
   uint8_t mNeedToReportBytesRead : 1;
 
-  // True after SendAsyncOpen is called.
-  uint8_t mSentAsyncOpen : 1;
-
   void FinishInterceptedRedirect();
   void CleanupRedirectingChannel(nsresult rv);
 
   // true after successful AsyncOpen until OnStopRequest completes.
-  // XXX valentin: technically the channel exists after SetLoadInfo, but it
-  // used to be created at AsyncOpen, and before that the parent actually
-  // doesn't have all the info for it to be safe to call any method.
-  // We should rename it in the future and allow some methods to be called
-  // before asyncOpen.
-  bool RemoteChannelExists()
-  {
-    return mIPCOpen && !mKeptAlive && mSentAsyncOpen;
-  }
+  bool RemoteChannelExists() { return mIPCOpen && !mKeptAlive; }
 
   void AssociateApplicationCache(const nsCString &groupID,
                                  const nsCString &clientID);
   void OnStartRequest(const nsresult& channelStatus,
                       const nsHttpResponseHead& responseHead,
                       const bool& useResponseHead,
                       const nsHttpHeaderArray& requestHeaders,
                       const ParentLoadInfoForwarderArgs& loadInfoForwarder,
--- a/netwerk/protocol/http/HttpChannelParent.cpp
+++ b/netwerk/protocol/http/HttpChannelParent.cpp
@@ -57,21 +57,23 @@
 
 using mozilla::BasePrincipal;
 using namespace mozilla::dom;
 using namespace mozilla::ipc;
 
 namespace mozilla {
 namespace net {
 
-HttpChannelParent::HttpChannelParent()
-  : mLoadContext(nullptr)
+HttpChannelParent::HttpChannelParent(const PBrowserOrId& iframeEmbedding,
+                                     nsILoadContext* aLoadContext,
+                                     PBOverrideStatus aOverrideStatus)
+  : mLoadContext(aLoadContext)
   , mNestedFrameId(0)
   , mIPCClosed(false)
-  , mPBOverride(kPBOverride_Unset)
+  , mPBOverride(aOverrideStatus)
   , mStatus(NS_OK)
   , mIgnoreProgress(false)
   , mSentRedirect1BeginFailed(false)
   , mReceivedRedirect2Verify(false)
   , mHasSuspendedByBackPressure(false)
   , mPendingDiversion(false)
   , mDivertingFromChild(false)
   , mDivertedOnStartRequest(false)
@@ -87,16 +89,24 @@ HttpChannelParent::HttpChannelParent()
 
   // Ensure gHttpHandler is initialized: we need the atom table up and running.
   nsCOMPtr<nsIHttpProtocolHandler> dummyInitializer =
     do_GetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "http");
 
   MOZ_ASSERT(gHttpHandler);
   mHttpHandler = gHttpHandler;
 
+  if (iframeEmbedding.type() == PBrowserOrId::TPBrowserParent) {
+    mTabParent = static_cast<dom::TabParent*>(iframeEmbedding.get_PBrowserParent());
+  } else {
+    mNestedFrameId = iframeEmbedding.get_TabId();
+  }
+
+  mSendWindowSize = gHttpHandler->SendWindowSize();
+
   mEventQ = new ChannelEventQueue(static_cast<nsIParentRedirectingChannel*>(this));
 }
 
 HttpChannelParent::~HttpChannelParent()
 {
   LOG(("Destroying HttpChannelParent [this=%p]\n", this));
   CleanupBackgroundChannel();
 }
@@ -160,53 +170,16 @@ HttpChannelParent::Init(const HttpChanne
     return ConnectChannel(cArgs.registrarId(), cArgs.shouldIntercept());
   }
   default:
     MOZ_ASSERT_UNREACHABLE("unknown open type");
     return false;
   }
 }
 
-mozilla::ipc::IPCResult
-HttpChannelParent::RecvAsyncOpen(const PBrowserOrId& aBrowser,
-                                 const SerializedLoadContext& aSerialized,
-                                 const HttpChannelCreationArgs& aOpenArgs)
-{
-  LOG(("HttpChannelParent::RecvAsyncOpen [this=%p]\n", this));
-
-  nsCOMPtr<nsIPrincipal> requestingPrincipal =
-    NeckoParent::GetRequestingPrincipal(aOpenArgs);
-
-  nsCOMPtr<nsILoadContext> loadContext;
-  const char* error =
-    NeckoParent::CreateChannelLoadContext(aBrowser,
-                                          Manager()->Manager(),
-                                          aSerialized,
-                                          requestingPrincipal,
-                                          loadContext);
-  if (error) {
-    return IPC_FAIL(this, "Error in NeckoParent::CreateChannelLoadContext");
-  }
-  mPBOverride = NeckoParent::PBOverrideStatusFromLoadContext(aSerialized);
-  mLoadContext = loadContext;
-
-  if (aBrowser.type() == PBrowserOrId::TPBrowserParent) {
-    mTabParent = static_cast<dom::TabParent*>(aBrowser.get_PBrowserParent());
-  } else {
-    mNestedFrameId = aBrowser.get_TabId();
-  }
-
-  mSendWindowSize = gHttpHandler->SendWindowSize();
-
-  if (!Init(aOpenArgs)) {
-    return IPC_FAIL(this, "Error in HttpChannelParent::Init");
-  }
-  return IPC_OK();
-}
-
 void
 HttpChannelParent::TryInvokeAsyncOpen(nsresult aRv)
 {
   LOG(("HttpChannelParent::TryInvokeAsyncOpen [this=%p barrier=%u rv=%" PRIx32
        "]\n", this, mAsyncOpenBarrier, static_cast<uint32_t>(aRv)));
   MOZ_ASSERT(NS_IsMainThread());
 
   // TryInvokeAsyncOpen is called more than we expected.
@@ -500,22 +473,18 @@ HttpChannelParent::DoAsyncOpen(  const U
     return false;
   }
   nsCOMPtr<nsIURI> originalUri = DeserializeURI(aOriginalURI);
   nsCOMPtr<nsIURI> docUri = DeserializeURI(aDocURI);
   nsCOMPtr<nsIURI> referrerUri = DeserializeURI(aReferrerURI);
   nsCOMPtr<nsIURI> apiRedirectToUri = DeserializeURI(aAPIRedirectToURI);
   nsCOMPtr<nsIURI> topWindowUri = DeserializeURI(aTopWindowURI);
 
-  LOG(("HttpChannelParent DoAsyncOpen [this=%p uri=%s, gid=%" PRIu64
-       " topwinid=%" PRIx64 "]\n",
-       this,
-       uri->GetSpecOrDefault().get(),
-       aChannelId,
-       aTopLevelOuterContentWindowId));
+  LOG(("HttpChannelParent RecvAsyncOpen [this=%p uri=%s, gid=%" PRIu64 " topwinid=%" PRIx64 "]\n",
+       this, uri->GetSpecOrDefault().get(), aChannelId, aTopLevelOuterContentWindowId));
 
   nsresult rv;
 
   nsCOMPtr<nsIIOService> ios(do_GetIOService(&rv));
   if (NS_FAILED(rv))
     return SendFailedAsyncOpen(rv);
 
   nsCOMPtr<nsILoadInfo> loadInfo;
--- a/netwerk/protocol/http/HttpChannelParent.h
+++ b/netwerk/protocol/http/HttpChannelParent.h
@@ -66,17 +66,19 @@ public:
   NS_DECL_NSIPROGRESSEVENTSINK
   NS_DECL_NSIINTERFACEREQUESTOR
   NS_DECL_NSIAUTHPROMPTPROVIDER
   NS_DECL_NSIDEPRECATIONWARNER
   NS_DECL_NSIASYNCVERIFYREDIRECTREADYCALLBACK
 
   NS_DECLARE_STATIC_IID_ACCESSOR(HTTP_CHANNEL_PARENT_IID)
 
-  HttpChannelParent();
+  HttpChannelParent(const dom::PBrowserOrId& iframeEmbedding,
+                    nsILoadContext* aLoadContext,
+                    PBOverrideStatus aStatus);
 
   MOZ_MUST_USE bool Init(const HttpChannelCreationArgs& aOpenArgs);
 
   // ADivertableParentChannel functions.
   void DivertTo(nsIStreamListener *aListener) override;
   MOZ_MUST_USE nsresult SuspendForDiversion() override;
   MOZ_MUST_USE nsresult SuspendMessageDiversion() override;
   MOZ_MUST_USE nsresult ResumeMessageDiversion() override;
@@ -226,20 +228,16 @@ protected:
   nsresult LogBlockedCORSRequest(const nsAString& aMessage, const nsACString& aCategory) override;
 
   // Calls SendDeleteSelf and sets mIPCClosed to true because we should not
   // send any more messages after that. Bug 1274886
   MOZ_MUST_USE bool DoSendDeleteSelf();
   // Called to notify the parent channel to not send any more IPC messages.
   virtual mozilla::ipc::IPCResult RecvDeletingChannel() override;
   virtual mozilla::ipc::IPCResult RecvFinishInterceptedRedirect() override;
-  virtual mozilla::ipc::IPCResult RecvAsyncOpen(
-    const PBrowserOrId& aBrowser,
-    const SerializedLoadContext& aSerialized,
-    const HttpChannelCreationArgs& aOpenArgs) override;
 
 private:
   void UpdateAndSerializeSecurityInfo(nsACString& aSerializedSecurityInfoOut);
 
   void DivertOnDataAvailable(const nsCString& data,
                              const uint64_t& offset,
                              const uint32_t& count);
   void DivertOnStopRequest(const nsresult& statusCode);
--- a/netwerk/protocol/http/PHttpChannel.ipdl
+++ b/netwerk/protocol/http/PHttpChannel.ipdl
@@ -8,37 +8,34 @@
 include protocol PNecko;
 include protocol PStreamFilter;
 include InputStreamParams;
 include URIParams;
 include PBackgroundSharedTypes;
 include NeckoChannelParams;
 include IPCServiceWorkerDescriptor;
 include IPCStream;
-include PBrowserOrId;
 
 include "mozilla/net/NeckoMessageUtils.h";
 
 using class nsHttpHeaderArray from "nsHttpHeaderArray.h";
 using mozilla::net::NetAddr from "mozilla/net/DNS.h";
 using struct mozilla::net::ResourceTimingStruct from "mozilla/net/TimingStruct.h";
-using class IPC::SerializedLoadContext from "SerializedLoadContext.h";
 
 namespace mozilla {
 namespace net {
 
 //-------------------------------------------------------------------
 protocol PHttpChannel
 {
   manager PNecko;
 
 parent:
-  async AsyncOpen(PBrowserOrId browser,
-                  SerializedLoadContext loadContext,
-                  HttpChannelCreationArgs args);
+  // Note: channels are opened during construction, so no open method here:
+  // see PNecko.ipdl
 
   async SetClassOfService(uint32_t cos);
 
   async SetCacheTokenCachedCharset(nsCString charset);
 
   async Suspend();
   async Resume();
 
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -77,16 +77,17 @@
 #include "netCore.h"
 #include "nsHttpTransaction.h"
 #include "nsICacheEntryDescriptor.h"
 #include "nsICancelable.h"
 #include "nsIHttpChannelAuthProvider.h"
 #include "nsIHttpChannelInternal.h"
 #include "nsIPrompt.h"
 #include "nsInputStreamPump.h"
+#include "nsIURIFixup.h"
 #include "nsURLHelper.h"
 #include "nsISocketTransport.h"
 #include "nsIStreamConverterService.h"
 #include "nsISiteSecurityService.h"
 #include "nsString.h"
 #include "nsCRT.h"
 #include "CacheObserver.h"
 #include "mozilla/dom/PerformanceStorage.h"
@@ -1108,21 +1109,27 @@ nsHttpChannel::SetupTransaction()
         mRequestHead.SetPath(*requestURI);
 
         // RequestURI should be the absolute uri H1 proxy syntax "http://foo/index.html"
         // so we will overwrite the relative version in requestURI
         rv = mURI->GetUserPass(buf);
         if (NS_FAILED(rv)) return rv;
         if (!buf.IsEmpty() && ((strncmp(mSpec.get(), "http:", 5) == 0) ||
                                 strncmp(mSpec.get(), "https:", 6) == 0)) {
+            nsCOMPtr<nsIURIFixup> urifixup = services::GetURIFixup();
+            if (NS_WARN_IF(!urifixup)) {
+              return NS_ERROR_FAILURE;
+            }
+
             nsCOMPtr<nsIURI> tempURI;
-            rv = NS_MutateURI(mURI)
-                   .SetUserPass(EmptyCString())
-                   .Finalize(tempURI);
-            if (NS_FAILED(rv)) return rv;
+            nsresult rv = urifixup->CreateExposableURI(mURI, getter_AddRefs(tempURI));
+            if (NS_WARN_IF(NS_FAILED(rv))) {
+              return rv;
+            }
+
             rv = tempURI->GetAsciiSpec(path);
             if (NS_FAILED(rv)) return rv;
             requestURI = &path;
         } else {
             requestURI = &mSpec;
         }
 
         // trim off the #ref portion if any...
--- a/python/mozbuild/mozbuild/artifacts.py
+++ b/python/mozbuild/mozbuild/artifacts.py
@@ -540,16 +540,22 @@ JOB_DETAILS = {
                                                 r'public/build/fennec-(.*)\.common\.tests\.(zip|tar\.gz)|'
                                                 r'public/build/target\.common\.tests\.(zip|tar\.gz)')),
     'android-api-16-debug': (AndroidArtifactJob, (r'public/build/target\.apk',
                                                   r'public/build/target\.common\.tests\.(zip|tar\.gz)')),
     'android-x86-opt': (AndroidArtifactJob, (r'public/build/target\.apk',
                                              r'public/build/target\.common\.tests\.(zip|tar\.gz)')),
     'android-x86_64-opt': (AndroidArtifactJob, (r'public/build/target\.apk',
                                                 r'public/build/target\.common\.tests\.(zip|tar\.gz)')),
+    'android-x86_64-debug': (AndroidArtifactJob, (r'public/build/target\.apk',
+                                                  r'public/build/target\.common\.tests\.(zip|tar\.gz)')),
+    'android-aarch64-opt': (AndroidArtifactJob, (r'public/build/target\.apk',
+                                                 r'public/build/target\.common\.tests\.(zip|tar\.gz)')),
+    'android-aarch64-debug': (AndroidArtifactJob, (r'public/build/target\.apk',
+                                                   r'public/build/target\.common\.tests\.(zip|tar\.gz)')),
     'linux-opt': (LinuxArtifactJob, (r'public/build/target\.tar\.bz2',
                                      r'public/build/target\.common\.tests\.(zip|tar\.gz)')),
     'linux-debug': (LinuxArtifactJob, (r'public/build/target\.tar\.bz2',
                                        r'public/build/target\.common\.tests\.(zip|tar\.gz)')),
     'linux64-opt': (LinuxArtifactJob, (r'public/build/target\.tar\.bz2',
                                        r'public/build/target\.common\.tests\.(zip|tar\.gz)')),
     'linux64-debug': (LinuxArtifactJob, (r'public/build/target\.tar\.bz2',
                                          r'public/build/target\.common\.tests\.(zip|tar\.gz)')),
--- a/python/mozbuild/mozbuild/backend/common.py
+++ b/python/mozbuild/mozbuild/backend/common.py
@@ -362,18 +362,19 @@ class CommonBackend(BuildBackend):
     def _write_unified_file(self, unified_file, source_filenames,
                             output_directory, poison_windows_h=False):
         with self._write_file(mozpath.join(output_directory, unified_file)) as f:
             f.write('#define MOZ_UNIFIED_BUILD\n')
             includeTemplate = '#include "%(cppfile)s"'
             if poison_windows_h:
                 includeTemplate += (
                     '\n'
-                    '#ifdef _WINDOWS_\n'
-                    '#error "%(cppfile)s included windows.h"\n'
+                    '#if defined(_WINDOWS_) && !defined(MOZ_WRAPPED_WINDOWS_H)\n'
+                    '#pragma message("wrapper failure reason: " MOZ_WINDOWS_WRAPPER_DISABLED_REASON)\n'
+                    '#error "%(cppfile)s included unwrapped windows.h"\n'
                     "#endif")
             includeTemplate += (
                 '\n'
                 '#ifdef PL_ARENA_CONST_ALIGN_MASK\n'
                 '#error "%(cppfile)s uses PL_ARENA_CONST_ALIGN_MASK, '
                 'so it cannot be built in unified mode."\n'
                 '#undef PL_ARENA_CONST_ALIGN_MASK\n'
                 '#endif\n'
--- a/taskcluster/ci/build/android.yml
+++ b/taskcluster/ci/build/android.yml
@@ -488,16 +488,70 @@ android-aarch64/opt:
         - android-sdk-linux
         - linux64-clang
         - linux64-rust-android
         - linux64-rust-size
         - linux64-cbindgen
         - linux64-sccache
         - linux64-node
 
+android-aarch64/debug:
+    description: "Android 5.0 AArch64 Debug"
+    index:
+        product: mobile
+        job-name: android-aarch64-debug
+    attributes:
+        enable-full-crashsymbols: true
+    treeherder:
+        platform: android-5-0-aarch64/debug
+        symbol: B
+    worker-type: aws-provisioner-v1/gecko-{level}-b-android
+    worker:
+        docker-image: {in-tree: android-build}
+        max-run-time: 7200
+        env:
+            GRADLE_USER_HOME: "/builds/worker/workspace/build/src/mobile/android/gradle/dotgradle-offline"
+            TOOLTOOL_MANIFEST: "mobile/android/config/tooltool-manifests/android/releng.manifest"
+        artifacts:
+            - name: public/android/maven
+              # TODO Bug 1433198. Remove the following entry once target.maven.zip is uploaded to a maven repository
+              path: /builds/worker/workspace/build/src/obj-firefox/gradle/build/mobile/android/geckoview/maven/
+              type: directory
+            - name: public/build/target.maven.zip
+              path: /builds/worker/workspace/build/src/obj-firefox/gradle/build/mobile/android/geckoview/target.maven.zip
+              type: file
+            - name: public/build/geckoview-androidTest.apk
+              path: /builds/worker/workspace/build/src/obj-firefox/gradle/build/mobile/android/geckoview/outputs/apk/androidTest/withGeckoBinaries/debug/geckoview-withGeckoBinaries-debug-androidTest.apk
+              type: file
+            - name: public/build/geckoview_example.apk
+              path: /builds/worker/workspace/build/src/obj-firefox/gradle/build/mobile/android/geckoview_example/outputs/apk/withGeckoBinaries/debug/geckoview_example-withGeckoBinaries-debug.apk
+              type: file
+            - name: public/build
+              path: /builds/worker/artifacts/
+              type: directory
+    run:
+        using: mozharness
+        actions: [get-secrets, build, multi-l10n]
+        config:
+            - builds/releng_base_android_64_builds.py
+        script: "mozharness/scripts/fx_desktop_build.py"
+        secrets: true
+        custom-build-variant-cfg: aarch64-debug
+        tooltool-downloads: internal
+    toolchains:
+        - android-gradle-dependencies
+        - android-ndk-linux
+        - android-sdk-linux
+        - linux64-clang
+        - linux64-rust-android
+        - linux64-rust-size
+        - linux64-cbindgen
+        - linux64-sccache
+        - linux64-node
+
 android-aarch64-nightly/opt:
     description: "Android 5.0 AArch64 Nightly"
     attributes:
         nightly: true
         enable-full-crashsymbols: true
     shipping-phase: build
     shipping-product: fennec
     index:
@@ -659,8 +713,62 @@ android-x86_64-nightly/opt:
         - android-ndk-linux
         - android-sdk-linux
         - linux64-clang
         - linux64-rust-android
         - linux64-rust-size
         - linux64-sccache
         - linux64-cbindgen
         - linux64-node
+
+android-x86_64/debug:
+    description: "Android 5.0 x86-64 Debug"
+    index:
+        product: mobile
+        job-name: android-x86_64-debug
+    attributes:
+        enable-full-crashsymbols: true
+    treeherder:
+        platform: android-5-0-x86_64/debug
+        symbol: B
+    worker-type: aws-provisioner-v1/gecko-{level}-b-android
+    worker:
+        docker-image: {in-tree: android-build}
+        max-run-time: 7200
+        env:
+            GRADLE_USER_HOME: "/builds/worker/workspace/build/src/mobile/android/gradle/dotgradle-offline"
+            TOOLTOOL_MANIFEST: "mobile/android/config/tooltool-manifests/android-x86/releng.manifest"
+        artifacts:
+            - name: public/android/maven
+              # TODO Bug 1433198. Remove the following entry once target.maven.zip is uploaded to a maven repository
+              path: /builds/worker/workspace/build/src/obj-firefox/gradle/build/mobile/android/geckoview/maven/
+              type: directory
+            - name: public/build/target.maven.zip
+              path: /builds/worker/workspace/build/src/obj-firefox/gradle/build/mobile/android/geckoview/target.maven.zip
+              type: file
+            - name: public/build/geckoview-androidTest.apk
+              path: /builds/worker/workspace/build/src/obj-firefox/gradle/build/mobile/android/geckoview/outputs/apk/androidTest/withGeckoBinaries/debug/geckoview-withGeckoBinaries-debug-androidTest.apk
+              type: file
+            - name: public/build/geckoview_example.apk
+              path: /builds/worker/workspace/build/src/obj-firefox/gradle/build/mobile/android/geckoview_example/outputs/apk/withGeckoBinaries/debug/geckoview_example-withGeckoBinaries-debug.apk
+              type: file
+            - name: public/build
+              path: /builds/worker/artifacts/
+              type: directory
+    run:
+        using: mozharness
+        actions: [get-secrets, build, multi-l10n]
+        config:
+            - builds/releng_base_android_64_builds.py
+        script: "mozharness/scripts/fx_desktop_build.py"
+        secrets: true
+        custom-build-variant-cfg: x86_64-debug
+        tooltool-downloads: internal
+    toolchains:
+        - android-gradle-dependencies
+        - android-ndk-linux
+        - android-sdk-linux
+        - linux64-clang
+        - linux64-rust-android
+        - linux64-rust-size
+        - linux64-cbindgen
+        - linux64-sccache
+        - linux64-node
--- a/taskcluster/ci/test/compiled.yml
+++ b/taskcluster/ci/test/compiled.yml
@@ -57,18 +57,32 @@ jittest:
     treeherder-symbol: Jit
     instance-size:
         by-test-platform:
             android-em.*: xlarge
             default: default
     run-on-projects:
         by-test-platform:
             android-hw.*: ['try']
-            linux.*: []  # redundant with SM(...)
-            windows.*: []  # redundant with SM(p)
+            # Turn off for all linux and windows platforms, since it is
+            # redundant with SM(...) jobs -- except for code coverage builds,
+            # which are still needed since ccov doesn't yet work with
+            # spidermonkey jobs.
+            #
+            # The regex is essentially /linux.*/ but with a negative match for
+            # the last 5 characters being "-ccov". The terminal ..... is needed
+            # because otherwise the .* could match the full string and the
+            # empty string would fail to match /(?!-ccov)/ so the whole thing
+            # would succeed. The beginning /?=linux/ is needed because the
+            # platform "linux64" doesn't have enough characters for both the
+            # beginning /linux/ and the final /...../.
+            #
+            # Additionally, platforms contain suffixes like "/opt" or "/debug".
+            (?=linux).*(?!-ccov)...../.*: []  # redundant with SM(...)
+            (?=windows).*(?!-ccov)...../.*: []  # redundant with SM(p)
             default: built-projects
     chunks:
         by-test-platform:
             windows.*: 1
             windows10-64-ccov/debug: 6
             macosx.*: 1
             macosx64-ccov/debug: 4
             android-em-4.3-arm7-api-15/debug: 20
--- a/testing/mozbase/mozrunner/mozrunner/devices/android_device.py
+++ b/testing/mozbase/mozrunner/mozrunner/devices/android_device.py
@@ -233,29 +233,50 @@ def verify_android_device(build_obj, ins
         #  - it prevents testing against other builds (downloaded apk)
         #  - installation may take a couple of minutes.
         if not app:
             app = build_obj.substs["ANDROID_PACKAGE_NAME"]
         device = _get_device(build_obj.substs, device_serial)
         response = ''
         while not device.is_app_installed(app):
             try:
-                if 'fennec' not in app and 'firefox' not in app:
-                    response = raw_input(
-                        "It looks like %s is not installed on this device,\n"
-                        "but I don't know how to install it.\n"
-                        "Install it now, then hit Enter or quit to exit " % app)
-                else:
+                if 'fennec' in app or 'firefox' in app:
                     response = response = raw_input(
                         "It looks like %s is not installed on this device.\n"
                         "Install Firefox? (Y/n) or quit to exit " % app).strip()
                     if response.lower().startswith('y') or response == '':
                         _log_info("Installing Firefox. This may take a while...")
                         build_obj._run_make(directory=".", target='install',
                                             ensure_exit_code=False)
+                elif app == 'org.mozilla.geckoview.test':
+                    response = response = raw_input(
+                        "It looks like %s is not installed on this device.\n"
+                        "Install geckoview AndroidTest? (Y/n) or quit to exit " % app).strip()
+                    if response.lower().startswith('y') or response == '':
+                        _log_info("Installing geckoview AndroidTest. This may take a while...")
+                        sub = 'geckoview:installWithGeckoBinariesDebugAndroidTest'
+                        build_obj._mach_context.commands.dispatch('gradle',
+                                                                  args=[sub],
+                                                                  context=build_obj._mach_context)
+                elif app == 'org.mozilla.geckoview_example':
+                    response = response = raw_input(
+                        "It looks like %s is not installed on this device.\n"
+                        "Install geckoview_example? (Y/n) or quit to exit " % app).strip()
+                    if response.lower().startswith('y') or response == '':
+                        _log_info("Installing geckoview_example. This may take a while...")
+                        sub = 'install-geckoview_example'
+                        build_obj._mach_context.commands.dispatch('android',
+                                                                  subcommand=sub,
+                                                                  args=[],
+                                                                  context=build_obj._mach_context)
+                else:
+                    response = raw_input(
+                        "It looks like %s is not installed on this device,\n"
+                        "but I don't know how to install it.\n"
+                        "Install it now, then hit Enter or quit to exit " % app)
             except EOFError:
                 response = 'quit'
             if response == 'quit':
                 device_verified = False
                 break
 
     if device_verified and xre:
         # Check whether MOZ_HOST_BIN has been set to a valid xre; if not,
new file mode 100644
--- /dev/null
+++ b/testing/mozharness/configs/builds/releng_sub_android_configs/64_aarch64_artifact.py
@@ -0,0 +1,6 @@
+config = {
+    'stage_platform': 'android-aarch64',
+    'src_mozconfig': 'mobile/android/config/mozconfigs/android-aarch64/nightly-artifact',
+    'tooltool_manifest_src': 'mobile/android/config/tooltool-manifests/android-aarch64/releng.manifest',
+    'multi_locale_config_platform': 'android',
+}
new file mode 100644
--- /dev/null
+++ b/testing/mozharness/configs/builds/releng_sub_android_configs/64_aarch64_debug.py
@@ -0,0 +1,7 @@
+config = {
+    'stage_platform': 'android-aarch64-debug',
+    'src_mozconfig': 'mobile/android/config/mozconfigs/android-aarch64/debug',
+    'multi_locale_config_platform': 'android',
+    'debug_build': True,
+    'artifact_flag_build_variant_in_try': 'aarch64-debug-artifact',
+}
new file mode 100644
--- /dev/null
+++ b/testing/mozharness/configs/builds/releng_sub_android_configs/64_aarch64_debug_artifact.py
@@ -0,0 +1,7 @@
+config = {
+    'stage_platform': 'android-aarch64-debug',
+    'src_mozconfig': 'mobile/android/config/mozconfigs/android-aarch64/debug-artifact',
+    'tooltool_manifest_src': 'mobile/android/config/tooltool-manifests/android-aarch64/releng.manifest',
+    'multi_locale_config_platform': 'android',
+    'debug_build': True,
+}
new file mode 100644
--- /dev/null
+++ b/testing/mozharness/configs/builds/releng_sub_android_configs/64_x86_64_debug.py
@@ -0,0 +1,7 @@
+config = {
+    'stage_platform': 'android-x86_64-debug',
+    'src_mozconfig': 'mobile/android/config/mozconfigs/android-x86_64/debug',
+    'multi_locale_config_platform': 'android',
+    'debug_build': True,
+    'artifact_flag_build_variant_in_try': 'x86_64-debug-artifact',
+}
new file mode 100644
--- /dev/null
+++ b/testing/mozharness/configs/builds/releng_sub_android_configs/64_x86_64_debug_artifact.py
@@ -0,0 +1,6 @@
+config = {
+    'stage_platform': 'android-x86_64-debug',
+    'src_mozconfig': 'mobile/android/config/mozconfigs/android-x86_64/debug-artifact',
+    'tooltool_manifest_src': 'mobile/android/config/tooltool-manifests/android-x86/releng.manifest',
+    'debug_build': True,
+}
--- a/testing/mozharness/mozharness/mozilla/building/buildbase.py
+++ b/testing/mozharness/mozharness/mozilla/building/buildbase.py
@@ -378,18 +378,23 @@ class BuildOptionParser(object):
             'builds/releng_sub_%s_configs/%s_api_16_without_google_play_services.py',
         'rusttests': 'builds/releng_sub_%s_configs/%s_rusttests.py',
         'rusttests-debug': 'builds/releng_sub_%s_configs/%s_rusttests_debug.py',
         'x86': 'builds/releng_sub_%s_configs/%s_x86.py',
         'x86-artifact': 'builds/releng_sub_%s_configs/%s_x86_artifact.py',
         'x86-fuzzing-debug': 'builds/releng_sub_%s_configs/%s_x86_fuzzing_debug.py',
         'x86_64': 'builds/releng_sub_%s_configs/%s_x86_64.py',
         'x86_64-artifact': 'builds/releng_sub_%s_configs/%s_x86_64_artifact.py',
+        'x86_64-debug': 'builds/releng_sub_%s_configs/%s_x86_64_debug.py',
+        'x86_64-debug-artifact': 'builds/releng_sub_%s_configs/%s_x86_64_debug_artifact.py',
         'api-16-partner-sample1': 'builds/releng_sub_%s_configs/%s_api_16_partner_sample1.py',
         'aarch64': 'builds/releng_sub_%s_configs/%s_aarch64.py',
+        'aarch64-artifact': 'builds/releng_sub_%s_configs/%s_aarch64_artifact.py',
+        'aarch64-debug': 'builds/releng_sub_%s_configs/%s_aarch64_debug.py',
+        'aarch64-debug-artifact': 'builds/releng_sub_%s_configs/%s_aarch64_debug_artifact.py',
         'android-test': 'builds/releng_sub_%s_configs/%s_test.py',
         'android-test-ccov': 'builds/releng_sub_%s_configs/%s_test_ccov.py',
         'android-checkstyle': 'builds/releng_sub_%s_configs/%s_checkstyle.py',
         'android-api-lint': 'builds/releng_sub_%s_configs/%s_api_lint.py',
         'android-lint': 'builds/releng_sub_%s_configs/%s_lint.py',
         'android-findbugs': 'builds/releng_sub_%s_configs/%s_findbugs.py',
         'android-geckoview-docs': 'builds/releng_sub_%s_configs/%s_geckoview_docs.py',
         'valgrind': 'builds/releng_sub_%s_configs/%s_valgrind.py',
--- a/toolkit/components/mozintl/MozIntlHelper.cpp
+++ b/toolkit/components/mozintl/MozIntlHelper.cpp
@@ -79,37 +79,16 @@ MozIntlHelper::AddDateTimeFormatConstruc
   if (!js::AddMozDateTimeFormatConstructor(cx, realIntlObj)) {
     return NS_ERROR_FAILURE;
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
-MozIntlHelper::AddRelativeTimeFormatConstructor(JS::Handle<JS::Value> val, JSContext* cx)
-{
-  if (!val.isObject()) {
-    return NS_ERROR_INVALID_ARG;
-  }
-
-  JS::Rooted<JSObject*> realIntlObj(cx, js::CheckedUnwrap(&val.toObject()));
-  if (!realIntlObj) {
-    return NS_ERROR_INVALID_ARG;
-  }
-
-  JSAutoRealm ar(cx, realIntlObj);
-
-  if (!js::AddRelativeTimeFormatConstructor(cx, realIntlObj)) {
-    return NS_ERROR_FAILURE;
-  }
-
-  return NS_OK;
-}
-
-NS_IMETHODIMP
 MozIntlHelper::AddGetLocaleInfo(JS::Handle<JS::Value> val, JSContext* cx)
 {
   static const JSFunctionSpec funcs[] = {
     JS_SELF_HOSTED_FN("getLocaleInfo", "Intl_getLocaleInfo", 1, 0),
     JS_FS_END
   };
 
   return AddFunctions(cx, val, funcs);
--- a/toolkit/components/mozintl/mozIMozIntlHelper.idl
+++ b/toolkit/components/mozintl/mozIMozIntlHelper.idl
@@ -37,16 +37,9 @@ interface mozIMozIntlHelper : nsISupport
    * Operating System regional preferences and adjust for that.
    *
    * That means that if user will manually select time format (hour12/24) or
    * adjust how the date should be displayed, MozDateTimeFormat will use that.
    *
    * This API should be used everywhere in the UI instead of regular Intl  API.
    */
   [implicit_jscontext] void addDateTimeFormatConstructor(in jsval intlObject);
-
-  /**
-   * Adds a RelativeTimeFormat constructor to the given object.  This function may only
-   * be called once within a realm/global object: calling it multiple times will
-   * throw.
-   */
-  [implicit_jscontext] void addRelativeTimeFormatConstructor(in jsval intlObject);
 };
--- a/toolkit/components/mozintl/mozIntl.js
+++ b/toolkit/components/mozintl/mozIntl.js
@@ -350,23 +350,17 @@ class MozIntl {
       constructor(locales, options, ...args) {
         super(getLocales(locales), options, ...args);
       }
     }
     return MozPluralRules;
   }
 
   get RelativeTimeFormat() {
-    if (!this._cache.hasOwnProperty("RelativeTimeFormat")) {
-      mozIntlHelper.addRelativeTimeFormatConstructor(this._cache);
-    }
-
-    const RelativeTimeFormat = this._cache.RelativeTimeFormat;
-
-    class MozRelativeTimeFormat extends RelativeTimeFormat {
+    class MozRelativeTimeFormat extends Intl.RelativeTimeFormat {
       constructor(locales, options = {}, ...args) {
 
         // If someone is asking for MozRelativeTimeFormat, it's likely they'll want
         // to use `formatBestUnit` which works better with `auto`
         if (options.numeric === undefined) {
           options.numeric = "auto";
         }
         super(getLocales(locales), options, ...args);
--- a/toolkit/components/mozintl/test/test_mozintlhelper.js
+++ b/toolkit/components/mozintl/test/test_mozintlhelper.js
@@ -32,27 +32,23 @@ function test_cross_global(miHelper) {
   equal(waivedX.getCalendarInfo() instanceof global.Object, true);
 }
 
 function test_methods_presence(miHelper) {
   equal(miHelper.addGetCalendarInfo instanceof Function, true);
   equal(miHelper.addGetDisplayNames instanceof Function, true);
   equal(miHelper.addGetLocaleInfo instanceof Function, true);
   equal(miHelper.addDateTimeFormatConstructor instanceof Function, true);
-  equal(miHelper.addRelativeTimeFormatConstructor instanceof Function, true);
 
   let x = {};
 
   miHelper.addGetCalendarInfo(x);
   equal(x.getCalendarInfo instanceof Function, true);
 
   miHelper.addGetDisplayNames(x);
   equal(x.getDisplayNames instanceof Function, true);
 
   miHelper.addGetLocaleInfo(x);
   equal(x.getLocaleInfo instanceof Function, true);
 
   miHelper.addDateTimeFormatConstructor(x);
   equal(x.DateTimeFormat instanceof Function, true);
-
-  miHelper.addRelativeTimeFormatConstructor(x);
-  equal(x.RelativeTimeFormat instanceof Function, true);
 }