Merge autoland to mozilla-central a=merge
authorarthur.iakab <aiakab@mozilla.com>
Wed, 15 Aug 2018 00:59:17 +0300
changeset 431426 fa2f3d22c1564e6318725061c390834729a4df66
parent 431405 0fc57261a269af5b3f787a48395c83ab558c3035 (current diff)
parent 431425 1e0dffecbfb06dafcd853949ac06e4f0e8c7eaca (diff)
child 431567 b80906e2fbc9f9ea6ad8eab753dafc9fb9b56b39
child 431586 34f564520a234b4318cce5be5c86adcd2be8187b
push id34442
push useraiakab@mozilla.com
push dateTue, 14 Aug 2018 21:59:35 +0000
treeherdermozilla-central@fa2f3d22c156 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone63.0a1
first release with
nightly linux32
fa2f3d22c156 / 63.0a1 / 20180814220344 / files
nightly linux64
fa2f3d22c156 / 63.0a1 / 20180814220344 / files
nightly mac
fa2f3d22c156 / 63.0a1 / 20180814220344 / files
nightly win32
fa2f3d22c156 / 63.0a1 / 20180814220344 / files
nightly win64
fa2f3d22c156 / 63.0a1 / 20180814220344 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge autoland to mozilla-central a=merge
toolkit/themes/linux/global/tree/twisty-collapsed-rtl.svg
toolkit/themes/linux/global/tree/twisty-collapsed.svg
toolkit/themes/linux/global/tree/twisty-expanded.svg
toolkit/themes/osx/global/tree/arrow-collapsed-rtl.svg
toolkit/themes/osx/global/tree/arrow-collapsed.svg
toolkit/themes/osx/global/tree/arrow-expanded.svg
toolkit/themes/windows/global/tree/twisty-collapsed-rtl.svg
toolkit/themes/windows/global/tree/twisty-collapsed.svg
toolkit/themes/windows/global/tree/twisty-expanded.svg
--- a/browser/base/content/browser-gestureSupport.js
+++ b/browser/base/content/browser-gestureSupport.js
@@ -762,14 +762,13 @@ var gHistorySwipeAnimation = {
    *
    * @param aID
    *        An identifier to create the element with.
    * @param aTagName
    *        The name of the tag to create the element for.
    * @return the newly created element.
    */
   _createElement: function HSA__createElement(aID, aTagName) {
-    let XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
-    let element = document.createElementNS(XULNS, aTagName);
+    let element = document.createXULElement(aTagName);
     element.id = aID;
     return element;
   },
 };
--- a/browser/base/content/browser-plugins.js
+++ b/browser/base/content/browser-plugins.js
@@ -648,18 +648,17 @@ var gPluginHandler = {
 
       buttons.push(submitButton);
     }
 
     notification = notificationBox.appendNotification(messageString, "plugin-crashed",
                                                       iconURL, priority, buttons);
 
     // Add the "learn more" link.
-    let XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
-    let link = notification.ownerDocument.createElementNS(XULNS, "label");
+    let link = notification.ownerDocument.createXULElement("label");
     link.className = "text-link";
     link.setAttribute("value", gNavigatorBundle.getString("crashedpluginsMessage.learnMore"));
     let crashurl = formatURL("app.support.baseURL", true);
     crashurl += "plugin-crashed-notificationbar";
     link.href = crashurl;
     let description = notification.ownerDocument.getAnonymousElementByAttribute(notification, "anonid", "messageText");
     description.appendChild(link);
   },
--- a/browser/base/content/browser.css
+++ b/browser/base/content/browser.css
@@ -61,24 +61,16 @@ searchbar {
 }
 
 /* Prevent shrinking the page content to 0 height and width */
 .browserStack > browser {
   min-height: 25px;
   min-width: 25px;
 }
 
