Merge autoland to mozilla-central. a=merge
authorDaniel Varga <dvarga@mozilla.com>
Wed, 10 Oct 2018 13:52:23 +0300
changeset 488810 91b4c3687d7563244fbba0f58075779eb89259fb
parent 488784 2d2dee08739f0293e1ac9e815a9acb80621c3bc4 (current diff)
parent 488809 c9d9dd203994cd2251869d7b470564281d5d995f (diff)
child 488811 857fb59a720e8f04e5d4f6ba60d4c9428f459751
child 488824 8dfeff72def34dea3ee1a59185a73d2840ae77f3
child 488835 08d597127e937fbcb9f2929c6cd772ed93bfd7d9
push id246
push userfmarier@mozilla.com
push dateSat, 13 Oct 2018 00:15:40 +0000
reviewersmerge
milestone64.0a1
Merge autoland to mozilla-central. a=merge
--- a/browser/actors/WebRTCChild.jsm
+++ b/browser/actors/WebRTCChild.jsm
@@ -235,17 +235,23 @@ function prompt(aContentWindow, aWindowI
     videoDevices,
   };
 
   let mm = getMessageManagerForWindow(aContentWindow);
   mm.sendAsyncMessage("webrtc:Request", request);
 }
 
 function denyGUMRequest(aData) {
-  Services.obs.notifyObservers(null, "getUserMedia:response:deny", aData.callID);
+  let subject;
+  if (aData.noOSPermission) {
+    subject = "getUserMedia:response:noOSPermission";
+  } else {
+    subject = "getUserMedia:response:deny";
+  }
+  Services.obs.notifyObservers(null, subject, aData.callID);
 
   if (!aData.windowID)
     return;
   let contentWindow = Services.wm.getOuterWindowWithId(aData.windowID);
   if (contentWindow.pendingGetUserMediaRequests)
     forgetGUMRequest(contentWindow, aData.callID);
 }
 
--- a/browser/base/content/test/urlbar/browser.ini
+++ b/browser/base/content/test/urlbar/browser.ini
@@ -94,16 +94,20 @@ skip-if = os == "linux" # Bug 1073339 - 
 [browser_urlbarOneOffs.js]
 support-files =
   searchSuggestionEngine.xml
   searchSuggestionEngine.sjs
 [browser_urlbarOneOffs_searchSuggestions.js]
 support-files =
   searchSuggestionEngine.xml
   searchSuggestionEngine.sjs
