Merge mozilla-central to inbound. a=merge CLOSED TREE
authorNoemi Erli <nerli@mozilla.com>
Thu, 02 May 2019 00:58:39 +0300
changeset 531100 89d5d74f031b79607e7e68c2a273ffb91d2539bd
parent 531099 2aa89383ecb54ec33d95f877ac7cfde429dfab7e (current diff)
parent 530965 c7a22f0ea7b4ce3ff379f5be1d1ad3070aa76317 (diff)
child 531101 11f4bd1a3b416c6b892b3dde7e6069b5de04c97f
push id11265
push userffxbld-merge
push dateMon, 13 May 2019 10:53:39 +0000
treeherdermozilla-beta@77e0fe8dbdd3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone68.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-central to inbound. a=merge CLOSED TREE
devtools/client/themes/images/globe-small.svg
media/webrtc/trunk/webrtc/modules/desktop_capture/app_capturer.cc
media/webrtc/trunk/webrtc/modules/desktop_capture/app_capturer.h
media/webrtc/trunk/webrtc/modules/desktop_capture/app_capturer_linux.cc
media/webrtc/trunk/webrtc/modules/desktop_capture/app_capturer_mac.mm
media/webrtc/trunk/webrtc/modules/desktop_capture/app_capturer_null.cc
media/webrtc/trunk/webrtc/modules/desktop_capture/app_capturer_unittest.cc
media/webrtc/trunk/webrtc/modules/desktop_capture/app_capturer_win.cc
--- a/accessible/base/TextAttrs.cpp
+++ b/accessible/base/TextAttrs.cpp
@@ -712,66 +712,45 @@ void TextAttrsMgr::TextPosTextAttr::Expo
 
     case eTextPosNone:
       break;
   }
 }
 
 TextAttrsMgr::TextPosValue TextAttrsMgr::TextPosTextAttr::GetTextPosValue(
     nsIFrame* aFrame) const {
-  const nsStyleCoord& styleCoord = aFrame->StyleDisplay()->mVerticalAlign;
-  switch (styleCoord.GetUnit()) {
-    case eStyleUnit_Enumerated:
-      switch (styleCoord.GetIntValue()) {
-        case NS_STYLE_VERTICAL_ALIGN_BASELINE:
-          return eTextPosBaseline;
-        case NS_STYLE_VERTICAL_ALIGN_SUB:
-          return eTextPosSub;
-        case NS_STYLE_VERTICAL_ALIGN_SUPER:
-          return eTextPosSuper;
-
-          // No good guess for these:
-          //   NS_STYLE_VERTICAL_ALIGN_TOP
-          //   NS_STYLE_VERTICAL_ALIGN_TEXT_TOP
-          //   NS_STYLE_VERTICAL_ALIGN_MIDDLE
-          //   NS_STYLE_VERTICAL_ALIGN_TEXT_BOTTOM
-          //   NS_STYLE_VERTICAL_ALIGN_BOTTOM
-          //   NS_STYLE_VERTICAL_ALIGN_MIDDLE_WITH_BASELINE
-          // Do not expose value of text-position attribute.
-
-        default:
-          break;
-      }
-      return eTextPosNone;
-
-    case eStyleUnit_Percent: {
-      float percentValue = styleCoord.GetPercentValue();
-      return percentValue > 0
-                 ? eTextPosSuper
-                 : (percentValue < 0 ? eTextPosSub : eTextPosBaseline);
+  const auto& verticalAlign = aFrame->StyleDisplay()->mVerticalAlign;
+  if (verticalAlign.IsKeyword()) {
+    switch (verticalAlign.AsKeyword()) {
+      case StyleVerticalAlignKeyword::Baseline:
+        return eTextPosBaseline;
+      case StyleVerticalAlignKeyword::Sub:
+        return eTextPosSub;
+      case StyleVerticalAlignKeyword::Super:
+        return eTextPosSuper;
+      // No good guess for the rest, so do not expose value of text-position
+      // attribute.
+      default:
+        return eTextPosNone;
     }
-
-    case eStyleUnit_Coord: {
-      nscoord coordValue = styleCoord.GetCoordValue();
-      return coordValue > 0 ? eTextPosSuper
-                            : (coordValue < 0 ? eTextPosSub : eTextPosBaseline);
-    }
-
-    case eStyleUnit_Null:
-    case eStyleUnit_Normal:
-    case eStyleUnit_Auto:
-    case eStyleUnit_None:
-    case eStyleUnit_Factor:
-    case eStyleUnit_Degree:
-    case eStyleUnit_FlexFraction:
-    case eStyleUnit_Integer:
-    case eStyleUnit_Calc:
-      break;
   }
 
-  const nsIContent* content = aFrame->GetContent();
-  if (content) {
+  const auto& length = verticalAlign.AsLength();
+  if (length.ConvertsToPercentage()) {
+    float percentValue = length.ToPercentage();
+    return percentValue > 0
+               ? eTextPosSuper
+               : (percentValue < 0 ? eTextPosSub : eTextPosBaseline);
+  }
+
+  if (length.ConvertsToLength()) {
+    nscoord coordValue = length.ToLength();
+    return coordValue > 0 ? eTextPosSuper
+                          : (coordValue < 0 ? eTextPosSub : eTextPosBaseline);
+  }
+
+  if (const nsIContent* content = aFrame->GetContent()) {
     if (content->IsHTMLElement(nsGkAtoms::sup)) return eTextPosSuper;
     if (content->IsHTMLElement(nsGkAtoms::sub)) return eTextPosSub;
   }
 
   return eTextPosNone;
 }
--- a/browser/actors/WebRTCChild.jsm
+++ b/browser/actors/WebRTCChild.jsm
@@ -336,19 +336,16 @@ function updateIndicators(aSubject, aTop
       state.showMicrophoneIndicator = true;
     }
     if (tabState.screen) {
       if (tabState.screen.startsWith("Screen")) {
         state.showScreenSharingIndicator = "Screen";
       } else if (tabState.screen.startsWith("Window")) {
         if (state.showScreenSharingIndicator != "Screen")
           state.showScreenSharingIndicator = "Window";
-      } else if (tabState.screen.startsWith("Application")) {
-        if (!state.showScreenSharingIndicator)
-          state.showScreenSharingIndicator = "Application";
       } else if (tabState.screen.startsWith("Browser")) {
         if (!state.showScreenSharingIndicator)
           state.showScreenSharingIndicator = "Browser";
       }
     }
 
     let mm = getMessageManagerForWindow(contentWindow);
     mm.sendAsyncMessage("webrtc:UpdateBrowserIndicators", tabState);
@@ -371,35 +368,31 @@ function removeBrowserSpecificIndicator(
     tabState = {windowId: tabState.windowId};
 
   let mm = getMessageManagerForWindow(contentWindow);
   if (mm)
     mm.sendAsyncMessage("webrtc:UpdateBrowserIndicators", tabState);
 }
 
 function getTabStateForContentWindow(aContentWindow) {
-  let camera = {}, microphone = {}, screen = {}, window = {}, app = {}, browser = {};
+  let camera = {}, microphone = {}, screen = {}, window = {}, browser = {};
   MediaManagerService.mediaCaptureWindowState(aContentWindow,
                                               camera, microphone,
-                                              screen, window, app, browser);
+                                              screen, window, browser);
   let tabState = {camera: camera.value, microphone: microphone.value};
   if (screen.value == MediaManagerService.STATE_CAPTURE_ENABLED)
     tabState.screen = "Screen";
   else if (window.value == MediaManagerService.STATE_CAPTURE_ENABLED)
     tabState.screen = "Window";
-  else if (app.value == MediaManagerService.STATE_CAPTURE_ENABLED)
-    tabState.screen = "Application";
   else if (browser.value == MediaManagerService.STATE_CAPTURE_ENABLED)
     tabState.screen = "Browser";
   else if (screen.value == MediaManagerService.STATE_CAPTURE_DISABLED)
     tabState.screen = "ScreenPaused";
   else if (window.value == MediaManagerService.STATE_CAPTURE_DISABLED)
     tabState.screen = "WindowPaused";
-  else if (app.value == MediaManagerService.STATE_CAPTURE_DISABLED)
-    tabState.screen = "ApplicationPaused";
   else if (browser.value == MediaManagerService.STATE_CAPTURE_DISABLED)
     tabState.screen = "BrowserPaused";
 
   let screenEnabled = tabState.screen && !tabState.screen.includes("Paused");
   let cameraEnabled = tabState.camera == MediaManagerService.STATE_CAPTURE_ENABLED;
   let microphoneEnabled = tabState.microphone == MediaManagerService.STATE_CAPTURE_ENABLED;
 
   // tabState.sharing controls which global indicator should be shown
--- a/browser/base/content/test/webrtc/get_user_media_content_script.js
+++ b/browser/base/content/test/webrtc/get_user_media_content_script.js
@@ -65,35 +65,32 @@ addMessageListener("Test:ExpectNoObserve
   gObservedTopics = {};
 });
 
 function _getMediaCaptureState() {
   let hasCamera = {};
   let hasMicrophone = {};
   let hasScreenShare = {};
   let hasWindowShare = {};
-  let hasAppShare = {};
   let hasBrowserShare = {};
   MediaManagerService.mediaCaptureWindowState(content,
                                               hasCamera, hasMicrophone,
                                               hasScreenShare, hasWindowShare,
-                                              hasAppShare, hasBrowserShare);
+                                              hasBrowserShare);
   let result = {};
 
   if (hasCamera.value != MediaManagerService.STATE_NOCAPTURE)
     result.video = true;
   if (hasMicrophone.value != MediaManagerService.STATE_NOCAPTURE)
     result.audio = true;
 
   if (hasScreenShare.value != MediaManagerService.STATE_NOCAPTURE)
     result.screen = "Screen";
   else if (hasWindowShare.value != MediaManagerService.STATE_NOCAPTURE)
     result.screen = "Window";
-  else if (hasAppShare.value != MediaManagerService.STATE_NOCAPTURE)
-    result.screen = "Application";
   else if (hasBrowserShare.value != MediaManagerService.STATE_NOCAPTURE)
     result.screen = "Browser";
 
   return result;
 }
 
 addMessageListener("Test:GetMediaCaptureState", data => {
   sendAsyncMessage("Test:MediaCaptureState", _getMediaCaptureState());
--- a/browser/components/extensions/parent/ext-chrome-settings-overrides.js
+++ b/browser/components/extensions/parent/ext-chrome-settings-overrides.js
@@ -127,17 +127,20 @@ this.chrome_settings_overrides = class e
     }
     // We can call removeEngine in nsSearchService startup, if so we dont
     // need to reforward the call, just disable the web extension.
     if (!Services.search.isInitialized) {
       return;
     }
 
     try {
-      await Services.search.removeWebExtensionEngine(id);
+      let engines = await Services.search.getEnginesByExtensionID(id);
+      if (engines.length > 0) {
+        await Services.search.removeWebExtensionEngine(id);
+      }
     } catch (e) {
       Cu.reportError(e);
     }
   }
 
   static removeSearchSettings(id) {
     return Promise.all([
       this.processDefaultSearchSetting("removeSetting", id),
--- a/browser/components/search/content/search-one-offs.js
+++ b/browser/components/search/content/search-one-offs.js
@@ -824,18 +824,18 @@ class SearchOneOffs {
 
   /**
    * This handles key presses specific to the one-off buttons like Tab and
    * Alt+Up/Down, and Up/Down keys within the buttons.  Since one-off buttons
    * are always used in conjunction with a list of some sort (in this.popup),
    * it also handles Up/Down keys that cross the boundaries between list
    * items and the one-off buttons.
    *
-   * If this method handles the key press, then event.defaultPrevented will
-   * be true when it returns.
+   * If this method handles the key press, then it will call
+   * event.preventDefault() and return true.
    *
    * @param {Event} event
    *        The key event.
    * @param {number} numListItems
    *        The number of items in the list.  The reason that this is a
    *        parameter at all is that the list may contain items at the end
    *        that should be ignored, depending on the consumer.  That's true
    *        for the urlbar for example.
@@ -844,28 +844,30 @@ class SearchOneOffs {
    *        buttons contains a selection.  Pass false if either the list or
    *        the one-off buttons (or both) should always contain a selection.
    * @param {string} [textboxUserValue]
    *        When the last list item is selected and the user presses Down,
    *        the first one-off becomes selected and the textbox value is
    *        restored to the value that the user typed.  Pass that value here.
    *        However, if you pass true for allowEmptySelection, you don't need
    *        to pass anything for this parameter.  (Pass undefined or null.)
+   * @returns {boolean} True if the one-offs handled the key press.
    */
   handleKeyPress(event, numListItems, allowEmptySelection, textboxUserValue) {
     if (!this.popup) {
-      return;
+      return false;
     }
     let handled = this._handleKeyPress(event, numListItems,
       allowEmptySelection,
       textboxUserValue);
     if (handled) {
       event.preventDefault();
       event.stopPropagation();
     }
+    return handled;
   }
 
   _handleKeyPress(event, numListItems, allowEmptySelection, textboxUserValue) {
     if (this.compact && this.buttons.collapsed) {
       return false;
     }
     if (event.keyCode == KeyEvent.DOM_VK_RIGHT &&
         this.selectedButton &&
--- a/browser/components/urlbar/UrlbarController.jsm
+++ b/browser/components/urlbar/UrlbarController.jsm
@@ -253,22 +253,22 @@ class UrlbarController {
       }
       event.preventDefault();
       return;
     }
 
     if (this.view.isOpen && executeAction) {
       let queryContext = this._lastQueryContext;
       if (queryContext) {
-        this.view.oneOffSearchButtons.handleKeyPress(
+        let handled = this.view.oneOffSearchButtons.handleKeyPress(
           event,
           queryContext.results.length,
           this.view.allowEmptySelection,
           queryContext.searchString);
-        if (event.defaultPrevented) {
+        if (handled) {
           return;
         }
       }
     }
 
     switch (event.keyCode) {
       case KeyEvent.DOM_VK_ESCAPE:
         if (executeAction) {
--- a/browser/components/urlbar/UrlbarProviderUnifiedComplete.jsm
+++ b/browser/components/urlbar/UrlbarProviderUnifiedComplete.jsm
@@ -122,20 +122,22 @@ class ProviderUnifiedComplete extends Ur
     await new Promise(resolve => {
       let listener = {
         onSearchResult(_, result) {
           let {done, matches} = convertResultToMatches(queryContext, result, urls);
           for (let match of matches) {
             addCallback(UrlbarProviderUnifiedComplete, match);
           }
           if (done) {
+            delete this._resolveSearch;
             resolve();
           }
         },
       };
+      this._resolveSearch = resolve;
       unifiedComplete.startSearch(queryContext.searchString,
                                   params.join(" "),
                                   null, // previousResult
                                   listener);
     });
 
     // We are done.
     this.queries.delete(queryContext);
@@ -145,16 +147,19 @@ class ProviderUnifiedComplete extends Ur
    * Cancels a running query.
    * @param {object} queryContext The query context object
    */
   cancelQuery(queryContext) {
     logger.info(`Canceling query for ${queryContext.searchString}`);
     // This doesn't properly support being used concurrently by multiple fields.
     this.queries.delete(queryContext);
     unifiedComplete.stopSearch();
+    if (this._resolveSearch) {
+      this._resolveSearch();
+    }
   }
 }
 
 var UrlbarProviderUnifiedComplete = new ProviderUnifiedComplete();
 
 /**
  * Convert from a nsIAutocompleteResult to a list of new matches.
  * Note that at every call we get the full set of matches, included the
--- a/browser/components/urlbar/tests/browser/browser.ini
+++ b/browser/components/urlbar/tests/browser/browser.ini
@@ -161,17 +161,16 @@ support-files =
 [browser_urlbarSearchSuggestions.js]
 support-files =
   searchSuggestionEngine.xml
   searchSuggestionEngine.sjs
 [browser_URLBarSetURI.js]
 skip-if = (os == "linux" || os == "mac") && debug # bug 970052, bug 970053
 [browser_urlbarStop.js]
 [browser_urlbarStopSearchOnSelection.js]
-skip-if = true # Bug 1524510 - Not implemented yet.
 support-files =
   searchSuggestionEngineSlow.xml
   searchSuggestionEngine.sjs
 [browser_urlbarTokenAlias.js]
 [browser_urlbarUpdateForDomainCompletion.js]
 [browser_urlbarValueOnTabSwitch.js]
 [browser_userTypedValue.js]
 support-files = file_userTypedValue.html
--- a/browser/components/urlbar/tests/browser/browser_urlbarStopSearchOnSelection.js
+++ b/browser/components/urlbar/tests/browser/browser_urlbarStopSearchOnSelection.js
@@ -8,17 +8,17 @@
  */
 
 "use strict";
 
 const TEST_ENGINE_BASENAME = "searchSuggestionEngineSlow.xml";
 
 // This should match the `timeout` query param used in the suggestions URL in
 // the test engine.
-const TEST_ENGINE_SUGGESTIONS_TIMEOUT = 1000;
+const TEST_ENGINE_SUGGESTIONS_TIMEOUT = 3000;
 
 // The number of suggestions returned by the test engine.
 const TEST_ENGINE_NUM_EXPECTED_RESULTS = 2;
 
 add_task(async function init() {
   await PlacesUtils.history.clear();
   await SpecialPowers.pushPrefEnv({
     set: [["browser.urlbar.suggest.searches", true]],
@@ -31,53 +31,85 @@ add_task(async function init() {
   await Services.search.setDefault(engine);
   registerCleanupFunction(async () => {
     await Services.search.setDefault(oldDefaultEngine);
     await PlacesUtils.history.clear();
   });
 });
 
 add_task(async function mainTest() {
-  // Trigger an initial search.  Restrict matches to search suggestions.
-  await promiseAutocompleteResultPopup(`${UrlbarTokenizer.RESTRICT.SEARCH} test`, window);
-  await promiseSuggestionsPresent("Waiting for initial suggestions");
-
-  // Now synthesize typing a character.  promiseAutocompleteResultPopup doesn't
-  // work in this case because it causes the popup to close and re-open with the
-  // new input text.
-  await new Promise(r => EventUtils.synthesizeKey("x", {}, window, r));
+  // Open a tab that will match the search string below so that we're guaranteed
+  // to have more than one result (the heuristic result) so that we can change
+  // the selected result.  We open a tab instead of adding a page in history
+  // because open tabs are kept in a memory SQLite table, so open-tab results
+  // are more likely than history results to be fetched before our slow search
+  // suggestions.  This is important when the test runs on slow debug builds on
+  // slow machines.
+  await BrowserTestUtils.withNewTab("http://example.com/", async () => {
+    await BrowserTestUtils.withNewTab("about:blank", async () => {
+      // Do an initial search.  There should be 4 results: heuristic, open tab,
+      // and the two suggestions.
+      await promiseAutocompleteResultPopup("amp");
+      await TestUtils.waitForCondition(() => {
+        return UrlbarTestUtils.getResultCount(window) ==
+               2 + TEST_ENGINE_NUM_EXPECTED_RESULTS;
+      });
 
-  // At this point, a new search starts, the autocomplete's _invalidate is
-  // called, _appendCurrentResult is called, and matchCount remains 3 from the
-  // previous search (the heuristic result plus the two search suggestions).
-  // The suggestion results should not outwardly change, however, since the
-  // autocomplete controller should still be returning the initial suggestions.
-  // What has changed, though, is the search string, which is kept in the
-  // results' ac-text attributes.  Call waitForAutocompleteResultAt to wait for
-  // the results' ac-text to be set to the new search string, "testx", to make
-  // sure the autocomplete has seen the new search.
-  await waitForAutocompleteResultAt(TEST_ENGINE_NUM_EXPECTED_RESULTS);
+      // Type a character to start a new search.  The new search should still
+      // match the open tab so that the open-tab result appears again.
+      EventUtils.synthesizeKey("l");
 
-  // Now press the Down arrow key to change the selection.
-  await new Promise(r => EventUtils.synthesizeKey("VK_DOWN", {}, window, r));
+      // There should be 2 results immediately: heuristic and open tab.  (On
+      // quantumbar, there will be only 2, but on awesomebar, there will be 4
+      // since awesomebar will keep showing the excess old results for a bit.)
+      await TestUtils.waitForCondition(() => {
+        return UrlbarTestUtils.getResultCount(window) >= 2;
+      });
+
+      // Before the search completes, change the selected result.  Pressing only
+      // the down arrow key ends up selecting the first one-off on Linux debug
+      // builds on the infrastructure for some reason, so arrow back up to
+      // select the heuristic result again.  The important thing is to change
+      // the selection.  It doesn't matter which result ends up selected.
+      EventUtils.synthesizeKey("KEY_ArrowDown");
+      EventUtils.synthesizeKey("KEY_ArrowUp");
 
-  // Changing the selection should have stopped the new search triggered by
-  // typing the "x" character.  Wait a bit to make sure it really stopped.
-  await new Promise(r => setTimeout(r, 2 * TEST_ENGINE_SUGGESTIONS_TIMEOUT));
+      // Wait for the new search to complete.  It should be canceled due to the
+      // selection change, but it should still complete.
+      await promiseSearchComplete();
 
-  // Both of the suggestion results should retain their initial values,
-  // "testfoo" and "testbar".  They should *not* be "testxfoo" and "textxbar".
+      // To make absolutely sure the suggestions don't appear after the search
+      // completes, wait a bit.
+      await new Promise(r => setTimeout(r, 1 + TEST_ENGINE_SUGGESTIONS_TIMEOUT));
+
+      // The heuristic result should reflect the new search, "ampl".
+      let result = await UrlbarTestUtils.getDetailsOfResultAt(window, 0);
+      Assert.equal(result.type, UrlbarUtils.RESULT_TYPE.SEARCH,
+        "Should have the correct result type");
+      Assert.equal(result.searchParams.query, "ampl",
+        "Should have the correct query");
 
-  // + 1 for the heuristic result
-  let numExpectedResults = TEST_ENGINE_NUM_EXPECTED_RESULTS + 1;
-  Assert.equal(UrlbarTestUtils.getResultCount(window), numExpectedResults,
-    "Should have the correct number of results displayed");
-
-  let expectedSuggestions = ["testfoo", "testbar"];
-  for (let i = 0; i < TEST_ENGINE_NUM_EXPECTED_RESULTS; i++) {
-    // + 1 to skip the heuristic result
-    let result = await UrlbarTestUtils.getDetailsOfResultAt(window, i + 1);
-    Assert.equal(result.type, UrlbarUtils.RESULT_TYPE.SEARCH,
-      "Should have the correct result type");
-    Assert.equal(result.searchParams.suggestion, expectedSuggestions[i],
-      "Should have the correct suggestion");
-  }
+      // None of the other results should be "ampl" suggestions, i.e., amplfoo
+      // and amplbar should not be in the results.
+      let count = UrlbarTestUtils.getResultCount(window);
+      for (let i = 1; i < count; i++) {
+        if (!UrlbarPrefs.get("quantumbar")) {
+          // On awesomebar, UrlbarTestUtils.getDetailsOfResultAt() can throw due
+          // to its urlbar.controller.getFinalCompleteValueAt() call.  That's
+          // because the results at this point may contain results from two
+          // different searches.  This only seems to happen on --verify runs.
+          try {
+            gURLBar.controller.getFinalCompleteValueAt(i);
+          } catch (ex) {
+            info("getFinalCompleteValueAt exception: " + ex);
+            continue;
+          }
+        }
+        result = await UrlbarTestUtils.getDetailsOfResultAt(window, i);
+        Assert.ok(
+          result.type != UrlbarUtils.RESULT_TYPE.SEARCH ||
+          !["amplfoo", "amplbar"].includes(result.searchParams.suggestion),
+          "Suggestions should not contain the typed l char"
+        );
+      }
+    });
+  });
 });
--- a/browser/components/urlbar/tests/browser/searchSuggestionEngineSlow.xml
+++ b/browser/components/urlbar/tests/browser/searchSuggestionEngineSlow.xml
@@ -1,11 +1,11 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <!-- Any copyright is dedicated to the Public Domain.
    - http://creativecommons.org/publicdomain/zero/1.0/ -->
 
 <SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
 <ShortName>searchSuggestionEngineSlow.xml</ShortName>
-<Url type="application/x-suggestions+json" method="GET" template="http://mochi.test:8888/browser/browser/components/urlbar/tests/browser/searchSuggestionEngine.sjs?query={searchTerms}&amp;timeout=1000"/>
+<Url type="application/x-suggestions+json" method="GET" template="http://mochi.test:8888/browser/browser/components/urlbar/tests/browser/searchSuggestionEngine.sjs?query={searchTerms}&amp;timeout=3000"/>
 <Url type="text/html" method="GET" template="http://mochi.test:8888/" rel="searchform">
   <Param name="terms" value="{searchTerms}"/>
 </Url>
 </SearchPlugin>
--- a/devtools/client/debugger/test/mochitest/browser.ini
+++ b/devtools/client/debugger/test/mochitest/browser.ini
@@ -786,12 +786,13 @@ skip-if = os == "win"
 [browser_dbg-toggling-tools.js]
 [browser_dbg-react-app.js]
 skip-if = os == "win"
 [browser_dbg-wasm-sourcemaps.js]
 skip-if = true
 [browser_dbg-windowless-workers.js]
 [browser_dbg-windowless-workers-early-breakpoint.js]
 [browser_dbg-worker-scopes.js]
+skip-if = (os == 'linux' && debug) || ccov #Bug 1456013
 [browser_dbg-event-handler.js]
 [browser_dbg-eval-throw.js]
 [browser_dbg-sourceURL-breakpoint.js]
 [browser_dbg-old-breakpoint.js]
--- a/devtools/client/jar.mn
+++ b/devtools/client/jar.mn
@@ -155,17 +155,16 @@ devtools.jar:
     skin/badge.css (themes/badge.css)
     skin/inspector.css (themes/inspector.css)
     skin/images/profiler-stopwatch.svg (themes/images/profiler-stopwatch.svg)
     skin/images/debugging-addons.svg (themes/images/debugging-addons.svg)
     skin/images/debugging-tabs.svg (themes/images/debugging-tabs.svg)
     skin/images/debugging-workers.svg (themes/images/debugging-workers.svg)
     skin/images/datastore.svg (themes/images/datastore.svg)
     skin/images/globe.svg (themes/images/globe.svg)
-    skin/images/globe-small.svg (themes/images/globe-small.svg)
     skin/images/next.svg (themes/images/next.svg)
     skin/images/next-circle.svg (themes/images/next-circle.svg)
     skin/images/folder.svg (themes/images/folder.svg)
     skin/images/sad-face.svg (themes/images/sad-face.svg)
     skin/images/shape-swatch.svg (themes/images/shape-swatch.svg)
     skin/images/tool-webconsole.svg (themes/images/tool-webconsole.svg)
     skin/images/tool-debugger.svg (themes/images/tool-debugger.svg)
     skin/images/tool-inspector.svg (themes/images/tool-inspector.svg)
--- a/devtools/client/netmonitor/src/assets/styles/RequestList.css
+++ b/devtools/client/netmonitor/src/assets/styles/RequestList.css
@@ -290,20 +290,16 @@
 .security-state-weak {
   background-image: url(chrome://devtools/skin/images/security-state-weak.svg);
 }
 
 .security-state-broken {
   background-image: url(chrome://devtools/skin/images/security-state-broken.svg);
 }
 
-.security-state-local {
-  background-image: url(chrome://devtools/skin/images/globe-small.svg);
-}
-
 .tracking-resource {
   display: inline-block;
   width: 16px;
   height: 16px;
   margin: 0 3px 0 -3px;
   vertical-align: middle;
   background-image: url(chrome://devtools/content/netmonitor/src/assets/icons/shield.svg);
   background-repeat: no-repeat;
--- a/devtools/client/netmonitor/src/components/RequestListColumnDomain.js
+++ b/devtools/client/netmonitor/src/components/RequestListColumnDomain.js
@@ -35,22 +35,27 @@ class RequestListColumnDomain extends Co
     const { item, onSecurityIconMouseDown } = this.props;
     const { remoteAddress, remotePort, securityState,
       urlDetails: { host, isLocal } } = item;
     const iconClassList = ["requests-security-state-icon"];
     let iconTitle;
     const title = host + (remoteAddress ?
       ` (${getFormattedIPAndPort(remoteAddress, remotePort)})` : "");
 
+    let realSecurityState = securityState;
+
+    // Locally delivered files such as http://localhost and file:// paths
+    // are considered to have been delivered securely.
     if (isLocal) {
-      iconClassList.push("security-state-local");
-      iconTitle = L10N.getStr("netmonitor.security.state.secure");
-    } else if (securityState) {
-      iconClassList.push(`security-state-${securityState}`);
-      iconTitle = L10N.getStr(`netmonitor.security.state.${securityState}`);
+      realSecurityState = "secure";
+    }
+
+    if (realSecurityState) {
+      iconClassList.push(`security-state-${realSecurityState}`);
+      iconTitle = L10N.getStr(`netmonitor.security.state.${realSecurityState}`);
     }
 
     return (
       dom.td({ className: "requests-list-column requests-list-domain", title },
         div({
           className: iconClassList.join(" "),
           onMouseDown: onSecurityIconMouseDown,
           title: iconTitle,
--- a/devtools/client/netmonitor/test/browser_net_security-state.js
+++ b/devtools/client/netmonitor/test/browser_net_security-state.js
@@ -8,35 +8,39 @@
  * state.
  */
 
 add_task(async function() {
   const EXPECTED_SECURITY_STATES = {
     "test1.example.com": "security-state-insecure",
     "example.com": "security-state-secure",
     "nocert.example.com": "security-state-broken",
-    "localhost": "security-state-local",
+    "localhost": "security-state-secure",
   };
 
   const { tab, monitor } = await initNetMonitor(CUSTOM_GET_URL);
   const { document, store, windowRequire } = monitor.panelWin;
   const Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
 
   store.dispatch(Actions.batchEnable(false));
 
   await performRequests();
 
   for (const subitemNode of Array.from(document.querySelectorAll(
-    "requests-list-column.requests-list-security-and-domain"))) {
-    const domain = subitemNode.querySelector(".requests-list-domain").textContent;
+    ".requests-list-column.requests-list-domain"))) {
+    // Skip header
+    const icon = subitemNode.querySelector(".requests-security-state-icon");
+    if (!icon) {
+      continue;
+    }
 
+    const domain = subitemNode.textContent;
     info("Found a request to " + domain);
-    ok(domain in EXPECTED_SECURITY_STATES, "Domain " + domain + " was expected.");
 
-    const classes = subitemNode.querySelector(".requests-security-state-icon").classList;
+    const classes = icon.classList;
     const expectedClass = EXPECTED_SECURITY_STATES[domain];
 
     info("Classes of security state icon are: " + classes);
     info("Security state icon is expected to contain class: " + expectedClass);
     ok(classes.contains(expectedClass), "Icon contained the correct class name.");
   }
 
   return teardown(monitor);
deleted file mode 100644
--- a/devtools/client/themes/images/globe-small.svg
+++ /dev/null
@@ -1,7 +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 xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12 12" width="12" height="12">
-  <path fill="context-fill" d="M6 1a5 5 0 1 0 0 10A5 5 0 0 0 6 1zM0 6a6 6 0 1 1 12 0A6 6 0 0 1 0 6zM1 4h10v1H1V4zM1 7h10v1H1V7z"/>
-  <path fill="context-fill" fill-rule="evenodd" d="M7.23 9.8C7.69 8.88 8 7.54 8 6s-.31-2.88-.77-3.8C6.73 1.23 6.25 1 6 1s-.74.23-1.23 1.2A8.74 8.74 0 0 0 4 6c0 1.54.31 2.88.77 3.8.5.97.98 1.2 1.23 1.2s.74-.23 1.23-1.2zM6 12c1.66 0 3-2.69 3-6S7.66 0 6 0 3 2.69 3 6s1.34 6 3 6z"/>
-</svg>
--- a/dom/base/Document.cpp
+++ b/dom/base/Document.cpp
@@ -4034,16 +4034,20 @@ void Document::DeletePresShell() {
   ImageTracker()->RequestDiscardAll();
 
   // Now that we no longer have a shell, we need to forget about any FontFace
   // objects for @font-face rules that came from the style set. There's no need
   // to call EnsureStyleFlush either, the shell is going away anyway, so there's
   // no point on it.
   MarkUserFontSetDirty();
 
+  if (mResizeObserverController) {
+    mResizeObserverController->ShellDetachedFromDocument();
+  }
+
   PresShell* oldPresShell = mPresShell;
   mPresShell = nullptr;
   UpdateFrameRequestCallbackSchedulingState(oldPresShell);
 
   ClearStaleServoData();
   AssertNoStaleServoDataIn(*this);
 
   mStyleSet->ShellDetachedFromDocument();
--- a/dom/base/Element.cpp
+++ b/dom/base/Element.cpp
@@ -2590,17 +2590,17 @@ bool Element::ParseAttribute(int32_t aNa
                              nsIPrincipal* aMaybeScriptedPrincipal,
                              nsAttrValue& aResult) {
   if (aAttribute == nsGkAtoms::lang) {
     aResult.ParseAtom(aValue);
     return true;
   }
 
   if (aNamespaceID == kNameSpaceID_None) {
-    if (aAttribute == nsGkAtoms::_class) {
+    if (aAttribute == nsGkAtoms::_class || aAttribute == nsGkAtoms::part) {
       aResult.ParseAtomArray(aValue);
       return true;
     }
 
     if (aAttribute == nsGkAtoms::id) {
       // Store id as an atom.  id="" means that the element has no id,
       // not that it has an emptystring as the id.
       if (aValue.IsEmpty()) {
--- a/dom/base/ResizeObserverController.cpp
+++ b/dom/base/ResizeObserverController.cpp
@@ -12,20 +12,19 @@
 #include "nsPresContext.h"
 #include "nsRefreshDriver.h"
 #include <limits>
 
 namespace mozilla {
 namespace dom {
 
 void ResizeObserverNotificationHelper::WillRefresh(TimeStamp aTime) {
-  MOZ_ASSERT(mOwner,
-             "Why mOwner already dead when this RefreshObserver still "
-             "registered?");
+  MOZ_DIAGNOSTIC_ASSERT(mOwner, "Should've de-registered on-time!");
   mOwner->Notify();
+  // Note that mOwner may be null / dead here.
 }
 
 nsRefreshDriver* ResizeObserverNotificationHelper::GetRefreshDriver() const {
   PresShell* presShell = mOwner->GetPresShell();
   if (MOZ_UNLIKELY(!presShell)) {
     return nullptr;
   }
 
@@ -54,46 +53,48 @@ void ResizeObserverNotificationHelper::R
 }
 
 void ResizeObserverNotificationHelper::Unregister() {
   if (!mRegistered) {
     return;
   }
 
   nsRefreshDriver* refreshDriver = GetRefreshDriver();
-  if (!refreshDriver) {
-    // We can't access RefreshDriver now. Just abort the Unregister().
-    return;
-  }
+  MOZ_RELEASE_ASSERT(refreshDriver,
+                     "We should not leave a dangling reference to the observer around");
 
-  DebugOnly<bool> rv =
-      refreshDriver->RemoveRefreshObserver(this, FlushType::Display);
-  MOZ_ASSERT(rv, "Should remove the observer successfully");
+  bool rv = refreshDriver->RemoveRefreshObserver(this, FlushType::Display);
+  MOZ_DIAGNOSTIC_ASSERT(rv, "Should remove the observer successfully");
 
   mRegistered = false;
 }
 
 ResizeObserverNotificationHelper::~ResizeObserverNotificationHelper() {
-  Unregister();
+  MOZ_RELEASE_ASSERT(!mRegistered, "How can we die when registered?");
+  MOZ_RELEASE_ASSERT(!mOwner, "Forgot to clear weak pointer?");
 }
 
 void ResizeObserverController::Traverse(
     nsCycleCollectionTraversalCallback& aCb) {
   ImplCycleCollectionTraverse(aCb, mResizeObservers, "mResizeObservers");
 }
 
 void ResizeObserverController::Unlink() { mResizeObservers.Clear(); }
 
 void ResizeObserverController::AddResizeObserver(ResizeObserver* aObserver) {
   MOZ_ASSERT(aObserver,
              "AddResizeObserver() should never be called with a null "
              "parameter");
   mResizeObservers.AppendElement(aObserver);
 }
 
+void ResizeObserverController::ShellDetachedFromDocument() {
+  mResizeObserverNotificationHelper->Unregister();
+}
+
 void ResizeObserverController::Notify() {
   if (mResizeObservers.IsEmpty()) {
     return;
   }
 
   // We may call BroadcastAllActiveObservations(), which might cause mDocument
   // to be destroyed (and the ResizeObserverController with it).
   // e.g. if mDocument is in an iframe, and the observer JS removes it from the
@@ -214,13 +215,16 @@ bool ResizeObserverController::HasAnySki
   return false;
 }
 
 void ResizeObserverController::ScheduleNotification() {
   mResizeObserverNotificationHelper->Register();
 }
 
 ResizeObserverController::~ResizeObserverController() {
-  mResizeObserverNotificationHelper->Unregister();
+  MOZ_RELEASE_ASSERT(
+      !mResizeObserverNotificationHelper->IsRegistered(),
+      "Nothing else should keep a reference to our helper when we go away");
+  mResizeObserverNotificationHelper->DetachFromOwner();
 }
 
 }  // namespace dom
 }  // namespace mozilla
--- a/dom/base/ResizeObserverController.h
+++ b/dom/base/ResizeObserverController.h
@@ -37,20 +37,24 @@ class ResizeObserverNotificationHelper f
   MOZ_CAN_RUN_SCRIPT void WillRefresh(TimeStamp aTime) override;
 
   nsRefreshDriver* GetRefreshDriver() const;
 
   void Register();
 
   void Unregister();
 
+  bool IsRegistered() const { return mRegistered; }
+
+  void DetachFromOwner() { mOwner = nullptr; }
+
  private:
   virtual ~ResizeObserverNotificationHelper();
 
-  ResizeObserverController* const mOwner;
+  ResizeObserverController* mOwner;
   bool mRegistered;
 };
 
 /**
  * ResizeObserverController contains the list of ResizeObservers and controls
  * the flow of notification.
  */
 class ResizeObserverController final {
@@ -61,16 +65,17 @@ class ResizeObserverController final {
             new ResizeObserverNotificationHelper(this)) {
     MOZ_ASSERT(mDocument, "Need a non-null document");
   }
 
   // Methods for supporting cycle-collection
   void Traverse(nsCycleCollectionTraversalCallback& aCb);
   void Unlink();
 
+  void ShellDetachedFromDocument();
   void AddResizeObserver(ResizeObserver* aObserver);
 
   /**
    * Schedule the notification via ResizeObserverNotificationHelper refresh
    * observer.
    */
   void ScheduleNotification();
 
--- a/dom/html/HTMLLegendElement.cpp
+++ b/dom/html/HTMLLegendElement.cpp
@@ -32,18 +32,16 @@ bool HTMLLegendElement::ParseAttribute(i
                                        const nsAString& aValue,
                                        nsIPrincipal* aMaybeScriptedPrincipal,
                                        nsAttrValue& aResult) {
   // this contains center, because IE4 does
   static const nsAttrValue::EnumTable kAlignTable[] = {
       {"left", NS_STYLE_TEXT_ALIGN_LEFT},
       {"right", NS_STYLE_TEXT_ALIGN_RIGHT},
       {"center", NS_STYLE_TEXT_ALIGN_CENTER},
-      {"bottom", NS_STYLE_VERTICAL_ALIGN_BOTTOM},
-      {"top", NS_STYLE_VERTICAL_ALIGN_TOP},
       {nullptr, 0}};
 
   if (aAttribute == nsGkAtoms::align && aNamespaceID == kNameSpaceID_None) {
     return aResult.ParseEnumValue(aValue, kAlignTable, false);
   }
 
   return nsGenericHTMLElement::ParseAttribute(aNamespaceID, aAttribute, aValue,
                                               aMaybeScriptedPrincipal, aResult);
--- a/dom/html/nsGenericHTMLElement.cpp
+++ b/dom/html/nsGenericHTMLElement.cpp
@@ -962,39 +962,39 @@ static const nsAttrValue::EnumTable kFra
 
 static const nsAttrValue::EnumTable kScrollingTable[] = {
     {"yes", NS_STYLE_FRAME_YES},       {"no", NS_STYLE_FRAME_NO},
     {"on", NS_STYLE_FRAME_ON},         {"off", NS_STYLE_FRAME_OFF},
     {"scroll", NS_STYLE_FRAME_SCROLL}, {"noscroll", NS_STYLE_FRAME_NOSCROLL},
     {"auto", NS_STYLE_FRAME_AUTO},     {nullptr, 0}};
 
 static const nsAttrValue::EnumTable kTableVAlignTable[] = {
-    {"top", NS_STYLE_VERTICAL_ALIGN_TOP},
-    {"middle", NS_STYLE_VERTICAL_ALIGN_MIDDLE},
-    {"bottom", NS_STYLE_VERTICAL_ALIGN_BOTTOM},
-    {"baseline", NS_STYLE_VERTICAL_ALIGN_BASELINE},
+    {"top", StyleVerticalAlignKeyword::Top},
+    {"middle", StyleVerticalAlignKeyword::Middle},
+    {"bottom", StyleVerticalAlignKeyword::Bottom},
+    {"baseline", StyleVerticalAlignKeyword::Baseline},
     {nullptr, 0}};
 
 bool nsGenericHTMLElement::ParseAlignValue(const nsAString& aString,
                                            nsAttrValue& aResult) {
   static const nsAttrValue::EnumTable kAlignTable[] = {
       {"left", NS_STYLE_TEXT_ALIGN_LEFT},
       {"right", NS_STYLE_TEXT_ALIGN_RIGHT},
 
-      {"top", NS_STYLE_VERTICAL_ALIGN_TOP},
-      {"middle", NS_STYLE_VERTICAL_ALIGN_MIDDLE_WITH_BASELINE},
-      {"bottom", NS_STYLE_VERTICAL_ALIGN_BASELINE},
-
-      {"center", NS_STYLE_VERTICAL_ALIGN_MIDDLE_WITH_BASELINE},
-      {"baseline", NS_STYLE_VERTICAL_ALIGN_BASELINE},
-
-      {"texttop", NS_STYLE_VERTICAL_ALIGN_TEXT_TOP},
-      {"absmiddle", NS_STYLE_VERTICAL_ALIGN_MIDDLE},
-      {"abscenter", NS_STYLE_VERTICAL_ALIGN_MIDDLE},
-      {"absbottom", NS_STYLE_VERTICAL_ALIGN_BOTTOM},
+      {"top", StyleVerticalAlignKeyword::Top},
+      {"middle", StyleVerticalAlignKeyword::MozMiddleWithBaseline},
+      {"bottom", StyleVerticalAlignKeyword::Bottom},
+
+      {"center", StyleVerticalAlignKeyword::MozMiddleWithBaseline},
+      {"baseline", StyleVerticalAlignKeyword::Baseline},
+
+      {"texttop", StyleVerticalAlignKeyword::TextTop},
+      {"absmiddle", StyleVerticalAlignKeyword::Middle},
+      {"abscenter", StyleVerticalAlignKeyword::Middle},
+      {"absbottom", StyleVerticalAlignKeyword::Bottom},
       {nullptr, 0}};
 
   return aResult.ParseEnumValue(aString, kAlignTable, false);
 }
 
 //----------------------------------------
 
 static const nsAttrValue::EnumTable kTableHAlignTable[] = {
--- a/dom/media/MediaManager.cpp
+++ b/dom/media/MediaManager.cpp
@@ -1254,17 +1254,16 @@ class GetUserMediaStreamRunnable : publi
         MOZ_ASSERT(IsOn(mConstraints.mVideo));
         RefPtr<MediaStreamTrack> track = domStream->CreateDOMTrack(
             kVideoTrack, MediaSegment::VIDEO, videoSource,
             GetInvariant(mConstraints.mVideo));
         domStream->AddTrackInternal(track);
         switch (source) {
           case MediaSourceEnum::Browser:
           case MediaSourceEnum::Screen:
-          case MediaSourceEnum::Application:
           case MediaSourceEnum::Window:
             // Wait for first frame for screen-sharing devices, to ensure
             // with and height settings are available immediately, to pass wpt.
             firstFramePromise = mVideoDevice->mSource->GetFirstFramePromise();
             break;
           default:
             break;
         }
@@ -2460,17 +2459,16 @@ RefPtr<MediaManager::StreamPromise> Medi
         // Functional defaults are helpful in tests, but also a natural outcome
         // of the constraints API's limited semantics for requiring input.
         if (!vc.mBrowserWindow.WasPassed()) {
           nsPIDOMWindowOuter* outer = aWindow->GetOuterWindow();
           vc.mBrowserWindow.Construct(outer->WindowID());
         }
         MOZ_FALLTHROUGH;
       case MediaSourceEnum::Screen:
-      case MediaSourceEnum::Application:
       case MediaSourceEnum::Window:
         // Deny screensharing request if support is disabled, or
         // the requesting document is not from a host on the whitelist.
         if (!Preferences::GetBool(
                 ((videoType == MediaSourceEnum::Browser)
                      ? "media.getusermedia.browser.enabled"
                      : "media.getusermedia.screensharing.enabled"),
                 false) ||
@@ -2519,17 +2517,16 @@ RefPtr<MediaManager::StreamPromise> Medi
       //
       // For UX reasons we don't want "Entire Screen" to be the first/default
       // choice (in our code first=default). It's a "scary" source that comes
       // with complicated warnings on-top that would be confusing as the first
       // thing people see, and also deserves to be listed as last resort for
       // privacy reasons.
 
       if (videoType == MediaSourceEnum::Screen ||
-          videoType == MediaSourceEnum::Application ||
           videoType == MediaSourceEnum::Browser) {
         videoType = MediaSourceEnum::Window;
         vc.mMediaSource.AssignASCII(
             EnumToASCII(dom::MediaSourceEnumValues::strings, videoType));
       }
       // only allow privileged content to set the window id
       if (vc.mBrowserWindow.WasPassed()) {
         vc.mBrowserWindow.Value() = -1;
@@ -3855,70 +3852,64 @@ struct CaptureWindowStateData {
   uint16_t* mAppShare;
   uint16_t* mBrowserShare;
 };
 
 NS_IMETHODIMP
 MediaManager::MediaCaptureWindowState(nsIDOMWindow* aCapturedWindow,
                                       uint16_t* aCamera, uint16_t* aMicrophone,
                                       uint16_t* aScreen, uint16_t* aWindow,
-                                      uint16_t* aApplication,
                                       uint16_t* aBrowser) {
   MOZ_ASSERT(NS_IsMainThread());
 
   CaptureState camera = CaptureState::Off;
   CaptureState microphone = CaptureState::Off;
   CaptureState screen = CaptureState::Off;
   CaptureState window = CaptureState::Off;
-  CaptureState application = CaptureState::Off;
   CaptureState browser = CaptureState::Off;
 
   nsCOMPtr<nsPIDOMWindowInner> piWin = do_QueryInterface(aCapturedWindow);
   if (piWin) {
     IterateWindowListeners(
-        piWin, [&camera, &microphone, &screen, &window, &application,
+        piWin, [&camera, &microphone, &screen, &window,
                 &browser](const RefPtr<GetUserMediaWindowListener>& aListener) {
           camera = CombineCaptureState(
               camera, aListener->CapturingSource(MediaSourceEnum::Camera));
           microphone = CombineCaptureState(
               microphone,
               aListener->CapturingSource(MediaSourceEnum::Microphone));
           screen = CombineCaptureState(
               screen, aListener->CapturingSource(MediaSourceEnum::Screen));
           window = CombineCaptureState(
               window, aListener->CapturingSource(MediaSourceEnum::Window));
-          application = CombineCaptureState(
-              application,
-              aListener->CapturingSource(MediaSourceEnum::Application));
           browser = CombineCaptureState(
               browser, aListener->CapturingSource(MediaSourceEnum::Browser));
         });
   }
 
   *aCamera = FromCaptureState(camera);
   *aMicrophone = FromCaptureState(microphone);
   *aScreen = FromCaptureState(screen);
   *aWindow = FromCaptureState(window);
-  *aApplication = FromCaptureState(application);
   *aBrowser = FromCaptureState(browser);
 
-  LOG("%s: window %" PRIu64 " capturing %s %s %s %s %s %s", __FUNCTION__,
+  LOG("%s: window %" PRIu64 " capturing %s %s %s %s %s", __FUNCTION__,
       piWin ? piWin->WindowID() : -1,
       *aCamera == nsIMediaManagerService::STATE_CAPTURE_ENABLED
           ? "camera (enabled)"
           : (*aCamera == nsIMediaManagerService::STATE_CAPTURE_DISABLED
                  ? "camera (disabled)"
                  : ""),
       *aMicrophone == nsIMediaManagerService::STATE_CAPTURE_ENABLED
           ? "microphone (enabled)"
           : (*aMicrophone == nsIMediaManagerService::STATE_CAPTURE_DISABLED
                  ? "microphone (disabled)"
                  : ""),
       *aScreen ? "screenshare" : "", *aWindow ? "windowshare" : "",
-      *aApplication ? "appshare" : "", *aBrowser ? "browsershare" : "");
+      *aBrowser ? "browsershare" : "");
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 MediaManager::SanitizeDeviceIds(int64_t aSinceWhen) {
   MOZ_ASSERT(NS_IsMainThread());
   LOG("%s: sinceWhen = %" PRId64, __FUNCTION__, aSinceWhen);
@@ -4460,18 +4451,16 @@ void SourceListener::StopSharing() {
   }
 
   MOZ_RELEASE_ASSERT(mWindowListener);
   LOG("SourceListener %p StopSharing", this);
 
   if (mVideoDeviceState && (mVideoDeviceState->mDevice->GetMediaSource() ==
                                 MediaSourceEnum::Screen ||
                             mVideoDeviceState->mDevice->GetMediaSource() ==
-                                MediaSourceEnum::Application ||
-                            mVideoDeviceState->mDevice->GetMediaSource() ==
                                 MediaSourceEnum::Window)) {
     // We want to stop the whole stream if there's no audio;
     // just the video track if we have both.
     // StopTrack figures this out for us.
     StopTrack(kVideoTrack);
   }
   if (mAudioDeviceState && mAudioDeviceState->mDevice->GetMediaSource() ==
                                MediaSourceEnum::AudioCapture) {
--- a/dom/media/nsIMediaManager.idl
+++ b/dom/media/nsIMediaManager.idl
@@ -24,15 +24,14 @@ interface nsIMediaManagerService : nsISu
   const unsigned short STATE_CAPTURE_DISABLED = 2;
 
   /* Get the capture state for the given window and all descendant windows (iframes, etc) */
   void mediaCaptureWindowState(in nsIDOMWindow aWindow,
                                out unsigned short aCamera,
                                out unsigned short aMicrophone,
                                [optional] out unsigned short aScreenShare,
                                [optional] out unsigned short aWindowShare,
-                               [optional] out unsigned short aAppShare,
                                [optional] out unsigned short aBrowserShare);
 
   /* Clear per-orgin list of persistent DeviceIds stored for enumerateDevices
      sinceTime is milliseconds since 1 January 1970 00:00:00 UTC. 0 = clear all */
   void sanitizeDeviceIds(in long long sinceWhen);
 };
--- a/dom/media/systemservices/CamerasParent.cpp
+++ b/dom/media/systemservices/CamerasParent.cpp
@@ -350,20 +350,16 @@ bool CamerasParent::SetupEngine(CaptureE
       case BrowserEngine:
         captureDeviceInfo = MakeUnique<webrtc::CaptureDeviceInfo>(
             webrtc::CaptureDeviceType::Browser);
         break;
       case WinEngine:
         captureDeviceInfo = MakeUnique<webrtc::CaptureDeviceInfo>(
             webrtc::CaptureDeviceType::Window);
         break;
-      case AppEngine:
-        captureDeviceInfo = MakeUnique<webrtc::CaptureDeviceInfo>(
-            webrtc::CaptureDeviceType::Application);
-        break;
       case CameraEngine:
         captureDeviceInfo = MakeUnique<webrtc::CaptureDeviceInfo>(
             webrtc::CaptureDeviceType::Camera);
         break;
       default:
         LOG(("Invalid webrtc Video engine"));
         MOZ_CRASH();
         break;
@@ -903,17 +899,17 @@ mozilla::ipc::IPCResult CamerasParent::R
                         minDistance = distance;
                       }
                     }
                     MOZ_ASSERT(minIdx != -1);
                     capability = candidateCapabilities->second[minIdx];
                   }
                 } else if (aCapEngine == ScreenEngine ||
                            aCapEngine == BrowserEngine ||
-                           aCapEngine == WinEngine || aCapEngine == AppEngine) {
+                           aCapEngine == WinEngine) {
                   for (const auto& it : sDeviceUniqueIDs) {
                     if (strcmp(it.second,
                                cap.VideoCapture()->CurrentDeviceName()) == 0) {
                       capability.maxFPS =
                           std::max(capability.maxFPS,
                                    sAllRequestedCapabilities[it.first].maxFPS);
                     }
                   }
--- a/dom/media/systemservices/CamerasTypes.h
+++ b/dom/media/systemservices/CamerasTypes.h
@@ -13,17 +13,16 @@ namespace mozilla {
 
 namespace camera {
 
 enum CaptureEngine : int {
   InvalidEngine = 0,
   ScreenEngine,
   BrowserEngine,
   WinEngine,
-  AppEngine,
   CameraEngine,
   MaxEngine
 };
 
 }  // namespace camera
 }  // namespace mozilla
 
 namespace IPC {
--- a/dom/media/systemservices/VideoEngine.cpp
+++ b/dom/media/systemservices/VideoEngine.cpp
@@ -184,19 +184,18 @@ VideoEngine::GetOrCreateVideoCaptureDevi
       break;
     }
     case webrtc::CaptureDeviceType::Browser: {
       mDeviceInfo.reset(webrtc::BrowserDeviceInfoImpl::CreateDeviceInfo());
       LOG((
           "webrtc::CaptureDeviceType::Browser: Finished creating new device."));
       break;
     }
-    // Window, Application, and Screen types are handled by DesktopCapture
+    // Window and Screen types are handled by DesktopCapture
     case webrtc::CaptureDeviceType::Window:
-    case webrtc::CaptureDeviceType::Application:
     case webrtc::CaptureDeviceType::Screen: {
 #if !defined(WEBRTC_ANDROID) && !defined(WEBRTC_IOS)
       mDeviceInfo.reset(webrtc::DesktopCaptureImpl::CreateDeviceInfo(
           mId, mCaptureDevInfo.type));
       LOG(("screen capture: Finished creating new device."));
 #else
       MOZ_ASSERT(
           "GetVideoCaptureDeviceInfo NO DESKTOP CAPTURE IMPL ON ANDROID" ==
--- a/dom/media/systemservices/video_engine/desktop_capture_impl.cc
+++ b/dom/media/systemservices/video_engine/desktop_capture_impl.cc
@@ -21,17 +21,16 @@
 #include "modules/video_capture/video_capture_config.h"
 #include "system_wrappers/include/clock.h"
 #include "rtc_base/logging.h"
 #include "rtc_base/refcountedobject.h"
 #include "rtc_base/trace_event.h"
 #include "video_engine/desktop_capture_impl.h"
 #include "modules/desktop_capture/desktop_frame.h"
 #include "modules/desktop_capture/desktop_device_info.h"
-#include "modules/desktop_capture/app_capturer.h"
 #include "modules/desktop_capture/desktop_capture_options.h"
 #include "modules/video_capture/video_capture.h"
 
 #if defined(_WIN32)
 #  include "platform_uithread.h"
 #else
 #  include "rtc_base/platform_thread.h"
 #endif
@@ -297,24 +296,17 @@ int32_t WindowDeviceInfoImpl::GetBestMat
 
 int32_t WindowDeviceInfoImpl::GetOrientation(const char* deviceUniqueIdUTF8,
                                              VideoRotation& orientation) {
   return 0;
 }
 
 VideoCaptureModule::DeviceInfo* DesktopCaptureImpl::CreateDeviceInfo(
     const int32_t id, const CaptureDeviceType type) {
-  if (type == CaptureDeviceType::Application) {
-    AppDeviceInfoImpl* pAppDeviceInfoImpl = new AppDeviceInfoImpl(id);
-    if (!pAppDeviceInfoImpl || pAppDeviceInfoImpl->Init()) {
-      delete pAppDeviceInfoImpl;
-      pAppDeviceInfoImpl = NULL;
-    }
-    return pAppDeviceInfoImpl;
-  } else if (type == CaptureDeviceType::Screen) {
+  if (type == CaptureDeviceType::Screen) {
     ScreenDeviceInfoImpl* pScreenDeviceInfoImpl = new ScreenDeviceInfoImpl(id);
     if (!pScreenDeviceInfoImpl || pScreenDeviceInfoImpl->Init()) {
       delete pScreenDeviceInfoImpl;
       pScreenDeviceInfoImpl = NULL;
     }
     return pScreenDeviceInfoImpl;
   } else if (type == CaptureDeviceType::Window) {
     WindowDeviceInfoImpl* pWindowDeviceInfoImpl = new WindowDeviceInfoImpl(id);
@@ -336,33 +328,17 @@ int32_t DesktopCaptureImpl::Init() {
   if (desktop_capturer_cursor_composer_) {
     return 0;
   }
 
   DesktopCaptureOptions options = DesktopCaptureOptions::CreateDefault();
   // Leave desktop effects enabled during WebRTC captures.
   options.set_disable_effects(false);
 
-  if (_deviceType == CaptureDeviceType::Application) {
-    std::unique_ptr<DesktopCapturer> pAppCapturer =
-        DesktopCapturer::CreateAppCapturer(options);
-    if (!pAppCapturer) {
-      return -1;
-    }
-
-    DesktopCapturer::SourceId sourceId = atoi(_deviceUniqueId.c_str());
-    pAppCapturer->SelectSource(sourceId);
-
-    MouseCursorMonitor* pMouseCursorMonitor =
-        MouseCursorMonitor::CreateForScreen(options,
-                                            webrtc::kFullDesktopScreenId);
-    desktop_capturer_cursor_composer_ =
-        std::unique_ptr<DesktopAndCursorComposer>(new DesktopAndCursorComposer(
-            pAppCapturer.release(), pMouseCursorMonitor));
-  } else if (_deviceType == CaptureDeviceType::Screen) {
+  if (_deviceType == CaptureDeviceType::Screen) {
     std::unique_ptr<DesktopCapturer> pScreenCapturer =
         DesktopCapturer::CreateScreenCapturer(options);
     if (!pScreenCapturer.get()) {
       return -1;
     }
 
     DesktopCapturer::SourceId sourceId = atoi(_deviceUniqueId.c_str());
     pScreenCapturer->SelectSource(sourceId);
--- a/dom/media/tests/mochitest/test_getUserMedia_constraints.html
+++ b/dom/media/tests/mochitest/test_getUserMedia_constraints.html
@@ -22,19 +22,19 @@ var tests = [
     constraints: { audio: { somethingUnknown: { exact: 0 } } },
     error: null },
   { message: "audio overconstrained by facingMode ignored",
     constraints: { audio: { facingMode: { exact: 'left' } } },
     error: null },
   { message: "full screensharing requires permission",
     constraints: { video: { mediaSource: 'screen' } },
     error: "NotAllowedError" },
-  { message: "application screensharing requires permission",
+  { message: "application screensharing no longer exists",
     constraints: { video: { mediaSource: 'application' } },
-    error: "NotAllowedError" },
+    error: "OverconstrainedError" },
   { message: "window screensharing requires permission",
     constraints: { video: { mediaSource: 'window' } },
     error: "NotAllowedError" },
   { message: "browser screensharing requires permission",
     constraints: { video: { mediaSource: 'browser' } },
     error: "NotAllowedError" },
   { message: "unknown mediaSource in video fails",
     constraints: { video: { mediaSource: 'uncle' } },
--- a/dom/media/webrtc/MediaEngineRemoteVideoSource.cpp
+++ b/dom/media/webrtc/MediaEngineRemoteVideoSource.cpp
@@ -55,18 +55,16 @@ MediaEngineRemoteVideoSource::~MediaEngi
 dom::MediaSourceEnum MediaEngineRemoteVideoSource::GetMediaSource() const {
   switch (mCapEngine) {
     case camera::BrowserEngine:
       return MediaSourceEnum::Browser;
     case camera::CameraEngine:
       return MediaSourceEnum::Camera;
     case camera::ScreenEngine:
       return MediaSourceEnum::Screen;
-    case camera::AppEngine:
-      return MediaSourceEnum::Application;
     case camera::WinEngine:
       return MediaSourceEnum::Window;
     default:
       MOZ_CRASH();
   }
 }
 
 void MediaEngineRemoteVideoSource::Init() {
@@ -319,17 +317,16 @@ nsresult MediaEngineRemoteVideoSource::S
 
   NS_DispatchToMainThread(NS_NewRunnableFunction(
       "MediaEngineRemoteVideoSource::SetLastCapability",
       [settings = mSettings, updated = mSettingsUpdatedByFrame,
        capEngine = mCapEngine, cap = mCapability]() mutable {
         switch (capEngine) {
           case camera::ScreenEngine:
           case camera::WinEngine:
-          case camera::AppEngine:
             // Undo the hack where ideal and max constraints are crammed
             // together in mCapability for consumption by low-level code. We
             // don't actually know the real resolution yet, so report min(ideal,
             // max) for now.
             // TODO: This can be removed in bug 1453269.
             cap.width = std::min(cap.width >> 16, cap.width & 0xffff);
             cap.height = std::min(cap.height >> 16, cap.height & 0xffff);
             break;
@@ -513,18 +510,17 @@ int MediaEngineRemoteVideoSource::Delive
       req_ideal_width > 0 ? req_ideal_width : aProps.width(), dst_max_width);
   int32_t dst_height =
       std::min(req_ideal_height > 0 ? req_ideal_height : aProps.height(),
                dst_max_height);
 
   // Apply scaling for screen sharing, see bug 1453269.
   switch (mCapEngine) {
     case camera::ScreenEngine:
-    case camera::WinEngine:
-    case camera::AppEngine: {
+    case camera::WinEngine: {
       // scale to average of portrait and landscape
       float scale_width = (float)dst_width / (float)aProps.width();
       float scale_height = (float)dst_height / (float)aProps.height();
       float scale = (scale_width + scale_height) / 2;
       dst_width = (int)(scale * target_width);
       dst_height = (int)(scale * target_height);
 
       // if scaled rectangle exceeds max rectangle, scale to minimum of portrait
@@ -778,18 +774,17 @@ bool MediaEngineRemoteVideoSource::Choos
       for (auto& advanced : aConstraints.mAdvanced) {
         MediaConstraintsHelper::LogConstraints(advanced);
       }
     }
   }
 
   switch (mCapEngine) {
     case camera::ScreenEngine:
-    case camera::WinEngine:
-    case camera::AppEngine: {
+    case camera::WinEngine: {
       FlattenedConstraints c(aConstraints);
       // The actual resolution to constrain around is not easy to find ahead of
       // time (and may in fact change over time), so as a hack, we push ideal
       // and max constraints down to desktop_capture_impl.cc and finish the
       // algorithm there.
       // TODO: This can be removed in bug 1453269.
       aCapability.width =
           (std::min(0xffff, c.mWidth.mIdeal.valueOr(0)) & 0xffff) << 16 |
--- a/dom/media/webrtc/MediaEngineSource.cpp
+++ b/dom/media/webrtc/MediaEngineSource.cpp
@@ -18,17 +18,16 @@ using dom::MediaTrackSettings;
 const unsigned int MediaEngineSource::kMaxDeviceNameLength;
 const unsigned int MediaEngineSource::kMaxUniqueIdLength;
 
 /* static */
 bool MediaEngineSource::IsVideo(MediaSourceEnum aSource) {
   switch (aSource) {
     case MediaSourceEnum::Camera:
     case MediaSourceEnum::Screen:
-    case MediaSourceEnum::Application:
     case MediaSourceEnum::Window:
     case MediaSourceEnum::Browser:
       return true;
     case MediaSourceEnum::Microphone:
     case MediaSourceEnum::AudioCapture:
     case MediaSourceEnum::Other:
       return false;
     default:
--- a/dom/media/webrtc/MediaEngineWebRTC.cpp
+++ b/dom/media/webrtc/MediaEngineWebRTC.cpp
@@ -241,19 +241,16 @@ void MediaEngineWebRTC::EnumerateDevices
       case dom::MediaSourceEnum::Window:
         // Since the mediaSource constraint is deprecated, treat the Window
         // value as a request for getDisplayMedia-equivalent sharing: Combine
         // window and fullscreen into a single list of choices. The other values
         // are still useful for testing.
         EnumerateVideoDevices(aWindowId, camera::WinEngine, aDevices);
         EnumerateVideoDevices(aWindowId, camera::ScreenEngine, aDevices);
         break;
-      case dom::MediaSourceEnum::Application:
-        EnumerateVideoDevices(aWindowId, camera::AppEngine, aDevices);
-        break;
       case dom::MediaSourceEnum::Screen:
         EnumerateVideoDevices(aWindowId, camera::ScreenEngine, aDevices);
         break;
       case dom::MediaSourceEnum::Browser:
         EnumerateVideoDevices(aWindowId, camera::BrowserEngine, aDevices);
         break;
       case dom::MediaSourceEnum::Camera:
         EnumerateVideoDevices(aWindowId, camera::CameraEngine, aDevices);
--- a/gfx/webrender_bindings/WebRenderAPI.cpp
+++ b/gfx/webrender_bindings/WebRenderAPI.cpp
@@ -802,20 +802,21 @@ Maybe<wr::WrSpatialId> DisplayListBuilde
 }
 
 void DisplayListBuilder::PopStackingContext(bool aIsReferenceFrame) {
   WRDL_LOG("PopStackingContext\n", mWrState);
   wr_dp_pop_stacking_context(mWrState, aIsReferenceFrame);
 }
 
 wr::WrClipChainId DisplayListBuilder::DefineClipChain(
-    const nsTArray<wr::WrClipId>& aClips, const wr::WrClipChainId* aParent) {
+    const nsTArray<wr::WrClipId>& aClips, bool aParentWithCurrentChain) {
   const uint64_t* parent = nullptr;
-  if (aParent && aParent->id != wr::ROOT_CLIP_CHAIN) {
-    parent = &aParent->id;
+  if (aParentWithCurrentChain &&
+      mCurrentSpaceAndClipChain.clip_chain != wr::ROOT_CLIP_CHAIN) {
+    parent = &mCurrentSpaceAndClipChain.clip_chain;
   }
   uint64_t clipchainId = wr_dp_define_clipchain(
       mWrState, parent, aClips.Elements(), aClips.Length());
   WRDL_LOG("DefineClipChain id=%" PRIu64 " clips=%zu\n", mWrState, clipchainId,
            aClips.Length());
   return wr::WrClipChainId{clipchainId};
 }
 
@@ -1165,19 +1166,18 @@ void DisplayListBuilder::PopAllShadows()
 void DisplayListBuilder::SuspendClipLeafMerging() {
   if (mClipChainLeaf) {
     // No one should reinitialize mClipChainLeaf while we're suspended
     MOZ_ASSERT(!mSuspendedClipChainLeaf);
 
     mSuspendedClipChainLeaf = mClipChainLeaf;
     mSuspendedSpaceAndClipChain = Some(mCurrentSpaceAndClipChain);
 
-    wr::WrClipChainId currentClipChainId{mCurrentSpaceAndClipChain.clip_chain};
     auto clipId = DefineClip(Nothing(), *mClipChainLeaf);
-    auto clipChainId = DefineClipChain({clipId}, &currentClipChainId);
+    auto clipChainId = DefineClipChain({clipId}, true);
 
     mCurrentSpaceAndClipChain.clip_chain = clipChainId.id;
     mClipChainLeaf = Nothing();
   }
 }
 
 void DisplayListBuilder::ResumeClipLeafMerging() {
   if (mSuspendedClipChainLeaf) {
--- a/gfx/webrender_bindings/WebRenderAPI.h
+++ b/gfx/webrender_bindings/WebRenderAPI.h
@@ -389,17 +389,17 @@ class DisplayListBuilder final {
   }
 
   Maybe<wr::WrSpatialId> PushStackingContext(
       const StackingContextParams& aParams, const wr::LayoutRect& aBounds,
       const wr::RasterSpace& aRasterSpace);
   void PopStackingContext(bool aIsReferenceFrame);
 
   wr::WrClipChainId DefineClipChain(const nsTArray<wr::WrClipId>& aClips,
-                                    const wr::WrClipChainId* aParent = nullptr);
+                                    bool aParentWithCurrentChain = false);
 
   wr::WrClipId DefineClip(
       const Maybe<wr::WrSpaceAndClip>& aParent, const wr::LayoutRect& aClipRect,
       const nsTArray<wr::ComplexClipRegion>* aComplex = nullptr,
       const wr::ImageMask* aMask = nullptr);
 
   wr::WrSpatialId DefineStickyFrame(const wr::LayoutRect& aContentRect,
                                     const float* aTopMargin,
--- a/gfx/wr/webrender/src/display_list_flattener.rs
+++ b/gfx/wr/webrender/src/display_list_flattener.rs
@@ -935,17 +935,17 @@ impl<'a> DisplayListFlattener<'a> {
     fn flatten_item<'b>(
         &'b mut self,
         item: DisplayItemRef<'a, 'b>,
         pipeline_id: PipelineId,
         apply_pipeline_clip: bool,
     ) -> Option<BuiltDisplayListIter<'a>> {
         match *item.item() {
             DisplayItem::Image(ref info) => {
-                let (mut layout, clip_and_scroll) = self.process_common_properties_with_bounds(
+                let (layout, clip_and_scroll) = self.process_common_properties_with_bounds(
                     &info.common,
                     &info.bounds,
                     apply_pipeline_clip,
                 );
 
                 self.add_image(
                     clip_and_scroll,
                     &layout,
@@ -954,33 +954,33 @@ impl<'a> DisplayListFlattener<'a> {
                     None,
                     info.image_key,
                     info.image_rendering,
                     info.alpha_type,
                     info.color,
                 );
             }
             DisplayItem::YuvImage(ref info) => {
-                let (mut layout, clip_and_scroll) = self.process_common_properties_with_bounds(
+                let (layout, clip_and_scroll) = self.process_common_properties_with_bounds(
                     &info.common,
                     &info.bounds,
                     apply_pipeline_clip,
                 );
 
                 self.add_yuv_image(
                     clip_and_scroll,
                     &layout,
                     info.yuv_data,
                     info.color_depth,
                     info.color_space,
                     info.image_rendering,
                 );
             }
             DisplayItem::Text(ref info) => {
-                let (mut layout, clip_and_scroll) = self.process_common_properties_with_bounds(
+                let (layout, clip_and_scroll) = self.process_common_properties_with_bounds(
                     &info.common,
                     &info.bounds,
                     apply_pipeline_clip,
                 );
 
                 self.add_text(
                     clip_and_scroll,
                     &layout,
@@ -1022,33 +1022,33 @@ impl<'a> DisplayListFlattener<'a> {
                 );
 
                 self.add_clear_rectangle(
                     clip_and_scroll,
                     &layout,
                 );
             }
             DisplayItem::Line(ref info) => {
-                let (mut layout, clip_and_scroll) = self.process_common_properties_with_bounds(
+                let (layout, clip_and_scroll) = self.process_common_properties_with_bounds(
                     &info.common,
                     &info.area,
                     apply_pipeline_clip,
                 );
 
                 self.add_line(
                     clip_and_scroll,
                     &layout,
                     info.wavy_line_thickness,
                     info.orientation,
                     info.color,
                     info.style,
                 );
             }
             DisplayItem::Gradient(ref info) => {
-                let (mut layout, clip_and_scroll) = self.process_common_properties_with_bounds(
+                let (layout, clip_and_scroll) = self.process_common_properties_with_bounds(
                     &info.common,
                     &info.bounds,
                     apply_pipeline_clip,
                 );
 
                 if let Some(prim_key_kind) = self.create_linear_gradient_prim(
                     &layout,
                     info.gradient.start_point,
@@ -1064,17 +1064,17 @@ impl<'a> DisplayListFlattener<'a> {
                         clip_and_scroll,
                         &layout,
                         Vec::new(),
                         prim_key_kind,
                     );
                 }
             }
             DisplayItem::RadialGradient(ref info) => {
-                let (mut layout, clip_and_scroll) = self.process_common_properties_with_bounds(
+                let (layout, clip_and_scroll) = self.process_common_properties_with_bounds(
                     &info.common,
                     &info.bounds,
                     apply_pipeline_clip,
                 );
 
                 let prim_key_kind = self.create_radial_gradient_prim(
                     &layout,
                     info.gradient.center,
@@ -1092,17 +1092,17 @@ impl<'a> DisplayListFlattener<'a> {
                 self.add_nonshadowable_primitive(
                     clip_and_scroll,
                     &layout,
                     Vec::new(),
                     prim_key_kind,
                 );
             }
             DisplayItem::BoxShadow(ref info) => {
-                let (mut layout, clip_and_scroll) = self.process_common_properties_with_bounds(
+                let (layout, clip_and_scroll) = self.process_common_properties_with_bounds(
                     &info.common,
                     &info.box_bounds,
                     apply_pipeline_clip,
                 );
 
                 self.add_box_shadow(
                     clip_and_scroll,
                     &layout,
@@ -1110,17 +1110,17 @@ impl<'a> DisplayListFlattener<'a> {
                     info.color,
                     info.blur_radius,
                     info.spread_radius,
                     info.border_radius,
                     info.clip_mode,
                 );
             }
             DisplayItem::Border(ref info) => {
-                let (mut layout, clip_and_scroll) = self.process_common_properties_with_bounds(
+                let (layout, clip_and_scroll) = self.process_common_properties_with_bounds(
                     &info.common,
                     &info.bounds,
                     apply_pipeline_clip,
                 );
 
                 self.add_border(
                     clip_and_scroll,
                     &layout,
--- a/gfx/wr/webrender/src/render_backend.rs
+++ b/gfx/wr/webrender/src/render_backend.rs
@@ -870,17 +870,17 @@ impl RenderBackend {
                             |document_id| txns.iter().any(|txn| txn.document_id == document_id));
 
                         for mut txn in txns.drain(..) {
                             let has_built_scene = txn.built_scene.is_some();
                             if let Some(doc) = self.documents.get_mut(&txn.document_id) {
 
                                 doc.removed_pipelines.append(&mut txn.removed_pipelines);
 
-                                if let Some(mut built_scene) = txn.built_scene.take() {
+                                if let Some(built_scene) = txn.built_scene.take() {
                                     doc.new_async_scene_ready(
                                         built_scene,
                                         &mut self.recycler,
                                     );
                                 }
 
                                 if let Some(ref tx) = result_tx {
                                     let (resume_tx, resume_rx) = channel();
--- a/gfx/wr/wrench/src/main.rs
+++ b/gfx/wr/wrench/src/main.rs
@@ -439,17 +439,17 @@ fn main() {
             let w = s[0 .. x].parse::<i32>().expect("Invalid size width");
             let h = s[x + 1 ..].parse::<i32>().expect("Invalid size height");
             DeviceIntSize::new(w, h)
         })
         .unwrap_or(DeviceIntSize::new(1920, 1080));
     let zoom_factor = args.value_of("zoom").map(|z| z.parse::<f32>().unwrap());
     let chase_primitive = match args.value_of("chase") {
         Some(s) => {
-            let mut items = s
+            let items = s
                 .split(',')
                 .map(|s| s.parse::<f32>().unwrap())
                 .collect::<Vec<_>>();
             let rect = LayoutRect::new(
                 LayoutPoint::new(items[0], items[1]),
                 LayoutSize::new(items[2], items[3]),
             );
             webrender::ChasePrimitive::LocalRect(rect)
--- a/js/src/frontend/TokenStream.cpp
+++ b/js/src/frontend/TokenStream.cpp
@@ -1904,17 +1904,18 @@ bool TokenStreamSpecific<Unit, AnyCharsA
   do {
     int32_t unit = getCodeUnit();
     if (unit == EOF) {
       break;
     }
 
     uint32_t codePoint;
     if (MOZ_LIKELY(isAsciiCodePoint(unit))) {
-      if (unicode::IsIdentifierPart(char16_t(unit))) {
+      if (unicode::IsIdentifierPart(char16_t(unit)) ||
+          (char16_t(unit) == '#')) {
         if (!this->charBuffer.append(unit)) {
           return false;
         }
 
         continue;
       }
 
       if (unit != '\\' || !matchUnicodeEscapeIdent(&codePoint)) {
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/fields/bug1547915.js
@@ -0,0 +1,6 @@
+// |jit-test| --enable-experimental-fields
+
+load(libdir + "asserts.js");
+
+source = `#_\\u200C`;
+assertThrowsInstanceOf(() => eval(source), ReferenceError);
--- a/layout/forms/nsFieldSetFrame.cpp
+++ b/layout/forms/nsFieldSetFrame.cpp
@@ -191,17 +191,17 @@ bool nsDisplayFieldSetBorder::CreateWebR
       wr::ComplexClipRegion region;
       region.rect = wr::ToRoundedLayoutRect(
           LayoutDeviceRect::FromAppUnits(legendRect, appUnitsPerDevPixel));
       region.mode = wr::ClipMode::ClipOut;
       region.radii = wr::EmptyBorderRadius();
       nsTArray<mozilla::wr::ComplexClipRegion> array{region};
 
       auto clip = aBuilder.DefineClip(Nothing(), layoutRect, &array, nullptr);
-      auto clipChain = aBuilder.DefineClipChain({clip});
+      auto clipChain = aBuilder.DefineClipChain({clip}, true);
       clipOut.emplace(aBuilder, clipChain);
     }
   } else {
     rect = nsRect(offset, frame->GetRect().Size());
   }
 
   ImgDrawResult drawResult = nsCSSRendering::CreateWebRenderCommandsForBorder(
       this, mFrame, rect, aBuilder, aResources, aSc, aManager,
@@ -566,17 +566,17 @@ void nsFieldSetFrame::Reflow(nsPresConte
   if (legend) {
     // The legend is positioned inline-wards within the inner's content rect
     // (so that padding on the fieldset affects the legend position).
     LogicalRect innerContentRect = contentRect;
     innerContentRect.Deflate(wm, aReflowInput.ComputedLogicalPadding());
     // If the inner content rect is larger than the legend, we can align the
     // legend.
     if (innerContentRect.ISize(wm) > mLegendRect.ISize(wm)) {
-      // NOTE legend @align values are: left/right/center/top/bottom.
+      // NOTE legend @align values are: left/right/center
       // GetLogicalAlign converts left/right to start/end for the given WM.
       // @see HTMLLegendElement::ParseAttribute, nsLegendFrame::GetLogicalAlign
       int32_t align =
           static_cast<nsLegendFrame*>(legend->GetContentInsertionFrame())
               ->GetLogicalAlign(wm);
       switch (align) {
         case NS_STYLE_TEXT_ALIGN_END:
           mLegendRect.IStart(wm) =
@@ -584,18 +584,16 @@ void nsFieldSetFrame::Reflow(nsPresConte
           break;
         case NS_STYLE_TEXT_ALIGN_CENTER:
           // Note: rounding removed; there doesn't seem to be any need
           mLegendRect.IStart(wm) =
               innerContentRect.IStart(wm) +
               (innerContentRect.ISize(wm) - mLegendRect.ISize(wm)) / 2;
           break;
         case NS_STYLE_TEXT_ALIGN_START:
-        case NS_STYLE_VERTICAL_ALIGN_TOP:
-        case NS_STYLE_VERTICAL_ALIGN_BOTTOM:
           mLegendRect.IStart(wm) = innerContentRect.IStart(wm);
           break;
         default:
           MOZ_ASSERT_UNREACHABLE("unexpected GetLogicalAlign value");
       }
     } else {
       // otherwise just start-align it.
       mLegendRect.IStart(wm) = innerContentRect.IStart(wm);
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -9758,65 +9758,65 @@ bool nsIFrame::IsFocusable(int32_t* aTab
 }
 
 /**
  * @return true if this text frame ends with a newline character which is
  * treated as preformatted. It should return false if this is not a text frame.
  */
 bool nsIFrame::HasSignificantTerminalNewline() const { return false; }
 
-static uint8_t ConvertSVGDominantBaselineToVerticalAlign(
+static StyleVerticalAlignKeyword ConvertSVGDominantBaselineToVerticalAlign(
     uint8_t aDominantBaseline) {
   // Most of these are approximate mappings.
   switch (aDominantBaseline) {
     case NS_STYLE_DOMINANT_BASELINE_HANGING:
     case NS_STYLE_DOMINANT_BASELINE_TEXT_BEFORE_EDGE:
-      return NS_STYLE_VERTICAL_ALIGN_TEXT_TOP;
+      return StyleVerticalAlignKeyword::TextTop;
     case NS_STYLE_DOMINANT_BASELINE_TEXT_AFTER_EDGE:
     case NS_STYLE_DOMINANT_BASELINE_IDEOGRAPHIC:
-      return NS_STYLE_VERTICAL_ALIGN_TEXT_BOTTOM;
+      return StyleVerticalAlignKeyword::TextBottom;
     case NS_STYLE_DOMINANT_BASELINE_CENTRAL:
     case NS_STYLE_DOMINANT_BASELINE_MIDDLE:
     case NS_STYLE_DOMINANT_BASELINE_MATHEMATICAL:
-      return NS_STYLE_VERTICAL_ALIGN_MIDDLE;
+      return StyleVerticalAlignKeyword::Middle;
     case NS_STYLE_DOMINANT_BASELINE_AUTO:
     case NS_STYLE_DOMINANT_BASELINE_ALPHABETIC:
-      return NS_STYLE_VERTICAL_ALIGN_BASELINE;
+      return StyleVerticalAlignKeyword::Baseline;
     case NS_STYLE_DOMINANT_BASELINE_USE_SCRIPT:
     case NS_STYLE_DOMINANT_BASELINE_NO_CHANGE:
     case NS_STYLE_DOMINANT_BASELINE_RESET_SIZE:
       // These three should not simply map to 'baseline', but we don't
       // support the complex baseline model that SVG 1.1 has and which
       // css3-linebox now defines.
-      return NS_STYLE_VERTICAL_ALIGN_BASELINE;
+      return StyleVerticalAlignKeyword::Baseline;
     default:
       MOZ_ASSERT_UNREACHABLE("unexpected aDominantBaseline value");
-      return NS_STYLE_VERTICAL_ALIGN_BASELINE;
-  }
-}
-
-uint8_t nsIFrame::VerticalAlignEnum() const {
+      return StyleVerticalAlignKeyword::Baseline;
+  }
+}
+
+Maybe<StyleVerticalAlignKeyword> nsIFrame::VerticalAlignEnum() const {
   if (nsSVGUtils::IsInSVGTextSubtree(this)) {
     uint8_t dominantBaseline;
     for (const nsIFrame* frame = this; frame; frame = frame->GetParent()) {
       dominantBaseline = frame->StyleSVGReset()->mDominantBaseline;
       if (dominantBaseline != NS_STYLE_DOMINANT_BASELINE_AUTO ||
           frame->IsSVGTextFrame()) {
         break;
       }
     }
-    return ConvertSVGDominantBaselineToVerticalAlign(dominantBaseline);
-  }
-
-  const nsStyleCoord& verticalAlign = StyleDisplay()->mVerticalAlign;
-  if (verticalAlign.GetUnit() == eStyleUnit_Enumerated) {
-    return verticalAlign.GetIntValue();
-  }
-
-  return eInvalidVerticalAlign;
+    return Some(ConvertSVGDominantBaselineToVerticalAlign(dominantBaseline));
+  }
+
+  const auto& verticalAlign = StyleDisplay()->mVerticalAlign;
+  if (verticalAlign.IsKeyword()) {
+    return Some(verticalAlign.AsKeyword());
+  }
+
+  return Nothing();
 }
 
 NS_IMETHODIMP
 nsFrame::RefreshSizeCache(nsBoxLayoutState& aState) {
   // XXXbz this comment needs some rewriting to make sense in the
   // post-reflow-branch world.
 
   // Ok we need to compute our minimum, preferred, and maximum sizes.
--- a/layout/generic/nsIFrame.h
+++ b/layout/generic/nsIFrame.h
@@ -3934,20 +3934,19 @@ class nsIFrame : public nsQueryFrame {
   // multi-column ancestor or not.
   inline bool IsColumnSpanInMulticolSubtree() const;
 
   /**
    * Returns the vertical-align value to be used for layout, if it is one
    * of the enumerated values.  If this is an SVG text frame, it returns a value
    * that corresponds to the value of dominant-baseline.  If the
    * vertical-align property has length or percentage value, this returns
-   * eInvalidVerticalAlign.
-   */
-  uint8_t VerticalAlignEnum() const;
-  enum { eInvalidVerticalAlign = 0xFF };
+   * Nothing().
+   */
+  Maybe<mozilla::StyleVerticalAlignKeyword> VerticalAlignEnum() const;
 
   void CreateOwnLayerIfNeeded(nsDisplayListBuilder* aBuilder,
                               nsDisplayList* aList,
                               bool* aCreatedContainerItem = nullptr);
 
   /**
    * Adds the NS_FRAME_IN_POPUP state bit to aFrame, and
    * all descendant frames (including cross-doc ones).
--- a/layout/generic/nsLineLayout.cpp
+++ b/layout/generic/nsLineLayout.cpp
@@ -1954,124 +1954,127 @@ void nsLineLayout::VerticalAlignFrames(P
           mPresContext->CompatibilityMode() == eCompatibility_NavQuirks) {
         pfd->mAscent -= logicalBSize;
         logicalBSize = 0;
       }
     }
 
     // Get vertical-align property ("vertical-align" is the CSS name for
     // block-direction align)
-    const nsStyleCoord& verticalAlign = frame->StyleDisplay()->mVerticalAlign;
-    uint8_t verticalAlignEnum = frame->VerticalAlignEnum();
+    const auto& verticalAlign = frame->StyleDisplay()->mVerticalAlign;
+    Maybe<StyleVerticalAlignKeyword> verticalAlignEnum =
+        frame->VerticalAlignEnum();
 #ifdef NOISY_BLOCKDIR_ALIGN
     printf("  [frame]");
     frame->ListTag(stdout);
-    printf(": verticalAlignUnit=%d (enum == %d", verticalAlign.GetUnit(),
-           ((eStyleUnit_Enumerated == verticalAlign.GetUnit())
-                ? verticalAlign.GetIntValue()
-                : -1));
-    if (verticalAlignEnum != nsIFrame::eInvalidVerticalAlign) {
+    printf(": verticalAlignIsKw=%d (enum == %d", verticalAlign.IsKeyword(),
+           verticalAlign.IsKeyword()
+               ? static_cast<int>(verticalAlign.AsKeyword())
+               : -1);
+    if (verticalAlignEnum) {
       printf(", after SVG dominant-baseline conversion == %d",
-             verticalAlignEnum);
+             static_cast<int>(*verticalAlignEnum));
     }
     printf(")\n");
 #endif
 
-    if (verticalAlignEnum != nsIFrame::eInvalidVerticalAlign) {
+    if (verticalAlignEnum) {
+      StyleVerticalAlignKeyword keyword = *verticalAlignEnum;
       if (lineWM.IsVertical()) {
-        if (verticalAlignEnum == NS_STYLE_VERTICAL_ALIGN_MIDDLE) {
+        if (keyword == StyleVerticalAlignKeyword::Middle) {
           // For vertical writing mode where the dominant baseline is centered
           // (i.e. text-orientation is not sideways-*), we remap 'middle' to
           // 'middle-with-baseline' so that images align sensibly with the
           // center-baseline-aligned text.
           if (!lineWM.IsSideways()) {
-            verticalAlignEnum = NS_STYLE_VERTICAL_ALIGN_MIDDLE_WITH_BASELINE;
+            keyword = StyleVerticalAlignKeyword::MozMiddleWithBaseline;
           }
         } else if (lineWM.IsLineInverted()) {
           // Swap the meanings of top and bottom when line is inverted
           // relative to block direction.
-          switch (verticalAlignEnum) {
-            case NS_STYLE_VERTICAL_ALIGN_TOP:
-              verticalAlignEnum = NS_STYLE_VERTICAL_ALIGN_BOTTOM;
+          switch (keyword) {
+            case StyleVerticalAlignKeyword::Top:
+              keyword = StyleVerticalAlignKeyword::Bottom;
+              break;
+            case StyleVerticalAlignKeyword::Bottom:
+              keyword = StyleVerticalAlignKeyword::Top;
               break;
-            case NS_STYLE_VERTICAL_ALIGN_BOTTOM:
-              verticalAlignEnum = NS_STYLE_VERTICAL_ALIGN_TOP;
+            case StyleVerticalAlignKeyword::TextTop:
+              keyword = StyleVerticalAlignKeyword::TextBottom;
               break;
-            case NS_STYLE_VERTICAL_ALIGN_TEXT_TOP:
-              verticalAlignEnum = NS_STYLE_VERTICAL_ALIGN_TEXT_BOTTOM;
+            case StyleVerticalAlignKeyword::TextBottom:
+              keyword = StyleVerticalAlignKeyword::TextTop;
               break;
-            case NS_STYLE_VERTICAL_ALIGN_TEXT_BOTTOM:
-              verticalAlignEnum = NS_STYLE_VERTICAL_ALIGN_TEXT_TOP;
+            default:
               break;
           }
         }
       }
 
       // baseline coord that may be adjusted for script offset
       nscoord revisedBaselineBCoord = baselineBCoord;
 
       // For superscript and subscript, raise or lower the baseline of the box
       // to the proper offset of the parent's box, then proceed as for BASELINE
-      if (verticalAlignEnum == NS_STYLE_VERTICAL_ALIGN_SUB ||
-          verticalAlignEnum == NS_STYLE_VERTICAL_ALIGN_SUPER) {
-        revisedBaselineBCoord +=
-            lineWM.FlowRelativeToLineRelativeFactor() *
-            (verticalAlignEnum == NS_STYLE_VERTICAL_ALIGN_SUB
-                 ? fm->SubscriptOffset()
-                 : -fm->SuperscriptOffset());
-        verticalAlignEnum = NS_STYLE_VERTICAL_ALIGN_BASELINE;
+      if (keyword == StyleVerticalAlignKeyword::Sub ||
+          keyword == StyleVerticalAlignKeyword::Super) {
+        revisedBaselineBCoord += lineWM.FlowRelativeToLineRelativeFactor() *
+                                 (keyword == StyleVerticalAlignKeyword::Sub
+                                      ? fm->SubscriptOffset()
+                                      : -fm->SuperscriptOffset());
+        keyword = StyleVerticalAlignKeyword::Baseline;
       }
 
-      switch (verticalAlignEnum) {
+      switch (keyword) {
         default:
-        case NS_STYLE_VERTICAL_ALIGN_BASELINE:
+        case StyleVerticalAlignKeyword::Baseline:
           if (lineWM.IsVertical() && !lineWM.IsSideways()) {
             if (frameSpan) {
               pfd->mBounds.BStart(lineWM) =
                   revisedBaselineBCoord - pfd->mBounds.BSize(lineWM) / 2;
             } else {
               pfd->mBounds.BStart(lineWM) = revisedBaselineBCoord -
                                             logicalBSize / 2 +
                                             pfd->mMargin.BStart(lineWM);
             }
           } else {
             pfd->mBounds.BStart(lineWM) = revisedBaselineBCoord - pfd->mAscent;
           }
           pfd->mBlockDirAlign = VALIGN_OTHER;
           break;
 
-        case NS_STYLE_VERTICAL_ALIGN_TOP: {
+        case StyleVerticalAlignKeyword::Top: {
           pfd->mBlockDirAlign = VALIGN_TOP;
           nscoord subtreeBSize = logicalBSize;
           if (frameSpan) {
             subtreeBSize = frameSpan->mMaxBCoord - frameSpan->mMinBCoord;
             NS_ASSERTION(subtreeBSize >= logicalBSize,
                          "unexpected subtree block size");
           }
           if (subtreeBSize > maxStartBoxBSize) {
             maxStartBoxBSize = subtreeBSize;
           }
           break;
         }
 
-        case NS_STYLE_VERTICAL_ALIGN_BOTTOM: {
+        case StyleVerticalAlignKeyword::Bottom: {
           pfd->mBlockDirAlign = VALIGN_BOTTOM;
           nscoord subtreeBSize = logicalBSize;
           if (frameSpan) {
             subtreeBSize = frameSpan->mMaxBCoord - frameSpan->mMinBCoord;
             NS_ASSERTION(subtreeBSize >= logicalBSize,
                          "unexpected subtree block size");
           }
           if (subtreeBSize > maxEndBoxBSize) {
             maxEndBoxBSize = subtreeBSize;
           }
           break;
         }
 
-        case NS_STYLE_VERTICAL_ALIGN_MIDDLE: {
+        case StyleVerticalAlignKeyword::Middle: {
           // Align the midpoint of the frame with 1/2 the parents
           // x-height above the baseline.
           nscoord parentXHeight =
               lineWM.FlowRelativeToLineRelativeFactor() * fm->XHeight();
           if (frameSpan) {
             pfd->mBounds.BStart(lineWM) =
                 baselineBCoord -
                 (parentXHeight + pfd->mBounds.BSize(lineWM)) / 2;
@@ -2079,17 +2082,17 @@ void nsLineLayout::VerticalAlignFrames(P
             pfd->mBounds.BStart(lineWM) = baselineBCoord -
                                           (parentXHeight + logicalBSize) / 2 +
                                           pfd->mMargin.BStart(lineWM);
           }
           pfd->mBlockDirAlign = VALIGN_OTHER;
           break;
         }
 
-        case NS_STYLE_VERTICAL_ALIGN_TEXT_TOP: {
+        case StyleVerticalAlignKeyword::TextTop: {
           // The top of the logical box is aligned with the top of
           // the parent element's text.
           // XXX For vertical text we will need a new API to get the logical
           //     max-ascent here
           nscoord parentAscent =
               lineWM.IsLineInverted() ? fm->MaxDescent() : fm->MaxAscent();
           if (frameSpan) {
             pfd->mBounds.BStart(lineWM) = baselineBCoord - parentAscent -
@@ -2098,17 +2101,17 @@ void nsLineLayout::VerticalAlignFrames(P
           } else {
             pfd->mBounds.BStart(lineWM) =
                 baselineBCoord - parentAscent + pfd->mMargin.BStart(lineWM);
           }
           pfd->mBlockDirAlign = VALIGN_OTHER;
           break;
         }
 
-        case NS_STYLE_VERTICAL_ALIGN_TEXT_BOTTOM: {
+        case StyleVerticalAlignKeyword::TextBottom: {
           // The bottom of the logical box is aligned with the
           // bottom of the parent elements text.
           nscoord parentDescent =
               lineWM.IsLineInverted() ? fm->MaxAscent() : fm->MaxDescent();
           if (frameSpan) {
             pfd->mBounds.BStart(lineWM) =
                 baselineBCoord + parentDescent - pfd->mBounds.BSize(lineWM) +
                 pfd->mBorderPadding.BEnd(lineWM) - frameSpan->mBEndLeading;
@@ -2116,42 +2119,41 @@ void nsLineLayout::VerticalAlignFrames(P
             pfd->mBounds.BStart(lineWM) = baselineBCoord + parentDescent -
                                           pfd->mBounds.BSize(lineWM) -
                                           pfd->mMargin.BEnd(lineWM);
           }
           pfd->mBlockDirAlign = VALIGN_OTHER;
           break;
         }
 
-        case NS_STYLE_VERTICAL_ALIGN_MIDDLE_WITH_BASELINE: {
+        case StyleVerticalAlignKeyword::MozMiddleWithBaseline: {
           // Align the midpoint of the frame with the baseline of the parent.
           if (frameSpan) {
             pfd->mBounds.BStart(lineWM) =
                 baselineBCoord - pfd->mBounds.BSize(lineWM) / 2;
           } else {
             pfd->mBounds.BStart(lineWM) =
                 baselineBCoord - logicalBSize / 2 + pfd->mMargin.BStart(lineWM);
           }
           pfd->mBlockDirAlign = VALIGN_OTHER;
           break;
         }
       }
     } else {
       // We have either a coord, a percent, or a calc().
-      nscoord pctBasis = 0;
-      if (verticalAlign.HasPercent()) {
+      nscoord offset = verticalAlign.AsLength().Resolve([&] {
         // Percentages are like lengths, except treated as a percentage
         // of the elements line block size value.
         float inflation =
             GetInflationForBlockDirAlignment(frame, mInflationMinFontSize);
-        pctBasis = ReflowInput::CalcLineHeight(
+        return ReflowInput::CalcLineHeight(
             frame->GetContent(), frame->Style(), frame->PresContext(),
             mBlockReflowInput->ComputedBSize(), inflation);
-      }
-      nscoord offset = verticalAlign.ComputeCoordPercentCalc(pctBasis);
+      });
+
       // According to the CSS2 spec (10.8.1), a positive value
       // "raises" the box by the given distance while a negative value
       // "lowers" the box by the given distance (with zero being the
       // baseline). Since Y coordinates increase towards the bottom of
       // the screen we reverse the sign, unless the line orientation is
       // inverted relative to block direction.
       nscoord revisedBaselineBCoord =
           baselineBCoord - offset * lineWM.FlowRelativeToLineRelativeFactor();
--- a/layout/generic/nsTextFrame.cpp
+++ b/layout/generic/nsTextFrame.cpp
@@ -1843,19 +1843,22 @@ bool BuildTextRunsScanner::ContinueTextR
             if (!padding.ConvertsToLength() || padding.ToLength() != 0) {
               return true;
             }
             if (ctx->StyleBorder()->GetComputedBorderWidth(aSide) != 0) {
               return true;
             }
 
             // 2. vertical-align is not baseline.
-            const nsStyleCoord& coord = ctx->StyleDisplay()->mVerticalAlign;
-            if (coord.GetUnit() != eStyleUnit_Enumerated ||
-                coord.GetIntValue() != NS_STYLE_VERTICAL_ALIGN_BASELINE) {
+            //
+            // FIXME: Should this use VerticalAlignEnum()?
+            const auto& verticalAlign = ctx->StyleDisplay()->mVerticalAlign;
+            if (!verticalAlign.IsKeyword() ||
+                verticalAlign.AsKeyword() !=
+                    StyleVerticalAlignKeyword::Baseline) {
               return true;
             }
 
             // 3. The boundary is a bidi isolation boundary.
             const uint8_t unicodeBidi = ctx->StyleTextReset()->mUnicodeBidi;
             if (unicodeBidi == NS_STYLE_UNICODE_BIDI_ISOLATE ||
                 unicodeBidi == NS_STYLE_UNICODE_BIDI_ISOLATE_OVERRIDE) {
               return true;
@@ -5017,17 +5020,19 @@ void nsTextFrame::GetTextDecorations(
     // Not updating positions once we hit a parent block is equivalent to
     // the CSS 2.1 spec that blocks should propagate decorations down to their
     // children (albeit the style should be preserved)
     // However, if we're vertically aligned within a block, then we need to
     // recover the correct baseline from the line by querying the FrameProperty
     // that should be set (see nsLineLayout::VerticalAlignLine).
     if (firstBlock) {
       // At this point, fChild can't be null since TextFrames can't be blocks
-      if (fChild->VerticalAlignEnum() != NS_STYLE_VERTICAL_ALIGN_BASELINE) {
+      Maybe<StyleVerticalAlignKeyword> verticalAlign =
+          fChild->VerticalAlignEnum();
+      if (verticalAlign != Some(StyleVerticalAlignKeyword::Baseline)) {
         // Since offset is the offset in the child's coordinate space, we have
         // to undo the accumulation to bring the transform out of the block's
         // coordinate space
         const nscoord lineBaselineOffset =
             LazyGetLineBaselineOffset(fChild, fBlock);
 
         baselineOffset = physicalBlockStartOffset - lineBaselineOffset -
                          (vertical ? fChild->GetNormalPosition().x
--- a/layout/mathml/nsMathMLmtableFrame.cpp
+++ b/layout/mathml/nsMathMLmtableFrame.cpp
@@ -29,23 +29,23 @@ using namespace mozilla::image;
 //
 // <mtable> -- table or matrix - implementation
 //
 
 static int8_t ParseStyleValue(nsAtom* aAttribute,
                               const nsAString& aAttributeValue) {
   if (aAttribute == nsGkAtoms::rowalign_) {
     if (aAttributeValue.EqualsLiteral("top"))
-      return NS_STYLE_VERTICAL_ALIGN_TOP;
+      return static_cast<int8_t>(StyleVerticalAlignKeyword::Top);
     else if (aAttributeValue.EqualsLiteral("bottom"))
-      return NS_STYLE_VERTICAL_ALIGN_BOTTOM;
+      return static_cast<int8_t>(StyleVerticalAlignKeyword::Bottom);
     else if (aAttributeValue.EqualsLiteral("center"))
-      return NS_STYLE_VERTICAL_ALIGN_MIDDLE;
+      return static_cast<int8_t>(StyleVerticalAlignKeyword::Middle);
     else
-      return NS_STYLE_VERTICAL_ALIGN_BASELINE;
+      return static_cast<int8_t>(StyleVerticalAlignKeyword::Baseline);
   } else if (aAttribute == nsGkAtoms::columnalign_) {
     if (aAttributeValue.EqualsLiteral("left"))
       return NS_STYLE_TEXT_ALIGN_LEFT;
     else if (aAttributeValue.EqualsLiteral("right"))
       return NS_STYLE_TEXT_ALIGN_RIGHT;
     else
       return NS_STYLE_TEXT_ALIGN_CENTER;
   } else if (aAttribute == nsGkAtoms::rowlines_ ||
@@ -1119,31 +1119,31 @@ nsresult nsMathMLmtdFrame::AttributeChan
     if (aAttribute == nsGkAtoms::columnspan_) aAttribute = nsGkAtoms::colspan;
     return nsTableCellFrame::AttributeChanged(aNameSpaceID, aAttribute,
                                               aModType);
   }
 
   return NS_OK;
 }
 
-uint8_t nsMathMLmtdFrame::GetVerticalAlign() const {
+StyleVerticalAlignKeyword nsMathMLmtdFrame::GetVerticalAlign() const {
   // Set the default alignment in case no alignment was specified
-  uint8_t alignment = nsTableCellFrame::GetVerticalAlign();
+  auto alignment = nsTableCellFrame::GetVerticalAlign();
 
   nsTArray<int8_t>* alignmentList = FindCellProperty(this, RowAlignProperty());
 
   if (alignmentList) {
     uint32_t rowIndex = RowIndex();
 
     // If the row number is greater than the number of provided rowalign values,
     // we simply repeat the last value.
-    if (rowIndex < alignmentList->Length())
-      alignment = alignmentList->ElementAt(rowIndex);
-    else
-      alignment = alignmentList->ElementAt(alignmentList->Length() - 1);
+    return static_cast<StyleVerticalAlignKeyword>(
+        (rowIndex < alignmentList->Length())
+            ? alignmentList->ElementAt(rowIndex)
+            : alignmentList->LastElement());
   }
 
   return alignment;
 }
 
 nsresult nsMathMLmtdFrame::ProcessBorders(nsTableFrame* aFrame,
                                           nsDisplayListBuilder* aBuilder,
                                           const nsDisplayListSet& aLists) {
--- a/layout/mathml/nsMathMLmtableFrame.h
+++ b/layout/mathml/nsMathMLmtableFrame.h
@@ -218,17 +218,17 @@ class nsMathMLmtdFrame final : public ns
   // overloaded nsTableCellFrame methods
 
   virtual void Init(nsIContent* aContent, nsContainerFrame* aParent,
                     nsIFrame* aPrevInFlow) override;
 
   virtual nsresult AttributeChanged(int32_t aNameSpaceID, nsAtom* aAttribute,
                                     int32_t aModType) override;
 
-  virtual uint8_t GetVerticalAlign() const override;
+  virtual mozilla::StyleVerticalAlignKeyword GetVerticalAlign() const override;
   virtual nsresult ProcessBorders(nsTableFrame* aFrame,
                                   nsDisplayListBuilder* aBuilder,
                                   const nsDisplayListSet& aLists) override;
 
   virtual bool IsFrameOfType(uint32_t aFlags) const override {
     return nsTableCellFrame::IsFrameOfType(aFlags & ~(nsIFrame::eMathML));
   }
 
--- a/layout/reftests/bugs/reftest.list
+++ b/layout/reftests/bugs/reftest.list
@@ -2003,17 +2003,17 @@ random-if(!winWidget) == 1273154-2.html 
 == 1275411-1.html 1275411-1-ref.html
 == 1288255.html 1288255-ref.html
 fuzzy(0-8,0-1900) fails-if(webrender) == 1291528.html 1291528-ref.html
 # Buttons in 2 pages have different position and the rendering result can be
 # different, but they should use the same button style and the background color
 # should be same.  |fuzzy()| here allows the difference in border, but not
 # background color.
 fuzzy(0-255,0-1000) skip-if(!cocoaWidget) == 1294102-1.html 1294102-1-ref.html
-random-if(Android) fuzzy-if(skiaContent,0-15,0-50) == 1295466-1.xhtml 1295466-1-ref.xhtml #bug 982547
+random-if(Android) fuzzy-if(skiaContent,0-15,0-50) fuzzy-if(gtkWidget,0-14,0-215) == 1295466-1.xhtml 1295466-1-ref.xhtml #bug 982547 #bug 1540635
 fuzzy-if(Android,0-27,0-874) fuzzy-if(!Android,0-14,0-43) == 1313772.xhtml 1313772-ref.xhtml # Bug 1128229, Bug 1389319
 fuzzy(0-3,0-320000) == 1315113-1.html 1315113-1-ref.html
 fuzzy(0-3,0-20000) == 1315113-2.html 1315113-2-ref.html
 == 1315632-1.html 1315632-1-ref.html
 fuzzy(0-2,0-40000) fuzzy-if(/^Windows\x20NT\x2010\.0/.test(http.oscpu),0-13,0-40000) == 1316719-1a.html 1316719-1-ref.html
 fuzzy(0-13,0-40000) fuzzy-if(/^Windows\x20NT\x2010\.0/.test(http.oscpu),0-13,0-40000) == 1316719-1b.html 1316719-1-ref.html
 fuzzy(0-13,0-40000) fuzzy-if(/^Windows\x20NT\x2010\.0/.test(http.oscpu),0-13,0-40000) == 1316719-1c.html 1316719-1-ref.html
 skip-if(Android) != 1318769-1.html 1318769-1-ref.html
--- a/layout/style/ServoBindings.toml
+++ b/layout/style/ServoBindings.toml
@@ -465,16 +465,18 @@ cbindgen-types = [
     { gecko = "StyleGenericFontFamily", servo = "values::computed::font::GenericFontFamily" },
     { gecko = "StyleFontFamilyNameSyntax", servo = "values::computed::font::FontFamilyNameSyntax" },
     { gecko = "StyleGenericColor", servo = "values::generics::color::Color" },
     { gecko = "StyleGenericColorOrAuto", servo = "values::generics::color::ColorOrAuto" },
     { gecko = "StyleGenericScrollbarColor", servo = "values::generics::ui::ScrollbarColor" },
     { gecko = "StyleRGBA", servo = "cssparser::RGBA" },
     { gecko = "StyleOrigin", servo = "stylesheets::Origin" },
     { gecko = "StyleGenericGradientItem", servo = "values::generics::image::GradientItem" },
+    { gecko = "StyleGenericVerticalAlign", servo = "values::generics::box_::VerticalAlign" },
+    { gecko = "StyleVerticalAlignKeyword", servo = "values::generics::box_::VerticalAlignKeyword" },
 ]
 
 mapped-generic-types = [
     { generic = true, gecko = "mozilla::RustCell", servo = "::std::cell::Cell" },
     { generic = false, gecko = "ServoNodeData", servo = "AtomicRefCell<ElementData>" },
     { generic = false, gecko = "mozilla::ServoWritingMode", servo = "::logical_geometry::WritingMode" },
     { generic = false, gecko = "mozilla::ServoCustomPropertiesMap", servo = "Option<::servo_arc::Arc<::custom_properties::CustomPropertiesMap>>" },
     { generic = false, gecko = "mozilla::ServoRuleNode", servo = "Option<::rule_tree::StrongRuleNode>" },
--- a/layout/style/nsCSSProps.cpp
+++ b/layout/style/nsCSSProps.cpp
@@ -376,29 +376,16 @@ const KTableEntry nsCSSProps::kTextEmpha
     {eCSSKeyword_sesame, NS_STYLE_TEXT_EMPHASIS_STYLE_SESAME},
     {eCSSKeyword_UNKNOWN, -1}};
 
 const KTableEntry nsCSSProps::kTextOverflowKTable[] = {
     {eCSSKeyword_clip, NS_STYLE_TEXT_OVERFLOW_CLIP},
     {eCSSKeyword_ellipsis, NS_STYLE_TEXT_OVERFLOW_ELLIPSIS},
     {eCSSKeyword_UNKNOWN, -1}};
 
-const KTableEntry nsCSSProps::kVerticalAlignKTable[] = {
-    {eCSSKeyword_baseline, NS_STYLE_VERTICAL_ALIGN_BASELINE},
-    {eCSSKeyword_sub, NS_STYLE_VERTICAL_ALIGN_SUB},
-    {eCSSKeyword_super, NS_STYLE_VERTICAL_ALIGN_SUPER},
-    {eCSSKeyword_top, NS_STYLE_VERTICAL_ALIGN_TOP},
-    {eCSSKeyword_text_top, NS_STYLE_VERTICAL_ALIGN_TEXT_TOP},
-    {eCSSKeyword_middle, NS_STYLE_VERTICAL_ALIGN_MIDDLE},
-    {eCSSKeyword__moz_middle_with_baseline,
-     NS_STYLE_VERTICAL_ALIGN_MIDDLE_WITH_BASELINE},
-    {eCSSKeyword_bottom, NS_STYLE_VERTICAL_ALIGN_BOTTOM},
-    {eCSSKeyword_text_bottom, NS_STYLE_VERTICAL_ALIGN_TEXT_BOTTOM},
-    {eCSSKeyword_UNKNOWN, -1}};
-
 // keyword tables for SVG properties
 
 const KTableEntry nsCSSProps::kShapeRadiusKTable[] = {
     {eCSSKeyword_closest_side, StyleShapeRadius::ClosestSide},
     {eCSSKeyword_farthest_side, StyleShapeRadius::FarthestSide},
     {eCSSKeyword_UNKNOWN, -1}};
 
 const KTableEntry nsCSSProps::kFilterFunctionKTable[] = {
--- a/layout/style/nsCSSProps.h
+++ b/layout/style/nsCSSProps.h
@@ -306,17 +306,16 @@ class nsCSSProps {
   static const KTableEntry kFontSmoothingKTable[];
   static const KTableEntry kGridAutoFlowKTable[];
   static const KTableEntry kGridTrackBreadthKTable[];
   static const KTableEntry kLineHeightKTable[];
   static const KTableEntry kTextAlignKTable[];
   static const KTableEntry kTextDecorationStyleKTable[];
   static const KTableEntry kTextEmphasisStyleShapeKTable[];
   static const KTableEntry kTextOverflowKTable[];
-  static const KTableEntry kVerticalAlignKTable[];
 };
 
 // MOZ_DBG support for nsCSSPropertyID
 
 inline std::ostream& operator<<(std::ostream& aOut, nsCSSPropertyID aProperty) {
   return aOut << nsCSSProps::GetStringValue(aProperty);
 }
 
--- a/layout/style/nsStyleConsts.h
+++ b/layout/style/nsStyleConsts.h
@@ -544,19 +544,16 @@ enum class StyleGridTrackBreadth : uint8
 #define NS_STYLE_TEXT_ALIGN_CHAR \
   5  // align based on a certain character, for table cell
 #define NS_STYLE_TEXT_ALIGN_END 6
 #define NS_STYLE_TEXT_ALIGN_AUTO 7
 #define NS_STYLE_TEXT_ALIGN_MOZ_CENTER 8
 #define NS_STYLE_TEXT_ALIGN_MOZ_RIGHT 9
 #define NS_STYLE_TEXT_ALIGN_MOZ_LEFT 10
 
-// Note: make sure that the largest NS_STYLE_TEXT_ALIGN_* value is smaller than
-// the smallest NS_STYLE_VERTICAL_ALIGN_* value below!
-
 // See nsStyleText
 #define NS_STYLE_TEXT_DECORATION_STYLE_NONE \
   0  // not in CSS spec, mapped to -moz-none
 #define NS_STYLE_TEXT_DECORATION_STYLE_DOTTED 1
 #define NS_STYLE_TEXT_DECORATION_STYLE_DASHED 2
 #define NS_STYLE_TEXT_DECORATION_STYLE_SOLID 3
 #define NS_STYLE_TEXT_DECORATION_STYLE_DOUBLE 4
 #define NS_STYLE_TEXT_DECORATION_STYLE_WAVY 5
@@ -574,30 +571,16 @@ enum class StyleGridTrackBreadth : uint8
 #define NS_STYLE_TEXT_TRANSFORM_UPPERCASE 3
 #define NS_STYLE_TEXT_TRANSFORM_FULL_WIDTH 4
 #define NS_STYLE_TEXT_TRANSFORM_FULL_SIZE_KANA 5
 
 // See nsStyleDisplay
 #define NS_STYLE_TOP_LAYER_NONE 0  // not in the top layer
 #define NS_STYLE_TOP_LAYER_TOP 1   // in the top layer
 
-// See nsStyleText
-// Note: these values pickup after the text-align values because there
-// are a few html cases where an object can have both types of
-// alignment applied with a single attribute
-#define NS_STYLE_VERTICAL_ALIGN_BASELINE 14
-#define NS_STYLE_VERTICAL_ALIGN_SUB 15
-#define NS_STYLE_VERTICAL_ALIGN_SUPER 16
-#define NS_STYLE_VERTICAL_ALIGN_TOP 17
-#define NS_STYLE_VERTICAL_ALIGN_TEXT_TOP 18
-#define NS_STYLE_VERTICAL_ALIGN_MIDDLE 19
-#define NS_STYLE_VERTICAL_ALIGN_TEXT_BOTTOM 20
-#define NS_STYLE_VERTICAL_ALIGN_BOTTOM 21
-#define NS_STYLE_VERTICAL_ALIGN_MIDDLE_WITH_BASELINE 22
-
 // See nsStyleVisibility
 #define NS_STYLE_VISIBILITY_HIDDEN 0
 #define NS_STYLE_VISIBILITY_VISIBLE 1
 #define NS_STYLE_VISIBILITY_COLLAPSE 2
 
 // See nsStyleText
 #define NS_STYLE_TABSIZE_INITIAL 8
 
--- a/layout/style/nsStyleStruct.cpp
+++ b/layout/style/nsStyleStruct.cpp
@@ -2960,17 +2960,18 @@ nsStyleDisplay::nsStyleDisplay(const Doc
       mBackfaceVisibility(NS_STYLE_BACKFACE_VISIBILITY_VISIBLE),
       mTransformStyle(NS_STYLE_TRANSFORM_STYLE_FLAT),
       mTransformBox(StyleGeometryBox::BorderBox),
       mTransformOrigin{LengthPercentage::FromPercentage(0.5),
                        LengthPercentage::FromPercentage(0.5),
                        {0.}},
       mChildPerspective(StylePerspective::None()),
       mPerspectiveOrigin(Position::FromPercentage(0.5f)),
-      mVerticalAlign(NS_STYLE_VERTICAL_ALIGN_BASELINE, eStyleUnit_Enumerated),
+      mVerticalAlign(
+          StyleVerticalAlign::Keyword(StyleVerticalAlignKeyword::Baseline)),
       mTransitions(
           nsStyleAutoArray<StyleTransition>::WITH_SINGLE_INITIAL_ELEMENT),
       mTransitionTimingFunctionCount(1),
       mTransitionDurationCount(1),
       mTransitionDelayCount(1),
       mTransitionPropertyCount(1),
       mAnimations(
           nsStyleAutoArray<StyleAnimation>::WITH_SINGLE_INITIAL_ELEMENT),
--- a/layout/style/nsStyleStruct.h
+++ b/layout/style/nsStyleStruct.h
@@ -1917,18 +1917,17 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsSt
   // mSpecifiedTranslate, and mSpecifiedScale.
   RefPtr<nsCSSValueSharedList> mIndividualTransform;
   mozilla::UniquePtr<mozilla::StyleMotion> mMotion;
 
   mozilla::StyleTransformOrigin mTransformOrigin;
   mozilla::StylePerspective mChildPerspective;
   mozilla::Position mPerspectiveOrigin;
 
-  nsStyleCoord mVerticalAlign;  // coord, percent, calc, enum
-                                // (NS_STYLE_VERTICAL_ALIGN_*)
+  mozilla::StyleVerticalAlign mVerticalAlign;
 
   nsStyleAutoArray<mozilla::StyleTransition> mTransitions;
 
   // The number of elements in mTransitions that are not from repeating
   // a list due to another property being longer.
   uint32_t mTransitionTimingFunctionCount;
   uint32_t mTransitionDurationCount;
   uint32_t mTransitionDelayCount;
--- a/layout/tables/nsTableCellFrame.cpp
+++ b/layout/tables/nsTableCellFrame.cpp
@@ -518,49 +518,47 @@ nsMargin nsTableCellFrame::GetBorderOver
 
 void nsTableCellFrame::BlockDirAlignChild(WritingMode aWM, nscoord aMaxAscent) {
   /* It's the 'border-collapse' on the table that matters */
   LogicalMargin borderPadding = GetLogicalUsedBorderAndPadding(aWM);
 
   nscoord bStartInset = borderPadding.BStart(aWM);
   nscoord bEndInset = borderPadding.BEnd(aWM);
 
-  uint8_t verticalAlignFlags = GetVerticalAlign();
-
   nscoord bSize = BSize(aWM);
   nsIFrame* firstKid = mFrames.FirstChild();
   nsSize containerSize = mRect.Size();
   NS_ASSERTION(firstKid,
                "Frame construction error, a table cell always has "
                "an inner cell frame");
   LogicalRect kidRect = firstKid->GetLogicalRect(aWM, containerSize);
   nscoord childBSize = kidRect.BSize(aWM);
 
   // Vertically align the child
   nscoord kidBStart = 0;
-  switch (verticalAlignFlags) {
-    case NS_STYLE_VERTICAL_ALIGN_BASELINE:
+  switch (GetVerticalAlign()) {
+    case StyleVerticalAlignKeyword::Baseline:
       // Align the baselines of the child frame with the baselines of
       // other children in the same row which have 'vertical-align: baseline'
       kidBStart = bStartInset + aMaxAscent - GetCellBaseline();
       break;
 
-    case NS_STYLE_VERTICAL_ALIGN_TOP:
+    case StyleVerticalAlignKeyword::Top:
       // Align the top of the child frame with the top of the content area,
       kidBStart = bStartInset;
       break;
 
-    case NS_STYLE_VERTICAL_ALIGN_BOTTOM:
+    case StyleVerticalAlignKeyword::Bottom:
       // Align the bottom of the child frame with the bottom of the content
       // area,
       kidBStart = bSize - childBSize - bEndInset;
       break;
 
     default:
-    case NS_STYLE_VERTICAL_ALIGN_MIDDLE:
+    case StyleVerticalAlignKeyword::Middle:
       // Align the middle of the child frame with the middle of the content
       // area,
       kidBStart = (bSize - childBSize - bEndInset + bStartInset) / 2;
   }
   // If the content is larger than the cell bsize, align from bStartInset
   // (cell's content-box bstart edge).
   kidBStart = std::max(bStartInset, kidBStart);
 
@@ -598,27 +596,27 @@ bool nsTableCellFrame::ComputeCustomOver
   bounds.Inflate(GetBorderOverflow());
 
   aOverflowAreas.UnionAllWith(bounds);
   return nsContainerFrame::ComputeCustomOverflow(aOverflowAreas);
 }
 
 // Per CSS 2.1, we map 'sub', 'super', 'text-top', 'text-bottom',
 // length, percentage, and calc() values to 'baseline'.
-uint8_t nsTableCellFrame::GetVerticalAlign() const {
-  const nsStyleCoord& verticalAlign = StyleDisplay()->mVerticalAlign;
-  if (verticalAlign.GetUnit() == eStyleUnit_Enumerated) {
-    uint8_t value = verticalAlign.GetIntValue();
-    if (value == NS_STYLE_VERTICAL_ALIGN_TOP ||
-        value == NS_STYLE_VERTICAL_ALIGN_MIDDLE ||
-        value == NS_STYLE_VERTICAL_ALIGN_BOTTOM) {
+StyleVerticalAlignKeyword nsTableCellFrame::GetVerticalAlign() const {
+  const StyleVerticalAlign& verticalAlign = StyleDisplay()->mVerticalAlign;
+  if (verticalAlign.IsKeyword()) {
+    auto value = verticalAlign.AsKeyword();
+    if (value == StyleVerticalAlignKeyword::Top ||
+        value == StyleVerticalAlignKeyword::Middle ||
+        value == StyleVerticalAlignKeyword::Bottom) {
       return value;
     }
   }
-  return NS_STYLE_VERTICAL_ALIGN_BASELINE;
+  return StyleVerticalAlignKeyword::Baseline;
 }
 
 bool nsTableCellFrame::CellHasVisibleContent(nscoord height,
                                              nsTableFrame* tableFrame,
                                              nsIFrame* kidFrame) {
   // see  http://www.w3.org/TR/CSS21/tables.html#empty-cells
   if (height > 0) return true;
   if (tableFrame->IsBorderCollapse()) return true;
--- a/layout/tables/nsTableCellFrame.h
+++ b/layout/tables/nsTableCellFrame.h
@@ -119,22 +119,22 @@ class nsTableCellFrame : public nsContai
   virtual nsresult GetFrameName(nsAString& aResult) const override;
 #endif
 
   void BlockDirAlignChild(mozilla::WritingMode aWM, nscoord aMaxAscent);
 
   /*
    * Get the value of vertical-align adjusted for CSS 2's rules for a
    * table cell, which means the result is always
-   * NS_STYLE_VERTICAL_ALIGN_{TOP,MIDDLE,BOTTOM,BASELINE}.
+   * StyleVerticalAlignKeyword::{Top,Middle,Bottom,Baseline}.
    */
-  virtual uint8_t GetVerticalAlign() const;
+  virtual mozilla::StyleVerticalAlignKeyword GetVerticalAlign() const;
 
   bool HasVerticalAlignBaseline() const {
-    return GetVerticalAlign() == NS_STYLE_VERTICAL_ALIGN_BASELINE;
+    return GetVerticalAlign() == mozilla::StyleVerticalAlignKeyword::Baseline;
   }
 
   bool CellHasVisibleContent(nscoord aBSize, nsTableFrame* tableFrame,
                              nsIFrame* kidFrame);
 
   /**
    * Get the first-line baseline of the cell relative to its block-start border
    * edge, as if the cell were vertically aligned to the top of the row.
--- a/layout/tables/nsTableWrapperFrame.cpp
+++ b/layout/tables/nsTableWrapperFrame.cpp
@@ -455,22 +455,19 @@ LogicalSize nsTableWrapperFrame::Compute
 uint8_t nsTableWrapperFrame::GetCaptionSide() {
   if (mCaptionFrames.NotEmpty()) {
     return mCaptionFrames.FirstChild()->StyleTableBorder()->mCaptionSide;
   } else {
     return NO_SIDE;  // no caption
   }
 }
 
-uint8_t nsTableWrapperFrame::GetCaptionVerticalAlign() {
-  const nsStyleCoord& va =
-      mCaptionFrames.FirstChild()->StyleDisplay()->mVerticalAlign;
-
-  return (va.GetUnit() == eStyleUnit_Enumerated) ? va.GetIntValue()
-                                                 : NS_STYLE_VERTICAL_ALIGN_TOP;
+StyleVerticalAlignKeyword nsTableWrapperFrame::GetCaptionVerticalAlign() const {
+  const auto& va = mCaptionFrames.FirstChild()->StyleDisplay()->mVerticalAlign;
+  return va.IsKeyword() ? va.AsKeyword() : StyleVerticalAlignKeyword::Top;
 }
 
 void nsTableWrapperFrame::SetDesiredSize(uint8_t aCaptionSide,
                                          const LogicalSize& aInnerSize,
                                          const LogicalSize& aCaptionSize,
                                          const LogicalMargin& aInnerMargin,
                                          const LogicalMargin& aCaptionMargin,
                                          nscoord& aISize, nscoord& aBSize,
@@ -583,22 +580,22 @@ nsresult nsTableWrapperFrame::GetCaption
       break;
   }
   // block-dir computation
   switch (aCaptionSide) {
     case NS_STYLE_CAPTION_SIDE_RIGHT:
     case NS_STYLE_CAPTION_SIDE_LEFT:
       aOrigin.B(aWM) = aInnerMargin.BStart(aWM);
       switch (GetCaptionVerticalAlign()) {
-        case NS_STYLE_VERTICAL_ALIGN_MIDDLE:
+        case StyleVerticalAlignKeyword::Middle:
           aOrigin.B(aWM) = std::max(
               0, aInnerMargin.BStart(aWM) +
                      ((aInnerSize.BSize(aWM) - aCaptionSize.BSize(aWM)) / 2));
           break;
-        case NS_STYLE_VERTICAL_ALIGN_BOTTOM:
+        case StyleVerticalAlignKeyword::Bottom:
           aOrigin.B(aWM) =
               std::max(0, aInnerMargin.BStart(aWM) + aInnerSize.BSize(aWM) -
                               aCaptionSize.BSize(aWM));
           break;
         default:
           break;
       }
       break;
@@ -672,22 +669,22 @@ nsresult nsTableWrapperFrame::GetInnerOr
     case NS_STYLE_CAPTION_SIDE_BOTTOM:
     case NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE:
       aOrigin.B(aWM) = aInnerMargin.BStart(aWM);
       break;
     case NS_STYLE_CAPTION_SIDE_LEFT:
     case NS_STYLE_CAPTION_SIDE_RIGHT:
       aOrigin.B(aWM) = aInnerMargin.BStart(aWM);
       switch (GetCaptionVerticalAlign()) {
-        case NS_STYLE_VERTICAL_ALIGN_MIDDLE:
+        case StyleVerticalAlignKeyword::Middle:
           aOrigin.B(aWM) =
               std::max(aInnerMargin.BStart(aWM),
                        (aCaptionSize.BSize(aWM) - aInnerSize.BSize(aWM)) / 2);
           break;
-        case NS_STYLE_VERTICAL_ALIGN_BOTTOM:
+        case StyleVerticalAlignKeyword::Bottom:
           aOrigin.B(aWM) =
               std::max(aInnerMargin.BStart(aWM),
                        aCaptionSize.BSize(aWM) - aInnerSize.BSize(aWM));
           break;
         default:
           break;
       }
       break;
--- a/layout/tables/nsTableWrapperFrame.h
+++ b/layout/tables/nsTableWrapperFrame.h
@@ -195,17 +195,17 @@ class nsTableWrapperFrame : public nsCon
   uint8_t GetCaptionSide();
 
   bool HasSideCaption() {
     uint8_t captionSide = GetCaptionSide();
     return captionSide == NS_STYLE_CAPTION_SIDE_LEFT ||
            captionSide == NS_STYLE_CAPTION_SIDE_RIGHT;
   }
 
-  uint8_t GetCaptionVerticalAlign();
+  mozilla::StyleVerticalAlignKeyword GetCaptionVerticalAlign() const;
 
   void SetDesiredSize(uint8_t aCaptionSide,
                       const mozilla::LogicalSize& aInnerSize,
                       const mozilla::LogicalSize& aCaptionSize,
                       const mozilla::LogicalMargin& aInnerMargin,
                       const mozilla::LogicalMargin& aCaptionMargin,
                       nscoord& aISize, nscoord& aBSize,
                       mozilla::WritingMode aWM);
--- a/media/webrtc/gn-configs/arm64_False_arm64_freebsd.json
+++ b/media/webrtc/gn-configs/arm64_False_arm64_freebsd.json
@@ -10028,17 +10028,16 @@
                 "//modules/desktop_capture/screen_capturer_helper.h",
                 "//modules/desktop_capture/window_finder.cc",
                 "//modules/desktop_capture/window_finder.h",
                 "//modules/desktop_capture/desktop_device_info.cc",
                 "//modules/desktop_capture/desktop_device_info.h",
                 "//modules/desktop_capture/mouse_cursor_monitor_linux.cc",
                 "//modules/desktop_capture/screen_capturer_linux.cc",
                 "//modules/desktop_capture/window_capturer_linux.cc",
-                "//modules/desktop_capture/app_capturer_linux.cc",
                 "//modules/desktop_capture/linux/mouse_cursor_monitor_x11.cc",
                 "//modules/desktop_capture/linux/mouse_cursor_monitor_x11.h",
                 "//modules/desktop_capture/linux/screen_capturer_x11.cc",
                 "//modules/desktop_capture/linux/screen_capturer_x11.h",
                 "//modules/desktop_capture/linux/shared_x_display.cc",
                 "//modules/desktop_capture/linux/shared_x_display.h",
                 "//modules/desktop_capture/linux/window_capturer_x11.cc",
                 "//modules/desktop_capture/linux/window_capturer_x11.h",
--- a/media/webrtc/gn-configs/arm64_False_arm64_linux.json
+++ b/media/webrtc/gn-configs/arm64_False_arm64_linux.json
@@ -11314,17 +11314,16 @@
                 "//modules/desktop_capture/screen_capturer_helper.h",
                 "//modules/desktop_capture/window_finder.cc",
                 "//modules/desktop_capture/window_finder.h",
                 "//modules/desktop_capture/desktop_device_info.cc",
                 "//modules/desktop_capture/desktop_device_info.h",
                 "//modules/desktop_capture/mouse_cursor_monitor_linux.cc",
                 "//modules/desktop_capture/screen_capturer_linux.cc",
                 "//modules/desktop_capture/window_capturer_linux.cc",
-                "//modules/desktop_capture/app_capturer_linux.cc",
                 "//modules/desktop_capture/linux/mouse_cursor_monitor_x11.cc",
                 "//modules/desktop_capture/linux/mouse_cursor_monitor_x11.h",
                 "//modules/desktop_capture/linux/screen_capturer_x11.cc",
                 "//modules/desktop_capture/linux/screen_capturer_x11.h",
                 "//modules/desktop_capture/linux/shared_x_display.cc",
                 "//modules/desktop_capture/linux/shared_x_display.h",
                 "//modules/desktop_capture/linux/window_capturer_x11.cc",
                 "//modules/desktop_capture/linux/window_capturer_x11.h",
--- a/media/webrtc/gn-configs/arm64_False_arm64_netbsd.json
+++ b/media/webrtc/gn-configs/arm64_False_arm64_netbsd.json
@@ -7491,17 +7491,16 @@
                 "//modules/desktop_capture/screen_capturer_helper.h",
                 "//modules/desktop_capture/window_finder.cc",
                 "//modules/desktop_capture/window_finder.h",
                 "//modules/desktop_capture/desktop_device_info.cc",
                 "//modules/desktop_capture/desktop_device_info.h",
                 "//modules/desktop_capture/mouse_cursor_monitor_linux.cc",
                 "//modules/desktop_capture/screen_capturer_linux.cc",
                 "//modules/desktop_capture/window_capturer_linux.cc",
-                "//modules/desktop_capture/app_capturer_linux.cc",
                 "//modules/desktop_capture/linux/mouse_cursor_monitor_x11.cc",
                 "//modules/desktop_capture/linux/mouse_cursor_monitor_x11.h",
                 "//modules/desktop_capture/linux/screen_capturer_x11.cc",
                 "//modules/desktop_capture/linux/screen_capturer_x11.h",
                 "//modules/desktop_capture/linux/shared_x_display.cc",
                 "//modules/desktop_capture/linux/shared_x_display.h",
                 "//modules/desktop_capture/linux/window_capturer_x11.cc",
                 "//modules/desktop_capture/linux/window_capturer_x11.h",
--- a/media/webrtc/gn-configs/arm64_False_arm64_openbsd.json
+++ b/media/webrtc/gn-configs/arm64_False_arm64_openbsd.json
@@ -10028,17 +10028,16 @@
                 "//modules/desktop_capture/screen_capturer_helper.h",
                 "//modules/desktop_capture/window_finder.cc",
                 "//modules/desktop_capture/window_finder.h",
                 "//modules/desktop_capture/desktop_device_info.cc",
                 "//modules/desktop_capture/desktop_device_info.h",
                 "//modules/desktop_capture/mouse_cursor_monitor_linux.cc",
                 "//modules/desktop_capture/screen_capturer_linux.cc",
                 "//modules/desktop_capture/window_capturer_linux.cc",
-                "//modules/desktop_capture/app_capturer_linux.cc",
                 "//modules/desktop_capture/linux/mouse_cursor_monitor_x11.cc",
                 "//modules/desktop_capture/linux/mouse_cursor_monitor_x11.h",
                 "//modules/desktop_capture/linux/screen_capturer_x11.cc",
                 "//modules/desktop_capture/linux/screen_capturer_x11.h",
                 "//modules/desktop_capture/linux/shared_x_display.cc",
                 "//modules/desktop_capture/linux/shared_x_display.h",
                 "//modules/desktop_capture/linux/window_capturer_x11.cc",
                 "//modules/desktop_capture/linux/window_capturer_x11.h",
--- a/media/webrtc/gn-configs/arm64_True_arm64_freebsd.json
+++ b/media/webrtc/gn-configs/arm64_True_arm64_freebsd.json
@@ -9533,17 +9533,16 @@
                 "//modules/desktop_capture/screen_capturer_helper.h",
                 "//modules/desktop_capture/window_finder.cc",
                 "//modules/desktop_capture/window_finder.h",
                 "//modules/desktop_capture/desktop_device_info.cc",
                 "//modules/desktop_capture/desktop_device_info.h",
                 "//modules/desktop_capture/mouse_cursor_monitor_linux.cc",
                 "//modules/desktop_capture/screen_capturer_linux.cc",
                 "//modules/desktop_capture/window_capturer_linux.cc",
-                "//modules/desktop_capture/app_capturer_linux.cc",
                 "//modules/desktop_capture/linux/mouse_cursor_monitor_x11.cc",
                 "//modules/desktop_capture/linux/mouse_cursor_monitor_x11.h",
                 "//modules/desktop_capture/linux/screen_capturer_x11.cc",
                 "//modules/desktop_capture/linux/screen_capturer_x11.h",
                 "//modules/desktop_capture/linux/shared_x_display.cc",
                 "//modules/desktop_capture/linux/shared_x_display.h",
                 "//modules/desktop_capture/linux/window_capturer_x11.cc",
                 "//modules/desktop_capture/linux/window_capturer_x11.h",
--- a/media/webrtc/gn-configs/arm64_True_arm64_linux.json
+++ b/media/webrtc/gn-configs/arm64_True_arm64_linux.json
@@ -10918,17 +10918,16 @@
                 "//modules/desktop_capture/screen_capturer_helper.h",
                 "//modules/desktop_capture/window_finder.cc",
                 "//modules/desktop_capture/window_finder.h",
                 "//modules/desktop_capture/desktop_device_info.cc",
                 "//modules/desktop_capture/desktop_device_info.h",
                 "//modules/desktop_capture/mouse_cursor_monitor_linux.cc",
                 "//modules/desktop_capture/screen_capturer_linux.cc",
                 "//modules/desktop_capture/window_capturer_linux.cc",
-                "//modules/desktop_capture/app_capturer_linux.cc",
                 "//modules/desktop_capture/linux/mouse_cursor_monitor_x11.cc",
                 "//modules/desktop_capture/linux/mouse_cursor_monitor_x11.h",
                 "//modules/desktop_capture/linux/screen_capturer_x11.cc",
                 "//modules/desktop_capture/linux/screen_capturer_x11.h",
                 "//modules/desktop_capture/linux/shared_x_display.cc",
                 "//modules/desktop_capture/linux/shared_x_display.h",
                 "//modules/desktop_capture/linux/window_capturer_x11.cc",
                 "//modules/desktop_capture/linux/window_capturer_x11.h",
--- a/media/webrtc/gn-configs/arm64_True_arm64_netbsd.json
+++ b/media/webrtc/gn-configs/arm64_True_arm64_netbsd.json
@@ -6996,17 +6996,16 @@
                 "//modules/desktop_capture/screen_capturer_helper.h",
                 "//modules/desktop_capture/window_finder.cc",
                 "//modules/desktop_capture/window_finder.h",
                 "//modules/desktop_capture/desktop_device_info.cc",
                 "//modules/desktop_capture/desktop_device_info.h",
                 "//modules/desktop_capture/mouse_cursor_monitor_linux.cc",
                 "//modules/desktop_capture/screen_capturer_linux.cc",
                 "//modules/desktop_capture/window_capturer_linux.cc",
-                "//modules/desktop_capture/app_capturer_linux.cc",
                 "//modules/desktop_capture/linux/mouse_cursor_monitor_x11.cc",
                 "//modules/desktop_capture/linux/mouse_cursor_monitor_x11.h",
                 "//modules/desktop_capture/linux/screen_capturer_x11.cc",
                 "//modules/desktop_capture/linux/screen_capturer_x11.h",
                 "//modules/desktop_capture/linux/shared_x_display.cc",
                 "//modules/desktop_capture/linux/shared_x_display.h",
                 "//modules/desktop_capture/linux/window_capturer_x11.cc",
                 "//modules/desktop_capture/linux/window_capturer_x11.h",
--- a/media/webrtc/gn-configs/arm64_True_arm64_openbsd.json
+++ b/media/webrtc/gn-configs/arm64_True_arm64_openbsd.json
@@ -9533,17 +9533,16 @@
                 "//modules/desktop_capture/screen_capturer_helper.h",
                 "//modules/desktop_capture/window_finder.cc",
                 "//modules/desktop_capture/window_finder.h",
                 "//modules/desktop_capture/desktop_device_info.cc",
                 "//modules/desktop_capture/desktop_device_info.h",
                 "//modules/desktop_capture/mouse_cursor_monitor_linux.cc",
                 "//modules/desktop_capture/screen_capturer_linux.cc",
                 "//modules/desktop_capture/window_capturer_linux.cc",
-                "//modules/desktop_capture/app_capturer_linux.cc",
                 "//modules/desktop_capture/linux/mouse_cursor_monitor_x11.cc",
                 "//modules/desktop_capture/linux/mouse_cursor_monitor_x11.h",
                 "//modules/desktop_capture/linux/screen_capturer_x11.cc",
                 "//modules/desktop_capture/linux/screen_capturer_x11.h",
                 "//modules/desktop_capture/linux/shared_x_display.cc",
                 "//modules/desktop_capture/linux/shared_x_display.h",
                 "//modules/desktop_capture/linux/window_capturer_x11.cc",
                 "//modules/desktop_capture/linux/window_capturer_x11.h",
--- a/media/webrtc/gn-configs/arm_False_arm_freebsd.json
+++ b/media/webrtc/gn-configs/arm_False_arm_freebsd.json
@@ -10814,17 +10814,16 @@
                 "//modules/desktop_capture/screen_capturer_helper.h",
                 "//modules/desktop_capture/window_finder.cc",
                 "//modules/desktop_capture/window_finder.h",
                 "//modules/desktop_capture/desktop_device_info.cc",
                 "//modules/desktop_capture/desktop_device_info.h",
                 "//modules/desktop_capture/mouse_cursor_monitor_linux.cc",
                 "//modules/desktop_capture/screen_capturer_linux.cc",
                 "//modules/desktop_capture/window_capturer_linux.cc",
-                "//modules/desktop_capture/app_capturer_linux.cc",
                 "//modules/desktop_capture/linux/mouse_cursor_monitor_x11.cc",
                 "//modules/desktop_capture/linux/mouse_cursor_monitor_x11.h",
                 "//modules/desktop_capture/linux/screen_capturer_x11.cc",
                 "//modules/desktop_capture/linux/screen_capturer_x11.h",
                 "//modules/desktop_capture/linux/shared_x_display.cc",
                 "//modules/desktop_capture/linux/shared_x_display.h",
                 "//modules/desktop_capture/linux/window_capturer_x11.cc",
                 "//modules/desktop_capture/linux/window_capturer_x11.h",
--- a/media/webrtc/gn-configs/arm_False_arm_linux.json
+++ b/media/webrtc/gn-configs/arm_False_arm_linux.json
@@ -11918,17 +11918,16 @@
                 "//modules/desktop_capture/screen_capturer_helper.h",
                 "//modules/desktop_capture/window_finder.cc",
                 "//modules/desktop_capture/window_finder.h",
                 "//modules/desktop_capture/desktop_device_info.cc",
                 "//modules/desktop_capture/desktop_device_info.h",
                 "//modules/desktop_capture/mouse_cursor_monitor_linux.cc",
                 "//modules/desktop_capture/screen_capturer_linux.cc",
                 "//modules/desktop_capture/window_capturer_linux.cc",
-                "//modules/desktop_capture/app_capturer_linux.cc",
                 "//modules/desktop_capture/linux/mouse_cursor_monitor_x11.cc",
                 "//modules/desktop_capture/linux/mouse_cursor_monitor_x11.h",
                 "//modules/desktop_capture/linux/screen_capturer_x11.cc",
                 "//modules/desktop_capture/linux/screen_capturer_x11.h",
                 "//modules/desktop_capture/linux/shared_x_display.cc",
                 "//modules/desktop_capture/linux/shared_x_display.h",
                 "//modules/desktop_capture/linux/window_capturer_x11.cc",
                 "//modules/desktop_capture/linux/window_capturer_x11.h",
--- a/media/webrtc/gn-configs/arm_False_arm_netbsd.json
+++ b/media/webrtc/gn-configs/arm_False_arm_netbsd.json
@@ -8324,17 +8324,16 @@
                 "//modules/desktop_capture/screen_capturer_helper.h",
                 "//modules/desktop_capture/window_finder.cc",
                 "//modules/desktop_capture/window_finder.h",
                 "//modules/desktop_capture/desktop_device_info.cc",
                 "//modules/desktop_capture/desktop_device_info.h",
                 "//modules/desktop_capture/mouse_cursor_monitor_linux.cc",
                 "//modules/desktop_capture/screen_capturer_linux.cc",
                 "//modules/desktop_capture/window_capturer_linux.cc",
-                "//modules/desktop_capture/app_capturer_linux.cc",
                 "//modules/desktop_capture/linux/mouse_cursor_monitor_x11.cc",
                 "//modules/desktop_capture/linux/mouse_cursor_monitor_x11.h",
                 "//modules/desktop_capture/linux/screen_capturer_x11.cc",
                 "//modules/desktop_capture/linux/screen_capturer_x11.h",
                 "//modules/desktop_capture/linux/shared_x_display.cc",
                 "//modules/desktop_capture/linux/shared_x_display.h",
                 "//modules/desktop_capture/linux/window_capturer_x11.cc",
                 "//modules/desktop_capture/linux/window_capturer_x11.h",
--- a/media/webrtc/gn-configs/arm_False_arm_openbsd.json
+++ b/media/webrtc/gn-configs/arm_False_arm_openbsd.json
@@ -10814,17 +10814,16 @@
                 "//modules/desktop_capture/screen_capturer_helper.h",
                 "//modules/desktop_capture/window_finder.cc",
                 "//modules/desktop_capture/window_finder.h",
                 "//modules/desktop_capture/desktop_device_info.cc",
                 "//modules/desktop_capture/desktop_device_info.h",
                 "//modules/desktop_capture/mouse_cursor_monitor_linux.cc",
                 "//modules/desktop_capture/screen_capturer_linux.cc",
                 "//modules/desktop_capture/window_capturer_linux.cc",
-                "//modules/desktop_capture/app_capturer_linux.cc",
                 "//modules/desktop_capture/linux/mouse_cursor_monitor_x11.cc",
                 "//modules/desktop_capture/linux/mouse_cursor_monitor_x11.h",
                 "//modules/desktop_capture/linux/screen_capturer_x11.cc",
                 "//modules/desktop_capture/linux/screen_capturer_x11.h",
                 "//modules/desktop_capture/linux/shared_x_display.cc",
                 "//modules/desktop_capture/linux/shared_x_display.h",
                 "//modules/desktop_capture/linux/window_capturer_x11.cc",
                 "//modules/desktop_capture/linux/window_capturer_x11.h",
--- a/media/webrtc/gn-configs/arm_True_arm_freebsd.json
+++ b/media/webrtc/gn-configs/arm_True_arm_freebsd.json
@@ -10309,17 +10309,16 @@
                 "//modules/desktop_capture/screen_capturer_helper.h",
                 "//modules/desktop_capture/window_finder.cc",
                 "//modules/desktop_capture/window_finder.h",
                 "//modules/desktop_capture/desktop_device_info.cc",
                 "//modules/desktop_capture/desktop_device_info.h",
                 "//modules/desktop_capture/mouse_cursor_monitor_linux.cc",
                 "//modules/desktop_capture/screen_capturer_linux.cc",
                 "//modules/desktop_capture/window_capturer_linux.cc",
-                "//modules/desktop_capture/app_capturer_linux.cc",
                 "//modules/desktop_capture/linux/mouse_cursor_monitor_x11.cc",
                 "//modules/desktop_capture/linux/mouse_cursor_monitor_x11.h",
                 "//modules/desktop_capture/linux/screen_capturer_x11.cc",
                 "//modules/desktop_capture/linux/screen_capturer_x11.h",
                 "//modules/desktop_capture/linux/shared_x_display.cc",
                 "//modules/desktop_capture/linux/shared_x_display.h",
                 "//modules/desktop_capture/linux/window_capturer_x11.cc",
                 "//modules/desktop_capture/linux/window_capturer_x11.h",
--- a/media/webrtc/gn-configs/arm_True_arm_linux.json
+++ b/media/webrtc/gn-configs/arm_True_arm_linux.json
@@ -11615,17 +11615,16 @@
                 "//modules/desktop_capture/screen_capturer_helper.h",
                 "//modules/desktop_capture/window_finder.cc",
                 "//modules/desktop_capture/window_finder.h",
                 "//modules/desktop_capture/desktop_device_info.cc",
                 "//modules/desktop_capture/desktop_device_info.h",
                 "//modules/desktop_capture/mouse_cursor_monitor_linux.cc",
                 "//modules/desktop_capture/screen_capturer_linux.cc",
                 "//modules/desktop_capture/window_capturer_linux.cc",
-                "//modules/desktop_capture/app_capturer_linux.cc",
                 "//modules/desktop_capture/linux/mouse_cursor_monitor_x11.cc",
                 "//modules/desktop_capture/linux/mouse_cursor_monitor_x11.h",
                 "//modules/desktop_capture/linux/screen_capturer_x11.cc",
                 "//modules/desktop_capture/linux/screen_capturer_x11.h",
                 "//modules/desktop_capture/linux/shared_x_display.cc",
                 "//modules/desktop_capture/linux/shared_x_display.h",
                 "//modules/desktop_capture/linux/window_capturer_x11.cc",
                 "//modules/desktop_capture/linux/window_capturer_x11.h",
--- a/media/webrtc/gn-configs/arm_True_arm_netbsd.json
+++ b/media/webrtc/gn-configs/arm_True_arm_netbsd.json
@@ -7819,17 +7819,16 @@
                 "//modules/desktop_capture/screen_capturer_helper.h",
                 "//modules/desktop_capture/window_finder.cc",
                 "//modules/desktop_capture/window_finder.h",
                 "//modules/desktop_capture/desktop_device_info.cc",
                 "//modules/desktop_capture/desktop_device_info.h",
                 "//modules/desktop_capture/mouse_cursor_monitor_linux.cc",
                 "//modules/desktop_capture/screen_capturer_linux.cc",
                 "//modules/desktop_capture/window_capturer_linux.cc",
-                "//modules/desktop_capture/app_capturer_linux.cc",
                 "//modules/desktop_capture/linux/mouse_cursor_monitor_x11.cc",
                 "//modules/desktop_capture/linux/mouse_cursor_monitor_x11.h",
                 "//modules/desktop_capture/linux/screen_capturer_x11.cc",
                 "//modules/desktop_capture/linux/screen_capturer_x11.h",
                 "//modules/desktop_capture/linux/shared_x_display.cc",
                 "//modules/desktop_capture/linux/shared_x_display.h",
                 "//modules/desktop_capture/linux/window_capturer_x11.cc",
                 "//modules/desktop_capture/linux/window_capturer_x11.h",
--- a/media/webrtc/gn-configs/arm_True_arm_openbsd.json
+++ b/media/webrtc/gn-configs/arm_True_arm_openbsd.json
@@ -10309,17 +10309,16 @@
                 "//modules/desktop_capture/screen_capturer_helper.h",
                 "//modules/desktop_capture/window_finder.cc",
                 "//modules/desktop_capture/window_finder.h",
                 "//modules/desktop_capture/desktop_device_info.cc",
                 "//modules/desktop_capture/desktop_device_info.h",
                 "//modules/desktop_capture/mouse_cursor_monitor_linux.cc",
                 "//modules/desktop_capture/screen_capturer_linux.cc",
                 "//modules/desktop_capture/window_capturer_linux.cc",
-                "//modules/desktop_capture/app_capturer_linux.cc",
                 "//modules/desktop_capture/linux/mouse_cursor_monitor_x11.cc",
                 "//modules/desktop_capture/linux/mouse_cursor_monitor_x11.h",
                 "//modules/desktop_capture/linux/screen_capturer_x11.cc",
                 "//modules/desktop_capture/linux/screen_capturer_x11.h",
                 "//modules/desktop_capture/linux/shared_x_display.cc",
                 "//modules/desktop_capture/linux/shared_x_display.h",
                 "//modules/desktop_capture/linux/window_capturer_x11.cc",
                 "//modules/desktop_capture/linux/window_capturer_x11.h",
--- a/media/webrtc/gn-configs/ppc64_False_ppc64_linux.json
+++ b/media/webrtc/gn-configs/ppc64_False_ppc64_linux.json
@@ -7380,17 +7380,16 @@
                 "//modules/desktop_capture/screen_capturer_helper.h",
                 "//modules/desktop_capture/window_finder.cc",
                 "//modules/desktop_capture/window_finder.h",
                 "//modules/desktop_capture/desktop_device_info.cc",
                 "//modules/desktop_capture/desktop_device_info.h",
                 "//modules/desktop_capture/mouse_cursor_monitor_linux.cc",
                 "//modules/desktop_capture/screen_capturer_linux.cc",
                 "//modules/desktop_capture/window_capturer_linux.cc",
-                "//modules/desktop_capture/app_capturer_linux.cc",
                 "//modules/desktop_capture/linux/mouse_cursor_monitor_x11.cc",
                 "//modules/desktop_capture/linux/mouse_cursor_monitor_x11.h",
                 "//modules/desktop_capture/linux/screen_capturer_x11.cc",
                 "//modules/desktop_capture/linux/screen_capturer_x11.h",
                 "//modules/desktop_capture/linux/shared_x_display.cc",
                 "//modules/desktop_capture/linux/shared_x_display.h",
                 "//modules/desktop_capture/linux/window_capturer_x11.cc",
                 "//modules/desktop_capture/linux/window_capturer_x11.h",
--- a/media/webrtc/gn-configs/ppc64_True_ppc64_linux.json
+++ b/media/webrtc/gn-configs/ppc64_True_ppc64_linux.json
@@ -7004,17 +7004,16 @@
                 "//modules/desktop_capture/screen_capturer_helper.h",
                 "//modules/desktop_capture/window_finder.cc",
                 "//modules/desktop_capture/window_finder.h",
                 "//modules/desktop_capture/desktop_device_info.cc",
                 "//modules/desktop_capture/desktop_device_info.h",
                 "//modules/desktop_capture/mouse_cursor_monitor_linux.cc",
                 "//modules/desktop_capture/screen_capturer_linux.cc",
                 "//modules/desktop_capture/window_capturer_linux.cc",
-                "//modules/desktop_capture/app_capturer_linux.cc",
                 "//modules/desktop_capture/linux/mouse_cursor_monitor_x11.cc",
                 "//modules/desktop_capture/linux/mouse_cursor_monitor_x11.h",
                 "//modules/desktop_capture/linux/screen_capturer_x11.cc",
                 "//modules/desktop_capture/linux/screen_capturer_x11.h",
                 "//modules/desktop_capture/linux/shared_x_display.cc",
                 "//modules/desktop_capture/linux/shared_x_display.h",
                 "//modules/desktop_capture/linux/window_capturer_x11.cc",
                 "//modules/desktop_capture/linux/window_capturer_x11.h",
--- a/media/webrtc/gn-configs/x64_False_arm64_win.json
+++ b/media/webrtc/gn-configs/x64_False_arm64_win.json
@@ -11197,17 +11197,16 @@
                 "//modules/desktop_capture/win/screen_capturer_win_magnifier.h",
                 "//modules/desktop_capture/win/window_capture_utils.cc",
                 "//modules/desktop_capture/win/window_capture_utils.h",
                 "//modules/desktop_capture/window_capturer_win.cc",
                 "//modules/desktop_capture/window_finder.cc",
                 "//modules/desktop_capture/window_finder.h",
                 "//modules/desktop_capture/window_finder_win.cc",
                 "//modules/desktop_capture/window_finder_win.h",
-                "//modules/desktop_capture/app_capturer_win.cc",
                 "//modules/desktop_capture/desktop_device_info.cc",
                 "//modules/desktop_capture/desktop_device_info.h",
                 "//modules/desktop_capture/win/desktop_device_info_win.cc",
                 "//modules/desktop_capture/win/win_shared.cc"
             ],
             "type": "static_library"
         },
         "//modules/desktop_capture:primitives": {
--- a/media/webrtc/gn-configs/x64_False_x64_dragonfly.json
+++ b/media/webrtc/gn-configs/x64_False_x64_dragonfly.json
@@ -7350,17 +7350,16 @@
                 "//modules/desktop_capture/screen_capturer_helper.h",
                 "//modules/desktop_capture/window_finder.cc",
                 "//modules/desktop_capture/window_finder.h",
                 "//modules/desktop_capture/desktop_device_info.cc",
                 "//modules/desktop_capture/desktop_device_info.h",
                 "//modules/desktop_capture/mouse_cursor_monitor_linux.cc",
                 "//modules/desktop_capture/screen_capturer_linux.cc",
                 "//modules/desktop_capture/window_capturer_linux.cc",
-                "//modules/desktop_capture/app_capturer_linux.cc",
                 "//modules/desktop_capture/linux/mouse_cursor_monitor_x11.cc",
                 "//modules/desktop_capture/linux/mouse_cursor_monitor_x11.h",
                 "//modules/desktop_capture/linux/screen_capturer_x11.cc",
                 "//modules/desktop_capture/linux/screen_capturer_x11.h",
                 "//modules/desktop_capture/linux/shared_x_display.cc",
                 "//modules/desktop_capture/linux/shared_x_display.h",
                 "//modules/desktop_capture/linux/window_capturer_x11.cc",
                 "//modules/desktop_capture/linux/window_capturer_x11.h",
--- a/media/webrtc/gn-configs/x64_False_x64_freebsd.json
+++ b/media/webrtc/gn-configs/x64_False_x64_freebsd.json
@@ -9744,17 +9744,16 @@
                 "//modules/desktop_capture/screen_capturer_helper.h",
                 "//modules/desktop_capture/window_finder.cc",
                 "//modules/desktop_capture/window_finder.h",
                 "//modules/desktop_capture/desktop_device_info.cc",
                 "//modules/desktop_capture/desktop_device_info.h",
                 "//modules/desktop_capture/mouse_cursor_monitor_linux.cc",
                 "//modules/desktop_capture/screen_capturer_linux.cc",
                 "//modules/desktop_capture/window_capturer_linux.cc",
-                "//modules/desktop_capture/app_capturer_linux.cc",
                 "//modules/desktop_capture/linux/mouse_cursor_monitor_x11.cc",
                 "//modules/desktop_capture/linux/mouse_cursor_monitor_x11.h",
                 "//modules/desktop_capture/linux/screen_capturer_x11.cc",
                 "//modules/desktop_capture/linux/screen_capturer_x11.h",
                 "//modules/desktop_capture/linux/shared_x_display.cc",
                 "//modules/desktop_capture/linux/shared_x_display.h",
                 "//modules/desktop_capture/linux/window_capturer_x11.cc",
                 "//modules/desktop_capture/linux/window_capturer_x11.h",
--- a/media/webrtc/gn-configs/x64_False_x64_linux.json
+++ b/media/webrtc/gn-configs/x64_False_x64_linux.json
@@ -10909,17 +10909,16 @@
                 "//modules/desktop_capture/screen_capturer_helper.h",
                 "//modules/desktop_capture/window_finder.cc",
                 "//modules/desktop_capture/window_finder.h",
                 "//modules/desktop_capture/desktop_device_info.cc",
                 "//modules/desktop_capture/desktop_device_info.h",
                 "//modules/desktop_capture/mouse_cursor_monitor_linux.cc",
                 "//modules/desktop_capture/screen_capturer_linux.cc",
                 "//modules/desktop_capture/window_capturer_linux.cc",
-                "//modules/desktop_capture/app_capturer_linux.cc",
                 "//modules/desktop_capture/linux/mouse_cursor_monitor_x11.cc",
                 "//modules/desktop_capture/linux/mouse_cursor_monitor_x11.h",
                 "//modules/desktop_capture/linux/screen_capturer_x11.cc",
                 "//modules/desktop_capture/linux/screen_capturer_x11.h",
                 "//modules/desktop_capture/linux/shared_x_display.cc",
                 "//modules/desktop_capture/linux/shared_x_display.h",
                 "//modules/desktop_capture/linux/window_capturer_x11.cc",
                 "//modules/desktop_capture/linux/window_capturer_x11.h",
--- a/media/webrtc/gn-configs/x64_False_x64_mac.json
+++ b/media/webrtc/gn-configs/x64_False_x64_mac.json
@@ -10226,17 +10226,16 @@
                 "//modules/desktop_capture/rgba_color.h",
                 "//modules/desktop_capture/screen_capture_frame_queue.h",
                 "//modules/desktop_capture/screen_capturer_helper.cc",
                 "//modules/desktop_capture/screen_capturer_helper.h",
                 "//modules/desktop_capture/window_finder.cc",
                 "//modules/desktop_capture/window_finder.h",
                 "//modules/desktop_capture/desktop_device_info.cc",
                 "//modules/desktop_capture/desktop_device_info.h",
-                "//modules/desktop_capture/app_capturer_mac.mm",
                 "//modules/desktop_capture/mac/desktop_device_info_mac.mm"
             ],
             "type": "static_library"
         },
         "//modules/desktop_capture:desktop_capture_objc": {
             "cflags": [
                 "-fno-strict-aliasing",
                 "-fstack-protector",
--- a/media/webrtc/gn-configs/x64_False_x64_netbsd.json
+++ b/media/webrtc/gn-configs/x64_False_x64_netbsd.json
@@ -7350,17 +7350,16 @@
                 "//modules/desktop_capture/screen_capturer_helper.h",
                 "//modules/desktop_capture/window_finder.cc",
                 "//modules/desktop_capture/window_finder.h",
                 "//modules/desktop_capture/desktop_device_info.cc",
                 "//modules/desktop_capture/desktop_device_info.h",
                 "//modules/desktop_capture/mouse_cursor_monitor_linux.cc",
                 "//modules/desktop_capture/screen_capturer_linux.cc",
                 "//modules/desktop_capture/window_capturer_linux.cc",
-                "//modules/desktop_capture/app_capturer_linux.cc",
                 "//modules/desktop_capture/linux/mouse_cursor_monitor_x11.cc",
                 "//modules/desktop_capture/linux/mouse_cursor_monitor_x11.h",
                 "//modules/desktop_capture/linux/screen_capturer_x11.cc",
                 "//modules/desktop_capture/linux/screen_capturer_x11.h",
                 "//modules/desktop_capture/linux/shared_x_display.cc",
                 "//modules/desktop_capture/linux/shared_x_display.h",
                 "//modules/desktop_capture/linux/window_capturer_x11.cc",
                 "//modules/desktop_capture/linux/window_capturer_x11.h",
--- a/media/webrtc/gn-configs/x64_False_x64_openbsd.json
+++ b/media/webrtc/gn-configs/x64_False_x64_openbsd.json
@@ -9744,17 +9744,16 @@
                 "//modules/desktop_capture/screen_capturer_helper.h",
                 "//modules/desktop_capture/window_finder.cc",
                 "//modules/desktop_capture/window_finder.h",
                 "//modules/desktop_capture/desktop_device_info.cc",
                 "//modules/desktop_capture/desktop_device_info.h",
                 "//modules/desktop_capture/mouse_cursor_monitor_linux.cc",
                 "//modules/desktop_capture/screen_capturer_linux.cc",
                 "//modules/desktop_capture/window_capturer_linux.cc",
-                "//modules/desktop_capture/app_capturer_linux.cc",
                 "//modules/desktop_capture/linux/mouse_cursor_monitor_x11.cc",
                 "//modules/desktop_capture/linux/mouse_cursor_monitor_x11.h",
                 "//modules/desktop_capture/linux/screen_capturer_x11.cc",
                 "//modules/desktop_capture/linux/screen_capturer_x11.h",
                 "//modules/desktop_capture/linux/shared_x_display.cc",
                 "//modules/desktop_capture/linux/shared_x_display.h",
                 "//modules/desktop_capture/linux/window_capturer_x11.cc",
                 "//modules/desktop_capture/linux/window_capturer_x11.h",
--- a/media/webrtc/gn-configs/x64_False_x64_win.json
+++ b/media/webrtc/gn-configs/x64_False_x64_win.json
@@ -10784,17 +10784,16 @@
                 "//modules/desktop_capture/win/screen_capturer_win_magnifier.h",
                 "//modules/desktop_capture/win/window_capture_utils.cc",
                 "//modules/desktop_capture/win/window_capture_utils.h",
                 "//modules/desktop_capture/window_capturer_win.cc",
                 "//modules/desktop_capture/window_finder.cc",
                 "//modules/desktop_capture/window_finder.h",
                 "//modules/desktop_capture/window_finder_win.cc",
                 "//modules/desktop_capture/window_finder_win.h",
-                "//modules/desktop_capture/app_capturer_win.cc",
                 "//modules/desktop_capture/desktop_device_info.cc",
                 "//modules/desktop_capture/desktop_device_info.h",
                 "//modules/desktop_capture/win/desktop_device_info_win.cc",
                 "//modules/desktop_capture/win/win_shared.cc"
             ],
             "type": "static_library"
         },
         "//modules/desktop_capture:primitives": {
--- a/media/webrtc/gn-configs/x64_False_x86_linux.json
+++ b/media/webrtc/gn-configs/x64_False_x86_linux.json
@@ -11488,17 +11488,16 @@
                 "//modules/desktop_capture/screen_capturer_helper.h",
                 "//modules/desktop_capture/window_finder.cc",
                 "//modules/desktop_capture/window_finder.h",
                 "//modules/desktop_capture/desktop_device_info.cc",
                 "//modules/desktop_capture/desktop_device_info.h",
                 "//modules/desktop_capture/mouse_cursor_monitor_linux.cc",
                 "//modules/desktop_capture/screen_capturer_linux.cc",
                 "//modules/desktop_capture/window_capturer_linux.cc",
-                "//modules/desktop_capture/app_capturer_linux.cc",
                 "//modules/desktop_capture/linux/mouse_cursor_monitor_x11.cc",
                 "//modules/desktop_capture/linux/mouse_cursor_monitor_x11.h",
                 "//modules/desktop_capture/linux/screen_capturer_x11.cc",
                 "//modules/desktop_capture/linux/screen_capturer_x11.h",
                 "//modules/desktop_capture/linux/shared_x_display.cc",
                 "//modules/desktop_capture/linux/shared_x_display.h",
                 "//modules/desktop_capture/linux/window_capturer_x11.cc",
                 "//modules/desktop_capture/linux/window_capturer_x11.h",
--- a/media/webrtc/gn-configs/x64_True_arm64_win.json
+++ b/media/webrtc/gn-configs/x64_True_arm64_win.json
@@ -10702,17 +10702,16 @@
                 "//modules/desktop_capture/win/screen_capturer_win_magnifier.h",
                 "//modules/desktop_capture/win/window_capture_utils.cc",
                 "//modules/desktop_capture/win/window_capture_utils.h",
                 "//modules/desktop_capture/window_capturer_win.cc",
                 "//modules/desktop_capture/window_finder.cc",
                 "//modules/desktop_capture/window_finder.h",
                 "//modules/desktop_capture/window_finder_win.cc",
                 "//modules/desktop_capture/window_finder_win.h",
-                "//modules/desktop_capture/app_capturer_win.cc",
                 "//modules/desktop_capture/desktop_device_info.cc",
                 "//modules/desktop_capture/desktop_device_info.h",
                 "//modules/desktop_capture/win/desktop_device_info_win.cc",
                 "//modules/desktop_capture/win/win_shared.cc"
             ],
             "type": "static_library"
         },
         "//modules/desktop_capture:primitives": {
--- a/media/webrtc/gn-configs/x64_True_x64_dragonfly.json
+++ b/media/webrtc/gn-configs/x64_True_x64_dragonfly.json
@@ -6865,17 +6865,16 @@
                 "//modules/desktop_capture/screen_capturer_helper.h",
                 "//modules/desktop_capture/window_finder.cc",
                 "//modules/desktop_capture/window_finder.h",
                 "//modules/desktop_capture/desktop_device_info.cc",
                 "//modules/desktop_capture/desktop_device_info.h",
                 "//modules/desktop_capture/mouse_cursor_monitor_linux.cc",
                 "//modules/desktop_capture/screen_capturer_linux.cc",
                 "//modules/desktop_capture/window_capturer_linux.cc",
-                "//modules/desktop_capture/app_capturer_linux.cc",
                 "//modules/desktop_capture/linux/mouse_cursor_monitor_x11.cc",
                 "//modules/desktop_capture/linux/mouse_cursor_monitor_x11.h",
                 "//modules/desktop_capture/linux/screen_capturer_x11.cc",
                 "//modules/desktop_capture/linux/screen_capturer_x11.h",
                 "//modules/desktop_capture/linux/shared_x_display.cc",
                 "//modules/desktop_capture/linux/shared_x_display.h",
                 "//modules/desktop_capture/linux/window_capturer_x11.cc",
                 "//modules/desktop_capture/linux/window_capturer_x11.h",
--- a/media/webrtc/gn-configs/x64_True_x64_freebsd.json
+++ b/media/webrtc/gn-configs/x64_True_x64_freebsd.json
@@ -9259,17 +9259,16 @@
                 "//modules/desktop_capture/screen_capturer_helper.h",
                 "//modules/desktop_capture/window_finder.cc",
                 "//modules/desktop_capture/window_finder.h",
                 "//modules/desktop_capture/desktop_device_info.cc",
                 "//modules/desktop_capture/desktop_device_info.h",
                 "//modules/desktop_capture/mouse_cursor_monitor_linux.cc",
                 "//modules/desktop_capture/screen_capturer_linux.cc",
                 "//modules/desktop_capture/window_capturer_linux.cc",
-                "//modules/desktop_capture/app_capturer_linux.cc",
                 "//modules/desktop_capture/linux/mouse_cursor_monitor_x11.cc",
                 "//modules/desktop_capture/linux/mouse_cursor_monitor_x11.h",
                 "//modules/desktop_capture/linux/screen_capturer_x11.cc",
                 "//modules/desktop_capture/linux/screen_capturer_x11.h",
                 "//modules/desktop_capture/linux/shared_x_display.cc",
                 "//modules/desktop_capture/linux/shared_x_display.h",
                 "//modules/desktop_capture/linux/window_capturer_x11.cc",
                 "//modules/desktop_capture/linux/window_capturer_x11.h",
--- a/media/webrtc/gn-configs/x64_True_x64_linux.json
+++ b/media/webrtc/gn-configs/x64_True_x64_linux.json
@@ -10618,17 +10618,16 @@
                 "//modules/desktop_capture/screen_capturer_helper.h",
                 "//modules/desktop_capture/window_finder.cc",
                 "//modules/desktop_capture/window_finder.h",
                 "//modules/desktop_capture/desktop_device_info.cc",
                 "//modules/desktop_capture/desktop_device_info.h",
                 "//modules/desktop_capture/mouse_cursor_monitor_linux.cc",
                 "//modules/desktop_capture/screen_capturer_linux.cc",
                 "//modules/desktop_capture/window_capturer_linux.cc",
-                "//modules/desktop_capture/app_capturer_linux.cc",
                 "//modules/desktop_capture/linux/mouse_cursor_monitor_x11.cc",
                 "//modules/desktop_capture/linux/mouse_cursor_monitor_x11.h",
                 "//modules/desktop_capture/linux/screen_capturer_x11.cc",
                 "//modules/desktop_capture/linux/screen_capturer_x11.h",
                 "//modules/desktop_capture/linux/shared_x_display.cc",
                 "//modules/desktop_capture/linux/shared_x_display.h",
                 "//modules/desktop_capture/linux/window_capturer_x11.cc",
                 "//modules/desktop_capture/linux/window_capturer_x11.h",
--- a/media/webrtc/gn-configs/x64_True_x64_mac.json
+++ b/media/webrtc/gn-configs/x64_True_x64_mac.json
@@ -10032,17 +10032,16 @@
                 "//modules/desktop_capture/rgba_color.h",
                 "//modules/desktop_capture/screen_capture_frame_queue.h",
                 "//modules/desktop_capture/screen_capturer_helper.cc",
                 "//modules/desktop_capture/screen_capturer_helper.h",
                 "//modules/desktop_capture/window_finder.cc",
                 "//modules/desktop_capture/window_finder.h",
                 "//modules/desktop_capture/desktop_device_info.cc",
                 "//modules/desktop_capture/desktop_device_info.h",
-                "//modules/desktop_capture/app_capturer_mac.mm",
                 "//modules/desktop_capture/mac/desktop_device_info_mac.mm"
             ],
             "type": "static_library"
         },
         "//modules/desktop_capture:desktop_capture_objc": {
             "cflags": [
                 "-fno-strict-aliasing",
                 "-fstack-protector-strong",
--- a/media/webrtc/gn-configs/x64_True_x64_netbsd.json
+++ b/media/webrtc/gn-configs/x64_True_x64_netbsd.json
@@ -6865,17 +6865,16 @@
                 "//modules/desktop_capture/screen_capturer_helper.h",
                 "//modules/desktop_capture/window_finder.cc",
                 "//modules/desktop_capture/window_finder.h",
                 "//modules/desktop_capture/desktop_device_info.cc",
                 "//modules/desktop_capture/desktop_device_info.h",
                 "//modules/desktop_capture/mouse_cursor_monitor_linux.cc",
                 "//modules/desktop_capture/screen_capturer_linux.cc",
                 "//modules/desktop_capture/window_capturer_linux.cc",
-                "//modules/desktop_capture/app_capturer_linux.cc",
                 "//modules/desktop_capture/linux/mouse_cursor_monitor_x11.cc",
                 "//modules/desktop_capture/linux/mouse_cursor_monitor_x11.h",
                 "//modules/desktop_capture/linux/screen_capturer_x11.cc",
                 "//modules/desktop_capture/linux/screen_capturer_x11.h",
                 "//modules/desktop_capture/linux/shared_x_display.cc",
                 "//modules/desktop_capture/linux/shared_x_display.h",
                 "//modules/desktop_capture/linux/window_capturer_x11.cc",
                 "//modules/desktop_capture/linux/window_capturer_x11.h",
--- a/media/webrtc/gn-configs/x64_True_x64_openbsd.json
+++ b/media/webrtc/gn-configs/x64_True_x64_openbsd.json
@@ -9259,17 +9259,16 @@
                 "//modules/desktop_capture/screen_capturer_helper.h",
                 "//modules/desktop_capture/window_finder.cc",
                 "//modules/desktop_capture/window_finder.h",
                 "//modules/desktop_capture/desktop_device_info.cc",
                 "//modules/desktop_capture/desktop_device_info.h",
                 "//modules/desktop_capture/mouse_cursor_monitor_linux.cc",
                 "//modules/desktop_capture/screen_capturer_linux.cc",
                 "//modules/desktop_capture/window_capturer_linux.cc",
-                "//modules/desktop_capture/app_capturer_linux.cc",
                 "//modules/desktop_capture/linux/mouse_cursor_monitor_x11.cc",
                 "//modules/desktop_capture/linux/mouse_cursor_monitor_x11.h",
                 "//modules/desktop_capture/linux/screen_capturer_x11.cc",
                 "//modules/desktop_capture/linux/screen_capturer_x11.h",
                 "//modules/desktop_capture/linux/shared_x_display.cc",
                 "//modules/desktop_capture/linux/shared_x_display.h",
                 "//modules/desktop_capture/linux/window_capturer_x11.cc",
                 "//modules/desktop_capture/linux/window_capturer_x11.h",
--- a/media/webrtc/gn-configs/x64_True_x64_win.json
+++ b/media/webrtc/gn-configs/x64_True_x64_win.json
@@ -10299,17 +10299,16 @@
                 "//modules/desktop_capture/win/screen_capturer_win_magnifier.h",
                 "//modules/desktop_capture/win/window_capture_utils.cc",
                 "//modules/desktop_capture/win/window_capture_utils.h",
                 "//modules/desktop_capture/window_capturer_win.cc",
                 "//modules/desktop_capture/window_finder.cc",
                 "//modules/desktop_capture/window_finder.h",
                 "//modules/desktop_capture/window_finder_win.cc",
                 "//modules/desktop_capture/window_finder_win.h",
-                "//modules/desktop_capture/app_capturer_win.cc",
                 "//modules/desktop_capture/desktop_device_info.cc",
                 "//modules/desktop_capture/desktop_device_info.h",
                 "//modules/desktop_capture/win/desktop_device_info_win.cc",
                 "//modules/desktop_capture/win/win_shared.cc"
             ],
             "type": "static_library"
         },
         "//modules/desktop_capture:primitives": {
--- a/media/webrtc/gn-configs/x64_True_x86_linux.json
+++ b/media/webrtc/gn-configs/x64_True_x86_linux.json
@@ -11197,17 +11197,16 @@
                 "//modules/desktop_capture/screen_capturer_helper.h",
                 "//modules/desktop_capture/window_finder.cc",
                 "//modules/desktop_capture/window_finder.h",
                 "//modules/desktop_capture/desktop_device_info.cc",
                 "//modules/desktop_capture/desktop_device_info.h",
                 "//modules/desktop_capture/mouse_cursor_monitor_linux.cc",
                 "//modules/desktop_capture/screen_capturer_linux.cc",
                 "//modules/desktop_capture/window_capturer_linux.cc",
-                "//modules/desktop_capture/app_capturer_linux.cc",
                 "//modules/desktop_capture/linux/mouse_cursor_monitor_x11.cc",
                 "//modules/desktop_capture/linux/mouse_cursor_monitor_x11.h",
                 "//modules/desktop_capture/linux/screen_capturer_x11.cc",
                 "//modules/desktop_capture/linux/screen_capturer_x11.h",
                 "//modules/desktop_capture/linux/shared_x_display.cc",
                 "//modules/desktop_capture/linux/shared_x_display.h",
                 "//modules/desktop_capture/linux/window_capturer_x11.cc",
                 "//modules/desktop_capture/linux/window_capturer_x11.h",
--- a/media/webrtc/gn-configs/x86_False_x86_freebsd.json
+++ b/media/webrtc/gn-configs/x86_False_x86_freebsd.json
@@ -10035,17 +10035,16 @@
                 "//modules/desktop_capture/screen_capturer_helper.h",
                 "//modules/desktop_capture/window_finder.cc",
                 "//modules/desktop_capture/window_finder.h",
                 "//modules/desktop_capture/desktop_device_info.cc",
                 "//modules/desktop_capture/desktop_device_info.h",
                 "//modules/desktop_capture/mouse_cursor_monitor_linux.cc",
                 "//modules/desktop_capture/screen_capturer_linux.cc",
                 "//modules/desktop_capture/window_capturer_linux.cc",
-                "//modules/desktop_capture/app_capturer_linux.cc",
                 "//modules/desktop_capture/linux/mouse_cursor_monitor_x11.cc",
                 "//modules/desktop_capture/linux/mouse_cursor_monitor_x11.h",
                 "//modules/desktop_capture/linux/screen_capturer_x11.cc",
                 "//modules/desktop_capture/linux/screen_capturer_x11.h",
                 "//modules/desktop_capture/linux/shared_x_display.cc",
                 "//modules/desktop_capture/linux/shared_x_display.h",
                 "//modules/desktop_capture/linux/window_capturer_x11.cc",
                 "//modules/desktop_capture/linux/window_capturer_x11.h",
--- a/media/webrtc/gn-configs/x86_False_x86_netbsd.json
+++ b/media/webrtc/gn-configs/x86_False_x86_netbsd.json
@@ -7544,17 +7544,16 @@
                 "//modules/desktop_capture/screen_capturer_helper.h",
                 "//modules/desktop_capture/window_finder.cc",
                 "//modules/desktop_capture/window_finder.h",
                 "//modules/desktop_capture/desktop_device_info.cc",
                 "//modules/desktop_capture/desktop_device_info.h",
                 "//modules/desktop_capture/mouse_cursor_monitor_linux.cc",
                 "//modules/desktop_capture/screen_capturer_linux.cc",
                 "//modules/desktop_capture/window_capturer_linux.cc",
-                "//modules/desktop_capture/app_capturer_linux.cc",
                 "//modules/desktop_capture/linux/mouse_cursor_monitor_x11.cc",
                 "//modules/desktop_capture/linux/mouse_cursor_monitor_x11.h",
                 "//modules/desktop_capture/linux/screen_capturer_x11.cc",
                 "//modules/desktop_capture/linux/screen_capturer_x11.h",
                 "//modules/desktop_capture/linux/shared_x_display.cc",
                 "//modules/desktop_capture/linux/shared_x_display.h",
                 "//modules/desktop_capture/linux/window_capturer_x11.cc",
                 "//modules/desktop_capture/linux/window_capturer_x11.h",
--- a/media/webrtc/gn-configs/x86_False_x86_openbsd.json
+++ b/media/webrtc/gn-configs/x86_False_x86_openbsd.json
@@ -10035,17 +10035,16 @@
                 "//modules/desktop_capture/screen_capturer_helper.h",
                 "//modules/desktop_capture/window_finder.cc",
                 "//modules/desktop_capture/window_finder.h",
                 "//modules/desktop_capture/desktop_device_info.cc",
                 "//modules/desktop_capture/desktop_device_info.h",
                 "//modules/desktop_capture/mouse_cursor_monitor_linux.cc",
                 "//modules/desktop_capture/screen_capturer_linux.cc",
                 "//modules/desktop_capture/window_capturer_linux.cc",
-                "//modules/desktop_capture/app_capturer_linux.cc",
                 "//modules/desktop_capture/linux/mouse_cursor_monitor_x11.cc",
                 "//modules/desktop_capture/linux/mouse_cursor_monitor_x11.h",
                 "//modules/desktop_capture/linux/screen_capturer_x11.cc",
                 "//modules/desktop_capture/linux/screen_capturer_x11.h",
                 "//modules/desktop_capture/linux/shared_x_display.cc",
                 "//modules/desktop_capture/linux/shared_x_display.h",
                 "//modules/desktop_capture/linux/window_capturer_x11.cc",
                 "//modules/desktop_capture/linux/window_capturer_x11.h",
--- a/media/webrtc/gn-configs/x86_False_x86_win.json
+++ b/media/webrtc/gn-configs/x86_False_x86_win.json
@@ -10878,17 +10878,16 @@
                 "//modules/desktop_capture/win/screen_capturer_win_magnifier.h",
                 "//modules/desktop_capture/win/window_capture_utils.cc",
                 "//modules/desktop_capture/win/window_capture_utils.h",
                 "//modules/desktop_capture/window_capturer_win.cc",
                 "//modules/desktop_capture/window_finder.cc",
                 "//modules/desktop_capture/window_finder.h",
                 "//modules/desktop_capture/window_finder_win.cc",
                 "//modules/desktop_capture/window_finder_win.h",
-                "//modules/desktop_capture/app_capturer_win.cc",
                 "//modules/desktop_capture/desktop_device_info.cc",
                 "//modules/desktop_capture/desktop_device_info.h",
                 "//modules/desktop_capture/win/desktop_device_info_win.cc",
                 "//modules/desktop_capture/win/win_shared.cc"
             ],
             "type": "static_library"
         },
         "//modules/desktop_capture:primitives": {
--- a/media/webrtc/gn-configs/x86_True_x86_freebsd.json
+++ b/media/webrtc/gn-configs/x86_True_x86_freebsd.json
@@ -9550,17 +9550,16 @@
                 "//modules/desktop_capture/screen_capturer_helper.h",
                 "//modules/desktop_capture/window_finder.cc",
                 "//modules/desktop_capture/window_finder.h",
                 "//modules/desktop_capture/desktop_device_info.cc",
                 "//modules/desktop_capture/desktop_device_info.h",
                 "//modules/desktop_capture/mouse_cursor_monitor_linux.cc",
                 "//modules/desktop_capture/screen_capturer_linux.cc",
                 "//modules/desktop_capture/window_capturer_linux.cc",
-                "//modules/desktop_capture/app_capturer_linux.cc",
                 "//modules/desktop_capture/linux/mouse_cursor_monitor_x11.cc",
                 "//modules/desktop_capture/linux/mouse_cursor_monitor_x11.h",
                 "//modules/desktop_capture/linux/screen_capturer_x11.cc",
                 "//modules/desktop_capture/linux/screen_capturer_x11.h",
                 "//modules/desktop_capture/linux/shared_x_display.cc",
                 "//modules/desktop_capture/linux/shared_x_display.h",
                 "//modules/desktop_capture/linux/window_capturer_x11.cc",
                 "//modules/desktop_capture/linux/window_capturer_x11.h",
--- a/media/webrtc/gn-configs/x86_True_x86_netbsd.json
+++ b/media/webrtc/gn-configs/x86_True_x86_netbsd.json
@@ -7059,17 +7059,16 @@
                 "//modules/desktop_capture/screen_capturer_helper.h",
                 "//modules/desktop_capture/window_finder.cc",
                 "//modules/desktop_capture/window_finder.h",
                 "//modules/desktop_capture/desktop_device_info.cc",
                 "//modules/desktop_capture/desktop_device_info.h",
                 "//modules/desktop_capture/mouse_cursor_monitor_linux.cc",
                 "//modules/desktop_capture/screen_capturer_linux.cc",
                 "//modules/desktop_capture/window_capturer_linux.cc",
-                "//modules/desktop_capture/app_capturer_linux.cc",
                 "//modules/desktop_capture/linux/mouse_cursor_monitor_x11.cc",
                 "//modules/desktop_capture/linux/mouse_cursor_monitor_x11.h",
                 "//modules/desktop_capture/linux/screen_capturer_x11.cc",
                 "//modules/desktop_capture/linux/screen_capturer_x11.h",
                 "//modules/desktop_capture/linux/shared_x_display.cc",
                 "//modules/desktop_capture/linux/shared_x_display.h",
                 "//modules/desktop_capture/linux/window_capturer_x11.cc",
                 "//modules/desktop_capture/linux/window_capturer_x11.h",
--- a/media/webrtc/gn-configs/x86_True_x86_openbsd.json
+++ b/media/webrtc/gn-configs/x86_True_x86_openbsd.json
@@ -9550,17 +9550,16 @@
                 "//modules/desktop_capture/screen_capturer_helper.h",
                 "//modules/desktop_capture/window_finder.cc",
                 "//modules/desktop_capture/window_finder.h",
                 "//modules/desktop_capture/desktop_device_info.cc",
                 "//modules/desktop_capture/desktop_device_info.h",
                 "//modules/desktop_capture/mouse_cursor_monitor_linux.cc",
                 "//modules/desktop_capture/screen_capturer_linux.cc",
                 "//modules/desktop_capture/window_capturer_linux.cc",
-                "//modules/desktop_capture/app_capturer_linux.cc",
                 "//modules/desktop_capture/linux/mouse_cursor_monitor_x11.cc",
                 "//modules/desktop_capture/linux/mouse_cursor_monitor_x11.h",
                 "//modules/desktop_capture/linux/screen_capturer_x11.cc",
                 "//modules/desktop_capture/linux/screen_capturer_x11.h",
                 "//modules/desktop_capture/linux/shared_x_display.cc",
                 "//modules/desktop_capture/linux/shared_x_display.h",
                 "//modules/desktop_capture/linux/window_capturer_x11.cc",
                 "//modules/desktop_capture/linux/window_capturer_x11.h",
--- a/media/webrtc/gn-configs/x86_True_x86_win.json
+++ b/media/webrtc/gn-configs/x86_True_x86_win.json
@@ -10393,17 +10393,16 @@
                 "//modules/desktop_capture/win/screen_capturer_win_magnifier.h",
                 "//modules/desktop_capture/win/window_capture_utils.cc",
                 "//modules/desktop_capture/win/window_capture_utils.h",
                 "//modules/desktop_capture/window_capturer_win.cc",
                 "//modules/desktop_capture/window_finder.cc",
                 "//modules/desktop_capture/window_finder.h",
                 "//modules/desktop_capture/window_finder_win.cc",
                 "//modules/desktop_capture/window_finder_win.h",
-                "//modules/desktop_capture/app_capturer_win.cc",
                 "//modules/desktop_capture/desktop_device_info.cc",
                 "//modules/desktop_capture/desktop_device_info.h",
                 "//modules/desktop_capture/win/desktop_device_info_win.cc",
                 "//modules/desktop_capture/win/win_shared.cc"
             ],
             "type": "static_library"
         },
         "//modules/desktop_capture:primitives": {
--- a/media/webrtc/signaling/src/peerconnection/TransceiverImpl.cpp
+++ b/media/webrtc/signaling/src/peerconnection/TransceiverImpl.cpp
@@ -882,17 +882,16 @@ nsresult TransceiverImpl::ConfigureVideo
     return NS_ERROR_FAILURE;
   }
 
   dom::MediaSourceEnum source = videotrack->GetSource().GetMediaSource();
   webrtc::VideoCodecMode mode = webrtc::kRealtimeVideo;
   switch (source) {
     case dom::MediaSourceEnum::Browser:
     case dom::MediaSourceEnum::Screen:
-    case dom::MediaSourceEnum::Application:
     case dom::MediaSourceEnum::Window:
       mode = webrtc::kScreensharing;
       break;
 
     case dom::MediaSourceEnum::Camera:
     default:
       mode = webrtc::kRealtimeVideo;
       break;
--- a/media/webrtc/trunk/webrtc/modules/desktop_capture/BUILD.gn
+++ b/media/webrtc/trunk/webrtc/modules/desktop_capture/BUILD.gn
@@ -305,26 +305,24 @@ rtc_static_library("desktop_capture_gene
     "window_finder_win.cc",
     "window_finder_win.h",
   ]
 
   if (build_with_mozilla) {
     defines = [ "MULTI_MONITOR_SCREENSHARE" ]
 
     sources += [
-      "app_capturer_win.cc",
       "desktop_device_info.cc",
       "desktop_device_info.h",
       "win/desktop_device_info_win.cc",
       "win/win_shared.cc",
     ]
 
     if (is_mac) {
       sources += [
-        "app_capturer_mac.mm",
         "mac/desktop_device_info_mac.mm",
       ]
     }
 
     include_dirs = [ "/media/libyuv/libyuv/include" ]
   } else {
     sources += [
       "fake_desktop_capturer.cc",
@@ -333,20 +331,16 @@ rtc_static_library("desktop_capture_gene
   }
 
   if (use_x11 || rtc_use_pipewire) {
     sources += [
       "mouse_cursor_monitor_linux.cc",
       "screen_capturer_linux.cc",
       "window_capturer_linux.cc",
     ]
-
-    if (build_with_mozilla) {
-      sources += [ "app_capturer_linux.cc" ]
-    }
   }
 
   if (use_x11) {
     sources += [
       "linux/mouse_cursor_monitor_x11.cc",
       "linux/mouse_cursor_monitor_x11.h",
       "linux/screen_capturer_x11.cc",
       "linux/screen_capturer_x11.h",
deleted file mode 100644
--- a/media/webrtc/trunk/webrtc/modules/desktop_capture/app_capturer.cc
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
-*  Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
-*
-*  Use of this source code is governed by a BSD-style license
-*  that can be found in the LICENSE file in the root of the source
-*  tree. An additional intellectual property rights grant can be found
-*  in the file PATENTS.  All contributing project authors may
-*  be found in the AUTHORS file in the root of the source tree.
-*/
-
-#include "modules/desktop_capture/app_capturer.h"
-#include "modules/desktop_capture/desktop_capture_options.h"
-
-namespace webrtc {
-
-// static
-AppCapturer* AppCapturer::Create() {
-  return Create(DesktopCaptureOptions::CreateDefault());
-}
-
-}  // namespace webrtc
deleted file mode 100644
--- a/media/webrtc/trunk/webrtc/modules/desktop_capture/app_capturer.h
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
-*  Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
-*
-*  Use of this source code is governed by a BSD-style license
-*  that can be found in the LICENSE file in the root of the source
-*  tree. An additional intellectual property rights grant can be found
-*  in the file PATENTS.  All contributing project authors may
-*  be found in the AUTHORS file in the root of the source tree.
-*/
-
-#ifndef WEBRTC_MODULES_DESKTOP_CAPTURE_APP_CAPTURER_H_
-#define WEBRTC_MODULES_DESKTOP_CAPTURE_APP_CAPTURER_H_
-
-#include <vector>
-#include <string>
-
-#include "modules/desktop_capture/desktop_capture_types.h"
-#include "modules/desktop_capture/desktop_capturer.h"
-#include "typedefs.h"
-
-namespace webrtc {
-
-class DesktopCaptureOptions;
-
-class AppCapturer : public DesktopCapturer {
-public:
-    typedef webrtc::ProcessId ProcessId;
-    struct App {
-        ProcessId id;
-        // Application Name in UTF-8 encoding.
-        std::string title;
-    };
-    typedef std::vector<App> AppList;
-
-    static AppCapturer* Create(const DesktopCaptureOptions& options);
-    static AppCapturer* Create();
-
-    virtual ~AppCapturer() {}
-
-    //AppCapturer Interfaces
-    virtual bool GetAppList(AppList* apps) = 0;
-    virtual bool SelectApp(ProcessId id) = 0;
-    virtual bool BringAppToFront() = 0;
-};
-
-}  // namespace webrtc
-
-#endif  // WEBRTC_MODULES_DESKTOP_CAPTURE_APP_CAPTURER_H_
-
deleted file mode 100755
--- a/media/webrtc/trunk/webrtc/modules/desktop_capture/app_capturer_linux.cc
+++ /dev/null
@@ -1,286 +0,0 @@
-/*
-*  Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
-*
-*  Use of this source code is governed by a BSD-style license
-*  that can be found in the LICENSE file in the root of the source
-*  tree. An additional intellectual property rights grant can be found
-*  in the file PATENTS.  All contributing project authors may
-*  be found in the AUTHORS file in the root of the source tree.
-*/
-#include "modules/desktop_capture/app_capturer.h"
-#include "modules/desktop_capture/shared_desktop_frame.h"
-#include "modules/desktop_capture/linux/shared_x_util.h"
-
-#include <assert.h>
-#include <string.h>
-#include <X11/Xatom.h>
-#include <X11/extensions/Xcomposite.h>
-#include <X11/extensions/Xrender.h>
-#include <X11/Xutil.h>
-#include <X11/Xregion.h>
-
-#include <algorithm>
-
-#include "modules/desktop_capture/desktop_capture_options.h"
-#include "modules/desktop_capture/desktop_frame.h"
-#include "modules/desktop_capture/linux/shared_x_display.h"
-#include "modules/desktop_capture/linux/x_error_trap.h"
-#include "modules/desktop_capture/linux/x_server_pixel_buffer.h"
-#include "rtc_base/logging.h"
-
-namespace webrtc {
-
-namespace {
-
-class ScreenCapturerProxy : DesktopCapturer::Callback {
-public:
-  ScreenCapturerProxy()
-    : screen_capturer_(DesktopCapturer::CreateScreenCapturer(DesktopCaptureOptions::CreateDefault())) {
-    screen_capturer_->SelectSource(kFullDesktopScreenId);
-    screen_capturer_->Start(this);
-  }
-  void CaptureFrame() { screen_capturer_->CaptureFrame(); }
-  std::unique_ptr<DesktopFrame> GetFrame() { return std::move(frame_); }
-
-   // Callback interface
-  virtual void OnCaptureResult(DesktopCapturer::Result result,
-                               std::unique_ptr<DesktopFrame> frame) {
-    frame_ = std::move(frame);
-  }
-
-protected:
-  std::unique_ptr<DesktopCapturer> screen_capturer_;
-  std::unique_ptr<DesktopFrame> frame_;
-};
-
-class AppCapturerLinux : public AppCapturer {
-public:
-  AppCapturerLinux(const DesktopCaptureOptions& options);
-  virtual ~AppCapturerLinux();
-
-  // AppCapturer interface.
-  virtual bool GetAppList(AppList* apps) override;
-  virtual bool SelectApp(ProcessId processId) override;
-  virtual bool BringAppToFront() override;
-
-  // DesktopCapturer interface.
-  virtual void Start(Callback* callback) override;
-  virtual void CaptureFrame() override;
-  virtual bool SelectSource(SourceId id) override
-  {
-    return SelectApp(static_cast<ProcessId>(id));
-  }
-
-protected:
-  Display* GetDisplay() { return x_display_->display(); }
-  bool UpdateRegions();
-
-  void FillDesktopFrameRegionWithColor(DesktopFrame* pDesktopFrame,Region rgn, uint32_t color);
-private:
-  Callback* callback_;
-  ProcessId selected_process_;
-
-  // Sample Mode
-  ScreenCapturerProxy screen_capturer_proxy_;
-  // Mask of foreground (non-app windows in front of selected)
-  Region rgn_mask_;
-  // Region of selected windows
-  Region rgn_visual_;
-  // Mask of background (desktop, non-app windows behind selected)
-  Region rgn_background_;
-
-  rtc::scoped_refptr<SharedXDisplay> x_display_;
-  RTC_DISALLOW_COPY_AND_ASSIGN(AppCapturerLinux);
-};
-
-AppCapturerLinux::AppCapturerLinux(const DesktopCaptureOptions& options)
-    : callback_(NULL),
-      selected_process_(0),
-      x_display_(options.x_display()) {
-  rgn_mask_ = XCreateRegion();
-  rgn_visual_ = XCreateRegion();
-  rgn_background_ = XCreateRegion();
-}
-
-AppCapturerLinux::~AppCapturerLinux() {
-  if (rgn_mask_) {
-    XDestroyRegion(rgn_mask_);
-  }
-  if (rgn_visual_) {
-    XDestroyRegion(rgn_visual_);
-  }
-  if (rgn_background_) {
-    XDestroyRegion(rgn_background_);
-  }
-}
-
-// AppCapturer interface.
-bool AppCapturerLinux::GetAppList(AppList* apps) {
-  // Implemented in DesktopDeviceInfo
-  return true;
-}
-bool AppCapturerLinux::SelectApp(ProcessId processId) {
-  selected_process_ = processId;
-  return true;
-}
-bool AppCapturerLinux::BringAppToFront() {
-  // Not implemented yet: See Bug 1036653
-  return true;
-}
-
-// DesktopCapturer interface.
-void AppCapturerLinux::Start(Callback* callback) {
-  assert(!callback_);
-  assert(callback);
-
-  callback_ = callback;
-}
-
-void AppCapturerLinux::CaptureFrame() {
-  XErrorTrap error_trap(GetDisplay());
-
-  //Capture screen >> set root window as capture window
-  screen_capturer_proxy_.CaptureFrame();
-  std::unique_ptr<DesktopFrame> frame = std::move(screen_capturer_proxy_.GetFrame());
-  if (frame) {
-
-    // calculate app visual/foreground region
-    UpdateRegions();
-
-    // TODO: background/foreground mask colors should be configurable; see Bug 1054503
-    // fill background with black
-    FillDesktopFrameRegionWithColor(frame.get(), rgn_background_, 0xFF000000);
-
-    // fill foreground with yellow
-    FillDesktopFrameRegionWithColor(frame.get(), rgn_mask_, 0xFFFFFF00);
- }
-
-  // trigger event
-  if (callback_) {
-    bool worked = error_trap.GetLastErrorAndDisable() == 0;
-    DesktopCapturer::Result res = worked ? DesktopCapturer::Result::SUCCESS
-                                         : DesktopCapturer::Result::ERROR_TEMPORARY;
-    callback_->OnCaptureResult(res, std::move(frame));
-  }
-}
-
-void AppCapturerLinux::FillDesktopFrameRegionWithColor(DesktopFrame* pDesktopFrame, Region rgn, uint32_t color) {
-  XErrorTrap error_trap(GetDisplay());
-
-  if (!pDesktopFrame) {
-    return;
-  }
-  if (XEmptyRegion(rgn)) {
-    return;
-  }
-
-  REGION * st_rgn = (REGION *)rgn;
-  if(st_rgn && st_rgn->numRects > 0) {
-    for (short i = 0; i < st_rgn->numRects; i++) {
-      for (short j = st_rgn->rects[i].y1; j < st_rgn->rects[i].y2; j++) {
-        uint32_t* dst_pos = reinterpret_cast<uint32_t*>(pDesktopFrame->data() + pDesktopFrame->stride() * j);
-        for (short k = st_rgn->rects[i].x1; k < st_rgn->rects[i].x2; k++) {
-          dst_pos[k] = color;
-        }
-      }
-    }
-  }
-}
-
-bool AppCapturerLinux::UpdateRegions() {
-  XErrorTrap error_trap(GetDisplay());
-
-  XSubtractRegion(rgn_visual_, rgn_visual_, rgn_visual_);
-  XSubtractRegion(rgn_mask_, rgn_mask_, rgn_mask_);
-  WindowUtilX11 window_util_x11(x_display_);
-  int num_screens = XScreenCount(GetDisplay());
-  for (int screen = 0; screen < num_screens; ++screen) {
-    int nScreenCX = DisplayWidth(GetDisplay(), screen);
-    int nScreenCY = DisplayHeight(GetDisplay(), screen);
-
-    XRectangle  screen_rect;
-    screen_rect.x = 0;
-    screen_rect.y = 0;
-    screen_rect.width = nScreenCX;
-    screen_rect.height = nScreenCY;
-
-    XUnionRectWithRegion(&screen_rect, rgn_background_, rgn_background_);
-    XXorRegion(rgn_mask_, rgn_mask_, rgn_mask_);
-    XXorRegion(rgn_visual_, rgn_visual_, rgn_visual_);
-
-    ::Window root_window = XRootWindow(GetDisplay(), screen);
-    ::Window parent;
-    ::Window root_return;
-    ::Window *children;
-    unsigned int num_children;
-    int status = XQueryTree(GetDisplay(), root_window, &root_return, &parent, &children, &num_children);
-    if (status == 0) {
-      RTC_LOG(LS_ERROR) << "Failed to query for child windows for screen " << screen;
-      continue;
-    }
-    for (unsigned int i = 0; i < num_children; ++i) {
-      ::Window app_window = window_util_x11.GetApplicationWindow(children[i]);
-      if (!app_window) {
-        continue;
-      }
-
-      // Get window region
-      XRectangle  win_rect;
-      window_util_x11.GetWindowRect(app_window, win_rect, true);
-      if (win_rect.width <= 0 || win_rect.height <= 0) {
-        continue;
-      }
-
-      Region win_rgn = XCreateRegion();
-      XUnionRectWithRegion(&win_rect, win_rgn, win_rgn);
-      // update rgn_visual_ , rgn_mask_,
-      unsigned int processId = window_util_x11.GetWindowProcessID(app_window);
-      if (processId != 0 && processId == selected_process_) {
-        XUnionRegion(rgn_visual_, win_rgn, rgn_visual_);
-        XSubtractRegion(rgn_mask_, win_rgn, rgn_mask_);
-      } else {
-        Region win_rgn_intersect = XCreateRegion();
-        XIntersectRegion(rgn_visual_, win_rgn, win_rgn_intersect);
-
-        XSubtractRegion(rgn_visual_, win_rgn_intersect, rgn_visual_);
-        XUnionRegion(win_rgn_intersect, rgn_mask_, rgn_mask_);
-
-        if (win_rgn_intersect) {
-          XDestroyRegion(win_rgn_intersect);
-        }
-      }
-      if (win_rgn) {
-        XDestroyRegion(win_rgn);
-      }
-    }
-
-    if (children) {
-      XFree(children);
-    }
-  }
-
-  XSubtractRegion(rgn_background_, rgn_visual_, rgn_background_);
-
-  return true;
-}
-
-}  // namespace
-
-// static
-AppCapturer* AppCapturer::Create(const DesktopCaptureOptions& options) {
-  return new AppCapturerLinux(options);
-}
-
-// static
-std::unique_ptr<DesktopCapturer> DesktopCapturer::CreateRawAppCapturer(
-    const DesktopCaptureOptions& options) {
-
-  if (!options.x_display())
-    return nullptr;
-
-  std::unique_ptr<AppCapturerLinux> capturer(new AppCapturerLinux(options));
-
-  return std::unique_ptr<DesktopCapturer>(std::move(capturer));
-}
-
-}  // namespace webrtc
deleted file mode 100644
--- a/media/webrtc/trunk/webrtc/modules/desktop_capture/app_capturer_mac.mm
+++ /dev/null
@@ -1,197 +0,0 @@
-/*
- *  Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
- *
- *  Use of this source code is governed by a BSD-style license
- *  that can be found in the LICENSE file in the root of the source
- *  tree. An additional intellectual property rights grant can be found
- *  in the file PATENTS.  All contributing project authors may
- *  be found in the AUTHORS file in the root of the source tree.
- */
-
-#include <assert.h>
-#include <ApplicationServices/ApplicationServices.h>
-#include <Cocoa/Cocoa.h>
-#include <Carbon/Carbon.h>
-#include <CoreFoundation/CoreFoundation.h>
-#include <AppKit/AppKit.h>
-
-#include "modules/desktop_capture/app_capturer.h"
-
-#include "modules/desktop_capture/desktop_frame.h"
-#include "rtc_base/constructormagic.h"
-#include "rtc_base/logging.h"
-
-namespace webrtc {
-
-namespace {
-
-class AppCapturerMac : public AppCapturer {
- public:
-  AppCapturerMac();
-  virtual ~AppCapturerMac();
-
-  // AppCapturer interface.
-  virtual bool GetAppList(AppList* apps) override;
-  virtual bool SelectApp(ProcessId processId) override;
-  virtual bool BringAppToFront() override;
-
-  // DesktopCapturer interface.
-  virtual void Start(Callback* callback) override;
-  virtual void CaptureFrame() override;
-  virtual bool SelectSource(SourceId id) override
-  {
-    return SelectApp(static_cast<ProcessId>(id));
-  }
-
- private:
-  Callback* callback_;
-  ProcessId process_id_;
-
-  RTC_DISALLOW_COPY_AND_ASSIGN(AppCapturerMac);
-};
-
-AppCapturerMac::AppCapturerMac()
-  : callback_(NULL),
-    process_id_(0) {
-}
-
-AppCapturerMac::~AppCapturerMac() {
-}
-
-// AppCapturer interface.
-bool AppCapturerMac::GetAppList(AppList* apps) {
-  // handled by DesktopDeviceInfo
-  return true;
-}
-
-bool AppCapturerMac::SelectApp(ProcessId processId) {
-  process_id_ = processId;
-
-  return true;
-}
-
-bool AppCapturerMac::BringAppToFront() {
-  return true;
-}
-
-// DesktopCapturer interface.
-void AppCapturerMac::Start(Callback* callback) {
-  assert(!callback_);
-  assert(callback);
-
-  callback_ = callback;
-}
-
-void AppCapturerMac::CaptureFrame() {
-  // Check that selected process exists
-  NSRunningApplication *ra = [NSRunningApplication runningApplicationWithProcessIdentifier:process_id_];
-  if (!ra) {
-    callback_->OnCaptureResult(DesktopCapturer::Result::ERROR_TEMPORARY, nullptr);
-    return;
-  }
-
-#if defined(__LP64__)
-#define CaptureWindowID int64_t
-#else
-#define CaptureWindowID CGWindowID
-#endif
-
-  CFArrayRef windowInfos = CGWindowListCopyWindowInfo(
-      kCGWindowListOptionOnScreenOnly | kCGWindowListExcludeDesktopElements,
-      kCGNullWindowID);
-  CFIndex windowInfosCount = CFArrayGetCount(windowInfos);
-  CaptureWindowID *captureWindowList = new CaptureWindowID[windowInfosCount];
-  CFIndex captureWindowListCount = 0;
-  for (CFIndex idx = 0; idx < windowInfosCount; idx++) {
-    CFDictionaryRef info = reinterpret_cast<CFDictionaryRef>(
-        CFArrayGetValueAtIndex(windowInfos, idx));
-    CFNumberRef winOwner = reinterpret_cast<CFNumberRef>(
-        CFDictionaryGetValue(info, kCGWindowOwnerPID));
-    CFNumberRef winId = reinterpret_cast<CFNumberRef>(
-        CFDictionaryGetValue(info, kCGWindowNumber));
-
-    pid_t owner;
-    CFNumberGetValue(winOwner, kCFNumberIntType, &owner);
-    if (owner != process_id_) {
-      continue;
-    }
-
-    CGWindowID ident;
-    CFNumberGetValue(winId, kCFNumberIntType, &ident);
-
-    captureWindowList[captureWindowListCount++] = ident;
-  }
-  CFRelease(windowInfos);
-
-  // Check that window list is not empty
-  if (captureWindowListCount <= 0) {
-    delete [] captureWindowList;
-    callback_->OnCaptureResult(DesktopCapturer::Result::ERROR_TEMPORARY, nullptr);
-    return;
-  }
-
-  // Does not support multi-display; See bug 1037997.
-  CGRect rectCapturedDisplay = CGDisplayBounds(CGMainDisplayID());
-
-  // Capture all windows of selected process, bounded by desktop.
-  CFArrayRef windowIDsArray = CFArrayCreate(kCFAllocatorDefault,
-                                            (const void**)captureWindowList,
-                                            captureWindowListCount,
-                                            NULL);
-  CGImageRef app_image = CGWindowListCreateImageFromArray(rectCapturedDisplay,
-                                                          windowIDsArray,
-                                                          kCGWindowImageDefault);
-  CFRelease (windowIDsArray);
-  delete [] captureWindowList;
-
-  // Wrap raw data into DesktopFrame
-  if (!app_image) {
-    CFRelease(app_image);
-    callback_->OnCaptureResult(DesktopCapturer::Result::ERROR_TEMPORARY, nullptr);
-    return;
-  }
-
-  int bits_per_pixel = CGImageGetBitsPerPixel(app_image);
-  if (bits_per_pixel != 32) {
-      RTC_LOG(LS_ERROR) << "Unsupported window image depth: " << bits_per_pixel;
-      CFRelease(app_image);
-      callback_->OnCaptureResult(DesktopCapturer::Result::ERROR_TEMPORARY, nullptr);
-      return;
-  }
-
-  int width = CGImageGetWidth(app_image);
-  int height = CGImageGetHeight(app_image);
-  std::unique_ptr<DesktopFrame> frame(new BasicDesktopFrame(DesktopSize(width, height)));
-
-  CGDataProviderRef provider = CGImageGetDataProvider(app_image);
-  CFDataRef cf_data = CGDataProviderCopyData(provider);
-  int src_stride = CGImageGetBytesPerRow(app_image);
-  const uint8_t* src_data = CFDataGetBytePtr(cf_data);
-  for (int y = 0; y < height; ++y) {
-    memcpy(frame->data() + frame->stride() * y, src_data + src_stride * y,
-            DesktopFrame::kBytesPerPixel * width);
-  }
-
-  CFRelease(cf_data);
-  CFRelease(app_image);
-
-  callback_->OnCaptureResult(DesktopCapturer::Result::SUCCESS, std::move(frame));
-}
-
-}  // namespace
-
-// static
-AppCapturer* AppCapturer::Create(const DesktopCaptureOptions& options) {
-  return new AppCapturerMac();
-}
-
-// static
-std::unique_ptr<DesktopCapturer> DesktopCapturer::CreateRawAppCapturer(
-    const DesktopCaptureOptions& options) {
-
-  std::unique_ptr<AppCapturerMac> capturer(new AppCapturerMac());
-
-  return std::unique_ptr<DesktopCapturer>(capturer.release());
-}
-
-}  // namespace webrtc
deleted file mode 100644
--- a/media/webrtc/trunk/webrtc/modules/desktop_capture/app_capturer_null.cc
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
-*  Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
-*
-*  Use of this source code is governed by a BSD-style license
-*  that can be found in the LICENSE file in the root of the source
-*  tree. An additional intellectual property rights grant can be found
-*  in the file PATENTS.  All contributing project authors may
-*  be found in the AUTHORS file in the root of the source tree.
-*/
-#include "webrtc/modules/desktop_capture/window_capturer.h"
-#include "webrtc/modules/desktop_capture/app_capturer.h"
-
-#include <assert.h>
-
-#include "webrtc/modules/desktop_capture/desktop_frame.h"
-
-namespace webrtc {
-
-namespace {
-
-class AppCapturerNull : public AppCapturer {
-public:
-  AppCapturerNull();
-  virtual ~AppCapturerNull();
-
-  // AppCapturer interface.
-  virtual bool GetAppList(AppList* apps) override;
-  virtual bool SelectApp(ProcessId id) override;
-  virtual bool BringAppToFront()	override;
-
-  // DesktopCapturer interface.
-  virtual void Start(Callback* callback) override;
-  virtual void Capture(const DesktopRegion& region) override;
-
-private:
-  Callback* callback_;
-
-  RTC_DISALLOW_COPY_AND_ASSIGN(AppCapturerNull);
-};
-
-AppCapturerNull::AppCapturerNull()
-  : callback_(NULL) {
-}
-
-AppCapturerNull::~AppCapturerNull() {
-}
-
-bool AppCapturerNull::GetAppList(AppList* apps) {
-  // Not implemented yet: See Bug 1036653
-  return false;
-}
-
-bool AppCapturerNull::SelectApp(ProcessId id) {
-  // Not implemented yet: See Bug 1036653
-  return false;
-}
-
-bool AppCapturerNull::BringAppToFront() {
-  // Not implemented yet: See Bug 1036653
-  return false;
-}
-
-// DesktopCapturer interface.
-void AppCapturerNull::Start(Callback* callback) {
-  assert(!callback_);
-  assert(callback);
-
-  callback_ = callback;
-}
-
-void AppCapturerNull::Capture(const DesktopRegion& region) {
-  // Not implemented yet: See Bug 1036653
-  callback_->OnCaptureCompleted(NULL);
-}
-
-}  // namespace
-
-// static
-AppCapturer* AppCapturer::Create(const DesktopCaptureOptions& options) {
-  return new AppCapturerNull();
-}
-
-}  // namespace webrtc
deleted file mode 100644
--- a/media/webrtc/trunk/webrtc/modules/desktop_capture/app_capturer_unittest.cc
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
-*  Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
-*
-*  Use of this source code is governed by a BSD-style license
-*  that can be found in the LICENSE file in the root of the source
-*  tree. An additional intellectual property rights grant can be found
-*  in the file PATENTS.  All contributing project authors may
-*  be found in the AUTHORS file in the root of the source tree.
-*/
-#include "webrtc/modules/desktop_capture/app_capturer.h"
-
-#include "gtest/gtest.h"
-#include "webrtc/modules/desktop_capture/desktop_capture_options.h"
-#include "webrtc/modules/desktop_capture/desktop_frame.h"
-#include "webrtc/modules/desktop_capture/desktop_region.h"
-#include "webrtc/system_wrappers/include/logging.h"
-
-namespace webrtc {
-
-class AppCapturerTest : public testing::Test,
-                        public DesktopCapturer::Callback {
-public:
-  void SetUp() override {
-    capturer_.reset(
-      AppCapturer::Create(DesktopCaptureOptions::CreateDefault())
-    );
-  }
-
-  void TearDown() override {
-  }
-
-  // DesktopCapturer::Callback interface
-  virtual SharedMemory* CreateSharedMemory(size_t size) override {
-    return NULL;
-  }
-
-  virtual void OnCaptureCompleted(DesktopFrame* frame) override {
-    frame_.reset(frame);
-  }
-
-protected:
-  rtc::scoped_refptr<AppCapturer> capturer_;
-  rtc::scoped_refptr<DesktopFrame> frame_;
-};
-
-// Verify that we can enumerate applications.
-TEST_F(AppCapturerTest, Enumerate) {
-  AppCapturer::AppList apps;
-  EXPECT_TRUE(capturer_->GetAppList(&apps));
-
-  // Verify that window titles are set.
-  for (AppCapturer::AppList::iterator it = apps.begin();
-       it != windows.end(); ++it) {
-    EXPECT_FALSE(it->title.empty());
-  }
-}
-
-// Verify we can capture a app.
-TEST_F(AppCapturerTest, Capture) {
-  AppCapturer::AppList apps;
-  capturer_->Start(this);
-  EXPECT_TRUE(capturer_->GetAppList(&apps));
-
-  // Verify that we can select and capture each app.
-  for (AppCapturer::AppList::iterator it = apps.begin();
-       it != apps.end(); ++it) {
-    frame_.reset();
-    if (capturer_->SelectApp(it->id)) {
-      capturer_->Capture(DesktopRegion());
-    }
-
-    // If we failed to capture a window make sure it no longer exists.
-    if (!frame_.get()) {
-      AppCapturer::AppList new_list;
-      EXPECT_TRUE(capturer_->GetAppList(&new_list));
-      for (AppCapturer::AppList::iterator new_list_it = apps.begin();
-           new_list_it != apps.end(); ++new_list_it) {
-        EXPECT_FALSE(it->id == new_list_it->id);
-      }
-      continue;
-    }
-
-    EXPECT_GT(frame_->size().width(), 0);
-    EXPECT_GT(frame_->size().height(), 0);
-  }
-}
-
-}  // namespace webrtc
deleted file mode 100644
--- a/media/webrtc/trunk/webrtc/modules/desktop_capture/app_capturer_win.cc
+++ /dev/null
@@ -1,437 +0,0 @@
-/*
-*  Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
-*
-*  Use of this source code is governed by a BSD-style license
-*  that can be found in the LICENSE file in the root of the source
-*  tree. An additional intellectual property rights grant can be found
-*  in the file PATENTS.  All contributing project authors may
-*  be found in the AUTHORS file in the root of the source tree.
-*/
-
-#include "modules/desktop_capture/app_capturer.h"
-#include "modules/desktop_capture/shared_desktop_frame.h"
-#include "modules/desktop_capture/win/win_shared.h"
-
-#include <windows.h>
-#include <vector>
-#include <cassert>
-
-#include "modules/desktop_capture/desktop_capturer.h"
-#include "modules/desktop_capture/desktop_capture_options.h"
-#include "modules/desktop_capture/desktop_frame_win.h"
-
-namespace webrtc {
-
-namespace {
-
-// Proxy over the WebRTC window capturer, to allow post-processing
-// of the frame to merge multiple window capture frames into a single frame
-class WindowsCapturerProxy : DesktopCapturer::Callback {
-public:
-  WindowsCapturerProxy() :
-      window_capturer_(DesktopCapturer::CreateWindowCapturer(DesktopCaptureOptions::CreateDefault())) {
-    window_capturer_->Start(this);
-  }
-  ~WindowsCapturerProxy(){}
-
-  void SelectSource(DesktopCapturer::SourceId id) { window_capturer_->SelectSource(id); }
-  void CaptureFrame() { window_capturer_->CaptureFrame(); }
-  std::unique_ptr<DesktopFrame> GetFrame() { return std::move(frame_); }
-
-  // Callback interface
-  virtual void OnCaptureResult(DesktopCapturer::Result result,
-                               std::unique_ptr<DesktopFrame> frame) {
-    frame_ = std::move(frame);
-  }
-
-private:
-  std::unique_ptr<DesktopCapturer> window_capturer_;
-  std::unique_ptr<DesktopFrame> frame_;
-};
-
-// Proxy over the WebRTC screen capturer, to allow post-processing
-// of the frame to mask out non-application windows
-class ScreenCapturerProxy : DesktopCapturer::Callback {
-public:
-  ScreenCapturerProxy()
-    : screen_capturer_(DesktopCapturer::CreateScreenCapturer(DesktopCaptureOptions::CreateDefault())) {
-    screen_capturer_->SelectSource(kFullDesktopScreenId);
-    screen_capturer_->Start(this);
-  }
-  void CaptureFrame() { screen_capturer_->CaptureFrame(); }
-  std::unique_ptr<DesktopFrame> GetFrame() { return std::move(frame_); }
-
-   // Callback interface
-  virtual void OnCaptureResult(DesktopCapturer::Result result,
-                               std::unique_ptr<DesktopFrame> frame) {
-    frame_ = std::move(frame);
-  }
-
-protected:
-  std::unique_ptr<DesktopCapturer> screen_capturer_;
-  std::unique_ptr<DesktopFrame> frame_;
-};
-
-class AppCapturerWin : public AppCapturer {
-public:
-  AppCapturerWin(const DesktopCaptureOptions& options);
-  virtual ~AppCapturerWin();
-
-  // AppCapturer interface.
-  virtual bool GetAppList(AppList* apps) override;
-  virtual bool SelectApp(ProcessId processId) override;
-  virtual bool BringAppToFront() override;
-
-  // DesktopCapturer interface.
-  virtual void Start(Callback* callback) override;
-  virtual void CaptureFrame() override;
-  virtual bool SelectSource(SourceId id) override
-  {
-    return SelectApp(static_cast<ProcessId>(id));
-  }
-
-  struct WindowItem {
-    HWND handle;
-    RECT bounds;
-    bool owned;
-  };
-
-  struct EnumWindowsCtx {
-    ProcessId process_id;
-    std::vector<WindowItem> windows;
-    bool list_all;
-  };
-
-  static BOOL CALLBACK EnumWindowsProc(HWND handle, LPARAM lParam);
-protected:
-  void CaptureByWebRTC();
-  void CaptureBySample();
-private:
-  Callback* callback_;
-
-  ProcessId processId_;
-
-  // Sample Mode
-  ScreenCapturerProxy screen_capturer_proxy_;
-  // Mask of foreground (non-app windows in front of selected)
-  HRGN hrgn_foreground_;
-  // Mask of background (desktop, non-app windows behind selected)
-  HRGN hrgn_background_;
-  // Region of selected windows
-  HRGN hrgn_visual_;
-
-  void UpdateRegions();
-
-  // WebRTC Window mode
-  WindowsCapturerProxy window_capturer_proxy_;
-
-  RTC_DISALLOW_COPY_AND_ASSIGN(AppCapturerWin);
-};
-
-AppCapturerWin::AppCapturerWin(const DesktopCaptureOptions& options)
-  : callback_(nullptr),
-    processId_(0) {
-  // Initialize regions to zero
-  hrgn_foreground_ = CreateRectRgn(0, 0, 0, 0);
-  hrgn_background_ = CreateRectRgn(0, 0, 0, 0);
-  hrgn_visual_ = CreateRectRgn(0, 0, 0, 0);
-}
-
-AppCapturerWin::~AppCapturerWin() {
-  if (hrgn_foreground_) {
-    DeleteObject(hrgn_foreground_);
-  }
-  if (hrgn_background_) {
-    DeleteObject(hrgn_background_);
-  }
-  if (hrgn_visual_) {
-    DeleteObject(hrgn_visual_);
-  }
-}
-
-// AppCapturer interface.
-bool AppCapturerWin::GetAppList(AppList* apps){
-  // Implemented via DesktopDeviceInfo
-  return true;
-}
-
-bool AppCapturerWin::SelectApp(ProcessId processId) {
-  processId_ = processId;
-  return true;
-}
-
-bool AppCapturerWin::BringAppToFront() {
-  // Not implemented yet: See Bug 1036653
-  return true;
-}
-
-// DesktopCapturer interface.
-void AppCapturerWin::Start(Callback* callback) {
-  assert(!callback_);
-  assert(callback);
-
-  callback_ = callback;
-}
-
-void AppCapturerWin::CaptureFrame() {
-  assert(IsGUIThread(false));
-  CaptureBySample();
-}
-
-BOOL CALLBACK AppCapturerWin::EnumWindowsProc(HWND handle, LPARAM lParam) {
-  EnumWindowsCtx *pEnumWindowsCtx = reinterpret_cast<EnumWindowsCtx *>(lParam);
-  if (!pEnumWindowsCtx) {
-    return FALSE;
-  }
-
-  DWORD procId = -1;
-  GetWindowThreadProcessId(handle, &procId);
-  if (procId == pEnumWindowsCtx->process_id || pEnumWindowsCtx->list_all) {
-    WindowItem window_item;
-    window_item.handle = handle;
-
-    if (!IsWindowVisible(handle) || IsIconic(handle)) {
-      return TRUE;
-    }
-
-    GetWindowRect(handle, &window_item.bounds);
-    window_item.owned = (procId == pEnumWindowsCtx->process_id);
-    pEnumWindowsCtx->windows.push_back(window_item);
-  }
-
-  return TRUE;
-}
-
-void AppCapturerWin::CaptureByWebRTC() {
-  assert(IsGUIThread(false));
-  // List Windows of selected application
-  EnumWindowsCtx lParamEnumWindows;
-  lParamEnumWindows.process_id = processId_;
-  lParamEnumWindows.list_all = false;
-  EnumWindows(EnumWindowsProc, (LPARAM)&lParamEnumWindows);
-
-  // Prepare capture dc context
-  // TODO: handle multi-monitor setups; see Bug 1037997
-  DesktopRect rcDesktop(DesktopRect::MakeXYWH(
-      GetSystemMetrics(SM_XVIRTUALSCREEN),
-      GetSystemMetrics(SM_YVIRTUALSCREEN),
-      GetSystemMetrics(SM_CXVIRTUALSCREEN),
-      GetSystemMetrics(SM_CYVIRTUALSCREEN)
-  ));
-
-  HDC dcScreen = GetDC(nullptr);
-  HDC memDcCapture = CreateCompatibleDC(dcScreen);
-  if (dcScreen) {
-    ReleaseDC(nullptr, dcScreen);
-  }
-
-  std::unique_ptr<DesktopFrameWin> frameCapture(DesktopFrameWin::Create(
-      DesktopSize(rcDesktop.width(), rcDesktop.height()),
-      nullptr, memDcCapture));
-  HBITMAP bmpOrigin = static_cast<HBITMAP>(SelectObject(memDcCapture, frameCapture->bitmap()));
-  BOOL bCaptureAppResult = false;
-  // Capture and Combine all windows into memDcCapture
-  std::vector<WindowItem>::reverse_iterator itItem;
-  for (itItem = lParamEnumWindows.windows.rbegin(); itItem != lParamEnumWindows.windows.rend(); itItem++) {
-    WindowItem window_item = *itItem;
-    HWND hWndCapturer = window_item.handle;
-    if (!IsWindow(hWndCapturer) || !IsWindowVisible(hWndCapturer) || IsIconic(hWndCapturer)) {
-      continue;
-    }
-
-    HDC memDcWin = nullptr;
-    HBITMAP bmpOriginWin = nullptr;
-    HBITMAP hBitmapFrame = nullptr;
-    HDC dcWin = nullptr;
-    RECT rcWin = window_item.bounds;
-    bool bCaptureResult = false;
-    std::unique_ptr<DesktopFrameWin> frame;
-    do {
-      if (rcWin.left == rcWin.right || rcWin.top == rcWin.bottom) {
-        break;
-      }
-
-      dcWin = GetWindowDC(hWndCapturer);
-      if (!dcWin) {
-        break;
-      }
-      memDcWin = CreateCompatibleDC(dcWin);
-
-      // Capture
-      window_capturer_proxy_.SelectSource((SourceId) hWndCapturer);
-      window_capturer_proxy_.CaptureFrame();
-      if (window_capturer_proxy_.GetFrame() != nullptr) {
-        DesktopFrameWin *pDesktopFrameWin = reinterpret_cast<DesktopFrameWin *>(
-            window_capturer_proxy_.GetFrame().get());
-        if (pDesktopFrameWin) {
-          hBitmapFrame = pDesktopFrameWin->bitmap();
-        }
-        if (GetObjectType(hBitmapFrame) != OBJ_BITMAP) {
-          hBitmapFrame = nullptr;
-        }
-      }
-      if (!hBitmapFrame) {
-        break;
-      }
-      bmpOriginWin = static_cast<HBITMAP>(SelectObject(memDcWin, hBitmapFrame));
-    } while(0);
-
-    // bitblt to capture memDcCapture
-    if (bmpOriginWin) {
-      BitBlt(memDcCapture,
-          rcWin.left, rcWin.top, rcWin.right - rcWin.left, rcWin.bottom - rcWin.top,
-          memDcWin, 0, 0, SRCCOPY);
-      bCaptureAppResult = true;
-    }
-
-    // Clean resource
-    if (memDcWin) {
-      SelectObject(memDcWin, bmpOriginWin);
-      DeleteDC(memDcWin);
-    }
-    if (dcWin) {
-      ReleaseDC(hWndCapturer, dcWin);
-    }
-  }
-
-  // Clean resource
-  if (memDcCapture) {
-    SelectObject(memDcCapture, bmpOrigin);
-    DeleteDC(memDcCapture);
-  }
-
-  // trigger event
-  if (bCaptureAppResult) {
-    callback_->OnCaptureResult(DesktopCapturer::Result::SUCCESS, std::move(frameCapture));
-  }
-}
-
-// Application Capturer by sample and region
-void AppCapturerWin::CaptureBySample(){
-  assert(IsGUIThread(false));
-  // capture entire screen
-  screen_capturer_proxy_.CaptureFrame();
-
-  HBITMAP hBitmapFrame = nullptr;
-  if (screen_capturer_proxy_.GetFrame() != nullptr) {
-    SharedDesktopFrame* pSharedDesktopFrame = reinterpret_cast<SharedDesktopFrame*>(
-        screen_capturer_proxy_.GetFrame().get());
-    if (pSharedDesktopFrame) {
-      DesktopFrameWin *pDesktopFrameWin =reinterpret_cast<DesktopFrameWin *>(
-          pSharedDesktopFrame->GetUnderlyingFrame());
-      if (pDesktopFrameWin) {
-        hBitmapFrame = pDesktopFrameWin->bitmap();
-      }
-      if (GetObjectType(hBitmapFrame) != OBJ_BITMAP) {
-        hBitmapFrame = nullptr;
-      }
-    }
-  }
-  if (hBitmapFrame) {
-    // calculate app visual/foreground region
-    UpdateRegions();
-
-    HDC dcScreen = GetDC(nullptr);
-    HDC memDcCapture = CreateCompatibleDC(dcScreen);
-
-    RECT rcScreen = {0, 0,
-        screen_capturer_proxy_.GetFrame()->size().width(),
-        screen_capturer_proxy_.GetFrame()->size().height()
-    };
-
-    HBITMAP bmpOriginCapture = (HBITMAP)SelectObject(memDcCapture, hBitmapFrame);
-
-    // TODO: background/foreground mask colors should be configurable; see Bug 1054503
-    // fill background
-    SelectClipRgn(memDcCapture, hrgn_background_);
-    SelectObject(memDcCapture, GetStockObject(DC_BRUSH));
-    SetDCBrushColor(memDcCapture, RGB(0, 0, 0));
-    FillRect(memDcCapture, &rcScreen, (HBRUSH)GetStockObject(DC_BRUSH));
-
-    // fill foreground
-    SelectClipRgn(memDcCapture, hrgn_foreground_);
-    SelectObject(memDcCapture, GetStockObject(DC_BRUSH));
-    SetDCBrushColor(memDcCapture, RGB(0xff, 0xff, 0));
-    FillRect(memDcCapture, &rcScreen, (HBRUSH)GetStockObject(DC_BRUSH));
-
-    if (dcScreen) {
-      ReleaseDC(nullptr, dcScreen);
-    }
-    SelectObject(memDcCapture, bmpOriginCapture);
-    DeleteDC(memDcCapture);
-  }
-
-  // trigger event
-  if (callback_) {
-    callback_->OnCaptureResult(DesktopCapturer::Result::SUCCESS, std::move(screen_capturer_proxy_.GetFrame()));
-  }
-}
-
-void AppCapturerWin::UpdateRegions() {
-  assert(IsGUIThread(false));
-  // List Windows of selected application
-  EnumWindowsCtx lParamEnumWindows;
-  lParamEnumWindows.process_id = processId_;
-  lParamEnumWindows.list_all = true;
-  EnumWindows(EnumWindowsProc, (LPARAM)&lParamEnumWindows);
-
-  SetRectRgn(hrgn_foreground_, 0, 0, 0, 0);
-  SetRectRgn(hrgn_visual_, 0, 0, 0, 0);
-  SetRectRgn(hrgn_background_, 0, 0, 0, 0);
-
-  HRGN hrgn_screen_ = CreateRectRgn(0, 0,
-      GetSystemMetrics(SM_CXVIRTUALSCREEN),
-      GetSystemMetrics(SM_CYVIRTUALSCREEN));
-
-  HRGN hrgn_window = CreateRectRgn(0, 0, 0, 0);
-  HRGN hrgn_internsect = CreateRectRgn(0, 0, 0, 0);
-  std::vector<WindowItem>::reverse_iterator itItem;
-  for (itItem = lParamEnumWindows.windows.rbegin(); itItem != lParamEnumWindows.windows.rend(); itItem++) {
-    WindowItem window_item = *itItem;
-    SetRectRgn(hrgn_window, 0, 0, 0, 0);
-    if (GetWindowRgn(window_item.handle, hrgn_window) == ERROR) {
-      SetRectRgn(hrgn_window, window_item.bounds.left,
-                 window_item.bounds.top,
-                 window_item.bounds.right,
-                 window_item.bounds.bottom);
-    }
-
-    if (window_item.owned) {
-      CombineRgn(hrgn_visual_, hrgn_visual_, hrgn_window, RGN_OR);
-      CombineRgn(hrgn_foreground_, hrgn_foreground_, hrgn_window, RGN_DIFF);
-    } else {
-      SetRectRgn(hrgn_internsect, 0, 0, 0, 0);
-      CombineRgn(hrgn_internsect, hrgn_visual_, hrgn_window, RGN_AND);
-
-      CombineRgn(hrgn_visual_, hrgn_visual_, hrgn_internsect, RGN_DIFF);
-
-      CombineRgn(hrgn_foreground_, hrgn_foreground_, hrgn_internsect, RGN_OR);
-    }
-  }
-  CombineRgn(hrgn_background_, hrgn_screen_, hrgn_visual_, RGN_DIFF);
-
-  if (hrgn_window) {
-    DeleteObject(hrgn_window);
-  }
-  if (hrgn_internsect) {
-    DeleteObject(hrgn_internsect);
-  }
-}
-
-}  // namespace
-
-// static
-AppCapturer* AppCapturer::Create(const DesktopCaptureOptions& options) {
-  return new AppCapturerWin(options);
-}
-
-// static
-std::unique_ptr<DesktopCapturer> DesktopCapturer::CreateRawAppCapturer(
-    const DesktopCaptureOptions& options) {
-
-  std::unique_ptr<AppCapturerWin> capturer(new AppCapturerWin(options));
-
-  return std::unique_ptr<DesktopCapturer>(std::move(capturer));
-}
-
-}  // namespace webrtc
--- a/media/webrtc/trunk/webrtc/modules/desktop_capture/desktop_capture_generic_gn/moz.build
+++ b/media/webrtc/trunk/webrtc/modules/desktop_capture/desktop_capture_generic_gn/moz.build
@@ -69,17 +69,16 @@ if CONFIG["OS_TARGET"] == "Darwin":
     DEFINES["WEBRTC_POSIX"] = True
     DEFINES["__ASSERT_MACROS_DEFINE_VERSIONS_WITHOUT_UNDERSCORE"] = "0"
 
     OS_LIBS += [
         "-framework Foundation"
     ]
 
     UNIFIED_SOURCES += [
-        "/media/webrtc/trunk/webrtc/modules/desktop_capture/app_capturer_mac.mm",
         "/media/webrtc/trunk/webrtc/modules/desktop_capture/mac/desktop_configuration_monitor.cc",
         "/media/webrtc/trunk/webrtc/modules/desktop_capture/mac/desktop_device_info_mac.mm",
         "/media/webrtc/trunk/webrtc/modules/desktop_capture/mac/full_screen_chrome_window_detector.cc",
         "/media/webrtc/trunk/webrtc/modules/desktop_capture/mac/scoped_pixel_buffer_object.cc",
         "/media/webrtc/trunk/webrtc/modules/desktop_capture/mac/window_list_utils.cc"
     ]
 
 if CONFIG["OS_TARGET"] == "DragonFly":
@@ -98,17 +97,16 @@ if CONFIG["OS_TARGET"] == "DragonFly":
         "Xdamage",
         "Xext",
         "Xfixes",
         "Xi",
         "Xrender"
     ]
 
     UNIFIED_SOURCES += [
-        "/media/webrtc/trunk/webrtc/modules/desktop_capture/app_capturer_linux.cc",
         "/media/webrtc/trunk/webrtc/modules/desktop_capture/linux/desktop_device_info_x11.cc",
         "/media/webrtc/trunk/webrtc/modules/desktop_capture/linux/mouse_cursor_monitor_x11.cc",
         "/media/webrtc/trunk/webrtc/modules/desktop_capture/linux/screen_capturer_x11.cc",
         "/media/webrtc/trunk/webrtc/modules/desktop_capture/linux/shared_x_display.cc",
         "/media/webrtc/trunk/webrtc/modules/desktop_capture/linux/shared_x_util.cc",
         "/media/webrtc/trunk/webrtc/modules/desktop_capture/linux/window_capturer_x11.cc",
         "/media/webrtc/trunk/webrtc/modules/desktop_capture/linux/window_finder_x11.cc",
         "/media/webrtc/trunk/webrtc/modules/desktop_capture/linux/window_list_utils.cc",
@@ -136,17 +134,16 @@ if CONFIG["OS_TARGET"] == "FreeBSD":
         "Xdamage",
         "Xext",
         "Xfixes",
         "Xi",
         "Xrender"
     ]
 
     UNIFIED_SOURCES += [
-        "/media/webrtc/trunk/webrtc/modules/desktop_capture/app_capturer_linux.cc",
         "/media/webrtc/trunk/webrtc/modules/desktop_capture/linux/desktop_device_info_x11.cc",
         "/media/webrtc/trunk/webrtc/modules/desktop_capture/linux/mouse_cursor_monitor_x11.cc",
         "/media/webrtc/trunk/webrtc/modules/desktop_capture/linux/screen_capturer_x11.cc",
         "/media/webrtc/trunk/webrtc/modules/desktop_capture/linux/shared_x_display.cc",
         "/media/webrtc/trunk/webrtc/modules/desktop_capture/linux/shared_x_util.cc",
         "/media/webrtc/trunk/webrtc/modules/desktop_capture/linux/window_capturer_x11.cc",
         "/media/webrtc/trunk/webrtc/modules/desktop_capture/linux/window_finder_x11.cc",
         "/media/webrtc/trunk/webrtc/modules/desktop_capture/linux/window_list_utils.cc",
@@ -176,17 +173,16 @@ if CONFIG["OS_TARGET"] == "Linux":
         "Xdamage",
         "Xext",
         "Xfixes",
         "Xi",
         "Xrender"
     ]
 
     UNIFIED_SOURCES += [
-        "/media/webrtc/trunk/webrtc/modules/desktop_capture/app_capturer_linux.cc",
         "/media/webrtc/trunk/webrtc/modules/desktop_capture/linux/desktop_device_info_x11.cc",
         "/media/webrtc/trunk/webrtc/modules/desktop_capture/linux/mouse_cursor_monitor_x11.cc",
         "/media/webrtc/trunk/webrtc/modules/desktop_capture/linux/screen_capturer_x11.cc",
         "/media/webrtc/trunk/webrtc/modules/desktop_capture/linux/shared_x_display.cc",
         "/media/webrtc/trunk/webrtc/modules/desktop_capture/linux/shared_x_util.cc",
         "/media/webrtc/trunk/webrtc/modules/desktop_capture/linux/window_capturer_x11.cc",
         "/media/webrtc/trunk/webrtc/modules/desktop_capture/linux/window_finder_x11.cc",
         "/media/webrtc/trunk/webrtc/modules/desktop_capture/linux/window_list_utils.cc",
@@ -214,17 +210,16 @@ if CONFIG["OS_TARGET"] == "NetBSD":
         "Xdamage",
         "Xext",
         "Xfixes",
         "Xi",
         "Xrender"
     ]
 
     UNIFIED_SOURCES += [
-        "/media/webrtc/trunk/webrtc/modules/desktop_capture/app_capturer_linux.cc",
         "/media/webrtc/trunk/webrtc/modules/desktop_capture/linux/desktop_device_info_x11.cc",
         "/media/webrtc/trunk/webrtc/modules/desktop_capture/linux/mouse_cursor_monitor_x11.cc",
         "/media/webrtc/trunk/webrtc/modules/desktop_capture/linux/screen_capturer_x11.cc",
         "/media/webrtc/trunk/webrtc/modules/desktop_capture/linux/shared_x_display.cc",
         "/media/webrtc/trunk/webrtc/modules/desktop_capture/linux/shared_x_util.cc",
         "/media/webrtc/trunk/webrtc/modules/desktop_capture/linux/window_capturer_x11.cc",
         "/media/webrtc/trunk/webrtc/modules/desktop_capture/linux/window_finder_x11.cc",
         "/media/webrtc/trunk/webrtc/modules/desktop_capture/linux/window_list_utils.cc",
@@ -252,17 +247,16 @@ if CONFIG["OS_TARGET"] == "OpenBSD":
         "Xdamage",
         "Xext",
         "Xfixes",
         "Xi",
         "Xrender"
     ]
 
     UNIFIED_SOURCES += [
-        "/media/webrtc/trunk/webrtc/modules/desktop_capture/app_capturer_linux.cc",
         "/media/webrtc/trunk/webrtc/modules/desktop_capture/linux/desktop_device_info_x11.cc",
         "/media/webrtc/trunk/webrtc/modules/desktop_capture/linux/mouse_cursor_monitor_x11.cc",
         "/media/webrtc/trunk/webrtc/modules/desktop_capture/linux/screen_capturer_x11.cc",
         "/media/webrtc/trunk/webrtc/modules/desktop_capture/linux/shared_x_display.cc",
         "/media/webrtc/trunk/webrtc/modules/desktop_capture/linux/shared_x_util.cc",
         "/media/webrtc/trunk/webrtc/modules/desktop_capture/linux/window_capturer_x11.cc",
         "/media/webrtc/trunk/webrtc/modules/desktop_capture/linux/window_finder_x11.cc",
         "/media/webrtc/trunk/webrtc/modules/desktop_capture/linux/window_list_utils.cc",
@@ -305,17 +299,16 @@ if CONFIG["OS_TARGET"] == "WINNT":
         "winmm"
     ]
 
     SOURCES += [
         "/media/webrtc/trunk/webrtc/modules/desktop_capture/win/screen_capturer_win_gdi.cc"
     ]
 
     UNIFIED_SOURCES += [
-        "/media/webrtc/trunk/webrtc/modules/desktop_capture/app_capturer_win.cc",
         "/media/webrtc/trunk/webrtc/modules/desktop_capture/cropping_window_capturer_win.cc",
         "/media/webrtc/trunk/webrtc/modules/desktop_capture/desktop_frame_win.cc",
         "/media/webrtc/trunk/webrtc/modules/desktop_capture/mouse_cursor_monitor_win.cc",
         "/media/webrtc/trunk/webrtc/modules/desktop_capture/screen_capturer_win.cc",
         "/media/webrtc/trunk/webrtc/modules/desktop_capture/win/cursor.cc",
         "/media/webrtc/trunk/webrtc/modules/desktop_capture/win/d3d_device.cc",
         "/media/webrtc/trunk/webrtc/modules/desktop_capture/win/desktop.cc",
         "/media/webrtc/trunk/webrtc/modules/desktop_capture/win/desktop_device_info_win.cc",
--- a/media/webrtc/trunk/webrtc/modules/desktop_capture/desktop_capturer.cc
+++ b/media/webrtc/trunk/webrtc/modules/desktop_capture/desktop_capturer.cc
@@ -55,26 +55,16 @@ std::unique_ptr<DesktopCapturer> Desktop
   std::unique_ptr<DesktopCapturer> capturer = CreateRawScreenCapturer(options);
   if (capturer && options.detect_updated_region()) {
     capturer.reset(new DesktopCapturerDifferWrapper(std::move(capturer)));
   }
 
   return capturer;
 }
 
-// static
-std::unique_ptr<DesktopCapturer> DesktopCapturer::CreateAppCapturer(
-    const DesktopCaptureOptions& options) {
-  std::unique_ptr<DesktopCapturer> capturer = CreateRawAppCapturer(options);
-  if (options.detect_updated_region()) {
-    capturer.reset(new DesktopCapturerDifferWrapper(std::move(capturer)));
-  }
-  return capturer;
-}
-
 #if defined(WEBRTC_USE_PIPEWIRE) || defined(USE_X11)
 bool DesktopCapturer::IsRunningUnderWayland() {
   const char* xdg_session_type = getenv("XDG_SESSION_TYPE");
   if (!xdg_session_type || strncmp(xdg_session_type, "wayland", 7) != 0)
     return false;
 
   if (!(getenv("WAYLAND_DISPLAY")))
     return false;
--- a/media/webrtc/trunk/webrtc/modules/video_capture/video_capture.h
+++ b/media/webrtc/trunk/webrtc/modules/video_capture/video_capture.h
@@ -21,21 +21,20 @@
 #if defined(ANDROID)
 #include <jni.h>
 #endif
 
 namespace webrtc {
 
 // Mozilla addition
 enum class CaptureDeviceType {
-  Camera = 0,
-  Screen = 1,
-  Application = 2,
-  Window = 3,
-  Browser = 4
+  Camera,
+  Screen,
+  Window,
+  Browser
 };
 // Mozilla addition
 
 struct CaptureDeviceInfo {
   CaptureDeviceType type;
 
   CaptureDeviceInfo() : type(CaptureDeviceType::Camera) {}
   CaptureDeviceInfo(CaptureDeviceType t) : type(t) {}
@@ -45,19 +44,16 @@ struct CaptureDeviceInfo {
   {
     switch(type) {
     case CaptureDeviceType::Camera: {
       return "Camera";
     }
     case CaptureDeviceType::Screen: {
       return "Screen";
     }
-    case CaptureDeviceType::Application: {
-      return "Application";
-    }
     case CaptureDeviceType::Window: {
       return "Window";
     }
     case CaptureDeviceType::Browser: {
       return "Browser";
     }
     }
     assert(false);
--- a/mobile/android/app/geckoview-prefs.js
+++ b/mobile/android/app/geckoview-prefs.js
@@ -45,8 +45,11 @@ pref("browser.safebrowsing.features.malw
 // Enable Tracking Protection blocklist updates
 pref("browser.safebrowsing.features.trackingAnnotation.update", true);
 pref("browser.safebrowsing.features.trackingProtection.update", true);
 
 // Enable cryptomining protection blocklist updates
 pref("browser.safebrowsing.features.cryptomining.update", true);
 // Enable fingerprinting protection blocklist updates
 pref("browser.safebrowsing.features.fingerprinting.update", true);
+
+// Treat mouse as touch only on TV-ish devices
+pref("ui.android.mouse_as_touch", 2);
--- a/mobile/android/base/AppConstants.java.in
+++ b/mobile/android/base/AppConstants.java.in
@@ -243,16 +243,23 @@ public class AppConstants {
 
     public static final boolean NIGHTLY_BUILD =
 //#ifdef NIGHTLY_BUILD
     true;
 //#else
     false;
 //#endif
 
+    public static final boolean FENNEC_NIGHTLY =
+//#ifdef FENNEC_NIGHTLY
+    true;
+//#else
+    false;
+//#endif
+
     public static final boolean DEBUG_BUILD =
 //#ifdef MOZ_DEBUG
     true;
 //#else
     false;
 //#endif
 
     public static final boolean MOZ_MEDIA_PLAYER =
--- a/mobile/android/config/mozconfigs/android-aarch64/debug
+++ b/mobile/android/config/mozconfigs/android-aarch64/debug
@@ -2,15 +2,17 @@
 
 # Global options
 ac_add_options --enable-debug
 
 # Android
 ac_add_options --with-android-min-sdk=21
 ac_add_options --target=aarch64-linux-android
 
+export FENNEC_NIGHTLY=1
+
 export MOZILLA_OFFICIAL=1
 export MOZ_TELEMETRY_REPORTING=1
 export MOZ_ANDROID_POCKET
 
 ac_add_options --with-branding=mobile/android/branding/nightly
 
 . "$topsrcdir/mobile/android/config/mozconfigs/common.override"
--- a/mobile/android/config/mozconfigs/android-aarch64/nightly
+++ b/mobile/android/config/mozconfigs/android-aarch64/nightly
@@ -1,16 +1,18 @@
 . "$topsrcdir/mobile/android/config/mozconfigs/common"
 
 # Android
 ac_add_options --with-android-min-sdk=21
 ac_add_options --target=aarch64-linux-android
 
 ac_add_options --with-branding=mobile/android/branding/nightly
 
+export FENNEC_NIGHTLY=1
+
 export AR="$topsrcdir/clang/bin/llvm-ar"
 export NM="$topsrcdir/clang/bin/llvm-nm"
 export RANLIB="$topsrcdir/clang/bin/llvm-ranlib"
 
 export MOZ_LTO=1
 
 export MOZILLA_OFFICIAL=1
 export MOZ_TELEMETRY_REPORTING=1
--- a/mobile/android/config/mozconfigs/android-api-16-frontend/nightly
+++ b/mobile/android/config/mozconfigs/android-api-16-frontend/nightly
@@ -21,16 +21,18 @@ ac_add_options --disable-tests
 # Warning: Before increasing the with-android-min-sdk value, please note several places in and out
 # of tree have to be changed. Otherwise, places like Treeherder or archive.mozilla.org will
 # advertise a bad API level. This may confuse people. As an example, please look at bug 1384482.
 # If you think you can't handle the whole set of changes, please reach out to the Release
 # Engineering team.
 ac_add_options --with-android-min-sdk=16
 ac_add_options --target=arm-linux-androideabi
 
+export FENNEC_NIGHTLY=1
+
 ac_add_options --with-branding=mobile/android/branding/nightly
 
 export MOZILLA_OFFICIAL=1
 export MOZ_TELEMETRY_REPORTING=1
 export MOZ_ANDROID_MMA=1
 export MOZ_ANDROID_POCKET=1
 
 . "$topsrcdir/mobile/android/config/mozconfigs/common.override"
--- a/mobile/android/config/mozconfigs/android-api-16-gradle-dependencies/nightly
+++ b/mobile/android/config/mozconfigs/android-api-16-gradle-dependencies/nightly
@@ -26,16 +26,18 @@ ac_add_options --disable-tests
 # Warning: Before increasing the with-android-min-sdk value, please note several places in and out
 # of tree have to be changed. Otherwise, places like Treeherder or archive.mozilla.org will
 # advertise a bad API level. This may confuse people. As an example, please look at bug 1384482.
 # If you think you can't handle the whole set of changes, please reach out to the Release
 # Engineering team.
 ac_add_options --with-android-min-sdk=16
 ac_add_options --target=arm-linux-androideabi
 
+export FENNEC_NIGHTLY=1
+
 ac_add_options --with-branding=mobile/android/branding/nightly
 
 export MOZILLA_OFFICIAL=1
 export MOZ_TELEMETRY_REPORTING=1
 export MOZ_ANDROID_MMA=1
 export MOZ_ANDROID_POCKET=1
 
 . "$topsrcdir/mobile/android/config/mozconfigs/common.override"
--- a/mobile/android/config/mozconfigs/android-api-16/debug
+++ b/mobile/android/config/mozconfigs/android-api-16/debug
@@ -7,14 +7,16 @@ ac_add_options --enable-debug
 # Warning: Before increasing the with-android-min-sdk value, please note several places in and out
 # of tree have to be changed. Otherwise, places like Treeherder or archive.mozilla.org will
 # advertise a bad API level. This may confuse people. As an example, please look at bug 1384482.
 # If you think you can't handle the whole set of changes, please reach out to the Release
 # Engineering team.
 ac_add_options --with-android-min-sdk=16
 ac_add_options --target=arm-linux-androideabi
 
+export FENNEC_NIGHTLY=1
+
 export MOZILLA_OFFICIAL=1
 export MOZ_TELEMETRY_REPORTING=1
 
 ac_add_options --with-branding=mobile/android/branding/nightly
 
 . "$topsrcdir/mobile/android/config/mozconfigs/common.override"
--- a/mobile/android/config/mozconfigs/android-api-16/debug-searchfox
+++ b/mobile/android/config/mozconfigs/android-api-16/debug-searchfox
@@ -7,16 +7,18 @@ ac_add_options --enable-debug
 # Warning: Before increasing the with-android-min-sdk value, please note several places in and out
 # of tree have to be changed. Otherwise, places like Treeherder or archive.mozilla.org will
 # advertise a bad API level. This may confuse people. As an example, please look at bug 1384482.
 # If you think you can't handle the whole set of changes, please reach out to the Release
 # Engineering team.
 ac_add_options --with-android-min-sdk=16
 ac_add_options --target=arm-linux-androideabi
 
+export FENNEC_NIGHTLY=1
+
 export MOZILLA_OFFICIAL=1
 export MOZ_TELEMETRY_REPORTING=1
 
 ac_add_options --with-branding=mobile/android/branding/nightly
 
 # Save rust analysis (this requires unlocking the unstable features,
 # which is done in the taskcluster task definition via RUSTC_BOOTSTRAP)
 export RUSTFLAGS="-Zsave-analysis"
--- a/mobile/android/config/mozconfigs/android-api-16/nightly
+++ b/mobile/android/config/mozconfigs/android-api-16/nightly
@@ -6,16 +6,18 @@
 # advertise a bad API level. This may confuse people. As an example, please look at bug 1384482.
 # If you think you can't handle the whole set of changes, please reach out to the Release
 # Engineering team.
 ac_add_options --with-android-min-sdk=16
 ac_add_options --target=arm-linux-androideabi
 
 ac_add_options --with-branding=mobile/android/branding/nightly
 
+export FENNEC_NIGHTLY=1
+
 export MOZILLA_OFFICIAL=1
 export MOZ_TELEMETRY_REPORTING=1
 export MOZ_ANDROID_MMA=1
 export MOZ_ANDROID_POCKET=1
 
 export AR="$topsrcdir/clang/bin/llvm-ar"
 export NM="$topsrcdir/clang/bin/llvm-nm"
 export RANLIB="$topsrcdir/clang/bin/llvm-ranlib"
--- a/mobile/android/config/mozconfigs/android-x86/debug
+++ b/mobile/android/config/mozconfigs/android-x86/debug
@@ -7,15 +7,17 @@ ac_add_options --enable-debug
 # Warning: Before increasing the with-android-min-sdk value, please note several places in and out
 # of tree have to be changed. Otherwise, places like Treeherder or archive.mozilla.org will
 # advertise a bad API level. This may confuse people. As an example, please look at bug 1384482.
 # If you think you can't handle the whole set of changes, please reach out to the Release
 # Engineering team.
 ac_add_options --target=i686-linux-android
 ac_add_options --with-android-min-sdk=16
 
+export FENNEC_NIGHTLY=1
+
 export MOZILLA_OFFICIAL=1
 export MOZ_TELEMETRY_REPORTING=1
 export MOZ_ANDROID_POCKET=1
 
 ac_add_options --with-branding=mobile/android/branding/nightly
 
 . "$topsrcdir/mobile/android/config/mozconfigs/common.override"
--- a/mobile/android/config/mozconfigs/android-x86/nightly
+++ b/mobile/android/config/mozconfigs/android-x86/nightly
@@ -5,16 +5,18 @@
 # advertise a bad API level. This may confuse people. As an example, please look at bug 1384482.
 # If you think you can't handle the whole set of changes, please reach out to the Release
 # Engineering team.
 ac_add_options --target=i686-linux-android
 ac_add_options --with-android-min-sdk=16
 
 ac_add_options --with-branding=mobile/android/branding/nightly
 
+export FENNEC_NIGHTLY=1
+
 export MOZILLA_OFFICIAL=1
 export MOZ_TELEMETRY_REPORTING=1
 export MOZ_ANDROID_POCKET=1
 
 export AR="$topsrcdir/clang/bin/llvm-ar"
 export NM="$topsrcdir/clang/bin/llvm-nm"
 export RANLIB="$topsrcdir/clang/bin/llvm-ranlib"
 
--- a/mobile/android/config/mozconfigs/android-x86_64/debug
+++ b/mobile/android/config/mozconfigs/android-x86_64/debug
@@ -2,15 +2,17 @@
 
 # Global options
 ac_add_options --enable-debug
 
 # Android
 ac_add_options --with-android-min-sdk=21
 ac_add_options --target=x86_64-linux-android
 
+export FENNEC_NIGHTLY=1
+
 export MOZILLA_OFFICIAL=1
 export MOZ_TELEMETRY_REPORTING=1
 export MOZ_ANDROID_POCKET
 
 ac_add_options --with-branding=mobile/android/branding/nightly
 
 . "$topsrcdir/mobile/android/config/mozconfigs/common.override"
--- a/mobile/android/config/mozconfigs/android-x86_64/nightly
+++ b/mobile/android/config/mozconfigs/android-x86_64/nightly
@@ -1,16 +1,18 @@
 . "$topsrcdir/mobile/android/config/mozconfigs/common"
 
 # Android
 ac_add_options --with-android-min-sdk=21
 ac_add_options --target=x86_64-linux-android
 
 ac_add_options --with-branding=mobile/android/branding/nightly
 
+export FENNEC_NIGHTLY=1
+
 export AR="$topsrcdir/clang/bin/llvm-ar"
 export NM="$topsrcdir/clang/bin/llvm-nm"
 export RANLIB="$topsrcdir/clang/bin/llvm-ranlib"
 
 export MOZ_LTO=1
 
 export MOZILLA_OFFICIAL=1
 export MOZ_TELEMETRY_REPORTING=1
--- a/mobile/android/moz.configure
+++ b/mobile/android/moz.configure
@@ -103,16 +103,24 @@ set_config('MOZ_ANDROID_ACTIVITY_STREAM'
 
 option(env='MOZ_ANDROID_MOZILLA_ONLINE',
        help='Enable MozillaOnline (Mozilla China) specific Android code',
        default=False)
 
 set_config('MOZ_ANDROID_MOZILLA_ONLINE',
            depends_if('MOZ_ANDROID_MOZILLA_ONLINE')(lambda _: True))
 
+option(env='FENNEC_NIGHTLY',
+       help='Enable experimental code for Fennec Nightly users. NOTE: This is *not* equivalent '
+            'to the NIGHTLY_BUILD flag set during configure.',
+       default=False)
+
+set_config('FENNEC_NIGHTLY', depends_if('FENNEC_NIGHTLY')(lambda _: True))
+set_define('FENNEC_NIGHTLY', depends_if('FENNEC_NIGHTLY')(lambda _: True))
+
 imply_option('MOZ_SERVICES_HEALTHREPORT', True)
 imply_option('MOZ_ANDROID_HISTORY', True)
 imply_option('--enable-small-chunk-size', True)
 
 @depends(target)
 def check_target(target):
     if target.os != 'Android':
         log.error('You must specify --target=arm-linux-androideabi (or some '
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -6023,16 +6023,24 @@ pref("dom.payments.defaults.saveAddress"
 pref("dom.payments.request.supportedRegions", "US,CA");
 
 #ifdef MOZ_ASAN_REPORTER
 pref("asanreporter.apiurl", "https://anf1.fuzzing.mozilla.org/crashproxy/submit/");
 pref("asanreporter.clientid", "unknown");
 pref("toolkit.telemetry.overrideUpdateChannel", "nightly-asan");
 #endif
 
+#if defined(XP_WIN)
+pref("layers.mlgpu.enabled", true);
+
+// Both this and the master "enabled" pref must be on to use Advanced Layers
+// on Windows 7.
+pref("layers.mlgpu.enable-on-windows7", true);
+#endif
+
 // Enable lowercased response header name
 pref("dom.xhr.lowercase_header.enabled", true);
 
 // Control whether clients.openWindow() opens windows in the same process
 // that called the API vs following our normal multi-process selection
 // algorithm.  Restricting openWindow to same process improves service worker
 // web compat in the short term.  Once the SW multi-e10s refactor is complete
 // this can be removed.
--- a/python/mozbuild/mozbuild/base.py
+++ b/python/mozbuild/mozbuild/base.py
@@ -985,16 +985,21 @@ class MachCommandConditions(object):
         """Must have a git source checkout."""
         return getattr(cls, 'substs', {}).get('VCS_CHECKOUT_TYPE') == 'git'
 
     @staticmethod
     def is_artifact_build(cls):
         """Must be an artifact build."""
         return getattr(cls, 'substs', {}).get('MOZ_ARTIFACT_BUILDS')
 
+    @staticmethod
+    def is_non_artifact_build(cls):
+        """Must not be an artifact build."""
+        return not MachCommandConditions.is_artifact_build(cls)
+
 
 class PathArgument(object):
     """Parse a filesystem path argument and transform it in various ways."""
 
     def __init__(self, arg, topsrcdir, topobjdir, cwd=None):
         self.arg = arg
         self.topsrcdir = topsrcdir
         self.topobjdir = topobjdir
--- a/security/manager/ssl/ContentSignatureVerifier.cpp
+++ b/security/manager/ssl/ContentSignatureVerifier.cpp
@@ -2,73 +2,77 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "ContentSignatureVerifier.h"
 
 #include "BRNameMatchingPolicy.h"
+#include "ScopedNSSTypes.h"
 #include "SharedCertVerifier.h"
 #include "cryptohi.h"
 #include "keyhi.h"
-#include "mozilla/Assertions.h"
 #include "mozilla/Base64.h"
-#include "mozilla/Casting.h"
 #include "mozilla/Unused.h"
 #include "nsCOMPtr.h"
-#include "nsContentUtils.h"
-#include "nsISupportsPriority.h"
-#include "nsIURI.h"
-#include "nsNSSComponent.h"
 #include "nsPromiseFlatString.h"
 #include "nsSecurityHeaderParser.h"
-#include "nsStreamUtils.h"
 #include "nsWhitespaceTokenizer.h"
 #include "mozpkix/pkix.h"
 #include "mozpkix/pkixtypes.h"
 #include "secerr.h"
 
-NS_IMPL_ISUPPORTS(ContentSignatureVerifier, nsIContentSignatureVerifier,
-                  nsIInterfaceRequestor, nsIStreamListener)
+NS_IMPL_ISUPPORTS(ContentSignatureVerifier, nsIContentSignatureVerifier)
 
 using namespace mozilla;
 using namespace mozilla::pkix;
 using namespace mozilla::psm;
 
 static LazyLogModule gCSVerifierPRLog("ContentSignatureVerifier");
 #define CSVerifier_LOG(args) MOZ_LOG(gCSVerifierPRLog, LogLevel::Debug, args)
 
 // Content-Signature prefix
-const nsLiteralCString kPREFIX = NS_LITERAL_CSTRING("Content-Signature:\x00");
+const unsigned char kPREFIX[] = {'C', 'o', 'n', 't', 'e', 'n', 't',
+                                 '-', 'S', 'i', 'g', 'n', 'a', 't',
+                                 'u', 'r', 'e', ':', 0};
 
 NS_IMETHODIMP
 ContentSignatureVerifier::VerifyContentSignature(const nsACString& aData,
                                                  const nsACString& aCSHeader,
                                                  const nsACString& aCertChain,
-                                                 const nsACString& aName,
+                                                 const nsACString& aHostname,
                                                  bool* _retval) {
   NS_ENSURE_ARG(_retval);
-  nsresult rv = CreateContext(aData, aCSHeader, aCertChain, aName);
+  *_retval = false;
+
+  // 3 is the default, non-specific, "something failed" error.
+  Telemetry::LABELS_CONTENT_SIGNATURE_VERIFICATION_ERRORS errorLabel =
+      Telemetry::LABELS_CONTENT_SIGNATURE_VERIFICATION_ERRORS::err3;
+  nsAutoCString certFingerprint;
+  uint32_t errorValue = 3;
+  nsresult rv =
+      VerifyContentSignatureInternal(aData, aCSHeader, aCertChain, aHostname,
+                                     errorLabel, certFingerprint, errorValue);
   if (NS_FAILED(rv)) {
-    *_retval = false;
-    CSVerifier_LOG(("CSVerifier: Signature verification failed\n"));
+    CSVerifier_LOG(("CSVerifier: Signature verification failed"));
+    if (certFingerprint.Length() > 0) {
+      Telemetry::AccumulateCategoricalKeyed(certFingerprint, errorLabel);
+    }
+    Accumulate(Telemetry::CONTENT_SIGNATURE_VERIFICATION_STATUS, errorValue);
     if (rv == NS_ERROR_INVALID_SIGNATURE) {
       return NS_OK;
     }
-    // This failure can have many different reasons but we don't treat it as
-    // invalid signature.
-    Accumulate(Telemetry::CONTENT_SIGNATURE_VERIFICATION_STATUS, 3);
-    Telemetry::AccumulateCategoricalKeyed(
-        mFingerprint,
-        Telemetry::LABELS_CONTENT_SIGNATURE_VERIFICATION_ERRORS::err3);
     return rv;
   }
 
-  return End(_retval);
+  *_retval = true;
+  Accumulate(Telemetry::CONTENT_SIGNATURE_VERIFICATION_STATUS, 0);
+
+  return NS_OK;
 }
 
 bool IsNewLine(char16_t c) { return c == '\n' || c == '\r'; }
 
 nsresult ReadChainIntoCertList(const nsACString& aCertChain,
                                CERTCertList* aCertList) {
   bool inBlock = false;
   bool certFound = false;
@@ -87,17 +91,17 @@ nsresult ReadChainIntoCertList(const nsA
     if (inBlock) {
       if (token.Equals(footer)) {
         inBlock = false;
         certFound = true;
         // base64 decode data, make certs, append to chain
         nsAutoCString derString;
         nsresult rv = Base64Decode(blockData, derString);
         if (NS_FAILED(rv)) {
-          CSVerifier_LOG(("CSVerifier: decoding the signature failed\n"));
+          CSVerifier_LOG(("CSVerifier: decoding the signature failed"));
           return rv;
         }
         SECItem der = {
             siBuffer,
             BitwiseCast<unsigned char*, const char*>(derString.get()),
             derString.Length(),
         };
         UniqueCERTCertificate tmpCert(CERT_NewTempCertificate(
@@ -116,27 +120,36 @@ nsresult ReadChainIntoCertList(const nsA
       }
     } else if (token.Equals(header)) {
       inBlock = true;
       blockData = "";
     }
   }
   if (inBlock || !certFound) {
     // the PEM data did not end; bad data.
-    CSVerifier_LOG(("CSVerifier: supplied chain contains bad data\n"));
+    CSVerifier_LOG(("CSVerifier: supplied chain contains bad data"));
     return NS_ERROR_FAILURE;
   }
   return NS_OK;
 }
 
-nsresult ContentSignatureVerifier::CreateContextInternal(
-    const nsACString& aData, const nsACString& aCertChain,
-    const nsACString& aName) {
-  MOZ_ASSERT(NS_IsMainThread());
-
+// Given data to verify, a content signature header value, a string representing
+// a list of PEM-encoded certificates, and a hostname to validate the
+// certificates against, this function attempts to validate the certificate
+// chain, extract the signature from the header, and verify the data using the
+// key in the end-entity certificate from the chain. Returns NS_OK if everything
+// is satisfactory and a failing nsresult otherwise. The output parameters are
+// filled with telemetry data to report in the case of failures.
+nsresult ContentSignatureVerifier::VerifyContentSignatureInternal(
+    const nsACString& aData, const nsACString& aCSHeader,
+    const nsACString& aCertChain, const nsACString& aHostname,
+    /* out */
+    Telemetry::LABELS_CONTENT_SIGNATURE_VERIFICATION_ERRORS& aErrorLabel,
+    /* out */ nsACString& aCertFingerprint,
+    /* out */ uint32_t& aErrorValue) {
   UniqueCERTCertList certCertList(CERT_NewCertList());
   if (!certCertList) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
   nsresult rv = ReadChainIntoCertList(aCertChain, certCertList.get());
   if (NS_FAILED(rv)) {
     return rv;
@@ -160,431 +173,196 @@ nsresult ContentSignatureVerifier::Creat
   // Get EE certificate fingerprint for telemetry.
   unsigned char fingerprint[SHA256_LENGTH] = {0};
   SECStatus srv = PK11_HashBuf(SEC_OID_SHA256, fingerprint, certSecItem->data,
                                AssertedCast<int32_t>(certSecItem->len));
   if (srv != SECSuccess) {
     return NS_ERROR_FAILURE;
   }
   SECItem fingerprintItem = {siBuffer, fingerprint, SHA256_LENGTH};
-  mFingerprint.Truncate();
   UniquePORTString tmpFingerprintString(CERT_Hexify(&fingerprintItem, 0));
-  mFingerprint.Append(tmpFingerprintString.get());
+  if (!tmpFingerprintString) {
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+  aCertFingerprint.Assign(tmpFingerprintString.get());
 
   // Check the signerCert chain is good
   CSTrustDomain trustDomain(certCertList);
   result = BuildCertChain(
       trustDomain, certDER, Now(), EndEntityOrCA::MustBeEndEntity,
       KeyUsage::noParticularKeyUsageRequired, KeyPurposeId::id_kp_codeSigning,
       CertPolicyId::anyPolicy, nullptr /*stapledOCSPResponse*/);
   if (result != Success) {
     // if there was a library error, return an appropriate error
     if (IsFatalError(result)) {
       return NS_ERROR_FAILURE;
     }
     // otherwise, assume the signature was invalid
     if (result == mozilla::pkix::Result::ERROR_EXPIRED_CERTIFICATE) {
-      Accumulate(Telemetry::CONTENT_SIGNATURE_VERIFICATION_STATUS, 4);
-      Telemetry::AccumulateCategoricalKeyed(
-          mFingerprint,
-          Telemetry::LABELS_CONTENT_SIGNATURE_VERIFICATION_ERRORS::err4);
+      aErrorLabel =
+          Telemetry::LABELS_CONTENT_SIGNATURE_VERIFICATION_ERRORS::err4;
+      aErrorValue = 4;
     } else if (result ==
                mozilla::pkix::Result::ERROR_NOT_YET_VALID_CERTIFICATE) {
-      Accumulate(Telemetry::CONTENT_SIGNATURE_VERIFICATION_STATUS, 5);
-      Telemetry::AccumulateCategoricalKeyed(
-          mFingerprint,
-          Telemetry::LABELS_CONTENT_SIGNATURE_VERIFICATION_ERRORS::err5);
+      aErrorLabel =
+          Telemetry::LABELS_CONTENT_SIGNATURE_VERIFICATION_ERRORS::err5;
+      aErrorValue = 5;
     } else {
       // Building cert chain failed for some other reason.
-      Accumulate(Telemetry::CONTENT_SIGNATURE_VERIFICATION_STATUS, 6);
-      Telemetry::AccumulateCategoricalKeyed(
-          mFingerprint,
-          Telemetry::LABELS_CONTENT_SIGNATURE_VERIFICATION_ERRORS::err6);
+      aErrorLabel =
+          Telemetry::LABELS_CONTENT_SIGNATURE_VERIFICATION_ERRORS::err6;
+      aErrorValue = 6;
     }
-    CSVerifier_LOG(("CSVerifier: The supplied chain is bad (%s)\n",
+    CSVerifier_LOG(("CSVerifier: The supplied chain is bad (%s)",
                     MapResultToName(result)));
     return NS_ERROR_INVALID_SIGNATURE;
   }
 
   // Check the SAN
   Input hostnameInput;
 
   result = hostnameInput.Init(
-      BitwiseCast<const uint8_t*, const char*>(aName.BeginReading()),
-      aName.Length());
+      BitwiseCast<const uint8_t*, const char*>(aHostname.BeginReading()),
+      aHostname.Length());
   if (result != Success) {
     return NS_ERROR_FAILURE;
   }
 
   BRNameMatchingPolicy nameMatchingPolicy(BRNameMatchingPolicy::Mode::Enforce);
   result = CheckCertHostname(certDER, hostnameInput, nameMatchingPolicy);
   if (result != Success) {
     // EE cert isnot valid for the given host name.
-    Accumulate(Telemetry::CONTENT_SIGNATURE_VERIFICATION_STATUS, 7);
-    Telemetry::AccumulateCategoricalKeyed(
-        mFingerprint,
-        Telemetry::LABELS_CONTENT_SIGNATURE_VERIFICATION_ERRORS::err7);
+    aErrorLabel = Telemetry::LABELS_CONTENT_SIGNATURE_VERIFICATION_ERRORS::err7;
+    aErrorValue = 7;
     return NS_ERROR_INVALID_SIGNATURE;
   }
 
-  mKey.reset(CERT_ExtractPublicKey(node->cert));
-
+  mozilla::UniqueSECKEYPublicKey key(CERT_ExtractPublicKey(node->cert));
   // in case we were not able to extract a key
-  if (!mKey) {
-    Accumulate(Telemetry::CONTENT_SIGNATURE_VERIFICATION_STATUS, 8);
-    Telemetry::AccumulateCategoricalKeyed(
-        mFingerprint,
-        Telemetry::LABELS_CONTENT_SIGNATURE_VERIFICATION_ERRORS::err8);
-    CSVerifier_LOG(("CSVerifier: unable to extract a key\n"));
+  if (!key) {
+    aErrorLabel = Telemetry::LABELS_CONTENT_SIGNATURE_VERIFICATION_ERRORS::err8;
+    aErrorValue = 8;
+    CSVerifier_LOG(("CSVerifier: unable to extract a key"));
     return NS_ERROR_INVALID_SIGNATURE;
   }
 
+  nsAutoCString signature;
+  rv = ParseContentSignatureHeader(aCSHeader, signature);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
   // Base 64 decode the signature
   nsAutoCString rawSignature;
-  rv = Base64Decode(mSignature, rawSignature);
+  rv = Base64Decode(signature, rawSignature);
   if (NS_FAILED(rv)) {
-    CSVerifier_LOG(("CSVerifier: decoding the signature failed\n"));
+    CSVerifier_LOG(("CSVerifier: decoding the signature failed"));
     return rv;
   }
 
   // get signature object
   ScopedAutoSECItem signatureItem;
   SECItem rawSignatureItem = {
       siBuffer,
       BitwiseCast<unsigned char*, const char*>(rawSignature.get()),
       rawSignature.Length(),
   };
   // We have a raw ecdsa signature r||s so we have to DER-encode it first
   // Note that we have to check rawSignatureItem->len % 2 here as
   // DSAU_EncodeDerSigWithLen asserts this
   if (rawSignatureItem.len == 0 || rawSignatureItem.len % 2 != 0) {
-    CSVerifier_LOG(("CSVerifier: signature length is bad\n"));
+    CSVerifier_LOG(("CSVerifier: signature length is bad"));
     return NS_ERROR_FAILURE;
   }
   if (DSAU_EncodeDerSigWithLen(&signatureItem, &rawSignatureItem,
                                rawSignatureItem.len) != SECSuccess) {
-    CSVerifier_LOG(("CSVerifier: encoding the signature failed\n"));
+    CSVerifier_LOG(("CSVerifier: encoding the signature failed"));
     return NS_ERROR_FAILURE;
   }
 
   // this is the only OID we support for now
   SECOidTag oid = SEC_OID_ANSIX962_ECDSA_SHA384_SIGNATURE;
-
-  mCx = UniqueVFYContext(
-      VFY_CreateContext(mKey.get(), &signatureItem, oid, nullptr));
-  if (!mCx) {
+  mozilla::UniqueVFYContext cx(
+      VFY_CreateContext(key.get(), &signatureItem, oid, nullptr));
+  if (!cx) {
     // Creating context failed.
-    Accumulate(Telemetry::CONTENT_SIGNATURE_VERIFICATION_STATUS, 9);
-    Telemetry::AccumulateCategoricalKeyed(
-        mFingerprint,
-        Telemetry::LABELS_CONTENT_SIGNATURE_VERIFICATION_ERRORS::err9);
-    return NS_ERROR_INVALID_SIGNATURE;
-  }
-
-  if (VFY_Begin(mCx.get()) != SECSuccess) {
-    // Creating context failed.
-    Accumulate(Telemetry::CONTENT_SIGNATURE_VERIFICATION_STATUS, 9);
-    Telemetry::AccumulateCategoricalKeyed(
-        mFingerprint,
-        Telemetry::LABELS_CONTENT_SIGNATURE_VERIFICATION_ERRORS::err9);
+    aErrorLabel = Telemetry::LABELS_CONTENT_SIGNATURE_VERIFICATION_ERRORS::err9;
+    aErrorValue = 9;
     return NS_ERROR_INVALID_SIGNATURE;
   }
 
-  rv = UpdateInternal(kPREFIX);
-  if (NS_FAILED(rv)) {
-    return rv;
+  if (VFY_Begin(cx.get()) != SECSuccess) {
+    // Creating context failed.
+    aErrorLabel = Telemetry::LABELS_CONTENT_SIGNATURE_VERIFICATION_ERRORS::err9;
+    aErrorValue = 9;
+    return NS_ERROR_INVALID_SIGNATURE;
   }
-  // add data if we got any
-  return UpdateInternal(aData);
-}
-
-nsresult ContentSignatureVerifier::DownloadCertChain() {
-  MOZ_ASSERT(NS_IsMainThread());
-
-  if (mCertChainURL.IsEmpty()) {
+  if (VFY_Update(cx.get(), kPREFIX, sizeof(kPREFIX)) != SECSuccess) {
+    aErrorLabel = Telemetry::LABELS_CONTENT_SIGNATURE_VERIFICATION_ERRORS::err1;
+    aErrorValue = 1;
     return NS_ERROR_INVALID_SIGNATURE;
   }
-
-  nsCOMPtr<nsIURI> certChainURI;
-  nsresult rv = NS_NewURI(getter_AddRefs(certChainURI), mCertChainURL);
-  if (NS_FAILED(rv) || !certChainURI) {
-    return rv;
-  }
-
-  // If the address is not https, fail.
-  bool isHttps = false;
-  rv = certChainURI->SchemeIs("https", &isHttps);
-  if (NS_FAILED(rv)) {
-    return rv;
-  }
-  if (!isHttps) {
+  if (VFY_Update(cx.get(),
+                 reinterpret_cast<const unsigned char*>(aData.BeginReading()),
+                 aData.Length()) != SECSuccess) {
+    aErrorLabel = Telemetry::LABELS_CONTENT_SIGNATURE_VERIFICATION_ERRORS::err1;
+    aErrorValue = 1;
     return NS_ERROR_INVALID_SIGNATURE;
   }
-
-  rv = NS_NewChannel(getter_AddRefs(mChannel), certChainURI,
-                     nsContentUtils::GetSystemPrincipal(),
-                     nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
-                     nsIContentPolicy::TYPE_OTHER);
-  if (NS_FAILED(rv)) {
-    return rv;
-  }
-
-  // we need this chain soon
-  nsCOMPtr<nsISupportsPriority> priorityChannel = do_QueryInterface(mChannel);
-  if (priorityChannel) {
-    priorityChannel->AdjustPriority(nsISupportsPriority::PRIORITY_HIGHEST);
-  }
-
-  rv = mChannel->AsyncOpen(this);
-  if (NS_FAILED(rv)) {
-    return rv;
+  if (VFY_End(cx.get()) != SECSuccess) {
+    aErrorLabel = Telemetry::LABELS_CONTENT_SIGNATURE_VERIFICATION_ERRORS::err1;
+    aErrorValue = 1;
+    return NS_ERROR_INVALID_SIGNATURE;
   }
 
   return NS_OK;
 }
 
-// Create a context for content signature verification using CreateContext
-// below. This function doesn't require a cert chain to be passed, but instead
-// aCSHeader must contain an x5u value that is then used to download the cert
-// chain.
-NS_IMETHODIMP
-ContentSignatureVerifier::CreateContextWithoutCertChain(
-    nsIContentSignatureReceiverCallback* aCallback, const nsACString& aCSHeader,
-    const nsACString& aName) {
-  MOZ_ASSERT(NS_IsMainThread());
-  MOZ_ASSERT(aCallback);
-  if (mInitialised) {
-    return NS_ERROR_ALREADY_INITIALIZED;
-  }
-  mInitialised = true;
-
-  // we get the raw content-signature header here, so first parse aCSHeader
-  nsresult rv = ParseContentSignatureHeader(aCSHeader);
-  if (NS_FAILED(rv)) {
-    return rv;
-  }
-
-  mCallback = aCallback;
-  mName.Assign(aName);
-
-  // We must download the cert chain now.
-  // This is async and blocks createContextInternal calls.
-  return DownloadCertChain();
-}
-
-// Create a context for a content signature verification.
-// It sets signature, certificate chain and name that should be used to verify
-// the data. The data parameter is the first part of the data to verify (this
-// can be the empty string).
-NS_IMETHODIMP
-ContentSignatureVerifier::CreateContext(const nsACString& aData,
-                                        const nsACString& aCSHeader,
-                                        const nsACString& aCertChain,
-                                        const nsACString& aName) {
-  if (mInitialised) {
-    return NS_ERROR_ALREADY_INITIALIZED;
-  }
-  mInitialised = true;
-  // The cert chain is given in aCertChain so we don't have to download
-  // anything.
-  mHasCertChain = true;
-
-  // we get the raw content-signature header here, so first parse aCSHeader
-  nsresult rv = ParseContentSignatureHeader(aCSHeader);
-  if (NS_FAILED(rv)) {
-    return rv;
-  }
-
-  return CreateContextInternal(aData, aCertChain, aName);
-}
+nsresult ContentSignatureVerifier::ParseContentSignatureHeader(
+    const nsACString& aContentSignatureHeader,
+    /* out */ nsCString& aSignature) {
+  // We only support p384 ecdsa.
+  NS_NAMED_LITERAL_CSTRING(signature_var, "p384ecdsa");
 
-nsresult ContentSignatureVerifier::UpdateInternal(const nsACString& aData) {
-  if (!aData.IsEmpty()) {
-    if (VFY_Update(mCx.get(),
-                   (const unsigned char*)nsPromiseFlatCString(aData).get(),
-                   aData.Length()) != SECSuccess) {
-      return NS_ERROR_INVALID_SIGNATURE;
-    }
-  }
-  return NS_OK;
-}
-
-/**
- * Add data to the context that shold be verified.
- */
-NS_IMETHODIMP
-ContentSignatureVerifier::Update(const nsACString& aData) {
-  MOZ_ASSERT(NS_IsMainThread());
-
-  // If we didn't create the context yet, bail!
-  if (!mHasCertChain) {
-    MOZ_ASSERT_UNREACHABLE(
-        "Someone called ContentSignatureVerifier::Update before "
-        "downloading the cert chain.");
-    return NS_ERROR_FAILURE;
-  }
-
-  return UpdateInternal(aData);
-}
-
-/**
- * Finish signature verification and return the result in _retval.
- */
-NS_IMETHODIMP
-ContentSignatureVerifier::End(bool* _retval) {
-  NS_ENSURE_ARG(_retval);
-  MOZ_ASSERT(NS_IsMainThread());
-
-  // If we didn't create the context yet, bail!
-  if (!mHasCertChain) {
-    Accumulate(Telemetry::CONTENT_SIGNATURE_VERIFICATION_STATUS, 2);
-    MOZ_ASSERT_UNREACHABLE(
-        "Someone called ContentSignatureVerifier::End before "
-        "downloading the cert chain.");
-    return NS_ERROR_FAILURE;
-  }
-
-  bool result = (VFY_End(mCx.get()) == SECSuccess);
-  if (result) {
-    Accumulate(Telemetry::CONTENT_SIGNATURE_VERIFICATION_STATUS, 0);
-  } else {
-    Accumulate(Telemetry::CONTENT_SIGNATURE_VERIFICATION_STATUS, 1);
-    Telemetry::AccumulateCategoricalKeyed(
-        mFingerprint,
-        Telemetry::LABELS_CONTENT_SIGNATURE_VERIFICATION_ERRORS::err1);
-  }
-  *_retval = result;
-
-  return NS_OK;
-}
-
-nsresult ContentSignatureVerifier::ParseContentSignatureHeader(
-    const nsACString& aContentSignatureHeader) {
-  MOZ_ASSERT(NS_IsMainThread());
-  // We only support p384 ecdsa according to spec
-  NS_NAMED_LITERAL_CSTRING(signature_var, "p384ecdsa");
-  NS_NAMED_LITERAL_CSTRING(certChainURL_var, "x5u");
+  aSignature.Truncate();
 
   const nsCString& flatHeader = PromiseFlatCString(aContentSignatureHeader);
   nsSecurityHeaderParser parser(flatHeader);
   nsresult rv = parser.Parse();
   if (NS_FAILED(rv)) {
-    CSVerifier_LOG(("CSVerifier: could not parse ContentSignature header\n"));
+    CSVerifier_LOG(("CSVerifier: could not parse ContentSignature header"));
     return NS_ERROR_FAILURE;
   }
   LinkedList<nsSecurityHeaderDirective>* directives = parser.GetDirectives();
 
   for (nsSecurityHeaderDirective* directive = directives->getFirst();
        directive != nullptr; directive = directive->getNext()) {
     CSVerifier_LOG(
-        ("CSVerifier: found directive %s\n", directive->mName.get()));
+        ("CSVerifier: found directive '%s'", directive->mName.get()));
     if (directive->mName.Length() == signature_var.Length() &&
         directive->mName.EqualsIgnoreCase(signature_var.get(),
                                           signature_var.Length())) {
-      if (!mSignature.IsEmpty()) {
-        CSVerifier_LOG(("CSVerifier: found two ContentSignatures\n"));
+      if (!aSignature.IsEmpty()) {
+        CSVerifier_LOG(("CSVerifier: found two ContentSignatures"));
         return NS_ERROR_INVALID_SIGNATURE;
       }
 
-      CSVerifier_LOG(("CSVerifier: found a ContentSignature directive\n"));
-      mSignature = directive->mValue;
-    }
-    if (directive->mName.Length() == certChainURL_var.Length() &&
-        directive->mName.EqualsIgnoreCase(certChainURL_var.get(),
-                                          certChainURL_var.Length())) {
-      if (!mCertChainURL.IsEmpty()) {
-        CSVerifier_LOG(("CSVerifier: found two x5u values\n"));
-        return NS_ERROR_INVALID_SIGNATURE;
-      }
-
-      CSVerifier_LOG(("CSVerifier: found an x5u directive\n"));
-      mCertChainURL = directive->mValue;
+      CSVerifier_LOG(("CSVerifier: found a ContentSignature directive"));
+      aSignature.Assign(directive->mValue);
     }
   }
 
   // we have to ensure that we found a signature at this point
-  if (mSignature.IsEmpty()) {
+  if (aSignature.IsEmpty()) {
     CSVerifier_LOG(
         ("CSVerifier: got a Content-Signature header but didn't find a "
-         "signature.\n"));
+         "signature."));
     return NS_ERROR_FAILURE;
   }
 
   // Bug 769521: We have to change b64 url to regular encoding as long as we
   // don't have a b64 url decoder. This should change soon, but in the meantime
   // we have to live with this.
-  mSignature.ReplaceChar('-', '+');
-  mSignature.ReplaceChar('_', '/');
+  aSignature.ReplaceChar('-', '+');
+  aSignature.ReplaceChar('_', '/');
 
   return NS_OK;
 }
-
-/* nsIStreamListener implementation */
-
-NS_IMETHODIMP
-ContentSignatureVerifier::OnStartRequest(nsIRequest* aRequest) {
-  MOZ_ASSERT(NS_IsMainThread());
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-ContentSignatureVerifier::OnStopRequest(nsIRequest* aRequest,
-                                        nsresult aStatus) {
-  MOZ_ASSERT(NS_IsMainThread());
-  nsCOMPtr<nsIContentSignatureReceiverCallback> callback;
-  callback.swap(mCallback);
-  nsresult rv;
-
-  // Check HTTP status code and return if it's not 200.
-  nsCOMPtr<nsIHttpChannel> http = do_QueryInterface(aRequest, &rv);
-  uint32_t httpResponseCode;
-  if (NS_FAILED(rv) || NS_FAILED(http->GetResponseStatus(&httpResponseCode)) ||
-      httpResponseCode != 200) {
-    callback->ContextCreated(false);
-    return NS_OK;
-  }
-
-  if (NS_FAILED(aStatus)) {
-    callback->ContextCreated(false);
-    return NS_OK;
-  }
-
-  nsAutoCString certChain;
-  for (uint32_t i = 0; i < mCertChain.Length(); ++i) {
-    certChain.Append(mCertChain[i]);
-  }
-
-  // We got the cert chain now. Let's create the context.
-  rv = CreateContextInternal(NS_LITERAL_CSTRING(""), certChain, mName);
-  if (NS_FAILED(rv)) {
-    callback->ContextCreated(false);
-    return NS_OK;
-  }
-
-  mHasCertChain = true;
-  callback->ContextCreated(true);
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-ContentSignatureVerifier::OnDataAvailable(nsIRequest* aRequest,
-                                          nsIInputStream* aInputStream,
-                                          uint64_t aOffset, uint32_t aCount) {
-  MOZ_ASSERT(NS_IsMainThread());
-  nsAutoCString buffer;
-
-  nsresult rv = NS_ConsumeStream(aInputStream, aCount, buffer);
-  if (NS_FAILED(rv)) {
-    return rv;
-  }
-
-  if (!mCertChain.AppendElement(buffer, fallible)) {
-    mCertChain.TruncateLength(0);
-    return NS_ERROR_OUT_OF_MEMORY;
-  }
-
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-ContentSignatureVerifier::GetInterface(const nsIID& uuid, void** result) {
-  return QueryInterface(uuid, result);
-}
--- a/security/manager/ssl/ContentSignatureVerifier.h
+++ b/security/manager/ssl/ContentSignatureVerifier.h
@@ -2,79 +2,42 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef ContentSignatureVerifier_h
 #define ContentSignatureVerifier_h
 
-#include "cert.h"
-#include "CSTrustDomain.h"
-#include "nsDirectoryServiceUtils.h"
 #include "nsIContentSignatureVerifier.h"
-#include "nsIStreamListener.h"
-#include "nsNetUtil.h"
 #include "nsString.h"
-#include "ScopedNSSTypes.h"
 
 // 45a5fe2f-c350-4b86-962d-02d5aaaa955a
 #define NS_CONTENTSIGNATUREVERIFIER_CID              \
   {                                                  \
     0x45a5fe2f, 0xc350, 0x4b86, {                    \
       0x96, 0x2d, 0x02, 0xd5, 0xaa, 0xaa, 0x95, 0x5a \
     }                                                \
   }
 #define NS_CONTENTSIGNATUREVERIFIER_CONTRACTID \
   "@mozilla.org/security/contentsignatureverifier;1"
 
-class ContentSignatureVerifier final : public nsIContentSignatureVerifier,
-                                       public nsIStreamListener,
-                                       public nsIInterfaceRequestor {
+class ContentSignatureVerifier final : public nsIContentSignatureVerifier {
  public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSICONTENTSIGNATUREVERIFIER
-  NS_DECL_NSIINTERFACEREQUESTOR
-  NS_DECL_NSISTREAMLISTENER
-  NS_DECL_NSIREQUESTOBSERVER
-
-  ContentSignatureVerifier()
-      : mCx(nullptr), mInitialised(false), mHasCertChain(false) {}
 
  private:
-  ~ContentSignatureVerifier() {}
-
-  nsresult UpdateInternal(const nsACString& aData);
-  nsresult DownloadCertChain();
-  nsresult CreateContextInternal(const nsACString& aData,
-                                 const nsACString& aCertChain,
-                                 const nsACString& aName);
-
-  nsresult ParseContentSignatureHeader(
-      const nsACString& aContentSignatureHeader);
+  ~ContentSignatureVerifier() = default;
 
-  // verifier context for incremental verifications
-  mozilla::UniqueVFYContext mCx;
-  bool mInitialised;
-  // Indicates whether we hold a cert chain to verify the signature or not.
-  // It's set by default in CreateContext or when the channel created in
-  // DownloadCertChain finished. Update and End must only be called after
-  // mHashCertChain is set.
-  bool mHasCertChain;
-  // signature to verify
-  nsCString mSignature;
-  // x5u (X.509 URL) value pointing to pem cert chain
-  nsCString mCertChainURL;
-  // the downloaded cert chain to verify against
-  FallibleTArray<nsCString> mCertChain;
-  // verification key
-  mozilla::UniqueSECKEYPublicKey mKey;
-  // name of the verifying context
-  nsCString mName;
-  // callback to notify when finished
-  nsCOMPtr<nsIContentSignatureReceiverCallback> mCallback;
-  // channel to download the cert chain
-  nsCOMPtr<nsIChannel> mChannel;
-  // EE certificate fingerprint
-  nsCString mFingerprint;
+  nsresult VerifyContentSignatureInternal(
+      const nsACString& aData, const nsACString& aCSHeader,
+      const nsACString& aCertChain, const nsACString& aHostname,
+      /* out */
+      mozilla::Telemetry::LABELS_CONTENT_SIGNATURE_VERIFICATION_ERRORS&
+          aErrorLabel,
+      /* out */ nsACString& aCertFingerprint, /* out */ uint32_t& aErrorValue);
+  nsresult ParseContentSignatureHeader(
+      const nsACString& aContentSignatureHeader,
+      /* out */ nsCString& aSignature);
 };
 
 #endif  // ContentSignatureVerifier_h
--- a/security/manager/ssl/nsIContentSignatureVerifier.idl
+++ b/security/manager/ssl/nsIContentSignatureVerifier.idl
@@ -6,116 +6,33 @@
 #include "nsISupports.idl"
 
 interface nsIContentSignatureReceiverCallback;
 
 /**
  * An interface for verifying content-signatures, inspired by
  * https://tools.ietf.org/html/draft-thomson-http-content-signature-00
  * described here https://github.com/franziskuskiefer/content-signature/tree/pki
- *
- * A new signature verifier instance should be created for each signature
- * verification - you can create these instances with do_CreateInstance.
- *
- * There are two ways to use this functionality:
- * The first allows a signature to be verified all at once by simply calling
- * verifyContentSignature.
- * The second allows for streaming; call createContext with the signature
- * information (and initial data), call update with more data as it becomes
- * available then, finally, call end to verify the signature.
  */
 [scriptable, uuid(45a5fe2f-c350-4b86-962d-02d5aaaa955a)]
 interface nsIContentSignatureVerifier : nsISupports
 {
 
   /**
    * Verifies that the data matches the data that was used to generate the
    * signature.
    *
    * @param aData                   The data to be tested.
    * @param aContentSignatureHeader The content-signature header,
    *                                url-safe base64 encoded.
    * @param aCertificateChain       The certificate chain to use for verification.
    *                                PEM encoded string.
-   * @param aName                   The (host)name for which the end entity must
+   * @param aHostname               The hostname for which the end entity must
                                     be valid.
    * @returns true if the signature matches the data and aCertificateChain is
    *          valid within aContext, false if not.
    */
   [must_use]
   boolean verifyContentSignature(in ACString aData,
                                  in ACString aContentSignatureHeader,
                                  in ACString aCertificateChain,
-                                 in ACString aName);
-
-  /**
-   * Creates a context to verify a content signature against data that is added
-   * later with update calls.
-   *
-   * @param aData                   The first chunk of data to be tested.
-   * @param aContentSignatureHeader The signature of the data, url-safe base64
-   *                                encoded.
-   * @param aCertificateChain       The certificate chain to use for
-   *                                verification. PEM encoded string.
-   * @param aName                   The (host)name for which the end entity must
-                                    be valid.
-   */
-  [must_use]
-  void createContext(in ACString aData, in ACString aContentSignatureHeader,
-                     in ACString aCertificateChain, in ACString aName);
-
-  /**
-   * Creates a context to verify a content signature against data that is added
-   * later with update calls.
-   * This does not require the caller to download the certificate chain. It's
-   * done internally.
-   * It requires the x5u parameter to be present in aContentSignatureHeader
-   *
-   * NOTE: Callers have to wait for aCallback to return before invoking anything
-   *       else. Otherwise the ContentSignatureVerifier will fail.
-   *
-   * @param aCallback               Callback that's invoked when the cert chain
-   *                                got fetched.
-   * @param aContentSignatureHeader The signature of the data, url-safe base64
-   *                                encoded, and the x5u value.
-   * @param aName                   The (host)name for which the end entity must
-                                    be valid.
-   */
-  [must_use]
-  void createContextWithoutCertChain(in nsIContentSignatureReceiverCallback aCallback,
-                                     in ACString aContentSignatureHeader,
-                                     in ACString aName);
-
-  /**
-   * Adds data to the context that was used to generate the signature.
-   *
-   * @param aData        More data to be tested.
-   */
-  [must_use]
-  void update(in ACString aData);
-
-  /**
-   * Finalises the signature and returns the result of the signature
-   * verification.
-   *
-   * @returns true if the signature matches the data added with createContext
-   *          and update, false if not.
-   */
-  [must_use]
-  boolean end();
+                                 in ACString aHostname);
 };
-
-/**
- * Callback for nsIContentSignatureVerifier.
- * { 0x1eb90707, 0xdf59, 0x48b7, \
- *   { 0x9d, 0x42, 0xd8, 0xbf, 0x63, 0x0a, 0xe7, 0x44 } }
- */
-[scriptable, uuid(1eb90707-df59-48b7-9d42-d8bf630ae744)]
-interface nsIContentSignatureReceiverCallback : nsISupports
-{
-  /**
-   * Notification callback that's called by nsIContentSignatureVerifier when
-   * the cert chain is downloaded.
-   * If download and initialisation were successful, successful is true,
-   * otherwise false. If successful is false, the verification must be aborted.
-   */
-  void contextCreated(in boolean successful);
-};
--- a/security/manager/ssl/tests/unit/test_content_signing.js
+++ b/security/manager/ssl/tests/unit/test_content_signing.js
@@ -15,17 +15,17 @@ const ONECRL_NAME = "oneCRL-signer.mozil
 const ABOUT_NEWTAB_NAME = "remotenewtab.content-signature.mozilla.org";
 var VERIFICATION_HISTOGRAM = Services.telemetry
                                      .getHistogramById("CONTENT_SIGNATURE_VERIFICATION_STATUS");
 var ERROR_HISTOGRAM = Services.telemetry
                               .getKeyedHistogramById("CONTENT_SIGNATURE_VERIFICATION_ERRORS");
 
 function getSignatureVerifier() {
   return Cc["@mozilla.org/security/contentsignatureverifier;1"]
-           .createInstance(Ci.nsIContentSignatureVerifier);
+           .getService(Ci.nsIContentSignatureVerifier);
 }
 
 function setRoot(filename) {
   let cert = constructCertFromFile(filename);
   Services.prefs.setCharPref(PREF_SIGNATURE_ROOT, cert.sha256Fingerprint);
 }
 
 function getCertHash(name) {
@@ -100,128 +100,113 @@ function run_test() {
   ok(!verifier.verifyContentSignature(DATA, GOOD_SIGNATURE, chain1, ONECRL_NAME),
      "Before the root is set, signatures should fail to verify but not throw.");
   // Check for generic chain building error.
   check_telemetry(6, 1, getCertHash("content_signing_onecrl_ee"));
 
   setRoot(TEST_DATA_DIR + "content_signing_root.pem");
 
   // Check good signatures from good certificates with the correct SAN
-  verifier = getSignatureVerifier();
   ok(verifier.verifyContentSignature(DATA, GOOD_SIGNATURE, chain1, ONECRL_NAME),
      "A OneCRL signature should verify with the OneCRL chain");
   let chain2 = remoteNewTabChain.join("\n");
-  verifier = getSignatureVerifier();
   ok(verifier.verifyContentSignature(DATA, GOOD_SIGNATURE, chain2,
                                      ABOUT_NEWTAB_NAME),
      "A newtab signature should verify with the newtab chain");
   // Check for valid signature
   check_telemetry(0, 2, getCertHash("content_signing_remote_newtab_ee"));
 
   // Check a bad signature when a good chain is provided
   chain1 = oneCRLChain.join("\n");
-  verifier = getSignatureVerifier();
   ok(!verifier.verifyContentSignature(DATA, BAD_SIGNATURE, chain1, ONECRL_NAME),
      "A bad signature should not verify");
   // Check for invalid signature
   check_telemetry(1, 1, getCertHash("content_signing_onecrl_ee"));
 
   // Check a good signature from cert with good SAN but a different key than the
   // one used to create the signature
   let badKeyChain = oneCRLBadKeyChain.join("\n");
-  verifier = getSignatureVerifier();
   ok(!verifier.verifyContentSignature(DATA, GOOD_SIGNATURE, badKeyChain,
                                       ONECRL_NAME),
      "A signature should not verify if the signing key is wrong");
   // Check for wrong key in cert.
   check_telemetry(9, 1, getCertHash("content_signing_onecrl_wrong_key_ee"));
 
   // Check a good signature from cert with good SAN but a different key than the
   // one used to create the signature (this time, an RSA key)
   let rsaKeyChain = oneCRLBadKeyChain.join("\n");
-  verifier = getSignatureVerifier();
   ok(!verifier.verifyContentSignature(DATA, GOOD_SIGNATURE, rsaKeyChain,
                                       ONECRL_NAME),
      "A signature should not verify if the signing key is wrong (RSA)");
   // Check for wrong key in cert.
   check_telemetry(9, 1, getCertHash("content_signing_onecrl_wrong_key_ee"));
 
   // Check a good signature from cert with good SAN but with chain missing root
   let missingRoot = [oneCRLChain[0], oneCRLChain[1]].join("\n");
-  verifier = getSignatureVerifier();
   ok(!verifier.verifyContentSignature(DATA, GOOD_SIGNATURE, missingRoot,
                                       ONECRL_NAME),
      "A signature should not verify if the chain is incomplete (missing root)");
   // Check for generic chain building error.
   check_telemetry(6, 1, getCertHash("content_signing_onecrl_ee"));
 
   // Check a good signature from cert with good SAN but with no path to root
   let missingInt = [oneCRLChain[0], oneCRLChain[2]].join("\n");
-  verifier = getSignatureVerifier();
   ok(!verifier.verifyContentSignature(DATA, GOOD_SIGNATURE, missingInt,
                                       ONECRL_NAME),
      "A signature should not verify if the chain is incomplete (missing int)");
   // Check for generic chain building error.
   check_telemetry(6, 1, getCertHash("content_signing_onecrl_ee"));
 
   // Check good signatures from good certificates with the wrong SANs
   chain1 = oneCRLChain.join("\n");
-  verifier = getSignatureVerifier();
   ok(!verifier.verifyContentSignature(DATA, GOOD_SIGNATURE, chain1,
                                       ABOUT_NEWTAB_NAME),
      "A OneCRL signature should not verify if we require the newtab SAN");
   // Check for invalid EE cert.
   check_telemetry(7, 1, getCertHash("content_signing_onecrl_ee"));
 
   chain2 = remoteNewTabChain.join("\n");
-  verifier = getSignatureVerifier();
   ok(!verifier.verifyContentSignature(DATA, GOOD_SIGNATURE, chain2,
                                       ONECRL_NAME),
      "A newtab signature should not verify if we require the OneCRL SAN");
   // Check for invalid EE cert.
   check_telemetry(7, 1, getCertHash("content_signing_remote_newtab_ee"));
 
   // Check good signatures with good chains with some other invalid names
-  verifier = getSignatureVerifier();
   ok(!verifier.verifyContentSignature(DATA, GOOD_SIGNATURE, chain1, ""),
      "A signature should not verify if the SANs do not match an empty name");
   // Check for invalid EE cert.
   check_telemetry(7, 1, getCertHash("content_signing_onecrl_ee"));
 
   // Test expired certificate.
   let chainExpired = expiredOneCRLChain.join("\n");
-  verifier = getSignatureVerifier();
   ok(!verifier.verifyContentSignature(DATA, GOOD_SIGNATURE, chainExpired, ""),
      "A signature should not verify if the signing certificate is expired");
   // Check for expired cert.
   check_telemetry(4, 1, getCertHash("content_signing_onecrl_ee_expired"));
 
   // Test not valid yet certificate.
   let chainNotValidYet = notValidYetOneCRLChain.join("\n");
-  verifier = getSignatureVerifier();
   ok(!verifier.verifyContentSignature(DATA, GOOD_SIGNATURE, chainNotValidYet, ""),
      "A signature should not verify if the signing certificate is not valid yet");
   // Check for not yet valid cert.
   check_telemetry(5, 1, getCertHash("content_signing_onecrl_ee_not_valid_yet"));
 
   let relatedName = "subdomain." + ONECRL_NAME;
-  verifier = getSignatureVerifier();
   ok(!verifier.verifyContentSignature(DATA, GOOD_SIGNATURE, chain1,
                                       relatedName),
      "A signature should not verify if the SANs do not match a related name");
 
   let randomName = "\xb1\x9bU\x1c\xae\xaa3\x19H\xdb\xed\xa1\xa1\xe0\x81\xfb" +
                    "\xb2\x8f\x1cP\xe5\x8b\x9c\xc2s\xd3\x1f\x8e\xbbN";
-  verifier = getSignatureVerifier();
   ok(!verifier.verifyContentSignature(DATA, GOOD_SIGNATURE, chain1, randomName),
      "A signature should not verify if the SANs do not match a random name");
 
   // check good signatures with chains that have strange or missing SANs
   chain1 = noSANChain.join("\n");
-  verifier = getSignatureVerifier();
   ok(!verifier.verifyContentSignature(DATA, GOOD_SIGNATURE, chain1,
                                       ONECRL_NAME),
      "A signature should not verify if the SANs do not match a supplied name");
 
   // Check malformed signature data
   chain1 = oneCRLChain.join("\n");
   let bad_signatures = [
     // wrong length
@@ -237,17 +222,16 @@ function run_test() {
     "p384ecdsa=XS_jiQsS5qlzQyUKaA1nAnQn_OvxhvDfKybflB8Xe5gNH1wNmPGK1qN-jpeTfK" +
     "6ob3l3gCTXrsMnOXMeht0kPP3wLfVgXbuuO135pQnsv0c-ltRMWLe56Cm4S4Z6E7WWKLPWaj" +
     "jhAcG5dZxjffP9g7tuPP4lTUJztyc4d1z_zQZakEG7R0vN7P5_CaX9MiMzP4R7nC3H4Ba6yi" +
     "yjlGvsZwJ_C5zDQzWWs95czUbMzbDScEZ_7AWnidw91jZn-fUK3xLb6m-Zb_b4GAqZ-vnXIf" +
     "LpLB1Nzal42BQZn7i4rhAldYdcVvy7rOMlsTUb5Zz6vpVW9LCT9lMJ7Sq1xbU-0g==",
     ];
   for (let badSig of bad_signatures) {
     throws(() => {
-      verifier = getSignatureVerifier();
       verifier.verifyContentSignature(DATA, badSig, chain1, ONECRL_NAME);
     }, /NS_ERROR/, `Bad or malformed signature "${badSig}" should be rejected`);
   }
 
   // Check malformed and missing certificate chain data
   let chainSuffix = [oneCRLChain[1], oneCRLChain[2]].join("\n");
   let badChains = [
     // no data
@@ -271,67 +255,21 @@ function run_test() {
     // ensure we test each bad section on its own...
     badChains.push(badSection);
     // ... and as part of a chain with good certificates
     badChains.push(badSection + "\n" + chainSuffix);
   }
 
   for (let badChain of badChains) {
     throws(() => {
-      verifier = getSignatureVerifier();
       verifier.verifyContentSignature(DATA, GOOD_SIGNATURE, badChain,
                                       ONECRL_NAME);
     }, /NS_ERROR/, `Bad chain data starting "${badChain.substring(0, 80)}" ` +
                    "should be rejected");
   }
 
-  // Check the streaming interface works OK when a good chain / data
-  // combination is provided
-  chain1 = oneCRLChain.join("\n");
-  verifier = getSignatureVerifier();
-  verifier.createContext("", GOOD_SIGNATURE, chain1, ONECRL_NAME);
-  verifier.update(DATA);
-  ok(verifier.end(),
-     "A good signature should verify using the stream interface");
-
-  // Check that the streaming interface works with multiple update calls
-  verifier = getSignatureVerifier();
-  verifier.createContext("", GOOD_SIGNATURE, chain1, ONECRL_NAME);
-  for (let c of DATA) {
-    verifier.update(c);
-  }
-  ok(verifier.end(),
-     "A good signature should verify using multiple updates");
-
-  // Check that the streaming interface works with multiple update calls and
-  // some data provided in CreateContext
-  verifier = getSignatureVerifier();
-  let start = DATA.substring(0, 5);
-  let rest = DATA.substring(start.length);
-  verifier.createContext(start, GOOD_SIGNATURE, chain1, ONECRL_NAME);
-  for (let c of rest) {
-    verifier.update(c);
-  }
-  ok(verifier.end(),
-     "A good signature should verify using data in CreateContext and updates");
-
-  // Check that a bad chain / data combination fails
-  verifier = getSignatureVerifier();
-  verifier.createContext("", GOOD_SIGNATURE, chain1, ONECRL_NAME);
-  ok(!verifier.end(),
-     "A bad signature should fail using the stream interface");
-
-  // Check that re-creating a context throws ...
-  verifier = getSignatureVerifier();
-  verifier.createContext("", GOOD_SIGNATURE, chain1, ONECRL_NAME);
-
-  // ... firstly, creating a context explicitly
-  throws(() => {
-    verifier.createContext(DATA, GOOD_SIGNATURE, chain1, ONECRL_NAME);
-  }, /NS_ERROR/, "Ensure a verifier cannot be re-used with createContext");
-
-  // ... secondly, by calling verifyContentSignature
-  throws(() => {
-    verifier.verifyContentSignature(DATA, GOOD_SIGNATURE, chain1, ONECRL_NAME);
-  }, /NS_ERROR/, "Ensure a verifier cannot be re-used with verifyContentSignature");
-
-  run_next_test();
+  ok(!verifier.verifyContentSignature(DATA + "appended data", GOOD_SIGNATURE, chain1, ONECRL_NAME),
+     "A good signature should not verify if the data is tampered with (append)");
+  ok(!verifier.verifyContentSignature("prefixed data" + DATA, GOOD_SIGNATURE, chain1, ONECRL_NAME),
+     "A good signature should not verify if the data is tampered with (prefix)");
+  ok(!verifier.verifyContentSignature(DATA.replace(/e/g, "i"), GOOD_SIGNATURE, chain1, ONECRL_NAME),
+     "A good signature should not verify if the data is tampered with (modify)");
 }
--- a/servo/components/malloc_size_of/lib.rs
+++ b/servo/components/malloc_size_of/lib.rs
@@ -744,16 +744,17 @@ where
             Component::Combinator(..) |
             Component::ExplicitAnyNamespace |
             Component::ExplicitNoNamespace |
             Component::DefaultNamespace(..) |
             Component::Namespace(..) |
             Component::ExplicitUniversalType |
             Component::LocalName(..) |
             Component::ID(..) |
+            Component::Part(..) |
             Component::Class(..) |
             Component::AttributeInNoNamespaceExists { .. } |
             Component::AttributeInNoNamespace { .. } |
             Component::FirstChild |
             Component::LastChild |
             Component::OnlyChild |
             Component::Root |
             Component::Empty |
--- a/servo/components/selectors/builder.rs
+++ b/servo/components/selectors/builder.rs
@@ -265,16 +265,17 @@ where
         specificity: &mut Specificity,
     ) where
         Impl: SelectorImpl,
     {
         match *simple_selector {
             Component::Combinator(..) => {
                 unreachable!("Found combinator in simple selectors vector?");
             },
+            Component::Part(..) |
             Component::PseudoElement(..) | Component::LocalName(..) => {
                 specificity.element_selectors += 1
             },
             Component::Slotted(ref selector) => {
                 specificity.element_selectors += 1;
                 // Note that due to the way ::slotted works we only compete with
                 // other ::slotted rules, so the above rule doesn't really
                 // matter, but we do it still for consistency with other
--- a/servo/components/selectors/matching.rs
+++ b/servo/components/selectors/matching.rs
@@ -445,16 +445,17 @@ where
             // are in a shadow tree of the host, and the next selector will only
             // match if the selector is a featureless :host selector.
             if !selector.clone().is_featureless_host_selector() {
                 return None;
             }
 
             element.containing_shadow_host()
         },
+        Combinator::Part => element.containing_shadow_host(),
         Combinator::SlotAssignment => {
             debug_assert!(
                 context.current_host.is_some(),
                 "Should not be trying to match slotted rules in a non-shadow-tree context"
             );
             debug_assert!(element
                 .assigned_slot()
                 .map_or(true, |s| s.is_html_slot_element()));
@@ -512,16 +513,17 @@ where
 
     let candidate_not_found = match combinator {
         Combinator::NextSibling | Combinator::LaterSibling => {
             SelectorMatchingResult::NotMatchedAndRestartFromClosestDescendant
         },
         Combinator::Child |
         Combinator::Descendant |
         Combinator::SlotAssignment |
+        Combinator::Part |
         Combinator::PseudoElement => SelectorMatchingResult::NotMatchedGlobally,
     };
 
     let mut next_element =
         next_element_for_combinator(element, combinator, &selector_iter, &context);
 
     // Stop matching :visited as soon as we find a link, or a combinator for
     // something that isn't an ancestor.
@@ -666,16 +668,17 @@ fn matches_simple_selector<E, F>(
 where
     E: Element,
     F: FnMut(&E, ElementSelectorFlags),
 {
     debug_assert!(context.shared.is_nested() || !context.shared.in_negation());
 
     match *selector {
         Component::Combinator(_) => unreachable!(),
+        Component::Part(ref part) => element.is_part(part),
         Component::Slotted(ref selector) => {
             // <slots> are never flattened tree slottables.
             !element.is_html_slot_element() &&
                 element.assigned_slot().is_some() &&
                 context.shared.nest(|context| {
                     matches_complex_selector(selector.iter(), element, context, flags_setter)
                 })
         },
--- a/servo/components/selectors/parser.rs
+++ b/servo/components/selectors/parser.rs
@@ -63,48 +63,62 @@ fn to_ascii_lowercase(s: &str) -> Cow<st
 }
 
 bitflags! {
     /// Flags that indicate at which point of parsing a selector are we.
     struct SelectorParsingState: u8 {
         /// Whether we're inside a negation. If we're inside a negation, we're
         /// not allowed to add another negation or such, for example.
         const INSIDE_NEGATION = 1 << 0;
-        /// Whether we've parsed an ::slotted() pseudo-element already.
+        /// Whether we've parsed a ::slotted() pseudo-element already.
         ///
         /// If so, then we can only parse a subset of pseudo-elements, and
         /// whatever comes after them if so.
         const AFTER_SLOTTED = 1 << 1;
+        /// Whether we've parsed a ::part() pseudo-element already.
+        ///
+        /// If so, then we can only parse a subset of pseudo-elements, and
+        /// whatever comes after them if so.
+        const AFTER_PART = 1 << 2;
         /// Whether we've parsed a pseudo-element (as in, an
-        /// `Impl::PseudoElement` thus not accounting for `::slotted`) already.
+        /// `Impl::PseudoElement` thus not accounting for `::slotted` or
+        /// `::part`) already.
         ///
         /// If so, then other pseudo-elements and most other selectors are
         /// disallowed.
-        const AFTER_PSEUDO_ELEMENT = 1 << 2;
+        const AFTER_PSEUDO_ELEMENT = 1 << 3;
         /// Whether we've parsed a non-stateful pseudo-element (again, as-in
         /// `Impl::PseudoElement`) already. If so, then other pseudo-classes are
         /// disallowed. If this flag is set, `AFTER_PSEUDO_ELEMENT` must be set
         /// as well.
-        const AFTER_NON_STATEFUL_PSEUDO_ELEMENT = 1 << 3;
+        const AFTER_NON_STATEFUL_PSEUDO_ELEMENT = 1 << 4;
         /// Whether we are after any of the pseudo-like things.
-        const AFTER_PSEUDO = Self::AFTER_SLOTTED.bits | Self::AFTER_PSEUDO_ELEMENT.bits;
+        const AFTER_PSEUDO = Self::AFTER_PART.bits | Self::AFTER_SLOTTED.bits | Self::AFTER_PSEUDO_ELEMENT.bits;
     }
 }
 
 impl SelectorParsingState {
     #[inline]
     fn allows_functional_pseudo_classes(self) -> bool {
         !self.intersects(SelectorParsingState::AFTER_PSEUDO)
     }
 
     #[inline]
     fn allows_slotted(self) -> bool {
         !self.intersects(SelectorParsingState::AFTER_PSEUDO)
     }
 
+    // TODO(emilio): Should we allow other ::part()s after ::part()?
+    //
+    // See https://github.com/w3c/csswg-drafts/issues/3841
+    #[inline]
+    fn allows_part(self) -> bool {
+        !self.intersects(SelectorParsingState::AFTER_PSEUDO)
+    }
+
     #[inline]
     fn allows_non_functional_pseudo_classes(self) -> bool {
         !self.intersects(SelectorParsingState::AFTER_SLOTTED | SelectorParsingState::AFTER_NON_STATEFUL_PSEUDO_ELEMENT)
     }
 
     #[inline]
     fn allows_tree_structural_pseudo_classes(self) -> bool {
         !self.intersects(SelectorParsingState::AFTER_PSEUDO)
@@ -151,16 +165,17 @@ macro_rules! with_all_bounds {
         /// NB: We need Clone so that we can derive(Clone) on struct with that
         /// are parameterized on SelectorImpl. See
         /// <https://github.com/rust-lang/rust/issues/26925>
         pub trait SelectorImpl: Clone + Debug + Sized + 'static {
             type ExtraMatchingData: Sized + Default + 'static;
             type AttrValue: $($InSelector)*;
             type Identifier: $($InSelector)*;
             type ClassName: $($InSelector)*;
+            type PartName: $($InSelector)*;
             type LocalName: $($InSelector)* + Borrow<Self::BorrowedLocalName>;
             type NamespaceUrl: $($CommonBounds)* + Default + Borrow<Self::BorrowedNamespaceUrl>;
             type NamespacePrefix: $($InSelector)* + Default;
             type BorrowedNamespaceUrl: ?Sized + Eq;
             type BorrowedLocalName: ?Sized + Eq;
 
             /// non tree-structural pseudo-classes
             /// (see: https://drafts.csswg.org/selectors/#structural-pseudos)
@@ -191,16 +206,21 @@ pub trait Parser<'i> {
     type Impl: SelectorImpl;
     type Error: 'i + From<SelectorParseErrorKind<'i>>;
 
     /// Whether to parse the `::slotted()` pseudo-element.
     fn parse_slotted(&self) -> bool {
         false
     }
 
+    /// Whether to parse the `::part()` pseudo-element.
+    fn parse_part(&self) -> bool {
+        false
+    }
+
     /// Whether to parse the `:host` pseudo-class.
     fn parse_host(&self) -> bool {
         false
     }
 
     /// This function can return an "Err" pseudo-element in order to support CSS2.1
     /// pseudo-elements.
     fn parse_non_ts_pseudo_class(
@@ -731,17 +751,18 @@ impl<'a, Impl: 'a + SelectorImpl> Select
     pub fn next_sequence(&mut self) -> Option<Combinator> {
         self.next_combinator.take()
     }
 
     /// Whether this selector is a featureless host selector, with no
     /// combinators to the left.
     #[inline]
     pub(crate) fn is_featureless_host_selector(&mut self) -> bool {
-        self.all(|component| matches!(*component, Component::Host(..))) &&
+        self.selector_length() > 0 &&
+            self.all(|component| matches!(*component, Component::Host(..))) &&
             self.next_sequence().is_none()
     }
 
     /// Returns remaining count of the simple selectors and combinators in the Selector.
     #[inline]
     pub fn selector_length(&self) -> usize {
         self.iter.len()
     }
@@ -835,16 +856,19 @@ pub enum Combinator {
     /// It serializes as the empty string, and acts effectively as a child
     /// combinator in most cases.  If we ever actually start using a child
     /// combinator for this, we will need to fix up the way hashes are computed
     /// for revalidation selectors.
     PseudoElement,
     /// Another combinator used for ::slotted(), which represent the jump from
     /// a node to its assigned slot.
     SlotAssignment,
+    /// Another combinator used for `::part()`, which represents the jump from
+    /// the part to the containing shadow host.
+    Part,
 }
 
 impl Combinator {
     /// Returns true if this combinator is a child or descendant combinator.
     #[inline]
     pub fn is_ancestor(&self) -> bool {
         matches!(
             *self,
@@ -928,29 +952,31 @@ pub enum Component<Impl: SelectorImpl> {
     NthChild(i32, i32),
     NthLastChild(i32, i32),
     NthOfType(i32, i32),
     NthLastOfType(i32, i32),
     FirstOfType,
     LastOfType,
     OnlyOfType,
     NonTSPseudoClass(#[shmem(field_bound)] Impl::NonTSPseudoClass),
-    /// The ::slotted() pseudo-element (which isn't actually a pseudo-element,
-    /// and probably should be a pseudo-class):
+    /// The ::slotted() pseudo-element:
     ///
     /// https://drafts.csswg.org/css-scoping/#slotted-pseudo
     ///
     /// The selector here is a compound selector, that is, no combinators.
     ///
     /// NOTE(emilio): This should support a list of selectors, but as of this
     /// writing no other browser does, and that allows them to put ::slotted()
     /// in the rule hash, so we do that too.
     ///
     /// See https://github.com/w3c/csswg-drafts/issues/2158
     Slotted(Selector<Impl>),
+    /// The `::part` pseudo-element.
+    ///   https://drafts.csswg.org/css-shadow-parts/#part
+    Part(#[shmem(field_bound)] Impl::PartName),
     /// The `:host` pseudo-class:
     ///
     /// https://drafts.csswg.org/css-scoping/#host-selector
     ///
     /// NOTE(emilio): This should support a list of selectors, but as of this
     /// writing no other browser does, and that allows them to put :host()
     /// in the rule hash, so we do that too.
     ///
@@ -1190,17 +1216,18 @@ impl ToCss for Combinator {
     where
         W: fmt::Write,
     {
         match *self {
             Combinator::Child => dest.write_str(" > "),
             Combinator::Descendant => dest.write_str(" "),
             Combinator::NextSibling => dest.write_str(" + "),
             Combinator::LaterSibling => dest.write_str(" ~ "),
-            Combinator::PseudoElement => Ok(()),
+            Combinator::PseudoElement |
+            Combinator::Part |
             Combinator::SlotAssignment => Ok(()),
         }
     }
 }
 
 impl<Impl: SelectorImpl> ToCss for Component<Impl> {
     fn to_css<W>(&self, dest: &mut W) -> fmt::Result
     where
@@ -1230,16 +1257,21 @@ impl<Impl: SelectorImpl> ToCss for Compo
 
         match *self {
             Combinator(ref c) => c.to_css(dest),
             Slotted(ref selector) => {
                 dest.write_str("::slotted(")?;
                 selector.to_css(dest)?;
                 dest.write_char(')')
             },
+            Part(ref part_name) => {
+                dest.write_str("::part(")?;
+                display_to_css_identifier(part_name, dest)?;
+                dest.write_char(')')
+            },
             PseudoElement(ref p) => p.to_css(dest),
             ID(ref s) => {
                 dest.write_char('#')?;
                 display_to_css_identifier(s, dest)
             },
             Class(ref s) => {
                 dest.write_char('.')?;
                 display_to_css_identifier(s, dest)
@@ -1401,35 +1433,36 @@ fn parse_selector<'i, 't, P, Impl>(
     input: &mut CssParser<'i, 't>,
 ) -> Result<Selector<Impl>, ParseError<'i, P::Error>>
 where
     P: Parser<'i, Impl = Impl>,
     Impl: SelectorImpl,
 {
     let mut builder = SelectorBuilder::default();
 
-    let mut has_pseudo_element;
-    let mut slotted;
+    let mut has_pseudo_element = false;
+    let mut slotted = false;
     'outer_loop: loop {
         // Parse a sequence of simple selectors.
-        match parse_compound_selector(parser, input, &mut builder)? {
-            Some(state) => {
-                has_pseudo_element = state.intersects(SelectorParsingState::AFTER_PSEUDO_ELEMENT);
-                slotted = state.intersects(SelectorParsingState::AFTER_SLOTTED);
-            },
+        let state = match parse_compound_selector(parser, input, &mut builder)? {
+            Some(state) => state,
             None => {
                 return Err(input.new_custom_error(if builder.has_combinators() {
                     SelectorParseErrorKind::DanglingCombinator
                 } else {
                     SelectorParseErrorKind::EmptySelector
                 }));
             },
         };
 
-        if has_pseudo_element || slotted {
+        if state.intersects(SelectorParsingState::AFTER_PSEUDO) {
+            has_pseudo_element = state.intersects(SelectorParsingState::AFTER_PSEUDO_ELEMENT);
+            slotted = state.intersects(SelectorParsingState::AFTER_SLOTTED);
+            let part = state.intersects(SelectorParsingState::AFTER_PART);
+            debug_assert!(has_pseudo_element || slotted || part);
             break;
         }
 
         // Parse a combinator.
         let combinator;
         let mut any_whitespace = false;
         loop {
             let before_this_token = input.state();
@@ -1457,16 +1490,18 @@ where
                         break 'outer_loop;
                     }
                 },
             }
         }
         builder.push_combinator(combinator);
     }
 
+    // TODO(emilio): We'll have to flag part() somehow as well, but we need more
+    // bits!
     Ok(Selector(builder.build(has_pseudo_element, slotted)))
 }
 
 impl<Impl: SelectorImpl> Selector<Impl> {
     /// Parse a selector, without any pseudo-element.
     #[inline]
     pub fn parse<'i, 't, P>(
         parser: &P,
@@ -1547,16 +1582,17 @@ where
     }
 }
 
 #[derive(Debug)]
 enum SimpleSelectorParseResult<Impl: SelectorImpl> {
     SimpleSelector(Component<Impl>),
     PseudoElement(Impl::PseudoElement),
     SlottedPseudo(Selector<Impl>),
+    PartPseudo(Impl::PartName),
 }
 
 #[derive(Debug)]
 enum QNamePrefix<Impl: SelectorImpl> {
     ImplicitNoNamespace,                          // `foo` in attr selectors
     ImplicitAnyNamespace,                         // `foo` in type selectors, without a default ns
     ImplicitDefaultNamespace(Impl::NamespaceUrl), // `foo` in type selectors, with a default ns
     ExplicitNoNamespace,                          // `|foo`
@@ -1893,16 +1929,17 @@ where
         match parse_one_simple_selector(parser, input, SelectorParsingState::INSIDE_NEGATION)? {
             Some(SimpleSelectorParseResult::SimpleSelector(s)) => {
                 sequence.push(s);
             },
             None => {
                 return Err(input.new_custom_error(SelectorParseErrorKind::EmptyNegation));
             },
             Some(SimpleSelectorParseResult::PseudoElement(_)) |
+            Some(SimpleSelectorParseResult::PartPseudo(_)) |
             Some(SimpleSelectorParseResult::SlottedPseudo(_)) => {
                 let e = SelectorParseErrorKind::NonSimpleSelectorInNegation;
                 return Err(input.new_custom_error(e));
             },
         }
     }
 
     // Success.
@@ -1949,16 +1986,21 @@ where
             };
 
         empty = false;
 
         match parse_result {
             SimpleSelectorParseResult::SimpleSelector(s) => {
                 builder.push_simple_selector(s);
             },
+            SimpleSelectorParseResult::PartPseudo(part_name) => {
+                state.insert(SelectorParsingState::AFTER_PART);
+                builder.push_combinator(Combinator::Part);
+                builder.push_simple_selector(Component::Part(part_name));
+            },
             SimpleSelectorParseResult::SlottedPseudo(selector) => {
                 state.insert(SelectorParsingState::AFTER_SLOTTED);
                 if !builder.is_empty() {
                     builder.push_combinator(Combinator::SlotAssignment);
                 }
                 builder.push_simple_selector(Component::Slotted(selector));
             },
             SimpleSelectorParseResult::PseudoElement(p) => {
@@ -2109,16 +2151,25 @@ where
             };
             let is_pseudo_element =
                 !is_single_colon || is_css2_pseudo_element(&name);
             if is_pseudo_element {
                 if state.intersects(SelectorParsingState::AFTER_PSEUDO_ELEMENT) {
                     return Err(input.new_custom_error(SelectorParseErrorKind::InvalidState));
                 }
                 let pseudo_element = if is_functional {
+                    if P::parse_part(parser) && name.eq_ignore_ascii_case("part") {
+                        if !state.allows_part() {
+                            return Err(input.new_custom_error(SelectorParseErrorKind::InvalidState));
+                        }
+                        let name = input.parse_nested_block(|input| {
+                            Ok(input.expect_ident()?.as_ref().into())
+                        })?;
+                        return Ok(Some(SimpleSelectorParseResult::PartPseudo(name)));
+                    }
                     if P::parse_slotted(parser) && name.eq_ignore_ascii_case("slotted") {
                         if !state.allows_slotted() {
                             return Err(input.new_custom_error(SelectorParseErrorKind::InvalidState));
                         }
                         let selector = input.parse_nested_block(|input| {
                             parse_inner_compound_selector(parser, input)
                         })?;
                         return Ok(Some(SimpleSelectorParseResult::SlottedPseudo(selector)));
@@ -2292,16 +2343,17 @@ pub mod tests {
         }
     }
 
     impl SelectorImpl for DummySelectorImpl {
         type ExtraMatchingData = ();
         type AttrValue = DummyAtom;
         type Identifier = DummyAtom;
         type ClassName = DummyAtom;
+        type PartName = DummyAtom;
         type LocalName = DummyAtom;
         type NamespaceUrl = DummyAtom;
         type NamespacePrefix = DummyAtom;
         type BorrowedLocalName = DummyAtom;
         type BorrowedNamespaceUrl = DummyAtom;
         type NonTSPseudoClass = PseudoClass;
         type PseudoElement = PseudoElement;
     }
@@ -2330,16 +2382,20 @@ pub mod tests {
     impl<'i> Parser<'i> for DummyParser {
         type Impl = DummySelectorImpl;
         type Error = SelectorParseErrorKind<'i>;
 
         fn parse_slotted(&self) -> bool {
             true
         }
 
+        fn parse_part(&self) -> bool {
+            true
+        }
+
         fn parse_non_ts_pseudo_class(
             &self,
             location: SourceLocation,
             name: CowRcStr<'i>,
         ) -> Result<PseudoClass, SelectorParseError<'i>> {
             match_ignore_ascii_case! { &name,
                 "hover" => return Ok(PseudoClass::Hover),
                 "active" => return Ok(PseudoClass::Active),
@@ -2904,16 +2960,24 @@ pub mod tests {
             )]))
         );
 
         assert!(parse("::slotted()").is_err());
         assert!(parse("::slotted(div)").is_ok());
         assert!(parse("::slotted(div).foo").is_err());
         assert!(parse("::slotted(div + bar)").is_err());
         assert!(parse("::slotted(div) + foo").is_err());
+
+        assert!(parse("::part()").is_err());
+        assert!(parse("::part(42)").is_err());
+        // Though note https://github.com/w3c/csswg-drafts/issues/3502
+        assert!(parse("::part(foo bar)").is_err());
+        assert!(parse("::part(foo):hover").is_ok());
+        assert!(parse("::part(foo) + bar").is_err());
+
         assert!(parse("div ::slotted(div)").is_ok());
         assert!(parse("div + slot::slotted(div)").is_ok());
         assert!(parse("div + slot::slotted(div.foo)").is_ok());
         assert!(parse("slot::slotted(div,foo)::first-line").is_err());
         assert!(parse("::slotted(div)::before").is_ok());
         assert!(parse("slot::slotted(div,foo)").is_err());
     }
 
--- a/servo/components/selectors/tree.rs
+++ b/servo/components/selectors/tree.rs
@@ -105,16 +105,21 @@ pub trait Element: Sized + Clone + Debug
     ) -> bool;
 
     fn has_class(
         &self,
         name: &<Self::Impl as SelectorImpl>::ClassName,
         case_sensitivity: CaseSensitivity,
     ) -> bool;
 
+    fn is_part(
+        &self,
+        name: &<Self::Impl as SelectorImpl>::PartName,
+    ) -> bool;
+
     /// Returns whether this element matches `:empty`.
     ///
     /// That is, whether it does not contain any child element or any non-zero-length text node.
     /// See http://dev.w3.org/csswg/selectors-3/#empty-pseudo
     fn is_empty(&self) -> bool;
 
     /// Returns whether this element matches `:root`,
     /// i.e. whether it is the root element of a document.
--- a/servo/components/style/gecko/conversions.rs
+++ b/servo/components/style/gecko/conversions.rs
@@ -17,17 +17,16 @@ use crate::gecko_bindings::structs::{nsS
 use crate::gecko_bindings::sugar::ns_style_coord::{CoordData, CoordDataMut, CoordDataValue};
 use crate::stylesheets::RulesMutateError;
 use crate::values::computed::image::LineDirection;
 use crate::values::computed::transform::Matrix3D;
 use crate::values::computed::url::ComputedImageUrl;
 use crate::values::computed::{Angle, Gradient, Image};
 use crate::values::computed::{Integer, LengthPercentage};
 use crate::values::computed::{Length, Percentage, TextAlign};
-use crate::values::generics::box_::VerticalAlign;
 use crate::values::generics::grid::{TrackListValue, TrackSize};
 use crate::values::generics::image::{CompatMode, Image as GenericImage};
 use crate::values::generics::rect::Rect;
 use crate::Zero;
 use app_units::Au;
 use std::f32::consts::PI;
 use style_traits::values::specified::AllowedNumericType;
 
@@ -870,36 +869,16 @@ where
             T::from_gecko_style_coord(&sides.data_at(0)).expect("coord[0] cound not convert"),
             T::from_gecko_style_coord(&sides.data_at(1)).expect("coord[1] cound not convert"),
             T::from_gecko_style_coord(&sides.data_at(2)).expect("coord[2] cound not convert"),
             T::from_gecko_style_coord(&sides.data_at(3)).expect("coord[3] cound not convert"),
         ))
     }
 }
 
-impl<L> VerticalAlign<L> {
-    /// Converts an enumerated value coming from Gecko to a `VerticalAlign<L>`.
-    pub fn from_gecko_keyword(value: u32) -> Self {
-        match value {
-            structs::NS_STYLE_VERTICAL_ALIGN_BASELINE => VerticalAlign::Baseline,
-            structs::NS_STYLE_VERTICAL_ALIGN_SUB => VerticalAlign::Sub,
-            structs::NS_STYLE_VERTICAL_ALIGN_SUPER => VerticalAlign::Super,
-            structs::NS_STYLE_VERTICAL_ALIGN_TOP => VerticalAlign::Top,
-            structs::NS_STYLE_VERTICAL_ALIGN_TEXT_TOP => VerticalAlign::TextTop,
-            structs::NS_STYLE_VERTICAL_ALIGN_MIDDLE => VerticalAlign::Middle,
-            structs::NS_STYLE_VERTICAL_ALIGN_BOTTOM => VerticalAlign::Bottom,
-            structs::NS_STYLE_VERTICAL_ALIGN_TEXT_BOTTOM => VerticalAlign::TextBottom,
-            structs::NS_STYLE_VERTICAL_ALIGN_MIDDLE_WITH_BASELINE => {
-                VerticalAlign::MozMiddleWithBaseline
-            },
-            _ => panic!("unexpected enumerated value for vertical-align"),
-        }
-    }
-}
-
 impl TextAlign {
     /// Obtain a specified value from a Gecko keyword value
     ///
     /// Intended for use with presentation attributes, not style structs
     pub fn from_gecko_keyword(kw: u32) -> Self {
         match kw {
             structs::NS_STYLE_TEXT_ALIGN_LEFT => TextAlign::Left,
             structs::NS_STYLE_TEXT_ALIGN_RIGHT => TextAlign::Right,
--- a/servo/components/style/gecko/selector_parser.rs
+++ b/servo/components/style/gecko/selector_parser.rs
@@ -284,16 +284,17 @@ impl ::selectors::parser::NonTSPseudoCla
 #[derive(Clone, Debug, Eq, PartialEq)]
 pub struct SelectorImpl;
 
 impl ::selectors::SelectorImpl for SelectorImpl {
     type ExtraMatchingData = InvalidationMatchingData;
     type AttrValue = Atom;
     type Identifier = Atom;
     type ClassName = Atom;
+    type PartName = Atom;
     type LocalName = Atom;
     type NamespacePrefix = Atom;
     type NamespaceUrl = Namespace;
     type BorrowedNamespaceUrl = WeakNamespace;
     type BorrowedLocalName = WeakAtom;
 
     type PseudoElement = PseudoElement;
     type NonTSPseudoClass = NonTSPseudoClass;
--- a/servo/components/style/gecko/snapshot.rs
+++ b/servo/components/style/gecko/snapshot.rs
@@ -179,22 +179,32 @@ impl ElementSnapshot for GeckoElementSna
         if !self.has_any(Flags::Id) {
             return None;
         }
 
         snapshot_helpers::get_id(&*self.mAttrs)
     }
 
     #[inline]
+    fn is_part(&self, name: &Atom) -> bool {
+        let attr = match snapshot_helpers::find_attr(&*self.mAttrs, &atom!("part")) {
+            Some(attr) => attr,
+            None => return false,
+        };
+
+        snapshot_helpers::has_class_or_part(name, CaseSensitivity::CaseSensitive, attr)
+    }
+
+    #[inline]
     fn has_class(&self, name: &Atom, case_sensitivity: CaseSensitivity) -> bool {
         if !self.has_any(Flags::MaybeClass) {
             return false;
         }
 
-        snapshot_helpers::has_class(name, case_sensitivity, &self.mClass)
+        snapshot_helpers::has_class_or_part(name, case_sensitivity, &self.mClass)
     }
 
     #[inline]
     fn each_class<F>(&self, callback: F)
     where
         F: FnMut(&Atom),
     {
         if !self.has_any(Flags::MaybeClass) {
--- a/servo/components/style/gecko/snapshot_helpers.rs
+++ b/servo/components/style/gecko/snapshot_helpers.rs
@@ -24,17 +24,17 @@ fn base_type(attr: &structs::nsAttrValue
 }
 
 #[inline(always)]
 unsafe fn ptr<T>(attr: &structs::nsAttrValue) -> *const T {
     (attr.mBits & !structs::NS_ATTRVALUE_BASETYPE_MASK) as *const T
 }
 
 #[inline(always)]
-unsafe fn get_class_from_attr(attr: &structs::nsAttrValue) -> Class {
+unsafe fn get_class_or_part_from_attr(attr: &structs::nsAttrValue) -> Class {
     debug_assert!(bindings::Gecko_AssertClassAttrValueIsSane(attr));
     let base_type = base_type(attr);
     if base_type == structs::nsAttrValue_ValueBaseType_eStringBase {
         return Class::None;
     }
     if base_type == structs::nsAttrValue_ValueBaseType_eAtomBase {
         return Class::One(ptr::<nsAtom>(attr));
     }
@@ -77,25 +77,25 @@ pub fn find_attr<'a>(
 }
 
 /// Finds the id attribute from a list of attributes.
 #[inline(always)]
 pub fn get_id(attrs: &[structs::AttrArray_InternalAttr]) -> Option<&WeakAtom> {
     Some(unsafe { get_id_from_attr(find_attr(attrs, &atom!("id"))?) })
 }
 
-/// Given a class name, a case sensitivity, and an array of attributes, returns
-/// whether the class has the attribute that name represents.
+/// Given a class or part name, a case sensitivity, and an array of attributes,
+/// returns whether the attribute has that name.
 #[inline(always)]
-pub fn has_class(
+pub fn has_class_or_part(
     name: &Atom,
     case_sensitivity: CaseSensitivity,
     attr: &structs::nsAttrValue,
 ) -> bool {
-    match unsafe { get_class_from_attr(attr) } {
+    match unsafe { get_class_or_part_from_attr(attr) } {
         Class::None => false,
         Class::One(atom) => unsafe { case_sensitivity.eq_atom(name, WeakAtom::new(atom)) },
         Class::More(atoms) => match case_sensitivity {
             CaseSensitivity::CaseSensitive => {
                 atoms.iter().any(|atom| atom.mRawPtr == name.as_ptr())
             },
             CaseSensitivity::AsciiCaseInsensitive => unsafe {
                 atoms
@@ -109,17 +109,17 @@ pub fn has_class(
 /// Given an item, a callback, and a getter, execute `callback` for each class
 /// this `item` has.
 #[inline(always)]
 pub fn each_class<F>(attr: &structs::nsAttrValue, mut callback: F)
 where
     F: FnMut(&Atom),
 {
     unsafe {
-        match get_class_from_attr(attr) {
+        match get_class_or_part_from_attr(attr) {
             Class::None => {},
             Class::One(atom) => Atom::with(atom, callback),
             Class::More(atoms) => {
                 for atom in atoms {
                     Atom::with(atom.mRawPtr, &mut callback)
                 }
             },
         }
--- a/servo/components/style/gecko/wrapper.rs
+++ b/servo/components/style/gecko/wrapper.rs
@@ -569,16 +569,21 @@ impl<'le> GeckoElement<'le> {
                 None => return &[],
             };
 
             attrs.mBuffer.as_slice(attrs.mAttrCount as usize)
         }
     }
 
     #[inline(always)]
+    fn get_part_attr(&self) -> Option<&structs::nsAttrValue> {
+        snapshot_helpers::find_attr(self.attrs(), &atom!("part"))
+    }
+
+    #[inline(always)]
     fn get_class_attr(&self) -> Option<&structs::nsAttrValue> {
         if !self.may_have_class() {
             return None;
         }
 
         if self.is_svg_element() {
             let svg_class = unsafe { bindings::Gecko_GetSVGAnimatedClass(self.0).as_ref() };
             if let Some(c) = svg_class {
@@ -2254,24 +2259,34 @@ impl<'le> ::selectors::Element for Gecko
         let element_id = match snapshot_helpers::get_id(self.attrs()) {
             Some(id) => id,
             None => return false,
         };
 
         case_sensitivity.eq_atom(element_id, id)
     }
 
+    #[inline]
+    fn is_part(&self, name: &Atom) -> bool {
+        let attr = match self.get_part_attr() {
+            Some(c) => c,
+            None => return false,
+        };
+
+        snapshot_helpers::has_class_or_part(name, CaseSensitivity::CaseSensitive, attr)
+    }
+
     #[inline(always)]
     fn has_class(&self, name: &Atom, case_sensitivity: CaseSensitivity) -> bool {
         let attr = match self.get_class_attr() {
             Some(c) => c,
             None => return false,
         };
 
-        snapshot_helpers::has_class(name, case_sensitivity, attr)
+        snapshot_helpers::has_class_or_part(name, case_sensitivity, attr)
     }
 
     #[inline]
     fn is_html_element_in_html_document(&self) -> bool {
         self.is_html_element() && self.as_node().owner_doc().is_html_document()
     }
 
     #[inline]
--- a/servo/components/style/invalidation/element/element_wrapper.rs
+++ b/servo/components/style/invalidation/element/element_wrapper.rs
@@ -53,16 +53,20 @@ pub trait ElementSnapshot: Sized {
     /// The ID attribute per this snapshot. Should only be called if
     /// `has_attrs()` returns true.
     fn id_attr(&self) -> Option<&WeakAtom>;
 
     /// Whether this snapshot contains the class `name`. Should only be called
     /// if `has_attrs()` returns true.
     fn has_class(&self, name: &Atom, case_sensitivity: CaseSensitivity) -> bool;
 
+    /// Whether this snapshot represents the part named `name`. Should only be
+    /// called if `has_attrs()` returns true.
+    fn is_part(&self, name: &Atom) -> bool;
+
     /// A callback that should be called for each class of the snapshot. Should
     /// only be called if `has_attrs()` returns true.
     fn each_class<F>(&self, _: F)
     where
         F: FnMut(&Atom);
 
     /// The `xml:lang=""` or `lang=""` attribute value per this snapshot.
     fn lang_attr(&self) -> Option<AttrValue>;
@@ -335,16 +339,23 @@ where
         match self.snapshot() {
             Some(snapshot) if snapshot.has_attrs() => snapshot
                 .id_attr()
                 .map_or(false, |atom| case_sensitivity.eq_atom(&atom, id)),
             _ => self.element.has_id(id, case_sensitivity),
         }
     }
 
+    fn is_part(&self, name: &Atom) -> bool {
+        match self.snapshot() {
+            Some(snapshot) if snapshot.has_attrs() => snapshot.is_part(name),
+            _ => self.element.is_part(name),
+        }
+    }
+
     fn has_class(&self, name: &Atom, case_sensitivity: CaseSensitivity) -> bool {
         match self.snapshot() {
             Some(snapshot) if snapshot.has_attrs() => snapshot.has_class(name, case_sensitivity),
             _ => self.element.has_class(name, case_sensitivity),
         }
     }
 
     fn is_empty(&self) -> bool {
--- a/servo/components/style/invalidation/element/invalidation_map.rs
+++ b/servo/components/style/invalidation/element/invalidation_map.rs
@@ -93,16 +93,17 @@ impl Dependency {
             },
             Some(Combinator::LaterSibling) | Some(Combinator::NextSibling) => {
                 DependencyInvalidationKind::Siblings
             },
             // TODO(emilio): We could look at the selector itself to see if it's
             // an eager pseudo, and return only Descendants here if not.
             Some(Combinator::PseudoElement) => DependencyInvalidationKind::ElementAndDescendants,
             Some(Combinator::SlotAssignment) => DependencyInvalidationKind::SlottedElements,
+            Some(Combinator::Part) => unimplemented!("Need to add invalidation for shadow parts"),
         }
     }
 }
 
 impl SelectorMapEntry for Dependency {
     fn selector(&self) -> SelectorIter<SelectorImpl> {
         self.selector.iter_from(self.selector_offset)
     }
--- a/servo/components/style/invalidation/element/invalidator.rs
+++ b/servo/components/style/invalidation/element/invalidator.rs
@@ -153,29 +153,35 @@ impl<'a> Invalidation<'a> {
         }
 
         // TODO(emilio): For pseudo-elements this should be mostly false, except
         // for the weird pseudos in <input type="number">.
         //
         // We should be able to do better here!
         match self.selector.combinator_at_parse_order(self.offset - 1) {
             Combinator::Descendant | Combinator::LaterSibling | Combinator::PseudoElement => true,
-            Combinator::SlotAssignment | Combinator::NextSibling | Combinator::Child => false,
+            Combinator::Part |
+            Combinator::SlotAssignment |
+            Combinator::NextSibling |
+            Combinator::Child => false,
         }
     }
 
     fn kind(&self) -> InvalidationKind {
         if self.offset == 0 {
             return InvalidationKind::Descendant(DescendantInvalidationKind::Dom);
         }
 
         match self.selector.combinator_at_parse_order(self.offset - 1) {
             Combinator::Child | Combinator::Descendant | Combinator::PseudoElement => {
                 InvalidationKind::Descendant(DescendantInvalidationKind::Dom)
             },
+            Combinator::Part => {
+                unimplemented!("Need to add invalidation for shadow parts");
+            },
             Combinator::SlotAssignment => {
                 InvalidationKind::Descendant(DescendantInvalidationKind::Slotted)
             },
             Combinator::NextSibling | Combinator::LaterSibling => InvalidationKind::Sibling,
         }
     }
 }
 
--- a/servo/components/style/properties/gecko.mako.rs
+++ b/servo/components/style/properties/gecko.mako.rs
@@ -33,17 +33,17 @@ use crate::gecko_bindings::bindings::Gec
 use crate::gecko_bindings::bindings::Gecko_nsStyleFont_CopyLangFrom;
 use crate::gecko_bindings::bindings::Gecko_SetListStyleImageNone;
 use crate::gecko_bindings::bindings::Gecko_SetListStyleImageImageValue;
 use crate::gecko_bindings::bindings::Gecko_SetNullImageValue;
 use crate::gecko_bindings::bindings::{Gecko_ResetFilters, Gecko_CopyFiltersFrom};
 use crate::gecko_bindings::structs;
 use crate::gecko_bindings::structs::nsCSSPropertyID;
 use crate::gecko_bindings::structs::mozilla::PseudoStyleType;
-use crate::gecko_bindings::sugar::ns_style_coord::{CoordDataValue, CoordData, CoordDataMut};
+use crate::gecko_bindings::sugar::ns_style_coord::{CoordDataValue, CoordDataMut};
 use crate::gecko_bindings::sugar::refptr::RefPtr;
 use crate::gecko::values::GeckoStyleCoordConvertible;
 use crate::gecko::values::round_border_to_device_pixels;
 use crate::logical_geometry::WritingMode;
 use crate::media_queries::Device;
 use crate::properties::computed_value_flags::*;
 use crate::properties::longhands;
 use crate::rule_tree::StrongRuleNode;
@@ -2500,17 +2500,17 @@ fn static_assert() {
         let list = unsafe { (*self.gecko.${gecko_ffi_name}.to_safe().get()).mHead.as_ref() };
 
         let mut transform = clone_transform_from_list(list);
         debug_assert_eq!(transform.0.len(), 1);
         ${type}::from_transform_operation(&transform.0.pop().unwrap())
     }
 </%def>
 
-<% skip_box_longhands= """display vertical-align
+<% skip_box_longhands= """display
                           animation-name animation-delay animation-duration
                           animation-direction animation-fill-mode animation-play-state
                           animation-iteration-count animation-timing-function
                           clear transition-duration transition-delay
                           transition-timing-function transition-property
                           transform-style
                           rotate scroll-snap-points-x scroll-snap-points-y
                           scroll-snap-coordinate -moz-binding will-change
@@ -2556,57 +2556,16 @@ fn static_assert() {
     <% clear_keyword = Keyword(
         "clear",
         "Left Right None Both",
         gecko_enum_prefix="StyleClear",
         gecko_inexhaustive=True,
     ) %>
     ${impl_keyword('clear', 'mBreakType', clear_keyword)}
 
-    pub fn set_vertical_align(&mut self, v: longhands::vertical_align::computed_value::T) {
-        use crate::values::generics::box_::VerticalAlign;
-        let value = match v {
-            VerticalAlign::Baseline => structs::NS_STYLE_VERTICAL_ALIGN_BASELINE,
-            VerticalAlign::Sub => structs::NS_STYLE_VERTICAL_ALIGN_SUB,
-            VerticalAlign::Super => structs::NS_STYLE_VERTICAL_ALIGN_SUPER,
-            VerticalAlign::Top => structs::NS_STYLE_VERTICAL_ALIGN_TOP,
-            VerticalAlign::TextTop => structs::NS_STYLE_VERTICAL_ALIGN_TEXT_TOP,
-            VerticalAlign::Middle => structs::NS_STYLE_VERTICAL_ALIGN_MIDDLE,
-            VerticalAlign::Bottom => structs::NS_STYLE_VERTICAL_ALIGN_BOTTOM,
-            VerticalAlign::TextBottom => structs::NS_STYLE_VERTICAL_ALIGN_TEXT_BOTTOM,
-            VerticalAlign::MozMiddleWithBaseline => {
-                structs::NS_STYLE_VERTICAL_ALIGN_MIDDLE_WITH_BASELINE
-            },
-            VerticalAlign::Length(length) => {
-                self.gecko.mVerticalAlign.set(length);
-                return;
-            },
-        };
-        self.gecko.mVerticalAlign.set_value(CoordDataValue::Enumerated(value));
-    }
-
-    pub fn clone_vertical_align(&self) -> longhands::vertical_align::computed_value::T {
-        use crate::values::computed::LengthPercentage;
-        use crate::values::generics::box_::VerticalAlign;
-
-        let gecko = &self.gecko.mVerticalAlign;
-        match gecko.as_value() {
-            CoordDataValue::Enumerated(value) => VerticalAlign::from_gecko_keyword(value),
-            _ => {
-                VerticalAlign::Length(
-                    LengthPercentage::from_gecko_style_coord(gecko).expect(
-                        "expected <length-percentage> for vertical-align",
-                    ),
-                )
-            },
-        }
-    }
-
-    <%call expr="impl_coord_copy('vertical_align', 'mVerticalAlign')"></%call>
-
     ${impl_style_coord("scroll_snap_points_x", "mScrollSnapPointsX")}
     ${impl_style_coord("scroll_snap_points_y", "mScrollSnapPointsY")}
 
     pub fn set_scroll_snap_coordinate<I>(&mut self, v: I)
         where I: IntoIterator<Item = longhands::scroll_snap_coordinate::computed_value::single_value::T>,
               I::IntoIter: ExactSizeIterator
     {
         self.gecko.mScrollSnapCoordinate.assign_from_iter_pod(v.into_iter());
--- a/servo/components/style/values/generics/box.rs
+++ b/servo/components/style/values/generics/box.rs
@@ -1,60 +1,77 @@
 /* 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 https://mozilla.org/MPL/2.0/. */
 
 //! Generic types for box properties.
 
 use crate::values::animated::ToAnimatedZero;
 
+#[derive(
+    Animate,
+    Clone,
+    ComputeSquaredDistance,
+    Copy,
+    Debug,
+    FromPrimitive,
+    MallocSizeOf,
+    Parse,
+    PartialEq,
+    SpecifiedValueInfo,
+    ToComputedValue,
+    ToCss,
+    ToResolvedValue,
+    ToShmem,
+)]
+#[repr(u8)]
+#[allow(missing_docs)]
+pub enum VerticalAlignKeyword {
+    Baseline,
+    Sub,
+    Super,
+    Top,
+    TextTop,
+    Middle,
+    Bottom,
+    TextBottom,
+    #[cfg(feature = "gecko")]
+    MozMiddleWithBaseline,
+}
+
 /// A generic value for the `vertical-align` property.
 #[derive(
     Animate,
     Clone,
     ComputeSquaredDistance,
     Copy,
     Debug,
     MallocSizeOf,
     PartialEq,
     SpecifiedValueInfo,
     ToComputedValue,
     ToCss,
     ToResolvedValue,
     ToShmem,
 )]
-pub enum VerticalAlign<LengthPercentage> {
-    /// `baseline`
-    Baseline,
-    /// `sub`
-    Sub,
-    /// `super`
-    Super,
-    /// `top`
-    Top,
-    /// `text-top`
-    TextTop,
-    /// `middle`
-    Middle,
-    /// `bottom`
-    Bottom,
-    /// `text-bottom`
-    TextBottom,
-    /// `-moz-middle-with-baseline`
-    #[cfg(feature = "gecko")]
-    MozMiddleWithBaseline,
+#[repr(C, u8)]
+pub enum GenericVerticalAlign<LengthPercentage> {
+    /// One of the vertical-align keywords.
+    Keyword(VerticalAlignKeyword),
     /// `<length-percentage>`
     Length(LengthPercentage),
 }
 
+pub use self::GenericVerticalAlign as VerticalAlign;
+
 impl<L> VerticalAlign<L> {
     /// Returns `baseline`.
     #[inline]
     pub fn baseline() -> Self {
-        VerticalAlign::Baseline
+        VerticalAlign::Keyword(VerticalAlignKeyword::Baseline)
     }
 }
 
 impl<L> ToAnimatedZero for VerticalAlign<L> {
     fn to_animated_zero(&self) -> Result<Self, ()> {
         Err(())
     }
 }
--- a/servo/components/style/values/specified/box.rs
+++ b/servo/components/style/values/specified/box.rs
@@ -5,17 +5,17 @@
 //! Specified types for box properties.
 
 use crate::custom_properties::Name as CustomPropertyName;
 use crate::parser::{Parse, ParserContext};
 use crate::properties::{LonghandId, PropertyDeclarationId, PropertyFlags};
 use crate::properties::{PropertyId, ShorthandId};
 use crate::values::generics::box_::AnimationIterationCount as GenericAnimationIterationCount;
 use crate::values::generics::box_::Perspective as GenericPerspective;
-use crate::values::generics::box_::VerticalAlign as GenericVerticalAlign;
+use crate::values::generics::box_::{GenericVerticalAlign, VerticalAlignKeyword};
 use crate::values::specified::length::{LengthPercentage, NonNegativeLength};
 use crate::values::specified::{AllowQuirks, Number};
 use crate::values::{CustomIdent, KeyframesName};
 use crate::Atom;
 use cssparser::Parser;
 use selectors::parser::SelectorParseErrorKind;
 use std::fmt::{self, Write};
 use style_traits::{CssWriter, KeywordsCollectFn, ParseError};
@@ -275,30 +275,17 @@ impl Parse for VerticalAlign {
         context: &ParserContext,
         input: &mut Parser<'i, 't>,
     ) -> Result<Self, ParseError<'i>> {
         if let Ok(lp) = input.try(|i| LengthPercentage::parse_quirky(context, i, AllowQuirks::Yes))
         {
             return Ok(GenericVerticalAlign::Length(lp));
         }
 
-        try_match_ident_ignore_ascii_case! { input,
-            "baseline" => Ok(GenericVerticalAlign::Baseline),
-            "sub" => Ok(GenericVerticalAlign::Sub),
-            "super" => Ok(GenericVerticalAlign::Super),
-            "top" => Ok(GenericVerticalAlign::Top),
-            "text-top" => Ok(GenericVerticalAlign::TextTop),
-            "middle" => Ok(GenericVerticalAlign::Middle),
-            "bottom" => Ok(GenericVerticalAlign::Bottom),
-            "text-bottom" => Ok(GenericVerticalAlign::TextBottom),
-            #[cfg(feature = "gecko")]
-            "-moz-middle-with-baseline" => {
-                Ok(GenericVerticalAlign::MozMiddleWithBaseline)
-            },
-        }
+        Ok(GenericVerticalAlign::Keyword(VerticalAlignKeyword::parse(input)?))
     }
 }
 
 /// https://drafts.csswg.org/css-animations/#animation-iteration-count
 pub type AnimationIterationCount = GenericAnimationIterationCount<Number>;
 
 impl Parse for AnimationIterationCount {
     fn parse<'i, 't>(
--- a/servo/components/style/values/specified/text.rs
+++ b/servo/components/style/values/specified/text.rs
@@ -558,18 +558,17 @@ pub enum TextAlignKeyword {
     #[cfg(feature = "servo")]
     ServoRight,
     #[css(skip)]
     #[cfg(feature = "gecko")]
     Char,
 }
 
 /// Specified value of text-align property.
-#[cfg_attr(feature = "gecko", derive(MallocSizeOf))]
-#[derive(Clone, Copy, Debug, Eq, Hash, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
+#[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
 pub enum TextAlign {
     /// Keyword value of text-align property.
     Keyword(TextAlignKeyword),
     /// `match-parent` value of text-align property. It has a different handling
     /// unlike other keywords.
     #[cfg(feature = "gecko")]
     MatchParent,
     /// `MozCenterOrInherit` value of text-align property. It cannot be parsed,
--- a/servo/ports/geckolib/cbindgen.toml
+++ b/servo/ports/geckolib/cbindgen.toml
@@ -112,16 +112,17 @@ include = [
   "MozListReversed",
   "Owned",
   "OwnedOrNull",
   "Strong",
   "ScrollbarColor",
   "Color",
   "ColorOrAuto",
   "GradientItem",
+  "VerticalAlign",
 ]
 item_types = ["enums", "structs", "typedefs", "functions"]
 renaming_overrides_prefixing = true
 
 # Prevent some renaming for Gecko types that cbindgen doesn't otherwise understand.
 [export.rename]
 "nscolor" = "nscolor"
 "nsAtom" = "nsAtom"
--- a/servo/ports/geckolib/glue.rs
+++ b/servo/ports/geckolib/glue.rs
@@ -4324,16 +4324,17 @@ pub extern "C" fn Servo_DeclarationBlock
     declarations: &RawServoDeclarationBlock,
     property: nsCSSPropertyID,
     value: i32,
 ) {
     use num_traits::FromPrimitive;
     use style::properties::longhands;
     use style::properties::PropertyDeclaration;
     use style::values::generics::font::FontStyle;
+    use style::values::generics::box_::{VerticalAlign, VerticalAlignKeyword};
     use style::values::specified::BorderStyle;
     use style::values::specified::Display;
     use style::values::specified::{Clear, Float};
 
     fn get_from_computed<T>(value: u32) -> T
     where
         T: ToComputedValue,
         T::ComputedValue: FromPrimitive,
@@ -4346,17 +4347,17 @@ pub extern "C" fn Servo_DeclarationBlock
     let value = value as u32;
 
     let prop = match_wrap_declared! { long,
         MozUserModify => longhands::_moz_user_modify::SpecifiedValue::from_gecko_keyword(value),
         Direction => longhands::direction::SpecifiedValue::from_gecko_keyword(value),
         Display => get_from_computed::<Display>(value),
         Float => get_from_computed::<Float>(value),
         Clear => get_from_computed::<Clear>(value),
-        VerticalAlign => longhands::vertical_align::SpecifiedValue::from_gecko_keyword(value),
+        VerticalAlign => VerticalAlign::Keyword(VerticalAlignKeyword::from_u32(value).unwrap()),
         TextAlign => longhands::text_align::SpecifiedValue::from_gecko_keyword(value),
         TextEmphasisPosition => longhands::text_emphasis_position::SpecifiedValue::from_gecko_keyword(value),
         FontSize => {
             // We rely on Gecko passing in font-size values (0...7) here.
             longhands::font_size::SpecifiedValue::from_html_size(value as u8)
         },
         FontStyle => {
             let val = if value == structs::NS_FONT_STYLE_ITALIC {
--- a/taskcluster/taskgraph/decision.py
+++ b/taskcluster/taskgraph/decision.py
@@ -3,18 +3,20 @@
 # 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 os
 import json
 import logging
+import time
+import sys
 
-import time
+from redo import retry
 import yaml
 
 from . import GECKO
 from .actions import render_actions_json
 from .create import create_tasks
 from .generator import TaskGraphGenerator
 from .parameters import Parameters, get_version, get_app_version
 from .taskgraph import TaskGraph
@@ -315,22 +317,26 @@ def get_decision_parameters(config, opti
 
 def get_existing_tasks(rebuild_kinds, parameters, graph_config):
     """
     Find the decision task corresponding to the on-push graph, and return
     a mapping of labels to task-ids from it. This will skip the kinds specificed
     by `rebuild_kinds`.
     """
     try:
-        decision_task = find_decision_task(parameters, graph_config)
-        task_graph = get_artifact(decision_task, "public/full-task-graph.json")
+        decision_task = retry(
+            find_decision_task,
+            args=(parameters, graph_config),
+            attempts=4,
+            sleeptime=5*60,
+        )
     except Exception:
         logger.exception("Didn't find existing push task.")
-        return
-    _, task_graph = TaskGraph.from_json(task_graph)
+        sys.exit(1)
+    _, task_graph = TaskGraph.from_json(get_artifact(decision_task, "public/full-task-graph.json"))
     parameters['existing_tasks'] = find_existing_tasks_from_previous_kinds(
         task_graph, [decision_task], rebuild_kinds
     )
 
 
 def set_try_config(parameters, task_config_file):
     if os.path.isfile(task_config_file):
         logger.info("using try tasks from {}".format(task_config_file))
--- a/taskcluster/taskgraph/transforms/tests.py
+++ b/taskcluster/taskgraph/transforms/tests.py
@@ -153,28 +153,30 @@ def runs_on_central(test):
     return match_run_on_projects('mozilla-central', test['run-on-projects'])
 
 
 TEST_VARIANTS = {
     'serviceworker': {
         'description': "{description} with serviceworker-e10s redesign enabled",
         'filterfn': runs_on_central,
         'suffix': 'sw',
-        'config': {
+        'replace': {
             'run-on-projects': ['mozilla-central'],
+        },
+        'merge': {
             'tier': 2,
             'mozharness': {
                 'extra-options': ['--setpref="dom.serviceWorkers.parent_intercept=true"'],
             },
-        }
+        },
     },
     'socketprocess': {
         'description': "{description} with socket process enabled",
         'suffix': 'spi',
-        'config': {
+        'merge': {
             'mozharness': {
                 'extra-options': [
                     '--setpref="media.peerconnection.mtransport_process=true"',
                     '--setpref="network.process.enabled=true"',
                 ],
             }
         }
     }
@@ -990,17 +992,18 @@ def split_variants(config, tests):
 
             group, symbol = split_symbol(testv['treeherder-symbol'])
             if group != '?':
                 group += suffix
             else:
                 symbol += suffix
             testv['treeherder-symbol'] = join_symbol(group, symbol)
 
-            yield merge(testv, variant['config'])
+            testv.update(variant.get('replace', {}))
+            yield merge(testv, variant.get('merge', {}))
 
 
 @transforms.add
 def split_e10s(config, tests):
     for test in tests:
         e10s = test['e10s']
 
         if e10s:
--- a/testing/mach_commands.py
+++ b/testing/mach_commands.py
@@ -1136,14 +1136,15 @@ class TestInfoCommand(MachCommandBase):
                        threshold_pct, max_run_time))
         else:
             print("No tasks found.")
 
 
 @CommandProvider
 class RustTests(MachCommandBase):
     @Command('rusttests', category='testing',
+             conditions=[conditions.is_non_artifact_build],
              description="Run rust unit tests (via cargo test).")
     def run_rusttests(self, **kwargs):
         return self._mach_context.commands.dispatch('build', self._mach_context,
                                                     what=['pre-export',
                                                           'export',
                                                           'recurse_rusttests'])
--- a/testing/web-platform/meta/service-workers/service-worker/waiting.https.html.ini
+++ b/testing/web-platform/meta/service-workers/service-worker/waiting.https.html.ini
@@ -1,6 +1,9 @@
 [waiting.https.html]
+  disabled:
+    if sw-e10s: https://bugzilla.mozilla.org/show_bug.cgi?id=1543316
+
   [waiting is set after installation]
     expected:
       if sw-e10s: PASS
       FAIL
 
--- a/testing/web-platform/meta/webrtc/RTCPeerConnection-remote-track-mute.https.html.ini
+++ b/testing/web-platform/meta/webrtc/RTCPeerConnection-remote-track-mute.https.html.ini
@@ -1,5 +1,9 @@
 [RTCPeerConnection-remote-track-mute.https.html]
   expected: TIMEOUT
-  [pc.close() mutes remote tracks]
+  [transceiver.stop() on one side (without renegotiation) causes mute events on the other]
     expected: TIMEOUT
-    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1531107
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1545855
+
+  [pc.close() on one side causes mute events on the other]
+    expected: NOTRUN
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1545855
--- a/testing/web-platform/tests/webrtc/RTCPeerConnection-remote-track-mute.https.html
+++ b/testing/web-platform/tests/webrtc/RTCPeerConnection-remote-track-mute.https.html
@@ -95,13 +95,33 @@ promise_test(async t => {
   // Need to wait for the initial unmute event before closing, otherwise
   // there will be no transition from unmuted->muted.
   const muteWatcher = new EventWatcher(t, e.track, ['mute', 'unmute']);
   const unmutePromise = muteWatcher.wait_for('unmute');
   await exchangeAnswer(pc1, pc2);
   await unmutePromise;
 
   const mutePromise = muteWatcher.wait_for('mute');
-  pc2.close();
+  localTransceiver.stop();
   await mutePromise;
-}, 'pc.close() mutes remote tracks');
+}, 'transceiver.stop() on one side (without renegotiation) causes mute events on the other');
+
+promise_test(async t => {
+  const pc1 = createPeerConnectionWithCleanup(t);
+  const pc1Sender = pc1.addTrack(...await createTrackAndStreamWithCleanup(t));
+  const localTransceiver = findTransceiverForSender(pc1, pc1Sender);
+  const pc2 = createPeerConnectionWithCleanup(t);
+  exchangeIceCandidates(pc1, pc2);
+
+  const e = await exchangeOfferAndListenToOntrack(t, pc1, pc2);
+  // Need to wait for the initial unmute event before closing, otherwise
+  // there will be no transition from unmuted->muted.
+  const muteWatcher = new EventWatcher(t, e.track, ['mute', 'unmute']);
+  const unmutePromise = muteWatcher.wait_for('unmute');
+  await exchangeAnswer(pc1, pc2);
+  await unmutePromise;
+
+  const mutePromise = muteWatcher.wait_for('mute');
+  pc1.close();
+  await mutePromise;
+}, 'pc.close() on one side causes mute events on the other');
 
 </script>
--- a/toolkit/actors/PictureInPictureChild.jsm
+++ b/toolkit/actors/PictureInPictureChild.jsm
@@ -77,16 +77,22 @@ class PictureInPictureToggleChild extend
       };
       this.weakDocStates.set(this.content.document, state);
     }
 
     return state;
   }
 
   handleEvent(event) {
+    if (!event.isTrusted) {
+      // We don't care about synthesized events that might be coming from
+      // content JS.
+      return;
+    }
+
     switch (event.type) {
       case "canplay": {
         if (this.toggleEnabled &&
             event.target instanceof this.content.HTMLVideoElement &&
             !event.target.controls &&
             event.target.ownerDocument == this.content.document) {
           this.registerVideo(event.target);
         }
copy from toolkit/components/search/SearchService.jsm
copy to toolkit/components/search/SearchEngine.jsm
--- a/toolkit/components/search/SearchService.jsm
+++ b/toolkit/components/search/SearchEngine.jsm
@@ -1,102 +1,47 @@
 
 /* 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/. */
 
 /* eslint no-shadow: error, mozilla/no-aArgs: error */
 
 const {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
-const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
-const {PromiseUtils} = ChromeUtils.import("resource://gre/modules/PromiseUtils.jsm");
-const {AppConstants} = ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
 
 XPCOMUtils.defineLazyModuleGetters(this, {
-  AddonManager: "resource://gre/modules/AddonManager.jsm",
-  DeferredTask: "resource://gre/modules/DeferredTask.jsm",
+  AppConstants: "resource://gre/modules/AppConstants.jsm",
   OS: "resource://gre/modules/osfile.jsm",
-  SearchStaticData: "resource://gre/modules/SearchStaticData.jsm",
-  setTimeout: "resource://gre/modules/Timer.jsm",
-  clearTimeout: "resource://gre/modules/Timer.jsm",
-  ExtensionParent: "resource://gre/modules/ExtensionParent.jsm",
-  RemoteSettings: "resource://services-settings/remote-settings.js",
+  Services: "resource://gre/modules/Services.jsm",
 });
 
 XPCOMUtils.defineLazyServiceGetters(this, {
   gEnvironment: ["@mozilla.org/process/environment;1", "nsIEnvironment"],
   gChromeReg: ["@mozilla.org/chrome/chrome-registry;1", "nsIChromeRegistry"],
 });
 
 const BROWSER_SEARCH_PREF = "browser.search.";
 
 XPCOMUtils.defineLazyPreferenceGetter(this, "loggingEnabled", BROWSER_SEARCH_PREF + "log", false);
-// Can't use defineLazyPreferenceGetter because we want the value
-// from the default branch
-XPCOMUtils.defineLazyGetter(this, "distroID", () => {
-  return Services.prefs.getDefaultBranch("distribution.").getCharPref("id", "");
-});
 
 const BinaryInputStream = Components.Constructor(
   "@mozilla.org/binaryinputstream;1",
   "nsIBinaryInputStream", "setInputStream");
 
-XPCOMUtils.defineLazyGlobalGetters(this, ["DOMParser", "XMLHttpRequest", "URLSearchParams"]);
-
-// A text encoder to UTF8, used whenever we commit the cache to disk.
-XPCOMUtils.defineLazyGetter(this, "gEncoder",
-                            function() {
-                              return new TextEncoder();
-                            });
-
-
-// Directory service keys
-const NS_APP_DISTRIBUTION_SEARCH_DIR_LIST = "SrchPluginsDistDL";
-
-// We load plugins from EXT_SEARCH_PREFIX, where a list.json
-// file needs to exist to list available engines.
-const EXT_SEARCH_PREFIX = "resource://search-extensions/";
 const APP_SEARCH_PREFIX = "resource://search-plugins/";
 
-// The address we use to sign the built in search extensions with.
-const EXT_SIGNING_ADDRESS = "search.mozilla.org";
-
 // See documentation in nsISearchService.idl.
 const SEARCH_ENGINE_TOPIC        = "browser-search-engine-modified";
-const TOPIC_LOCALES_CHANGE       = "intl:app-locales-changed";
-const QUIT_APPLICATION_TOPIC     = "quit-application";
 
-const SEARCH_ENGINE_REMOVED      = "engine-removed";
-const SEARCH_ENGINE_ADDED        = "engine-added";
 const SEARCH_ENGINE_CHANGED      = "engine-changed";
 const SEARCH_ENGINE_LOADED       = "engine-loaded";
-const SEARCH_ENGINE_DEFAULT      = "engine-default";
 
 // The following constants are left undocumented in nsISearchService.idl
 // For the moment, they are meant for testing/debugging purposes only.
 
-/**
- * Topic used for events involving the service itself.
- */
-const SEARCH_SERVICE_TOPIC       = "browser-search-service";
-
-/**
- * Sent whenever the cache is fully written to disk.
- */
-const SEARCH_SERVICE_CACHE_WRITTEN  = "write-cache-to-disk-complete";
-
-// Delay for batching invalidation of the JSON cache (ms)
-const CACHE_INVALIDATION_DELAY = 1000;
-
-// Current cache version. This should be incremented if the format of the cache
-// file is modified.
-const CACHE_VERSION = 1;
-
-const CACHE_FILENAME = "search.json.mozlz4";
-
 // Set an arbitrary cap on the maximum icon size. Without this, large icons can
 // cause big delays when loading them at startup.
 const MAX_ICON_SIZE   = 20000;
 
 // Default charset to use for sending search parameters. ISO-8859-1 is used to
 // match previous nsInternetSearchService behavior as a URL parameter. Label
 // resolution causes windows-1252 to be actually used.
 const DEFAULT_QUERY_CHARSET = "ISO-8859-1";
@@ -160,70 +105,27 @@ const OS_PARAM_START_PAGE_DEF   = "1"; /
 // required, since our values are just really arbitrary "guesses" that should
 // give us the output we want.
 var OS_UNSUPPORTED_PARAMS = [
   [OS_PARAM_COUNT, OS_PARAM_COUNT_DEF],
   [OS_PARAM_START_INDEX, OS_PARAM_START_INDEX_DEF],
   [OS_PARAM_START_PAGE, OS_PARAM_START_PAGE_DEF],
 ];
 
-// The default engine update interval, in days. This is only used if an engine
-// specifies an updateURL, but not an updateInterval.
-const SEARCH_DEFAULT_UPDATE_INTERVAL = 7;
-
-// The default interval before checking again for the name of the
-// default engine for the region, in seconds. Only used if the response
-// from the server doesn't specify an interval.
-const SEARCH_GEO_DEFAULT_UPDATE_INTERVAL = 2592000; // 30 days.
-
-// Some extensions package multiple locales into a single extension, for those
-// engines we use engine-locale to address the engine.
-// This is to be removed in https://bugzilla.mozilla.org/show_bug.cgi?id=1532246
-const MULTI_LOCALE_ENGINES = [
-  "amazon", "amazondotcom", "bolcom", "ebay", "google", "marktplaats",
-  "mercadolibre", "twitter", "wikipedia", "wiktionary", "yandex", "multilocale",
-];
-
-// A tag to denote when we are using the "default_locale" of an engine
-const DEFAULT_TAG = "default";
-
-/**
- * Prefixed to all search debug output.
- */
-const SEARCH_LOG_PREFIX = "*** Search: ";
-
-/**
- * This is the Remote Settings key that we use to get the ignore lists for
- * engines.
- */
-const SETTINGS_IGNORELIST_KEY = "hijack-blocklists";
-
 /**
  * Outputs text to the JavaScript console as well as to stdout.
  */
 function LOG(text) {
   if (loggingEnabled) {
-    dump(SEARCH_LOG_PREFIX + text + "\n");
+    dump("*** Search: " + text + "\n");
     Services.console.logStringMessage(text);
   }
 }
 
 /**
- * Presents an assertion dialog in non-release builds and throws.
- * @param  message
- *         A message to display
- * @param  resultCode
- *         The NS_ERROR_* value to throw.
- * @throws resultCode
- */
-function ERROR(message, resultCode) {
-  throw Components.Exception(message, resultCode);
-}
-
-/**
  * Logs the failure message (if browser.search.log is enabled) and throws.
  * @param  message
  *         A message to display
  * @param  resultCode
  *         The NS_ERROR_* value to throw.
  * @throws resultCode or NS_ERROR_INVALID_ARG if resultCode isn't specified.
  */
 function FAIL(message, resultCode) {
@@ -355,367 +257,16 @@ function rescaleIcon(byteArray, contentT
   let stream = imgTools.encodeScaledImage(container, "image/png", size, size);
   let streamSize = stream.available();
   if (streamSize > MAX_ICON_SIZE)
     throw new Error("Icon is too big");
   let bis = new BinaryInputStream(stream);
   return [bis.readByteArray(streamSize), "image/png"];
 }
 
-function isPartnerBuild() {
-  // Mozilla-provided builds (i.e. funnelcakes) are not partner builds
-  return distroID && !distroID.startsWith("mozilla");
-}
-
-// Method to determine if we should be using geo-specific defaults
-function geoSpecificDefaultsEnabled() {
-  return Services.prefs.getBoolPref("browser.search.geoSpecificDefaults", false);
-}
-
-// A method that tries to determine if this user is in a US geography.
-function isUSTimezone() {
-  // Timezone assumptions! We assume that if the system clock's timezone is
-  // between Newfoundland and Hawaii, that the user is in North America.
-
-  // This includes all of South America as well, but we have relatively few
-  // en-US users there, so that's OK.
-
-  // 150 minutes = 2.5 hours (UTC-2.5), which is
-  // Newfoundland Daylight Time (http://www.timeanddate.com/time/zones/ndt)
-
-  // 600 minutes = 10 hours (UTC-10), which is
-  // Hawaii-Aleutian Standard Time (http://www.timeanddate.com/time/zones/hast)
-
-  let UTCOffset = (new Date()).getTimezoneOffset();
-  return UTCOffset >= 150 && UTCOffset <= 600;
-}
-
-// A method that tries to determine our region via an XHR geoip lookup.
-var ensureKnownRegion = async function(ss) {
-  // If we have a region already stored in our prefs we trust it.
-  let region = Services.prefs.getCharPref("browser.search.region", "");
-  try {
-    if (!region) {
-      // We don't have it cached, so fetch it. fetchRegion() will call
-      // storeRegion if it gets a result (even if that happens after the
-      // promise resolves) and fetchRegionDefault.
-      await fetchRegion(ss);
-    } else if (geoSpecificDefaultsEnabled()) {
-      // The territory default we have already fetched may have expired.
-      let expired = (ss.getGlobalAttr("searchDefaultExpir") || 0) <= Date.now();
-      // If we have a default engine or a list of visible default engines
-      // saved, the hashes should be valid, verify them now so that we can
-      // refetch if they have been tampered with.
-      let defaultEngine = ss.getVerifiedGlobalAttr("searchDefault");
-      let visibleDefaultEngines = ss.getVerifiedGlobalAttr("visibleDefaultEngines");
-      let hasValidHashes = (defaultEngine || defaultEngine === undefined) &&
-                           (visibleDefaultEngines || visibleDefaultEngines === undefined);
-      if (expired || !hasValidHashes) {
-        await new Promise(resolve => {
-          let timeoutMS = Services.prefs.getIntPref("browser.search.geoip.timeout");
-          let timerId = setTimeout(() => {
-            timerId = null;
-            resolve();
-          }, timeoutMS);
-
-          let callback = () => {
-            clearTimeout(timerId);
-            resolve();
-          };
-          fetchRegionDefault(ss).then(callback).catch(err => {
-            Cu.reportError(err);
-            callback();
-          });
-        });
-      }
-    }
-
-    // If gInitialized is true then the search service was forced to perform
-    // a sync initialization during our XHRs - capture this via telemetry.
-    Services.telemetry.getHistogramById("SEARCH_SERVICE_COUNTRY_FETCH_CAUSED_SYNC_INIT").add(gInitialized);
-  } catch (ex) {
-    Cu.reportError(ex);
-  } finally {
-    // Since bug 1492475, we don't block our init flows on the region fetch as
-    // performed here. But we'd still like to unit-test its implementation, thus
-    // we fire this observer notification.
-    Services.obs.notifyObservers(null, SEARCH_SERVICE_TOPIC, "ensure-known-region-done");
-  }
-};
-
-// Store the result of the geoip request as well as any other values and
-// telemetry which depend on it.
-function storeRegion(region) {
-  let isTimezoneUS = isUSTimezone();
-  // If it's a US region, but not a US timezone, we don't store the value.
-  // This works because no region defaults to ZZ (unknown) in nsURLFormatter
-  if (region != "US" || isTimezoneUS) {
-    Services.prefs.setCharPref("browser.search.region", region);
-  }
-
-  // and telemetry...
-  if (region == "US" && !isTimezoneUS) {
-    LOG("storeRegion mismatch - US Region, non-US timezone");
-    Services.telemetry.getHistogramById("SEARCH_SERVICE_US_COUNTRY_MISMATCHED_TIMEZONE").add(1);
-  }
-  if (region != "US" && isTimezoneUS) {
-    LOG("storeRegion mismatch - non-US Region, US timezone");
-    Services.telemetry.getHistogramById("SEARCH_SERVICE_US_TIMEZONE_MISMATCHED_COUNTRY").add(1);
-  }
-  // telemetry to compare our geoip response with platform-specific country data.
-  // On Mac and Windows, we can get a country code via sysinfo
-  let platformCC = Services.sysinfo.get("countryCode");
-  if (platformCC) {
-    let probeUSMismatched, probeNonUSMismatched;
-    switch (Services.appinfo.OS) {
-      case "Darwin":
-        probeUSMismatched = "SEARCH_SERVICE_US_COUNTRY_MISMATCHED_PLATFORM_OSX";
-        probeNonUSMismatched = "SEARCH_SERVICE_NONUS_COUNTRY_MISMATCHED_PLATFORM_OSX";
-        break;
-      case "WINNT":
-        probeUSMismatched = "SEARCH_SERVICE_US_COUNTRY_MISMATCHED_PLATFORM_WIN";
-        probeNonUSMismatched = "SEARCH_SERVICE_NONUS_COUNTRY_MISMATCHED_PLATFORM_WIN";
-        break;
-      default:
-        Cu.reportError("Platform " + Services.appinfo.OS +
-          " has system country code but no search service telemetry probes");
-        break;
-    }
-    if (probeUSMismatched && probeNonUSMismatched) {
-      if (region == "US" || platformCC == "US") {
-        // one of the 2 said US, so record if they are the same.
-        Services.telemetry.getHistogramById(probeUSMismatched).add(region != platformCC);
-      } else {
-        // non-US - record if they are the same
-        Services.telemetry.getHistogramById(probeNonUSMismatched).add(region != platformCC);
-      }
-    }
-  }
-}
-
-// Get the region we are in via a XHR geoip request.
-function fetchRegion(ss) {
-  // values for the SEARCH_SERVICE_COUNTRY_FETCH_RESULT 'enum' telemetry probe.
-  const TELEMETRY_RESULT_ENUM = {
-    SUCCESS: 0,
-    SUCCESS_WITHOUT_DATA: 1,
-    XHRTIMEOUT: 2,
-    ERROR: 3,
-    // Note that we expect to add finer-grained error types here later (eg,
-    // dns error, network error, ssl error, etc) with .ERROR remaining as the
-    // generic catch-all that doesn't fit into other categories.
-  };
-  let endpoint = Services.urlFormatter.formatURLPref("browser.search.geoip.url");
-  LOG("_fetchRegion starting with endpoint " + endpoint);
-  // As an escape hatch, no endpoint means no geoip.
-  if (!endpoint) {
-    return Promise.resolve();
-  }
-  let startTime = Date.now();
-  return new Promise(resolve => {
-    // Instead of using a timeout on the xhr object itself, we simulate one
-    // using a timer and let the XHR request complete.  This allows us to
-    // capture reliable telemetry on what timeout value should actually be
-    // used to ensure most users don't see one while not making it so large
-    // that many users end up doing a sync init of the search service and thus
-    // would see the jank that implies.
-    // (Note we do actually use a timeout on the XHR, but that's set to be a
-    // large value just incase the request never completes - we don't want the
-    // XHR object to live forever)
-    let timeoutMS = Services.prefs.getIntPref("browser.search.geoip.timeout");
-    let geoipTimeoutPossible = true;
-    let timerId = setTimeout(() => {
-      LOG("_fetchRegion: timeout fetching region information");
-      if (geoipTimeoutPossible)
-        Services.telemetry.getHistogramById("SEARCH_SERVICE_COUNTRY_TIMEOUT").add(1);
-      timerId = null;
-      resolve();
-    }, timeoutMS);
-
-    let resolveAndReportSuccess = (result, reason) => {
-      // Even if we timed out, we want to save the region and everything
-      // related so next startup sees the value and doesn't retry this dance.
-      if (result) {
-        storeRegion(result);
-      }
-      Services.telemetry.getHistogramById("SEARCH_SERVICE_COUNTRY_FETCH_RESULT").add(reason);
-
-      // This notification is just for tests...
-      Services.obs.notifyObservers(null, SEARCH_SERVICE_TOPIC, "geoip-lookup-xhr-complete");
-
-      if (timerId) {
-        Services.telemetry.getHistogramById("SEARCH_SERVICE_COUNTRY_TIMEOUT").add(0);
-        geoipTimeoutPossible = false;
-      }
-
-      let callback = () => {
-        // If we've already timed out then we've already resolved the promise,
-        // so there's nothing else to do.
-        if (timerId == null) {
-          return;
-        }
-        clearTimeout(timerId);
-        resolve();
-      };
-
-      if (result && geoSpecificDefaultsEnabled()) {
-        fetchRegionDefault(ss).then(callback).catch(err => {
-          Cu.reportError(err);
-          callback();
-        });
-      } else {
-        callback();
-      }
-    };
-
-    let request = new XMLHttpRequest();
-    // This notification is just for tests...
-    Services.obs.notifyObservers(request, SEARCH_SERVICE_TOPIC, "geoip-lookup-xhr-starting");
-    request.timeout = 100000; // 100 seconds as the last-chance fallback
-    request.onload = function(event) {
-      let took = Date.now() - startTime;
-      let region = event.target.response && event.target.response.country_code;
-      LOG("_fetchRegion got success response in " + took + "ms: " + region);
-      Services.telemetry.getHistogramById("SEARCH_SERVICE_COUNTRY_FETCH_TIME_MS").add(took);
-      let reason = region ? TELEMETRY_RESULT_ENUM.SUCCESS : TELEMETRY_RESULT_ENUM.SUCCESS_WITHOUT_DATA;
-      resolveAndReportSuccess(region, reason);
-    };
-    request.ontimeout = function(event) {
-      LOG("_fetchRegion: XHR finally timed-out fetching region information");
-      resolveAndReportSuccess(null, TELEMETRY_RESULT_ENUM.XHRTIMEOUT);
-    };
-    request.onerror = function(event) {
-      LOG("_fetchRegion: failed to retrieve region information");
-      resolveAndReportSuccess(null, TELEMETRY_RESULT_ENUM.ERROR);
-    };
-    request.open("POST", endpoint, true);
-    request.setRequestHeader("Content-Type", "application/json");
-    request.responseType = "json";
-    request.send("{}");
-  });
-}
-
-// This converts our legacy google engines to the
-// new codes. We have to manually change them here
-// because we can't change the default name in absearch.
-function convertGoogleEngines(engineNames) {
-  let overrides = {
-    "google": "google-b-d",
-    "google-2018": "google-b-1-d",
-  };
-
-  let mobileOverrides = {
-    "google": "google-b-m",
-    "google-2018": "google-b-1-m",
-  };
-
-  if (AppConstants.platform == "android") {
-    overrides = mobileOverrides;
-  }
-  for (let engine in overrides) {
-    let index = engineNames.indexOf(engine);
-    if (index > -1) {
-      engineNames[index] = overrides[engine];
-    }
-  }
-  return engineNames;
-}
-
-// This will make an HTTP request to a Mozilla server that will return
-// JSON data telling us what engine should be set as the default for
-// the current region, and how soon we should check again.
-//
-// The optional cohort value returned by the server is to be kept locally
-// and sent to the server the next time we ping it. It lets the server
-// identify profiles that have been part of a specific experiment.
-//
-// This promise may take up to 100s to resolve, it's the caller's
-// responsibility to ensure with a timer that we are not going to
-// block the async init for too long.
-var fetchRegionDefault = (ss) => new Promise(resolve => {
-  let urlTemplate = Services.prefs.getDefaultBranch(BROWSER_SEARCH_PREF)
-                            .getCharPref("geoSpecificDefaults.url");
-  let endpoint = Services.urlFormatter.formatURL(urlTemplate);
-
-  // As an escape hatch, no endpoint means no region specific defaults.
-  if (!endpoint) {
-    resolve();
-    return;
-  }
-
-  // Append the optional cohort value.
-  const cohortPref = "browser.search.cohort";
-  let cohort = Services.prefs.getCharPref(cohortPref, "");
-  if (cohort)
-    endpoint += "/" + cohort;
-
-  LOG("fetchRegionDefault starting with endpoint " + endpoint);
-
-  let startTime = Date.now();
-  let request = new XMLHttpRequest();
-  request.timeout = 100000; // 100 seconds as the last-chance fallback
-  request.onload = function(event) {
-    let took = Date.now() - startTime;
-
-    let status = event.target.status;
-    if (status != 200) {
-      LOG("fetchRegionDefault failed with HTTP code " + status);
-      let retryAfter = request.getResponseHeader("retry-after");
-      if (retryAfter) {
-        ss.setGlobalAttr("searchDefaultExpir", Date.now() + retryAfter * 1000);
-      }
-      resolve();
-      return;
-    }
-
-    let response = event.target.response || {};
-    LOG("received " + response.toSource());
-
-    if (response.cohort) {
-      Services.prefs.setCharPref(cohortPref, response.cohort);
-    } else {
-      Services.prefs.clearUserPref(cohortPref);
-    }
-
-    if (response.settings && response.settings.searchDefault) {
-      let defaultEngine = response.settings.searchDefault;
-      ss.setVerifiedGlobalAttr("searchDefault", defaultEngine);
-      LOG("fetchRegionDefault saved searchDefault: " + defaultEngine);
-    }
-
-    if (response.settings && response.settings.visibleDefaultEngines) {
-      let visibleDefaultEngines = response.settings.visibleDefaultEngines;
-      let string = visibleDefaultEngines.join(",");
-      ss.setVerifiedGlobalAttr("visibleDefaultEngines", string);
-      LOG("fetchRegionDefault saved visibleDefaultEngines: " + string);
-    }
-
-    let interval = response.interval || SEARCH_GEO_DEFAULT_UPDATE_INTERVAL;
-    let milliseconds = interval * 1000; // |interval| is in seconds.
-    ss.setGlobalAttr("searchDefaultExpir", Date.now() + milliseconds);
-
-    LOG("fetchRegionDefault got success response in " + took + "ms");
-    // If we're doing this somewhere during the app's lifetime, reload the list
-    // of engines in order to pick up any geo-specific changes.
-    ss._maybeReloadEngines().finally(resolve);
-  };
-  request.ontimeout = function(event) {
-    LOG("fetchRegionDefault: XHR finally timed-out");
-    resolve();
-  };
-  request.onerror = function(event) {
-    LOG("fetchRegionDefault: failed to retrieve territory default information");
-    resolve();
-  };
-  request.open("GET", endpoint, true);
-  request.setRequestHeader("Content-Type", "application/json");
-  request.responseType = "json";
-  request.send();
-});
-
 function getVerificationHash(name) {
   let disclaimer = "By modifying this file, I agree that I am doing so " +
     "only within $appName itself, using official, user-driven search " +
     "engine selection processes, and in a way which does not circumvent " +
     "user consent. I acknowledge that any attempt to change this file " +
     "from outside of $appName is a malicious act, and will be responded " +
     "to accordingly.";
 
@@ -781,43 +332,16 @@ function makeChannel(url) {
 function getDir(key, iface) {
   if (!key)
     FAIL("getDir requires a directory key!");
 
   return Services.dirsvc.get(key, iface || Ci.nsIFile);
 }
 
 /**
- * Gets the current value of the locale.  It's possible for this preference to
- * be localized, so we have to do a little extra work here.  Similar code
- * exists in nsHttpHandler.cpp when building the UA string.
- */
-function getLocale() {
-  return Services.locale.requestedLocale;
-}
-
-/**
- * Wrapper for nsIPrefBranch::getComplexValue.
- * @param {string} prefName
- *   The name of the pref to get.
- * @param {*} defaultValue
- *   The value to return if the preference isn't found.
- * @returns {*}
- *   Returns either the preference value, or the default value.
- */
-function getLocalizedPref(prefName, defaultValue) {
-  try {
-    return Services.prefs.getComplexValue(prefName,
-      Ci.nsIPrefLocalizedString).data;
-  } catch (ex) {}
-
-  return defaultValue;
-}
-
-/**
  * Sanitizes a name so that it can be used as a filename. If it cannot be
  * sanitized (e.g. no valid characters), then it returns a random name.
  *
  * @param {string} name
  *  The name to be sanitized.
  * @returns {string}
  *  The sanitized name.
  */
@@ -855,20 +379,18 @@ function getMozParamPref(prefName) {
  *
  * @param {nsISearchEngine} engine
  *   The engine to which the change applies.
  * @param {string} verb
  *   A verb describing the change.
  *
  * @see nsISearchService.idl
  */
-var gInitialized = false;
-var gReinitializing = false;
 function notifyAction(engine, verb) {
-  if (gInitialized) {
+  if (Services.search.isInitialized) {
     LOG("NOTIFY: Engine: \"" + engine.name + "\"; Verb: \"" + verb + "\"");
     Services.obs.notifyObservers(engine, SEARCH_ENGINE_TOPIC, verb);
   }
 }
 
 /**
  * Simple object representing a name/value pair.
  * @see nsISearchEngine::addParam
@@ -911,33 +433,33 @@ function ParamSubstitution(paramValue, s
     // {inputEncoding} is the second most common param.
     if (name == OS_PARAM_INPUT_ENCODING)
       return engine.queryCharset;
 
     // moz: parameters are only available for default search engines.
     if (name.startsWith("moz:") && engine._isDefault) {
       // {moz:locale} and {moz:distributionID} are common
       if (name == MOZ_PARAM_LOCALE)
-        return getLocale();
+        return Services.locale.requestedLocale;
       if (name == MOZ_PARAM_DIST_ID) {
         return Services.prefs.getCharPref(BROWSER_SEARCH_PREF + "distributionID",
                                           Services.appinfo.distributionID || "");
       }
       // {moz:official} seems to have little use.
       if (name == MOZ_PARAM_OFFICIAL) {
         if (Services.prefs.getBoolPref(BROWSER_SEARCH_PREF + "official",
                                        AppConstants.MOZ_OFFICIAL_BRANDING))
           return "official";
         return "unofficial";
       }
     }
 
     // Handle the less common OpenSearch parameters we're confident about.
     if (name == OS_PARAM_LANGUAGE)
-      return getLocale() || OS_PARAM_LANGUAGE_DEF;
+      return Services.locale.requestedLocale || OS_PARAM_LANGUAGE_DEF;
     if (name == OS_PARAM_OUTPUT_ENCODING)
       return OS_PARAM_OUTPUT_ENCODING_DEF;
 
     // At this point, if a parameter is optional, just omit it.
     if (optional)
       return "";
 
     // Replace unsupported parameters that only have hardcoded default values.
@@ -1155,94 +677,121 @@ EngineURL.prototype = {
     json.params = this.params.map(collapseMozParams, this);
 
     return json;
   },
 };
 
 /**
  * nsISearchEngine constructor.
- * @param {nsIFile|nsIURI} location
+ *
+ * @param {object} options
+ *   The options for this search engine. At least one of options.name,
+ *   options.fileURI or options.uri are required.
+ * @param {string} [options.name]
+ *   The short name to use for the search engine.
+ * @param {nsIFile} [options.fileURI]
+ *   The file URI that points to the search engine data.
+ * @param {nsIURI|string} [options.uri]
  *   Represents the location of the search engine data file.
- * @param {boolean} isReadOnly
+ * @param {boolean} [options.sanitizeName]
+ *   Only applies when options.name is specified, will santize the name so
+ *   it can be used as a file name, defaults to false.
+ * @param {boolean} options.readOnly
  *   Indicates whether the engine should be treated as read-only.
  */
-function Engine(location, isReadOnly) {
-  this._readOnly = isReadOnly;
+function SearchEngine(options = {}) {
+  if (!("readOnly" in options)) {
+    throw new Error("readOnly missing from options.");
+  }
+  this._readOnly = options.readOnly;
   this._urls = [];
   this._metaData = {};
 
   let file, uri;
-  if (typeof location == "string") {
-    this._shortName = location;
-  } else if (location instanceof Ci.nsIFile) {
-    file = location;
-  } else if (location instanceof Ci.nsIURI) {
-    switch (location.scheme) {
+  if ("name" in options) {
+    if ("sanitizeName" in options && options.sanitizeName) {
+      this._shortName = sanitizeName(options.name);
+    } else {
+      this._shortName = options.name;
+    }
+  } else if ("fileURI" in options && options.fileURI instanceof Ci.nsIFile) {
+    file = options.fileURI;
+  } else if ("uri" in options) {
+    let optionsURI = options.uri;
+    if (typeof optionsURI == "string") {
+      optionsURI = makeURI(optionsURI);
+    }
+    // makeURI can return null if the URI is invalid.
+    if (!optionsURI || !(optionsURI instanceof Ci.nsIURI)) {
+      throw new Components.Exception("options.uri isn't a string nor an nsIURI",
+                                     Cr.NS_ERROR_INVALID_ARG);
+    }
+    switch (optionsURI.scheme) {
       case "https":
       case "http":
       case "ftp":
       case "data":
       case "file":
       case "resource":
       case "chrome":
-        uri = location;
+        uri = optionsURI;
         break;
       default:
-        ERROR("Invalid URI passed to the nsISearchEngine constructor",
-              Cr.NS_ERROR_INVALID_ARG);
+        throw Components.Exception("Invalid URI passed to SearchEngine constructor",
+                                   Cr.NS_ERROR_INVALID_ARG);
     }
   } else {
-    ERROR("Engine location is neither a File nor a URI object",
-          Cr.NS_ERROR_INVALID_ARG);
+    throw Components.Exception("Invalid name/fileURI/uri options passed to SearchEngine",
+                               Cr.NS_ERROR_INVALID_ARG);
   }
 
   if (!this._shortName) {
     // If we don't have a shortName at this point, it's the first time we load
     // this engine, so let's generate the shortName, id and loadPath values.
     let shortName;
     if (file) {
       shortName = file.leafName;
     } else if (uri && uri instanceof Ci.nsIURL) {
-      if (isReadOnly || (gEnvironment.get("XPCSHELL_TEST_PROFILE_DIR") &&
-                          uri.scheme == "resource")) {
+      if (this._readOnly || (gEnvironment.get("XPCSHELL_TEST_PROFILE_DIR") &&
+                             uri.scheme == "resource")) {
         shortName = uri.fileName;
       }
     }
     if (shortName && shortName.endsWith(".xml")) {
       this._shortName = shortName.slice(0, -4);
     }
     this._loadPath = this.getAnonymizedLoadPath(file, uri);
 
-    if (!shortName && !isReadOnly) {
+    if (!shortName && !this._readOnly) {
       // We are in the process of downloading and installing the engine.
       // We'll have the shortName and id once we are done parsing it.
      return;
     }
 
     // Build the id used for the legacy metadata storage, so that we
     // can do a one-time import of data from old profiles.
     if (this._isDefault ||
         (uri && uri.spec.startsWith(APP_SEARCH_PREFIX))) {
       // The second part of the check is to catch engines from language packs.
       // They aren't default engines (because they aren't app-shipped), but we
       // still need to give their id an [app] prefix for backward compat.
       this._id = "[app]/" + this._shortName + ".xml";
-    } else if (!isReadOnly) {
+    } else if (!this._readOnly) {
       this._id = "[profile]/" + this._shortName + ".xml";
     } else {
       // If the engine is neither a default one, nor a user-installed one,
       // it must be extension-shipped, so use the full path as id.
       LOG("Setting _id to full path for engine from " + this._loadPath);
       this._id = file ? file.path : uri.spec;
     }
   }
 }
 
-Engine.prototype = {
+SearchEngine.prototype = {
   // Data set by the user.
   _metaData: null,
   // The data describing the engine, in the form of an XML document element.
   _data: null,
   // Whether or not the engine is readonly.
   _readOnly: true,
   // Anonymized path of where we initially loaded the engine from.
   // This will stay null for engines installed in the profile before we moved
@@ -1311,33 +860,34 @@ Engine.prototype = {
     // Now that the data is loaded, initialize the engine object
     this._initFromData();
   },
 
   /**
    * Retrieves the engine data from a URI. Initializes the engine, flushes to
    * disk, and notifies the search service once initialization is complete.
    *
-   * @param uri The uri to load the search plugin from.
+   * @param {string|nsIURI} uri The uri to load the search plugin from.
    */
   _initFromURIAndLoad(uri) {
-    ENSURE_WARN(uri instanceof Ci.nsIURI,
+    let loadURI = uri instanceof Ci.nsIURI ? uri : makeURI(uri);
+    ENSURE_WARN(loadURI,
                 "Must have URI when calling _initFromURIAndLoad!",
                 Cr.NS_ERROR_UNEXPECTED);
 
-    LOG("_initFromURIAndLoad: Downloading engine from: \"" + uri.spec + "\".");
+    LOG("_initFromURIAndLoad: Downloading engine from: \"" + loadURI.spec + "\".");
 
-    var chan = makeChannel(uri);
+    var chan = makeChannel(loadURI);
 
     if (this._engineToUpdate && (chan instanceof Ci.nsIHttpChannel)) {
       var lastModified = this._engineToUpdate.getAttr("updatelastmodified");
       if (lastModified)
         chan.setRequestHeader("If-Modified-Since", lastModified, false);
     }
-    this._uri = uri;
+    this._uri = loadURI;
     var listener = new loadListener(chan, this, this._onLoad);
     chan.notificationCallbacks = listener;
     chan.asyncOpen(listener);
   },
 
   /**
    * Retrieves the engine data from a URI asynchronously and initializes it.
    *
@@ -2614,2247 +2164,9 @@ Submission.prototype = {
     return this._uri;
   },
   get postData() {
     return this._postData;
   },
   QueryInterface: ChromeUtils.generateQI([Ci.nsISearchSubmission]),
 };
 
-// nsISearchParseSubmissionResult
-function ParseSubmissionResult(engine, terms, termsOffset, termsLength) {
-  this._engine = engine;
-  this._terms = terms;
-  this._termsOffset = termsOffset;
-  this._termsLength = termsLength;
-}
-ParseSubmissionResult.prototype = {
-  get engine() {
-    return this._engine;
-  },
-  get terms() {
-    return this._terms;
-  },
-  get termsOffset() {
-    return this._termsOffset;
-  },
-  get termsLength() {
-    return this._termsLength;
-  },
-  QueryInterface: ChromeUtils.generateQI([Ci.nsISearchParseSubmissionResult]),
-};
-
-const gEmptyParseSubmissionResult =
-      Object.freeze(new ParseSubmissionResult(null, "", -1, 0));
-
-/**
- * The search service handles loading and maintaining of search engines. It will
- * also work out the default lists for each locale/region.
- *
- * @implements {nsISearchService}
- */
-function SearchService() {
-  this._initObservers = PromiseUtils.defer();
-}
-
-SearchService.prototype = {
-  classID: Components.ID("{7319788a-fe93-4db3-9f39-818cf08f4256}"),
-
-  // The current status of initialization. Note that it does not determine if
-  // initialization is complete, only if an error has been encountered so far.
-  _initRV: Cr.NS_OK,
-
-  // The boolean indicates that the initialization has started or not.
-  _initStarted: null,
-
-  _ensureKnownRegionPromise: null,
-
-  // Reading the JSON cache file is the first thing done during initialization.
-  // During the async init, we save it in a field so that if we have to do a
-  // sync init before the async init finishes, we can avoid reading the cache
-  // with sync disk I/O and handling lz4 decompression synchronously.
-  // This is set back to null as soon as the initialization is finished.
-  _cacheFileJSON: null,
-
-  /**
-   * Various search engines may be ignored if their submission urls contain a
-   * string that is in the list. The list is controlled via remote settings.
-   */
-  _submissionURLIgnoreList: [],
-
-  /**
-   * Various search engines may be ignored if their load path is contained
-   * in this list. The list is controlled via remote settings.
-   */
-  _loadPathIgnoreList: [],
-
-  // If initialization has not been completed yet, perform synchronous
-  // initialization.
-  // Throws in case of initialization error.
-  _ensureInitialized() {
-    if (gInitialized) {
-      if (!Components.isSuccessCode(this._initRV)) {
-        LOG("_ensureInitialized: failure");
-        throw this._initRV;
-      }
-      return;
-    }
-
-    let err = new Error("Something tried to use the search service before it's been " +
-      "properly intialized. Please examine the stack trace to figure out what and " +
-      "where to fix it:\n");
-    err.message += err.stack;
-    throw err;
-  },
-
-  /**
-   * Asynchronous implementation of the initializer.
-   *
-   * @param   [optional] skipRegionCheck
-   *          A boolean value indicating whether we should explicitly await the
-   *          the region check process to complete, which may be fetched remotely.
-   *          Pass in `false` if the caller needs to be absolutely certain of the
-   *          correct default engine and/ or ordering of visible engines.
-   * @returns {Promise} A promise, resolved successfully if the initialization
-   * succeeds.
-   */
-  async _init(skipRegionCheck) {
-    LOG("_init start");
-
-    // See if we have a cache file so we don't have to parse a bunch of XML.
-    let cache = await this._readCacheFile();
-
-    // The init flow is not going to block on a fetch from an external service,
-    // but we're kicking it off as soon as possible to prevent UI flickering as
-    // much as possible.
-    this._ensureKnownRegionPromise = ensureKnownRegion(this)
-      .catch(ex => LOG("_init: failure determining region: " + ex))
-      .finally(() => this._ensureKnownRegionPromise = null);
-    if (!skipRegionCheck) {
-      await this._ensureKnownRegionPromise;
-    }
-
-    this._setupRemoteSettings().catch(Cu.reportError);
-
-    try {
-      await this._loadEngines(cache);
-    } catch (ex) {
-      this._initRV = Cr.NS_ERROR_FAILURE;
-      LOG("_init: failure loading engines: " + ex + "\n" + ex.stack);
-    }
-    // Make sure the current list of engines is persisted, without the need to wait.
-    this._buildCache();
-    this._addObservers();
-    gInitialized = true;
-    if (Components.isSuccessCode(this._initRV)) {
-      this._initObservers.resolve(this._initRV);
-    } else {
-      this._initObservers.reject(this._initRV);
-    }
-    Services.obs.notifyObservers(null, SEARCH_SERVICE_TOPIC, "init-complete");
-
-    LOG("_init: Completed _init");
-    return this._initRV;
-  },
-
-  /**
-   * Obtains the remote settings for the search service. This should only be
-   * called from init(). Any subsequent updates to the remote settings are
-   * handled via a sync listener.
-   *
-   * For desktop, the initial remote settings are obtained from dumps in
-   * `services/settings/dumps/main/`. These are not shipped with Android, and
-   * hence the `get` may take a while to return.
-   */
-  async _setupRemoteSettings() {
-    const ignoreListSettings = RemoteSettings(SETTINGS_IGNORELIST_KEY);
-    // Trigger a get of the initial value.
-    const current = await ignoreListSettings.get();
-
-    // Now we have the values, listen for future updates.
-    this._ignoreListListener = this._handleIgnoreListUpdated.bind(this);
-    ignoreListSettings.on("sync", this._ignoreListListener);
-
-    await this._handleIgnoreListUpdated({data: {current}});
-    Services.obs.notifyObservers(null, SEARCH_SERVICE_TOPIC, "settings-update-complete");
-  },
-
-  /**
-   * This handles updating of the ignore list settings, and removing any ignored
-   * engines.
-   *
-   * @param {object} eventData
-   *   The event in the format received from RemoteSettings.
-   */
-  async _handleIgnoreListUpdated(eventData) {
-    LOG("_handleIgnoreListUpdated");
-    const {data: {current}} = eventData;
-
-    for (const entry of current) {
-      if (entry.id == "load-paths") {
-        this._loadPathIgnoreList = [...entry.matches];
-      } else if (entry.id == "submission-urls") {
-        this._submissionURLIgnoreList = [...entry.matches];
-      }
-    }
-
-    // If we have not finished initializing, then we wait for the initialization
-    // to complete.
-    if (!this.isInitialized) {
-      await this._initObservers;
-    }
-    // We try to remove engines manually, as this should be more efficient and
-    // we don't really want to cause a re-init as this upsets unit tests.
-    let engineRemoved = false;
-    for (let name in this._engines) {
-      let engine = this._engines[name];
-      if (this._engineMatchesIgnoreLists(engine)) {
-        await this.removeEngine(engine);
-        engineRemoved = true;
-      }
-    }
-    // If we've removed an engine, and we don't have any left, we need to do
-    // a re-init - it is possible the cache just had one engine in it, and that
-    // is now empty, so we need to load from our main list.
-    if (engineRemoved && !Object.keys(this._engines).length) {
-      this._reInit();
-    }
-  },
-
-  /**
-   * Determines if a given engine matches the ignorelists or not.
-   *
-   * @param {Engine} engine
-   *   The engine to check against the ignorelists.
-   * @returns {boolean}
-   *   Returns true if the engine matches a ignorelists entry.
-   */
-  _engineMatchesIgnoreLists(engine) {
-    if (this._loadPathIgnoreList.includes(engine._loadPath)) {
-      return true;
-    }
-    let url = engine._getURLOfType("text/html")
-                    .getSubmission("dummy", engine).uri.spec.toLowerCase();
-    if (this._submissionURLIgnoreList.some(code => url.includes(code.toLowerCase()))) {
-      return true;
-    }
-    return false;
-  },
-
-  _metaData: { },
-  setGlobalAttr(name, val) {
-    this._metaData[name] = val;
-    this.batchTask.disarm();
-    this.batchTask.arm();
-  },
-  setVerifiedGlobalAttr(name, val) {
-    this.setGlobalAttr(name, val);
-    this.setGlobalAttr(name + "Hash", getVerificationHash(val));
-  },
-
-  getGlobalAttr(name) {
-    return this._metaData[name] || undefined;
-  },
-  getVerifiedGlobalAttr(name) {
-    let val = this.getGlobalAttr(name);
-    if (val && this.getGlobalAttr(name + "Hash") != getVerificationHash(val)) {
-      LOG("getVerifiedGlobalAttr, invalid hash for " + name);
-      return "";
-    }
-    return val;
-  },
-
-  _listJSONURL: ((AppConstants.platform == "android") ? APP_SEARCH_PREFIX : EXT_SEARCH_PREFIX) + "list.json",
-
-  _engines: { },
-  __sortedEngines: null,
-  _visibleDefaultEngines: [],
-  _searchDefault: null,
-  _searchOrder: [],
-  // Stores a map of the built in engines installed and their params so
-  // they can be reconstructed in restarts.
-  _extensions: new Map(),
-
-  get _sortedEngines() {
-    if (!this.__sortedEngines)
-      return this._buildSortedEngineList();
-    return this.__sortedEngines;
-  },
-
-  // Get the original Engine object that is the default for this region,
-  // ignoring changes the user may have subsequently made.
-  get originalDefaultEngine() {
-    let defaultEngineName = this.getVerifiedGlobalAttr("searchDefault");
-    if (!defaultEngineName) {
-      // We only allow the old defaultenginename pref for distributions
-      // We can't use isPartnerBuild because we need to allow reading
-      // of the defaultengine name pref for funnelcakes.
-      if (distroID) {
-        let defaultPrefB = Services.prefs.getDefaultBranch(BROWSER_SEARCH_PREF);
-        let nsIPLS = Ci.nsIPrefLocalizedString;
-
-        try {
-          defaultEngineName = defaultPrefB.getComplexValue("defaultenginename", nsIPLS).data;
-        } catch (ex) {
-          // If the default pref is invalid (e.g. an add-on set it to a bogus value)
-          // use the default engine from the list.json.
-          // This should eventually be the common case. We should only have the
-          // defaultenginename pref for distributions.
-          // Worst case, getEngineByName will just return null, which is the best we can do.
-          defaultEngineName = this._searchDefault;
-        }
-      } else {
-        defaultEngineName = this._searchDefault;
-      }
-    }
-
-    let defaultEngine = this.getEngineByName(defaultEngineName);
-    if (!defaultEngine) {
-      // Something unexpected as happened. In order to recover the original default engine,
-      // use the first visible engine which us what currentEngine will use.
-      return this._getSortedEngines(false)[0];
-    }
-
-    return defaultEngine;
-  },
-
-  resetToOriginalDefaultEngine() {
-    let originalDefaultEngine = this.originalDefaultEngine;
-    originalDefaultEngine.hidden = false;
-    this.defaultEngine = originalDefaultEngine;
-  },
-
-  async _buildCache() {
-    if (this._batchTask)
-      this._batchTask.disarm();
-
-    let cache = {};
-    let locale = getLocale();
-    let buildID = Services.appinfo.platformBuildID;
-    let appVersion = Services.appinfo.version;
-
-    // Allows us to force a cache refresh should the cache format change.
-    cache.version = CACHE_VERSION;
-    // We don't want to incur the costs of stat()ing each plugin on every
-    // startup when the only (supported) time they will change is during
-    // app updates (where the buildID is obviously going to change).
-    // Extension-shipped plugins are the only exception to this, but their
-    // directories are blown away during updates, so we'll detect their changes.
-    cache.buildID = buildID;
-    // Store the appVersion as well so we can do extra things during major updates.
-    cache.appVersion = appVersion;
-    cache.locale = locale;
-
-    cache.visibleDefaultEngines = this._visibleDefaultEngines;
-    cache.metaData = this._metaData;
-    cache.engines = [];
-
-    for (let name in this._engines) {
-      cache.engines.push(this._engines[name]);
-    }
-
-    try {
-      if (!cache.engines.length)
-        throw new Error("cannot write without any engine.");
-
-      LOG("_buildCache: Writing to cache file.");
-      let path = OS.Path.join(OS.Constants.Path.profileDir, CACHE_FILENAME);
-      let data = gEncoder.encode(JSON.stringify(cache));
-      await OS.File.writeAtomic(path, data, {compression: "lz4",
-                                             tmpPath: path + ".tmp"});
-      LOG("_buildCache: cache file written to disk.");
-      Services.obs.notifyObservers(null, SEARCH_SERVICE_TOPIC, SEARCH_SERVICE_CACHE_WRITTEN);
-    } catch (ex) {
-      LOG("_buildCache: Could not write to cache file: " + ex);
-    }
-  },
-
-  /**
-   * Loads engines asynchronously.
-   *
-   * @returns {Promise} A promise, resolved successfully if loading data
-   * succeeds.
-   */
-  async _loadEngines(cache, isReload) {
-    LOG("_loadEngines: start");
-    Services.obs.notifyObservers(null, SEARCH_SERVICE_TOPIC, "find-jar-engines");
-    let engines = await this._findEngines();
-
-    // Get the non-empty distribution directories into distDirs...
-    let distDirs = [];
-    let locations;
-    try {
-      locations = getDir(NS_APP_DISTRIBUTION_SEARCH_DIR_LIST,
-                         Ci.nsISimpleEnumerator);
-    } catch (e) {
-      // NS_APP_DISTRIBUTION_SEARCH_DIR_LIST is defined by each app
-      // so this throws during unit tests (but not xpcshell tests).
-      locations = [];
-    }
-    for (let dir of locations) {
-      let iterator = new OS.File.DirectoryIterator(dir.path,
-                                                   { winPattern: "*.xml" });
-      try {
-        // Add dir to distDirs if it contains any files.
-        let {done} = await iterator.next();
-        if (!done) {
-          distDirs.push(dir);
-        }
-      } finally {
-        iterator.close();
-      }
-    }
-
-    function notInCacheVisibleEngines(engineName) {
-      return !cache.visibleDefaultEngines.includes(engineName);
-    }
-
-    // Parse the engine name into the extension name + locale pair, some engines
-    // will be exempt (ie yahoo-jp-auctions), can turn this from a whitelist to a
-    // blacklist when more engines are multilocale than not.
-    function parseEngineName(engineName) {
-      let [name, locale] = engineName.split(/-(.+)/);
-
-      if (!MULTI_LOCALE_ENGINES.includes(name)) {
-        return [engineName, DEFAULT_TAG];
-      }
-
-      if (!locale) {
-        locale = DEFAULT_TAG;
-      }
-      return [name, locale];
-    }
-
-    function extensionId(name) {
-      return name + "@" + EXT_SIGNING_ADDRESS;
-    }
-
-    let buildID = Services.appinfo.platformBuildID;
-    let rebuildCache = !cache.engines ||
-                       cache.version != CACHE_VERSION ||
-                       cache.locale != getLocale() ||
-                       cache.buildID != buildID ||
-                       cache.visibleDefaultEngines.length != this._visibleDefaultEngines.length ||
-                       this._visibleDefaultEngines.some(notInCacheVisibleEngines);
-
-    // If we are reiniting, delete previously installed built in
-    // extensions that arent in the current engine list.
-    for (let id of this._extensions.keys()) {
-      let policy = WebExtensionPolicy.getByID(id);
-      if (!policy) {
-        // If we are reiniting due to a remote settings update, we may have
-        // removed all the engines and be rebuilding without the cache. In this
-        // case, we won't have the cached engines loaded, so just skip this check.
-        continue;
-      }
-      let extension = policy.extension;
-      if (extension.addonData.builtIn && !engines.some(name => extensionId(name) === id)) {
-        this._extensions.delete(id);
-      }
-    }
-
-    // Turn our engine list into a list of extension names + the locales
-    // to be installed.
-    for (let engine of engines) {
-      let [extensionName, locale] = parseEngineName(engine);
-      let id = extensionId(extensionName);
-      let localeMap = this._extensions.get(id) || new Map();
-      let params = localeMap.get(locale);
-
-      if (!params) {
-        localeMap.set(locale, !rebuildCache);
-        this._extensions.set(id, localeMap);
-      }
-    }
-
-    if (!rebuildCache) {
-      LOG("_loadEngines: loading from cache directories");
-      this._loadEnginesFromCache(cache);
-      if (Object.keys(this._engines).length) {
-        LOG("_loadEngines: done using existing cache");
-        return;
-      }
-      LOG("_loadEngines: No valid engines found in cache. Loading engines from disk.");
-    }
-
-    LOG("_loadEngines: Absent or outdated cache. Loading engines from disk.");
-    for (let loadDir of distDirs) {
-      let enginesFromDir = await this._loadEnginesFromDir(loadDir);
-      enginesFromDir.forEach(this._addEngineToStore, this);
-    }
-    if (AppConstants.platform == "android") {
-      let enginesFromURLs = await this._loadFromChromeURLs(engines, isReload);
-      enginesFromURLs.forEach(this._addEngineToStore, this);
-    } else {
-      for (let [id, localeMap] of this._extensions) {
-        let policy = WebExtensionPolicy.getByID(id);
-        if (policy) {
-          LOG("_loadEngines: Found previously installed extension");
-          await this.addEnginesFromExtension(policy.extension);
-        } else {
-          LOG("_loadEngines: Installing " + id);
-          // We may have marked these as loading from the cache previously
-          // but if there wasnt an valid cache, mark as installing.
-          for (let [locale, installed] of localeMap) {
-            if (installed === true) {
-              localeMap.set(locale, null);
-            }
-          }
-          this._extensions.set(id, localeMap);
-          let path = EXT_SEARCH_PREFIX + id.split("@")[0] + "/";
-          await AddonManager.installBuiltinAddon(path);
-          // The AddonManager will install the engine asynchronously
-          // We can manually wait on that happening here.
-          await ExtensionParent.apiManager.global.pendingSearchSetupTasks.get(id);
-          LOG("_loadEngines: " + id + " installed");
-        }
-      }
-    }
-
-    LOG("_loadEngines: loading user-installed engines from the obsolete cache");
-    this._loadEnginesFromCache(cache, true);
-
-    this._loadEnginesMetadataFromCache(cache);
-
-    LOG("_loadEngines: done using rebuilt cache");
-  },
-
-  /**
-   * Reloads engines asynchronously, but only when the service has already been
-   * initialized.
-   *
-   * @return {Promise} A promise, resolved successfully if loading data succeeds.
-   */
-  async _maybeReloadEngines() {
-    // There's no point in already reloading the list of engines, when the service
-    // hasn't even initialized yet.
-    if (!gInitialized) {
-      return;
-    }
-
-    // Before we read the cache file, first make sure all pending tasks are clear.
-    if (this._batchTask) {
-      let task = this._batchTask;
-      this._batchTask = null;
-      await task.finalize();
-    }
-    // Capture the current engine state, in case we need to notify below.
-    let prevCurrentEngine = this._currentEngine;
-    this._currentEngine = null;
-
-    await this._loadEngines(await this._readCacheFile(), true);
-    // Make sure the current list of engines is persisted.
-    await this._buildCache();
-
-    // If the defaultEngine has changed between the previous load and this one,
-    // dispatch the appropriate notifications.
-    if (prevCurrentEngine && this.defaultEngine !== prevCurrentEngine) {
-      notifyAction(this._currentEngine, SEARCH_ENGINE_DEFAULT);
-    }
-    Services.obs.notifyObservers(null, SEARCH_SERVICE_TOPIC, "engines-reloaded");
-  },
-
-  _reInit(origin, skipRegionCheck = true) {
-    LOG("_reInit");
-    // Re-entrance guard, because we're using an async lambda below.
-    if (gReinitializing) {
-      LOG("_reInit: already re-initializing, bailing out.");
-      return;
-    }
-    gReinitializing = true;
-
-    // Start by clearing the initialized state, so we don't abort early.
-    gInitialized = false;
-
-    (async () => {
-      try {
-        this._initObservers = PromiseUtils.defer();
-        if (this._batchTask) {
-          LOG("finalizing batch task");
-          let task = this._batchTask;
-          this._batchTask = null;
-          // Tests manipulate the cache directly, so let's not double-write with
-          // stale cache data here.
-          if (origin == "test") {
-            task.disarm();
-          } else {
-            await task.finalize();
-          }
-        }
-
-        // Clear the engines, too, so we don't stick with the stale ones.
-        this._engines = {};
-        this.__sortedEngines = null;
-        this._currentEngine = null;
-        this._visibleDefaultEngines = [];
-        this._searchDefault = null;
-        this._searchOrder = [];
-        this._metaData = {};
-
-        // Tests that want to force a synchronous re-initialization need to
-        // be notified when we are done uninitializing.
-        Services.obs.notifyObservers(null, SEARCH_SERVICE_TOPIC,
-                                     "uninit-complete");
-
-        let cache = await this._readCacheFile();
-        // The init flow is not going to block on a fetch from an external service,
-        // but we're kicking it off as soon as possible to prevent UI flickering as
-        // much as possible.
-        this._ensureKnownRegionPromise = ensureKnownRegion(this)
-          .catch(ex => LOG("_reInit: failure determining region: " + ex))
-          .finally(() => this._ensureKnownRegionPromise = null);
-
-        if (!skipRegionCheck) {
-          await this._ensureKnownRegionPromise;
-        }
-
-        await this._loadEngines(cache);
-        // Make sure the current list of engines is persisted.
-        await this._buildCache();
-
-        // Typically we'll re-init as a result of a pref observer,
-        // so signal to 'callers' that we're done.
-        gInitialized = true;
-        this._initObservers.resolve();
-        Services.obs.notifyObservers(null, SEARCH_SERVICE_TOPIC, "init-complete");
-      } catch (err) {
-        LOG("Reinit failed: " + err);
-        LOG(err.stack);
-        Services.obs.notifyObservers(null, SEARCH_SERVICE_TOPIC, "reinit-failed");
-      } finally {
-        gReinitializing = false;
-        Services.obs.notifyObservers(null, SEARCH_SERVICE_TOPIC, "reinit-complete");
-      }
-    })();
-  },
-
-  /**
-   * Reset SearchService data.
-   */
-  reset() {
-    gInitialized = false;
-    this._extensions = new Map();
-    this._startupExtensions = new Map();
-  },
-
-  /**
-   * Read the cache file asynchronously.
-   *
-   * @returns {Promise} A promise, resolved successfully if retrieveing data
-   * succeeds.
-   */
-  async _readCacheFile() {
-    let json;
-    try {
-      let cacheFilePath = OS.Path.join(OS.Constants.Path.profileDir, CACHE_FILENAME);
-      let bytes = await OS.File.read(cacheFilePath, {compression: "lz4"});
-      json = JSON.parse(new TextDecoder().decode(bytes));
-      if (!json.engines || !json.engines.length)
-        throw new Error("no engine in the file");
-      // Reset search default expiration on major releases
-      if (json.appVersion != Services.appinfo.version &&
-          geoSpecificDefaultsEnabled() &&
-          json.metaData) {
-        json.metaData.searchDefaultExpir = 0;
-      }
-    } catch (ex) {
-      LOG("_readCacheFile: Error reading cache file: " + ex);
-      json = {};
-    }
-    if (!gInitialized && json.metaData)
-      this._metaData = json.metaData;
-
-    return json;
-  },
-
-  _batchTask: null,
-  get batchTask() {
-    if (!this._batchTask) {
-      let task = async () => {
-        LOG("batchTask: Invalidating engine cache");
-        await this._buildCache();
-      };
-      this._batchTask = new DeferredTask(task, CACHE_INVALIDATION_DELAY);
-    }
-    return this._batchTask;
-  },
-
-  _addEngineToStore(engine) {
-    if (this._engineMatchesIgnoreLists(engine)) {
-      LOG("_addEngineToStore: Ignoring engine");
-      return;
-    }
-
-    LOG("_addEngineToStore: Adding engine: \"" + engine.name + "\"");
-
-    // See if there is an existing engine with the same name. However, if this
-    // engine is updating another engine, it's allowed to have the same name.
-    var hasSameNameAsUpdate = (engine._engineToUpdate &&
-                               engine.name == engine._engineToUpdate.name);
-    if (engine.name in this._engines && !hasSameNameAsUpdate) {
-      LOG("_addEngineToStore: Duplicate engine found, aborting!");
-      return;
-    }
-
-    if (engine._engineToUpdate) {
-      // We need to replace engineToUpdate with the engine that just loaded.
-      var oldEngine = engine._engineToUpdate;
-
-      // Remove the old engine from the hash, since it's keyed by name, and our
-      // name might change (the update might have a new name).
-      delete this._engines[oldEngine.name];
-
-      // Hack: we want to replace the old engine with the new one, but since
-      // people may be holding refs to the nsISearchEngine objects themselves,
-      // we'll just copy over all "private" properties (those without a getter
-      // or setter) from one object to the other.
-      for (var p in engine) {
-        if (!(engine.__lookupGetter__(p) || engine.__lookupSetter__(p)))
-          oldEngine[p] = engine[p];
-      }
-      engine = oldEngine;
-      engine._engineToUpdate = null;
-
-      // Add the engine back
-      this._engines[engine.name] = engine;
-      notifyAction(engine, SEARCH_ENGINE_CHANGED);
-    } else {
-      // Not an update, just add the new engine.
-      this._engines[engine.name] = engine;
-      // Only add the engine to the list of sorted engines if the initial list
-      // has already been built (i.e. if this.__sortedEngines is non-null). If
-      // it hasn't, we're loading engines from disk and the sorted engine list
-      // will be built once we need it.
-      if (this.__sortedEngines) {
-        this.__sortedEngines.push(engine);
-        this._saveSortedEngineList();
-      }
-      notifyAction(engine, SEARCH_ENGINE_ADDED);
-    }
-
-    if (engine._hasUpdates) {
-      // Schedule the engine's next update, if it isn't already.
-      if (!engine.getAttr("updateexpir"))
-        engineUpdateService.scheduleNextUpdate(engine);
-    }
-  },
-
-  _loadEnginesMetadataFromCache(cache) {
-    if (!cache.engines)
-      return;
-
-    for (let engine of cache.engines) {
-      let name = engine._name;
-      if (name in this._engines) {
-        LOG("_loadEnginesMetadataFromCache, transfering metadata for " + name);
-        this._engines[name]._metaData = engine._metaData || {};
-      }
-    }
-  },
-
-  _loadEnginesFromCache(cache, skipReadOnly) {
-    if (!cache.engines)
-      return;
-
-    LOG("_loadEnginesFromCache: Loading " +
-        cache.engines.length + " engines from cache");
-
-    let skippedEngines = 0;
-    for (let engine of cache.engines) {
-      if (skipReadOnly && engine._readOnly == undefined) {
-        ++skippedEngines;
-        continue;
-      }
-
-      this._loadEngineFromCache(engine);
-    }
-
-    if (skippedEngines) {
-      LOG("_loadEnginesFromCache: skipped " + skippedEngines + " read-only engines.");
-    }
-  },
-
-  _loadEngineFromCache(json) {
-    try {
-      let engine = new Engine(json._shortName, json._readOnly == undefined);
-      engine._initWithJSON(json);
-      this._addEngineToStore(engine);
-    } catch (ex) {
-      LOG("Failed to load " + json._name + " from cache: " + ex);
-      LOG("Engine JSON: " + json.toSource());
-    }
-  },
-
-  /**
-   * Loads engines from a given directory asynchronously.
-   *
-   * @param {OS.File} dir the directory.
-   *
-   * @returns {Promise} A promise, resolved successfully if retrieveing data
-   * succeeds.
-   */
-  async _loadEnginesFromDir(dir) {
-    LOG("_loadEnginesFromDir: Searching in " + dir.path + " for search engines.");
-
-    let iterator = new OS.File.DirectoryIterator(dir.path);
-
-    let osfiles = await iterator.nextBatch();
-    iterator.close();
-
-    let engines = [];
-    for (let osfile of osfiles) {
-      if (osfile.isDir || osfile.isSymLink)
-        continue;
-
-      let fileInfo = await OS.File.stat(osfile.path);
-      if (fileInfo.size == 0)
-        continue;
-
-      let parts = osfile.path.split(".");
-      if (parts.length <= 1 || (parts.pop()).toLowerCase() != "xml") {
-        // Not an engine
-        continue;
-      }
-
-      let addedEngine = null;
-      try {
-        let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
-        file.initWithPath(osfile.path);
-        addedEngine = new Engine(file, false);
-        await addedEngine._initFromFile(file);
-        engines.push(addedEngine);
-      } catch (ex) {
-        LOG("_loadEnginesFromDir: Failed to load " + osfile.path + "!\n" + ex);
-      }
-    }
-    return engines;
-  },
-
-  /**
-   * Loads engines from Chrome URLs asynchronously.
-   *
-   * @param {array} urls
-   *   a list of URLs.
-   * @param {boolean} [isReload]
-   *   is being called from maybeReloadEngines.
-   * @returns {Promise} A promise, resolved successfully if loading data
-   * succeeds.
-   */
-  async _loadFromChromeURLs(urls, isReload = false) {
-    let engines = [];
-    for (let url of urls) {
-      try {
-        LOG("_loadFromChromeURLs: loading engine from chrome url: " + url);
-        let uri = Services.io.newURI(APP_SEARCH_PREFIX + url + ".xml");
-        let engine = new Engine(uri, true);
-        await engine._initFromURI(uri);
-        // If there is an existing engine with the same name then update that engine.
-        // Only do this during reloads so it doesnt interfere with distribution
-        // engines
-        if (isReload && engine.name in this._engines) {
-          engine._engineToUpdate = this._engines[engine.name];
-        }
-        engines.push(engine);
-      } catch (ex) {
-        LOG("_loadFromChromeURLs: failed to load engine: " + ex);
-      }
-    }
-    return engines;
-  },
-
-  /**
-   * Loads jar engines asynchronously.
-   *
-   * @returns {Promise} A promise, resolved successfully if finding jar engines
-   * succeeds.
-   */
-  async _findEngines() {
-    LOG("_findEngines: looking for engines in JARs");
-
-    let chan = makeChannel(this._listJSONURL);
-    if (!chan) {
-      LOG("_findEngines: " + this._listJSONURL + " isn't registered");
-      return [];
-    }
-
-    let uris = [];
-
-    // Read list.json to find the engines we need to load.
-    let request = new XMLHttpRequest();
-    request.overrideMimeType("text/plain");
-    let list = await new Promise(resolve => {
-      request.onload = function(event) {
-        resolve(event.target.responseText);
-      };
-      request.onerror = function(event) {
-        LOG("_findEngines: failed to read " + this._listJSONURL);
-        resolve();
-      };
-      request.open("GET", Services.io.newURI(this._listJSONURL).spec, true);
-      request.send();
-    });
-
-    this._parseListJSON(list, uris);
-    return uris;
-  },
-
-  _parseListJSON(list, uris) {
-    let json;
-    try {
-      json = JSON.parse(list);
-    } catch (e) {
-      Cu.reportError("parseListJSON: Failed to parse list.json: " + e);
-      dump("parseListJSON: Failed to parse list.json: " + e + "\n");
-      return;
-    }
-
-    let searchRegion = Services.prefs.getCharPref("browser.search.region", null);
-
-    let searchSettings;
-    let locale = Services.locale.appLocaleAsBCP47;
-    if ("locales" in json &&
-        locale in json.locales) {
-      searchSettings = json.locales[locale];
-    } else {
-      // No locales were found, so use the JSON as is.
-      // It should have a default section.
-      if (!("default" in json)) {
-        Cu.reportError("parseListJSON: Missing default in list.json");
-        dump("parseListJSON: Missing default in list.json\n");
-        return;
-      }
-      searchSettings = json;
-    }
-
-    // Check if we have a useable region specific list of visible default engines.
-    // This will only be set if we got the list from the Mozilla search server;
-    // it will not be set for distributions.
-    let engineNames;
-    let visibleDefaultEngines = this.getVerifiedGlobalAttr("visibleDefaultEngines");
-    if (visibleDefaultEngines) {
-      let jarNames = new Set();
-      for (let region in searchSettings) {
-        // Artifact builds use the full list.json which parses
-        // slightly differently
-        if (!("visibleDefaultEngines" in searchSettings[region])) {
-          continue;
-        }
-        for (let engine of searchSettings[region].visibleDefaultEngines) {
-          jarNames.add(engine);
-        }
-        if ("regionOverrides" in json &&
-            searchRegion in json.regionOverrides) {
-          for (let engine in json.regionOverrides[searchRegion]) {
-            jarNames.add(json.regionOverrides[searchRegion][engine]);
-          }
-        }
-      }
-
-      engineNames = visibleDefaultEngines.split(",");
-      // absearch can't be modified to use the new engine names.
-      // Convert them here.
-      engineNames = convertGoogleEngines(engineNames);
-
-      for (let engineName of engineNames) {
-        // If all engineName values are part of jarNames,
-        // then we can use the region specific list, otherwise ignore it.
-        // The visibleDefaultEngines string containing the name of an engine we
-        // don't ship indicates the server is misconfigured to answer requests
-        // from the specific Firefox version we are running, so ignoring the
-        // value altogether is safer.
-        if (!jarNames.has(engineName)) {
-          LOG("_parseListJSON: ignoring visibleDefaultEngines value because " +
-              engineName + " is not in the jar engines we have found");
-          engineNames = null;
-          break;
-        }
-      }
-    }
-
-    // Fallback to building a list based on the regions in the JSON
-    if (!engineNames || !engineNames.length) {
-      if (searchRegion && searchRegion in searchSettings &&
-          "visibleDefaultEngines" in searchSettings[searchRegion]) {
-        engineNames = searchSettings[searchRegion].visibleDefaultEngines;
-      } else {
-        engineNames = searchSettings.default.visibleDefaultEngines;
-      }
-    }
-
-    // Remove any engine names that are supposed to be ignored.
-    // This pref is only allowed in a partner distribution.
-    let branch = Services.prefs.getDefaultBranch(BROWSER_SEARCH_PREF);
-    if (isPartnerBuild() &&
-        branch.getPrefType("ignoredJAREngines") == branch.PREF_STRING) {
-      let ignoredJAREngines = branch.getCharPref("ignoredJAREngines")
-                                    .split(",");
-      let filteredEngineNames = engineNames.filter(e => !ignoredJAREngines.includes(e));
-      // Don't allow all engines to be hidden
-      if (filteredEngineNames.length > 0) {
-        engineNames = filteredEngineNames;
-      }
-    }
-
-    if ("regionOverrides" in json &&
-        searchRegion in json.regionOverrides) {
-      for (let engine in json.regionOverrides[searchRegion]) {
-        let index = engineNames.indexOf(engine);
-        if (index > -1) {
-          engineNames[index] = json.regionOverrides[searchRegion][engine];
-        }
-      }
-    }
-
-    // ESR uses different codes. Convert them here.
-    if (AppConstants.MOZ_APP_VERSION_DISPLAY.endsWith("esr")) {
-      let esrOverrides = {
-        "google-b-d": "google-b-e",
-        "google-b-1-d": "google-b-1-e",
-      };
-
-      for (let engine in esrOverrides) {
-        let index = engineNames.indexOf(engine);
-        if (index > -1) {
-          engineNames[index] = esrOverrides[engine];
-        }
-      }
-    }
-
-    for (let name of engineNames) {
-      uris.push(name);
-    }
-
-    // Store this so that it can be used while writing the cache file.
-    this._visibleDefaultEngines = engineNames;
-
-    if (searchRegion && searchRegion in searchSettings &&
-        "searchDefault" in searchSettings[searchRegion]) {
-      this._searchDefault = searchSettings[searchRegion].searchDefault;
-    } else if ("searchDefault" in searchSettings.default) {
-      this._searchDefault = searchSettings.default.searchDefault;
-    } else {
-      this._searchDefault = json.default.searchDefault;