-.browserStack > browser {
-  -moz-binding: url("chrome://browser/content/tabbrowser.xml#tabbrowser-browser");
-}
-
-.browserStack > browser[remote="true"] {
-  -moz-binding: url("chrome://browser/content/tabbrowser.xml#tabbrowser-remote-browser");
-}
-
 toolbar[customizable="true"] {
   -moz-binding: url("chrome://browser/content/customizableui/toolbar.xml#toolbar");
 }
 
 %ifdef XP_MACOSX
 #toolbar-menubar {
   -moz-binding: url("chrome://browser/content/customizableui/toolbar.xml#toolbar-menubar-stub");
 }
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -8071,18 +8071,17 @@ TabModalPromptBox.prototype = {
         allowFocusCheckbox.checked) {
       Services.perms.addFromPrincipal(principalToAllowFocusFor, "focus-tab-by-prompt",
                                       Services.perms.ALLOW_ACTION);
     }
     onCloseCallback.apply(this, args);
   },
 
   appendPrompt(args, onCloseCallback) {
-    const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
-    let newPrompt = document.createElementNS(XUL_NS, "tabmodalprompt");
+    let newPrompt = document.createXULElement("tabmodalprompt");
     let browser = this.browser;
     browser.parentNode.insertBefore(newPrompt, browser.nextElementSibling);
     browser.setAttribute("tabmodalPromptShowing", true);
 
     newPrompt.clientTop; // style flush to assure binding is attached
 
     let prompts = this.listPrompts();
     if (prompts.length > 1) {
@@ -8094,19 +8093,19 @@ TabModalPromptBox.prototype = {
     delete this._allowTabFocusByPromptPrincipal;
 
     let allowFocusCheckbox; // Define outside the if block so we can bind it into the callback.
     let hostForAllowFocusCheckbox = "";
     try {
       hostForAllowFocusCheckbox = principalToAllowFocusFor.URI.host;
     } catch (ex) { /* Ignore exceptions for host-less URIs */ }
     if (hostForAllowFocusCheckbox) {
-      let allowFocusRow = document.createElementNS(XUL_NS, "row");
-      allowFocusCheckbox = document.createElementNS(XUL_NS, "checkbox");
-      let spacer = document.createElementNS(XUL_NS, "spacer");
+      let allowFocusRow = document.createXULElement("row");
+      allowFocusCheckbox = document.createXULElement("checkbox");
+      let spacer = document.createXULElement("spacer");
       allowFocusRow.appendChild(spacer);
       let label = gTabBrowserBundle.formatStringFromName("tabs.allowTabFocusByPromptForSite",
                                                       [hostForAllowFocusCheckbox], 1);
       allowFocusCheckbox.setAttribute("label", label);
       allowFocusRow.appendChild(allowFocusCheckbox);
       newPrompt.appendChild(allowFocusRow);
     }
 
--- a/browser/base/content/nsContextMenu.js
+++ b/browser/base/content/nsContextMenu.js
@@ -826,16 +826,17 @@ nsContextMenu.prototype = {
     openLinkIn(this.linkURL, "current", this._openLinkInParameters());
   },
 
   // Open frame in a new tab.
   openFrameInTab() {
     let referrer = gContextMenuContentData.referrer;
     openLinkIn(gContextMenuContentData.docLocation, "tab",
                { charset: gContextMenuContentData.charSet,
+                 triggeringPrincipal: this.browser.contentPrincipal,
                  referrerURI: referrer ? makeURI(referrer) : null });
   },
 
   // Reload clicked-in frame.
   reloadFrame(aEvent) {
     let forceReload = aEvent.shiftKey;
     this.browser.messageManager.sendAsyncMessage("ContextMenu:ReloadFrame",
                                                  null, { target: this.target, forceReload });
--- a/browser/base/content/tabbrowser.js
+++ b/browser/base/content/tabbrowser.js
@@ -253,17 +253,17 @@ window._gBrowser = {
   },
 
   get popupAnchor() {
     if (this.selectedTab._popupAnchor) {
       return this.selectedTab._popupAnchor;
     }
     let stack = this.selectedBrowser.parentNode;
     // Create an anchor for the popup
-    let popupAnchor = document.createElementNS(this._XUL_NS, "hbox");
+    let popupAnchor = document.createXULElement("hbox");
     popupAnchor.className = "popup-anchor";
     popupAnchor.hidden = true;
     stack.appendChild(popupAnchor);
     return this.selectedTab._popupAnchor = popupAnchor;
   },
 
   set selectedTab(val) {
     if (gNavToolbox.collapsed && !this._allowTabChange) {
@@ -288,16 +288,17 @@ window._gBrowser = {
   },
 
   _setupInitialBrowserAndTab() {
     let browser = this.initialBrowser;
     this._selectedBrowser = browser;
 
     browser.permanentKey = {};
     browser.droppedLinkHandler = handleDroppedLink;
+    browser.loadURI = _loadURI.bind(null, browser);
 
     let autoScrollPopup = browser._createAutoScrollPopup();
     autoScrollPopup.id = "autoscroller";
     document.getElementById("mainPopupSet").appendChild(autoScrollPopup);
     browser.setAttribute("autoscrollpopup", autoScrollPopup.id);
 
     this._defaultBrowserAttributes = {
       autoscrollpopup: "",
@@ -508,17 +509,17 @@ window._gBrowser = {
   },
 
   /**
    * Create a findbar instance.
    * @param aTab the tab to create the find bar for.
    * @return the created findbar, or null if the window or tab is closed/closing.
    */
   async _createFindBar(aTab) {
-    let findBar = document.createElementNS(this._XUL_NS, "findbar");
+    let findBar = document.createXULElement("findbar");
     let browser = this.getBrowserForTab(aTab);
     let browserContainer = this.getBrowserContainer(browser);
     browserContainer.appendChild(findBar);
 
     await new Promise(r => requestAnimationFrame(r));
     delete aTab._pendingFindBar;
     if (window.closed || aTab.closing) {
       return null;
@@ -1794,17 +1795,17 @@ window._gBrowser = {
     openerWindow,
     recordExecution,
     remoteType,
     replayExecution,
     sameProcessAsFrameLoader,
     uriIsAboutBlank,
     userContextId,
   } = {}) {
-    let b = document.createElementNS(this._XUL_NS, "browser");
+    let b = document.createXULElement("browser");
     b.permanentKey = {};
 
     for (let attribute in this._defaultBrowserAttributes) {
       b.setAttribute(attribute, this._defaultBrowserAttributes[attribute]);
     }
 
     if (userContextId) {
       b.setAttribute("usercontextid", userContextId);
@@ -1869,35 +1870,35 @@ window._gBrowser = {
     // window.
     if (name) {
       // XXX: The `name` property is special in HTML and XUL. Should
       // we use a different attribute name for this?
       b.setAttribute("name", name);
     }
 
     // Create the browserStack container
-    let stack = document.createElementNS(this._XUL_NS, "stack");
+    let stack = document.createXULElement("stack");
     stack.className = "browserStack";
     stack.appendChild(b);
     stack.setAttribute("flex", "1");
 
     // Create the browserContainer
-    let browserContainer = document.createElementNS(this._XUL_NS, "vbox");
+    let browserContainer = document.createXULElement("vbox");
     browserContainer.className = "browserContainer";
     browserContainer.appendChild(stack);
     browserContainer.setAttribute("flex", "10000");
 
     // Create the sidebar container
-    let browserSidebarContainer = document.createElementNS(this._XUL_NS, "hbox");
+    let browserSidebarContainer = document.createXULElement("hbox");
     browserSidebarContainer.className = "browserSidebarContainer";
     browserSidebarContainer.appendChild(browserContainer);
     browserSidebarContainer.setAttribute("flex", "10000");
 
     // Add the Message and the Browser to the box
-    let notificationbox = document.createElementNS(this._XUL_NS, "notificationbox");
+    let notificationbox = document.createXULElement("notificationbox");
     notificationbox.setAttribute("flex", "1");
     notificationbox.setAttribute("notificationside", "top");
     notificationbox.appendChild(browserSidebarContainer);
 
     // Prevent the superfluous initial load of a blank document
     // if we're going to load something other than about:blank.
     if (!uriIsAboutBlank) {
       b.setAttribute("nodefaultsrc", "true");
@@ -2035,16 +2036,17 @@ window._gBrowser = {
     const filter = Cc["@mozilla.org/appshell/component/browser-status-filter;1"]
       .createInstance(Ci.nsIWebProgress);
     filter.addProgressListener(tabListener, Ci.nsIWebProgress.NOTIFY_ALL);
     browser.webProgress.addProgressListener(filter, Ci.nsIWebProgress.NOTIFY_ALL);
     this._tabListeners.set(aTab, tabListener);
     this._tabFilters.set(aTab, filter);
 
     browser.droppedLinkHandler = handleDroppedLink;
+    browser.loadURI = _loadURI.bind(null, browser);
 
     // Most of the time, we start our browser's docShells out as inactive,
     // and then maintain activeness in the tab switcher. Preloaded about:newtab's
     // are already created with their docShell's as inactive, but then explicitly
     // render their layers to ensure that we can switch to them quickly. We avoid
     // setting docShellIsActive to false again in this case, since that'd cause
     // the layers for the preloaded tab to be dropped, and we'd see a flash
     // of empty content instead.
@@ -2228,17 +2230,17 @@ window._gBrowser = {
     // undefined if the tab is opened from an external application or
     // bookmark (i.e. somewhere other than an existing tab).
     if (relatedToCurrent == null) {
       relatedToCurrent = !!referrerURI;
     }
     let openerTab = ((openerBrowser && this.getTabForBrowser(openerBrowser)) ||
       (relatedToCurrent && this.selectedTab));
 
-    var t = document.createElementNS(this._XUL_NS, "tab");
+    var t = document.createXULElement("tab");
 
     t.openerTab = openerTab;
 
     aURI = aURI || "about:blank";
     let aURIObject = null;
     try {
       aURIObject = Services.io.newURI(aURI);
     } catch (ex) { /* we'll try to fix up this URL later */ }
@@ -3719,16 +3721,17 @@ window._gBrowser = {
   },
 
   addToMultiSelectedTabs(aTab, skipPositionalAttributes) {
     if (aTab.multiselected) {
       return;
     }
 
     aTab.setAttribute("multiselected", "true");
+    aTab.setAttribute("aria-selected", "true");
     this._multiSelectedTabsSet.add(aTab);
     this._startMultiSelectChange();
     if (this._multiSelectChangeRemovals.has(aTab)) {
       this._multiSelectChangeRemovals.delete(aTab);
     } else {
       this._multiSelectChangeAdditions.add(aTab);
     }
 
@@ -3758,16 +3761,17 @@ window._gBrowser = {
     this.tabContainer._setPositionalAttributes();
   },
 
   removeFromMultiSelectedTabs(aTab, updatePositionalAttributes) {
     if (!aTab.multiselected) {
       return;
     }
     aTab.removeAttribute("multiselected");
+    aTab.removeAttribute("aria-selected");
     this._multiSelectedTabsSet.delete(aTab);
     this._startMultiSelectChange();
     if (this._multiSelectChangeAdditions.has(aTab)) {
       this._multiSelectChangeAdditions.delete(aTab);
     } else {
       this._multiSelectChangeRemovals.add(aTab);
     }
     if (updatePositionalAttributes) {
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -123,16 +123,21 @@
             newValue => {
               const LIMIT = 50;
               return Math.max(newValue, LIMIT);
             },
           );
 
           this._tabMinWidth = this._tabMinWidthPref;
 
+          XPCOMUtils.defineLazyPreferenceGetter(this, "_multiselectEnabledPref",
+            "browser.tabs.multiselect", null,
+            (pref, prevValue, newValue) => this._multiselectEnabled = newValue);
+          this._multiselectEnabled = this._multiselectEnabledPref;
+
           this._setPositionalAttributes();
 
           CustomizableUI.addListener(this);
           this._updateNewTabVisibility();
 
           XPCOMUtils.defineLazyPreferenceGetter(this, "_closeTabByDblclick",
             "browser.tabs.closeTabByDblclick", false);
         ]]>
@@ -167,16 +172,27 @@
 
       <property name="_tabMinWidth">
         <setter>
           this.style.setProperty("--tab-min-width", val + "px");
           return val;
         </setter>
       </property>
 
+      <property name="_multiselectEnabled">
+        <setter>
+          // Unlike boolean HTML attributes, the value of boolean ARIA attributes actually matters.
+          this.setAttribute("aria-multiselectable", !!val);
+          return val;
+        </setter>
+        <getter>
+          return this.getAttribute("aria-multiselectable") == "true";
+        </getter>
+      </property>
+
       <method name="observe">
         <parameter name="aSubject"/>
         <parameter name="aTopic"/>
         <parameter name="aData"/>
         <body><![CDATA[
           switch (aTopic) {
             case "nsPref:changed":
               // This is has to deal with changes in
@@ -2064,17 +2080,17 @@
           this._selectedOnFirstMouseDown = this.selected;
         }
 
         if (this.selected) {
           this.style.MozUserFocus = "ignore";
         } else {
           // When browser.tabs.multiselect config is set to false,
           // then we ignore the state of multi-selection keys (Ctrl/Cmd).
-          const tabSelectionToggled = Services.prefs.getBoolPref("browser.tabs.multiselect") &&
+          const tabSelectionToggled = tabContainer._multiselectEnabled &&
             (event.getModifierState("Accel") || event.shiftKey);
 
           if (this.mOverCloseButton || this._overPlayingIcon || tabSelectionToggled) {
             // Prevent tabbox.xml from selecting the tab.
             event.stopPropagation();
           }
         }
 
@@ -2087,17 +2103,18 @@
         // Make sure that clear-selection is released.
         // Otherwise selection using Shift key may be broken.
         gBrowser.unlockClearMultiSelection();
 
         this.style.MozUserFocus = "";
       </handler>
 
       <handler event="click" button="0"><![CDATA[
-        if (Services.prefs.getBoolPref("browser.tabs.multiselect")) {
+        let tabContainer = this.parentNode;
+        if (tabContainer._multiselectEnabled) {
           let shiftKey = event.shiftKey;
           let accelKey = event.getModifierState("Accel");
           if (shiftKey) {
             const lastSelectedTab = gBrowser.lastMultiSelectedTab;
             if (!accelKey) {
               gBrowser.selectedTab = lastSelectedTab;
 
               // Make sure selection is cleared when tab-switch doesn't happen.
@@ -2183,46 +2200,9 @@
       <![CDATA[
         if (event.originalTarget.getAttribute("anonid") == "tab-loading-burst") {
           this.removeAttribute("bursting");
         }
       ]]>
       </handler>
     </handlers>
   </binding>
-
-  <binding id="tabbrowser-browser"
-           extends="chrome://global/content/bindings/browser.xml#browser">
-    <implementation>
-      <field name="tabModalPromptBox">null</field>
-
-      <!-- throws exception for unknown schemes -->
-      <method name="loadURI">
-        <parameter name="aURI"/>
-        <parameter name="aParams"/>
-        <body>
-          <![CDATA[
-            _loadURI(this, aURI, aParams);
-          ]]>
-        </body>
-      </method>
-    </implementation>
-  </binding>
-
-  <binding id="tabbrowser-remote-browser"
-           extends="chrome://global/content/bindings/remote-browser.xml#remote-browser">
-    <implementation>
-      <field name="tabModalPromptBox">null</field>
-
-      <!-- throws exception for unknown schemes -->
-      <method name="loadURI">
-        <parameter name="aURI"/>
-        <parameter name="aParams"/>
-        <body>
-          <![CDATA[
-            _loadURI(this, aURI, aParams);
-          ]]>
-        </body>
-      </method>
-    </implementation>
-  </binding>
-
 </bindings>
--- a/browser/base/content/test/contextMenu/browser.ini
+++ b/browser/base/content/test/contextMenu/browser.ini
@@ -2,11 +2,14 @@
 support-files =
   !/browser/base/content/test/general/contextmenu_common.js
   subtst_contextmenu_webext.html
   test_contextmenu_links.html
 
 [browser_contextmenu_touch.js]
 skip-if = !(os == 'win' && os_version == '10.0')
 [browser_contextmenu_linkopen.js]
+[browser_contextmenu_iframe.js]
+support-files =
+  test_contextmenu_iframe.html
 [browser_utilityOverlay.js]
 skip-if = os == "linux" || os == "mac" #Bug 1444631
 [browser_utilityOverlayPrincipal.js]
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/contextMenu/browser_contextmenu_iframe.js
@@ -0,0 +1,47 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const TEST_LINK = "https://example.com/";
+const RESOURCE_LINK = getRootDirectory(gTestPath).replace("chrome://mochitests/content", "https://example.com") + "test_contextmenu_iframe.html";
+
+/* This test checks that a context menu can open up
+ * a frame into it's own tab. */
+
+add_task(async function test_open_iframe() {
+  let testTab = await BrowserTestUtils.openNewForegroundTab(gBrowser, RESOURCE_LINK);
+  const selector = "#iframe";
+  const openPromise = BrowserTestUtils.waitForNewTab(gBrowser, TEST_LINK, false);
+  const contextMenu = document.getElementById("contentAreaContextMenu");
+  is(contextMenu.state, "closed", "checking if popup is closed");
+  let awaitPopupShown = BrowserTestUtils.waitForEvent(contextMenu, "popupshown");
+  await BrowserTestUtils.synthesizeMouseAtCenter(selector, {
+      type: "contextmenu",
+      button: 2,
+      centered: true,
+    },
+    gBrowser.selectedBrowser);
+  await awaitPopupShown;
+  info("Popup Shown");
+  const awaitPopupHidden = BrowserTestUtils.waitForEvent(contextMenu, "popuphidden");
+
+  // Open frame submenu
+  const menuPopup = contextMenu.querySelector("#frame").menupopup;
+  const menuPopupPromise = BrowserTestUtils.waitForEvent(menuPopup, "popupshown");
+  menuPopup.openPopup();
+  await menuPopupPromise;
+
+  let domItem = contextMenu.querySelector("#context-openframeintab");
+  info("Going to click item " + domItem.id);
+  ok(BrowserTestUtils.is_visible(domItem), "DOM context menu item tab should be visible");
+  ok(!domItem.disabled, "DOM context menu item tab shouldn't be disabled");
+  domItem.click();
+
+  let openedTab = await openPromise;
+  contextMenu.hidePopup();
+  await awaitPopupHidden;
+  await BrowserTestUtils.removeTab(openedTab);
+
+  BrowserTestUtils.removeTab(testTab);
+});
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/contextMenu/test_contextmenu_iframe.html
@@ -0,0 +1,11 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Subtest for browser context menu iframes</title>
+</head>
+<body>
+Browser context menu iframe subtest.
+
+<iframe src="https://example.com/" id="iframe"></iframe>
+</body>
+</html>
--- a/browser/base/content/test/tabs/browser_multiselect_tabs_using_selectedTabs.js
+++ b/browser/base/content/test/tabs/browser_multiselect_tabs_using_selectedTabs.js
@@ -7,30 +7,35 @@ const PREF_MULTISELECT_TABS = "browser.t
 add_task(async function() {
   await SpecialPowers.pushPrefEnv({
     set: [
       [PREF_MULTISELECT_TABS, true]
     ]
   });
 
   function testSelectedTabs(tabs) {
+    is(gBrowser.tabContainer.getAttribute("aria-multiselectable"), "true",
+       "tabbrowser should be marked as aria-multiselectable");
     gBrowser.selectedTabs = tabs;
     let {selectedTab, selectedTabs, _multiSelectedTabsSet} = gBrowser;
     is(selectedTab, tabs[0], "The selected tab should be the expected one");
     if (tabs.length == 1) {
       ok(!selectedTab.multiselected, "Selected tab shouldn't be multi-selected because we are not in multi-select context yet");
       ok(!_multiSelectedTabsSet.has(selectedTab), "Selected tab shouldn't be in _multiSelectedTabsSet");
       is(selectedTabs.length, 1, "selectedTabs should contain a single tab");
       is(selectedTabs[0], selectedTab, "selectedTabs should contain the selected tab");
+      ok(!selectedTab.hasAttribute("aria-selected"),
+         "Selected tab shouldn't be marked as aria-selected when only one tab is selected");
     } else {
       ok(selectedTabs.length, tabs.length, "Check number of selected tabs");
       for (let tab of tabs) {
         ok(tab.multiselected, "Tab should be multi-selected");
         ok(_multiSelectedTabsSet.has(tab), "Tab should be in _multiSelectedTabsSet");
         ok(selectedTabs.includes(tab), "Tab should be in selectedTabs");
+        is(tab.getAttribute("aria-selected"), "true", "Selected tab should be marked as aria-selected");
       }
     }
   }
 
   const tab1 = await addTab();
   const tab2 = await addTab();
   const tab3 = await addTab();
 
--- a/browser/base/content/urlbarBindings.xml
+++ b/browser/base/content/urlbarBindings.xml
@@ -2637,19 +2637,17 @@ file, You can obtain one at http://mozil
           for (let anonid of this._addonIframeHiddenAnonids) {
             let child = document.getAnonymousElementByAttribute(
               this, "anonid", anonid
             );
             this._addonIframeHiddenDisplaysByAnonid[anonid] =
               child.style.display;
             child.style.display = "none";
           }
-          let XUL_NS =
-            "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
-          let iframe = document.createElementNS(XUL_NS, "iframe");
+          let iframe = document.createXULElement("iframe");
           iframe.setAttribute("type", "content");
           iframe.setAttribute("flex", "1");
           iframe.style.transition = "height 100ms";
           this.appendChild(iframe);
           return iframe;
         ]]></body>
       </method>
 
--- a/browser/base/content/webext-panels.js
+++ b/browser/base/content/webext-panels.js
@@ -20,20 +20,20 @@ var {
 const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
 
 function getBrowser(sidebar) {
   let browser = document.getElementById("webext-panels-browser");
   if (browser) {
     return Promise.resolve(browser);
   }
 
-  let stack = document.createElementNS(XUL_NS, "stack");
+  let stack = document.createXULElement("stack");
   stack.setAttribute("flex", "1");
 
-  browser = document.createElementNS(XUL_NS, "browser");
+  browser = document.createXULElement("browser");
   browser.setAttribute("id", "webext-panels-browser");
   browser.setAttribute("type", "content");
   browser.setAttribute("flex", "1");
   browser.setAttribute("disableglobalhistory", "true");
   browser.setAttribute("webextension-view-type", "sidebar");
   browser.setAttribute("context", "contentAreaContextMenu");
   browser.setAttribute("tooltip", "aHTMLTooltip");
   browser.setAttribute("autocompletepopup", "PopupAutoComplete");
--- a/browser/components/customizableui/CustomizableUI.jsm
+++ b/browser/components/customizableui/CustomizableUI.jsm
@@ -23,18 +23,16 @@ XPCOMUtils.defineLazyModuleGetters(this,
 XPCOMUtils.defineLazyGetter(this, "gWidgetsBundle", function() {
   const kUrl = "chrome://browser/locale/customizableui/customizableWidgets.properties";
   return Services.strings.createBundle(kUrl);
 });
 
 XPCOMUtils.defineLazyServiceGetter(this, "gELS",
   "@mozilla.org/eventlistenerservice;1", "nsIEventListenerService");
 
-const kNSXUL = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
-
 const kSpecialWidgetPfx = "customizableui-special-";
 
 const kPrefCustomizationState        = "browser.uiCustomization.state";
 const kPrefCustomizationAutoAdd      = "browser.uiCustomization.autoAdd";
 const kPrefCustomizationDebug        = "browser.uiCustomization.debug";
 const kPrefDrawInTitlebar            = "browser.tabs.drawInTitlebar";
 const kPrefExtraDragSpace            = "browser.tabs.extraDragSpace";
 const kPrefUIDensity                 = "browser.uidensity";
@@ -1359,17 +1357,17 @@ var CustomizableUIInternal = {
       // Ids are differentiated through a unique count suffix.
       return kSpecialWidgetPfx + aId + (++gNewElementCount);
     }
     return aId;
   },
 
   createSpecialWidget(aId, aDocument) {
     let nodeName = "toolbar" + aId.match(/spring|spacer|separator/)[0];
-    let node = aDocument.createElementNS(kNSXUL, nodeName);
+    let node = aDocument.createXULElement(nodeName);
     node.className = "chromeclass-toolbar-additional";
     node.id = this.ensureSpecialWidgetId(aId);
     return node;
   },
 
   /* Find a XUL-provided widget in a window. Don't try to use this
    * for an API-provided widget or a special widget.
    */
@@ -1454,17 +1452,17 @@ var CustomizableUIInternal = {
         node = aWidget.onBuild(aDocument);
       }
       if (!node || !(node instanceof aDocument.defaultView.XULElement))
         log.error("Custom widget with id " + aWidget.id + " does not return a valid node");
     } else {
       if (aWidget.onBeforeCreated) {
         aWidget.onBeforeCreated(aDocument);
       }
-      node = aDocument.createElementNS(kNSXUL, "toolbarbutton");
+      node = aDocument.createXULElement("toolbarbutton");
 
       node.setAttribute("id", aWidget.id);
       node.setAttribute("widget-id", aWidget.id);
       node.setAttribute("widget-type", aWidget.type);
       if (aWidget.disabled) {
         node.setAttribute("disabled", true);
       }
       node.setAttribute("removable", aWidget.removable);
@@ -3901,19 +3899,19 @@ var CustomizableUI = {
 
       let subviewItem;
       if (menuChild.localName == "menuseparator") {
         // Don't insert duplicate or leading separators. This can happen if there are
         // menus (which we don't copy) above the separator.
         if (!fragment.lastElementChild || fragment.lastElementChild.localName == "menuseparator") {
           continue;
         }
-        subviewItem = doc.createElementNS(kNSXUL, "menuseparator");
+        subviewItem = doc.createXULElement("menuseparator");
       } else if (menuChild.localName == "menuitem") {
-        subviewItem = doc.createElementNS(kNSXUL, "toolbarbutton");
+        subviewItem = doc.createXULElement("toolbarbutton");
         CustomizableUI.addShortcut(menuChild, subviewItem);
 
         let item = menuChild;
         if (!item.hasAttribute("onclick")) {
           subviewItem.addEventListener("click", event => {
             let newEvent = new doc.defaultView.MouseEvent(event.type, event);
             item.dispatchEvent(newEvent);
           });
--- a/browser/components/customizableui/CustomizableWidgets.jsm
+++ b/browser/components/customizableui/CustomizableWidgets.jsm
@@ -21,17 +21,16 @@ XPCOMUtils.defineLazyModuleGetters(this,
   SyncedTabs: "resource://services-sync/SyncedTabs.jsm",
 });
 
 XPCOMUtils.defineLazyGetter(this, "CharsetBundle", function() {
   const kCharsetBundle = "chrome://global/locale/charsetMenu.properties";
   return Services.strings.createBundle(kCharsetBundle);
 });
 
-const kNSXUL = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
 const kPrefCustomizationDebug = "browser.uiCustomization.debug";
 
 XPCOMUtils.defineLazyGetter(this, "log", () => {
   let scope = {};
   ChromeUtils.import("resource://gre/modules/Console.jsm", scope);
   let debug = Services.prefs.getBoolPref(kPrefCustomizationDebug, false);
   let consoleOptions = {
     maxLogLevel: debug ? "all" : "log",
@@ -191,20 +190,20 @@ const CustomizableWidgets = [
     tooltiptext: "sidebar-button.tooltiptext2",
     onCommand(aEvent) {
       let win = aEvent.target.ownerGlobal;
       win.SidebarUI.toggle();
     },
     onCreated(aNode) {
       // Add an observer so the button is checked while the sidebar is open
       let doc = aNode.ownerDocument;
-      let obChecked = doc.createElementNS(kNSXUL, "observes");
+      let obChecked = doc.createXULElement("observes");
       obChecked.setAttribute("element", "sidebar-box");
       obChecked.setAttribute("attribute", "checked");
-      let obPosition = doc.createElementNS(kNSXUL, "observes");
+      let obPosition = doc.createXULElement("observes");
       obPosition.setAttribute("element", "sidebar-box");
       obPosition.setAttribute("attribute", "positionend");
 
       aNode.appendChild(obChecked);
       aNode.appendChild(obPosition);
     }
   }, {
     id: "add-ons-button",
@@ -239,29 +238,29 @@ const CustomizableWidgets = [
         command: "cmd_fullZoomEnlarge",
         closemenu: "none",
         label: true,
         tooltiptext: "tooltiptext2",
         shortcutId: "key_fullZoomEnlarge",
         "class": "toolbarbutton-1 toolbarbutton-combined",
       }];
 
-      let node = aDocument.createElementNS(kNSXUL, "toolbaritem");
+      let node = aDocument.createXULElement("toolbaritem");
       node.setAttribute("id", "zoom-controls");
       node.setAttribute("label", CustomizableUI.getLocalizedProperty(this, "label"));
       node.setAttribute("title", CustomizableUI.getLocalizedProperty(this, "tooltiptext"));
       // Set this as an attribute in addition to the property to make sure we can style correctly.
       node.setAttribute("removable", "true");
       node.classList.add("chromeclass-toolbar-additional");
       node.classList.add("toolbaritem-combined-buttons");
 
       buttons.forEach(function(aButton, aIndex) {
         if (aIndex != 0)
-          node.appendChild(aDocument.createElementNS(kNSXUL, "separator"));
-        let btnNode = aDocument.createElementNS(kNSXUL, "toolbarbutton");
+          node.appendChild(aDocument.createXULElement("separator"));
+        let btnNode = aDocument.createXULElement("toolbarbutton");
         setAttributes(btnNode, aButton);
         node.appendChild(btnNode);
       });
       return node;
     }
   }, {
     id: "edit-controls",
     type: "custom",
@@ -285,29 +284,29 @@ const CustomizableWidgets = [
         id: "paste-button",
         command: "cmd_paste",
         label: true,
         tooltiptext: "tooltiptext2",
         shortcutId: "key_paste",
         "class": "toolbarbutton-1 toolbarbutton-combined",
       }];
 
-      let node = aDocument.createElementNS(kNSXUL, "toolbaritem");
+      let node = aDocument.createXULElement("toolbaritem");
       node.setAttribute("id", "edit-controls");
       node.setAttribute("label", CustomizableUI.getLocalizedProperty(this, "label"));
       node.setAttribute("title", CustomizableUI.getLocalizedProperty(this, "tooltiptext"));
       // Set this as an attribute in addition to the property to make sure we can style correctly.
       node.setAttribute("removable", "true");
       node.classList.add("chromeclass-toolbar-additional");
       node.classList.add("toolbaritem-combined-buttons");
 
       buttons.forEach(function(aButton, aIndex) {
         if (aIndex != 0)
-          node.appendChild(aDocument.createElementNS(kNSXUL, "separator"));
-        let btnNode = aDocument.createElementNS(kNSXUL, "toolbarbutton");
+          node.appendChild(aDocument.createXULElement("separator"));
+        let btnNode = aDocument.createXULElement("toolbarbutton");
         setAttributes(btnNode, aButton);
         node.appendChild(btnNode);
       });
 
       let listener = {
         onWidgetInstanceRemoved: (aWidgetId, aDoc) => {
           if (aWidgetId != this.id || aDoc != aDocument)
             return;
@@ -381,17 +380,17 @@ const CustomizableWidgets = [
     populateList(aDocument, aContainerId, aSection) {
       let containerElem = aDocument.getElementById(aContainerId);
 
       containerElem.addEventListener("command", this.onCommand);
 
       let list = this.charsetInfo[aSection];
 
       for (let item of list) {
-        let elem = aDocument.createElementNS(kNSXUL, "toolbarbutton");
+        let elem = aDocument.createXULElement("toolbarbutton");
         elem.setAttribute("label", item.label);
         elem.setAttribute("type", "checkbox");
         elem.section = aSection;
         elem.value = item.value;
         elem.setAttribute("class", "subviewbutton");
         containerElem.appendChild(elem);
       }
     },
@@ -639,17 +638,17 @@ if (Services.prefs.getBoolPref("identity
         this.setDeckIndex(this.deckIndices.DECKINDEX_TABS);
         this._clearTabList();
         SyncedTabs.sortTabClientsByLastUsed(clients);
         let fragment = doc.createDocumentFragment();
 
         for (let client of clients) {
           // add a menu separator for all clients other than the first.
           if (fragment.lastElementChild) {
-            let separator = doc.createElementNS(kNSXUL, "menuseparator");
+            let separator = doc.createXULElement("menuseparator");
             fragment.appendChild(separator);
           }
           if (paginationInfo && paginationInfo.clientId == client.id) {
             this._appendClient(client, fragment, paginationInfo.maxTabs);
           } else {
             this._appendClient(client, fragment);
           }
         }
@@ -673,25 +672,25 @@ if (Services.prefs.getBoolPref("identity
       this._appendMessageLabel("notabslabel");
     },
     _appendMessageLabel(messageAttr, appendTo = null) {
       if (!appendTo) {
         appendTo = this._tabsList;
       }
       let message = this._tabsList.getAttribute(messageAttr);
       let doc = this._tabsList.ownerDocument;
-      let messageLabel = doc.createElementNS(kNSXUL, "label");
+      let messageLabel = doc.createXULElement("label");
       messageLabel.textContent = message;
       appendTo.appendChild(messageLabel);
       return messageLabel;
     },
     _appendClient(client, attachFragment, maxTabs = this.TABS_PER_PAGE) {
       let doc = attachFragment.ownerDocument;
       // Create the element for the remote client.
-      let clientItem = doc.createElementNS(kNSXUL, "label");
+      let clientItem = doc.createXULElement("label");
       clientItem.setAttribute("itemtype", "client");
       let window = doc.defaultView;
       clientItem.setAttribute("tooltiptext",
         window.gSync.formatLastSyncDate(new Date(client.lastModified)));
       clientItem.textContent = client.name;
 
       attachFragment.appendChild(clientItem);
 
@@ -721,17 +720,17 @@ if (Services.prefs.getBoolPref("identity
                                                        nextPageIsLastPage ?
                                                        Infinity :
                                                        maxTabs + this.TABS_PER_PAGE);
           attachFragment.appendChild(showAllEnt);
         }
       }
     },
     _createTabElement(doc, tabInfo) {
-      let item = doc.createElementNS(kNSXUL, "toolbarbutton");
+      let item = doc.createXULElement("toolbarbutton");
       let tooltipText = (tabInfo.title ? tabInfo.title + "\n" : "") + tabInfo.url;
       item.setAttribute("itemtype", "tab");
       item.setAttribute("class", "subviewbutton");
       item.setAttribute("targetURI", tabInfo.url);
       item.setAttribute("label", tabInfo.title != "" ? tabInfo.title : tabInfo.url);
       item.setAttribute("image", tabInfo.icon);
       item.setAttribute("tooltiptext", tooltipText);
       // We need to use "click" instead of "command" here so openUILink
@@ -753,17 +752,17 @@ if (Services.prefs.getBoolPref("identity
       let labelAttr, tooltipAttr;
       if (showCount === Infinity) {
         labelAttr = "showAllLabel";
         tooltipAttr = "showAllTooltipText";
       } else {
         labelAttr = "showMoreLabel";
         tooltipAttr = "showMoreTooltipText";
       }
-      let showAllItem = doc.createElementNS(kNSXUL, "toolbarbutton");
+      let showAllItem = doc.createXULElement("toolbarbutton");
       showAllItem.setAttribute("itemtype", "showmorebutton");
       showAllItem.setAttribute("class", "subviewbutton");
       let label = this._tabsList.getAttribute(labelAttr);
       showAllItem.setAttribute("label", label);
       let tooltipText = this._tabsList.getAttribute(tooltipAttr);
       showAllItem.setAttribute("tooltiptext", tooltipText);
       showAllItem.addEventListener("click", e => {
         e.preventDefault();
--- a/browser/components/customizableui/content/panelUI.js
+++ b/browser/components/customizableui/content/panelUI.js
@@ -618,30 +618,29 @@ const PanelUI = {
 
   _onHelpViewShow(aEvent) {
     // Call global menu setup function
     buildHelpMenu();
 
     let helpMenu = document.getElementById("menu_HelpPopup");
     let items = this.getElementsByTagName("vbox")[0];
     let attrs = ["oncommand", "onclick", "label", "key", "disabled"];
-    let NSXUL = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
 
     // Remove all buttons from the view
     while (items.firstChild) {
       items.firstChild.remove();
     }
 
     // Add the current set of menuitems of the Help menu to this view
     let menuItems = Array.prototype.slice.call(helpMenu.getElementsByTagName("menuitem"));
     let fragment = document.createDocumentFragment();
     for (let node of menuItems) {
       if (node.hidden)
         continue;
-      let button = document.createElementNS(NSXUL, "toolbarbutton");
+      let button = document.createXULElement("toolbarbutton");
       // Copy specific attributes from a menuitem of the Help menu
       for (let attrName of attrs) {
         if (!node.hasAttribute(attrName))
           continue;
         button.setAttribute(attrName, node.getAttribute(attrName));
       }
       button.setAttribute("class", "subviewbutton");
       fragment.appendChild(button);
--- a/browser/components/extensions/ExtensionPopups.jsm
+++ b/browser/components/extensions/ExtensionPopups.jsm
@@ -28,18 +28,16 @@ var {
 
 const {
   makeWidgetId,
 } = ExtensionCommon;
 
 
 const POPUP_LOAD_TIMEOUT_MS = 200;
 
-const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
-
 function promisePopupShown(popup) {
   return new Promise(resolve => {
     if (popup.state == "open") {
       resolve();
     } else {
       popup.addEventListener("popupshown", function(event) {
         resolve();
       }, {once: true});
@@ -234,20 +232,20 @@ class BasePopup {
         }
         break;
     }
   }
 
   createBrowser(viewNode, popupURL = null) {
     let document = viewNode.ownerDocument;
 
-    let stack = document.createElementNS(XUL_NS, "stack");
+    let stack = document.createXULElement("stack");
     stack.setAttribute("class", "webextension-popup-stack");
 
-    let browser = document.createElementNS(XUL_NS, "browser");
+    let browser = document.createXULElement("browser");
     browser.setAttribute("type", "content");
     browser.setAttribute("disableglobalhistory", "true");
     browser.setAttribute("transparent", "true");
     browser.setAttribute("class", "webextension-popup-browser");
     browser.setAttribute("webextension-view-type", "popup");
     browser.setAttribute("tooltip", "aHTMLTooltip");
     browser.setAttribute("contextmenu", "contentAreaContextMenu");
     browser.setAttribute("autocompletepopup", "PopupAutoComplete");
--- a/browser/components/extensions/parent/ext-browserAction.js
+++ b/browser/components/extensions/parent/ext-browserAction.js
@@ -26,18 +26,16 @@ var {
 } = ExtensionParent;
 
 XPCOMUtils.defineLazyGlobalGetters(this, ["InspectorUtils"]);
 
 const POPUP_PRELOAD_TIMEOUT_MS = 200;
 const POPUP_OPEN_MS_HISTOGRAM = "WEBEXT_BROWSERACTION_POPUP_OPEN_MS";
 const POPUP_RESULT_HISTOGRAM = "WEBEXT_BROWSERACTION_POPUP_PRELOAD_RESULT_COUNT";
 
-var XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
-
 // WeakMap[Extension -> BrowserAction]
 const browserActionMap = new WeakMap();
 
 XPCOMUtils.defineLazyGetter(this, "browserAreas", () => {
   return {
     "navbar": CustomizableUI.AREA_NAVBAR,
     "menupanel": CustomizableUI.AREA_FIXED_OVERFLOW_PANEL,
     "tabstrip": CustomizableUI.AREA_TABSTRIP,
@@ -138,17 +136,17 @@ this.browserAction = class extends Exten
       tooltiptext: this.defaults.title || "",
       defaultArea: this.defaults.area,
 
       // Don't attempt to load properties from the built-in widget string
       // bundle.
       localized: false,
 
       onBeforeCreated: document => {
-        let view = document.createElementNS(XUL_NS, "panelview");
+        let view = document.createXULElement("panelview");
         view.id = this.viewId;
         view.setAttribute("flex", "1");
         view.setAttribute("extension", true);
 
         document.getElementById("appMenu-viewCache").appendChild(view);
 
         if (this.extension.hasPermission("menus") ||
             this.extension.hasPermission("contextMenus")) {
--- a/browser/components/extensions/parent/ext-commands.js
+++ b/browser/components/extensions/parent/ext-commands.js
@@ -7,18 +7,16 @@ ChromeUtils.defineModuleGetter(this, "Ex
 ChromeUtils.defineModuleGetter(this, "ExtensionSettingsStore",
                                "resource://gre/modules/ExtensionSettingsStore.jsm");
 
 var {
   chromeModifierKeyMap,
   ExtensionError,
 } = ExtensionUtils;
 
-var XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
-
 const EXECUTE_PAGE_ACTION = "_execute_page_action";
 const EXECUTE_BROWSER_ACTION = "_execute_browser_action";
 const EXECUTE_SIDEBAR_ACTION = "_execute_sidebar_action";
 
 function normalizeShortcut(shortcut) {
   return shortcut ? shortcut.replace(/\s+/g, "") : null;
 }
 
@@ -149,17 +147,17 @@ this.commands = class extends ExtensionA
 
   /**
    * Registers the commands to a document.
    * @param {ChromeWindow} window The XUL window to insert the Keyset.
    * @param {Map} commands The commands to be set.
    */
   registerKeysToDocument(window, commands) {
     let doc = window.document;
-    let keyset = doc.createElementNS(XUL_NS, "keyset");
+    let keyset = doc.createXULElement("keyset");
     keyset.id = `ext-keyset-id-${this.id}`;
     if (this.keysetsMap.has(window)) {
       this.keysetsMap.get(window).remove();
     }
     let sidebarKey;
     commands.forEach((command, name) => {
       if (command.shortcut) {
         let keyElement = this.buildKey(doc, name, command.shortcut);
@@ -227,17 +225,17 @@ this.commands = class extends ExtensionA
    * @param {Document} doc The XUL document.
    * @param {string} name The name of the shortcut.
    * @param {string} shortcut The shortcut provided in the manifest.
    *
    * @see https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XUL/key
    * @returns {Document} The newly created Key element.
    */
   buildKeyFromShortcut(doc, name, shortcut) {
-    let keyElement = doc.createElementNS(XUL_NS, "key");
+    let keyElement = doc.createXULElement("key");
 
     let parts = shortcut.split("+");
 
     // The key is always the last element.
     let chromeKey = parts.pop();
 
     // The modifiers are the remaining elements.
     keyElement.setAttribute("modifiers", this.getModifiersAttribute(parts));
--- a/browser/components/extensions/parent/ext-devtools-panels.js
+++ b/browser/components/extensions/parent/ext-devtools-panels.js
@@ -11,18 +11,16 @@ var {
   IconDetails,
   watchExtensionProxyContextLoad,
 } = ExtensionParent;
 
 var {
   promiseEvent,
 } = ExtensionUtils;
 
-var XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
-
 /**
  * Represents an addon devtools panel in the main process.
  *
  * @param {ExtensionChildProxyContext} context
  *        A devtools extension proxy context running in a main process.
  * @param {object} options
  * @param {string} options.id
  *        The id of the addon devtools panel.
@@ -213,23 +211,23 @@ class ParentDevToolsPanel {
     const {document} = window;
 
     // TODO Bug 1442601: Refactor ext-devtools-panels.js to reuse the helpers
     // functions defined in webext-panels.xul (e.g. create the browser element
     // using an helper function defined in webext-panels.js and shared with the
     // extension sidebar pages).
     let stack = document.getElementById("webext-panels-stack");
     if (!stack) {
-      stack = document.createElementNS(XUL_NS, "stack");
+      stack = document.createXULElement("stack");
       stack.setAttribute("flex", "1");
       stack.setAttribute("id", "webext-panels-stack");
       document.documentElement.appendChild(stack);
     }
 
-    const browser = document.createElementNS(XUL_NS, "browser");
+    const browser = document.createXULElement("browser");
     browser.setAttribute("id", "webext-panels-browser");
     browser.setAttribute("type", "content");
     browser.setAttribute("disableglobalhistory", "true");
     browser.setAttribute("flex", "1");
     browser.setAttribute("class", "webextension-devtoolsPanel-browser");
     browser.setAttribute("webextension-view-type", "devtools_panel");
     // TODO Bug 1442604: Add additional tests for the select and autocompletion
     // popups used in an extension devtools panels (in oop and non-oop mode).
--- a/browser/components/extensions/parent/ext-sidebarAction.js
+++ b/browser/components/extensions/parent/ext-sidebarAction.js
@@ -7,18 +7,16 @@ ChromeUtils.import("resource://gre/modul
 var {
   ExtensionError,
 } = ExtensionUtils;
 
 var {
   IconDetails,
 } = ExtensionParent;
 
-var XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
-
 // WeakMap[Extension -> SidebarAction]
 let sidebarActionMap = new WeakMap();
 
 const sidebarURL = "chrome://browser/content/webext-panels.xul";
 
 /**
  * Responsible for the sidebar_action section of the manifest as well
  * as the associated sidebar browser.
@@ -144,17 +142,17 @@ this.sidebarAction = class extends Exten
     }
   }
 
   createMenuItem(window, details) {
     let {document, SidebarUI} = window;
 
     // Use of the broadcaster allows browser-sidebar.js to properly manage the
     // checkmarks in the menus.
-    let broadcaster = document.createElementNS(XUL_NS, "broadcaster");
+    let broadcaster = document.createXULElement("broadcaster");
     broadcaster.setAttribute("id", this.id);
     broadcaster.setAttribute("autoCheck", "false");
     broadcaster.setAttribute("type", "checkbox");
     broadcaster.setAttribute("group", "sidebar");
     broadcaster.setAttribute("label", details.title);
     broadcaster.setAttribute("sidebarurl", sidebarURL);
     broadcaster.setAttribute("panel", details.panel);
     if (this.browserStyle) {
@@ -167,24 +165,24 @@ this.sidebarAction = class extends Exten
     // oncommand gets attached to menuitem, so we use the observes attribute to
     // get the command id we pass to SidebarUI.
     broadcaster.setAttribute("oncommand", "SidebarUI.toggle(this.getAttribute('observes'))");
 
     let header = document.getElementById("sidebar-switcher-target");
     header.addEventListener("SidebarShown", this.updateHeader);
 
     // Insert a menuitem for View->Show Sidebars.
-    let menuitem = document.createElementNS(XUL_NS, "menuitem");
+    let menuitem = document.createXULElement("menuitem");
     menuitem.setAttribute("id", this.menuId);
     menuitem.setAttribute("observes", this.id);
     menuitem.setAttribute("class", "menuitem-iconic webextension-menuitem");
     this.setMenuIcon(menuitem, details);
 
     // Insert a toolbarbutton for the sidebar dropdown selector.
-    let toolbarbutton = document.createElementNS(XUL_NS, "toolbarbutton");
+    let toolbarbutton = document.createXULElement("toolbarbutton");
     toolbarbutton.setAttribute("id", this.buttonId);
     toolbarbutton.setAttribute("observes", this.id);
     toolbarbutton.setAttribute("class", "subviewbutton subviewbutton-iconic webextension-menuitem");
     this.setMenuIcon(toolbarbutton, details);
 
     document.getElementById("mainBroadcasterSet").appendChild(broadcaster);
     document.getElementById("viewSidebarMenu").appendChild(menuitem);
     let separator = document.getElementById("sidebar-extensions-separator");
--- a/browser/components/newtab/lib/AboutPreferences.jsm
+++ b/browser/components/newtab/lib/AboutPreferences.jsm
@@ -122,17 +122,17 @@ this.AboutPreferences = class AboutPrefe
 
   /**
    * Render preferences to an about:preferences content window with the provided
    * strings and preferences structure.
    */
   renderPreferences({document, Preferences, gHomePane}, strings, prefStructure) {
     // Helper to create a new element and append it
     const createAppend = (tag, parent) => parent.appendChild(
-      document.createElementNS(XUL_NS, tag));
+      document.createXULElement(tag));
 
     // Helper to get strings and format with values if necessary
     const formatString = id => {
       if (typeof id !== "object") {
         return strings[id] || id;
       }
       let string = strings[id.id] || JSON.stringify(id);
       if (id.values) {
--- a/browser/components/search/content/search.xml
+++ b/browser/components/search/content/search.xml
@@ -525,49 +525,47 @@
         } catch (ex) { }
       ]]></destructor>
 
       // Add items to context menu and attach controller to handle them the
       // first time the context menu is opened.
       <method name="initContextMenu">
         <parameter name="aMenu"/>
         <body><![CDATA[
-          const kXULNS =
-            "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
           let stringBundle = document.getBindingParent(this)._stringBundle;
 
           let pasteAndSearch, suggestMenuItem;
           let element, label, akey;
 
-          element = document.createElementNS(kXULNS, "menuseparator");
+          element = document.createXULElement("menuseparator");
           aMenu.appendChild(element);
 
           let insertLocation = aMenu.firstElementChild;
           while (insertLocation.nextElementSibling &&
                  insertLocation.getAttribute("cmd") != "cmd_paste")
             insertLocation = insertLocation.nextElementSibling;
           if (insertLocation) {
-            element = document.createElementNS(kXULNS, "menuitem");
+            element = document.createXULElement("menuitem");
             label = stringBundle.getString("cmd_pasteAndSearch");
             element.setAttribute("label", label);
             element.setAttribute("anonid", "paste-and-search");
             element.setAttribute("oncommand", "BrowserSearch.pasteAndSearch(event)");
             aMenu.insertBefore(element, insertLocation.nextElementSibling);
             pasteAndSearch = element;
           }
 
-          element = document.createElementNS(kXULNS, "menuitem");
+          element = document.createXULElement("menuitem");
           label = stringBundle.getString("cmd_clearHistory");
           akey = stringBundle.getString("cmd_clearHistory_accesskey");
           element.setAttribute("label", label);
           element.setAttribute("accesskey", akey);
           element.setAttribute("cmd", "cmd_clearhistory");
           aMenu.appendChild(element);
 
-          element = document.createElementNS(kXULNS, "menuitem");
+          element = document.createXULElement("menuitem");
           label = stringBundle.getString("cmd_showSuggestions");
           akey = stringBundle.getString("cmd_showSuggestions_accesskey");
           element.setAttribute("anonid", "toggle-suggest-item");
           element.setAttribute("label", label);
           element.setAttribute("accesskey", akey);
           element.setAttribute("cmd", "cmd_togglesuggest");
           element.setAttribute("type", "checkbox");
           element.setAttribute("autocheck", "false");
@@ -1503,23 +1501,20 @@
           let height = rowCount * 33; // 32px per row, 1px border.
           this.buttons.setAttribute("height", height + "px");
 
           // Ensure we can refer to the settings buttons by ID:
           let origin = this.telemetryOrigin;
           this.settingsButton.id = origin + "-anon-search-settings";
           this.settingsButtonCompact.id = origin + "-anon-search-settings-compact";
 
-          const kXULNS =
-            "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
-
           let dummyItems = enginesPerRow - (oneOffCount % enginesPerRow || enginesPerRow);
           for (let i = 0; i < engines.length; ++i) {
             let engine = engines[i];
-            let button = document.createElementNS(kXULNS, "button");
+            let button = document.createXULElement("button");
             button.id = this._buttonIDForEngine(engine);
             let uri = "chrome://browser/skin/search-engine-placeholder.png";
             if (engine.iconURI) {
               uri = engine.iconURI.spec;
             }
             button.setAttribute("image", uri);
             button.setAttribute("class", "searchbar-engine-one-off-item");
             button.setAttribute("tooltiptext", engine.name);
@@ -1535,17 +1530,17 @@
             if (i >= oneOffCount + dummyItems - enginesPerRow)
               button.classList.add("last-row");
 
             this.buttons.insertBefore(button, this.settingsButtonCompact);
           }
 
           let hasDummyItems = !!dummyItems;
           while (dummyItems) {
-            let button = document.createElementNS(kXULNS, "button");
+            let button = document.createXULElement("button");
             button.setAttribute("class", "searchbar-engine-one-off-item dummy last-row");
             button.setAttribute("width", buttonWidth);
 
             if (!--dummyItems)
               button.classList.add("last-of-row");
 
             this.buttons.insertBefore(button, this.settingsButtonCompact);
           }
@@ -1584,25 +1579,22 @@
         // The popup isn't designed to handle too many (by scrolling for
         // example), so a page could break the popup by offering too many.
         // Instead, add a single menu button with a submenu of all the engines.
 
         if (!gBrowser.selectedBrowser.engines) {
           return;
         }
 
-        const kXULNS =
-          "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
-
         let engines = gBrowser.selectedBrowser.engines;
         let tooManyEngines = engines.length > this._addEngineMenuThreshold;
 
         if (tooManyEngines) {
           // Make the top-level menu button.
-          let button = document.createElementNS(kXULNS, "toolbarbutton");
+          let button = document.createXULElement("toolbarbutton");
           list.appendChild(button);
           button.classList.add("addengine-item", "badged-button");
           button.setAttribute("anonid", "addengine-menu-button");
           button.setAttribute("type", "menu");
           button.setAttribute("label",
             this.bundle.GetStringFromName("cmd_addFoundEngineMenu"));
           button.setAttribute("crop", "end");
           button.setAttribute("pack", "start");
@@ -1611,17 +1603,17 @@
           // offered engines may have differing images, so there's no perfect
           // choice here.
           let engine = engines[0];
           if (engine.icon) {
             button.setAttribute("image", engine.icon);
           }
 
           // Now make the button's child menupopup.
-          list = document.createElementNS(kXULNS, "menupopup");
+          list = document.createXULElement("menupopup");
           button.appendChild(list);
           list.setAttribute("anonid", "addengine-menu");
           list.setAttribute("position", "topright topleft");
 
           // Events from child menupopups bubble up to the autocomplete binding,
           // which breaks it, so prevent these events from propagating.
           let suppressEventTypes = [
             "popupshowing",
@@ -1638,17 +1630,17 @@
 
         // Finally, add the engines to the list.  If there aren't too many
         // engines, the list is the add-engines vbox.  Otherwise it's the
         // menupopup created earlier.  In the latter case, create menuitem
         // elements instead of buttons, because buttons don't get keyboard
         // handling for free inside menupopups.
         let eltType = tooManyEngines ? "menuitem" : "toolbarbutton";
         for (let engine of engines) {
-          let button = document.createElementNS(kXULNS, eltType);
+          let button = document.createXULElement(eltType);
           button.classList.add("addengine-item");
           if (!tooManyEngines) {
             button.classList.add("badged-button");
           }
           button.id = this.telemetryOrigin + "-add-engine-" +
                       this._fixUpEngineNameForID(engine.title);
           let label = this.bundle.formatStringFromName("cmd_addFoundEngine",
                                                        [engine.title], 1);
--- a/browser/components/sessionstore/RecentlyClosedTabsAndWindowsMenuUtils.jsm
+++ b/browser/components/sessionstore/RecentlyClosedTabsAndWindowsMenuUtils.jsm
@@ -1,16 +1,14 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 var EXPORTED_SYMBOLS = ["RecentlyClosedTabsAndWindowsMenuUtils"];
 
-const kNSXUL = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
-
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 
 ChromeUtils.defineModuleGetter(this, "PluralForm",
                                "resource://gre/modules/PluralForm.jsm");
 ChromeUtils.defineModuleGetter(this, "SessionStore",
                                "resource:///modules/sessionstore/SessionStore.jsm");
 
 var navigatorBundle = Services.strings.createBundle("chrome://browser/locale/browser.properties");
@@ -133,17 +131,17 @@ function setImage(aItem, aElement) {
  *        a document that can be used to create the entry
  * @param aMenuLabel
  *        the label the created entry will have
  * @param aFragment
  *        the fragment the created entry will be in
  */
 function createEntry(aTagName, aIsWindowsFragment, aIndex, aClosedTab,
                      aDocument, aMenuLabel, aFragment) {
-  let element = aDocument.createElementNS(kNSXUL, aTagName);
+  let element = aDocument.createXULElement(aTagName);
 
   element.setAttribute("label", aMenuLabel);
   if (aClosedTab.image) {
     setImage(aClosedTab, element);
   }
   if (!aIsWindowsFragment) {
     element.setAttribute("value", aIndex);
   }
@@ -191,21 +189,21 @@ function createEntry(aTagName, aIsWindow
  * @param aEntryCount
  *        the number of elements to be restored by this entry
  * @param aTagName
  *        the tag name that will be used when creating the UI entry
  */
 function createRestoreAllEntry(aDocument, aFragment, aPrefixRestoreAll,
                                 aIsWindowsFragment, aRestoreAllLabel,
                                 aEntryCount, aTagName) {
-  let restoreAllElements = aDocument.createElementNS(kNSXUL, aTagName);
+  let restoreAllElements = aDocument.createXULElement(aTagName);
   restoreAllElements.classList.add("restoreallitem");
   restoreAllElements.setAttribute("label", navigatorBundle.GetStringFromName(aRestoreAllLabel));
   restoreAllElements.setAttribute("oncommand",
                                   "for (var i = 0; i < " + aEntryCount + "; i++) undoClose" +
                                     (aIsWindowsFragment ? "Window" : "Tab") + "();");
   if (aPrefixRestoreAll) {
     aFragment.insertBefore(restoreAllElements, aFragment.firstChild);
   } else {
-    aFragment.appendChild(aDocument.createElementNS(kNSXUL, "menuseparator"));
+    aFragment.appendChild(aDocument.createXULElement("menuseparator"));
     aFragment.appendChild(restoreAllElements);
   }
 }
--- a/browser/extensions/formautofill/FormAutofillPreferences.jsm
+++ b/browser/extensions/formautofill/FormAutofillPreferences.jsm
@@ -10,17 +10,16 @@
 
 var EXPORTED_SYMBOLS = ["FormAutofillPreferences"];
 
 // Add addresses enabled flag in telemetry environment for recording the number of
 // users who disable/enable the address autofill feature.
 const BUNDLE_URI = "chrome://formautofill/locale/formautofill.properties";
 const MANAGE_ADDRESSES_URL = "chrome://formautofill/content/manageAddresses.xhtml";
 const MANAGE_CREDITCARDS_URL = "chrome://formautofill/content/manageCreditCards.xhtml";
-const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
 
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 ChromeUtils.import("resource://formautofill/FormAutofill.jsm");
 ChromeUtils.import("resource://formautofill/FormAutofillUtils.jsm");
 
 const {
   ENABLED_AUTOFILL_ADDRESSES_PREF,
   ENABLED_AUTOFILL_CREDITCARDS_PREF,
@@ -63,27 +62,27 @@ FormAutofillPreferences.prototype = {
 
   /**
    * Create Form Autofill preference group
    *
    * @param  {XULDocument} document
    */
   createPreferenceGroup(document) {
     let learnMoreURL = Services.urlFormatter.formatURLPref("app.support.baseURL") + "autofill-card-address";
-    let formAutofillGroup = document.createElementNS(XUL_NS, "vbox");
-    let addressAutofill = document.createElementNS(XUL_NS, "hbox");
-    let addressAutofillCheckboxGroup = document.createElementNS(XUL_NS, "hbox");
-    let addressAutofillCheckbox = document.createElementNS(XUL_NS, "checkbox");
-    let addressAutofillCheckboxLabel = document.createElementNS(XUL_NS, "label");
-    let addressAutofillCheckboxLabelSpacer = document.createElementNS(XUL_NS, "spacer");
-    let addressAutofillLearnMore = document.createElementNS(XUL_NS, "label");
-    let savedAddressesBtn = document.createElementNS(XUL_NS, "button");
+    let formAutofillGroup = document.createXULElement("vbox");
+    let addressAutofill = document.createXULElement("hbox");
+    let addressAutofillCheckboxGroup = document.createXULElement("hbox");
+    let addressAutofillCheckbox = document.createXULElement("checkbox");
+    let addressAutofillCheckboxLabel = document.createXULElement("label");
+    let addressAutofillCheckboxLabelSpacer = document.createXULElement("spacer");
+    let addressAutofillLearnMore = document.createXULElement("label");
+    let savedAddressesBtn = document.createXULElement("button");
     // Wrappers are used to properly compute the search tooltip positions
-    let savedAddressesBtnWrapper = document.createElementNS(XUL_NS, "hbox");
-    let savedCreditCardsBtnWrapper = document.createElementNS(XUL_NS, "hbox");
+    let savedAddressesBtnWrapper = document.createXULElement("hbox");
+    let savedCreditCardsBtnWrapper = document.createXULElement("hbox");
 
     savedAddressesBtn.className = "accessory-button";
     addressAutofillCheckboxLabelSpacer.className = "tail-with-learn-more";
     addressAutofillLearnMore.className = "learnMore text-link";
 
     formAutofillGroup.id = "formAutofillGroup";
     addressAutofill.id = "addressAutofill";
     addressAutofillLearnMore.id = "addressAutofillLearnMore";
@@ -124,23 +123,23 @@ FormAutofillPreferences.prototype = {
     this.refs = {
       formAutofillGroup,
       addressAutofillCheckbox,
       addressAutofillCheckboxLabel,
       savedAddressesBtn,
     };
 
     if (FormAutofill.isAutofillCreditCardsAvailable) {
-      let creditCardAutofill = document.createElementNS(XUL_NS, "hbox");
-      let creditCardAutofillCheckboxGroup = document.createElementNS(XUL_NS, "hbox");
-      let creditCardAutofillCheckbox = document.createElementNS(XUL_NS, "checkbox");
-      let creditCardAutofillCheckboxLabel = document.createElementNS(XUL_NS, "label");
-      let creditCardAutofillCheckboxLabelSpacer = document.createElementNS(XUL_NS, "spacer");
-      let creditCardAutofillLearnMore = document.createElementNS(XUL_NS, "label");
-      let savedCreditCardsBtn = document.createElementNS(XUL_NS, "button");
+      let creditCardAutofill = document.createXULElement("hbox");
+      let creditCardAutofillCheckboxGroup = document.createXULElement("hbox");
+      let creditCardAutofillCheckbox = document.createXULElement("checkbox");
+      let creditCardAutofillCheckboxLabel = document.createXULElement("label");
+      let creditCardAutofillCheckboxLabelSpacer = document.createXULElement("spacer");
+      let creditCardAutofillLearnMore = document.createXULElement("label");
+      let savedCreditCardsBtn = document.createXULElement("button");
       savedCreditCardsBtn.className = "accessory-button";
       creditCardAutofillCheckboxLabelSpacer.className = "tail-with-learn-more";
       creditCardAutofillLearnMore.className = "learnMore text-link";
 
       creditCardAutofill.id = "creditCardAutofill";
       creditCardAutofillLearnMore.id = "creditCardAutofillLearnMore";
 
       creditCardAutofill.setAttribute("data-subcategory", "credit-card-autofill");
--- a/browser/modules/TabsList.jsm
+++ b/browser/modules/TabsList.jsm
@@ -3,17 +3,16 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 ChromeUtils.defineModuleGetter(this, "PanelMultiView",
                                "resource:///modules/PanelMultiView.jsm");
 
 var EXPORTED_SYMBOLS = ["TabsPanel"];
-const NSXUL = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
 
 function setAttributes(element, attrs) {
   for (let [name, value] of Object.entries(attrs)) {
     if (value) {
       element.setAttribute(name, value);
     } else {
       element.removeAttribute(name);
     }
@@ -211,34 +210,34 @@ class TabsPanel extends TabsListBase {
 
   _cleanupListeners() {
     super._cleanupListeners();
     this.panelMultiView.removeEventListener(TABS_PANEL_EVENTS.hide, this);
   }
 
   _createRow(tab) {
     let {doc} = this;
-    let row = doc.createElementNS(NSXUL, "toolbaritem");
+    let row = doc.createXULElement("toolbaritem");
     row.setAttribute("class", "all-tabs-item");
     if (this.className) {
       row.classList.add(this.className);
     }
     row.tab = tab;
     row.addEventListener("command", this);
     this.tabToElement.set(tab, row);
 
-    let button = doc.createElementNS(NSXUL, "toolbarbutton");
+    let button = doc.createXULElement("toolbarbutton");
     button.setAttribute("class", "all-tabs-button subviewbutton subviewbutton-iconic");
     button.setAttribute("flex", "1");
     button.setAttribute("crop", "right");
     button.tab = tab;
 
     row.appendChild(button);
 
-    let secondaryButton = doc.createElementNS(NSXUL, "toolbarbutton");
+    let secondaryButton = doc.createXULElement("toolbarbutton");
     secondaryButton.setAttribute(
       "class", "all-tabs-secondary-button subviewbutton subviewbutton-iconic");
     secondaryButton.setAttribute("closemenu", "none");
     secondaryButton.setAttribute("toggle-mute", "true");
     secondaryButton.tab = tab;
     row.appendChild(secondaryButton);
 
     this._setRowAttributes(row, tab);
--- a/browser/themes/linux/places/places.css
+++ b/browser/themes/linux/places/places.css
@@ -41,20 +41,16 @@
 
 .sidebar-placesTree {
   margin: 0;
   color: inherit;
   -moz-appearance: none;
   background: transparent;
 }
 
-.sidebar-placesTreechildren::-moz-tree-row {
-  min-height: 24px;
-}
-
 :root[uidensity=touch] #search-box,
 :root[uidensity=touch] .sidebar-placesTreechildren::-moz-tree-row {
   min-height: 32px;
 }
 
 .sidebar-placesTreechildren::-moz-tree-cell(leaf) ,
 .sidebar-placesTreechildren::-moz-tree-image(leaf) {
   cursor: pointer;
@@ -67,13 +63,8 @@
 
 .sidebar-placesTreechildren::-moz-tree-cell(separator) {
   cursor: default;
 }
 
 /* Trees */
 
 %include ../../shared/places/tree-icons.inc.css
-
-treechildren:-moz-tree-image(container,selected),
-treechildren:-moz-tree-image(selected,focus) {
-  fill: HighlightText;
-}
--- a/browser/themes/linux/syncedtabs/sidebar.css
+++ b/browser/themes/linux/syncedtabs/sidebar.css
@@ -45,31 +45,8 @@ html {
 
 .search-box > .textbox-input-box:-moz-locale-dir(rtl) {
   background-position: right center;
 }
 
 .textbox-search-clear:not([disabled]) {
   cursor: pointer;
 }
-
-.item-twisty-container {
-  background-size: contain;
-  background-repeat: no-repeat;
-  background-position: center;
-  min-width: 9px; /* The image's width is 9 pixels */
-  height: 9px;
-  margin: auto;
-}
-
-.item.client .item-twisty-container {
-  background-image: url("chrome://global/skin/tree/twisty-expanded.svg");
-  -moz-context-properties: fill;
-  fill: currentColor;
-}
-
-.item.client.closed .item-twisty-container {
-  background-image: url("chrome://global/skin/tree/twisty-collapsed.svg");
-}
-
-.item.client.closed .item-twisty-container:dir(rtl) {
-  background-image: url("chrome://global/skin/tree/twisty-collapsed-rtl.svg");
-}
--- a/browser/themes/osx/places/organizer.css
+++ b/browser/themes/osx/places/organizer.css
@@ -1,23 +1,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/. */
 
 /* Places Organizer Sidebars */
 
 #placesList > treechildren::-moz-tree-row {
-  background-color: transparent;
-  border-color: transparent;
   padding-bottom: 1px;
-  height: 24px;
 }
 
 #placesList > treechildren::-moz-tree-cell-text {
-  font-size: 12px;
   margin-inline-end: 6px;
 }
 
 #placesList > treechildren::-moz-tree-row(selected) {
   -moz-appearance: -moz-mac-source-list-selection;
   -moz-font-smoothing-background-color: -moz-mac-source-list-selection;
 }
 
@@ -42,37 +38,16 @@
 
 #placesList > treechildren::-moz-tree-twisty(selected),
 #placesList > treechildren::-moz-tree-cell-text(selected) {
   color: #fff;
   fill-opacity: 1;
   font-weight: bold;
 }
 
-#placesList > treechildren::-moz-tree-twisty {
-  -moz-appearance: none;
-  padding: 0 2px;
-  list-style-image: url("chrome://global/skin/tree/arrow-collapsed.svg");
-  -moz-context-properties: fill, fill-opacity;
-  fill: currentColor;
-  fill-opacity: 0.6;
-}
-
-#placesList > treechildren::-moz-tree-twisty(selected) {
-  fill: currentColor;
-}
-
-#placesList > treechildren::-moz-tree-twisty(open) {
-  list-style-image: url("chrome://global/skin/tree/arrow-expanded.svg");
-}
-
-#placesList > treechildren:-moz-locale-dir(rtl)::-moz-tree-twisty(closed) {
-  list-style-image: url("chrome://global/skin/tree/arrow-collapsed-rtl.svg");
-}
-
 @media (-moz-mac-yosemite-theme) {
   #placesList > treechildren::-moz-tree-twisty(selected),
   #placesList > treechildren::-moz-tree-cell-text(selected) {
     color: -moz-dialogtext;
     fill-opacity: 0.6;
     font-weight: 500;
   }
 
--- a/browser/themes/osx/places/places.css
+++ b/browser/themes/osx/places/places.css
@@ -49,63 +49,39 @@
 
 .sidebar-placesTreechildren::-moz-tree-cell-text {
   margin-inline-end: 6px;
 }
 
 .sidebar-placesTreechildren::-moz-tree-twisty(selected),
 .sidebar-placesTreechildren::-moz-tree-cell-text(selected) {
   color: #fff;
-  fill-opacity: 1;
   font-weight: bold;
 }
 
 #sidebar-search-label {
   display: none;
 }
 
 #sidebar-search-container {
   /* Native searchbar styling already adds 4px margin on Mac, so
    * adding 4px padding results in 8px of total whitespace. */
   padding: 4px;
 }
 
-.sidebar-placesTreechildren::-moz-tree-twisty {
-  -moz-appearance: none;
-  padding: 0 2px;
-  list-style-image: url("chrome://global/skin/tree/arrow-collapsed.svg");
-  -moz-context-properties: fill, fill-opacity;
-  fill: currentColor;
-  fill-opacity: 0.6;
-}
-
-.sidebar-placesTreechildren::-moz-tree-twisty(selected) {
-  fill: currentColor;
-}
-
-.sidebar-placesTreechildren::-moz-tree-twisty(open) {
-  list-style-image: url("chrome://global/skin/tree/arrow-expanded.svg");
-}
-
-.sidebar-placesTreechildren:-moz-locale-dir(rtl)::-moz-tree-twisty(closed) {
-  list-style-image: url("chrome://global/skin/tree/arrow-collapsed-rtl.svg");
-}
-
 @media (-moz-mac-yosemite-theme) {
   .sidebar-placesTreechildren::-moz-tree-twisty(selected),
   .sidebar-placesTreechildren::-moz-tree-cell-text(selected) {
     color: -moz-dialogtext;
-    fill-opacity: 0.6;
     font-weight: 500;
   }
 
   .sidebar-placesTreechildren::-moz-tree-twisty(selected, focus),
   .sidebar-placesTreechildren::-moz-tree-cell-text(selected, focus) {
     color: #fff;
-    fill-opacity: 1;
   }
 }
 
 #viewButton {
   -moz-appearance: none;
   border-radius: 4px;
   padding: 2px 4px;
   margin: 4px 0;
@@ -131,12 +107,8 @@
   list-style-image: url("chrome://global/skin/icons/arrow-dropdown-12.svg");
   width: 12px;
   height: 12px;
 }
 
 /* Trees */
 
 %include ../../shared/places/tree-icons.inc.css
-
-treechildren:-moz-tree-image(selected,focus) {
-  fill: HighlightText;
-}
--- a/browser/themes/osx/syncedtabs/sidebar.css
+++ b/browser/themes/osx/syncedtabs/sidebar.css
@@ -32,44 +32,16 @@
   -moz-font-smoothing-background-color: -moz-mac-source-list-selection;
 }
 
 .item.selected:focus > .item-title-container {
   -moz-appearance: -moz-mac-active-source-list-selection;
   -moz-font-smoothing-background-color: -moz-mac-active-source-list-selection;
 }
 
-.item.client .item-twisty-container {
-  min-width: 16px;
-  height: 16px;
-  background-image: url("chrome://global/skin/tree/arrow-expanded.svg");
-  -moz-context-properties: fill, fill-opacity;
-  fill: currentColor;
-  fill-opacity: 1;
-}
-
-.item.client.closed .item-twisty-container {
-  background-image: url("chrome://global/skin/tree/arrow-collapsed.svg");
-}
-
-.item.client.closed .item-twisty-container:dir(rtl) {
-  background-image: url("chrome://global/skin/tree/arrow-collapsed-rtl.svg");
-}
-
-.item.client.selected:focus .item-twisty-container {
-  fill-opacity: 1;
-}
-
-@media (-moz-mac-yosemite-theme: 0) {
-  .item.client.selected .item-twisty-container {
-    fill-opacity: 1;
-  }
-}
-
-
 @media (-moz-mac-yosemite-theme) {
   .item.selected > .item-title-container {
     color: -moz-dialogtext;
     font-weight: 500;
   }
 
   .item.selected:focus > .item-title-container {
     color: #fff;
--- a/browser/themes/shared/places/tree-icons.inc.css
+++ b/browser/themes/shared/places/tree-icons.inc.css
@@ -1,15 +1,14 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 treechildren:-moz-tree-image {
   -moz-context-properties: fill, fill-opacity;
-  fill: -moz-FieldText;
   fill-opacity: 0.7;
 }
 
 treechildren::-moz-tree-image(title) {
   list-style-image: url("chrome://mozapps/skin/places/defaultFavicon.svg");
   padding-inline-end: 2px;
   margin: 0 2px;
   width: 16px;
--- a/browser/themes/shared/syncedtabs/sidebar.inc.css
+++ b/browser/themes/shared/syncedtabs/sidebar.inc.css
@@ -75,31 +75,44 @@ body {
   color: -moz-cellhighlighttext;
 }
 
 .item.selected:focus > .item-title-container {
   background-color: Highlight;
   color: HighlightText;
 }
 
+.item.client .item-twisty-container {
+  min-width: 8px;
+  height: 8px;
+  background-image: url("chrome://global/skin/icons/twisty-expanded.svg");
+  -moz-context-properties: fill, fill-opacity;
+  fill: currentColor;
+  fill-opacity: 1;
+}
+
+.item.client.closed .item-twisty-container {
+  background-image: url("chrome://global/skin/icons/twisty-collapsed.svg");
+}
+
+.item.client.closed .item-twisty-container:dir(rtl) {
+  background-image: url("chrome://global/skin/icons/twisty-collapsed-rtl.svg");
+}
+
 .client .item.tab > .item-title-container {
   padding-inline-start: 35px;
 }
 
 .item.tab > .item-title-container {
   padding-inline-start: 20px;
 }
 
 .item.client[clientType] > .item-title-container > .item-icon-container {
   -moz-context-properties: fill;
-  fill: #4d4d4d;
-}
-
-.item.client[clientType].selected:focus > .item-title-container > .item-icon-container {
-  fill: white;
+  fill: currentColor;
 }
 
 .item.client[clientType=phone] > .item-title-container > .item-icon-container {
   background-image: url("chrome://browser/skin/device-phone.svg");
 }
 
 .item.client[clientType=tablet] > .item-title-container > .item-icon-container {
   background-image: url("chrome://browser/skin/device-tablet.svg");
@@ -128,16 +141,17 @@ body {
   background-position: center;
 }
 
 .item-title-container {
   display: flex;
   flex-flow: row;
   overflow: hidden;
   flex-grow: 1;
+  align-items: center;
   padding: 4px;
 }
 
 .item-title {
   flex-grow: 1;
   overflow: hidden;
   text-overflow: ellipsis;
   margin: 0px;
--- a/browser/themes/windows/syncedtabs/sidebar.css
+++ b/browser/themes/windows/syncedtabs/sidebar.css
@@ -70,40 +70,8 @@
   max-width: 16px;
   min-height: 16px;
   max-height: 16px;
   margin-right: 5px;
   background-size: 16px 16px;
   background-repeat: no-repeat;
   background-position: center;
 }
-
-.item-twisty-container {
-  background-size: contain;
-  background-repeat: no-repeat;
-  background-position: center;
-  min-width: 9px; /* The image's width is 9 pixels */
-  height: 9px;
-  margin: auto;
-}
-
-.item.client .item-twisty-container {
-  background-image: url("chrome://global/skin/tree/twisty-expanded.svg");
-  -moz-context-properties: fill;
-  fill: #636363;
-}
-
-.item.client.closed .item-twisty-container {
-  background-image: url("chrome://global/skin/tree/twisty-collapsed.svg");
-  fill: #b6b6b6;
-}
-
-.item.client.closed .item-twisty-container:dir(rtl) {
-  background-image: url("chrome://global/skin/tree/twisty-collapsed-rtl.svg");
-}
-
-.item.client .item-twisty-container:hover {
-  fill: #4ed0f9;
-}
-
-.item.client.selected .item-twisty-container:not(:hover) {
-  fill: currentColor;
-}
--- a/devtools/client/framework/menu.js
+++ b/devtools/client/framework/menu.js
@@ -1,17 +1,16 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set ft=javascript 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/. */
 
 "use strict";
 
-const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
 const EventEmitter = require("devtools/shared/event-emitter");
 const { getCurrentZoom } = require("devtools/shared/layout/utils");
 
 /**
  * A partial implementation of the Menu API provided by electron:
  * https://github.com/electron/electron/blob/master/docs/api/menu.md.
  *
  * Extra features:
@@ -79,29 +78,29 @@ Menu.prototype.popupWithZoom = function(
  * @param Toolbox toolbox (non standard)
  *        Needed so we in which window to inject XUL
  */
 Menu.prototype.popup = function(screenX, screenY, toolbox) {
   const doc = toolbox.doc;
 
   let popupset = doc.querySelector("popupset");
   if (!popupset) {
-    popupset = doc.createElementNS(XUL_NS, "popupset");
+    popupset = doc.createXULElement("popupset");
     doc.documentElement.appendChild(popupset);
   }
   // See bug 1285229, on Windows, opening the same popup multiple times in a
   // row ends up duplicating the popup. The newly inserted popup doesn't
   // dismiss the old one. So remove any previously displayed popup before
   // opening a new one.
   let popup = popupset.querySelector("menupopup[menu-api=\"true\"]");
   if (popup) {
     popup.hidePopup();
   }
 
-  popup = doc.createElementNS(XUL_NS, "menupopup");
+  popup = doc.createXULElement("menupopup");
   popup.setAttribute("menu-api", "true");
   popup.setAttribute("consumeoutsideclicks", "true");
 
   if (this.id) {
     popup.id = this.id;
   }
   this._createMenuItems(popup);
 
@@ -126,28 +125,28 @@ Menu.prototype.popup = function(screenX,
 Menu.prototype._createMenuItems = function(parent) {
   const doc = parent.ownerDocument;
   this.menuitems.forEach(item => {
     if (!item.visible) {
       return;
     }
 
     if (item.submenu) {
-      const menupopup = doc.createElementNS(XUL_NS, "menupopup");
+      const menupopup = doc.createXULElement("menupopup");
       item.submenu._createMenuItems(menupopup);
 
-      const menu = doc.createElementNS(XUL_NS, "menu");
+      const menu = doc.createXULElement("menu");
       menu.appendChild(menupopup);
       applyItemAttributesToNode(item, menu);
       parent.appendChild(menu);
     } else if (item.type === "separator") {
-      const menusep = doc.createElementNS(XUL_NS, "menuseparator");
+      const menusep = doc.createXULElement("menuseparator");
       parent.appendChild(menusep);
     } else {
-      const menuitem = doc.createElementNS(XUL_NS, "menuitem");
+      const menuitem = doc.createXULElement("menuitem");
       applyItemAttributesToNode(item, menuitem);
 
       menuitem.addEventListener("command", () => {
         item.click();
       });
       menuitem.addEventListener("DOMMenuItemActive", () => {
         item.hover();
       });
--- a/devtools/client/framework/sidebar.js
+++ b/devtools/client/framework/sidebar.js
@@ -6,18 +6,16 @@
 
 var Services = require("Services");
 var EventEmitter = require("devtools/shared/event-emitter");
 var Telemetry = require("devtools/client/shared/telemetry");
 
 const {LocalizationHelper} = require("devtools/shared/l10n");
 const L10N = new LocalizationHelper("devtools/client/locales/toolbox.properties");
 
-const XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
-
 /**
  * ToolSidebar provides methods to register tabs in the sidebar.
  * It's assumed that the sidebar contains a xul:tabbox.
  * Typically, you'll want the tabbox parameter to be a XUL tabbox like this:
  *
  * <tabbox id="inspector-sidebar" handleCtrlTab="false" class="devtools-sidebar-tabs">
  *   <tabs/>
  *   <tabpanels flex="1"/>
@@ -113,36 +111,36 @@ ToolSidebar.prototype = {
   addAllTabsMenu: function() {
     if (this._allTabsBtn) {
       return;
     }
 
     const tabs = this._tabbox.tabs;
 
     // Create a container and insert it first in the tabbox
-    const allTabsContainer = this._panelDoc.createElementNS(XULNS, "stack");
+    const allTabsContainer = this._panelDoc.createXULElement("stack");
     this._tabbox.insertBefore(allTabsContainer, tabs);
 
     // Move the tabs inside and make them flex
     allTabsContainer.appendChild(tabs);
     tabs.setAttribute("flex", "1");
 
     // Create the dropdown menu next to the tabs
-    this._allTabsBtn = this._panelDoc.createElementNS(XULNS, "toolbarbutton");
+    this._allTabsBtn = this._panelDoc.createXULElement("toolbarbutton");
     this._allTabsBtn.setAttribute("class", "devtools-sidebar-alltabs");
     this._allTabsBtn.setAttribute("end", "0");
     this._allTabsBtn.setAttribute("top", "0");
     this._allTabsBtn.setAttribute("width", "15");
     this._allTabsBtn.setAttribute("type", "menu");
     this._allTabsBtn.setAttribute("tooltiptext",
       L10N.getStr("sidebar.showAllTabs.tooltip"));
     this._allTabsBtn.setAttribute("hidden", "true");
     allTabsContainer.appendChild(this._allTabsBtn);
 
-    const menuPopup = this._panelDoc.createElementNS(XULNS, "menupopup");
+    const menuPopup = this._panelDoc.createXULElement("menupopup");
     this._allTabsBtn.appendChild(menuPopup);
 
     // Listening to tabs overflow event to toggle the alltabs button
     tabs.addEventListener("overflow", this._onTabBoxOverflow);
     tabs.addEventListener("underflow", this._onTabBoxUnderflow);
 
     // Add menuitems to the alltabs menu if there are already tabs in the
     // sidebar
@@ -184,17 +182,17 @@ ToolSidebar.prototype = {
   /**
    * Add an item in the allTabs menu for a given tab.
    */
   _addItemToAllTabsMenu: function(id, tab, options) {
     if (!this._allTabsBtn) {
       return;
     }
 
-    const item = this._panelDoc.createElementNS(XULNS, "menuitem");
+    const item = this._panelDoc.createXULElement("menuitem");
     const idPrefix = "sidebar-alltabs-item-";
     item.setAttribute("id", idPrefix + id);
     item.setAttribute("label", tab.getAttribute("label"));
     item.setAttribute("type", "checkbox");
     if (options.selected) {
       item.setAttribute("checked", true);
     }
     // The auto-checking of menuitems in this menu doesn't work, so let's do
@@ -225,24 +223,24 @@ ToolSidebar.prototype = {
    * @param {string} id The unique id for this tab.
    * @param {string} url The URL of the document to load in this new tab.
    * @param {Object} options A set of options for this new tab:
    * - {Boolean} selected Set to true to make this new tab selected by default.
    * - {String} insertBefore By default, the new tab is appended at the end of the
    * tabbox, pass the ID of an existing tab to insert it before that tab instead.
    */
   addTab: function(id, url, options = {}) {
-    const iframe = this._panelDoc.createElementNS(XULNS, "iframe");
+    const iframe = this._panelDoc.createXULElement("iframe");
     iframe.className = "iframe-" + id;
     iframe.setAttribute("flex", "1");
     iframe.setAttribute("src", url);
     iframe.tooltip = "aHTMLTooltip";
 
     // Creating the tab and adding it to the tabbox
-    const tab = this._panelDoc.createElementNS(XULNS, "tab");
+    const tab = this._panelDoc.createXULElement("tab");
 
     tab.setAttribute("id", this.TAB_ID_PREFIX + id);
     tab.setAttribute("crop", "end");
     // Avoid showing "undefined" while the tab is loading
     tab.setAttribute("label", "");
 
     if (options.insertBefore) {
       const referenceTab = this.getTab(options.insertBefore);
@@ -267,28 +265,28 @@ ToolSidebar.prototype = {
       if ("setPanel" in win) {
         win.setPanel(this._toolPanel, iframe);
       }
       this.emit(id + "-ready");
     };
 
     iframe.addEventListener("load", onIFrameLoaded, true);
 
-    const tabpanel = this._panelDoc.createElementNS(XULNS, "tabpanel");
+    const tabpanel = this._panelDoc.createXULElement("tabpanel");
     tabpanel.setAttribute("id", this.TABPANEL_ID_PREFIX + id);
     tabpanel.appendChild(iframe);
 
     if (options.insertBefore) {
       const referenceTabpanel = this.getTabPanel(options.insertBefore);
       this._tabbox.tabpanels.insertBefore(tabpanel, referenceTabpanel);
     } else {
       this._tabbox.tabpanels.appendChild(tabpanel);
     }
 
-    this._tooltip = this._panelDoc.createElementNS(XULNS, "tooltip");
+    this._tooltip = this._panelDoc.createXULElement("tooltip");
     this._tooltip.id = "aHTMLTooltip";
     tabpanel.appendChild(this._tooltip);
     this._tooltip.page = true;
 
     tab.linkedPanel = this.TABPANEL_ID_PREFIX + id;
 
     // We store the index of this tab.
     this._tabs.set(id, tab);
--- a/devtools/client/netmonitor/src/middleware/event-telemetry.js
+++ b/devtools/client/netmonitor/src/middleware/event-telemetry.js
@@ -5,54 +5,72 @@
 "use strict";
 
 const { gDevTools } = require("devtools/client/framework/devtools");
 
 const {
   TOGGLE_REQUEST_FILTER_TYPE,
   ENABLE_REQUEST_FILTER_TYPE_ONLY,
   SET_REQUEST_FILTER_TEXT,
+  SELECT_DETAILS_PANEL_TAB,
 } = require("../constants");
 
 /**
  * Event telemetry middleware is responsible for logging
- * specific filter events to telemetry. This telemetry
- * helps to track Net panel filtering usage.
+ * various events to telemetry. This helps to track Network
+ * panel usage.
  */
 function eventTelemetryMiddleware(connector, telemetry) {
   return store => next => action => {
     const oldState = store.getState();
     const res = next(action);
     const toolbox = gDevTools.getToolbox(connector.getTabTarget());
     if (!toolbox) {
       return res;
     }
 
     const state = store.getState();
+    const sessionId = toolbox.sessionId;
 
     const filterChangeActions = [
       TOGGLE_REQUEST_FILTER_TYPE,
       ENABLE_REQUEST_FILTER_TYPE_ONLY,
       SET_REQUEST_FILTER_TEXT,
     ];
 
+    // Record telemetry event when filter changes.
     if (filterChangeActions.includes(action.type)) {
       filterChange({
         action,
         state,
         oldState,
         telemetry,
-        sessionId: toolbox.sessionId,
+        sessionId,
+      });
+    }
+
+    // Record telemetry event when side panel is selected.
+    if (action.type == SELECT_DETAILS_PANEL_TAB) {
+      sidePanelChange({
+        action,
+        state,
+        oldState,
+        telemetry,
+        sessionId,
       });
     }
 
     return res;
   };
 }
 
+/**
+ * This helper function is executed when filter related action is fired.
+ * It's responsible for recording "filters_changed" telemetry event.
+ */
 function filterChange({action, state, oldState, telemetry, sessionId}) {
   const oldFilterState = oldState.filters;
   const filterState = state.filters;
   const activeFilters = [];
   const inactiveFilters = [];
 
   for (const [key, value] of Object.entries(filterState.requestFilterTypes)) {
     if (value) {
@@ -74,13 +92,26 @@ function filterChange({action, state, ol
 
     trigger = "text";
   }
 
   telemetry.recordEvent("devtools.main", "filters_changed", "netmonitor", null, {
     "trigger": trigger,
     "active": activeFilters.join(","),
     "inactive": inactiveFilters.join(","),
-    "session_id": sessionId
+    "session_id": sessionId,
+  });
+}
+
+/**
+ * This helper function is executed when side panel is selected.
+ * It's responsible for recording "sidepanel_tool_changed"
+ * telemetry event.
+ */
+function sidePanelChange({action, state, oldState, telemetry, sessionId}) {
+  telemetry.recordEvent("devtools.main", "sidepanel_changed", "netmonitor", null, {
+    "oldpanel": oldState.ui.detailsPanelSelectedTab,
+    "newpanel": state.ui.detailsPanelSelectedTab,
+    "session_id": sessionId,
   });
 }
 
 module.exports = eventTelemetryMiddleware;
--- a/devtools/client/netmonitor/test/browser.ini
+++ b/devtools/client/netmonitor/test/browser.ini
@@ -181,15 +181,16 @@ skip-if = true # Bug 1258809
 [browser_net_sort-02.js]
 [browser_net_statistics-01.js]
 skip-if = true # Bug 1373558
 [browser_net_statistics-02.js]
 [browser_net_status-bar.js]
 [browser_net_status-codes.js]
 [browser_net_streaming-response.js]
 [browser_net_telemetry_filters_changed.js]
+[browser_net_telemetry_sidepanel_changed.js]
 [browser_net_throttle.js]
 [browser_net_timeline_ticks.js]
 skip-if = true # TODO: fix the test
 [browser_net_timing-division.js]
 [browser_net_truncate.js]
 [browser_net_view-source-debugger.js]
 [browser_net_waterfall-click.js]
--- a/devtools/client/netmonitor/test/browser_net_telemetry_filters_changed.js
+++ b/devtools/client/netmonitor/test/browser_net_telemetry_filters_changed.js
@@ -37,73 +37,52 @@ add_task(async function() {
   info("Click on the 'HTML' filter");
   EventUtils.sendMouseEvent({ type: "click" },
     document.querySelector(".requests-list-filter-html-button"));
 
   checkTelemetryEvent({
     trigger: "html",
     active: "html",
     inactive: "all,css,js,xhr,fonts,images,media,ws,other",
+  }, {
+    method: "filters_changed"
   });
 
   info("Click on the 'CSS' filter");
   EventUtils.sendMouseEvent({ type: "click" },
     document.querySelector(".requests-list-filter-css-button"));
 
   checkTelemetryEvent({
     trigger: "css",
     active: "html,css",
     inactive: "all,js,xhr,fonts,images,media,ws,other",
+  }, {
+    method: "filters_changed"
   });
 
   info("Filter the output using the text filter input");
   setFreetextFilter(monitor, "nomatch");
 
   // Wait till the text filter is applied.
   await waitUntil(() => getDisplayedRequests(store.getState()).length == 0);
 
   checkTelemetryEvent({
     trigger: "text",
     active: "html,css",
     inactive: "all,js,xhr,fonts,images,media,ws,other",
+  }, {
+    method: "filters_changed"
   });
 
   return teardown(monitor);
 });
 
 function setFreetextFilter(monitor, value) {
   const { document } = monitor.panelWin;
 
   const filterBox = document.querySelector(".devtools-filterinput");
   filterBox.focus();
   filterBox.value = "";
 
   for (const ch of value) {
     EventUtils.synthesizeKey(ch, {}, monitor.panelWin);
   }
 }
-
-function checkTelemetryEvent(expectedEvent) {
-  const events = getFiltersChangedEventsExtra();
-  is(events.length, 1, "There was only 1 event logged");
-  const [event] = events;
-  ok(event.session_id > 0, "There is a valid session_id in the logged event");
-  const f = e => JSON.stringify(e, null, 2);
-  is(f(event), f({
-    ...expectedEvent,
-    "session_id": event.session_id
-  }), "The event has the expected data");
-}
-
-function getFiltersChangedEventsExtra() {
-  // Retrieve and clear telemetry events.
-  const snapshot = Services.telemetry.snapshotEvents(OPTOUT, true);
-
-  const filtersChangedEvents = snapshot.parent.filter(event =>
-    event[1] === "devtools.main" &&
-    event[2] === "filters_changed" &&
-    event[3] === "netmonitor"
-  );
-
-  // Since we already know we have the correct event, we only return the `extra` field
-  // that was passed to it (which is event[5] here).
-  return filtersChangedEvents.map(event => event[5]);
-}
new file mode 100644
--- /dev/null
+++ b/devtools/client/netmonitor/test/browser_net_telemetry_sidepanel_changed.js
@@ -0,0 +1,55 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const OPTOUT = Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTOUT;
+
+/**
+ * Test the sidepanel_changed telemetry event.
+ */
+add_task(async function() {
+  const { tab, monitor } = await initNetMonitor(SIMPLE_URL);
+  info("Starting test... ");
+
+  const { document, store, windowRequire } = monitor.panelWin;
+  const Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
+  store.dispatch(Actions.batchEnable(false));
+
+  // Remove all telemetry events (you can check about:telemetry).
+  Services.telemetry.clearEvents();
+
+  // Ensure no events have been logged
+  const snapshot = Services.telemetry.snapshotEvents(OPTOUT, true);
+  ok(!snapshot.parent, "No events have been logged for the main process");
+
+  // Reload to have one request in the list.
+  const waitForEvents = waitForNetworkEvents(monitor, 1);
+  tab.linkedBrowser.loadURI(SIMPLE_URL);
+  await waitForEvents;
+
+  // Click on a request and wait till the default "Headers" side panel is opened.
+  info("Click on a request");
+  const waitForHeaders = waitUntil(() => document.querySelector(".headers-overview"));
+  EventUtils.sendMouseEvent({ type: "mousedown" },
+    document.querySelectorAll(".request-list-item")[0]);
+  await waitForHeaders;
+  await waitForRequestData(store, ["requestHeaders", "responseHeaders"]);
+
+  // Click on the Cookies panel and wait till it's opened.
+  info("Click on the Cookies panel");
+  EventUtils.sendMouseEvent({ type: "click" },
+    document.querySelector("#cookies-tab"));
+  await waitForRequestData(store, ["requestCookies", "responseCookies"]);
+
+  checkTelemetryEvent({
+    oldpanel: "headers",
+    newpanel: "cookies",
+  }, {
+    method: "sidepanel_changed"
+  });
+
+  return teardown(monitor);
+});
--- a/devtools/client/netmonitor/test/head.js
+++ b/devtools/client/netmonitor/test/head.js
@@ -794,8 +794,47 @@ function waitForRequestData(store, field
     for (const field of fields) {
       if (!item[field]) {
         return false;
       }
     }
     return true;
   });
 }
+
+// Telemetry
+
+/**
+ * Helper for verifying telemetry event.
+ *
+ * @param Object expectedEvent object representing expected event data.
+ * @param Object query fields specifying category, method and object
+ *                     of the target telemetry event.
+ */
+function checkTelemetryEvent(expectedEvent, query) {
+  const events = queryTelemetryEvents(query);
+  is(events.length, 1, "There was only 1 event logged");
+
+  const [event] = events;
+  ok(event.session_id > 0, "There is a valid session_id in the logged event");
+
+  const f = e => JSON.stringify(e, null, 2);
+  is(f(event), f({
+    ...expectedEvent,
+    "session_id": event.session_id
+  }), "The event has the expected data");
+}
+
+function queryTelemetryEvents(query) {
+  const OPTOUT = Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTOUT;
+  const snapshot = Services.telemetry.snapshotEvents(OPTOUT, true);
+  const category = query.category || "devtools.main";
+  const object = query.object || "netmonitor";
+
+  const filtersChangedEvents = snapshot.parent.filter(event =>
+    event[1] === category &&
+    event[2] === query.method &&
+    event[3] === object
+  );
+
+  // Return the `extra` field (which is event[5]e).
+  return filtersChangedEvents.map(event => event[5]);
+}
--- a/devtools/client/shared/widgets/TableWidget.js
+++ b/devtools/client/shared/widgets/TableWidget.js
@@ -7,17 +7,16 @@ const EventEmitter = require("devtools/s
 loader.lazyRequireGetter(this, "setNamedTimeout",
   "devtools/client/shared/widgets/view-helpers", true);
 loader.lazyRequireGetter(this, "clearNamedTimeout",
   "devtools/client/shared/widgets/view-helpers", true);
 loader.lazyRequireGetter(this, "naturalSortCaseInsensitive",
   "devtools/client/shared/natural-sort", true);
 const {KeyCodes} = require("devtools/client/shared/keycodes");
 
-const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
 const HTML_NS = "http://www.w3.org/1999/xhtml";
 const AFTER_SCROLL_DELAY = 100;
 
 // Different types of events emitted by the Various components of the
 // TableWidget.
 const EVENTS = {
   CELL_EDIT: "cell-edit",
   COLUMN_SORTED: "column-sorted",
@@ -72,25 +71,25 @@ function TableWidget(node, options = {})
   this.emptyText = emptyText || "";
   this.uniqueId = uniqueId || "name";
   this.wrapTextInElements = wrapTextInElements || false;
   this.firstColumn = firstColumn || "";
   this.highlightUpdated = highlightUpdated || false;
   this.removableColumns = removableColumns !== false;
   this.cellContextMenuId = cellContextMenuId;
 
-  this.tbody = this.document.createElementNS(XUL_NS, "hbox");
+  this.tbody = this.document.createXULElement("hbox");
   this.tbody.className = "table-widget-body theme-body";
   this.tbody.setAttribute("flex", "1");
   this.tbody.setAttribute("tabindex", "0");
   this._parent.appendChild(this.tbody);
   this.afterScroll = this.afterScroll.bind(this);
   this.tbody.addEventListener("scroll", this.onScroll.bind(this));
 
-  this.placeholder = this.document.createElementNS(XUL_NS, "label");
+  this.placeholder = this.document.createXULElement("label");
   this.placeholder.className = "plain table-widget-empty-text";
   this.placeholder.setAttribute("flex", "1");
   this._parent.appendChild(this.placeholder);
 
   this.items = new Map();
   this.columns = new Map();
 
   // Setup the column headers context menu to allow users to hide columns at
@@ -639,21 +638,21 @@ TableWidget.prototype = {
   /**
    * Prepares the context menu for the headers of the table columns. This
    * context menu allows users to toggle various columns, only with an exception
    * of the unique columns and when only two columns are visible in the table.
    */
   setupHeadersContextMenu: function() {
     let popupset = this.document.getElementsByTagName("popupset")[0];
     if (!popupset) {
-      popupset = this.document.createElementNS(XUL_NS, "popupset");
+      popupset = this.document.createXULElement("popupset");
       this.document.documentElement.appendChild(popupset);
     }
 
-    this.menupopup = this.document.createElementNS(XUL_NS, "menupopup");
+    this.menupopup = this.document.createXULElement("menupopup");
     this.menupopup.id = "table-widget-column-select";
     this.menupopup.addEventListener("command", this.onPopupCommand);
     popupset.appendChild(this.menupopup);
     this.populateMenuPopup();
   },
 
   /**
    * Populates the header context menu with the names of the columns along with
@@ -673,17 +672,17 @@ TableWidget.prototype = {
       this.menupopup.firstChild.remove();
     }
 
     for (const column of this.columns.values()) {
       if (privateColumns.includes(column.id)) {
         continue;
       }
 
-      const menuitem = this.document.createElementNS(XUL_NS, "menuitem");
+      const menuitem = this.document.createXULElement("menuitem");
       menuitem.setAttribute("label", column.header.getAttribute("value"));
       menuitem.setAttribute("data-id", column.id);
       menuitem.setAttribute("type", "checkbox");
       menuitem.setAttribute("checked", !column.wrapper.getAttribute("hidden"));
       if (column.id == this.uniqueId) {
         menuitem.setAttribute("disabled", "true");
       }
       this.menupopup.appendChild(menuitem);
@@ -1040,32 +1039,32 @@ function Column(table, id, header) {
   this.table = table;
   this.cells = [];
   this.items = {};
 
   this.highlightUpdated = table.highlightUpdated;
 
   // This wrapping element is required solely so that position:sticky works on
   // the headers of the columns.
-  this.wrapper = this.document.createElementNS(XUL_NS, "vbox");
+  this.wrapper = this.document.createXULElement("vbox");
   this.wrapper.className = "table-widget-wrapper";
   this.wrapper.setAttribute("flex", "1");
   this.wrapper.setAttribute("tabindex", "0");
   this.tbody.appendChild(this.wrapper);
 
-  this.splitter = this.document.createElementNS(XUL_NS, "splitter");
+  this.splitter = this.document.createXULElement("splitter");
   this.splitter.className = "devtools-side-splitter";
   this.tbody.appendChild(this.splitter);
 
   this.column = this.document.createElementNS(HTML_NS, "div");
   this.column.id = id;
   this.column.className = "table-widget-column";
   this.wrapper.appendChild(this.column);
 
-  this.header = this.document.createElementNS(XUL_NS, "label");
+  this.header = this.document.createXULElement("label");
   this.header.className = "devtools-toolbar table-widget-column-header";
   this.header.setAttribute("value", header);
   this.column.appendChild(this.header);
   if (table.headersContextMenu) {
     this.header.setAttribute("context", table.headersContextMenu);
   }
   this.toggleColumn = this.toggleColumn.bind(this);
   this.table.on(EVENTS.HEADER_CONTEXT_MENU, this.toggleColumn);
@@ -1560,17 +1559,17 @@ Column.prototype = {
  * @param {Cell} nextCell
  *        The cell object which is next to this cell. null if this cell is last
  *        cell of the column
  */
 function Cell(column, item, nextCell) {
   const document = column.document;
 
   this.wrapTextInElements = column.wrapTextInElements;
-  this.label = document.createElementNS(XUL_NS, "label");
+  this.label = document.createXULElement("label");
   this.label.setAttribute("crop", "end");
   this.label.className = "plain table-widget-cell";
 
   if (nextCell) {
     column.column.insertBefore(this.label, nextCell.label);
   } else {
     column.column.appendChild(this.label);
   }
@@ -1732,17 +1731,17 @@ EditableFieldsEngine.prototype = {
 
   get isEditing() {
     return this.root && !this.textbox.hidden;
   },
 
   get textbox() {
     if (!this._textbox) {
       const doc = this.root.ownerDocument;
-      this._textbox = doc.createElementNS(XUL_NS, "textbox");
+      this._textbox = doc.createXULElement("textbox");
       this._textbox.id = this.INPUT_ID;
 
       this._textbox.setAttribute("flex", "1");
 
       this.onKeydown = this.onKeydown.bind(this);
       this._textbox.addEventListener("keydown", this.onKeydown);
 
       this.completeEdit = this.completeEdit.bind(this);
--- a/devtools/client/shared/widgets/tooltip/HTMLTooltip.js
+++ b/devtools/client/shared/widgets/tooltip/HTMLTooltip.js
@@ -873,17 +873,17 @@ HTMLTooltip.prototype = {
   /**
    * Check if the tooltip's owner document is a XUL document.
    */
   _isXUL: function() {
     return this.doc.documentElement.namespaceURI === XUL_NS;
   },
 
   _createXulPanelWrapper: function() {
-    const panel = this.doc.createElementNS(XUL_NS, "panel");
+    const panel = this.doc.createXULElement("panel");
 
     // XUL panel is only a way to display DOM elements outside of the document viewport,
     // so disable all features that impact the behavior.
     panel.setAttribute("animate", false);
     panel.setAttribute("consumeoutsideclicks", false);
     panel.setAttribute("noautofocus", true);
     panel.setAttribute("ignorekeys", true);
     panel.setAttribute("tooltip", "aHTMLTooltip");
--- a/devtools/server/actors/webconsole.js
+++ b/devtools/server/actors/webconsole.js
@@ -1175,19 +1175,20 @@ WebConsoleActor.prototype =
 
       if (!hadDebuggee && dbgObject) {
         this.dbg.removeDebuggee(this.evalWindow);
       }
 
       matches = result.matches || [];
       matchProp = result.matchProp;
 
-      // We consider '$' as alphanumerc because it is used in the names of some
-      // helper functions.
-      const lastNonAlphaIsDot = /[.][a-zA-Z0-9$]*$/.test(reqText);
+      // We consider '$' as alphanumeric because it is used in the names of some
+      // helper functions; we also consider whitespace as alphanum since it should not
+      // be seen as break in the evaled string.
+      const lastNonAlphaIsDot = /[.][a-zA-Z0-9$\s]*$/.test(reqText);
       if (!lastNonAlphaIsDot) {
         matches = matches.concat(this._getWebConsoleCommandsCache().filter(n =>
           // filter out `screenshot` command as it is inaccessible without
           // the `:` prefix
           n !== "screenshot" && n.startsWith(result.matchProp)
         ));
       }
     }
--- a/devtools/shared/webconsole/js-property-provider.js
+++ b/devtools/shared/webconsole/js-property-provider.js
@@ -46,39 +46,68 @@ function hasArrayIndex(str) {
  *          If there was an error in the string detected, then a object like
  *
  *            { err: "ErrorMesssage" }
  *
  *          is returned, otherwise a object like
  *
  *            {
  *              state: STATE_NORMAL|STATE_QUOTE|STATE_DQUOTE,
- *              startPos: index of where the last statement begins
+ *              lastStatement: the last statement in the string
  *            }
  */
 function findCompletionBeginning(str) {
   const bodyStack = [];
 
   let state = STATE_NORMAL;
   let start = 0;
   let c;
-  for (let i = 0; i < str.length; i++) {
-    c = str[i];
+
+  // Use an array in order to handle character with a length > 2 (e.g. 😎).
+  const characters = Array.from(str);
+  for (let i = 0; i < characters.length; i++) {
+    c = characters[i];
 
     switch (state) {
       // Normal JS state.
       case STATE_NORMAL:
         if (c == '"') {
           state = STATE_DQUOTE;
         } else if (c == "'") {
           state = STATE_QUOTE;
         } else if (c == ";") {
           start = i + 1;
         } else if (c == " ") {
-          start = i + 1;
+          const before = characters.slice(0, i);
+          const after = characters.slice(i + 1);
+          const trimmedBefore = Array.from(before.join("").trimRight());
+          const trimmedAfter = Array.from(after.join("").trimLeft());
+
+          const nextNonSpaceChar = trimmedAfter[0];
+          const nextNonSpaceCharIndex = after.indexOf(nextNonSpaceChar);
+          const previousNonSpaceChar = trimmedBefore[trimmedBefore.length - 1];
+
+          // If the previous meaningful char was a dot and there is no meaningful char
+          // after, we can break out of the loop.
+          if (previousNonSpaceChar === "." && !nextNonSpaceChar) {
+            break;
+          }
+
+          if (nextNonSpaceChar) {
+            // If the previous char wasn't a dot, and the next one isn't a dot either,
+            // update the start pos.
+            if (previousNonSpaceChar !== "." && nextNonSpaceChar !== ".") {
+              start = i + nextNonSpaceCharIndex;
+            }
+            // Let's jump to handle the next non-space char.
+            i = i + nextNonSpaceCharIndex;
+          } else {
+            // There's only spaces after that, so we can break out of the loop.
+            break;
+          }
         } else if (OPEN_BODY.includes(c)) {
           bodyStack.push({
             token: c,
             start: start
           });
           start = i + 1;
         } else if (CLOSE_BODY.includes(c)) {
           const last = bodyStack.pop();
@@ -120,17 +149,17 @@ function findCompletionBeginning(str) {
           state = STATE_NORMAL;
         }
         break;
     }
   }
 
   return {
     state: state,
-    startPos: start
+    lastStatement: characters.slice(start).join("")
   };
 }
 
 /**
  * Provides a list of properties, that are possible matches based on the passed
  * Debugger.Environment/Debugger.Object and inputValue.
  *
  * @param object dbgObject
@@ -160,30 +189,30 @@ function JSPropertyProvider(dbgObject, a
   if (cursor === undefined) {
     cursor = inputValue.length;
   }
 
   inputValue = inputValue.substring(0, cursor);
 
   // Analyse the inputValue and find the beginning of the last part that
   // should be completed.
-  const beginning = findCompletionBeginning(inputValue);
+  const {err, state, lastStatement} = findCompletionBeginning(inputValue);
 
   // There was an error analysing the string.
-  if (beginning.err) {
+  if (err) {
     return null;
   }
 
   // If the current state is not STATE_NORMAL, then we are inside of an string
   // which means that no completion is possible.
-  if (beginning.state != STATE_NORMAL) {
+  if (state != STATE_NORMAL) {
     return null;
   }
 
-  const completionPart = inputValue.substring(beginning.startPos);
+  const completionPart = lastStatement;
   const lastDot = completionPart.lastIndexOf(".");
 
   // Don't complete on just an empty string.
   if (completionPart.trim() == "") {
     return null;
   }
 
   // Catch literals like [1,2,3] or "foo" and return the matches from
@@ -196,17 +225,17 @@ function JSPropertyProvider(dbgObject, a
     const syntaxTree = parser.get(completionPart.slice(0, lastDot));
     const lastTree = syntaxTree.getLastSyntaxTree();
     const lastBody = lastTree && lastTree.AST.body[lastTree.AST.body.length - 1];
 
     // Finding the last expression since we've sliced up until the dot.
     // If there were parse errors this won't exist.
     if (lastBody) {
       const expression = lastBody.expression;
-      const matchProp = completionPart.slice(lastDot + 1);
+      const matchProp = completionPart.slice(lastDot + 1).trimLeft();
       if (expression.type === "ArrayExpression") {
         return getMatchedProps(Array.prototype, matchProp);
       } else if (expression.type === "Literal" &&
                  (typeof expression.value === "string")) {
         return getMatchedProps(String.prototype, matchProp);
       }
     }
   }
--- a/devtools/shared/webconsole/test/test_jsterm_autocomplete.html
+++ b/devtools/shared/webconsole/test/test_jsterm_autocomplete.html
@@ -7,219 +7,241 @@
   <script type="text/javascript" src="common.js"></script>
   <!-- Any copyright is dedicated to the Public Domain.
      - http://creativecommons.org/publicdomain/zero/1.0/ -->
 </head>
 <body>
 <p>Test for JavaScript terminal autocomplete functionality</p>
 
 <script class="testbody" type="text/javascript">
-SimpleTest.waitForExplicitFinish();
+  SimpleTest.waitForExplicitFinish();
+  const {
+    MAX_AUTOCOMPLETE_ATTEMPTS,
+    MAX_AUTOCOMPLETIONS
+  } = require("devtools/shared/webconsole/js-property-provider");
 
-let gState;
-let {MAX_AUTOCOMPLETE_ATTEMPTS,MAX_AUTOCOMPLETIONS} = require("devtools/shared/webconsole/js-property-provider");
+  addEventListener("load", startTest);
+
+  async function startTest() {
+    // First run the tests with a tab as a target.
+    let state = await new Promise(resolve => attachConsoleToTab(["PageError"], resolve));
+    await performTests({state, isWorker: false});
 
-function evaluateJS(input, options = {}) {
-  return new Promise((resolve, reject) => {
-    gState.client.evaluateJSAsync(input, resolve, options);
-  });
-}
+    // Then run the tests with a worker as a target.
+    state = await new Promise(resolve => attachConsoleToWorker(["PageError"], resolve));
+    await performTests({state, isWorker: true});
+
+    SimpleTest.finish();
+  }
 
-function autocompletePromise(str, cursor = str.length, frameActor) {
-  return gState.client.autocomplete(str, cursor, frameActor);
-}
+  async function performTests({state, isWorker}) {
+    // Set up the global variables needed to test autocompletion in the target.
+    const script = `
+      // This is for workers so autocomplete acts the same
+      if (!this.window) {
+        window = this;
+      }
 
-// This test runs all of its assertions twice - once with
-// the tab as a target and once with a worker
-let runningInTab = true;
-function startTest({worker}) {
-  if (worker) {
-    attachConsoleToWorker(["PageError"], onAttach.bind(null, true));
-  } else {
-    attachConsoleToTab(["PageError"], onAttach.bind(null, false));
-  }
-};
+      window.foobarObject = Object.create(null);
+      window.foobarObject.foo = 1;
+      window.foobarObject.foobar = 2;
+      window.foobarObject.foobaz = 3;
+      window.foobarObject.omg = 4;
+      window.foobarObject.omgfoo = 5;
+      window.foobarObject.strfoo = "foobarz";
+      window.foobarObject.omgstr = "foobarz" +
+        (new Array(${DebuggerServer.LONG_STRING_LENGTH})).join("abb");
+      window.largeObject1 = Object.create(null);
+      for (let i = 0; i < ${MAX_AUTOCOMPLETE_ATTEMPTS + 1}; i++) {
+        window.largeObject1['a' + i] = i;
+      }
+
+      window.largeObject2 = Object.create(null);
+      for (let i = 0; i < ${MAX_AUTOCOMPLETIONS * 2}; i++) {
+        window.largeObject2['a' + i] = i;
+      }
 
-let onAttach = async function (isWorker, aState, response) {
-  gState = aState;
-
-  let longStrLength = DebuggerServer.LONG_STRING_LENGTH;
+      window.proxy1 = new Proxy({foo: 1}, {
+        getPrototypeOf() { throw new Error() }
+      });
+      window.proxy2 = new Proxy(Object.create(Object.create(null, {foo:{}})), {
+        ownKeys() { throw new Error() }
+      });
+      window.emojiObject = Object.create(null);
+      window.emojiObject["😎"] = "😎";
+    `;
+    await state.client.evaluateJSAsync(script);
 
-  // Set up the global variables needed to test autocompletion
-  // in the target.
-  let script = `
-    // This is for workers so autocomplete acts the same
-    if (!this.window) {
-      window = this;
+    const tests = [
+      doAutocomplete1,
+      doAutocomplete2,
+      doAutocomplete3,
+      doAutocomplete4,
+      doAutocompleteLarge1,
+      doAutocompleteLarge2,
+      doAutocompleteProxyThrowsPrototype,
+      doAutocompleteProxyThrowsOwnKeys,
+      doAutocompleteDotSurroundedBySpaces,
+      doAutocompleteAfterOr,
+    ];
+
+    if (!isWorker) {
+      // `Cu` is not defined in workers, then we can't test `Cu.Sandbox`
+      tests.push(doAutocompleteSandbox);
+      // Array literal completion isn't handled in Workers yet.
+      tests.push(doAutocompleteArray);
     }
 
-    window.foobarObject = Object.create(null);
-    window.foobarObject.foo = 1;
-    window.foobarObject.foobar = 2;
-    window.foobarObject.foobaz = 3;
-    window.foobarObject.omg = 4;
-    window.foobarObject.omgfoo = 5;
-    window.foobarObject.strfoo = "foobarz";
-    window.foobarObject.omgstr = "foobarz" +
-      (new Array(${longStrLength})).join("abb");
-    window.largeObject1 = Object.create(null);
-    for (let i = 0; i < ${MAX_AUTOCOMPLETE_ATTEMPTS + 1}; i++) {
-      window.largeObject1['a' + i] = i;
+    for (const test of tests) {
+      await test(state.client);
     }
 
-    window.largeObject2 = Object.create(null);
-    for (let i = 0; i < ${MAX_AUTOCOMPLETIONS * 2}; i++) {
-      window.largeObject2['a' + i] = i;
-    }
+    await closeDebugger(state);
+  }
+
+  async function doAutocomplete1(client) {
+    info("test autocomplete for 'window.foo'");
+    let response = await client.autocomplete("window.foo");
+    let matches = response.matches;
 
-    window.proxy1 = new Proxy({foo: 1}, {
-      getPrototypeOf() { throw new Error() }
-    });
-    window.proxy2 = new Proxy(Object.create(Object.create(null, {foo:{}})), {
-      ownKeys() { throw new Error() }
-    });
-  `;
+    is(response.matchProp, "foo", "matchProp");
+    is(matches.length, 1, "matches.length");
+    is(matches[0], "foobarObject", "matches[0]");
+  }
 
-  await evaluateJS(script);
+  async function doAutocomplete2(client) {
+    info("test autocomplete for 'window.foobarObject.'");
+    let response = await client.autocomplete("window.foobarObject.");
+    let matches = response.matches;
 
-  let tests = [doAutocomplete1, doAutocomplete2, doAutocomplete3,
-               doAutocomplete4, doAutocompleteLarge1,
-               doAutocompleteLarge2, doAutocompleteProxyThrowsPrototype,
-               doAutocompleteProxyThrowsOwnKeys];
-  if (!isWorker) {
-    // `Cu` is not defined in workers, then we can't test `Cu.Sandbox`
-    tests.push(doAutocompleteSandbox);
+    ok(!response.matchProp, "matchProp");
+    is(matches.length, 7, "matches.length");
+    checkObject(matches,
+      ["foo", "foobar", "foobaz", "omg", "omgfoo", "omgstr", "strfoo"]);
   }
 
-  runTests(tests, testEnd);
-};
-
-async function doAutocomplete1() {
-  info("test autocomplete for 'window.foo'");
-  let response = await autocompletePromise("window.foo");
-  let matches = response.matches;
-
-  is(response.matchProp, "foo", "matchProp");
-  is(matches.length, 1, "matches.length");
-  is(matches[0], "foobarObject", "matches[0]");
+  async function doAutocomplete3(client) {
+    // Check that completion suggestions are offered inside the string.
+    info("test autocomplete for 'dump(window.foobarObject.)'");
+    let response = await client.autocomplete("dump(window.foobarObject.)", 25);
+    let matches = response.matches;
 
-  nextTest();
-}
-
-async function doAutocomplete2() {
-  info("test autocomplete for 'window.foobarObject.'");
-  let response = await autocompletePromise("window.foobarObject.");
-  let matches = response.matches;
+    ok(!response.matchProp, "matchProp");
+    is(matches.length, 7, "matches.length");
+    checkObject(matches,
+      ["foo", "foobar", "foobaz", "omg", "omgfoo", "omgstr", "strfoo"]);
+  }
 
-  ok(!response.matchProp, "matchProp");
-  is(matches.length, 7, "matches.length");
-  checkObject(matches,
-    ["foo", "foobar", "foobaz", "omg", "omgfoo", "omgstr", "strfoo"]);
-
-  nextTest();
-}
+  async function doAutocomplete4(client) {
+    // Check that completion requests can have no suggestions.
+    info("test autocomplete for 'dump(window.foobarObject.)'");
+    let response = await client.autocomplete("dump(window.foobarObject.)");
+    ok(!response.matchProp, "matchProp");
+    is(response.matches.length, 0, "matches.length");
+  }
 
-async function doAutocomplete3() {
-  // Check that completion suggestions are offered inside the string.
-  info("test autocomplete for 'dump(window.foobarObject.)'");
-  let response = await autocompletePromise("dump(window.foobarObject.)", 25);
-  let matches = response.matches;
-
-  ok(!response.matchProp, "matchProp");
-  is(matches.length, 7, "matches.length");
-  checkObject(matches,
-    ["foo", "foobar", "foobaz", "omg", "omgfoo", "omgstr", "strfoo"]);
-
-  nextTest();
-}
+  async function doAutocompleteLarge1(client) {
+    // Check that completion requests with too large objects will
+    // have no suggestions.
+    info("test autocomplete for 'window.largeObject1.'");
+    let response = await client.autocomplete("window.largeObject1.");
+    ok(!response.matchProp, "matchProp");
+    info (response.matches.join("|"));
+    is(response.matches.length, 0, "Bailed out with too many properties");
+  }
 
-async function doAutocomplete4() {
-  // Check that completion requests can have no suggestions.
-  info("test autocomplete for 'dump(window.foobarObject.)'");
-  let response = await autocompletePromise("dump(window.foobarObject.)");
-  ok(!response.matchProp, "matchProp");
-  is(response.matches.length, 0, "matches.length");
-
-  nextTest();
-}
+  async function doAutocompleteLarge2(client) {
+    // Check that completion requests with pretty large objects will
+    // have MAX_AUTOCOMPLETIONS suggestions
+    info("test autocomplete for 'window.largeObject2.'");
+    let response = await client.autocomplete("window.largeObject2.");
+    ok(!response.matchProp, "matchProp");
+    is(response.matches.length, MAX_AUTOCOMPLETIONS, "matches.length is MAX_AUTOCOMPLETIONS");
+  }
 
-async function doAutocompleteLarge1() {
-  // Check that completion requests with too large objects will
-  // have no suggestions.
-  info("test autocomplete for 'window.largeObject1.'");
-  let response = await autocompletePromise("window.largeObject1.");
-  ok(!response.matchProp, "matchProp");
-  info (response.matches.join("|"));
-  is(response.matches.length, 0, "Bailed out with too many properties");
+  async function doAutocompleteProxyThrowsPrototype(client) {
+    // Check that completion provides own properties even if [[GetPrototypeOf]] throws.
+    info("test autocomplete for 'window.proxy1.'");
+    let response = await client.autocomplete("window.proxy1.");
+    ok(!response.matchProp, "matchProp");
+    is(response.matches.length, 1, "matches.length");
+    checkObject(response.matches, ["foo"]);
+  }
 
-  nextTest();
-}
+  async function doAutocompleteProxyThrowsOwnKeys(client) {
+    // Check that completion provides inherited properties even if [[OwnPropertyKeys]] throws.
+    info("test autocomplete for 'window.proxy2.'");
+    let response = await client.autocomplete("window.proxy2.");
+    ok(!response.matchProp, "matchProp");
+    is(response.matches.length, 1, "matches.length");
+    checkObject(response.matches, ["foo"]);
+  }
 
-async function doAutocompleteLarge2() {
-  // Check that completion requests with pretty large objects will
-  // have MAX_AUTOCOMPLETIONS suggestions
-  info("test autocomplete for 'window.largeObject2.'");
-  let response = await autocompletePromise("window.largeObject2.");
-  ok(!response.matchProp, "matchProp");
-  is(response.matches.length, MAX_AUTOCOMPLETIONS, "matches.length is MAX_AUTOCOMPLETIONS");
+  async function doAutocompleteSandbox(client) {
+    // Check that completion provides inherited properties even if [[OwnPropertyKeys]] throws.
+    info("test autocomplete for 'Cu.Sandbox.'");
+    let response = await client.autocomplete("Cu.Sandbox.");
+    ok(!response.matchProp, "matchProp");
+    let keys = Object.getOwnPropertyNames(Object.prototype).sort();
+    is(response.matches.length, keys.length, "matches.length");
+    checkObject(response.matches, keys);
+  }
 
-  nextTest();
-}
+  async function doAutocompleteArray(client) {
+    info("test autocomplete for [1,2,3]");
+    let response = await client.autocomplete("[1,2,3].");
+    let {matches} = response;
 
-async function doAutocompleteProxyThrowsPrototype() {
-  // Check that completion provides own properties even if [[GetPrototypeOf]] throws.
-  info("test autocomplete for 'window.proxy1.'");
-  let response = await autocompletePromise("window.proxy1.");
-  ok(!response.matchProp, "matchProp");
-  is(response.matches.length, 1, "matches.length");
-  checkObject(response.matches, ["foo"]);
-
-  nextTest();
-}
+    ok(matches.length > 0, "There are completion results for the array");
+    ok(matches.includes("length") && matches.includes("filter"),
+      "Array autocomplete contains expected results");
 
-async function doAutocompleteProxyThrowsOwnKeys() {
-  // Check that completion provides inherited properties even if [[OwnPropertyKeys]] throws.
-  info("test autocomplete for 'window.proxy2.'");
-  let response = await autocompletePromise("window.proxy2.");
-  ok(!response.matchProp, "matchProp");
-  is(response.matches.length, 1, "matches.length");
-  checkObject(response.matches, ["foo"]);
+    info("test autocomplete for '[] . '");
+    matches = (await client.autocomplete("[] . ")).matches;
+    ok(matches.length > 1);
+    ok(matches.includes("length") && matches.includes("filter"),
+      "Array autocomplete contains expected results");
+    ok(!matches.includes("copy"), "Array autocomplete does not contain helpers");
+  }
 
-  nextTest();
-}
+  async function doAutocompleteDotSurroundedBySpaces(client) {
+    info("test autocomplete for 'window.foobarObject\n  .'");
+    let {matches} = await client.autocomplete("window.foobarObject\n  .");
+    is(matches.length, 7);
+    checkObject(matches,
+      ["foo", "foobar", "foobaz", "omg", "omgfoo", "omgstr", "strfoo"]);
 
-async function doAutocompleteSandbox() {
-  // Check that completion provides inherited properties even if [[OwnPropertyKeys]] throws.
-  info("test autocomplete for 'Cu.Sandbox.'");
-  let response = await autocompletePromise("Cu.Sandbox.");
-  ok(!response.matchProp, "matchProp");
-  let keys = Object.getOwnPropertyNames(Object.prototype).sort();
-  is(response.matches.length, keys.length, "matches.length");
-  checkObject(response.matches, keys);
+    info("test autocomplete for 'window.foobarObject\n  .o'");
+    matches = (await client.autocomplete("window.foobarObject\n  .o")).matches;
+    is(matches.length, 3);
+    checkObject(matches, ["omg", "omgfoo", "omgstr"]);
 
-  nextTest();
-}
+    info("test autocomplete for 'window.foobarObject\n  .\n  s'");
+    matches = (await client.autocomplete("window.foobarObject\n  .\n  s")).matches;
+    is(matches.length, 1);
+    checkObject(matches, ["strfoo"]);
+
+    info("test autocomplete for 'window.foobarObject\n  .  '");
+    matches = (await client.autocomplete("window.foobarObject\n  .  ")).matches;
+    is(matches.length, 7);
+    checkObject(matches,
+      ["foo", "foobar", "foobaz", "omg", "omgfoo", "omgstr", "strfoo"]);
 
-function testEnd()
-{
-  // If this is the first run, reload the page and do it again
-  // in a worker.  Otherwise, end the test.
-  closeDebugger(gState, function() {
-    gState = null;
-    if (runningInTab) {
-      runningInTab = false;
-      startTest({
-        worker: true
-      });
-    } else {
-      SimpleTest.finish();
-    }
-  });
-}
+    matches =
+      (await client.autocomplete("window.foobarObject.  foo ; window.foo")).matches;
+    is(matches.length, 1);
+    checkObject(matches, ["foobarObject"]);
 
-addEventListener("load", () => {
-  startTest({
-    worker: false
-  });
-});
+    matches =
+      (await client.autocomplete("window.emojiObject  .  ")).matches;
+    is(matches.length, 1);
+    checkObject(matches, ["😎"]);
+  }
+
+  async function doAutocompleteAfterOr(client) {
+    info("test autocomplete for 'true || foo'");
+    const {matches} = await client.autocomplete("true || foobar");
+    is(matches.length, 1, "autocomplete returns expected results");
+    is(matches.join("-"), "foobarObject");
+  }
 </script>
 </body>
 </html>
--- a/dom/base/nsRange.cpp
+++ b/dom/base/nsRange.cpp
@@ -2357,48 +2357,52 @@ nsRange::CloneParentsBetween(nsINode *aA
   NS_ENSURE_ARG_POINTER((aAncestor && aNode && aClosestAncestor && aFarthestAncestor));
 
   *aClosestAncestor  = nullptr;
   *aFarthestAncestor = nullptr;
 
   if (aAncestor == aNode)
     return NS_OK;
 
-  nsCOMPtr<nsINode> firstParent, lastParent;
+  AutoTArray<nsCOMPtr<nsINode>, 16> parentStack;
+
   nsCOMPtr<nsINode> parent = aNode->GetParentNode();
-
   while(parent && parent != aAncestor)
   {
+    parentStack.AppendElement(parent);
+    parent = parent->GetParentNode();
+  }
+
+  nsCOMPtr<nsINode> firstParent;
+  nsCOMPtr<nsINode> lastParent;
+  for (int32_t i = parentStack.Length() - 1; i >= 0; i--) {
     ErrorResult rv;
-    nsCOMPtr<nsINode> clone = parent->CloneNode(false, rv);
+    nsCOMPtr<nsINode> clone = parentStack[i]->CloneNode(false, rv);
 
     if (rv.Failed()) {
       return rv.StealNSResult();
     }
     if (!clone) {
       return NS_ERROR_FAILURE;
     }
 
-    if (! firstParent) {
-      firstParent = lastParent = clone;
+    if (!lastParent) {
+      lastParent = clone;
     } else {
-      clone->AppendChild(*lastParent, rv);
-      if (rv.Failed()) return rv.StealNSResult();
-
-      lastParent = clone;
+      firstParent->AppendChild(*clone, rv);
+      if (rv.Failed()) {
+        return rv.StealNSResult();
+      }
     }
 
-    parent = parent->GetParentNode();
+    firstParent = clone;
   }
 
-  *aClosestAncestor  = firstParent;
-  NS_IF_ADDREF(*aClosestAncestor);
-
-  *aFarthestAncestor = lastParent;
-  NS_IF_ADDREF(*aFarthestAncestor);
+  firstParent.forget(aClosestAncestor);
+  lastParent.forget(aFarthestAncestor);
 
   return NS_OK;
 }
 
 already_AddRefed<DocumentFragment>
 nsRange::CloneContents(ErrorResult& aRv)
 {
   nsCOMPtr<nsINode> commonAncestor = GetCommonAncestorContainer(aRv);
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -843,16 +843,17 @@ ParserBase::ParserBase(JSContext* cx, Li
     sourceObject(cx, sourceObject),
     keepAtoms(cx),
     foldConstants(foldConstants),
 #ifdef DEBUG
     checkOptionsCalled(false),
 #endif
     isUnexpectedEOF_(false),
     awaitHandling_(AwaitIsName),
+    inParametersOfAsyncFunction_(false),
     parseGoal_(uint8_t(parseGoal))
 {
     cx->frontendCollectionPool().addActiveCompilation();
     tempPoolMark = alloc.mark();
 }
 
 bool
 ParserBase::checkOptions()
@@ -927,16 +928,39 @@ Parser<FullParseHandler, CharT>::setAwai
 
 template <class ParseHandler, typename CharT>
 inline void
 GeneralParser<ParseHandler, CharT>::setAwaitHandling(AwaitHandling awaitHandling)
 {
     asFinalParser()->setAwaitHandling(awaitHandling);
 }
 
+template <typename CharT>
+void
+Parser<SyntaxParseHandler, CharT>::setInParametersOfAsyncFunction(bool inParameters)
+{
+    this->inParametersOfAsyncFunction_ = inParameters;
+}
+
+template <typename CharT>
+void
+Parser<FullParseHandler, CharT>::setInParametersOfAsyncFunction(bool inParameters)
+{
+    this->inParametersOfAsyncFunction_ = inParameters;
+    if (SyntaxParser* syntaxParser = getSyntaxParser())
+        syntaxParser->setInParametersOfAsyncFunction(inParameters);
+}
+
+template <class ParseHandler, typename CharT>
+inline void
+GeneralParser<ParseHandler, CharT>::setInParametersOfAsyncFunction(bool inParameters)
+{
+    asFinalParser()->setInParametersOfAsyncFunction(inParameters);
+}
+
 ObjectBox*
 ParserBase::newObjectBox(JSObject* obj)
 {
     MOZ_ASSERT(obj);
 
     /*
      * We use JSContext.tempLifoAlloc to allocate parsed objects and place them
      * on a list in this Parser to ensure GC safety. Thus the tempLifoAlloc
@@ -3817,16 +3841,17 @@ GeneralParser<ParseHandler, CharT>::func
     // See below for an explanation why arrow function parameters and arrow
     // function bodies are parsed with different yield/await settings.
     {
         AwaitHandling awaitHandling =
             (funbox->isAsync() || (kind == FunctionSyntaxKind::Arrow && awaitIsKeyword()))
             ? AwaitIsKeyword
             : AwaitIsName;
         AutoAwaitIsKeyword<ParseHandler, CharT> awaitIsKeyword(this, awaitHandling);
+        AutoInParametersOfAsyncFunction<ParseHandler, CharT> inParameters(this, funbox->isAsync());
         if (!functionArguments(yieldHandling, kind, *pn))
             return false;
     }
 
     Maybe<ParseContext::VarScope> varScope;
     if (funbox->hasParameterExprs) {
         varScope.emplace(this);
         if (!varScope->init(pc))
@@ -3878,16 +3903,17 @@ GeneralParser<ParseHandler, CharT>::func
     // Whereas the |yield| in the function body is always parsed as a name.
     // The same goes when parsing |await| in arrow functions.
     YieldHandling bodyYieldHandling = GetYieldHandling(pc->generatorKind());
     AwaitHandling bodyAwaitHandling = GetAwaitHandling(pc->asyncKind());
     bool inheritedStrict = pc->sc()->strict();
     Node body;
     {
         AutoAwaitIsKeyword<ParseHandler, CharT> awaitIsKeyword(this, bodyAwaitHandling);
+        AutoInParametersOfAsyncFunction<ParseHandler, CharT> inParameters(this, false);
         body = functionBody(inHandling, bodyYieldHandling, kind, bodyType);
         if (!body)
             return false;
     }
 
     // Revalidate the function name when we transitioned to strict mode.
     if ((kind == FunctionSyntaxKind::Statement || kind == FunctionSyntaxKind::Expression) &&
         fun->explicitName() &&
@@ -8581,16 +8607,20 @@ GeneralParser<ParseHandler, CharT>::unar
             pc->sc()->setBindingsAccessedDynamically();
         }
 
         return handler.newDelete(begin, expr);
       }
 
       case TokenKind::Await: {
         if (pc->isAsync()) {
+            if (inParametersOfAsyncFunction()) {
+                error(JSMSG_AWAIT_IN_DEFAULT);
+                return null();
+            }
             Node kid = unaryExpr(yieldHandling, tripledotHandling, possibleError, invoked);
             if (!kid)
                 return null();
             pc->lastAwaitOffset = begin;
             return handler.newAwaitExpression(begin, kid);
         }
       }
 
--- a/js/src/frontend/Parser.h
+++ b/js/src/frontend/Parser.h
@@ -244,16 +244,19 @@ enum class PropertyType {
     DerivedConstructor
 };
 
 enum AwaitHandling : uint8_t { AwaitIsName, AwaitIsKeyword, AwaitIsModuleKeyword };
 
 template <class ParseHandler, typename CharT>
 class AutoAwaitIsKeyword;
 
+template <class ParseHandler, typename CharT>
+class AutoInParametersOfAsyncFunction;
+
 class MOZ_STACK_CLASS ParserBase
   : public StrictModeGetter,
     private JS::AutoGCRooter
 {
   private:
     ParserBase* thisForCtor() { return this; }
 
     // This is needed to cast a parser to JS::AutoGCRooter.
@@ -292,28 +295,35 @@ class MOZ_STACK_CLASS ParserBase
     bool checkOptionsCalled:1;
 #endif
 
     /* Unexpected end of input, i.e. Eof not at top-level. */
     bool isUnexpectedEOF_:1;
 
     /* AwaitHandling */ uint8_t awaitHandling_:2;
 
+    bool inParametersOfAsyncFunction_:1;
+
     /* ParseGoal */ uint8_t parseGoal_:1;
 
   public:
     bool awaitIsKeyword() const {
       return awaitHandling_ != AwaitIsName;
     }
 
+    bool inParametersOfAsyncFunction() const {
+        return inParametersOfAsyncFunction_;
+    }
+
     ParseGoal parseGoal() const {
         return ParseGoal(parseGoal_);
     }
 
     template<class, typename> friend class AutoAwaitIsKeyword;
+    template<class, typename> friend class AutoInParametersOfAsyncFunction;
 
     ParserBase(JSContext* cx, LifoAlloc& alloc, const ReadOnlyCompileOptions& options,
                bool foldConstants, UsedNameTracker& usedNames,
                ScriptSourceObject* sourceObject, ParseGoal parseGoal);
     ~ParserBase();
 
     bool checkOptions();
 
@@ -685,16 +695,17 @@ class MOZ_STACK_CLASS GeneralParser
     using Modifier = TokenStreamShared::Modifier;
     using Position = typename TokenStream::Position;
 
     using Base::PredictUninvoked;
     using Base::PredictInvoked;
 
     using Base::alloc;
     using Base::awaitIsKeyword;
+    using Base::inParametersOfAsyncFunction;
     using Base::parseGoal;
 #if DEBUG
     using Base::checkOptionsCalled;
 #endif
     using Base::finishFunctionScopes;
     using Base::finishLexicalScope;
     using Base::foldConstants;
     using Base::getFilename;
@@ -891,16 +902,17 @@ class MOZ_STACK_CLASS GeneralParser
     GeneralParser(JSContext* cx, LifoAlloc& alloc, const ReadOnlyCompileOptions& options,
                   const CharT* chars, size_t length, bool foldConstants,
                   UsedNameTracker& usedNames, SyntaxParser* syntaxParser,
                   LazyScript* lazyOuterFunction,
                   ScriptSourceObject* sourceObject,
                   ParseGoal parseGoal);
 
     inline void setAwaitHandling(AwaitHandling awaitHandling);
+    inline void setInParametersOfAsyncFunction(bool inParameters);
 
     /*
      * Parse a top-level JS script.
      */
     Node parse();
 
     /* Report the given error at the current offset. */
     void error(unsigned errorNumber, ...);
@@ -1350,16 +1362,17 @@ class MOZ_STACK_CLASS Parser<SyntaxParse
 
     PropertyName* bindingIdentifier(YieldHandling yieldHandling) {
         return Base::bindingIdentifier(yieldHandling);
     }
 
     // Functions present in both Parser<ParseHandler, CharT> specializations.
 
     inline void setAwaitHandling(AwaitHandling awaitHandling);
+    inline void setInParametersOfAsyncFunction(bool inParameters);
 
     Node newRegExp();
 
     // Parse a module.
     Node moduleBody(ModuleSharedContext* modulesc);
 
     inline Node importDeclaration();
     inline bool checkLocalExportNames(Node node);
@@ -1468,16 +1481,19 @@ class MOZ_STACK_CLASS Parser<FullParseHa
         return Base::bindingIdentifier(yieldHandling);
     }
 
     // Functions present in both Parser<ParseHandler, CharT> specializations.
 
     friend class AutoAwaitIsKeyword<SyntaxParseHandler, CharT>;
     inline void setAwaitHandling(AwaitHandling awaitHandling);
 
+    friend class AutoInParametersOfAsyncFunction<SyntaxParseHandler, CharT>;
+    inline void setInParametersOfAsyncFunction(bool inParameters);
+
     Node newRegExp();
 
     // Parse a module.
     Node moduleBody(ModuleSharedContext* modulesc);
 
     Node importDeclaration();
     bool checkLocalExportNames(Node node);
     bool checkExportedName(JSAtom* exportName);
@@ -1607,16 +1623,37 @@ class MOZ_STACK_CLASS AutoAwaitIsKeyword
             parser_->setAwaitHandling(awaitHandling);
     }
 
     ~AutoAwaitIsKeyword() {
         parser_->setAwaitHandling(oldAwaitHandling_);
     }
 };
 
+template <class ParseHandler, typename CharT>
+class MOZ_STACK_CLASS AutoInParametersOfAsyncFunction
+{
+    using GeneralParser = frontend::GeneralParser<ParseHandler, CharT>;
+
+  private:
+    GeneralParser* parser_;
+    bool oldInParametersOfAsyncFunction_;
+
+  public:
+    AutoInParametersOfAsyncFunction(GeneralParser* parser, bool inParameters) {
+        parser_ = parser;
+        oldInParametersOfAsyncFunction_ = parser_->inParametersOfAsyncFunction_;
+        parser_->setInParametersOfAsyncFunction(inParameters);
+    }
+
+    ~AutoInParametersOfAsyncFunction() {
+        parser_->setInParametersOfAsyncFunction(oldInParametersOfAsyncFunction_);
+    }
+};
+
 template <typename Scope>
 extern typename Scope::Data*
 NewEmptyBindingData(JSContext* cx, LifoAlloc& alloc, uint32_t numBindings);
 
 mozilla::Maybe<GlobalScope::Data*>
 NewGlobalScopeData(JSContext* context, ParseContext::Scope& scope, LifoAlloc& alloc, ParseContext* pc);
 mozilla::Maybe<EvalScope::Data*>
 NewEvalScopeData(JSContext* context, ParseContext::Scope& scope, LifoAlloc& alloc, ParseContext* pc);
new file mode 100644
--- /dev/null
+++ b/js/src/tests/non262/async-functions/await-in-parameters-of-async-func.js
@@ -0,0 +1,67 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/
+ * Contributor: 
+ */
+
+//-----------------------------------------------------------------------------
+var BUGNUMBER = 1478910;
+var summary = 'JSMSG_AWAIT_IN_DEFAULT error for incomplete await expr in async function/generator parameter';
+
+
+//-----------------------------------------------------------------------------
+test();
+//-----------------------------------------------------------------------------
+
+function test()
+{
+  printBugNumber(BUGNUMBER);
+  printStatus(summary);
+
+  let testAwaitInDefaultExprOfAsyncFunc = (code) => {
+  	assertThrowsInstanceOf(() => eval(code), SyntaxError, "await can't be used in default expression");
+  };
+
+  let testNoException = (code) => {
+  	assertEq(completesNormally(code), true);
+  };
+
+  // https://www.ecma-international.org/ecma-262/9.0/
+
+  // Async Generator Function Definitions : AsyncGeneratorDeclaration & AsyncGeneratorExpression
+  // async function* f() {}
+  // f = async function*() {}
+  testAwaitInDefaultExprOfAsyncFunc("async function* f(a = await) {}");
+  testAwaitInDefaultExprOfAsyncFunc("let f = async function*(a = await) {}");
+
+  testAwaitInDefaultExprOfAsyncFunc("function f(a = async function*(a = await) {}) {}");
+  testAwaitInDefaultExprOfAsyncFunc("function f() { a = async function*(a = await) {}; }");
+
+  testAwaitInDefaultExprOfAsyncFunc("async function* f() { a = async function*(a = await) {}; }");
+  testNoException("async function* f() { let a = function(a = await) {}; }");
+
+  testNoException("async function* f(a = async function*() { await 1; }) {}");
+
+  // Async Function Definitions : AsyncFunctionDeclaration & AsyncFunctionExpression
+  // async function f() {}
+  // f = async function() {}
+  testAwaitInDefaultExprOfAsyncFunc("async function f(a = await) {}");
+  testAwaitInDefaultExprOfAsyncFunc("let f = async function(a = await) {}");
+
+  testAwaitInDefaultExprOfAsyncFunc("function f(a = async function(a = await) {}) {}");
+  testAwaitInDefaultExprOfAsyncFunc("function f() { a = async function(a = await) {}; }");
+
+  testAwaitInDefaultExprOfAsyncFunc("async function f() { a = async function(a = await) {}; }");
+  testNoException("async function f() { let a = function(a = await) {}; }");
+
+  testNoException("async function f(a = async function() { await 1; }) {}");
+
+  // Async Arrow Function Definitions : AsyncArrowFunction
+  // async () => {}
+  testAwaitInDefaultExprOfAsyncFunc("async (a = await) => {}");
+
+  testNoException("async (a = async () => { await 1; }) => {}");
+
+  reportCompare(true, true, summary);
+}
--- a/layout/reftests/bugs/reftest.list
+++ b/layout/reftests/bugs/reftest.list
@@ -1680,17 +1680,17 @@ HTTP == 652991-4.html 652991-4-ref.html
 fuzzy-if(skiaContent,0-1,0-5) == 653930-1.html 653930-1-ref.html
 == 654057-1.html 654057-1-ref.html
 fuzzy-if(skiaContent,0-1,0-65536) == 654950-1.html 654950-1-ref.html # Quartz alpha blending doesn't match GL alpha blending
 == 655549-1.html 655549-1-ref.html
 == 655836-1.html 655836-1-ref.html
 != 656875.html about:blank
 == 658952.html 658952-ref.html
 fuzzy-if(skiaContent,0-10,0-919) fuzzy-if(webrender&&cocoaWidget,54-54,831-831) == 660682-1.html 660682-1-ref.html
-pref(layout.css.xul-tree-pseudos.content.enabled,true) fuzzy-if(d2d,0-1,0-256) skip-if(Android) fuzzy-if(skiaContent,0-1,0-68000) fuzzy-if(webrender&&cocoaWidget,2-2,256-256) == 664127-1.xul 664127-1-ref.xul # Android: Intermittent failures - bug 1019131
+pref(layout.css.xul-tree-pseudos.content.enabled,true) fuzzy-if(d2d,0-1,0-256) skip-if(Android) fuzzy-if(skiaContent,0-1,0-68000) == 664127-1.xul 664127-1-ref.xul # Android: Intermittent failures - bug 1019131
 == 665597-1.html 665597-1-ref.html
 == 665597-2.html 665597-2-ref.html
 == 667079-1.html 667079-1-ref.html
 pref(layout.css.xul-tree-pseudos.content.enabled,true) == 668319-1.xul about:blank
 != 669015-1.xul 669015-1-notref.xul
 skip-if(azureSkiaGL) == 670442-1.html 670442-1-ref.html
 == 670467-1.html 670467-1-ref.html
 == 670467-2.html 670467-2-ref.html
--- a/layout/reftests/xul/reftest.list
+++ b/layout/reftests/xul/reftest.list
@@ -2,17 +2,17 @@
 
 == menuitem-key.xul menuitem-key-ref.xul
 # these random-if(Android) are due to differences between Android Native & Xul, see bug 732569
 random-if(Android) == menulist-shrinkwrap-1.xul menulist-shrinkwrap-1-ref.xul
 random-if(Android) == menulist-shrinkwrap-2.xul menulist-shrinkwrap-2-ref.xul
 == textbox-overflow-1.xul textbox-overflow-1-ref.xul # for bug 749658
 # accesskeys are not normally displayed on Mac, so skip this test
 skip-if(cocoaWidget) == accesskey.xul accesskey-ref.xul
-pref(layout.css.xul-tree-pseudos.content.enabled,true) fails-if(cocoaWidget) fuzzy-if(xulRuntime.widgetToolkit=="gtk3",0-1,0-11) == tree-row-outline-1.xul tree-row-outline-1-ref.xul # win8: bug 1254832
+pref(layout.css.xul-tree-pseudos.content.enabled,true) fuzzy-if(xulRuntime.widgetToolkit=="gtk3",0-1,0-11) == tree-row-outline-1.xul tree-row-outline-1-ref.xul # win8: bug 1254832
 skip-if(!cocoaWidget) fails-if(webrender&&cocoaWidget) == mac-tab-toolbar.xul mac-tab-toolbar-ref.xul
 pref(layout.css.xul-tree-pseudos.content.enabled,true) != tree-row-outline-1.xul tree-row-outline-1-notref.xul
 == text-crop.xul text-crop-ref.xul
 random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) == text-small-caps-1.xul text-small-caps-1-ref.xul
 fuzzy-if(skiaContent,0-1,0-60) fuzzy-if(cocoaWidget&&browserIsRemote&&!skiaContent,0-1,0-31) fuzzy-if(winWidget&&browserIsRemote&&layersGPUAccelerated,0-1,0-50) == inactive-fixed-bg-bug1205630.xul inactive-fixed-bg-bug1205630-ref.html
 fuzzy-if(skiaContent,0-1,0-60) fuzzy-if(cocoaWidget&&browserIsRemote&&!skiaContent,0-1,0-31) fuzzy-if(winWidget&&browserIsRemote&&layersGPUAccelerated,0-1,0-50) == inactive-fixed-bg-bug1272525.xul inactive-fixed-bg-bug1272525-ref.html
 
 # Tests for XUL <image> with 'object-fit' & 'object-position':
--- a/mobile/android/app/ua-update.json.in
+++ b/mobile/android/app/ua-update.json.in
@@ -11,10 +11,57 @@
   "lohaco.jp": "Mozilla/5.0 (Linux; Android 5.0.2; Galaxy Nexus Build/IMM76B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.93 Mobile Safari/537.36",
   // bug 1177298, www.nhk.or.jp
   "nhk.or.jp": "\\)\\s# AppleWebKit ",
   // bug 1177298, uniqlo.com
   "uniqlo.com": "\\)\\s#) Mobile Safari ",
   // bug 1338260, directv.com
   "directv.com": "Mozilla/5.0 (Linux; Android 6.0.1; SM-G920F Build/MMB29K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.91 Mobile Safari/537.36",
   // bug 1385206, rakuten.co.jp
-  "rakuten.co.jp": "Firefox.+$#"
+  "rakuten.co.jp": "Firefox.+$#",
+  // bug 1483233, ebay.com, m.ebay.com, and localized versions
+  "ebay.at": "Mozilla/5.0 (Linux; Android 6.0.1; SM-G920F Build/MMB29K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.91 Mobile Safari/537.36",
+  "m.ebay.at": "Mozilla/5.0 (Linux; Android 6.0.1; SM-G920F Build/MMB29K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.91 Mobile Safari/537.36",
+  "ebay.be": "Mozilla/5.0 (Linux; Android 6.0.1; SM-G920F Build/MMB29K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.91 Mobile Safari/537.36",
+  "m.ebay.be": "Mozilla/5.0 (Linux; Android 6.0.1; SM-G920F Build/MMB29K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.91 Mobile Safari/537.36",
+  "ebay.ca": "Mozilla/5.0 (Linux; Android 6.0.1; SM-G920F Build/MMB29K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.91 Mobile Safari/537.36",
+  "m.ebay.ca": "Mozilla/5.0 (Linux; Android 6.0.1; SM-G920F Build/MMB29K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.91 Mobile Safari/537.36",
+  "ebay.ch": "Mozilla/5.0 (Linux; Android 6.0.1; SM-G920F Build/MMB29K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.91 Mobile Safari/537.36",
+  "m.ebay.ch": "Mozilla/5.0 (Linux; Android 6.0.1; SM-G920F Build/MMB29K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.91 Mobile Safari/537.36",
+  "ebay.cn": "Mozilla/5.0 (Linux; Android 6.0.1; SM-G920F Build/MMB29K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.91 Mobile Safari/537.36",
+  "m.ebay.cn": "Mozilla/5.0 (Linux; Android 6.0.1; SM-G920F Build/MMB29K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.91 Mobile Safari/537.36",
+  "ebay.co.th": "Mozilla/5.0 (Linux; Android 6.0.1; SM-G920F Build/MMB29K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.91 Mobile Safari/537.36",
+  "m.ebay.co.th": "Mozilla/5.0 (Linux; Android 6.0.1; SM-G920F Build/MMB29K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.91 Mobile Safari/537.36",
+  "ebay.co.uk": "Mozilla/5.0 (Linux; Android 6.0.1; SM-G920F Build/MMB29K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.91 Mobile Safari/537.36",
+  "m.ebay.co.uk": "Mozilla/5.0 (Linux; Android 6.0.1; SM-G920F Build/MMB29K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.91 Mobile Safari/537.36",
+  "ebay.com": "Mozilla/5.0 (Linux; Android 6.0.1; SM-G920F Build/MMB29K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.91 Mobile Safari/537.36",
+  "m.ebay.com": "Mozilla/5.0 (Linux; Android 6.0.1; SM-G920F Build/MMB29K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.91 Mobile Safari/537.36",
+  "ebay.com.au": "Mozilla/5.0 (Linux; Android 6.0.1; SM-G920F Build/MMB29K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.91 Mobile Safari/537.36",
+  "m.ebay.com.au": "Mozilla/5.0 (Linux; Android 6.0.1; SM-G920F Build/MMB29K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.91 Mobile Safari/537.36",
+  "ebay.com.hk": "Mozilla/5.0 (Linux; Android 6.0.1; SM-G920F Build/MMB29K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.91 Mobile Safari/537.36",
+  "m.ebay.com.hk": "Mozilla/5.0 (Linux; Android 6.0.1; SM-G920F Build/MMB29K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.91 Mobile Safari/537.36",
+  "ebay.com.my": "Mozilla/5.0 (Linux; Android 6.0.1; SM-G920F Build/MMB29K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.91 Mobile Safari/537.36",
+  "m.ebay.com.my": "Mozilla/5.0 (Linux; Android 6.0.1; SM-G920F Build/MMB29K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.91 Mobile Safari/537.36",
+  "ebay.com.sg": "Mozilla/5.0 (Linux; Android 6.0.1; SM-G920F Build/MMB29K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.91 Mobile Safari/537.36",
+  "m.ebay.com.sg": "Mozilla/5.0 (Linux; Android 6.0.1; SM-G920F Build/MMB29K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.91 Mobile Safari/537.36",
+  "ebay.com.tw": "Mozilla/5.0 (Linux; Android 6.0.1; SM-G920F Build/MMB29K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.91 Mobile Safari/537.36",
+  "m.ebay.com.tw": "Mozilla/5.0 (Linux; Android 6.0.1; SM-G920F Build/MMB29K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.91 Mobile Safari/537.36",
+  "ebay.de": "Mozilla/5.0 (Linux; Android 6.0.1; SM-G920F Build/MMB29K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.91 Mobile Safari/537.36",
+  "m.ebay.de": "Mozilla/5.0 (Linux; Android 6.0.1; SM-G920F Build/MMB29K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.91 Mobile Safari/537.36",
+  "ebay.es": "Mozilla/5.0 (Linux; Android 6.0.1; SM-G920F Build/MMB29K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.91 Mobile Safari/537.36",
+  "m.ebay.es": "Mozilla/5.0 (Linux; Android 6.0.1; SM-G920F Build/MMB29K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.91 Mobile Safari/537.36",
+  "ebay.fr": "Mozilla/5.0 (Linux; Android 6.0.1; SM-G920F Build/MMB29K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.91 Mobile Safari/537.36",
+  "m.ebay.fr": "Mozilla/5.0 (Linux; Android 6.0.1; SM-G920F Build/MMB29K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.91 Mobile Safari/537.36",
+  "ebay.ie": "Mozilla/5.0 (Linux; Android 6.0.1; SM-G920F Build/MMB29K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.91 Mobile Safari/537.36",
+  "m.ebay.ie": "Mozilla/5.0 (Linux; Android 6.0.1; SM-G920F Build/MMB29K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.91 Mobile Safari/537.36",
+  "ebay.in": "Mozilla/5.0 (Linux; Android 6.0.1; SM-G920F Build/MMB29K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.91 Mobile Safari/537.36",
+  "m.ebay.in": "Mozilla/5.0 (Linux; Android 6.0.1; SM-G920F Build/MMB29K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.91 Mobile Safari/537.36",
+  "ebay.it": "Mozilla/5.0 (Linux; Android 6.0.1; SM-G920F Build/MMB29K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.91 Mobile Safari/537.36",
+  "m.ebay.it": "Mozilla/5.0 (Linux; Android 6.0.1; SM-G920F Build/MMB29K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.91 Mobile Safari/537.36",
+  "ebay.nl": "Mozilla/5.0 (Linux; Android 6.0.1; SM-G920F Build/MMB29K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.91 Mobile Safari/537.36",
+  "m.ebay.nl": "Mozilla/5.0 (Linux; Android 6.0.1; SM-G920F Build/MMB29K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.91 Mobile Safari/537.36",
+  "ebay.ph": "Mozilla/5.0 (Linux; Android 6.0.1; SM-G920F Build/MMB29K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.91 Mobile Safari/537.36",
+  "m.ebay.ph": "Mozilla/5.0 (Linux; Android 6.0.1; SM-G920F Build/MMB29K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.91 Mobile Safari/537.36",
+  "ebay.pl": "Mozilla/5.0 (Linux; Android 6.0.1; SM-G920F Build/MMB29K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.91 Mobile Safari/537.36",
+  "m.ebay.pl": "Mozilla/5.0 (Linux; Android 6.0.1; SM-G920F Build/MMB29K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.91 Mobile Safari/537.36",
+  "ebay.vn": "Mozilla/5.0 (Linux; Android 6.0.1; SM-G920F Build/MMB29K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.91 Mobile Safari/537.36",
+  "m.ebay.vn": "Mozilla/5.0 (Linux; Android 6.0.1; SM-G920F Build/MMB29K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.91 Mobile Safari/537.36"
 }
--- a/mobile/android/base/java/org/mozilla/gecko/media/GeckoMediaControlAgent.java
+++ b/mobile/android/base/java/org/mozilla/gecko/media/GeckoMediaControlAgent.java
@@ -413,17 +413,17 @@ public class GeckoMediaControlAgent {
                 .addAction(createNotificationAction())
                 .setOngoing(mediaNotification.isOnGoing())
                 .setShowWhen(false)
                 .setWhen(0)
                 .setVisibility(mediaNotification.getVisibility());
 
         if (!AppConstants.Versions.preO) {
             notificationBuilder.setChannelId(NotificationHelper.getInstance(mContext)
-                    .getNotificationChannel(NotificationHelper.Channel.DEFAULT).getId());
+                    .getNotificationChannel(NotificationHelper.Channel.MEDIA).getId());
         }
 
         return notificationBuilder.build();
     }
 
     private Notification.Action createNotificationAction() {
         final Intent intent = createIntentUponState(sMediaState);
         boolean isPlayAction = intent.getAction().equals(ACTION_RESUME);
--- a/mobile/android/base/java/org/mozilla/gecko/notifications/NotificationHelper.java
+++ b/mobile/android/base/java/org/mozilla/gecko/notifications/NotificationHelper.java
@@ -92,28 +92,36 @@ public final class NotificationHelper im
         DEFAULT,
         /**
          * Mozilla Location Services notification channel.
          */
         MLS,
         /**
          * Mozilla Location Services notification channel.
          */
-        DOWNLOAD
+        DOWNLOAD,
+        /**
+         *  Media notification channel
+         */
+        MEDIA
     }
 
     private final Map<Channel, String> mDefinedNotificationChannels = new HashMap<Channel, String>() {{
         final String DEFAULT_CHANNEL_TAG = "default-notification-channel";
         put(Channel.DEFAULT, DEFAULT_CHANNEL_TAG);
 
         final String MLS_CHANNEL_TAG     = "mls-notification-channel";
         put(Channel.MLS, MLS_CHANNEL_TAG);
 
         final String DOWNLOAD_NOTIFICATION_TAG = "download-notification-channel";
         put(Channel.DOWNLOAD, DOWNLOAD_NOTIFICATION_TAG);
+
+
+        final String MEDIA_CHANNEL_TAG = "media-notification-channel";
+        put(Channel.MEDIA, MEDIA_CHANNEL_TAG);
     }};
 
     // Holds a list of notifications that should be cleared if the Fennec Activity is shut down.
     // Will not include ongoing or persistent notifications that are tied to Gecko's lifecycle.
     private SimpleArrayMap<String, GeckoBundle> mClearableNotifications;
 
     private boolean mInitialized;
     private static NotificationHelper sInstance;
@@ -171,16 +179,22 @@ public final class NotificationHelper im
                 break;
 
                 case DOWNLOAD: {
                     channel = new NotificationChannel(mDefinedNotificationChannels.get(definedChannel),
                             mContext.getString(R.string.download_notification_channel), NotificationManager.IMPORTANCE_LOW);
                 }
                 break;
 
+                case MEDIA: {
+                    channel = new NotificationChannel(mDefinedNotificationChannels.get(definedChannel),
+                            mContext.getString(R.string.media_notification_channel), NotificationManager.IMPORTANCE_LOW);
+                }
+                break;
+
                 case DEFAULT:
 
                 default: {
                     channel = new NotificationChannel(mDefinedNotificationChannels.get(definedChannel),
                             mContext.getString(R.string.default_notification_channel), NotificationManager.IMPORTANCE_HIGH);
                 }
                 break;
             }
--- a/mobile/android/base/locales/en-US/android_strings.dtd
+++ b/mobile/android/base/locales/en-US/android_strings.dtd
@@ -892,9 +892,10 @@ Picture-in-picture mini window -->
 <!ENTITY pip_play_button_title "Play">
 <!ENTITY pip_play_button_description "Resume playing">
 <!ENTITY pip_pause_button_title "Pause">
 <!ENTITY pip_pause_button_description "Pause playing">
 
 <!-- Notification channels names -->
 <!ENTITY default_notification_channel "&brandShortName;">
 <!ENTITY mls_notification_channel "&vendorShortName; Location Service">
-<!ENTITY download_notification_channel "Downloads">
\ No newline at end of file
+<!ENTITY download_notification_channel "Downloads">
+<!ENTITY media_notification_channel "Media playback">
\ No newline at end of file
--- a/mobile/android/base/strings.xml.in
+++ b/mobile/android/base/strings.xml.in
@@ -645,10 +645,11 @@
 
   <string name="pip_play_button_title">&pip_play_button_title;</string>
   <string name="pip_play_button_description">&pip_play_button_description;</string>
   <string name="pip_pause_button_title">&pip_pause_button_title;</string>
   <string name="pip_pause_button_description">&pip_pause_button_description;</string>
 
   <string name="default_notification_channel">&default_notification_channel;</string>
   <string name="mls_notification_channel">&mls_notification_channel;</string>
+  <string name="media_notification_channel">&media_notification_channel;</string>
   <string name="download_notification_channel">&download_notification_channel;</string>
 </resources>
--- a/servo/support/gecko/nsstring/src/conversions.rs
+++ b/servo/support/gecko/nsstring/src/conversions.rs
@@ -37,40 +37,42 @@ fn plus_one(a: usize) -> Option<usize> {
 /// https://stackoverflow.com/questions/14707803/line-size-of-l1-and-l2-caches
 ///
 /// For consistent behavior, not trying to use 128 on aarch64
 /// or other fanciness like that.
 const CACHE_LINE: usize = 64;
 
 const CACHE_LINE_MASK: usize = CACHE_LINE - 1;
 
+/// Returns true if the string is both longer than a cache line
+/// and the first cache line is ASCII.
 #[inline(always)]
-fn starts_with_ascii(buffer: &[u8]) -> bool {
+fn long_string_starts_with_ascii(buffer: &[u8]) -> bool {
     // We examine data only up to the end of the cache line
     // to make this check minimally disruptive.
-    let bound = if buffer.len() <= CACHE_LINE {
-        buffer.len()
-    } else {
-        CACHE_LINE - ((buffer.as_ptr() as usize) & CACHE_LINE_MASK)
-    };
+    if buffer.len() <= CACHE_LINE {
+        return false;
+    }
+    let bound = CACHE_LINE - ((buffer.as_ptr() as usize) & CACHE_LINE_MASK);
     is_ascii(&buffer[..bound])
 }
 
+/// Returns true if the string is both longer than two cache lines
+/// and the first two cache lines are Basic Latin.
 #[inline(always)]
-fn starts_with_basic_latin(buffer: &[u16]) -> bool {
+fn long_string_stars_with_basic_latin(buffer: &[u16]) -> bool {
     // We look at two cache lines with code unit size of two. There is need
     // to look at more than one cache line in the UTF-16 case, because looking
     // at just one cache line wouldn't catch non-ASCII Latin with high enough
     // probability with Latin-script languages that have relatively infrequent
     // non-ASCII characters.
-    let bound = if buffer.len() <= CACHE_LINE {
-        buffer.len()
-    } else {
-        (CACHE_LINE * 2 - ((buffer.as_ptr() as usize) & CACHE_LINE_MASK)) / 2
-    };
+    if buffer.len() <= CACHE_LINE {
+        return false;
+    }
+    let bound = (CACHE_LINE * 2 - ((buffer.as_ptr() as usize) & CACHE_LINE_MASK)) / 2;
     is_basic_latin(&buffer[..bound])
 }
 
 // Ignoring the copy avoidance complications of conversions between Latin1 and
 // UTF-8, a conversion function has the outward form of
 // `fn F(&mut self, other: &[T], old_len: usize) -> Result<BulkWriteOk, ()>`,
 // where `T` is either `u8` or `u16`. `other` is the slice whose converted
 // content are to be appended to `self` and `old_len` indicates how many
@@ -113,17 +115,18 @@ macro_rules! shrinking_conversion {
      other_ty = $other_ty:ty,
      math = $math:ident) => (
         fn $name(&mut self, other: $other_ty, old_len: usize) -> Result<BulkWriteOk, ()> {
             let needed = $math(other.len()).ok_or(())?;
             let mut handle = unsafe {
                 self.bulk_write(old_len.checked_add(needed).ok_or(())?, old_len, false)?
             };
             let written = $convert(other, &mut handle.as_mut_slice()[old_len..]);
-            Ok(handle.finish(old_len + written, true))
+            let new_len = old_len + written;
+            Ok(handle.finish(new_len, new_len > CACHE_LINE))
         }
      )
 }
 
 /// A conversion where the number of code units in the output is always equal
 /// to the number of code units in the input.
 ///
 /// Takes the name of the method to be generated, the name of the conversion
@@ -308,36 +311,53 @@ impl nsAString {
 impl nsACString {
     // UTF-16 to UTF-8
 
     fn fallible_append_utf16_to_utf8_impl(
         &mut self,
         other: &[u16],
         old_len: usize,
     ) -> Result<BulkWriteOk, ()> {
-        // We first size the buffer for ASCII if the first cache line is ASCII. If that turns out not to
-        // be enough, we size for the worst case given the length of the remaining input at that point.
-        let (filled, num_ascii, mut handle) = if starts_with_basic_latin(other) {
+        // We first size the buffer for ASCII if the first two cache lines are ASCII. If that turns out
+        // not to be enough, we size for the worst case given the length of the remaining input at that
+        // point. BUT if the worst case fits inside the inline capacity of an autostring, we skip
+        // the ASCII stuff.
+        let worst_case_needed = if let Some(inline_capacity) = self.inline_capacity() {
+            let worst_case = times_three_plus_one(other.len()).ok_or(())?;
+            if worst_case <= inline_capacity {
+                Some(worst_case)
+            } else {
+                None
+            }
+        } else {
+            None
+        };
+        let (filled, num_ascii, mut handle) = if worst_case_needed.is_none() &&
+                                                 long_string_stars_with_basic_latin(other) {
             let new_len_with_ascii = old_len.checked_add(other.len()).ok_or(())?;
             let mut handle = unsafe { self.bulk_write(new_len_with_ascii, old_len, false)? };
             let num_ascii = copy_basic_latin_to_ascii(other, &mut handle.as_mut_slice()[old_len..]);
             let left = other.len() - num_ascii;
             if left == 0 {
                 return Ok(handle.finish(old_len + num_ascii, true));
             }
             let filled = old_len + num_ascii;
             let needed = times_three_plus_one(left).ok_or(())?;
             let new_len = filled.checked_add(needed).ok_or(())?;
             unsafe {
                 handle.restart_bulk_write(new_len, filled, false)?;
             }
             (filled, num_ascii, handle)
         } else {
             // Started with non-ASCII. Compute worst case
-            let needed = times_three_plus_one(other.len()).ok_or(())?;
+            let needed = if let Some(n) = worst_case_needed {
+                n
+            } else {
+                times_three_plus_one(other.len()).ok_or(())?
+            };
             let new_len = old_len.checked_add(needed).ok_or(())?;
             let mut handle = unsafe { self.bulk_write(new_len, old_len, false)? };
             (old_len, 0, handle)
         };
         let written =
             convert_utf16_to_utf8(&other[num_ascii..], &mut handle.as_mut_slice()[filled..]);
         Ok(handle.finish(filled + written, true))
     }
@@ -563,41 +583,57 @@ impl nsACString {
             let filled = old_len + num_ascii;
             let needed = left.checked_mul(2).ok_or(())?;
             let new_len = filled.checked_add(needed).ok_or(())?;
             let mut handle = unsafe { self.bulk_write(new_len, old_len, false)? };
             if num_ascii != 0 {
                 (&mut handle.as_mut_slice()[old_len..filled]).copy_from_slice(&other[..num_ascii]);
             }
             (filled, num_ascii, handle)
-        } else if starts_with_ascii(other) {
-            // Wrapper didn't check for ASCII, so let's see if `other` starts with ASCII
-            // `other` starts with ASCII, so let's first size the buffer
-            // with optimism that it's ASCII-only.
-            let new_len_with_ascii = old_len.checked_add(other.len()).ok_or(())?;
-            let mut handle = unsafe { self.bulk_write(new_len_with_ascii, old_len, false)? };
-            let num_ascii = copy_ascii_to_ascii(other, &mut handle.as_mut_slice()[old_len..]);
-            let left = other.len() - num_ascii;
-            let filled = old_len + num_ascii;
-            if left == 0 {
-                // `other` was all ASCII
-                return Ok(handle.finish(filled, true));
+        } else {
+            let worst_case_needed = if let Some(inline_capacity) = self.inline_capacity() {
+                let worst_case = other.len().checked_mul(2).ok_or(())?;
+                if worst_case <= inline_capacity {
+                    Some(worst_case)
+                } else {
+                    None
+                }
+            } else {
+                None
+            };
+            if worst_case_needed.is_none() && long_string_starts_with_ascii(other) {
+                // Wrapper didn't check for ASCII, so let's see if `other` starts with ASCII
+                // `other` starts with ASCII, so let's first size the buffer
+                // with optimism that it's ASCII-only.
+                let new_len_with_ascii = old_len.checked_add(other.len()).ok_or(())?;
+                let mut handle = unsafe { self.bulk_write(new_len_with_ascii, old_len, false)? };
+                let num_ascii = copy_ascii_to_ascii(other, &mut handle.as_mut_slice()[old_len..]);
+                let left = other.len() - num_ascii;
+                let filled = old_len + num_ascii;
+                if left == 0 {
+                    // `other` was all ASCII
+                    return Ok(handle.finish(filled, true));
+                }
+                let needed = left.checked_mul(2).ok_or(())?;
+                let new_len = filled.checked_add(needed).ok_or(())?;
+                unsafe {
+                    handle.restart_bulk_write(new_len, filled, false)?;
+                }
+                (filled, num_ascii, handle)
+            } else {
+                // Started with non-ASCII. Assume worst case.
+                let needed = if let Some(n) = worst_case_needed {
+                    n
+                } else {
+                    other.len().checked_mul(2).ok_or(())?
+                };
+                let new_len = old_len.checked_add(needed).ok_or(())?;
+                let mut handle = unsafe { self.bulk_write(new_len, old_len, false)? };
+                (old_len, 0, handle)
             }
-            let needed = left.checked_mul(2).ok_or(())?;
-            let new_len = filled.checked_add(needed).ok_or(())?;
-            unsafe {
-                handle.restart_bulk_write(new_len, filled, false)?;
-            }
-            (filled, num_ascii, handle)
-        } else {
-            // Started with non-ASCII. Assume worst case.
-            let needed = other.len().checked_mul(2).ok_or(())?;
-            let new_len = old_len.checked_add(needed).ok_or(())?;
-            let mut handle = unsafe { self.bulk_write(new_len, old_len, false)? };
-            (old_len, 0, handle)
         };
         let written =
             convert_latin1_to_utf8(&other[num_ascii..], &mut handle.as_mut_slice()[filled..]);
         Ok(handle.finish(filled + written, true))
     }
 
     /// Convert a Latin1 (i.e. byte value equals scalar value; not windows-1252!)
     /// into UTF-8 and replace the content of this string with the conversion result.
--- a/servo/support/gecko/nsstring/src/lib.rs
+++ b/servo/support/gecko/nsstring/src/lib.rs
@@ -139,16 +139,21 @@ pub use self::conversions::nsstring_fall
 pub use self::conversions::nsstring_fallible_append_utf8_impl;
 
 /// A type for showing that `finish()` was called on a `BulkWriteHandle`.
 /// Instantiating this type from elsewhere is basically an assertion that
 /// there is no `BulkWriteHandle` around, so be very careful with instantiating
 /// this type!
 pub struct BulkWriteOk;
 
+/// Semi-arbitrary threshold below which we don't care about shrinking
+/// buffers to size. Currently matches `CACHE_LINE` in the `conversions`
+/// module.
+const SHRINKING_THRESHOLD: usize = 64;
+
 ///////////////////////////////////
 // Internal Implementation Flags //
 ///////////////////////////////////
 
 mod data_flags {
     bitflags! {
         // While this has the same layout as u16, it cannot be passed
         // over FFI safely as a u16.
@@ -331,16 +336,17 @@ macro_rules! define_string_types {
         AString = $AString: ident;
         String = $String: ident;
         Str = $Str: ident;
 
         StringLike = $StringLike: ident;
         StringAdapter = $StringAdapter: ident;
 
         StringRepr = $StringRepr: ident;
+        AutoStringRepr = $AutoStringRepr: ident;
 
         BulkWriteHandle = $BulkWriteHandle: ident;
 
         drop = $drop: ident;
         assign = $assign: ident, $fallible_assign: ident;
         take_from = $take_from: ident, $fallible_take_from: ident;
         append = $append: ident, $fallible_append: ident;
         set_length = $set_length: ident, $fallible_set_length: ident;
@@ -390,16 +396,23 @@ macro_rules! define_string_types {
         impl DerefMut for $StringRepr {
             fn deref_mut(&mut self) -> &mut $AString {
                 unsafe {
                     mem::transmute(self)
                 }
             }
         }
 
+        #[repr(C)]
+        #[derive(Debug)]
+        pub struct $AutoStringRepr {
+            super_repr: $StringRepr,
+            inline_capacity: u32,
+        }
+
         pub struct $BulkWriteHandle<'a> {
             string: &'a mut $AString,
             capacity: usize,
         }
 
         impl<'a> $BulkWriteHandle<'a> {
             fn new(string: &'a mut $AString, capacity: usize) -> Self {
                 $BulkWriteHandle{ string: string, capacity: capacity }
@@ -425,17 +438,17 @@ macro_rules! define_string_types {
                 assert!(length <= self.capacity);
                 if length == 0 {
                     // `truncate()` is OK even when the string
                     // is in invalid state.
                     self.string.truncate();
                     mem::forget(self); // Don't run the failure path in drop()
                     return BulkWriteOk{};
                 }
-                if allow_shrinking {
+                if allow_shrinking && length > SHRINKING_THRESHOLD {
                     unsafe {
                         let _ = self.restart_bulk_write(length, length, true);
                     }
                 }
                 unsafe {
                     let mut this = self.string.as_repr();
                     this.as_mut().length = length as u32;
                     *(this.as_mut().data.as_ptr().offset(length as isize)) = 0;
@@ -617,27 +630,48 @@ macro_rules! define_string_types {
                                             allow_shrinking: bool) -> Result<usize, ()> {
                 if capacity > u32::max_value() as usize {
                     Err(())
                 } else {
                     let capacity32 = capacity as u32;
                     let rounded = $start_bulk_write(self,
                                                     capacity32,
                                                     units_to_preserve as u32,
-                                                    allow_shrinking);
+                                                    allow_shrinking && capacity > SHRINKING_THRESHOLD);
                     if rounded == u32::max_value() {
                         return Err(())
                     }
                     Ok(rounded as usize)
                 }
             }
 
             fn as_repr(&mut self) -> ptr::NonNull<$StringRepr> {
                 unsafe { ptr::NonNull::new_unchecked(self as *mut _ as *mut $StringRepr)}
             }
+
+            /// If this is an autostring, returns the capacity (excluding the zero
+            /// terminator) of the inline buffer within `Some()`. Otherwise returns
+            /// `None`.
+            pub fn inline_capacity(&self) -> Option<usize> {
+                if unsafe {
+                    // All $AString values point to a struct prefix which is
+                    // identical to $StringRepr, this we can transmute `self`
+                    // into $StringRepr to get the reference to the underlying
+                    // data.
+                    let this: &$StringRepr = mem::transmute(self);
+                    this.classflags.contains(ClassFlags::INLINE)
+                } {
+                    unsafe {
+                        let this: &$AutoStringRepr = mem::transmute(self);
+                        Some(this.inline_capacity as usize)
+                    }
+                } else {
+                    None
+                }
+            }
         }
 
         impl Deref for $AString {
             type Target = [$char_t];
             fn deref(&self) -> &[$char_t] {
                 unsafe {
                     // All $AString values point to a struct prefix which is
                     // identical to $StringRepr, this we can transmute `self`
@@ -1011,16 +1045,17 @@ define_string_types! {
     AString = nsACString;
     String = nsCString;
     Str = nsCStr;
 
     StringLike = nsCStringLike;
     StringAdapter = nsCStringAdapter;
 
     StringRepr = nsCStringRepr;
+    AutoStringRepr = nsAutoCStringRepr;
 
     BulkWriteHandle = nsACStringBulkWriteHandle;
 
     drop = Gecko_FinalizeCString;
     assign = Gecko_AssignCString, Gecko_FallibleAssignCString;
     take_from = Gecko_TakeFromCString, Gecko_FallibleTakeFromCString;
     append = Gecko_AppendCString, Gecko_FallibleAppendCString;
     set_length = Gecko_SetLengthCString, Gecko_FallibleSetLengthCString;
@@ -1141,16 +1176,17 @@ define_string_types! {
     AString = nsAString;
     String = nsString;
     Str = nsStr;
 
     StringLike = nsStringLike;
     StringAdapter = nsStringAdapter;
 
     StringRepr = nsStringRepr;
+    AutoStringRepr = nsAutoStringRepr;
 
     BulkWriteHandle = nsAStringBulkWriteHandle;
 
     drop = Gecko_FinalizeString;
     assign = Gecko_AssignString, Gecko_FallibleAssignString;
     take_from = Gecko_TakeFromString, Gecko_FallibleTakeFromString;
     append = Gecko_AppendString, Gecko_FallibleAppendString;
     set_length = Gecko_SetLengthString, Gecko_FallibleSetLengthString;
@@ -1254,16 +1290,17 @@ extern "C" {
 
 pub mod test_helpers {
     //! This module only exists to help with ensuring that the layout of the
     //! structs inside of rust and C++ are identical.
     //!
     //! It is public to ensure that these testing functions are avaliable to
     //! gtest code.
 
+    use super::{nsACString, nsAString};
     use super::{nsCStr, nsCString, nsCStringRepr};
     use super::{nsStr, nsString, nsStringRepr};
     use super::{ClassFlags, DataFlags};
     use std::mem;
 
     /// Generates an #[no_mangle] extern "C" function which returns the size and
     /// alignment of the given type with the given name.
     macro_rules! size_align_check {
@@ -1395,9 +1432,21 @@ pub mod test_helpers {
             *f_refcounted = DataFlags::REFCOUNTED.bits();
             *f_owned = DataFlags::OWNED.bits();
             *f_inline = DataFlags::INLINE.bits();
             *f_literal = DataFlags::LITERAL.bits();
             *f_class_inline = ClassFlags::INLINE.bits();
             *f_class_null_terminated = ClassFlags::NULL_TERMINATED.bits();
         }
     }
+
+    #[no_mangle]
+    #[allow(non_snake_case)]
+    pub extern fn Rust_InlineCapacityFromRust(cstring: *const nsACString,
+                                              string: *const nsAString,
+                                              cstring_capacity: *mut usize,
+                                              string_capacity: *mut usize) {
+        unsafe {
+            *cstring_capacity = (*cstring).inline_capacity().unwrap();
+            *string_capacity = (*string).inline_capacity().unwrap();
+        }
+    }
 }
--- a/taskcluster/ci/test/raptor.yml
+++ b/taskcluster/ci/test/raptor.yml
@@ -216,8 +216,37 @@ raptor-unity-webgl-chrome:
     max-run-time: 3600
     mozharness:
         extra-options:
             - --test=raptor-unity-webgl
             - --app=chrome
     fetches:
         fetch:
             - unity-webgl
+
+raptor-assorted-dom-firefox:
+    description: "Raptor Assorted-Dom on Firefox"
+    try-name: raptor-assorted-dom-firefox
+    treeherder-symbol: Rap(dom)
+    run-on-projects: ['try', 'mozilla-central']
+    tier: 3
+    max-run-time: 1500
+    mozharness:
+        extra-options:
+            - --test=raptor-assorted-dom
+    fetches:
+        fetch:
+            - assorted-dom
+
+raptor-assorted-dom-chrome:
+    description: "Raptor Assorted-Dom on Chrome"
+    try-name: raptor-assorted-dom-chrome
+    treeherder-symbol: Rap-C(dom)
+    run-on-projects: ['try', 'mozilla-central']
+    tier: 3
+    max-run-time: 1500
+    mozharness:
+        extra-options:
+            - --test=raptor-assorted-dom
+            - --app=chrome
+    fetches:
+        fetch:
+            - assorted-dom
--- a/taskcluster/ci/test/test-sets.yml
+++ b/taskcluster/ci/test/test-sets.yml
@@ -85,26 +85,28 @@ raptor-firefox:
     - raptor-tp6-firefox
     - raptor-speedometer-firefox
     - raptor-stylebench-firefox
     - raptor-motionmark-htmlsuite-firefox
     - raptor-motionmark-animometer-firefox
     - raptor-webaudio-firefox
     - raptor-gdocs-firefox
     - raptor-sunspider-firefox
+    - raptor-assorted-dom-firefox
 
 raptor-chrome:
     - raptor-tp6-chrome
     - raptor-speedometer-chrome
     - raptor-stylebench-chrome
     - raptor-motionmark-htmlsuite-chrome
     - raptor-motionmark-animometer-chrome
     - raptor-webaudio-chrome
     - raptor-gdocs-chrome
     - raptor-sunspider-chrome
+    - raptor-assorted-dom-chrome
 
 # Fetch tasks are only supported on Linux for now,
 # so these need to be separate sets.
 raptor-fetch-firefox:
     - raptor-unity-webgl-firefox
 
 raptor-fetch-chrome:
     - raptor-unity-webgl-chrome
--- a/testing/raptor/mach_commands.py
+++ b/testing/raptor/mach_commands.py
@@ -6,28 +6,28 @@
 
 # Integrates raptor mozharness with mach
 
 from __future__ import absolute_import, print_function, unicode_literals
 
 import os
 import sys
 import json
+import shutil
 import socket
 import subprocess
 
+import mozfile
 from mach.decorators import CommandProvider, Command
 from mozboot.util import get_state_dir
 from mozbuild.base import MozbuildObject, MachCommandBase
-from mozpack.copier import FileCopier
-from mozpack.manifests import InstallManifest
 
 HERE = os.path.dirname(os.path.realpath(__file__))
 BENCHMARK_REPOSITORY = 'https://github.com/mozilla/perf-automation'
-BENCHMARK_REVISION = '6beb3d3e22abce8cf8e2e89bc45acd4152258f12'
+BENCHMARK_REVISION = '4befd28725c687b91ce749420eab29352ecbcab4'
 
 
 class RaptorRunner(MozbuildObject):
     def run_test(self, raptor_args):
         """
         We want to do couple of things before running raptor
         1. Clone mozharness
         2. Make config for raptor mozharness
@@ -69,34 +69,40 @@ class RaptorRunner(MozbuildObject):
         if not os.path.isdir(external_repo_path):
             subprocess.check_call(['git', 'clone', BENCHMARK_REPOSITORY, external_repo_path])
         else:
             subprocess.check_call(['git', 'checkout', 'master'], cwd=external_repo_path)
             subprocess.check_call(['git', 'pull'], cwd=external_repo_path)
 
         subprocess.check_call(['git', 'checkout', BENCHMARK_REVISION], cwd=external_repo_path)
 
-        # Link benchmarks to the objdir
+        # Link or copy benchmarks to the objdir
         benchmark_paths = (
             os.path.join(external_repo_path, 'benchmarks'),
             os.path.join(self.topsrcdir, 'third_party', 'webkit', 'PerformanceTests'),
         )
-        manifest = InstallManifest()
+
+        benchmark_dest = os.path.join(self.topobjdir, 'testing', 'raptor', 'benchmarks')
+        if not os.path.isdir(benchmark_dest):
+            os.makedirs(benchmark_dest)
 
         for benchmark_path in benchmark_paths:
-            for path in os.listdir(benchmark_path):
-                abspath = os.path.join(benchmark_path, path)
-                if not os.path.isdir(abspath) or path.startswith('.'):
+            for name in os.listdir(benchmark_path):
+                path = os.path.join(benchmark_path, name)
+                dest = os.path.join(benchmark_dest, name)
+                if not os.path.isdir(path) or name.startswith('.'):
                     continue
 
-                manifest.add_link(abspath, path)
-
-        copier = FileCopier()
-        manifest.populate_registry(copier)
-        copier.copy(os.path.join(self.topobjdir, 'testing', 'raptor', 'benchmarks'))
+                if hasattr(os, 'symlink'):
+                    if not os.path.exists(dest):
+                        os.symlink(path, dest)
+                else:
+                    # Clobber the benchmark in case a recent update removed any files.
+                    mozfile.remove(dest)
+                    shutil.copytree(path, dest)
 
     def make_config(self):
         default_actions = ['populate-webroot', 'install-chrome', 'create-virtualenv', 'run-tests']
         self.config = {
             'run_local': True,
             'binary_path': self.binary_path,
             'repo_path': self.topsrcdir,
             'raptor_path': self.raptor_dir,
--- a/testing/raptor/raptor/output.py
+++ b/testing/raptor/raptor/output.py
@@ -92,16 +92,18 @@ class Output(object):
                 elif 'motionmark' in test.measurements:
                     subtests, vals = self.parseMotionmarkOutput(test)
                 elif 'sunspider' in test.measurements:
                     subtests, vals = self.parseSunspiderOutput(test)
                 elif 'webaudio' in test.measurements:
                     subtests, vals = self.parseWebaudioOutput(test)
                 elif 'unity-webgl' in test.measurements:
                     subtests, vals = self.parseUnityWebGLOutput(test)
+                elif 'assorted-dom' in test.measurements:
+                    subtests, vals = self.parseAssortedDomOutput(test)
                 suite['subtests'] = subtests
 
             else:
                 LOG.error("output.summarize received unsupported test results type")
                 return
 
             # for pageload tests, if there are > 1 subtests here, that means there
             # were multiple measurements captured in each single pageload; we want
@@ -337,16 +339,57 @@ class Output(object):
         names.sort(reverse=True)
         for name in names:
             _subtests[name]['value'] = filter.median(_subtests[name]['replicates'])
             subtests.append(_subtests[name])
             vals.append([_subtests[name]['value'], name])
 
         return subtests, vals
 
+    def parseAssortedDomOutput(self, test):
+        # each benchmark 'index' becomes a subtest; each pagecycle / iteration
+        # of the test has multiple values
+
+        # this is the format we receive the results in from the benchmark
+        # i.e. this is ONE pagecycle of assorted-dom ('test' is a valid subtest name btw):
+
+        # {u'worker-getname-performance-getter': 5.9, u'window-getname-performance-getter': 6.1,
+        # u'window-getprop-performance-getter': 6.1, u'worker-getprop-performance-getter': 6.1,
+        # u'test': 5.8, u'total': 30}
+
+        # the 'total' is provided for us from the benchmark; the overall score will be the mean of
+        # the totals from all pagecycles; but keep all the subtest values for the logs/json
+
+        _subtests = {}
+        data = test.measurements['assorted-dom']
+        for pagecycle in data:
+            for _sub, _value in pagecycle[0].iteritems():
+                # build a list of subtests and append all related replicates
+                if _sub not in _subtests.keys():
+                    # subtest not added yet, first pagecycle, so add new one
+                    _subtests[_sub] = {'unit': test.unit,
+                                       'alertThreshold': float(test.alert_threshold),
+                                       'lowerIsBetter': test.lower_is_better,
+                                       'name': _sub,
+                                       'replicates': []}
+                _subtests[_sub]['replicates'].extend([_value])
+
+        vals = []
+        subtests = []
+        names = _subtests.keys()
+        names.sort(reverse=True)
+        for name in names:
+            _subtests[name]['value'] = round(filter.median(_subtests[name]['replicates']), 2)
+            subtests.append(_subtests[name])
+            # only use the 'total's to compute the overall result
+            if name == 'total':
+                vals.append([_subtests[name]['value'], name])
+
+        return subtests, vals
+
     def output(self):
         """output to file and perfherder data json """
         if self.summarized_results == {}:
             LOG.error("error: no summarized raptor results found!")
             return False
 
         if os.environ['MOZ_UPLOAD_DIR']:
             # i.e. testing/mozharness/build/raptor.json locally; in production it will
@@ -472,16 +515,21 @@ class Output(object):
         score = 60 * 1000 / filter.geometric_mean(results) / correctionFactor
         return score
 
     @classmethod
     def sunspider_score(cls, val_list):
         results = [i for i, j in val_list]
         return sum(results)
 
+    @classmethod
+    def assorted_dom_score(cls, val_list):
+        results = [i for i, j in val_list]
+        return round(filter.geometric_mean(results), 2)
+
     def construct_summary(self, vals, testname):
         if testname.startswith('raptor-v8_7'):
             return self.v8_Metric(vals)
         elif testname.startswith('raptor-kraken'):
             return self.JS_Metric(vals)
         elif testname.startswith('raptor-jetstream'):
             return self.benchmark_score(vals)
         elif testname.startswith('raptor-speedometer'):
@@ -489,12 +537,14 @@ class Output(object):
         elif testname.startswith('raptor-stylebench'):
             return self.stylebench_score(vals)
         elif testname.startswith('raptor-sunspider'):
             return self.sunspider_score(vals)
         elif testname.startswith('raptor-unity-webgl'):
             return self.unity_webgl_score(vals)
         elif testname.startswith('raptor-webaudio'):
             return self.webaudio_score(vals)
+        elif testname.startswith('raptor-assorted-dom'):
+            return self.assorted_dom_score(vals)
         elif len(vals) > 1:
             return round(filter.geometric_mean([i for i, j in vals]), 2)
         else:
             return round(filter.mean([i for i, j in vals]), 2)
--- a/testing/raptor/raptor/raptor.ini
+++ b/testing/raptor/raptor/raptor.ini
@@ -3,8 +3,9 @@
 [include:tests/raptor-speedometer.ini]
 [include:tests/raptor-stylebench.ini]
 [include:tests/raptor-sunspider.ini]
 [include:tests/raptor-motionmark-htmlsuite.ini]
 [include:tests/raptor-motionmark-animometer.ini]
 [include:tests/raptor-unity-webgl.ini]
 [include:tests/raptor-webaudio.ini]
 [include:tests/raptor-gdocs.ini]
+[include:tests/raptor-assorted-dom.ini]
new file mode 100644
--- /dev/null
+++ b/testing/raptor/raptor/tests/raptor-assorted-dom.ini
@@ -0,0 +1,14 @@
+[DEFAULT]
+type =  benchmark
+test_url = http://localhost:<port>/assorted-dom/assorted/driver.html?raptor
+page_cycles = 10
+page_timeout = 60000
+unit = ms
+lower_is_better = true
+alert_threshold = 2.0
+
+[raptor-assorted-dom-firefox]
+apps = firefox
+
+[raptor-assorted-dom-chrome]
+apps = chrome
--- a/testing/raptor/webext/raptor/manifest.json
+++ b/testing/raptor/webext/raptor/manifest.json
@@ -20,17 +20,18 @@
       "js": ["measure.js"]
     },
     {
       "matches": ["*://*/Speedometer/index.html*",
                   "*://*/StyleBench/*",
                   "*://*/MotionMark/*",
                   "*://*/SunSpider/*",
                   "*://*/webaudio/*",
-                  "*://*/unity-webgl/index.html*"],
+                  "*://*/unity-webgl/index.html*",
+                  "*://*/assorted-dom/assorted/results.html*"],
       "js": ["benchmark-relay.js"]
     }
   ],
   "permissions": [
     "<all_urls>",
     "tabs",
     "storage",
     "alarms"
--- a/testing/web-platform/meta/MANIFEST.json
+++ b/testing/web-platform/meta/MANIFEST.json
@@ -338320,16 +338320,22 @@
     ]
    ],
    "custom-elements/pseudo-class-defined.html": [
     [
      "/custom-elements/pseudo-class-defined.html",
      {}
     ]
    ],
+   "custom-elements/range-and-constructors.html": [
+    [
+     "/custom-elements/range-and-constructors.html",
+     {}
+    ]
+   ],
    "custom-elements/reaction-timing.html": [
     [
      "/custom-elements/reaction-timing.html",
      {}
     ]
    ],
    "custom-elements/reactions/Attr.html": [
     [
@@ -578379,16 +578385,20 @@
   "custom-elements/parser/serializing-html-fragments.html": [
    "6992dd6df6a1b9b75e84d13c8bfc6857b12cee54",
    "testharness"
   ],
   "custom-elements/pseudo-class-defined.html": [
    "60d88cffb517c0062db338e5ba89e98f7748c280",
    "testharness"
   ],
+  "custom-elements/range-and-constructors.html": [
+   "d17c3b71480aa9dd2a75ccbc2cccfd362060eb3c",
+   "testharness"
+  ],
   "custom-elements/reaction-timing.html": [
    "9e5bafbedfec42d28eb94c95ed84396941bc61ac",
    "testharness"
   ],
   "custom-elements/reactions/Attr.html": [
    "c9fa37f961159fffc19e8fdbe72df8b5686681a2",
    "testharness"
   ],
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/custom-elements/range-and-constructors.html
@@ -0,0 +1,61 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Custom elements: Range APIs should invoke constructor in tree order</title>
+<meta name="author" title="Edgar Chen" href="mailto:echen@mozilla.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/scripting.html#concept-upgrade-an-element">
+<link rel="help" href="https://dom.spec.whatwg.org/#concept-create-element">
+<line rel="help" href="https://dom.spec.whatwg.org/#concept-range-extract">
+<line rel="help" href="https://dom.spec.whatwg.org/#concept-range-clone">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<div id="log"></div>
+
+<c-e data-index="root">
+  <c-e data-index="root-0">
+    <c-e data-index="root-0-0">
+      <c-e data-index="root-0-0-0"></c-e>
+      <span id="start"></span>
+    </c-e>
+  </c-e>
+  <c-e data-index="root-1"></c-e>
+  <span id="end"></span>
+</c-e>
+
+<script>
+
+var logs = [];
+class CE extends HTMLElement {
+  constructor() {
+    super();
+    logs.push(this.dataset.index);
+  }
+}
+customElements.define('c-e', CE);
+
+function getRange() {
+  const range = new Range();
+  range.setStart(document.getElementById('start'), 0);
+  range.setEnd(document.getElementById('end'), 0);
+  return range;
+}
+
+test(function () {
+  // Clear log for testing.
+  logs = [];
+  getRange().cloneContents();
+  assert_array_equals(logs, ['root-0', 'root-0-0', 'root-1']);
+}, 'Range.cloneContents should invoke constructor in tree order');
+
+test(function () {
+  // Clear log for testing.
+  logs = [];
+  getRange().extractContents();
+  assert_array_equals(logs, ['root-0', 'root-0-0']);
+}, 'Range.extractContents should invoke constructor in tree order');
+
+</script>
+</body>
+</html>
--- a/toolkit/components/gfx/SanityTest.js
+++ b/toolkit/components/gfx/SanityTest.js
@@ -202,17 +202,17 @@ var listener = {
     switch (message.name) {
       case "gfxSanity:ContentLoaded":
         this.runSanityTest();
         break;
     }
   },
 
   onWindowLoaded() {
-    let browser = this.win.document.createElementNS(XUL_NS, "browser");
+    let browser = this.win.document.createXULElement("browser");
     browser.setAttribute("type", "content");
     browser.setAttribute("disableglobalhistory", "true");
 
     let remoteBrowser = Services.appinfo.browserTabsRemoteAutostart;
     browser.setAttribute("remote", remoteBrowser);
 
     browser.style.width = PAGE_WIDTH + "px";
     browser.style.height = PAGE_HEIGHT + "px";
--- a/toolkit/components/printing/content/printUtils.js
+++ b/toolkit/components/printing/content/printUtils.js
@@ -538,20 +538,17 @@ var PrintUtils = {
       if (this._listener.activateBrowser) {
         this._listener.activateBrowser(this._sourceBrowser);
       } else {
         this._sourceBrowser.docShellIsActive = true;
       }
 
       // show the toolbar after we go into print preview mode so
       // that we can initialize the toolbar with total num pages
-      const XUL_NS =
-        "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
-
-      printPreviewTB = document.createElementNS(XUL_NS, "toolbar",
+      printPreviewTB = document.createXULElement("toolbar",
         { is: "printpreview-toolbar" });
       printPreviewTB.setAttribute("fullscreentoolbar", true);
       printPreviewTB.id = "print-preview-toolbar";
 
       let navToolbox = this._listener.getNavToolbox();
       navToolbox.parentNode.insertBefore(printPreviewTB, navToolbox);
       printPreviewTB.initialize(ppBrowser);
 
--- a/toolkit/components/telemetry/Events.yaml
+++ b/toolkit/components/telemetry/Events.yaml
@@ -18,17 +18,19 @@ activity_stream:
       "MIGRATION_START",
       "OPEN_NEWTAB_PREFS",
       "OPEN_NEW_WINDOW",
       "OPEN_PRIVATE_WINDOW",
       "PIN",
       "PREVIEW_REQUEST",
       "SAVE_TO_POCKET",
       "SEARCH",
+      "SEARCH_EDIT_ADD",
       "SEARCH_EDIT_CLOSE",
+      "SEARCH_EDIT_DELETE",
       "SKIPPED_SIGNIN",
       "SUBMIT_EMAIL",
       "DISCLAIMER_ACKED",
       "MENU_ADD_SEARCH",
       "MENU_ADD_TOPSITE",
       "MENU_COLLAPSE",
       "MENU_EXPAND",
       "MENU_MANAGE",
@@ -521,18 +523,18 @@ devtools.main:
     notification_emails: ["dev-developer-tools@lists.mozilla.org", "hkirschner@mozilla.com"]
     record_in_processes: ["main"]
     description: User is editing a CSS rule by clicking on or next to a CSS property, enabling / disabling a rule or creating a new property.
     release_channel_collection: opt-out
     expiry_version: never
     extra_keys:
       session_id: The start time of the session in milliseconds since epoch (Unix Timestamp) e.g. 1396381378123.
   sidepanel_changed:
-    objects: ["inspector"]
-    bug_numbers: [1463083]
+    objects: ["inspector", "netmonitor"]
+    bug_numbers: [1463083, 1463169]
     notification_emails: ["dev-developer-tools@lists.mozilla.org", "hkirschner@mozilla.com"]
     record_in_processes: ["main"]
     description: User has switched sidepanel tabs.
     release_channel_collection: opt-out
     expiry_version: never
     extra_keys:
       oldpanel: The panel the user is switching from
       newpanel: The panel the user is switching to
--- a/toolkit/components/telemetry/docs/internals/preferences.rst
+++ b/toolkit/components/telemetry/docs/internals/preferences.rst
@@ -101,17 +101,18 @@ Preferences
   Allow pings to be archived locally. This can only be enabled if ``unified`` is on.
 
 ``toolkit.telemetry.server``
 
   The server Telemetry pings are sent to.
 
 ``toolkit.telemetry.log.level``
 
-  This sets the Telemetry logging verbosity per ``Log.jsm``, with ``Trace`` or ``0`` being the most verbose and the default being ``Warn``.
+  This sets the Telemetry logging verbosity per ``Log.jsm``. The available levels, in descending order of verbosity, are ``Trace``, ``Debug``, ``Config``, ``Info``, ``Warn``, ``Error`` and ``Fatal`` with the default being ``Warn``.
+
   By default logging goes only the console service.
 
 ``toolkit.telemetry.log.dump``
 
   Sets whether to dump Telemetry log messages to ``stdout`` too.
 
 ``toolkit.telemetry.shutdownPingSender.enabled``
 
--- a/toolkit/components/thumbnails/BackgroundPageThumbs.jsm
+++ b/toolkit/components/thumbnails/BackgroundPageThumbs.jsm
@@ -7,18 +7,16 @@ const EXPORTED_SYMBOLS = [
 ];
 
 const DEFAULT_CAPTURE_TIMEOUT = 30000; // ms
 const DESTROY_BROWSER_TIMEOUT = 60000; // ms
 const FRAME_SCRIPT_URL = "chrome://global/content/backgroundPageThumbsContent.js";
 
 const TELEMETRY_HISTOGRAM_ID_PREFIX = "FX_THUMBNAILS_BG_";
 
-const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
-
 const ABOUT_NEWTAB_SEGREGATION_PREF = "privacy.usercontext.about_newtab_segregation.enabled";
 
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm", this);
 ChromeUtils.import("resource://gre/modules/PageThumbs.jsm");
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 
 // possible FX_THUMBNAILS_BG_CAPTURE_DONE_REASON_2 telemetry values
 const TEL_CAPTURE_DONE_OK = 0;
@@ -251,17 +249,17 @@ const BackgroundPageThumbs = {
    */
   _ensureBrowser() {
     if (this._thumbBrowser && !this._renewThumbBrowser)
       return;
 
     this._destroyBrowser();
     this._renewThumbBrowser = false;
 
-    let browser = this._parentWin.document.createElementNS(XUL_NS, "browser");
+    let browser = this._parentWin.document.createXULElement("browser");
     browser.setAttribute("type", "content");
     browser.setAttribute("remote", "true");
     browser.setAttribute("disableglobalhistory", "true");
 
     if (Services.prefs.getBoolPref(ABOUT_NEWTAB_SEGREGATION_PREF)) {
       // Use the private container for thumbnails.
       let privateIdentity =
         ContextualIdentityService.getPrivateIdentity("userContextIdInternal.thumbnail");
--- a/toolkit/content/widgets/autocomplete.xml
+++ b/toolkit/content/widgets/autocomplete.xml
@@ -974,17 +974,17 @@
               ];
               // Reuse the item when its style is exactly equal to the previous style or
               // neither of their style are in the UNREUSEABLE_STYLES.
               reusable = originalType === style ||
                 !(UNREUSEABLE_STYLES.includes(style) || UNREUSEABLE_STYLES.includes(originalType));
 
             } else {
               // need to create a new item
-              item = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", "richlistitem");
+              item = document.createXULElement("richlistitem");
             }
 
             item.setAttribute("dir", this.style.direction);
             item.setAttribute("ac-image", image);
             item.setAttribute("ac-value", value);
             item.setAttribute("ac-label", label);
             item.setAttribute("ac-comment", comment);
             item.setAttribute("ac-text", trimmedSearchString);
--- a/toolkit/content/widgets/browser.xml
+++ b/toolkit/content/widgets/browser.xml
@@ -1108,18 +1108,17 @@
             }
          ]]>
        </body>
      </method>
 
       <method name="_createAutoScrollPopup">
         <body>
           <![CDATA[
-            const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
-            var popup = document.createElementNS(XUL_NS, "panel");
+            var popup = document.createXULElement("panel");
             popup.className = "autoscroller";
             // We set this attribute on the element so that mousemove
             // events can be handled by browser-content.js.
             popup.setAttribute("mousethrough", "always");
             popup.setAttribute("consumeoutsideclicks", "true");
             popup.setAttribute("rolluponmousewheel", "true");
             popup.setAttribute("hidden", "true");
             return popup;
--- a/toolkit/content/widgets/menu.xml
+++ b/toolkit/content/widgets/menu.xml
@@ -59,26 +59,23 @@
         ]]></getter>
       </property>
 
       <!-- nsIDOMXULContainerElement interface -->
       <method name="appendItem">
         <parameter name="aLabel"/>
         <parameter name="aValue"/>
         <body>
-          const XUL_NS =
-            "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
-
           var menupopup = this.menupopup;
           if (!menupopup) {
-            menupopup = this.ownerDocument.createElementNS(XUL_NS, "menupopup");
+            menupopup = this.ownerDocument.createXULElement("menupopup");
             this.appendChild(menupopup);
           }
 
-          var menuitem = this.ownerDocument.createElementNS(XUL_NS, "menuitem");
+          var menuitem = this.ownerDocument.createXULElement("menuitem");
           menuitem.setAttribute("label", aLabel);
           menuitem.setAttribute("value", aValue);
 
           return menupopup.appendChild(menuitem);
         </body>
       </method>
 
       <property name="itemCount" readonly="true">
--- a/toolkit/content/widgets/menulist.xml
+++ b/toolkit/content/widgets/menulist.xml
@@ -313,20 +313,19 @@
       </method>
 
       <method name="appendItem">
         <parameter name="label"/>
         <parameter name="value"/>
         <parameter name="description"/>
         <body>
         <![CDATA[
-          const XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
           var popup = this.menupopup ||
-                      this.appendChild(document.createElementNS(XULNS, "menupopup"));
-          var item = document.createElementNS(XULNS, "menuitem");
+                      this.appendChild(document.createXULElement("menupopup"));
+          var item = document.createXULElement("menuitem");
           item.setAttribute("label", label);
           item.setAttribute("value", value);
           if (description)
             item.setAttribute("description", description);
 
           popup.appendChild(item);
           return item;
         ]]>
--- a/toolkit/content/widgets/notification.xml
+++ b/toolkit/content/widgets/notification.xml
@@ -107,18 +107,17 @@
             var notifications = this.allNotifications;
             var insertPos = null;
             for (var n = notifications.length - 1; n >= 0; n--) {
               if (notifications[n].priority < aPriority)
                 break;
               insertPos = notifications[n];
             }
 
-            const XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
-            var newitem = document.createElementNS(XULNS, "notification");
+            var newitem = document.createXULElement("notification");
             // Can't use instanceof in case this was created from a different document:
             let labelIsDocFragment = aLabel && typeof aLabel == "object" && aLabel.nodeType &&
                                      aLabel.nodeType == aLabel.DOCUMENT_FRAGMENT_NODE;
             if (!labelIsDocFragment)
               newitem.setAttribute("label", aLabel);
             newitem.setAttribute("value", aValue);
             if (aImage)
               newitem.setAttribute("image", aImage);
@@ -128,17 +127,17 @@
               // The notification-button-default class is added to the button
               // with isDefault set to true. If there is no such button, it is
               // added to the first button (unless that button has isDefault
               // set to false). There cannot be multiple default buttons.
               var defaultElem;
 
               for (var b = 0; b < aButtons.length; b++) {
                 var button = aButtons[b];
-                var buttonElem = document.createElementNS(XULNS, "button");
+                var buttonElem = document.createXULElement("button");
                 buttonElem.setAttribute("label", button.label);
                 if (typeof button.accessKey == "string")
                   buttonElem.setAttribute("accesskey", button.accessKey);
                 if (typeof button.type == "string") {
                   buttonElem.setAttribute("type", button.type);
                   if ((button.type == "menu-button" || button.type == "menu") &&
                       "popup" in button) {
                     buttonElem.appendChild(button.popup);
--- a/toolkit/content/widgets/radio.xml
+++ b/toolkit/content/widgets/radio.xml
@@ -301,18 +301,17 @@
         </body>
       </method>
 
       <method name="appendItem">
         <parameter name="label"/>
         <parameter name="value"/>
         <body>
         <![CDATA[
-          var XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
-          var radio = document.createElementNS(XULNS, "radio");
+          var radio = document.createXULElement("radio");
           radio.setAttribute("label", label);
           radio.setAttribute("value", value);
           this.appendChild(radio);
           this._radioChildren = null;
           return radio;
         ]]>
         </body>
       </method>
--- a/toolkit/content/widgets/richlistbox.xml
+++ b/toolkit/content/widgets/richlistbox.xml
@@ -112,24 +112,21 @@
         ]]>
         </body>
       </method>
 
       <method name="appendItem">
         <parameter name="aLabel"/>
         <parameter name="aValue"/>
         <body>
-          const XULNS =
-            "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
-
           var item =
-            this.ownerDocument.createElementNS(XULNS, "richlistitem");
+            this.ownerDocument.createXULElement("richlistitem");
           item.setAttribute("value", aValue);
 
-          var label = this.ownerDocument.createElementNS(XULNS, "label");
+          var label = this.ownerDocument.createXULElement("label");
           label.setAttribute("value", aLabel);
           label.setAttribute("flex", "1");
           label.setAttribute("crop", "end");
           item.appendChild(label);
 
           this.appendChild(item);
 
           return item;
--- a/toolkit/content/widgets/tabbox.xml
+++ b/toolkit/content/widgets/tabbox.xml
@@ -501,18 +501,17 @@
         </body>
       </method>
 
       <method name="appendItem">
         <parameter name="label"/>
         <parameter name="value"/>
         <body>
         <![CDATA[
-          var XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
-          var tab = document.createElementNS(XULNS, "tab");
+          var tab = document.createXULElement("tab");
           tab.setAttribute("label", label);
           tab.setAttribute("value", value);
           this.appendChild(tab);
           return tab;
         ]]>
         </body>
       </method>
     </implementation>
--- a/toolkit/modules/PopupNotifications.jsm
+++ b/toolkit/modules/PopupNotifications.jsm
@@ -782,32 +782,30 @@ PopupNotifications.prototype = {
     text.name = n.options.name || "";
     text.end = array[1] || "";
     return text;
   },
 
   _refreshPanel: function PopupNotifications_refreshPanel(notificationsToShow) {
     this._clearPanel();
 
-    const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
-
     notificationsToShow.forEach(function(n) {
       let doc = this.window.document;
 
       // Append "-notification" to the ID to try to avoid ID conflicts with other stuff
       // in the document.
       let popupnotificationID = n.id + "-notification";
 
       // If the chrome document provides a popupnotification with this id, use
       // that. Otherwise create it ad-hoc.
       let popupnotification = doc.getElementById(popupnotificationID);
       if (popupnotification)
         gNotificationParents.set(popupnotification, popupnotification.parentNode);
       else
-        popupnotification = doc.createElementNS(XUL_NS, "popupnotification");
+        popupnotification = doc.createXULElement("popupnotification");
 
       // Create the notification description element.
       let desc = this._formatDescriptionMessage(n);
       popupnotification.setAttribute("label", desc.start);
       popupnotification.setAttribute("name", desc.name);
       popupnotification.setAttribute("endlabel", desc.end);
 
       popupnotification.setAttribute("id", popupnotificationID);
@@ -879,17 +877,17 @@ PopupNotifications.prototype = {
 
         let secondaryAction = n.secondaryActions[0];
         popupnotification.setAttribute("secondarybuttonlabel", secondaryAction.label);
         popupnotification.setAttribute("secondarybuttonaccesskey", secondaryAction.accessKey);
         popupnotification.setAttribute("secondarybuttoncommand", "PopupNotifications._onButtonEvent(event, 'secondarybuttoncommand');");
 
         for (let i = 1; i < n.secondaryActions.length; i++) {
           let action = n.secondaryActions[i];
-          let item = doc.createElementNS(XUL_NS, "menuitem");
+          let item = doc.createXULElement("menuitem");
           item.setAttribute("label", action.label);
           item.setAttribute("accesskey", action.accessKey);
           item.notification = n;
           item.action = action;
 
           popupnotification.appendChild(item);
 
           // We can only record a limited number of actions in telemetry. If
--- a/toolkit/mozapps/update/content/history.js
+++ b/toolkit/mozapps/update/content/history.js
@@ -1,15 +1,13 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-const NS_XUL  = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
-
 var gUpdateHistory = {
   _view: null,
 
   /**
    * Initialize the User Interface
    */
   onLoad() {
     this._view = document.getElementById("historyItems");
@@ -30,17 +28,17 @@ var gUpdateHistory = {
         if (!update || !update.name)
           continue;
 
         // Don't display updates that are downloading since they don't have
         // valid statusText for the UI (bug 485493).
         if (!update.statusText)
           continue;
 
-        var element = document.createElementNS(NS_XUL, "richlistitem");
+        var element = document.createXULElement("richlistitem");
         element.className = "update";
         this._view.appendChild(element);
         element.name = bundle.getFormattedString("updateFullName",
           [update.name, update.buildID]);
         element.installDate = this._formatDate(update.installDate);
         if (update.detailsURL)
           element.detailsURL = update.detailsURL;
         else
--- a/toolkit/themes/linux/global/button.css
+++ b/toolkit/themes/linux/global/button.css
@@ -67,17 +67,17 @@ button[type="menu-button"] {
 button.plain {
   margin: 0 !important;
   padding: 0 !important;
 }
 
 button[type="disclosure"] {
   margin: 0;
   -moz-appearance: none;
-  list-style-image: url("chrome://global/skin/tree/twisty-collapsed.svg");
+  list-style-image: url("chrome://global/skin/icons/twisty-collapsed.svg");
   -moz-context-properties: fill;
   fill: currentColor;
   min-width: 0;
 }
 
 button[type="disclosure"][open="true"] {
-  list-style-image: url("chrome://global/skin/tree/twisty-expanded.svg");
+  list-style-image: url("chrome://global/skin/icons/twisty-expanded.svg");
 }
--- a/toolkit/themes/linux/global/jar.mn
+++ b/toolkit/themes/linux/global/jar.mn
@@ -24,24 +24,21 @@ toolkit.jar:
    skin/classic/global/richlistbox.css
    skin/classic/global/scrollbox.css
    skin/classic/global/splitter.css
    skin/classic/global/tabbox.css
    skin/classic/global/textbox.css
    skin/classic/global/toolbar.css
    skin/classic/global/toolbarbutton.css
    skin/classic/global/tooltip.css
-   skin/classic/global/tree.css
+*  skin/classic/global/tree.css
 *  skin/classic/global/alerts/alert.css                        (alerts/alert.css)
 
    skin/classic/global/icons/Authentication.png                (icons/Authentication.png)
    skin/classic/global/icons/blacklist_favicon.png             (icons/blacklist_favicon.png)
    skin/classic/global/icons/blacklist_large.png               (icons/blacklist_large.png)
    skin/classic/global/icons/Close.gif                         (icons/Close.gif)
    skin/classic/global/icons/Minimize.gif                      (icons/Minimize.gif)
    skin/classic/global/icons/Restore.gif                       (icons/Restore.gif)
    skin/classic/global/icons/sslWarning.png                    (icons/sslWarning.png)
 
 *  skin/classic/global/in-content/common.css                   (in-content/common.css)
 *  skin/classic/global/in-content/info-pages.css               (in-content/info-pages.css)
-   skin/classic/global/tree/twisty-collapsed.svg               (tree/twisty-collapsed.svg)
-   skin/classic/global/tree/twisty-collapsed-rtl.svg           (tree/twisty-collapsed-rtl.svg)
-   skin/classic/global/tree/twisty-expanded.svg                (tree/twisty-expanded.svg)
--- a/toolkit/themes/linux/global/tree.css
+++ b/toolkit/themes/linux/global/tree.css
@@ -1,216 +1,11 @@
 /* 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.css ===================================================
-  == Styles used by the XUL outline element.
-  ======================================================================= */
-
-@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
-
-/* ::::: tree ::::: */
-
-tree {
-  margin: 0 4px;
-  background-color: -moz-Field;
-  color: -moz-FieldText;
-  -moz-appearance: listbox;
-}
-
-/* ::::: tree focusring ::::: */
-
-.focusring > .tree-stack > .tree-rows > .tree-bodybox {
-  border: 1px solid transparent;
-}
-
-.focusring:focus > .tree-stack > .tree-rows > .tree-bodybox {
-  border: 1px solid #000000;
-}
-
-
-/* ::::: tree rows ::::: */
-
-treechildren::-moz-tree-row {
-  border: 1px solid transparent;
-  min-height: 18px;
-  height: 1.3em;
-}
-
-treechildren::-moz-tree-row(multicol, odd) {
-  background-color: -moz-oddtreerow;
-}
-
-treechildren::-moz-tree-row(selected) {
-  background-color: -moz-cellhighlight;
-}
-
-treechildren::-moz-tree-row(selected, focus) {
-  background-color: Highlight;
-}
-
-treechildren::-moz-tree-row(current, focus) {
-  border: 1px dotted Highlight;
-}
-
-treechildren::-moz-tree-row(selected, current, focus) {
-  border: 1px dotted #F3D982;
-}
-
-/* ::::: tree cells ::::: */
-
-treechildren::-moz-tree-cell {
-  padding: 0 2px;
-}
-
-treechildren::-moz-tree-cell-text {
-  color: inherit;
-}
-
-treechildren::-moz-tree-cell-text(selected) {
-  color: -moz-cellhighlighttext;
-}
-
-treechildren::-moz-tree-cell-text(selected, focus) {
-  color: HighlightText;
-}
-
-/* ::::: lines connecting cells ::::: */
-
-treechildren::-moz-tree-line {
-  border: 1px dotted ThreeDShadow;
-}
-
-treechildren::-moz-tree-line(selected, focus) {
-  border: 1px dotted HighlightText;
-}
-
-
-/* ::::: tree separator ::::: */
-
-treechildren::-moz-tree-separator {
-  border-top: 1px solid ThreeDShadow;
-  border-bottom: 1px solid ThreeDHighlight;
-}
-
-
-/* ::::: drop feedback ::::: */
-
-treechildren::-moz-tree-cell-text(primary, dropOn) {
-  background-color: Highlight;
-  color: HighlightText;
-}
-
-treechildren::-moz-tree-drop-feedback {
-  background-color: Highlight;
-  width: 50px;
-  height: 2px;
-  margin-inline-start: 5px;
-}
-
-/* ::::: tree columns ::::: */
-
-treecol,
-treecolpicker {
-  -moz-appearance: treeheadercell;
-  -moz-box-align: center;
-  -moz-box-pack: center;
-  color: -moz-DialogText;
-}
-
-treecol:hover,
-treecolpicker:hover {
-  color: -moz-buttonhovertext;
-}
-
-.treecol-image {
-  padding: 0 1px;
-}
-
-.treecol-text {
-  margin: 0 !important;
-}
-
-treecol[hideheader="true"] {
-  -moz-appearance: none;
-}
-
-/* ::::: column drag and drop styles ::::: */
-
-treecol[dragging="true"] {
-  color: Graytext;
-}
-
-treechildren::-moz-tree-column(insertbefore) {
-  border-inline-start: 1px solid ThreeDShadow;
-}
-
-treechildren::-moz-tree-column(insertafter) {
-  border-inline-end: 1px solid ThreeDShadow;
-}
+%include ../../shared/tree.inc.css
 
 /* ::::: sort direction indicator :::::  */
 
 .treecol-sortdirection {
   -moz-appearance: treeheadersortarrow;
 }
-
-/* ::::: column picker :::::  */
-
-.tree-columnpicker-icon {
-  list-style-image: url("chrome://global/skin/tree/columnpicker.gif");
-}
-
-/* ::::: twisty :::::  */
-
-treechildren::-moz-tree-twisty {
-  padding-inline-end: 4px;
-  padding-top: 1px;
-  width: 9px; /* The image's width is 9 pixels */
-  list-style-image: url("chrome://global/skin/tree/twisty-collapsed.svg");
-  -moz-context-properties: fill;
-  fill: currentColor;
-}
-
-treechildren:-moz-locale-dir(rtl)::-moz-tree-twisty(closed) {
-  list-style-image: url("chrome://global/skin/tree/twisty-collapsed-rtl.svg");
-}
-
-treechildren::-moz-tree-twisty(open) {
-  list-style-image: url("chrome://global/skin/tree/twisty-expanded.svg");
-}
-
-treechildren::-moz-tree-twisty(selected) {
-  fill: -moz-cellhighlighttext;
-}
-
-treechildren::-moz-tree-indentation {
-  width: 18px;
-}
-
-/* ::::: editable tree ::::: */
-
-treechildren::-moz-tree-row(selected, editing) {
-  background-color: transparent;
-  border: none;
-}
-
-treechildren::-moz-tree-cell-text(selected, editing) {
-  color: inherit;
-}
-
-treechildren::-moz-tree-cell(active, selected, focus, editing) {
-  background-color: transparent;
-  border: none;
-}
-
-treechildren::-moz-tree-cell-text(active, selected, editing) {
-  opacity: 0;
-}
-
-.tree-input {
-  -moz-appearance: none;
-  border: 1px solid Highlight;
-  margin: 0;
-  margin-inline-start: -4px;
-  padding: 1px;
-}
deleted file mode 100644
--- a/toolkit/themes/linux/global/tree/twisty-collapsed-rtl.svg
+++ /dev/null
@@ -1,6 +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 width="9" height="9" xmlns="http://www.w3.org/2000/svg" stroke="context-fill" stroke-width="1.6" fill="none">
-  <path d="m 6.5,0.5 -4,4 4,4"/>
-</svg>
deleted file mode 100644
--- a/toolkit/themes/linux/global/tree/twisty-collapsed.svg
+++ /dev/null
@@ -1,6 +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 width="9" height="9" xmlns="http://www.w3.org/2000/svg" stroke="context-fill" stroke-width="1.6" fill="none">
-  <path d="m 2.5,0.5 4,4 -4,4"/>
-</svg>
deleted file mode 100644
--- a/toolkit/themes/linux/global/tree/twisty-expanded.svg
+++ /dev/null
@@ -1,6 +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 width="9" height="9" xmlns="http://www.w3.org/2000/svg" stroke="context-fill" stroke-width="1.6" fill="none">
-  <path d="m 8.5,2.5 -4,4 -4,-4"/>
-</svg>
--- a/toolkit/themes/osx/global/jar.mn
+++ b/toolkit/themes/osx/global/jar.mn
@@ -67,12 +67,9 @@ toolkit.jar:
   skin/classic/global/icons/error-16.png                             (icons/error-16.png)
   skin/classic/global/icons/error-64.png                             (icons/error-64.png)
   skin/classic/global/icons/question-16.png                          (icons/question-16.png)
   skin/classic/global/icons/question-32.png                          (icons/question-32.png)
   skin/classic/global/icons/question-64.png                          (icons/question-64.png)
   skin/classic/global/icons/sslWarning.png                           (icons/sslWarning.png)
 * skin/classic/global/in-content/common.css                          (in-content/common.css)
 * skin/classic/global/in-content/info-pages.css                      (in-content/info-pages.css)
-  skin/classic/global/tree/arrow-collapsed.svg                       (tree/arrow-collapsed.svg)
-  skin/classic/global/tree/arrow-collapsed-rtl.svg                   (tree/arrow-collapsed-rtl.svg)
-  skin/classic/global/tree/arrow-expanded.svg                        (tree/arrow-expanded.svg)
   skin/classic/global/tree/columnpicker.gif                          (tree/columnpicker.gif)
--- a/toolkit/themes/osx/global/tree.css
+++ b/toolkit/themes/osx/global/tree.css
@@ -1,186 +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/. */
 
-%include shared.inc
-@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
-
-tree {
-  margin: 0px 4px;
-  color: -moz-DialogText;
-  background-color: #FFFFFF;
-  -moz-appearance: listbox;
-}
+%include ../../shared/tree.inc.css
 
 /* ::::: tree focusring ::::: */
 
 .focusring > .tree-stack > .tree-rows > .tree-bodybox {
   border: 1px solid transparent;
 }
 
 .focusring:focus > .tree-stack > .tree-rows > .tree-bodybox {
   border: 1px solid -moz-mac-focusring;
 }
 
-
-/* ::::: tree rows ::::: */
-
-treechildren::-moz-tree-row {
-  border-top: 1px solid transparent;
-  height: 18px;
-  background-color: -moz-field;
-}
-
-treechildren::-moz-tree-row(multicol, odd) {
-  background-color: -moz-oddtreerow;
-}
-
-treechildren::-moz-tree-row(selected) {
-  background-color: -moz-mac-secondaryhighlight;
-}
-
-treechildren::-moz-tree-row(selected, focus) {
-  background-color: Highlight;
-  color: HighlightText;
-}
-
-/* ::::: tree cells ::::: */
-
-treechildren::-moz-tree-cell {
-  padding: 0px 2px 0px 2px;
-}
-
-treechildren::-moz-tree-cell-text {
-  color: inherit;
-}
-
-treechildren::-moz-tree-cell-text(selected) {
-  color: -moz-DialogText;
-}
-
-treechildren::-moz-tree-cell-text(selected, focus) {
-  color: HighlightText;
-}
-
-/* ::::: lines connecting cells ::::: */
-
-treechildren::-moz-tree-line {
-  /* XXX there should be no border on Mac, but trees currently
-         paint the line black by default, so I'll just leave this
-         for now. */
-  visibility: hidden;
-  border: 1px dotted grey;
-}
-
-
-/* ::::: tree separator ::::: */
-
-treechildren::-moz-tree-separator {
-  border-top: 1px dashed #C7C7C7;
-  margin: 0 2px;
-}
-
-
-/* ::::: drop feedback ::::: */
-
-treechildren::-moz-tree-cell(primary, dropOn) {
-  background-color: #A1A1A1 !important;
-  color: #FFF !important;
-  background-image: none;
-}
-
-treechildren::-moz-tree-cell-text(primary, dropOn) {
-  color: #FFF !important;
-}
-
-treechildren::-moz-tree-drop-feedback {
-  background-color: #A1A1A1;
-  width: 50px;
-  height: 2px;
-  margin-inline-start: 5px;
-}
-
-/* ::::: tree columns ::::: */
-
-treecol,
-treecolpicker {
-  -moz-appearance: treeheadercell;
-  -moz-box-align: center;
-  -moz-box-pack: center;
-  color: -moz-DialogText;
-  padding: 0 4px;
-}
-
-.treecol-image {
-  padding: 0 1px;
-}
-
-.treecol-text {
-  margin: 0 !important;
-}
-
-treecol[hideheader="true"] {
-  -moz-appearance: none;
-  border: none;
-  padding: 0;
-  max-height: 0;
-}
-
-/* ::::: column drag and drop styles ::::: */
-
-treecol[dragging="true"] {
-  color: GrayText;
-}
-
-treechildren::-moz-tree-column(insertbefore) {
-  border-inline-start: 1px solid ThreeDShadow;
-}
-
-treechildren::-moz-tree-column(insertafter) {
-  border-inline-end: 1px solid ThreeDShadow;
-}
-
-/* ::::: column picker :::::  */
-
-.tree-columnpicker-icon {
-  list-style-image: url("chrome://global/skin/tree/columnpicker.gif");
-}
-
-/* ::::: twisty :::::  */
-
-treechildren::-moz-tree-twisty {
-  -moz-appearance: treetwisty;
-  padding-inline-end: 2px;
-}
-
-treechildren::-moz-tree-twisty(open) {
-  -moz-appearance: treetwistyopen;
-}
-
-treechildren::-moz-tree-twisty(Name, separator) {
-  -moz-appearance: none;
-}
-
-treechildren::-moz-tree-indentation {
-  width: 16px;
-}
-
 /* ::::: editable tree ::::: */
 
 .tree-input {
   -moz-appearance: none;
   border-width: 0;
   box-shadow: var(--focus-ring-box-shadow);
-  margin: 0;
-  margin-inline-start: -2px;
-  padding: 2px 1px 1px;
 }
-
-treechildren::-moz-tree-cell(active, selected, focus, editing) {
-  background-color: transparent;
-  border: none;
-}
-
-treechildren::-moz-tree-cell-text(active, selected, editing) {
-  opacity: 0;
-}
deleted file mode 100644
--- a/toolkit/themes/osx/global/tree/arrow-collapsed-rtl.svg
+++ /dev/null
@@ -1,6 +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 width="16" height="16" xmlns="http://www.w3.org/2000/svg" fill="context-fill" fill-opacity="context-fill-opacity">
-  <polygon points="4,8.5  12,4  12,13" />
-</svg>
deleted file mode 100644
--- a/toolkit/themes/osx/global/tree/arrow-collapsed.svg
+++ /dev/null
@@ -1,6 +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 width="16" height="16" xmlns="http://www.w3.org/2000/svg" fill="context-fill" fill-opacity="context-fill-opacity">
-  <polygon points="4,4  12,8.5  4,13" />
-</svg>
deleted file mode 100644
--- a/toolkit/themes/osx/global/tree/arrow-expanded.svg
+++ /dev/null
@@ -1,6 +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 width="16" height="16" xmlns="http://www.w3.org/2000/svg" fill="context-fill" fill-opacity="context-fill-opacity">
-  <polygon points="3,5  12,5  7.5,13" />
-</svg>
new file mode 100644
--- /dev/null
+++ b/toolkit/themes/shared/icons/twisty-collapsed-rtl.svg
@@ -0,0 +1,6 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+   - License, v. 2.0. If a copy of the MPL was not distributed with this
+   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+<svg width="8" height="8" viewBox="0 0 8 8" xmlns="http://www.w3.org/2000/svg" >
+  <path d="M1 4l4 4 1-1-3-3 3-3-1-1-4 4z" fill="context-fill"/>
+</svg>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/toolkit/themes/shared/icons/twisty-collapsed.svg
@@ -0,0 +1,6 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+   - License, v. 2.0. If a copy of the MPL was not distributed with this
+   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+<svg width="8" height="8" viewBox="0 0 8 8" xmlns="http://www.w3.org/2000/svg">
+  <path d="M7,4L3,8L2,7l3-3L2,1l1-1L7,4z" fill="context-fill"/>
+</svg>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/toolkit/themes/shared/icons/twisty-expanded.svg
@@ -0,0 +1,6 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+   - License, v. 2.0. If a copy of the MPL was not distributed with this
+   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+<svg width="8" height="8" xmlns="http://www.w3.org/2000/svg">
+  <path d="M4 7L0 3l1-1 3 3 3-3 1 1-4 4z" fill="context-fill"/>
+</svg>
\ No newline at end of file
--- a/toolkit/themes/shared/jar.inc.mn
+++ b/toolkit/themes/shared/jar.inc.mn
@@ -35,16 +35,19 @@ toolkit.jar:
   skin/classic/global/icons/find-next-arrow.svg            (../../shared/icons/find-next-arrow.svg)
   skin/classic/global/icons/help.svg                       (../../shared/icons/help.svg)
   skin/classic/global/icons/info.svg                       (../../shared/incontent-icons/info.svg)
   skin/classic/global/icons/loading.png                    (../../shared/icons/loading.png)
   skin/classic/global/icons/loading@2x.png                 (../../shared/icons/loading@2x.png)
   skin/classic/global/icons/resizer.svg                    (../../shared/icons/resizer.svg)
   skin/classic/global/icons/spinner-arrow-down.svg         (../../shared/icons/spinner-arrow-down.svg)
   skin/classic/global/icons/spinner-arrow-up.svg           (../../shared/icons/spinner-arrow-up.svg)
+  skin/classic/global/icons/twisty-collapsed.svg           (../../shared/icons/twisty-collapsed.svg)
+  skin/classic/global/icons/twisty-collapsed-rtl.svg       (../../shared/icons/twisty-collapsed-rtl.svg)
+  skin/classic/global/icons/twisty-expanded.svg            (../../shared/icons/twisty-expanded.svg)
   skin/classic/global/icons/arrow-dropdown-12.svg          (../../shared/icons/arrow-dropdown-12.svg)
   skin/classic/global/icons/arrow-dropdown-16.svg          (../../shared/icons/arrow-dropdown-16.svg)
   skin/classic/global/icons/warning.svg                    (../../shared/icons/warning.svg)
   skin/classic/global/illustrations/about-rights.svg       (../../shared/illustrations/about-rights.svg)
   skin/classic/global/icons/blocked.svg                    (../../shared/incontent-icons/blocked.svg)
   skin/classic/global/illustrations/about-license.svg      (../../shared/illustrations/about-license.svg)
   skin/classic/global/narrate.css                          (../../shared/narrate.css)
   skin/classic/global/narrate/arrow.svg                    (../../shared/narrate/arrow.svg)
copy from toolkit/themes/linux/global/tree.css
copy to toolkit/themes/shared/tree.inc.css
--- a/toolkit/themes/linux/global/tree.css
+++ b/toolkit/themes/shared/tree.inc.css
@@ -1,14 +1,14 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /* ===== tree.css ===================================================
-  == Styles used by the XUL outline element.
+  == Styles used by the XUL tree element.
   ======================================================================= */
 
 @namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
 
 /* ::::: tree ::::: */
 
 tree {
   margin: 0 4px;
@@ -27,58 +27,56 @@ tree {
   border: 1px solid #000000;
 }
 
 
 /* ::::: tree rows ::::: */
 
 treechildren::-moz-tree-row {
   border: 1px solid transparent;
-  min-height: 18px;
+  min-height: 24px;
   height: 1.3em;
 }
 
 treechildren::-moz-tree-row(multicol, odd) {
   background-color: -moz-oddtreerow;
 }
 
 treechildren::-moz-tree-row(selected) {
   background-color: -moz-cellhighlight;
 }
 
 treechildren::-moz-tree-row(selected, focus) {
   background-color: Highlight;
 }
 
-treechildren::-moz-tree-row(current, focus) {
-  border: 1px dotted Highlight;
-}
-
-treechildren::-moz-tree-row(selected, current, focus) {
-  border: 1px dotted #F3D982;
-}
 
 /* ::::: tree cells ::::: */
 
 treechildren::-moz-tree-cell {
   padding: 0 2px;
 }
 
 treechildren::-moz-tree-cell-text {
   color: inherit;
 }
 
+treechildren::-moz-tree-image(selected),
+treechildren::-moz-tree-twisty(selected),
 treechildren::-moz-tree-cell-text(selected) {
   color: -moz-cellhighlighttext;
 }
 
+treechildren::-moz-tree-image(selected, focus),
+treechildren::-moz-tree-twisty(selected, focus),
 treechildren::-moz-tree-cell-text(selected, focus) {
   color: HighlightText;
 }
 
+
 /* ::::: lines connecting cells ::::: */
 
 treechildren::-moz-tree-line {
   border: 1px dotted ThreeDShadow;
 }
 
 treechildren::-moz-tree-line(selected, focus) {
   border: 1px dotted HighlightText;
@@ -110,21 +108,17 @@ treechildren::-moz-tree-drop-feedback {
 /* ::::: tree columns ::::: */
 
 treecol,
 treecolpicker {
   -moz-appearance: treeheadercell;
   -moz-box-align: center;
   -moz-box-pack: center;
   color: -moz-DialogText;
-}
-
-treecol:hover,
-treecolpicker:hover {
-  color: -moz-buttonhovertext;
+  padding: 0 4px;
 }
 
 .treecol-image {
   padding: 0 1px;
 }
 
 .treecol-text {
   margin: 0 !important;
@@ -143,53 +137,51 @@ treecol[dragging="true"] {
 treechildren::-moz-tree-column(insertbefore) {
   border-inline-start: 1px solid ThreeDShadow;
 }
 
 treechildren::-moz-tree-column(insertafter) {
   border-inline-end: 1px solid ThreeDShadow;
 }
 
-/* ::::: sort direction indicator :::::  */
-
-.treecol-sortdirection {
-  -moz-appearance: treeheadersortarrow;
-}
-
 /* ::::: column picker :::::  */
 
 .tree-columnpicker-icon {
   list-style-image: url("chrome://global/skin/tree/columnpicker.gif");
 }
 
+/* ::::: tree icons ::::: */
+
+treechildren::-moz-tree-image {
+  -moz-context-properties: fill;
+  fill: currentColor;
+}
+
 /* ::::: twisty :::::  */
 
 treechildren::-moz-tree-twisty {
   padding-inline-end: 4px;
+  padding-inline-start: 4px;
   padding-top: 1px;
-  width: 9px; /* The image's width is 9 pixels */
-  list-style-image: url("chrome://global/skin/tree/twisty-collapsed.svg");
+  width: 8px; /* The image's width is 8 pixels */
+  list-style-image: url("chrome://global/skin/icons/twisty-collapsed.svg");
   -moz-context-properties: fill;
   fill: currentColor;
 }
 
 treechildren:-moz-locale-dir(rtl)::-moz-tree-twisty(closed) {
-  list-style-image: url("chrome://global/skin/tree/twisty-collapsed-rtl.svg");
+  list-style-image: url("chrome://global/skin/icons/twisty-collapsed-rtl.svg");
 }
 
 treechildren::-moz-tree-twisty(open) {
-  list-style-image: url("chrome://global/skin/tree/twisty-expanded.svg");
-}
-
-treechildren::-moz-tree-twisty(selected) {
-  fill: -moz-cellhighlighttext;
+  list-style-image: url("chrome://global/skin/icons/twisty-expanded.svg");
 }
 
 treechildren::-moz-tree-indentation {
-  width: 18px;
+  width: 16px;
 }
 
 /* ::::: editable tree ::::: */
 
 treechildren::-moz-tree-row(selected, editing) {
   background-color: transparent;
   border: none;
 }
--- a/toolkit/themes/windows/global/button.css
+++ b/toolkit/themes/windows/global/button.css
@@ -103,17 +103,17 @@ button[type="menu-button"] {
 button.plain {
   margin: 0 !important;
   padding: 0 !important;
 }
 
 button[type="disclosure"] {
   margin: 0;
   -moz-appearance: none;
-  list-style-image: url("chrome://global/skin/tree/twisty-collapsed.svg");
+  list-style-image: url("chrome://global/skin/icons/twisty-collapsed.svg");
   -moz-context-properties: fill;
   fill: currentColor;
   min-width: 0;
 }
 
 button[type="disclosure"][open="true"] {
-  list-style-image: url("chrome://global/skin/tree/twisty-expanded.svg");
+  list-style-image: url("chrome://global/skin/icons/twisty-expanded.svg");
 }
--- a/toolkit/themes/windows/global/jar.mn
+++ b/toolkit/themes/windows/global/jar.mn
@@ -51,11 +51,8 @@ toolkit.jar:
   skin/classic/global/icons/Search-close.png               (icons/Search-close.png)
   skin/classic/global/icons/Question.png                   (icons/Question.png)
   skin/classic/global/icons/sslWarning.png                 (icons/sslWarning.png)
   skin/classic/global/icons/Warning.png                    (icons/Warning.png)
   skin/classic/global/icons/warning-large.png              (icons/warning-large.png)
   skin/classic/global/icons/windowControls.png             (icons/windowControls.png)
 * skin/classic/global/in-content/common.css                (in-content/common.css)
 * skin/classic/global/in-content/info-pages.css            (in-content/info-pages.css)
-  skin/classic/global/tree/twisty-collapsed.svg            (tree/twisty-collapsed.svg)
-  skin/classic/global/tree/twisty-collapsed-rtl.svg        (tree/twisty-collapsed-rtl.svg)
-  skin/classic/global/tree/twisty-expanded.svg             (tree/twisty-expanded.svg)
--- a/toolkit/themes/windows/global/tree.css
+++ b/toolkit/themes/windows/global/tree.css
@@ -1,170 +1,13 @@
 /* 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.css ===================================================
-  == Styles used by the XUL outline element.
-  ======================================================================= */
-
-@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
-
-/* ::::: tree ::::: */
-
-tree {
-  margin: 0 4px;
-  background-color: -moz-Field;
-  color: -moz-FieldText;
-  -moz-appearance: listbox;
-}
-
-/* ::::: tree focusring ::::: */
-
-.focusring > .tree-stack > .tree-rows > .tree-bodybox {
-  border: 1px solid transparent;
-}
-
-.focusring:-moz-focusring > .tree-stack > .tree-rows > .tree-bodybox {
-  border: 1px solid #000000;
-}
-
-
-/* ::::: tree rows ::::: */
-
-treechildren::-moz-tree-row {
-  border: 1px solid transparent;
-  min-height: 18px;
-  height: 1.3em;
-}
-
-treechildren::-moz-tree-row(selected) {
-  background-color: -moz-cellhighlight;
-}
-
-treechildren::-moz-tree-row(selected, focus) {
-  background-color: Highlight;
-}
-
-treechildren::-moz-tree-row(current, focus) {
-  border: 1px dotted Highlight;
-}
-
-treechildren::-moz-tree-row(selected, current, focus) {
-  border: 1px dotted #F3D982;
-}
-
-/* ::::: tree cells ::::: */
-
-treechildren::-moz-tree-cell {
-  padding: 0 2px;
-}
-
-treechildren::-moz-tree-cell-text {
-  color: inherit;
-}
-
-treechildren::-moz-tree-cell-text(selected) {
-  color: -moz-cellhighlighttext;
-}
-
-treechildren::-moz-tree-cell-text(selected, focus) {
-  color: HighlightText;
-}
-
-/* ::::: lines connecting cells ::::: */
-
-treechildren::-moz-tree-line {
-  border: 1px dotted ThreeDShadow;
-}
-
-treechildren::-moz-tree-line(selected, focus) {
-  border: 1px dotted HighlightText;
-}
-
-/* ::::: tree separator ::::: */
-
-treechildren::-moz-tree-separator {
-  border-top: 1px solid ThreeDShadow;
-  border-bottom: 1px solid ThreeDHighlight;
-}
-
-
-/* ::::: drop feedback ::::: */
-
-treechildren::-moz-tree-row(dropOn) {
-  background-color: Highlight;
-}
-
-treechildren::-moz-tree-cell-text(primary, dropOn) {
-  color: HighlightText;
-}
-
-treechildren::-moz-tree-drop-feedback {
-  background-color: Highlight;
-  width: 50px;
-  height: 2px;
-  margin-inline-start: 5px;
-}
-
-/* ::::: tree columns ::::: */
-
-treecol,
-treecolpicker {
-  -moz-appearance: treeheadercell;
-  -moz-box-align: center;
-  -moz-box-pack: center;
-  color: -moz-DialogText;
-  padding: 0 4px;
-}
-
-.treecol-image {
-  padding: 0 1px;
-}
-
-.treecol-text {
-  margin: 0 !important;
-}
-
-treecol[hideheader="true"] {
-  -moz-appearance: none;
-  border: none;
-  padding: 0;
-}
-
-/* ..... internal box ..... */
-
-treecol:hover:active,
-treecolpicker:hover:active {
-  padding-top: 1px;
-  padding-bottom: 0;
-  padding-inline-start: 5px;
-  padding-inline-end: 3px;
-}
-
-.treecol-image:hover:active {
-  padding-top: 1px;
-  padding-bottom: 0;
-  padding-inline-start: 2px;
-  padding-inline-end: 0;
-}
-
-/* ::::: column drag and drop styles ::::: */
-
-treecol[dragging="true"] {
-  color: GrayText;
-}
-
-treechildren::-moz-tree-column(insertbefore) {
-  border-inline-start: 1px solid ThreeDShadow;
-}
-
-treechildren::-moz-tree-column(insertafter) {
-  border-inline-end: 1px solid ThreeDShadow;
-}
+%include ../../shared/tree.inc.css
 
 /* ::::: sort direction indicator :::::  */
 
 .treecol-sortdirection {
   list-style-image: none;
 }
 
 treecol:not([hideheader="true"]) > .treecol-sortdirection[sortDirection="ascending"] {
@@ -179,202 +22,8 @@ treecol:not([hideheader="true"]) > .tree
   treecol:not([hideheader="true"]) > .treecol-sortdirection[sortDirection="ascending"] {
     list-style-image: url("chrome://global/skin/tree/sort-asc-classic.png");
   }
 
   treecol:not([hideheader="true"]) > .treecol-sortdirection[sortDirection="descending"] {
     list-style-image: url("chrome://global/skin/tree/sort-dsc-classic.png");
   }
 }
-
-/* ::::: column picker :::::  */
-
-.tree-columnpicker-icon {
-  list-style-image: url("chrome://global/skin/tree/columnpicker.gif");
-}
-
-/* ::::: twisty :::::  */
-
-treechildren::-moz-tree-twisty {
-  padding-inline-end: 1px;
-  padding-top: 1px;
-  width: 9px; /* The image's width is 9 pixels */
-  list-style-image: url("chrome://global/skin/tree/twisty-collapsed.svg");
-  -moz-context-properties: fill;
-  fill: #b6b6b6;
-}
-
-treechildren:-moz-locale-dir(rtl)::-moz-tree-twisty(closed) {
-  list-style-image: url("chrome://global/skin/tree/twisty-collapsed-rtl.svg");
-}
-
-treechildren::-moz-tree-twisty(open) {
-  list-style-image: url("chrome://global/skin/tree/twisty-expanded.svg");
-  fill: #636363;
-}
-
-treechildren::-moz-tree-twisty(hover) {
-  fill: #4ed0f9;
-}
-
-treechildren::-moz-tree-indentation {
-  width: 12px;
-}
-
-/* ::::: editable tree ::::: */
-
-treechildren::-moz-tree-row(selected, editing) {
-  background-color: transparent;
-  border: none;
-}
-
-treechildren::-moz-tree-cell-text(selected, editing) {
-  color: inherit;
-}
-
-treechildren::-moz-tree-cell(active, selected, focus, editing) {
-  background-color: transparent;
-  border: none;
-}
-
-treechildren::-moz-tree-cell-text(active, selected, editing) {
-  opacity: 0;
-}
-
-.tree-input {
-  -moz-appearance: none;
-  border: 1px solid Highlight;
-  margin: 0;
-  margin-inline-start: -4px;
-  padding: 1px;
-}
-
-%ifdef XP_WIN
-@media (-moz-windows-default-theme) {
-  treechildren {
-    --treechildren-outline: none;
-    --treechildren-focusColor: rgb(123,195,255);
-    --treechildren-selectedFocusColor: rgb(205,232,255);
-    --treechildren-currentColor: rgb(125,162,206);
-    --treechildren-hoverColor: rgb(229,243,255);
-    --treechildren-selectedBorder: rgb(217,217,217);
-    --treechildren-selectedBackground: rgb(217,217,217);
-    --treechildren-currentFocusBorder: var(--treechildren-focusColor);
-    --treechildren-selectedFocusBorder: var(--treechildren-selectedFocusColor) var(--treechildren-selectedFocusColor) rgb(165,214,255);
-    --treechildren-selectedFocusBackground: var(--treechildren-selectedFocusColor);
-    --treechildren-selectedFocusCurrentBorder: var(--treechildren-focusColor);
-    --treechildren-selectedFocusCurrentBackground: rgb(205,232,255);
-    --treechildren-hoverBorder: var(--treechildren-hoverColor);
-    --treechildren-hoverBackground: rgb(229,243,255);
-    --treechildren-hoverCurrentBorder: var(--treechildren-currentColor);
-    --treechildren-hoverCurrentBackground: rgba(131,183,249,.16);
-    --treechildren-hoverSelectedBorder: var(--treechildren-focusColor);
-    --treechildren-hoverSelectedBackground: rgb(205,232,255);
-  }
-
-  treechildren::-moz-tree-row {
-    height: 1.8em;
-    color: -moz-FieldText;
-    margin-inline-start: 1px;
-    margin-inline-end: 1px;
-    border-width: 1px;
-    border-color: transparent;
-    background-repeat: no-repeat;
-    background-size: 100% 100%;
-  }
-
-  treechildren::-moz-tree-row(selected) {
-    border-color: var(--treechildren-selectedBorder);
-    background-color: var(--treechildren-selectedBackground);
-    outline: var(--treechildren-outline);
-  }
-
-  treechildren::-moz-tree-row(current, focus) {
-    border-style: solid;
-    border-color: var(--treechildren-currentFocusBorder);
-    outline: var(--treechildren-outline);
-  }
-
-  treechildren::-moz-tree-row(selected, focus),
-  treechildren::-moz-tree-row(dropOn) {
-    border-color: var(--treechildren-selectedFocusBorder);
-    background-color: var(--treechildren-selectedFocusBackground);
-  }
-
-  treechildren::-moz-tree-row(selected, current, focus) {
-    border-style: solid;
-    border-color: var(--treechildren-selectedFocusCurrentBorder);
-    background-color: var(--treechildren-selectedFocusCurrentBackground);
-  }
-
-  treechildren::-moz-tree-row(hover) {
-    border-color: var(--treechildren-hoverBorder);
-    background-color: var(--treechildren-hoverBackground);
-    outline: var(--treechildren-outline);
-  }
-
-  treechildren::-moz-tree-row(hover, current) {
-    border-color: var(--treechildren-hoverCurrentBorder);
-    background-image: var(--treechildren-hoverCurrentBackground);
-  }
-
-  treechildren::-moz-tree-row(hover, selected) {
-    border-color: var(--treechildren-hoverSelectedBorder);
-    background-color: var(--treechildren-hoverSelectedBackground);
-  }
-
-  tree[disabled="true"] > treechildren::-moz-tree-row {
-    background: none;
-    border-color: transparent;
-  }
-
-  treechildren::-moz-tree-cell(dropOn) {
-    background-image: none;
-    background-color: transparent;
-    border-radius: 0;
-  }
-
-  treechildren::-moz-tree-cell-text(primary, dropOn) {
-    color: -moz-FieldText;
-  }
-
-  treechildren::-moz-tree-cell-text {
-    padding-bottom: initial;
-    border-color: transparent;
-    background-color: transparent;
-  }
-
-  treechildren::-moz-tree-cell-text(selected, focus) {
-    color: -moz-DialogText;
-  }
-
-  @media (-moz-os-version: windows-win7),
-         (-moz-os-version: windows-win8) {
-    treechildren {
-      --treechildren-2ndBorderColor: rgba(255,255,255,.4);
-      --treechildren-outline: 1px solid var(--treechildren-2ndBorderColor);
-      --treechildren-selectedBackground: rgba(190,190,190,.4);
-      --treechildren-currentFocusBorder: var(--treechildren-currentColor);
-      --treechildren-selectedFocusBorder: rgb(132,172,221) var(--treechildren-2ndBorderColor) var(--treechildren-currentColor);
-      --treechildren-selectedFocusBackground: rgba(131,183,249,.375);
-      --treechildren-selectedFocusCurrentBorder: var(--treechildren-currentColor);
-      --treechildren-selectedFocusCurrentBackground: rgba(131,183,249,.5);
-      --treechildren-hoverBorder: rgb(184,214,251);
-      --treechildren-hoverBackground: rgba(131,183,249,.16);
-      --treechildren-hoverSelectedBorder: var(--treechildren-currentColor);
-      --treechildren-hoverSelectedBackground: rgba(131,183,249,.5);
-    }
-  }
-
-  @media (-moz-os-version: windows-win7) {
-    treechildren::-moz-tree-row(selected),
-    treechildren::-moz-tree-row(dropOn),
-    treechildren::-moz-tree-row(hover) {
-      background-image: linear-gradient(rgba(255,255,255,.7), transparent);
-    }
-
-    treechildren::-moz-tree-row {
-      border-radius: 3px;
-      -moz-outline-radius: 3px;
-    }
-  }
-}
-%endif
deleted file mode 100644
--- a/toolkit/themes/windows/global/tree/twisty-collapsed-rtl.svg
+++ /dev/null
@@ -1,6 +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 width="9" height="9" xmlns="http://www.w3.org/2000/svg" stroke="context-fill" stroke-width="1.6" fill="none">
-  <path d="m 6.5,0.5 -4,4 4,4"/>
-</svg>
deleted file mode 100644
--- a/toolkit/themes/windows/global/tree/twisty-collapsed.svg
+++ /dev/null
@@ -1,6 +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 width="9" height="9" xmlns="http://www.w3.org/2000/svg" stroke="context-fill" stroke-width="1.6" fill="none">
-  <path d="m 2.5,0.5 4,4 -4,4"/>
-</svg>
deleted file mode 100644
--- a/toolkit/themes/windows/global/tree/twisty-expanded.svg
+++ /dev/null
@@ -1,6 +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 width="9" height="9" xmlns="http://www.w3.org/2000/svg" stroke="context-fill" stroke-width="1.6" fill="none">
-  <path d="m 8.5,2.5 -4,4 -4,-4"/>
-</svg>
--- a/tools/tryselect/push.py
+++ b/tools/tryselect/push.py
@@ -1,14 +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/.
 
 from __future__ import absolute_import, print_function
 
+import hashlib
 import json
 import os
 import sys
 
 from mozboot.util import get_state_dir
 from mozbuild.base import MozbuildObject
 from mozversioncontrol import get_repository_object, MissingVCSExtension
 
@@ -39,17 +40,19 @@ UNCOMMITTED_CHANGES = """
 ERROR please commit changes before continuing
 """.strip()
 
 MAX_HISTORY = 10
 
 here = os.path.abspath(os.path.dirname(__file__))
 build = MozbuildObject.from_environment(cwd=here)
 vcs = get_repository_object(build.topsrcdir)
-history_path = os.path.join(get_state_dir()[0], 'history', 'try_task_configs.json')
+topsrcdir_hash = hashlib.sha256(os.path.abspath(build.topsrcdir)).hexdigest()
+history_path = os.path.join(get_state_dir()[0], 'history', topsrcdir_hash, 'try_task_configs.json')
+old_history_path = os.path.join(get_state_dir()[0], 'history', 'try_task_configs.json')
 
 
 def write_task_config(try_task_config):
     config_path = os.path.join(vcs.path, 'try_task_config.json')
     with open(config_path, 'w') as fh:
         json.dump(try_task_config, fh, indent=2, separators=(',', ':'))
         fh.write('\n')
     return config_path
--- a/tools/tryselect/selectors/again.py
+++ b/tools/tryselect/selectors/again.py
@@ -1,19 +1,20 @@
 # 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 absolute_import, print_function, unicode_literals
 
 import json
 import os
+import shutil
 
 from ..cli import BaseTryParser
-from ..push import push_to_try, history_path
+from ..push import push_to_try, history_path, old_history_path
 
 
 class AgainParser(BaseTryParser):
     name = 'again'
     arguments = [
         [['--index'],
          {'default': 0,
           'type': int,
@@ -32,16 +33,22 @@ class AgainParser(BaseTryParser):
           'action': 'store_true',
           'help': "Remove all history and exit",
           }],
     ]
     common_groups = ['push']
 
 
 def run_try_again(index=0, purge=False, list_configs=False, message='{msg}', **pushargs):
+    # Try to move existing history file from the old location to the new one.
+    if os.path.isfile(old_history_path) and not os.path.isfile(history_path):
+        if not os.path.isdir(os.path.dirname(history_path)):
+            os.makedirs(os.path.dirname(history_path))
+        shutil.move(old_history_path, history_path)
+
     if purge:
         os.remove(history_path)
         return
 
     if not os.path.isfile(history_path):
         print("error: history file not found: {}".format(history_path))
         return 1
 
--- a/xpcom/rust/gtest/nsstring/Test.cpp
+++ b/xpcom/rust/gtest/nsstring/Test.cpp
@@ -139,8 +139,18 @@ TEST(RustNsString, WriteToBufferFromRust
   Rust_WriteToBufferFromRust(&cStr, &str, &fallibleCStr, &fallibleStr);
 
   EXPECT_TRUE(cStr.EqualsASCII("ABC"));
   EXPECT_TRUE(str.EqualsASCII("ABC"));
   EXPECT_TRUE(fallibleCStr.EqualsASCII("ABC"));
   EXPECT_TRUE(fallibleStr.EqualsASCII("ABC"));
 }
 
+extern "C" void Rust_InlineCapacityFromRust(const nsACString* aCStr, const nsAString* aStr, size_t* aCStrCapacity, size_t* aStrCapacity);
+TEST(RustNsString, InlineCapacityFromRust) {
+  size_t cStrCapacity;
+  size_t strCapacity;
+  nsAutoCStringN<93> cs;
+  nsAutoStringN<93> s;
+  Rust_InlineCapacityFromRust(&cs, &s, &cStrCapacity, &strCapacity);
+  EXPECT_EQ(cStrCapacity, 92U);
+  EXPECT_EQ(strCapacity, 92U);
+}