+[browser_urlbarOneOffs_settings.js]
+support-files =
+  searchSuggestionEngine.xml
+  searchSuggestionEngine.sjs
 [browser_urlbarPlaceholder.js]
 support-files =
   searchSuggestionEngine.xml
   searchSuggestionEngine.sjs
 [browser_urlbarPrivateBrowsingWindowChange.js]
 [browser_urlbarRaceWithTabs.js]
 [browser_urlbarRevert.js]
 [browser_urlbarSearchFunction.js]
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/urlbar/browser_urlbarOneOffs_settings.js
@@ -0,0 +1,78 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+const TEST_ENGINE_BASENAME = "searchSuggestionEngine.xml";
+
+let gMaxResults;
+
+add_task(async function init() {
+  Services.prefs.setBoolPref("browser.urlbar.oneOffSearches", true);
+  gMaxResults = Services.prefs.getIntPref("browser.urlbar.maxRichResults");
+
+  // Add a search suggestion engine and move it to the front so that it appears
+  // as the first one-off.
+  let engine = await SearchTestUtils.promiseNewSearchEngine(
+    getRootDirectory(gTestPath) + TEST_ENGINE_BASENAME);
+  Services.search.moveEngine(engine, 0);
+
+  registerCleanupFunction(async function() {
+    await hidePopup();
+    await PlacesUtils.history.clear();
+  });
+
+  await PlacesUtils.history.clear();
+
+  let visits = [];
+  for (let i = 0; i < gMaxResults; i++) {
+    visits.push({
+      uri: makeURI("http://example.com/browser_urlbarOneOffs.js/?" + i),
+      // TYPED so that the visit shows up when the urlbar's drop-down arrow is
+      // pressed.
+      transition: Ci.nsINavHistoryService.TRANSITION_TYPED,
+    });
+  }
+  await PlacesTestUtils.addVisits(visits);
+});
+
+
+async function selectSettings(activateFn) {
+  await BrowserTestUtils.withNewTab({gBrowser, url: "about:blank"}, async browser => {
+    gURLBar.focus();
+    EventUtils.synthesizeKey("KEY_ArrowDown");
+    await promisePopupShown(gURLBar.popup);
+    await waitForAutocompleteResultAt(gMaxResults - 1);
+
+    let promiseHidden = promisePopupHidden(gURLBar.popup);
+    let prefPaneLoaded = TestUtils.topicObserved("sync-pane-loaded", () => true);
+
+    activateFn();
+
+    await prefPaneLoaded;
+    await promiseHidden;
+
+    Assert.equal(gBrowser.contentWindow.history.state, "paneSearch",
+      "Should have opened the search preferences pane");
+  });
+}
+
+add_task(async function test_open_settings_with_enter() {
+  await selectSettings(() => {
+    EventUtils.synthesizeKey("KEY_ArrowUp");
+
+    Assert.equal(gURLBar.popup.oneOffSearchButtons.selectedButton.getAttribute("anonid"),
+      "search-settings-compact", "Should have selected the settings button");
+
+    EventUtils.synthesizeKey("KEY_Enter");
+  });
+});
+
+add_task(async function test_open_settings_with_click() {
+  await selectSettings(() => {
+    gURLBar.popup.oneOffSearchButtons.settingsButton.click();
+  });
+});
+
+async function hidePopup() {
+  EventUtils.synthesizeKey("KEY_Escape");
+  await promisePopupHidden(gURLBar.popup);
+}
--- a/browser/base/content/urlbarBindings.xml
+++ b/browser/base/content/urlbarBindings.xml
@@ -647,19 +647,16 @@ file, You can obtain one at http://mozil
         <parameter name="triggeringPrincipal"/>
         <body><![CDATA[
           let isMouseEvent = event instanceof MouseEvent;
           if (isMouseEvent && event.button == 2) {
             // Do nothing for right clicks.
             return;
           }
 
-          BrowserUsageTelemetry.recordUrlbarSelectedResultMethod(
-            event, this.userSelectionBehavior);
-
           // Determine whether to use the selected one-off search button.  In
           // one-off search buttons parlance, "selected" means that the button
           // has been navigated to via the keyboard.  So we want to use it if
           // the triggering event is not a mouse click -- i.e., it's a Return
           // key -- or if the one-off was mouse-clicked.
           let selectedOneOff = this.popup.oneOffSearchButtons.selectedButton;
           if (selectedOneOff &&
               isMouseEvent &&
@@ -668,16 +665,19 @@ file, You can obtain one at http://mozil
           }
 
           // Do the command of the selected one-off if it's not an engine.
           if (selectedOneOff && !selectedOneOff.engine) {
             selectedOneOff.doCommand();
             return;
           }
 
+          BrowserUsageTelemetry.recordUrlbarSelectedResultMethod(
+            event, this.userSelectionBehavior);
+
           let where = openUILinkWhere || this._whereToOpen(event);
 
           let url = this.value;
           if (!url) {
             return;
           }
 
           let mayInheritPrincipal = false;
--- a/browser/modules/webrtcUI.jsm
+++ b/browser/modules/webrtcUI.jsm
@@ -18,16 +18,20 @@ ChromeUtils.defineModuleGetter(this, "Pr
                                "resource://gre/modules/PrivateBrowsingUtils.jsm");
 ChromeUtils.defineModuleGetter(this, "SitePermissions",
                                "resource:///modules/SitePermissions.jsm");
 
 XPCOMUtils.defineLazyGetter(this, "gBrandBundle", function() {
   return Services.strings.createBundle("chrome://branding/locale/brand.properties");
 });
 
+XPCOMUtils.defineLazyServiceGetter(this, "OSPermissions",
+                                   "@mozilla.org/ospermissionrequest;1",
+                                   "nsIOSPermissionRequest");
+
 var webrtcUI = {
   peerConnectionBlockers: new Set(),
   emitter: new EventEmitter(),
 
   init() {
     Services.obs.addObserver(maybeAddMenuIndicator, "browser-delayed-startup-finished");
     Services.ppmm.addMessageListener("child-process-shutdown", this);
   },
@@ -295,16 +299,77 @@ var webrtcUI = {
 };
 
 function denyRequest(aBrowser, aRequest) {
   aBrowser.messageManager.sendAsyncMessage("webrtc:Deny",
                                            {callID: aRequest.callID,
                                             windowID: aRequest.windowID});
 }
 
+//
+// Deny the request because the browser does not have access to the
+// camera or microphone due to OS security restrictions. The user may
+// have granted camera/microphone access to the site, but not have
+// allowed the browser access in OS settings.
+//
+function denyRequestNoPermission(aBrowser, aRequest) {
+  aBrowser.messageManager.sendAsyncMessage("webrtc:Deny",
+                                           {callID: aRequest.callID,
+                                            windowID: aRequest.windowID,
+                                            noOSPermission: true});
+}
+
+//
+// Check if we have permission to access the camera and or microphone at the
+// OS level. Triggers a request to access the device if access is needed and
+// the permission state has not yet been determined.
+//
+async function checkOSPermission(camNeeded, micNeeded) {
+  let camStatus = {}, micStatus = {};
+  OSPermissions.getMediaCapturePermissionState(camStatus, micStatus);
+  if (camNeeded) {
+    let camPermission = camStatus.value;
+    let camAccessible = await checkAndGetOSPermission(camPermission,
+      OSPermissions.requestVideoCapturePermission);
+    if (!camAccessible) {
+      return false;
+    }
+  }
+  if (micNeeded) {
+    let micPermission = micStatus.value;
+    let micAccessible = await checkAndGetOSPermission(micPermission,
+      OSPermissions.requestAudioCapturePermission);
+    if (!micAccessible) {
+      return false;
+    }
+  }
+  return true;
+}
+
+//
+// Given a device's permission, return true if the device is accessible. If
+// the device's permission is not yet determined, request access to the device.
+// |requestPermissionFunc| must return a promise that resolves with true
+// if the device is accessible and false otherwise.
+//
+async function checkAndGetOSPermission(devicePermission,
+                                       requestPermissionFunc) {
+  if (devicePermission == OSPermissions.PERMISSION_STATE_DENIED ||
+      devicePermission == OSPermissions.PERMISSION_STATE_RESTRICTED) {
+    return false;
+  }
+  if (devicePermission == OSPermissions.PERMISSION_STATE_NOTDETERMINED) {
+    let deviceAllowed = await requestPermissionFunc();
+    if (!deviceAllowed) {
+      return false;
+    }
+  }
+  return true;
+}
+
 function getHostOrExtensionName(uri, href) {
   let host;
   try {
     if (!uri) {
       uri = Services.io.newURI(href);
     }
 
     let addonPolicy = WebExtensionPolicy.getByURI(uri);
@@ -512,20 +577,29 @@ function prompt(aBrowser, aRequest) {
           // Remember on which URIs we found persistent permissions so that we
           // can remove them if the user clicks 'Stop Sharing'. There's no
           // other way for the stop sharing code to know the hostnames of frames
           // using devices until bug 1066082 is fixed.
           let browser = this.browser;
           browser._devicePermissionURIs = browser._devicePermissionURIs || [];
           browser._devicePermissionURIs.push(uri);
 
-          let mm = browser.messageManager;
-          mm.sendAsyncMessage("webrtc:Allow", {callID: aRequest.callID,
-                                               windowID: aRequest.windowID,
-                                               devices: allowedDevices});
+          let camNeeded = videoDevices.length > 0;
+          let micNeeded = audioDevices.length > 0;
+          checkOSPermission(camNeeded, micNeeded).then((havePermission) => {
+            if (havePermission) {
+              let mm = browser.messageManager;
+              mm.sendAsyncMessage("webrtc:Allow", {callID: aRequest.callID,
+                                                   windowID: aRequest.windowID,
+                                                   devices: allowedDevices});
+            } else {
+              denyRequestNoPermission(browser, aRequest);
+            }
+          });
+
           this.remove();
           return true;
         }
       }
 
       function listDevices(menupopup, devices) {
         while (menupopup.lastChild)
           menupopup.removeChild(menupopup.lastChild);
@@ -712,17 +786,17 @@ function prompt(aBrowser, aRequest) {
       } else {
         listDevices(camMenupopup, videoDevices);
         doc.getElementById("webRTC-shareDevices-notification").removeAttribute("invalidselection");
       }
 
       if (!sharingAudio)
         listDevices(micMenupopup, audioDevices);
 
-      this.mainAction.callback = function(aState) {
+      this.mainAction.callback = async function(aState) {
         let remember = aState && aState.checkboxChecked;
         let allowedDevices = [];
         let perms = Services.perms;
         if (videoDevices.length) {
           let listId = "webRTC-select" + (sharingScreen ? "Window" : "Camera") + "-menulist";
           let videoDeviceIndex = doc.getElementById(listId).value;
           let allowVideoDevice = videoDeviceIndex != "-1";
           if (allowVideoDevice) {
@@ -779,16 +853,24 @@ function prompt(aBrowser, aRequest) {
 
         if (remember) {
           // Remember on which URIs we set persistent permissions so that we
           // can remove them if the user clicks 'Stop Sharing'.
           aBrowser._devicePermissionURIs = aBrowser._devicePermissionURIs || [];
           aBrowser._devicePermissionURIs.push(uri);
         }
 
+        let camNeeded = videoDevices.length > 0;
+        let micNeeded = audioDevices.length > 0;
+        let havePermission = await checkOSPermission(camNeeded, micNeeded);
+        if (!havePermission) {
+          denyRequestNoPermission(notification.browser, aRequest);
+          return;
+        }
+
         let mm = notification.browser.messageManager;
         mm.sendAsyncMessage("webrtc:Allow", {callID: aRequest.callID,
                                              windowID: aRequest.windowID,
                                              devices: allowedDevices});
       };
       return false;
     },
   };
--- a/devtools/client/netmonitor/src/components/ResponsePanel.js
+++ b/devtools/client/netmonitor/src/components/ResponsePanel.js
@@ -25,17 +25,17 @@ const JSON_FILTER_TEXT = L10N.getStr("js
 const RESPONSE_IMG_NAME = L10N.getStr("netmonitor.response.name");
 const RESPONSE_IMG_DIMENSIONS = L10N.getStr("netmonitor.response.dimensions");
 const RESPONSE_IMG_MIMETYPE = L10N.getStr("netmonitor.response.mime");
 const RESPONSE_PAYLOAD = L10N.getStr("responsePayload");
 const RESPONSE_PREVIEW = L10N.getStr("responsePreview");
 
 const JSON_VIEW_MIME_TYPE = "application/vnd.mozilla.json.view";
 
-/*
+/**
  * Response panel component
  * Displays the GET parameters and POST data of a request
  */
 class ResponsePanel extends Component {
   static get propTypes() {
     return {
       request: PropTypes.object.isRequired,
       openLink: PropTypes.func,
@@ -48,53 +48,77 @@ class ResponsePanel extends Component {
 
     this.state = {
       imageDimensions: {
         width: 0,
         height: 0,
       },
     };
 
-    this.updateImageDimemsions = this.updateImageDimemsions.bind(this);
-    this.isJSON = this.isJSON.bind(this);
+    this.updateImageDimensions = this.updateImageDimensions.bind(this);
   }
 
   componentDidMount() {
     const { request, connector } = this.props;
     fetchNetworkUpdatePacket(connector.requestData, request, ["responseContent"]);
   }
 
   componentWillReceiveProps(nextProps) {
     const { request, connector } = nextProps;
     fetchNetworkUpdatePacket(connector.requestData, request, ["responseContent"]);
   }
 
-  updateImageDimemsions({ target }) {
+  updateImageDimensions({ target }) {
     this.setState({
       imageDimensions: {
         width: target.naturalWidth,
         height: target.naturalHeight,
       },
     });
   }
 
-  // Handle json, which we tentatively identify by checking the MIME type
-  // for "json" after any word boundary. This works for the standard
-  // "application/json", and also for custom types like "x-bigcorp-json".
-  // Additionally, we also directly parse the response text content to
-  // verify whether it's json or not, to handle responses incorrectly
-  // labeled as text/plain instead.
+  /**
+   * This method checks that the response is base64 encoded by
+   * comparing these 2 values:
+   * 1. The original response
+   * 2. The value of doing a base64 decode on the
+   * response and then base64 encoding the result.
+   * If the values are different or an error is thrown,
+   * the method will return false.
+   */
+  isBase64(response) {
+    try {
+      return btoa(atob(response)) == response;
+    } catch (err) {
+      return false;
+    }
+  }
+
+  /**
+   * Handle json, which we tentatively identify by checking the
+   * MIME type for "json" after any word boundary. This works
+   * for the standard "application/json", and also for custom
+   * types like "x-bigcorp-json". Additionally, we also
+   * directly parse the response text content to verify whether
+   * it's json or not, to handle responses incorrectly labeled
+   * as text/plain instead.
+   */
   isJSON(mimeType, response) {
     let json, error;
+
     try {
       json = JSON.parse(response);
     } catch (err) {
-      try {
-        json = JSON.parse(atob(response));
-      } catch (err64) {
+      if (this.isBase64(response)) {
+        try {
+          json = JSON.parse(atob(response));
+        } catch (err64) {
+          error = err;
+        }
+      } else {
         error = err;
       }
     }
 
     if (/\bjson/.test(mimeType) || json) {
       // Extract the actual json substring in case this might be a "JSONP".
       // This regex basically parses a function call and captures the
       // function name and arguments in two separate groups.
@@ -145,17 +169,17 @@ class ResponsePanel extends Component {
     if (mimeType.includes("image/")) {
       const { width, height } = this.state.imageDimensions;
 
       return (
         div({ className: "panel-container response-image-box devtools-monospace" },
           img({
             className: "response-image",
             src: formDataURI(mimeType, encoding, text),
-            onLoad: this.updateImageDimemsions,
+            onLoad: this.updateImageDimensions,
           }),
           div({ className: "response-summary" },
             div({ className: "tabpanel-summary-label" }, RESPONSE_IMG_NAME),
             div({ className: "tabpanel-summary-value" }, getUrlBaseName(url)),
           ),
           div({ className: "response-summary" },
             div({ className: "tabpanel-summary-label" }, RESPONSE_IMG_DIMENSIONS),
             div({ className: "tabpanel-summary-value" }, `${width} × ${height}`),
--- a/devtools/client/netmonitor/test/browser.ini
+++ b/devtools/client/netmonitor/test/browser.ini
@@ -13,16 +13,17 @@ support-files =
   html_cyrillic-test-page.html
   html_frame-test-page.html
   html_frame-subdocument.html
   html_filter-test-page.html
   html_infinite-get-page.html
   html_json-b64.html
   html_json-basic.html
   html_json-custom-mime-test-page.html
+  html_json-empty.html
   html_json-long-test-page.html
   html_json-malformed-test-page.html
   html_json-text-mime-test-page.html
   html_jsonp-test-page.html
   html_maps-test-page.html
   html_navigate-test-page.html
   html_params-test-page.html
   html_pause-test-page.html
@@ -128,16 +129,17 @@ skip-if = (os == 'mac') # Bug 1479782
 [browser_net_filter-autocomplete.js]
 [browser_net_filter-flags.js]
 [browser_net_footer-summary.js]
 [browser_net_headers-alignment.js]
 [browser_net_headers_filter.js]
 [browser_net_headers_sorted.js]
 [browser_net_image-tooltip.js]
 [browser_net_json-b64.js]
+[browser_net_json-empty.js]
 [browser_net_json-null.js]
 [browser_net_json-long.js]
 [browser_net_json-malformed.js]
 [browser_net_json-nogrip.js]
 [browser_net_json_custom_mime.js]
 [browser_net_json_text_mime.js]
 [browser_net_jsonp.js]
 [browser_net_large-response.js]
new file mode 100644
--- /dev/null
+++ b/devtools/client/netmonitor/test/browser_net_json-empty.js
@@ -0,0 +1,47 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+/**
+ * Tests if empty JSON responses are properly displayed.
+ */
+
+add_task(async function() {
+  const { tab, monitor } = await initNetMonitor(JSON_EMPTY_URL + "?name=empty");
+  info("Starting test... ");
+
+  const { document, store, windowRequire } = monitor.panelWin;
+  const { L10N } = windowRequire("devtools/client/netmonitor/src/utils/l10n");
+  const Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
+
+  store.dispatch(Actions.batchEnable(false));
+
+  // Execute requests.
+  await performRequests(monitor, tab, 1);
+
+  const onResponsePanelReady = waitForDOM(document, "#response-panel .CodeMirror-code");
+  store.dispatch(Actions.toggleNetworkDetails());
+  EventUtils.sendMouseEvent({ type: "click" },
+    document.querySelector("#response-tab"));
+  await onResponsePanelReady;
+
+  const tabpanel = document.querySelector("#response-panel");
+  is(tabpanel.querySelectorAll(".tree-section").length, 2,
+    "There should be 2 tree sections displayed in this tabpanel.");
+  is(tabpanel.querySelectorAll(".empty-notice").length, 0,
+    "The empty notice should not be displayed in this tabpanel.");
+
+  is(tabpanel.querySelector(".response-error-header") === null, true,
+    "The response error header doesn't have the intended visibility.");
+  is(tabpanel.querySelector(".CodeMirror-code") === null, false,
+    "The response editor has the intended visibility.");
+  is(tabpanel.querySelector(".response-image-box") === null, true,
+    "The response image box doesn't have the intended visibility.");
+
+  const jsonView = tabpanel.querySelector(".tree-section .treeLabel") || {};
+  is(jsonView.textContent === L10N.getStr("jsonScopeName"), true,
+    "The response json view has the intended visibility.");
+
+  await teardown(monitor);
+});
--- a/devtools/client/netmonitor/test/browser_net_json-null.js
+++ b/devtools/client/netmonitor/test/browser_net_json-null.js
@@ -15,21 +15,21 @@ add_task(async function() {
   const { L10N } = windowRequire("devtools/client/netmonitor/src/utils/l10n");
   const Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
 
   store.dispatch(Actions.batchEnable(false));
 
   // Execute requests.
   await performRequests(monitor, tab, 1);
 
-  const onReponsePanelReady = waitForDOM(document, "#response-panel .CodeMirror-code");
+  const onResponsePanelReady = waitForDOM(document, "#response-panel .CodeMirror-code");
   store.dispatch(Actions.toggleNetworkDetails());
   EventUtils.sendMouseEvent({ type: "click" },
     document.querySelector("#response-tab"));
-  await onReponsePanelReady;
+  await onResponsePanelReady;
 
   checkResponsePanelDisplaysJSON();
 
   const tabpanel = document.querySelector("#response-panel");
   is(tabpanel.querySelectorAll(".tree-section").length, 2,
     "There should be 2 tree sections displayed in this tabpanel.");
   is(tabpanel.querySelectorAll(".treeRow:not(.tree-section)").length, 1,
     "There should be 1 json properties displayed in this tabpanel.");
--- a/devtools/client/netmonitor/test/head.js
+++ b/devtools/client/netmonitor/test/head.js
@@ -56,16 +56,17 @@ const POST_RAW_WITH_HEADERS_URL = EXAMPL
 const PARAMS_URL = EXAMPLE_URL + "html_params-test-page.html";
 const JSONP_URL = EXAMPLE_URL + "html_jsonp-test-page.html";
 const JSON_LONG_URL = EXAMPLE_URL + "html_json-long-test-page.html";
 const JSON_MALFORMED_URL = EXAMPLE_URL + "html_json-malformed-test-page.html";
 const JSON_CUSTOM_MIME_URL = EXAMPLE_URL + "html_json-custom-mime-test-page.html";
 const JSON_TEXT_MIME_URL = EXAMPLE_URL + "html_json-text-mime-test-page.html";
 const JSON_B64_URL = EXAMPLE_URL + "html_json-b64.html";
 const JSON_BASIC_URL = EXAMPLE_URL + "html_json-basic.html";
+const JSON_EMPTY_URL = EXAMPLE_URL + "html_json-empty.html";
 const SORTING_URL = EXAMPLE_URL + "html_sorting-test-page.html";
 const FILTERING_URL = EXAMPLE_URL + "html_filter-test-page.html";
 const INFINITE_GET_URL = EXAMPLE_URL + "html_infinite-get-page.html";
 const CUSTOM_GET_URL = EXAMPLE_URL + "html_custom-get-page.html";
 const SINGLE_GET_URL = EXAMPLE_URL + "html_single-get-page.html";
 const STATISTICS_URL = EXAMPLE_URL + "html_statistics-test-page.html";
 const CURL_URL = EXAMPLE_URL + "html_copy-as-curl.html";
 const CURL_UTILS_URL = EXAMPLE_URL + "html_curl-utils.html";
new file mode 100644
--- /dev/null
+++ b/devtools/client/netmonitor/test/html_json-empty.html
@@ -0,0 +1,42 @@
+<!-- Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/ -->
+<!doctype html>
+
+<html>
+  <head>
+    <meta charset="utf-8"/>
+    <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
+    <meta http-equiv="Pragma" content="no-cache" />
+    <meta http-equiv="Expires" content="0" />
+    <title>Network Monitor test page</title>
+  </head>
+
+  <body>
+    <p>Empty JSON test page</p>
+
+    <script type="text/javascript">
+      /* exported performRequests */
+      "use strict";
+
+      function get(address, callback) {
+        const xhr = new XMLHttpRequest();
+        xhr.open("GET", address, true);
+
+        xhr.onreadystatechange = function() {
+          if (this.readyState == this.DONE) {
+            callback();
+          }
+        };
+        xhr.send(null);
+      }
+
+      function performRequests() {
+        // Forward the query parameter for this page to sjs_json-test-server
+        get("sjs_json-test-server.sjs" + window.location.search, function() {
+          // Done.
+        });
+      }
+    </script>
+  </body>
+
+</html>
--- a/devtools/client/netmonitor/test/sjs_json-test-server.sjs
+++ b/devtools/client/netmonitor/test/sjs_json-test-server.sjs
@@ -16,10 +16,13 @@ function handleRequest(request, response
   let name = (params.filter((s) => s.includes("name="))[0] || "").split("=")[1];
   switch (name) {
     case "null":
       response.write("{ \"greeting\": null }");
       break;
     case "nogrip":
       response.write("{\"obj\": {\"type\": \"string\" }}");
       break;
+    case "empty":
+      response.write("{}");
+      break;
   }
 }
--- a/dom/base/Element.cpp
+++ b/dom/base/Element.cpp
@@ -617,17 +617,17 @@ Element::WrapObject(JSContext *aCx, JS::
     bool ok = GetBindingURL(doc, getter_AddRefs(bindingURL));
     if (!ok) {
       dom::Throw(aCx, NS_ERROR_FAILURE);
       return nullptr;
     }
 
     if (bindingURL) {
       nsCOMPtr<nsIURI> uri = bindingURL->GetURI();
-      nsCOMPtr<nsIPrincipal> principal = bindingURL->mExtraData->GetPrincipal();
+      nsCOMPtr<nsIPrincipal> principal = bindingURL->mExtraData->Principal();
 
       // We have a binding that must be installed.
       bool dummy;
 
       nsXBLService* xblService = nsXBLService::GetInstance();
       if (!xblService) {
         dom::Throw(aCx, NS_ERROR_NOT_AVAILABLE);
         return nullptr;
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -3794,17 +3794,17 @@ nsIDocument::DefaultStyleAttrURLData()
   nsIURI* baseURI = GetDocBaseURI();
   nsIURI* docURI = GetDocumentURI();
   nsIPrincipal* principal = NodePrincipal();
   mozilla::net::ReferrerPolicy policy = GetReferrerPolicy();
   if (!mCachedURLData ||
       mCachedURLData->BaseURI() != baseURI ||
       mCachedURLData->GetReferrer() != docURI ||
       mCachedURLData->GetReferrerPolicy() != policy ||
-      mCachedURLData->GetPrincipal() != principal) {
+      mCachedURLData->Principal() != principal) {
     mCachedURLData = new URLExtraData(baseURI, docURI, principal, policy);
   }
   return mCachedURLData;
 }
 
 void
 nsIDocument::SetDocumentCharacterSet(NotNull<const Encoding*> aEncoding)
 {
--- a/dom/media/MediaManager.cpp
+++ b/dom/media/MediaManager.cpp
@@ -2255,16 +2255,17 @@ MediaManager::Get() {
 
     nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
     if (obs) {
       obs->AddObserver(sSingleton, "last-pb-context-exited", false);
       obs->AddObserver(sSingleton, "getUserMedia:got-device-permission", false);
       obs->AddObserver(sSingleton, "getUserMedia:privileged:allow", false);
       obs->AddObserver(sSingleton, "getUserMedia:response:allow", false);
       obs->AddObserver(sSingleton, "getUserMedia:response:deny", false);
+      obs->AddObserver(sSingleton, "getUserMedia:response:noOSPermission", false);
       obs->AddObserver(sSingleton, "getUserMedia:revoke", false);
     }
     // else MediaManager won't work properly and will leak (see bug 837874)
     nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
     if (prefs) {
       prefs->AddObserver("media.navigator.video.default_width", sSingleton, false);
       prefs->AddObserver("media.navigator.video.default_height", sSingleton, false);
       prefs->AddObserver("media.navigator.video.default_fps", sSingleton, false);
@@ -3629,16 +3630,17 @@ MediaManager::Shutdown()
   }
 
   nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
 
   obs->RemoveObserver(this, "last-pb-context-exited");
   obs->RemoveObserver(this, "getUserMedia:privileged:allow");
   obs->RemoveObserver(this, "getUserMedia:response:allow");
   obs->RemoveObserver(this, "getUserMedia:response:deny");
+  obs->RemoveObserver(this, "getUserMedia:response:noOSPermission");
   obs->RemoveObserver(this, "getUserMedia:revoke");
 
   nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
   if (prefs) {
     prefs->RemoveObserver("media.navigator.video.default_width", this);
     prefs->RemoveObserver("media.navigator.video.default_height", this);
     prefs->RemoveObserver("media.navigator.video.default_fps", this);
     prefs->RemoveObserver("media.navigator.audio.fake_frequency", this);
@@ -3764,22 +3766,40 @@ MediaManager::SendPendingGUMRequest()
 {
   if (mPendingGUMRequest.Length() > 0) {
     nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
     obs->NotifyObservers(mPendingGUMRequest[0], "getUserMedia:request", nullptr);
     mPendingGUMRequest.RemoveElementAt(0);
   }
 }
 
+bool
+IsGUMResponseNoAccess(const char* aTopic, MediaMgrError::Name& aErrorName)
+{
+  if (!strcmp(aTopic, "getUserMedia:response:deny")) {
+    aErrorName = MediaMgrError::Name::NotAllowedError;
+    return true;
+  }
+
+  if (!strcmp(aTopic, "getUserMedia:response:noOSPermission")) {
+    aErrorName = MediaMgrError::Name::NotFoundError;
+    return true;
+  }
+
+  return false;
+}
+
 nsresult
 MediaManager::Observe(nsISupports* aSubject, const char* aTopic,
   const char16_t* aData)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
+  MediaMgrError::Name gumNoAccessError = MediaMgrError::Name::NotAllowedError;
+
   if (!strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
     nsCOMPtr<nsIPrefBranch> branch( do_QueryInterface(aSubject) );
     if (branch) {
       GetPrefs(branch,NS_ConvertUTF16toUTF8(aData).get());
       LOG(("%s: %dx%d @%dfps", __FUNCTION__,
            mPrefs.mWidth, mPrefs.mHeight, mPrefs.mFPS));
     }
   } else if (!strcmp(aTopic, "last-pb-context-exited")) {
@@ -3855,22 +3875,22 @@ MediaManager::Observe(nsISupports* aSubj
     if (sHasShutdown) {
       return task->Denied(MediaMgrError::Name::AbortError,
                           NS_LITERAL_STRING("In shutdown"));
     }
     // Reuse the same thread to save memory.
     MediaManager::PostTask(task.forget());
     return NS_OK;
 
-  } else if (!strcmp(aTopic, "getUserMedia:response:deny")) {
+  } else if (IsGUMResponseNoAccess(aTopic, gumNoAccessError)) {
     nsString key(aData);
     RefPtr<GetUserMediaTask> task;
     mActiveCallbacks.Remove(key, getter_AddRefs(task));
     if (task) {
-      task->Denied(MediaMgrError::Name::NotAllowedError);
+      task->Denied(gumNoAccessError);
       nsTArray<nsString>* array;
       if (!mCallIds.Get(task->GetWindowID(), &array)) {
         return NS_OK;
       }
       array->RemoveElement(key);
       SendPendingGUMRequest();
     }
     return NS_OK;
--- a/dom/plugins/ipc/PluginInstanceChild.cpp
+++ b/dom/plugins/ipc/PluginInstanceChild.cpp
@@ -3509,17 +3509,16 @@ PluginInstanceChild::PaintRectWithAlphaE
                                                                surfaceAsImage);
         }
     }
 
     RefPtr<gfxImageSurface> whiteImage;
     RefPtr<gfxImageSurface> blackImage;
     gfxRect targetRect(rect.x, rect.y, rect.width, rect.height);
     IntSize targetSize(rect.width, rect.height);
-    gfxPoint deviceOffset = -targetRect.TopLeft();
 
     // We always use a temporary "white image"
     whiteImage = new gfxImageSurface(targetSize, SurfaceFormat::X8R8G8B8_UINT32);
     if (whiteImage->CairoStatus()) {
         return;
     }
 
 #ifdef XP_WIN
@@ -3544,16 +3543,17 @@ PluginInstanceChild::PaintRectWithAlphaE
     PaintRectToSurface(rect, aSurface, Color(0.f, 0.f, 0.f));
 
     // Don't copy the result, just extract a subimage so that we can
     // recover alpha directly into the target
     gfxImageSurface *image = static_cast<gfxImageSurface*>(aSurface);
     blackImage = image->GetSubimage(targetRect);
 
 #else
+    gfxPoint deviceOffset = -targetRect.TopLeft();
     // Paint onto white background
     whiteImage->SetDeviceOffset(deviceOffset);
     PaintRectToSurface(rect, whiteImage, Color(1.f, 1.f, 1.f));
 
     if (useSurfaceSubimageForBlack) {
         gfxImageSurface *surface = static_cast<gfxImageSurface*>(aSurface);
         blackImage = surface->GetSubimage(targetRect);
     } else {
--- a/dom/plugins/ipc/PluginInstanceParent.cpp
+++ b/dom/plugins/ipc/PluginInstanceParent.cpp
@@ -119,17 +119,16 @@ PluginInstanceParent::PluginInstancePare
     , mDrawingModel(kDefaultDrawingModel)
     , mLastRecordedDrawingModel(-1)
     , mFrameID(0)
 #if defined(OS_WIN)
     , mPluginHWND(nullptr)
     , mChildPluginHWND(nullptr)
     , mChildPluginsParentHWND(nullptr)
     , mPluginWndProc(nullptr)
-    , mNestedEventState(false)
 #endif // defined(XP_WIN)
 #if defined(XP_MACOSX)
     , mShWidth(0)
     , mShHeight(0)
     , mShColorSpace(nullptr)
 #endif
 {
 #if defined(OS_WIN)
--- a/dom/plugins/ipc/PluginInstanceParent.h
+++ b/dom/plugins/ipc/PluginInstanceParent.h
@@ -397,17 +397,16 @@ private:
     nsIntRect          mPluginPort;
     nsIntRect          mSharedSize;
     HWND               mPluginHWND;
     // This is used for the normal child plugin HWND for windowed plugins and,
     // if needed, also the child popup surrogate HWND for windowless plugins.
     HWND               mChildPluginHWND;
     HWND               mChildPluginsParentHWND;
     WNDPROC            mPluginWndProc;
-    bool               mNestedEventState;
 #endif // defined(XP_WIN)
 #if defined(MOZ_WIDGET_COCOA)
 private:
     Shmem                  mShSurface;
     uint16_t               mShWidth;
     uint16_t               mShHeight;
     CGColorSpaceRef        mShColorSpace;
     RefPtr<MacIOSurface> mIOSurface;
--- a/dom/plugins/ipc/PluginModuleParent.cpp
+++ b/dom/plugins/ipc/PluginModuleParent.cpp
@@ -490,17 +490,17 @@ PluginModuleChromeParent::LoadModule(con
     if (NS_FAILED(rv)) {
         parent->mShutdown = true;
         return nullptr;
     }
 
     parent->mBrokerParent =
       FunctionBrokerParent::Create(std::move(brokerParentEnd));
     if (parent->mBrokerParent) {
-      parent->SendInitPluginFunctionBroker(std::move(brokerChildEnd));
+      Unused << parent->SendInitPluginFunctionBroker(std::move(brokerChildEnd));
     }
 #endif
     return parent.forget();
 }
 
 static const char* gCallbackPrefs[] = {
     kChildTimeoutPref,
     kParentTimeoutPref,
--- a/gfx/layers/wr/WebRenderCommandBuilder.cpp
+++ b/gfx/layers/wr/WebRenderCommandBuilder.cpp
@@ -470,43 +470,32 @@ struct DIGroup
       GP("else invalidate: %s\n", aItem->Name());
       // this includes situations like reflow changing the position
       aItem->ComputeInvalidationRegion(aBuilder, aData->mGeometry.get(), &combined);
       if (!combined.IsEmpty()) {
         // There might be no point in doing this elaborate tracking here to get
         // smaller areas
         InvalidateRect(aData->mRect.Intersect(imageRect)); // invalidate the old area -- in theory combined should take care of this
         UniquePtr<nsDisplayItemGeometry> geometry(aItem->AllocateGeometry(aBuilder));
-        aData->mClip.AddOffsetAndComputeDifference(nsPoint(), aData->mGeometry->ComputeInvalidationRegion(), clip,
-                                                   geometry ? geometry->ComputeInvalidationRegion() :
-                                                   aData->mGeometry->ComputeInvalidationRegion(),
-                                                   &combined);
-        IntRect transformedRect = ToDeviceSpace(combined.GetBounds(), aMatrix, appUnitsPerDevPixel, mLayerBounds.TopLeft());
-        IntRect invalidRect = transformedRect.Intersect(imageRect);
-        GP("combined not empty: mRect %d %d %d %d\n", invalidRect.x, invalidRect.y, invalidRect.width, invalidRect.height);
         // invalidate the invalidated area.
-        InvalidateRect(invalidRect);
 
         aData->mGeometry = std::move(geometry);
 
         combined = clip.ApplyNonRoundedIntersection(aData->mGeometry->ComputeInvalidationRegion());
-        transformedRect = ToDeviceSpace(combined.GetBounds(), aMatrix, appUnitsPerDevPixel, mLayerBounds.TopLeft());
+        IntRect transformedRect = ToDeviceSpace(combined.GetBounds(), aMatrix, appUnitsPerDevPixel, mLayerBounds.TopLeft());
         aData->mRect = transformedRect.Intersect(imageRect);
+        InvalidateRect(aData->mRect);
 
         // CGC invariant broken
         if (!mInvalidRect.Contains(aData->mRect)) {
           gfxCriticalError() << "CGC-" <<
             "-" << aData->mRect.x <<
             "-" << aData->mRect.y <<
             "-" << aData->mRect.width <<
             "-" << aData->mRect.height <<
-            "," << invalidRect.x <<
-            "-" << invalidRect.y <<
-            "-" << invalidRect.width <<
-            "-" << invalidRect.height <<
             "-ib";
         }
 
         aData->mInvalid = true;
         aData->mInvalidRegion = true;
       } else {
         if (aData->mClip != clip) {
           UniquePtr<nsDisplayItemGeometry> geometry(aItem->AllocateGeometry(aBuilder));
--- a/ipc/mscom/Interceptor.cpp
+++ b/ipc/mscom/Interceptor.cpp
@@ -14,17 +14,17 @@
 #include "mozilla/mscom/InterceptorLog.h"
 #include "mozilla/mscom/MainThreadInvoker.h"
 #include "mozilla/mscom/Objref.h"
 #include "mozilla/mscom/Registration.h"
 #include "mozilla/mscom/Utils.h"
 #include "mozilla/ThreadLocal.h"
 #include "MainThreadUtils.h"
 #include "mozilla/Assertions.h"
-#include "mozilla/DebugOnly.h"
+#include "mozilla/Unused.h"
 #include "nsDirectoryServiceDefs.h"
 #include "nsDirectoryServiceUtils.h"
 #include "nsExceptionHandler.h"
 #include "nsPrintfCString.h"
 #include "nsRefPtrHashtable.h"
 #include "nsThreadUtils.h"
 #include "nsXULAppAPI.h"
 
@@ -272,16 +272,17 @@ Interceptor::Interceptor(IInterceptorSin
   : WeakReferenceSupport(WeakReferenceSupport::Flags::eDestroyOnMainThread)
   , mEventSink(aSink)
   , mInterceptorMapMutex("mozilla::mscom::Interceptor::mInterceptorMapMutex")
   , mStdMarshalMutex("mozilla::mscom::Interceptor::mStdMarshalMutex")
   , mStdMarshal(nullptr)
 {
   static const bool kHasTls = tlsCreatingStdMarshal.init();
   MOZ_ASSERT(kHasTls);
+  Unused << kHasTls;
 
   MOZ_ASSERT(aSink);
   RefPtr<IWeakReference> weakRef;
   if (SUCCEEDED(GetWeakReference(getter_AddRefs(weakRef)))) {
     aSink->SetInterceptor(weakRef);
   }
 }
 
--- a/ipc/mscom/MainThreadHandoff.cpp
+++ b/ipc/mscom/MainThreadHandoff.cpp
@@ -11,16 +11,17 @@
 #include "mozilla/DebugOnly.h"
 #include "mozilla/Move.h"
 #include "mozilla/mscom/AgileReference.h"
 #include "mozilla/mscom/InterceptorLog.h"
 #include "mozilla/mscom/Registration.h"
 #include "mozilla/mscom/Utils.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/ThreadLocal.h"
+#include "mozilla/Unused.h"
 #include "nsThreadUtils.h"
 #include "nsProxyRelease.h"
 
 using mozilla::DebugOnly;
 using mozilla::mscom::AgileReference;
 
 namespace {
 
@@ -161,16 +162,17 @@ class MOZ_RAII SavedCallFrame final
 public:
   explicit SavedCallFrame(mozilla::NotNull<ICallFrame*> aFrame)
     : mCallFrame(aFrame)
   {
     static const bool sIsInit = tlsFrame.init();
     MOZ_ASSERT(sIsInit);
     MOZ_ASSERT(!tlsFrame.get());
     tlsFrame.set(this);
+    Unused << sIsInit;
   }
 
   ~SavedCallFrame()
   {
     MOZ_ASSERT(tlsFrame.get());
     tlsFrame.set(nullptr);
   }
 
--- a/ipc/mscom/MainThreadInvoker.cpp
+++ b/ipc/mscom/MainThreadInvoker.cpp
@@ -6,20 +6,20 @@
 
 #include "mozilla/mscom/MainThreadInvoker.h"
 
 #include "GeckoProfiler.h"
 #include "MainThreadUtils.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/BackgroundHangMonitor.h"
 #include "mozilla/ClearOnShutdown.h"
-#include "mozilla/DebugOnly.h"
 #include "mozilla/mscom/SpinEvent.h"
 #include "mozilla/RefPtr.h"
 #include "mozilla/SystemGroup.h"
+#include "mozilla/Unused.h"
 #include "private/prpriv.h" // For PR_GetThreadID
 #include <winternl.h> // For NTSTATUS and NTAPI
 
 namespace {
 
 typedef NTSTATUS (NTAPI* NtTestAlertPtr)(VOID);
 
 /**
@@ -33,16 +33,17 @@ class SyncRunnable : public mozilla::Run
 {
 public:
   explicit SyncRunnable(already_AddRefed<nsIRunnable> aRunnable)
     : mozilla::Runnable("MainThreadInvoker")
     , mRunnable(aRunnable)
   {
     static const bool gotStatics = InitStatics();
     MOZ_ASSERT(gotStatics);
+    Unused << gotStatics;
   }
 
   ~SyncRunnable() = default;
 
   NS_IMETHOD Run() override
   {
     if (mHasRun) {
       // The APC already ran, so we have nothing to do.
@@ -126,16 +127,17 @@ MainThreadInvoker::InitStatics()
 
   return !!sMainThread;
 }
 
 MainThreadInvoker::MainThreadInvoker()
 {
   static const bool gotStatics = InitStatics();
   MOZ_ASSERT(gotStatics);
+  Unused << gotStatics;
 }
 
 bool
 MainThreadInvoker::Invoke(already_AddRefed<nsIRunnable>&& aRunnable)
 {
   nsCOMPtr<nsIRunnable> runnable(std::move(aRunnable));
   if (!runnable) {
     return false;
--- a/ipc/mscom/ProxyStream.cpp
+++ b/ipc/mscom/ProxyStream.cpp
@@ -291,17 +291,17 @@ ProxyStream::ProxyStream(REFIID aIID, IU
 
   HRESULT createStreamResult = S_OK;
   HRESULT marshalResult = S_OK;
   HRESULT statResult = S_OK;
   HRESULT getHGlobalResult = S_OK;
 
   nsAutoString manifestPath;
 
-  auto marshalFn = [this, &aIID, aObject, mshlFlags, &stream, &streamSize,
+  auto marshalFn = [&aIID, aObject, mshlFlags, &stream, &streamSize,
                     &hglobal, &createStreamResult, &marshalResult, &statResult,
                     &getHGlobalResult, aEnv, &manifestPath]() -> void
   {
     if (aEnv) {
       bool pushOk = aEnv->Push();
       MOZ_DIAGNOSTIC_ASSERT(pushOk);
       if (!pushOk) {
         return;
--- a/layout/base/nsCSSFrameConstructor.cpp
+++ b/layout/base/nsCSSFrameConstructor.cpp
@@ -2421,17 +2421,17 @@ nsCSSFrameConstructor::ConstructDocEleme
 
     nsXBLService* xblService = nsXBLService::GetInstance();
     if (!xblService) {
       return nullptr;
     }
 
     RefPtr<nsXBLBinding> binding;
     rv = xblService->LoadBindings(aDocElement, display->mBinding->GetURI(),
-                                  display->mBinding->mExtraData->GetPrincipal(),
+                                  display->mBinding->mExtraData->Principal(),
                                   getter_AddRefs(binding), &resolveStyle);
     if (NS_FAILED(rv) && rv != NS_ERROR_XBL_BLOCKED) {
       // Binding will load asynchronously.
       return nullptr;
     }
 
     if (binding) {
       // For backwards compat, keep firing the root's constructor
@@ -5574,17 +5574,17 @@ nsCSSFrameConstructor::LoadXBLBindingIfN
     return { };
   }
 
   auto newPendingBinding = MakeUnique<PendingBinding>();
 
   bool resolveStyle;
   nsresult rv = xblService->LoadBindings(aContent.AsElement(),
                                          binding->GetURI(),
-                                         binding->mExtraData->GetPrincipal(),
+                                         binding->mExtraData->Principal(),
                                          getter_AddRefs(newPendingBinding->mBinding),
                                          &resolveStyle);
   if (NS_FAILED(rv)) {
     if (rv == NS_ERROR_XBL_BLOCKED) {
       return { aContent, aStyle };
     }
     return { };
   }
--- a/layout/style/FontFaceSet.cpp
+++ b/layout/style/FontFaceSet.cpp
@@ -1100,18 +1100,17 @@ FontFaceSet::FindOrCreateUserFontEntryFr
         case StyleFontFaceSourceListComponent::Tag::Url: {
           face->mSourceType = gfxFontFaceSrc::eSourceType_URL;
           const URLValue* url = component.url._0;
           nsIURI* uri = url->GetURI();
           face->mURI = uri ? new gfxFontSrcURI(uri) : nullptr;
           face->mReferrer = url->mExtraData->GetReferrer();
           face->mReferrerPolicy = url->mExtraData->GetReferrerPolicy();
           face->mOriginPrincipal =
-            new gfxFontSrcPrincipal(url->mExtraData->GetPrincipal());
-          NS_ASSERTION(face->mOriginPrincipal, "null origin principal in @font-face rule");
+            new gfxFontSrcPrincipal(url->mExtraData->Principal());
 
           // agent and user stylesheets are treated slightly differently,
           // the same-site origin check and access control headers are
           // enforced against the sheet principal rather than the document
           // principal to allow user stylesheets to include @font-face rules
           face->mUseOriginPrincipal = (aSheetType == SheetType::User ||
                                        aSheetType == SheetType::Agent);
 
--- a/layout/style/ImageLoader.cpp
+++ b/layout/style/ImageLoader.cpp
@@ -48,17 +48,17 @@ ImageLoader::DropDocumentReference()
   // been destroyed, and it also calls ClearFrames when it is destroyed.
   ClearFrames(GetPresContext());
 
   for (auto it = mRegisteredImages.Iter(); !it.Done(); it.Next()) {
     if (imgRequestProxy* request = it.Data()) {
       request->CancelAndForgetObserver(NS_BINDING_ABORTED);
     }
 
-    // Need to check whether the entry exists, since the css::ImageValue might
+    // Need to check whether the entry exists, since the css::URLValue might
     // go away before ImageLoader::DropDocumentReference is called.
     uint64_t imageLoadID = it.Key();
     if (auto entry = sImages->Lookup(imageLoadID)) {
       entry.Data()->mImageLoaders.RemoveEntry(this);
     }
   }
 
   mRegisteredImages.Clear();
@@ -203,17 +203,17 @@ ImageLoader::AssociateRequestToFrame(img
     didAddToRequestSet = true;
   }
 
   MOZ_ASSERT(didAddToFrameSet == didAddToRequestSet,
              "We should only add to one map iff we also add to the other map.");
 }
 
 imgRequestProxy*
-ImageLoader::RegisterCSSImage(ImageLoader::Image* aImage)
+ImageLoader::RegisterCSSImage(URLValue* aImage)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aImage);
 
   if (aImage->LoadID() == 0) {
     MOZ_ASSERT_UNREACHABLE("Image should have a valid LoadID");
     return nullptr;
   }
@@ -249,17 +249,17 @@ ImageLoader::RegisterCSSImage(ImageLoade
   MOZ_ASSERT(!mRegisteredImages.Contains(aImage->LoadID()));
 
   imgRequestProxy* requestWeak = request;
   mRegisteredImages.Put(aImage->LoadID(), request.forget());
   return requestWeak;
 }
 
 /* static */ void
-ImageLoader::DeregisterCSSImageFromAllLoaders(ImageLoader::Image* aImage)
+ImageLoader::DeregisterCSSImageFromAllLoaders(URLValue* aImage)
 {
   MOZ_ASSERT(aImage);
 
   uint64_t loadID = aImage->LoadID();
 
   if (loadID == 0) {
     MOZ_ASSERT_UNREACHABLE("Image should have a valid LoadID");
     return;
@@ -450,31 +450,31 @@ ImageLoader::ClearFrames(nsPresContext* 
 }
 
 /* static */ void
 ImageLoader::LoadImage(nsIURI* aURI,
                        nsIPrincipal* aOriginPrincipal,
                        nsIURI* aReferrer,
                        mozilla::net::ReferrerPolicy aPolicy,
                        nsIDocument* aDocument,
-                       ImageLoader::Image* aImage,
+                       URLValue* aImage,
                        CORSMode aCorsMode)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aDocument);
   MOZ_ASSERT(aImage);
   MOZ_ASSERT(aImage->LoadID() != 0);
 
   if (aImage->LoadID() == 0) {
     MOZ_ASSERT_UNREACHABLE("Image should have a valid LoadID");
     return;
   }
 
   if (sImages->Contains(aImage->LoadID())) {
-    // This css::ImageValue has already been loaded.
+    // This css::URLValue has already been loaded.
     return;
   }
 
   ImageTableEntry* entry = new ImageTableEntry();
   sImages->Put(aImage->LoadID(), entry);
 
   if (!aURI) {
     return;
--- a/layout/style/ImageLoader.h
+++ b/layout/style/ImageLoader.h
@@ -26,17 +26,17 @@ class nsIFrame;
 class nsIDocument;
 class nsPresContext;
 class nsIURI;
 class nsIPrincipal;
 
 namespace mozilla {
 namespace css {
 
-struct ImageValue;
+struct URLValue;
 
 /**
  * NOTE: All methods must be called from the main thread unless otherwise
  * specified.
  */
 class ImageLoader final : public imgINotificationObserver
 {
 public:
@@ -46,31 +46,29 @@ public:
   // We also associate flags alongside frames in the request-to-frames hashmap.
   // These are used for special handling of events for requests.
   typedef uint32_t FrameFlags;
   enum {
     REQUEST_REQUIRES_REFLOW      = 1u << 0,
     REQUEST_HAS_BLOCKED_ONLOAD   = 1u << 1,
   };
 
-  typedef mozilla::css::ImageValue Image;
-
   explicit ImageLoader(nsIDocument* aDocument)
   : mDocument(aDocument),
     mInClone(false)
   {
     MOZ_ASSERT(mDocument);
   }
 
   NS_DECL_ISUPPORTS
   NS_DECL_IMGINOTIFICATIONOBSERVER
 
   void DropDocumentReference();
 
-  imgRequestProxy* RegisterCSSImage(Image* aImage);
+  imgRequestProxy* RegisterCSSImage(URLValue* aImage);
 
   void AssociateRequestToFrame(imgIRequest* aRequest,
                                nsIFrame* aFrame,
                                FrameFlags aFlags);
 
   void DisassociateRequestFromFrame(imgIRequest* aRequest,
                                     nsIFrame* aFrame);
 
@@ -83,24 +81,24 @@ public:
   // presshell pointer on the document has been cleared.
   void ClearFrames(nsPresContext* aPresContext);
 
   static void LoadImage(nsIURI* aURI,
                         nsIPrincipal* aPrincipal,
                         nsIURI* aReferrer,
                         mozilla::net::ReferrerPolicy aPolicy,
                         nsIDocument* aDocument,
-                        Image* aCSSValue,
+                        URLValue* aImage,
                         CORSMode aCorsMode);
 
-  // Cancels the image load for the given css::ImageValue and deregisters
+  // Cancels the image load for the given css::URLValue and deregisters
   // it from any ImageLoaders it was registered with.
   //
   // May be called from any thread.
-  static void DeregisterCSSImageFromAllLoaders(Image* aImage);
+  static void DeregisterCSSImageFromAllLoaders(URLValue* aImage);
 
   void FlushUseCounters();
 
 private:
   // This callback is used to unblock document onload after a reflow
   // triggered from an image load.
   struct ImageReflowCallback final : public nsIReflowCallback
   {
@@ -182,49 +180,49 @@ private:
   RequestToFrameMap mRequestToFrameMap;
 
   // A map of nsIFrames to the imgIRequests they use.
   FrameToRequestMap mFrameToRequestMap;
 
   // A weak pointer to our document. Nulled out by DropDocumentReference.
   nsIDocument* mDocument;
 
-  // A map of css::ImageValues, keyed by their LoadID(), to the imgRequestProxy
+  // A map of css::URLValues, keyed by their LoadID(), to the imgRequestProxy
   // representing the load of the image for this ImageLoader's document.
   //
   // We use the LoadID() as the key since we can only access mRegisteredImages
-  // on the main thread, but css::ImageValues might be destroyed from other
+  // on the main thread, but css::URLValues might be destroyed from other
   // threads, and we don't want to leave dangling pointers around.
   nsRefPtrHashtable<nsUint64HashKey, imgRequestProxy> mRegisteredImages;
 
   // Are we cloning?  If so, ignore any notifications we get.
   bool mInClone;
 
-  // Data associated with every css::ImageValue object that has had a load
+  // Data associated with every css::URLValue object that has had a load
   // started.
   struct ImageTableEntry
   {
-    // Set of all ImageLoaders that have registered this css::ImageValue.
+    // Set of all ImageLoaders that have registered this css::URLValue.
     nsTHashtable<nsPtrHashKey<ImageLoader>> mImageLoaders;
 
-    // The "canonical" image request for this css::ImageValue.
+    // The "canonical" image request for this css::URLValue.
     //
-    // This request is held on to as long as the specified css::ImageValue
+    // This request is held on to as long as the specified css::URLValue
     // object is, so that any image that has already started loading (or
     // has completed loading) will stay alive even if all computed values
     // referencing the image requesst have gone away.
     RefPtr<imgRequestProxy> mCanonicalRequest;
   };
 
-  // A table of all css::ImageValues that have been loaded, keyed by their
+  // A table of all css::URLValues that have been loaded, keyed by their
   // LoadID(), mapping them to the set of ImageLoaders they have been registered
   // in, and recording their "canonical" image request.
   //
   // We use the LoadID() as the key since we can only access sImages on the
-  // main thread, but css::ImageValues might be destroyed from other threads,
+  // main thread, but css::URLValues might be destroyed from other threads,
   // and we don't want to leave dangling pointers around.
   static nsClassHashtable<nsUint64HashKey, ImageTableEntry>* sImages;
 };
 
 } // namespace css
 } // namespace mozilla
 
 #endif /* mozilla_css_ImageLoader_h___ */
--- a/layout/style/ServoBindings.cpp
+++ b/layout/style/ServoBindings.cpp
@@ -1381,31 +1381,20 @@ Gecko_CounterStyle_GetName(const Counter
 
 const AnonymousCounterStyle*
 Gecko_CounterStyle_GetAnonymous(const CounterStylePtr* aPtr)
 {
   return aPtr->AsAnonymous();
 }
 
 already_AddRefed<css::URLValue>
-ServoBundledURI::IntoCssUrl()
+ServoBundledURI::IntoCssUrl(CORSMode aCorsMode)
 {
-  MOZ_ASSERT(mExtraData->GetReferrer());
-  MOZ_ASSERT(mExtraData->GetPrincipal());
-
   RefPtr<css::URLValue> urlValue =
-    new css::URLValue(mURLString, do_AddRef(mExtraData));
-  return urlValue.forget();
-}
-
-already_AddRefed<css::ImageValue>
-ServoBundledURI::IntoCssImage(mozilla::CORSMode aCorsMode)
-{
-  RefPtr<css::ImageValue> urlValue =
-    new css::ImageValue(mURLString, do_AddRef(mExtraData), aCorsMode);
+    new css::URLValue(mURLString, do_AddRef(mExtraData), aCorsMode);
   return urlValue.forget();
 }
 
 void
 Gecko_SetNullImageValue(nsStyleImage* aImage)
 {
   MOZ_ASSERT(aImage);
   aImage->SetNull();
@@ -1413,45 +1402,28 @@ Gecko_SetNullImageValue(nsStyleImage* aI
 
 void
 Gecko_SetGradientImageValue(nsStyleImage* aImage, nsStyleGradient* aGradient)
 {
   MOZ_ASSERT(aImage);
   aImage->SetGradientData(aGradient);
 }
 
-NS_IMPL_THREADSAFE_FFI_REFCOUNTING(mozilla::css::ImageValue, ImageValue);
-
 static already_AddRefed<nsStyleImageRequest>
 CreateStyleImageRequest(nsStyleImageRequest::Mode aModeFlags,
-                        mozilla::css::ImageValue* aImageValue)
+                        URLValue* aImageValue)
 {
   RefPtr<nsStyleImageRequest> req =
     new nsStyleImageRequest(aModeFlags, aImageValue);
   return req.forget();
 }
 
-mozilla::css::ImageValue*
-Gecko_ImageValue_Create(ServoBundledURI aURI, mozilla::CORSMode aCORSMode)
-{
-  return aURI.IntoCssImage(aCORSMode).take();
-}
-
-MOZ_DEFINE_MALLOC_SIZE_OF(GeckoImageValueMallocSizeOf)
-
-size_t
-Gecko_ImageValue_SizeOfIncludingThis(mozilla::css::ImageValue* aImageValue)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  return aImageValue->SizeOfIncludingThis(GeckoImageValueMallocSizeOf);
-}
-
 void
 Gecko_SetLayerImageImageValue(nsStyleImage* aImage,
-                              mozilla::css::ImageValue* aImageValue)
+                              URLValue* aImageValue)
 {
   MOZ_ASSERT(aImage && aImageValue);
 
   RefPtr<nsStyleImageRequest> req =
     CreateStyleImageRequest(nsStyleImageRequest::Mode::Track, aImageValue);
   aImage->SetImageRequest(req.forget());
 }
 
@@ -1481,33 +1453,33 @@ void
 Gecko_SetCursorArrayLength(nsStyleUI* aStyleUI, size_t aLen)
 {
   aStyleUI->mCursorImages.Clear();
   aStyleUI->mCursorImages.SetLength(aLen);
 }
 
 void
 Gecko_SetCursorImageValue(nsCursorImage* aCursor,
-                          mozilla::css::ImageValue* aImageValue)
+                          URLValue* aImageValue)
 {
   MOZ_ASSERT(aCursor && aImageValue);
 
   aCursor->mImage =
     CreateStyleImageRequest(nsStyleImageRequest::Mode::Discard, aImageValue);
 }
 
 void
 Gecko_CopyCursorArrayFrom(nsStyleUI* aDest, const nsStyleUI* aSrc)
 {
   aDest->mCursorImages = aSrc->mCursorImages;
 }
 
 void
 Gecko_SetContentDataImageValue(nsStyleContentData* aContent,
-                               mozilla::css::ImageValue* aImageValue)
+                               URLValue* aImageValue)
 {
   MOZ_ASSERT(aContent && aImageValue);
 
   RefPtr<nsStyleImageRequest> req =
     CreateStyleImageRequest(nsStyleImageRequest::Mode::Track, aImageValue);
   aContent->SetImageRequest(req.forget());
 }
 
@@ -1579,17 +1551,17 @@ Gecko_GetGradientImageValue(const nsStyl
 void
 Gecko_SetListStyleImageNone(nsStyleList* aList)
 {
   aList->mListStyleImage = nullptr;
 }
 
 void
 Gecko_SetListStyleImageImageValue(nsStyleList* aList,
-                             mozilla::css::ImageValue* aImageValue)
+                                  URLValue* aImageValue)
 {
   MOZ_ASSERT(aList && aImageValue);
 
   aList->mListStyleImage =
     CreateStyleImageRequest(nsStyleImageRequest::Mode(0), aImageValue);
 }
 
 void
@@ -1647,27 +1619,27 @@ Gecko_CopyStyleGridTemplateValues(Unique
 {
   if (aOther) {
     *aGridTemplate = MakeUnique<nsStyleGridTemplate>(*aOther);
   } else {
     *aGridTemplate = nullptr;
   }
 }
 
-mozilla::css::GridTemplateAreasValue*
+GridTemplateAreasValue*
 Gecko_NewGridTemplateAreasValue(uint32_t aAreas, uint32_t aTemplates, uint32_t aColumns)
 {
-  RefPtr<mozilla::css::GridTemplateAreasValue> value = new mozilla::css::GridTemplateAreasValue;
+  RefPtr<GridTemplateAreasValue> value = new GridTemplateAreasValue;
   value->mNamedAreas.SetLength(aAreas);
   value->mTemplates.SetLength(aTemplates);
   value->mNColumns = aColumns;
   return value.forget().take();
 }
 
-NS_IMPL_THREADSAFE_FFI_REFCOUNTING(mozilla::css::GridTemplateAreasValue, GridTemplateAreasValue);
+NS_IMPL_THREADSAFE_FFI_REFCOUNTING(GridTemplateAreasValue, GridTemplateAreasValue);
 
 void
 Gecko_ClearAndResizeStyleContents(nsStyleContent* aContent, uint32_t aHowMany)
 {
   aContent->AllocateContents(aHowMany);
 }
 
 void
@@ -2032,45 +2004,45 @@ void
 Gecko_nsStyleSVG_CopyContextProperties(nsStyleSVG* aDst, const nsStyleSVG* aSrc)
 {
   aDst->mContextProps = aSrc->mContextProps;
   aDst->mContextPropsBits = aSrc->mContextPropsBits;
 }
 
 
 css::URLValue*
-Gecko_NewURLValue(ServoBundledURI aURI)
+Gecko_URLValue_Create(ServoBundledURI aURI, mozilla::CORSMode aCORSMode)
 {
-  RefPtr<css::URLValue> url = aURI.IntoCssUrl();
+  RefPtr<css::URLValue> url = aURI.IntoCssUrl(aCORSMode);
   return url.forget().take();
 }
 
 MOZ_DEFINE_MALLOC_SIZE_OF(GeckoURLValueMallocSizeOf)
 
 size_t
 Gecko_URLValue_SizeOfIncludingThis(URLValue* aURL)
 {
   MOZ_ASSERT(NS_IsMainThread());
   return aURL->SizeOfIncludingThis(GeckoURLValueMallocSizeOf);
 }
 
 void
-Gecko_GetComputedURLSpec(const URLValueData* aURL, nsCString* aOut)
+Gecko_GetComputedURLSpec(const URLValue* aURL, nsCString* aOut)
 {
   MOZ_ASSERT(aURL);
   MOZ_ASSERT(aOut);
   if (aURL->IsLocalRef()) {
     aOut->Assign(aURL->GetString());
     return;
   }
   Gecko_GetComputedImageURLSpec(aURL, aOut);
 }
 
 void
-Gecko_GetComputedImageURLSpec(const URLValueData* aURL, nsCString* aOut)
+Gecko_GetComputedImageURLSpec(const URLValue* aURL, nsCString* aOut)
 {
   // Image URIs don't serialize local refs as local.
   if (nsIURI* uri = aURL->GetURI()) {
     nsresult rv = uri->GetSpec(*aOut);
     if (NS_SUCCEEDED(rv)) {
       return;
     }
   }
@@ -2602,17 +2574,17 @@ LoadImportSheet(css::Loader* aLoader,
     RefPtr<StyleSheet> emptySheet =
       aParent->CreateEmptyChildSheet(media.forget());
     // Make a dummy URI if we don't have one because some methods assume
     // non-null URIs.
     if (!uri) {
       NS_NewURI(getter_AddRefs(uri), NS_LITERAL_CSTRING("about:invalid"));
     }
     emptySheet->SetURIs(uri, uri, uri);
-    emptySheet->SetPrincipal(aURL->mExtraData->GetPrincipal());
+    emptySheet->SetPrincipal(aURL->mExtraData->Principal());
     emptySheet->SetComplete();
     aParent->PrependStyleSheet(emptySheet);
     return emptySheet.forget();
   }
 
   RefPtr<StyleSheet> sheet =
     static_cast<StyleSheet*>(aParent->GetFirstChild());
   return sheet.forget();
@@ -2622,29 +2594,34 @@ StyleSheet*
 Gecko_LoadStyleSheet(css::Loader* aLoader,
                      StyleSheet* aParent,
                      SheetLoadData* aParentLoadData,
                      css::LoaderReusableStyleSheets* aReusableSheets,
                      ServoBundledURI aServoURL,
                      RawServoMediaListStrong aMediaList)
 {
   MOZ_ASSERT(NS_IsMainThread());
-  RefPtr<css::URLValue> url = aServoURL.IntoCssUrl();
+
+  // The CORS mode in the URLValue is irrelevant here.
+  // (CORS_NONE is used for all imported sheets in Load::LoadChildSheet.)
+  RefPtr<css::URLValue> url = aServoURL.IntoCssUrl(CORS_NONE);
   return LoadImportSheet(aLoader, aParent, aParentLoadData, aReusableSheets,
                          url, aMediaList.Consume()).take();
 }
 
 void
 Gecko_LoadStyleSheetAsync(css::SheetLoadDataHolder* aParentData,
                           ServoBundledURI aServoURL,
                           RawServoMediaListStrong aMediaList,
                           RawServoImportRuleStrong aImportRule)
 {
   RefPtr<SheetLoadDataHolder> loadData = aParentData;
-  RefPtr<css::URLValue> urlVal = aServoURL.IntoCssUrl();
+  // The CORS mode in the URLValue is irrelevant here.
+  // (CORS_NONE is used for all imported sheets in Load::LoadChildSheet.)
+  RefPtr<css::URLValue> urlVal = aServoURL.IntoCssUrl(CORS_NONE);
   RefPtr<RawServoMediaList> mediaList = aMediaList.Consume();
   RefPtr<RawServoImportRule> importRule = aImportRule.Consume();
   NS_DispatchToMainThread(NS_NewRunnableFunction(__func__,
                                                  [data = std::move(loadData),
                                                   url = std::move(urlVal),
                                                   media = std::move(mediaList),
                                                   import = std::move(importRule)]() mutable {
     MOZ_ASSERT(NS_IsMainThread());
--- a/layout/style/ServoBindings.h
+++ b/layout/style/ServoBindings.h
@@ -39,17 +39,16 @@ namespace mozilla {
   enum FontFamilyType : uint8_t;
   class SharedFontList;
   enum class CSSPseudoElementType : uint8_t;
   struct Keyframe;
   struct StyleTransition;
   namespace css {
     class ErrorReporter;
     struct URLValue;
-    struct ImageValue;
     class LoaderReusableStyleSheets;
   };
   namespace dom {
     enum class IterationCompositeOperation : uint8_t;
   };
   enum class UpdateAnimationsTasks : uint8_t;
   struct LangGroupFontPrefs;
   class SeenPtrs;
@@ -109,20 +108,19 @@ const bool GECKO_IS_NIGHTLY = false;
 DEFINE_ARRAY_TYPE_FOR(uintptr_t);
 #undef DEFINE_ARRAY_TYPE_FOR
 
 extern "C" {
 
 class ServoBundledURI
 {
 public:
-  // NOTE(emilio): Not calling IntoCssUrl or IntoCssImage will cause to leak the
+  // NOTE(emilio): Not calling IntoCssUrl will cause to leak the
   // string, so don't do that :)
-  already_AddRefed<mozilla::css::URLValue> IntoCssUrl();
-  already_AddRefed<mozilla::css::ImageValue> IntoCssImage(mozilla::CORSMode);
+  already_AddRefed<mozilla::css::URLValue> IntoCssUrl(mozilla::CORSMode);
   mozilla::ServoRawOffsetArc<RustString> mURLString;
   mozilla::URLExtraData* mExtraData;
 };
 
 struct FontSizePrefs
 {
   void CopyFrom(const mozilla::LangGroupFontPrefs&);
   nscoord mDefaultVariableSize;
@@ -347,22 +345,18 @@ void Gecko_CopyCounterStyle(mozilla::Cou
                             const mozilla::CounterStylePtr* src);
 nsAtom* Gecko_CounterStyle_GetName(const mozilla::CounterStylePtr* ptr);
 const mozilla::AnonymousCounterStyle*
 Gecko_CounterStyle_GetAnonymous(const mozilla::CounterStylePtr* ptr);
 
 // background-image style.
 void Gecko_SetNullImageValue(nsStyleImage* image);
 void Gecko_SetGradientImageValue(nsStyleImage* image, nsStyleGradient* gradient);
-NS_DECL_THREADSAFE_FFI_REFCOUNTING(mozilla::css::ImageValue, ImageValue);
-mozilla::css::ImageValue* Gecko_ImageValue_Create(ServoBundledURI aURI,
-                                                  mozilla::CORSMode aCORSMode);
-size_t Gecko_ImageValue_SizeOfIncludingThis(mozilla::css::ImageValue* aImageValue);
 void Gecko_SetLayerImageImageValue(nsStyleImage* image,
-                                   mozilla::css::ImageValue* aImageValue);
+                                   mozilla::css::URLValue* image_value);
 
 void Gecko_SetImageElement(nsStyleImage* image, nsAtom* atom);
 void Gecko_CopyImageValueFrom(nsStyleImage* image, const nsStyleImage* other);
 void Gecko_InitializeImageCropRect(nsStyleImage* image);
 
 nsStyleGradient* Gecko_CreateGradient(uint8_t shape,
                                       uint8_t size,
                                       bool repeating,
@@ -372,27 +366,27 @@ nsStyleGradient* Gecko_CreateGradient(ui
 
 const nsStyleImageRequest* Gecko_GetImageRequest(const nsStyleImage* image);
 nsAtom* Gecko_GetImageElement(const nsStyleImage* image);
 const nsStyleGradient* Gecko_GetGradientImageValue(const nsStyleImage* image);
 
 // list-style-image style.
 void Gecko_SetListStyleImageNone(nsStyleList* style_struct);
 void Gecko_SetListStyleImageImageValue(nsStyleList* style_struct,
-                                  mozilla::css::ImageValue* aImageValue);
+                                  mozilla::css::URLValue* aImageValue);
 void Gecko_CopyListStyleImageFrom(nsStyleList* dest, const nsStyleList* src);
 
 // cursor style.
 void Gecko_SetCursorArrayLength(nsStyleUI* ui, size_t len);
 void Gecko_SetCursorImageValue(nsCursorImage* aCursor,
-                               mozilla::css::ImageValue* aImageValue);
+                               mozilla::css::URLValue* aImageValue);
 void Gecko_CopyCursorArrayFrom(nsStyleUI* dest, const nsStyleUI* src);
 
 void Gecko_SetContentDataImageValue(nsStyleContentData* aList,
-                                    mozilla::css::ImageValue* aImageValue);
+                                    mozilla::css::URLValue* aImageValue);
 nsStyleContentData::CounterFunction* Gecko_SetCounterFunction(
     nsStyleContentData* content_data, mozilla::StyleContentType);
 
 // Dirtiness tracking.
 void Gecko_SetNodeFlags(RawGeckoNodeBorrowed node, uint32_t flags);
 void Gecko_UnsetNodeFlags(RawGeckoNodeBorrowed node, uint32_t flags);
 void Gecko_NoteDirtyElement(RawGeckoElementBorrowed element);
 void Gecko_NoteDirtySubtreeForInvalidation(RawGeckoElementBorrowed element);
@@ -542,20 +536,20 @@ void Gecko_nsStyleSVGPaint_CopyFrom(nsSt
 void Gecko_nsStyleSVGPaint_SetURLValue(nsStyleSVGPaint* paint, mozilla::css::URLValue* uri);
 void Gecko_nsStyleSVGPaint_Reset(nsStyleSVGPaint* paint);
 
 void Gecko_nsStyleSVG_SetDashArrayLength(nsStyleSVG* svg, uint32_t len);
 void Gecko_nsStyleSVG_CopyDashArray(nsStyleSVG* dst, const nsStyleSVG* src);
 void Gecko_nsStyleSVG_SetContextPropertiesLength(nsStyleSVG* svg, uint32_t len);
 void Gecko_nsStyleSVG_CopyContextProperties(nsStyleSVG* dst, const nsStyleSVG* src);
 
-mozilla::css::URLValue* Gecko_NewURLValue(ServoBundledURI uri);
+mozilla::css::URLValue* Gecko_URLValue_Create(ServoBundledURI uri, mozilla::CORSMode aCORSMode);
 size_t Gecko_URLValue_SizeOfIncludingThis(mozilla::css::URLValue* url);
-void Gecko_GetComputedURLSpec(const mozilla::css::URLValueData* url, nsCString* spec);
-void Gecko_GetComputedImageURLSpec(const mozilla::css::URLValueData* url, nsCString* spec);
+void Gecko_GetComputedURLSpec(const mozilla::css::URLValue* url, nsCString* spec);
+void Gecko_GetComputedImageURLSpec(const mozilla::css::URLValue* url, nsCString* spec);
 void Gecko_nsIURI_Debug(nsIURI*, nsCString* spec);
 
 NS_DECL_THREADSAFE_FFI_REFCOUNTING(mozilla::css::URLValue, CSSURLValue);
 NS_DECL_THREADSAFE_FFI_REFCOUNTING(RawGeckoURLExtraData, URLExtraData);
 
 void Gecko_FillAllImageLayers(nsStyleImageLayers* layers, uint32_t max_len);
 NS_DECL_THREADSAFE_FFI_REFCOUNTING(nsStyleCoord::Calc, Calc);
 
--- a/layout/style/ServoBindings.toml
+++ b/layout/style/ServoBindings.toml
@@ -475,19 +475,17 @@ raw-lines = [
     "pub type ComputedStyleBorrowed<'a> = &'a ::properties::ComputedValues;",
     "pub type ComputedStyleBorrowedOrNull<'a> = Option<&'a ::properties::ComputedValues>;",
     "pub type ServoComputedDataBorrowed<'a> = &'a ServoComputedData;",
     "pub type RawServoAnimationValueTableBorrowed<'a> = &'a ();"
 ]
 whitelist-functions = ["Servo_.*", "Gecko_.*"]
 structs-types = [
     "mozilla::css::GridTemplateAreasValue",
-    "mozilla::css::ImageValue",
     "mozilla::css::URLValue",
-    "mozilla::css::URLValueData",
     "mozilla::dom::CallerType",
     "mozilla::dom::ShadowRoot",
     "mozilla::AnonymousCounterStyle",
     "mozilla::AtomArray",
     "mozilla::CORSMode",
     "mozilla::FontStretch",
     "mozilla::FontSlantStyle",
     "mozilla::FontWeight",
--- a/layout/style/URLExtraData.h
+++ b/layout/style/URLExtraData.h
@@ -30,31 +30,32 @@ struct URLExtraData
     , mReferrer(std::move(aReferrer))
     , mReferrerPolicy(aReferrerPolicy)
     , mPrincipal(std::move(aPrincipal))
       // When we hold the URI data of a style sheet, mReferrer is always
       // equal to the sheet URI.
     , mIsChrome(mReferrer ? dom::IsChromeURI(mReferrer) : false)
   {
     MOZ_ASSERT(mBaseURI);
+    MOZ_ASSERT(mPrincipal);
   }
 
   URLExtraData(nsIURI* aBaseURI, nsIURI* aReferrer, nsIPrincipal* aPrincipal,
                net::ReferrerPolicy aReferrerPolicy)
     : URLExtraData(do_AddRef(aBaseURI),
                    do_AddRef(aReferrer),
                    do_AddRef(aPrincipal),
                    aReferrerPolicy) {}
 
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(URLExtraData)
 
   nsIURI* BaseURI() const { return mBaseURI; }
   nsIURI* GetReferrer() const { return mReferrer; }
   net::ReferrerPolicy GetReferrerPolicy() const { return mReferrerPolicy;}
-  nsIPrincipal* GetPrincipal() const { return mPrincipal; }
+  nsIPrincipal* Principal() const { return mPrincipal; }
 
   static URLExtraData* Dummy() {
     MOZ_ASSERT(sDummy);
     return sDummy;
   }
   static void InitDummy();
   static void ReleaseDummy();
 
--- a/layout/style/nsCSSValue.cpp
+++ b/layout/style/nsCSSValue.cpp
@@ -880,96 +880,73 @@ nsCSSValue::Array::SizeOfIncludingThis(m
 {
   size_t n = aMallocSizeOf(this);
   for (size_t i = 0; i < mCount; i++) {
     n += mArray[i].SizeOfExcludingThis(aMallocSizeOf);
   }
   return n;
 }
 
-css::URLValueData::URLValueData(already_AddRefed<nsIURI> aURI,
-                                ServoRawOffsetArc<RustString> aString,
-                                already_AddRefed<URLExtraData> aExtraData,
-                                CORSMode aCORSMode)
-  : mURI(std::move(aURI))
-  , mExtraData(std::move(aExtraData))
-  , mURIResolved(true)
-  , mString(aString)
-  , mCORSMode(aCORSMode)
+css::URLValue::~URLValue()
 {
-  MOZ_ASSERT(mExtraData);
-  MOZ_ASSERT(mExtraData->GetPrincipal());
-}
+  if (mLoadID != 0) {
+    ImageLoader::DeregisterCSSImageFromAllLoaders(this);
+  }
 
-css::URLValueData::URLValueData(ServoRawOffsetArc<RustString> aString,
-                                already_AddRefed<URLExtraData> aExtraData,
-                                CORSMode aCORSMode)
-  : mExtraData(std::move(aExtraData))
-  , mURIResolved(false)
-  , mString(aString)
-  , mCORSMode(aCORSMode)
-{
-  MOZ_ASSERT(mExtraData);
-  MOZ_ASSERT(mExtraData->GetPrincipal());
-}
-
-css::URLValueData::~URLValueData()
-{
   Servo_ReleaseArcStringData(&mString);
 }
 
 bool
-css::URLValueData::Equals(const URLValueData& aOther) const
+css::URLValue::Equals(const URLValue& aOther) const
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   bool eq;
   const URLExtraData* self = mExtraData;
   const URLExtraData* other = aOther.mExtraData;
   return GetString() == aOther.GetString() &&
           (GetURI() == aOther.GetURI() || // handles null == null
            (mURI && aOther.mURI &&
             NS_SUCCEEDED(mURI->Equals(aOther.mURI, &eq)) &&
             eq)) &&
           (self->BaseURI() == other->BaseURI() ||
            (NS_SUCCEEDED(self->BaseURI()->Equals(other->BaseURI(), &eq)) &&
             eq)) &&
-          (self->GetPrincipal() == other->GetPrincipal() ||
-           self->GetPrincipal()->Equals(other->GetPrincipal())) &&
+          self->Principal()->Equals(other->Principal()) &&
           IsLocalRef() == aOther.IsLocalRef();
 }
 
 bool
-css::URLValueData::DefinitelyEqualURIs(const URLValueData& aOther) const
+css::URLValue::DefinitelyEqualURIs(const URLValue& aOther) const
 {
   if (mExtraData->BaseURI() != aOther.mExtraData->BaseURI()) {
     return false;
   }
   return GetString() == aOther.GetString();
 }
 
 bool
-css::URLValueData::DefinitelyEqualURIsAndPrincipal(
-    const URLValueData& aOther) const
+css::URLValue::DefinitelyEqualURIsAndPrincipal(
+    const URLValue& aOther) const
 {
-  return mExtraData->GetPrincipal() == aOther.mExtraData->GetPrincipal() &&
+  return mExtraData->Principal() == aOther.mExtraData->Principal() &&
          DefinitelyEqualURIs(aOther);
 }
 
 nsDependentCSubstring
-css::URLValueData::GetString() const
+css::URLValue::GetString() const
 {
   const uint8_t* chars;
   uint32_t len;
   Servo_GetArcStringData(mString.mPtr, &chars, &len);
   return nsDependentCSubstring(reinterpret_cast<const char*>(chars), len);
 }
 
 nsIURI*
-css::URLValueData::GetURI() const
+css::URLValue::GetURI() const
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   if (!mURIResolved) {
     MOZ_ASSERT(!mURI);
     nsCOMPtr<nsIURI> newURI;
     NS_NewURI(getter_AddRefs(newURI),
               GetString(),
@@ -977,43 +954,43 @@ css::URLValueData::GetURI() const
     mURI = newURI.forget();
     mURIResolved = true;
   }
 
   return mURI;
 }
 
 bool
-css::URLValueData::IsLocalRef() const
+css::URLValue::IsLocalRef() const
 {
   if (mIsLocalRef.isNothing()) {
     // IsLocalRefURL is O(N), use it only when IsLocalRef is called.
     mIsLocalRef.emplace(nsContentUtils::IsLocalRefURL(GetString()));
   }
   return mIsLocalRef.value();
 }
 
 bool
-css::URLValueData::HasRef() const
+css::URLValue::HasRef() const
 {
   if (IsLocalRef()) {
     return true;
   }
 
   if (nsIURI* uri = GetURI()) {
     nsAutoCString ref;
     nsresult rv = uri->GetRef(ref);
     return NS_SUCCEEDED(rv) && !ref.IsEmpty();
   }
 
   return false;
 }
 
 already_AddRefed<nsIURI>
-css::URLValueData::ResolveLocalRef(nsIURI* aURI) const
+css::URLValue::ResolveLocalRef(nsIURI* aURI) const
 {
   nsCOMPtr<nsIURI> result = GetURI();
 
   if (result && IsLocalRef()) {
     nsCString ref;
     mURI->GetRef(ref);
 
     nsresult rv = NS_MutateURI(aURI)
@@ -1025,104 +1002,68 @@ css::URLValueData::ResolveLocalRef(nsIUR
       result = aURI;
     }
   }
 
   return result.forget();
 }
 
 already_AddRefed<nsIURI>
-css::URLValueData::ResolveLocalRef(nsIContent* aContent) const
+css::URLValue::ResolveLocalRef(nsIContent* aContent) const
 {
   nsCOMPtr<nsIURI> url = aContent->GetBaseURI();
   return ResolveLocalRef(url);
 }
 
 void
-css::URLValueData::GetSourceString(nsString& aRef) const
+css::URLValue::GetSourceString(nsString& aRef) const
 {
   nsIURI* uri = GetURI();
   if (!uri) {
     aRef.Truncate();
     return;
   }
 
   nsCString cref;
   if (IsLocalRef()) {
     // XXXheycam It's possible we can just return mString in this case, since
-    // it should be the "#fragment" string the URLValueData was created with.
+    // it should be the "#fragment" string the URLValue was created with.
     uri->GetRef(cref);
     cref.Insert('#', 0);
   } else {
     // It's not entirely clear how to best handle failure here. Ensuring the
     // string is empty seems safest.
     nsresult rv = uri->GetSpec(cref);
     if (NS_FAILED(rv)) {
       cref.Truncate();
     }
   }
 
   aRef = NS_ConvertUTF8toUTF16(cref);
 }
 
-bool
-css::URLValueData::EqualsExceptRef(nsIURI* aURI) const
-{
-  nsIURI* uri = GetURI();
-  if (!uri) {
-    return false;
-  }
-
-  bool ret = false;
-  uri->EqualsExceptRef(aURI, &ret);
-  return ret;
-}
-
 size_t
-css::URLValueData::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+css::URLValue::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
 {
   // Measurement of the following members may be added later if DMD finds it
   // is worthwhile:
   // - mURI
   // - mString
   // - mExtraData
-  return 0;
-}
 
-size_t
-css::URLValue::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
-{
   // Only measure it if it's unshared, to avoid double-counting.
   size_t n = 0;
   if (mRefCnt <= 1) {
     n += aMallocSizeOf(this);
-    n += URLValueData::SizeOfExcludingThis(aMallocSizeOf);
   }
   return n;
 }
 
-css::ImageValue::ImageValue(nsIURI* aURI,
-                            ServoRawOffsetArc<RustString> aString,
-                            already_AddRefed<URLExtraData> aExtraData,
-                            nsIDocument* aDocument,
-                            CORSMode aCORSMode)
-  : URLValueData(do_AddRef(aURI), aString, std::move(aExtraData), aCORSMode)
-{
-  LoadImage(aDocument);
-}
-
-css::ImageValue::ImageValue(ServoRawOffsetArc<RustString> aString,
-                            already_AddRefed<URLExtraData> aExtraData,
-                            CORSMode aCORSMode)
-  : URLValueData(aString, std::move(aExtraData), aCORSMode)
-{
-}
-
 imgRequestProxy*
-css::ImageValue::LoadImage(nsIDocument* aDocument)
+css::URLValue::LoadImage(nsIDocument* aDocument)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   static uint64_t sNextLoadID = 1;
 
   if (mLoadID == 0) {
     mLoadID = sNextLoadID++;
   }
@@ -1132,42 +1073,27 @@ css::ImageValue::LoadImage(nsIDocument* 
   // and clone it to aDocument.
   nsIDocument* loadingDoc = aDocument->GetOriginalDocument();
   if (!loadingDoc) {
     loadingDoc = aDocument;
   }
 
   // Kick off the load in the loading document.
   ImageLoader::LoadImage(GetURI(),
-                         mExtraData->GetPrincipal(),
+                         mExtraData->Principal(),
                          mExtraData->GetReferrer(),
                          mExtraData->GetReferrerPolicy(),
                          loadingDoc,
                          this,
                          mCORSMode);
 
   // Register the image in the document that's using it.
   return aDocument->StyleImageLoader()->RegisterCSSImage(this);
 }
 
-css::ImageValue::~ImageValue()
-{
-  if (mLoadID != 0) {
-    ImageLoader::DeregisterCSSImageFromAllLoaders(this);
-  }
-}
-
-size_t
-css::ImageValue::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
-{
-  size_t n = aMallocSizeOf(this);
-  n += css::URLValueData::SizeOfExcludingThis(aMallocSizeOf);
-  return n;
-}
-
 size_t
 mozilla::css::GridTemplateAreasValue::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
 {
   // Only measure it if it's unshared, to avoid double-counting.
   size_t n = 0;
   if (mRefCnt <= 1) {
     n += aMallocSizeOf(this);
     n += mNamedAreas.ShallowSizeOfExcludingThis(aMallocSizeOf);
--- a/layout/style/nsCSSValue.h
+++ b/layout/style/nsCSSValue.h
@@ -88,81 +88,77 @@ class CSSStyleSheet;
       dest->member_ = clm_clone;                                               \
       dest = clm_clone;                                                        \
     }                                                                          \
   }
 
 namespace mozilla {
 namespace css {
 
-struct URLValueData
+struct URLValue final
 {
-protected:
-  // Methods are not inline because using an nsIPrincipal means requiring
-  // caps, which leads to REQUIRES hell, since this header is included all
-  // over.
-
-  // aString must not be null.
-  // principal of aExtraData must not be null.
+public:
+  // aString and aExtraData must not be null.
+  //
   // Construct with a base URI; this will create the actual URI lazily from
   // aString and aExtraData.
-  URLValueData(ServoRawOffsetArc<RustString> aString,
-               already_AddRefed<URLExtraData> aExtraData,
-               CORSMode aCORSMode);
-  // Construct with the actual URI.
-  URLValueData(already_AddRefed<nsIURI> aURI,
-               ServoRawOffsetArc<RustString> aString,
-               already_AddRefed<URLExtraData> aExtraData,
-               CORSMode aCORSMode);
+  URLValue(ServoRawOffsetArc<RustString> aString,
+           already_AddRefed<URLExtraData> aExtraData,
+           CORSMode aCORSMode)
+    : mExtraData(std::move(aExtraData))
+    , mURIResolved(false)
+    , mString(aString)
+    , mCORSMode(aCORSMode)
+  {
+    MOZ_ASSERT(mExtraData);
+  }
 
-public:
-  // Returns true iff all fields of the two URLValueData objects are equal.
+  // Returns true iff all fields of the two URLValue objects are equal.
   //
   // Only safe to call on the main thread, since this will call Equals on the
-  // nsIURI and nsIPrincipal objects stored on the URLValueData objects.
-  bool Equals(const URLValueData& aOther) const;
+  // nsIURI and nsIPrincipal objects stored on the URLValue objects.
+  bool Equals(const URLValue& aOther) const;
 
   // Returns true iff we know for sure, by comparing the mBaseURI pointer,
   // the specified url() value mString, and the mIsLocalRef, that these
-  // two URLValueData objects represent the same computed url() value.
+  // two URLValue objects represent the same computed url() value.
   //
   // Doesn't look at mReferrer or mOriginPrincipal.
   //
   // Safe to call from any thread.
-  bool DefinitelyEqualURIs(const URLValueData& aOther) const;
+  bool DefinitelyEqualURIs(const URLValue& aOther) const;
 
   // Smae as DefinitelyEqualURIs but additionally compares the nsIPrincipal
-  // pointers of the two URLValueData objects.
-  bool DefinitelyEqualURIsAndPrincipal(const URLValueData& aOther) const;
+  // pointers of the two URLValue objects.
+  bool DefinitelyEqualURIsAndPrincipal(const URLValue& aOther) const;
 
   nsIURI* GetURI() const;
 
   bool IsLocalRef() const;
 
   bool HasRef() const;
 
-  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(URLValueData)
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(URLValue)
 
   // When matching a url with mIsLocalRef set, resolve it against aURI;
   // Otherwise, ignore aURL and return mURL directly.
   already_AddRefed<nsIURI> ResolveLocalRef(nsIURI* aURI) const;
   already_AddRefed<nsIURI> ResolveLocalRef(nsIContent* aContent) const;
 
   // Serializes mURI as a computed URI value, taking into account mIsLocalRef
   // and serializing just the fragment if true.
   void GetSourceString(nsString& aRef) const;
 
-  bool EqualsExceptRef(nsIURI* aURI) const;
+  nsDependentCSubstring GetString() const;
+
+  size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
 
-  bool IsStringEmpty() const
-  {
-    return GetString().IsEmpty();
-  }
+  imgRequestProxy* LoadImage(nsIDocument* aDocument);
 
-  nsDependentCSubstring GetString() const;
+  uint64_t LoadID() const { return mLoadID; }
 
 private:
   // mURI stores the lazily resolved URI.  This may be null if the URI is
   // invalid, even once resolved.
   mutable nsCOMPtr<nsIURI> mURI;
 
 public:
   RefPtr<URLExtraData> mExtraData;
@@ -170,96 +166,34 @@ public:
 private:
   mutable bool mURIResolved;
 
   // mIsLocalRef is set when url starts with a U+0023 number sign(#) character.
   mutable Maybe<bool> mIsLocalRef;
 
   mozilla::ServoRawOffsetArc<RustString> mString;
 
-protected:
   const CORSMode mCORSMode;
 
-  virtual ~URLValueData();
-
-  size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
-
-private:
-  URLValueData(const URLValueData& aOther) = delete;
-  URLValueData& operator=(const URLValueData& aOther) = delete;
-
-  friend struct ImageValue;
-};
-
-struct URLValue final : public URLValueData
-{
-  URLValue(ServoRawOffsetArc<RustString> aString,
-           already_AddRefed<URLExtraData> aExtraData)
-    : URLValueData(aString, std::move(aExtraData), CORSMode::CORS_NONE)
-  { }
-
-  URLValue(const URLValue&) = delete;
-  URLValue& operator=(const URLValue&) = delete;
-
-  size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
-};
-
-struct ImageValue final : public URLValueData
-{
-  // Not making the constructor and destructor inline because that would
-  // force us to include imgIRequest.h, which leads to REQUIRES hell, since
-  // this header is included all over.
-  //
-  // This constructor is only safe to call from the main thread.
-  ImageValue(nsIURI* aURI, const nsAString& aString,
-             already_AddRefed<URLExtraData> aExtraData,
-             nsIDocument* aDocument,
-             CORSMode aCORSMode);
-
-  // This constructor is only safe to call from the main thread.
-  ImageValue(nsIURI* aURI, ServoRawOffsetArc<RustString> aString,
-             already_AddRefed<URLExtraData> aExtraData,
-             nsIDocument* aDocument,
-             CORSMode aCORSMode);
-
-  // This constructor is safe to call from any thread, but Initialize
-  // must be called later for the object to be useful.
-  ImageValue(const nsAString& aString,
-             already_AddRefed<URLExtraData> aExtraData,
-             CORSMode aCORSMode);
-
-  // This constructor is safe to call from any thread, but Initialize
-  // must be called later for the object to be useful.
-  ImageValue(ServoRawOffsetArc<RustString> aURIString,
-             already_AddRefed<URLExtraData> aExtraData,
-             CORSMode aCORSMode);
-
-  ImageValue(const ImageValue&) = delete;
-  ImageValue& operator=(const ImageValue&) = delete;
-
-  imgRequestProxy* LoadImage(nsIDocument* aDocument);
-
-  size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
-
-  uint64_t LoadID() const { return mLoadID; }
-
-protected:
-  ~ImageValue();
-
-private:
-  // A unique, non-reused ID value for this ImageValue over the life of the
+  // A unique, non-reused ID value for this URLValue over the life of the
   // process.  This value is only valid after LoadImage has been called.
   //
   // We use this as a key in some tables in ImageLoader.  This is better than
   // using the pointer value of the ImageValue object, since we can sometimes
   // delete ImageValues OMT but cannot update the ImageLoader tables until
   // we're back on the main thread.  So to avoid dangling pointers that might
   // get re-used by the time we want to update the ImageLoader tables, we use
   // these IDs.
   uint64_t mLoadID = 0;
+
+  ~URLValue();
+
+private:
+  URLValue(const URLValue& aOther) = delete;
+  URLValue& operator=(const URLValue& aOther) = delete;
 };
 
 struct GridNamedArea {
   nsString mName;
   uint32_t mColumnStart;
   uint32_t mColumnEnd;
   uint32_t mRowStart;
   uint32_t mRowEnd;
@@ -435,18 +369,16 @@ struct nsCSSValueSharedList;
 struct nsCSSValuePairList;
 struct nsCSSValuePairList_heap;
 
 class nsCSSValue {
 public:
   struct Array;
   friend struct Array;
 
-  friend struct mozilla::css::ImageValue;
-
   // for valueless units only (null, auto, inherit, none, all, normal)
   explicit nsCSSValue(nsCSSUnit aUnit = eCSSUnit_Null)
     : mUnit(aUnit)
   {
     MOZ_ASSERT(aUnit <= eCSSUnit_DummyInherit, "not a valueless unit");
   }
 
   nsCSSValue(int32_t aValue, nsCSSUnit aUnit);
--- a/layout/style/nsComputedDOMStyle.cpp
+++ b/layout/style/nsComputedDOMStyle.cpp
@@ -657,17 +657,17 @@ AddImageURL(nsIURI& aURI, nsTArray<nsStr
     return;
   }
 
   aURLs.AppendElement(NS_ConvertUTF8toUTF16(spec));
 }
 
 
 static void
-AddImageURL(const css::URLValueData& aURL, nsTArray<nsString>& aURLs)
+AddImageURL(const css::URLValue& aURL, nsTArray<nsString>& aURLs)
 {
   if (aURL.IsLocalRef()) {
     return;
   }
 
   if (nsIURI* uri = aURL.GetURI()) {
     AddImageURL(*uri, aURLs);
   }
@@ -1691,25 +1691,25 @@ nsComputedDOMStyle::SetValueToPosition(
 
   RefPtr<nsROCSSPrimitiveValue> valY = new nsROCSSPrimitiveValue;
   SetValueToPositionCoord(aPosition.mYPosition, valY);
   aValueList->AppendCSSValue(valY.forget());
 }
 
 
 void
-nsComputedDOMStyle::SetValueToURLValue(const css::URLValueData* aURL,
+nsComputedDOMStyle::SetValueToURLValue(const css::URLValue* aURL,
                                        nsROCSSPrimitiveValue* aValue)
 {
   if (!aURL) {
     aValue->SetIdent(eCSSKeyword_none);
     return;
   }
 
-  // If we have a usable nsIURI in the URLValueData, and the url() wasn't
+  // If we have a usable nsIURI in the URLValue, and the url() wasn't
   // a fragment-only URL, serialize the nsIURI.
   if (!aURL->IsLocalRef()) {
     if (nsIURI* uri = aURL->GetURI()) {
       aValue->SetURI(uri);
       return;
     }
   }
 
--- a/layout/style/nsComputedDOMStyle.h
+++ b/layout/style/nsComputedDOMStyle.h
@@ -465,17 +465,17 @@ private:
                                 const mozilla::StyleComplexColor& aColor);
   void SetValueForWidgetColor(nsROCSSPrimitiveValue* aValue,
                               const mozilla::StyleComplexColor& aColor,
                               mozilla::StyleAppearance aWidgetType);
   void SetValueToPositionCoord(const mozilla::Position::Coord& aCoord,
                                nsROCSSPrimitiveValue* aValue);
   void SetValueToPosition(const mozilla::Position& aPosition,
                           nsDOMCSSValueList* aValueList);
-  void SetValueToURLValue(const mozilla::css::URLValueData* aURL,
+  void SetValueToURLValue(const mozilla::css::URLValue* aURL,
                           nsROCSSPrimitiveValue* aValue);
 
   /**
    * A method to get a percentage base for a percentage value.  Returns true
    * if a percentage base value was determined, false otherwise.
    */
   typedef bool (nsComputedDOMStyle::*PercentageBaseGetter)(nscoord&);
 
--- a/layout/style/nsStyleStruct.cpp
+++ b/layout/style/nsStyleStruct.cpp
@@ -54,26 +54,26 @@ using namespace mozilla::dom;
 static constexpr size_t kStyleStructSizeLimit = 504;
 #define STYLE_STRUCT(name_) \
   static_assert(sizeof(nsStyle##name_) <= kStyleStructSizeLimit, \
                 "nsStyle" #name_ " became larger than the size limit");
 #include "nsStyleStructList.h"
 #undef STYLE_STRUCT
 
 static bool
-DefinitelyEqualURIs(css::URLValueData* aURI1,
-                    css::URLValueData* aURI2)
+DefinitelyEqualURIs(css::URLValue* aURI1,
+                    css::URLValue* aURI2)
 {
   return aURI1 == aURI2 ||
          (aURI1 && aURI2 && aURI1->DefinitelyEqualURIs(*aURI2));
 }
 
 static bool
-DefinitelyEqualURIsAndPrincipal(css::URLValueData* aURI1,
-                                css::URLValueData* aURI2)
+DefinitelyEqualURIsAndPrincipal(css::URLValue* aURI1,
+                                css::URLValue* aURI2)
 {
   return aURI1 == aURI2 ||
          (aURI1 && aURI2 && aURI1->DefinitelyEqualURIsAndPrincipal(*aURI2));
 }
 
 static bool
 DefinitelyEqualImages(nsStyleImageRequest* aRequest1,
                       nsStyleImageRequest* aRequest2)
@@ -1246,17 +1246,17 @@ nsStyleSVGReset::nsStyleSVGReset(const n
 void
 nsStyleSVGReset::FinishStyle(nsPresContext* aPresContext, const nsStyleSVGReset* aOldStyle)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   NS_FOR_VISIBLE_IMAGE_LAYERS_BACK_TO_FRONT(i, mMask) {
     nsStyleImage& image = mMask.mLayers[i].mImage;
     if (image.GetType() == eStyleImageType_Image) {
-      css::URLValueData* url = image.GetURLValue();
+      css::URLValue* url = image.GetURLValue();
       // If the url is a local ref, it must be a <mask-resource>, so we don't
       // need to resolve the style image.
       if (url->IsLocalRef()) {
         continue;
       }
 #if 0
       // XXX The old style system also checks whether this is a reference to
       // the current document with reference, but it doesn't seem to be a
@@ -2078,17 +2078,17 @@ private:
   Mode mModeFlags;
   // Since we always dispatch this runnable to the main thread, these will be
   // released on the main thread when the runnable itself is released.
   RefPtr<imgRequestProxy> mRequestProxy;
   RefPtr<ImageTracker> mImageTracker;
 };
 
 nsStyleImageRequest::nsStyleImageRequest(Mode aModeFlags,
-                                         css::ImageValue* aImageValue)
+                                         css::URLValue* aImageValue)
   : mImageValue(aImageValue)
   , mModeFlags(aModeFlags)
   , mResolved(false)
 {
 }
 
 nsStyleImageRequest::~nsStyleImageRequest()
 {
@@ -2660,17 +2660,17 @@ nsStyleImage::GetImageURI() const
   if (mType != eStyleImageType_Image) {
     return nullptr;
   }
 
   nsCOMPtr<nsIURI> uri = mImage->GetImageURI();
   return uri.forget();
 }
 
-css::URLValueData*
+css::URLValue*
 nsStyleImage::GetURLValue() const
 {
   if (mType == eStyleImageType_Image) {
     return mImage->GetImageValue();
   } else if (mType == eStyleImageType_URL) {
     return mURLValue;
   }
 
--- a/layout/style/nsStyleStruct.h
+++ b/layout/style/nsStyleStruct.h
@@ -214,25 +214,23 @@ private:
  * This can be called from any thread.  The nsStyleImageRequest is not
  * considered "resolved" at this point, and the Resolve() method must be called
  * later to initiate the image load and make calls to get() valid.
  *
  * Calls to TrackImage(), UntrackImage(), LockImage(), UnlockImage() and
  * RequestDiscard() are made to the imgRequestProxy and ImageTracker as
  * appropriate, according to the mode flags passed in to the constructor.
  *
- * The constructor receives a css::ImageValue to represent the url()
+ * The constructor receives a css::URLValue to represent the url()
  * information, which is held on to for the comparisons done in
  * DefinitelyEquals().
  */
 class nsStyleImageRequest
 {
 public:
-  typedef mozilla::css::URLValueData URLValueData;
-
   // Flags describing whether the imgRequestProxy must be tracked in the
   // ImageTracker, whether LockImage/UnlockImage calls will be made
   // when obtaining and releasing the imgRequestProxy, and whether
   // RequestDiscard will be called on release.
   enum class Mode : uint8_t {
     // The imgRequestProxy will be added to the ImageTracker when resolved
     // Without this flag, the nsStyleImageRequest itself will call LockImage/
     // UnlockImage on the imgRequestProxy, rather than leaving locking to the
@@ -246,47 +244,47 @@ public:
     // the nsStyleImageRequest is going away.
     //
     // This is currently used only for cursor images.
     Discard = 0x2,
   };
 
   // Can be called from any thread, but Resolve() must be called later
   // on the main thread before get() can be used.
-  nsStyleImageRequest(Mode aModeFlags, mozilla::css::ImageValue* aImageValue);
+  nsStyleImageRequest(Mode aModeFlags, mozilla::css::URLValue* aImageValue);
 
   bool Resolve(nsPresContext*, const nsStyleImageRequest* aOldImageRequest);
   bool IsResolved() const { return mResolved; }
 
   imgRequestProxy* get() {
     MOZ_ASSERT(IsResolved(), "Resolve() must be called first");
     MOZ_ASSERT(NS_IsMainThread());
     return mRequestProxy.get();
   }
   const imgRequestProxy* get() const {
     return const_cast<nsStyleImageRequest*>(this)->get();
   }
 
-  // Returns whether the ImageValue objects in the two nsStyleImageRequests
-  // return true from URLValueData::DefinitelyEqualURIs.
+  // Returns whether the URLValue objects in the two nsStyleImageRequests
+  // return true from URLValue::DefinitelyEqualURIs.
   bool DefinitelyEquals(const nsStyleImageRequest& aOther) const;
 
-  mozilla::css::ImageValue* GetImageValue() const { return mImageValue; }
+  mozilla::css::URLValue* GetImageValue() const { return mImageValue; }
 
   already_AddRefed<nsIURI> GetImageURI() const;
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(nsStyleImageRequest);
 
 private:
   ~nsStyleImageRequest();
   nsStyleImageRequest& operator=(const nsStyleImageRequest& aOther) = delete;
 
   void MaybeTrackAndLock();
 
   RefPtr<imgRequestProxy> mRequestProxy;
-  RefPtr<mozilla::css::ImageValue> mImageValue;
+  RefPtr<mozilla::css::URLValue> mImageValue;
   RefPtr<mozilla::dom::ImageTracker> mImageTracker;
 
   // Cache DocGroup for dispatching events in the destructor.
   RefPtr<mozilla::dom::DocGroup> mDocGroup;
 
   Mode mModeFlags;
   bool mResolved;
 };
@@ -330,18 +328,17 @@ private:
  * (3) An element within a document, or an <img>, <video>, or <canvas> element
  *     not in a document.
  * (*) Optionally a crop rect can be set to paint a partial (rectangular)
  * region of an image. (Currently, this feature is only supported with an
  * image of type (1)).
  */
 struct nsStyleImage
 {
-  typedef mozilla::css::URLValue     URLValue;
-  typedef mozilla::css::URLValueData URLValueData;
+  typedef mozilla::css::URLValue URLValue;
 
   nsStyleImage();
   ~nsStyleImage();
   nsStyleImage(const nsStyleImage& aOther);
   nsStyleImage& operator=(const nsStyleImage& aOther);
 
   void SetNull();
   void SetImageRequest(already_AddRefed<nsStyleImageRequest> aImage);
@@ -385,17 +382,17 @@ struct nsStyleImage
   const mozilla::UniquePtr<nsStyleSides>& GetCropRect() const {
     NS_ASSERTION(mType == eStyleImageType_Image,
                  "Only image data can have a crop rect");
     return mCropRect;
   }
 
   already_AddRefed<nsIURI> GetImageURI() const;
 
-  URLValueData* GetURLValue() const;
+  URLValue* GetURLValue() const;
 
   /**
    * Compute the actual crop rect in pixels, using the source image bounds.
    * The computation involves converting percentage unit to pixel unit and
    * clamping each side value to fit in the source image bounds.
    * @param aActualCropRect the computed actual crop rect.
    * @param aIsEntireImage true iff |aActualCropRect| is identical to the
    * source image bounds.
--- a/layout/svg/SVGObserverUtils.cpp
+++ b/layout/svg/SVGObserverUtils.cpp
@@ -22,17 +22,17 @@
 #include "SVGTextPathElement.h"
 #include "SVGUseElement.h"
 #include "ImageLoader.h"
 #include "mozilla/net/ReferrerPolicy.h"
 
 using namespace mozilla::dom;
 
 static already_AddRefed<URLAndReferrerInfo>
-ResolveURLUsingLocalRef(nsIFrame* aFrame, const css::URLValueData* aURL)
+ResolveURLUsingLocalRef(nsIFrame* aFrame, const css::URLValue* aURL)
 {
   MOZ_ASSERT(aFrame);
 
   if (!aURL) {
     return nullptr;
   }
 
   nsCOMPtr<nsIURI> uri = aURL->GetURI();
@@ -795,17 +795,17 @@ private:
 NS_IMPL_ISUPPORTS(SVGMaskObserverList, nsISupports)
 
 SVGMaskObserverList::SVGMaskObserverList(nsIFrame* aFrame)
   : mFrame(aFrame)
 {
   const nsStyleSVGReset *svgReset = aFrame->StyleSVGReset();
 
   for (uint32_t i = 0; i < svgReset->mMask.mImageCount; i++) {
-    css::URLValueData* data = svgReset->mMask.mLayers[i].mImage.GetURLValue();
+    css::URLValue* data = svgReset->mMask.mLayers[i].mImage.GetURLValue();
     RefPtr<URLAndReferrerInfo> maskUri = ResolveURLUsingLocalRef(aFrame, data);
 
     bool hasRef = false;
     if (maskUri) {
       maskUri->GetURI()->GetHasRef(&hasRef);
     }
 
     // Accrording to maskUri, nsSVGPaintingProperty's ctor may trigger an
--- a/servo/components/style/gecko/conversions.rs
+++ b/servo/components/style/gecko/conversions.rs
@@ -220,23 +220,23 @@ fn line_direction(horizontal: LengthOrPe
 }
 
 impl nsStyleImage {
     /// Set a given Servo `Image` value into this `nsStyleImage`.
     pub fn set(&mut self, image: Image) {
         match image {
             GenericImage::Gradient(boxed_gradient) => self.set_gradient(*boxed_gradient),
             GenericImage::Url(ref url) => unsafe {
-                bindings::Gecko_SetLayerImageImageValue(self, url.0.image_value.get());
+                bindings::Gecko_SetLayerImageImageValue(self, (url.0).0.url_value.get());
             },
             GenericImage::Rect(ref image_rect) => {
                 unsafe {
                     bindings::Gecko_SetLayerImageImageValue(
                         self,
-                        image_rect.url.0.image_value.get(),
+                        (image_rect.url.0).0.url_value.get(),
                     );
                     bindings::Gecko_InitializeImageCropRect(self);
 
                     // Set CropRect
                     let ref mut rect = *self.mCropRect.mPtr;
                     image_rect
                         .top
                         .to_gecko_style_coord(&mut rect.data_at_mut(0));
--- a/servo/components/style/gecko/url.rs
+++ b/servo/components/style/gecko/url.rs
@@ -2,20 +2,19 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 //! Common handling for the specified value CSS url() values.
 
 use cssparser::Parser;
 use gecko_bindings::bindings;
 use gecko_bindings::structs::ServoBundledURI;
-use gecko_bindings::structs::mozilla::css::URLValueData;
 use gecko_bindings::structs::root::{RustString, nsStyleImageRequest};
 use gecko_bindings::structs::root::mozilla::CORSMode;
-use gecko_bindings::structs::root::mozilla::css::{ImageValue, URLValue};
+use gecko_bindings::structs::root::mozilla::css::URLValue;
 use gecko_bindings::sugar::refptr::RefPtr;
 use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
 use nsstring::nsCString;
 use parser::{Parse, ParserContext};
 use servo_arc::{Arc, RawOffsetArc};
 use std::fmt::{self, Write};
 use std::mem;
 use style_traits::{CssWriter, ParseError, ToCss};
@@ -33,34 +32,33 @@ pub struct CssUrl {
     serialization: Arc<String>,
 
     /// The URL extra data.
     #[css(skip)]
     pub extra_data: UrlExtraData,
 }
 
 impl CssUrl {
-    /// Try to parse a URL from a string value that is a valid CSS token for a
-    /// URL.
+    /// Parse a URL from a string value that is a valid CSS token for a URL.
     pub fn parse_from_string(url: String, context: &ParserContext) -> Self {
         CssUrl {
             serialization: Arc::new(url),
             extra_data: context.url_data.clone(),
         }
     }
 
     /// Returns true if the URL is definitely invalid. We don't eagerly resolve
     /// URLs in gecko, so we just return false here.
     /// use its |resolved| status.
     pub fn is_invalid(&self) -> bool {
         false
     }
 
-    /// Convert from URLValueData to SpecifiedUrl.
-    unsafe fn from_url_value_data(url: &URLValueData) -> Self {
+    /// Convert from URLValue to CssUrl.
+    unsafe fn from_url_value(url: &URLValue) -> Self {
         let arc_type = &url.mString as *const _ as *const RawOffsetArc<String>;
         CssUrl {
             serialization: Arc::from_raw_offset((*arc_type).clone()),
             extra_data: UrlExtraData(url.mExtraData.to_safe()),
         }
     }
 
     /// Returns true if this URL looks like a fragment.
@@ -112,234 +110,213 @@ impl MallocSizeOf for CssUrl {
 
         // We ignore `extra_data`, because RefPtr is tricky, and there aren't
         // many of them in practise (sharing is common).
 
         0
     }
 }
 
-/// A specified url() value for general usage.
+/// A specified non-image `url()` value.
 #[derive(Clone, Debug, SpecifiedValueInfo, ToCss)]
 pub struct SpecifiedUrl {
     /// The specified url value.
     pub url: CssUrl,
     /// Gecko's URLValue so that we can reuse it while rematching a
     /// property with this specified value.
     #[css(skip)]
     pub url_value: RefPtr<URLValue>,
 }
 
 impl SpecifiedUrl {
-    fn from_css_url(url: CssUrl) -> Self {
+    /// Parse a URL from a string value.
+    pub fn parse_from_string(url: String, context: &ParserContext) -> Self {
+        Self::from_css_url(CssUrl::parse_from_string(url, context))
+    }
+
+    fn from_css_url_with_cors(url: CssUrl, cors: CORSMode) -> Self {
         let url_value = unsafe {
-            let ptr = bindings::Gecko_NewURLValue(url.for_ffi());
-            // We do not expect Gecko_NewURLValue returns null.
+            let ptr = bindings::Gecko_URLValue_Create(url.for_ffi(), cors);
+            // We do not expect Gecko_URLValue_Create returns null.
             debug_assert!(!ptr.is_null());
             RefPtr::from_addrefed(ptr)
         };
         Self { url, url_value }
     }
+
+    fn from_css_url(url: CssUrl) -> Self {
+        use gecko_bindings::structs::root::mozilla::CORSMode_CORS_NONE;
+        Self::from_css_url_with_cors(url, CORSMode_CORS_NONE)
+    }
+
+    fn from_css_url_with_cors_anonymous(url: CssUrl) -> Self {
+        use gecko_bindings::structs::root::mozilla::CORSMode_CORS_ANONYMOUS;
+        Self::from_css_url_with_cors(url, CORSMode_CORS_ANONYMOUS)
+    }
+}
+
+impl Parse for SpecifiedUrl {
+    fn parse<'i, 't>(
+        context: &ParserContext,
+        input: &mut Parser<'i, 't>,
+    ) -> Result<Self, ParseError<'i>> {
+        CssUrl::parse(context, input).map(Self::from_css_url)
+    }
 }
 
 impl PartialEq for SpecifiedUrl {
     fn eq(&self, other: &Self) -> bool {
         self.url.eq(&other.url)
     }
 }
 
 impl Eq for SpecifiedUrl {}
 
-impl Parse for SpecifiedUrl {
-    fn parse<'i, 't>(
-        context: &ParserContext,
-        input: &mut Parser<'i, 't>,
-    ) -> Result<Self, ParseError<'i>> {
-        CssUrl::parse(context, input).map(Self::from_css_url)
-    }
-}
-
 impl MallocSizeOf for SpecifiedUrl {
     fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
         let mut n = self.url.size_of(ops);
         // Although this is a RefPtr, this is the primary reference because
         // SpecifiedUrl is responsible for creating the url_value. So we
         // measure unconditionally here.
         n += unsafe { bindings::Gecko_URLValue_SizeOfIncludingThis(self.url_value.get()) };
         n
     }
 }
 
-/// A specified url() value for image.
-///
-/// This exists so that we can construct `ImageValue` and reuse it.
-#[derive(Clone, Debug, SpecifiedValueInfo, ToCss)]
-pub struct SpecifiedImageUrl {
-    /// The specified url value.
-    pub url: CssUrl,
-    /// Gecko's ImageValue so that we can reuse it while rematching a
-    /// property with this specified value.
-    #[css(skip)]
-    pub image_value: RefPtr<ImageValue>,
-}
-
-impl SpecifiedImageUrl {
-    /// Parse a URL from a string value. See SpecifiedUrl::parse_from_string.
-    pub fn parse_from_string(url: String, context: &ParserContext) -> Self {
-        Self::from_css_url(CssUrl::parse_from_string(url, context))
-    }
-
-    fn from_css_url_with_cors(url: CssUrl, cors: CORSMode) -> Self {
-        let image_value = unsafe {
-            let ptr = bindings::Gecko_ImageValue_Create(url.for_ffi(), cors);
-            // We do not expect Gecko_ImageValue_Create returns null.
-            debug_assert!(!ptr.is_null());
-            RefPtr::from_addrefed(ptr)
-        };
-        Self { url, image_value }
-    }
-
-    fn from_css_url(url: CssUrl) -> Self {
-        use gecko_bindings::structs::root::mozilla::CORSMode_CORS_NONE;
-        Self::from_css_url_with_cors(url, CORSMode_CORS_NONE)
-    }
-
-    fn from_css_url_with_cors_anonymous(url: CssUrl) -> Self {
-        use gecko_bindings::structs::root::mozilla::CORSMode_CORS_ANONYMOUS;
-        Self::from_css_url_with_cors(url, CORSMode_CORS_ANONYMOUS)
-    }
-
-    /// Provides an alternate method for parsing that associates the URL
-    /// with anonymous CORS headers.
-    pub fn parse_with_cors_anonymous<'i, 't>(
-        context: &ParserContext,
-        input: &mut Parser<'i, 't>,
-    ) -> Result<Self, ParseError<'i>> {
-        CssUrl::parse(context, input).map(Self::from_css_url_with_cors_anonymous)
-    }
-}
-
-impl Parse for SpecifiedImageUrl {
-    fn parse<'i, 't>(
-        context: &ParserContext,
-        input: &mut Parser<'i, 't>,
-    ) -> Result<Self, ParseError<'i>> {
-        CssUrl::parse(context, input).map(Self::from_css_url)
-    }
-}
-
-impl PartialEq for SpecifiedImageUrl {
-    fn eq(&self, other: &Self) -> bool {
-        self.url.eq(&other.url)
-    }
-}
-
-impl Eq for SpecifiedImageUrl {}
-
-impl MallocSizeOf for SpecifiedImageUrl {
-    fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
-        let mut n = self.url.size_of(ops);
-        // Although this is a RefPtr, this is the primary reference because
-        // SpecifiedUrl is responsible for creating the image_value. So we
-        // measure unconditionally here.
-        n += unsafe { bindings::Gecko_ImageValue_SizeOfIncludingThis(self.image_value.get()) };
-        n
-    }
-}
-
 impl ToComputedValue for SpecifiedUrl {
     type ComputedValue = ComputedUrl;
 
     #[inline]
     fn to_computed_value(&self, _: &Context) -> Self::ComputedValue {
         ComputedUrl(self.clone())
     }
 
     #[inline]
     fn from_computed_value(computed: &Self::ComputedValue) -> Self {
         computed.0.clone()
     }
 }
 
+/// A specified image `url()` value.
+#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss)]
+pub struct SpecifiedImageUrl(pub SpecifiedUrl);
+
+impl SpecifiedImageUrl {
+    /// Parse a URL from a string value that is a valid CSS token for a URL.
+    pub fn parse_from_string(url: String, context: &ParserContext) -> Self {
+        SpecifiedImageUrl(SpecifiedUrl::parse_from_string(url, context))
+    }
+
+    /// Provides an alternate method for parsing that associates the URL
+    /// with anonymous CORS headers.
+    pub fn parse_with_cors_anonymous<'i, 't>(
+        context: &ParserContext,
+        input: &mut Parser<'i, 't>,
+    ) -> Result<Self, ParseError<'i>> {
+        CssUrl::parse(context, input)
+            .map(SpecifiedUrl::from_css_url_with_cors_anonymous)
+            .map(SpecifiedImageUrl)
+    }
+}
+
+impl Parse for SpecifiedImageUrl {
+    fn parse<'i, 't>(
+        context: &ParserContext,
+        input: &mut Parser<'i, 't>,
+    ) -> Result<Self, ParseError<'i>> {
+        SpecifiedUrl::parse(context, input).map(SpecifiedImageUrl)
+    }
+}
+
 impl ToComputedValue for SpecifiedImageUrl {
     type ComputedValue = ComputedImageUrl;
 
     #[inline]
     fn to_computed_value(&self, _: &Context) -> Self::ComputedValue {
         ComputedImageUrl(self.clone())
     }
 
     #[inline]
     fn from_computed_value(computed: &Self::ComputedValue) -> Self {
         computed.0.clone()
     }
 }
 
 fn serialize_computed_url<W>(
-    url_value_data: &URLValueData,
+    url_value: &URLValue,
     dest: &mut CssWriter<W>,
-    get_url: unsafe extern "C" fn(*const URLValueData, *mut nsCString),
+    get_url: unsafe extern "C" fn(*const URLValue, *mut nsCString) -> (),
 ) -> fmt::Result
 where
     W: Write,
 {
     dest.write_str("url(")?;
     unsafe {
         let mut string = nsCString::new();
-        get_url(url_value_data, &mut string);
+        get_url(url_value, &mut string);
         string.as_str_unchecked().to_css(dest)?;
     }
     dest.write_char(')')
 }
 
-/// The computed value of a CSS `url()`.
+/// The computed value of a CSS non-image `url()`.
 ///
 /// The only difference between specified and computed URLs is the
 /// serialization.
 #[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq)]
 pub struct ComputedUrl(pub SpecifiedUrl);
 
 impl ToCss for ComputedUrl {
     fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
     where
         W: Write,
     {
         serialize_computed_url(
-            &self.0.url_value._base,
+            &self.0.url_value,
             dest,
             bindings::Gecko_GetComputedURLSpec,
         )
     }
 }
 
 impl ComputedUrl {
     /// Convert from RefPtr<URLValue> to ComputedUrl.
     pub unsafe fn from_url_value(url_value: RefPtr<URLValue>) -> Self {
-        let url = CssUrl::from_url_value_data(&url_value._base);
+        let url = CssUrl::from_url_value(&*url_value);
         ComputedUrl(SpecifiedUrl { url, url_value })
     }
+
+    /// Get a raw pointer to the URLValue held by this ComputedUrl, for FFI.
+    pub fn url_value_ptr(&self) -> *mut URLValue {
+        self.0.url_value.get()
+    }
 }
 
-/// The computed value of a CSS `url()` for image.
+/// The computed value of a CSS image `url()`.
 #[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq)]
 pub struct ComputedImageUrl(pub SpecifiedImageUrl);
 
 impl ToCss for ComputedImageUrl {
     fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
     where
         W: Write,
     {
         serialize_computed_url(
-            &self.0.image_value._base,
+            &(self.0).0.url_value,
             dest,
             bindings::Gecko_GetComputedImageURLSpec,
         )
     }
 }
 
 impl ComputedImageUrl {
     /// Convert from nsStyleImageReques to ComputedImageUrl.
     pub unsafe fn from_image_request(image_request: &nsStyleImageRequest) -> Self {
-        let image_value = image_request.mImageValue.to_safe();
-        let url = CssUrl::from_url_value_data(&image_value._base);
-        ComputedImageUrl(SpecifiedImageUrl { url, image_value })
+        let url_value = image_request.mImageValue.to_safe();
+        let url = CssUrl::from_url_value(&*url_value);
+        ComputedImageUrl(SpecifiedImageUrl(SpecifiedUrl { url, url_value }))
+    }
+
+    /// Get a raw pointer to the URLValue held by this ComputedImageUrl, for FFI.
+    pub fn url_value_ptr(&self) -> *mut URLValue {
+        (self.0).0.url_value.get()
     }
 }
--- a/servo/components/style/gecko_bindings/sugar/refptr.rs
+++ b/servo/components/style/gecko_bindings/sugar/refptr.rs
@@ -293,21 +293,16 @@ impl_threadsafe_refcount!(
     bindings::Gecko_ReleaseCSSURLValueArbitraryThread
 );
 impl_threadsafe_refcount!(
     structs::mozilla::css::GridTemplateAreasValue,
     bindings::Gecko_AddRefGridTemplateAreasValueArbitraryThread,
     bindings::Gecko_ReleaseGridTemplateAreasValueArbitraryThread
 );
 impl_threadsafe_refcount!(
-    structs::ImageValue,
-    bindings::Gecko_AddRefImageValueArbitraryThread,
-    bindings::Gecko_ReleaseImageValueArbitraryThread
-);
-impl_threadsafe_refcount!(
     structs::SharedFontList,
     bindings::Gecko_AddRefSharedFontListArbitraryThread,
     bindings::Gecko_ReleaseSharedFontListArbitraryThread
 );
 impl_threadsafe_refcount!(
     structs::SheetLoadDataHolder,
     bindings::Gecko_AddRefSheetLoadDataHolderArbitraryThread,
     bindings::Gecko_ReleaseSheetLoadDataHolderArbitraryThread
--- a/servo/components/style/properties/gecko.mako.rs
+++ b/servo/components/style/properties/gecko.mako.rs
@@ -684,17 +684,20 @@ def set_gecko_property(ffi_name, expr):
             SVGPaintKind::ContextFill => {
                 paint.mType = nsStyleSVGPaintType::eStyleSVGPaintType_ContextFill;
             }
             SVGPaintKind::ContextStroke => {
                 paint.mType = nsStyleSVGPaintType::eStyleSVGPaintType_ContextStroke;
             }
             SVGPaintKind::PaintServer(url) => {
                 unsafe {
-                    bindings::Gecko_nsStyleSVGPaint_SetURLValue(paint, url.0.url_value.get());
+                    bindings::Gecko_nsStyleSVGPaint_SetURLValue(
+                        paint,
+                        url.url_value_ptr(),
+                    )
                 }
             }
             SVGPaintKind::Color(color) => {
                 paint.mType = nsStyleSVGPaintType::eStyleSVGPaintType_Color;
                 unsafe {
                     *paint.mPaint.mColor.as_mut() = color.into();
                 }
             }
@@ -4148,17 +4151,20 @@ fn static_assert() {
         match image {
             UrlOrNone::None => {
                 unsafe {
                     Gecko_SetListStyleImageNone(&mut self.gecko);
                 }
             }
             UrlOrNone::Url(ref url) => {
                 unsafe {
-                    Gecko_SetListStyleImageImageValue(&mut self.gecko, url.0.image_value.get());
+                    Gecko_SetListStyleImageImageValue(
+                        &mut self.gecko,
+                        url.url_value_ptr(),
+                    );
                 }
             }
         }
     }
 
     pub fn copy_list_style_image_from(&mut self, other: &Self) {
         unsafe { Gecko_CopyListStyleImageFrom(&mut self.gecko, &other.gecko); }
     }
@@ -5353,17 +5359,17 @@ clip-path
 
         unsafe {
             Gecko_SetCursorArrayLength(&mut self.gecko, v.images.len());
         }
         for i in 0..v.images.len() {
             unsafe {
                 Gecko_SetCursorImageValue(
                     &mut self.gecko.mCursorImages[i],
-                    v.images[i].url.0.image_value.get(),
+                    v.images[i].url.url_value_ptr(),
                 );
             }
 
             match v.images[i].hotspot {
                 Some((x, y)) => {
                     self.gecko.mCursorImages[i].mHaveHotspot = true;
                     self.gecko.mCursorImages[i].mHotspotX = x;
                     self.gecko.mCursorImages[i].mHotspotY = y;
@@ -5654,17 +5660,17 @@ clip-path
                                 style.clone(),
                                 device,
                             );
                         }
                         ContentItem::Url(ref url) => {
                             unsafe {
                                 bindings::Gecko_SetContentDataImageValue(
                                     &mut self.gecko.mContents[i],
-                                    url.0.image_value.get(),
+                                    url.url_value_ptr(),
                                 )
                             }
                         }
                     }
                 }
             }
         }
     }
--- a/toolkit/components/telemetry/core/Telemetry.cpp
+++ b/toolkit/components/telemetry/core/Telemetry.cpp
@@ -1,107 +1,102 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+#include "Telemetry.h"
+
 #include <algorithm>
-
 #include <prio.h>
 #include <prproces.h>
 #if defined(XP_UNIX) && !defined(XP_DARWIN)
 #include <time.h>
 #else
 #include <chrono>
 #endif
-
+#include "base/pickle.h"
+#if defined(MOZ_TELEMETRY_GECKOVIEW)
+#include "geckoview/TelemetryGeckoViewPersistence.h"
+#endif
+#include "ipc/TelemetryIPCAccumulator.h"
+#include "jsapi.h"
+#include "jsfriendapi.h"
+#include "js/GCAPI.h"
 #include "mozilla/dom/ToJSValue.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/Atomics.h"
 #include "mozilla/Attributes.h"
+#include "mozilla/BackgroundHangMonitor.h"
 #include "mozilla/DebugOnly.h"
+#include "mozilla/FStream.h"
+#include "mozilla/IOInterposer.h"
 #include "mozilla/Likely.h"
 #include "mozilla/MathAlgorithms.h"
-#include "mozilla/Unused.h"
-
-#include "base/pickle.h"
-#include "other/CombinedStacks.h"
-#include "nsIComponentManager.h"
-#include "nsIServiceManager.h"
-#include "nsThreadManager.h"
-#include "nsXPCOMCIDInternal.h"
-#include "nsCOMArray.h"
-#include "nsCOMPtr.h"
-#include "nsXPCOMPrivate.h"
-#include "nsIXULAppInfo.h"
-#include "nsVersionComparator.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/ModuleUtils.h"
-#include "nsIXPConnect.h"
+#include "mozilla/Mutex.h"
+#include "mozilla/PoisonIOInterposer.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/ProcessedStack.h"
 #include "mozilla/Services.h"
-#include "jsapi.h"
-#include "jsfriendapi.h"
-#include "js/GCAPI.h"
-#include "nsString.h"
-#include "nsITelemetry.h"
+#include "mozilla/StartupTimeline.h"
+#include "mozilla/StaticPtr.h"
+#include "mozilla/Unused.h"
+#if defined(XP_WIN)
+#include "mozilla/WinDllServices.h"
+#endif
+#include "nsAppDirectoryServiceDefs.h"
+#include "nsBaseHashtable.h"
+#include "nsClassHashtable.h"
+#include "nsCOMArray.h"
+#include "nsCOMPtr.h"
+#include "nsDataHashtable.h"
+#include "nsHashKeys.h"
+#include "nsIComponentManager.h"
 #include "nsIDirectoryEnumerator.h"
 #include "nsIFileStreams.h"
-#include "nsLocalFile.h"
 #include "nsIMemoryReporter.h"
 #include "nsISeekableStream.h"
+#include "nsIServiceManager.h"
 #include "nsISimpleEnumerator.h"
-#include "Telemetry.h"
-#include "TelemetryCommon.h"
-#include "TelemetryHistogram.h"
-#include "other/TelemetryIOInterposeObserver.h"
-#include "ipc/TelemetryIPCAccumulator.h"
-#include "TelemetryScalar.h"
-#include "TelemetryEvent.h"
-#include "other/WebrtcTelemetry.h"
+#include "nsITelemetry.h"
+#include "nsIXPConnect.h"
+#include "nsIXULAppInfo.h"
+#include "nsJSUtils.h"
+#include "nsLocalFile.h"
+#include "nsNativeCharsetUtils.h"
+#include "nsNetCID.h"
+#include "nsNetUtil.h"
+#include "nsProxyRelease.h"
+#include "nsReadableUtils.h"
+#include "nsString.h"
 #include "nsTHashtable.h"
-#include "nsHashKeys.h"
-#include "nsBaseHashtable.h"
-#include "nsClassHashtable.h"
-#include "nsDataHashtable.h"
-#include "nsXULAppAPI.h"
-#include "nsReadableUtils.h"
+#include "nsThreadManager.h"
 #include "nsThreadUtils.h"
 #if defined(XP_WIN)
 #include "nsUnicharUtils.h"
 #endif
-#include "nsNetCID.h"
-#include "nsNetUtil.h"
-#include "nsJSUtils.h"
-#include "nsReadableUtils.h"
+#include "nsVersionComparator.h"
+#include "nsXPCOMCIDInternal.h"
+#include "nsXPCOMPrivate.h"
+#include "nsXULAppAPI.h"
+#include "other/CombinedStacks.h"
+#include "other/TelemetryIOInterposeObserver.h"
+#include "other/WebrtcTelemetry.h"
 #include "plstr.h"
-#include "nsAppDirectoryServiceDefs.h"
-#include "mozilla/BackgroundHangMonitor.h"
-#include "mozilla/FStream.h"
-#include "mozilla/ProcessedStack.h"
-#include "mozilla/Mutex.h"
-#include "mozilla/Preferences.h"
-#include "mozilla/StaticPtr.h"
-#include "mozilla/IOInterposer.h"
-#include "mozilla/PoisonIOInterposer.h"
-#include "mozilla/StartupTimeline.h"
-#if defined(XP_WIN)
-#include "mozilla/WinDllServices.h"
-#endif
-#include "nsNativeCharsetUtils.h"
-#include "nsProxyRelease.h"
-
 #if defined(MOZ_GECKO_PROFILER)
 #include "shared-libraries.h"
 #include "other/KeyedStackCapturer.h"
 #endif // MOZ_GECKO_PROFILER
-
-#if defined(MOZ_TELEMETRY_GECKOVIEW)
-#include "geckoview/TelemetryGeckoViewPersistence.h"
-#endif
+#include "TelemetryCommon.h"
+#include "TelemetryEvent.h"
+#include "TelemetryHistogram.h"
+#include "TelemetryScalar.h"
 
 namespace {
 
 using namespace mozilla;
 using Telemetry::Common::AutoHashtable;
 using Telemetry::Common::ToJSString;
 using Telemetry::Common::GetCurrentProduct;
 using Telemetry::Common::SetCurrentProduct;
--- a/toolkit/components/telemetry/core/Telemetry.h
+++ b/toolkit/components/telemetry/core/Telemetry.h
@@ -2,24 +2,23 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef Telemetry_h__
 #define Telemetry_h__
 
 #include "mozilla/GuardObjects.h"
-#include "mozilla/TimeStamp.h"
 #include "mozilla/StartupTimeline.h"
-#include "nsTArray.h"
-#include "nsString.h"
-#include "nsXULAppAPI.h"
-
 #include "mozilla/TelemetryHistogramEnums.h"
 #include "mozilla/TelemetryScalarEnums.h"
+#include "mozilla/TimeStamp.h"
+#include "nsString.h"
+#include "nsTArray.h"
+#include "nsXULAppAPI.h"
 
 /******************************************************************************
  * This implements the Telemetry system.
  * It allows recording into histograms as well some more specialized data
  * points and gives access to the data.
  *
  * For documentation on how to add and use new Telemetry probes, see:
  * https://developer.mozilla.org/en-US/docs/Mozilla/Performance/Adding_a_new_Telemetry_probe
--- a/toolkit/components/telemetry/core/TelemetryCommon.cpp
+++ b/toolkit/components/telemetry/core/TelemetryCommon.cpp
@@ -1,26 +1,25 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#include "nsITelemetry.h"
-#include "nsVersionComparator.h"
+#include "TelemetryCommon.h"
+
+#include <cstring>
 #include "mozilla/TimeStamp.h"
 #include "mozilla/Preferences.h"
 #include "nsIConsoleService.h"
+#include "nsITelemetry.h"
 #include "nsThreadUtils.h"
-
-#include "TelemetryCommon.h"
+#include "nsVersionComparator.h"
 #include "TelemetryProcessData.h"
 
-#include <cstring>
-
 namespace mozilla {
 namespace Telemetry {
 namespace Common {
 
 bool
 IsExpiredVersion(const char* aExpiration)
 {
   MOZ_ASSERT(aExpiration);
--- a/toolkit/components/telemetry/core/TelemetryCommon.h
+++ b/toolkit/components/telemetry/core/TelemetryCommon.h
@@ -1,22 +1,22 @@
 /* -*-  Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef TelemetryCommon_h__
 #define TelemetryCommon_h__
 
-#include "nsTHashtable.h"
 #include "jsapi.h"
-#include "nsIScriptError.h"
-#include "nsXULAppAPI.h"
 #include "mozilla/TypedEnumBits.h"
 #include "mozilla/TelemetryProcessEnums.h"
+#include "nsIScriptError.h"
+#include "nsTHashtable.h"
+#include "nsXULAppAPI.h"
 
 namespace mozilla {
 namespace Telemetry {
 namespace Common {
 
 enum class RecordedProcessType : uint32_t {
   Main         = (1 << GeckoProcessType_Default),  // Also known as "parent process"
   Content      = (1 << GeckoProcessType_Content),
--- a/toolkit/components/telemetry/core/TelemetryEvent.cpp
+++ b/toolkit/components/telemetry/core/TelemetryEvent.cpp
@@ -1,41 +1,40 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+#include "Telemetry.h"
+#include "TelemetryEvent.h"
 #include <prtime.h>
 #include <limits>
+#include "ipc/TelemetryIPCAccumulator.h"
+#include "jsapi.h"
+#include "mozilla/Maybe.h"
+#include "mozilla/Pair.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/Services.h"
+#include "mozilla/StaticMutex.h"
+#include "mozilla/StaticPtr.h"
+#include "mozilla/Unused.h"
+#include "nsClassHashtable.h"
+#include "nsDataHashtable.h"
+#include "nsHashKeys.h"
 #include "nsIObserverService.h"
 #include "nsITelemetry.h"
-#include "nsHashKeys.h"
-#include "nsDataHashtable.h"
-#include "nsClassHashtable.h"
+#include "nsJSUtils.h"
+#include "nsPrintfCString.h"
 #include "nsTArray.h"
-#include "mozilla/Preferences.h"
-#include "mozilla/StaticMutex.h"
-#include "mozilla/Unused.h"
-#include "mozilla/Maybe.h"
-#include "mozilla/Services.h"
-#include "mozilla/StaticPtr.h"
-#include "mozilla/Pair.h"
-#include "jsapi.h"
-#include "nsJSUtils.h"
+#include "nsUTF8Utils.h"
 #include "nsXULAppAPI.h"
-#include "nsUTF8Utils.h"
-#include "nsPrintfCString.h"
-
-#include "Telemetry.h"
 #include "TelemetryCommon.h"
-#include "TelemetryEvent.h"
 #include "TelemetryEventData.h"
 #include "TelemetryScalar.h"
-#include "ipc/TelemetryIPCAccumulator.h"
 
 using mozilla::StaticMutex;
 using mozilla::StaticMutexAutoLock;
 using mozilla::ArrayLength;
 using mozilla::Maybe;
 using mozilla::Nothing;
 using mozilla::StaticAutoPtr;
 using mozilla::TimeStamp;
--- a/toolkit/components/telemetry/core/TelemetryEvent.h
+++ b/toolkit/components/telemetry/core/TelemetryEvent.h
@@ -1,16 +1,17 @@
 /* -*-  Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef TelemetryEvent_h__
 #define TelemetryEvent_h__
 
+#include <stdint.h>
 #include "mozilla/TelemetryEventEnums.h"
 #include "mozilla/TelemetryProcessEnums.h"
 
 namespace mozilla {
 namespace Telemetry {
   struct ChildEventData;
 }
 }
--- a/toolkit/components/telemetry/core/TelemetryHistogram.cpp
+++ b/toolkit/components/telemetry/core/TelemetryHistogram.cpp
@@ -1,42 +1,39 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+#include "TelemetryHistogram.h"
+
+#include <limits>
+#include "base/histogram.h"
+#include "ipc/TelemetryIPCAccumulator.h"
 #include "jsapi.h"
 #include "jsfriendapi.h"
 #include "js/GCAPI.h"
-#include "nsString.h"
-#include "nsTHashtable.h"
-#include "nsHashKeys.h"
-#include "nsBaseHashtable.h"
-#include "nsClassHashtable.h"
-#include "nsITelemetry.h"
-#include "nsPrintfCString.h"
-
 #include "mozilla/dom/ToJSValue.h"
 #include "mozilla/gfx/GPUProcessManager.h"
 #include "mozilla/Atomics.h"
 #include "mozilla/JSONWriter.h"
 #include "mozilla/StartupTimeline.h"
 #include "mozilla/StaticMutex.h"
 #include "mozilla/Unused.h"
-
+#include "nsBaseHashtable.h"
+#include "nsClassHashtable.h"
+#include "nsString.h"
+#include "nsTHashtable.h"
+#include "nsHashKeys.h"
+#include "nsITelemetry.h"
+#include "nsPrintfCString.h"
 #include "TelemetryCommon.h"
-#include "TelemetryHistogram.h"
 #include "TelemetryHistogramNameMap.h"
 #include "TelemetryScalar.h"
-#include "ipc/TelemetryIPCAccumulator.h"
-
-#include "base/histogram.h"
-
-#include <limits>
 
 using base::Histogram;
 using base::BooleanHistogram;
 using base::CountHistogram;
 using base::FlagHistogram;
 using base::LinearHistogram;
 using mozilla::MakeTuple;
 using mozilla::StaticMutexNotRecorded;
--- a/toolkit/components/telemetry/core/TelemetryHistogram.h
+++ b/toolkit/components/telemetry/core/TelemetryHistogram.h
@@ -1,20 +1,19 @@
 /* -*-  Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef TelemetryHistogram_h__
 #define TelemetryHistogram_h__
 
+#include "mozilla/TelemetryComms.h"
 #include "mozilla/TelemetryHistogramEnums.h"
 #include "mozilla/TelemetryProcessEnums.h"
-
-#include "mozilla/TelemetryComms.h"
 #include "nsXULAppAPI.h"
 
 namespace mozilla{
 // This is only used for the GeckoView persistence.
 class JSONWriter;
 }
 
 // This module is internal to Telemetry.  It encapsulates Telemetry's
--- a/toolkit/components/telemetry/core/TelemetryScalar.cpp
+++ b/toolkit/components/telemetry/core/TelemetryScalar.cpp
@@ -1,39 +1,39 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#include "nsITelemetry.h"
-#include "nsIVariant.h"
-#include "nsVariant.h"
-#include "nsHashKeys.h"
-#include "nsBaseHashtable.h"
-#include "nsClassHashtable.h"
-#include "nsDataHashtable.h"
-#include "nsIXPConnect.h"
-#include "nsContentUtils.h"
-#include "nsThreadUtils.h"
-#include "nsJSUtils.h"
-#include "nsPrintfCString.h"
+#include "TelemetryScalar.h"
+
+#include "ipc/TelemetryComms.h"
+#include "ipc/TelemetryIPCAccumulator.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/PContent.h"
 #include "mozilla/JSONWriter.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/StaticMutex.h"
 #include "mozilla/StaticPtr.h"
 #include "mozilla/Unused.h"
-
+#include "nsBaseHashtable.h"
+#include "nsClassHashtable.h"
+#include "nsContentUtils.h"
+#include "nsDataHashtable.h"
+#include "nsHashKeys.h"
+#include "nsITelemetry.h"
+#include "nsIVariant.h"
+#include "nsIXPConnect.h"
+#include "nsJSUtils.h"
+#include "nsPrintfCString.h"
+#include "nsThreadUtils.h"
+#include "nsVariant.h"
 #include "TelemetryCommon.h"
-#include "TelemetryScalar.h"
 #include "TelemetryScalarData.h"
-#include "ipc/TelemetryComms.h"
-#include "ipc/TelemetryIPCAccumulator.h"
 
 using mozilla::Preferences;
 using mozilla::StaticAutoPtr;
 using mozilla::StaticMutex;
 using mozilla::StaticMutexNotRecorded;
 using mozilla::StaticMutexAutoLock;
 using mozilla::Some;
 using mozilla::Nothing;
--- a/toolkit/components/telemetry/core/TelemetryScalar.h
+++ b/toolkit/components/telemetry/core/TelemetryScalar.h
@@ -1,19 +1,20 @@
 /* -*-  Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef TelemetryScalar_h__
 #define TelemetryScalar_h__
 
-#include "nsTArray.h"
+#include <stdint.h>
+#include "mozilla/TelemetryProcessEnums.h"
 #include "mozilla/TelemetryScalarEnums.h"
-#include "mozilla/TelemetryProcessEnums.h"
+#include "nsTArray.h"
 
 // This module is internal to Telemetry. It encapsulates Telemetry's
 // scalar accumulation and storage logic. It should only be used by
 // Telemetry.cpp. These functions should not be used anywhere else.
 // For the public interface to Telemetry functionality, see Telemetry.h.
 
 namespace mozilla {
 // This is only used for the GeckoView persistence.
--- a/toolkit/components/telemetry/core/ipc/TelemetryComms.h
+++ b/toolkit/components/telemetry/core/ipc/TelemetryComms.h
@@ -2,20 +2,20 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  */
 
 #ifndef Telemetry_Comms_h__
 #define Telemetry_Comms_h__
 
 #include "ipc/IPCMessageUtils.h"
+#include "mozilla/TelemetryProcessEnums.h"
+#include "mozilla/TimeStamp.h"
 #include "nsITelemetry.h"
 #include "nsVariant.h"
-#include "mozilla/TimeStamp.h"
-#include "mozilla/TelemetryProcessEnums.h"
 
 namespace mozilla {
 namespace Telemetry {
 
 // Histogram accumulation types.
 enum HistogramID : uint32_t;
 
 struct HistogramAccumulation
--- a/toolkit/components/telemetry/core/ipc/TelemetryIPC.h
+++ b/toolkit/components/telemetry/core/ipc/TelemetryIPC.h
@@ -1,19 +1,20 @@
 /* -*-  Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef TelemetryIPC_h__
 #define TelemetryIPC_h__
 
+#include <stdint.h>
+#include "mozilla/TelemetryProcessEnums.h"
 #include "nsTArray.h"
 #include "nsXULAppAPI.h"
-#include "mozilla/TelemetryProcessEnums.h"
 
 // This module provides the interface to accumulate Telemetry from child processes.
 // Top-level actors for different child processes types (ContentParent, GPUChild)
 // will call this for messages from their respective processes.
 
 namespace mozilla {
 
 namespace Telemetry {
--- a/toolkit/components/telemetry/core/ipc/TelemetryIPCAccumulator.cpp
+++ b/toolkit/components/telemetry/core/ipc/TelemetryIPCAccumulator.cpp
@@ -1,28 +1,28 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "TelemetryIPCAccumulator.h"
 
+#include "core/TelemetryHistogram.h"
+#include "core/TelemetryScalar.h"
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/gfx/GPUParent.h"
 #include "mozilla/gfx/GPUProcessManager.h"
 #include "mozilla/StaticMutex.h"
 #include "mozilla/StaticPtr.h"
 #include "mozilla/SystemGroup.h"
 #include "mozilla/Unused.h"
 #include "nsComponentManagerUtils.h"
 #include "nsITimer.h"
 #include "nsThreadUtils.h"
-#include "core/TelemetryHistogram.h"
-#include "core/TelemetryScalar.h"
 
 using mozilla::StaticMutex;
 using mozilla::StaticMutexAutoLock;
 using mozilla::StaticAutoPtr;
 using mozilla::SystemGroup;
 using mozilla::TaskCategory;
 using mozilla::Telemetry::HistogramAccumulation;
 using mozilla::Telemetry::DiscardedData;
--- a/toolkit/components/telemetry/geckoview/TelemetryGeckoViewPersistence.cpp
+++ b/toolkit/components/telemetry/geckoview/TelemetryGeckoViewPersistence.cpp
@@ -1,41 +1,41 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "TelemetryGeckoViewPersistence.h"
 
+#include "core/TelemetryHistogram.h"
+#include "core/TelemetryScalar.h"
 #include "jsapi.h"
 #include "js/JSON.h"
+#include "mozilla/dom/ScriptSettings.h" // for AutoJSAPI
+#include "mozilla/dom/SimpleGlobalObject.h"
 #include "mozilla/ErrorNames.h"
 #include "mozilla/JSONWriter.h"
 #include "mozilla/Path.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/ScopeExit.h"
 #include "mozilla/StaticPtr.h"
 #include "mozilla/SystemGroup.h"
-#include "mozilla/dom/ScriptSettings.h" // for AutoJSAPI
-#include "mozilla/dom/SimpleGlobalObject.h"
 #include "nsDirectoryServiceDefs.h"
 #include "nsIFile.h"
 #include "nsIInputStream.h"
 #include "nsIObserverService.h"
 #include "nsIOutputStream.h"
 #include "nsISafeOutputStream.h"
 #include "nsITimer.h"
 #include "nsLocalFile.h"
 #include "nsNetUtil.h"
 #include "nsXULAppAPI.h"
 #include "prenv.h"
 #include "prio.h"
-#include "core/TelemetryScalar.h"
-#include "core/TelemetryHistogram.h"
 #include "xpcpublic.h"
 
 using mozilla::GetErrorName;
 using mozilla::MakeScopeExit;
 using mozilla::Preferences;
 using mozilla::StaticRefPtr;
 using mozilla::SystemGroup;
 using mozilla::TaskCategory;
--- a/toolkit/components/telemetry/geckoview/gtest/TestGeckoView.cpp
+++ b/toolkit/components/telemetry/geckoview/gtest/TestGeckoView.cpp
@@ -7,18 +7,18 @@
 #include "mozilla/JSONWriter.h"
 #include "nsDirectoryServiceDefs.h"
 #include "nsIObserver.h"
 #include "nsIObserverService.h"
 #include "nsIOutputStream.h"
 #include "nsITelemetry.h"
 #include "nsJSUtils.h"
 #include "nsNetUtil.h"
+#include "nsPrintfCString.h"
 #include "nsThreadUtils.h"
-#include "nsPrintfCString.h"
 #include "prenv.h"
 #include "mozilla/Telemetry.h"
 #include "TelemetryFixture.h"
 #include "TelemetryGeckoViewPersistence.h"
 #include "core/TelemetryScalar.h"
 #include "TelemetryTestHelpers.h"
 
 using namespace mozilla;
--- a/toolkit/components/telemetry/other/CombinedStacks.cpp
+++ b/toolkit/components/telemetry/other/CombinedStacks.cpp
@@ -1,17 +1,18 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "CombinedStacks.h"
+
+#include "jsapi.h"
 #include "mozilla/HangAnnotations.h"
-#include "jsapi.h"
 
 namespace mozilla {
 namespace Telemetry {
 
 // The maximum number of chrome hangs stacks that we're keeping.
 const size_t kMaxChromeStacksKept = 50;
 
 CombinedStacks::CombinedStacks()
--- a/toolkit/components/telemetry/other/KeyedStackCapturer.cpp
+++ b/toolkit/components/telemetry/other/KeyedStackCapturer.cpp
@@ -1,19 +1,20 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "KeyedStackCapturer.h"
-#include "nsPrintfCString.h"
+
+#include "jsapi.h"
 #include "mozilla/StackWalk.h"
+#include "nsPrintfCString.h"
 #include "ProcessedStack.h"
-#include "jsapi.h"
 
 namespace {
 
 /** Defines the size of the keyed stack dictionary. */
 const uint8_t kMaxKeyLength = 50;
 
 /** The maximum number of captured stacks that we're keeping. */
 const size_t kMaxCapturedStacksKept = 50;
--- a/toolkit/components/telemetry/other/KeyedStackCapturer.h
+++ b/toolkit/components/telemetry/other/KeyedStackCapturer.h
@@ -1,21 +1,21 @@
 /* -*-  Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef KeyedStackCapturer_h__
 #define KeyedStackCapturer_h__
 
+#include "CombinedStacks.h"
+#include "mozilla/Mutex.h"
 #include "mozilla/Telemetry.h"
-#include "nsString.h"
 #include "nsClassHashtable.h"
-#include "mozilla/Mutex.h"
-#include "CombinedStacks.h"
+#include "nsString.h"
 
 struct JSContext;
 
 namespace mozilla {
 namespace Telemetry {
 
 /**
 * Allows taking a snapshot of a call stack on demand. Captured stacks are
--- a/toolkit/components/telemetry/other/TelemetryIOInterposeObserver.h
+++ b/toolkit/components/telemetry/other/TelemetryIOInterposeObserver.h
@@ -8,25 +8,24 @@
 /**
  * IOInterposeObserver recording statistics of main-thread I/O during execution,
  * aimed at consumption by TelemetryImpl
  */
 
 #ifndef TelemetryIOInterposeObserver_h__
 #define TelemetryIOInterposeObserver_h__
 
+#include "core/TelemetryCommon.h"
 #include "jsapi.h"
+#include "mozilla/IOInterposer.h"
+#include "nsBaseHashtable.h"
+#include "nsClassHashtable.h"
+#include "nsHashKeys.h"
 #include "nsTArray.h"
 #include "nsTHashtable.h"
-#include "nsHashKeys.h"
-#include "nsBaseHashtable.h"
-#include "nsClassHashtable.h"
-
-#include "core/TelemetryCommon.h"
-#include "mozilla/IOInterposer.h"
 
 namespace mozilla {
 namespace Telemetry {
 
 class TelemetryIOInterposeObserver : public IOInterposeObserver
 {
   /** File-level statistics structure */
   struct FileStats {
--- a/toolkit/components/telemetry/other/WebrtcTelemetry.cpp
+++ b/toolkit/components/telemetry/other/WebrtcTelemetry.cpp
@@ -1,21 +1,20 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+#include "WebrtcTelemetry.h"
 
+#include "jsapi.h"
 #include "mozilla/Telemetry.h"
-#include "WebrtcTelemetry.h"
-#include "jsapi.h"
 #include "nsPrintfCString.h"
 #include "nsTHashtable.h"
-
 void
 WebrtcTelemetry::RecordIceCandidateMask(const uint32_t iceCandidateBitmask,
                                         const bool success)
 {
   WebrtcIceCandidateType *entry = mWebrtcIceCandidates.GetEntry(iceCandidateBitmask);
   if (!entry) {
     entry = mWebrtcIceCandidates.PutEntry(iceCandidateBitmask);
     if (MOZ_UNLIKELY(!entry))
--- a/toolkit/components/telemetry/pingsender/pingsender_unix_common.cpp
+++ b/toolkit/components/telemetry/pingsender/pingsender_unix_common.cpp
@@ -1,24 +1,21 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include <cerrno>
 #include <cstring>
+#include <dlfcn.h>
 #include <string>
-
-#include <dlfcn.h>
 #include <unistd.h>
-
+#include "mozilla/Unused.h"
 #include "third_party/curl/curl.h"
 
-#include "mozilla/Unused.h"
-
 namespace PingSender {
 
 using std::string;
 
 using mozilla::Unused;
 
 /**
  * A simple wrapper around libcurl "easy" functions. Provides RAII opening
--- a/toolkit/components/telemetry/tests/gtest/TelemetryFixture.h
+++ b/toolkit/components/telemetry/tests/gtest/TelemetryFixture.h
@@ -1,19 +1,19 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 #ifndef TelemetryFixture_h_
 #define TelemetryFixture_h_
 
 #include "gtest/gtest.h"
-#include "nsITelemetry.h"
 #include "mozilla/CycleCollectedJSContext.h"
 #include "mozilla/dom/ScriptSettings.h"
+#include "nsITelemetry.h"
 
 class TelemetryTestFixture: public ::testing::Test {
 protected:
   TelemetryTestFixture() : mCleanGlobal(nullptr) {}
   virtual void SetUp();
 
   JSObject* mCleanGlobal;
 
--- a/toolkit/components/telemetry/tests/gtest/TelemetryTestHelpers.cpp
+++ b/toolkit/components/telemetry/tests/gtest/TelemetryTestHelpers.cpp
@@ -1,17 +1,17 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 #include "TelemetryTestHelpers.h"
 
+#include "core/TelemetryCommon.h"
 #include "gtest/gtest.h"
 #include "mozilla/CycleCollectedJSContext.h"
-#include "core/TelemetryCommon.h"
 #include "mozilla/Unused.h"
 
 using namespace mozilla;
 
 // Helper methods provided to simplify writing tests and meant to be used in C++ Gtests.
 namespace TelemetryTestHelpers {
 
 void
--- a/toolkit/components/telemetry/tests/gtest/TestCounters.cpp
+++ b/toolkit/components/telemetry/tests/gtest/TestCounters.cpp
@@ -1,17 +1,17 @@
 /* vim:set ts=2 sw=2 sts=0 et: */
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 #include "gtest/gtest.h"
 #include "js/Conversions.h"
+#include "mozilla/Telemetry.h"
 #include "nsITelemetry.h"
-#include "mozilla/Telemetry.h"
 #include "TelemetryFixture.h"
 #include "TelemetryTestHelpers.h"
 
 using namespace mozilla;
 using namespace TelemetryTestHelpers;
 
 TEST_F(TelemetryTestFixture, AutoCounter)
 {
--- a/toolkit/components/telemetry/tests/gtest/TestHistograms.cpp
+++ b/toolkit/components/telemetry/tests/gtest/TestHistograms.cpp
@@ -1,17 +1,17 @@
 /* vim:set ts=2 sw=2 sts=0 et: */
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 #include "gtest/gtest.h"
 #include "js/Conversions.h"
+#include "mozilla/Telemetry.h"
 #include "nsITelemetry.h"
-#include "mozilla/Telemetry.h"
 #include "TelemetryFixture.h"
 #include "TelemetryTestHelpers.h"
 
 using namespace mozilla;
 using namespace TelemetryTestHelpers;
 
 TEST_F(TelemetryTestFixture, AccumulateCountHistogram)
 {
--- a/toolkit/components/telemetry/tests/gtest/TestScalars.cpp
+++ b/toolkit/components/telemetry/tests/gtest/TestScalars.cpp
@@ -1,24 +1,23 @@
 /* vim:set ts=2 sw=2 sts=2 et: */
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
+#include "core/TelemetryScalar.h"
 #include "gtest/gtest.h"
-
 #include "js/Conversions.h"
+#include "mozilla/Telemetry.h"
+#include "mozilla/TelemetryProcessEnums.h"
 #include "mozilla/Unused.h"
 #include "nsJSUtils.h" // nsAutoJSString
 #include "nsITelemetry.h"
 #include "nsThreadUtils.h"
-#include "mozilla/Telemetry.h"
-#include "mozilla/TelemetryProcessEnums.h"
 #include "TelemetryFixture.h"
-#include "core/TelemetryScalar.h"
 #include "TelemetryTestHelpers.h"
 
 using namespace mozilla;
 using namespace TelemetryTestHelpers;
 using mozilla::Telemetry::ProcessID;
 
 #define EXPECTED_STRING "Nice, expected and creative string."